Best Python code snippet using hypothesis
test_compiler.py
Source:test_compiler.py
1import pytest2from naulang.compiler import ast3from naulang.compiler.context import FunctionCompilerContext4from naulang.compiler.compiler import SyntaxDirectedTranslator5from naulang.interpreter.space import ObjectSpace6from naulang.interpreter.bytecode import Bytecode7from naulang.interpreter.objectspace.integer import Integer8from naulang.interpreter.objectspace.float import Float9from naulang.interpreter.objectspace.boolean import Boolean10class DummyCompilationUnit(ast.Node):11 def __init__(self, code_to_emit):12 self.code_to_emit = code_to_emit13 def compile(self, context):14 context.emit([self.code_to_emit])15 def accept(self, visitor):16 visitor.visit_dummycompilationunit(self)17 def __repr__(self):18 return "DummyCompilationUnit(%r)" % self.code_to_emit19def create_interpreter_context():20 space = ObjectSpace()21 ctx = FunctionCompilerContext(space)22 return ctx23def create_syntax_directed_translator(ctx):24 def dummy_visit(self, node):25 self.context.emit([node.code_to_emit])26 return True27 # Patch the visit dummy method on to the translator28 SyntaxDirectedTranslator.visit_dummycompilationunit = dummy_visit29 return SyntaxDirectedTranslator(ctx)30def test_optimise_sequential_load_consts():31 ctx = create_interpreter_context()32 ctx.should_optimise = True33 t = create_syntax_directed_translator(ctx)34 node = ast.Block([ast.Multiply(ast.IntegerConstant(10), ast.IntegerConstant(10))])35 node.accept(t)36 assert ctx.get_bytecode() == [Bytecode.LOAD_CONST, 0, Bytecode.DUP, Bytecode.MUL]37def test_optimise_sequential_loads():38 ctx = create_interpreter_context()39 ctx.should_optimise = True40 t = create_syntax_directed_translator(ctx)41 a = ctx.register_local('a')42 node = ast.Block([ast.Multiply(ast.IdentifierExpression('a'), ast.IdentifierExpression('a'))])43 node.accept(t)44 assert ctx.get_bytecode() == [Bytecode.LOAD, a, Bytecode.DUP, Bytecode.MUL]45def test_compile_empty_functionexpression():46 ctx = create_interpreter_context()47 t = create_syntax_directed_translator(ctx)48 node = ast.FunctionExpression(ast.ParameterList([]), None)49 node.accept(t)50 assert ctx.inner_contexts[0].get_bytecode() == [Bytecode.HALT]51def test_return_statement_empty():52 ctx = create_interpreter_context()53 t = create_syntax_directed_translator(ctx)54 node = ast.ReturnStatement(None)55 node.accept(t)56 assert ctx.get_bytecode() == [Bytecode.HALT]57def test_return_statement():58 ctx = create_interpreter_context()59 t = create_syntax_directed_translator(ctx)60 node = ast.ReturnStatement(ast.IntegerConstant(10))61 node.accept(t)62 assert ctx.literals[0] == Integer(10)63 assert ctx.get_bytecode() == [Bytecode.LOAD_CONST, 0, Bytecode.RETURN]64def test_ast_integer_compile():65 ctx = create_interpreter_context()66 t = create_syntax_directed_translator(ctx)67 node = ast.IntegerConstant(100)68 node.accept(t)69 # Expect the constant to be stored in the literals area at position 0 (as this was a new context)70 assert ctx.literals[0] == Integer(100)71 # Expect the byte code to be [Bytecode.LOAD_CONST, 0]72 assert ctx.get_bytecode() == [Bytecode.LOAD_CONST, 0]73def test_ast_float_compile():74 ctx = create_interpreter_context()75 t = create_syntax_directed_translator(ctx)76 node = ast.FloatConstant(100.213)77 node.accept(t)78 # Expect the constant to be stored in the literals area at position 0 (as this was a new context)79 assert ctx.literals[0] == Float(100.213)80 # Expect the byte code to be [Bytecode.LOAD_CONST, 0]81 assert ctx.get_bytecode() == [Bytecode.LOAD_CONST, 0]82def test_ast_boolean_constant_compiler():83 ctx = create_interpreter_context()84 t = create_syntax_directed_translator(ctx)85 node = ast.BooleanConstant(True)86 node.accept(t)87 # Expect the constant to be stored in the literals area at position 0 (as this was a new context)88 assert ctx.literals[0] == Boolean(True)89 # Expect the byte code to be [Bytecode.LOAD_CONST, 0]90 assert ctx.get_bytecode() == [Bytecode.LOAD_CONST, 0]91def test_ast_assignment_compiler():92 ctx = create_interpreter_context()93 t = create_syntax_directed_translator(ctx)94 node = ast.ScopedAssignment('a', ast.BooleanConstant(True))95 node.accept(t)96 # Expect the constant to be stored in the literals area at position 097 assert ctx.literals[0] == Boolean(True)98 # Expect the bytecode to be [Bytecode.LOAD_CONST, 0, Bytecode.STORE, 0]99 assert ctx.get_bytecode() == [Bytecode.LOAD_CONST, 0, Bytecode.STORE, 0]100def test_ast_or_compiler():101 ctx = create_interpreter_context()102 t = create_syntax_directed_translator(ctx)103 node = ast.Or(DummyCompilationUnit(91), DummyCompilationUnit(90))104 node.accept(t)105 # Expect bytecode: [91, 90, Bytecode.OR]106 assert ctx.get_bytecode() == [91, 90, Bytecode.OR]107def test_ast_and_compiler():108 ctx = create_interpreter_context()109 t = create_syntax_directed_translator(ctx)110 node = ast.And(DummyCompilationUnit(91), DummyCompilationUnit(90))111 node.accept(t)112 assert ctx.get_bytecode() == [91, 90, Bytecode.AND]113def test_ast_equals_compiler():114 ctx = create_interpreter_context()115 t = create_syntax_directed_translator(ctx)116 node = ast.Equals(DummyCompilationUnit(91), DummyCompilationUnit(90))117 node.accept(t)118 assert ctx.get_bytecode() == [91, 90, Bytecode.EQUAL]119def test_ast_not_equals_compiler():120 ctx = create_interpreter_context()121 t = create_syntax_directed_translator(ctx)122 node = ast.NotEquals(DummyCompilationUnit(91), DummyCompilationUnit(90))123 node.accept(t)124 assert ctx.get_bytecode() == [91, 90, Bytecode.NOT_EQUAL]125def test_ast_lessthan_compiler():126 ctx = create_interpreter_context()127 t = create_syntax_directed_translator(ctx)128 node = ast.LessThan(DummyCompilationUnit(91), DummyCompilationUnit(90))129 node.accept(t)130 assert ctx.get_bytecode() == [91, 90, Bytecode.LESS_THAN]131def test_ast_lessthanorequal_compiler():132 ctx = create_interpreter_context()133 t = create_syntax_directed_translator(ctx)134 node = ast.LessThanOrEqual(DummyCompilationUnit(91), DummyCompilationUnit(90))135 node.accept(t)136 assert ctx.get_bytecode() == [91, 90, Bytecode.LESS_THAN_EQ]137def test_ast_greaterthanorequal_compiler():138 ctx = create_interpreter_context()139 t = create_syntax_directed_translator(ctx)140 node = ast.GreaterThanOrEqual(DummyCompilationUnit(91), DummyCompilationUnit(90))141 node.accept(t)142 assert ctx.get_bytecode() == [91, 90, Bytecode.GREATER_THAN_EQ]143def test_ast_greaterthan_compiler():144 ctx = create_interpreter_context()145 t = create_syntax_directed_translator(ctx)146 node = ast.GreaterThan(DummyCompilationUnit(91), DummyCompilationUnit(90))147 node.accept(t)148 assert ctx.get_bytecode() == [91, 90, Bytecode.GREATER_THAN]149def test_ast_addop_compiler():150 ctx = create_interpreter_context()151 t = create_syntax_directed_translator(ctx)152 node = ast.Add(DummyCompilationUnit(91), DummyCompilationUnit(90))153 node.accept(t)154 assert ctx.get_bytecode() == [90, 91, Bytecode.ADD]155def test_ast_subtractop_compiler():156 ctx = create_interpreter_context()157 t = create_syntax_directed_translator(ctx)158 node = ast.Subtract(DummyCompilationUnit(91), DummyCompilationUnit(90))159 node.accept(t)160 assert ctx.get_bytecode() == [90, 91, Bytecode.SUB]161def test_ast_mulop_compiler():162 ctx = create_interpreter_context()163 t = create_syntax_directed_translator(ctx)164 node = ast.Multiply(DummyCompilationUnit(91), DummyCompilationUnit(90))165 node.accept(t)166 assert ctx.get_bytecode() == [90, 91, Bytecode.MUL]167def test_ast_divop_compiler():168 ctx = create_interpreter_context()169 t = create_syntax_directed_translator(ctx)170 node = ast.Divide(DummyCompilationUnit(91), DummyCompilationUnit(90))171 node.accept(t)172 assert ctx.get_bytecode() == [90, 91, Bytecode.DIV]173def test_ast_unarynot_compiler():174 ctx = create_interpreter_context()175 t = create_syntax_directed_translator(ctx)176 node = ast.UnaryNot(DummyCompilationUnit(90))177 node.accept(t)178 assert ctx.get_bytecode() == [90, Bytecode.NOT]179def test_ast_unarynegate_compiler():180 ctx = create_interpreter_context()181 t = create_syntax_directed_translator(ctx)182 node = ast.UnaryNegate(DummyCompilationUnit(90))183 node.accept(t)184 assert ctx.get_bytecode() == [90, Bytecode.NEG]185def test_ast_whilestatement_compiler():186 ctx = create_interpreter_context()187 t = create_syntax_directed_translator(ctx)188 # Add padding to bytecodes to test non-zero based context (this is more realistic)189 ctx.emit([100])190 node = ast.WhileStatement(DummyCompilationUnit(90), ast.Block([DummyCompilationUnit(91)]))191 node.accept(t)192 assert ctx.get_bytecode() == [100, 90, Bytecode.JUMP_IF_FALSE, 7, 91, Bytecode.JUMP, 1]193def test_ast_ifstatement_compiler():194 ctx = create_interpreter_context()195 t = create_syntax_directed_translator(ctx)196 # Add padding to bytecodes to test non-zero based context (this is more realistic)197 ctx.emit([100])198 node = ast.IfStatement(DummyCompilationUnit(90), ast.Block([DummyCompilationUnit(91)]))199 node.accept(t)200 assert ctx.get_bytecode() == [100, 90, Bytecode.JUMP_IF_FALSE, 5, 91]201def test_ast_printstatement():202 ctx = create_interpreter_context()203 t = create_syntax_directed_translator(ctx)204 node = ast.PrintStatement(DummyCompilationUnit(90))205 node.accept(t)206 assert ctx.get_bytecode() == [90, Bytecode.PRINT]207@pytest.mark.xfail208def test_ast_functionstatement():209 ctx = create_interpreter_context()210 t = create_syntax_directed_translator(ctx)211 node = ast.FunctionStatement('a', ast.ParameterList(['a']), ast.Block([DummyCompilationUnit(90)]))212 node.accept(t)213 assert ctx.get_bytecode() == [Bytecode.LOAD_CONST, 0]214 assert len(ctx.inner_contexts) == 1215 assert ctx.inner_contexts[0].get_bytecode() == [90, Bytecode.HALT]216 assert ctx.inner_contexts[0].has_local('a')217 assert ctx.inner_contexts[0].get_parameter_count() == 1218def test_ast_functionexpression():219 ctx = create_interpreter_context()220 t = create_syntax_directed_translator(ctx)221 node = ast.FunctionExpression(ast.ParameterList(['a']),222 ast.Block([ast.ReturnStatement(ast.IdentifierExpression('a'))]))223 node.accept(t)224 assert ctx.get_bytecode() == [Bytecode.LOAD_CONST, 0]225 assert len(ctx.inner_contexts) == 1226 assert ctx.inner_contexts[0].get_bytecode() == [Bytecode.LOAD, 0, Bytecode.RETURN, Bytecode.HALT]227 assert ctx.inner_contexts[0].has_local('a')228 assert ctx.inner_contexts[0].get_parameter_count() == 1229def test_ast_functioncall():230 ctx = create_interpreter_context()231 t = create_syntax_directed_translator(ctx)232 function_local = ctx.register_local('a')233 node = ast.FunctionCall(ast.IdentifierExpression('a'), ast.ArgumentList(234 [DummyCompilationUnit(90), DummyCompilationUnit(91)]))235 node.accept(t)236 assert ctx.get_bytecode() == [90, 91, Bytecode.LOAD, function_local, Bytecode.INVOKE]237def test_ast_asyncfunctioncall():238 ctx = create_interpreter_context()239 t = create_syntax_directed_translator(ctx)240 function_local = ctx.register_local('a')241 node = ast.AsyncFunctionCall(ast.IdentifierExpression('a'), ast.ArgumentList(242 [DummyCompilationUnit(90), DummyCompilationUnit(91)]))243 node.accept(t)244 assert ctx.get_bytecode() == [90, 91, Bytecode.LOAD, function_local, Bytecode.INVOKE_ASYNC]245def test_ast_returnstatement():246 ctx = create_interpreter_context()247 t = create_syntax_directed_translator(ctx)248 node = ast.ReturnStatement(DummyCompilationUnit(90))249 node.accept(t)250 assert ctx.get_bytecode() == [90, Bytecode.RETURN]251def test_array_access():252 ctx = create_interpreter_context()253 t = create_syntax_directed_translator(ctx)254 node = ast.ArrayAccess(DummyCompilationUnit(90), DummyCompilationUnit(91))255 node.accept(t)256 assert ctx.get_bytecode() == [90, 91, Bytecode.ARRAY_LOAD]257def test_array_access_assignment():258 ctx = create_interpreter_context()259 t = create_syntax_directed_translator(ctx)260 node = ast.ArrayAssignment(261 ast.ArrayAccess(262 DummyCompilationUnit(90),263 DummyCompilationUnit(91)),264 DummyCompilationUnit(93))265 node.accept(t)266 assert ctx.get_bytecode() == [90, 91, 93, Bytecode.ARRAY_STORE]267def test_invoke_global_list():268 ctx = create_interpreter_context()269 t = create_syntax_directed_translator(ctx)270 node = ast.FunctionCall(ast.IdentifierExpression('list'), ast.ArgumentList([ast.IntegerConstant(10)]))271 node.accept(t)272 assert ctx.get_bytecode() == [Bytecode.LOAD_CONST, 0, Bytecode.INVOKE_GLOBAL, 0]273def test_break_statement():274 ctx = create_interpreter_context()275 t = create_syntax_directed_translator(ctx)276 node = ast.WhileStatement(DummyCompilationUnit(90), ast.Block([ast.BreakStatement()]))277 node.accept(t)278 assert ctx.get_bytecode() == [279 90,280 Bytecode.JUMP_IF_FALSE, 7,281 Bytecode.JUMP, 7,282 Bytecode.JUMP, 0283 ]284def test_continue_statement():285 ctx = create_interpreter_context()286 t = create_syntax_directed_translator(ctx)287 node = ast.WhileStatement(DummyCompilationUnit(90), ast.Block([ast.ContinueStatement()]))288 node.accept(t)289 assert ctx.get_bytecode() == [290 90,291 Bytecode.JUMP_IF_FALSE, 7,292 Bytecode.JUMP, 0,293 Bytecode.JUMP, 0294 ]295def test_channel_out():296 ctx = create_interpreter_context()297 t = create_syntax_directed_translator(ctx)298 node = ast.ChannelOut(DummyCompilationUnit(90))299 node.accept(t)300 assert ctx.get_bytecode() == [301 90,302 Bytecode.CHAN_OUT,303 ]304def test_channel_in():305 ctx = create_interpreter_context()306 t = create_syntax_directed_translator(ctx)307 node = ast.ChannelIn(DummyCompilationUnit(90), ast.Multiply(ast.IntegerConstant(10), ast.IntegerConstant(10)))308 node.accept(t)309 assert ctx.get_bytecode() == [310 90,311 Bytecode.LOAD_CONST, 0,312 Bytecode.LOAD_CONST, 0,313 Bytecode.MUL,314 Bytecode.CHAN_IN,315 ]316def test_channel_in_out():317 ctx = create_interpreter_context()318 t = create_syntax_directed_translator(ctx)319 node = ast.Block([ast.ChannelIn(DummyCompilationUnit(90), ast.ChannelOut(DummyCompilationUnit(91)))])320 node.accept(t)321 assert ctx.get_bytecode() == [322 90,323 91,324 Bytecode.CHAN_OUT,325 Bytecode.CHAN_IN326 ]327def test_ast_scoped_assignment():328 ctx = create_interpreter_context()329 t = create_syntax_directed_translator(ctx)330 node = ast.Block([331 ast.ScopedAssignment('x', ast.IntegerConstant(10)),332 ast.FunctionExpression(333 ast.ParameterList(['a']),334 ast.Block([335 ast.Assignment('x', ast.IntegerConstant(12)),336 ast.PrintStatement(ast.IdentifierExpression('x'))337 ])338 )339 ])340 """ AST Equivalent to:341 let x = 10342 fn(a) {343 x = 12344 print x345 }346 """347 node.accept(t)348 assert ctx.literals[0] == Integer(10)349 # Outer context loads the function expression constant from literals area 0350 assert ctx.get_bytecode() == [351 Bytecode.LOAD_CONST, 0, # Push the constant at 0 onto the stack (10)352 Bytecode.STORE, 0, # Store the top of the stack into locals aread at 0353 Bytecode.LOAD_CONST, 1 # Push the function expression onto the top of the stack354 ]355 # Expect the constant to be stored in the literals area at position 0356 # Of the first inner method context357 inner_contexts = ctx.get_inner_contexts()358 assert len(inner_contexts) == 1359 assert inner_contexts[0].literals[0] == Integer(12)360 assert inner_contexts[0].get_bytecode() == [361 Bytecode.LOAD_CONST, 0, # Push 12 onto the stack362 Bytecode.STORE_DYNAMIC, 0, 1, # Store 12 into the dynamic variable x363 Bytecode.LOAD_DYNAMIC, 0, 1, # Load dynamic variable x onto the top of the stack364 Bytecode.PRINT, # Call print365 Bytecode.HALT # All functions end in HALT366 ]367def test_ast_scoped_usage():368 ctx = create_interpreter_context()369 t = create_syntax_directed_translator(ctx)370 node = ast.Block([371 ast.ScopedAssignment('n', ast.IntegerConstant(10)),372 ast.ScopedAssignment('a', ast.FunctionExpression(373 ast.ParameterList(['x']),374 ast.Block([375 ast.PrintStatement(376 ast.Add(377 ast.Multiply(378 ast.IdentifierExpression('x'),379 ast.IntegerConstant(2)380 ),381 ast.IdentifierExpression('n')382 )383 )384 ])385 )),386 ast.FunctionCall(387 ast.IdentifierExpression('a'),388 ast.ArgumentList([ast.IntegerConstant(2)])389 ),390 ast.FunctionCall(391 ast.IdentifierExpression('a'),392 ast.ArgumentList([ast.IntegerConstant(4)])393 )394 ])395 node.accept(t)396 expected = [397 Bytecode.LOAD_CONST, 0,398 Bytecode.STORE, 0,399 Bytecode.LOAD_CONST, 1,400 Bytecode.STORE, 1,401 Bytecode.LOAD_CONST, 2,402 Bytecode.LOAD, 1,403 Bytecode.INVOKE,404 Bytecode.LOAD_CONST, 3,405 Bytecode.LOAD, 1,406 Bytecode.INVOKE,407 ]408 assert ctx.get_bytecode() == expected409 inner_contexts = ctx.get_inner_contexts()410 assert inner_contexts[0].get_bytecode() == [411 Bytecode.LOAD_DYNAMIC, 0, 1,412 Bytecode.LOAD_CONST, 0,413 Bytecode.LOAD, 0,414 Bytecode.MUL,415 Bytecode.ADD,416 Bytecode.PRINT,417 Bytecode.HALT...
translator.py
Source:translator.py
1from naulang.compiler.context import FunctionCompilerContext2from naulang.compiler import ast3from naulang.compiler.error import CompilerException4from naulang.interpreter.bytecode import Bytecode5from naulang.interpreter.objectspace.primitives.builtin_definitions import builtin_functions6_builtin_functions = builtin_functions()7class SyntaxDirectedTranslator(ast.ASTVisitor):8 """ Translate an AST to a function object graph.9 This doesn't emit pure bytecode, literals and symbol tables. Instead it10 uses the AST to build a tree of Method objects using the11 FunctionCompilerContext. Each time a function expression is encountered12 in the AST, a new FunctionCompilerContext is created and a new13 translator is created and invoked on the function subtree, this means14 the tree structure of the function definitions is recursively translated15 into a tree of method objects. Inner functions are stored as literals16 in its containing function to be referenced with LOAD_CONST bytecodes17 in the same way that all data is referenced. This allows us to use18 functions, as first class data types and gives rise to the 'lambda'19 style syntax of function composition:20 let f = fn(b, x) {21 return b(fn(y) {22 return x + y23 })24 }25 print f(fn(x) {26 return x(10)27 }, 15)28 Output: 25 (the sum of 10 and 15 [in a somewhat contrived way])29 The translator would create 4 Method objects in this instance. One for30 the containing 'main' function. One for the function assigned to f,31 within that function there is another that is used to pass into the32 function in 'b'.33 Another contained in the main function is the function that returns34 x(10). The structure created would look something like the following:35 [ MAIN ]36 |37 |------ fn(b,x) { ... }38 | |39 | |----- fn(y) { ... }40 |41 |------ fn(x) { ... }42 """43 def __init__(self, compiler_context):44 self.context = compiler_context45 def visit_booleanconstant(self, node):46 boolean = self.context.space.new_boolean(node.get_boolean_value())47 self.context.emit([Bytecode.LOAD_CONST,48 self.context.register_literal(boolean)],49 sourceposition=node.getsourcepos())50 return True51 def visit_integerconstant(self, node):52 integer = self.context.space.new_integer(node.get_integer_constant())53 self.context.emit([Bytecode.LOAD_CONST,54 self.context.register_literal(integer)],55 sourceposition=node.getsourcepos())56 return True57 def visit_floatconstant(self, node):58 float_val = self.context.space.new_float(node.get_float_constant())59 self.context.emit([Bytecode.LOAD_CONST,60 self.context.register_literal(float_val)],61 sourceposition=node.getsourcepos())62 def visit_stringconstant(self, node):63 string = self.context.space.new_string(node.get_string_value())64 self.context.emit([Bytecode.LOAD_CONST,65 self.context.register_literal(string)],66 sourceposition=node.getsourcepos())67 return True68 def visit_assignment(self, node):69 if self.context.has_local(node.get_varname()):70 local = self.context.register_local(node.get_varname())71 node.expression.accept(self)72 self.context.emit([Bytecode.STORE, local], sourceposition=node.getsourcepos())73 else:74 slot, level = self.context.register_dynamic(node.get_varname())75 if slot is FunctionCompilerContext.REGISTER_DYNAMIC_FAILED:76 raise CompilerException(77 "'%s' has not been defined in this scope. You should use `let %s = ...` to initialise a variable" %78 (node.get_varname(), node.get_varname()), node.getsourcepos())79 node.expression.accept(self)80 self.context.emit([Bytecode.STORE_DYNAMIC, slot, level], sourceposition=node.getsourcepos())81 return False82 def visit_or(self, node):83 node.lhs.accept(self)84 node.rhs.accept(self)85 self.context.emit([Bytecode.OR], sourceposition=node.getsourcepos())86 return False87 def visit_and(self, node):88 node.lhs.accept(self)89 node.rhs.accept(self)90 self.context.emit([Bytecode.AND], sourceposition=node.getsourcepos())91 return False92 def visit_equals(self, node):93 node.lhs.accept(self)94 node.rhs.accept(self)95 self.context.emit([Bytecode.EQUAL], sourceposition=node.getsourcepos())96 return False97 def visit_notequals(self, node):98 node.lhs.accept(self)99 node.rhs.accept(self)100 self.context.emit([Bytecode.NOT_EQUAL], sourceposition=node.getsourcepos())101 return False102 def visit_lessthan(self, node):103 node.lhs.accept(self)104 node.rhs.accept(self)105 self.context.emit([Bytecode.LESS_THAN], sourceposition=node.getsourcepos())106 return False107 def visit_lessthanorequal(self, node):108 node.lhs.accept(self)109 node.rhs.accept(self)110 self.context.emit([Bytecode.LESS_THAN_EQ], sourceposition=node.getsourcepos())111 return False112 def visit_greaterthan(self, node):113 node.lhs.accept(self)114 node.rhs.accept(self)115 self.context.emit([Bytecode.GREATER_THAN], sourceposition=node.getsourcepos())116 return False117 def visit_greaterthanorequal(self, node):118 node.lhs.accept(self)119 node.rhs.accept(self)120 self.context.emit([Bytecode.GREATER_THAN_EQ], sourceposition=node.getsourcepos())121 return False122 def visit_add(self, node):123 node.rhs.accept(self)124 node.lhs.accept(self)125 self.context.emit([Bytecode.ADD], sourceposition=node.getsourcepos())126 return False127 def visit_subtract(self, node):128 node.rhs.accept(self)129 node.lhs.accept(self)130 self.context.emit([Bytecode.SUB], sourceposition=node.getsourcepos())131 return False132 def visit_multiply(self, node):133 node.rhs.accept(self)134 node.lhs.accept(self)135 self.context.emit([Bytecode.MUL], sourceposition=node.getsourcepos())136 return False137 def visit_divide(self, node):138 node.rhs.accept(self)139 node.lhs.accept(self)140 self.context.emit([Bytecode.DIV], sourceposition=node.getsourcepos())141 return False142 def visit_mod(self, node):143 node.rhs.accept(self)144 node.lhs.accept(self)145 self.context.emit([Bytecode.MOD], sourceposition=node.getsourcepos())146 return False147 def visit_unarynot(self, node):148 node.expression.accept(self)149 self.context.emit([Bytecode.NOT], sourceposition=node.getsourcepos())150 return False151 def visit_unarynegate(self, node):152 node.expression.accept(self)153 self.context.emit([Bytecode.NEG], sourceposition=node.getsourcepos())154 return False155 def visit_breakstatement(self, node):156 loop_control = self.context.peek_loop_control()157 self.context.emit([Bytecode.JUMP, loop_control[1]], sourceposition=node.getsourcepos())158 return True159 def visit_continuestatement(self, node):160 loop_control = self.context.peek_loop_control()161 self.context.emit([Bytecode.JUMP, loop_control[0]], sourceposition=node.getsourcepos())162 return True163 def visit_whilestatement(self, node):164 # Set up the labels for this while block and push them onto the loop control stack165 label_start = self.context.add_label(166 initial_value=self.context.get_top_position() + 1167 )168 label_end = self.context.add_label()169 self.context.push_loop_control(label_start, label_end)170 # Evaluate the condition and emit control instructions171 node.condition.accept(self)172 self.context.emit([Bytecode.JUMP_IF_FALSE, label_end], sourceposition=node.getsourcepos())173 # Evaluate block174 node.block.accept(self)175 # Loop block so remove loop control labels from stack176 self.context.pop_loop_control()177 # Emit a GOTO to actually loop178 self.context.emit([Bytecode.JUMP, label_start], sourceposition=node.getsourcepos())179 # Now we know what the value of the end label should be set it.180 self.context.set_label(label_end, self.context.get_top_position() + 1)181 return False182 def visit_ifstatement(self, node):183 node.condition.accept(self)184 endlabel = self.context.add_label()185 self.context.emit([Bytecode.JUMP_IF_FALSE, endlabel], sourceposition=node.getsourcepos())186 node.ifclause.accept(self)187 self.context.set_label(endlabel, self.context.get_top_position() + 1)188 return False189 def visit_printstatement(self, node):190 node.expression.accept(self)191 self.context.emit([Bytecode.PRINT], sourceposition=node.getsourcepos())192 return False193 def visit_functionstatement(self, node):194 raise NotImplementedError()195 def visit_functionexpression(self, node):196 new_context = FunctionCompilerContext(197 self.context.space,198 outer=self.context,199 optimise=self.context.should_optimise)200 self.context.add_inner_context(new_context)201 parameters = node.get_parameterlist().get_parameter_list()202 parameter_count = len(parameters)203 for param in parameters:204 new_context.register_local(param)205 new_context.set_parameter_count(parameter_count)206 new_visitor = SyntaxDirectedTranslator(new_context)207 if node.block is not None:208 node.block.accept(new_visitor)209 new_context.emit([Bytecode.HALT])210 method = new_context.generate_method()211 self.context.emit([Bytecode.LOAD_CONST,212 self.context.register_literal(method)],213 sourceposition=node.getsourcepos())214 return False215 def visit_asyncfunctioncall(self, node):216 for arg in node.get_arguments().get_argument_list():217 arg.accept(self)218 if node.identifier.get_identifier() in _builtin_functions:219 raise CompilerException("Built in functions can not be called with the async modifier", node.getsourcepos())220 node.identifier.accept(self)221 self.context.emit([Bytecode.INVOKE_ASYNC], sourceposition=node.getsourcepos())222 return False223 def visit_functioncall(self, node):224 for arg in node.get_arguments().get_argument_list():225 arg.accept(self)226 if node.identifier.get_identifier() in _builtin_functions:227 function = _builtin_functions[node.identifier.get_identifier()]228 self.context.emit([Bytecode.INVOKE_GLOBAL, function[1]], sourceposition=node.getsourcepos())229 else:230 node.identifier.accept(self)231 self.context.emit([Bytecode.INVOKE], sourceposition=node.getsourcepos())232 return False233 def visit_returnstatement(self, node):234 if node.expression is not None:235 node.expression.accept(self)236 self.context.emit([Bytecode.RETURN], sourceposition=node.getsourcepos())237 else:238 self.context.emit([Bytecode.HALT])239 return False240 def visit_identifierexpression(self, node):241 if self.context.has_local(node.identifier):242 local = self.context.register_local(node.identifier)243 self.context.emit([Bytecode.LOAD, local], sourceposition=node.getsourcepos())244 else:245 slot, level = self.context.register_dynamic(node.identifier)246 if slot == FunctionCompilerContext.REGISTER_DYNAMIC_FAILED:247 raise CompilerException(248 "'%s' has not been defined in any scope. You should use `let %s = ...` to initialise a variable" %249 (node.identifier, node.identifier), node.getsourcepos())250 self.context.emit([Bytecode.LOAD_DYNAMIC, slot, level], sourceposition=node.getsourcepos())251 return True252 def visit_arrayaccess(self, node):253 node.identifier.accept(self)254 node.index.accept(self)255 self.context.emit([Bytecode.ARRAY_LOAD], sourceposition=node.getsourcepos())256 return False257 def visit_arrayassignment(self, node):258 assert isinstance(node, ast.ArrayAssignment) # RPython259 node.get_array_access().get_identifier().accept(self)260 node.array_access.index.accept(self)261 node.expression.accept(self)262 self.context.emit([Bytecode.ARRAY_STORE], sourceposition=node.getsourcepos())263 return False264 def visit_scopedassignment(self, node):265 local = self.context.register_local(node.varname)266 node.expression.accept(self)267 self.context.emit([Bytecode.STORE, local], sourceposition=node.getsourcepos())268 return False269 def visit_channelout(self, node):270 node.channel.accept(self)271 self.context.emit([Bytecode.CHAN_OUT], sourceposition=node.getsourcepos())272 return False273 def visit_channelin(self, node):274 node.get_channel().accept(self)275 node.get_expression().accept(self)...
context.py
Source:context.py
1from naulang.interpreter.objectspace.method import Method2from naulang.interpreter.bytecode import Bytecode, get_stack_effect, stack_effect_depends_on_args, get_bytecode_length3from naulang.compiler.sourcemap import SourceMap4class FunctionCompilerContext(object):5 """ Context used for compiling a function """6 REGISTER_DYNAMIC_FAILED = -17 def __init__(self, object_space, outer=None, optimise=False):8 self.space = object_space9 self.literals = []10 self.locals = []11 self.parameter_count = 012 self.bytecode = []13 self.outer = outer14 # This maintains the symbol table. When an identifier is encountered15 # it is entered into this dictionary with a unique numeric identifier16 self.id_to_number = {}17 self.inner_contexts = []18 self.labels = []19 # A stack, the top contains a 2-tuple of current labels for the20 # current loop value 1 being the label for the top of the loop (pre-condition)21 # value 2 being the label for the block after the loop.22 self.loop_control = []23 self.sourcemap = SourceMap()24 self.should_optimise = optimise25 self.last_bytecode_seq = []26 def get_top_position(self):27 """ Returns:28 The position of the last operation, 0 if none29 """30 return len(self.bytecode) - 131 def add_label(self, initial_value=-1):32 """ Returns:33 A new label.34 Labels are represented by integers.35 Keyword Arguments:36 initial_value -- Set an initial value for this label, default is -1 representing an unassigned label37 """38 self.labels.append(initial_value)39 return len(self.labels) - 140 def set_label(self, label, value):41 """42 Updates the value of a label43 Arguments:44 label -- The label number to update (should be an integer)45 value -- The value to assign to this label (should be an integer)46 """47 self.labels[label] = value48 def get_label_value(self, label):49 """50 Returns the value of a label51 Arguments:52 label -- The label number to retrieve (expects an integer)53 """54 return self.labels[label]55 def push_loop_control(self, label_start, label_end):56 """57 Pushes the label representing the top of the loop and the tail of the loop to the loop control stack.58 This is useful for implementing statements such as 'break' and 'continue'59 Arguments:60 label_start -- The Label representing the start of the loop61 label_end -- The label representing the end of the loop62 """63 self.loop_control.append((label_start, label_end))64 def peek_loop_control(self):65 """66 Returns the current loop control 2-tuple67 """68 return self.loop_control[len(self.loop_control) - 1]69 def pop_loop_control(self):70 """71 Returns the current loop control 2-tuple and removes it from the stack.72 """73 return self.loop_control.pop()74 def add_inner_context(self, context):75 """76 Add an inner function context to this context.77 """78 self.inner_contexts.append(context)79 def get_inner_contexts(self):80 """81 Get a list of inner function contexts.82 """83 return self.inner_contexts84 def get_outer_context(self):85 """86 Get the context enclosing this function context87 """88 return self.outer89 def objspace(self):90 """91 Get object space92 """93 return self.space94 def generate_method(self):95 """96 Generate a method object from this function context.97 """98 # First replace bytecode labels with actual values99 bytecode = self.get_bytecode()100 stack_depth = self._calculate_stack_depth(bytecode)101 # finalize locals and literals102 literals = [None] * len(self.literals)103 locals = [None] * len(self.locals)104 for i in range(0, len(literals)):105 literals[i] = self.literals[i]106 return Method(literals, len(locals), bytecode,107 stack_depth, argument_count=self.parameter_count,108 source_map=self.sourcemap)109 def _calculate_stack_depth(self, finalized_bytecode):110 max_depth = 0111 depth = 0112 i = 0113 while i < len(finalized_bytecode):114 bc = finalized_bytecode[i]115 if stack_effect_depends_on_args(bc):116 # HACK: It's difficult to find the arguments of a dynamically117 # loaded method therefore the arguments consumed by this118 # argument are assumed to be none. Having a slightly larger119 # stack size than necessary shouldn't have a huge effect on120 # performance, although not ideal.121 # Adding one for the return value122 depth += 1123 else:124 depth += get_stack_effect(bc)125 if depth > max_depth:126 max_depth = depth127 i += get_bytecode_length(bc)128 return max_depth129 def get_bytecode(self):130 """131 Get the bytecode representation of this function context with any132 optimisations or labels applied.133 """134 return self._add_labels(self.bytecode)135 def set_outer(self, outer_context):136 """137 Set the outer context of this function context138 """139 self.outer = outer_context140 def set_parameter_count(self, value):141 """142 Set the number of arguments this function takes143 """144 self.parameter_count = value145 def get_parameter_count(self):146 """147 Get the parameter count148 """149 return self.parameter_count150 def register_dynamic(self, identifier):151 """152 Register lookup of a non local scoped variable153 """154 if self.outer is None:155 return self.REGISTER_DYNAMIC_FAILED, 0156 outer_context = self.outer157 level = 1158 while outer_context is not None:159 if outer_context.has_local(identifier):160 return outer_context.register_local(identifier), level161 outer_context = outer_context.outer162 level += 1163 return self.REGISTER_DYNAMIC_FAILED, 0164 def register_local(self, identifier):165 """ Register a local variable in this method context166 and retrieve an offset that will be used in the stack167 to retrieve this variables value. """168 if identifier in self.id_to_number:169 return self.id_to_number[identifier]170 # If we don't have a mapping for this identifier yet create one171 # and create some space in the _locals list for it172 num = len(self.locals)173 self.id_to_number[identifier] = num174 self.locals.append(None)175 return num176 def has_local(self, identifier):177 """ Get whether this function context has the identifier registered """178 if identifier in self.id_to_number:179 return True180 return False181 def register_literal(self, constant_value):182 """ Register a constant value within this function context """183 if constant_value in self.literals:184 return self.literals.index(constant_value)185 self.literals.append(constant_value)186 return len(self.literals) - 1187 def emit(self, bytecode_sequence, sourceposition=None):188 """ Emit a bytecode into this function context """189 if sourceposition is not None:190 self.sourcemap.add(len(self.bytecode), sourceposition)191 if self.should_optimise:192 try:193 # TODO put this stuff in a module194 if bytecode_sequence[0] is Bytecode.LOAD_CONST:195 if self.last_bytecode_seq[0] is Bytecode.LOAD_CONST:196 if bytecode_sequence[1] is self.last_bytecode_seq[1]:197 self.bytecode.append(Bytecode.DUP)198 return199 if bytecode_sequence[0] is Bytecode.LOAD:200 if self.last_bytecode_seq[0] is Bytecode.LOAD:201 if bytecode_sequence[1] is self.last_bytecode_seq[1]:202 self.bytecode.append(Bytecode.DUP)203 return204 except IndexError:205 pass206 for bytecode in bytecode_sequence:207 self.bytecode.append(bytecode)208 self.last_bytecode_seq = bytecode_sequence209 def _add_labels(self, bytecode):210 """211 Update the bytecode with added labels212 """213 # TODO: This is a bit of a mess: we should use the Bytecode size214 # information in Bytecode here215 bytecodes = [0] * len(bytecode)216 i = 0217 while i < len(self.bytecode):218 bytecodes[i] = self.bytecode[i]219 if bytecode[i] == Bytecode.LOAD_CONST or bytecode[i] == Bytecode.STORE or bytecode[220 i] == Bytecode.LOAD or bytecode[i] == Bytecode.INVOKE_GLOBAL:221 i += 1222 bytecodes[i] = bytecode[i]223 i += 1224 elif bytecode[i] == Bytecode.LOAD_DYNAMIC or bytecode[i] == Bytecode.STORE_DYNAMIC:225 i += 1226 bytecodes[i] = bytecode[i]227 i += 1228 bytecodes[i] = bytecode[i]229 i += 1230 elif bytecode[i] == Bytecode.JUMP or bytecode[i] == Bytecode.JUMP_IF_FALSE:231 bytecodes[i + 1] = self.get_label_value(bytecode[i + 1])232 i += 2233 else:234 i += 1...
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!