summaryrefslogtreecommitdiff
path: root/src/parser.rs
diff options
context:
space:
mode:
authorMike Buland <mike@xagasoft.com>2026-06-05 11:28:07 -0700
committerMike Buland <mike@xagasoft.com>2026-06-05 11:28:07 -0700
commit11f5bc0c5a8bcfb6ac8fef8abc33297c4addc81b (patch)
treec5d08d320c1cd280ce9ba1f59c0385808a036e67 /src/parser.rs
parent414681bc8b8ccbff81c0878fbf3ff193f7f55e0f (diff)
downloadcrimtag-11f5bc0c5a8bcfb6ac8fef8abc33297c4addc81b.tar.gz
crimtag-11f5bc0c5a8bcfb6ac8fef8abc33297c4addc81b.tar.bz2
crimtag-11f5bc0c5a8bcfb6ac8fef8abc33297c4addc81b.tar.xz
crimtag-11f5bc0c5a8bcfb6ac8fef8abc33297c4addc81b.zip
Started on the parser. Thinking about some tweaks.
- Creating a LookaheadIterator as a general class that could be used. - Wrap symbols in a symbol struct that tracks symbol start and end position for error reporting. - Make a struct for position in a file as well.
Diffstat (limited to 'src/parser.rs')
-rw-r--r--src/parser.rs280
1 files changed, 280 insertions, 0 deletions
diff --git a/src/parser.rs b/src/parser.rs
new file mode 100644
index 0000000..54b77ac
--- /dev/null
+++ b/src/parser.rs
@@ -0,0 +1,280 @@
1use crate::lexer::*;
2use crate::*;
3
4use core::error::Error;
5use std::fmt;
6
7#[derive(Debug)]
8struct ParseError {
9 line: u32,
10 row: u32,
11 what: String,
12}
13
14impl fmt::Display for ParseError {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 write!(f, "Error parsing input: {}", "yeah")
17 }
18}
19
20impl Error for ParseError { }
21
22struct Context<'a> {
23 cur: [Option<Symbol<'a>>;2],
24 icur: usize,
25 ll: Lexer<'a>,
26}
27
28impl<'a> Context<'a> {
29 pub fn new( mut ll: Lexer<'a> ) -> Self {
30 let cur = [ll.next(), ll.next()];
31 Self {
32 cur,
33 icur: 0,
34 ll
35 }
36 }
37
38 pub fn next(&mut self) -> Option<Symbol<'a>> {
39 self.cur[self.icur] = self.ll.next();
40 self.icur = (self.icur+1)%2;
41 println!(" - cur: {:?}, peek: {:?}", self.cur(), self.peek() );
42 self.cur[self.icur]
43 }
44
45 pub fn cur(&self) -> Option<Symbol<'a>> {
46 self.cur[self.icur]
47 }
48
49 pub fn peek(&self) -> Option<Symbol<'a>> {
50 self.cur[(self.icur+1)%2]
51 }
52}
53
54pub struct Parser {
55}
56
57/**
58 * input: input tag
59 * | input tag_pair
60 * | input text
61 * |
62 * ;
63 *
64 * tag: '[|' tag_guts '|]'
65 * ;
66 *
67 * tag_pair: open_tag tag_body close_tag
68 * ;
69 *
70 * tag_body: tag_body tag
71 * | tag_body tag_pair
72 * | tag_body text
73 * |
74 * ;
75 *
76 * open_tag: '[|' tag_guts '|>'
77 * ;
78 *
79 * close_tag: '<|' token '|]'
80 * ;
81 *
82 * tag_guts: 'fragment' literal props
83 * | 'section' literal
84 * | 'output' literal
85 * ;
86 *
87 * literal: '"' [^"]* '"'
88 * ;
89 *
90 * props: props token '=' literal
91 * |
92 * ;
93 *
94 * disambiguate (tm):
95 *
96 * input: input tag
97 * | input text
98 * |
99 * ;
100 *
101 * tag: open_tag_base unary_tag
102 * | open_tag_base binary_tag
103 * ;
104 *
105 * open_tag_base: '[|' tag_guts
106 * ;
107 *
108 * unary_tag: '|]'
109 * ;
110 *
111 * binary_tag: '|>' tag_body close_tag
112 * ;
113 *
114 * tag_body: tag_body tag
115 * | tag_body text
116 * |
117 * ;
118 *
119 * close_tag: '<|' token '|]'
120 * ;
121 *
122 * tag_guts: 'fragment' literal props
123 * | 'section' literal
124 * | 'output' literal
125 * ;
126 *
127 * literal: '"' [^"]* '"'
128 * ;
129 *
130 * props: props token '=' literal
131 * |
132 * ;
133 */
134impl Parser {
135 pub fn new() -> Self {
136 Self {
137 }
138 }
139
140 fn lex_error(&self, error: &Symbol) -> Result<(),Box<dyn Error>> {
141 if let Symbol::Error{line,row,what} = error {
142 Err(Box::new(ParseError {
143 line: *line,
144 row: *row,
145 what: format!("What: {:?}", *what)
146 }))
147 } else {
148 Err(Box::new(ParseError {
149 line: 0, row: 0, what: "Not an error?".to_string()
150 }))
151 }
152 }
153
154 pub fn parse(&mut self, crim: &mut Crimtag, src: &str ) -> Result<(),Box<dyn Error>> {
155 let mut ll = Lexer::new( src );
156 let mut ctx = Context::new( ll );
157
158 // Parse the root of the file, the input context
159 self.p_input( &mut ctx )?;
160
161 Ok(())
162 }
163
164 fn p_input(&mut self, ctx: &mut Context ) -> Result<(), Box<dyn Error>> {
165 loop {
166 if ctx.cur().is_none() {
167 break;
168 }
169 match ctx.cur() {
170 Some(Symbol::Text(_)) => { /* Skip top level text */ }
171 Some(Symbol::StartFlat) => {
172 self.parse_tag( ctx );
173 }
174 Some(Symbol::Error{..}) => {
175 return self.lex_error( &ctx.cur().unwrap() );
176 }
177 _ => {
178
179 }
180 }
181 ctx.next();
182 }
183 Ok(())
184 }
185
186 fn parse_tag(&mut self, ctx: &mut Context) -> Result<(), Box<dyn Error>> {
187 let mut tb = TagBuilder::new();
188
189 let start_sym = ctx.cur().unwrap();
190
191 if let Some(Symbol::Token(s)) = ctx.next() {
192 tb.set_name( s.to_string() );
193 } else {
194 return Err(Box::new(ParseError{line: 0, row: 0, what: "Unexpecetd symbol".to_string()}));
195 }
196
197 self.parse_tag_params( ctx, &mut tb )?;
198 self.parse_tag_props( ctx, &mut tb )?;
199
200 Ok(())
201 }
202
203 fn parse_tag_params(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> Result<(), Box<dyn Error>> {
204 loop {
205 if let Some(Symbol::Token(s)) = ctx.cur() {
206 if matches!(ctx.peek(), Some(Symbol::Equals)) {
207 // Done with parameters
208 break;
209 }
210 else {
211 tb.add_param( s.to_string() );
212 }
213 }
214 ctx.next();
215 }
216 Ok(())
217 }
218
219 fn parse_tag_props(&mut self, ctx: &mut Context, tb: &mut TagBuilder ) -> Result<(), Box<dyn Error>> {
220 loop {
221 if let Some(Symbol::Token(s)) = ctx.cur() {
222 if matches!(ctx.peek(), Some(Symbol::Equals)) {
223 ctx.next();
224 if let Some(Symbol::Literal(lv)) = ctx.next() {
225 tb.add_prop( s.to_string(), lv.to_string() );
226 } else {
227 return Err(Box::new(ParseError{line: 0, row: 0, what: "Expected quoted literal string".to_string()}));
228 }
229 } else {
230 return Err(Box::new(ParseError{line: 0, row: 0, what: "Expected = ".to_string()}));
231 }
232 } else {
233 break;
234 }
235 ctx.next();
236 }
237 Ok(())
238 }
239}
240
241enum TagType {
242 Unknown,
243 Unary,
244 BinaryOpen,
245 BinaryClose,
246}
247
248struct TagBuilder {
249 name: String,
250 params: Vec<String>,
251 props: Vec<(String,String)>,
252 tag_type: TagType,
253}
254
255impl TagBuilder {
256 pub fn new() -> TagBuilder {
257 TagBuilder {
258 name: String::new(),
259 params: Vec::new(),
260 props: Vec::new(),
261 tag_type: TagType::Unknown,
262 }
263 }
264
265 pub fn set_name(&mut self, name: String) {
266 self.name = name;
267 }
268
269 pub fn add_param(&mut self, param: String) {
270 self.params.push( param );
271 }
272
273 pub fn add_prop(&mut self, key: String, value: String) {
274 self.props.push( (key, value) );
275 }
276
277 pub fn set_type(&mut self, tag_type: TagType) {
278 self.tag_type = tag_type;
279 }
280}