Skip to content

Commit

Permalink
Merge pull request #147 from NeowayLabs/setenv
Browse files Browse the repository at this point in the history
Setenv
  • Loading branch information
i4ki authored Nov 20, 2016
2 parents db4c0b9 + e13fbdd commit 93b28d5
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 12 deletions.
20 changes: 18 additions & 2 deletions ast/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type (
token.FileInfo

varName string
assign Node
}

// AssignmentNode is a node for variable assignments
Expand Down Expand Up @@ -455,18 +456,27 @@ func (n *ImportNode) IsEqual(other Node) bool {
}

// NewSetenvNode creates a new assignment node
func NewSetenvNode(info token.FileInfo, name string) *SetenvNode {
func NewSetenvNode(info token.FileInfo, name string, assign Node) (*SetenvNode, error) {
if assign != nil && assign.Type() != NodeAssignment &&
assign.Type() != NodeExecAssign {
return nil, errors.New("Invalid assignment in setenv")
}

return &SetenvNode{
NodeType: NodeSetenv,
FileInfo: info,

varName: name,
}
assign: assign,
}, nil
}

// Identifier returns the environment name.
func (n *SetenvNode) Identifier() string { return n.varName }

// Assignment returns the setenv assignment (if any)
func (n *SetenvNode) Assignment() Node { return n.assign }

