lucia_lang/compiler/
ast.rs

1//! The Lucia Abstract Syntax Tree (AST).
2
3use std::fmt;
4
5use crate::utils::{escape_str, Join, Location};
6
7/// Kind of function.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum FunctionKind {
10    Function,
11    Closure,
12    Do,
13}
14
15/// A statement.
16#[derive(Debug, Clone)]
17pub struct Stmt {
18    pub kind: StmtKind,
19    pub start: Location,
20    pub end: Location,
21}
22
23impl fmt::Display for Stmt {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        write!(f, "{}", self.kind)
26    }
27}
28
29impl From<Block> for Stmt {
30    fn from(value: Block) -> Self {
31        Stmt {
32            start: value.start,
33            end: value.end,
34            kind: StmtKind::Block(Box::new(value)),
35        }
36    }
37}
38
39impl From<Expr> for Stmt {
40    fn from(value: Expr) -> Self {
41        Stmt {
42            start: value.start,
43            end: value.end,
44            kind: StmtKind::Expr(Box::new(value)),
45        }
46    }
47}
48
49/// Kind of statement.
50#[derive(Debug, Clone)]
51pub enum StmtKind {
52    If {
53        test: Box<Expr>,
54        consequent: Box<Block>,
55        alternate: Option<Box<Stmt>>,
56    },
57    Loop {
58        body: Box<Block>,
59    },
60    While {
61        test: Box<Expr>,
62        body: Box<Block>,
63    },
64    For {
65        left: Vec<Ident>,
66        right: Box<Expr>,
67        body: Box<Block>,
68    },
69    Break,
70    Continue,
71    Return {
72        argument: Box<Expr>,
73    },
74    Throw {
75        argument: Box<Expr>,
76    },
77    Global {
78        arguments: Vec<Ident>,
79    },
80    Import {
81        path: Vec<Ident>,
82        kind: ImportKind,
83    },
84    Assign {
85        left: Box<Expr>,
86        right: Box<Expr>,
87    },
88    AssignOp {
89        operator: BinOp,
90        left: Box<Expr>,
91        right: Box<Expr>,
92    },
93    AssignUnpack {
94        left: Vec<Expr>,
95        right: Box<Expr>,
96    },
97    AssignMulti {
98        left: Vec<Expr>,
99        right: Vec<Expr>,
100    },
101    Block(Box<Block>),
102    Expr(Box<Expr>),
103}
104
105impl fmt::Display for StmtKind {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        match self {
108            StmtKind::If {
109                test,
110                consequent,
111                alternate,
112            } => {
113                if let Some(alternate) = alternate {
114                    write!(f, "if {test} {consequent} else {alternate}")
115                } else {
116                    write!(f, "if {test} {consequent}")
117                }
118            }
119            StmtKind::Loop { body } => write!(f, "loop {body}"),
120            StmtKind::While { test, body } => write!(f, "while {test} {body}"),
121            StmtKind::For { left, right, body } => {
122                write!(f, "for {} in {right} {body}", left.iter().join(", "))
123            }
124            StmtKind::Break => write!(f, "break"),
125            StmtKind::Continue => write!(f, "continue"),
126            StmtKind::Return { argument } => write!(f, "return {argument}"),
127            StmtKind::Throw { argument } => write!(f, "throw {argument}"),
128            StmtKind::Global { arguments } => write!(f, "global {}", arguments.iter().join(", ")),
129            StmtKind::Import { path, kind } => match kind {
130                ImportKind::Simple(alias) => {
131                    write!(f, "import {} as {alias}", path.iter().join("::"))
132                }
133                ImportKind::Nested(v) => write!(
134                    f,
135                    "import {}::{{{}}}",
136                    path.iter().join("::"),
137                    v.iter()
138                        .map(|(name, alias)| format!("{name} as {alias}"))
139                        .join(", ")
140                ),
141                ImportKind::Glob => write!(f, "import {}::*", path.iter().join("::")),
142            },
143            StmtKind::Assign { left, right } => write!(f, "{left} = {right}"),
144            StmtKind::AssignOp {
145                operator,
146                left,
147                right,
148            } => write!(f, "{left} {operator}= {right}"),
149            StmtKind::AssignUnpack { left, right } => {
150                write!(f, "{} = {right}", left.iter().join(", "))
151            }
152            StmtKind::AssignMulti { left, right } => {
153                write!(
154                    f,
155                    "{} = {}",
156                    left.iter().join(", "),
157                    right.iter().join(", ")
158                )
159            }
160            StmtKind::Block(block) => write!(f, "{}", block),
161            StmtKind::Expr(expr) => write!(f, "{}", expr),
162        }
163    }
164}
165
166/// A block.
167#[derive(Debug, Clone)]
168pub struct Block {
169    pub body: Vec<Stmt>,
170    pub start: Location,
171    pub end: Location,
172}
173
174impl fmt::Display for Block {
175    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176        writeln!(f, "{{")?;
177        for stmt in &self.body {
178            writeln!(
179                f,
180                "{}",
181                format!("{}", stmt)
182                    .split('\n')
183                    .map(|x| format!("    {x}"))
184                    .join("\n")
185            )?;
186        }
187        write!(f, "}}")
188    }
189}
190
191/// An expression.
192#[derive(Debug, Clone)]
193pub struct Expr {
194    pub kind: ExprKind,
195    pub start: Location,
196    pub end: Location,
197}
198
199impl fmt::Display for Expr {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        write!(f, "{}", self.kind)
202    }
203}
204
205impl From<Lit> for Expr {
206    fn from(value: Lit) -> Self {
207        Expr {
208            start: value.start,
209            end: value.end,
210            kind: ExprKind::Lit(Box::new(value)),
211        }
212    }
213}
214
215impl From<Ident> for Expr {
216    fn from(value: Ident) -> Self {
217        Expr {
218            start: value.start,
219            end: value.end,
220            kind: ExprKind::Ident(Box::new(value)),
221        }
222    }
223}
224
225/// Kind of expression.
226#[derive(Debug, Clone)]
227pub enum ExprKind {
228    Lit(Box<Lit>),
229    Ident(Box<Ident>),
230    Function {
231        kind: FunctionKind,
232        params: Vec<Ident>,
233        variadic: Option<Box<Ident>>,
234        body: Box<Block>,
235    },
236    FunctionId(usize),
237    Table {
238        properties: Vec<TableProperty>,
239    },
240    List {
241        items: Vec<Expr>,
242    },
243    Unary {
244        operator: UnOp,
245        argument: Box<Expr>,
246    },
247    Binary {
248        operator: BinOp,
249        left: Box<Expr>,
250        right: Box<Expr>,
251    },
252    Member {
253        table: Box<Expr>,
254        property: Box<Expr>,
255        kind: MemberKind,
256        safe: bool,
257    },
258    MetaMember {
259        table: Box<Expr>,
260        safe: bool,
261    },
262    Call {
263        callee: Box<Expr>,
264        arguments: Vec<Expr>,
265        propagating_error: bool,
266    },
267}
268
269impl fmt::Display for ExprKind {
270    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271        match self {
272            ExprKind::Lit(lit) => write!(f, "{lit}"),
273            ExprKind::Ident(ident) => write!(f, "{ident}"),
274            ExprKind::Function {
275                kind,
276                params,
277                variadic,
278                body,
279            } => {
280                if let Some(variadic) = variadic {
281                    match kind {
282                        FunctionKind::Function => write!(
283                            f,
284                            "fn ({}, {}) {}",
285                            params.iter().join(", "),
286                            variadic,
287                            body
288                        ),
289                        FunctionKind::Closure => {
290                            write!(f, "|{}, {}| {}", params.iter().join(", "), variadic, body)
291                        }
292                        FunctionKind::Do => panic!(),
293                    }
294                } else {
295                    match kind {
296                        FunctionKind::Function => {
297                            write!(f, "fn ({}) {}", params.iter().join(", "), body)
298                        }
299                        FunctionKind::Closure => {
300                            write!(f, "|{}| {}", params.iter().join(", "), body)
301                        }
302                        FunctionKind::Do => write!(f, "do {body}"),
303                    }
304                }
305            }
306            ExprKind::FunctionId(id) => write!(f, "<function: {id}>"),
307            ExprKind::Table { properties } => {
308                if properties.is_empty() {
309                    write!(f, "{{}}")
310                } else {
311                    write!(
312                        f,
313                        "{{\n{}\n}}",
314                        properties
315                            .iter()
316                            .join(",\n")
317                            .split('\n')
318                            .map(|x| format!("    {x}"))
319                            .join("\n")
320                    )
321                }
322            }
323            ExprKind::List { items } => {
324                if items.is_empty() {
325                    write!(f, "[]")
326                } else {
327                    write!(
328                        f,
329                        "[\n{}\n]",
330                        items
331                            .iter()
332                            .join(",\n")
333                            .split('\n')
334                            .map(|x| format!("    {x}"))
335                            .join("\n")
336                    )
337                }
338            }
339            ExprKind::Unary { operator, argument } => write!(f, "{operator} {argument}"),
340            ExprKind::Binary {
341                operator,
342                left,
343                right,
344            } => write!(f, "({left} {operator} {right})"),
345            ExprKind::Member {
346                table,
347                property,
348                kind,
349                safe,
350            } => {
351                if *safe {
352                    match kind {
353                        MemberKind::Bracket => write!(f, "{table}?[{property}]"),
354                        MemberKind::Dot => write!(f, "{table}?.{property}"),
355                        MemberKind::DoubleColon => write!(f, "{table}?::{property}"),
356                    }
357                } else {
358                    match kind {
359                        MemberKind::Bracket => write!(f, "{table}[{property}]"),
360                        MemberKind::Dot => write!(f, "{table}.{property}"),
361                        MemberKind::DoubleColon => write!(f, "{table}::{property}"),
362                    }
363                }
364            }
365            ExprKind::MetaMember { table, safe } => {
366                if *safe {
367                    write!(f, "{table}?[#]")
368                } else {
369                    write!(f, "{table}[#]")
370                }
371            }
372            ExprKind::Call {
373                callee,
374                arguments,
375                propagating_error,
376            } => {
377                if *propagating_error {
378                    write!(f, "{callee}({})?", arguments.iter().join(", "))
379                } else {
380                    write!(f, "{callee}({})", arguments.iter().join(", "))
381                }
382            }
383        }
384    }
385}
386
387/// A literal.
388#[derive(Debug, Clone)]
389pub struct Lit {
390    pub value: LitKind,
391    pub start: Location,
392    pub end: Location,
393}
394
395impl fmt::Display for Lit {
396    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
397        write!(f, "{}", self.value)
398    }
399}
400
401/// Kind of literal.
402#[derive(Debug, Clone, PartialEq, PartialOrd)]
403pub enum LitKind {
404    /// "null"
405    Null,
406    /// "true", "false"
407    Bool(bool),
408    /// "12", "0o100", "0b110"
409    Int(i64),
410    /// "12.34", "0b100.100"
411    Float(f64),
412    /// ""abc"", ""abc"
413    Str(String),
414}
415
416impl fmt::Display for LitKind {
417    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
418        match self {
419            LitKind::Null => write!(f, "null"),
420            LitKind::Bool(v) => write!(f, "{v}"),
421            LitKind::Int(v) => write!(f, "{v}"),
422            LitKind::Float(v) => write!(f, "{v}"),
423            LitKind::Str(v) => write!(f, "\"{}\"", escape_str(v, false)),
424        }
425    }
426}
427
428/// An ident.
429#[derive(Debug, Clone)]
430pub struct Ident {
431    pub name: String,
432    pub start: Location,
433    pub end: Location,
434}
435
436impl fmt::Display for Ident {
437    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
438        write!(f, "{}", self.name)
439    }
440}
441
442/// Unary operator.
443#[derive(Debug, Clone, Copy, PartialEq, Eq)]
444pub enum UnOp {
445    /// The `not` operator for logical inversion
446    Not,
447    /// The `-` operator for negation
448    Neg,
449}
450
451impl fmt::Display for UnOp {
452    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
453        match self {
454            UnOp::Not => write!(f, "not"),
455            UnOp::Neg => write!(f, "-"),
456        }
457    }
458}
459
460/// Binary operator.
461#[derive(Debug, Clone, Copy, PartialEq, Eq)]
462pub enum BinOp {
463    /// The `+` operator (addition)
464    Add,
465    /// The `-` operator (subtraction)
466    Sub,
467    /// The `*` operator (multiplication)
468    Mul,
469    /// The `/` operator (division)
470    Div,
471    /// The `%` operator (modulus)
472    Mod,
473    /// The `and` operator (logical and)
474    And,
475    /// The `or` operator (logical or)
476    Or,
477    /// The `==` operator (equality)
478    Eq,
479    /// The `<` operator (less than)
480    Lt,
481    /// The `<=` operator (less than or equal to)
482    Le,
483    /// The `!=` operator (not equal to)
484    Ne,
485    /// The `>=` operator (greater than or equal to)
486    Ge,
487    /// The `>` operator (greater than)
488    Gt,
489    /// The 'is' operator (identity comparison)
490    Is,
491}
492
493impl fmt::Display for BinOp {
494    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
495        match self {
496            BinOp::Add => write!(f, "+"),
497            BinOp::Sub => write!(f, "-"),
498            BinOp::Mul => write!(f, "*"),
499            BinOp::Div => write!(f, "/"),
500            BinOp::Mod => write!(f, "%"),
501            BinOp::And => write!(f, "and"),
502            BinOp::Or => write!(f, "or"),
503            BinOp::Eq => write!(f, "=="),
504            BinOp::Lt => write!(f, "<"),
505            BinOp::Le => write!(f, "<="),
506            BinOp::Ne => write!(f, "!="),
507            BinOp::Ge => write!(f, ">"),
508            BinOp::Gt => write!(f, ">="),
509            BinOp::Is => write!(f, "is"),
510        }
511    }
512}
513
514impl BinOp {
515    pub fn precedence(&self) -> u32 {
516        match self {
517            BinOp::Mul => 5,
518            BinOp::Div => 5,
519            BinOp::Mod => 5,
520
521            BinOp::Add => 4,
522            BinOp::Sub => 4,
523
524            BinOp::Eq => 3,
525            BinOp::Lt => 3,
526            BinOp::Le => 3,
527            BinOp::Ne => 3,
528            BinOp::Ge => 3,
529            BinOp::Gt => 3,
530            BinOp::Is => 3,
531
532            BinOp::And => 2,
533
534            BinOp::Or => 1,
535        }
536    }
537}
538
539/// Kind of member expression.
540#[derive(Debug, Clone, Copy, PartialEq, Eq)]
541pub enum MemberKind {
542    /// `[]`
543    Bracket,
544    /// `.`
545    Dot,
546    /// `::`
547    DoubleColon,
548}
549
550/// Kind of import statement.
551#[derive(Debug, Clone)]
552pub enum ImportKind {
553    /// `import path::xxx as xxx`
554    Simple(Box<Ident>),
555    /// `import path::{...}`
556    Nested(Vec<(Ident, Ident)>),
557    /// `import path::*`
558    Glob,
559}
560
561/// A TableProperty.
562#[derive(Debug, Clone)]
563pub struct TableProperty {
564    pub key: Box<Expr>,
565    pub value: Box<Expr>,
566    pub start: Location,
567    pub end: Location,
568}
569
570impl fmt::Display for TableProperty {
571    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
572        write!(f, "{}: {}", self.key, self.value)
573    }
574}