feat(charka): PICTURE de edición — Z, coma de millares y punto decimal

El formateo de informes de COBOL: supresión de ceros a la izquierda,
coma de millares e inserción del punto decimal. Rebanada vertical.

- charka-lexer: el punto separador exige un espacio detrás; un punto
  pegado a un carácter (ZZ9.99) ya no es terminador, sino símbolo —
  el parser lo reensambla dentro de la cláusula PICTURE.
- charka-runtime: format_edited(valor, pic) — 9, Z, coma, punto, B.
- charka-ir: Field::edit guarda la PICTURE; el campo es texto.
- charka-codegen / charka-shadow: MOVE a un campo de edición pasa por
  format_edited antes de almacenar.
- Corpus: 19-reporte. Sombra y crate compilado dan la misma salida.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-21 23:00:15 +00:00
parent b3278bdb0c
commit 634a43006a
15 changed files with 264 additions and 23 deletions
@@ -11,7 +11,7 @@ use charka_ir::{
BinOp, CmpOp, Cond, ConditionName, Expr, Figurative, FileMode, InspectOp, Ir, Operand, Perform,
PerformControl, PerformTarget, Stmt, WhenTest,
};
use charka_runtime::{cobol_text_cmp, CobFile, Decimal, Num, Rounding, Text};
use charka_runtime::{cobol_text_cmp, format_edited, CobFile, Decimal, Num, Rounding, Text};
use crate::field::{build_fields, Cell};
@@ -531,6 +531,15 @@ impl<'a> Machine<'a> {
/// `MOVE from` a un solo destino (escalar o elemento de tabla).
fn do_move(&mut self, from: &Operand, target: &Operand) {
// Un destino con PICTURE de edición formatea el valor numérico.
if let Operand::Data(name) = target {
if let Some(pic) = self.ir.model.field(name).and_then(|f| f.edit.clone()) {
let value = self.eval_decimal(from);
let text = format_edited(value, &pic);
self.store_text(target, &text);
return;
}
}
let Some((key, idx)) = self.resolve(target) else {
return;
};