summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-parser.cpp
diff options
context:
space:
mode:
authorYong He <yonghe@outlook.com>2025-05-29 08:05:57 -0700
committerGitHub <noreply@github.com>2025-05-29 08:05:57 -0700
commitfaf042ecc3e688a1a3ffbe1ac44d18dd7ddf441a (patch)
treeb54abb2e65b7791d74335ead396cf762f805ab5c /source/slang/slang-parser.cpp
parent45d794f57d453a5564a7360400c5bfc04bf12b31 (diff)
Language version + tuple syntax. (#7230)
* Language version + tuple syntax. * Fix compile error. * regenerate documentation Table of Contents * Fix. * regenerate command line reference * Fix. * Fix. * Fix more test failures. * revert empty line change, * Retrigger CI * #version->#lang * Update source/core/slang-type-text-util.cpp Co-authored-by: ArielG-NV <159081215+ArielG-NV@users.noreply.github.com> * Remove comments. * Fix parsing logic. * Fix parser. * Fix parser. * update test comment * Update options. * regenerate documentation Table of Contents * regenerate command line reference --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com> Co-authored-by: ArielG-NV <159081215+ArielG-NV@users.noreply.github.com>
Diffstat (limited to 'source/slang/slang-parser.cpp')
-rw-r--r--source/slang/slang-parser.cpp164
1 files changed, 120 insertions, 44 deletions
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp
index 565cb2e5f..9eb8c1391 100644
--- a/source/slang/slang-parser.cpp
+++ b/source/slang/slang-parser.cpp
@@ -1185,6 +1185,14 @@ bool tryParseUsingSyntaxDecl(
return tryParseUsingSyntaxDeclImpl<T>(parser, syntaxDecl, outSyntax);
}
+static void maybeUpgradeLanguageVersionFromLegacy(SlangLanguageVersion& ver)
+{
+ if (ver == SLANG_LANGUAGE_VERSION_LEGACY)
+ {
+ ver = SLANG_LANGUAGE_VERSION_2025;
+ }
+}
+
static Modifiers ParseModifiers(Parser* parser, LookupMask modifierLookupMask = LookupMask::Default)
{
Modifiers modifiers;
@@ -1216,7 +1224,7 @@ static Modifiers ParseModifiers(Parser* parser, LookupMask modifierLookupMask =
if (as<VisibilityModifier>(parsedModifier))
{
if (auto currentModule = parser->getCurrentModuleDecl())
- currentModule->isInLegacyLanguage = false;
+ maybeUpgradeLanguageVersionFromLegacy(currentModule->languageVersion);
}
AddModifier(&modifierLink, parsedModifier);
continue;
@@ -1321,7 +1329,7 @@ static NodeBase* parseIncludeDecl(Parser* parser, void* /*userData*/)
auto decl = parser->astBuilder->create<IncludeDecl>();
parseFileReferenceDeclBase(parser, decl);
if (auto currentModule = parser->getCurrentModuleDecl())
- currentModule->isInLegacyLanguage = false;
+ maybeUpgradeLanguageVersionFromLegacy(currentModule->languageVersion);
return decl;
}
@@ -1361,7 +1369,7 @@ static NodeBase* parseModuleDeclarationDecl(Parser* parser, void* /*userData*/)
}
parser->ReadToken(TokenType::Semicolon);
if (auto currentModule = parser->getCurrentModuleDecl())
- currentModule->isInLegacyLanguage = false;
+ maybeUpgradeLanguageVersionFromLegacy(currentModule->languageVersion);
return decl;
}
@@ -5153,7 +5161,20 @@ void Parser::parseSourceFile(ContainerDecl* program)
currentModule = getModuleDecl(program);
- // If the program already has a scope, then reuse it instead of overwriting it!
+ // Verify that the language version is valid.
+ if (sourceLanguage == SourceLanguage::Slang)
+ {
+ if (!isValidSlangLanguageVersion(currentModule->languageVersion))
+ {
+ sink->diagnose(
+ program->loc,
+ Diagnostics::unknownLanguageVersion,
+ currentModule->languageVersion);
+ }
+ }
+
+ // If the program already has a scope, then
+ // reuse it instead of overwriting it!
if (program->ownedScope)
PushScope(program->ownedScope);
else
@@ -5607,7 +5628,7 @@ static Decl* _tryResolveDecl(Parser* parser, Expr* expr)
auto lookupResult = lookUpDirectAndTransparentMembers(
parser->astBuilder,
- nullptr, // no semantics visitor available yet
+ parser->semanticsVisitor,
staticMemberExpr->name,
aggTypeDecl,
declRef);
@@ -5627,7 +5648,7 @@ static Decl* _tryResolveDecl(Parser* parser, Expr* expr)
// Do the lookup in the current scope
auto lookupResult = lookUp(
parser->astBuilder,
- nullptr, // no semantics visitor available yet
+ parser->semanticsVisitor,
varExpr->name,
parser->currentScope);
if (!lookupResult.isValid() || lookupResult.isOverloaded())
@@ -5641,11 +5662,8 @@ static Decl* _tryResolveDecl(Parser* parser, Expr* expr)
static bool isTypeName(Parser* parser, Name* name)
{
- auto lookupResult = lookUp(
- parser->astBuilder,
- nullptr, // no semantics visitor available yet
- name,
- parser->currentScope);
+ auto lookupResult =
+ lookUp(parser->astBuilder, parser->semanticsVisitor, name, parser->currentScope);
if (!lookupResult.isValid() || lookupResult.isOverloaded())
return false;
@@ -7201,17 +7219,23 @@ static bool _isCast(Parser* parser, Expr* expr)
return false;
}
-static bool tryParseExpression(Parser* parser, Expr*& outExpr, TokenType tokenTypeAfter)
+template<typename FIsValidFollowToken>
+static bool tryParseExpression(
+ Parser* parser,
+ Expr*& outExpr,
+ Precedence exprLevel,
+ const FIsValidFollowToken& isValidFollowToken)
{
auto cursor = parser->tokenReader.getCursor();
auto isRecovering = parser->isRecovering;
auto oldSink = parser->sink;
DiagnosticSink newSink(parser->sink->getSourceManager(), nullptr);
parser->sink = &newSink;
- outExpr = parser->ParseExpression();
+ outExpr = parser->ParseExpression(exprLevel);
parser->sink = oldSink;
parser->isRecovering = isRecovering;
- if (outExpr && newSink.getErrorCount() == 0 && parser->LookAheadToken(tokenTypeAfter))
+ if (outExpr && newSink.getErrorCount() == 0 &&
+ isValidFollowToken(parser->tokenReader.peekTokenType()))
{
return true;
}
@@ -7297,9 +7321,7 @@ static Expr* parseAtomicExpr(Parser* parser)
tcexpr->loc = openParen.loc;
tcexpr->functionExpr = varExpr;
-
- auto arg = parsePrefixExpr(parser);
- tcexpr->arguments.add(arg);
+ tcexpr->arguments.add(parsePrefixExpr(parser));
return tcexpr;
}
@@ -7313,45 +7335,99 @@ static Expr* parseAtomicExpr(Parser* parser)
Expr* base = nullptr;
if (parser->LookAheadToken(TokenType::RParent))
{
- // We don't support empty parentheses `()` as a valid expression.
- parser->diagnose(openParen, Diagnostics::invalidEmptyParenthesisExpr);
- base = parser->astBuilder->create<IncompleteExpr>();
- base->type = parser->astBuilder->getErrorType();
+ if (parser->currentModule->languageVersion >= SLANG_LANGUAGE_VERSION_2026)
+ {
+ // We support empty parentheses `()` as a valid expression to construct an
+ // empty tuple in Slang 2026.
+ base = parser->astBuilder->create<TupleExpr>();
+ base->loc = openParen.loc;
+ }
+ else
+ {
+ // We don't support empty parentheses `()` as a valid expression prior to
+ // Slang 2026.
+ parser->diagnose(openParen, Diagnostics::invalidEmptyParenthesisExpr);
+ base = parser->astBuilder->create<IncompleteExpr>();
+ base->type = parser->astBuilder->getErrorType();
+ }
}
else
{
- if (!tryParseExpression(parser, base, TokenType::RParent))
+ // When language version is 2026 or later, we no longer parse comma as a
+ // C/C++-style comma operator when it is inside a paranthesis,
+ // but rather as a tuple element separator.
+ //
+ Precedence exprLevel = Precedence::Comma;
+ if (parser->sourceLanguage == SourceLanguage::Slang &&
+ parser->currentModule->languageVersion >= SLANG_LANGUAGE_VERSION_2026)
+ {
+ // Setting exprLevel to Assignment here will allow the following
+ // tryParseExpression call to parse the expression until the next `)` or
+ // `,`, instead of consuming any ',' as a comma operator.
+ exprLevel = Precedence::Assignment;
+ }
+ auto isValidFollowToken = [=](TokenType tokenType)
+ {
+ if (tokenType == TokenType::RParent)
+ return true;
+ if (exprLevel == Precedence::Assignment && tokenType == TokenType::Comma)
+ return true;
+ return false;
+ };
+ if (!tryParseExpression(parser, base, exprLevel, isValidFollowToken))
{
base = parser->ParseType();
}
}
- parser->ReadToken(TokenType::RParent);
-
- // We now try and determine by what base is, if this is actually a cast or an
- // expression in parentheses
- if (_isCast(parser, base))
+ if (parser->LookAheadToken(TokenType::Comma))
{
- // Parse as a cast
-
- TypeCastExpr* tcexpr = parser->astBuilder->create<ExplicitCastExpr>();
- tcexpr->loc = openParen.loc;
-
- tcexpr->functionExpr = base;
-
- auto arg = parsePrefixExpr(parser);
- tcexpr->arguments.add(arg);
-
- return tcexpr;
+ // We have a comma, so we are not done yet.
+ // If we reach here, the language version must be 2026 or later,
+ // where we allow (a,b,c,...) syntax as a tuple, otherwise we would have
+ // parsed it into `base`.
+ //
+ List<Expr*> elementExprs;
+ elementExprs.add(base);
+ while (AdvanceIf(parser, TokenType::Comma))
+ {
+ if (parser->LookAheadToken(TokenType::RParent))
+ break;
+ auto elementExpr = parser->ParseArgExpr();
+ elementExprs.add(elementExpr);
+ }
+ parser->ReadToken(TokenType::RParent);
+ TupleExpr* tupleExpr = parser->astBuilder->create<TupleExpr>();
+ tupleExpr->loc = openParen.loc;
+ tupleExpr->elements = _Move(elementExprs);
+ return tupleExpr;
}
else
{
- // Pass as an expression in parentheses
+ // We parsed an expression in the form of (expr), without
+ // any commas in `expr`.
+ // We now try and determine by what base is, if this is actually a cast or an
+ // expression in parentheses.
+ //
+ parser->ReadToken(TokenType::RParent);
+ if (_isCast(parser, base))
+ {
+ // Parse as a cast
+ TypeCastExpr* tcexpr = parser->astBuilder->create<ExplicitCastExpr>();
+ tcexpr->loc = openParen.loc;
+
+ tcexpr->functionExpr = base;
+ tcexpr->arguments.add(parsePrefixExpr(parser));
- ParenExpr* parenExpr = parser->astBuilder->create<ParenExpr>();
- parenExpr->loc = openParen.loc;
- parenExpr->base = base;
- return parenExpr;
+ return tcexpr;
+ }
+ else
+ {
+ ParenExpr* parenExpr = parser->astBuilder->create<ParenExpr>();
+ parenExpr->loc = openParen.loc;
+ parenExpr->base = base;
+ return parenExpr;
+ }
}
}
}
@@ -8519,7 +8595,7 @@ void parseSourceFile(
Parser parser(astBuilder, tokens, sink, outerScope, options);
parser.namePool = translationUnit->getNamePool();
- parser.sourceLanguage = translationUnit->sourceLanguage;
+ parser.sourceLanguage = sourceLanguage;
return parser.parseSourceFile(parentDecl);
}