931 lines
15 KiB
Go
931 lines
15 KiB
Go
|
package parser
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/robertkrimen/otto/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.VariableExpression:
|
||
|
return []interface{}{node.Name, 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", marshal("", 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.Property:
|
||
|
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))
|
||
|
|
||
|
}
|
||
|
|
||
|
{
|
||
|
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
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if node != nil {
|
||
|
fmt.Fprintf(os.Stderr, "testMarshalNode(%T)\n", node)
|
||
|
}
|
||
|
|
||
|
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": "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": "abc",
|
||
|
"Value": {
|
||
|
"Literal": "'def'"
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
]
|
||
|
`)
|
||
|
|
||
|
test(`
|
||
|
[,]
|
||
|
---
|
||
|
[
|
||
|
{
|
||
|
"Array": [
|
||
|
null
|
||
|
]
|
||
|
}
|
||
|
]
|
||
|
`)
|
||
|
|
||
|
test(`
|
||
|
[,,]
|
||
|
---
|
||
|
[
|
||
|
{
|
||
|
"Array": [
|
||
|
null,
|
||
|
null
|
||
|
]
|
||
|
}
|
||
|
]
|
||
|
`)
|
||
|
|
||
|
test(`
|
||
|
({ get abc() {} })
|
||
|
---
|
||
|
[
|
||
|
{
|
||
|
"Object": [
|
||
|
{
|
||
|
"Key": "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": [
|
||
|
[
|
||
|
"abc",
|
||
|
{
|
||
|
"Call": {
|
||
|
"ArgumentList": [],
|
||
|
"Callee": {
|
||
|
"Call": {
|
||
|
"ArgumentList": [
|
||
|
{
|
||
|
"Function": {
|
||
|
"BlockStatement": []
|
||
|
}
|
||
|
}
|
||
|
],
|
||
|
"Callee": {
|
||
|
"Literal": 1
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
]
|
||
|
}
|
||
|
]
|
||
|
`)
|
||
|
|
||
|
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": [
|
||
|
"abc",
|
||
|
null
|
||
|
],
|
||
|
"Source": {
|
||
|
"Identifier": "def"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
`)
|
||
|
|
||
|
test(`
|
||
|
abc = {
|
||
|
'"': "'",
|
||
|
"'": '"',
|
||
|
}
|
||
|
---
|
||
|
[
|
||
|
{
|
||
|
"Assign": {
|
||
|
"Left": {
|
||
|
"Identifier": "abc"
|
||
|
},
|
||
|
"Right": {
|
||
|
"Object": [
|
||
|
{
|
||
|
"Key": "\"",
|
||
|
"Value": {
|
||
|
"Literal": "\"'\""
|
||
|
}
|
||
|
},
|
||
|
{
|
||
|
"Key": "'",
|
||
|
"Value": {
|
||
|
"Literal": "'\"'"
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
`)
|
||
|
|
||
|
return
|
||
|
|
||
|
test(`
|
||
|
if (!abc && abc.jkl(def) && abc[0] === +abc[0] && abc.length < ghi) {
|
||
|
}
|
||
|
---
|
||
|
[
|
||
|
{
|
||
|
"If": {
|
||
|
"Consequent": {
|
||
|
"BlockStatement": []
|
||
|
},
|
||
|
"Test": {
|
||
|
"BinaryExpression": {
|
||
|
"Left": {
|
||
|
"BinaryExpression": {
|
||
|
"Left": {
|
||
|
"BinaryExpression": {
|
||
|
"Left": null,
|
||
|
"Operator": "\u0026\u0026",
|
||
|
"Right": {
|
||
|
"Call": {
|
||
|
"ArgumentList": [
|
||
|
{
|
||
|
"Identifier": "def"
|
||
|
}
|
||
|
],
|
||
|
"Callee": {
|
||
|
"Dot": {
|
||
|
"Left": {
|
||
|
"Identifier": "abc"
|
||
|
},
|
||
|
"Member": "jkl"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
"Operator": "\u0026\u0026",
|
||
|
"Right": {
|
||
|
"BinaryExpression": {
|
||
|
"Left": null,
|
||
|
"Operator": "===",
|
||
|
"Right": null
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
"Operator": "\u0026\u0026",
|
||
|
"Right": {
|
||
|
"BinaryExpression": {
|
||
|
"Left": {
|
||
|
"Dot": {
|
||
|
"Left": {
|
||
|
"Identifier": "abc"
|
||
|
},
|
||
|
"Member": "length"
|
||
|
}
|
||
|
},
|
||
|
"Operator": "\u003c",
|
||
|
"Right": {
|
||
|
"Identifier": "ghi"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
`)
|
||
|
})
|
||
|
}
|