tawasuyu

tawasuyu/brahman

Rust 582TOML 222Markdown 73JSON 198635 Definitions

⎇ 3902763daa94397756ff2394116daef97814090a crates/modules/charka/charka-ir/src/cursor.rs Rust · 155 lines · 4.6 KB History Who touched what raw

feat(charka): OCCURS — tablas y referencias con subíndice 3902763da · sergio ·

1//! `Cursor` — un cursor de avance sobre la lista de tokens de una
2//! sentencia, más las primitivas para leer un operando.
3
4use charka_parser::{Token, TokenKind};
5
6use crate::ast::{Figurative, Operand};
7
8/// Cursor sobre los tokens de una sentencia. `pos` es público dentro
9/// del crate para que el parser de condiciones pueda rebobinar.
10pub(crate) struct Cursor<'a> {
11    pub(crate) toks: &'a [Token],
12    pub(crate) pos: usize,
13}
14
15impl<'a> Cursor<'a> {
16    pub(crate) fn new(toks: &'a [Token]) -> Self {
17        Self { toks, pos: 0 }
18    }
19
20    /// ¿Se agotaron los tokens?
21    pub(crate) fn done(&self) -> bool {
22        self.pos >= self.toks.len()
23    }
24
25    /// El token actual, sin consumirlo.
26    pub(crate) fn peek(&self) -> Option<&Token> {
27        self.toks.get(self.pos)
28    }
29
30    /// El token `n` posiciones adelante, sin consumirlo.
31    pub(crate) fn peek_at(&self, n: usize) -> Option<&Token> {
32        self.toks.get(self.pos + n)
33    }
34
35    /// Consume y devuelve el token actual.
36    pub(crate) fn bump(&mut self) -> Option<Token> {
37        let t = self.toks.get(self.pos).cloned();
38        if t.is_some() {
39            self.pos += 1;
40        }
41        t
42    }
43
44    /// El token actual, si es una palabra, en mayúsculas.
45    pub(crate) fn peek_word(&self) -> Option<String> {
46        word_of(self.peek())
47    }
48
49    /// La palabra `n` posiciones adelante, en mayúsculas.
50    pub(crate) fn word_at(&self, n: usize) -> Option<String> {
51        word_of(self.peek_at(n))
52    }
53
54    /// ¿El token actual es la palabra `kw`?
55    pub(crate) fn at_word(&self, kw: &str) -> bool {
56        self.peek_word().as_deref() == Some(kw)
57    }
58
59    /// Consume el token actual si es la palabra `kw`.
60    pub(crate) fn eat_word(&mut self, kw: &str) -> bool {
61        if self.at_word(kw) {
62            self.pos += 1;
63            true
64        } else {
65            false
66        }
67    }
68
69    /// ¿El token actual es el símbolo `s`?
70    pub(crate) fn at_sym(&self, s: &str) -> bool {
71        matches!(self.peek(), Some(t) if t.kind == TokenKind::Symbol && t.text == s)
72    }
73
74    /// Consume el token actual si es el símbolo `s`.
75    pub(crate) fn eat_sym(&mut self, s: &str) -> bool {
76        if self.at_sym(s) {
77            self.pos += 1;
78            true
79        } else {
80            false
81        }
82    }
83}
84
85/// Si el token es una palabra, su texto en mayúsculas.
86fn word_of(t: Option<&Token>) -> Option<String> {
87    match t {
88        Some(t) if t.kind == TokenKind::Word => Some(t.text.to_uppercase()),
89        _ => None,
90    }
91}
92
93/// Lee un operando: un literal con signo opcional, o un token suelto.
94pub(crate) fn parse_operand(c: &mut Cursor) -> Operand {
95    // Signo delante de un literal numérico (`-5`, `+3`).
96    if (c.at_sym("-") || c.at_sym("+")) && c.peek_at(1).map(|t| t.kind) == Some(TokenKind::Number) {
97        let neg = c.at_sym("-");
98        c.bump();
99        let num = c.bump().expect("número tras el signo");
100        return Operand::Num(if neg {
101            format!("-{}", num.text)
102        } else {
103            num.text
104        });
105    }
106    let base = match c.bump() {
107        Some(t) => token_to_operand(&t),
108        None => return Operand::Num("0".into()),
109    };
110    // Subíndice de tabla: `name(index)`. La v1 toma un solo subíndice;
111    // lo demás dentro del paréntesis se descarta.
112    if let Operand::Data(name) = &base {
113        if c.eat_sym("(") {
114            let index = parse_operand(c);
115            while !c.at_sym(")") && !c.done() {
116                c.bump();
117            }
118            c.eat_sym(")");
119            return Operand::Indexed {
120                name: name.clone(),
121                index: Box::new(index),
122            };
123        }
124    }
125    base
126}
127
128/// Clasifica un token suelto como operando.
129pub(crate) fn token_to_operand(t: &Token) -> Operand {
130    match t.kind {
131        TokenKind::Number => Operand::Num(t.text.clone()),
132        TokenKind::String => Operand::Str(t.text.clone()),
133        TokenKind::Word => {
134            let u = t.text.to_uppercase();
135            match figurative(&u) {
136                Some(f) => Operand::Figurative(f),
137                None => Operand::Data(u),
138            }
139        }
140        TokenKind::Period | TokenKind::Symbol => Operand::Data(t.text.clone()),
141    }
142}
143
144/// Reconoce una constante figurativa por su nombre en mayúsculas.
145fn figurative(w: &str) -> Option<Figurative> {
146    Some(match w {
147        "ZERO" | "ZEROS" | "ZEROES" => Figurative::Zero,
148        "SPACE" | "SPACES" => Figurative::Space,
149        "HIGH-VALUE" | "HIGH-VALUES" => Figurative::HighValue,
150        "LOW-VALUE" | "LOW-VALUES" => Figurative::LowValue,
151        "QUOTE" | "QUOTES" => Figurative::Quote,
152        "NULL" | "NULLS" => Figurative::Null,
153        _ => return None,
154    })
155}