- Infix operator parsing is now possible.
git-svn-id: https://svn.tlawal.org/svn/monkey@25 f6afcba9-9ef1-4bdd-9b72-7484f5705bac
This commit is contained in:
parent
2253d6b02c
commit
e5d95dc7b6
21
ast/ast.go
21
ast/ast.go
@ -64,6 +64,13 @@ type PrefixExpression struct {
|
|||||||
Right Expression
|
Right Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InfixExpression struct {
|
||||||
|
Token token.Token // operator tokens i.e. +, -, *, /
|
||||||
|
Left Expression
|
||||||
|
Operator string
|
||||||
|
Right Expression
|
||||||
|
}
|
||||||
|
|
||||||
// Let Statements
|
// Let Statements
|
||||||
func (ls *LetStatement) statement_node() {}
|
func (ls *LetStatement) statement_node() {}
|
||||||
|
|
||||||
@ -161,3 +168,17 @@ func (pe *PrefixExpression) String() string {
|
|||||||
|
|
||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Infix Expression
|
||||||
|
func (ie *InfixExpression) expression_node() {}
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PRECEDENCE of operations
|
// Precedence of operations
|
||||||
const (
|
const (
|
||||||
_ int = iota // iota means start from 0, hence _ starts from 0
|
_ int = iota // iota means start from 0, hence _ starts from 0
|
||||||
LOWEST
|
LOWEST
|
||||||
@ -20,6 +20,32 @@ const (
|
|||||||
CALL // simple_function(x)
|
CALL // simple_function(x)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Precedence Table
|
||||||
|
var precedences = map[token.TokenType]int{
|
||||||
|
token.EQ: EQUALS,
|
||||||
|
token.NOT_EQ: EQUALS,
|
||||||
|
token.LT: LESSGREATER,
|
||||||
|
token.GT: LESSGREATER,
|
||||||
|
token.PLUS: SUM,
|
||||||
|
token.MINUS: SUM,
|
||||||
|
token.SLASH: PRODUCT,
|
||||||
|
token.ASTERISK: PRODUCT,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l_parser *Parser) peek_precedence() int {
|
||||||
|
if l_parser, ok := precedences[l_parser.peek_token.Type]; ok {
|
||||||
|
return l_parser
|
||||||
|
}
|
||||||
|
return LOWEST
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l_parser *Parser) current_precedence() int {
|
||||||
|
if l_parser, ok := precedences[l_parser.current_token.Type]; ok {
|
||||||
|
return l_parser
|
||||||
|
}
|
||||||
|
return LOWEST
|
||||||
|
}
|
||||||
|
|
||||||
// Pratt Parsing
|
// Pratt Parsing
|
||||||
type (
|
type (
|
||||||
prefix_parse_function func() ast.Expression
|
prefix_parse_function func() ast.Expression
|
||||||
@ -44,12 +70,24 @@ func New(l_lexer *lexer.Lexer) *Parser {
|
|||||||
l_parser.next_token()
|
l_parser.next_token()
|
||||||
l_parser.next_token()
|
l_parser.next_token()
|
||||||
|
|
||||||
|
// Prefix Operations
|
||||||
l_parser.prefix_parse_functions = make(map[token.TokenType]prefix_parse_function)
|
l_parser.prefix_parse_functions = make(map[token.TokenType]prefix_parse_function)
|
||||||
l_parser.register_prefix(token.IDENT, l_parser.parse_identifier)
|
l_parser.register_prefix(token.IDENT, l_parser.parse_identifier)
|
||||||
l_parser.register_prefix(token.INT, l_parser.parse_integer_literal)
|
l_parser.register_prefix(token.INT, l_parser.parse_integer_literal)
|
||||||
l_parser.register_prefix(token.BANG, l_parser.parse_prefix_expression)
|
l_parser.register_prefix(token.BANG, l_parser.parse_prefix_expression)
|
||||||
l_parser.register_prefix(token.MINUS, l_parser.parse_prefix_expression)
|
l_parser.register_prefix(token.MINUS, l_parser.parse_prefix_expression)
|
||||||
|
|
||||||
|
// Infix Operation
|
||||||
|
l_parser.infix_parse_functions = make(map[token.TokenType]infix_parse_function)
|
||||||
|
l_parser.register_infix(token.PLUS, l_parser.parse_infix_expression)
|
||||||
|
l_parser.register_infix(token.MINUS, l_parser.parse_infix_expression)
|
||||||
|
l_parser.register_infix(token.SLASH, l_parser.parse_infix_expression)
|
||||||
|
l_parser.register_infix(token.ASTERISK, l_parser.parse_infix_expression)
|
||||||
|
l_parser.register_infix(token.EQ, l_parser.parse_infix_expression)
|
||||||
|
l_parser.register_infix(token.NOT_EQ, l_parser.parse_infix_expression)
|
||||||
|
l_parser.register_infix(token.LT, l_parser.parse_infix_expression)
|
||||||
|
l_parser.register_infix(token.GT, l_parser.parse_infix_expression)
|
||||||
|
|
||||||
return l_parser
|
return l_parser
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,6 +210,7 @@ func (l_parser *Parser) parse_integer_literal() ast.Expression {
|
|||||||
return literal
|
return literal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Here lies the heart of Pratt Parsing
|
||||||
func (l_parser *Parser) parse_expression(precedence int) ast.Expression {
|
func (l_parser *Parser) parse_expression(precedence int) ast.Expression {
|
||||||
prefix := l_parser.prefix_parse_functions[l_parser.current_token.Type]
|
prefix := l_parser.prefix_parse_functions[l_parser.current_token.Type]
|
||||||
if prefix == nil {
|
if prefix == nil {
|
||||||
@ -179,6 +218,16 @@ func (l_parser *Parser) parse_expression(precedence int) ast.Expression {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
left_expression := prefix()
|
left_expression := prefix()
|
||||||
|
|
||||||
|
for !l_parser.peek_token_is(token.SEMICOLON) && precedence < l_parser.peek_precedence() {
|
||||||
|
infix := l_parser.infix_parse_functions[l_parser.peek_token.Type]
|
||||||
|
if infix == nil {
|
||||||
|
return left_expression
|
||||||
|
}
|
||||||
|
l_parser.next_token()
|
||||||
|
left_expression = infix(left_expression)
|
||||||
|
}
|
||||||
|
|
||||||
return left_expression
|
return left_expression
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,3 +245,16 @@ func (l_parser *Parser) parse_prefix_expression() ast.Expression {
|
|||||||
expression.Right = l_parser.parse_expression(PREFIX)
|
expression.Right = l_parser.parse_expression(PREFIX)
|
||||||
return expression
|
return expression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l_parser *Parser) parse_infix_expression(left ast.Expression) ast.Expression {
|
||||||
|
expression := &ast.InfixExpression{
|
||||||
|
Token: l_parser.current_token,
|
||||||
|
Operator: l_parser.current_token.Literal,
|
||||||
|
Left: left,
|
||||||
|
}
|
||||||
|
precedence := l_parser.current_precedence()
|
||||||
|
l_parser.next_token()
|
||||||
|
expression.Right = l_parser.parse_expression(precedence)
|
||||||
|
|
||||||
|
return expression
|
||||||
|
}
|
||||||
|
@ -235,3 +235,121 @@ func testIntegerLiteral(l_test *testing.T, il ast.Expression, value int64) bool
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParsingInfixExpressions(l_test *testing.T) {
|
||||||
|
infix_tests := []struct {
|
||||||
|
input string
|
||||||
|
left_value int64
|
||||||
|
operator string
|
||||||
|
right_value 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 infix_tests {
|
||||||
|
l_lexer := lexer.New(tt.input)
|
||||||
|
l_parser := New(l_lexer)
|
||||||
|
program := l_parser.ParseProgram()
|
||||||
|
check_parser_errors(l_test, l_parser)
|
||||||
|
|
||||||
|
if len(program.Statements) != 1 {
|
||||||
|
l_test.Fatalf("program.Statements does not contain %d statements, got=%d\n", 1, len(program.Statements))
|
||||||
|
}
|
||||||
|
|
||||||
|
statement, ok := program.Statements[0].(*ast.ExpressionStatement)
|
||||||
|
if !ok {
|
||||||
|
l_test.Fatalf("program.Statements[0] is not ast.ExpressionStatement, got=%T", program.Statements[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
expression, ok := statement.Expression.(*ast.InfixExpression)
|
||||||
|
if !ok {
|
||||||
|
l_test.Fatalf("expression is not ast.InfixExpression, got=%T", statement.Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testIntegerLiteral(l_test, expression.Left, tt.left_value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if expression.Operator != tt.operator {
|
||||||
|
l_test.Fatalf("expression.Operator is not '%s', got=%s", tt.operator, expression.Operator)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testIntegerLiteral(l_test, expression.Right, tt.right_value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOperatorPrecedenceParsing(l_test *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"-a * b",
|
||||||
|
"((-a) * b)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"!-a",
|
||||||
|
"(!(-a))",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a + b + c",
|
||||||
|
"((a + b) + c)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a + b - c",
|
||||||
|
"((a + b) - c)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a * b * c",
|
||||||
|
"((a * b) * c)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a * b / c",
|
||||||
|
"((a * b) / c)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a + b / c",
|
||||||
|
"(a + (b / c))",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a + b * c + d / e - f",
|
||||||
|
"(((a + (b * c)) + (d / e)) - f)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"3 + 4; -5 * 5",
|
||||||
|
"(3 + 4)((-5) * 5)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"5 > 4 == 3 < 4",
|
||||||
|
"((5 > 4) == (3 < 4))",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"5 < 4 != 3 > 4",
|
||||||
|
"((5 < 4) != (3 > 4))",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"3 + 4 * 5 == 3 * 1 + 4 * 5",
|
||||||
|
"((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
l_lexer := lexer.New(tt.input)
|
||||||
|
l_parser := New(l_lexer)
|
||||||
|
program := l_parser.ParseProgram()
|
||||||
|
check_parser_errors(l_test, l_parser)
|
||||||
|
|
||||||
|
actual := program.String()
|
||||||
|
if actual != tt.expected {
|
||||||
|
l_test.Errorf("expected=%q, got=%q", tt.expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user