Skip to main content

lex_syntax/
syntax.rs

1//! Syntax tree (the parser's direct output, before canonicalization in §5).
2//!
3//! These nodes hew closely to source syntax; canonicalization (§5.3) folds
4//! `if` into `Match`, `?` into its match expansion, etc.
5
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
9pub struct Program {
10    pub items: Vec<Item>,
11}
12
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
14pub enum Item {
15    Import(Import),
16    TypeDecl(TypeDecl),
17    FnDecl(FnDecl),
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
21pub struct Import {
22    pub reference: String,
23    pub alias: String,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
27pub struct TypeDecl {
28    pub name: String,
29    pub params: Vec<String>,
30    pub definition: TypeExpr,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
34pub struct FnDecl {
35    pub name: String,
36    pub type_params: Vec<String>,
37    pub params: Vec<Param>,
38    pub effects: Vec<Effect>,
39    pub return_type: TypeExpr,
40    pub body: Block,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
44pub struct Param {
45    pub name: String,
46    pub ty: TypeExpr,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
50pub struct Effect {
51    pub name: String,
52    pub arg: Option<EffectArg>,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
56pub enum EffectArg {
57    Str(String),
58    Int(i64),
59    Ident(String),
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
63pub enum TypeExpr {
64    /// Named primitive or constructor application (`Int`, `Result[Int, Str]`, `T`).
65    /// We resolve which it is during type-checking, not parsing.
66    Named { name: String, args: Vec<TypeExpr> },
67    Record(Vec<TypeField>),
68    Tuple(Vec<TypeExpr>),
69    Function {
70        params: Vec<TypeExpr>,
71        effects: Vec<Effect>,
72        ret: Box<TypeExpr>,
73    },
74    Union(Vec<UnionVariant>),
75    /// Refinement type (#209): a base type plus a predicate the
76    /// inhabitant must satisfy. `Int{x | x > 0 and x <= balance}`
77    /// parses with `base = Named { name: "Int", args: [] }`,
78    /// `binding = "x"`, and `predicate = (x > 0) and (x <= balance)`.
79    /// Slice 1 stores the refinement; the type checker treats the
80    /// refined type as its base. Slice 2 wires up static discharge
81    /// via the spec-checker's gate evaluator; slice 3 adds the
82    /// residual runtime check at call boundaries.
83    Refined {
84        base: Box<TypeExpr>,
85        binding: String,
86        predicate: Box<Expr>,
87    },
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
91pub struct TypeField {
92    pub name: String,
93    pub ty: TypeExpr,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
97pub struct UnionVariant {
98    pub name: String,
99    /// `None` = tag-only (`Empty`); `Some(payload)` = constructor with payload.
100    pub payload: Option<TypeExpr>,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
104pub struct Block {
105    pub statements: Vec<Statement>,
106    pub result: Box<Expr>,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
110pub enum Statement {
111    Let { name: String, ty: Option<TypeExpr>, value: Expr },
112    Expr(Expr),
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
116pub enum Expr {
117    Lit(Literal),
118    Var(String),
119    Block(Block),
120    Call { callee: Box<Expr>, args: Vec<Expr> },
121    Pipe { left: Box<Expr>, right: Box<Expr> },
122    /// `expr?` postfix.
123    Try(Box<Expr>),
124    /// `expr.field`
125    Field { value: Box<Expr>, field: String },
126    BinOp { op: BinOp, lhs: Box<Expr>, rhs: Box<Expr> },
127    UnaryOp { op: UnaryOp, expr: Box<Expr> },
128    If { cond: Box<Expr>, then_block: Block, else_block: Block },
129    Match { scrutinee: Box<Expr>, arms: Vec<Arm> },
130    RecordLit(Vec<RecordLitField>),
131    TupleLit(Vec<Expr>),
132    ListLit(Vec<Expr>),
133    /// A bare constructor name (`None`, `Empty`) or constructor call (`Ok(x)`).
134    /// Since we cannot distinguish a constructor from a variable at parse
135    /// time, the parser emits `Var`/`Call` and the type checker resolves it.
136    /// This variant is kept for the canonicalizer to lift detected
137    /// constructors into.
138    Constructor { name: String, args: Vec<Expr> },
139    Lambda(Box<Lambda>),
140    /// Inline type ascription `(expr :: Type)`. The declared type is checked
141    /// against the inferred type at type-check time; at runtime it compiles
142    /// identically to the inner expression (type-only annotation, erased at
143    /// bytecode level). (#319)
144    Ascription { value: Box<Expr>, ty: TypeExpr },
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
148pub struct Lambda {
149    pub params: Vec<Param>,
150    pub return_type: TypeExpr,
151    pub effects: Vec<Effect>,
152    pub body: Block,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
156pub struct RecordLitField {
157    pub name: String,
158    pub value: Expr,
159}
160
161#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
162pub enum BinOp {
163    Add, Sub, Mul, Div, Mod,
164    Eq, Neq, Lt, Lte, Gt, Gte,
165    And, Or,
166}
167
168impl BinOp {
169    pub fn precedence(self) -> u8 {
170        use BinOp::*;
171        match self {
172            Or => 1,
173            And => 2,
174            Eq | Neq | Lt | Lte | Gt | Gte => 3,
175            Add | Sub => 4,
176            Mul | Div | Mod => 5,
177        }
178    }
179
180    pub fn as_str(self) -> &'static str {
181        use BinOp::*;
182        match self {
183            Add => "+", Sub => "-", Mul => "*", Div => "/", Mod => "%",
184            Eq => "==", Neq => "!=", Lt => "<", Lte => "<=", Gt => ">", Gte => ">=",
185            And => "and", Or => "or",
186        }
187    }
188}
189
190#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
191pub enum UnaryOp { Neg, Not }
192
193#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
194pub struct Arm {
195    pub pattern: Pattern,
196    pub body: Expr,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
200pub enum Pattern {
201    Lit(Literal),
202    /// Bare ident — either a binder or (during canonicalization) a tag-only constructor.
203    Var(String),
204    Wild,
205    Constructor { name: String, args: Vec<Pattern> },
206    Record { fields: Vec<RecordPatField>, rest: bool },
207    Tuple(Vec<Pattern>),
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
211pub struct RecordPatField {
212    pub name: String,
213    /// `None` means shorthand `{ name }` => `{ name: name }`.
214    pub pattern: Option<Pattern>,
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
218pub enum Literal {
219    Int(i64),
220    Float(f64),
221    Str(String),
222    Bytes(Vec<u8>),
223    Bool(bool),
224    Unit,
225}