#include "parser.h" #include #include "compiler.h" #include "lookup.h" #include "visitor.h" namespace Slang { // Pre-declare static Name* getName(Parser* parser, String const& text); // Helper class useful to build a list of modifiers. struct ModifierListBuilder { ModifierListBuilder() { m_next = &m_result; } void add(Modifier* modifier) { // Doesn't handle SharedModifiers SLANG_ASSERT(modifier->As() == nullptr); // Splice at end *m_next = modifier; m_next = &modifier->next; } template T* find() const { Modifier* cur = m_result; while (cur) { T* castCur = cur->As(); if (castCur) { return castCur; } cur = cur->next; } return nullptr; } template bool hasType() const { return find() != nullptr; } RefPtr getFirst() { return m_result; }; protected: RefPtr m_result; RefPtr* m_next; }; enum Precedence : int { Invalid = -1, Comma, Assignment, TernaryConditional, LogicalOr, LogicalAnd, BitOr, BitXor, BitAnd, EqualityComparison, RelationalComparison, BitShift, Additive, Multiplicative, Prefix, Postfix, }; // TODO: implement two pass parsing for file reference and struct type recognition class Parser { public: TranslationUnitRequest* translationUnit; int anonymousCounter = 0; RefPtr outerScope; RefPtr currentScope; TokenReader tokenReader; DiagnosticSink * sink; int genericDepth = 0; // Have we seen any `import` declarations? If so, we need // to parse function bodies completely, even if we are in // "rewrite" mode. bool haveSeenAnyImportDecls = false; // Is the parser in a "recovering" state? // During recovery we don't emit additional errors, until we find // a token that we expected, when we exit recovery. bool isRecovering = false; void FillPosition(SyntaxNode * node) { node->loc = tokenReader.PeekLoc(); } void PushScope(ContainerDecl* containerDecl) { RefPtr newScope = new Scope(); newScope->containerDecl = containerDecl; newScope->parent = currentScope; currentScope = newScope; } void pushScopeAndSetParent(ContainerDecl* containerDecl) { containerDecl->ParentDecl = currentScope->containerDecl; PushScope(containerDecl); } void PopScope() { currentScope = currentScope->parent; } Parser( TokenSpan const& _tokens, DiagnosticSink * sink, RefPtr const& outerScope) : tokenReader(_tokens) , sink(sink) , outerScope(outerScope) {} Parser(const Parser & other) = default; Session* getSession() { return translationUnit->compileRequest->mSession; } RefPtr Parse(); Token ReadToken(); Token ReadToken(TokenType type); Token ReadToken(const char * string); bool LookAheadToken(TokenType type, int offset = 0); bool LookAheadToken(const char * string, int offset = 0); void parseSourceFile(ModuleDecl* program); RefPtr ParseProgram(); RefPtr ParseStruct(); RefPtr ParseClass(); RefPtr ParseStatement(); RefPtr parseBlockStatement(); RefPtr parseVarDeclrStatement(Modifiers modifiers); RefPtr parseIfStatement(); RefPtr ParseForStatement(); RefPtr ParseWhileStatement(); RefPtr ParseDoWhileStatement(); RefPtr ParseBreakStatement(); RefPtr ParseContinueStatement(); RefPtr ParseReturnStatement(); RefPtr ParseExpressionStatement(); RefPtr ParseExpression(Precedence level = Precedence::Comma); // Parse an expression that might be used in an initializer or argument context, so we should avoid operator-comma inline RefPtr ParseInitExpr() { return ParseExpression(Precedence::Assignment); } inline RefPtr ParseArgExpr() { return ParseExpression(Precedence::Assignment); } RefPtr ParseLeafExpression(); RefPtr ParseParameter(); RefPtr ParseType(); TypeExp ParseTypeExp(); Parser & operator = (const Parser &) = delete; }; // Forward Declarations static void ParseDeclBody( Parser* parser, ContainerDecl* containerDecl, TokenType closingToken); static RefPtr parseEnumDecl(Parser* parser); // Parse the `{}`-delimeted body of an aggregate type declaration static void parseAggTypeDeclBody( Parser* parser, AggTypeDeclBase* decl); static RefPtr ParseOptSemantics( Parser* parser); static void ParseOptSemantics( Parser* parser, Decl* decl); static RefPtr ParseDecl( Parser* parser, ContainerDecl* containerDecl); static RefPtr ParseSingleDecl( Parser* parser, ContainerDecl* containerDecl); // static void Unexpected( Parser* parser) { // Don't emit "unexpected token" errors if we are in recovering mode if (!parser->isRecovering) { parser->sink->diagnose(parser->tokenReader.PeekLoc(), Diagnostics::unexpectedToken, parser->tokenReader.PeekTokenType()); // Switch into recovery mode, to suppress additional errors parser->isRecovering = true; } } static void Unexpected( Parser* parser, char const* expected) { // Don't emit "unexpected token" errors if we are in recovering mode if (!parser->isRecovering) { parser->sink->diagnose(parser->tokenReader.PeekLoc(), Diagnostics::unexpectedTokenExpectedTokenName, parser->tokenReader.PeekTokenType(), expected); // Switch into recovery mode, to suppress additional errors parser->isRecovering = true; } } static void Unexpected( Parser* parser, TokenType expected) { // Don't emit "unexpected token" errors if we are in recovering mode if (!parser->isRecovering) { parser->sink->diagnose(parser->tokenReader.PeekLoc(), Diagnostics::unexpectedTokenExpectedTokenType, parser->tokenReader.PeekTokenType(), expected); // Switch into recovery mode, to suppress additional errors parser->isRecovering = true; } } static TokenType SkipToMatchingToken(TokenReader* reader, TokenType tokenType); // Skip a singel balanced token, which is either a single token in // the common case, or a matched pair of tokens for `()`, `[]`, and `{}` static TokenType SkipBalancedToken( TokenReader* reader) { TokenType tokenType = reader->AdvanceToken().type; switch (tokenType) { default: break; case TokenType::LParent: tokenType = SkipToMatchingToken(reader, TokenType::RParent); break; case TokenType::LBrace: tokenType = SkipToMatchingToken(reader, TokenType::RBrace); break; case TokenType::LBracket: tokenType = SkipToMatchingToken(reader, TokenType::RBracket); break; } return tokenType; } // Skip balanced static TokenType SkipToMatchingToken( TokenReader* reader, TokenType tokenType) { for (;;) { if (reader->IsAtEnd()) return TokenType::EndOfFile; if (reader->PeekTokenType() == tokenType) { reader->AdvanceToken(); return tokenType; } SkipBalancedToken(reader); } } // Is the given token type one that is used to "close" a // balanced construct. static bool IsClosingToken(TokenType tokenType) { switch (tokenType) { case TokenType::EndOfFile: case TokenType::RBracket: case TokenType::RParent: case TokenType::RBrace: return true; default: return false; } } // Expect an identifier token with the given content, and consume it. Token Parser::ReadToken(const char* expected) { if (tokenReader.PeekTokenType() == TokenType::Identifier && tokenReader.PeekToken().Content == expected) { isRecovering = false; return tokenReader.AdvanceToken(); } if (!isRecovering) { Unexpected(this, expected); return tokenReader.PeekToken(); } else { // Try to find a place to recover for (;;) { // The token we expected? // Then exit recovery mode and pretend like all is well. if (tokenReader.PeekTokenType() == TokenType::Identifier && tokenReader.PeekToken().Content == expected) { isRecovering = false; return tokenReader.AdvanceToken(); } // Don't skip past any "closing" tokens. if (IsClosingToken(tokenReader.PeekTokenType())) { return tokenReader.PeekToken(); } // Skip balanced tokens and try again. SkipBalancedToken(&tokenReader); } } } Token Parser::ReadToken() { return tokenReader.AdvanceToken(); } static bool TryRecover( Parser* parser, TokenType const* recoverBefore, int recoverBeforeCount, TokenType const* recoverAfter, int recoverAfterCount) { if (!parser->isRecovering) return true; // Determine if we are looking for common closing tokens, // so that we can know whether or we are allowed to skip // over them. bool lookingForEOF = false; bool lookingForRCurly = false; bool lookingForRParen = false; bool lookingForRSquare = false; for (int ii = 0; ii < recoverBeforeCount; ++ii) { switch (recoverBefore[ii]) { default: break; case TokenType::EndOfFile: lookingForEOF = true; break; case TokenType::RBrace: lookingForRCurly = true; break; case TokenType::RParent: lookingForRParen = true; break; case TokenType::RBracket: lookingForRSquare = true; break; } } for (int ii = 0; ii < recoverAfterCount; ++ii) { switch (recoverBefore[ii]) { default: break; case TokenType::EndOfFile: lookingForEOF = true; break; case TokenType::RBrace: lookingForRCurly = true; break; case TokenType::RParent: lookingForRParen = true; break; case TokenType::RBracket: lookingForRSquare = true; break; } } TokenReader* tokenReader = &parser->tokenReader; for (;;) { TokenType peek = tokenReader->PeekTokenType(); // Is the next token in our recover-before set? // If so, then we have recovered successfully! for (int ii = 0; ii < recoverBeforeCount; ++ii) { if (peek == recoverBefore[ii]) { parser->isRecovering = false; return true; } } // If we are looking at a token in our recover-after set, // then consume it and recover for (int ii = 0; ii < recoverAfterCount; ++ii) { if (peek == recoverAfter[ii]) { tokenReader->AdvanceToken(); parser->isRecovering = false; return true; } } // Don't try to skip past end of file if (peek == TokenType::EndOfFile) return false; switch (peek) { // Don't skip past simple "closing" tokens, *unless* // we are looking for a closing token case TokenType::RParent: case TokenType::RBracket: if (lookingForRParen || lookingForRSquare || lookingForRCurly || lookingForEOF) { // We are looking for a closing token, so it is okay to skip these } else return false; break; // Don't skip a `}`, to avoid spurious errors, // with the exception of when we are looking for EOF case TokenType::RBrace: if (lookingForRCurly || lookingForEOF) { // We are looking for end-of-file, so it is okay to skip here } else return false; } // 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 for (int ii = 0; ii < recoverAfterCount; ++ii) { if (skipped == recoverAfter[ii]) { parser->isRecovering = false; return true; } } } } static bool TryRecoverBefore( Parser* parser, TokenType before0) { TokenType recoverBefore[] = { before0 }; return TryRecover(parser, recoverBefore, 1, nullptr, 0); } // Default recovery strategy, to use inside `{}`-delimeted blocks. static bool TryRecover( Parser* parser) { TokenType recoverBefore[] = { TokenType::RBrace }; TokenType recoverAfter[] = { TokenType::Semicolon }; return TryRecover(parser, recoverBefore, 1, recoverAfter, 1); } Token Parser::ReadToken(TokenType expected) { if (tokenReader.PeekTokenType() == expected) { isRecovering = false; return tokenReader.AdvanceToken(); } if (!isRecovering) { Unexpected(this, expected); return tokenReader.PeekToken(); } else { // Try to find a place to recover if (TryRecoverBefore(this, expected)) { isRecovering = false; return tokenReader.AdvanceToken(); } return tokenReader.PeekToken(); } } bool Parser::LookAheadToken(const char * string, int offset) { TokenReader r = tokenReader; for (int ii = 0; ii < offset; ++ii) r.AdvanceToken(); return r.PeekTokenType() == TokenType::Identifier && r.PeekToken().Content == string; } bool Parser::LookAheadToken(TokenType type, int offset) { TokenReader r = tokenReader; for (int ii = 0; ii < offset; ++ii) r.AdvanceToken(); return r.PeekTokenType() == type; } // Consume a token and return true it if matches, otherwise false bool AdvanceIf(Parser* parser, TokenType tokenType) { if (parser->LookAheadToken(tokenType)) { parser->ReadToken(); return true; } return false; } // Consume a token and return true it if matches, otherwise false bool AdvanceIf(Parser* parser, char const* text) { if (parser->LookAheadToken(text)) { parser->ReadToken(); return true; } return false; } // Consume a token and return true if it matches, otherwise check // for end-of-file and expect that token (potentially producing // an error) and return true to maintain forward progress. // Otherwise return false. bool AdvanceIfMatch(Parser* parser, TokenType tokenType) { // If we've run into a syntax error, but haven't recovered inside // the block, then try to recover here. if (parser->isRecovering) { TryRecoverBefore(parser, tokenType); } if (AdvanceIf(parser, tokenType)) return true; if (parser->tokenReader.PeekTokenType() == TokenType::EndOfFile) { parser->ReadToken(tokenType); return true; } return false; } RefPtr Parser::Parse() { return ParseProgram(); } RefPtr ParseTypeDef(Parser* parser, void* /*userData*/) { RefPtr typeDefDecl = new TypeDefDecl(); // TODO(tfoley): parse an actual declarator auto type = parser->ParseTypeExp(); auto nameToken = parser->ReadToken(TokenType::Identifier); typeDefDecl->loc = nameToken.loc; typeDefDecl->nameAndLoc = NameLoc(nameToken); typeDefDecl->type = type; return typeDefDecl; } // Add a modifier to a list of modifiers being built static void AddModifier(RefPtr** ioModifierLink, RefPtr modifier) { RefPtr*& modifierLink = *ioModifierLink; // We'd like to add the modifier to the end of the list, // but we need to be careful, in case there is a "shared" // section of modifiers for multiple declarations. // // TODO: This whole approach is a mess because we are "accidentally quadratic" // when adding many modifiers. for(;;) { // At end of the chain? Done. if(!*modifierLink) break; // About to look at shared modifiers? Done. RefPtr linkMod = *modifierLink; if(linkMod.As()) { break; } // Otherwise: keep traversing the modifier list. modifierLink = &(*modifierLink)->next; } // Splice the modifier into the linked list // We need to deal with the case where the modifier to // be spliced in might actually be a modifier *list*, // so that we actually want to splice in at the // end of the new list... auto spliceLink = &modifier->next; while(*spliceLink) spliceLink = &(*spliceLink)->next; // Do the splice. *spliceLink = *modifierLink; *modifierLink = modifier; modifierLink = &modifier->next; } void addModifier( RefPtr syntax, RefPtr modifier) { auto modifierLink = &syntax->modifiers.first; AddModifier(&modifierLink, modifier); } // // '::'? identifier ('::' identifier)* static Token parseAttributeName(Parser* parser) { const SourceLoc scopedIdSourceLoc = parser->tokenReader.PeekLoc(); // Strip initial :: if there is one const TokenType initialTokenType = parser->tokenReader.PeekTokenType(); if (initialTokenType == TokenType::Scope) { parser->ReadToken(TokenType::Scope); } const Token firstIdentifier = parser->ReadToken(TokenType::Identifier); if (initialTokenType != TokenType::Scope && parser->tokenReader.PeekTokenType() != TokenType::Scope) { return firstIdentifier; } // Build up scoped string StringBuilder scopedIdentifierBuilder; if (initialTokenType == TokenType::Scope) { scopedIdentifierBuilder.Append('_'); } scopedIdentifierBuilder.Append(firstIdentifier.Content); while (parser->tokenReader.PeekTokenType() == TokenType::Scope) { parser->ReadToken(TokenType::Scope); scopedIdentifierBuilder.Append('_'); const Token nextIdentifier(parser->ReadToken(TokenType::Identifier)); scopedIdentifierBuilder.Append(nextIdentifier.Content); } // Make a 'token' const String scopedIdentifier(scopedIdentifierBuilder.ToString()); Token token(TokenType::Identifier, scopedIdentifier, scopedIdSourceLoc); token.ptrValue = getName(parser, token.Content); return token; } // Parse HLSL-style `[name(arg, ...)]` style "attribute" modifiers static void ParseSquareBracketAttributes(Parser* parser, RefPtr** ioModifierLink) { parser->ReadToken(TokenType::LBracket); const bool hasDoubleBracket = AdvanceIf(parser, TokenType::LBracket); for(;;) { // Note: When parsing we just construct an AST node for an // "unchecked" attribute, and defer all detailed semantic // checking until later. // // An alternative would be to perform lookup of an `AttributeDecl` // at this point, similar to what we do for `SyntaxDecl`, but it // seems better to not complicate the parsing process any more. // Token nameToken = parseAttributeName(parser); RefPtr modifier = new UncheckedAttribute(); modifier->name = nameToken.getName(); modifier->loc = nameToken.getLoc(); modifier->scope = parser->currentScope; if (AdvanceIf(parser, TokenType::LParent)) { // HLSL-style `[name(arg0, ...)]` attribute while (!AdvanceIfMatch(parser, TokenType::RParent)) { auto arg = parser->ParseArgExpr(); if (arg) { modifier->args.Add(arg); } if (AdvanceIfMatch(parser, TokenType::RParent)) break; parser->ReadToken(TokenType::Comma); } } AddModifier(ioModifierLink, modifier); if (AdvanceIfMatch(parser, TokenType::RBracket)) break; parser->ReadToken(TokenType::Comma); } if (hasDoubleBracket) { // Read the second ] parser->ReadToken(TokenType::RBracket); } } static TokenType peekTokenType(Parser* parser) { return parser->tokenReader.PeekTokenType(); } static Token advanceToken(Parser* parser) { return parser->ReadToken(); } static Token peekToken(Parser* parser) { return parser->tokenReader.PeekToken(); } static SyntaxDecl* tryLookUpSyntaxDecl( Parser* parser, Name* name) { // Let's look up the name and see what we find. auto lookupResult = lookUp( parser->getSession(), nullptr, // no semantics visitor available yet name, parser->currentScope); // If we didn't find anything, or the result was overloaded, // then we aren't going to be able to extract a single decl. if(!lookupResult.isValid() || lookupResult.isOverloaded()) return nullptr; auto decl = lookupResult.item.declRef.getDecl(); if( auto syntaxDecl = dynamic_cast(decl) ) { return syntaxDecl; } else { return nullptr; } } template bool tryParseUsingSyntaxDecl( Parser* parser, SyntaxDecl* syntaxDecl, RefPtr* outSyntax) { if (!syntaxDecl) return false; if (!syntaxDecl->syntaxClass.isSubClassOf()) return false; // Consume the token that specified the keyword auto keywordToken = advanceToken(parser); RefPtr parsedObject = syntaxDecl->parseCallback(parser, syntaxDecl->parseUserData); auto syntax = parsedObject.As(); if (syntax) { if (!syntax->loc.isValid()) { syntax->loc = keywordToken.loc; } } else if (parsedObject) { // Something was parsed, but it didn't have the expected type! SLANG_DIAGNOSE_UNEXPECTED(parser->sink, keywordToken, "parser callback did not return the expected type"); } *outSyntax = syntax; return true; } template bool tryParseUsingSyntaxDecl( Parser* parser, RefPtr* outSyntax) { if (peekTokenType(parser) != TokenType::Identifier) return false; auto nameToken = peekToken(parser); auto name = nameToken.getName(); auto syntaxDecl = tryLookUpSyntaxDecl(parser, name); if (!syntaxDecl) return false; return tryParseUsingSyntaxDecl(parser, syntaxDecl, outSyntax); } static Modifiers ParseModifiers(Parser* parser) { Modifiers modifiers; RefPtr* modifierLink = &modifiers.first; for (;;) { SourceLoc loc = parser->tokenReader.PeekLoc(); switch (peekTokenType(parser)) { default: // If we don't see a token type that we recognize, then // assume we are done with the modifier sequence. return modifiers; case TokenType::Identifier: { // We see an identifier ahead, and it might be the name // of a modifier keyword of some kind. Token nameToken = peekToken(parser); RefPtr parsedModifier; if (tryParseUsingSyntaxDecl(parser, &parsedModifier)) { parsedModifier->name = nameToken.getName(); if (!parsedModifier->loc.isValid()) { parsedModifier->loc = nameToken.loc; } AddModifier(&modifierLink, parsedModifier); continue; } // If there was no match for a modifier keyword, then we // must be at the end of the modifier sequence return modifiers; } break; // HLSL uses `[attributeName]` style for its modifiers, which closely // matches the C++ `[[attributeName]]` style. case TokenType::LBracket: ParseSquareBracketAttributes(parser, &modifierLink); break; } } } static Name* getName(Parser* parser, String const& text) { return parser->translationUnit->compileRequest->getNamePool()->getName(text); } static NameLoc expectIdentifier(Parser* parser) { return NameLoc(parser->ReadToken(TokenType::Identifier)); } static RefPtr parseImportDecl( Parser* parser, void* /*userData*/) { parser->haveSeenAnyImportDecls = true; auto decl = new ImportDecl(); decl->scope = parser->currentScope; if (peekTokenType(parser) == TokenType::StringLiteral) { auto nameToken = parser->ReadToken(TokenType::StringLiteral); auto nameString = getStringLiteralTokenValue(nameToken); auto moduleName = getName(parser, nameString); decl->moduleNameAndLoc = NameLoc(moduleName, nameToken.loc); } else { auto moduleNameAndLoc = expectIdentifier(parser); // We allow a dotted format for the name, as sugar if (peekTokenType(parser) == TokenType::Dot) { StringBuilder sb; sb << getText(moduleNameAndLoc.name); while (AdvanceIf(parser, TokenType::Dot)) { sb << "/"; sb << parser->ReadToken(TokenType::Identifier).Content; } moduleNameAndLoc.name = getName(parser, sb.ProduceString()); } decl->moduleNameAndLoc = moduleNameAndLoc; } parser->ReadToken(TokenType::Semicolon); return decl; } static NameLoc ParseDeclName( Parser* parser) { Token nameToken; if (AdvanceIf(parser, "operator")) { nameToken = parser->ReadToken(); switch (nameToken.type) { case TokenType::OpAdd: case TokenType::OpSub: case TokenType::OpMul: case TokenType::OpDiv: case TokenType::OpMod: case TokenType::OpNot: case TokenType::OpBitNot: case TokenType::OpLsh: case TokenType::OpRsh: case TokenType::OpEql: case TokenType::OpNeq: case TokenType::OpGreater: case TokenType::OpLess: case TokenType::OpGeq: case TokenType::OpLeq: case TokenType::OpAnd: case TokenType::OpOr: case TokenType::OpBitXor: case TokenType::OpBitAnd: case TokenType::OpBitOr: case TokenType::OpInc: case TokenType::OpDec: case TokenType::OpAddAssign: case TokenType::OpSubAssign: case TokenType::OpMulAssign: case TokenType::OpDivAssign: case TokenType::OpModAssign: case TokenType::OpShlAssign: case TokenType::OpShrAssign: case TokenType::OpOrAssign: case TokenType::OpAndAssign: case TokenType::OpXorAssign: // Note(tfoley): A bit of a hack: case TokenType::Comma: case TokenType::OpAssign: break; // Note(tfoley): Even more of a hack! case TokenType::QuestionMark: if (AdvanceIf(parser, TokenType::Colon)) { nameToken.Content = nameToken.Content + ":"; break; } default: parser->sink->diagnose(nameToken.loc, Diagnostics::invalidOperator, nameToken); break; } return NameLoc( getName(parser, nameToken.Content), nameToken.loc); } else { nameToken = parser->ReadToken(TokenType::Identifier); return NameLoc(nameToken); } } // A "declarator" as used in C-style languages struct Declarator : RefObject { // Different cases of declarator appear as "flavors" here enum class Flavor { name, Pointer, Array, }; Flavor flavor; }; // The most common case of declarator uses a simple name struct NameDeclarator : Declarator { NameLoc nameAndLoc; }; // A declarator that declares a pointer type struct PointerDeclarator : Declarator { // location of the `*` token SourceLoc starLoc; RefPtr inner; }; // A declarator that declares an array type struct ArrayDeclarator : Declarator { RefPtr inner; // location of the `[` token SourceLoc openBracketLoc; // The expression that yields the element count, or NULL RefPtr elementCountExpr; }; // "Unwrapped" information about a declarator struct DeclaratorInfo { RefPtr typeSpec; NameLoc nameAndLoc; RefPtr semantics; RefPtr initializer; }; // Add a member declaration to its container, and ensure that its // parent link is set up correctly. static void AddMember(RefPtr container, RefPtr member) { if (container) { member->ParentDecl = container.Ptr(); container->Members.Add(member); container->memberDictionaryIsValid = false; } } static void AddMember(RefPtr scope, RefPtr member) { if (scope) { AddMember(scope->containerDecl, member); } } static RefPtr ParseGenericParamDecl( Parser* parser, RefPtr genericDecl) { // simple syntax to introduce a value parameter if (AdvanceIf(parser, "let")) { // default case is a type parameter auto paramDecl = new GenericValueParamDecl(); paramDecl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); if (AdvanceIf(parser, TokenType::Colon)) { paramDecl->type = parser->ParseTypeExp(); } if (AdvanceIf(parser, TokenType::OpAssign)) { paramDecl->initExpr = parser->ParseInitExpr(); } return paramDecl; } else { // default case is a type parameter RefPtr paramDecl = new GenericTypeParamDecl(); parser->FillPosition(paramDecl); paramDecl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); if (AdvanceIf(parser, TokenType::Colon)) { // The user is apply a constraint to this type parameter... auto paramConstraint = new GenericTypeConstraintDecl(); parser->FillPosition(paramConstraint); auto paramType = DeclRefType::Create( parser->getSession(), DeclRef(paramDecl, nullptr)); auto paramTypeExpr = new SharedTypeExpr(); paramTypeExpr->loc = paramDecl->loc; paramTypeExpr->base.type = paramType; paramTypeExpr->type = QualType(getTypeType(paramType)); paramConstraint->sub = TypeExp(paramTypeExpr); paramConstraint->sup = parser->ParseTypeExp(); AddMember(genericDecl, paramConstraint); } if (AdvanceIf(parser, TokenType::OpAssign)) { paramDecl->initType = parser->ParseTypeExp(); } return paramDecl; } } template static void ParseGenericDeclImpl( Parser* parser, GenericDecl* decl, const TFunc & parseInnerFunc) { parser->ReadToken(TokenType::OpLess); parser->genericDepth++; while (!parser->LookAheadToken(TokenType::OpGreater)) { AddMember(decl, ParseGenericParamDecl(parser, decl)); if (parser->LookAheadToken(TokenType::OpGreater)) break; parser->ReadToken(TokenType::Comma); } parser->genericDepth--; parser->ReadToken(TokenType::OpGreater); decl->inner = parseInnerFunc(decl); decl->inner->ParentDecl = decl; // A generic decl hijacks the name of the declaration // it wraps, so that lookup can find it. if (decl->inner) { decl->nameAndLoc = decl->inner->nameAndLoc; decl->loc = decl->inner->loc; } } static RefPtr ParseGenericDecl(Parser* parser, void*) { RefPtr decl = new GenericDecl(); parser->FillPosition(decl.Ptr()); parser->PushScope(decl.Ptr()); ParseGenericDeclImpl(parser, decl.Ptr(), [=](GenericDecl* genDecl) {return ParseSingleDecl(parser, genDecl); }); parser->PopScope(); return decl; } static void parseParameterList( Parser* parser, RefPtr decl) { parser->ReadToken(TokenType::LParent); // Allow a declaration to use the keyword `void` for a parameter list, // since that was required in ancient C, and continues to be supported // in a bunc hof its derivatives even if it is a Bad Design Choice // // TODO: conditionalize this so we don't keep this around for "pure" // Slang code if( parser->LookAheadToken("void") && parser->LookAheadToken(TokenType::RParent, 1) ) { parser->ReadToken("void"); parser->ReadToken(TokenType::RParent); return; } while (!AdvanceIfMatch(parser, TokenType::RParent)) { AddMember(decl, parser->ParseParameter()); if (AdvanceIf(parser, TokenType::RParent)) break; parser->ReadToken(TokenType::Comma); } } // systematically replace all scopes in an expression tree class ReplaceScopeVisitor : public ExprVisitor { public: RefPtr scope; void visitDeclRefExpr(DeclRefExpr* expr) { expr->scope = scope; } void visitGenericAppExpr(GenericAppExpr * expr) { expr->FunctionExpr->accept(this, nullptr); for (auto arg : expr->Arguments) arg->accept(this, nullptr); } void visitIndexExpr(IndexExpr * expr) { expr->BaseExpression->accept(this, nullptr); expr->IndexExpression->accept(this, nullptr); } void visitMemberExpr(MemberExpr * expr) { expr->BaseExpression->accept(this, nullptr); expr->scope = scope; } void visitStaticMemberExpr(StaticMemberExpr * expr) { expr->BaseExpression->accept(this, nullptr); expr->scope = scope; } void visitExpr(Expr* /*expr*/) {} }; static RefPtr ParseFuncDeclHeader( Parser* parser, DeclaratorInfo const& declaratorInfo, RefPtr decl, RefPtr genDecl) { RefPtr retDecl = decl; parser->FillPosition(decl.Ptr()); decl->loc = declaratorInfo.nameAndLoc.loc; decl->nameAndLoc = declaratorInfo.nameAndLoc; // if return type is a DeclRef type, we need to update its scope to use this function decl's scope // so that LookUp can find the generic type parameters declared after the function name ReplaceScopeVisitor replaceScopeVisitor; replaceScopeVisitor.scope = parser->currentScope; declaratorInfo.typeSpec->accept(&replaceScopeVisitor, nullptr); decl->ReturnType = TypeExp(declaratorInfo.typeSpec); auto parseFuncDeclHeaderInner = [&](GenericDecl *) { parseParameterList(parser, decl); ParseOptSemantics(parser, decl.Ptr()); return decl; }; if (parser->LookAheadToken(TokenType::OpLess)) { // parse generic parameters ParseGenericDeclImpl(parser, genDecl.Ptr(), parseFuncDeclHeaderInner); retDecl = genDecl; } else parseFuncDeclHeaderInner(nullptr); return retDecl; } static RefPtr ParseFuncDecl( Parser* parser, ContainerDecl* /*containerDecl*/, DeclaratorInfo const& declaratorInfo, bool isGeneric) { RefPtr decl = new FuncDecl(); RefPtr retDecl = decl; RefPtr genDecl; if (isGeneric) { genDecl = new GenericDecl(); parser->FillPosition(genDecl); parser->PushScope(genDecl); retDecl = genDecl; } parser->PushScope(decl.Ptr()); ParseFuncDeclHeader(parser, declaratorInfo, decl, genDecl); if (AdvanceIf(parser, TokenType::Semicolon)) { // empty body } else { decl->Body = parser->parseBlockStatement(); } parser->PopScope(); if (isGeneric) { parser->PopScope(); } return retDecl; } static RefPtr CreateVarDeclForContext( ContainerDecl* containerDecl ) { if (dynamic_cast(containerDecl) || dynamic_cast(containerDecl)) { return new StructField(); } else if (dynamic_cast(containerDecl)) { return new ParamDecl(); } else { return new Variable(); } } // Add modifiers to the end of the modifier list for a declaration void AddModifiers(Decl* decl, RefPtr modifiers) { if (!modifiers) return; RefPtr* link = &decl->modifiers.first; while (*link) { link = &(*link)->next; } *link = modifiers; } static Name* generateName(Parser* parser, String const& base) { // TODO: somehow mangle the name to avoid clashes return getName(parser, "SLANG_" + base); } static Name* generateName(Parser* parser) { return generateName(parser, "anonymous_" + String(parser->anonymousCounter++)); } // Set up a variable declaration based on what we saw in its declarator... static void CompleteVarDecl( Parser* parser, RefPtr decl, DeclaratorInfo const& declaratorInfo) { parser->FillPosition(decl.Ptr()); if( !declaratorInfo.nameAndLoc.name ) { // HACK(tfoley): we always give a name, even if the declarator didn't include one... :( decl->nameAndLoc = NameLoc(generateName(parser)); } else { decl->loc = declaratorInfo.nameAndLoc.loc; decl->nameAndLoc = declaratorInfo.nameAndLoc; } decl->type = TypeExp(declaratorInfo.typeSpec); AddModifiers(decl.Ptr(), declaratorInfo.semantics); decl->initExpr = declaratorInfo.initializer; } static RefPtr ParseDeclarator(Parser* parser); static RefPtr ParseDirectAbstractDeclarator( Parser* parser) { RefPtr declarator; switch( parser->tokenReader.PeekTokenType() ) { case TokenType::Identifier: { auto nameDeclarator = new NameDeclarator(); nameDeclarator->flavor = Declarator::Flavor::name; nameDeclarator->nameAndLoc = ParseDeclName(parser); declarator = nameDeclarator; } break; case TokenType::LParent: { // Note(tfoley): This is a point where disambiguation is required. // We could be looking at an abstract declarator for a function-type // parameter: // // void F( int(int) ); // // Or we could be looking at the use of parenthesese in an ordinary // declarator: // // void (*f)(int); // // The difference really doesn't matter right now, but we err in // the direction of assuming the second case. parser->ReadToken(TokenType::LParent); declarator = ParseDeclarator(parser); parser->ReadToken(TokenType::RParent); } break; default: // an empty declarator is allowed return nullptr; } // postifx additions for( ;;) { switch( parser->tokenReader.PeekTokenType() ) { case TokenType::LBracket: { auto arrayDeclarator = new ArrayDeclarator(); arrayDeclarator->openBracketLoc = parser->tokenReader.PeekLoc(); arrayDeclarator->flavor = Declarator::Flavor::Array; arrayDeclarator->inner = declarator; parser->ReadToken(TokenType::LBracket); if( parser->tokenReader.PeekTokenType() != TokenType::RBracket ) { arrayDeclarator->elementCountExpr = parser->ParseExpression(); } parser->ReadToken(TokenType::RBracket); declarator = arrayDeclarator; continue; } case TokenType::LParent: break; default: break; } break; } return declarator; } // Parse a declarator (or at least as much of one as we support) static RefPtr ParseDeclarator( Parser* parser) { if( parser->tokenReader.PeekTokenType() == TokenType::OpMul ) { auto ptrDeclarator = new PointerDeclarator(); ptrDeclarator->starLoc = parser->tokenReader.PeekLoc(); ptrDeclarator->flavor = Declarator::Flavor::Pointer; parser->ReadToken(TokenType::OpMul); // TODO(tfoley): allow qualifiers like `const` here? ptrDeclarator->inner = ParseDeclarator(parser); return ptrDeclarator; } else { return ParseDirectAbstractDeclarator(parser); } } // A declarator plus optional semantics and initializer struct InitDeclarator { RefPtr declarator; RefPtr semantics; RefPtr initializer; }; // Parse a declarator plus optional semantics static InitDeclarator ParseSemanticDeclarator( Parser* parser) { InitDeclarator result; result.declarator = ParseDeclarator(parser); result.semantics = ParseOptSemantics(parser); return result; } // Parse a declarator plus optional semantics and initializer static InitDeclarator ParseInitDeclarator( Parser* parser) { InitDeclarator result = ParseSemanticDeclarator(parser); if (AdvanceIf(parser, TokenType::OpAssign)) { result.initializer = parser->ParseInitExpr(); } return result; } static void UnwrapDeclarator( RefPtr declarator, DeclaratorInfo* ioInfo) { while( declarator ) { switch(declarator->flavor) { case Declarator::Flavor::name: { auto nameDeclarator = (NameDeclarator*) declarator.Ptr(); ioInfo->nameAndLoc = nameDeclarator->nameAndLoc; return; } break; case Declarator::Flavor::Pointer: { auto ptrDeclarator = (PointerDeclarator*) declarator.Ptr(); // TODO(tfoley): we don't support pointers for now // ioInfo->typeSpec = new PointerTypeExpr(ioInfo->typeSpec); declarator = ptrDeclarator->inner; } break; case Declarator::Flavor::Array: { // TODO(tfoley): we don't support pointers for now auto arrayDeclarator = (ArrayDeclarator*) declarator.Ptr(); auto arrayTypeExpr = new IndexExpr(); arrayTypeExpr->loc = arrayDeclarator->openBracketLoc; arrayTypeExpr->BaseExpression = ioInfo->typeSpec; arrayTypeExpr->IndexExpression = arrayDeclarator->elementCountExpr; ioInfo->typeSpec = arrayTypeExpr; declarator = arrayDeclarator->inner; } break; default: SLANG_UNREACHABLE("all cases handled"); break; } } } static void UnwrapDeclarator( InitDeclarator const& initDeclarator, DeclaratorInfo* ioInfo) { UnwrapDeclarator(initDeclarator.declarator, ioInfo); ioInfo->semantics = initDeclarator.semantics; ioInfo->initializer = initDeclarator.initializer; } // Either a single declaration, or a group of them struct DeclGroupBuilder { SourceLoc startPosition; RefPtr decl; RefPtr group; // Add a new declaration to the potential group void addDecl( RefPtr newDecl) { SLANG_ASSERT(newDecl); if( decl ) { group = new DeclGroup(); group->loc = startPosition; group->decls.Add(decl); decl = nullptr; } if( group ) { group->decls.Add(newDecl); } else { decl = newDecl; } } RefPtr getResult() { if(group) return group; return decl; } }; // Pares an argument to an application of a generic RefPtr ParseGenericArg(Parser* parser) { return parser->ParseArgExpr(); } // Create a type expression that will refer to the given declaration static RefPtr createDeclRefType(Parser* parser, RefPtr decl) { // For now we just construct an expression that // will look up the given declaration by name. // // TODO: do this better, e.g. by filling in the `declRef` field directly auto expr = new VarExpr(); expr->scope = parser->currentScope.Ptr(); expr->loc = decl->getNameLoc(); expr->name = decl->getName(); return expr; } // Representation for a parsed type specifier, which might // include a declaration (e.g., of a `struct` type) struct TypeSpec { // If the type-spec declared something, then put it here RefPtr decl; // Put the resulting expression (which should evaluate to a type) here RefPtr expr; }; static RefPtr parseGenericApp( Parser* parser, RefPtr base) { RefPtr genericApp = new GenericAppExpr(); parser->FillPosition(genericApp.Ptr()); // set up scope for lookup genericApp->FunctionExpr = base; parser->ReadToken(TokenType::OpLess); parser->genericDepth++; // For now assume all generics have at least one argument genericApp->Arguments.Add(ParseGenericArg(parser)); while (AdvanceIf(parser, TokenType::Comma)) { genericApp->Arguments.Add(ParseGenericArg(parser)); } parser->genericDepth--; // TODO: probably want to suppot `>>` and `>=` here as well, // and split up those tokens as needed. // parser->ReadToken(TokenType::OpGreater); return genericApp; } static bool isGenericName(Parser* parser, Name* name) { auto lookupResult = lookUp( parser->getSession(), nullptr, // no semantics visitor available yet name, parser->currentScope); if (!lookupResult.isValid() || lookupResult.isOverloaded()) return false; auto decl = lookupResult.item.declRef.getDecl(); if (auto genericDecl = dynamic_cast(decl)) { return true; } else { return false; } } static RefPtr tryParseGenericApp( Parser* parser, RefPtr base) { Name * baseName = nullptr; if (auto varExpr = base->As()) baseName = varExpr->name; // if base is a known generics, parse as generics if (baseName && isGenericName(parser, baseName)) return parseGenericApp(parser, base); // otherwise, we speculate as generics, and fallback to comparison when parsing failed TokenSpan tokenSpan; tokenSpan.mBegin = parser->tokenReader.mCursor; tokenSpan.mEnd = parser->tokenReader.mEnd; DiagnosticSink newSink; newSink.sourceManager = parser->sink->sourceManager; Parser newParser(*parser); newParser.sink = &newSink; auto speculateParseRs = parseGenericApp(&newParser, base); if (newSink.errorCount == 0) { // disambiguate based on FOLLOW set switch (peekTokenType(&newParser)) { case TokenType::Dot: case TokenType::LParent: case TokenType::RParent: case TokenType::RBracket: case TokenType::Colon: case TokenType::Comma: case TokenType::QuestionMark: case TokenType::Semicolon: case TokenType::OpEql: case TokenType::OpNeq: { return parseGenericApp(parser, base); } } } return base; } static RefPtr parseMemberType(Parser * parser, RefPtr base) { RefPtr memberExpr = new MemberExpr(); parser->ReadToken(TokenType::Dot); parser->FillPosition(memberExpr.Ptr()); memberExpr->BaseExpression = base; memberExpr->name = expectIdentifier(parser).name; return memberExpr; } // Parse option `[]` braces after a type expression, that indicate an array type static RefPtr parsePostfixTypeSuffix( Parser* parser, RefPtr inTypeExpr) { auto typeExpr = inTypeExpr; while (parser->LookAheadToken(TokenType::LBracket)) { RefPtr arrType = new IndexExpr(); arrType->loc = typeExpr->loc; arrType->BaseExpression = typeExpr; parser->ReadToken(TokenType::LBracket); if (!parser->LookAheadToken(TokenType::RBracket)) { arrType->IndexExpression = parser->ParseExpression(); } parser->ReadToken(TokenType::RBracket); typeExpr = arrType; } return typeExpr; } static TypeSpec parseTypeSpec(Parser* parser) { TypeSpec typeSpec; // 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(); typeSpec.decl = decl; typeSpec.expr = createDeclRefType(parser, decl); return typeSpec; } else if( parser->LookAheadToken("class") ) { auto decl = parser->ParseClass(); typeSpec.decl = decl; 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); auto basicType = new VarExpr(); basicType->scope = parser->currentScope.Ptr(); basicType->loc = typeName.loc; basicType->name = typeName.getNameOrNull(); RefPtr typeExpr = basicType; bool shouldLoop = true; while (shouldLoop) { switch (peekTokenType(parser)) { case TokenType::OpLess: typeExpr = parseGenericApp(parser, typeExpr); break; case TokenType::Dot: typeExpr = parseMemberType(parser, typeExpr); break; default: shouldLoop = false; } } // GLSL allows `[]` directly in a type specifier if (parser->translationUnit->sourceLanguage == SourceLanguage::GLSL) { typeExpr = parsePostfixTypeSuffix(parser, typeExpr); } typeSpec.expr = typeExpr; return typeSpec; } static RefPtr ParseDeclaratorDecl( Parser* parser, ContainerDecl* containerDecl) { SourceLoc startPosition = parser->tokenReader.PeekLoc(); auto typeSpec = parseTypeSpec(parser); // We may need to build up multiple declarations in a group, // but the common case will be when we have just a single // declaration DeclGroupBuilder declGroupBuilder; declGroupBuilder.startPosition = startPosition; // The type specifier may include a declaration. E.g., // it might declare a `struct` type. if(typeSpec.decl) declGroupBuilder.addDecl(typeSpec.decl); if( AdvanceIf(parser, TokenType::Semicolon) ) { // No actual variable is being declared here, but // that might not be an error. auto result = declGroupBuilder.getResult(); if( !result ) { parser->sink->diagnose(startPosition, Diagnostics::declarationDidntDeclareAnything); } 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); DeclaratorInfo declaratorInfo; declaratorInfo.typeSpec = typeSpec.expr; // Rather than parse function declarators properly for now, // we'll just do a quick disambiguation here. This won't // matter unless we actually decide to support function-type parameters, // using C syntax. // if ((parser->tokenReader.PeekTokenType() == TokenType::LParent || parser->tokenReader.PeekTokenType() == TokenType::OpLess) // Only parse as a function if we didn't already see mutually-exclusive // constructs when parsing the declarator. && !initDeclarator.initializer && !initDeclarator.semantics) { // Looks like a function, so parse it like one. UnwrapDeclarator(initDeclarator, &declaratorInfo); return ParseFuncDecl(parser, containerDecl, declaratorInfo, parser->tokenReader.PeekTokenType() == TokenType::OpLess); } // Otherwise we are looking at a variable declaration, which could be one in a sequence... if( AdvanceIf(parser, TokenType::Semicolon) ) { // easy case: we only had a single declaration! UnwrapDeclarator(initDeclarator, &declaratorInfo); RefPtr firstDecl = CreateVarDeclForContext(containerDecl); CompleteVarDecl(parser, firstDecl, declaratorInfo); declGroupBuilder.addDecl(firstDecl); return declGroupBuilder.getResult(); } // Otherwise we have multiple declarations in a sequence, and these // declarations need to somehow share both the type spec and modifiers. // // If there are any errors in the type specifier, we only want to hear // about it once, so we need to share structure rather than just // clone syntax. auto sharedTypeSpec = new SharedTypeExpr(); sharedTypeSpec->loc = typeSpec.expr->loc; sharedTypeSpec->base = TypeExp(typeSpec.expr); for(;;) { declaratorInfo.typeSpec = sharedTypeSpec; UnwrapDeclarator(initDeclarator, &declaratorInfo); RefPtr varDecl = CreateVarDeclForContext(containerDecl); CompleteVarDecl(parser, varDecl, declaratorInfo); declGroupBuilder.addDecl(varDecl); // end of the sequence? if(AdvanceIf(parser, TokenType::Semicolon)) return declGroupBuilder.getResult(); // ad-hoc recovery, to avoid infinite loops if( parser->isRecovering ) { parser->ReadToken(TokenType::Semicolon); return declGroupBuilder.getResult(); } // Let's default to assuming that a missing `,` // indicates the end of a declaration, // where a `;` would be expected, and not // a continuation of this declaration, where // a `,` would be expected (this is tailoring // the diagnostic message a bit). // // TODO: a more advanced heuristic here might // look at whether the next token is on the // same line, to predict whether `,` or `;` // would be more likely... if (!AdvanceIf(parser, TokenType::Comma)) { parser->ReadToken(TokenType::Semicolon); return declGroupBuilder.getResult(); } // expect another variable declaration... initDeclarator = ParseInitDeclarator(parser); } } /// Parse the "register name" part of a `register` or `packoffset` semantic. /// /// The syntax matched is: /// /// register-name-and-component-mask ::= register-name component-mask? /// register-name ::= identifier /// component-mask ::= '.' identifier /// static void parseHLSLRegisterNameAndOptionalComponentMask( Parser* parser, HLSLLayoutSemantic* semantic) { semantic->registerName = parser->ReadToken(TokenType::Identifier); if (AdvanceIf(parser, TokenType::Dot)) { semantic->componentMask = parser->ReadToken(TokenType::Identifier); } } /// Parse an HLSL `register` semantic. /// /// The syntax matched is: /// /// register-semantic ::= 'register' '(' register-name-and-component-mask register-space? ')' /// register-space ::= ',' identifier /// static void parseHLSLRegisterSemantic( Parser* parser, HLSLRegisterSemantic* semantic) { // Read the `register` keyword semantic->name = parser->ReadToken(TokenType::Identifier); // Expect a parenthized list of additional arguments parser->ReadToken(TokenType::LParent); // First argument is a required register name and optional component mask parseHLSLRegisterNameAndOptionalComponentMask(parser, semantic); // Second argument is an optional register space if(AdvanceIf(parser, TokenType::Comma)) { semantic->spaceName = parser->ReadToken(TokenType::Identifier); } parser->ReadToken(TokenType::RParent); } /// Parse an HLSL `packoffset` semantic. /// /// The syntax matched is: /// /// packoffset-semantic ::= 'packoffset' '(' register-name-and-component-mask ')' /// static void parseHLSLPackOffsetSemantic( Parser* parser, HLSLPackOffsetSemantic* semantic) { // Read the `packoffset` keyword semantic->name = parser->ReadToken(TokenType::Identifier); // Expect a parenthized list of additional arguments parser->ReadToken(TokenType::LParent); // First and only argument is a required register name and optional component mask parseHLSLRegisterNameAndOptionalComponentMask(parser, semantic); parser->ReadToken(TokenType::RParent); parser->sink->diagnose(semantic, Diagnostics::packOffsetNotSupported); } // // semantic ::= identifier ( '(' args ')' )? // static RefPtr ParseSemantic( Parser* parser) { if (parser->LookAheadToken("register")) { RefPtr semantic = new HLSLRegisterSemantic(); parser->FillPosition(semantic); parseHLSLRegisterSemantic(parser, semantic.Ptr()); return semantic; } else if (parser->LookAheadToken("packoffset")) { RefPtr semantic = new HLSLPackOffsetSemantic(); parser->FillPosition(semantic); parseHLSLPackOffsetSemantic(parser, semantic.Ptr()); return semantic; } else if (parser->LookAheadToken(TokenType::Identifier)) { RefPtr semantic = new HLSLSimpleSemantic(); parser->FillPosition(semantic); semantic->name = parser->ReadToken(TokenType::Identifier); return semantic; } else { // expect an identifier, just to produce an error message parser->ReadToken(TokenType::Identifier); return nullptr; } } // // opt-semantics ::= (':' semantic)* // static RefPtr ParseOptSemantics( Parser* parser) { if (!AdvanceIf(parser, TokenType::Colon)) return nullptr; RefPtr result; RefPtr* link = &result; SLANG_ASSERT(!*link); for (;;) { RefPtr semantic = ParseSemantic(parser); if (semantic) { *link = semantic; link = &semantic->next; } // If we see another `:`, then that means there // is yet another semantic to be processed. // Otherwise we assume we are at the end of the list. // // TODO: This could produce sub-optimal diagnostics // when the user *meant* to apply multiple semantics // to a single declaration: // // Foo foo : register(t0) register(s0); // ^ // missing ':' here | // // 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 // avoiding an infinite loop here. if (!AdvanceIf(parser, TokenType::Colon)) { return result; } } } static void ParseOptSemantics( Parser* parser, Decl* decl) { AddModifiers(decl, ParseOptSemantics(parser)); } static RefPtr ParseHLSLBufferDecl( Parser* parser, String bufferWrapperTypeName) { // An HLSL declaration of a constant buffer like this: // // cbuffer Foo : register(b0) { int a; float b; }; // // is treated as syntax sugar for a type declaration // and then a global variable declaration using that type: // // struct $anonymous { int a; float b; }; // ConstantBuffer<$anonymous> Foo; // // where `$anonymous` is a fresh name, and the variable // declaration is made to be "transparent" so that lookup // will see through it to the members inside. auto bufferWrapperTypeNamePos = parser->tokenReader.PeekLoc(); // We are going to represent each buffer as a pair of declarations. // The first is a type declaration that holds all the members, while // the second is a variable declaration that uses the buffer type. RefPtr bufferDataTypeDecl = new StructDecl(); RefPtr bufferVarDecl = new Variable(); // Both declarations will have a location that points to the name parser->FillPosition(bufferDataTypeDecl.Ptr()); parser->FillPosition(bufferVarDecl.Ptr()); auto reflectionNameToken = parser->ReadToken(TokenType::Identifier); // Attach the reflection name to the block so we can use it auto reflectionNameModifier = new ParameterGroupReflectionName(); reflectionNameModifier->nameAndLoc = NameLoc(reflectionNameToken); addModifier(bufferVarDecl, reflectionNameModifier); // Both the buffer variable and its type need to have names generated bufferVarDecl->nameAndLoc.name = generateName(parser, "parameterGroup_" + reflectionNameToken.Content); bufferDataTypeDecl->nameAndLoc.name = generateName(parser, "ParameterGroup_" + reflectionNameToken.Content); addModifier(bufferDataTypeDecl, new ImplicitParameterGroupElementTypeModifier()); addModifier(bufferVarDecl, new ImplicitParameterGroupVariableModifier()); // TODO(tfoley): We end up constructing unchecked syntax here that // is expected to type check into the right form, but it might be // cleaner to have a more explicit desugaring pass where we parse // these constructs directly into the AST and *then* desugar them. // Construct a type expression to reference the buffer data type auto bufferDataTypeExpr = new VarExpr(); bufferDataTypeExpr->loc = bufferDataTypeDecl->loc; bufferDataTypeExpr->name = bufferDataTypeDecl->nameAndLoc.name; bufferDataTypeExpr->scope = parser->currentScope.Ptr(); // Construct a type exrpession to reference the type constructor auto bufferWrapperTypeExpr = new VarExpr(); bufferWrapperTypeExpr->loc = bufferWrapperTypeNamePos; bufferWrapperTypeExpr->name = getName(parser, bufferWrapperTypeName); // Always need to look this up in the outer scope, // so that it won't collide with, e.g., a local variable called `ConstantBuffer` bufferWrapperTypeExpr->scope = parser->outerScope; // Construct a type expression that represents the type for the variable, // which is the wrapper type applied to the data type auto bufferVarTypeExpr = new GenericAppExpr(); bufferVarTypeExpr->loc = bufferVarDecl->loc; bufferVarTypeExpr->FunctionExpr = bufferWrapperTypeExpr; bufferVarTypeExpr->Arguments.Add(bufferDataTypeExpr); bufferVarDecl->type.exp = bufferVarTypeExpr; // Any semantics applied to the bufer declaration are taken as applying // to the variable instead. ParseOptSemantics(parser, bufferVarDecl.Ptr()); // The declarations in the body belong to the data type. parseAggTypeDeclBody(parser, bufferDataTypeDecl.Ptr()); // All HLSL buffer declarations are "transparent" in that their // members are implicitly made visible in the parent scope. // We achieve this by applying the transparent modifier to the variable. auto transparentModifier = new TransparentModifier(); transparentModifier->next = bufferVarDecl->modifiers.first; bufferVarDecl->modifiers.first = transparentModifier; // Because we are constructing two declarations, we have a thorny // issue that were are only supposed to return one. // For now we handle this by adding the type declaration to // the current scope manually, and then returning the variable // declaration. // // Note: this means that any modifiers that have already been parsed // will get attached to the variable declaration, not the type. // There might be cases where we need to shuffle things around. AddMember(parser->currentScope, bufferDataTypeDecl); return bufferVarDecl; } static RefPtr parseHLSLCBufferDecl( Parser* parser, void* /*userData*/) { return ParseHLSLBufferDecl(parser, "ConstantBuffer"); } static RefPtr parseHLSLTBufferDecl( Parser* parser, void* /*userData*/) { return ParseHLSLBufferDecl(parser, "TextureBuffer"); } static void removeModifier( Modifiers& modifiers, RefPtr modifier) { RefPtr* link = &modifiers.first; while (*link) { if (*link == modifier) { *link = (*link)->next; return; } link = &(*link)->next; } } static RefPtr parseGLSLBlockDecl( Parser* parser, Modifiers& modifiers) { // An GLSL block like this: // // uniform Foo { int a; float b; } foo; // // is treated as syntax sugar for a type declaration // and then a global variable declaration using that type: // // struct $anonymous { int a; float b; }; // Block<$anonymous> foo; // // where `$anonymous` is a fresh name. // // If a "local name" like `foo` is not given, then // we make the declaration "transparent" so that lookup // will see through it to the members inside. SourceLoc pos = parser->tokenReader.PeekLoc(); // The initial name before the `{` is only supposed // to be made visible to reflection auto reflectionNameToken = parser->ReadToken(TokenType::Identifier); // Look at the qualifiers present on the block to decide what kind // of block we are looking at. Also *remove* those qualifiers so // that they don't interfere with downstream work. String blockWrapperTypeName; if( auto uniformMod = modifiers.findModifier() ) { removeModifier(modifiers, uniformMod); blockWrapperTypeName = "ConstantBuffer"; } else if( auto inMod = modifiers.findModifier() ) { removeModifier(modifiers, inMod); blockWrapperTypeName = "__GLSLInputParameterGroup"; } else if( auto outMod = modifiers.findModifier() ) { removeModifier(modifiers, outMod); blockWrapperTypeName = "__GLSLOutputParameterGroup"; } else if( auto bufferMod = modifiers.findModifier() ) { removeModifier(modifiers, bufferMod); blockWrapperTypeName = "__GLSLShaderStorageBuffer"; } else { // Unknown case: just map to a constant buffer and hope for the best blockWrapperTypeName = "ConstantBuffer"; } // We are going to represent each buffer as a pair of declarations. // The first is a type declaration that holds all the members, while // the second is a variable declaration that uses the buffer type. RefPtr blockDataTypeDecl = new StructDecl(); RefPtr blockVarDecl = new Variable(); addModifier(blockDataTypeDecl, new ImplicitParameterGroupElementTypeModifier()); addModifier(blockVarDecl, new ImplicitParameterGroupVariableModifier()); // Attach the reflection name to the block so we can use it auto reflectionNameModifier = new ParameterGroupReflectionName(); reflectionNameModifier->nameAndLoc = NameLoc(reflectionNameToken); addModifier(blockVarDecl, reflectionNameModifier); // Both declarations will have a location that points to the name parser->FillPosition(blockDataTypeDecl.Ptr()); parser->FillPosition(blockVarDecl.Ptr()); // Generate a unique name for the data type blockDataTypeDecl->nameAndLoc.name = generateName(parser, "ParameterGroup_" + reflectionNameToken.Content); // TODO(tfoley): We end up constructing unchecked syntax here that // is expected to type check into the right form, but it might be // cleaner to have a more explicit desugaring pass where we parse // these constructs directly into the AST and *then* desugar them. // Construct a type expression to reference the buffer data type auto blockDataTypeExpr = new VarExpr(); blockDataTypeExpr->loc = blockDataTypeDecl->loc; blockDataTypeExpr->name = blockDataTypeDecl->getName(); blockDataTypeExpr->scope = parser->currentScope.Ptr(); // Construct a type exrpession to reference the type constructor auto blockWrapperTypeExpr = new VarExpr(); blockWrapperTypeExpr->loc = pos; blockWrapperTypeExpr->name = getName(parser, blockWrapperTypeName); // Always need to look this up in the outer scope, // so that it won't collide with, e.g., a local variable called `ConstantBuffer` blockWrapperTypeExpr->scope = parser->outerScope; // Construct a type expression that represents the type for the variable, // which is the wrapper type applied to the data type auto blockVarTypeExpr = new GenericAppExpr(); blockVarTypeExpr->loc = blockVarDecl->loc; blockVarTypeExpr->FunctionExpr = blockWrapperTypeExpr; blockVarTypeExpr->Arguments.Add(blockDataTypeExpr); blockVarDecl->type.exp = blockVarTypeExpr; // The declarations in the body belong to the data type. parseAggTypeDeclBody(parser, blockDataTypeDecl.Ptr()); if( parser->LookAheadToken(TokenType::Identifier) ) { // The user gave an explicit name to the block, // so we need to use that as our variable name blockVarDecl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); // TODO: in this case we make actually have a more complex // declarator, including `[]` brackets. } else { // synthesize a dummy name blockVarDecl->nameAndLoc.name = generateName(parser, "parameterGroup_" + reflectionNameToken.Content); // Otherwise we have a transparent declaration, similar // to an HLSL `cbuffer` auto transparentModifier = new TransparentModifier(); transparentModifier->loc = pos; addModifier(blockVarDecl, transparentModifier); } // Expect a trailing `;` parser->ReadToken(TokenType::Semicolon); // Because we are constructing two declarations, we have a thorny // issue that were are only supposed to return one. // For now we handle this by adding the type declaration to // the current scope manually, and then returning the variable // declaration. // // Note: this means that any modifiers that have already been parsed // will get attached to the variable declaration, not the type. // There might be cases where we need to shuffle things around. AddMember(parser->currentScope, blockDataTypeDecl); return blockVarDecl; } static void parseOptionalInheritanceClause(Parser* parser, AggTypeDeclBase* decl) { if (AdvanceIf(parser, TokenType::Colon)) { do { auto base = parser->ParseTypeExp(); auto inheritanceDecl = new InheritanceDecl(); inheritanceDecl->loc = base.exp->loc; inheritanceDecl->nameAndLoc.name = getName(parser, "$inheritance"); inheritanceDecl->base = base; AddMember(decl, inheritanceDecl); } while (AdvanceIf(parser, TokenType::Comma)); } } static RefPtr ParseExtensionDecl(Parser* parser, void* /*userData*/) { RefPtr decl = new ExtensionDecl(); parser->FillPosition(decl.Ptr()); decl->targetType = parser->ParseTypeExp(); parseOptionalInheritanceClause(parser, decl); parseAggTypeDeclBody(parser, decl.Ptr()); return decl; } void parseOptionalGenericConstraints(Parser * parser, ContainerDecl* decl) { if (AdvanceIf(parser, TokenType::Colon)) { do { RefPtr paramConstraint = new GenericTypeConstraintDecl(); parser->FillPosition(paramConstraint); // substitution needs to be filled during check RefPtr paramType = DeclRefType::Create( parser->getSession(), DeclRef(decl, nullptr)); RefPtr paramTypeExpr = new SharedTypeExpr(); paramTypeExpr->loc = decl->loc; paramTypeExpr->base.type = paramType; paramTypeExpr->type = QualType(getTypeType(paramType)); paramConstraint->sub = TypeExp(paramTypeExpr); paramConstraint->sup = parser->ParseTypeExp(); AddMember(decl, paramConstraint); } while (AdvanceIf(parser, TokenType::Comma)); } } RefPtr parseAssocType(Parser * parser, void *) { RefPtr assocTypeDecl = new AssocTypeDecl(); auto nameToken = parser->ReadToken(TokenType::Identifier); assocTypeDecl->nameAndLoc = NameLoc(nameToken); assocTypeDecl->loc = nameToken.loc; parseOptionalGenericConstraints(parser, assocTypeDecl); parser->ReadToken(TokenType::Semicolon); return assocTypeDecl; } RefPtr parseGlobalGenericParamDecl(Parser * parser, void *) { RefPtr genParamDecl = new GlobalGenericParamDecl(); auto nameToken = parser->ReadToken(TokenType::Identifier); genParamDecl->nameAndLoc = NameLoc(nameToken); genParamDecl->loc = nameToken.loc; parseOptionalGenericConstraints(parser, genParamDecl); parser->ReadToken(TokenType::Semicolon); return genParamDecl; } static RefPtr parseInterfaceDecl(Parser* parser, void* /*userData*/) { RefPtr decl = new InterfaceDecl(); parser->FillPosition(decl.Ptr()); decl->nameAndLoc = NameLoc(parser->ReadToken(TokenType::Identifier)); parseOptionalInheritanceClause(parser, decl.Ptr()); parseAggTypeDeclBody(parser, decl.Ptr()); return decl; } static RefPtr parseConstructorDecl(Parser* parser, void* /*userData*/) { RefPtr decl = new ConstructorDecl(); parser->FillPosition(decl.Ptr()); // TODO: we need to make sure that all initializers have // the same name, but that this name doesn't conflict // with any user-defined names. // Giving them a name (rather than leaving it null) // ensures that we can use name-based lookup to find // all of the initializers on a type (and has // the potential to unify initializer lookup with // ordinary member lookup). decl->nameAndLoc.name = getName(parser, "$init"); parseParameterList(parser, decl); if( AdvanceIf(parser, TokenType::Semicolon) ) { // empty body } else { decl->Body = parser->parseBlockStatement(); } return decl; } static RefPtr parseAccessorDecl(Parser* parser) { Modifiers modifiers = ParseModifiers(parser); RefPtr decl; if( AdvanceIf(parser, "get") ) { decl = new GetterDecl(); } else if( AdvanceIf(parser, "set") ) { decl = new SetterDecl(); } else if( AdvanceIf(parser, "ref") ) { decl = new RefAccessorDecl(); } else { Unexpected(parser); return nullptr; } AddModifiers(decl, modifiers.first); if( parser->tokenReader.PeekTokenType() == TokenType::LBrace ) { decl->Body = parser->parseBlockStatement(); } else { parser->ReadToken(TokenType::Semicolon); } return decl; } static RefPtr ParseSubscriptDecl(Parser* parser, void* /*userData*/) { RefPtr decl = new SubscriptDecl(); parser->FillPosition(decl.Ptr()); // TODO: the use of this name here is a bit magical... decl->nameAndLoc.name = getName(parser, "operator[]"); parseParameterList(parser, decl); if( AdvanceIf(parser, TokenType::RightArrow) ) { decl->ReturnType = parser->ParseTypeExp(); } if( AdvanceIf(parser, TokenType::LBrace) ) { // We want to parse nested "accessor" declarations while( !AdvanceIfMatch(parser, TokenType::RBrace) ) { auto accessor = parseAccessorDecl(parser); AddMember(decl, accessor); } } else { parser->ReadToken(TokenType::Semicolon); // empty body should be treated like `{ get; }` } return decl; } static Token expect(Parser* parser, TokenType tokenType) { return parser->ReadToken(tokenType); } // This is a catch-all syntax-construction callback to handle cases where // a piece of syntax is fully defined by the keyword to use, along with // the class of AST node to construct. static RefPtr parseSimpleSyntax(Parser* /*parser*/, void* userData) { SyntaxClassBase syntaxClass((SyntaxClassBase::ClassInfo*) userData); return (RefObject*) syntaxClass.createInstanceImpl(); } // Parse a declaration of a keyword that can be used to define further syntax. static RefPtr parseSyntaxDecl(Parser* parser, void* /*userData*/) { // Right now the basic form is: // // syntax [: ] [= ]; // // - `name` gives the name of the keyword to define. // - `syntaxClass` is the name of an AST node class that we expect // this syntax to construct when parsed. // - `existingKeyword` is the name of an existing keyword that // the new syntax should be an alias for. // First we parse the keyword name. auto nameAndLoc = expectIdentifier(parser); // Next we look for a clause that specified the AST node class. SyntaxClass syntaxClass; if (AdvanceIf(parser, TokenType::Colon)) { // User is specifying the class that should be construted auto classNameAndLoc = expectIdentifier(parser); syntaxClass = parser->getSession()->findSyntaxClass(classNameAndLoc.name); } // If the user specified a syntax class, then we will default // to the `parseSimpleSyntax` callback that will just construct // an instance of that type to represent the keyword in the AST. SyntaxParseCallback parseCallback = &parseSimpleSyntax; void* parseUserData = (void*) syntaxClass.classInfo; // Next we look for an initializer that will make this keyword // an alias for some existing keyword. if (AdvanceIf(parser, TokenType::OpAssign)) { auto existingKeywordNameAndLoc = expectIdentifier(parser); auto existingSyntax = tryLookUpSyntaxDecl(parser, existingKeywordNameAndLoc.name); if (!existingSyntax) { // TODO: diagnose: keyword did not name syntax } else { // The user is expecting us to parse our new syntax like // the existing syntax given, so we need to override // the callback. parseCallback = existingSyntax->parseCallback; parseUserData = existingSyntax->parseUserData; // If we don't already have a syntax class specified, then // we will crib the one from the existing syntax, to ensure // that we are creating a drop-in alias. if (!syntaxClass.classInfo) syntaxClass = existingSyntax->syntaxClass; } } // It is an error if the user didn't give us either an existing keyword // to use to the define the callback, or a valid AST node class to construct. // // TODO: down the line this should be expanded so that the user can reference // an existing *function* to use to parse the chosen syntax. if (!syntaxClass.classInfo) { // TODO: diagnose: either a type or an existing keyword needs to be specified } expect(parser, TokenType::Semicolon); // TODO: skip creating the declaration if anything failed, just to not screw things // up for downstream code? RefPtr syntaxDecl = new SyntaxDecl(); syntaxDecl->nameAndLoc = nameAndLoc; syntaxDecl->loc = nameAndLoc.loc; syntaxDecl->syntaxClass = syntaxClass; syntaxDecl->parseCallback = parseCallback; syntaxDecl->parseUserData = parseUserData; return syntaxDecl; } // A parameter declaration in an attribute declaration. // // We are going to use `name: type` syntax just for simplicty, and let the type // be optional, because we don't actually need it in all cases. // static RefPtr parseAttributeParamDecl(Parser* parser) { auto nameAndLoc = expectIdentifier(parser); RefPtr paramDecl = new ParamDecl(); paramDecl->nameAndLoc = nameAndLoc; if(AdvanceIf(parser, TokenType::Colon)) { paramDecl->type = parser->ParseTypeExp(); } if(AdvanceIf(parser, TokenType::OpAssign)) { paramDecl->initExpr = parser->ParseInitExpr(); } return paramDecl; } // Parse declaration of a name to be used for resolving `[attribute(...)]` style modifiers. // // These are distinct from `syntax` declarations, because their names don't get added // to the current scope using their default name. // // Also, attribute-specific code doesn't get invokved during parsing. We always parse // using the default attribute-parsing logic and then all specialized behavior takes // place during semantic checking. // static RefPtr parseAttributeSyntaxDecl(Parser* parser, void* /*userData*/) { // Right now the basic form is: // // attribute_syntax : ; // // - `name` gives the name of the attribute to define. // - `syntaxClass` is the name of an AST node class that we expect // this attribute to create when checked. // - `existingKeyword` is the name of an existing keyword that // the new syntax should be an alias for. expect(parser, TokenType::LBracket); // First we parse the attribute name. auto nameAndLoc = expectIdentifier(parser); RefPtr attrDecl = new AttributeDecl(); if(AdvanceIf(parser, TokenType::LParent)) { while(!AdvanceIfMatch(parser, TokenType::RParent)) { auto param = parseAttributeParamDecl(parser); AddMember(attrDecl, param); if(AdvanceIfMatch(parser, TokenType::RParent)) break; expect(parser, TokenType::Comma); } } expect(parser, TokenType::RBracket); // TODO: we should allow parameters to be specified here, to cut down // on the amount of per-attribute-type logic that has to occur later. // Next we look for a clause that specified the AST node class. SyntaxClass syntaxClass; if (AdvanceIf(parser, TokenType::Colon)) { // User is specifying the class that should be construted auto classNameAndLoc = expectIdentifier(parser); syntaxClass = parser->getSession()->findSyntaxClass(classNameAndLoc.name); } else { // For now we don't support the alternative approach where // an existing piece of syntax is named to provide the parsing // support. // TODO: diagnose: a syntax class must be specified. } expect(parser, TokenType::Semicolon); // TODO: skip creating the declaration if anything failed, just to not screw things // up for downstream code? attrDecl->nameAndLoc = nameAndLoc; attrDecl->loc = nameAndLoc.loc; attrDecl->syntaxClass = syntaxClass; return attrDecl; } // Finish up work on a declaration that was parsed static void CompleteDecl( Parser* /*parser*/, RefPtr decl, ContainerDecl* containerDecl, Modifiers modifiers) { // Add any modifiers we parsed before the declaration to the list // of modifiers on the declaration itself. AddModifiers(decl.Ptr(), modifiers.first); // Make sure the decl is properly nested inside its lexical parent if (containerDecl) { AddMember(containerDecl, decl); } } static RefPtr ParseDeclWithModifiers( Parser* parser, ContainerDecl* containerDecl, Modifiers modifiers ) { RefPtr decl; auto loc = parser->tokenReader.PeekLoc(); switch (peekTokenType(parser)) { case TokenType::Identifier: { // A declaration that starts with an identifier might be: // // - A keyword-based declaration (e.g., `cbuffer ...`) // - The begining of a type in a declarator-based declaration (e.g., `int ...`) // - A GLSL block declaration (e.g., `uniform Foo { ... }`) // Let's deal with the GLSL block case first. This is something like: // // uniform Foo { ... }; // // The `uniform` keyword has already been parsed as a modifier, // so the identifier we are looking at is `Foo`. If the token // after that is `{`, we assume this is a block. // // Of course, we only want to allow this syntax when parsing GLSL... if (parser->translationUnit->sourceLanguage == SourceLanguage::GLSL) { if( parser->LookAheadToken(TokenType::LBrace, 1) ) { decl = parseGLSLBlockDecl(parser, modifiers); break; } } // Next we will check whether we can use the identifier token // as a declaration keyword and parse a declaration using // its associated callback: RefPtr parsedDecl; if (tryParseUsingSyntaxDecl(parser, &parsedDecl)) { decl = parsedDecl; break; } // Our final fallback case is to assume that the user is // probably writing a C-style declarator-based declaration. decl = ParseDeclaratorDecl(parser, containerDecl); break; } break; // It is valid in HLSL/GLSL to have an "empty" declaration // that consists of just a semicolon. In particular, this // gets used a lot in GLSL to attach custom semantics to // shader input or output. // case TokenType::Semicolon: { advanceToken(parser); decl = new EmptyDecl(); decl->loc = loc; } break; // If nothing else matched, we try to parse an "ordinary" declarator-based declaration default: decl = ParseDeclaratorDecl(parser, containerDecl); break; } if (decl) { if( auto dd = decl.As() ) { CompleteDecl(parser, dd, containerDecl, modifiers); } else if(auto declGroup = decl.As()) { // We are going to add the same modifiers to *all* of these declarations, // so we want to give later passes a way to detect which modifiers // were shared, vs. which ones are specific to a single declaration. auto sharedModifiers = new SharedModifiers(); sharedModifiers->next = modifiers.first; modifiers.first = sharedModifiers; for( auto subDecl : declGroup->decls ) { CompleteDecl(parser, subDecl, containerDecl, modifiers); } } } return decl; } static RefPtr ParseDecl( Parser* parser, ContainerDecl* containerDecl) { Modifiers modifiers = ParseModifiers(parser); return ParseDeclWithModifiers(parser, containerDecl, modifiers); } static RefPtr ParseSingleDecl( Parser* parser, ContainerDecl* containerDecl) { auto declBase = ParseDecl(parser, containerDecl); if(!declBase) return nullptr; if( auto decl = declBase.As() ) { return decl; } else if( auto declGroup = declBase.As() ) { if( declGroup->decls.Count() == 1 ) { return declGroup->decls[0]; } } parser->sink->diagnose(declBase->loc, Diagnostics::unimplemented, "didn't expect multiple declarations here"); return nullptr; } // Parse a body consisting of declarations static void ParseDeclBody( Parser* parser, ContainerDecl* containerDecl, TokenType closingToken) { while(!AdvanceIfMatch(parser, closingToken)) { ParseDecl(parser, containerDecl); } } // Parse the `{}`-delimeted body of an aggregate type declaration static void parseAggTypeDeclBody( Parser* parser, AggTypeDeclBase* decl) { // TODO: the scope used for the body might need to be // slightly specialized to deal with the complexity // of how `this` works. // // Alternatively, that complexity can be pushed down // to semantic analysis so that it doesn't clutter // things here. parser->PushScope(decl); parser->ReadToken(TokenType::LBrace); ParseDeclBody(parser, decl, TokenType::RBrace); parser->PopScope(); } void Parser::parseSourceFile(ModuleDecl* program) { if (outerScope) { currentScope = outerScope; } PushScope(program); program->loc = tokenReader.PeekLoc(); program->scope = currentScope; ParseDeclBody(this, program, TokenType::EndOfFile); PopScope(); SLANG_RELEASE_ASSERT(currentScope == outerScope); currentScope = nullptr; } RefPtr Parser::ParseProgram() { RefPtr program = new ModuleDecl(); parseSourceFile(program.Ptr()); return program; } RefPtr Parser::ParseStruct() { RefPtr rs = new StructDecl(); RefPtr retDecl = rs; FillPosition(rs.Ptr()); ReadToken("struct"); // TODO: support `struct` declaration without tag rs->nameAndLoc = expectIdentifier(this); auto parseStructInner = [&](GenericDecl*) { // We allow for an inheritance clause on a `struct` // so that it can conform to interfaces. parseOptionalInheritanceClause(this, rs.Ptr()); parseAggTypeDeclBody(this, rs.Ptr()); return rs; }; if (LookAheadToken(TokenType::OpLess)) { RefPtr genDecl = new GenericDecl(); FillPosition(genDecl.Ptr()); PushScope(genDecl); ParseGenericDeclImpl(this, genDecl.Ptr(), parseStructInner); PopScope(); retDecl = genDecl; } else { parseStructInner(nullptr); } return retDecl; } RefPtr Parser::ParseClass() { RefPtr rs = new ClassDecl(); FillPosition(rs.Ptr()); ReadToken("class"); rs->nameAndLoc = expectIdentifier(this); ReadToken(TokenType::LBrace); parseOptionalInheritanceClause(this, rs.Ptr()); parseAggTypeDeclBody(this, rs.Ptr()); return rs; } static RefPtr parseEnumCaseDecl(Parser* parser) { RefPtr decl = new EnumCaseDecl(); decl->nameAndLoc = expectIdentifier(parser); if(AdvanceIf(parser, TokenType::OpAssign)) { decl->initExpr = parser->ParseArgExpr(); } return decl; } static RefPtr parseEnumDecl(Parser* parser) { RefPtr 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 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 = new GenericDecl(); parser->FillPosition(genericDecl); parser->PushScope(genericDecl); ParseGenericDeclImpl(parser, genericDecl, parseEnumDeclInner); parser->PopScope(); return genericDecl; } else { parseEnumDeclInner(nullptr); } return decl; } static RefPtr ParseSwitchStmt(Parser* parser) { RefPtr stmt = new SwitchStmt(); parser->FillPosition(stmt.Ptr()); parser->ReadToken("switch"); parser->ReadToken(TokenType::LParent); stmt->condition = parser->ParseExpression(); parser->ReadToken(TokenType::RParent); stmt->body = parser->parseBlockStatement(); return stmt; } static RefPtr ParseCaseStmt(Parser* parser) { RefPtr stmt = new CaseStmt(); parser->FillPosition(stmt.Ptr()); parser->ReadToken("case"); stmt->expr = parser->ParseExpression(); parser->ReadToken(TokenType::Colon); return stmt; } static RefPtr ParseDefaultStmt(Parser* parser) { RefPtr stmt = new DefaultStmt(); parser->FillPosition(stmt.Ptr()); parser->ReadToken("default"); parser->ReadToken(TokenType::Colon); return stmt; } static bool isTypeName(Parser* parser, Name* name) { auto lookupResult = lookUp( parser->getSession(), nullptr, // no semantics visitor available yet name, parser->currentScope); if(!lookupResult.isValid() || lookupResult.isOverloaded()) return false; auto decl = lookupResult.item.declRef.getDecl(); if( auto typeDecl = dynamic_cast(decl) ) { return true; } else if( auto typeVarDecl = dynamic_cast(decl) ) { return true; } else { return false; } } static bool peekTypeName(Parser* parser) { if(!parser->LookAheadToken(TokenType::Identifier)) return false; auto name = parser->tokenReader.PeekToken().getName(); return isTypeName(parser, name); } RefPtr parseCompileTimeForStmt( Parser* parser) { RefPtr scopeDecl = new ScopeDecl(); RefPtr stmt = new CompileTimeForStmt(); stmt->scopeDecl = scopeDecl; parser->ReadToken("for"); parser->ReadToken(TokenType::LParent); NameLoc varNameAndLoc = expectIdentifier(parser); RefPtr varDecl = new Variable(); varDecl->nameAndLoc = varNameAndLoc; varDecl->loc = varNameAndLoc.loc; stmt->varDecl = varDecl; parser->ReadToken("in"); parser->ReadToken("Range"); parser->ReadToken(TokenType::LParent); RefPtr rangeBeginExpr; RefPtr rangeEndExpr = parser->ParseArgExpr(); if (AdvanceIf(parser, TokenType::Comma)) { rangeBeginExpr = rangeEndExpr; rangeEndExpr = parser->ParseArgExpr(); } stmt->rangeBeginExpr = rangeBeginExpr; stmt->rangeEndExpr = rangeEndExpr; parser->ReadToken(TokenType::RParent); parser->ReadToken(TokenType::RParent); parser->pushScopeAndSetParent(scopeDecl); AddMember(parser->currentScope, varDecl); stmt->body = parser->ParseStatement(); parser->PopScope(); return stmt; } RefPtr parseCompileTimeStmt( Parser* parser) { parser->ReadToken(TokenType::Dollar); if (parser->LookAheadToken("for")) { return parseCompileTimeForStmt(parser); } else { Unexpected(parser); return nullptr; } } RefPtr Parser::ParseStatement() { auto modifiers = ParseModifiers(this); RefPtr statement; if (LookAheadToken(TokenType::LBrace)) statement = parseBlockStatement(); else if (peekTypeName(this)) statement = parseVarDeclrStatement(modifiers); else if (LookAheadToken("if")) statement = parseIfStatement(); else if (LookAheadToken("for")) statement = ParseForStatement(); else if (LookAheadToken("while")) statement = ParseWhileStatement(); else if (LookAheadToken("do")) statement = ParseDoWhileStatement(); else if (LookAheadToken("break")) statement = ParseBreakStatement(); else if (LookAheadToken("continue")) statement = ParseContinueStatement(); else if (LookAheadToken("return")) statement = ParseReturnStatement(); else if (LookAheadToken("discard")) { statement = new DiscardStmt(); FillPosition(statement.Ptr()); ReadToken("discard"); ReadToken(TokenType::Semicolon); } else if (LookAheadToken("switch")) statement = ParseSwitchStmt(this); else if (LookAheadToken("case")) statement = ParseCaseStmt(this); else if (LookAheadToken("default")) statement = ParseDefaultStmt(this); else if (LookAheadToken(TokenType::Dollar)) { statement = parseCompileTimeStmt(this); } else if (LookAheadToken(TokenType::Identifier)) { // We might be looking at a local declaration, or an // expression statement, and we need to figure out which. // // We'll solve this with backtracking for now. Token* startPos = tokenReader.mCursor; // Try to parse a type (knowing that the type grammar is // a subset of the expression grammar, and so this should // always succeed). RefPtr type = ParseType(); // We don't actually care about the type, though, so // don't retain it type = nullptr; // If the next token after we parsed a type looks like // we are going to declare a variable, then lets guess // that this is a declaration. // // TODO(tfoley): this wouldn't be robust for more // general kinds of declarators (notably pointer declarators), // so we'll need to be careful about this. if (LookAheadToken(TokenType::Identifier)) { // Reset the cursor and try to parse a declaration now. // Note: the declaration will consume any modifiers // that had been in place on the statement. tokenReader.mCursor = startPos; statement = parseVarDeclrStatement(modifiers); return statement; } // Fallback: reset and parse an expression tokenReader.mCursor = startPos; statement = ParseExpressionStatement(); } else if (LookAheadToken(TokenType::Semicolon)) { statement = new EmptyStmt(); FillPosition(statement.Ptr()); ReadToken(TokenType::Semicolon); } else { // Default case should always fall back to parsing an expression, // and then let that detect any errors statement = ParseExpressionStatement(); } if (statement && !statement->As()) { // Install any modifiers onto the statement. // Note: this path is bypassed in the case of a // declaration statement, so we don't end up // doubling up the modifiers. statement->modifiers = modifiers; } return statement; } RefPtr Parser::parseBlockStatement() { RefPtr scopeDecl = new ScopeDecl(); RefPtr blockStatement = new BlockStmt(); blockStatement->scopeDecl = scopeDecl; pushScopeAndSetParent(scopeDecl.Ptr()); ReadToken(TokenType::LBrace); RefPtr body; if(!tokenReader.IsAtEnd()) { FillPosition(blockStatement.Ptr()); } while (!AdvanceIfMatch(this, TokenType::RBrace)) { auto stmt = ParseStatement(); if(stmt) { if (!body) { body = stmt; } else if (auto seqStmt = body.As()) { seqStmt->stmts.Add(stmt); } else { RefPtr newBody = new SeqStmt(); newBody->loc = blockStatement->loc; newBody->stmts.Add(body); newBody->stmts.Add(stmt); body = newBody; } } TryRecover(this); } PopScope(); if(!body) { body = new EmptyStmt(); body->loc = blockStatement->loc; } blockStatement->body = body; return blockStatement; } RefPtr Parser::parseVarDeclrStatement( Modifiers modifiers) { RefPtrvarDeclrStatement = new DeclStmt(); FillPosition(varDeclrStatement.Ptr()); auto decl = ParseDeclWithModifiers(this, currentScope->containerDecl, modifiers); varDeclrStatement->decl = decl; return varDeclrStatement; } RefPtr Parser::parseIfStatement() { RefPtr ifStatement = new IfStmt(); FillPosition(ifStatement.Ptr()); ReadToken("if"); ReadToken(TokenType::LParent); ifStatement->Predicate = ParseExpression(); ReadToken(TokenType::RParent); ifStatement->PositiveStatement = ParseStatement(); if (LookAheadToken("else")) { ReadToken("else"); ifStatement->NegativeStatement = ParseStatement(); } return ifStatement; } RefPtr Parser::ParseForStatement() { RefPtr scopeDecl = new ScopeDecl(); // HLSL implements the bad approach to scoping a `for` loop // variable, and we want to respect that, but *only* when // parsing HLSL code. // bool brokenScoping = translationUnit->sourceLanguage == SourceLanguage::HLSL; // We will create a distinct syntax node class for the unscoped // case, just so that we can correctly handle it in downstream // logic. // RefPtr stmt; if (brokenScoping) { stmt = new UnscopedForStmt(); } else { stmt = new ForStmt(); } stmt->scopeDecl = scopeDecl; if(!brokenScoping) pushScopeAndSetParent(scopeDecl.Ptr()); FillPosition(stmt.Ptr()); ReadToken("for"); ReadToken(TokenType::LParent); if (peekTypeName(this)) { stmt->InitialStatement = parseVarDeclrStatement(Modifiers()); } else { if (!LookAheadToken(TokenType::Semicolon)) { stmt->InitialStatement = ParseExpressionStatement(); } else { ReadToken(TokenType::Semicolon); } } if (!LookAheadToken(TokenType::Semicolon)) stmt->PredicateExpression = ParseExpression(); ReadToken(TokenType::Semicolon); if (!LookAheadToken(TokenType::RParent)) stmt->SideEffectExpression = ParseExpression(); ReadToken(TokenType::RParent); stmt->Statement = ParseStatement(); if (!brokenScoping) PopScope(); return stmt; } RefPtr Parser::ParseWhileStatement() { RefPtr whileStatement = new WhileStmt(); FillPosition(whileStatement.Ptr()); ReadToken("while"); ReadToken(TokenType::LParent); whileStatement->Predicate = ParseExpression(); ReadToken(TokenType::RParent); whileStatement->Statement = ParseStatement(); return whileStatement; } RefPtr Parser::ParseDoWhileStatement() { RefPtr doWhileStatement = new DoWhileStmt(); FillPosition(doWhileStatement.Ptr()); ReadToken("do"); doWhileStatement->Statement = ParseStatement(); ReadToken("while"); ReadToken(TokenType::LParent); doWhileStatement->Predicate = ParseExpression(); ReadToken(TokenType::RParent); ReadToken(TokenType::Semicolon); return doWhileStatement; } RefPtr Parser::ParseBreakStatement() { RefPtr breakStatement = new BreakStmt(); FillPosition(breakStatement.Ptr()); ReadToken("break"); ReadToken(TokenType::Semicolon); return breakStatement; } RefPtr Parser::ParseContinueStatement() { RefPtr continueStatement = new ContinueStmt(); FillPosition(continueStatement.Ptr()); ReadToken("continue"); ReadToken(TokenType::Semicolon); return continueStatement; } RefPtr Parser::ParseReturnStatement() { RefPtr returnStatement = new ReturnStmt(); FillPosition(returnStatement.Ptr()); ReadToken("return"); if (!LookAheadToken(TokenType::Semicolon)) returnStatement->Expression = ParseExpression(); ReadToken(TokenType::Semicolon); return returnStatement; } RefPtr Parser::ParseExpressionStatement() { RefPtr statement = new ExpressionStmt(); FillPosition(statement.Ptr()); statement->Expression = ParseExpression(); ReadToken(TokenType::Semicolon); return statement; } RefPtr Parser::ParseParameter() { RefPtr parameter = new ParamDecl(); parameter->modifiers = ParseModifiers(this); DeclaratorInfo declaratorInfo; declaratorInfo.typeSpec = ParseType(); InitDeclarator initDeclarator = ParseInitDeclarator(this); UnwrapDeclarator(initDeclarator, &declaratorInfo); // Assume it is a variable-like declarator CompleteVarDecl(this, parameter, declaratorInfo); return parameter; } RefPtr Parser::ParseType() { auto typeSpec = parseTypeSpec(this); if( typeSpec.decl ) { AddMember(currentScope, typeSpec.decl); } auto typeExpr = typeSpec.expr; typeExpr = parsePostfixTypeSuffix(this, typeExpr); return typeExpr; } TypeExp Parser::ParseTypeExp() { return TypeExp(ParseType()); } enum class Associativity { Left, Right }; Associativity GetAssociativityFromLevel(Precedence level) { if (level == Precedence::Assignment) return Associativity::Right; else return Associativity::Left; } Precedence GetOpLevel(Parser* parser, TokenType type) { switch(type) { case TokenType::QuestionMark: return Precedence::TernaryConditional; case TokenType::Comma: return Precedence::Comma; case TokenType::OpAssign: case TokenType::OpMulAssign: case TokenType::OpDivAssign: case TokenType::OpAddAssign: case TokenType::OpSubAssign: case TokenType::OpModAssign: case TokenType::OpShlAssign: case TokenType::OpShrAssign: case TokenType::OpOrAssign: case TokenType::OpAndAssign: case TokenType::OpXorAssign: return Precedence::Assignment; case TokenType::OpOr: return Precedence::LogicalOr; case TokenType::OpAnd: return Precedence::LogicalAnd; case TokenType::OpBitOr: return Precedence::BitOr; case TokenType::OpBitXor: return Precedence::BitXor; case TokenType::OpBitAnd: return Precedence::BitAnd; case TokenType::OpEql: case TokenType::OpNeq: return Precedence::EqualityComparison; case TokenType::OpGreater: case TokenType::OpGeq: // Don't allow these ops inside a generic argument if (parser->genericDepth > 0) return Precedence::Invalid; case TokenType::OpLeq: case TokenType::OpLess: return Precedence::RelationalComparison; case TokenType::OpRsh: // Don't allow this op inside a generic argument if (parser->genericDepth > 0) return Precedence::Invalid; case TokenType::OpLsh: return Precedence::BitShift; case TokenType::OpAdd: case TokenType::OpSub: return Precedence::Additive; case TokenType::OpMul: case TokenType::OpDiv: case TokenType::OpMod: return Precedence::Multiplicative; default: return Precedence::Invalid; } } static RefPtr parseOperator(Parser* parser) { Token opToken; switch(parser->tokenReader.PeekTokenType()) { case TokenType::QuestionMark: opToken = parser->ReadToken(); opToken.Content = "?:"; break; default: opToken = parser->ReadToken(); break; } auto opExpr = new VarExpr(); opExpr->name = getName(parser, opToken.Content); opExpr->scope = parser->currentScope; opExpr->loc = opToken.loc; return opExpr; } static RefPtr createInfixExpr( Parser* /*parser*/, RefPtr left, RefPtr op, RefPtr right) { RefPtr expr = new InfixExpr(); expr->loc = op->loc; expr->FunctionExpr = op; expr->Arguments.Add(left); expr->Arguments.Add(right); return expr; } static RefPtr parseInfixExprWithPrecedence( Parser* parser, RefPtr inExpr, Precedence prec) { auto expr = inExpr; for(;;) { auto opTokenType = parser->tokenReader.PeekTokenType(); auto opPrec = GetOpLevel(parser, opTokenType); if(opPrec < prec) break; auto op = parseOperator(parser); // Special case the `?:` operator since it is the // one non-binary case we need to deal with. if(opTokenType == TokenType::QuestionMark) { RefPtr select = new SelectExpr(); select->loc = op->loc; select->FunctionExpr = op; select->Arguments.Add(expr); select->Arguments.Add(parser->ParseExpression(opPrec)); parser->ReadToken(TokenType::Colon); select->Arguments.Add(parser->ParseExpression(opPrec)); expr = select; continue; } auto right = parser->ParseLeafExpression(); for(;;) { auto nextOpPrec = GetOpLevel(parser, parser->tokenReader.PeekTokenType()); if((GetAssociativityFromLevel(nextOpPrec) == Associativity::Right) ? (nextOpPrec < opPrec) : (nextOpPrec <= opPrec)) break; right = parseInfixExprWithPrecedence(parser, right, nextOpPrec); } if (opTokenType == TokenType::OpAssign) { RefPtr assignExpr = new AssignExpr(); assignExpr->loc = op->loc; assignExpr->left = expr; assignExpr->right = right; expr = assignExpr; } else { expr = createInfixExpr(parser, expr, op, right); } } return expr; } RefPtr Parser::ParseExpression(Precedence level) { auto expr = ParseLeafExpression(); return parseInfixExprWithPrecedence(this, expr, level); #if 0 if (level == Precedence::Prefix) return ParseLeafExpression(); if (level == Precedence::TernaryConditional) { // parse select clause auto condition = ParseExpression(Precedence(level + 1)); if (LookAheadToken(TokenType::QuestionMark)) { RefPtr select = new SelectExpr(); FillPosition(select.Ptr()); select->Arguments.Add(condition); select->FunctionExpr = parseOperator(this); select->Arguments.Add(ParseExpression(level)); ReadToken(TokenType::Colon); select->Arguments.Add(ParseExpression(level)); return select; } else return condition; } else { if (GetAssociativityFromLevel(level) == Associativity::Left) { auto left = ParseExpression(Precedence(level + 1)); while (GetOpLevel(this, tokenReader.PeekTokenType()) == level) { RefPtr tmp = new InfixExpr(); tmp->FunctionExpr = parseOperator(this); tmp->Arguments.Add(left); FillPosition(tmp.Ptr()); tmp->Arguments.Add(ParseExpression(Precedence(level + 1))); left = tmp; } return left; } else { auto left = ParseExpression(Precedence(level + 1)); if (GetOpLevel(this, tokenReader.PeekTokenType()) == level) { RefPtr tmp = new InfixExpr(); tmp->Arguments.Add(left); FillPosition(tmp.Ptr()); tmp->FunctionExpr = parseOperator(this); tmp->Arguments.Add(ParseExpression(level)); left = tmp; } return left; } } #endif } // We *might* be looking at an application of a generic to arguments, // but we need to disambiguate to make sure. static RefPtr maybeParseGenericApp( Parser* parser, // TODO: need to support more general expressions here RefPtr base) { if(peekTokenType(parser) != TokenType::OpLess) return base; return tryParseGenericApp(parser, base); } static RefPtr parsePrefixExpr(Parser* parser); // Parse OOP `this` expression syntax static RefPtr parseThisExpr(Parser* parser, void* /*userData*/) { RefPtr expr = new ThisExpr(); expr->scope = parser->currentScope; return expr; } static RefPtr parseBoolLitExpr(Parser* /*parser*/, bool value) { RefPtr expr = new BoolLiteralExpr(); expr->value = value; return expr; } static RefPtr parseTrueExpr(Parser* parser, void* /*userData*/) { return parseBoolLitExpr(parser, true); } static RefPtr parseFalseExpr(Parser* parser, void* /*userData*/) { return parseBoolLitExpr(parser, false); } static RefPtr parseAtomicExpr(Parser* parser) { switch( peekTokenType(parser) ) { default: // TODO: should this return an error expression instead of NULL? parser->sink->diagnose(parser->tokenReader.PeekLoc(), Diagnostics::syntaxError); return nullptr; // Either: // - parenthized expression `(exp)` // - cast `(type) exp` // // Proper disambiguation requires mixing up parsing // and semantic checking (which we should do eventually) // but for now we will follow some hueristics. case TokenType::LParent: { Token openParen = parser->ReadToken(TokenType::LParent); if (peekTypeName(parser) && parser->LookAheadToken(TokenType::RParent, 1)) { RefPtr tcexpr = new ExplicitCastExpr(); parser->FillPosition(tcexpr.Ptr()); tcexpr->FunctionExpr = parser->ParseType(); parser->ReadToken(TokenType::RParent); auto arg = parsePrefixExpr(parser); tcexpr->Arguments.Add(arg); return tcexpr; } else { RefPtr base = parser->ParseExpression(); parser->ReadToken(TokenType::RParent); RefPtr parenExpr = new ParenExpr(); parenExpr->loc = openParen.loc; parenExpr->base = base; return parenExpr; } } // An initializer list `{ expr, ... }` case TokenType::LBrace: { RefPtr initExpr = new InitializerListExpr(); parser->FillPosition(initExpr.Ptr()); // Initializer list parser->ReadToken(TokenType::LBrace); List> exprs; for(;;) { if(AdvanceIfMatch(parser, TokenType::RBrace)) break; auto expr = parser->ParseArgExpr(); if( expr ) { initExpr->args.Add(expr); } if(AdvanceIfMatch(parser, TokenType::RBrace)) break; parser->ReadToken(TokenType::Comma); } return initExpr; } case TokenType::IntegerLiteral: { RefPtr constExpr = new IntegerLiteralExpr(); parser->FillPosition(constExpr.Ptr()); auto token = parser->tokenReader.AdvanceToken(); constExpr->token = token; String suffix; IntegerLiteralValue value = getIntegerLiteralValue(token, &suffix); // Look at any suffix on the value char const* suffixCursor = suffix.begin(); RefPtr suffixType = nullptr; if( suffixCursor && *suffixCursor ) { int lCount = 0; int uCount = 0; int unknownCount = 0; while(*suffixCursor) { switch( *suffixCursor++ ) { case 'l': case 'L': lCount++; break; case 'u': case 'U': uCount++; break; default: unknownCount++; break; } } if(unknownCount) { parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix); suffixType = parser->getSession()->getErrorType(); } // `u` or `ul` suffix -> `uint` else if(uCount == 1 && (lCount <= 1)) { suffixType = parser->getSession()->getUIntType(); } // `l` suffix on integer -> `int` (== `long`) else if(lCount == 1 && !uCount) { suffixType = parser->getSession()->getIntType(); } // `ull` suffix -> `uint64_t` else if(uCount == 1 && lCount == 2) { suffixType = parser->getSession()->getUInt64Type(); } // `ll` suffix -> `int64_t` else if(uCount == 0 && lCount == 2) { suffixType = parser->getSession()->getInt64Type(); } // TODO: do we need suffixes for smaller integer types? else { parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix); suffixType = parser->getSession()->getErrorType(); } } constExpr->value = value; constExpr->type = QualType(suffixType); return constExpr; } case TokenType::FloatingPointLiteral: { RefPtr constExpr = new FloatingPointLiteralExpr(); parser->FillPosition(constExpr.Ptr()); auto token = parser->tokenReader.AdvanceToken(); constExpr->token = token; String suffix; FloatingPointLiteralValue value = getFloatingPointLiteralValue(token, &suffix); // Look at any suffix on the value char const* suffixCursor = suffix.begin(); RefPtr suffixType = nullptr; if( suffixCursor && *suffixCursor ) { int fCount = 0; int lCount = 0; int hCount = 0; int unknownCount = 0; while(*suffixCursor) { switch( *suffixCursor++ ) { case 'f': case 'F': fCount++; break; case 'l': case 'L': lCount++; break; case 'h': case 'H': hCount++; break; default: unknownCount++; break; } } if(unknownCount) { parser->sink->diagnose(token, Diagnostics::invalidFloatingPOintLiteralSuffix, suffix); suffixType = parser->getSession()->getErrorType(); } // `f` suffix -> `float` if(fCount == 1 && !lCount) { suffixType = parser->getSession()->getFloatType(); } // `l` or `lf` suffix on floating-point literal -> `double` else if(lCount == 1 && (fCount <= 1)) { suffixType = parser->getSession()->getDoubleType(); } // `h` or `hf` suffix on floating-point literal -> `half` else if(lCount == 1 && (fCount <= 1)) { suffixType = parser->getSession()->getHalfType(); } // TODO: are there other suffixes we need to handle? else { parser->sink->diagnose(token, Diagnostics::invalidFloatingPOintLiteralSuffix, suffix); suffixType = parser->getSession()->getErrorType(); } } constExpr->value = value; constExpr->type = QualType(suffixType); return constExpr; } case TokenType::StringLiteral: { RefPtr constExpr = new StringLiteralExpr(); auto token = parser->tokenReader.AdvanceToken(); constExpr->token = token; parser->FillPosition(constExpr.Ptr()); if (!parser->LookAheadToken(TokenType::StringLiteral)) { // Easy/common case: a single string constExpr->value = getStringLiteralTokenValue(token); } else { StringBuilder sb; sb << getStringLiteralTokenValue(token); while (parser->LookAheadToken(TokenType::StringLiteral)) { token = parser->tokenReader.AdvanceToken(); sb << getStringLiteralTokenValue(token); } constExpr->value = sb.ProduceString(); } return constExpr; } case TokenType::Identifier: { // We will perform name lookup here so that we can find syntax // keywords registered for use as expressions. Token nameToken = peekToken(parser); RefPtr parsedExpr; if (tryParseUsingSyntaxDecl(parser, &parsedExpr)) { if (!parsedExpr->loc.isValid()) { parsedExpr->loc = nameToken.loc; } return parsedExpr; } // Default behavior is just to create a name expression RefPtr varExpr = new VarExpr(); varExpr->scope = parser->currentScope.Ptr(); parser->FillPosition(varExpr.Ptr()); auto nameAndLoc = expectIdentifier(parser); varExpr->name = nameAndLoc.name; if(peekTokenType(parser) == TokenType::OpLess) { return maybeParseGenericApp(parser, varExpr); } return varExpr; } } } static RefPtr parsePostfixExpr(Parser* parser) { auto expr = parseAtomicExpr(parser); for(;;) { switch( peekTokenType(parser) ) { default: return expr; // Postfix increment/decrement case TokenType::OpInc: case TokenType::OpDec: { RefPtr postfixExpr = new PostfixExpr(); parser->FillPosition(postfixExpr.Ptr()); postfixExpr->FunctionExpr = parseOperator(parser); postfixExpr->Arguments.Add(expr); expr = postfixExpr; } break; // Subscript operation `a[i]` case TokenType::LBracket: { RefPtr indexExpr = new IndexExpr(); indexExpr->BaseExpression = expr; parser->FillPosition(indexExpr.Ptr()); parser->ReadToken(TokenType::LBracket); // TODO: eventually we may want to support multiple arguments inside the `[]` if (!parser->LookAheadToken(TokenType::RBracket)) { indexExpr->IndexExpression = parser->ParseExpression(); } parser->ReadToken(TokenType::RBracket); expr = indexExpr; } break; // Call oepration `f(x)` case TokenType::LParent: { RefPtr invokeExpr = new InvokeExpr(); invokeExpr->FunctionExpr = expr; parser->FillPosition(invokeExpr.Ptr()); parser->ReadToken(TokenType::LParent); while (!parser->tokenReader.IsAtEnd()) { if (!parser->LookAheadToken(TokenType::RParent)) invokeExpr->Arguments.Add(parser->ParseArgExpr()); else { break; } if (!parser->LookAheadToken(TokenType::Comma)) break; parser->ReadToken(TokenType::Comma); } parser->ReadToken(TokenType::RParent); expr = invokeExpr; } break; // Member access `x.m` case TokenType::Dot: { RefPtr memberExpr = new MemberExpr(); // TODO(tfoley): why would a member expression need this? memberExpr->scope = parser->currentScope.Ptr(); parser->FillPosition(memberExpr.Ptr()); memberExpr->BaseExpression = expr; parser->ReadToken(TokenType::Dot); memberExpr->name = expectIdentifier(parser).name; if (peekTokenType(parser) == TokenType::OpLess) expr = maybeParseGenericApp(parser, memberExpr); else expr = memberExpr; } break; } } } static RefPtr parsePrefixExpr(Parser* parser) { switch( peekTokenType(parser) ) { default: return parsePostfixExpr(parser); case TokenType::OpInc: case TokenType::OpDec: case TokenType::OpNot: case TokenType::OpBitNot: case TokenType::OpAdd: case TokenType::OpSub: { RefPtr prefixExpr = new PrefixExpr(); parser->FillPosition(prefixExpr.Ptr()); prefixExpr->FunctionExpr = parseOperator(parser); prefixExpr->Arguments.Add(parsePrefixExpr(parser)); return prefixExpr; } break; } } RefPtr Parser::ParseLeafExpression() { return parsePrefixExpr(this); } RefPtr parseTypeFromSourceFile(TranslationUnitRequest* translationUnit, TokenSpan const& tokens, DiagnosticSink* sink, RefPtr const& outerScope) { Parser parser(tokens, sink, outerScope); parser.translationUnit = translationUnit; parser.currentScope = outerScope; return parser.ParseType(); } // Parse a source file into an existing translation unit void parseSourceFile( TranslationUnitRequest* translationUnit, TokenSpan const& tokens, DiagnosticSink* sink, RefPtr const& outerScope) { Parser parser(tokens, sink, outerScope); parser.translationUnit = translationUnit; return parser.parseSourceFile(translationUnit->SyntaxNode.Ptr()); } static void addBuiltinSyntaxImpl( Session* session, Scope* scope, char const* nameText, SyntaxParseCallback callback, void* userData, SyntaxClass syntaxClass) { Name* name = session->getNamePool()->getName(nameText); RefPtr syntaxDecl = new SyntaxDecl(); syntaxDecl->nameAndLoc = NameLoc(name); syntaxDecl->syntaxClass = syntaxClass; syntaxDecl->parseCallback = callback; syntaxDecl->parseUserData = userData; AddMember(scope, syntaxDecl); } template static void addBuiltinSyntax( Session* session, Scope* scope, char const* name, SyntaxParseCallback callback, void* userData = nullptr) { addBuiltinSyntaxImpl(session, scope, name, callback, userData, getClass()); } template static void addSimpleModifierSyntax( Session* session, Scope* scope, char const* name) { auto syntaxClass = getClass(); addBuiltinSyntaxImpl(session, scope, name, &parseSimpleSyntax, (void*) syntaxClass.classInfo, getClass()); } static RefPtr parseIntrinsicOpModifier(Parser* parser, void* /*userData*/) { RefPtr modifier = new IntrinsicOpModifier(); // We allow a few difference forms here: // // First, we can specify the intrinsic op `enum` value directly: // // __intrinsic_op() // // Second, we can specify the operation by name: // // __intrinsic_op() // // Finally, we can leave off the specification, so that the // op name will be derived fromthe function name: // // __intrinsic_op // if (AdvanceIf(parser, TokenType::LParent)) { if (AdvanceIf(parser, TokenType::OpSub)) { modifier->op = IROp(-StringToInt(parser->ReadToken().Content)); } else if (parser->LookAheadToken(TokenType::IntegerLiteral)) { modifier->op = IROp(StringToInt(parser->ReadToken().Content)); } else { modifier->opToken = parser->ReadToken(TokenType::Identifier); modifier->op = findIROp(modifier->opToken.Content.Buffer()); if (modifier->op == kIROp_Invalid) { parser->sink->diagnose(modifier->opToken, Diagnostics::unimplemented, "unknown intrinsic op"); } } parser->ReadToken(TokenType::RParent); } return modifier; } static RefPtr parseTargetIntrinsicModifier(Parser* parser, void* /*userData*/) { auto modifier = new TargetIntrinsicModifier(); if (AdvanceIf(parser, TokenType::LParent)) { modifier->targetToken = parser->ReadToken(TokenType::Identifier); if( AdvanceIf(parser, TokenType::Comma) ) { if( parser->LookAheadToken(TokenType::StringLiteral) ) { modifier->definitionToken = parser->ReadToken(); } else { modifier->definitionToken = parser->ReadToken(TokenType::Identifier); } } parser->ReadToken(TokenType::RParent); } return modifier; } static RefPtr parseSpecializedForTargetModifier(Parser* parser, void* /*userData*/) { auto modifier = new SpecializedForTargetModifier(); if (AdvanceIf(parser, TokenType::LParent)) { modifier->targetToken = parser->ReadToken(TokenType::Identifier); parser->ReadToken(TokenType::RParent); } return modifier; } static RefPtr parseGLSLExtensionModifier(Parser* parser, void* /*userData*/) { auto modifier = new RequiredGLSLExtensionModifier(); parser->ReadToken(TokenType::LParent); modifier->extensionNameToken = parser->ReadToken(TokenType::Identifier); parser->ReadToken(TokenType::RParent); return modifier; } static RefPtr parseGLSLVersionModifier(Parser* parser, void* /*userData*/) { auto modifier = new RequiredGLSLVersionModifier(); parser->ReadToken(TokenType::LParent); modifier->versionNumberToken = parser->ReadToken(TokenType::IntegerLiteral); parser->ReadToken(TokenType::RParent); return modifier; } static RefPtr parseLayoutModifier(Parser* parser, void* /*userData*/) { ModifierListBuilder listBuilder; listBuilder.add(new GLSLLayoutModifierGroupBegin()); parser->ReadToken(TokenType::LParent); while (!AdvanceIfMatch(parser, TokenType::RParent)) { auto nameAndLoc = expectIdentifier(parser); const String& nameText = nameAndLoc.name->text; if (nameText == "binding" || nameText == "set") { GLSLBindingAttribute* attr = listBuilder.find(); if (!attr) { attr = new GLSLBindingAttribute(); listBuilder.add(attr); } parser->ReadToken(TokenType::OpAssign); Token valToken = parser->ReadToken(TokenType::IntegerLiteral); // Work out the value auto value = getIntegerLiteralValue(valToken); if (nameText == "binding") { attr->binding = int32_t(value); } else { attr->set = int32_t(value); } } else { if (nameText == "push_constant") { RefPtr modifier(new PushConstantAttribute); modifier->name = nameAndLoc.name; modifier->loc = nameAndLoc.loc; listBuilder.add(modifier); } else { RefPtr modifier; // TODO: better handling of this choice (e.g., lookup in scope) if(0) {} #define CASE(KEYWORD, CLASS) \ else if(nameText == #KEYWORD) modifier = new CLASS() CASE(constant_id, GLSLConstantIDLayoutModifier); CASE(location, GLSLLocationLayoutModifier); CASE(local_size_x, GLSLLocalSizeXLayoutModifier); CASE(local_size_y, GLSLLocalSizeYLayoutModifier); CASE(local_size_z, GLSLLocalSizeZLayoutModifier); #undef CASE else { modifier = new GLSLUnparsedLayoutModifier(); } modifier->name = nameAndLoc.name; modifier->loc = nameAndLoc.loc; if(AdvanceIf(parser, TokenType::OpAssign)) { modifier->valToken = parser->ReadToken(TokenType::IntegerLiteral); } listBuilder.add(modifier); } } if (AdvanceIf(parser, TokenType::RParent)) break; parser->ReadToken(TokenType::Comma); } listBuilder.add(new GLSLLayoutModifierGroupEnd()); return listBuilder.getFirst(); } static RefPtr parseBuiltinTypeModifier(Parser* parser, void* /*userData*/) { RefPtr modifier = new BuiltinTypeModifier(); parser->ReadToken(TokenType::LParent); modifier->tag = BaseType(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).Content)); parser->ReadToken(TokenType::RParent); return modifier; } static RefPtr parseMagicTypeModifier(Parser* parser, void* /*userData*/) { RefPtr modifier = new MagicTypeModifier(); parser->ReadToken(TokenType::LParent); modifier->name = parser->ReadToken(TokenType::Identifier).Content; if (AdvanceIf(parser, TokenType::Comma)) { modifier->tag = uint32_t(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).Content)); } parser->ReadToken(TokenType::RParent); return modifier; } static RefPtr parseIntrinsicTypeModifier(Parser* parser, void* /*userData*/) { RefPtr modifier = new IntrinsicTypeModifier(); parser->ReadToken(TokenType::LParent); modifier->irOp = uint32_t(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).Content)); while( AdvanceIf(parser, TokenType::Comma) ) { auto operand = uint32_t(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).Content)); modifier->irOperands.Add(operand); } parser->ReadToken(TokenType::RParent); return modifier; } static RefPtr parseImplicitConversionModifier(Parser* parser, void* /*userData*/) { RefPtr modifier = new ImplicitConversionModifier(); ConversionCost cost = kConversionCost_Default; if( AdvanceIf(parser, TokenType::LParent) ) { cost = ConversionCost(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).Content)); parser->ReadToken(TokenType::RParent); } modifier->cost = cost; return modifier; } static RefPtr parseAttributeTargetModifier(Parser* parser, void* /*userData*/) { expect(parser, TokenType::LParent); auto syntaxClassNameAndLoc = expectIdentifier(parser); expect(parser, TokenType::RParent); auto syntaxClass = parser->getSession()->findSyntaxClass(syntaxClassNameAndLoc.name); RefPtr modifier = new AttributeTargetModifier(); modifier->syntaxClass = syntaxClass; return modifier; } RefPtr populateBaseLanguageModule( Session* session, RefPtr scope) { RefPtr moduleDecl = new ModuleDecl(); scope->containerDecl = moduleDecl; // Add syntax for declaration keywords #define DECL(KEYWORD, CALLBACK) \ addBuiltinSyntax(session, scope, #KEYWORD, &CALLBACK) DECL(typedef, ParseTypeDef); DECL(associatedtype, parseAssocType); DECL(__generic_param, parseGlobalGenericParamDecl); DECL(type_param, parseGlobalGenericParamDecl); DECL(cbuffer, parseHLSLCBufferDecl); DECL(tbuffer, parseHLSLTBufferDecl); DECL(__generic, ParseGenericDecl); DECL(__extension, ParseExtensionDecl); DECL(extension, ParseExtensionDecl); DECL(__init, parseConstructorDecl); DECL(__subscript, ParseSubscriptDecl); DECL(interface, parseInterfaceDecl); DECL(syntax, parseSyntaxDecl); DECL(attribute_syntax,parseAttributeSyntaxDecl); DECL(__import, parseImportDecl); DECL(import, parseImportDecl); #undef DECL // Add syntax for "simple" modifier keywords. // These are the ones that just appear as a single // keyword (no further tokens expected/allowed), // and which can be represented just by creating // a new AST node of the corresponding type. #define MODIFIER(KEYWORD, CLASS) \ addSimpleModifierSyntax(session, scope, #KEYWORD) MODIFIER(in, InModifier); MODIFIER(input, InputModifier); MODIFIER(out, OutModifier); MODIFIER(inout, InOutModifier); MODIFIER(__ref, RefModifier); MODIFIER(const, ConstModifier); MODIFIER(instance, InstanceModifier); MODIFIER(__builtin, BuiltinModifier); MODIFIER(inline, InlineModifier); MODIFIER(public, PublicModifier); MODIFIER(require, RequireModifier); MODIFIER(param, ParamModifier); MODIFIER(extern, ExternModifier); MODIFIER(row_major, HLSLRowMajorLayoutModifier); MODIFIER(column_major, HLSLColumnMajorLayoutModifier); MODIFIER(nointerpolation, HLSLNoInterpolationModifier); MODIFIER(noperspective, HLSLNoPerspectiveModifier); MODIFIER(linear, HLSLLinearModifier); MODIFIER(sample, HLSLSampleModifier); MODIFIER(centroid, HLSLCentroidModifier); MODIFIER(precise, HLSLPreciseModifier); MODIFIER(shared, HLSLEffectSharedModifier); MODIFIER(groupshared, HLSLGroupSharedModifier); MODIFIER(static, HLSLStaticModifier); MODIFIER(uniform, HLSLUniformModifier); MODIFIER(volatile, HLSLVolatileModifier); // Modifiers for geometry shader input MODIFIER(point, HLSLPointModifier); MODIFIER(line, HLSLLineModifier); MODIFIER(triangle, HLSLTriangleModifier); MODIFIER(lineadj, HLSLLineAdjModifier); MODIFIER(triangleadj, HLSLTriangleAdjModifier); // Modifiers for unary operator declarations MODIFIER(__prefix, PrefixModifier); MODIFIER(__postfix, PostfixModifier); // Modifier to apply to `import` that should be re-exported MODIFIER(__exported, ExportedModifier); #undef MODIFIER // Add syntax for more complex modifiers, which allow // or expect more tokens after the initial keyword. #define MODIFIER(KEYWORD, CALLBACK) \ addBuiltinSyntax(session, scope, #KEYWORD, &CALLBACK) MODIFIER(layout, parseLayoutModifier); MODIFIER(__intrinsic_op, parseIntrinsicOpModifier); MODIFIER(__target_intrinsic, parseTargetIntrinsicModifier); MODIFIER(__specialized_for_target, parseSpecializedForTargetModifier); MODIFIER(__glsl_extension, parseGLSLExtensionModifier); MODIFIER(__glsl_version, parseGLSLVersionModifier); MODIFIER(__builtin_type, parseBuiltinTypeModifier); MODIFIER(__magic_type, parseMagicTypeModifier); MODIFIER(__intrinsic_type, parseIntrinsicTypeModifier); MODIFIER(__implicit_conversion, parseImplicitConversionModifier); MODIFIER(__attributeTarget, parseAttributeTargetModifier); #undef MODIFIER // Add syntax for expression keywords #define EXPR(KEYWORD, CALLBACK) \ addBuiltinSyntax(session, scope, #KEYWORD, &CALLBACK) EXPR(this, parseThisExpr); EXPR(true, parseTrueExpr); EXPR(false, parseFalseExpr); #undef EXPR return moduleDecl; } }