From 6e29ea03661fa4e982e152171ab10560d33f934f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 14 Jan 2015 12:18:51 -0800 Subject: [PATCH] config/lang: some hoops to get the types to work --- config/lang/ast/concat.go | 10 ++++++ config/lang/lang.y | 17 +++++++++ config/lang/parse_test.go | 74 +++++++++++++++++++++++++-------------- config/lang/y.go | 47 +++++++++++++++++-------- config/lang/y.output | 28 +++++++-------- 5 files changed, 120 insertions(+), 56 deletions(-) diff --git a/config/lang/ast/concat.go b/config/lang/ast/concat.go index 9d3d998ee..238912697 100644 --- a/config/lang/ast/concat.go +++ b/config/lang/ast/concat.go @@ -1,6 +1,7 @@ package ast import ( + "bytes" "fmt" ) @@ -26,3 +27,12 @@ func (n *Concat) Pos() Pos { func (n *Concat) GoString() string { return fmt.Sprintf("*%#v", *n) } + +func (n *Concat) String() string { + var b bytes.Buffer + for _, expr := range n.Exprs { + b.WriteString(fmt.Sprintf("%s", expr)) + } + + return b.String() +} diff --git a/config/lang/lang.y b/config/lang/lang.y index 310af6128..fffe53089 100644 --- a/config/lang/lang.y +++ b/config/lang/lang.y @@ -40,6 +40,23 @@ top: | literalModeTop { parserResult = $1 + + // 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 + // interpolation. + // + // The logic for checking for a LiteralNode is a little annoying + // 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 := $1.(*ast.Concat); !ok { + if n, ok := $1.(*ast.LiteralNode); !ok || n.Type != ast.TypeString { + parserResult = &ast.Concat{ + Exprs: []ast.Node{$1}, + Posx: $1.Pos(), + } + } + } } literalModeTop: diff --git a/config/lang/parse_test.go b/config/lang/parse_test.go index cf7163271..a176da7f9 100644 --- a/config/lang/parse_test.go +++ b/config/lang/parse_test.go @@ -149,23 +149,33 @@ func TestParse(t *testing.T) { { "${foo()}", false, - &ast.Call{ - Func: "foo", - Args: nil, + &ast.Concat{ Posx: ast.Pos{Column: 3, Line: 1}, + Exprs: []ast.Node{ + &ast.Call{ + Func: "foo", + Args: nil, + Posx: ast.Pos{Column: 3, Line: 1}, + }, + }, }, }, { "${foo(bar)}", false, - &ast.Call{ - Func: "foo", + &ast.Concat{ Posx: ast.Pos{Column: 3, Line: 1}, - Args: []ast.Node{ - &ast.VariableAccess{ - Name: "bar", - Posx: ast.Pos{Column: 7, Line: 1}, + Exprs: []ast.Node{ + &ast.Call{ + Func: "foo", + Posx: ast.Pos{Column: 3, Line: 1}, + Args: []ast.Node{ + &ast.VariableAccess{ + Name: "bar", + Posx: ast.Pos{Column: 7, Line: 1}, + }, + }, }, }, }, @@ -174,17 +184,22 @@ func TestParse(t *testing.T) { { "${foo(bar, baz)}", false, - &ast.Call{ - Func: "foo", + &ast.Concat{ Posx: ast.Pos{Column: 3, Line: 1}, - Args: []ast.Node{ - &ast.VariableAccess{ - Name: "bar", - Posx: ast.Pos{Column: 7, Line: 1}, - }, - &ast.VariableAccess{ - Name: "baz", - Posx: ast.Pos{Column: 11, Line: 1}, + Exprs: []ast.Node{ + &ast.Call{ + Func: "foo", + Posx: ast.Pos{Column: 3, Line: 1}, + Args: []ast.Node{ + &ast.VariableAccess{ + Name: "bar", + Posx: ast.Pos{Column: 7, Line: 1}, + }, + &ast.VariableAccess{ + Name: "baz", + Posx: ast.Pos{Column: 11, Line: 1}, + }, + }, }, }, }, @@ -193,17 +208,22 @@ func TestParse(t *testing.T) { { "${foo(bar(baz))}", false, - &ast.Call{ - Func: "foo", + &ast.Concat{ Posx: ast.Pos{Column: 3, Line: 1}, - Args: []ast.Node{ + Exprs: []ast.Node{ &ast.Call{ - Func: "bar", - Posx: ast.Pos{Column: 7, Line: 1}, + Func: "foo", + Posx: ast.Pos{Column: 3, Line: 1}, Args: []ast.Node{ - &ast.VariableAccess{ - Name: "baz", - Posx: ast.Pos{Column: 11, Line: 1}, + &ast.Call{ + Func: "bar", + Posx: ast.Pos{Column: 7, Line: 1}, + Args: []ast.Node{ + &ast.VariableAccess{ + Name: "baz", + Posx: ast.Pos{Column: 11, Line: 1}, + }, + }, }, }, }, diff --git a/config/lang/y.go b/config/lang/y.go index a6b139520..6d3a456a6 100644 --- a/config/lang/y.go +++ b/config/lang/y.go @@ -48,7 +48,7 @@ const parserEofCode = 1 const parserErrCode = 2 const parserMaxDepth = 200 -//line lang.y:134 +//line lang.y:151 //line yacctab:1 var parserExca = []int{ @@ -354,14 +354,31 @@ parserdefault: //line lang.y:41 { parserResult = parserS[parserpt-0].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 + // interpolation. + // + // The logic for checking for a LiteralNode is a little annoying + // 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.Type != ast.TypeString { + parserResult = &ast.Concat{ + Exprs: []ast.Node{parserS[parserpt-0].node}, + Posx: parserS[parserpt-0].node.Pos(), + } + } + } } case 3: - //line lang.y:47 + //line lang.y:64 { parserVAL.node = parserS[parserpt-0].node } case 4: - //line lang.y:51 + //line lang.y:68 { var result []ast.Node if c, ok := parserS[parserpt-1].node.(*ast.Concat); ok { @@ -376,27 +393,27 @@ parserdefault: } } case 5: - //line lang.y:67 + //line lang.y:84 { parserVAL.node = parserS[parserpt-0].node } case 6: - //line lang.y:71 + //line lang.y:88 { parserVAL.node = parserS[parserpt-0].node } case 7: - //line lang.y:77 + //line lang.y:94 { parserVAL.node = parserS[parserpt-1].node } case 8: - //line lang.y:83 + //line lang.y:100 { parserVAL.node = parserS[parserpt-0].node } case 9: - //line lang.y:87 + //line lang.y:104 { parserVAL.node = &ast.LiteralNode{ Value: parserS[parserpt-0].token.Value.(int), @@ -405,7 +422,7 @@ parserdefault: } } case 10: - //line lang.y:95 + //line lang.y:112 { parserVAL.node = &ast.LiteralNode{ Value: parserS[parserpt-0].token.Value.(float64), @@ -414,32 +431,32 @@ parserdefault: } } case 11: - //line lang.y:103 + //line lang.y:120 { parserVAL.node = &ast.VariableAccess{Name: parserS[parserpt-0].token.Value.(string), Posx: parserS[parserpt-0].token.Pos} } case 12: - //line lang.y:107 + //line lang.y:124 { parserVAL.node = &ast.Call{Func: parserS[parserpt-3].token.Value.(string), Args: parserS[parserpt-1].nodeList, Posx: parserS[parserpt-3].token.Pos} } case 13: - //line lang.y:112 + //line lang.y:129 { parserVAL.nodeList = nil } case 14: - //line lang.y:116 + //line lang.y:133 { parserVAL.nodeList = append(parserS[parserpt-2].nodeList, parserS[parserpt-0].node) } case 15: - //line lang.y:120 + //line lang.y:137 { parserVAL.nodeList = append(parserVAL.nodeList, parserS[parserpt-0].node) } case 16: - //line lang.y:126 + //line lang.y:143 { parserVAL.node = &ast.LiteralNode{ Value: parserS[parserpt-0].token.Value.(string), diff --git a/config/lang/y.output b/config/lang/y.output index df08ec4ad..aa076e45a 100644 --- a/config/lang/y.output +++ b/config/lang/y.output @@ -35,25 +35,25 @@ state 2 state 3 literalModeTop: literalModeValue. (3) - . reduce 3 (src line 45) + . reduce 3 (src line 62) state 4 literalModeValue: literal. (5) - . reduce 5 (src line 65) + . reduce 5 (src line 82) state 5 literalModeValue: interpolation. (6) - . reduce 6 (src line 70) + . reduce 6 (src line 87) state 6 literal: STRING. (16) - . reduce 16 (src line 124) + . reduce 16 (src line 141) state 7 @@ -75,7 +75,7 @@ state 7 state 8 literalModeTop: literalModeTop literalModeValue. (4) - . reduce 4 (src line 50) + . reduce 4 (src line 67) state 9 @@ -91,7 +91,7 @@ state 10 PROGRAM_BRACKET_LEFT shift 7 STRING shift 6 - . reduce 8 (src line 81) + . reduce 8 (src line 98) interpolation goto 5 literal goto 4 @@ -100,13 +100,13 @@ state 10 state 11 expr: INTEGER. (9) - . reduce 9 (src line 86) + . reduce 9 (src line 103) state 12 expr: FLOAT. (10) - . reduce 10 (src line 94) + . reduce 10 (src line 111) state 13 @@ -114,13 +114,13 @@ state 13 expr: IDENTIFIER.PAREN_LEFT args PAREN_RIGHT PAREN_LEFT shift 15 - . reduce 11 (src line 102) + . reduce 11 (src line 119) state 14 interpolation: PROGRAM_BRACKET_LEFT expr PROGRAM_BRACKET_RIGHT. (7) - . reduce 7 (src line 75) + . reduce 7 (src line 92) state 15 @@ -132,7 +132,7 @@ state 15 INTEGER shift 11 FLOAT shift 12 STRING shift 6 - . reduce 13 (src line 111) + . reduce 13 (src line 128) expr goto 17 interpolation goto 5 @@ -153,13 +153,13 @@ state 16 state 17 args: expr. (15) - . reduce 15 (src line 119) + . reduce 15 (src line 136) state 18 expr: IDENTIFIER PAREN_LEFT args PAREN_RIGHT. (12) - . reduce 12 (src line 106) + . reduce 12 (src line 123) state 19 @@ -181,7 +181,7 @@ state 19 state 20 args: args COMMA expr. (14) - . reduce 14 (src line 115) + . reduce 14 (src line 132) 14 terminals, 8 nonterminals