Skip to main content

formalang/ast/
mod.rs

1//! Abstract Syntax Tree (AST) for `FormaLang`
2//!
3//! # Invocation Disambiguation
4//!
5//! `FormaLang` uses a two-phase approach for handling struct instantiation and function calls:
6//!
7//! 1. **Parsing Phase**: Both struct instantiation (`Point(x: 1, y: 2)`) and function calls
8//!    (`max(a, b)`) are parsed as a unified [`Expr::Invocation`] node. The parser cannot
9//!    distinguish between them syntactically since both use `Name(args)` syntax.
10//!
11//! 2. **Semantic Analysis Phase**: The semantic analyzer looks up the name in the symbol table
12//!    to determine whether it's a struct or function:
13//!    - **Struct instantiation**: Requires named arguments (`field: value`), supports generic
14//!      type arguments.
15//!    - **Function call**: Uses positional or named arguments; type arguments are rejected.
16//!
17//! This approach follows Rust's model where the same syntax can represent different constructs
18//! depending on what the name resolves to.
19//!
20//! # Module layout
21//!
22//! - [`types`]   — [`Type`], generics, function attributes, extern ABI
23//! - [`definitions`] — [`File`], [`Statement`], [`Definition`] and friends
24//! - [`expressions`] — [`Expr`], [`Literal`], patterns, block statements
25//!
26//! Everything is re-exported here so callers continue to use
27//! `crate::ast::Foo` paths.
28
29mod definitions;
30mod expressions;
31mod numbers;
32mod types;
33
34#[cfg(test)]
35mod tests;
36
37use crate::location::Span;
38use serde::{Deserialize, Serialize};
39
40pub use definitions::{
41    Definition, EnumDef, EnumVariant, FieldDef, File, FnDef, FnParam, FnSig, FunctionDef, ImplDef,
42    LetBinding, ModuleDef, Statement, StructDef, StructField, TraitDef, UseItems, UseStmt,
43    FORMAT_VERSION,
44};
45pub use expressions::{
46    ArrayPatternElement, BindingPattern, BlockStatement, ClosureParam, Expr, Literal, MatchArm,
47    Pattern, StructPatternField,
48};
49pub use numbers::{NumberLiteral, NumberSourceKind, NumberValue, NumericSuffix};
50pub use types::{
51    AttributeAnnotation, ExternAbi, FunctionAttribute, GenericConstraint, GenericParam, TupleField,
52    Type,
53};
54
55/// Visibility modifier
56#[non_exhaustive]
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
58pub enum Visibility {
59    Public,
60    Private,
61}
62
63/// Parameter passing convention (Mutable Value Semantics).
64///
65/// - `Let` — immutable borrow (default). The callee reads but cannot mutate.
66/// - `Mut` — exclusive mutable. The callee may mutate; the updated value is returned
67///   to the caller at the end of the call.
68/// - `Sink` — ownership transfer. The caller gives up the value; the callee owns it.
69#[non_exhaustive]
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
71pub enum ParamConvention {
72    #[default]
73    Let,
74    Mut,
75    Sink,
76}
77
78/// Primitive types
79#[non_exhaustive]
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
81pub enum PrimitiveType {
82    String,
83    I32,
84    I64,
85    F32,
86    F64,
87    Boolean,
88    Path,
89    Regex,
90    /// Uninhabited type — has no values
91    Never,
92}
93
94/// Binary operators
95#[non_exhaustive]
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
97pub enum BinaryOperator {
98    Add,
99    Sub,
100    Mul,
101    Div,
102    Mod,
103    Lt,
104    Gt,
105    Le,
106    Ge,
107    Eq,
108    Ne,
109    And,
110    Or,
111    Range,
112}
113
114impl BinaryOperator {
115    /// Get operator precedence (higher = tighter binding)
116    #[must_use]
117    pub const fn precedence(&self) -> u8 {
118        match self {
119            Self::Range => 0,
120            Self::Or => 1,
121            Self::And => 2,
122            Self::Eq | Self::Ne => 3,
123            Self::Lt | Self::Gt | Self::Le | Self::Ge => 4,
124            Self::Add | Self::Sub => 5,
125            Self::Mul | Self::Div | Self::Mod => 6,
126        }
127    }
128
129    /// Check if operator is left-associative
130    #[must_use]
131    pub const fn is_left_associative(&self) -> bool {
132        true
133    }
134}
135
136/// Unary operators
137#[non_exhaustive]
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
139pub enum UnaryOperator {
140    Neg,
141    Not,
142}
143
144/// Identifier with source location
145#[non_exhaustive]
146#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
147pub struct Ident {
148    pub name: String,
149    pub span: Span,
150}
151
152impl Ident {
153    pub fn new(name: impl Into<String>, span: Span) -> Self {
154        Self {
155            name: name.into(),
156            span,
157        }
158    }
159}