-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpl0namelist.py
More file actions
278 lines (219 loc) · 9.62 KB
/
pl0namelist.py
File metadata and controls
278 lines (219 loc) · 9.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# file: pl0namelist.py
# description: Namelist which keeps track of all parsed idents and procedures/variables/constants
# author: Raphael Pour <info@raphaelpour.de>
# date: 24.01.2018
# license: GPL v3 (https://www.gnu.org/licenses/gpl-3.0.en.html)
#
import logging
#
# All NL*-Classes are only Data-Structures to hold
# the neccessary information for building up a namelist
#
class NLIdent():
def __init__(self, name, value=None):
self.name = name
self.value = value
class NLProc(NLIdent):
def __init__(self, name,parent,index):
super().__init__(name)
self.parent = parent
self.constants = []
self.variables = []
self.childProcedures = []
self.paramAddressOffset = -16
self.localAddressOffset = 0
self.index = index
class NLConst(NLIdent):
def __init__(self, value,index,name=None):
super().__init__(name,value)
self.index = index
class NLVar(NLIdent):
def __init__(self, name,addressOffset, parent,value=None, procedureParameter=False, fields=0):
super().__init__(name,value)
self.parent = parent
self.addressOffset = addressOffset
self.procedureParameter = procedureParameter
self.fields = fields
#
# PL0NameList is an interface to access and arrange
# the Elements of a Name-List. It also does semantic checks and
# avoids duplicate idents depending on the context
#
class PL0NameList:
def __init__(self):
""" Initializes the NameList by building up the
neccessary data-structure for semantic lookup operations.
It also adds a main/root procedure
"""
# For the semantic checks and searches, it is neccessary to
# carry additional lists which overlaps with the data-structure
# build up with all the NL*-Classes.
self.procedures = []
self.constantList = []
# Main Programm will be the first procedure without a parent
self.procedures.append(NLProc(parent=None,name="main",index=0))
self.currentProcedure = self.procedures[-1]
def mainProc(self):
""" Returns the Main/Root Procedure. This is the (grand)parent of
all other procedures
"""
return self.procedures[0]
def createConst(self, value, name=None):
"""Adds a new constant to the current procedure.
It doesn't check if the ident is already existing,
while this should be done in the parser when the
ident-name is parsed
In order to add anonym constants, the name can be left out.
Additionally the value will be looked up in the global
constant list to get the index of the previously added
constant with the same value. The aim is to store each
constant value only once, even used with differen idents.
"""
# Check if ident-name already exists in local scope
#result = self.searchIdentNameLocal(name)
#if result is not None:
# logging.error("Ident " + name + " already exists.")
# return False
# Check if value is already in the global constant list
# If not: index will be the length of the list
index = len(self.constantList)
cachedConst = None
for c in self.constantList:
if c.value == value:
cachedConst = c
index = c.index
break
# If the constant is not already existent in the global
# constant list, create it
if cachedConst is None:
cachedConst = NLConst(value,index=index)
self.constantList.append(cachedConst)
# If the constant is anonym, the procedure will get the
# instance of the global constant list for more storage efficency
# otherwise a new instance will be created with the name
newConst = None
if name is None:
newConst = cachedConst
else:
newConst = NLConst(value,index,name)
self.currentProcedure.childProcedures.append(newConst)
return newConst
def createProcedureParam(self,name):
currentOffset = self.currentProcedure.paramAddressOffset
self.currentProcedure.paramAddressOffset -= 4
newVar = NLVar(name=name,parent=self.currentProcedure,addressOffset=currentOffset, procedureParameter=True)
self.currentProcedure.variables.append(newVar)
return newVar
def turnLastVarToArray(self,fields):
self.currentProcedure.variables[-1].fields = fields
# Corrent current local address offset
self.currentProcedure.localAddressOffset += 4*(fields-1)
def createVar(self, name):
"""Adds a new variable to the current procedure.
It doesn't check if the ident is already existing,
while this should be done in the parser when the
ident-name is parsed
"""
# Each variable has a relative address offset
currentOffset = self.currentProcedure.localAddressOffset
# Increase address offset for the next variable
self.currentProcedure.localAddressOffset += 4
newVar = NLVar(name=name,parent=self.currentProcedure,addressOffset=currentOffset)
# Add variable to the local variable list of the current procedure
self.currentProcedure.variables.append(newVar)
return newVar
def correctParameterList(self):
if len(self.currentProcedure.variables) == 0:
return
# Correct Address
# The first argument has to be the highest negative relative address
# because it was pushed at very first onto the stack
params = list(filter(lambda v: v.procedureParameter, self.currentProcedure.variables))
# Reverse parameters
params = params[::-1]
# -16 is the negative offset in order to overstep the entry proc command with
# properties of the current procedure
for i in range(0,len(params)):
params[i].addressOffset = -16 -i*4
def createProc(self, name,parent=None):
# The current procedure is the default value for procedure
if parent is None:
parent = self.currentProcedure
newProc = NLProc(name=name, parent=parent, index=len(self.procedures))
# Append new procedure as child to the current
# Procedure
parent.childProcedures.append(newProc)
# Append new procedure to the global procedure list
self.procedures.append(newProc)
# Our current procedure is now the newly created until we
# end the procedure with endProc() to go back to the parent
self.currentProcedure = newProc
return newProc
def endProc(self):
""" Ends the current procedure by resetting it to its parent.
This is equal to leave a procedure and go back to the next
higher one.
"""
#if self.currentProcedure.parent is None and self.currentProcedure is not self.mainProc:
# logging.error("[NameList] Procedure can't be ended. Parent not found.")
# return False
self.currentProcedure = self.currentProcedure.parent
return True
def searchConstByValue(self,value):
for const in self.constantList:
if const.value == value:
return const
return None
def searchIdentNameLocal(self, name, procedure=None):
""" The Local Scope is provided using the local search to
find out if an ident is already in use.
"""
# The current procedure is the default value for procedure
if procedure is None:
procedure = self.currentProcedure
# Procedure itself is named after given ident-name?
if procedure.name == name:
return procedure
# Procedure has child named after given ident-name?
for cp in procedure.childProcedures:
if cp.name == name:
return cp
# Local Constant named after the given ident-name?
for c in procedure.constants:
if c.name == name:
return c
# Local Variable named after the given ident-name?
for v in procedure.variables:
if v.name == name:
return v
return None
def searchIdentNameGlobal(self,name,procedure=None):
""" In order to check if an ident is used in global scope,
this search goes from "inner to outer" scope and makes a local
search in each one.
The rule is: local scope overwrites global scope.
"""
if procedure is None:
procedure = self.currentProcedure
while 1:
ident = self.searchIdentNameLocal(name=name, procedure=procedure)
if ident is None:
# If the current procedure has no parent
# -> we reached the main procedure and the ident
# doesn't exist
if procedure is None or procedure.parent is None:
return None
else:
# Otherwise we use the parent of the current procedure
# for the next round. This enables the "from inner to outer"
# search
procedure = procedure.parent
else:
return ident
def isLocalIdentName(self,name,procedure=None):
return self.searchIdentNameLocal(procedure=procedure, name=name) != None
def isGlobalIdentName(self,name,procedure=None):
return self.searchIdentNameGlobal(procedure=procedure, name=name)