mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Refactor expressions and create tuples and objects 🧮
This commit is contained in:
parent
6527d31dfb
commit
dde69276d4
@ -16,7 +16,7 @@ pub mod prelude {
|
|||||||
pub use crate::layout::prelude::*;
|
pub use crate::layout::prelude::*;
|
||||||
pub use crate::syntax::{
|
pub use crate::syntax::{
|
||||||
ParseContext, ParseResult,
|
ParseContext, ParseResult,
|
||||||
SyntaxTree, FuncCall, FuncArgs, PosArg, KeyArg,
|
SyntaxTree, FuncCall, FuncArgs,
|
||||||
Expression, Ident, ExpressionKind,
|
Expression, Ident, ExpressionKind,
|
||||||
Spanned, Span
|
Spanned, Span
|
||||||
};
|
};
|
||||||
|
@ -34,6 +34,13 @@ key!(AxisKey, "axis",
|
|||||||
"secondary" | "s" => Generic(Secondary),
|
"secondary" | "s" => Generic(Secondary),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
key!(Direction, "direction",
|
||||||
|
"left-to-right" | "ltr" => LeftToRight,
|
||||||
|
"right-to-left" | "rtl" => RightToLeft,
|
||||||
|
"top-to-bottom" | "ttb" => TopToBottom,
|
||||||
|
"bottom-to-top" | "btt" => BottomToTop,
|
||||||
|
);
|
||||||
|
|
||||||
/// A map for storing extents along axes.
|
/// A map for storing extents along axes.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ExtentMap<E: ExpressionKind + Copy>(ConsistentMap<AxisKey, E>);
|
pub struct ExtentMap<E: ExpressionKind + Copy>(ConsistentMap<AxisKey, E>);
|
||||||
@ -41,27 +48,27 @@ pub struct ExtentMap<E: ExpressionKind + Copy>(ConsistentMap<AxisKey, E>);
|
|||||||
impl<E: ExpressionKind + Copy> ExtentMap<E> {
|
impl<E: ExpressionKind + Copy> ExtentMap<E> {
|
||||||
/// Parse an extent map from the function args.
|
/// Parse an extent map from the function args.
|
||||||
///
|
///
|
||||||
/// If `enforce` is true other arguments will create an error, otherwise
|
/// If `all` is true other arguments will create an error, otherwise
|
||||||
/// they are left intact.
|
/// they are left intact.
|
||||||
pub fn new(args: &mut FuncArgs, enforce: bool) -> ParseResult<ExtentMap<E>> {
|
pub fn new(args: &mut FuncArgs, all: bool) -> ParseResult<ExtentMap<E>> {
|
||||||
let mut map = ConsistentMap::new();
|
let mut map = ConsistentMap::new();
|
||||||
|
|
||||||
for arg in args.keys() {
|
for arg in args.iter_keys() {
|
||||||
let key = match arg.v.key.v.as_str() {
|
let key = match arg.key.v.as_str() {
|
||||||
"width" | "w" => AxisKey::Specific(Horizontal),
|
"width" | "w" => AxisKey::Specific(Horizontal),
|
||||||
"height" | "h" => AxisKey::Specific(Vertical),
|
"height" | "h" => AxisKey::Specific(Vertical),
|
||||||
"primary-size" | "ps" => AxisKey::Generic(Primary),
|
"primary-size" | "ps" => AxisKey::Generic(Primary),
|
||||||
"secondary-size" | "ss" => AxisKey::Generic(Secondary),
|
"secondary-size" | "ss" => AxisKey::Generic(Secondary),
|
||||||
|
|
||||||
_ => if enforce {
|
_ => if all {
|
||||||
error!("expected dimension")
|
error!("expected dimension")
|
||||||
} else {
|
} else {
|
||||||
args.add_key(arg);
|
args.add_key_pair(arg);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let e = E::from_expr(arg.v.value)?;
|
let e = E::from_expr(arg.value)?;
|
||||||
map.add(key, e)?;
|
map.add(key, e)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,9 +105,9 @@ impl<E: ExpressionKind + Copy> PosAxisMap<E> {
|
|||||||
map.add_opt(PosAxisKey::First, args.get_pos_opt::<E>()?)?;
|
map.add_opt(PosAxisKey::First, args.get_pos_opt::<E>()?)?;
|
||||||
map.add_opt(PosAxisKey::Second, args.get_pos_opt::<E>()?)?;
|
map.add_opt(PosAxisKey::Second, args.get_pos_opt::<E>()?)?;
|
||||||
|
|
||||||
for arg in args.keys() {
|
for arg in args.iter_keys() {
|
||||||
let axis = AxisKey::from_ident(&arg.v.key)?;
|
let axis = AxisKey::from_ident(&arg.key)?;
|
||||||
let value = E::from_expr(arg.v.value)?;
|
let value = E::from_expr(arg.value)?;
|
||||||
|
|
||||||
map.add(PosAxisKey::Keyword(axis), value)?;
|
map.add(PosAxisKey::Keyword(axis), value)?;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,37 @@ pub_use_mod!(axis);
|
|||||||
pub_use_mod!(alignment);
|
pub_use_mod!(alignment);
|
||||||
pub_use_mod!(padding);
|
pub_use_mod!(padding);
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum DefaultKey<T> {
|
||||||
|
Some(T),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Into<Option<T>> for DefaultKey<T> {
|
||||||
|
fn into(self) -> Option<T> {
|
||||||
|
match self {
|
||||||
|
DefaultKey::Some(v) => Some(v),
|
||||||
|
DefaultKey::None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ExpressionKind for DefaultKey<T> where T: ExpressionKind {
|
||||||
|
const NAME: &'static str = T::NAME;
|
||||||
|
|
||||||
|
fn from_expr(expr: Spanned<Expression>) -> ParseResult<DefaultKey<T>> {
|
||||||
|
if let Expression::Ident(ident) = &expr.v {
|
||||||
|
match ident.as_str() {
|
||||||
|
"default" => return Ok(DefaultKey::None),
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T::from_expr(expr).map(|v| DefaultKey::Some(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A deduplicating map type useful for storing possibly redundant arguments.
|
/// A deduplicating map type useful for storing possibly redundant arguments.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ConsistentMap<K, V> where K: Hash + Eq {
|
pub struct ConsistentMap<K, V> where K: Hash + Eq {
|
||||||
@ -95,10 +126,3 @@ impl<K, V> ConsistentMap<K, V> where K: Hash + Eq {
|
|||||||
self.map.iter()
|
self.map.iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
key!(Direction, "direction",
|
|
||||||
"left-to-right" | "ltr" => LeftToRight,
|
|
||||||
"right-to-left" | "rtl" => RightToLeft,
|
|
||||||
"top-to-bottom" | "ttb" => TopToBottom,
|
|
||||||
"bottom-to-top" | "btt" => BottomToTop,
|
|
||||||
);
|
|
||||||
|
@ -46,11 +46,12 @@ impl PaddingMap {
|
|||||||
/// Parse a padding map from the function args.
|
/// Parse a padding map from the function args.
|
||||||
pub fn new(args: &mut FuncArgs) -> ParseResult<PaddingMap> {
|
pub fn new(args: &mut FuncArgs) -> ParseResult<PaddingMap> {
|
||||||
let mut map = ConsistentMap::new();
|
let mut map = ConsistentMap::new();
|
||||||
map.add_opt(PaddingKey::All, args.get_pos_opt::<Option<PSize>>()?)?;
|
map.add_opt(PaddingKey::All,
|
||||||
|
args.get_pos_opt::<DefaultKey<PSize>>()?.map(Into::into))?;
|
||||||
|
|
||||||
for arg in args.keys() {
|
for arg in args.iter_keys() {
|
||||||
let key = PaddingKey::from_ident(&arg.v.key)?;
|
let key = PaddingKey::from_ident(&arg.key)?;
|
||||||
let size = Option::<PSize>::from_expr(arg.v.value)?;
|
let size = DefaultKey::<PSize>::from_expr(arg.value)?.into();
|
||||||
map.add(key, size)?;
|
map.add(key, size)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ function! {
|
|||||||
FontFamilyFunc {
|
FontFamilyFunc {
|
||||||
body: parse!(optional: body, ctx),
|
body: parse!(optional: body, ctx),
|
||||||
list: {
|
list: {
|
||||||
args.pos().map(|arg| match arg.v {
|
args.iter_pos().map(|arg| match arg.v {
|
||||||
Expression::Str(s) |
|
Expression::Str(s) |
|
||||||
Expression::Ident(Ident(s)) => Ok(s.to_lowercase()),
|
Expression::Ident(Ident(s)) => Ok(s.to_lowercase()),
|
||||||
_ => error!("expected identifier or string"),
|
_ => error!("expected identifier or string"),
|
||||||
@ -118,7 +118,7 @@ function! {
|
|||||||
FontWeightFunc {
|
FontWeightFunc {
|
||||||
body: parse!(optional: body, ctx),
|
body: parse!(optional: body, ctx),
|
||||||
weight: match args.get_pos::<Expression>()? {
|
weight: match args.get_pos::<Expression>()? {
|
||||||
Expression::Num(weight) => {
|
Expression::Number(weight) => {
|
||||||
let weight = weight.round() as i16;
|
let weight = weight.round() as i16;
|
||||||
FontWeight(
|
FontWeight(
|
||||||
if weight < 100 { 100 }
|
if weight < 100 { 100 }
|
||||||
@ -264,13 +264,15 @@ function! {
|
|||||||
axis: AxisKey::Specific(axis),
|
axis: AxisKey::Specific(axis),
|
||||||
spacing: FSize::from_expr(args.get_pos::<Spanned<Expression>>()?)?,
|
spacing: FSize::from_expr(args.get_pos::<Spanned<Expression>>()?)?,
|
||||||
}
|
}
|
||||||
} else if let Some(arg) = args.get_key_next() {
|
|
||||||
let axis = AxisKey::from_ident(&arg.v.key)
|
|
||||||
.map_err(|_| error!(@unexpected_argument))?;
|
|
||||||
|
|
||||||
let spacing = FSize::from_expr(arg.v.value)?;
|
|
||||||
SpacingFunc { axis, spacing }
|
|
||||||
} else {
|
} else {
|
||||||
|
for arg in args.iter_keys() {
|
||||||
|
let axis = AxisKey::from_ident(&arg.key)
|
||||||
|
.map_err(|_| error!(@unexpected_argument))?;
|
||||||
|
|
||||||
|
let spacing = FSize::from_expr(arg.value)?;
|
||||||
|
return Ok(SpacingFunc { axis, spacing });
|
||||||
|
}
|
||||||
|
|
||||||
error!("expected axis and spacing")
|
error!("expected axis and spacing")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ pub enum ColorToken {
|
|||||||
Brace,
|
Brace,
|
||||||
|
|
||||||
ExprIdent,
|
ExprIdent,
|
||||||
ExprString,
|
ExprStr,
|
||||||
ExprNumber,
|
ExprNumber,
|
||||||
ExprSize,
|
ExprSize,
|
||||||
ExprBool,
|
ExprBool,
|
||||||
|
@ -1,125 +1,16 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
||||||
/// The arguments passed to a function.
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct FuncArgs {
|
|
||||||
pub pos: Vec<Spanned<PosArg>>,
|
|
||||||
pub key: Vec<Spanned<KeyArg>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FuncArgs {
|
|
||||||
/// Create an empty collection of arguments.
|
|
||||||
pub fn new() -> FuncArgs {
|
|
||||||
FuncArgs {
|
|
||||||
pos: vec![],
|
|
||||||
key: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a positional argument.
|
|
||||||
pub fn add_pos(&mut self, arg: Spanned<PosArg>) {
|
|
||||||
self.pos.push(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a keyword argument.
|
|
||||||
pub fn add_key(&mut self, arg: Spanned<KeyArg>) {
|
|
||||||
self.key.push(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Force-extract the first positional argument.
|
|
||||||
pub fn get_pos<E: ExpressionKind>(&mut self) -> ParseResult<E> {
|
|
||||||
expect(self.get_pos_opt())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract the first positional argument.
|
|
||||||
pub fn get_pos_opt<E: ExpressionKind>(&mut self) -> ParseResult<Option<E>> {
|
|
||||||
Ok(if !self.pos.is_empty() {
|
|
||||||
let spanned = self.pos.remove(0);
|
|
||||||
Some(E::from_expr(spanned)?)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator over positional arguments.
|
|
||||||
pub fn pos(&mut self) -> std::vec::IntoIter<Spanned<PosArg>> {
|
|
||||||
let vec = std::mem::replace(&mut self.pos, vec![]);
|
|
||||||
vec.into_iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Force-extract a keyword argument.
|
|
||||||
pub fn get_key<E: ExpressionKind>(&mut self, name: &str) -> ParseResult<E> {
|
|
||||||
expect(self.get_key_opt(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract a keyword argument.
|
|
||||||
pub fn get_key_opt<E: ExpressionKind>(&mut self, name: &str) -> ParseResult<Option<E>> {
|
|
||||||
Ok(if let Some(index) = self.key.iter().position(|arg| arg.v.key.v.0 == name) {
|
|
||||||
let value = self.key.swap_remove(index).v.value;
|
|
||||||
Some(E::from_expr(value)?)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract any keyword argument.
|
|
||||||
pub fn get_key_next(&mut self) -> Option<Spanned<KeyArg>> {
|
|
||||||
self.key.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator over all keyword arguments.
|
|
||||||
pub fn keys(&mut self) -> std::vec::IntoIter<Spanned<KeyArg>> {
|
|
||||||
let vec = std::mem::replace(&mut self.key, vec![]);
|
|
||||||
vec.into_iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clear the argument lists.
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.pos.clear();
|
|
||||||
self.key.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether both the positional and keyword argument lists are empty.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.pos.is_empty() && self.key.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract the option expression kind from the option or return an error.
|
|
||||||
fn expect<E: ExpressionKind>(opt: ParseResult<Option<E>>) -> ParseResult<E> {
|
|
||||||
match opt {
|
|
||||||
Ok(Some(spanned)) => Ok(spanned),
|
|
||||||
Ok(None) => error!("expected {}", E::NAME),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A positional argument passed to a function.
|
|
||||||
pub type PosArg = Expression;
|
|
||||||
|
|
||||||
/// A keyword argument passed to a function.
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct KeyArg {
|
|
||||||
pub key: Spanned<Ident>,
|
|
||||||
pub value: Spanned<Expression>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Either a positional or keyword argument.
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum DynArg {
|
|
||||||
Pos(Spanned<PosArg>),
|
|
||||||
Key(Spanned<KeyArg>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An argument or return value.
|
/// An argument or return value.
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Ident(Ident),
|
Ident(Ident),
|
||||||
Str(String),
|
Str(String),
|
||||||
Num(f64),
|
Number(f64),
|
||||||
Size(Size),
|
Size(Size),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
Tuple(Tuple),
|
||||||
|
Object(Object),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Expression {
|
impl Display for Expression {
|
||||||
@ -128,20 +19,17 @@ impl Display for Expression {
|
|||||||
match self {
|
match self {
|
||||||
Ident(i) => write!(f, "{}", i),
|
Ident(i) => write!(f, "{}", i),
|
||||||
Str(s) => write!(f, "{:?}", s),
|
Str(s) => write!(f, "{:?}", s),
|
||||||
Num(n) => write!(f, "{}", n),
|
Number(n) => write!(f, "{}", n),
|
||||||
Size(s) => write!(f, "{}", s),
|
Size(s) => write!(f, "{}", s),
|
||||||
Bool(b) => write!(f, "{}", b),
|
Bool(b) => write!(f, "{}", b),
|
||||||
|
Tuple(t) => write!(f, "{}", t),
|
||||||
|
Object(o) => write!(f, "{}", o),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_display!(Expression);
|
|
||||||
|
|
||||||
pub struct Tuple;
|
|
||||||
pub struct Object;
|
|
||||||
|
|
||||||
/// An identifier.
|
/// An identifier.
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
pub struct Ident(pub String);
|
pub struct Ident(pub String);
|
||||||
|
|
||||||
impl Ident {
|
impl Ident {
|
||||||
@ -164,16 +52,98 @@ impl Display for Ident {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A sequence of expressions.
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct Tuple {
|
||||||
|
pub items: Vec<Spanned<Expression>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tuple {
|
||||||
|
pub fn new() -> Tuple {
|
||||||
|
Tuple { items: vec![] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, item: Spanned<Expression>) {
|
||||||
|
self.items.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Tuple {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "(")?;
|
||||||
|
|
||||||
|
let mut first = true;
|
||||||
|
for item in &self.items {
|
||||||
|
if !first {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
write!(f, "{}", item.v)?;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, ")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A key-value collection of identifiers and associated expressions.
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct Object {
|
||||||
|
pub pairs: Vec<Pair>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct Pair {
|
||||||
|
pub key: Spanned<Ident>,
|
||||||
|
pub value: Spanned<Expression>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object {
|
||||||
|
pub fn new() -> Object {
|
||||||
|
Object { pairs: vec![] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, key: Spanned<Ident>, value: Spanned<Expression>) {
|
||||||
|
self.pairs.push(Pair { key, value });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_pair(&mut self, pair: Pair) {
|
||||||
|
self.pairs.push(pair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Object {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{{ ")?;
|
||||||
|
|
||||||
|
let mut first = true;
|
||||||
|
for pair in &self.pairs {
|
||||||
|
if !first {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
write!(f, "{}: {}", pair.key.v, pair.value.v)?;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, " }}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
debug_display!(Ident);
|
debug_display!(Ident);
|
||||||
|
debug_display!(Expression);
|
||||||
|
debug_display!(Tuple);
|
||||||
|
debug_display!(Object);
|
||||||
|
|
||||||
|
|
||||||
/// Kinds of expressions.
|
/// Kinds of expressions.
|
||||||
pub trait ExpressionKind: Sized {
|
pub trait ExpressionKind: Sized {
|
||||||
|
/// The name of the expression in an `expected <name>` error.
|
||||||
const NAME: &'static str;
|
const NAME: &'static str;
|
||||||
|
|
||||||
/// Create from expression.
|
/// Create from expression.
|
||||||
fn from_expr(expr: Spanned<Expression>) -> ParseResult<Self>;
|
fn from_expr(expr: Spanned<Expression>) -> ParseResult<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implements the expression kind trait for a type.
|
||||||
macro_rules! kind {
|
macro_rules! kind {
|
||||||
($type:ty, $name:expr, $($patterns:tt)*) => {
|
($type:ty, $name:expr, $($patterns:tt)*) => {
|
||||||
impl ExpressionKind for $type {
|
impl ExpressionKind for $type {
|
||||||
@ -190,15 +160,18 @@ macro_rules! kind {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
kind!(Expression, "expression", e => e);
|
kind!(Expression, "expression", e => e);
|
||||||
kind!(Ident, "identifier", Expression::Ident(ident) => ident);
|
kind!(Ident, "identifier", Expression::Ident(ident) => ident);
|
||||||
kind!(String, "string", Expression::Str(string) => string);
|
kind!(String, "string", Expression::Str(string) => string);
|
||||||
kind!(f64, "number", Expression::Num(num) => num);
|
kind!(f64, "number", Expression::Number(num) => num);
|
||||||
kind!(bool, "boolean", Expression::Bool(boolean) => boolean);
|
kind!(bool, "boolean", Expression::Bool(boolean) => boolean);
|
||||||
kind!(Size, "size", Expression::Size(size) => size);
|
kind!(Size, "size", Expression::Size(size) => size);
|
||||||
|
kind!(Tuple, "tuple", Expression::Tuple(tuple) => tuple);
|
||||||
|
kind!(Object, "object", Expression::Object(object) => object);
|
||||||
|
|
||||||
kind!(ScaleSize, "number or size",
|
kind!(ScaleSize, "number or size",
|
||||||
Expression::Size(size) => ScaleSize::Absolute(size),
|
Expression::Size(size) => ScaleSize::Absolute(size),
|
||||||
Expression::Num(scale) => ScaleSize::Scaled(scale as f32)
|
Expression::Number(scale) => ScaleSize::Scaled(scale as f32)
|
||||||
);
|
);
|
||||||
|
|
||||||
impl<T> ExpressionKind for Spanned<T> where T: ExpressionKind {
|
impl<T> ExpressionKind for Spanned<T> where T: ExpressionKind {
|
||||||
@ -206,22 +179,6 @@ impl<T> ExpressionKind for Spanned<T> where T: ExpressionKind {
|
|||||||
|
|
||||||
fn from_expr(expr: Spanned<Expression>) -> ParseResult<Spanned<T>> {
|
fn from_expr(expr: Spanned<Expression>) -> ParseResult<Spanned<T>> {
|
||||||
let span = expr.span;
|
let span = expr.span;
|
||||||
T::from_expr(expr)
|
T::from_expr(expr).map(|v| Spanned { v, span })
|
||||||
.map(|v| Spanned::new(v, span))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> ExpressionKind for Option<T> where T: ExpressionKind {
|
|
||||||
const NAME: &'static str = T::NAME;
|
|
||||||
|
|
||||||
fn from_expr(expr: Spanned<Expression>) -> ParseResult<Option<T>> {
|
|
||||||
if let Expression::Ident(ident) = &expr.v {
|
|
||||||
match ident.as_str() {
|
|
||||||
"default" | "none" => return Ok(None),
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
T::from_expr(expr).map(|v| Some(v))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,99 @@ impl PartialEq for FuncCall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FuncArgs {
|
||||||
|
positional: Tuple,
|
||||||
|
keyword: Object,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FuncArgs {
|
||||||
|
fn new() -> FuncArgs {
|
||||||
|
FuncArgs {
|
||||||
|
positional: Tuple::new(),
|
||||||
|
keyword: Object::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a positional argument.
|
||||||
|
pub fn add_pos(&mut self, item: Spanned<Expression>) {
|
||||||
|
self.positional.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Force-extract the first positional argument.
|
||||||
|
pub fn get_pos<E: ExpressionKind>(&mut self) -> ParseResult<E> {
|
||||||
|
expect(self.get_pos_opt())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract the first positional argument.
|
||||||
|
pub fn get_pos_opt<E: ExpressionKind>(&mut self) -> ParseResult<Option<E>> {
|
||||||
|
Ok(if !self.positional.items.is_empty() {
|
||||||
|
let spanned = self.positional.items.remove(0);
|
||||||
|
Some(E::from_expr(spanned)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a keyword argument.
|
||||||
|
pub fn add_key(&mut self, key: Spanned<Ident>, value: Spanned<Expression>) {
|
||||||
|
self.keyword.add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a keyword argument from an existing pair.
|
||||||
|
pub fn add_key_pair(&mut self, pair: Pair) {
|
||||||
|
self.keyword.add_pair(pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Force-extract a keyword argument.
|
||||||
|
pub fn get_key<E: ExpressionKind>(&mut self, name: &str) -> ParseResult<E> {
|
||||||
|
expect(self.get_key_opt(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract a keyword argument.
|
||||||
|
pub fn get_key_opt<E: ExpressionKind>(&mut self, name: &str) -> ParseResult<Option<E>> {
|
||||||
|
self.keyword.pairs.iter()
|
||||||
|
.position(|p| p.key.v.0 == name)
|
||||||
|
.map(|index| {
|
||||||
|
let value = self.keyword.pairs.swap_remove(index).value;
|
||||||
|
E::from_expr(value)
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator over positional arguments.
|
||||||
|
pub fn iter_pos(&mut self) -> std::vec::IntoIter<Spanned<Expression>> {
|
||||||
|
let tuple = std::mem::replace(&mut self.positional, Tuple::new());
|
||||||
|
tuple.items.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator over all keyword arguments.
|
||||||
|
pub fn iter_keys(&mut self) -> std::vec::IntoIter<Pair> {
|
||||||
|
let object = std::mem::replace(&mut self.keyword, Object::new());
|
||||||
|
object.pairs.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the argument lists.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.positional.items.clear();
|
||||||
|
self.keyword.pairs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether both the positional and keyword argument lists are empty.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.positional.items.is_empty() && self.keyword.pairs.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract the option expression kind from the option or return an error.
|
||||||
|
fn expect<E: ExpressionKind>(opt: ParseResult<Option<E>>) -> ParseResult<E> {
|
||||||
|
match opt {
|
||||||
|
Ok(Some(spanned)) => Ok(spanned),
|
||||||
|
Ok(None) => error!("expected {}", E::NAME),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses source code into a syntax tree given a context.
|
/// Parses source code into a syntax tree given a context.
|
||||||
pub fn parse(src: &str, ctx: ParseContext) -> SyntaxTree {
|
pub fn parse(src: &str, ctx: ParseContext) -> SyntaxTree {
|
||||||
Parser::new(src, ctx).parse()
|
Parser::new(src, ctx).parse()
|
||||||
@ -346,7 +439,7 @@ impl<'s> Parser<'s> {
|
|||||||
Comma => Some(ColorToken::Comma),
|
Comma => Some(ColorToken::Comma),
|
||||||
Equals => Some(ColorToken::Equals),
|
Equals => Some(ColorToken::Equals),
|
||||||
ExprIdent(_) => Some(ColorToken::ExprIdent),
|
ExprIdent(_) => Some(ColorToken::ExprIdent),
|
||||||
ExprString(_) => Some(ColorToken::ExprString),
|
ExprStr(_) => Some(ColorToken::ExprStr),
|
||||||
ExprNumber(_) => Some(ColorToken::ExprNumber),
|
ExprNumber(_) => Some(ColorToken::ExprNumber),
|
||||||
ExprSize(_) => Some(ColorToken::ExprSize),
|
ExprSize(_) => Some(ColorToken::ExprSize),
|
||||||
ExprBool(_) => Some(ColorToken::ExprBool),
|
ExprBool(_) => Some(ColorToken::ExprBool),
|
||||||
@ -387,7 +480,7 @@ fn name(token: Token) -> &'static str {
|
|||||||
Comma => "comma",
|
Comma => "comma",
|
||||||
Equals => "equals sign",
|
Equals => "equals sign",
|
||||||
ExprIdent(_) => "identifier",
|
ExprIdent(_) => "identifier",
|
||||||
ExprString(_) => "string",
|
ExprStr(_) => "string",
|
||||||
ExprNumber(_) => "number",
|
ExprNumber(_) => "number",
|
||||||
ExprSize(_) => "size",
|
ExprSize(_) => "size",
|
||||||
ExprBool(_) => "bool",
|
ExprBool(_) => "bool",
|
||||||
|
@ -4,7 +4,7 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
|
|
||||||
|
|
||||||
/// Annotates a value with the part of the source code it corresponds to.
|
/// Annotates a value with the part of the source code it corresponds to.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct Spanned<T> {
|
pub struct Spanned<T> {
|
||||||
pub v: T,
|
pub v: T,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
@ -37,7 +37,7 @@ impl<T> Display for Spanned<T> where T: std::fmt::Debug {
|
|||||||
debug_display!(Spanned; T where T: std::fmt::Debug);
|
debug_display!(Spanned; T where T: std::fmt::Debug);
|
||||||
|
|
||||||
/// Describes a slice of source code.
|
/// Describes a slice of source code.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct Span {
|
pub struct Span {
|
||||||
pub start: Position,
|
pub start: Position,
|
||||||
pub end: Position,
|
pub end: Position,
|
||||||
|
@ -45,7 +45,7 @@ pub enum Token<'s> {
|
|||||||
/// An identifier in a function header: `center`.
|
/// An identifier in a function header: `center`.
|
||||||
ExprIdent(&'s str),
|
ExprIdent(&'s str),
|
||||||
/// A quoted string in a function header: `"..."`.
|
/// A quoted string in a function header: `"..."`.
|
||||||
ExprString(&'s str),
|
ExprStr(&'s str),
|
||||||
/// A number in a function header: `3.14`.
|
/// A number in a function header: `3.14`.
|
||||||
ExprNumber(f64),
|
ExprNumber(f64),
|
||||||
/// A size in a function header: `12pt`.
|
/// A size in a function header: `12pt`.
|
||||||
@ -220,7 +220,7 @@ impl<'s> Tokens<'s> {
|
|||||||
|
|
||||||
fn parse_string(&mut self) -> Token<'s> {
|
fn parse_string(&mut self) -> Token<'s> {
|
||||||
let mut escaped = false;
|
let mut escaped = false;
|
||||||
ExprString(self.read_string_until(|n| {
|
ExprStr(self.read_string_until(|n| {
|
||||||
if n == '"' && !escaped {
|
if n == '"' && !escaped {
|
||||||
return true;
|
return true;
|
||||||
} else if n == '\\' {
|
} else if n == '\\' {
|
||||||
|
@ -10,7 +10,7 @@ use Token::{
|
|||||||
LeftParen as LP, RightParen as RP,
|
LeftParen as LP, RightParen as RP,
|
||||||
LeftBrace as LBR, RightBrace as RBR,
|
LeftBrace as LBR, RightBrace as RBR,
|
||||||
Colon as CL, Comma as CM, Equals as EQ,
|
Colon as CL, Comma as CM, Equals as EQ,
|
||||||
ExprIdent as ID, ExprString as STR, ExprSize as SIZE,
|
ExprIdent as ID, ExprStr as STR, ExprSize as SIZE,
|
||||||
ExprNumber as NUM, ExprBool as BOOL,
|
ExprNumber as NUM, ExprBool as BOOL,
|
||||||
Star as ST, Underscore as U, Backtick as B, Text as T,
|
Star as ST, Underscore as U, Backtick as B, Text as T,
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user