Ethgoin commited on
Commit
d02548d
·
1 Parent(s): ae21790

Migración completa a Python

Browse files
Files changed (7) hide show
  1. app.py +29 -0
  2. entrada.txt +5 -0
  3. lexer.py +62 -0
  4. main.py +19 -0
  5. parser.py +68 -0
  6. requirements.txt +1 -0
  7. semantico.py +46 -0
app.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import json
3
+ from lexer import lexer
4
+ from parser import Parser
5
+ from semantico import AnalizadorSemantico
6
+
7
+ def analizar_codigo(archivo):
8
+ try:
9
+ contenido = archivo.read().decode("utf-8")
10
+ tokens = lexer(contenido)
11
+ parser = Parser(tokens)
12
+ ast = parser.parse()
13
+ analizador = AnalizadorSemantico(ast)
14
+ resultado = analizador.analizar()
15
+ errores = "\n".join(resultado["errores_semanticos"])
16
+ json_resultado = json.dumps(resultado, indent=2)
17
+ return errores, json_resultado
18
+ except Exception as e:
19
+ return f"Error: {e}", "{}"
20
+
21
+ gr.Interface(
22
+ fn=analizar_codigo,
23
+ inputs=gr.File(label="Sube un archivo de código"),
24
+ outputs=[
25
+ gr.Textbox(label="Errores detectados"),
26
+ gr.Code(label="Contenido de analisis.json", language="json")
27
+ ],
28
+ title="Analizador Semántico en Python - Lenguaje de Robots"
29
+ ).launch()
entrada.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ x = 5;
2
+ y = z + 3.5;
3
+ IF y THEN {
4
+ a = y - 1;
5
+ }
lexer.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ from typing import List, Tuple
3
+
4
+ Token = Tuple[str, str]
5
+
6
+ # Lista de palabras clave y símbolos
7
+ RESERVED = {
8
+ "ACTIVATE_ALARM", "ACTIVATE_SENSOR", "BREAK", "CALIBRATE", "CHARGE_BATTERY", "CHECK_BATTERY",
9
+ "CLOSE_DOOR", "CONTINUE", "COPY_FILE", "DEACTIVATE_ALARM", "DEACTIVATE_SENSOR",
10
+ "DECREASE_SPEED", "DELETE_FILE", "DOWNLOAD", "ELSE", "FALSE", "FOR", "IF", "INCREASE_SPEED",
11
+ "INIT", "LOCK", "LOG", "LOW_BATTERY", "MOVE_BACKWARD", "MOVE_FORWARD", "MOVE_TO", "NULL",
12
+ "OPEN_DOOR", "PAUSE", "PRINT", "READ_SENSOR", "REBOOT", "RENAME_FILE", "RESET", "RESUME",
13
+ "REVERSE", "ROTATE", "SAVE_FILE", "SCAN", "SET_SPEED", "SHUTDOWN", "SHUT_OFF", "START", "STOP",
14
+ "STOP_IMMEDIATELY", "THEN", "TOGGLE_LIGHT", "TRUE", "TURN_DOWN", "TURN_LEFT", "TURN_RIGHT",
15
+ "TURN_UP", "UNLOCK", "UPLOAD", "UPLOAD_FILE", "WAIT", "WHILE"
16
+ }
17
+
18
+ TOKEN_SPEC = [
19
+ ("FLOAT", r'\d+\.\d+'),
20
+ ("INT", r'\d+'),
21
+ ("ASSIGN", r'='),
22
+ ("PLUS", r'\+'),
23
+ ("MINUS", r'-'),
24
+ ("MULTIPLY", r'\*'),
25
+ ("DIVIDE", r'/'),
26
+ ("EQUAL", r'=='),
27
+ ("NOT_EQUAL", r'!='),
28
+ ("GREATER", r'>'),
29
+ ("LESS", r'<'),
30
+ ("OPEN_PAREN", r'\('),
31
+ ("CLOSE_PAREN", r'\)'),
32
+ ("OPEN_BRACE", r'\{'),
33
+ ("CLOSE_BRACE", r'\}'),
34
+ ("SEMICOLON", r';'),
35
+ ("COLON", r':'),
36
+ ("COMMA", r','),
37
+ ("NEWLINE", r'\n'),
38
+ ("SKIP", r'[ \t\r]+'),
39
+ ("IDENTIFIER", r'[a-zA-Z_][a-zA-Z0-9_]*'),
40
+ ("MISMATCH", r'.'),
41
+ ]
42
+
43
+ TOK_REGEX = '|'.join(f'(?P<{name}>{pattern})' for name, pattern in TOKEN_SPEC)
44
+ token_re = re.compile(TOK_REGEX)
45
+
46
+ def lexer(code: str) -> List[Token]:
47
+ tokens = []
48
+ for match in token_re.finditer(code):
49
+ kind = match.lastgroup
50
+ value = match.group()
51
+ if kind == "NEWLINE" or kind == "SKIP":
52
+ continue
53
+ elif kind == "IDENTIFIER":
54
+ if value in RESERVED:
55
+ tokens.append((value, value))
56
+ else:
57
+ tokens.append(("IDENTIFIER", value))
58
+ elif kind == "MISMATCH":
59
+ raise RuntimeError(f"Token no reconocido: {value}")
60
+ else:
61
+ tokens.append((kind, value))
62
+ return tokens
main.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from lexer import lexer
2
+ from parser import Parser
3
+ from semantico import AnalizadorSemantico
4
+ import json
5
+
6
+ with open("entrada.txt", "r", encoding="utf-8") as f:
7
+ codigo = f.read()
8
+
9
+ tokens = lexer(codigo)
10
+ parser = Parser(tokens)
11
+ ast = parser.parse()
12
+
13
+ analizador = AnalizadorSemantico(ast)
14
+ resultado = analizador.analizar()
15
+
16
+ with open("analisis.json", "w", encoding="utf-8") as out:
17
+ json.dump(resultado, out, indent=2)
18
+
19
+ print("✔ Análisis completo. Resultados guardados en 'analisis.json'")
parser.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Tuple, Any
2
+
3
+ Token = Tuple[str, str]
4
+
5
+ class Parser:
6
+ def __init__(self, tokens: List[Token]):
7
+ self.tokens = tokens
8
+ self.pos = 0
9
+ self.ast = []
10
+
11
+ def current(self) -> Token:
12
+ return self.tokens[self.pos] if self.pos < len(self.tokens) else ("EOF", "")
13
+
14
+ def match(self, expected_type: str) -> Token:
15
+ if self.current()[0] == expected_type:
16
+ tok = self.current()
17
+ self.pos += 1
18
+ return tok
19
+ raise SyntaxError(f"Se esperaba {expected_type} pero se encontró {self.current()}")
20
+
21
+ def parse(self):
22
+ while self.current()[0] != "EOF":
23
+ self.ast.append(self.instruction())
24
+ return self.ast
25
+
26
+ def instruction(self) -> Any:
27
+ token = self.current()
28
+ if token[0] == "IDENTIFIER":
29
+ return self.assignment()
30
+ elif token[0] == "IF":
31
+ return self.if_statement()
32
+ else:
33
+ raise SyntaxError(f"Instrucción no válida: {token}")
34
+
35
+ def assignment(self):
36
+ ident = self.match("IDENTIFIER")[1]
37
+ self.match("ASSIGN")
38
+ expr = self.expression()
39
+ self.match("SEMICOLON")
40
+ return {"type": "assign", "var": ident, "value": expr}
41
+
42
+ def if_statement(self):
43
+ self.match("IF")
44
+ condition = self.expression()
45
+ self.match("THEN")
46
+ self.match("OPEN_BRACE")
47
+ body = []
48
+ while self.current()[0] != "CLOSE_BRACE":
49
+ body.append(self.instruction())
50
+ self.match("CLOSE_BRACE")
51
+ return {"type": "if", "condition": condition, "body": body}
52
+
53
+ def expression(self):
54
+ left = self.term()
55
+ while self.current()[0] in ("PLUS", "MINUS"):
56
+ op = self.match(self.current()[0])[0]
57
+ right = self.term()
58
+ left = {"type": "binop", "op": op, "left": left, "right": right}
59
+ return left
60
+
61
+ def term(self):
62
+ token_type, token_value = self.current()
63
+ if token_type == "IDENTIFIER":
64
+ return {"type": "var", "value": self.match("IDENTIFIER")[1]}
65
+ elif token_type == "NUMERO":
66
+ return {"type": "num", "value": self.match("NUMERO")[1]}
67
+ else:
68
+ raise SyntaxError(f"Expresión inválida: {self.current()}")
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ gradio
semantico.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class AnalizadorSemantico:
2
+ def __init__(self, ast):
3
+ self.ast = ast
4
+ self.tabla_simbolos = {}
5
+ self.errores = []
6
+
7
+ def analizar(self):
8
+ for nodo in self.ast:
9
+ self.analizar_instruccion(nodo)
10
+ return {
11
+ "variables_declaradas": self.tabla_simbolos,
12
+ "errores_semanticos": self.errores
13
+ }
14
+
15
+ def analizar_instruccion(self, nodo):
16
+ if nodo["type"] == "assign":
17
+ tipo_valor = self.analizar_expresion(nodo["value"])
18
+ self.tabla_simbolos[nodo["var"]] = tipo_valor
19
+ elif nodo["type"] == "if":
20
+ tipo_condicion = self.analizar_expresion(nodo["condition"])
21
+ if tipo_condicion != "int":
22
+ self.errores.append(
23
+ f"Condición del IF debe ser de tipo int, pero se encontró '{tipo_condicion}'"
24
+ )
25
+ for instr in nodo["body"]:
26
+ self.analizar_instruccion(instr)
27
+
28
+ def analizar_expresion(self, expr):
29
+ if expr["type"] == "num":
30
+ return "float" if "." in expr["value"] else "int"
31
+ elif expr["type"] == "var":
32
+ nombre = expr["value"]
33
+ if nombre not in self.tabla_simbolos:
34
+ self.errores.append(f"Variable '{nombre}' usada sin ser declarada.")
35
+ return "error"
36
+ return self.tabla_simbolos[nombre]
37
+ elif expr["type"] == "binop":
38
+ tipo_izq = self.analizar_expresion(expr["left"])
39
+ tipo_der = self.analizar_expresion(expr["right"])
40
+ if tipo_izq != tipo_der:
41
+ self.errores.append(f"Tipos incompatibles: {tipo_izq} y {tipo_der}")
42
+ return "error"
43
+ return tipo_izq
44
+ else:
45
+ self.errores.append(f"Expresión no reconocida: {expr}")
46
+ return "error"