diff options
Diffstat (limited to 'source/slang/slang-parser.cpp')
| -rw-r--r-- | source/slang/slang-parser.cpp | 134 |
1 files changed, 114 insertions, 20 deletions
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 179587550..f2bc2eb9a 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -773,6 +773,15 @@ namespace Slang return parser->tokenReader.peekTokenType(); } + /// Peek the token `offset` tokens after the cursor + static TokenType peekTokenType(Parser* parser, int offset) + { + TokenReader r = parser->tokenReader; + for (int ii = 0; ii < offset; ++ii) + r.advanceToken(); + return r.peekTokenType(); + } + static Token advanceToken(Parser* parser) { return parser->ReadToken(); @@ -2746,18 +2755,80 @@ namespace Slang return parser->ReadToken(tokenType).type == tokenType; } + /// Peek in the token stream and return `true` if it looks like a modern-style variable declaration is coming up. + static bool _peekModernStyleVarDecl(Parser* parser) + { + // A modern-style variable declaration always starts with an identifier + if(peekTokenType(parser) != TokenType::Identifier) + return false; + + switch(peekTokenType(parser, 1)) + { + default: + return false; + + case TokenType::Colon: + case TokenType::Comma: + case TokenType::RParent: + case TokenType::RBrace: + case TokenType::RBracket: + case TokenType::LBrace: + return true; + } + } + static NodeBase* ParsePropertyDecl(Parser* parser, void* /*userData*/) { PropertyDecl* decl = parser->astBuilder->create<PropertyDecl>(); parser->FillPosition(decl); parser->PushScope(decl); - decl->nameAndLoc = expectIdentifier(parser); - - if( expect(parser, TokenType::Colon) ) + // We want to support property declarations with two + // different syntaxes. + // + // First, we want to support a syntax that is consistent + // with C-style ("traditional") variable declarations: + // + // int myVar = 2; + // proprerty int myProp { ... } + // + // Second we want to support a syntax that is + // consistent with `let` and `var` declarations: + // + // let myVar : int = 2; + // property myProp : int { ... } + // + // The latter case is more constrained, and we will + // detect with two tokens of lookahead. If the + // next token (after `property`) is an identifier, + // and the token after that is a colon (`:`), then + // we assume we are in the `let`/`var`-style case. + // + if(_peekModernStyleVarDecl(parser)) { + decl->nameAndLoc = expectIdentifier(parser); + expect(parser, TokenType::Colon); decl->type = parser->ParseTypeExp(); } + else + { + // The traditional syntax requires a bit more + // care to parse, since it needs to support + // C declarator syntax. + // + DeclaratorInfo declaratorInfo; + declaratorInfo.typeSpec = parser->ParseType(); + + auto declarator = parseDeclarator(parser, kDeclaratorParseOptions_None); + UnwrapDeclarator(parser->astBuilder, declarator, &declaratorInfo); + + // TODO: We might want to handle the case where the + // resulting declarator is not valid to use for + // declaring a property (e.g., it has function parameters). + + decl->nameAndLoc = declaratorInfo.nameAndLoc; + decl->type = TypeExp(declaratorInfo.typeSpec); + } parseStorageDeclBody(parser, decl); @@ -2807,21 +2878,50 @@ namespace Slang return decl; } + /// Parse the common structured of a traditional-style parameter declaration (excluding the trailing semicolon) + static void _parseTraditionalParamDeclCommonBase(Parser* parser, VarDeclBase* decl, DeclaratorParseOptions options = kDeclaratorParseOptions_None) + { + DeclaratorInfo declaratorInfo; + declaratorInfo.typeSpec = parser->ParseType(); + + InitDeclarator initDeclarator = parseInitDeclarator(parser, options); + UnwrapDeclarator(parser->astBuilder, initDeclarator, &declaratorInfo); + + // Assume it is a variable-like declarator + CompleteVarDecl(parser, decl, declaratorInfo); + } + static ParamDecl* parseModernParamDecl( Parser* parser) { - ParamDecl* decl = parser->astBuilder->create<ParamDecl>(); - - // TODO: "modern" parameters should not accept keyword-based - // modifiers and should only accept `[attribute]` syntax for - // modifiers to keep the grammar as simple as possible. + // TODO: For "modern" parameters, we should probably + // not allow arbitrary keyword-based modifiers (only allowing + // `[attribute]`s), and should require that direction modifiers + // like `in`, `out`, and `in out`/`inout` be applied to the + // type (after the colon). // - // Further, they should accept `out` and `in out`/`inout` - // before the type (e.g., `a: inout float4`). + auto modifiers = ParseModifiers(parser); + + // We want to allow both "modern"-style and traditional-style + // parameters to appear in any modern-style parameter list, + // in order to allow programmers the flexibility to code in + // a way that feels natural and not run into lots of + // errors. // - decl->modifiers = ParseModifiers(parser); - parseModernVarDeclBaseCommon(parser, decl); - return decl; + if(_peekModernStyleVarDecl(parser)) + { + ParamDecl* decl = parser->astBuilder->create<ModernParamDecl>(); + decl->modifiers = modifiers; + parseModernVarDeclBaseCommon(parser, decl); + return decl; + } + else + { + ParamDecl* decl = parser->astBuilder->create<ParamDecl>(); + decl->modifiers = modifiers; + _parseTraditionalParamDeclCommonBase(parser, decl); + return decl; + } } static void parseModernParamList( @@ -3924,14 +4024,8 @@ namespace Slang ParamDecl* parameter = astBuilder->create<ParamDecl>(); parameter->modifiers = ParseModifiers(this); - DeclaratorInfo declaratorInfo; - declaratorInfo.typeSpec = ParseType(); - - InitDeclarator initDeclarator = parseInitDeclarator(this, kDeclaratorParseOption_AllowEmpty); - UnwrapDeclarator(astBuilder, initDeclarator, &declaratorInfo); + _parseTraditionalParamDeclCommonBase(this, parameter, kDeclaratorParseOption_AllowEmpty); - // Assume it is a variable-like declarator - CompleteVarDecl(this, parameter, declaratorInfo); return parameter; } |
