-
Notifications
You must be signed in to change notification settings - Fork 33
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
Object comprehensions via loops in braces #1563
Object comprehensions via loops in braces #1563
Conversation
Parses and transpiles object comprehension syntax proposed in issue DanielXMoore#439 Tested only in limited contexts, of the 4 tests added one is failing
Good start! Instead of making a separate rule {
name: "Upgrade"
for key, value in object
[key.toLowerCase()]: value + 1
} Admittedly, this will be harder to compile. I'm imagining this: {
name: "Upgrade",
...(() => {const results=[]; for (key in object) {const value = object[key];
Object.assign(results, {[key.toLowerCase()]: value + 1})
})()
} The code above might look complicated, but it's mostly what we already get from the You might check out the recent PR #1509 that provided alternatives to I think this approach might also fix your first issue about priority of blocks vs. object literals, because object comprehensions will now be considered an object literal. (I'm not totally sure, but generally we favor object literals.)
No, try to preserve the indentation in the original input. Also try not to add any extra lines (except what
Use
To help with this, use tokens as much as possible. For example, instead of using Hope this helps! |
Thanks, very helpful! I did not even realize that Civet already has these for loops. The fact that they are expression-like (they evaluate to their results) is quite interesting and changes the way I was thinking about incorporating loops into Object Comprehensions. I'll study these a bit and then proceed. |
IterationExpressions now allowed in PropertyDefinition and assumed to be object comprehensions Code generation now handled in same way as for loop reduction logic Revamp tests to suit the new implementation All tests passing
All tests passing
dd005fb
to
f87b488
Compare
Hey @edemaine, look at this! I think it's exactly how you wanted to implement it? When a loop ( A few notes:
Technically this is a "support loops inside object expressions" implementation which is rather clean, and as a special and most-commonly used case, it provides for syntax such as in #439. As a consequence, here's a nice self-describing program console.log({
name: 'Square table generator'
description: 'This table contains numeric properties mapped to their squares'
for x of [1, 2, 3]
[x]: x * x
[x * 2]: 4 * x * x
}) That would produce: {
name: 'Square table generator',
description: 'This table contains numeric properties mapped to their squares',
'1': 1,
'2': 4,
'3': 9,
'4': 16,
'6': 36
} What do you think? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great!! Awesome how simple the code is now.
I like your idea of allowing arbitrary iteration expressions. I can imagine while
making sense, and especially do
being helpful (to allow for intermediate computations). Adding some tests for these would be good.
Actually, it'd be cool to allow if
expressions too. (But feel free to leave that for another PR.)
Does this PR work with postfixed iteration, such as {[i]: i*2} for i of [1,2,3]
? I'm guessing not. I'm not really sure whether it's a good idea... (i: i*2 for i of [1,2,3]
would be neat but that already has a different meaning: i: (i*2 for i of [1,2,3])
.)
All tests passing
Follows code review comments in DanielXMoore#1563
In line with other PropertyDefinition rules
c46394b
to
548bc91
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your review!
I currently do not support postfixed iteration, I have not thought about it much yet.
EDIT: In an earlier version of comment I had misunderstood what you meant by postfixed iteration.
I'll also leave the if
expressions out of the scope of this PR, but I agree that it could be interesting to have values conditionally slurped into the object, e.g.
{
a: 1
if x
b: 2
}
Currently, the way to do that is not very friendly:
{
a: 1,
...(x? {b : 2} : {})
}
I also added some docs!
Prefer them over "do" and "loop" prop keys
- Add example with a do...while loop - Fix colon in an existing example
Ready to merge from my end. Let me know if anything else is required! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, except for some overdue work on PropertyGlob
(they probably shouldn't allow {} foo, bar
either...??).
Possible TODO: if there's just a single loop and nothing else in the object literal, then we don't need a { ...(comprehension) }
wrapper so should avoid emitting one. Also postfix loops remains an interesting TODO.
@STRd6 do you approve of this extension? Sadly we won't be able to do the same for array literals, as loops naturally make nested arrays unless you explicitly add ...
, but I think it's pretty natural for objects to behave this way and it unlocks a new "reduction" for combining object literals (that you couldn't get with just ...
).
test/object.civet
Outdated
--- | ||
({and:state.and,await:state.await,break:state.break,case:state.case,catch:state.catch,class:state.class,const:state.const,continue:state.continue,debugger:state.debugger,default:state.default,delete:state.delete,do:state.do,else:state.else,enum:state.enum,export:state.export,extends:state.extends}) | ||
({and:state.and,await:state.await,break:state.break,case:state.case,catch:state.catch,class:state.class,const:state.const,continue:state.continue,debugger:state.debugger,default:state.default,delete:state.delete,else:state.else,enum:state.enum,export:state.export,extends:state.extends}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I didn't realize that it's property globs that are failing. This is annoying; this is valid syntax that we'd lose. But fixing it will require some work on PropertyGlob
to not use BracedObjectLiteral
, but rather just use a subset of those rules. I can work on this in another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for clarifying. I do not know much about property globs so I'll safely leave it with you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was able to fix this without messing with globs; just forbidding implicit blocks with do
and loop
.
I also wish to leave this optimization for later. I could definitely investigate both of these myself, but only later and not now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @peey for all the work on this and @edemaine for the feedback and review! This is a great extension to the language.
I think we may want to use direct assignment of the result rather than Object.assign
but that can be done later as it is pretty minor.
Also added a note about a debugger statement that snuck in.
Work-in-progress to address the "object comprehension" part of issue #439.
Implementation rationale is that whenever the following is seen:
Then it should be recognized as object comprehension (instead of being recognized as a block containing a ForOf statement).
This works because
for identifier of expression
being followed by a computed property key-value pair is invalid syntax in js.I need some guidance:
1
My rules recognize the syntax but it isn't prioritized over "block containing a for-of" rule when the [a] : b are indented. This results in the following input-output pair:
This is also a test (last one) which fails. If I remove the leading whitespace, then it works fine
Which provides the output
{ '1': 2, '2': 4, '3': 6, '4': 8, '6': 12 }
if you run it. Note that the indentation in this output has been done by me manually for readability.2
How should I deal with the indentation in output? I'm introduce an IIFE, a for loop, and an
Object.assign
. That's 3 extra levels of nesting. Should I just add two spaces for each level?3
The second test case transpiles but it's incorrect because the inner
_results
shadows the outer_results
and I need to use the variable I introduce in the inner scope. How do I create a variable that's guaranteed to not conflict with the inner scope?4
I've noticed that my code differs from the existing code in following ways:
type
I'll address these in future commits. Tell me if there are other conventions that I should learn about!