diff options
-rw-r--r-- | eval.go | 6 | ||||
-rw-r--r-- | evalcmd.go | 2 | ||||
-rw-r--r-- | expr.go | 53 | ||||
-rw-r--r-- | expr_test.go | 2 | ||||
-rw-r--r-- | func.go | 2 | ||||
-rw-r--r-- | parser.go | 16 | ||||
-rw-r--r-- | testcase/unmatched_paren.mk | 2 | ||||
-rw-r--r-- | var.go | 8 |
8 files changed, 52 insertions, 39 deletions
@@ -289,7 +289,7 @@ func (ev *Evaluator) evalMaybeRule(ast *maybeRuleAST) error { if assign != nil { if ast.term == ';' { - nexpr, _, err := parseExpr(ast.afterTerm, nil, false) + nexpr, _, err := parseExpr(ast.afterTerm, nil, parseOp{}) if err != nil { return ast.errorf("parse error: %q: %v", string(ast.afterTerm), err) } @@ -424,7 +424,7 @@ func (ev *Evaluator) evalInclude(ast *includeAST) error { ev.srcpos = ast.srcpos logf("%s include %q", ev.srcpos, ast.expr) - v, _, err := parseExpr([]byte(ast.expr), nil, false) + v, _, err := parseExpr([]byte(ast.expr), nil, parseOp{}) if err != nil { return ast.errorf("parse failed: %q: %v", ast.expr, err) } @@ -538,7 +538,7 @@ func (ev *Evaluator) evalExport(ast *exportAST) error { ev.lastRule = nil ev.srcpos = ast.srcpos - v, _, err := parseExpr(ast.expr, nil, false) + v, _, err := parseExpr(ast.expr, nil, parseOp{}) if err != nil { return ast.errorf("failed to parse: %q: %v", string(ast.expr), err) } @@ -236,7 +236,7 @@ func (r runner) eval(ev *Evaluator, s string) ([]runner, error) { return []runner{r}, nil } // TODO(ukai): parse once more earlier? - expr, _, err := parseExpr([]byte(r.cmd), nil, false) + expr, _, err := parseExpr([]byte(r.cmd), nil, parseOp{}) if err != nil { return nil, ev.errorf("parse cmd %q: %v", r.cmd, err) } @@ -26,7 +26,6 @@ import ( ) var ( - errEndOfInput = errors.New("parse: unexpected end of input") errNotLiteral = errors.New("valueNum: not literal") bufFree = sync.Pool{ @@ -323,14 +322,21 @@ func valueNum(v Value) (int, error) { return 0, errNotLiteral } +type parseOp struct { + // alloc indicates text will be allocated as literal (string) + alloc bool + + // matchParen matches parenthesis. + // note: required for func arg + matchParen bool +} + // parseExpr parses expression in `in` until it finds any byte in term. // if term is nil, it will parse to end of input. -// if term is not nil, and it reaches to end of input, return errEndOfInput. +// if term is not nil, and it reaches to end of input, return error. // it returns parsed value, and parsed length `n`, so in[n-1] is any byte of // term, and in[n:] is next input. -// if alloc is true, text will be literal (allocate string). -// otherwise, text will be tmpval on in. -func parseExpr(in, term []byte, alloc bool) (Value, int, error) { +func parseExpr(in, term []byte, op parseOp) (Value, int, error) { var exp expr b := 0 i := 0 @@ -348,20 +354,20 @@ Loop: break Loop } if in[i+1] == '$' { - exp = appendStr(exp, in[b:i+1], alloc) + exp = appendStr(exp, in[b:i+1], op.alloc) i += 2 b = i continue } if bytes.IndexByte(term, in[i+1]) >= 0 { - exp = appendStr(exp, in[b:i], alloc) + exp = appendStr(exp, in[b:i], op.alloc) exp = append(exp, &varref{varname: literal("")}) i++ b = i break Loop } - exp = appendStr(exp, in[b:i], alloc) - v, n, err := parseDollar(in[i:], alloc) + exp = appendStr(exp, in[b:i], op.alloc) + v, n, err := parseDollar(in[i:], op.alloc) if err != nil { return nil, 0, err } @@ -370,6 +376,9 @@ Loop: exp = append(exp, v) continue case '(', '{': + if !op.matchParen { + break + } cp := closeParen(ch) if i := bytes.IndexByte(term, cp); i >= 0 { parenDepth++ @@ -379,6 +388,9 @@ Loop: parenDepth++ } case saveParen: + if !op.matchParen { + break + } parenDepth-- if parenDepth == 0 { i := bytes.IndexByte(term, 0) @@ -388,9 +400,9 @@ Loop: } i++ } - exp = appendStr(exp, in[b:i], alloc) + exp = appendStr(exp, in[b:i], op.alloc) if i == len(in) && term != nil { - return exp, i, errEndOfInput + return exp, i, fmt.Errorf("parse: unexpected end of input: %q %d [%q]", in, i, term) } return compactExpr(exp), i, nil } @@ -433,9 +445,10 @@ func parseDollar(in []byte, alloc bool) (Value, int, error) { term := []byte{paren, ':', ' '} var varname expr i := 2 + op := parseOp{alloc: alloc} Again: for { - e, n, err := parseExpr(in[i:], term, alloc) + e, n, err := parseExpr(in[i:], term, op) if err != nil { return nil, 0, err } @@ -457,7 +470,7 @@ Again: case literal, tmpval: funcName := intern(token.String()) if f, ok := funcMap[funcName]; ok { - return parseFunc(f(), in, i+1, term[:1], funcName, alloc) + return parseFunc(f(), in, i+1, term[:1], funcName, op.alloc) } } term = term[:2] // drop ' ' @@ -465,20 +478,21 @@ Again: case ':': // ${varname:...} colon := in[i : i+1] - term = term[:2] - term[1] = '=' // term={paren, '='}. - e, n, err := parseExpr(in[i+1:], term, alloc) + var vterm []byte + vterm = append(vterm, term[:2]...) + vterm[1] = '=' // term={paren, '='}. + e, n, err := parseExpr(in[i+1:], vterm, op) if err != nil { return nil, 0, err } i += 1 + n if in[i] == paren { - varname = appendStr(varname, colon, alloc) + varname = appendStr(varname, colon, op.alloc) return &varref{varname: varname, paren: oparen}, i + 1, nil } // ${varname:xx=...} pat := e - subst, n, err := parseExpr(in[i+1:], term[:1], alloc) + subst, n, err := parseExpr(in[i+1:], term[:1], op) if err != nil { return nil, 0, err } @@ -600,12 +614,13 @@ func parseFunc(f mkFunc, in []byte, s int, term []byte, funcName string, alloc b return f, i, nil } narg := 1 + op := parseOp{alloc: alloc, matchParen: true} for { if arity != 0 && narg >= arity { // final arguments. term = term[:1] // drop ',' } - v, n, err := parseExpr(in[i:], term, alloc) + v, n, err := parseExpr(in[i:], term, op) if err != nil { return nil, 0, err } diff --git a/expr_test.go b/expr_test.go index 7a38cf5..ad51a89 100644 --- a/expr_test.go +++ b/expr_test.go @@ -290,7 +290,7 @@ func TestParseExpr(t *testing.T) { }, }, } { - val, _, err := parseExpr([]byte(tc.in), nil, true) + val, _, err := parseExpr([]byte(tc.in), nil, parseOp{alloc: true}) if tc.isErr { if err == nil { t.Errorf(`parseExpr(%q)=_, _, nil; want error`, tc.in) @@ -1240,7 +1240,7 @@ func (f *funcEvalAssign) Eval(w io.Writer, ev *Evaluator) error { case ":=": // TODO(ukai): compute parsed expr in Compact when f.rhs is // literal? e.g. literal("$(foo)") => varref{literal("foo")}. - exp, _, err := parseExpr(rhs, nil, false) + exp, _, err := parseExpr(rhs, nil, parseOp{}) if err != nil { return ev.errorf("eval assign error: %q: %v", f.String(), err) } @@ -167,11 +167,11 @@ func (p *parser) processRecipeLine(line []byte) []byte { } func newAssignAST(p *parser, lhsBytes []byte, rhsBytes []byte, op string) (*assignAST, error) { - lhs, _, err := parseExpr(lhsBytes, nil, true) + lhs, _, err := parseExpr(lhsBytes, nil, parseOp{alloc: true}) if err != nil { return nil, err } - rhs, _, err := parseExpr(rhsBytes, nil, true) + rhs, _, err := parseExpr(rhsBytes, nil, parseOp{alloc: true}) if err != nil { return nil, err } @@ -224,7 +224,7 @@ func (p *parser) parseMaybeRule(line []byte, equalIndex, semicolonIndex int) (as term = '=' } - v, _, err := parseExpr(expr, nil, true) + v, _, err := parseExpr(expr, nil, parseOp{alloc: true}) if err != nil { return nil, p.srcpos().error(err) } @@ -249,7 +249,7 @@ func (p *parser) parseInclude(line string, oplen int) { } func (p *parser) parseIfdef(line []byte, oplen int) { - lhs, _, err := parseExpr(trimLeftSpaceBytes(line[oplen+1:]), nil, true) + lhs, _, err := parseExpr(trimLeftSpaceBytes(line[oplen+1:]), nil, parseOp{alloc: true}) if err != nil { p.err = p.srcpos().error(err) return @@ -296,14 +296,14 @@ func (p *parser) parseEq(s string, op string) (string, string, bool, error) { s = s[1 : len(s)-1] term := []byte{','} in := []byte(s) - v, n, err := parseExpr(in, term, false) + v, n, err := parseExpr(in, term, parseOp{matchParen: true}) if err != nil { return "", "", false, err } lhs := v.String() n++ n += skipSpaces(in[n:], nil) - v, n, err = parseExpr(in[n:], nil, false) + v, n, err = parseExpr(in[n:], nil, parseOp{matchParen: true}) if err != nil { return "", "", false, err } @@ -329,12 +329,12 @@ func (p *parser) parseIfeq(line string, oplen int) { return } - lhs, _, err := parseExpr([]byte(lhsBytes), nil, true) + lhs, _, err := parseExpr([]byte(lhsBytes), nil, parseOp{matchParen: true}) if err != nil { p.err = p.srcpos().error(err) return } - rhs, _, err := parseExpr([]byte(rhsBytes), nil, true) + rhs, _, err := parseExpr([]byte(rhsBytes), nil, parseOp{matchParen: true}) if err != nil { p.err = p.srcpos().error(err) return diff --git a/testcase/unmatched_paren.mk b/testcase/unmatched_paren.mk index 6f5d6c8..38c8954 100644 --- a/testcase/unmatched_paren.mk +++ b/testcase/unmatched_paren.mk @@ -1,5 +1,3 @@ -# TODO(go): Fix. - PAREN:=( $(PAREN):=PASS @@ -115,7 +115,7 @@ func (v *simpleVar) dump(d *dumpbuf) { } func (v *simpleVar) Append(ev *Evaluator, s string) (Var, error) { - val, _, err := parseExpr([]byte(s), nil, false) + val, _, err := parseExpr([]byte(s), nil, parseOp{}) if err != nil { return nil, err } @@ -165,7 +165,7 @@ func (v *automaticVar) dump(d *dumpbuf) { } func (v *automaticVar) Append(ev *Evaluator, s string) (Var, error) { - val, _, err := parseExpr([]byte(s), nil, false) + val, _, err := parseExpr([]byte(s), nil, parseOp{}) if err != nil { return nil, err } @@ -230,7 +230,7 @@ func (v *recursiveVar) Append(_ *Evaluator, s string) (Var, error) { } else { exp = expr{v.expr, literal(" ")} } - sv, _, err := parseExpr([]byte(s), nil, true) + sv, _, err := parseExpr([]byte(s), nil, parseOp{alloc: true}) if err != nil { return nil, err } @@ -248,7 +248,7 @@ func (v *recursiveVar) AppendVar(ev *Evaluator, val Value) (Var, error) { buf.WriteString(v.expr.String()) buf.WriteByte(' ') buf.WriteString(val.String()) - e, _, err := parseExpr(buf.Bytes(), nil, true) + e, _, err := parseExpr(buf.Bytes(), nil, parseOp{alloc: true}) if err != nil { return nil, err } |