Skip to main content

sequoia_seq/
parser.rs

1use crate::error::SeqError;
2use crate::ir::{
3    BinOp, CompParam, ComponentDef, DeferredDecl, DeferredKind, FnParam, ImportDecl, LengthVal,
4    MatchPat, SeqApp, SeqArg, SeqExpr, SeqExprKind, SeqFile, SeqFn, SeqMatchArm, SeqNode, SeqStmt,
5    SeqValue, SeqWindow, StateDecl, StyleDecl, ThemeName, UnOp, Visibility,
6};
7
8pub fn parse(file: &str, src: &str) -> Result<SeqApp, SeqError> {
9    let seq = parse_seq_file(file, src)?;
10    let mut app = seq
11        .app
12        .ok_or_else(|| SeqError::new(file, 1, 1, "brak bloku `app Nazwa { }`"))?;
13    app.components = seq.components;
14    app.imports = seq.imports;
15    Ok(app)
16}
17
18pub fn parse_seq_file(file: &str, src: &str) -> Result<SeqFile, SeqError> {
19    let mut p = Parser::new(file, src);
20    p.parse_seq_file()
21}
22
23const MAX_NESTING_DEPTH: usize = 256;
24
25struct Parser<'a> {
26    file: &'a str,
27    chars: Vec<char>,
28    pos: usize,
29    line: usize,
30    col: usize,
31    depth: usize,
32}
33
34impl<'a> Parser<'a> {
35    fn new(file: &'a str, src: &str) -> Self {
36        Self {
37            file,
38            chars: src.chars().collect(),
39            pos: 0,
40            line: 1,
41            col: 1,
42            depth: 0,
43        }
44    }
45
46    fn err(&self, line: usize, col: usize, message: impl Into<String>) -> SeqError {
47        SeqError::new(self.file, line, col, message)
48    }
49
50    fn peek(&self) -> Option<char> {
51        self.chars.get(self.pos).copied()
52    }
53
54    fn bump(&mut self) -> Option<char> {
55        let c = self.chars.get(self.pos).copied()?;
56        self.pos += 1;
57        if c == '\n' {
58            self.line += 1;
59            self.col = 1;
60        } else {
61            self.col += 1;
62        }
63        Some(c)
64    }
65
66    fn skip_trivia(&mut self) {
67        loop {
68            match self.peek() {
69                Some(c) if c == ' ' || c == '\t' || c == '\r' || c == '\n' => {
70                    self.bump();
71                }
72                Some('/') if self.chars.get(self.pos + 1) == Some(&'/') => {
73                    while !matches!(self.peek(), None | Some('\n')) {
74                        self.bump();
75                    }
76                }
77                _ => break,
78            }
79        }
80    }
81
82    fn loc(&mut self) -> (usize, usize) {
83        self.skip_trivia();
84        (self.line, self.col)
85    }
86
87    fn eat(&mut self, c: char) -> bool {
88        self.skip_trivia();
89        if self.peek() == Some(c) {
90            self.bump();
91            true
92        } else {
93            false
94        }
95    }
96
97    fn expect(&mut self, c: char, what: &str) -> Result<(), SeqError> {
98        let (line, col) = self.loc();
99        if self.peek() == Some(c) {
100            self.bump();
101            Ok(())
102        } else {
103            Err(self.err(line, col, format!("oczekiwano {what}")))
104        }
105    }
106
107    fn ident(&mut self) -> Result<(String, usize, usize), SeqError> {
108        let (line, col) = self.loc();
109        let start = self.pos;
110        match self.peek() {
111            Some(c) if c.is_ascii_alphabetic() || c == '_' => {
112                self.bump();
113            }
114            _ => return Err(self.err(line, col, "oczekiwano identyfikatora")),
115        }
116        while matches!(self.peek(), Some(c) if c.is_ascii_alphanumeric() || c == '_') {
117            self.bump();
118        }
119        Ok((self.chars[start..self.pos].iter().collect(), line, col))
120    }
121
122    fn number(&mut self) -> Result<(f32, usize, usize), SeqError> {
123        let (line, col) = self.loc();
124        let start = self.pos;
125        if self.peek() == Some('-') {
126            self.bump();
127        }
128        let mut digits = 0;
129        while matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
130            self.bump();
131            digits += 1;
132        }
133        if self.peek() == Some('.') {
134            self.bump();
135            while matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
136                self.bump();
137                digits += 1;
138            }
139        }
140        if digits == 0 {
141            return Err(self.err(line, col, "oczekiwano liczby"));
142        }
143        let text: String = self.chars[start..self.pos].iter().collect();
144        text.parse::<f32>()
145            .map(|v| (v, line, col))
146            .map_err(|_| self.err(line, col, format!("bad number '{text}'")))
147    }
148
149    fn string(&mut self) -> Result<(String, usize, usize), SeqError> {
150        let (line, col) = self.loc();
151        if self.peek() != Some('"') {
152            return Err(self.err(line, col, "expected string in quotation marks"));
153        }
154        self.bump();
155        let mut out = String::new();
156        loop {
157            match self.bump() {
158                None => return Err(self.err(line, col, "unclosed string")),
159                Some('"') => break,
160                Some('\\') => match self.bump() {
161                    Some('"') => out.push('"'),
162                    Some('\\') => out.push('\\'),
163                    Some('n') => out.push('\n'),
164                    Some('t') => out.push('\t'),
165                    Some(c) => {
166                        return Err(self.err(self.line, self.col, format!("bad escape '\\{c}'")));
167                    }
168                    None => return Err(self.err(line, col, "unclosed string")),
169                },
170                Some(c) => out.push(c),
171            }
172        }
173        Ok((out, line, col))
174    }
175
176    fn color(&mut self) -> Result<(String, usize, usize), SeqError> {
177        let (line, col) = self.loc();
178        debug_assert_eq!(self.peek(), Some('#'));
179        self.bump();
180        let start = self.pos;
181        while matches!(self.peek(), Some(c) if c.is_ascii_hexdigit()) {
182            self.bump();
183        }
184        let hex: String = self.chars[start..self.pos].iter().collect();
185        if hex.len() != 6 && hex.len() != 8 {
186            return Err(self.err(line, col, "kolor to #RRGGBB lub #RRGGBBAA"));
187        }
188        Ok((hex, line, col))
189    }
190
191    fn parse_seq_file(&mut self) -> Result<SeqFile, SeqError> {
192        let mut app: Option<SeqApp> = None;
193        let mut components: Vec<ComponentDef> = Vec::new();
194        let mut imports: Vec<ImportDecl> = Vec::new();
195        loop {
196            self.skip_trivia();
197            if self.peek().is_none() {
198                break;
199            }
200            let (kw, line, col) = self.ident()?;
201            match kw.as_str() {
202                "app" => {
203                    if app.is_some() {
204                        return Err(self.err(line, col, "drugi blok `app` (dozwolony jeden)"));
205                    }
206                    app = Some(self.app_rest(line, col)?);
207                }
208                "import" => {
209                    let (path, _, _) = self.string()?;
210                    imports.push(ImportDecl { path, line });
211                }
212                "pub" => {
213                    let (kw2, l2, c2) = self.ident()?;
214                    if kw2 != "component" {
215                        return Err(self.err(l2, c2, "`pub` tylko przed `component`"));
216                    }
217                    components.push(self.component_rest(line, Visibility::Pub)?);
218                }
219                "component" => {
220                    components.push(self.component_rest(line, Visibility::Priv)?);
221                }
222                _ => {
223                    return Err(self.err(line, col, "oczekiwano `app`, `component` albo `import`"));
224                }
225            }
226        }
227        if app.is_none() && components.is_empty() && imports.is_empty() {
228            return Err(self.err(
229                1,
230                1,
231                "pusty plik: oczekiwano `app`, `component` albo `import`",
232            ));
233        }
234        Ok(SeqFile {
235            imports,
236            components,
237            app,
238        })
239    }
240
241    fn app_rest(&mut self, line: usize, col: usize) -> Result<SeqApp, SeqError> {
242        let (name, _, _) = self.ident()?;
243        let _ = name;
244        self.expect('{', "'{' po `app Nazwa`")?;
245        let mut app = SeqApp {
246            title: String::new(),
247            size: (0.0, 0.0),
248            theme: ThemeName::Dark,
249            windows: Vec::new(),
250            deferred: Vec::new(),
251            styles: Vec::new(),
252            components: Vec::new(),
253            states: Vec::new(),
254            fns: Vec::new(),
255            imports: Vec::new(),
256            app_line: line,
257            has_title: false,
258            has_size: false,
259        };
260        loop {
261            self.skip_trivia();
262            if self.peek() == Some('}') {
263                self.bump();
264                break;
265            }
266            if self.peek().is_none() {
267                return Err(self.err(self.line, self.col, "unclosed `app` block"));
268            }
269            self.app_item(&mut app)?;
270        }
271        if !app.has_title {
272            return Err(self.err(line, col, "app wymaga `title: \"...\"`"));
273        }
274        if !app.has_size {
275            return Err(self.err(line, col, "app wymaga `size: (W, H)`"));
276        }
277        if app.windows.is_empty() {
278            return Err(self.err(line, col, "app wymaga min. 1 bloku `window`"));
279        }
280        Ok(app)
281    }
282
283    fn component_rest(
284        &mut self,
285        line: usize,
286        visibility: Visibility,
287    ) -> Result<ComponentDef, SeqError> {
288        let (name, _, _) = self.ident()?;
289        self.expect('(', "'(' po nazwie component")?;
290        let mut params = Vec::new();
291        loop {
292            self.skip_trivia();
293            if self.eat(')') {
294                break;
295            }
296            let (pname, pline, _) = self.ident()?;
297            self.expect(':', "':' po nazwie parametru")?;
298            let (pty, _, _) = self.ident()?;
299            params.push(CompParam {
300                name: pname,
301                line: pline,
302                ty: pty,
303            });
304            if !self.eat(',') {
305                self.expect(')', "')' po parametrach `component`")?;
306                break;
307            }
308        }
309        self.expect('{', "'{' after `component` header")?;
310        let mut body = Vec::new();
311        loop {
312            self.skip_trivia();
313            if self.peek() == Some('}') {
314                self.bump();
315                break;
316            }
317            if self.peek().is_none() {
318                return Err(self.err(self.line, self.col, "unclosed `component` block"));
319            }
320            body.push(self.node()?);
321        }
322        if body.is_empty() {
323            return Err(self.err(line, 1, "component requires at least 1 node"));
324        }
325        Ok(ComponentDef {
326            name,
327            line,
328            params,
329            body,
330            visibility,
331            file: self.file.to_string(),
332        })
333    }
334
335    fn app_item(&mut self, app: &mut SeqApp) -> Result<(), SeqError> {
336        let (kw, line, col) = self.ident()?;
337        match kw.as_str() {
338            "title" => {
339                self.expect(':', "':' po `title`")?;
340                let (s, _, _) = self.string()?;
341                app.title = s;
342                app.has_title = true;
343            }
344            "size" => {
345                self.expect(':', "':' po `size`")?;
346                app.size = self.size_tuple()?;
347                app.has_size = true;
348            }
349            "theme" => {
350                self.expect(':', "':' po `theme`")?;
351                app.theme = self.theme_name()?;
352            }
353            "window" => {
354                let w = self.window(line)?;
355                app.windows.push(w);
356            }
357            "prop" => {
358                let (name, _, _) = self.ident()?;
359                self.expect(':', "':' po nazwie prop")?;
360                let (ty, _, _) = self.ident()?;
361                app.deferred.push(DeferredDecl {
362                    kind: DeferredKind::Prop,
363                    name,
364                    line,
365                    sig: vec![ty],
366                });
367            }
368            "on" => {
369                let (name, _, _) = self.ident()?;
370                self.expect('(', "'(' po nazwie on")?;
371                let mut sig = Vec::new();
372                loop {
373                    self.skip_trivia();
374                    if self.eat(')') {
375                        break;
376                    }
377                    let (_, _, _) = self.ident()?;
378                    self.expect(':', "':' po nazwie parametru")?;
379                    let (ty, _, _) = self.ident()?;
380                    sig.push(ty);
381                    if !self.eat(',') {
382                        self.expect(')', "')' po parametrach `on`")?;
383                        break;
384                    }
385                }
386                app.deferred.push(DeferredDecl {
387                    kind: DeferredKind::On,
388                    name,
389                    line,
390                    sig,
391                });
392            }
393            "state" => {
394                let (name, _, _) = self.ident()?;
395                self.expect(':', "':' po nazwie state")?;
396                let ty = self.fn_type()?;
397                self.expect('=', "'=' po typie state")?;
398                let init = self.state_literal()?;
399                self.eat(';');
400                app.states.push(StateDecl {
401                    name,
402                    line,
403                    ty,
404                    init,
405                });
406            }
407            "fn" => {
408                let f = self.fn_decl(line)?;
409                app.fns.push(f);
410            }
411            "style" => {
412                let (name, _, _) = self.ident()?;
413                self.expect(':', "':' po nazwie stylu")?;
414                let (target, _, _) = self.ident()?;
415                let mut base = None;
416                let (save_pos, save_line, save_col) = (self.pos, self.line, self.col);
417                if let Ok((w, _, _)) = self.ident() {
418                    if w == "extends" {
419                        let (b, _, _) = self.ident()?;
420                        base = Some(b);
421                    } else {
422                        (self.pos, self.line, self.col) = (save_pos, save_line, save_col);
423                    }
424                } else {
425                    (self.pos, self.line, self.col) = (save_pos, save_line, save_col);
426                }
427                self.expect('{', "'{' po `style Nazwa: Typ`")?;
428                let args = self.style_args()?;
429                self.expect('}', "'}' po polach stylu")?;
430                app.styles.push(StyleDecl {
431                    name,
432                    line,
433                    target,
434                    base,
435                    args,
436                });
437            }
438            "component" => {
439                return Err(self.err(line, col, "blok component poza `app` (definiuj obok `app`)"));
440            }
441            "import" => {
442                return Err(self.err(
443                    line,
444                    col,
445                    "`import` tylko na poziomie pliku (poza blokiem `app`)",
446                ));
447            }
448            "pub" => {
449                return Err(self.err(
450                    line,
451                    col,
452                    "`pub` tylko przed `component` (poza blokiem `app`)",
453                ));
454            }
455            _ => {
456                return Err(self.err(
457                    line,
458                    col,
459                    format!(
460                        "nieznany element app '{kw}' (title/size/theme/window/prop/on/state/fn/style)"
461                    ),
462                ));
463            }
464        }
465        Ok(())
466    }
467
468    fn state_literal(&mut self) -> Result<SeqExpr, SeqError> {
469        self.skip_trivia();
470        let (line, col) = (self.line, self.col);
471        match self.peek() {
472            Some('"') => {
473                let (s, _, _) = self.string()?;
474                Ok(SeqExpr {
475                    line,
476                    kind: SeqExprKind::Str(s),
477                })
478            }
479            Some(c) if c == '-' || c.is_ascii_digit() => {
480                let (n, int, _, _) = self.number_expr()?;
481                Ok(SeqExpr {
482                    line,
483                    kind: if int {
484                        SeqExprKind::Int(n as i64)
485                    } else {
486                        SeqExprKind::Float(n)
487                    },
488                })
489            }
490            _ => {
491                let (id, _, _) = self.ident()?;
492                match id.as_str() {
493                    "true" => Ok(SeqExpr {
494                        line,
495                        kind: SeqExprKind::Bool(true),
496                    }),
497                    "false" => Ok(SeqExpr {
498                        line,
499                        kind: SeqExprKind::Bool(false),
500                    }),
501                    "None" => Ok(SeqExpr {
502                        line,
503                        kind: SeqExprKind::Var("None".to_string()),
504                    }),
505                    other => Err(self.err(
506                        line,
507                        col,
508                        format!(
509                            "state '{other}': initial value is literal (number/string/true/false)"
510                        ),
511                    )),
512                }
513            }
514        }
515    }
516
517    fn fn_decl(&mut self, line: usize) -> Result<SeqFn, SeqError> {
518        let (name, _, _) = self.ident()?;
519        self.expect('(', "'(' po nazwie funkcji")?;
520        let mut params = Vec::new();
521        loop {
522            self.skip_trivia();
523            if self.eat(')') {
524                break;
525            }
526            let mut mutable = false;
527            let (first, first_line, _) = self.ident()?;
528            let pname;
529            let pline;
530            if first == "mut" {
531                mutable = true;
532                let (n, l, _) = self.ident()?;
533                pname = n;
534                pline = l;
535            } else {
536                pname = first;
537                pline = first_line;
538            }
539            self.expect(':', "':' po nazwie parametru funkcji")?;
540            let ty = self.fn_type()?;
541            params.push(FnParam {
542                name: pname,
543                mutable,
544                line: pline,
545                ty,
546            });
547            if !self.eat(',') {
548                self.expect(')', "')' po parametrach funkcji")?;
549                break;
550            }
551        }
552        let mut ret = None;
553        if self.eat_arrow() {
554            ret = Some(self.fn_type()?);
555        }
556        let body = self.fn_block("funkcji")?;
557        Ok(SeqFn {
558            name,
559            line,
560            params,
561            ret,
562            body,
563        })
564    }
565
566    fn fn_type(&mut self) -> Result<String, SeqError> {
567        self.skip_trivia();
568        if self.peek() == Some('O') {
569            let save = (self.pos, self.line, self.col);
570            if let Ok((id, _, _)) = self.ident()
571                && id == "Option"
572            {
573                self.skip_trivia();
574                if self.eat('<') {
575                    let inner = self.fn_type()?;
576                    self.skip_trivia();
577                    self.expect('>', "'>' po typie Option")?;
578                    return Ok(format!("Option<{}>", inner));
579                }
580            } else {
581                (self.pos, self.line, self.col) = save;
582            }
583            (self.pos, self.line, self.col) = save;
584        }
585        if self.eat('(') {
586            let mut parts = vec![self.fn_type()?];
587            while self.eat(',') {
588                parts.push(self.fn_type()?);
589            }
590            self.expect(')', "')' po typie krotki")?;
591            Ok(format!("({})", parts.join(",")))
592        } else {
593            let (id, _, _) = self.ident()?;
594            Ok(id)
595        }
596    }
597
598    fn eat_arrow(&mut self) -> bool {
599        let save = (self.pos, self.line, self.col);
600        self.skip_trivia();
601        if self.peek() == Some('-') && self.chars.get(self.pos + 1) == Some(&'>') {
602            self.bump();
603            self.bump();
604            true
605        } else {
606            (self.pos, self.line, self.col) = save;
607            false
608        }
609    }
610
611    fn fn_block(&mut self, what: &str) -> Result<Vec<SeqStmt>, SeqError> {
612        self.depth += 1;
613        if self.depth > MAX_NESTING_DEPTH {
614            self.depth -= 1;
615            return Err(self.err(
616                self.line,
617                self.col,
618                format!("nesting too deep (limit {MAX_NESTING_DEPTH} levels)"),
619            ));
620        }
621        let result = self.fn_block_inner(what);
622        self.depth -= 1;
623        result
624    }
625
626    fn fn_block_inner(&mut self, what: &str) -> Result<Vec<SeqStmt>, SeqError> {
627        self.expect('{', format!("'{{' w ciele {what}").as_str())?;
628        let mut out = Vec::new();
629        loop {
630            self.skip_trivia();
631            if self.peek() == Some('}') {
632                self.bump();
633                break;
634            }
635            if self.peek().is_none() {
636                return Err(self.err(self.line, self.col, format!("unclosed block {what}")));
637            }
638            let stmt = self.fn_stmt()?;
639            if let Some(stmt) = stmt {
640                out.push(stmt);
641            }
642        }
643        Ok(out)
644    }
645
646    fn fn_stmt(&mut self) -> Result<Option<SeqStmt>, SeqError> {
647        self.skip_trivia();
648        if self.eat(';') {
649            return Ok(None);
650        }
651        let (kw, line, col) = self.ident()?;
652        match kw.as_str() {
653            "let" | "mut" => {
654                let (name, _, _) = self.ident()?;
655                self.expect('=', "'=' po nazwie zmiennej")?;
656                let value = self.fn_expr()?;
657                self.expect(';', "';' po `let`/`mut`")?;
658                Ok(Some(SeqStmt::Let {
659                    name,
660                    mutable: kw == "mut",
661                    value,
662                    line,
663                }))
664            }
665            "if" => Ok(Some(self.if_rest(line)?)),
666            "for" => {
667                let (var, _, _) = self.ident()?;
668                let (in_kw, l2, c2) = self.ident()?;
669                if in_kw != "in" {
670                    return Err(self.err(l2, c2, "'in' after loop variable `for x in rows`"));
671                }
672                let (source, _, _) = self.ident()?;
673                let body = self.fn_block("for loop")?;
674                Ok(Some(SeqStmt::For {
675                    var,
676                    source,
677                    body,
678                    line,
679                }))
680            }
681            "match" => self.match_rest(line),
682            "return" => {
683                self.skip_trivia();
684                let value = if self.peek() == Some(';') {
685                    None
686                } else {
687                    Some(self.fn_expr()?)
688                };
689                self.expect(';', "';' po `return`")?;
690                Ok(Some(SeqStmt::Return { value, line }))
691            }
692            _ => {
693                if self.eat_eq() {
694                    let value = self.fn_expr()?;
695                    self.expect(';', "';' po przypisaniu")?;
696                    return Ok(Some(SeqStmt::Assign {
697                        name: kw,
698                        value,
699                        line,
700                    }));
701                }
702                let base = SeqExpr {
703                    line,
704                    kind: SeqExprKind::Var(kw),
705                };
706                let expr = self.fn_postfix(base)?;
707                if matches!(expr.kind, SeqExprKind::Var(_) | SeqExprKind::Field { .. }) {
708                    return Err(self.err(
709                        line,
710                        col,
711                        "an expression as a statement is a function call (e.g. `connect();`)",
712                    ));
713                }
714                self.expect(';', "';' po instrukcji")?;
715                Ok(Some(SeqStmt::Expr(expr)))
716            }
717        }
718    }
719
720    fn if_rest(&mut self, line: usize) -> Result<SeqStmt, SeqError> {
721        self.expect('(', "'(' po `if`")?;
722        let cond = self.fn_expr()?;
723        self.expect(')', "')' po warunku `if`")?;
724        let then = self.fn_block("branch `if`")?;
725        let mut elifs = Vec::new();
726        let mut els = None;
727        loop {
728            if !self.peek_keyword("else") {
729                break;
730            }
731            let _ = self.ident();
732            if self.peek_keyword("if") {
733                let _ = self.ident();
734                self.expect('(', "'(' po `else if`")?;
735                let cond = self.fn_expr()?;
736                self.expect(')', "')' po warunku `else if`")?;
737                let body = self.fn_block("branch `else if`")?;
738                elifs.push((cond, body));
739            } else {
740                els = Some(self.fn_block("branch `else`")?);
741                break;
742            }
743        }
744        Ok(SeqStmt::If {
745            cond,
746            then,
747            elifs,
748            els,
749            line,
750        })
751    }
752
753    fn match_rest(&mut self, line: usize) -> Result<Option<SeqStmt>, SeqError> {
754        let value = self.fn_expr()?;
755        self.expect('{', "'{' after `match`")?;
756        let mut arms = Vec::new();
757        loop {
758            self.skip_trivia();
759            if self.peek() == Some('}') {
760                self.bump();
761                break;
762            }
763            if self.peek().is_none() {
764                return Err(self.err(self.line, self.col, "unclosed `match` block"));
765            }
766            let (aline, acol) = (self.line, self.col);
767            let pattern = match self.peek() {
768                Some('"') => MatchPat::Str(self.string()?.0),
769                Some(c) if c == '-' || c.is_ascii_digit() => {
770                    let (n, int, _, _) = self.number_expr()?;
771                    if !int {
772                        return Err(self.err(aline, acol, "match pattern is integer"));
773                    }
774                    #[allow(clippy::cast_possible_truncation)]
775                    MatchPat::Int(n as i64)
776                }
777                _ => {
778                    let (id, _, _) = self.ident()?;
779                    match id.as_str() {
780                        "true" => MatchPat::Bool(true),
781                        "false" => MatchPat::Bool(false),
782                        "_" => MatchPat::Wildcard,
783                        other => {
784                            return Err(self.err(
785                                aline,
786                                acol,
787                                format!(
788                                    "wzorzec match to \"tekst\"/liczba/true/false/_, nie '{other}'"
789                                ),
790                            ));
791                        }
792                    }
793                }
794            };
795            self.expect('=', "'=' we wzorcu match (`wzorzec => { ... }`)")?;
796            self.expect('>', "'>' we wzorcu match (`wzorzec => { ... }`)")?;
797            let body = self.fn_block("branch `match`")?;
798            arms.push(SeqMatchArm {
799                pattern,
800                body,
801                line: aline,
802            });
803            self.eat(',');
804        }
805        Ok(Some(SeqStmt::Match { value, arms, line }))
806    }
807
808    fn eat_eq(&mut self) -> bool {
809        let save = (self.pos, self.line, self.col);
810        self.skip_trivia();
811        if self.peek() == Some('=') && self.chars.get(self.pos + 1) != Some(&'=') {
812            self.bump();
813            true
814        } else {
815            (self.pos, self.line, self.col) = save;
816            false
817        }
818    }
819
820    fn peek_keyword(&mut self, kw: &str) -> bool {
821        let save = (self.pos, self.line, self.col);
822        let ok = matches!(self.ident(), Ok((w, _, _)) if w == kw);
823        (self.pos, self.line, self.col) = save;
824        ok
825    }
826
827    fn number_expr(&mut self) -> Result<(f64, bool, usize, usize), SeqError> {
828        let (line, col) = self.loc();
829        let start = self.pos;
830        if self.peek() == Some('-') {
831            self.bump();
832        }
833        let mut digits = 0;
834        while matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
835            self.bump();
836            digits += 1;
837        }
838        let mut int = true;
839        if self.peek() == Some('.') {
840            int = false;
841            self.bump();
842            while matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
843                self.bump();
844                digits += 1;
845            }
846        }
847        if digits == 0 {
848            return Err(self.err(line, col, "oczekiwano liczby"));
849        }
850        let text: String = self.chars[start..self.pos].iter().collect();
851        text.parse::<f64>()
852            .map(|v| (v, int, line, col))
853            .map_err(|_| self.err(line, col, format!("bad number '{text}'")))
854    }
855
856    fn peek_paren_trivia(&mut self) -> bool {
857        self.skip_trivia();
858        self.peek() == Some('(')
859    }
860
861    fn fn_or(&mut self) -> Result<SeqExpr, SeqError> {
862        let mut lhs = self.fn_and()?;
863        loop {
864            if !self.eat_op("||") {
865                break;
866            }
867            let line = self.line;
868            let rhs = self.fn_and()?;
869            lhs = SeqExpr {
870                line,
871                kind: SeqExprKind::Binary {
872                    op: BinOp::Or,
873                    lhs: Box::new(lhs),
874                    rhs: Box::new(rhs),
875                },
876            };
877        }
878        Ok(lhs)
879    }
880
881    fn fn_and(&mut self) -> Result<SeqExpr, SeqError> {
882        let mut lhs = self.fn_eq()?;
883        loop {
884            if !self.eat_op("&&") {
885                break;
886            }
887            let line = self.line;
888            let rhs = self.fn_eq()?;
889            lhs = SeqExpr {
890                line,
891                kind: SeqExprKind::Binary {
892                    op: BinOp::And,
893                    lhs: Box::new(lhs),
894                    rhs: Box::new(rhs),
895                },
896            };
897        }
898        Ok(lhs)
899    }
900
901    fn fn_eq(&mut self) -> Result<SeqExpr, SeqError> {
902        let mut lhs = self.fn_cmp()?;
903        loop {
904            let op = if self.eat_op("==") {
905                BinOp::Eq
906            } else if self.eat_op("!=") {
907                BinOp::Ne
908            } else {
909                break;
910            };
911            let line = self.line;
912            let rhs = self.fn_cmp()?;
913            lhs = SeqExpr {
914                line,
915                kind: SeqExprKind::Binary {
916                    op,
917                    lhs: Box::new(lhs),
918                    rhs: Box::new(rhs),
919                },
920            };
921        }
922        Ok(lhs)
923    }
924
925    fn fn_cmp(&mut self) -> Result<SeqExpr, SeqError> {
926        let mut lhs = self.fn_add()?;
927        loop {
928            let op = if self.eat_op("<=") {
929                BinOp::Le
930            } else if self.eat_op(">=") {
931                BinOp::Ge
932            } else if self.eat_op("<") {
933                BinOp::Lt
934            } else if self.eat_op(">") {
935                BinOp::Gt
936            } else {
937                break;
938            };
939            let line = self.line;
940            let rhs = self.fn_add()?;
941            lhs = SeqExpr {
942                line,
943                kind: SeqExprKind::Binary {
944                    op,
945                    lhs: Box::new(lhs),
946                    rhs: Box::new(rhs),
947                },
948            };
949        }
950        Ok(lhs)
951    }
952
953    fn fn_add(&mut self) -> Result<SeqExpr, SeqError> {
954        let mut lhs = self.fn_mul()?;
955        loop {
956            let op = if self.eat_op("+") {
957                BinOp::Add
958            } else if self.eat_op("-") {
959                BinOp::Sub
960            } else {
961                break;
962            };
963            let line = self.line;
964            let rhs = self.fn_mul()?;
965            lhs = SeqExpr {
966                line,
967                kind: SeqExprKind::Binary {
968                    op,
969                    lhs: Box::new(lhs),
970                    rhs: Box::new(rhs),
971                },
972            };
973        }
974        Ok(lhs)
975    }
976
977    fn fn_mul(&mut self) -> Result<SeqExpr, SeqError> {
978        let mut lhs = self.fn_unary()?;
979        loop {
980            let op = if self.eat_op("*") {
981                BinOp::Mul
982            } else if self.eat_op("/") {
983                BinOp::Div
984            } else if self.eat_op("%") {
985                BinOp::Rem
986            } else {
987                break;
988            };
989            let line = self.line;
990            let rhs = self.fn_unary()?;
991            lhs = SeqExpr {
992                line,
993                kind: SeqExprKind::Binary {
994                    op,
995                    lhs: Box::new(lhs),
996                    rhs: Box::new(rhs),
997                },
998            };
999        }
1000        Ok(lhs)
1001    }
1002
1003    fn fn_unary(&mut self) -> Result<SeqExpr, SeqError> {
1004        self.skip_trivia();
1005        let (line, _) = (self.line, self.col);
1006        if self.eat_op("!") {
1007            let expr = self.fn_unary()?;
1008            return Ok(SeqExpr {
1009                line,
1010                kind: SeqExprKind::Unary {
1011                    op: UnOp::Not,
1012                    expr: Box::new(expr),
1013                },
1014            });
1015        }
1016        if self.eat_op("-") {
1017            let expr = self.fn_unary()?;
1018            return Ok(SeqExpr {
1019                line,
1020                kind: SeqExprKind::Unary {
1021                    op: UnOp::Neg,
1022                    expr: Box::new(expr),
1023                },
1024            });
1025        }
1026        let primary = self.fn_primary()?;
1027        self.fn_postfix(primary)
1028    }
1029
1030    fn fn_postfix(&mut self, mut base: SeqExpr) -> Result<SeqExpr, SeqError> {
1031        loop {
1032            if self.eat('.') {
1033                self.skip_trivia();
1034                let (name, nline) = if matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
1035                    let nline = self.line;
1036                    let start = self.pos;
1037                    while matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
1038                        self.bump();
1039                    }
1040                    (
1041                        self.chars[start..self.pos].iter().collect::<String>(),
1042                        nline,
1043                    )
1044                } else {
1045                    let (name, line, _) = self.ident()?;
1046                    (name, line)
1047                };
1048                if self.peek_paren_trivia() {
1049                    self.expect('(', "'(' po metodzie")?;
1050                    let args = self.fn_call_args()?;
1051                    let mut all = vec![base];
1052                    all.extend(args);
1053                    base = SeqExpr {
1054                        line: nline,
1055                        kind: SeqExprKind::Call { name, args: all },
1056                    };
1057                } else {
1058                    let SeqExprKind::Var(var) = base.kind else {
1059                        return Err(self.err(
1060                            nline,
1061                            self.col,
1062                            "field only on loop variable (e.g. `x.id`)",
1063                        ));
1064                    };
1065                    base = SeqExpr {
1066                        line: nline,
1067                        kind: SeqExprKind::Field {
1068                            base: var,
1069                            field: name,
1070                        },
1071                    };
1072                }
1073            } else if self.peek_paren_trivia() {
1074                self.skip_trivia();
1075                let line = self.line;
1076                self.expect('(', "'(' po nazwie funkcji")?;
1077                let args = self.fn_call_args()?;
1078                let SeqExprKind::Var(name) = base.kind else {
1079                    return Err(self.err(line, self.col, "you can call a function by name"));
1080                };
1081                base = SeqExpr {
1082                    line,
1083                    kind: SeqExprKind::Call { name, args },
1084                };
1085            } else {
1086                break;
1087            }
1088        }
1089        Ok(base)
1090    }
1091
1092    fn fn_call_args(&mut self) -> Result<Vec<SeqExpr>, SeqError> {
1093        let mut args = Vec::new();
1094        self.skip_trivia();
1095        if self.eat(')') {
1096            return Ok(args);
1097        }
1098        args.push(self.fn_expr()?);
1099        loop {
1100            if !self.eat(',') {
1101                break;
1102            }
1103            self.skip_trivia();
1104            if self.eat(')') {
1105                return Ok(args);
1106            }
1107            args.push(self.fn_expr()?);
1108        }
1109        self.expect(')', "')' after call arguments")?;
1110        Ok(args)
1111    }
1112
1113    fn fn_primary(&mut self) -> Result<SeqExpr, SeqError> {
1114        self.depth += 1;
1115        if self.depth > MAX_NESTING_DEPTH {
1116            self.depth -= 1;
1117            return Err(self.err(
1118                self.line,
1119                self.col,
1120                format!("nesting too deep (limit {MAX_NESTING_DEPTH} levels)"),
1121            ));
1122        }
1123        let result = self.fn_primary_inner();
1124        self.depth -= 1;
1125        result
1126    }
1127
1128    fn fn_primary_inner(&mut self) -> Result<SeqExpr, SeqError> {
1129        self.skip_trivia();
1130        let (line, col) = (self.line, self.col);
1131        match self.peek() {
1132            Some('"') => {
1133                let (s, _, _) = self.string()?;
1134                Ok(SeqExpr {
1135                    line,
1136                    kind: SeqExprKind::Str(s),
1137                })
1138            }
1139            Some(c) if c.is_ascii_digit() => {
1140                let (n, int, _, _) = self.number_expr()?;
1141                Ok(SeqExpr {
1142                    line,
1143                    kind: if int {
1144                        #[allow(clippy::cast_possible_truncation)]
1145                        SeqExprKind::Int(n as i64)
1146                    } else {
1147                        SeqExprKind::Float(n)
1148                    },
1149                })
1150            }
1151            Some('(') => {
1152                self.bump();
1153                let first = self.fn_expr()?;
1154                self.skip_trivia();
1155                if self.eat(',') {
1156                    let mut items = vec![first];
1157                    loop {
1158                        self.skip_trivia();
1159                        if self.eat(')') {
1160                            break;
1161                        }
1162                        items.push(self.fn_expr()?);
1163                        if !self.eat(',') {
1164                            self.expect(')', "')' po krotce `(a, b)`")?;
1165                            break;
1166                        }
1167                    }
1168                    Ok(SeqExpr {
1169                        line,
1170                        kind: SeqExprKind::Tuple(items),
1171                    })
1172                } else {
1173                    self.expect(')', "')' after the expression in brackets")?;
1174                    Ok(first)
1175                }
1176            }
1177            Some('_') => Err(self.err(line, col, "`_` tylko jako wzorzec `match`")),
1178            _ => {
1179                let (id, _, _) = self.ident()?;
1180                Ok(SeqExpr {
1181                    line,
1182                    kind: match id.as_str() {
1183                        "true" => SeqExprKind::Bool(true),
1184                        "false" => SeqExprKind::Bool(false),
1185                        _ => SeqExprKind::Var(id),
1186                    },
1187                })
1188            }
1189        }
1190    }
1191
1192    fn eat_op(&mut self, s: &str) -> bool {
1193        self.skip_trivia();
1194        for (i, c) in s.chars().enumerate() {
1195            if self.chars.get(self.pos + i) != Some(&c) {
1196                return false;
1197            }
1198        }
1199        for _ in s.chars() {
1200            self.bump();
1201        }
1202        true
1203    }
1204
1205    fn fn_expr(&mut self) -> Result<SeqExpr, SeqError> {
1206        self.fn_or()
1207    }
1208
1209    fn theme_name(&mut self) -> Result<ThemeName, SeqError> {
1210        let (line, col) = self.loc();
1211        match self.peek() {
1212            Some('"') => {
1213                let (s, _, _) = self.string()?;
1214                match s.as_str() {
1215                    "dark" => Ok(ThemeName::Dark),
1216                    "light" => Ok(ThemeName::Light),
1217                    _ => Ok(ThemeName::File(s)),
1218                }
1219            }
1220            _ => {
1221                let (id, _, _) = self.ident()?;
1222                match id.as_str() {
1223                    "dark" => Ok(ThemeName::Dark),
1224                    "light" => Ok(ThemeName::Light),
1225                    _ => Err(self.err(line, col, "theme to dark/light albo \"plik-tokens.json\"")),
1226                }
1227            }
1228        }
1229    }
1230
1231    fn size_tuple(&mut self) -> Result<(f32, f32), SeqError> {
1232        self.expect('(', "'(' przed rozmiarem")?;
1233        let (w, _, _) = self.number()?;
1234        self.expect(',', "',' between W and H")?;
1235        let (h, _, _) = self.number()?;
1236        self.expect(')', "')' po rozmiarze")?;
1237        if !(w.is_finite() && h.is_finite() && w > 0.0 && h > 0.0) {
1238            let (l, c) = (self.line, self.col);
1239            return Err(self.err(l, c, "size must be positive and finite"));
1240        }
1241        Ok((w, h))
1242    }
1243
1244    fn style_args(&mut self) -> Result<Vec<SeqArg>, SeqError> {
1245        let mut args = Vec::new();
1246        loop {
1247            self.skip_trivia();
1248            if self.peek() == Some('}') {
1249                break;
1250            }
1251            if self.peek().is_none() {
1252                return Err(self.err(self.line, self.col, "unclosed `style` block"));
1253            }
1254            let (name, line, _) = self.ident()?;
1255            self.expect(':', format!("':' po polu stylu '{name}'").as_str())?;
1256            let value = self.value()?;
1257            args.push(SeqArg { name, line, value });
1258
1259            self.eat(',');
1260        }
1261        Ok(args)
1262    }
1263
1264    fn window(&mut self, line: usize) -> Result<SeqWindow, SeqError> {
1265        let (name, _, _) = self.ident()?;
1266        let mut size = None;
1267        if self.eat('(') {
1268            let (kw, l, c) = self.ident()?;
1269            if kw != "size" {
1270                return Err(self.err(l, c, "w nawiasie window dozwolone tylko `size: (W, H)`"));
1271            }
1272            self.expect(':', "':' po `size`")?;
1273            size = Some(self.size_tuple()?);
1274            self.expect(')', "')' po `window Nazwa (...)`")?;
1275        }
1276        self.expect('{', "'{' after `window` header")?;
1277        let mut nodes = Vec::new();
1278        loop {
1279            self.skip_trivia();
1280            if self.peek() == Some('}') {
1281                self.bump();
1282                break;
1283            }
1284            if self.peek().is_none() {
1285                return Err(self.err(self.line, self.col, "unclosed `window` block"));
1286            }
1287            nodes.push(self.node()?);
1288        }
1289        Ok(SeqWindow {
1290            name,
1291            line,
1292            size,
1293            nodes,
1294        })
1295    }
1296
1297    fn node(&mut self) -> Result<SeqNode, SeqError> {
1298        self.depth += 1;
1299        if self.depth > MAX_NESTING_DEPTH {
1300            self.depth -= 1;
1301            return Err(self.err(
1302                self.line,
1303                self.col,
1304                format!("nesting too deep (limit {MAX_NESTING_DEPTH} levels)"),
1305            ));
1306        }
1307        let result = self.node_inner();
1308        self.depth -= 1;
1309        result
1310    }
1311
1312    fn node_inner(&mut self) -> Result<SeqNode, SeqError> {
1313        let (kind, line, _) = self.ident()?;
1314        let mut label = None;
1315        let mut args = Vec::new();
1316
1317        if self.eat('(') {
1318            self.skip_trivia();
1319            if self.peek() == Some('"') {
1320                let (s, _, _) = self.string()?;
1321                label = Some(s);
1322                self.skip_trivia();
1323                if self.eat(',') {
1324                    self.skip_trivia();
1325                    if self.peek() != Some(')') {
1326                        args = self.named_args()?;
1327                    }
1328                }
1329            } else if self.peek() != Some(')') {
1330                args = self.named_args()?;
1331            }
1332            self.expect(')', "')' po atrybutach")?;
1333        }
1334        let mut children = Vec::new();
1335        if self.eat('{') {
1336            loop {
1337                self.skip_trivia();
1338                if self.peek() == Some('}') {
1339                    self.bump();
1340                    break;
1341                }
1342                if self.peek().is_none() {
1343                    return Err(self.err(self.line, self.col, "unclosed children block `{ }`"));
1344                }
1345                children.push(self.node()?);
1346            }
1347        }
1348        Ok(SeqNode {
1349            kind,
1350            line,
1351            label,
1352            args,
1353            children,
1354        })
1355    }
1356
1357    fn named_args(&mut self) -> Result<Vec<SeqArg>, SeqError> {
1358        let mut args = Vec::new();
1359        loop {
1360            let (name, line, _) = self.ident()?;
1361            self.expect(':', format!("':' po atrybucie '{name}'").as_str())?;
1362            let value = self.value()?;
1363            args.push(SeqArg { name, line, value });
1364            if !self.eat(',') {
1365                break;
1366            }
1367            self.skip_trivia();
1368            if self.peek() == Some(')') {
1369                break;
1370            }
1371        }
1372        Ok(args)
1373    }
1374
1375    fn peek_paren(&self) -> bool {
1376        let mut i = self.pos;
1377        while let Some(&c) = self.chars.get(i) {
1378            if c == ' ' || c == '\t' || c == '\r' || c == '\n' {
1379                i += 1;
1380            } else {
1381                return c == '(';
1382            }
1383        }
1384        false
1385    }
1386
1387    fn value(&mut self) -> Result<SeqValue, SeqError> {
1388        self.depth += 1;
1389        if self.depth > MAX_NESTING_DEPTH {
1390            self.depth -= 1;
1391            return Err(self.err(
1392                self.line,
1393                self.col,
1394                format!("nesting too deep (limit {MAX_NESTING_DEPTH} levels)"),
1395            ));
1396        }
1397        let result = self.value_inner();
1398        self.depth -= 1;
1399        result
1400    }
1401
1402    fn value_inner(&mut self) -> Result<SeqValue, SeqError> {
1403        self.skip_trivia();
1404        Ok(match self.peek() {
1405            Some('"') => {
1406                let (s, _, _) = self.string()?;
1407                SeqValue::Str(s)
1408            }
1409            Some(c) if c == '-' || c.is_ascii_digit() => {
1410                let (n, _, _) = self.number()?;
1411                SeqValue::Num(n)
1412            }
1413            Some('#') => {
1414                let (hex, _, _) = self.color()?;
1415                SeqValue::Color(hex)
1416            }
1417            Some('{') => return self.callback(),
1418            Some('(') => {
1419                self.bump();
1420                let mut items = vec![self.value()?];
1421                while self.eat(',') {
1422                    self.skip_trivia();
1423                    if self.peek() == Some(')') {
1424                        break;
1425                    }
1426                    items.push(self.value()?);
1427                }
1428                self.expect(')', "')' po krotce `(a, b)`")?;
1429                SeqValue::Tuple(items)
1430            }
1431            Some('[') => {
1432                self.bump();
1433                let mut items = vec![self.value()?];
1434                while self.eat(',') {
1435                    self.skip_trivia();
1436                    if self.peek() == Some(']') {
1437                        break;
1438                    }
1439                    items.push(self.value()?);
1440                }
1441                self.expect(']', "']' after list `[a, b]`")?;
1442                SeqValue::Array(items)
1443            }
1444            _ => {
1445                let (id, _, _) = self.ident()?;
1446                match id.as_str() {
1447                    "true" => SeqValue::Bool(true),
1448                    "false" => SeqValue::Bool(false),
1449                    "fit" => SeqValue::Length(LengthVal::Fit),
1450
1451                    "fill" if self.peek_paren() => {
1452                        return self.length_paren(LengthVal::Fill, "fill(waga)");
1453                    }
1454                    "px" if self.peek_paren() => {
1455                        return self.length_paren(LengthVal::Px, "px(liczba)");
1456                    }
1457                    "percent" if self.peek_paren() => {
1458                        return self.length_paren(LengthVal::Percent, "percent(liczba)");
1459                    }
1460                    "dynamic" => {
1461                        self.expect('(', "'(' po `dynamic`")?;
1462                        let (name, _, _) = self.ident()?;
1463                        self.expect(')', "')' po `dynamic(nazwa)`")?;
1464                        SeqValue::Dynamic(name)
1465                    }
1466                    "theme" => {
1467                        self.expect('.', "'.' po `theme` (theme.pole)")?;
1468                        let (field, _, _) = self.ident()?;
1469                        SeqValue::Theme(field)
1470                    }
1471                    "env" => {
1472                        self.expect('.', "'.' po `env` (env.nazwa)")?;
1473                        let (field, _, _) = self.ident()?;
1474                        SeqValue::Env(field)
1475                    }
1476                    "item" => {
1477                        self.expect('.', "'.' po `item` (item.pole)")?;
1478                        let (field, _, _) = self.ident()?;
1479                        SeqValue::ItemField(field)
1480                    }
1481                    _ => SeqValue::Ident(id),
1482                }
1483            }
1484        })
1485    }
1486
1487    fn length_paren(&mut self, f: fn(f32) -> LengthVal, what: &str) -> Result<SeqValue, SeqError> {
1488        self.expect('(', format!("'(' — {what}").as_str())?;
1489        let (n, _, _) = self.number()?;
1490        self.expect(')', format!("')' — {what}").as_str())?;
1491        Ok(SeqValue::Length(f(n)))
1492    }
1493
1494    fn callback(&mut self) -> Result<SeqValue, SeqError> {
1495        debug_assert_eq!(self.peek(), Some('{'));
1496        self.bump();
1497        self.skip_trivia();
1498        let (action, _, _) = self.ident()?;
1499        self.expect('(', "'(' po nazwie callbacku")?;
1500        let mut args = Vec::new();
1501        self.skip_trivia();
1502        if self.peek() != Some(')') {
1503            args.push(self.value()?);
1504            while self.eat(',') {
1505                self.skip_trivia();
1506                if self.peek() == Some(')') {
1507                    break;
1508                }
1509                args.push(self.value()?);
1510            }
1511        }
1512        self.expect(')', "')' po argumencie callbacku")?;
1513        self.skip_trivia();
1514        if self.peek() == Some('}') {
1515            self.bump();
1516        } else {
1517            let (l, c) = (self.line, self.col);
1518            return Err(self.err(l, c, "'}' po callbacku `{ akcja() }`"));
1519        }
1520        Ok(SeqValue::Callback { action, args })
1521    }
1522}