expressions now support dict data-type
This commit is contained in:
parent
b28d6a8f02
commit
323308d86f
@ -140,3 +140,7 @@ func ImportImportFuncs(ctx ExprContext) {
|
|||||||
ctx.RegisterFunc("import", &simpleFunctor{f: importFunc}, 1, -1)
|
ctx.RegisterFunc("import", &simpleFunctor{f: importFunc}, 1, -1)
|
||||||
ctx.RegisterFunc("include", &simpleFunctor{f: includeFunc}, 1, -1)
|
ctx.RegisterFunc("include", &simpleFunctor{f: includeFunc}, 1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registerImport("import", ImportImportFuncs)
|
||||||
|
}
|
||||||
|
@ -162,3 +162,7 @@ func ImportOsFuncs(ctx ExprContext) {
|
|||||||
ctx.RegisterFunc("readFile", &simpleFunctor{f: readFileFunc}, 1, 2)
|
ctx.RegisterFunc("readFile", &simpleFunctor{f: readFileFunc}, 1, 2)
|
||||||
ctx.RegisterFunc("closeFile", &simpleFunctor{f: closeFileFunc}, 1, 1)
|
ctx.RegisterFunc("closeFile", &simpleFunctor{f: closeFileFunc}, 1, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registerImport("os", ImportOsFuncs)
|
||||||
|
}
|
||||||
|
38
operand-dict.go
Normal file
38
operand-dict.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operand-dict.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
// -------- dict term
|
||||||
|
// func newDictTermA(args ...*term) *term {
|
||||||
|
// return newDictTerm(args)
|
||||||
|
// }
|
||||||
|
|
||||||
|
func newDictTerm(args map[any]*term) *term {
|
||||||
|
return &term{
|
||||||
|
tk: *NewValueToken(0, 0, SymDict, "{}", args),
|
||||||
|
parent: nil,
|
||||||
|
children: nil,
|
||||||
|
position: posLeaf,
|
||||||
|
priority: priValue,
|
||||||
|
evalFunc: evalDict,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- dict func
|
||||||
|
func evalDict(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
dict, _ := self.value().(map[any]*term)
|
||||||
|
items := make(map[any]any, len(dict))
|
||||||
|
for key, tree := range dict {
|
||||||
|
var param any
|
||||||
|
if param, err = tree.compute(ctx); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
items[key] = param
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
v = items
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -23,8 +23,14 @@ func newVarTerm(tk *Token) *term {
|
|||||||
// -------- eval func
|
// -------- eval func
|
||||||
func evalVar(ctx ExprContext, self *term) (v any, err error) {
|
func evalVar(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var exists bool
|
var exists bool
|
||||||
if v, exists = ctx.GetVar(self.tk.source); !exists {
|
name := self.source()
|
||||||
err = fmt.Errorf("undefined variable %q", self.tk.source)
|
if v, exists = ctx.GetVar(name); !exists {
|
||||||
|
info := ctx.GetFuncInfo(name)
|
||||||
|
if info != nil {
|
||||||
|
v = info.Functor()
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("undefined variable or function %q", name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
// operator-dot.go
|
// operator-dot.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
// -------- dot term
|
// -------- dot term
|
||||||
func newDotTerm(tk *Token) (inst *term) {
|
func newDotTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
@ -23,15 +25,13 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
indexTerm := self.children[1]
|
indexTerm := self.children[1]
|
||||||
if !isInteger(rightValue) {
|
|
||||||
err = indexTerm.Errorf("index expression must be integer, got %T", rightValue)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
index64, _ := rightValue.(int64)
|
|
||||||
index := int(index64)
|
|
||||||
|
|
||||||
if isList(leftValue) {
|
if isList(leftValue) {
|
||||||
|
var index int
|
||||||
|
if index, err = indexTerm.toInt(rightValue, "index expression value must be integer"); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
list, _ := leftValue.([]any)
|
list, _ := leftValue.([]any)
|
||||||
if index >= 0 && index < len(list) {
|
if index >= 0 && index < len(list) {
|
||||||
v = list[index]
|
v = list[index]
|
||||||
@ -41,6 +41,11 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
err = indexTerm.Errorf("index %v out of bounds", index)
|
err = indexTerm.Errorf("index %v out of bounds", index)
|
||||||
}
|
}
|
||||||
} else if isString(leftValue) {
|
} else if isString(leftValue) {
|
||||||
|
var index int
|
||||||
|
if index, err = indexTerm.toInt(rightValue, "index expression value must be integer"); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
s, _ := leftValue.(string)
|
s, _ := leftValue.(string)
|
||||||
if index >= 0 && index < len(s) {
|
if index >= 0 && index < len(s) {
|
||||||
v = string(s[index])
|
v = string(s[index])
|
||||||
@ -49,6 +54,12 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
} else {
|
} else {
|
||||||
err = indexTerm.Errorf("index %v out of bounds", index)
|
err = indexTerm.Errorf("index %v out of bounds", index)
|
||||||
}
|
}
|
||||||
|
} else if isDict(leftValue) {
|
||||||
|
var ok bool
|
||||||
|
d, _ := leftValue.(map[any]any)
|
||||||
|
if v, ok = d[rightValue]; !ok {
|
||||||
|
err = fmt.Errorf("key %v does not belong to the dictionary", rightValue)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
|
73
parser.go
73
parser.go
@ -97,16 +97,22 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
||||||
args := make([]*term, 0)
|
args := make([]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
|
itemExpected := false
|
||||||
for lastSym != SymClosedSquare && lastSym != SymEos {
|
for lastSym != SymClosedSquare && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
|
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
|
||||||
if subTree.root != nil {
|
if subTree.root != nil {
|
||||||
args = append(args, subTree.root)
|
args = append(args, subTree.root)
|
||||||
|
} else if itemExpected {
|
||||||
|
prev := scanner.Previous()
|
||||||
|
err = prev.Errorf("expected list item, got %q", prev)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
lastSym = scanner.Previous().Sym
|
lastSym = scanner.Previous().Sym
|
||||||
|
itemExpected = lastSym == SymComma
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO Check arguments
|
// TODO Check arguments
|
||||||
@ -119,6 +125,67 @@ func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, err error) {
|
||||||
|
tk := scanner.Next()
|
||||||
|
if tk.Sym == SymError {
|
||||||
|
err = tk.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tk.Sym == SymClosedBrace || tk.Sym == SymEos {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tk.Sym == SymInteger || tk.Sym == SymString {
|
||||||
|
tkSep := scanner.Next()
|
||||||
|
if tkSep.Sym != SymColon {
|
||||||
|
err = tkSep.Errorf("expected \":\", got %q", tkSep)
|
||||||
|
} else {
|
||||||
|
key = tk.Value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = tk.Errorf("expected dictionary key or closed brace, got %q", tk)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
||||||
|
args := make(map[any]*term, 0)
|
||||||
|
lastSym := SymUnknown
|
||||||
|
itemExpected := false
|
||||||
|
for lastSym != SymClosedBrace && lastSym != SymEos {
|
||||||
|
var subTree *ast
|
||||||
|
var key any
|
||||||
|
if key, err = self.parseDictKey(scanner, allowVarRef); err != nil {
|
||||||
|
break
|
||||||
|
} else if key == nil && itemExpected {
|
||||||
|
tk := scanner.Previous()
|
||||||
|
err = tk.Errorf("expected dictionary key, got %q", tk)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil {
|
||||||
|
if subTree.root != nil {
|
||||||
|
args[key] = subTree.root
|
||||||
|
} else if key != nil {
|
||||||
|
prev := scanner.Previous()
|
||||||
|
err = prev.Errorf("expected dictionary value, got %q", prev)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
lastSym = scanner.Previous().Sym
|
||||||
|
itemExpected = lastSym == SymComma
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
// TODO Check arguments
|
||||||
|
if lastSym != SymClosedBrace {
|
||||||
|
err = scanner.Previous().Errorf("unterminate dictionary")
|
||||||
|
} else {
|
||||||
|
subtree = newDictTerm(args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
|
func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
|
||||||
var filterList *term
|
var filterList *term
|
||||||
var caseExpr *ast
|
var caseExpr *ast
|
||||||
@ -241,6 +308,12 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
|
|||||||
err = tree.addTerm(listTerm)
|
err = tree.addTerm(listTerm)
|
||||||
currentTerm = listTerm
|
currentTerm = listTerm
|
||||||
}
|
}
|
||||||
|
case SymOpenBrace:
|
||||||
|
var mapTerm *term
|
||||||
|
if mapTerm, err = self.parseDictionary(scanner, allowVarRef); err == nil {
|
||||||
|
err = tree.addTerm(mapTerm)
|
||||||
|
currentTerm = mapTerm
|
||||||
|
}
|
||||||
case SymEqual:
|
case SymEqual:
|
||||||
if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
||||||
currentTerm, err = tree.addToken2(tk)
|
currentTerm, err = tree.addToken2(tk)
|
||||||
|
@ -76,6 +76,7 @@ const (
|
|||||||
SymFuncCall
|
SymFuncCall
|
||||||
SymFuncDef
|
SymFuncDef
|
||||||
SymList
|
SymList
|
||||||
|
SymDict
|
||||||
SymExpression
|
SymExpression
|
||||||
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||||
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||||
|
9
term.go
9
term.go
@ -144,6 +144,15 @@ func (self *term) compute(ctx ExprContext) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *term) toInt(computedValue any, valueDescription string) (i int, err error) {
|
||||||
|
if index64, ok := computedValue.(int64); ok {
|
||||||
|
i = int(index64)
|
||||||
|
} else {
|
||||||
|
err = self.Errorf("%s, got %T", valueDescription, computedValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
|
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
|
||||||
return self.tk.Errorf(
|
return self.tk.Errorf(
|
||||||
"left operand '%v' [%T] and right operand '%v' [%T] are not compatible with operator %q",
|
"left operand '%v' [%T] and right operand '%v' [%T] are not compatible with operator %q",
|
||||||
|
Loading…
Reference in New Issue
Block a user