Selector operator, multi-operand, added

This commit is contained in:
Celestino Amoroso 2024-04-08 22:16:07 +02:00
parent f74e523617
commit d3f388f7e1
10 changed files with 263 additions and 34 deletions

16
ast.go
View File

@ -54,7 +54,21 @@ func (self *ast) addTokens(tokens ...*Token) (err error) {
}
func (self *ast) addToken(tk *Token) (err error) {
if t := newTerm(tk, nil); t != nil {
_, err = self.addToken2(tk)
return
}
// func (self *ast) addToken(tk *Token) (err error) {
// if t := newTerm(tk, nil); t != nil {
// err = self.addTerm(t)
// } else {
// err = tk.Errorf("No term constructor for token %q", tk.String())
// }
// return
// }
func (self *ast) addToken2(tk *Token) (t *term, err error) {
if t = newTerm(tk, nil); t != nil {
err = self.addTerm(t)
} else {
err = tk.Errorf("No term constructor for token %q", tk.String())

View File

@ -13,7 +13,7 @@ func EvalString(ctx exprContext, source string) (result any, err error) {
scanner := NewScanner(r, DefaultTranslations())
parser := NewParser(ctx)
if tree, err = parser.parse(scanner); err == nil {
if tree, err = parser.Parse(scanner); err == nil {
result, err = tree.Eval(ctx, true)
}
return

View File

@ -4,7 +4,7 @@
// operand-expr.go
package expr
import "errors"
import "fmt"
// -------- expr term
func newExprTerm(tk *Token) *term {
@ -25,7 +25,7 @@ func evalExpr(ctx exprContext, self *term) (v any, err error) {
if expr, ok := self.value().(Expr); ok {
v, err = expr.Eval(ctx, false)
} else {
err = errors.New("invalid body of function definition")
err = fmt.Errorf("expression expected, got %T", self.value())
}
return
}

37
operand-selector-case.go Normal file
View File

@ -0,0 +1,37 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operand-selector-case.go
package expr
import "fmt"
// -------- selector case term
type selectorCase struct {
filterList *term
caseExpr Expr
}
func newSelectorCaseTerm(row, col int, filterList *term, caseExpr Expr) *term {
tk := NewValueToken(row, col, SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr})
return &term{
tk: *tk,
class: classVar,
kind: kindUnknown,
parent: nil,
children: nil,
position: posLeaf,
priority: priValue,
evalFunc: evalSelectorCase,
}
}
// -------- eval selector case
func evalSelectorCase(ctx exprContext, self *term) (v any, err error) {
var ok bool
if v, ok = self.value().(*selectorCase); !ok {
err = fmt.Errorf("selector-case expected, got %T", self.value())
}
return
}

70
operator-selector.go Normal file
View File

@ -0,0 +1,70 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-selector.go
package expr
//-------- export all term
func newSelectorTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
class: classOperator,
kind: kindUnknown,
children: make([]*term, 0, 3),
position: posMultifix,
priority: priSelector,
evalFunc: evalSelector,
}
}
func isSelectorCase(ctx exprContext, exprValue, caseSel any, caseIndex int) (selectedValue any, err error) {
caseData, _ := caseSel.(*selectorCase)
if caseData.filterList == nil {
selectedValue, err = caseData.caseExpr.Eval(ctx, false)
} else {
filterList := caseData.filterList.children
if len(filterList) == 0 && exprValue == int64(caseIndex) {
selectedValue, err = caseData.caseExpr.Eval(ctx, false)
} else {
var caseValue any
for _, caseTerm := range filterList {
if caseValue, err = caseTerm.compute(ctx); err != nil || caseValue == exprValue {
selectedValue, err = caseData.caseExpr.Eval(ctx, false)
break
}
}
}
}
return
}
func evalSelector(ctx exprContext, self *term) (v any, err error) {
var exprValue any
// var caseList []*term
if err = self.checkOperands(); err != nil {
return
}
exprTerm := self.children[0]
if exprValue, err = exprTerm.compute(ctx); err != nil {
return
}
caseList := self.children[1:]
for i, caseTerm := range caseList {
caseSel := caseTerm.value()
if v, err = isSelectorCase(ctx, exprValue, caseSel, i); err != nil || v != nil {
break
}
}
if err == nil && v == nil {
err = exprTerm.tk.Errorf("no case catches the value (%v) of the selection expression", exprValue)
}
return
}
// init
func init() {
registerTermConstructor(SymSelector, newSelectorTerm)
}

View File

@ -94,7 +94,7 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
return
}
func (self *parser) parseList(scanner *scanner, allowVarRef bool) (tree *term, err error) {
func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
args := make([]*term, 0)
lastSym := SymUnknown
for lastSym != SymClosedSquare && lastSym != SymEos {
@ -113,21 +113,72 @@ func (self *parser) parseList(scanner *scanner, allowVarRef bool) (tree *term, e
if lastSym != SymClosedSquare {
err = scanner.Previous().Errorf("unterminate items list")
} else {
tree = newListTerm(args)
subtree = newListTerm(args)
}
}
return
}
func (self *parser) parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
return self.parseGeneral(scanner, true, false, termSymbols...)
func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
var filterList *term
var caseExpr *ast
tk := scanner.Next()
startRow := tk.row
startCol := tk.col
if tk.Sym == SymOpenSquare {
if defaultCase {
err = tk.Errorf("case list in default clause")
return
}
if filterList, err = self.parseList(scanner, allowVarRef); err != nil {
return
}
tk = scanner.Next()
startRow = tk.row
startCol = tk.col
} else if !defaultCase {
filterList = newListTerm(make([]*term, 0))
}
if tk.Sym == SymOpenBrace {
if caseExpr, err = self.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil {
return
}
} else {
err = tk.Errorf("selector-case expected, got %q", tk.source)
}
if err == nil {
caseTerm = newSelectorCaseTerm(startRow, startCol, filterList, caseExpr)
}
return
}
func (self *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) {
var caseTerm *term
tk := scanner.makeToken(SymSelector, '?')
if selectorTerm, err = tree.addToken2(tk); err != nil {
return
}
if caseTerm, err = self.parseSelectorCase(scanner, allowVarRef, false); err == nil {
selectorTerm.children = append(selectorTerm.children, caseTerm)
caseTerm.parent = selectorTerm
}
return
}
func (self *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
return self.parseGeneral(scanner, false, allowVarRef, termSymbols...)
}
func (self *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
return self.parseGeneral(scanner, true, false, termSymbols...)
}
func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
var selectorTerm *term = nil
var currentTerm *term = nil
tree = NewAst()
firstToken := true
lastSym := SymUnknown
@ -135,6 +186,7 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
if tk.Sym == SymComment {
continue
}
//resetSelector := true
if tk.Sym == SymSemiColon {
if allowForest {
@ -162,36 +214,64 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
var subTree *ast
if subTree, err = self.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil {
subTree.root.priority = priValue
tree.addTerm(subTree.root)
err = tree.addTerm(subTree.root)
currentTerm = subTree.root
}
case SymFuncCall:
var funcCallTerm *term
if funcCallTerm, err = self.parseFuncCall(scanner, allowVarRef, tk); err == nil {
err = tree.addTerm(funcCallTerm)
currentTerm = funcCallTerm
}
case SymOpenSquare:
var listTerm *term
if listTerm, err = self.parseList(scanner, allowVarRef); err == nil {
err = tree.addTerm(listTerm)
currentTerm = listTerm
}
case SymEqual:
if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
err = tree.addToken(tk)
currentTerm, err = tree.addToken2(tk)
}
case SymFuncDef:
var funcDefTerm *term
if funcDefTerm, err = self.parseFuncDef(scanner); err == nil {
err = tree.addTerm(funcDefTerm)
currentTerm = funcDefTerm
}
case SymIdentifier:
if tk.source[0] == '@' && !allowVarRef {
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
} else {
err = tree.addToken(tk)
currentTerm, err = tree.addToken2(tk)
}
case SymQuestion:
if selectorTerm != nil {
err = tk.Errorf("nested selectors must be enclosed in parentheses")
} else if selectorTerm, err = self.parseSelector(scanner, tree, allowVarRef); err == nil {
currentTerm = selectorTerm
}
case SymColon, SymDoubleColon:
var caseTerm *term
if selectorTerm == nil {
err = tk.Errorf("selector-case outside of a selector context")
} else if caseTerm, err = self.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
selectorTerm.children = append(selectorTerm.children, caseTerm)
caseTerm.parent = selectorTerm
currentTerm = caseTerm
}
//resetSelector = tk.Sym == SymDoubleColon
default:
err = tree.addToken(tk)
currentTerm, err = tree.addToken2(tk)
}
if currentTerm != nil && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
selectorTerm = nil
}
// if resetSelector {
// selectorTree = nil
// }
lastSym = tk.Sym
}
return

