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:
sergio
2026-05-21 22:17:47 +00:00
parent 47c49acd47
commit 2728698f5e
13 changed files with 205 additions and 18 deletions
@@ -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("");