mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 13:40:11 +01:00
Support lexing and parsing positional params (#7651)
This implements lexing and parsing positional parameters such as `$0`.
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/handle.h"
|
||||
#include "toolchain/parse/node_ids.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
auto HandleParseNode(Context& context, Parse::PositionalParamExprId node_id)
|
||||
-> bool {
|
||||
return context.TODO(node_id, "HandlePositionalParamExprId");
|
||||
}
|
||||
} // namespace Carbon::Check
|
||||
@@ -59,12 +59,16 @@ CARBON_DIAGNOSTIC_KIND(ErrorReadingFile)
|
||||
// ============================================================================
|
||||
|
||||
CARBON_DIAGNOSTIC_KIND(InvalidRealLiteralRadix)
|
||||
CARBON_DIAGNOSTIC_KIND(CharacterOnlyAllowedAtStart)
|
||||
CARBON_DIAGNOSTIC_KIND(ContentBeforeStringTerminator)
|
||||
CARBON_DIAGNOSTIC_KIND(DecimalEscapeSequence)
|
||||
CARBON_DIAGNOSTIC_KIND(DollarIntLiteralLeadingZero)
|
||||
CARBON_DIAGNOSTIC_KIND(DollarIntLiteralMissingNumber)
|
||||
CARBON_DIAGNOSTIC_KIND(DumpSemIRRangeMissingEnd)
|
||||
CARBON_DIAGNOSTIC_KIND(DumpSemIRRangeMissingBegin)
|
||||
CARBON_DIAGNOSTIC_KIND(EmptyDigitSequence)
|
||||
CARBON_DIAGNOSTIC_KIND(HexadecimalEscapeMissingDigits)
|
||||
CARBON_DIAGNOSTIC_KIND(InvalidCharacterInDollarIntLiteral)
|
||||
CARBON_DIAGNOSTIC_KIND(InvalidDigit)
|
||||
CARBON_DIAGNOSTIC_KIND(InvalidDigitSeparator)
|
||||
CARBON_DIAGNOSTIC_KIND(InvalidHorizontalWhitespaceInString)
|
||||
@@ -75,6 +79,7 @@ CARBON_DIAGNOSTIC_KIND(NoWhitespaceAfterCommentIntroducer)
|
||||
CARBON_DIAGNOSTIC_KIND(TooManyDigits)
|
||||
CARBON_DIAGNOSTIC_KIND(TooManyTokens)
|
||||
CARBON_DIAGNOSTIC_KIND(TooManyTypeBitWidthDigits)
|
||||
CARBON_DIAGNOSTIC_KIND(TooManyDollarIntDigits)
|
||||
CARBON_DIAGNOSTIC_KIND(UnicodeEscapeMissingBracedDigits)
|
||||
CARBON_DIAGNOSTIC_KIND(UnicodeEscapeSurrogate)
|
||||
CARBON_DIAGNOSTIC_KIND(UnicodeEscapeTooLarge)
|
||||
|
||||
@@ -191,6 +191,11 @@ class [[clang::internal_linkage]] Lexer {
|
||||
auto LexWordAsTypeLiteralToken(llvm::StringRef word, int32_t byte_offset)
|
||||
-> LexResult;
|
||||
|
||||
// Given a lexed word, determine whether it is a dollar int literal and if so
|
||||
// form the corresponding token,
|
||||
auto LexWordAsDollarIntLiteralToken(llvm::StringRef word, int32_t byte_offset)
|
||||
-> LexResult;
|
||||
|
||||
auto LexKeywordOrIdentifier(llvm::StringRef source_text, ssize_t& position)
|
||||
-> LexResult;
|
||||
|
||||
@@ -286,6 +291,7 @@ static constexpr std::array<bool, 256> IsIdStartByteTable = [] {
|
||||
table[c] = true;
|
||||
}
|
||||
table['_'] = true;
|
||||
table['$'] = true;
|
||||
return table;
|
||||
}();
|
||||
|
||||
@@ -297,6 +303,8 @@ static constexpr std::array<bool, 256> IsIdByteTable = [] {
|
||||
for (char c = '0'; c <= '9'; ++c) {
|
||||
table[c] = true;
|
||||
}
|
||||
// Identifiers can only have `$` in start.
|
||||
table['$'] = false;
|
||||
return table;
|
||||
}();
|
||||
|
||||
@@ -307,6 +315,12 @@ static constexpr std::array<bool, 256> IsIdByteTable = [] {
|
||||
static auto ScanForIdentifierPrefixScalar(llvm::StringRef text, ssize_t i)
|
||||
-> llvm::StringRef {
|
||||
const ssize_t size = text.size();
|
||||
if (i == 0 && !text.empty()) {
|
||||
if (!IsIdStartByteTable[static_cast<unsigned char>(text[i])]) {
|
||||
return {};
|
||||
}
|
||||
++i;
|
||||
}
|
||||
while (i < size && IsIdByteTable[static_cast<unsigned char>(text[i])]) {
|
||||
++i;
|
||||
}
|
||||
@@ -410,6 +424,13 @@ static auto ScanForIdentifierPrefixX86(llvm::StringRef text)
|
||||
|
||||
// Use `ssize_t` for performance here as we index memory in a tight loop.
|
||||
ssize_t i = 0;
|
||||
if (!text.empty()) {
|
||||
if (!IsIdStartByteTable[static_cast<unsigned char>(text[i])]) {
|
||||
return {};
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
const ssize_t size = text.size();
|
||||
while ((i + 16) <= size) {
|
||||
__m128i input =
|
||||
@@ -644,6 +665,7 @@ static constexpr auto MakeDispatchTable() -> DispatchTableT {
|
||||
table['/'] = &DispatchLexCommentOrSlash;
|
||||
|
||||
table['_'] = &DispatchLexKeywordOrIdentifier;
|
||||
table['$'] = &DispatchLexKeywordOrIdentifier;
|
||||
// Note that we don't use `llvm::seq` because this needs to be `constexpr`
|
||||
// evaluated.
|
||||
for (unsigned char c = 'a'; c <= 'z'; ++c) {
|
||||
@@ -1453,6 +1475,89 @@ auto Lexer::LexWordAsTypeLiteralToken(llvm::StringRef word, int32_t byte_offset)
|
||||
return LexTokenWithPayload(kind, bit_width_payload, byte_offset);
|
||||
}
|
||||
|
||||
auto Lexer::LexWordAsDollarIntLiteralToken(llvm::StringRef word,
|
||||
int32_t byte_offset) -> LexResult {
|
||||
if (!word.starts_with('$')) {
|
||||
return LexResult::NoMatch();
|
||||
}
|
||||
|
||||
if (!has_leading_space_) {
|
||||
auto prev_token = buffer_.tokens().end()[-1];
|
||||
auto kind = buffer_.GetKind(prev_token);
|
||||
if (kind.is_word()) {
|
||||
CARBON_DIAGNOSTIC(
|
||||
CharacterOnlyAllowedAtStart, Error,
|
||||
"`$` is only allowed at the start of a positional parameter");
|
||||
emitter_.Emit(word.begin(), CharacterOnlyAllowedAtStart);
|
||||
|
||||
auto& prev_token_info = buffer_.token_infos_.Get(prev_token);
|
||||
auto prev_token_text_size = buffer_.GetTokenText(prev_token).size();
|
||||
prev_token_info = TokenInfo(TokenKind::Error, has_leading_space_,
|
||||
prev_token_text_size + word.size(),
|
||||
prev_token_info.byte_offset());
|
||||
return LexResult(TokenIndex(buffer_.token_infos_.size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
auto diagnose_invalid_char = [&]() {
|
||||
CARBON_DIAGNOSTIC(
|
||||
InvalidCharacterInDollarIntLiteral, Error,
|
||||
"Positional parameters can only contain digits after `$`");
|
||||
emitter_.Emit(word.begin() + 1, InvalidCharacterInDollarIntLiteral);
|
||||
return LexTokenWithPayload(TokenKind::Error, word.size(), byte_offset);
|
||||
};
|
||||
|
||||
if (word.size() < 2) {
|
||||
CARBON_DIAGNOSTIC(DollarIntLiteralMissingNumber, Error,
|
||||
"Expected digits after `$`");
|
||||
emitter_.Emit(word.begin() + 1, DollarIntLiteralMissingNumber);
|
||||
return LexTokenWithPayload(TokenKind::Error, word.size(), byte_offset);
|
||||
}
|
||||
if (word[1] == '0' && word.size() > 2) {
|
||||
CARBON_DIAGNOSTIC(
|
||||
DollarIntLiteralLeadingZero, Error,
|
||||
"Leading zeroes are not allowed in positional parameters");
|
||||
emitter_.Emit(word.begin() + 1, DollarIntLiteralLeadingZero);
|
||||
return LexTokenWithPayload(TokenKind::Error, word.size(), byte_offset);
|
||||
}
|
||||
if ((word[1] < '0' || word[1] > '9')) {
|
||||
return diagnose_invalid_char();
|
||||
}
|
||||
|
||||
auto suffix = word.substr(1);
|
||||
int64_t suffix_value;
|
||||
constexpr ssize_t DigitLimit =
|
||||
std::numeric_limits<decltype(suffix_value)>::digits10;
|
||||
if (suffix.size() > DigitLimit) {
|
||||
// See if this is not actually a dollar int literal.
|
||||
if (!llvm::all_of(suffix, IsDecimalDigit)) {
|
||||
return diagnose_invalid_char();
|
||||
}
|
||||
|
||||
// Otherwise, diagnose and produce an error token.
|
||||
CARBON_DIAGNOSTIC(TooManyDollarIntDigits, Error,
|
||||
"found a positional parameter using {0} digits, "
|
||||
"which is greater than the limit of {1}",
|
||||
size_t, size_t);
|
||||
emitter_.Emit(word.begin() + 1, TooManyDollarIntDigits, suffix.size(),
|
||||
DigitLimit);
|
||||
return LexTokenWithPayload(TokenKind::Error, word.size(), byte_offset);
|
||||
}
|
||||
|
||||
suffix_value = suffix[0] - '0';
|
||||
for (char c : suffix.drop_front()) {
|
||||
if (!IsDecimalDigit(c)) {
|
||||
return diagnose_invalid_char();
|
||||
}
|
||||
suffix_value = suffix_value * 10 + (c - '0');
|
||||
}
|
||||
CARBON_CHECK(suffix_value >= 0);
|
||||
return LexTokenWithPayload(
|
||||
TokenKind::DollarIntLiteral,
|
||||
buffer_.value_stores_->ints().Add(suffix_value).AsTokenPayload(),
|
||||
byte_offset);
|
||||
}
|
||||
|
||||
auto Lexer::LexKeywordOrIdentifier(llvm::StringRef source_text,
|
||||
ssize_t& position) -> LexResult {
|
||||
if (static_cast<unsigned char>(source_text[position]) > 0x7F) {
|
||||
@@ -1476,6 +1581,10 @@ auto Lexer::LexKeywordOrIdentifier(llvm::StringRef source_text,
|
||||
LexWordAsTypeLiteralToken(identifier_text, byte_offset)) {
|
||||
return result;
|
||||
}
|
||||
if (LexResult result =
|
||||
LexWordAsDollarIntLiteralToken(identifier_text, byte_offset)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check if the text matches a keyword token, and if so use that.
|
||||
TokenKind kind = llvm::StringSwitch<TokenKind>(identifier_text)
|
||||
|
||||
@@ -42,7 +42,7 @@ auto NumericLiteral::Lex(llvm::StringRef source_text,
|
||||
int n = source_text.size();
|
||||
for (; i != n; ++i) {
|
||||
char c = source_text[i];
|
||||
if (IsAlnum(c) || c == '_') {
|
||||
if (IsAlnum(c) || c == '_' || c == '$') {
|
||||
if (IsLower(c) && seen_radix_point && !seen_plus_minus) {
|
||||
result.exponent_ = i;
|
||||
seen_potential_exponent = true;
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lex/testdata/dollar_int_literals.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lex/testdata/dollar_int_literals.carbon
|
||||
|
||||
// --- valid.carbon
|
||||
// CHECK:STDOUT: - filename: valid.carbon
|
||||
// CHECK:STDOUT: tokens:
|
||||
$0
|
||||
// CHECK:STDOUT: - { index: 1, kind: "DollarIntLiteral", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "$0", has_leading_space: true }
|
||||
$1
|
||||
// CHECK:STDOUT: - { index: 2, kind: "DollarIntLiteral", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "$1", has_leading_space: true }
|
||||
$42
|
||||
// CHECK:STDOUT: - { index: 3, kind: "DollarIntLiteral", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "$42", has_leading_space: true }
|
||||
$100
|
||||
// CHECK:STDOUT: - { index: 4, kind: "DollarIntLiteral", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "$100", has_leading_space: true }
|
||||
$999999999999
|
||||
// CHECK:STDOUT: - { index: 5, kind: "DollarIntLiteral", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "$999999999999", has_leading_space: true }
|
||||
|
||||
// --- fail_invalid.carbon
|
||||
// CHECK:STDOUT: - filename: fail_invalid.carbon
|
||||
// CHECK:STDOUT: tokens:
|
||||
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+4]]:2: error: Expected digits after `$` [DollarIntLiteralMissingNumber]
|
||||
// CHECK:STDERR: $
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
$
|
||||
// CHECK:STDOUT: - { index: 1, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "$", has_leading_space: true }
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+4]]:2: error: Expected digits after `$` [DollarIntLiteralMissingNumber]
|
||||
// CHECK:STDERR: $ 1
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
$ 1
|
||||
// CHECK:STDOUT: - { index: 2, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "$", has_leading_space: true }
|
||||
// CHECK:STDOUT: - { index: 3, kind: "IntLiteral", line: {{ *}}[[@LINE-2]], column: 3, indent: 1, spelling: "1", value: "1", has_leading_space: true }
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+4]]:2: error: Positional parameters can only contain digits after `$` [InvalidCharacterInDollarIntLiteral]
|
||||
// CHECK:STDERR: $foo
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
$foo
|
||||
// CHECK:STDOUT: - { index: 4, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "$foo", has_leading_space: true }
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+4]]:2: error: Positional parameters can only contain digits after `$` [InvalidCharacterInDollarIntLiteral]
|
||||
// CHECK:STDERR: $1a32
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
$1a32
|
||||
// CHECK:STDOUT: - { index: 5, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "$1a32", has_leading_space: true }
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+4]]:2: error: Leading zeroes are not allowed in positional parameters [DollarIntLiteralLeadingZero]
|
||||
// CHECK:STDERR: $00
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
$00
|
||||
// CHECK:STDOUT: - { index: 6, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "$00", has_leading_space: true }
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+4]]:2: error: Leading zeroes are not allowed in positional parameters [DollarIntLiteralLeadingZero]
|
||||
// CHECK:STDERR: $025321
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
$025321
|
||||
// CHECK:STDOUT: - { index: 7, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "$025321", has_leading_space: true }
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+4]]:2: error: Leading zeroes are not allowed in positional parameters [DollarIntLiteralLeadingZero]
|
||||
// CHECK:STDERR: $0x12
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
$0x12
|
||||
// CHECK:STDOUT: - { index: 8, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "$0x12", has_leading_space: true }
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+4]]:2: error: Leading zeroes are not allowed in positional parameters [DollarIntLiteralLeadingZero]
|
||||
// CHECK:STDERR: $0b1100
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
$0b1100
|
||||
// CHECK:STDOUT: - { index: 9, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "$0b1100", has_leading_space: true }
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+4]]:2: error: invalid digit '$' in decimal numeric literal [InvalidDigit]
|
||||
// CHECK:STDERR: 2$63
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
2$63
|
||||
// CHECK:STDOUT: - { index: 10, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "2$63", has_leading_space: true }
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+4]]:4: error: `$` is only allowed at the start of a positional parameter [CharacterOnlyAllowedAtStart]
|
||||
// CHECK:STDERR: abc$37
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
abc$37
|
||||
// CHECK:STDOUT: - { index: 11, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "abc$37" }
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+4]]:6: error: `$` is only allowed at the start of a positional parameter [CharacterOnlyAllowedAtStart]
|
||||
// CHECK:STDERR: r#foo$0
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
r#foo$0
|
||||
// CHECK:STDOUT: - { index: 12, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "r#foo$0" }
|
||||
// CHECK:STDERR: fail_invalid.carbon:[[@LINE+4]]:2: error: found a positional parameter using 19 digits, which is greater than the limit of 18 [TooManyDollarIntDigits]
|
||||
// CHECK:STDERR: $9999999999999999999
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
$9999999999999999999
|
||||
// CHECK:STDOUT: - { index: 13, kind: "Error", line: {{ *}}[[@LINE-1]], column: 1, indent: 1, spelling: "$9999999999999999999", has_leading_space: true }
|
||||
@@ -94,7 +94,8 @@ class TokenInfo {
|
||||
CARBON_DCHECK(kind() == TokenKind::IntLiteral ||
|
||||
kind() == TokenKind::IntTypeLiteral ||
|
||||
kind() == TokenKind::UnsignedIntTypeLiteral ||
|
||||
kind() == TokenKind::FloatTypeLiteral);
|
||||
kind() == TokenKind::FloatTypeLiteral ||
|
||||
kind() == TokenKind::DollarIntLiteral);
|
||||
return IntId::MakeFromTokenPayload(token_payload_);
|
||||
}
|
||||
|
||||
|
||||
@@ -248,6 +248,7 @@ CARBON_TOKEN(CharLiteral)
|
||||
CARBON_TOKEN(IntTypeLiteral)
|
||||
CARBON_TOKEN(UnsignedIntTypeLiteral)
|
||||
CARBON_TOKEN(FloatTypeLiteral)
|
||||
CARBON_TOKEN(DollarIntLiteral)
|
||||
CARBON_TOKEN(FileStart)
|
||||
CARBON_TOKEN(FileEnd)
|
||||
|
||||
|
||||
@@ -86,6 +86,11 @@ class TokenKind : public CARBON_ENUM_BASE(TokenKind) {
|
||||
*this == TokenKind::FloatTypeLiteral;
|
||||
}
|
||||
|
||||
// Test whether this kind of token is a dollar int literal.
|
||||
auto is_dollar_int_literal() const -> bool {
|
||||
return *this == TokenKind::DollarIntLiteral;
|
||||
}
|
||||
|
||||
// Test whether this kind of token is a word.
|
||||
auto is_word() const -> bool {
|
||||
return *this == TokenKind::Identifier || *this == TokenKind::Underscore ||
|
||||
|
||||
@@ -93,7 +93,8 @@ auto TokenizedBuffer::GetTokenText(TokenIndex token) const -> llvm::StringRef {
|
||||
|
||||
// Refer back to the source text to avoid needing to reconstruct the
|
||||
// spelling from the size.
|
||||
if (token_info.kind().is_sized_type_literal()) {
|
||||
if (token_info.kind().is_sized_type_literal() ||
|
||||
token_info.kind().is_dollar_int_literal()) {
|
||||
llvm::StringRef suffix = source_->text()
|
||||
.substr(token_info.byte_offset() + 1)
|
||||
.take_while(IsDecimalDigit);
|
||||
|
||||
@@ -369,7 +369,7 @@ TEST_F(LexerTest, SplitsNumericLiteralsProperly) {
|
||||
}
|
||||
|
||||
TEST_F(LexerTest, HandlesGarbageCharacters) {
|
||||
constexpr char GarbageText[] = "$$💩-$\n$\0$12$\n\\\"\\\n\"x";
|
||||
constexpr char GarbageText[] = "##💩-#\n#\0#12#\n\\\"\\\n\"x";
|
||||
auto& buffer = compile_helper_.GetTokenizedBuffer(
|
||||
llvm::StringRef(GarbageText, sizeof(GarbageText) - 1));
|
||||
EXPECT_TRUE(buffer.has_errors());
|
||||
@@ -381,16 +381,16 @@ TEST_F(LexerTest, HandlesGarbageCharacters) {
|
||||
.line = 1,
|
||||
.column = 1,
|
||||
// 💩 takes 4 bytes, and we count column as bytes offset.
|
||||
.text = llvm::StringRef("$$💩", 6)},
|
||||
.text = llvm::StringRef("##💩", 6)},
|
||||
{.kind = TokenKind::Minus, .line = 1, .column = 7},
|
||||
{.kind = TokenKind::Error, .line = 1, .column = 8, .text = "$"},
|
||||
{.kind = TokenKind::Error, .line = 1, .column = 8, .text = "#"},
|
||||
// newline
|
||||
{.kind = TokenKind::Error,
|
||||
.line = 2,
|
||||
.column = 1,
|
||||
.text = llvm::StringRef("$\0$", 3)},
|
||||
.text = llvm::StringRef("#\0#", 3)},
|
||||
{.kind = TokenKind::IntLiteral, .line = 2, .column = 4, .text = "12"},
|
||||
{.kind = TokenKind::Error, .line = 2, .column = 6, .text = "$"},
|
||||
{.kind = TokenKind::Error, .line = 2, .column = 6, .text = "#"},
|
||||
// newline
|
||||
{.kind = TokenKind::Backslash, .line = 3, .column = 1, .text = "\\"},
|
||||
{.kind = TokenKind::Error, .line = 3, .column = 2, .text = "\"\\"},
|
||||
|
||||
@@ -139,6 +139,11 @@ auto HandleExprInPostfix(Context& context) -> void {
|
||||
context.PushState(state);
|
||||
break;
|
||||
}
|
||||
case Lex::TokenKind::DollarIntLiteral: {
|
||||
context.AddLeafNode(NodeKind::PositionalParamExpr, context.Consume());
|
||||
context.PushState(state);
|
||||
break;
|
||||
}
|
||||
case Lex::TokenKind::Str: {
|
||||
context.AddLeafNode(NodeKind::StringTypeLiteral, context.Consume());
|
||||
context.PushState(state);
|
||||
|
||||
@@ -311,6 +311,8 @@ CARBON_PARSE_NODE_KIND_EXPRESSION(PointerMemberAccessExpr)
|
||||
|
||||
CARBON_PARSE_NODE_KIND_EXPRESSION(IntLiteral)
|
||||
|
||||
CARBON_PARSE_NODE_KIND_EXPRESSION(PositionalParamExpr)
|
||||
|
||||
CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(BoolLiteralFalse, False)
|
||||
CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(BoolLiteralTrue, True)
|
||||
CARBON_PARSE_NODE_KIND_TOKEN_LITERAL(CharLiteral, CharLiteral)
|
||||
|
||||
@@ -18,6 +18,12 @@ fn foo(bar: i64, baz: i64) {
|
||||
foo(baz, bar + baz);
|
||||
}
|
||||
|
||||
// --- with_positional_params.carbon
|
||||
|
||||
fn foo {
|
||||
foo($0, $42.0 + $86);
|
||||
}
|
||||
|
||||
// --- with_return_type.carbon
|
||||
|
||||
fn foo() -> f64 {
|
||||
@@ -116,6 +122,25 @@ fn TestRecoveryFromSpuriousEquals();
|
||||
// CHECK:STDOUT: ├─FunctionDefinition '}'
|
||||
// CHECK:STDOUT: ├─FileEnd ''
|
||||
// CHECK:STDOUT: (root)
|
||||
// CHECK:STDOUT: - filename: with_positional_params.carbon
|
||||
// CHECK:STDOUT: ╭─FileStart ''
|
||||
// CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn'
|
||||
// CHECK:STDOUT: │ ├─IdentifierNameNotBeforeSignature 'foo'
|
||||
// CHECK:STDOUT: │ ╭─FunctionDefinitionStart '{'
|
||||
// CHECK:STDOUT: │ │ ╭─IdentifierNameExpr 'foo'
|
||||
// CHECK:STDOUT: │ │ ╭─CallExprStart '('
|
||||
// CHECK:STDOUT: │ │ ├─PositionalParamExpr '$0'
|
||||
// CHECK:STDOUT: │ │ ├─TupleLiteralComma ','
|
||||
// CHECK:STDOUT: │ │ │ ╭─PositionalParamExpr '$42'
|
||||
// CHECK:STDOUT: │ │ │ ├─IntLiteral '0'
|
||||
// CHECK:STDOUT: │ │ │ ╭─MemberAccessExpr '.'
|
||||
// CHECK:STDOUT: │ │ │ ├─PositionalParamExpr '$86'
|
||||
// CHECK:STDOUT: │ │ ├─InfixOperatorPlus '+'
|
||||
// CHECK:STDOUT: │ │ ╭─CallExpr ')'
|
||||
// CHECK:STDOUT: │ ├─ExprStatement ';'
|
||||
// CHECK:STDOUT: ├─FunctionDefinition '}'
|
||||
// CHECK:STDOUT: ├─FileEnd ''
|
||||
// CHECK:STDOUT: (root)
|
||||
// CHECK:STDOUT: - filename: with_return_type.carbon
|
||||
// CHECK:STDOUT: ╭─FileStart ''
|
||||
// CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn'
|
||||
|
||||
@@ -1837,6 +1837,11 @@ struct NamedConstraintDefinition {
|
||||
Lex::CloseCurlyBraceTokenIndex token;
|
||||
};
|
||||
|
||||
// `$0`
|
||||
using PositionalParamExpr =
|
||||
LeafNode<NodeKind::PositionalParamExpr, Lex::DollarIntLiteralTokenIndex,
|
||||
NodeCategory::Expr>;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// A complete source file. Note that there is no corresponding parse node for
|
||||
|
||||
Reference in New Issue
Block a user