1
\$\begingroup\$

Code for grammar

grammar EpicLang;
Break : 'break';
Do : 'do';
Continue : 'continue';
Else : 'else';
Func : 'func';
For : 'for';
If : 'if';
Then : 'then';
Len : 'len';
NoneObj : 'none';
Print : 'print';
Return : 'return';
While : 'while';
TrueLiteral : 'true';
FalseLiteral : 'false';
// Lexer rules
LINE_COMMENT
 : '//' ~[\r\n]* -> skip;
NUMBER: [0-9]+;
SPACE: [ \t\n]+ -> skip;
VARIABLE: [a-zA-Z_][a-zA-Z0-9_]*;
// Parser rules
program: functionDecl* EOF;
block: '{' stmt* '}';
identifier: var=VARIABLE;
functionDecl: Func (name=VARIABLE) '(' (identifier (',' identifier)* )? ')' block;
stmt: block # BlockStmt
 | Print expr ';'# PrintStmt
 | Continue ';' # ContinueStmt
 | Break ';' # BreakStmt
 | Return expr? ';' # ReturnStmt
 | If expr Then stmt # IfStmt
 | If expr Then stmt Else stmt # IfElseStmt
 | For var=VARIABLE 'in' (lbound=expr) '..' (ubound=expr) Do stmt # ForStmt
 | While expr Do stmt # WhileStmt
 | (var=VARIABLE) '=' (rhs=expr) ';'# AssignStmt
 | expr '[' expr ']' '=' expr # AssignIdxStmt
 | expr ';' # SingleStmt
 | ';' # NullStmt
 ;
expr: (name=VARIABLE) '(' (expr (',' expr)*)? ')' # FuncCall
 | '(' expr ')' # ParenExpr
 | expr '[' expr ']' # IndexExpr
 | op=('+' | '-' | '!' | Len ) expr # UnaryExpr
 | expr op=('*' | '/' | '%' ) expr # BinExpr
 | expr op=('+' | '-') expr # BinExpr
 | expr op=('<' | '<=' | '>' | '>=' | '==' | '!=') expr #BinExpr
 | expr op=('&' | '|') expr #BinExpr
 | '[' (expr (',' expr)*)? ']' #ListExpr
 | TrueLiteral #TrueLiteral
 | FalseLiteral #FalseLiteral
 | num=NUMBER # NumExpr
 | var=VARIABLE # VarExpr
 | NoneObj # NoneExpr
 ;

Code for Interpreter

import sys
from antlr4 import FileStream, CommonTokenStream
from antlr4.error.ErrorListener import ErrorListener, ConsoleErrorListener
from EpicLangLexer import EpicLangLexer
from EpicLangParser import EpicLangParser
from EpicLangVisitor import EpicLangVisitor
class BreakException(Exception):
 pass
class ContinueException(Exception):
 pass
class ReturnException(Exception):
 pass
# handler for syntax errors
class ErrorHandler(ErrorListener):
 def syntaxError(self, *args):
 print('syntax error')
 sys.exit(0)
class FunctionVisitor(EpicLangVisitor):
 def __init__(self):
 self.vars = {}
 self.funcs = {}
 def visitFunctionDecl(self, ctx):
 name = ctx.name.text
 if name in self.funcs:
 print('runtime error')
 exit(0)
 params = ctx.identifier() if hasattr(ctx, 'identifier') else []
 params = [self.visit(x) for x in params]
 self.funcs[name] = (params, ctx.block())
 def visitIdentifier(self, ctx):
 return ctx.var.text
