-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser_rules.py
282 lines (229 loc) · 9.02 KB
/
parser_rules.py
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
279
280
281
282
import ply.yacc as yacc
import sys
from parser import Parser
from lexer_rules import tokens
DEBUG = True
#Clase propia para manejar los errores de Musileng
class SemanticException(Exception):
def __init__(self, message, lineNumber):
# Call the base class constructor with the parameters it needs
#super(MusilengException, self).__init__(message)
# Now for your custom code...
self.message = message
self.lineNumber = lineNumber
self.filename = ''
Reglas.cantvoices = 1
Reglas.dicc = {}
Reglas.consts = {}
Reglas.constsCon = {}
def __str__(self):
return self.errorMsg()
def errorMsg(self):
""" Muestro un mesaje custom de error diciendo en que archivo, nro de linea y error que hubo """
return " --> " + self.filename + ":" + str(self.lineNumber) + " - " + self.message
class Reglas():
cantvoices = 1
dicc = {}
consts = {}
constsCon = {}
@classmethod
def instrumentoEnRango(cls, idInstrumento):
""" Devuelve True si un instrumento esta bien configurado en el rango aceptado por MIDI """
return idInstrumento >= 0 and idInstrumento <= 127
@classmethod
def octavaEnRango(cls, octava):
""" Chequeo que el valor de la octava este bien definido """
return octava >= 1 and octava <= 9
#BNF
def p_start(p):
'start : encabezado constantes voces'
Reglas.cantvoices = 1
Reglas.dicc = {}
Reglas.consts = {}
Reglas.constsCon = {}
v = p[3]
p[2] = []
p[0] = [p[1][1], v]
if(len(v) == 0 or len(v) > 16):
raise SemanticException("La cantidad de voces debe ser mayor a cero", p.lineno(3))
def p_encabezado(p):
'encabezado : tempo compasDef'
p[0] = ["encabezado", [p[1], p[2]]]
def p_tempo(p):
'tempo : HASH TEMPO FIGURE NUMBER'
tempo = p[4]
if tempo <= 0:
raise SemanticException("El tiempo de duracion de la figura "+ str(p[3]["type"]) +" debe ser mayor a 0.", p.lineno(3))
p[0] = [p[3]["type"] , tempo]
def p_compas_def(p):
'compasDef : HASH COMPAS NUMBER DIV NUMBER'
p[0] = [p[3], p[5]]
Reglas.dicc["compas_val"] = (float(p[3]) / float(p[5]))
def p_constantes(p):
'constantes : CONST CONSTID EQUAL NUMBER SEMICOLON constantes'
heredated = Reglas.consts.keys()
con = p[2]
if(con in heredated):
message = "Constant " + con + " has already been declared"
raise SemanticException(message, p.lineno(2))
Reglas.consts[con] = p[4]
prev = p[-1][0]
if prev == "encabezado":
for current in Reglas.constsCon.keys():
if Reglas.constsCon[current][0] in Reglas.consts.keys():
Reglas.consts[current] = Reglas.consts[Reglas.constsCon[current][0]]
else:
message = "The constant value " + Reglas.constsCon[current][0] + "has never been declared"
raise SemanticException(message, Reglas.constsCon[current][1])
def p_constantes_constid(p):
'constantes : CONST CONSTID EQUAL CONSTID SEMICOLON constantes'
toDefine = p[4]
con = p[2]
heredated = Reglas.consts.keys()
if(con in heredated):
message = "Constant " + con + " has already been declared"
raise SemanticException(message, p.lineno(2))
Reglas.constsCon[con] = [toDefine, p.lineno(2)]
prev = p[-1][0]
if prev == "encabezado":
for current in Reglas.constsCon.keys():
if Reglas.constsCon[current][0] in Reglas.consts.keys():
Reglas.consts[current] = Reglas.consts[Reglas.constsCon[current][0]]
else:
message = "The constant value " + Reglas.constsCon[current][0] + "has never been declared"
raise SemanticException(message, Reglas.constsCon[current][1])
def p_constantes_lambda(p):
'constantes : '
def p_voces(p):
'voces : voz voces'
p[0] = [p[1]] + p[2]
def p_voces_lambda(p):
'voces : '
p[0] = []
def p_voz(p):
'voz : decla_instrumento LCURL musica RCURL'
mus = p[3]
if not mus:
message = "Las voces tienen que tener un compas definido como minimo."
raise SemanticException(message, p.lineno(3))
p[0] = [p[1], mus]
def p_decla_instrumento(p):
'decla_instrumento : VOICE LPAREN NUMBER RPAREN'
Reglas.cantvoices = Reglas.cantvoices + 1
if(Reglas.cantvoices > 16):
raise SemanticException("No se pueden configurar mas de 16 voces para el estandar", p.lineno(1))
idInstrumento = p[3]
if not(Reglas.instrumentoEnRango(idInstrumento)):
msg = "El instrumento '" + str(idInstrumento) + "' no esta en el rango valido soportado por MIDI (0 a 127)"
raise SemanticException(msg, p.lineno(3))
p[0] = idInstrumento
def p_decla_instrumento_const(p):
'decla_instrumento : VOICE LPAREN CONSTID RPAREN'
if(not(p[3] in Reglas.consts)):
message = "Constante " + p[3] + " no declarada!"
raise SemanticException(message, p.lineno(3))
else:
Reglas.cantvoices = Reglas.cantvoices + 1
p[0] = Reglas.consts[p[3]]
def p_musica_lambda(p):
'musica :'
p[0] = []
def p_musica_compas(p):
'musica : compas musica'
p[0] = [['C', p[1]]] + p[2]
def p_musica_bucle(p):
'musica : bucle musica'
p[0] = [['B', p[1]]] + p[2]
def p_compas(p):
'compas : COMPAS LCURL notas RCURL'
lista = p[3]
duracion_compas = 0
for diccs in lista:
duracion_compas += diccs["duration"]
if(duracion_compas != Reglas.dicc["compas_val"]):
message = "El tiempo del compas es erroneo"
raise SemanticException(message, p.lineno(1))
else:
p[0] = lista
def p_bucle(p):
'bucle : REPEAT LPAREN NUMBER RPAREN LCURL musica RCURL'
p[0] = [p[3], p[6]]
def p_bucle_constid(p):
'bucle : REPEAT LPAREN CONSTID RPAREN LCURL musica RCURL'
con = p[3]
if(con in Reglas.consts.keys()):
p[0] = [Reglas.consts[con], p[6]]
else:
message = "La constante " + con + " no esta definida"
raise SemanticException(message, p.lineno(1))
def p_notas_lambda(p):
'notas : '
p[0] = []
def p_notas(p):
'notas : figura notas'
p[0] = [p[1]] + p[2]
def p_figura_nota(p):
'figura : notaProd'
p[0] = p[1]
def p_figura_silencio(p):
'figura : silencio'
p[0] = p[1]
def p_nota_prod(p):
'notaProd : NOTA LPAREN altura COMMA NUMBER COMMA duracion RPAREN SEMICOLON'
if not(Reglas.octavaEnRango(p[5])):
raise SemanticException("El valor de octava definido como '"+ str(p[5])+"' esta fuera del rango permitido", p.lineno(5))
p[0] = {}
p[0]["duration"] = p[7]
p[0]["nota"] = p[3][0]
p[0]["desv"] = p[3][1]
p[0]["octava"] = p[5]
p[0]["type"] = "NOT"
# FALTA HACER QUE EN VEZ DE ACEPTAR UN NUMBER PUEDA ACEPTAR UN CONSTID
def p_nota_prod_constid(p):
'notaProd : NOTA LPAREN altura COMMA CONSTID COMMA duracion RPAREN SEMICOLON'
var = p[5]
if(not(var in Reglas.consts)):
message = "Constante '" + var + "' no declarada"
raise SemanticException(message, p.lineno(5))
else:
if not(Reglas.octavaEnRango(Reglas.consts[var])):
raise SemanticException("El valor de octava definido como '"+ str(p[5])+"'=" + str(Reglas.consts[var]) +" esta fuera del rango permitido", p.lineno(5))
p[0] = {}
p[0]["duration"] = p[7]
p[0]["nota"] = p[3][0]
p[0]["desv"] = p[3][1]
p[0]["octava"] = Reglas.consts[var]
p[0]["type"] = "NOT"
def p_altura(p):
'altura : NOTAID simbolo_altura'
p[0] = [p[1], p[2]]
def p_simbolo_altura_lambda(p):
'simbolo_altura : '
p[0] = ""
def p_simbolo_altura(p):
'simbolo_altura : ALTURA'
p[0] = p[1]
def p_duracion(p):
'duracion : FIGURE'
p[0] = (float(1) / float(p[1]["value"]))
def p_duracion_punto(p):
'duracion : FIGURE DOT'
p[0] = (1 / float(p[1]["value"])) * 1.5
def p_silencio(p):
'silencio : SILENCIO LPAREN duracion RPAREN SEMICOLON'
p[0] = {}
p[0]["duration"] = p[3]
p[0]["type"] = "SIL"
def p_error(token):
Reglas.cantvoices = 1
Reglas.dicc = {}
Reglas.consts = {}
Reglas.constsCon = {}
message = "[Syntax error]"
if token is not None:
message += "\ntype:" + token.type
message += "\nvalue:" + str(token.value)
message += "\nline:" + str(token.lineno)
message += "\nposition:" + str(token.lexpos)
raise Exception(message)
yacc = yacc.yacc()