Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add parsing/serialisation of 'grid-auto-flow', fixing issue #15313 #15364

Closed
wants to merge 1 commit into from

Conversation

@galexite
Copy link
Contributor

galexite commented Feb 3, 2017

This pull request adds the parsing and serialisation code for 'grid-auto-flow', as part of issue #15313.


  • ./mach build -d does not report any errors
  • ./mach test-tidy does not report any errors
  • These changes fix #15313
  • There are tests for these changes OR
  • These changes do not require tests because only the parsing/serialisation code is added by this PR

This change is Reviewable

@highfive
Copy link

highfive commented Feb 3, 2017

Thanks for the pull request, and welcome! The Servo team is excited to review your changes, and you should hear from @Manishearth (or someone else) soon.

@highfive
Copy link

highfive commented Feb 3, 2017

Heads up! This PR modifies the following files:

  • @bholley: components/style/properties/longhand/position.mako.rs
  • @emilio: components/style/properties/longhand/position.mako.rs
@highfive
Copy link

highfive commented Feb 3, 2017

warning Warning warning

  • These commits modify style code, but no tests are modified. Please consider adding a test!
#[derive(PartialEq, Clone, Eq, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct T {
pub row_or_column: Option<RowOrColumn>,

This comment has been minimized.

@wafflespeanut

wafflespeanut Feb 3, 2017

Member

Gecko uses bit fields to check whether dense exists along with row/column. Let's achieve this simply with a bool.

pub struct T {
    is_row: bool,
    dense: bool,
}

This will reduce a lot of unwanted complexity in the code :)

This comment has been minimized.

@wafflespeanut

wafflespeanut Feb 3, 2017

Member

@emilio You okay with this? I don't think we need an enum to represent whether it's a row or column.

This comment has been minimized.

@emilio

emilio Feb 3, 2017

Member

Sure, as long as we serialize the same way, that's fine. (We could also use bitflags!)

This comment has been minimized.

@Manishearth

Manishearth Feb 3, 2017

Member

Enums are fine IMO. It's more Rusty to use an enum (whereas they get used less in C++)

But yeah, for the computed value the Option isn't necessary.

This comment has been minimized.

@stshine

stshine Feb 4, 2017

Contributor

+1 for enums.

This comment has been minimized.

@wafflespeanut

wafflespeanut Feb 4, 2017

Member

Fair enough. Then, let's have AutoFlow::{Row, Column} in that case without the Option.

@Manishearth
Copy link
Member

Manishearth commented Feb 3, 2017

@highfive highfive assigned wafflespeanut and unassigned Manishearth Feb 3, 2017
#[derive(PartialEq, Clone, Eq, Copy, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct T {
pub row_or_column: Option<RowOrColumn>,

This comment has been minimized.

@emilio

emilio Feb 3, 2017

Member

Sure, as long as we serialize the same way, that's fine. (We could also use bitflags!)

}
}).is_ok() {}

if row_or_column | dense { Ok(value) } else { Err(()) }

This comment has been minimized.

@emilio

emilio Feb 3, 2017

Member

nit: use || instead of a single bitwise |.