// IsEqual returns if it is equal to the other node.
func (n *SetenvNode) IsEqual(other Node) bool {
if n == other {
Expand All @@ -484,6 +494,12 @@ func (n *SetenvNode) IsEqual(other Node) bool {
return false
}

if n.assign != o.assign {
if !n.assign.IsEqual(o.assign) {
return false
}
}

return n.varName == o.varName
}

Expand Down
6 changes: 5 additions & 1 deletion ast/node_fmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@ func (n *ImportNode) String() string {

// String returns the string representation of assignment
func (n *SetenvNode) String() string {
return "setenv " + n.varName
if n.assign == nil {
return "setenv " + n.varName
}

return "setenv " + n.assign.String()
}

func (n *AssignmentNode) string() (string, bool) {
Expand Down
19 changes: 19 additions & 0 deletions internal/sh/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -1474,8 +1474,27 @@ func (shell *Shell) executeSetenv(v *ast.SetenvNode) error {
var (
varValue sh.Obj
ok bool
assign = v.Assignment()
err error
)

if assign != nil {
switch assign.Type() {
case ast.NodeAssignment:
err = shell.executeAssignment(assign.(*ast.AssignmentNode))
case ast.NodeExecAssign:
err = shell.executeExecAssign(assign.(*ast.ExecAssignNode))
default:
err = errors.NewEvalError(shell.filename,
v, "Failed to eval setenv, invalid assignment type: %+v",
assign)
}

if err != nil {
return err
}
}

varName := v.Identifier()

if varValue, ok = shell.Getvar(varName); !ok {
Expand Down
41 changes: 39 additions & 2 deletions internal/sh/shell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,44 @@ func TestExecuteRedirectionMap(t *testing.T) {
}
}

func TestExecuteSetenv(t *testing.T) {
for _, test := range []execTest{
{
"test setenv basic",
`test = "hello"
setenv test
` + nashdPath + ` -c "echo $test"`,
"hello\n", "", "",
},
{
"test setenv assignment",
`setenv test = "hello"
` + nashdPath + ` -c "echo $test"`,
"hello\n", "", "",
},
{
"test setenv exec cmd",
`setenv test <= echo -n "hello"
` + nashdPath + ` -c "echo $test"`,
"hello\n", "", "",
},
{
"test setenv semicolon",
`setenv a setenv b`,
"", "",
"test setenv semicolon:1:9: Unexpected token setenv, expected semicolon (;) or EOL",
},
} {
testExec(t,
test.desc,
test.execStr,
test.expectedStdout,
test.expectedStderr,
test.expectedErr,
)
}
}

func TestExecuteCd(t *testing.T) {
for _, test := range []execTest{
{
Expand Down Expand Up @@ -1689,8 +1727,7 @@ func TestExecuteSubShellDoesNotOverwriteparentEnv(t *testing.T) {
return
}

err = shell.Exec("set env", `SHELL = "bleh"
setenv SHELL`)
err = shell.Exec("set env", `setenv SHELL = "bleh"`)

if err != nil {
t.Error(err)
Expand Down
35 changes: 31 additions & 4 deletions parser/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,19 +454,46 @@ func (p *Parser) parseImport(importToken scanner.Token) (ast.Node, error) {
}

func (p *Parser) parseSetenv(it scanner.Token) (ast.Node, error) {
fileInfo := it.FileInfo
var (
setenv *ast.SetenvNode
assign ast.Node
err error
fileInfo = it.FileInfo
)

it = p.next()
next := p.peek()

if it.Type() != token.Ident {
return nil, newParserError(it, p.name, "Unexpected token %v, expected VARIABLE", it)
return nil, newParserError(it, p.name, "Unexpected token %v, expected identifier", it)
}

if p.peek().Type() == token.Semicolon {
if next.Type() == token.Assign || next.Type() == token.AssignCmd {
assign, err = p.parseAssignment(it)

if err != nil {
return nil, err
}

setenv, err = ast.NewSetenvNode(fileInfo, it.Value(), assign)
} else {
setenv, err = ast.NewSetenvNode(fileInfo, it.Value(), nil)

if p.peek().Type() != token.Semicolon {
return nil, newParserError(p.peek(),
p.name,
"Unexpected token %v, expected semicolon (;) or EOL",
p.peek())
}

p.ignore()
}

return ast.NewSetenvNode(fileInfo, it.Value()), nil
if err != nil {
return nil, err
}

return setenv, nil
}

func (p *Parser) getArgument(allowArg, allowConcat bool) (ast.Expr, error) {
Expand Down
52 changes: 50 additions & 2 deletions parser/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,55 @@ func TestParsePipe(t *testing.T) {
func TestBasicSetAssignment(t *testing.T) {
expected := ast.NewTree("simple set assignment")
ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
set := ast.NewSetenvNode(token.NewFileInfo(1, 0), "test")
set, err := ast.NewSetenvNode(token.NewFileInfo(1, 0), "test", nil)

if err != nil {
t.Fatal(err)
}

ln.Push(set)
expected.Root = ln

parserTestTable("simple set assignment", `setenv test`, expected, t, true)

// setenv with assignment
expected = ast.NewTree("setenv with simple assignment")
ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
assign := ast.NewAssignmentNode(token.NewFileInfo(1, 7),
"test",
ast.NewStringExpr(token.NewFileInfo(1, 15), "hello", true))
set, err = ast.NewSetenvNode(token.NewFileInfo(1, 0), "test", assign)

if err != nil {
t.Fatal(err)
}

ln.Push(set)
expected.Root = ln

parserTestTable("setenv with simple assignment", `setenv test = "hello"`, expected, t, true)

expected = ast.NewTree("setenv with simple cmd assignment")
ln = ast.NewBlockNode(token.NewFileInfo(1, 0))

cmd := ast.NewCommandNode(token.NewFileInfo(1, 15), "ls", false)

cmdAssign, err := ast.NewExecAssignNode(token.NewFileInfo(1, 7), "test", cmd)

if err != nil {
t.Fatal(err)
}

set, err = ast.NewSetenvNode(token.NewFileInfo(1, 0), "test", cmdAssign)

if err != nil {
t.Fatal(err)
}

ln.Push(set)
expected.Root = ln

parserTestTable("simple assignment", `setenv test <= ls`, expected, t, true)
}

func TestBasicAssignment(t *testing.T) {
Expand Down Expand Up @@ -436,7 +479,12 @@ func TestParseCd(t *testing.T) {

assign := ast.NewAssignmentNode(token.NewFileInfo(1, 0), "HOME", ast.NewStringExpr(token.NewFileInfo(1, 8), "/", true))

set := ast.NewSetenvNode(token.NewFileInfo(3, 0), "HOME")
set, err := ast.NewSetenvNode(token.NewFileInfo(3, 0), "HOME", nil)

if err != nil {
t.Fatal(err)
}

cd = ast.NewCommandNode(token.NewFileInfo(5, 0), "cd", false)
pwd := ast.NewCommandNode(token.NewFileInfo(6, 0), "pwd", false)

Expand Down
2 changes: 1 addition & 1 deletion spec.ebnf
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ bindfn = "bindfn" identifier identifier .
dump = "dump" [ filename ] .

/* Set environment variable */
setenvDecl = "setenv" identifier .
setenvDecl = "setenv" ( identifier | varDecl ) .

/* Comment */
comment = "#" { unicode_char } .
Expand Down

0 comments on commit 93b28d5

Please sign in to comment.