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,56 @@
1
+ '''
2
+ Functions for dealing with the .NET Common Type System
3
+ '''
4
+
5
+ import clr
6
+ import System
7
+
8
+ from owl_basic.owltyping.type_system import (VoidOwlType, IntegerOwlType, FloatOwlType, StringOwlType,
9
+ ByteOwlType, ObjectOwlType)
10
+
11
+ # A basic mapping of OWL BASIC types to CTS types
12
+ type_map = { VoidOwlType() : System.Void,
13
+ IntegerOwlType() : System.Int32,
14
+ FloatOwlType() : System.Double,
15
+ StringOwlType() : System.String,
16
+ ByteOwlType() : System.Byte,
17
+ ObjectOwlType() : System.Object }
18
+
19
+ # Some useful .NET types
20
+ string_array_type = clr.GetClrType(System.String).MakeArrayType()
21
+ generic_dictionary_type = clr.GetClrType(System.Collections.Generic.Dictionary)
22
+ int_int_dictionary_type = generic_dictionary_type.MakeGenericType(
23
+ System.Array[System.Type]((clr.GetClrType(System.Int32), clr.GetClrType(System.Int32))))
24
+
25
+ print()
26
+
27
+ def typeof(t):
28
+ 'Simulate C# typeof operator'
29
+ return clr.GetClrType(t)
30
+
31
+ def mapType(basic_type):
32
+ '''
33
+ Map an OWL BASIC type to its equivalent CTS type
34
+ :param basic_type: A BASIC Type
35
+ '''
36
+ return typeof(type_map[basic_type])
37
+
38
+ def symbolType(symbol):
39
+ '''
40
+ Given a symbol return its CTS type
41
+ :param symbol: A SymbolInfo instance
42
+ :returns: A System.Type
43
+ '''
44
+ t = symbol.type
45
+ if t.isArray():
46
+ # TODO: Rank of array is important here
47
+ assert symbol.rank > 0
48
+ element_type = typeof(type_map[t.elementType()])
49
+ # See http://marcgravell.blogspot.com/2010/03/when-is-int-not-int.html
50
+ # for why MakeArrayType() is different to MakeArrayType(1)
51
+ if symbol.rank == 1:
52
+ return element_type.MakeArrayType()
53
+ return element_type.MakeArrayType(symbol.rank)
54
+ return typeof(type_map[t])
55
+
56
+
@@ -0,0 +1,94 @@
1
+ '''
2
+ Functions which emit the most optimal OpCodes.
3
+ '''
4
+
5
+ import clr
6
+ import System
7
+ from System.Reflection.Emit import *
8
+
9
+ from .cts import typeof
10
+
11
+ def emitLdarg(generator, index):
12
+ assert 0 <= index <= 65535
13
+ if index == 0:
14
+ generator.Emit(OpCodes.Ldarg_0)
15
+ elif index == 1:
16
+ generator.Emit(OpCodes.Ldarg_1)
17
+ elif index == 2:
18
+ generator.Emit(OpCodes.Ldarg_2)
19
+ elif index == 3:
20
+ generator.Emit(OpCodes.Ldarg_3)
21
+ elif 3 < index <= 255:
22
+ generator.Emit.Overloads[OpCode, System.Byte](OpCodes.Ldarg_S, System.Byte(index))
23
+ else:
24
+ generator.Emit(OpCodes.LdArg, index)
25
+
26
+ def emitLdc_I4(generator, constant):
27
+ assert -2147483648 <= constant <= 2147483647
28
+ if constant == -1:
29
+ generator.Emit(OpCodes.Ldc_I4_M1)
30
+ elif constant == 0:
31
+ generator.Emit(OpCodes.Ldc_I4_0)
32
+ elif constant == 1:
33
+ generator.Emit(OpCodes.Ldc_I4_1)
34
+ elif constant == 2:
35
+ generator.Emit(OpCodes.Ldc_I4_2)
36
+ elif constant == 3:
37
+ generator.Emit(OpCodes.Ldc_I4_3)
38
+ elif constant == 4:
39
+ generator.Emit(OpCodes.Ldc_I4_4)
40
+ elif constant == 5:
41
+ generator.Emit(OpCodes.Ldc_I4_5)
42
+ elif constant == 6:
43
+ generator.Emit(OpCodes.Ldc_I4_6)
44
+ elif constant == 7:
45
+ generator.Emit(OpCodes.Ldc_I4_7)
46
+ elif constant == 8:
47
+ generator.Emit(OpCodes.Ldc_I4_8)
48
+ elif -128 <= constant <= 127:
49
+ generator.Emit.Overloads[OpCode, System.SByte](OpCodes.Ldc_I4_S, System.SByte(constant))
50
+ else:
51
+ generator.Emit(OpCodes.Ldc_I4, constant)
52
+
53
+ def emitLdc_T(generator, constant, type):
54
+ '''
55
+ Given a type emit either an integer or floating point number
56
+ '''
57
+ if type == System.Int32:
58
+ emitLdc_I4(generator, int(constant))
59
+ elif type == System.Double:
60
+ generator.Emit(OpCodes.Ldc_R8, float(constant))
61
+ else:
62
+ assert 0, "Unsupported type %s" % type
63
+
64
+ def emitStarg(generator, index):
65
+ assert 0 <= index <= 65535
66
+ if index <= 255:
67
+ generator.Emit.Overloads[OpCode, System.Byte](OpCodes.Starg_S, System.SByte(index))
68
+ else:
69
+ generator.Emit(OpCodes.Starg, index)
70
+
71
+ def emitLdelem_T(generator, type):
72
+ if type == System.Int32:
73
+ generator.Emit(OpCodes.Ldelem_I4)
74
+ elif type == System.Double:
75
+ generator.Emit(OpCodes.Ldelem_R8)
76
+ elif type == System.String:
77
+ generator.Emit(OpCodes.Ldelem, typeof(System.String))
78
+ elif type == System.Object:
79
+ generator.Emit(OpCodes.Ldelem_Ref)
80
+ else:
81
+ generator.Emit(OpCodes.Ldelem, typeof(type))
82
+
83
+ def emitStelem_T(generator, type):
84
+ if type == System.Int32:
85
+ generator.Emit(OpCodes.Stelem_I4)
86
+ elif type == System.Double:
87
+ generator.Emit(OpCodes.Stelem_R8)
88
+ elif type == System.String:
89
+ generator.Emit(OpCodes.Stelem, typeof(System.String))
90
+ elif type == System.Object:
91
+ generator.Emit(OpCodes.Stelem_Ref)
92
+ else:
93
+ generator.Emit(OpCodes.Stelem, typeof(type))
94
+
@@ -0,0 +1,539 @@
1
+ import sys
2
+ import os
3
+ import re
4
+ import logging
5
+ from functools import partial
6
+
7
+ import clr
8
+ import System
9
+ from System.Threading import Thread
10
+ from System.Reflection import *
11
+ from System.Reflection.Emit import *
12
+
13
+ # Debug builds
14
+ from System.Diagnostics.SymbolStore import *
15
+ from System.Diagnostics import *
16
+ import shutil
17
+
18
+ from owl_basic.visitor import Visitor
19
+ from owl_basic.singleton import Singleton
20
+
21
+ from owl_basic.syntax.ast import DefinitionStatement, DefineProcedure, DefineFunction, Local
22
+ from owl_basic.ast_utils import findNode
23
+ from .cil_visitor import CilVisitor, CodeGenerationError
24
+ from owl_basic.symbol_tables import hasSymbolTableLookup, StaticSymbolTable
25
+ from .emitters import *
26
+ from . import cts
27
+ from .cts import typeof
28
+ from owl_basic.algorithms import representative
29
+ from owl_basic.flow.traversal import depthFirstSearch
30
+
31
+ # TODO: This block to import OwlRuntime exists in multiple locations
32
+ # Load the OWL Runtime library so we may both call and reference
33
+ # methods within it. For this to work, the compiler/codegen/clr directory must
34
+ # contain a copy of the OwlRuntime.dll.
35
+ owl_runtime_filename = 'OwlRuntime.dll'
36
+ owl_runtime_path = os.path.join(os.path.dirname(__file__),
37
+ owl_runtime_filename)
38
+ try:
39
+ clr.AddReferenceToFileAndPath(owl_runtime_path)
40
+ except IOError as e:
41
+ logging.critical(e)
42
+ logging.critical("A copy of the Owl Runtime Library (OwlRuntime.dll) must "
43
+ "be present in the compiler/codegen/clr directory")
44
+ sys.exit(1)
45
+
46
+ import OwlRuntime
47
+
48
+ def installRuntimeLibrary(dir_path):
49
+ logging.debug("Installing OWL Runtime Library to %s", dir_path)
50
+ shutil.copyfile(owl_runtime_path, os.path.join(dir_path, owl_runtime_filename))
51
+
52
+ def ctsIdentifier(symbol):
53
+ """
54
+ A mapping from BASIC names with sigils to .NET compliant name
55
+ using Systems Hungarian notation
56
+ """
57
+ # TODO - we can probably do away with this method
58
+ # if we can write the OwlModule class in OWL BASIC or CIL directly.
59
+ name = symbol.name
60
+ if name.endswith('$'):
61
+ return 's' + symbol.name[:-1]
62
+ if name.endswith('%'):
63
+ return 'i' + symbol.name[:-1]
64
+ # TODO: May need additions to deal with arrays
65
+ return 'f' + symbol.name
66
+
67
+
68
+ class AssemblyGenerator(object):
69
+ def __init__(self, line_mapper):
70
+ self.line_mapper = line_mapper
71
+ self.owl_to_clr_method_names = {} # A map of OWL basic names to CLR names
72
+ self.clr_to_owl_method_names = {} # A map of CLR names to OWL basic names
73
+ self.method_builders = {} # A map of CLR names to MethodBuilders
74
+
75
+ def createAndAttachGlobalEmitters(self, global_symbols, type_builder):
76
+
77
+ # Add global variables and their accessors to the class
78
+ for symbol in global_symbols.symbols.values():
79
+ #identifier = ctsIdentifier(symbol)
80
+ identifier = symbol.name
81
+ field_builder = type_builder.DefineField(identifier, cts.symbolType(symbol), FieldAttributes.Private | FieldAttributes.Static)
82
+ print(identifier)
83
+ assert field_builder is not None
84
+ self.createAndAttachFieldEmitters(field_builder, symbol)
85
+
86
+
87
+ def createAndAttachStaticEmitters(self, owl_module):
88
+ # Add accessors for the inherited static variables
89
+ static_symbols = StaticSymbolTable.getInstance()
90
+ for symbol in static_symbols.symbols.values():
91
+ # TODO: If we can fix the names in OwlModule we can use the raw names
92
+ identifier = ctsIdentifier(symbol)
93
+ print(identifier)
94
+ field_info = owl_module.GetField(identifier)
95
+ assert field_info is not None
96
+ self.createAndAttachFieldEmitters(field_info, symbol)
97
+
98
+ def lookupMethod(self, clr_name):
99
+ '''
100
+ Lookup a MethodBuilder using is CLR name
101
+ '''
102
+ return self.method_builders[clr_name]
103
+
104
+ def lookupCtsMethodName(self, owl_name):
105
+ '''
106
+ Lookup from an OWL identifier name (i.e. includes PROC or FN)
107
+ '''
108
+ return self.owl_to_clr_method_names[owl_name]
109
+
110
+ def generateAssembly(self, source_file, name, global_symbols, data_visitor, ordered_basic_blocks):
111
+ """
112
+ :param source_file: The BASIC source file - for debugging information
113
+ :param name: The name given to the assembly to be generated.
114
+ :param ordered_basic_blocks: A mapping type where keys are the entry point name
115
+ and values are a sequence of BasicBlocks for that method/program.
116
+ """
117
+ # Generate an assembly
118
+ # Generate a namespace
119
+ # Generate a static class
120
+ # Create global variables as members
121
+ # For each entry point
122
+ # - create a method
123
+ # Mark the entry point to the assembly
124
+
125
+ # We build the assembly in the current AppDomain
126
+ domain = Thread.GetDomain()
127
+ assembly_name = AssemblyName(name)
128
+ assembly_builder = domain.DefineDynamicAssembly(assembly_name, AssemblyBuilderAccess.RunAndSave)
129
+
130
+ self.makeAssemblyDebuggable(assembly_builder)
131
+
132
+ module_builder = assembly_builder.DefineDynamicModule(name + ".exe", True) # pass True to track debug info
133
+ owl_module = typeof(OwlRuntime.OwlModule)
134
+
135
+ # Set the source file that we want to associate with this module
136
+ self.doc = module_builder.DefineDocument(source_file, System.Guid.Empty, System.Guid.Empty, System.Guid.Empty)
137
+
138
+ type_builder = module_builder.DefineType(name,
139
+ TypeAttributes.Class | TypeAttributes.Public,
140
+ object().GetType())
141
+
142
+ self.createAndAttachStaticEmitters(owl_module)
143
+ self.createAndAttachGlobalEmitters(global_symbols, type_builder)
144
+
145
+ if len(data_visitor.data) > 0:
146
+ self.generateStaticDataInitialization(data_visitor, type_builder)
147
+
148
+ # Generate all the unique method names
149
+ # TODO: This would be sooo much easier if the entry_point.name
150
+ # property had been set useful, and PROC and FN retained in identifier names everywhere!
151
+ # TODO: Should also wrap the main program in DEF PROCMain - safely!
152
+ for entry_name, basic_blocks in ordered_basic_blocks.items():
153
+ entry_point = basic_blocks[0].entryPoint
154
+ if isinstance(entry_point, DefinitionStatement):
155
+ self.createCtsMethodName(entry_point.name)
156
+ else: # Main
157
+ assert entry_name == '__owl__main'
158
+ assert iter(entry_point.entryPoints).next().startswith('MAIN')
159
+ self.createCtsMethodName('FNMain')
160
+
161
+ #for owl_name, clr_name in self.owl_to_clr_method_names.items():
162
+ # print owl_name, " ==> ", clr_name
163
+
164
+ # Generate all the empty methods, so we can retrieve them from the type builder
165
+ for basic_blocks in ordered_basic_blocks.values():
166
+ self.generateMethod(type_builder, basic_blocks)
167
+
168
+ # Generate the body of each method
169
+ stop_on_error = False
170
+ for basic_blocks in ordered_basic_blocks.values():
171
+ try:
172
+ self.generateMethodBody(type_builder, basic_blocks)
173
+ except CodeGenerationError as e:
174
+ logging.critical("STOPPING %s\n\n\n", e)
175
+ if stop_on_error:
176
+ break
177
+
178
+ if 'Main' in self.method_builders:
179
+ logging.debug("Setting assembly entry point")
180
+ assembly_builder.SetEntryPoint(self.method_builders['Main'])
181
+
182
+ result = type_builder.CreateType()
183
+ name += ".exe"
184
+ logging.debug("Creating %s", name)
185
+ assembly_builder.Save(name)
186
+
187
+ directory = os.path.dirname(os.path.abspath(name))
188
+ installRuntimeLibrary(directory)
189
+
190
+ return name
191
+
192
+ def makeAssemblyDebuggable(self, assembly_builder):
193
+ '''
194
+ Make the assembly debuggable by adding specific attributes.
195
+ '''
196
+ # http://blogs.msdn.com/rmbyers/archive/2005/06/26/432922.aspx
197
+ da_type = typeof(DebuggableAttribute)
198
+ da_ctor = da_type.GetConstructor(System.Array[System.Type]([typeof(DebuggableAttribute.DebuggingModes)]))
199
+ da_builder = CustomAttributeBuilder(da_ctor, System.Array[System.Object]([DebuggableAttribute.DebuggingModes.DisableOptimizations |
200
+ DebuggableAttribute.DebuggingModes.Default]))
201
+ assembly_builder.SetCustomAttribute(da_builder)
202
+
203
+ def createAndAttachFieldEmitters(self, field_info, symbol):
204
+ '''
205
+ Generate a two methods for generating CIL to load and store the value of the global variable (field)
206
+ :param generator: A CIL generator
207
+ :param field_info: The FieldInfo metadata
208
+ :param symbol: The symbol to which fieldStoreEmitter and fieldLoadEmitter methods will be attached
209
+ '''
210
+ assert field_info is not None
211
+
212
+ def fieldLoadEmitter(generator, field_info=field_info):
213
+ '''
214
+ A closure which emits CIL into the supplied generator to load a field
215
+ :param generator: A CIL generator
216
+ :param field_info: The FieldInfo metadata
217
+ '''
218
+ print("field_info = ", field_info)
219
+ generator.Emit(OpCodes.Ldsfld, field_info)
220
+
221
+ def fieldStoreEmitter(generator, field_info=field_info):
222
+ '''
223
+ A closure which emits CIL into the supplied generator to store a field
224
+ :param generator: A CIL generator
225
+ :param field_info: The FieldInfo metadata
226
+ '''
227
+ generator.Emit(OpCodes.Stsfld, field_info)
228
+
229
+ symbol.loadEmitter = fieldLoadEmitter
230
+ symbol.storeEmitter = fieldStoreEmitter
231
+
232
+ def generateStaticDataInitialization(self, data_visitor, type_builder):
233
+ """
234
+ Generate a type constructor to setup static data for DATA statements
235
+ """
236
+ # static (type) constructor - initialise static DATA
237
+
238
+
239
+ # TODO: Replace this with Dictionary<int, int>
240
+
241
+ self.data_field = type_builder.DefineField('data', cts.string_array_type,
242
+ FieldAttributes.Private | FieldAttributes.Static)
243
+ self.data_line_number_map_field = type_builder.DefineField('dataLineNumbers', cts.int_int_dictionary_type,
244
+ FieldAttributes.Private | FieldAttributes.Static)
245
+ self.data_index_field = type_builder.DefineField('dataIndex', typeof(System.Int32),
246
+ FieldAttributes.Private | FieldAttributes.Static)
247
+
248
+ type_constructor_builder = type_builder.DefineTypeInitializer()
249
+ generator = type_constructor_builder.GetILGenerator()
250
+ data_local = generator.DeclareLocal(cts.string_array_type)
251
+ data_index_local = generator.DeclareLocal(cts.int_int_dictionary_type)
252
+
253
+ # Initialise the data field
254
+ emitLdc_I4(generator, len(data_visitor.data)) # Load the array length onto the stack
255
+ generator.Emit(OpCodes.Newarr, System.String) # New array with type information
256
+ generator.Emit(OpCodes.Stloc, data_local) # Store array reference in local 0
257
+ for index, item in enumerate(data_visitor.data):
258
+ generator.Emit(OpCodes.Ldloc, data_local) # Load the array onto the stack
259
+ emitLdc_I4(generator, index) # Load the index onto the stack
260
+ generator.Emit(OpCodes.Ldstr, item) # Load the string onto the stack
261
+ generator.Emit(OpCodes.Stelem_Ref) # Assign to array element
262
+ generator.Emit(OpCodes.Ldloc, data_local) # Load the array onto the stack
263
+ generator.Emit(OpCodes.Stsfld, self.data_field) # Store it in the static field
264
+
265
+ # Initialise the data index field -
266
+ # this needs to be initialized with a Dictionary
267
+ #generic_dictionary_type = typeof(System.Collections.Generic.Dictionary)
268
+ #int_int_dictionary_type = generic_dictionary_type.MakeGenericType(
269
+ # System.Array[System.Type]((typeof(System.Int32), typeof(System.Int32))))
270
+
271
+ int_int_dictionary_ctor_info = cts.int_int_dictionary_type.GetConstructor(System.Type.EmptyTypes) # Get the default constructor
272
+ generator.Emit(OpCodes.Newobj, int_int_dictionary_ctor_info)
273
+ generator.Emit(OpCodes.Stloc, data_index_local) # Store dictionary reference in local 1
274
+
275
+ add_method_info = cts.int_int_dictionary_type.GetMethod('Add')
276
+
277
+ for line_number, index in data_visitor.index.items():
278
+ generator.Emit(OpCodes.Ldloc, data_index_local) # Load the dictionary onto the stack
279
+ emitLdc_I4(generator, line_number) # Load the line_number onto the stack
280
+ emitLdc_I4(generator, index) # Load the index onto the stack
281
+ generator.Emit(OpCodes.Call, add_method_info) # Call Dictionary<int,int>.Add()
282
+
283
+ generator.Emit(OpCodes.Ldloc, data_index_local) # Load the dictionary onto the stack
284
+ generator.Emit(OpCodes.Stsfld, self.data_line_number_map_field) # Store it in the static field
285
+ generator.Emit(OpCodes.Ret)
286
+
287
+ def createCtsMethodName(self, owl_name):
288
+ '''
289
+ Given an entry point node devise a suitable name. Some names are reserved
290
+ and names will be converted to valid CLR method identifiers.
291
+
292
+ 'Main' is reserved so will not be permitted.
293
+ consecutive underscores will be removed
294
+ leading @ will be removed
295
+ The leading PROC or FN will be removed if names clash
296
+ '''
297
+ m = re.match(r'(PROC|FN)([a-zA-Z_0-9`@]+)', owl_name)
298
+ assert m is not None
299
+ owl_prefix = m.group(1)
300
+ identifier = m.group(2)
301
+ # TODO Remove initial digits
302
+ # TODO: Deal with Main - and prior registration of that name, correctly
303
+ identifier = re.sub(r'[@`]', '_', identifier) # Replace @ and ` with underscores
304
+ identifier = re.sub(r'_{2,}', '_', identifier) # Replace consecutive underscores with one
305
+ # Remove the leading underscore
306
+ if identifier.startswith('_'):
307
+ if len(identifier == 1):
308
+ identifier = 'Unnamed'
309
+ else:
310
+ identifier = identifier [1:]
311
+ # Attempt to insert the identifier
312
+ counter = None
313
+ while True:
314
+ if identifier not in self.clr_to_owl_method_names:
315
+ self.owl_to_clr_method_names[owl_name] = identifier
316
+ self.clr_to_owl_method_names[identifier] = owl_name
317
+ break
318
+ else:
319
+ # Generated name already used
320
+ clashing_owl_name = self.clr_to_owl_method_names[identifier]
321
+ logging.debug("clashing_owl_name = %s", clashing_owl_name)
322
+ # Attempt to resolve by prefixing with Proc or Fn
323
+ # TODO: In future could consider simply overloading so
324
+ # long as the signatures are different
325
+ logging.debug("owl_prefix = %s", owl_prefix)
326
+ if owl_prefix == 'FN':
327
+ if clashing_owl_name.startswith('PROC'):
328
+ # Prefix the clashing identifier with 'Proc' and
329
+ # prefix this new identifier with 'Fn'
330
+
331
+ # Modify the clasing entry by prefixing with 'Proc'
332
+ self.owl_to_clr_method_names[clashing_owl_name] = 'Proc' + identifier
333
+ self.clr_to_owl_method_names['Proc' + identifier] = clashing_owl_name
334
+ # Remove the old entry
335
+ del self.clr_to_owl_method_names[identifier]
336
+ # Add the new entry
337
+ identifier = 'Fn' + identifier
338
+ self.clr_to_owl_method_names[identifier] = owl_name
339
+ self.owl_to_clr_method_names[owl_name] = identifier
340
+ break
341
+ elif clashing_owl_name.startswith('FN'):
342
+ # Modify our name in some way to distinguish it
343
+ identifier = self.modifyName(identifier)
344
+ # TODO
345
+ elif owl_prefix == 'PROC':
346
+ if clashing_owl_name.startswith('FN'):
347
+ # Prefix the clashing identifier with 'Fn' and
348
+ # prefix this new identifier with 'Proc'
349
+ self.owl_to_clr_method_names[clashing_owl_name] = 'Fn' + identifier
350
+ self.clr_to_owl_method_names['Fn' + identifier] = clashing_owl_name
351
+ # Remove the old entry
352
+ del self.clr_to_owl_method_names[identifier]
353
+ # Add the new entry
354
+ identifier = 'Proc' + identifier
355
+ self.clr_to_owl_method_names[identifier] = owl_name
356
+ self.owl_to_clr_method_names[owl_name] = identifier
357
+ break
358
+ elif clashing_owl_name.startswith('PROC'):
359
+ # Modify our name in some way to distinguish it
360
+ identifier = self.modifyName(identifier)
361
+ #TODO
362
+ return identifier
363
+
364
+ def modifyName(self, identifier):
365
+ '''
366
+ Modify the given identifier into a new identifier which should be
367
+ unique in the set of known identifiers
368
+ '''
369
+ raise NotImplementedException;
370
+
371
+ def generateMethod(self, type_builder, basic_blocks):
372
+ """
373
+ Generate the code for a single method starting a the entry_point node in the CFG. Attaches
374
+ code generation functionality to the symbols representing the formal parameters of the method.
375
+ """
376
+ logging.debug("generateMethod")
377
+
378
+ # The first statement of the first basic block is the entry point
379
+ entry_point_node = basic_blocks[0].entryPoint
380
+
381
+ # Set up the method attributes
382
+ method_attributes = MethodAttributes.Static
383
+ method_return_type = None
384
+
385
+ if isinstance(entry_point_node, DefinitionStatement):
386
+ method_name, method_return_type, method_attributes, method_parameters = self.generateDefinedMethod(entry_point_node, method_attributes)
387
+ else:
388
+ method_name, method_return_type, method_attributes, method_parameters = self.generateMainProgram(entry_point_node, method_attributes)
389
+
390
+ logging.debug("generating method %s with type %s", method_name, method_return_type)
391
+ self.method_builders[method_name] = type_builder.DefineMethod(method_name, method_attributes, CallingConventions.Standard,
392
+ method_return_type, method_parameters )
393
+
394
+ def generateDefinedMethod(self, entry_point_node, method_attributes):
395
+ method_attributes |= MethodAttributes.Public
396
+ method_parameters = self.methodParameters(entry_point_node)
397
+ method_name = self.lookupCtsMethodName(entry_point_node.name)
398
+ logging.debug("name = %s", entry_point_node.name)
399
+ # Setup code generators for loading and storing the methods arguments
400
+ if entry_point_node.formalParameters is not None:
401
+ formal_parameters = entry_point_node.formalParameters.arguments
402
+ for index, param in enumerate(formal_parameters):
403
+ # TODO: Lookup the argument symbol
404
+ name = param.argument.identifier
405
+ logging.debug("formal parameter identifier = %s", name)
406
+ symbol_node = findNode(entry_point_node, hasSymbolTableLookup)
407
+ arg_symbol = symbol_node.symbolTable.lookup(name)
408
+ assert arg_symbol is not None
409
+ arg_symbol.loadEmitter = partial(emitLdarg, index=index)
410
+ arg_symbol.storeEmitter = partial(emitStarg, index=index)
411
+ # Setup the return type of the method
412
+
413
+ if isinstance(entry_point_node, DefineProcedure):
414
+ method_return_type = System.Void
415
+ elif isinstance(entry_point_node, DefineFunction):
416
+ method_return_type = cts.mapType(entry_point_node.returnType)
417
+
418
+ return method_name, method_return_type, method_attributes, method_parameters
419
+
420
+ def generateMainProgram(self, entry_point_node, method_attributes):
421
+ assert iter(entry_point_node.entryPoints).next().startswith('MAIN')
422
+ method_name = self.lookupCtsMethodName('FNMain')
423
+ entry_point_node.name = method_name
424
+ method_attributes |= MethodAttributes.Public
425
+ # TODO: Parameters and returns from Main
426
+ method_return_type = None
427
+ method_parameters = None
428
+ return method_name, method_return_type, method_attributes, method_parameters
429
+
430
+ def methodParameters(self, statement):
431
+ '''
432
+ Convert the formalParameters property of the supplied
433
+ DefinitionStatement into an Array[Type]
434
+
435
+ :param statement: A DefintionStatement
436
+ :returns: An Array[Type] containing CTS types
437
+ '''
438
+ # TODO: Reference and out parameters not dealt with here!
439
+ assert isinstance(statement, DefinitionStatement)
440
+ print(statement.formalParameters)
441
+ types = ()
442
+ if statement.formalParameters is not None:
443
+ formal_parameters = statement.formalParameters.arguments
444
+ types = [cts.mapType(param.argument.actualType) for param in formal_parameters]
445
+ return System.Array[System.Type](types)
446
+
447
+ def generateMethodBody(self, type_builder, basic_blocks):
448
+ # The first statement of the first basic block is the entry point
449
+ entry_point_node = basic_blocks[0].entryPoint
450
+ try:
451
+ name = entry_point_node.name
452
+ if name == 'Main':
453
+ name = 'FNMain'
454
+ clr_method_name = self.owl_to_clr_method_names[name]
455
+ except KeyError:
456
+ print(entry_point_node.name)
457
+ print(self.owl_to_clr_method_names)
458
+ assert 0
459
+ logging.debug("Creating CIL for %s", clr_method_name)
460
+ method_builder = self.method_builders[clr_method_name]
461
+ logging.debug("entry_point_node = %s", entry_point_node)
462
+
463
+ # Create the visitor which holds the code generator
464
+ cv = CilVisitor(self, type_builder, method_builder, self.line_mapper, self.doc)
465
+
466
+ # Declare LOCAL variables and attach load and store emitters to the symbols
467
+ for node in depthFirstSearch(entry_point_node):
468
+ if isinstance(node, Local):
469
+ symbol_table = node.symbolTable
470
+ for symbol in symbol_table.symbols.values():
471
+ local_builder = cv.generator.DeclareLocal(cts.symbolType(symbol))
472
+ local_builder.SetLocalSymInfo(symbol.name) # Provide symbol name for debugger
473
+ self.createAndAttachLocalEmitters(local_builder, symbol)
474
+
475
+ # TODO: Declare PRIVATE variables and attach load and store emitters to the symbols
476
+
477
+ # For each basic block (including the first, if it has an in-degree > 1)
478
+ # define a label, and attach it to the block
479
+ for basic_block in basic_blocks:
480
+ basic_block.label = cv.generator.DefineLabel()
481
+ basic_block.is_label_marked = False
482
+
483
+ # Generate the code for blocks and statements in sequence
484
+ for basic_block in basic_blocks:
485
+ for statement in basic_block.statements:
486
+ if statement.startLine and statement.startColumn and statement.endLine and statement.endColumn:
487
+ cv.generator.MarkSequencePoint(self.doc, statement.startLine, statement.startColumn,
488
+ statement.endLine, statement.endColumn)
489
+ cv.checkMark(statement) # TODO: Could push this out a level to be per block
490
+ cv.visit(statement)
491
+ assert statement.block.is_label_marked
492
+ self.transferControlToNextBlock(cv.generator, basic_block)
493
+ logging.debug("COMPLETE\n\n\n")
494
+
495
+ def createAndAttachLocalEmitters(self, local_builder, symbol):
496
+ '''
497
+ Generate a two methods for generating CIL to load and store the value of the global variable (field)
498
+ :param generator: A CIL generator
499
+ :param local_builder: The LocalBuilder metadata
500
+ :param symbol: The symbol to which localStoreEmitter and localLoadEmitter methods will be attached
501
+ '''
502
+ def localLoadEmitter(generator, local_builder=local_builder):
503
+ '''
504
+ A closure which emits CIL into the supplied generator to load a field
505
+ :param generator: A CIL generator
506
+ :param local_builder: The LocalBuilder metadata
507
+ '''
508
+ generator.Emit(OpCodes.Ldloc, local_builder)
509
+
510
+ def localStoreEmitter(generator, local_builder=local_builder):
511
+ '''
512
+ A closure which emits CIL into the supplied generator to store a field
513
+ :param generator: A CIL generator
514
+ :param local_builder: The LocalBuilder metadata
515
+ '''
516
+ generator.Emit(OpCodes.Stloc, local_builder)
517
+
518
+ symbol.loadEmitter = localLoadEmitter
519
+ symbol.storeEmitter = localStoreEmitter
520
+
521
+ def transferControlToNextBlock(self, generator, current_block):
522
+ '''
523
+ Transfer control to the next block for blocks with
524
+ an out-degree of one. (More complex block exits should
525
+ already have been dealt with by code generation of the
526
+ last statement of the block). If control can fall through
527
+ to the next block because they are consecutive in the topological
528
+ order no code is generated, otherwise an unconditional branch is inserted.
529
+ :param generator: A code generator.
530
+ :param current_block: The block from which to transfer control.
531
+ '''
532
+
533
+ if current_block.outDegree == 1:
534
+ successor_block = representative(current_block.outEdges)
535
+ # If we can't fall through to the next block, branch unconditionally to it
536
+ if successor_block.topological_order != current_block.topological_order + 1:
537
+ generator.Emit(OpCodes.Br, successor_block.label)
538
+
539
+