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); }