⎇ 3902763daa94397756ff2394116daef97814090a
crates/modules/charka/charka-ir/src/cursor.rs
History
Who touched what
raw
feat(charka): OCCURS — tablas y referencias con subíndice
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}