1mod constant;
2mod echo;
3mod error;
4mod evaluated;
5mod function;
6mod graph;
7mod host;
8mod materialize;
9mod profile;
10mod state;
11mod value;
12
13pub(crate) use host::{
14 StoredRuntimeList, StoredRuntimeListCustomFields, StoredRuntimeListItem,
15 StoredRuntimeListTupleItems, StoredRuntimeValue,
16};
17
18pub use echo::{EchoLocation, EchoOutput, EchoSink};
19pub use error::{
20 BitArraySegmentPanicReason, ExecutionError, HostError, HostLocation, HostOrigin,
21 InvariantError, Panic, PanicDetails, PanicKind, PanicMessage,
22};
23pub(in crate::runtime) use evaluated::{
24 EvaluatedBitArray, EvaluatedBitArrayFunction, EvaluatedBoolFunction, EvaluatedCapture,
25 EvaluatedCustomFunction, EvaluatedCustomValue, EvaluatedExternalFunction,
26 EvaluatedExternalValue, EvaluatedFloatFunction, EvaluatedFunctionFunction,
27 EvaluatedFunctionValueKind, EvaluatedGenericFunction, EvaluatedIntFunction,
28 EvaluatedListFunction, EvaluatedNeverFunction, EvaluatedNilFunction, EvaluatedStringFunction,
29 EvaluatedTupleFunction, EvaluatedUtfCodepointFunction, EvaluatedValue,
30};
31#[cfg(test)]
32pub(in crate::runtime) use evaluated::{EvaluatedFunctionValue, EvaluatedListCapture};
33pub(crate) use value::{
34 BitArrayFunctionValue, BoolFunctionValue, CaptureListValue, CaptureValue, CustomFunctionValue,
35 CustomFunctionValueTarget, ExternalFunctionValue, FloatFunctionValue, FunctionFunctionValue,
36 FunctionValueKind, GenericFunctionValue, IntFunctionValue, ListFunctionValue,
37 NeverFunctionValue, NilFunctionValue, StringFunctionValue, TupleFunctionValue,
38 UtfCodepointFunctionValue,
39};
40pub use value::{
41 BitArrayValue, BitArrayValueLengthError, CustomFieldValue, CustomValue, ExternalValue,
42 ExternalValueIdentity, FunctionValue, ListValue, ListValueItemTypeMismatch, Value,
43 ValueInspection,
44};
45
46pub(in crate::runtime) use profile::{ExecutableRuntimePlan, RuntimeGraph};
47
48use crate::plan::execution::ExecutionPlan;
49use crate::plan::execution::function::{
50 ExecutionGraphProfile, ProfiledCoreRuntimeFunctionId, ProfiledRuntimeFunctionId,
51};
52use crate::plan::execution::runtime::RuntimeExecutionPlan;
53use crate::runtime::error::ExecutionResult;
54use crate::runtime::graph::RetainedValues;
55use crate::runtime::state::{RuntimeState, RuntimeStateFor};
56
57pub fn run_main(plan: &ExecutionPlan, echo: &mut dyn EchoSink) -> Result<Value, ExecutionError> {
58 let mut state = RuntimeState::new(echo);
59 let function = match RuntimeExecutionPlan::main_runtime(plan) {
60 ProfiledRuntimeFunctionId::Core(function) => function,
61 ProfiledRuntimeFunctionId::External(function) => match function {},
62 };
63 let value = run_core_program(plan, &mut state, function, RetainedValues::empty())?;
64 finish_program(plan, &mut state, value)
65}
66
67pub(crate) fn run_hosted_main<Profile: crate::HostProfile>(
68 plan: &crate::plan::execution::HostedExecution<Profile>,
69 host: &mut Profile::RunState,
70 echo: &mut dyn EchoSink,
71) -> Result<Value, ExecutionError> {
72 let mut state = RuntimeState::with_host(echo, host);
73 run_hosted_program_inner(plan, &mut state)
74}
75
76#[cfg(test)]
77fn run_hosted_program<Profile: crate::HostProfile>(
78 plan: &crate::plan::execution::HostedExecution<Profile>,
79 state: &mut RuntimeStateFor<'_, crate::plan::execution::HostedExecution<Profile>>,
80) -> Result<Value, ExecutionError> {
81 run_hosted_program_inner(plan, state)
82}
83
84fn run_hosted_program_inner<Profile: crate::HostProfile>(
85 plan: &crate::plan::execution::HostedExecution<Profile>,
86 state: &mut RuntimeStateFor<'_, crate::plan::execution::HostedExecution<Profile>>,
87) -> Result<Value, ExecutionError> {
88 let inputs = RetainedValues::empty();
89 let value = match plan.main_runtime() {
90 ProfiledRuntimeFunctionId::Core(function) => {
91 run_core_program(plan, state, function, inputs)
92 }
93 ProfiledRuntimeFunctionId::External(function) => {
94 function::run_external(plan, state, function, error::HostCallOrigin::Entry, inputs)
95 .map(EvaluatedValue::External)
96 }
97 }?;
98 finish_program(plan, state, value)
99}
100
101fn run_core_program<Plan>(
102 plan: &Plan,
103 state: &mut RuntimeStateFor<'_, Plan>,
104 function: ProfiledCoreRuntimeFunctionId<RuntimeGraph<Plan>>,
105 inputs: RetainedValues,
106) -> ExecutionResult<EvaluatedValue>
107where
108 Plan: ExecutableRuntimePlan,
109{
110 match function {
111 ProfiledCoreRuntimeFunctionId::Never(function) => {
112 function::run_never(plan, state, function, error::HostCallOrigin::Entry, inputs)
113 .map(|never| match never {})
114 }
115 ProfiledCoreRuntimeFunctionId::Int(function) => {
116 function::run_int(plan, state, function, error::HostCallOrigin::Entry, inputs)
117 .map(EvaluatedValue::Int)
118 }
119 ProfiledCoreRuntimeFunctionId::Float(function) => {
120 function::run_float(plan, state, function, error::HostCallOrigin::Entry, inputs)
121 .map(EvaluatedValue::Float)
122 }
123 ProfiledCoreRuntimeFunctionId::String(function) => {
124 function::run_string(plan, state, function, error::HostCallOrigin::Entry, inputs)
125 .map(EvaluatedValue::String)
126 }
127 ProfiledCoreRuntimeFunctionId::BitArray(function) => {
128 function::run_bit_array(plan, state, function, error::HostCallOrigin::Entry, inputs)
129 .map(EvaluatedValue::BitArray)
130 }
131 ProfiledCoreRuntimeFunctionId::UtfCodepoint(function) => {
132 function::run_utf_codepoint(plan, state, function, error::HostCallOrigin::Entry, inputs)
133 .map(EvaluatedValue::UtfCodepoint)
134 }
135 ProfiledCoreRuntimeFunctionId::Custom(function) => {
136 function::run_custom(plan, state, function, error::HostCallOrigin::Entry, inputs)
137 .map(EvaluatedValue::Custom)
138 }
139 ProfiledCoreRuntimeFunctionId::Bool(function) => {
140 function::run_bool(plan, state, function, error::HostCallOrigin::Entry, inputs)
141 .map(EvaluatedValue::Bool)
142 }
143 ProfiledCoreRuntimeFunctionId::Nil(function) => {
144 function::run_nil(plan, state, function, error::HostCallOrigin::Entry, inputs)
145 .map(|()| EvaluatedValue::Nil)
146 }
147 ProfiledCoreRuntimeFunctionId::Tuple { id, .. } => {
148 function::run_tuple(plan, state, id, error::HostCallOrigin::Entry, inputs)
149 .map(EvaluatedValue::Tuple)
150 }
151 ProfiledCoreRuntimeFunctionId::List(function) => {
152 let function = <RuntimeGraph<Plan> as ExecutionGraphProfile>::list_function(&function);
153 function::run_list(plan, state, function, error::HostCallOrigin::Entry, inputs)
154 .map(EvaluatedValue::from)
155 }
156 ProfiledCoreRuntimeFunctionId::Function { id, .. } => plan
157 .run_function_return(state, id, error::HostCallOrigin::Entry, inputs)
158 .map(EvaluatedValue::Function),
159 }
160}
161
162fn finish_program<Plan>(
163 plan: &Plan,
164 state: &mut RuntimeStateFor<'_, Plan>,
165 value: EvaluatedValue,
166) -> Result<Value, ExecutionError>
167where
168 Plan: ExecutableRuntimePlan,
169{
170 state.lists_mut().drain_releases();
171 Ok(materialize::value(
172 plan.value_metadata(),
173 state.lists(),
174 value,
175 ))
176}
177
178#[cfg(test)]
179fn run_src(src: &str) -> Value {
180 let module =
181 crate::compile_typed_module("main", "main.gleam", src).expect("source should compile");
182 let module_plan = crate::plan_module(module).expect("source should plan");
183 let plan = crate::ExecutionPlan::from_module_plan(module_plan);
184 run_main(&plan, &mut Vec::new()).expect("source should run")
185}
186
187#[cfg(test)]
188fn run_src_error(src: &str) -> ExecutionError {
189 let module =
190 crate::compile_typed_module("main", "main.gleam", src).expect("source should compile");
191 let module_plan = crate::plan_module(module).expect("source should plan");
192 let plan = crate::ExecutionPlan::from_module_plan(module_plan);
193 run_main(&plan, &mut Vec::new()).expect_err("source should fail at runtime")
194}
195
196#[cfg(test)]
197fn plan_src(src: &str) -> crate::ExecutionPlan {
198 let module =
199 crate::compile_typed_module("main", "main.gleam", src).expect("source should compile");
200 let module_plan = crate::plan_module(module).expect("source should plan");
201 crate::ExecutionPlan::from_module_plan(module_plan)
202}
203
204#[cfg(test)]
205fn int(value: i64) -> Value {
206 Value::Int(num_bigint::BigInt::from(value))
207}
208
209#[cfg(test)]
210mod tests {
211 use super::{BitArrayValue, ListValue, Value, int, run_src};
212
213 #[test]
214 fn run_main() {
215 assert_eq!(
216 run_src(
217 r#"
218pub fn main() {
219 1
220}
221"#,
222 ),
223 int(1),
224 );
225 }
226
227 #[test]
228 fn run_main_materializes_utf_codepoint_and_nil_returns() {
229 assert_eq!(
230 run_src("pub fn main() { let assert <<value:utf8_codepoint>> = <<65>> value }"),
231 Value::UtfCodepoint('A'),
232 );
233 assert_eq!(run_src("pub fn main() { Nil }"), Value::Nil);
234 }
235
236 #[test]
237 fn source_constants_preserve_runtime_values_and_function_identity() {
238 let source = r#"
239pub type Boxed(value) { Boxed(value) }
240
241const int = 1
242const float = 1.5
243const string = "geam"
244const bit_array = <<1>>
245const bool = True
246const nil = Nil
247const tuple = #(1, "one")
248const list = [1, 2]
249const empty = []
250const other_empty = []
251const nested = [[]]
252const boxed = Boxed(1)
253const function = identity
254const other_function = identity
255
256fn identity(value) { value }
257
258pub fn main() {
259 #(
260 int,
261 float,
262 string,
263 bit_array,
264 bool,
265 nil,
266 tuple,
267 list,
268 empty == [],
269 empty == other_empty,
270 nested == [[]],
271 boxed == Boxed(1),
272 function == function,
273 function == other_function,
274 )
275}
276"#;
277
278 assert_eq!(
279 run_src(source),
280 Value::Tuple(vec![
281 Value::Int(1.into()),
282 Value::Float(1.5),
283 Value::String("geam".into()),
284 Value::BitArray(BitArrayValue::from_bytes(vec![1])),
285 Value::Bool(true),
286 Value::Nil,
287 Value::Tuple(vec![Value::Int(1.into()), Value::String("one".into())]),
288 Value::List(ListValue::int(vec![1.into(), 2.into()])),
289 Value::Bool(true),
290 Value::Bool(true),
291 Value::Bool(true),
292 Value::Bool(true),
293 Value::Bool(true),
294 Value::Bool(true),
295 ]),
296 );
297 }
298
299 #[test]
300 fn constants_referenced_only_by_unreachable_functions_are_not_evaluated() {
301 let source = r#"
302const failing = <<<<1>>:bits-size(16)>>
303
304fn unused() {
305 failing
306}
307
308pub fn main() {
309 1
310}
311"#;
312
313 assert_eq!(run_src(source), Value::Int(1.into()));
314 }
315
316 #[test]
317 fn constants_are_evaluated_only_when_their_reference_is_evaluated() {
318 let source = r#"
319const failing = <<<<1>>:bits-size(16)>>
320
321pub fn main() {
322 case False {
323 True -> failing
324 False -> <<>>
325 }
326}
327"#;
328
329 assert_eq!(
330 run_src(source),
331 Value::BitArray(BitArrayValue::from_bytes(Vec::new())),
332 );
333 }
334
335 #[test]
336 fn function_constants_preserve_reference_and_instance_identity() {
337 let source = r#"
338pub type Boxed(value) { Boxed(value) }
339
340const constructor = Boxed
341const function = identity
342
343fn identity(value) { value }
344
345pub fn main() {
346 #(
347 constructor == constructor,
348 Boxed == Boxed,
349 function == function,
350 identity == identity,
351 )
352}
353"#;
354
355 assert_eq!(
356 run_src(source),
357 Value::Tuple(vec![
358 Value::Bool(false),
359 Value::Bool(false),
360 Value::Bool(true),
361 Value::Bool(true),
362 ]),
363 );
364 }
365}