Skip to main content

probl_syntax/
ast.rs

1//! The abstract syntax tree produced by the parser.
2
3use crate::span::Span;
4
5#[derive(Clone, Debug, PartialEq)]
6pub struct Ident {
7    pub name: String,
8    pub span: Span,
9}
10
11#[derive(Clone, Debug, PartialEq)]
12pub struct Program {
13    pub pragmas: Vec<Pragma>,
14    pub items: Vec<Item>,
15}
16
17/// `@mode exact`, `@mode sample(runs: 1000)`, `@epsilon 1e-9`.
18#[derive(Clone, Debug, PartialEq)]
19pub struct Pragma {
20    pub name: Ident,
21    pub arg: Option<Expr>,
22    pub span: Span,
23}
24
25#[derive(Clone, Debug, PartialEq)]
26pub enum Item {
27    Fn(FnDecl),
28    Type(TypeDecl),
29    Enum(EnumDecl),
30    Import(Import),
31    Stmt(Stmt),
32}
33
34#[derive(Clone, Debug, PartialEq)]
35pub struct FnDecl {
36    pub name: Ident,
37    pub params: Vec<Param>,
38    pub ret: Option<TypeExpr>,
39    pub body: Block,
40    pub span: Span,
41}
42
43#[derive(Clone, Debug, PartialEq)]
44pub struct Param {
45    pub name: Ident,
46    pub ty: Option<TypeExpr>,
47}
48
49#[derive(Clone, Debug, PartialEq)]
50pub struct TypeDecl {
51    pub name: Ident,
52    pub ty: TypeExpr,
53    pub span: Span,
54}
55
56#[derive(Clone, Debug, PartialEq)]
57pub struct EnumDecl {
58    pub name: Ident,
59    pub variants: Vec<Ident>,
60    pub span: Span,
61}
62
63#[derive(Clone, Debug, PartialEq)]
64pub struct Import {
65    pub path: String,
66    pub span: Span,
67}
68
69#[derive(Clone, Debug, PartialEq)]
70pub enum TypeExpr {
71    /// `int`, `list[int]`, `dist[int]`, `Fighter`.
72    Named { name: Ident, args: Vec<TypeExpr> },
73    /// `{ hp: int, ac: int }`.
74    Record { fields: Vec<(Ident, TypeExpr)>, span: Span },
75}
76
77#[derive(Clone, Debug, PartialEq)]
78pub struct Block {
79    pub stmts: Vec<Stmt>,
80    pub span: Span,
81}
82
83#[derive(Clone, Debug, PartialEq)]
84pub struct Stmt {
85    pub kind: StmtKind,
86    pub span: Span,
87}
88
89/// `=` or `~` in a binding or assignment.
90#[derive(Clone, Copy, Debug, PartialEq, Eq)]
91pub enum BindOp {
92    Assign,
93    Draw,
94}
95
96#[derive(Clone, Copy, Debug, PartialEq, Eq)]
97pub enum AssignOp {
98    Set,
99    Draw,
100    Add,
101    Sub,
102    Mul,
103    Div,
104}
105
106#[derive(Clone, Debug, PartialEq)]
107pub enum StmtKind {
108    /// `let` / `var` bindings.
109    Let {
110        mutable: bool,
111        pattern: Pattern,
112        ty: Option<TypeExpr>,
113        op: BindOp,
114        value: Expr,
115    },
116    Assign {
117        target: Expr,
118        op: AssignOp,
119        value: Expr,
120    },
121    For {
122        pattern: Pattern,
123        iter: Expr,
124        body: Block,
125    },
126    While {
127        cond: Expr,
128        body: Block,
129    },
130    Repeat {
131        count: Expr,
132        body: Block,
133    },
134    Loop {
135        body: Block,
136    },
137    Break,
138    Continue,
139    Return(Option<Expr>),
140    Observe {
141        value: Expr,
142        from: Option<Expr>,
143    },
144    /// Multiply the current world's weight by a probability.
145    Score(Expr),
146    Report {
147        value: Expr,
148        by: Option<Expr>,
149        label: Option<(String, Span)>,
150    },
151    Expr(Expr),
152}
153
154#[derive(Clone, Debug, PartialEq)]
155pub struct Expr {
156    pub kind: ExprKind,
157    pub span: Span,
158}
159
160#[derive(Clone, Debug, PartialEq)]
161pub enum ExprKind {
162    Int(probl_number::Integer),
163    Float(f64),
164    /// Already divided by 100.
165    Percent(f64),
166    Dice {
167        count: u32,
168        sides: u32,
169    },
170    Str(Vec<StrSegment>),
171    Bool(bool),
172    Name(String),
173    List(Vec<Expr>),
174    Map(Vec<(Expr, Expr)>),
175    /// `Fighter { hp: 12 }` (named) or `{ won: true }` (anonymous).
176    Record {
177        name: Option<Ident>,
178        fields: Vec<Field>,
179    },
180    Unary {
181        op: UnOp,
182        expr: Box<Expr>,
183    },
184    /// Prefix `~`: draw one value each time this expression is evaluated.
185    Draw(Box<Expr>),
186    Binary {
187        op: BinOp,
188        lhs: Box<Expr>,
189        rhs: Box<Expr>,
190    },
191    Call {
192        callee: Box<Expr>,
193        args: Vec<Arg>,
194    },
195    /// `receiver.name(args)`.
196    Method {
197        receiver: Box<Expr>,
198        name: Ident,
199        args: Vec<Arg>,
200    },
201    Field {
202        expr: Box<Expr>,
203        name: Ident,
204    },
205    Index {
206        expr: Box<Expr>,
207        index: Box<Expr>,
208    },
209    /// `expr with { field: value }`.
210    With {
211        expr: Box<Expr>,
212        fields: Vec<Field>,
213    },
214    Lambda {
215        params: Vec<Ident>,
216        body: Box<Expr>,
217    },
218    If {
219        cond: Box<Expr>,
220        then: Block,
221        otherwise: Option<Box<Expr>>,
222    },
223    Chance {
224        arms: Vec<ChanceArm>,
225    },
226    Match {
227        scrutinee: Box<Expr>,
228        arms: Vec<MatchArm>,
229    },
230    Simulate(Block),
231    Block(Block),
232}
233
234#[derive(Clone, Debug, PartialEq)]
235pub enum StrSegment {
236    Lit(String),
237    Expr(Expr),
238}
239
240#[derive(Clone, Debug, PartialEq)]
241pub struct Field {
242    pub name: Ident,
243    pub value: Expr,
244}
245
246#[derive(Clone, Debug, PartialEq)]
247pub struct Arg {
248    pub name: Option<Ident>,
249    pub value: Expr,
250}
251
252/// `weight => body`; the weight is `None` for `else`.
253#[derive(Clone, Debug, PartialEq)]
254pub struct ChanceArm {
255    pub weight: Option<Expr>,
256    pub body: Stmt,
257    pub span: Span,
258}
259
260#[derive(Clone, Debug, PartialEq)]
261pub struct MatchArm {
262    pub pattern: Pattern,
263    pub guard: Option<Expr>,
264    pub body: Stmt,
265    pub span: Span,
266}
267
268#[derive(Clone, Copy, Debug, PartialEq, Eq)]
269pub enum UnOp {
270    Neg,
271    Not,
272    Typeof,
273}
274
275#[derive(Clone, Copy, Debug, PartialEq, Eq)]
276pub enum BinOp {
277    Or,
278    And,
279    Eq,
280    Ne,
281    Lt,
282    Le,
283    Gt,
284    Ge,
285    In,
286    NotIn,
287    /// `a..b`, both ends included.
288    Range,
289    /// `a..<b`.
290    RangeExcl,
291    /// `a to b`: an estimate with a 90% interval.
292    To,
293    Add,
294    Sub,
295    Mul,
296    Div,
297    IntDiv,
298    Mod,
299    Pow,
300}
301
302impl BinOp {
303    pub fn symbol(self) -> &'static str {
304        match self {
305            BinOp::Or => "or",
306            BinOp::And => "and",
307            BinOp::Eq => "==",
308            BinOp::Ne => "!=",
309            BinOp::Lt => "<",
310            BinOp::Le => "<=",
311            BinOp::Gt => ">",
312            BinOp::Ge => ">=",
313            BinOp::In => "in",
314            BinOp::NotIn => "not in",
315            BinOp::Range => "..",
316            BinOp::RangeExcl => "..<",
317            BinOp::To => "to",
318            BinOp::Add => "+",
319            BinOp::Sub => "-",
320            BinOp::Mul => "*",
321            BinOp::Div => "/",
322            BinOp::IntDiv => "div",
323            BinOp::Mod => "mod",
324            BinOp::Pow => "^",
325        }
326    }
327}
328
329#[derive(Clone, Debug, PartialEq)]
330pub struct Pattern {
331    pub kind: PatternKind,
332    pub span: Span,
333}
334
335#[derive(Clone, Debug, PartialEq)]
336pub enum PatternKind {
337    Wildcard,
338    /// A name: binds a new variable, or matches an enum variant of that name.
339    Name(String),
340    Literal(Expr),
341    List(Vec<Pattern>),
342    Or(Vec<Pattern>),
343}