restructure
This commit is contained in:
parent
edcbfde351
commit
eebaadc16e
21 changed files with 937 additions and 626 deletions
206
parser/parser.go
Normal file
206
parser/parser.go
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
"git.red-panda.pet/pandaware/lox-go/ast"
|
||||
"git.red-panda.pet/pandaware/lox-go/lexer"
|
||||
)
|
||||
|
||||
type ParseError interface {
|
||||
Error() string
|
||||
Token() *lexer.Token
|
||||
}
|
||||
|
||||
type SyntaxError struct {
|
||||
token *lexer.Token
|
||||
message string
|
||||
}
|
||||
|
||||
func newSyntaxError(token *lexer.Token, message string) *SyntaxError {
|
||||
return &SyntaxError{token, message}
|
||||
}
|
||||
|
||||
func (s *SyntaxError) Token() *lexer.Token {
|
||||
return s.token
|
||||
}
|
||||
|
||||
func (s *SyntaxError) Error() string {
|
||||
return s.message
|
||||
}
|
||||
|
||||
type operandFunc func() (ast.Expr, error)
|
||||
|
||||
type Parser struct {
|
||||
tokens []*lexer.Token
|
||||
current int
|
||||
}
|
||||
|
||||
func New(tokens []*lexer.Token) *Parser {
|
||||
return &Parser{
|
||||
tokens: tokens,
|
||||
current: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) Parse() (ast.Expr, error) {
|
||||
e, err := p.expression()
|
||||
if err != nil {
|
||||
return e, err
|
||||
}
|
||||
|
||||
return e, err
|
||||
}
|
||||
|
||||
// expression -> equality
|
||||
func (p *Parser) expression() (ast.Expr, error) {
|
||||
return p.equality()
|
||||
}
|
||||
|
||||
func (p *Parser) parseLeftAssocBinOps(operand operandFunc, tokenTypes ...lexer.TokenType) (ast.Expr, error) {
|
||||
e, err := operand()
|
||||
if err != nil {
|
||||
return e, err
|
||||
}
|
||||
|
||||
for p.match(tokenTypes...) {
|
||||
op := p.previous()
|
||||
r, err := operand()
|
||||
if err != nil {
|
||||
return e, err
|
||||
}
|
||||
e = &ast.BinaryExpr{Left: e, Operator: op, Right: r}
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// eqality -> comparison ( ("!=" | "==") comparison )*
|
||||
func (p *Parser) equality() (ast.Expr, error) {
|
||||
return p.parseLeftAssocBinOps(
|
||||
p.comparison,
|
||||
lexer.TokenTypeBangEq, lexer.TokenTypeEqualEqual,
|
||||
)
|
||||
}
|
||||
|
||||
// comparison -> term ( ( ">" | ">=" | "<" | "<=" ) term )*
|
||||
func (p *Parser) comparison() (ast.Expr, error) {
|
||||
return p.parseLeftAssocBinOps(
|
||||
p.term,
|
||||
lexer.TokenTypeGreater, lexer.TokenTypeGreaterEq,
|
||||
lexer.TokenTypeLess, lexer.TokenTypeLessEq,
|
||||
)
|
||||
}
|
||||
|
||||
// term -> factor ( ( "-" | "+" ) factor )*
|
||||
func (p *Parser) term() (ast.Expr, error) {
|
||||
return p.parseLeftAssocBinOps(
|
||||
p.factor,
|
||||
lexer.TokenTypeMinus, lexer.TokenTypePlus,
|
||||
)
|
||||
}
|
||||
|
||||
// factor -> unary ( ( "*" | "/" ) unary )*
|
||||
func (p *Parser) factor() (ast.Expr, error) {
|
||||
return p.parseLeftAssocBinOps(
|
||||
p.unary,
|
||||
lexer.TokenTypeSlash, lexer.TokenTypeStar,
|
||||
)
|
||||
}
|
||||
|
||||
// unary -> ( "!" | "-" ) unary | primary;
|
||||
func (p *Parser) unary() (ast.Expr, error) {
|
||||
if p.match(lexer.TokenTypeBang, lexer.TokenTypeMinus) {
|
||||
op := p.previous()
|
||||
r, err := p.unary()
|
||||
return &ast.UnaryExpr{Operator: op, Right: r}, err
|
||||
}
|
||||
|
||||
return p.primary()
|
||||
}
|
||||
|
||||
// primary -> STRING | NUMBER | "true" | "false" | "nil" | "(" expression ")"
|
||||
func (p *Parser) primary() (ast.Expr, error) {
|
||||
if p.match(lexer.TokenTypeTrue) {
|
||||
return &ast.LiteralExpr{Value: true}, nil
|
||||
}
|
||||
if p.match(lexer.TokenTypeFalse) {
|
||||
return &ast.LiteralExpr{Value: false}, nil
|
||||
}
|
||||
if p.match(lexer.TokenTypeNil) {
|
||||
return &ast.LiteralExpr{Value: nil}, nil
|
||||
}
|
||||
|
||||
if p.match(lexer.TokenTypeString, lexer.TokenTypeNumber) {
|
||||
return &ast.LiteralExpr{Value: p.previous().Literal}, nil
|
||||
}
|
||||
|
||||
if p.match(lexer.TokenTypeLeftParen) {
|
||||
e, err := p.expression()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = p.consume(lexer.TokenTypeRightParen, "expected ')' after expression")
|
||||
return &ast.GroupingExpr{Expr: e}, err
|
||||
}
|
||||
|
||||
return nil, newSyntaxError(p.peek(), "expected expression")
|
||||
}
|
||||
|
||||
func (p *Parser) consume(tokenType lexer.TokenType, msg string) (*lexer.Token, error) {
|
||||
if p.check(tokenType) {
|
||||
return p.advance(), nil
|
||||
}
|
||||
|
||||
return nil, newSyntaxError(p.peek(), msg)
|
||||
}
|
||||
|
||||
func (p *Parser) synchronize() {
|
||||
p.advance()
|
||||
|
||||
for !p.isAtEnd() {
|
||||
if p.previous().Type == lexer.TokenTypeSemicolon {
|
||||
return
|
||||
}
|
||||
|
||||
if lexer.IsKeyword(p.peek()) {
|
||||
return
|
||||
}
|
||||
p.advance()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) match(tokenTypes ...lexer.TokenType) bool {
|
||||
for _, t := range tokenTypes {
|
||||
if p.check(t) {
|
||||
p.advance()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Parser) check(t lexer.TokenType) bool {
|
||||
if p.isAtEnd() {
|
||||
return false
|
||||
}
|
||||
return p.peek().Type == t
|
||||
}
|
||||
|
||||
func (p *Parser) advance() *lexer.Token {
|
||||
if !p.isAtEnd() {
|
||||
p.current += 1
|
||||
}
|
||||
return p.previous()
|
||||
}
|
||||
|
||||
func (p *Parser) isAtEnd() bool {
|
||||
return p.peek().Type == lexer.TokenTypeEOF
|
||||
}
|
||||
|
||||
func (p *Parser) peek() *lexer.Token {
|
||||
return p.tokens[p.current]
|
||||
}
|
||||
|
||||
func (p *Parser) previous() *lexer.Token {
|
||||
return p.tokens[p.current-1]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue