From cf651aa1b89fb9bb5bb24b22a3f4ff688a066363 Mon Sep 17 00:00:00 2001 From: tijani Date: Sun, 22 May 2022 06:51:19 +0000 Subject: [PATCH] ast and parser git-svn-id: https://svn.tlawal.org/svn/monkey@7 f6afcba9-9ef1-4bdd-9b72-7484f5705bac --- ast/ast.go | 50 +++++++++++++++++++++++++++++++++ debug.rdbg | Bin 0 -> 243 bytes parser/parser.go | 39 ++++++++++++++++++++++++++ parser/parser_test.go | 63 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 ast/ast.go create mode 100644 debug.rdbg create mode 100644 parser/parser.go create mode 100644 parser/parser_test.go diff --git a/ast/ast.go b/ast/ast.go new file mode 100644 index 0000000..3080356 --- /dev/null +++ b/ast/ast.go @@ -0,0 +1,50 @@ +package ast + +import "monkey/token" + +type Node interface { + TokenLiteral() string +} + +type Statement interface { + Node + statementNode() +} + +type Expression interface { + Node + expressionNode() +} + +type Identifier struct { + Token token.Token // the token.IDENT token + Value string +} + +type Program struct { + Statements []Statement +} + +type LetStatement struct { + Token token.Token // the token.LET token + Name *Identifier + Value Expression +} + +func (ls *LetStatement) stamementNode() {} +func (ls *LetStatement) TokenLiteral() string { + return ls.Token.Literal +} + +func (i *Identifier) expressionNode() {} +func (i *Identifier) TokenLiteral() string { + return i.Token.Literal +} + +func (p *Program) TokenLiteral() string { + if len(p.Statements) > 0 { + return p.Statements[0].TokenLiteral() + } else { + return "" + } +} diff --git a/debug.rdbg b/debug.rdbg new file mode 100644 index 0000000000000000000000000000000000000000..9e53659f4af7495b84e885b5536e8f4e99852c46 GIT binary patch literal 243 zcmWG?adH=8U|?{uipkB-%TBF~$xY16(@U*L1qy-zBajpXVl)-$`5*}pz^OnOA`ey| h{Vm)BrU9-C#9)Ui!bO2J!7wg0U~#Y_4!lYrk^m{D7V`iA literal 0 HcmV?d00001 diff --git a/parser/parser.go b/parser/parser.go new file mode 100644 index 0000000..b2a0a4e --- /dev/null +++ b/parser/parser.go @@ -0,0 +1,39 @@ +/* NOTE(tijani): + * l_ in variables names stand for local_ identifiers. The reason is because I do not like + * single letter variable names, but I also do not want to confuse the myself + * when I come back to reading this code after a while hence l_ to signify that it is local to that function. +*/ + +package parser + +import ( + "monkey/ast" + "monkey/lexer" + "monkey/token" +) + +type Parser struct { + l_lexer *lexer.Lexer + current_token token.Token + peek_token token.Token +} + +func (l_parser *Parser) next_token(){ + l_parser.current_token = l_parser.peek_token + l_parser.peek_token = l_parser.l_lexer.NextToken() +} + +func (p *Parser) ParseProgram() *ast.Program { + return nil +} + +func New(lexer *lexer.Lexer) *Parser { + l_parser := &Parser {l_lexer: lexer} + // Read two tokens so current_token and peek_token are set. + // NOTE(tijani): the first time l_parser.next_token() is called, current_token and peek_token will be pointing to the same token. + l_parser.next_token() + l_parser.next_token() + + return l_parser +} + diff --git a/parser/parser_test.go b/parser/parser_test.go new file mode 100644 index 0000000..9f24739 --- /dev/null +++ b/parser/parser_test.go @@ -0,0 +1,63 @@ +package parser + +import ( + "monkey/ast" + "monkey/lexer" + "testing" +) + +func TestLetStatement(t *testing.T) { + input := ` + let x = 5; + let y = 10; + let foobar = 878688; + ` + l_lexer := lexer.New(input) + l_parser = New(l_lexer) + + program := l_parser.ParseProgram() + if program == nil { + t.Fatalf("ParseProgram() returned nil") + } + if len(program.Statements) != 3 { + t.Fatalf("program.Statements does not contain 3 statements, got=%d", len(program.Statements)) + } + + tests := []struct { + expetedIdentifier string + }{ + {"x"}, + {"y"}, + {"foobar"}, + } + + for i, tt := range tests { + stmt := program.Statements[i] + if !testLetStatement(t, stmt, tt.expectedIdentifier) { + return + } + } +} + +func testLetStatement(t *testing.T, s ast.Statement, name string) bool { + if s.TokenLiteral() != "let" { + t.Errorf("s.TokenLiteral not 'let', got=%q", s.TokenLiteral()) + return false + } + + letStmt, ok := s.(*ast.LetStatement) + if !ok { + t.Errorf("s not *ast.LetStatement, got=%T", s) + return false + } + + if letStmt.Name.Value != name { + t.Errorf("letStmt.Name.Value not '%s', got=%s", name, letStmt.Name.Value) + return false + } + + if letStmt.Name.TokenLiteral() != name { + e.Errorf("letStmt.Name.TokenLiteral() not '%s', got=%s", name, letStmt.Name.TokenLiteral()) + } + return true +}