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] => match op.tok {
1006 Token::EqEq | Token::Neq => {
1007 if let (Some(lhs), Some(rhs)) = (static_string(a), static_string(b)) {
1008 return Some(if matches!(op.tok, Token::EqEq) {
1009 lhs == rhs
1010 } else {
1011 lhs != rhs
1012 });
1013 }
1014 if let (Some(lhs), Some(rhs)) = (static_number(a), static_number(b)) {
1015 return Some(if matches!(op.tok, Token::EqEq) {
1016 (lhs - rhs).abs() < f64::EPSILON
1017 } else {
1018 (lhs - rhs).abs() >= f64::EPSILON
1019 });
1020 }
1021 None
1022 }
1023 _ => None,
1024 },
1025 [a, op, b, op2, c] if matches!(op2.tok, Token::Plus) => {
1026 let lhs = static_string(a)?;
1027 let mut rhs = static_string(b)?;
1028 rhs.push_str(&static_string(c)?);
1029 match op.tok {
1030 Token::EqEq => Some(lhs == rhs),
1031 Token::Neq => Some(lhs != rhs),
1032 _ => None,
1033 }
1034 }
1035 _ => None,
1036 }
1037}
1038
1039fn trim_trailing_eof(toks: &[Spanned]) -> &[Spanned] {
1040 if matches!(toks.last().map(|s| &s.tok), Some(Token::Eof)) {
1041 &toks[..toks.len() - 1]
1042 } else {
1043 toks
1044 }
1045}
1046
1047fn static_string(s: &Spanned) -> Option<String> {
1048 match &s.tok {
1049 Token::Str(v) => Some(v.clone()),
1050 _ => None,
1051 }
1052}
1053
1054fn static_number(s: &Spanned) -> Option<f64> {
1055 match &s.tok {
1056 Token::Float(v) => Some(*v),
1057 Token::Name(n) | Token::Label(n) => dpic_backend_constant(n),
1058 _ => None,
1059 }
1060}
1061
1062fn dpic_backend_constant(name: &str) -> Option<f64> {
1063 match name {
1064 "optMFpic" => Some(0.0),
1065 "optMpost" => Some(1.0),
1066 "optPDF" => Some(2.0),
1067 "optPGF" => Some(3.0),
1068 "optPict2e" => Some(4.0),
1069 "optPS" => Some(5.0),
1070 "optPSfrag" => Some(6.0),
1071 "optPSTricks" => Some(7.0),
1072 "optSVG" | "dpicopt" => Some(8.0),
1073 "optTeX" => Some(9.0),
1074 "opttTeX" => Some(10.0),
1075 "optxfig" => Some(11.0),
1076 _ => None,
1077 }
1078}
1079
1080type PResult<T> = Result<T, ParseError>;
1081
1082fn is_assign_op(t: &Token) -> bool {
1083 matches!(
1084 t,
1085 Token::Eq
1086 | Token::ColonEq
1087 | Token::PlusEq
1088 | Token::MinusEq
1089 | Token::MultEq
1090 | Token::DivEq
1091 | Token::RemEq
1092 )
1093}
1094
1095struct Parser {
1096 toks: Vec<Spanned>,
1097 idx: usize,
1098}
1099
1100impl Parser {
1101 fn new(toks: Vec<Spanned>) -> Self {
1102 Parser { toks, idx: 0 }
1103 }
1104
1105 fn cur(&self) -> &Token {
1108 &self.toks[self.idx].tok
1109 }
1110 fn peek(&self, n: usize) -> &Token {
1111 self.toks
1112 .get(self.idx + n)
1113 .map(|s| &s.tok)
1114 .unwrap_or(&Token::Eof)
1115 }
1116 fn at(&self, t: &Token) -> bool {
1117 self.cur() == t
1118 }
1119 fn bump(&mut self) -> Token {
1120 let t = self.toks[self.idx].tok.clone();
1121 if self.idx + 1 < self.toks.len() {
1122 self.idx += 1;
1123 }
1124 t
1125 }
1126 fn eat(&mut self, t: &Token) -> bool {
1127 if self.at(t) {
1128 self.bump();
1129 true
1130 } else {
1131 false
1132 }
1133 }
1134 fn expect(&mut self, t: &Token) -> PResult<()> {
1135 if self.eat(t) {
1136 Ok(())
1137 } else {
1138 self.err(format!("expected {t:?}, found {:?}", self.cur()))
1139 }
1140 }
1141 fn err<T>(&self, msg: impl Into<String>) -> PResult<T> {
1142 let s = &self.toks[self.idx];
1143 Err(ParseError {
1144 msg: msg.into(),
1145 line: s.line,
1146 col: s.col,
1147 })
1148 }
1149 fn at_kw(&self, k: Kw) -> bool {
1150 matches!(self.cur(), Token::Kw(x) if *x == k)
1151 }
1152 fn eat_kw(&mut self, k: Kw) -> bool {
1153 if self.at_kw(k) {
1154 self.bump();
1155 true
1156 } else {
1157 false
1158 }
1159 }
1160 fn skip_newlines(&mut self) {
1161 while self.at(&Token::Newline) {
1162 self.bump();
1163 }
1164 }
1165
1166 fn parse_picture(&mut self) -> PResult<Picture> {
1169 let (mut width, mut height) = (None, None);
1173 let mut seen_ps = false;
1174 let mut stmts = Vec::new();
1175 loop {
1176 self.skip_newlines();
1177 match self.cur() {
1178 Token::Eof => break,
1179 Token::DotPS => {
1180 self.bump();
1181 if !seen_ps && self.starts_scalar() {
1182 width = Some(self.parse_expr()?);
1183 if self.starts_scalar() {
1184 height = Some(self.parse_expr()?);
1185 }
1186 }
1187 seen_ps = true;
1188 while !self.at(&Token::Newline) && !self.at(&Token::Eof) {
1189 self.bump();
1190 }
1191 continue;
1192 }
1193 Token::DotPE => {
1194 self.bump();
1195 continue;
1196 }
1197 _ => {}
1198 }
1199 stmts.push(self.parse_element()?);
1200 if !self.at(&Token::Newline)
1201 && !self.at(&Token::Eof)
1202 && !self.at(&Token::DotPE)
1203 && !self.at(&Token::DotPS)
1204 {
1205 return self.err(format!("unexpected {:?} after statement", self.cur()));
1206 }
1207 }
1208 Ok(Picture {
1209 width,
1210 height,
1211 stmts,
1212 macros: HashMap::new(),
1213 base_dir: None,
1214 })
1215 }
1216
1217 fn parse_elementlist(&mut self, terminators: &[Token]) -> PResult<Vec<Stmt>> {
1219 let mut stmts = Vec::new();
1220 loop {
1221 self.skip_newlines();
1222 if self.at(&Token::Eof) || terminators.iter().any(|t| self.at(t)) {
1223 break;
1224 }
1225 let s = self.parse_element()?;
1226 stmts.push(s);
1227 if !self.at(&Token::Newline)
1229 && !self.at(&Token::Eof)
1230 && !terminators.iter().any(|t| self.at(t))
1231 {
1232 return self.err(format!("unexpected {:?} after statement", self.cur()));
1233 }
1234 }
1235 Ok(stmts)
1236 }
1237
1238 fn parse_element(&mut self) -> PResult<Stmt> {
1241 if self.at(&Token::Percent) {
1245 while !self.at(&Token::Newline) && !self.at(&Token::Eof) {
1246 self.bump();
1247 }
1248 return Ok(Stmt::Print(PrintItem::Str(StringExpr::Lit(String::new()))));
1249 }
1250
1251 if self.at_kw(Kw::Animate) {
1253 return Ok(Stmt::Animate(self.parse_animate()?));
1254 }
1255
1256 match self.cur() {
1258 Token::Kw(Kw::If) => return self.parse_if(),
1259 Token::Kw(Kw::For) => return self.parse_for(),
1260 Token::Kw(Kw::Print) => return self.parse_print(),
1261 Token::Kw(Kw::Exec) => return self.parse_exec(),
1262 Token::Kw(Kw::Reset) => return self.parse_reset(),
1263 _ => {}
1264 }
1265
1266 if let Token::Kw(k) = self.cur() {
1269 match k {
1270 Kw::Define | Kw::Undef => {
1271 return self.err("only the `define name { body }` macro form is supported");
1272 }
1273 Kw::Command | Kw::Sh => {
1279 self.bump();
1280 return Ok(Stmt::Group(Vec::new()));
1281 }
1282 Kw::Copy => {
1283 return self.err("`copy` is not supported yet (planned milestone)");
1284 }
1285 _ => {}
1286 }
1287 }
1288
1289 if self.eat(&Token::LeftBrace) {
1291 let stmts = self.parse_elementlist(&[Token::RightBrace])?;
1292 self.expect(&Token::RightBrace)?;
1293 return Ok(Stmt::Group(stmts));
1294 }
1295
1296 if matches!(self.cur(), Token::Label(_)) && self.label_colon_ahead() {
1298 let label = self.parse_label()?;
1299 self.expect(&Token::Colon)?;
1300 if self.at_object_start() {
1301 let object = self.parse_object()?;
1302 return Ok(Stmt::Object {
1303 label: Some(label),
1304 object,
1305 });
1306 } else {
1307 let pos = self.parse_label_position()?;
1308 return Ok(Stmt::Place { label, pos });
1309 }
1310 }
1311
1312 if self.at_assignment_start() {
1314 return Ok(Stmt::Assign(self.parse_assignlist()?));
1315 }
1316
1317 if let Token::Dir(d) = self.cur() {
1319 let d = *d;
1320 if matches!(self.peek(1), Token::Newline | Token::Eof) {
1322 self.bump();
1323 return Ok(Stmt::Direction(d));
1324 }
1325 }
1326
1327 let object = self.parse_object()?;
1329 Ok(Stmt::Object {
1330 label: None,
1331 object,
1332 })
1333 }
1334
1335 fn parse_if(&mut self) -> PResult<Stmt> {
1336 self.expect_kw(Kw::If)?;
1337 let cond = self.parse_expr()?;
1338 self.expect_kw(Kw::Then)?;
1339 let then_body = self.capture_braced()?;
1340 let save = self.idx;
1343 self.skip_newlines();
1344 let else_body = if self.eat_kw(Kw::Else) {
1345 Some(self.capture_braced()?)
1346 } else {
1347 self.idx = save;
1348 None
1349 };
1350 Ok(Stmt::If {
1351 cond,
1352 then_body,
1353 else_body,
1354 })
1355 }
1356
1357 fn parse_for(&mut self) -> PResult<Stmt> {
1358 self.expect_kw(Kw::For)?;
1359 let var = match self.bump() {
1360 Token::Name(s) | Token::Label(s) => s,
1361 other => return self.err(format!("expected loop variable, found {other:?}")),
1362 };
1363 let subscript = if self.eat(&Token::LeftBrack) {
1364 let e = self.parse_subscript()?;
1365 self.expect(&Token::RightBrack)?;
1366 Some(e)
1367 } else {
1368 None
1369 };
1370 match self.bump() {
1371 Token::Eq | Token::ColonEq => {}
1372 other => return self.err(format!("expected `=` in for, found {other:?}")),
1373 }
1374 let from = self.parse_expr()?;
1375 self.expect_kw(Kw::To)?;
1376 let to = self.parse_expr()?;
1377 let mut by = Expr::Num(1.0);
1378 let mut mult = false;
1379 if self.eat_kw(Kw::By) {
1380 mult = self.eat(&Token::Mult);
1381 by = self.parse_expr()?;
1382 }
1383 self.expect_kw(Kw::Do)?;
1384 let body = self.capture_braced()?;
1385 Ok(Stmt::For {
1386 var,
1387 subscript,
1388 from,
1389 to,
1390 by,
1391 mult,
1392 body,
1393 })
1394 }
1395
1396 fn capture_braced(&mut self) -> PResult<Body> {
1399 self.skip_newlines();
1400 self.expect(&Token::LeftBrace)?;
1401 let start = self.idx;
1402 let mut depth = 1i32;
1403 loop {
1404 match self.cur() {
1405 Token::LeftBrace => depth += 1,
1406 Token::RightBrace => {
1407 depth -= 1;
1408 if depth == 0 {
1409 break;
1410 }
1411 }
1412 Token::Eof => return self.err("unterminated `{` body"),
1413 _ => {}
1414 }
1415 self.bump();
1416 }
1417 let body = self.toks[start..self.idx].to_vec();
1418 self.expect(&Token::RightBrace)?;
1419 Ok(body)
1420 }
1421
1422 fn parse_print(&mut self) -> PResult<Stmt> {
1423 self.expect_kw(Kw::Print)?;
1424 let item = if self.at_string_start() {
1425 PrintItem::Str(self.parse_stringexpr()?)
1426 } else {
1427 PrintItem::Expr(self.parse_expr()?)
1428 };
1429 Ok(Stmt::Print(item))
1430 }
1431
1432 fn parse_exec(&mut self) -> PResult<Stmt> {
1433 let arg_frame = self.toks[self.idx].arg_frame.clone();
1434 self.expect_kw(Kw::Exec)?;
1435 let command = self.parse_stringexpr()?;
1436 Ok(Stmt::Exec { command, arg_frame })
1437 }
1438
1439 fn parse_reset(&mut self) -> PResult<Stmt> {
1440 self.expect_kw(Kw::Reset)?;
1441 let mut list = Vec::new();
1442 if let Token::EnvVar(v) = self.cur() {
1443 list.push(*v);
1444 self.bump();
1445 while self.eat(&Token::Comma) {
1446 match self.cur() {
1447 Token::EnvVar(v) => {
1448 list.push(*v);
1449 self.bump();
1450 }
1451 other => {
1452 return self.err(format!("expected environment variable, found {other:?}"));
1453 }
1454 }
1455 }
1456 }
1457 Ok(Stmt::Reset(list))
1458 }
1459
1460 fn at_string_start(&self) -> bool {
1461 self.token_starts_string_at(0)
1462 }
1463
1464 fn parse_animate(&mut self) -> PResult<Animate> {
1465 self.expect_kw(Kw::Animate)?;
1466 let target = self.parse_place()?;
1467 self.expect_kw(Kw::With)?;
1468 let effect = self.parse_stringexpr()?;
1469 let mut duration = None;
1470 let mut timing = Timing::Sequential;
1471 let mut delay = None;
1472 loop {
1473 if self.eat_kw(Kw::For) {
1474 duration = Some(self.parse_expr()?);
1475 } else if self.eat_kw(Kw::At) {
1476 timing = Timing::At(self.parse_expr()?);
1477 } else if self.eat_kw(Kw::After) {
1478 timing = Timing::After(self.parse_place()?);
1479 } else if self.eat_kw(Kw::Delay) {
1480 delay = Some(self.parse_expr()?);
1481 } else {
1482 break;
1483 }
1484 }
1485 Ok(Animate {
1486 target,
1487 effect,
1488 duration,
1489 timing,
1490 delay,
1491 })
1492 }
1493
1494 fn label_colon_ahead(&self) -> bool {
1496 match self.peek(1) {
1497 Token::Colon => true,
1498 Token::LeftBrack => {
1499 let mut depth = 0;
1501 let mut i = self.idx + 1;
1502 while i < self.toks.len() {
1503 match &self.toks[i].tok {
1504 Token::LeftBrack => depth += 1,
1505 Token::RightBrack => {
1506 depth -= 1;
1507 if depth == 0 {
1508 return matches!(
1509 self.toks.get(i + 1).map(|s| &s.tok),
1510 Some(Token::Colon)
1511 );
1512 }
1513 }
1514 Token::Newline | Token::Eof => return false,
1515 _ => {}
1516 }
1517 i += 1;
1518 }
1519 false
1520 }
1521 _ => false,
1522 }
1523 }
1524
1525 fn at_object_start(&self) -> bool {
1526 matches!(
1527 self.cur(),
1528 Token::Prim(_)
1529 | Token::LeftBrack
1530 | Token::Block
1531 | Token::Str(_)
1532 | Token::Arg(_)
1533 | Token::Kw(Kw::Sprintf)
1534 ) || matches!(self.cur(), Token::Name(n) if n == "brace")
1535 }
1536
1537 fn at_assignment_start(&self) -> bool {
1538 match self.cur() {
1539 Token::Name(_) | Token::Label(_) => self.assignment_op_after_var_ref(),
1540 Token::EnvVar(_) => is_assign_op(self.peek(1)),
1541 _ => false,
1542 }
1543 }
1544
1545 fn assignment_op_after_var_ref(&self) -> bool {
1546 let mut i = self.idx + 1;
1547 if matches!(self.toks.get(i).map(|s| &s.tok), Some(Token::LeftBrack)) {
1548 i += 1;
1549 let mut depth = 1i32;
1550 while let Some(tok) = self.toks.get(i).map(|s| &s.tok) {
1551 match tok {
1552 Token::LeftBrack => depth += 1,
1553 Token::RightBrack => {
1554 depth -= 1;
1555 if depth == 0 {
1556 i += 1;
1557 break;
1558 }
1559 }
1560 Token::Eof | Token::Newline if depth > 0 => return false,
1561 _ => {}
1562 }
1563 i += 1;
1564 }
1565 if depth != 0 {
1566 return false;
1567 }
1568 }
1569 self.toks.get(i).is_some_and(|s| is_assign_op(&s.tok))
1570 }
1571
1572 fn parse_label(&mut self) -> PResult<Label> {
1573 let name = match self.bump() {
1574 Token::Label(s) => s,
1575 other => return self.err(format!("expected label, found {other:?}")),
1576 };
1577 let subscript = if self.eat(&Token::LeftBrack) {
1578 let e = self.parse_subscript()?;
1579 self.expect(&Token::RightBrack)?;
1580 Some(e)
1581 } else {
1582 None
1583 };
1584 Ok(Label { name, subscript })
1585 }
1586
1587 fn parse_assignlist(&mut self) -> PResult<Vec<Assignment>> {
1588 let mut list = vec![self.parse_assignment()?];
1589 while self.eat(&Token::Comma) {
1590 list.push(self.parse_assignment()?);
1591 }
1592 Ok(list)
1593 }
1594
1595 fn parse_assignment(&mut self) -> PResult<Assignment> {
1596 let target = match self.cur().clone() {
1597 Token::Name(name) | Token::Label(name) => {
1598 self.bump();
1599 let sub = if self.eat(&Token::LeftBrack) {
1600 let e = self.parse_subscript()?;
1601 self.expect(&Token::RightBrack)?;
1602 Some(e)
1603 } else {
1604 None
1605 };
1606 AssignTarget::Var(name, sub)
1607 }
1608 Token::EnvVar(v) => {
1609 self.bump();
1610 AssignTarget::Env(v)
1611 }
1612 other => return self.err(format!("expected assignment target, found {other:?}")),
1613 };
1614 let op = match self.bump() {
1615 Token::Eq => AssignOp::Set,
1616 Token::ColonEq => AssignOp::ColonSet,
1617 Token::PlusEq => AssignOp::Add,
1618 Token::MinusEq => AssignOp::Sub,
1619 Token::MultEq => AssignOp::Mul,
1620 Token::DivEq => AssignOp::Div,
1621 Token::RemEq => AssignOp::Rem,
1622 other => return self.err(format!("expected assignment operator, found {other:?}")),
1623 };
1624 let value = self.parse_expr()?;
1625 Ok(Assignment { target, op, value })
1626 }
1627
1628 fn parse_subscript(&mut self) -> PResult<Expr> {
1629 let mut items = vec![self.parse_expr()?];
1630 while self.eat(&Token::Comma) {
1631 items.push(self.parse_expr()?);
1632 }
1633 if items.len() == 1 {
1634 Ok(items.pop().unwrap())
1635 } else {
1636 Ok(Expr::Index(items))
1637 }
1638 }
1639
1640 fn parse_object(&mut self) -> PResult<Object> {
1643 let mut attrs = Vec::new();
1644 if self.at_string_start() {
1647 attrs.push(Attr::Text(self.parse_stringexpr()?));
1648 while let Some(a) = self.parse_attr(false, false, false, false)? {
1649 attrs.push(a);
1650 }
1651 return Ok(Object {
1652 kind: ObjectKind::Text,
1653 attrs,
1654 });
1655 }
1656 let kind = match self.cur().clone() {
1657 Token::Prim(p) => {
1658 self.bump();
1659 ObjectKind::Primitive(p)
1660 }
1661 Token::Block => {
1662 self.bump();
1663 ObjectKind::Empty
1664 }
1665 Token::Name(n) if n == "brace" => {
1666 self.bump();
1667 ObjectKind::Brace
1668 }
1669 Token::LeftBrack => {
1670 self.bump();
1671 let stmts = self.parse_elementlist(&[Token::RightBrack])?;
1672 self.expect(&Token::RightBrack)?;
1673 ObjectKind::Block(stmts)
1674 }
1675 Token::Str(s) => {
1676 self.bump();
1677 attrs.push(Attr::Text(self.continue_string(StringExpr::Lit(s))?));
1678 ObjectKind::Text
1679 }
1680 Token::Kw(Kw::Continue) => {
1681 self.bump();
1682 ObjectKind::Continue
1683 }
1684 other => return self.err(format!("expected an object, found {other:?}")),
1685 };
1686 if matches!(kind, ObjectKind::Primitive(Prim::Spline)) && self.spline_tension_ahead() {
1691 attrs.push(Attr::SplineTension(self.parse_expr()?));
1692 }
1693 let allow_fit = matches!(
1694 kind,
1695 ObjectKind::Primitive(Prim::Box | Prim::Circle | Prim::Ellipse)
1696 );
1697 let allow_brace = matches!(kind, ObjectKind::Brace);
1698 let allow_hatch = matches!(
1699 kind,
1700 ObjectKind::Primitive(
1701 Prim::Box
1702 | Prim::Circle
1703 | Prim::Ellipse
1704 | Prim::Line
1705 | Prim::Arrow
1706 | Prim::Spline
1707 | Prim::Arc
1708 )
1709 );
1710 let allow_close = matches!(kind, ObjectKind::Primitive(Prim::Line));
1711 while let Some(a) = self.parse_attr(allow_fit, allow_brace, allow_hatch, allow_close)? {
1712 attrs.push(a);
1713 }
1714 Ok(Object { kind, attrs })
1715 }
1716
1717 fn spline_tension_ahead(&self) -> bool {
1721 matches!(
1722 self.cur(),
1723 Token::Float(_)
1724 | Token::Lparen
1725 | Token::EnvVar(_)
1726 | Token::Func1(_)
1727 | Token::Func2(_)
1728 | Token::Name(_)
1729 | Token::Minus
1730 | Token::Plus
1731 | Token::Kw(Kw::Rand)
1732 )
1733 }
1734
1735 fn parse_attr(
1736 &mut self,
1737 allow_fit: bool,
1738 allow_brace: bool,
1739 allow_hatch: bool,
1740 allow_close: bool,
1741 ) -> PResult<Option<Attr>> {
1742 if self.at_string_start() {
1744 return Ok(Some(Attr::Text(self.parse_stringexpr()?)));
1745 }
1746 let attr = match self.cur().clone() {
1747 Token::Kw(Kw::Ht) => {
1748 self.bump();
1749 Attr::Dim(DimKind::Ht, self.parse_expr()?)
1750 }
1751 Token::Kw(Kw::Wid) => {
1752 self.bump();
1753 Attr::Dim(DimKind::Wid, self.parse_expr()?)
1754 }
1755 Token::Kw(Kw::Rad) => {
1756 self.bump();
1757 Attr::Dim(DimKind::Rad, self.parse_expr()?)
1758 }
1759 Token::Kw(Kw::Diam) => {
1760 self.bump();
1761 Attr::Dim(DimKind::Diam, self.parse_expr()?)
1762 }
1763 Token::Kw(Kw::Thick) => {
1764 self.bump();
1765 Attr::Dim(DimKind::Thick, self.parse_expr()?)
1766 }
1767 Token::Kw(Kw::Scaled) => {
1768 self.bump();
1769 Attr::Dim(DimKind::Scaled, self.parse_expr()?)
1770 }
1771 Token::Dir(d) => {
1772 self.bump();
1773 Attr::Direction(
1774 d,
1775 self.opt_attr_expr(allow_fit, allow_brace, allow_hatch, allow_close)?,
1776 )
1777 }
1778 Token::LineType(lt) => {
1779 self.bump();
1780 Attr::LineStyle(
1781 lt,
1782 self.opt_attr_expr(allow_fit, allow_brace, allow_hatch, allow_close)?,
1783 )
1784 }
1785 Token::Kw(Kw::Chop) => {
1786 self.bump();
1787 Attr::Chop(self.opt_attr_expr(allow_fit, allow_brace, allow_hatch, allow_close)?)
1788 }
1789 Token::Kw(Kw::Fill) => {
1790 self.bump();
1791 Attr::Fill(self.opt_attr_expr(allow_fit, allow_brace, allow_hatch, allow_close)?)
1792 }
1793 Token::Arrow(a) => {
1794 self.bump();
1795 Attr::Arrowhead(
1796 a,
1797 self.opt_attr_expr(allow_fit, allow_brace, allow_hatch, allow_close)?,
1798 )
1799 }
1800 Token::Kw(Kw::Then) => {
1801 self.bump();
1802 Attr::Then
1803 }
1804 Token::Kw(Kw::Cw) => {
1805 self.bump();
1806 Attr::Cw
1807 }
1808 Token::Kw(Kw::Ccw) => {
1809 self.bump();
1810 Attr::Ccw
1811 }
1812 Token::Kw(Kw::Same) => {
1813 self.bump();
1814 Attr::Same
1815 }
1816 Token::Kw(Kw::Continue) => {
1817 self.bump();
1818 Attr::Continue
1819 }
1820 Token::Kw(Kw::From) => {
1821 self.bump();
1822 Attr::From(self.parse_position()?)
1823 }
1824 Token::Kw(Kw::To) => {
1825 self.bump();
1826 Attr::To(self.parse_position()?)
1827 }
1828 Token::Kw(Kw::At) => {
1829 self.bump();
1830 Attr::At(self.parse_position()?)
1831 }
1832 Token::Kw(Kw::By) => {
1833 self.bump();
1834 Attr::By(self.parse_position()?)
1835 }
1836 Token::Kw(Kw::With) => {
1837 self.bump();
1838 let anchor = if self.eat(&Token::Dot) {
1839 WithAnchor::Place(self.parse_place()?)
1840 } else if let Token::Corner(c) = self.cur() {
1841 let c = *c;
1842 self.bump();
1843 WithAnchor::Corner(c)
1844 } else if self.at(&Token::Lparen) {
1845 self.bump();
1846 let x = self.parse_expr()?;
1847 self.expect(&Token::Comma)?;
1848 let y = self.parse_expr()?;
1849 self.expect(&Token::Rparen)?;
1850 WithAnchor::Pair(x, y)
1851 } else {
1852 WithAnchor::Plain
1853 };
1854 self.expect_kw(Kw::At)?;
1855 Attr::With {
1856 anchor,
1857 at: self.parse_position()?,
1858 }
1859 }
1860 Token::TextPos(tp) => {
1861 self.bump();
1862 Attr::TextPos(tp)
1863 }
1864 Token::Color(c) => {
1865 self.bump();
1866 let s = match self.cur().clone() {
1869 Token::Name(n) | Token::Label(n) => {
1870 self.bump();
1871 StringExpr::Lit(n)
1872 }
1873 _ => self.parse_stringexpr()?,
1874 };
1875 Attr::Color(c, s)
1876 }
1877 Token::Name(n) if allow_fit && n == "fit" => {
1878 self.bump();
1879 Attr::Fit
1880 }
1881 Token::Name(n) if allow_hatch && n == "hatch" => {
1882 self.bump();
1883 Attr::Hatch(HatchKind::Single)
1884 }
1885 Token::Name(n) if allow_hatch && n == "crosshatch" => {
1886 self.bump();
1887 Attr::Hatch(HatchKind::Cross)
1888 }
1889 Token::Name(n) if allow_hatch && n == "hatchangle" => {
1890 self.bump();
1891 Attr::HatchAngle(self.parse_expr()?)
1892 }
1893 Token::Name(n) if allow_hatch && n == "hatchsep" => {
1894 self.bump();
1895 Attr::HatchSep(self.parse_expr()?)
1896 }
1897 Token::Name(n) if allow_hatch && (n == "hatchwid" || n == "hatchwidth") => {
1898 self.bump();
1899 Attr::HatchWidth(self.parse_expr()?)
1900 }
1901 Token::Name(n) if allow_hatch && n == "hatchcolor" => {
1902 self.bump();
1903 let s = match self.cur().clone() {
1904 Token::Name(n) | Token::Label(n) => {
1905 self.bump();
1906 StringExpr::Lit(n)
1907 }
1908 _ => self.parse_stringexpr()?,
1909 };
1910 Attr::HatchColor(s)
1911 }
1912 Token::Name(n) if n == "opacity" => {
1913 self.bump();
1914 Attr::Opacity(self.parse_expr()?)
1915 }
1916 Token::Name(n) if allow_close && n == "close" => {
1917 self.bump();
1918 Attr::Close
1919 }
1920 Token::Name(n) if allow_brace && n == "bracepos" => {
1921 self.bump();
1922 Attr::BracePos(self.parse_expr()?)
1923 }
1924 Token::Name(n) if allow_brace && n == "labeloffset" => {
1925 self.bump();
1926 Attr::BraceLabelOffset(self.parse_expr()?)
1927 }
1928 Token::Name(n) if n == "behind" => {
1929 self.bump();
1930 Attr::Behind(self.parse_place()?)
1931 }
1932 Token::Float(_)
1935 | Token::Lparen
1936 | Token::EnvVar(_)
1937 | Token::Func1(_)
1938 | Token::Func2(_)
1939 | Token::Name(_)
1940 | Token::Minus
1941 | Token::Plus
1942 | Token::Kw(Kw::Rand) => Attr::Dist(self.parse_expr()?),
1943 _ if self.place_is_scalar_ahead() => Attr::Dist(self.parse_expr()?),
1944 _ => return Ok(None),
1945 };
1946 Ok(Some(attr))
1947 }
1948
1949 fn expect_kw(&mut self, k: Kw) -> PResult<()> {
1950 if self.eat_kw(k) {
1951 Ok(())
1952 } else {
1953 self.err(format!("expected `{k:?}`, found {:?}", self.cur()))
1954 }
1955 }
1956
1957 fn parse_stringexpr(&mut self) -> PResult<StringExpr> {
1960 let first = self.parse_string_atom()?;
1961 self.continue_string(first)
1962 }
1963
1964 fn continue_string(&mut self, first: StringExpr) -> PResult<StringExpr> {
1966 let mut e = first;
1967 while self.at(&Token::Plus) && self.string_after_plus() {
1968 self.bump();
1969 let rhs = self.parse_string_atom()?;
1970 e = StringExpr::Concat(Box::new(e), Box::new(rhs));
1971 }
1972 Ok(e)
1973 }
1974
1975 fn string_after_plus(&self) -> bool {
1976 self.token_starts_string_at(1)
1977 }
1978
1979 fn token_starts_string_at(&self, offset: usize) -> bool {
1980 match self.toks.get(self.idx + offset).map(|s| &s.tok) {
1981 Some(Token::Str(_) | Token::Arg(_) | Token::Kw(Kw::Sprintf)) => true,
1982 Some(Token::Name(n)) if n == "svg_font" => {
1983 matches!(
1984 self.toks.get(self.idx + offset + 1).map(|s| &s.tok),
1985 Some(Token::Lparen)
1986 )
1987 }
1988 _ => false,
1989 }
1990 }
1991
1992 fn parse_string_atom(&mut self) -> PResult<StringExpr> {
1993 match self.cur().clone() {
1994 Token::Str(s) => {
1995 self.bump();
1996 Ok(StringExpr::Lit(s))
1997 }
1998 Token::Arg(n) => {
1999 self.bump();
2000 Ok(StringExpr::Arg(n))
2001 }
2002 Token::Kw(Kw::Sprintf) => {
2003 self.bump();
2004 self.expect(&Token::Lparen)?;
2005 let fmt = self.parse_stringexpr()?;
2006 let mut args = Vec::new();
2007 while self.eat(&Token::Comma) {
2008 args.push(self.parse_expr()?);
2009 }
2010 self.expect(&Token::Rparen)?;
2011 Ok(StringExpr::Sprintf(Box::new(fmt), args))
2012 }
2013 Token::Name(n) if n == "svg_font" && matches!(self.peek(1), Token::Lparen) => {
2014 self.bump();
2015 self.expect(&Token::Lparen)?;
2016 let mut args = Vec::new();
2017 if !self.at(&Token::Rparen) {
2018 args.push(self.parse_expr()?);
2019 while self.eat(&Token::Comma) {
2020 args.push(self.parse_expr()?);
2021 }
2022 }
2023 self.expect(&Token::Rparen)?;
2024 Ok(StringExpr::SvgFont(args))
2025 }
2026 other => self.err(format!("expected a string, found {other:?}")),
2027 }
2028 }
2029
2030 fn parse_label_position(&mut self) -> PResult<Position> {
2033 let save = self.idx;
2034 if let Ok(x) = self.parse_expr()
2035 && self.eat(&Token::Comma)
2036 {
2037 let y = self.parse_expr()?;
2038 return Ok(Position::Pair(x, y));
2039 }
2040 self.idx = save;
2041 self.parse_position()
2042 }
2043
2044 fn parse_position(&mut self) -> PResult<Position> {
2046 let mut left = self.parse_pos_mul()?;
2047 loop {
2048 let sign = if self.at(&Token::Plus) {
2049 Sign::Plus
2050 } else if self.at(&Token::Minus) {
2051 Sign::Minus
2052 } else {
2053 break;
2054 };
2055 self.bump();
2056 let right = self.parse_pos_mul()?;
2057 left = Position::Sum(sign, Box::new(left), Box::new(right));
2058 }
2059 Ok(left)
2060 }
2061
2062 fn parse_pos_mul(&mut self) -> PResult<Position> {
2064 let mut left = self.parse_pos_primary()?;
2065 loop {
2066 if self.eat(&Token::Mult) {
2067 left = Position::Scale(Box::new(left), self.parse_unary()?, false);
2068 } else if self.eat(&Token::Div) {
2069 left = Position::Scale(Box::new(left), self.parse_unary()?, true);
2070 } else {
2071 break;
2072 }
2073 }
2074 Ok(left)
2075 }
2076
2077 fn parse_pos_primary(&mut self) -> PResult<Position> {
2078 if let Some(p) = self.try_fraction()? {
2082 return Ok(p);
2083 }
2084 if self.eat(&Token::Lparen) {
2089 let save = self.idx;
2090 if let Ok(p1) = self.parse_position() {
2095 let p = if self.eat(&Token::Comma) {
2096 let p2 = self.parse_position()?;
2097 Position::Place(Location::ParenPair(Box::new(p1), Box::new(p2)))
2098 } else {
2099 p1 };
2101 self.expect(&Token::Rparen)?;
2102 return Ok(p);
2103 }
2104 self.idx = save;
2105 let e1 = self.parse_add()?;
2106 self.expect(&Token::Comma)?;
2107 let e2 = self.parse_add()?;
2108 self.expect(&Token::Rparen)?;
2109 return Ok(Position::Pair(e1, e2));
2110 }
2111 if self.at_place_start() && !self.place_is_scalar_ahead() {
2114 return Ok(Position::Place(Location::Place(self.parse_place()?)));
2115 }
2116 let e1 = self.parse_add()?;
2118 if self.eat(&Token::Comma) {
2119 let e2 = self.parse_add()?;
2120 return Ok(Position::Pair(e1, e2));
2121 }
2122 self.err("expected `,`, `between`, or `of the way between` in position")
2123 }
2124
2125 fn try_fraction(&mut self) -> PResult<Option<Position>> {
2129 let save = self.idx;
2130 let Ok(frac) = self.parse_add() else {
2131 self.idx = save;
2132 return Ok(None);
2133 };
2134 let mk = |frac, a, b, of_the_way| {
2135 Ok(Some(Position::Between {
2136 frac: Box::new(frac),
2137 a: Box::new(a),
2138 b: Box::new(b),
2139 of_the_way,
2140 }))
2141 };
2142 if self.eat(&Token::Lt) {
2143 let a = self.parse_position()?;
2144 self.expect(&Token::Comma)?;
2145 let b = self.parse_position()?;
2146 self.expect(&Token::Gt)?;
2147 return mk(frac, a, b, false);
2148 }
2149 let of_the_way = if self.at_kw(Kw::Of) {
2150 self.eat_kw(Kw::Of);
2151 if !(self.eat_kw(Kw::The) && self.eat_kw(Kw::Way) && self.eat_kw(Kw::Between)) {
2152 self.idx = save;
2153 return Ok(None);
2154 }
2155 true
2156 } else if self.eat_kw(Kw::Between) {
2157 false
2158 } else {
2159 self.idx = save;
2160 return Ok(None);
2161 };
2162 let a = self.parse_position()?;
2163 self.expect_kw(Kw::And)?;
2164 let b = self.parse_position()?;
2165 mk(frac, a, b, of_the_way)
2166 }
2167
2168 fn place_is_scalar_ahead(&mut self) -> bool {
2171 let save = self.idx;
2172 let parsed = self.parse_place().is_ok();
2173 let scalar = parsed && matches!(self.cur(), Token::DotX | Token::DotY | Token::Param(_));
2174 self.idx = save;
2175 scalar
2176 }
2177
2178 fn at_place_start(&self) -> bool {
2179 match self.cur() {
2180 Token::Label(_) | Token::Block | Token::Corner(_) => true,
2181 Token::Kw(Kw::Last) | Token::Kw(Kw::Here) => true,
2182 Token::Float(_) => matches!(self.peek(1), Token::Kw(Kw::Nth)),
2183 Token::LeftBrace | Token::LeftQuote => true,
2186 _ => false,
2187 }
2188 }
2189
2190 fn parse_place(&mut self) -> PResult<Place> {
2191 if let Token::Corner(c) = self.cur() {
2193 let c = *c;
2194 self.bump();
2195 self.eat_kw(Kw::Of);
2196 let inner = self.parse_place()?;
2197 return Ok(Place::CornerOf(c, Box::new(inner)));
2198 }
2199
2200 let mut place = self.parse_place_base()?;
2201
2202 loop {
2204 if let Token::Corner(c) = self.cur() {
2205 let c = *c;
2206 self.bump();
2207 place = Place::Corner(Box::new(place), c);
2208 } else if self.at(&Token::Dot) {
2209 self.bump();
2210 let rhs = self.parse_place_base()?;
2211 place = Place::Member(Box::new(place), Box::new(rhs));
2212 } else {
2213 break;
2214 }
2215 }
2216 Ok(place)
2217 }
2218
2219 fn parse_place_base(&mut self) -> PResult<Place> {
2220 match self.cur().clone() {
2221 Token::Kw(Kw::Here) => {
2222 self.bump();
2223 Ok(Place::Here)
2224 }
2225 Token::Label(name) => {
2226 self.bump();
2227 let subscript = if self.eat(&Token::LeftBrack) {
2228 let e = self.parse_subscript()?;
2229 self.expect(&Token::RightBrack)?;
2230 Some(Box::new(e))
2231 } else {
2232 None
2233 };
2234 Ok(Place::Name { name, subscript })
2235 }
2236 Token::Kw(Kw::Last) | Token::Float(_) | Token::LeftBrace | Token::LeftQuote => {
2237 let count = self.parse_nth()?;
2238 let obj = if self.at_primobj() {
2242 self.parse_primobj()?
2243 } else {
2244 PrimObj::Any
2245 };
2246 Ok(Place::Nth { count, obj })
2247 }
2248 other => self.err(format!("expected a place, found {other:?}")),
2249 }
2250 }
2251
2252 fn parse_nth(&mut self) -> PResult<Nth> {
2253 if self.eat_kw(Kw::Last) {
2254 return Ok(Nth::Last);
2255 }
2256 let e = self.parse_ncount()?;
2258 self.expect_kw(Kw::Nth)?;
2259 let from_last = self.eat_kw(Kw::Last);
2260 Ok(Nth::Count(Box::new(e), from_last))
2261 }
2262
2263 fn parse_ncount(&mut self) -> PResult<Expr> {
2267 match self.cur().clone() {
2268 Token::Float(v) => {
2269 self.bump();
2270 Ok(Expr::Num(v))
2271 }
2272 Token::LeftBrace => {
2273 self.bump();
2274 let e = self.parse_expr()?;
2275 self.expect(&Token::RightBrace)?;
2276 Ok(e)
2277 }
2278 Token::LeftQuote => {
2279 self.bump();
2280 let e = self.parse_expr()?;
2281 self.expect(&Token::RightQuote)?;
2282 Ok(e)
2283 }
2284 other => self.err(format!("expected an ordinal count, found {other:?}")),
2285 }
2286 }
2287
2288 fn at_primobj(&self) -> bool {
2291 matches!(
2292 self.cur(),
2293 Token::Prim(_) | Token::Block | Token::Str(_) | Token::LeftBrack
2294 ) || matches!(self.cur(), Token::Name(n) if n == "brace")
2295 }
2296
2297 fn parse_primobj(&mut self) -> PResult<PrimObj> {
2298 match self.cur().clone() {
2299 Token::Prim(p) => {
2300 self.bump();
2301 Ok(PrimObj::Prim(p))
2302 }
2303 Token::Name(n) if n == "brace" => {
2304 self.bump();
2305 Ok(PrimObj::Brace)
2306 }
2307 Token::Block => {
2308 self.bump();
2309 Ok(PrimObj::Block)
2310 }
2311 Token::Str(s) => {
2312 self.bump();
2313 Ok(PrimObj::Str(s))
2314 }
2315 Token::LeftBrack => {
2316 self.bump();
2317 self.expect(&Token::RightBrack)?;
2318 Ok(PrimObj::EmptyBrack)
2319 }
2320 other => self.err(format!("expected a primitive object, found {other:?}")),
2321 }
2322 }
2323
2324 fn opt_expr(&mut self) -> PResult<Option<Expr>> {
2327 if self.starts_scalar() || self.place_is_scalar_ahead() {
2328 Ok(Some(self.parse_expr()?))
2329 } else {
2330 Ok(None)
2331 }
2332 }
2333
2334 fn opt_attr_expr(
2335 &mut self,
2336 allow_fit: bool,
2337 allow_brace: bool,
2338 allow_hatch: bool,
2339 allow_close: bool,
2340 ) -> PResult<Option<Expr>> {
2341 if self.contextual_attr_ahead(allow_fit, allow_brace, allow_hatch, allow_close) {
2342 Ok(None)
2343 } else {
2344 self.opt_expr()
2345 }
2346 }
2347
2348 fn contextual_attr_ahead(
2349 &self,
2350 allow_fit: bool,
2351 allow_brace: bool,
2352 allow_hatch: bool,
2353 allow_close: bool,
2354 ) -> bool {
2355 matches!(
2356 self.cur(),
2357 Token::Name(n)
2358 if (allow_fit && n == "fit")
2359 || (allow_hatch
2360 && matches!(
2361 n.as_str(),
2362 "hatch"
2363 | "crosshatch"
2364 | "hatchangle"
2365 | "hatchsep"
2366 | "hatchwid"
2367 | "hatchwidth"
2368 | "hatchcolor"
2369 ))
2370 || n == "opacity"
2371 || (allow_brace && matches!(n.as_str(), "bracepos" | "labeloffset"))
2372 || n == "behind"
2373 || (allow_close && n == "close")
2374 )
2375 }
2376
2377 fn starts_scalar(&self) -> bool {
2378 matches!(
2379 self.cur(),
2380 Token::Float(_)
2381 | Token::Name(_)
2382 | Token::EnvVar(_)
2383 | Token::Lparen
2384 | Token::Minus
2385 | Token::Plus
2386 | Token::Not
2387 | Token::Func1(_)
2388 | Token::Func2(_)
2389 | Token::Kw(Kw::Rand)
2390 | Token::ArgCount
2391 )
2392 }
2393
2394 fn parse_expr(&mut self) -> PResult<Expr> {
2395 self.parse_or()
2396 }
2397
2398 fn parse_or(&mut self) -> PResult<Expr> {
2399 let mut e = self.parse_and()?;
2400 while self.eat(&Token::OrOr) {
2401 let r = self.parse_and()?;
2402 e = Expr::Bin(BinOp::Or, Box::new(e), Box::new(r));
2403 }
2404 Ok(e)
2405 }
2406
2407 fn parse_and(&mut self) -> PResult<Expr> {
2408 let mut e = self.parse_cmp()?;
2409 while self.eat(&Token::AndAnd) {
2410 let r = self.parse_cmp()?;
2411 e = Expr::Bin(BinOp::And, Box::new(e), Box::new(r));
2412 }
2413 Ok(e)
2414 }
2415
2416 fn parse_cmp(&mut self) -> PResult<Expr> {
2417 let mut e = self.parse_add()?;
2418 loop {
2419 let op = match self.cur() {
2420 Token::EqEq => BinOp::Eq,
2421 Token::Neq => BinOp::Ne,
2422 Token::Lt => BinOp::Lt,
2423 Token::Le => BinOp::Le,
2424 Token::Gt => BinOp::Gt,
2425 Token::Ge => BinOp::Ge,
2426 _ => break,
2427 };
2428 self.bump();
2429 let r = self.parse_add()?;
2430 e = Expr::Bin(op, Box::new(e), Box::new(r));
2431 }
2432 Ok(e)
2433 }
2434
2435 fn parse_add(&mut self) -> PResult<Expr> {
2436 let mut e = self.parse_mul()?;
2437 loop {
2438 let op = match self.cur() {
2439 Token::Plus => BinOp::Add,
2440 Token::Minus => BinOp::Sub,
2441 _ => break,
2442 };
2443 self.bump();
2444 let r = self.parse_mul()?;
2445 e = Expr::Bin(op, Box::new(e), Box::new(r));
2446 }
2447 Ok(e)
2448 }
2449
2450 fn parse_mul(&mut self) -> PResult<Expr> {
2451 let mut e = self.parse_unary()?;
2452 loop {
2453 let op = match self.cur() {
2454 Token::Mult => BinOp::Mul,
2455 Token::Div => BinOp::Div,
2456 Token::Percent => BinOp::Mod,
2457 _ => break,
2458 };
2459 self.bump();
2460 let r = self.parse_unary()?;
2461 e = Expr::Bin(op, Box::new(e), Box::new(r));
2462 }
2463 Ok(e)
2464 }
2465
2466 fn parse_unary(&mut self) -> PResult<Expr> {
2467 let op = match self.cur() {
2468 Token::Minus => Some(UnOp::Neg),
2469 Token::Plus => Some(UnOp::Pos),
2470 Token::Not => Some(UnOp::Not),
2471 _ => None,
2472 };
2473 if let Some(op) = op {
2474 self.bump();
2475 let e = self.parse_unary()?;
2476 Ok(Expr::Unary(op, Box::new(e)))
2477 } else {
2478 self.parse_pow()
2479 }
2480 }
2481
2482 fn parse_pow(&mut self) -> PResult<Expr> {
2483 let base = self.parse_primary()?;
2484 if self.eat(&Token::Caret) {
2485 let exp = self.parse_unary()?; Ok(Expr::Bin(BinOp::Pow, Box::new(base), Box::new(exp)))
2487 } else {
2488 Ok(base)
2489 }
2490 }
2491
2492 fn paren_followed_by_dot_xy(&self) -> bool {
2495 let mut depth = 1i32;
2496 let mut i = self.idx;
2497 while let Some(s) = self.toks.get(i) {
2498 match &s.tok {
2499 Token::Lparen => depth += 1,
2500 Token::Rparen => {
2501 depth -= 1;
2502 if depth == 0 {
2503 return matches!(
2504 self.toks.get(i + 1).map(|t| &t.tok),
2505 Some(Token::DotX | Token::DotY)
2506 );
2507 }
2508 }
2509 Token::Eof => return false,
2510 _ => {}
2511 }
2512 i += 1;
2513 }
2514 false
2515 }
2516
2517 fn parse_primary(&mut self) -> PResult<Expr> {
2518 if self.at_place_start() && self.place_is_scalar_ahead() {
2520 return self.parse_place_scalar();
2521 }
2522 if self.at_string_start() {
2524 return Ok(Expr::Str(self.parse_stringexpr()?));
2525 }
2526 match self.cur().clone() {
2527 Token::Float(v) => {
2528 self.bump();
2529 Ok(Expr::Num(v))
2530 }
2531 Token::Name(name) | Token::Label(name) => {
2532 self.bump();
2533 let subscript = if self.eat(&Token::LeftBrack) {
2534 let e = self.parse_subscript()?;
2535 self.expect(&Token::RightBrack)?;
2536 Some(Box::new(e))
2537 } else {
2538 None
2539 };
2540 Ok(Expr::Var(name, subscript))
2541 }
2542 Token::EnvVar(v) => {
2543 self.bump();
2544 Ok(Expr::Env(v))
2545 }
2546 Token::Lparen => {
2547 self.bump();
2548 if matches!(self.cur(), Token::Name(_) | Token::Label(_))
2550 && self.assignment_op_after_var_ref()
2551 {
2552 let name = match self.bump() {
2553 Token::Name(n) | Token::Label(n) => n,
2554 _ => unreachable!(),
2555 };
2556 let subscript = if self.eat(&Token::LeftBrack) {
2557 let e = self.parse_subscript()?;
2558 self.expect(&Token::RightBrack)?;
2559 Some(Box::new(e))
2560 } else {
2561 None
2562 };
2563 self.bump(); let v = self.parse_expr()?;
2565 self.expect(&Token::Rparen)?;
2566 return Ok(Expr::Assign(name, subscript, Box::new(v)));
2567 }
2568 if self.paren_followed_by_dot_xy() {
2573 let pos = self.parse_position()?;
2574 self.expect(&Token::Rparen)?;
2575 let loc = Location::Paren(Box::new(pos));
2576 return Ok(if self.eat(&Token::DotX) {
2577 Expr::DotX(loc)
2578 } else {
2579 self.expect(&Token::DotY)?;
2580 Expr::DotY(loc)
2581 });
2582 }
2583 let e = self.parse_expr()?;
2585 self.expect(&Token::Rparen)?;
2586 Ok(e)
2587 }
2588 Token::ArgCount => {
2590 self.bump();
2591 Ok(Expr::Num(0.0))
2592 }
2593 Token::Func1(f) => {
2594 self.bump();
2595 self.expect(&Token::Lparen)?;
2596 let e = self.parse_expr()?;
2597 self.expect(&Token::Rparen)?;
2598 Ok(Expr::Func1(f, Box::new(e)))
2599 }
2600 Token::Func2(f) => {
2601 self.bump();
2602 self.expect(&Token::Lparen)?;
2603 let a = self.parse_expr()?;
2604 self.expect(&Token::Comma)?;
2605 let b = self.parse_expr()?;
2606 self.expect(&Token::Rparen)?;
2607 Ok(Expr::Func2(f, Box::new(a), Box::new(b)))
2608 }
2609 Token::Kw(Kw::Rand) => {
2610 self.bump();
2611 self.expect(&Token::Lparen)?;
2612 let arg = if self.at(&Token::Rparen) {
2613 None
2614 } else {
2615 Some(Box::new(self.parse_expr()?))
2616 };
2617 self.expect(&Token::Rparen)?;
2618 Ok(Expr::Rand(arg))
2619 }
2620 other => self.err(format!("expected an expression, found {other:?}")),
2621 }
2622 }
2623
2624 fn parse_place_scalar(&mut self) -> PResult<Expr> {
2626 let place = self.parse_place()?;
2627 match self.cur().clone() {
2628 Token::DotX => {
2629 self.bump();
2630 Ok(Expr::DotX(Location::Place(place)))
2631 }
2632 Token::DotY => {
2633 self.bump();
2634 Ok(Expr::DotY(Location::Place(place)))
2635 }
2636 Token::Param(p) => {
2637 self.bump();
2638 Ok(Expr::PlaceAttr(place, p))
2639 }
2640 other => self.err(format!(
2641 "a place is not a number here; expected `.x`, `.y`, or an attribute, found {other:?}"
2642 )),
2643 }
2644 }
2645}
2646
2647#[cfg(test)]
2648mod tests {
2649 use super::*;
2650
2651 fn pic(src: &str) -> Picture {
2652 parse(src).unwrap_or_else(|e| panic!("parse error: {e}"))
2653 }
2654
2655 #[test]
2656 fn kernighan_pipeline() {
2657 let p = pic(r#".PS
2658ellipse "document"
2659arrow
2660box "PIC"
2661arrow
2662box "TBL/EQN" "(optional)" dashed
2663arrow
2664box "TROFF"
2665arrow
2666ellipse "typesetter"
2667.PE
2668"#);
2669 assert_eq!(p.stmts.len(), 9);
2670 if let Stmt::Object { object, .. } = &p.stmts[4] {
2672 assert_eq!(object.kind, ObjectKind::Primitive(Prim::Box));
2673 let texts = object
2674 .attrs
2675 .iter()
2676 .filter(|a| matches!(a, Attr::Text(_)))
2677 .count();
2678 assert_eq!(texts, 2);
2679 assert!(
2680 object
2681 .attrs
2682 .iter()
2683 .any(|a| matches!(a, Attr::LineStyle(LineType::Dashed, _)))
2684 );
2685 } else {
2686 panic!("expected object");
2687 }
2688 }
2689
2690 #[test]
2691 fn box_with_dims_and_at() {
2692 let p = pic("box ht 0.3 wid 0.5 at 0.25,0.15");
2693 let Stmt::Object { object, .. } = &p.stmts[0] else {
2694 panic!()
2695 };
2696 assert!(matches!(object.attrs[0], Attr::Dim(DimKind::Ht, _)));
2697 assert!(matches!(object.attrs[1], Attr::Dim(DimKind::Wid, _)));
2698 assert!(matches!(object.attrs[2], Attr::At(Position::Pair(_, _))));
2699 }
2700
2701 #[test]
2702 fn behind_parses_as_contextual_extension_attribute() {
2703 let p = pic("A: box\nbox behind A");
2704 let Stmt::Object { object, .. } = &p.stmts[1] else {
2705 panic!()
2706 };
2707 let Some(Attr::Behind(Place::Name {
2708 name, subscript, ..
2709 })) = object.attrs.last()
2710 else {
2711 panic!("expected behind attribute");
2712 };
2713 assert_eq!(name, "A");
2714 assert!(subscript.is_none());
2715
2716 let p = pic("behind = 2\nbox wid behind");
2717 assert_eq!(p.stmts.len(), 2);
2718 assert!(matches!(p.stmts[0], Stmt::Assign(_)));
2719 }
2720
2721 #[test]
2722 fn fit_parses_as_contextual_extension_attribute() {
2723 let p = pic("box \"long label\" fit");
2724 let Stmt::Object { object, .. } = &p.stmts[0] else {
2725 panic!()
2726 };
2727 assert!(object.attrs.iter().any(|a| matches!(a, Attr::Fit)));
2728
2729 let p = pic("fit = 2\nbox wid fit");
2730 assert_eq!(p.stmts.len(), 2);
2731 assert!(matches!(p.stmts[0], Stmt::Assign(_)));
2732
2733 let p = pic("fit = 2\nline fit");
2734 let Stmt::Object { object, .. } = &p.stmts[1] else {
2735 panic!()
2736 };
2737 assert!(matches!(object.attrs[0], Attr::Dist(_)));
2738 }
2739
2740 #[test]
2741 fn hatch_parses_as_contextual_extension_attribute() {
2742 let p = pic("box hatch hatchangle 30 hatchsep .05 hatchwid 1.2 hatchcolor red");
2743 let Stmt::Object { object, .. } = &p.stmts[0] else {
2744 panic!()
2745 };
2746 assert!(
2747 object
2748 .attrs
2749 .iter()
2750 .any(|a| matches!(a, Attr::Hatch(HatchKind::Single)))
2751 );
2752 assert!(
2753 object
2754 .attrs
2755 .iter()
2756 .any(|a| matches!(a, Attr::HatchAngle(_)))
2757 );
2758 assert!(object.attrs.iter().any(|a| matches!(a, Attr::HatchSep(_))));
2759 assert!(
2760 object
2761 .attrs
2762 .iter()
2763 .any(|a| matches!(a, Attr::HatchWidth(_)))
2764 );
2765 assert!(
2766 object
2767 .attrs
2768 .iter()
2769 .any(|a| matches!(a, Attr::HatchColor(_)))
2770 );
2771
2772 let p = pic("hatch = 2\nbox wid hatch");
2773 assert_eq!(p.stmts.len(), 2);
2774 assert!(matches!(p.stmts[0], Stmt::Assign(_)));
2775 let Stmt::Object { object, .. } = &p.stmts[1] else {
2776 panic!()
2777 };
2778 assert!(matches!(object.attrs[0], Attr::Dim(DimKind::Wid, _)));
2779 }
2780
2781 #[test]
2782 fn opacity_parses_as_contextual_extension_attribute() {
2783 let p = pic("box opacity 0.5");
2784 let Stmt::Object { object, .. } = &p.stmts[0] else {
2785 panic!()
2786 };
2787 assert!(object.attrs.iter().any(|a| matches!(a, Attr::Opacity(_))));
2788
2789 let p = pic("opacity = 2\nbox wid opacity");
2790 assert_eq!(p.stmts.len(), 2);
2791 assert!(matches!(p.stmts[0], Stmt::Assign(_)));
2792 let Stmt::Object { object, .. } = &p.stmts[1] else {
2793 panic!()
2794 };
2795 assert!(matches!(object.attrs[0], Attr::Dim(DimKind::Wid, _)));
2796 }
2797
2798 #[test]
2799 fn close_parses_as_contextual_line_extension_attribute() {
2800 let p = pic("line right then up close");
2801 let Stmt::Object { object, .. } = &p.stmts[0] else {
2802 panic!()
2803 };
2804 assert!(object.attrs.iter().any(|a| matches!(a, Attr::Close)));
2805
2806 let p = pic("close = 2\nbox wid close");
2807 assert_eq!(p.stmts.len(), 2);
2808 assert!(matches!(p.stmts[0], Stmt::Assign(_)));
2809 let Stmt::Object { object, .. } = &p.stmts[1] else {
2810 panic!()
2811 };
2812 assert!(matches!(object.attrs[0], Attr::Dim(DimKind::Wid, _)));
2813 }
2814
2815 #[test]
2816 fn brace_parses_as_contextual_extension_object() {
2817 let p = pic(
2818 "A: box\nB: box\nbrace from A.e to B.w down \"group\" wid .2 bracepos .4 labeloffset .1",
2819 );
2820 let Stmt::Object { object, .. } = &p.stmts[2] else {
2821 panic!()
2822 };
2823 assert_eq!(object.kind, ObjectKind::Brace);
2824 assert!(object.attrs.iter().any(|a| matches!(a, Attr::From(_))));
2825 assert!(object.attrs.iter().any(|a| matches!(a, Attr::To(_))));
2826 assert!(
2827 object
2828 .attrs
2829 .iter()
2830 .any(|a| matches!(a, Attr::Direction(Dir::Down, None)))
2831 );
2832 assert!(object.attrs.iter().any(|a| matches!(a, Attr::Text(_))));
2833 assert!(object.attrs.iter().any(|a| matches!(a, Attr::BracePos(_))));
2834 assert!(
2835 object
2836 .attrs
2837 .iter()
2838 .any(|a| matches!(a, Attr::BraceLabelOffset(_)))
2839 );
2840
2841 let p = pic("brace = 2\nline right brace");
2842 assert!(matches!(p.stmts[0], Stmt::Assign(_)));
2843 let Stmt::Object { object, .. } = &p.stmts[1] else {
2844 panic!()
2845 };
2846 assert_eq!(object.kind, ObjectKind::Primitive(Prim::Line));
2847
2848 let p = pic("brace from 0,0 to 1,0\nline from last brace.start to last brace.end");
2849 assert_eq!(p.stmts.len(), 2);
2850 }
2851
2852 #[test]
2853 fn labeled_and_corners() {
2854 let p = pic("B1: box\narc -> from top of B1 to last box.ne");
2855 assert!(matches!(p.stmts[0], Stmt::Object { label: Some(_), .. }));
2856 let Stmt::Object { object, .. } = &p.stmts[1] else {
2857 panic!()
2858 };
2859 assert_eq!(object.kind, ObjectKind::Primitive(Prim::Arc));
2860 assert!(
2861 object
2862 .attrs
2863 .iter()
2864 .any(|a| matches!(a, Attr::Arrowhead(Arrow::Right, _)))
2865 );
2866 assert!(object.attrs.iter().any(|a| matches!(
2868 a,
2869 Attr::From(Position::Place(Location::Place(Place::CornerOf(
2870 Corner::N,
2871 _
2872 ))))
2873 )));
2874 }
2875
2876 #[test]
2877 fn with_at_and_shift() {
2878 let p = pic("ellipse \"2\" with .nw at last ellipse.se + (0.1,0)");
2879 let Stmt::Object { object, .. } = &p.stmts[0] else {
2880 panic!()
2881 };
2882 let with = object
2883 .attrs
2884 .iter()
2885 .find(|a| matches!(a, Attr::With { .. }))
2886 .unwrap();
2887 let Attr::With { anchor, at } = with else {
2888 panic!()
2889 };
2890 assert_eq!(*anchor, WithAnchor::Corner(Corner::Nw));
2891 assert!(matches!(at, Position::Sum(Sign::Plus, _, _)));
2893 }
2894
2895 #[test]
2896 fn with_member_anchor_parses() {
2897 let p = pic("[ A: box ] with .A.c at Here");
2898 let Stmt::Object { object, .. } = &p.stmts[0] else {
2899 panic!()
2900 };
2901 let with = object
2902 .attrs
2903 .iter()
2904 .find(|a| matches!(a, Attr::With { .. }))
2905 .unwrap();
2906 let Attr::With { anchor, .. } = with else {
2907 panic!()
2908 };
2909 assert!(matches!(
2910 anchor,
2911 WithAnchor::Place(Place::Corner(inner, Corner::Center))
2912 if matches!(inner.as_ref(), Place::Name { name, .. } if name == "A")
2913 ));
2914 }
2915
2916 #[test]
2917 fn expression_precedence() {
2918 let p = pic("x = 2 + 3 * 4 ^ 2");
2920 let Stmt::Assign(list) = &p.stmts[0] else {
2921 panic!()
2922 };
2923 let Expr::Bin(BinOp::Add, _, rhs) = &list[0].value else {
2925 panic!("expected top-level add")
2926 };
2927 assert!(matches!(**rhs, Expr::Bin(BinOp::Mul, _, _)));
2928 }
2929
2930 #[test]
2931 fn between_position() {
2932 let p = pic("arrow from 1/3 of the way between A.ne and A.se");
2933 let Stmt::Object { object, .. } = &p.stmts[0] else {
2934 panic!()
2935 };
2936 assert!(object.attrs.iter().any(|a| matches!(
2937 a,
2938 Attr::From(Position::Between {
2939 of_the_way: true,
2940 ..
2941 })
2942 )));
2943 }
2944
2945 #[test]
2946 fn assignment_list_and_envvar() {
2947 let p = pic("boxht = 0.3; boxwid = 2 * boxht");
2948 assert_eq!(p.stmts.len(), 2);
2949 let Stmt::Assign(a0) = &p.stmts[0] else {
2950 panic!()
2951 };
2952 assert_eq!(a0[0].target, AssignTarget::Env(EnvVar::Boxht));
2953 }
2954
2955 #[test]
2956 fn dpic_svg_font_stub_parses_as_string() {
2957 let p = pic("print svg_font(\"Times\", 12)");
2958 let Stmt::Print(PrintItem::Str(StringExpr::SvgFont(args))) = &p.stmts[0] else {
2959 panic!()
2960 };
2961 assert_eq!(args.len(), 2);
2962 }
2963
2964 #[test]
2965 fn subscripted_variable_refs_parse() {
2966 let p = pic("P[1] = 2\nx = P[1]");
2967 let Stmt::Assign(a0) = &p.stmts[0] else {
2968 panic!()
2969 };
2970 assert!(matches!(&a0[0].target, AssignTarget::Var(name, Some(_)) if name == "P"));
2971
2972 let Stmt::Assign(a1) = &p.stmts[1] else {
2973 panic!()
2974 };
2975 assert!(matches!(&a1[0].value, Expr::Var(name, Some(_)) if name == "P"));
2976 }
2977
2978 #[test]
2979 fn block_object() {
2980 let p = pic("[ box; circle ] with .nw at Here");
2981 let Stmt::Object { object, .. } = &p.stmts[0] else {
2982 panic!()
2983 };
2984 let ObjectKind::Block(inner) = &object.kind else {
2985 panic!()
2986 };
2987 assert_eq!(inner.len(), 2);
2988 }
2989
2990 #[test]
2991 fn diamond_line_with_then() {
2992 let p = pic("line up right then down right then down left then up left");
2993 let Stmt::Object { object, .. } = &p.stmts[0] else {
2994 panic!()
2995 };
2996 let thens = object
2997 .attrs
2998 .iter()
2999 .filter(|a| matches!(a, Attr::Then))
3000 .count();
3001 assert_eq!(thens, 3);
3002 }
3003
3004 #[test]
3005 fn place_scalar_in_coord_pair() {
3006 let p = pic("A: box\n\"t\" at (A.x, A.y - 0.5)");
3008 let Stmt::Object { object, .. } = &p.stmts[1] else {
3009 panic!()
3010 };
3011 assert!(
3012 object
3013 .attrs
3014 .iter()
3015 .any(|a| matches!(a, Attr::At(Position::Pair(_, _))))
3016 );
3017 let q = pic("A: box\nbox at A.ne");
3019 let Stmt::Object { object, .. } = &q.stmts[1] else {
3020 panic!()
3021 };
3022 assert!(object.attrs.iter().any(|a| matches!(
3023 a,
3024 Attr::At(Position::Place(Location::Place(Place::Corner(_, _))))
3025 )));
3026 }
3027
3028 #[test]
3029 fn ignores_non_svg_backend_preambles() {
3030 let p = pic(r#".PS
3031verbatimtex
3032\global\def\foo#1{#1}
3033etex
3034\global\def\bar#1{#1}
3035\psset{arrowsize=4pt}
3036box
3037.PE
3038"#);
3039 assert_eq!(p.stmts.len(), 1);
3040 let Stmt::Object { object, .. } = &p.stmts[0] else {
3041 panic!()
3042 };
3043 assert_eq!(object.kind, ObjectKind::Primitive(Prim::Box));
3044 }
3045
3046 #[test]
3047 fn backend_filter_keeps_global_lines_inside_strings() {
3048 let p = pic(
3049 "sh \"echo -n \\\"print \\\\\"\\\" > x\"\nif dpicopt==optPGF then { command \"cycle; \\\n\\global\\let\\dpicdraw=x\" } else { box }",
3050 );
3051 assert_eq!(p.stmts.len(), 2);
3052 let Stmt::Object { object, .. } = &p.stmts[1] else {
3053 panic!()
3054 };
3055 assert_eq!(object.kind, ObjectKind::Primitive(Prim::Box));
3056 }
3057
3058 #[test]
3059 fn static_if_copy_defines_macros_before_following_statements() {
3060 let dir = std::env::temp_dir().join(format!("rpic_static_if_{}", std::process::id()));
3061 std::fs::create_dir_all(&dir).unwrap();
3062 std::fs::write(dir.join("macros.pic"), "define makebox { box wid $1 }\n").unwrap();
3063 std::fs::write(
3064 dir.join("inc.pic"),
3065 "define makecircle { circle rad 0.1 }\n",
3066 )
3067 .unwrap();
3068 let p = parse_in_dir(
3069 "if \"plotlib\" != \"1\" then { copy \"macros.pic\" }\ndefine choose { if \"$1\"==\"\" then { box } else { copy \"$1/inc.pic\" } }\nchoose(.)\nmakecircle()\nmakebox(0.4)",
3070 Some(dir.as_path()),
3071 )
3072 .unwrap_or_else(|e| panic!("parse error: {e}"));
3073 let _ = std::fs::remove_dir_all(&dir);
3074 assert_eq!(p.stmts.len(), 2);
3075 let Stmt::Object { object, .. } = &p.stmts[0] else {
3076 panic!()
3077 };
3078 assert_eq!(object.kind, ObjectKind::Primitive(Prim::Circle));
3079 let Stmt::Object { object, .. } = &p.stmts[1] else {
3080 panic!()
3081 };
3082 assert_eq!(object.kind, ObjectKind::Primitive(Prim::Box));
3083 }
3084
3085 #[test]
3086 fn unsupported_control_is_clear() {
3087 let e = parse("copy \"x\"").unwrap_err();
3089 assert!(e.msg.contains("copy") && e.msg.contains("file"));
3090 }
3091
3092 #[test]
3093 fn control_constructs_parse() {
3094 assert!(parse("for i = 1 to 3 do { box }").is_ok());
3095 assert!(parse("if 1 > 0 then { box } else { circle }").is_ok());
3096 assert!(parse("reset boxht, boxwid").is_ok());
3097 let p = parse("define e { box }\ne\ne").unwrap();
3099 assert_eq!(p.stmts.len(), 2);
3100 }
3101}