Compare commits
16 Commits
5da5a61a42
...
6211be8a8f
Author | SHA1 | Date | |
---|---|---|---|
6211be8a8f | |||
f50ddf48db | |||
76e01f12d2 | |||
406bced450 | |||
409dc86a92 | |||
4184221428 | |||
8cf8b36a26 | |||
de87050188 | |||
a1ec0cc611 | |||
8e5550bfa7 | |||
6ee21e10af | |||
5c44532790 | |||
cb66c1ab19 | |||
a28d24ba68 | |||
523349a204 | |||
b185f1df3a |
@ -44,7 +44,7 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
|
|||||||
var expr *ast
|
var expr *ast
|
||||||
scanner := NewScanner(file, DefaultTranslations())
|
scanner := NewScanner(file, DefaultTranslations())
|
||||||
parser := NewParser()
|
parser := NewParser()
|
||||||
if expr, err = parser.parseGeneral(scanner, true, true); err == nil {
|
if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymEos); err == nil {
|
||||||
result, err = expr.Eval(ctx)
|
result, err = expr.Eval(ctx)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -50,9 +50,9 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
|
|||||||
} else if s[0] == '+' {
|
} else if s[0] == '+' {
|
||||||
s = s[1:]
|
s = s[1:]
|
||||||
}
|
}
|
||||||
// if strings.HasSuffix(s, "()") {
|
// if strings.HasSuffix(s, "()") {
|
||||||
// s = s[0 : len(s)-2]
|
// s = s[0 : len(s)-2]
|
||||||
// }
|
// }
|
||||||
s = strings.TrimSuffix(s, "()")
|
s = strings.TrimSuffix(s, "()")
|
||||||
parts = strings.SplitN(s, ".", 2)
|
parts = strings.SplitN(s, ".", 2)
|
||||||
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
|
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
|
||||||
@ -124,7 +124,7 @@ func (f *FractionType) String() string {
|
|||||||
func (f *FractionType) ToString(opt FmtOpt) string {
|
func (f *FractionType) ToString(opt FmtOpt) string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
if opt&MultiLine == 0 {
|
if opt&MultiLine == 0 {
|
||||||
sb.WriteString(fmt.Sprintf("%d|%d", f.num, f.den))
|
sb.WriteString(fmt.Sprintf("%d:%d", f.num, f.den))
|
||||||
} else {
|
} else {
|
||||||
var s, num string
|
var s, num string
|
||||||
if f.num < 0 && opt&TTY == 0 {
|
if f.num < 0 && opt&TTY == 0 {
|
||||||
|
6
go.mod
6
go.mod
@ -1,3 +1,7 @@
|
|||||||
module git.portale-stac.it/go-pkg/expr
|
module git.portale-stac.it/go-pkg/expr
|
||||||
|
|
||||||
go 1.21.6
|
go 1.22.0
|
||||||
|
|
||||||
|
toolchain go1.23.3
|
||||||
|
|
||||||
|
require golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
|
||||||
|
4
go.sum
Normal file
4
go.sum
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe h1:bWYrKmmfv37uNgXTdwkLSKYiYPJ1yfWmjBnvtMyAYzk=
|
||||||
|
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe/go.mod h1:alTKUpAJ/zbp17qvZwcFNwzufrb5DljMDY4mgJlIHao=
|
||||||
|
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
|
||||||
|
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
@ -99,7 +99,106 @@ func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-------- assign term
|
||||||
|
|
||||||
|
func newOpAssignTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priAssign,
|
||||||
|
evalFunc: evalOpAssign,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCollectionItemValue(ctx ExprContext, collectionTerm, keyListTerm *term) (value any, err error) {
|
||||||
|
var collectionValue, keyListValue, keyValue any
|
||||||
|
var keyList *ListType
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if collectionValue, err = collectionTerm.compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyListValue, err = keyListTerm.compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 {
|
||||||
|
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if keyValue = (*keyList)[0]; keyValue == nil {
|
||||||
|
err = keyListTerm.Errorf("index/key is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch collection := collectionValue.(type) {
|
||||||
|
case *ListType:
|
||||||
|
if index, ok := keyValue.(int64); ok {
|
||||||
|
value = (*collection)[index]
|
||||||
|
} else {
|
||||||
|
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, TypeName(keyValue))
|
||||||
|
}
|
||||||
|
case *DictType:
|
||||||
|
value = (*collection)[keyValue]
|
||||||
|
default:
|
||||||
|
err = collectionTerm.Errorf("collection expected")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAssignValue(ctx ExprContext, leftTerm *term) (value any, err error) {
|
||||||
|
if leftTerm.symbol() == SymIndex {
|
||||||
|
value, err = getCollectionItemValue(ctx, leftTerm.children[0], leftTerm.children[1])
|
||||||
|
} else {
|
||||||
|
value, _ = ctx.GetVar(leftTerm.source())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalOpAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
var rightValue, leftValue any
|
||||||
|
if err = opTerm.checkOperands(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
leftTerm := opTerm.children[0]
|
||||||
|
leftSym := leftTerm.symbol()
|
||||||
|
if leftSym != SymVariable && leftSym != SymIndex {
|
||||||
|
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rightChild := opTerm.children[1]
|
||||||
|
|
||||||
|
if rightValue, err = rightChild.compute(ctx); err == nil {
|
||||||
|
if leftValue, err = getAssignValue(ctx, leftTerm); err == nil {
|
||||||
|
switch opTerm.symbol() {
|
||||||
|
case SymPlusEqual:
|
||||||
|
v, err = sumValues(opTerm, leftValue, rightValue)
|
||||||
|
case SymMinusEqual:
|
||||||
|
v, err = diffValues(opTerm, leftValue, rightValue)
|
||||||
|
case SymStarEqual:
|
||||||
|
v, err = mulValues(opTerm, leftValue, rightValue)
|
||||||
|
case SymSlashEqual:
|
||||||
|
v, err = divValues(opTerm, leftValue, rightValue)
|
||||||
|
case SymPercEqual:
|
||||||
|
v, err = remainderValues(opTerm, leftValue, rightValue)
|
||||||
|
default:
|
||||||
|
err = opTerm.Errorf("unsupported assign operator %q", opTerm.source())
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = assignValue(ctx, leftTerm, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymEqual, newAssignTerm)
|
registerTermConstructor(SymEqual, newAssignTerm)
|
||||||
|
registerTermConstructor(SymPlusEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymMinusEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymStarEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymSlashEqual, newOpAssignTerm)
|
||||||
}
|
}
|
||||||
|
104
operator-binary.go
Normal file
104
operator-binary.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-binary.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
//-------- NOT term
|
||||||
|
|
||||||
|
func newBinNotTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 1),
|
||||||
|
position: posPrefix,
|
||||||
|
priority: priNot,
|
||||||
|
evalFunc: evalBinaryNot,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalBinaryNot(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
var value any
|
||||||
|
|
||||||
|
if value, err = opTerm.evalPrefix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsInteger(value) {
|
||||||
|
i, _ := value.(int64)
|
||||||
|
v = ^i
|
||||||
|
} else {
|
||||||
|
err = opTerm.errIncompatibleType(value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------- Binary AND term
|
||||||
|
|
||||||
|
func newBinAndTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priAnd,
|
||||||
|
evalFunc: evalBinaryAnd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalBinaryAnd(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
var leftInt, rightInt int64
|
||||||
|
var lok, rok bool
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
leftInt, lok = leftValue.(int64)
|
||||||
|
rightInt, rok = rightValue.(int64)
|
||||||
|
|
||||||
|
if lok && rok {
|
||||||
|
v = leftInt & rightInt
|
||||||
|
} else {
|
||||||
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------- Binary OR term
|
||||||
|
|
||||||
|
func newBinOrTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priOr,
|
||||||
|
evalFunc: evalBinaryOr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalBinaryOr(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
var leftInt, rightInt int64
|
||||||
|
var lok, rok bool
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
leftInt, lok = leftValue.(int64)
|
||||||
|
rightInt, rok = rightValue.(int64)
|
||||||
|
|
||||||
|
if lok && rok {
|
||||||
|
v = leftInt | rightInt
|
||||||
|
} else {
|
||||||
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
registerTermConstructor(SymTilde, newBinNotTerm)
|
||||||
|
registerTermConstructor(SymAmpersand, newBinAndTerm)
|
||||||
|
registerTermConstructor(SymVertBar, newBinOrTerm)
|
||||||
|
}
|
@ -49,18 +49,23 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
den = -den
|
den = -den
|
||||||
num = -num
|
num = -num
|
||||||
}
|
}
|
||||||
g := gcd(num, den)
|
if num != 0 {
|
||||||
num = num / g
|
g := gcd(num, den)
|
||||||
den = den / g
|
num = num / g
|
||||||
if den == 1 {
|
den = den / g
|
||||||
v = num
|
if den == 1 {
|
||||||
|
v = num
|
||||||
|
} else {
|
||||||
|
v = &FractionType{num, den}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
v = &FractionType{num, den}
|
v = &FractionType{0, den}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymVertBar, newFractionTerm)
|
// registerTermConstructor(SymVertBar, newFractionTerm)
|
||||||
|
registerTermConstructor(SymColon, newFractionTerm)
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,9 @@ func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
} else if IsDict(leftValue) {
|
} else if IsDict(leftValue) {
|
||||||
d := leftValue.(*DictType)
|
d := leftValue.(*DictType)
|
||||||
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
||||||
|
} else {
|
||||||
|
rightChild := opTerm.children[1]
|
||||||
|
err = rightChild.Errorf("invalid index type: %v", (*indexList)[0])
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,10 @@ func evalInsert(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
if opTerm.children[1].symbol() == SymVariable {
|
if opTerm.children[1].symbol() == SymVariable {
|
||||||
ctx.UnsafeSetVar(opTerm.children[1].source(), v)
|
ctx.UnsafeSetVar(opTerm.children[1].source(), v)
|
||||||
}
|
}
|
||||||
|
} else if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||||
|
leftInt := leftValue.(int64)
|
||||||
|
rightInt := rightValue.(int64)
|
||||||
|
v = leftInt >> rightInt
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
@ -60,6 +64,10 @@ func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
if opTerm.children[0].symbol() == SymVariable {
|
if opTerm.children[0].symbol() == SymVariable {
|
||||||
ctx.UnsafeSetVar(opTerm.children[0].source(), v)
|
ctx.UnsafeSetVar(opTerm.children[0].source(), v)
|
||||||
}
|
}
|
||||||
|
} else if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||||
|
leftInt := leftValue.(int64)
|
||||||
|
rightInt := rightValue.(int64)
|
||||||
|
v = leftInt << rightInt
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
func newMultiplyTerm(tk *Token) (inst *term) {
|
func newMultiplyTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priProduct,
|
priority: priProduct,
|
||||||
@ -21,13 +21,7 @@ func newMultiplyTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
func mulValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsString(leftValue) && IsInteger(rightValue) {
|
if IsString(leftValue) && IsInteger(rightValue) {
|
||||||
s, _ := leftValue.(string)
|
s, _ := leftValue.(string)
|
||||||
n, _ := rightValue.(int64)
|
n, _ := rightValue.(int64)
|
||||||
@ -43,16 +37,26 @@ func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
|||||||
v = leftInt * rightInt
|
v = leftInt * rightInt
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = prodTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return mulValues(prodTerm, leftValue, rightValue)
|
||||||
|
}
|
||||||
|
|
||||||
//-------- divide term
|
//-------- divide term
|
||||||
|
|
||||||
func newDivideTerm(tk *Token) (inst *term) {
|
func newDivideTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priProduct,
|
priority: priProduct,
|
||||||
@ -60,13 +64,7 @@ func newDivideTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||||
d := numAsFloat(rightValue)
|
d := numAsFloat(rightValue)
|
||||||
@ -91,6 +89,16 @@ func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return divValues(opTerm, leftValue, rightValue)
|
||||||
|
}
|
||||||
|
|
||||||
//-------- divide as float term
|
//-------- divide as float term
|
||||||
|
|
||||||
func newDivideAsFloatTerm(tk *Token) (inst *term) {
|
func newDivideAsFloatTerm(tk *Token) (inst *term) {
|
||||||
@ -127,21 +135,14 @@ func evalDivideAsFloat(ctx ExprContext, floatDivTerm *term) (v any, err error) {
|
|||||||
|
|
||||||
func newRemainderTerm(tk *Token) (inst *term) {
|
func newRemainderTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priProduct,
|
priority: priProduct,
|
||||||
evalFunc: evalReminder,
|
evalFunc: evalRemainder,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func remainderValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
func evalReminder(ctx ExprContext, ramainderTerm *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = ramainderTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||||
rightInt, _ := rightValue.(int64)
|
rightInt, _ := rightValue.(int64)
|
||||||
if rightInt == 0 {
|
if rightInt == 0 {
|
||||||
@ -151,11 +152,21 @@ func evalReminder(ctx ExprContext, ramainderTerm *term) (v any, err error) {
|
|||||||
v = leftInt % rightInt
|
v = leftInt % rightInt
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = ramainderTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalRemainder(ctx ExprContext, remainderTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = remainderTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return remainderValues(remainderTerm, leftValue, rightValue)
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymStar, newMultiplyTerm)
|
registerTermConstructor(SymStar, newMultiplyTerm)
|
||||||
|
@ -34,12 +34,16 @@ func newRangeTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func changeColonToRange(t *term) {
|
||||||
|
if t.tk.IsSymbol(SymColon) {
|
||||||
|
t.tk.Sym = SymRange
|
||||||
|
t.evalFunc = evalRange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
// if err = self.checkOperands(); err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
if len(opTerm.children) == 0 {
|
if len(opTerm.children) == 0 {
|
||||||
leftValue = int64(0)
|
leftValue = int64(0)
|
||||||
rightValue = int64(-1)
|
rightValue = int64(-1)
|
||||||
@ -52,7 +56,8 @@ func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !(IsInteger(leftValue) && IsInteger(rightValue)) {
|
if !(IsInteger(leftValue) && IsInteger(rightValue)) {
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
// err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
err = errRangeInvalidSpecification(opTerm)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +68,15 @@ func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func errRangeInvalidSpecification(t *term) error {
|
||||||
|
return t.Errorf("invalid range specification")
|
||||||
|
}
|
||||||
|
|
||||||
|
func errRangeUnexpectedExpression(t *term) error {
|
||||||
|
return t.Errorf("unexpected range expression")
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymColon, newRangeTerm)
|
registerTermConstructor(SymRange, newRangeTerm)
|
||||||
}
|
}
|
||||||
|
@ -21,13 +21,7 @@ func newPlusTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
func sumValues(plusTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsString(leftValue) && isNumberString(rightValue)) || (IsString(rightValue) && isNumberString(leftValue)) {
|
if (IsString(leftValue) && isNumberString(rightValue)) || (IsString(rightValue) && isNumberString(leftValue)) {
|
||||||
v = fmt.Sprintf("%v%v", leftValue, rightValue)
|
v = fmt.Sprintf("%v%v", leftValue, rightValue)
|
||||||
} else if IsNumber(leftValue) && IsNumber(rightValue) {
|
} else if IsNumber(leftValue) && IsNumber(rightValue) {
|
||||||
@ -59,10 +53,22 @@ func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
|||||||
c := leftDict.clone()
|
c := leftDict.clone()
|
||||||
c.merge(rightDict)
|
c.merge(rightDict)
|
||||||
v = c
|
v = c
|
||||||
|
} else if isFraction(leftValue) && isFraction(rightValue) {
|
||||||
|
v, err = sumAnyFract(leftValue, rightValue)
|
||||||
} else {
|
} else {
|
||||||
err = plusTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = plusTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return sumValues(plusTerm, leftValue, rightValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------- minus term
|
//-------- minus term
|
||||||
@ -77,13 +83,7 @@ func newMinusTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
func diffValues(minusTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||||
v = numAsFloat(leftValue) - numAsFloat(rightValue)
|
v = numAsFloat(leftValue) - numAsFloat(rightValue)
|
||||||
@ -110,6 +110,16 @@ func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return diffValues(minusTerm, leftValue, rightValue)
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymPlus, newPlusTerm)
|
registerTermConstructor(SymPlus, newPlusTerm)
|
||||||
|
230
parser.go
230
parser.go
@ -6,10 +6,46 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- parser
|
//-------- parser
|
||||||
|
|
||||||
|
type parserContext uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
parserNoFlags = 0
|
||||||
|
allowMultiExpr parserContext = 1 << iota
|
||||||
|
allowVarRef
|
||||||
|
selectorContext
|
||||||
|
listContext // squareContext for list
|
||||||
|
indexContext // squareContext for index
|
||||||
|
allowIndex // allow index in squareContext
|
||||||
|
squareContext = listContext | indexContext // Square parenthesis for list or index
|
||||||
|
)
|
||||||
|
|
||||||
|
func hasFlag[T constraints.Unsigned](set T, singleFlag T) bool {
|
||||||
|
return (set & singleFlag) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFlags[T constraints.Unsigned](set T, flags T) T {
|
||||||
|
return set | flags
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFlagsCond[T constraints.Unsigned](set T, flags T, cond bool) (newSet T) {
|
||||||
|
if cond {
|
||||||
|
newSet = set | flags
|
||||||
|
} else {
|
||||||
|
newSet = set
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func remFlags[T constraints.Unsigned](set T, flags T) T {
|
||||||
|
return set & (^flags)
|
||||||
|
}
|
||||||
|
|
||||||
type parser struct {
|
type parser struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,13 +60,13 @@ func (parser *parser) Next(scanner *scanner) (tk *Token) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
|
func (parser *parser) parseFuncCall(scanner *scanner, ctx parserContext, tk *Token) (tree *term, err error) {
|
||||||
args := make([]*term, 0, 10)
|
args := make([]*term, 0, 10)
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil {
|
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
@ -77,7 +113,7 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
if tk.Sym == SymEqual {
|
if tk.Sym == SymEqual {
|
||||||
var paramExpr *ast
|
var paramExpr *ast
|
||||||
defaultParamsStarted = true
|
defaultParamsStarted = true
|
||||||
if paramExpr, err = parser.parseItem(scanner, false, SymComma, SymClosedRound); err != nil {
|
if paramExpr, err = parser.parseItem(scanner, parserNoFlags, SymComma, SymClosedRound); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
param.forceChild(paramExpr.root)
|
param.forceChild(paramExpr.root)
|
||||||
@ -100,7 +136,7 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
tk = parser.Next(scanner)
|
tk = parser.Next(scanner)
|
||||||
if tk.IsSymbol(SymOpenBrace) {
|
if tk.IsSymbol(SymOpenBrace) {
|
||||||
body, err = parser.parseGeneral(scanner, true, true, SymClosedBrace)
|
body, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymClosedBrace)
|
||||||
} else {
|
} else {
|
||||||
err = tk.ErrorExpectedGot("{")
|
err = tk.ErrorExpectedGot("{")
|
||||||
}
|
}
|
||||||
@ -126,27 +162,33 @@ func paramAlreadyDefined(args []*term, param *term) (position int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) {
|
func (parser *parser) parseList(scanner *scanner, ctx parserContext) (listTerm *term, err error) {
|
||||||
r, c := scanner.lastPos()
|
r, c := scanner.lastPos()
|
||||||
args := make([]*term, 0)
|
args := make([]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
|
itemCtx := remFlags(ctx, allowIndex)
|
||||||
for lastSym != SymClosedSquare && lastSym != SymEos {
|
for lastSym != SymClosedSquare && lastSym != SymEos {
|
||||||
var subTree *ast
|
|
||||||
zeroRequired := scanner.current.Sym == SymColon
|
zeroRequired := scanner.current.Sym == SymColon
|
||||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
|
var itemTree *ast
|
||||||
root := subTree.root
|
if itemTree, err = parser.parseItem(scanner, itemCtx, SymComma, SymClosedSquare); err == nil {
|
||||||
|
root := itemTree.root
|
||||||
if root != nil {
|
if root != nil {
|
||||||
if !parsingIndeces && root.symbol() == SymColon {
|
if hasFlag(ctx, allowIndex) && root.symbol() == SymColon {
|
||||||
err = root.Errorf("unexpected range expression")
|
changeColonToRange(root)
|
||||||
|
}
|
||||||
|
if !hasFlag(ctx, allowIndex) && root.symbol() == SymRange {
|
||||||
|
// err = root.Errorf("unexpected range expression")
|
||||||
|
err = errRangeUnexpectedExpression(root)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
args = append(args, root)
|
args = append(args, root)
|
||||||
if parsingIndeces && root.symbol() == SymColon && zeroRequired { //len(root.children) == 0 {
|
if hasFlag(ctx, allowIndex) && root.symbol() == SymRange && zeroRequired { //len(root.children) == 0 {
|
||||||
if len(root.children) == 1 {
|
if len(root.children) == 1 {
|
||||||
root.children = append(root.children, root.children[0])
|
root.children = append(root.children, root.children[0])
|
||||||
} else if len(root.children) > 1 {
|
} else if len(root.children) > 1 {
|
||||||
err = root.Errorf("invalid range specification")
|
// err = root.Errorf("invalid range specification")
|
||||||
|
err = errRangeInvalidSpecification(root)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0))
|
zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0))
|
||||||
@ -163,26 +205,28 @@ func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarR
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
lastSym = scanner.Previous().Sym
|
lastSym = scanner.Previous().Sym
|
||||||
itemExpected = lastSym == SymComma
|
if itemExpected = lastSym == SymComma; itemExpected {
|
||||||
|
remFlags(ctx, allowIndex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if lastSym != SymClosedSquare {
|
if lastSym != SymClosedSquare {
|
||||||
err = scanner.Previous().ErrorExpectedGot("]")
|
err = scanner.Previous().ErrorExpectedGot("]")
|
||||||
} else {
|
} else {
|
||||||
subtree = newListTerm(r, c, args)
|
listTerm = newListTerm(r, c, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
func (parser *parser) parseIterDef(scanner *scanner, ctx parserContext) (subtree *term, err error) {
|
||||||
tk := scanner.Previous()
|
tk := scanner.Previous()
|
||||||
args := make([]*term, 0)
|
args := make([]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
|
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err == nil {
|
||||||
if subTree.root != nil {
|
if subTree.root != nil {
|
||||||
args = append(args, subTree.root)
|
args = append(args, subTree.root)
|
||||||
} else if itemExpected {
|
} else if itemExpected {
|
||||||
@ -228,7 +272,7 @@ func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subtree *term, err error) {
|
||||||
args := make(map[any]*term, 0)
|
args := make(map[any]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
@ -245,7 +289,7 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil {
|
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedBrace); err == nil {
|
||||||
if subTree.root != nil {
|
if subTree.root != nil {
|
||||||
args[key] = subTree.root
|
args[key] = subTree.root
|
||||||
} else /*if key != nil*/ {
|
} else /*if key != nil*/ {
|
||||||
@ -264,15 +308,15 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
|
|||||||
err = scanner.Previous().ErrorExpectedGot("}")
|
err = scanner.Previous().ErrorExpectedGot("}")
|
||||||
} else {
|
} else {
|
||||||
subtree = newDictTerm(args)
|
subtree = newDictTerm(args)
|
||||||
// subtree = newMapTerm(args)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
|
func (parser *parser) parseSelectorCase(scanner *scanner, ctx parserContext, defaultCase bool) (caseTerm *term, err error) {
|
||||||
var filterList *term
|
var filterList *term
|
||||||
var caseExpr *ast
|
var caseExpr *ast
|
||||||
|
ctx = remFlags(ctx, allowIndex)
|
||||||
tk := parser.Next(scanner)
|
tk := parser.Next(scanner)
|
||||||
startRow := tk.row
|
startRow := tk.row
|
||||||
startCol := tk.col
|
startCol := tk.col
|
||||||
@ -281,7 +325,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
|
|||||||
err = tk.Errorf("case list in default clause")
|
err = tk.Errorf("case list in default clause")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if filterList, err = parser.parseList(scanner, false, allowVarRef); err != nil {
|
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex)); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tk = parser.Next(scanner)
|
tk = parser.Next(scanner)
|
||||||
@ -292,7 +336,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
|
|||||||
}
|
}
|
||||||
|
|
||||||
if tk.Sym == SymOpenBrace {
|
if tk.Sym == SymOpenBrace {
|
||||||
if caseExpr, err = parser.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil {
|
if caseExpr, err = parser.parseGeneral(scanner, ctx|allowMultiExpr, SymClosedBrace); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -318,25 +362,28 @@ func addSelectorCase(selectorTerm, caseTerm *term) {
|
|||||||
caseTerm.parent = selectorTerm
|
caseTerm.parent = selectorTerm
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) {
|
func (parser *parser) parseSelector(scanner *scanner, tree *ast, ctx parserContext) (selectorTerm *term, err error) {
|
||||||
var caseTerm *term
|
var caseTerm *term
|
||||||
|
|
||||||
|
ctx = remFlags(ctx, allowIndex)
|
||||||
tk := scanner.makeToken(SymSelector, '?')
|
tk := scanner.makeToken(SymSelector, '?')
|
||||||
if selectorTerm, err = tree.addToken(tk); err != nil {
|
if selectorTerm, err = tree.addToken(tk); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, false); err == nil {
|
if caseTerm, err = parser.parseSelectorCase(scanner, ctx|allowVarRef, false); err == nil {
|
||||||
addSelectorCase(selectorTerm, caseTerm)
|
addSelectorCase(selectorTerm, caseTerm)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
func (parser *parser) parseItem(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
return parser.parseGeneral(scanner, false, allowVarRef, termSymbols...)
|
return parser.parseGeneral(scanner, ctx|allowVarRef, termSymbols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
return parser.parseGeneral(scanner, true, false, termSymbols...)
|
termSymbols = append(termSymbols, SymEos)
|
||||||
|
return parser.parseGeneral(scanner, allowMultiExpr, termSymbols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func couldBeACollection(t *term) bool {
|
func couldBeACollection(t *term) bool {
|
||||||
@ -347,15 +394,22 @@ func couldBeACollection(t *term) bool {
|
|||||||
return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable
|
return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable
|
||||||
}
|
}
|
||||||
|
|
||||||
// func areSymbolsOutOfCtx(tk *Token, ctxTerm *term, syms ...Symbol) bool {
|
func listSubTree(tree *ast, listTerm *term, allowIndeces bool) (root *term, err error) {
|
||||||
// var areOut = false
|
var tk *Token
|
||||||
// if ctxTerm != nil {
|
if allowIndeces {
|
||||||
// areOut = tk.IsOneOf(syms)
|
tk = NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
||||||
// }
|
root = newTerm(tk)
|
||||||
// return areOut
|
if err = tree.addTerm(root); err == nil {
|
||||||
// }
|
err = tree.addTerm(listTerm)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root = listTerm
|
||||||
|
err = tree.addTerm(listTerm)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
var selectorTerm *term = nil
|
var selectorTerm *term = nil
|
||||||
var currentTerm *term = nil
|
var currentTerm *term = nil
|
||||||
var tk *Token
|
var tk *Token
|
||||||
@ -369,7 +423,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
if tk.Sym == SymSemiColon {
|
if tk.Sym == SymSemiColon {
|
||||||
if allowForest {
|
if hasFlag(ctx, allowMultiExpr) {
|
||||||
tree.ToForest()
|
tree.ToForest()
|
||||||
firstToken = true
|
firstToken = true
|
||||||
currentTerm = nil
|
currentTerm = nil
|
||||||
@ -394,47 +448,36 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
|||||||
switch tk.Sym {
|
switch tk.Sym {
|
||||||
case SymOpenRound:
|
case SymOpenRound:
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil {
|
if subTree, err = parser.parseGeneral(scanner, ctx, SymClosedRound); err == nil {
|
||||||
subTree.root.priority = priValue
|
subTree.root.priority = priValue
|
||||||
err = tree.addTerm(newExprTerm(subTree.root))
|
err = tree.addTerm(newExprTerm(subTree.root))
|
||||||
currentTerm = subTree.root
|
currentTerm = subTree.root
|
||||||
}
|
}
|
||||||
case SymFuncCall:
|
case SymFuncCall:
|
||||||
var funcCallTerm *term
|
var funcCallTerm *term
|
||||||
if funcCallTerm, err = parser.parseFuncCall(scanner, allowVarRef, tk); err == nil {
|
if funcCallTerm, err = parser.parseFuncCall(scanner, ctx, tk); err == nil {
|
||||||
err = tree.addTerm(funcCallTerm)
|
err = tree.addTerm(funcCallTerm)
|
||||||
currentTerm = funcCallTerm
|
currentTerm = funcCallTerm
|
||||||
}
|
}
|
||||||
case SymOpenSquare:
|
case SymOpenSquare:
|
||||||
var listTerm *term
|
var listTerm *term
|
||||||
parsingIndeces := couldBeACollection(currentTerm)
|
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
|
||||||
if listTerm, err = parser.parseList(scanner, parsingIndeces, allowVarRef); err == nil {
|
if listTerm, err = parser.parseList(scanner, newCtx); err == nil {
|
||||||
if parsingIndeces {
|
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
||||||
indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
|
||||||
indexTerm := newTerm(indexTk)
|
|
||||||
if err = tree.addTerm(indexTerm); err == nil {
|
|
||||||
err = tree.addTerm(listTerm)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = tree.addTerm(listTerm)
|
|
||||||
}
|
|
||||||
currentTerm = listTerm
|
|
||||||
}
|
}
|
||||||
case SymOpenBrace:
|
case SymOpenBrace:
|
||||||
if currentTerm != nil && currentTerm.symbol() == SymColon {
|
if currentTerm != nil && currentTerm.symbol() == SymColon {
|
||||||
err = currentTerm.Errorf(`selector-case outside of a selector context`)
|
err = currentTerm.Errorf(`selector-case outside of a selector context`)
|
||||||
} else {
|
} else {
|
||||||
var mapTerm *term
|
var mapTerm *term
|
||||||
if mapTerm, err = parser.parseDictionary(scanner, allowVarRef); err == nil {
|
if mapTerm, err = parser.parseDictionary(scanner, ctx); err == nil {
|
||||||
err = tree.addTerm(mapTerm)
|
err = tree.addTerm(mapTerm)
|
||||||
currentTerm = mapTerm
|
currentTerm = mapTerm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case SymEqual:
|
case SymEqual, SymPlusEqual, SymMinusEqual, SymStarEqual, SymSlashEqual, SymPercEqual:
|
||||||
// if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
firstToken = true
|
firstToken = true
|
||||||
// }
|
|
||||||
case SymFuncDef:
|
case SymFuncDef:
|
||||||
var funcDefTerm *term
|
var funcDefTerm *term
|
||||||
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
|
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
|
||||||
@ -443,24 +486,25 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
|||||||
}
|
}
|
||||||
case SymDollarRound:
|
case SymDollarRound:
|
||||||
var iterDefTerm *term
|
var iterDefTerm *term
|
||||||
if iterDefTerm, err = parser.parseIterDef(scanner, allowVarRef); err == nil {
|
if iterDefTerm, err = parser.parseIterDef(scanner, ctx); err == nil {
|
||||||
err = tree.addTerm(iterDefTerm)
|
err = tree.addTerm(iterDefTerm)
|
||||||
currentTerm = iterDefTerm
|
currentTerm = iterDefTerm
|
||||||
}
|
}
|
||||||
case SymIdentifier:
|
case SymIdentifier:
|
||||||
if tk.source[0] == '@' && !allowVarRef {
|
if tk.source[0] == '@' && !hasFlag(ctx, allowVarRef) {
|
||||||
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
|
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
|
||||||
} else {
|
} else {
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
}
|
}
|
||||||
case SymQuestion:
|
case SymQuestion:
|
||||||
if selectorTerm, err = parser.parseSelector(scanner, tree, allowVarRef); err == nil {
|
if selectorTerm, err = parser.parseSelector(scanner, tree, ctx); err == nil {
|
||||||
currentTerm = selectorTerm
|
currentTerm = selectorTerm
|
||||||
|
addFlags(ctx, selectorContext)
|
||||||
}
|
}
|
||||||
case SymColon, SymDoubleColon:
|
case SymColon, SymDoubleColon:
|
||||||
var caseTerm *term
|
var caseTerm *term
|
||||||
if selectorTerm != nil {
|
if selectorTerm != nil {
|
||||||
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
|
if caseTerm, err = parser.parseSelectorCase(scanner, ctx, tk.Sym == SymDoubleColon); err == nil {
|
||||||
addSelectorCase(selectorTerm, caseTerm)
|
addSelectorCase(selectorTerm, caseTerm)
|
||||||
currentTerm = caseTerm
|
currentTerm = caseTerm
|
||||||
if tk.Sym == SymDoubleColon {
|
if tk.Sym == SymDoubleColon {
|
||||||
@ -468,80 +512,38 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// if hasFlag(ctx, allowIndex) {
|
||||||
|
// tk.Sym = SymRange
|
||||||
|
// }
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
}
|
}
|
||||||
if tk.IsSymbol(SymColon) {
|
if tk.IsOneOfA(SymColon, SymRange) {
|
||||||
// Colon outside a selector term acts like a separator
|
// Colon outside a selector term acts like a separator
|
||||||
firstToken = true
|
firstToken = true
|
||||||
}
|
}
|
||||||
case SymPlusEqual, SymMinusEqual, SymStarEqual:
|
|
||||||
currentTerm, err = parser.expandOpAssign(scanner, tree, tk, allowVarRef)
|
|
||||||
default:
|
default:
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
|
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
|
||||||
selectorTerm = nil
|
selectorTerm = nil
|
||||||
|
remFlags(ctx, selectorContext)
|
||||||
}
|
}
|
||||||
// lastSym = tk.Sym
|
// lastSym = tk.Sym
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = tk.Error()
|
if !tk.IsOneOf(termSymbols) {
|
||||||
}
|
var symDesc string
|
||||||
return
|
if tk.IsSymbol(SymError) {
|
||||||
}
|
symDesc = tk.ErrorText()
|
||||||
|
} else {
|
||||||
// func checkPrevSymbol(lastSym, wantedSym Symbol, tk *Token) (err error) {
|
symDesc = SymToString(tk.Sym)
|
||||||
// if lastSym != wantedSym {
|
|
||||||
// err = fmt.Errorf(`assign operator (%q) must be preceded by a variable`, tk.source)
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (parser *parser) expandOpAssign(scanner *scanner, tree *ast, tk *Token, allowVarRef bool) (t *term, err error) {
|
|
||||||
var opSym Symbol
|
|
||||||
var opString string
|
|
||||||
|
|
||||||
if tree.root != nil {
|
|
||||||
switch tk.Sym {
|
|
||||||
case SymPlusEqual:
|
|
||||||
opString = "+"
|
|
||||||
opSym = SymPlus
|
|
||||||
case SymMinusEqual:
|
|
||||||
opString = "-"
|
|
||||||
opSym = SymMinus
|
|
||||||
case SymStarEqual:
|
|
||||||
opString = "*"
|
|
||||||
opSym = SymStar
|
|
||||||
default:
|
|
||||||
err = tk.Errorf("unsopported operator %q", tk.source)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
leftExpr := tree.root.Clone()
|
|
||||||
leftExpr.setParent(nil)
|
|
||||||
if t, err = tree.addToken(NewToken(tk.row, tk.col, SymEqual, "=")); err == nil {
|
|
||||||
t = leftExpr
|
|
||||||
if err = tree.addTerm(leftExpr); err == nil {
|
|
||||||
if t, err = tree.addToken(NewToken(tk.row, tk.col, opSym, opString)); err == nil {
|
|
||||||
|
|
||||||
var subTree *ast
|
|
||||||
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare); err == nil {
|
|
||||||
if scanner.Previous().IsOneOfA(SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare) {
|
|
||||||
if err = scanner.UnreadToken(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subTree.root.priority = priValue
|
|
||||||
err = tree.addTerm(newExprTerm(subTree.root))
|
|
||||||
t = subTree.root
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
err = tk.ErrorExpectedGotStringWithPrefix("expected one of", SymListToString(termSymbols, true), symDesc)
|
||||||
|
} else {
|
||||||
|
err = tk.Error()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
err = tk.Errorf("left operand of %q must be a variable or a variable expression", tk)
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
10
scanner.go
10
scanner.go
@ -40,9 +40,9 @@ func DefaultTranslations() map[Symbol]Symbol {
|
|||||||
SymKwAnd: SymAnd,
|
SymKwAnd: SymAnd,
|
||||||
SymDoubleVertBar: SymOr,
|
SymDoubleVertBar: SymOr,
|
||||||
SymKwOr: SymOr,
|
SymKwOr: SymOr,
|
||||||
SymTilde: SymNot,
|
// SymTilde: SymNot,
|
||||||
SymKwNot: SymNot,
|
SymKwNot: SymNot,
|
||||||
SymLessGreater: SymNotEqual,
|
SymLessGreater: SymNotEqual,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +149,8 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
|||||||
if next, _ := scanner.peek(); next == '*' {
|
if next, _ := scanner.peek(); next == '*' {
|
||||||
scanner.readChar()
|
scanner.readChar()
|
||||||
tk = scanner.fetchBlockComment()
|
tk = scanner.fetchBlockComment()
|
||||||
|
} else if next, _ = scanner.peek(); next == '=' {
|
||||||
|
tk = scanner.moveOn(SymSlashEqual, ch, next)
|
||||||
} else if next == '/' {
|
} else if next == '/' {
|
||||||
scanner.readChar()
|
scanner.readChar()
|
||||||
tk = scanner.fetchOnLineComment()
|
tk = scanner.fetchOnLineComment()
|
||||||
@ -590,7 +592,7 @@ func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
|
|||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
tk = scanner.makeErrorToken(errors.New("missing string termination \""))
|
tk = scanner.makeErrorToken(errors.New(string(termCh)))
|
||||||
} else {
|
} else {
|
||||||
tk = scanner.makeErrorToken(err)
|
tk = scanner.makeErrorToken(err)
|
||||||
}
|
}
|
||||||
|
181
symbol-map.go
Normal file
181
symbol-map.go
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// Symbol.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var symbolMap map[Symbol]symbolSpec
|
||||||
|
|
||||||
|
type symbolClass int16
|
||||||
|
|
||||||
|
const (
|
||||||
|
symClassOperator symbolClass = iota
|
||||||
|
symClassPostOp
|
||||||
|
symClassIdentifier
|
||||||
|
symClassDelimiter
|
||||||
|
symClassParenthesis
|
||||||
|
symClassDeclaration
|
||||||
|
symClassValue
|
||||||
|
symClassOther
|
||||||
|
)
|
||||||
|
|
||||||
|
type symbolSpec struct {
|
||||||
|
repr string
|
||||||
|
kind symbolClass
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
symbolMap = map[Symbol]symbolSpec{
|
||||||
|
SymUnknown: {"<unknown>", symClassOther}, // -1: Unknown symbol
|
||||||
|
SymNone: {"<null>", symClassOther}, // 0: Null value for variable of type symbol
|
||||||
|
SymError: {"<error>", symClassOther}, // 1: Error reading from stream
|
||||||
|
SymEos: {"<eos>", symClassOther}, // 2: End of stream
|
||||||
|
SymMinus: {"-", symClassOperator}, // 3: '-'
|
||||||
|
SymMinusEqual: {"-=", symClassOperator}, // 4: '-='
|
||||||
|
SymDoubleMinus: {"--", symClassOperator}, // 5: '--'
|
||||||
|
SymPlus: {"+", symClassOperator}, // 6: '+'
|
||||||
|
SymPlusEqual: {"+=", symClassOperator}, // 7: '+='
|
||||||
|
SymDoublePlus: {"++", symClassOperator}, // 8: '++'
|
||||||
|
SymStar: {"*", symClassOperator}, // 9: '*'
|
||||||
|
SymDoubleStar: {"**", symClassOperator}, // 10: '**'
|
||||||
|
SymSlash: {"/", symClassOperator}, // 11: '/'
|
||||||
|
SymBackSlash: {"\\", symClassOperator}, // 12: '\'
|
||||||
|
SymVertBar: {"|", symClassOperator}, // 13: '|'
|
||||||
|
SymDoubleVertBar: {"||", symClassOperator}, // 14: '||'
|
||||||
|
SymComma: {",", symClassOperator}, // 15: ','
|
||||||
|
SymColon: {":", symClassOperator}, // 16: ':'
|
||||||
|
SymSemiColon: {";", symClassOperator}, // 17: ';'
|
||||||
|
SymDot: {".", symClassOperator}, // 18: '.'
|
||||||
|
SymDotSlash: {"./", symClassOperator}, // 19: './'
|
||||||
|
SymQuote: {"'", symClassDelimiter}, // 20: '\''
|
||||||
|
SymDoubleQuote: {"\"", symClassDelimiter}, // 21: '"'
|
||||||
|
SymBackTick: {"`", symClassOperator}, // 22: '`'
|
||||||
|
SymExclamation: {"!", symClassPostOp}, // 23: '!'
|
||||||
|
SymQuestion: {"?", symClassOperator}, // 24: '?'
|
||||||
|
SymAmpersand: {"&", symClassOperator}, // 25: '&'
|
||||||
|
SymDoubleAmpersand: {"&&", symClassOperator}, // 26: '&&'
|
||||||
|
SymPercent: {"%", symClassOperator}, // 27: '%'
|
||||||
|
SymAt: {"@", symClassOperator}, // 28: '@'
|
||||||
|
SymUndescore: {"_", symClassOperator}, // 29: '_'
|
||||||
|
SymEqual: {"=", symClassOperator}, // 30: '='
|
||||||
|
SymDoubleEqual: {"==", symClassOperator}, // 31: '=='
|
||||||
|
SymLess: {"<", symClassOperator}, // 32: '<'
|
||||||
|
SymLessOrEqual: {"<=", symClassOperator}, // 33: '<='
|
||||||
|
SymGreater: {">", symClassOperator}, // 34: '>'
|
||||||
|
SymGreaterOrEqual: {">=", symClassOperator}, // 35: '>='
|
||||||
|
SymLessGreater: {"<>", symClassOperator}, // 36: '<>'
|
||||||
|
SymNotEqual: {"!=", symClassOperator}, // 37: '!='
|
||||||
|
SymDollar: {"$", symClassOperator}, // 38: '$'
|
||||||
|
SymHash: {"#", symClassOperator}, // 39: '#'
|
||||||
|
SymOpenRound: {"(", symClassParenthesis}, // 40: '('
|
||||||
|
SymClosedRound: {")", symClassParenthesis}, // 41: ')'
|
||||||
|
SymOpenSquare: {"[", symClassParenthesis}, // 42: '['
|
||||||
|
SymClosedSquare: {"]", symClassParenthesis}, // 43: ']'
|
||||||
|
SymOpenBrace: {"{", symClassParenthesis}, // 44: '{'
|
||||||
|
SymClosedBrace: {"}", symClassParenthesis}, // 45: '}'
|
||||||
|
SymTilde: {"~", symClassOperator}, // 46: '~'
|
||||||
|
SymDoubleQuestion: {"??", symClassOperator}, // 47: '??'
|
||||||
|
SymQuestionEqual: {"?=", symClassOperator}, // 48: '?='
|
||||||
|
SymQuestionExclam: {"?!", symClassOperator}, // 49: '?!'
|
||||||
|
SymDoubleAt: {"@@", symClassOperator}, // 50: '@@'
|
||||||
|
SymDoubleColon: {"::", symClassOperator}, // 51: '::'
|
||||||
|
SymInsert: {">>", symClassOperator}, // 52: '>>'
|
||||||
|
SymAppend: {"<<", symClassOperator}, // 53: '<<'
|
||||||
|
SymCaret: {"^", symClassOperator}, // 54: '^'
|
||||||
|
SymDollarRound: {"$(", symClassOperator}, // 55: '$('
|
||||||
|
SymOpenClosedRound: {"()", symClassPostOp}, // 56: '()'
|
||||||
|
SymDoubleDollar: {"$$", symClassOperator}, // 57: '$$'
|
||||||
|
SymDoubleDot: {"..", symClassOperator}, // 58: '..'
|
||||||
|
SymTripleDot: {"...", symClassOperator}, // 59: '...'
|
||||||
|
SymStarEqual: {"*=", symClassOperator}, // 60: '*='
|
||||||
|
SymSlashEqual: {"/=", symClassOperator}, // 61: '/='
|
||||||
|
SymPercEqual: {"%=", symClassOperator}, // 62: '%='
|
||||||
|
// SymChangeSign
|
||||||
|
// SymUnchangeSign
|
||||||
|
// SymIdentifier
|
||||||
|
// SymBool
|
||||||
|
// SymInteger
|
||||||
|
// SymVariable
|
||||||
|
// SymFloat
|
||||||
|
// SymFraction
|
||||||
|
// SymString
|
||||||
|
// SymIterator
|
||||||
|
// SymOr: "or",
|
||||||
|
// SymAnd: "and",
|
||||||
|
// SymNot: "not",
|
||||||
|
// SymComment
|
||||||
|
// SymFuncCall
|
||||||
|
// SymFuncDef
|
||||||
|
// SymList
|
||||||
|
// SymDict
|
||||||
|
// SymIndex
|
||||||
|
// SymExpression
|
||||||
|
// SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||||
|
// SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||||
|
// // SymOpenComment // 0: '/*'
|
||||||
|
// // SymClosedComment // 0: '*/'
|
||||||
|
// // SymOneLineComment // 0: '//'
|
||||||
|
// keywordBase
|
||||||
|
SymKwAnd: {"and", symClassOperator},
|
||||||
|
SymKwNot: {"not", symClassOperator},
|
||||||
|
SymKwOr: {"or", symClassOperator},
|
||||||
|
SymKwBut: {"but", symClassOperator},
|
||||||
|
SymKwFunc: {"func(", symClassDeclaration},
|
||||||
|
SymKwBuiltin: {"builtin", symClassOperator},
|
||||||
|
SymKwPlugin: {"plugin", symClassOperator},
|
||||||
|
SymKwIn: {"in", symClassOperator},
|
||||||
|
SymKwInclude: {"include", symClassOperator},
|
||||||
|
SymKwNil: {"nil", symClassValue},
|
||||||
|
SymKwUnset: {"unset", symClassOperator},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SymToString(sym Symbol) string {
|
||||||
|
if s, ok := symbolMap[sym]; ok {
|
||||||
|
return s.repr
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func SymListToString(symList []Symbol, quote bool) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
if len(symList) == 0 {
|
||||||
|
sb.WriteString("<nothing>")
|
||||||
|
} else {
|
||||||
|
for _, sym := range symList {
|
||||||
|
if sb.Len() > 0 {
|
||||||
|
sb.WriteByte(',')
|
||||||
|
sb.WriteByte(' ')
|
||||||
|
}
|
||||||
|
if quote {
|
||||||
|
sb.WriteByte('`')
|
||||||
|
}
|
||||||
|
sb.WriteString(SymToString(sym))
|
||||||
|
if quote {
|
||||||
|
sb.WriteByte('`')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func StringEndsWithOperator(s string) bool {
|
||||||
|
return endingOperator(s) != SymNone
|
||||||
|
}
|
||||||
|
|
||||||
|
func endingOperator(s string) (sym Symbol) {
|
||||||
|
sym = SymNone
|
||||||
|
lower := strings.ToLower(s)
|
||||||
|
for symbol, spec := range symbolMap {
|
||||||
|
if spec.kind == symClassOperator && strings.HasSuffix(lower, spec.repr) {
|
||||||
|
sym = symbol
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -69,6 +69,8 @@ const (
|
|||||||
SymDoubleDot // 58: '..'
|
SymDoubleDot // 58: '..'
|
||||||
SymTripleDot // 59: '...'
|
SymTripleDot // 59: '...'
|
||||||
SymStarEqual // 60: '*='
|
SymStarEqual // 60: '*='
|
||||||
|
SymSlashEqual // 61: '/='
|
||||||
|
SymPercEqual // 62: '%='
|
||||||
SymChangeSign
|
SymChangeSign
|
||||||
SymUnchangeSign
|
SymUnchangeSign
|
||||||
SymIdentifier
|
SymIdentifier
|
||||||
@ -88,6 +90,7 @@ const (
|
|||||||
SymList
|
SymList
|
||||||
SymDict
|
SymDict
|
||||||
SymIndex
|
SymIndex
|
||||||
|
SymRange // [index : index]
|
||||||
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> "}"
|
||||||
|
@ -30,9 +30,9 @@ func TestFuncBase(t *testing.T) {
|
|||||||
/* 16 */ {`isString("3" + 1)`, true, nil},
|
/* 16 */ {`isString("3" + 1)`, true, nil},
|
||||||
/* 17 */ {`isList(["3", 1])`, true, nil},
|
/* 17 */ {`isList(["3", 1])`, true, nil},
|
||||||
/* 18 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
/* 18 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
||||||
/* 19 */ {`isFract(1|3)`, true, nil},
|
/* 19 */ {`isFract(1:3)`, true, nil},
|
||||||
/* 20 */ {`isFract(3|1)`, false, nil},
|
/* 20 */ {`isFract(3:1)`, false, nil},
|
||||||
/* 21 */ {`isRational(3|1)`, true, nil},
|
/* 21 */ {`isRational(3:1)`, true, nil},
|
||||||
/* 22 */ {`fract("2.2(3)")`, newFraction(67, 30), nil},
|
/* 22 */ {`fract("2.2(3)")`, newFraction(67, 30), nil},
|
||||||
/* 23 */ {`fract("1.21(3)")`, newFraction(91, 75), nil},
|
/* 23 */ {`fract("1.21(3)")`, newFraction(91, 75), nil},
|
||||||
/* 24 */ {`fract(1.21(3))`, newFraction(91, 75), nil},
|
/* 24 */ {`fract(1.21(3))`, newFraction(91, 75), nil},
|
||||||
@ -41,26 +41,26 @@ func TestFuncBase(t *testing.T) {
|
|||||||
/* 27 */ {`dec(2.0)`, float64(2), nil},
|
/* 27 */ {`dec(2.0)`, float64(2), nil},
|
||||||
/* 28 */ {`dec("2.0")`, float64(2), nil},
|
/* 28 */ {`dec("2.0")`, float64(2), nil},
|
||||||
/* 29 */ {`dec(true)`, float64(1), nil},
|
/* 29 */ {`dec(true)`, float64(1), nil},
|
||||||
/* 30 */ {`dec(true")`, nil, `[1:11] missing string termination "`},
|
/* 30 */ {`dec(true")`, nil, "[1:11] expected one of `,`, `)`, got `\"`"},
|
||||||
/* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`},
|
/* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`},
|
||||||
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too many params -- expected 1, got 3`},
|
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too many params -- expected 1, got 3`},
|
||||||
/* 33 */ {`isBool(false)`, true, nil},
|
/* 33 */ {`isBool(false)`, true, nil},
|
||||||
/* 34 */ {`fract(1|2)`, newFraction(1, 2), nil},
|
/* 34 */ {`fract(1:2)`, newFraction(1, 2), nil},
|
||||||
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
|
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
|
||||||
/* 36 */ {`bool(2)`, true, nil},
|
/* 36 */ {`bool(2)`, true, nil},
|
||||||
/* 37 */ {`bool(1|2)`, true, nil},
|
/* 37 */ {`bool(1:2)`, true, nil},
|
||||||
/* 38 */ {`bool(1.0)`, true, nil},
|
/* 38 */ {`bool(1.0)`, true, nil},
|
||||||
/* 39 */ {`bool("1")`, true, nil},
|
/* 39 */ {`bool("1")`, true, nil},
|
||||||
/* 40 */ {`bool(false)`, false, nil},
|
/* 40 */ {`bool(false)`, false, nil},
|
||||||
/* 41 */ {`bool([1])`, nil, `bool(): can't convert list to bool`},
|
/* 41 */ {`bool([1])`, nil, `bool(): can't convert list to bool`},
|
||||||
/* 42 */ {`dec(false)`, float64(0), nil},
|
/* 42 */ {`dec(false)`, float64(0), nil},
|
||||||
/* 43 */ {`dec(1|2)`, float64(0.5), nil},
|
/* 43 */ {`dec(1:2)`, float64(0.5), nil},
|
||||||
/* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
|
/* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
|
||||||
// /* 45 */ {`string([1])`, nil, `string(): can't convert list to string`},
|
// /* 45 */ {`string([1])`, nil, `string(): can't convert list to string`},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("EXPR_PATH", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
//runTestSuiteSpec(t, section, inputs, 10)
|
// runTestSuiteSpec(t, section, inputs, 30)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,13 @@ func TestExpr(t *testing.T) {
|
|||||||
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
|
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
|
||||||
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
||||||
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
|
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
|
||||||
/* 6 */ {`a=3; a+=1`, int64(4), nil},
|
/* 6 */ {`a=3; a+=1; a`, int64(4), nil},
|
||||||
/* 7 */ {`a=3; a-=1`, int64(2), nil},
|
/* 7 */ {`a=3; a-=1; a`, int64(2), nil},
|
||||||
/* 8 */ {`a=3; a*=2`, int64(6), nil},
|
/* 8 */ {`a=3; a*=2; a`, int64(6), nil},
|
||||||
/* 9 */ {`v=[10,20,30]; v[0]+=1; v[0]`, int64(11), nil},
|
/* 9 */ {`v=[10,20,30]; v[0]+=1; v[0]`, int64(11), nil},
|
||||||
/* 10 */ {`r={"a":10}; r["a"]+=1; r["a"]`, int64(11), nil},
|
/* 10 */ {`r={"a":10}; r["a"]+=1; r["a"]`, int64(11), nil},
|
||||||
/* 11 */ {`a=3; a/=2`, nil, `[1:8] left operand of "=" must be a variable or a collection's item`},
|
/* 11 */ {`a=3; a/=2`, int64(1), nil},
|
||||||
/* 12 */ {`*=2`, nil, `[1:2] left operand of "*=" must be a variable or a variable expression`},
|
/* 12 */ {`*=2`, nil, `[1:2] infix operator "*=" requires two non-nil operands, got 1`},
|
||||||
/* 13 */ {`a=3; a*=2+1; a`, int64(9), nil},
|
/* 13 */ {`a=3; a*=2+1; a`, int64(9), nil},
|
||||||
/* 14 */ {`a=3; (a*=2)+1; a`, int64(6), nil},
|
/* 14 */ {`a=3; (a*=2)+1; a`, int64(6), nil},
|
||||||
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
|
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
|
||||||
@ -44,6 +44,6 @@ func TestExpr(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// runTestSuiteSpec(t, section, inputs, 17)
|
// runTestSuiteSpec(t, section, inputs, 6, 7, 8, 9)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
@ -11,35 +11,39 @@ import (
|
|||||||
func TestFractionsParser(t *testing.T) {
|
func TestFractionsParser(t *testing.T) {
|
||||||
section := "Fraction"
|
section := "Fraction"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`1|2`, newFraction(1, 2), nil},
|
/* 1 */ {`1:2`, newFraction(1, 2), nil},
|
||||||
/* 2 */ {`1|2 + 1`, newFraction(3, 2), nil},
|
/* 2 */ {`1:2 + 1`, newFraction(3, 2), nil},
|
||||||
/* 3 */ {`1|2 - 1`, newFraction(-1, 2), nil},
|
/* 3 */ {`1:2 - 1`, newFraction(-1, 2), nil},
|
||||||
/* 4 */ {`1|2 * 1`, newFraction(1, 2), nil},
|
/* 4 */ {`1:2 * 1`, newFraction(1, 2), nil},
|
||||||
/* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil},
|
/* 5 */ {`1:2 * 2:3`, newFraction(2, 6), nil},
|
||||||
/* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil},
|
/* 6 */ {`1:2 / 2:3`, newFraction(3, 4), nil},
|
||||||
/* 7 */ {`1|"5"`, nil, `denominator must be integer, got string (5)`},
|
/* 7 */ {`1:"5"`, nil, `denominator must be integer, got string (5)`},
|
||||||
/* 8 */ {`"1"|5`, nil, `numerator must be integer, got string (1)`},
|
/* 8 */ {`"1":5`, nil, `numerator must be integer, got string (1)`},
|
||||||
/* 9 */ {`1|+5`, nil, `[1:3] infix operator "|" requires two non-nil operands, got 1`},
|
/* 9 */ {`1:+5`, newFraction(1, 5), nil},
|
||||||
/* 10 */ {`1|(-2)`, newFraction(-1, 2), nil},
|
/* 10 */ {`1:(-2)`, newFraction(-1, 2), nil},
|
||||||
/* 11 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil},
|
/* 11 */ {`builtin "math.arith"; add(1:2, 2:3)`, newFraction(7, 6), nil},
|
||||||
/* 12 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil},
|
/* 12 */ {`builtin "math.arith"; add(1:2, 1.0, 2)`, float64(3.5), nil},
|
||||||
/* 13 */ {`builtin "math.arith"; mul(1|2, 2|3)`, newFraction(2, 6), nil},
|
/* 13 */ {`builtin "math.arith"; mul(1:2, 2:3)`, newFraction(2, 6), nil},
|
||||||
/* 14 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil},
|
/* 14 */ {`builtin "math.arith"; mul(1:2, 1.0, 2)`, float64(1.0), nil},
|
||||||
/* 15 */ {`1|0`, nil, `division by zero`},
|
/* 15 */ {`1:0`, nil, `division by zero`},
|
||||||
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
|
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
|
||||||
/* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`},
|
/* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`},
|
||||||
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
|
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
|
||||||
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
|
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
|
||||||
/* 20 */ {`fract("1a")`, (*FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`},
|
/* 20 */ {`fract("1a")`, (*FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`},
|
||||||
/* 21 */ {`fract(1,0)`, nil, `fract(): division by zero`},
|
/* 21 */ {`fract(1,0)`, nil, `fract(): division by zero`},
|
||||||
/* 22 */ {`string(1|2)`, "1|2", nil},
|
/* 22 */ {`string(1:2)`, "1:2", nil},
|
||||||
|
/* 23 */ {`1+1:2+0.5`, float64(2), nil},
|
||||||
|
/* 24 */ {`1:(2-2)`, nil, `division by zero`},
|
||||||
|
/* 25 */ {`[0,1][1-1]:1`, newFraction(0, 1), nil},
|
||||||
}
|
}
|
||||||
|
// runTestSuiteSpec(t, section, inputs, 25)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFractionToStringSimple(t *testing.T) {
|
func TestFractionToStringSimple(t *testing.T) {
|
||||||
source := newFraction(1, 2)
|
source := newFraction(1, 2)
|
||||||
want := "1|2"
|
want := "1:2"
|
||||||
got := source.ToString(0)
|
got := source.ToString(0)
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf(`(1,2) -> result = %v [%T], want = %v [%T]`, got, got, want, want)
|
t.Errorf(`(1,2) -> result = %v [%T], want = %v [%T]`, got, got, want, want)
|
||||||
|
@ -16,11 +16,13 @@ func TestCollections(t *testing.T) {
|
|||||||
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
||||||
/* 4 */ {`"abcdef"[:]`, "abcdef", nil},
|
/* 4 */ {`"abcdef"[:]`, "abcdef", nil},
|
||||||
// /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
|
// /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
|
||||||
/* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] left operand '(1, 2)' [pair] and right operand '3' [integer] are not compatible with operator ":"`},
|
/* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] invalid range specification`},
|
||||||
|
/* 6 */ {`"abcdef"[((1>0)?{1}:{0}):3]`, "bc", nil},
|
||||||
|
/* 7 */ {`"abcdef"[[0,1][0]:1]`, "a", nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("EXPR_PATH", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 5)
|
// runTestSuiteSpec(t, section, inputs, 5)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,9 @@ func TestListParser(t *testing.T) {
|
|||||||
/* 25 */ {`["a","b","c","d"][1,1]`, nil, `[1:19] one index only is allowed`},
|
/* 25 */ {`["a","b","c","d"][1,1]`, nil, `[1:19] one index only is allowed`},
|
||||||
/* 26 */ {`[0,1,2,3,4][:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
/* 26 */ {`[0,1,2,3,4][:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||||
/* 27 */ {`["a", "b", "c"] << ;`, nil, `[1:18] infix operator "<<" requires two non-nil operands, got 1`},
|
/* 27 */ {`["a", "b", "c"] << ;`, nil, `[1:18] infix operator "<<" requires two non-nil operands, got 1`},
|
||||||
/* 28 */ {`2 << 3;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator "<<"`},
|
/* 28 */ {`2 << 3;`, int64(16), nil},
|
||||||
/* 29 */ {`but >> ["a", "b", "c"]`, nil, `[1:6] infix operator ">>" requires two non-nil operands, got 0`},
|
/* 29 */ {`but >> ["a", "b", "c"]`, nil, `[1:6] infix operator ">>" requires two non-nil operands, got 0`},
|
||||||
/* 30 */ {`2 >> 3;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator ">>"`},
|
/* 30 */ {`2 >> 3;`, int64(0), nil},
|
||||||
/* 31 */ {`a=[1,2]; a<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
/* 31 */ {`a=[1,2]; a<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||||
/* 33 */ {`a=[1,2]; 5>>a`, newListA(int64(5), int64(1), int64(2)), nil},
|
/* 33 */ {`a=[1,2]; 5>>a`, newListA(int64(5), int64(1), int64(2)), nil},
|
||||||
/* 34 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
|
/* 34 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
|
||||||
|
@ -77,7 +77,7 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 63 */ {`"1.5" == `, nil, `[1:8] infix operator "==" requires two non-nil operands, got 1`},
|
/* 63 */ {`"1.5" == `, nil, `[1:8] infix operator "==" requires two non-nil operands, got 1`},
|
||||||
/* 64 */ {`"1.5" != `, nil, `[1:8] infix operator "!=" requires two non-nil operands, got 1`},
|
/* 64 */ {`"1.5" != `, nil, `[1:8] infix operator "!=" requires two non-nil operands, got 1`},
|
||||||
/* 65 */ {"+1.5", float64(1.5), nil},
|
/* 65 */ {"+1.5", float64(1.5), nil},
|
||||||
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one not nil operand`},
|
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one non-nil operand`},
|
||||||
/* 67 */ {"4 / 0", nil, `division by zero`},
|
/* 67 */ {"4 / 0", nil, `division by zero`},
|
||||||
/* 68 */ {"4.0 / 0", nil, `division by zero`},
|
/* 68 */ {"4.0 / 0", nil, `division by zero`},
|
||||||
/* 69 */ {"4.0 / \n2", float64(2.0), nil},
|
/* 69 */ {"4.0 / \n2", float64(2.0), nil},
|
||||||
@ -94,10 +94,10 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 80 */ {`5 % 2.0`, nil, `[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`},
|
/* 80 */ {`5 % 2.0`, nil, `[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`},
|
||||||
/* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
|
/* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
|
||||||
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
|
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
|
||||||
/* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
|
/* 83 */ {`"a" < "b" AND NOT 2 == 1`, true, nil},
|
||||||
/* 84 */ {`~ 2 > 1`, false, nil},
|
/* 84 */ {`NOT 2 > 1`, false, nil},
|
||||||
/* 85 */ {`~ true && true`, false, nil},
|
/* 85 */ {`nOT true && true`, false, nil},
|
||||||
/* 86 */ {`~ false || true`, true, nil},
|
/* 86 */ {`NOT false || true`, true, nil},
|
||||||
/* 87 */ {`false but true`, true, nil},
|
/* 87 */ {`false but true`, true, nil},
|
||||||
/* 88 */ {`2+3 but 5*2`, int64(10), nil},
|
/* 88 */ {`2+3 but 5*2`, int64(10), nil},
|
||||||
/* 89 */ {`x=2`, int64(2), nil},
|
/* 89 */ {`x=2`, int64(2), nil},
|
||||||
@ -132,15 +132,13 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"},
|
/* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"},
|
||||||
/* 119 */ {`{}`, &DictType{}, nil},
|
/* 119 */ {`{}`, &DictType{}, nil},
|
||||||
/* 120 */ {`v=10; v++; v`, int64(11), nil},
|
/* 120 */ {`v=10; v++; v`, int64(11), nil},
|
||||||
/* 121 */ {`1+1|2+0.5`, float64(2), nil},
|
/* 121 */ {`1.2()`, newFraction(6, 5), nil},
|
||||||
/* 122 */ {`1.2()`, newFraction(6, 5), nil},
|
/* 122 */ {`x="abc"; x ?! #x`, int64(3), nil},
|
||||||
/* 123 */ {`1|(2-2)`, nil, `division by zero`},
|
/* 123 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '<nil>' [nil]`},
|
||||||
/* 124 */ {`x="abc"; x ?! #x`, int64(3), nil},
|
/* 124 */ {`x ?! (x+1)`, nil, nil},
|
||||||
/* 125 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '<nil>' [nil]`},
|
/* 125 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`},
|
||||||
/* 126 */ {`x ?! (x+1)`, nil, nil},
|
/* 126 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`},
|
||||||
/* 127 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`},
|
/* 127 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`},
|
||||||
/* 128 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`},
|
|
||||||
/* 129 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
@ -21,9 +21,9 @@ func TestRelational(t *testing.T) {
|
|||||||
/* 8 */ {`true != false`, true, nil},
|
/* 8 */ {`true != false`, true, nil},
|
||||||
/* 9 */ {`1.0 != 3.0-2`, false, nil},
|
/* 9 */ {`1.0 != 3.0-2`, false, nil},
|
||||||
/* 10 */ {`[1,2] != [2,1]`, true, nil},
|
/* 10 */ {`[1,2] != [2,1]`, true, nil},
|
||||||
/* 11 */ {`1|2 == 1|3`, false, nil},
|
/* 11 */ {`1:2 == 1:3`, false, nil},
|
||||||
/* 12 */ {`1|2 != 1|3`, true, nil},
|
/* 12 */ {`1:2 != 1:3`, true, nil},
|
||||||
/* 13 */ {`1|2 == 4|8`, true, nil},
|
/* 13 */ {`1:2 == 4:8`, true, nil},
|
||||||
/* 14 */ {`1 < 8`, true, nil},
|
/* 14 */ {`1 < 8`, true, nil},
|
||||||
/* 15 */ {`1 <= 8`, true, nil},
|
/* 15 */ {`1 <= 8`, true, nil},
|
||||||
/* 16 */ {`"a" < "b"`, true, nil},
|
/* 16 */ {`"a" < "b"`, true, nil},
|
||||||
@ -32,10 +32,10 @@ func TestRelational(t *testing.T) {
|
|||||||
/* 19 */ {`1.0 <= 8`, true, nil},
|
/* 19 */ {`1.0 <= 8`, true, nil},
|
||||||
/* 20 */ {`1.0 <= 1.0`, true, nil},
|
/* 20 */ {`1.0 <= 1.0`, true, nil},
|
||||||
/* 21 */ {`1.0 == 1`, true, nil},
|
/* 21 */ {`1.0 == 1`, true, nil},
|
||||||
/* 22 */ {`1|2 < 1|3`, false, nil},
|
/* 22 */ {`1:2 < 1:3`, false, nil},
|
||||||
/* 23 */ {`1|2 <= 1|3`, false, nil},
|
/* 23 */ {`1:2 <= 1:3`, false, nil},
|
||||||
/* 24 */ {`1|2 > 1|3`, true, nil},
|
/* 24 */ {`1:2 > 1:3`, true, nil},
|
||||||
/* 25 */ {`1|2 >= 1|3`, true, nil},
|
/* 25 */ {`1:2 >= 1:3`, true, nil},
|
||||||
/* 26 */ {`[1,2,3] > [2]`, true, nil},
|
/* 26 */ {`[1,2,3] > [2]`, true, nil},
|
||||||
/* 27 */ {`[1,2,3] > [9]`, false, nil},
|
/* 27 */ {`[1,2,3] > [9]`, false, nil},
|
||||||
/* 28 */ {`[1,2,3] >= [6]`, false, nil},
|
/* 28 */ {`[1,2,3] >= [6]`, false, nil},
|
||||||
|
8
term.go
8
term.go
@ -62,8 +62,8 @@ func (s *term) Clone() (d *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
d = &term{
|
d = &term{
|
||||||
tk: *s.tk.Clone(),
|
tk: *s.tk.Clone(),
|
||||||
parent: s.parent,
|
parent: s.parent,
|
||||||
children: children,
|
children: children,
|
||||||
position: s.position,
|
position: s.position,
|
||||||
priority: s.priority,
|
priority: s.priority,
|
||||||
@ -211,11 +211,11 @@ func (term *term) checkOperands() (err error) {
|
|||||||
}
|
}
|
||||||
case posPrefix:
|
case posPrefix:
|
||||||
if term.children == nil || len(term.children) != 1 || term.children[0] == nil {
|
if term.children == nil || len(term.children) != 1 || term.children[0] == nil {
|
||||||
err = term.tk.Errorf("prefix operator %q requires one not nil operand", term.tk.String())
|
err = term.tk.Errorf("prefix operator %q requires one non-nil operand", term.tk.String())
|
||||||
}
|
}
|
||||||
case posPostfix:
|
case posPostfix:
|
||||||
if term.children == nil || len(term.children) != 1 || term.anyChildrenNil() {
|
if term.children == nil || len(term.children) != 1 || term.anyChildrenNil() {
|
||||||
err = term.tk.Errorf("postfix operator %q requires one not nil operand", term.tk.String())
|
err = term.tk.Errorf("postfix operator %q requires one non-nil operand", term.tk.String())
|
||||||
}
|
}
|
||||||
case posMultifix:
|
case posMultifix:
|
||||||
if term.children == nil || len(term.children) < 3 || term.anyChildrenNil() {
|
if term.children == nil || len(term.children) < 3 || term.anyChildrenNil() {
|
||||||
|
15
token.go
15
token.go
@ -93,6 +93,15 @@ func (tk *Token) Error() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tk *Token) ErrorText() (err string) {
|
||||||
|
if tk.Sym == SymError {
|
||||||
|
if msg, ok := tk.Value.(error); ok {
|
||||||
|
err = msg.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (tk *Token) Errors(msg string) (err error) {
|
func (tk *Token) Errors(msg string) (err error) {
|
||||||
err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
|
err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
|
||||||
return
|
return
|
||||||
@ -104,6 +113,10 @@ func (tk *Token) ErrorExpectedGot(symbol string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
|
func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
|
||||||
err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, got)
|
return tk.ErrorExpectedGotStringWithPrefix("expected", symbol, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *Token) ErrorExpectedGotStringWithPrefix(prefix, symbol, got string) (err error) {
|
||||||
|
err = fmt.Errorf("[%d:%d] %s %s, got `%s`", tk.row, tk.col, prefix, symbol, got)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user