Skip to main content

openscad_rs/
ast.rs

1/// AST node types for the `OpenSCAD` language.
2///
3/// Every node carries a [`Span`] for source-location mapping.
4use crate::span::Span;
5
6/// A complete `OpenSCAD` source file.
7#[derive(Debug, Clone, PartialEq)]
8pub struct SourceFile {
9    pub statements: Vec<Statement>,
10    pub span: Span,
11}
12
13/// Top-level and block-level statements.
14#[derive(Debug, Clone, PartialEq)]
15pub enum Statement {
16    /// `include <path>`
17    Include { path: String, span: Span },
18    /// `use <path>`
19    Use { path: String, span: Span },
20    /// Variable assignment: `name = expr;`
21    Assignment {
22        name: String,
23        expr: Expr,
24        span: Span,
25    },
26    /// Module definition: `module name(params) { body }`
27    ModuleDefinition {
28        name: String,
29        params: Vec<Parameter>,
30        body: Vec<Self>,
31        span: Span,
32    },
33    /// Function definition: `function name(params) = expr;`
34    FunctionDefinition {
35        name: String,
36        params: Vec<Parameter>,
37        body: Expr,
38        span: Span,
39    },
40    /// Module instantiation: `name(args) { children }` or `name(args);`
41    ModuleInstantiation {
42        name: String,
43        args: Vec<Argument>,
44        children: Vec<Self>,
45        modifiers: Modifiers,
46        span: Span,
47    },
48    /// `if (cond) { ... } else { ... }`
49    IfElse {
50        condition: Expr,
51        then_body: Vec<Self>,
52        else_body: Option<Vec<Self>>,
53        span: Span,
54    },
55    /// A bare block `{ ... }` (rare but valid)
56    Block { body: Vec<Self>, span: Span },
57    /// Empty statement `;`
58    Empty { span: Span },
59}
60
61/// Modifier prefixes for module instantiation: `!`, `#`, `%`, `*`
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
63#[allow(clippy::struct_excessive_bools)]
64pub struct Modifiers {
65    /// `!` — root modifier
66    pub root: bool,
67    /// `#` — highlight/debug modifier
68    pub highlight: bool,
69    /// `%` — background/transparent modifier
70    pub background: bool,
71    /// `*` — disable modifier
72    pub disable: bool,
73}
74
75/// An expression.
76#[derive(Debug, Clone, PartialEq)]
77pub struct Expr {
78    pub kind: ExprKind,
79    pub span: Span,
80}
81
82impl Expr {
83    #[must_use]
84    pub const fn new(kind: ExprKind, span: Span) -> Self {
85        Self { kind, span }
86    }
87}
88
89#[derive(Debug, Clone, PartialEq)]
90pub enum ExprKind {
91    /// Numeric literal
92    Number(f64),
93    /// String literal (already unescaped)
94    String(String),
95    /// `true`
96    BoolTrue,
97    /// `false`
98    BoolFalse,
99    /// `undef`
100    Undef,
101    /// Variable reference
102    Identifier(String),
103
104    /// Unary operation: `-x`, `!x`, `+x`, `~x`
105    UnaryOp { op: UnaryOp, operand: Box<Expr> },
106    /// Binary operation
107    BinaryOp {
108        op: BinaryOp,
109        left: Box<Expr>,
110        right: Box<Expr>,
111    },
112    /// Ternary: `cond ? then : else`
113    Ternary {
114        condition: Box<Expr>,
115        then_expr: Box<Expr>,
116        else_expr: Box<Expr>,
117    },
118
119    /// Function call: `name(args)` or `expr(args)`
120    FunctionCall {
121        callee: Box<Expr>,
122        args: Vec<Argument>,
123    },
124    /// Index access: `expr[index]`
125    Index { object: Box<Expr>, index: Box<Expr> },
126    /// Member access: `expr.member`
127    MemberAccess { object: Box<Expr>, member: String },
128
129    /// Vector/list literal: `[a, b, c]`
130    Vector(Vec<Expr>),
131    /// Range: `[start : end]` or `[start : step : end]`
132    Range {
133        start: Box<Expr>,
134        step: Option<Box<Expr>>,
135        end: Box<Expr>,
136    },
137
138    /// `let (assignments) expr`
139    Let {
140        assignments: Vec<Argument>,
141        body: Box<Expr>,
142    },
143    /// `assert(args) expr`
144    Assert {
145        args: Vec<Argument>,
146        body: Option<Box<Expr>>,
147    },
148    /// `echo(args) expr`
149    Echo {
150        args: Vec<Argument>,
151        body: Option<Box<Expr>>,
152    },
153
154    /// Anonymous function: `function(params) expr`
155    AnonymousFunction {
156        params: Vec<Parameter>,
157        body: Box<Expr>,
158    },
159
160    // ── List comprehension elements ──────────────────────────
161    /// `for (assignments) expr`
162    LcFor {
163        assignments: Vec<Argument>,
164        body: Box<Expr>,
165    },
166    /// C-style for: `for (init ; cond ; update) expr`
167    LcForC {
168        init: Vec<Argument>,
169        condition: Box<Expr>,
170        update: Vec<Argument>,
171        body: Box<Expr>,
172    },
173    /// `if (cond) expr` / `if (cond) expr else expr` in list comprehension
174    LcIf {
175        condition: Box<Expr>,
176        then_expr: Box<Expr>,
177        else_expr: Option<Box<Expr>>,
178    },
179    /// `let (assignments) expr` in list comprehension
180    LcLet {
181        assignments: Vec<Argument>,
182        body: Box<Expr>,
183    },
184    /// `each expr`
185    LcEach { body: Box<Expr> },
186}
187
188/// Unary operators.
189#[derive(Debug, Clone, Copy, PartialEq, Eq)]
190pub enum UnaryOp {
191    Negate,
192    Not,
193    Plus,
194    BinaryNot,
195}
196
197/// Binary operators (ordered by precedence, lowest first).
198#[derive(Debug, Clone, Copy, PartialEq, Eq)]
199pub enum BinaryOp {
200    // Logical
201    LogicalOr,
202    LogicalAnd,
203    // Equality
204    Equal,
205    NotEqual,
206    // Comparison
207    Less,
208    LessEqual,
209    Greater,
210    GreaterEqual,
211    // Bitwise
212    BitwiseOr,
213    BitwiseAnd,
214    // Shift
215    ShiftLeft,
216    ShiftRight,
217    // Arithmetic
218    Add,
219    Subtract,
220    Multiply,
221    Divide,
222    Modulo,
223    // Exponent
224    Exponent,
225}
226
227/// A function/module parameter: `name` or `name = default`
228#[derive(Debug, Clone, PartialEq)]
229pub struct Parameter {
230    pub name: String,
231    pub default: Option<Expr>,
232    pub span: Span,
233}
234
235/// A function/module argument: positional `expr` or named `name = expr`
236#[derive(Debug, Clone, PartialEq)]
237pub struct Argument {
238    pub name: Option<String>,
239    pub value: Expr,
240    pub span: Span,
241}
242
243impl Statement {
244    #[must_use]
245    pub const fn span(&self) -> Span {
246        match self {
247            Self::Include { span, .. }
248            | Self::Use { span, .. }
249            | Self::Assignment { span, .. }
250            | Self::ModuleDefinition { span, .. }
251            | Self::FunctionDefinition { span, .. }
252            | Self::ModuleInstantiation { span, .. }
253            | Self::IfElse { span, .. }
254            | Self::Block { span, .. }
255            | Self::Empty { span, .. } => *span,
256        }
257    }
258}