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