summaryrefslogtreecommitdiff
path: root/src/lexer.rs
diff options
context:
space:
mode:
authorMike Buland <mike@xagasoft.com>2026-06-05 14:46:35 -0700
committerMike Buland <mike@xagasoft.com>2026-06-05 14:46:35 -0700
commit3dd9ca69340512e42867b43db689e494bada2000 (patch)
tree8834a0736873139302c8555bc5a7768cd739f74b /src/lexer.rs
parent11f5bc0c5a8bcfb6ac8fef8abc33297c4addc81b (diff)
downloadcrimtag-3dd9ca69340512e42867b43db689e494bada2000.tar.gz
crimtag-3dd9ca69340512e42867b43db689e494bada2000.tar.bz2
crimtag-3dd9ca69340512e42867b43db689e494bada2000.tar.xz
crimtag-3dd9ca69340512e42867b43db689e494bada2000.zip
Symbols have a wrapper with positional data now.
It required some weird changes, I'm not sure I'm happy with them all yet, but I"m getting closer. It parses all basics, now we need to build an AST so it's actually useful :-P
Diffstat (limited to 'src/lexer.rs')
-rw-r--r--src/lexer.rs105
1 files changed, 78 insertions, 27 deletions
diff --git a/src/lexer.rs b/src/lexer.rs
index 94825b2..04be350 100644
--- a/src/lexer.rs
+++ b/src/lexer.rs
@@ -2,13 +2,15 @@ use std::iter::Iterator;
2//use core::error::Error; 2//use core::error::Error;
3use std::str::CharIndices; 3use std::str::CharIndices;
4 4
5use crate::Position;
6
5#[derive(Debug,Copy,Clone)] 7#[derive(Debug,Copy,Clone)]
6pub enum ErrorType { 8pub enum ErrorType {
7 UnexpectedChar(char), 9 UnexpectedChar(char),
8} 10}
9 11
10#[derive(Debug,Copy,Clone)] 12#[derive(Debug,Copy,Clone)]
11pub enum Symbol<'a> { 13pub enum SymbolType<'a> {
12 StartFlat, 14 StartFlat,
13 StartPoint, 15 StartPoint,
14 EndFlat, 16 EndFlat,
@@ -20,10 +22,41 @@ pub enum Symbol<'a> {
20 Token(&'a str), 22 Token(&'a str),
21 Literal(&'a str), 23 Literal(&'a str),
22 Text(&'a str), 24 Text(&'a str),
23 Error{ line: u32, row: u32, what: ErrorType }, 25 Error{ what: ErrorType },
24 EOS, 26 EOS,
25} 27}
26 28
29#[derive(Debug,Copy,Clone)]
30pub struct Symbol<'a> {
31 symbol: SymbolType<'a>,
32 start: Position,
33 end: Position,
34}
35
36impl<'a> Symbol<'a> {
37 pub fn new( symbol: SymbolType<'a>, start: Position, end: Position ) -> Self {
38 Self {
39 symbol, start, end,
40 }
41 }
42
43 pub fn check_type<T: Fn( &SymbolType ) -> bool>(&self, f: T ) -> bool {
44 f( &self.symbol )
45 }
46
47 pub fn symbol(&self) -> &SymbolType<'a> {
48 &self.symbol
49 }
50
51 pub fn start(&self) -> &Position {
52 &self.start
53 }
54
55 pub fn end(&self) -> &Position {
56 &self.end
57 }
58}
59
27enum Mode { 60enum Mode {
28 Text, 61 Text,
29 InTag, 62 InTag,
@@ -35,8 +68,7 @@ pub struct Lexer<'a> {
35 cur: [Option<(usize, char)>;2], 68 cur: [Option<(usize, char)>;2],
36 icur: usize, 69 icur: usize,
37 mode: Mode, 70 mode: Mode,
38 line: u32, 71 pos: Position,
39 row: u32,
40} 72}
41 73
42fn is_valid_token_char( c: char ) -> bool { 74fn is_valid_token_char( c: char ) -> bool {
@@ -61,8 +93,7 @@ impl<'a> Lexer<'a> {
61 cur: [cur, cur2], 93 cur: [cur, cur2],
62 icur: 0, 94 icur: 0,
63 mode: Mode::Text, 95 mode: Mode::Text,
64 line: 0, 96 pos: Position::new(1,1),
65 row: 0,
66 } 97 }
67 } 98 }
68 99
@@ -71,6 +102,12 @@ impl<'a> Lexer<'a> {
71 self.icur = (self.icur+1)%2; 102 self.icur = (self.icur+1)%2;
72 //println!(" - cur: {:?}, peek: {:?}", self.cur(), self.peek() ); 103 //println!(" - cur: {:?}, peek: {:?}", self.cur(), self.peek() );
73 if let Some((_,chr)) = self.cur[self.icur] { 104 if let Some((_,chr)) = self.cur[self.icur] {
105 if chr == '\n' {
106 self.pos.column = 1;
107 self.pos.line += 1;
108 } else {
109 self.pos.column += 1;
110 }
74 Some(chr) 111 Some(chr)
75 } else { 112 } else {
76 None 113 None
@@ -110,11 +147,9 @@ impl<'a> Lexer<'a> {
110 } 147 }
111 148
112 fn error( &self, what: ErrorType ) -> Option<Symbol<'a>> { 149 fn error( &self, what: ErrorType ) -> Option<Symbol<'a>> {
113 Some(Symbol::Error { 150 Some(Symbol::new( SymbolType::Error {
114 line: self.line,
115 row: self.row,
116 what: what 151 what: what
117 }) 152 }, self.pos, self.pos ))
118 } 153 }
119 154
120 fn skip_ws( &mut self ) { 155 fn skip_ws( &mut self ) {
@@ -149,14 +184,16 @@ impl<'a> Lexer<'a> {
149 184
150 fn parse_text(&mut self) -> Option<Symbol<'a>> { 185 fn parse_text(&mut self) -> Option<Symbol<'a>> {
151 let start = self.cur_index(); 186 let start = self.cur_index();
187 let start_pos = self.pos.clone();
152 while self.next().is_some() && !self.is_start_tag() { } 188 while self.next().is_some() && !self.is_start_tag() { }
153 let end = self.cur_index(); 189 let end = self.cur_index();
190 let end_pos = self.pos.clone();
154 let s = &self.data[start..end]; 191 let s = &self.data[start..end];
155 //println!(" text: >>>{}<<<", s); 192 //println!(" text: >>>{}<<<", s);
156 if start == end { 193 if start == end {
157 None 194 None
158 } else { 195 } else {
159 Some(Symbol::Text(s)) 196 Some(Symbol::new( SymbolType::Text(s), start_pos, end_pos ))
160 } 197 }
161 } 198 }
162 199
@@ -168,25 +205,27 @@ impl<'a> Lexer<'a> {
168 } 205 }
169 Some('=') => { 206 Some('=') => {
170 self.next(); 207 self.next();
171 return Some(Symbol::Equals); 208 return Some(Symbol::new(SymbolType::Equals, self.pos, self.pos));
172 } 209 }
173 _ => {} 210 _ => {}
174 } 211 }
175 let start = self.cur_index(); 212 let start = self.cur_index();
213 let start_pos = self.pos.clone();
176 214
177 while self.next().is_some_and(|ch| is_valid_token_char(ch) ) && 215 while self.next().is_some_and(|ch| is_valid_token_char(ch) ) &&
178 !self.is_end_tag() {} 216 !self.is_end_tag() {}
179 let end = self.cur_index(); 217 let end = self.cur_index();
218 let end_pos = self.pos.clone();
180 let s = &self.data[start..end]; 219 let s = &self.data[start..end];
181 if start == end { 220 if start == end {
182 None 221 None
183 } else { 222 } else {
184 Some(match s { 223 Some(Symbol::new( match s {
185 "fragment" => Symbol::Fragment, 224 "fragment" => SymbolType::Fragment,
186 "section" => Symbol::Section, 225 "section" => SymbolType::Section,
187 "output" => Symbol::Output, 226 "output" => SymbolType::Output,
188 _ => Symbol::Token(s) 227 _ => SymbolType::Token(s)
189 }) 228 }, start_pos, end_pos ))
190 } 229 }
191 } 230 }
192 231
@@ -195,11 +234,13 @@ impl<'a> Lexer<'a> {
195 return self.error( ErrorType::UnexpectedChar(chr) ); 234 return self.error( ErrorType::UnexpectedChar(chr) );
196 } 235 }
197 let start = self.peek_index(); 236 let start = self.peek_index();
237 let start_pos = self.pos.clone();
198 while self.next().is_some_and(|chr| chr != '"') { } 238 while self.next().is_some_and(|chr| chr != '"') { }
199 let end = self.cur_index(); 239 let end = self.cur_index();
240 let end_pos = self.pos.clone();
200 self.next(); 241 self.next();
201 let s = &self.data[start..end]; 242 let s = &self.data[start..end];
202 Some(Symbol::Literal(s)) 243 Some(Symbol::new(SymbolType::Literal(s), start_pos, end_pos ))
203 } 244 }
204 245
205 fn is_start_tag(&mut self) -> bool { 246 fn is_start_tag(&mut self) -> bool {
@@ -212,21 +253,26 @@ impl<'a> Lexer<'a> {
212 } 253 }
213 254
214 fn parse_start_tag(&mut self) -> Option<Symbol<'a>> { 255 fn parse_start_tag(&mut self) -> Option<Symbol<'a>> {
256 let start_pos = self.pos.clone();
215 match self.cur() { 257 match self.cur() {
216 Some('[') => { 258 Some('[') => {
217 if let Some(p) = self.peek() && p == '|' { 259 if let Some(p) = self.peek() && p == '|' {
218 self.next(); self.next(); 260 self.next();
261 let end_pos = self.pos.clone();
262 self.next();
219 self.mode = Mode::InTag; 263 self.mode = Mode::InTag;
220 Some(Symbol::StartFlat) 264 Some(Symbol::new( SymbolType::StartFlat, start_pos, end_pos ) )
221 } else { 265 } else {
222 None 266 None
223 } 267 }
224 } 268 }
225 Some('<') => { 269 Some('<') => {
226 if let Some(p) = self.peek() && p == '|' { 270 if let Some(p) = self.peek() && p == '|' {
227 self.next(); self.next(); 271 self.next();
272 let end_pos = self.pos.clone();
273 self.next();
228 self.mode = Mode::InTag; 274 self.mode = Mode::InTag;
229 Some(Symbol::StartPoint) 275 Some(Symbol::new(SymbolType::StartPoint, start_pos, end_pos ) )
230 } else { 276 } else {
231 None 277 None
232 } 278 }
@@ -247,17 +293,22 @@ impl<'a> Lexer<'a> {
247 } 293 }
248 294
249 fn parse_end_tag(&mut self) -> Option<Symbol<'a>> { 295 fn parse_end_tag(&mut self) -> Option<Symbol<'a>> {
296 let start_pos = self.pos.clone();
250 if let Some(chr) = self.cur() && chr == '|' { 297 if let Some(chr) = self.cur() && chr == '|' {
251 match self.peek() { 298 match self.peek() {
252 Some(']') => { 299 Some(']') => {
253 self.next(); self.next(); 300 self.next();
301 let end_pos = self.pos.clone();
302 self.next();
254 self.mode = Mode::Text; 303 self.mode = Mode::Text;
255 Some(Symbol::EndFlat) 304 Some(Symbol::new( SymbolType::EndFlat, start_pos, end_pos))
256 } 305 }
257 Some('>') => { 306 Some('>') => {
258 self.next(); self.next(); 307 self.next();
308 let end_pos = self.pos.clone();
309 self.next();
259 self.mode = Mode::Text; 310 self.mode = Mode::Text;
260 Some(Symbol::EndPoint) 311 Some(Symbol::new( SymbolType::EndPoint, start_pos, end_pos))
261 } 312 }
262 _ => { 313 _ => {
263 None 314 None