tawasuyu

tawasuyu/brahman

Rust 296TOML 105JSON 14Markdown 73760 Definiciones

⎇ 321e6f8e27aea06b5a868b37598148e15bf71f57 crates/modules/charka/charka-codegen/src/stmt.rs Rust · 379 líneas · 11.8 KB Historia Quién tocó qué crudo

feat(charka): PERFORM VARYING — el bucle con variable de control 321e6f8e2 · sergio ·

1//! Emisión de los statements del PROCEDURE: cada [`Stmt`] se traduce a
2//! una o varias líneas de código Rust sobre `charka-runtime`.
3
4use charka_ir::{Operand, Perform, PerformControl, PerformTarget, Stmt};
5
6use crate::emit::Emitter;
7use crate::expr::{
8    emit_cond, emit_expr, figurative_fill, operand_decimal, operand_display, operand_str,
9};
10use crate::sym::{paragraph_method, FieldKind, Symbols};
11
12/// Emite un statement.
13pub(crate) fn emit_stmt(em: &mut Emitter, sym: &Symbols, stmt: &Stmt) {
14    match stmt {
15        Stmt::Move { from, to } => emit_move(em, sym, from, to),
16        Stmt::Display { items } => emit_display(em, sym, items),
17        Stmt::Accept { into } => {
18            em.line(&format!(
19                "// charka: ACCEPT {into} — entrada interactiva no soportada en v1"
20            ));
21        }
22        Stmt::Compute {
23            targets,
24            rounded,
25            expr,
26        } => {
27            let value = emit_expr(sym, expr);
28            for t in targets {
29                emit_store(em, sym, t, &value, *rounded);
30            }
31        }
32        Stmt::Add {
33            addends,
34            to,
35            giving,
36            rounded,
37        } => emit_add(em, sym, addends, to, giving, *rounded),
38        Stmt::Subtract {
39            amounts,
40            from,
41            giving,
42            rounded,
43        } => emit_subtract(em, sym, amounts, from, giving, *rounded),
44        Stmt::Multiply {
45            left,
46            by,
47            giving,
48            rounded,
49        } => emit_multiply(em, sym, left, by, giving, *rounded),
50        Stmt::Divide {
51            left,
52            right,
53            by_form,
54            giving,
55            rounded,
56        } => emit_divide(em, sym, left, right, *by_form, giving, *rounded),
57        Stmt::If {
58            cond,
59            then_branch,
60            else_branch,
61        } => {
62            em.line(&format!("if {} {{", emit_cond(sym, cond)));
63            em.indent();
64            emit_block(em, sym, then_branch);
65            em.dedent();
66            if else_branch.is_empty() {
67                em.line("}");
68            } else {
69                em.line("} else {");
70                em.indent();
71                emit_block(em, sym, else_branch);
72                em.dedent();
73                em.line("}");
74            }
75        }
76        Stmt::Perform(p) => emit_perform(em, sym, p),
77        Stmt::GoTo { target } => {
78            em.line(&format!(
79                "self.{}(); return; // charka: GO TO (aproximado)",
80                paragraph_method(target)
81            ));
82        }
83        Stmt::StopRun | Stmt::Goback => em.line("std::process::exit(0);"),
84        Stmt::Exit => em.line("return;"),
85        Stmt::Continue => em.line("// CONTINUE"),
86        Stmt::Unknown { verb, .. } => {
87            em.line(&format!("// charka: verbo no transpilado — {verb}"));
88        }
89    }
90}
91
92/// Emite una secuencia de statements (un cuerpo de bloque).
93fn emit_block(em: &mut Emitter, sym: &Symbols, stmts: &[Stmt]) {
94    for s in stmts {
95        emit_stmt(em, sym, s);
96    }
97}
98
99/// Almacena un valor `Decimal` (texto de expresión) en un campo.
100fn emit_store(em: &mut Emitter, sym: &Symbols, name: &str, value: &str, rounded: bool) {
101    match sym.lookup(name) {
102        Some(f) => match f.kind {
103            FieldKind::Num { .. } => {
104                let method = if rounded { "store_rounded" } else { "store" };
105                em.line(&format!("self.{}.{method}({value});", f.ident));
106            }
107            FieldKind::Text { .. } => {
108                em.line(&format!(
109                    "self.{}.store(({value}).to_string().as_str());",
110                    f.ident
111                ));
112            }
113        },
114        None => em.line(&format!("// charka: destino no resuelto — {name}")),
115    }
116}
117
118fn emit_move(em: &mut Emitter, sym: &Symbols, from: &Operand, to: &[String]) {
119    for t in to {
120        match sym.lookup(t) {
121            Some(f) => match f.kind {
122                FieldKind::Num { .. } => {
123                    em.line(&format!(
124                        "self.{}.store({});",
125                        f.ident,
126                        operand_decimal(sym, from)
127                    ));
128                }
129                FieldKind::Text { .. } => {
130                    if let Operand::Figurative(fig) = from {
131                        em.line(&format!(
132                            "self.{}.fill('{}');",
133                            f.ident,
134                            figurative_fill(*fig)
135                        ));
136                    } else {
137                        em.line(&format!(
138                            "self.{}.store({});",
139                            f.ident,
140                            operand_str(sym, from)
141                        ));
142                    }
143                }
144            },
145            None => em.line(&format!("// charka: destino MOVE no resuelto — {t}")),
146        }
147    }
148}
149
150fn emit_display(em: &mut Emitter, sym: &Symbols, items: &[Operand]) {
151    if items.is_empty() {
152        em.line("println!();");
153        return;
154    }
155    let placeholders = "{}".repeat(items.len());
156    let args: Vec<String> = items.iter().map(|o| operand_display(sym, o)).collect();
157    em.line(&format!(
158        "println!(\"{placeholders}\", {});",
159        args.join(", ")
160    ));
161}
162
163/// La suma de una lista de operandos, encadenando `.add`.
164fn fold_sum(sym: &Symbols, ops: &[Operand]) -> String {
165    let mut it = ops.iter();
166    let Some(first) = it.next() else {
167        return "Decimal::zero()".to_string();
168    };
169    let mut acc = operand_decimal(sym, first);
170    for o in it {
171        acc = format!("({acc}).add(&({}))", operand_decimal(sym, o));
172    }
173    acc
174}
175
176fn emit_add(
177    em: &mut Emitter,
178    sym: &Symbols,
179    addends: &[Operand],
180    to: &[String],
181    giving: &[String],
182    rounded: bool,
183) {
184    let sum = fold_sum(sym, addends);
185    if !giving.is_empty() {
186        let base = match to.first() {
187            Some(first) => format!(
188                "({sum}).add(&({}))",
189                operand_decimal(sym, &Operand::Data(first.clone()))
190            ),
191            None => sum,
192        };
193        for g in giving {
194            emit_store(em, sym, g, &base, rounded);
195        }
196    } else {
197        for t in to {
198            emit_inplace(em, sym, t, "add", &sum, rounded);
199        }
200    }
201}
202
203fn emit_subtract(
204    em: &mut Emitter,
205    sym: &Symbols,
206    amounts: &[Operand],
207    from: &[String],
208    giving: &[String],
209    rounded: bool,
210) {
211    let sum = fold_sum(sym, amounts);
212    if !giving.is_empty() {
213        let minuend = from
214            .first()
215            .map(|f| operand_decimal(sym, &Operand::Data(f.clone())))
216            .unwrap_or_else(|| "Decimal::zero()".to_string());
217        let value = format!("({minuend}).sub(&({sum}))");
218        for g in giving {
219            emit_store(em, sym, g, &value, rounded);
220        }
221    } else {
222        for t in from {
223            emit_inplace(em, sym, t, "sub", &sum, rounded);
224        }
225    }
226}
227
228fn emit_multiply(
229    em: &mut Emitter,
230    sym: &Symbols,
231    left: &Operand,
232    by: &Operand,
233    giving: &[String],
234    rounded: bool,
235) {
236    let l = operand_decimal(sym, left);
237    if !giving.is_empty() {
238        let value = format!("({l}).mul(&({}))", operand_decimal(sym, by));
239        for g in giving {
240            emit_store(em, sym, g, &value, rounded);
241        }
242    } else if let Operand::Data(name) = by {
243        // `MULTIPLY a BY b` sin GIVING: b queda con a*b.
244        emit_inplace(em, sym, name, "mul", &l, rounded);
245    } else {
246        em.line("// charka: MULTIPLY sin destino claro");
247    }
248}
249
250fn emit_divide(
251    em: &mut Emitter,
252    sym: &Symbols,
253    left: &Operand,
254    right: &Operand,
255    by_form: bool,
256    giving: &[String],
257    rounded: bool,
258) {
259    // `a BY b` → a/b; `a INTO b` → b/a.
260    let (num, den) = if by_form {
261        (operand_decimal(sym, left), operand_decimal(sym, right))
262    } else {
263        (operand_decimal(sym, right), operand_decimal(sym, left))
264    };
265    if !giving.is_empty() {
266        for g in giving {
267            let value = format!(
268                "({num}).div(&({den}), {}, Rounding::Truncate).unwrap_or_else(|_| Decimal::zero())",
269                target_scale(sym, g)
270            );
271            emit_store(em, sym, g, &value, rounded);
272        }
273    } else if let Operand::Data(name) = right {
274        // `DIVIDE a INTO b` sin GIVING: b queda con b/a.
275        let value = format!(
276            "({num}).div(&({den}), {}, Rounding::Truncate).unwrap_or_else(|_| Decimal::zero())",
277            target_scale(sym, name)
278        );
279        emit_store(em, sym, name, &value, rounded);
280    } else {
281        em.line("// charka: DIVIDE sin destino claro");
282    }
283}
284
285/// Emite una operación aritmética en el lugar: `t = t <op> rhs`.
286fn emit_inplace(em: &mut Emitter, sym: &Symbols, name: &str, op: &str, rhs: &str, rounded: bool) {
287    match sym.lookup(name) {
288        Some(f) if matches!(f.kind, FieldKind::Num { .. }) => {
289            let method = if rounded { "store_rounded" } else { "store" };
290            em.line(&format!(
291                "self.{0}.{method}(self.{0}.value().{op}(&({rhs})));",
292                f.ident
293            ));
294        }
295        _ => em.line(&format!(
296            "// charka: destino aritmético no resuelto — {name}"
297        )),
298    }
299}
300
301/// La escala de redondeo de un destino numérico (sus dígitos
302/// fraccionarios), o 4 por defecto.
303fn target_scale(sym: &Symbols, name: &str) -> u8 {
304    match sym.lookup(name).map(|f| &f.kind) {
305        Some(FieldKind::Num { frac, .. }) => *frac,
306        _ => 4,
307    }
308}
309
310/// Una expresión `usize` para el número de repeticiones de un `PERFORM`.
311fn count_expr(sym: &Symbols, op: &Operand) -> String {
312    match op {
313        Operand::Num(n) => match n.trim_start_matches('+').parse::<i128>() {
314            Ok(v) if v >= 0 => format!("{v}usize"),
315            _ => "0usize".to_string(),
316        },
317        _ => format!(
318            "(({}).rescale(0, Rounding::Truncate).mantissa().max(0) as usize)",
319            operand_decimal(sym, op)
320        ),
321    }
322}
323
324fn emit_perform(em: &mut Emitter, sym: &Symbols, p: &Perform) {
325    // Emite el "cuerpo": la llamada al párrafo o el bloque en línea.
326    let emit_body = |em: &mut Emitter, sym: &Symbols| match &p.target {
327        PerformTarget::Paragraph { name, thru } => {
328            let note = thru
329                .as_ref()
330                .map(|t| format!(" // charka: THRU {t} — rango no soportado"))
331                .unwrap_or_default();
332            em.line(&format!("self.{}();{note}", paragraph_method(name)));
333        }
334        PerformTarget::Inline(body) => emit_block(em, sym, body),
335    };
336
337    match &p.control {
338        PerformControl::Once => {
339            if matches!(p.target, PerformTarget::Inline(_)) {
340                em.line("{");
341                em.indent();
342                emit_body(em, sym);
343                em.dedent();
344                em.line("}");
345            } else {
346                emit_body(em, sym);
347            }
348        }
349        PerformControl::Times(n) => {
350            em.line(&format!("for _ in 0..{} {{", count_expr(sym, n)));
351            em.indent();
352            emit_body(em, sym);
353            em.dedent();
354            em.line("}");
355        }
356        PerformControl::Until(cond) => {
357            em.line(&format!("while !({}) {{", emit_cond(sym, cond)));
358            em.indent();
359            emit_body(em, sym);
360            em.dedent();
361            em.line("}");
362        }
363        PerformControl::Varying {
364            var,
365            from,
366            by,
367            until,
368        } => {
369            // var = from; mientras no se cumpla `until`: cuerpo; var += by.
370            emit_store(em, sym, var, &operand_decimal(sym, from), false);
371            em.line(&format!("while !({}) {{", emit_cond(sym, until)));
372            em.indent();
373            emit_body(em, sym);
374            emit_inplace(em, sym, var, "add", &operand_decimal(sym, by), false);
375            em.dedent();
376            em.line("}");
377        }
378    }
379}