@@ -63,8 +63,11 @@ def block_to_code(block: Dict[str, Any], level: int = 0) -> str:
6363 target = _safe_str (block .get ('target' , 'res' ))
6464 left = _safe_str (block .get ('left' , '0' ))
6565 right = _safe_str (block .get ('right' , '0' ))
66+ # Leemos el operador, pero si viene vacío ("") o es inválido, forzamos el '+'
6667 op = block .get ('op' , '+' )
67- # Si no hay target, es una expresión suelta (raro en scripts, pero posible)
68+ if not op or op not in ['+' , '-' , '*' , '/' ]:
69+ op = '+'
70+ # Si no hay target, es una expresión suelta
6871 if not target :
6972 return f"{ indent } { left } { op } { right } \n "
7073 return f"{ indent } { target } = { left } { op } { right } \n "
@@ -90,9 +93,13 @@ def block_to_code(block: Dict[str, Any], level: int = 0) -> str:
9093
9194 # Comparaciones (Sueltas o para IF)
9295 if b_type == 'py_compare' :
93- # Nota: Normalmente esto iría dentro de un If, pero si está suelto:
9496 left = _safe_str (block .get ('left' , 'a' ))
97+ # Leemos el operador, pero si viene vacío ("") o es inválido, forzamos el '=='
9598 op = block .get ('op' , '==' )
99+ valid_ops = ['==' , '!=' , '>' , '<' , '>=' , '<=' ]
100+ if not op or op not in valid_ops :
101+ op = '=='
102+
96103 right = _safe_str (block .get ('right' , 'b' ))
97104 return f"{ indent } { left } { op } { right } \n "
98105
@@ -104,6 +111,17 @@ def block_to_code(block: Dict[str, Any], level: int = 0) -> str:
104111 header = f"{ indent } if { cond } :\n "
105112 return header + _generate_body (block .get ('body' , []), level + 1 )
106113
114+ # Elif
115+ if b_type == 'py_elif' :
116+ cond = _safe_str (block .get ('condition' , 'True' ))
117+ header = f"{ indent } elif { cond } :\n "
118+ return header + _generate_body (block .get ('body' , []), level + 1 )
119+
120+ # Else
121+ if b_type == 'py_else' :
122+ header = f"{ indent } else:\n "
123+ return header + _generate_body (block .get ('body' , []), level + 1 )
124+
107125 # Loops
108126 if b_type == 'py_loop' : # For
109127 iterator = _safe_str (block .get ('iterator' , 'i' ))
@@ -131,12 +149,16 @@ def block_to_code(block: Dict[str, Any], level: int = 0) -> str:
131149 header = f"{ indent } class { name } { base_str } :\n "
132150 return header + _generate_body (block .get ('body' , []), level + 1 )
133151
134- # Try/Except
152+ # Try
135153 if b_type == 'py_try' :
136154 header = f"{ indent } try:\n "
137- try_body = _generate_body (block .get ('body' , []), level + 1 )
138- except_blk = f"{ indent } except Exception as e:\n { indent } print(e)\n "
139- return header + try_body + except_blk
155+ return header + _generate_body (block .get ('body' , []), level + 1 )
156+
157+ # Except
158+ if b_type == 'py_except' :
159+ err_type = _safe_str (block .get ('error_type' , 'Exception as e' ))
160+ header = f"{ indent } except { err_type } :\n "
161+ return header + _generate_body (block .get ('body' , []), level + 1 )
140162
141163 # Otros
142164 if b_type == 'py_return' :
@@ -166,7 +188,15 @@ def block_to_code(block: Dict[str, Any], level: int = 0) -> str:
166188 return f"{ indent } import { names } \n "
167189
168190 if b_type == 'py_control' :
169- return f"{ indent } { block .get ('control_type' , 'pass' )} \n "
191+ # Si el usuario no interactúa con el desplegable, la variable viene vacía.
192+ # Forzamos a que el valor por defecto sea 'break', igual que en la interfaz visual.
193+ c_type = block .get ('control_type' , 'break' )
194+
195+ # Validación extra de seguridad para evitar inyección de código
196+ if c_type not in ['break' , 'continue' , 'pass' ]:
197+ c_type = 'break'
198+
199+ return f"{ indent } { c_type } \n "
170200
171201 # MACHINE LEARNING
172202 if ml_rules and b_type .startswith ('ml_' ):
@@ -209,7 +239,14 @@ def _ast_expr_to_str(node):
209239
210240def _parse_body (stmts : List [ast .stmt ]) -> List [Dict [str , Any ]]:
211241 """Parsea recursivamente una lista de sentencias AST."""
212- return [ast_node_to_block (n ) for n in stmts ]
242+ blocks = []
243+ for n in stmts :
244+ res = ast_node_to_block (n )
245+ if isinstance (res , list ):
246+ blocks .extend (res )
247+ else :
248+ blocks .append (res )
249+ return blocks
213250
214251def ast_node_to_block (node : ast .AST ) -> Dict [str , Any ]:
215252 """Convierte un nodo AST en un diccionario de bloque."""
@@ -245,11 +282,31 @@ def ast_node_to_block(node: ast.AST) -> Dict[str, Any]:
245282
246283 # Control de Flujo (Recursivo)
247284 if isinstance (node , ast .If ):
248- return {
285+ if_block = {
249286 "type" : "py_if" ,
250287 "condition" : _ast_expr_to_str (node .test ),
251288 "body" : _parse_body (node .body )
252289 }
290+ if node .orelse :
291+ # Detección de Elif (En AST, un elif es un if dentro de un orelse)
292+ if len (node .orelse ) == 1 and isinstance (node .orelse [0 ], ast .If ):
293+ elif_res = ast_node_to_block (node .orelse [0 ])
294+ if isinstance (elif_res , dict ):
295+ elif_res ['type' ] = 'py_elif'
296+ return [if_block , elif_res ]
297+ elif isinstance (elif_res , list ):
298+ elif_res [0 ]['type' ] = 'py_elif'
299+ return [if_block ] + elif_res
300+ else :
301+ # Detección de Else tradicional
302+ else_block = {
303+ "type" : "py_else" ,
304+ "body" : _parse_body (node .orelse )
305+ }
306+ return [if_block , else_block ]
307+
308+ return if_block
309+
253310 if isinstance (node , ast .For ):
254311 return {
255312 "type" : "py_loop" ,
@@ -284,13 +341,32 @@ def ast_node_to_block(node: ast.AST) -> Dict[str, Any]:
284341
285342 # Try/Except
286343 if isinstance (node , ast .Try ):
287- # Mapeamos el cuerpo principal al bloque.
288- # Nota: Los handlers (except) son complejos de mapear visualmente en un solo nodo.
289- # Por ahora capturamos el cuerpo del try.
290- return {
344+ try_block = {
291345 "type" : "py_try" ,
292346 "body" : _parse_body (node .body )
293347 }
348+
349+ blocks = [try_block ]
350+
351+ # Iteramos sobre todos los bloques 'except' que tenga el código
352+ for handler in node .handlers :
353+ err_type = ""
354+ if handler .type :
355+ err_type = _ast_expr_to_str (handler .type )
356+ # Si el error tiene un alias (ej: except ValueError as e)
357+ if handler .name :
358+ err_type += f" as { handler .name } "
359+ else :
360+ err_type = "Exception as e"
361+
362+ except_block = {
363+ "type" : "py_except" ,
364+ "error_type" : err_type ,
365+ "body" : _parse_body (handler .body )
366+ }
367+ blocks .append (except_block )
368+
369+ return blocks
294370
295371 # Expresiones
296372 if isinstance (node , ast .Expr ):
0 commit comments