-
Notifications
You must be signed in to change notification settings - Fork 210
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
refactor(chevrotain): set noImplicitAny: true and fix errors #1688
Conversation
b31a5a7
to
c0c6ab2
Compare
c0c6ab2
to
76141af
Compare
Okay @NaridaL there are a couple of comments that needs to be resolved and this can be merged. |
81f9638
to
0af7457
Compare
@bd82 This work for you?
…On Fri, Oct 29, 2021, 01:09 Shahar Soel ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In packages/chevrotain/src/parse/parser/traits/parser_traits.ts
<#1688 (comment)>
:
> @@ -35,6 +35,11 @@ export type MixedInParser = ParserConstructorImpel &
GastRecorder &
PerformanceTracer
+export type ParserMethod<T> = ((idx: number, args?: any[]) => T) & {
It is very much needed.
It is used at *runtime* to understand the "grammar location" inside the
specific rule and thus use the correct lookahead logic to use. Basically,
to distinguish between OPTION1 vs OPTION2 vs OPTION-N instead the same rule.
Here is where the unique ID for the "grammar location" is built:
-
https://github.com/Chevrotain/chevrotain/blob/master/packages/chevrotain/src/parse/grammar/keys.ts
- Note the bit operations as they ended up much faster than concating
strings.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1688 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACSTYSQWXA7N5TX675PZUVDUJHQ2PANCNFSM5G2YYATA>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
|
packages/types/api.d.ts
Outdated
@@ -875,21 +880,21 @@ export declare class CstParser extends BaseParser { | |||
/** | |||
* Creates a Grammar Rule | |||
*/ | |||
protected RULE( | |||
protected RULE<ARGS extends unknown[]>( |
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.
What is the purpose of "extends unknown" here?
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.
Would the generic argument gets resolved / inferred correctly according to the provided implementation?
Meaning would a SUBRULE call that provides ARGS have type checks on the ARGS depending on which rule is invoked?
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.
extends "unknown array". This is needed to do ...args: ARGS
. ... which I wasn't doing. Should be fixed now.
The inference should work as expected yes.
try { | ||
this.ruleInvocationStateUpdate(shortName, ruleName, this.subruleIdx) |
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.
now that you simplified this logic, we may be able to to get rid of the conditional related to outputCst
- But not in this PR its getting large anyhow
packages/chevrotain/src/parse/parser/traits/recognizer_engine.ts
Outdated
Show resolved
Hide resolved
try { | ||
this.ruleInvocationStateUpdate(shortName, ruleName, this.subruleIdx) | ||
this.subruleIdx = 0 |
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.
why do we need to reset this.subruleIdx = 0' ? won't the next
SUBRULE` invocation take care of it?
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.
invokeRuleWithTry
is a hotspot, so we want to avoid any none mandatory statements.
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 believe there is a reset
method on this class/trait where it should be reset to 0 instead.
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've got it in init, it's not strictly required in reset, as top-level rule calls should work with any idx right?
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.
Yeah, good point, it may not be strictly required.
I may add it later though to be explicit and be more consistent between different invocations (inputs) on the same parser instance.
let ruleResult | ||
try { | ||
const args = options !== undefined ? options.ARGS : undefined | ||
ruleResult = ruleToCall.call(this, idx, args) | ||
this.subruleIdx = idx | ||
ruleResult = ruleToCall.apply(this, args) |
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 wonder if call
vs apply
would have different performance here
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.
In invokeWithTry we are using the spread operator and then calling apply again.
So it seems we are "packing" and "unpacking" the ARGS too many times.
invokeRuleWithTry(this: MixedInParser, ...args: ARGS[])
If we remove the spread operator there, we could then keep this line with the possibly faster call
and avoid un-needed packs/unpacks ?
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.
Yes, but then the result of RULE wouldn't have the same signature as the implementation definition, which is the most intuitive. As there are going to be no arguments in most cases, I think a minor performance impact would be worth it.
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 will investigate the performance impact (if any) after we merge.
Hi @NaridaL I really like the simplification of the Idx parameter 👍 I will also try the api.d.ts changes on a Parser implemented in TypeScript, so I can understand the limits of the type inference capabilities and the usage of |
I've worked it the comments. I also changed the typings on RULE slightly again, so that all parameters must be optional. I.e. must be assignable to () => any. |
The new signatures look much better, and the use of One question, |
Does this PR now replace #1672 ? |
It's by design, mainly because args will be undefined during grammar recording. |
Mostly, I will have a look at it once this is merged. |
I'm merging this.
|
Hmm I suspect the improvement is from the fact there is now only one wrapper lambda instead of one... I tried to profile the benchmark in chrome but didn't get it to work as the parser itself runs in an iframe... |
Could definitively also affect this.
I recall I kept trying more and more ways to isolate the benchmarks, due to strange behavior and results. |
I am now inspecting the cost of the spread operator in the function invokeRuleWithTry() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
// ...
} Which is obviously ineffective, but 99% of the time there are no arguments, so only a single additional statement would be executed. On the other hand I recall some versions of V8 had problems optimizing functions which uses the |
I compare master version, versus one that changes the generated code to use the spread operator: function invokeRuleWithTry(...args) {
// ...
} I get some very interesting results:
I am guessing there is some magic combination of factors that can enable V8 to optimize the ECMAScript grammar better. |
Removing the spread operator and replacing the I wonder what should be done:
At the moment I think we should just enjoy the small boost we already gained. For reference I am using Chrome 97 for testing. |
Definitely not 3., that seems very difficult to maintain and prone to break if TypeScript or other tools change things. I've tested it, as far as I can see there are only 3-4 problems to fix to get ES2015 to work. As for performance degradation, profiling might show where the problem is with the ES2015 version. |
The main problem with target ES2015 is that it seems to break prototype inheritance from Chevrotain classes. |
No description provided.