204 lines
7.4 KiB
Python
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
|