owl-basic 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. owl_basic/__init__.py +3 -0
  2. owl_basic/algorithms.py +29 -0
  3. owl_basic/ast_utils.py +204 -0
  4. owl_basic/basic_visitor.py +55 -0
  5. owl_basic/cfg_vertex.py +65 -0
  6. owl_basic/codegen/__init__.py +0 -0
  7. owl_basic/codegen/clr/__init__.py +0 -0
  8. owl_basic/codegen/clr/cil_visitor.py +1296 -0
  9. owl_basic/codegen/clr/cts.py +56 -0
  10. owl_basic/codegen/clr/emitters.py +94 -0
  11. owl_basic/codegen/clr/generate.py +539 -0
  12. owl_basic/correlation_visitor.py +119 -0
  13. owl_basic/data_visitor.py +62 -0
  14. owl_basic/decoder.py +339 -0
  15. owl_basic/errors.py +22 -0
  16. owl_basic/flow/__init__.py +17 -0
  17. owl_basic/flow/basic_block.py +34 -0
  18. owl_basic/flow/basic_block_identifier.py +66 -0
  19. owl_basic/flow/basic_block_orderer.py +29 -0
  20. owl_basic/flow/connectors.py +19 -0
  21. owl_basic/flow/convert_sub_visitor.py +28 -0
  22. owl_basic/flow/entry_point_locator.py +55 -0
  23. owl_basic/flow/entry_point_visitor.py +48 -0
  24. owl_basic/flow/flow_analysis.py +56 -0
  25. owl_basic/flow/flow_graph_creator.py +14 -0
  26. owl_basic/flow/flowgraph_visitor.py +178 -0
  27. owl_basic/flow/longjump_converter.py +20 -0
  28. owl_basic/flow/longjump_visitor.py +53 -0
  29. owl_basic/flow/subroutine_converter.py +38 -0
  30. owl_basic/flow/traversal.py +110 -0
  31. owl_basic/gml_visitor.py +151 -0
  32. owl_basic/line_mapper.py +43 -0
  33. owl_basic/line_number_visitor.py +65 -0
  34. owl_basic/main.py +381 -0
  35. owl_basic/node.py +21 -0
  36. owl_basic/options.py +22 -0
  37. owl_basic/owltyping/__init__.py +1 -0
  38. owl_basic/owltyping/function_type_inferer.py +50 -0
  39. owl_basic/owltyping/hindley_milner.py +524 -0
  40. owl_basic/owltyping/set_function_type_visitor.py +25 -0
  41. owl_basic/owltyping/type_system.py +220 -0
  42. owl_basic/owltyping/typecheck.py +60 -0
  43. owl_basic/owltyping/typecheck_visitor.py +471 -0
  44. owl_basic/parent_visitor.py +37 -0
  45. owl_basic/process.py +36 -0
  46. owl_basic/separation_visitor.py +98 -0
  47. owl_basic/sigil.py +30 -0
  48. owl_basic/simplify_visitor.py +204 -0
  49. owl_basic/singleton.py +127 -0
  50. owl_basic/source_debugging.py +124 -0
  51. owl_basic/symbol_table_visitor.py +220 -0
  52. owl_basic/symbol_tables.py +195 -0
  53. owl_basic/syntax/__init__.py +0 -0
  54. owl_basic/syntax/ast.py +1081 -0
  55. owl_basic/syntax/ast_meta.py +228 -0
  56. owl_basic/syntax/grammar.py +1972 -0
  57. owl_basic/syntax/lexer.py +943 -0
  58. owl_basic/syntax/parser.py +77 -0
  59. owl_basic/utility.py +26 -0
  60. owl_basic/visitor.py +43 -0
  61. owl_basic/xml_blocks.py +137 -0
  62. owl_basic/xml_visitor.py +101 -0
  63. owl_basic-0.6.0.dist-info/METADATA +37 -0
  64. owl_basic-0.6.0.dist-info/RECORD +69 -0
  65. owl_basic-0.6.0.dist-info/WHEEL +5 -0
  66. owl_basic-0.6.0.dist-info/entry_points.txt +2 -0
  67. owl_basic-0.6.0.dist-info/licenses/LICENSE +21 -0
  68. owl_basic-0.6.0.dist-info/licenses/THIRD-PARTY-NOTICES.md +57 -0
  69. owl_basic-0.6.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1296 @@
