diff options
| author | Bobby <[email protected]> | 2023-11-03 16:17:15 +0000 |
|---|---|---|
| committer | Bobby <[email protected]> | 2023-11-03 16:17:15 +0000 |
| commit | e8a6c163c9a58b69f6f96c373118ac6996637e25 (patch) | |
| tree | d16ab60698adbd9491e5904809c67a4ecc41cd0e /parser | |
| parent | 15f9a15757322221d8bf9a3ecd5d8ce8490ebda6 (diff) | |
| download | mana-e8a6c163c9a58b69f6f96c373118ac6996637e25.tar.xz mana-e8a6c163c9a58b69f6f96c373118ac6996637e25.zip | |
parser:init pratt parser. parser:add prefix, infix and `parseIntegerLiteral`
Diffstat (limited to 'parser')
| -rw-r--r-- | parser/parser.go | 86 | ||||
| -rw-r--r-- | parser/parser_test.go | 70 |
2 files changed, 154 insertions, 2 deletions
diff --git a/parser/parser.go b/parser/parser.go index df15165..32bb4bc 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -5,16 +5,36 @@ import ( "mana/ast" "mana/lexer" "mana/tokens" + "strconv" +) + +// Define the precedence of the operators. +const ( + _ int = iota + LOWEST + EQUALS // == + LESSGREATER // > or < + SUM // + + PRODUCT // * + PREFIX // -X or !X + CALL // myFunction(X) +) + +type ( + prefixParseFn func() ast.Expression + infixParseFn func(ast.Expression) ast.Expression ) // Parser represents a parser. type Parser struct { - l *lexer.Lexer + l *lexer.Lexer + errors []string curToken tokens.Token peekToken tokens.Token - errors []string + prefixParseFns map[tokens.TokenType]prefixParseFn + infixParseFns map[tokens.TokenType]infixParseFn } // New returns a new Parser. @@ -28,6 +48,11 @@ func New(l *lexer.Lexer) *Parser { p.nextToken() p.nextToken() + // Initialize the prefix parse functions. + p.prefixParseFns = make(map[tokens.TokenType]prefixParseFn) + p.registerPrefix(tokens.IDENT, p.parseIdentifier) + p.registerPrefix(tokens.INT, p.parseIntegerLiteral) + return p } @@ -37,6 +62,16 @@ func (p *Parser) nextToken() { p.peekToken = p.l.NextToken() } +// registerPrefix registers a prefix parse function. +func (p *Parser) registerPrefix(tokenType tokens.TokenType, fn prefixParseFn) { + p.prefixParseFns[tokenType] = fn +} + +// registerInfix registers an infix parse function. +func (p *Parser) registerInfix(tokenType tokens.TokenType, fn infixParseFn) { + p.infixParseFns[tokenType] = fn +} + // ParseProgram parses a program. func (p *Parser) ParseProgram() *ast.Program { @@ -69,8 +104,29 @@ func (p *Parser) parseStatement() ast.Statement { case tokens.RETURN: return p.parseReturnStatement() default: + return p.parseExpressionStatement() + } +} + +// parseIdentifier parses an identifier. +func (p *Parser) parseIdentifier() ast.Expression { + return &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal} +} + +// parseIntegerLiteral parses an integer literal. +func (p *Parser) parseIntegerLiteral() ast.Expression { + var lit *ast.IntegerLiteral = &ast.IntegerLiteral{ Token: p.curToken } + + var value, err = strconv.ParseInt(p.curToken.Literal, 0, 64) + if err != nil { + var msg string = fmt.Sprintf("could not parse %q as integer", p.curToken.Literal) + p.errors = append(p.errors, msg) return nil } + + lit.Value = value + + return lit } // parseLetStatement parses a let statement. @@ -113,6 +169,32 @@ func (p *Parser) parseReturnStatement() *ast.ReturnStatement { return stmt } +// parseExpressionStatement parses an expression statement. +func (p *Parser) parseExpressionStatement() *ast.ExpressionStatement { + var stmt *ast.ExpressionStatement = &ast.ExpressionStatement{Token: p.curToken} + + stmt.Expression = p.parseExpression(LOWEST) + + if p.peekTokenIs(tokens.SEMICOLON) { + p.nextToken() + } + + return stmt +} + +// parseExpression parses an expression. +func (p *Parser) parseExpression(precedence int) ast.Expression { + var prefix = p.prefixParseFns[p.curToken.Type] + + if prefix == nil { + return nil + } + + var leftExp ast.Expression = prefix() + + return leftExp +} + // 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 diff --git a/parser/parser_test.go b/parser/parser_test.go index 8e06ecd..cd1d506 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -122,3 +122,73 @@ func TestReturnStatements(t *testing.T) { } } } + +// Identifier expression tests. +func TestIdentifierExpression(t *testing.T) { + var input string = "foobar;" + + var l *lexer.Lexer = lexer.New(input) + var p *Parser = New(l) + var program *ast.Program = p.ParseProgram() + checkParserErrors(t, p) + + if len(program.Statements) != 1 { + t.Fatalf("program has not enough statements. got=%d", 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]) + } + + ident, ok := stmt.Expression.(*ast.Identifier) + + if !ok { + t.Fatalf("exp not *ast.Identifier. got=%T", stmt.Expression) + } + + if ident.Value != "foobar" { + t.Errorf("ident.Value not %s. got=%s", "foobar", ident.Value) + } + + if ident.TokenLiteral() != "foobar" { + t.Errorf("ident.TokenLiteral not %s. got=%s", "foobar", ident.TokenLiteral()) + } +} + +// Integer literal expression tests. +func TestIntegerLiteralExpression(t *testing.T) { + var input string = "5;" + + var l *lexer.Lexer = lexer.New(input) + var p *Parser = New(l) + var program *ast.Program = p.ParseProgram() + checkParserErrors(t, p) + + if len(program.Statements) != 1 { + t.Fatalf("program has not enough statements. got=%d", 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]) + } + + literal, ok := stmt.Expression.(*ast.IntegerLiteral) + + if !ok { + t.Fatalf("exp not *ast.IntegerLiteral. got=%T", stmt.Expression) + } + + + if literal.Value != 5 { + t.Errorf("literal.Value not %d. got=%d", 5, literal.Value) + } + + if literal.TokenLiteral() != "5" { + t.Errorf("literal.TokenLiteral not %s. got=%s", "5", literal.TokenLiteral()) + } +} + |
