#include "Parser.h" #include #include "lookup.h" namespace Slang { 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: CompileOptions& options; int anonymousCounter = 0; RefPtr outerScope; RefPtr currentScope; TokenReader tokenReader; DiagnosticSink * sink; String fileName; int genericDepth = 0; // 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->Position = tokenReader.PeekLoc(); } void PushScope(ContainerDecl* containerDecl) { RefPtr newScope = new Scope(); newScope->containerDecl = containerDecl; newScope->parent = currentScope; currentScope = newScope; } void PopScope() { currentScope = currentScope->parent; } Parser( CompileOptions& options, TokenSpan const& _tokens, DiagnosticSink * sink, String _fileName, RefPtr const& outerScope) : options(options) , tokenReader(_tokens) , sink(sink) , fileName(_fileName) , outerScope(outerScope) {} 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(ProgramSyntaxNode* 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); // 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 a closing token at all... bool lookingForClose = false; for (int ii = 0; ii < recoverBeforeCount; ++ii) { if (IsClosingToken(recoverBefore[ii])) lookingForClose = true; } for (int ii = 0; ii < recoverAfterCount; ++ii) { if (IsClosingToken(recoverAfter[ii])) lookingForClose = true; } 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 (!lookingForClose) return false; break; // never skip a `}`, to avoid spurious errors case TokenType::RBrace: 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) { // Consume the `typedef` keyword parser->ReadToken("typedef"); // TODO(tfoley): parse an actual declarator auto type = parser->ParseTypeExp(); auto nameToken = parser->ReadToken(TokenType::Identifier); RefPtr typeDefDecl = new TypeDefDecl(); typeDefDecl->Name = 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; while(*modifierLink) modifierLink = &(*modifierLink)->next; *modifierLink = modifier; modifierLink = &modifier->next; } void addModifier( RefPtr syntax, RefPtr modifier) { auto modifierLink = &syntax->modifiers.first; AddModifier(&modifierLink, modifier); } // Parse HLSL-style `[name(arg, ...)]` style "attribute" modifiers static void ParseSquareBracketAttributes(Parser* parser, RefPtr** ioModifierLink) { parser->ReadToken(TokenType::LBracket); for(;;) { auto nameToken = parser->ReadToken(TokenType::Identifier); RefPtr modifier = new HLSLUncheckedAttribute(); modifier->nameToken = nameToken; 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); } } static Modifiers ParseModifiers(Parser* parser) { Modifiers modifiers; RefPtr* modifierLink = &modifiers.first; for (;;) { CodePosition loc = parser->tokenReader.PeekLoc(); if (0) {} #define CASE(KEYWORD, TYPE) \ else if(AdvanceIf(parser, #KEYWORD)) do { \ RefPtr modifier = new TYPE(); \ modifier->Position = loc; \ AddModifier(&modifierLink, modifier); \ } while(0) CASE(in, InModifier); CASE(input, InputModifier); CASE(out, OutModifier); CASE(inout, InOutModifier); CASE(const, ConstModifier); CASE(instance, InstanceModifier); CASE(__builtin, BuiltinModifier); CASE(inline, InlineModifier); CASE(public, PublicModifier); CASE(require, RequireModifier); CASE(param, ParamModifier); CASE(extern, ExternModifier); CASE(row_major, HLSLRowMajorLayoutModifier); CASE(column_major, HLSLColumnMajorLayoutModifier); CASE(nointerpolation, HLSLNoInterpolationModifier); CASE(linear, HLSLLinearModifier); CASE(sample, HLSLSampleModifier); CASE(centroid, HLSLCentroidModifier); CASE(precise, HLSLPreciseModifier); CASE(shared, HLSLEffectSharedModifier); CASE(groupshared, HLSLGroupSharedModifier); CASE(static, HLSLStaticModifier); CASE(uniform, HLSLUniformModifier); CASE(volatile, HLSLVolatileModifier); // Modifiers for geometry shader input CASE(point, HLSLPointModifier); CASE(line, HLSLLineModifier); CASE(triangle, HLSLTriangleModifier); CASE(lineadj, HLSLLineAdjModifier); CASE(triangleadj, HLSLTriangleAdjModifier); // Modifiers for unary operator declarations CASE(__prefix, PrefixModifier); CASE(__postfix, PostfixModifier); #undef CASE else if (AdvanceIf(parser, "__intrinsic_op")) { auto modifier = new IntrinsicOpModifier(); modifier->Position = loc; parser->ReadToken(TokenType::LParent); if (parser->LookAheadToken(TokenType::IntLiterial)) { modifier->op = (IntrinsicOp)StringToInt(parser->ReadToken().Content); } else { modifier->opToken = parser->ReadToken(TokenType::Identifier); modifier->op = findIntrinsicOp(modifier->opToken.Content.Buffer()); if (modifier->op == IntrinsicOp::Unknown) { parser->sink->diagnose(loc, Diagnostics::unimplemented, "unknown intrinsic op"); } } parser->ReadToken(TokenType::RParent); AddModifier(&modifierLink, modifier); } else if (AdvanceIf(parser, "__intrinsic")) { auto modifier = new TargetIntrinsicModifier(); modifier->Position = loc; if (AdvanceIf(parser, TokenType::LParent)) { modifier->targetToken = parser->ReadToken(TokenType::Identifier); if( AdvanceIf(parser, TokenType::Comma) ) { if( parser->LookAheadToken(TokenType::StringLiterial) ) { modifier->definitionToken = parser->ReadToken(); } else { modifier->definitionToken = parser->ReadToken(TokenType::Identifier); } } parser->ReadToken(TokenType::RParent); } AddModifier(&modifierLink, modifier); } else if (AdvanceIf(parser, "layout")) { parser->ReadToken(TokenType::LParent); while (!AdvanceIfMatch(parser, TokenType::RParent)) { auto nameToken = parser->ReadToken(TokenType::Identifier); RefPtr modifier; // TODO: better handling of this choise (e.g., lookup in scope) if(0) {} #define CASE(KEYWORD, CLASS) \ else if(nameToken.Content == #KEYWORD) modifier = new CLASS() CASE(constant_id, GLSLConstantIDLayoutModifier); CASE(binding, GLSLBindingLayoutModifier); CASE(set, GLSLSetLayoutModifier); CASE(location, GLSLLocationLayoutModifier); #undef CASE else { modifier = new GLSLUnparsedLayoutModifier(); } modifier->nameToken = nameToken; if(AdvanceIf(parser, TokenType::OpAssign)) { modifier->valToken = parser->ReadToken(TokenType::IntLiterial); } AddModifier(&modifierLink, modifier); if (AdvanceIf(parser, TokenType::RParent)) break; parser->ReadToken(TokenType::Comma); } } else if (parser->tokenReader.PeekTokenType() == TokenType::LBracket) { ParseSquareBracketAttributes(parser, &modifierLink); } else if (AdvanceIf(parser,"__builtin_type")) { RefPtr modifier = new BuiltinTypeModifier(); parser->ReadToken(TokenType::LParent); modifier->tag = BaseType(StringToInt(parser->ReadToken(TokenType::IntLiterial).Content)); parser->ReadToken(TokenType::RParent); AddModifier(&modifierLink, modifier); } else if (AdvanceIf(parser,"__magic_type")) { 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::IntLiterial).Content)); } parser->ReadToken(TokenType::RParent); AddModifier(&modifierLink, modifier); } else { // Fallback case if none of the above explicit cases matched. // If we are looking at an identifier, then it may map to a // modifier declaration visible in the current scope if( parser->LookAheadToken(TokenType::Identifier) ) { LookupResult lookupResult = LookUp( parser->tokenReader.PeekToken().Content, parser->currentScope); if( lookupResult.isValid() && !lookupResult.isOverloaded() ) { auto& item = lookupResult.item; auto decl = item.declRef.getDecl(); if( auto modifierDecl = dynamic_cast(decl) ) { // We found a declaration for some modifier syntax, // so lets create an instance of the type it names // here. auto syntax = createInstanceOfSyntaxClassByName(modifierDecl->classNameToken.Content); auto modifier = dynamic_cast(syntax); if( modifier ) { modifier->Position = parser->tokenReader.PeekLoc(); modifier->nameToken = parser->ReadToken(TokenType::Identifier); AddModifier(&modifierLink, modifier); continue; } else { parser->ReadToken(TokenType::Identifier); assert(!"unexpected"); } } } } // Done with modifier list return modifiers; } } } static RefPtr parseImportDecl( Parser* parser) { parser->ReadToken("__import"); auto decl = new ImportDecl(); decl->nameToken = parser->ReadToken(TokenType::Identifier); decl->scope = parser->currentScope; parser->ReadToken(TokenType::Semicolon); return decl; } static Token ParseDeclName( Parser* parser) { Token name; if (AdvanceIf(parser, "operator")) { name = parser->ReadToken(); switch (name.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)) { name.Content = name.Content + ":"; break; } default: parser->sink->diagnose(name.Position, Diagnostics::invalidOperator, name.Content); break; } } else { name = parser->ReadToken(TokenType::Identifier); } return name; } // 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 { Token nameToken; }; // A declarator that declares a pointer type struct PointerDeclarator : Declarator { // location of the `*` token CodePosition starLoc; RefPtr inner; }; // A declarator that declares an array type struct ArrayDeclarator : Declarator { RefPtr inner; // location of the `[` token CodePosition openBracketLoc; // The expression that yields the element count, or NULL RefPtr elementCountExpr; }; // "Unwrapped" information about a declarator struct DeclaratorInfo { RefPtr typeSpec; Token nameToken; 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 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); } } static void ParseFuncDeclHeader( Parser* parser, DeclaratorInfo const& declaratorInfo, RefPtr decl) { parser->PushScope(decl.Ptr()); parser->FillPosition(decl.Ptr()); decl->Position = declaratorInfo.nameToken.Position; decl->Name = declaratorInfo.nameToken; decl->ReturnType = TypeExp(declaratorInfo.typeSpec); parseParameterList(parser, decl); ParseOptSemantics(parser, decl.Ptr()); } static RefPtr ParseFuncDecl( Parser* parser, ContainerDecl* /*containerDecl*/, DeclaratorInfo const& declaratorInfo) { RefPtr decl = new FunctionSyntaxNode(); ParseFuncDeclHeader(parser, declaratorInfo, decl); if (AdvanceIf(parser, TokenType::Semicolon)) { // empty body } else { decl->Body = parser->ParseBlockStatement(); } parser->PopScope(); return decl; } static RefPtr CreateVarDeclForContext( ContainerDecl* containerDecl ) { if (dynamic_cast(containerDecl) || dynamic_cast(containerDecl)) { return new StructField(); } else if (dynamic_cast(containerDecl)) { return new ParameterSyntaxNode(); } 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 String GenerateName(Parser* /*parser*/, String const& base) { // TODO: somehow mangle the name to avoid clashes return base; } static String 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.nameToken.Type == TokenType::Unknown ) { // HACK(tfoley): we always give a name, even if the declarator didn't include one... :( decl->Name.Content = GenerateName(parser); } else { decl->Position = declaratorInfo.nameToken.Position; decl->Name = declaratorInfo.nameToken; } decl->Type = TypeExp(declaratorInfo.typeSpec); AddModifiers(decl.Ptr(), declaratorInfo.semantics); decl->Expr = 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->nameToken = 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->nameToken = nameDeclarator->nameToken; 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 IndexExpressionSyntaxNode(); arrayTypeExpr->Position = 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 { CodePosition startPosition; RefPtr decl; RefPtr group; // Add a new declaration to the potential group void addDecl( RefPtr newDecl) { assert(newDecl); if( decl ) { group = new DeclGroup(); group->Position = 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 VarExpressionSyntaxNode(); expr->scope = parser->currentScope.Ptr(); expr->Position = decl->getNameToken().Position; expr->Variable = 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 TypeSpec parseTypeSpec(Parser* parser) { TypeSpec typeSpec; // We may see a `struct` type 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;`) // 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; } Token typeName = parser->ReadToken(TokenType::Identifier); auto basicType = new VarExpressionSyntaxNode(); basicType->scope = parser->currentScope.Ptr(); basicType->Position = typeName.Position; basicType->Variable = typeName.Content; RefPtr typeExpr = basicType; if (parser->LookAheadToken(TokenType::OpLess)) { RefPtr gtype = new GenericAppExpr(); parser->FillPosition(gtype.Ptr()); // set up scope for lookup gtype->Position = typeName.Position; gtype->FunctionExpr = typeExpr; parser->ReadToken(TokenType::OpLess); parser->genericDepth++; // For now assume all generics have at least one argument gtype->Arguments.Add(ParseGenericArg(parser)); while (AdvanceIf(parser, TokenType::Comma)) { gtype->Arguments.Add(ParseGenericArg(parser)); } parser->genericDepth--; parser->ReadToken(TokenType::OpGreater); typeExpr = gtype; } typeSpec.expr = typeExpr; return typeSpec; } static RefPtr ParseDeclaratorDecl( Parser* parser, ContainerDecl* containerDecl) { CodePosition 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; } 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 // 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); } // 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->Position = typeSpec.expr->Position; 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); } } // // layout-semantic ::= (register | packoffset) '(' register-name component-mask? ')' // register-name ::= identifier // component-mask ::= '.' identifier // static void ParseHLSLLayoutSemantic( Parser* parser, HLSLLayoutSemantic* semantic) { semantic->name = parser->ReadToken(TokenType::Identifier); parser->ReadToken(TokenType::LParent); semantic->registerName = parser->ReadToken(TokenType::Identifier); if (AdvanceIf(parser, TokenType::Dot)) { semantic->componentMask = parser->ReadToken(TokenType::Identifier); } parser->ReadToken(TokenType::RParent); } // // semantic ::= identifier ( '(' args ')' )? // static RefPtr ParseSemantic( Parser* parser) { if (parser->LookAheadToken("register")) { RefPtr semantic = new HLSLRegisterSemantic(); ParseHLSLLayoutSemantic(parser, semantic.Ptr()); return semantic; } else if (parser->LookAheadToken("packoffset")) { RefPtr semantic = new HLSLPackOffsetSemantic(); ParseHLSLLayoutSemantic(parser, semantic.Ptr()); return semantic; } else { RefPtr semantic = new HLSLSimpleSemantic(); semantic->name = parser->ReadToken(TokenType::Identifier); return semantic; } } // // opt-semantics ::= (':' semantic)* // static RefPtr ParseOptSemantics( Parser* parser) { if (!AdvanceIf(parser, TokenType::Colon)) return nullptr; RefPtr result; RefPtr* link = &result; assert(!*link); for (;;) { RefPtr semantic = ParseSemantic(parser); if (semantic) { *link = semantic; link = &semantic->next; } switch (parser->tokenReader.PeekTokenType()) { case TokenType::LBrace: case TokenType::Semicolon: case TokenType::Comma: case TokenType::RParent: case TokenType::EndOfFile: return result; default: break; } parser->ReadToken(TokenType::Colon); } } static void ParseOptSemantics( Parser* parser, Decl* decl) { AddModifiers(decl, ParseOptSemantics(parser)); } static RefPtr ParseHLSLBufferDecl( Parser* parser) { // 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. // We first look at the declaration keywrod to determine // the type of buffer to declare: String bufferWrapperTypeName; CodePosition bufferWrapperTypeNamePos = parser->tokenReader.PeekLoc(); if (AdvanceIf(parser, "cbuffer")) { bufferWrapperTypeName = "ConstantBuffer"; } else if (AdvanceIf(parser, "tbuffer")) { bufferWrapperTypeName = "TextureBuffer"; } else { Unexpected(parser); } // 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 StructSyntaxNode(); 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 ParameterBlockReflectionName(); reflectionNameModifier->nameToken = reflectionNameToken; addModifier(bufferVarDecl, reflectionNameModifier); // Both the buffer variable and its type need to have names generated bufferVarDecl->Name.Content = GenerateName(parser, "SLANG_constantBuffer_" + reflectionNameToken.Content); bufferDataTypeDecl->Name.Content = GenerateName(parser, "SLANG_ConstantBuffer_" + reflectionNameToken.Content); addModifier(bufferDataTypeDecl, new ImplicitParameterBlockElementTypeModifier()); addModifier(bufferVarDecl, new ImplicitParameterBlockVariableModifier()); // 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 VarExpressionSyntaxNode(); bufferDataTypeExpr->Position = bufferDataTypeDecl->Position; bufferDataTypeExpr->Variable = bufferDataTypeDecl->Name.Content; bufferDataTypeExpr->scope = parser->currentScope.Ptr(); // Construct a type exrpession to reference the type constructor auto bufferWrapperTypeExpr = new VarExpressionSyntaxNode(); bufferWrapperTypeExpr->Position = bufferWrapperTypeNamePos; bufferWrapperTypeExpr->Variable = 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->Position = bufferVarDecl->Position; 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 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. CodePosition 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 = "__GLSLInputParameterBlock"; } else if( auto outMod = modifiers.findModifier() ) { removeModifier(modifiers, outMod); blockWrapperTypeName = "__GLSLOutputParameterBlock"; } 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 StructSyntaxNode(); RefPtr blockVarDecl = new Variable(); addModifier(blockDataTypeDecl, new ImplicitParameterBlockElementTypeModifier()); addModifier(blockVarDecl, new ImplicitParameterBlockVariableModifier()); // Attach the reflection name to the block so we can use it auto reflectionNameModifier = new ParameterBlockReflectionName(); reflectionNameModifier->nameToken = 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->Name.Content = GenerateName(parser, "SLANG_ParameterBlock_" + 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 VarExpressionSyntaxNode(); blockDataTypeExpr->Position = blockDataTypeDecl->Position; blockDataTypeExpr->Variable = blockDataTypeDecl->Name.Content; blockDataTypeExpr->scope = parser->currentScope.Ptr(); // Construct a type exrpession to reference the type constructor auto blockWrapperTypeExpr = new VarExpressionSyntaxNode(); blockWrapperTypeExpr->Position = pos; blockWrapperTypeExpr->Variable = 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->Position = blockVarDecl->Position; 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->Name = parser->ReadToken(TokenType::Identifier); // TODO: in this case we make actually have a more complex // declarator, including `[]` brackets. } else { // synthesize a dummy name blockVarDecl->Name.Content = GenerateName(parser, "SLANG_parameterBlock_" + reflectionNameToken.Content); // Otherwise we have a transparent declaration, similar // to an HLSL `cbuffer` auto transparentModifier = new TransparentModifier(); transparentModifier->Position = 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 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->Name = parser->ReadToken(TokenType::Identifier); if (AdvanceIf(parser, TokenType::Colon)) { paramDecl->Type = parser->ParseTypeExp(); } if (AdvanceIf(parser, TokenType::OpAssign)) { paramDecl->Expr = parser->ParseInitExpr(); } return paramDecl; } else { // default case is a type parameter auto paramDecl = new GenericTypeParamDecl(); parser->FillPosition(paramDecl); paramDecl->Name = 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(DeclRef(paramDecl, nullptr)); auto paramTypeExpr = new SharedTypeExpr(); paramTypeExpr->Position = paramDecl->Position; paramTypeExpr->base.type = paramType; paramTypeExpr->Type = new TypeType(paramType); paramConstraint->sub = TypeExp(paramTypeExpr); paramConstraint->sup = parser->ParseTypeExp(); AddMember(genericDecl, paramConstraint); } if (AdvanceIf(parser, TokenType::OpAssign)) { paramDecl->initType = parser->ParseTypeExp(); } return paramDecl; } } static RefPtr ParseGenericDecl( Parser* parser) { RefPtr decl = new GenericDecl(); parser->FillPosition(decl.Ptr()); parser->PushScope(decl.Ptr()); parser->ReadToken("__generic"); 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 = ParseSingleDecl(parser, decl.Ptr()); // A generic decl hijacks the name of the declaration // it wraps, so that lookup can find it. decl->Name = decl->inner->Name; parser->PopScope(); return decl; } static RefPtr ParseExtensionDecl(Parser* parser) { RefPtr decl = new ExtensionDecl(); parser->FillPosition(decl.Ptr()); parser->ReadToken("__extension"); decl->targetType = parser->ParseTypeExp(); parseAggTypeDeclBody(parser, decl.Ptr()); return decl; } static void parseOptionalInheritanceClause(Parser* parser, AggTypeDecl* decl) { if( AdvanceIf(parser, TokenType::Colon) ) { do { auto base = parser->ParseTypeExp(); auto inheritanceDecl = new InheritanceDecl(); inheritanceDecl->Position = base.exp->Position; inheritanceDecl->base = base; AddMember(decl, inheritanceDecl); } while( AdvanceIf(parser, TokenType::Comma) ); } } static RefPtr parseInterfaceDecl(Parser* parser) { RefPtr decl = new InterfaceDecl(); parser->FillPosition(decl.Ptr()); parser->ReadToken("interface"); decl->Name = parser->ReadToken(TokenType::Identifier); parseOptionalInheritanceClause(parser, decl.Ptr()); parseAggTypeDeclBody(parser, decl.Ptr()); return decl; } static RefPtr ParseConstructorDecl(Parser* parser) { RefPtr decl = new ConstructorDecl(); parser->FillPosition(decl.Ptr()); parser->ReadToken("__init"); parseParameterList(parser, decl); if( AdvanceIf(parser, TokenType::Semicolon) ) { // empty body } else { decl->Body = parser->ParseBlockStatement(); } return decl; } static RefPtr parseAccessorDecl(Parser* parser) { RefPtr decl; if( AdvanceIf(parser, "get") ) { decl = new GetterDecl(); } else if( AdvanceIf(parser, "set") ) { decl = new SetterDecl(); } else { Unexpected(parser); return nullptr; } if( parser->tokenReader.PeekTokenType() == TokenType::LBrace ) { decl->Body = parser->ParseBlockStatement(); } else { parser->ReadToken(TokenType::Semicolon); } return decl; } static RefPtr ParseSubscriptDecl(Parser* parser) { RefPtr decl = new SubscriptDecl(); parser->FillPosition(decl.Ptr()); parser->ReadToken("__subscript"); // TODO: the use of this name here is a bit magical... decl->Name.Content = "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; } // Parse a declaration of a new modifier keyword static RefPtr parseModifierDecl(Parser* parser) { RefPtr decl = new ModifierDecl(); // read the `__modifier` keyword parser->ReadToken(TokenType::Identifier); parser->ReadToken(TokenType::LParent); decl->classNameToken = parser->ReadToken(TokenType::Identifier); parser->ReadToken(TokenType::RParent); parser->FillPosition(decl.Ptr()); decl->Name = parser->ReadToken(TokenType::Identifier); parser->ReadToken(TokenType::Semicolon); return decl; } // 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(); // TODO: actual dispatch! if (parser->LookAheadToken("struct")) decl = ParseDeclaratorDecl(parser, containerDecl); else if (parser->LookAheadToken("class")) decl = ParseDeclaratorDecl(parser, containerDecl); else if (parser->LookAheadToken("typedef")) decl = ParseTypeDef(parser); else if (parser->LookAheadToken("cbuffer") || parser->LookAheadToken("tbuffer")) decl = ParseHLSLBufferDecl(parser); else if (parser->LookAheadToken("__generic")) decl = ParseGenericDecl(parser); else if (parser->LookAheadToken("__extension")) decl = ParseExtensionDecl(parser); else if (parser->LookAheadToken("__init")) decl = ParseConstructorDecl(parser); else if (parser->LookAheadToken("__subscript")) decl = ParseSubscriptDecl(parser); else if (parser->LookAheadToken("interface")) decl = parseInterfaceDecl(parser); else if(parser->LookAheadToken("__modifier")) decl = parseModifierDecl(parser); else if(parser->LookAheadToken("__import")) decl = parseImportDecl(parser); else if (AdvanceIf(parser, TokenType::Semicolon)) { decl = new EmptyDecl(); decl->Position = loc; } // GLSL requires that we be able to parse "block" declarations, // which look superficially similar to declarator declarations else if( parser->LookAheadToken(TokenType::Identifier) && parser->LookAheadToken(TokenType::LBrace, 1) ) { decl = parseGLSLBlockDecl(parser, modifiers); } else { // Default case: just parse a declarator-based declaration decl = ParseDeclaratorDecl(parser, containerDecl); } 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->Position, 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); TryRecover(parser); } } // 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(ProgramSyntaxNode* program) { if (outerScope) { currentScope = outerScope; } PushScope(program); program->Position = CodePosition(0, 0, 0, fileName); ParseDeclBody(this, program, TokenType::EndOfFile); PopScope(); assert(currentScope == outerScope); currentScope = nullptr; } RefPtr Parser::ParseProgram() { RefPtr program = new ProgramSyntaxNode(); parseSourceFile(program.Ptr()); return program; } RefPtr Parser::ParseStruct() { RefPtr rs = new StructSyntaxNode(); FillPosition(rs.Ptr()); ReadToken("struct"); // TODO: support `struct` declaration without tag rs->Name = ReadToken(TokenType::Identifier); // 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; } RefPtr Parser::ParseClass() { RefPtr rs = new ClassSyntaxNode(); FillPosition(rs.Ptr()); ReadToken("class"); rs->Name = ReadToken(TokenType::Identifier); ReadToken(TokenType::LBrace); parseOptionalInheritanceClause(this, rs.Ptr()); parseAggTypeDeclBody(this, rs.Ptr()); return rs; } 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 peekTypeName(Parser* parser) { if(!parser->LookAheadToken(TokenType::Identifier)) return false; auto name = parser->tokenReader.PeekToken().Content; auto lookupResult = LookUp(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; } } 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 DiscardStatementSyntaxNode(); 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::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 EmptyStatementSyntaxNode(); 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) { // 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() { if( options.flags & SLANG_COMPILE_FLAG_NO_CHECKING ) { // We have been asked to parse the input, but not attempt to understand it. // TODO: record start/end locations... List tokens; ReadToken(TokenType::LBrace); int depth = 1; for( ;;) { switch( tokenReader.PeekTokenType() ) { case TokenType::EndOfFile: goto done; case TokenType::RBrace: depth--; if(depth == 0) goto done; break; case TokenType::LBrace: depth++; break; default: break; } auto token = tokenReader.AdvanceToken(); tokens.Add(token); } done: ReadToken(TokenType::RBrace); RefPtr unparsedStmt = new UnparsedStmt(); unparsedStmt->tokens = tokens; return unparsedStmt; } RefPtr scopeDecl = new ScopeDecl(); RefPtr blockStatement = new BlockStatementSyntaxNode(); blockStatement->scopeDecl = scopeDecl; PushScope(scopeDecl.Ptr()); ReadToken(TokenType::LBrace); if(!tokenReader.IsAtEnd()) { FillPosition(blockStatement.Ptr()); } while (!AdvanceIfMatch(this, TokenType::RBrace)) { auto stmt = ParseStatement(); if(stmt) { blockStatement->Statements.Add(stmt); } TryRecover(this); } PopScope(); return blockStatement; } RefPtr Parser::ParseVarDeclrStatement( Modifiers modifiers) { RefPtrvarDeclrStatement = new VarDeclrStatementSyntaxNode(); FillPosition(varDeclrStatement.Ptr()); auto decl = ParseDeclWithModifiers(this, currentScope->containerDecl, modifiers); varDeclrStatement->decl = decl; return varDeclrStatement; } RefPtr Parser::ParseIfStatement() { RefPtr ifStatement = new IfStatementSyntaxNode(); 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(); RefPtr stmt = new ForStatementSyntaxNode(); stmt->scopeDecl = scopeDecl; // Note(tfoley): HLSL implements `for` with incorrect scoping. // We need an option to turn on this behavior in a kind of "legacy" mode // PushScope(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(); // PopScope(); return stmt; } RefPtr Parser::ParseWhileStatement() { RefPtr whileStatement = new WhileStatementSyntaxNode(); 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 DoWhileStatementSyntaxNode(); 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 BreakStatementSyntaxNode(); FillPosition(breakStatement.Ptr()); ReadToken("break"); ReadToken(TokenType::Semicolon); return breakStatement; } RefPtr Parser::ParseContinueStatement() { RefPtr continueStatement = new ContinueStatementSyntaxNode(); FillPosition(continueStatement.Ptr()); ReadToken("continue"); ReadToken(TokenType::Semicolon); return continueStatement; } RefPtr Parser::ParseReturnStatement() { RefPtr returnStatement = new ReturnStatementSyntaxNode(); FillPosition(returnStatement.Ptr()); ReadToken("return"); if (!LookAheadToken(TokenType::Semicolon)) returnStatement->Expression = ParseExpression(); ReadToken(TokenType::Semicolon); return returnStatement; } RefPtr Parser::ParseExpressionStatement() { RefPtr statement = new ExpressionStatementSyntaxNode(); FillPosition(statement.Ptr()); statement->Expression = ParseExpression(); ReadToken(TokenType::Semicolon); return statement; } RefPtr Parser::ParseParameter() { RefPtr parameter = new ParameterSyntaxNode(); 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; while (LookAheadToken(TokenType::LBracket)) { RefPtr arrType = new IndexExpressionSyntaxNode(); arrType->Position = typeExpr->Position; arrType->BaseExpression = typeExpr; ReadToken(TokenType::LBracket); if (!LookAheadToken(TokenType::RBracket)) { arrType->IndexExpression = ParseExpression(); } ReadToken(TokenType::RBracket); typeExpr = arrType; } 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::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; } } Operator GetOpFromToken(Token & token) { switch(token.Type) { case TokenType::Comma: return Operator::Sequence; case TokenType::OpAssign: return Operator::Assign; case TokenType::OpAddAssign: return Operator::AddAssign; case TokenType::OpSubAssign: return Operator::SubAssign; case TokenType::OpMulAssign: return Operator::MulAssign; case TokenType::OpDivAssign: return Operator::DivAssign; case TokenType::OpModAssign: return Operator::ModAssign; case TokenType::OpShlAssign: return Operator::LshAssign; case TokenType::OpShrAssign: return Operator::RshAssign; case TokenType::OpOrAssign: return Operator::OrAssign; case TokenType::OpAndAssign: return Operator::AddAssign; case TokenType::OpXorAssign: return Operator::XorAssign; case TokenType::OpOr: return Operator::Or; case TokenType::OpAnd: return Operator::And; case TokenType::OpBitOr: return Operator::BitOr; case TokenType::OpBitXor: return Operator::BitXor; case TokenType::OpBitAnd: return Operator::BitAnd; case TokenType::OpEql: return Operator::Eql; case TokenType::OpNeq: return Operator::Neq; case TokenType::OpGeq: return Operator::Geq; case TokenType::OpLeq: return Operator::Leq; case TokenType::OpGreater: return Operator::Greater; case TokenType::OpLess: return Operator::Less; case TokenType::OpLsh: return Operator::Lsh; case TokenType::OpRsh: return Operator::Rsh; case TokenType::OpAdd: return Operator::Add; case TokenType::OpSub: return Operator::Sub; case TokenType::OpMul: return Operator::Mul; case TokenType::OpDiv: return Operator::Div; case TokenType::OpMod: return Operator::Mod; case TokenType::OpInc: return Operator::PostInc; case TokenType::OpDec: return Operator::PostDec; case TokenType::OpNot: return Operator::Not; case TokenType::OpBitNot: return Operator::BitNot; default: throw "Illegal TokenType."; } } 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 VarExpressionSyntaxNode(); opExpr->Variable = opToken.Content; opExpr->scope = parser->currentScope; opExpr->Position = opToken.Position; return opExpr; } RefPtr Parser::ParseExpression(Precedence level) { 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 SelectExpressionSyntaxNode(); 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; } } } RefPtr Parser::ParseLeafExpression() { RefPtr rs; if (LookAheadToken(TokenType::OpInc) || LookAheadToken(TokenType::OpDec) || LookAheadToken(TokenType::OpNot) || LookAheadToken(TokenType::OpBitNot) || LookAheadToken(TokenType::OpSub)) { RefPtr unaryExpr = new PrefixExpr(); FillPosition(unaryExpr.Ptr()); unaryExpr->FunctionExpr = parseOperator(this); unaryExpr->Arguments.Add(ParseLeafExpression()); rs = unaryExpr; return rs; } if (LookAheadToken(TokenType::LParent)) { ReadToken(TokenType::LParent); RefPtr expr; if (peekTypeName(this) && LookAheadToken(TokenType::RParent, 1)) { RefPtr tcexpr = new TypeCastExpressionSyntaxNode(); FillPosition(tcexpr.Ptr()); tcexpr->TargetType = ParseTypeExp(); ReadToken(TokenType::RParent); tcexpr->Expression = ParseExpression(Precedence::Multiplicative); // Note(tfoley): need to double-check this expr = tcexpr; } else { expr = ParseExpression(); ReadToken(TokenType::RParent); } rs = expr; } else if( LookAheadToken(TokenType::LBrace) ) { RefPtr initExpr = new InitializerListExpr(); FillPosition(initExpr.Ptr()); // Initializer list ReadToken(TokenType::LBrace); List> exprs; for(;;) { if(AdvanceIfMatch(this, TokenType::RBrace)) break; auto expr = ParseArgExpr(); if( expr ) { initExpr->args.Add(expr); } if(AdvanceIfMatch(this, TokenType::RBrace)) break; ReadToken(TokenType::Comma); } rs = initExpr; } else if (LookAheadToken(TokenType::IntLiterial) || LookAheadToken(TokenType::DoubleLiterial)) { RefPtr constExpr = new ConstantExpressionSyntaxNode(); auto token = tokenReader.AdvanceToken(); FillPosition(constExpr.Ptr()); if (token.Type == TokenType::IntLiterial) { constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Int; constExpr->IntValue = StringToInt(token.Content); } else if (token.Type == TokenType::DoubleLiterial) { constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Float; constExpr->FloatValue = (FloatingPointLiteralValue) StringToDouble(token.Content); } rs = constExpr; } else if (LookAheadToken("true") || LookAheadToken("false")) { RefPtr constExpr = new ConstantExpressionSyntaxNode(); auto token = tokenReader.AdvanceToken(); FillPosition(constExpr.Ptr()); constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Bool; constExpr->IntValue = token.Content == "true" ? 1 : 0; rs = constExpr; } else if (LookAheadToken(TokenType::Identifier)) { RefPtr varExpr = new VarExpressionSyntaxNode(); varExpr->scope = currentScope.Ptr(); FillPosition(varExpr.Ptr()); auto token = ReadToken(TokenType::Identifier); varExpr->Variable = token.Content; rs = varExpr; } while (!tokenReader.IsAtEnd() && (LookAheadToken(TokenType::OpInc) || LookAheadToken(TokenType::OpDec) || LookAheadToken(TokenType::Dot) || LookAheadToken(TokenType::LBracket) || LookAheadToken(TokenType::LParent))) { if (LookAheadToken(TokenType::OpInc)) { RefPtr unaryExpr = new PostfixExpr(); FillPosition(unaryExpr.Ptr()); unaryExpr->FunctionExpr = parseOperator(this); unaryExpr->Arguments.Add(rs); rs = unaryExpr; } else if (LookAheadToken(TokenType::OpDec)) { RefPtr unaryExpr = new PostfixExpr(); FillPosition(unaryExpr.Ptr()); unaryExpr->FunctionExpr = parseOperator(this); unaryExpr->Arguments.Add(rs); rs = unaryExpr; } else if (LookAheadToken(TokenType::LBracket)) { RefPtr indexExpr = new IndexExpressionSyntaxNode(); indexExpr->BaseExpression = rs; FillPosition(indexExpr.Ptr()); ReadToken(TokenType::LBracket); indexExpr->IndexExpression = ParseExpression(); ReadToken(TokenType::RBracket); rs = indexExpr; } else if (LookAheadToken(TokenType::LParent)) { RefPtr invokeExpr = new InvokeExpressionSyntaxNode(); invokeExpr->FunctionExpr = rs; FillPosition(invokeExpr.Ptr()); ReadToken(TokenType::LParent); while (!tokenReader.IsAtEnd()) { if (!LookAheadToken(TokenType::RParent)) invokeExpr->Arguments.Add(ParseArgExpr()); else { break; } if (!LookAheadToken(TokenType::Comma)) break; ReadToken(TokenType::Comma); } ReadToken(TokenType::RParent); rs = invokeExpr; } else if (LookAheadToken(TokenType::Dot)) { RefPtr memberExpr = new MemberExpressionSyntaxNode(); memberExpr->scope = currentScope.Ptr(); FillPosition(memberExpr.Ptr()); memberExpr->BaseExpression = rs; ReadToken(TokenType::Dot); memberExpr->MemberName = ReadToken(TokenType::Identifier).Content; rs = memberExpr; } } if (!rs) { sink->diagnose(tokenReader.PeekLoc(), Diagnostics::syntaxError); } return rs; } // Parse a source file into an existing translation unit void parseSourceFile( ProgramSyntaxNode* translationUnitSyntax, CompileOptions& options, TokenSpan const& tokens, DiagnosticSink* sink, String const& fileName, RefPtr const&outerScope) { Parser parser(options, tokens, sink, fileName, outerScope); return parser.parseSourceFile(translationUnitSyntax); } }