diff options
Diffstat (limited to 'src/eval')
| -rw-r--r-- | src/eval/array.rs | 14 | ||||
| -rw-r--r-- | src/eval/func.rs | 69 | ||||
| -rw-r--r-- | src/eval/library.rs | 26 | ||||
| -rw-r--r-- | src/eval/methods.rs | 31 | ||||
| -rw-r--r-- | src/eval/mod.rs | 42 | ||||
| -rw-r--r-- | src/eval/scope.rs | 4 | ||||
| -rw-r--r-- | src/eval/symbol.rs | 126 | ||||
| -rw-r--r-- | src/eval/value.rs | 9 |
8 files changed, 168 insertions, 153 deletions
diff --git a/src/eval/array.rs b/src/eval/array.rs index fa71ff1a..bebbe809 100644 --- a/src/eval/array.rs +++ b/src/eval/array.rs @@ -137,7 +137,7 @@ impl Array { self.0.contains(value) } - /// Return the first matching element. + /// Return the first matching item. pub fn find(&self, vm: &mut Vm, func: Func) -> SourceResult<Option<Value>> { for item in self.iter() { let args = Args::new(func.span(), [item.clone()]); @@ -148,7 +148,7 @@ impl Array { Ok(None) } - /// Return the index of the first matching element. + /// Return the index of the first matching item. pub fn position(&self, vm: &mut Vm, func: Func) -> SourceResult<Option<i64>> { for (i, item) in self.iter().enumerate() { let args = Args::new(func.span(), [item.clone()]); @@ -160,8 +160,8 @@ impl Array { Ok(None) } - /// Return a new array with only those elements for which the function - /// returns true. + /// Return a new array with only those items for which the function returns + /// true. pub fn filter(&self, vm: &mut Vm, func: Func) -> SourceResult<Self> { let mut kept = EcoVec::new(); for item in self.iter() { @@ -189,7 +189,7 @@ impl Array { .collect() } - /// Fold all of the array's elements into one with a function. + /// Fold all of the array's items into one with a function. pub fn fold(&self, vm: &mut Vm, init: Value, func: Func) -> SourceResult<Value> { let mut acc = init; for item in self.iter() { @@ -199,7 +199,7 @@ impl Array { Ok(acc) } - /// Whether any element matches. + /// Whether any item matches. pub fn any(&self, vm: &mut Vm, func: Func) -> SourceResult<bool> { for item in self.iter() { let args = Args::new(func.span(), [item.clone()]); @@ -211,7 +211,7 @@ impl Array { Ok(false) } - /// Whether all elements match. + /// Whether all items match. pub fn all(&self, vm: &mut Vm, func: Func) -> SourceResult<bool> { for item in self.iter() { let args = Args::new(func.span(), [item.clone()]); diff --git a/src/eval/func.rs b/src/eval/func.rs index 7bf1814f..ef042d6d 100644 --- a/src/eval/func.rs +++ b/src/eval/func.rs @@ -8,14 +8,12 @@ use comemo::{Prehashed, Track, Tracked, TrackedMut}; use once_cell::sync::Lazy; use super::{ - cast_to_value, Args, CastInfo, Dict, Eval, Flow, Route, Scope, Scopes, Tracer, Value, - Vm, + cast_to_value, Args, CastInfo, Eval, Flow, Route, Scope, Scopes, Tracer, Value, Vm, }; -use crate::diag::{bail, SourceResult, StrResult}; -use crate::model::{Introspector, NodeId, Selector, StabilityProvider, StyleMap, Vt}; +use crate::diag::{bail, SourceResult}; +use crate::model::{ElemFunc, Introspector, StabilityProvider, Vt}; use crate::syntax::ast::{self, AstNode, Expr, Ident}; use crate::syntax::{SourceId, Span, SyntaxNode}; -use crate::util::hash128; use crate::World; /// An evaluatable function. @@ -32,8 +30,8 @@ pub struct Func { enum Repr { /// A native Rust function. Native(NativeFunc), - /// A function for a node. - Node(NodeId), + /// A function for an element. + Elem(ElemFunc), /// A user-defined closure. Closure(Closure), /// A nested function with pre-applied arguments. @@ -45,7 +43,7 @@ impl Func { pub fn name(&self) -> Option<&str> { match &**self.repr { Repr::Native(native) => Some(native.info.name), - Repr::Node(node) => Some(node.info.name), + Repr::Elem(func) => Some(func.info().name), Repr::Closure(closure) => closure.name.as_deref(), Repr::With(func, _) => func.name(), } @@ -55,7 +53,7 @@ impl Func { pub fn info(&self) -> Option<&FuncInfo> { match &**self.repr { Repr::Native(native) => Some(&native.info), - Repr::Node(node) => Some(&node.info), + Repr::Elem(func) => Some(func.info()), Repr::With(func, _) => func.info(), _ => None, } @@ -93,8 +91,8 @@ impl Func { args.finish()?; Ok(value) } - Repr::Node(node) => { - let value = (node.construct)(vm, &mut args)?; + Repr::Elem(func) => { + let value = func.construct(vm, &mut args)?; args.finish()?; Ok(Value::Content(value)) } @@ -145,46 +143,13 @@ impl Func { } } - /// Create a selector for this function's node type, filtering by node's - /// whose [fields](super::Content::field) match the given arguments. - pub fn where_(self, args: &mut Args) -> StrResult<Selector> { - let fields = args.to_named(); - args.items.retain(|arg| arg.name.is_none()); - self.select(Some(fields)) - } - - /// The node id of this function if it is an element function. - pub fn id(&self) -> Option<NodeId> { + /// Extract the element function, if it is one. + pub fn element(&self) -> Option<ElemFunc> { match **self.repr { - Repr::Node(id) => Some(id), + Repr::Elem(func) => Some(func), _ => None, } } - - /// Execute the function's set rule and return the resulting style map. - pub fn set(&self, mut args: Args) -> SourceResult<StyleMap> { - Ok(match &**self.repr { - Repr::Node(node) => { - let styles = (node.set)(&mut args)?; - args.finish()?; - styles - } - _ => StyleMap::new(), - }) - } - - /// Create a selector for this function's node type. - pub fn select(&self, fields: Option<Dict>) -> StrResult<Selector> { - let Some(id) = self.id() else { - return Err("this function is not selectable".into()); - }; - - if id == item!(text_id) { - Err("to select text, please use a string or regex instead")?; - } - - Ok(Selector::Node(id, fields)) - } } impl Debug for Func { @@ -198,7 +163,7 @@ impl Debug for Func { impl PartialEq for Func { fn eq(&self, other: &Self) -> bool { - hash128(&self.repr) == hash128(&other.repr) + self.repr == other.repr } } @@ -211,13 +176,13 @@ impl From<Repr> for Func { } } -impl From<NodeId> for Func { - fn from(id: NodeId) -> Self { - Repr::Node(id).into() +impl From<ElemFunc> for Func { + fn from(func: ElemFunc) -> Self { + Repr::Elem(func).into() } } -/// A native Rust function. +/// A Typst function defined by a native Rust function. pub struct NativeFunc { /// The function's implementation. pub func: fn(&mut Vm, &mut Args) -> SourceResult<Value>, diff --git a/src/eval/library.rs b/src/eval/library.rs index eae342c2..85d5647b 100644 --- a/src/eval/library.rs +++ b/src/eval/library.rs @@ -10,7 +10,7 @@ use super::{Args, Dynamic, Module, Value, Vm}; use crate::diag::SourceResult; use crate::doc::Document; use crate::geom::{Abs, Dir}; -use crate::model::{Content, Introspector, Label, NodeId, StyleChain, StyleMap, Vt}; +use crate::model::{Content, ElemFunc, Introspector, Label, StyleChain, Styles, Vt}; use crate::syntax::Span; use crate::util::hash128; use crate::World; @@ -23,7 +23,7 @@ pub struct Library { /// The scope containing definitions available in math mode. pub math: Module, /// The default properties for page size, font selection and so on. - pub styles: StyleMap, + pub styles: Styles, /// Defines which standard library items fulfill which syntactical roles. pub items: LangItems, } @@ -44,9 +44,9 @@ pub struct LangItems { pub linebreak: fn() -> Content, /// Plain text without markup. pub text: fn(text: EcoString) -> Content, - /// The id of the text node. - pub text_id: NodeId, - /// Get the string if this is a text node. + /// The text function. + pub text_func: ElemFunc, + /// Get the string if this is a text element. pub text_str: fn(&Content) -> Option<EcoString>, /// A smart quote: `'` or `"`. pub smart_quote: fn(double: bool) -> Content, @@ -114,7 +114,7 @@ impl Hash for LangItems { self.space.hash(state); self.linebreak.hash(state); self.text.hash(state); - self.text_id.hash(state); + self.text_func.hash(state); (self.text_str as usize).hash(state); self.smart_quote.hash(state); self.parbreak.hash(state); @@ -140,13 +140,15 @@ impl Hash for LangItems { #[doc(hidden)] pub static LANG_ITEMS: OnceCell<LangItems> = OnceCell::new(); -/// Set the lang items. This is a hack :( +/// Set the lang items. /// -/// Passing the lang items everywhere they are needed (especially the text node -/// related things) is very painful. By storing them globally, in theory, we -/// break incremental, but only when different sets of lang items are used in -/// the same program. For this reason, if this function is called multiple -/// times, the items must be the same. +/// This is a hack :( +/// +/// Passing the lang items everywhere they are needed (especially text related +/// things) is very painful. By storing them globally, in theory, we break +/// incremental, but only when different sets of lang items are used in the same +/// program. For this reason, if this function is called multiple times, the +/// items must be the same (and this is enforced). pub fn set_lang_items(items: LangItems) { if let Err(items) = LANG_ITEMS.set(items) { let first = hash128(LANG_ITEMS.get().unwrap()); diff --git a/src/eval/methods.rs b/src/eval/methods.rs index 324191ab..72245fb0 100644 --- a/src/eval/methods.rs +++ b/src/eval/methods.rs @@ -4,7 +4,7 @@ use ecow::EcoString; use super::{Args, Str, Value, Vm}; use crate::diag::{At, SourceResult}; -use crate::model::StableId; +use crate::model::Location; use crate::syntax::Span; /// Call a method on a value. @@ -71,12 +71,12 @@ pub fn call( }, Value::Content(content) => match method { - "func" => Value::Func(content.id().into()), + "func" => content.func().into(), "has" => Value::Bool(content.has(&args.expect::<EcoString>("field")?)), "at" => content.at(&args.expect::<EcoString>("field")?).at(span)?.clone(), - "id" => content - .stable_id() - .ok_or("this method can only be called on content returned by query()") + "location" => content + .location() + .ok_or("this method can only be called on content returned by query(..)") .at(span)? .into(), _ => return missing(), @@ -130,7 +130,16 @@ pub fn call( Value::Func(func) => match method { "with" => Value::Func(func.with(args.take())), - "where" => Value::dynamic(func.where_(&mut args).at(span)?), + "where" => { + let fields = args.to_named(); + args.items.retain(|arg| arg.name.is_none()); + Value::dynamic( + func.element() + .ok_or("`where()` can only be called on element functions") + .at(span)? + .where_(fields), + ) + } _ => return missing(), }, @@ -141,10 +150,10 @@ pub fn call( }, Value::Dyn(dynamic) => { - if let Some(&id) = dynamic.downcast::<StableId>() { + if let Some(&location) = dynamic.downcast::<Location>() { match method { - "page" => vm.vt.introspector.page(id).into(), - "location" => vm.vt.introspector.location(id).into(), + "page" => vm.vt.introspector.page(location).into(), + "position" => vm.vt.introspector.position(location).into(), _ => return missing(), } } else { @@ -263,7 +272,7 @@ pub fn methods_on(type_name: &str) -> &[(&'static str, bool)] { ("starts-with", true), ("trim", true), ], - "content" => &[("func", false), ("has", true), ("at", true), ("id", false)], + "content" => &[("func", false), ("has", true), ("at", true), ("location", false)], "array" => &[ ("all", true), ("any", true), @@ -299,7 +308,7 @@ pub fn methods_on(type_name: &str) -> &[(&'static str, bool)] { ], "function" => &[("where", true), ("with", true)], "arguments" => &[("named", false), ("pos", false)], - "stable id" => &[("page", false), ("location", false)], + "location" => &[("page", false), ("position", false)], "counter" => &[ ("display", true), ("at", true), diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 74c5f0b3..f19e4305 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -29,13 +29,14 @@ pub use self::cast::*; pub use self::dict::*; pub use self::func::*; pub use self::library::*; -pub use self::methods::*; pub use self::module::*; pub use self::scope::*; pub use self::str::*; pub use self::symbol::*; pub use self::value::*; +pub(crate) use self::methods::methods_on; + use std::collections::BTreeMap; use std::mem; use std::path::{Path, PathBuf}; @@ -47,11 +48,10 @@ use unicode_segmentation::UnicodeSegmentation; use crate::diag::{ bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint, }; -use crate::model::Introspector; -use crate::model::StabilityProvider; -use crate::model::Unlabellable; -use crate::model::Vt; -use crate::model::{Content, Label, Recipe, Selector, StyleMap, Transform}; +use crate::model::{ + Content, Introspector, Label, Recipe, Selector, StabilityProvider, Styles, Transform, + Unlabellable, Vt, +}; use crate::syntax::ast::AstNode; use crate::syntax::{ ast, parse_code, Source, SourceId, Span, Spanned, SyntaxKind, SyntaxNode, @@ -114,12 +114,12 @@ pub fn eval( /// /// Everything in the output is associated with the given `span`. #[comemo::memoize] -pub fn eval_code_str( +pub fn eval_string( world: Tracked<dyn World>, - text: &str, + code: &str, span: Span, ) -> SourceResult<Value> { - let mut root = parse_code(text); + let mut root = parse_code(code); root.synthesize(span); let errors = root.errors(); @@ -290,7 +290,7 @@ impl Route { } } -/// Traces which values existed for the expression with the given span. +/// Traces which values existed for the expression at a span. #[derive(Default, Clone)] pub struct Tracer { span: Option<Span>, @@ -377,10 +377,10 @@ fn eval_markup( } expr => match expr.eval(vm)? { Value::Label(label) => { - if let Some(node) = + if let Some(elem) = seq.iter_mut().rev().find(|node| !node.can::<dyn Unlabellable>()) { - *node = mem::take(node).labelled(label); + *elem = mem::take(elem).labelled(label); } } value => seq.push(value.display().spanned(expr.span())), @@ -643,7 +643,7 @@ impl Eval for ast::Math { Ok(Content::sequence( self.exprs() .map(|expr| expr.eval_display(vm)) - .collect::<SourceResult<_>>()?, + .collect::<SourceResult<Vec<_>>>()?, )) } } @@ -1049,7 +1049,7 @@ impl Eval for ast::FuncCall { if in_math && !matches!(callee, Value::Func(_)) { if let Value::Symbol(sym) = &callee { let c = sym.get(); - if let Some(accent) = combining_accent(c) { + if let Some(accent) = Symbol::combining_accent(c) { let base = args.expect("base")?; args.finish()?; return Ok(Value::Content((vm.items.math_accent)(base, accent))); @@ -1198,17 +1198,25 @@ impl Eval for ast::LetBinding { } impl Eval for ast::SetRule { - type Output = StyleMap; + type Output = Styles; fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> { if let Some(condition) = self.condition() { if !condition.eval(vm)?.cast::<bool>().at(condition.span())? { - return Ok(StyleMap::new()); + return Ok(Styles::new()); } } let target = self.target(); - let target = target.eval(vm)?.cast::<Func>().at(target.span())?; + let target = target + .eval(vm)? + .cast::<Func>() + .and_then(|func| { + func.element().ok_or_else(|| { + "only element functions can be used in set rules".into() + }) + }) + .at(target.span())?; let args = self.args().eval(vm)?; Ok(target.set(args)?.spanned(self.span())) } diff --git a/src/eval/scope.rs b/src/eval/scope.rs index d4338b5c..e241cac5 100644 --- a/src/eval/scope.rs +++ b/src/eval/scope.rs @@ -163,7 +163,9 @@ impl Slot { fn write(&mut self) -> StrResult<&mut Value> { match self.kind { Kind::Normal => Ok(&mut self.value), - Kind::Captured => Err("cannot mutate a captured variable")?, + Kind::Captured => { + Err("variables from outside the function are read-only and cannot be modified")? + } } } } diff --git a/src/eval/symbol.rs b/src/eval/symbol.rs index 73c41067..6a199a1d 100644 --- a/src/eval/symbol.rs +++ b/src/eval/symbol.rs @@ -1,94 +1,127 @@ use std::cmp::Reverse; use std::collections::BTreeSet; use std::fmt::{self, Debug, Display, Formatter, Write}; +use std::sync::Arc; -use ecow::{EcoString, EcoVec}; +use ecow::EcoString; use crate::diag::StrResult; #[doc(inline)] pub use typst_macros::symbols; -/// A symbol. +/// A symbol, possibly with variants. #[derive(Clone, Eq, PartialEq, Hash)] -pub struct Symbol { - repr: Repr, - modifiers: EcoString, -} +pub struct Symbol(Repr); -/// A collection of symbols. +/// The internal representation. #[derive(Clone, Eq, PartialEq, Hash)] enum Repr { Single(char), + Const(&'static [(&'static str, char)]), + Multi(Arc<(List, EcoString)>), +} + +/// A collection of symbols. +#[derive(Clone, Eq, PartialEq, Hash)] +enum List { Static(&'static [(&'static str, char)]), - Runtime(EcoVec<(EcoString, char)>), + Runtime(Box<[(EcoString, char)]>), } impl Symbol { /// Create a new symbol from a single character. pub const fn new(c: char) -> Self { - Self { repr: Repr::Single(c), modifiers: EcoString::new() } + Self(Repr::Single(c)) } /// Create a symbol with a static variant list. #[track_caller] pub const fn list(list: &'static [(&'static str, char)]) -> Self { debug_assert!(!list.is_empty()); - Self { - repr: Repr::Static(list), - modifiers: EcoString::new(), - } + Self(Repr::Const(list)) } /// Create a symbol with a runtime variant list. #[track_caller] - pub fn runtime(list: EcoVec<(EcoString, char)>) -> Self { + pub fn runtime(list: Box<[(EcoString, char)]>) -> Self { debug_assert!(!list.is_empty()); - Self { - repr: Repr::Runtime(list), - modifiers: EcoString::new(), - } + Self(Repr::Multi(Arc::new((List::Runtime(list), EcoString::new())))) } /// Get the symbol's text. pub fn get(&self) -> char { - match self.repr { - Repr::Single(c) => c, - _ => find(self.variants(), &self.modifiers).unwrap(), + match &self.0 { + Repr::Single(c) => *c, + Repr::Const(_) => find(self.variants(), "").unwrap(), + Repr::Multi(arc) => find(self.variants(), &arc.1).unwrap(), } } /// Apply a modifier to the symbol. pub fn modified(mut self, modifier: &str) -> StrResult<Self> { - if !self.modifiers.is_empty() { - self.modifiers.push('.'); + if let Repr::Const(list) = self.0 { + self.0 = Repr::Multi(Arc::new((List::Static(list), EcoString::new()))); } - self.modifiers.push_str(modifier); - if find(self.variants(), &self.modifiers).is_none() { - Err("unknown modifier")? + + if let Repr::Multi(arc) = &mut self.0 { + let (list, modifiers) = Arc::make_mut(arc); + if !modifiers.is_empty() { + modifiers.push('.'); + } + modifiers.push_str(modifier); + if find(list.variants(), &modifiers).is_some() { + return Ok(self); + } } - Ok(self) + + Err("unknown symbol modifier".into()) } /// The characters that are covered by this symbol. pub fn variants(&self) -> impl Iterator<Item = (&str, char)> { - match &self.repr { + match &self.0 { Repr::Single(c) => Variants::Single(Some(*c).into_iter()), - Repr::Static(list) => Variants::Static(list.iter()), - Repr::Runtime(list) => Variants::Runtime(list.iter()), + Repr::Const(list) => Variants::Static(list.iter()), + Repr::Multi(arc) => arc.0.variants(), } } /// Possible modifiers. pub fn modifiers(&self) -> impl Iterator<Item = &str> + '_ { let mut set = BTreeSet::new(); + let modifiers = match &self.0 { + Repr::Multi(arc) => arc.1.as_str(), + _ => "", + }; for modifier in self.variants().flat_map(|(name, _)| name.split('.')) { - if !modifier.is_empty() && !contained(&self.modifiers, modifier) { + if !modifier.is_empty() && !contained(modifiers, modifier) { set.insert(modifier); } } set.into_iter() } + + /// Normalize an accent to a combining one. + pub fn combining_accent(c: char) -> Option<char> { + Some(match c { + '\u{0300}' | '`' => '\u{0300}', + '\u{0301}' | '´' => '\u{0301}', + '\u{0302}' | '^' | 'ˆ' => '\u{0302}', + '\u{0303}' | '~' | '∼' | '˜' => '\u{0303}', + '\u{0304}' | '¯' => '\u{0304}', + '\u{0305}' | '-' | '‾' | '−' => '\u{0305}', + '\u{0306}' | '˘' => '\u{0306}', + '\u{0307}' | '.' | '˙' | '⋅' => '\u{0307}', + '\u{0308}' | '¨' => '\u{0308}', + '\u{030a}' | '∘' | '○' => '\u{030a}', + '\u{030b}' | '˝' => '\u{030b}', + '\u{030c}' | 'ˇ' => '\u{030c}', + '\u{20d6}' | '←' => '\u{20d6}', + '\u{20d7}' | '→' | '⟶' => '\u{20d7}', + _ => return None, + }) + } } impl Debug for Symbol { @@ -103,6 +136,16 @@ impl Display for Symbol { } } +impl List { + /// The characters that are covered by this list. + fn variants(&self) -> Variants<'_> { + match self { + List::Static(list) => Variants::Static(list.iter()), + List::Runtime(list) => Variants::Runtime(list.iter()), + } + } +} + /// Iterator over variants. enum Variants<'a> { Single(std::option::IntoIter<char>), @@ -166,24 +209,3 @@ fn parts(modifiers: &str) -> impl Iterator<Item = &str> { fn contained(modifiers: &str, m: &str) -> bool { parts(modifiers).any(|part| part == m) } - -/// Normalize an accent to a combining one. -pub fn combining_accent(c: char) -> Option<char> { - Some(match c { - '\u{0300}' | '`' => '\u{0300}', - '\u{0301}' | '´' => '\u{0301}', - '\u{0302}' | '^' | 'ˆ' => '\u{0302}', - '\u{0303}' | '~' | '∼' | '˜' => '\u{0303}', - '\u{0304}' | '¯' => '\u{0304}', - '\u{0305}' | '-' | '‾' | '−' => '\u{0305}', - '\u{0306}' | '˘' => '\u{0306}', - '\u{0307}' | '.' | '˙' | '⋅' => '\u{0307}', - '\u{0308}' | '¨' => '\u{0308}', - '\u{030a}' | '∘' | '○' => '\u{030a}', - '\u{030b}' | '˝' => '\u{030b}', - '\u{030c}' | 'ˇ' => '\u{030c}', - '\u{20d6}' | '←' => '\u{20d6}', - '\u{20d7}' | '→' | '⟶' => '\u{20d7}', - _ => return None, - }) -} diff --git a/src/eval/value.rs b/src/eval/value.rs index 61af36f5..ce9c4e0e 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -13,6 +13,7 @@ use super::{ }; use crate::diag::StrResult; use crate::geom::{Abs, Angle, Color, Em, Fr, Length, Ratio, Rel}; +use crate::model::Styles; use crate::syntax::{ast, Span}; /// A computational value. @@ -48,6 +49,8 @@ pub enum Value { Label(Label), /// A content value: `[*Hi* there]`. Content(Content), + // Content styles. + Styles(Styles), /// An array of values: `(1, "hi", 12cm)`. Array(Array), /// A dictionary value: `(color: #f79143, pattern: dashed)`. @@ -101,6 +104,7 @@ impl Value { Self::Str(_) => Str::TYPE_NAME, Self::Label(_) => Label::TYPE_NAME, Self::Content(_) => Content::TYPE_NAME, + Self::Styles(_) => Styles::TYPE_NAME, Self::Array(_) => Array::TYPE_NAME, Self::Dict(_) => Dict::TYPE_NAME, Self::Func(_) => Func::TYPE_NAME, @@ -120,7 +124,7 @@ impl Value { match self { Self::Symbol(symbol) => symbol.clone().modified(&field).map(Self::Symbol), Self::Dict(dict) => dict.at(&field).cloned(), - Self::Content(content) => content.at(&field).cloned(), + Self::Content(content) => content.at(&field), Self::Module(module) => module.get(&field).cloned(), v => Err(eco_format!("cannot access fields on type {}", v.type_name())), } @@ -188,6 +192,7 @@ impl Debug for Value { Self::Str(v) => Debug::fmt(v, f), Self::Label(v) => Debug::fmt(v, f), Self::Content(v) => Debug::fmt(v, f), + Self::Styles(v) => Debug::fmt(v, f), Self::Array(v) => Debug::fmt(v, f), Self::Dict(v) => Debug::fmt(v, f), Self::Func(v) => Debug::fmt(v, f), @@ -229,6 +234,7 @@ impl Hash for Value { Self::Str(v) => v.hash(state), Self::Label(v) => v.hash(state), Self::Content(v) => v.hash(state), + Self::Styles(v) => v.hash(state), Self::Array(v) => v.hash(state), Self::Dict(v) => v.hash(state), Self::Func(v) => v.hash(state), @@ -400,6 +406,7 @@ primitive! { Content: "content", Symbol(v) => item!(text)(v.get().into()), Str(v) => item!(text)(v.into()) } +primitive! { Styles: "styles", Styles } primitive! { Array: "array", Array } primitive! { Dict: "dictionary", Dict } primitive! { Func: "function", Func } |
