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,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
|
+
|