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.
- owl_basic/__init__.py +3 -0
- owl_basic/algorithms.py +29 -0
- owl_basic/ast_utils.py +204 -0
- owl_basic/basic_visitor.py +55 -0
- owl_basic/cfg_vertex.py +65 -0
- owl_basic/codegen/__init__.py +0 -0
- owl_basic/codegen/clr/__init__.py +0 -0
- owl_basic/codegen/clr/cil_visitor.py +1296 -0
- owl_basic/codegen/clr/cts.py +56 -0
- owl_basic/codegen/clr/emitters.py +94 -0
- owl_basic/codegen/clr/generate.py +539 -0
- owl_basic/correlation_visitor.py +119 -0
- owl_basic/data_visitor.py +62 -0
- owl_basic/decoder.py +339 -0
- owl_basic/errors.py +22 -0
- owl_basic/flow/__init__.py +17 -0
- owl_basic/flow/basic_block.py +34 -0
- owl_basic/flow/basic_block_identifier.py +66 -0
- owl_basic/flow/basic_block_orderer.py +29 -0
- owl_basic/flow/connectors.py +19 -0
- owl_basic/flow/convert_sub_visitor.py +28 -0
- owl_basic/flow/entry_point_locator.py +55 -0
- owl_basic/flow/entry_point_visitor.py +48 -0
- owl_basic/flow/flow_analysis.py +56 -0
- owl_basic/flow/flow_graph_creator.py +14 -0
- owl_basic/flow/flowgraph_visitor.py +178 -0
- owl_basic/flow/longjump_converter.py +20 -0
- owl_basic/flow/longjump_visitor.py +53 -0
- owl_basic/flow/subroutine_converter.py +38 -0
- owl_basic/flow/traversal.py +110 -0
- owl_basic/gml_visitor.py +151 -0
- owl_basic/line_mapper.py +43 -0
- owl_basic/line_number_visitor.py +65 -0
- owl_basic/main.py +381 -0
- owl_basic/node.py +21 -0
- owl_basic/options.py +22 -0
- owl_basic/owltyping/__init__.py +1 -0
- owl_basic/owltyping/function_type_inferer.py +50 -0
- owl_basic/owltyping/hindley_milner.py +524 -0
- owl_basic/owltyping/set_function_type_visitor.py +25 -0
- owl_basic/owltyping/type_system.py +220 -0
- owl_basic/owltyping/typecheck.py +60 -0
- owl_basic/owltyping/typecheck_visitor.py +471 -0
- owl_basic/parent_visitor.py +37 -0
- owl_basic/process.py +36 -0
- owl_basic/separation_visitor.py +98 -0
- owl_basic/sigil.py +30 -0
- owl_basic/simplify_visitor.py +204 -0
- owl_basic/singleton.py +127 -0
- owl_basic/source_debugging.py +124 -0
- owl_basic/symbol_table_visitor.py +220 -0
- owl_basic/symbol_tables.py +195 -0
- owl_basic/syntax/__init__.py +0 -0
- owl_basic/syntax/ast.py +1081 -0
- owl_basic/syntax/ast_meta.py +228 -0
- owl_basic/syntax/grammar.py +1972 -0
- owl_basic/syntax/lexer.py +943 -0
- owl_basic/syntax/parser.py +77 -0
- owl_basic/utility.py +26 -0
- owl_basic/visitor.py +43 -0
- owl_basic/xml_blocks.py +137 -0
- owl_basic/xml_visitor.py +101 -0
- owl_basic-0.6.0.dist-info/METADATA +37 -0
- owl_basic-0.6.0.dist-info/RECORD +69 -0
- owl_basic-0.6.0.dist-info/WHEEL +5 -0
- owl_basic-0.6.0.dist-info/entry_points.txt +2 -0
- owl_basic-0.6.0.dist-info/licenses/LICENSE +21 -0
- owl_basic-0.6.0.dist-info/licenses/THIRD-PARTY-NOTICES.md +57 -0
- 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
|
+
|