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    /// Comments at the top of the file, before the first item. Each
12    /// entry is one line of source, including the leading `#`.
13    /// Preserved by the formatter (`lex fmt`); stripped by the
14    /// canonicalizer so they never participate in SigId.
15    #[serde(default, skip_serializing_if = "Vec::is_empty")]
16    pub leading_comments: Vec<String>,
17    /// Comments after the last item (or all of the file, if `items`
18    /// is empty). Same semantics as `leading_comments`.
19    #[serde(default, skip_serializing_if = "Vec::is_empty")]
20    pub trailing_comments: Vec<String>,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
24pub enum Item {
25    Import(Import),
26    TypeDecl(TypeDecl),
27    FnDecl(FnDecl),
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
31pub struct Import {
32    pub reference: String,
33    pub alias: String,
34    /// Comments immediately preceding this import (one line each, `#`
35    /// preserved). Preserved by the formatter; ignored everywhere
36    /// else.
37    #[serde(default, skip_serializing_if = "Vec::is_empty")]
38    pub leading_comments: Vec<String>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
42pub struct TypeDecl {
43    pub name: String,
44    pub params: Vec<String>,
45    pub definition: TypeExpr,
46    /// Comments immediately preceding this declaration. See `Import`
47    /// for semantics.
48    #[serde(default, skip_serializing_if = "Vec::is_empty")]
49    pub leading_comments: Vec<String>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
53pub struct FnDecl {
54    pub name: String,
55    pub type_params: Vec<String>,
56    pub params: Vec<Param>,
57    pub effects: Vec<Effect>,
58    pub return_type: TypeExpr,
59    pub body: Block,
60    /// Optional `examples { call(a, b) => expected, ... }` block (#369).
61    /// Each case binds the function on literal-or-pure arguments and
62    /// declares the value the body is expected to produce. Pure-only in
63    /// v1: a function carrying examples must declare no effects.
64    /// Serialized as an empty `Vec` so the JSON shape stays compatible
65    /// with pre-#369 signatures when the block is absent.
66    #[serde(default, skip_serializing_if = "Vec::is_empty")]
67    pub examples: Vec<Example>,
68    /// Comments immediately preceding this fn declaration. See
69    /// `Import` for semantics.
70    #[serde(default, skip_serializing_if = "Vec::is_empty")]
71    pub leading_comments: Vec<String>,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
75pub struct Example {
76    /// Arguments passed to the function, in declaration order.
77    pub args: Vec<Expr>,
78    /// Value the body is expected to produce.
79    pub expected: Expr,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
83pub struct Param {
84    pub name: String,
85    pub ty: TypeExpr,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
89pub struct Effect {
90    pub name: String,
91    pub arg: Option<EffectArg>,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
95pub enum EffectArg {
96    Str(String),
97    Int(i64),
98    Ident(String),
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
102pub enum TypeExpr {
103    /// Named primitive or constructor application (`Int`, `Result[Int, Str]`, `T`).
104    /// We resolve which it is during type-checking, not parsing.
105    Named { name: String, args: Vec<TypeExpr> },
106    Record(Vec<TypeField>),
107    Tuple(Vec<TypeExpr>),
108    Function {
109        params: Vec<TypeExpr>,
110        effects: Vec<Effect>,
111        ret: Box<TypeExpr>,
112    },
113    Union(Vec<UnionVariant>),
114    /// Record type with one or more spread bases (#363): `{ ...TypeName, field :: Type }`.
115    /// Resolved to a flat `Ty::Record` during type-checking.
116    RecordWithSpreads {
117        spreads: Vec<String>,
118        fields: Vec<TypeField>,
119    },
120    /// Refinement type (#209): a base type plus a predicate the
121    /// inhabitant must satisfy. `Int{x | x > 0 and x <= balance}`
122    /// parses with `base = Named { name: "Int", args: [] }`,
123    /// `binding = "x"`, and `predicate = (x > 0) and (x <= balance)`.
124    /// Slice 1 stores the refinement; the type checker treats the
125    /// refined type as its base. Slice 2 wires up static discharge
126    /// via the spec-checker's gate evaluator; slice 3 adds the
127    /// residual runtime check at call boundaries.
128    Refined {
129        base: Box<TypeExpr>,
130        binding: String,
131        predicate: Box<Expr>,
132    },
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
136pub struct TypeField {
137    pub name: String,
138    pub ty: TypeExpr,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
142pub struct UnionVariant {
143    pub name: String,
144    /// `None` = tag-only (`Empty`); `Some(payload)` = constructor with payload.
145    pub payload: Option<TypeExpr>,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
149pub struct Block {
150    pub statements: Vec<Statement>,
151    pub result: Box<Expr>,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
155pub enum Statement {
156    Let { name: String, ty: Option<TypeExpr>, value: Expr },
157    Expr(Expr),
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
161pub enum Expr {
162    Lit(Literal),
163    Var(String),
164    Block(Block),
165    Call { callee: Box<Expr>, args: Vec<Expr> },
166    Pipe { left: Box<Expr>, right: Box<Expr> },
167    /// `expr?` postfix.
168    Try(Box<Expr>),
169    /// `expr.field`
170    Field { value: Box<Expr>, field: String },
171    BinOp { op: BinOp, lhs: Box<Expr>, rhs: Box<Expr> },
172    UnaryOp { op: UnaryOp, expr: Box<Expr> },
173    If { cond: Box<Expr>, then_block: Block, else_block: Block },
174    Match { scrutinee: Box<Expr>, arms: Vec<Arm> },
175    RecordLit(Vec<RecordLitField>),
176    TupleLit(Vec<Expr>),
177    ListLit(Vec<Expr>),
178    /// A bare constructor name (`None`, `Empty`) or constructor call (`Ok(x)`).
179    /// Since we cannot distinguish a constructor from a variable at parse
180    /// time, the parser emits `Var`/`Call` and the type checker resolves it.
181    /// This variant is kept for the canonicalizer to lift detected
182    /// constructors into.
183    Constructor { name: String, args: Vec<Expr> },
184    Lambda(Box<Lambda>),
185    /// Inline type ascription `(expr :: Type)`. The declared type is checked
186    /// against the inferred type at type-check time; at runtime it compiles
187    /// identically to the inner expression (type-only annotation, erased at
188    /// bytecode level). (#319)
189    Ascription { value: Box<Expr>, ty: TypeExpr },
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
193pub struct Lambda {
194    pub params: Vec<Param>,
195    pub return_type: TypeExpr,
196    pub effects: Vec<Effect>,
197    pub body: Block,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
201pub struct RecordLitField {
202    pub name: String,
203    pub value: Expr,
204}
205
206#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
207pub enum BinOp {
208    Add, Sub, Mul, Div, Mod,
209    Eq, Neq, Lt, Lte, Gt, Gte,
210    And, Or,
211}
212
213impl BinOp {
214    pub fn precedence(self) -> u8 {
215        use BinOp::*;
216        match self {
217            Or => 1,
218            And => 2,
219            Eq | Neq | Lt | Lte | Gt | Gte => 3,
220            Add | Sub => 4,
221            Mul | Div | Mod => 5,
222        }
223    }
224
225    pub fn as_str(self) -> &'static str {
226        use BinOp::*;
227        match self {
228            Add => "+", Sub => "-", Mul => "*", Div => "/", Mod => "%",
229            Eq => "==", Neq => "!=", Lt => "<", Lte => "<=", Gt => ">", Gte => ">=",
230            And => "and", Or => "or",
231        }
232    }
233}
234
235#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
236pub enum UnaryOp { Neg, Not }
237
238#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
239pub struct Arm {
240    pub pattern: Pattern,
241    pub body: Expr,
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
245pub enum Pattern {
246    Lit(Literal),
247    /// Bare ident — either a binder or (during canonicalization) a tag-only constructor.
248    Var(String),
249    Wild,
250    Constructor { name: String, args: Vec<Pattern> },
251    Record { fields: Vec<RecordPatField>, rest: bool },
252    Tuple(Vec<Pattern>),
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
256pub struct RecordPatField {
257    pub name: String,
258    /// `None` means shorthand `{ name }` => `{ name: name }`.
259    pub pattern: Option<Pattern>,
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
263pub enum Literal {
264    Int(i64),
265    Float(f64),
266    Str(String),
267    Bytes(Vec<u8>),
268    Bool(bool),
269    Unit,
270}