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

Class-based Hooks for rendering #7

Open
volks73 opened this issue Apr 27, 2020 · 3 comments
Open

Class-based Hooks for rendering #7

volks73 opened this issue Apr 27, 2020 · 3 comments
Assignees
Labels
enhancement New feature or request

Comments

@volks73
Copy link
Member

volks73 commented Apr 27, 2020

Hooks should be added to the rendering stage, and eventually the lexer and parser, that would allow, relatively easy extension and customization to the template compiling process.

Initial implementation would be a Pre- and Post- hook for rendering each node in an Abstract Syntax Tree (AST) from the parser. Each hook would be a LabVIEW Class that can be inherited to create a custom hook. For example, there would be a "abstract" LabVIEW Class called "Hook", which has a single method to be overridden: Execute or Do. Then, for rendering each node, there would be a call to the Execute method before and after rendering (pre and post, respectively).

I think the Context class will need to be expanded to be a container for the Hook objects because there would be two Hook objects per node (Section, Variable, Comment, etc.). The Context would then be an optional input to the Compile VI. The default Context would have no hooks and just create the default data stack context.

The hooks can then be used to implement the logging feature in #3.

@volks73 volks73 added the enhancement New feature or request label Apr 27, 2020
@volks73 volks73 self-assigned this Apr 27, 2020
@volks73
Copy link
Member Author

volks73 commented May 3, 2020

I think this is going to require changing the private Context class to a public, but low-level class and changing the terminal connectors for all of the "major" VIs to take a Context object in and output the same Context object out. This way the hooks are passed to each phase of the compilation (Lex, Parse, and Rend). The Context class could also contain the Loader, which would reduce the number of terminals on the Compile VIs. It might also be reasonable to move the Compile VIs to the Context class. Not sure on this last one.

@volks73
Copy link
Member Author

volks73 commented May 17, 2020

I have been experimenting with changing the Context support class to a public, top-level Compiler class and added Parser and Renderer classes. The Compiler class contains Lexer, Parser, and Renderer objects that are instances of their respective classes. Each class has an Initialize.vi, Run.vi, and Shutdown.vi that must be executed for each phase of the compilation. These three VIs can be override b a sub-class in the future.

This architecture and project organization change is turning out quite nicely. Everything feels cleaner, clearer, more organized, more extensible, and more testable. I am just about to merge this experiment, but it will necessitate a major version bump in the package from v1 to v2 because the Low-Level API has changed significantly.

I mention this here because I think this new architecture eliminates the need for hooks. I tried implementing hooks (Pre, Post, etc.) for each class, but the API for defining a hook and attaching it to an object was becoming complicated and unwieldy. For example, data member access getter/reader and setter/writer methods more been needed for each pre and post hook for each class in order to execute and act on the same object before and after compilation because objects are pass by value.

Also, in this new architecture, it makes sense to have each hook object be a member of the Lexer, Parser, Renderer, or Compiler class. The various "phase" classes should not be descendants of a Hook class, i.e. it makes more sense for phase objects to have a "has a" relationship than an "is a" relationship with hooks. This is fine, but then the hook should take the phase object as an input and output. Without getting weird with Data Value References (DVRs), there is not really a good way for a child data member value to be stored in a parent class but have a reference/handle to the parent. It just gets messy real quick.

This new architecture seems to eliminate the possibility of implementing hooks, event though earlier I mentioned, "...more extensible...". The functionality provided by hooks, i.e. executing custom code before and after various events during the compilation process, can still be achieved with the new architecture. Instead of needing another class and creating more objects and then an interface for attaching and accessing the functionality, the Lexer class could just be sub-classed. In other words, if a developer want to execute custom code before and after lexing but without modifying the lexing implementation, i.e. extend functionality, the developer can just create a new class that inherits from the Lexer class and overrides the parent's Run.vi to execute the before and after code. The developer can still use the parent's functionality in the override as well. This is essentially that same thing as creating and attaching a hook, but follows more naturally from the new architecture and OOP refactoring.

I think this idea is cool, but it was an idea for the v1 architecture and organization. The desired outcome of this idea: "extend the functionality of the compiler without modification" is still desired but it can be achieved without the need for implementing hooks.

@volks73
Copy link
Member Author

volks73 commented May 17, 2020

See e48e048 for the new architecture.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant