1mod access;
2mod expression;
3mod statement;
4pub mod test;
5mod vm;
6
7pub use crate::vm::Machine;
8use crate::vm::Tracked;
9use compose_library::Value;
10use compose_library::diag::{SourceDiagnostic, SourceResult, Warned, error};
11use compose_syntax::ast::Statement;
12use compose_syntax::{Source, Span};
13use ecow::{EcoVec, eco_vec};
14use std::cmp::min;
15use std::ops::Range;
16
17pub trait Eval {
18 fn eval(self, vm: &mut Machine) -> SourceResult<Evaluated>;
19}
20
21#[derive(Debug, Clone, PartialEq)]
22pub struct Evaluated {
23 pub value: Value,
24 pub mutable: bool,
28 pub origin: Option<Span>,
30}
31
32impl Evaluated {
33 pub fn new(value: Value, mutable: bool) -> Self {
34 Self { value, mutable, origin: None }
35 }
36
37 pub fn mutable(value: Value) -> Self {
38 Self::new(value, true)
39 }
40
41 pub fn immutable(value: Value) -> Self {
42 Self::new(value, false)
43 }
44
45 pub fn unit() -> Self {
46 Self::new(Value::unit(), true)
47 }
48
49 pub fn spanned(self, span: Span) -> Self {
50 Self {
51 value: self.value.spanned(span),
52 ..self
53 }
54 }
55
56 pub fn with_origin(self, origin: Span) -> Self {
57 Self { origin: Some(origin), ..self }
58 }
59
60 pub fn with_value(self, value: Value) -> Self {
61 Self { value, ..self }
62 }
63
64 pub fn make_mutable(self) -> Self {
65 Self { mutable: true, ..self }
66 }
67
68 pub fn value(&self) -> &Value {
69 &self.value
70 }
71
72 pub fn into_value(self) -> Value {
73 self.value
74 }
75}
76
77impl Tracked for Evaluated {
78 fn track_tmp_root(self, vm: &mut Machine) -> Self {
79 Self {
80 value: self.value.track_tmp_root(vm),
81 ..self
82 }
83 }
84}
85
86pub trait ValueEvaluatedExtensions {
87 fn mutable(self) -> Evaluated;
88 fn immutable(self) -> Evaluated;
89}
90
91impl ValueEvaluatedExtensions for Value {
92 fn mutable(self) -> Evaluated {
93 Evaluated::new(self, true)
94 }
95
96 fn immutable(self) -> Evaluated {
97 Evaluated::new(self, false)
98 }
99}
100
101
102#[derive(Default)]
103pub struct EvalConfig {
104 pub include_syntax_warnings: bool,
106}
107
108pub fn eval(
109 source: &Source,
110 vm: &mut Machine,
111 eval_config: &EvalConfig,
112) -> Warned<SourceResult<Value>> {
113 eval_range(source, 0..usize::MAX, vm, eval_config)
114}
115
116pub fn eval_range(
120 source: &Source,
121 eval_range: Range<usize>,
122 vm: &mut Machine,
123 config: &EvalConfig,
124) -> Warned<SourceResult<Value>> {
125 let mut result = Value::unit();
126
127 let range_start = min(eval_range.start, source.nodes().len());
128 let range_end = min(eval_range.end, source.nodes().len());
129
130 let nodes = source.nodes().get(range_start..range_end).unwrap();
131 let errors = nodes
132 .iter()
133 .flat_map(|n| n.errors())
134 .map(|e| e.into())
135 .collect::<EcoVec<SourceDiagnostic>>();
136
137 let syntax_warnings = if config.include_syntax_warnings {
138 nodes
139 .iter()
140 .flat_map(|n| n.warnings())
141 .map(|e| e.into())
142 .collect::<EcoVec<SourceDiagnostic>>()
143 } else {
144 eco_vec![]
145 };
146
147 if !errors.is_empty() {
148 return Warned::new(Err(errors)).with_warnings(syntax_warnings);
149 }
150
151 for node in nodes {
152 let statement: Statement = match node.cast() {
153 Some(expr) => expr,
154 None => {
155 let span = node.span();
156 let err = error!(span, "expected a statement, found {:?}", node);
157
158 return build_err(&syntax_warnings, vm, eco_vec![err], config);
159 }
160 };
161 result = match statement.eval(vm) {
162 Ok(value) => value.value,
163 Err(err) => return build_err(&syntax_warnings, vm, err, config),
164 }
165 }
166
167 let mut warnings = vm.sink_mut().take_warnings();
168 if config.include_syntax_warnings {
169 warnings.extend_from_slice(&syntax_warnings);
170 }
171
172 Warned::new(Ok(result)).with_warnings(warnings)
173}
174
175pub fn build_err(
176 syntax_warnings: &[SourceDiagnostic],
177 vm: &mut Machine,
178 errs: EcoVec<SourceDiagnostic>,
179 config: &EvalConfig,
180) -> Warned<SourceResult<Value>> {
181 let mut warnings = vm.sink_mut().take_warnings();
182 if config.include_syntax_warnings {
183 warnings.extend_from_slice(syntax_warnings);
184 }
185
186 Warned::new(Err(errs)).with_warnings(warnings)
187}