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
86#[derive(Default)]
87pub struct EvalConfig {
88 pub include_syntax_warnings: bool,
90}
91
92pub fn eval(
93 source: &Source,
94 vm: &mut Machine,
95 eval_config: &EvalConfig,
96) -> Warned<SourceResult<Value>> {
97 eval_range(source, 0..usize::MAX, vm, eval_config)
98}
99
100pub fn eval_range(
104 source: &Source,
105 eval_range: Range<usize>,
106 vm: &mut Machine,
107 config: &EvalConfig,
108) -> Warned<SourceResult<Value>> {
109 let mut result = Value::unit();
110
111 let range_start = min(eval_range.start, source.nodes().len());
112 let range_end = min(eval_range.end, source.nodes().len());
113
114 let nodes = source.nodes().get(range_start..range_end).unwrap();
115 let errors = nodes
116 .iter()
117 .flat_map(|n| n.errors())
118 .map(|e| e.into())
119 .collect::<EcoVec<SourceDiagnostic>>();
120
121 let syntax_warnings = if config.include_syntax_warnings {
122 nodes
123 .iter()
124 .flat_map(|n| n.warnings())
125 .map(|e| e.into())
126 .collect::<EcoVec<SourceDiagnostic>>()
127 } else {
128 eco_vec![]
129 };
130
131 if !errors.is_empty() {
132 return Warned::new(Err(errors)).with_warnings(syntax_warnings);
133 }
134
135 for node in nodes {
136 let statement: Statement = match node.cast() {
137 Some(expr) => expr,
138 None => {
139 let span = node.span();
140 let err = error!(span, "expected a statement, found {:?}", node);
141
142 return build_err(&syntax_warnings, vm, eco_vec![err], config);
143 }
144 };
145 result = match statement.eval(vm) {
146 Ok(value) => value.value,
147 Err(err) => return build_err(&syntax_warnings, vm, err, config),
148 }
149 }
150
151 let mut warnings = vm.sink_mut().take_warnings();
152 if config.include_syntax_warnings {
153 warnings.extend_from_slice(&syntax_warnings);
154 }
155
156 Warned::new(Ok(result)).with_warnings(warnings)
157}
158
159pub fn build_err(
160 syntax_warnings: &[SourceDiagnostic],
161 vm: &mut Machine,
162 errs: EcoVec<SourceDiagnostic>,
163 config: &EvalConfig,
164) -> Warned<SourceResult<Value>> {
165 let mut warnings = vm.sink_mut().take_warnings();
166 if config.include_syntax_warnings {
167 warnings.extend_from_slice(syntax_warnings);
168 }
169
170 Warned::new(Err(errs)).with_warnings(warnings)
171}