diff --git a/.travis.yml b/.travis.yml index ab1bf0b5..4fc91680 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,11 @@ matrix: - os: osx language: go -sudo: required - -services: - - docker +sudo: false go: + - tip + - 1.7 - 1.6 - 1.5 install: @@ -23,13 +22,8 @@ script: - go get github.com/mattn/goveralls - go get golang.org/x/tools/cmd/cover - make test -# - goveralls -coverprofile=coverage.txt -service=travis-ci -before_install: - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install go ; fi - - sudo pip install codecov after_success: - codecov + - bash <(curl -s https://codecov.io/bash) notifications: webhooks: urls: diff --git a/cmd/nash/Makefile b/cmd/nash/Makefile index 94f06ca5..4d0c6cd3 100644 --- a/cmd/nash/Makefile +++ b/cmd/nash/Makefile @@ -1,7 +1,7 @@ all: build install VERSION=$(shell git rev-parse --abbrev-ref HEAD) -BUILDARGS = -ldflags "-linkmode external -extldflags -static -X main.VersionString=$(VERSION)" -v +BUILDARGS = -installsuffix netgo -ldflags "-linkmode external -extldflags -static -X main.VersionString=$(VERSION)" -v build: GO15VENDOREXPERIMENT=1 go build $(BUILDARGS) diff --git a/internal/sh/shell_test.go b/internal/sh/shell_test.go index c12cfb11..27c6cfac 100644 --- a/internal/sh/shell_test.go +++ b/internal/sh/shell_test.go @@ -1175,35 +1175,22 @@ func TestExecuteUDPRedirection(t *testing.T) { } func TestExecuteReturn(t *testing.T) { - shell, err := NewShell() - - if err != nil { - t.Error(err) - return - } - - shell.SetNashdPath(nashdPath) - - err = shell.Exec("test return fail", "return") - - if err == nil { - t.Errorf("Must fail. Return is only valid inside function") - return - } - - err = shell.Exec("test return", `fn test() { return } -test()`) - - if err != nil { - t.Error(err) - return - } - - var out bytes.Buffer - - shell.SetStdout(&out) - - err = shell.Exec("test return", `fn test() { + for _, test := range []execTest{ + { + "return invalid", + `return`, + "", "", + ":1:0: Unexpected return outside of function declaration.", + }, + { + "test simple return", + `fn test() { return } +test()`, + "", "", "", + }, + { + "return must finish func evaluation", + `fn test() { if "1" == "1" { return "1" } @@ -1212,23 +1199,12 @@ test()`) } res <= test() -echo -n $res`) - - if err != nil { - t.Error(err) - return - } - - got := string(out.Bytes()) - - if got != "1" { - t.Errorf("Expected '1' but got '%s'", got) - return - } - - out.Reset() - - err = shell.Exec("ret from for", `fn test() { +echo -n $res`, + "1", "", "", + }, + { + "ret from for", + `fn test() { values = (0 1 2 3 4 5 6 7 8 9) for i in $values { @@ -1240,23 +1216,12 @@ echo -n $res`) return "0" } a <= test() -echo -n $a`) - - if err != nil { - t.Error(err) - return - } - - got = string(out.Bytes()) - - if "5" != got { - t.Errorf("Expected '5' but got '%s'", got) - return - } - - out.Reset() - - err = shell.Exec("inf loop ret", `fn test() { +echo -n $a`, + "5", "", "", + }, + { + "inf loop ret", + `fn test() { for { if "1" == "1" { return "1" @@ -1267,18 +1232,25 @@ echo -n $a`) return "bleh" } a <= test() -echo -n $a`) - - if err != nil { - t.Error(err) - return - } - - got = string(out.Bytes()) - - if got != "1" { - t.Errorf("Expected '1' but got '%s'", got) - return +echo -n $a`, + "1", "", "", + }, + { + "test returning funcall", + `fn a() { return "1" } + fn b() { return a() } + c <= b() + echo -n $c`, + "1", "", "", + }, + } { + testExec(t, + test.desc, + test.execStr, + test.expectedStdout, + test.expectedStderr, + test.expectedErr, + ) } } diff --git a/parser/parse.go b/parser/parse.go index 4f3de971..4dbf1476 100644 --- a/parser/parse.go +++ b/parser/parse.go @@ -1078,13 +1078,15 @@ func (p *Parser) parseReturn(retIt scanner.Token) (ast.Node, error) { // return $v // return "" // return ( ... values ... ) + // return () if valueIt.Type() != token.Semicolon && valueIt.Type() != token.RBrace && valueIt.Type() != token.Variable && valueIt.Type() != token.String && - valueIt.Type() != token.LParen { + valueIt.Type() != token.LParen && + valueIt.Type() != token.Ident { return nil, newParserError(valueIt, p.name, - "Expected ';', STRING, VARIABLE or LPAREN, but found %v", + "Expected ';', STRING, VARIABLE, FUNCALL or LPAREN, but found %v", valueIt) } @@ -1128,6 +1130,26 @@ func (p *Parser) parseReturn(retIt scanner.Token) (ast.Node, error) { return ret, nil } + if valueIt.Type() == token.Ident { + p.next() + next := p.peek() + + if next.Type() != token.LParen { + return nil, newParserError(valueIt, p.name, + "Expected FUNCALL, STRING, VARIABLE or LPAREN, but found %v %v", + valueIt, next) + } + + arg, err := p.parseFnInv(valueIt, true) + + if err != nil { + return nil, err + } + + ret.SetReturn(arg) + return ret, nil + } + arg, err := p.getArgument(false, true) if err != nil { diff --git a/parser/parse_test.go b/parser/parse_test.go index 4347464d..a268f56a 100644 --- a/parser/parse_test.go +++ b/parser/parse_test.go @@ -1169,6 +1169,20 @@ func TestParseReturn(t *testing.T) { expected.Root = ln parserTestTable("return", `return "value"`, expected, t, true) + + expected = ast.NewTree("return funcall") + ln = ast.NewBlockNode(token.NewFileInfo(1, 0)) + + ret = ast.NewReturnNode(token.NewFileInfo(1, 0)) + + aFn := ast.NewFnInvNode(token.NewFileInfo(1, 7), "a") + + ret.SetReturn(aFn) + + ln.Push(ret) + expected.Root = ln + + parserTestTable("return", `return a()`, expected, t, true) } func TestParseIfInvalid(t *testing.T) { diff --git a/spec.ebnf b/spec.ebnf index 9505728e..9fe3e17e 100644 --- a/spec.ebnf +++ b/spec.ebnf @@ -51,7 +51,7 @@ fnArgs = { fnArg [ "," ] } . fnArg = identifier . /* return declaration */ -returnDecl = "return" [ ( variable | string_lit | list ) ] . +returnDecl = "return" [ ( variable | string_lit | list | fnInv ) ] . /* Function invocation */ fnInv = ( variable | identifier ) "(" fnArgValues ")" .