-
Notifications
You must be signed in to change notification settings - Fork 47
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
Allow nested namespaces #708
Allow nested namespaces #708
Conversation
Update... as I think about this more, if the output of Parser is an AST then my approach is definitely wrong because you want to be able to clip and paste nodes around in the tree and have their output be correct. So I think I only need advice on the unit test -- maybe the Parser test just confirms that Level3 is named "Level3", and there's a spot for a higher level unit test that confirms that program produces output function "Level1_Level2_Level3_main". |
Hey @elliot-nelson, thanks for your contribution! Yes from an AST point of view I'm not sure this is right. Maybe you could follow how classes build their qualified name ( |
I plan on fully reviewing this PR tomorrow when I'm back to work, but I agree with @elsassph . These need to follow the same pattern as classes (using .findAncestor). This is because we need to support plugins that manipulate the AST, and so doing this in the parser is far too early in the process and would break AST modifications by plugins. The .parent property is set during onProgramValidate, so any unit tests will need to call program.validate() before building/verifying the fully qualified name. |
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.
Hey, thanks for your work on this feature! I really appreciate how focused you kept the PR. Your in-depth explanation really helped understand how and why you made your design decisions.
I have requested a few changes that better align this implementation with the design of the project.
The big thing is, I think you need to eliminate the ParserScope
concept entirely. brighterscript plugins can modify the AST at several points within the project lifecycle, so your current implementation would prevent plugins from creating/editing/deleting namespace statements effectively because it would tie the AST to the original structure as found in the source files.
The correct way to handle this is to use the .findAncestor()
ast functionality. As you already noticed, .parent
isn't immediately available to ast nodes. Instead, the .parent
property is set in the onFileValidate
hook for every file. This means that several of the namespace tests may need modified to use program.setFile()
and then program.validate()
before checking the fully-qualified namespace name.
Thanks all for the feedback, based on comments I have:
(The test for BrsFileValidator could be more robust, but I tested out putting a namespace in other places e.g. inside a class, in the middle of an expression, as a parameter to a function call, and all of that stuff is a syntax error for other reasons, so I think it can be left simple for 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.
Looks great! I have two small suggestions in your unit tests that will improve error reporting if the tests were to ever start failing, but then this will be good to go!
Thanks!
|
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.
Looks great! Thanks for this.
[0.60.6](rokucommunity/brighterscript@v0.60.5...0.60.6) - 2022-11-08 - double `super` call transpile in subclasses ([#740](rokucommunity/brighterscript#740)) - issues with Roku doc scraper and adds missing components ([#736](rokucommunity/brighterscript#736)) [0.60.5](rokucommunity/brighterscript@v0.60.4...0.60.5) - 2022-11-03 - Refactor SymbolTable and AST parent logic so that SymbolTables get their parent symbol table from its own (AstNode) ([#732](rokucommunity/brighterscript#732)) - Significant performance boost in `validate()` by caching `getCallableByName` ([#739](rokucommunity/brighterscript#739)) - Add diagnostic when using namespaces as variables ([#738](rokucommunity/brighterscript#738)) - Fix crash in `getDefinition` ([#734](rokucommunity/brighterscript#734)) [0.60.4](rokucommunity/brighterscript@v0.60.3...0.60.4) - 2022-10-28 - Add `name` to symbol table ([#728](rokucommunity/brighterscript#728)) - Allow `continue` as local var ([#730](rokucommunity/brighterscript#730)) - language server semanticToken request now waits until validate finishes ([#727](rokucommunity/brighterscript#727)) [0.60.3](rokucommunity/brighterscript@v0.60.2...0.60.3) - 2022-10-20 - better parse recovery for unknown function parameter types ([#722](rokucommunity/brighterscript#722)) [0.60.2](rokucommunity/brighterscript@v0.60.1...0.60.2) - 2022-10-18 - if statement block var bug ([#698](rokucommunity/brighterscript#698)) [0.60.1](rokucommunity/brighterscript@v0.60.0...0.60.1) - 2022-10-18 - Beter location for bs1042 ([#719](rokucommunity/brighterscript#719)) [0.60.0](rokucommunity/brighterscript@v0.59.0...0.60.0) - 2022-10-10 - goto definition for enum statements and enum members ([#715](rokucommunity/brighterscript#715)) - nested namespace support ([#708](rokucommunity/brighterscript#708)) - upgrade to [[email protected]](https://github.com/rokucommunity/roku-deploy/blob/master/CHANGELOG.md#392---2022-10-03). Notable changes since 3.9.1: - Replace minimatch with picomatch ([roku-deploy#101](rokucommunity/roku-deploy#101)) - fixes signature help resolution for callexpressions ([#707](rokucommunity/brighterscript#707)) - Fix transpilation of simple else block with leading comment ([#712](rokucommunity/brighterscript#712))
[0.60.6](rokucommunity/brighterscript@v0.60.5...0.60.6) - 2022-11-08 - double `super` call transpile in subclasses ([#740](rokucommunity/brighterscript#740)) - issues with Roku doc scraper and adds missing components ([#736](rokucommunity/brighterscript#736)) [0.60.5](rokucommunity/brighterscript@v0.60.4...0.60.5) - 2022-11-03 - Refactor SymbolTable and AST parent logic so that SymbolTables get their parent symbol table from its own (AstNode) ([#732](rokucommunity/brighterscript#732)) - Significant performance boost in `validate()` by caching `getCallableByName` ([#739](rokucommunity/brighterscript#739)) - Add diagnostic when using namespaces as variables ([#738](rokucommunity/brighterscript#738)) - Fix crash in `getDefinition` ([#734](rokucommunity/brighterscript#734)) [0.60.4](rokucommunity/brighterscript@v0.60.3...0.60.4) - 2022-10-28 - Add `name` to symbol table ([#728](rokucommunity/brighterscript#728)) - Allow `continue` as local var ([#730](rokucommunity/brighterscript#730)) - language server semanticToken request now waits until validate finishes ([#727](rokucommunity/brighterscript#727)) [0.60.3](rokucommunity/brighterscript@v0.60.2...0.60.3) - 2022-10-20 - better parse recovery for unknown function parameter types ([#722](rokucommunity/brighterscript#722)) [0.60.2](rokucommunity/brighterscript@v0.60.1...0.60.2) - 2022-10-18 - if statement block var bug ([#698](rokucommunity/brighterscript#698)) [0.60.1](rokucommunity/brighterscript@v0.60.0...0.60.1) - 2022-10-18 - Beter location for bs1042 ([#719](rokucommunity/brighterscript#719)) [0.60.0](rokucommunity/brighterscript@v0.59.0...0.60.0) - 2022-10-10 - goto definition for enum statements and enum members ([#715](rokucommunity/brighterscript#715)) - nested namespace support ([#708](rokucommunity/brighterscript#708)) - upgrade to [[email protected]](https://github.com/rokucommunity/roku-deploy/blob/master/CHANGELOG.md#392---2022-10-03). Notable changes since 3.9.1: - Replace minimatch with picomatch ([roku-deploy#101](rokucommunity/roku-deploy#101)) - fixes signature help resolution for callexpressions ([#707](rokucommunity/brighterscript#707)) - Fix transpilation of simple else block with leading comment ([#712](rokucommunity/brighterscript#712))
[0.60.6](rokucommunity/brighterscript@v0.60.5...0.60.6) - 2022-11-08 - double `super` call transpile in subclasses ([#740](rokucommunity/brighterscript#740)) - issues with Roku doc scraper and adds missing components ([#736](rokucommunity/brighterscript#736)) [0.60.5](rokucommunity/brighterscript@v0.60.4...0.60.5) - 2022-11-03 - Refactor SymbolTable and AST parent logic so that SymbolTables get their parent symbol table from its own (AstNode) ([#732](rokucommunity/brighterscript#732)) - Significant performance boost in `validate()` by caching `getCallableByName` ([#739](rokucommunity/brighterscript#739)) - Add diagnostic when using namespaces as variables ([#738](rokucommunity/brighterscript#738)) - Fix crash in `getDefinition` ([#734](rokucommunity/brighterscript#734)) [0.60.4](rokucommunity/brighterscript@v0.60.3...0.60.4) - 2022-10-28 - Add `name` to symbol table ([#728](rokucommunity/brighterscript#728)) - Allow `continue` as local var ([#730](rokucommunity/brighterscript#730)) - language server semanticToken request now waits until validate finishes ([#727](rokucommunity/brighterscript#727)) [0.60.3](rokucommunity/brighterscript@v0.60.2...0.60.3) - 2022-10-20 - better parse recovery for unknown function parameter types ([#722](rokucommunity/brighterscript#722)) [0.60.2](rokucommunity/brighterscript@v0.60.1...0.60.2) - 2022-10-18 - if statement block var bug ([#698](rokucommunity/brighterscript#698)) [0.60.1](rokucommunity/brighterscript@v0.60.0...0.60.1) - 2022-10-18 - Beter location for bs1042 ([#719](rokucommunity/brighterscript#719)) [0.60.0](rokucommunity/brighterscript@v0.59.0...0.60.0) - 2022-10-10 - goto definition for enum statements and enum members ([#715](rokucommunity/brighterscript#715)) - nested namespace support ([#708](rokucommunity/brighterscript#708)) - upgrade to [[email protected]](https://github.com/rokucommunity/roku-deploy/blob/master/CHANGELOG.md#392---2022-10-03). Notable changes since 3.9.1: - Replace minimatch with picomatch ([roku-deploy#101](rokucommunity/roku-deploy#101)) - fixes signature help resolution for callexpressions ([#707](rokucommunity/brighterscript#707)) - Fix transpilation of simple else block with leading comment ([#712](rokucommunity/brighterscript#712))
SUMMARY
Allow nested namespaces (fixes #624).
DETAILS
Disclaimer - this is my first go-round in the codebase, so if I've everything I've done here is totally wrong, don't hesitate to say so! Any feedback appreciated.
Here's the problems I attempted to solve:
We need to unblock
namespace
statements, but, they aren't generically allowed -- they can't be inside a function or a class, for example. To accomplish this I invented a new concept called aParserScope
, kept on a stack, that tries to express the stack of namespaces, classes, and functions below us. As long as the stack consists only of namespaces, it's OK to declare a namespace.Once you have declared a namespace, you need a way to fully qualify the name to include your parent namespaces. The way I've chosen to do this is determine the set of parent name expressions at parse time -- we have the stack, so we can grab the name expressions of all containing namespaces and hold onto them in the namespace statement, consuming them at the appropriate time (when we know whether it's brightscript or brighterscript mode).
Last, we need a diagnostic adjustment. I chose to leave the root-level diagnostic alone (there's no callers anymore, but there might be one in the future), and invent a new one for the "root- or namespace-level" requirement.
POSSIBLE ALTERNATIVES
I am most hesitant about the code for problem (2). I actually didn't need to do this to get it working; I could create the NamespaceStatement as normal, and have the NamespaceStatement recursively walk up the
findAncestor
tree atgetName()
time to construct the qualified name.What I didn't like about that approach is that running the compiler on my sample program produced the right output, but the unit tests gave me a lot of grief -- I kept running the parser, examining the namespace statement references, and all those statements were divorced of any tree, so the names were wrong (unqualified), even though the code generated the right output. (Maybe this is expected and I'm testing the wrong thing here; if so, perhaps we could swap implementation of (2) to walk ancestors instead, since I see that pattern in a couple other places in
Statements.ts
).TESTING