summaryrefslogtreecommitdiff
path: root/src/parse
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-10-10 22:19:36 +0200
committerLaurenz <laurmaedje@gmail.com>2020-10-10 22:19:36 +0200
commit92c01da36016e94ff20163806ddcbcf7e33d4031 (patch)
tree1a900b3c11edcc93e9153fada3ce92310db5768b /src/parse
parent42500d5ed85539c5ab04dd3544beaf802da29be9 (diff)
Switch back to custom geometry types, unified with layout primitives 🏞
Diffstat (limited to 'src/parse')
-rw-r--r--src/parse/mod.rs2
-rw-r--r--src/parse/tests.rs33
-rw-r--r--src/parse/tokens.rs60
3 files changed, 64 insertions, 31 deletions
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 3d980d7c..90fdbf5d 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -432,7 +432,7 @@ fn value(p: &mut Parser) -> Option<Expr> {
Token::Bool(b) => Expr::Lit(Lit::Bool(b)),
Token::Int(i) => Expr::Lit(Lit::Int(i)),
Token::Float(f) => Expr::Lit(Lit::Float(f)),
- Token::Length(l) => Expr::Lit(Lit::Length(l)),
+ Token::Length(val, unit) => Expr::Lit(Lit::Length(val, unit)),
Token::Percent(p) => Expr::Lit(Lit::Percent(p)),
Token::Hex(hex) => Expr::Lit(Lit::Color(color(p, hex, start))),
Token::Str(token) => Expr::Lit(Lit::Str(string(p, token))),
diff --git a/src/parse/tests.rs b/src/parse/tests.rs
index d76c6dca..e0712b36 100644
--- a/src/parse/tests.rs
+++ b/src/parse/tests.rs
@@ -8,7 +8,7 @@ use super::parse;
use crate::color::RgbaColor;
use crate::diag::Deco;
use crate::eval::DictKey;
-use crate::length::Length;
+use crate::geom::Unit;
use crate::syntax::*;
// ------------------------------ Construct Syntax Nodes ------------------------------ //
@@ -51,6 +51,7 @@ macro_rules! F {
use BinOp::*;
use UnOp::*;
+use Unit::*;
fn Id(ident: &str) -> Expr {
Expr::Lit(Lit::Ident(Ident(ident.to_string())))
@@ -67,8 +68,8 @@ fn Float(float: f64) -> Expr {
fn Percent(percent: f64) -> Expr {
Expr::Lit(Lit::Percent(percent))
}
-fn Len(length: Length) -> Expr {
- Expr::Lit(Lit::Length(length))
+fn Length(val: f64, unit: Unit) -> Expr {
+ Expr::Lit(Lit::Length(val, unit))
}
fn Color(color: RgbaColor) -> Expr {
Expr::Lit(Lit::Color(color))
@@ -347,10 +348,10 @@ fn test_parse_chaining() {
// Things the parser has to make sense of
t!("[hi: (5.0, 2.1 >> you]" => F!("hi"; Dict![Float(5.0), Float(2.1)], Tree![F!("you")]));
t!("[box >> pad: 1pt][Hi]" => F!("box"; Tree![
- F!("pad"; Len(Length::pt(1.0)), Tree!(T("Hi")))
+ F!("pad"; Length(1.0, Pt), Tree!(T("Hi")))
]));
t!("[bold: 400, >> emph >> sub: 1cm]" => F!("bold"; Int(400), Tree![
- F!("emph"; Tree!(F!("sub"; Len(Length::cm(1.0)))))
+ F!("emph"; Tree!(F!("sub"; Length(1.0, Cm))))
]));
// Errors for unclosed / empty predecessor groups
@@ -411,8 +412,8 @@ fn test_parse_values() {
v!("1.0e-4" => Float(1e-4));
v!("3.15" => Float(3.15));
v!("50%" => Percent(50.0));
- v!("4.5cm" => Len(Length::cm(4.5)));
- v!("12e1pt" => Len(Length::pt(12e1)));
+ v!("4.5cm" => Length(4.5, Cm));
+ v!("12e1pt" => Length(12e1, Pt));
v!("#f7a20500" => Color(RgbaColor::new(0xf7, 0xa2, 0x05, 0x00)));
v!("\"a\n[]\\\"string\"" => Str("a\n[]\"string"));
@@ -446,15 +447,15 @@ fn test_parse_expressions() {
// Operations.
v!("-1" => Unary(Neg, Int(1)));
v!("-- 1" => Unary(Neg, Unary(Neg, Int(1))));
- v!("3.2in + 6pt" => Binary(Add, Len(Length::inches(3.2)), Len(Length::pt(6.0))));
+ v!("3.2in + 6pt" => Binary(Add, Length(3.2, In), Length(6.0, Pt)));
v!("5 - 0.01" => Binary(Sub, Int(5), Float(0.01)));
- v!("(3mm * 2)" => Binary(Mul, Len(Length::mm(3.0)), Int(2)));
- v!("12e-3cm/1pt" => Binary(Div, Len(Length::cm(12e-3)), Len(Length::pt(1.0))));
+ v!("(3mm * 2)" => Binary(Mul, Length(3.0, Mm), Int(2)));
+ v!("12e-3cm/1pt" => Binary(Div, Length(12e-3, Cm), Length(1.0, Pt)));
// More complex.
v!("(3.2in + 6pt)*(5/2-1)" => Binary(
Mul,
- Binary(Add, Len(Length::inches(3.2)), Len(Length::pt(6.0))),
+ Binary(Add, Length(3.2, In), Length(6.0, Pt)),
Binary(Sub, Binary(Div, Int(5), Int(2)), Int(1))
));
v!("(6.3E+2+4* - 3.2pt)/2" => Binary(
@@ -462,7 +463,7 @@ fn test_parse_expressions() {
Binary(Add, Float(6.3e2), Binary(
Mul,
Int(4),
- Unary(Neg, Len(Length::pt(3.2)))
+ Unary(Neg, Length(3.2, Pt))
)),
Int(2)
));
@@ -483,11 +484,11 @@ fn test_parse_expressions() {
ts!("[val: (1)]" => s(0, 10, F!(s(1, 4, "val"), 5 .. 9; s(6, 9, Int(1)))));
// Invalid expressions.
- v!("4pt--" => Len(Length::pt(4.0)));
+ v!("4pt--" => Length(4.0, Pt));
e!("[val: 4pt--]" => s(10, 11, "missing factor"),
s(6, 10, "missing right summand"));
- v!("3mm+4pt*" => Binary(Add, Len(Length::mm(3.0)), Len(Length::pt(4.0))));
+ v!("3mm+4pt*" => Binary(Add, Length(3.0, Mm), Length(4.0, Pt)));
e!("[val: 3mm+4pt*]" => s(10, 14, "missing right factor"));
}
@@ -525,7 +526,7 @@ fn test_parse_dicts_compute_func_calls() {
// More complex.
v!("css(1pt, rgb(90, 102, 254), \"solid\")" => Call!(
"css";
- Len(Length::pt(1.0)),
+ Length(1.0, Pt),
Call!("rgb"; Int(90), Int(102), Int(254)),
Str("solid"),
));
@@ -546,7 +547,7 @@ fn test_parse_dicts_nested() {
Int(1),
Dict!(
"ab" => Dict![],
- "d" => Dict!(Int(3), Len(Length::pt(14.0))),
+ "d" => Dict!(Int(3), Length(14.0, Pt)),
),
],
Bool(false),
diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs
index 2ff580b6..f53ab586 100644
--- a/src/parse/tokens.rs
+++ b/src/parse/tokens.rs
@@ -3,7 +3,7 @@
use std::fmt::{self, Debug, Formatter};
use super::{is_newline, Scanner};
-use crate::length::Length;
+use crate::geom::Unit;
use crate::syntax::token::*;
use crate::syntax::{is_ident, Pos};
@@ -279,8 +279,8 @@ fn parse_expr(text: &str) -> Token<'_> {
Token::Float(num)
} else if let Some(percent) = parse_percent(text) {
Token::Percent(percent)
- } else if let Ok(length) = text.parse::<Length>() {
- Token::Length(length)
+ } else if let Some((val, unit)) = parse_length(text) {
+ Token::Length(val, unit)
} else if is_ident(text) {
Token::Ident(text)
} else {
@@ -292,19 +292,41 @@ fn parse_percent(text: &str) -> Option<f64> {
text.strip_suffix('%').and_then(|num| num.parse::<f64>().ok())
}
+fn parse_length(text: &str) -> Option<(f64, Unit)> {
+ let len = text.len();
+
+ // We need at least some number and the unit.
+ if len <= 2 {
+ return None;
+ }
+
+ // We can view the string as bytes since a multibyte UTF-8 char cannot
+ // have valid ASCII chars as subbytes.
+ let split = len - 2;
+ let bytes = text.as_bytes();
+ let unit = match &bytes[split ..] {
+ b"pt" => Unit::Pt,
+ b"mm" => Unit::Mm,
+ b"cm" => Unit::Cm,
+ b"in" => Unit::In,
+ _ => return None,
+ };
+
+ text[.. split].parse::<f64>().ok().map(|val| (val, unit))
+}
+
#[cfg(test)]
#[allow(non_snake_case)]
mod tests {
use super::*;
- use crate::length::Length;
use crate::parse::tests::check;
use Token::{
- BlockComment as BC, Bool, Chain, Float, Hex, Hyphen as Min, Ident as Id, Int,
- LeftBrace as LB, LeftBracket as L, LeftParen as LP, Length as Len,
- LineComment as LC, NonBreakingSpace as Nbsp, Percent, Plus, RightBrace as RB,
- RightBracket as R, RightParen as RP, Slash, Space as S, Star, Text as T, *,
+ BlockComment as BC, Hyphen as Min, Ident as Id, LeftBrace as LB,
+ LeftBracket as L, LeftParen as LP, LineComment as LC, NonBreakingSpace as Nbsp,
+ RightBrace as RB, RightBracket as R, RightParen as RP, Space as S, Text as T, *,
};
+ use Unit::*;
fn Str(string: &str, terminated: bool) -> Token {
Token::Str(TokenStr { string, terminated })
@@ -325,6 +347,16 @@ mod tests {
}
#[test]
+ fn test_length_from_str_parses_correct_value_and_unit() {
+ assert_eq!(parse_length("2.5cm"), Some((2.5, Cm)));
+ }
+
+ #[test]
+ fn test_length_from_str_works_with_non_ascii_chars() {
+ assert_eq!(parse_length("123🚚"), None);
+ }
+
+ #[test]
fn tokenize_whitespace() {
t!(Body, "" => );
t!(Body, " " => S(0));
@@ -429,7 +461,7 @@ mod tests {
t!(Header, "🌓, 🌍," => Invalid("🌓"), Comma, S(0), Invalid("🌍"), Comma);
t!(Header, "{abc}" => LB, Id("abc"), RB);
t!(Header, "(1,2)" => LP, Int(1), Comma, Int(2), RP);
- t!(Header, "12_pt, 12pt" => Invalid("12_pt"), Comma, S(0), Len(Length::pt(12.0)));
+ t!(Header, "12_pt, 12pt" => Invalid("12_pt"), Comma, S(0), Length(12.0, Pt));
t!(Header, "f: arg >> g" => Id("f"), Colon, S(0), Id("arg"), S(0), Chain, S(0), Id("g"));
t!(Header, "=3.15" => Equals, Float(3.15));
t!(Header, "arg, _b, _1" => Id("arg"), Comma, S(0), Id("_b"), Comma, S(0), Id("_1"));
@@ -446,9 +478,9 @@ mod tests {
t!(Header, "12.3e5" => Float(12.3e5));
t!(Header, "120%" => Percent(120.0));
t!(Header, "12e4%" => Percent(120000.0));
- t!(Header, "1e5in" => Len(Length::inches(100000.0)));
- t!(Header, "2.3cm" => Len(Length::cm(2.3)));
- t!(Header, "02.4mm" => Len(Length::mm(2.4)));
+ t!(Header, "1e5in" => Length(100000.0, In));
+ t!(Header, "2.3cm" => Length(2.3, Cm));
+ t!(Header, "02.4mm" => Length(2.4, Mm));
t!(Header, "2.4.cm" => Invalid("2.4.cm"));
t!(Header, "#6ae6dd" => Hex("6ae6dd"));
t!(Header, "#8A083c" => Hex("8A083c"));
@@ -469,11 +501,11 @@ mod tests {
#[test]
fn tokenize_math() {
- t!(Header, "12e-3in" => Len(Length::inches(12e-3)));
+ t!(Header, "12e-3in" => Length(12e-3, In));
t!(Header, "-1" => Min, Int(1));
t!(Header, "--1" => Min, Min, Int(1));
t!(Header, "- 1" => Min, S(0), Int(1));
- t!(Header, "6.1cm + 4pt,a=1*2" => Len(Length::cm(6.1)), S(0), Plus, S(0), Len(Length::pt(4.0)),
+ t!(Header, "6.1cm + 4pt,a=1*2" => Length(6.1, Cm), S(0), Plus, S(0), Length(4.0, Pt),
Comma, Id("a"), Equals, Int(1), Star, Int(2));
t!(Header, "(5 - 1) / 2.1" => LP, Int(5), S(0), Min, S(0), Int(1), RP,
S(0), Slash, S(0), Float(2.1));