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,204 @@
1
+ import logging
2
+
3
+ from owl_basic.utility import camelCaseToUnderscores
4
+ from owl_basic.visitor import Visitor
5
+ from owl_basic.node import *
6
+ from owl_basic.options import *
7
+ from owl_basic.ast_utils import elideNode, insertStatementAfter
8
+ from owl_basic.syntax.ast import StatementList, Next
9
+
10
+ logger = logging.getLogger('simplify_visitor')
11
+
12
+ class SimplifyStatementListVisitor(Visitor):
13
+ """
14
+ Visitor for simplifying nested StatementList nodes by flattening the
15
+ list of statements.
16
+ """
17
+ def __init__(self):
18
+ self._accumulated_statements = []
19
+
20
+ def visitAstNode(self, node):
21
+ if node is not None:
22
+ self._accumulated_statements.append(node)
23
+
24
+ def visitStatementList(self, statement_list):
25
+ statement_list.forEachChild(self.visit)
26
+
27
+ def visitAstStatement(self, statement):
28
+ self._accumulated_statements.append(statement)
29
+
30
+ def _accumulatedStatements(self):
31
+ return self._accumulated_statements;
32
+
33
+ accumulatedStatements = property(_accumulatedStatements)
34
+
35
+ class SimplificationVisitor(Visitor):
36
+ """
37
+ AST visitor for simplifying the AST, by removing redundant nodes.
38
+ """
39
+
40
+ def visitAstNode(self, node):
41
+ node.forEachChild(self.visit)
42
+
43
+ def visitStatementList(self, statement_list):
44
+ "Flatten nested StatementLists and remove Statement nodes."
45
+ if not hasattr(statement_list, "parent"):
46
+ print(statement_list.statements)
47
+ assert 0
48
+ sslv = SimplifyStatementListVisitor()
49
+ sslv.visit(statement_list)
50
+ statement_list.statements = sslv.accumulatedStatements
51
+ for index, statement in enumerate(statement_list.statements):
52
+ statement.parent = statement_list
53
+ statement.parent_property = "statements"
54
+ statement.parent_index = index
55
+ self.visit(statement)
56
+
57
+ statement_list.parent.child_infos["statements"] = statement_list.child_infos["statements"]
58
+ assert hasattr(statement_list, "statements")
59
+ statement_list.parent.statements = statement_list.statements
60
+
61
+ def visitIf(self, iff):
62
+ if isinstance(iff.trueClause, StatementList):
63
+ sslv = SimplifyStatementListVisitor()
64
+ sslv.visit(iff.trueClause)
65
+ iff.child_infos['true_clause'] = iff.trueClause.child_infos['statements']
66
+ iff.trueClause = sslv.accumulatedStatements
67
+ if len(iff.trueClause) == 0:
68
+ iff.trueClause = None
69
+ else:
70
+ for index, statement in enumerate(iff.trueClause):
71
+ statement.parent = iff
72
+ statement.parent_property = 'trueClause'
73
+ statement.parent_index = index
74
+ self.visit(statement)
75
+ else:
76
+ self.visit(iff.trueClause)
77
+
78
+ if isinstance(iff.falseClause, StatementList):
79
+ sslv = SimplifyStatementListVisitor()
80
+ sslv.visit(iff.falseClause)
81
+ iff.child_infos['false_clause'] = iff.falseClause.child_infos['statements']
82
+ iff.falseClause = sslv.accumulatedStatements
83
+ if len(iff.falseClause) == 0:
84
+ iff.falseClause = None
85
+ else:
86
+ for index, statement in enumerate(iff.falseClause):
87
+ statement.parent = iff
88
+ statement.parent_property = 'falseClause'
89
+ statement.parent_index = index
90
+ self.visit(statement)
91
+ else:
92
+ self.visit(iff.falseClause)
93
+
94
+ self.visit(iff.condition)
95
+
96
+ def visitOnGoto(self, ongoto):
97
+ if ongoto.outOfRangeClause is not None:
98
+ if isinstance(ongoto.outOfRangeClause, StatementList):
99
+ sslv = SimplifyStatementListVisitor()
100
+ sslv.visit(ongoto.outOfRangeClause)
101
+ ongoto.child_infos['out_of_range_clause'] = ongoto.outOfRangeClause.child_infos['statements']
102
+ ongoto.outOfRangeClause = sslv.accumulatedStatements
103
+ if len(ongoto.outOfRangeClause) == 0:
104
+ ongoto.outOfRangeClause = None
105
+ else:
106
+ for index, statement in enumerate(ongoto.outOfRangeClause):
107
+ statement.parent = ongoto
108
+ statement.parent_property = 'outOfRangeClause'
109
+ statement.parent_index = index
110
+ self.visit(statement)
111
+ else:
112
+ self.visit(ongoto.outOfRangeClause)
113
+
114
+ self.visit(ongoto.switch)
115
+ self.visit(ongoto.targetLogicalLines)
116
+
117
+ def visitCase(self, case):
118
+ "Remove the WhenClauseList level from the AST"
119
+ case.child_infos["when_clauses"] = case.whenClauses.child_infos["clauses"]
120
+ case.whenClauses = case.whenClauses.clauses
121
+ for clause in case.whenClauses:
122
+ clause.parent = case
123
+ self.visit(clause)
124
+
125
+ def visitMarkerStatement(self, marker):
126
+ """
127
+ Remove the followingStatement from the Repeat, DefineProcedure, etc, moving it to immediately
128
+ after the statement in the parent StatementList
129
+ """
130
+ logger.debug("visitMarkerStatement %s at line number %s", marker, marker.lineNum)
131
+ if marker.followingStatement is not None:
132
+ following = marker.followingStatement
133
+ marker.followingStatement = None
134
+ insertStatementAfter(marker, following)
135
+ # TODO: Does the moved statement node ever get visited? Are we inserting into a sequence during iteration?
136
+ #self.visit(following)
137
+
138
+ def visitExpressionList(self, expr_list):
139
+ """
140
+ Remove ExpressionList level from the AST by replacing the contents of
141
+ the owning attribute of its parents with the ExpressionList's own list of expressions
142
+ """
143
+ expr_list.forEachChild(self.visit)
144
+ elideNode(expr_list, liftFormalTypes=True)
145
+
146
+ def visitVduList(self, vdu_list):
147
+ """
148
+ Remove VduList level from the AST by replacing the contents of
149
+ the owning attribute of its parent with the VduList's own list of items
150
+ """
151
+ vdu_list.forEachChild(self.visit)
152
+ elideNode(vdu_list, liftFormalTypes=True)
153
+
154
+ def visitActualArgList(self, actual_arg_list):
155
+ """
156
+ Remove the ActualArgList level from the AST by replacing the contents of
157
+ the owning attribute of its parent with the ActualArgList's own list of arguments
158
+ """
159
+ actual_arg_list.forEachChild(self.visit)
160
+ elideNode(actual_arg_list, liftFormalTypes=True)
161
+
162
+ def visitFormalArgList(self, formal_arg_list):
163
+ """
164
+ Remove the FormalArgList level from the AST by replacing the contents of
165
+ the owning attribute of its parent with the FormalArgList's own list of arguments
166
+ """
167
+ formal_arg_list.forEachChild(self.visit)
168
+ elideNode(formal_arg_list, liftFormalTypes=True)
169
+
170
+ def visitPrintList(self, print_list):
171
+ """
172
+ Remove the PrintList level from the AST by replacing the contents of the
173
+ owning attribute of its parent.
174
+ """
175
+ print_list.forEachChild(self.visit)
176
+ elideNode(print_list, liftFormalTypes=True)
177
+
178
+ def visitInputList(self, input_list):
179
+ """
180
+ Remove the InputList level from the AST by replacing the contents of the
181
+ owning attribute of its parent.
182
+ """
183
+ input_list.forEachChild(self.visit)
184
+ elideNode(input_list, liftFormalTypes=True)
185
+
186
+ def visitVariableList(self, variable_list):
187
+ """
188
+ Remove the VariableList level from the AST by replacing the contents of the
189
+ owning attribute of its parent.
190
+ """
191
+ variable_list.forEachChild(self.visit)
192
+ elideNode(variable_list, liftFormalTypes=True)
193
+
194
+ def visitExpressionList(self, expression_list):
195
+ """
196
+ Remove the ExpresionList level from the AST by replacing the contents of the
197
+ owning attribute of its parent.
198
+ """
199
+ expression_list.forEachChild(self.visit)
200
+ elideNode(expression_list, liftFormalTypes=True)
201
+
202
+ # TODO: visitInputList
203
+
204
+
owl_basic/singleton.py ADDED
@@ -0,0 +1,127 @@
1
+ """
2
+ A Python Singleton mixin class that makes use of some of the ideas
3
+ found at http://c2.com/cgi/wiki?PythonSingleton. Just inherit
4
+ from it and you have a singleton. No code is required in
5
+ subclasses to create singleton behavior -- inheritance from
6
+ Singleton is all that is needed.
7
+
8
+ Assume S is a class that inherits from Singleton. Useful behaviors
9
+ are:
10
+
11
+ 1) Getting the singleton:
12
+
13
+ S.getInstance()
14
+
15
+ returns the instance of S. If none exists, it is created.
16
+
17
+ 2) The usual idiom to construct an instance by calling the class, i.e.
18
+
19
+ S()
20
+
21
+ is disabled for the sake of clarity. If it were allowed, a programmer
22
+ who didn't happen notice the inheritance from Singleton might think he
23
+ was creating a new instance. So it is felt that it is better to
24
+ make that clearer by requiring the call of a class method that is defined in
25
+ Singleton. An attempt to instantiate via S() will restult in an SingletonException
26
+ being raised.
27
+
28
+ 3) If S.__init__(.) requires parameters, include them in the
29
+ first call to S.getInstance(.). If subsequent calls have parameters,
30
+ a SingletonException is raised.
31
+
32
+ 4) As an implementation detail, classes that inherit
33
+ from Singleton may not have their own __new__
34
+ methods. To make sure this requirement is followed,
35
+ an exception is raised if a Singleton subclass includ
36
+ es __new__. This happens at subclass instantiation
37
+ time (by means of the MetaSingleton metaclass.
38
+
39
+ By Gary Robinson, grobinson@transpose.com. No rights reserved --
40
+ placed in the public domain -- which is only reasonable considering
41
+ how much it owes to other people's version which are in the
42
+ public domain. The idea of using a metaclass came from
43
+ a comment on Gary's blog (see
44
+ http://www.garyrobinson.net/2004/03/python_singleto.html#comments).
45
+ Other improvements came from comments and email from other
46
+ people who saw it online. (See the blog post and comments
47
+ for further credits.)
48
+
49
+ Not guaranteed to be fit for any particular purpose. Use at your
50
+ own risk.
51
+ """
52
+
53
+
54
+
55
+
56
+
57
+ class SingletonException(Exception):
58
+ pass
59
+
60
+
61
+ class MetaSingleton(type):
62
+ def __new__(metaclass, strName, tupBases, dict):
63
+ if '__new__' in dict:
64
+ raise SingletonException('Can not override __new__ in a Singleton')
65
+ return super(MetaSingleton,metaclass).__new__(metaclass, strName, tupBases, dict)
66
+
67
+ def __call__(cls, *lstArgs, **dictArgs):
68
+ raise SingletonException('Singletons may only be instantiated through getInstance()')
69
+
70
+ class Singleton(object, metaclass=MetaSingleton):
71
+ def __init__(self, *args, **kwargs):
72
+ """
73
+ Provided for compatibility with subclasses which call super() in their __init__
74
+ """
75
+ super(Singleton, self).__init__(*args, **kwargs)
76
+
77
+ # @TODO: This method NEEDS to be thread-safe
78
+ def getInstance(cls, *lstArgs, **kwargs):
79
+ """
80
+ Call this to instantiate an instance or retrieve the existing instance.
81
+ If the singleton requires args to be instantiated, include them the first
82
+ time you call getInstance.
83
+ """
84
+ if cls._isInstantiated():
85
+ if len(lstArgs) + len(kwargs) != 0:
86
+ raise SingletonException('If no supplied args, singleton must already be instantiated, or __init__ must require no args')
87
+ else:
88
+ instance = cls.__new__(cls)
89
+ instance.__init__(*lstArgs, **kwargs)
90
+ cls.cInstance = instance
91
+ return cls.cInstance
92
+ getInstance = classmethod(getInstance)
93
+
94
+ def _isInstantiated(cls):
95
+ return hasattr(cls, 'cInstance')
96
+ _isInstantiated = classmethod(_isInstantiated)
97
+
98
+ def _getConstructionArgCountNotCountingSelf(cls):
99
+ return cls.__init__.im_func.func_code.co_argcount - 1
100
+ _getConstructionArgCountNotCountingSelf = classmethod(_getConstructionArgCountNotCountingSelf)
101
+
102
+ def _forgetClassInstanceReferenceForTesting(cls):
103
+ """
104
+ This is designed for convenience in testing -- sometimes you
105
+ want to get rid of a singleton during test code to see what
106
+ happens when you call getInstance() under a new situation.
107
+
108
+ To really delete the object, all external references to it
109
+ also need to be deleted.
110
+ """
111
+ try:
112
+ delattr(cls,'cInstance')
113
+ except AttributeError:
114
+ # run up the chain of base classes until we find the one that has the instance
115
+ # and then delete it there
116
+ for baseClass in cls.__bases__:
117
+ if issubclass(baseClass, Singleton):
118
+ baseClass._forgetClassInstanceReferenceForTesting()
119
+ _forgetClassInstanceReferenceForTesting = classmethod(_forgetClassInstanceReferenceForTesting)
120
+
121
+
122
+
123
+ if __name__ == '__main__':
124
+
125
+ unittest.main()
126
+
127
+
@@ -0,0 +1,124 @@
1
+ import logging
2
+ import re
3
+ from bisect import bisect_right
4
+
5
+ from owl_basic.visitor import Visitor
6
+
7
+ class SourceDebuggingVisitor(Visitor):
8
+ '''
9
+ A visitor for computing start and end character columns based upon
10
+ file offsets attached to each AstStatement node and offsets to the beginning
11
+ of each line. The operation of this visitor assumes that a depth-first traversal
12
+ of the AST will visit all statements in source program order.
13
+ '''
14
+ def __init__(self, data, line_offsets, line_number_prefixes):
15
+ '''
16
+ :param data: The source file as a string
17
+ :param line_offsets: an array of line offsets
18
+ :param line_number_prexies: an array of the lengths of the line-number and
19
+ whitespace prefix to each source line
20
+ '''
21
+ self.__data = data
22
+ self.__line_offsets = line_offsets
23
+ self.__line_number_prefixes = line_number_prefixes
24
+ self.__previous_statement = None
25
+ self.__separator_regex = re.compile(r'\s*([\n:]|ELSE)')
26
+
27
+ def visitAstNode(self, node):
28
+ node.forEachChild(self.visit)
29
+
30
+ def visitAstStatement(self, statement):
31
+ '''
32
+ Get the startPos of the visited statement. Use this value, together with the
33
+ probably innaccurate endPos of the *previous* statement to locate the end of
34
+ the previous statement, and update its endPos value.
35
+ '''
36
+ #print statement
37
+ self.setPreviousStatementColumns(statement)
38
+ self.__previous_statement = statement
39
+ # There is no need to visit the children of most statements
40
+
41
+ def visitIf(self, statement):
42
+ #print statement
43
+ self.setPreviousStatementColumns(statement)
44
+ # If statements, don't need adjusting, so we unset self.__previous_statement
45
+ self.__previous_statement = None
46
+ # We must visit the children of IF statements
47
+ if statement.trueClause is not None:
48
+ #print "visiting trueClause"
49
+ self.visit(statement.trueClause)
50
+
51
+ if statement.falseClause is not None:
52
+ #print "visiting falseClause"
53
+ self.visit(statement.falseClause)
54
+
55
+ # Set up the columns for this IF statement itself
56
+ self.setStartAndEndColumns(statement)
57
+
58
+ def setPreviousStatementColumns(self, statement):
59
+ '''
60
+ Update the endPos attribute on self.__previous_statement based upon the startPos attribute
61
+ of the statement parameters. A search between the two statements for a statement separator
62
+ is performed, and computations of the start and end character columns for __previous_statement
63
+ are performed and these attributes set.
64
+ Note: Assumes that self.__previous statement is the prior statement in the program
65
+ order to statement
66
+ :param statement: The statement being visited, the start of which marks the latest position
67
+ for the end of the previous statement.
68
+ '''
69
+ if self.__previous_statement is not None:
70
+ self.adjustEndPos(self.__previous_statement, statement.startPos)
71
+ self.setStartAndEndColumns(self.__previous_statement)
72
+
73
+ def adjustEndPos(self, statement, limit_pos):
74
+ '''
75
+ Adjust the endPos attribute of statement forwards by searching from its existing
76
+ location forwards up to limit_pos for a statement separator (colon or newline)
77
+ '''
78
+ # Define a half-open range [search_start_pos, search_end_pos) within which we expect
79
+ # to locate a statement boundary.
80
+ #print statement
81
+ #print statement.startPos
82
+ #print statement.endPos
83
+ search_start_pos = statement.endPos
84
+ search_end_pos = limit_pos
85
+ assert search_start_pos is not None
86
+ assert search_end_pos is not None
87
+ if search_start_pos < search_end_pos - 1:
88
+ # TODO: Some possible issue here with line end markers (because of normalised line endings), ignoring the contents of REMs, DATA and LiteralStrings
89
+ # and line continuation
90
+ # Search for new-lines or COLONs within the search string
91
+ m = self.__separator_regex.search(self.__data, search_start_pos, search_end_pos)
92
+ if m is not None:
93
+ statement.endPos = m.start()
94
+ else:
95
+ print("Error: Could not locate statement separator in >>>%s<<<" % self.__data[search_start_pos:search_end_pos])
96
+ #print ">>>%s<<<" % self.__data[statement.startPos:statement.endPos]
97
+
98
+ def setStartAndEndColumns(self, statement):
99
+ '''
100
+ Sets the start and end columns on statement from start and end lexing positions.
101
+ :param statement: The statement node to be modified.
102
+ '''
103
+ statement.startColumn = self.columnFromPos(statement.startPos)
104
+ statement.endColumn = self.columnFromPos(statement.endPos)
105
+
106
+ def columnFromPos(self, pos):
107
+ '''
108
+ :param pos: An zero-based offset from the beginning of the source file
109
+ :returns: A one-based character column from the beginning of the line
110
+ '''
111
+ # TODO: Could make this faster by passing in a range on lines to check
112
+ #print "pos =", pos
113
+ index = bisect_right(self.__line_offsets, pos) - 1
114
+ #print "index =", index
115
+ line_start_pos = self.__line_offsets[index]
116
+ #print "line_start_pos =", line_start_pos
117
+ prefix_length = self.__line_number_prefixes[index]
118
+ #print "prefix_length =", prefix_length
119
+ column = pos - line_start_pos + prefix_length + 1 # One based
120
+ #print "column =", column
121
+ #print
122
+ return column
123
+
124
+
@@ -0,0 +1,220 @@
1
+ # A visitor for performing type-checking over the Abstract Syntax Tree
2
+
3
+ import logging
4
+ from functools import partial
5
+
6
+ from owl_basic.visitor import Visitor
7
+ from owl_basic.errors import *
8
+ from owl_basic.symbol_tables import *
9
+ from owl_basic.syntax.ast import FormalArgument, FormalReferenceArgument, Variable, AstStatement
10
+ from owl_basic.ast_utils import findNode
11
+ from owl_basic import sigil
12
+
13
+ logger = logging.getLogger('symbol_table_visitor')
14
+
15
+ class SymbolTableVisitor(Visitor):
16
+ """
17
+ CFG visitor for annotating statement nodes with
18
+ references to a symbol table.
19
+ """
20
+ def __init__(self):
21
+ self.__global_symbols = SymbolTable("global symbol table",
22
+ protection=SymbolTable.writable,
23
+ parent=SystemSymbolTable.getInstance())
24
+
25
+ def _getGlobalSymbols(self):
26
+ return self.__global_symbols
27
+
28
+ globalSymbols = property(_getGlobalSymbols)
29
+
30
+ def followSuccessors(self, statement):
31
+ # Visit successors - depth first through CFG
32
+ for out_edge in statement.outEdges:
33
+ if out_edge.symbolTable is None:
34
+ self.visit(out_edge)
35
+
36
+ def checkPredecessorsAndRefer(self, statement):
37
+ """
38
+ Given a statement, return the symbol table of the
39
+ preceding statement. If a statement has multiple predecessors,
40
+ check that all predecessors refer to the same
41
+ symbol table - raise an error if not.
42
+ """
43
+ if statement.symbolTable is None:
44
+ symbol_table = None
45
+ for in_edge in statement.inEdges:
46
+ if in_edge.symbolTable is not None:
47
+ if symbol_table is None:
48
+ symbol_table = in_edge.symbolTable
49
+ else:
50
+ if in_edge.symbolTable is not symbol_table:
51
+ errors.fatalError("Inconsistent variable scopes for %s at line %s" % statement, statement.lineNum)
52
+ assert symbol_table is not None
53
+ return symbol_table
54
+ return statement.symbolTable
55
+
56
+ def tryAddVariable(self, symbol_table, variable):
57
+ if (isinstance(variable, Variable)):
58
+ symbol_info = SymbolInfo(variable.identifier, variable.actualType)
59
+ symbol_table.tryAdd(symbol_info)
60
+ else:
61
+ assert 0, "%s is not a variable" % variable
62
+ # TODO: What?
63
+ pass
64
+
65
+ def visitAstStatement(self, statement):
66
+ """
67
+ Attaches the same symbol table as the predecessor statement to this
68
+ statement Depth first search visit of successors statements
69
+ """
70
+ #logger.debug("SymbolTableVisitor.visitAstStatement %s at %s", statement, statement.lineNum)
71
+ statement.symbolTable = self.checkPredecessorsAndRefer(statement)
72
+ assert statement.symbolTable is not None
73
+ # TODO: Check that all other variable references within this statement can
74
+ # be successfully looked up.
75
+ self.followSuccessors(statement)
76
+
77
+ def visitDefinitionStatement(self, defproc):
78
+ """
79
+ Visit DEFPROC DEFFN. Create a new symbol table containing the formal
80
+ parameters of the procedure or function, which also refers to the global symbol
81
+ table.
82
+ """
83
+ #logger.debug("SymbolTableVisitor.visitDefinitionStatement")
84
+ if defproc.symbolTable is None:
85
+ symbol_table = None
86
+ if defproc.formalParameters is None or len(defproc.formalParameters.arguments) == 0:
87
+ # If there are no parameters, we just use the
88
+ # global symbol table
89
+ symbol_table = self.__global_symbols
90
+ else:
91
+ symbol_infos = []
92
+
93
+ for formal_argument in defproc.formalParameters.arguments:
94
+ name = formal_argument.argument.identifier
95
+ type = formal_argument.argument.actualType
96
+ if isinstance(formal_argument.argument, FormalArgument):
97
+ modifier = SymbolInfo.modifier_arg
98
+ elif isinstance(formal_argument.argument, FormalReferenceArgument):
99
+ modifier = SymbolInfo.modifier_ref_arg
100
+ symbol_info = SymbolInfo(name, type, SymbolInfo.modifier_arg)
101
+ symbol_infos.append(symbol_info)
102
+ symbol_table = FormalParameterSymbolTable(symbol_infos, defproc.name, self.__global_symbols)
103
+ assert symbol_table is not None
104
+ defproc.symbolTable = symbol_table
105
+ self.followSuccessors(defproc)
106
+
107
+ def visitLocal(self, local):
108
+ #logger.debug("SymbolTableVisitor.visitLocal")
109
+ # TODO: We should have a warning if LOCAL and PRIVATE are not the first
110
+ # statements in a definition
111
+ # TODO: REFACTOR This is almost identical to visitPrivate
112
+ if 'MAIN' in local.entryPoints:
113
+ errors.fatalError("Items can only be made local in a function or procedure at line %s" % local.lineNum)
114
+ if local.symbolTable is None:
115
+ symbol_infos = []
116
+ for variable in local.variables:
117
+ name = variable.identifier
118
+ type = variable.actualType
119
+ symbol_info = SymbolInfo(name, type, SymbolInfo.modifier_local)
120
+ symbol_infos.append(symbol_info)
121
+ assert len(local.entryPoints) == 1
122
+ procedure = iter(local.entryPoints).next()
123
+ symbol_table = LocalSymbolTable(symbol_infos, procedure, self.checkPredecessorsAndRefer(local))
124
+ assert symbol_table is not None
125
+ local.symbolTable = symbol_table
126
+ self.followSuccessors(local)
127
+
128
+ def visitPrivate(self, private):
129
+ #logger.debug("SymbolTableVisitor.visitPrivate")
130
+ # TODO: We should have a warning if LOCAL and PRIVATE are not the first
131
+ # statements in a definition
132
+ # TODO: REFACTOR This is almost identical to visitLocal
133
+ if 'MAIN' in private.entryPoints:
134
+ errors.fatalError("Items can only be made local in a function or procedure at line %s" % local.lineNum)
135
+ if private.symbolTable is None:
136
+ symbol_infos = []
137
+ for variable in private.variables:
138
+ name = variable.identifier
139
+ type = variable.actualType
140
+ symbol_info = SymbolInfo(name, type, SymbolInfo.modifier_private)
141
+ symbol_infos.append(symbol_info)
142
+ assert len(private.entryPoints) == 1
143
+ procedure = iter(private.entryPoints).next()
144
+ symbol_table = PrivateSymbolTable(symbol_infos, procedure, self.checkPredecessorsAndRefer(private))
145
+ assert symbol_table is not None
146
+ private.symbolTable = symbol_table
147
+ self.followSuccessors(private)
148
+
149
+ def visitAssignment(self, statement):
150
+ logger.debug("SymbolTableVisitor.visitAssignment")
151
+ print(statement)
152
+ #assert statement.symbolTable is not None
153
+ statement.symbolTable = self.checkPredecessorsAndRefer(statement)
154
+ #self.tryAddVariable(statement.symbolTable, statement.lValue)
155
+ statement.lValue.accept(self)
156
+ self.followSuccessors(statement)
157
+
158
+ def visitVariable(self, variable):
159
+ logger.debug("SymbolTableVisitor.visitVariable")
160
+ statement_node = findNode(variable, lambda node: isinstance(node, AstStatement))
161
+ symbol_table = statement_node.symbolTable
162
+ self.tryAddVariable(symbol_table, variable)
163
+
164
+ def visitAllocateArray(self, statement):
165
+ logger.debug("SymbolTableVisitor.allocateArray")
166
+ statement.symbolTable = self.checkPredecessorsAndRefer(statement)
167
+ symbol_info = SymbolInfo(statement.identifier, sigil.identifierToType(statement.identifier),
168
+ rank=len(statement.dimensions))
169
+ statement.symbolTable.tryAdd(symbol_info)
170
+ self.followSuccessors(statement)
171
+
172
+ def visitAllocateBlock(self, statement):
173
+ #logger.debug("SymbolTableVisitor.allocateBlock")
174
+ statement.symbolTable = self.checkPredecessorsAndRefer(statement)
175
+ assert statement.symbolTable is not None
176
+ symbol_info = SymbolInfo(statement.identifier, PtrType)
177
+ self.followSuccessors(statement)
178
+
179
+ def visitForToStep(self, statement):
180
+ #logger.debug("SymbolTableVisitor.visitForToStep")
181
+ statement.symbolTable = self.checkPredecessorsAndRefer(statement)
182
+ assert statement.symbolTable is not None
183
+ self.tryAddVariable(statement.symbolTable, statement.identifier)
184
+ self.followSuccessors(statement)
185
+
186
+ def visitInput(self, statement):
187
+ #logger.debug("SymbolTableVisitor.visitInput")
188
+ statement.symbolTable = self.checkPredecessorsAndRefer(statement)
189
+ assert statement.symbolTable is not None
190
+ variables = (item for item in statement.inputList if isinstance(item, Variable))
191
+ for variable in variables:
192
+ self.tryAddVariable(statement.symbolTable, variable)
193
+ self.followSuccessors(statement)
194
+
195
+ def visitInputFile(self, input):
196
+ #logger.debug("SymbolTableVisitor.visitInputFile")
197
+ statement.symbolTable = self.checkPredecessorsAndRefer(statement)
198
+ assert statement.symbolTable is not None
199
+ for item in statement.inputList:
200
+ self.tryAddVariable(statement.symbolTable, item)
201
+ self.followSuccessors(input)
202
+
203
+ def visitMouse(self, mouse):
204
+ #logger.debug("SymbolTableVisitor.visitInput")
205
+ statement.symbolTable = self.checkPredecessorsAndRefer(statement)
206
+ assert statement.symbolTable is not None
207
+ self.tryAddVariable(statement.symbolTable, statement.xCoord)
208
+ self.tryAddVariable(statement.symbolTable, statement.yCoord)
209
+ self.tryAddVariable(statement.symbolTable, statement.buttons)
210
+ self.tryAddVariable(statement.symbolTable, statement.time)
211
+ self.followSuccessors(statement)
212
+
213
+ def visitRead(self, statement):
214
+ #logger.debug("SymbolTableVisitior.visitRead")
215
+ statement.symbolTable = self.checkPredecessorsAndRefer(statement)
216
+ assert statement.symbolTable is not None
217
+ for writable in statement.writables.writables:
218
+ self.tryAddVariable(statement.symbolTable, writable)
219
+
220
+