summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lexer.rs258
-rw-r--r--src/lib.rs75
2 files changed, 333 insertions, 0 deletions
diff --git a/src/lexer.rs b/src/lexer.rs
new file mode 100644
index 0000000..d6806d8
--- /dev/null
+++ b/src/lexer.rs
@@ -0,0 +1,258 @@
1use std::iter::Iterator;
2//use core::error::Error;
3use std::str::CharIndices;
4
5#[derive(Debug)]
6pub enum Symbol<'a> {
7 StartFlat,
8 StartPoint,
9 EndFlat,
10 EndPoint,
11 Fragment,
12 Section,
13 Output,
14 Equals,
15 Token(&'a str),
16 Literal(&'a str),
17 Text(&'a str),
18 Error{ line: u32, row: u32, what: String },
19 EOS,
20}
21
22enum Mode {
23 Text,
24 InTag,
25}
26
27pub struct Lexer<'a> {
28 data: &'a str,
29 chars: CharIndices<'a>,
30 cur: [Option<(usize, char)>;2],
31 icur: usize,
32 mode: Mode,
33 line: u32,
34 row: u32,
35}
36
37impl<'a> Lexer<'a> {
38 pub fn new( data: &'a str ) -> Lexer<'a> {
39 let mut chars = data.char_indices();
40 let cur = chars.next();
41 let cur2 = chars.next();
42 //println!(" - cur: {:?}, peek: {:?}", cur, cur2 );
43 Lexer {
44 data,
45 chars,
46 cur: [cur, cur2],
47 icur: 0,
48 mode: Mode::Text,
49 line: 0,
50 row: 0,
51 }
52 }
53
54 fn next(&mut self) -> Option<char> {
55 self.cur[self.icur] = self.chars.next();
56 self.icur = (self.icur+1)%2;
57 //println!(" - cur: {:?}, peek: {:?}", self.cur(), self.peek() );
58 if let Some((_,chr)) = self.cur[self.icur] {
59 Some(chr)
60 } else {
61 None
62 }
63 }
64
65 fn cur(&self) -> Option<char> {
66 if let Some((_, chr)) = self.cur[self.icur] {
67 Some(chr)
68 } else {
69 None
70 }
71 }
72
73 fn cur_index(&self) -> usize {
74 if let Some((idx, _)) = self.cur[self.icur] {
75 idx
76 } else {
77 self.chars.offset()
78 }
79 }
80
81 fn peek(&self) -> Option<char> {
82 if let Some((_,chr)) = self.cur[(self.icur+1)%2] {
83 Some(chr)
84 } else {
85 None
86 }
87 }
88
89 fn peek_index(&self) -> usize {
90 if let Some((idx, _)) = self.cur[(self.icur+1)%2] {
91 idx
92 } else {
93 self.chars.offset()
94 }
95 }
96
97 fn error( &self, what: String ) -> Option<Symbol<'a>> {
98 Some(Symbol::Error {
99 line: self.line,
100 row: self.row,
101 what: what
102 })
103 }
104
105 fn skip_ws( &mut self ) {
106 while self.cur().is_some_and(|x|x.is_whitespace()) {
107 self.next();
108 }
109 }
110
111 fn next_symbol(&mut self) -> Option<Symbol<'a>> {
112 // If we hit the end then we're already done.
113 if self.cur().is_none() {
114 return None;
115 }
116
117 match self.mode {
118 Mode::Text => {
119 if let Some(sym) = self.parse_start_tag() {
120 Some(sym)
121 } else {
122 self.parse_text()
123 }
124 }
125 Mode::InTag => {
126 if let Some(sym) = self.parse_end_tag() {
127 Some(sym)
128 } else {
129 self.parse_token()
130 }
131 }
132 }
133 }
134
135 fn parse_text(&mut self) -> Option<Symbol<'a>> {
136 let start = self.cur_index();
137 while self.next().is_some() && !self.is_start_tag() { }
138 let end = self.cur_index();
139 let s = &self.data[start..end];
140 //println!(" text: >>>{}<<<", s);
141 if start == end {
142 None
143 } else {
144 Some(Symbol::Text(s))
145 }
146 }
147
148 fn parse_token(&mut self) -> Option<Symbol<'a>> {
149 self.skip_ws();
150 if let Some(chr) = self.cur() && chr == '"' {
151 // Literal string
152 return self.parse_literal_str();
153 }
154 let start = self.cur_index();
155
156 while self.next().is_some_and(|ch| !ch.is_whitespace() ) &&
157 !self.is_end_tag() {}
158 let end = self.cur_index();
159 let s = &self.data[start..end];
160 if start == end {
161 None
162 } else {
163 Some(match s {
164 "fragment" => Symbol::Fragment,
165 "section" => Symbol::Section,
166 "output" => Symbol::Output,
167 _ => Symbol::Token(s)
168 })
169 }
170 }
171
172 fn parse_literal_str(&mut self) -> Option<Symbol<'a>> {
173 if let Some(chr) = self.cur() && chr != '"' {
174 return self.error(format!("Expected '\"' but found '{}'", chr));
175 }
176 let start = self.peek_index();
177 while self.next().is_some_and(|chr| chr != '"') { }
178 let end = self.cur_index();
179 self.next();
180 let s = &self.data[start..end];
181 Some(Symbol::Literal(s))
182 }
183
184 fn is_start_tag(&mut self) -> bool {
185 if let Some(cur) = self.cur() && (cur == '[' || cur == '<') &&
186 let Some(peek) = self.peek() && peek == '|' {
187 true
188 } else {
189 false
190 }
191 }
192
193 fn parse_start_tag(&mut self) -> Option<Symbol<'a>> {
194 match self.cur() {
195 Some('[') => {
196 if let Some(p) = self.peek() && p == '|' {
197 self.next(); self.next();
198 self.mode = Mode::InTag;
199 Some(Symbol::StartFlat)
200 } else {
201 None
202 }
203 }
204 Some('<') => {
205 if let Some(p) = self.peek() && p == '|' {
206 self.next(); self.next();
207 self.mode = Mode::InTag;
208 Some(Symbol::StartPoint)
209 } else {
210 None
211 }
212 }
213 _ => {
214 None
215 }
216 }
217 }
218
219 fn is_end_tag(&mut self) -> bool {
220 if let Some(cur) = self.cur() && cur == '|' &&
221 let Some(peek) = self.peek() && (peek == '>' || peek == ']') {
222 true
223 } else {
224 false
225 }
226 }
227
228 fn parse_end_tag(&mut self) -> Option<Symbol<'a>> {
229 if let Some(chr) = self.cur() && chr == '|' {
230 match self.peek() {
231 Some(']') => {
232 self.next(); self.next();
233 self.mode = Mode::Text;
234 Some(Symbol::EndFlat)
235 }
236 Some('>') => {
237 self.next(); self.next();
238 self.mode = Mode::Text;
239 Some(Symbol::EndPoint)
240 }
241 _ => {
242 None
243 }
244 }
245 } else {
246 None
247 }
248 }
249}
250
251impl<'a> Iterator for Lexer<'a> {
252 type Item = Symbol<'a>;
253
254 fn next(&mut self) -> Option<Self::Item> {
255 self.next_symbol()
256 }
257}
258
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..57f777e
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,75 @@
1use std::path::{PathBuf,Path};
2use std::collections::HashMap;
3use core::error::Error;
4
5mod lexer;
6
7pub struct Tx {
8 fragments: HashMap<String, Fragment>,
9}
10
11enum FragmentSource {
12 File {
13 path: PathBuf,
14 loaded: usize,
15 }
16}
17
18struct Fragment {
19 name: String,
20 source: FragmentSource,
21 sections: HashMap<String, Section>,
22}
23
24struct Section {
25 name: String,
26 ast_root: Token,
27}
28
29type Properties = HashMap<String, String>;
30
31enum Token {
32 Root(Vec<Token>),
33 Fragment(String,Properties,Vec<Token>),
34 Section(String,Properties,Vec<Token>),
35 Text(String),
36
37}
38
39struct State {
40}
41
42impl Tx {
43 pub fn new() -> Self {
44 Self {
45 fragments: HashMap::new(),
46 }
47 }
48
49 pub fn load(path: &Path) -> Result<(),Box::<dyn Error>> {
50 Ok(())
51 }
52
53 pub fn parse(data: String) -> Result<(),Box::<dyn Error>> {
54 Ok(())
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn lexing() {
64 let data = r#"Leading comment: [|fragment "basic"|>Hello there <|fragment|] Trailing text"#;
65 let ll = lexer::Lexer::new( &data );
66 for sym in ll {
67 if let lexer::Symbol::Error{line,row,what} = sym {
68 println!("Error {}:{}: {}", line, row, what );
69 break;
70 } else {
71 println!("Symbol: {:?}", sym );
72 }
73 }
74 }
75}