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    /// `continue` — extend the previous line with another segment.
155    Continue,
156}
157
158/// An object attribute (applied left to right).
159#[derive(Debug, Clone, PartialEq)]
160pub enum Attr {
161    Dim(DimKind, Expr),
162    Direction(Dir, Option<Expr>),
163    /// A bare distance with no direction word (e.g. `move 1`): advance by this
164    /// much along the prevailing direction.
165    Dist(Expr),
166    /// `spline <expr> …`: the expression right after `spline` is a spline
167    /// tension parameter (dpic semantics), NOT a distance.
168    SplineTension(Expr),
169    LineStyle(LineType, Option<Expr>),
170    Chop(Option<Expr>),
171    Fill(Option<Expr>),
172    Arrowhead(Arrow, Option<Expr>),
173    Then,
174    Cw,
175    Ccw,
176    Same,
177    Continue,
178    Text(StringExpr),
179    TextPos(TextPos),
180    From(Position),
181    To(Position),
182    At(Position),
183    By(Position),
184    With {
185        anchor: WithAnchor,
186        at: Position,
187    },
188    Color(Color, StringExpr),
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192pub enum DimKind {
193    Ht,
194    Wid,
195    Rad,
196    Diam,
197    Thick,
198    Scaled,
199}
200
201/// The reference point on an object used by `with … at …`.
202#[derive(Debug, Clone, PartialEq)]
203pub enum WithAnchor {
204    Corner(Corner),
205    Pair(Expr, Expr),
206    Place(Place),
207    Plain,
208}
209
210/// A position (point) expression. Positions support full vector arithmetic
211/// (dpic): `p + q`, `p - q`, `p * s`, `p / s` with the usual precedence.
212#[derive(Debug, Clone, PartialEq)]
213pub enum Position {
214    /// Explicit `x , y`.
215    Pair(Expr, Expr),
216    /// A bare location.
217    Place(Location),
218    /// `frac [of the way] between A and B`.
219    Between {
220        frac: Box<Expr>,
221        a: Box<Position>,
222        b: Box<Position>,
223        of_the_way: bool,
224    },
225    /// `p + q` / `p - q`.
226    Sum(Sign, Box<Position>, Box<Position>),
227    /// `p * s` (or `p / s` when `div`), scaling a position by a scalar.
228    Scale(Box<Position>, Expr, bool),
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
232pub enum Sign {
233    Plus,
234    Minus,
235}
236
237/// A point-valued location.
238#[derive(Debug, Clone, PartialEq)]
239pub enum Location {
240    Place(Place),
241    /// `( position )`.
242    Paren(Box<Position>),
243    /// `( position , position )` — x of the first, y of the second.
244    ParenPair(Box<Position>, Box<Position>),
245}
246
247/// A named place in the drawing.
248#[derive(Debug, Clone, PartialEq)]
249pub enum Place {
250    /// A label, optionally subscripted.
251    Name {
252        name: String,
253        subscript: Option<Box<Expr>>,
254    },
255    /// `last box`, `2nd last circle`, etc.
256    Nth {
257        count: Nth,
258        obj: PrimObj,
259    },
260    /// `place . corner` (e.g. `A.ne`).
261    Corner(Box<Place>, Corner),
262    /// `corner of place` / `corner place` (e.g. `top of A`).
263    CornerOf(Corner, Box<Place>),
264    /// `place . place` (block sub-label, e.g. `B.A`).
265    Member(Box<Place>, Box<Place>),
266    Here,
267}
268
269#[derive(Debug, Clone, PartialEq)]
270pub enum Nth {
271    Last,
272    /// `count`-th object; `from_last` true for `… last`.
273    Count(Box<Expr>, bool),
274}
275
276#[derive(Debug, Clone, PartialEq)]
277pub enum PrimObj {
278    Prim(Prim),
279    Block,
280    Str(String),
281    EmptyBrack,
282    /// Untyped `last` / `Nth last` — the most recent object of any kind
283    /// (e.g. `last.c`, `2nd last.n`). Used when no type keyword follows.
284    Any,
285}
286
287/// A string-valued expression.
288#[derive(Debug, Clone, PartialEq)]
289pub enum StringExpr {
290    Lit(String),
291    Concat(Box<StringExpr>, Box<StringExpr>),
292    Sprintf(Box<StringExpr>, Vec<Expr>),
293    /// dpic SVG-backend helper. rpic does not emit backend preamble text, so
294    /// this evaluates to a harmless empty string.
295    SvgFont(Vec<Expr>),
296    Arg(u32),
297}
298
299/// A scalar (numeric / boolean) expression. pic treats booleans as numbers.
300#[derive(Debug, Clone, PartialEq)]
301pub enum Expr {
302    Num(f64),
303    /// A string operand, valid only as an operand of `==`/`!=` (pic compares
304    /// strings for equality, e.g. the `"$1"==""` default-argument idiom).
305    Str(StringExpr),
306    /// Comma-separated array subscript, valid only inside `name[...]`.
307    Index(Vec<Expr>),
308    Var(String, Option<Box<Expr>>),
309    Env(EnvVar),
310    Unary(UnOp, Box<Expr>),
311    Bin(BinOp, Box<Expr>, Box<Expr>),
312    Func1(Func1, Box<Expr>),
313    Func2(Func2, Box<Expr>, Box<Expr>),
314    Rand(Option<Box<Expr>>),
315    /// `( name = expr )` — assign and yield the assigned value.
316    Assign(String, Option<Box<Expr>>, Box<Expr>),
317    DotX(Location),
318    DotY(Location),
319    PlaceAttr(Place, Param),
320}
321
322#[derive(Debug, Clone, Copy, PartialEq, Eq)]
323pub enum UnOp {
324    Neg,
325    Pos,
326    Not,
327}
328
329#[derive(Debug, Clone, Copy, PartialEq, Eq)]
330pub enum BinOp {
331    Add,
332    Sub,
333    Mul,
334    Div,
335    Mod,
336    Pow,
337    Eq,
338    Ne,
339    Lt,
340    Le,
341    Gt,
342    Ge,
343    And,
344    Or,
345}