feat(charka): EVALUATE TRUE y rangos WHEN ... THRU
Completa el EVALUATE con sus dos formas que faltaban. - IR: la rama WhenBranch pasa de values: Vec<Operand> a tests: Vec<WhenTest>, donde WhenTest es Value (igualdad), Range (WHEN lo THRU hi) o Cond (EVALUATE TRUE WHEN cond). - Parser: detecta EVALUATE TRUE y entonces cada WHEN parsea una condición; en modo valor reconoce WHEN lo THRU hi. - Codegen y shadow: una prueba Range se traduce a lo <= s <= hi; una Cond, a la condición directa. - Corpus: programa nuevo 14-clasifica (clasifica notas con rangos THRU y un EVALUATE TRUE). Verificado: intérprete sombra y crate compilado dan la misma salida. Tests: charka-ir 27, charka-codegen 21, charka-shadow 19. fmt + clippy limpios. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -399,6 +399,20 @@ mod tests {
|
||||
assert!(out.contains(".replace("));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn evaluate_true_and_range_emit() {
|
||||
let out = gen("DATA DIVISION.\n\
|
||||
WORKING-STORAGE SECTION.\n\
|
||||
01 WS-X PIC 9(3).\n\
|
||||
PROCEDURE DIVISION.\n\
|
||||
MAIN.\n\
|
||||
EVALUATE WS-X WHEN 1 THRU 9 DISPLAY 'R' END-EVALUATE.\n\
|
||||
EVALUATE TRUE WHEN WS-X > 5 DISPLAY 'T' END-EVALUATE.\n");
|
||||
assert!(out.contains(">= (dec(\"1\"))"));
|
||||
assert!(out.contains("<= (dec(\"9\"))"));
|
||||
assert!(out.contains("> (dec(\"5\"))"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_program_still_compiles_shape() {
|
||||
let out = gen("");
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use charka_ir::{
|
||||
CmpOp, Cond, InspectOp, Operand, Perform, PerformControl, PerformTarget, Stmt, WhenBranch,
|
||||
WhenTest,
|
||||
};
|
||||
|
||||
use crate::emit::Emitter;
|
||||
@@ -349,32 +350,45 @@ fn emit_evaluate(
|
||||
}
|
||||
}
|
||||
|
||||
/// La condición de una rama `WHEN`: el sujeto igual a cualquiera de
|
||||
/// sus valores.
|
||||
/// La condición de una rama `WHEN`: pasa si **alguna** de sus pruebas
|
||||
/// se cumple.
|
||||
fn branch_condition(sym: &Symbols, subject: &Operand, branch: &WhenBranch) -> String {
|
||||
if branch.values.is_empty() {
|
||||
if branch.tests.is_empty() {
|
||||
return "false".to_string();
|
||||
}
|
||||
branch
|
||||
.values
|
||||
.tests
|
||||
.iter()
|
||||
.map(|v| {
|
||||
format!(
|
||||
"({})",
|
||||
emit_cond(
|
||||
sym,
|
||||
&Cond::Compare {
|
||||
lhs: subject.clone(),
|
||||
op: CmpOp::Eq,
|
||||
rhs: v.clone(),
|
||||
},
|
||||
)
|
||||
)
|
||||
})
|
||||
.map(|t| format!("({})", test_condition(sym, subject, t)))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" || ")
|
||||
}
|
||||
|
||||
/// Traduce una prueba `WHEN` a una expresión Rust de tipo `bool`.
|
||||
fn test_condition(sym: &Symbols, subject: &Operand, test: &WhenTest) -> String {
|
||||
let compare = |op: CmpOp, rhs: &Operand| {
|
||||
emit_cond(
|
||||
sym,
|
||||
&Cond::Compare {
|
||||
lhs: subject.clone(),
|
||||
op,
|
||||
rhs: rhs.clone(),
|
||||
},
|
||||
)
|
||||
};
|
||||
match test {
|
||||
WhenTest::Value(v) => compare(CmpOp::Eq, v),
|
||||
WhenTest::Range(lo, hi) => {
|
||||
format!(
|
||||
"({}) && ({})",
|
||||
compare(CmpOp::Ge, lo),
|
||||
compare(CmpOp::Le, hi)
|
||||
)
|
||||
}
|
||||
WhenTest::Cond(cond) => emit_cond(sym, cond),
|
||||
}
|
||||
}
|
||||
|
||||
/// Almacena una expresión `&str` en un destino: directo si es de
|
||||
/// texto, parseado a `Decimal` si es numérico.
|
||||
fn emit_store_text(em: &mut Emitter, sym: &Symbols, target: &Operand, text: &str) {
|
||||
|
||||
Reference in New Issue
Block a user