summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/array.rs14
-rw-r--r--src/eval/func.rs69
-rw-r--r--src/eval/library.rs26
-rw-r--r--src/eval/methods.rs31
-rw-r--r--src/eval/mod.rs42
-rw-r--r--src/eval/scope.rs4
-rw-r--r--src/eval/symbol.rs126
-rw-r--r--src/eval/value.rs9
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 }