Skip to main content

bern_syntax/
ast.rs

1//! Syntax tree for Bern declarations. Spans are kept so diagnostics from
2//! both the macros and the build tool point at the developer's source.
3
4use proc_macro2::Span;
5use syn::Ident;
6
7/// One translation inside `tr!`.
8#[derive(Clone, Debug)]
9pub struct TrEntry {
10    pub language: String,
11    pub text: String,
12    pub span: Span,
13}
14
15/// An interpolation argument after the `;` in `tr!`.
16#[derive(Clone, Debug)]
17pub struct TrArg {
18    pub name: Ident,
19    pub value: Expr,
20}
21
22/// `tr! { "zh-Hans" => "…{count}…", "en" => "…"; count: app.count }`
23#[derive(Clone, Debug)]
24pub struct Tr {
25    pub entries: Vec<TrEntry>,
26    pub args: Vec<TrArg>,
27    pub span: Span,
28}
29
30#[derive(Clone, Debug, PartialEq)]
31pub enum Literal {
32    Text(String),
33    Int(i64),
34    Float(f64),
35    Bool(bool),
36}
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub enum UnaryOp {
40    Not,
41    Neg,
42}
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45pub enum BinaryOp {
46    Eq,
47    Ne,
48    Lt,
49    Le,
50    Gt,
51    Ge,
52    And,
53    Or,
54}
55
56impl BinaryOp {
57    pub fn symbol(self) -> &'static str {
58        match self {
59            BinaryOp::Eq => "==",
60            BinaryOp::Ne => "!=",
61            BinaryOp::Lt => "<",
62            BinaryOp::Le => "<=",
63            BinaryOp::Gt => ">",
64            BinaryOp::Ge => ">=",
65            BinaryOp::And => "&&",
66            BinaryOp::Or => "||",
67        }
68    }
69}
70
71/// `a`, `a.b` or `a.b.c`. Roots are component parameters or template
72/// variables; a lone identifier may also be a keyword such as `segmented`.
73#[derive(Clone, Debug)]
74pub struct PathExpr {
75    pub segments: Vec<Ident>,
76}
77
78impl PathExpr {
79    pub fn span(&self) -> Span {
80        self.segments[0].span()
81    }
82
83    pub fn dotted(&self) -> String {
84        self.segments
85            .iter()
86            .map(Ident::to_string)
87            .collect::<Vec<_>>()
88            .join(".")
89    }
90
91    pub fn single(&self) -> Option<&Ident> {
92        (self.segments.len() == 1).then(|| &self.segments[0])
93    }
94}
95
96/// `Type::Variant` or `Type::Variant { field: expr }`.
97#[derive(Clone, Debug)]
98pub struct VariantExpr {
99    pub ty: Ident,
100    pub variant: Ident,
101    pub fields: Vec<(Ident, Expr)>,
102    pub has_braces: bool,
103}
104
105/// Restricted expressions: literals, translations, typed bindings, enum
106/// values and a fixed set of operators. Arbitrary function calls do not
107/// exist in this grammar.
108#[derive(Clone, Debug)]
109pub enum Expr {
110    Literal {
111        value: Literal,
112        span: Span,
113    },
114    Tr(Tr),
115    Path(PathExpr),
116    Variant(VariantExpr),
117    Unary {
118        op: UnaryOp,
119        expr: Box<Expr>,
120        span: Span,
121    },
122    Binary {
123        op: BinaryOp,
124        lhs: Box<Expr>,
125        rhs: Box<Expr>,
126        span: Span,
127    },
128    /// `client.query(args)`: a typed query binding. It is only written as
129    /// the `query:` property of `Query`; calls exist nowhere else.
130    Query(Action),
131    /// `if cond { a } else { b }`: one of two values of the same type.
132    If {
133        cond: Box<Expr>,
134        then: Box<Expr>,
135        otherwise: Box<Expr>,
136        span: Span,
137    },
138}
139
140impl Expr {
141    pub fn span(&self) -> Span {
142        match self {
143            Expr::Literal { span, .. }
144            | Expr::Unary { span, .. }
145            | Expr::Binary { span, .. }
146            | Expr::If { span, .. } => *span,
147            Expr::Tr(tr) => tr.span,
148            Expr::Path(path) => path.span(),
149            Expr::Variant(variant) => variant.ty.span(),
150            Expr::Query(call) => call.span,
151        }
152    }
153}
154
155#[derive(Clone, Debug)]
156pub struct Prop {
157    pub attrs: Vec<syn::Attribute>,
158    pub name: Ident,
159    pub value: Expr,
160}
161
162impl Prop {
163    /// The value when it is a lone keyword, as `cover` in `fit: cover`.
164    pub fn keyword(&self) -> Option<String> {
165        match &self.value {
166            Expr::Path(path) => path.single().map(Ident::to_string),
167            _ => None,
168        }
169    }
170}
171
172/// `receiver.member.method(args)`: an action after `=>`, or the query call
173/// of a `query:` property.
174#[derive(Clone, Debug)]
175pub struct Action {
176    pub receiver: Vec<Ident>,
177    pub method: Ident,
178    pub args: Vec<Expr>,
179    pub span: Span,
180}
181
182impl Action {
183    pub fn receiver_path(&self) -> String {
184        self.receiver
185            .iter()
186            .map(Ident::to_string)
187            .collect::<Vec<_>>()
188            .join(".")
189    }
190}
191
192/// A built-in component such as `Button(...) { ... } => action`.
193#[derive(Clone, Debug)]
194pub struct Element {
195    pub name: Ident,
196    pub props: Vec<Prop>,
197    /// Row template variable, as in `List(...) |item| { ... }`.
198    pub template: Option<Ident>,
199    pub children: Option<Vec<Node>>,
200    pub action: Option<Action>,
201    pub span: Span,
202}
203
204impl Element {
205    pub fn prop(&self, name: &str) -> Option<&Prop> {
206        self.props.iter().find(|prop| prop.name == name)
207    }
208}
209
210/// A reference to a declared Bern component with named arguments.
211#[derive(Clone, Debug)]
212pub struct ComponentCall {
213    pub path: Vec<Ident>,
214    pub args: Vec<Prop>,
215    pub span: Span,
216}
217
218impl ComponentCall {
219    pub fn name(&self) -> &Ident {
220        self.path.last().expect("non-empty path")
221    }
222
223    pub fn path_string(&self) -> String {
224        self.path
225            .iter()
226            .map(Ident::to_string)
227            .collect::<Vec<_>>()
228            .join("::")
229    }
230}
231
232#[derive(Clone, Debug)]
233pub struct IfNode {
234    pub branches: Vec<(Expr, Vec<Node>)>,
235    pub otherwise: Option<Vec<Node>>,
236    pub span: Span,
237}
238
239/// `if let Some(name) = expr { … } else { … }`
240#[derive(Clone, Debug)]
241pub struct IfLetNode {
242    pub binding: Ident,
243    pub value: Expr,
244    pub then: Vec<Node>,
245    pub otherwise: Option<Vec<Node>>,
246    pub span: Span,
247}
248
249#[derive(Clone, Debug)]
250pub enum Pattern {
251    Variant {
252        ty: Ident,
253        variant: Ident,
254        bindings: Vec<Ident>,
255    },
256    Wildcard(Span),
257}
258
259#[derive(Clone, Debug)]
260pub struct MatchArm {
261    pub attrs: Vec<syn::Attribute>,
262    pub pattern: Pattern,
263    pub body: Vec<Node>,
264    pub span: Span,
265}
266
267#[derive(Clone, Debug)]
268pub struct MatchNode {
269    pub value: Expr,
270    pub arms: Vec<MatchArm>,
271    pub span: Span,
272}
273
274#[derive(Clone, Debug)]
275pub enum Node {
276    Element(Element),
277    Component(ComponentCall),
278    If(IfNode),
279    IfLet(IfLetNode),
280    Match(MatchNode),
281}
282
283/// The element names among `children`, `None` for anything else. With
284/// `items`, for navigation containers, the elements in each branch of a
285/// condition or `match` and in each `ForEach` template count instead.
286pub fn child_names(children: &[Node], items: bool) -> Vec<Option<String>> {
287    fn flatten(nodes: &[Node], out: &mut Vec<Option<String>>) {
288        for node in nodes {
289            match node {
290                Node::Element(child) if child.name == "ForEach" => {
291                    flatten(child.children.as_deref().unwrap_or(&[]), out)
292                }
293                Node::Element(child) => out.push(Some(child.name.to_string())),
294                Node::If(node) => {
295                    for (_, body) in &node.branches {
296                        flatten(body, out);
297                    }
298                    flatten(node.otherwise.as_deref().unwrap_or(&[]), out);
299                }
300                Node::IfLet(node) => {
301                    flatten(&node.then, out);
302                    flatten(node.otherwise.as_deref().unwrap_or(&[]), out);
303                }
304                Node::Match(node) => {
305                    for arm in &node.arms {
306                        flatten(&arm.body, out);
307                    }
308                }
309                Node::Component(_) => out.push(None),
310            }
311        }
312    }
313    if items {
314        let mut out = Vec::new();
315        flatten(children, &mut out);
316        return out;
317    }
318    children
319        .iter()
320        .map(|child| match child {
321            Node::Element(child) => Some(child.name.to_string()),
322            _ => None,
323        })
324        .collect()
325}
326
327impl Node {
328    pub fn span(&self) -> Span {
329        match self {
330            Node::Element(element) => element.span,
331            Node::Component(call) => call.span,
332            Node::If(node) => node.span,
333            Node::IfLet(node) => node.span,
334            Node::Match(node) => node.span,
335        }
336    }
337}
338
339/// The contents of one `ui!` invocation.
340#[derive(Clone, Debug)]
341pub struct UiBody {
342    pub nodes: Vec<Node>,
343}
344
345/// Bern's view of a Rust type used in declarations.
346#[derive(Clone, Debug, PartialEq, Eq)]
347pub enum TypeRef {
348    Unit,
349    Bool,
350    Int,
351    Float,
352    Text,
353    /// A declared enum, record or handle type; the path is kept for
354    /// resolution and the last segment names the type.
355    Named(Vec<String>),
356    Optional(Box<TypeRef>),
357    List(Box<TypeRef>),
358}
359
360impl TypeRef {
361    pub fn name(&self) -> Option<&str> {
362        match self {
363            TypeRef::Named(path) => path.last().map(String::as_str),
364            _ => None,
365        }
366    }
367}