Skip to main content

geam_core/
runtime.rs

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}