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, def};
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(def::Workbench),
20    /// Inline Module: `mod foo { ... }`
21    InlineModule(def::InlineModule),
22    /// File Module: `mod foo;`
23    FileModule(def::FileModule),
24    /// Function statement: `fn bar() { ... }`
25    Function(def::Function),
26    /// Use statement: `use foo::bar;`
27    Use(def::Use),
28    /// Constant definition: `const FOO = 42mm`
29    Const(def::Constant),
30    /// Init definition: `init() { ... }`
31    Init(Init),
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) => {
89                !matches!(&e.expr, ast::Expression::Body(_) | ast::Expression::If(_))
90            }
91        }
92    }
93}
94
95/// An init definition for a workbench
96#[derive(Debug, PartialEq)]
97#[allow(missing_docs)]
98pub struct Init {
99    pub span: Span,
100    pub keyword_span: Span,
101    pub extras: ast::ItemExtras,
102    pub doc: DocBlock,
103    pub attr: Vec<Attribute>,
104    pub parameters: ParameterList,
105    pub body: ast::Body,
106}
107
108/// A return statement
109#[derive(Debug, PartialEq)]
110#[allow(missing_docs)]
111pub struct Return {
112    pub span: Span,
113    pub keyword_span: Span,
114    pub extras: ast::ItemExtras,
115    pub expr: Option<ast::Expression>,
116}
117
118/// A parameter list of a workbench definition or function definition
119#[derive(Debug, PartialEq)]
120#[allow(missing_docs)]
121pub struct ParameterList {
122    pub span: Span,
123    pub extras: ast::ItemExtras,
124    pub parameters: Vec<Parameter>,
125}
126
127impl ast::Dummy for ParameterList {
128    fn dummy(span: Span) -> Self {
129        Self {
130            span,
131            extras: ast::ItemExtras::default(),
132            parameters: Vec::default(),
133        }
134    }
135}
136
137/// A parameter for a workbench definition or function definition
138#[derive(Debug, PartialEq)]
139#[allow(missing_docs)]
140pub struct Parameter {
141    pub span: Span,
142    pub extras: ast::ItemExtras,
143    pub doc: ast::DocBlock,
144    pub attr: Vec<Attribute>,
145    pub id: ast::Identifier,
146    pub ty: Option<ast::Type>,
147    pub default: Option<ast::Expression>,
148}
149
150/// An attribute that can be attached to a statement
151#[derive(Debug, PartialEq)]
152#[allow(missing_docs)]
153pub struct Attribute {
154    pub span: Span,
155    pub is_inner: bool,
156    pub extras: ast::ItemExtras,
157    pub commands: Vec<AttributeCommand>,
158}
159
160/// The contents an an [`Attribute`]
161#[derive(Debug, PartialEq)]
162pub enum AttributeCommand {
163    /// A single identifier: `#[deprecated]`
164    Ident(ast::Identifier),
165    /// A meta data assignent: `#[color = RED]`
166    Assignment(LocalAssignment),
167    /// A call: `#[export("file.svg")`
168    Call(ast::Call),
169}
170
171/// A local assignment: `a = 42`
172#[derive(Debug, PartialEq)]
173#[allow(missing_docs)]
174pub struct LocalAssignment {
175    pub span: Span,
176    pub extras: ast::ItemExtras,
177    pub attr: Vec<Attribute>,
178    pub id: ast::Identifier,
179    pub ty: Option<ast::Type>,
180    pub expr: Box<ast::Expression>,
181}
182
183/// A property assignment: `prop a = 42`
184#[derive(Debug, PartialEq)]
185#[allow(missing_docs)]
186pub struct PropertyAssignment {
187    pub span: Span,
188    pub keyword_span: Span,
189    pub extras: ast::ItemExtras,
190    pub doc: DocBlock,
191    pub attr: Vec<Attribute>,
192    pub id: ast::Identifier,
193    pub ty: Option<ast::Type>,
194    pub value: Box<ast::Expression>,
195}
196
197#[derive(Debug, Clone, PartialEq)]
198#[allow(missing_docs)]
199pub enum CommentInner {
200    // A list of single line comments starting with `//`.
201    SingleLine(String),
202    // Comments embraced with `/* ... */`.
203    MultiLine(String),
204}
205
206/// A single- or multi-line comment
207#[derive(Debug, Clone, PartialEq)]
208#[allow(missing_docs)]
209pub struct Comment {
210    pub span: Span,
211    pub inner: CommentInner,
212}
213
214/// Lines of inner or outer doc block including prefix `///`/`//!`.
215#[derive(Debug, PartialEq)]
216#[allow(missing_docs)]
217pub struct DocBlock {
218    pub span: Span,
219    pub lines: Vec<String>,
220}
221
222/// A statement containing of a bare expression
223#[derive(Debug, PartialEq)]
224#[allow(missing_docs)]
225pub struct ExpressionStatement {
226    pub span: Span,
227    pub extras: ast::ItemExtras,
228    pub attr: Vec<Attribute>,
229    pub expr: ast::Expression,
230}
231
232/// A list of statements, with optional trailing whitespace kept and an optional "tail" expression
233#[derive(Debug, PartialEq)]
234#[allow(missing_docs)]
235pub struct StatementList {
236    pub span: Span,
237    pub extras: ast::ItemExtras,
238    pub statements: Vec<(Statement, ast::TrailingExtras)>,
239    pub tail: Option<Box<ExpressionStatement>>,
240}
241
242impl ast::Dummy for StatementList {
243    fn dummy(span: Span) -> Self {
244        Self {
245            span,
246            extras: ast::ItemExtras::default(),
247            statements: Vec::default(),
248            tail: None,
249        }
250    }
251}