summaryrefslogtreecommitdiffstats
path: root/source/slang/parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/parser.cpp')
-rw-r--r--source/slang/parser.cpp158
1 files changed, 145 insertions, 13 deletions
diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp
index ad4eef9a6..fee5d04b6 100644
--- a/source/slang/parser.cpp
+++ b/source/slang/parser.cpp
@@ -127,6 +127,8 @@ namespace Slang
ContainerDecl* containerDecl,
TokenType closingToken);
+ static RefPtr<Decl> parseEnumDecl(Parser* parser);
+
// Parse the `{}`-delimeted body of an aggregate type declaration
static void parseAggTypeDeclBody(
Parser* parser,
@@ -215,7 +217,7 @@ namespace Slang
return tokenType;
}
- // Skip balanced
+ // Skip balanced
static TokenType SkipToMatchingToken(
TokenReader* reader,
TokenType tokenType)
@@ -403,7 +405,7 @@ namespace Slang
// Skip balanced tokens and try again.
TokenType skipped = SkipBalancedToken(tokenReader);
-
+
// If we happened to find a matched pair of tokens, and
// the end of it was a token we were looking for,
// then recover here
@@ -1097,7 +1099,7 @@ namespace Slang
{
public:
RefPtr<Scope> scope;
- void visitDeclRefExpr(DeclRefExpr* expr)
+ void visitDeclRefExpr(DeclRefExpr* expr)
{
expr->scope = scope;
}
@@ -1161,7 +1163,7 @@ namespace Slang
}
else
parseFuncDeclHeaderInner(nullptr);
-
+
return retDecl;
}
@@ -1471,7 +1473,7 @@ namespace Slang
RefPtr<Decl> newDecl)
{
SLANG_ASSERT(newDecl);
-
+
if( decl )
{
group = new DeclGroup();
@@ -1655,11 +1657,24 @@ namespace Slang
{
TypeSpec typeSpec;
- // We may see a `struct` type specified here, and need to act accordingly
+ // We may see a `struct` (or `enum` or `class`) tag specified here, and need to act accordingly
//
// TODO(tfoley): Handle the case where the user is just using `struct`
// as a way to name an existing struct "tag" (e.g., `struct Foo foo;`)
//
+ // TODO: We should really make these keywords be registered like any other
+ // syntax category, rather than be special-cased here. The main issue here
+ // is that we need to allow them to be used as type specififers, as in:
+ //
+ // struct Foo { int x } foo;
+ //
+ // The ideal answer would be to register certain keywords as being able
+ // to parse a type specififer, and look for those keywords here.
+ // We should ideally add special case logic that bails out of declarator
+ // parsing iff we have one of these kinds of type specififers and the
+ // closing `}` is at the end of its line, as a bit of a special case
+ // to allow the common idiom.
+ //
if( parser->LookAheadToken("struct") )
{
auto decl = parser->ParseStruct();
@@ -1674,6 +1689,13 @@ namespace Slang
typeSpec.expr = createDeclRefType(parser, decl);
return typeSpec;
}
+ else if(parser->LookAheadToken("enum"))
+ {
+ auto decl = parseEnumDecl(parser);
+ typeSpec.decl = decl;
+ typeSpec.expr = createDeclRefType(parser, decl);
+ return typeSpec;
+ }
Token typeName = parser->ReadToken(TokenType::Identifier);
@@ -1742,6 +1764,51 @@ namespace Slang
return result;
}
+ // It is possible that we have a plain `struct`, `enum`,
+ // or similar declaration that isn't being used to declare
+ // any variable, and the user didn't put a trailing
+ // semicolon on it:
+ //
+ // struct Batman
+ // {
+ // int cape;
+ // }
+ //
+ // We want to allow this syntax (rather than give an
+ // inscrutable error), but also support the less common
+ // idiom where that declaration is used as part of
+ // a variable declaration:
+ //
+ // struct Robin
+ // {
+ // float tights;
+ // } boyWonder;
+ //
+ // As a bit of a hack (insofar as it means we aren't
+ // *really* compatible with arbitrary HLSL code), we
+ // will check if there are any more tokens on the
+ // same line as the closing `}`, and if not, we
+ // will treat it like the end of the declaration.
+ //
+ // Just as a safety net, only apply this logic for
+ // a file that is being passed in as "true" Slang code.
+ //
+ if(parser->translationUnit->sourceLanguage == SourceLanguage::Slang)
+ {
+ if(typeSpec.decl)
+ {
+ if(peekToken(parser).flags & TokenFlag::AtStartOfLine)
+ {
+ // The token after the `}` is at the start of its
+ // own line, which means it can't be on the same line.
+ //
+ // This means the programmer probably wants to
+ // just treat this as a declaration.
+ return declGroupBuilder.getResult();
+ }
+ }
+ }
+
InitDeclarator initDeclarator = ParseInitDeclarator(parser);
@@ -1978,7 +2045,7 @@ namespace Slang
//
// However, that is an uncommon occurence, and trying
// to continue parsing semantics here even if we didn't
- // see a colon forces us to be careful about
+ // see a colon forces us to be careful about
// avoiding an infinite loop here.
if (!AdvanceIf(parser, TokenType::Colon))
{
@@ -2921,6 +2988,71 @@ namespace Slang
return rs;
}
+ static RefPtr<EnumCaseDecl> parseEnumCaseDecl(Parser* parser)
+ {
+ RefPtr<EnumCaseDecl> decl = new EnumCaseDecl();
+ decl->nameAndLoc = expectIdentifier(parser);
+
+ if(AdvanceIf(parser, TokenType::OpAssign))
+ {
+ decl->initExpr = parser->ParseArgExpr();
+ }
+
+ return decl;
+ }
+
+ static RefPtr<Decl> parseEnumDecl(Parser* parser)
+ {
+ RefPtr<EnumDecl> decl = new EnumDecl();
+ parser->FillPosition(decl);
+
+ parser->ReadToken("enum");
+
+ // HACK: allow the user to write `enum class` in case
+ // they are trying to share a header between C++ and Slang.
+ //
+ // TODO: diagnose this with a warning some day, and move
+ // toward deprecating it.
+ //
+ AdvanceIf(parser, "class");
+
+ decl->nameAndLoc = expectIdentifier(parser);
+
+ auto parseEnumDeclInner = [&](GenericDecl*)
+ {
+ parseOptionalInheritanceClause(parser, decl);
+ parser->ReadToken(TokenType::LBrace);
+
+ while(!AdvanceIfMatch(parser, TokenType::RBrace))
+ {
+ RefPtr<EnumCaseDecl> caseDecl = parseEnumCaseDecl(parser);
+ AddMember(decl, caseDecl);
+
+ if(AdvanceIf(parser, TokenType::RBrace))
+ break;
+
+ parser->ReadToken(TokenType::Comma);
+ }
+ return decl;
+ };
+
+ if (parser->LookAheadToken(TokenType::OpLess))
+ {
+ RefPtr<GenericDecl> genericDecl = new GenericDecl();
+ parser->FillPosition(genericDecl);
+ parser->PushScope(genericDecl);
+ ParseGenericDeclImpl(parser, genericDecl, parseEnumDeclInner);
+ parser->PopScope();
+ return genericDecl;
+ }
+ else
+ {
+ parseEnumDeclInner(nullptr);
+ }
+
+ return decl;
+ }
+
static RefPtr<Stmt> ParseSwitchStmt(Parser* parser)
{
RefPtr<SwitchStmt> stmt = new SwitchStmt();
@@ -3205,7 +3337,7 @@ namespace Slang
Modifiers modifiers)
{
RefPtr<DeclStmt>varDeclrStatement = new DeclStmt();
-
+
FillPosition(varDeclrStatement.Ptr());
auto decl = ParseDeclWithModifiers(this, currentScope->containerDecl, modifiers);
varDeclrStatement->decl = decl;
@@ -3348,10 +3480,10 @@ namespace Slang
RefPtr<ExpressionStmt> Parser::ParseExpressionStatement()
{
RefPtr<ExpressionStmt> statement = new ExpressionStmt();
-
+
FillPosition(statement.Ptr());
statement->Expression = ParseExpression();
-
+
ReadToken(TokenType::Semicolon);
return statement;
}
@@ -3544,7 +3676,7 @@ namespace Slang
for(;;)
{
auto nextOpPrec = GetOpLevel(parser, parser->tokenReader.PeekTokenType());
-
+
if((GetAssociativityFromLevel(nextOpPrec) == Associativity::Right) ? (nextOpPrec < opPrec) : (nextOpPrec <= opPrec))
break;
@@ -3632,7 +3764,7 @@ namespace Slang
}
#endif
}
-
+
// We *might* be looking at an application of a generic to arguments,
// but we need to disambiguate to make sure.
static RefPtr<Expr> maybeParseGenericApp(
@@ -4021,7 +4153,7 @@ namespace Slang
parser->FillPosition(memberExpr.Ptr());
memberExpr->BaseExpression = expr;
- parser->ReadToken(TokenType::Dot);
+ parser->ReadToken(TokenType::Dot);
memberExpr->name = expectIdentifier(parser).name;
if (peekTokenType(parser) == TokenType::OpLess)