rpic_core/ast.rs
1//! Abstract syntax tree for the pic language.
2//!
3//! Mirrors the structure of dpic's `grammar.txt`. The parser populates the
4//! drawing core (pictures, primitives + attributes, positions, expressions,
5//! blocks, assignments) plus the control constructs (`if`/`for`/`define`/
6//! `print`/`exec`) used by the evaluator and macro preprocessor.
7
8use crate::lexer::Spanned;
9use crate::token::{
10 Arrow, Color, Corner, Dir, EnvVar, Func1, Func2, LineType, Param, Prim, TextPos,
11};
12
13/// Macro definitions (name → body tokens), carried from parse to eval so that
14/// `if`/`for` bodies can be expanded lazily along the executed path.
15pub type Macros = std::collections::HashMap<String, Vec<Spanned>>;
16
17/// A deferred block of statements, kept as raw tokens until the branch/iteration
18/// that contains it actually runs (so dead branches and recursive macro calls
19/// are never parsed). Parsed on demand by the evaluator.
20pub type Body = Vec<Spanned>;
21
22/// A complete picture: optional `.PS <w> <h>` dimensions plus a list of elements.
23#[derive(Debug, Clone, PartialEq)]
24pub struct Picture {
25 pub width: Option<Expr>,
26 pub height: Option<Expr>,
27 pub stmts: Vec<Stmt>,
28 pub macros: Macros,
29 /// Directory used to resolve `copy "file"` includes (set by `parse_in_dir`);
30 /// `None` when parsing has no filesystem context (WASM/bindings).
31 pub base_dir: Option<std::path::PathBuf>,
32}
33
34/// A label with an optional `[subscript]` suffix.
35#[derive(Debug, Clone, PartialEq)]
36pub struct Label {
37 pub name: String,
38 pub subscript: Option<Expr>,
39}
40
41/// A top-level element.
42#[derive(Debug, Clone, PartialEq)]
43pub enum Stmt {
44 /// A drawn object, optionally labelled (`Start: box …`).
45 Object {
46 label: Option<Label>,
47 object: Object,
48 },
49 /// `Label: position` — names a point without drawing.
50 Place { label: Label, pos: Position },
51 /// One or more comma-separated assignments.
52 Assign(Vec<Assignment>),
53 /// A bare direction change (`up` / `down` / `left` / `right`).
54 Direction(Dir),
55 /// `{ … }` grouping block (local scope, no bounding object).
56 Group(Vec<Stmt>),
57 /// rpic animation directive (extension; see [`Animate`]).
58 Animate(Animate),
59 /// `if <cond> then { … } [else { … }]`. Bodies are deferred raw tokens.
60 If {
61 cond: Expr,
62 then_body: Body,
63 else_body: Option<Body>,
64 },
65 /// `for v = from to to [by [*] step] do { … }`. Body is deferred raw tokens.
66 For {
67 var: String,
68 subscript: Option<Expr>,
69 from: Expr,
70 to: Expr,
71 by: Expr,
72 /// `by *` multiplies instead of adds.
73 mult: bool,
74 body: Body,
75 },
76 /// `print …` (evaluated for diagnostics; no drawing effect).
77 Print(PrintItem),
78 /// `exec <string>` — evaluate generated pic source in the current state.
79 Exec {
80 command: StringExpr,
81 arg_frame: Option<Vec<Vec<Spanned>>>,
82 },
83 /// `reset` (all) or `reset a, b, …` — restore environment variables.
84 Reset(Vec<EnvVar>),
85}
86
87#[derive(Debug, Clone, PartialEq)]
88pub enum PrintItem {
89 Str(StringExpr),
90 Expr(Expr),
91}
92
93/// `animate <target> with "<effect>" [for <dur>] [at <t> | after <ref>] [delay <d>]`.
94#[derive(Debug, Clone, PartialEq)]
95pub struct Animate {
96 pub target: Place,
97 pub effect: StringExpr,
98 pub duration: Option<Expr>,
99 pub timing: Timing,
100 pub delay: Option<Expr>,
101}
102
103/// When an animation starts.
104#[derive(Debug, Clone, PartialEq)]
105pub enum Timing {
106 /// After the previously declared animation ends (default).
107 Sequential,
108 /// At an absolute time (seconds).
109 At(Expr),
110 /// After the named object's animation ends.
111 After(Place),
112}
113
114/// A single assignment `target op value`.
115#[derive(Debug, Clone, PartialEq)]
116pub struct Assignment {
117 pub target: AssignTarget,
118 pub op: AssignOp,
119 pub value: Expr,
120}
121
122#[derive(Debug, Clone, PartialEq)]
123pub enum AssignTarget {
124 Var(String, Option<Expr>),
125 Env(EnvVar),
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129pub enum AssignOp {
130 Set, // =
131 ColonSet, // :=
132 Add, // +=
133 Sub, // -=
134 Mul, // *=
135 Div, // /=
136 Rem, // %=
137}
138
139/// A drawable object: a base plus a chain of attributes.
140#[derive(Debug, Clone, PartialEq)]
141pub struct Object {
142 pub kind: ObjectKind,
143 pub attrs: Vec<Attr>,
144}
145
146#[derive(Debug, Clone, PartialEq)]
147pub enum ObjectKind {
148 Primitive(Prim),
149 Block(Vec<Stmt>),
150 /// `[]` empty-block reference.
151 Empty,
152 /// A bare quoted string places a text-only object.
153 Text,
154 /// rpic extension: a curly brace annotation between two points.
155 Brace,
156 /// `continue` — extend the previous line with another segment.
157 Continue,
158}
159
160/// An object attribute (applied left to right).
161#[derive(Debug, Clone, PartialEq)]
162pub enum Attr {
163 Dim(DimKind, Expr),
164 Direction(Dir, Option<Expr>),
165 /// A bare distance with no direction word (e.g. `move 1`): advance by this
166 /// much along the prevailing direction.
167 Dist(Expr),
168 /// `spline <expr> …`: the expression right after `spline` is a spline
169 /// tension parameter (dpic semantics), NOT a distance.
170 SplineTension(Expr),
171 LineStyle(LineType, Option<Expr>),
172 Chop(Option<Expr>),
173 Fill(Option<Expr>),
174 Arrowhead(Arrow, Option<Expr>),
175 Then,
176 Cw,
177 Ccw,
178 Same,
179 Continue,
180 Text(StringExpr),
181 /// rpic extension: size a closed object to text declared before `fit`.
182 Fit,
183 /// rpic extension: hatch-filled closed regions.
184 Hatch(HatchKind),
185 /// rpic extension: hatch line angle in degrees.
186 HatchAngle(Expr),
187 /// rpic extension: hatch line spacing in pic units.
188 HatchSep(Expr),
189 /// rpic extension: hatch line width in points.
190 HatchWidth(Expr),
191 /// rpic extension: hatch line color.
192 HatchColor(StringExpr),
193 /// rpic extension: fill opacity, 0 = transparent and 1 = opaque.
194 Opacity(Expr),
195 /// rpic extension: close a `line` path into a polygon.
196 Close,
197 TextPos(TextPos),
198 From(Position),
199 To(Position),
200 At(Position),
201 By(Position),
202 With {
203 anchor: WithAnchor,
204 at: Position,
205 },
206 Color(Color, StringExpr),
207 /// rpic extension: draw this object below another already-placed object.
208 Behind(Place),
209 /// rpic extension: relative cusp position for a `brace` object.
210 BracePos(Expr),
211 /// rpic extension: extra outward spacing between a `brace` cusp and label.
212 BraceLabelOffset(Expr),
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq)]
216pub enum HatchKind {
217 Single,
218 Cross,
219}
220
221#[derive(Debug, Clone, Copy, PartialEq, Eq)]
222pub enum DimKind {
223 Ht,
224 Wid,
225 Rad,
226 Diam,
227 Thick,
228 Scaled,
229}
230
231/// The reference point on an object used by `with … at …`.
232#[derive(Debug, Clone, PartialEq)]
233pub enum WithAnchor {
234 Corner(Corner),
235 Pair(Expr, Expr),
236 Place(Place),
237 Plain,
238}
239
240/// A position (point) expression. Positions support full vector arithmetic
241/// (dpic): `p + q`, `p - q`, `p * s`, `p / s` with the usual precedence.
242#[derive(Debug, Clone, PartialEq)]
243pub enum Position {
244 /// Explicit `x , y`.
245 Pair(Expr, Expr),
246 /// A bare location.
247 Place(Location),
248 /// `frac [of the way] between A and B`.
249 Between {
250 frac: Box<Expr>,
251 a: Box<Position>,
252 b: Box<Position>,
253 of_the_way: bool,
254 },
255 /// `p + q` / `p - q`.
256 Sum(Sign, Box<Position>, Box<Position>),
257 /// `p * s` (or `p / s` when `div`), scaling a position by a scalar.
258 Scale(Box<Position>, Expr, bool),
259}
260
261#[derive(Debug, Clone, Copy, PartialEq, Eq)]
262pub enum Sign {
263 Plus,
264 Minus,
265}
266
267/// A point-valued location.
268#[derive(Debug, Clone, PartialEq)]
269pub enum Location {
270 Place(Place),
271 /// `( position )`.
272 Paren(Box<Position>),
273 /// `( position , position )` — x of the first, y of the second.
274 ParenPair(Box<Position>, Box<Position>),
275}
276
277/// A named place in the drawing.
278#[derive(Debug, Clone, PartialEq)]
279pub enum Place {
280 /// A label, optionally subscripted.
281 Name {
282 name: String,
283 subscript: Option<Box<Expr>>,
284 },
285 /// `last box`, `2nd last circle`, etc.
286 Nth {
287 count: Nth,
288 obj: PrimObj,
289 },
290 /// `place . corner` (e.g. `A.ne`).
291 Corner(Box<Place>, Corner),
292 /// `corner of place` / `corner place` (e.g. `top of A`).
293 CornerOf(Corner, Box<Place>),
294 /// `place . place` (block sub-label, e.g. `B.A`).
295 Member(Box<Place>, Box<Place>),
296 Here,
297}
298
299#[derive(Debug, Clone, PartialEq)]
300pub enum Nth {
301 Last,
302 /// `count`-th object; `from_last` true for `… last`.
303 Count(Box<Expr>, bool),
304}
305
306#[derive(Debug, Clone, PartialEq)]
307pub enum PrimObj {
308 Prim(Prim),
309 Brace,
310 Block,
311 Str(String),
312 EmptyBrack,
313 /// Untyped `last` / `Nth last` — the most recent object of any kind
314 /// (e.g. `last.c`, `2nd last.n`). Used when no type keyword follows.
315 Any,
316}
317
318/// A string-valued expression.
319#[derive(Debug, Clone, PartialEq)]
320pub enum StringExpr {
321 Lit(String),
322 Concat(Box<StringExpr>, Box<StringExpr>),
323 Sprintf(Box<StringExpr>, Vec<Expr>),
324 /// dpic SVG-backend helper. rpic does not emit backend preamble text, so
325 /// this evaluates to a harmless empty string.
326 SvgFont(Vec<Expr>),
327 Arg(u32),
328}
329
330/// A scalar (numeric / boolean) expression. pic treats booleans as numbers.
331#[derive(Debug, Clone, PartialEq)]
332pub enum Expr {
333 Num(f64),
334 /// A string operand, valid only as an operand of `==`/`!=` (pic compares
335 /// strings for equality, e.g. the `"$1"==""` default-argument idiom).
336 Str(StringExpr),
337 /// Comma-separated array subscript, valid only inside `name[...]`.
338 Index(Vec<Expr>),
339 Var(String, Option<Box<Expr>>),
340 Env(EnvVar),
341 Unary(UnOp, Box<Expr>),
342 Bin(BinOp, Box<Expr>, Box<Expr>),
343 Func1(Func1, Box<Expr>),
344 Func2(Func2, Box<Expr>, Box<Expr>),
345 Rand(Option<Box<Expr>>),
346 /// `( name = expr )` — assign and yield the assigned value.
347 Assign(String, Option<Box<Expr>>, Box<Expr>),
348 DotX(Location),
349 DotY(Location),
350 PlaceAttr(Place, Param),
351}
352
353#[derive(Debug, Clone, Copy, PartialEq, Eq)]
354pub enum UnOp {
355 Neg,
356 Pos,
357 Not,
358}
359
360#[derive(Debug, Clone, Copy, PartialEq, Eq)]
361pub enum BinOp {
362 Add,
363 Sub,
364 Mul,
365 Div,
366 Mod,
367 Pow,
368 Eq,
369 Ne,
370 Lt,
371 Le,
372 Gt,
373 Ge,
374 And,
375 Or,
376}