2024-03-26 08:45:18 +01:00
|
|
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
|
|
// All rights reserved.
|
|
|
|
|
2024-03-26 07:00:53 +01:00
|
|
|
// ast.go
|
|
|
|
package expr
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
//-------- ast
|
|
|
|
|
|
|
|
type ast struct {
|
2024-03-31 05:57:02 +02:00
|
|
|
forest []*term
|
2024-03-26 07:00:53 +01:00
|
|
|
root *term
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewAst() *ast {
|
|
|
|
return &ast{}
|
|
|
|
}
|
|
|
|
|
2024-03-31 05:57:02 +02:00
|
|
|
func (self *ast) ToForest() {
|
|
|
|
if self.root != nil {
|
|
|
|
if self.forest == nil {
|
|
|
|
self.forest = make([]*term, 0)
|
|
|
|
}
|
|
|
|
self.forest = append(self.forest, self.root)
|
|
|
|
self.root = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-26 07:00:53 +01:00
|
|
|
func (self *ast) String() string {
|
|
|
|
var sb strings.Builder
|
|
|
|
if self.root == nil {
|
|
|
|
sb.WriteString("(nil)")
|
|
|
|
} else {
|
|
|
|
self.root.toString(&sb)
|
|
|
|
}
|
|
|
|
return sb.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *ast) addTokens(tokens ...*Token) (err error) {
|
|
|
|
for _, tk := range tokens {
|
|
|
|
if err = self.addToken(tk); err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *ast) addToken(tk *Token) (err error) {
|
|
|
|
if t := newTerm(tk, nil); t != nil {
|
|
|
|
err = self.addTerm(t)
|
|
|
|
} else {
|
|
|
|
err = fmt.Errorf("No term constructor for token %q", tk.String())
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *ast) addTerm(node *term) (err error) {
|
|
|
|
if self.root == nil {
|
|
|
|
self.root = node
|
|
|
|
} else {
|
|
|
|
self.root, err = self.insert(self.root, node)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *ast) insert(tree, node *term) (root *term, err error) {
|
|
|
|
if tree.getPriority() < node.getPriority() {
|
|
|
|
root = tree
|
|
|
|
if tree.isComplete() {
|
|
|
|
var subRoot *term
|
|
|
|
last := tree.removeLastChild()
|
|
|
|
subRoot, err = self.insert(last, node)
|
|
|
|
subRoot.setParent(tree)
|
|
|
|
} else {
|
|
|
|
node.setParent(tree)
|
|
|
|
}
|
|
|
|
} else if !node.isLeaf() {
|
|
|
|
root = node
|
|
|
|
tree.setParent(node)
|
|
|
|
} else {
|
|
|
|
err = fmt.Errorf("two adjacent operators: %q and %q", tree, node)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-03-31 05:57:02 +02:00
|
|
|
func (self *ast) Finish() {
|
|
|
|
if self.root == nil && self.forest != nil && len(self.forest) >= 1 {
|
|
|
|
self.root = self.forest[len(self.forest)-1]
|
|
|
|
self.forest = self.forest[0:len(self.forest) - 1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *ast) Eval(ctx exprContext) (result any, err error) {
|
|
|
|
self.Finish()
|
|
|
|
|
2024-03-26 07:00:53 +01:00
|
|
|
if self.root != nil {
|
2024-03-31 05:06:24 +02:00
|
|
|
initDefaultVars(ctx)
|
2024-03-31 05:57:02 +02:00
|
|
|
if self.forest != nil {
|
|
|
|
for i, root := range self.forest {
|
|
|
|
if result, err = root.compute(ctx); err == nil {
|
|
|
|
ctx.SetValue(preset_last_result, result)
|
|
|
|
} else {
|
|
|
|
err = fmt.Errorf("error in expression nr %d: %v", i+1, err)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err == nil {
|
|
|
|
result, err = self.root.compute(ctx)
|
|
|
|
}
|
2024-03-26 07:00:53 +01:00
|
|
|
} else {
|
|
|
|
err = errors.New("empty expression")
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2024-03-31 05:06:24 +02:00
|
|
|
|
|
|
|
// Preset variables
|
|
|
|
const (
|
2024-03-31 05:57:02 +02:00
|
|
|
preset_last_result = "_last"
|
2024-03-31 05:06:24 +02:00
|
|
|
preset_bool_shortcut = "_bool_shortcut"
|
|
|
|
)
|
|
|
|
|
|
|
|
func initDefaultVars(ctx exprContext) {
|
|
|
|
ctx.SetValue(preset_bool_shortcut, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
func enable(ctx exprContext, name string) {
|
|
|
|
if strings.HasPrefix(name, "_") {
|
|
|
|
ctx.SetValue(name, true)
|
|
|
|
} else {
|
|
|
|
ctx.SetValue("_"+name, true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func disable(ctx exprContext, name string) {
|
|
|
|
if strings.HasPrefix(name, "_") {
|
|
|
|
ctx.SetValue(name, false)
|
|
|
|
} else {
|
|
|
|
ctx.SetValue("_"+name, false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func isEnabled(ctx exprContext, name string) (status bool) {
|
|
|
|
if v, exists := ctx.GetValue(name); exists {
|
|
|
|
if b, ok := v.(bool); ok {
|
|
|
|
status = b
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|