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,471 @@
|
|
|
1
|
+
# A visitor for performing type-checking over the Abstract Syntax Tree
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from owl_basic.visitor import Visitor
|
|
6
|
+
from owl_basic.errors import *
|
|
7
|
+
from owl_basic.utility import underscoresToCamelCase
|
|
8
|
+
from owl_basic.syntax.ast import Cast, Concatenate
|
|
9
|
+
from owl_basic.ast_utils import elideNode
|
|
10
|
+
from owl_basic.owltyping.type_system import (NumericOwlType, ObjectOwlType, IntegerOwlType,
|
|
11
|
+
FloatOwlType, ByteOwlType, PendingOwlType,
|
|
12
|
+
StringOwlType, ArrayOwlType)
|
|
13
|
+
from owl_basic import sigil
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger('owltyping.typecheck_visitor')
|
|
16
|
+
|
|
17
|
+
class TypecheckVisitor(Visitor):
|
|
18
|
+
"""
|
|
19
|
+
AST visitor for determining the actual type of each node
|
|
20
|
+
"""
|
|
21
|
+
def __init__(self, entry_points):
|
|
22
|
+
'''
|
|
23
|
+
:param entry_points: A dictionary of entry_point names to AstStatements.
|
|
24
|
+
'''
|
|
25
|
+
self.__entry_points = entry_points
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
def visit(self, node):
|
|
29
|
+
"Override visit to allow safe traversal of lists"
|
|
30
|
+
if isinstance(node, list):
|
|
31
|
+
for elem in node:
|
|
32
|
+
self.visit(elem)
|
|
33
|
+
else:
|
|
34
|
+
super(TypecheckVisitor, self).visit(node)
|
|
35
|
+
|
|
36
|
+
def visitAstNode(self, node):
|
|
37
|
+
node.forEachChild(self.visit)
|
|
38
|
+
self.checkSignature(node) # TODO: What about return types
|
|
39
|
+
self.insertNumericCasts(node)
|
|
40
|
+
|
|
41
|
+
def visitAstStatement(self, statement):
|
|
42
|
+
"Generic visitor for simple statements"
|
|
43
|
+
# TODO: If this is the same as above, it can be removed
|
|
44
|
+
statement.forEachChild(self.visit)
|
|
45
|
+
if not self.checkSignature(statement):
|
|
46
|
+
return
|
|
47
|
+
self.insertNumericCasts(statement)
|
|
48
|
+
|
|
49
|
+
def visitAssignment(self, assignment):
|
|
50
|
+
# Determine the actual type of the lValue and rValue
|
|
51
|
+
|
|
52
|
+
self.visit(assignment.lValue)
|
|
53
|
+
self.visit(assignment.rValue)
|
|
54
|
+
if isinstance(assignment.rValue, list):
|
|
55
|
+
# Deal with L-values which are lists
|
|
56
|
+
if assignment.lValue.actualType.isA(ArrayOwlType):
|
|
57
|
+
for item in assignment.rValue:
|
|
58
|
+
self.checkAndInsertRValueCast(item, assignment.lValue.actualType._getElementType())
|
|
59
|
+
else:
|
|
60
|
+
message = "List is only assignable to an array"
|
|
61
|
+
self.typeMismatch(assignment, message)
|
|
62
|
+
else:
|
|
63
|
+
logger.debug(assignment.lValue)
|
|
64
|
+
self.checkAndInsertRValueCast(assignment.rValue, assignment.lValue.actualType)
|
|
65
|
+
|
|
66
|
+
def visitForToStep(self, for_stmt):
|
|
67
|
+
'''
|
|
68
|
+
Visit FOR N=1 TO 10 STEP 2
|
|
69
|
+
'''
|
|
70
|
+
self.visit(for_stmt.identifier)
|
|
71
|
+
self.visit(for_stmt.first)
|
|
72
|
+
self.visit(for_stmt.last)
|
|
73
|
+
self.visit(for_stmt.step)
|
|
74
|
+
counter_type = for_stmt.identifier.actualType
|
|
75
|
+
self.checkAndInsertRValueCast(for_stmt.first, counter_type)
|
|
76
|
+
self.checkAndInsertRValueCast(for_stmt.last, counter_type)
|
|
77
|
+
self.checkAndInsertRValueCast(for_stmt.step, counter_type)
|
|
78
|
+
|
|
79
|
+
def visitBinaryNumericOperator(self, operator):
|
|
80
|
+
'''
|
|
81
|
+
Visit - * / ^
|
|
82
|
+
'''
|
|
83
|
+
self.visit(operator.lhs)
|
|
84
|
+
self.visit(operator.rhs)
|
|
85
|
+
# TODO: Propagate pending types
|
|
86
|
+
self.determineNumericResultType(operator)
|
|
87
|
+
self.promoteNumericOperands(operator)
|
|
88
|
+
|
|
89
|
+
def visitPlus(self, plus):
|
|
90
|
+
'''
|
|
91
|
+
Specialization of visitBinaryNumericOperator to handle string concatenation
|
|
92
|
+
'''
|
|
93
|
+
# Determine the actual type of sub-expressions
|
|
94
|
+
self.visit(plus.lhs)
|
|
95
|
+
self.visit(plus.rhs)
|
|
96
|
+
|
|
97
|
+
# If this is a string concatenation, convert the node and re-visit
|
|
98
|
+
if plus.lhs.actualType == StringOwlType() and plus.rhs.actualType == StringOwlType():
|
|
99
|
+
# TODO: Create a function in ast_utils to replace a node
|
|
100
|
+
concat = Concatenate(lhs = plus.lhs, rhs = plus.rhs)
|
|
101
|
+
concat.lhs.parent = concat
|
|
102
|
+
concat.rhs.parent = concat
|
|
103
|
+
concat.parent = plus.parent
|
|
104
|
+
plus.parent.setProperty(concat, plus.parent_property, plus.parent_index)
|
|
105
|
+
self.visit(concat)
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
self.determineNumericResultType(plus)
|
|
109
|
+
self.promoteNumericOperands(plus)
|
|
110
|
+
|
|
111
|
+
def visitRelationalOperator(self, operator):
|
|
112
|
+
'''
|
|
113
|
+
Visit = <> < > <= >=
|
|
114
|
+
'''
|
|
115
|
+
self.visit(operator.lhs)
|
|
116
|
+
self.visit(operator.rhs)
|
|
117
|
+
|
|
118
|
+
if not (operator.lhs.actualType.isConvertibleTo(operator.rhs.actualType) or operator.rhs.actualType.isConvertibleTo(operator.lhs.actualType)):
|
|
119
|
+
message = "Cannot compare %s with %s using operator %s" % (operator.lhs.actualType.__doc__, operator.rhs.actualType.__doc__, operator.__doc__)
|
|
120
|
+
self.typeMismatch(operator, message)
|
|
121
|
+
|
|
122
|
+
self.promoteNumericOperands(operator)
|
|
123
|
+
operator.actualType = IntegerOwlType()
|
|
124
|
+
|
|
125
|
+
def visitArray(self, array):
|
|
126
|
+
# Decode the variable name sigil into the actual type
|
|
127
|
+
# The sigils are one of [$%&~]
|
|
128
|
+
array.actualType = sigil.identifierToType(array.identifier)
|
|
129
|
+
|
|
130
|
+
def visitVariable(self, variable):
|
|
131
|
+
# Decode the variable name sigil into the actual type
|
|
132
|
+
# The sigils are one of [$%&~]
|
|
133
|
+
variable.actualType = sigil.identifierToType(variable.identifier)
|
|
134
|
+
|
|
135
|
+
def visitIndexer(self, indexer):
|
|
136
|
+
# Decode the variable name sigil into the actual type
|
|
137
|
+
# The sigils are one of [$%&~]
|
|
138
|
+
indexer.actualType = sigil.identifierToType(indexer.identifier[:-1])
|
|
139
|
+
for index in indexer.indices:
|
|
140
|
+
self.visit(index)
|
|
141
|
+
self.checkAndInsertRValueCast(index, IntegerOwlType())
|
|
142
|
+
|
|
143
|
+
def visitIf(self, iff):
|
|
144
|
+
# TODO: Does this do anything that visitAstNode doesn't do?
|
|
145
|
+
self.visit(iff.condition)
|
|
146
|
+
self.visit(iff.trueClause)
|
|
147
|
+
self.visit(iff.falseClause)
|
|
148
|
+
condition_formal_type = iff.child_infos['condition'].formalType
|
|
149
|
+
if iff.condition.actualType.isConvertibleTo(condition_formal_type):
|
|
150
|
+
self.insertCast(iff.condition, iff.condition.actualType, condition_formal_type)
|
|
151
|
+
else:
|
|
152
|
+
self.typeMismatch(iff, "Conditional expression must be convertible to %s." % condition_formal_type.__doc__)
|
|
153
|
+
|
|
154
|
+
def visitOnGoto(self, ongoto):
|
|
155
|
+
# TODO: Does this do anything that visitAstNode doesn't do?
|
|
156
|
+
self.visit(ongoto.switch)
|
|
157
|
+
switch_formal_type = ongoto.child_infos['switch'].formalType
|
|
158
|
+
if ongoto.switch.actualType.isConvertibleTo(switch_formal_type):
|
|
159
|
+
self.insertCast(ongoto.switch, ongoto.switch.actualType, switch_formal_type)
|
|
160
|
+
else:
|
|
161
|
+
self.typeMismatch(ongoto, "Selector expression must be convertible to %s" % switch_formal_type.__doc__)
|
|
162
|
+
|
|
163
|
+
for target in ongoto.targetLogicalLines:
|
|
164
|
+
self.visit(target)
|
|
165
|
+
if target.actualType.isConvertibleTo(IntegerOwlType()):
|
|
166
|
+
self.insertCast(target, target.actualType, IntegerOwlType())
|
|
167
|
+
else:
|
|
168
|
+
self.typeMismatch(ongoto, "Target expressions must be convertible to Integer")
|
|
169
|
+
|
|
170
|
+
self.visit(ongoto.outOfRangeClause)
|
|
171
|
+
|
|
172
|
+
def visitUnaryNumericOperator(self, operator):
|
|
173
|
+
self.visit(operator.factor)
|
|
174
|
+
if not self.checkSignature(operator):
|
|
175
|
+
return
|
|
176
|
+
operator.actualType = operator.factor.actualType
|
|
177
|
+
|
|
178
|
+
def visitBinaryIntegerOperator(self, operator):
|
|
179
|
+
self.visit(operator.lhs)
|
|
180
|
+
self.visit(operator.rhs)
|
|
181
|
+
if not self.checkSignature(operator):
|
|
182
|
+
return
|
|
183
|
+
# TODO: Pull this out into a function
|
|
184
|
+
if operator.lhs.actualType != IntegerOwlType():
|
|
185
|
+
self.insertCast(operator.lhs, source=operator.lhs.actualType, target=IntegerOwlType())
|
|
186
|
+
if operator.rhs.actualType != IntegerOwlType():
|
|
187
|
+
self.insertCast(operator.rhs, source=operator.rhs.actualType, target=IntegerOwlType())
|
|
188
|
+
|
|
189
|
+
def visitDyadicIndirection(self, dyadic):
|
|
190
|
+
self.visit(dyadic.base)
|
|
191
|
+
self.visit(dyadic.offset)
|
|
192
|
+
if not self.checkSignature(dyadic):
|
|
193
|
+
# TODO: Error?
|
|
194
|
+
return
|
|
195
|
+
self.insertNumericCasts(dyadic)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def visitUnaryNumericFunc(self, func):
|
|
199
|
+
self.visit(func.factor)
|
|
200
|
+
if not self.checkSignature(func):
|
|
201
|
+
# TODO: Error?
|
|
202
|
+
return
|
|
203
|
+
if func.factor.actualType == IntegerOwlType():
|
|
204
|
+
self.insertCast(func.factor, source=func.factor.actualType, target=FloatOwlType())
|
|
205
|
+
|
|
206
|
+
def visitAbsFunc(self, abs):
|
|
207
|
+
'''
|
|
208
|
+
Check that the argument is numeric. If so, propagate the type of the argument to
|
|
209
|
+
the type of the ABS function.
|
|
210
|
+
'''
|
|
211
|
+
self.visit(abs.factor)
|
|
212
|
+
if not self.checkSignature(abs):
|
|
213
|
+
# TODO: Error?
|
|
214
|
+
return
|
|
215
|
+
abs.actualType = abs.factor.actualType
|
|
216
|
+
|
|
217
|
+
def visitIntFunc(self, func):
|
|
218
|
+
self.visit(func.factor)
|
|
219
|
+
if not self.checkSignature(func):
|
|
220
|
+
# TODO: Error?
|
|
221
|
+
return
|
|
222
|
+
if func.factor.actualType == IntegerOwlType():
|
|
223
|
+
elideNode(func)
|
|
224
|
+
|
|
225
|
+
def visitNot(self, operator):
|
|
226
|
+
self.visit(operator.factor)
|
|
227
|
+
if not self.checkSignature(operator):
|
|
228
|
+
# TODO: Error?
|
|
229
|
+
return
|
|
230
|
+
if operator.factor.actualType != IntegerOwlType():
|
|
231
|
+
self.insertCast(operator.factor, source = operator.factor.actualType, target=IntegerOwlType())
|
|
232
|
+
|
|
233
|
+
def visitInstr(self, instr):
|
|
234
|
+
self.visit(instr.source)
|
|
235
|
+
self.visit(instr.subString)
|
|
236
|
+
self.visit(instr.startPosition)
|
|
237
|
+
if not self.checkSignature(instr):
|
|
238
|
+
# TODO: Error?
|
|
239
|
+
return
|
|
240
|
+
if instr.startPosition is not None and instr.startPosition.actualType != IntegerOwlType():
|
|
241
|
+
self.insertCast(instr.startPosition, source = instr.startPosition.actualType, target = IntegerOwlType())
|
|
242
|
+
|
|
243
|
+
def visitReadFunc(self, read_func):
|
|
244
|
+
# Infer the type of ReadFunc in x = ReadFunc from the type of x
|
|
245
|
+
# This depends on the type of the lValue of the assignment having been
|
|
246
|
+
# determined previously, and assumes that the parent of ReadFunc is always
|
|
247
|
+
# an Assignment
|
|
248
|
+
read_func.actualType = read_func.parent.lValue.actualType
|
|
249
|
+
|
|
250
|
+
def visitUserFunc(self, user_func):
|
|
251
|
+
if user_func.actualParameters:
|
|
252
|
+
self.visit(user_func.actualParameters)
|
|
253
|
+
# TODO: Check argument types against Procedure
|
|
254
|
+
# TODO: This needs different code for internal and external linkage
|
|
255
|
+
self.checkActualParameters(user_func)
|
|
256
|
+
user_func.actualType = PendingOwlType()
|
|
257
|
+
|
|
258
|
+
def visitCallProcedure(self, proc):
|
|
259
|
+
if proc.actualParameters:
|
|
260
|
+
self.visit(proc.actualParameters)
|
|
261
|
+
# TODO: Check argument types against Procedure
|
|
262
|
+
# TODO: This needs different code for internal and external linkage
|
|
263
|
+
self.checkActualParameters(proc)
|
|
264
|
+
|
|
265
|
+
def checkActualParameters(self, call):
|
|
266
|
+
'''
|
|
267
|
+
Check the actualParameters of 'call' against the formal parameters
|
|
268
|
+
of the callable.
|
|
269
|
+
:param call: An AstNode with an actualParameters property and a name property
|
|
270
|
+
:returns: True is the actual parameter types are compatible with the formal parameter types, otherwise False
|
|
271
|
+
'''
|
|
272
|
+
# Lookup the callable and retrieve its formal parameters
|
|
273
|
+
if call.name in self.__entry_points:
|
|
274
|
+
callable = self.__entry_points[call.name]
|
|
275
|
+
n = 1
|
|
276
|
+
for actual, formal in zip(call.actualParameters,
|
|
277
|
+
callable.formalParameters.arguments):
|
|
278
|
+
if formal.argument.actualType is None:
|
|
279
|
+
# There is no type information on the callable yet, so visit it
|
|
280
|
+
self.visit(callable)
|
|
281
|
+
if actual.actualType.isConvertibleTo(formal.argument.actualType):
|
|
282
|
+
self.insertCast(actual, source=actual.actualType, target=formal.argument.actualType)
|
|
283
|
+
else:
|
|
284
|
+
message = "Cannot pass actual parameter number %d of type %s to formal parameter type of %s" % (n, actual.actualType.__doc__, formal.argument.actualType.__doc__)
|
|
285
|
+
self.typeMismatch(call, message)
|
|
286
|
+
n += 1
|
|
287
|
+
else:
|
|
288
|
+
error("Did not find entry point for %s" % call.name)
|
|
289
|
+
|
|
290
|
+
# Check each formal parameter against an actual parameter
|
|
291
|
+
pass
|
|
292
|
+
|
|
293
|
+
def determineNumericResultType(self, operator):
|
|
294
|
+
if operator.lhs.actualType == PendingOwlType() or operator.rhs.actualType == PendingOwlType():
|
|
295
|
+
operator.actualType = PendingOwlType()
|
|
296
|
+
return
|
|
297
|
+
|
|
298
|
+
if not self.checkSignature(operator):
|
|
299
|
+
# TODO: Error?
|
|
300
|
+
return
|
|
301
|
+
|
|
302
|
+
def opTypes(lhs_type, rhs_type):
|
|
303
|
+
return operator.lhs.actualType.isA(lhs_type) and operator.rhs.actualType.isA(rhs_type)
|
|
304
|
+
|
|
305
|
+
if opTypes(ObjectOwlType(), NumericOwlType()) : operator.actualType = FloatOwlType()
|
|
306
|
+
elif opTypes(NumericOwlType(), ObjectOwlType()) : operator.actualType = FloatOwlType()
|
|
307
|
+
elif opTypes(IntegerOwlType(), FloatOwlType()) : operator.actualType = FloatOwlType()
|
|
308
|
+
elif opTypes(FloatOwlType(), IntegerOwlType()) : operator.actualType = FloatOwlType()
|
|
309
|
+
elif operator.lhs.actualType == operator.rhs.actualType:
|
|
310
|
+
operator.actualType = operator.lhs.actualType
|
|
311
|
+
else:
|
|
312
|
+
message = "Cannot apply operator %s to operands of type of %s and %s" % (operator.__doc__, operator.lhs.actualType.__doc__, operator.rhs.actualType.__doc__)
|
|
313
|
+
self.typeMismatch(operator, message)
|
|
314
|
+
|
|
315
|
+
def promoteNumericOperands(self, operator):
|
|
316
|
+
'''
|
|
317
|
+
Given a binary operator with lhs and rhs operands, if the operands are of
|
|
318
|
+
NumericType, insert casts as necessary to promote operands as necessary to
|
|
319
|
+
FloatType from IntegerType in the case of mixed operand types.
|
|
320
|
+
e.g. Int op Int => Int op Int
|
|
321
|
+
Float op Float => Float op Float
|
|
322
|
+
Float op Int => Float op Float
|
|
323
|
+
Int op FLoat => Float op Float
|
|
324
|
+
'''
|
|
325
|
+
# TODO: Handle byte types - use the precision of the types to decide how to promote...
|
|
326
|
+
def opTypes(lhs_type, rhs_type):
|
|
327
|
+
return operator.lhs.actualType.isA(lhs_type) and operator.rhs.actualType.isA(rhs_type)
|
|
328
|
+
|
|
329
|
+
if opTypes(IntegerOwlType(), FloatOwlType()):
|
|
330
|
+
self.insertCast(operator.lhs, source=IntegerOwlType(), target=FloatOwlType())
|
|
331
|
+
elif opTypes(ByteOwlType(), FloatOwlType()):
|
|
332
|
+
self.insertCast(operator.lhs, source=ByteOwlType(), target=FloatOwlType())
|
|
333
|
+
elif opTypes(FloatOwlType(), IntegerOwlType()):
|
|
334
|
+
self.insertCast(operator.rhs, source=IntegerOwlType(), target=FloatOwlType())
|
|
335
|
+
elif opTypes(FloatOwlType(), ByteOwlType()):
|
|
336
|
+
self.insertCast(operator.rhs, source=ByteOwlType(), target=FloatOwlType())
|
|
337
|
+
|
|
338
|
+
def insertNumericCasts(self, node):
|
|
339
|
+
"""
|
|
340
|
+
Where an Integer value is being passed to a parameter of Numeric type,
|
|
341
|
+
insert an Integer->Float cast operation.
|
|
342
|
+
"""
|
|
343
|
+
for name, child in node.children.items():
|
|
344
|
+
if child is not None:
|
|
345
|
+
if isinstance(child, list):
|
|
346
|
+
formal_type = node.child_infos[name][0].formalType
|
|
347
|
+
if formal_type is not None:
|
|
348
|
+
if formal_type.isA(NumericOwlType()):
|
|
349
|
+
for subchild in child:
|
|
350
|
+
self.insertCast(subchild, source=subchild.actualType, target=formal_type)
|
|
351
|
+
else:
|
|
352
|
+
sys.stderr.write("Compiler construction: Missing formal type information on %s, %s\n" % (node, name))
|
|
353
|
+
else:
|
|
354
|
+
formal_type = node.child_infos[name].formalType
|
|
355
|
+
if formal_type is not None:
|
|
356
|
+
if formal_type.isA(NumericOwlType()):
|
|
357
|
+
self.insertCast(child, source=child.actualType, target=formal_type)
|
|
358
|
+
else:
|
|
359
|
+
sys.stderr.write("Compiler construction: Missing formal type information on %s, %s\n" % (node, name))
|
|
360
|
+
|
|
361
|
+
def insertCast(self, child, source, target):
|
|
362
|
+
"""Wrap the supplied node in a Cast node from source type to target type"""
|
|
363
|
+
|
|
364
|
+
if source is target:
|
|
365
|
+
return
|
|
366
|
+
|
|
367
|
+
if source.isA(PendingOwlType()):
|
|
368
|
+
logging.debug("Ignoring request to insert cast from PendingType")
|
|
369
|
+
return
|
|
370
|
+
|
|
371
|
+
if source.isA(target):
|
|
372
|
+
# Implicit conversion allowed, no cast needed
|
|
373
|
+
logging.debug("%s implicitly converted to %s" % (source, target))
|
|
374
|
+
return
|
|
375
|
+
|
|
376
|
+
if source.isA(NumericOwlType()) and target.isA(NumericOwlType()):
|
|
377
|
+
if target.bitsIntegerPrecision() < source.bitsIntegerPrecision():
|
|
378
|
+
message = "of %s to %s, possible loss of data" % (source.__doc__, target.__doc__)
|
|
379
|
+
self.castWarning(child, message)
|
|
380
|
+
|
|
381
|
+
parent = child.parent
|
|
382
|
+
parent_property = child.parent_property
|
|
383
|
+
parent_index = child.parent_index
|
|
384
|
+
cast = Cast(sourceType=source, targetType=target, value=child)
|
|
385
|
+
cast.lineNum = parent.lineNum
|
|
386
|
+
# TODO: Tidy up this redundancy!
|
|
387
|
+
cast.formalType = cast.targetType
|
|
388
|
+
cast.actualType = cast.formalType
|
|
389
|
+
cast.parent = parent
|
|
390
|
+
cast.parent_property = parent_property
|
|
391
|
+
cast.parent_index = parent_index
|
|
392
|
+
cast.value.parent = cast
|
|
393
|
+
cast.value.parent_property = "value"
|
|
394
|
+
cast.value.parent_index = None
|
|
395
|
+
parent.setProperty(cast, parent_property, parent_index)
|
|
396
|
+
|
|
397
|
+
def checkSignature(self, node):
|
|
398
|
+
"""
|
|
399
|
+
Check the actualType of each child node against the formalType of each
|
|
400
|
+
child node and determine if they are of compatible type. For example,
|
|
401
|
+
IntegerType is compatible with NumericType, and NumericType is compatible
|
|
402
|
+
with ScalarType, but StringType is not compatible with NumericType.
|
|
403
|
+
"""
|
|
404
|
+
result = True
|
|
405
|
+
for name, info in node.child_infos.items():
|
|
406
|
+
if isinstance(info, list):
|
|
407
|
+
info = info[0]
|
|
408
|
+
formal_type = info.formalType
|
|
409
|
+
child_nodes = getattr(node, underscoresToCamelCase(name))
|
|
410
|
+
if child_nodes is not None:
|
|
411
|
+
for child_node in child_nodes:
|
|
412
|
+
child_result = self.checkType(node, child_node, formal_type, info)
|
|
413
|
+
result = result and child_result
|
|
414
|
+
else:
|
|
415
|
+
formal_type = info.formalType
|
|
416
|
+
child_node = getattr(node, underscoresToCamelCase(name))
|
|
417
|
+
child_result = self.checkType(node, child_node, formal_type, info)
|
|
418
|
+
result = result and child_result
|
|
419
|
+
return result
|
|
420
|
+
|
|
421
|
+
def checkType(self, node, child_node, formal_type, info):
|
|
422
|
+
"""
|
|
423
|
+
Checks that child_node of node is of formal_type.
|
|
424
|
+
"""
|
|
425
|
+
if child_node is not None:
|
|
426
|
+
logger.debug("child_node = %s" % child_node)
|
|
427
|
+
actual_type = child_node.actualType
|
|
428
|
+
logger.debug("formal_type = %s" % formal_type)
|
|
429
|
+
logger.debug("actual_type = %s" % actual_type)
|
|
430
|
+
if formal_type is not None: # None types do not need to be checked
|
|
431
|
+
if actual_type is not None:
|
|
432
|
+
if not actual_type.isConvertibleTo(formal_type):
|
|
433
|
+
message = "%s of %s is incompatible with supplied parameter of type %s at line %s" % (info.description, node.description, actual_type.__doc__, node.lineNum)
|
|
434
|
+
self.typeMismatch(node, message)
|
|
435
|
+
return False
|
|
436
|
+
else:
|
|
437
|
+
|
|
438
|
+
message = "%s of %s has no type information" % (info.description, node.description)
|
|
439
|
+
self.typeError(node, message)
|
|
440
|
+
return False
|
|
441
|
+
return True
|
|
442
|
+
|
|
443
|
+
def checkAndInsertRValueCast(self, r_value, target_type):
|
|
444
|
+
'''
|
|
445
|
+
Check the value of the given r_value for compatibility with the target_type
|
|
446
|
+
and insert casts as necessary, or raise an error if no conversion is possible.
|
|
447
|
+
:param r_value: The r_value Node which is to be type checked.
|
|
448
|
+
:param target_type: The type to which the r_value should be converted.
|
|
449
|
+
'''
|
|
450
|
+
assert target_type is not None
|
|
451
|
+
if r_value is not None: # TODO Could this be an assert?
|
|
452
|
+
if r_value.actualType.isConvertibleTo(target_type):
|
|
453
|
+
if r_value.actualType is not target_type:
|
|
454
|
+
self.insertCast(r_value, r_value.actualType, target_type)
|
|
455
|
+
else:
|
|
456
|
+
message = "Cannot assign %s to %s" % (r_value.actualType.__doc__, target_type.__doc__)
|
|
457
|
+
self.typeMismatch(r_value, message)
|
|
458
|
+
|
|
459
|
+
def typeError(self, node, message):
|
|
460
|
+
message = "%s at line %d" % (message, node.lineNum)
|
|
461
|
+
internal(message)
|
|
462
|
+
|
|
463
|
+
def typeMismatch(self, node, message):
|
|
464
|
+
message = "Type mismatch: %s at line %s" % (message, node.lineNum)
|
|
465
|
+
error(message)
|
|
466
|
+
|
|
467
|
+
def castWarning(self, node, message):
|
|
468
|
+
message = "Implicit conversion %s at line %s" % (message, node.lineNum)
|
|
469
|
+
warning(message)
|
|
470
|
+
|
|
471
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from owl_basic.utility import underscoresToCamelCase
|
|
4
|
+
from owl_basic.visitor import Visitor
|
|
5
|
+
|
|
6
|
+
class ParentVisitor(Visitor):
|
|
7
|
+
"""
|
|
8
|
+
Visitor setting and verifying existing parent references on each AST node
|
|
9
|
+
"""
|
|
10
|
+
def __init__(self):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
def visitAstNode(self, node):
|
|
14
|
+
# TODO: This partial function application doesn't work correctly with IronPython
|
|
15
|
+
#node.forEachChild(partial(self._setParent, parent = node))
|
|
16
|
+
|
|
17
|
+
# TODO: Inlining the forEachChild function works, however...
|
|
18
|
+
for name, child in node.children.items():
|
|
19
|
+
if isinstance(child, list):
|
|
20
|
+
for index, subchild in enumerate(child):
|
|
21
|
+
self._setParent(node, subchild, name, index)
|
|
22
|
+
else:
|
|
23
|
+
self._setParent(node, child, name)
|
|
24
|
+
|
|
25
|
+
node.forEachChild(self.visit)
|
|
26
|
+
|
|
27
|
+
def _setParent(self, parent, node, name, index=None):
|
|
28
|
+
"""
|
|
29
|
+
Given a reference to the parent, and the name of a child value, create the
|
|
30
|
+
property name and attach it, together with the reference to the parent, to
|
|
31
|
+
the supplied node.
|
|
32
|
+
"""
|
|
33
|
+
if node is not None:
|
|
34
|
+
node.parent = parent
|
|
35
|
+
node.parent_property = underscoresToCamelCase(name) # The property through which the parent can be accessed.
|
|
36
|
+
node.parent_index = index
|
|
37
|
+
|
owl_basic/process.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'''Tools for subprocesses in IronPython'''
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
__author__ = 'rjs'
|
|
5
|
+
|
|
6
|
+
def execute(name, *args):
|
|
7
|
+
'''Execute an external process, and wait for it to complete.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
name: The path name to the executable.
|
|
11
|
+
|
|
12
|
+
*args: Remaining positional arguments are stringified and passed in
|
|
13
|
+
order as command line arguments to the process.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
A 3-tuple containing an integer exit code and stdout and stderr as
|
|
17
|
+
strings.
|
|
18
|
+
'''
|
|
19
|
+
logging.debug("process.execute(%s)" % name)
|
|
20
|
+
from System.Diagnostics import Process
|
|
21
|
+
p = Process()
|
|
22
|
+
p.StartInfo.FileName = name
|
|
23
|
+
p.StartInfo.Arguments = ' '.join(map(str, args))
|
|
24
|
+
p.StartInfo.CreateNoWindow = True
|
|
25
|
+
p.StartInfo.UseShellExecute = False
|
|
26
|
+
p.StartInfo.RedirectStandardInput = True
|
|
27
|
+
p.StartInfo.RedirectStandardOutput = True
|
|
28
|
+
p.StartInfo.RedirectStandardError = True
|
|
29
|
+
p.Start()
|
|
30
|
+
p.WaitForExit()
|
|
31
|
+
stdout = p.StandardOutput.ReadToEnd()
|
|
32
|
+
stderr = p.StandardError.ReadToEnd()
|
|
33
|
+
exit_code = p.ExitCode
|
|
34
|
+
p.Close()
|
|
35
|
+
return exit_code, stdout, stderr
|
|
36
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# A visitor implementation that creates an XML representation of the abstract syntax tree
|
|
2
|
+
|
|
3
|
+
from owl_basic.visitor import Visitor
|
|
4
|
+
from owl_basic.syntax.ast import StatementList, ScalarAssignment, Next, VariableList, Read, ReadFunc, WritableList
|
|
5
|
+
from owl_basic.ast_utils import replaceStatement, insertStatementBefore, removeStatement
|
|
6
|
+
|
|
7
|
+
class SeparationVisitor(Visitor):
|
|
8
|
+
"""
|
|
9
|
+
AST visitor for separating complex nodes into multiple simpler nodes
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def visitAstNode(self, node):
|
|
13
|
+
node.forEachChild(self.visit)
|
|
14
|
+
|
|
15
|
+
def visitDim(self, dim):
|
|
16
|
+
"""
|
|
17
|
+
Split DIM i%(1), j%(2), k% 3 statements into separate AllocateArray and AllocateBlock
|
|
18
|
+
statement nodes.
|
|
19
|
+
"""
|
|
20
|
+
#self.visit(next.identifiers)
|
|
21
|
+
statement_list = StatementList()
|
|
22
|
+
statement_list.parent = dim.parent
|
|
23
|
+
statement_list.parent_property = dim.parent_property
|
|
24
|
+
statement_list.parent_index = dim.parent_index
|
|
25
|
+
statement_list.statements = []
|
|
26
|
+
|
|
27
|
+
for allocator in dim.items.items:
|
|
28
|
+
allocator.parent = statement_list
|
|
29
|
+
allocator.parent_property = 'statements'
|
|
30
|
+
allocator.parent_index = len(statement_list.statements)
|
|
31
|
+
allocator.lineNum = dim.lineNum
|
|
32
|
+
statement_list.append(allocator)
|
|
33
|
+
|
|
34
|
+
getattr(dim.parent, dim.parent_property)[dim.parent_index] = statement_list
|
|
35
|
+
|
|
36
|
+
def visitNext(self, next):
|
|
37
|
+
"""
|
|
38
|
+
Split NEXT i%, j%, k% statements into NEXT i% : NEXT j% : NEXT k%
|
|
39
|
+
"""
|
|
40
|
+
self.visit(next.identifiers)
|
|
41
|
+
statement_list = StatementList()
|
|
42
|
+
statement_list.parent = next.parent
|
|
43
|
+
statement_list.parent_property = next.parent_property
|
|
44
|
+
statement_list.parent_index = next.parent_index
|
|
45
|
+
statement_list.statements = []
|
|
46
|
+
|
|
47
|
+
for identifier in next.identifiers.variables:
|
|
48
|
+
new_next = Next()
|
|
49
|
+
new_next.parent = statement_list
|
|
50
|
+
new_next.parent_property = 'statements'
|
|
51
|
+
new_next.parent_index = len(statement_list.statements)
|
|
52
|
+
new_next.lineNum = next.lineNum
|
|
53
|
+
statement_list.append(new_next)
|
|
54
|
+
|
|
55
|
+
variable_list = VariableList()
|
|
56
|
+
variable_list.parent = new_next
|
|
57
|
+
variable_list.parent_property = 'identifiers'
|
|
58
|
+
new_next.identifiers = variable_list
|
|
59
|
+
|
|
60
|
+
variable_list.variables = [identifier] if identifier is not None else []
|
|
61
|
+
|
|
62
|
+
getattr(next.parent, next.parent_property)[next.parent_index] = statement_list
|
|
63
|
+
|
|
64
|
+
# TODO: Much of this code can be factored out of this method and the above one
|
|
65
|
+
def visitRead(self, read):
|
|
66
|
+
"""
|
|
67
|
+
Split READ A, B, C statements into assignments A = READ : B = READ : C = READ
|
|
68
|
+
where READ becomes a function.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
# TODO Split READ A, B, C statements into READ A : READ B : READ C
|
|
72
|
+
self.visit(read.writables)
|
|
73
|
+
statement_list = StatementList()
|
|
74
|
+
statement_list.parent = read.parent
|
|
75
|
+
statement_list.parent_property = read.parent_property
|
|
76
|
+
statement_list.parent_index = read.parent_index
|
|
77
|
+
statement_list.statements = []
|
|
78
|
+
|
|
79
|
+
for writable in read.writables.writables:
|
|
80
|
+
read_func = ReadFunc()
|
|
81
|
+
new_assignment = ScalarAssignment(lValue=writable, rValue=read_func)
|
|
82
|
+
|
|
83
|
+
writable.parent = new_assignment
|
|
84
|
+
writable.parent_property = 'lValue'
|
|
85
|
+
writable.lineNum = read.lineNum
|
|
86
|
+
|
|
87
|
+
read_func.parent = new_assignment
|
|
88
|
+
read_func.parent_property = 'rValue'
|
|
89
|
+
read_func.lineNum = read.lineNum
|
|
90
|
+
|
|
91
|
+
new_assignment.parent = statement_list
|
|
92
|
+
new_assignment.parent_property = 'statements'
|
|
93
|
+
new_assignment.parent_index = len(statement_list.statements)
|
|
94
|
+
new_assignment.lineNum = read.lineNum
|
|
95
|
+
|
|
96
|
+
statement_list.append(new_assignment)
|
|
97
|
+
|
|
98
|
+
getattr(read.parent, read.parent_property)[read.parent_index] = statement_list
|
owl_basic/sigil.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from owl_basic.owltyping.type_system import (StringOwlType, IntegerOwlType, ByteOwlType, ObjectOwlType,
|
|
2
|
+
StringArrayOwlType, IntegerArrayOwlType, ByteArrayOwlType,
|
|
3
|
+
ObjectArrayOwlType, FloatArrayOwlType, FloatOwlType)
|
|
4
|
+
|
|
5
|
+
def identifierToType(identifier):
|
|
6
|
+
"""
|
|
7
|
+
Convert an variable name identifier to a type
|
|
8
|
+
"""
|
|
9
|
+
sigil = identifier[-1]
|
|
10
|
+
if sigil == '$':
|
|
11
|
+
return StringOwlType()
|
|
12
|
+
elif sigil == '%':
|
|
13
|
+
return IntegerOwlType()
|
|
14
|
+
elif sigil == '&':
|
|
15
|
+
return ByteOwlType()
|
|
16
|
+
elif sigil == '~':
|
|
17
|
+
return ObjectOwlType()
|
|
18
|
+
elif sigil == '(':
|
|
19
|
+
sigil = identifier[-2:-1]
|
|
20
|
+
if sigil == '$':
|
|
21
|
+
return StringArrayOwlType()
|
|
22
|
+
elif sigil == '%':
|
|
23
|
+
return IntegerArrayOwlType()
|
|
24
|
+
elif sigil == '&':
|
|
25
|
+
return ByteArrayOwlType()
|
|
26
|
+
elif sigil == '~':
|
|
27
|
+
return ObjectArrayOwlType()
|
|
28
|
+
else:
|
|
29
|
+
return FloatArrayOwlType()
|
|
30
|
+
return FloatOwlType()
|