diff --git a/fraction-type.go b/fraction-type.go index 7eb3376..0284b87 100644 --- a/fraction-type.go +++ b/fraction-type.go @@ -50,9 +50,9 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) { } else if s[0] == '+' { s = s[1:] } -// if strings.HasSuffix(s, "()") { -// s = s[0 : len(s)-2] -// } + // if strings.HasSuffix(s, "()") { + // s = s[0 : len(s)-2] + // } s = strings.TrimSuffix(s, "()") parts = strings.SplitN(s, ".", 2) if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil { @@ -124,7 +124,7 @@ func (f *FractionType) String() string { func (f *FractionType) ToString(opt FmtOpt) string { var sb strings.Builder if opt&MultiLine == 0 { - sb.WriteString(fmt.Sprintf("%d|%d", f.num, f.den)) + sb.WriteString(fmt.Sprintf("%d:%d", f.num, f.den)) } else { var s, num string if f.num < 0 && opt&TTY == 0 { diff --git a/operator-binary.go b/operator-binary.go index b8211ea..13d2307 100644 --- a/operator-binary.go +++ b/operator-binary.go @@ -100,5 +100,5 @@ func evalBinaryOr(ctx ExprContext, self *term) (v any, err error) { func init() { registerTermConstructor(SymTilde, newBinNotTerm) registerTermConstructor(SymAmpersand, newBinAndTerm) - // registerTermConstructor(SymVertBar, newBinOrTerm) + registerTermConstructor(SymVertBar, newBinOrTerm) } diff --git a/operator-fraction.go b/operator-fraction.go index aba351d..b979721 100644 --- a/operator-fraction.go +++ b/operator-fraction.go @@ -49,19 +49,23 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) { den = -den num = -num } - g := gcd(num, den) - num = num / g - den = den / g - if den == 1 { - v = num + if num != 0 { + g := gcd(num, den) + num = num / g + den = den / g + if den == 1 { + v = num + } else { + v = &FractionType{num, den} + } } else { - v = &FractionType{num, den} + v = &FractionType{0, den} } return } // init func init() { - registerTermConstructor(SymVertBar, newFractionTerm) - // registerTermConstructor(SymColon, newFractionTerm) + // registerTermConstructor(SymVertBar, newFractionTerm) + registerTermConstructor(SymColon, newFractionTerm) } diff --git a/operator-index.go b/operator-index.go index ec19582..069081b 100644 --- a/operator-index.go +++ b/operator-index.go @@ -113,6 +113,9 @@ func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) { } else if IsDict(leftValue) { d := leftValue.(*DictType) v, err = getDictItem(d, indexTerm, indexList, rightValue) + } else { + rightChild := opTerm.children[1] + err = rightChild.Errorf("invalid index type: %v", (*indexList)[0]) } return } diff --git a/parser.go b/parser.go index a720123..1f801a2 100644 --- a/parser.go +++ b/parser.go @@ -162,29 +162,33 @@ func paramAlreadyDefined(args []*term, param *term) (position int) { return } -func (parser *parser) parseList(scanner *scanner, ctx parserContext) (subtree *term, err error) { +func (parser *parser) parseList(scanner *scanner, ctx parserContext) (listTerm *term, err error) { r, c := scanner.lastPos() args := make([]*term, 0) lastSym := SymUnknown itemExpected := false + itemCtx := remFlags(ctx, allowIndex) for lastSym != SymClosedSquare && lastSym != SymEos { - var subTree *ast zeroRequired := scanner.current.Sym == SymColon - if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedSquare); err == nil { - root := subTree.root + var itemTree *ast + if itemTree, err = parser.parseItem(scanner, itemCtx, SymComma, SymClosedSquare); err == nil { + root := itemTree.root if root != nil { - //if !parsingIndeces && root.symbol() == SymColon { - if !hasFlag(ctx, allowIndex) && root.symbol() == SymColon { - err = root.Errorf("unexpected range expression") + if hasFlag(ctx, allowIndex) && root.symbol() == SymColon { + changeColonToRange(root) + } + if !hasFlag(ctx, allowIndex) && root.symbol() == SymRange { + // err = root.Errorf("unexpected range expression") + err = errRangeUnexpectedExpression(root) break } args = append(args, root) - // if parsingIndeces && root.symbol() == SymColon && zeroRequired { //len(root.children) == 0 { - if hasFlag(ctx, allowIndex) && root.symbol() == SymColon && zeroRequired { //len(root.children) == 0 { + if hasFlag(ctx, allowIndex) && root.symbol() == SymRange && zeroRequired { //len(root.children) == 0 { if len(root.children) == 1 { root.children = append(root.children, root.children[0]) } else if len(root.children) > 1 { - err = root.Errorf("invalid range specification") + // err = root.Errorf("invalid range specification") + err = errRangeInvalidSpecification(root) break } zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0)) @@ -201,13 +205,15 @@ func (parser *parser) parseList(scanner *scanner, ctx parserContext) (subtree *t break } lastSym = scanner.Previous().Sym - itemExpected = lastSym == SymComma + if itemExpected = lastSym == SymComma; itemExpected { + remFlags(ctx, allowIndex) + } } if err == nil { if lastSym != SymClosedSquare { err = scanner.Previous().ErrorExpectedGot("]") } else { - subtree = newListTerm(r, c, args) + listTerm = newListTerm(r, c, args) } } return @@ -302,7 +308,6 @@ func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subt err = scanner.Previous().ErrorExpectedGot("}") } else { subtree = newDictTerm(args) - // subtree = newMapTerm(args) } } return @@ -311,6 +316,7 @@ func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subt func (parser *parser) parseSelectorCase(scanner *scanner, ctx parserContext, defaultCase bool) (caseTerm *term, err error) { var filterList *term var caseExpr *ast + ctx = remFlags(ctx, allowIndex) tk := parser.Next(scanner) startRow := tk.row startCol := tk.col @@ -358,6 +364,8 @@ func addSelectorCase(selectorTerm, caseTerm *term) { func (parser *parser) parseSelector(scanner *scanner, tree *ast, ctx parserContext) (selectorTerm *term, err error) { var caseTerm *term + + ctx = remFlags(ctx, allowIndex) tk := scanner.makeToken(SymSelector, '?') if selectorTerm, err = tree.addToken(tk); err != nil { return @@ -386,13 +394,20 @@ func couldBeACollection(t *term) bool { return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable } -// func areSymbolsOutOfCtx(tk *Token, ctxTerm *term, syms ...Symbol) bool { -// var areOut = false -// if ctxTerm != nil { -// areOut = tk.IsOneOf(syms) -// } -// return areOut -// } +func listSubTree(tree *ast, listTerm *term, allowIndeces bool) (root *term, err error) { + var tk *Token + if allowIndeces { + tk = NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source()) + root = newTerm(tk) + if err = tree.addTerm(root); err == nil { + err = tree.addTerm(listTerm) + } + } else { + root = listTerm + err = tree.addTerm(listTerm) + } + return +} func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) { var selectorTerm *term = nil @@ -448,16 +463,7 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb var listTerm *term newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm)) if listTerm, err = parser.parseList(scanner, newCtx); err == nil { - if hasFlag(newCtx, allowIndex) { - indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source()) - indexTerm := newTerm(indexTk) - if err = tree.addTerm(indexTerm); err == nil { - err = tree.addTerm(listTerm) - } - } else { - err = tree.addTerm(listTerm) - } - currentTerm = listTerm + currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex)) } case SymOpenBrace: if currentTerm != nil && currentTerm.symbol() == SymColon { @@ -493,6 +499,7 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb case SymQuestion: if selectorTerm, err = parser.parseSelector(scanner, tree, ctx); err == nil { currentTerm = selectorTerm + addFlags(ctx, selectorContext) } case SymColon, SymDoubleColon: var caseTerm *term @@ -505,9 +512,12 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb } } } else { + // if hasFlag(ctx, allowIndex) { + // tk.Sym = SymRange + // } currentTerm, err = tree.addToken(tk) } - if tk.IsSymbol(SymColon) { + if tk.IsOneOfA(SymColon, SymRange) { // Colon outside a selector term acts like a separator firstToken = true } @@ -517,7 +527,7 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector { selectorTerm = nil - + remFlags(ctx, selectorContext) } // lastSym = tk.Sym } @@ -535,9 +545,5 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb err = tk.Error() } } - - // if err == nil { - // err = tk.Error() - // } return } diff --git a/t_builtin-base_test.go b/t_builtin-base_test.go index 52a51de..88a91af 100644 --- a/t_builtin-base_test.go +++ b/t_builtin-base_test.go @@ -30,9 +30,9 @@ func TestFuncBase(t *testing.T) { /* 16 */ {`isString("3" + 1)`, true, nil}, /* 17 */ {`isList(["3", 1])`, true, nil}, /* 18 */ {`isDict({"a":"3", "b":1})`, true, nil}, - /* 19 */ {`isFract(1|3)`, true, nil}, - /* 20 */ {`isFract(3|1)`, false, nil}, - /* 21 */ {`isRational(3|1)`, true, nil}, + /* 19 */ {`isFract(1:3)`, true, nil}, + /* 20 */ {`isFract(3:1)`, false, nil}, + /* 21 */ {`isRational(3:1)`, true, nil}, /* 22 */ {`fract("2.2(3)")`, newFraction(67, 30), nil}, /* 23 */ {`fract("1.21(3)")`, newFraction(91, 75), nil}, /* 24 */ {`fract(1.21(3))`, newFraction(91, 75), nil}, @@ -45,16 +45,16 @@ func TestFuncBase(t *testing.T) { /* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`}, /* 32 */ {`dec(1,2,3)`, nil, `dec(): too many params -- expected 1, got 3`}, /* 33 */ {`isBool(false)`, true, nil}, - /* 34 */ {`fract(1|2)`, newFraction(1, 2), nil}, + /* 34 */ {`fract(1:2)`, newFraction(1, 2), nil}, /* 35 */ {`fract(12,2)`, newFraction(6, 1), nil}, /* 36 */ {`bool(2)`, true, nil}, - /* 37 */ {`bool(1|2)`, true, nil}, + /* 37 */ {`bool(1:2)`, true, nil}, /* 38 */ {`bool(1.0)`, true, nil}, /* 39 */ {`bool("1")`, true, nil}, /* 40 */ {`bool(false)`, false, nil}, /* 41 */ {`bool([1])`, nil, `bool(): can't convert list to bool`}, /* 42 */ {`dec(false)`, float64(0), nil}, - /* 43 */ {`dec(1|2)`, float64(0.5), nil}, + /* 43 */ {`dec(1:2)`, float64(0.5), nil}, /* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`}, // /* 45 */ {`string([1])`, nil, `string(): can't convert list to string`}, } diff --git a/t_fractions_test.go b/t_fractions_test.go index 4d45a86..105c16d 100644 --- a/t_fractions_test.go +++ b/t_fractions_test.go @@ -11,36 +11,39 @@ import ( func TestFractionsParser(t *testing.T) { section := "Fraction" inputs := []inputType{ - /* 1 */ {`1|2`, newFraction(1, 2), nil}, - /* 2 */ {`1|2 + 1`, newFraction(3, 2), nil}, - /* 3 */ {`1|2 - 1`, newFraction(-1, 2), nil}, - /* 4 */ {`1|2 * 1`, newFraction(1, 2), nil}, - /* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil}, - /* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil}, - /* 7 */ {`1|"5"`, nil, `denominator must be integer, got string (5)`}, - /* 8 */ {`"1"|5`, nil, `numerator must be integer, got string (1)`}, - /* 9 */ {`1|+5`, nil, `[1:3] infix operator "|" requires two non-nil operands, got 1`}, - /* 10 */ {`1|(-2)`, newFraction(-1, 2), nil}, - /* 11 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil}, - /* 12 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil}, - /* 13 */ {`builtin "math.arith"; mul(1|2, 2|3)`, newFraction(2, 6), nil}, - /* 14 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil}, - /* 15 */ {`1|0`, nil, `division by zero`}, + /* 1 */ {`1:2`, newFraction(1, 2), nil}, + /* 2 */ {`1:2 + 1`, newFraction(3, 2), nil}, + /* 3 */ {`1:2 - 1`, newFraction(-1, 2), nil}, + /* 4 */ {`1:2 * 1`, newFraction(1, 2), nil}, + /* 5 */ {`1:2 * 2:3`, newFraction(2, 6), nil}, + /* 6 */ {`1:2 / 2:3`, newFraction(3, 4), nil}, + /* 7 */ {`1:"5"`, nil, `denominator must be integer, got string (5)`}, + /* 8 */ {`"1":5`, nil, `numerator must be integer, got string (1)`}, + /* 9 */ {`1:+5`, newFraction(1, 5), nil}, + /* 10 */ {`1:(-2)`, newFraction(-1, 2), nil}, + /* 11 */ {`builtin "math.arith"; add(1:2, 2:3)`, newFraction(7, 6), nil}, + /* 12 */ {`builtin "math.arith"; add(1:2, 1.0, 2)`, float64(3.5), nil}, + /* 13 */ {`builtin "math.arith"; mul(1:2, 2:3)`, newFraction(2, 6), nil}, + /* 14 */ {`builtin "math.arith"; mul(1:2, 1.0, 2)`, float64(1.0), nil}, + /* 15 */ {`1:0`, nil, `division by zero`}, /* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil}, /* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`}, /* 18 */ {`fract("-1")`, newFraction(-1, 1), nil}, /* 19 */ {`fract("+1")`, newFraction(1, 1), nil}, /* 20 */ {`fract("1a")`, (*FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`}, /* 21 */ {`fract(1,0)`, nil, `fract(): division by zero`}, - /* 22 */ {`string(1|2)`, "1|2", nil}, + /* 22 */ {`string(1:2)`, "1:2", nil}, + /* 23 */ {`1+1:2+0.5`, float64(2), nil}, + /* 24 */ {`1:(2-2)`, nil, `division by zero`}, + /* 25 */ {`[0,1][1-1]:1`, newFraction(0, 1), nil}, } - runTestSuiteSpec(t, section, inputs, 1) - // runTestSuite(t, section, inputs) + // runTestSuiteSpec(t, section, inputs, 25) + runTestSuite(t, section, inputs) } func TestFractionToStringSimple(t *testing.T) { source := newFraction(1, 2) - want := "1|2" + want := "1:2" got := source.ToString(0) if got != want { t.Errorf(`(1,2) -> result = %v [%T], want = %v [%T]`, got, got, want, want) diff --git a/t_index_test.go b/t_index_test.go index 52005be..80b944f 100644 --- a/t_index_test.go +++ b/t_index_test.go @@ -16,11 +16,13 @@ func TestCollections(t *testing.T) { /* 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, `[1:14] left operand '(1, 2)' [pair] and right operand '3' [integer] are not compatible with operator ":"`}, + /* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] invalid range specification`}, + /* 6 */ {`"abcdef"[((1>0)?{1}:{0}):3]`, "bc", nil}, + /* 7 */ {`"abcdef"[[0,1][0]:1]`, "a", nil}, } t.Setenv("EXPR_PATH", ".") - // parserTestSpec(t, section, inputs, 5) + // runTestSuiteSpec(t, section, inputs, 5) runTestSuite(t, section, inputs) } diff --git a/t_parser_test.go b/t_parser_test.go index 1b1d31e..7d385b2 100644 --- a/t_parser_test.go +++ b/t_parser_test.go @@ -77,7 +77,7 @@ func TestGeneralParser(t *testing.T) { /* 63 */ {`"1.5" == `, nil, `[1:8] infix operator "==" requires two non-nil operands, got 1`}, /* 64 */ {`"1.5" != `, nil, `[1:8] infix operator "!=" requires two non-nil operands, got 1`}, /* 65 */ {"+1.5", float64(1.5), nil}, - /* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one not nil operand`}, + /* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one non-nil operand`}, /* 67 */ {"4 / 0", nil, `division by zero`}, /* 68 */ {"4.0 / 0", nil, `division by zero`}, /* 69 */ {"4.0 / \n2", float64(2.0), nil}, @@ -132,15 +132,13 @@ func TestGeneralParser(t *testing.T) { /* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"}, /* 119 */ {`{}`, &DictType{}, nil}, /* 120 */ {`v=10; v++; v`, int64(11), nil}, - /* 121 */ {`1+1|2+0.5`, float64(2), nil}, - /* 122 */ {`1.2()`, newFraction(6, 5), nil}, - /* 123 */ {`1|(2-2)`, nil, `division by zero`}, - /* 124 */ {`x="abc"; x ?! #x`, int64(3), nil}, - /* 125 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '' [nil]`}, - /* 126 */ {`x ?! (x+1)`, nil, nil}, - /* 127 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`}, - /* 128 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`}, - /* 129 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`}, + /* 121 */ {`1.2()`, newFraction(6, 5), nil}, + /* 122 */ {`x="abc"; x ?! #x`, int64(3), nil}, + /* 123 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '' [nil]`}, + /* 124 */ {`x ?! (x+1)`, nil, nil}, + /* 125 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`}, + /* 126 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`}, + /* 127 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`}, } // t.Setenv("EXPR_PATH", ".") diff --git a/t_relational_test.go b/t_relational_test.go index 6c8b7a7..0815a53 100644 --- a/t_relational_test.go +++ b/t_relational_test.go @@ -21,9 +21,9 @@ func TestRelational(t *testing.T) { /* 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}, + /* 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}, @@ -32,10 +32,10 @@ func TestRelational(t *testing.T) { /* 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}, + /* 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},