class GoatVisitor(EpicLangVisitor):
 def __init__(self, variables=None, funcs=None):
 self.variables = variables or {}
 self.funcs = funcs or {}
 self.retval = None
 def visitBlock(self, ctx):
 for stmt in ctx.stmt():
 self.visit(stmt)
 def visitPrintStmt(self, ctx):
 print(self.visit(ctx.expr()))
 def visitBinExpr(self, ctx):
 left = self.visit(ctx.expr()[0])
 right = self.visit(ctx.expr()[1])
 if type(left) != type(right):
 print('runtime error')
 exit(0)
 op = {
 '+': lambda a, b: a + b,
 '-': lambda a, b: a - b,
 '*': lambda a, b: a * b,
 '/': lambda a, b: a // b,
 '%': lambda a, b: a % b,
 '&' : lambda a, b: a & b,
 '|' : lambda a, b: a | b,
 '<' : lambda a, b: a < b,
 '<=' : lambda a, b: a <= b,
 '>' : lambda a, b: a > b,
 '>=' : lambda a, b: a >= b,
 '==' : lambda a, b: a == b,
 '!=' : lambda a, b: a != b
 }
 try:
 return op[ctx.op.text](left, right)
 except (TypeError, ValueError, ZeroDivisionError):
 print('runtime error')
 exit(0)
 def visitUnaryExpr(self, ctx):
 op = {
 '+' : lambda x: x,
 '-' : lambda x: -x,
 '!' : lambda x: not x,
 'len' : len
 }
 try:
 return op[ctx.op.text](self.visit(ctx.expr()))
 except (TypeError, ValueError, ZeroDivisionError):
 print('runtime error')
 exit(0)
 def visitParenExpr(self, ctx):
 return self.visit(ctx.expr())
 def visitNumExpr(self, ctx):
 return int(ctx.num.text)
 def visitTrueLiteral(self, ctx):
 return True
 def visitFalseLiteral(self, ctx):
 return False
 def visitIfStmt(self, ctx):
 cond = self.visit(ctx.expr())
 if not isinstance(cond, bool):
 print('runtime error')
 exit(0)
 if cond:
 self.visit(ctx.stmt())
 def visitIfElseStmt(self, ctx):
 cond = self.visit(ctx.expr())
 if not isinstance(cond, bool):
 print('runtime error')
 exit(0)
 if cond:
 self.visit(ctx.stmt()[0])
 else:
 self.visit(ctx.stmt()[1])
 def visitWhileStmt(self, ctx):
 while True:
 cond = self.visit(ctx.expr())
 if not isinstance(cond, bool):
 print('runtime error')
 exit(0)
 if not cond:
 break
 try:
 self.visit(ctx.stmt())
 except BreakException:
 break
 except ContinueException:
 continue
 def visitBreakStmt(self, ctx):
 raise BreakException()
 def visitContinueStmt(self, ctx):
 raise ContinueException()
 def visitNoneObj(self, ctx):
 return None
 def visitFuncCall(self, ctx):
 fname = ctx.name.text
 if fname not in self.funcs:
 print('runtime error')
 exit(0)
 (formal_parameters, function_body) = self.funcs[fname]
 actual_parameters = []
 if ctx.expr():
 actual_parameters = [self.visit(param) for param in ctx.expr()]
 if len(actual_parameters) != len(formal_parameters):
 print('runtime error')
 exit(0)
 stack_frame = dict(zip(formal_parameters, actual_parameters))
 visitor = GoatVisitor(stack_frame, self.funcs)
 try:
 visitor.visit(function_body)
 except BreakException:
 print('runtime error')
 exit(0)
 except ContinueException:
 print('runtime error')
 exit(0)
 except ReturnException:
 pass
 return visitor.retval
 def visitReturnStmt(self, ctx):
 x = ctx.expr()
 if x:
 self.retval = self.visit(x)
 raise ReturnException
 def visitForStmt(self, ctx):
 var = ctx.var.text
 lbound = self.visit(ctx.lbound)
 ubound = self.visit(ctx.ubound)
 if (not isinstance(lbound, int)) or (not isinstance(ubound, int)):
 print("runtime error")
 exit(0)
 need_restore = False
 if var in self.variables:
 need_restore = True
 restore_val = self.variables[var]
 for i in range(lbound, ubound):
 self.variables[var] = i
 try:
 self.visit(ctx.stmt())
 except BreakException:
 break
 except ContinueException:
 continue
 if need_restore:
 self.variables[var] = restore_val
 def visitListExpr(self, ctx):
 return [self.visit(x) for x in ctx.expr()] if ctx.expr() else []
 def visitIndexExpr(self, ctx):
 base = self.visit(ctx.expr()[0])
 idx = self.visit(ctx.expr()[1])
 try:
 return base[idx]
 except (TypeError, ValueError, IndexError):
 print("runtime error")
 exit(0)
 def visitVarExpr(self, ctx):
 var = ctx.var.text
 try:
 return self.variables[var]
 except KeyError:
 print("runtime error")
 exit(0)
 def visitAssignStmt(self, ctx):
 var = ctx.var.text
 self.variables[var] = self.visit(ctx.rhs)
 def visitAssignIdxStmt(self, ctx):
 base = self.visit(ctx.expr()[0])
 idx = self.visit(ctx.expr()[1])
 rhs = self.visit(ctx.expr()[2])
 try:
 base[idx] = rhs
 except (TypeError, ValueError, IndexError):
 print("runtime error")
 exit(0)
def main():
 # create input stream
 in_stream = FileStream(sys.argv[1])
 # create a lexer
 lexer = EpicLangLexer(in_stream)
 # add error handling for our lexer
 lexer.addErrorListener(ErrorHandler())
 lexer.removeErrorListener(ConsoleErrorListener.INSTANCE)
 # create a parser with the lexer as an input
 stream = CommonTokenStream(lexer)
 parser = EpicLangParser(stream)
 # add error handling for our parser
 parser.addErrorListener(ErrorHandler())
 parser.removeErrorListener(ConsoleErrorListener.INSTANCE)
 # use the parser to obtain an abstract syntax tree
 tree = parser.program()
 visitor = FunctionVisitor()
 visitor.visit(tree)
 if ('main' not in visitor.funcs) or (visitor.funcs['main'][0]):
 print("runtime error")
 exit(0)
 visitor2 = GoatVisitor({}, visitor.funcs)
 try:
 visitor2.visit(visitor.funcs['main'][1])
 except BreakException:
 print("runtime error")
 exit(0)
 except ContinueException:
 print("runtime error")
if __name__ == "__main__":
 main()

Example program:

func main() {
 count = 500;
 is_prime = [false, false];
 while len(is_prime) < count do
 is_prime = is_prime + [true];
 for i in 2..count do {
 if !is_prime[i] then continue;
 j = i*i;
 while j < count do {
 is_prime[j] = false;
 j = j + i;
 }
 }
 primes = [];
 for i in 0..count do {
 if is_prime[i] then
 primes = primes + [i];
 }
 print(primes);
}
asked Oct 14, 2022 at 18:16
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Sample code for the language would be helpful. \$\endgroup\$ Commented Oct 14, 2022 at 20:34
  • 1
    \$\begingroup\$ github.com/menezesd/epic-lang/tree/master/tests \$\endgroup\$ Commented Oct 14, 2022 at 22:59

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.