View File

@ -63,7 +63,7 @@ func TestParser(t *testing.T) {
/* 42 */ {`"s" + true`, nil, errors.New(`[1:6] left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`)},
/* 43 */ {`+false`, nil, errors.New(`[1:2] prefix/postfix operator "+" do not support operand 'false' [bool]`)},
/* 44 */ {`false // very simple expression`, false, nil},
/* 45 */ {`1 + // Missing right operator`, nil, errors.New(`[1:4] infix operator "+" requires two operands, got 1`)},
/* 45 */ {`1 + // Missing right operator`, nil, errors.New(`[1:4] infix operator "+" requires two not nil operands, got 1`)},
/* 46 */ {"", nil, errors.New(`empty expression`)},
/* 47 */ {"4!", int64(24), nil},
/* 48 */ {"(-4)!", nil, errors.New(`factorial of a negative integer (-4) is not allowed`)},
@ -78,18 +78,18 @@ func TestParser(t *testing.T) {
/* 57 */ {`"1.5" > "7"`, false, nil},
/* 58 */ {`"1.5" == "7"`, false, nil},
/* 59 */ {`"1.5" != "7"`, true, nil},
/* 60 */ {"1.5 < ", nil, errors.New(`[1:6] infix operator "<" requires two operands, got 1`)},
/* 61 */ {"1.5 > ", nil, errors.New(`[1:6] infix operator ">" requires two operands, got 1`)},
/* 62 */ {"1.5 <= ", nil, errors.New(`[1:6] infix operator "<=" requires two operands, got 1`)},
/* 63 */ {"1.5 >= ", nil, errors.New(`[1:6] infix operator ">=" requires two operands, got 1`)},
/* 64 */ {"1.5 != ", nil, errors.New(`[1:6] infix operator "!=" requires two operands, got 1`)},
/* 65 */ {"1.5 == ", nil, errors.New(`[1:6] infix operator "==" requires two operands, got 1`)},
/* 66 */ {`"1.5" < `, nil, errors.New(`[1:8] infix operator "<" requires two operands, got 1`)},
/* 67 */ {`"1.5" > `, nil, errors.New(`[1:8] infix operator ">" requires two operands, got 1`)},
/* 68 */ {`"1.5" == `, nil, errors.New(`[1:8] infix operator "==" requires two operands, got 1`)},
/* 69 */ {`"1.5" != `, nil, errors.New(`[1:8] infix operator "!=" requires two operands, got 1`)},
/* 60 */ {"1.5 < ", nil, errors.New(`[1:6] infix operator "<" requires two not nil operands, got 1`)},
/* 61 */ {"1.5 > ", nil, errors.New(`[1:6] infix operator ">" requires two not nil operands, got 1`)},
/* 62 */ {"1.5 <= ", nil, errors.New(`[1:6] infix operator "<=" requires two not nil operands, got 1`)},
/* 63 */ {"1.5 >= ", nil, errors.New(`[1:6] infix operator ">=" requires two not nil operands, got 1`)},
/* 64 */ {"1.5 != ", nil, errors.New(`[1:6] infix operator "!=" requires two not nil operands, got 1`)},
/* 65 */ {"1.5 == ", nil, errors.New(`[1:6] infix operator "==" requires two not nil operands, got 1`)},
/* 66 */ {`"1.5" < `, nil, errors.New(`[1:8] infix operator "<" requires two not nil operands, got 1`)},
/* 67 */ {`"1.5" > `, nil, errors.New(`[1:8] infix operator ">" requires two not nil operands, got 1`)},
/* 68 */ {`"1.5" == `, nil, errors.New(`[1:8] infix operator "==" requires two not nil operands, got 1`)},
/* 69 */ {`"1.5" != `, nil, errors.New(`[1:8] infix operator "!=" requires two not nil operands, got 1`)},
/* 70 */ {"+1.5", float64(1.5), nil},
/* 71 */ {"+", nil, errors.New(`[1:2] prefix operator "+" requires one operand`)},
/* 71 */ {"+", nil, errors.New(`[1:2] prefix operator "+" requires one not nil operand`)},
/* 72 */ {"4 / 0", nil, errors.New(`division by zero`)},
/* 73 */ {"4.0 / 0", nil, errors.New(`division by zero`)},
/* 74 */ {"4.0 / \n2", float64(2.0), nil},
@ -146,6 +146,13 @@ func TestParser(t *testing.T) {
/* 125 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil},
/* 126 */ {`include("./test-funcs.expr"); six()`, int64(6), nil},
/* 127 */ {`import("./sample-export-all.expr"); six()`, int64(6), nil},
/* 128 */ {`1 ? {"a"} : {"b"}`, "b", nil},
/* 129 */ {`10 ? {"a"} : {"b"} :: {"c"}`, "c", nil},
/* 130 */ {`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}`, "b", nil},
/* 131 */ {`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}`, nil, errors.New(`[1:34] case list in default clause`)},
/* 132 */ {`10 ? {"a"} :[10] {x="b" but x} :: {"c"}`, "b", nil},
/* 133 */ {`10 ? {"a"} :[10] {x="b"; x} :: {"c"}`, "b", nil},
/* 134 */ {`10 ? {"a"} : {"b"}`, nil, errors.New(`[1:3] no case catches the value (10) of the selection expression`)},
}
check_env_expr_path := 113
@ -153,7 +160,7 @@ func TestParser(t *testing.T) {
failed := 0
// inputs1 := []inputType{
// {`import("./sample-export-all.expr"); six()`, int64(6), nil},
// {`1 ? {"a"} : {"b"}`, "b", nil},
// }
for i, input := range inputs {
@ -174,7 +181,7 @@ func TestParser(t *testing.T) {
scanner := NewScanner(r, DefaultTranslations())
good := true
if expr, gotErr = parser.parse(scanner); gotErr == nil {
if expr, gotErr = parser.Parse(scanner); gotErr == nil {
gotResult, gotErr = expr.Eval(ctx, true)
}
@ -202,7 +209,7 @@ func TestParser(t *testing.T) {
t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed))
}
func TestListParser(t *testing.T) {
func NoTestListParser(t *testing.T) {
type inputType struct {
source string
wantResult any
@ -249,7 +256,7 @@ func TestListParser(t *testing.T) {
scanner := NewScanner(r, DefaultTranslations())
good := true
if expr, gotErr = parser.parse(scanner); gotErr == nil {
if expr, gotErr = parser.Parse(scanner); gotErr == nil {
gotResult, gotErr = expr.Eval(ctx, true)
}

View File

@ -144,7 +144,11 @@ func (self *scanner) fetchNextToken() (tk *Token) {
case ',':
tk = self.makeToken(SymComma, ch)
case ':':
if next, _ := self.peek(); next == ':' {
tk = self.moveOn(SymDoubleColon, ch, next)
} else {
tk = self.makeToken(SymColon, ch)
}
case ';':
tk = self.makeToken(SymSemiColon, ch)
case '.':

View File

@ -58,6 +58,7 @@ const (
SymDoubleQuestion // 47: '??'
SymQuestionEqual // 48: '?='
SymDoubleAt // 49: '@@'
SymDoubleColon // 50: '::'
SymChangeSign
SymUnchangeSign
SymIdentifier
@ -73,6 +74,8 @@ const (
SymFuncDef
SymList
SymExpression
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
// SymOpenComment // 0: '/*'
// SymClosedComment // 0: '*/'
// SymOneLineComment // 0: '//'

24
term.go
View File

@ -41,6 +41,7 @@ const (
priRelational
priSum
priProduct
priSelector
priSign
priFact
priCoalesce
@ -54,6 +55,7 @@ const (
posInfix
posPrefix
posPostfix
posMultifix
)
type evalFuncType func(ctx exprContext, self *term) (v any, err error)
@ -196,21 +198,33 @@ func (self *term) Errorf(template string, args ...any) (err error) {
func (self *term) checkOperands() (err error) {
switch self.position {
case posInfix:
if self.children == nil || len(self.children) != 2 || self.children[0] == nil || self.children[1] == nil {
err = self.tk.Errorf("infix operator %q requires two operands, got %d", self.source(), self.getChildrenCount())
if self.children == nil || len(self.children) != 2 || self.anyChildrenNil() {
err = self.tk.Errorf("infix operator %q requires two not nil operands, got %d", self.source(), self.getChildrenCount())
}
case posPrefix:
if self.children == nil || len(self.children) != 1 || self.children[0] == nil {
err = self.tk.Errorf("prefix operator %q requires one operand", self.tk.String())
err = self.tk.Errorf("prefix operator %q requires one not nil operand", self.tk.String())
}
case posPostfix:
if self.children == nil || len(self.children) != 1 || self.children[0] == nil {
err = self.tk.Errorf("postfix operator %q requires one operand", self.tk.String())
if self.children == nil || len(self.children) != 1 || self.anyChildrenNil() {
err = self.tk.Errorf("postfix operator %q requires one not nil operand", self.tk.String())
}
case posMultifix:
if self.children == nil || len(self.children) < 3 || self.anyChildrenNil() {
err = self.tk.Errorf("infix operator %q requires at least three not operands, got %d", self.source(), self.getChildrenCount())
}
}
return
}
func (self *term) anyChildrenNil() bool {
for _, child := range self.children {
if child == nil {
return true
}
}
return false
}
func (self *term) evalInfix(ctx exprContext) (leftValue, rightValue any, err error) {
if err = self.checkOperands(); err == nil {
if leftValue, err = self.children[0].compute(ctx); err == nil {