159 lines
5.4 KiB
Python
159 lines
5.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.
|
|
"""
|
|
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):
|
|
idx_lines, idx_var = emit_expr(node.index, ctx)
|
|
val_lines, val_var = emit_expr(node.value, ctx)
|
|
arr_tmp = ctx.fresh_tmp()
|
|
return (
|
|
idx_lines + val_lines + [
|
|
f'CentValue {arr_tmp} = cent_scope_get(&_scope, "{node.id.name}");',
|
|
f"cent_list_index_set(&{arr_tmp}, {idx_var}, {val_var});",
|
|
f'cent_scope_set(&_scope, "{node.id.name}", {arr_tmp});',
|
|
]
|
|
)
|
|
|
|
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()
|
|
var_name = node.variable_name.name
|
|
body_lines = _emit_body(node.statements, ctx)
|
|
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
|