mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Rename table to dict ✏
This commit is contained in:
parent
885bfec5d7
commit
aafd3c95ca
@ -6,21 +6,21 @@ use std::ops::Index;
|
|||||||
|
|
||||||
use crate::syntax::{Span, Spanned};
|
use crate::syntax::{Span, Spanned};
|
||||||
|
|
||||||
/// A table data structure, which maps from integers (`u64`) or strings to a
|
/// A dictionary data structure, which maps from integers (`u64`) or strings to
|
||||||
/// generic value type.
|
/// a generic value type.
|
||||||
///
|
///
|
||||||
/// The table can be used to model arrays by assigns values to successive
|
/// The dictionary can be used to model arrays by assigning values to successive
|
||||||
/// indices from `0..n`. The table type offers special support for this pattern
|
/// indices from `0..n`. The `push` method offers special support for this
|
||||||
/// through the `push` method.
|
/// pattern.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Table<V> {
|
pub struct Dict<V> {
|
||||||
nums: BTreeMap<u64, V>,
|
nums: BTreeMap<u64, V>,
|
||||||
strs: BTreeMap<String, V>,
|
strs: BTreeMap<String, V>,
|
||||||
lowest_free: u64,
|
lowest_free: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V> Table<V> {
|
impl<V> Dict<V> {
|
||||||
/// Create a new empty table.
|
/// Create a new empty dictionary.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
nums: BTreeMap::new(),
|
nums: BTreeMap::new(),
|
||||||
@ -29,12 +29,12 @@ impl<V> Table<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The total number of entries in the table.
|
/// The total number of entries in the dictionary.
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.nums.len() + self.strs.len()
|
self.nums.len() + self.strs.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the table contains no entries.
|
/// Whether the dictionary contains no entries.
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.len() == 0
|
self.len() == 0
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ impl<V> Table<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a value into the table.
|
/// Insert a value into the dictionary.
|
||||||
pub fn insert<K>(&mut self, key: K, value: V)
|
pub fn insert<K>(&mut self, key: K, value: V)
|
||||||
where
|
where
|
||||||
K: Into<OwnedKey>,
|
K: Into<OwnedKey>,
|
||||||
@ -89,7 +89,7 @@ impl<V> Table<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the value with the given key from the table.
|
/// Remove the value with the given key from the dictionary.
|
||||||
pub fn remove<'a, K>(&mut self, key: K) -> Option<V>
|
pub fn remove<'a, K>(&mut self, key: K) -> Option<V>
|
||||||
where
|
where
|
||||||
K: Into<BorrowedKey<'a>>,
|
K: Into<BorrowedKey<'a>>,
|
||||||
@ -103,7 +103,7 @@ impl<V> Table<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Append a value to the table.
|
/// Append a value to the dictionary.
|
||||||
///
|
///
|
||||||
/// This will associate the `value` with the lowest free number key (zero if
|
/// This will associate the `value` with the lowest free number key (zero if
|
||||||
/// there is no number key so far).
|
/// there is no number key so far).
|
||||||
@ -122,7 +122,7 @@ impl<V> Table<V> {
|
|||||||
.chain(self.strs().map(|(k, v)| (BorrowedKey::Str(k), v)))
|
.chain(self.strs().map(|(k, v)| (BorrowedKey::Str(k), v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all values in the table.
|
/// Iterate over all values in the dictionary.
|
||||||
pub fn values(&self) -> impl Iterator<Item = &V> {
|
pub fn values(&self) -> impl Iterator<Item = &V> {
|
||||||
self.nums().map(|(_, v)| v).chain(self.strs().map(|(_, v)| v))
|
self.nums().map(|(_, v)| v).chain(self.strs().map(|(_, v)| v))
|
||||||
}
|
}
|
||||||
@ -145,7 +145,7 @@ impl<V> Table<V> {
|
|||||||
.chain(self.strs.into_iter().map(|(k, v)| (OwnedKey::Str(k), v)))
|
.chain(self.strs.into_iter().map(|(k, v)| (OwnedKey::Str(k), v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move into an owned iterator over all values in the table.
|
/// Move into an owned iterator over all values in the dictionary.
|
||||||
pub fn into_values(self) -> impl Iterator<Item = V> {
|
pub fn into_values(self) -> impl Iterator<Item = V> {
|
||||||
self.nums
|
self.nums
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -164,32 +164,32 @@ impl<V> Table<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, K, V> Index<K> for Table<V>
|
impl<'a, K, V> Index<K> for Dict<V>
|
||||||
where
|
where
|
||||||
K: Into<BorrowedKey<'a>>,
|
K: Into<BorrowedKey<'a>>,
|
||||||
{
|
{
|
||||||
type Output = V;
|
type Output = V;
|
||||||
|
|
||||||
fn index(&self, index: K) -> &Self::Output {
|
fn index(&self, index: K) -> &Self::Output {
|
||||||
self.get(index).expect("key not in table")
|
self.get(index).expect("key not in dict")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V> Default for Table<V> {
|
impl<V> Default for Dict<V> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Eq> Eq for Table<V> {}
|
impl<V: Eq> Eq for Dict<V> {}
|
||||||
|
|
||||||
impl<V: PartialEq> PartialEq for Table<V> {
|
impl<V: PartialEq> PartialEq for Dict<V> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.iter().eq(other.iter())
|
self.iter().eq(other.iter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Debug> Debug for Table<V> {
|
impl<V: Debug> Debug for Dict<V> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
return f.write_str("()");
|
return f.write_str("()");
|
||||||
@ -228,7 +228,7 @@ impl<V: Debug> Debug for Table<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The owned variant of a table key.
|
/// The owned variant of a dictionary key.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub enum OwnedKey {
|
pub enum OwnedKey {
|
||||||
Num(u64),
|
Num(u64),
|
||||||
@ -262,7 +262,7 @@ impl From<&'static str> for OwnedKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The borrowed variant of a table key.
|
/// The borrowed variant of a dictionary key.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub enum BorrowedKey<'a> {
|
pub enum BorrowedKey<'a> {
|
||||||
Num(u64),
|
Num(u64),
|
||||||
@ -287,7 +287,7 @@ impl<'a> From<&'a str> for BorrowedKey<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An table entry which tracks key and value span.
|
/// A dictionary entry which tracks key and value span.
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct SpannedEntry<V> {
|
pub struct SpannedEntry<V> {
|
||||||
pub key: Span,
|
pub key: Span,
|
||||||
@ -329,78 +329,78 @@ impl<V: Debug> Debug for SpannedEntry<V> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Table;
|
use super::Dict;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_table_different_key_types_dont_interfere() {
|
fn test_dict_different_key_types_dont_interfere() {
|
||||||
let mut table = Table::new();
|
let mut dict = Dict::new();
|
||||||
table.insert(10, "hello");
|
dict.insert(10, "hello");
|
||||||
table.insert("twenty", "there");
|
dict.insert("twenty", "there");
|
||||||
assert_eq!(table.len(), 2);
|
assert_eq!(dict.len(), 2);
|
||||||
assert_eq!(table[10], "hello");
|
assert_eq!(dict[10], "hello");
|
||||||
assert_eq!(table["twenty"], "there");
|
assert_eq!(dict["twenty"], "there");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_table_push_skips_already_inserted_keys() {
|
fn test_dict_push_skips_already_inserted_keys() {
|
||||||
let mut table = Table::new();
|
let mut dict = Dict::new();
|
||||||
table.insert(2, "2");
|
dict.insert(2, "2");
|
||||||
table.push("0");
|
dict.push("0");
|
||||||
table.insert(3, "3");
|
dict.insert(3, "3");
|
||||||
table.push("1");
|
dict.push("1");
|
||||||
table.push("4");
|
dict.push("4");
|
||||||
assert_eq!(table.len(), 5);
|
assert_eq!(dict.len(), 5);
|
||||||
assert_eq!(table[0], "0");
|
assert_eq!(dict[0], "0");
|
||||||
assert_eq!(table[1], "1");
|
assert_eq!(dict[1], "1");
|
||||||
assert_eq!(table[2], "2");
|
assert_eq!(dict[2], "2");
|
||||||
assert_eq!(table[3], "3");
|
assert_eq!(dict[3], "3");
|
||||||
assert_eq!(table[4], "4");
|
assert_eq!(dict[4], "4");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_table_push_remove_push_reuses_index() {
|
fn test_dict_push_remove_push_reuses_index() {
|
||||||
let mut table = Table::new();
|
let mut dict = Dict::new();
|
||||||
table.push("0");
|
dict.push("0");
|
||||||
table.push("1");
|
dict.push("1");
|
||||||
table.push("2");
|
dict.push("2");
|
||||||
table.remove(1);
|
dict.remove(1);
|
||||||
table.push("a");
|
dict.push("a");
|
||||||
table.push("3");
|
dict.push("3");
|
||||||
assert_eq!(table.len(), 4);
|
assert_eq!(dict.len(), 4);
|
||||||
assert_eq!(table[0], "0");
|
assert_eq!(dict[0], "0");
|
||||||
assert_eq!(table[1], "a");
|
assert_eq!(dict[1], "a");
|
||||||
assert_eq!(table[2], "2");
|
assert_eq!(dict[2], "2");
|
||||||
assert_eq!(table[3], "3");
|
assert_eq!(dict[3], "3");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_table_first_and_last_are_correct() {
|
fn test_dict_first_and_last_are_correct() {
|
||||||
let mut table = Table::new();
|
let mut dict = Dict::new();
|
||||||
assert_eq!(table.first(), None);
|
assert_eq!(dict.first(), None);
|
||||||
assert_eq!(table.last(), None);
|
assert_eq!(dict.last(), None);
|
||||||
table.insert(4, "hi");
|
dict.insert(4, "hi");
|
||||||
table.insert("string", "hi");
|
dict.insert("string", "hi");
|
||||||
assert_eq!(table.first(), Some((4, &"hi")));
|
assert_eq!(dict.first(), Some((4, &"hi")));
|
||||||
assert_eq!(table.last(), Some((4, &"hi")));
|
assert_eq!(dict.last(), Some((4, &"hi")));
|
||||||
table.insert(2, "bye");
|
dict.insert(2, "bye");
|
||||||
assert_eq!(table.first(), Some((2, &"bye")));
|
assert_eq!(dict.first(), Some((2, &"bye")));
|
||||||
assert_eq!(table.last(), Some((4, &"hi")));
|
assert_eq!(dict.last(), Some((4, &"hi")));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_table_format_debug() {
|
fn test_dict_format_debug() {
|
||||||
let mut table = Table::new();
|
let mut dict = Dict::new();
|
||||||
assert_eq!(format!("{:?}", table), "()");
|
assert_eq!(format!("{:?}", dict), "()");
|
||||||
assert_eq!(format!("{:#?}", table), "()");
|
assert_eq!(format!("{:#?}", dict), "()");
|
||||||
|
|
||||||
table.insert(10, "hello");
|
dict.insert(10, "hello");
|
||||||
table.insert("twenty", "there");
|
dict.insert("twenty", "there");
|
||||||
table.insert("sp ace", "quotes");
|
dict.insert("sp ace", "quotes");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{:?}", table),
|
format!("{:?}", dict),
|
||||||
r#"(10="hello", "sp ace"="quotes", twenty="there")"#,
|
r#"(10="hello", "sp ace"="quotes", twenty="there")"#,
|
||||||
);
|
);
|
||||||
assert_eq!(format!("{:#?}", table).lines().collect::<Vec<_>>(), [
|
assert_eq!(format!("{:#?}", dict).lines().collect::<Vec<_>>(), [
|
||||||
"(",
|
"(",
|
||||||
r#" 10 = "hello","#,
|
r#" 10 = "hello","#,
|
||||||
r#" "sp ace" = "quotes","#,
|
r#" "sp ace" = "quotes","#,
|
@ -1,5 +1,5 @@
|
|||||||
//! Building blocks for the computational part.
|
//! Building blocks for the computational part.
|
||||||
|
|
||||||
|
pub mod dict;
|
||||||
pub mod scope;
|
pub mod scope;
|
||||||
pub mod table;
|
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
@ -6,7 +6,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use fontdock::{FontStretch, FontStyle, FontWeight};
|
use fontdock::{FontStretch, FontStyle, FontWeight};
|
||||||
|
|
||||||
use super::table::{SpannedEntry, Table};
|
use super::dict::{Dict, SpannedEntry};
|
||||||
use crate::color::RgbaColor;
|
use crate::color::RgbaColor;
|
||||||
use crate::layout::{Command, Commands, Dir, LayoutContext, SpecAlign};
|
use crate::layout::{Command, Commands, Dir, LayoutContext, SpecAlign};
|
||||||
use crate::length::{Length, ScaleLength};
|
use crate::length::{Length, ScaleLength};
|
||||||
@ -29,8 +29,8 @@ pub enum Value {
|
|||||||
Length(Length),
|
Length(Length),
|
||||||
/// A color value with alpha channel: `#f79143ff`.
|
/// A color value with alpha channel: `#f79143ff`.
|
||||||
Color(RgbaColor),
|
Color(RgbaColor),
|
||||||
/// A table value: `(false, 12cm, greeting="hi")`.
|
/// A dictionary value: `(false, 12cm, greeting="hi")`.
|
||||||
Table(TableValue),
|
Dict(DictValue),
|
||||||
/// A syntax tree containing typesetting content.
|
/// A syntax tree containing typesetting content.
|
||||||
Tree(SyntaxTree),
|
Tree(SyntaxTree),
|
||||||
/// An executable function.
|
/// An executable function.
|
||||||
@ -51,7 +51,7 @@ impl Value {
|
|||||||
Number(_) => "number",
|
Number(_) => "number",
|
||||||
Length(_) => "length",
|
Length(_) => "length",
|
||||||
Color(_) => "color",
|
Color(_) => "color",
|
||||||
Table(_) => "table",
|
Dict(_) => "dict",
|
||||||
Tree(_) => "syntax tree",
|
Tree(_) => "syntax tree",
|
||||||
Func(_) => "function",
|
Func(_) => "function",
|
||||||
Commands(_) => "commands",
|
Commands(_) => "commands",
|
||||||
@ -70,10 +70,10 @@ impl Spanned<Value> {
|
|||||||
Value::Tree(tree) => vec![Command::LayoutSyntaxTree(tree)],
|
Value::Tree(tree) => vec![Command::LayoutSyntaxTree(tree)],
|
||||||
|
|
||||||
// Forward to each entry, separated with spaces.
|
// Forward to each entry, separated with spaces.
|
||||||
Value::Table(table) => {
|
Value::Dict(dict) => {
|
||||||
let mut commands = vec![];
|
let mut commands = vec![];
|
||||||
let mut end = None;
|
let mut end = None;
|
||||||
for entry in table.into_values() {
|
for entry in dict.into_values() {
|
||||||
if let Some(last_end) = end {
|
if let Some(last_end) = end {
|
||||||
let span = Span::new(last_end, entry.key.start);
|
let span = Span::new(last_end, entry.key.start);
|
||||||
let tree = vec![SyntaxNode::Spacing.span_with(span)];
|
let tree = vec![SyntaxNode::Spacing.span_with(span)];
|
||||||
@ -106,7 +106,7 @@ impl Debug for Value {
|
|||||||
Number(n) => n.fmt(f),
|
Number(n) => n.fmt(f),
|
||||||
Length(s) => s.fmt(f),
|
Length(s) => s.fmt(f),
|
||||||
Color(c) => c.fmt(f),
|
Color(c) => c.fmt(f),
|
||||||
Table(t) => t.fmt(f),
|
Dict(t) => t.fmt(f),
|
||||||
Tree(t) => t.fmt(f),
|
Tree(t) => t.fmt(f),
|
||||||
Func(_) => f.pad("<function>"),
|
Func(_) => f.pad("<function>"),
|
||||||
Commands(c) => c.fmt(f),
|
Commands(c) => c.fmt(f),
|
||||||
@ -124,7 +124,7 @@ impl PartialEq for Value {
|
|||||||
(Number(a), Number(b)) => a == b,
|
(Number(a), Number(b)) => a == b,
|
||||||
(Length(a), Length(b)) => a == b,
|
(Length(a), Length(b)) => a == b,
|
||||||
(Color(a), Color(b)) => a == b,
|
(Color(a), Color(b)) => a == b,
|
||||||
(Table(a), Table(b)) => a == b,
|
(Dict(a), Dict(b)) => a == b,
|
||||||
(Tree(a), Tree(b)) => a == b,
|
(Tree(a), Tree(b)) => a == b,
|
||||||
(Func(a), Func(b)) => Rc::ptr_eq(a, b),
|
(Func(a), Func(b)) => Rc::ptr_eq(a, b),
|
||||||
(Commands(a), Commands(b)) => a == b,
|
(Commands(a), Commands(b)) => a == b,
|
||||||
@ -135,7 +135,7 @@ impl PartialEq for Value {
|
|||||||
|
|
||||||
/// An executable function value.
|
/// An executable function value.
|
||||||
///
|
///
|
||||||
/// The first argument is a table containing the arguments passed to the
|
/// The first argument is a dictionary containing the arguments passed to the
|
||||||
/// function. The function may be asynchronous (as such it returns a dynamic
|
/// function. The function may be asynchronous (as such it returns a dynamic
|
||||||
/// future) and it may emit diagnostics, which are contained in the returned
|
/// future) and it may emit diagnostics, which are contained in the returned
|
||||||
/// `Pass`. In the end, the function must evaluate to `Value`. Your typical
|
/// `Pass`. In the end, the function must evaluate to `Value`. Your typical
|
||||||
@ -144,17 +144,17 @@ impl PartialEq for Value {
|
|||||||
///
|
///
|
||||||
/// The dynamic function object is wrapped in an `Rc` to keep `Value` clonable.
|
/// The dynamic function object is wrapped in an `Rc` to keep `Value` clonable.
|
||||||
pub type FuncValue =
|
pub type FuncValue =
|
||||||
Rc<dyn Fn(Span, TableValue, LayoutContext<'_>) -> DynFuture<Pass<Value>>>;
|
Rc<dyn Fn(Span, DictValue, LayoutContext<'_>) -> DynFuture<Pass<Value>>>;
|
||||||
|
|
||||||
/// A table of values.
|
/// A dictionary of values.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```typst
|
/// ```typst
|
||||||
/// (false, 12cm, greeting="hi")
|
/// (false, 12cm, greeting="hi")
|
||||||
/// ```
|
/// ```
|
||||||
pub type TableValue = Table<SpannedEntry<Value>>;
|
pub type DictValue = Dict<SpannedEntry<Value>>;
|
||||||
|
|
||||||
impl TableValue {
|
impl DictValue {
|
||||||
/// Retrieve and remove the matching value with the lowest number key,
|
/// Retrieve and remove the matching value with the lowest number key,
|
||||||
/// skipping and ignoring all non-matching entries with lower keys.
|
/// skipping and ignoring all non-matching entries with lower keys.
|
||||||
pub fn take<T: TryFromValue>(&mut self) -> Option<T> {
|
pub fn take<T: TryFromValue>(&mut self) -> Option<T> {
|
||||||
@ -341,7 +341,7 @@ impl_match!(bool, "bool", &Value::Bool(b) => b);
|
|||||||
impl_match!(f64, "number", &Value::Number(n) => n);
|
impl_match!(f64, "number", &Value::Number(n) => n);
|
||||||
impl_match!(Length, "length", &Value::Length(l) => l);
|
impl_match!(Length, "length", &Value::Length(l) => l);
|
||||||
impl_match!(SyntaxTree, "tree", Value::Tree(t) => t.clone());
|
impl_match!(SyntaxTree, "tree", Value::Tree(t) => t.clone());
|
||||||
impl_match!(TableValue, "table", Value::Table(t) => t.clone());
|
impl_match!(DictValue, "dict", Value::Dict(t) => t.clone());
|
||||||
impl_match!(FuncValue, "function", Value::Func(f) => f.clone());
|
impl_match!(FuncValue, "function", Value::Func(f) => f.clone());
|
||||||
impl_match!(ScaleLength, "number or length",
|
impl_match!(ScaleLength, "number or length",
|
||||||
&Value::Length(length) => ScaleLength::Absolute(length),
|
&Value::Length(length) => ScaleLength::Absolute(length),
|
||||||
@ -437,60 +437,60 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_table_take_removes_correct_entry() {
|
fn test_dict_take_removes_correct_entry() {
|
||||||
let mut table = Table::new();
|
let mut dict = Dict::new();
|
||||||
table.insert(1, entry(Value::Bool(false)));
|
dict.insert(1, entry(Value::Bool(false)));
|
||||||
table.insert(2, entry(Value::Str("hi".to_string())));
|
dict.insert(2, entry(Value::Str("hi".to_string())));
|
||||||
assert_eq!(table.take::<String>(), Some("hi".to_string()));
|
assert_eq!(dict.take::<String>(), Some("hi".to_string()));
|
||||||
assert_eq!(table.len(), 1);
|
assert_eq!(dict.len(), 1);
|
||||||
assert_eq!(table.take::<bool>(), Some(false));
|
assert_eq!(dict.take::<bool>(), Some(false));
|
||||||
assert!(table.is_empty());
|
assert!(dict.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_table_expect_errors_about_previous_entries() {
|
fn test_dict_expect_errors_about_previous_entries() {
|
||||||
let mut f = Feedback::new();
|
let mut f = Feedback::new();
|
||||||
let mut table = Table::new();
|
let mut dict = Dict::new();
|
||||||
table.insert(1, entry(Value::Bool(false)));
|
dict.insert(1, entry(Value::Bool(false)));
|
||||||
table.insert(3, entry(Value::Str("hi".to_string())));
|
dict.insert(3, entry(Value::Str("hi".to_string())));
|
||||||
table.insert(5, entry(Value::Bool(true)));
|
dict.insert(5, entry(Value::Bool(true)));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
table.expect::<String>("", Span::ZERO, &mut f),
|
dict.expect::<String>("", Span::ZERO, &mut f),
|
||||||
Some("hi".to_string())
|
Some("hi".to_string())
|
||||||
);
|
);
|
||||||
assert_eq!(f.diagnostics, [error!(
|
assert_eq!(f.diagnostics, [error!(
|
||||||
Span::ZERO,
|
Span::ZERO,
|
||||||
"expected string, found bool"
|
"expected string, found bool"
|
||||||
)]);
|
)]);
|
||||||
assert_eq!(table.len(), 1);
|
assert_eq!(dict.len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_table_take_with_key_removes_the_entry() {
|
fn test_dict_take_with_key_removes_the_entry() {
|
||||||
let mut f = Feedback::new();
|
let mut f = Feedback::new();
|
||||||
let mut table = Table::new();
|
let mut dict = Dict::new();
|
||||||
table.insert(1, entry(Value::Bool(false)));
|
dict.insert(1, entry(Value::Bool(false)));
|
||||||
table.insert("hi", entry(Value::Bool(true)));
|
dict.insert("hi", entry(Value::Bool(true)));
|
||||||
assert_eq!(table.take::<bool>(), Some(false));
|
assert_eq!(dict.take::<bool>(), Some(false));
|
||||||
assert_eq!(table.take_key::<f64>("hi", &mut f), None);
|
assert_eq!(dict.take_key::<f64>("hi", &mut f), None);
|
||||||
assert_eq!(f.diagnostics, [error!(
|
assert_eq!(f.diagnostics, [error!(
|
||||||
Span::ZERO,
|
Span::ZERO,
|
||||||
"expected number, found bool"
|
"expected number, found bool"
|
||||||
)]);
|
)]);
|
||||||
assert!(table.is_empty());
|
assert!(dict.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_table_take_all_removes_the_correct_entries() {
|
fn test_dict_take_all_removes_the_correct_entries() {
|
||||||
let mut table = Table::new();
|
let mut dict = Dict::new();
|
||||||
table.insert(1, entry(Value::Bool(false)));
|
dict.insert(1, entry(Value::Bool(false)));
|
||||||
table.insert(3, entry(Value::Number(0.0)));
|
dict.insert(3, entry(Value::Number(0.0)));
|
||||||
table.insert(7, entry(Value::Bool(true)));
|
dict.insert(7, entry(Value::Bool(true)));
|
||||||
assert_eq!(table.take_all_num::<bool>().collect::<Vec<_>>(), [
|
assert_eq!(dict.take_all_num::<bool>().collect::<Vec<_>>(), [
|
||||||
(1, false),
|
(1, false),
|
||||||
(7, true)
|
(7, true)
|
||||||
],);
|
],);
|
||||||
assert_eq!(table.len(), 1);
|
assert_eq!(dict.len(), 1);
|
||||||
assert_eq!(table[3].val.v, Value::Number(0.0));
|
assert_eq!(dict[3].val.v, Value::Number(0.0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ use super::*;
|
|||||||
/// - `vertical`: Any of `top`, `bottom` or `center`.
|
/// - `vertical`: Any of `top`, `bottom` or `center`.
|
||||||
///
|
///
|
||||||
/// There may not be two alignment specifications for the same axis.
|
/// There may not be two alignment specifications for the same axis.
|
||||||
pub async fn align(_: Span, mut args: TableValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
pub async fn align(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
||||||
let mut f = Feedback::new();
|
let mut f = Feedback::new();
|
||||||
|
|
||||||
let content = args.take::<SyntaxTree>();
|
let content = args.take::<SyntaxTree>();
|
||||||
|
@ -8,7 +8,7 @@ use crate::length::ScaleLength;
|
|||||||
/// - `height`: The height of the box (length of relative to parent's height).
|
/// - `height`: The height of the box (length of relative to parent's height).
|
||||||
pub async fn boxed(
|
pub async fn boxed(
|
||||||
_: Span,
|
_: Span,
|
||||||
mut args: TableValue,
|
mut args: DictValue,
|
||||||
mut ctx: LayoutContext<'_>,
|
mut ctx: LayoutContext<'_>,
|
||||||
) -> Pass<Value> {
|
) -> Pass<Value> {
|
||||||
let mut f = Feedback::new();
|
let mut f = Feedback::new();
|
||||||
|
@ -2,7 +2,7 @@ use super::*;
|
|||||||
use crate::color::RgbaColor;
|
use crate::color::RgbaColor;
|
||||||
|
|
||||||
/// `rgb`: Create an RGB(A) color.
|
/// `rgb`: Create an RGB(A) color.
|
||||||
pub async fn rgb(span: Span, mut args: TableValue, _: LayoutContext<'_>) -> Pass<Value> {
|
pub async fn rgb(span: Span, mut args: DictValue, _: LayoutContext<'_>) -> Pass<Value> {
|
||||||
let mut f = Feedback::new();
|
let mut f = Feedback::new();
|
||||||
|
|
||||||
let r = args.expect::<Spanned<f64>>("red value", span, &mut f);
|
let r = args.expect::<Spanned<f64>>("red value", span, &mut f);
|
||||||
|
@ -12,13 +12,18 @@ use crate::length::ScaleLength;
|
|||||||
/// # Keyword arguments
|
/// # Keyword arguments
|
||||||
/// - `style`: `normal`, `italic` or `oblique`.
|
/// - `style`: `normal`, `italic` or `oblique`.
|
||||||
/// - `weight`: `100` - `900` or a name like `thin`.
|
/// - `weight`: `100` - `900` or a name like `thin`.
|
||||||
/// - `width`: `normal`, `condensed`, `expanded`, ...
|
/// - `width`: `1` - `9` or a name like `condensed`.
|
||||||
/// - Any other keyword argument whose value is a table of strings is a class
|
/// - Any other keyword argument whose value is a dictionary of strings defines
|
||||||
/// fallback definition like:
|
/// a fallback class, for example:
|
||||||
/// ```typst
|
/// ```typst
|
||||||
/// serif = ("Source Serif Pro", "Noto Serif")
|
/// [font: serif = ("Source Serif Pro", "Noto Serif")]
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn font(_: Span, mut args: TableValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
/// This class can be used in the fallback list or other fallback classes as
|
||||||
|
/// long as the resulting fallback tree is acylic.
|
||||||
|
/// ```typst
|
||||||
|
/// [font: "My Serif", serif]
|
||||||
|
/// ```
|
||||||
|
pub async fn font(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
||||||
let mut f = Feedback::new();
|
let mut f = Feedback::new();
|
||||||
let mut text = ctx.style.text.clone();
|
let mut text = ctx.style.text.clone();
|
||||||
let mut updated_fallback = false;
|
let mut updated_fallback = false;
|
||||||
@ -57,8 +62,8 @@ pub async fn font(_: Span, mut args: TableValue, ctx: LayoutContext<'_>) -> Pass
|
|||||||
text.variant.stretch = stretch;
|
text.variant.stretch = stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (class, mut table) in args.take_all_str::<TableValue>() {
|
for (class, mut dict) in args.take_all_str::<DictValue>() {
|
||||||
let fallback = table
|
let fallback = dict
|
||||||
.take_all_num_vals::<StringLike>()
|
.take_all_num_vals::<StringLike>()
|
||||||
.map(|s| s.to_lowercase())
|
.map(|s| s.to_lowercase())
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -16,7 +16,7 @@ use crate::paper::{Paper, PaperClass};
|
|||||||
/// - `top`: The top margin (length or relative to height).
|
/// - `top`: The top margin (length or relative to height).
|
||||||
/// - `bottom`: The bottom margin (length or relative to height).
|
/// - `bottom`: The bottom margin (length or relative to height).
|
||||||
/// - `flip`: Flips custom or paper-defined width and height (boolean).
|
/// - `flip`: Flips custom or paper-defined width and height (boolean).
|
||||||
pub async fn page(_: Span, mut args: TableValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
pub async fn page(_: Span, mut args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
||||||
let mut f = Feedback::new();
|
let mut f = Feedback::new();
|
||||||
let mut style = ctx.style.page;
|
let mut style = ctx.style.page;
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ pub async fn page(_: Span, mut args: TableValue, ctx: LayoutContext<'_>) -> Pass
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// `pagebreak`: Ends the current page.
|
/// `pagebreak`: Ends the current page.
|
||||||
pub async fn pagebreak(_: Span, args: TableValue, _: LayoutContext<'_>) -> Pass<Value> {
|
pub async fn pagebreak(_: Span, args: DictValue, _: LayoutContext<'_>) -> Pass<Value> {
|
||||||
let mut f = Feedback::new();
|
let mut f = Feedback::new();
|
||||||
args.unexpected(&mut f);
|
args.unexpected(&mut f);
|
||||||
Pass::commands(vec![BreakPage], f)
|
Pass::commands(vec![BreakPage], f)
|
||||||
|
@ -6,7 +6,7 @@ use crate::length::ScaleLength;
|
|||||||
///
|
///
|
||||||
/// # Positional arguments
|
/// # Positional arguments
|
||||||
/// - The spacing (length or relative to font size).
|
/// - The spacing (length or relative to font size).
|
||||||
pub async fn h(name: Span, args: TableValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
pub async fn h(name: Span, args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
||||||
spacing(name, args, ctx, Horizontal)
|
spacing(name, args, ctx, Horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14,13 +14,13 @@ pub async fn h(name: Span, args: TableValue, ctx: LayoutContext<'_>) -> Pass<Val
|
|||||||
///
|
///
|
||||||
/// # Positional arguments
|
/// # Positional arguments
|
||||||
/// - The spacing (length or relative to font size).
|
/// - The spacing (length or relative to font size).
|
||||||
pub async fn v(name: Span, args: TableValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
pub async fn v(name: Span, args: DictValue, ctx: LayoutContext<'_>) -> Pass<Value> {
|
||||||
spacing(name, args, ctx, Vertical)
|
spacing(name, args, ctx, Vertical)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spacing(
|
fn spacing(
|
||||||
name: Span,
|
name: Span,
|
||||||
mut args: TableValue,
|
mut args: DictValue,
|
||||||
ctx: LayoutContext<'_>,
|
ctx: LayoutContext<'_>,
|
||||||
axis: SpecAxis,
|
axis: SpecAxis,
|
||||||
) -> Pass<Value> {
|
) -> Pass<Value> {
|
||||||
|
@ -14,7 +14,7 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::color::RgbaColor;
|
use crate::color::RgbaColor;
|
||||||
use crate::compute::table::SpannedEntry;
|
use crate::compute::dict::SpannedEntry;
|
||||||
use crate::syntax::*;
|
use crate::syntax::*;
|
||||||
use crate::{Feedback, Pass};
|
use crate::{Feedback, Pass};
|
||||||
|
|
||||||
@ -199,13 +199,13 @@ impl Parser<'_> {
|
|||||||
self.skip_ws();
|
self.skip_ws();
|
||||||
|
|
||||||
let mut args = match self.eatv() {
|
let mut args = match self.eatv() {
|
||||||
Some(Token::Colon) => self.parse_table_contents().0,
|
Some(Token::Colon) => self.parse_dict_contents().0,
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
self.expected_at("colon", name.span.end);
|
self.expected_at("colon", name.span.end);
|
||||||
while self.eat().is_some() {}
|
while self.eat().is_some() {}
|
||||||
TableExpr::new()
|
DictExpr::new()
|
||||||
}
|
}
|
||||||
None => TableExpr::new(),
|
None => DictExpr::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.end_group();
|
self.end_group();
|
||||||
@ -243,17 +243,17 @@ impl Parser<'_> {
|
|||||||
|
|
||||||
fn parse_paren_call(&mut self, name: Spanned<Ident>) -> Spanned<CallExpr> {
|
fn parse_paren_call(&mut self, name: Spanned<Ident>) -> Spanned<CallExpr> {
|
||||||
self.start_group(Group::Paren);
|
self.start_group(Group::Paren);
|
||||||
let args = self.parse_table_contents().0;
|
let args = self.parse_dict_contents().0;
|
||||||
let args_span = self.end_group();
|
let args_span = self.end_group();
|
||||||
let span = Span::merge(name.span, args_span);
|
let span = Span::merge(name.span, args_span);
|
||||||
CallExpr { name, args }.span_with(span)
|
CallExpr { name, args }.span_with(span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tables.
|
// Dicts.
|
||||||
impl Parser<'_> {
|
impl Parser<'_> {
|
||||||
fn parse_table_contents(&mut self) -> (TableExpr, bool) {
|
fn parse_dict_contents(&mut self) -> (DictExpr, bool) {
|
||||||
let mut table = TableExpr::new();
|
let mut dict = DictExpr::new();
|
||||||
let mut comma_and_keyless = true;
|
let mut comma_and_keyless = true;
|
||||||
|
|
||||||
while {
|
while {
|
||||||
@ -292,12 +292,12 @@ impl Parser<'_> {
|
|||||||
let behind = val.span.end;
|
let behind = val.span.end;
|
||||||
if let Some(key) = key {
|
if let Some(key) = key {
|
||||||
comma_and_keyless = false;
|
comma_and_keyless = false;
|
||||||
table.insert(key.v.0, SpannedEntry::new(key.span, val));
|
dict.insert(key.v.0, SpannedEntry::new(key.span, val));
|
||||||
self.feedback
|
self.feedback
|
||||||
.decorations
|
.decorations
|
||||||
.push(Decoration::TableKey.span_with(key.span));
|
.push(Decoration::DictKey.span_with(key.span));
|
||||||
} else {
|
} else {
|
||||||
table.push(SpannedEntry::val(val));
|
dict.push(SpannedEntry::val(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
if {
|
if {
|
||||||
@ -311,8 +311,8 @@ impl Parser<'_> {
|
|||||||
comma_and_keyless = false;
|
comma_and_keyless = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let coercable = comma_and_keyless && !table.is_empty();
|
let coercable = comma_and_keyless && !dict.is_empty();
|
||||||
(table, coercable)
|
(dict, coercable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,18 +421,18 @@ impl Parser<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This could be a table or a parenthesized expression. We parse as
|
// This could be a dictionary or a parenthesized expression. We
|
||||||
// a table in any case and coerce the table into a value if it is
|
// parse as a dictionary in any case and coerce into a value if
|
||||||
// coercable (length 1 and no trailing comma).
|
// that's coercable (length 1 and no trailing comma).
|
||||||
Token::LeftParen => {
|
Token::LeftParen => {
|
||||||
self.start_group(Group::Paren);
|
self.start_group(Group::Paren);
|
||||||
let (table, coercable) = self.parse_table_contents();
|
let (dict, coercable) = self.parse_dict_contents();
|
||||||
let span = self.end_group();
|
let span = self.end_group();
|
||||||
|
|
||||||
let expr = if coercable {
|
let expr = if coercable {
|
||||||
table.into_values().next().expect("table is coercable").val.v
|
dict.into_values().next().expect("dict is coercable").val.v
|
||||||
} else {
|
} else {
|
||||||
Expr::Table(table)
|
Expr::Dict(dict)
|
||||||
};
|
};
|
||||||
|
|
||||||
expr.span_with(span)
|
expr.span_with(span)
|
||||||
|
@ -6,7 +6,7 @@ use std::fmt::Debug;
|
|||||||
|
|
||||||
use super::parse;
|
use super::parse;
|
||||||
use crate::color::RgbaColor;
|
use crate::color::RgbaColor;
|
||||||
use crate::compute::table::SpannedEntry;
|
use crate::compute::dict::SpannedEntry;
|
||||||
use crate::length::Length;
|
use crate::length::Length;
|
||||||
use crate::syntax::*;
|
use crate::syntax::*;
|
||||||
|
|
||||||
@ -59,26 +59,26 @@ fn Str(string: &str) -> Expr {
|
|||||||
Expr::Str(string.to_string())
|
Expr::Str(string.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! Table {
|
macro_rules! Dict {
|
||||||
(@table=$table:expr,) => {};
|
(@dict=$dict:expr,) => {};
|
||||||
(@table=$table:expr, $key:expr => $value:expr $(, $($tts:tt)*)?) => {{
|
(@dict=$dict:expr, $key:expr => $value:expr $(, $($tts:tt)*)?) => {{
|
||||||
let key = Into::<Spanned<&str>>::into($key);
|
let key = Into::<Spanned<&str>>::into($key);
|
||||||
let val = Into::<Spanned<Expr>>::into($value);
|
let val = Into::<Spanned<Expr>>::into($value);
|
||||||
$table.insert(key.v, SpannedEntry::new(key.span, val));
|
$dict.insert(key.v, SpannedEntry::new(key.span, val));
|
||||||
Table![@table=$table, $($($tts)*)?];
|
Dict![@dict=$dict, $($($tts)*)?];
|
||||||
}};
|
}};
|
||||||
(@table=$table:expr, $value:expr $(, $($tts:tt)*)?) => {
|
(@dict=$dict:expr, $value:expr $(, $($tts:tt)*)?) => {
|
||||||
let val = Into::<Spanned<Expr>>::into($value);
|
let val = Into::<Spanned<Expr>>::into($value);
|
||||||
$table.push(SpannedEntry::val(val));
|
$dict.push(SpannedEntry::val(val));
|
||||||
Table![@table=$table, $($($tts)*)?];
|
Dict![@dict=$dict, $($($tts)*)?];
|
||||||
};
|
};
|
||||||
(@$($tts:tt)*) => {{
|
(@$($tts:tt)*) => {{
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut table = TableExpr::new();
|
let mut dict = DictExpr::new();
|
||||||
Table![@table=table, $($tts)*];
|
Dict![@dict=dict, $($tts)*];
|
||||||
table
|
dict
|
||||||
}};
|
}};
|
||||||
($($tts:tt)*) => { Expr::Table(Table![@$($tts)*]) };
|
($($tts:tt)*) => { Expr::Dict(Dict![@$($tts)*]) };
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! Tree {
|
macro_rules! Tree {
|
||||||
@ -93,7 +93,7 @@ macro_rules! Call {
|
|||||||
let name = Into::<Spanned<&str>>::into($name);
|
let name = Into::<Spanned<&str>>::into($name);
|
||||||
CallExpr {
|
CallExpr {
|
||||||
name: name.map(|n| Ident(n.to_string())),
|
name: name.map(|n| Ident(n.to_string())),
|
||||||
args: Table![@$($($tts)*)?],
|
args: Dict![@$($($tts)*)?],
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
($($tts:tt)*) => { Expr::Call(Call![@$($tts)*]) };
|
($($tts:tt)*) => { Expr::Call(Call![@$($tts)*]) };
|
||||||
@ -321,7 +321,7 @@ fn test_parse_function_names() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_chaining() {
|
fn test_parse_chaining() {
|
||||||
// Things the parser has to make sense of
|
// Things the parser has to make sense of
|
||||||
t!("[hi: (5.0, 2.1 >> you]" => F!("hi"; Table![Num(5.0), Num(2.1)], Tree![F!("you")]));
|
t!("[hi: (5.0, 2.1 >> you]" => F!("hi"; Dict![Num(5.0), Num(2.1)], Tree![F!("you")]));
|
||||||
t!("[box >>][Hi]" => F!("box"; Tree![T("Hi")]));
|
t!("[box >>][Hi]" => F!("box"; Tree![T("Hi")]));
|
||||||
t!("[box >> pad: 1pt][Hi]" => F!("box"; Tree![
|
t!("[box >> pad: 1pt][Hi]" => F!("box"; Tree![
|
||||||
F!("pad"; Len(Length::pt(1.0)), Tree!(T("Hi")))
|
F!("pad"; Len(Length::pt(1.0)), Tree!(T("Hi")))
|
||||||
@ -416,7 +416,7 @@ fn test_parse_values() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_expressions() {
|
fn test_parse_expressions() {
|
||||||
// Coerced table.
|
// Coerced dict.
|
||||||
v!("(hi)" => Id("hi"));
|
v!("(hi)" => Id("hi"));
|
||||||
|
|
||||||
// Operations.
|
// Operations.
|
||||||
@ -458,19 +458,19 @@ fn test_parse_expressions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_tables() {
|
fn test_parse_dicts() {
|
||||||
// Okay.
|
// Okay.
|
||||||
v!("()" => Table![]);
|
v!("()" => Dict![]);
|
||||||
v!("(false)" => Bool(false));
|
v!("(false)" => Bool(false));
|
||||||
v!("(true,)" => Table![Bool(true)]);
|
v!("(true,)" => Dict![Bool(true)]);
|
||||||
v!("(key=val)" => Table!["key" => Id("val")]);
|
v!("(key=val)" => Dict!["key" => Id("val")]);
|
||||||
v!("(1, 2)" => Table![Num(1.0), Num(2.0)]);
|
v!("(1, 2)" => Dict![Num(1.0), Num(2.0)]);
|
||||||
v!("(1, key=\"value\")" => Table![Num(1.0), "key" => Str("value")]);
|
v!("(1, key=\"value\")" => Dict![Num(1.0), "key" => Str("value")]);
|
||||||
|
|
||||||
// Decorations.
|
// Decorations.
|
||||||
d!("[val: key=hi]" => s(6, 9, TableKey));
|
d!("[val: key=hi]" => s(6, 9, DictKey));
|
||||||
d!("[val: (key=hi)]" => s(7, 10, TableKey));
|
d!("[val: (key=hi)]" => s(7, 10, DictKey));
|
||||||
d!("[val: f(key=hi)]" => s(8, 11, TableKey));
|
d!("[val: f(key=hi)]" => s(8, 11, DictKey));
|
||||||
|
|
||||||
// Spanned with spacing around keyword arguments.
|
// Spanned with spacing around keyword arguments.
|
||||||
ts!("[val: \n hi \n = /* //\n */ \"s\n\"]" => s(0, 30, F!(
|
ts!("[val: \n hi \n = /* //\n */ \"s\n\"]" => s(0, 30, F!(
|
||||||
@ -481,7 +481,7 @@ fn test_parse_tables() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_tables_compute_func_calls() {
|
fn test_parse_dicts_compute_func_calls() {
|
||||||
v!("empty()" => Call!("empty"));
|
v!("empty()" => Call!("empty"));
|
||||||
v!("add ( 1 , 2 )" => Call!("add"; Num(1.0), Num(2.0)));
|
v!("add ( 1 , 2 )" => Call!("add"; Num(1.0), Num(2.0)));
|
||||||
v!("items(\"fire\", #f93a6d)" => Call!("items";
|
v!("items(\"fire\", #f93a6d)" => Call!("items";
|
||||||
@ -501,18 +501,18 @@ fn test_parse_tables_compute_func_calls() {
|
|||||||
e!("[val: lang(中文]" => s(17, 17, "expected closing paren"));
|
e!("[val: lang(中文]" => s(17, 17, "expected closing paren"));
|
||||||
|
|
||||||
// Invalid name.
|
// Invalid name.
|
||||||
v!("👠(\"abc\", 13e-5)" => Table!(Str("abc"), Num(13.0e-5)));
|
v!("👠(\"abc\", 13e-5)" => Dict!(Str("abc"), Num(13.0e-5)));
|
||||||
e!("[val: 👠(\"abc\", 13e-5)]" => s(6, 10, "expected value, found invalid token"));
|
e!("[val: 👠(\"abc\", 13e-5)]" => s(6, 10, "expected value, found invalid token"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_tables_nested() {
|
fn test_parse_dicts_nested() {
|
||||||
v!("(1, ( ab=(), d = (3, 14pt) )), false" =>
|
v!("(1, ( ab=(), d = (3, 14pt) )), false" =>
|
||||||
Table![
|
Dict![
|
||||||
Num(1.0),
|
Num(1.0),
|
||||||
Table!(
|
Dict!(
|
||||||
"ab" => Table![],
|
"ab" => Dict![],
|
||||||
"d" => Table!(Num(3.0), Len(Length::pt(14.0))),
|
"d" => Dict!(Num(3.0), Len(Length::pt(14.0))),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
Bool(false),
|
Bool(false),
|
||||||
@ -520,17 +520,17 @@ fn test_parse_tables_nested() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_tables_errors() {
|
fn test_parse_dicts_errors() {
|
||||||
// Expected value.
|
// Expected value.
|
||||||
e!("[val: (=)]" => s(7, 8, "expected value, found equals sign"));
|
e!("[val: (=)]" => s(7, 8, "expected value, found equals sign"));
|
||||||
e!("[val: (,)]" => s(7, 8, "expected value, found comma"));
|
e!("[val: (,)]" => s(7, 8, "expected value, found comma"));
|
||||||
v!("(\x07 abc,)" => Table![Id("abc")]);
|
v!("(\x07 abc,)" => Dict![Id("abc")]);
|
||||||
e!("[val: (\x07 abc,)]" => s(7, 8, "expected value, found invalid token"));
|
e!("[val: (\x07 abc,)]" => s(7, 8, "expected value, found invalid token"));
|
||||||
e!("[val: (key=,)]" => s(11, 12, "expected value, found comma"));
|
e!("[val: (key=,)]" => s(11, 12, "expected value, found comma"));
|
||||||
e!("[val: hi,)]" => s(9, 10, "expected value, found closing paren"));
|
e!("[val: hi,)]" => s(9, 10, "expected value, found closing paren"));
|
||||||
|
|
||||||
// Expected comma.
|
// Expected comma.
|
||||||
v!("(true false)" => Table![Bool(true), Bool(false)]);
|
v!("(true false)" => Dict![Bool(true), Bool(false)]);
|
||||||
e!("[val: (true false)]" => s(11, 11, "expected comma"));
|
e!("[val: (true false)]" => s(11, 11, "expected comma"));
|
||||||
|
|
||||||
// Expected closing paren.
|
// Expected closing paren.
|
||||||
|
@ -21,6 +21,6 @@ pub enum Decoration {
|
|||||||
Resolved,
|
Resolved,
|
||||||
/// An invalid, unresolved name.
|
/// An invalid, unresolved name.
|
||||||
Unresolved,
|
Unresolved,
|
||||||
/// The key part of a key-value entry in a table.
|
/// A key in a dictionary.
|
||||||
TableKey,
|
DictKey,
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,8 @@ use unicode_xid::UnicodeXID;
|
|||||||
use super::span::{SpanVec, SpanWith, Spanned};
|
use super::span::{SpanVec, SpanWith, Spanned};
|
||||||
use super::Decoration;
|
use super::Decoration;
|
||||||
use crate::color::RgbaColor;
|
use crate::color::RgbaColor;
|
||||||
use crate::compute::table::{SpannedEntry, Table};
|
use crate::compute::dict::{Dict, SpannedEntry};
|
||||||
use crate::compute::value::{TableValue, Value};
|
use crate::compute::value::{DictValue, Value};
|
||||||
use crate::layout::LayoutContext;
|
use crate::layout::LayoutContext;
|
||||||
use crate::length::Length;
|
use crate::length::Length;
|
||||||
use crate::{DynFuture, Feedback};
|
use crate::{DynFuture, Feedback};
|
||||||
@ -143,8 +143,8 @@ pub enum Expr {
|
|||||||
Length(Length),
|
Length(Length),
|
||||||
/// A color value with alpha channel: `#f79143ff`.
|
/// A color value with alpha channel: `#f79143ff`.
|
||||||
Color(RgbaColor),
|
Color(RgbaColor),
|
||||||
/// A table expression: `(false, 12cm, greeting="hi")`.
|
/// A dictionary expression: `(false, 12cm, greeting="hi")`.
|
||||||
Table(TableExpr),
|
Dict(DictExpr),
|
||||||
/// A syntax tree containing typesetting content.
|
/// A syntax tree containing typesetting content.
|
||||||
Tree(SyntaxTree),
|
Tree(SyntaxTree),
|
||||||
/// A function call expression: `cmyk(37.7, 0, 3.9, 1.1)`.
|
/// A function call expression: `cmyk(37.7, 0, 3.9, 1.1)`.
|
||||||
@ -173,7 +173,7 @@ impl Expr {
|
|||||||
Number(_) => "number",
|
Number(_) => "number",
|
||||||
Length(_) => "length",
|
Length(_) => "length",
|
||||||
Color(_) => "color",
|
Color(_) => "color",
|
||||||
Table(_) => "table",
|
Dict(_) => "dictg",
|
||||||
Tree(_) => "syntax tree",
|
Tree(_) => "syntax tree",
|
||||||
Call(_) => "function call",
|
Call(_) => "function call",
|
||||||
Neg(_) => "negation",
|
Neg(_) => "negation",
|
||||||
@ -194,7 +194,7 @@ impl Expr {
|
|||||||
&Number(n) => Value::Number(n),
|
&Number(n) => Value::Number(n),
|
||||||
&Length(s) => Value::Length(s),
|
&Length(s) => Value::Length(s),
|
||||||
&Color(c) => Value::Color(c),
|
&Color(c) => Value::Color(c),
|
||||||
Table(t) => Value::Table(t.eval(ctx, f).await),
|
Dict(t) => Value::Dict(t.eval(ctx, f).await),
|
||||||
Tree(t) => Value::Tree(t.clone()),
|
Tree(t) => Value::Tree(t.clone()),
|
||||||
Call(call) => call.eval(ctx, f).await,
|
Call(call) => call.eval(ctx, f).await,
|
||||||
Neg(_) => todo!("eval neg"),
|
Neg(_) => todo!("eval neg"),
|
||||||
@ -216,7 +216,7 @@ impl Debug for Expr {
|
|||||||
Number(n) => n.fmt(f),
|
Number(n) => n.fmt(f),
|
||||||
Length(s) => s.fmt(f),
|
Length(s) => s.fmt(f),
|
||||||
Color(c) => c.fmt(f),
|
Color(c) => c.fmt(f),
|
||||||
Table(t) => t.fmt(f),
|
Dict(t) => t.fmt(f),
|
||||||
Tree(t) => t.fmt(f),
|
Tree(t) => t.fmt(f),
|
||||||
Call(c) => c.fmt(f),
|
Call(c) => c.fmt(f),
|
||||||
Neg(e) => write!(f, "-{:?}", e),
|
Neg(e) => write!(f, "-{:?}", e),
|
||||||
@ -282,32 +282,32 @@ pub fn is_ident(string: &str) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A table of expressions.
|
/// A dictionary of expressions.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```typst
|
/// ```typst
|
||||||
/// (false, 12cm, greeting="hi")
|
/// (false, 12cm, greeting="hi")
|
||||||
/// ```
|
/// ```
|
||||||
pub type TableExpr = Table<SpannedEntry<Expr>>;
|
pub type DictExpr = Dict<SpannedEntry<Expr>>;
|
||||||
|
|
||||||
impl TableExpr {
|
impl DictExpr {
|
||||||
/// Evaluate the table expression to a table value.
|
/// Evaluate the dictionary expression to a dictionary value.
|
||||||
pub fn eval<'a>(
|
pub fn eval<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
ctx: &'a LayoutContext<'a>,
|
ctx: &'a LayoutContext<'a>,
|
||||||
f: &'a mut Feedback,
|
f: &'a mut Feedback,
|
||||||
) -> DynFuture<'a, TableValue> {
|
) -> DynFuture<'a, DictValue> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let mut table = TableValue::new();
|
let mut dict = DictValue::new();
|
||||||
|
|
||||||
for (key, entry) in self.iter() {
|
for (key, entry) in self.iter() {
|
||||||
let val = entry.val.v.eval(ctx, f).await;
|
let val = entry.val.v.eval(ctx, f).await;
|
||||||
let spanned = val.span_with(entry.val.span);
|
let spanned = val.span_with(entry.val.span);
|
||||||
let entry = SpannedEntry::new(entry.key, spanned);
|
let entry = SpannedEntry::new(entry.key, spanned);
|
||||||
table.insert(key, entry);
|
dict.insert(key, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
table
|
dict
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -316,7 +316,7 @@ impl TableExpr {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct CallExpr {
|
pub struct CallExpr {
|
||||||
pub name: Spanned<Ident>,
|
pub name: Spanned<Ident>,
|
||||||
pub args: TableExpr,
|
pub args: DictExpr,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CallExpr {
|
impl CallExpr {
|
||||||
@ -336,7 +336,7 @@ impl CallExpr {
|
|||||||
error!(@f, span, "unknown function");
|
error!(@f, span, "unknown function");
|
||||||
f.decorations.push(Decoration::Unresolved.span_with(span));
|
f.decorations.push(Decoration::Unresolved.span_with(span));
|
||||||
}
|
}
|
||||||
Value::Table(args)
|
Value::Dict(args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user