tawasuyu

tawasuyu/brahman

Rust 321TOML 122JSON 14Markdown 114332 Definitions

⎇ e52b3fb572cec07d35c55d87519ecc8e0bd0505a crates/modules/charka/charka-codegen/src/expr.rs Rust · 170 lines · 5.9 KB History Who touched what raw

feat(charka): charka-codegen — emisión de Rust desde el IR e52b3fb57 · sergio ·

1//! Emisión de expresiones y condiciones: cada nodo del IR se convierte
2//! en un fragmento de código Rust (un `String`).
3
4use charka_ir::{BinOp, CmpOp, Cond, Expr, Figurative, Operand};
5
6use crate::sym::{FieldKind, Symbols};
7
8/// Un literal de texto Rust con las comillas y los escapes adecuados.
9pub(crate) fn rust_str(s: &str) -> String {
10    let mut out = String::from("\"");
11    for c in s.chars() {
12        match c {
13            '"' => out.push_str("\\\""),
14            '\\' => out.push_str("\\\\"),
15            '\n' => out.push_str("\\n"),
16            '\r' => out.push_str("\\r"),
17            '\t' => out.push_str("\\t"),
18            _ => out.push(c),
19        }
20    }
21    out.push('"');
22    out
23}
24
25/// El texto que representa una constante figurativa.
26pub(crate) fn figurative_text(f: Figurative) -> &'static str {
27    match f {
28        Figurative::Zero => "0",
29        Figurative::Space => " ",
30        Figurative::Quote => "\"",
31        Figurative::HighValue | Figurative::LowValue | Figurative::Null => "",
32    }
33}
34
35/// El carácter de relleno de una figurativa, para `Text::fill`.
36pub(crate) fn figurative_fill(f: Figurative) -> char {
37    match f {
38        Figurative::Zero => '0',
39        Figurative::Quote => '"',
40        _ => ' ',
41    }
42}
43
44/// Un operando como expresión de tipo `Decimal`.
45pub(crate) fn operand_decimal(sym: &Symbols, op: &Operand) -> String {
46    match op {
47        Operand::Num(n) => format!("dec({})", rust_str(n)),
48        Operand::Str(s) => format!(
49            "Decimal::parse({}).unwrap_or_else(|_| Decimal::zero())",
50            rust_str(s)
51        ),
52        Operand::Figurative(_) => "Decimal::zero()".to_string(),
53        Operand::Data(name) => match sym.lookup(name) {
54            Some(f) => match f.kind {
55                FieldKind::Num { .. } => format!("self.{}.value()", f.ident),
56                FieldKind::Text { .. } => format!(
57                    "Decimal::parse(self.{}.display().trim()).unwrap_or_else(|_| Decimal::zero())",
58                    f.ident
59                ),
60            },
61            None => format!("Decimal::zero() /* charka: dato no resuelto {name} */"),
62        },
63    }
64}
65
66/// Un operando como expresión de tipo `&str` (para texto).
67pub(crate) fn operand_str(sym: &Symbols, op: &Operand) -> String {
68    match op {
69        Operand::Str(s) => rust_str(s),
70        Operand::Num(n) => rust_str(n),
71        Operand::Figurative(f) => rust_str(figurative_text(*f)),
72        Operand::Data(name) => match sym.lookup(name) {
73            Some(f) => format!("self.{}.display().as_str()", f.ident),
74            None => format!("\"\" /* charka: dato no resuelto {name} */"),
75        },
76    }
77}
78
79/// Un operando como expresión que implementa `Display` (para `DISPLAY`).
80pub(crate) fn operand_display(sym: &Symbols, op: &Operand) -> String {
81    match op {
82        Operand::Str(s) => rust_str(s),
83        Operand::Num(n) => rust_str(n),
84        Operand::Figurative(f) => rust_str(figurative_text(*f)),
85        Operand::Data(name) => match sym.lookup(name) {
86            Some(f) => format!("self.{}.display()", f.ident),
87            None => format!("\"\" /* charka: dato no resuelto {name} */"),
88        },
89    }
90}
91
92/// Emite una expresión aritmética como código Rust de tipo `Decimal`.
93pub(crate) fn emit_expr(sym: &Symbols, e: &Expr) -> String {
94    match e {
95        Expr::Operand(op) => operand_decimal(sym, op),
96        Expr::Neg(inner) => format!("Decimal::zero().sub(&({}))", emit_expr(sym, inner)),
97        Expr::Binary { op, lhs, rhs } => {
98            let l = emit_expr(sym, lhs);
99            let r = emit_expr(sym, rhs);
100            match op {
101                BinOp::Add => format!("({l}).add(&({r}))"),
102                BinOp::Sub => format!("({l}).sub(&({r}))"),
103                BinOp::Mul => format!("({l}).mul(&({r}))"),
104                BinOp::Div => format!(
105                    "({l}).div(&({r}), 9, Rounding::Truncate).unwrap_or_else(|_| Decimal::zero())"
106                ),
107                BinOp::Pow => "Decimal::zero() /* charka: ** no soportado */".to_string(),
108            }
109        }
110    }
111}
112
113/// Emite una condición como código Rust de tipo `bool`.
114pub(crate) fn emit_cond(sym: &Symbols, c: &Cond) -> String {
115    match c {
116        Cond::Compare { lhs, op, rhs } => emit_compare(sym, lhs, *op, rhs),
117        Cond::Named(name) => {
118            format!("false /* charka: condición 88 no soportada: {name} */")
119        }
120        Cond::Not(inner) => format!("!({})", emit_cond(sym, inner)),
121        Cond::And(a, b) => format!("({}) && ({})", emit_cond(sym, a), emit_cond(sym, b)),
122        Cond::Or(a, b) => format!("({}) || ({})", emit_cond(sym, a), emit_cond(sym, b)),
123    }
124}
125
126/// Emite una comparación: numérica si ambos lados lo son, alfanumérica
127/// si alguno es texto.
128fn emit_compare(sym: &Symbols, lhs: &Operand, op: CmpOp, rhs: &Operand) -> String {
129    if is_text_operand(sym, lhs) || is_text_operand(sym, rhs) {
130        let method = match op {
131            CmpOp::Eq => "is_eq",
132            CmpOp::Ne => "is_ne",
133            CmpOp::Lt => "is_lt",
134            CmpOp::Gt => "is_gt",
135            CmpOp::Le => "is_le",
136            CmpOp::Ge => "is_ge",
137        };
138        format!(
139            "cobol_text_cmp({}, {}).{method}()",
140            operand_str(sym, lhs),
141            operand_str(sym, rhs)
142        )
143    } else {
144        let rust_op = match op {
145            CmpOp::Eq => "==",
146            CmpOp::Ne => "!=",
147            CmpOp::Lt => "<",
148            CmpOp::Gt => ">",
149            CmpOp::Le => "<=",
150            CmpOp::Ge => ">=",
151        };
152        format!(
153            "({}) {rust_op} ({})",
154            operand_decimal(sym, lhs),
155            operand_decimal(sym, rhs)
156        )
157    }
158}
159
160/// ¿El operando es alfanumérico?
161fn is_text_operand(sym: &Symbols, op: &Operand) -> bool {
162    match op {
163        Operand::Str(_) => true,
164        Operand::Data(name) => matches!(
165            sym.lookup(name).map(|f| &f.kind),
166            Some(FieldKind::Text { .. })
167        ),
168        _ => false,
169    }
170}