Skip to main content

microcad_lang_parse/ast/
statement.rs

1// Copyright © 2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4use crate::ast;
5use crate::ast::Span;
6
7/// An inner doc block
8#[derive(Debug, PartialEq)]
9#[allow(missing_docs)]
10pub struct InnerDocComment {
11    pub span: Span,
12    pub line: String,
13}
14
15/// A µcad statement.
16#[derive(Debug, PartialEq)]
17pub enum Statement {
18    /// Workbench statement: `part Foo() { ... }`
19    Workbench(WorkbenchDefinition),
20    /// Inline Module: `mod foo { ... }`
21    InlineModule(InlineModule),
22    /// File Module: `mod foo;`
23    FileModule(FileModule),
24    /// Function statement: `fn bar() { ... }`
25    Function(FunctionDefinition),
26    /// Use statement: `use foo::bar;`
27    Use(UseStatement),
28    /// Const definition: `const FOO = 42mm`
29    Const(ConstAssignment),
30    /// Init definition: `init() { ... }`
31    Init(InitDefinition),
32    /// Return statement: `return 23mm;`
33    Return(Return),
34    /// Inner attribute: `#![...]`
35    InnerAttribute(Attribute),
36    /// Inner documentation: `//! Doc comment`
37    InnerDocComment(InnerDocComment),
38    /// Local assignment: `foo = bar;`
39    LocalAssignment(LocalAssignment),
40    /// Property: `prop bar = 42mm;`
41    Property(PropertyAssignment),
42    /// Expression statement: `foo | bar;`
43    Expression(ExpressionStatement),
44    /// Any error occured during parsing.
45    Error(Span),
46}
47
48impl Statement {
49    /// Get the span for the statement
50    pub fn span(&self) -> Span {
51        use Statement::*;
52
53        match self {
54            Workbench(st) => st.span.clone(),
55            InlineModule(st) => st.span.clone(),
56            FileModule(st) => st.span.clone(),
57            Function(st) => st.span.clone(),
58            Use(st) => st.span.clone(),
59            Const(st) => st.span.clone(),
60            Init(st) => st.span.clone(),
61            Return(st) => st.span.clone(),
62            InnerAttribute(st) => st.span.clone(),
63            LocalAssignment(st) => st.span.clone(),
64            Property(st) => st.span.clone(),
65            Expression(st) => st.span.clone(),
66            InnerDocComment(st) => st.span.clone(),
67            Error(span) => span.clone(),
68        }
69    }
70
71    /// Test if statement is supposed to end with a semicolon.
72    pub fn ends_with_semicolon(&self) -> bool {
73        match self {
74            Statement::Workbench(_) => false,
75            Statement::InlineModule(_) => false,
76            Statement::Function(_) => false,
77            Statement::InnerAttribute(_) => false,
78            Statement::InnerDocComment(_) => false,
79            Statement::Init(_) => false,
80            Statement::Error(_) => false,
81
82            Statement::Use(_) => true,
83            Statement::Const(_) => true,
84            Statement::Return(_) => true,
85            Statement::FileModule(_) => true,
86            Statement::LocalAssignment(_) => true,
87            Statement::Property(_) => true,
88            Statement::Expression(e) => !matches!(
89                &e.expression,
90                ast::Expression::Body(_) | ast::Expression::If(_)
91            ),
92        }
93    }
94}
95
96/// The possible type of workbenches
97#[derive(Debug, PartialEq, Copy, Clone)]
98pub enum WorkbenchKind {
99    /// `sketch`
100    Sketch,
101    /// `part`
102    Part,
103    /// `op`
104    Op,
105}
106
107impl std::fmt::Display for WorkbenchKind {
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        write!(
110            f,
111            "{}",
112            match &self {
113                WorkbenchKind::Sketch => "sketch",
114                WorkbenchKind::Part => "part",
115                WorkbenchKind::Op => "op",
116            }
117        )
118    }
119}
120
121/// A definition of a workbench
122#[derive(Debug, PartialEq)]
123#[allow(missing_docs)]
124pub struct WorkbenchDefinition {
125    pub span: Span,
126    pub keyword_span: Span,
127    pub extras: ast::ItemExtras,
128    pub doc: DocBlock,
129    pub kind: WorkbenchKind,
130    pub attributes: Vec<Attribute>,
131    pub visibility: Option<Visibility>,
132    pub name: ast::Identifier,
133    pub plan: ParameterList,
134    pub body: ast::Body,
135}
136
137/// A definition of a module
138#[derive(Debug, PartialEq)]
139#[allow(missing_docs)]
140pub struct InlineModule {
141    pub span: Span,
142    pub keyword_span: Span,
143    pub extras: ast::ItemExtras,
144    pub doc: DocBlock,
145    pub attributes: Vec<Attribute>,
146    pub visibility: Option<Visibility>,
147    pub name: ast::Identifier,
148    pub body: ast::Body,
149}
150
151/// A definition of a module
152#[derive(Debug, PartialEq)]
153#[allow(missing_docs)]
154pub struct FileModule {
155    pub span: Span,
156    pub keyword_span: Span,
157    pub extras: ast::ItemExtras,
158    pub doc: DocBlock,
159    pub attributes: Vec<Attribute>,
160    pub visibility: Option<Visibility>,
161    pub name: ast::Identifier,
162}
163
164/// A definition of a function
165#[derive(Debug, PartialEq)]
166#[allow(missing_docs)]
167pub struct FunctionDefinition {
168    pub span: Span,
169    pub keyword_span: Span,
170    pub extras: ast::ItemExtras,
171    pub doc: DocBlock,
172    pub attributes: Vec<Attribute>,
173    pub visibility: Option<Visibility>,
174    pub name: ast::Identifier,
175    pub parameters: ParameterList,
176    pub return_type: Option<ast::Type>,
177    pub body: ast::Body,
178}
179
180/// An init definition for a workbench
181#[derive(Debug, PartialEq)]
182#[allow(missing_docs)]
183pub struct InitDefinition {
184    pub span: Span,
185    pub keyword_span: Span,
186    pub extras: ast::ItemExtras,
187    pub doc: DocBlock,
188    pub attributes: Vec<Attribute>,
189    pub parameters: ParameterList,
190    pub body: ast::Body,
191}
192
193/// A use statement that imports an item from an external library
194#[derive(Debug, PartialEq)]
195#[allow(missing_docs)]
196pub struct UseStatement {
197    pub span: Span,
198    pub attributes: Vec<Attribute>,
199    pub keyword_span: Span,
200    pub extras: ast::ItemExtras,
201    pub visibility: Option<Visibility>,
202    pub name: UseName,
203    pub use_as: Option<ast::Identifier>,
204}
205
206/// The name of the item being imported
207#[derive(Debug, PartialEq)]
208#[allow(missing_docs)]
209pub struct UseName {
210    pub span: Span,
211    pub extras: ast::ItemExtras,
212    pub parts: Vec<UseStatementPart>,
213}
214
215/// The parts a [`UseName`] consists of, separated by `::`
216#[derive(Debug, PartialEq)]
217#[allow(missing_docs)]
218pub enum UseStatementPart {
219    Identifier(ast::Identifier),
220    Glob(Span),
221    Error(Span),
222}
223
224/// A return statement
225#[derive(Debug, PartialEq)]
226#[allow(missing_docs)]
227pub struct Return {
228    pub span: Span,
229    pub keyword_span: Span,
230    pub extras: ast::ItemExtras,
231    pub value: Option<ast::Expression>,
232}
233
234/// A parameter list of a workbench definition or function definition
235#[derive(Debug, PartialEq)]
236#[allow(missing_docs)]
237pub struct ParameterList {
238    pub span: Span,
239    pub extras: ast::ItemExtras,
240    pub parameters: Vec<Parameter>,
241}
242
243impl ast::Dummy for ParameterList {
244    fn dummy(span: Span) -> Self {
245        Self {
246            span,
247            extras: ast::ItemExtras::default(),
248            parameters: Vec::default(),
249        }
250    }
251}
252
253/// A parameter for a workbench definition or function definition
254#[derive(Debug, PartialEq)]
255#[allow(missing_docs)]
256pub struct Parameter {
257    pub span: Span,
258    pub extras: ast::ItemExtras,
259    pub name: ast::Identifier,
260    pub ty: Option<ast::Type>,
261    pub default: Option<ast::Expression>,
262}
263
264/// An attribute that can be attached to a statement
265#[derive(Debug, PartialEq)]
266#[allow(missing_docs)]
267pub struct Attribute {
268    pub span: Span,
269    pub is_inner: bool,
270    pub extras: ast::ItemExtras,
271    pub commands: Vec<AttributeCommand>,
272}
273
274/// The contents an an [`Attribute`]
275#[derive(Debug, PartialEq)]
276pub enum AttributeCommand {
277    /// A single identifier: `#[deprecated]`
278    Ident(ast::Identifier),
279    /// A meta data assignent: `#[color = RED]`
280    Assignment(LocalAssignment),
281    /// A call: `#[export("file.svg")`
282    Call(ast::Call),
283}
284
285/// A local assignment: `a = 42`
286#[derive(Debug, PartialEq)]
287#[allow(missing_docs)]
288pub struct LocalAssignment {
289    pub span: Span,
290    pub extras: ast::ItemExtras,
291    pub attributes: Vec<Attribute>,
292    pub name: ast::Identifier,
293    pub ty: Option<ast::Type>,
294    pub value: Box<ast::Expression>,
295}
296
297/// A const assignment: `const A = 42` / `pub A = 32`
298#[derive(Debug, PartialEq)]
299#[allow(missing_docs)]
300pub struct ConstAssignment {
301    pub span: Span,
302    pub keyword_span: Span,
303    pub extras: ast::ItemExtras,
304    pub doc: DocBlock,
305    pub attributes: Vec<Attribute>,
306    pub visibility: Option<Visibility>,
307    pub name: ast::Identifier,
308    pub ty: Option<ast::Type>,
309    pub value: Box<ast::Expression>,
310}
311
312/// A property assignment: `prop a = 42`
313#[derive(Debug, PartialEq)]
314#[allow(missing_docs)]
315pub struct PropertyAssignment {
316    pub span: Span,
317    pub keyword_span: Span,
318    pub extras: ast::ItemExtras,
319    pub doc: DocBlock,
320    pub attributes: Vec<Attribute>,
321    pub name: ast::Identifier,
322    pub ty: Option<ast::Type>,
323    pub value: Box<ast::Expression>,
324}
325
326#[derive(Debug, Clone, PartialEq)]
327#[allow(missing_docs)]
328pub enum CommentInner {
329    // A list of single line comments starting with `//`.
330    SingleLine(String),
331    // Comments embraced with `/* ... */`.
332    MultiLine(String),
333}
334
335/// A single- or multi-line comment
336#[derive(Debug, Clone, PartialEq)]
337#[allow(missing_docs)]
338pub struct Comment {
339    pub span: Span,
340    pub inner: CommentInner,
341}
342
343/// Lines of inner or outer doc block including prefix `///`/`//!`.
344#[derive(Debug, PartialEq)]
345#[allow(missing_docs)]
346pub struct DocBlock {
347    pub span: Span,
348    pub lines: Vec<String>,
349}
350
351/// An optional visibility modifier
352///
353/// it can be part of constant, module, function or workbench definitions.
354#[derive(Debug, PartialEq)]
355pub enum Visibility {
356    /// `pub`
357    Public,
358}
359
360/// A statement containing of a bare expression
361#[derive(Debug, PartialEq)]
362#[allow(missing_docs)]
363pub struct ExpressionStatement {
364    pub span: Span,
365    pub extras: ast::ItemExtras,
366    pub attributes: Vec<Attribute>,
367    pub expression: ast::Expression,
368}
369
370/// A list of statements, with optional trailing whitespace kept and an optional "tail" expression
371#[derive(Debug, PartialEq)]
372#[allow(missing_docs)]
373pub struct StatementList {
374    pub span: Span,
375    pub extras: ast::ItemExtras,
376    pub statements: Vec<(Statement, ast::TrailingExtras)>,
377    pub tail: Option<Box<ExpressionStatement>>,
378}
379
380impl ast::Dummy for StatementList {
381    fn dummy(span: Span) -> Self {
382        Self {
383            span,
384            extras: ast::ItemExtras::default(),
385            statements: Vec::default(),
386            tail: None,
387        }
388    }
389}