Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f41ea96d17 | |||
| d84e690ef3 | |||
| 4b25a07699 | |||
| 3736214c5a | |||
| 78cbb7b36f | |||
| 2c87d6bf9e | |||
| 691c213d17 | |||
| fa136cb70b | |||
| 76dd01afcd | |||
| 4283fab816 | |||
| 03d4c192c2 | |||
| e5f63c3d9d | |||
| d545a35acf | |||
| e4275e2cb6 | |||
| 1ff5770264 | |||
| ba32e2dccf |
@@ -5,7 +5,26 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
paramCount = "count"
|
||||||
|
paramItem = "item"
|
||||||
paramParts = "parts"
|
paramParts = "parts"
|
||||||
paramSeparator = "separator"
|
paramSeparator = "separator"
|
||||||
paramSource = "source"
|
paramSource = "source"
|
||||||
|
paramSuffix = "suffix"
|
||||||
|
paramPrefix = "prefix"
|
||||||
|
paramStart = "start"
|
||||||
|
paramEnd = "end"
|
||||||
|
paramValue = "value"
|
||||||
|
paramEllipsis = "..."
|
||||||
|
typeFilepath = "filepath"
|
||||||
|
typeDirpath = "dirpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// const (
|
||||||
|
// typeInteger = "int"
|
||||||
|
// typeFloat = "float"
|
||||||
|
// typeString = "string"
|
||||||
|
// typeFraction = "fract"
|
||||||
|
// typeList = "list"
|
||||||
|
// typeDict = "dict"
|
||||||
|
// )
|
||||||
|
|||||||
@@ -5,10 +5,14 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
typeAny = "any"
|
||||||
typeBoolean = "boolean"
|
typeBoolean = "boolean"
|
||||||
typeFloat = "decimal"
|
typeFloat = "float"
|
||||||
typeFraction = "fraction"
|
typeFraction = "fraction"
|
||||||
|
typeHandle = "handle"
|
||||||
typeInt = "integer"
|
typeInt = "integer"
|
||||||
|
typeItem = "item"
|
||||||
typeNumber = "number"
|
typeNumber = "number"
|
||||||
|
typePair = "pair"
|
||||||
typeString = "string"
|
typeString = "string"
|
||||||
)
|
)
|
||||||
|
|||||||
+3
-1
@@ -22,7 +22,9 @@ func exportFunc(ctx ExprContext, name string, info ExprFunc) {
|
|||||||
if name[0] == '@' {
|
if name[0] == '@' {
|
||||||
name = name[1:]
|
name = name[1:]
|
||||||
}
|
}
|
||||||
ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
|
// ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
|
||||||
|
// ctx.RegisterFuncInfo(name, info)
|
||||||
|
ctx.RegisterFunc(name, info.Functor(), info.ReturnType(), info.Params())
|
||||||
}
|
}
|
||||||
|
|
||||||
func exportObjects(destCtx, sourceCtx ExprContext) {
|
func exportObjects(destCtx, sourceCtx ExprContext) {
|
||||||
|
|||||||
+16
-10
@@ -4,33 +4,37 @@
|
|||||||
// context.go
|
// context.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
// ---- Function template
|
|
||||||
type FuncTemplate func(ctx ExprContext, name string, args []any) (result any, err error)
|
|
||||||
|
|
||||||
// ---- Functor interface
|
// ---- Functor interface
|
||||||
type Functor interface {
|
type Functor interface {
|
||||||
Invoke(ctx ExprContext, name string, args []any) (result any, err error)
|
Invoke(ctx ExprContext, name string, args []any) (result any, err error)
|
||||||
|
SetFunc(info ExprFunc)
|
||||||
|
GetFunc() ExprFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
type simpleFunctor struct {
|
// ---- Function Param Info
|
||||||
f FuncTemplate
|
type ExprFuncParam interface {
|
||||||
}
|
Name() string
|
||||||
|
Type() string
|
||||||
func (functor *simpleFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
|
IsOptional() bool
|
||||||
return functor.f(ctx, name, args)
|
IsRepeat() bool
|
||||||
|
DefaultValue() any
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Function Info
|
// ---- Function Info
|
||||||
type ExprFunc interface {
|
type ExprFunc interface {
|
||||||
|
Formatter
|
||||||
Name() string
|
Name() string
|
||||||
MinArgs() int
|
MinArgs() int
|
||||||
MaxArgs() int
|
MaxArgs() int
|
||||||
Functor() Functor
|
Functor() Functor
|
||||||
|
Params() []ExprFuncParam
|
||||||
|
ReturnType() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----Expression Context
|
// ----Expression Context
|
||||||
type ExprContext interface {
|
type ExprContext interface {
|
||||||
Clone() ExprContext
|
Clone() ExprContext
|
||||||
|
Merge(ctx ExprContext)
|
||||||
GetVar(varName string) (value any, exists bool)
|
GetVar(varName string) (value any, exists bool)
|
||||||
SetVar(varName string, value any)
|
SetVar(varName string, value any)
|
||||||
UnsafeSetVar(varName string, value any)
|
UnsafeSetVar(varName string, value any)
|
||||||
@@ -38,5 +42,7 @@ type ExprContext interface {
|
|||||||
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
|
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
|
||||||
GetFuncInfo(name string) (item ExprFunc, exists bool)
|
GetFuncInfo(name string) (item ExprFunc, exists bool)
|
||||||
Call(name string, args []any) (result any, err error)
|
Call(name string, args []any) (result any, err error)
|
||||||
RegisterFunc(name string, f Functor, minArgs, maxArgs int)
|
// RegisterFunc(name string, f Functor, minArgs, maxArgs int)
|
||||||
|
RegisterFuncInfo(info ExprFunc)
|
||||||
|
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) error
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -295,7 +295,7 @@ _item_ = _string-expr_ "**.**" _integer-expr_
|
|||||||
=== Booleans
|
=== Booleans
|
||||||
Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relational and boolean expressions result in boolean values.
|
Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relational and boolean expressions result in boolean values.
|
||||||
|
|
||||||
.Relational operators
|
.Relational operators^(*)^
|
||||||
[cols="^1,^2,6,4"]
|
[cols="^1,^2,6,4"]
|
||||||
|===
|
|===
|
||||||
| Symbol | Operation | Description | Examples
|
| Symbol | Operation | Description | Examples
|
||||||
@@ -314,6 +314,8 @@ Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relationa
|
|||||||
[blue]`"b" \<= "b"` -> _true_
|
[blue]`"b" \<= "b"` -> _true_
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
^(*)^ See also the [blue]`in` operator in the _list_ and _dictionary_ sections.
|
||||||
|
|
||||||
|
|
||||||
.Boolean operators
|
.Boolean operators
|
||||||
[cols="^2,^2,5,4"]
|
[cols="^2,^2,5,4"]
|
||||||
@@ -575,7 +577,7 @@ The table below shows all supported operators by decreasing priorities.
|
|||||||
| Priority | Operators | Position | Operation | Operands and results
|
| Priority | Operators | Position | Operation | Operands and results
|
||||||
|
|
||||||
.2+|*ITEM*| [blue]`.` | _Infix_ | _List item_| _list_ `"."` _integer_ -> _any_
|
.2+|*ITEM*| [blue]`.` | _Infix_ | _List item_| _list_ `"."` _integer_ -> _any_
|
||||||
| [blue]`.` | _Infix_ | _Dict item_ | _dict_ `""` _any_ -> _any_
|
| [blue]`.` | _Infix_ | _Dict item_ | _dict_ `"."` _any_ -> _any_
|
||||||
.2+|*INC*| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `"++"` -> _integer_
|
.2+|*INC*| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `"++"` -> _integer_
|
||||||
| [blue]`++` | _Postfix_ | _Next item_ | _iterator_ `"++"` -> _any_
|
| [blue]`++` | _Postfix_ | _Next item_ | _iterator_ `"++"` -> _any_
|
||||||
.1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ `"!"` -> _integer_
|
.1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ `"!"` -> _integer_
|
||||||
|
|||||||
+6
-3
@@ -585,7 +585,7 @@ pre.rouge .ss {
|
|||||||
<div class="sectionbody">
|
<div class="sectionbody">
|
||||||
<!-- toc disabled -->
|
<!-- toc disabled -->
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><mark>TODO: Work in progress (last update on 2024/05/17, 15:47 p.m.)</mark></p>
|
<p><mark>TODO: Work in progress (last update on 2024/05/20, 06:58 a.m.)</mark></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1041,7 +1041,7 @@ pre.rouge .ss {
|
|||||||
<p>Boolean data type has two values only: <em class="blue">true</em> and <em class="blue">false</em>. Relational and boolean expressions result in boolean values.</p>
|
<p>Boolean data type has two values only: <em class="blue">true</em> and <em class="blue">false</em>. Relational and boolean expressions result in boolean values.</p>
|
||||||
</div>
|
</div>
|
||||||
<table class="tableblock frame-all grid-all stretch">
|
<table class="tableblock frame-all grid-all stretch">
|
||||||
<caption class="title">Table 4. Relational operators</caption>
|
<caption class="title">Table 4. Relational operators<sup>(*)</sup></caption>
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col style="width: 7.6923%;">
|
<col style="width: 7.6923%;">
|
||||||
<col style="width: 15.3846%;">
|
<col style="width: 15.3846%;">
|
||||||
@@ -1101,6 +1101,9 @@ pre.rouge .ss {
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><sup>(*)</sup> See also the <code class="blue">in</code> operator in the <em>list</em> and <em>dictionary</em> sections.</p>
|
||||||
|
</div>
|
||||||
<table class="tableblock frame-all grid-all stretch">
|
<table class="tableblock frame-all grid-all stretch">
|
||||||
<caption class="title">Table 5. Boolean operators</caption>
|
<caption class="title">Table 5. Boolean operators</caption>
|
||||||
<colgroup>
|
<colgroup>
|
||||||
@@ -1871,7 +1874,7 @@ These operators have a high priority, in particular higher than the operator <co
|
|||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<div id="footer-text">
|
<div id="footer-text">
|
||||||
Last updated 2024-05-20 06:58:09 +0200
|
Last updated 2024-05-20 09:40:23 +0200
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -0,0 +1,401 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// fraction-type.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
//https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FractionType struct {
|
||||||
|
num, den int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFraction(num, den int64) *FractionType {
|
||||||
|
num, den = simplifyIntegers(num, den)
|
||||||
|
return &FractionType{num, den}
|
||||||
|
}
|
||||||
|
|
||||||
|
func float64ToFraction(f float64) (fract *FractionType, err error) {
|
||||||
|
var sign string
|
||||||
|
intPart, decPart := math.Modf(f)
|
||||||
|
if decPart < 0.0 {
|
||||||
|
sign = "-"
|
||||||
|
intPart = -intPart
|
||||||
|
decPart = -decPart
|
||||||
|
}
|
||||||
|
dec := fmt.Sprintf("%.12f", decPart)
|
||||||
|
s := fmt.Sprintf("%s%.f%s", sign, intPart, dec[1:])
|
||||||
|
// fmt.Printf("S: '%s'\n",s)
|
||||||
|
return makeGeneratingFraction(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on https://cs.opensource.google/go/go/+/refs/tags/go1.22.3:src/math/big/rat.go;l=39
|
||||||
|
/*
|
||||||
|
func _float64ToFraction(f float64) (num, den int64, err error) {
|
||||||
|
const expMask = 1<<11 - 1
|
||||||
|
bits := math.Float64bits(f)
|
||||||
|
mantissa := bits & (1<<52 - 1)
|
||||||
|
exp := int((bits >> 52) & expMask)
|
||||||
|
switch exp {
|
||||||
|
case expMask: // non-finite
|
||||||
|
err = errors.New("infite")
|
||||||
|
return
|
||||||
|
case 0: // denormal
|
||||||
|
exp -= 1022
|
||||||
|
default: // normal
|
||||||
|
mantissa |= 1 << 52
|
||||||
|
exp -= 1023
|
||||||
|
}
|
||||||
|
|
||||||
|
shift := 52 - exp
|
||||||
|
|
||||||
|
// Optimization (?): partially pre-normalise.
|
||||||
|
for mantissa&1 == 0 && shift > 0 {
|
||||||
|
mantissa >>= 1
|
||||||
|
shift--
|
||||||
|
}
|
||||||
|
|
||||||
|
if f < 0 {
|
||||||
|
num = -int64(mantissa)
|
||||||
|
} else {
|
||||||
|
num = int64(mantissa)
|
||||||
|
}
|
||||||
|
den = int64(1)
|
||||||
|
|
||||||
|
if shift > 0 {
|
||||||
|
den = den << shift
|
||||||
|
} else {
|
||||||
|
num = num << (-shift)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func makeGeneratingFraction(s string) (f *FractionType, err error) {
|
||||||
|
var num, den int64
|
||||||
|
var sign int64 = 1
|
||||||
|
var parts []string
|
||||||
|
if len(s) == 0 {
|
||||||
|
goto exit
|
||||||
|
}
|
||||||
|
if s[0] == '-' {
|
||||||
|
sign = int64(-1)
|
||||||
|
s = s[1:]
|
||||||
|
} else if s[0] == '+' {
|
||||||
|
s = s[1:]
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(s, "()") {
|
||||||
|
s = s[0 : len(s)-2]
|
||||||
|
}
|
||||||
|
parts = strings.SplitN(s, ".", 2)
|
||||||
|
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(parts) == 1 {
|
||||||
|
f = newFraction(sign*num, 1)
|
||||||
|
} else if len(parts) == 2 {
|
||||||
|
subParts := strings.SplitN(parts[1], "(", 2)
|
||||||
|
if len(subParts) == 1 {
|
||||||
|
den = 1
|
||||||
|
dec := parts[1]
|
||||||
|
lsd := len(dec)
|
||||||
|
for i := lsd - 1; i >= 0 && dec[i] == '0'; i-- {
|
||||||
|
lsd--
|
||||||
|
}
|
||||||
|
for _, c := range dec[0:lsd] {
|
||||||
|
if c < '0' || c > '9' {
|
||||||
|
return nil, errExpectedGot("fract", "digit", c)
|
||||||
|
}
|
||||||
|
num = num*10 + int64(c-'0')
|
||||||
|
den = den * 10
|
||||||
|
}
|
||||||
|
f = newFraction(sign*num, den)
|
||||||
|
} else if len(subParts) == 2 {
|
||||||
|
sub := num
|
||||||
|
mul := int64(1)
|
||||||
|
for _, c := range subParts[0] {
|
||||||
|
if c < '0' || c > '9' {
|
||||||
|
return nil, errExpectedGot("fract", "digit", c)
|
||||||
|
}
|
||||||
|
num = num*10 + int64(c-'0')
|
||||||
|
sub = sub*10 + int64(c-'0')
|
||||||
|
mul *= 10
|
||||||
|
}
|
||||||
|
if len(subParts) == 2 {
|
||||||
|
if s[len(s)-1] != ')' {
|
||||||
|
goto exit
|
||||||
|
}
|
||||||
|
p := subParts[1][0 : len(subParts[1])-1]
|
||||||
|
for _, c := range p {
|
||||||
|
if c < '0' || c > '9' {
|
||||||
|
return nil, errExpectedGot("fract", "digit", c)
|
||||||
|
}
|
||||||
|
num = num*10 + int64(c-'0')
|
||||||
|
den = den*10 + 9
|
||||||
|
}
|
||||||
|
den *= mul
|
||||||
|
}
|
||||||
|
num -= sub
|
||||||
|
f = newFraction(sign*num, den)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit:
|
||||||
|
if f == nil {
|
||||||
|
err = errors.New("bad syntax")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FractionType) toFloat() float64 {
|
||||||
|
return float64(f.num) / float64(f.den)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FractionType) String() string {
|
||||||
|
return f.ToString(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FractionType) ToString(opt FmtOpt) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
if opt&MultiLine == 0 {
|
||||||
|
sb.WriteString(fmt.Sprintf("%d|%d", f.num, f.den))
|
||||||
|
} else {
|
||||||
|
var s, num string
|
||||||
|
if f.num < 0 && opt&TTY == 0 {
|
||||||
|
num = strconv.FormatInt(-f.num, 10)
|
||||||
|
s = "-"
|
||||||
|
} else {
|
||||||
|
num = strconv.FormatInt(f.num, 10)
|
||||||
|
}
|
||||||
|
den := strconv.FormatInt(f.den, 10)
|
||||||
|
size := max(len(num), len(den))
|
||||||
|
if opt&TTY != 0 {
|
||||||
|
sb.WriteString(fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, s+num)))
|
||||||
|
} else {
|
||||||
|
if len(s) > 0 {
|
||||||
|
sb.WriteString(" ")
|
||||||
|
}
|
||||||
|
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, num)))
|
||||||
|
sb.WriteByte('\n')
|
||||||
|
if len(s) > 0 {
|
||||||
|
sb.WriteString(s)
|
||||||
|
sb.WriteByte(' ')
|
||||||
|
}
|
||||||
|
sb.WriteString(strings.Repeat("-", size))
|
||||||
|
sb.WriteByte('\n')
|
||||||
|
if len(s) > 0 {
|
||||||
|
sb.WriteString(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FractionType) TypeName() string {
|
||||||
|
return "fraction"
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- fraction utility functions
|
||||||
|
|
||||||
|
// greatest common divider
|
||||||
|
func gcd(a, b int64) (g int64) {
|
||||||
|
if a < 0 {
|
||||||
|
a = -a
|
||||||
|
}
|
||||||
|
if b < 0 {
|
||||||
|
b = -b
|
||||||
|
}
|
||||||
|
if a < b {
|
||||||
|
a, b = b, a
|
||||||
|
}
|
||||||
|
r := a % b
|
||||||
|
for r > 0 {
|
||||||
|
a, b = b, r
|
||||||
|
r = a % b
|
||||||
|
}
|
||||||
|
g = b
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// lower common multiple
|
||||||
|
func lcm(a, b int64) (l int64) {
|
||||||
|
g := gcd(a, b)
|
||||||
|
l = a * b / g
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum two fractions
|
||||||
|
func sumFract(f1, f2 *FractionType) (sum *FractionType) {
|
||||||
|
m := lcm(f1.den, f2.den)
|
||||||
|
sum = newFraction(f1.num*(m/f1.den)+f2.num*(m/f2.den), m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiply two fractions
|
||||||
|
func mulFract(f1, f2 *FractionType) (prod *FractionType) {
|
||||||
|
prod = newFraction(f1.num*f2.num, f1.den*f2.den)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func anyToFract(v any) (f *FractionType, err error) {
|
||||||
|
var ok bool
|
||||||
|
if f, ok = v.(*FractionType); !ok {
|
||||||
|
if n, ok := v.(int64); ok {
|
||||||
|
f = intToFraction(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if f == nil {
|
||||||
|
err = errExpectedGot("fract", typeFraction, v)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func anyPairToFract(v1, v2 any) (f1, f2 *FractionType, err error) {
|
||||||
|
if f1, err = anyToFract(v1); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f2, err = anyToFract(v2); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func sumAnyFract(af1, af2 any) (sum any, err error) {
|
||||||
|
var f1, f2 *FractionType
|
||||||
|
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f := sumFract(f1, f2)
|
||||||
|
if f.num == 0 {
|
||||||
|
sum = 0
|
||||||
|
} else {
|
||||||
|
sum = simplifyFraction(f)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns
|
||||||
|
//
|
||||||
|
// <0 if af1 < af2
|
||||||
|
// =0 if af1 == af2
|
||||||
|
// >0 if af1 > af2
|
||||||
|
// err if af1 or af2 is not convertible to fraction
|
||||||
|
func cmpAnyFract(af1, af2 any) (result int, err error) {
|
||||||
|
var f1, f2 *FractionType
|
||||||
|
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result = cmpFract(f1, f2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns
|
||||||
|
//
|
||||||
|
// <0 if af1 < af2
|
||||||
|
// =0 if af1 == af2
|
||||||
|
// >0 if af1 > af2
|
||||||
|
func cmpFract(f1, f2 *FractionType) (result int) {
|
||||||
|
f2.num = -f2.num
|
||||||
|
f := sumFract(f1, f2)
|
||||||
|
if f.num < 0 {
|
||||||
|
result = -1
|
||||||
|
} else if f.num > 0 {
|
||||||
|
result = 1
|
||||||
|
} else {
|
||||||
|
result = 0
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func subAnyFract(af1, af2 any) (sum any, err error) {
|
||||||
|
var f1, f2 *FractionType
|
||||||
|
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f2.num = -f2.num
|
||||||
|
f := sumFract(f1, f2)
|
||||||
|
if f.num == 0 {
|
||||||
|
sum = 0
|
||||||
|
} else {
|
||||||
|
sum = simplifyFraction(f)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func mulAnyFract(af1, af2 any) (prod any, err error) {
|
||||||
|
var f1, f2 *FractionType
|
||||||
|
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f1.num == 0 || f2.num == 0 {
|
||||||
|
prod = 0
|
||||||
|
} else {
|
||||||
|
f := &FractionType{f1.num * f2.num, f1.den * f2.den}
|
||||||
|
prod = simplifyFraction(f)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func divAnyFract(af1, af2 any) (quot any, err error) {
|
||||||
|
var f1, f2 *FractionType
|
||||||
|
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f2.num == 0 {
|
||||||
|
err = errors.New("division by zero")
|
||||||
|
return
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f1.num == 0 || f2.den == 0 {
|
||||||
|
quot = 0
|
||||||
|
} else {
|
||||||
|
f := &FractionType{f1.num * f2.den, f1.den * f2.num}
|
||||||
|
quot = simplifyFraction(f)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func simplifyFraction(f *FractionType) (v any) {
|
||||||
|
f.num, f.den = simplifyIntegers(f.num, f.den)
|
||||||
|
if f.den == 1 {
|
||||||
|
v = f.num
|
||||||
|
} else {
|
||||||
|
v = &FractionType{f.num, f.den}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func simplifyIntegers(num, den int64) (a, b int64) {
|
||||||
|
if num == 0 {
|
||||||
|
return 0, 1
|
||||||
|
}
|
||||||
|
if den == 0 {
|
||||||
|
panic("fraction with denominator == 0")
|
||||||
|
}
|
||||||
|
if den < 0 {
|
||||||
|
den = -den
|
||||||
|
num = -num
|
||||||
|
}
|
||||||
|
g := gcd(num, den)
|
||||||
|
a = num / g
|
||||||
|
b = den / g
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func intToFraction(n int64) *FractionType {
|
||||||
|
return &FractionType{n, 1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isFraction(v any) (ok bool) {
|
||||||
|
_, ok = v.(*FractionType)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
+41
-30
@@ -54,6 +54,24 @@ func isDictionaryFunc(ctx ExprContext, name string, args []any) (result any, err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func boolFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
switch v := args[0].(type) {
|
||||||
|
case int64:
|
||||||
|
result = (v != 0)
|
||||||
|
case *FractionType:
|
||||||
|
result = v.num != 0
|
||||||
|
case float64:
|
||||||
|
result = v != 0.0
|
||||||
|
case bool:
|
||||||
|
result = v
|
||||||
|
case string:
|
||||||
|
result = len(v) > 0
|
||||||
|
default:
|
||||||
|
err = errCantConvert(name, v, "bool")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
switch v := args[0].(type) {
|
switch v := args[0].(type) {
|
||||||
case int64:
|
case int64:
|
||||||
@@ -94,7 +112,7 @@ func decFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
|||||||
if f, err = strconv.ParseFloat(v, 64); err == nil {
|
if f, err = strconv.ParseFloat(v, 64); err == nil {
|
||||||
result = f
|
result = f
|
||||||
}
|
}
|
||||||
case *fraction:
|
case *FractionType:
|
||||||
result = v.toFloat()
|
result = v.toFloat()
|
||||||
default:
|
default:
|
||||||
err = errCantConvert(name, v, "float")
|
err = errCantConvert(name, v, "float")
|
||||||
@@ -119,10 +137,6 @@ func fractFunc(ctx ExprContext, name string, args []any) (result any, err error)
|
|||||||
}
|
}
|
||||||
case float64:
|
case float64:
|
||||||
result, err = float64ToFraction(v)
|
result, err = float64ToFraction(v)
|
||||||
// var n, d int64
|
|
||||||
// if n, d, err = float64ToFraction(v); err == nil {
|
|
||||||
// result = newFraction(n, d)
|
|
||||||
// }
|
|
||||||
case bool:
|
case bool:
|
||||||
if v {
|
if v {
|
||||||
result = newFraction(1, 1)
|
result = newFraction(1, 1)
|
||||||
@@ -131,17 +145,7 @@ func fractFunc(ctx ExprContext, name string, args []any) (result any, err error)
|
|||||||
}
|
}
|
||||||
case string:
|
case string:
|
||||||
result, err = makeGeneratingFraction(v)
|
result, err = makeGeneratingFraction(v)
|
||||||
// var f float64
|
case *FractionType:
|
||||||
// // TODO temporary implementation
|
|
||||||
// if f, err = strconv.ParseFloat(v, 64); err == nil {
|
|
||||||
// var n, d int64
|
|
||||||
// if n, d, err = float64ToFraction(f); err == nil {
|
|
||||||
// result = newFraction(n, d)
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// errors.New("convertion from string to float is ongoing")
|
|
||||||
// }
|
|
||||||
case *fraction:
|
|
||||||
result = v
|
result = v
|
||||||
default:
|
default:
|
||||||
err = errCantConvert(name, v, "float")
|
err = errCantConvert(name, v, "float")
|
||||||
@@ -154,20 +158,27 @@ func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ImportBuiltinsFuncs(ctx ExprContext) {
|
func ImportBuiltinsFuncs(ctx ExprContext) {
|
||||||
ctx.RegisterFunc("isNil", &simpleFunctor{f: isNilFunc}, 1, 1)
|
anyParams := []ExprFuncParam{
|
||||||
ctx.RegisterFunc("isInt", &simpleFunctor{f: isIntFunc}, 1, 1)
|
newFuncParam(paramValue),
|
||||||
ctx.RegisterFunc("isFloat", &simpleFunctor{f: isFloatFunc}, 1, 1)
|
}
|
||||||
ctx.RegisterFunc("isBool", &simpleFunctor{f: isBoolFunc}, 1, 1)
|
|
||||||
ctx.RegisterFunc("isString", &simpleFunctor{f: isStringFunc}, 1, 1)
|
ctx.RegisterFunc("isNil", newGolangFunctor(isNilFunc), typeBoolean, anyParams)
|
||||||
ctx.RegisterFunc("isFraction", &simpleFunctor{f: isFractionFunc}, 1, 1)
|
ctx.RegisterFunc("isInt", newGolangFunctor(isIntFunc), typeBoolean, anyParams)
|
||||||
ctx.RegisterFunc("isFract", &simpleFunctor{f: isFractionFunc}, 1, 1)
|
ctx.RegisterFunc("isFloat", newGolangFunctor(isFloatFunc), typeBoolean, anyParams)
|
||||||
ctx.RegisterFunc("isRational", &simpleFunctor{f: isRationalFunc}, 1, 1)
|
ctx.RegisterFunc("isBool", newGolangFunctor(isBoolFunc), typeBoolean, anyParams)
|
||||||
ctx.RegisterFunc("isList", &simpleFunctor{f: isListFunc}, 1, 1)
|
ctx.RegisterFunc("isString", newGolangFunctor(isStringFunc), typeBoolean, anyParams)
|
||||||
ctx.RegisterFunc("isDictionary", &simpleFunctor{f: isDictionaryFunc}, 1, 1)
|
ctx.RegisterFunc("isFract", newGolangFunctor(isFractionFunc), typeBoolean, anyParams)
|
||||||
ctx.RegisterFunc("isDict", &simpleFunctor{f: isDictionaryFunc}, 1, 1)
|
ctx.RegisterFunc("isRational", newGolangFunctor(isRationalFunc), typeBoolean, anyParams)
|
||||||
ctx.RegisterFunc("int", &simpleFunctor{f: intFunc}, 1, 1)
|
ctx.RegisterFunc("isList", newGolangFunctor(isListFunc), typeBoolean, anyParams)
|
||||||
ctx.RegisterFunc("dec", &simpleFunctor{f: decFunc}, 1, 1)
|
ctx.RegisterFunc("isDict", newGolangFunctor(isDictionaryFunc), typeBoolean, anyParams)
|
||||||
ctx.RegisterFunc("fract", &simpleFunctor{f: fractFunc}, 1, 2)
|
|
||||||
|
ctx.RegisterFunc("bool", newGolangFunctor(boolFunc), typeBoolean, anyParams)
|
||||||
|
ctx.RegisterFunc("int", newGolangFunctor(intFunc), typeInt, anyParams)
|
||||||
|
ctx.RegisterFunc("dec", newGolangFunctor(decFunc), typeFloat, anyParams)
|
||||||
|
ctx.RegisterFunc("fract", newGolangFunctor(fractFunc), typeFraction, []ExprFuncParam{
|
||||||
|
newFuncParam(paramValue),
|
||||||
|
newFuncParamFlagDef("denominator", pfOptional, 1),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
+6
-2
@@ -137,8 +137,12 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ImportImportFuncs(ctx ExprContext) {
|
func ImportImportFuncs(ctx ExprContext) {
|
||||||
ctx.RegisterFunc("import", &simpleFunctor{f: importFunc}, 1, -1)
|
ctx.RegisterFunc("import", newGolangFunctor(importFunc), typeAny, []ExprFuncParam{
|
||||||
ctx.RegisterFunc("importAll", &simpleFunctor{f: importAllFunc}, 1, -1)
|
newFuncParamFlag(typeFilepath, pfRepeat),
|
||||||
|
})
|
||||||
|
ctx.RegisterFunc("importAll", newGolangFunctor(importAllFunc), typeAny, []ExprFuncParam{
|
||||||
|
newFuncParamFlag(typeFilepath, pfRepeat),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
+13
-8
@@ -21,7 +21,7 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
|
|||||||
var sumAsFloat, sumAsFract bool
|
var sumAsFloat, sumAsFract bool
|
||||||
var floatSum float64 = 0.0
|
var floatSum float64 = 0.0
|
||||||
var intSum int64 = 0
|
var intSum int64 = 0
|
||||||
var fractSum *fraction
|
var fractSum *FractionType
|
||||||
var v any
|
var v any
|
||||||
|
|
||||||
level++
|
level++
|
||||||
@@ -61,9 +61,9 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
|
|||||||
if sumAsFloat {
|
if sumAsFloat {
|
||||||
floatSum += numAsFloat(v)
|
floatSum += numAsFloat(v)
|
||||||
} else if sumAsFract {
|
} else if sumAsFract {
|
||||||
var item *fraction
|
var item *FractionType
|
||||||
var ok bool
|
var ok bool
|
||||||
if item, ok = v.(*fraction); !ok {
|
if item, ok = v.(*FractionType); !ok {
|
||||||
iv, _ := v.(int64)
|
iv, _ := v.(int64)
|
||||||
item = newFraction(iv, 1)
|
item = newFraction(iv, 1)
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
|
|||||||
var mulAsFloat, mulAsFract bool
|
var mulAsFloat, mulAsFract bool
|
||||||
var floatProd float64 = 1.0
|
var floatProd float64 = 1.0
|
||||||
var intProd int64 = 1
|
var intProd int64 = 1
|
||||||
var fractProd *fraction
|
var fractProd *FractionType
|
||||||
var v any
|
var v any
|
||||||
|
|
||||||
level++
|
level++
|
||||||
@@ -136,9 +136,9 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
|
|||||||
if mulAsFloat {
|
if mulAsFloat {
|
||||||
floatProd *= numAsFloat(v)
|
floatProd *= numAsFloat(v)
|
||||||
} else if mulAsFract {
|
} else if mulAsFract {
|
||||||
var item *fraction
|
var item *FractionType
|
||||||
var ok bool
|
var ok bool
|
||||||
if item, ok = v.(*fraction); !ok {
|
if item, ok = v.(*FractionType); !ok {
|
||||||
iv, _ := v.(int64)
|
iv, _ := v.(int64)
|
||||||
item = newFraction(iv, 1)
|
item = newFraction(iv, 1)
|
||||||
}
|
}
|
||||||
@@ -167,8 +167,13 @@ func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ImportMathFuncs(ctx ExprContext) {
|
func ImportMathFuncs(ctx ExprContext) {
|
||||||
ctx.RegisterFunc("add", &simpleFunctor{f: addFunc}, 0, -1)
|
ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, typeNumber, []ExprFuncParam{
|
||||||
ctx.RegisterFunc("mul", &simpleFunctor{f: mulFunc}, 0, -1)
|
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 0),
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, typeNumber, []ExprFuncParam{
|
||||||
|
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 1),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
+20
-6
@@ -158,12 +158,26 @@ func readFileFunc(ctx ExprContext, name string, args []any) (result any, err err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ImportOsFuncs(ctx ExprContext) {
|
func ImportOsFuncs(ctx ExprContext) {
|
||||||
ctx.RegisterFunc("openFile", &simpleFunctor{f: openFileFunc}, 1, 1)
|
ctx.RegisterFunc("openFile", newGolangFunctor(openFileFunc), typeHandle, []ExprFuncParam{
|
||||||
ctx.RegisterFunc("appendFile", &simpleFunctor{f: appendFileFunc}, 1, 1)
|
newFuncParam(typeFilepath),
|
||||||
ctx.RegisterFunc("createFile", &simpleFunctor{f: createFileFunc}, 1, 1)
|
})
|
||||||
ctx.RegisterFunc("writeFile", &simpleFunctor{f: writeFileFunc}, 1, -1)
|
ctx.RegisterFunc("appendFile", newGolangFunctor(appendFileFunc), typeHandle, []ExprFuncParam{
|
||||||
ctx.RegisterFunc("readFile", &simpleFunctor{f: readFileFunc}, 1, 2)
|
newFuncParam(typeFilepath),
|
||||||
ctx.RegisterFunc("closeFile", &simpleFunctor{f: closeFileFunc}, 1, 1)
|
})
|
||||||
|
ctx.RegisterFunc("createFile", newGolangFunctor(createFileFunc), typeHandle, []ExprFuncParam{
|
||||||
|
newFuncParam(typeFilepath),
|
||||||
|
})
|
||||||
|
ctx.RegisterFunc("writeFile", newGolangFunctor(writeFileFunc), typeInt, []ExprFuncParam{
|
||||||
|
newFuncParam(typeHandle),
|
||||||
|
newFuncParamFlagDef(typeItem, pfOptional|pfRepeat, ""),
|
||||||
|
})
|
||||||
|
ctx.RegisterFunc("readFile", newGolangFunctor(readFileFunc), typeString, []ExprFuncParam{
|
||||||
|
newFuncParam(typeHandle),
|
||||||
|
newFuncParamFlagDef("limitCh", pfOptional, "\\n"),
|
||||||
|
})
|
||||||
|
ctx.RegisterFunc("closeFile", newGolangFunctor(closeFileFunc), typeBoolean, []ExprFuncParam{
|
||||||
|
newFuncParam(typeHandle),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
+35
-24
@@ -33,9 +33,9 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func joinStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func joinStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
// if len(args) < 1 {
|
// if len(args) < 1 {
|
||||||
// return nil, errMissingRequiredParameter(name, paramSeparator)
|
// return nil, errMissingRequiredParameter(name, paramSeparator)
|
||||||
// }
|
// }
|
||||||
if sep, ok := args[0].(string); ok {
|
if sep, ok := args[0].(string); ok {
|
||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
result = ""
|
result = ""
|
||||||
@@ -62,9 +62,6 @@ func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error
|
|||||||
var source string
|
var source string
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
// if len(args) < 1 {
|
|
||||||
// return nil, errMissingRequiredParameter(name, paramSource)
|
|
||||||
// }
|
|
||||||
if source, ok = args[0].(string); !ok {
|
if source, ok = args[0].(string); !ok {
|
||||||
return nil, errWrongParamType(name, paramSource, typeString, args[0])
|
return nil, errWrongParamType(name, paramSource, typeString, args[0])
|
||||||
}
|
}
|
||||||
@@ -93,9 +90,6 @@ func trimStrFunc(ctx ExprContext, name string, args []any) (result any, err erro
|
|||||||
var source string
|
var source string
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
// if len(args) < 1 {
|
|
||||||
// return nil, errMissingRequiredParameter(name, paramSource)
|
|
||||||
// }
|
|
||||||
if source, ok = args[0].(string); !ok {
|
if source, ok = args[0].(string); !ok {
|
||||||
return nil, errWrongParamType(name, paramSource, typeString, args[0])
|
return nil, errWrongParamType(name, paramSource, typeString, args[0])
|
||||||
}
|
}
|
||||||
@@ -108,9 +102,7 @@ func startsWithStrFunc(ctx ExprContext, name string, args []any) (result any, er
|
|||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
result = false
|
result = false
|
||||||
// if len(args) < 1 {
|
|
||||||
// return result, errMissingRequiredParameter(name, paramSource)
|
|
||||||
// }
|
|
||||||
if source, ok = args[0].(string); !ok {
|
if source, ok = args[0].(string); !ok {
|
||||||
return result, errWrongParamType(name, paramSource, typeString, args[0])
|
return result, errWrongParamType(name, paramSource, typeString, args[0])
|
||||||
}
|
}
|
||||||
@@ -133,9 +125,7 @@ func endsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err
|
|||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
result = false
|
result = false
|
||||||
// if len(args) < 1 {
|
|
||||||
// return result, errMissingRequiredParameter(name, paramSource)
|
|
||||||
// }
|
|
||||||
if source, ok = args[0].(string); !ok {
|
if source, ok = args[0].(string); !ok {
|
||||||
return result, errWrongParamType(name, paramSource, typeString, args[0])
|
return result, errWrongParamType(name, paramSource, typeString, args[0])
|
||||||
}
|
}
|
||||||
@@ -159,9 +149,6 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
|
|||||||
var parts []string
|
var parts []string
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
// if len(args) < 1 {
|
|
||||||
// return result, errMissingRequiredParameter(name, paramSource)
|
|
||||||
// }
|
|
||||||
if source, ok = args[0].(string); !ok {
|
if source, ok = args[0].(string); !ok {
|
||||||
return result, errWrongParamType(name, paramSource, typeString, args[0])
|
return result, errWrongParamType(name, paramSource, typeString, args[0])
|
||||||
}
|
}
|
||||||
@@ -196,12 +183,36 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
|
|||||||
|
|
||||||
// Import above functions in the context
|
// Import above functions in the context
|
||||||
func ImportStringFuncs(ctx ExprContext) {
|
func ImportStringFuncs(ctx ExprContext) {
|
||||||
ctx.RegisterFunc("joinStr", &simpleFunctor{f: joinStrFunc}, 1, -1)
|
ctx.RegisterFunc("joinStr", newGolangFunctor(joinStrFunc), typeString, []ExprFuncParam{
|
||||||
ctx.RegisterFunc("subStr", &simpleFunctor{f: subStrFunc}, 1, -1)
|
newFuncParam(paramSeparator),
|
||||||
ctx.RegisterFunc("splitStr", &simpleFunctor{f: splitStrFunc}, 2, -1)
|
newFuncParamFlagDef(paramItem, pfOptional|pfRepeat, ""),
|
||||||
ctx.RegisterFunc("trimStr", &simpleFunctor{f: trimStrFunc}, 1, -1)
|
})
|
||||||
ctx.RegisterFunc("startsWithStr", &simpleFunctor{f: startsWithStrFunc}, 2, -1)
|
|
||||||
ctx.RegisterFunc("endsWithStr", &simpleFunctor{f: endsWithStrFunc}, 2, -1)
|
ctx.RegisterFunc("subStr", newGolangFunctor(subStrFunc), typeString, []ExprFuncParam{
|
||||||
|
newFuncParam(paramSource),
|
||||||
|
newFuncParamFlagDef(paramStart, pfOptional, 0),
|
||||||
|
newFuncParamFlagDef(paramCount, pfOptional, -1),
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.RegisterFunc("splitStr", newGolangFunctor(splitStrFunc), "list of "+typeString, []ExprFuncParam{
|
||||||
|
newFuncParam(paramSource),
|
||||||
|
newFuncParamFlagDef(paramSeparator, pfOptional, ""),
|
||||||
|
newFuncParamFlagDef(paramCount, pfOptional, -1),
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.RegisterFunc("trimStr", newGolangFunctor(trimStrFunc), typeString, []ExprFuncParam{
|
||||||
|
newFuncParam(paramSource),
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.RegisterFunc("startsWithStr", newGolangFunctor(startsWithStrFunc), typeBoolean, []ExprFuncParam{
|
||||||
|
newFuncParam(paramSource),
|
||||||
|
newFuncParamFlag(paramPrefix, pfRepeat),
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.RegisterFunc("endsWithStr", newGolangFunctor(endsWithStrFunc), typeBoolean, []ExprFuncParam{
|
||||||
|
newFuncParam(paramSource),
|
||||||
|
newFuncParamFlag(paramSuffix, pfRepeat),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the import function in the import-register.
|
// Register the import function in the import-register.
|
||||||
|
|||||||
+237
@@ -0,0 +1,237 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// function.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ---- Function template
|
||||||
|
type FuncTemplate func(ctx ExprContext, name string, args []any) (result any, err error)
|
||||||
|
|
||||||
|
// ---- Common functor definition
|
||||||
|
type baseFunctor struct {
|
||||||
|
info ExprFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (functor *baseFunctor) ToString(opt FmtOpt) (s string) {
|
||||||
|
if functor.info != nil {
|
||||||
|
s = functor.info.ToString(opt)
|
||||||
|
} else {
|
||||||
|
s = "func() {<body>}"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (functor *baseFunctor) SetFunc(info ExprFunc) {
|
||||||
|
functor.info = info
|
||||||
|
}
|
||||||
|
|
||||||
|
func (functor *baseFunctor) GetFunc() ExprFunc {
|
||||||
|
return functor.info
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Linking with Go functions
|
||||||
|
type golangFunctor struct {
|
||||||
|
baseFunctor
|
||||||
|
f FuncTemplate
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGolangFunctor(f FuncTemplate) *golangFunctor {
|
||||||
|
return &golangFunctor{f: f}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
return functor.f(ctx, name, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Linking with Expr functions
|
||||||
|
type exprFunctor struct {
|
||||||
|
baseFunctor
|
||||||
|
params []string
|
||||||
|
expr Expr
|
||||||
|
defCtx ExprContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func newExprFunctor(e Expr, params []string, ctx ExprContext) *exprFunctor {
|
||||||
|
return &exprFunctor{expr: e, params: params, defCtx: ctx}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
if functor.defCtx != nil {
|
||||||
|
ctx.Merge(functor.defCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, p := range functor.params {
|
||||||
|
if i < len(args) {
|
||||||
|
arg := args[i]
|
||||||
|
if funcArg, ok := arg.(Functor); ok {
|
||||||
|
// ctx.RegisterFunc(p, functor, 0, -1)
|
||||||
|
ctx.RegisterFunc(p, funcArg, typeAny, []ExprFuncParam{
|
||||||
|
newFuncParam(paramValue),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ctx.UnsafeSetVar(p, arg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.UnsafeSetVar(p, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result, err = functor.expr.eval(ctx, false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Function Parameters
|
||||||
|
type paramFlags uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
pfOptional paramFlags = 1 << iota
|
||||||
|
pfRepeat
|
||||||
|
)
|
||||||
|
|
||||||
|
type funcParamInfo struct {
|
||||||
|
name string
|
||||||
|
flags paramFlags
|
||||||
|
defaultValue any
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFuncParam(name string) ExprFuncParam {
|
||||||
|
return &funcParamInfo{name: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFuncParamFlag(name string, flags paramFlags) ExprFuncParam {
|
||||||
|
return &funcParamInfo{name: name, flags: flags}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFuncParamFlagDef(name string, flags paramFlags, defValue any) *funcParamInfo {
|
||||||
|
return &funcParamInfo{name: name, flags: flags, defaultValue: defValue}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (param *funcParamInfo) Name() string {
|
||||||
|
return param.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (param *funcParamInfo) Type() string {
|
||||||
|
return "any"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (param *funcParamInfo) IsOptional() bool {
|
||||||
|
return (param.flags & pfOptional) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (param *funcParamInfo) IsRepeat() bool {
|
||||||
|
return (param.flags & pfRepeat) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (param *funcParamInfo) DefaultValue() any {
|
||||||
|
return param.defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Functions
|
||||||
|
type funcInfo struct {
|
||||||
|
name string
|
||||||
|
minArgs int
|
||||||
|
maxArgs int
|
||||||
|
functor Functor
|
||||||
|
params []ExprFuncParam
|
||||||
|
returnType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
|
||||||
|
var minArgs = 0
|
||||||
|
var maxArgs = 0
|
||||||
|
if params != nil {
|
||||||
|
for _, p := range params {
|
||||||
|
if maxArgs == -1 {
|
||||||
|
return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
|
||||||
|
}
|
||||||
|
if p.IsOptional() {
|
||||||
|
maxArgs++
|
||||||
|
} else if maxArgs == minArgs {
|
||||||
|
minArgs++
|
||||||
|
maxArgs++
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name())
|
||||||
|
}
|
||||||
|
if p.IsRepeat() {
|
||||||
|
maxArgs = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info = &funcInfo{
|
||||||
|
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, params: params,
|
||||||
|
}
|
||||||
|
functor.SetFunc(info)
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUnnamedFuncInfo(functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
|
||||||
|
return newFuncInfo("unnamed", functor, returnType, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *funcInfo) Params() []ExprFuncParam {
|
||||||
|
return info.params
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *funcInfo) ReturnType() string {
|
||||||
|
return info.returnType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *funcInfo) ToString(opt FmtOpt) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
if len(info.Name()) == 0 {
|
||||||
|
sb.WriteString("func")
|
||||||
|
} else {
|
||||||
|
sb.WriteString(info.Name())
|
||||||
|
}
|
||||||
|
sb.WriteByte('(')
|
||||||
|
if info.params != nil {
|
||||||
|
for i, p := range info.params {
|
||||||
|
if i > 0 {
|
||||||
|
sb.WriteString(", ")
|
||||||
|
}
|
||||||
|
sb.WriteString(p.Name())
|
||||||
|
|
||||||
|
if p.IsOptional() {
|
||||||
|
sb.WriteByte('=')
|
||||||
|
if s, ok := p.DefaultValue().(string); ok {
|
||||||
|
sb.WriteByte('"')
|
||||||
|
sb.WriteString(s)
|
||||||
|
sb.WriteByte('"')
|
||||||
|
} else {
|
||||||
|
sb.WriteString(fmt.Sprintf("%v", p.DefaultValue()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if info.maxArgs < 0 {
|
||||||
|
sb.WriteString(" ...")
|
||||||
|
}
|
||||||
|
sb.WriteString("): ")
|
||||||
|
if len(info.returnType) > 0 {
|
||||||
|
sb.WriteString(info.returnType)
|
||||||
|
} else {
|
||||||
|
sb.WriteString(typeAny)
|
||||||
|
}
|
||||||
|
sb.WriteString(" {<body>}")
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *funcInfo) Name() string {
|
||||||
|
return info.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *funcInfo) MinArgs() int {
|
||||||
|
return info.minArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *funcInfo) MaxArgs() int {
|
||||||
|
return info.maxArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *funcInfo) Functor() Functor {
|
||||||
|
return info.functor
|
||||||
|
}
|
||||||
+3
-2
@@ -6,7 +6,7 @@ package expr
|
|||||||
|
|
||||||
import "path/filepath"
|
import "path/filepath"
|
||||||
|
|
||||||
var globalCtx *SimpleFuncStore
|
var globalCtx *SimpleStore
|
||||||
|
|
||||||
func ImportInContext(name string) (exists bool) {
|
func ImportInContext(name string) (exists bool) {
|
||||||
var mod *module
|
var mod *module
|
||||||
@@ -50,5 +50,6 @@ func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool, owne
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
globalCtx = NewSimpleFuncStore()
|
globalCtx = NewSimpleStore()
|
||||||
|
ImportBuiltinsFuncs(globalCtx)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-3
@@ -34,12 +34,15 @@ func EvalStringA(source string, args ...Arg) (result any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func EvalStringV(source string, args []Arg) (result any, err error) {
|
func EvalStringV(source string, args []Arg) (result any, err error) {
|
||||||
ctx := NewSimpleFuncStore()
|
ctx := NewSimpleStore()
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
if isFunc(arg.Value) {
|
if isFunc(arg.Value) {
|
||||||
if f, ok := arg.Value.(FuncTemplate); ok {
|
if f, ok := arg.Value.(FuncTemplate); ok {
|
||||||
functor := &simpleFunctor{f: f}
|
functor := newGolangFunctor(f)
|
||||||
ctx.RegisterFunc(arg.Name, functor, 0, -1)
|
// ctx.RegisterFunc(arg.Name, functor, 0, -1)
|
||||||
|
ctx.RegisterFunc(arg.Name, functor, typeAny, []ExprFuncParam{
|
||||||
|
newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 0),
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("invalid function specification: %q", arg.Name)
|
err = fmt.Errorf("invalid function specification: %q", arg.Name)
|
||||||
}
|
}
|
||||||
|
|||||||
+149
@@ -0,0 +1,149 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// list-type.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListType []any
|
||||||
|
|
||||||
|
func newListA(listAny ...any) (list *ListType) {
|
||||||
|
return newList(listAny)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newList(listAny []any) (list *ListType) {
|
||||||
|
if listAny != nil {
|
||||||
|
ls := make(ListType, len(listAny))
|
||||||
|
for i, item := range listAny {
|
||||||
|
ls[i] = item
|
||||||
|
}
|
||||||
|
list = &ls
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *ListType) ToString(opt FmtOpt) (s string) {
|
||||||
|
var sb strings.Builder
|
||||||
|
sb.WriteByte('[')
|
||||||
|
if len(*ls) > 0 {
|
||||||
|
if opt&MultiLine != 0 {
|
||||||
|
sb.WriteString("\n ")
|
||||||
|
}
|
||||||
|
for i, item := range []any(*ls) {
|
||||||
|
if i > 0 {
|
||||||
|
if opt&MultiLine != 0 {
|
||||||
|
sb.WriteString(",\n ")
|
||||||
|
} else {
|
||||||
|
sb.WriteString(", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s, ok := item.(string); ok {
|
||||||
|
sb.WriteByte('"')
|
||||||
|
sb.WriteString(s)
|
||||||
|
sb.WriteByte('"')
|
||||||
|
} else {
|
||||||
|
sb.WriteString(fmt.Sprintf("%v", item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opt&MultiLine != 0 {
|
||||||
|
sb.WriteByte('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.WriteByte(']')
|
||||||
|
s = sb.String()
|
||||||
|
if opt&Truncate != 0 && len(s) > TruncateSize {
|
||||||
|
s = TruncateString(s)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *ListType) String() string {
|
||||||
|
return ls.ToString(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *ListType) TypeName() string {
|
||||||
|
return "list"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (list *ListType) indexDeepCmp(target any) (index int) {
|
||||||
|
index = -1
|
||||||
|
for i, item := range *list {
|
||||||
|
if reflect.DeepEqual(item, target) {
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *ListType) contains(t *ListType) (answer bool) {
|
||||||
|
if len(*ls) >= len(*t) {
|
||||||
|
answer = true
|
||||||
|
for _, item := range *t {
|
||||||
|
if answer = ls.indexDeepSameCmp(item) >= 0; !answer {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (list *ListType) indexDeepSameCmp(target any) (index int) {
|
||||||
|
var eq bool
|
||||||
|
var err error
|
||||||
|
index = -1
|
||||||
|
for i, item := range *list {
|
||||||
|
if eq, err = deepSame(item, target, sameContent); err != nil {
|
||||||
|
break
|
||||||
|
} else if eq {
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func sameContent(a, b any) (same bool, err error) {
|
||||||
|
la, _ := a.(*ListType)
|
||||||
|
lb, _ := b.(*ListType)
|
||||||
|
if len(*la) == len(*lb) {
|
||||||
|
same = true
|
||||||
|
for _, item := range *la {
|
||||||
|
if pos := lb.indexDeepSameCmp(item); pos < 0 {
|
||||||
|
same = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func deepSame(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) {
|
||||||
|
if isNumOrFract(a) && isNumOrFract(b) {
|
||||||
|
if IsNumber(a) && IsNumber(b) {
|
||||||
|
if IsInteger(a) && IsInteger(b) {
|
||||||
|
li, _ := a.(int64)
|
||||||
|
ri, _ := b.(int64)
|
||||||
|
eq = li == ri
|
||||||
|
} else {
|
||||||
|
eq = numAsFloat(a) == numAsFloat(b)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var cmp int
|
||||||
|
if cmp, err = cmpAnyFract(a, b); err == nil {
|
||||||
|
eq = cmp == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if deepCmp != nil && IsList(a) && IsList(b) {
|
||||||
|
eq, err = deepCmp(a, b)
|
||||||
|
} else {
|
||||||
|
eq = reflect.DeepEqual(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
+26
-25
@@ -31,7 +31,8 @@ func checkFunctionCall(ctx ExprContext, name string, params []any) (err error) {
|
|||||||
err = errTooMuchParams(info.MaxArgs(), len(params))
|
err = errTooMuchParams(info.MaxArgs(), len(params))
|
||||||
}
|
}
|
||||||
if err == nil && owner != ctx {
|
if err == nil && owner != ctx {
|
||||||
ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
|
// ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
|
||||||
|
ctx.RegisterFuncInfo(info)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("unknown function %s()", name)
|
err = fmt.Errorf("unknown function %s()", name)
|
||||||
@@ -75,27 +76,30 @@ func newFuncDefTerm(tk *Token, args []*term) *term {
|
|||||||
|
|
||||||
// -------- eval func def
|
// -------- eval func def
|
||||||
// TODO
|
// TODO
|
||||||
type funcDefFunctor struct {
|
// type funcDefFunctor struct {
|
||||||
params []string
|
// params []string
|
||||||
expr Expr
|
// expr Expr
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (functor *funcDefFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
|
// func (funcDef *funcDefFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
for i, p := range functor.params {
|
// for i, p := range funcDef.params {
|
||||||
if i < len(args) {
|
// if i < len(args) {
|
||||||
arg := args[i]
|
// arg := args[i]
|
||||||
if functor, ok := arg.(Functor); ok {
|
// if functor, ok := arg.(Functor); ok {
|
||||||
ctx.RegisterFunc(p, functor, 0, -1)
|
// // ctx.RegisterFunc(p, functor, 0, -1)
|
||||||
} else {
|
// ctx.RegisterFunc2(p, functor, typeAny, []ExprFuncParam{
|
||||||
ctx.UnsafeSetVar(p, arg)
|
// newFuncParam(paramValue),
|
||||||
}
|
// })
|
||||||
} else {
|
// } else {
|
||||||
ctx.UnsafeSetVar(p, nil)
|
// ctx.UnsafeSetVar(p, arg)
|
||||||
}
|
// }
|
||||||
}
|
// } else {
|
||||||
result, err = functor.expr.eval(ctx, false)
|
// ctx.UnsafeSetVar(p, nil)
|
||||||
return
|
// }
|
||||||
}
|
// }
|
||||||
|
// result, err = funcDef.expr.eval(ctx, false)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
func evalFuncDef(ctx ExprContext, self *term) (v any, err error) {
|
func evalFuncDef(ctx ExprContext, self *term) (v any, err error) {
|
||||||
bodySpec := self.value()
|
bodySpec := self.value()
|
||||||
@@ -104,10 +108,7 @@ func evalFuncDef(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
for _, param := range self.children {
|
for _, param := range self.children {
|
||||||
paramList = append(paramList, param.source())
|
paramList = append(paramList, param.source())
|
||||||
}
|
}
|
||||||
v = &funcDefFunctor{
|
v = newExprFunctor(expr, paramList, ctx)
|
||||||
params: paramList,
|
|
||||||
expr: expr,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
err = errors.New("invalid function definition: the body specification must be an expression")
|
err = errors.New("invalid function definition: the body specification must be an expression")
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -74,7 +74,8 @@ func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map
|
|||||||
ds = make(map[string]Functor)
|
ds = make(map[string]Functor)
|
||||||
for keyAny, item := range *dictAny {
|
for keyAny, item := range *dictAny {
|
||||||
if key, ok := keyAny.(string); ok {
|
if key, ok := keyAny.(string); ok {
|
||||||
if functor, ok := item.(*funcDefFunctor); ok {
|
//if functor, ok := item.(*funcDefFunctor); ok {
|
||||||
|
if functor, ok := item.(Functor); ok {
|
||||||
ds[key] = functor
|
ds[key] = functor
|
||||||
if index := slices.Index(requiredFields, key); index >= 0 {
|
if index := slices.Index(requiredFields, key); index >= 0 {
|
||||||
foundFields |= 1 << index
|
foundFields |= 1 << index
|
||||||
|
|||||||
+3
-80
@@ -4,91 +4,14 @@
|
|||||||
// operand-list.go
|
// operand-list.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ListType []any
|
|
||||||
|
|
||||||
func newListA(listAny ...any) (list *ListType) {
|
|
||||||
return newList(listAny)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newList(listAny []any) (list *ListType) {
|
|
||||||
if listAny != nil {
|
|
||||||
ls := make(ListType, len(listAny))
|
|
||||||
for i, item := range listAny {
|
|
||||||
ls[i] = item
|
|
||||||
}
|
|
||||||
list = &ls
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ls *ListType) ToString(opt FmtOpt) (s string) {
|
|
||||||
var sb strings.Builder
|
|
||||||
sb.WriteByte('[')
|
|
||||||
if len(*ls) > 0 {
|
|
||||||
if opt&MultiLine != 0 {
|
|
||||||
sb.WriteString("\n ")
|
|
||||||
}
|
|
||||||
for i, item := range []any(*ls) {
|
|
||||||
if i > 0 {
|
|
||||||
if opt&MultiLine != 0 {
|
|
||||||
sb.WriteString(",\n ")
|
|
||||||
} else {
|
|
||||||
sb.WriteString(", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if s, ok := item.(string); ok {
|
|
||||||
sb.WriteByte('"')
|
|
||||||
sb.WriteString(s)
|
|
||||||
sb.WriteByte('"')
|
|
||||||
} else {
|
|
||||||
sb.WriteString(fmt.Sprintf("%v", item))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if opt&MultiLine != 0 {
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.WriteByte(']')
|
|
||||||
s = sb.String()
|
|
||||||
if opt&Truncate != 0 && len(s) > TruncateSize {
|
|
||||||
s = TruncateString(s)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ls *ListType) String() string {
|
|
||||||
return ls.ToString(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ls *ListType) TypeName() string {
|
|
||||||
return "list"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (list *ListType) indexDeepCmp(target any) (index int) {
|
|
||||||
index = -1
|
|
||||||
for i, item := range *list {
|
|
||||||
if reflect.DeepEqual(item, target) {
|
|
||||||
index = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------- list term
|
// -------- list term
|
||||||
func newListTermA(args ...*term) *term {
|
func newListTermA(args ...*term) *term {
|
||||||
return newListTerm(args)
|
return newListTerm(0, 0, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newListTerm(args []*term) *term {
|
func newListTerm(row, col int, args []*term) *term {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *NewValueToken(0, 0, SymList, "[]", args),
|
tk: *NewValueToken(row, col, SymList, "[]", args),
|
||||||
parent: nil,
|
parent: nil,
|
||||||
children: nil,
|
children: nil,
|
||||||
position: posLeaf,
|
position: posLeaf,
|
||||||
|
|||||||
+13
-10
@@ -31,18 +31,21 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
|
|
||||||
if v, err = rightChild.compute(ctx); err == nil {
|
if v, err = rightChild.compute(ctx); err == nil {
|
||||||
if functor, ok := v.(Functor); ok {
|
if functor, ok := v.(Functor); ok {
|
||||||
var minArgs, maxArgs int = 0, -1
|
|
||||||
|
|
||||||
funcName := rightChild.source()
|
funcName := rightChild.source()
|
||||||
if info, exists := ctx.GetFuncInfo(funcName); exists {
|
if info, exists, _ := GetFuncInfo(ctx, funcName); exists {
|
||||||
minArgs = info.MinArgs()
|
// ctx.RegisterFuncInfo(info)
|
||||||
maxArgs = info.MaxArgs()
|
ctx.RegisterFunc(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params())
|
||||||
} else if funcDef, ok := functor.(*funcDefFunctor); ok {
|
} else if funcDef, ok := functor.(*exprFunctor); ok {
|
||||||
l := len(funcDef.params)
|
paramSpecs := ForAll(funcDef.params, newFuncParam)
|
||||||
minArgs = l
|
// paramCount := len(funcDef.params)
|
||||||
maxArgs = l
|
// paramSpecs := make([]ExprFuncParam, paramCount)
|
||||||
|
// for i := range paramSpecs {
|
||||||
|
// paramSpecs[i] = newFuncParam(funcDef.params[i])
|
||||||
|
// }
|
||||||
|
ctx.RegisterFunc(leftTerm.source(), functor, typeAny, paramSpecs)
|
||||||
|
} else {
|
||||||
|
err = self.Errorf("unknown function %s()", funcName)
|
||||||
}
|
}
|
||||||
ctx.RegisterFunc(leftTerm.source(), functor, minArgs, maxArgs)
|
|
||||||
} else {
|
} else {
|
||||||
ctx.UnsafeSetVar(leftTerm.source(), v)
|
ctx.UnsafeSetVar(leftTerm.source(), v)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,10 @@ func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
v = leftValue
|
v = leftValue
|
||||||
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||||
if functor, ok := rightValue.(Functor); ok {
|
if functor, ok := rightValue.(Functor); ok {
|
||||||
ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
||||||
|
ctx.RegisterFunc(leftTerm.source(), functor, typeAny, []ExprFuncParam{
|
||||||
|
newFuncParamFlag(paramValue, pfOptional|pfRepeat),
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
v = rightValue
|
v = rightValue
|
||||||
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
|
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
|
||||||
|
|||||||
+4
-4
@@ -20,18 +20,18 @@ func evalContextValue(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
var childValue any
|
var childValue any
|
||||||
|
|
||||||
var sourceCtx ExprContext
|
var sourceCtx ExprContext
|
||||||
if len(self.children) == 0 {
|
if self.children == nil || len(self.children) == 0 {
|
||||||
sourceCtx = ctx
|
sourceCtx = ctx
|
||||||
|
} else if self.children[0].symbol() == SymVariable && self.children[0].source() == "global" {
|
||||||
|
sourceCtx = globalCtx
|
||||||
} else if childValue, err = self.evalPrefix(ctx); err == nil {
|
} else if childValue, err = self.evalPrefix(ctx); err == nil {
|
||||||
if dc, ok := childValue.(*dataCursor); ok {
|
if dc, ok := childValue.(*dataCursor); ok {
|
||||||
sourceCtx = dc.ctx
|
sourceCtx = dc.ctx
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceCtx != nil {
|
if sourceCtx != nil {
|
||||||
if formatter, ok := ctx.(Formatter); ok {
|
if formatter, ok := sourceCtx.(Formatter); ok {
|
||||||
v = formatter.ToString(0)
|
v = formatter.ToString(0)
|
||||||
} else {
|
} else {
|
||||||
keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' })
|
keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' })
|
||||||
|
|||||||
+6
-41
@@ -4,8 +4,6 @@
|
|||||||
// operator-dot.go
|
// operator-dot.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// -------- dot term
|
// -------- dot term
|
||||||
func newDotTerm(tk *Token) (inst *term) {
|
func newDotTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
@@ -17,24 +15,6 @@ func newDotTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyIndex(ctx ExprContext, indexTerm *term, maxValue int) (index int, err error) {
|
|
||||||
var v int
|
|
||||||
var indexValue any
|
|
||||||
if indexValue, err = indexTerm.compute(ctx); err == nil {
|
|
||||||
if v, err = indexTerm.toInt(indexValue, "index expression value must be integer"); err == nil {
|
|
||||||
if v < 0 && v >= -maxValue {
|
|
||||||
v = maxValue + v
|
|
||||||
}
|
|
||||||
if v >= 0 && v < maxValue {
|
|
||||||
index = v
|
|
||||||
} else {
|
|
||||||
err = indexTerm.Errorf("index %d out of bounds", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
@@ -48,27 +28,8 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
indexTerm := self.children[1]
|
indexTerm := self.children[1]
|
||||||
|
|
||||||
switch unboxedValue := leftValue.(type) {
|
switch unboxedValue := leftValue.(type) {
|
||||||
case *ListType:
|
|
||||||
var index int
|
|
||||||
array := ([]any)(*unboxedValue)
|
|
||||||
if index, err = verifyIndex(ctx, indexTerm, len(array)); err == nil {
|
|
||||||
v = array[index]
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
var index int
|
|
||||||
if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil {
|
|
||||||
v = string(unboxedValue[index])
|
|
||||||
}
|
|
||||||
case *DictType:
|
|
||||||
var ok bool
|
|
||||||
var indexValue any
|
|
||||||
if indexValue, err = indexTerm.compute(ctx); err == nil {
|
|
||||||
if v, ok = (*unboxedValue)[indexValue]; !ok {
|
|
||||||
err = fmt.Errorf("key %v does not belong to the dictionary", rightValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case ExtIterator:
|
case ExtIterator:
|
||||||
if indexTerm.symbol() == SymVariable {
|
if indexTerm.symbol() == SymVariable /*|| indexTerm.symbol() == SymString */ {
|
||||||
opName := indexTerm.source()
|
opName := indexTerm.source()
|
||||||
if unboxedValue.HasOperation(opName) {
|
if unboxedValue.HasOperation(opName) {
|
||||||
v, err = unboxedValue.CallOperation(opName, []any{})
|
v, err = unboxedValue.CallOperation(opName, []any{})
|
||||||
@@ -76,9 +37,13 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
err = indexTerm.Errorf("this iterator do not support the %q command", opName)
|
err = indexTerm.Errorf("this iterator do not support the %q command", opName)
|
||||||
v = false
|
v = false
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
err = indexTerm.tk.ErrorExpectedGot("identifier")
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||||
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-355
@@ -9,205 +9,8 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type fraction struct {
|
|
||||||
num, den int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFraction(num, den int64) *fraction {
|
|
||||||
/* if den < 0 {
|
|
||||||
den = -den
|
|
||||||
num = -num
|
|
||||||
}*/
|
|
||||||
num, den = simplifyIntegers(num, den)
|
|
||||||
return &fraction{num, den}
|
|
||||||
}
|
|
||||||
|
|
||||||
func float64ToFraction(f float64) (fract *fraction, err error) {
|
|
||||||
var sign string
|
|
||||||
intPart, decPart := math.Modf(f)
|
|
||||||
if decPart < 0.0 {
|
|
||||||
sign = "-"
|
|
||||||
intPart = -intPart
|
|
||||||
decPart = -decPart
|
|
||||||
}
|
|
||||||
dec := fmt.Sprintf("%.12f", decPart)
|
|
||||||
s := fmt.Sprintf("%s%.f%s", sign, intPart, dec[1:])
|
|
||||||
// fmt.Printf("S: '%s'\n",s)
|
|
||||||
return makeGeneratingFraction(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Based on https://cs.opensource.google/go/go/+/refs/tags/go1.22.3:src/math/big/rat.go;l=39
|
|
||||||
/*
|
|
||||||
func _float64ToFraction(f float64) (num, den int64, err error) {
|
|
||||||
const expMask = 1<<11 - 1
|
|
||||||
bits := math.Float64bits(f)
|
|
||||||
mantissa := bits & (1<<52 - 1)
|
|
||||||
exp := int((bits >> 52) & expMask)
|
|
||||||
switch exp {
|
|
||||||
case expMask: // non-finite
|
|
||||||
err = errors.New("infite")
|
|
||||||
return
|
|
||||||
case 0: // denormal
|
|
||||||
exp -= 1022
|
|
||||||
default: // normal
|
|
||||||
mantissa |= 1 << 52
|
|
||||||
exp -= 1023
|
|
||||||
}
|
|
||||||
|
|
||||||
shift := 52 - exp
|
|
||||||
|
|
||||||
// Optimization (?): partially pre-normalise.
|
|
||||||
for mantissa&1 == 0 && shift > 0 {
|
|
||||||
mantissa >>= 1
|
|
||||||
shift--
|
|
||||||
}
|
|
||||||
|
|
||||||
if f < 0 {
|
|
||||||
num = -int64(mantissa)
|
|
||||||
} else {
|
|
||||||
num = int64(mantissa)
|
|
||||||
}
|
|
||||||
den = int64(1)
|
|
||||||
|
|
||||||
if shift > 0 {
|
|
||||||
den = den << shift
|
|
||||||
} else {
|
|
||||||
num = num << (-shift)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
func makeGeneratingFraction(s string) (f *fraction, err error) {
|
|
||||||
var num, den int64
|
|
||||||
var sign int64 = 1
|
|
||||||
var parts []string
|
|
||||||
if len(s) == 0 {
|
|
||||||
goto exit
|
|
||||||
}
|
|
||||||
if s[0] == '-' {
|
|
||||||
sign = int64(-1)
|
|
||||||
s = s[1:]
|
|
||||||
} else if s[0] == '+' {
|
|
||||||
s = s[1:]
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(s, "()") {
|
|
||||||
s = s[0 : len(s)-2]
|
|
||||||
}
|
|
||||||
parts = strings.SplitN(s, ".", 2)
|
|
||||||
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(parts) == 1 {
|
|
||||||
f = newFraction(sign*num, 1)
|
|
||||||
} else if len(parts) == 2 {
|
|
||||||
subParts := strings.SplitN(parts[1], "(", 2)
|
|
||||||
if len(subParts) == 1 {
|
|
||||||
den = 1
|
|
||||||
dec := parts[1]
|
|
||||||
lsd := len(dec)
|
|
||||||
for i := lsd - 1; i >= 0 && dec[i] == '0'; i-- {
|
|
||||||
lsd--
|
|
||||||
}
|
|
||||||
for _, c := range dec[0:lsd] {
|
|
||||||
if c < '0' || c > '9' {
|
|
||||||
return nil, errExpectedGot("fract", "digit", c)
|
|
||||||
}
|
|
||||||
num = num*10 + int64(c-'0')
|
|
||||||
den = den * 10
|
|
||||||
}
|
|
||||||
f = newFraction(sign*num, den)
|
|
||||||
} else if len(subParts) == 2 {
|
|
||||||
sub := num
|
|
||||||
mul := int64(1)
|
|
||||||
for _, c := range subParts[0] {
|
|
||||||
if c < '0' || c > '9' {
|
|
||||||
return nil, errExpectedGot("fract", "digit", c)
|
|
||||||
}
|
|
||||||
num = num*10 + int64(c-'0')
|
|
||||||
sub = sub*10 + int64(c-'0')
|
|
||||||
mul *= 10
|
|
||||||
}
|
|
||||||
if len(subParts) == 2 {
|
|
||||||
if s[len(s)-1] != ')' {
|
|
||||||
goto exit
|
|
||||||
}
|
|
||||||
p := subParts[1][0 : len(subParts[1])-1]
|
|
||||||
for _, c := range p {
|
|
||||||
if c < '0' || c > '9' {
|
|
||||||
return nil, errExpectedGot("fract", "digit", c)
|
|
||||||
}
|
|
||||||
num = num*10 + int64(c-'0')
|
|
||||||
den = den*10 + 9
|
|
||||||
}
|
|
||||||
den *= mul
|
|
||||||
}
|
|
||||||
num -= sub
|
|
||||||
f = newFraction(sign*num, den)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit:
|
|
||||||
if f == nil {
|
|
||||||
err = errors.New("bad syntax")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fraction) toFloat() float64 {
|
|
||||||
return float64(f.num) / float64(f.den)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fraction) String() string {
|
|
||||||
return f.ToString(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fraction) ToString(opt FmtOpt) string {
|
|
||||||
var sb strings.Builder
|
|
||||||
if opt&MultiLine == 0 {
|
|
||||||
sb.WriteString(fmt.Sprintf("%d|%d", f.num, f.den))
|
|
||||||
} else {
|
|
||||||
var s, num string
|
|
||||||
if f.num < 0 && opt&TTY == 0 {
|
|
||||||
num = strconv.FormatInt(-f.num, 10)
|
|
||||||
s = "-"
|
|
||||||
} else {
|
|
||||||
num = strconv.FormatInt(f.num, 10)
|
|
||||||
}
|
|
||||||
den := strconv.FormatInt(f.den, 10)
|
|
||||||
size := max(len(num), len(den))
|
|
||||||
if opt&TTY != 0 {
|
|
||||||
sb.WriteString(fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, s+num)))
|
|
||||||
} else {
|
|
||||||
if len(s) > 0 {
|
|
||||||
sb.WriteString(" ")
|
|
||||||
}
|
|
||||||
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, num)))
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
if len(s) > 0 {
|
|
||||||
sb.WriteString(s)
|
|
||||||
sb.WriteByte(' ')
|
|
||||||
}
|
|
||||||
sb.WriteString(strings.Repeat("-", size))
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
if len(s) > 0 {
|
|
||||||
sb.WriteString(" ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fraction) TypeName() string {
|
|
||||||
return "fraction"
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------- fraction term
|
// -------- fraction term
|
||||||
func newFractionTerm(tk *Token) *term {
|
func newFractionTerm(tk *Token) *term {
|
||||||
return &term{
|
return &term{
|
||||||
@@ -252,168 +55,11 @@ func evalFraction(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
if den == 1 {
|
if den == 1 {
|
||||||
v = num
|
v = num
|
||||||
} else {
|
} else {
|
||||||
v = &fraction{num, den}
|
v = &FractionType{num, den}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func gcd(a, b int64) (g int64) {
|
|
||||||
if a < 0 {
|
|
||||||
a = -a
|
|
||||||
}
|
|
||||||
if b < 0 {
|
|
||||||
b = -b
|
|
||||||
}
|
|
||||||
if a < b {
|
|
||||||
a, b = b, a
|
|
||||||
}
|
|
||||||
r := a % b
|
|
||||||
for r > 0 {
|
|
||||||
a, b = b, r
|
|
||||||
r = a % b
|
|
||||||
}
|
|
||||||
g = b
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func lcm(a, b int64) (l int64) {
|
|
||||||
g := gcd(a, b)
|
|
||||||
l = a * b / g
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func sumFract(f1, f2 *fraction) (sum *fraction) {
|
|
||||||
m := lcm(f1.den, f2.den)
|
|
||||||
sum = newFraction(f1.num*(m/f1.den)+f2.num*(m/f2.den), m)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func mulFract(f1, f2 *fraction) (prod *fraction) {
|
|
||||||
prod = newFraction(f1.num*f2.num, f1.den*f2.den)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func anyToFract(v any) (f *fraction, err error) {
|
|
||||||
var ok bool
|
|
||||||
if f, ok = v.(*fraction); !ok {
|
|
||||||
if n, ok := v.(int64); ok {
|
|
||||||
f = intToFraction(n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if f == nil {
|
|
||||||
err = errExpectedGot("fract", typeFraction, v)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func anyPairToFract(v1, v2 any) (f1, f2 *fraction, err error) {
|
|
||||||
if f1, err = anyToFract(v1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if f2, err = anyToFract(v2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func sumAnyFract(af1, af2 any) (sum any, err error) {
|
|
||||||
var f1, f2 *fraction
|
|
||||||
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f := sumFract(f1, f2)
|
|
||||||
if f.num == 0 {
|
|
||||||
sum = 0
|
|
||||||
} else {
|
|
||||||
sum = simplifyFraction(f)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func subAnyFract(af1, af2 any) (sum any, err error) {
|
|
||||||
var f1, f2 *fraction
|
|
||||||
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f2.num = -f2.num
|
|
||||||
f := sumFract(f1, f2)
|
|
||||||
if f.num == 0 {
|
|
||||||
sum = 0
|
|
||||||
} else {
|
|
||||||
sum = simplifyFraction(f)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func mulAnyFract(af1, af2 any) (prod any, err error) {
|
|
||||||
var f1, f2 *fraction
|
|
||||||
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if f1.num == 0 || f2.num == 0 {
|
|
||||||
prod = 0
|
|
||||||
} else {
|
|
||||||
f := &fraction{f1.num * f2.num, f1.den * f2.den}
|
|
||||||
prod = simplifyFraction(f)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func divAnyFract(af1, af2 any) (quot any, err error) {
|
|
||||||
var f1, f2 *fraction
|
|
||||||
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if f2.num == 0 {
|
|
||||||
err = errors.New("division by zero")
|
|
||||||
return
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if f1.num == 0 || f2.den == 0 {
|
|
||||||
quot = 0
|
|
||||||
} else {
|
|
||||||
f := &fraction{f1.num * f2.den, f1.den * f2.num}
|
|
||||||
quot = simplifyFraction(f)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func simplifyFraction(f *fraction) (v any) {
|
|
||||||
f.num, f.den = simplifyIntegers(f.num, f.den)
|
|
||||||
if f.den == 1 {
|
|
||||||
v = f.num
|
|
||||||
} else {
|
|
||||||
v = &fraction{f.num, f.den}
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func simplifyIntegers(num, den int64) (a, b int64) {
|
|
||||||
if num == 0 {
|
|
||||||
return 0, 1
|
|
||||||
}
|
|
||||||
if den == 0 {
|
|
||||||
panic("fraction with denominator == 0")
|
|
||||||
}
|
|
||||||
if den < 0 {
|
|
||||||
den = -den
|
|
||||||
num = -num
|
|
||||||
}
|
|
||||||
g := gcd(num, den)
|
|
||||||
a = num / g
|
|
||||||
b = den / g
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func intToFraction(n int64) *fraction {
|
|
||||||
return &fraction{n, 1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isFraction(v any) (ok bool) {
|
|
||||||
_, ok = v.(*fraction)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymVertBar, newFractionTerm)
|
registerTermConstructor(SymVertBar, newFractionTerm)
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ func evalIn(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
|
|
||||||
if IsList(rightValue) {
|
if IsList(rightValue) {
|
||||||
list, _ := rightValue.(*ListType)
|
list, _ := rightValue.(*ListType)
|
||||||
v = list.indexDeepCmp(leftValue) >= 0
|
v = list.indexDeepSameCmp(leftValue) >= 0
|
||||||
} else if IsDict(rightValue) {
|
} else if IsDict(rightValue) {
|
||||||
dict, _ := rightValue.(*DictType)
|
dict, _ := rightValue.(*DictType)
|
||||||
v = dict.hasKey(leftValue)
|
v = dict.hasKey(leftValue)
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-index.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
// -------- index term
|
||||||
|
func newIndexTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priDot,
|
||||||
|
evalFunc: evalIndex,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyKey(indexTerm *term, indexList *ListType) (index any, err error) {
|
||||||
|
index = (*indexList)[0]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int, err error) {
|
||||||
|
var v int
|
||||||
|
|
||||||
|
if v, err = toInt((*indexList)[0], "index expression"); err == nil {
|
||||||
|
if v < 0 && v >= -maxValue {
|
||||||
|
v = maxValue + v
|
||||||
|
}
|
||||||
|
if v >= 0 && v < maxValue {
|
||||||
|
index = v
|
||||||
|
} else {
|
||||||
|
err = indexTerm.Errorf("index %d out of bounds", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyRange(indexTerm *term, indexList *ListType, maxValue int) (startIndex, endIndex int, err error) {
|
||||||
|
v, _ := ((*indexList)[0]).(*intPair)
|
||||||
|
startIndex = v.a
|
||||||
|
endIndex = v.b
|
||||||
|
if startIndex < 0 && startIndex >= -maxValue {
|
||||||
|
startIndex = maxValue + startIndex
|
||||||
|
}
|
||||||
|
if endIndex < 0 && endIndex >= -maxValue {
|
||||||
|
endIndex = maxValue + endIndex + 1
|
||||||
|
}
|
||||||
|
if startIndex < 0 || startIndex > maxValue {
|
||||||
|
err = indexTerm.Errorf("range start-index %d is out of bounds", startIndex)
|
||||||
|
} else if endIndex < 0 || endIndex > maxValue {
|
||||||
|
err = indexTerm.Errorf("range end-index %d is out of bounds", endIndex)
|
||||||
|
} else if startIndex > endIndex {
|
||||||
|
err = indexTerm.Errorf("range start-index %d must not be greater than end-index %d", startIndex, endIndex)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalIndex(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
var indexList *ListType
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
indexTerm := self.children[1]
|
||||||
|
if indexList, ok = rightValue.(*ListType); !ok {
|
||||||
|
err = self.Errorf("invalid index expression")
|
||||||
|
return
|
||||||
|
} else if len(*indexList) != 1 {
|
||||||
|
err = indexTerm.Errorf("one index only is allowed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsInteger((*indexList)[0]) {
|
||||||
|
switch unboxedValue := leftValue.(type) {
|
||||||
|
case *ListType:
|
||||||
|
var index int
|
||||||
|
if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil {
|
||||||
|
v = (*unboxedValue)[index]
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
var index int
|
||||||
|
if index, err = verifyIndex(indexTerm, indexList, len(unboxedValue)); err == nil {
|
||||||
|
v = string(unboxedValue[index])
|
||||||
|
}
|
||||||
|
case *DictType:
|
||||||
|
var ok bool
|
||||||
|
var indexValue any
|
||||||
|
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
|
||||||
|
if v, ok = (*unboxedValue)[indexValue]; !ok {
|
||||||
|
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
|
} else if isIntPair((*indexList)[0]) {
|
||||||
|
switch unboxedValue := leftValue.(type) {
|
||||||
|
case *ListType:
|
||||||
|
var start, end int
|
||||||
|
if start, end, err = verifyRange(indexTerm, indexList, len(*unboxedValue)); err == nil {
|
||||||
|
sublist := ListType((*unboxedValue)[start:end])
|
||||||
|
v = &sublist
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
var start, end int
|
||||||
|
if start, end, err = verifyRange(indexTerm, indexList, len(unboxedValue)); err == nil {
|
||||||
|
v = unboxedValue[start:end]
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
registerTermConstructor(SymIndex, newIndexTerm)
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-range.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// -------- range term
|
||||||
|
type intPair struct {
|
||||||
|
a, b int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *intPair) TypeName() string {
|
||||||
|
return typePair
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *intPair) ToString(opt FmtOpt) string {
|
||||||
|
return fmt.Sprintf("(%d, %d)", p.a, p.b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIntPair(v any) bool {
|
||||||
|
_, ok := v.(*intPair)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRangeTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priDot,
|
||||||
|
evalFunc: evalRange,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalRange(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
// if err = self.checkOperands(); err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
if len(self.children) == 0 {
|
||||||
|
leftValue = int64(0)
|
||||||
|
rightValue = int64(-1)
|
||||||
|
} else if len(self.children) == 1 {
|
||||||
|
if leftValue, err = self.children[0].compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rightValue = int64(-1)
|
||||||
|
} else if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !(IsInteger(leftValue) && IsInteger(rightValue)) {
|
||||||
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
startIndex, _ := leftValue.(int64)
|
||||||
|
endIndex, _ := rightValue.(int64)
|
||||||
|
|
||||||
|
v = &intPair{int(startIndex), int(endIndex)}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
registerTermConstructor(SymColon, newRangeTerm)
|
||||||
|
}
|
||||||
+94
-92
@@ -4,13 +4,13 @@
|
|||||||
// operator-rel.go
|
// operator-rel.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
//-------- equal term
|
//-------- equal term
|
||||||
|
|
||||||
func newEqualTerm(tk *Token) (inst *term) {
|
func newEqualTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
// class: classOperator,
|
|
||||||
// kind: kindBool,
|
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priRelational,
|
priority: priRelational,
|
||||||
@@ -18,6 +18,33 @@ func newEqualTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type deepFuncTemplate func(a, b any) (eq bool, err error)
|
||||||
|
|
||||||
|
func equals(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) {
|
||||||
|
if isNumOrFract(a) && isNumOrFract(b) {
|
||||||
|
if IsNumber(a) && IsNumber(b) {
|
||||||
|
if IsInteger(a) && IsInteger(b) {
|
||||||
|
li, _ := a.(int64)
|
||||||
|
ri, _ := b.(int64)
|
||||||
|
eq = li == ri
|
||||||
|
} else {
|
||||||
|
eq = numAsFloat(a) == numAsFloat(b)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var cmp int
|
||||||
|
if cmp, err = cmpAnyFract(a, b); err == nil {
|
||||||
|
eq = cmp == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if deepCmp != nil && IsList(a) && IsList(b) {
|
||||||
|
eq, err = deepCmp(a, b)
|
||||||
|
} else {
|
||||||
|
eq = reflect.DeepEqual(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func evalEqual(ctx ExprContext, self *term) (v any, err error) {
|
func evalEqual(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
@@ -25,21 +52,7 @@ func evalEqual(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if IsNumber(leftValue) && IsNumber(rightValue) {
|
v, err = equals(leftValue, rightValue, nil)
|
||||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
|
||||||
li, _ := leftValue.(int64)
|
|
||||||
ri, _ := rightValue.(int64)
|
|
||||||
v = li == ri
|
|
||||||
} else {
|
|
||||||
v = numAsFloat(leftValue) == numAsFloat(rightValue)
|
|
||||||
}
|
|
||||||
} else if IsString(leftValue) && IsString(rightValue) {
|
|
||||||
ls, _ := leftValue.(string)
|
|
||||||
rs, _ := rightValue.(string)
|
|
||||||
v = ls == rs
|
|
||||||
} else {
|
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,9 +60,7 @@ func evalEqual(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
|
|
||||||
func newNotEqualTerm(tk *Token) (inst *term) {
|
func newNotEqualTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
// class: classOperator,
|
|
||||||
// kind: kindBool,
|
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priRelational,
|
priority: priRelational,
|
||||||
@@ -65,38 +76,11 @@ func evalNotEqual(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// func evalNotEqual(ctx exprContext, self *term) (v any, err error) {
|
|
||||||
// var leftValue, rightValue any
|
|
||||||
|
|
||||||
// if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if isNumber(leftValue) && isNumber(rightValue) {
|
|
||||||
// if isInteger(leftValue) && isInteger(rightValue) {
|
|
||||||
// li, _ := leftValue.(int64)
|
|
||||||
// ri, _ := rightValue.(int64)
|
|
||||||
// v = li != ri
|
|
||||||
// } else {
|
|
||||||
// v = numAsFloat(leftValue) != numAsFloat(rightValue)
|
|
||||||
// }
|
|
||||||
// } else if isString(leftValue) && isString(rightValue) {
|
|
||||||
// ls, _ := leftValue.(string)
|
|
||||||
// rs, _ := rightValue.(string)
|
|
||||||
// v = ls != rs
|
|
||||||
// } else {
|
|
||||||
// err = self.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
//-------- less term
|
//-------- less term
|
||||||
|
|
||||||
func newLessTerm(tk *Token) (inst *term) {
|
func newLessTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
// class: classOperator,
|
|
||||||
// kind: kindBool,
|
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priRelational,
|
priority: priRelational,
|
||||||
@@ -104,28 +88,44 @@ func newLessTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lessThan(self *term, a, b any) (isLess bool, err error) {
|
||||||
|
if isNumOrFract(a) && isNumOrFract(b) {
|
||||||
|
if IsNumber(a) && IsNumber(b) {
|
||||||
|
if IsInteger(a) && IsInteger(b) {
|
||||||
|
li, _ := a.(int64)
|
||||||
|
ri, _ := b.(int64)
|
||||||
|
isLess = li < ri
|
||||||
|
} else {
|
||||||
|
isLess = numAsFloat(a) < numAsFloat(b)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var cmp int
|
||||||
|
if cmp, err = cmpAnyFract(a, b); err == nil {
|
||||||
|
isLess = cmp < 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if IsString(a) && IsString(b) {
|
||||||
|
ls, _ := a.(string)
|
||||||
|
rs, _ := b.(string)
|
||||||
|
isLess = ls < rs
|
||||||
|
// Inclusion test
|
||||||
|
} else if IsList(a) && IsList(b) {
|
||||||
|
aList, _ := a.(*ListType)
|
||||||
|
bList, _ := b.(*ListType)
|
||||||
|
isLess = len(*aList) < len(*bList) && bList.contains(aList)
|
||||||
|
} else {
|
||||||
|
err = self.errIncompatibleTypes(a, b)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func evalLess(ctx ExprContext, self *term) (v any, err error) {
|
func evalLess(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
v, err = lessThan(self, leftValue, rightValue)
|
||||||
if IsNumber(leftValue) && IsNumber(rightValue) {
|
|
||||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
|
||||||
li, _ := leftValue.(int64)
|
|
||||||
ri, _ := rightValue.(int64)
|
|
||||||
v = li < ri
|
|
||||||
} else {
|
|
||||||
v = numAsFloat(leftValue) < numAsFloat(rightValue)
|
|
||||||
}
|
|
||||||
} else if IsString(leftValue) && IsString(rightValue) {
|
|
||||||
ls, _ := leftValue.(string)
|
|
||||||
rs, _ := rightValue.(string)
|
|
||||||
v = ls < rs
|
|
||||||
} else {
|
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,6 +141,19 @@ func newLessEqualTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lessThanOrEqual(self *term, a, b any) (isLessEq bool, err error) {
|
||||||
|
if isLessEq, err = lessThan(self, a, b); err == nil {
|
||||||
|
if !isLessEq {
|
||||||
|
if IsList(a) && IsList(b) {
|
||||||
|
isLessEq, err = sameContent(a, b)
|
||||||
|
} else {
|
||||||
|
isLessEq, err = equals(a, b, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func evalLessEqual(ctx ExprContext, self *term) (v any, err error) {
|
func evalLessEqual(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
@@ -148,21 +161,8 @@ func evalLessEqual(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if IsNumber(leftValue) && IsNumber(rightValue) {
|
v, err = lessThanOrEqual(self, leftValue, rightValue)
|
||||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
|
||||||
li, _ := leftValue.(int64)
|
|
||||||
ri, _ := rightValue.(int64)
|
|
||||||
v = li <= ri
|
|
||||||
} else {
|
|
||||||
v = numAsFloat(leftValue) <= numAsFloat(rightValue)
|
|
||||||
}
|
|
||||||
} else if IsString(leftValue) && IsString(rightValue) {
|
|
||||||
ls, _ := leftValue.(string)
|
|
||||||
rs, _ := rightValue.(string)
|
|
||||||
v = ls <= rs
|
|
||||||
} else {
|
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,9 +170,7 @@ func evalLessEqual(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
|
|
||||||
func newGreaterTerm(tk *Token) (inst *term) {
|
func newGreaterTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
// class: classOperator,
|
|
||||||
// kind: kindBool,
|
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priRelational,
|
priority: priRelational,
|
||||||
@@ -181,10 +179,13 @@ func newGreaterTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func evalGreater(ctx ExprContext, self *term) (v any, err error) {
|
func evalGreater(ctx ExprContext, self *term) (v any, err error) {
|
||||||
if v, err = evalLessEqual(ctx, self); err == nil {
|
var leftValue, rightValue any
|
||||||
b, _ := toBool(v)
|
|
||||||
v = !b
|
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v, err = lessThan(self, rightValue, leftValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,9 +193,7 @@ func evalGreater(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
|
|
||||||
func newGreaterEqualTerm(tk *Token) (inst *term) {
|
func newGreaterEqualTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
// class: classOperator,
|
|
||||||
// kind: kindBool,
|
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priRelational,
|
priority: priRelational,
|
||||||
@@ -203,10 +202,13 @@ func newGreaterEqualTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func evalGreaterEqual(ctx ExprContext, self *term) (v any, err error) {
|
func evalGreaterEqual(ctx ExprContext, self *term) (v any, err error) {
|
||||||
if v, err = evalLess(ctx, self); err == nil {
|
var leftValue, rightValue any
|
||||||
b, _ := toBool(v)
|
|
||||||
v = !b
|
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v, err = lessThanOrEqual(self, rightValue, leftValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,23 +38,6 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
rightInt, _ := rightValue.(int64)
|
rightInt, _ := rightValue.(int64)
|
||||||
v = leftInt + rightInt
|
v = leftInt + rightInt
|
||||||
}
|
}
|
||||||
// } else if IsList(leftValue) || IsList(rightValue) {
|
|
||||||
// var leftList, rightList *ListType
|
|
||||||
// var ok bool
|
|
||||||
// if leftList, ok = leftValue.(*ListType); !ok {
|
|
||||||
// leftList = &ListType{leftValue}
|
|
||||||
// }
|
|
||||||
// if rightList, ok = rightValue.(*ListType); !ok {
|
|
||||||
// rightList = &ListType{rightValue}
|
|
||||||
// }
|
|
||||||
// sumList := make(ListType, 0, len(*leftList)+len(*rightList))
|
|
||||||
// for _, item := range *leftList {
|
|
||||||
// sumList = append(sumList, item)
|
|
||||||
// }
|
|
||||||
// for _, item := range *rightList {
|
|
||||||
// sumList = append(sumList, item)
|
|
||||||
// }
|
|
||||||
// v = &sumList
|
|
||||||
} else if IsList(leftValue) && IsList(rightValue) {
|
} else if IsList(leftValue) && IsList(rightValue) {
|
||||||
var leftList, rightList *ListType
|
var leftList, rightList *ListType
|
||||||
leftList, _ = leftValue.(*ListType)
|
leftList, _ = leftValue.(*ListType)
|
||||||
|
|||||||
@@ -154,15 +154,34 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
func (self *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) {
|
||||||
|
r, c := scanner.lastPos()
|
||||||
args := make([]*term, 0)
|
args := make([]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
for lastSym != SymClosedSquare && lastSym != SymEos {
|
for lastSym != SymClosedSquare && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
|
zeroRequired := scanner.current.Sym == SymColon
|
||||||
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
|
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
|
||||||
if subTree.root != nil {
|
root := subTree.root
|
||||||
args = append(args, subTree.root)
|
if root != nil {
|
||||||
|
if !parsingIndeces && root.symbol() == SymColon {
|
||||||
|
err = root.Errorf("unexpected range expression")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
args = append(args, root)
|
||||||
|
if parsingIndeces && root.symbol() == SymColon && zeroRequired { //len(root.children) == 0 {
|
||||||
|
if len(root.children) == 1 {
|
||||||
|
root.children = append(root.children, root.children[0])
|
||||||
|
} else if len(root.children) > 1 {
|
||||||
|
err = root.Errorf("invalid range specification")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0))
|
||||||
|
zeroTerm := newTerm(zeroTk)
|
||||||
|
zeroTerm.setParent(root)
|
||||||
|
root.children[0] = zeroTerm
|
||||||
|
}
|
||||||
} else if itemExpected {
|
} else if itemExpected {
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
err = prev.ErrorExpectedGot("list-item")
|
err = prev.ErrorExpectedGot("list-item")
|
||||||
@@ -175,11 +194,10 @@ func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term
|
|||||||
itemExpected = lastSym == SymComma
|
itemExpected = lastSym == SymComma
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO Check arguments
|
|
||||||
if lastSym != SymClosedSquare {
|
if lastSym != SymClosedSquare {
|
||||||
err = scanner.Previous().ErrorExpectedGot("]")
|
err = scanner.Previous().ErrorExpectedGot("]")
|
||||||
} else {
|
} else {
|
||||||
subtree = newListTerm(args)
|
subtree = newListTerm(r, c, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -294,14 +312,14 @@ func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaul
|
|||||||
err = tk.Errorf("case list in default clause")
|
err = tk.Errorf("case list in default clause")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if filterList, err = self.parseList(scanner, allowVarRef); err != nil {
|
if filterList, err = self.parseList(scanner, false, allowVarRef); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tk = scanner.Next()
|
tk = scanner.Next()
|
||||||
startRow = tk.row
|
startRow = tk.row
|
||||||
startCol = tk.col
|
startCol = tk.col
|
||||||
} else if !defaultCase {
|
} else if !defaultCase {
|
||||||
filterList = newListTerm(make([]*term, 0))
|
filterList = newListTerm(startRow, startCol, make([]*term, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
if tk.Sym == SymOpenBrace {
|
if tk.Sym == SymOpenBrace {
|
||||||
@@ -352,6 +370,14 @@ func (self *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, e
|
|||||||
return self.parseGeneral(scanner, true, false, termSymbols...)
|
return self.parseGeneral(scanner, true, false, termSymbols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func couldBeACollection(t *term) bool {
|
||||||
|
var sym = SymUnknown
|
||||||
|
if t != nil {
|
||||||
|
sym = t.symbol()
|
||||||
|
}
|
||||||
|
return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable
|
||||||
|
}
|
||||||
|
|
||||||
func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
var selectorTerm *term = nil
|
var selectorTerm *term = nil
|
||||||
var currentTerm *term = nil
|
var currentTerm *term = nil
|
||||||
@@ -401,15 +427,28 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
|
|||||||
}
|
}
|
||||||
case SymOpenSquare:
|
case SymOpenSquare:
|
||||||
var listTerm *term
|
var listTerm *term
|
||||||
if listTerm, err = self.parseList(scanner, allowVarRef); err == nil {
|
parsingIndeces := couldBeACollection(currentTerm)
|
||||||
err = tree.addTerm(listTerm)
|
if listTerm, err = self.parseList(scanner, parsingIndeces, allowVarRef); err == nil {
|
||||||
|
if parsingIndeces {
|
||||||
|
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
|
currentTerm = listTerm
|
||||||
}
|
}
|
||||||
case SymOpenBrace:
|
case SymOpenBrace:
|
||||||
var mapTerm *term
|
if currentTerm != nil && currentTerm.symbol() == SymColon {
|
||||||
if mapTerm, err = self.parseDictionary(scanner, allowVarRef); err == nil {
|
err = currentTerm.Errorf(`selector-case outside of a selector context`)
|
||||||
err = tree.addTerm(mapTerm)
|
} else {
|
||||||
currentTerm = mapTerm
|
var mapTerm *term
|
||||||
|
if mapTerm, err = self.parseDictionary(scanner, allowVarRef); err == nil {
|
||||||
|
err = tree.addTerm(mapTerm)
|
||||||
|
currentTerm = mapTerm
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case SymEqual:
|
case SymEqual:
|
||||||
if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
||||||
@@ -439,14 +478,16 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
|
|||||||
}
|
}
|
||||||
case SymColon, SymDoubleColon:
|
case SymColon, SymDoubleColon:
|
||||||
var caseTerm *term
|
var caseTerm *term
|
||||||
if selectorTerm == nil {
|
if selectorTerm != nil {
|
||||||
err = tk.Errorf("selector-case outside of a selector context")
|
if caseTerm, err = self.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
|
||||||
} else if caseTerm, err = self.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
|
addSelectorCase(selectorTerm, caseTerm)
|
||||||
addSelectorCase(selectorTerm, caseTerm)
|
currentTerm = caseTerm
|
||||||
currentTerm = caseTerm
|
if tk.Sym == SymDoubleColon {
|
||||||
if tk.Sym == SymDoubleColon {
|
selectorTerm = nil
|
||||||
selectorTerm = nil
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
currentTerm, err = tree.addToken2(tk)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
currentTerm, err = tree.addToken2(tk)
|
currentTerm, err = tree.addToken2(tk)
|
||||||
|
|||||||
@@ -74,6 +74,14 @@ func (self *scanner) unreadChar() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *scanner) lastPos() (r, c int) {
|
||||||
|
if self.prev != nil {
|
||||||
|
r = self.prev.row
|
||||||
|
c = self.prev.col
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (self *scanner) Previous() *Token {
|
func (self *scanner) Previous() *Token {
|
||||||
return self.prev
|
return self.prev
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,142 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// simple-func-store.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SimpleFuncStore struct {
|
|
||||||
SimpleVarStore
|
|
||||||
funcStore map[string]*funcInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
type funcInfo struct {
|
|
||||||
name string
|
|
||||||
minArgs int
|
|
||||||
maxArgs int
|
|
||||||
functor Functor
|
|
||||||
}
|
|
||||||
|
|
||||||
func (info *funcInfo) ToString(opt FmtOpt) string {
|
|
||||||
var sb strings.Builder
|
|
||||||
var i int
|
|
||||||
sb.WriteString("func(")
|
|
||||||
for i = 0; i < info.minArgs; i++ {
|
|
||||||
if i > 0 {
|
|
||||||
sb.WriteString(", ")
|
|
||||||
}
|
|
||||||
sb.WriteString(fmt.Sprintf("arg%d", i+1))
|
|
||||||
}
|
|
||||||
for ; i < info.maxArgs; i++ {
|
|
||||||
sb.WriteString(fmt.Sprintf("arg%d", i+1))
|
|
||||||
}
|
|
||||||
if info.maxArgs < 0 {
|
|
||||||
if info.minArgs > 0 {
|
|
||||||
sb.WriteString(", ")
|
|
||||||
}
|
|
||||||
sb.WriteString("...")
|
|
||||||
}
|
|
||||||
sb.WriteString(") {...}")
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (info *funcInfo) Name() string {
|
|
||||||
return info.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (info *funcInfo) MinArgs() int {
|
|
||||||
return info.minArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (info *funcInfo) MaxArgs() int {
|
|
||||||
return info.maxArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (info *funcInfo) Functor() Functor {
|
|
||||||
return info.functor
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSimpleFuncStore() *SimpleFuncStore {
|
|
||||||
ctx := &SimpleFuncStore{
|
|
||||||
SimpleVarStore: SimpleVarStore{varStore: make(map[string]any)},
|
|
||||||
funcStore: make(map[string]*funcInfo),
|
|
||||||
}
|
|
||||||
ImportBuiltinsFuncs(ctx)
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *SimpleFuncStore) Clone() ExprContext {
|
|
||||||
svs := ctx.SimpleVarStore
|
|
||||||
return &SimpleFuncStore{
|
|
||||||
// SimpleVarStore: SimpleVarStore{varStore: CloneMap(ctx.varStore)},
|
|
||||||
SimpleVarStore: SimpleVarStore{varStore: svs.cloneVars()},
|
|
||||||
funcStore: CloneFilteredMap(ctx.funcStore, func(name string) bool { return name[0] != '@' }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func funcsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
|
|
||||||
sb.WriteString("funcs: {\n")
|
|
||||||
first := true
|
|
||||||
for _, name := range ctx.EnumFuncs(func(name string) bool { return true }) {
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
} else {
|
|
||||||
sb.WriteByte(',')
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
}
|
|
||||||
value, _ := ctx.GetFuncInfo(name)
|
|
||||||
sb.WriteString(strings.Repeat("\t", indent+1))
|
|
||||||
sb.WriteString(name)
|
|
||||||
sb.WriteString("=")
|
|
||||||
if formatter, ok := value.(Formatter); ok {
|
|
||||||
sb.WriteString(formatter.ToString(0))
|
|
||||||
} else {
|
|
||||||
sb.WriteString(fmt.Sprintf("%v", value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.WriteString("\n}\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *SimpleFuncStore) ToString(opt FmtOpt) string {
|
|
||||||
var sb strings.Builder
|
|
||||||
sb.WriteString(ctx.SimpleVarStore.ToString(opt))
|
|
||||||
funcsCtxToBuilder(&sb, ctx, 0)
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *SimpleFuncStore) GetFuncInfo(name string) (info ExprFunc, exists bool) {
|
|
||||||
info, exists = ctx.funcStore[name]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *SimpleFuncStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) {
|
|
||||||
ctx.funcStore[name] = &funcInfo{name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *SimpleFuncStore) EnumFuncs(acceptor func(name string) (accept bool)) (funcNames []string) {
|
|
||||||
funcNames = make([]string, 0)
|
|
||||||
for name := range ctx.funcStore {
|
|
||||||
if acceptor != nil {
|
|
||||||
if acceptor(name) {
|
|
||||||
funcNames = append(funcNames, name)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
funcNames = append(funcNames, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *SimpleFuncStore) Call(name string, args []any) (result any, err error) {
|
|
||||||
if info, exists := ctx.funcStore[name]; exists {
|
|
||||||
functor := info.functor
|
|
||||||
result, err = functor.Invoke(ctx, name, args)
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("unknown function %s()", name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// simple-func-store.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SimpleFuncStore struct {
|
||||||
|
SimpleVarStore
|
||||||
|
funcStore map[string]*funcInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type paramFlags uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
pfOptional paramFlags = 1 << iota
|
||||||
|
pfRepeat
|
||||||
|
)
|
||||||
|
|
||||||
|
type funcParamInfo struct {
|
||||||
|
name string
|
||||||
|
flags paramFlags
|
||||||
|
defaultValue any
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFuncParam(name string) *funcParamInfo {
|
||||||
|
return &funcParamInfo{name: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFuncParamFlag(name string, flags paramFlags) *funcParamInfo {
|
||||||
|
return &funcParamInfo{name: name, flags: flags}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFuncParamFlagDef(name string, flags paramFlags, defValue any) *funcParamInfo {
|
||||||
|
return &funcParamInfo{name: name, flags: flags, defaultValue: defValue}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (param *funcParamInfo) Name() string {
|
||||||
|
return param.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (param *funcParamInfo) Type() string {
|
||||||
|
return "any"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (param *funcParamInfo) IsOptional() bool {
|
||||||
|
return (param.flags & pfOptional) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (param *funcParamInfo) IsRepeat() bool {
|
||||||
|
return (param.flags & pfRepeat) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (param *funcParamInfo) DefaultValue() any {
|
||||||
|
return param.defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
type funcInfo struct {
|
||||||
|
name string
|
||||||
|
minArgs int
|
||||||
|
maxArgs int
|
||||||
|
functor Functor
|
||||||
|
params []ExprFuncParam
|
||||||
|
returnType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *funcInfo) Params() []ExprFuncParam {
|
||||||
|
return info.params
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *funcInfo) ReturnType() string {
|
||||||
|
return info.returnType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *funcInfo) ToString(opt FmtOpt) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
sb.WriteByte('(')
|
||||||
|
if info.params != nil {
|
||||||
|
for i, p := range info.params {
|
||||||
|
if i > 0 {
|
||||||
|
sb.WriteString(", ")
|
||||||
|
}
|
||||||
|
sb.WriteString(p.Name())
|
||||||
|
if p.IsOptional() {
|
||||||
|
sb.WriteByte('=')
|
||||||
|
if s, ok := p.DefaultValue().(string); ok {
|
||||||
|
sb.WriteByte('"')
|
||||||
|
sb.WriteString(s)
|
||||||
|
sb.WriteByte('"')
|
||||||
|
} else {
|
||||||
|
sb.WriteString(fmt.Sprintf("%v", p.DefaultValue()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if info.maxArgs < 0 {
|
||||||
|
sb.WriteString(" ...")
|
||||||
|
}
|
||||||
|
sb.WriteString(") -> ")
|
||||||
|
if len(info.returnType) > 0 {
|
||||||
|
sb.WriteString(info.returnType)
|
||||||
|
} else {
|
||||||
|
sb.WriteString(typeAny)
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *funcInfo) Name() string {
|
||||||
|
return info.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *funcInfo) MinArgs() int {
|
||||||
|
return info.minArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *funcInfo) MaxArgs() int {
|
||||||
|
return info.maxArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *funcInfo) Functor() Functor {
|
||||||
|
return info.functor
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSimpleFuncStore() *SimpleFuncStore {
|
||||||
|
ctx := &SimpleFuncStore{
|
||||||
|
SimpleVarStore: SimpleVarStore{varStore: make(map[string]any)},
|
||||||
|
funcStore: make(map[string]*funcInfo),
|
||||||
|
}
|
||||||
|
//ImportBuiltinsFuncs(ctx)
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleFuncStore) Clone() ExprContext {
|
||||||
|
svs := ctx.SimpleVarStore
|
||||||
|
return &SimpleFuncStore{
|
||||||
|
// SimpleVarStore: SimpleVarStore{varStore: CloneMap(ctx.varStore)},
|
||||||
|
SimpleVarStore: SimpleVarStore{varStore: svs.cloneVars()},
|
||||||
|
funcStore: CloneFilteredMap(ctx.funcStore, func(name string) bool { return name[0] != '@' }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func funcsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
|
||||||
|
sb.WriteString("funcs: {\n")
|
||||||
|
first := true
|
||||||
|
names := ctx.EnumFuncs(func(name string) bool { return true })
|
||||||
|
slices.Sort(names)
|
||||||
|
for _, name := range names {
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
sb.WriteByte(',')
|
||||||
|
sb.WriteByte('\n')
|
||||||
|
}
|
||||||
|
value, _ := ctx.GetFuncInfo(name)
|
||||||
|
sb.WriteString(strings.Repeat("\t", indent+1))
|
||||||
|
sb.WriteString(name)
|
||||||
|
//sb.WriteString("=")
|
||||||
|
if formatter, ok := value.(Formatter); ok {
|
||||||
|
sb.WriteString(formatter.ToString(0))
|
||||||
|
} else {
|
||||||
|
sb.WriteString(fmt.Sprintf("%v", value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.WriteString("\n}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleFuncStore) ToString(opt FmtOpt) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
sb.WriteString(ctx.SimpleVarStore.ToString(opt))
|
||||||
|
funcsCtxToBuilder(&sb, ctx, 0)
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleFuncStore) GetFuncInfo(name string) (info ExprFunc, exists bool) {
|
||||||
|
info, exists = ctx.funcStore[name]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (ctx *SimpleFuncStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) {
|
||||||
|
// ctx.funcStore[name] = &funcInfo{name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor}
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (ctx *SimpleFuncStore) RegisterFuncInfo(info ExprFunc) {
|
||||||
|
ctx.funcStore[info.Name()], _ = info.(*funcInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleFuncStore) RegisterFunc2(name string, functor Functor, returnType string, params []ExprFuncParam) error {
|
||||||
|
var minArgs = 0
|
||||||
|
var maxArgs = 0
|
||||||
|
if params != nil {
|
||||||
|
for _, p := range params {
|
||||||
|
if maxArgs == -1 {
|
||||||
|
return fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
|
||||||
|
// } else if p.IsRepeat() {
|
||||||
|
// maxArgs = -1
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
if p.IsOptional() {
|
||||||
|
maxArgs++
|
||||||
|
} else if maxArgs == minArgs {
|
||||||
|
minArgs++
|
||||||
|
maxArgs++
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name())
|
||||||
|
}
|
||||||
|
if p.IsRepeat() {
|
||||||
|
maxArgs = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.funcStore[name] = &funcInfo{
|
||||||
|
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, params: params,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleFuncStore) EnumFuncs(acceptor func(name string) (accept bool)) (funcNames []string) {
|
||||||
|
funcNames = make([]string, 0)
|
||||||
|
for name := range ctx.funcStore {
|
||||||
|
if acceptor != nil {
|
||||||
|
if acceptor(name) {
|
||||||
|
funcNames = append(funcNames, name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
funcNames = append(funcNames, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleFuncStore) Call(name string, args []any) (result any, err error) {
|
||||||
|
if info, exists := ctx.funcStore[name]; exists {
|
||||||
|
functor := info.functor
|
||||||
|
result, err = functor.Invoke(ctx, name, args)
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("unknown function %s()", name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
+184
@@ -0,0 +1,184 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// simple-store.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SimpleStore struct {
|
||||||
|
varStore map[string]any
|
||||||
|
funcStore map[string]ExprFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSimpleStore() *SimpleStore {
|
||||||
|
ctx := &SimpleStore{
|
||||||
|
varStore: make(map[string]any),
|
||||||
|
funcStore: make(map[string]ExprFunc),
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterRefName(name string) bool { return name[0] != '@' }
|
||||||
|
func filterPrivName(name string) bool { return name[0] != '_' }
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) Clone() ExprContext {
|
||||||
|
return &SimpleStore{
|
||||||
|
varStore: CloneFilteredMap(ctx.varStore, filterRefName),
|
||||||
|
funcStore: CloneFilteredMap(ctx.funcStore, filterRefName),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) Merge(src ExprContext) {
|
||||||
|
for _, name := range src.EnumVars(filterRefName) {
|
||||||
|
ctx.varStore[name], _ = src.GetVar(name)
|
||||||
|
}
|
||||||
|
for _, name := range src.EnumFuncs(filterRefName) {
|
||||||
|
ctx.funcStore[name], _ = src.GetFuncInfo(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
|
||||||
|
sb.WriteString("vars: {\n")
|
||||||
|
first := true
|
||||||
|
for _, name := range ctx.EnumVars(filterPrivName) {
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
sb.WriteByte(',')
|
||||||
|
sb.WriteByte('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
value, _ := ctx.GetVar(name)
|
||||||
|
sb.WriteString(strings.Repeat("\t", indent+1))
|
||||||
|
sb.WriteString(name)
|
||||||
|
sb.WriteString(": ")
|
||||||
|
if f, ok := value.(Formatter); ok {
|
||||||
|
sb.WriteString(f.ToString(0))
|
||||||
|
} else if _, ok = value.(Functor); ok {
|
||||||
|
sb.WriteString("func(){}")
|
||||||
|
// } else if _, ok = value.(map[any]any); ok {
|
||||||
|
// sb.WriteString("dict{}")
|
||||||
|
} else {
|
||||||
|
sb.WriteString(fmt.Sprintf("%v", value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.WriteString(strings.Repeat("\t", indent))
|
||||||
|
sb.WriteString("\n}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func varsCtxToString(ctx ExprContext, indent int) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
varsCtxToBuilder(&sb, ctx, indent)
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func funcsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
|
||||||
|
sb.WriteString("funcs: {\n")
|
||||||
|
first := true
|
||||||
|
names := ctx.EnumFuncs(func(name string) bool { return true })
|
||||||
|
slices.Sort(names)
|
||||||
|
for _, name := range names {
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
sb.WriteByte(',')
|
||||||
|
sb.WriteByte('\n')
|
||||||
|
}
|
||||||
|
value, _ := ctx.GetFuncInfo(name)
|
||||||
|
sb.WriteString(strings.Repeat("\t", indent+1))
|
||||||
|
sb.WriteString(name)
|
||||||
|
//sb.WriteString("=")
|
||||||
|
if formatter, ok := value.(Formatter); ok {
|
||||||
|
sb.WriteString(formatter.ToString(0))
|
||||||
|
} else {
|
||||||
|
sb.WriteString(fmt.Sprintf("%v", value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.WriteString("\n}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) ToString(opt FmtOpt) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
varsCtxToBuilder(&sb, ctx, 0)
|
||||||
|
funcsCtxToBuilder(&sb, ctx, 0)
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) GetVar(varName string) (v any, exists bool) {
|
||||||
|
v, exists = ctx.varStore[varName]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) UnsafeSetVar(varName string, value any) {
|
||||||
|
// fmt.Printf("[%p] setVar(%v, %v)\n", ctx, varName, value)
|
||||||
|
ctx.varStore[varName] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) SetVar(varName string, value any) {
|
||||||
|
// fmt.Printf("[%p] SetVar(%v, %v)\n", ctx, varName, value)
|
||||||
|
if allowedValue, ok := fromGenericAny(value); ok {
|
||||||
|
ctx.varStore[varName] = allowedValue
|
||||||
|
} else {
|
||||||
|
panic(fmt.Errorf("unsupported type %T of value %v", value, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) EnumVars(acceptor func(name string) (accept bool)) (varNames []string) {
|
||||||
|
varNames = make([]string, 0)
|
||||||
|
for name := range ctx.varStore {
|
||||||
|
if acceptor != nil {
|
||||||
|
if acceptor(name) {
|
||||||
|
varNames = append(varNames, name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
varNames = append(varNames, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) GetFuncInfo(name string) (info ExprFunc, exists bool) {
|
||||||
|
info, exists = ctx.funcStore[name]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) RegisterFuncInfo(info ExprFunc) {
|
||||||
|
ctx.funcStore[info.Name()], _ = info.(*funcInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) RegisterFunc(name string, functor Functor, returnType string, params []ExprFuncParam) (err error) {
|
||||||
|
var info *funcInfo
|
||||||
|
if info, err = newFuncInfo(name, functor, returnType, params); err == nil {
|
||||||
|
ctx.funcStore[name] = info
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) EnumFuncs(acceptor func(name string) (accept bool)) (funcNames []string) {
|
||||||
|
funcNames = make([]string, 0)
|
||||||
|
for name := range ctx.funcStore {
|
||||||
|
if acceptor != nil {
|
||||||
|
if acceptor(name) {
|
||||||
|
funcNames = append(funcNames, name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
funcNames = append(funcNames, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) Call(name string, args []any) (result any, err error) {
|
||||||
|
if info, exists := ctx.funcStore[name]; exists {
|
||||||
|
functor := info.Functor()
|
||||||
|
result, err = functor.Invoke(ctx, name, args)
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("unknown function %s()", name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -72,7 +72,12 @@ func (ctx *SimpleVarStore) Call(name string, args []any) (result any, err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *SimpleVarStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) {
|
// func (ctx *SimpleVarStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) {
|
||||||
|
// }
|
||||||
|
func (ctx *SimpleVarStore) RegisterFuncInfo(info ExprFunc) {
|
||||||
|
}
|
||||||
|
func (ctx *SimpleVarStore) RegisterFunc2(name string, f Functor, returnType string, param []ExprFuncParam) error {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *SimpleVarStore) EnumFuncs(acceptor func(name string) (accept bool)) (funcNames []string) {
|
func (ctx *SimpleVarStore) EnumFuncs(acceptor func(name string) (accept bool)) (funcNames []string) {
|
||||||
@@ -85,6 +85,7 @@ const (
|
|||||||
SymFuncDef
|
SymFuncDef
|
||||||
SymList
|
SymList
|
||||||
SymDict
|
SymDict
|
||||||
|
SymIndex
|
||||||
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> "}"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// ast_test.go
|
// t_ast_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// dict_test.go
|
// t_dict_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -24,7 +24,7 @@ func TestDictParser(t *testing.T) {
|
|||||||
/* 1 */ {`{}`, map[any]any{}, nil},
|
/* 1 */ {`{}`, map[any]any{}, nil},
|
||||||
/* 2 */ {`{123}`, nil, errors.New(`[1:6] expected ":", got "}"`)},
|
/* 2 */ {`{123}`, nil, errors.New(`[1:6] expected ":", got "}"`)},
|
||||||
/* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
|
/* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
|
||||||
/* 4 */ {`{1:"one",2:"two",3:"three"}.2`, "three", nil},
|
/* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil},
|
||||||
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
|
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
|
||||||
/* 6 */ {`{1:"one"} + {2:"two"}`, map[any]any{1: "one", 2: "two"}, nil},
|
/* 6 */ {`{1:"one"} + {2:"two"}`, map[any]any{1: "one", 2: "two"}, nil},
|
||||||
/* 7 */ {`2 in {1:"one", 2:"two"}`, true, nil},
|
/* 7 */ {`2 in {1:"one", 2:"two"}`, true, nil},
|
||||||
@@ -42,7 +42,7 @@ func TestDictParser(t *testing.T) {
|
|||||||
var gotResult any
|
var gotResult any
|
||||||
var gotErr error
|
var gotErr error
|
||||||
|
|
||||||
ctx := NewSimpleFuncStore()
|
ctx := NewSimpleStore()
|
||||||
ctx.SetVar("var1", int64(123))
|
ctx.SetVar("var1", int64(123))
|
||||||
ctx.SetVar("var2", "abc")
|
ctx.SetVar("var2", "abc")
|
||||||
ImportMathFuncs(ctx)
|
ImportMathFuncs(ctx)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// expr_test.go
|
// t_expr_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -44,7 +44,7 @@ func TestExpr(t *testing.T) {
|
|||||||
var gotResult any
|
var gotResult any
|
||||||
var gotErr error
|
var gotErr error
|
||||||
|
|
||||||
ctx := NewSimpleFuncStore()
|
ctx := NewSimpleStore()
|
||||||
// ImportMathFuncs(ctx)
|
// ImportMathFuncs(ctx)
|
||||||
// ImportImportFunc(ctx)
|
// ImportImportFunc(ctx)
|
||||||
ImportOsFuncs(ctx)
|
ImportOsFuncs(ctx)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// funcs_test.go
|
// t_funcs_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -76,14 +76,41 @@ func TestFuncs(t *testing.T) {
|
|||||||
/* 63 */ {`dec(true)`, float64(1), nil},
|
/* 63 */ {`dec(true)`, float64(1), nil},
|
||||||
/* 64 */ {`dec(true")`, nil, errors.New("[1:11] missing string termination \"")},
|
/* 64 */ {`dec(true")`, nil, errors.New("[1:11] missing string termination \"")},
|
||||||
/* 65 */ {`builtin "string"; joinStr("-", [1, "two", "three"])`, nil, errors.New(`joinStr() expected string, got int64 (1)`)},
|
/* 65 */ {`builtin "string"; joinStr("-", [1, "two", "three"])`, nil, errors.New(`joinStr() expected string, got int64 (1)`)},
|
||||||
/* 65 */ {`dec()`, nil, errors.New(`too few params -- expected 1, got 0`)},
|
/* 66 */ {`dec()`, nil, errors.New(`too few params -- expected 1, got 0`)},
|
||||||
/* 66 */ {`dec(1,2,3)`, nil, errors.New(`too much params -- expected 1, got 3`)},
|
/* 67 */ {`dec(1,2,3)`, nil, errors.New(`too much params -- expected 1, got 3`)},
|
||||||
/* 67 */ {`builtin "string"; joinStr()`, nil, errors.New(`too few params -- expected 1 or more, got 0`)},
|
/* 68 */ {`builtin "string"; joinStr()`, nil, errors.New(`too few params -- expected 1 or more, got 0`)},
|
||||||
// /* 64 */ {`string(true)`, "true", nil},
|
/* 69 */ /*{`builtin "string"; $$global`, `vars: {
|
||||||
|
|
||||||
|
}
|
||||||
|
funcs: {
|
||||||
|
add(any=0 ...) -> number,
|
||||||
|
dec(any) -> decimal,
|
||||||
|
endsWithStr(source, suffix) -> boolean,
|
||||||
|
fract(any, denominator=1) -> fraction,
|
||||||
|
import( ...) -> any,
|
||||||
|
importAll( ...) -> any,
|
||||||
|
int(any) -> integer,
|
||||||
|
isBool(any) -> boolean,
|
||||||
|
isDec(any) -> boolean,
|
||||||
|
isDict(any) -> boolean,
|
||||||
|
isFloat(any) -> boolean,
|
||||||
|
isFract(any) -> boolean,
|
||||||
|
isInt(any) -> boolean,
|
||||||
|
isList(any) -> boolean,
|
||||||
|
isNil(any) -> boolean,
|
||||||
|
isString(any) -> boolean,
|
||||||
|
joinStr(separator, item="" ...) -> string,
|
||||||
|
mul(any=1 ...) -> number,
|
||||||
|
splitStr(source, separator="", count=-1) -> list of string,
|
||||||
|
startsWithStr(source, prefix) -> boolean,
|
||||||
|
subStr(source, start=0, count=-1) -> string,
|
||||||
|
trimStr(source) -> string
|
||||||
|
}
|
||||||
|
`, nil},*/
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("EXPR_PATH", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
//parserTest(t, "Func", inputs[54:55])
|
// parserTestSpec(t, "Func", inputs, 69)
|
||||||
parserTest(t, "Func", inputs)
|
parserTest(t, "Func", inputs)
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// t_graph_test.go
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// graph_test.go
|
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// helpers_test.go
|
// t_helpers_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -52,7 +52,7 @@ func TestEvalStringA(t *testing.T) {
|
|||||||
|
|
||||||
func TestEvalString(t *testing.T) {
|
func TestEvalString(t *testing.T) {
|
||||||
|
|
||||||
ctx := NewSimpleVarStore()
|
ctx := NewSimpleStore()
|
||||||
ctx.SetVar("a", uint8(1))
|
ctx.SetVar("a", uint8(1))
|
||||||
ctx.SetVar("b", int8(2))
|
ctx.SetVar("b", int8(2))
|
||||||
ctx.SetVar("f", 2.0)
|
ctx.SetVar("f", 2.0)
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_index_test.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCollections(t *testing.T) {
|
||||||
|
section := "Collection"
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`"abcdef"[1:3]`, "bc", nil},
|
||||||
|
/* 2 */ {`"abcdef"[:3]`, "abc", nil},
|
||||||
|
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
||||||
|
/* 4 */ {`"abcdef"[:]`, "abcdef", 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, errors.New(`[1:14] left operand '(1, 2)' [pair] and right operand '3' [integer] are not compatible with operator ":"`)},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
|
// parserTestSpec(t, section, inputs, 5)
|
||||||
|
parserTest(t, section, inputs)
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// iterator_test.go
|
// t_iterator_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// list_test.go
|
// t_list_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -33,16 +33,19 @@ func TestListParser(t *testing.T) {
|
|||||||
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
||||||
/* 12 */ {`[1,2,3] << 2+2`, []any{int64(1), int64(2), int64(3), int64(4)}, nil},
|
/* 12 */ {`[1,2,3] << 2+2`, []any{int64(1), int64(2), int64(3), int64(4)}, nil},
|
||||||
/* 13 */ {`2-1 >> [2,3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
/* 13 */ {`2-1 >> [2,3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
||||||
/* 14 */ {`[1,2,3].1`, int64(2), nil},
|
/* 14 */ {`[1,2,3][1]`, int64(2), nil},
|
||||||
/* 15 */ {`ls=[1,2,3] but ls.1`, int64(2), nil},
|
/* 15 */ {`ls=[1,2,3] but ls[1]`, int64(2), nil},
|
||||||
/* 16 */ {`ls=[1,2,3] but ls.(-1)`, int64(3), nil},
|
/* 16 */ {`ls=[1,2,3] but ls[-1]`, int64(3), nil},
|
||||||
/* 17 */ {`list=["one","two","three"]; list.10`, nil, errors.New(`[1:36] index 10 out of bounds`)},
|
/* 17 */ {`list=["one","two","three"]; list[10]`, nil, errors.New(`[1:34] index 10 out of bounds`)},
|
||||||
/* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil},
|
/* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil},
|
||||||
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
|
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
|
||||||
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
|
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
|
||||||
/* 21 */ {`"b" in ["a", "b", "c"]`, true, nil},
|
/* 21 */ {`"b" in ["a", "b", "c"]`, true, nil},
|
||||||
/* 22 */ {`a=[1,2]; (a)<<3`, []any{1, 2, 3}, nil},
|
/* 22 */ {`a=[1,2]; (a)<<3`, []any{1, 2, 3}, nil},
|
||||||
/* 23 */ {`a=[1,2]; (a)<<3; 1`, []any{1, 2}, nil},
|
/* 23 */ {`a=[1,2]; (a)<<3; 1`, []any{1, 2}, nil},
|
||||||
|
/* 24 */ {`["a","b","c","d"][1]`, "b", nil},
|
||||||
|
/* 25 */ {`["a","b","c","d"][1,1]`, nil, errors.New(`[1:19] one index only is allowed`)},
|
||||||
|
/* 26 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
|
||||||
|
|
||||||
// /* 8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil},
|
// /* 8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil},
|
||||||
// /* 9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil},
|
// /* 9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil},
|
||||||
@@ -60,9 +63,7 @@ func TestListParser(t *testing.T) {
|
|||||||
var gotResult any
|
var gotResult any
|
||||||
var gotErr error
|
var gotErr error
|
||||||
|
|
||||||
ctx := NewSimpleFuncStore()
|
ctx := NewSimpleStore()
|
||||||
// ctx.SetVar("var1", int64(123))
|
|
||||||
// ctx.SetVar("var2", "abc")
|
|
||||||
ImportMathFuncs(ctx)
|
ImportMathFuncs(ctx)
|
||||||
parser := NewParser(ctx)
|
parser := NewParser(ctx)
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// parser_test.go
|
// t_parser_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -163,46 +163,32 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
// parserTestSpec(t, "General", inputs, 102)
|
||||||
parserTest(t, "General", inputs)
|
parserTest(t, "General", inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parserTestSpec(t *testing.T, section string, inputs []inputType, spec ...int) {
|
||||||
|
succeeded := 0
|
||||||
|
failed := 0
|
||||||
|
for _, count := range spec {
|
||||||
|
good := doTest(t, section, &inputs[count-1], count)
|
||||||
|
|
||||||
|
if good {
|
||||||
|
succeeded++
|
||||||
|
} else {
|
||||||
|
failed++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(spec), succeeded, failed)
|
||||||
|
}
|
||||||
|
|
||||||
func parserTest(t *testing.T, section string, inputs []inputType) {
|
func parserTest(t *testing.T, section string, inputs []inputType) {
|
||||||
|
|
||||||
succeeded := 0
|
succeeded := 0
|
||||||
failed := 0
|
failed := 0
|
||||||
|
|
||||||
for i, input := range inputs {
|
for i, input := range inputs {
|
||||||
var expr Expr
|
good := doTest(t, section, &input, i+1)
|
||||||
var gotResult any
|
|
||||||
var gotErr error
|
|
||||||
|
|
||||||
ctx := NewSimpleFuncStore()
|
|
||||||
parser := NewParser(ctx)
|
|
||||||
|
|
||||||
logTest(t, i+1, section, input.source, input.wantResult, input.wantErr)
|
|
||||||
|
|
||||||
r := strings.NewReader(input.source)
|
|
||||||
scanner := NewScanner(r, DefaultTranslations())
|
|
||||||
|
|
||||||
good := true
|
|
||||||
if expr, gotErr = parser.Parse(scanner); gotErr == nil {
|
|
||||||
gotResult, gotErr = expr.Eval(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
eq := reflect.DeepEqual(gotResult, input.wantResult)
|
|
||||||
|
|
||||||
if !eq /*gotResult != input.wantResult*/ {
|
|
||||||
t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
|
|
||||||
good = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if gotErr != input.wantErr {
|
|
||||||
if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) {
|
|
||||||
t.Errorf("%d: %q -> err = <%v>, want <%v>", i+1, input.source, gotErr, input.wantErr)
|
|
||||||
good = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if good {
|
if good {
|
||||||
succeeded++
|
succeeded++
|
||||||
} else {
|
} else {
|
||||||
@@ -212,6 +198,40 @@ func parserTest(t *testing.T, section string, inputs []inputType) {
|
|||||||
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
|
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func doTest(t *testing.T, section string, input *inputType, count int) (good bool) {
|
||||||
|
var expr Expr
|
||||||
|
var gotResult any
|
||||||
|
var gotErr error
|
||||||
|
|
||||||
|
ctx := NewSimpleStore()
|
||||||
|
parser := NewParser(ctx)
|
||||||
|
|
||||||
|
logTest(t, count, section, input.source, input.wantResult, input.wantErr)
|
||||||
|
|
||||||
|
r := strings.NewReader(input.source)
|
||||||
|
scanner := NewScanner(r, DefaultTranslations())
|
||||||
|
|
||||||
|
good = true
|
||||||
|
if expr, gotErr = parser.Parse(scanner); gotErr == nil {
|
||||||
|
gotResult, gotErr = expr.Eval(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
eq := reflect.DeepEqual(gotResult, input.wantResult)
|
||||||
|
|
||||||
|
if !eq /*gotResult != input.wantResult*/ {
|
||||||
|
t.Errorf("%d: %q -> result = %v [%T], want = %v [%T]", count, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
|
||||||
|
good = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if gotErr != input.wantErr {
|
||||||
|
if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) {
|
||||||
|
t.Errorf("%d: %q -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, input.wantErr)
|
||||||
|
good = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) {
|
func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) {
|
||||||
if wantErr == nil {
|
if wantErr == nil {
|
||||||
t.Logf("[+]%s nr %3d -- %q --> %v", section, n, source, wantResult)
|
t.Logf("[+]%s nr %3d -- %q --> %v", section, n, source, wantResult)
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_template_test.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRelational(t *testing.T) {
|
||||||
|
section := "Relational"
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`1 == 3-2`, true, nil},
|
||||||
|
/* 2 */ {`"a" == "a"`, true, nil},
|
||||||
|
/* 3 */ {`true == false`, false, nil},
|
||||||
|
/* 4 */ {`1.0 == 3.0-2`, true, nil},
|
||||||
|
/* 5 */ {`[1,2] == [2,1]`, false, nil},
|
||||||
|
/* 6 */ {`1 != 3-2`, false, nil},
|
||||||
|
/* 7 */ {`"a" != "a"`, false, nil},
|
||||||
|
/* 8 */ {`true != false`, true, nil},
|
||||||
|
/* 9 */ {`1.0 != 3.0-2`, false, nil},
|
||||||
|
/* 10 */ {`[1,2] != [2,1]`, true, nil},
|
||||||
|
/* 11 */ {`1|2 == 1|3`, false, nil},
|
||||||
|
/* 12 */ {`1|2 != 1|3`, true, nil},
|
||||||
|
/* 13 */ {`1|2 == 4|8`, true, nil},
|
||||||
|
/* 14 */ {`1 < 8`, true, nil},
|
||||||
|
/* 15 */ {`1 <= 8`, true, nil},
|
||||||
|
/* 16 */ {`"a" < "b"`, true, nil},
|
||||||
|
/* 17 */ {`"a" <= "b"`, true, nil},
|
||||||
|
/* 18 */ {`1.0 < 8`, true, nil},
|
||||||
|
/* 19 */ {`1.0 <= 8`, true, nil},
|
||||||
|
/* 20 */ {`1.0 <= 1.0`, true, nil},
|
||||||
|
/* 21 */ {`1.0 == 1`, true, nil},
|
||||||
|
/* 22 */ {`1|2 < 1|3`, false, nil},
|
||||||
|
/* 23 */ {`1|2 <= 1|3`, false, nil},
|
||||||
|
/* 24 */ {`1|2 > 1|3`, true, nil},
|
||||||
|
/* 25 */ {`1|2 >= 1|3`, true, nil},
|
||||||
|
/* 26 */ {`[1,2,3] > [2]`, true, nil},
|
||||||
|
/* 27 */ {`[1,2,3] > [9]`, false, nil},
|
||||||
|
/* 28 */ {`[1,2,3] >= [6]`, false, nil},
|
||||||
|
/* 29 */ {`[1,2,3] >= [2,6]`, false, nil},
|
||||||
|
/* 30 */ {`[1,[2,3],4] > [[3,2]]`, true, nil},
|
||||||
|
/* 31 */ {`[1,[2,[3,4]],5] > [[[4,3],2]]`, true, nil},
|
||||||
|
/* 32 */ {`[[4,3],2] IN [1,[2,[3,4]],5]`, true, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
|
// parserTestSpec(t, section, inputs, 31)
|
||||||
|
parserTest(t, section, inputs)
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// scanner_test.go
|
// t_scanner_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// strings_test.go
|
// t_strings_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -14,8 +14,8 @@ func TestStringsParser(t *testing.T) {
|
|||||||
/* 2 */ {`"uno" + 2`, `uno2`, nil},
|
/* 2 */ {`"uno" + 2`, `uno2`, nil},
|
||||||
/* 3 */ {`"uno" + (2+1)`, `uno3`, nil},
|
/* 3 */ {`"uno" + (2+1)`, `uno3`, nil},
|
||||||
/* 4 */ {`"uno" * (2+1)`, `unounouno`, nil},
|
/* 4 */ {`"uno" * (2+1)`, `unounouno`, nil},
|
||||||
/* 5 */ {`"abc".1`, `b`, nil},
|
/* 5 */ {`"abc"[1]`, `b`, nil},
|
||||||
/* 5 */ {`#"abc"`, int64(3), nil},
|
/* 6 */ {`#"abc"`, int64(3), nil},
|
||||||
}
|
}
|
||||||
parserTest(t, "String", inputs)
|
parserTest(t, "String", inputs)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_template_test.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSomething(t *testing.T) {
|
||||||
|
section := "Something"
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`1`, int64(1), nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
|
// parserTestSpec(t, section, inputs, 1)
|
||||||
|
parserTest(t, section, inputs)
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// term_test.go
|
// t_term_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// token_test.go
|
// t_token_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// utils_test.go
|
// t_utils_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -12,6 +12,7 @@ type termPriority uint32
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
priNone termPriority = iota
|
priNone termPriority = iota
|
||||||
|
priRange
|
||||||
priBut
|
priBut
|
||||||
priAssign
|
priAssign
|
||||||
priOr
|
priOr
|
||||||
|
|||||||
@@ -40,12 +40,12 @@ func IsDict(v any) (ok bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IsFract(v any) (ok bool) {
|
func IsFract(v any) (ok bool) {
|
||||||
_, ok = v.(*fraction)
|
_, ok = v.(*FractionType)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsRational(v any) (ok bool) {
|
func IsRational(v any) (ok bool) {
|
||||||
if _, ok = v.(*fraction); !ok {
|
if _, ok = v.(*FractionType); !ok {
|
||||||
_, ok = v.(int64)
|
_, ok = v.(int64)
|
||||||
}
|
}
|
||||||
return ok
|
return ok
|
||||||
@@ -76,7 +76,7 @@ func isIterator(v any) (ok bool) {
|
|||||||
func numAsFloat(v any) (f float64) {
|
func numAsFloat(v any) (f float64) {
|
||||||
var ok bool
|
var ok bool
|
||||||
if f, ok = v.(float64); !ok {
|
if f, ok = v.(float64); !ok {
|
||||||
if fract, ok := v.(*fraction); ok {
|
if fract, ok := v.(*FractionType); ok {
|
||||||
f = fract.toFloat()
|
f = fract.toFloat()
|
||||||
} else {
|
} else {
|
||||||
i, _ := v.(int64)
|
i, _ := v.(int64)
|
||||||
@@ -209,3 +209,11 @@ func toInt(value any, description string) (i int, err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ForAll[T, V any](ts []T, fn func(T) V) []V {
|
||||||
|
result := make([]V, len(ts))
|
||||||
|
for i, t := range ts {
|
||||||
|
result[i] = fn(t)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user