feat(charka): charka-runtime — soporte de ejecución (campos Num y Text)
El soporte que los programas COBOL transpilados enlazan. charka-codegen emitirá Rust que llama a esta biblioteca, no Rust autónomo. - Num: campo numérico (PIC 9(5)V99) — un Decimal conformado a su Picture. store trunca a la escala declarada, store_rounded redondea; al desbordar la parte entera conserva los dígitos de bajo orden (el ON SIZE ERROR de COBOL sin cláusula). display da los dígitos con relleno de ceros y signo. - Text: campo alfanumérico (PIC X(n)) de longitud fija — store justifica a la izquierda y rellena/trunca; fill mueve figurativas. - cobol_text_cmp: comparación alfanumérica con relleno de espacios. - Reexporta Decimal/Picture/Rounding de charka-bcd. Construido antes que charka-codegen (la nota de orden del plan los listaba al revés): el codegen emite contra esta API. 17 tests; fmt + clippy limpios. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Generated
+7
@@ -2338,6 +2338,13 @@ dependencies = [
|
|||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "charka-runtime"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"charka-bcd",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chasqui-card"
|
name = "chasqui-card"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ members = [
|
|||||||
"crates/modules/charka/charka-lexer",
|
"crates/modules/charka/charka-lexer",
|
||||||
"crates/modules/charka/charka-parser",
|
"crates/modules/charka/charka-parser",
|
||||||
"crates/modules/charka/charka-ir",
|
"crates/modules/charka/charka-ir",
|
||||||
|
"crates/modules/charka/charka-runtime",
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# modules/mirada/ — Compositor Wayland
|
# modules/mirada/ — Compositor Wayland
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ embebido, dialectos IBM Enterprise) es un esfuerzo multi-mes.
|
|||||||
| `charka-lexer` | lib | Tokenizador COBOL: formato fijo (tarjeta de 80 columnas) y libre |
|
| `charka-lexer` | lib | Tokenizador COBOL: formato fijo (tarjeta de 80 columnas) y libre |
|
||||||
| `charka-parser` | lib | Parser COBOL'85 (subconjunto): tokens → AST (`Program`) |
|
| `charka-parser` | lib | Parser COBOL'85 (subconjunto): tokens → AST (`Program`) |
|
||||||
| `charka-ir` | lib | Representación intermedia: el AST con los statements del PROCEDURE ya tipados |
|
| `charka-ir` | lib | Representación intermedia: el AST con los statements del PROCEDURE ya tipados |
|
||||||
|
| `charka-runtime` | lib | Soporte de ejecución de los programas transpilados: campos `Num` y `Text` |
|
||||||
|
|
||||||
## charka-bcd
|
## charka-bcd
|
||||||
|
|
||||||
@@ -89,16 +90,39 @@ Tercera etapa: `Program` → `Ir`. Aquí se parsea cada `Sentence` cruda
|
|||||||
- Fuera de alcance v1: `EVALUATE`, `STRING`/`UNSTRING`, E/S de
|
- Fuera de alcance v1: `EVALUATE`, `STRING`/`UNSTRING`, E/S de
|
||||||
ficheros, `PERFORM VARYING`, CICS, SQL embebido.
|
ficheros, `PERFORM VARYING`, CICS, SQL embebido.
|
||||||
|
|
||||||
|
## charka-runtime
|
||||||
|
|
||||||
|
El soporte de ejecución: lo que `charka-codegen` emite es Rust que
|
||||||
|
enlaza contra este crate. Da a un programa transpilado la semántica de
|
||||||
|
COBOL en tiempo de ejecución.
|
||||||
|
|
||||||
|
- `Num` — campo numérico (`PIC 9(5)V99`): un `Decimal` conformado a su
|
||||||
|
`Picture`. `store`/`store_rounded` truncan o redondean a la escala
|
||||||
|
declarada; al desbordar conservan los dígitos de bajo orden (el
|
||||||
|
`ON SIZE ERROR` sin cláusula). `display` da los dígitos con relleno
|
||||||
|
de ceros.
|
||||||
|
- `Text` — campo alfanumérico (`PIC X(20)`) de longitud fija: `store`
|
||||||
|
justifica a la izquierda y rellena/trunca; `fill` mueve figurativas
|
||||||
|
(`SPACES`, `ZEROS`).
|
||||||
|
- `cobol_text_cmp` — comparación alfanumérica con relleno de espacios.
|
||||||
|
- Reexporta `Decimal`/`Picture`/`Rounding` de `charka-bcd` para que el
|
||||||
|
código generado sólo necesite `use charka_runtime::*;`.
|
||||||
|
|
||||||
|
Construido **antes** que `charka-codegen` (la nota de orden del plan
|
||||||
|
los listaba al revés): el codegen emite llamadas contra esta API, así
|
||||||
|
que el runtime debe existir primero — y es un crate autocontenido,
|
||||||
|
verificable sin depender del código emitido.
|
||||||
|
|
||||||
## Estado
|
## Estado
|
||||||
|
|
||||||
`charka-bcd` (22 tests), `charka-lexer` (17 tests), `charka-parser`
|
`charka-bcd` (22 tests), `charka-lexer` (17 tests), `charka-parser`
|
||||||
(15 tests) y `charka-ir` (17 tests) implementados y verdes.
|
(15 tests), `charka-ir` (17 tests) y `charka-runtime` (17 tests)
|
||||||
**Pendiente** — el resto del transpilador (Fase D del plan macro):
|
implementados y verdes. **Pendiente** — el resto del transpilador
|
||||||
|
(Fase D del plan macro):
|
||||||
|
|
||||||
| crate pendiente | rol |
|
| crate pendiente | rol |
|
||||||
| ----------------- | ---------------------------------------------------- |
|
| ----------------- | ---------------------------------------------------- |
|
||||||
| `charka-codegen` | emisión de Rust |
|
| `charka-codegen` | emisión de Rust (IR → fuente Rust sobre el runtime) |
|
||||||
| `charka-shadow` | validador en sombra (original vs transpilado) |
|
| `charka-shadow` | validador en sombra (original vs transpilado) |
|
||||||
| `charka-runtime` | runtime determinista (sobre `charka-bcd`) |
|
|
||||||
|
|
||||||
Hito intermedio sugerido: subconjunto COBOL'85 puro antes de CICS/SQL.
|
Hito intermedio sugerido: subconjunto COBOL'85 puro antes de CICS/SQL.
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "charka-runtime"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
description = "charka-runtime — soporte de ejecución de los programas COBOL transpilados: campos numéricos (Num) y alfanuméricos (Text) con la semántica de COBOL."
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
charka-bcd = { path = "../charka-bcd" }
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
//! `charka-runtime` — el soporte de ejecución de los programas COBOL
|
||||||
|
//! transpilados.
|
||||||
|
//!
|
||||||
|
//! Lo que `charka-codegen` emite no es Rust autónomo: es Rust que
|
||||||
|
//! enlaza contra esta biblioteca. Aquí viven los tipos que dan a un
|
||||||
|
//! programa transpilado la semántica de COBOL en tiempo de ejecución:
|
||||||
|
//!
|
||||||
|
//! - [`Num`] — un campo numérico (`PIC 9(5)V99`): un [`Decimal`] de
|
||||||
|
//! punto fijo conformado a su [`Picture`]. Toda asignación trunca a
|
||||||
|
//! la escala y al tamaño declarados, como el `MOVE` de COBOL.
|
||||||
|
//! - [`Text`] — un campo alfanumérico (`PIC X(20)`) de longitud fija:
|
||||||
|
//! toda asignación justifica a la izquierda y rellena o trunca.
|
||||||
|
//!
|
||||||
|
//! La aritmética decimal exacta la aporta `charka-bcd`, cuyos tipos
|
||||||
|
//! ([`Decimal`], [`Picture`], [`Rounding`]) se reexportan para que el
|
||||||
|
//! código generado sólo necesite `use charka_runtime::*;`.
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
|
mod num;
|
||||||
|
mod text;
|
||||||
|
|
||||||
|
pub use charka_bcd::{Decimal, Picture, Rounding};
|
||||||
|
pub use num::Num;
|
||||||
|
pub use text::Text;
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
/// Compara dos campos alfanuméricos con la semántica de COBOL: el más
|
||||||
|
/// corto se considera rellenado con espacios a la derecha, de modo que
|
||||||
|
/// `"AB"` y `"AB "` son iguales.
|
||||||
|
pub fn cobol_text_cmp(a: &str, b: &str) -> Ordering {
|
||||||
|
let n = a.chars().count().max(b.chars().count());
|
||||||
|
let padded = |s: &str| -> Vec<char> {
|
||||||
|
let mut v: Vec<char> = s.chars().collect();
|
||||||
|
while v.len() < n {
|
||||||
|
v.push(' ');
|
||||||
|
}
|
||||||
|
v
|
||||||
|
};
|
||||||
|
padded(a).cmp(&padded(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn text_cmp_orders_lexically() {
|
||||||
|
assert_eq!(cobol_text_cmp("ABC", "ABD"), Ordering::Less);
|
||||||
|
assert_eq!(cobol_text_cmp("ABD", "ABC"), Ordering::Greater);
|
||||||
|
assert_eq!(cobol_text_cmp("ABC", "ABC"), Ordering::Equal);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn text_cmp_pads_the_shorter_with_spaces() {
|
||||||
|
assert_eq!(cobol_text_cmp("AB", "AB "), Ordering::Equal);
|
||||||
|
assert_eq!(cobol_text_cmp("AB", "ABC"), Ordering::Less);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fields_compose_for_generated_code() {
|
||||||
|
// Un mini-programa transpilado a mano: WS-CT crece, WS-MSG fijo.
|
||||||
|
let mut ws_ct = Num::with_value(Picture::new(3, 0, false), "0");
|
||||||
|
ws_ct.store(ws_ct.value().add(&Decimal::from_integer(5)));
|
||||||
|
assert_eq!(ws_ct.display(), "005");
|
||||||
|
|
||||||
|
let mut ws_msg = Text::new(10);
|
||||||
|
ws_msg.store("LISTO");
|
||||||
|
assert_eq!(ws_msg.as_str(), "LISTO ");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
//! `Num` — un campo numérico COBOL en tiempo de ejecución.
|
||||||
|
|
||||||
|
use charka_bcd::{Decimal, Picture, Rounding};
|
||||||
|
|
||||||
|
/// Un campo numérico: un valor [`Decimal`] más la [`Picture`] que lo
|
||||||
|
/// conforma. Toda asignación pasa por la PICTURE — ese es el `MOVE` de
|
||||||
|
/// COBOL: el valor se ajusta a la escala y al tamaño declarados.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct Num {
|
||||||
|
value: Decimal,
|
||||||
|
pic: Picture,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Num {
|
||||||
|
/// Campo nuevo en cero, con la PICTURE dada.
|
||||||
|
pub fn new(pic: Picture) -> Self {
|
||||||
|
Self {
|
||||||
|
value: Decimal::zero(),
|
||||||
|
pic,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Campo con un `VALUE` inicial (el texto del literal). Un literal
|
||||||
|
/// inválido deja el campo en cero.
|
||||||
|
pub fn with_value(pic: Picture, literal: &str) -> Self {
|
||||||
|
let mut n = Self::new(pic);
|
||||||
|
if let Ok(d) = Decimal::parse(literal) {
|
||||||
|
n.store(d);
|
||||||
|
}
|
||||||
|
n
|
||||||
|
}
|
||||||
|
|
||||||
|
/// El valor decimal actual.
|
||||||
|
pub fn value(&self) -> Decimal {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// La PICTURE del campo.
|
||||||
|
pub fn picture(&self) -> Picture {
|
||||||
|
self.pic
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asigna un valor conformándolo a la PICTURE: ajusta la escala
|
||||||
|
/// truncando los dígitos fraccionarios sobrantes.
|
||||||
|
pub fn store(&mut self, v: Decimal) {
|
||||||
|
self.value = fit(v, &self.pic, Rounding::Truncate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Como [`store`](Self::store) pero redondeando — el `ROUNDED`.
|
||||||
|
pub fn store_rounded(&mut self, v: Decimal) {
|
||||||
|
self.value = fit(v, &self.pic, Rounding::HalfUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Representación para `DISPLAY`: los dígitos del campo, rellenados
|
||||||
|
/// con ceros a la izquierda hasta el total de la PICTURE; con un
|
||||||
|
/// `-` adelante si el campo lleva signo y el valor es negativo.
|
||||||
|
pub fn display(&self) -> String {
|
||||||
|
let total = self.pic.total_digits() as usize;
|
||||||
|
let abs = self.value.mantissa().unsigned_abs().to_string();
|
||||||
|
let digits = if abs.len() >= total {
|
||||||
|
abs[abs.len() - total..].to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}{}", "0".repeat(total - abs.len()), abs)
|
||||||
|
};
|
||||||
|
if self.pic.signed && self.value.is_negative() {
|
||||||
|
format!("-{digits}")
|
||||||
|
} else {
|
||||||
|
digits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Conforma un valor a una PICTURE. Si la parte entera no cabe (el
|
||||||
|
/// `ON SIZE ERROR` de COBOL) y ninguna cláusula lo captura, COBOL deja
|
||||||
|
/// los dígitos de bajo orden: reescalamos y enmascaramos.
|
||||||
|
fn fit(v: Decimal, pic: &Picture, rounding: Rounding) -> Decimal {
|
||||||
|
if let Ok(d) = v.coerce(pic, rounding) {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
let r = v.rescale(pic.fraction_digits, rounding);
|
||||||
|
let modulus = 10i128.pow(pic.total_digits() as u32);
|
||||||
|
let mut m = r.mantissa() % modulus;
|
||||||
|
if !pic.signed && m < 0 {
|
||||||
|
m = -m;
|
||||||
|
}
|
||||||
|
Decimal::new(m, pic.fraction_digits)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn pic(s: &str) -> Picture {
|
||||||
|
Picture::parse(s).expect("PICTURE válida")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dec(s: &str) -> Decimal {
|
||||||
|
Decimal::parse(s).expect("decimal válido")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_field_is_zero() {
|
||||||
|
let n = Num::new(pic("9(5)"));
|
||||||
|
assert!(n.value().is_zero());
|
||||||
|
assert_eq!(n.display(), "00000");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn with_value_initializes() {
|
||||||
|
let n = Num::with_value(pic("9(3)"), "42");
|
||||||
|
assert_eq!(n.display(), "042");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn store_truncates_fraction() {
|
||||||
|
let mut n = Num::new(pic("9(3)V99"));
|
||||||
|
n.store(dec("12.3456"));
|
||||||
|
assert_eq!(n.value(), dec("12.34"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn store_rounded_rounds_fraction() {
|
||||||
|
let mut n = Num::new(pic("9(3)V99"));
|
||||||
|
n.store_rounded(dec("12.3456"));
|
||||||
|
assert_eq!(n.value(), dec("12.35"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn store_overflow_keeps_low_order_digits() {
|
||||||
|
// 1234 no cabe en 9(3): COBOL conserva los 3 dígitos bajos.
|
||||||
|
let mut n = Num::new(pic("9(3)"));
|
||||||
|
n.store(dec("1234"));
|
||||||
|
assert_eq!(n.display(), "234");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unsigned_field_stores_magnitude() {
|
||||||
|
let mut n = Num::new(pic("9(3)"));
|
||||||
|
n.store(dec("-7"));
|
||||||
|
assert_eq!(n.display(), "007");
|
||||||
|
assert!(!n.value().is_negative());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn signed_field_keeps_sign_in_display() {
|
||||||
|
let mut n = Num::new(pic("S9(3)"));
|
||||||
|
n.store(dec("-7"));
|
||||||
|
assert_eq!(n.display(), "-007");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn display_includes_implied_fraction_digits() {
|
||||||
|
// PIC 9(2)V99, valor 7.5 → dígitos 0750.
|
||||||
|
let mut n = Num::new(pic("9(2)V99"));
|
||||||
|
n.store(dec("7.5"));
|
||||||
|
assert_eq!(n.display(), "0750");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
//! `Text` — un campo alfanumérico COBOL en tiempo de ejecución.
|
||||||
|
|
||||||
|
/// Un campo alfanumérico de longitud fija (`PIC X(n)`). El contenido
|
||||||
|
/// se mantiene siempre con exactamente `len` caracteres — toda
|
||||||
|
/// asignación justifica a la izquierda y rellena o trunca.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Text {
|
||||||
|
buf: String,
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Text {
|
||||||
|
/// Campo nuevo de `len` caracteres, lleno de espacios.
|
||||||
|
pub fn new(len: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
buf: " ".repeat(len),
|
||||||
|
len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Campo con un `VALUE` inicial.
|
||||||
|
pub fn with_value(len: usize, literal: &str) -> Self {
|
||||||
|
let mut t = Self::new(len);
|
||||||
|
t.store(literal);
|
||||||
|
t
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asigna un texto: lo justifica a la izquierda, y rellena con
|
||||||
|
/// espacios o trunca hasta `len` — el `MOVE` alfanumérico de COBOL.
|
||||||
|
pub fn store(&mut self, s: &str) {
|
||||||
|
let mut chars: Vec<char> = s.chars().take(self.len).collect();
|
||||||
|
while chars.len() < self.len {
|
||||||
|
chars.push(' ');
|
||||||
|
}
|
||||||
|
self.buf = chars.into_iter().collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Llena el campo entero con un carácter — para mover las
|
||||||
|
/// constantes figurativas (`SPACES`, `ZEROS`...).
|
||||||
|
pub fn fill(&mut self, ch: char) {
|
||||||
|
self.buf = (0..self.len).map(|_| ch).collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// El contenido actual (siempre exactamente `len` caracteres).
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
&self.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
/// La longitud declarada del campo.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.len
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ¿El campo se declaró con longitud cero?
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.len == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Representación para `DISPLAY` — el contenido tal cual, con sus
|
||||||
|
/// espacios de relleno incluidos.
|
||||||
|
pub fn display(&self) -> String {
|
||||||
|
self.buf.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_field_is_all_spaces() {
|
||||||
|
let t = Text::new(5);
|
||||||
|
assert_eq!(t.as_str(), " ");
|
||||||
|
assert_eq!(t.len(), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn with_value_left_justifies_and_pads() {
|
||||||
|
let t = Text::with_value(5, "AB");
|
||||||
|
assert_eq!(t.as_str(), "AB ");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn store_truncates_when_too_long() {
|
||||||
|
let mut t = Text::new(3);
|
||||||
|
t.store("HELLO");
|
||||||
|
assert_eq!(t.as_str(), "HEL");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn store_pads_when_too_short() {
|
||||||
|
let mut t = Text::new(6);
|
||||||
|
t.store("HI");
|
||||||
|
assert_eq!(t.as_str(), "HI ");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fill_sets_every_position() {
|
||||||
|
let mut t = Text::new(4);
|
||||||
|
t.fill('0');
|
||||||
|
assert_eq!(t.as_str(), "0000");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn zero_length_field_is_empty() {
|
||||||
|
let t = Text::new(0);
|
||||||
|
assert!(t.is_empty());
|
||||||
|
assert_eq!(t.as_str(), "");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,30 @@
|
|||||||
Transpilador COBOL → Rust. El módulo más grande del ecosistema (Fase D
|
Transpilador COBOL → Rust. El módulo más grande del ecosistema (Fase D
|
||||||
del plan macro) — el parser COBOL completo es un esfuerzo multi-mes.
|
del plan macro) — el parser COBOL completo es un esfuerzo multi-mes.
|
||||||
|
|
||||||
|
### feat(charka-runtime): soporte de ejecución — campos Num y Text
|
||||||
|
|
||||||
|
Crate nuevo `crates/modules/charka/charka-runtime` — el soporte que los
|
||||||
|
programas COBOL transpilados enlazan. `charka-codegen` no emitirá Rust
|
||||||
|
autónomo: emitirá Rust que llama a esta biblioteca.
|
||||||
|
|
||||||
|
- `Num` — campo numérico (`PIC 9(5)V99`): un `Decimal` conformado a su
|
||||||
|
`Picture`. `store` trunca a la escala declarada; `store_rounded`
|
||||||
|
redondea; al desbordar la parte entera conserva los dígitos de bajo
|
||||||
|
orden (el `ON SIZE ERROR` de COBOL sin cláusula). `display` da los
|
||||||
|
dígitos con relleno de ceros y signo si corresponde.
|
||||||
|
- `Text` — campo alfanumérico (`PIC X(n)`) de longitud fija: `store`
|
||||||
|
justifica a la izquierda y rellena con espacios o trunca; `fill`
|
||||||
|
mueve las constantes figurativas (`SPACES`, `ZEROS`).
|
||||||
|
- `cobol_text_cmp` — comparación alfanumérica que rellena el más corto
|
||||||
|
con espacios.
|
||||||
|
- Reexporta `Decimal`/`Picture`/`Rounding` de `charka-bcd`.
|
||||||
|
- Construido antes que `charka-codegen` (la nota de orden del plan los
|
||||||
|
listaba al revés): el codegen emite contra esta API, así que el
|
||||||
|
runtime debe existir primero — y se verifica solo, sin el codegen.
|
||||||
|
- 17 tests: campo en cero, `VALUE` inicial, truncado y redondeo,
|
||||||
|
desbordamiento que conserva bajo orden, magnitud sin signo y signo
|
||||||
|
con signo, justificación y relleno de texto, `fill`, comparación.
|
||||||
|
|
||||||
### feat(charka-ir): representación intermedia — statements tipados
|
### feat(charka-ir): representación intermedia — statements tipados
|
||||||
|
|
||||||
Crate nuevo `crates/modules/charka/charka-ir` — la tercera etapa del
|
Crate nuevo `crates/modules/charka/charka-ir` — la tercera etapa del
|
||||||
|
|||||||
Reference in New Issue
Block a user