Add SequenceNode

Fixes #717
This commit is contained in:
Anton Medvedev
2025-03-08 09:27:09 +01:00
parent 02dc5c04fc
commit c146c2fc9b
5 changed files with 99 additions and 13 deletions
+7
View File
@@ -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
View File
@@ -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 {
+4
View File
@@ -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
View File
@@ -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{
+48
View File
@@ -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) {