expressions now support dict data-type

This commit is contained in:
Celestino Amoroso 2024-04-21 14:24:56 +02:00
parent b28d6a8f02
commit 323308d86f
9 changed files with 160 additions and 9 deletions

View File

@ -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)
}

View File

@ -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
View 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
}

View File

@ -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
} }

View File

@ -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)
} }

View File

@ -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)

View File

@ -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> "}"

View File

@ -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",

View File

@ -26,6 +26,11 @@ func isList(v any) (ok bool) {
return ok return ok
} }
func isDict(v any) (ok bool) {
_, ok = v.(map[any]any)
return ok
}
func isNumber(v any) (ok bool) { func isNumber(v any) (ok bool) {
return isFloat(v) || isInteger(v) return isFloat(v) || isInteger(v)
} }