zclsyntax: start of testing expression evaluation

This commit is contained in:
Martin Atkins 2017-05-31 19:27:16 -07:00
parent 3765519c52
commit 085dff2472
3 changed files with 105 additions and 2 deletions

View File

@ -9,6 +9,61 @@ import (
"github.com/zclconf/go-zcl/zcl"
)
func TestExpressionParseAndValue(t *testing.T) {
// This is a combo test that exercises both the parser and the Value
// method, with the focus on the latter but indirectly testing the former.
tests := []struct {
input string
ctx *zcl.EvalContext
want cty.Value
diagCount int
}{
{
`1`,
nil,
cty.NumberIntVal(1),
0,
},
{
`(1)`,
nil,
cty.NumberIntVal(1),
0,
},
{
`(1`,
nil,
cty.NumberIntVal(1),
1, // Unbalanced parentheses
},
}
for _, test := range tests {
t.Run(test.input, func(t *testing.T) {
expr, parseDiags := ParseExpression([]byte(test.input), "", zcl.Pos{Line: 1, Column: 1, Byte: 0})
got, valDiags := expr.Value(test.ctx)
diagCount := len(parseDiags) + len(valDiags)
if diagCount != test.diagCount {
t.Errorf("wrong number of diagnostics %d; want %d", diagCount, test.diagCount)
for _, diag := range parseDiags {
t.Logf(" - %s", diag.Error())
}
for _, diag := range valDiags {
t.Logf(" - %s", diag.Error())
}
}
if !got.RawEquals(test.want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want)
}
})
}
}
func TestFunctionCallExprValue(t *testing.T) {
funcs := map[string]function.Function{
"length": stdlib.StrlenFunc,

View File

@ -7,6 +7,7 @@ import (
"github.com/apparentlymart/go-textseg/textseg"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/convert"
"github.com/zclconf/go-zcl/zcl"
)
@ -418,8 +419,40 @@ func (p *parser) parseExpressionTerm() (Expression, zcl.Diagnostics) {
p.setRecovery()
}
p.Read() // eat closing paren
return expr, diags
case TokenNumberLit:
tok := p.Read() // eat number token
// We'll lean on the cty converter to do the conversion, to ensure that
// the behavior is the same as what would happen if converting a
// non-literal string to a number.
numStrVal := cty.StringVal(string(tok.Bytes))
numVal, err := convert.Convert(numStrVal, cty.Number)
if err != nil {
ret := &LiteralValueExpr{
Val: cty.UnknownVal(cty.Number),
SrcRange: tok.Range,
}
return ret, zcl.Diagnostics{
{
Severity: zcl.DiagError,
Summary: "Invalid number literal",
// FIXME: not a very good error message, but convert only
// gives us "a number is required", so not much help either.
Detail: "Failed to recognize the value of this number literal.",
Subject: &ret.SrcRange,
},
}
}
return &LiteralValueExpr{
Val: numVal,
SrcRange: tok.Range,
}, nil
default:
var diags zcl.Diagnostics
if !p.recovery {

View File

@ -27,8 +27,23 @@ func ParseConfig(src []byte, filename string, start zcl.Pos) (*zcl.File, zcl.Dia
// ParseExpression parses the given buffer as a standalone zcl expression,
// returning it as an instance of Expression.
func ParseExpression(src []byte, filename string, start zcl.Pos) (*Expression, zcl.Diagnostics) {
panic("ParseExpression is not yet implemented")
func ParseExpression(src []byte, filename string, start zcl.Pos) (Expression, zcl.Diagnostics) {
tokens := LexExpression(src, filename, start)
peeker := newPeeker(tokens, false)
parser := &parser{peeker: peeker}
expr, diags := parser.ParseExpression()
next := parser.Peek()
if next.Type != TokenEOF {
diags = append(diags, &zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: "Extra characters after expression",
Detail: "An expression was successfully parsed, but extra characters were found after it.",
Subject: &next.Range,
})
}
return expr, diags
}
// ParseTemplate parses the given buffer as a standalone zcl template,