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            anims: Vec::new(),
256            machines: Vec::new(),
257            springs: Vec::new(),
258            imports: Vec::new(),
259            app_line: line,
260            has_title: false,
261            has_size: false,
262        };
263        loop {
264            self.skip_trivia();
265            if self.peek() == Some('}') {
266                self.bump();
267                break;
268            }
269            if self.peek().is_none() {
270                return Err(self.err(self.line, self.col, "unclosed `app` block"));
271            }
272            self.app_item(&mut app)?;
273        }
274        if !app.has_title {
275            return Err(self.err(line, col, "app wymaga `title: \"...\"`"));
276        }
277        if !app.has_size {
278            return Err(self.err(line, col, "app wymaga `size: (W, H)`"));
279        }
280        if app.windows.is_empty() {
281            return Err(self.err(line, col, "app wymaga min. 1 bloku `window`"));
282        }
283        Ok(app)
284    }
285
286    fn component_rest(
287        &mut self,
288        line: usize,
289        visibility: Visibility,
290    ) -> Result<ComponentDef, SeqError> {
291        let (name, _, _) = self.ident()?;
292        self.expect('(', "'(' po nazwie component")?;
293        let mut params = Vec::new();
294        loop {
295            self.skip_trivia();
296            if self.eat(')') {
297                break;
298            }
299            let (pname, pline, _) = self.ident()?;
300            self.expect(':', "':' po nazwie parametru")?;
301            let (pty, _, _) = self.ident()?;
302            params.push(CompParam {
303                name: pname,
304                line: pline,
305                ty: pty,
306            });
307            if !self.eat(',') {
308                self.expect(')', "')' po parametrach `component`")?;
309                break;
310            }
311        }
312        self.expect('{', "'{' after `component` header")?;
313        let mut body = Vec::new();
314        loop {
315            self.skip_trivia();
316            if self.peek() == Some('}') {
317                self.bump();
318                break;
319            }
320            if self.peek().is_none() {
321                return Err(self.err(self.line, self.col, "unclosed `component` block"));
322            }
323            body.push(self.node()?);
324        }
325        if body.is_empty() {
326            return Err(self.err(line, 1, "component requires at least 1 node"));
327        }
328        Ok(ComponentDef {
329            name,
330            line,
331            params,
332            body,
333            visibility,
334            file: self.file.to_string(),
335        })
336    }
337
338    fn anim_decl(&mut self, line: usize) -> Result<crate::ir::AnimDecl, SeqError> {
339        use crate::ir::AnimDecl;
340        use crate::ir::AnimTrackDecl;
341        let (name, _, _) = self.ident()?;
342        self.expect('{', "'{' po anim Nazwa")?;
343        let mut duration = None;
344        let mut speed = None;
345        let mut loop_mode = None;
346        let mut tracks = Vec::new();
347        loop {
348            self.skip_trivia();
349            if self.peek() == Some('}') {
350                self.bump();
351                break;
352            }
353            if self.peek().is_none() {
354                return Err(self.err(self.line, self.col, "niezamkniety blok anim"));
355            }
356            let (kw, l, _) = self.ident()?;
357            match kw.as_str() {
358                "duration" => {
359                    self.expect(':', "':' po duration")?;
360                    let (n, _, _) = self.number()?;
361                    duration = Some(n);
362                }
363                "speed" => {
364                    self.expect(':', "':' po speed")?;
365                    let (n, _, _) = self.number()?;
366                    speed = Some(n);
367                }
368                "loop" => {
369                    self.expect(':', "':' po loop")?;
370                    let (m, _, _) = self.ident()?;
371                    loop_mode = Some(m);
372                }
373                "track" => {
374                    let (tname, _, _) = self.ident()?;
375                    self.expect(':', "':' po nazwie toru")?;
376                    let val = self.value()?;
377                    tracks.push(AnimTrackDecl {
378                        name: tname,
379                        line: l,
380                        value: val,
381                    });
382                }
383                _ => return Err(self.err(l, self.col, format!("nieznany element anim '{}'", kw))),
384            }
385            self.eat(';');
386        }
387        Ok(AnimDecl {
388            name,
389            line,
390            duration,
391            speed,
392            loop_mode,
393            tracks,
394        })
395    }
396
397    fn capture_balanced(&mut self) -> Result<String, SeqError> {
398        self.skip_trivia();
399        if self.peek() != Some('(') {
400            return Err(self.err(self.line, self.col, "oczekiwano '('"));
401        }
402        let mut buf = String::new();
403        let mut depth = 0usize;
404        while let Some(c) = self.peek() {
405            buf.push(c);
406            self.bump();
407            if c == '(' {
408                depth += 1;
409            } else if c == ')' {
410                depth -= 1;
411                if depth == 0 {
412                    break;
413                }
414            }
415        }
416        if depth != 0 {
417            return Err(self.err(self.line, self.col, "niezamkniety nawias"));
418        }
419        Ok(buf)
420    }
421
422    fn machine_decl(&mut self, line: usize) -> Result<crate::ir::MachineDecl, SeqError> {
423        use crate::ir::MachineDecl;
424        use crate::ir::MachineStateDecl;
425        use crate::ir::MachineTransitionDecl;
426        let (name, _, _) = self.ident()?;
427        self.expect('{', "'{' po machine Nazwa")?;
428        let mut states = Vec::new();
429        let mut transitions = Vec::new();
430        loop {
431            self.skip_trivia();
432            if self.peek() == Some('}') {
433                self.bump();
434                break;
435            }
436            if self.peek().is_none() {
437                return Err(self.err(self.line, self.col, "niezamkniety blok machine"));
438            }
439            let (kw, l, _) = self.ident()?;
440            match kw.as_str() {
441                "state" => {
442                    let (sname, _, _) = self.ident()?;
443                    let mut kind = String::from("anim");
444                    let mut target = None;
445                    let args = Vec::new();
446                    self.skip_trivia();
447                    if self.peek() == Some(':') {
448                        self.bump();
449                        self.skip_trivia();
450                        let (k, _, _) = self.ident()?;
451                        if k == "anim" {
452                            let (t, _, _) = self.ident()?;
453                            target = Some(t);
454                            kind = String::from("anim");
455                        } else if k == "pose" {
456                            let raw = self.capture_balanced()?;
457                            target = Some(raw);
458                            kind = String::from("pose");
459                        } else {
460                            kind = k;
461                        }
462                    } else {
463                        if sname == "entry" || sname == "exit" {
464                            kind = sname.clone();
465                        }
466                    }
467                    states.push(MachineStateDecl {
468                        name: sname,
469                        line: l,
470                        kind,
471                        target,
472                        args,
473                    });
474                }
475                "transition" => {
476                    let (from, _, _) = self.ident()?;
477                    self.skip_trivia();
478                    if !self.eat('-') {
479                        return Err(self.err(self.line, self.col, "oczekiwano '->'"));
480                    }
481                    if !self.eat('>') {
482                        return Err(self.err(self.line, self.col, "oczekiwano '->'"));
483                    }
484                    let (to, _, _) = self.ident()?;
485                    let mut conditions = Vec::new();
486                    let mut mix = None;
487                    self.skip_trivia();
488                    let save = (self.pos, self.line, self.col);
489                    let mut on = false;
490                    if let Ok((w, _, _)) = self.ident() {
491                        if w == "on" {
492                            on = true;
493                        } else {
494                            (self.pos, self.line, self.col) = save;
495                        }
496                    } else {
497                        (self.pos, self.line, self.col) = save;
498                    }
499                    if on {
500                        loop {
501                            self.skip_trivia();
502                            if self.peek() == Some('}') {
503                                break;
504                            }
505                            let save2 = (self.pos, self.line, self.col);
506                            if let Ok((w, _, _)) = self.ident() {
507                                if w == "mix" {
508                                    self.expect(':', "':' po mix")?;
509                                    let (n, _, _) = self.number()?;
510                                    mix = Some(n);
511                                    break;
512                                }
513                            }
514                            (self.pos, self.line, self.col) = save2;
515                            let cond = self.parse_condition()?;
516                            conditions.push(cond);
517                            self.skip_trivia();
518                            if !self.eat(',') {
519                                let save3 = (self.pos, self.line, self.col);
520                                if let Ok((w, _, _)) = self.ident() {
521                                    if w == "mix" {
522                                        self.expect(':', "':' po mix")?;
523                                        let (n, _, _) = self.number()?;
524                                        mix = Some(n);
525                                    } else {
526                                        (self.pos, self.line, self.col) = save3;
527                                    }
528                                } else {
529                                    (self.pos, self.line, self.col) = save3;
530                                }
531                                break;
532                            }
533                        }
534                    }
535                    self.skip_trivia();
536                    let save4 = (self.pos, self.line, self.col);
537                    if let Ok((w, _, _)) = self.ident() {
538                        if w == "mix" {
539                            self.expect(':', "':' po mix")?;
540                            let (n, _, _) = self.number()?;
541                            mix = Some(n);
542                        } else {
543                            (self.pos, self.line, self.col) = save4;
544                        }
545                    } else {
546                        (self.pos, self.line, self.col) = save4;
547                    }
548                    transitions.push(MachineTransitionDecl {
549                        from,
550                        to,
551                        line: l,
552                        conditions,
553                        mix,
554                    });
555                }
556                _ => {
557                    return Err(self.err(
558                        l,
559                        self.col,
560                        format!("nieznany element machine '{}'", kw),
561                    ));
562                }
563            }
564            self.eat(';');
565        }
566        Ok(MachineDecl {
567            name,
568            line,
569            states,
570            transitions,
571        })
572    }
573
574    fn parse_condition(&mut self) -> Result<String, SeqError> {
575        let (name, _, _) = self.ident()?;
576        self.expect('(', "'(' po nazwie warunku")?;
577        let mut buf = String::new();
578        buf.push_str(&name);
579        buf.push('(');
580        let mut depth = 1usize;
581        while depth > 0 {
582            match self.peek() {
583                None => return Err(self.err(self.line, self.col, "niezamkniety warunek")),
584                Some('(') => {
585                    buf.push('(');
586                    self.bump();
587                    depth += 1;
588                }
589                Some(')') => {
590                    buf.push(')');
591                    self.bump();
592                    depth -= 1;
593                }
594                Some(c) => {
595                    buf.push(c);
596                    self.bump();
597                }
598            }
599        }
600        Ok(buf)
601    }
602
603    fn spring_decl(&mut self, line: usize) -> Result<crate::ir::SpringDecl, SeqError> {
604        use crate::ir::SpringDecl;
605        use crate::ir::SpringStateDecl;
606        use crate::ir::SpringTransitionDecl;
607        let (name, _, _) = self.ident()?;
608        self.expect('{', "'{' po spring Nazwa")?;
609        let mut states = Vec::new();
610        let mut transitions = Vec::new();
611        loop {
612            self.skip_trivia();
613            if self.peek() == Some('}') {
614                self.bump();
615                break;
616            }
617            if self.peek().is_none() {
618                return Err(self.err(self.line, self.col, "niezamkniety blok spring"));
619            }
620            let (kw, l, _) = self.ident()?;
621            match kw.as_str() {
622                "state" => {
623                    let (sname, _, _) = self.ident()?;
624                    self.expect(':', "':' po nazwie stanu")?;
625                    self.skip_trivia();
626                    let val = if self.peek() == Some('(') {
627                        let raw = self.capture_balanced()?;
628                        crate::ir::SeqValue::Str(raw)
629                    } else {
630                        self.value()?
631                    };
632                    states.push(SpringStateDecl {
633                        name: sname,
634                        line: l,
635                        value: val,
636                    });
637                }
638                "transition" => {
639                    let (from, _, _) = self.ident()?;
640                    self.skip_trivia();
641                    if !self.eat('-') {
642                        return Err(self.err(self.line, self.col, "oczekiwano '->'"));
643                    }
644                    if !self.eat('>') {
645                        return Err(self.err(self.line, self.col, "oczekiwano '->'"));
646                    }
647                    let (to, _, _) = self.ident()?;
648                    self.skip_trivia();
649                    let mut trigger = String::new();
650                    let save = (self.pos, self.line, self.col);
651                    if let Ok((w, _, _)) = self.ident() {
652                        if w == "on" {
653                            self.skip_trivia();
654                            if self.peek() == Some('"') {
655                                let (s, _, _) = self.string()?;
656                                trigger = s;
657                            } else {
658                                let (t, _, _) = self.ident()?;
659                                trigger = t;
660                            }
661                        } else {
662                            (self.pos, self.line, self.col) = save;
663                        }
664                    } else {
665                        (self.pos, self.line, self.col) = save;
666                    }
667                    transitions.push(SpringTransitionDecl {
668                        from,
669                        to,
670                        line: l,
671                        trigger,
672                    });
673                }
674                _ => {
675                    return Err(self.err(l, self.col, format!("nieznany element spring '{}'", kw)));
676                }
677            }
678            self.eat(';');
679        }
680        Ok(SpringDecl {
681            name,
682            line,
683            states,
684            transitions,
685        })
686    }
687
688    fn app_item(&mut self, app: &mut SeqApp) -> Result<(), SeqError> {
689        let (kw, line, col) = self.ident()?;
690        match kw.as_str() {
691            "title" => {
692                self.expect(':', "':' po `title`")?;
693                let (s, _, _) = self.string()?;
694                app.title = s;
695                app.has_title = true;
696            }
697            "size" => {
698                self.expect(':', "':' po `size`")?;
699                app.size = self.size_tuple()?;
700                app.has_size = true;
701            }
702            "theme" => {
703                self.expect(':', "':' po `theme`")?;
704                app.theme = self.theme_name()?;
705            }
706            "window" => {
707                let w = self.window(line)?;
708                app.windows.push(w);
709            }
710            "prop" => {
711                let (name, _, _) = self.ident()?;
712                self.expect(':', "':' po nazwie prop")?;
713                let (ty, _, _) = self.ident()?;
714                app.deferred.push(DeferredDecl {
715                    kind: DeferredKind::Prop,
716                    name,
717                    line,
718                    sig: vec![ty],
719                });
720            }
721            "on" => {
722                let (name, _, _) = self.ident()?;
723                self.expect('(', "'(' po nazwie on")?;
724                let mut sig = Vec::new();
725                loop {
726                    self.skip_trivia();
727                    if self.eat(')') {
728                        break;
729                    }
730                    let (_, _, _) = self.ident()?;
731                    self.expect(':', "':' po nazwie parametru")?;
732                    let (ty, _, _) = self.ident()?;
733                    sig.push(ty);
734                    if !self.eat(',') {
735                        self.expect(')', "')' po parametrach `on`")?;
736                        break;
737                    }
738                }
739                app.deferred.push(DeferredDecl {
740                    kind: DeferredKind::On,
741                    name,
742                    line,
743                    sig,
744                });
745            }
746            "state" => {
747                let (name, _, _) = self.ident()?;
748                self.expect(':', "':' po nazwie state")?;
749                let ty = self.fn_type()?;
750                self.expect('=', "'=' po typie state")?;
751                let init = self.state_literal()?;
752                self.eat(';');
753                app.states.push(StateDecl {
754                    name,
755                    line,
756                    ty,
757                    init,
758                });
759            }
760            "fn" => {
761                let f = self.fn_decl(line)?;
762                app.fns.push(f);
763            }
764            "style" => {
765                let (name, _, _) = self.ident()?;
766                self.expect(':', "':' po nazwie stylu")?;
767                let (target, _, _) = self.ident()?;
768                let mut base = None;
769                let (save_pos, save_line, save_col) = (self.pos, self.line, self.col);
770                if let Ok((w, _, _)) = self.ident() {
771                    if w == "extends" {
772                        let (b, _, _) = self.ident()?;
773                        base = Some(b);
774                    } else {
775                        (self.pos, self.line, self.col) = (save_pos, save_line, save_col);
776                    }
777                } else {
778                    (self.pos, self.line, self.col) = (save_pos, save_line, save_col);
779                }
780                self.expect('{', "'{' po `style Nazwa: Typ`")?;
781                let args = self.style_args()?;
782                self.expect('}', "'}' po polach stylu")?;
783                app.styles.push(StyleDecl {
784                    name,
785                    line,
786                    target,
787                    base,
788                    args,
789                });
790            }
791            "anim" => {
792                let a = self.anim_decl(line)?;
793                app.anims.push(a);
794            }
795            "machine" => {
796                let m = self.machine_decl(line)?;
797                app.machines.push(m);
798            }
799            "spring" => {
800                let sp = self.spring_decl(line)?;
801                app.springs.push(sp);
802            }
803            "component" => {
804                return Err(self.err(line, col, "blok component poza `app` (definiuj obok `app`)"));
805            }
806            "import" => {
807                return Err(self.err(
808                    line,
809                    col,
810                    "`import` tylko na poziomie pliku (poza blokiem `app`)",
811                ));
812            }
813            "pub" => {
814                return Err(self.err(
815                    line,
816                    col,
817                    "`pub` tylko przed `component` (poza blokiem `app`)",
818                ));
819            }
820            _ => {
821                return Err(self.err(
822                    line,
823                    col,
824                    format!(
825                        "nieznany element app '{kw}' (title/size/theme/window/prop/on/state/fn/style)"
826                    ),
827                ));
828            }
829        }
830        Ok(())
831    }
832
833    fn state_literal(&mut self) -> Result<SeqExpr, SeqError> {
834        self.skip_trivia();
835        let (line, col) = (self.line, self.col);
836        match self.peek() {
837            Some('"') => {
838                let (s, _, _) = self.string()?;
839                Ok(SeqExpr {
840                    line,
841                    kind: SeqExprKind::Str(s),
842                })
843            }
844            Some(c) if c == '-' || c.is_ascii_digit() => {
845                let (n, int, _, _) = self.number_expr()?;
846                Ok(SeqExpr {
847                    line,
848                    kind: if int {
849                        SeqExprKind::Int(n as i64)
850                    } else {
851                        SeqExprKind::Float(n)
852                    },
853                })
854            }
855            _ => {
856                let (id, _, _) = self.ident()?;
857                match id.as_str() {
858                    "true" => Ok(SeqExpr {
859                        line,
860                        kind: SeqExprKind::Bool(true),
861                    }),
862                    "false" => Ok(SeqExpr {
863                        line,
864                        kind: SeqExprKind::Bool(false),
865                    }),
866                    "None" => Ok(SeqExpr {
867                        line,
868                        kind: SeqExprKind::Var("None".to_string()),
869                    }),
870                    other => Err(self.err(
871                        line,
872                        col,
873                        format!(
874                            "state '{other}': initial value is literal (number/string/true/false)"
875                        ),
876                    )),
877                }
878            }
879        }
880    }
881
882    fn fn_decl(&mut self, line: usize) -> Result<SeqFn, SeqError> {
883        let (name, _, _) = self.ident()?;
884        self.expect('(', "'(' po nazwie funkcji")?;
885        let mut params = Vec::new();
886        loop {
887            self.skip_trivia();
888            if self.eat(')') {
889                break;
890            }
891            let mut mutable = false;
892            let (first, first_line, _) = self.ident()?;
893            let pname;
894            let pline;
895            if first == "mut" {
896                mutable = true;
897                let (n, l, _) = self.ident()?;
898                pname = n;
899                pline = l;
900            } else {
901                pname = first;
902                pline = first_line;
903            }
904            self.expect(':', "':' po nazwie parametru funkcji")?;
905            let ty = self.fn_type()?;
906            params.push(FnParam {
907                name: pname,
908                mutable,
909                line: pline,
910                ty,
911            });
912            if !self.eat(',') {
913                self.expect(')', "')' po parametrach funkcji")?;
914                break;
915            }
916        }
917        let mut ret = None;
918        if self.eat_arrow() {
919            ret = Some(self.fn_type()?);
920        }
921        let body = self.fn_block("funkcji")?;
922        Ok(SeqFn {
923            name,
924            line,
925            params,
926            ret,
927            body,
928        })
929    }
930
931    fn fn_type(&mut self) -> Result<String, SeqError> {
932        self.skip_trivia();
933        if self.peek() == Some('O') {
934            let save = (self.pos, self.line, self.col);
935            if let Ok((id, _, _)) = self.ident()
936                && id == "Option"
937            {
938                self.skip_trivia();
939                if self.eat('<') {
940                    let inner = self.fn_type()?;
941                    self.skip_trivia();
942                    self.expect('>', "'>' po typie Option")?;
943                    return Ok(format!("Option<{}>", inner));
944                }
945            } else {
946                (self.pos, self.line, self.col) = save;
947            }
948            (self.pos, self.line, self.col) = save;
949        }
950        if self.eat('(') {
951            let mut parts = vec![self.fn_type()?];
952            while self.eat(',') {
953                parts.push(self.fn_type()?);
954            }
955            self.expect(')', "')' po typie krotki")?;
956            Ok(format!("({})", parts.join(",")))
957        } else {
958            let (id, _, _) = self.ident()?;
959            Ok(id)
960        }
961    }
962
963    fn eat_arrow(&mut self) -> bool {
964        let save = (self.pos, self.line, self.col);
965        self.skip_trivia();
966        if self.peek() == Some('-') && self.chars.get(self.pos + 1) == Some(&'>') {
967            self.bump();
968            self.bump();
969            true
970        } else {
971            (self.pos, self.line, self.col) = save;
972            false
973        }
974    }
975
976    fn fn_block(&mut self, what: &str) -> Result<Vec<SeqStmt>, SeqError> {
977        self.depth += 1;
978        if self.depth > MAX_NESTING_DEPTH {
979            self.depth -= 1;
980            return Err(self.err(
981                self.line,
982                self.col,
983                format!("nesting too deep (limit {MAX_NESTING_DEPTH} levels)"),
984            ));
985        }
986        let result = self.fn_block_inner(what);
987        self.depth -= 1;
988        result
989    }
990
991    fn fn_block_inner(&mut self, what: &str) -> Result<Vec<SeqStmt>, SeqError> {
992        self.expect('{', format!("'{{' w ciele {what}").as_str())?;
993        let mut out = Vec::new();
994        loop {
995            self.skip_trivia();
996            if self.peek() == Some('}') {
997                self.bump();
998                break;
999            }
1000            if self.peek().is_none() {
1001                return Err(self.err(self.line, self.col, format!("unclosed block {what}")));
1002            }
1003            let stmt = self.fn_stmt()?;
1004            if let Some(stmt) = stmt {
1005                out.push(stmt);
1006            }
1007        }
1008        Ok(out)
1009    }
1010
1011    fn fn_stmt(&mut self) -> Result<Option<SeqStmt>, SeqError> {
1012        self.skip_trivia();
1013        if self.eat(';') {
1014            return Ok(None);
1015        }
1016        let (kw, line, col) = self.ident()?;
1017        match kw.as_str() {
1018            "let" | "mut" => {
1019                let (name, _, _) = self.ident()?;
1020                self.expect('=', "'=' po nazwie zmiennej")?;
1021                let value = self.fn_expr()?;
1022                self.expect(';', "';' po `let`/`mut`")?;
1023                Ok(Some(SeqStmt::Let {
1024                    name,
1025                    mutable: kw == "mut",
1026                    value,
1027                    line,
1028                }))
1029            }
1030            "if" => Ok(Some(self.if_rest(line)?)),
1031            "for" => {
1032                let (var, _, _) = self.ident()?;
1033                let (in_kw, l2, c2) = self.ident()?;
1034                if in_kw != "in" {
1035                    return Err(self.err(l2, c2, "'in' after loop variable `for x in rows`"));
1036                }
1037                let (source, _, _) = self.ident()?;
1038                let body = self.fn_block("for loop")?;
1039                Ok(Some(SeqStmt::For {
1040                    var,
1041                    source,
1042                    body,
1043                    line,
1044                }))
1045            }
1046            "match" => self.match_rest(line),
1047            "return" => {
1048                self.skip_trivia();
1049                let value = if self.peek() == Some(';') {
1050                    None
1051                } else {
1052                    Some(self.fn_expr()?)
1053                };
1054                self.expect(';', "';' po `return`")?;
1055                Ok(Some(SeqStmt::Return { value, line }))
1056            }
1057            _ => {
1058                if self.eat_eq() {
1059                    let value = self.fn_expr()?;
1060                    self.expect(';', "';' po przypisaniu")?;
1061                    return Ok(Some(SeqStmt::Assign {
1062                        name: kw,
1063                        value,
1064                        line,
1065                    }));
1066                }
1067                let base = SeqExpr {
1068                    line,
1069                    kind: SeqExprKind::Var(kw),
1070                };
1071                let expr = self.fn_postfix(base)?;
1072                if matches!(expr.kind, SeqExprKind::Var(_) | SeqExprKind::Field { .. }) {
1073                    return Err(self.err(
1074                        line,
1075                        col,
1076                        "an expression as a statement is a function call (e.g. `connect();`)",
1077                    ));
1078                }
1079                self.expect(';', "';' po instrukcji")?;
1080                Ok(Some(SeqStmt::Expr(expr)))
1081            }
1082        }
1083    }
1084
1085    fn if_rest(&mut self, line: usize) -> Result<SeqStmt, SeqError> {
1086        self.expect('(', "'(' po `if`")?;
1087        let cond = self.fn_expr()?;
1088        self.expect(')', "')' po warunku `if`")?;
1089        let then = self.fn_block("branch `if`")?;
1090        let mut elifs = Vec::new();
1091        let mut els = None;
1092        loop {
1093            if !self.peek_keyword("else") {
1094                break;
1095            }
1096            let _ = self.ident();
1097            if self.peek_keyword("if") {
1098                let _ = self.ident();
1099                self.expect('(', "'(' po `else if`")?;
1100                let cond = self.fn_expr()?;
1101                self.expect(')', "')' po warunku `else if`")?;
1102                let body = self.fn_block("branch `else if`")?;
1103                elifs.push((cond, body));
1104            } else {
1105                els = Some(self.fn_block("branch `else`")?);
1106                break;
1107            }
1108        }
1109        Ok(SeqStmt::If {
1110            cond,
1111            then,
1112            elifs,
1113            els,
1114            line,
1115        })
1116    }
1117
1118    fn match_rest(&mut self, line: usize) -> Result<Option<SeqStmt>, SeqError> {
1119        let value = self.fn_expr()?;
1120        self.expect('{', "'{' after `match`")?;
1121        let mut arms = Vec::new();
1122        loop {
1123            self.skip_trivia();
1124            if self.peek() == Some('}') {
1125                self.bump();
1126                break;
1127            }
1128            if self.peek().is_none() {
1129                return Err(self.err(self.line, self.col, "unclosed `match` block"));
1130            }
1131            let (aline, acol) = (self.line, self.col);
1132            let pattern = match self.peek() {
1133                Some('"') => MatchPat::Str(self.string()?.0),
1134                Some(c) if c == '-' || c.is_ascii_digit() => {
1135                    let (n, int, _, _) = self.number_expr()?;
1136                    if !int {
1137                        return Err(self.err(aline, acol, "match pattern is integer"));
1138                    }
1139                    #[allow(clippy::cast_possible_truncation)]
1140                    MatchPat::Int(n as i64)
1141                }
1142                _ => {
1143                    let (id, _, _) = self.ident()?;
1144                    match id.as_str() {
1145                        "true" => MatchPat::Bool(true),
1146                        "false" => MatchPat::Bool(false),
1147                        "_" => MatchPat::Wildcard,
1148                        other => {
1149                            return Err(self.err(
1150                                aline,
1151                                acol,
1152                                format!(
1153                                    "wzorzec match to \"tekst\"/liczba/true/false/_, nie '{other}'"
1154                                ),
1155                            ));
1156                        }
1157                    }
1158                }
1159            };
1160            self.expect('=', "'=' we wzorcu match (`wzorzec => { ... }`)")?;
1161            self.expect('>', "'>' we wzorcu match (`wzorzec => { ... }`)")?;
1162            let body = self.fn_block("branch `match`")?;
1163            arms.push(SeqMatchArm {
1164                pattern,
1165                body,
1166                line: aline,
1167            });
1168            self.eat(',');
1169        }
1170        Ok(Some(SeqStmt::Match { value, arms, line }))
1171    }
1172
1173    fn eat_eq(&mut self) -> bool {
1174        let save = (self.pos, self.line, self.col);
1175        self.skip_trivia();
1176        if self.peek() == Some('=') && self.chars.get(self.pos + 1) != Some(&'=') {
1177            self.bump();
1178            true
1179        } else {
1180            (self.pos, self.line, self.col) = save;
1181            false
1182        }
1183    }
1184
1185    fn peek_keyword(&mut self, kw: &str) -> bool {
1186        let save = (self.pos, self.line, self.col);
1187        let ok = matches!(self.ident(), Ok((w, _, _)) if w == kw);
1188        (self.pos, self.line, self.col) = save;
1189        ok
1190    }
1191
1192    fn number_expr(&mut self) -> Result<(f64, bool, usize, usize), SeqError> {
1193        let (line, col) = self.loc();
1194        let start = self.pos;
1195        if self.peek() == Some('-') {
1196            self.bump();
1197        }
1198        let mut digits = 0;
1199        while matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
1200            self.bump();
1201            digits += 1;
1202        }
1203        let mut int = true;
1204        if self.peek() == Some('.') {
1205            int = false;
1206            self.bump();
1207            while matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
1208                self.bump();
1209                digits += 1;
1210            }
1211        }
1212        if digits == 0 {
1213            return Err(self.err(line, col, "oczekiwano liczby"));
1214        }
1215        let text: String = self.chars[start..self.pos].iter().collect();
1216        text.parse::<f64>()
1217            .map(|v| (v, int, line, col))
1218            .map_err(|_| self.err(line, col, format!("bad number '{text}'")))
1219    }
1220
1221    fn peek_paren_trivia(&mut self) -> bool {
1222        self.skip_trivia();
1223        self.peek() == Some('(')
1224    }
1225
1226    fn fn_or(&mut self) -> Result<SeqExpr, SeqError> {
1227        let mut lhs = self.fn_and()?;
1228        loop {
1229            if !self.eat_op("||") {
1230                break;
1231            }
1232            let line = self.line;
1233            let rhs = self.fn_and()?;
1234            lhs = SeqExpr {
1235                line,
1236                kind: SeqExprKind::Binary {
1237                    op: BinOp::Or,
1238                    lhs: Box::new(lhs),
1239                    rhs: Box::new(rhs),
1240                },
1241            };
1242        }
1243        Ok(lhs)
1244    }
1245
1246    fn fn_and(&mut self) -> Result<SeqExpr, SeqError> {
1247        let mut lhs = self.fn_eq()?;
1248        loop {
1249            if !self.eat_op("&&") {
1250                break;
1251            }
1252            let line = self.line;
1253            let rhs = self.fn_eq()?;
1254            lhs = SeqExpr {
1255                line,
1256                kind: SeqExprKind::Binary {
1257                    op: BinOp::And,
1258                    lhs: Box::new(lhs),
1259                    rhs: Box::new(rhs),
1260                },
1261            };
1262        }
1263        Ok(lhs)
1264    }
1265
1266    fn fn_eq(&mut self) -> Result<SeqExpr, SeqError> {
1267        let mut lhs = self.fn_cmp()?;
1268        loop {
1269            let op = if self.eat_op("==") {
1270                BinOp::Eq
1271            } else if self.eat_op("!=") {
1272                BinOp::Ne
1273            } else {
1274                break;
1275            };
1276            let line = self.line;
1277            let rhs = self.fn_cmp()?;
1278            lhs = SeqExpr {
1279                line,
1280                kind: SeqExprKind::Binary {
1281                    op,
1282                    lhs: Box::new(lhs),
1283                    rhs: Box::new(rhs),
1284                },
1285            };
1286        }
1287        Ok(lhs)
1288    }
1289
1290    fn fn_cmp(&mut self) -> Result<SeqExpr, SeqError> {
1291        let mut lhs = self.fn_add()?;
1292        loop {
1293            let op = if self.eat_op("<=") {
1294                BinOp::Le
1295            } else if self.eat_op(">=") {
1296                BinOp::Ge
1297            } else if self.eat_op("<") {
1298                BinOp::Lt
1299            } else if self.eat_op(">") {
1300                BinOp::Gt
1301            } else {
1302                break;
1303            };
1304            let line = self.line;
1305            let rhs = self.fn_add()?;
1306            lhs = SeqExpr {
1307                line,
1308                kind: SeqExprKind::Binary {
1309                    op,
1310                    lhs: Box::new(lhs),
1311                    rhs: Box::new(rhs),
1312                },
1313            };
1314        }
1315        Ok(lhs)
1316    }
1317
1318    fn fn_add(&mut self) -> Result<SeqExpr, SeqError> {
1319        let mut lhs = self.fn_mul()?;
1320        loop {
1321            let op = if self.eat_op("+") {
1322                BinOp::Add
1323            } else if self.eat_op("-") {
1324                BinOp::Sub
1325            } else {
1326                break;
1327            };
1328            let line = self.line;
1329            let rhs = self.fn_mul()?;
1330            lhs = SeqExpr {
1331                line,
1332                kind: SeqExprKind::Binary {
1333                    op,
1334                    lhs: Box::new(lhs),
1335                    rhs: Box::new(rhs),
1336                },
1337            };
1338        }
1339        Ok(lhs)
1340    }
1341
1342    fn fn_mul(&mut self) -> Result<SeqExpr, SeqError> {
1343        let mut lhs = self.fn_unary()?;
1344        loop {
1345            let op = if self.eat_op("*") {
1346                BinOp::Mul
1347            } else if self.eat_op("/") {
1348                BinOp::Div
1349            } else if self.eat_op("%") {
1350                BinOp::Rem
1351            } else {
1352                break;
1353            };
1354            let line = self.line;
1355            let rhs = self.fn_unary()?;
1356            lhs = SeqExpr {
1357                line,
1358                kind: SeqExprKind::Binary {
1359                    op,
1360                    lhs: Box::new(lhs),
1361                    rhs: Box::new(rhs),
1362                },
1363            };
1364        }
1365        Ok(lhs)
1366    }
1367
1368    fn fn_unary(&mut self) -> Result<SeqExpr, SeqError> {
1369        self.skip_trivia();
1370        let (line, _) = (self.line, self.col);
1371        if self.eat_op("!") {
1372            let expr = self.fn_unary()?;
1373            return Ok(SeqExpr {
1374                line,
1375                kind: SeqExprKind::Unary {
1376                    op: UnOp::Not,
1377                    expr: Box::new(expr),
1378                },
1379            });
1380        }
1381        if self.eat_op("-") {
1382            let expr = self.fn_unary()?;
1383            return Ok(SeqExpr {
1384                line,
1385                kind: SeqExprKind::Unary {
1386                    op: UnOp::Neg,
1387                    expr: Box::new(expr),
1388                },
1389            });
1390        }
1391        let primary = self.fn_primary()?;
1392        self.fn_postfix(primary)
1393    }
1394
1395    fn fn_postfix(&mut self, mut base: SeqExpr) -> Result<SeqExpr, SeqError> {
1396        loop {
1397            if self.eat('.') {
1398                self.skip_trivia();
1399                let (name, nline) = if matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
1400                    let nline = self.line;
1401                    let start = self.pos;
1402                    while matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
1403                        self.bump();
1404                    }
1405                    (
1406                        self.chars[start..self.pos].iter().collect::<String>(),
1407                        nline,
1408                    )
1409                } else {
1410                    let (name, line, _) = self.ident()?;
1411                    (name, line)
1412                };
1413                if self.peek_paren_trivia() {
1414                    self.expect('(', "'(' po metodzie")?;
1415                    let args = self.fn_call_args()?;
1416                    let mut all = vec![base];
1417                    all.extend(args);
1418                    base = SeqExpr {
1419                        line: nline,
1420                        kind: SeqExprKind::Call { name, args: all },
1421                    };
1422                } else {
1423                    let SeqExprKind::Var(var) = base.kind else {
1424                        return Err(self.err(
1425                            nline,
1426                            self.col,
1427                            "field only on loop variable (e.g. `x.id`)",
1428                        ));
1429                    };
1430                    base = SeqExpr {
1431                        line: nline,
1432                        kind: SeqExprKind::Field {
1433                            base: var,
1434                            field: name,
1435                        },
1436                    };
1437                }
1438            } else if self.peek_paren_trivia() {
1439                self.skip_trivia();
1440                let line = self.line;
1441                self.expect('(', "'(' po nazwie funkcji")?;
1442                let args = self.fn_call_args()?;
1443                let SeqExprKind::Var(name) = base.kind else {
1444                    return Err(self.err(line, self.col, "you can call a function by name"));
1445                };
1446                base = SeqExpr {
1447                    line,
1448                    kind: SeqExprKind::Call { name, args },
1449                };
1450            } else {
1451                break;
1452            }
1453        }
1454        Ok(base)
1455    }
1456
1457    fn fn_call_args(&mut self) -> Result<Vec<SeqExpr>, SeqError> {
1458        let mut args = Vec::new();
1459        self.skip_trivia();
1460        if self.eat(')') {
1461            return Ok(args);
1462        }
1463        args.push(self.fn_expr()?);
1464        loop {
1465            if !self.eat(',') {
1466                break;
1467            }
1468            self.skip_trivia();
1469            if self.eat(')') {
1470                return Ok(args);
1471            }
1472            args.push(self.fn_expr()?);
1473        }
1474        self.expect(')', "')' after call arguments")?;
1475        Ok(args)
1476    }
1477
1478    fn fn_primary(&mut self) -> Result<SeqExpr, SeqError> {
1479        self.depth += 1;
1480        if self.depth > MAX_NESTING_DEPTH {
1481            self.depth -= 1;
1482            return Err(self.err(
1483                self.line,
1484                self.col,
1485                format!("nesting too deep (limit {MAX_NESTING_DEPTH} levels)"),
1486            ));
1487        }
1488        let result = self.fn_primary_inner();
1489        self.depth -= 1;
1490        result
1491    }
1492
1493    fn fn_primary_inner(&mut self) -> Result<SeqExpr, SeqError> {
1494        self.skip_trivia();
1495        let (line, col) = (self.line, self.col);
1496        match self.peek() {
1497            Some('"') => {
1498                let (s, _, _) = self.string()?;
1499                Ok(SeqExpr {
1500                    line,
1501                    kind: SeqExprKind::Str(s),
1502                })
1503            }
1504            Some(c) if c.is_ascii_digit() => {
1505                let (n, int, _, _) = self.number_expr()?;
1506                Ok(SeqExpr {
1507                    line,
1508                    kind: if int {
1509                        #[allow(clippy::cast_possible_truncation)]
1510                        SeqExprKind::Int(n as i64)
1511                    } else {
1512                        SeqExprKind::Float(n)
1513                    },
1514                })
1515            }
1516            Some('(') => {
1517                self.bump();
1518                let first = self.fn_expr()?;
1519                self.skip_trivia();
1520                if self.eat(',') {
1521                    let mut items = vec![first];
1522                    loop {
1523                        self.skip_trivia();
1524                        if self.eat(')') {
1525                            break;
1526                        }
1527                        items.push(self.fn_expr()?);
1528                        if !self.eat(',') {
1529                            self.expect(')', "')' po krotce `(a, b)`")?;
1530                            break;
1531                        }
1532                    }
1533                    Ok(SeqExpr {
1534                        line,
1535                        kind: SeqExprKind::Tuple(items),
1536                    })
1537                } else {
1538                    self.expect(')', "')' after the expression in brackets")?;
1539                    Ok(first)
1540                }
1541            }
1542            Some('_') => Err(self.err(line, col, "`_` tylko jako wzorzec `match`")),
1543            _ => {
1544                let (id, _, _) = self.ident()?;
1545                Ok(SeqExpr {
1546                    line,
1547                    kind: match id.as_str() {
1548                        "true" => SeqExprKind::Bool(true),
1549                        "false" => SeqExprKind::Bool(false),
1550                        _ => SeqExprKind::Var(id),
1551                    },
1552                })
1553            }
1554        }
1555    }
1556
1557    fn eat_op(&mut self, s: &str) -> bool {
1558        self.skip_trivia();
1559        for (i, c) in s.chars().enumerate() {
1560            if self.chars.get(self.pos + i) != Some(&c) {
1561                return false;
1562            }
1563        }
1564        for _ in s.chars() {
1565            self.bump();
1566        }
1567        true
1568    }
1569
1570    fn fn_expr(&mut self) -> Result<SeqExpr, SeqError> {
1571        self.fn_or()
1572    }
1573
1574    fn theme_name(&mut self) -> Result<ThemeName, SeqError> {
1575        let (line, col) = self.loc();
1576        match self.peek() {
1577            Some('"') => {
1578                let (s, _, _) = self.string()?;
1579                match s.as_str() {
1580                    "dark" => Ok(ThemeName::Dark),
1581                    "light" => Ok(ThemeName::Light),
1582                    _ => Ok(ThemeName::File(s)),
1583                }
1584            }
1585            _ => {
1586                let (id, _, _) = self.ident()?;
1587                match id.as_str() {
1588                    "dark" => Ok(ThemeName::Dark),
1589                    "light" => Ok(ThemeName::Light),
1590                    _ => Err(self.err(line, col, "theme to dark/light albo \"plik-tokens.json\"")),
1591                }
1592            }
1593        }
1594    }
1595
1596    fn size_tuple(&mut self) -> Result<(f32, f32), SeqError> {
1597        self.expect('(', "'(' przed rozmiarem")?;
1598        let (w, _, _) = self.number()?;
1599        self.expect(',', "',' between W and H")?;
1600        let (h, _, _) = self.number()?;
1601        self.expect(')', "')' po rozmiarze")?;
1602        if !(w.is_finite() && h.is_finite() && w > 0.0 && h > 0.0) {
1603            let (l, c) = (self.line, self.col);
1604            return Err(self.err(l, c, "size must be positive and finite"));
1605        }
1606        Ok((w, h))
1607    }
1608
1609    fn style_args(&mut self) -> Result<Vec<SeqArg>, SeqError> {
1610        let mut args = Vec::new();
1611        loop {
1612            self.skip_trivia();
1613            if self.peek() == Some('}') {
1614                break;
1615            }
1616            if self.peek().is_none() {
1617                return Err(self.err(self.line, self.col, "unclosed `style` block"));
1618            }
1619            let (name, line, _) = self.ident()?;
1620            self.expect(':', format!("':' po polu stylu '{name}'").as_str())?;
1621            let value = self.value()?;
1622            args.push(SeqArg { name, line, value });
1623
1624            self.eat(',');
1625        }
1626        Ok(args)
1627    }
1628
1629    fn window(&mut self, line: usize) -> Result<SeqWindow, SeqError> {
1630        let (name, _, _) = self.ident()?;
1631        let mut size = None;
1632        if self.eat('(') {
1633            let (kw, l, c) = self.ident()?;
1634            if kw != "size" {
1635                return Err(self.err(l, c, "w nawiasie window dozwolone tylko `size: (W, H)`"));
1636            }
1637            self.expect(':', "':' po `size`")?;
1638            size = Some(self.size_tuple()?);
1639            self.expect(')', "')' po `window Nazwa (...)`")?;
1640        }
1641        self.expect('{', "'{' after `window` header")?;
1642        let mut nodes = Vec::new();
1643        loop {
1644            self.skip_trivia();
1645            if self.peek() == Some('}') {
1646                self.bump();
1647                break;
1648            }
1649            if self.peek().is_none() {
1650                return Err(self.err(self.line, self.col, "unclosed `window` block"));
1651            }
1652            nodes.push(self.node()?);
1653        }
1654        Ok(SeqWindow {
1655            name,
1656            line,
1657            size,
1658            nodes,
1659        })
1660    }
1661
1662    fn node(&mut self) -> Result<SeqNode, SeqError> {
1663        self.depth += 1;
1664        if self.depth > MAX_NESTING_DEPTH {
1665            self.depth -= 1;
1666            return Err(self.err(
1667                self.line,
1668                self.col,
1669                format!("nesting too deep (limit {MAX_NESTING_DEPTH} levels)"),
1670            ));
1671        }
1672        let result = self.node_inner();
1673        self.depth -= 1;
1674        result
1675    }
1676
1677    fn node_inner(&mut self) -> Result<SeqNode, SeqError> {
1678        let (kind, line, _) = self.ident()?;
1679        let mut label = None;
1680        let mut args = Vec::new();
1681
1682        if self.eat('(') {
1683            self.skip_trivia();
1684            if self.peek() == Some('"') {
1685                let (s, _, _) = self.string()?;
1686                label = Some(s);
1687                self.skip_trivia();
1688                if self.eat(',') {
1689                    self.skip_trivia();
1690                    if self.peek() != Some(')') {
1691                        args = self.named_args()?;
1692                    }
1693                }
1694            } else if self.peek() != Some(')') {
1695                args = self.named_args()?;
1696            }
1697            self.expect(')', "')' po atrybutach")?;
1698        }
1699        let mut children = Vec::new();
1700        if self.eat('{') {
1701            loop {
1702                self.skip_trivia();
1703                if self.peek() == Some('}') {
1704                    self.bump();
1705                    break;
1706                }
1707                if self.peek().is_none() {
1708                    return Err(self.err(self.line, self.col, "unclosed children block `{ }`"));
1709                }
1710                children.push(self.node()?);
1711            }
1712        }
1713        Ok(SeqNode {
1714            kind,
1715            line,
1716            label,
1717            args,
1718            children,
1719        })
1720    }
1721
1722    fn named_args(&mut self) -> Result<Vec<SeqArg>, SeqError> {
1723        let mut args = Vec::new();
1724        loop {
1725            let (name, line, _) = self.ident()?;
1726            self.expect(':', format!("':' po atrybucie '{name}'").as_str())?;
1727            let value = self.value()?;
1728            args.push(SeqArg { name, line, value });
1729            if !self.eat(',') {
1730                break;
1731            }
1732            self.skip_trivia();
1733            if self.peek() == Some(')') {
1734                break;
1735            }
1736        }
1737        Ok(args)
1738    }
1739
1740    fn peek_paren(&self) -> bool {
1741        let mut i = self.pos;
1742        while let Some(&c) = self.chars.get(i) {
1743            if c == ' ' || c == '\t' || c == '\r' || c == '\n' {
1744                i += 1;
1745            } else {
1746                return c == '(';
1747            }
1748        }
1749        false
1750    }
1751
1752    fn value(&mut self) -> Result<SeqValue, SeqError> {
1753        self.depth += 1;
1754        if self.depth > MAX_NESTING_DEPTH {
1755            self.depth -= 1;
1756            return Err(self.err(
1757                self.line,
1758                self.col,
1759                format!("nesting too deep (limit {MAX_NESTING_DEPTH} levels)"),
1760            ));
1761        }
1762        let result = self.value_inner();
1763        self.depth -= 1;
1764        result
1765    }
1766
1767    fn value_inner(&mut self) -> Result<SeqValue, SeqError> {
1768        self.skip_trivia();
1769        Ok(match self.peek() {
1770            Some('"') => {
1771                let (s, _, _) = self.string()?;
1772                SeqValue::Str(s)
1773            }
1774            Some(c) if c == '-' || c.is_ascii_digit() => {
1775                let (n, _, _) = self.number()?;
1776                SeqValue::Num(n)
1777            }
1778            Some('#') => {
1779                let (hex, _, _) = self.color()?;
1780                SeqValue::Color(hex)
1781            }
1782            Some('{') => return self.callback(),
1783            Some('(') => {
1784                self.bump();
1785                let mut items = vec![self.value()?];
1786                while self.eat(',') {
1787                    self.skip_trivia();
1788                    if self.peek() == Some(')') {
1789                        break;
1790                    }
1791                    items.push(self.value()?);
1792                }
1793                self.expect(')', "')' po krotce `(a, b)`")?;
1794                SeqValue::Tuple(items)
1795            }
1796            Some('[') => {
1797                self.bump();
1798                let mut items = vec![self.value()?];
1799                while self.eat(',') {
1800                    self.skip_trivia();
1801                    if self.peek() == Some(']') {
1802                        break;
1803                    }
1804                    items.push(self.value()?);
1805                }
1806                self.expect(']', "']' after list `[a, b]`")?;
1807                SeqValue::Array(items)
1808            }
1809            _ => {
1810                let (id, _, _) = self.ident()?;
1811                match id.as_str() {
1812                    "true" => SeqValue::Bool(true),
1813                    "false" => SeqValue::Bool(false),
1814                    "fit" => SeqValue::Length(LengthVal::Fit),
1815
1816                    "fill" if self.peek_paren() => {
1817                        return self.length_paren(LengthVal::Fill, "fill(waga)");
1818                    }
1819                    "px" if self.peek_paren() => {
1820                        return self.length_paren(LengthVal::Px, "px(liczba)");
1821                    }
1822                    "percent" if self.peek_paren() => {
1823                        return self.length_paren(LengthVal::Percent, "percent(liczba)");
1824                    }
1825                    "dynamic" => {
1826                        self.expect('(', "'(' po `dynamic`")?;
1827                        let (name, _, _) = self.ident()?;
1828                        self.expect(')', "')' po `dynamic(nazwa)`")?;
1829                        SeqValue::Dynamic(name)
1830                    }
1831                    "theme" => {
1832                        self.expect('.', "'.' po `theme` (theme.pole)")?;
1833                        let (field, _, _) = self.ident()?;
1834                        SeqValue::Theme(field)
1835                    }
1836                    "env" => {
1837                        self.expect('.', "'.' po `env` (env.nazwa)")?;
1838                        let (field, _, _) = self.ident()?;
1839                        SeqValue::Env(field)
1840                    }
1841                    "item" => {
1842                        self.expect('.', "'.' po `item` (item.pole)")?;
1843                        let (field, _, _) = self.ident()?;
1844                        SeqValue::ItemField(field)
1845                    }
1846                    _ => SeqValue::Ident(id),
1847                }
1848            }
1849        })
1850    }
1851
1852    fn length_paren(&mut self, f: fn(f32) -> LengthVal, what: &str) -> Result<SeqValue, SeqError> {
1853        self.expect('(', format!("'(' — {what}").as_str())?;
1854        let (n, _, _) = self.number()?;
1855        self.expect(')', format!("')' — {what}").as_str())?;
1856        Ok(SeqValue::Length(f(n)))
1857    }
1858
1859    fn callback(&mut self) -> Result<SeqValue, SeqError> {
1860        debug_assert_eq!(self.peek(), Some('{'));
1861        self.bump();
1862        self.skip_trivia();
1863        let (action, _, _) = self.ident()?;
1864        self.expect('(', "'(' po nazwie callbacku")?;
1865        let mut args = Vec::new();
1866        self.skip_trivia();
1867        if self.peek() != Some(')') {
1868            args.push(self.value()?);
1869            while self.eat(',') {
1870                self.skip_trivia();
1871                if self.peek() == Some(')') {
1872                    break;
1873                }
1874                args.push(self.value()?);
1875            }
1876        }
1877        self.expect(')', "')' po argumencie callbacku")?;
1878        self.skip_trivia();
1879        if self.peek() == Some('}') {
1880            self.bump();
1881        } else {
1882            let (l, c) = (self.line, self.col);
1883            return Err(self.err(l, c, "'}' po callbacku `{ akcja() }`"));
1884        }
1885        Ok(SeqValue::Callback { action, args })
1886    }
1887}