diff --git a/examples/yolol/operations4.yolol b/examples/yolol/operations4.yolol index 3974bf0..dee9cd7 100644 --- a/examples/yolol/operations4.yolol +++ b/examples/yolol/operations4.yolol @@ -1,5 +1,5 @@ -i="abc" j=i++ k=++i :testi=i=="abc " and j=="abc" and k=="abc " -i="abc" j=i-- k=--i :testd=i=="a" and j=="abc" and k=="a" +i="abc" j=i++ k= ++i :testi=i=="abc " and j=="abc" and k=="abc " +i="abc" j=i-- k= --i :testd=i=="a" and j=="abc" and k=="a" :testsum="abc"+"xyz"=="abcxyz" :testsub="abcdecddc"-"cd"=="abcdedc" :testeq="abc"=="abc" and not ("mno"=="pqr") @@ -8,4 +8,5 @@ i="abc" j=i-- k=--i :testd=i=="a" and j=="abc" and k=="a" :testgte="jkl">="aaa" and not ("aaa">="jkl") and "jkl">="jkl" :testlt="qqq"<"xyz" and not ("xyz"<"qqq") and ""<"a" :testlte="qqq"<="xyz" and not ("xyz"<="qqq") and "qqq">="qqq" +:testra=2*3^4==162 and (2*3)^4==1296 :done=1 \ No newline at end of file diff --git a/examples/yolol/operations4_test.yaml b/examples/yolol/operations4_test.yaml index 6d8cf61..d3d9ee7 100644 --- a/examples/yolol/operations4_test.yaml +++ b/examples/yolol/operations4_test.yaml @@ -13,5 +13,6 @@ cases: testgte: 1 testlt: 1 testlte: 1 + testra: 1 \ No newline at end of file diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index 788beaf..8240bfc 100644 --- a/pkg/parser/parser.go +++ b/pkg/parser/parser.go @@ -416,9 +416,10 @@ func (p *Parser) ParseExpression() ast.Expression { func (p *Parser) ParseBinaryExpression(idx int) ast.Expression { p.LogI(idx) - var exp ast.Expression + var exp *ast.BinaryOperation var ops []string expectedType := ast.TypeSymbol + leftAssoc := true switch idx { case 0: @@ -435,31 +436,57 @@ func (p *Parser) ParseBinaryExpression(idx int) ast.Expression { ops = []string{"+", "-"} break case 4: - ops = []string{"*", "/", "%", "^"} + ops = []string{"*", "/", "%"} + break + case 5: + ops = []string{"^"} + leftAssoc = false break default: - exp = p.This.ParseUnaryExpression() - return exp + return p.This.ParseUnaryExpression() } idx++ - exp = p.This.ParseBinaryExpression(idx) - if exp == nil { + leftExp := p.This.ParseBinaryExpression(idx) + if leftExp == nil { return nil } + for p.IsCurrentType(expectedType) && p.IsCurrentValueIn(ops) { binexp := &ast.BinaryOperation{ Operator: p.CurrentToken.Value, - Exp1: exp, } + p.Advance() - binexp.Exp2 = p.This.ParseBinaryExpression(idx) - if binexp.Exp2 == nil { + rightExp := p.This.ParseBinaryExpression(idx) + if rightExp == nil { p.ErrorCurrent(fmt.Sprintf("Expected expression on right side of %s", binexp.Operator)) + return binexp } - exp = binexp + + if exp == nil { + binexp.Exp1 = leftExp + binexp.Exp2 = rightExp + exp = binexp + continue + } + + if leftAssoc { + binexp.Exp1 = exp + binexp.Exp2 = rightExp + exp = binexp + } else { + binexp.Exp1 = exp.Exp2 + binexp.Exp2 = rightExp + exp.Exp2 = binexp + } + } + + if exp == nil { + return leftExp } + return exp } diff --git a/pkg/parser/printer.go b/pkg/parser/printer.go index a651f77..bd08957 100644 --- a/pkg/parser/printer.go +++ b/pkg/parser/printer.go @@ -53,9 +53,9 @@ var operatorPriority = map[string]int{ "-": 3, "*": 4, "/": 4, - "^": 4, "%": 4, - "not": 5, + "^": 5, + "not": 6, } // end and else are missing here, because unlike other keywords they might require a space after them @@ -259,16 +259,22 @@ func insertEscapesIntoString(in string) string { func (p *Printer) printBinaryOperation(o *ast.BinaryOperation, visitType int) { lPrio := priorityForExpression(o.Exp1) rPrio := priorityForExpression(o.Exp2) - _, rBinary := o.Exp2.(*ast.BinaryOperation) + rBinary, rIsBinary := o.Exp2.(*ast.BinaryOperation) + lBinary, lIsBinary := o.Exp1.(*ast.BinaryOperation) myPrio := priorityForExpression(o) + + // check if we need braces because of the right-associativity of the ^-operator + rightAssocL := o.Operator == "^" && lIsBinary && lBinary.Operator == "^" + rightAssocR := o.Operator == "^" && rIsBinary && rBinary.Operator == "^" + switch visitType { case ast.PreVisit: - if lPrio < myPrio { + if lPrio < myPrio || rightAssocL { p.Write("(") } break case ast.InterVisit1: - if lPrio < myPrio { + if lPrio < myPrio || rightAssocL { p.Write(")") } op := o.Operator @@ -282,12 +288,12 @@ func (p *Printer) printBinaryOperation(o *ast.BinaryOperation, visitType int) { p.OptionalSpace() } - if rBinary && rPrio <= myPrio { + if rIsBinary && rPrio <= myPrio && !rightAssocR { p.Write("(") } break case ast.PostVisit: - if rBinary && rPrio <= myPrio { + if rIsBinary && rPrio <= myPrio && !rightAssocR { p.Write(")") } break