fluid_parser/
parser.rs

1use crate::ast::*;
2use crate::lexer::Lexer;
3use crate::token::{Token, TokenType};
4
5pub struct Parser<'a> {
6    lexer: Lexer<'a>,
7    pub i: usize,
8    pub tokens: Vec<Token<'a>>,
9}
10
11impl<'a> Parser<'a> {
12    pub fn new(mut lexer: Lexer<'a>) -> Self {
13        let mut t = lexer.next();
14        let mut tokens = vec![t];
15        while t.typ != TokenType::Eof {
16            t = lexer.next();
17            tokens.push(t);
18        }
19        Self {
20            lexer,
21            i: 0,
22            tokens,
23        }
24    }
25    pub fn parse(&mut self) -> Ast {
26        let mut a = Ast::default();
27        while self.i < self.tokens.len() {
28            let curr = self.tokens[self.i];
29            match curr.typ {
30                TokenType::Eof => break,
31                TokenType::Word => match curr.word {
32                    "i18n_type" => {
33                        a.i18n_type = Some(true);
34                        self.i += 2;
35                    }
36                    "class" => {
37                        let c = self.consume_class();
38                        a.classes.push(c);
39                    }
40                    "Function" => {
41                        let f = self.consume_func();
42                        a.functions.push(f);
43                    }
44                    "comment" => {
45                        let c = self.consume_comment();
46                        a.comments.push(c);
47                    }
48                    "decl" => {
49                        let d = self.consume_decl();
50                        a.decls.push(d);
51                    }
52                    "widget_class" => {
53                        let w = self.consume_widget();
54                        a.widget_classes.push(w);
55                    }
56                    _ => (),
57                },
58                _ => (),
59            }
60            self.i += 1;
61        }
62        a
63    }
64    fn consume_func(&mut self) -> Function {
65        let mut f = Function::default();
66        self.i += 1;
67        f.name = self.consume_braced_string();
68        self.i += 1; // opening parens of props
69        while self.tokens[self.i].typ != TokenType::Eof {
70            self.i += 1;
71            if self.tokens[self.i].typ == TokenType::CloseBrace {
72                break;
73            }
74            match self.tokens[self.i].word {
75                "open" => f.props.open = Some(true),
76                "C" => f.props.c = Some(true),
77                "protected" => f.props.visibility = Some(Visibility::PROTECTED),
78                "private" => f.props.visibility = Some(Visibility::PRIVATE),
79                "comment" => {
80                    self.i += 1;
81                    if self.tokens[self.i].typ == TokenType::OpenBrace {
82                        f.props.comment = Some(self.consume_braced_string());
83                    } else {
84                        f.props.comment = Some(self.tokens[self.i].word.to_string());
85                    }
86                }
87                "return_type" => {
88                    self.i += 1;
89                    if self.tokens[self.i].typ == TokenType::OpenBrace {
90                        f.props.return_type = Some(self.consume_braced_string());
91                    } else {
92                        f.props.return_type = Some(self.tokens[self.i].word.to_string());
93                    }
94                }
95                _ => (),
96            }
97        }
98        self.i += 1; // close props parens
99        if self.tokens[self.i].typ == TokenType::OpenBrace {
100            while self.tokens[self.i].typ != TokenType::CloseBrace {
101                self.i += 1;
102                if self.tokens[self.i].word == "code" {
103                    self.i += 1;
104                    f.code = Some(self.consume_code());
105                    self.i += 1;
106                }
107                if self.tokens[self.i].word.starts_with("Fl_")
108                    || self.tokens[self.i].word == "MenuItem"
109                    || self.tokens[self.i].word == "Submenu"
110                {
111                    let w = self.consume_widget();
112                    f.widgets.push(w);
113                    self.i += 1;
114                }
115            }
116        }
117        f
118    }
119    fn consume_widget(&mut self) -> Widget {
120        let mut w = Widget {
121            typ: self.tokens[self.i].word.to_string(),
122            ..Default::default()
123        };
124        self.i += 1;
125        if self.tokens[self.i].typ == TokenType::OpenBrace {
126            self.i += 1;
127        }
128        if !self.tokens[self.i].word.is_empty() {
129            w.name = self.tokens[self.i].word.to_string();
130        } else {
131            w.name = String::new();
132        }
133        while self.tokens[self.i].typ != TokenType::Eof {
134            self.i += 1;
135            if self.tokens[self.i].typ == TokenType::CloseBrace {
136                break;
137            }
138            match self.tokens[self.i].word {
139                "open" => w.props.open = Some(true),
140                "hide" => w.props.hide = Some(true),
141                "deactivate" => w.props.deactivate = Some(true),
142                "divider" => w.props.divider = Some(true),
143                "resizable" => w.props.resizable = Some(true),
144                "visible" => w.props.visible = Some(true),
145                "hotspot" => w.props.hotspot = Some(true),
146                "modal" => w.props.modal = Some(true),
147                "non_modal" => w.props.non_modal = Some(true),
148                "noborder" => w.props.noborder = Some(true),
149                "xywh" => {
150                    self.i += 1;
151                    w.props.xywh = self.consume_braced_string();
152                }
153                "size_range" => {
154                    self.i += 1;
155                    w.props.size_range = Some(self.consume_braced_string());
156                }
157                "color" => {
158                    self.i += 1;
159                    w.props.color = Some(self.tokens[self.i].word.to_string().parse().unwrap_or_else(|_| panic!("Failed to parse numeric value for `align` property at token:\"{:?}\"!",
160                            self.tokens[self.i])));
161                }
162                "selection_color" => {
163                    self.i += 1;
164                    w.props.selection_color = Some(
165                        self.tokens[self.i]
166                            .word
167                            .to_string()
168                            .parse()
169                            .unwrap_or_else(|_| panic!("Failed to parse numeric value for `selection_color` property at token:\"{:?}\"!",
170                            self.tokens[self.i])),
171                    );
172                }
173                "labelcolor" => {
174                    self.i += 1;
175                    w.props.labelcolor = Some(self.tokens[self.i].word.to_string().parse().unwrap_or_else(|_| panic!("Failed to parse numeric value for `labelcolor` property at token:\"{:?}\"!",
176                            self.tokens[self.i])));
177                }
178                "textcolor" => {
179                    self.i += 1;
180                    w.props.textcolor = Some(self.tokens[self.i].word.to_string().parse().unwrap_or_else(|_| panic!("Failed to parse numeric value for `textcolor` property at token:\"{:?}\"!",
181                            self.tokens[self.i])));
182                }
183                "type" => {
184                    self.i += 1;
185                    if self.tokens[self.i].typ == TokenType::OpenBrace {
186                        w.props.typ = Some(self.consume_braced_string());
187                    } else {
188                        w.props.typ = Some(self.tokens[self.i].word.to_string());
189                    }
190                }
191                "labeltype" => {
192                    self.i += 1;
193                    w.props.labeltype = Some(self.tokens[self.i].word.to_string());
194                }
195                "labelfont" => {
196                    self.i += 1;
197                    w.props.labelfont = Some(self.tokens[self.i].word.to_string().parse().unwrap_or_else(|_| panic!("Failed to parse numeric value for `labeltype` property at token:\"{:?}\"!",
198                            self.tokens[self.i])));
199                }
200                "textfont" => {
201                    self.i += 1;
202                    w.props.textfont = Some(self.tokens[self.i].word.to_string().parse().unwrap_or_else(|_| panic!("Failed to parse numeric value for `textfont` property at token:\"{:?}\"!",
203                            self.tokens[self.i])));
204                }
205                "labelsize" => {
206                    self.i += 1;
207                    w.props.labelsize = Some(self.tokens[self.i].word.to_string().parse().unwrap_or_else(|_| panic!("Failed to parse numeric value for `labelsize` property at token:\"{:?}\"!",
208                            self.tokens[self.i])));
209                }
210                "textsize" => {
211                    self.i += 1;
212                    w.props.textsize = Some(self.tokens[self.i].word.to_string().parse().unwrap_or_else(|_| panic!("Failed to parse numeric value for `textsize` property at token:\"{:?}\"!",
213                            self.tokens[self.i])));
214                }
215                "box" => {
216                    self.i += 1;
217                    w.props.r#box = Some(self.tokens[self.i].word.to_string());
218                }
219                "down_box" => {
220                    self.i += 1;
221                    w.props.down_box = Some(self.tokens[self.i].word.to_string());
222                }
223                "align" => {
224                    self.i += 1;
225                    w.props.align = Some(self.tokens[self.i].word.to_string().parse().unwrap_or_else(|_| panic!("Failed to parse numeric value for `align` property at token:\"{:?}\"!",
226                            self.tokens[self.i])));
227                }
228                "when" => {
229                    self.i += 1;
230                    w.props.when = Some(self.tokens[self.i].word.to_string().parse().unwrap_or_else(|_| panic!("Failed to parse numeric value for `when` property at token:\"{:?}\"!",
231                            self.tokens[self.i])));
232                }
233                "shortcut" => {
234                    self.i += 1;
235                    w.props.shortcut = Some(self.tokens[self.i].word.to_string());
236                }
237                "gap" => {
238                    self.i += 1;
239                    if self.tokens[self.i].typ == TokenType::OpenBrace {
240                        w.props.gap = Some(self.consume_braced_string());
241                    } else {
242                        w.props.gap = Some(self.tokens[self.i].word.to_string());
243                    }
244                }
245                "minimum" => {
246                    self.i += 1;
247                    w.props.minimum = Some(self.tokens[self.i].word.to_string().parse().unwrap_or_else(|_| panic!("Failed to parse numeric value for `minimum` property at token:\"{:?}\"!",
248                            self.tokens[self.i])));
249                }
250                "maximum" => {
251                    self.i += 1;
252                    w.props.maximum = Some(self.tokens[self.i].word.to_string().parse().unwrap_or_else(|_| panic!("Failed to parse numeric value for `maximum` property at token:\"{:?}\"!",
253                            self.tokens[self.i])));
254                }
255                "step" => {
256                    self.i += 1;
257                    w.props.step = Some(self.tokens[self.i].word.to_string().parse().unwrap_or_else(|_| panic!("Failed to parse numeric value for `step` property at token:\"{:?}\"!",
258                            self.tokens[self.i])));
259                }
260                "slider_size" => {
261                    self.i += 1;
262                    w.props.slider_size = Some(
263                        self.tokens[self.i]
264                            .word
265                            .to_string()
266                            .parse()
267                            .unwrap_or_else(|_| panic!("Failed to parse numeric value for `slider_size` property at token:\"{:?}\"!",
268                            self.tokens[self.i])),
269                    );
270                }
271                "size" => {
272                    self.i += 1;
273                    w.props.size = Some(self.tokens[self.i].word.to_string().parse().unwrap_or_else(|_| panic!("Failed to parse numeric value for `size` property at token:\"{:?}\"!",
274                            self.tokens[self.i])));
275                }
276                "label" => {
277                    self.i += 1;
278                    if self.tokens[self.i].typ == TokenType::OpenBrace {
279                        w.props.label = Some(self.consume_braced_string());
280                    } else {
281                        w.props.label = Some(self.tokens[self.i].word.to_string());
282                    }
283                }
284                "xclass" => {
285                    self.i += 1;
286                    if self.tokens[self.i].typ == TokenType::OpenBrace {
287                        w.props.xclass = Some(self.consume_braced_string());
288                    } else {
289                        w.props.xclass = Some(self.tokens[self.i].word.to_string());
290                    }
291                }
292                "class" => {
293                    self.i += 1;
294                    if self.tokens[self.i].typ == TokenType::OpenBrace {
295                        w.props.class = Some(self.consume_braced_string());
296                    } else {
297                        w.props.class = Some(self.tokens[self.i].word.to_string());
298                    }
299                }
300                "tooltip" => {
301                    self.i += 1;
302                    if self.tokens[self.i].typ == TokenType::OpenBrace {
303                        w.props.tooltip = Some(self.consume_braced_string());
304                    } else {
305                        w.props.tooltip = Some(self.tokens[self.i].word.to_string());
306                    }
307                }
308                "image" => {
309                    self.i += 1;
310                    if self.tokens[self.i].typ == TokenType::OpenBrace {
311                        w.props.image = Some(self.consume_braced_string());
312                    } else {
313                        w.props.image = Some(self.tokens[self.i].word.to_string());
314                    }
315                }
316                "deimage" => {
317                    self.i += 1;
318                    if self.tokens[self.i].typ == TokenType::OpenBrace {
319                        w.props.deimage = Some(self.consume_braced_string());
320                    } else {
321                        w.props.deimage = Some(self.tokens[self.i].word.to_string());
322                    }
323                }
324                "value" => {
325                    self.i += 1;
326                    if self.tokens[self.i].typ == TokenType::OpenBrace {
327                        w.props.value = Some(self.consume_braced_string());
328                    } else {
329                        w.props.value = Some(self.tokens[self.i].word.to_string());
330                    }
331                }
332                "set_size_tuples" => {
333                    self.i += 1;
334                    w.props.size_tuple = Some(self.consume_braced_string());
335                }
336                "margins" => {
337                    self.i += 1;
338                    w.props.margins = Some(self.consume_braced_string());
339                }
340                "dimensions" => {
341                    self.i += 1;
342                    w.props.dimensions = Some(self.consume_braced_string());
343                }
344                "margin" => {
345                    self.i += 1;
346                    w.props.margin = Some(self.consume_braced_string());
347                }
348                "fixed_size_tuples" => {
349                    self.i += 1;
350                    w.props.size_tuple = Some(self.consume_braced_string());
351                }
352                "code0" => {
353                    self.i += 1;
354                    w.props.code0 = Some(self.consume_braced_string());
355                }
356                "code1" => {
357                    self.i += 1;
358                    w.props.code1 = Some(self.consume_braced_string());
359                }
360                "code2" => {
361                    self.i += 1;
362                    w.props.code2 = Some(self.consume_braced_string());
363                }
364                "code3" => {
365                    self.i += 1;
366                    w.props.code3 = Some(self.consume_braced_string());
367                }
368                "extra_code" => {
369                    self.i += 1;
370                    w.props.extra_code = Some(self.consume_braced_string());
371                }
372                "callback" => {
373                    self.i += 1;
374                    if self.tokens[self.i].typ == TokenType::OpenBrace {
375                        w.props.callback = Some(self.consume_braced_string());
376                    } else {
377                        w.props.callback = Some(self.tokens[self.i].word.to_string());
378                    }
379                }
380                "user_data" => {
381                    self.i += 1;
382                    if self.tokens[self.i].typ == TokenType::OpenBrace {
383                        w.props.user_data = Some(self.consume_braced_string());
384                    } else {
385                        w.props.user_data = Some(self.tokens[self.i].word.to_string());
386                    }
387                }
388                "user_data_type" => {
389                    self.i += 1;
390                    if self.tokens[self.i].typ == TokenType::OpenBrace {
391                        w.props.user_data_type = Some(self.consume_braced_string());
392                    } else {
393                        w.props.user_data_type = Some(self.tokens[self.i].word.to_string());
394                    }
395                }
396                "comment" => {
397                    self.i += 1;
398                    if self.tokens[self.i].typ == TokenType::OpenBrace {
399                        w.props.comment = Some(self.consume_braced_string());
400                    } else {
401                        w.props.comment = Some(self.tokens[self.i].word.to_string());
402                    }
403                }
404                "parent_properties" => {
405                    while self.tokens[self.i].typ != TokenType::CloseBrace {
406                        self.i += 1;
407                        w.props.parent_properties = Some(self.consume_parent_props());
408                    }
409                }
410                _ => (),
411            }
412        }
413        if self.tokens[self.i + 1].typ == TokenType::OpenBrace {
414            self.i += 1;
415            while self.tokens[self.i].typ != TokenType::CloseBrace {
416                self.i += 1;
417                while self.tokens[self.i].word.starts_with("Fl_")
418                    || self.tokens[self.i].word == "MenuItem"
419                    || self.tokens[self.i].word == "Submenu"
420                {
421                    let c = self.consume_widget();
422                    w.children.push(c);
423                    self.i += 1;
424                }
425            }
426        }
427        w
428    }
429    fn consume_class(&mut self) -> Class {
430        let mut c = Class::default();
431        self.i += 1;
432        if self.tokens[self.i].typ == TokenType::OpenBrace {
433            self.i += 2;
434        }
435        c.name = self.tokens[self.i].word.to_string();
436        self.i += 1;
437        // handle props
438        while self.tokens[self.i].typ != TokenType::CloseBrace {
439            self.i += 1;
440            match self.tokens[self.i].word {
441                "open" => c.props.open = Some(true),
442                "protected" => c.props.visibility = Some(Visibility::PROTECTED),
443                "private" => c.props.visibility = Some(Visibility::PRIVATE),
444                "comment" => {
445                    self.i += 1;
446                    if self.tokens[self.i].typ == TokenType::OpenBrace {
447                        c.props.comment = Some(self.consume_braced_string());
448                    } else {
449                        c.props.comment = Some(self.tokens[self.i].word.to_string());
450                    }
451                }
452                _ => (),
453            }
454        }
455        self.i += 1;
456        if self.tokens[self.i].typ == TokenType::OpenBrace {
457            while self.tokens[self.i].typ != TokenType::CloseBrace {
458                self.i += 1;
459                while self.tokens[self.i].word == "Function" {
460                    let f = self.consume_func();
461                    c.functions.push(f);
462                }
463                if self.tokens[self.i].word == "comment" {
464                    self.i += 1;
465                    if self.tokens[self.i].typ == TokenType::OpenBrace {
466                        c.props.comment = Some(self.consume_braced_string());
467                    } else {
468                        c.props.comment = Some(self.tokens[self.i].word.to_string());
469                    }
470                }
471            }
472        }
473        c
474    }
475    fn consume_comment(&mut self) -> Comment {
476        let mut c = Comment::default();
477        self.i += 1;
478        c.comment = self.consume_braced_string();
479        while self.tokens[self.i].typ != TokenType::Eof {
480            self.i += 1;
481            if self.tokens[self.i].typ == TokenType::CloseBrace {
482                break;
483            }
484            match self.tokens[self.i].word {
485                "in_source" => c.props.in_source = Some(true),
486                "in_header" => c.props.in_header = Some(true),
487                _ => (),
488            }
489        }
490        c
491    }
492    fn consume_decl(&mut self) -> Decl {
493        let mut d = Decl::default();
494        self.i += 1;
495        d.decl = self.consume_braced_string();
496        while self.tokens[self.i].typ != TokenType::Eof {
497            self.i += 1;
498            if self.tokens[self.i].typ == TokenType::CloseBrace {
499                break;
500            }
501            match self.tokens[self.i].word {
502                "private" => d.props.visibility = Visibility::PRIVATE,
503                "public" => d.props.visibility = Visibility::PUBLIC,
504                "global" => d.props.global = Some(true),
505                "local" => d.props.local = Some(true),
506                _ => (),
507            }
508        }
509        d
510    }
511    fn consume_code(&mut self) -> String {
512        let s = self.consume_braced_string();
513        // skip last 2 braces
514        self.i += 1;
515        self.i += 1;
516        s
517    }
518
519    fn consume_braced_string(&mut self) -> String {
520        self.i += 1;
521        let mut t = self.tokens[self.i];
522        let start = t.start;
523        let mut openbrace = 1;
524        while t.typ != TokenType::Eof {
525            self.i += 1;
526            t = self.tokens[self.i];
527            if t.typ == TokenType::OpenBrace {
528                openbrace += 1;
529            }
530            if t.typ == TokenType::CloseBrace {
531                openbrace -= 1;
532            }
533            if openbrace == 0 {
534                break;
535            }
536        }
537        let end = self.tokens[self.i].end - 1;
538        self.lexer.s[start..end].to_string()
539    }
540    fn consume_parent_props(&mut self) -> ParentProps {
541        let mut p = ParentProps::default();
542        while self.tokens[self.i].typ != TokenType::Eof {
543            self.i += 1;
544            if self.tokens[self.i].typ == TokenType::CloseBrace {
545                break;
546            }
547            match self.tokens[self.i].word {
548                "location" => {
549                    self.i += 1;
550                    p.location = Some(self.consume_braced_string());
551                },
552                _ => (),
553            }
554        }
555        p
556    }
557}