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}