feat(charka): INSPECT — contar y reemplazar caracteres
El verbo de COBOL para analizar y limpiar campos de texto.
- IR: Stmt::Inspect { target, op } con InspectOp::TallyingForAll
(cuenta apariciones y las suma a un contador) y
InspectOp::ReplacingAll (reemplaza apariciones).
- Parser: INSPECT t TALLYING n FOR ALL lit y
INSPECT t REPLACING ALL a BY b. Una forma no soportada cae a
Stmt::Unknown.
- Codegen: TALLYING -> str::matches(..).count(); REPLACING ->
str::replace.
- Shadow: el intérprete cuenta / reemplaza el texto.
- Corpus: programa nuevo 13-inspeccion. Verificado: el intérprete
sombra y el crate compilado por scaffold dan la misma salida.
Alcance v1: TALLYING FOR ALL y REPLACING ALL; sin LEADING, FIRST,
CHARACTERS, BEFORE/AFTER.
Tests: charka-ir 26, charka-codegen 20, charka-shadow 18. fmt +
clippy limpios.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -280,8 +280,8 @@ mod tests {
|
||||
fn unknown_verb_becomes_a_comment() {
|
||||
let out = gen("PROCEDURE DIVISION.\n\
|
||||
MAIN.\n\
|
||||
INSPECT WS-X TALLYING WS-N FOR ALL ' '.\n");
|
||||
assert!(out.contains("// charka: verbo no transpilado — INSPECT"));
|
||||
INITIALIZE WS-X.\n");
|
||||
assert!(out.contains("// charka: verbo no transpilado — INITIALIZE"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -384,6 +384,21 @@ mod tests {
|
||||
assert!(out.contains("__it.next().unwrap_or(\"\")"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inspect_emits_tally_and_replace() {
|
||||
let out = gen("DATA DIVISION.\n\
|
||||
WORKING-STORAGE SECTION.\n\
|
||||
01 WS-T PIC X(10).\n\
|
||||
01 WS-N PIC 9(3).\n\
|
||||
PROCEDURE DIVISION.\n\
|
||||
MAIN.\n\
|
||||
INSPECT WS-T TALLYING WS-N FOR ALL 'X'.\n\
|
||||
INSPECT WS-T REPLACING ALL 'X' BY 'Y'.\n");
|
||||
assert!(out.contains(".matches("));
|
||||
assert!(out.contains("Decimal::from_integer(__n)"));
|
||||
assert!(out.contains(".replace("));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_program_still_compiles_shape() {
|
||||
let out = gen("");
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
//! Emisión de los statements del PROCEDURE: cada [`Stmt`] se traduce a
|
||||
//! una o varias líneas de código Rust sobre `charka-runtime`.
|
||||
|
||||
use charka_ir::{CmpOp, Cond, Operand, Perform, PerformControl, PerformTarget, Stmt, WhenBranch};
|
||||
use charka_ir::{
|
||||
CmpOp, Cond, InspectOp, Operand, Perform, PerformControl, PerformTarget, Stmt, WhenBranch,
|
||||
};
|
||||
|
||||
use crate::emit::Emitter;
|
||||
use crate::expr::{
|
||||
@@ -82,6 +84,7 @@ pub(crate) fn emit_stmt(em: &mut Emitter, sym: &Symbols, stmt: &Stmt) {
|
||||
delimiter,
|
||||
into,
|
||||
} => emit_unstring(em, sym, source, delimiter, into),
|
||||
Stmt::Inspect { target, op } => emit_inspect(em, sym, target, op),
|
||||
Stmt::Perform(p) => emit_perform(em, sym, p),
|
||||
Stmt::GoTo { target } => {
|
||||
em.line(&format!(
|
||||
@@ -423,6 +426,38 @@ fn emit_unstring(
|
||||
em.line("}");
|
||||
}
|
||||
|
||||
/// `INSPECT` — cuenta (`TALLYING`) o reemplaza (`REPLACING`).
|
||||
fn emit_inspect(em: &mut Emitter, sym: &Symbols, target: &Operand, op: &InspectOp) {
|
||||
match op {
|
||||
InspectOp::TallyingForAll { counter, search } => {
|
||||
em.line("{");
|
||||
em.indent();
|
||||
em.line(&format!(
|
||||
"let __n = ({}).matches({}).count() as i128;",
|
||||
operand_display(sym, target),
|
||||
operand_str(sym, search)
|
||||
));
|
||||
match field_ref(sym, counter) {
|
||||
Some((lref, FieldKind::Num { .. })) => em.line(&format!(
|
||||
"{lref}.store({lref}.value().add(&Decimal::from_integer(__n)));"
|
||||
)),
|
||||
_ => em.line("// charka: contador INSPECT no resuelto"),
|
||||
}
|
||||
em.dedent();
|
||||
em.line("}");
|
||||
}
|
||||
InspectOp::ReplacingAll { from, to } => {
|
||||
let replaced = format!(
|
||||
"({}).replace({}, {})",
|
||||
operand_display(sym, target),
|
||||
operand_str(sym, from),
|
||||
operand_str(sym, to)
|
||||
);
|
||||
emit_store_text(em, sym, target, &format!("{replaced}.as_str()"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_perform(em: &mut Emitter, sym: &Symbols, p: &Perform) {
|
||||
// Emite el "cuerpo": la llamada al párrafo o el bloque en línea.
|
||||
let emit_body = |em: &mut Emitter, sym: &Symbols| match &p.target {
|
||||
|
||||
Reference in New Issue
Block a user