let mut dense = false;
let mut value = SpecifiedValue { row_or_column: None, dense: false };
while input.try(|input| {
if let Ok(ident) = input.expect_ident() {

This comment has been minimized.

@emilio

emilio Feb 3, 2017

Member

nit: match_ignore_ascii_case! { input.expect_ident()?,, so you don't need to indent this more than necessary.

let mut row_or_column = false;
let mut dense = false;
let mut value = SpecifiedValue { row_or_column: None, dense: false };
while input.try(|input| {

This comment has been minimized.

@emilio

emilio Feb 3, 2017

Member

why not just loop? We're not trying multiple things, so this try is doing just nothing.

/// [ row | column ] || dense
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
let mut row_or_column = false;
let mut dense = false;

This comment has been minimized.

@emilio

emilio Feb 3, 2017

Member

You can just use value.dense, and remove this variable.

else { value.dense = true; continue },
_ => break
}
}

This comment has been minimized.

@wafflespeanut

wafflespeanut Feb 5, 2017

Member

We don't parse more than 2 keywords here. I don't think there's much need for a loop at all. We could have something like,

if input.try(|i| i.expect_ident_matching("row")) {
    // row thing
} else if ... {
    // column thing
}

// finally check dense

This comment has been minimized.

@emilio

emilio Feb 6, 2017

Member

That'd be wrong, dense row is also a valid value for this property, the loop is required.

This comment has been minimized.

@wafflespeanut

wafflespeanut Feb 6, 2017

Member

I didn't realize that until now. I thought the order actually mattered 😄

This comment has been minimized.

@galexite

galexite Feb 8, 2017

Author Contributor

Yes, I did wonder how that could work out! Sorry for delay: busy since.

@wafflespeanut
Copy link
Member

wafflespeanut commented Feb 5, 2017

Everything looks good (except the parsing code). Once that's fixed, we'll land this :)

let mut value = get_initial_value();
loop {
match_ignore_ascii_case! { input.expect_ident()?,
"row" => if autoflow { break }

This comment has been minimized.

@emilio

emilio Feb 6, 2017

Member

This should return an error right? otherwise we'd be parsing things like "row column".

else { value.dense = true; continue },
_ => break
}
}

This comment has been minimized.

@emilio

emilio Feb 6, 2017

Member

That'd be wrong, dense row is also a valid value for this property, the loop is required.

},
"dense" => if value.dense { break }
else { value.dense = true; continue },
_ => break

This comment has been minimized.

@emilio

emilio Feb 6, 2017

Member

Similarly, this should return an error.

value.autoflow = computed_value::AutoFlow::Row;
continue
},
"column" => if autoflow { break }

This comment has been minimized.

@emilio

emilio Feb 6, 2017

Member

Similarly here.

value.autoflow = computed_value::AutoFlow::Column;
continue
},
"dense" => if value.dense { break }

This comment has been minimized.

@emilio

emilio Feb 6, 2017

Member

Similarly, this would parse dense dense correctly. Please fix and add a test case. You may need to change the loop to while !input.is_exhausted().

Please add tests for all the test cases I've mentioned.

@bors-servo
Copy link
Contributor

bors-servo commented Feb 10, 2017

The latest upstream changes (presumably #15466) made this pull request unmergeable. Please resolve the merge conflicts.

@jdm
Copy link
Member

jdm commented Feb 18, 2017

@galexite Are you planning to address the review comments?

@wafflespeanut
Copy link
Member

wafflespeanut commented Feb 24, 2017

I never got notified of this. Next time, feel free to ping us whenever you want something to be landed :)

let mut value = get_initial_value();
loop {
match_ignore_ascii_case! { input.expect_ident()?,
"row" => if autoflow { Err(()) }

This comment has been minimized.

@wafflespeanut

wafflespeanut Feb 24, 2017

Member

I don't think this works as expected. Shouldn't all these be return Err(())?

_ => Err(())
}
}

This comment has been minimized.

@wafflespeanut

wafflespeanut Feb 24, 2017

Member

Also, shouldn't the loop break at some point of time?

@wafflespeanut
Copy link
Member

wafflespeanut commented Feb 24, 2017

Like @emilio said, tests could help you with seeing what the code really serializes into. Please consider adding unit tests for this :)

@galexite
Copy link
Contributor Author

galexite commented Feb 27, 2017

@wafflespeanut @emilio Working on adding unit tests!

@jdm
Copy link
Member

