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

Allow nested namespaces #708

Merged

Conversation

elliot-nelson
Copy link
Contributor

@elliot-nelson elliot-nelson commented Oct 2, 2022

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:

  1. 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 a ParserScope, 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.

  2. 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).

  3. 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 at getName() 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

  • Ran locally on a sample program with a few namespaces and methods, produced the expected output.
  • Added one new test, adjusted one expected test.
  • Adjusted one unexpected test -- it seems like there was a syntax error in a test regarding completions, which I'm not sure was intentional or not, but had to remove to get the test passing.

@elliot-nelson
Copy link
Contributor Author

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".

@elsassph
Copy link
Contributor

elsassph commented Oct 2, 2022

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 (getName) by walking the AST using the findAncestor method.

@TwitchBronBron
Copy link
Member

TwitchBronBron commented Oct 2, 2022

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.

Copy link
Member

@TwitchBronBron TwitchBronBron left a 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.

src/parser/Parser.spec.ts Show resolved Hide resolved
src/parser/Statement.ts Outdated Show resolved Hide resolved
src/parser/Parser.ts Outdated Show resolved Hide resolved
@elliot-nelson
Copy link
Contributor Author

elliot-nelson commented Oct 3, 2022

Thanks all for the feedback, based on comments I have:

  • Cut the ParserScope stuff
  • Updated NamespaceStatement to walk its ancestors to get the full dotted namespace name
  • The test for Parser just confirms you can create nested namespaces
  • The test for Statement now confirms that a NamespaceStatement retrieves its full dotted name, if in a program
  • The test for BrsFileValidator now confirms that a namespace can't be declared in the wrong place
  • Updated namespace docs to add the new allowed syntax

(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.)

Copy link
Member

@TwitchBronBron TwitchBronBron left a 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!

src/bscPlugin/validation/BrsFileValidator.spec.ts Outdated Show resolved Hide resolved
src/bscPlugin/validation/BrsFileValidator.spec.ts Outdated Show resolved Hide resolved
@elliot-nelson
Copy link
Contributor Author

Thanks!

  • Updated BrsFileValidator tests to use diagnostic expectations.

Copy link
Member

@TwitchBronBron TwitchBronBron left a 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.

@TwitchBronBron TwitchBronBron merged commit 8a065b7 into rokucommunity:master Oct 4, 2022
@elliot-nelson elliot-nelson deleted the enelson/nested-namespace branch October 4, 2022 11:58
xgouchet added a commit to DataDog/dd-sdk-roku that referenced this pull request Nov 22, 2022
[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))
xgouchet added a commit to DataDog/dd-sdk-roku that referenced this pull request Nov 23, 2022
[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))
xgouchet added a commit to DataDog/dd-sdk-roku that referenced this pull request Nov 23, 2022
[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))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

can we have nested namespaces please?
3 participants