added statement parsing, missing tests and expression
All checks were successful
Go / build (push) Successful in 20s
All checks were successful
Go / build (push) Successful in 20s
This commit is contained in:
parent
e1137fb8ef
commit
5db30a2a84
25
ast/ast.go
25
ast/ast.go
@ -34,6 +34,29 @@ func (t Type) String() string {
|
|||||||
return string(t)
|
return string(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Program struct {
|
||||||
|
Statements []StatementNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Program) TokenLiteral() string {
|
||||||
|
if len(p.Statements) > 0 {
|
||||||
|
return p.Statements[0].TokenLiteral()
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Program) String() string {
|
||||||
|
out := strings.Builder{}
|
||||||
|
|
||||||
|
for _, statement := range p.Statements {
|
||||||
|
out.WriteString(statement.String(0))
|
||||||
|
out.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.String()
|
||||||
|
}
|
||||||
|
|
||||||
type Block struct {
|
type Block struct {
|
||||||
Token token.Token // the RBrace token
|
Token token.Token // the RBrace token
|
||||||
Statements []StatementNode
|
Statements []StatementNode
|
||||||
@ -66,7 +89,7 @@ type Function struct {
|
|||||||
Arguments []FunctionArgument
|
Arguments []FunctionArgument
|
||||||
ReturnType Type
|
ReturnType Type
|
||||||
HasReturnType bool
|
HasReturnType bool
|
||||||
Block Block
|
Block *Block
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Function) TokenLiteral() string { return f.Token.Literal }
|
func (f *Function) TokenLiteral() string { return f.Token.Literal }
|
||||||
|
@ -48,7 +48,7 @@ func (l *Lexer) readChar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *Lexer) makeToken(t token.TokenType, literal string) token.Token {
|
func (l *Lexer) makeToken(t token.TokenType, literal string) token.Token {
|
||||||
return token.Token{Token: t, Literal: literal, Loc: token.Loc{Line: l.line, Col: l.col}}
|
return token.Token{Type: t, Literal: literal, Loc: token.Loc{Line: l.line, Col: l.col}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDigit(ch byte) bool {
|
func isDigit(ch byte) bool {
|
||||||
@ -81,7 +81,7 @@ func (l *Lexer) readIdentifier() token.Token {
|
|||||||
|
|
||||||
t := token.LookupKeyword(l.input[pos:l.pos])
|
t := token.LookupKeyword(l.input[pos:l.pos])
|
||||||
|
|
||||||
return token.Token{Token: t, Loc: loc, Literal: l.input[pos:l.pos]}
|
return token.Token{Type: t, Loc: loc, Literal: l.input[pos:l.pos]}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Lexer) readNumber() token.Token {
|
func (l *Lexer) readNumber() token.Token {
|
||||||
@ -92,7 +92,7 @@ func (l *Lexer) readNumber() token.Token {
|
|||||||
l.readChar()
|
l.readChar()
|
||||||
}
|
}
|
||||||
|
|
||||||
return token.Token{Token: token.Integer, Loc: loc, Literal: l.input[pos:l.pos]}
|
return token.Token{Type: token.Integer, Loc: loc, Literal: l.input[pos:l.pos]}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Lexer) NextToken() token.Token {
|
func (l *Lexer) NextToken() token.Token {
|
||||||
@ -103,21 +103,23 @@ func (l *Lexer) NextToken() token.Token {
|
|||||||
|
|
||||||
switch l.ch {
|
switch l.ch {
|
||||||
case '\n':
|
case '\n':
|
||||||
tok.Token = token.NewLine
|
tok.Type = token.NewLine
|
||||||
case ';':
|
case ';':
|
||||||
tok.Token = token.Semicolon
|
tok.Type = token.Semicolon
|
||||||
case ':':
|
case ':':
|
||||||
tok.Token = token.Colon
|
tok.Type = token.Colon
|
||||||
|
case ',':
|
||||||
|
tok.Type = token.Comma
|
||||||
case '=':
|
case '=':
|
||||||
tok.Token = token.Equal
|
tok.Type = token.Equal
|
||||||
case '{':
|
case '{':
|
||||||
tok.Token = token.LBrace
|
tok.Type = token.LBrace
|
||||||
case '}':
|
case '}':
|
||||||
tok.Token = token.RBrace
|
tok.Type = token.RBrace
|
||||||
case '(':
|
case '(':
|
||||||
tok.Token = token.LParen
|
tok.Type = token.LParen
|
||||||
case ')':
|
case ')':
|
||||||
tok.Token = token.RParen
|
tok.Type = token.RParen
|
||||||
|
|
||||||
case 0:
|
case 0:
|
||||||
return l.makeToken(token.Eof, "")
|
return l.makeToken(token.Eof, "")
|
||||||
@ -129,7 +131,7 @@ func (l *Lexer) NextToken() token.Token {
|
|||||||
return l.readNumber()
|
return l.readNumber()
|
||||||
}
|
}
|
||||||
|
|
||||||
tok.Token = token.Illegal
|
tok.Type = token.Illegal
|
||||||
}
|
}
|
||||||
|
|
||||||
l.readChar()
|
l.readChar()
|
||||||
|
@ -12,19 +12,19 @@ func TestCorrectTokens(t *testing.T) {
|
|||||||
expectedTokens []token.Token
|
expectedTokens []token.Token
|
||||||
input string
|
input string
|
||||||
}{{
|
}{{
|
||||||
expectedTokens: []token.Token{{Token: token.Eof, Literal: "", Loc: token.Loc{Line: 1, Col: 1}}},
|
expectedTokens: []token.Token{{Type: token.Eof, Literal: "", Loc: token.Loc{Line: 1, Col: 1}}},
|
||||||
input: "",
|
input: "",
|
||||||
}, {input: "hello 1234 ; () {}\n",
|
}, {input: "hello 1234 ; () {}\n",
|
||||||
expectedTokens: []token.Token{
|
expectedTokens: []token.Token{
|
||||||
{Token: token.Identifier, Literal: "hello", Loc: token.Loc{Line: 1, Col: 1}},
|
{Type: token.Identifier, Literal: "hello", Loc: token.Loc{Line: 1, Col: 1}},
|
||||||
{Token: token.Integer, Literal: "1234", Loc: token.Loc{Line: 1, Col: 7}},
|
{Type: token.Integer, Literal: "1234", Loc: token.Loc{Line: 1, Col: 7}},
|
||||||
{Token: token.Semicolon, Literal: ";", Loc: token.Loc{Line: 1, Col: 12}},
|
{Type: token.Semicolon, Literal: ";", Loc: token.Loc{Line: 1, Col: 12}},
|
||||||
{Token: token.LParen, Literal: "(", Loc: token.Loc{Line: 1, Col: 14}},
|
{Type: token.LParen, Literal: "(", Loc: token.Loc{Line: 1, Col: 14}},
|
||||||
{Token: token.RParen, Literal: ")", Loc: token.Loc{Line: 1, Col: 15}},
|
{Type: token.RParen, Literal: ")", Loc: token.Loc{Line: 1, Col: 15}},
|
||||||
{Token: token.LBrace, Literal: "{", Loc: token.Loc{Line: 1, Col: 17}},
|
{Type: token.LBrace, Literal: "{", Loc: token.Loc{Line: 1, Col: 17}},
|
||||||
{Token: token.RBrace, Literal: "}", Loc: token.Loc{Line: 1, Col: 18}},
|
{Type: token.RBrace, Literal: "}", Loc: token.Loc{Line: 1, Col: 18}},
|
||||||
{Token: token.NewLine, Literal: "\n", Loc: token.Loc{Line: 2, Col: 1}},
|
{Type: token.NewLine, Literal: "\n", Loc: token.Loc{Line: 2, Col: 1}},
|
||||||
{Token: token.Eof, Literal: "", Loc: token.Loc{Line: 2, Col: 2}},
|
{Type: token.Eof, Literal: "", Loc: token.Loc{Line: 2, Col: 2}},
|
||||||
}}}
|
}}}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@ -36,8 +36,8 @@ func TestCorrectTokens(t *testing.T) {
|
|||||||
t.Errorf("Literal is not equal: actual = (%v) is not expected = (%v)", actual.Literal, expected.Literal)
|
t.Errorf("Literal is not equal: actual = (%v) is not expected = (%v)", actual.Literal, expected.Literal)
|
||||||
}
|
}
|
||||||
|
|
||||||
if expected.Token != actual.Token {
|
if expected.Type != actual.Type {
|
||||||
t.Errorf("Token is not equal: actual = (%v) is not expected = (%v)", actual.Token, expected.Token)
|
t.Errorf("Token is not equal: actual = (%v) is not expected = (%v)", actual.Type, expected.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if expected.Loc.Line != actual.Loc.Line {
|
if expected.Loc.Line != actual.Loc.Line {
|
||||||
|
162
parser/parser.go
162
parser/parser.go
@ -1 +1,163 @@
|
|||||||
package parser
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.robaertschi.xyz/robaertschi/thorgot/ast"
|
||||||
|
"git.robaertschi.xyz/robaertschi/thorgot/lexer"
|
||||||
|
"git.robaertschi.xyz/robaertschi/thorgot/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Parser struct {
|
||||||
|
lexer lexer.Lexer
|
||||||
|
curToken token.Token
|
||||||
|
peekToken token.Token
|
||||||
|
|
||||||
|
Errors []error
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(lexer lexer.Lexer) Parser {
|
||||||
|
p := Parser{}
|
||||||
|
|
||||||
|
p.nextToken()
|
||||||
|
p.nextToken()
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) error(err error) ast.StatementNode {
|
||||||
|
p.Errors = append(p.Errors, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) expectPeek(t token.TokenType) bool {
|
||||||
|
if p.peekToken.Type == t {
|
||||||
|
p.nextToken()
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
p.peekError(t)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) peekTokenIs(t token.TokenType) bool {
|
||||||
|
return p.peekToken.Type == t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) peekError(t token.TokenType) {
|
||||||
|
err := fmt.Errorf("Expected token %v to be %v", t, p.peekToken.Type)
|
||||||
|
p.Errors = append(p.Errors, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) nextToken() {
|
||||||
|
p.curToken = p.peekToken
|
||||||
|
p.peekToken = p.lexer.NextToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) ParseProgram() ast.Program {
|
||||||
|
program := ast.Program{}
|
||||||
|
program.Statements = make([]ast.StatementNode, 0)
|
||||||
|
|
||||||
|
for p.curToken.Type != token.Eof {
|
||||||
|
stmt := p.parseStatement()
|
||||||
|
if stmt != nil {
|
||||||
|
program.Statements = append(program.Statements, stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return program
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseStatement() ast.StatementNode {
|
||||||
|
switch p.curToken.Type {
|
||||||
|
case token.Fn:
|
||||||
|
return p.parseFunction()
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.error(fmt.Errorf("Invalid token %v found with literal %v", p.curToken.Type, p.curToken.Literal))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseFunctionArguments() []ast.FunctionArgument {
|
||||||
|
args := make([]ast.FunctionArgument, 0)
|
||||||
|
|
||||||
|
for p.peekTokenIs(token.Identifier) {
|
||||||
|
p.nextToken()
|
||||||
|
name := p.curToken.Literal
|
||||||
|
|
||||||
|
if !p.expectPeek(token.Identifier) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, ast.FunctionArgument{Name: name, Type: ast.Type(p.curToken.Literal)})
|
||||||
|
|
||||||
|
if !p.peekTokenIs(token.Comma) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p.nextToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.expectPeek(token.RParen) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseFunction() *ast.Function {
|
||||||
|
f := &ast.Function{Token: p.curToken}
|
||||||
|
|
||||||
|
if !p.expectPeek(token.Identifier) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Name = p.curToken.Literal
|
||||||
|
|
||||||
|
if !p.expectPeek(token.LParen) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
args := p.parseFunctionArguments()
|
||||||
|
|
||||||
|
if args == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Arguments = args
|
||||||
|
|
||||||
|
if p.peekTokenIs(token.Identifier) {
|
||||||
|
p.nextToken()
|
||||||
|
f.ReturnType = ast.Type(p.curToken.Literal)
|
||||||
|
f.HasReturnType = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.expectPeek(token.LBrace) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse block
|
||||||
|
|
||||||
|
f.Block = p.parseBlock()
|
||||||
|
if f.Block == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseBlock() *ast.Block {
|
||||||
|
b := &ast.Block{Token: p.curToken}
|
||||||
|
// skip {
|
||||||
|
p.nextToken()
|
||||||
|
|
||||||
|
for p.curToken.Type != token.RBrace {
|
||||||
|
stmt := p.parseStatement()
|
||||||
|
if stmt == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
b.Statements = append(b.Statements, stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.nextToken()
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@ type Loc struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Token struct {
|
type Token struct {
|
||||||
Token TokenType
|
Type TokenType
|
||||||
Literal string
|
Literal string
|
||||||
Loc Loc
|
Loc Loc
|
||||||
}
|
}
|
||||||
@ -21,6 +21,7 @@ const (
|
|||||||
|
|
||||||
Semicolon = "Semicolon" // ;
|
Semicolon = "Semicolon" // ;
|
||||||
Colon = "Colon" // :
|
Colon = "Colon" // :
|
||||||
|
Comma = "," // ,
|
||||||
Equal = "Equal" // =
|
Equal = "Equal" // =
|
||||||
LBrace = "LBrace" // {
|
LBrace = "LBrace" // {
|
||||||
RBrace = "RBrace" // }
|
RBrace = "RBrace" // }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user