def run_test_executor(self, instructions, final_stack, initial_environment, final_environment): code = Code() for inst in instructions: code.add_instruction(Instruction(inst[0], *tuple(inst[1:len(inst)]))) env = Environment() for key, value in initial_environment.items(): env.defineVariable(key, value) executor = Executor(env) executor.execute(code) self.assertEqual(len(final_stack), len(executor.stack.stack)) for i in range(0, len(final_stack)): f_elt = final_stack[i] a_elt = executor.stack.stack[i] if(isinstance(f_elt, Object) or isinstance(f_elt, Function)): self.assertEqual(f_elt.__class__, a_elt.__class__) else: self.assertEqual(f_elt, a_elt) for key, value in final_environment.items(): self.assertEqual(env.value(key), value) return (executor.stack.stack, env)
def call(self, that, this, *args): ''' Call the function. This function is usefull since in ECMAScript, a function is an object and it can be called with the function "call". For instance: function MyFunction(arg) { console.log(arg) } MyFunction.call(2) In which case, that is a pointer to MyFunction and this to None. But where it becomes tricky is with: var obj = { member: function(arg) { this.value = arg } } obj.call(2) In which case that contains obj.member and this contains obj. In practice, the that argument can be ignored. In other word: * that is the pointer to the object of the function * this is the pointer to the object (equivalent of self in python) * args is the list of arguments passed to the function ''' # Should you create a new Environment here and add the args to that env? current_env = Environment(self.environment) for i in range(len(args)): current_env.defineVariable(self.args[i], args[i]) # Add 'this' and 'that' to current_env?? current_env.defineVariable('this', this) current_env.defineVariable('that', that) return self.body(current_env)
def run_test_executor(self, instructions, final_stack, initial_environment, final_environment): code = Code() for inst in instructions: code.add_instruction( Instruction(inst[0], *tuple(inst[1:len(inst)]))) env = Environment() for key, value in initial_environment.items(): env.defineVariable(key, value) executor = Executor(env) executor.execute(code) self.assertEqual(len(final_stack), len(executor.stack.stack)) for i in range(0, len(final_stack)): f_elt = final_stack[i] a_elt = executor.stack.stack[i] if (isinstance(f_elt, Object) or isinstance(f_elt, Function)): self.assertEqual(f_elt.__class__, a_elt.__class__) else: self.assertEqual(f_elt, a_elt) for key, value in final_environment.items(): self.assertEqual(env.value(key), value) return (executor.stack.stack, env)
def test_single_environment(self): environment = Environment() self.assertRaises(Utils.UnknownVariable, environment.value, "test") environment.defineVariable("test", 10) self.assertEqual(environment.value("test"), 10) environment.setVariable("test", 5) self.assertEqual(environment.value("test"), 5) self.assertRaises(Utils.UnknownVariable, environment.setVariable, "test2", 5)
def __call__(self, this, *args): ''' Call the function. With the this argument. step by step: create function-environment with correct parent in the local scope create pointer to self (this) zip argument names from function initiation with argument values set all environment variables with its values return function-body(function-arguments) == return f(x) ''' localEnvironment = Environment(self.parent) localEnvironment.defineVariable("this", this) argValuePairs = zip(self.argNames, args) for name, value in argValuePairs: localEnvironment.defineVariable(name, value) return self.body(localEnvironment)
def test_function(self): # # This test that a simple call works # env = Environment() function = Function(["arg1", "arg2", "finalarg"], env, lambda environment: self.func1(environment)) function(None, 10, 2, 5) function.call(None, None, 10, 2, 5) # # This test that "this" is correctly set # env = Environment() function = Function(["arg1", "arg2"], env, lambda environment: self.func2(environment)) function(10, 2, 5) function.call(None, 10, 2, 5) # # This test that global variables works # env = Environment() env.defineVariable("glob", 4) function = Function(["arg1", "arg2"], env, lambda environment: self.func3(environment)) function(10, 2, 5) self.assertEqual(env.value("glob"), 3) self.assertRaises(Utils.UnknownVariable, env.value, "testator") env.setVariable("glob", 4) function.call(None, 10, 2, 5) self.assertEqual(env.value("glob"), 3) self.assertRaises(Utils.UnknownVariable, env.value, "testator") # # This test that the ReturnException is correctly catched in the call function # env = Environment() function = Function(["arg1", "arg2"], env, lambda environment: self.func4(environment)) self.assertEqual(function(None, 2, 5), 7)
def test_chain_environment(self): root_environment = Environment() child_environment = Environment(root_environment) root_environment.defineVariable("test", 5) self.assertEqual(root_environment.value("test"), 5) self.assertEqual(child_environment.value("test"), 5) child_environment.setVariable("test", 2) self.assertEqual(root_environment.value("test"), 2) self.assertEqual(child_environment.value("test"), 2) child_environment.defineVariable("test", 3) self.assertEqual(root_environment.value("test"), 2) self.assertEqual(child_environment.value("test"), 3) child_environment.setVariable("test", 6) self.assertEqual(root_environment.value("test"), 2) self.assertEqual(child_environment.value("test"), 6) child_environment.defineVariable("test2", 4) self.assertRaises(Utils.UnknownVariable, root_environment.value, "test2") self.assertEqual(child_environment.value("test2"), 4) self.assertRaises(Utils.UnknownVariable, root_environment.setVariable, "test2", 5)
class InterpreterVisitor(ECMAScriptVisitor): def __init__(self, environment=Environment(), input=None): self.environment = environment self.environment.defineVariable("console", Console()) self.environment.defineVariable("Math", MathModule()) self.environment.defineVariable("Object", ObjectModule()) def visitTerminal(self, node): if node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.BooleanLiteral: # 54 if node.symbol.text == 'true': return True elif node.symbol.text == 'false': return False elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.DecimalLiteral: # 55 # Not sure if were supposed to ever return int # Changes this because test 02_expression/01_addition expects float as result # Change back to make the implementation of binary_ops easier try: return int(node.symbol.text) except ValueError: return float(node.symbol.text) elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.HexIntegerLiteral: # 56 return float(int(node.symbol.text, 0)) elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.StringLiteral: # 101 # Assume that a string atleast contains ""? return node.symbol.text[1:-1] else: return node.symbol.text # Visit a parse tree produced by ECMAScriptParser#PropertyExpressionAssignment. def visitPropertyExpressionAssignment(self, ctx): # Example of input: <name>:<expression> # NOTE: convert expr to float if int name = ctx.children[0].accept(self) expr = ctx.children[2].accept(self) if isinstance(expr, int): expr = float(expr) return (name, expr) # Visit a parse tree produced by ECMAScriptParser#assignmentOperator. def visitAssignmentOperator(self, ctx): # Returns a lambda which performs the right operator node = ctx.children[0] if node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.Assign: # 11 return lambda x, y: y elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.PlusAssign: # 43 return lambda x, y: x + y elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.MinusAssign: # 44 return lambda x, y: x - y elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.MultiplyAssign: # 40 return lambda x, y: x * y elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.DivideAssign: # 41 return lambda x, y: x / y else: raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#eos. def visitEos(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#program. def visitProgram(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#argumentList. def visitArgumentList(self, ctx): args = [] for c in ctx.children: if (not isinstance(c, antlr4.tree.Tree.TerminalNodeImpl)): # Skip "," value = c.accept(self) if isinstance(value, Property): args.append(value.get()) else: args.append(value) return args # Visit a parse tree produced by ECMAScriptParser#ArgumentsExpression. def visitArgumentsExpression(self, ctx): func = ctx.children[0].accept(self) args = ctx.children[1].accept(self) if (args == None): args = [] res = None if isinstance(func, tuple): res = func[1](func[0], *args) elif str(type(func)) == "<class 'builtin_function_or_method'>": return func(*args) else: res = func(None, *args) if isinstance(res, int): res = float(res) #print('THE RES FROM ARGEXPR:', res) return res # Visit a parse tree produced by ECMAScriptParser#ThisExpression. def visitThisExpression(self, ctx): return self.environment.value("this") # Visit a parse tree produced by ECMAScriptParser#identifierName. def visitIdentifierName(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#BinaryExpression. def visitBinaryExpression(self, ctx): node = ctx.children[1] # Handle AND and OR first to achieve normal-order evaluation if node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.And: # 38 return ctx.children[0].accept(self) and ctx.children[2].accept( self) elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.Or: # 39 return ctx.children[0].accept(self) or ctx.children[2].accept(self) op1 = ctx.children[0].accept(self) op2 = ctx.children[2].accept(self) # Only convert to int if type is float if isinstance(op1, float) and op1 == int(op1): op1 = int(op1) if isinstance(op2, float) and op2 == int(op2): op2 = int(op2) if node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.Plus: # 17 if isinstance(op1, int): return float(op1 + op2) else: return op1 + op2 elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.Minus: # 18 return float(op1 - op2) elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.Multiply: # 21 return float(op1 * op2) elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.Divide: # 22 return float(op1 / op2) elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.Modulus: # 23 return float(op1 % op2) elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.LeftShiftArithmetic: # 25 return float(op1 << op2) elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.RightShiftArithmetic: # 24 return float(op1 >> op2) elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.RightShiftLogical: # 26 return float((op1 % 0x100000000) >> op2) elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.LessThan: # 27 return op1 < op2 elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.MoreThan: # 28 return op1 > op2 elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.LessThanEquals: # 29 return op1 <= op2 elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.GreaterThanEquals: # 30 return op1 >= op2 elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.Equals: # 31 return op1 == op2 elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.NotEquals: # 32 return not op1 == op2 elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.IdentityEquals: # 33 return op1 is op2 elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.IdentityNotEquals: # 34 return op1 is not op2 else: raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#futureReservedWord. def visitFutureReservedWord(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#initialiser. def visitInitialiser(self, ctx): return ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#statementList. def visitStatementList(self, ctx): res = None for c in ctx.children: res = c.accept(self) if res == "BREAK-LOOP": return "BREAK-LOOP" elif res == "CONTINUE-LOOP": # Skip the remaining statements return "CONTINUE-LOOP" elif isinstance(res, tuple) and res[0] == "THROW": return res return res # Visit a parse tree produced by ECMAScriptParser#PropertyGetter. def visitPropertyGetter(self, ctx): name = ctx.children[1].accept(self) getter_body = ctx.children[-2].children[0] def func(env): previous_env = self.environment self.environment = env return_val = getter_body.accept(self) if isinstance(return_val, tuple): return_val = return_val[1] self.environment = previous_env return return_val return ('get', name, Function([], self.environment, func)) #raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#block. def visitBlock(self, ctx): res = ctx.children[1].accept(self) return res # Visit a parse tree produced by ECMAScriptParser#expressionStatement. def visitExpressionStatement(self, ctx): return self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#keyword. def visitKeyword(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#elementList. def visitElementList(self, ctx): array = [] for c in ctx.children: if (not isinstance(c, antlr4.tree.Tree.TerminalNodeImpl)): # Skip "," res = c.accept(self) # NOTE: Conversion of int to float, should it be done here? if isinstance(res, int): res = float(res) array.append(res) return array # Visit a parse tree produced by ECMAScriptParser#numericLiteral. def visitNumericLiteral(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#ForInStatement. def visitForInStatement(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#emptyStatement. def visitEmptyStatement(self, ctx): return # Visit a parse tree produced by ECMAScriptParser#labelledStatement. def visitLabelledStatement(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#PropertySetter. def visitPropertySetter(self, ctx): name = ctx.children[1].accept(self) param = ctx.children[3].accept(self) setter_body = ctx.children[-2].children[0] def func(env): previous_env = self.environment self.environment = env return_value = setter_body.accept(self) self.environment = previous_env return return_value return ('set', name, Function([param], self.environment, func)) #raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#NewExpression. def visitNewExpression(self, ctx): obj = ctx.children[1].accept(self) params = ctx.children[2].accept(self) if params == None: params = [] if isinstance(obj, ObjectModule): return obj elif isinstance(obj, Function): new_obj = ObjectModule() obj(obj, *params) obj2 = new_obj.create(None, obj) setattr(obj2, "prototype", obj) obj = obj2 return obj else: raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#LiteralExpression. def visitLiteralExpression(self, ctx): return self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#ArrayLiteralExpression. def visitArrayLiteralExpression(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#MemberDotExpression. def visitMemberDotExpression(self, ctx): obj = ctx.children[0].accept(self) member = ctx.children[2].accept(self) if isinstance(obj, tuple): obj = obj[0] returnval = None if hasattr(obj, member): returnval = getattr(obj, member) elif isinstance(obj, list) and member == 'length': returnval = float(len(obj)) else: returnval = getattr(getattr(obj, "prototype"), member) if isinstance(returnval, Function): return (obj, returnval) return returnval # Visit a parse tree produced by ECMAScriptParser#withStatement. def visitWithStatement(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#MemberIndexExpression. def visitMemberIndexExpression(self, ctx): obj = ctx.children[0].accept(self) idx = ctx.children[2].accept(self) if (isinstance(obj, Object)): return getattr(obj, str(idx)) else: return obj[int(idx)] # Visit a parse tree produced by ECMAScriptParser#formalParameterList. def visitFormalParameterList(self, ctx): params = [param.accept(self) for param in ctx.children[0::2]] return params # Visit a parse tree produced by ECMAScriptParser#incrementOperator. def visitIncrementOperator(self, ctx): # Return a lambda based on what type of operator if ctx.children[ 0].symbol.type == ECMAScriptLexer.ECMAScriptLexer.PlusPlus: return lambda x: x + 1 elif ctx.children[ 0].symbol.type == ECMAScriptLexer.ECMAScriptLexer.MinusMinus: return lambda x: x - 1 else: raise Utils.UnimplementedVisitorException(ctx) ########################################################################### ############### Helper functions for AssignmentOperatorExpression ######### ########################################################################### def objectExpression(self, ctx, var): member = ctx.children[2].accept(self) op = None val = None if isinstance(ctx.children[3], antlr4.tree.Tree.TerminalNodeImpl): op = ctx.children[4].accept(self) val = ctx.children[5].accept(self) #print(ctx.children[5].children) else: op = ctx.children[3].accept(self) val = ctx.children[4].accept(self) #print(ctx.children[4].children) if isinstance(val, int): val = float(val) if hasattr(var, member): attr = getattr(var, member) if isinstance(attr, Property): attr.set(val) return val setattr(var, member, val) return val def prototypeExpression(self, ctx): prototype = ctx.children[0].accept(self) if isinstance(prototype, tuple): prototype = prototype[0] member = ctx.children[2].accept(self) value = ctx.children[4].accept(self) # if value is an object, add each member if isinstance(value, Object): for attr in dir(value): if attr.startswith('__'): continue val = getattr(value, attr) if isinstance(val, tuple): val = list(val) val[0] = prototype val = (val[0], val[1]) setattr(prototype, attr, val) else: setattr(prototype, member, value) # Visit a parse tree produced by ECMAScriptParser#AssignmentOperatorExpression. def visitAssignmentOperatorExpression(self, ctx): if isinstance( ctx.children[0], ECMAScriptParser.ECMAScriptParser.IdentifierExpressionContext): var = ctx.children[0].accept(self) #print('Type of var:',type(var)) if isinstance(var, Object) or isinstance(var, ObjectModule): return self.objectExpression(ctx, var) elif isinstance(var, Function): return self.prototypeExpression(ctx) else: # Handle the case when assigning to array element #print('IN ARRAY CASE') res = ctx.children[5].accept(self) if isinstance(res, int): res = float(res) var[ctx.children[2].accept(self)] = res return elif isinstance( ctx.children[0], ECMAScriptParser.ECMAScriptParser.ThisExpressionContext): return self.objectExpression(ctx, ctx.children[0].accept(self)) elif isinstance( ctx.children[0], ECMAScriptParser.ECMAScriptParser.MemberDotExpressionContext): return self.prototypeExpression(ctx) var_name = ctx.children[0].accept(self) func = ctx.children[1].accept(self) value = ctx.children[2].accept(self) res = func(self.environment.value(var_name), value) self.environment.setVariable(var_name, res) # Visit a parse tree produced by ECMAScriptParser#PostUnaryAssignmentExpression. def visitPostUnaryAssignmentExpression(self, ctx): var_name = ctx.children[0].accept(self) func = ctx.children[1].accept(self) old_value = self.environment.value(var_name) res = func(old_value) self.environment.setVariable(var_name, res) return float(old_value) # Visit a parse tree produced by ECMAScriptParser#TernaryExpression. def visitTernaryExpression(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#tryStatement. def visitTryStatement(self, ctx): res = ctx.children[1].accept(self) if isinstance( res, tuple ) and res[0] == "THROW": # There was a throw in the try-block var_name, block = ctx.children[2].accept(self) # Create new environment for the catch block self.environment = Environment(self.environment) self.environment.defineVariable(var_name.accept(self), res[1].accept(self)) block.accept(self) # Set back the environment self.environment = self.environment.parent # Check for finally-clause and evaluate if so if len(ctx.children) == 4: ctx.children[3].accept(self) # Visit a parse tree produced by ECMAScriptParser#debuggerStatement. def visitDebuggerStatement(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#DoStatement. def visitDoStatement(self, ctx): ctx.children[1].accept(self) while ctx.children[4].accept(self): if ctx.children[1].accept(self) == "BREAK-LOOP": break #raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#ObjectLiteralExpression. def visitObjectLiteralExpression(self, ctx): obj = Object() # res is a list of tuples res = ctx.children[0].accept(self) for value in res: if len(value) == 3: prop = Property(obj) if value[0] == 'get': prop.getter = value[-1] else: prop.setter = value[-1] setattr(obj, str(value[1]), prop) else: setattr(obj, str(value[0]), value[1]) return obj #raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#arrayLiteral. def visitArrayLiteral(self, ctx): return ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#elision. def visitElision(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#statements. def visitStatements(self, ctx): return self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#UnaryExpression. def visitUnaryExpression(self, ctx): node = ctx.children[0] if node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.Minus: # 18 return -float(ctx.children[1].accept(self)) elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.Plus: # 17 return float(ctx.children[1].accept(self)) elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.BitNot: # 19 return float(~int(ctx.children[1].accept(self))) elif node.symbol.type == ECMAScriptLexer.ECMAScriptLexer.Not: # 20 return not bool(ctx.children[1].accept(self)) else: raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#WhileStatement. def visitWhileStatement(self, ctx): while ctx.children[2].accept(self): res = ctx.children[4].accept(self) if res == "BREAK-LOOP": break #raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#returnStatement. def visitReturnStatement(self, ctx): return ("RETURN", ctx.children[1].accept(self)) # Visit a parse tree produced by ECMAScriptParser#switchStatement. def visitSwitchStatement(self, ctx): val_to_switch = ctx.children[2].accept(self) case_found = False # Indicates a case without break has been found cases = ctx.children[4].accept(self) for case in cases: if val_to_switch == case[0] or case_found: if case[1].accept(self) == "BREAK-LOOP": return case_found = True # Handle default case here if not case_found: for case in cases: if case[0] == "DEFAULT": case[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#expressionSequence. def visitExpressionSequence(self, ctx): return self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#literal. def visitLiteral(self, ctx): res = ctx.children[0].accept(self) return res # Visit a parse tree produced by ECMAScriptParser#variableStatement. def visitVariableStatement(self, ctx): # TODO: should probably somehow check if the variable already has been declared / # perhaps return variable_name and value as a tuple and then check... args = ctx.children[1].accept(self) for name, value in args: self.environment.defineVariable(name, value) #raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#FunctionExpression. def visitFunctionExpression(self, ctx): # Now uses the Function class # Check if it is an anonomous function if ctx.children[ 1].symbol.type == ECMAScriptLexer.ECMAScriptLexer.OpenParen: # 5 # Check if there are any params if isinstance( ctx.children[2], ECMAScriptParser.ECMAScriptParser. FormalParameterListContext): params = ctx.children[2].accept(self) body = ctx.children[5].accept(self) else: params = [] body = ctx.children[4].accept(self) new_func_to_return = Function(params, self.environment, body) setattr(new_func_to_return, "prototype", new_func_to_return) return new_func_to_return elif ctx.children[ 1].symbol.type == ECMAScriptLexer.ECMAScriptLexer.Identifier: # 100 variable declaration func_name = ctx.children[1].accept(self) # Check if there are any params if isinstance( ctx.children[3], ECMAScriptParser.ECMAScriptParser. FormalParameterListContext): params = ctx.children[3].accept(self) body = ctx.children[6].accept(self) else: params = [] body = ctx.children[5].accept(self) new_func = Function(params, self.environment, body) setattr(new_func, "prototype", new_func) self.environment.defineVariable(func_name, new_func) else: raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#defaultClause. def visitDefaultClause(self, ctx): return ("DEFAULT", ctx.children[2]) # Visit a parse tree produced by ECMAScriptParser#statement. def visitStatement(self, ctx): res = ctx.children[0].accept(self) return res class MockExpression(object): def __init__(self): self.v = True def accept(self, ctx): return True # Visit a parse tree produced by ECMAScriptParser#ForStatement. def visitForStatement(self, ctx): # This is working for the tests but there are alot of variations # of the for-loop that will not break, most of which will happen # when there is a variable definition in a statement. expression1 = InterpreterVisitor.MockExpression() expression2 = InterpreterVisitor.MockExpression() expression3 = InterpreterVisitor.MockExpression() # Three different cases to consider: 0,1,2 och 3(all) statements missing if len(ctx.children) == 10: # Not a really good solution, not generic at all... args = ctx.children[3].accept(self) for name, value in args: self.environment.defineVariable(name, value) expression2 = ctx.children[5] expression3 = ctx.children[7] elif len(ctx.children) == 9: # All statements provided expression1 = ctx.children[2] expression2 = ctx.children[4] expression3 = ctx.children[6] elif len(ctx.children) == 8: # One statement missing # Find which one if isinstance(ctx.children[2], antlr4.tree.Tree.TerminalNodeImpl): # First missing expression2 = ctx.children[3] expression3 = ctx.children[5] elif isinstance(ctx.children[4], antlr4.tree.Tree.TerminalNodeImpl): # Sec missing expression1 = ctx.children[2] expression3 = ctx.children[5] else: # third(last) missing expression1 = ctx.children[2] expression2 = ctx.children[4] elif len(ctx.children) == 7: # Two statements missing # Find which one exists if isinstance( ctx.children[2], ECMAScriptParser.ECMAScriptParser. ExpressionSequenceContext): # First provided expression1 = ctx.children[2] elif isinstance( ctx.children[3], ECMAScriptParser.ECMAScriptParser. ExpressionSequenceContext): # Second provided expression2 = ctx.children[3] else: # Third provided expression3 = ctx.children[4] #else: # all missing, no need to do anythin #print("In FOR-LOOP") #raise Utils.UnimplementedVisitorException(ctx) # Evaluate the for-loop: expression1.accept(self) while expression2.accept(self): # Execute the body if ctx.children[-1].accept(self) == "BREAK-LOOP": break # Execute last statement in for-loop expression3.accept(self) # Visit a parse tree produced by ECMAScriptParser#caseBlock. def visitCaseBlock(self, ctx): res = [] for c in ctx.children: if (not isinstance(c, antlr4.tree.Tree.TerminalNodeImpl)): # Skip "," res.append(c.accept(self)) return res #raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#ParenthesizedExpression. def visitParenthesizedExpression(self, ctx): return ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#objectLiteral. def visitObjectLiteral(self, ctx): res = [] for i in range(1, len(ctx.children), 2): res.append(ctx.children[i].accept(self)) return res # raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#throwStatement. def visitThrowStatement(self, ctx): #raise Utils.UnimplementedVisitorException(ctx) return ("THROW", ctx.children[1]) # Visit a parse tree produced by ECMAScriptParser#breakStatement. def visitBreakStatement(self, ctx): #raise Utils.UnimplementedVisitorException(ctx) return "BREAK-LOOP" # Visit a parse tree produced by ECMAScriptParser#ifStatement. def visitIfStatement(self, ctx): if not len(ctx.children) == 5 and not len(ctx.children) == 7: raise Utils.UnimplementedVisitorException(ctx) if ctx.children[2].accept(self): return ctx.children[4].accept(self) elif len(ctx.children) == 7: return ctx.children[6].accept(self) else: # The expression evaluated to false and no else clause: return None # Visit a parse tree produced by ECMAScriptParser#reservedWord. def visitReservedWord(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#variableDeclaration. def visitVariableDeclaration(self, ctx): variable_name = ctx.children[0].accept(self) value = None if len(ctx.children) == 2: value = ctx.children[1].accept(self) #print('######', value) #pprint(vars(value)) return (variable_name, value) #raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#finallyProduction. def visitFinallyProduction(self, ctx): #raise Utils.UnimplementedVisitorException(ctx) return ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#IdentifierExpression. def visitIdentifierExpression(self, ctx): res = self.environment.value(ctx.children[0].accept(self)) if isinstance(res, int): return float(res) else: return res # Visit a parse tree produced by ECMAScriptParser#propertyName. def visitPropertyName(self, ctx): return ctx.children[0].accept(self) raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#catchProduction. def visitCatchProduction(self, ctx): return (ctx.children[2], ctx.children[4]) # Visit a parse tree produced by ECMAScriptParser#continueStatement. def visitContinueStatement(self, ctx): return "CONTINUE-LOOP" # Visit a parse tree produced by ECMAScriptParser#caseClause. def visitCaseClause(self, ctx): return (ctx.children[1].accept(self), ctx.children[3]) # Visit a parse tree produced by ECMAScriptParser#arguments. def visitArguments(self, ctx): if len(ctx.children) == 2: return None else: return ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#variableDeclarationList. def visitVariableDeclarationList(self, ctx): args = [] for c in ctx.children: if (not isinstance(c, antlr4.tree.Tree.TerminalNodeImpl)): # Skip "," args.append(c.accept(self)) return args # Visit a parse tree produced by ECMAScriptParser#functionBody. def visitFunctionBody(self, ctx): # construct the function to be returned so this # body can be called def func(env): previous_environment = self.environment self.environment = Environment(env) for c in ctx.children: res = c.accept(self) if isinstance(res, tuple) and res[0] == "RETURN": res = res[1] break self.environment = previous_environment return res return func # Visit a parse tree produced by ECMAScriptParser#eof. def visitEof(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#UnaryAssignmentExpression. def visitUnaryAssignmentExpression(self, ctx): # It seems as if there is no way to determine if its pre- or postincrement # other than the position of IncrementOperatorContext in children # NOTE: It seem post increment is handled elsewhere... # Handle preincrement first if isinstance(ctx.children[1], antlr4.tree.Tree.TerminalNodeImpl): func = ctx.children[0].accept(self) var_name = ctx.children[1].accept(self) res = func(self.environment.value(var_name)) self.environment.setVariable(var_name, res) return float(res) else: raise Utils.UnimplementedVisitorException(ctx)
class InterpreterVisitor(ECMAScriptVisitor): def __init__(self, environment=Environment(), input=None): self.environment = environment self.environment.defineVariable("console", Console()) self.environment.defineVariable("Math", MathModule()) self.environment.defineVariable("Object", ObjectModule()) self.binaries = { '+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv, '%': operator.mod, '<<': lambda x, y: float(operator.lshift(int(x), int(y))), '>>': lambda x, y: float(operator.rshift(int(x), int(y))), '>>>': lambda x, y: float((int(x) % 0x100000000) >> int(y)), '<': operator.lt, '>': operator.gt, '<=': operator.le, '>=': operator.ge, '==': operator.eq, '!=': operator.ne, '===': lambda x, y: type(x) == type(y) and x == y, '!==': lambda x, y: type(x) != type(y) or x != y, '||': lambda x, y: x or y, '&&': lambda x, y: x and y } self.unaries = { '-': operator.neg, '+': operator.pos, '~': lambda x: float(~int(x)), '!': operator.not_, '++': lambda x: x.__add__(1), '--': lambda x: x.__add__(-1) } self.assignment = { '=': lambda x, y: y, '+=': operator.iadd, '-=': operator.isub, '*=': operator.mul, '/=': operator.truediv } def print_env_chain(self, env): if not env.parent: print("GlobalFrame:", env.variableDictionary) else: print("Frame:", env.variableDictionary) self.print_env_chain(env.parent) def inspector(self, ctx): print("‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾") print("In function: ", inspect.stack()[1].function) self.print_env_chain(self.environment) print("_______________________") print("") print("Begin child list, there are", len(ctx.children), "children.") i = 0 for child in ctx.children: try: typ = type(child.accept(self)) except: typ = "<Unacceptable>" val = child.getText() print(i, str(typ) + ": " + str(val)) i += 1 print("End child list of", inspect.stack()[1].function, "\t.!.") print("") def visitTerminal(self, node): if node.symbol.text == "true": return True elif node.symbol.text == "false": return False elif node.symbol.text[0] == '"' or node.symbol.text[0] == "'": return node.symbol.text[1:-1] elif node.symbol.text[0:2] == "0x": return float.fromhex(node.symbol.text) else: return node.symbol.text # Visit a parse tree produced by ECMAScriptParser#PropertyExpressionAssignment. def visitPropertyExpressionAssignment(self, ctx): return (ctx.children[0].accept(self), ctx.children[2].accept(self)) # Visit a parse tree produced by ECMAScriptParser#assignmentOperator. def visitAssignmentOperator(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#eos. def visitEos(self, ctx): return # Visit a parse tree produced by ECMAScriptParser#program. def visitProgram(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#argumentList. def visitArgumentList(self, ctx): args = [] for c in ctx.children: if (not isinstance(c, antlr4.tree.Tree.TerminalNodeImpl)): # Skip "," args.append(c.accept(self)) return args # Visit a parse tree produced by ECMAScriptParser#ArgumentsExpression. def visitArgumentsExpression(self, ctx): this = self.environment.value(ctx.children[0].getText().split('.')[0]) func = ctx.children[0].accept(self) args = ctx.children[1].accept(self) if (args == None or args == ')'): args = [] if isinstance(func, types.MethodType ) and func.__func__ == ObjectModule.defineProperty: return func(args[0], *args) else: return func(this, *args) # Visit a parse tree produced by ECMAScriptParser#ThisExpression. def visitThisExpression(self, ctx): return self.environment.value(ctx.children[0].accept(self)) # Visit a parse tree produced by ECMAScriptParser#identifierName. def visitIdentifierName(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#BinaryExpression. def visitBinaryExpression(self, ctx): arg1 = ctx.children[0].accept(self) operator = ctx.children[1].accept(self) if operator == "&&": return arg1 and ctx.children[2].accept(self) elif operator == "||": return arg1 or ctx.children[2].accept(self) arg2 = ctx.children[2].accept(self) return self.binaries[operator](arg1, arg2) # Visit a parse tree produced by ECMAScriptParser#futureReservedWord. def visitFutureReservedWord(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#initialiser. def visitInitialiser(self, ctx): return ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#statementList. def visitStatementList(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#PropertyGetter. def visitPropertyGetter(self, ctx): name = ctx.children[1].accept(self) body = ctx.children[5].accept(self) param = Object() param.get = Function([], self.environment, body) return (name, param) # Visit a parse tree produced by ECMAScriptParser#block. def visitBlock(self, ctx): # We don't have to define any environment since we don't have # to implement the let keyword. self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#expressionStatement. def visitExpressionStatement(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#keyword. def visitKeyword(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#elementList. def visitElementList(self, ctx): return self.childrenToList(ctx.children) # Visit a parse tree produced by ECMAScriptParser#numericLiteral. def visitNumericLiteral(self, ctx): return float(self.visitChildren(ctx)) # Visit a parse tree produced by ECMAScriptParser#ForInStatement. def visitForInStatement(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#emptyStatement. def visitEmptyStatement(self, ctx): return # Visit a parse tree produced by ECMAScriptParser#labelledStatement. def visitLabelledStatement(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#PropertySetter. def visitPropertySetter(self, ctx): name = ctx.children[1].accept(self) argName = ctx.children[3].accept(self) body = ctx.children[6].accept(self) param = Object() param.set = Function([argName], self.environment, body) return (name, param) # Visit a parse tree produced by ECMAScriptParser#NewExpression. def visitNewExpression(self, ctx): func = ctx.children[1].accept(self) args = ctx.children[2].accept(self) if (args == None or args == ')'): args = [] if hasattr(func, "prototype") and hasattr(func.prototype, "create"): theNewObject = func.prototype.create(None, func.prototype) else: theNewObject = ObjectModule() self.environment = Environment(self.environment) func(theNewObject, *args) self.environment.defineVariable('this', theNewObject) if self.environment.parent: self.environment = self.environment.parent return theNewObject # Visit a parse tree produced by ECMAScriptParser#LiteralExpression. def visitLiteralExpression(self, ctx): return self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#ArrayLiteralExpression. def visitArrayLiteralExpression(self, ctx): return JSList(ctx.children[0].accept(self)) # Visit a parse tree produced by ECMAScriptParser#MemberDotExpression. def visitMemberDotExpression(self, ctx): obj = ctx.children[0].accept(self) member = ctx.children[2].accept(self) retval = getattr(obj, member) if type(retval) == Property: if isinstance(retval.get(), types.MethodType): return retval.get()() return retval.get() else: return retval # Visit a parse tree produced by ECMAScriptParser#withStatement. def visitWithStatement(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#MemberIndexExpression. def visitMemberIndexExpression(self, ctx): array = ctx.children[0].accept(self) index = ctx.children[2].accept(self) if type(array) == ObjectModule: return array.__dict__[str(index)] else: return array[int(index)] # Visit a parse tree produced by ECMAScriptParser#formalParameterList. def visitFormalParameterList(self, ctx): return self.childrenToList(ctx.children) # Visit a parse tree produced by ECMAScriptParser#incrementOperator. def visitIncrementOperator(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#AssignmentOperatorExpression. def visitAssignmentOperatorExpression(self, ctx): name = ctx.children[0].getText() # We're doing arrays now. if ctx.children[1].getText() == '[': index = ctx.children[2].accept(self) operator = ctx.children[4].accept(self) rhs = ctx.children[5].accept(self) lhs = self.environment.value(name) if type(lhs) == ObjectModule: lhs.__dict__[index] = self.assignment[operator]( lhs.__dict__[index], rhs) value = lhs.__dict__[index] else: lhs[int(index)] = self.assignment[operator](lhs[int(index)], rhs) value = lhs[int(index)] self.environment.setVariable(name, lhs) elif ctx.children[1].getText() == '.': key = ctx.children[2].accept(self) operator = ctx.children[3].accept(self) rhs = ctx.children[4].accept(self) lhs = ctx.children[0].accept(self) if key not in lhs.__dict__: lhs.__dict__[key] = None # Is there a setter there? if type(lhs.__dict__[key]) == Property: lhs.__dict__[key].set(rhs) value = rhs else: lhs.__dict__[key] = self.assignment[operator]( lhs.__dict__[key], rhs) value = lhs.__dict__[key] # If there are nestled objects if '.' in name: rootName = name.split('.')[0] rootObject = self.environment.value(rootName) rootObject.__dict__[name.split('.')[1]] = lhs self.environment.defineVariable(rootName, rootObject) else: self.environment.setVariable(name, lhs) else: operator = ctx.children[1].accept(self) rhs = ctx.children[2].accept(self) lhs = self.environment.value(name) if not self.environment.exists(name) and operator == '=': self.environment.defineGlobal(name) value = self.assignment[operator](lhs, rhs) self.environment.setVariable(name, value) return value # Visit a parse tree produced by ECMAScriptParser#PostUnaryAssignmentExpression. def visitPostUnaryAssignmentExpression(self, ctx): name = ctx.children[0].accept(self) operator = ctx.children[1].accept(self) value = self.environment.value(name) self.environment.setVariable(name, self.unaries[operator](value)) return value # Visit a parse tree produced by ECMAScriptParser#TernaryExpression. def visitTernaryExpression(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#tryStatement. def visitTryStatement(self, ctx): try: ctx.children[1].accept(self) except ESException as e: ctx.children[2].exceptionValue = e.value ctx.children[2].accept(self) if len(ctx.children) == 4: ctx.children[3].accept(self) # Visit a parse tree produced by ECMAScriptParser#debuggerStatement. def visitDebuggerStatement(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#DoStatement. def visitDoStatement(self, ctx): ctx.children[1].accept(self) while ctx.children[4].accept(self): ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#ObjectLiteralExpression. def visitObjectLiteralExpression(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#arrayLiteral. def visitArrayLiteral(self, ctx): return ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#elision. def visitElision(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#statements. def visitStatements(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#UnaryExpression. def visitUnaryExpression(self, ctx): operator = ctx.children[0].accept(self) argument = ctx.children[1].accept(self) return self.unaries[operator](argument) # Visit a parse tree produced by ECMAScriptParser#WhileStatement. def visitWhileStatement(self, ctx): while ctx.children[2].accept(self): try: ctx.children[4].accept(self) except BreakException: break except ContinueException: continue # Visit a parse tree produced by ECMAScriptParser#returnStatement. def visitReturnStatement(self, ctx): returnValue = ctx.children[1].accept(self) raise ReturnException(returnValue) # Visit a parse tree produced by ECMAScriptParser#switchStatement. def visitSwitchStatement(self, ctx): ctx.children[4].chosenValue = ctx.children[2].accept(self) ctx.children[4].accept(self) # Visit a parse tree produced by ECMAScriptParser#expressionSequence. def visitExpressionSequence(self, ctx): return self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#literal. def visitLiteral(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#variableStatement. def visitVariableStatement(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#FunctionExpression. def visitFunctionExpression(self, ctx): argumentIndex = 3 # If we are dealing with a lambda. if ctx.children[1].getText() == '(': argumentIndex -= 1 else: name = ctx.children[1].accept(self) if ctx.children[argumentIndex].getText() == ')': arguments = [] argumentIndex -= 1 else: arguments = ctx.children[argumentIndex].accept(self) body = ctx.children[argumentIndex + 3].accept(self) function = Function(arguments, self.environment, body) if ctx.children[1].getText() == '(': return Function(arguments, self.environment, body) else: self.environment.defineVariable(name, function) # Visit a parse tree produced by ECMAScriptParser#defaultClause. def visitDefaultClause(self, ctx): return (ctx.children[0].accept(self), ctx.children[2]) # Visit a parse tree produced by ECMAScriptParser#statement. def visitStatement(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#ForStatement. def visitForStatement(self, ctx): initialization = ctx.children[2].accept(self) condIndex = 4 if initialization == ';': condIndex -= 1 elif initialization == 'var': condIndex += 1 ctx.children[3].accept(self) condition = ctx.children[condIndex].accept(self) incrementorIndex = condIndex + 2 if condition == ';': incrementorIndex -= 1 incrementor = ctx.children[incrementorIndex].getText() expressionIndex = incrementorIndex + 2 if incrementor == ')': expressionIndex -= 1 while ctx.children[condIndex].accept(self): try: ctx.children[expressionIndex].accept(self) ctx.children[incrementorIndex].accept(self) except BreakException: break except ContinueException: ctx.children[incrementorIndex].accept(self) continue # Visit a parse tree produced by ECMAScriptParser#caseBlock. def visitCaseBlock(self, ctx): keyBodyPairs = collections.OrderedDict() for child in ctx.children[1:-1]: (key, body) = child.accept(self) keyBodyPairs[key] = body keys = list(keyBodyPairs) if ctx.chosenValue in keys: startIndex = keys.index(ctx.chosenValue) elif "default" in keys: startIndex = keys.index("default") bodies = list(keyBodyPairs.values()) for body in bodies[startIndex:]: try: body.accept(self) except BreakException: break # Visit a parse tree produced by ECMAScriptParser#ParenthesizedExpression. def visitParenthesizedExpression(self, ctx): # Return what's between the parenthesis ( what ). return ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#objectLiteral. def visitObjectLiteral(self, ctx): obj = ObjectModule() for key, value in self.childrenToList(ctx.children[1:-1]): if hasattr(value, "get") or hasattr(value, "set"): obj.defineProperty(obj, obj, str(key), value) else: setattr(obj, str(key), value) return obj # Visit a parse tree produced by ECMAScriptParser#throwStatement. def visitThrowStatement(self, ctx): raise ESException(ctx.children[1].accept(self)) # Visit a parse tree produced by ECMAScriptParser#breakStatement. def visitBreakStatement(self, ctx): raise BreakException() # Visit a parse tree produced by ECMAScriptParser#ifStatement. def visitIfStatement(self, ctx): if ctx.children[2].accept(self): ctx.children[4].accept(self) # If there is an else statement elif len(ctx.children) > 6: ctx.children[6].accept(self) # Visit a parse tree produced by ECMAScriptParser#reservedWord. def visitReservedWord(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#variableDeclaration. def visitVariableDeclaration(self, ctx): name = ctx.children[0].accept(self) if len(ctx.children) == 2: value = ctx.children[1].accept(self) else: value = None self.environment.defineVariable(name, value) # Visit a parse tree produced by ECMAScriptParser#finallyProduction. def visitFinallyProduction(self, ctx): ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#IdentifierExpression. def visitIdentifierExpression(self, ctx): return self.environment.value(ctx.children[0].accept(self)) # Visit a parse tree produced by ECMAScriptParser#propertyName. def visitPropertyName(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#catchProduction. def visitCatchProduction(self, ctx): exceptionName = ctx.children[2].accept(self) self.environment.defineVariable(exceptionName, ctx.exceptionValue) ctx.children[4].accept(self) self.environment.removeVariable(exceptionName) # Visit a parse tree produced by ECMAScriptParser#continueStatement. def visitContinueStatement(self, ctx): raise ContinueException # Visit a parse tree produced by ECMAScriptParser#caseClause. def visitCaseClause(self, ctx): return (ctx.children[1].accept(self), ctx.children[3]) # Visit a parse tree produced by ECMAScriptParser#arguments. def visitArguments(self, ctx): return ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#variableDeclarationList. def visitVariableDeclarationList(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#functionBody. def visitFunctionBody(self, ctx): def runFunction(environment): self.environment = Environment(environment) for c in ctx.children: try: c.accept(self) except ReturnException as re: return re.value finally: self.environment = self.environment.parent return runFunction # Visit a parse tree produced by ECMAScriptParser#eof. def visitEof(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#UnaryAssignmentExpression. def visitUnaryAssignmentExpression(self, ctx): operator = ctx.children[0].accept(self) name = ctx.children[1].accept(self) value = self.environment.value(name) self.environment.setVariable(name, self.unaries[operator](value)) return self.environment.value(name) def childrenToList(self, items): return [ item.accept(self) for item in items if not item.getText() == ',' ]
class InterpreterVisitor(ECMAScriptVisitor): def __init__(self, environment = Environment(), input=None): self.environment = environment self.environment.defineVariable("console", Console()) self.environment.defineVariable("Math", MathModule()) self.environment.defineVariable("Object", ObjectModule()) self.binaries = { '+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv, '%': operator.mod, '<<': lambda x, y: float(operator.lshift(int(x), int(y))), '>>': lambda x, y: float(operator.rshift(int(x), int(y))), '>>>': lambda x, y: float((int(x) % 0x100000000) >> int(y)), '<': operator.lt, '>': operator.gt, '<=': operator.le, '>=': operator.ge, '==': operator.eq, '!=': operator.ne, '===': lambda x, y: type(x) == type(y) and x == y, '!==': lambda x, y: type(x) != type(y) or x != y, '||': lambda x, y: x or y, '&&': lambda x, y: x and y } self.unaries = { '-': operator.neg, '+': operator.pos, '~': lambda x: float(~int(x)), '!': operator.not_, '++': lambda x: x.__add__(1), '--': lambda x: x.__add__(-1)} self.assignment = { '=': lambda x, y: y, '+=': operator.iadd, '-=': operator.isub, '*=': operator.mul, '/=': operator.truediv} def print_env_chain(self, env): if not env.parent: print("GlobalFrame:", env.variableDictionary) else: print("Frame:", env.variableDictionary) self.print_env_chain(env.parent) def inspector(self, ctx): print("‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾") print("In function: ", inspect.stack()[1].function) self.print_env_chain(self.environment) print("_______________________") print("") print("Begin child list, there are", len(ctx.children), "children.") i = 0 for child in ctx.children: try: typ = type(child.accept(self)) except: typ = "<Unacceptable>" val = child.getText() print(i, str(typ) + ": " + str(val)) i += 1 print("End child list of", inspect.stack()[1].function, "\t.!.") print("") def visitTerminal(self, node): if node.symbol.text == "true": return True elif node.symbol.text == "false": return False elif node.symbol.text[0] == '"' or node.symbol.text[0] == "'" : return node.symbol.text[1:-1] elif node.symbol.text[0:2] == "0x": return float.fromhex(node.symbol.text) else: return node.symbol.text # Visit a parse tree produced by ECMAScriptParser#PropertyExpressionAssignment. def visitPropertyExpressionAssignment(self, ctx): return (ctx.children[0].accept(self), ctx.children[2].accept(self)) # Visit a parse tree produced by ECMAScriptParser#assignmentOperator. def visitAssignmentOperator(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#eos. def visitEos(self, ctx): return # Visit a parse tree produced by ECMAScriptParser#program. def visitProgram(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#argumentList. def visitArgumentList(self, ctx): args = [] for c in ctx.children: if(not isinstance(c, antlr4.tree.Tree.TerminalNodeImpl)): # Skip "," args.append(c.accept(self)) return args # Visit a parse tree produced by ECMAScriptParser#ArgumentsExpression. def visitArgumentsExpression(self, ctx): this = self.environment.value(ctx.children[0].getText().split('.')[0]) func = ctx.children[0].accept(self) args = ctx.children[1].accept(self) if(args == None or args == ')'): args = [] if isinstance(func, types.MethodType) and func.__func__ == ObjectModule.defineProperty: return func(args[0], *args) else: return func(this, *args) # Visit a parse tree produced by ECMAScriptParser#ThisExpression. def visitThisExpression(self, ctx): return self.environment.value(ctx.children[0].accept(self)) # Visit a parse tree produced by ECMAScriptParser#identifierName. def visitIdentifierName(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#BinaryExpression. def visitBinaryExpression(self, ctx): arg1 = ctx.children[0].accept(self) operator = ctx.children[1].accept(self) if operator == "&&": return arg1 and ctx.children[2].accept(self) elif operator == "||": return arg1 or ctx.children[2].accept(self) arg2 = ctx.children[2].accept(self) return self.binaries[operator](arg1, arg2) # Visit a parse tree produced by ECMAScriptParser#futureReservedWord. def visitFutureReservedWord(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#initialiser. def visitInitialiser(self, ctx): return ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#statementList. def visitStatementList(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#PropertyGetter. def visitPropertyGetter(self, ctx): name = ctx.children[1].accept(self) body = ctx.children[5].accept(self) param = Object() param.get = Function([], self.environment, body) return (name, param) # Visit a parse tree produced by ECMAScriptParser#block. def visitBlock(self, ctx): # We don't have to define any environment since we don't have # to implement the let keyword. self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#expressionStatement. def visitExpressionStatement(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#keyword. def visitKeyword(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#elementList. def visitElementList(self, ctx): return self.childrenToList(ctx.children) # Visit a parse tree produced by ECMAScriptParser#numericLiteral. def visitNumericLiteral(self, ctx): return float(self.visitChildren(ctx)) # Visit a parse tree produced by ECMAScriptParser#ForInStatement. def visitForInStatement(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#emptyStatement. def visitEmptyStatement(self, ctx): return # Visit a parse tree produced by ECMAScriptParser#labelledStatement. def visitLabelledStatement(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#PropertySetter. def visitPropertySetter(self, ctx): name = ctx.children[1].accept(self) argName = ctx.children[3].accept(self) body = ctx.children[6].accept(self) param = Object() param.set = Function([argName], self.environment, body) return (name, param) # Visit a parse tree produced by ECMAScriptParser#NewExpression. def visitNewExpression(self, ctx): func = ctx.children[1].accept(self) args = ctx.children[2].accept(self) if(args == None or args == ')'): args = [] if hasattr(func, "prototype") and hasattr(func.prototype, "create"): theNewObject = func.prototype.create(None, func.prototype) else: theNewObject = ObjectModule() self.environment = Environment(self.environment) func(theNewObject, *args) self.environment.defineVariable('this', theNewObject) if self.environment.parent: self.environment = self.environment.parent return theNewObject # Visit a parse tree produced by ECMAScriptParser#LiteralExpression. def visitLiteralExpression(self, ctx): return self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#ArrayLiteralExpression. def visitArrayLiteralExpression(self, ctx): return JSList(ctx.children[0].accept(self)) # Visit a parse tree produced by ECMAScriptParser#MemberDotExpression. def visitMemberDotExpression(self, ctx): obj = ctx.children[0].accept(self) member = ctx.children[2].accept(self) retval = getattr(obj, member) if type(retval) == Property: if isinstance(retval.get(), types.MethodType): return retval.get()() return retval.get() else: return retval # Visit a parse tree produced by ECMAScriptParser#withStatement. def visitWithStatement(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#MemberIndexExpression. def visitMemberIndexExpression(self, ctx): array = ctx.children[0].accept(self) index = ctx.children[2].accept(self) if type(array) == ObjectModule: return array.__dict__[str(index)] else: return array[int(index)] # Visit a parse tree produced by ECMAScriptParser#formalParameterList. def visitFormalParameterList(self, ctx): return self.childrenToList(ctx.children) # Visit a parse tree produced by ECMAScriptParser#incrementOperator. def visitIncrementOperator(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#AssignmentOperatorExpression. def visitAssignmentOperatorExpression(self, ctx): name = ctx.children[0].getText() # We're doing arrays now. if ctx.children[1].getText() == '[': index = ctx.children[2].accept(self) operator = ctx.children[4].accept(self) rhs = ctx.children[5].accept(self) lhs = self.environment.value(name) if type(lhs) == ObjectModule: lhs.__dict__[index] = self.assignment[operator](lhs.__dict__[index], rhs) value = lhs.__dict__[index] else: lhs[int(index)] = self.assignment[operator](lhs[int(index)], rhs) value = lhs[int(index)] self.environment.setVariable(name, lhs) elif ctx.children[1].getText() == '.': key = ctx.children[2].accept(self) operator = ctx.children[3].accept(self) rhs = ctx.children[4].accept(self) lhs = ctx.children[0].accept(self) if key not in lhs.__dict__: lhs.__dict__[key] = None # Is there a setter there? if type(lhs.__dict__[key]) == Property: lhs.__dict__[key].set(rhs) value = rhs else: lhs.__dict__[key] = self.assignment[operator](lhs.__dict__[key], rhs) value = lhs.__dict__[key] # If there are nestled objects if '.' in name: rootName = name.split('.')[0] rootObject = self.environment.value(rootName) rootObject.__dict__[name.split('.')[1]] = lhs self.environment.defineVariable(rootName, rootObject) else: self.environment.setVariable(name, lhs) else: operator = ctx.children[1].accept(self) rhs = ctx.children[2].accept(self) lhs = self.environment.value(name) if not self.environment.exists(name) and operator == '=': self.environment.defineGlobal(name) value = self.assignment[operator](lhs, rhs) self.environment.setVariable(name, value) return value # Visit a parse tree produced by ECMAScriptParser#PostUnaryAssignmentExpression. def visitPostUnaryAssignmentExpression(self, ctx): name = ctx.children[0].accept(self) operator = ctx.children[1].accept(self) value = self.environment.value(name) self.environment.setVariable(name, self.unaries[operator](value)) return value # Visit a parse tree produced by ECMAScriptParser#TernaryExpression. def visitTernaryExpression(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#tryStatement. def visitTryStatement(self, ctx): try: ctx.children[1].accept(self) except ESException as e: ctx.children[2].exceptionValue = e.value ctx.children[2].accept(self) if len(ctx.children) == 4: ctx.children[3].accept(self) # Visit a parse tree produced by ECMAScriptParser#debuggerStatement. def visitDebuggerStatement(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#DoStatement. def visitDoStatement(self, ctx): ctx.children[1].accept(self) while ctx.children[4].accept(self): ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#ObjectLiteralExpression. def visitObjectLiteralExpression(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#arrayLiteral. def visitArrayLiteral(self, ctx): return ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#elision. def visitElision(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#statements. def visitStatements(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#UnaryExpression. def visitUnaryExpression(self, ctx): operator = ctx.children[0].accept(self) argument = ctx.children[1].accept(self) return self.unaries[operator](argument) # Visit a parse tree produced by ECMAScriptParser#WhileStatement. def visitWhileStatement(self, ctx): while ctx.children[2].accept(self): try: ctx.children[4].accept(self) except BreakException: break except ContinueException: continue # Visit a parse tree produced by ECMAScriptParser#returnStatement. def visitReturnStatement(self, ctx): returnValue = ctx.children[1].accept(self) raise ReturnException(returnValue) # Visit a parse tree produced by ECMAScriptParser#switchStatement. def visitSwitchStatement(self, ctx): ctx.children[4].chosenValue = ctx.children[2].accept(self) ctx.children[4].accept(self) # Visit a parse tree produced by ECMAScriptParser#expressionSequence. def visitExpressionSequence(self, ctx): return self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#literal. def visitLiteral(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#variableStatement. def visitVariableStatement(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#FunctionExpression. def visitFunctionExpression(self, ctx): argumentIndex = 3 # If we are dealing with a lambda. if ctx.children[1].getText() == '(': argumentIndex -= 1 else: name = ctx.children[1].accept(self) if ctx.children[argumentIndex].getText() == ')': arguments = [] argumentIndex -= 1 else: arguments = ctx.children[argumentIndex].accept(self) body = ctx.children[argumentIndex + 3].accept(self) function = Function(arguments, self.environment, body) if ctx.children[1].getText() == '(': return Function(arguments, self.environment, body) else: self.environment.defineVariable(name, function) # Visit a parse tree produced by ECMAScriptParser#defaultClause. def visitDefaultClause(self, ctx): return (ctx.children[0].accept(self), ctx.children[2]) # Visit a parse tree produced by ECMAScriptParser#statement. def visitStatement(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#ForStatement. def visitForStatement(self, ctx): initialization = ctx.children[2].accept(self) condIndex = 4 if initialization == ';': condIndex -= 1 elif initialization == 'var': condIndex += 1 ctx.children[3].accept(self) condition = ctx.children[condIndex].accept(self) incrementorIndex = condIndex + 2 if condition == ';': incrementorIndex -= 1 incrementor = ctx.children[incrementorIndex].getText() expressionIndex = incrementorIndex + 2 if incrementor == ')': expressionIndex -= 1 while ctx.children[condIndex].accept(self): try: ctx.children[expressionIndex].accept(self) ctx.children[incrementorIndex].accept(self) except BreakException: break except ContinueException: ctx.children[incrementorIndex].accept(self) continue # Visit a parse tree produced by ECMAScriptParser#caseBlock. def visitCaseBlock(self, ctx): keyBodyPairs = collections.OrderedDict() for child in ctx.children[1:-1]: (key, body) = child.accept(self) keyBodyPairs[key] = body keys = list(keyBodyPairs) if ctx.chosenValue in keys: startIndex = keys.index(ctx.chosenValue) elif "default" in keys: startIndex = keys.index("default") bodies = list(keyBodyPairs.values()) for body in bodies[startIndex:]: try: body.accept(self) except BreakException: break # Visit a parse tree produced by ECMAScriptParser#ParenthesizedExpression. def visitParenthesizedExpression(self, ctx): # Return what's between the parenthesis ( what ). return ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#objectLiteral. def visitObjectLiteral(self, ctx): obj = ObjectModule() for key, value in self.childrenToList(ctx.children[1:-1]): if hasattr(value, "get") or hasattr(value, "set"): obj.defineProperty(obj, obj, str(key), value) else: setattr(obj, str(key), value) return obj # Visit a parse tree produced by ECMAScriptParser#throwStatement. def visitThrowStatement(self, ctx): raise ESException(ctx.children[1].accept(self)) # Visit a parse tree produced by ECMAScriptParser#breakStatement. def visitBreakStatement(self, ctx): raise BreakException() # Visit a parse tree produced by ECMAScriptParser#ifStatement. def visitIfStatement(self, ctx): if ctx.children[2].accept(self): ctx.children[4].accept(self) # If there is an else statement elif len(ctx.children) > 6: ctx.children[6].accept(self) # Visit a parse tree produced by ECMAScriptParser#reservedWord. def visitReservedWord(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#variableDeclaration. def visitVariableDeclaration(self, ctx): name = ctx.children[0].accept(self) if len(ctx.children) == 2: value = ctx.children[1].accept(self) else: value = None self.environment.defineVariable(name, value) # Visit a parse tree produced by ECMAScriptParser#finallyProduction. def visitFinallyProduction(self, ctx): ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#IdentifierExpression. def visitIdentifierExpression(self, ctx): return self.environment.value(ctx.children[0].accept(self)) # Visit a parse tree produced by ECMAScriptParser#propertyName. def visitPropertyName(self, ctx): return ctx.children[0].accept(self) # Visit a parse tree produced by ECMAScriptParser#catchProduction. def visitCatchProduction(self, ctx): exceptionName = ctx.children[2].accept(self) self.environment.defineVariable(exceptionName, ctx.exceptionValue) ctx.children[4].accept(self) self.environment.removeVariable(exceptionName) # Visit a parse tree produced by ECMAScriptParser#continueStatement. def visitContinueStatement(self, ctx): raise ContinueException # Visit a parse tree produced by ECMAScriptParser#caseClause. def visitCaseClause(self, ctx): return (ctx.children[1].accept(self), ctx.children[3]) # Visit a parse tree produced by ECMAScriptParser#arguments. def visitArguments(self, ctx): return ctx.children[1].accept(self) # Visit a parse tree produced by ECMAScriptParser#variableDeclarationList. def visitVariableDeclarationList(self, ctx): self.visitChildren(ctx) # Visit a parse tree produced by ECMAScriptParser#functionBody. def visitFunctionBody(self, ctx): def runFunction(environment): self.environment = Environment(environment) for c in ctx.children: try: c.accept(self) except ReturnException as re: return re.value finally: self.environment = self.environment.parent return runFunction # Visit a parse tree produced by ECMAScriptParser#eof. def visitEof(self, ctx): raise Utils.UnimplementedVisitorException(ctx) # Visit a parse tree produced by ECMAScriptParser#UnaryAssignmentExpression. def visitUnaryAssignmentExpression(self, ctx): operator = ctx.children[0].accept(self) name = ctx.children[1].accept(self) value = self.environment.value(name) self.environment.setVariable(name, self.unaries[operator](value)) return self.environment.value(name) def childrenToList(self, items): return [item.accept(self) for item in items if not item.getText() == ',']