diff --git a/config/lang/ast/unary_arithmetic.go b/config/lang/ast/unary_arithmetic.go new file mode 100644 index 000000000000..d6b65b36529b --- /dev/null +++ b/config/lang/ast/unary_arithmetic.go @@ -0,0 +1,42 @@ +package ast + +import ( + "fmt" +) + +// UnaryArithmetic represents a node where the result is arithmetic of +// one operands +type UnaryArithmetic struct { + Op ArithmeticOp + Expr Node + Posx Pos +} + +func (n *UnaryArithmetic) Accept(v Visitor) Node { + n.Expr = n.Expr.Accept(v) + + return v(n) +} + +func (n *UnaryArithmetic) Pos() Pos { + return n.Posx +} + +func (n *UnaryArithmetic) GoString() string { + return fmt.Sprintf("*%#v", *n) +} + +func (n *UnaryArithmetic) String() string { + var sign rune + switch n.Op { + case ArithmeticOpAdd: + sign = '+' + case ArithmeticOpSub: + sign = '-' + } + return fmt.Sprintf("%c%s", sign, n.Expr) +} + +func (n *UnaryArithmetic) Type(Scope) (Type, error) { + return TypeInt, nil +} diff --git a/config/lang/builtins.go b/config/lang/builtins.go index bf918c9c75a6..457a5ef3720f 100644 --- a/config/lang/builtins.go +++ b/config/lang/builtins.go @@ -24,11 +24,53 @@ func registerBuiltins(scope *ast.BasicScope) *ast.BasicScope { scope.FuncMap["__builtin_StringToInt"] = builtinStringToInt() // Math operations + scope.FuncMap["__builtin_UnaryIntMath"] = builtinUnaryIntMath() + scope.FuncMap["__builtin_UnaryFloatMath"] = builtinUnaryFloatMath() scope.FuncMap["__builtin_IntMath"] = builtinIntMath() scope.FuncMap["__builtin_FloatMath"] = builtinFloatMath() return scope } +func builtinUnaryIntMath() ast.Function { + return ast.Function{ + ArgTypes: []ast.Type{ast.TypeInt}, + Variadic: false, + ReturnType: ast.TypeInt, + Callback: func(args []interface{}) (interface{}, error) { + op := args[0].(ast.ArithmeticOp) + result := args[1].(int) + switch op { + case ast.ArithmeticOpAdd: + result = result + case ast.ArithmeticOpSub: + result = -result + } + + return result, nil + }, + } +} + +func builtinUnaryFloatMath() ast.Function { + return ast.Function{ + ArgTypes: []ast.Type{ast.TypeFloat}, + Variadic: false, + ReturnType: ast.TypeFloat, + Callback: func(args []interface{}) (interface{}, error) { + op := args[0].(ast.ArithmeticOp) + result := args[1].(float64) + switch op { + case ast.ArithmeticOpAdd: + result = result + case ast.ArithmeticOpSub: + result = -result + } + + return result, nil + }, + } +} + func builtinFloatMath() ast.Function { return ast.Function{ ArgTypes: []ast.Type{ast.TypeInt}, diff --git a/config/lang/check_types.go b/config/lang/check_types.go index 4fbcd731adde..0ff6ac93ba63 100644 --- a/config/lang/check_types.go +++ b/config/lang/check_types.go @@ -55,6 +55,9 @@ func (v *TypeCheck) visit(raw ast.Node) ast.Node { var result ast.Node var err error switch n := raw.(type) { + case *ast.UnaryArithmetic: + tc := &typeCheckUnaryArithmetic{n} + result, err = tc.TypeCheck(v) case *ast.Arithmetic: tc := &typeCheckArithmetic{n} result, err = tc.TypeCheck(v) @@ -89,6 +92,48 @@ func (v *TypeCheck) visit(raw ast.Node) ast.Node { return result } +type typeCheckUnaryArithmetic struct { + n *ast.UnaryArithmetic +} + +func (tc *typeCheckUnaryArithmetic) TypeCheck(v *TypeCheck) (ast.Node, error) { + // Only support + or - as unary op + if tc.n.Op != ast.ArithmeticOpAdd && tc.n.Op != ast.ArithmeticOpSub { + fmt.Printf("%+v\n", tc.n.Op) + return nil, fmt.Errorf("only + or - supported as unary operator") + } + expr := v.StackPop() + + mathFunc := "__builtin_UnaryIntMath" + mathType := ast.TypeInt + switch expr { + case ast.TypeInt: + mathFunc = "__builtin_UnaryIntMath" + mathType = expr + case ast.TypeFloat: + mathFunc = "__builtin_UnaryFloatMath" + mathType = expr + } + + // Return type + v.StackPush(mathType) + + args := make([]ast.Node, 2) + args[0] = &ast.LiteralNode{ + Value: tc.n.Op, + Typex: ast.TypeInt, + Posx: tc.n.Pos(), + } + args[1] = tc.n.Expr + // Replace our node with a call to the proper function. This isn't + // type checked but we already verified types. + return &ast.Call{ + Func: mathFunc, + Args: args, + Posx: tc.n.Pos(), + }, nil +} + type typeCheckArithmetic struct { n *ast.Arithmetic } diff --git a/config/lang/eval_test.go b/config/lang/eval_test.go index 122f44d1f4ac..63c7ce984b57 100644 --- a/config/lang/eval_test.go +++ b/config/lang/eval_test.go @@ -251,6 +251,60 @@ func TestEval(t *testing.T) { "foo 43", ast.TypeString, }, + + { + "foo ${-46}", + nil, + false, + "foo -46", + ast.TypeString, + }, + + { + "foo ${-46 + 5}", + nil, + false, + "foo -41", + ast.TypeString, + }, + + { + "foo ${46 + -5}", + nil, + false, + "foo 41", + ast.TypeString, + }, + + { + "foo ${-bar}", + &ast.BasicScope{ + VarMap: map[string]ast.Variable{ + "bar": ast.Variable{ + Value: 41, + Type: ast.TypeInt, + }, + }, + }, + false, + "foo -41", + ast.TypeString, + }, + + { + "foo ${5 + -bar}", + &ast.BasicScope{ + VarMap: map[string]ast.Variable{ + "bar": ast.Variable{ + Value: 41, + Type: ast.TypeInt, + }, + }, + }, + false, + "foo -36", + ast.TypeString, + }, } for _, tc := range cases { diff --git a/config/lang/lang.y b/config/lang/lang.y index c531860e51fa..f55f7bf98227 100644 --- a/config/lang/lang.y +++ b/config/lang/lang.y @@ -130,6 +130,14 @@ expr: Posx: $1.Pos(), } } +| ARITH_OP expr + { + $$ = &ast.UnaryArithmetic{ + Op: $1.Value.(ast.ArithmeticOp), + Expr: $2, + Posx: $1.Pos, + } + } | IDENTIFIER { $$ = &ast.VariableAccess{Name: $1.Value.(string), Posx: $1.Pos} diff --git a/config/lang/lex_test.go b/config/lang/lex_test.go index 5341e594a6fa..572aa0f532ec 100644 --- a/config/lang/lex_test.go +++ b/config/lang/lex_test.go @@ -63,6 +63,20 @@ func TestLex(t *testing.T) { PROGRAM_BRACKET_RIGHT, lexEOF}, }, + { + "${bar(-42)}", + []int{PROGRAM_BRACKET_LEFT, + IDENTIFIER, PAREN_LEFT, ARITH_OP, INTEGER, PAREN_RIGHT, + PROGRAM_BRACKET_RIGHT, lexEOF}, + }, + + { + "${bar(-42.0)}", + []int{PROGRAM_BRACKET_LEFT, + IDENTIFIER, PAREN_LEFT, ARITH_OP, FLOAT, PAREN_RIGHT, + PROGRAM_BRACKET_RIGHT, lexEOF}, + }, + { "${bar(42+1)}", []int{PROGRAM_BRACKET_LEFT, @@ -72,6 +86,15 @@ func TestLex(t *testing.T) { PROGRAM_BRACKET_RIGHT, lexEOF}, }, + { + "${bar(42+-1)}", + []int{PROGRAM_BRACKET_LEFT, + IDENTIFIER, PAREN_LEFT, + INTEGER, ARITH_OP, ARITH_OP, INTEGER, + PAREN_RIGHT, + PROGRAM_BRACKET_RIGHT, lexEOF}, + }, + { "${bar(3.14159)}", []int{PROGRAM_BRACKET_LEFT, diff --git a/config/lang/y.go b/config/lang/y.go index e7dd185ae1ef..faffd55d31db 100644 --- a/config/lang/y.go +++ b/config/lang/y.go @@ -30,7 +30,10 @@ const INTEGER = 57355 const FLOAT = 57356 const STRING = 57357 -var parserToknames = []string{ +var parserToknames = [...]string{ + "$end", + "error", + "$unk", "PROGRAM_BRACKET_LEFT", "PROGRAM_BRACKET_RIGHT", "PROGRAM_STRING_START", @@ -44,98 +47,127 @@ var parserToknames = []string{ "FLOAT", "STRING", } -var parserStatenames = []string{} +var parserStatenames = [...]string{} const parserEofCode = 1 const parserErrCode = 2 const parserMaxDepth = 200 -//line lang.y:165 +//line lang.y:173 //line yacctab:1 -var parserExca = []int{ +var parserExca = [...]int{ -1, 1, 1, -1, -2, 0, } -const parserNprod = 19 +const parserNprod = 20 const parserPrivate = 57344 var parserTokenNames []string var parserStates []string -const parserLast = 30 +const parserLast = 34 -var parserAct = []int{ +var parserAct = [...]int{ - 9, 20, 16, 16, 7, 7, 3, 18, 10, 8, - 1, 17, 14, 12, 13, 6, 6, 19, 8, 22, - 15, 23, 24, 11, 2, 25, 16, 21, 4, 5, + 9, 7, 3, 16, 22, 8, 17, 17, 20, 17, + 1, 18, 6, 23, 8, 19, 25, 26, 21, 11, + 2, 24, 7, 4, 5, 0, 10, 27, 0, 14, + 15, 12, 13, 6, } -var parserPact = []int{ +var parserPact = [...]int{ - 1, -1000, 1, -1000, -1000, -1000, -1000, 0, -1000, 15, - 0, 1, -1000, -1000, -1, -1000, 0, -8, 0, -1000, - -1000, 12, -9, -1000, 0, -9, + -3, -1000, -3, -1000, -1000, -1000, -1000, 18, -1000, -2, + 18, -3, -1000, -1000, 18, 0, -1000, 18, -5, -1000, + 18, -1000, -1000, 7, -4, -1000, 18, -4, } -var parserPgo = []int{ +var parserPgo = [...]int{ - 0, 0, 29, 28, 23, 6, 27, 10, + 0, 0, 24, 23, 19, 2, 13, 10, } -var parserR1 = []int{ +var parserR1 = [...]int{ 0, 7, 7, 4, 4, 5, 5, 2, 1, 1, - 1, 1, 1, 1, 1, 6, 6, 6, 3, + 1, 1, 1, 1, 1, 1, 6, 6, 6, 3, } -var parserR2 = []int{ +var parserR2 = [...]int{ 0, 0, 1, 1, 2, 1, 1, 3, 3, 1, - 1, 1, 3, 1, 4, 0, 3, 1, 1, + 1, 1, 3, 2, 1, 4, 0, 3, 1, 1, } -var parserChk = []int{ +var parserChk = [...]int{ -1000, -7, -4, -5, -3, -2, 15, 4, -5, -1, - 8, -4, 13, 14, 12, 5, 11, -1, 8, -1, - 9, -6, -1, 9, 10, -1, + 8, -4, 13, 14, 11, 12, 5, 11, -1, -1, + 8, -1, 9, -6, -1, 9, 10, -1, } -var parserDef = []int{ +var parserDef = [...]int{ - 1, -2, 2, 3, 5, 6, 18, 0, 4, 0, - 0, 9, 10, 11, 13, 7, 0, 0, 15, 12, - 8, 0, 17, 14, 0, 16, + 1, -2, 2, 3, 5, 6, 19, 0, 4, 0, + 0, 9, 10, 11, 0, 14, 7, 0, 0, 13, + 16, 12, 8, 0, 18, 15, 0, 17, } -var parserTok1 = []int{ +var parserTok1 = [...]int{ 1, } -var parserTok2 = []int{ +var parserTok2 = [...]int{ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, } -var parserTok3 = []int{ +var parserTok3 = [...]int{ 0, } +var parserErrorMessages = [...]struct { + state int + token int + msg string +}{} + //line yaccpar:1 /* parser for yacc output */ -var parserDebug = 0 +var ( + parserDebug = 0 + parserErrorVerbose = false +) type parserLexer interface { Lex(lval *parserSymType) int Error(s string) } +type parserParser interface { + Parse(parserLexer) int + Lookahead() int +} + +type parserParserImpl struct { + lookahead func() int +} + +func (p *parserParserImpl) Lookahead() int { + return p.lookahead() +} + +func parserNewParser() parserParser { + p := &parserParserImpl{ + lookahead: func() int { return -1 }, + } + return p +} + const parserFlag = -1000 func parserTokname(c int) string { - // 4 is TOKSTART above - if c >= 4 && c-4 < len(parserToknames) { - if parserToknames[c-4] != "" { - return parserToknames[c-4] + if c >= 1 && c-1 < len(parserToknames) { + if parserToknames[c-1] != "" { + return parserToknames[c-1] } } return __yyfmt__.Sprintf("tok-%v", c) @@ -150,51 +182,129 @@ func parserStatname(s int) string { return __yyfmt__.Sprintf("state-%v", s) } -func parserlex1(lex parserLexer, lval *parserSymType) int { - c := 0 - char := lex.Lex(lval) +func parserErrorMessage(state, lookAhead int) string { + const TOKSTART = 4 + + if !parserErrorVerbose { + return "syntax error" + } + + for _, e := range parserErrorMessages { + if e.state == state && e.token == lookAhead { + return "syntax error: " + e.msg + } + } + + res := "syntax error: unexpected " + parserTokname(lookAhead) + + // To match Bison, suggest at most four expected tokens. + expected := make([]int, 0, 4) + + // Look for shiftable tokens. + base := parserPact[state] + for tok := TOKSTART; tok-1 < len(parserToknames); tok++ { + if n := base + tok; n >= 0 && n < parserLast && parserChk[parserAct[n]] == tok { + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + } + + if parserDef[state] == -2 { + i := 0 + for parserExca[i] != -1 || parserExca[i+1] != state { + i += 2 + } + + // Look for tokens that we accept or reduce. + for i += 2; parserExca[i] >= 0; i += 2 { + tok := parserExca[i] + if tok < TOKSTART || parserExca[i+1] == 0 { + continue + } + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + + // If the default action is to accept or reduce, give up. + if parserExca[i+1] != 0 { + return res + } + } + + for i, tok := range expected { + if i == 0 { + res += ", expecting " + } else { + res += " or " + } + res += parserTokname(tok) + } + return res +} + +func parserlex1(lex parserLexer, lval *parserSymType) (char, token int) { + token = 0 + char = lex.Lex(lval) if char <= 0 { - c = parserTok1[0] + token = parserTok1[0] goto out } if char < len(parserTok1) { - c = parserTok1[char] + token = parserTok1[char] goto out } if char >= parserPrivate { if char < parserPrivate+len(parserTok2) { - c = parserTok2[char-parserPrivate] + token = parserTok2[char-parserPrivate] goto out } } for i := 0; i < len(parserTok3); i += 2 { - c = parserTok3[i+0] - if c == char { - c = parserTok3[i+1] + token = parserTok3[i+0] + if token == char { + token = parserTok3[i+1] goto out } } out: - if c == 0 { - c = parserTok2[1] /* unknown char */ + if token == 0 { + token = parserTok2[1] /* unknown char */ } if parserDebug >= 3 { - __yyfmt__.Printf("lex %s(%d)\n", parserTokname(c), uint(char)) + __yyfmt__.Printf("lex %s(%d)\n", parserTokname(token), uint(char)) } - return c + return char, token } func parserParse(parserlex parserLexer) int { + return parserNewParser().Parse(parserlex) +} + +func (parserrcvr *parserParserImpl) Parse(parserlex parserLexer) int { var parsern int var parserlval parserSymType var parserVAL parserSymType + var parserDollar []parserSymType + _ = parserDollar // silence set and not used parserS := make([]parserSymType, parserMaxDepth) Nerrs := 0 /* number of errors */ Errflag := 0 /* error recovery flag */ parserstate := 0 parserchar := -1 + parsertoken := -1 // parserchar translated into internal numbering + parserrcvr.lookahead = func() int { return parserchar } + defer func() { + // Make sure we report no lookahead when not parsing. + parserstate = -1 + parserchar = -1 + parsertoken = -1 + }() parserp := -1 goto parserstack @@ -207,7 +317,7 @@ ret1: parserstack: /* put a state and value onto the stack */ if parserDebug >= 4 { - __yyfmt__.Printf("char %v in %v\n", parserTokname(parserchar), parserStatname(parserstate)) + __yyfmt__.Printf("char %v in %v\n", parserTokname(parsertoken), parserStatname(parserstate)) } parserp++ @@ -225,15 +335,16 @@ parsernewstate: goto parserdefault /* simple state */ } if parserchar < 0 { - parserchar = parserlex1(parserlex, &parserlval) + parserchar, parsertoken = parserlex1(parserlex, &parserlval) } - parsern += parserchar + parsern += parsertoken if parsern < 0 || parsern >= parserLast { goto parserdefault } parsern = parserAct[parsern] - if parserChk[parsern] == parserchar { /* valid shift */ + if parserChk[parsern] == parsertoken { /* valid shift */ parserchar = -1 + parsertoken = -1 parserVAL = parserlval parserstate = parsern if Errflag > 0 { @@ -247,7 +358,7 @@ parserdefault: parsern = parserDef[parserstate] if parsern == -2 { if parserchar < 0 { - parserchar = parserlex1(parserlex, &parserlval) + parserchar, parsertoken = parserlex1(parserlex, &parserlval) } /* look through exception table */ @@ -260,7 +371,7 @@ parserdefault: } for xi += 2; ; xi += 2 { parsern = parserExca[xi+0] - if parsern < 0 || parsern == parserchar { + if parsern < 0 || parsern == parsertoken { break } } @@ -273,11 +384,11 @@ parserdefault: /* error ... attempt to resume parsing */ switch Errflag { case 0: /* brand new error */ - parserlex.Error("syntax error") + parserlex.Error(parserErrorMessage(parserstate, parsertoken)) Nerrs++ if parserDebug >= 1 { __yyfmt__.Printf("%s", parserStatname(parserstate)) - __yyfmt__.Printf(" saw %s\n", parserTokname(parserchar)) + __yyfmt__.Printf(" saw %s\n", parserTokname(parsertoken)) } fallthrough @@ -305,12 +416,13 @@ parserdefault: case 3: /* no shift yet; clobber input char */ if parserDebug >= 2 { - __yyfmt__.Printf("error recovery discards %s\n", parserTokname(parserchar)) + __yyfmt__.Printf("error recovery discards %s\n", parserTokname(parsertoken)) } - if parserchar == parserEofCode { + if parsertoken == parserEofCode { goto ret1 } parserchar = -1 + parsertoken = -1 goto parsernewstate /* try again in the same state */ } } @@ -325,6 +437,13 @@ parserdefault: _ = parserpt // guard against "declared and not used" parserp -= parserR2[parsern] + // parserp is now the index of $0. Perform the default action. Iff the + // reduced production is ε, $1 is possibly out of range. + if parserp+1 >= len(parserS) { + nyys := make([]parserSymType, len(parserS)*2) + copy(nyys, parserS) + parserS = nyys + } parserVAL = parserS[parserp+1] /* consult goto table to find next state */ @@ -344,6 +463,7 @@ parserdefault: switch parsernt { case 1: + parserDollar = parserS[parserpt-0 : parserpt+1] //line lang.y:35 { parserResult = &ast.LiteralNode{ @@ -353,9 +473,10 @@ parserdefault: } } case 2: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:43 { - parserResult = parserS[parserpt-0].node + parserResult = parserDollar[1].node // We want to make sure that the top value is always a Concat // so that the return value is always a string type from an @@ -365,28 +486,30 @@ parserdefault: // because functionally the AST is the same, but we do that because // it makes for an easy literal check later (to check if a string // has any interpolations). - if _, ok := parserS[parserpt-0].node.(*ast.Concat); !ok { - if n, ok := parserS[parserpt-0].node.(*ast.LiteralNode); !ok || n.Typex != ast.TypeString { + if _, ok := parserDollar[1].node.(*ast.Concat); !ok { + if n, ok := parserDollar[1].node.(*ast.LiteralNode); !ok || n.Typex != ast.TypeString { parserResult = &ast.Concat{ - Exprs: []ast.Node{parserS[parserpt-0].node}, - Posx: parserS[parserpt-0].node.Pos(), + Exprs: []ast.Node{parserDollar[1].node}, + Posx: parserDollar[1].node.Pos(), } } } } case 3: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:66 { - parserVAL.node = parserS[parserpt-0].node + parserVAL.node = parserDollar[1].node } case 4: + parserDollar = parserS[parserpt-2 : parserpt+1] //line lang.y:70 { var result []ast.Node - if c, ok := parserS[parserpt-1].node.(*ast.Concat); ok { - result = append(c.Exprs, parserS[parserpt-0].node) + if c, ok := parserDollar[1].node.(*ast.Concat); ok { + result = append(c.Exprs, parserDollar[2].node) } else { - result = []ast.Node{parserS[parserpt-1].node, parserS[parserpt-0].node} + result = []ast.Node{parserDollar[1].node, parserDollar[2].node} } parserVAL.node = &ast.Concat{ @@ -395,89 +518,113 @@ parserdefault: } } case 5: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:86 { - parserVAL.node = parserS[parserpt-0].node + parserVAL.node = parserDollar[1].node } case 6: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:90 { - parserVAL.node = parserS[parserpt-0].node + parserVAL.node = parserDollar[1].node } case 7: + parserDollar = parserS[parserpt-3 : parserpt+1] //line lang.y:96 { - parserVAL.node = parserS[parserpt-1].node + parserVAL.node = parserDollar[2].node } case 8: + parserDollar = parserS[parserpt-3 : parserpt+1] //line lang.y:102 { - parserVAL.node = parserS[parserpt-1].node + parserVAL.node = parserDollar[2].node } case 9: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:106 { - parserVAL.node = parserS[parserpt-0].node + parserVAL.node = parserDollar[1].node } case 10: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:110 { parserVAL.node = &ast.LiteralNode{ - Value: parserS[parserpt-0].token.Value.(int), + Value: parserDollar[1].token.Value.(int), Typex: ast.TypeInt, - Posx: parserS[parserpt-0].token.Pos, + Posx: parserDollar[1].token.Pos, } } case 11: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:118 { parserVAL.node = &ast.LiteralNode{ - Value: parserS[parserpt-0].token.Value.(float64), + Value: parserDollar[1].token.Value.(float64), Typex: ast.TypeFloat, - Posx: parserS[parserpt-0].token.Pos, + Posx: parserDollar[1].token.Pos, } } case 12: + parserDollar = parserS[parserpt-3 : parserpt+1] //line lang.y:126 { parserVAL.node = &ast.Arithmetic{ - Op: parserS[parserpt-1].token.Value.(ast.ArithmeticOp), - Exprs: []ast.Node{parserS[parserpt-2].node, parserS[parserpt-0].node}, - Posx: parserS[parserpt-2].node.Pos(), + Op: parserDollar[2].token.Value.(ast.ArithmeticOp), + Exprs: []ast.Node{parserDollar[1].node, parserDollar[3].node}, + Posx: parserDollar[1].node.Pos(), } } case 13: + parserDollar = parserS[parserpt-2 : parserpt+1] //line lang.y:134 { - parserVAL.node = &ast.VariableAccess{Name: parserS[parserpt-0].token.Value.(string), Posx: parserS[parserpt-0].token.Pos} + parserVAL.node = &ast.UnaryArithmetic{ + Op: parserDollar[1].token.Value.(ast.ArithmeticOp), + Expr: parserDollar[2].node, + Posx: parserDollar[1].token.Pos, + } } case 14: - //line lang.y:138 + parserDollar = parserS[parserpt-1 : parserpt+1] + //line lang.y:142 { - parserVAL.node = &ast.Call{Func: parserS[parserpt-3].token.Value.(string), Args: parserS[parserpt-1].nodeList, Posx: parserS[parserpt-3].token.Pos} + parserVAL.node = &ast.VariableAccess{Name: parserDollar[1].token.Value.(string), Posx: parserDollar[1].token.Pos} } case 15: - //line lang.y:143 + parserDollar = parserS[parserpt-4 : parserpt+1] + //line lang.y:146 { - parserVAL.nodeList = nil + parserVAL.node = &ast.Call{Func: parserDollar[1].token.Value.(string), Args: parserDollar[3].nodeList, Posx: parserDollar[1].token.Pos} } case 16: - //line lang.y:147 + parserDollar = parserS[parserpt-0 : parserpt+1] + //line lang.y:151 { - parserVAL.nodeList = append(parserS[parserpt-2].nodeList, parserS[parserpt-0].node) + parserVAL.nodeList = nil } case 17: - //line lang.y:151 + parserDollar = parserS[parserpt-3 : parserpt+1] + //line lang.y:155 { - parserVAL.nodeList = append(parserVAL.nodeList, parserS[parserpt-0].node) + parserVAL.nodeList = append(parserDollar[1].nodeList, parserDollar[3].node) } case 18: - //line lang.y:157 + parserDollar = parserS[parserpt-1 : parserpt+1] + //line lang.y:159 + { + parserVAL.nodeList = append(parserVAL.nodeList, parserDollar[1].node) + } + case 19: + parserDollar = parserS[parserpt-1 : parserpt+1] + //line lang.y:165 { parserVAL.node = &ast.LiteralNode{ - Value: parserS[parserpt-0].token.Value.(string), + Value: parserDollar[1].token.Value.(string), Typex: ast.TypeString, - Posx: parserS[parserpt-0].token.Pos, + Posx: parserDollar[1].token.Pos, } } } diff --git a/config/lang/y.output b/config/lang/y.output index 17352390dd1b..998d2673cc1b 100644 --- a/config/lang/y.output +++ b/config/lang/y.output @@ -51,9 +51,9 @@ state 5 state 6 - literal: STRING. (18) + literal: STRING. (19) - . reduce 18 (src line 155) + . reduce 19 (src line 163) state 7 @@ -61,7 +61,8 @@ state 7 PROGRAM_BRACKET_LEFT shift 7 PAREN_LEFT shift 10 - IDENTIFIER shift 14 + ARITH_OP shift 14 + IDENTIFIER shift 15 INTEGER shift 12 FLOAT shift 13 STRING shift 6 @@ -83,8 +84,8 @@ state 9 interpolation: PROGRAM_BRACKET_LEFT expr.PROGRAM_BRACKET_RIGHT expr: expr.ARITH_OP expr - PROGRAM_BRACKET_RIGHT shift 15 - ARITH_OP shift 16 + PROGRAM_BRACKET_RIGHT shift 16 + ARITH_OP shift 17 . error @@ -93,13 +94,14 @@ state 10 PROGRAM_BRACKET_LEFT shift 7 PAREN_LEFT shift 10 - IDENTIFIER shift 14 + ARITH_OP shift 14 + IDENTIFIER shift 15 INTEGER shift 12 FLOAT shift 13 STRING shift 6 . error - expr goto 17 + expr goto 18 interpolation goto 5 literal goto 4 literalModeTop goto 11 @@ -130,134 +132,162 @@ state 13 state 14 - expr: IDENTIFIER. (13) - expr: IDENTIFIER.PAREN_LEFT args PAREN_RIGHT + expr: ARITH_OP.expr - PAREN_LEFT shift 18 - . reduce 13 (src line 133) + PROGRAM_BRACKET_LEFT shift 7 + PAREN_LEFT shift 10 + ARITH_OP shift 14 + IDENTIFIER shift 15 + INTEGER shift 12 + FLOAT shift 13 + STRING shift 6 + . error + expr goto 19 + interpolation goto 5 + literal goto 4 + literalModeTop goto 11 + literalModeValue goto 3 state 15 + expr: IDENTIFIER. (14) + expr: IDENTIFIER.PAREN_LEFT args PAREN_RIGHT + + PAREN_LEFT shift 20 + . reduce 14 (src line 141) + + +state 16 interpolation: PROGRAM_BRACKET_LEFT expr PROGRAM_BRACKET_RIGHT. (7) . reduce 7 (src line 94) -state 16 +state 17 expr: expr ARITH_OP.expr PROGRAM_BRACKET_LEFT shift 7 PAREN_LEFT shift 10 - IDENTIFIER shift 14 + ARITH_OP shift 14 + IDENTIFIER shift 15 INTEGER shift 12 FLOAT shift 13 STRING shift 6 . error - expr goto 19 + expr goto 21 interpolation goto 5 literal goto 4 literalModeTop goto 11 literalModeValue goto 3 -state 17 +state 18 expr: PAREN_LEFT expr.PAREN_RIGHT expr: expr.ARITH_OP expr - PAREN_RIGHT shift 20 - ARITH_OP shift 16 + PAREN_RIGHT shift 22 + ARITH_OP shift 17 . error -state 18 +state 19 + expr: expr.ARITH_OP expr + expr: ARITH_OP expr. (13) + + . reduce 13 (src line 133) + + +state 20 expr: IDENTIFIER PAREN_LEFT.args PAREN_RIGHT - args: . (15) + args: . (16) PROGRAM_BRACKET_LEFT shift 7 PAREN_LEFT shift 10 - IDENTIFIER shift 14 + ARITH_OP shift 14 + IDENTIFIER shift 15 INTEGER shift 12 FLOAT shift 13 STRING shift 6 - . reduce 15 (src line 142) + . reduce 16 (src line 150) - expr goto 22 + expr goto 24 interpolation goto 5 literal goto 4 literalModeTop goto 11 literalModeValue goto 3 - args goto 21 + args goto 23 -state 19 +state 21 expr: expr.ARITH_OP expr expr: expr ARITH_OP expr. (12) . reduce 12 (src line 125) -state 20 +state 22 expr: PAREN_LEFT expr PAREN_RIGHT. (8) . reduce 8 (src line 100) -state 21 +state 23 expr: IDENTIFIER PAREN_LEFT args.PAREN_RIGHT args: args.COMMA expr - PAREN_RIGHT shift 23 - COMMA shift 24 + PAREN_RIGHT shift 25 + COMMA shift 26 . error -state 22 +state 24 expr: expr.ARITH_OP expr - args: expr. (17) + args: expr. (18) - ARITH_OP shift 16 - . reduce 17 (src line 150) + ARITH_OP shift 17 + . reduce 18 (src line 158) -state 23 - expr: IDENTIFIER PAREN_LEFT args PAREN_RIGHT. (14) +state 25 + expr: IDENTIFIER PAREN_LEFT args PAREN_RIGHT. (15) - . reduce 14 (src line 137) + . reduce 15 (src line 145) -state 24 +state 26 args: args COMMA.expr PROGRAM_BRACKET_LEFT shift 7 PAREN_LEFT shift 10 - IDENTIFIER shift 14 + ARITH_OP shift 14 + IDENTIFIER shift 15 INTEGER shift 12 FLOAT shift 13 STRING shift 6 . error - expr goto 25 + expr goto 27 interpolation goto 5 literal goto 4 literalModeTop goto 11 literalModeValue goto 3 -state 25 +state 27 expr: expr.ARITH_OP expr - args: args COMMA expr. (16) + args: args COMMA expr. (17) - ARITH_OP shift 16 - . reduce 16 (src line 146) + ARITH_OP shift 17 + . reduce 17 (src line 154) 15 terminals, 8 nonterminals -19 grammar rules, 26/2000 states +20 grammar rules, 28/2000 states 0 shift/reduce, 0 reduce/reduce conflicts reported 57 working sets used -memory: parser 35/30000 -21 extra closures -45 shift entries, 1 exceptions -14 goto entries -23 entries saved by goto default -Optimizer space used: output 30/30000 -30 table entries, 0 zero -maximum spread: 15, maximum offset: 24 +memory: parser 40/30000 +23 extra closures +57 shift entries, 1 exceptions +15 goto entries +27 entries saved by goto default +Optimizer space used: output 34/30000 +34 table entries, 2 zero +maximum spread: 15, maximum offset: 26