diff options
| author | Tim Foley <tfoley@nvidia.com> | 2017-08-11 12:34:58 -0700 |
|---|---|---|
| committer | Tim Foley <tfoley@nvidia.com> | 2017-08-11 12:34:58 -0700 |
| commit | b2febc7966c2533f756a6829656423f05b2f21e5 (patch) | |
| tree | 8be4a0505bd121edbbcf5dd5b3cd9d43c51ec377 /source/slang/syntax.cpp | |
| parent | db4079f7e3635a6a61b8725643b1d7ecf68b97d8 (diff) | |
Look up declaration keywords using ordinary scoping.
The existing parser code was doing string-based matching on the lookahead token to figure out how to parse a declaration, e.g.:
```
if(lookAhead == "struct") { /* do struct thing */ }
else if(lookAhead == "interface") { /* do interface thing * }
...
```
That approach has some annoying down-sides:
- It is slower than it needs to be
- It is annoying to deal with cases where the available declaration keywords might differ by language
- Most importantly, it is not possible for us to introduce "extended" keywords that the user can make use of, but which can be ignored by the user and treated as an ordinary identifier.
That last part is important. Suppose the user wanted to have a local variable named `import`, but we also had a Slang extension that added an `import` keyword. Then a line of code like `import += 1` would lead to a failure because we'd try to parse an import declaration, even when it is obvious that the user meant their local variable. This would mean that Slang can't parse existing user code that might clash with syntax extensions. This issue is the reason why we currently have keywords like `__import`.
A traditional solution in a compiler is to map keywords to distinct token codes as part of lexing, which eliminates the first conern (performance) because now we can dispatch with `switch`. It can also aleviate the second concern if we add/remove names from the string->code mapping based on language (the rest of the parsing logic doesn't have to know about keywords being added/removed).
The solution we go for here is more aggressive.
Instead of mapping keyword names to special token codes during lexing, we instead introduce logical "syntax declarations" into the AST, which are looked up using the ordinary scoping rules of the language.
Depending on what code is imported into the scope where parsing is going on, different keywords may then be visible.
This solves our last concern, since a user-defined variable that just happens to use the same name as a keyword is now allowed to shadow the imported declaration for syntax (this is akin to, e.g., Scheme where there really aren't any "keywords").
This also opens the door to the possibility of eventually allowing user to define their own syntax (again, like Scheme).
For now I'm only using this for the declaration keywords.
With this change it should be pretty easy to also add statement keywords in the same fashion.
Diffstat (limited to 'source/slang/syntax.cpp')
| -rw-r--r-- | source/slang/syntax.cpp | 40 |
1 files changed, 35 insertions, 5 deletions
diff --git a/source/slang/syntax.cpp b/source/slang/syntax.cpp index 33fa5be20..0135e2316 100644 --- a/source/slang/syntax.cpp +++ b/source/slang/syntax.cpp @@ -62,11 +62,14 @@ namespace Slang #undef SYNTAX_CLASS #undef ABSTRACT_SYNTAX_CLASS -#define ABSTRACT_SYNTAX_CLASS(NAME, BASE) /* empty */ -#define SYNTAX_CLASS(NAME, BASE) \ - void NAME::accept(NAME::Visitor* visitor, void* extra) \ - { visitor->dispatch_##NAME(this, extra); } \ - void* SyntaxClassBase::Impl<NAME>::createFunc() { return new NAME(); } +#define ABSTRACT_SYNTAX_CLASS(NAME, BASE) \ + SyntaxClassBase::ClassInfo const SyntaxClassBase::Impl<NAME>::kClassInfo = { #NAME, &SyntaxClassBase::Impl<BASE>::kClassInfo, nullptr }; + +#define SYNTAX_CLASS(NAME, BASE) \ + void NAME::accept(NAME::Visitor* visitor, void* extra) \ + { visitor->dispatch_##NAME(this, extra); } \ + void* SyntaxClassBase::Impl<NAME>::createFunc() { return new NAME(); } \ + SyntaxClassBase::ClassInfo const SyntaxClassBase::Impl<NAME>::kClassInfo = { #NAME, &SyntaxClassBase::Impl<BASE>::kClassInfo, &SyntaxClassBase::Impl<NAME>::createFunc }; #include "expr-defs.h" #include "decl-defs.h" #include "modifier-defs.h" @@ -74,8 +77,35 @@ namespace Slang #include "type-defs.h" #include "val-defs.h" +SyntaxClassBase::ClassInfo const SyntaxClassBase::Impl<RefObject>::kClassInfo = { "RefObject", nullptr, nullptr }; + +ABSTRACT_SYNTAX_CLASS(SyntaxNodeBase, RefObject); +ABSTRACT_SYNTAX_CLASS(SyntaxNode, SyntaxNodeBase); +ABSTRACT_SYNTAX_CLASS(ModifiableSyntaxNode, SyntaxNode); +ABSTRACT_SYNTAX_CLASS(DeclBase, ModifiableSyntaxNode); +ABSTRACT_SYNTAX_CLASS(Decl, DeclBase); +ABSTRACT_SYNTAX_CLASS(Stmt, ModifiableSyntaxNode); +ABSTRACT_SYNTAX_CLASS(Val, RefObject); +ABSTRACT_SYNTAX_CLASS(Type, Val); +ABSTRACT_SYNTAX_CLASS(Modifier, SyntaxNodeBase); +ABSTRACT_SYNTAX_CLASS(Expr, SyntaxNode); + #include "object-meta-end.h" +bool SyntaxClassBase::isSubClassOfImpl(SyntaxClassBase const& super) const +{ + SyntaxClassBase::ClassInfo const* info = classInfo; + while (info) + { + if (info == super.classInfo) + return true; + + info = info->baseClass; + } + + return false; +} + void Type::accept(IValVisitor* visitor, void* extra) { accept((ITypeVisitor*)visitor, extra); |
