diff --git a/ast.go b/ast.go index 8620720..1f3e16d 100644 --- a/ast.go +++ b/ast.go @@ -45,19 +45,19 @@ func (expr *ast) String() string { func (expr *ast) addTokens(tokens ...*Token) (err error) { for _, tk := range tokens { - if err = expr.addToken(tk); err != nil { + if _, err = expr.addToken(tk); err != nil { break } } return } -func (expr *ast) addToken(tk *Token) (err error) { - _, err = expr.addToken2(tk) - return -} +// func (expr *ast) addToken(tk *Token) (err error) { +// _, err = expr.addToken2(tk) +// return +// } -func (expr *ast) addToken2(tk *Token) (t *term, err error) { +func (expr *ast) addToken(tk *Token) (t *term, err error) { if t = newTerm(tk); t != nil { err = expr.addTerm(t) } else { diff --git a/parser.go b/parser.go index a424462..08abf27 100644 --- a/parser.go +++ b/parser.go @@ -19,7 +19,8 @@ func NewParser() (p *parser) { } func (parser *parser) Next(scanner *scanner) (tk *Token) { - for tk=scanner.Next(); tk.IsSymbol(SymComment); tk=scanner.Next() {} + for tk = scanner.Next(); tk.IsSymbol(SymComment); tk = scanner.Next() { + } return } @@ -304,7 +305,7 @@ func addSelectorCase(selectorTerm, caseTerm *term) { func (parser *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) { var caseTerm *term tk := scanner.makeToken(SymSelector, '?') - if selectorTerm, err = tree.addToken2(tk); err != nil { + if selectorTerm, err = tree.addToken(tk); err != nil { return } @@ -342,13 +343,14 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR var selectorTerm *term = nil var currentTerm *term = nil var tk *Token + tree = NewAst() firstToken := true // lastSym := SymUnknown for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = parser.Next(scanner) { - if tk.Sym == SymComment { - continue - } + // if tk.Sym == SymComment { + // continue + // } if tk.Sym == SymSemiColon { if allowForest { @@ -414,7 +416,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR } case SymEqual: // if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil { - currentTerm, err = tree.addToken2(tk) + currentTerm, err = tree.addToken(tk) // } case SymFuncDef: var funcDefTerm *term @@ -432,7 +434,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR if tk.source[0] == '@' && !allowVarRef { err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source) } else { - currentTerm, err = tree.addToken2(tk) + currentTerm, err = tree.addToken(tk) } case SymQuestion: if selectorTerm, err = parser.parseSelector(scanner, tree, allowVarRef); err == nil { @@ -449,14 +451,16 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR } } } else { - currentTerm, err = tree.addToken2(tk) + currentTerm, err = tree.addToken(tk) } if tk.IsSymbol(SymColon) { // Colon outside a selector term acts like a separator firstToken = true } + case SymPlusEqual, SymMinusEqual, SymStarEqual: + currentTerm, err = parser.expandOpAssign(scanner, tree, tk, allowVarRef) default: - currentTerm, err = tree.addToken2(tk) + currentTerm, err = tree.addToken(tk) } if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector { @@ -465,6 +469,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR } // lastSym = tk.Sym } + if err == nil { err = tk.Error() } @@ -477,3 +482,49 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR // } // return // } + +func (parser *parser) expandOpAssign(scanner * scanner, tree *ast, tk *Token, allowVarRef bool) (t *term, err error) { + var opSym Symbol + var opString string + + if tree.root != nil { + switch tk.Sym { + case SymPlusEqual: + opString = "+" + opSym = SymPlus + case SymMinusEqual: + opString = "-" + opSym = SymMinus + case SymStarEqual: + opString = "*" + opSym = SymStar + default: + err = tk.Errorf("unsopported operator %q", tk.source) + return + } + leftExpr := tree.root.Clone() + leftExpr.setParent(nil) + if t, err = tree.addToken(NewToken(tk.row, tk.col, SymEqual, "=")); err == nil { + t = leftExpr + if err = tree.addTerm(leftExpr); err == nil { + if t, err = tree.addToken(NewToken(tk.row, tk.col, opSym, opString)); err == nil { + + var subTree *ast + if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymSemiColon, SymClosedRound); err == nil { + if scanner.Previous().IsOneOfA(SymSemiColon, SymClosedRound) { + if err = scanner.UnreadToken(); err != nil { + return + } + } + subTree.root.priority = priValue + err = tree.addTerm(newExprTerm(subTree.root)) + t = subTree.root + } + } + } + } + } else { + err = tk.Errorf("left operand of %q must be a variable or a variable expression", tk) + } + return +} diff --git a/t_ast_test.go b/t_ast_test.go index cc5e499..45ed9a6 100644 --- a/t_ast_test.go +++ b/t_ast_test.go @@ -47,7 +47,7 @@ func TestAddUnknownTokens(t *testing.T) { wantErr := errors.New(`unexpected token "%"`) tree := NewAst() - if gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() { + if _, gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() { t.Errorf("err: got <%v>, want <%v>", gotErr, wantErr) } } diff --git a/t_expr_test.go b/t_expr_test.go index d58389b..be1f009 100644 --- a/t_expr_test.go +++ b/t_expr_test.go @@ -17,7 +17,17 @@ func TestExpr(t *testing.T) { /* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil}, /* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil}, /* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil}, - /* 6 */ {` + /* 6 */ {`a=3; a+=1`, int64(4), nil}, + /* 7 */ {`a=3; a-=1`, int64(2), nil}, + /* 8 */ {`a=3; a*=2`, int64(6), nil}, + /* 9 */ {`v=[10,20,30]; v[0]+=1; v[0]`, int64(11), nil}, + /* 10 */ {`r={"a":10}; r["a"]+=1; r["a"]`, int64(11), nil}, + /* 11 */ {`a=3; a/=2`, nil, `[1:8] left operand of "=" must be a variable or a collection's item`}, + /* 12 */ {`*=2`, nil, `[1:2] left operand of "*=" must be a variable or a variable expression`}, + /* 13 */ {`a=3; a*=2+1; a`, int64(9), nil}, + /* 14 */ {`a=3; (a*=2)+1; a`, int64(6), nil}, + /* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`}, + /* 16 */ {` ds={ "init":func(@end){@current=0 but true}, //"current":func(){current}, @@ -32,6 +42,6 @@ func TestExpr(t *testing.T) { } // t.Setenv("EXPR_PATH", ".") - //runTestSuiteSpec(t, section, inputs, 6) + //runTestSuiteSpec(t, section, inputs, 9) runTestSuite(t, section, inputs) }