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}