jdm commented Feb 27, 2017

   Compiling style v0.0.1 (file:///C:/projects/servo/components/style)
error[E0432]: unresolved import `values::NoViewportPercentage`
     --> C:\projects\servo\target\debug\build\style-477c81918bb3d90e\out/properties.rs:43553:9
      |
43553 |     use values::NoViewportPercentage;
      |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `NoViewportPercentage` in `values`. Did you mean to use `HasViewportPercentage`?
error: cannot continue compilation due to previous error
error: Could not compile `style`.
To learn more, run the command again with --verbose.
@galexite
Copy link
Contributor Author

galexite commented Feb 27, 2017

@jdm yes, I haven't uploaded my updated code, I just resynched my fork with upstream!

Is it OK to replace the loop with while !input.expect_semicolon().is_ok()? If not, how do I break the loop when the statement ends?

@wafflespeanut
Copy link
Member

wafflespeanut commented Feb 27, 2017

I think we can loop as long as we get an identifier from the parser.

@galexite
Copy link
Contributor Author

galexite commented Feb 27, 2017

@wafflespeanut but what happens if an integer or string is encountered? The loop breaks and there will be no error.

@galexite
Copy link
Contributor Author

galexite commented Feb 27, 2017

If I looped until the semicolon, then if an integer or string appeared, &try! returns Err(()) early, as it should.

@wafflespeanut
Copy link
Member

wafflespeanut commented Feb 27, 2017

Why wouldn't there be an error? Once the loop ends, we should check that one of dense or row or column keywords have been parsed successfully. If not, we'll return an error.

@galexite
Copy link
Contributor Author

galexite commented Feb 27, 2017

@wafflespeanut then how do I break the loop when the statement ends? i.e.

grid-auto-flow: row dense;
/*                       ^ what happens here? */

Because of try!, the function will return Err(()) when the semicolon is encountered as it's not an ident.

rustc believes that the loop won't end either: error: unreachable code at the last line (if autoflow...) of the parse function.

If I were to replace loop with while a semicolon is not present, the while loop breaks when the semicolon is parsed.

@emilio
Copy link
Member

emilio commented Feb 27, 2017

Note that the parser has already looked for the semicolon so in your example you'll only be parsing row dense, which produces the behavior you expect.

Also, the parser looks for exhaustion, so if you return Ok(()) but the input is not exhausted, it will discard the result and return an error.

@galexite
Copy link
Contributor Author

galexite commented Feb 27, 2017

@emilio I couldn't grasp that from the http://docs.rs/ documentation. Great, thank you.

@galexite
Copy link
Contributor Author

galexite commented Feb 27, 2017

Copy link
Member

wafflespeanut left a comment

Thanks! I've got some comments.

pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
let mut autoflow = false;
let mut value = get_initial_value();
while !input.is_exhausted() {

This comment has been minimized.

@wafflespeanut

wafflespeanut Feb 27, 2017

Member

This could simply be a loop

This comment has been minimized.

@emilio

emilio Feb 27, 2017

Member

How would it finish without returning an error then?

let mut value = get_initial_value();
while !input.is_exhausted() {
match_ignore_ascii_case! { &try!(input.expect_ident()),
"row" => if autoflow { return Err(()) }

This comment has been minimized.

@wafflespeanut

wafflespeanut Feb 27, 2017

Member

Since they're Copy, everything could be handled with pattern guards like so,

"row" if !autoflow => { }

and everything else will be matched with the wildcard pattern, returning Err(())

This comment has been minimized.

@emilio

emilio Feb 27, 2017

Member

I don't think that match_ignore_ascii_case! supports guards, so this is fine IMO.

else {
autoflow = true;
value.autoflow = computed_value::AutoFlow::Row;
continue

This comment has been minimized.

@wafflespeanut

wafflespeanut Feb 27, 2017

Member

We could also avoid continue everywhere.

let mut autoflow = false;
let mut value = get_initial_value();
while !input.is_exhausted() {
match_ignore_ascii_case! { &try!(input.expect_ident()),

This comment has been minimized.

@wafflespeanut

wafflespeanut Feb 27, 2017

Member

This should be input.try(|input| input.expect_ident()) so that once we find an error, we can break out of the loop. Here's a similar case that may be of help (it makes use of bit fields, but the essential idea is the same).

This comment has been minimized.

@emilio

emilio Feb 27, 2017

Member

I think the code as written before worked fine? What's the advantage of that? This function only parses idents.

This comment has been minimized.

@wafflespeanut

wafflespeanut Feb 27, 2017

Member

It's just that I've never seen using input.is_exhausted anywhere in our parsing code. Mostly, we just loop, input.try(...) and break on getting an error. Here, the loop doesn't break at all.

@wafflespeanut
Copy link
Member

wafflespeanut commented Feb 27, 2017

Also, please don't specify our names in the commit message 😄

@wafflespeanut
Copy link
Member

wafflespeanut commented Feb 28, 2017

For the record, servo/rust-cssparser#123 adds support for match_ascii_ignore_case! with pattern guards, but this doesn't have to block on that. We'll land it whenever this is ready!

@wafflespeanut
Copy link
Member

wafflespeanut commented Mar 3, 2017

@galexite FYI, the cssparser bump landed a few days back. You can now add pattern guards to the macro. You planning on finishing this off? I think the code needs changes (it's not what I or emilio suggested).

@galexite
Copy link
Contributor Author

galexite commented Mar 4, 2017

OK, thank you!

@wafflespeanut
Copy link
Member

wafflespeanut commented Mar 18, 2017

Updated and reopened in #16024

bors-servo added a commit that referenced this pull request Mar 18, 2017
Add parsing/serialisation for 'grid-auto-flow'

Didn't want the work (and review comments) in #15364 to go wasted. Fixes #15313

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16024)
<!-- Reviewable:end -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

8 participants
You can’t perform that action at this time.