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