Skip to main content

bubbles/compiler/
ast.rs

1//! AST types: node/statement types and expression tree — data only, no logic.
2
3use std::sync::Arc;
4
5use indexmap::IndexMap;
6
7/// A complete parsed node from a `.bub` script.
8#[derive(Debug, Clone)]
9pub struct Node {
10    /// The node title (must be unique within the program, or share a `when:` clause for groups).
11    pub title: String,
12    /// Tags declared in the `tags:` header.
13    pub tags: Vec<String>,
14    /// All other header key-value pairs, preserved verbatim (minus `title` / `tags` / `when`).
15    pub headers: IndexMap<String, String>,
16    /// Optional `when:` condition for node-group selection (parsed at compile time).
17    pub when: Option<Arc<Expr>>,
18    /// The statements making up the node body (shared [`Arc`] so pickers can clone cheaply).
19    pub body: Arc<Vec<Stmt>>,
20}
21
22/// One branch of an `<<if>>` chain: condition AST + body statements.
23#[derive(Debug, Clone)]
24pub struct IfBranch {
25    /// Parsed condition (same as would be produced from the source string at compile time).
26    pub cond: Arc<Expr>,
27    /// Statements when this branch is taken.
28    pub body: Vec<Stmt>,
29}
30
31/// A statement in a node body.
32#[derive(Debug, Clone)]
33pub enum Stmt {
34    /// A line of dialogue.
35    Line {
36        /// Optional speaker prefix (`Alice:`).
37        speaker: Option<String>,
38        /// Pre-parsed text segments (literals and `{expr}` fragments).
39        text: Vec<TextSegment>,
40        /// Trailing `#tag` metadata.
41        tags: Vec<String>,
42    },
43    /// A `<<set $var = expr>>` assignment.
44    Set {
45        /// Variable name including the `$` sigil.
46        name: String,
47        /// Parsed right-hand expression (compile-time).
48        expr: Arc<Expr>,
49    },
50    /// A conditional block.
51    If {
52        /// `if` / `elseif` branches in order.
53        branches: Vec<IfBranch>,
54        /// `else` body.
55        else_body: Vec<Self>,
56    },
57    /// A `<<jump NodeTitle>>` statement.
58    Jump(String),
59    /// A `<<detour NodeTitle>>` statement.
60    Detour(String),
61    /// A `<<return>>` statement.
62    Return,
63    /// A generic host command `<<name args…>>`.
64    Command {
65        /// Command name.
66        name: String,
67        /// Pre-parsed argument segments (literals and `{expr}` fragments).
68        args: Vec<TextSegment>,
69        /// Trailing `#tag` metadata.
70        tags: Vec<String>,
71    },
72    /// A `<<once>>` … `<<endonce>>` block.
73    Once {
74        /// Stable block id (line number–based), used to track seen state.
75        block_id: String,
76        /// Optional condition for `<<once if …>>` (parsed at compile time).
77        cond: Option<Arc<Expr>>,
78        /// Body that runs the first time.
79        body: Vec<Self>,
80        /// Body that runs after the first time.
81        else_body: Vec<Self>,
82    },
83    /// A `<<declare $var = expr>>` smart-variable declaration.
84    Declare {
85        /// Variable name.
86        name: String,
87        /// Parsed default expression.
88        expr: Arc<Expr>,
89        /// Expression source as written (for [`crate::VariableDecl`] / tooling).
90        default_src: String,
91    },
92    /// A shortcut-option block.
93    Options(Vec<OptionItem>),
94    /// A line-group block (alternatives selected by saliency).
95    LineGroup(Vec<LineVariant>),
96}
97
98/// A single shortcut option.
99#[derive(Debug, Clone)]
100pub struct OptionItem {
101    /// Stable id for once/saliency tracking.
102    pub id: String,
103    /// Pre-parsed display text (literals and `{expr}` fragments).
104    pub text: Vec<TextSegment>,
105    /// Optional guard (`-> text <<if cond>>`); `None` = always available if not `once` exhausted.
106    pub cond: Option<Arc<Expr>>,
107    /// Whether this option is a once-option.
108    pub once: bool,
109    /// Trailing tags.
110    pub tags: Vec<String>,
111    /// Indented body statements executed after selection.
112    pub body: Vec<Stmt>,
113}
114
115/// A line variant inside a `=>` line-group.
116#[derive(Debug, Clone)]
117pub struct LineVariant {
118    /// Stable id.
119    pub id: String,
120    /// Optional speaker.
121    pub speaker: Option<String>,
122    /// Pre-parsed text segments (literals and `{expr}` fragments).
123    pub text: Vec<TextSegment>,
124    /// Optional guard; `None` = always considered with saliency.
125    pub cond: Option<Arc<Expr>>,
126    /// Whether this variant is a once-variant.
127    pub once: bool,
128    /// Trailing tags.
129    pub tags: Vec<String>,
130}
131
132// ── interpolated text ─────────────────────────────────────────────────────────
133
134/// One segment of text that may contain `{expr}` fragments.
135///
136/// Line text, option text, line-variant text, and command argument strings are
137/// all stored as `Vec<TextSegment>` so that `{expr}` fragments are parsed once
138/// at compile time and evaluated cheaply at runtime.
139#[derive(Debug, Clone)]
140pub enum TextSegment {
141    /// A literal string with no interpolation.
142    Literal(String),
143    /// An `{expr}` fragment whose source has already been parsed.
144    Expr(Arc<Expr>),
145}
146
147impl TextSegment {
148    /// Convenience: construct a literal segment from any `Into<String>`.
149    pub fn literal(s: impl Into<String>) -> Self {
150        Self::Literal(s.into())
151    }
152}
153
154// ── expression AST ────────────────────────────────────────────────────────────
155
156/// A node in the expression AST.
157#[derive(Debug, Clone, PartialEq)]
158pub enum Expr {
159    /// Numeric literal.
160    Number(f64),
161    /// String literal.
162    Text(String),
163    /// Boolean literal.
164    Bool(bool),
165    /// Variable read, e.g. `$gold`.
166    Var(String),
167    /// Function call, e.g. `random(1, 6)`.
168    Call {
169        /// Function name.
170        name: String,
171        /// Argument expressions.
172        args: Vec<Self>,
173    },
174    /// Unary operator.
175    Unary {
176        /// Operator.
177        op: UnOp,
178        /// Operand.
179        expr: Box<Self>,
180    },
181    /// Binary operator.
182    Binary {
183        /// Left operand.
184        left: Box<Self>,
185        /// Operator.
186        op: BinOp,
187        /// Right operand.
188        right: Box<Self>,
189    },
190}
191
192/// Binary operator kinds.
193#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194pub enum BinOp {
195    /// `+`
196    Add,
197    /// `-`
198    Sub,
199    /// `*`
200    Mul,
201    /// `/`
202    Div,
203    /// `%`
204    Rem,
205    /// `==`
206    Eq,
207    /// `!=`
208    Neq,
209    /// `<`
210    Lt,
211    /// `<=`
212    Lte,
213    /// `>`
214    Gt,
215    /// `>=`
216    Gte,
217    /// `&&`
218    And,
219    /// `||`
220    Or,
221}
222
223/// Unary operator kinds.
224#[derive(Debug, Clone, Copy, PartialEq, Eq)]
225pub enum UnOp {
226    /// Arithmetic negation `-`.
227    Neg,
228    /// Logical negation `!`.
229    Not,
230}