aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2023-11-08 17:56:53 +0000
committerBobby <[email protected]>2023-11-08 17:56:53 +0000
commit152e63c1865d8bc1df36f54218cc9286b7fd1ff2 (patch)
tree4c8b996fb65d8b86a0c0d1ee620ff5f13732963e
parentaed19195e4476ed6933eac2d1d748bcdcb4277e7 (diff)
downloadmana-152e63c1865d8bc1df36f54218cc9286b7fd1ff2.tar.xz
mana-152e63c1865d8bc1df36f54218cc9286b7fd1ff2.zip
parse infix operators
-rw-r--r--ast/ast.go27
-rw-r--r--parser/parser.go72
-rw-r--r--parser/parser_test.go57
3 files changed, 154 insertions, 2 deletions
diff --git a/ast/ast.go b/ast/ast.go
index 021b387..1942a68 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -151,6 +151,33 @@ func (pe *PrefixExpression) String() string {
return out.String()
}
+// InfixExpression represents an infix expression.
+
+type InfixExpression struct {
+ Token tokens.Token // the operator token, e.g. +
+ Left Expression
+ Operator string
+ Right Expression
+}
+
+func (ie *InfixExpression) expressionNode() {}
+func (ie *InfixExpression) TokenLiteral() string {
+ return ie.Token.Literal
+}
+func (ie *InfixExpression) String() string {
+ var out bytes.Buffer
+
+
+ out.WriteString("(")
+ out.WriteString(ie.Left.String())
+ out.WriteString(" " + ie.Operator + " ")
+ out.WriteString(ie.Right.String())
+ out.WriteString(")")
+
+ return out.String()
+}
+
+
// IntegerLiteral represents an integer literal.
type IntegerLiteral struct {
Token tokens.Token // the token.INT token
diff --git a/parser/parser.go b/parser/parser.go
index b9fb91f..6645da1 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -20,6 +20,17 @@ const (
CALL // myFunction(X)
)
+var precedences = map[tokens.TokenType]int{
+ tokens.EQ: EQUALS,
+ tokens.NOT_EQ: EQUALS,
+ tokens.LT: LESSGREATER,
+ tokens.GT: LESSGREATER,
+ tokens.PLUS: SUM,
+ tokens.MINUS: SUM,
+ tokens.SLASH: PRODUCT,
+ tokens.ASTERISK: PRODUCT,
+}
+
type (
prefixParseFn func() ast.Expression
infixParseFn func(ast.Expression) ast.Expression
@@ -55,6 +66,17 @@ func New(l *lexer.Lexer) *Parser {
p.registerPrefix(tokens.BANG, p.parsePrefixExpression)
p.registerPrefix(tokens.MINUS, p.parsePrefixExpression)
+ // Initialize the infix parse functions.
+ p.infixParseFns = make(map[tokens.TokenType]infixParseFn)
+ p.registerInfix(tokens.PLUS, p.parseInfixExpression)
+ p.registerInfix(tokens.MINUS, p.parseInfixExpression)
+ p.registerInfix(tokens.SLASH, p.parseInfixExpression)
+ p.registerInfix(tokens.ASTERISK, p.parseInfixExpression)
+ p.registerInfix(tokens.EQ, p.parseInfixExpression)
+ p.registerInfix(tokens.NOT_EQ, p.parseInfixExpression)
+ p.registerInfix(tokens.LT, p.parseInfixExpression)
+ p.registerInfix(tokens.GT, p.parseInfixExpression)
+
return p
}
@@ -193,14 +215,27 @@ func (p *Parser) noPrefixParseFnError(t tokens.TokenType) {
// parseExpression parses an expression.
func (p *Parser) parseExpression(precedence int) ast.Expression {
- var prefix = p.prefixParseFns[p.curToken.Type]
+ prefix := p.prefixParseFns[p.curToken.Type]
if prefix == nil {
p.noPrefixParseFnError(p.curToken.Type)
return nil
}
- var leftExp ast.Expression = prefix()
+ leftExp := prefix()
+
+ for !p.peekTokenIs(tokens.SEMICOLON) && precedence < p.peekPrecedence() {
+ infix := p.infixParseFns[p.peekToken.Type]
+
+ if infix == nil {
+ return leftExp
+ }
+
+ p.nextToken()
+
+ leftExp = infix(leftExp)
+
+ }
return leftExp
}
@@ -219,6 +254,21 @@ func (p *Parser) parsePrefixExpression() ast.Expression {
return expression
}
+// parseInfixExpression parses an infix expression.
+func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
+ expression := &ast.InfixExpression{
+ Token: p.curToken,
+ Operator: p.curToken.Literal,
+ Left: left,
+ }
+
+ precedence := p.curPrecedence()
+ p.nextToken()
+ expression.Right = p.parseExpression(precedence)
+
+ return expression
+}
+
// curTokenIs returns true if the current token is of the given type.
func (p *Parser) curTokenIs(t tokens.TokenType) bool {
return p.curToken.Type == t
@@ -253,3 +303,21 @@ func (p *Parser) peekError(t tokens.TokenType) {
p.errors = append(p.errors, msg)
}
+
+// peek and cur precedences
+
+func (p *Parser) peekPrecedence() int {
+ if p, ok := precedences[p.peekToken.Type]; ok {
+ return p
+ }
+
+ return LOWEST
+}
+
+func (p *Parser) curPrecedence() int {
+ if p, ok := precedences[p.curToken.Type]; ok {
+ return p
+ }
+
+ return LOWEST
+}
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 828db24..d3e2d1d 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -259,3 +259,60 @@ func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
return true
}
+
+// Infix expression tests.
+
+func TestParsingInfixExpressions(t *testing.T) {
+ var infixTests = []struct {
+ input string
+ leftValue int64
+ operator string
+ rightValue int64
+ }{
+ {"5 + 5;", 5, "+", 5},
+ {"5 - 5;", 5, "-", 5},
+ {"5 * 5;", 5, "*", 5},
+ {"5 / 5;", 5, "/", 5},
+ {"5 > 5;", 5, ">", 5},
+ {"5 < 5;", 5, "<", 5},
+ {"5 == 5;", 5, "==", 5},
+ {"5 != 5;", 5, "!=", 5},
+ }
+
+ for _, tt := range infixTests {
+ var l *lexer.Lexer = lexer.New(tt.input)
+ var p *Parser = New(l)
+
+ var program *ast.Program = p.ParseProgram()
+ checkParserErrors(t, p)
+
+
+ if len(program.Statements) != 1 {
+ t.Fatalf("program.Statements does not contain %d statements, got=%d\n", 1, len(program.Statements))
+ }
+
+ stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
+
+ if !ok {
+ t.Fatalf("program.Statements[0] is not ast.ExpressionStatement, got=%T", program.Statements[0])
+ }
+
+ exp, ok := stmt.Expression.(*ast.InfixExpression)
+
+ if !ok {
+ t.Fatalf("exp is not ast.InfixExpression. got=%T", stmt.Expression)
+ }
+
+ if !testIntegerLiteral(t, exp.Left, tt.leftValue) {
+ return
+ }
+
+ if exp.Operator != tt.operator {
+ t.Fatalf("exp.Operator is not '%s', got=%s", tt.operator, exp.Operator)
+ }
+
+ if !testIntegerLiteral(t, exp.Right, tt.rightValue) {
+ return
+ }
+ }
+}