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

✨ Parse list notation #159

Merged
merged 11 commits into from
Dec 12, 2017
Merged

✨ Parse list notation #159

merged 11 commits into from
Dec 12, 2017

Conversation

nlepage
Copy link
Member

@nlepage nlepage commented Dec 8, 2017

Issue : fix #151

@nlepage nlepage added this to the 1.0.0-RC1 milestone Dec 8, 2017
@nlepage nlepage self-assigned this Dec 8, 2017
@codecov-io
Copy link

codecov-io commented Dec 11, 2017

Codecov Report

Merging #159 into master will not change coverage.
The diff coverage is 100%.

Impacted file tree graph

@@          Coverage Diff          @@
##           master   #159   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files          79     79           
  Lines         296    306   +10     
=====================================
+ Hits          296    306   +10
Impacted Files Coverage Δ
packages/immutadot/src/path/consts.js 100% <100%> (ø) ⬆️
packages/immutadot/src/path/toPath.js 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 20a1153...70eb1d6. Read the comment docs.

@nlepage nlepage changed the title 🚧 Parse list notation ✨ Parse list notation Dec 11, 2017
@nlepage nlepage requested a review from hgwood December 11, 2017 10:39
@nlepage
Copy link
Member Author

nlepage commented Dec 11, 2017

I deliberatly changed the behavior of incomplete notation for the list.
Instead of running to the end of the path like array and slice do, we make a prop including the opening curly until the next segment.
I think most of the time this will give a result closer to what the user meant, and highlight his mistake with the opening curly included in the prop.

@hgwood @frinyvonnick What do you think ? If you are OK, I will comment on #113 in order to refactor array and slice to make the same.

I also mutualized the later parsers in only one.
I had not anticipated the removal of str => [[prop, str]], but code coverage reported that this code was dead, and all the tests were still working ! The perks of having 100% coverage !

([beforeBracket, atBracket]) => [[prop, beforeBracket], ...stringToPath(atBracket)],
const pathSegmentEndedByNewSegment = map(
regexp(/^([^.[{]*)\.?([[{]?.*)$/),
([beforeNewSegment, rest]) => [[prop, beforeNewSegment], ...stringToPath(rest)],
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice simplification!

regexp(/^(\{[^.[{]*)(.*)$/),
([beforeNewSegment, rest]) => {
return [[prop, beforeNewSegment], ...stringToPath(rest)]
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe the lambda could be written in the expression form (ie without braces and return)?

for (let propMatch = rawProps.match(listPropRegexp); propMatch !== undefined; propMatch = propMatch[5] === '' ? undefined : propMatch[5].match(listPropRegexp))
props.push(propMatch[2] === undefined ? propMatch[4] : propMatch[2])
return props.length === 1 ? [[prop, props[0]], ...stringToPath(rest)] : [[list, props], ...stringToPath(rest)]
},
Copy link
Contributor

Choose a reason for hiding this comment

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

This piece of code is a little overwhelming. My advice would be:

  • use destructuring to name the elements of propMatch
  • use functional programming to make meaningful transformation names emerge (what is this loop doing? a reduction? a mapping?)

([beforeDot, afterDot]) => [[prop, beforeDot], ...stringToPath(afterDot)],
const listPropRegexp = /^,?((?!["'])([^,]*)|(["'])(.*?[^\\])\3)(.*)/
const listNotationParser = map(
regexp(/^\{(((?!["'])[^,}]*|(["']).*?[^\\]\2)(,((?!["'])[^,}]*|(["']).*?[^\\]\6))*)\}\.?(.*)$/),
Copy link
Contributor

Choose a reason for hiding this comment

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

We might want to split the quoted case and the bare case like I did for the bracket notation. I feel it yielded simpler regexes. These ones are a bit hard to read.

@hgwood
Copy link
Contributor

hgwood commented Dec 11, 2017

@nlepage *their mistake :)

That last parser is dead because pathSegmentEndedByNewSegment already matches everything. Again, nice simplification.

As for the incomplete cases, I'm fine both ways.

@nlepage
Copy link
Member Author

nlepage commented Dec 11, 2017

@hgwood I've used a generator to extract list props, and propMatch elements are now named as you suggested, what do you think ?
Regarding the main regexp, I don't see how to split quoted and bare case... Do you have any pointers ?

@nlepage nlepage requested a review from charlyx December 11, 2017 22:07
@frinyvonnick
Copy link
Contributor

@nlepage i agree for incomplete cases !

@hgwood
Copy link
Contributor

hgwood commented Dec 12, 2017

@nlepage Great improvement to listNotationParser, I think it is a lot faster to read and understand now.

As for splitting the regex, I see now that you cannot really do that as you may have lists with both quoted and unquoted props. I'm still a bit worried by the length of the regex and also the duplication. I think I may have an idea to mitigate that, but I don't want to block this PR on it.

My idea is to go full parser combinators to have modular, composable, easy-to-understand parsers so you could describe lists like between("{}", interspersedBy(",", maybeBetween("''\"\""))). Might be an interesting refactoring to undertake, but again, not worth blocking on.

hgwood
hgwood previously approved these changes Dec 12, 2017
charlyx
charlyx previously approved these changes Dec 12, 2017
Copy link
Contributor

@charlyx charlyx left a comment

Choose a reason for hiding this comment

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

👌

([beforeDot, afterDot]) => [[prop, beforeDot], ...stringToPath(afterDot)],
const listPropRegexp = /^,?((?!["'])([^,]*)|(["'])(.*?[^\\])\3)(.*)/
function* extractListProps(rawProps) {
if (rawProps.startsWith(',')) yield ''
Copy link
Contributor

Choose a reason for hiding this comment

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

This case isn't tested 😢

const listNotationParser = map(
regexp(/^\{(((?!["'])[^,}]*|(["']).*?[^\\]\2)(,((?!["'])[^,}]*|(["']).*?[^\\]\6))*)\}\.?(.*)$/),
([rawProps, , , , , , rest]) => {
const props = [...extractListProps(rawProps)]
Copy link
Contributor

Choose a reason for hiding this comment

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

Smart 💡 !

@nlepage nlepage dismissed stale reviews from charlyx and hgwood via 70eb1d6 December 12, 2017 21:00
@frinyvonnick
Copy link
Contributor

Nice work 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Parse list notation in path
5 participants