summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/eval/mod.rs3
-rw-r--r--src/eval/ops.rs12
-rw-r--r--src/eval/value.rs6
-rw-r--r--src/geom/fr.rs101
-rw-r--r--src/geom/mod.rs2
-rw-r--r--src/parse/mod.rs1
-rw-r--r--src/parse/tokens.rs2
-rw-r--r--src/pretty.rs7
-rw-r--r--src/syntax/expr.rs3
-rw-r--r--src/syntax/token.rs3
-rw-r--r--src/syntax/visit.rs1
-rw-r--r--tests/typ/code/ops.typ2
-rw-r--r--tools/support/typst.tmLanguage.json4
13 files changed, 143 insertions, 4 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index d1307b6d..5bc2f101 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -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) {
diff --git a/src/eval/ops.rs b/src/eval/ops.rs
index 69a0b02b..15c09e03 100644
--- a/src/eval/ops.rs
+++ b/src/eval/ops.rs
@@ -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,
diff --git a/src/eval/value.rs b/src/eval/value.rs
index 94f7f569..498403e6 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -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,
diff --git a/src/geom/fr.rs b/src/geom/fr.rs
new file mode 100644
index 00000000..974d675e
--- /dev/null
+++ b/src/geom/fr.rs
@@ -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);
diff --git a/src/geom/mod.rs b/src/geom/mod.rs
index 0031c6df..ce8a7276 100644
--- a/src/geom/mod.rs
+++ b/src/geom/mod.rs
@@ -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::*;
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 048bcb1c..3d4cc2ac 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -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 {
diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs
index 74051801..9d3cbc9a 100644
--- a/src/parse/tokens.rs
+++ b/src/parse/tokens.rs
@@ -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)),
diff --git a/src/pretty.rs b/src/pretty.rs
index dc1e284c..df1d844c 100644
--- a/src/pretty.rs
+++ b/src/pretty.rs
@@ -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""#);
diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs
index e0135d1c..4dac9c59 100644
--- a/src/syntax/expr.rs
+++ b/src/syntax/expr.rs
@@ -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,
diff --git a/src/syntax/token.rs b/src/syntax/token.rs
index 9098f176..56ab3dd6 100644
--- a/src/syntax/token.rs
+++ b/src/syntax/token.rs
@@ -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",
diff --git a/src/syntax/visit.rs b/src/syntax/visit.rs
index 86481d4e..ba7555f2 100644
--- a/src/syntax/visit.rs
+++ b/src/syntax/visit.rs
@@ -80,6 +80,7 @@ visit! {
Expr::Length(_, _, _) => {}
Expr::Angle(_, _, _) => {}
Expr::Percent(_, _) => {}
+ Expr::Fractional(_, _) => {}
Expr::Color(_, _) => {}
Expr::Str(_, _) => {}
Expr::Ident(_) => {}
diff --git a/tests/typ/code/ops.typ b/tests/typ/code/ops.typ
index ef249c43..e2ecd4ff 100644
--- a/tests/typ/code/ops.typ
+++ b/tests/typ/code/ops.typ
@@ -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)
diff --git a/tools/support/typst.tmLanguage.json b/tools/support/typst.tmLanguage.json
index c7fdcb52..0b0298fe 100644
--- a/tools/support/typst.tmLanguage.json
+++ b/tools/support/typst.tmLanguage.json
@@ -276,6 +276,10 @@
"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"
},