diff --git a/config/lang/ast/ast.go b/config/lang/ast/ast.go index 343deb7031..a03070180b 100644 --- a/config/lang/ast/ast.go +++ b/config/lang/ast/ast.go @@ -39,4 +39,5 @@ const ( TypeInvalid Type = 0 TypeString = 1 << iota TypeInt + TypeFloat ) diff --git a/config/lang/lang.y b/config/lang/lang.y index d379a6a097..241b286fb2 100644 --- a/config/lang/lang.y +++ b/config/lang/lang.y @@ -22,7 +22,7 @@ import ( %token PROGRAM_STRING_START PROGRAM_STRING_END %token PAREN_LEFT PAREN_RIGHT COMMA -%token IDENTIFIER STRING +%token IDENTIFIER INTEGER FLOAT STRING %type expr interpolation literal literalModeTop literalModeValue %type args @@ -76,6 +76,22 @@ expr: { $$ = $1 } +| INTEGER + { + $$ = &ast.LiteralNode{ + Value: $1.Value.(int), + Type: ast.TypeInt, + Posx: $1.Pos, + } + } +| FLOAT + { + $$ = &ast.LiteralNode{ + Value: $1.Value.(float64), + Type: ast.TypeFloat, + Posx: $1.Pos, + } + } | IDENTIFIER { $$ = &ast.VariableAccess{Name: $1.Value.(string), Posx: $1.Pos} diff --git a/config/lang/lex.go b/config/lang/lex.go index a4ee94b2a2..30be9b072c 100644 --- a/config/lang/lex.go +++ b/config/lang/lex.go @@ -3,6 +3,7 @@ package lang import ( "bytes" "fmt" + "strconv" "unicode" "unicode/utf8" @@ -145,6 +146,12 @@ func (x *parserLex) lexModeInterpolation(yylval *parserSymType) int { return result } + // If we are seeing a number, it is the start of a number. Lex it. + if c >= '0' && c <= '9' { + x.backup() + return x.lexNumber(yylval) + } + switch c { case '}': x.interpolationDepth-- @@ -193,6 +200,61 @@ func (x *parserLex) lexId(yylval *parserSymType) int { return IDENTIFIER } +// lexNumber lexes out a number: an integer or a float. +func (x *parserLex) lexNumber(yylval *parserSymType) int { + var b bytes.Buffer + gotPeriod := false + for { + c := x.next() + if c == lexEOF { + break + } + + // If we see a period, we might be getting a float.. + if c == '.' { + // If we've already seen a period, then ignore it, and + // exit. This will probably result in a syntax error later. + if gotPeriod { + x.backup() + break + } + + gotPeriod = true + } else if c < '0' || c > '9' { + // If we're not seeing a number, then also exit. + x.backup() + break + } + + if _, err := b.WriteRune(c); err != nil { + x.Error(fmt.Sprintf("internal error: %s", err)) + return lexEOF + } + } + + // If we didn't see a period, it is an int + if !gotPeriod { + v, err := strconv.ParseInt(b.String(), 0, 0) + if err != nil { + x.Error(fmt.Sprintf("expected number: %s", err)) + return lexEOF + } + + yylval.token = &parserToken{Value: int(v)} + return INTEGER + } + + // If we did see a period, it is a float + f, err := strconv.ParseFloat(b.String(), 64) + if err != nil { + x.Error(fmt.Sprintf("expected float: %s", err)) + return lexEOF + } + + yylval.token = &parserToken{Value: f} + return FLOAT +} + func (x *parserLex) lexString(yylval *parserSymType, quoted bool) (int, bool) { var b bytes.Buffer terminated := false diff --git a/config/lang/lex_test.go b/config/lang/lex_test.go index b5cc7c583c..73913adf65 100644 --- a/config/lang/lex_test.go +++ b/config/lang/lex_test.go @@ -46,6 +46,20 @@ func TestLex(t *testing.T) { PROGRAM_BRACKET_RIGHT, lexEOF}, }, + { + "${bar(42)}", + []int{PROGRAM_BRACKET_LEFT, + IDENTIFIER, PAREN_LEFT, INTEGER, PAREN_RIGHT, + PROGRAM_BRACKET_RIGHT, lexEOF}, + }, + + { + "${bar(3.14159)}", + []int{PROGRAM_BRACKET_LEFT, + IDENTIFIER, PAREN_LEFT, FLOAT, PAREN_RIGHT, + PROGRAM_BRACKET_RIGHT, lexEOF}, + }, + { "${bar(inner(baz))}", []int{PROGRAM_BRACKET_LEFT, diff --git a/config/lang/parse_test.go b/config/lang/parse_test.go index 9d17072e81..d8e2c7236e 100644 --- a/config/lang/parse_test.go +++ b/config/lang/parse_test.go @@ -86,6 +86,46 @@ func TestParse(t *testing.T) { }, }, + { + "foo ${42}", + false, + &ast.Concat{ + Posx: ast.Pos{Column: 1, Line: 1}, + Exprs: []ast.Node{ + &ast.LiteralNode{ + Value: "foo ", + Type: ast.TypeString, + Posx: ast.Pos{Column: 1, Line: 1}, + }, + &ast.LiteralNode{ + Value: 42, + Type: ast.TypeInt, + Posx: ast.Pos{Column: 7, Line: 1}, + }, + }, + }, + }, + + { + "foo ${3.14159}", + false, + &ast.Concat{ + Posx: ast.Pos{Column: 1, Line: 1}, + Exprs: []ast.Node{ + &ast.LiteralNode{ + Value: "foo ", + Type: ast.TypeString, + Posx: ast.Pos{Column: 1, Line: 1}, + }, + &ast.LiteralNode{ + Value: 3.14159, + Type: ast.TypeFloat, + Posx: ast.Pos{Column: 7, Line: 1}, + }, + }, + }, + }, + { "${foo()}", false, diff --git a/config/lang/y.go b/config/lang/y.go index e8ce25a931..07ae55cdc8 100644 --- a/config/lang/y.go +++ b/config/lang/y.go @@ -25,7 +25,9 @@ const PAREN_LEFT = 57350 const PAREN_RIGHT = 57351 const COMMA = 57352 const IDENTIFIER = 57353 -const STRING = 57354 +const INTEGER = 57354 +const FLOAT = 57355 +const STRING = 57356 var parserToknames = []string{ "PROGRAM_BRACKET_LEFT", @@ -36,6 +38,8 @@ var parserToknames = []string{ "PAREN_RIGHT", "COMMA", "IDENTIFIER", + "INTEGER", + "FLOAT", "STRING", } var parserStatenames = []string{} @@ -44,7 +48,7 @@ const parserEofCode = 1 const parserErrCode = 2 const parserMaxDepth = 200 -//line lang.y:111 +//line lang.y:127 //line yacctab:1 var parserExca = []int{ @@ -53,48 +57,51 @@ var parserExca = []int{ -2, 0, } -const parserNprod = 14 +const parserNprod = 16 const parserPrivate = 57344 var parserTokenNames []string var parserStates []string -const parserLast = 21 +const parserLast = 23 var parserAct = []int{ - 9, 7, 7, 13, 3, 16, 17, 8, 11, 6, - 6, 12, 10, 2, 15, 8, 1, 14, 18, 4, - 5, + 9, 7, 7, 3, 18, 19, 8, 15, 13, 11, + 12, 6, 6, 14, 8, 1, 17, 10, 2, 16, + 20, 4, 5, } var parserPact = []int{ - -2, -1000, -2, -1000, -1000, -1000, -1000, -3, -1000, 6, - -2, -5, -1000, -3, -4, -1000, -1000, -3, -1000, + -2, -1000, -2, -1000, -1000, -1000, -1000, -3, -1000, 8, + -2, -1000, -1000, -1, -1000, -3, -5, -1000, -1000, -3, + -1000, } var parserPgo = []int{ - 0, 0, 20, 19, 12, 4, 17, 16, + 0, 0, 22, 21, 17, 3, 19, 15, } var parserR1 = []int{ 0, 7, 4, 4, 5, 5, 2, 1, 1, 1, - 6, 6, 6, 3, + 1, 1, 6, 6, 6, 3, } var parserR2 = []int{ - 0, 1, 1, 2, 1, 1, 3, 1, 1, 4, - 0, 3, 1, 1, + 0, 1, 1, 2, 1, 1, 3, 1, 1, 1, + 1, 4, 0, 3, 1, 1, } var parserChk = []int{ - -1000, -7, -4, -5, -3, -2, 12, 4, -5, -1, - -4, 11, 5, 8, -6, -1, 9, 10, -1, + -1000, -7, -4, -5, -3, -2, 14, 4, -5, -1, + -4, 12, 13, 11, 5, 8, -6, -1, 9, 10, + -1, } var parserDef = []int{ - 0, -2, 1, 2, 4, 5, 13, 0, 3, 0, - 7, 8, 6, 10, 0, 12, 9, 0, 11, + 0, -2, 1, 2, 4, 5, 15, 0, 3, 0, + 7, 8, 9, 10, 6, 12, 0, 14, 11, 0, + 13, } var parserTok1 = []int{ @@ -103,7 +110,7 @@ var parserTok1 = []int{ var parserTok2 = []int{ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 12, + 12, 13, 14, } var parserTok3 = []int{ 0, @@ -382,30 +389,48 @@ parserdefault: case 8: //line lang.y:80 { - parserVAL.node = &ast.VariableAccess{Name: parserS[parserpt-0].token.Value.(string), Posx: parserS[parserpt-0].token.Pos} + parserVAL.node = &ast.LiteralNode{ + Value: parserS[parserpt-0].token.Value.(int), + Type: ast.TypeInt, + Posx: parserS[parserpt-0].token.Pos, + } } case 9: - //line lang.y:84 + //line lang.y:88 + { + parserVAL.node = &ast.LiteralNode{ + Value: parserS[parserpt-0].token.Value.(float64), + Type: ast.TypeFloat, + Posx: parserS[parserpt-0].token.Pos, + } + } + case 10: + //line lang.y:96 + { + parserVAL.node = &ast.VariableAccess{Name: parserS[parserpt-0].token.Value.(string), Posx: parserS[parserpt-0].token.Pos} + } + case 11: + //line lang.y:100 { parserVAL.node = &ast.Call{Func: parserS[parserpt-3].token.Value.(string), Args: parserS[parserpt-1].nodeList, Posx: parserS[parserpt-3].token.Pos} } - case 10: - //line lang.y:89 + case 12: + //line lang.y:105 { parserVAL.nodeList = nil } - case 11: - //line lang.y:93 + case 13: + //line lang.y:109 { parserVAL.nodeList = append(parserS[parserpt-2].nodeList, parserS[parserpt-0].node) } - case 12: - //line lang.y:97 + case 14: + //line lang.y:113 { parserVAL.nodeList = append(parserVAL.nodeList, parserS[parserpt-0].node) } - case 13: - //line lang.y:103 + case 15: + //line lang.y:119 { parserVAL.node = &ast.LiteralNode{ Value: parserS[parserpt-0].token.Value.(string), diff --git a/config/lang/y.output b/config/lang/y.output index 86f1ffef23..ce531c4612 100644 --- a/config/lang/y.output +++ b/config/lang/y.output @@ -50,16 +50,18 @@ state 5 state 6 - literal: STRING. (13) + literal: STRING. (15) - . reduce 13 (src line 101) + . reduce 15 (src line 117) state 7 interpolation: PROGRAM_BRACKET_LEFT.expr PROGRAM_BRACKET_RIGHT PROGRAM_BRACKET_LEFT shift 7 - IDENTIFIER shift 11 + IDENTIFIER shift 13 + INTEGER shift 11 + FLOAT shift 12 STRING shift 6 . error @@ -78,7 +80,7 @@ state 8 state 9 interpolation: PROGRAM_BRACKET_LEFT expr.PROGRAM_BRACKET_RIGHT - PROGRAM_BRACKET_RIGHT shift 12 + PROGRAM_BRACKET_RIGHT shift 14 . error @@ -95,85 +97,101 @@ state 10 literalModeValue goto 8 state 11 - expr: IDENTIFIER. (8) - expr: IDENTIFIER.PAREN_LEFT args PAREN_RIGHT + expr: INTEGER. (8) - PAREN_LEFT shift 13 . reduce 8 (src line 79) state 12 + expr: FLOAT. (9) + + . reduce 9 (src line 87) + + +state 13 + expr: IDENTIFIER. (10) + expr: IDENTIFIER.PAREN_LEFT args PAREN_RIGHT + + PAREN_LEFT shift 15 + . reduce 10 (src line 95) + + +state 14 interpolation: PROGRAM_BRACKET_LEFT expr PROGRAM_BRACKET_RIGHT. (6) . reduce 6 (src line 68) -state 13 +state 15 expr: IDENTIFIER PAREN_LEFT.args PAREN_RIGHT - args: . (10) + args: . (12) PROGRAM_BRACKET_LEFT shift 7 - IDENTIFIER shift 11 + IDENTIFIER shift 13 + INTEGER shift 11 + FLOAT shift 12 STRING shift 6 - . reduce 10 (src line 88) + . reduce 12 (src line 104) - expr goto 15 + expr goto 17 interpolation goto 5 literal goto 4 literalModeTop goto 10 literalModeValue goto 3 - args goto 14 + args goto 16 -state 14 +state 16 expr: IDENTIFIER PAREN_LEFT args.PAREN_RIGHT args: args.COMMA expr - PAREN_RIGHT shift 16 - COMMA shift 17 + PAREN_RIGHT shift 18 + COMMA shift 19 . error -state 15 - args: expr. (12) - - . reduce 12 (src line 96) - - -state 16 - expr: IDENTIFIER PAREN_LEFT args PAREN_RIGHT. (9) - - . reduce 9 (src line 83) - - state 17 + args: expr. (14) + + . reduce 14 (src line 112) + + +state 18 + expr: IDENTIFIER PAREN_LEFT args PAREN_RIGHT. (11) + + . reduce 11 (src line 99) + + +state 19 args: args COMMA.expr PROGRAM_BRACKET_LEFT shift 7 - IDENTIFIER shift 11 + IDENTIFIER shift 13 + INTEGER shift 11 + FLOAT shift 12 STRING shift 6 . error - expr goto 18 + expr goto 20 interpolation goto 5 literal goto 4 literalModeTop goto 10 literalModeValue goto 3 -state 18 - args: args COMMA expr. (11) +state 20 + args: args COMMA expr. (13) - . reduce 11 (src line 92) + . reduce 13 (src line 108) -12 terminals, 8 nonterminals -14 grammar rules, 19/2000 states +14 terminals, 8 nonterminals +16 grammar rules, 21/2000 states 0 shift/reduce, 0 reduce/reduce conflicts reported 57 working sets used memory: parser 25/30000 -14 extra closures -19 shift entries, 1 exceptions +16 extra closures +25 shift entries, 1 exceptions 12 goto entries 15 entries saved by goto default -Optimizer space used: output 21/30000 -21 table entries, 0 zero -maximum spread: 12, maximum offset: 17 +Optimizer space used: output 23/30000 +23 table entries, 0 zero +maximum spread: 14, maximum offset: 19