config/lang: some hoops to get the types to work

This commit is contained in:
Mitchell Hashimoto 2015-01-14 12:18:51 -08:00
parent 591610deea
commit 6e29ea0366
5 changed files with 120 additions and 56 deletions

View File

@ -1,6 +1,7 @@
package ast package ast
import ( import (
"bytes"
"fmt" "fmt"
) )
@ -26,3 +27,12 @@ func (n *Concat) Pos() Pos {
func (n *Concat) GoString() string { func (n *Concat) GoString() string {
return fmt.Sprintf("*%#v", *n) 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()
}

View File

@ -40,6 +40,23 @@ top:
| literalModeTop | literalModeTop
{ {
parserResult = $1 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: literalModeTop:

View File

@ -149,23 +149,33 @@ func TestParse(t *testing.T) {
{ {
"${foo()}", "${foo()}",
false, false,
&ast.Call{ &ast.Concat{
Func: "foo",
Args: nil,
Posx: ast.Pos{Column: 3, Line: 1}, 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)}", "${foo(bar)}",
false, false,
&ast.Call{ &ast.Concat{
Func: "foo",
Posx: ast.Pos{Column: 3, Line: 1}, Posx: ast.Pos{Column: 3, Line: 1},
Args: []ast.Node{ Exprs: []ast.Node{
&ast.VariableAccess{ &ast.Call{
Name: "bar", Func: "foo",
Posx: ast.Pos{Column: 7, Line: 1}, 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)}", "${foo(bar, baz)}",
false, false,
&ast.Call{ &ast.Concat{
Func: "foo",
Posx: ast.Pos{Column: 3, Line: 1}, Posx: ast.Pos{Column: 3, Line: 1},
Args: []ast.Node{ Exprs: []ast.Node{
&ast.VariableAccess{ &ast.Call{
Name: "bar", Func: "foo",
Posx: ast.Pos{Column: 7, Line: 1}, Posx: ast.Pos{Column: 3, Line: 1},
}, Args: []ast.Node{
&ast.VariableAccess{ &ast.VariableAccess{
Name: "baz", Name: "bar",
Posx: ast.Pos{Column: 11, Line: 1}, 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))}", "${foo(bar(baz))}",
false, false,
&ast.Call{ &ast.Concat{
Func: "foo",
Posx: ast.Pos{Column: 3, Line: 1}, Posx: ast.Pos{Column: 3, Line: 1},
Args: []ast.Node{ Exprs: []ast.Node{
&ast.Call{ &ast.Call{
Func: "bar", Func: "foo",
Posx: ast.Pos{Column: 7, Line: 1}, Posx: ast.Pos{Column: 3, Line: 1},
Args: []ast.Node{ Args: []ast.Node{
&ast.VariableAccess{ &ast.Call{
Name: "baz", Func: "bar",
Posx: ast.Pos{Column: 11, Line: 1}, Posx: ast.Pos{Column: 7, Line: 1},
Args: []ast.Node{
&ast.VariableAccess{
Name: "baz",
Posx: ast.Pos{Column: 11, Line: 1},
},
},
}, },
}, },
}, },

View File

@ -48,7 +48,7 @@ const parserEofCode = 1
const parserErrCode = 2 const parserErrCode = 2
const parserMaxDepth = 200 const parserMaxDepth = 200
//line lang.y:134 //line lang.y:151
//line yacctab:1 //line yacctab:1
var parserExca = []int{ var parserExca = []int{
@ -354,14 +354,31 @@ parserdefault:
//line lang.y:41 //line lang.y:41
{ {
parserResult = parserS[parserpt-0].node 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: case 3:
//line lang.y:47 //line lang.y:64
{ {
parserVAL.node = parserS[parserpt-0].node parserVAL.node = parserS[parserpt-0].node
} }
case 4: case 4:
//line lang.y:51 //line lang.y:68
{ {
var result []ast.Node var result []ast.Node
if c, ok := parserS[parserpt-1].node.(*ast.Concat); ok { if c, ok := parserS[parserpt-1].node.(*ast.Concat); ok {
@ -376,27 +393,27 @@ parserdefault:
} }
} }
case 5: case 5:
//line lang.y:67 //line lang.y:84
{ {
parserVAL.node = parserS[parserpt-0].node parserVAL.node = parserS[parserpt-0].node
} }
case 6: case 6:
//line lang.y:71 //line lang.y:88
{ {
parserVAL.node = parserS[parserpt-0].node parserVAL.node = parserS[parserpt-0].node
} }
case 7: case 7:
//line lang.y:77 //line lang.y:94
{ {
parserVAL.node = parserS[parserpt-1].node parserVAL.node = parserS[parserpt-1].node
} }
case 8: case 8:
//line lang.y:83 //line lang.y:100
{ {
parserVAL.node = parserS[parserpt-0].node parserVAL.node = parserS[parserpt-0].node
} }
case 9: case 9:
//line lang.y:87 //line lang.y:104
{ {
parserVAL.node = &ast.LiteralNode{ parserVAL.node = &ast.LiteralNode{
Value: parserS[parserpt-0].token.Value.(int), Value: parserS[parserpt-0].token.Value.(int),
@ -405,7 +422,7 @@ parserdefault:
} }
} }
case 10: case 10:
//line lang.y:95 //line lang.y:112
{ {
parserVAL.node = &ast.LiteralNode{ parserVAL.node = &ast.LiteralNode{
Value: parserS[parserpt-0].token.Value.(float64), Value: parserS[parserpt-0].token.Value.(float64),
@ -414,32 +431,32 @@ parserdefault:
} }
} }
case 11: 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} parserVAL.node = &ast.VariableAccess{Name: parserS[parserpt-0].token.Value.(string), Posx: parserS[parserpt-0].token.Pos}
} }
case 12: 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} parserVAL.node = &ast.Call{Func: parserS[parserpt-3].token.Value.(string), Args: parserS[parserpt-1].nodeList, Posx: parserS[parserpt-3].token.Pos}
} }
case 13: case 13:
//line lang.y:112 //line lang.y:129
{ {
parserVAL.nodeList = nil parserVAL.nodeList = nil
} }
case 14: case 14:
//line lang.y:116 //line lang.y:133
{ {
parserVAL.nodeList = append(parserS[parserpt-2].nodeList, parserS[parserpt-0].node) parserVAL.nodeList = append(parserS[parserpt-2].nodeList, parserS[parserpt-0].node)
} }
case 15: case 15:
//line lang.y:120 //line lang.y:137
{ {
parserVAL.nodeList = append(parserVAL.nodeList, parserS[parserpt-0].node) parserVAL.nodeList = append(parserVAL.nodeList, parserS[parserpt-0].node)
} }
case 16: case 16:
//line lang.y:126 //line lang.y:143
{ {
parserVAL.node = &ast.LiteralNode{ parserVAL.node = &ast.LiteralNode{
Value: parserS[parserpt-0].token.Value.(string), Value: parserS[parserpt-0].token.Value.(string),

View File

@ -35,25 +35,25 @@ state 2
state 3 state 3
literalModeTop: literalModeValue. (3) literalModeTop: literalModeValue. (3)
. reduce 3 (src line 45) . reduce 3 (src line 62)
state 4 state 4
literalModeValue: literal. (5) literalModeValue: literal. (5)
. reduce 5 (src line 65) . reduce 5 (src line 82)
state 5 state 5
literalModeValue: interpolation. (6) literalModeValue: interpolation. (6)
. reduce 6 (src line 70) . reduce 6 (src line 87)
state 6 state 6
literal: STRING. (16) literal: STRING. (16)
. reduce 16 (src line 124) . reduce 16 (src line 141)
state 7 state 7
@ -75,7 +75,7 @@ state 7
state 8 state 8
literalModeTop: literalModeTop literalModeValue. (4) literalModeTop: literalModeTop literalModeValue. (4)
. reduce 4 (src line 50) . reduce 4 (src line 67)
state 9 state 9
@ -91,7 +91,7 @@ state 10
PROGRAM_BRACKET_LEFT shift 7 PROGRAM_BRACKET_LEFT shift 7
STRING shift 6 STRING shift 6
. reduce 8 (src line 81) . reduce 8 (src line 98)
interpolation goto 5 interpolation goto 5
literal goto 4 literal goto 4
@ -100,13 +100,13 @@ state 10
state 11 state 11
expr: INTEGER. (9) expr: INTEGER. (9)
. reduce 9 (src line 86) . reduce 9 (src line 103)
state 12 state 12
expr: FLOAT. (10) expr: FLOAT. (10)
. reduce 10 (src line 94) . reduce 10 (src line 111)
state 13 state 13
@ -114,13 +114,13 @@ state 13
expr: IDENTIFIER.PAREN_LEFT args PAREN_RIGHT expr: IDENTIFIER.PAREN_LEFT args PAREN_RIGHT
PAREN_LEFT shift 15 PAREN_LEFT shift 15
. reduce 11 (src line 102) . reduce 11 (src line 119)
state 14 state 14
interpolation: PROGRAM_BRACKET_LEFT expr PROGRAM_BRACKET_RIGHT. (7) interpolation: PROGRAM_BRACKET_LEFT expr PROGRAM_BRACKET_RIGHT. (7)
. reduce 7 (src line 75) . reduce 7 (src line 92)
state 15 state 15
@ -132,7 +132,7 @@ state 15
INTEGER shift 11 INTEGER shift 11
FLOAT shift 12 FLOAT shift 12
STRING shift 6 STRING shift 6
. reduce 13 (src line 111) . reduce 13 (src line 128)
expr goto 17 expr goto 17
interpolation goto 5 interpolation goto 5
@ -153,13 +153,13 @@ state 16
state 17 state 17
args: expr. (15) args: expr. (15)
. reduce 15 (src line 119) . reduce 15 (src line 136)
state 18 state 18
expr: IDENTIFIER PAREN_LEFT args PAREN_RIGHT. (12) expr: IDENTIFIER PAREN_LEFT args PAREN_RIGHT. (12)
. reduce 12 (src line 106) . reduce 12 (src line 123)
state 19 state 19
@ -181,7 +181,7 @@ state 19
state 20 state 20
args: args COMMA expr. (14) args: args COMMA expr. (14)
. reduce 14 (src line 115) . reduce 14 (src line 132)
14 terminals, 8 nonterminals 14 terminals, 8 nonterminals