1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
//! _(grain)_ A bytecode [`VM`] for Rhai.
//!
//! Exported under the `grain` feature only.
//!
//! [`AST`]: crate::AST
//! [`Engine`]: crate::Engine
//! [`VM`]: vm::Vm
//!
//! # Core Concept
//!
//! Rhai evaluates a script by walking its [`AST`], which the parser allocates
//! a node at a time — so holding a script costs in proportion to how much program
//! there is, and the parser's peak is higher again than what it settles at.
//!
//! That is usually what caps script size on a resource-constrained embedded target
//! long before anything else does.
//!
//! Rhai Grain transpiles the [`AST`] to a flat instruction stream that can be
//! produced elsewhere and loaded without a parser or even the [`AST`] interpreter.
//!
//! ### Note
//!
//! `tests/grain/allocation.rs` measures both ends with a tracking allocator.
//!
//! # Safety
//!
//! The entire Rhai Grain module contains no `unsafe` code.
//! It is marked with `#![forbid(unsafe_code)]`.
//!
//! # Reading and Writing Rhai Grain Programs
//!
//! An [`AST`] that has been lowered all the way through can be written out with
//! [`Program::write`] and read back with [`Program::read`]. That is the artifact
//! the device loads, and the reason the [`AST`] never has to exist there.
//!
//! # Transpiling and Executing Bytecodes
//!
//! Bytecodes execution reuses the host [`Engine`]: [`Dynamic`][crate::Dynamic] stays
//! the value type and every registered function is dispatched by the [`Engine`] itself.
//!
//! Only control flow, local variables access and operator fast paths are reimplemented
//! by the [`VM`].
//!
//! The [`Engine`] does the parsing to [`AST`] and, at runtime, all the functions
//! dispatching. The [`VM`] essentially flattens and threads the [`AST`] into a linear
//! instruction stream so it can be executed without recursion nor multiple layers
//! of function calls.
//!
//! ```
//! use rhai::grain::{Compiler, Vm};
//! use rhai::{Engine, Scope};
//!
//! let engine = Engine::new();
//! let ast = engine.compile("let total = 0; for i in 0..10 { total += i; } total")?;
//!
//! let program = Compiler::new().compile(&ast);
//!
//! // The `Scope` is the caller's locals: a script declares are left in it,
//! // exactly as `Engine::eval_with_scope` would.
//! let mut scope = Scope::new();
//! let value = Vm::new(&engine).eval_with_scope(&mut scope, &program)?;
//!
//! assert_eq!(value.as_int().unwrap(), 45);
//! # Ok::<_, Box<rhai::EvalAltResult>>(())
//! ```
//!
//! # Residual Fragments
//!
//! Coverage is total from the start, by construction rather than by effort.
//!
//! Anything the transpiler cannot yet lower is kept as an _AST fragment_ and
//! handed back to Rhai's interpreter through
//! [`Op::EvalAst`][crate::grain::bytecode::Op::EvalAst], so a [`Program`] always
//! means the same thing as the [`AST`] it came from.
//!
//! [`Program::residual_count`] indicates how much of the [`AST`] is still residual.
//!
//! When it drops to zero, the [`AST`] has been fully lowered and the [`VM`] can execute
//! it directly without any help from the [`AST`] interpreter, which can even be removed
//! by enabling the `no_ast` feature.
//!
//! # Shipping Bytecodes
//!
//! The entire point of using bytecodes is:
//! - compile on a host,
//! - run somewhere that never sees the source.
//!
//! A loaded [`Program`] borrows its instructions directly from the raw bytecodes,
//!
//! Nothing it retains grows with how long the script is.
//!
//! ```
//! use rhai::grain::{Compiler, Program, Vm};
//! use rhai::{Engine, Scope};
//!
//! let engine = Engine::new();
//!
//! // On the host.
//! let ast = engine.compile("let x = 6; x * 7")?;
//! let program = Compiler::new().compile(&ast);
//!
//! // `write` refuses a program still holding AST fragments, so this is also
//! // the check that the script lowered all the way through.
//! assert_eq!(program.residual_count(), 0);
//! let bytes = program.write().expect("no residuals, so it is writable");
//!
//! // On the device, with no parser, no interpreter and no AST in sight.
//! let loaded = Program::read(&bytes).expect("written by this build");
//! let value = Vm::new(&engine).eval(&loaded)?;
//!
//! assert_eq!(value.as_int().unwrap(), 42);
//! # Ok::<_, Box<rhai::EvalAltResult>>(())
//! ```
//!
//! # Diagnostics
//!
//! Diagnostics are separable: [`Program::write_stripped`] hands back the
//! bytecodes artifact and a [`Sidecar`] separately, so the device carries
//! only the first.
//!
//! A failure comes back as one [`Fault`] per frame, and the host resolves
//! those against the sidecar it kept — a stack of addresses on one side,
//! a symbol file on the other, as a crash reporter does.
//!
//! ### Example
//!
//! The example below needs script functions to have two frames, positions to
//! resolve them to, and a division by zero that raises rather than panicking,
//! so it is compiled only where all three exist.
//! # Debugging - Differences With the [`AST`] Interpreter
//!
//! A `debugging` build marks every statement, and the [`VM`] stops at the markers:
//! back-trace, stepping, break-points by position as well as function-exit events
//! all work.
//!
//! ### Statement-Level Debugging Only
//!
//! However, a _statement_ is as fine as Rhai Grain gets.
//!
//! The [`AST`] interpreter stops at every *expression*, which the [`Program`] no
//! longer has — so a step lands on the next _statement_ rather than part way through
//! the one it is on.
//!
//! Similarly, a break-point on a function name or a property name etc. never matches,
//! because what the [`AST`] interpreter hands the callback a synthetic `Noop` node,
//! not the call itself. A break-point by position covers the same line.
//!
//! ### Cost of Debug Markers
//!
//! The debugging markers are the one part of a program that a shipping build
//! (intended for a [`VM`] without the `debugging` feature flag enabled) should not include:
//! an [`Engine`] with no `debugging` callbacks has nothing to stop for.
//!
//! Each marker costs about six bytes per statement where they are compiled and a
//! [`Program`] without them still runs anywhere -- it simply cannot be stopped.
//!
// A [`VM`] that runs untrusted bytecode has no business containing any debugging markers.
pub
pub use Compiler;
pub use ;
pub use Site;
pub use ;
pub use ;
pub use Function;
pub use ;