tawasuyu

tawasuyu/brahman

Rust 492TOML 194Markdown 53JSON 176939 Sutichaykuna

⎇ 71a4068d12e81d0be7b08d0717114dd5b10868fe crates/modules/charka/charka-ir/src/cursor.rs Rust · 139 siq'ikuna · 4.1 KB Ñawpa willay Pi imata llamkarqan ch'uya

feat(charka): charka-ir — representación intermedia con statements tipados 71a4068d1 · 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    match c.bump() {
107        Some(t) => token_to_operand(&t),
108        None => Operand::Num("0".into()),
109    }
110}
111
112/// Clasifica un token suelto como operando.
113pub(crate) fn token_to_operand(t: &Token) -> Operand {
114    match t.kind {
115        TokenKind::Number => Operand::Num(t.text.clone()),
116        TokenKind::String => Operand::Str(t.text.clone()),
117        TokenKind::Word => {
118            let u = t.text.to_uppercase();
119            match figurative(&u) {
120                Some(f) => Operand::Figurative(f),
121                None => Operand::Data(u),
122            }
123        }
124        TokenKind::Period | TokenKind::Symbol => Operand::Data(t.text.clone()),
125    }
126}
127
128/// Reconoce una constante figurativa por su nombre en mayúsculas.
129fn figurative(w: &str) -> Option<Figurative> {
130    Some(match w {
131        "ZERO" | "ZEROS" | "ZEROES" => Figurative::Zero,
132        "SPACE" | "SPACES" => Figurative::Space,
133        "HIGH-VALUE" | "HIGH-VALUES" => Figurative::HighValue,
134        "LOW-VALUE" | "LOW-VALUES" => Figurative::LowValue,
135        "QUOTE" | "QUOTES" => Figurative::Quote,
136        "NULL" | "NULLS" => Figurative::Null,
137        _ => return None,
138    })
139}