-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSyntaxRepl.py
More file actions
237 lines (200 loc) · 7.91 KB
/
Copy pathSyntaxRepl.py
File metadata and controls
237 lines (200 loc) · 7.91 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
# Module PreCompiler
# Author Raul
# 31/01/2014 19:38:00
import re
import fileinput
import logging
import sys
from Config import *
from pyPEG import parse, Symbol
from GrammarRules import GRAMMAR
from Debug import debug
from Utils import getAst, ast2str, commaSeparatedString
from Exceptions import Error, Critical
from Config import LOG_FILE__
# MODULE PLAIN API #############################################################
def precompile(ast=[], outputFile=""):
"""
@input ast: parsed model in a pyAST
@
* get definitions
* check there are no cycles in them
* replace inside definitions ( from leafs to top using dfs)
* use definitions to replace everywhere else
* write new file
* parse it and return the result
"""
_p = preCompiler(ast)
_p.sintaxReplacement(outputFile)
return
# PRECOMPILER CLASS ############################################################
class preCompiler():
def __init__(self, ast=[]):
self.ast = ast
self.defs = {} # Definitions dictionary
self.adj = {} # Adjacency dictionary
self.getDefs()
self.checkCircularDependance()
def getDefs(self):
""" Get the definitions from self.ast and fill in self.defs with them.
"""
_dlist = getAst(self.ast, ["DEFINE"])
for _x in _dlist:
_xn = _x.what[2] # FIXME CACASO
_xv = _x.what[4] # FIXME CACASO
self.defs[ast2str(_xn)] = ast2str(_xv)
logging.log(logging.INSPECT, "Definitions dictionary " + str(self.defs))
def checkCircularDependance(self):
""" Check that there is no circular dependance beteween definitions.
Rise Utils.Error() exception otherwise.
@ require: must call self.getDefs() first
"""
self.adj = {}
for d,v in self.defs.iteritems():
splits = ' |\,|\+|\-|\%|\/|\*|\<|\>|\=|\;' #TODO should . split too?
self.adj[d] = [x for x in re.split(splits,v) if x in self.defs]
cycl = hasCycle(self.adj)
if cycl != []:
raise Error("Circular dependance in DEFINES: " \
+ commaSeparatedString(cycl) + ".")
#FIXME won't print correctly in wt1.fll
def sintaxReplacement(self, outputFile=""):
""" Make the sintax replacements due to DEFINES in the model.
@return: a string with the model after the sintactic replacements
have been done.
"""
# We need to make replacements in definitions first
self.setDefs()
replace(self.ast, self.defs, outputFile)
def setDefs(self):
""" Make the replacements in self.defs due to defintions it self. """
visited = {}
for _d, _dummy in self.defs.iteritems():
visited[_d] = False
for _d, _dummy in self.defs.iteritems():
_dummy, visited = self.setDefsDFS(_d, visited)
def setDefsDFS(self, d, visited):
""" A DFS algorithm to set de definitions correctly. """
if self.adj[d] != []:
for x in self.adj[d]:
visited[x] = True
self.defs[d] = \
self.defs[d].replace( x, self.setDefsDFS(x, visited)[0])
return self.defs[d], visited
# EXTRA FUNCTIONS ##############################################################
# TODO very ineficent algorithm!! make it better
def hasCycle(adj):
""" Given an adjacency dictionary check if there are cycles in it.
@input adj: An adjacency dictionary, with names as keys and lists of
dajacent names as values.
@return: [] if no cycles are found, a list with the first found cycle
otherwise.
"""
stack = []
for e,v in adj.iteritems():
try:
stack = cycleDFS(e, adj, stack)
except Exception:
return stack
# its acyclic:
return []
#..............................................................................
def cycleDFS(r, adj, stack):
for a in adj[r]:
if a in stack:
raise Exception(stack)
else:
stack.append(a)
stack = cycleDFS(a, adj, stack)
return stack[:-1:]
#..............................................................................
def replace(ast=[] , defs={}, path=""):
""" Write a file with a model from a pyAST structure but making sintax
replacements from definitions.
@input ast: the model in a pyAST.
@input defs: a dictionary with the sintax replacements to be done.
@input path: the path to the file to be written.
@return : a string with the model if path is "". None otherwise.
"""
result = ""
if isinstance(ast, Symbol):
if ast.__name__ == "DEFINE":
for x in ast.what:
if isinstance(x, Symbol) and x.__name__ != "EXPRESION":
result += ast2str(x)
else:
result += replace(x, defs, "")
elif ast.__name__ == "OPTIONS" or \
ast.__name__ == "ENDOPTIONS" or \
ast.__name__ == "CHECKDEADLOCK" or \
ast.__name__ == "FAULTFAIRDISABLE" or \
ast.__name__ == "MODULEWFAIRDISABLE" or \
ast.__name__ == "COMMENT"or \
ast.__name__ == "EXPLAIN" or \
ast.__name__ == "BL":
result += ast2str(ast)
else:
for x in ast.what:
result += replace(x, defs, "")
elif isinstance(ast, list):
for x in ast:
result += replace(x, defs, "")
elif isinstance(ast, unicode):
result += strrepl(ast, defs)
else:
raise Critical("Wrong input for this function:%s"%str(ast))
if path != "":
try:
f = open(path, 'w')
f.write(result)
except Exception as e:
raise Error( "Coudn't write the file with sintax replacement.\n" \
+ "Because: " + str(e))
finally:
logging.log( logging.INSPECT,\
"The precompiled file looks like \n%s\n"%result)
f.close()
result = ""
return result
#..............................................................................
def strrepl( string="", defs={}):
""" Return a modified version of a string following sintax definitions.
@input string; the string we want to modify.
@input defs: the sintactic definitions for making the replacements
@return: a string which is equal from 'string' except for the
replacements that 'defs' sugests.
"""
assert isinstance(string, str) | isinstance(string, unicode) \
, "The following is not a string: %r" %string
result = string
for d, v in defs.iteritems():
# debug("debugGREEN", "Replacing " + d + " by " + v + " in " + result)
result = re.sub( u'\\b' + d + u'\\b', v, result)
# debug("debugLBLUE", "Got " + result)
return result
# MODULE MAIN ##################################################################
if __name__ == "__main__":
_file = fileinput.input()
# logging.basicConfig(filename='logfile.log', level=logging.DEBUG)
# Logging levels:
# CRITICAL 50
# ERROR 40
# WARNING 30
# INFO 20
# DEBUG 10
# NOTSET 0
logging.basicConfig( level=logging.DEBUG
, format = '[ %(levelname)s ] ' \
+ '[%(filename)s] %(message)s')
LINFO("Parsing ...")
_ast = parse(GRAMMAR, _file, False, packrat = False)
LINFO("Parsed <%s>."%_file.filename())
logging.log(logging.INSPECT, str(_ast))
try:
LINFO("Precompiling <%s> ..."%_file.filename())
precompile(_ast, _file.filename()+".precompiled")
LINFO("Precompiled into <%s>."%(_file.filename()+".precompiled"))
except Critical:
LEXCEPTION(":S something very bad happened.")
except Error,e:
LERROR(str(e))