Skip to content

Commit

Permalink
Improved handling of precedence when parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
dbaumgarten committed Nov 26, 2020
1 parent 22efec5 commit a894225
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 102 deletions.
3 changes: 2 additions & 1 deletion examples/yolol/operations3.yolol
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ k=2
:testamod=:testamod==0
:testmxor=1!=1!=1
:testfloor=100.123/1000*1000==100
:done = 1
x=3 y=2 z=1 :testprec=x>y==y>z
:done=1
3 changes: 2 additions & 1 deletion examples/yolol/operations3_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ cases:
testadiv: 1
testamod: 1
testmxor: 1
testfloor: 1
testfloor: 1
testprec: 1
133 changes: 44 additions & 89 deletions pkg/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,7 @@ type YololParserFunctions interface {
ParseAssignment() ast.Statement
ParseIf() ast.Statement
ParseExpression() ast.Expression
ParseLogicExpression() ast.Expression
ParseCompareExpression() ast.Expression
ParseSumExpression() ast.Expression
ParseProdExpression() ast.Expression
ParseBinaryExpression(int) ast.Expression
ParseUnaryExpression() ast.Expression
ParseBracketExpression() ast.Expression
ParseSingleExpression() ast.Expression
Expand Down Expand Up @@ -411,103 +408,53 @@ func (p *Parser) ParseIf() ast.Statement {
// ParseExpression parses an expression
func (p *Parser) ParseExpression() ast.Expression {
p.Log()
return p.This.ParseLogicExpression()
return p.This.ParseBinaryExpression(0)
}

// ParseLogicExpression parses a logical expression
func (p *Parser) ParseLogicExpression() ast.Expression {
p.Log()
var exp ast.Expression

exp = p.This.ParseCompareExpression()
if exp == nil {
return nil
}
logOps := []string{"or", "and"}

for p.IsCurrentType(ast.TypeKeyword) && p.IsCurrentValueIn(logOps) {
binexp := &ast.BinaryOperation{
Operator: p.CurrentToken.Value,
Exp1: exp,
}
p.Advance()
binexp.Exp2 = p.This.ParseCompareExpression()
if binexp.Exp2 == nil {
p.ErrorCurrent(fmt.Sprintf("Expected expression on right side of %s", binexp.Operator))
}
exp = binexp
}
return exp
}

// ParseCompareExpression parses a compare expression
func (p *Parser) ParseCompareExpression() ast.Expression {
p.Log()
exp := p.This.ParseSumExpression()
if exp == nil {
return nil
}
logOps := []string{"==", "!=", "<=", ">=", "<", ">"}

for p.IsCurrentType(ast.TypeSymbol) && p.IsCurrentValueIn(logOps) {
binexp := &ast.BinaryOperation{
Operator: p.CurrentToken.Value,
Exp1: exp,
}
p.Advance()
binexp.Exp2 = p.This.ParseSumExpression()
if binexp.Exp2 == nil {
p.ErrorCurrent(fmt.Sprintf("Expected expression on right side of %s", binexp.Operator))
}
exp = binexp
}
return exp
}
// ParseBinaryExpression parses a binary expression
// The kind of binary-expression to be parsed is given as idx
func (p *Parser) ParseBinaryExpression(idx int) ast.Expression {
p.LogI(idx)

// ParseSumExpression parses a sum-expression
func (p *Parser) ParseSumExpression() ast.Expression {
p.Log()
var exp ast.Expression

exp = p.This.ParseProdExpression()
var ops []string
expectedType := ast.TypeSymbol

switch idx {
case 0:
ops = []string{"or", "and"}
expectedType = ast.TypeKeyword
break
case 1:
ops = []string{"==", "!="}
break
case 2:
ops = []string{"<=", ">=", "<", ">"}
break
case 3:
ops = []string{"+", "-"}
break
case 4:
ops = []string{"*", "/", "%", "^"}
break
default:
exp = p.This.ParseUnaryExpression()
return exp
}

idx++

exp = p.This.ParseBinaryExpression(idx)
if exp == nil {
return nil
}
logOps := []string{"+", "-"}

for p.IsCurrentType(ast.TypeSymbol) && p.IsCurrentValueIn(logOps) {
for p.IsCurrentType(expectedType) && p.IsCurrentValueIn(ops) {
binexp := &ast.BinaryOperation{
Operator: p.CurrentToken.Value,
Exp1: exp,
}
p.Advance()
binexp.Exp2 = p.This.ParseProdExpression()
if binexp.Exp2 == nil {
p.ErrorCurrent(fmt.Sprintf("Expected expression on right side of %s", binexp.Operator))
}
exp = binexp
}
return exp
}

// ParseProdExpression parses a product expression
func (p *Parser) ParseProdExpression() ast.Expression {
p.Log()
var exp ast.Expression

exp = p.This.ParseUnaryExpression()
if exp == nil {
return nil
}
logOps := []string{"*", "/", "%", "^"}

for p.IsCurrentType(ast.TypeSymbol) && p.IsCurrentValueIn(logOps) {
binexp := &ast.BinaryOperation{
Operator: p.CurrentToken.Value,
Exp1: exp,
}
p.Advance()
binexp.Exp2 = p.This.ParseUnaryExpression()
binexp.Exp2 = p.This.ParseBinaryExpression(idx)
if binexp.Exp2 == nil {
p.ErrorCurrent(fmt.Sprintf("Expected expression on right side of %s", binexp.Operator))
}
Expand Down Expand Up @@ -667,3 +614,11 @@ func (p *Parser) Log() {
fmt.Println("Called:", callingFunctionName(), "from line", callingLineNumber(), "with", p.CurrentToken.String())
}
}

// Log logs the visiting of a parsing function
func (p *Parser) LogI(arg int) {
if p.DebugLog {
// Print the name of the function
fmt.Println("Called:", callingFunctionName(), "from line", callingLineNumber(), "with", p.CurrentToken.String(), "arg", arg)
}
}
22 changes: 11 additions & 11 deletions pkg/parser/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@ var operatorPriority = map[string]int{
"and": 0,
"==": 1,
"!=": 1,
">=": 1,
"<=": 1,
">": 1,
"<": 1,
"+": 2,
"-": 2,
"*": 3,
"/": 3,
"^": 3,
"%": 3,
"not": 4,
">=": 2,
"<=": 2,
">": 2,
"<": 2,
"+": 3,
"-": 3,
"*": 4,
"/": 4,
"^": 4,
"%": 4,
"not": 5,
}

// end and else are missing here, because unlike other keywords they might require a space after them
Expand Down

0 comments on commit a894225

Please sign in to comment.