diff options
Diffstat (limited to 'src/syntax/expr.rs')
| -rw-r--r-- | src/syntax/expr.rs | 88 |
1 files changed, 80 insertions, 8 deletions
diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index b4c0dfaa..879e5fae 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -1,8 +1,10 @@ +//! Expressions in function headers. + use std::fmt::{self, Display, Formatter}; use crate::error::Errors; use crate::size::Size; -use super::func::{keys::Key, values::Value}; +use super::func::{Key, Value}; use super::span::{Span, Spanned}; use super::tokens::is_identifier; @@ -10,16 +12,24 @@ use super::tokens::is_identifier; /// An argument or return value. #[derive(Clone, PartialEq)] pub enum Expr { + /// An identifier: `ident`. Ident(Ident), + /// A string: `"string"`. Str(String), + /// A number: `1.2, 200%`. Number(f64), + /// A size: `2cm, 5.2in`. Size(Size), + /// A bool: `true, false`. Bool(bool), + /// A tuple: `(false, 12cm, "hi")`. Tuple(Tuple), + /// An object: `{ fit: false, size: 12pt }`. Object(Object), } impl Expr { + /// A natural-language name of the type of this expression, e.g. "identifier". pub fn name(&self) -> &'static str { use Expr::*; match self { @@ -34,11 +44,21 @@ impl Expr { } } -/// An identifier. +/// A unicode identifier. +/// +/// The identifier must be valid! This is checked in [`Ident::new`] or +/// [`is_identifier`]. +/// +/// # Example +/// ```typst +/// [func: "hi", ident] +/// ^^^^ ^^^^^ +/// ``` #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct Ident(pub String); impl Ident { + /// Create a new identifier from a string checking that it is valid. pub fn new<S>(ident: S) -> Option<Ident> where S: AsRef<str> + Into<String> { if is_identifier(ident.as_ref()) { Some(Ident(ident.into())) @@ -47,26 +67,37 @@ impl Ident { } } + /// Return a reference to the underlying string. pub fn as_str(&self) -> &str { self.0.as_str() } } -/// A sequence of expressions. +/// An untyped sequence of expressions. +/// +/// # Example +/// ```typst +/// (false, 12cm, "hi") +/// ``` #[derive(Clone, PartialEq)] pub struct Tuple { + /// The elements of the tuple. pub items: Vec<Spanned<Expr>>, } impl Tuple { + /// Create an empty tuple. pub fn new() -> Tuple { Tuple { items: vec![] } } + /// Add an element. pub fn add(&mut self, item: Spanned<Expr>) { self.items.push(item); } + /// Extract (and remove) the first matching value and remove and generate + /// errors for all previous items that did not match. pub fn get<V: Value>(&mut self, errors: &mut Errors) -> Option<V::Output> { while !self.items.is_empty() { let expr = self.items.remove(0); @@ -79,6 +110,8 @@ impl Tuple { None } + /// Extract and return an iterator over all values that match and generate + /// errors for all items that do not match. pub fn get_all<'a, V: Value>(&'a mut self, errors: &'a mut Errors) -> impl Iterator<Item=V::Output> + 'a { self.items.drain(..).filter_map(move |expr| { @@ -92,36 +125,63 @@ impl Tuple { } /// A key-value collection of identifiers and associated expressions. +/// +/// The pairs themselves are not spanned, but the combined spans can easily be +/// retrieved by merging the spans of key and value as happening in +/// [`FuncArg::span`](super::func::FuncArg::span). +/// +/// # Example +/// ```typst +/// { fit: false, size: 12cm, items: (1, 2, 3) } +/// ``` #[derive(Clone, PartialEq)] pub struct Object { + /// The key-value pairs of the object. pub pairs: Vec<Pair>, } /// A key-value pair in an object. #[derive(Clone, PartialEq)] pub struct Pair { + /// The key part. + /// ```typst + /// key: value + /// ^^^ + /// ``` pub key: Spanned<Ident>, + /// The value part. + /// ```typst + /// key: value + /// ^^^^^ + /// ``` pub value: Spanned<Expr>, } impl Object { + /// Create an empty object. pub fn new() -> Object { Object { pairs: vec![] } } - pub fn add(&mut self, key: Spanned<Ident>, value: Spanned<Expr>) { - self.pairs.push(Pair { key, value }); - } - - pub fn add_pair(&mut self, pair: Pair) { + /// Add a pair to object. + pub fn add(&mut self, pair: Pair) { self.pairs.push(pair); } + /// Extract (and remove) a pair with the given key string and matching + /// value. + /// + /// Inserts an error if the value does not match. If the key is not + /// contained, no error is inserted. pub fn get<V: Value>(&mut self, errors: &mut Errors, key: &str) -> Option<V::Output> { let index = self.pairs.iter().position(|pair| pair.key.v.as_str() == key)?; self.get_index::<V>(errors, index) } + /// Extract (and remove) a pair with a matching key and value. + /// + /// Inserts an error if the value does not match. If no matching key is + /// found, no error is inserted. pub fn get_with_key<K: Key, V: Value>( &mut self, errors: &mut Errors, @@ -135,6 +195,9 @@ impl Object { None } + /// Extract (and remove) all pairs with matching keys and values. + /// + /// Inserts errors for values that do not match. pub fn get_all<'a, K: Key, V: Value>( &'a mut self, errors: &'a mut Errors, @@ -157,6 +220,13 @@ impl Object { }).filter_map(|x| x) } + /// Extract all key value pairs with span information. + /// + /// The spans are over both key and value, like so: + /// ```typst + /// { key: value } + /// ^^^^^^^^^^ + /// ``` pub fn get_all_spanned<'a, K: Key + 'a, V: Value + 'a>( &'a mut self, errors: &'a mut Errors, @@ -165,6 +235,8 @@ impl Object { .map(|(k, v)| Spanned::new((k.v, v.v), Span::merge(k.span, v.span))) } + /// Extract the argument at the given index and insert an error if the value + /// does not match. fn get_index<V: Value>(&mut self, errors: &mut Errors, index: usize) -> Option<V::Output> { let expr = self.pairs.remove(index).value; let span = expr.span; |
