1use crate::ast::*;
10use crate::lexer::{LexError, Spanned, lex};
11use crate::token::*;
12
13#[derive(Debug, Clone, PartialEq)]
15pub struct ParseError {
16 pub msg: String,
17 pub line: u32,
18 pub col: u32,
19}
20
21impl std::fmt::Display for ParseError {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 write!(f, "{}:{}: {}", self.line, self.col, self.msg)
24 }
25}
26
27impl From<LexError> for ParseError {
28 fn from(e: LexError) -> Self {
29 ParseError {
30 msg: e.msg,
31 line: e.line,
32 col: e.col,
33 }
34 }
35}
36
37pub fn parse(src: &str) -> Result<Picture, ParseError> {
40 parse_in_dir(src, None)
41}
42
43pub fn parse_in_dir(src: &str, base: Option<&Path>) -> Result<Picture, ParseError> {
45 let src = strip_backend_preamble(src);
46 let toks = lex(&src)?;
47 let (toks, macros) = preprocess(toks, base)?;
48 let mut pic = Parser::new(toks).parse_picture()?;
49 pic.macros = macros;
50 pic.base_dir = base.map(|p| p.to_path_buf());
51 Ok(pic)
52}
53
54pub fn parse_body_tokens(
59 toks: &[Spanned],
60 macros: &mut Macros,
61 base: Option<&Path>,
62) -> Result<Vec<Stmt>, ParseError> {
63 let before = body_macro_frame(toks).unwrap_or_else(|| macros.clone());
64 let mut m = before.clone();
65 let mut input = toks.to_vec();
66 input.push(Spanned::new(Token::Eof, 0, 0));
67 let expanded = expand(&input, &mut m, 0, base)?;
68 propagate_macro_changes(macros, &before, &m);
69 let mut p = Parser::new(expanded);
70 p.parse_elementlist(&[])
71}
72
73fn body_macro_frame(toks: &[Spanned]) -> Option<Macros> {
74 toks.iter()
75 .find_map(|s| s.macro_frame.as_ref().map(|m| m.as_ref().clone()))
76}
77
78fn propagate_macro_changes(macros: &mut Macros, before: &Macros, after: &Macros) {
79 for name in before.keys() {
80 if !after.contains_key(name) {
81 macros.remove(name);
82 }
83 }
84 for (name, body) in after {
85 if before.get(name) != Some(body) {
86 macros.insert(name.clone(), body.clone());
87 }
88 }
89}
90
91pub(crate) fn parse_exec_source(
94 src: &str,
95 macros: &Macros,
96 base: Option<&Path>,
97 arg_frame: Option<&[Vec<Spanned>]>,
98) -> Result<Vec<Stmt>, ParseError> {
99 let mut toks = lex(src)?;
100 if let Some(args) = arg_frame {
101 toks = substitute(&toks, args);
102 }
103 let mut m = macros.clone();
104 let expanded = expand(&toks, &mut m, 0, base)?;
105 let mut p = Parser::new(expanded);
106 p.parse_elementlist(&[])
107}
108
109pub(crate) fn parse_stringexpr_tokens(
110 toks: &[Spanned],
111 macros: &mut Macros,
112 base: Option<&Path>,
113) -> Result<StringExpr, ParseError> {
114 let mut input = toks.to_vec();
115 input.push(Spanned::new(Token::Eof, 0, 0));
116 let expanded = expand(&input, macros, 0, base)?;
117 let mut p = Parser::new(expanded);
118 p.skip_newlines();
119 let expr = p.parse_stringexpr()?;
120 p.skip_newlines();
121 if !p.at(&Token::Eof) {
122 return p.err(format!("unexpected {:?} after string expression", p.cur()));
123 }
124 Ok(expr)
125}
126
127fn strip_backend_preamble(src: &str) -> String {
135 let mut out = String::with_capacity(src.len());
136 let mut in_verbatimtex = false;
137 let mut in_string = false;
138 let mut in_raw_sh = false;
139
140 for line in src.lines() {
141 let trimmed = line.trim_start();
142 if in_verbatimtex {
143 out.push('\n');
144 if starts_word(trimmed, "etex") {
145 in_verbatimtex = false;
146 }
147 continue;
148 }
149
150 if in_raw_sh {
151 out.push_str(line);
152 out.push('\n');
153 in_raw_sh = line_continues(line);
154 continue;
155 }
156
157 if !in_string && starts_word(trimmed, "verbatimtex") {
158 in_verbatimtex = true;
159 out.push('\n');
160 } else if !in_string && is_ignored_backend_line(trimmed) {
161 out.push('\n');
162 } else {
163 out.push_str(line);
164 out.push('\n');
165 if starts_word(trimmed, "sh") {
166 in_raw_sh = line_continues(line);
167 } else {
168 in_string = update_string_state(line, in_string);
169 }
170 }
171 }
172 out
173}
174
175fn line_continues(line: &str) -> bool {
176 line.trim_end_matches([' ', '\t', '\r']).ends_with('\\')
177}
178
179fn update_string_state(line: &str, mut in_string: bool) -> bool {
180 let mut slashes = 0usize;
181 for c in line.chars() {
182 if c == '\\' {
183 slashes += 1;
184 continue;
185 }
186 if c == '"' && slashes.is_multiple_of(2) {
187 in_string = !in_string;
188 }
189 slashes = 0;
190 }
191 in_string
192}
193
194fn is_ignored_backend_line(trimmed: &str) -> bool {
195 trimmed.starts_with("\\global") || trimmed.starts_with("\\psset")
196}
197
198fn starts_word(s: &str, word: &str) -> bool {
199 let Some(rest) = s.strip_prefix(word) else {
200 return false;
201 };
202 !rest
203 .chars()
204 .next()
205 .is_some_and(|c| c.is_alphanumeric() || c == '_')
206}
207
208use std::collections::HashMap;
216use std::path::Path;
217
218fn preprocess(
219 input: Vec<Spanned>,
220 base: Option<&Path>,
221) -> Result<(Vec<Spanned>, Macros), ParseError> {
222 let mut macros: Macros = builtin_unit_macros();
223 let out = expand(&input, &mut macros, 0, base)?;
224 Ok((out, macros))
225}
226
227fn builtin_unit_macros() -> Macros {
231 let defs = [
232 ("bp__", "*(scale/72)"), ("pt__", "*(scale/72.27)"), ("pc__", "*(12*scale/72.27)"), ("in__", "*scale"), ("cm__", "*(scale/2.54)"), ("mm__", "*(scale/25.4)"), ("px__", "*(scale/96)"), ];
240 let mut m = Macros::new();
241 for (name, body) in defs {
242 if let Ok(toks) = lex(body) {
243 let body_toks: Vec<Spanned> =
244 toks.into_iter().filter(|s| s.tok != Token::Eof).collect();
245 m.insert(name.to_string(), body_toks);
246 }
247 }
248 m
249}
250
251fn loc(toks: &[Spanned], i: usize) -> (u32, u32) {
252 toks.get(i).map(|s| (s.line, s.col)).unwrap_or((0, 0))
253}
254
255fn expand(
256 toks: &[Spanned],
257 macros: &mut HashMap<String, Vec<Spanned>>,
258 depth: usize,
259 base: Option<&Path>,
260) -> Result<Vec<Spanned>, ParseError> {
261 if depth > 64 {
262 return Err(ParseError {
263 msg: "macro expansion too deep (recursive define?)".into(),
264 line: 0,
265 col: 0,
266 });
267 }
268 let mut out = Vec::new();
269 let mut i = 0;
270 while i < toks.len() {
271 match &toks[i].tok {
272 Token::Kw(Kw::Define) => {
273 let (l, c) = loc(toks, i);
274 i += 1;
275 let name = match toks.get(i).map(|s| &s.tok) {
276 Some(Token::Name(n)) | Some(Token::Label(n)) => n.clone(),
277 _ => {
278 return Err(ParseError {
279 msg: "define: expected a macro name".into(),
280 line: l,
281 col: c,
282 });
283 }
284 };
285 i += 1;
286 while toks.get(i).map(|s| &s.tok) == Some(&Token::Newline) {
288 i += 1;
289 }
290 let Some(delim) = toks.get(i).map(|s| s.tok.clone()) else {
291 return Err(ParseError {
292 msg: "define: expected a body delimiter".into(),
293 line: l,
294 col: c,
295 });
296 };
297 let body = if delim == Token::LeftBrace {
298 i += 1; let start = i;
300 let mut bd = 1;
301 while i < toks.len() && bd > 0 {
302 match &toks[i].tok {
303 Token::LeftBrace => bd += 1,
304 Token::RightBrace => {
305 bd -= 1;
306 if bd == 0 {
307 break;
308 }
309 }
310 _ => {}
311 }
312 i += 1;
313 }
314 if bd != 0 {
315 return Err(ParseError {
316 msg: "define: unterminated `{` body".into(),
317 line: l,
318 col: c,
319 });
320 }
321 let body = trim_edge_newlines(&toks[start..i]);
322 i += 1; body
324 } else {
325 if matches!(delim, Token::Eof | Token::Newline) {
326 let (l, c) = loc(toks, i);
327 return Err(ParseError {
328 msg: "define: expected a body delimiter".into(),
329 line: l,
330 col: c,
331 });
332 }
333 i += 1; let start = i;
335 while i < toks.len() && toks[i].tok != delim {
336 i += 1;
337 }
338 if i >= toks.len() {
339 return Err(ParseError {
340 msg: "define: unterminated delimited body".into(),
341 line: l,
342 col: c,
343 });
344 }
345 let body = trim_edge_newlines(&toks[start..i]);
346 i += 1; body
348 };
349 macros.insert(name, body);
350 }
351 Token::Kw(Kw::Undef) => {
352 i += 1;
353 if let Some(Token::Name(n)) | Some(Token::Label(n)) = toks.get(i).map(|s| &s.tok) {
354 macros.remove(n);
355 }
356 i += 1;
357 }
358 Token::Kw(Kw::If) => {
363 let if_tok = toks[i].clone();
364 out.push(toks[i].clone());
365 i += 1;
366 let Some(te) = find_kw_depth0(toks, i, Kw::Then) else {
367 continue; };
369 let cond = expand(&toks[i..te], macros, depth + 1, base)?;
370 if let Some((then_body, after_then)) = read_braced_body(toks, te + 1)? {
371 let mut j = after_then;
372 while matches!(toks.get(j).map(|s| &s.tok), Some(Token::Newline)) {
373 j += 1;
374 }
375 let else_body =
376 if matches!(toks.get(j).map(|s| &s.tok), Some(Token::Kw(Kw::Else))) {
377 read_braced_body(toks, j + 1)?
378 } else {
379 None
380 };
381 if let Some(take_then) = static_truth(&cond) {
382 let after_static = else_body
383 .as_ref()
384 .map(|(_, after)| *after)
385 .unwrap_or(after_then);
386 out.pop(); if take_then {
388 out.extend(expand(&then_body, macros, depth + 1, base)?);
389 i = after_static;
390 } else if let Some((body, after)) = else_body {
391 out.extend(expand(&body, macros, depth + 1, base)?);
392 i = after;
393 } else {
394 i = after_then;
395 }
396 continue;
397 }
398 }
399 *out.last_mut().unwrap() = if_tok;
400 out.extend(cond);
401 out.push(toks[te].clone()); i = copy_braced(toks, te + 1, &mut out, macros)?;
403 let mut j = i;
405 while matches!(toks.get(j).map(|s| &s.tok), Some(Token::Newline)) {
406 j += 1;
407 }
408 if matches!(toks.get(j).map(|s| &s.tok), Some(Token::Kw(Kw::Else))) {
409 out.push(toks[j].clone());
410 i = copy_braced(toks, j + 1, &mut out, macros)?;
411 }
412 }
413 Token::Kw(Kw::For) => {
414 out.push(toks[i].clone());
415 i += 1;
416 let Some(de) = find_kw_depth0(toks, i, Kw::Do) else {
417 continue;
418 };
419 out.extend(expand(&toks[i..de], macros, depth + 1, base)?);
420 out.push(toks[de].clone()); i = copy_braced(toks, de + 1, &mut out, macros)?;
422 }
423 Token::Name(n) | Token::Label(n) if macros.contains_key(n) => {
424 let body = macros.get(n).unwrap().clone();
425 i += 1;
426 let args = if toks.get(i).map(|s| &s.tok) == Some(&Token::Lparen) {
427 i += 1;
428 let (a, ni) = read_args(toks, i)?;
429 i = ni;
430 a
431 } else {
432 Vec::new()
433 };
434 let sub = substitute(&body, &args);
435 let expanded = expand(&sub, macros, depth + 1, base)?;
436 out.extend(expanded);
437 }
438 Token::Kw(Kw::Copy) => {
440 let (l, c) = loc(toks, i);
441 i += 1;
442 let Some(Token::Str(fname)) = toks.get(i).map(|s| &s.tok) else {
443 return Err(ParseError {
444 msg:
445 "copy: expected a quoted file name (only `copy \"file\"` is supported)"
446 .into(),
447 line: l,
448 col: c,
449 });
450 };
451 let fname = fname.clone();
452 i += 1;
453 let inc = include_file(base, &fname, macros, depth, l, c)?;
454 out.extend(inc);
455 }
456 _ => {
457 out.push(toks[i].clone());
458 i += 1;
459 }
460 }
461 }
462 Ok(out)
463}
464
465fn read_args(toks: &[Spanned], mut i: usize) -> Result<(Vec<Vec<Spanned>>, usize), ParseError> {
468 let mut args: Vec<Vec<Spanned>> = Vec::new();
469 let mut cur: Vec<Spanned> = Vec::new();
470 let mut depth = 0i32;
471 loop {
472 let Some(s) = toks.get(i) else {
473 return Err(ParseError {
474 msg: "unterminated macro arguments".into(),
475 line: 0,
476 col: 0,
477 });
478 };
479 match &s.tok {
480 Token::Lparen | Token::LeftBrack | Token::LeftBrace => {
481 depth += 1;
482 cur.push(s.clone());
483 i += 1;
484 }
485 Token::Rparen if depth == 0 => {
486 i += 1;
487 trim_trailing_newlines(&mut cur);
488 if !cur.is_empty() || !args.is_empty() {
489 args.push(cur);
490 }
491 break;
492 }
493 Token::Rparen | Token::RightBrack | Token::RightBrace => {
494 depth -= 1;
495 cur.push(s.clone());
496 i += 1;
497 }
498 Token::Comma if depth == 0 => {
499 trim_trailing_newlines(&mut cur);
500 args.push(std::mem::take(&mut cur));
501 i += 1;
502 }
503 Token::Newline if depth == 0 && cur.is_empty() => {
504 i += 1;
505 }
506 _ => {
507 cur.push(s.clone());
508 i += 1;
509 }
510 }
511 }
512 Ok((args, i))
513}
514
515fn trim_trailing_newlines(toks: &mut Vec<Spanned>) {
516 while matches!(toks.last().map(|s| &s.tok), Some(Token::Newline)) {
517 toks.pop();
518 }
519}
520
521fn trim_edge_newlines(toks: &[Spanned]) -> Vec<Spanned> {
524 let mut start = 0;
525 let mut end = toks.len();
526 while start < end && toks[start].tok == Token::Newline {
527 start += 1;
528 }
529 while end > start && toks[end - 1].tok == Token::Newline {
530 end -= 1;
531 }
532 toks[start..end].to_vec()
533}
534
535fn substitute(body: &[Spanned], args: &[Vec<Spanned>]) -> Vec<Spanned> {
537 let mut out = Vec::new();
538 let mut i = 0;
539 while i < body.len() {
540 if matches!(body[i].tok, Token::Kw(Kw::Define))
541 && let Some((define, next)) = copy_define_verbatim(body, i)
542 {
543 out.extend(define);
544 i = next;
545 continue;
546 }
547
548 if let Some((pasted, next)) = paste_adjacent_args(body, args, i) {
549 out.push(pasted.with_arg_frame(args));
550 i = next;
551 continue;
552 }
553
554 let s = &body[i];
555 match &s.tok {
556 Token::Arg(k) => {
557 if let Some(a) = args.get((*k as usize).wrapping_sub(1)) {
558 out.extend(a.iter().cloned().map(|s| s.with_arg_frame(args)));
559 }
560 }
561 Token::ArgCount => out.push(
563 Spanned::new(Token::Float(args.len() as f64), s.line, s.col).with_arg_frame(args),
564 ),
565 Token::Str(text) if text.contains('$') => {
568 out.push(
569 Spanned::new(Token::Str(subst_in_string(text, args)), s.line, s.col)
570 .with_arg_frame(args),
571 );
572 }
573 _ => out.push(s.clone().with_arg_frame(args)),
574 }
575 i += 1;
576 }
577 out
578}
579
580fn copy_define_verbatim(body: &[Spanned], start: usize) -> Option<(Vec<Spanned>, usize)> {
581 let mut name_idx = start + 1;
582 while matches!(body.get(name_idx).map(|s| &s.tok), Some(Token::Newline)) {
583 name_idx += 1;
584 }
585 if !matches!(
586 body.get(name_idx).map(|s| &s.tok),
587 Some(Token::Name(_)) | Some(Token::Label(_))
588 ) {
589 return None;
590 }
591
592 let mut out = Vec::new();
593 let mut i = start;
594 while let Some(s) = body.get(i) {
595 out.push(s.clone());
596 i += 1;
597 if matches!(s.tok, Token::LeftBrace) {
598 break;
599 }
600 }
601
602 if !matches!(out.last().map(|s| &s.tok), Some(Token::LeftBrace)) {
603 return None;
604 }
605
606 let mut depth = 1i32;
607 while let Some(s) = body.get(i) {
608 match &s.tok {
609 Token::LeftBrace => depth += 1,
610 Token::RightBrace => depth -= 1,
611 _ => {}
612 }
613 out.push(s.clone());
614 i += 1;
615 if depth == 0 {
616 return Some((out, i));
617 }
618 }
619 None
620}
621
622fn paste_adjacent_args(
623 body: &[Spanned],
624 args: &[Vec<Spanned>],
625 start: usize,
626) -> Option<(Spanned, usize)> {
627 let first = body.get(start)?;
628 let Token::Arg(k) = &first.tok else {
629 return None;
630 };
631
632 let mut text = arg_text(*k, args);
633 let mut count = 1usize;
634 let mut end = start + 1;
635 let mut prev = first;
636 while let Some(next) = body.get(end) {
637 let Token::Arg(k) = &next.tok else {
638 break;
639 };
640 if !adjacent_arg_tokens(prev, next) {
641 break;
642 }
643 text.push_str(&arg_text(*k, args));
644 count += 1;
645 prev = next;
646 end += 1;
647 }
648
649 if count < 2 || text.is_empty() {
650 return None;
651 }
652
653 Some((tokenize_pasted_arg_text(&text, first.line, first.col), end))
654}
655
656fn arg_text(k: u32, args: &[Vec<Spanned>]) -> String {
657 args.get((k as usize).wrapping_sub(1))
658 .map(|a| tokens_to_text(a))
659 .unwrap_or_default()
660}
661
662fn adjacent_arg_tokens(left: &Spanned, right: &Spanned) -> bool {
663 left.line == right.line && arg_end_col(left) == Some(right.col)
664}
665
666fn arg_end_col(s: &Spanned) -> Option<u32> {
667 let Token::Arg(k) = &s.tok else {
668 return None;
669 };
670 Some(s.col + 1 + k.to_string().len() as u32)
671}
672
673fn tokenize_pasted_arg_text(text: &str, line: u32, col: u32) -> Spanned {
674 if let Ok(toks) = lex(text)
675 && toks.len() == 2
676 && matches!(toks[1].tok, Token::Eof)
677 {
678 return Spanned::new(toks[0].tok.clone(), line, col);
679 }
680
681 let tok = if text.chars().next().is_some_and(|c| c.is_ascii_uppercase()) {
682 Token::Label(text.to_string())
683 } else {
684 Token::Name(text.to_string())
685 };
686 Spanned::new(tok, line, col)
687}
688
689fn subst_in_string(text: &str, args: &[Vec<Spanned>]) -> String {
692 let chars: Vec<char> = text.chars().collect();
693 let mut out = String::new();
694 let mut i = 0;
695 while i < chars.len() {
696 if chars[i] == '$' && chars.get(i + 1) == Some(&'+') {
697 out.push_str(&args.len().to_string());
698 i += 2;
699 } else if chars[i] == '$' && chars.get(i + 1).is_some_and(|c| c.is_ascii_digit()) {
700 let mut j = i + 1;
701 let mut num = String::new();
702 while j < chars.len() && chars[j].is_ascii_digit() {
703 num.push(chars[j]);
704 j += 1;
705 }
706 if let Ok(k) = num.parse::<usize>()
707 && k >= 1
708 && let Some(a) = args.get(k - 1)
709 {
710 out.push_str(&tokens_to_text(a));
711 }
712 i = j;
713 } else {
714 out.push(chars[i]);
715 i += 1;
716 }
717 }
718 out
719}
720
721fn tokens_to_text(toks: &[Spanned]) -> String {
724 let mut s = String::new();
725 for t in toks {
726 match &t.tok {
727 Token::Float(v) => s.push_str(&format!("{v}")),
728 Token::Str(t) => s.push_str(t),
729 Token::Name(n) | Token::Label(n) => s.push_str(n),
730 Token::Lparen => s.push('('),
731 Token::Rparen => s.push(')'),
732 Token::LeftBrack => s.push('['),
733 Token::RightBrack => s.push(']'),
734 Token::LeftBrace => s.push('{'),
735 Token::RightBrace => s.push('}'),
736 Token::Comma => s.push(','),
737 Token::Colon => s.push(':'),
738 Token::Dot => s.push('.'),
739 Token::Plus => s.push('+'),
740 Token::Minus => s.push('-'),
741 Token::Mult => s.push('*'),
742 Token::Div => s.push('/'),
743 Token::Percent => s.push('%'),
744 Token::Dollar => s.push('$'),
745 Token::Backslash => s.push('\\'),
746 Token::DotX => s.push_str(".x"),
747 Token::DotY => s.push_str(".y"),
748 Token::DotPS => s.push_str(".PS"),
749 Token::DotPE => s.push_str(".PE"),
750 Token::Corner(c) => s.push_str(corner_text(*c)),
751 Token::Param(p) => s.push_str(param_text(*p)),
752 Token::LineType(l) => s.push_str(line_type_text(*l)),
753 Token::TextPos(p) => s.push_str(text_pos_text(*p)),
754 Token::Arrow(a) => s.push_str(arrow_text(*a)),
755 Token::Dir(d) => s.push_str(dir_text(*d)),
756 Token::Prim(p) => s.push_str(prim_text(*p)),
757 Token::Color(c) => s.push_str(color_text(*c)),
758 _ => {}
759 }
760 }
761 s
762}
763
764fn corner_text(c: Corner) -> &'static str {
765 match c {
766 Corner::N => ".n",
767 Corner::S => ".s",
768 Corner::E => ".e",
769 Corner::W => ".w",
770 Corner::Ne => ".ne",
771 Corner::Se => ".se",
772 Corner::Nw => ".nw",
773 Corner::Sw => ".sw",
774 Corner::Start => ".start",
775 Corner::End => ".end",
776 Corner::Center => ".c",
777 }
778}
779
780fn param_text(p: Param) -> &'static str {
781 match p {
782 Param::Height => ".ht",
783 Param::Width => ".wid",
784 Param::Radius => ".rad",
785 Param::Diameter => ".diam",
786 Param::Thickness => ".thick",
787 Param::Length => ".len",
788 }
789}
790
791fn line_type_text(l: LineType) -> &'static str {
792 match l {
793 LineType::Solid => "solid",
794 LineType::Dotted => "dotted",
795 LineType::Dashed => "dashed",
796 LineType::Invis => "invis",
797 }
798}
799
800fn text_pos_text(p: TextPos) -> &'static str {
801 match p {
802 TextPos::Center => "center",
803 TextPos::Ljust => "ljust",
804 TextPos::Rjust => "rjust",
805 TextPos::Above => "above",
806 TextPos::Below => "below",
807 }
808}
809
810fn arrow_text(a: Arrow) -> &'static str {
811 match a {
812 Arrow::Left => "<-",
813 Arrow::Right => "->",
814 Arrow::Double => "<->",
815 }
816}
817
818fn dir_text(d: Dir) -> &'static str {
819 match d {
820 Dir::Up => "up",
821 Dir::Down => "down",
822 Dir::Right => "right",
823 Dir::Left => "left",
824 }
825}
826
827fn prim_text(p: Prim) -> &'static str {
828 match p {
829 Prim::Box => "box",
830 Prim::Circle => "circle",
831 Prim::Ellipse => "ellipse",
832 Prim::Arc => "arc",
833 Prim::Line => "line",
834 Prim::Arrow => "arrow",
835 Prim::Move => "move",
836 Prim::Spline => "spline",
837 }
838}
839
840fn color_text(c: Color) -> &'static str {
841 match c {
842 Color::Colored => "color",
843 Color::Outlined => "outlined",
844 Color::Shaded => "shaded",
845 }
846}
847
848fn include_file(
852 base: Option<&Path>,
853 fname: &str,
854 macros: &mut HashMap<String, Vec<Spanned>>,
855 depth: usize,
856 l: u32,
857 c: u32,
858) -> Result<Vec<Spanned>, ParseError> {
859 let mkerr = |msg: String| ParseError {
860 msg,
861 line: l,
862 col: c,
863 };
864 let p = Path::new(fname);
865 let path = if p.is_absolute() {
866 p.to_path_buf()
867 } else {
868 match base {
869 Some(b) => b.join(p),
870 None => {
871 return Err(mkerr(format!(
872 "copy \"{fname}\": file includes require a file path (unavailable here)"
873 )));
874 }
875 }
876 };
877 let content =
878 std::fs::read_to_string(&path).map_err(|e| mkerr(format!("copy \"{fname}\": {e}")))?;
879 let toks = lex(&content)?;
880 let inc_base = path.parent().map(|d| d.to_path_buf());
881 let mut expanded = expand(&toks, macros, depth + 1, inc_base.as_deref())?;
882 if matches!(expanded.last().map(|s| &s.tok), Some(Token::Eof)) {
883 expanded.pop();
884 }
885 Ok(expanded)
886}
887
888fn find_kw_depth0(toks: &[Spanned], start: usize, kw: Kw) -> Option<usize> {
890 let mut depth = 0i32;
891 for (off, s) in toks[start..].iter().enumerate() {
892 match &s.tok {
893 Token::Lparen | Token::LeftBrace | Token::LeftBrack => depth += 1,
894 Token::Rparen | Token::RightBrace | Token::RightBrack => {
895 depth -= 1;
896 if depth < 0 {
897 return None;
898 }
899 }
900 Token::Kw(k) if *k == kw && depth == 0 => return Some(start + off),
901 _ => {}
902 }
903 }
904 None
905}
906
907fn copy_braced(
910 toks: &[Spanned],
911 mut i: usize,
912 out: &mut Vec<Spanned>,
913 macros: &HashMap<String, Vec<Spanned>>,
914) -> Result<usize, ParseError> {
915 while matches!(toks.get(i).map(|s| &s.tok), Some(Token::Newline)) {
916 out.push(toks[i].clone());
917 i += 1;
918 }
919 if !matches!(toks.get(i).map(|s| &s.tok), Some(Token::LeftBrace)) {
920 return Ok(i); }
922 let mut depth = 0i32;
923 let mut tagged_body = false;
924 while let Some(s) = toks.get(i) {
925 let mut s = s.clone();
926 let outer_left_brace = depth == 0 && matches!(s.tok, Token::LeftBrace);
927 match &s.tok {
928 Token::LeftBrace => depth += 1,
929 Token::RightBrace => {
930 out.push(s);
931 i += 1;
932 depth -= 1;
933 if depth == 0 {
934 return Ok(i);
935 }
936 continue;
937 }
938 _ => {}
939 }
940 if depth == 1 && !outer_left_brace && !tagged_body {
941 s = s.with_macro_frame(macros);
942 tagged_body = true;
943 }
944 out.push(s);
945 i += 1;
946 }
947 Err(ParseError {
948 msg: "unterminated `{` body".into(),
949 line: 0,
950 col: 0,
951 })
952}
953
954fn read_braced_body(
955 toks: &[Spanned],
956 mut i: usize,
957) -> Result<Option<(Vec<Spanned>, usize)>, ParseError> {
958 while matches!(toks.get(i).map(|s| &s.tok), Some(Token::Newline)) {
959 i += 1;
960 }
961 if !matches!(toks.get(i).map(|s| &s.tok), Some(Token::LeftBrace)) {
962 return Ok(None);
963 }
964 i += 1;
965 let start = i;
966 let mut depth = 1i32;
967 while let Some(s) = toks.get(i) {
968 match &s.tok {
969 Token::LeftBrace => depth += 1,
970 Token::RightBrace => {
971 depth -= 1;
972 if depth == 0 {
973 return Ok(Some((toks[start..i].to_vec(), i + 1)));
974 }
975 }
976 _ => {}
977 }
978 i += 1;
979 }
980 Err(ParseError {
981 msg: "unterminated `{` body".into(),
982 line: 0,
983 col: 0,
984 })
985}
986
987fn static_truth(toks: &[Spanned]) -> Option<bool> {
988 let toks = trim_trailing_eof(toks);
989 match toks {
990 [
991 Spanned {
992 tok: Token::Float(v),
993 ..
994 },
995 ] => Some(*v != 0.0),
996 [
997 Spanned {
998 tok: Token::Not, ..
999 },
1000 Spanned {
1001 tok: Token::Float(v),
1002 ..
1003 },
1004 ] => Some(*v == 0.0),
1005 [a, op, b] => {
1006 let lhs = static_string(a)?;
1007 let rhs = static_string(b)?;
1008 match op.tok {
1009 Token::EqEq => Some(lhs == rhs),
1010 Token::Neq => Some(lhs != rhs),
1011 _ => None,
1012 }
1013 }
1014 [a, op, b, op2, c] if matches!(op2.tok, Token::Plus) => {
1015 let lhs = static_string(a)?;
1016 let mut rhs = static_string(b)?;
1017 rhs.push_str(&static_string(c)?);
1018 match op.tok {
1019 Token::EqEq => Some(lhs == rhs),
1020 Token::Neq => Some(lhs != rhs),
1021 _ => None,
1022 }
1023 }
1024 _ => None,
1025 }
1026}
1027
1028fn trim_trailing_eof(toks: &[Spanned]) -> &[Spanned] {
1029 if matches!(toks.last().map(|s| &s.tok), Some(Token::Eof)) {
1030 &toks[..toks.len() - 1]
1031 } else {
1032 toks
1033 }
1034}
1035
1036fn static_string(s: &Spanned) -> Option<String> {
1037 match &s.tok {
1038 Token::Str(v) => Some(v.clone()),
1039 _ => None,
1040 }
1041}
1042
1043type PResult<T> = Result<T, ParseError>;
1044
1045fn is_assign_op(t: &Token) -> bool {
1046 matches!(
1047 t,
1048 Token::Eq
1049 | Token::ColonEq
1050 | Token::PlusEq
1051 | Token::MinusEq
1052 | Token::MultEq
1053 | Token::DivEq
1054 | Token::RemEq
1055 )
1056}
1057
1058struct Parser {
1059 toks: Vec<Spanned>,
1060 idx: usize,
1061}
1062
1063impl Parser {
1064 fn new(toks: Vec<Spanned>) -> Self {
1065 Parser { toks, idx: 0 }
1066 }
1067
1068 fn cur(&self) -> &Token {
1071 &self.toks[self.idx].tok
1072 }
1073 fn peek(&self, n: usize) -> &Token {
1074 self.toks
1075 .get(self.idx + n)
1076 .map(|s| &s.tok)
1077 .unwrap_or(&Token::Eof)
1078 }
1079 fn at(&self, t: &Token) -> bool {
1080 self.cur() == t
1081 }
1082 fn bump(&mut self) -> Token {
1083 let t = self.toks[self.idx].tok.clone();
1084 if self.idx + 1 < self.toks.len() {
1085 self.idx += 1;
1086 }
1087 t
1088 }
1089 fn eat(&mut self, t: &Token) -> bool {
1090 if self.at(t) {
1091 self.bump();
1092 true
1093 } else {
1094 false
1095 }
1096 }
1097 fn expect(&mut self, t: &Token) -> PResult<()> {
1098 if self.eat(t) {
1099 Ok(())
1100 } else {
1101 self.err(format!("expected {t:?}, found {:?}", self.cur()))
1102 }
1103 }
1104 fn err<T>(&self, msg: impl Into<String>) -> PResult<T> {
1105 let s = &self.toks[self.idx];
1106 Err(ParseError {
1107 msg: msg.into(),
1108 line: s.line,
1109 col: s.col,
1110 })
1111 }
1112 fn at_kw(&self, k: Kw) -> bool {
1113 matches!(self.cur(), Token::Kw(x) if *x == k)
1114 }
1115 fn eat_kw(&mut self, k: Kw) -> bool {
1116 if self.at_kw(k) {
1117 self.bump();
1118 true
1119 } else {
1120 false
1121 }
1122 }
1123 fn skip_newlines(&mut self) {
1124 while self.at(&Token::Newline) {
1125 self.bump();
1126 }
1127 }
1128
1129 fn parse_picture(&mut self) -> PResult<Picture> {
1132 let (mut width, mut height) = (None, None);
1136 let mut seen_ps = false;
1137 let mut stmts = Vec::new();
1138 loop {
1139 self.skip_newlines();
1140 match self.cur() {
1141 Token::Eof => break,
1142 Token::DotPS => {
1143 self.bump();
1144 if !seen_ps && self.starts_scalar() {
1145 width = Some(self.parse_expr()?);
1146 if self.starts_scalar() {
1147 height = Some(self.parse_expr()?);
1148 }
1149 }
1150 seen_ps = true;
1151 while !self.at(&Token::Newline) && !self.at(&Token::Eof) {
1152 self.bump();
1153 }
1154 continue;
1155 }
1156 Token::DotPE => {
1157 self.bump();
1158 continue;
1159 }
1160 _ => {}
1161 }
1162 stmts.push(self.parse_element()?);
1163 if !self.at(&Token::Newline)
1164 && !self.at(&Token::Eof)
1165 && !self.at(&Token::DotPE)
1166 && !self.at(&Token::DotPS)
1167 {
1168 return self.err(format!("unexpected {:?} after statement", self.cur()));
1169 }
1170 }
1171 Ok(Picture {
1172 width,
1173 height,
1174 stmts,
1175 macros: HashMap::new(),
1176 base_dir: None,
1177 })
1178 }
1179
1180 fn parse_elementlist(&mut self, terminators: &[Token]) -> PResult<Vec<Stmt>> {
1182 let mut stmts = Vec::new();
1183 loop {
1184 self.skip_newlines();
1185 if self.at(&Token::Eof) || terminators.iter().any(|t| self.at(t)) {
1186 break;
1187 }
1188 let s = self.parse_element()?;
1189 stmts.push(s);
1190 if !self.at(&Token::Newline)
1192 && !self.at(&Token::Eof)
1193 && !terminators.iter().any(|t| self.at(t))
1194 {
1195 return self.err(format!("unexpected {:?} after statement", self.cur()));
1196 }
1197 }
1198 Ok(stmts)
1199 }
1200
1201 fn parse_element(&mut self) -> PResult<Stmt> {
1204 if self.at(&Token::Percent) {
1208 while !self.at(&Token::Newline) && !self.at(&Token::Eof) {
1209 self.bump();
1210 }
1211 return Ok(Stmt::Print(PrintItem::Str(StringExpr::Lit(String::new()))));
1212 }
1213
1214 if self.at_kw(Kw::Animate) {
1216 return Ok(Stmt::Animate(self.parse_animate()?));
1217 }
1218
1219 match self.cur() {
1221 Token::Kw(Kw::If) => return self.parse_if(),
1222 Token::Kw(Kw::For) => return self.parse_for(),
1223 Token::Kw(Kw::Print) => return self.parse_print(),
1224 Token::Kw(Kw::Exec) => return self.parse_exec(),
1225 Token::Kw(Kw::Reset) => return self.parse_reset(),
1226 _ => {}
1227 }
1228
1229 if let Token::Kw(k) = self.cur() {
1232 match k {
1233 Kw::Define | Kw::Undef => {
1234 return self.err("only the `define name { body }` macro form is supported");
1235 }
1236 Kw::Command | Kw::Sh => {
1240 self.bump();
1241 return Ok(Stmt::Print(PrintItem::Str(StringExpr::Lit(String::new()))));
1242 }
1243 Kw::Copy => {
1244 return self.err("`copy` is not supported yet (planned milestone)");
1245 }
1246 _ => {}
1247 }
1248 }
1249
1250 if self.eat(&Token::LeftBrace) {
1252 let stmts = self.parse_elementlist(&[Token::RightBrace])?;
1253 self.expect(&Token::RightBrace)?;
1254 return Ok(Stmt::Group(stmts));
1255 }
1256
1257 if matches!(self.cur(), Token::Label(_)) && self.label_colon_ahead() {
1259 let label = self.parse_label()?;
1260 self.expect(&Token::Colon)?;
1261 if self.at_object_start() {
1262 let object = self.parse_object()?;
1263 return Ok(Stmt::Object {
1264 label: Some(label),
1265 object,
1266 });
1267 } else {
1268 let pos = self.parse_label_position()?;
1269 return Ok(Stmt::Place { label, pos });
1270 }
1271 }
1272
1273 if self.at_assignment_start() {
1275 return Ok(Stmt::Assign(self.parse_assignlist()?));
1276 }
1277
1278 if let Token::Dir(d) = self.cur() {
1280 let d = *d;
1281 if matches!(self.peek(1), Token::Newline | Token::Eof) {
1283 self.bump();
1284 return Ok(Stmt::Direction(d));
1285 }
1286 }
1287
1288 let object = self.parse_object()?;
1290 Ok(Stmt::Object {
1291 label: None,
1292 object,
1293 })
1294 }
1295
1296 fn parse_if(&mut self) -> PResult<Stmt> {
1297 self.expect_kw(Kw::If)?;
1298 let cond = self.parse_expr()?;
1299 self.expect_kw(Kw::Then)?;
1300 let then_body = self.capture_braced()?;
1301 let save = self.idx;
1304 self.skip_newlines();
1305 let else_body = if self.eat_kw(Kw::Else) {
1306 Some(self.capture_braced()?)
1307 } else {
1308 self.idx = save;
1309 None
1310 };
1311 Ok(Stmt::If {
1312 cond,
1313 then_body,
1314 else_body,
1315 })
1316 }
1317
1318 fn parse_for(&mut self) -> PResult<Stmt> {
1319 self.expect_kw(Kw::For)?;
1320 let var = match self.bump() {
1321 Token::Name(s) | Token::Label(s) => s,
1322 other => return self.err(format!("expected loop variable, found {other:?}")),
1323 };
1324 let subscript = if self.eat(&Token::LeftBrack) {
1325 let e = self.parse_subscript()?;
1326 self.expect(&Token::RightBrack)?;
1327 Some(e)
1328 } else {
1329 None
1330 };
1331 match self.bump() {
1332 Token::Eq | Token::ColonEq => {}
1333 other => return self.err(format!("expected `=` in for, found {other:?}")),
1334 }
1335 let from = self.parse_expr()?;
1336 self.expect_kw(Kw::To)?;
1337 let to = self.parse_expr()?;
1338 let mut by = Expr::Num(1.0);
1339 let mut mult = false;
1340 if self.eat_kw(Kw::By) {
1341 mult = self.eat(&Token::Mult);
1342 by = self.parse_expr()?;
1343 }
1344 self.expect_kw(Kw::Do)?;
1345 let body = self.capture_braced()?;
1346 Ok(Stmt::For {
1347 var,
1348 subscript,
1349 from,
1350 to,
1351 by,
1352 mult,
1353 body,
1354 })
1355 }
1356
1357 fn capture_braced(&mut self) -> PResult<Body> {
1360 self.skip_newlines();
1361 self.expect(&Token::LeftBrace)?;
1362 let start = self.idx;
1363 let mut depth = 1i32;
1364 loop {
1365 match self.cur() {
1366 Token::LeftBrace => depth += 1,
1367 Token::RightBrace => {
1368 depth -= 1;
1369 if depth == 0 {
1370 break;
1371 }
1372 }
1373 Token::Eof => return self.err("unterminated `{` body"),
1374 _ => {}
1375 }
1376 self.bump();
1377 }
1378 let body = self.toks[start..self.idx].to_vec();
1379 self.expect(&Token::RightBrace)?;
1380 Ok(body)
1381 }
1382
1383 fn parse_print(&mut self) -> PResult<Stmt> {
1384 self.expect_kw(Kw::Print)?;
1385 let item = if self.at_string_start() {
1386 PrintItem::Str(self.parse_stringexpr()?)
1387 } else {
1388 PrintItem::Expr(self.parse_expr()?)
1389 };
1390 Ok(Stmt::Print(item))
1391 }
1392
1393 fn parse_exec(&mut self) -> PResult<Stmt> {
1394 let arg_frame = self.toks[self.idx].arg_frame.clone();
1395 self.expect_kw(Kw::Exec)?;
1396 let command = self.parse_stringexpr()?;
1397 Ok(Stmt::Exec { command, arg_frame })
1398 }
1399
1400 fn parse_reset(&mut self) -> PResult<Stmt> {
1401 self.expect_kw(Kw::Reset)?;
1402 let mut list = Vec::new();
1403 if let Token::EnvVar(v) = self.cur() {
1404 list.push(*v);
1405 self.bump();
1406 while self.eat(&Token::Comma) {
1407 match self.cur() {
1408 Token::EnvVar(v) => {
1409 list.push(*v);
1410 self.bump();
1411 }
1412 other => {
1413 return self.err(format!("expected environment variable, found {other:?}"));
1414 }
1415 }
1416 }
1417 }
1418 Ok(Stmt::Reset(list))
1419 }
1420
1421 fn at_string_start(&self) -> bool {
1422 self.token_starts_string_at(0)
1423 }
1424
1425 fn parse_animate(&mut self) -> PResult<Animate> {
1426 self.expect_kw(Kw::Animate)?;
1427 let target = self.parse_place()?;
1428 self.expect_kw(Kw::With)?;
1429 let effect = self.parse_stringexpr()?;
1430 let mut duration = None;
1431 let mut timing = Timing::Sequential;
1432 let mut delay = None;
1433 loop {
1434 if self.eat_kw(Kw::For) {
1435 duration = Some(self.parse_expr()?);
1436 } else if self.eat_kw(Kw::At) {
1437 timing = Timing::At(self.parse_expr()?);
1438 } else if self.eat_kw(Kw::After) {
1439 timing = Timing::After(self.parse_place()?);
1440 } else if self.eat_kw(Kw::Delay) {
1441 delay = Some(self.parse_expr()?);
1442 } else {
1443 break;
1444 }
1445 }
1446 Ok(Animate {
1447 target,
1448 effect,
1449 duration,
1450 timing,
1451 delay,
1452 })
1453 }
1454
1455 fn label_colon_ahead(&self) -> bool {
1457 match self.peek(1) {
1458 Token::Colon => true,
1459 Token::LeftBrack => {
1460 let mut depth = 0;
1462 let mut i = self.idx + 1;
1463 while i < self.toks.len() {
1464 match &self.toks[i].tok {
1465 Token::LeftBrack => depth += 1,
1466 Token::RightBrack => {
1467 depth -= 1;
1468 if depth == 0 {
1469 return matches!(
1470 self.toks.get(i + 1).map(|s| &s.tok),
1471 Some(Token::Colon)
1472 );
1473 }
1474 }
1475 Token::Newline | Token::Eof => return false,
1476 _ => {}
1477 }
1478 i += 1;
1479 }
1480 false
1481 }
1482 _ => false,
1483 }
1484 }
1485
1486 fn at_object_start(&self) -> bool {
1487 matches!(
1488 self.cur(),
1489 Token::Prim(_)
1490 | Token::LeftBrack
1491 | Token::Block
1492 | Token::Str(_)
1493 | Token::Arg(_)
1494 | Token::Kw(Kw::Sprintf)
1495 )
1496 }
1497
1498 fn at_assignment_start(&self) -> bool {
1499 match self.cur() {
1500 Token::Name(_) | Token::Label(_) => self.assignment_op_after_var_ref(),
1501 Token::EnvVar(_) => is_assign_op(self.peek(1)),
1502 _ => false,
1503 }
1504 }
1505
1506 fn assignment_op_after_var_ref(&self) -> bool {
1507 let mut i = self.idx + 1;
1508 if matches!(self.toks.get(i).map(|s| &s.tok), Some(Token::LeftBrack)) {
1509 i += 1;
1510 let mut depth = 1i32;
1511 while let Some(tok) = self.toks.get(i).map(|s| &s.tok) {
1512 match tok {
1513 Token::LeftBrack => depth += 1,
1514 Token::RightBrack => {
1515 depth -= 1;
1516 if depth == 0 {
1517 i += 1;
1518 break;
1519 }
1520 }
1521 Token::Eof | Token::Newline if depth > 0 => return false,
1522 _ => {}
1523 }
1524 i += 1;
1525 }
1526 if depth != 0 {
1527 return false;
1528 }
1529 }
1530 self.toks.get(i).is_some_and(|s| is_assign_op(&s.tok))
1531 }
1532
1533 fn parse_label(&mut self) -> PResult<Label> {
1534 let name = match self.bump() {
1535 Token::Label(s) => s,
1536 other => return self.err(format!("expected label, found {other:?}")),
1537 };
1538 let subscript = if self.eat(&Token::LeftBrack) {
1539 let e = self.parse_subscript()?;
1540 self.expect(&Token::RightBrack)?;
1541 Some(e)
1542 } else {
1543 None
1544 };
1545 Ok(Label { name, subscript })
1546 }
1547
1548 fn parse_assignlist(&mut self) -> PResult<Vec<Assignment>> {
1549 let mut list = vec![self.parse_assignment()?];
1550 while self.eat(&Token::Comma) {
1551 list.push(self.parse_assignment()?);
1552 }
1553 Ok(list)
1554 }
1555
1556 fn parse_assignment(&mut self) -> PResult<Assignment> {
1557 let target = match self.cur().clone() {
1558 Token::Name(name) | Token::Label(name) => {
1559 self.bump();
1560 let sub = if self.eat(&Token::LeftBrack) {
1561 let e = self.parse_subscript()?;
1562 self.expect(&Token::RightBrack)?;
1563 Some(e)
1564 } else {
1565 None
1566 };
1567 AssignTarget::Var(name, sub)
1568 }
1569 Token::EnvVar(v) => {
1570 self.bump();
1571 AssignTarget::Env(v)
1572 }
1573 other => return self.err(format!("expected assignment target, found {other:?}")),
1574 };
1575 let op = match self.bump() {
1576 Token::Eq => AssignOp::Set,
1577 Token::ColonEq => AssignOp::ColonSet,
1578 Token::PlusEq => AssignOp::Add,
1579 Token::MinusEq => AssignOp::Sub,
1580 Token::MultEq => AssignOp::Mul,
1581 Token::DivEq => AssignOp::Div,
1582 Token::RemEq => AssignOp::Rem,
1583 other => return self.err(format!("expected assignment operator, found {other:?}")),
1584 };
1585 let value = self.parse_expr()?;
1586 Ok(Assignment { target, op, value })
1587 }
1588
1589 fn parse_subscript(&mut self) -> PResult<Expr> {
1590 let mut items = vec![self.parse_expr()?];
1591 while self.eat(&Token::Comma) {
1592 items.push(self.parse_expr()?);
1593 }
1594 if items.len() == 1 {
1595 Ok(items.pop().unwrap())
1596 } else {
1597 Ok(Expr::Index(items))
1598 }
1599 }
1600
1601 fn parse_object(&mut self) -> PResult<Object> {
1604 let mut attrs = Vec::new();
1605 if self.at_string_start() {
1608 attrs.push(Attr::Text(self.parse_stringexpr()?));
1609 while let Some(a) = self.parse_attr()? {
1610 attrs.push(a);
1611 }
1612 return Ok(Object {
1613 kind: ObjectKind::Text,
1614 attrs,
1615 });
1616 }
1617 let kind = match self.cur().clone() {
1618 Token::Prim(p) => {
1619 self.bump();
1620 ObjectKind::Primitive(p)
1621 }
1622 Token::Block => {
1623 self.bump();
1624 ObjectKind::Empty
1625 }
1626 Token::LeftBrack => {
1627 self.bump();
1628 let stmts = self.parse_elementlist(&[Token::RightBrack])?;
1629 self.expect(&Token::RightBrack)?;
1630 ObjectKind::Block(stmts)
1631 }
1632 Token::Str(s) => {
1633 self.bump();
1634 attrs.push(Attr::Text(self.continue_string(StringExpr::Lit(s))?));
1635 ObjectKind::Text
1636 }
1637 Token::Kw(Kw::Continue) => {
1638 self.bump();
1639 ObjectKind::Continue
1640 }
1641 other => return self.err(format!("expected an object, found {other:?}")),
1642 };
1643 if matches!(kind, ObjectKind::Primitive(Prim::Spline)) && self.spline_tension_ahead() {
1648 attrs.push(Attr::SplineTension(self.parse_expr()?));
1649 }
1650 while let Some(a) = self.parse_attr()? {
1651 attrs.push(a);
1652 }
1653 Ok(Object { kind, attrs })
1654 }
1655
1656 fn spline_tension_ahead(&self) -> bool {
1660 matches!(
1661 self.cur(),
1662 Token::Float(_)
1663 | Token::Lparen
1664 | Token::EnvVar(_)
1665 | Token::Func1(_)
1666 | Token::Func2(_)
1667 | Token::Name(_)
1668 | Token::Minus
1669 | Token::Plus
1670 | Token::Kw(Kw::Rand)
1671 )
1672 }
1673
1674 fn parse_attr(&mut self) -> PResult<Option<Attr>> {
1675 if self.at_string_start() {
1677 return Ok(Some(Attr::Text(self.parse_stringexpr()?)));
1678 }
1679 let attr = match self.cur().clone() {
1680 Token::Kw(Kw::Ht) => {
1681 self.bump();
1682 Attr::Dim(DimKind::Ht, self.parse_expr()?)
1683 }
1684 Token::Kw(Kw::Wid) => {
1685 self.bump();
1686 Attr::Dim(DimKind::Wid, self.parse_expr()?)
1687 }
1688 Token::Kw(Kw::Rad) => {
1689 self.bump();
1690 Attr::Dim(DimKind::Rad, self.parse_expr()?)
1691 }
1692 Token::Kw(Kw::Diam) => {
1693 self.bump();
1694 Attr::Dim(DimKind::Diam, self.parse_expr()?)
1695 }
1696 Token::Kw(Kw::Thick) => {
1697 self.bump();
1698 Attr::Dim(DimKind::Thick, self.parse_expr()?)
1699 }
1700 Token::Kw(Kw::Scaled) => {
1701 self.bump();
1702 Attr::Dim(DimKind::Scaled, self.parse_expr()?)
1703 }
1704 Token::Dir(d) => {
1705 self.bump();
1706 Attr::Direction(d, self.opt_expr()?)
1707 }
1708 Token::LineType(lt) => {
1709 self.bump();
1710 Attr::LineStyle(lt, self.opt_expr()?)
1711 }
1712 Token::Kw(Kw::Chop) => {
1713 self.bump();
1714 Attr::Chop(self.opt_expr()?)
1715 }
1716 Token::Kw(Kw::Fill) => {
1717 self.bump();
1718 Attr::Fill(self.opt_expr()?)
1719 }
1720 Token::Arrow(a) => {
1721 self.bump();
1722 Attr::Arrowhead(a, self.opt_expr()?)
1723 }
1724 Token::Kw(Kw::Then) => {
1725 self.bump();
1726 Attr::Then
1727 }
1728 Token::Kw(Kw::Cw) => {
1729 self.bump();
1730 Attr::Cw
1731 }
1732 Token::Kw(Kw::Ccw) => {
1733 self.bump();
1734 Attr::Ccw
1735 }
1736 Token::Kw(Kw::Same) => {
1737 self.bump();
1738 Attr::Same
1739 }
1740 Token::Kw(Kw::Continue) => {
1741 self.bump();
1742 Attr::Continue
1743 }
1744 Token::Kw(Kw::From) => {
1745 self.bump();
1746 Attr::From(self.parse_position()?)
1747 }
1748 Token::Kw(Kw::To) => {
1749 self.bump();
1750 Attr::To(self.parse_position()?)
1751 }
1752 Token::Kw(Kw::At) => {
1753 self.bump();
1754 Attr::At(self.parse_position()?)
1755 }
1756 Token::Kw(Kw::By) => {
1757 self.bump();
1758 Attr::By(self.parse_position()?)
1759 }
1760 Token::Kw(Kw::With) => {
1761 self.bump();
1762 let anchor = if self.eat(&Token::Dot) {
1763 WithAnchor::Place(self.parse_place()?)
1764 } else if let Token::Corner(c) = self.cur() {
1765 let c = *c;
1766 self.bump();
1767 WithAnchor::Corner(c)
1768 } else if self.at(&Token::Lparen) {
1769 self.bump();
1770 let x = self.parse_expr()?;
1771 self.expect(&Token::Comma)?;
1772 let y = self.parse_expr()?;
1773 self.expect(&Token::Rparen)?;
1774 WithAnchor::Pair(x, y)
1775 } else {
1776 WithAnchor::Plain
1777 };
1778 self.expect_kw(Kw::At)?;
1779 Attr::With {
1780 anchor,
1781 at: self.parse_position()?,
1782 }
1783 }
1784 Token::TextPos(tp) => {
1785 self.bump();
1786 Attr::TextPos(tp)
1787 }
1788 Token::Color(c) => {
1789 self.bump();
1790 let s = match self.cur().clone() {
1793 Token::Name(n) | Token::Label(n) => {
1794 self.bump();
1795 StringExpr::Lit(n)
1796 }
1797 _ => self.parse_stringexpr()?,
1798 };
1799 Attr::Color(c, s)
1800 }
1801 Token::Float(_)
1804 | Token::Lparen
1805 | Token::EnvVar(_)
1806 | Token::Func1(_)
1807 | Token::Func2(_)
1808 | Token::Name(_)
1809 | Token::Minus
1810 | Token::Plus
1811 | Token::Kw(Kw::Rand) => Attr::Dist(self.parse_expr()?),
1812 _ if self.place_is_scalar_ahead() => Attr::Dist(self.parse_expr()?),
1813 _ => return Ok(None),
1814 };
1815 Ok(Some(attr))
1816 }
1817
1818 fn expect_kw(&mut self, k: Kw) -> PResult<()> {
1819 if self.eat_kw(k) {
1820 Ok(())
1821 } else {
1822 self.err(format!("expected `{k:?}`, found {:?}", self.cur()))
1823 }
1824 }
1825
1826 fn parse_stringexpr(&mut self) -> PResult<StringExpr> {
1829 let first = self.parse_string_atom()?;
1830 self.continue_string(first)
1831 }
1832
1833 fn continue_string(&mut self, first: StringExpr) -> PResult<StringExpr> {
1835 let mut e = first;
1836 while self.at(&Token::Plus) && self.string_after_plus() {
1837 self.bump();
1838 let rhs = self.parse_string_atom()?;
1839 e = StringExpr::Concat(Box::new(e), Box::new(rhs));
1840 }
1841 Ok(e)
1842 }
1843
1844 fn string_after_plus(&self) -> bool {
1845 self.token_starts_string_at(1)
1846 }
1847
1848 fn token_starts_string_at(&self, offset: usize) -> bool {
1849 match self.toks.get(self.idx + offset).map(|s| &s.tok) {
1850 Some(Token::Str(_) | Token::Arg(_) | Token::Kw(Kw::Sprintf)) => true,
1851 Some(Token::Name(n)) if n == "svg_font" => {
1852 matches!(
1853 self.toks.get(self.idx + offset + 1).map(|s| &s.tok),
1854 Some(Token::Lparen)
1855 )
1856 }
1857 _ => false,
1858 }
1859 }
1860
1861 fn parse_string_atom(&mut self) -> PResult<StringExpr> {
1862 match self.cur().clone() {
1863 Token::Str(s) => {
1864 self.bump();
1865 Ok(StringExpr::Lit(s))
1866 }
1867 Token::Arg(n) => {
1868 self.bump();
1869 Ok(StringExpr::Arg(n))
1870 }
1871 Token::Kw(Kw::Sprintf) => {
1872 self.bump();
1873 self.expect(&Token::Lparen)?;
1874 let fmt = self.parse_stringexpr()?;
1875 let mut args = Vec::new();
1876 while self.eat(&Token::Comma) {
1877 args.push(self.parse_expr()?);
1878 }
1879 self.expect(&Token::Rparen)?;
1880 Ok(StringExpr::Sprintf(Box::new(fmt), args))
1881 }
1882 Token::Name(n) if n == "svg_font" && matches!(self.peek(1), Token::Lparen) => {
1883 self.bump();
1884 self.expect(&Token::Lparen)?;
1885 let mut args = Vec::new();
1886 if !self.at(&Token::Rparen) {
1887 args.push(self.parse_expr()?);
1888 while self.eat(&Token::Comma) {
1889 args.push(self.parse_expr()?);
1890 }
1891 }
1892 self.expect(&Token::Rparen)?;
1893 Ok(StringExpr::SvgFont(args))
1894 }
1895 other => self.err(format!("expected a string, found {other:?}")),
1896 }
1897 }
1898
1899 fn parse_label_position(&mut self) -> PResult<Position> {
1902 let save = self.idx;
1903 if let Ok(x) = self.parse_expr()
1904 && self.eat(&Token::Comma)
1905 {
1906 let y = self.parse_expr()?;
1907 return Ok(Position::Pair(x, y));
1908 }
1909 self.idx = save;
1910 self.parse_position()
1911 }
1912
1913 fn parse_position(&mut self) -> PResult<Position> {
1915 let mut left = self.parse_pos_mul()?;
1916 loop {
1917 let sign = if self.at(&Token::Plus) {
1918 Sign::Plus
1919 } else if self.at(&Token::Minus) {
1920 Sign::Minus
1921 } else {
1922 break;
1923 };
1924 self.bump();
1925 let right = self.parse_pos_mul()?;
1926 left = Position::Sum(sign, Box::new(left), Box::new(right));
1927 }
1928 Ok(left)
1929 }
1930
1931 fn parse_pos_mul(&mut self) -> PResult<Position> {
1933 let mut left = self.parse_pos_primary()?;
1934 loop {
1935 if self.eat(&Token::Mult) {
1936 left = Position::Scale(Box::new(left), self.parse_unary()?, false);
1937 } else if self.eat(&Token::Div) {
1938 left = Position::Scale(Box::new(left), self.parse_unary()?, true);
1939 } else {
1940 break;
1941 }
1942 }
1943 Ok(left)
1944 }
1945
1946 fn parse_pos_primary(&mut self) -> PResult<Position> {
1947 if let Some(p) = self.try_fraction()? {
1951 return Ok(p);
1952 }
1953 if self.eat(&Token::Lparen) {
1958 let save = self.idx;
1959 if let Ok(p1) = self.parse_position() {
1964 let p = if self.eat(&Token::Comma) {
1965 let p2 = self.parse_position()?;
1966 Position::Place(Location::ParenPair(Box::new(p1), Box::new(p2)))
1967 } else {
1968 p1 };
1970 self.expect(&Token::Rparen)?;
1971 return Ok(p);
1972 }
1973 self.idx = save;
1974 let e1 = self.parse_add()?;
1975 self.expect(&Token::Comma)?;
1976 let e2 = self.parse_add()?;
1977 self.expect(&Token::Rparen)?;
1978 return Ok(Position::Pair(e1, e2));
1979 }
1980 if self.at_place_start() && !self.place_is_scalar_ahead() {
1983 return Ok(Position::Place(Location::Place(self.parse_place()?)));
1984 }
1985 let e1 = self.parse_add()?;
1987 if self.eat(&Token::Comma) {
1988 let e2 = self.parse_add()?;
1989 return Ok(Position::Pair(e1, e2));
1990 }
1991 self.err("expected `,`, `between`, or `of the way between` in position")
1992 }
1993
1994 fn try_fraction(&mut self) -> PResult<Option<Position>> {
1998 let save = self.idx;
1999 let Ok(frac) = self.parse_add() else {
2000 self.idx = save;
2001 return Ok(None);
2002 };
2003 let mk = |frac, a, b, of_the_way| {
2004 Ok(Some(Position::Between {
2005 frac: Box::new(frac),
2006 a: Box::new(a),
2007 b: Box::new(b),
2008 of_the_way,
2009 }))
2010 };
2011 if self.eat(&Token::Lt) {
2012 let a = self.parse_position()?;
2013 self.expect(&Token::Comma)?;
2014 let b = self.parse_position()?;
2015 self.expect(&Token::Gt)?;
2016 return mk(frac, a, b, false);
2017 }
2018 let of_the_way = if self.at_kw(Kw::Of) {
2019 self.eat_kw(Kw::Of);
2020 if !(self.eat_kw(Kw::The) && self.eat_kw(Kw::Way) && self.eat_kw(Kw::Between)) {
2021 self.idx = save;
2022 return Ok(None);
2023 }
2024 true
2025 } else if self.eat_kw(Kw::Between) {
2026 false
2027 } else {
2028 self.idx = save;
2029 return Ok(None);
2030 };
2031 let a = self.parse_position()?;
2032 self.expect_kw(Kw::And)?;
2033 let b = self.parse_position()?;
2034 mk(frac, a, b, of_the_way)
2035 }
2036
2037 fn place_is_scalar_ahead(&mut self) -> bool {
2040 let save = self.idx;
2041 let parsed = self.parse_place().is_ok();
2042 let scalar = parsed && matches!(self.cur(), Token::DotX | Token::DotY | Token::Param(_));
2043 self.idx = save;
2044 scalar
2045 }
2046
2047 fn at_place_start(&self) -> bool {
2048 match self.cur() {
2049 Token::Label(_) | Token::Block | Token::Corner(_) => true,
2050 Token::Kw(Kw::Last) | Token::Kw(Kw::Here) => true,
2051 Token::Float(_) => matches!(self.peek(1), Token::Kw(Kw::Nth)),
2052 Token::LeftBrace | Token::LeftQuote => true,
2055 _ => false,
2056 }
2057 }
2058
2059 fn parse_place(&mut self) -> PResult<Place> {
2060 if let Token::Corner(c) = self.cur() {
2062 let c = *c;
2063 self.bump();
2064 self.eat_kw(Kw::Of);
2065 let inner = self.parse_place()?;
2066 return Ok(Place::CornerOf(c, Box::new(inner)));
2067 }
2068
2069 let mut place = self.parse_place_base()?;
2070
2071 loop {
2073 if let Token::Corner(c) = self.cur() {
2074 let c = *c;
2075 self.bump();
2076 place = Place::Corner(Box::new(place), c);
2077 } else if self.at(&Token::Dot) {
2078 self.bump();
2079 let rhs = self.parse_place_base()?;
2080 place = Place::Member(Box::new(place), Box::new(rhs));
2081 } else {
2082 break;
2083 }
2084 }
2085 Ok(place)
2086 }
2087
2088 fn parse_place_base(&mut self) -> PResult<Place> {
2089 match self.cur().clone() {
2090 Token::Kw(Kw::Here) => {
2091 self.bump();
2092 Ok(Place::Here)
2093 }
2094 Token::Label(name) => {
2095 self.bump();
2096 let subscript = if self.eat(&Token::LeftBrack) {
2097 let e = self.parse_subscript()?;
2098 self.expect(&Token::RightBrack)?;
2099 Some(Box::new(e))
2100 } else {
2101 None
2102 };
2103 Ok(Place::Name { name, subscript })
2104 }
2105 Token::Kw(Kw::Last) | Token::Float(_) | Token::LeftBrace | Token::LeftQuote => {
2106 let count = self.parse_nth()?;
2107 let obj = if self.at_primobj() {
2111 self.parse_primobj()?
2112 } else {
2113 PrimObj::Any
2114 };
2115 Ok(Place::Nth { count, obj })
2116 }
2117 other => self.err(format!("expected a place, found {other:?}")),
2118 }
2119 }
2120
2121 fn parse_nth(&mut self) -> PResult<Nth> {
2122 if self.eat_kw(Kw::Last) {
2123 return Ok(Nth::Last);
2124 }
2125 let e = self.parse_ncount()?;
2127 self.expect_kw(Kw::Nth)?;
2128 let from_last = self.eat_kw(Kw::Last);
2129 Ok(Nth::Count(Box::new(e), from_last))
2130 }
2131
2132 fn parse_ncount(&mut self) -> PResult<Expr> {
2136 match self.cur().clone() {
2137 Token::Float(v) => {
2138 self.bump();
2139 Ok(Expr::Num(v))
2140 }
2141 Token::LeftBrace => {
2142 self.bump();
2143 let e = self.parse_expr()?;
2144 self.expect(&Token::RightBrace)?;
2145 Ok(e)
2146 }
2147 Token::LeftQuote => {
2148 self.bump();
2149 let e = self.parse_expr()?;
2150 self.expect(&Token::RightQuote)?;
2151 Ok(e)
2152 }
2153 other => self.err(format!("expected an ordinal count, found {other:?}")),
2154 }
2155 }
2156
2157 fn at_primobj(&self) -> bool {
2160 matches!(
2161 self.cur(),
2162 Token::Prim(_) | Token::Block | Token::Str(_) | Token::LeftBrack
2163 )
2164 }
2165
2166 fn parse_primobj(&mut self) -> PResult<PrimObj> {
2167 match self.cur().clone() {
2168 Token::Prim(p) => {
2169 self.bump();
2170 Ok(PrimObj::Prim(p))
2171 }
2172 Token::Block => {
2173 self.bump();
2174 Ok(PrimObj::Block)
2175 }
2176 Token::Str(s) => {
2177 self.bump();
2178 Ok(PrimObj::Str(s))
2179 }
2180 Token::LeftBrack => {
2181 self.bump();
2182 self.expect(&Token::RightBrack)?;
2183 Ok(PrimObj::EmptyBrack)
2184 }
2185 other => self.err(format!("expected a primitive object, found {other:?}")),
2186 }
2187 }
2188
2189 fn opt_expr(&mut self) -> PResult<Option<Expr>> {
2192 if self.starts_scalar() || self.place_is_scalar_ahead() {
2193 Ok(Some(self.parse_expr()?))
2194 } else {
2195 Ok(None)
2196 }
2197 }
2198
2199 fn starts_scalar(&self) -> bool {
2200 matches!(
2201 self.cur(),
2202 Token::Float(_)
2203 | Token::Name(_)
2204 | Token::EnvVar(_)
2205 | Token::Lparen
2206 | Token::Minus
2207 | Token::Plus
2208 | Token::Not
2209 | Token::Func1(_)
2210 | Token::Func2(_)
2211 | Token::Kw(Kw::Rand)
2212 | Token::ArgCount
2213 )
2214 }
2215
2216 fn parse_expr(&mut self) -> PResult<Expr> {
2217 self.parse_or()
2218 }
2219
2220 fn parse_or(&mut self) -> PResult<Expr> {
2221 let mut e = self.parse_and()?;
2222 while self.eat(&Token::OrOr) {
2223 let r = self.parse_and()?;
2224 e = Expr::Bin(BinOp::Or, Box::new(e), Box::new(r));
2225 }
2226 Ok(e)
2227 }
2228
2229 fn parse_and(&mut self) -> PResult<Expr> {
2230 let mut e = self.parse_cmp()?;
2231 while self.eat(&Token::AndAnd) {
2232 let r = self.parse_cmp()?;
2233 e = Expr::Bin(BinOp::And, Box::new(e), Box::new(r));
2234 }
2235 Ok(e)
2236 }
2237
2238 fn parse_cmp(&mut self) -> PResult<Expr> {
2239 let mut e = self.parse_add()?;
2240 loop {
2241 let op = match self.cur() {
2242 Token::EqEq => BinOp::Eq,
2243 Token::Neq => BinOp::Ne,
2244 Token::Lt => BinOp::Lt,
2245 Token::Le => BinOp::Le,
2246 Token::Gt => BinOp::Gt,
2247 Token::Ge => BinOp::Ge,
2248 _ => break,
2249 };
2250 self.bump();
2251 let r = self.parse_add()?;
2252 e = Expr::Bin(op, Box::new(e), Box::new(r));
2253 }
2254 Ok(e)
2255 }
2256
2257 fn parse_add(&mut self) -> PResult<Expr> {
2258 let mut e = self.parse_mul()?;
2259 loop {
2260 let op = match self.cur() {
2261 Token::Plus => BinOp::Add,
2262 Token::Minus => BinOp::Sub,
2263 _ => break,
2264 };
2265 self.bump();
2266 let r = self.parse_mul()?;
2267 e = Expr::Bin(op, Box::new(e), Box::new(r));
2268 }
2269 Ok(e)
2270 }
2271
2272 fn parse_mul(&mut self) -> PResult<Expr> {
2273 let mut e = self.parse_unary()?;
2274 loop {
2275 let op = match self.cur() {
2276 Token::Mult => BinOp::Mul,
2277 Token::Div => BinOp::Div,
2278 Token::Percent => BinOp::Mod,
2279 _ => break,
2280 };
2281 self.bump();
2282 let r = self.parse_unary()?;
2283 e = Expr::Bin(op, Box::new(e), Box::new(r));
2284 }
2285 Ok(e)
2286 }
2287
2288 fn parse_unary(&mut self) -> PResult<Expr> {
2289 let op = match self.cur() {
2290 Token::Minus => Some(UnOp::Neg),
2291 Token::Plus => Some(UnOp::Pos),
2292 Token::Not => Some(UnOp::Not),
2293 _ => None,
2294 };
2295 if let Some(op) = op {
2296 self.bump();
2297 let e = self.parse_unary()?;
2298 Ok(Expr::Unary(op, Box::new(e)))
2299 } else {
2300 self.parse_pow()
2301 }
2302 }
2303
2304 fn parse_pow(&mut self) -> PResult<Expr> {
2305 let base = self.parse_primary()?;
2306 if self.eat(&Token::Caret) {
2307 let exp = self.parse_unary()?; Ok(Expr::Bin(BinOp::Pow, Box::new(base), Box::new(exp)))
2309 } else {
2310 Ok(base)
2311 }
2312 }
2313
2314 fn paren_followed_by_dot_xy(&self) -> bool {
2317 let mut depth = 1i32;
2318 let mut i = self.idx;
2319 while let Some(s) = self.toks.get(i) {
2320 match &s.tok {
2321 Token::Lparen => depth += 1,
2322 Token::Rparen => {
2323 depth -= 1;
2324 if depth == 0 {
2325 return matches!(
2326 self.toks.get(i + 1).map(|t| &t.tok),
2327 Some(Token::DotX | Token::DotY)
2328 );
2329 }
2330 }
2331 Token::Eof => return false,
2332 _ => {}
2333 }
2334 i += 1;
2335 }
2336 false
2337 }
2338
2339 fn parse_primary(&mut self) -> PResult<Expr> {
2340 if self.at_place_start() && self.place_is_scalar_ahead() {
2342 return self.parse_place_scalar();
2343 }
2344 if self.at_string_start() {
2346 return Ok(Expr::Str(self.parse_stringexpr()?));
2347 }
2348 match self.cur().clone() {
2349 Token::Float(v) => {
2350 self.bump();
2351 Ok(Expr::Num(v))
2352 }
2353 Token::Name(name) | Token::Label(name) => {
2354 self.bump();
2355 let subscript = if self.eat(&Token::LeftBrack) {
2356 let e = self.parse_subscript()?;
2357 self.expect(&Token::RightBrack)?;
2358 Some(Box::new(e))
2359 } else {
2360 None
2361 };
2362 Ok(Expr::Var(name, subscript))
2363 }
2364 Token::EnvVar(v) => {
2365 self.bump();
2366 Ok(Expr::Env(v))
2367 }
2368 Token::Lparen => {
2369 self.bump();
2370 if matches!(self.cur(), Token::Name(_) | Token::Label(_))
2372 && self.assignment_op_after_var_ref()
2373 {
2374 let name = match self.bump() {
2375 Token::Name(n) | Token::Label(n) => n,
2376 _ => unreachable!(),
2377 };
2378 let subscript = if self.eat(&Token::LeftBrack) {
2379 let e = self.parse_subscript()?;
2380 self.expect(&Token::RightBrack)?;
2381 Some(Box::new(e))
2382 } else {
2383 None
2384 };
2385 self.bump(); let v = self.parse_expr()?;
2387 self.expect(&Token::Rparen)?;
2388 return Ok(Expr::Assign(name, subscript, Box::new(v)));
2389 }
2390 if self.paren_followed_by_dot_xy() {
2395 let pos = self.parse_position()?;
2396 self.expect(&Token::Rparen)?;
2397 let loc = Location::Paren(Box::new(pos));
2398 return Ok(if self.eat(&Token::DotX) {
2399 Expr::DotX(loc)
2400 } else {
2401 self.expect(&Token::DotY)?;
2402 Expr::DotY(loc)
2403 });
2404 }
2405 let e = self.parse_expr()?;
2407 self.expect(&Token::Rparen)?;
2408 Ok(e)
2409 }
2410 Token::ArgCount => {
2412 self.bump();
2413 Ok(Expr::Num(0.0))
2414 }
2415 Token::Func1(f) => {
2416 self.bump();
2417 self.expect(&Token::Lparen)?;
2418 let e = self.parse_expr()?;
2419 self.expect(&Token::Rparen)?;
2420 Ok(Expr::Func1(f, Box::new(e)))
2421 }
2422 Token::Func2(f) => {
2423 self.bump();
2424 self.expect(&Token::Lparen)?;
2425 let a = self.parse_expr()?;
2426 self.expect(&Token::Comma)?;
2427 let b = self.parse_expr()?;
2428 self.expect(&Token::Rparen)?;
2429 Ok(Expr::Func2(f, Box::new(a), Box::new(b)))
2430 }
2431 Token::Kw(Kw::Rand) => {
2432 self.bump();
2433 self.expect(&Token::Lparen)?;
2434 let arg = if self.at(&Token::Rparen) {
2435 None
2436 } else {
2437 Some(Box::new(self.parse_expr()?))
2438 };
2439 self.expect(&Token::Rparen)?;
2440 Ok(Expr::Rand(arg))
2441 }
2442 other => self.err(format!("expected an expression, found {other:?}")),
2443 }
2444 }
2445
2446 fn parse_place_scalar(&mut self) -> PResult<Expr> {
2448 let place = self.parse_place()?;
2449 match self.cur().clone() {
2450 Token::DotX => {
2451 self.bump();
2452 Ok(Expr::DotX(Location::Place(place)))
2453 }
2454 Token::DotY => {
2455 self.bump();
2456 Ok(Expr::DotY(Location::Place(place)))
2457 }
2458 Token::Param(p) => {
2459 self.bump();
2460 Ok(Expr::PlaceAttr(place, p))
2461 }
2462 other => self.err(format!(
2463 "a place is not a number here; expected `.x`, `.y`, or an attribute, found {other:?}"
2464 )),
2465 }
2466 }
2467}
2468
2469#[cfg(test)]
2470mod tests {
2471 use super::*;
2472
2473 fn pic(src: &str) -> Picture {
2474 parse(src).unwrap_or_else(|e| panic!("parse error: {e}"))
2475 }
2476
2477 #[test]
2478 fn kernighan_pipeline() {
2479 let p = pic(r#".PS
2480ellipse "document"
2481arrow
2482box "PIC"
2483arrow
2484box "TBL/EQN" "(optional)" dashed
2485arrow
2486box "TROFF"
2487arrow
2488ellipse "typesetter"
2489.PE
2490"#);
2491 assert_eq!(p.stmts.len(), 9);
2492 if let Stmt::Object { object, .. } = &p.stmts[4] {
2494 assert_eq!(object.kind, ObjectKind::Primitive(Prim::Box));
2495 let texts = object
2496 .attrs
2497 .iter()
2498 .filter(|a| matches!(a, Attr::Text(_)))
2499 .count();
2500 assert_eq!(texts, 2);
2501 assert!(
2502 object
2503 .attrs
2504 .iter()
2505 .any(|a| matches!(a, Attr::LineStyle(LineType::Dashed, _)))
2506 );
2507 } else {
2508 panic!("expected object");
2509 }
2510 }
2511
2512 #[test]
2513 fn box_with_dims_and_at() {
2514 let p = pic("box ht 0.3 wid 0.5 at 0.25,0.15");
2515 let Stmt::Object { object, .. } = &p.stmts[0] else {
2516 panic!()
2517 };
2518 assert!(matches!(object.attrs[0], Attr::Dim(DimKind::Ht, _)));
2519 assert!(matches!(object.attrs[1], Attr::Dim(DimKind::Wid, _)));
2520 assert!(matches!(object.attrs[2], Attr::At(Position::Pair(_, _))));
2521 }
2522
2523 #[test]
2524 fn labeled_and_corners() {
2525 let p = pic("B1: box\narc -> from top of B1 to last box.ne");
2526 assert!(matches!(p.stmts[0], Stmt::Object { label: Some(_), .. }));
2527 let Stmt::Object { object, .. } = &p.stmts[1] else {
2528 panic!()
2529 };
2530 assert_eq!(object.kind, ObjectKind::Primitive(Prim::Arc));
2531 assert!(
2532 object
2533 .attrs
2534 .iter()
2535 .any(|a| matches!(a, Attr::Arrowhead(Arrow::Right, _)))
2536 );
2537 assert!(object.attrs.iter().any(|a| matches!(
2539 a,
2540 Attr::From(Position::Place(Location::Place(Place::CornerOf(
2541 Corner::N,
2542 _
2543 ))))
2544 )));
2545 }
2546
2547 #[test]
2548 fn with_at_and_shift() {
2549 let p = pic("ellipse \"2\" with .nw at last ellipse.se + (0.1,0)");
2550 let Stmt::Object { object, .. } = &p.stmts[0] else {
2551 panic!()
2552 };
2553 let with = object
2554 .attrs
2555 .iter()
2556 .find(|a| matches!(a, Attr::With { .. }))
2557 .unwrap();
2558 let Attr::With { anchor, at } = with else {
2559 panic!()
2560 };
2561 assert_eq!(*anchor, WithAnchor::Corner(Corner::Nw));
2562 assert!(matches!(at, Position::Sum(Sign::Plus, _, _)));
2564 }
2565
2566 #[test]
2567 fn with_member_anchor_parses() {
2568 let p = pic("[ A: box ] with .A.c at Here");
2569 let Stmt::Object { object, .. } = &p.stmts[0] else {
2570 panic!()
2571 };
2572 let with = object
2573 .attrs
2574 .iter()
2575 .find(|a| matches!(a, Attr::With { .. }))
2576 .unwrap();
2577 let Attr::With { anchor, .. } = with else {
2578 panic!()
2579 };
2580 assert!(matches!(
2581 anchor,
2582 WithAnchor::Place(Place::Corner(inner, Corner::Center))
2583 if matches!(inner.as_ref(), Place::Name { name, .. } if name == "A")
2584 ));
2585 }
2586
2587 #[test]
2588 fn expression_precedence() {
2589 let p = pic("x = 2 + 3 * 4 ^ 2");
2591 let Stmt::Assign(list) = &p.stmts[0] else {
2592 panic!()
2593 };
2594 let Expr::Bin(BinOp::Add, _, rhs) = &list[0].value else {
2596 panic!("expected top-level add")
2597 };
2598 assert!(matches!(**rhs, Expr::Bin(BinOp::Mul, _, _)));
2599 }
2600
2601 #[test]
2602 fn between_position() {
2603 let p = pic("arrow from 1/3 of the way between A.ne and A.se");
2604 let Stmt::Object { object, .. } = &p.stmts[0] else {
2605 panic!()
2606 };
2607 assert!(object.attrs.iter().any(|a| matches!(
2608 a,
2609 Attr::From(Position::Between {
2610 of_the_way: true,
2611 ..
2612 })
2613 )));
2614 }
2615
2616 #[test]
2617 fn assignment_list_and_envvar() {
2618 let p = pic("boxht = 0.3; boxwid = 2 * boxht");
2619 assert_eq!(p.stmts.len(), 2);
2620 let Stmt::Assign(a0) = &p.stmts[0] else {
2621 panic!()
2622 };
2623 assert_eq!(a0[0].target, AssignTarget::Env(EnvVar::Boxht));
2624 }
2625
2626 #[test]
2627 fn dpic_svg_font_stub_parses_as_string() {
2628 let p = pic("print svg_font(\"Times\", 12)");
2629 let Stmt::Print(PrintItem::Str(StringExpr::SvgFont(args))) = &p.stmts[0] else {
2630 panic!()
2631 };
2632 assert_eq!(args.len(), 2);
2633 }
2634
2635 #[test]
2636 fn subscripted_variable_refs_parse() {
2637 let p = pic("P[1] = 2\nx = P[1]");
2638 let Stmt::Assign(a0) = &p.stmts[0] else {
2639 panic!()
2640 };
2641 assert!(matches!(&a0[0].target, AssignTarget::Var(name, Some(_)) if name == "P"));
2642
2643 let Stmt::Assign(a1) = &p.stmts[1] else {
2644 panic!()
2645 };
2646 assert!(matches!(&a1[0].value, Expr::Var(name, Some(_)) if name == "P"));
2647 }
2648
2649 #[test]
2650 fn block_object() {
2651 let p = pic("[ box; circle ] with .nw at Here");
2652 let Stmt::Object { object, .. } = &p.stmts[0] else {
2653 panic!()
2654 };
2655 let ObjectKind::Block(inner) = &object.kind else {
2656 panic!()
2657 };
2658 assert_eq!(inner.len(), 2);
2659 }
2660
2661 #[test]
2662 fn diamond_line_with_then() {
2663 let p = pic("line up right then down right then down left then up left");
2664 let Stmt::Object { object, .. } = &p.stmts[0] else {
2665 panic!()
2666 };
2667 let thens = object
2668 .attrs
2669 .iter()
2670 .filter(|a| matches!(a, Attr::Then))
2671 .count();
2672 assert_eq!(thens, 3);
2673 }
2674
2675 #[test]
2676 fn place_scalar_in_coord_pair() {
2677 let p = pic("A: box\n\"t\" at (A.x, A.y - 0.5)");
2679 let Stmt::Object { object, .. } = &p.stmts[1] else {
2680 panic!()
2681 };
2682 assert!(
2683 object
2684 .attrs
2685 .iter()
2686 .any(|a| matches!(a, Attr::At(Position::Pair(_, _))))
2687 );
2688 let q = pic("A: box\nbox at A.ne");
2690 let Stmt::Object { object, .. } = &q.stmts[1] else {
2691 panic!()
2692 };
2693 assert!(object.attrs.iter().any(|a| matches!(
2694 a,
2695 Attr::At(Position::Place(Location::Place(Place::Corner(_, _))))
2696 )));
2697 }
2698
2699 #[test]
2700 fn ignores_non_svg_backend_preambles() {
2701 let p = pic(r#".PS
2702verbatimtex
2703\global\def\foo#1{#1}
2704etex
2705\global\def\bar#1{#1}
2706\psset{arrowsize=4pt}
2707box
2708.PE
2709"#);
2710 assert_eq!(p.stmts.len(), 1);
2711 let Stmt::Object { object, .. } = &p.stmts[0] else {
2712 panic!()
2713 };
2714 assert_eq!(object.kind, ObjectKind::Primitive(Prim::Box));
2715 }
2716
2717 #[test]
2718 fn backend_filter_keeps_global_lines_inside_strings() {
2719 let p = pic(
2720 "sh \"echo -n \\\"print \\\\\"\\\" > x\"\nif dpicopt==optPGF then { command \"cycle; \\\n\\global\\let\\dpicdraw=x\" } else { box }",
2721 );
2722 assert_eq!(p.stmts.len(), 2);
2723 assert!(matches!(p.stmts[1], Stmt::If { .. }));
2724 }
2725
2726 #[test]
2727 fn static_if_copy_defines_macros_before_following_statements() {
2728 let dir = std::env::temp_dir().join(format!("rpic_static_if_{}", std::process::id()));
2729 std::fs::create_dir_all(&dir).unwrap();
2730 std::fs::write(dir.join("macros.pic"), "define makebox { box wid $1 }\n").unwrap();
2731 std::fs::write(
2732 dir.join("inc.pic"),
2733 "define makecircle { circle rad 0.1 }\n",
2734 )
2735 .unwrap();
2736 let p = parse_in_dir(
2737 "if \"plotlib\" != \"1\" then { copy \"macros.pic\" }\ndefine choose { if \"$1\"==\"\" then { box } else { copy \"$1/inc.pic\" } }\nchoose(.)\nmakecircle()\nmakebox(0.4)",
2738 Some(dir.as_path()),
2739 )
2740 .unwrap_or_else(|e| panic!("parse error: {e}"));
2741 let _ = std::fs::remove_dir_all(&dir);
2742 assert_eq!(p.stmts.len(), 2);
2743 let Stmt::Object { object, .. } = &p.stmts[0] else {
2744 panic!()
2745 };
2746 assert_eq!(object.kind, ObjectKind::Primitive(Prim::Circle));
2747 let Stmt::Object { object, .. } = &p.stmts[1] else {
2748 panic!()
2749 };
2750 assert_eq!(object.kind, ObjectKind::Primitive(Prim::Box));
2751 }
2752
2753 #[test]
2754 fn unsupported_control_is_clear() {
2755 let e = parse("copy \"x\"").unwrap_err();
2757 assert!(e.msg.contains("copy") && e.msg.contains("file"));
2758 }
2759
2760 #[test]
2761 fn control_constructs_parse() {
2762 assert!(parse("for i = 1 to 3 do { box }").is_ok());
2763 assert!(parse("if 1 > 0 then { box } else { circle }").is_ok());
2764 assert!(parse("reset boxht, boxwid").is_ok());
2765 let p = parse("define e { box }\ne\ne").unwrap();
2767 assert_eq!(p.stmts.len(), 2);
2768 }
2769}