Skip to content

About

Monkey Lang using ANTLR4 parser

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

 

History

136 Commits

Folders and files

Repository files navigation

Monkey Lang

The Monkey programming language designed in Writing An Interpreter In Go and Writing a Compiler in Go by Thorsten Ball. I highly recommend picking up a copy of his books.

ANTLR4 parser

  1. Write an ANTLR4 grammer rule file for Monkey Lang:
grammar Monkey;

IDENT  : LETER (LETER | DIFIT)* ;
INT    : '0' | [1-9] DIFIT* ;
STRING : '"' (~["])* '"' ;
WS     : [ \t\r\n]+ -> skip ;

fragment LETER : [a-zA-Z_] ;
fragment DIFIT : [0-9] ;

prog : stat* ;

stat : 'let' IDENT '=' expr  ';'?  # letStat
     | 'return' expr ';'?          # retStat
     | expr ';'?                   # exprStat
     ;

expr : expr '[' expr ']'                          # indexExpr
     | expr '(' exprs ')'                         # callExpr
     | op=('-'|'!') expr                          # unOpExpr
     | expr op=('*'|'/') expr                     # mulDivExpr
     | expr op=('+'|'-') expr                     # addSubExpr
     | expr op=('<'|'>') expr                     # ltGtExpr
     | expr op=('=='|'!=') expr                   # eqNeExpr
     | 'if' '(' expr ')' block ('else' block)?    # ifExpr
     | 'fn' '(' params ')' block                  # fnLit
     | '[' exprs ']'                              # arrLit
     | '{' pairs '}'                              # hashLit
     | IDENT                                      # ident
     | INT                                        # intLit
     | STRING                                     # strLit
     | ('true'|'false')                           # boolLit
     | '(' expr ')'                               # parenExpr
     ;

exprs : expr (',' expr)* | ;

block : '{' stat* '}' ;

params : IDENT (',' IDENT)* | ;

pairs : pair (',' pair)* | ;

pair: expr ':' expr ;
  1. Generate Golang target source code for Lexer & Parser of Monkey Lang:
% java -jar /usr/local/lib/antlr-4.13.1-complete.jar -Dlanguage=Go -o monkey Monkey.g4

% ls monkey 
Monkey.interp
Monkey.tokens
MonkeyLexer.interp
MonkeyLexer.tokens
monkey_base_listener.go
monkey_lexer.go
monkey_listener.go
monkey_parser.go
  1. Extends *monkey.BaseMonkeyListener to translate antlr4 AST to monkey AST:
package parser_antlr4

import (
	"monkey/ast"
	monkey "monkey/parser_antlr4/monkey"
	"monkey/token"
	"strconv"

	"github.com/antlr4-go/antlr/v4"
)

type Parser struct {
	*monkey.BaseMonkeyListener
	*antlr.DefaultErrorListener
	parser *monkey.MonkeyParser

	errors []string
	values map[antlr.ParserRuleContext]any
}

func New(input string) *Parser {
	lexer := monkey.NewMonkeyLexer(antlr.NewInputStream(input))
	parser := monkey.NewMonkeyParser(antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel))

	p := &Parser{
		BaseMonkeyListener:   &monkey.BaseMonkeyListener{},
		DefaultErrorListener: &antlr.DefaultErrorListener{},
		parser:               parser,
		errors:               []string{},
		values:               map[antlr.ParserRuleContext]any{},
	}

	lexer.RemoveErrorListeners()
	lexer.AddErrorListener(p)

	parser.RemoveErrorListeners()
	parser.AddErrorListener(p)

	return p
}

func (p *Parser) setValue(ctx antlr.ParserRuleContext, value any) {
	p.values[ctx] = value
}

func (p *Parser) getValue(ctx antlr.ParserRuleContext) any {
	return p.values[ctx]
}

func (p *Parser) ParseProgram() *ast.Program {
	prog := p.parser.Prog()
	if len(p.errors) > 0 {
		return &ast.Program{
			Statements: []ast.Statement{},
		}
	}
	antlr.NewParseTreeWalker().Walk(p, prog)
	return p.getValue(prog).(*ast.Program)
}

func (p *Parser) Errors() []string {
	return p.errors
}

func (p *Parser) SyntaxError(_ antlr.Recognizer, _ interface{}, line, column int, msg string, _ antlr.RecognitionException) {
	p.errors = append(p.errors, "line "+strconv.Itoa(line)+":"+strconv.Itoa(column)+" "+msg)
}

func (p *Parser) VisitErrorNode(node antlr.ErrorNode) {
	p.errors = append(p.errors, node.GetText())
}

func (p *Parser) ExitProg(ctx *monkey.ProgContext) {
	n := len(ctx.AllStat())
	stats := make([]ast.Statement, n)
	for i := 0; i < n; i++ {
		stats[i] = p.getValue(ctx.Stat(i)).(ast.Statement)
	}
	p.setValue(ctx, &ast.Program{
		Statements: stats,
	})
}

func (p *Parser) ExitLetStat(ctx *monkey.LetStatContext) {
	value := p.getValue(ctx.Expr()).(ast.Expression)
	name := ctx.IDENT().GetText()
	if fnLit, ok := value.(*ast.FunctionLiteral); ok {
		fnLit.Name = name
	}
	p.setValue(ctx, &ast.LetStatement{
		Token: token.Token{Type: token.LET, Literal: "let"},
		Name:  newIdentifier(name),
		Value: value,
	})
}

...

About

Monkey Lang using ANTLR4 parser

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages