runningwater 3 лет назад
Родитель
Сommit
0b113f503d
2 измененных файлов с 130 добавлено и 100 удалено
  1. 72 56
      parser/parser.go
  2. 58 44
      parser/parser_test.go

+ 72 - 56
parser/parser.go

@@ -1,9 +1,10 @@
 package parser
 
 import (
-	"github/runnignwater/monkey/ast"
-	"github/runnignwater/monkey/lexer"
-	"github/runnignwater/monkey/token"
+    "fmt"
+    "github/runnignwater/monkey/ast"
+    "github/runnignwater/monkey/lexer"
+    "github/runnignwater/monkey/token"
 )
 
 /**
@@ -13,85 +14,100 @@ import (
  * @Desc:
  */
 type Parser struct {
-	l *lexer.Lexer // point to the instance of the lexer
+    l *lexer.Lexer // point to the instance of the lexer
 
-	curToken  token.Token // point to the current token
-	peekToken token.Token // point to the next token
+    curToken  token.Token // point to the current token
+    peekToken token.Token // point to the next token
+
+    errors []string
 }
 
 func New(l *lexer.Lexer) *Parser {
-	p := &Parser{l: l}
+    p := &Parser{
+        l:      l,
+        errors: []string{},
+    }
+
+    // Read two tokens, so curToken and peekToken are both set
+    p.nextToken()
+    p.nextToken()
 
-	// Read two tokens, so curToken and peekToken are both set
-	p.nextToken()
-	p.nextToken()
+    return p
+}
+
+func (p *Parser) Errors() []string {
+    return p.errors
+}
 
-	return p
+func (p *Parser) peekError(t token.TypeToken) {
+    msg := fmt.Sprintf("exepected next token to be %s, got %s instead.", t, p.peekToken.Type)
+    p.errors = append(p.errors, msg)
 }
 
 func (p *Parser) nextToken() {
-	p.curToken = p.peekToken
-	p.peekToken = p.l.NextToken()
+    p.curToken = p.peekToken
+    p.peekToken = p.l.NextToken()
 }
 
 func (p *Parser) ParseProgram() *ast.Program {
-	program := &ast.Program{}
-	program.Statements = []ast.Statement{}
-
-	for p.curToken.Type != token.EOF {
-		stmt := p.parseStatement()
-		if stmt != nil {
-			program.Statements = append(program.Statements, stmt)
-		}
-		p.nextToken()
-
-	}
-	return program
+    program := &ast.Program{}
+    program.Statements = []ast.Statement{}
+
+    for !p.curTokenIs(token.EOF) {
+        stmt := p.parseStatement()
+        if stmt != nil {
+            program.Statements = append(program.Statements, stmt)
+        }
+        p.nextToken()
+
+    }
+    return program
 }
 
 func (p *Parser) parseStatement() ast.Statement {
-	switch p.curToken.Type {
-	case token.LET:
-		return p.parseLetStatement()
-	default:
-		return nil
-	}
+    switch p.curToken.Type {
+    case token.LET:
+        return p.parseLetStatement()
+    default:
+        return nil
+    }
 }
 
 // let <identifier> = <expression>;
 func (p *Parser) parseLetStatement() *ast.LetStatement {
-	stmt := &ast.LetStatement{Token: p.curToken}
-
-	if !p.expectPeek(token.IDENT) {
-		return nil
-	}
-
-	stmt.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
-	if !p.expectPeek(token.ASSIGN) {
-		return nil
-	}
-
-	// TODO: we're skipping the expression until we
-	// we encounter a semicolon
-	for !p.curTokenIs(token.SEMICOLON) {
-		p.nextToken()
-	}
-	return stmt
+    stmt := &ast.LetStatement{Token: p.curToken}
+
+    if !p.expectPeek(token.IDENT) {
+        return nil
+    }
+
+    stmt.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
+    if !p.expectPeek(token.ASSIGN) {
+        return nil
+    }
+
+    // TODO: we're skipping the expression until we
+    //       we encounter a semicolon
+    for !p.curTokenIs(token.SEMICOLON) {
+        p.nextToken()
+    }
+    return stmt
 }
 
 func (p *Parser) curTokenIs(t token.TypeToken) bool {
-	return p.curToken.Type == t
+    return p.curToken.Type == t
 }
 
 func (p *Parser) peekTokenIs(t token.TypeToken) bool {
-	return p.peekToken.Type == t
+    return p.peekToken.Type == t
 }
 
 func (p *Parser) expectPeek(t token.TypeToken) bool {
-	if p.peekTokenIs(t) {
-		p.nextToken()
-		return true
-	} else {
-		return false
-	}
+    if p.peekTokenIs(t) {
+        p.nextToken()
+        return true
+    } else {
+        p.peekError(t)
+        return false
+    }
 }

+ 58 - 44
parser/parser_test.go

@@ -1,9 +1,9 @@
 package parser
 
 import (
-	"github/runnignwater/monkey/ast"
-	"github/runnignwater/monkey/lexer"
-	"testing"
+    "github/runnignwater/monkey/ast"
+    "github/runnignwater/monkey/lexer"
+    "testing"
 )
 
 /**
@@ -13,59 +13,73 @@ import (
  * @Desc: LetStatement test case
  */
 func TestLetStatements(t *testing.T) {
-	input := `
+    input := `
         let x = 5;
         let y = 10;
+        
         let foo = 838383;
         `
-	l := lexer.New(input)
-	p := New(l)
+    l := lexer.New(input)
+    p := New(l)
 
-	program := p.ParseProgram()
-	if program == nil {
-		t.Fatalf("ParseProgram() return nil")
-	}
-	if len(program.Statements) != 3 {
-		t.Fatalf("Program.Statements does not contain 3 statements. got=%d", len(program.Statements))
-	}
+    program := p.ParseProgram()
+    checkParseErrors(t, p)
+    if program == nil {
+        t.Fatalf("ParseProgram() return nil")
+    }
+    if len(program.Statements) != 3 {
+        t.Fatalf("Program.Statements does not contain 3 statements. got=%d", len(program.Statements))
+    }
 
-	tests := []struct {
-		expectedIdentifies string
-	}{
-		{"x"},
-		{"y"},
-		{"foo"},
-	}
+    tests := []struct {
+        expectedIdentifies string
+    }{
+        {"x"},
+        {"y"},
+        {"foo"},
+    }
 
-	for i, tt := range tests {
-		stmt := program.Statements[i]
-		if !testLetStatement(t, stmt, tt.expectedIdentifies) {
-			return
-		}
-	}
+    for i, tt := range tests {
+        stmt := program.Statements[i]
+        if !testLetStatement(t, stmt, tt.expectedIdentifies) {
+            return
+        }
+    }
+}
+
+func checkParseErrors(t *testing.T, p *Parser) {
+    errors := p.errors
+    if len(errors) == 0 {
+        return
+    }
+    t.Errorf("parse has %d errors.", len(errors))
+    for _, msg := range errors {
+        t.Errorf("parse error: %q", msg)
+    }
+    t.FailNow()
 }
 
 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
-	}
+    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 is not *ast.LetStatement. got=%T", s)
-		return false
-	}
+    letStmt, ok := s.(*ast.LetStatement)
+    if !ok {
+        t.Errorf("s is 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.Value != name {
+        t.Errorf("letStmt.Name.Value not '%s'. got=%s", name, letStmt.Name.Value)
+        return false
+    }
 
-	if letStmt.Name.TokenLiteral() != name {
-		t.Errorf("s.name not '%s. got=%s", name, letStmt.Name)
-		return false
-	}
+    if letStmt.Name.TokenLiteral() != name {
+        t.Errorf("s.name not '%s. got=%s", name, letStmt.Name)
+        return false
+    }
 
-	return true
+    return true
 }