Refactor expressions and create tuples and objects 🧮

This commit is contained in:
Laurenz 2020-01-13 14:36:40 +01:00
parent 6527d31dfb
commit dde69276d4
11 changed files with 266 additions and 182 deletions

View File

@ -16,7 +16,7 @@ pub mod prelude {
pub use crate::layout::prelude::*;
pub use crate::syntax::{
ParseContext, ParseResult,
SyntaxTree, FuncCall, FuncArgs, PosArg, KeyArg,
SyntaxTree, FuncCall, FuncArgs,
Expression, Ident, ExpressionKind,
Spanned, Span
};

View File

@ -34,6 +34,13 @@ key!(AxisKey, "axis",
"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.
#[derive(Debug, Clone, PartialEq)]
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> {
/// 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.
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();
for arg in args.keys() {
let key = match arg.v.key.v.as_str() {
for arg in args.iter_keys() {
let key = match arg.key.v.as_str() {
"width" | "w" => AxisKey::Specific(Horizontal),
"height" | "h" => AxisKey::Specific(Vertical),
"primary-size" | "ps" => AxisKey::Generic(Primary),
"secondary-size" | "ss" => AxisKey::Generic(Secondary),
_ => if enforce {
_ => if all {
error!("expected dimension")
} else {
args.add_key(arg);
args.add_key_pair(arg);
continue;
}
};
let e = E::from_expr(arg.v.value)?;
let e = E::from_expr(arg.value)?;
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::Second, args.get_pos_opt::<E>()?)?;
for arg in args.keys() {
let axis = AxisKey::from_ident(&arg.v.key)?;
let value = E::from_expr(arg.v.value)?;
for arg in args.iter_keys() {
let axis = AxisKey::from_ident(&arg.key)?;
let value = E::from_expr(arg.value)?;
map.add(PosAxisKey::Keyword(axis), value)?;
}

View File

@ -36,6 +36,37 @@ pub_use_mod!(axis);
pub_use_mod!(alignment);
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.
#[derive(Debug, Clone, PartialEq)]
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()
}
}
key!(Direction, "direction",
"left-to-right" | "ltr" => LeftToRight,
"right-to-left" | "rtl" => RightToLeft,
"top-to-bottom" | "ttb" => TopToBottom,
"bottom-to-top" | "btt" => BottomToTop,
);

View File

@ -46,11 +46,12 @@ impl PaddingMap {
/// Parse a padding map from the function args.
pub fn new(args: &mut FuncArgs) -> ParseResult<PaddingMap> {
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() {
let key = PaddingKey::from_ident(&arg.v.key)?;
let size = Option::<PSize>::from_expr(arg.v.value)?;
for arg in args.iter_keys() {
let key = PaddingKey::from_ident(&arg.key)?;
let size = DefaultKey::<PSize>::from_expr(arg.value)?.into();
map.add(key, size)?;
}

View File

@ -62,7 +62,7 @@ function! {
FontFamilyFunc {
body: parse!(optional: body, ctx),
list: {
args.pos().map(|arg| match arg.v {
args.iter_pos().map(|arg| match arg.v {
Expression::Str(s) |
Expression::Ident(Ident(s)) => Ok(s.to_lowercase()),
_ => error!("expected identifier or string"),
@ -118,7 +118,7 @@ function! {
FontWeightFunc {
body: parse!(optional: body, ctx),
weight: match args.get_pos::<Expression>()? {
Expression::Num(weight) => {
Expression::Number(weight) => {
let weight = weight.round() as i16;
FontWeight(
if weight < 100 { 100 }
@ -264,13 +264,15 @@ function! {
axis: AxisKey::Specific(axis),
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 {
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")
}
}

View File

@ -15,7 +15,7 @@ pub enum ColorToken {
Brace,
ExprIdent,
ExprString,
ExprStr,
ExprNumber,
ExprSize,
ExprBool,

View File

@ -1,125 +1,16 @@
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.
#[derive(Clone, PartialEq)]
pub enum Expression {
Ident(Ident),
Str(String),
Num(f64),
Number(f64),
Size(Size),
Bool(bool),
Tuple(Tuple),
Object(Object),
}
impl Display for Expression {
@ -128,20 +19,17 @@ impl Display for Expression {
match self {
Ident(i) => write!(f, "{}", i),
Str(s) => write!(f, "{:?}", s),
Num(n) => write!(f, "{}", n),
Number(n) => write!(f, "{}", n),
Size(s) => write!(f, "{}", s),
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.
#[derive(Clone, PartialEq)]
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Ident(pub String);
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!(Expression);
debug_display!(Tuple);
debug_display!(Object);
/// Kinds of expressions.
pub trait ExpressionKind: Sized {
/// The name of the expression in an `expected <name>` error.
const NAME: &'static str;
/// Create from expression.
fn from_expr(expr: Spanned<Expression>) -> ParseResult<Self>;
}
/// Implements the expression kind trait for a type.
macro_rules! kind {
($type:ty, $name:expr, $($patterns:tt)*) => {
impl ExpressionKind for $type {
@ -190,15 +160,18 @@ macro_rules! kind {
};
}
kind!(Expression, "expression", e => e);
kind!(Ident, "identifier", Expression::Ident(ident) => ident);
kind!(String, "string", Expression::Str(string) => string);
kind!(f64, "number", Expression::Num(num) => num);
kind!(bool, "boolean", Expression::Bool(boolean) => boolean);
kind!(Size, "size", Expression::Size(size) => size);
kind!(Expression, "expression", e => e);
kind!(Ident, "identifier", Expression::Ident(ident) => ident);
kind!(String, "string", Expression::Str(string) => string);
kind!(f64, "number", Expression::Number(num) => num);
kind!(bool, "boolean", Expression::Bool(boolean) => boolean);
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",
Expression::Size(size) => ScaleSize::Absolute(size),
Expression::Num(scale) => ScaleSize::Scaled(scale as f32)
Expression::Size(size) => ScaleSize::Absolute(size),
Expression::Number(scale) => ScaleSize::Scaled(scale as f32)
);
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>> {
let span = expr.span;
T::from_expr(expr)
.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))
T::from_expr(expr).map(|v| Spanned { v, span })
}
}

View File

@ -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.
pub fn parse(src: &str, ctx: ParseContext) -> SyntaxTree {
Parser::new(src, ctx).parse()
@ -346,7 +439,7 @@ impl<'s> Parser<'s> {
Comma => Some(ColorToken::Comma),
Equals => Some(ColorToken::Equals),
ExprIdent(_) => Some(ColorToken::ExprIdent),
ExprString(_) => Some(ColorToken::ExprString),
ExprStr(_) => Some(ColorToken::ExprStr),
ExprNumber(_) => Some(ColorToken::ExprNumber),
ExprSize(_) => Some(ColorToken::ExprSize),
ExprBool(_) => Some(ColorToken::ExprBool),
@ -387,7 +480,7 @@ fn name(token: Token) -> &'static str {
Comma => "comma",
Equals => "equals sign",
ExprIdent(_) => "identifier",
ExprString(_) => "string",
ExprStr(_) => "string",
ExprNumber(_) => "number",
ExprSize(_) => "size",
ExprBool(_) => "bool",

View File

@ -4,7 +4,7 @@ use std::fmt::{self, Display, Formatter};
/// 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 v: T,
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);
/// Describes a slice of source code.
#[derive(Copy, Clone, Eq, PartialEq)]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Span {
pub start: Position,
pub end: Position,

View File

@ -45,7 +45,7 @@ pub enum Token<'s> {
/// An identifier in a function header: `center`.
ExprIdent(&'s str),
/// A quoted string in a function header: `"..."`.
ExprString(&'s str),
ExprStr(&'s str),
/// A number in a function header: `3.14`.
ExprNumber(f64),
/// A size in a function header: `12pt`.
@ -220,7 +220,7 @@ impl<'s> Tokens<'s> {
fn parse_string(&mut self) -> Token<'s> {
let mut escaped = false;
ExprString(self.read_string_until(|n| {
ExprStr(self.read_string_until(|n| {
if n == '"' && !escaped {
return true;
} else if n == '\\' {

View File

@ -10,7 +10,7 @@ use Token::{
LeftParen as LP, RightParen as RP,
LeftBrace as LBR, RightBrace as RBR,
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,
Star as ST, Underscore as U, Backtick as B, Text as T,
};