Skip to main content

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}