mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
@@ -216,6 +216,13 @@ type VariableDeclaratorNode struct {
|
||||
Expr Node // Expression of the variable. Like "foo + 1" in "let foo = 1; foo + 1".
|
||||
}
|
||||
|
||||
// SequenceNode represents a sequence of nodes separated by semicolons.
|
||||
// All nodes are executed, only the last node will be returned.
|
||||
type SequenceNode struct {
|
||||
base
|
||||
Nodes []Node
|
||||
}
|
||||
|
||||
// ArrayNode represents an array.
|
||||
type ArrayNode struct {
|
||||
base
|
||||
|
||||
+12
-4
@@ -83,8 +83,8 @@ func (n *BinaryNode) String() string {
|
||||
if operator.Less(lb.Operator, n.Operator) {
|
||||
lwrap = true
|
||||
}
|
||||
if operator.Binary[lb.Operator].Precedence ==
|
||||
operator.Binary[n.Operator].Precedence &&
|
||||
if operator.Binary[lb.Operator].Precedence ==
|
||||
operator.Binary[n.Operator].Precedence &&
|
||||
operator.Binary[n.Operator].Associativity == operator.Right {
|
||||
lwrap = true
|
||||
}
|
||||
@@ -99,8 +99,8 @@ func (n *BinaryNode) String() string {
|
||||
if operator.Less(rb.Operator, n.Operator) {
|
||||
rwrap = true
|
||||
}
|
||||
if operator.Binary[rb.Operator].Precedence ==
|
||||
operator.Binary[n.Operator].Precedence &&
|
||||
if operator.Binary[rb.Operator].Precedence ==
|
||||
operator.Binary[n.Operator].Precedence &&
|
||||
operator.Binary[n.Operator].Associativity == operator.Left {
|
||||
rwrap = true
|
||||
}
|
||||
@@ -198,6 +198,14 @@ func (n *VariableDeclaratorNode) String() string {
|
||||
return fmt.Sprintf("let %s = %s; %s", n.Name, n.Value.String(), n.Expr.String())
|
||||
}
|
||||
|
||||
func (n *SequenceNode) String() string {
|
||||
nodes := make([]string, len(n.Nodes))
|
||||
for i, node := range n.Nodes {
|
||||
nodes[i] = node.String()
|
||||
}
|
||||
return strings.Join(nodes, "; ")
|
||||
}
|
||||
|
||||
func (n *ConditionalNode) String() string {
|
||||
var cond, exp1, exp2 string
|
||||
if _, ok := n.Cond.(*ConditionalNode); ok {
|
||||
|
||||
@@ -51,6 +51,10 @@ func Walk(node *Node, v Visitor) {
|
||||
case *VariableDeclaratorNode:
|
||||
Walk(&n.Value, v)
|
||||
Walk(&n.Expr, v)
|
||||
case *SequenceNode:
|
||||
for i := range n.Nodes {
|
||||
Walk(&n.Nodes[i], v)
|
||||
}
|
||||
case *ConditionalNode:
|
||||
Walk(&n.Cond, v)
|
||||
Walk(&n.Exp1, v)
|
||||
|
||||
+28
-9
@@ -49,12 +49,11 @@ type parser struct {
|
||||
current Token
|
||||
pos int
|
||||
err *file.Error
|
||||
depth int // predicate call depth
|
||||
config *conf.Config
|
||||
depth int // predicate call depth
|
||||
nodeCount uint // tracks number of AST nodes created
|
||||
}
|
||||
|
||||
// checkNodeLimit verifies that adding a new node won't exceed configured limits
|
||||
func (p *parser) checkNodeLimit() error {
|
||||
p.nodeCount++
|
||||
if p.config.MaxNodes > 0 && p.nodeCount > p.config.MaxNodes {
|
||||
@@ -64,7 +63,6 @@ func (p *parser) checkNodeLimit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// createNode handles creation of regular nodes
|
||||
func (p *parser) createNode(n Node, loc file.Location) Node {
|
||||
if err := p.checkNodeLimit(); err != nil {
|
||||
return nil
|
||||
@@ -76,7 +74,6 @@ func (p *parser) createNode(n Node, loc file.Location) Node {
|
||||
return n
|
||||
}
|
||||
|
||||
// createMemberNode handles creation of member nodes
|
||||
func (p *parser) createMemberNode(n *MemberNode, loc file.Location) *MemberNode {
|
||||
if err := p.checkNodeLimit(); err != nil {
|
||||
return nil
|
||||
@@ -163,6 +160,27 @@ func (p *parser) expect(kind Kind, values ...string) {
|
||||
|
||||
// parse functions
|
||||
|
||||
func (p *parser) parseSequenceExpression() Node {
|
||||
nodes := []Node{p.parseExpression(0)}
|
||||
|
||||
for p.current.Is(Operator, ";") && p.err == nil {
|
||||
p.next()
|
||||
// If a trailing semicolon is present, break out.
|
||||
if p.current.Is(EOF) {
|
||||
break
|
||||
}
|
||||
nodes = append(nodes, p.parseExpression(0))
|
||||
}
|
||||
|
||||
if len(nodes) == 1 {
|
||||
return nodes[0]
|
||||
}
|
||||
|
||||
return p.createNode(&SequenceNode{
|
||||
Nodes: nodes,
|
||||
}, nodes[0].Location())
|
||||
}
|
||||
|
||||
func (p *parser) parseExpression(precedence int) Node {
|
||||
if p.err != nil {
|
||||
return nil
|
||||
@@ -171,6 +189,7 @@ func (p *parser) parseExpression(precedence int) Node {
|
||||
if precedence == 0 && p.current.Is(Operator, "let") {
|
||||
return p.parseVariableDeclaration()
|
||||
}
|
||||
|
||||
if p.current.Is(Operator, "if") {
|
||||
return p.parseConditionalIf()
|
||||
}
|
||||
@@ -283,11 +302,11 @@ func (p *parser) parseConditionalIf() Node {
|
||||
p.next()
|
||||
nodeCondition := p.parseExpression(0)
|
||||
p.expect(Bracket, "{")
|
||||
expr1 := p.parseExpression(0)
|
||||
expr1 := p.parseSequenceExpression()
|
||||
p.expect(Bracket, "}")
|
||||
p.expect(Operator, "else")
|
||||
p.expect(Bracket, "{")
|
||||
expr2 := p.parseExpression(0)
|
||||
expr2 := p.parseSequenceExpression()
|
||||
p.expect(Bracket, "}")
|
||||
|
||||
return &ConditionalNode{
|
||||
@@ -304,13 +323,13 @@ func (p *parser) parseConditional(node Node) Node {
|
||||
p.next()
|
||||
|
||||
if !p.current.Is(Operator, ":") {
|
||||
expr1 = p.parseExpression(0)
|
||||
expr1 = p.parseSequenceExpression()
|
||||
p.expect(Operator, ":")
|
||||
expr2 = p.parseExpression(0)
|
||||
expr2 = p.parseSequenceExpression()
|
||||
} else {
|
||||
p.next()
|
||||
expr1 = node
|
||||
expr2 = p.parseExpression(0)
|
||||
expr2 = p.parseSequenceExpression()
|
||||
}
|
||||
|
||||
node = p.createNode(&ConditionalNode{
|
||||
|
||||
@@ -659,6 +659,54 @@ world`},
|
||||
Exp1: &BoolNode{Value: true},
|
||||
Exp2: &IdentifierNode{Value: "x"}},
|
||||
},
|
||||
{
|
||||
"true ? 1; 2; 3 : 4",
|
||||
&ConditionalNode{
|
||||
Cond: &BoolNode{Value: true},
|
||||
Exp1: &SequenceNode{
|
||||
Nodes: []Node{
|
||||
&IntegerNode{Value: 1},
|
||||
&IntegerNode{Value: 2},
|
||||
&IntegerNode{Value: 3}}},
|
||||
Exp2: &IntegerNode{Value: 4}},
|
||||
},
|
||||
{
|
||||
"true ? 1 : 2; 3 ; 4",
|
||||
&ConditionalNode{
|
||||
Cond: &BoolNode{Value: true},
|
||||
Exp1: &IntegerNode{Value: 1},
|
||||
Exp2: &SequenceNode{
|
||||
Nodes: []Node{
|
||||
&IntegerNode{Value: 2},
|
||||
&IntegerNode{Value: 3},
|
||||
&IntegerNode{Value: 4}}}},
|
||||
},
|
||||
{
|
||||
"true ?: 1; 2; 3",
|
||||
&ConditionalNode{
|
||||
Cond: &BoolNode{Value: true},
|
||||
Exp1: &BoolNode{Value: true},
|
||||
Exp2: &SequenceNode{
|
||||
Nodes: []Node{
|
||||
&IntegerNode{Value: 1},
|
||||
&IntegerNode{Value: 2},
|
||||
&IntegerNode{Value: 3}}}},
|
||||
},
|
||||
{
|
||||
"if true { 1; 2; 3 } else { 4; 5; 6 }",
|
||||
&ConditionalNode{
|
||||
Cond: &BoolNode{Value: true},
|
||||
Exp1: &SequenceNode{
|
||||
Nodes: []Node{
|
||||
&IntegerNode{Value: 1},
|
||||
&IntegerNode{Value: 2},
|
||||
&IntegerNode{Value: 3}}},
|
||||
Exp2: &SequenceNode{
|
||||
Nodes: []Node{
|
||||
&IntegerNode{Value: 4},
|
||||
&IntegerNode{Value: 5},
|
||||
&IntegerNode{Value: 6}}}},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.input, func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user