diff --git a/operator-assign.go b/operator-assign.go index c26e52c..9ea97b4 100644 --- a/operator-assign.go +++ b/operator-assign.go @@ -187,6 +187,8 @@ func evalOpAssign(ctx ExprContext, opTerm *term) (v any, err error) { v, err = bitwiseAnd(opTerm, leftValue, rightValue) case SymVertBarEqual: v, err = bitwiseOr(opTerm, leftValue, rightValue) + case SymCaretEqual: + v, err = bitwiseXor(opTerm, leftValue, rightValue) case SymDoubleLessEqual: v, err = bitLeftShift(opTerm, leftValue, rightValue) case SymDoubleGreaterEqual: @@ -214,4 +216,5 @@ func init() { registerTermConstructor(SymDoubleGreaterEqual, newOpAssignTerm) registerTermConstructor(SymAmpersandEqual, newOpAssignTerm) registerTermConstructor(SymVertBarEqual, newOpAssignTerm) + registerTermConstructor(SymCaretEqual, newOpAssignTerm) } diff --git a/operator-bitwise.go b/operator-bitwise.go index 745fbfb..6815025 100644 --- a/operator-bitwise.go +++ b/operator-bitwise.go @@ -107,9 +107,48 @@ func evalBitwiseOr(ctx ExprContext, opTerm *term) (v any, err error) { return } +//-------- Bitwise XOR term + +func newBitwiseXorTerm(tk *Token) (inst *term) { + return &term{ + tk: *tk, + children: make([]*term, 0, 2), + position: posInfix, + priority: priBitwiseOr, + evalFunc: evalBitwiseXor, + } +} + +func bitwiseXor(opTerm *term, leftValue, rightValue any) (v any, err error) { + var leftInt, rightInt int64 + var lok, rok bool + + leftInt, lok = leftValue.(int64) + rightInt, rok = rightValue.(int64) + + if lok && rok { + v = leftInt ^ rightInt + } else { + err = opTerm.errIncompatibleTypes(leftValue, rightValue) + } + return +} + +func evalBitwiseXor(ctx ExprContext, opTerm *term) (v any, err error) { + var leftValue, rightValue any + + if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { + return + } + + v, err = bitwiseXor(opTerm, leftValue, rightValue) + return +} + // init func init() { registerTermConstructor(SymTilde, newBitwiseNotTerm) registerTermConstructor(SymAmpersand, newBitwiseAndTerm) registerTermConstructor(SymVertBar, newBitwiseOrTerm) + registerTermConstructor(SymCaret, newBitwiseXorTerm) } diff --git a/operator-iter-value.go b/operator-iter-value.go index 17abcaa..0340a5f 100644 --- a/operator-iter-value.go +++ b/operator-iter-value.go @@ -11,7 +11,7 @@ func newIterValueTerm(tk *Token) (inst *term) { tk: *tk, children: make([]*term, 0, 1), position: posPrefix, - priority: priIterValue, + priority: priDereference, evalFunc: evalIterValue, } } @@ -33,6 +33,6 @@ func evalIterValue(ctx ExprContext, opTerm *term) (v any, err error) { // init func init() { -// registerTermConstructor(SymOpenClosedRound, newIterValueTerm) - registerTermConstructor(SymCaret, newIterValueTerm) + // registerTermConstructor(SymOpenClosedRound, newIterValueTerm) + registerTermConstructor(SymDereference, newIterValueTerm) } diff --git a/parser.go b/parser.go index ea607fa..5c23171 100644 --- a/parser.go +++ b/parser.go @@ -441,8 +441,10 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb tk.Sym = SymChangeSign } else if tk.Sym == SymPlus { tk.Sym = SymUnchangeSign + } else if tk.IsSymbol(SymStar) { + tk.SetSymbol(SymDereference) } else if tk.IsSymbol(SymExclamation) { - err = tk.Errorf("postfix opertor %q requires an operand on its left", tk) + err = tk.Errorf("postfix opertor %q requires an operand on its left side", tk) break } firstToken = false @@ -481,7 +483,7 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb currentTerm = mapTerm } } - case SymEqual, SymPlusEqual, SymMinusEqual, SymStarEqual, SymSlashEqual, SymPercEqual: + case SymEqual, SymPlusEqual, SymMinusEqual, SymStarEqual, SymSlashEqual, SymPercEqual, SymAmpersandEqual, SymVertBarEqual, SymDoubleLessEqual, SymDoubleGreaterEqual, SymCaretEqual: currentTerm, err = tree.addToken(tk) firstToken = true case SymFuncDef: @@ -518,14 +520,11 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb } } } else { - // if hasFlag(ctx, allowIndex) { - // tk.Sym = SymRange - // } currentTerm, err = tree.addToken(tk) - } - if tk.IsOneOfA(SymColon, SymRange) { - // Colon outside a selector term acts like a separator - firstToken = true + if tk.IsOneOfA(SymColon, SymRange) { + // Colon outside a selector term acts like a separator + firstToken = true + } } default: currentTerm, err = tree.addToken(tk) diff --git a/scanner.go b/scanner.go index 362979a..7e8d88f 100644 --- a/scanner.go +++ b/scanner.go @@ -177,7 +177,11 @@ func (scanner *scanner) fetchNextToken() (tk *Token) { case ',': tk = scanner.makeToken(SymComma, ch) case '^': - tk = scanner.makeToken(SymCaret, ch) + if next, _ := scanner.peek(); next == '=' { + tk = scanner.moveOn(SymCaretEqual, ch, next) + } else { + tk = scanner.makeToken(SymCaret, ch) + } case ':': if next, _ := scanner.peek(); next == ':' { tk = scanner.moveOn(SymDoubleColon, ch, next) diff --git a/symbol-map.go b/symbol-map.go index 2ab988e..6aed2ba 100644 --- a/symbol-map.go +++ b/symbol-map.go @@ -100,8 +100,9 @@ func init() { SymDoubleGreaterEqual: {">>=", symClassOperator, posInfix}, // 64: '>>=' SymAmpersandEqual: {"&=", symClassOperator, posInfix}, // 65: '&=' SymVertBarEqual: {"|=", symClassOperator, posInfix}, // 65: '|=' - SymPlusGreater: {"+>", symClassOperator, posInfix}, // 66: '+>' - SymLessPlus: {"<+", symClassOperator, posInfix}, // 67: '<+' + SymCaretEqual: {"^=", symClassOperator, posInfix}, // 66: '^=' + SymPlusGreater: {"+>", symClassOperator, posInfix}, // 67: '+>' + SymLessPlus: {"<+", symClassOperator, posInfix}, // 68: '<+' // SymChangeSign // SymUnchangeSign // SymIdentifier diff --git a/symbol.go b/symbol.go index f492969..02eb6f8 100644 --- a/symbol.go +++ b/symbol.go @@ -75,10 +75,12 @@ const ( SymDoubleGreaterEqual // 64: '>>=' SymAmpersandEqual // 65: '&=' SymVertBarEqual // 65: '|=' - SymPlusGreater // 66: '+>' - SymLessPlus // 67: '<+' + SymCaretEqual // 66: '^=' + SymPlusGreater // 67: '+>' + SymLessPlus // 68: '<+' SymChangeSign SymUnchangeSign + SymDereference SymIdentifier SymBool SymInteger diff --git a/t_iterator_test.go b/t_iterator_test.go index d2787a9..01dcb76 100644 --- a/t_iterator_test.go +++ b/t_iterator_test.go @@ -9,10 +9,10 @@ import "testing" func TestIteratorParser(t *testing.T) { section := "Iterator" inputs := []inputType{ - /* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ^it`, int64(0), nil}, + /* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); *it`, int64(0), nil}, /* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil}, /* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(3), nil}, - /* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ^it`, int64(0), nil}, + /* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; *it`, int64(0), nil}, /* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil}, /* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil}, /* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil}, diff --git a/t_operator_test.go b/t_operator_test.go index c2a83fc..e278c95 100644 --- a/t_operator_test.go +++ b/t_operator_test.go @@ -28,6 +28,9 @@ func TestOperator(t *testing.T) { /* 15 */ {`0o10`, int64(8), nil}, /* 16 */ {`0b10`, int64(2), nil}, /* 17 */ {`~true`, nil, `[1:2] prefix/postfix operator "~" do not support operand 'true' [bool]`}, + /* 18 */ {`1^2`, int64(3), nil}, + /* 19 */ {`3^2`, int64(1), nil}, + /* 19 */ {`a=1; a^=2`, int64(3), nil}, } // t.Setenv("EXPR_PATH", ".") diff --git a/t_parser_test.go b/t_parser_test.go index e9a1aa4..778286f 100644 --- a/t_parser_test.go +++ b/t_parser_test.go @@ -142,6 +142,6 @@ func TestGeneralParser(t *testing.T) { } // t.Setenv("EXPR_PATH", ".") - //runTestSuiteSpec(t, section, inputs, 130) + // runTestSuiteSpec(t, section, inputs, 114) runTestSuite(t, section, inputs) }