mirror of
https://github.com/romychs/Ocean-240.2-Emulator.git
synced 2026-04-21 11:03:21 +03:00
819 lines
19 KiB
Go
819 lines
19 KiB
Go
package gval
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"text/scanner"
|
|
)
|
|
|
|
func TestNoParameter(t *testing.T) {
|
|
testEvaluate(
|
|
[]evaluationTest{
|
|
{
|
|
name: "Number",
|
|
expression: "100",
|
|
want: 100.0,
|
|
},
|
|
{
|
|
name: "Single PLUS",
|
|
expression: "51 + 49",
|
|
want: 100.0,
|
|
},
|
|
{
|
|
name: "Single MINUS",
|
|
expression: "100 - 51",
|
|
want: 49.0,
|
|
},
|
|
{
|
|
name: "Single BITWISE AND",
|
|
expression: "100 & 50",
|
|
want: 32.0,
|
|
},
|
|
{
|
|
name: "Single BITWISE OR",
|
|
expression: "100 | 50",
|
|
want: 118.0,
|
|
},
|
|
{
|
|
name: "Single BITWISE XOR",
|
|
expression: "100 ^ 50",
|
|
want: 86.0,
|
|
},
|
|
{
|
|
name: "Single shift left",
|
|
expression: "2 << 1",
|
|
want: 4.0,
|
|
},
|
|
{
|
|
name: "Single shift right",
|
|
expression: "2 >> 1",
|
|
want: 1.0,
|
|
},
|
|
{
|
|
name: "Single BITWISE NOT",
|
|
expression: "~10",
|
|
want: -11.0,
|
|
},
|
|
{
|
|
|
|
name: "Single MULTIPLY",
|
|
expression: "5 * 20",
|
|
want: 100.0,
|
|
},
|
|
{
|
|
|
|
name: "Single DIVIDE",
|
|
expression: "100 / 20",
|
|
want: 5.0,
|
|
},
|
|
{
|
|
|
|
name: "Single even MODULUS",
|
|
expression: "100 % 2",
|
|
want: 0.0,
|
|
},
|
|
{
|
|
name: "Single odd MODULUS",
|
|
expression: "101 % 2",
|
|
want: 1.0,
|
|
},
|
|
{
|
|
|
|
name: "Single EXPONENT",
|
|
expression: "10 ** 2",
|
|
want: 100.0,
|
|
},
|
|
{
|
|
|
|
name: "Compound PLUS",
|
|
expression: "20 + 30 + 50",
|
|
want: 100.0,
|
|
},
|
|
{
|
|
|
|
name: "Compound BITWISE AND",
|
|
expression: "20 & 30 & 50",
|
|
want: 16.0,
|
|
},
|
|
{
|
|
name: "Mutiple operators",
|
|
expression: "20 * 5 - 49",
|
|
want: 51.0,
|
|
},
|
|
{
|
|
name: "Parenthesis usage",
|
|
expression: "100 - (5 * 10)",
|
|
want: 50.0,
|
|
},
|
|
{
|
|
|
|
name: "Nested parentheses",
|
|
expression: "50 + (5 * (15 - 5))",
|
|
want: 100.0,
|
|
},
|
|
{
|
|
|
|
name: "Nested parentheses with bitwise",
|
|
expression: "100 ^ (23 * (2 | 5))",
|
|
want: 197.0,
|
|
},
|
|
{
|
|
name: "Logical OR operation of two clauses",
|
|
expression: "(1 == 1) || (true == true)",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Logical AND operation of two clauses",
|
|
expression: "(1 == 1) && (true == true)",
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Implicit boolean",
|
|
expression: "2 > 1",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Equal test minus numbers and no spaces",
|
|
expression: "-1==-1",
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Compound boolean",
|
|
expression: "5 < 10 && 1 < 5",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Evaluated true && false operation (for issue #8)",
|
|
expression: "1 > 10 && 11 > 10",
|
|
want: false,
|
|
},
|
|
{
|
|
|
|
name: "Evaluated true && false operation (for issue #8)",
|
|
expression: "true == true && false == true",
|
|
want: false,
|
|
},
|
|
{
|
|
|
|
name: "Parenthesis boolean",
|
|
expression: "10 < 50 && (1 != 2 && 1 > 0)",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Comparison of string constants",
|
|
expression: `"foo" == "foo"`,
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "NEQ comparison of string constants",
|
|
expression: `"foo" != "bar"`,
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "REQ comparison of string constants",
|
|
expression: `"foobar" =~ "oba"`,
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "NREQ comparison of string constants",
|
|
expression: `"foo" !~ "bar"`,
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Multiplicative/additive order",
|
|
expression: "5 + 10 * 2",
|
|
want: 25.0,
|
|
},
|
|
{
|
|
name: "Multiple constant multiplications",
|
|
expression: "10 * 10 * 10",
|
|
want: 1000.0,
|
|
},
|
|
{
|
|
|
|
name: "Multiple adds/multiplications",
|
|
expression: "10 * 10 * 10 + 1 * 10 * 10",
|
|
want: 1100.0,
|
|
},
|
|
{
|
|
|
|
name: "Modulus operatorPrecedence",
|
|
expression: "1 + 101 % 2 * 5",
|
|
want: 6.0,
|
|
},
|
|
{
|
|
name: "Exponent operatorPrecedence",
|
|
expression: "1 + 5 ** 3 % 2 * 5",
|
|
want: 6.0,
|
|
},
|
|
{
|
|
|
|
name: "Bit shift operatorPrecedence",
|
|
expression: "50 << 1 & 90",
|
|
want: 64.0,
|
|
},
|
|
{
|
|
|
|
name: "Bit shift operatorPrecedence",
|
|
expression: "90 & 50 << 1",
|
|
want: 64.0,
|
|
},
|
|
{
|
|
|
|
name: "Bit shift operatorPrecedence amongst non-bitwise",
|
|
expression: "90 + 50 << 1 * 5",
|
|
want: 4480.0,
|
|
},
|
|
{
|
|
name: "Order of non-commutative same-operatorPrecedence operators (additive)",
|
|
expression: "1 - 2 - 4 - 8",
|
|
want: -13.0,
|
|
},
|
|
{
|
|
name: "Order of non-commutative same-operatorPrecedence operators (multiplicative)",
|
|
expression: "1 * 4 / 2 * 8",
|
|
want: 16.0,
|
|
},
|
|
{
|
|
name: "Null coalesce operatorPrecedence",
|
|
expression: "true ?? true ? 100 + 200 : 400",
|
|
want: 300.0,
|
|
},
|
|
{
|
|
name: "Identical date equivalence",
|
|
expression: `"2014-01-02 14:12:22" == "2014-01-02 14:12:22"`,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Positive date GT",
|
|
expression: `"2014-01-02 14:12:22" > "2014-01-02 12:12:22"`,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Negative date GT",
|
|
expression: `"2014-01-02 14:12:22" > "2014-01-02 16:12:22"`,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "Positive date GTE",
|
|
expression: `"2014-01-02 14:12:22" >= "2014-01-02 12:12:22"`,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Negative date GTE",
|
|
expression: `"2014-01-02 14:12:22" >= "2014-01-02 16:12:22"`,
|
|
want: false,
|
|
},
|
|
{
|
|
name: "Positive date LT",
|
|
expression: `"2014-01-02 14:12:22" < "2014-01-02 16:12:22"`,
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Negative date LT",
|
|
expression: `"2014-01-02 14:12:22" < "2014-01-02 11:12:22"`,
|
|
want: false,
|
|
},
|
|
{
|
|
|
|
name: "Positive date LTE",
|
|
expression: `"2014-01-02 09:12:22" <= "2014-01-02 12:12:22"`,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Negative date LTE",
|
|
expression: `"2014-01-02 14:12:22" <= "2014-01-02 11:12:22"`,
|
|
want: false,
|
|
},
|
|
{
|
|
|
|
name: "Sign prefix comparison",
|
|
expression: "-1 < 0",
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Lexicographic LT",
|
|
expression: `"ab" < "abc"`,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Lexicographic LTE",
|
|
expression: `"ab" <= "abc"`,
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Lexicographic GT",
|
|
expression: `"aba" > "abc"`,
|
|
want: false,
|
|
},
|
|
{
|
|
|
|
name: "Lexicographic GTE",
|
|
expression: `"aba" >= "abc"`,
|
|
want: false,
|
|
},
|
|
{
|
|
|
|
name: "Boolean sign prefix comparison",
|
|
expression: "!true == false",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Inversion of clause",
|
|
expression: "!(10 < 0)",
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Negation after modifier",
|
|
expression: "10 * -10",
|
|
want: -100.0,
|
|
},
|
|
{
|
|
|
|
name: "Ternary with single boolean",
|
|
expression: "true ? 10",
|
|
want: 10.0,
|
|
},
|
|
{
|
|
|
|
name: "Ternary nil with single boolean",
|
|
expression: "false ? 10",
|
|
want: nil,
|
|
},
|
|
{
|
|
name: "Ternary with comparator boolean",
|
|
expression: "10 > 5 ? 35.50",
|
|
want: 35.50,
|
|
},
|
|
{
|
|
|
|
name: "Ternary nil with comparator boolean",
|
|
expression: "1 > 5 ? 35.50",
|
|
want: nil,
|
|
},
|
|
{
|
|
|
|
name: "Ternary with parentheses",
|
|
expression: "(5 * (15 - 5)) > 5 ? 35.50",
|
|
want: 35.50,
|
|
},
|
|
{
|
|
|
|
name: "Ternary operatorPrecedence",
|
|
expression: "true ? 35.50 > 10",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Ternary-else",
|
|
expression: "false ? 35.50 : 50",
|
|
want: 50.0,
|
|
},
|
|
{
|
|
|
|
name: "Ternary-else inside clause",
|
|
expression: "(false ? 5 : 35.50) > 10",
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Ternary-else (true-case) inside clause",
|
|
expression: "(true ? 1 : 5) < 10",
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Ternary-else before comparator (negative case)",
|
|
expression: "true ? 1 : 5 > 10",
|
|
want: 1.0,
|
|
},
|
|
{
|
|
name: "Nested ternaries (#32)",
|
|
expression: "(2 == 2) ? 1 : (true ? 2 : 3)",
|
|
want: 1.0,
|
|
},
|
|
{
|
|
|
|
name: "Nested ternaries, right case (#32)",
|
|
expression: "false ? 1 : (true ? 2 : 3)",
|
|
want: 2.0,
|
|
},
|
|
{
|
|
|
|
name: "Doubly-nested ternaries (#32)",
|
|
expression: "true ? (false ? 1 : (false ? 2 : 3)) : (false ? 4 : 5)",
|
|
want: 3.0,
|
|
},
|
|
{
|
|
|
|
name: "String to string concat",
|
|
expression: `"foo" + "bar" == "foobar"`,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "String to float64 concat",
|
|
expression: `"foo" + 123 == "foo123"`,
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Float64 to string concat",
|
|
expression: `123 + "bar" == "123bar"`,
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "String to date concat",
|
|
expression: `"foo" + "02/05/1970" == "foobar"`,
|
|
want: false,
|
|
},
|
|
{
|
|
|
|
name: "String to bool concat",
|
|
expression: `"foo" + true == "footrue"`,
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Bool to string concat",
|
|
expression: `true + "bar" == "truebar"`,
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Null coalesce left",
|
|
expression: "1 ?? 2",
|
|
want: 1.0,
|
|
},
|
|
{
|
|
|
|
name: "Array membership literals",
|
|
expression: "1 in [1, 2, 3]",
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Array membership literal with inversion",
|
|
expression: "!(1 in [1, 2, 3])",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "Logical operator reordering (#30)",
|
|
expression: "(true && true) || (true && false)",
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Logical operator reordering without parens (#30)",
|
|
expression: "true && true || true && false",
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Logical operator reordering with multiple OR (#30)",
|
|
expression: "false || true && true || false",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Left-side multiple consecutive (should be reordered) operators",
|
|
expression: "(10 * 10 * 10) > 10",
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Three-part non-paren logical op reordering (#44)",
|
|
expression: "false && true || true",
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Three-part non-paren logical op reordering (#44), second one",
|
|
expression: "true || false && true",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Logical operator reordering without parens (#45)",
|
|
expression: "true && true || false && false",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Single function",
|
|
expression: "foo()",
|
|
extension: Function("foo", func(arguments ...interface{}) (interface{}, error) {
|
|
return true, nil
|
|
}),
|
|
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Func with argument",
|
|
expression: "passthrough(1)",
|
|
extension: Function("passthrough", func(arguments ...interface{}) (interface{}, error) {
|
|
return arguments[0], nil
|
|
}),
|
|
want: 1.0,
|
|
},
|
|
{
|
|
name: "Func with arguments",
|
|
expression: "passthrough(1, 2)",
|
|
extension: Function("passthrough", func(arguments ...interface{}) (interface{}, error) {
|
|
return arguments[0].(float64) + arguments[1].(float64), nil
|
|
}),
|
|
want: 3.0,
|
|
},
|
|
{
|
|
name: "Nested function with operatorPrecedence",
|
|
expression: "sum(1, sum(2, 3), 2 + 2, true ? 4 : 5)",
|
|
extension: Function("sum", func(arguments ...interface{}) (interface{}, error) {
|
|
sum := 0.0
|
|
for _, v := range arguments {
|
|
sum += v.(float64)
|
|
}
|
|
return sum, nil
|
|
}),
|
|
want: 14.0,
|
|
},
|
|
{
|
|
name: "Empty function and modifier, compared",
|
|
expression: "numeric()-1 > 0",
|
|
extension: Function("numeric", func(arguments ...interface{}) (interface{}, error) {
|
|
return 2.0, nil
|
|
}),
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Empty function comparator",
|
|
expression: "numeric() > 0",
|
|
extension: Function("numeric", func(arguments ...interface{}) (interface{}, error) {
|
|
return 2.0, nil
|
|
}),
|
|
want: true,
|
|
},
|
|
{
|
|
|
|
name: "Empty function logical operator",
|
|
expression: "success() && !false",
|
|
extension: Function("success", func(arguments ...interface{}) (interface{}, error) {
|
|
return true, nil
|
|
}),
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Empty function ternary",
|
|
expression: "nope() ? 1 : 2.0",
|
|
extension: Function("nope", func(arguments ...interface{}) (interface{}, error) {
|
|
return false, nil
|
|
}),
|
|
want: 2.0,
|
|
},
|
|
{
|
|
|
|
name: "Empty function null coalesce",
|
|
expression: "null() ?? 2",
|
|
extension: Function("null", func(arguments ...interface{}) (interface{}, error) {
|
|
return nil, nil
|
|
}),
|
|
want: 2.0,
|
|
},
|
|
{
|
|
name: "Empty function with prefix",
|
|
expression: "-ten()",
|
|
extension: Function("ten", func(arguments ...interface{}) (interface{}, error) {
|
|
return 10.0, nil
|
|
}),
|
|
want: -10.0,
|
|
},
|
|
{
|
|
name: "Empty function as part of chain",
|
|
expression: "10 - numeric() - 2",
|
|
extension: Function("numeric", func(arguments ...interface{}) (interface{}, error) {
|
|
return 5.0, nil
|
|
}),
|
|
want: 3.0,
|
|
},
|
|
{
|
|
name: "Empty function near separator",
|
|
expression: "10 in [1, 2, 3, ten(), 8]",
|
|
extension: Function("ten", func(arguments ...interface{}) (interface{}, error) {
|
|
return 10.0, nil
|
|
}),
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Enclosed empty function with modifier and comparator (#28)",
|
|
expression: "(ten() - 1) > 3",
|
|
extension: Function("ten", func(arguments ...interface{}) (interface{}, error) {
|
|
return 10.0, nil
|
|
}),
|
|
want: true,
|
|
},
|
|
{
|
|
name: "Array",
|
|
expression: `[(ten() - 1) > 3, (ten() - 1),"hey"]`,
|
|
extension: Function("ten", func(arguments ...interface{}) (interface{}, error) {
|
|
return 10.0, nil
|
|
}),
|
|
want: []interface{}{true, 9., "hey"},
|
|
},
|
|
{
|
|
name: "Object",
|
|
expression: `{1: (ten() - 1) > 3, 7 + ".X" : (ten() - 1),"hello" : "hey"}`,
|
|
extension: Function("ten", func(arguments ...interface{}) (interface{}, error) {
|
|
return 10.0, nil
|
|
}),
|
|
want: map[string]interface{}{"1": true, "7.X": 9., "hello": "hey"},
|
|
},
|
|
{
|
|
name: "Object negativ value",
|
|
expression: `{1: -1,"hello" : "hey"}`,
|
|
want: map[string]interface{}{"1": -1., "hello": "hey"},
|
|
},
|
|
{
|
|
name: "Empty Array",
|
|
expression: `[]`,
|
|
want: []interface{}{},
|
|
},
|
|
{
|
|
name: "Empty Object",
|
|
expression: `{}`,
|
|
want: map[string]interface{}{},
|
|
},
|
|
{
|
|
name: "Variadic",
|
|
expression: `sum(1,2,3,4)`,
|
|
extension: Function("sum", func(arguments ...float64) (interface{}, error) {
|
|
sum := 0.
|
|
for _, a := range arguments {
|
|
sum += a
|
|
}
|
|
return sum, nil
|
|
}),
|
|
want: 10.0,
|
|
},
|
|
{
|
|
name: "Ident Operator",
|
|
expression: `1 plus 1`,
|
|
extension: InfixNumberOperator("plus", func(a, b float64) (interface{}, error) {
|
|
return a + b, nil
|
|
}),
|
|
want: 2.0,
|
|
},
|
|
{
|
|
name: "Postfix Operator",
|
|
expression: `4§`,
|
|
extension: PostfixOperator("§", func(_ context.Context, _ *Parser, eval Evaluable) (Evaluable, error) {
|
|
return func(ctx context.Context, parameter interface{}) (interface{}, error) {
|
|
i, err := eval.EvalInt(ctx, parameter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return fmt.Sprintf("§%d", i), nil
|
|
}, nil
|
|
}),
|
|
want: "§4",
|
|
},
|
|
{
|
|
name: "Tabs as non-whitespace",
|
|
expression: "4\t5\t6",
|
|
extension: NewLanguage(
|
|
Init(func(ctx context.Context, p *Parser) (Evaluable, error) {
|
|
p.SetWhitespace('\n', '\r', ' ')
|
|
return p.ParseExpression(ctx)
|
|
}),
|
|
InfixNumberOperator("\t", func(a, b float64) (interface{}, error) {
|
|
return a * b, nil
|
|
}),
|
|
),
|
|
want: 120.0,
|
|
},
|
|
{
|
|
name: "Handle all other prefixes",
|
|
expression: "^foo + $bar + &baz",
|
|
extension: DefaultExtension(func(ctx context.Context, p *Parser) (Evaluable, error) {
|
|
var mul int
|
|
switch p.TokenText() {
|
|
case "^":
|
|
mul = 1
|
|
case "$":
|
|
mul = 2
|
|
case "&":
|
|
mul = 3
|
|
}
|
|
|
|
switch p.Scan() {
|
|
case scanner.Ident:
|
|
return p.Const(mul * len(p.TokenText())), nil
|
|
default:
|
|
return nil, p.Expected("length multiplier", scanner.Ident)
|
|
}
|
|
}),
|
|
want: 18.0,
|
|
},
|
|
{
|
|
name: "Embed languages",
|
|
expression: "left { 5 + 5 } right",
|
|
extension: func() Language {
|
|
step := func(ctx context.Context, p *Parser, cur Evaluable) (Evaluable, error) {
|
|
next, err := p.ParseExpression(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return func(ctx context.Context, parameter interface{}) (interface{}, error) {
|
|
us, err := cur.EvalString(ctx, parameter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
them, err := next.EvalString(ctx, parameter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return us + them, nil
|
|
}, nil
|
|
}
|
|
|
|
return NewLanguage(
|
|
Init(func(ctx context.Context, p *Parser) (Evaluable, error) {
|
|
p.SetWhitespace()
|
|
p.SetMode(0)
|
|
|
|
return p.ParseExpression(ctx)
|
|
}),
|
|
DefaultExtension(func(ctx context.Context, p *Parser) (Evaluable, error) {
|
|
return step(ctx, p, p.Const(p.TokenText()))
|
|
}),
|
|
PrefixExtension(scanner.EOF, func(ctx context.Context, p *Parser) (Evaluable, error) {
|
|
return p.Const(""), nil
|
|
}),
|
|
PrefixExtension('{', func(ctx context.Context, p *Parser) (Evaluable, error) {
|
|
eval, err := p.ParseSublanguage(ctx, Full())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch p.Scan() {
|
|
case '}':
|
|
default:
|
|
return nil, p.Expected("embedded", '}')
|
|
}
|
|
|
|
return step(ctx, p, eval)
|
|
}),
|
|
)
|
|
}(),
|
|
want: "left 10 right",
|
|
},
|
|
{
|
|
name: "Late binding",
|
|
expression: "5 * [ 10 * { 20 / [ 10 ] } ]",
|
|
extension: func() Language {
|
|
var inner, outer Language
|
|
|
|
parseCurly := func(ctx context.Context, p *Parser) (Evaluable, error) {
|
|
eval, err := p.ParseSublanguage(ctx, outer)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if p.Scan() != '}' {
|
|
return nil, p.Expected("end", '}')
|
|
}
|
|
|
|
return eval, nil
|
|
}
|
|
|
|
parseSquare := func(ctx context.Context, p *Parser) (Evaluable, error) {
|
|
eval, err := p.ParseSublanguage(ctx, inner)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if p.Scan() != ']' {
|
|
return nil, p.Expected("end", ']')
|
|
}
|
|
|
|
return eval, nil
|
|
}
|
|
|
|
inner = Full(PrefixExtension('{', parseCurly))
|
|
outer = Full(PrefixExtension('[', parseSquare))
|
|
return outer
|
|
}(),
|
|
want: 100.0,
|
|
},
|
|
},
|
|
t,
|
|
)
|
|
}
|