Skip to content

Add named tuples and hex color tokens#1

Merged
laurmaedje merged 3 commits intomasterfrom
named_tuples
Jul 15, 2020
Merged

Add named tuples and hex color tokens#1
laurmaedje merged 3 commits intomasterfrom
named_tuples

Conversation

@reknih
Copy link
Member

@reknih reknih commented Jul 14, 2020

This Pull Request implements the specification for

  • named tuples like cmyk(52.6, 16, 80.4, 41.1)
  • and hexadecimal RGB(A) color literals, e.g. #20d82a

This pull request includes comprehensive test cases for the new parsing features.
The request should be merged to provide the language groundwork for color usage and other styling applications.

@reknih reknih requested a review from laurmaedje July 14, 2020 17:13
Copy link
Member

@laurmaedje laurmaedje left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some thoughts and comments, but I think this is already pretty good :)

use std::fmt::{self, Write, Debug, Formatter};
use std::iter::FromIterator;
use std::ops::Deref;
use std::u8;
Copy link
Member

@laurmaedje laurmaedje Jul 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need that use std::u8; import? I think u8::from_str_radix is associated with the type, not the module.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not seem to work without the import, so it has to stay :/

Comment on lines +132 to +137
let permissable = &[3, 4, 6, 8];

if !permissable.contains(&len) {
return None;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest adding another let short = len == 3 || len == 4; and then checking long || short and returning None if that is false instead of the slice-contains.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

let pos = elem * item_len;

if let Ok(val) = u8::from_str_radix(
&hex_str[pos..(pos+item_len)], 16) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can panic if hex_str turns out not to be ASCII if you index into a UTF-8 char. I suggest adding a hex_str.is_ascii() check at the start of the method.

let item_len = if long { 2 } else { 1 };
let pos = elem * item_len;

if let Ok(val) = u8::from_str_radix(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be written more concisely as

let item = &hex_str[pos..(pos+item_len)];
values[elem] = u8::from_str_radix(item, 16).ok()?;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done as well.

/// ```
#[derive(Clone, PartialEq)]
pub struct NamedTuple {
pub name: Spanned<Ident>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a doc-comment, even though I guess it's pretty obvious.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha.

Comment on lines +301 to +308
impl Debug for NamedTuple {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_struct("named tuple")
.field("name", &self.name)
.field("values", &self.tuple)
.finish()
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty close to the output of derive(Debug). In that case, I think deriving would be simpler.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping it DRY. Smart.

Comment on lines +256 to +258
let color_opt = RgbaColor::from_str(s);

if let Some(color) = color_opt {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe write that RgbaColor::from_str(s) directly into the if let.

} else {
// Heal color by assuming black
self.feedback.errors.push(err!(first.span;
"expected valid color, found invalid color #{}", s));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think adding the error message & healing here is a good idea, but the message might be slightly redundant. What do you think about adding it here, but keeping just "invalid color"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have also introduced a healed flag on RgbaColor that indicates whether the color is a fallback, so that other methods can react more smartly (or not at all).

Comment on lines +289 to +290
let start = name.span.start.clone();
let end = tuple.span.end.clone();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Position should implement Copy, so I don't think you need to clone. But, in fact, there is also a convenience method Span::merge(name.span, tuple.span), which you can use here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Googled up on the difference between Clone and Copy traits in general and copy seems like the Right Thing To Do:tm: here,

Comment on lines +230 to +244

// This may be a hex expression
let hex_expr = if c == '#' && !body {
let payload = self.read_string_until(|n| {
match n {
'0'..='9' | 'a'..='f' | 'A'..='F' => false,
_ => true,
}
}, false, 0, 0).0;

Some(ExprHex(payload))
} else {
None
};

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this starts with a clearly defined character (#), you should be able to write it earlier in match instead of writing it in the fallback case. For example, right after the string case á la

'#' if self.mode == Header => self.parse_hex_value(),

and then factor the code out into that method. Also, I just noticed that there is a function in the standard library, so you can just do it like this:

ExprHex(self.read_string_until(|n| !n.is_digit(16), false, 0, 0).0)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also the thing that stopping when there is no hex digit might lead to a bit counter-intuitive error messages:

[page: background=#55z555]
                     ^^^^
                     unexpected identifier

Where on might expect:

[page: background=#55z555]
                  ^^^^^^^
                  invalid color

But I'm also not sure how important that is and what the best way would be here. Maybe reading until there is a non-alphanumeric character would also be an option.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, it will now do is_ascii_alphanumeric in the tokenizer, that will hopefully create better error messages.

@reknih
Copy link
Member Author

reknih commented Jul 15, 2020

The new commit with the feedback incorporated is now pushed.

@laurmaedje laurmaedje merged commit 0fd327b into master Jul 15, 2020
@laurmaedje laurmaedje deleted the named_tuples branch July 15, 2020 21:49
kan190729 pushed a commit to oss-experiment-uec/2024-k2210165-typst that referenced this pull request Dec 23, 2024
kan190729 pushed a commit to oss-experiment-uec/2024-k2210165-typst that referenced this pull request Dec 23, 2024
Multi-container apps: add workaround for non amd64 machines
@uwni uwni mentioned this pull request Feb 3, 2026
1 task
fabianbosshard pushed a commit to fabianbosshard/typst that referenced this pull request Feb 21, 2026
* feat: mod typst

* chore: bump version

* Avoid panic if a field doesn't have name

---------

Co-authored-by: ParaN3xus <paran3xus007@gmail.com>
Co-authored-by: ParaN3xus <136563585+ParaN3xus@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants