From 17e89468847735df10381c47c46c7d82d33cc463 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 30 Jun 2021 22:32:24 +0200 Subject: [PATCH] Remove color literals (#39) --- src/color.rs | 4 +-- src/eval/mod.rs | 2 -- src/library/utility.rs | 43 +++++++++++++++----------- src/parse/mod.rs | 8 ++--- src/parse/tokens.rs | 56 +++++++++------------------------- src/pretty.rs | 2 -- src/syntax/expr.rs | 4 --- src/syntax/token.rs | 4 --- src/syntax/visit.rs | 1 - tests/typ/code/array.typ | 2 +- tests/typ/code/repr.typ | 2 +- tests/typ/insert/circle.typ | 6 ++-- tests/typ/insert/ellipse.typ | 2 +- tests/typ/insert/rect.typ | 14 ++++----- tests/typ/insert/square.typ | 4 +-- tests/typ/layout/grid-1.typ | 24 +++++++-------- tests/typ/layout/grid-3.typ | 4 +-- tests/typ/layout/pad.typ | 2 +- tests/typ/layout/stack.typ | 2 +- tests/typ/text/decorations.typ | 13 +++----- tests/typ/text/font.typ | 2 +- tests/typ/utility/color.typ | 13 +++++--- 22 files changed, 89 insertions(+), 125 deletions(-) diff --git a/src/color.rs b/src/color.rs index c3ab5aa90..124c20423 100644 --- a/src/color.rs +++ b/src/color.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; /// A color in a dynamic format. #[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub enum Color { - /// An 8-bit RGBA color: `#423abaff`. + /// An 8-bit RGBA color. Rgba(RgbaColor), } @@ -28,7 +28,7 @@ impl Debug for Color { } } -/// An 8-bit RGBA color: `#423abaff`. +/// An 8-bit RGBA color. #[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct RgbaColor { /// Red channel. diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 8579025fe..96ac87a99 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -16,7 +16,6 @@ use std::path::{Path, PathBuf}; use std::rc::Rc; use crate::cache::Cache; -use crate::color::Color; use crate::diag::{Diag, DiagSet, Pass}; use crate::geom::{Angle, Fractional, Length, Relative}; use crate::loading::{FileHash, Loader}; @@ -248,7 +247,6 @@ impl Eval for Expr { 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) { Some(slot) => slot.borrow().clone(), diff --git a/src/library/utility.rs b/src/library/utility.rs index 146fce9c8..272183aac 100644 --- a/src/library/utility.rs +++ b/src/library/utility.rs @@ -1,4 +1,5 @@ use std::cmp::Ordering; +use std::str::FromStr; use crate::color::{Color, RgbaColor}; use crate::pretty::pretty; @@ -37,26 +38,32 @@ pub fn len(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { /// `rgb`: Create an RGB(A) color. pub fn rgb(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { - let r = args.expect(ctx, "red component"); - let g = args.expect(ctx, "green component"); - let b = args.expect(ctx, "blue component"); - let a = args.eat(ctx); - - let mut clamp = |component: Option>, default| { - component.map_or(default, |c| { - if c.v < 0.0 || c.v > 1.0 { - ctx.diag(warning!(c.span, "should be between 0.0 and 1.0")); + Value::Color(Color::Rgba( + if let Some(string) = args.eat::>(ctx) { + match RgbaColor::from_str(&string.v) { + Ok(color) => color, + Err(_) => { + ctx.diag(error!(string.span, "invalid color")); + return Value::Error; + } } - (c.v.max(0.0).min(1.0) * 255.0).round() as u8 - }) - }; + } else { + let r = args.expect(ctx, "red component"); + let g = args.expect(ctx, "green component"); + let b = args.expect(ctx, "blue component"); + let a = args.eat(ctx); + let mut clamp = |component: Option>, default| { + component.map_or(default, |c| { + if c.v < 0.0 || c.v > 1.0 { + ctx.diag(warning!(c.span, "should be between 0.0 and 1.0")); + } + (c.v.max(0.0).min(1.0) * 255.0).round() as u8 + }) + }; - Value::Color(Color::Rgba(RgbaColor::new( - clamp(r, 0), - clamp(g, 0), - clamp(b, 0), - clamp(a, 255), - ))) + RgbaColor::new(clamp(r, 0), clamp(g, 0), clamp(b, 0), clamp(a, 255)) + }, + )) } /// `min`: The minimum of a sequence of values. diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 0afcd88b1..a56e451d9 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -385,7 +385,6 @@ fn literal(p: &mut Parser) -> Option { 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 { p.expected_at("quote", p.peek_span().end); @@ -672,10 +671,9 @@ fn if_expr(p: &mut Parser) -> Option { // We are in code mode but still want to react to `#else` if the // outer mode is markup. - if match p.outer_mode() { - TokenMode::Markup => p.eat_if(Token::Invalid("#else")), - TokenMode::Code => p.eat_if(Token::Else), - } { + if (p.outer_mode() == TokenMode::Code || p.eat_if(Token::Invalid("#"))) + && p.eat_if(Token::Else) + { else_body = body(p); } diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index 4d90dded6..522b3136d 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -1,8 +1,6 @@ use std::fmt::{self, Debug, Formatter}; -use std::str::FromStr; use super::{is_newline, Scanner}; -use crate::color::RgbaColor; use crate::geom::{AngularUnit, LengthUnit}; use crate::syntax::*; @@ -71,9 +69,6 @@ impl<'s> Iterator for Tokens<'s> { '{' => Token::LeftBrace, '}' => Token::RightBrace, - // Headings, keywords, identifiers, colors. - '#' => self.hash(start), - // Whitespace. c if c.is_whitespace() => self.whitespace(c), @@ -104,6 +99,9 @@ impl<'s> Tokens<'s> { '-' => self.hyph(start), c if c == '.' || c.is_ascii_digit() => self.numbering(start, c), + // Headings, keywords and identifiers. + '#' => self.hash(start), + // Plain text. _ => self.text(start), } @@ -236,29 +234,17 @@ impl<'s> Tokens<'s> { } fn hash(&mut self, start: usize) -> Token<'s> { - match self.mode { - TokenMode::Markup => { - if self.s.check(is_id_start) { - let read = self.s.eat_while(is_id_continue); - if let Some(keyword) = keyword(read) { - keyword - } else { - Token::Ident(read) - } - } else if self.s.check(|c| c != '#' && !c.is_whitespace()) { - Token::Text(self.s.eaten_from(start)) - } else { - Token::Hashtag - } - } - TokenMode::Code => { - let read = self.s.eat_while(is_id_continue); - if let Ok(color) = RgbaColor::from_str(read) { - Token::Color(color) - } else { - Token::Invalid(self.s.eaten_from(start)) - } + if self.s.check(is_id_start) { + let read = self.s.eat_while(is_id_continue); + if let Some(keyword) = keyword(read) { + keyword + } else { + Token::Ident(read) } + } else if self.s.check(|c| c != '#' && !c.is_whitespace()) { + Token::Text(self.s.eaten_from(start)) + } else { + Token::Hashtag } } @@ -528,10 +514,6 @@ mod tests { Token::Math(MathToken { formula, display, terminated }) } - const fn Color(r: u8, g: u8, b: u8, a: u8) -> Token<'static> { - Token::Color(RgbaColor { r, g, b, a }) - } - const fn Str(string: &str, terminated: bool) -> Token { Token::Str(StrToken { string, terminated }) } @@ -583,7 +565,6 @@ mod tests { ('/', Some(Code), "(", LeftParen), ('/', Some(Code), ":", Colon), ('/', Some(Code), "+=", PlusEq), - ('/', Some(Code), "#123", Color(0x11, 0x22, 0x33, 0xff)), ]; macro_rules! t { @@ -926,13 +907,6 @@ mod tests { } } - #[test] - fn test_tokenize_color() { - t!(Code[" /"]: "#ABC" => Color(0xAA, 0xBB, 0xCC, 0xff)); - t!(Code[" /"]: "#6ae6dd" => Color(0x6a, 0xe6, 0xdd, 0xff)); - t!(Code[" /"]: "#8A083caf" => Color(0x8A, 0x08, 0x3c, 0xaf)); - } - #[test] fn test_tokenize_strings() { // Test basic strings. @@ -999,13 +973,11 @@ mod tests { t!(Code: r"\:" => Invalid(r"\"), Colon); t!(Code: "meal⌚" => Ident("meal"), Invalid("⌚")); t!(Code[" /"]: r"\a" => Invalid(r"\"), Ident("a")); + t!(Code[" /"]: "#" => Invalid("#")); // Test invalid number suffixes. t!(Code[" /"]: "1foo" => Invalid("1foo")); t!(Code: "1p%" => Invalid("1p"), Invalid("%")); t!(Code: "1%%" => Percent(1.0), Invalid("%")); - - // Test invalid color. - t!(Code[" /"]: r"#letter" => Invalid(r"#letter")); } } diff --git a/src/pretty.rs b/src/pretty.rs index ccf4d88d3..17609ceaf 100644 --- a/src/pretty.rs +++ b/src/pretty.rs @@ -205,7 +205,6 @@ impl Pretty for Expr { 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), Self::Array(v) => v.pretty(p), @@ -687,7 +686,6 @@ mod tests { roundtrip("{14.1deg}"); roundtrip("{20%}"); roundtrip("{0.5fr}"); - roundtrip("{#abcdef}"); roundtrip(r#"{"hi"}"#); test_parse(r#"{"let's \" go"}"#, r#"{"let's \" go"}"#); roundtrip("{hi}"); diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index 26ec7f487..a8a5854a3 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -1,7 +1,6 @@ use std::rc::Rc; use super::*; -use crate::color::RgbaColor; use crate::geom::{AngularUnit, LengthUnit}; /// An expression. @@ -28,8 +27,6 @@ pub enum Expr { Percent(Span, f64), /// A fraction unit literal: `1fr`. Fractional(Span, f64), - /// A color literal: `#ffccee`. - Color(Span, RgbaColor), /// A string literal: `"hello!"`. Str(Span, String), /// An identifier: `left`. @@ -81,7 +78,6 @@ impl Expr { 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, Self::Array(ref v) => v.span, diff --git a/src/syntax/token.rs b/src/syntax/token.rs index 250622643..32a14d58f 100644 --- a/src/syntax/token.rs +++ b/src/syntax/token.rs @@ -1,4 +1,3 @@ -use crate::color::RgbaColor; use crate::geom::{AngularUnit, LengthUnit}; /// A minimal semantic entity of source code. @@ -145,8 +144,6 @@ pub enum Token<'s> { Percent(f64), /// A fraction unit: `3fr`. Fraction(f64), - /// A color value: `#20d82a`. - Color(RgbaColor), /// A quoted string: `"..."`. Str(StrToken<'s>), /// Two slashes followed by inner contents, terminated with a newline: @@ -275,7 +272,6 @@ impl<'s> Token<'s> { Self::Angle(_, _) => "angle", Self::Percent(_) => "percentage", Self::Fraction(_) => "`fr` value", - Self::Color(_) => "color", Self::Str(_) => "string", Self::LineComment(_) => "line comment", Self::BlockComment(_) => "block comment", diff --git a/src/syntax/visit.rs b/src/syntax/visit.rs index 66c5f9796..1184010bf 100644 --- a/src/syntax/visit.rs +++ b/src/syntax/visit.rs @@ -124,7 +124,6 @@ impl_visitors! { Expr::Angle(_, _, _) => {} Expr::Percent(_, _) => {} Expr::Fractional(_, _) => {} - Expr::Color(_, _) => {} Expr::Str(_, _) => {} Expr::Ident(_) => {} Expr::Array(e) => v.visit_array(e), diff --git a/tests/typ/code/array.typ b/tests/typ/code/array.typ index c93835017..002a792a5 100644 --- a/tests/typ/code/array.typ +++ b/tests/typ/code/array.typ @@ -15,7 +15,7 @@ // Multiple lines and items and trailing comma. {("1" - , #002 + , rgb("002") ,)} // Error: 3 expected closing paren diff --git a/tests/typ/code/repr.typ b/tests/typ/code/repr.typ index eacbf8e81..3da86bf8a 100644 --- a/tests/typ/code/repr.typ +++ b/tests/typ/code/repr.typ @@ -34,7 +34,7 @@ --- // Colors. -{#f7a20500} +#rgb("f7a20500") --- // Strings and escaping. diff --git a/tests/typ/insert/circle.typ b/tests/typ/insert/circle.typ index 1c8808a48..fbb45435f 100644 --- a/tests/typ/insert/circle.typ +++ b/tests/typ/insert/circle.typ @@ -4,7 +4,7 @@ // Test auto sizing. Auto-sized circle. \ -#circle(fill: #eb5278)[ +#circle(fill: rgb("eb5278"))[ #align!(center, center) But, soft! ] @@ -28,9 +28,9 @@ Expanded by height. --- // Test relative sizing. -#rect(width: 100%, height: 50pt, fill: #aaa)[ +#rect(width: 100%, height: 50pt, fill: rgb("aaa"))[ #align!(center, center) - #font!(color: #fff) + #font!(color: white) #circle(radius: 10pt, fill: eastern)[A] #circle(height: 60%, fill: eastern)[B] #circle(width: 20% + 20pt, fill: eastern)[C] diff --git a/tests/typ/insert/ellipse.typ b/tests/typ/insert/ellipse.typ index a086ab740..29f9b556d 100644 --- a/tests/typ/insert/ellipse.typ +++ b/tests/typ/insert/ellipse.typ @@ -2,7 +2,7 @@ --- 100% rect in 100% ellipse in fixed rect. \ -#rect(width: 3cm, height: 2cm, fill: #2a631a)[ +#rect(width: 3cm, height: 2cm, fill: rgb("2a631a"))[ #ellipse!(width: 100%, height: 100%, fill: forest) #rect!(width: 100%, height: 100%, fill: conifer) #align!(center, center) diff --git a/tests/typ/insert/rect.typ b/tests/typ/insert/rect.typ index 93df071be..7f6bb899d 100644 --- a/tests/typ/insert/rect.typ +++ b/tests/typ/insert/rect.typ @@ -9,19 +9,19 @@ #rect(fill: conifer)[Textbox] // Empty with fixed width and height. -#rect(width: 3cm, height: 12pt, fill: #CB4CED) +#rect(width: 3cm, height: 12pt, fill: rgb("CB4CED")) // Fixed width, text height. -#rect(width: 2cm, fill: #9650D6, pad(5pt)[Fixed and padded]) +#rect(width: 2cm, fill: rgb("9650D6"), pad(5pt)[Fixed and padded]) // Page width, fixed height. -#rect(height: 1cm, width: 100%, fill: #734CED)[Topleft] +#rect(height: 1cm, width: 100%, fill: rgb("734CED"))[Topleft] // Not visible, but creates a gap between the boxes above and below // due to line spacing. -#rect(width: 1in, fill: #ff0000) +#rect(width: 1in, fill: rgb("ff0000")) // These are in a row! -#rect(width: 0.5in, height: 10pt, fill: #D6CD67) -#rect(width: 0.5in, height: 10pt, fill: #EDD466) -#rect(width: 0.5in, height: 10pt, fill: #E3BE62) +#rect(width: 0.5in, height: 10pt, fill: rgb("D6CD67")) +#rect(width: 0.5in, height: 10pt, fill: rgb("EDD466")) +#rect(width: 0.5in, height: 10pt, fill: rgb("E3BE62")) diff --git a/tests/typ/insert/square.typ b/tests/typ/insert/square.typ index 4dd8964f2..f9efb98ed 100644 --- a/tests/typ/insert/square.typ +++ b/tests/typ/insert/square.typ @@ -5,7 +5,7 @@ Auto-sized square. \ #square(fill: eastern)[ #align!(center) #pad!(5pt) - #font!(color: #fff, weight: bold) + #font!(color: white, weight: bold) Typst ] @@ -13,7 +13,7 @@ Auto-sized square. \ // Length wins over width and height. // Error: 2:9-2:20 unexpected argument // Error: 1:22-1:34 unexpected argument -#square(width: 10cm, height: 20cm, length: 1cm, fill: #eb5278) +#square(width: 10cm, height: 20cm, length: 1cm, fill: rgb("eb5278")) --- // Test height overflow. diff --git a/tests/typ/layout/grid-1.typ b/tests/typ/layout/grid-1.typ index 2938a7f29..ad55b53fb 100644 --- a/tests/typ/layout/grid-1.typ +++ b/tests/typ/layout/grid-1.typ @@ -6,18 +6,18 @@ #page!(width: 100pt, height: 140pt) #grid( columns: (auto, 1fr, 3fr, 0.25cm, 3%, 2mm + 10%), - rect(0.5cm, #2a631a), + rect(0.5cm, rgb("2a631a")), rect(100%, forest), rect(100%, conifer), - rect(100%, #ff0000), - rect(100%, #00ff00), - rect(80%, #00faf0), - rect(1cm, #00ff00), - rect(0.5cm, #2a631a), + rect(100%, rgb("ff0000")), + rect(100%, rgb("00ff00")), + rect(80%, rgb("00faf0")), + rect(1cm, rgb("00ff00")), + rect(0.5cm, rgb("2a631a")), rect(100%, forest), rect(100%, conifer), - rect(100%, #ff0000), - rect(100%, #00ff00), + rect(100%, rgb("ff0000")), + rect(100%, rgb("00ff00")), ) #grid() @@ -29,7 +29,7 @@ gutter-rows: (1fr,), rect(fill: eastern)[dddaa aaa aaa], rect(fill: conifer)[ccc], - rect(width: 100%, fill: #dddddd)[aaa], + rect(width: 100%, fill: rgb("dddddd"))[aaa], ) --- @@ -38,11 +38,11 @@ columns: (1fr, 1cm, 1fr, 1fr), column-dir: ttb, rows: (auto, 1fr), - rect(height: 100%, fill: #222222)[foo], - rect(height: 100%, fill: #547d0a)[bar], + rect(height: 100%, fill: rgb("222222"))[foo], + rect(height: 100%, fill: rgb("547d0a"))[bar], rect(height: 100%, fill: eastern)[hab], rect(height: 100%, fill: conifer)[baz], - rect(height: 100%, width: 100%, fill: #547d0a)[bar], + rect(height: 100%, width: 100%, fill: rgb("547d0a"))[bar], ) --- diff --git a/tests/typ/layout/grid-3.typ b/tests/typ/layout/grid-3.typ index 62af4072b..385725200 100644 --- a/tests/typ/layout/grid-3.typ +++ b/tests/typ/layout/grid-3.typ @@ -67,9 +67,9 @@ columns: 2 * (1fr,), rows: (1fr, 2fr, auto, 1fr, 1cm), gutter-rows: 4 * (10pt,), - rect(height: 100%, width: 100%, fill: #ff0000)[No height], + rect(height: 100%, width: 100%, fill: rgb("ff0000"))[No height], [foo], - rect(height: 100%, width: 100%, fill: #fc0030)[Still no height], + rect(height: 100%, width: 100%, fill: rgb("fc0030"))[Still no height], [bar], [The nature of being itself is in question. Am I One? Am I Many? What is being alive?], [baz], diff --git a/tests/typ/layout/pad.typ b/tests/typ/layout/pad.typ index 05f9b359d..d43cafc5c 100644 --- a/tests/typ/layout/pad.typ +++ b/tests/typ/layout/pad.typ @@ -7,7 +7,7 @@ // All sides together. #rect(fill: conifer)[ #pad!(10pt, right: 20pt) - #rect(width: 20pt, height: 20pt, fill: #eb5278) + #rect(width: 20pt, height: 20pt, fill: rgb("eb5278")) ] // Error: 13-23 missing argument: body diff --git a/tests/typ/layout/stack.typ b/tests/typ/layout/stack.typ index 89e587c73..fd5201df1 100644 --- a/tests/typ/layout/stack.typ +++ b/tests/typ/layout/stack.typ @@ -3,7 +3,7 @@ --- #let rect(width, color) = rect(width: width, height: 1cm, fill: color) #stack( - rect(2cm, #2a631a), + rect(2cm, rgb("2a631a")), rect(3cm, forest), rect(1cm, conifer), ) diff --git a/tests/typ/text/decorations.typ b/tests/typ/text/decorations.typ index 3e298ece9..247178b29 100644 --- a/tests/typ/text/decorations.typ +++ b/tests/typ/text/decorations.typ @@ -3,17 +3,14 @@ --- #strike[Statements dreamt up by the utterly deranged.] -Sometimes, we work #strike(extent: 5%, strength: 10pt)[in secret]. -There might be #strike(extent: 5%, strength: 10pt, color: #abcdef88)[redacted] -things. +Sometimes, we work #strike(extent: 5%, strength: 10pt)[in secret]. There might +be #strike(extent: 5%, strength: 10pt, color: rgb("abcdef88"))[redacted] things. --- -#underline(color: #fc0030)[Critical information is conveyed here.] -#underline[ - Still important, but not #underline(strength: 0pt)[mission ]critical. -] +#underline(color: rgb("fc0030"))[Critical information is conveyed here.] +#underline[Still important, but not #underline(strength: 0pt)[mission ]critical.] -#font(color: #fc0030, underline[Change with the wind.]) +#font(color: rgb("fc0030"), underline[Change with the wind.]) --- #overline(underline[Running amongst the wolves.]) diff --git a/tests/typ/text/font.typ b/tests/typ/text/font.typ index d6161c815..9bfeb6849 100644 --- a/tests/typ/text/font.typ +++ b/tests/typ/text/font.typ @@ -30,7 +30,7 @@ Emoji: πŸͺ, πŸŒ‹, 🏞 ] // Colors. -#font(color: eastern)[This is #font(color: #FA644B)[way more] colorful.] +#font(color: eastern)[This is #font(color: rgb("FA644B"))[way more] colorful.] --- // Test top and bottom edge. diff --git a/tests/typ/utility/color.typ b/tests/typ/utility/color.typ index 2e40828b4..6523a08df 100644 --- a/tests/typ/utility/color.typ +++ b/tests/typ/utility/color.typ @@ -2,18 +2,21 @@ // Ref: false --- -// Check the output. -#test(rgb(0.0, 0.3, 0.7), #004db3) +// Compare both ways. +#test(rgb(0.0, 0.3, 0.7), rgb("004db3")) // Alpha channel. -#test(rgb(1.0, 0.0, 0.0, 0.5), #ff000080) +#test(rgb(1.0, 0.0, 0.0, 0.5), rgb("ff000080")) // Warning: 2:11-2:14 should be between 0.0 and 1.0 // Warning: 1:16-1:20 should be between 0.0 and 1.0 -#test(rgb(-30, 15.5, 0.5), #00ff80) +#test(rgb(-30, 15.5, 0.5), rgb("00ff80")) // Error: 11-15 missing argument: blue component -#test(rgb(0, 1), #00ff00) +#test(rgb(0, 1), rgb("00ff00")) + +// Error: 11-16 invalid color +#test(rgb("lol"), error) // Error: 3:11-3:11 missing argument: red component // Error: 2:11-2:11 missing argument: green component