Files
centvrion/centvrion/compiler/emit_stmt.py
2026-04-25 20:21:16 +02:00

204 lines
7.4 KiB
Python

from centvrion.ast_nodes import (
Designa, DesignaIndex, DesignaDestructure, SiStatement, DumStatement,
PerStatement, TemptaStatement, Defini, Redi, Erumpe, Continva,
ExpressionStatement, ID,
)
from centvrion.compiler.emit_expr import emit_expr
def emit_stmt(node, ctx):
"""
Emit C code for a CENTVRION statement node.
Returns lines — list of C statements.
"""
body = _emit_stmt_body(node, ctx)
pos = getattr(node, "pos", None)
if pos is not None:
return [f"_cent_current_line = {pos[0]};"] + body
return body
def _emit_stmt_body(node, ctx):
if isinstance(node, Designa):
val_lines, val_var = emit_expr(node.value, ctx)
return val_lines + [f'cent_scope_set(&_scope, "{node.id.name}", {val_var});']
if isinstance(node, DesignaIndex):
lines = []
idx_vars = []
for idx_expr in node.indices:
idx_lines, idx_var = emit_expr(idx_expr, ctx)
lines += idx_lines
idx_vars.append(idx_var)
val_lines, val_var = emit_expr(node.value, ctx)
lines += val_lines
root_tmp = ctx.fresh_tmp()
lines.append(f'CentValue {root_tmp} = cent_scope_get(&_scope, "{node.id.name}");')
if len(idx_vars) == 1:
lines.append(f"cent_list_index_set(&{root_tmp}, {idx_vars[0]}, {val_var});")
else:
# Walk down to collect intermediate containers
container_tmps = [root_tmp]
for idx_var in idx_vars[:-1]:
tmp = ctx.fresh_tmp()
lines.append(f"CentValue {tmp} = cent_list_index({container_tmps[-1]}, {idx_var});")
container_tmps.append(tmp)
# Set at deepest level
lines.append(f"cent_list_index_set(&{container_tmps[-1]}, {idx_vars[-1]}, {val_var});")
# Rebuild up the chain
for i in range(len(container_tmps) - 2, -1, -1):
lines.append(f"cent_list_index_set(&{container_tmps[i]}, {idx_vars[i]}, {container_tmps[i + 1]});")
lines.append(f'cent_scope_set(&_scope, "{node.id.name}", {root_tmp});')
return lines
if isinstance(node, DesignaDestructure):
n = len(node.ids)
val_lines, val_var = emit_expr(node.value, ctx)
lines = val_lines[:]
lines.append(f'if ({val_var}.type != CENT_LIST) cent_type_error("Cannot destructure non-array value");')
lines.append(f'if ({val_var}.lval.len != {n}) cent_runtime_error("Destructuring mismatch");')
for i, id_node in enumerate(node.ids):
tmp = ctx.fresh_tmp()
lines.append(f"CentValue {tmp} = cent_list_index({val_var}, cent_int({i + 1}));")
lines.append(f'cent_scope_set(&_scope, "{id_node.name}", {tmp});')
return lines
if isinstance(node, SiStatement):
cond_lines, cond_var = emit_expr(node.test, ctx)
then_lines = _emit_body(node.statements, ctx)
lines = cond_lines + [f"if (cent_truthy({cond_var})) {{"]
lines += [f" {l}" for l in then_lines]
if node.else_part:
else_lines = _emit_body(node.else_part, ctx)
lines += ["} else {"]
lines += [f" {l}" for l in else_lines]
lines += ["}"]
return lines
if isinstance(node, DumStatement):
# DVM loops UNTIL condition is true (inverted while)
lines = ["while (1) {"]
cond_lines, cond_var = emit_expr(node.test, ctx)
lines += [f" {l}" for l in cond_lines]
lines += [f" if (cent_truthy({cond_var})) break;"]
body_lines = _emit_body(node.statements, ctx)
lines += [f" {l}" for l in body_lines]
lines += ["}"]
return lines
if isinstance(node, PerStatement):
arr_lines, arr_var = emit_expr(node.data_list, ctx)
i_var = ctx.fresh_tmp()
body_lines = _emit_body(node.statements, ctx)
if node.destructure:
# Destructuring PER — each element must be a list
elem_var = ctx.fresh_tmp()
assign_lines = [
f"CentValue {elem_var} = {arr_var}.lval.items[{i_var}];",
f'if ({elem_var}.type != CENT_LIST) cent_type_error("Cannot destructure non-array value in PER loop");',
f'if ({elem_var}.lval.len != {len(node.variable_name)}) cent_runtime_error("Destructuring mismatch");',
]
for j, id_node in enumerate(node.variable_name):
tmp = ctx.fresh_tmp()
assign_lines.append(f"CentValue {tmp} = cent_list_index({elem_var}, cent_int({j + 1}));")
assign_lines.append(f'cent_scope_set(&_scope, "{id_node.name}", {tmp});')
lines = arr_lines + [
f'if ({arr_var}.type != CENT_LIST) cent_type_error("PER requires an array");',
f"for (int {i_var} = 0; {i_var} < {arr_var}.lval.len; {i_var}++) {{",
]
lines += [f" {l}" for l in assign_lines]
lines += [f" {l}" for l in body_lines]
lines += ["}"]
else:
var_name = node.variable_name.name
lines = arr_lines + [
f"if ({arr_var}.type == CENT_DICT) {{",
f" for (int {i_var} = 0; {i_var} < {arr_var}.dval.len; {i_var}++) {{",
f' cent_scope_set(&_scope, "{var_name}", {arr_var}.dval.keys[{i_var}]);',
]
lines += [f" {l}" for l in body_lines]
lines += [
" }",
"} else {",
f' if ({arr_var}.type != CENT_LIST) cent_type_error("PER requires an array or dict");',
f" for (int {i_var} = 0; {i_var} < {arr_var}.lval.len; {i_var}++) {{",
f' cent_scope_set(&_scope, "{var_name}", {arr_var}.lval.items[{i_var}]);',
]
lines += [f" {l}" for l in body_lines]
lines += [" }", "}"]
return lines
if isinstance(node, Defini):
# Top-level definitions are handled by emitter.py (hoisted + scope-set).
# Nested definitions (inside another function) need runtime scope-set.
if ctx.current_function is not None:
name = node.name.name
c_name = ctx.func_resolve[name]
param_names = ctx.functions[c_name]
pn_var = ctx.fresh_tmp() + "_pn"
return [
f"static const char *{pn_var}[] = {{"
+ ", ".join(f'"{p}"' for p in param_names)
+ "};",
f'cent_scope_set(&_scope, "{name}", '
f"cent_func_val({c_name}, {pn_var}, {len(param_names)}));",
]
return []
if isinstance(node, Redi):
lines = []
val_vars = []
for v in node.values:
v_lines, v_var = emit_expr(v, ctx)
lines.extend(v_lines)
val_vars.append(v_var)
if len(val_vars) == 1:
lines.append(f"_return_val = {val_vars[0]};")
else:
# multiple return values → pack into a list
lst_tmp = ctx.fresh_tmp()
lines.append(f"CentValue {lst_tmp} = cent_list_new({len(val_vars)});")
for vv in val_vars:
lines.append(f"cent_list_push(&{lst_tmp}, {vv});")
lines.append(f"_return_val = {lst_tmp};")
lines.append("goto _func_return;")
return lines
if isinstance(node, Erumpe):
return ["break;"]
if isinstance(node, Continva):
return ["continue;"]
if isinstance(node, TemptaStatement):
lines = [
"_cent_try_depth++;",
"if (setjmp(_cent_try_stack[_cent_try_depth - 1]) == 0) {",
]
try_lines = _emit_body(node.try_statements, ctx)
lines += [f" {l}" for l in try_lines]
lines += [
" _cent_try_depth--;",
"} else {",
" _cent_try_depth--;",
f' cent_scope_set(&_scope, "{node.error_var.name}", cent_str(_cent_error_msg));',
]
catch_lines = _emit_body(node.catch_statements, ctx)
lines += [f" {l}" for l in catch_lines]
lines += ["}"]
return lines
if isinstance(node, ExpressionStatement):
lines, _ = emit_expr(node.expression, ctx)
return lines
raise NotImplementedError(type(node).__name__)
def _emit_body(stmts, ctx):
lines = []
for s in stmts:
lines.extend(emit_stmt(s, ctx))
return lines