-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patharith_eval.py
256 lines (212 loc) · 10.2 KB
/
arith_eval.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
# arith_eval
import random
class ArithEval:
# guardar as atribuições de variáveis
symbols = {}
# guardar as funções declaradas
functions = {}
# Defenir o que cada operador faz
operators = {
"+": lambda args: args[0] + args[1],
"-": lambda args: args[0] - args[1],
"*": lambda args: args[0] * args[1],
"/": lambda args: args[0] / args[1],
# Condições dos ifs
"==": lambda args: args[0] == args[1],
"!=": lambda args: args[0] != args[1],
"<": lambda args: args[0] < args[1],
">": lambda args: args[0] > args[1],
"<=": lambda args: args[0] <= args[1],
">=": lambda args: args[0] >= args[1],
"&&": lambda args: args[0] and args[1],
"||": lambda args: args[0] or args[1],
'neg': lambda args: not args[0],
#####
"seq": lambda args: args[-1],
"string": lambda args: str(args[0]),
"float": lambda args: float(args[0]),
"int": lambda args: int(args[0]),
"escrever": lambda args: print(args[0]),
"atribuicao": lambda args: ArithEval._atribuicao(args),
"comentario": lambda args: ArithEval._comentario(args),
"concat": lambda args: f'{args[0]}{args[1]}',
"interpolacao": lambda args: ''.join(args),
"entrada": lambda args: ArithEval._entrada(args),
"aleatorio": lambda args: ArithEval._aleatorio(args),
"call_func": lambda args: ArithEval._call_func(args),
"elementos_array": lambda args: args,
"array_vazio": lambda args: [],
"map": lambda args: ArithEval._map(args),
"fold": lambda args: ArithEval._fold(args),
}
@staticmethod
def _atribuicao(args):
varid = args[0] # 'A'
value = args[1] # 10
ArithEval.symbols[varid] = value # symbols { 'A':10 }
#print(f'{varid}: {value}')
return value
@staticmethod
def _comentario(args):
# Devolver None porque é um comentario
return None
@staticmethod
def _entrada(args):
# Chamar a função de input do python para introduzir o valor
return input("Função entrada, introduza o valor: ")
@staticmethod
def _aleatorio(args):
maxNumero = int(args[0]) # Obter o parametro da função aleatorio
# Validar se não é menor que 0
if maxNumero <= 0:
raise Exception("O valor do aleatorio tem de ser maior que 0")
# Devolver um número random entre 0 e o parametro
return random.randint(0, maxNumero)
@staticmethod
def _funcao(args):
nome_funcao = args[0]
parametros_funcao = args[1]
corpo_funcao = args[2]
if nome_funcao not in ArithEval.functions:
ArithEval.functions[nome_funcao] = []
ArithEval.functions[nome_funcao].append({
'parametros': parametros_funcao,
'corpo': corpo_funcao
})
return nome_funcao
@staticmethod
def _call_func(args):
nome_funcao = args[0]
parametros_chamada = args[1]
# Validar se a função foi declarada
if nome_funcao not in ArithEval.functions:
raise Exception(f"Função '{nome_funcao}' não declarada")
# Percorrer as funções com o nome
for func_def in ArithEval.functions[nome_funcao]:
parametros_funcao = func_def['parametros'] # Obter os parametros da função
copor_funcao = func_def['corpo'] # Obter o corpo da função
# Validar se os parametros da função e os que estamos a passar são os mesmos
if len(parametros_funcao) == len(parametros_chamada):
# Salvar o estado atual das variaveis
estado_temp = ArithEval.symbols.copy()
# Adicionar as variaveis as variaveis da função porque as mesmas são globais
funcao_symbols = estado_temp.copy()
# O zip é para percorrer os dois arrays ao mesmo tempo, ou sejá, ele meio que gera uma matriz
for param, value in zip(parametros_funcao, parametros_chamada):
# Se o parametro da função for uma var adicionamos o valor que estamos a passar na chamada
if 'var' in param:
funcao_symbols[param['var']] = value
# Se for um var:array pegamos no 1º valor do array e depois passamos o resto do array
elif 'op' in param and param['op'] == 'var_id_array':
# Validar se é realmente um array
if type(value) is not list:
raise Exception(f"O parametro da função {nome_funcao} tem de ser um array")
funcao_symbols[param['args'][0]] = value.pop() # Remover o 1º elemento do array
funcao_symbols[param['args'][1]] = value # Passar o resto dos elementos do array
# Se o parametro for um numero e for igual ao que estamos a passar nós avançamos
elif 'op' in param and ArithEval.evaluate(param) == value:
continue
else:
break
else: # Se todos os parametros da função estiverem validos nós executamos a função da iteração atual do loop
# Substituir temporariamente as variaveis globais
ArithEval.symbols = funcao_symbols
# Processar o corpo da função
result = ArithEval.evaluate(copor_funcao)
# Restaurar as variaveis globais
ArithEval.symbols = estado_temp
# Returnar o resultado da execução do corpo da função
return result
# Devolver o erro caso não exista nenhuma convinação de parametros correta para a função
raise Exception(f"Nenhuma correspondência de parâmetros para a função '{nome_funcao}'")
@staticmethod
def _map(args):
funcao = args[0] # Obter o nome da função
array = args[1] # Obter o array
# Validar se o parametro é mesmo um array
if type(array) is not list:
raise Exception(f"O segundo parametro do map tem de ser um array")
# Validar se a função existe
if funcao not in ArithEval.functions:
raise Exception(f"A função passada no map não foi declarada")
resultado = []
# Percorrer os elementos do array e chamar a função para cada elemento
for item in array:
resultado.append(ArithEval._call_func([funcao, [item]]))
# Devolver o resultado final
return resultado
@staticmethod
def _fold(args):
funcao = args[0] # Obter o nome da função
array = args[1] # Obter o array
valor_inicial = args[2] # Obter o valor inicial para o fold
# Validar se o parametro é mesmo um array
if type(array) is not list:
raise Exception(f"O segundo parametro do fold tem de ser um array")
# Validar se o parametro é mesmo um número
if type(valor_inicial) is not int:
raise Exception(f"O terceiro parametro do fold tem de ser um número ")
# Validar se a função existe
if funcao not in ArithEval.functions:
raise Exception(f"A função passada no map não foi declarada")
# O resultado começa com o valor inicial
resultado = valor_inicial
# Percorrer o array ao "contrario"
for item in reversed(array):
resultado = ArithEval._call_func([funcao, [item, resultado]])
return resultado
@staticmethod
def _se(args):
condicao = ArithEval._eval_operator(args[0]) # Validar a condição
# Se for verdadeira retornar o valor do SE
if condicao:
return ArithEval.evaluate(args[1])
elif len(args) > 2: # Se os args forem maiores que 2 quer dizer que tem um senão ou um senão se
index = 2
while index < len(args):
# Verificar se é uma condição senao_se
if 'op' in args[index] and args[index]['op'] == 'senao_se':
condicao_senao_se = ArithEval.evaluate(args[index]['args'][0]) # Obter o valor da condição
# Validar condição, se for verdadeira executar o código da mesma
if condicao_senao_se:
return ArithEval.evaluate(args[index]['args'][1])
index +=1 # Pular para o proxima senão_se ou senao
# Validar a condição de senão
elif 'op' in args[index] and args[index]['op'] == 'senao':
return ArithEval.evaluate(args[index]['args'][0])
else:
index += 1
return None
@staticmethod
def evaluate(ast):
if type(ast) is int: # constant value, eg in (int, str)
return ast
if type(ast) is dict: # { 'op': ... , 'args': ...}
return ArithEval._eval_operator(ast)
if type(ast) is str:
return ast
if type(ast) is list: # [ item, item ]
return [ArithEval.evaluate(a) for a in ast]
raise Exception(f"Tipo de AST desconhecido {ast}")
@staticmethod
def _eval_operator(ast):
# Processar quando for uma função
if 'op' in ast and ast['op'] == 'funcao':
return ArithEval._funcao(ast['args'])
# Processar quando for um se
if 'op' in ast and ast['op'] == 'se':
return ArithEval._se(ast['args'])
if 'op' in ast:
op = ast["op"]
args = [ArithEval.evaluate(a) for a in ast['args']]
if op in ArithEval.operators:
func = ArithEval.operators[op]
return func(args)
else:
raise Exception(f"Operador '{op}' desconhecido")
if 'var' in ast:
varid = ast["var"] # ast={ 'var': "A" } => ast["var"] varid="A"
if varid in ArithEval.symbols: # "A" in symbols { 'A':10 }
return ArithEval.symbols[varid] # 10
raise Exception(f"error: '{varid}' não declarado (primeira utilização nesta função)")
raise Exception('AST indefinido')