summaryrefslogtreecommitdiff
path: root/src/syntax/expr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/syntax/expr.rs')
-rw-r--r--src/syntax/expr.rs88
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;