fluid_parser/
parser.rs

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