Introduce frs

This commit is contained in:
Martin Haug 2021-06-01 14:56:02 +02:00 committed by Laurenz
parent 9983634cd5
commit 73fa2eda2c
13 changed files with 143 additions and 4 deletions

View File

@ -18,7 +18,7 @@ use std::rc::Rc;
use crate::cache::Cache;
use crate::color::Color;
use crate::diag::{Diag, DiagSet, Pass};
use crate::geom::{Angle, Length, Relative};
use crate::geom::{Angle, Fractional, Length, Relative};
use crate::loading::{FileHash, Loader};
use crate::parse::parse;
use crate::syntax::visit::Visit;
@ -250,6 +250,7 @@ impl Eval for Expr {
Self::Length(_, v, unit) => Value::Length(Length::with_unit(v, unit)),
Self::Angle(_, v, unit) => Value::Angle(Angle::with_unit(v, unit)),
Self::Percent(_, v) => Value::Relative(Relative::new(v / 100.0)),
Self::Fractional(_, v) => Value::Fractional(Fractional::new(v)),
Self::Color(_, v) => Value::Color(Color::Rgba(v)),
Self::Str(_, ref v) => Value::Str(v.clone()),
Self::Ident(ref v) => match ctx.scopes.get(&v) {

View File

@ -11,6 +11,7 @@ pub fn pos(value: Value) -> Value {
Length(v) => Length(v),
Angle(v) => Angle(v),
Relative(v) => Relative(v),
Fractional(v) => Fractional(v),
Linear(v) => Linear(v),
_ => Error,
}
@ -24,6 +25,7 @@ pub fn neg(value: Value) -> Value {
Length(v) => Length(-v),
Angle(v) => Angle(-v),
Relative(v) => Relative(-v),
Fractional(v) => Fractional(-v),
Linear(v) => Linear(-v),
_ => Error,
}
@ -44,6 +46,7 @@ pub fn add(lhs: Value, rhs: Value) -> Value {
(Relative(a), Length(b)) => Linear(a + b),
(Relative(a), Relative(b)) => Relative(a + b),
(Relative(a), Linear(b)) => Linear(a + b),
(Fractional(a), Fractional(b)) => Fractional(a + b),
(Linear(a), Length(b)) => Linear(a + b),
(Linear(a), Relative(b)) => Linear(a + b),
(Linear(a), Linear(b)) => Linear(a + b),
@ -84,6 +87,7 @@ pub fn sub(lhs: Value, rhs: Value) -> Value {
(Relative(a), Length(b)) => Linear(a - b),
(Relative(a), Relative(b)) => Relative(a - b),
(Relative(a), Linear(b)) => Linear(a - b),
(Fractional(a), Fractional(b)) => Fractional(a - b),
(Linear(a), Length(b)) => Linear(a - b),
(Linear(a), Relative(b)) => Linear(a - b),
(Linear(a), Linear(b)) => Linear(a - b),
@ -108,8 +112,13 @@ pub fn mul(lhs: Value, rhs: Value) -> Value {
(Float(a), Angle(b)) => Angle(a * b),
(Relative(a), Int(b)) => Relative(a * b as f64),
(Relative(a), Float(b)) => Relative(a * b),
(Fractional(a), Fractional(b)) => Fractional(a * b.get()),
(Fractional(a), Int(b)) => Fractional(a * b as f64),
(Fractional(a), Float(b)) => Fractional(a * b),
(Int(a), Relative(b)) => Relative(a as f64 * b),
(Int(a), Fractional(b)) => Fractional(a as f64 * b),
(Float(a), Relative(b)) => Relative(a * b),
(Float(a), Fractional(b)) => Fractional(a * b),
(Linear(a), Int(b)) => Linear(a * b as f64),
(Linear(a), Float(b)) => Linear(a * b),
(Int(a), Linear(b)) => Linear(a as f64 * b),
@ -134,6 +143,9 @@ pub fn div(lhs: Value, rhs: Value) -> Value {
(Relative(a), Int(b)) => Relative(a / b as f64),
(Relative(a), Float(b)) => Relative(a / b),
(Relative(a), Relative(b)) => Float(a / b),
(Fractional(a), Fractional(b)) => Float(a.get() / b.get()),
(Fractional(a), Int(b)) => Fractional(a / b as f64),
(Fractional(a), Float(b)) => Fractional(a / b),
(Linear(a), Int(b)) => Linear(a / b as f64),
(Linear(a), Float(b)) => Linear(a / b),
_ => Error,

View File

@ -8,7 +8,7 @@ use std::rc::Rc;
use super::EvalContext;
use crate::color::{Color, RgbaColor};
use crate::exec::ExecContext;
use crate::geom::{Angle, Length, Linear, Relative};
use crate::geom::{Angle, Fractional, Length, Linear, Relative};
use crate::syntax::{Expr, Span, Spanned, Tree};
/// A computational value.
@ -28,6 +28,8 @@ pub enum Value {
Angle(Angle),
/// A relative value: `50%`.
Relative(Relative),
/// A fractional value: `1fr`.
Fractional(Fractional),
/// A combination of an absolute length and a relative value: `20% + 5cm`.
Linear(Linear),
/// A color value: `#f79143ff`.
@ -75,6 +77,7 @@ impl Value {
Self::Length(_) => Length::TYPE_NAME,
Self::Angle(_) => Angle::TYPE_NAME,
Self::Relative(_) => Relative::TYPE_NAME,
Self::Fractional(_) => Fractional::TYPE_NAME,
Self::Linear(_) => Linear::TYPE_NAME,
Self::Color(_) => Color::TYPE_NAME,
Self::Str(_) => String::TYPE_NAME,
@ -601,6 +604,7 @@ primitive! {
primitive! { Length: "length", Value::Length }
primitive! { Angle: "angle", Value::Angle }
primitive! { Relative: "relative", Value::Relative }
primitive! { Fractional: "fractional", Value::Fractional }
primitive! {
Linear: "linear",
Value::Linear,

101
src/geom/fr.rs Normal file
View File

@ -0,0 +1,101 @@
use decorum::N64;
use super::*;
/// A fractional length.
#[derive(Default, Copy, Clone, PartialEq, PartialOrd, Hash)]
pub struct Fractional(N64);
impl Fractional {
/// Takes up zero space: `0fr`.
pub fn zero() -> Self {
Self(N64::from(0.0))
}
/// Takes up as much space as all other items with this fractional size: `1fr`.
pub fn one() -> Self {
Self(N64::from(1.0))
}
/// Create a new fractional value.
pub fn new(ratio: f64) -> Self {
Self(N64::from(ratio))
}
/// Get the underlying ratio.
pub fn get(self) -> f64 {
self.0.into()
}
/// Whether the ratio is zero.
pub fn is_zero(self) -> bool {
self.0 == 0.0
}
}
impl Display for Fractional {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}fr", self.get())
}
}
impl Debug for Fractional {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl Neg for Fractional {
type Output = Self;
fn neg(self) -> Self {
Self(-self.0)
}
}
impl Add for Fractional {
type Output = Self;
fn add(self, other: Self) -> Self {
Self(self.0 + other.0)
}
}
sub_impl!(Fractional - Fractional -> Fractional);
impl Mul<f64> for Fractional {
type Output = Self;
fn mul(self, other: f64) -> Self {
Self(self.0 * other)
}
}
impl Mul<Fractional> for f64 {
type Output = Fractional;
fn mul(self, other: Fractional) -> Fractional {
other * self
}
}
impl Div<f64> for Fractional {
type Output = Self;
fn div(self, other: f64) -> Self {
Self(self.0 / other)
}
}
impl Div for Fractional {
type Output = f64;
fn div(self, other: Self) -> f64 {
self.get() / other.get()
}
}
assign_impl!(Fractional += Fractional);
assign_impl!(Fractional -= Fractional);
assign_impl!(Fractional *= f64);
assign_impl!(Fractional /= f64);

View File

@ -5,6 +5,7 @@ mod macros;
mod align;
mod angle;
mod dir;
mod fr;
mod gen;
mod length;
mod linear;
@ -18,6 +19,7 @@ mod spec;
pub use align::*;
pub use angle::*;
pub use dir::*;
pub use fr::*;
pub use gen::*;
pub use length::*;
pub use linear::*;

View File

@ -335,6 +335,7 @@ fn literal(p: &mut Parser) -> Option<Expr> {
Token::Length(val, unit) => Expr::Length(span, val, unit),
Token::Angle(val, unit) => Expr::Angle(span, val, unit),
Token::Percent(p) => Expr::Percent(span, p),
Token::Fraction(p) => Expr::Fractional(span, p),
Token::Color(color) => Expr::Color(span, color),
Token::Str(token) => Expr::Str(span, {
if !token.terminated {

View File

@ -393,6 +393,7 @@ impl<'s> Tokens<'s> {
// Otherwise parse into the fitting numeric type.
let build = match suffix {
"%" => Token::Percent,
"fr" => Token::Fraction,
"pt" => |x| Token::Length(x, LengthUnit::Pt),
"mm" => |x| Token::Length(x, LengthUnit::Mm),
"cm" => |x| Token::Length(x, LengthUnit::Cm),
@ -880,6 +881,7 @@ mod tests {
let suffixes = [
("%", Percent as fn(f64) -> Token<'static>),
("fr", Fraction as fn(f64) -> Token<'static>),
("mm", |x| Length(x, LengthUnit::Mm)),
("pt", |x| Length(x, LengthUnit::Pt)),
("cm", |x| Length(x, LengthUnit::Cm)),

View File

@ -4,7 +4,7 @@ use std::fmt::{self, Arguments, Write};
use crate::color::{Color, RgbaColor};
use crate::eval::*;
use crate::geom::{Angle, Length, Linear, Relative};
use crate::geom::{Angle, Fractional, Length, Linear, Relative};
use crate::syntax::*;
/// Pretty print an item and return the resulting string.
@ -192,6 +192,7 @@ impl Pretty for Expr {
Self::Length(_, v, u) => write!(p, "{}{}", v, u).unwrap(),
Self::Angle(_, v, u) => write!(p, "{}{}", v, u).unwrap(),
Self::Percent(_, v) => write!(p, "{}%", v).unwrap(),
Self::Fractional(_, v) => write!(p, "{}fr", v).unwrap(),
Self::Color(_, v) => v.pretty(p),
Self::Str(_, v) => v.pretty(p),
Self::Ident(v) => v.pretty(p),
@ -456,6 +457,7 @@ impl Pretty for Value {
Value::Length(v) => v.pretty(p),
Value::Angle(v) => v.pretty(p),
Value::Relative(v) => v.pretty(p),
Value::Fractional(v) => v.pretty(p),
Value::Linear(v) => v.pretty(p),
Value::Color(v) => v.pretty(p),
Value::Str(v) => v.pretty(p),
@ -575,6 +577,7 @@ pretty_display! {
Length,
Angle,
Relative,
Fractional,
Linear,
RgbaColor,
Color,
@ -659,6 +662,7 @@ mod tests {
roundtrip("{10pt}");
roundtrip("{14.1deg}");
roundtrip("{20%}");
roundtrip("{0.5fr}");
roundtrip("{#abcdef}");
roundtrip(r#"{"hi"}"#);
test_parse(r#"{"let's \" go"}"#, r#"{"let's \" go"}"#);
@ -725,6 +729,7 @@ mod tests {
test_value(Angle::deg(90.0), "90deg");
test_value(Relative::one() / 2.0, "50%");
test_value(Relative::new(0.3) + Length::cm(2.0), "30% + 2cm");
test_value(Fractional::one() * 7.55, "7.55fr");
test_value(Color::Rgba(RgbaColor::new(1, 1, 1, 0xff)), "#010101");
test_value("hello", r#""hello""#);
test_value("\n", r#""\n""#);

View File

@ -24,6 +24,8 @@ pub enum Expr {
/// _Note_: `50%` is stored as `50.0` here, but as `0.5` in the
/// corresponding [value](crate::geom::Relative).
Percent(Span, f64),
/// A fraction unit literal: `1fr`.
Fractional(Span, f64),
/// A color literal: `#ffccee`.
Color(Span, RgbaColor),
/// A string literal: `"hello!"`.
@ -73,6 +75,7 @@ impl Expr {
Self::Length(span, _, _) => span,
Self::Angle(span, _, _) => span,
Self::Percent(span, _) => span,
Self::Fractional(span, _) => span,
Self::Color(span, _) => span,
Self::Str(span, _) => span,
Self::Ident(ref v) => v.span,

View File

@ -133,6 +133,8 @@ pub enum Token<'s> {
/// _Note_: `50%` is stored as `50.0` here, as in the corresponding
/// [literal](super::Expr::Percent).
Percent(f64),
/// A fraction unit: `3fr`.
Fraction(f64),
/// A color value: `#20d82a`.
Color(RgbaColor),
/// A quoted string: `"..."`.
@ -258,6 +260,7 @@ impl<'s> Token<'s> {
Self::Length(_, _) => "length",
Self::Angle(_, _) => "angle",
Self::Percent(_) => "percentage",
Self::Fraction(_) => "`fr` value",
Self::Color(_) => "color",
Self::Str(_) => "string",
Self::LineComment(_) => "line comment",

View File

@ -80,6 +80,7 @@ visit! {
Expr::Length(_, _, _) => {}
Expr::Angle(_, _, _) => {}
Expr::Percent(_, _) => {}
Expr::Fractional(_, _) => {}
Expr::Color(_, _) => {}
Expr::Str(_, _) => {}
Expr::Ident(_) => {}

View File

@ -10,7 +10,7 @@
// Test math operators.
// Test plus and minus.
#for v in (1, 3.14, 12pt, 45deg, 90%, 13% + 10pt) {
#for v in (1, 3.14, 12pt, 45deg, 90%, 13% + 10pt, 6.3fr) {
// Test plus.
test(+v, v)

View File

@ -275,6 +275,10 @@
"name": "constant.numeric.percentage.typst",
"match": "\\b(\\d*)?\\.?\\d+([eE][+-]?\\d+)?%"
},
{
"name": "constant.numeric.fr.typst",
"match": "\\b(\\d*)?\\.?\\d+([eE][+-]?\\d+)?fr"
},
{
"name": "constant.numeric.integer.typst",
"match": "\\b\\d+\\b"