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
86#[derive(Default)]
87pub struct EvalConfig {
88    /// Whether to include syntax warnings in the returned result.
89    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
100/// Eval a source file.
101///
102/// eval_range: eval these nodes
103pub 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}