compose_eval/
lib.rs

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    /// Whether the value is allowed to be mutated.
25    ///
26    /// True for any expression except for reading or dereferencing immutable values.
27    pub mutable: bool,
28    /// The span of the binding this value is related to
29    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    /// Whether to include syntax warnings in the returned result.
105    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
116/// Eval a source file.
117///
118/// eval_range: eval these nodes
119pub 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}