ai_old/goja/parser/marshal_test.go
2024-09-20 16:50:35 +08:00

891 lines
14 KiB
Go

package parser
import (
"bytes"
"encoding/json"
"reflect"
"strings"
"testing"
"github.com/dop251/goja/ast"
)
func marshal(name string, children ...interface{}) interface{} {
if len(children) == 1 {
if name == "" {
return testMarshalNode(children[0])
}
return map[string]interface{}{
name: children[0],
}
}
map_ := map[string]interface{}{}
length := len(children) / 2
for i := 0; i < length; i++ {
name := children[i*2].(string)
value := children[i*2+1]
map_[name] = value
}
if name == "" {
return map_
}
return map[string]interface{}{
name: map_,
}
}
func testMarshalNode(node interface{}) interface{} {
switch node := node.(type) {
// Expression
case *ast.ArrayLiteral:
return marshal("Array", testMarshalNode(node.Value))
case *ast.AssignExpression:
return marshal("Assign",
"Left", testMarshalNode(node.Left),
"Right", testMarshalNode(node.Right),
)
case *ast.BinaryExpression:
return marshal("BinaryExpression",
"Operator", node.Operator.String(),
"Left", testMarshalNode(node.Left),
"Right", testMarshalNode(node.Right),
)
case *ast.BooleanLiteral:
return marshal("Literal", node.Value)
case *ast.CallExpression:
return marshal("Call",
"Callee", testMarshalNode(node.Callee),
"ArgumentList", testMarshalNode(node.ArgumentList),
)
case *ast.ConditionalExpression:
return marshal("Conditional",
"Test", testMarshalNode(node.Test),
"Consequent", testMarshalNode(node.Consequent),
"Alternate", testMarshalNode(node.Alternate),
)
case *ast.DotExpression:
return marshal("Dot",
"Left", testMarshalNode(node.Left),
"Member", node.Identifier.Name,
)
case *ast.NewExpression:
return marshal("New",
"Callee", testMarshalNode(node.Callee),
"ArgumentList", testMarshalNode(node.ArgumentList),
)
case *ast.NullLiteral:
return marshal("Literal", nil)
case *ast.NumberLiteral:
return marshal("Literal", node.Value)
case *ast.ObjectLiteral:
return marshal("Object", testMarshalNode(node.Value))
case *ast.RegExpLiteral:
return marshal("Literal", node.Literal)
case *ast.StringLiteral:
return marshal("Literal", node.Literal)
case *ast.Binding:
return marshal("Binding", "Target", testMarshalNode(node.Target),
"Initializer", testMarshalNode(node.Initializer))
// Statement
case *ast.Program:
return testMarshalNode(node.Body)
case *ast.BlockStatement:
return marshal("BlockStatement", testMarshalNode(node.List))
case *ast.EmptyStatement:
return "EmptyStatement"
case *ast.ExpressionStatement:
return testMarshalNode(node.Expression)
case *ast.ForInStatement:
return marshal("ForIn",
"Into", testMarshalNode(node.Into),
"Source", marshal("", node.Source),
"Body", marshal("", node.Body),
)
case *ast.FunctionLiteral:
return marshal("Function", testMarshalNode(node.Body))
case *ast.Identifier:
return marshal("Identifier", node.Name)
case *ast.IfStatement:
if_ := marshal("",
"Test", testMarshalNode(node.Test),
"Consequent", testMarshalNode(node.Consequent),
).(map[string]interface{})
if node.Alternate != nil {
if_["Alternate"] = testMarshalNode(node.Alternate)
}
return marshal("If", if_)
case *ast.LabelledStatement:
return marshal("Label",
"Name", node.Label.Name,
"Statement", testMarshalNode(node.Statement),
)
case *ast.PropertyKeyed:
return marshal("",
"Key", node.Key,
"Value", testMarshalNode(node.Value),
)
case *ast.ReturnStatement:
return marshal("Return", testMarshalNode(node.Argument))
case *ast.SequenceExpression:
return marshal("Sequence", testMarshalNode(node.Sequence))
case *ast.ThrowStatement:
return marshal("Throw", testMarshalNode(node.Argument))
case *ast.VariableStatement:
return marshal("Var", testMarshalNode(node.List))
// Special
case *ast.ForDeclaration:
return marshal("For-Into-Decl", testMarshalNode(node.Target))
case *ast.ForIntoVar:
return marshal("For-Into-Var", testMarshalNode(node.Binding))
}
{
value := reflect.ValueOf(node)
if value.Kind() == reflect.Slice {
tmp0 := []interface{}{}
for index := 0; index < value.Len(); index++ {
tmp0 = append(tmp0, testMarshalNode(value.Index(index).Interface()))
}
return tmp0
}
}
return nil
}
func testMarshal(node interface{}) string {
value, err := json.Marshal(testMarshalNode(node))
if err != nil {
panic(err)
}
return string(value)
}
func TestParserAST(t *testing.T) {
tt(t, func() {
test := func(inputOutput string) {
match := matchBeforeAfterSeparator.FindStringIndex(inputOutput)
input := strings.TrimSpace(inputOutput[0:match[0]])
wantOutput := strings.TrimSpace(inputOutput[match[1]:])
_, program, err := testParse(input)
is(err, nil)
haveOutput := testMarshal(program)
tmp0, tmp1 := bytes.Buffer{}, bytes.Buffer{}
json.Indent(&tmp0, []byte(haveOutput), "\t\t", " ")
json.Indent(&tmp1, []byte(wantOutput), "\t\t", " ")
is("\n\t\t"+tmp0.String(), "\n\t\t"+tmp1.String())
}
test(`
---
[]
`)
test(`
;
---
[
"EmptyStatement"
]
`)
test(`
;;;
---
[
"EmptyStatement",
"EmptyStatement",
"EmptyStatement"
]
`)
test(`
1; true; abc; "abc"; null;
---
[
{
"Literal": 1
},
{
"Literal": true
},
{
"Identifier": "abc"
},
{
"Literal": "\"abc\""
},
{
"Literal": null
}
]
`)
test(`
{ 1; null; 3.14159; ; }
---
[
{
"BlockStatement": [
{
"Literal": 1
},
{
"Literal": null
},
{
"Literal": 3.14159
},
"EmptyStatement"
]
}
]
`)
test(`
new abc();
---
[
{
"New": {
"ArgumentList": [],
"Callee": {
"Identifier": "abc"
}
}
}
]
`)
test(`
new abc(1, 3.14159)
---
[
{
"New": {
"ArgumentList": [
{
"Literal": 1
},
{
"Literal": 3.14159
}
],
"Callee": {
"Identifier": "abc"
}
}
}
]
`)
test(`
true ? false : true
---
[
{
"Conditional": {
"Alternate": {
"Literal": true
},
"Consequent": {
"Literal": false
},
"Test": {
"Literal": true
}
}
}
]
`)
test(`
true || false
---
[
{
"BinaryExpression": {
"Left": {
"Literal": true
},
"Operator": "||",
"Right": {
"Literal": false
}
}
}
]
`)
test(`
0 + { abc: true }
---
[
{
"BinaryExpression": {
"Left": {
"Literal": 0
},
"Operator": "+",
"Right": {
"Object": [
{
"Key": {
"Idx": 7,
"Literal": "abc",
"Value": "abc"
},
"Value": {
"Literal": true
}
}
]
}
}
}
]
`)
test(`
1 == "1"
---
[
{
"BinaryExpression": {
"Left": {
"Literal": 1
},
"Operator": "==",
"Right": {
"Literal": "\"1\""
}
}
}
]
`)
test(`
abc(1)
---
[
{
"Call": {
"ArgumentList": [
{
"Literal": 1
}
],
"Callee": {
"Identifier": "abc"
}
}
}
]
`)
test(`
Math.pow(3, 2)
---
[
{
"Call": {
"ArgumentList": [
{
"Literal": 3
},
{
"Literal": 2
}
],
"Callee": {
"Dot": {
"Left": {
"Identifier": "Math"
},
"Member": "pow"
}
}
}
}
]
`)
test(`
1, 2, 3
---
[
{
"Sequence": [
{
"Literal": 1
},
{
"Literal": 2
},
{
"Literal": 3
}
]
}
]
`)
test(`
/ abc /gim;
---
[
{
"Literal": "/ abc /gim"
}
]
`)
test(`
if (0)
1;
---
[
{
"If": {
"Consequent": {
"Literal": 1
},
"Test": {
"Literal": 0
}
}
}
]
`)
test(`
0+function(){
return;
}
---
[
{
"BinaryExpression": {
"Left": {
"Literal": 0
},
"Operator": "+",
"Right": {
"Function": {
"BlockStatement": [
{
"Return": null
}
]
}
}
}
}
]
`)
test(`
xyzzy // Ignore it
// Ignore this
// And this
/* And all..
... of this!
*/
"Nothing happens."
// And finally this
---
[
{
"Identifier": "xyzzy"
},
{
"Literal": "\"Nothing happens.\""
}
]
`)
test(`
((x & (x = 1)) !== 0)
---
[
{
"BinaryExpression": {
"Left": {
"BinaryExpression": {
"Left": {
"Identifier": "x"
},
"Operator": "\u0026",
"Right": {
"Assign": {
"Left": {
"Identifier": "x"
},
"Right": {
"Literal": 1
}
}
}
}
},
"Operator": "!==",
"Right": {
"Literal": 0
}
}
}
]
`)
test(`
{ abc: 'def' }
---
[
{
"BlockStatement": [
{
"Label": {
"Name": "abc",
"Statement": {
"Literal": "'def'"
}
}
}
]
}
]
`)
test(`
// This is not an object, this is a string literal with a label!
({ abc: 'def' })
---
[
{
"Object": [
{
"Key": {
"Idx": 77,
"Literal": "abc",
"Value": "abc"
},
"Value": {
"Literal": "'def'"
}
}
]
}
]
`)
test(`
[,]
---
[
{
"Array": [
null
]
}
]
`)
test(`
[,,]
---
[
{
"Array": [
null,
null
]
}
]
`)
test(`
({ get abc() {} })
---
[
{
"Object": [
{
"Key": {
"Idx": 8,
"Literal": "abc",
"Value": "abc"
},
"Value": {
"Function": {
"BlockStatement": []
}
}
}
]
}
]
`)
test(`
/abc/.source
---
[
{
"Dot": {
"Left": {
"Literal": "/abc/"
},
"Member": "source"
}
}
]
`)
test(`
xyzzy
throw new TypeError("Nothing happens.")
---
[
{
"Identifier": "xyzzy"
},
{
"Throw": {
"New": {
"ArgumentList": [
{
"Literal": "\"Nothing happens.\""
}
],
"Callee": {
"Identifier": "TypeError"
}
}
}
}
]
`)
// When run, this will call a type error to be thrown
// This is essentially the same as:
//
// var abc = 1(function(){})()
//
test(`
var abc = 1
(function(){
})()
---
[
{
"Var": [
{
"Binding": {
"Initializer": {
"Call": {
"ArgumentList": [],
"Callee": {
"Call": {
"ArgumentList": [
{
"Function": {
"BlockStatement": []
}
}
],
"Callee": {
"Literal": 1
}
}
}
}
},
"Target": {
"Identifier": "abc"
}
}
}
]
}
]
`)
test(`
"use strict"
---
[
{
"Literal": "\"use strict\""
}
]
`)
test(`
"use strict"
abc = 1 + 2 + 11
---
[
{
"Literal": "\"use strict\""
},
{
"Assign": {
"Left": {
"Identifier": "abc"
},
"Right": {
"BinaryExpression": {
"Left": {
"BinaryExpression": {
"Left": {
"Literal": 1
},
"Operator": "+",
"Right": {
"Literal": 2
}
}
},
"Operator": "+",
"Right": {
"Literal": 11
}
}
}
}
}
]
`)
test(`
abc = function() { 'use strict' }
---
[
{
"Assign": {
"Left": {
"Identifier": "abc"
},
"Right": {
"Function": {
"BlockStatement": [
{
"Literal": "'use strict'"
}
]
}
}
}
}
]
`)
test(`
for (var abc in def) {
}
---
[
{
"ForIn": {
"Body": {
"BlockStatement": []
},
"Into": {
"For-Into-Var": {
"Binding": {
"Initializer": null,
"Target": {
"Identifier": "abc"
}
}
}
},
"Source": {
"Identifier": "def"
}
}
}
]
`)
test(`
abc = {
'"': "'",
"'": '"',
}
---
[
{
"Assign": {
"Left": {
"Identifier": "abc"
},
"Right": {
"Object": [
{
"Key": {
"Idx": 21,
"Literal": "'\"'",
"Value": "\""
},
"Value": {
"Literal": "\"'\""
}
},
{
"Key": {
"Idx": 43,
"Literal": "\"'\"",
"Value": "'"
},
"Value": {
"Literal": "'\"'"
}
}
]
}
}
}
]
`)
})
}