146 lines
3.1 KiB
Go
146 lines
3.1 KiB
Go
package expression
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"git.akyoto.dev/cli/q/src/build/token"
|
|
)
|
|
|
|
// Expression is a binary tree with an operator on each node.
|
|
type Expression struct {
|
|
Parent *Expression
|
|
Children []*Expression
|
|
Token token.Token
|
|
Precedence int8
|
|
}
|
|
|
|
// New creates a new expression.
|
|
func New() *Expression {
|
|
return &Expression{}
|
|
}
|
|
|
|
// NewLeaf creates a new leaf node.
|
|
func NewLeaf(t token.Token) *Expression {
|
|
return &Expression{
|
|
Token: t,
|
|
}
|
|
}
|
|
|
|
// AddChild adds a child to the expression.
|
|
func (expr *Expression) AddChild(child *Expression) {
|
|
if expr.Children == nil {
|
|
expr.Children = make([]*Expression, 0, 2)
|
|
}
|
|
|
|
expr.Children = append(expr.Children, child)
|
|
child.Parent = expr
|
|
}
|
|
|
|
// Count counts how often the given token appears in the expression.
|
|
func (expr *Expression) Count(buffer []byte, kind token.Kind, name string) int {
|
|
count := 0
|
|
|
|
expr.EachLeaf(func(leaf *Expression) error {
|
|
if leaf.Token.Kind == kind && leaf.Token.Text(buffer) == name {
|
|
count++
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return count
|
|
}
|
|
|
|
// Reset resets all values to the default.
|
|
func (expr *Expression) Reset() {
|
|
for _, child := range expr.Children {
|
|
child.Reset()
|
|
}
|
|
|
|
expr.Token.Reset()
|
|
expr.Parent = nil
|
|
expr.Children = expr.Children[:0]
|
|
expr.Precedence = 0
|
|
}
|
|
|
|
// EachLeaf iterates through all leaves in the tree.
|
|
func (expr *Expression) EachLeaf(call func(*Expression) error) error {
|
|
if expr.IsLeaf() {
|
|
return call(expr)
|
|
}
|
|
|
|
for _, child := range expr.Children {
|
|
err := child.EachLeaf(call)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemoveChild removes a child from the expression.
|
|
func (expr *Expression) RemoveChild(child *Expression) {
|
|
for i, c := range expr.Children {
|
|
if c == child {
|
|
expr.Children = append(expr.Children[:i], expr.Children[i+1:]...)
|
|
child.Parent = nil
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// Replace replaces the tree with the new expression and adds the previous expression to it.
|
|
func (expr *Expression) Replace(tree *Expression) {
|
|
if expr.Parent != nil {
|
|
expr.Parent.Children[len(expr.Parent.Children)-1] = tree
|
|
tree.Parent = expr.Parent
|
|
}
|
|
|
|
tree.AddChild(expr)
|
|
}
|
|
|
|
// IsLeaf returns true if the expression has no children.
|
|
func (expr *Expression) IsLeaf() bool {
|
|
return len(expr.Children) == 0
|
|
}
|
|
|
|
// LastChild returns the last child.
|
|
func (expr *Expression) LastChild() *Expression {
|
|
return expr.Children[len(expr.Children)-1]
|
|
}
|
|
|
|
// String generates a textual representation of the expression.
|
|
func (expr *Expression) String(data []byte) string {
|
|
builder := strings.Builder{}
|
|
expr.write(&builder, data)
|
|
return builder.String()
|
|
}
|
|
|
|
// write generates a textual representation of the expression.
|
|
func (expr *Expression) write(builder *strings.Builder, data []byte) {
|
|
if expr.IsLeaf() {
|
|
builder.WriteString(expr.Token.Text(data))
|
|
return
|
|
}
|
|
|
|
builder.WriteByte('(')
|
|
|
|
switch expr.Token.Kind {
|
|
case token.Call:
|
|
builder.WriteString("λ")
|
|
case token.Array:
|
|
builder.WriteString("@")
|
|
default:
|
|
builder.WriteString(expr.Token.Text(data))
|
|
}
|
|
|
|
for _, child := range expr.Children {
|
|
builder.WriteByte(' ')
|
|
child.write(builder, data)
|
|
}
|
|
|
|
builder.WriteByte(')')
|
|
}
|