diff options
| -rw-r--r-- | src/lexer.rs | 105 | ||||
| -rw-r--r-- | src/lib.rs | 24 | ||||
| -rw-r--r-- | src/parser.rs | 56 |
3 files changed, 133 insertions, 52 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; |
| 3 | use std::str::CharIndices; | 3 | use std::str::CharIndices; |
| 4 | 4 | ||
| 5 | use crate::Position; | ||
| 6 | |||
| 5 | #[derive(Debug,Copy,Clone)] | 7 | #[derive(Debug,Copy,Clone)] |
| 6 | pub enum ErrorType { | 8 | pub enum ErrorType { |
| 7 | UnexpectedChar(char), | 9 | UnexpectedChar(char), |
| 8 | } | 10 | } |
| 9 | 11 | ||
| 10 | #[derive(Debug,Copy,Clone)] | 12 | #[derive(Debug,Copy,Clone)] |
| 11 | pub enum Symbol<'a> { | 13 | pub 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)] | ||
| 30 | pub struct Symbol<'a> { | ||
| 31 | symbol: SymbolType<'a>, | ||
| 32 | start: Position, | ||
| 33 | end: Position, | ||
| 34 | } | ||
| 35 | |||
| 36 | impl<'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 | |||
| 27 | enum Mode { | 60 | enum 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 | ||
| 42 | fn is_valid_token_char( c: char ) -> bool { | 74 | fn 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 |
| @@ -8,6 +8,20 @@ use std::io::Read; | |||
| 8 | mod lexer; | 8 | mod lexer; |
| 9 | mod parser; | 9 | mod parser; |
| 10 | 10 | ||
| 11 | #[derive(Debug,Copy,Clone)] | ||
| 12 | pub struct Position { | ||
| 13 | pub line: u32, | ||
| 14 | pub column: u32, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl Position { | ||
| 18 | pub fn new( line: u32, column: u32 ) -> Self { | ||
| 19 | Self { | ||
| 20 | line, column, | ||
| 21 | } | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 11 | pub struct Crimtag { | 25 | pub struct Crimtag { |
| 12 | fragments: HashMap<String, Fragment>, | 26 | fragments: HashMap<String, Fragment>, |
| 13 | sources: Vec<FragmentSource>, | 27 | sources: Vec<FragmentSource>, |
| @@ -104,13 +118,15 @@ impl Crimtag { | |||
| 104 | mod tests { | 118 | mod tests { |
| 105 | use super::*; | 119 | use super::*; |
| 106 | 120 | ||
| 121 | use crate::lexer::*; | ||
| 122 | |||
| 107 | #[test] | 123 | #[test] |
| 108 | fn lexing() { | 124 | fn lexing() { |
| 109 | let data = r#"Leading comment: [|fragment "basic" something="yeup"|>Hello there <|fragment|] Trailing text"#; | 125 | let data = r#"Leading comment: [|fragment "basic" something="yeup"|>Hello there <|fragment|] Trailing text"#; |
| 110 | let ll = lexer::Lexer::new( &data ); | 126 | let ll = lexer::Lexer::new( &data ); |
| 111 | for sym in ll { | 127 | for sym in ll { |
| 112 | if let lexer::Symbol::Error{line,row,what} = sym { | 128 | if let SymbolType::Error{what} = sym.symbol() { |
| 113 | println!("Error {}:{}: {:?}", line, row, what ); | 129 | println!("Error {}:{}: {:?}", sym.start().line, sym.start().column, what ); |
| 114 | break; | 130 | break; |
| 115 | } else { | 131 | } else { |
| 116 | println!("Symbol: {:?}", sym ); | 132 | println!("Symbol: {:?}", sym ); |
| @@ -121,6 +137,8 @@ mod tests { | |||
| 121 | #[test] | 137 | #[test] |
| 122 | fn parsing() { | 138 | fn parsing() { |
| 123 | let mut ct = Crimtag::new(); | 139 | let mut ct = Crimtag::new(); |
| 124 | ct.load_static(r#"Here is a sample [|frament "index" theme="standard"|><html><body>Hi</body></html><|fragment|] That was fun!"#); | 140 | ct.load_static(r#"Here is a sample |
| 141 | [|frament "index" theme="standard"|><html><body>Hi</body></html><|fragment|] | ||
| 142 | That was fun!"#); | ||
| 125 | } | 143 | } |
| 126 | } | 144 | } |
diff --git a/src/parser.rs b/src/parser.rs index 54b77ac..e5ea1f5 100644 --- a/src/parser.rs +++ b/src/parser.rs | |||
| @@ -6,8 +6,7 @@ use std::fmt; | |||
| 6 | 6 | ||
| 7 | #[derive(Debug)] | 7 | #[derive(Debug)] |
| 8 | struct ParseError { | 8 | struct ParseError { |
| 9 | line: u32, | 9 | position: Position, |
| 10 | row: u32, | ||
| 11 | what: String, | 10 | what: String, |
| 12 | } | 11 | } |
| 13 | 12 | ||
| @@ -138,15 +137,15 @@ impl Parser { | |||
| 138 | } | 137 | } |
| 139 | 138 | ||
| 140 | fn lex_error(&self, error: &Symbol) -> Result<(),Box<dyn Error>> { | 139 | fn lex_error(&self, error: &Symbol) -> Result<(),Box<dyn Error>> { |
| 141 | if let Symbol::Error{line,row,what} = error { | 140 | if let SymbolType::Error{what} = error.symbol() { |
| 142 | Err(Box::new(ParseError { | 141 | Err(Box::new(ParseError { |
| 143 | line: *line, | 142 | position: error.start().clone(), |
| 144 | row: *row, | ||
| 145 | what: format!("What: {:?}", *what) | 143 | what: format!("What: {:?}", *what) |
| 146 | })) | 144 | })) |
| 147 | } else { | 145 | } else { |
| 148 | Err(Box::new(ParseError { | 146 | Err(Box::new(ParseError { |
| 149 | line: 0, row: 0, what: "Not an error?".to_string() | 147 | position: Position::new(0,0), |
| 148 | what: "Not an error?".to_string(), | ||
| 150 | })) | 149 | })) |
| 151 | } | 150 | } |
| 152 | } | 151 | } |
| @@ -166,12 +165,12 @@ impl Parser { | |||
| 166 | if ctx.cur().is_none() { | 165 | if ctx.cur().is_none() { |
| 167 | break; | 166 | break; |
| 168 | } | 167 | } |
| 169 | match ctx.cur() { | 168 | match ctx.cur().unwrap().symbol() { |
| 170 | Some(Symbol::Text(_)) => { /* Skip top level text */ } | 169 | SymbolType::Text(_) => { /* Skip top level text */ } |
| 171 | Some(Symbol::StartFlat) => { | 170 | SymbolType::StartFlat => { |
| 172 | self.parse_tag( ctx ); | 171 | self.parse_tag( ctx ); |
| 173 | } | 172 | } |
| 174 | Some(Symbol::Error{..}) => { | 173 | SymbolType::Error{..} => { |
| 175 | return self.lex_error( &ctx.cur().unwrap() ); | 174 | return self.lex_error( &ctx.cur().unwrap() ); |
| 176 | } | 175 | } |
| 177 | _ => { | 176 | _ => { |
| @@ -188,10 +187,14 @@ impl Parser { | |||
| 188 | 187 | ||
| 189 | let start_sym = ctx.cur().unwrap(); | 188 | let start_sym = ctx.cur().unwrap(); |
| 190 | 189 | ||
| 191 | if let Some(Symbol::Token(s)) = ctx.next() { | 190 | if ctx.next().is_some() { |
| 192 | tb.set_name( s.to_string() ); | 191 | if let SymbolType::Token(s) = ctx.next().unwrap().symbol() { |
| 192 | tb.set_name( s.to_string() ); | ||
| 193 | } else { | ||
| 194 | return Err(Box::new(ParseError{position: ctx.cur().unwrap().start().clone(), what: "Unexpecetd symbol".to_string()})); | ||
| 195 | } | ||
| 193 | } else { | 196 | } else { |
| 194 | return Err(Box::new(ParseError{line: 0, row: 0, what: "Unexpecetd symbol".to_string()})); | 197 | return Err(Box::new(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()})); |
| 195 | } | 198 | } |
| 196 | 199 | ||
| 197 | self.parse_tag_params( ctx, &mut tb )?; | 200 | self.parse_tag_params( ctx, &mut tb )?; |
| @@ -202,10 +205,15 @@ impl Parser { | |||
| 202 | 205 | ||
| 203 | fn parse_tag_params(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> Result<(), Box<dyn Error>> { | 206 | fn parse_tag_params(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> Result<(), Box<dyn Error>> { |
| 204 | loop { | 207 | loop { |
| 205 | if let Some(Symbol::Token(s)) = ctx.cur() { | 208 | if ctx.cur().is_none() { |
| 206 | if matches!(ctx.peek(), Some(Symbol::Equals)) { | 209 | |
| 207 | // Done with parameters | 210 | return Err(Box::new(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()})); |
| 208 | break; | 211 | } |
| 212 | if let SymbolType::Token(s) = ctx.cur().unwrap().symbol() { | ||
| 213 | if let Some(p) = ctx.peek() { | ||
| 214 | if p.check_type(|t| matches!(t, SymbolType::Equals)) { | ||
| 215 | break; | ||
| 216 | } | ||
| 209 | } | 217 | } |
| 210 | else { | 218 | else { |
| 211 | tb.add_param( s.to_string() ); | 219 | tb.add_param( s.to_string() ); |
| @@ -218,16 +226,20 @@ impl Parser { | |||
| 218 | 226 | ||
| 219 | fn parse_tag_props(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> Result<(), Box<dyn Error>> { | 227 | fn parse_tag_props(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> Result<(), Box<dyn Error>> { |
| 220 | loop { | 228 | loop { |
| 221 | if let Some(Symbol::Token(s)) = ctx.cur() { | 229 | if ctx.cur().is_none() { |
| 222 | if matches!(ctx.peek(), Some(Symbol::Equals)) { | 230 | return Err(Box::new(ParseError{position: Position::new(0,0), what: "Unexpecetd end of stream".to_string()})); |
| 231 | } | ||
| 232 | if let SymbolType::Token(s) = ctx.cur().unwrap().symbol() { | ||
| 233 | if ctx.peek().is_some_and(|s|s.check_type(|t| matches!(t,SymbolType::Equals))) { | ||
| 223 | ctx.next(); | 234 | ctx.next(); |
| 224 | if let Some(Symbol::Literal(lv)) = ctx.next() { | 235 | if let Some(sym) = ctx.next() && |
| 236 | let SymbolType::Literal(lv) = sym.symbol() { | ||
| 225 | tb.add_prop( s.to_string(), lv.to_string() ); | 237 | tb.add_prop( s.to_string(), lv.to_string() ); |
| 226 | } else { | 238 | } else { |
| 227 | return Err(Box::new(ParseError{line: 0, row: 0, what: "Expected quoted literal string".to_string()})); | 239 | return Err(Box::new(ParseError{position: Position::new(0,0), what: "Expected quoted literal string".to_string()})); |
| 228 | } | 240 | } |
| 229 | } else { | 241 | } else { |
| 230 | return Err(Box::new(ParseError{line: 0, row: 0, what: "Expected = ".to_string()})); | 242 | return Err(Box::new(ParseError{position:Position::new(0,0), what: "Expected = ".to_string()})); |
| 231 | } | 243 | } |
| 232 | } else { | 244 | } else { |
| 233 | break; | 245 | break; |
