// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // ast.go package expr import ( "errors" "fmt" "strings" ) //-------- ast type ast struct { root *term } func NewAst() *ast { return &ast{} } 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 } func (self *ast) eval(ctx exprContext) (result any, err error) { if self.root != nil { initDefaultVars(ctx) result, err = self.root.compute(ctx) } else { err = errors.New("empty expression") } return } // Preset variables const ( 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 }