xgo/parser/parser_test.go
2019-01-11 01:16:34 -08:00

455 lines
15 KiB
Go

package parser_test
import (
"fmt"
"reflect"
"strings"
"testing"
"github.com/d5/tengo/assert"
"github.com/d5/tengo/ast"
"github.com/d5/tengo/parser"
"github.com/d5/tengo/source"
"github.com/d5/tengo/token"
)
type pfn func(int, int) source.Pos // position conversion function
type expectedFn func(pos pfn) []ast.Stmt // callback function to return expected results
type tracer struct {
out []string
}
func (o *tracer) Write(p []byte) (n int, err error) {
o.out = append(o.out, string(p))
return len(p), nil
}
//type slowPrinter struct {
//}
//
//func (o *slowPrinter) Write(p []byte) (n int, err error) {
// fmt.Print(string(p))
// time.Sleep(25 * time.Millisecond)
// return len(p), nil
//}
func expect(t *testing.T, input string, fn expectedFn) (ok bool) {
testFileSet := source.NewFileSet()
testFile := testFileSet.AddFile("", -1, len(input))
defer func() {
if !ok {
// print trace
tr := &tracer{}
actual, _ := parser.ParseFile(testFile, []byte(input), tr)
if actual != nil {
t.Logf("Parsed:\n%s", actual.String())
}
t.Logf("Trace:\n%s", strings.Join(tr.out, ""))
}
}()
actual, err := parser.ParseFile(testFile, []byte(input), nil)
if !assert.NoError(t, err) {
return
}
expected := fn(func(line, column int) source.Pos {
return source.Pos(int(testFile.LineStart(line)) + (column - 1))
})
if !assert.Equal(t, len(expected), len(actual.Stmts)) {
return
}
for i := 0; i < len(expected); i++ {
if !equalStmt(t, expected[i], actual.Stmts[i]) {
return
}
}
ok = true
return
}
func expectError(t *testing.T, input string) (ok bool) {
testFileSet := source.NewFileSet()
testFile := testFileSet.AddFile("", -1, len(input))
defer func() {
if !ok {
// print trace
tr := &tracer{}
_, _ = parser.ParseFile(testFile, []byte(input), tr)
t.Logf("Trace:\n%s", strings.Join(tr.out, ""))
}
}()
_, err := parser.ParseFile(testFile, []byte(input), nil)
if !assert.Error(t, err) {
return
}
ok = true
return
}
func expectString(t *testing.T, input, expected string) (ok bool) {
testFileSet := source.NewFileSet()
testFile := testFileSet.AddFile("", -1, len(input))
defer func() {
if !ok {
// print trace
tr := &tracer{}
_, _ = parser.ParseFile(testFile, []byte(input), tr)
t.Logf("Trace:\n%s", strings.Join(tr.out, ""))
}
}()
actual, err := parser.ParseFile(testFile, []byte(input), nil)
if !assert.NoError(t, err) {
return
}
if !assert.Equal(t, expected, actual.String()) {
return
}
ok = true
return
}
//func printTrace(input string) {
// testFileSet := source.NewFileSet()
// testFile := testFileSet.AddFile("", -1, len(input))
//
// _, _ = parser.ParseFile(testFile, []byte(input), &slowPrinter{})
//}
func stmts(s ...ast.Stmt) []ast.Stmt {
return s
}
func exprStmt(x ast.Expr) *ast.ExprStmt {
return &ast.ExprStmt{Expr: x}
}
func assignStmt(lhs, rhs []ast.Expr, token token.Token, pos source.Pos) *ast.AssignStmt {
return &ast.AssignStmt{Lhs: lhs, Rhs: rhs, Token: token, TokenPos: pos}
}
func emptyStmt(implicit bool, pos source.Pos) *ast.EmptyStmt {
return &ast.EmptyStmt{Implicit: implicit, Semicolon: pos}
}
func returnStmt(pos source.Pos, results ...ast.Expr) *ast.ReturnStmt {
return &ast.ReturnStmt{Results: results, ReturnPos: pos}
}
func forStmt(init ast.Stmt, cond ast.Expr, post ast.Stmt, body *ast.BlockStmt, pos source.Pos) *ast.ForStmt {
return &ast.ForStmt{Cond: cond, Init: init, Post: post, Body: body, ForPos: pos}
}
func forInStmt(key, value *ast.Ident, seq ast.Expr, body *ast.BlockStmt, pos source.Pos) *ast.ForInStmt {
return &ast.ForInStmt{Key: key, Value: value, Iterable: seq, Body: body, ForPos: pos}
}
func ifStmt(init ast.Stmt, cond ast.Expr, body *ast.BlockStmt, elseStmt ast.Stmt, pos source.Pos) *ast.IfStmt {
return &ast.IfStmt{Init: init, Cond: cond, Body: body, Else: elseStmt, IfPos: pos}
}
func incDecStmt(expr ast.Expr, tok token.Token, pos source.Pos) *ast.IncDecStmt {
return &ast.IncDecStmt{Expr: expr, Token: tok, TokenPos: pos}
}
func funcType(params *ast.IdentList, pos source.Pos) *ast.FuncType {
return &ast.FuncType{Params: params, FuncPos: pos}
}
func blockStmt(lbrace, rbrace source.Pos, list ...ast.Stmt) *ast.BlockStmt {
return &ast.BlockStmt{Stmts: list, LBrace: lbrace, RBrace: rbrace}
}
func ident(name string, pos source.Pos) *ast.Ident {
return &ast.Ident{Name: name, NamePos: pos}
}
func identList(opening, closing source.Pos, list ...*ast.Ident) *ast.IdentList {
return &ast.IdentList{List: list, LParen: opening, RParen: closing}
}
func binaryExpr(x, y ast.Expr, op token.Token, pos source.Pos) *ast.BinaryExpr {
return &ast.BinaryExpr{Lhs: x, Rhs: y, Token: op, TokenPos: pos}
}
func unaryExpr(x ast.Expr, op token.Token, pos source.Pos) *ast.UnaryExpr {
return &ast.UnaryExpr{Expr: x, Token: op, TokenPos: pos}
}
func exprs(list ...ast.Expr) []ast.Expr {
return list
}
func intLit(value int64, pos source.Pos) *ast.IntLit {
return &ast.IntLit{Value: value, ValuePos: pos}
}
func floatLit(value float64, pos source.Pos) *ast.FloatLit {
return &ast.FloatLit{Value: value, ValuePos: pos}
}
func stringLit(value string, pos source.Pos) *ast.StringLit {
return &ast.StringLit{Value: value, ValuePos: pos}
}
func charLit(value rune, pos source.Pos) *ast.CharLit {
return &ast.CharLit{Value: value, ValuePos: pos, Literal: fmt.Sprintf("'%c'", value)}
}
func boolLit(value bool, pos source.Pos) *ast.BoolLit {
return &ast.BoolLit{Value: value, ValuePos: pos}
}
func arrayLit(lbracket, rbracket source.Pos, list ...ast.Expr) *ast.ArrayLit {
return &ast.ArrayLit{LBrack: lbracket, RBrack: rbracket, Elements: list}
}
func mapElementLit(key string, keyPos source.Pos, colonPos source.Pos, value ast.Expr) *ast.MapElementLit {
return &ast.MapElementLit{Key: key, KeyPos: keyPos, ColonPos: colonPos, Value: value}
}
func mapLit(lbrace, rbrace source.Pos, list ...*ast.MapElementLit) *ast.MapLit {
return &ast.MapLit{LBrace: lbrace, RBrace: rbrace, Elements: list}
}
func funcLit(funcType *ast.FuncType, body *ast.BlockStmt) *ast.FuncLit {
return &ast.FuncLit{Type: funcType, Body: body}
}
func parenExpr(x ast.Expr, lparen, rparen source.Pos) *ast.ParenExpr {
return &ast.ParenExpr{Expr: x, LParen: lparen, RParen: rparen}
}
func callExpr(f ast.Expr, lparen, rparen source.Pos, args ...ast.Expr) *ast.CallExpr {
return &ast.CallExpr{Func: f, LParen: lparen, RParen: rparen, Args: args}
}
func indexExpr(x, index ast.Expr, lbrack, rbrack source.Pos) *ast.IndexExpr {
return &ast.IndexExpr{Expr: x, Index: index, LBrack: lbrack, RBrack: rbrack}
}
func sliceExpr(x, low, high ast.Expr, lbrack, rbrack source.Pos) *ast.SliceExpr {
return &ast.SliceExpr{Expr: x, Low: low, High: high, LBrack: lbrack, RBrack: rbrack}
}
func selectorExpr(x, sel ast.Expr) *ast.SelectorExpr {
return &ast.SelectorExpr{Expr: x, Sel: sel}
}
func equalStmt(t *testing.T, expected, actual ast.Stmt) bool {
if expected == nil || reflect.ValueOf(expected).IsNil() {
return assert.Nil(t, actual, "expected nil, but got not nil")
}
if !assert.NotNil(t, actual, "expected not nil, but got nil") {
return false
}
if !assert.IsType(t, expected, actual) {
return false
}
switch expected := expected.(type) {
case *ast.ExprStmt:
return equalExpr(t, expected.Expr, actual.(*ast.ExprStmt).Expr)
case *ast.EmptyStmt:
return assert.Equal(t, expected.Implicit, actual.(*ast.EmptyStmt).Implicit) &&
assert.Equal(t, expected.Semicolon, actual.(*ast.EmptyStmt).Semicolon)
case *ast.BlockStmt:
return assert.Equal(t, expected.LBrace, actual.(*ast.BlockStmt).LBrace) &&
assert.Equal(t, expected.RBrace, actual.(*ast.BlockStmt).RBrace) &&
equalStmts(t, expected.Stmts, actual.(*ast.BlockStmt).Stmts)
case *ast.AssignStmt:
return equalExprs(t, expected.Lhs, actual.(*ast.AssignStmt).Lhs) &&
equalExprs(t, expected.Rhs, actual.(*ast.AssignStmt).Rhs) &&
assert.Equal(t, int(expected.Token), int(actual.(*ast.AssignStmt).Token)) &&
assert.Equal(t, int(expected.TokenPos), int(actual.(*ast.AssignStmt).TokenPos))
case *ast.IfStmt:
return equalStmt(t, expected.Init, actual.(*ast.IfStmt).Init) &&
equalExpr(t, expected.Cond, actual.(*ast.IfStmt).Cond) &&
equalStmt(t, expected.Body, actual.(*ast.IfStmt).Body) &&
equalStmt(t, expected.Else, actual.(*ast.IfStmt).Else) &&
assert.Equal(t, expected.IfPos, actual.(*ast.IfStmt).IfPos)
case *ast.IncDecStmt:
return equalExpr(t, expected.Expr, actual.(*ast.IncDecStmt).Expr) &&
assert.Equal(t, expected.Token, actual.(*ast.IncDecStmt).Token) &&
assert.Equal(t, expected.TokenPos, actual.(*ast.IncDecStmt).TokenPos)
case *ast.ForStmt:
return equalStmt(t, expected.Init, actual.(*ast.ForStmt).Init) &&
equalExpr(t, expected.Cond, actual.(*ast.ForStmt).Cond) &&
equalStmt(t, expected.Post, actual.(*ast.ForStmt).Post) &&
equalStmt(t, expected.Body, actual.(*ast.ForStmt).Body) &&
assert.Equal(t, expected.ForPos, actual.(*ast.ForStmt).ForPos)
case *ast.ForInStmt:
return equalExpr(t, expected.Key, actual.(*ast.ForInStmt).Key) &&
equalExpr(t, expected.Value, actual.(*ast.ForInStmt).Value) &&
equalExpr(t, expected.Iterable, actual.(*ast.ForInStmt).Iterable) &&
equalStmt(t, expected.Body, actual.(*ast.ForInStmt).Body) &&
assert.Equal(t, expected.ForPos, actual.(*ast.ForInStmt).ForPos)
case *ast.ReturnStmt:
return equalExprs(t, expected.Results, actual.(*ast.ReturnStmt).Results) &&
assert.Equal(t, expected.ReturnPos, actual.(*ast.ReturnStmt).ReturnPos)
case *ast.BranchStmt:
return equalExpr(t, expected.Label, actual.(*ast.BranchStmt).Label) &&
assert.Equal(t, expected.Token, actual.(*ast.BranchStmt).Token) &&
assert.Equal(t, expected.TokenPos, actual.(*ast.BranchStmt).TokenPos)
default:
panic(fmt.Errorf("unknown type: %T", expected))
}
}
func equalExpr(t *testing.T, expected, actual ast.Expr) bool {
if expected == nil || reflect.ValueOf(expected).IsNil() {
return assert.Nil(t, actual, "expected nil, but got not nil")
}
if !assert.NotNil(t, actual, "expected not nil, but got nil") {
return false
}
if !assert.IsType(t, expected, actual) {
return false
}
switch expected := expected.(type) {
case *ast.Ident:
return assert.Equal(t, expected.Name, actual.(*ast.Ident).Name) &&
assert.Equal(t, int(expected.NamePos), int(actual.(*ast.Ident).NamePos))
case *ast.IntLit:
return assert.Equal(t, expected.Value, actual.(*ast.IntLit).Value) &&
assert.Equal(t, int(expected.ValuePos), int(actual.(*ast.IntLit).ValuePos))
case *ast.FloatLit:
return assert.Equal(t, expected.Value, actual.(*ast.FloatLit).Value) &&
assert.Equal(t, int(expected.ValuePos), int(actual.(*ast.FloatLit).ValuePos))
case *ast.BoolLit:
return assert.Equal(t, expected.Value, actual.(*ast.BoolLit).Value) &&
assert.Equal(t, int(expected.ValuePos), int(actual.(*ast.BoolLit).ValuePos))
case *ast.CharLit:
return assert.Equal(t, expected.Value, actual.(*ast.CharLit).Value) &&
assert.Equal(t, int(expected.ValuePos), int(actual.(*ast.CharLit).ValuePos))
case *ast.StringLit:
return assert.Equal(t, expected.Value, actual.(*ast.StringLit).Value) &&
assert.Equal(t, int(expected.ValuePos), int(actual.(*ast.StringLit).ValuePos))
case *ast.ArrayLit:
return assert.Equal(t, expected.LBrack, actual.(*ast.ArrayLit).LBrack) &&
assert.Equal(t, expected.RBrack, actual.(*ast.ArrayLit).RBrack) &&
equalExprs(t, expected.Elements, actual.(*ast.ArrayLit).Elements)
case *ast.MapLit:
return assert.Equal(t, expected.LBrace, actual.(*ast.MapLit).LBrace) &&
assert.Equal(t, expected.RBrace, actual.(*ast.MapLit).RBrace) &&
equalMapElements(t, expected.Elements, actual.(*ast.MapLit).Elements)
case *ast.BinaryExpr:
return equalExpr(t, expected.Lhs, actual.(*ast.BinaryExpr).Lhs) &&
equalExpr(t, expected.Rhs, actual.(*ast.BinaryExpr).Rhs) &&
assert.Equal(t, expected.Token, actual.(*ast.BinaryExpr).Token) &&
assert.Equal(t, expected.TokenPos, actual.(*ast.BinaryExpr).TokenPos)
case *ast.UnaryExpr:
return equalExpr(t, expected.Expr, actual.(*ast.UnaryExpr).Expr) &&
assert.Equal(t, expected.Token, actual.(*ast.UnaryExpr).Token) &&
assert.Equal(t, expected.TokenPos, actual.(*ast.UnaryExpr).TokenPos)
case *ast.FuncLit:
return equalFuncType(t, expected.Type, actual.(*ast.FuncLit).Type) &&
equalStmt(t, expected.Body, actual.(*ast.FuncLit).Body)
case *ast.CallExpr:
return equalExpr(t, expected.Func, actual.(*ast.CallExpr).Func) &&
assert.Equal(t, expected.LParen, actual.(*ast.CallExpr).LParen) &&
assert.Equal(t, expected.RParen, actual.(*ast.CallExpr).RParen) &&
equalExprs(t, expected.Args, actual.(*ast.CallExpr).Args)
case *ast.ParenExpr:
return equalExpr(t, expected.Expr, actual.(*ast.ParenExpr).Expr) &&
assert.Equal(t, expected.LParen, actual.(*ast.ParenExpr).LParen) &&
assert.Equal(t, expected.RParen, actual.(*ast.ParenExpr).RParen)
case *ast.IndexExpr:
return equalExpr(t, expected.Expr, actual.(*ast.IndexExpr).Expr) &&
equalExpr(t, expected.Index, actual.(*ast.IndexExpr).Index) &&
assert.Equal(t, expected.LBrack, actual.(*ast.IndexExpr).LBrack) &&
assert.Equal(t, expected.RBrack, actual.(*ast.IndexExpr).RBrack)
case *ast.SliceExpr:
return equalExpr(t, expected.Expr, actual.(*ast.SliceExpr).Expr) &&
equalExpr(t, expected.Low, actual.(*ast.SliceExpr).Low) &&
equalExpr(t, expected.High, actual.(*ast.SliceExpr).High) &&
assert.Equal(t, expected.LBrack, actual.(*ast.SliceExpr).LBrack) &&
assert.Equal(t, expected.RBrack, actual.(*ast.SliceExpr).RBrack)
case *ast.SelectorExpr:
return equalExpr(t, expected.Expr, actual.(*ast.SelectorExpr).Expr) &&
equalExpr(t, expected.Sel, actual.(*ast.SelectorExpr).Sel)
default:
panic(fmt.Errorf("unknown type: %T", expected))
}
}
func equalFuncType(t *testing.T, expected, actual *ast.FuncType) bool {
return assert.Equal(t, expected.Params.LParen, actual.Params.LParen) &&
assert.Equal(t, expected.Params.RParen, actual.Params.RParen) &&
equalIdents(t, expected.Params.List, actual.Params.List)
}
func equalIdents(t *testing.T, expected, actual []*ast.Ident) bool {
if !assert.Equal(t, len(expected), len(actual)) {
return false
}
for i := 0; i < len(expected); i++ {
if !equalExpr(t, expected[i], actual[i]) {
return false
}
}
return true
}
func equalExprs(t *testing.T, expected, actual []ast.Expr) bool {
if !assert.Equal(t, len(expected), len(actual)) {
return false
}
for i := 0; i < len(expected); i++ {
if !equalExpr(t, expected[i], actual[i]) {
return false
}
}
return true
}
func equalStmts(t *testing.T, expected, actual []ast.Stmt) bool {
if !assert.Equal(t, len(expected), len(actual)) {
return false
}
for i := 0; i < len(expected); i++ {
if !equalStmt(t, expected[i], actual[i]) {
return false
}
}
return true
}
func equalMapElements(t *testing.T, expected, actual []*ast.MapElementLit) bool {
if !assert.Equal(t, len(expected), len(actual)) {
return false
}
for i := 0; i < len(expected); i++ {
if !assert.Equal(t, expected[i].Key, actual[i].Key) ||
!assert.Equal(t, expected[i].KeyPos, actual[i].KeyPos) ||
!assert.Equal(t, expected[i].ColonPos, actual[i].ColonPos) ||
!equalExpr(t, expected[i].Value, actual[i].Value) {
return false
}
}
return true
}