1
+ '''
2
+ Created on 10 Aug 2009
3
+
4
+ @author: rjs
5
+ '''
6
+ import os
7
+ import sys
8
+ import logging
9
+
10
+ import clr
11
+ import System
12
+ from System.Reflection import *
13
+ from System.Reflection.Emit import *
14
+
15
+ from owl_basic.visitor import Visitor
16
+ from owl_basic.syntax.ast import *
17
+ from owl_basic.ast_utils import findNode
18
+ from . import cts
19
+ from owl_basic import errors
20
+ from .emitters import *
21
+ from owl_basic.symbol_tables import hasSymbolTableLookup
22
+ from owl_basic.algorithms import representative
23
+ from owl_basic.owltyping.type_system import (OwlType, NumericOwlType, ObjectOwlType, StringOwlType, ByteArrayOwlType)
24
+
25
+ # TODO: This block to import OwlRuntime exists in multiple locations
26
+ # Load the OWL Runtime library so we may both call and reference
27
+ # methods within it. For this to work, the compiler/codegen/clr directory must
28
+ # contain a copy of the OwlRuntime.dll.
29
+ owl_runtime_filename = 'OwlRuntime.dll'
30
+ owl_runtime_path = os.path.join(os.path.dirname(__file__),
31
+ owl_runtime_filename)
32
+ try:
33
+ clr.AddReferenceToFileAndPath(owl_runtime_path)
34
+ except IOError as e:
35
+ logging.critical(e)
36
+ logging.critical("A copy of the Owl Runtime Library (OwlRuntime.dll) must "
37
+ "be present in the compiler/codegen/clr directory")
38
+ sys.exit(1)
39
+
40
+ import OwlRuntime
41
+
42
+ class CodeGenerationError(Exception):
43
+ def __init__(self, value):
44
+ self.value = value
45
+ def __str__(self):
46
+ return repr(self.value)
47
+
48
+ def binaryTypeMatch(operator, lhs_type, rhs_type):
49
+ '''
50
+ Returns True if the left hand side and right hand side of the supplied binary operator
51
+ match the supplied types.
52
+ :param lhs_type: The Type of the left hand side expression
53
+ :param rhs_type: The Type of the right hand side expression
54
+ :returns: True if the types match, otherwise False
55
+ '''
56
+ return operator.lhs.actualType.isA(lhs_type) and operator.lhs.actualType.isA(rhs_type)
57
+
58
+ def getMethod(type, name, *args):
59
+ '''
60
+ Obtain the MethodInfo object from a type for a method called name
61
+ with argument types equivalent to the OWL BASIC types supplied in
62
+ *args.
63
+ '''
64
+ return type.GetMethod(name, System.Array[System.Type]([cts.mapType(arg) for arg in args]))
65
+
66
+ class CilVisitor(Visitor):
67
+ '''
68
+ Emit CIL bytecode whilst traversing the AST/CFG
69
+ '''
70
+
71
+
72
+ def __init__(self, assembly_generator, type_builder, method_builder, line_mapper, doc):
73
+ '''
74
+ Create a new CilVisitor for generating a CIL method.
75
+ :param type_builder A System.Reflection.Emit.TypeBuilder
76
+ :param entry_point_node The entry point CFG node of a method
77
+ :param doc: The ISymbolDocumentWriter for debugging information
78
+ '''
79
+ self.assembly_generator = assembly_generator
80
+ self.type_builder = type_builder
81
+ self.method_builder = method_builder
82
+ self.line_mapper = line_mapper
83
+ self.doc = doc
84
+
85
+ # Pending rvalue - used using generation of assignment statements
86
+ # A callable used to defer generation of code to get the right stack sequence
87
+ self.__pending_rvalue = None
88
+
89
+ # Used for tracking whether we need to generation a query on INPUT
90
+ self.__query = True
91
+
92
+ # Used for the local variable builder for the temporary queue used by INPUT
93
+ self.__queue_builder = None
94
+
95
+ self.__cil_debug = True
96
+
97
+ # Do we allow arrays to be re-DIMed; BBC BASIC does not
98
+ self.__allow_redimension = False
99
+
100
+ # Get the type of OwnRuntime.BasicCommand so we can retrieve methods
101
+ self.basic_commands_type = clr.GetClrType(OwlRuntime.BasicCommands)
102
+ self.memory_map_type = clr.GetClrType(OwlRuntime.MemoryMap)
103
+ self.string_type = clr.GetClrType(System.String)
104
+ self.math_type = clr.GetClrType(System.Math)
105
+ self.console_type = clr.GetClrType(System.Console)
106
+ self.type_type = clr.GetClrType(System.Type)
107
+ self.array_type = clr.GetClrType(System.Array)
108
+ generic_queue_type = clr.GetClrType(System.Collections.Generic.Queue)
109
+ self.object_queue_type = generic_queue_type.MakeGenericType(
110
+ System.Array[System.Type]([clr.GetClrType(System.Object)]))
111
+ self.generator = self.method_builder.GetILGenerator()
112
+ #self.generator.Emit(OpCodes.Nop) # Every method needs at least one OpCode
113
+
114
+ def symbolFromVariable(self, variable):
115
+ name = variable.identifier
116
+ logging.debug("identifier = %s", name)
117
+ symbol_node = findNode(variable, hasSymbolTableLookup)
118
+ symbol = symbol_node.symbolTable.lookup(name)
119
+ assert symbol is not None
120
+ return symbol
121
+
122
+
123
+ def generatePendingRValue(self):
124
+ assert self.__pending_rvalue
125
+ self.__pending_rvalue()
126
+ self.__pending_rvalue = None
127
+
128
+ def lookupMethod(self, name):
129
+ '''
130
+ Return a MethodInfo object for the named PROC or FN
131
+ using the OWL name, e.g. 'PROCfoo'
132
+ '''
133
+ cts_name = self.assembly_generator.lookupCtsMethodName(name)
134
+ return self.assembly_generator.lookupMethod(cts_name)
135
+
136
+ def basicCommandMethod(self, name, *args):
137
+ '''
138
+ Return a MethodInfo object for the named method of OwlRuntime.BasicCommands
139
+ '''
140
+ print(args)
141
+ method = self.basic_commands_type.GetMethod(name)
142
+ assert method, "Could not locate BasicCommands method %s" % name
143
+ return method
144
+
145
+ def basicCommandOverloadedMethod(self, name, *args):
146
+ '''
147
+ Return a MethodInfo object for the named method of OwlRuntime.BasicCommands
148
+ '''
149
+ print(args)
150
+ method = self.basic_commands_type.GetMethod(name, System.Array[System.Type]([cts.mapType(arg) for arg in args]))
151
+ assert method, "Could not locate BasicCommands method %s" % name
152
+ return method
153
+
154
+ def convertClrToOwlBool(self):
155
+ '''
156
+ Convert the CLR bool value on the stack (0 or 1) to an OWL BASIC
157
+ integer on the stack (0 or -1)
158
+ '''
159
+ self.generator.Emit(OpCodes.Neg)
160
+
161
+ def successorOf(self, node):
162
+ assert len(node.outEdges) <= 1
163
+ if len(node.outEdges) == 0:
164
+ return None
165
+ return representative(node.outEdges)
166
+
167
+ def checkMark(self, statement):
168
+ '''
169
+ If this basic block has not yet been marked, mark the label prior
170
+ to the next instruction to be emitted into the CIL stream.
171
+ '''
172
+ if not statement.block.is_label_marked:
173
+ self.generator.MarkLabel(statement.block.label)
174
+ statement.block.is_label_marked = True
175
+
176
+ def visitAstNode(self, node):
177
+ raise CodeGenerationError("Visiting unhandled node %s" % node)
178
+
179
+ def visitAstStatement(self, statement):
180
+ raise CodeGenerationError("Visiting unhandled statement %s" % statement)
181
+
182
+ def visitData(self, data):
183
+ #self.checkMark(data)
184
+ pass
185
+
186
+ def visitRem(self, rem):
187
+ #self.checkMark(rem)
188
+ pass
189
+
190
+ def visitAllocateArray(self, allocator):
191
+ logging.debug("Visiting %s", allocator)
192
+ print(allocator.dimensions)
193
+ assert len(allocator.dimensions) > 0
194
+ symbol_node = findNode(allocator, hasSymbolTableLookup)
195
+ symbol = symbol_node.symbolTable.lookup(allocator.identifier)
196
+ assert symbol is not None
197
+ assert symbol.storeEmitter is not None
198
+ assert symbol.loadEmitter is not None
199
+ assert symbol.type.isArray()
200
+ element_type = symbol.type.elementType()
201
+ assert element_type is not None
202
+
203
+ # Check that the array hasn't already been allocated. We could
204
+ # control this through a compiler option.
205
+ if not self.__allow_redimension:
206
+ symbol.loadEmitter(self.generator) # Push the array reference on to the stack
207
+ begin_allocation = self.generator.DefineLabel()
208
+ self.generator.Emit(OpCodes.Brfalse, begin_allocation) # TODO: Short branch # TODO: Short branch
209
+ emitLdc_I4(self.generator, self.line_mapper.physicalToLogical(allocator.lineNum)) # Load logical line number onto the stack
210
+ bad_dim_exception_ctor = clr.GetClrType(OwlRuntime.BadDimException).GetConstructor(System.Array[System.Type]([System.Int32]))
211
+ assert bad_dim_exception_ctor
212
+ self.generator.Emit(OpCodes.Newobj, bad_dim_exception_ctor) # BadDimException on the stack
213
+ self.generator.Emit(OpCodes.Throw)
214
+ self.generator.MarkLabel(begin_allocation)
215
+
216
+ num_dims = len(allocator.dimensions)
217
+ assert num_dims > 0
218
+ if num_dims == 1:
219
+ allocator.dimensions[0].accept(self)
220
+ self.generator.Emit(OpCodes.Newarr, cts.mapType(element_type))
221
+ else:
222
+ ctor_args = []
223
+ for dimension in allocator.dimensions:
224
+ dimension.accept(self)
225
+ ctor_args.append(cts.typeof(System.Int32))
226
+ ctor = cts.symbolType(symbol).GetConstructor(System.Array[System.Type](ctor_args))
227
+ self.generator.Emit(OpCodes.Newobj, ctor)
228
+ symbol.storeEmitter(self.generator)
229
+
230
+ def visitScalarAssignment(self, assignment):
231
+ logging.debug("Visiting %s", assignment)
232
+ #self.checkMark(assignment)
233
+ # The code for generating the rvalue may need to be interleaved
234
+ # with the code for generating the lvalue, in cases where the
235
+ # lvalue is an assignment to an array element or an indirection
236
+ # operator. To handle these cases, we store a reference ot the
237
+ # rvalue in this visitor and generate the lvalue. We expect
238
+ # the lvalue generator to also generate the code for the rvalue at
239
+ # the appropriate point, and then set the stored rvalue in the
240
+ # visitor to None.
241
+
242
+ def generateRValue(self=self, r_value=assignment.rValue):
243
+ r_value.accept(self) # Store the rvalue
244
+
245
+ self.__pending_rvalue = generateRValue
246
+ assignment.lValue.accept(self) # Store the top of the stack into the lValue
247
+ assert self.__pending_rvalue is None # Check that the rvalue has been used
248
+
249
+ def visitVariable(self, variable):
250
+ logging.debug("Visiting %s", variable)
251
+ symbol = self.symbolFromVariable(variable)
252
+ logging.debug(repr(symbol))
253
+
254
+ # If this is an l-value take the rvalue from the top of the stack, and assign it,
255
+ # otherwise read from that value
256
+ if variable.isLValue:
257
+ assert self.__pending_rvalue is not None
258
+ # Generate the code for the rvalue
259
+ self.generatePendingRValue()
260
+ # Store in the lvalue
261
+ # Lookup in the symbol table
262
+ symbol.storeEmitter(self.generator)
263
+ else:
264
+ symbol.loadEmitter(self.generator)
265
+
266
+ def visitIndexer(self, indexer):
267
+ logging.debug("Visiting %s", indexer)
268
+ symbol = self.symbolFromVariable(indexer)
269
+ logging.debug(repr(indexer))
270
+
271
+ num_dims = len(indexer.indices)
272
+ assert num_dims > 0
273
+ if num_dims == 1:
274
+ # If this is an l-value take the rvalue from the top of the stack, and assign it,
275
+ # otherwise read from that value
276
+ if indexer.isLValue:
277
+ assert self.__pending_rvalue is not None
278
+ symbol.loadEmitter(self.generator) # Push array reference on the stack
279
+ indexer.indices[0].accept(self) # Push array index on the stack
280
+ self.generatePendingRValue() # Push the element value on the stack
281
+ emitStelem_T(self.generator, cts.mapType(symbol.type.elementType())) # Store element
282
+ else:
283
+ symbol.loadEmitter(self.generator) # Push array reference on the stack
284
+ indexer.indices[0].accept(self) # Push array index on the stack
285
+ emitLdelem_T(self.generator, cts.mapType(symbol.type.elementType())) # Load element
286
+ else:
287
+ # If this is an l-value take the rvalue from the top of the stack, and assign it,
288
+ # otherwise read from that value
289
+ if indexer.isLValue:
290
+ symbol.loadEmitter(self.generator) # Push array reference on the stack
291
+ for index in indexer.indices:
292
+ index.accept(self) # Push array index on the stack
293
+ self.generatePendingRValue() # Push the element value on the stack
294
+ self.generator.Emit(OpCodes.Call, cts.symbolType(symbol).GetMethod("Set"))
295
+ else:
296
+ symbol.loadEmitter(self.generator) # Push array reference on the stack
297
+ for index in indexer.indices:
298
+ index.accept(self) # Push array index on the stack
299
+ self.generator.Emit(OpCodes.Call, cts.symbolType(symbol).GetMethod("Get"))
300
+
301
+ def visitLiteralString(self, literal_string):
302
+ logging.debug("Visiting %s", literal_string)
303
+ logging.debug("value = %s", literal_string.value)
304
+ self.generator.Emit(OpCodes.Ldstr, literal_string.value)
305
+
306
+ def visitCast(self, cast):
307
+ logging.debug("Visiting %s", cast)
308
+ # Get the value onto the stack
309
+ cast.value.accept(self)
310
+
311
+ # TODO: Use multimethods in here : http://www.artima.com/weblogs/viewpost.jsp?thread=101605
312
+ #
313
+ # Convert - Int32 can be exactly converted to Double
314
+ if cast.sourceType == IntegerOwlType():
315
+ if cast.targetType == FloatOwlType():
316
+ self.generator.Emit(OpCodes.Conv_R8)
317
+ return
318
+ if cast.targetType == ByteOwlType():
319
+ # TODO: Are BBC BASIC bytes signed, or unsigned?
320
+ # Should we truncate the value here to 0-255 ?
321
+ return
322
+ if cast.targetType == ObjectOwlType():
323
+ self.generator.Emit(OpCodes.Box, cts.mapType(IntegerOwlType()))
324
+ return
325
+ elif cast.sourceType == ByteOwlType():
326
+ if cast.targetType == FloatOwlType():
327
+ self.generator.Emit(OpCodes.Conv_R8)
328
+ return
329
+ if cast.targetType == IntegerOwlType():
330
+ # TODO: Is this correct?
331
+ pass
332
+ if cast.targetType == ObjectOwlType():
333
+ self.generator.Emit(OpCodes.Box, cts.mapType(IntegerOwlType()))
334
+ return
335
+ elif cast.sourceType == FloatOwlType():
336
+ if cast.targetType == IntegerOwlType():
337
+ self.generator.Emit(OpCodes.Conv_Ovf_I4)
338
+ return
339
+ if cast.targetType == AddressOwlType():
340
+ self.generator.Emit(OpCodes.Conv_Ovf_I)
341
+ return
342
+ if cast.targetType == ByteOwlType():
343
+ # TODO: Are BBC BASIC bytes signed, or unsigned?
344
+ self.generator.Emit(OpCodes.Conv_Ovf_I4)
345
+ return
346
+ if cast.targetType == ObjectOwlType():
347
+ self.generator.Emit(OpCodes.Box, cts.mapType(IntegerOwlType()))
348
+ return
349
+ elif cast.sourceType == ObjectOwlType():
350
+ if cast.targetType == StringOwlType():
351
+ self.generator.Emit(OpCodes.Castclass, cts.mapType(StringOwlType()))
352
+ return
353
+ else:
354
+ self.generator.Emit(OpCodes.Unbox, cts.mapType(cast.targetType))
355
+ self.generator.Emit(OpCodes.Ldobj, cts.mapType(cast.targetType))
356
+ return
357
+
358
+ errors.internal("Unsupported cast from %s to %s" % (cast.sourceType, cast.targetType))
359
+
360
+ def visitLiteralInteger(self, literal_integer):
361
+ logging.debug("Visiting %s", literal_integer)
362
+ logging.debug("value = %s", literal_integer.value)
363
+ emitLdc_I4(self.generator, literal_integer.value)
364
+
365
+ def visitLiteralFloat(self, literal_float):
366
+ logging.debug("Visiting %s", literal_float)
367
+ logging.debug("value = %s", literal_float.value)
368
+ self.generator.Emit(OpCodes.Ldc_R8, literal_float.value)
369
+
370
+ def visitVdu(self, vdu):
371
+ logging.debug("Visiting %s", vdu)
372
+ #self.checkMark(vdu)
373
+ assert hasattr(vdu.bytes, '__getitem__')
374
+ assert hasattr(vdu.bytes, '__len__')
375
+
376
+ # TODO: For long lists of VDU items it may be cheaper to
377
+ # assemble an array of bytes and then make one call
378
+ for item in vdu.bytes:
379
+ item.accept(self)
380
+
381
+ def visitVduItem(self, item):
382
+ item.item.accept(self) # Value on the stack
383
+ # Convert to a byte or short as appropriate
384
+ if item.length == 1 or item.length == 9:
385
+ self.generator.Emit(OpCodes.Conv_I1)
386
+ arg_type = System.Byte # TODO: Signed or unsigned?
387
+ elif item.length == 2:
388
+ self.generator.Emit(OpCodes.Conv_I2)
389
+ arg_type = System.Int16 # TODO: Signed or unsigned?
390
+ vdu_method = self.basic_commands_type.GetMethod('Vdu', System.Array[System.Type]([cts.typeof(arg_type)]))
391
+ self.generator.Emit(OpCodes.Call, vdu_method)
392
+ if item.length == 9:
393
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('VduFlush'))
394
+
395
+ def visitCls(self, cls):
396
+ logging.debug("Visiting %s", cls)
397
+ #self.checkMark(cls)
398
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('Cls'))
399
+
400
+ def visitDefineProcedure(self, defproc):
401
+ logging.debug("Visiting %s", defproc)
402
+ if self.__cil_debug:
403
+ self.generator.Emit(OpCodes.Nop)
404
+
405
+ def visitDefineFunction(self, deffn):
406
+ logging.debug("Visiting %s", deffn)
407
+ if self.__cil_debug:
408
+ self.generator.Emit(OpCodes.Nop)
409
+
410
+ def visitCallProcedure(self, call_proc):
411
+ logging.debug("Visiting %s", call_proc)
412
+ logging.debug("name = %s", call_proc.name)
413
+ #self.checkMark(call_proc)
414
+
415
+ proc_method_info = self.lookupMethod(call_proc.name)
416
+
417
+ for actual_parameter in call_proc.actualParameters:
418
+ actual_parameter.accept(self)
419
+
420
+ self.generator.Emit(OpCodes.Call, proc_method_info)
421
+
422
+ def visitReturnFromProcedure(self, endproc):
423
+ logging.debug("Visiting %s", endproc)
424
+ # TODO: Must not be used from within an exception handler
425
+ #self.checkMark(endproc)
426
+ self.generator.Emit(OpCodes.Ret)
427
+
428
+ def visitUserFunc(self, user_func):
429
+ logging.debug("Visiting %s", user_func)
430
+ logging.debug("name = %s", user_func.name)
431
+
432
+ user_func_method_info = self.lookupMethod(user_func.name)
433
+
434
+ for actual_parameter in user_func.actualParameters:
435
+ actual_parameter.accept(self)
436
+
437
+ self.generator.Emit(OpCodes.Call, user_func_method_info)
438
+
439
+ def visitReturnFromFunction(self, func):
440
+ logging.debug("Visiting %s", func)
441
+ #self.checkMark(func)
442
+ # TODO: Must not be used from within an exception handler
443
+ func.returnValue.accept(self)
444
+ self.generator.Emit(OpCodes.Ret)
445
+
446
+ def visitRestore(self, restore):
447
+ # TODO: Can we RESTORE to lines which don't contain DATA?
448
+ logging.debug("Visiting %s", restore)
449
+ logging.debug("target_logical_line = %s", restore.targetLogicalLine)
450
+ #self.checkMark(restore)
451
+ # Lookup the data pointer value for this line number
452
+ self.generator.Emit(OpCodes.Ldsfld, self.assembly_generator.data_line_number_map_field) # Load the dictionary onto the stack
453
+ restore.targetLogicalLine.accept(self) # Push the line number onto the stack
454
+ get_item_method_info = cts.int_int_dictionary_type.GetMethod('get_Item')
455
+ self.generator.Emit(OpCodes.Call, get_item_method_info) # Call get_Item and the put the new data pointer result on the stack
456
+ emitLdc_I4(self.generator, 1)
457
+ self.generator.Emit(OpCodes.Sub) # Subtract 1, because READ will pre-increment the data index
458
+ self.generator.Emit(OpCodes.Stsfld, self.assembly_generator.data_index_field)
459
+
460
+ def visitRepeat(self, repeat):
461
+ logging.debug("Visiting %s", repeat)
462
+ #self.checkMark(repeat)
463
+ repeat.label = self.generator.DefineLabel()
464
+ self.generator.MarkLabel(repeat.label)
465
+ if self.__cil_debug:
466
+ self.generator.Emit(OpCodes.Nop)
467
+
468
+ def visitUntil(self, until):
469
+ logging.debug("Visiting %s", until)
470
+ #self.checkMark(until)
471
+ if len(until.loopBackEdges) != 0:
472
+ assert len(until.loopBackEdges) == 1
473
+ # Correlated NEXT
474
+ repeat = representative(until.loopBackEdges)
475
+ logging.debug("UNTIL correlates with %s", repeat)
476
+ until.condition.accept(self) # Push the condition onto the stack
477
+ self.generator.Emit(OpCodes.Brfalse, repeat.label) # Branch if false # TODO: Short branch?
478
+ else:
479
+ # Non-correlated UNTIL
480
+ errors.internal("TODO: Non-correlated UNTIL")
481
+
482
+ def visitForToStep(self, for_to_step):
483
+ # TODO: Future optimizations
484
+ # - if STEP is a constant we can simplify the test depending on its sign
485
+ # - if the values are ints we can convert the type of the counter to an int
486
+ # - if last is a constant its worth convert <= last into < last + 1 or the
487
+ # reverse, if we know the sign of step
488
+
489
+ # TODO: Could probably hit most common cases STEP == +1 with completely different
490
+ # code gen FOR i% = 0 TO 9
491
+
492
+ logging.debug("Visiting %s", for_to_step)
493
+ #self.checkMark(for_to_step)
494
+
495
+ # Load the initial counter value onto the stack
496
+ for_to_step.first.accept(self)
497
+
498
+ # Store in the loop counter variable
499
+ name = for_to_step.identifier.identifier
500
+ logging.debug("counter identifier = %s", name)
501
+ counter_symbol = for_to_step.symbolTable.lookup(name)
502
+ counter_type = cts.symbolType(counter_symbol)
503
+ assert counter_symbol is not None
504
+ logging.debug(repr(counter_symbol))
505
+ counter_symbol.storeEmitter(self.generator)
506
+
507
+ # Evaluate the last value and store in an unnamed local
508
+ last_value_local = self.generator.DeclareLocal(counter_type)
509
+ for_to_step.last.accept(self)
510
+ self.generator.Emit(OpCodes.Stloc, last_value_local)
511
+
512
+ # Evaluate the step value and store in an unnamed local
513
+ step_value_local = self.generator.DeclareLocal(counter_type)
514
+ for_to_step.step.accept(self)
515
+ self.generator.Emit(OpCodes.Stloc, step_value_local)
516
+
517
+ # The loop body goes in here - later, but first...
518
+ loop_body_label = self.generator.DefineLabel()
519
+ self.generator.MarkLabel(loop_body_label)
520
+
521
+ # Define a function (closure) which can be called later to generate
522
+ # the code for the corresponding NEXT statements
523
+ def correspondingNext():
524
+ # Increment the counter
525
+ counter_symbol.loadEmitter(self.generator) # Load the counter
526
+ self.generator.Emit(OpCodes.Ldloc, step_value_local) # Load the STEP value
527
+ self.generator.Emit(OpCodes.Add) # Add
528
+ counter_symbol.storeEmitter(self.generator) # Load the counter # Store the counter
529
+
530
+ # Check the sign of the step value
531
+ self.generator.Emit(OpCodes.Ldloc, step_value_local) # Load the STEP value
532
+ emitLdc_T(self.generator, 0, counter_type) # Push zero on the stack
533
+
534
+ positive_step_label = self.generator.DefineLabel()
535
+ self.generator.Emit(OpCodes.Bgt, positive_step_label) # if step > 0 jump to positive_step_label # TODO: Short branch?
536
+
537
+ loop_back_label = self.generator.DefineLabel()
538
+
539
+ # step is negative - implement >= as NOT <
540
+ counter_symbol.loadEmitter(self.generator) # Load the counter
541
+ self.generator.Emit(OpCodes.Ldloc, last_value_local) # Load the last value
542
+ self.generator.Emit(OpCodes.Clt) # Compare less-than
543
+ emitLdc_I4(self.generator, 0) # Load 0
544
+ self.generator.Emit(OpCodes.Ceq) # Compare equal
545
+ self.generator.Emit(OpCodes.Br, loop_back_label) # TODO: Short branch?
546
+
547
+ # step is positive - implement <= as NOT >
548
+ self.generator.MarkLabel(positive_step_label)
549
+ counter_symbol.loadEmitter(self.generator) # Load the counter
550
+ self.generator.Emit(OpCodes.Ldloc, last_value_local) # Load the last value
551
+ self.generator.Emit(OpCodes.Cgt) # Compare greater-than
552
+ emitLdc_I4(self.generator, 0) # Load 0
553
+ self.generator.Emit(OpCodes.Ceq) # Compare equal
554
+
555
+ # loop back if not finished
556
+ self.generator.MarkLabel(loop_back_label)
557
+ self.generator.Emit(OpCodes.Brtrue, loop_body_label)
558
+
559
+ # Attach the closure to the for_to_step object for later use
560
+ for_to_step.generateNext = correspondingNext
561
+
562
+ def visitNext(self, next):
563
+ logging.debug("Visiting %s", next)
564
+ #self.checkMark(next)
565
+ if len(next.loopBackEdges) != 0:
566
+ assert len(next.loopBackEdges) == 1
567
+ # Correlated NEXT
568
+ for_to_step = representative(next.loopBackEdges)
569
+ logging.debug("NEXT correlates with %s", for_to_step)
570
+ for_to_step.generateNext()
571
+ else:
572
+ # Non-correlated NEXT
573
+ errors.internal("TODO: Non-correlated NEXT")
574
+
575
+ def visitReadFunc(self, read_func):
576
+ # Determine the type of the value and dispatch appropriately
577
+ # Read the DATA, evaluate the expression in the context of the
578
+ # value required, and place the value of the stack.
579
+ logging.debug("Visiting %s", read_func)
580
+ # TODO: Convert IndexOutOfRangeException to NoDataException
581
+ # Get the DATA as a string
582
+ self.generator.Emit(OpCodes.Ldsfld, self.assembly_generator.data_field) # Load the DATA array onto the stack
583
+ self.generator.Emit(OpCodes.Ldsfld, self.assembly_generator.data_index_field) # Load the DATA index onto the stack
584
+ emitLdc_I4(self.generator, 1) # Load 1 onto the stack
585
+ self.generator.Emit(OpCodes.Add) # Increment
586
+ self.generator.Emit(OpCodes.Dup) # Duplicate the incremented DATA index
587
+ self.generator.Emit(OpCodes.Stsfld, self.assembly_generator.data_index_field) # Store the incremented DATA index
588
+ self.generator.Emit(OpCodes.Ldelem_Ref) # Push the DATA element (a string) onto the stack
589
+
590
+ # DEBUG
591
+ #self.generator.Emit(OpCodes.Dup)
592
+ #print_method = self.basic_commands_type.GetMethod("Print", System.Array[System.Type]([cts.mapType(StringOwlType())]))
593
+ #self.generator.Emit(OpCodes.Call, print_method)
594
+ #self.generator.Emit(OpCodes.Call, self.basicCommandMethod('NewLine'))
595
+ # END DEBUG
596
+
597
+ # TODO: Convert to the required type
598
+ system_convert_type = clr.GetClrType(System.Convert)
599
+
600
+ if read_func.actualType == ByteOwlType():
601
+ conversion_method = system_convert_type.GetMethod("ToByte", System.Array[System.Type]([clr.GetClrType(str)]))
602
+ elif read_func.actualType == IntegerOwlType():
603
+ conversion_method = system_convert_type.GetMethod("ToInt32", System.Array[System.Type]([clr.GetClrType(str)]))
604
+ elif read_func.actualType == FloatOwlType():
605
+ conversion_method = system_convert_type.GetMethod("ToDouble", System.Array[System.Type]([clr.GetClrType(str)]))
606
+ else:
607
+ conversion_method = None
608
+ # TODO: etc
609
+ # TODO: Should handle conversion failures gracefully. How does BBC BASIC do this?
610
+
611
+ if conversion_method:
612
+ self.generator.Emit(OpCodes.Call, conversion_method)
613
+
614
+ def visitDyadicByteIndirection(self, dyadic):
615
+ logging.debug("Visiting %s", dyadic)
616
+ # If this is an l-value take the value from the top of the stack, and assign it
617
+ # as a byte to the location, otherwise read from that value
618
+ if dyadic.isLValue:
619
+ # Check that there is a pending rvalue waiting to be written
620
+ assert self.__pending_rvalue is not None
621
+ # Pop the byte on top of the stack and write to the location
622
+ logging.debug("Dyadic byte indirection l-value")
623
+ # Are we writing to a block or directly into memory?
624
+ if dyadic.base.formalType == AddressOwlType():
625
+ # Writing directly into address space
626
+ # Push the array representing our faked address space onto the stack
627
+ memory_getter = self.memory_map_type.GetMethod("get_Memory")
628
+ self.generator.Emit(OpCodes.Call, memory_getter)
629
+
630
+ dyadic.base.accept(self) # Push the base address onto the stack
631
+ dyadic.offset.accept(self) # Push the offset onto the stack
632
+ self.generator.Emit(OpCodes.Add) # Add base to offset
633
+
634
+ # Get the value on the stack here
635
+ self.generatePendingRValue()
636
+
637
+ self.generator.Emit(OpCodes.Stelem_I1) # Store into the array
638
+
639
+ elif dyadic.base.formalType == ByteArrayOwlType(): # TODO: Check for rank == 1
640
+ # Writing into a byte array
641
+ # TODO: get the array onto the stack
642
+ logging.critical("TODO: Dyadic byte indirection array l-value")
643
+ pass
644
+ # TODO: Index and write into the array
645
+ else:
646
+ # Read from the location and push onto the stack
647
+ logging.debug("Dyadic byte indirection r-value")
648
+ # Are we reading from a block or directly from memory?
649
+ if dyadic.base.formalType == AddressOwlType():
650
+ # Reading directly from address space
651
+ # Push the array representing our faked address space onto the stack
652
+ memory_getter = self.memory_map_type.GetMethod("get_Memory")
653
+ self.generator.Emit(OpCodes.Call, memory_getter)
654
+
655
+ dyadic.base.accept(self) # Push the base address onto the stack
656
+ dyadic.offset.accept(self) # Push the offset onto the stack
657
+ self.generator.Emit(OpCodes.Add) # Add base to offset
658
+
659
+ self.generator.Emit(OpCodes.Ldelem_I1) # Store into the array
660
+ else:
661
+ logging.critical("TODO: Dyadic byte indirection array r-value")
662
+
663
+ def visitPrint(self, print_stmt):
664
+ logging.debug("Visiting %s", print_stmt)
665
+ #self.checkMark(print_stmt)
666
+ # Convert each print item into a call to the runtime library
667
+ logging.debug("print list = %s", str(print_stmt.printList))
668
+ suppress_newline = False
669
+
670
+ if print_stmt.printList is not None:
671
+ for print_item in print_stmt.printList:
672
+ item = print_item.item
673
+ item.accept(self)
674
+ if not isinstance(item, PrintManipulator):
675
+ print_method = self.basic_commands_type.GetMethod("Print", System.Array[System.Type]([cts.mapType(item.actualType)]))
676
+ self.generator.Emit(OpCodes.Call, print_method)
677
+
678
+ if len(print_stmt.printList) > 0:
679
+ last_item = print_stmt.printList[-1].item
680
+ if isinstance(last_item, PrintManipulator) and last_item.manipulator == ';':
681
+ suppress_newline = True
682
+
683
+ if not suppress_newline:
684
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('NewLine'))
685
+
686
+ def visitInput(self, input):
687
+ logging.debug("Visiting %s", input)
688
+ #self.checkMark(input)
689
+ # Convert each input item int a call to the runtime library
690
+ logging.debug("input list = %s", str(input.inputList))
691
+ self.__query = True
692
+ items = input.inputList
693
+ if items is not None:
694
+ print(items)
695
+ while len(items) > 0:
696
+ item = items.pop(0).item
697
+ if not isinstance(item, Variable):
698
+ print("Input Item = ", item)
699
+ item.accept(self)
700
+ if isinstance(item, LiteralString):
701
+ self.__query = False
702
+ print_method = self.basic_commands_type.GetMethod("Print", System.Array[System.Type]([cts.mapType(item.actualType)]))
703
+ self.generator.Emit(OpCodes.Call, print_method)
704
+ else:
705
+ variables = [item]
706
+ while len(items) > 0 and isinstance(items[0], Variable):
707
+ item = items.pop(0).item
708
+ variables.append(item)
709
+ # Set up the function call to input
710
+ emitLdc_I4(self.generator, 1 if self.__query else 0) # Push __query on the stack
711
+ emitLdc_I4(self.generator, len(variables)) # Push the number of variables onto the stack
712
+ self.generator.Emit(OpCodes.Newarr, clr.GetClrType(System.Type)) # Array of Types on the stack
713
+
714
+ var_types = (cts.symbolType(self.symbolFromVariable(var)) for var in variables)
715
+ for i, type in enumerate(var_types):
716
+ self.generator.Emit(OpCodes.Dup) # Array on the stack # TODO: Load local
717
+ emitLdc_I4(self.generator, i) # Index on the stack
718
+ self.generator.Emit(OpCodes.Ldtoken, type) # Type token on the stack
719
+ get_type_from_handle_method = self.type_type.GetMethod("GetTypeFromHandle", System.Array[System.Type]([clr.GetClrType(System.RuntimeTypeHandle)]))
720
+ self.generator.Emit(OpCodes.Call, get_type_from_handle_method) # Type on the stack
721
+ self.generator.Emit(OpCodes.Stelem_Ref) # Store in the array
722
+
723
+ # Call the function
724
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('Input')) # Queue on the stack
725
+ # Store the queue in a local
726
+ if self.__queue_builder is None:
727
+ self.__queue_builder = self.generator.DeclareLocal(clr.GetClrType(self.object_queue_type))
728
+ self.generator.Emit(OpCodes.Stloc, self.__queue_builder)
729
+
730
+ # Dequeue the results into the variables
731
+ dequeue_method = self.object_queue_type.GetMethod("Dequeue")
732
+ for variable in variables:
733
+ def generateRValue(self=self, variable=variable):
734
+ self.generator.Emit(OpCodes.Ldloc, self.__queue_builder) # Load queue local
735
+ self.generator.Emit(OpCodes.Call, dequeue_method)
736
+ cts_type = cts.symbolType(self.symbolFromVariable(variable))
737
+ if cts_type.IsValueType:
738
+ self.generator.Emit(OpCodes.Unbox, cts_type)
739
+ else:
740
+ self.generator.Emit(OpCodes.Castclass, cts.mapType(StringOwlType()))
741
+ self.__pending_rvalue = generateRValue
742
+ variable.accept(self)
743
+ assert self.__pending_rvalue is None
744
+
745
+ def visitInputManipulator(self, node):
746
+ logging.debug("Visiting %s", node)
747
+ manipulator = node.manipulator
748
+ logging.debug("manipulator = %s", manipulator)
749
+ # TODO: This is likely to result in a lot of redundant function calls, so we
750
+ # should be smarter about only making those calls which are necessary
751
+ if manipulator == "'":
752
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('NewLine'))
753
+ self.__query = False
754
+ elif manipulator == ",":
755
+ self.__query = True
756
+ elif manipulator == ";":
757
+ self.__query = True
758
+
759
+ def visitTabH(self, tabh):
760
+ logging.debug("Visiting %s", tabh)
761
+ tabh.xCoord.accept(self) # Get the horizontal tab value onto the stack
762
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('TabH')) # Call the runtime library
763
+
764
+ def visitTabXY(self, tabxy):
765
+ logging.debug("Visiting %s", tabxy)
766
+ tabxy.xCoord.accept(self)
767
+ tabxy.yCoord.accept(self)
768
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('TabXY'))
769
+
770
+ # TODO: This code for converting manipulators into function calls should happen earlier
771
+ # since it is independent of the back-end
772
+
773
+ def visitFormatManipulator(self, node):
774
+ logging.debug("Visiting %s", node)
775
+ manipulator = node.manipulator
776
+ logging.debug("manipulator = %s", manipulator)
777
+ # TODO: This is likely to result in a lot of redundant function calls, so we
778
+ # should be smarter about only making those calls which are necessary
779
+ if manipulator == "~":
780
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('HexFormat'))
781
+ elif manipulator == "'":
782
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('NewLine'))
783
+ elif manipulator == ",":
784
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('RightJustifyNumerics'))
785
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('DecFormat'))
786
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('CompleteField'))
787
+ elif manipulator == ";":
788
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('DisableRightJustifyNumerics'))
789
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('DecFormat'))
790
+
791
+ def visitSpc(self, spc):
792
+ logging.debug("Visiting %s", spc)
793
+ spc.spaces.accept(self)
794
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('Spc'))
795
+
796
+ def visitGetFunc(self, get):
797
+ "GET - read a character from the console"
798
+ logging.debug("Visiting %s", get)
799
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod('Get'))
800
+
801
+ def visitEnd(self, end):
802
+ logging.debug("Visiting %s", end)
803
+ #self.checkMark(end)
804
+ # If we are emitting code in Main, then just return,
805
+ # otherwise throw an EndException
806
+ if "MAIN" in end.entryPoints:
807
+ self.generator.Emit(OpCodes.Ret)
808
+ else:
809
+ emitLdc_I4(self.generator, self.line_mapper.physicalToLogical(end.lineNum)) # Load logical line number onto the stack
810
+ end_exception_ctor = clr.GetClrType(OwlRuntime.EndException).GetConstructor(System.Array[System.Type]([System.Int32]))
811
+ assert end_exception_ctor
812
+ self.generator.Emit(OpCodes.Newobj, end_exception_ctor) # EndException on the stack
813
+ self.generator.Emit(OpCodes.Throw)
814
+
815
+ def visitUnaryMinus(self, unary_minus):
816
+ logging.debug("Visiting %s", unary_minus)
817
+ # TODO: Deal with unknown types (e.g. Object)
818
+ unary_minus.factor.accept(self)
819
+ self.generator.Emit(OpCodes.Neg)
820
+
821
+ def visitPlus(self, plus):
822
+ logging.debug("Visiting %s", plus)
823
+ # TODO: Deal with unknown types (e.g. Object)
824
+ # TODO: Factor out for BinaryNumericOperators
825
+ plus.lhs.accept(self)
826
+ plus.rhs.accept(self)
827
+ self.generator.Emit(OpCodes.Add)
828
+
829
+ def visitMinus(self, minus):
830
+ logging.debug("Visiting %s", minus)
831
+ # TODO: Deal with unknown types (e.g. Object)
832
+ # TODO: Factor out for BinaryNumericOperators
833
+ minus.lhs.accept(self)
834
+ minus.rhs.accept(self)
835
+ self.generator.Emit(OpCodes.Sub)
836
+
837
+ def visitMultiply(self, multiply):
838
+ logging.debug("Visiting %s", multiply)
839
+ # TODO: Deal with unknown types (e.g. Object)
840
+ # TODO: Factor out for BinaryNumericOperators
841
+ multiply.lhs.accept(self)
842
+ multiply.rhs.accept(self)
843
+ self.generator.Emit(OpCodes.Mul)
844
+
845
+ def visitDivide(self, divide):
846
+ logging.debug("Visiting %s", divide)
847
+ # TODO: Deal with unknown types (e.g. Object)
848
+ # TODO: Factor out for BinaryNumericOperators
849
+ divide.lhs.accept(self)
850
+ divide.rhs.accept(self)
851
+ self.generator.Emit(OpCodes.Div)
852
+
853
+ def visitPower(self, power):
854
+ logging.debug("Visiting %s", power)
855
+ # TODO: Deal with unknown types (e.g. Object)
856
+ # TODO: Factor out for BinaryNumericOperators
857
+ power.lhs.accept(self)
858
+ power.rhs.accept(self)
859
+ if binaryTypeMatch(power, IntegerOwlType(), IntegerOwlType()):
860
+ power_method = self.basicCommandOverloadedMethod('Pow', IntegerOwlType(), IntegerOwlType())
861
+ else:
862
+ power_method = self.basicCommandOverloadedMethod('Pow', FloatOwlType(), FloatOwlType())
863
+ self.generator.Emit(OpCodes.Call, power_method)
864
+
865
+ def visitAnd(self, op):
866
+ # TODO: Deal with unknown types (e.g. Object)
867
+ logging.debug("Visiting %s", op)
868
+ op.lhs.accept(self)
869
+ op.rhs.accept(self)
870
+ self.generator.Emit(OpCodes.And)
871
+
872
+ def visitOr(self, op):
873
+ # TODO: Deal with unknown types (e.g. Object)
874
+ logging.debug("Visiting %s", op)
875
+ op.lhs.accept(self)
876
+ op.rhs.accept(self)
877
+ self.generator.Emit(OpCodes.Or)
878
+
879
+ def visitEor(self, op):
880
+ # TODO: Deal with unknown types (e.g. Object)
881
+ logging.debug("Visiting %s", op)
882
+ op.lhs.accept(self)
883
+ op.rhs.accept(self)
884
+ self.generator.Emit(OpCodes.Xor)
885
+
886
+ def visitNot(self, op):
887
+ op.factor.accept(self)
888
+ # TODO: Deal with unknown types (e.g. Object)
889
+ self.generator.Emit(OpCodes.Not)
890
+
891
+ def visitEqual(self, operator):
892
+ logging.debug("Visiting %s", operator)
893
+ operator.lhs.accept(self) # Lhs on the stack
894
+ operator.rhs.accept(self) # Rhs on the stack
895
+
896
+ if binaryTypeMatch(operator, NumericOwlType(), NumericOwlType()):
897
+ self.generator.Emit(OpCodes.Ceq)
898
+ self.convertClrToOwlBool()
899
+ elif binaryTypeMatch(operator, StringOwlType(), StringOwlType()):
900
+ string_equals_method = self.string_type.GetMethod("Equals", System.Array[System.Type]([cts.mapType(StringOwlType()),
901
+ cts.mapType(StringOwlType())]))
902
+ self.generator.Emit(OpCodes.Call, string_equals_method)
903
+ self.convertClrToOwlBool()
904
+ elif binaryTypeMatch(operator, ObjectOwlType(), OwlType()) or \
905
+ binaryTypeMatch(operator, OwlType(), ObjectOwlType()):
906
+ equal_method = self.basic_commands_type.GetMethod("Equal",
907
+ System.Array[System.Type]([cts.mapType(operator.lhs.actualType),
908
+ cts.mapType(operator.rhs.actualType)]))
909
+ self.generator.Emit(OpCodes.Call, equal_method)
910
+
911
+ def visitNotEqual(self, operator):
912
+ logging.debug("Visiting %s", operator)
913
+ operator.lhs.accept(self) # Lhs on the stack
914
+ operator.rhs.accept(self) # Rhs on the stack
915
+
916
+ if binaryTypeMatch(operator, NumericOwlType(), NumericOwlType()):
917
+ self.generator.Emit(OpCodes.Ceq)
918
+ emitLdc_I4(self.generator, 1) # 0 ==> -1, 1 ==> 0
919
+ self.generator.Emit(OpCodes.Sub)
920
+ elif binaryTypeMatch(operator, StringOwlType(), StringOwlType()):
921
+ string_equals_method = self.string_type.GetMethod("Equals", System.Array[System.Type]([cts.mapType(StringOwlType()),
922
+ cts.mapType(StringOwlType())]))
923
+ self.generator.Emit(OpCodes.Call, string_equals_method)
924
+ emitLdc_I4(self.generator, 1) # 0 ==> -1, 1 ==> 0
925
+ self.generator.Emit(OpCodes.Sub)
926
+ elif binaryTypeMatch(operator, ObjectOwlType(), OwlType()) or \
927
+ binaryTypeMatch(operator, OwlType(), ObjectOwlType()):
928
+ equal_method = self.basic_commands_type.GetMethod("NotEqual",
929
+ System.Array[System.Type]([cts.mapType(operator.lhs.actualType),
930
+ cts.mapType(operator.rhs.actualType)]))
931
+ self.generator.Emit(OpCodes.Call, equal_method)
932
+
933
+ def visitLessThan(self, operator):
934
+ logging.debug("Visiting %s", operator)
935
+ operator.lhs.accept(self) # Lhs on the stack
936
+ operator.rhs.accept(self) # Rhs on the stack
937
+
938
+ if binaryTypeMatch(operator, NumericOwlType(), NumericOwlType()):
939
+ self.generator.Emit(OpCodes.Clt)
940
+ self.convertClrToOwlBool()
941
+ elif binaryTypeMatch(operator, StringOwlType(), StringOwlType()):
942
+ string_equals_method = self.string_type.GetMethod("Compare", System.Array[System.Type]([cts.mapType(StringOwlType()),
943
+ cts.mapType(StringOwlType())]))
944
+ self.generator.Emit(OpCodes.Call, string_equals_method)
945
+ # Convert -1 => -1, 0 => 0, +1 => 0
946
+ emitLdc_I4(self.generator, -1)
947
+ self.generator.Emit(OpCodes.Ceq)
948
+ self.convertClrToOwlBool()
949
+ elif binaryTypeMatch(operator, ObjectOwlType(), OwlType()) or \
950
+ binaryTypeMatch(operator, OwlType(), ObjectOwlType()):
951
+ logging.critical("Unsupported less-than operand types")
952
+
953
+ def visitLessThanEqual(self, operator):
954
+ logging.debug("Visiting %s", operator)
955
+ operator.lhs.accept(self) # Lhs on the stack
956
+ operator.rhs.accept(self) # Rhs on the stack
957
+ if binaryTypeMatch(operator, NumericOwlType(), NumericOwlType()):
958
+ self.generator.Emit(OpCodes.Cgt)
959
+ emitLdc_I4(self.generator, 0)
960
+ self.generator.Emit(OpCodes.Ceq)
961
+ self.convertClrToOwlBool()
962
+ elif binaryTypeMatch(operator, StringOwlType(), StringOwlType()):
963
+ string_equals_method = self.string_type.GetMethod("Compare", System.Array[System.Type]([cts.mapType(StringOwlType()),
964
+ cts.mapType(StringOwlType())]))
965
+ self.generator.Emit(OpCodes.Call, string_equals_method)
966
+ # Convert -1 => -1, 0 => -1, +1 => 0
967
+ emitLdc_I4(self.generator, 1)
968
+ self.generator.Emit(OpCodes.Ceq)
969
+ emitLdc_I4(self.generator, 1)
970
+ self.generator.Emit(OpCodes.Sub)
971
+
972
+ elif binaryTypeMatch(operator, ObjectOwlType(), OwlType()) or \
973
+ binaryTypeMatch(operator, OwlType(), ObjectOwlType()):
974
+ logging.critical("Unsupported less-than operand types")
975
+
976
+ def visitGreaterThan(self, operator):
977
+ logging.debug("Visiting %s", operator)
978
+ operator.lhs.accept(self) # Lhs on the stack
979
+ operator.rhs.accept(self) # Rhs on the stack
980
+
981
+ if binaryTypeMatch(operator, NumericOwlType(), NumericOwlType()):
982
+ self.generator.Emit(OpCodes.Cgt)
983
+ self.convertClrToOwlBool()
984
+ elif binaryTypeMatch(operator, StringOwlType(), StringOwlType()):
985
+ string_equals_method = self.string_type.GetMethod("Compare", System.Array[System.Type]([cts.mapType(StringOwlType()),
986
+ cts.mapType(StringOwlType())]))
987
+ self.generator.Emit(OpCodes.Call, string_equals_method)
988
+ # Convert -1 => 0, 0 => 0, +1 => -1
989
+ emitLdc_I4(self.generator, +1)
990
+ self.generator.Emit(OpCodes.Ceq)
991
+ self.convertClrToOwlBool()
992
+ elif binaryTypeMatch(operator, ObjectOwlType(), OwlType()) or \
993
+ binaryTypeMatch(operator, OwlType(), ObjectOwlType()):
994
+ logging.critical("Unsupported greater-than operand types")
995
+
996
+ def visitGreaterThanEqual(self, operator):
997
+ logging.debug("Visiting %s", operator)
998
+ operator.lhs.accept(self) # Lhs on the stack
999
+ operator.rhs.accept(self) # Rhs on the stack
1000
+
1001
+ if binaryTypeMatch(operator, NumericOwlType(), NumericOwlType()):
1002
+ self.generator.Emit(OpCodes.Clt)
1003
+ emitLdc_I4(self.generator, 0)
1004
+ self.generator.Emit(OpCodes.Ceq)
1005
+ self.convertClrToOwlBool()
1006
+ elif binaryTypeMatch(operator, StringOwlType(), StringOwlType()):
1007
+ string_equals_method = self.string_type.GetMethod("Compare", System.Array[System.Type]([cts.mapType(StringOwlType()),
1008
+ cts.mapType(StringOwlType())]))
1009
+ self.generator.Emit(OpCodes.Call, string_equals_method)
1010
+ # Convert -1 => 0, 0 => -1, +1 => -1
1011
+ emitLdc_I4(self.generator, -1)
1012
+ self.generator.Emit(OpCodes.Ceq)
1013
+ emitLdc_I4(self.generator, 1)
1014
+ self.generator.Emit(OpCodes.Sub)
1015
+
1016
+ elif binaryTypeMatch(operator, ObjectOwlType(), OwlType()) or \
1017
+ binaryTypeMatch(operator, OwlType(), ObjectOwlType()):
1018
+ logging.critical("Unsupported greater-than operand types")
1019
+
1020
+ def visitIf(self, if_stmt):
1021
+ logging.debug("Visiting %s", if_stmt)
1022
+ #self.checkMark(if_stmt)
1023
+ if_stmt.condition.accept(self) # Condition on the stack
1024
+ assert if_stmt.trueClause is not None
1025
+ first_true_stmt = if_stmt.trueClause[0]
1026
+
1027
+ # Find the target statement for the negative (ELSE) case, which is
1028
+ # either the ELSE clause or just the following statement if there is
1029
+ # no else clause
1030
+ outEdges = set(if_stmt.outEdges)
1031
+ outEdges.discard(first_true_stmt)
1032
+ assert len(outEdges) == 1
1033
+ representative(outEdges)
1034
+ first_false_stmt = representative(outEdges)
1035
+ if if_stmt.falseClause:
1036
+ assert if_stmt.falseClause[0] is first_false_stmt
1037
+ # Usually (always?) only one of the following calls will result in a generated branch
1038
+ # Whether we branch to the trueClause, the falseClause or both depends of the
1039
+ # ordering of the basic blocks
1040
+ this_block_ordinal = if_stmt.block.topological_order
1041
+ true_block_ordinal = first_true_stmt.block.topological_order
1042
+ false_block_ordinal = first_false_stmt.block.topological_order
1043
+
1044
+ if true_block_ordinal == this_block_ordinal + 1:
1045
+ # The true block immediately succeeds this block
1046
+ # Branch on the negative case
1047
+ self.generator.Emit(OpCodes.Brfalse, first_false_stmt.block.label)
1048
+ # Fall through to the true block
1049
+ elif false_block_ordinal == this_block_ordinal + 1:
1050
+ # The false block immediately succeeds this block
1051
+ # Branch on the positive case
1052
+ self.generator.Emit(OpCodes.Brtrue, first_true_stmt.block.label)
1053
+ # Fall through to the false block
1054
+ else:
1055
+ # Neither the true block nor the false block immediately succeed this block
1056
+ # Conditionally branch on the true case
1057
+ self.generator.Emit(OpCodes.Brtrue, first_true_stmt.block.label)
1058
+ self.generator.Emit(OpCodes.Br, first_false_stmt.block.label)
1059
+ # Unconditionally branch on the false case
1060
+
1061
+ def visitOnGoto(self, ongoto):
1062
+ logging.debug("Visiting %s", ongoto)
1063
+ #self.checkMark(ongoto)
1064
+ # Build the jump table
1065
+ # TODO: There is some duplication here with the flowgraph visitor
1066
+ jump_table = []
1067
+ for target_statement in ongoto.targetStatements:
1068
+ jump_table.append(target_statement.block.label)
1069
+
1070
+ ongoto.switch.accept(self) # Integer on the stack
1071
+ self.generator.Emit.Overloads[OpCode, System.Array[Label]](OpCodes.Switch, System.Array[Label](jump_table))
1072
+ if ongoto.outOfRangeStatement is not None:
1073
+ self.generator.Emit(OpCodes.Br, ongoto.outOfRangeStatement.block.label)
1074
+ else:
1075
+ on_range_exception_ctor = clr.GetClrType(OwlRuntime.OnRangeException).GetConstructor(System.Array[System.Type]([]))
1076
+ assert on_range_exception_ctor
1077
+ self.generator.Emit(OpCodes.Newobj, on_range_exception_ctor) # OnRangeException on the stack
1078
+ self.generator.Emit(OpCodes.Throw)
1079
+
1080
+ def visitLocal(self, local):
1081
+ logging.debug("Visiting %s", local)
1082
+ #self.checkMark(local)
1083
+ if self.__cil_debug:
1084
+ self.generator.Emit(OpCodes.Nop)
1085
+
1086
+ def visitGoto(self, goto):
1087
+ logging.debug("Visiting %s", goto)
1088
+ #self.checkMark(goto)
1089
+ # No code needs to be generated for GOTO statements here, so the
1090
+ # routine which generates code for transferring control from the end
1091
+ # of a basic block with out-degree one will do it.
1092
+ if self.__cil_debug:
1093
+ self.generator.Emit(OpCodes.Nop)
1094
+
1095
+ def visitGcol(self, gcol):
1096
+ logging.debug("Visiting %s", gcol)
1097
+ gcol.mode.accept(self)
1098
+ gcol.logicalColour.accept(self)
1099
+ if gcol.tint is not None:
1100
+ gcol.tint.accept(self)
1101
+ gcol_method = self.basicCommandMethod("GcolTint")
1102
+ else:
1103
+ gcol_method = self.basicCommandMethod("Gcol")
1104
+ self.generator.Emit(OpCodes.Call, gcol_method)
1105
+
1106
+ def visitColour(self, colour):
1107
+ logging.debug("Visiting %s", colour)
1108
+ colour.colour.accept(self)
1109
+ if colour.tint is not None:
1110
+ colour.tint.accept(self)
1111
+ colour_method = self.basicCommandMethod("ColourTint")
1112
+ else:
1113
+ colour_method = self.basicCommandMethod("Colour")
1114
+ self.generator.Emit(OpCodes.Call, colour_method)
1115
+
1116
+ def visitAscFunc(self, asc):
1117
+ logging.debug("Visiting %s", asc)
1118
+ asc.factor.accept(self)
1119
+ asc_method = self.basicCommandMethod("Asc")
1120
+ self.generator.Emit(OpCodes.Call, asc_method)
1121
+
1122
+ def visitAbsFunc(self, abs):
1123
+ logging.debug("Visiting %s", abs)
1124
+ abs.factor.accept(self)
1125
+ abs_method = getMethod(self.math_type, "Abs", abs.factor.actualType)
1126
+ self.generator.Emit(OpCodes.Call, abs_method)
1127
+
1128
+ def visitChrStrFunc(self, chr):
1129
+ logging.debug("Visiting %s", chr)
1130
+ chr.factor.accept(self)
1131
+ chr_method = self.basicCommandMethod("Chr")
1132
+ self.generator.Emit(OpCodes.Call, chr_method)
1133
+
1134
+ def visitRndFunc(self, rnd):
1135
+ logging.debug("Visiting %s", rnd)
1136
+ if rnd.option is None:
1137
+ # Return a four-byte random signed integer between -2147483648 and +2147483647
1138
+ self.generator.Emit(OpCodes.Call, self.basicCommandOverloadedMethod("Rnd"))
1139
+ else:
1140
+ rnd.option.accept(self)
1141
+ self.generator.Emit(OpCodes.Call, self.basicCommandOverloadedMethod("Rnd", IntegerOwlType()))
1142
+
1143
+ def visitInstrFunc(self, instr):
1144
+ logging.debug("Visiting %s", instr)
1145
+ instr.source.accept(self)
1146
+ instr.subString.accept(self)
1147
+ if instr.startPosition is not None:
1148
+ instr.startPosition.accept(self)
1149
+ instr_method = self.basicCommandMethod("InstrAt")
1150
+ else:
1151
+ instr_method = self.basicCommandMethod("Instr")
1152
+ self.generator.Emit(OpCodes.Call, instr_method)
1153
+
1154
+ def visitLenFunc(self, len_func):
1155
+ logging.debug("Visiting %s", len_func)
1156
+ len_func.factor.accept(self)
1157
+ string_count_method = getMethod(self.string_type, "get_Length")
1158
+ self.generator.Emit(OpCodes.Call, string_count_method)
1159
+
1160
+ def visitSgnFunc(self, sgn):
1161
+ logging.debug("Visiting %s", sgn)
1162
+ sgn.factor.accept(self)
1163
+ sgn_method = getMethod(self.math_type, "Sign", sgn.factor.actualType)
1164
+ self.generator.Emit(OpCodes.Call, sgn_method)
1165
+
1166
+ def visitSqrFunc(self, sqr):
1167
+ logging.debug("Visiting %s", sqr)
1168
+ sqr.factor.accept(self)
1169
+ sqr_method = self.basicCommandMethod("Sqr")
1170
+ self.generator.Emit(OpCodes.Call, sqr_method)
1171
+
1172
+ def visitTrueFunc(self, true):
1173
+ emitLdc_I4(self.generator, -1)
1174
+
1175
+ def visitFalseFunc(self, false):
1176
+ emitLdc_I4(self.generator, 0)
1177
+
1178
+ def visitConcatenate(self, concat):
1179
+ logging.debug("Visiting %s", concat)
1180
+ concat.lhs.accept(self)
1181
+ concat.rhs.accept(self)
1182
+ string_concat_method = getMethod(self.string_type, "Concat", StringOwlType(), StringOwlType())
1183
+ self.generator.Emit(OpCodes.Call, string_concat_method)
1184
+
1185
+ def visitLeftStrFunc(self, left_str):
1186
+ logging.debug("Visiting %s", left_str)
1187
+ left_str.source.accept(self) # String the the stack
1188
+ if left_str.length is not None:
1189
+ left_str.length.accept(self) # Length on the stack
1190
+ method = self.basicCommandOverloadedMethod("LeftStr", StringOwlType(), IntegerOwlType())
1191
+ else:
1192
+ method = self.basicCommandOverloadedMethod("LeftStr", StringOwlType())
1193
+ self.generator.Emit(OpCodes.Call, method)
1194
+
1195
+ def visitMidStrFunc(self, mid_str):
1196
+ logging.debug("Visiting %s", mid_str)
1197
+ mid_str.source.accept(self) # String on the stack
1198
+ mid_str.position.accept(self) # 1-based index on stack
1199
+ if mid_str.length is not None:
1200
+ mid_str.length.accept(self) # length on the stack
1201
+ method = self.basicCommandOverloadedMethod("MidStr", StringOwlType(), IntegerOwlType(), IntegerOwlType())
1202
+ else:
1203
+ method = self.basicCommandOverloadedMethod("MidStr", StringOwlType(), IntegerOwlType())
1204
+ self.generator.Emit(OpCodes.Call, method)
1205
+
1206
+ def visitRightStrFunc(self, right_str):
1207
+ logging.debug("Visiting %s", right_str)
1208
+ right_str.source.accept(self) # String on the stack
1209
+ if right_str.length is not None:
1210
+ right_str.length.accept(self)
1211
+ method = self.basicCommandOverloadedMethod("RightStr", StringOwlType(), IntegerOwlType())
1212
+ else:
1213
+ method = self.basicCommandOverloadedMethod("RightStr", StringOwlType())
1214
+ self.generator.Emit(OpCodes.Call, method)
1215
+
1216
+ def visitPosFunc(self, pos):
1217
+ logging.debug("Visiting %s", pos)
1218
+ pos_method = self.basicCommandMethod("Pos")
1219
+ self.generator.Emit(OpCodes.Call, pos_method)
1220
+
1221
+ def visitVposFunc(self, vpos):
1222
+ logging.debug("Visiting %s", vpos)
1223
+ vpos_method = self.basicCommandMethod("VPos")
1224
+ self.generator.Emit(OpCodes.Call, vpos_method)
1225
+
1226
+ def visitMode(self, mode):
1227
+ logging.debug("Visiting %s", mode)
1228
+ #self.checkMark(mode)
1229
+ assert mode.number is not None
1230
+ # TODO: Extended MODE syntax not yet supported
1231
+ mode.number.accept(self)
1232
+ mode_method = self.basicCommandMethod("Mode")
1233
+ self.generator.Emit(OpCodes.Call, mode_method)
1234
+
1235
+ def visitPlot(self, plot):
1236
+ if plot.relative:
1237
+ assert plot.mode is None
1238
+ # PLOT BY x, y is equivalent to PLOT 65, x, y - BB4W only
1239
+ emitLdc_I4(self.generator, 65)
1240
+ elif plot.mode is None:
1241
+ assert plot.relative is None
1242
+ # PLOT x,y is equivalent to PLOT 69, x, y
1243
+ emitLdc_I4(self.generator, 69)
1244
+ else:
1245
+ assert plot.mode is not None
1246
+ plot.mode.accept(self)
1247
+ plot.xCoord.accept(self)
1248
+ plot.yCoord.accept(self)
1249
+ plot_method = self.basicCommandMethod("Plot")
1250
+ self.generator.Emit(OpCodes.Call, plot_method)
1251
+
1252
+ def visitLongJump(self, long_jump):
1253
+ logging.debug("Visiting %s", long_jump)
1254
+ #self.checkMark(long_jump)
1255
+ long_jump.targetLogicalLine.accept(self) # Target line on the stack
1256
+ long_jump_exception_ctor = clr.GetClrType(OwlRuntime.LongJumpException).GetConstructor(System.Array[System.Type]([System.Int32]))
1257
+ assert long_jump_exception_ctor
1258
+ self.generator.Emit(OpCodes.Newobj, long_jump_exception_ctor) # LongJumpException on the stack
1259
+ self.generator.Emit(OpCodes.Throw)
1260
+
1261
+ def visitRaise(self, raise_stmt):
1262
+ logging.debug("Visiting %s", raise_stmt)
1263
+ #self.checkMark(raise_stmt)
1264
+ emitLdc_I4(self.generator, self.line_mapper.physicalToLogical(raise_stmt.lineNum)) # Source line on the stack
1265
+ exception_type = getattr(OwlRuntime, raise_stmt.type)
1266
+ exception_ctor = clr.GetClrType(exception_type).GetConstructor(System.Array[System.Type]([System.Int32]))
1267
+ assert exception_ctor
1268
+ self.generator.Emit(OpCodes.Newobj, exception_ctor) # LongJumpException on the stack
1269
+ self.generator.Emit(OpCodes.Throw)
1270
+
1271
+ def visitIntFunc(self, int_func):
1272
+ logging.debug("Visiting %s", int_func)
1273
+ int_func.factor.accept(self)
1274
+ floor_method = getMethod(self.math_type, "Floor", int_func.factor.actualType)
1275
+ self.generator.Emit(OpCodes.Call, floor_method)
1276
+ self.generator.Emit(OpCodes.Conv_Ovf_I4)
1277
+
1278
+ def visitEvalFunc(self, eval_func):
1279
+ logging.debug("Visiting %s", eval_func)
1280
+ eval_func.factor.accept(self) # String to be EVALed on the stack
1281
+ self.generator.Emit(OpCodes.Ldtoken, self.type_builder) # Push the type of the module we are compiling on the stack
1282
+ get_type_from_handle_method = self.type_type.GetMethod("GetTypeFromHandle", System.Array[System.Type]([clr.GetClrType(System.RuntimeTypeHandle)]))
1283
+ self.generator.Emit(OpCodes.Call, get_type_from_handle_method) # Type on the stack
1284
+ self.generator.Emit(OpCodes.Call, self.basicCommandMethod("Eval"))
1285
+
1286
+ def visitTimeValue(self, time_value):
1287
+ logging.debug("Visiting %s", time_value)
1288
+ get_time_method = self.basicCommandMethod("get_Time")
1289
+ self.generator.Emit(OpCodes.Call, get_time_method)
1290
+
1291
+ def visitTimeStrValue(self, time_str_value):
1292
+ logging.debug("Visiting %s", time_str_value)
1293
+ get_datetime_method = self.basicCommandMethod("get_CurrentDateTime")
1294
+ self.generator.Emit(OpCodes.Call, get_datetime_method)
1295
+
1296
+