diff options
| author | Laurenz <laurmaedje@gmail.com> | 2024-10-27 19:04:55 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-27 18:04:55 +0000 |
| commit | be7cfc85d08c545abfac08098b7b33b4bd71f37e (patch) | |
| tree | f4137fa2aaa57babae1f7603a9b2ed7e688f43d8 /crates/typst-eval/src/access.rs | |
| parent | b8034a343831e8609aec2ec81eb7eeda57aa5d81 (diff) | |
Split out four new crates (#5302)
Diffstat (limited to 'crates/typst-eval/src/access.rs')
| -rw-r--r-- | crates/typst-eval/src/access.rs | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/crates/typst-eval/src/access.rs b/crates/typst-eval/src/access.rs new file mode 100644 index 00000000..9bcac4d6 --- /dev/null +++ b/crates/typst-eval/src/access.rs @@ -0,0 +1,101 @@ +use ecow::eco_format; +use typst_library::diag::{bail, At, Hint, SourceResult, Trace, Tracepoint}; +use typst_library::foundations::{Dict, Value}; +use typst_syntax::ast::{self, AstNode}; + +use crate::{call_method_access, is_accessor_method, Eval, Vm}; + +/// Access an expression mutably. +pub(crate) trait Access { + /// Access the expression's evaluated value mutably. + fn access<'a>(self, vm: &'a mut Vm) -> SourceResult<&'a mut Value>; +} + +impl Access for ast::Expr<'_> { + fn access<'a>(self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> { + match self { + Self::Ident(v) => v.access(vm), + Self::Parenthesized(v) => v.access(vm), + Self::FieldAccess(v) => v.access(vm), + Self::FuncCall(v) => v.access(vm), + _ => { + let _ = self.eval(vm)?; + bail!(self.span(), "cannot mutate a temporary value"); + } + } + } +} + +impl Access for ast::Ident<'_> { + fn access<'a>(self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> { + let span = self.span(); + if vm.inspected == Some(span) { + if let Ok(value) = vm.scopes.get(&self).cloned() { + vm.trace(value); + } + } + let value = vm.scopes.get_mut(&self).at(span)?; + Ok(value) + } +} + +impl Access for ast::Parenthesized<'_> { + fn access<'a>(self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> { + self.expr().access(vm) + } +} + +impl Access for ast::FieldAccess<'_> { + fn access<'a>(self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> { + access_dict(vm, self)?.at_mut(self.field().get()).at(self.span()) + } +} + +impl Access for ast::FuncCall<'_> { + fn access<'a>(self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> { + if let ast::Expr::FieldAccess(access) = self.callee() { + let method = access.field(); + if is_accessor_method(&method) { + let span = self.span(); + let world = vm.world(); + let args = self.args().eval(vm)?.spanned(span); + let value = access.target().access(vm)?; + let result = call_method_access(value, &method, args, span); + let point = || Tracepoint::Call(Some(method.get().clone())); + return result.trace(world, point, span); + } + } + + let _ = self.eval(vm)?; + bail!(self.span(), "cannot mutate a temporary value"); + } +} + +pub(crate) fn access_dict<'a>( + vm: &'a mut Vm, + access: ast::FieldAccess, +) -> SourceResult<&'a mut Dict> { + match access.target().access(vm)? { + Value::Dict(dict) => Ok(dict), + value => { + let ty = value.ty(); + let span = access.target().span(); + if matches!( + value, // those types have their own field getters + Value::Symbol(_) | Value::Content(_) | Value::Module(_) | Value::Func(_) + ) { + bail!(span, "cannot mutate fields on {ty}"); + } else if typst_library::foundations::fields_on(ty).is_empty() { + bail!(span, "{ty} does not have accessible fields"); + } else { + // type supports static fields, which don't yet have + // setters + Err(eco_format!("fields on {ty} are not yet mutable")) + .hint(eco_format!( + "try creating a new {ty} with the updated field value instead" + )) + .at(span) + } + } + } +} |
