Files
compiler-explorer/static/modes/cppfront-mode.ts
Matt Godbolt f824efe73e Library updates and lint fixes (#8099)
* Minor updates only
* Added explicit radix parameter (10) to all Number.parseInt() calls throughout the codebase (new lint rule)
* Updated several @ts-ignore comments to @ts-expect-error for better TypeScript practices (new lint rule)
* Removed unnecessary @ts-ignore comments in some mode files (ditto)
* Used "none return" based arrow functions for some map stuff
* Replaced a `map()` call that didn't return anything to a for() loop
* Fixed up some cypress stuff, noting work for the future
2025-09-12 14:23:49 -05:00

873 lines
41 KiB
TypeScript

// Copyright (c) 2022, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import $ from 'jquery';
import * as monaco from 'monaco-editor';
import * as cpp from 'monaco-editor/esm/vs/basic-languages/cpp/cpp';
import cppp from './cppp-mode.js';
function definition(): monaco.languages.IMonarchLanguage {
const cppfront = $.extend(true, {}, cppp); // deep copy
cppfront.tokenPostfix = '.herb';
cppfront.defaultToken = 'invalid';
// # Cpp2 token CSS class legend.
//
// Declaration/use of an _identifier_.
// - 'identifier.definition'
// - 'identifier.use'
// - 'keyword.identifier.definition'
// - 'keyword.identifier.use'
//
// Identifier that is contextually a type.
// - 'type.contextual'
// - 'keyword.type.contextual'
//
// Grammar term.
// - 'keyword.contract-kind'
// - 'keyword.this-specifier'
// - 'keyword.parameter-direction'
// Generic parsers.
function parseCpp2Balanced(delimiters: string, delimiter: string, opener: RegExp, closer: RegExp) {
return (cppfront.tokenizer['parse_cpp2_balanced_' + delimiters] = [
{include: '@whitespace'},
[opener, 'delimiter.' + delimiter, '$S2.$S3.$S4'],
[closer, 'delimiter.' + delimiter, '@pop'],
[/./, 'invalid', '@pop'],
]);
}
parseCpp2Balanced('angles', 'angle', /</, />/);
parseCpp2Balanced('parentheses', 'parenthesis', /\(/, /\)/);
parseCpp2Balanced('squares', 'square', /\[/, /\]/);
parseCpp2Balanced('curlies', 'curly', /{/, /}/);
cppfront.tokenizer.parse_cpp2_balanced_punctuators = [
// `$S2` is the parser.
[/</, {token: '@rematch', switchTo: 'parse_cpp2_balanced_angles.$S2.$S3'}],
[/\(/, {token: '@rematch', switchTo: 'parse_cpp2_balanced_parentheses.$S2'}],
[/\[/, {token: '@rematch', switchTo: 'parse_cpp2_balanced_squares.$S2.$S3'}],
[/{/, {token: '@rematch', switchTo: 'parse_cpp2_balanced_curlies.$S2.$S3.$S4'}],
];
// Until the single character `$S2`, parses `$S3`.
cppfront.tokenizer.parse_cpp2_until = [
{include: '@whitespace'},
[
/(.).*/,
{
cases: {
'$1==$S2': {token: '@rematch', next: '@pop'},
'@': {token: '@rematch', next: '$S3'},
},
},
],
];
function parseCommaSeparated(state) {
return [{include: '@whitespace'}, state, [/,/, 'delimiter'], [/./, '@rematch', '@pop']];
}
function setupLiteralParsers() {
function balancedParenthesesRegex(max_nesting: number) {
const front = String.raw`\((?:[^\\()]*|`;
const back = String.raw`)*\)`;
return front.repeat(max_nesting + 1) + back.repeat(max_nesting + 1);
}
cppfront.at_cpp2_balanced_parentheses = balancedParenthesesRegex(5);
cppfront.at_cpp2_interpolation = /@at_cpp2_balanced_parentheses\$/;
cppfront.tokenizer.parse_cpp2_interpolation = [
[/(\()(.)/, ['delimiter.parenthesis', {token: '@rematch', next: 'parse_cpp2_expression'}]],
[/:[^)]*/, 'string'],
[/\)/, 'delimiter.parenthesis'],
[/\$/, 'delimiter.interpolation', '@pop'],
];
cppfront.at_cpp2_string_literal = /@encoding?(?:\$?R)?"/;
cppfront.tokenizer.parse_cpp2_string_literal = [
// Rules adapted from [Monaco's C++][].
[/@encoding?\$?R"(?:([^ ()\\\t]*))\(/, {token: 'string.raw.begin', switchTo: '@raw.$1.cpp2'}],
[/@encoding?"([^"\\]|\\.)*$/, 'string.invalid', '@pop'],
[/@encoding?"/, {token: 'string', switchTo: '@string..cpp2'}],
];
function parseCpp2Interpolation(state: string, prefix_token_regex: RegExp, token_class: string) {
cppfront.tokenizer[state].unshift([
prefix_token_regex.source + /(@at_cpp2_interpolation)/.source,
{
cases: {
'$S3==cpp2': [token_class, {token: '@rematch', next: 'parse_cpp2_interpolation'}],
'@': [token_class, token_class],
},
},
]);
}
// Highlight interpolation also in non-interpolated raw string literal as it's most likely used for reflection.
parseCpp2Interpolation('string', /([^\\"]*?)/, 'string');
parseCpp2Interpolation('raw', /(.*?)/, 'string.raw');
// Rules adapted from [Monaco's C++][]:
cppfront.tokenizer.parse_cpp2_number_literal = [
[/\d*\d+[eE]([-+]?\d+)?(@floatsuffix)/, 'number.float', '@pop'],
[/\d*\.\d+([eE][-+]?\d+)?(@floatsuffix)/, 'number.float', '@pop'],
[/0[xX][0-9a-fA-F']*[0-9a-fA-F](@integersuffix)/, 'number.hex', '@pop'],
[/0[0-7']*[0-7](@integersuffix)/, 'number.octal', '@pop'],
[/0[bB][0-1']*[0-1](@integersuffix)/, 'number.binary', '@pop'],
[/\d[\d']*\d(@integersuffix)/, 'number', '@pop'],
[/\d(@integersuffix)/, 'number', '@pop'],
[/./, 'invalid', '@pop'],
];
cppfront.tokenizer.parse_cpp2_character_literal = [
[/'[^\\']'/, 'string', '@pop'],
[/(')(@escapes)(')/, ['string', 'string.escape', {token: 'string', next: '@pop'}]],
[/'/, 'string.invalid', '@pop'],
];
cppfront.at_cpp2_literal_keyword = /(?:nullptr|true|false)\b/;
cppfront.at_cpp2_literal = /\d|'|@at_cpp2_literal_keyword|@at_cpp2_string_literal/; // No `.`; `.0` isn't Cpp2.
cppfront.tokenizer.parse_cpp2_literal = [
[/\d/, {token: '@rematch', switchTo: 'parse_cpp2_number_literal'}],
[/'/, {token: '@rematch', switchTo: 'parse_cpp2_character_literal'}],
[/@at_cpp2_literal_keyword/, 'keyword.$0', '@pop'],
[/@at_cpp2_string_literal/, {token: '@rematch', switchTo: 'parse_cpp2_string_literal'}],
[/./, 'invalid', '@pop'],
];
}
setupLiteralParsers();
function setupIdExpressionParsers() {
cppfront.at_cpp2_is_as_operator = /(?:is|as)\b/;
cppfront.at_cpp2_overloaded_operator_keyword = cppfront.at_cpp2_is_as_operator;
cppfront.at_cpp2_overloaded_operator =
/\/=|\/|<<=|<<|<=>|<=|<|>>=|>>|>=|>|\+\+|\+=|\+|--|-=|->|-|/.source +
/\|\||\|=|\||&&|&=|&|\*=|\*|%=|%|\^=|\^|~|==|=|!=|!|\(\s*\)|\[\s*\]|/.source +
cppfront.at_cpp2_overloaded_operator_keyword.source;
cppfront.at_cpp2_non_operator_identifier = /[a-zA-Z_]\w*/;
cppfront.at_cpp2_operator_identifier = /operator\b\s*@at_cpp2_overloaded_operator/;
cppfront.at_cpp2_identifier = /@at_cpp2_non_operator_identifier|@at_cpp2_operator_identifier/;
cppfront.tokenizer.parse_cpp2_identifier = [
[
/(operator)(\s+)(@at_cpp2_overloaded_operator_keyword)/,
[{token: 'keyword.identifier.$S2'}, '', {token: 'keyword.identifier.$S2', next: '@pop'}],
],
[
/(operator\b)(\s*)(@at_cpp2_overloaded_operator)/,
[{token: 'keyword.identifier.$S2'}, '', {token: 'delimiter', next: '@pop'}],
],
[
/(?:this|that)\b/,
{
cases: {
'$S2==parameter': {token: 'keyword.identifier.definition', next: '@pop'},
'@': {token: 'keyword.identifier.$S2', next: '@pop'},
},
},
],
[/_(?=\s*=[^=])/, 'keyword.identifier.use', '@pop'],
[
/_\b/,
{
cases: {
'$S2~definition|parameter': {token: 'keyword.identifier.definition', next: '@pop'}, // Anonymous definition.
'$S2==using_statement': {token: 'keyword', next: '@pop'},
'@': {token: '@rematch', switchTo: 'parse_cpp2_non_operator_identifier.$S2'},
},
},
],
[/./, {token: '@rematch', switchTo: 'parse_cpp2_non_operator_identifier.$S2'}],
];
cppfront.tokenizer.parse_cpp2_non_operator_identifier = [
[
/@at_cpp2_non_operator_identifier/,
{
cases: {
'$S2~definition|parameter': {
token: 'identifier.definition',
switchTo: 'parse_cpp2_parameter_ellipsis.$S2',
},
'$S2==type': {token: 'type.contextual', next: '@pop'},
'$S2==contract_kind': {token: 'keyword.contract-kind', next: '@pop'},
'$S2==builtin_meta_function': {token: 'keyword', next: '@pop'},
'@': {token: 'identifier.use', next: '@pop'},
},
},
],
];
cppfront.tokenizer.parse_cpp2_parameter_ellipsis = [
[
/\.\.\./,
{
cases: {
'$S2==parameter': {token: 'delimiter.ellipsis', next: '@pop'},
'@': {token: '@rematch', next: '@pop'},
},
},
],
[/./, '@rematch', '@pop'],
];
cppfront.at_cpp2_unqualified_id_template_type_keyword = /(?:finally|cpp1_ref|cpp1_rvalue_ref)\b/;
cppfront.at_cpp2_unqualified_id_template_expression_keyword = /(?:new|unchecked_(?:narrow|cast))\b/;
cppfront.at_cpp2_unqualified_id_type_function_keyword =
/(?:type_of|static_assert|unchecked_(?:(?:cmp_)(?:less|greater)(?:_eq)?|div|dereference|subscript))\b(?=\s*\()/;
cppfront.at_cpp2_unqualified_id_keywords = /(unique|shared)(\.)(new(?=<))/;
cppfront.at_cpp2_unqualified_id_keyword =
/@at_cpp2_unqualified_id_template_type_keyword|@at_cpp2_unqualified_id_template_expression_keyword|@at_cpp2_unqualified_id_type_function_keyword|@at_cpp2_unqualified_id_keywords/;
cppfront.tokenizer.parse_cpp2_unqualified_id_keywords = [
[
/@at_cpp2_unqualified_id_keywords/,
[
'keyword',
'delimiter',
{token: 'keyword.$3', switchTo: 'parse_cpp2_single_type_template_argument_list'},
],
],
[
/@at_cpp2_unqualified_id_template_type_keyword(?=<)/,
{token: 'keyword.type', switchTo: 'parse_cpp2_single_type_template_argument_list'},
],
[
/@at_cpp2_unqualified_id_template_expression_keyword(?=<)/,
{
cases: {
'$S2==expression': {
token: 'keyword',
switchTo: 'parse_cpp2_single_type_template_argument_list',
},
'@': {token: 'invalid', switchTo: 'parse_cpp2_single_type_template_argument_list'},
},
},
],
[
/@at_cpp2_unqualified_id_type_function_keyword(?=\s*\()/,
{token: 'keyword.type', switchTo: 'parse_cpp2_expression_list'},
],
[
/@at_cpp2_unqualified_id_template_type_keyword(?=\s*\()/,
{
cases: {
'$S2==expression': {token: 'keyword.type', switchTo: 'parse_cpp2_expression_list'}, // CTAD in _expression_.
'@': {token: 'invalid', switchTo: 'parse_cpp2_expression_list'},
},
},
],
[
/@at_cpp2_unqualified_id_template_type_keyword/,
{
cases: {
'$S2==type': {token: 'keyword.type', next: '@pop'}, // CTAD in _type-id_.
'@': {token: 'invalid', next: '@pop'},
},
},
],
];
cppfront.at_cpp2_type_qualifier = /const\b|\*/;
cppfront.tokenizer.parse_cpp2_type_qualifier_seq = [
{include: '@whitespace'},
[/const\b/, 'keyword'],
[/\*/, 'delimiter'],
[/./, '@rematch', '@pop'],
];
cppfront.at_cpp2_keyword_type =
/(?:[iu](?:8|16|32|64)|u?(?:short|int|long|longlong)|(?:un)?signed|void|bool|(?:_[su])?char|(?:long)?double|float)\b/;
cppfront.at_cpp2_type_id =
/@at_cpp2_type_qualifier|@at_cpp2_non_operator_id_expression|@at_cpp2_function_type_id/;
cppfront.at_cpp2_function_type_id =
/\(\s*\)|\(\s*(?:(?:@at_cpp2_parameter_direction\s+)?@at_cpp2_non_operator_identifier\s*)?@at_cpp2_unnamed_declaration_head/;
cppfront.tokenizer.parse_cpp2_type_id = [
[/@at_cpp2_type_qualifier/, '@rematch', 'parse_cpp2_type_qualifier_seq'],
[/@at_cpp2_keyword_type\s*@at_cpp2_keyword_type/, 'invalid', '@pop'],
[/@at_cpp2_keyword_type|_\b/, 'keyword.type.contextual', '@pop'],
[
/@at_cpp2_unqualified_id_keyword/,
{token: '@rematch', switchTo: 'parse_cpp2_unqualified_id_keywords.type'},
],
[/=/, '@rematch', '@pop'],
[/@at_cpp2_non_operator_id_expression/, {token: '@rematch', switchTo: 'parse_cpp2_id_expression.type'}],
[/\(/, {token: '@rematch', switchTo: 'parse_cpp2_function_type'}],
];
cppfront.at_cpp2_builtin_contract_group = /(?:default|type_safety|bounds_safety|null_safety|testing)\b/;
cppfront.at_cpp2_template_argument = /@at_cpp2_string_literal|@at_cpp2_expression|@at_cpp2_type_id/;
cppfront.tokenizer.parse_cpp2_template_argument = [
[/@at_cpp2_keyword_type/, 'keyword.type', '@pop'],
[
/@at_cpp2_builtin_contract_group/,
{
cases: {
'$S2==contract_kind': {token: 'keyword.contract-group', next: '@pop'},
'@': {token: '@rematch', switchTo: 'parse_cpp2_template_argument_rest'},
},
},
],
[/./, {token: '@rematch', switchTo: 'parse_cpp2_template_argument_rest'}],
];
cppfront.tokenizer.parse_cpp2_template_argument_rest = [
[/@at_cpp2_function_type_id/, {token: '@rematch', switchTo: 'parse_cpp2_type_id'}],
[/@at_cpp2_type_qualifier/, {token: '@rematch', switchTo: 'parse_cpp2_type_id'}],
[/@at_cpp2_expression/, {token: '@rematch', switchTo: 'parse_cpp2_expression.template_argument'}],
[/@at_cpp2_type_id/, {token: '@rematch', switchTo: 'parse_cpp2_type_id'}],
];
cppfront.tokenizer.parse_cpp2_template_argument_seq = parseCommaSeparated([
/@at_cpp2_template_argument/,
'@rematch',
'parse_cpp2_template_argument.$S2',
]);
cppfront.tokenizer.parse_cpp2_template_argument_list = [
[/</, {token: '@rematch', switchTo: 'parse_cpp2_balanced_angles.parse_cpp2_template_argument_seq.$S2'}],
];
cppfront.tokenizer.parse_cpp2_single_type_template_argument_list = [
[/</, {token: '@rematch', switchTo: 'parse_cpp2_balanced_angles.parse_cpp2_type_id'}],
];
cppfront.at_cpp2_id_expression = /::|@at_cpp2_identifier/;
cppfront.at_cpp2_non_operator_id_expression = /::|@at_cpp2_non_operator_identifier/;
cppfront.tokenizer.parse_cpp2_template_id = [
[/@at_cpp2_identifier/, '@rematch', 'parse_cpp2_identifier.$S2'],
[/</, '@rematch', 'parse_cpp2_template_argument_list.$S2'],
[/\s*(?:\.\.?|::)/, {token: '@rematch', switchTo: 'parse_cpp2_id_expression.$S2'}],
[/./, '@rematch', '@pop'],
];
cppfront.tokenizer.parse_cpp2_id_expression = [
{include: '@whitespace'},
[/::/, ''],
[/\.\.[.<=]/, '@rematch', '@pop'],
[/\.\.?/, 'delimiter'],
[/@at_cpp2_identifier</, {token: '@rematch', switchTo: 'parse_cpp2_template_id.$S2'}],
[/@at_cpp2_non_operator_identifier(?=\s*(?:\.\.?|::))/, '@rematch', 'parse_cpp2_identifier.use'],
[/@at_cpp2_identifier/, {token: '@rematch', switchTo: 'parse_cpp2_identifier.$S2'}],
];
}
setupIdExpressionParsers();
function setupExpressionParsers() {
cppfront.tokenizer.parse_cpp2_primary_expression_id_expression = [
[
/@at_cpp2_unqualified_id_keyword/,
{token: '@rematch', switchTo: 'parse_cpp2_unqualified_id_keywords.expression'},
],
[/@at_cpp2_keyword_type/, 'keyword.type', '@pop'],
[/./, {token: '@rematch', switchTo: 'parse_cpp2_id_expression.use'}],
];
cppfront.tokenizer.parse_cpp2_expression_seq = parseCommaSeparated([
/@at_cpp2_expression/,
'@rematch',
'parse_cpp2_expression',
]);
cppfront.tokenizer.parse_cpp2_expression_list = [
[/./, {token: '@rematch', switchTo: 'parse_cpp2_balanced_punctuators.parse_cpp2_expression_seq'}],
];
cppfront.at_cpp2_primary_expression =
/@at_cpp2_literal|@at_cpp2_id_expression|\.\.\.|\(|@at_cpp2_unnamed_declaration_head/;
// Can't ensure sequential parsing.
cppfront.tokenizer.parse_cpp2_primary_expression = [
[/inspect\b/, '@rematch', 'parse_cpp2_inspect_expression'],
// These two happen to parse UDLs:
[/@at_cpp2_literal/, {token: '@rematch', switchTo: 'parse_cpp2_primary_expression_literal_.$S2.$S3'}],
[
/@at_cpp2_id_expression/,
{token: '@rematch', switchTo: 'parse_cpp2_primary_expression_id_expression_.$S2.$S3'},
],
[/\.\.\./, 'delimiter.ellipsis'],
// Handle `(` later to workaround `(0)is` being parsed as two adjacent primary expressions.
[/@at_cpp2_unnamed_declaration_head/, '@rematch', 'parse_cpp2_declaration.expression'],
[/./, {token: '@rematch', switchTo: 'parse_cpp2_postfix_expression.$S2.$S3'}],
];
cppfront.tokenizer.parse_cpp2_primary_expression_id_expression_ = [
[/@at_cpp2_id_expression/, '@rematch', 'parse_cpp2_primary_expression_id_expression'],
[/\.\.\./, 'delimiter.ellipsis'],
[/./, {token: '@rematch', switchTo: 'parse_cpp2_postfix_expression.$S2.$S3'}],
];
cppfront.tokenizer.parse_cpp2_primary_expression_literal_ = [
[/@at_cpp2_literal/, '@rematch', 'parse_cpp2_literal'],
[/./, {token: '@rematch', switchTo: 'parse_cpp2_postfix_expression.$S2.$S3'}],
];
cppfront.at_cpp2_postfix_operator =
/\+\+|--|~|\$|[*&](?!\s*(?=\(|@at_cpp2_non_operator_identifier|@at_cpp2_literal))/;
cppfront.tokenizer.parse_cpp2_postfix_expression = [
{include: '@whitespace'},
[/@at_cpp2_postfix_operator/, 'delimiter.postfix-operator'],
[/[[(]/, {token: '@rematch', next: 'parse_cpp2_expression_list'}],
[
/(\.\.?)(\s*)(@at_cpp2_id_expression)/,
['delimiter', '', {token: '@rematch', next: 'parse_cpp2_id_expression'}],
],
[
/(\.\.[<=])(\s*)/,
['delimiter.range_operator', {token: '', switchTo: 'parse_cpp2_primary_expression.stop_at_postfix'}],
],
[
/./,
{
cases: {
'$S2==stop_at_postfix': {token: '@rematch', next: '@pop'},
'@': {
token: '@rematch',
switchTo:
'parse_cpp2_is_as_expression_target.parse_cpp2_expression.' +
'parse_cpp2_binary_expression_tail.$S2.$S3',
},
},
},
],
];
cppfront.at_cpp2_prefix_expression = /@at_cpp2_prefix_operator_delimiter|@at_cpp2_primary_expression/;
cppfront.at_cpp2_parameter_direction = /(?:in(?:_ref)?|inout|copy|out|move|forward(?:_ref)?)\b/;
cppfront.at_cpp2_prefix_operator_delimiter = /!|-|\+/;
cppfront.tokenizer.parse_cpp2_prefix_expression = [
{include: '@whitespace'},
[
/@at_cpp2_parameter_direction(?=\s*@at_cpp2_primary_expression)/,
'keyword.parameter-direction.prefix-operator',
],
[/@at_cpp2_prefix_operator_delimiter/, 'delimiter.prefix-operator'],
[/@at_cpp2_primary_expression/, {token: '@rematch', switchTo: 'parse_cpp2_primary_expression.$S2.$S3'}],
[/./, 'invalid', '@pop'],
];
cppfront.tokenizer.parse_cpp2_is_as_expression_target = [
// `@$S2` is the expression parser.
// `@$S3.$S4.$S5` is the continuation parser.
{include: '@whitespace'},
[
/(@at_cpp2_is_as_operator)(\s+)(@at_cpp2_type_id)/,
['keyword', '', {token: '@rematch', switchTo: 'parse_cpp2_type_id'}],
],
[/(is\b)(\s*)(@at_cpp2_expression)/, ['keyword', '', {token: '@rematch', switchTo: '@$S2'}]],
[/./, {token: '@rematch', switchTo: '@$S3.$S4.$S5'}],
];
cppfront.at_cpp2_logical_or_operator = /\*|\/|%|\+|-|<<|>>|<=>|<|>|<=|>=|==|!=|&|\^|\||&&|\|\|/;
cppfront.at_cpp2_assignment_operator = /=|\*=|\/=|%=|\+=|-=|>>=|<<=|&=|\^=|\|=/;
cppfront.tokenizer.parse_cpp2_binary_expression_tail = [
[/./, {token: '@rematch', switchTo: 'parse_cpp2_binary_expression_tail_preface.$S2.$S3'}],
];
cppfront.tokenizer.parse_cpp2_binary_expression_tail_preface = [
{include: '@whitespace'},
[
/>>?(?!=)/,
{
cases: {
'$S3==template_argument': {token: '@rematch', next: '@pop'},
'@': {token: '@rematch', switchTo: '@parse_cpp2_binary_expression_tail_body.$S2.$S3'},
},
},
],
[/./, {token: '@rematch', switchTo: '@parse_cpp2_binary_expression_tail_body.$S2.$S3'}],
];
cppfront.tokenizer.parse_cpp2_binary_expression_tail_body = [
{include: '@whitespace'},
[
/(@at_cpp2_logical_or_operator)(\s*)(@at_cpp2_prefix_expression)/,
['delimiter', '', {token: '@rematch', next: 'parse_cpp2_prefix_expression.$S2.$S3'}],
],
[
/@at_cpp2_assignment_operator/,
{
cases: {
'$S2==assignment': {token: 'delimiter', switchTo: 'parse_cpp2_prefix_expression.$S2.$S3'},
'@': {token: '@rematch', next: '@pop'},
},
},
],
[/./, '@rematch', '@pop'],
];
cppfront.tokenizer.parse_cpp2_logical_or_expression = [
[/./, {token: '@rematch', switchTo: 'parse_cpp2_prefix_expression'}],
];
cppfront.tokenizer.parse_cpp2_assignment_expression = [
[/./, {token: '@rematch', switchTo: 'parse_cpp2_prefix_expression.assignment.$S2'}],
];
cppfront.at_cpp2_expression = cppfront.at_cpp2_prefix_expression;
cppfront.tokenizer.parse_cpp2_expression = [
[/./, {token: '@rematch', switchTo: 'parse_cpp2_assignment_expression.$S2'}],
];
}
setupExpressionParsers();
function setupStatementParsers() {
cppfront.tokenizer.parse_cpp2_selection_statement = [
{include: '@whitespace'},
[
/(if)(\s+)(constexpr\b)/,
['keyword.if', '', {token: 'keyword.constexpr', next: 'parse_cpp2_logical_or_expression'}],
],
[/if\b/, 'keyword.if', 'parse_cpp2_logical_or_expression'],
[/{/, '@rematch', 'parse_cpp2_compound_statement'],
[/else\b/, 'keyword.else'],
[/./, '@rematch', '@pop'],
];
cppfront.tokenizer.parse_cpp2_using_statement = [
{include: '@whitespace'},
[/using\b/, {token: 'keyword.using', switchTo: 'parse_cpp2_id_expression.using_statement'}],
];
cppfront.tokenizer.parse_cpp2_alternative = [
{include: '@whitespace'},
[
/@at_cpp2_is_as_operator/,
'@rematch',
'parse_cpp2_is_as_expression_target.parse_cpp2_logical_or_expression.pop',
],
[/@at_cpp2_non_operator_identifier/, '@rematch', 'parse_cpp2_identifier.definition'],
[/@at_cpp2_unnamed_declaration_head/, 'identifier.definition'],
[/=/, {token: 'delimiter', switchTo: 'parse_cpp2_statement'}],
[/./, 'invalid', '@pop'],
];
cppfront.tokenizer.parse_cpp2_inspect_expression = [
{include: '@whitespace'},
[
/(inspect)(\s+)(constexpr\b)/,
['keyword.inspect', '', {token: 'keyword.constexpr', next: 'parse_cpp2_expression'}],
],
[/inspect\b/, 'keyword.inspect', 'parse_cpp2_expression'],
[/->/, '@rematch', 'parse_cpp2_return_list'],
[
/{/,
{token: '@rematch', switchTo: 'parse_cpp2_balanced_curlies.parse_cpp2_until.}.parse_cpp2_alternative'},
],
[/./, 'invalid', '@pop'],
];
cppfront.tokenizer.parse_cpp2_return_statement = [
[/(return)(\s*)(;)/, ['keyword.return', '', {token: 'delimiter', next: '@pop'}]],
[/return\b/, {token: 'keyword.return', switchTo: 'parse_cpp2_expression_statement'}],
];
cppfront.at_cpp2_jump_statement = /(?:break|continue)\b/;
cppfront.tokenizer.parse_cpp2_jump_statement = [
{include: '@whitespace'},
[/@at_cpp2_jump_statement/, {token: 'keyword.$0'}],
[/@at_cpp2_non_operator_identifier/, '@rematch', 'parse_cpp2_identifier'],
[/;/, 'delimiter', '@pop'],
];
cppfront.tokenizer.parse_cpp2_next_clause = [[/next\b/, 'keyword.next', 'parse_cpp2_assignment_expression']];
cppfront.tokenizer.parse_cpp2_while_statement = [
{include: '@whitespace'},
[/while\b/, 'keyword.while', 'parse_cpp2_logical_or_expression'],
{include: '@parse_cpp2_next_clause'},
[/{/, {token: '@rematch', switchTo: 'parse_cpp2_compound_statement'}],
];
cppfront.tokenizer.parse_cpp2_do_statement = [
{include: '@whitespace'},
[/do\b/, 'keyword.do', 'parse_cpp2_compound_statement'],
[/while\b/, 'keyword.while', 'parse_cpp2_logical_or_expression'],
{include: '@parse_cpp2_next_clause'},
[/;/, 'delimiter', '@pop'],
];
cppfront.tokenizer.parse_cpp2_for_statement = [
{include: '@whitespace'},
[/for\b/, 'keyword.for', 'parse_cpp2_expression'],
{include: '@parse_cpp2_next_clause'},
[/(do\b)(\s*)/, ['keyword.do', {token: '', switchTo: 'parse_cpp2_parameterized_statement'}]],
];
cppfront.at_cpp2_iteration_statement_head = /(?:while|do|for)\b/;
cppfront.tokenizer.parse_cpp2_iteration_statement = [
[/while\b/, {token: '@rematch', switchTo: 'parse_cpp2_while_statement'}],
[/do\b/, {token: '@rematch', switchTo: 'parse_cpp2_do_statement'}],
[/for\b/, {token: '@rematch', switchTo: 'parse_cpp2_for_statement'}],
];
cppfront.tokenizer.parse_cpp2_compound_statement = [
[/{/, {token: '@rematch', switchTo: 'parse_cpp2_balanced_curlies.parse_cpp2_until.}.parse_cpp2_statement'}],
];
cppfront.at_cpp2_parameterized_statement =
/\(\s*(?:@at_cpp2_parameter_direction\s+)?@at_cpp2_identifier_definition/;
cppfront.tokenizer.parse_cpp2_parameterized_statement = [
[/\(/, '@rematch', 'parse_cpp2_parameter_declaration_list'],
[/./, {token: '@rematch', switchTo: 'parse_cpp2_statement'}],
];
cppfront.tokenizer.parse_cpp2_expression_statement = [
{include: '@whitespace'},
[/@at_cpp2_expression/, '@rematch', 'parse_cpp2_expression'],
// Handle optional `;` after unbraced statement of unnamed declaration:
[/;(?=\s*(?:[()\],]|@at_cpp2_is_as_operator))/, 'delimiter', '@pop'],
[
/;/,
{
cases: {
'$S2==expression': {token: '@rematch', next: '@pop'},
'@': {token: 'delimiter', next: '@pop'},
},
},
],
[
/./,
{
cases: {
'$S2~expression|parameter': {token: '@rematch', next: '@pop'},
'@': {token: 'invalid', next: '@pop'},
},
},
],
];
cppfront.at_cpp2_contract_kind = /(?:pre|post|assert)\b/;
cppfront.tokenizer.parse_cpp2_contract = [
[/@at_cpp2_contract_kind/, '@rematch', 'parse_cpp2_id_expression.contract_kind'],
[/\(/, {token: '@rematch', switchTo: 'parse_cpp2_expression_list'}],
[/./, '@rematch', '@pop'],
];
cppfront.tokenizer.parse_cpp2_statement = [
{include: '@whitespace'},
[/if\b/, {token: '@rematch', switchTo: 'parse_cpp2_selection_statement'}],
[/using\b/, {token: '@rematch', switchTo: 'parse_cpp2_using_statement'}],
[/inspect\b/, {token: '@rematch', switchTo: 'parse_cpp2_inspect_expression'}],
[/return\b/, {token: '@rematch', switchTo: 'parse_cpp2_return_statement'}],
[/@at_cpp2_jump_statement/, {token: '@rematch', switchTo: 'parse_cpp2_jump_statement'}],
[/@at_cpp2_iteration_statement_head/, {token: '@rematch', switchTo: 'parse_cpp2_iteration_statement'}],
[/{/, {token: '@rematch', switchTo: 'parse_cpp2_compound_statement'}],
[/@at_cpp2_declaration_head/, {token: '@rematch', switchTo: 'parse_cpp2_declaration.definition'}],
[/@at_cpp2_parameterized_statement/, {token: '@rematch', switchTo: 'parse_cpp2_parameterized_statement'}],
[/@at_cpp2_contract_kind/, {token: '@rematch', switchTo: 'parse_cpp2_contract'}],
[/./, {token: '@rematch', switchTo: 'parse_cpp2_expression_statement.$S2'}],
];
}
setupStatementParsers();
function setupDeclarationParsers() {
cppfront.at_cpp2_builtin_meta_function =
/(?:ordered|weakly_ordered|partially_ordered|copyable|basic_value|value|weakly_ordered_value|partially_ordered_value|struct|hashable|interface|polymorphic_base|enum|flag_enum|union|noisy|regex|cpp1_rule_of_zero|print)\b/;
cppfront.tokenizer.parse_cpp2_meta_functions_list = [
[/[^@]/, '@rematch', '@pop'],
[
/(@)(\s*)(@at_cpp2_builtin_meta_function)/,
['delimiter', '', {token: '@rematch', next: 'parse_cpp2_id_expression.builtin_meta_function'}],
],
[
/(@)(\s*)(@at_cpp2_non_operator_id_expression)/,
['delimiter', '', {token: '@rematch', next: 'parse_cpp2_id_expression'}],
],
];
cppfront.at_cpp2_parameter_declaration = /@at_cpp2_non_operator_identifier|@at_cpp2_unnamed_declaration_head/;
cppfront.at_cpp2_this_specifier = /(?:implicit|virtual|override|final)\b/;
cppfront.tokenizer.parse_cpp2_parameter_declaration = [
[
/(@at_cpp2_parameter_direction)(\s*)(@at_cpp2_unnamed_declaration_head)/,
['keyword.parameter-direction', '', {token: '@rematch', switchTo: 'parse_cpp2_declaration.parameter'}],
],
[
/@at_cpp2_declaration_head|@at_cpp2_unnamed_declaration_head/,
{token: '@rematch', switchTo: 'parse_cpp2_declaration.parameter.$S2'},
],
[
/(@at_cpp2_parameter_direction)(\s+)(@at_cpp2_declaration_head)/,
['keyword.parameter-direction', '', {token: '@rematch', switchTo: 'parse_cpp2_declaration.parameter'}],
],
[
/(@at_cpp2_this_specifier)(\s+)(@at_cpp2_declaration_head)/,
['keyword.this-specifier', '', {token: '@rematch', switchTo: 'parse_cpp2_declaration.parameter'}],
],
[
/(@at_cpp2_this_specifier)(\s+)(@at_cpp2_parameter_direction)(\s+)(@at_cpp2_declaration_head)/,
[
'keyword.this-specifier',
'',
'keyword.parameter-direction',
'',
{token: '@rematch', switchTo: 'parse_cpp2_declaration.parameter'},
],
],
[
/(@at_cpp2_parameter_direction)(\s+)(@at_cpp2_non_operator_identifier)/,
['keyword.parameter-direction', '', {token: '@rematch', switchTo: 'parse_cpp2_identifier.parameter'}],
],
[
/(@at_cpp2_this_specifier)(\s+)(@at_cpp2_parameter_direction)(\s+)(@at_cpp2_non_operator_identifier)/,
[
'keyword.this-specifier',
'',
'keyword.parameter-direction',
'',
{token: '@rematch', switchTo: 'parse_cpp2_identifier.parameter'},
],
],
[
/(@at_cpp2_this_specifier)(\s+)(@at_cpp2_non_operator_identifier)/,
['keyword.this-specifier', '', {token: '@rematch', switchTo: 'parse_cpp2_identifier.parameter'}],
],
[/@at_cpp2_non_operator_identifier/, {token: '@rematch', switchTo: 'parse_cpp2_identifier.parameter'}],
];
cppfront.tokenizer.parse_cpp2_parameter_declaration_seq = parseCommaSeparated([
/@at_cpp2_parameter_declaration/,
'@rematch',
'parse_cpp2_parameter_declaration.$S2',
]);
cppfront.tokenizer.parse_cpp2_parameter_declaration_list = [
[
/./,
{
token: '@rematch',
switchTo: 'parse_cpp2_balanced_punctuators.parse_cpp2_parameter_declaration_seq.$S2',
},
],
];
cppfront.tokenizer.parse_cpp2_return_list = [
[
/(->)(\s*)(@at_cpp2_parameter_direction)(\s+)(@at_cpp2_type_id)/,
[
'delimiter',
'',
'keyword.parameter-direction',
'',
{token: '@rematch', switchTo: 'parse_cpp2_type_id'},
],
],
[/(->)(\s*)(@at_cpp2_type_id)/, ['delimiter', '', {token: '@rematch', switchTo: 'parse_cpp2_type_id'}]],
[/->/, 'invalid', '@pop'],
];
cppfront.tokenizer.parse_cpp2_function_type = [
{include: '@whitespace'},
[/\(/, '@rematch', 'parse_cpp2_parameter_declaration_list'],
[/throws\b/, 'keyword'],
[/->/, '@rematch', 'parse_cpp2_return_list'],
[/@at_cpp2_contract_kind/, '@rematch', 'parse_cpp2_contract'],
[/./, '@rematch', '@pop'],
];
cppfront.tokenizer.parse_cpp2_declaration_initializer = [
[/./, {token: '@rematch', switchTo: 'parse_cpp2_statement.$S2'}],
];
cppfront.tokenizer.parse_cpp2_declaration_signature = [
{include: '@whitespace'},
[/@/, '@rematch', 'parse_cpp2_meta_functions_list'],
[/</, '@rematch', 'parse_cpp2_parameter_declaration_list.template'],
[/\(/, '@rematch', 'parse_cpp2_function_type'],
[/requires\b/, 'keyword', 'parse_cpp2_logical_or_expression'],
[/final\b/, 'keyword'],
[/type\b/, {token: 'keyword', switchTo: 'parse_cpp2_declaration_signature.type.$S3'}],
[/namespace\b/, 'keyword'],
[/@at_cpp2_iteration_statement_head/, {token: '@rematch', switchTo: 'parse_cpp2_iteration_statement'}],
[/@at_cpp2_is_as_operator/, '@rematch', 'parse_cpp2_is_as_expression_target.pop.pop'],
[/@at_cpp2_type_id/, '@rematch', 'parse_cpp2_type_id'],
[
/==/,
{
cases: {
'$S2==type': {token: 'delimiter', next: 'parse_cpp2_type_id'},
'@': {token: 'delimiter', switchTo: 'parse_cpp2_declaration_initializer.$S2'},
},
},
],
[
/=/,
{
cases: {
'$S3==template': {token: 'delimiter', next: 'parse_cpp2_type_id'},
'@': {token: 'delimiter', switchTo: 'parse_cpp2_declaration_initializer.$S2'},
},
},
],
[
/;/,
{
cases: {
'$S2==expression': {token: '@rematch', next: '@pop'},
'@': {token: 'delimiter', next: '@pop'},
},
},
],
[/./, '@rematch', '@pop'],
];
cppfront.at_cpp2_unnamed_declaration_head = /:(?!:)/;
cppfront.at_cpp2_identifier_definition =
/@at_cpp2_identifier\s*(?:\.\.\.)?\s*@at_cpp2_unnamed_declaration_head/;
cppfront.at_cpp2_top_level_declaration_head =
/(?:@at_cpp2_access_specifier\s+)?(?!@at_cpp2_access_specifier)@at_cpp2_identifier_definition/;
cppfront.at_cpp2_declaration_head = /(?:@at_cpp2_access_specifier\s+)?@at_cpp2_identifier_definition/;
cppfront.at_cpp2_access_specifier = /(?:export|public|protected|private)\b/;
cppfront.tokenizer.parse_cpp2_declaration_head = [
{include: '@whitespace'},
[/@at_cpp2_access_specifier/, 'keyword'],
[/guard(?=@at_cpp2_non_operator_identifier?\s*@at_cpp2_unnamed_declaration_head)/, 'keyword'],
[/@at_cpp2_identifier/, '@rematch', 'parse_cpp2_identifier.$S2'],
[/\.\.\./, 'delimiter.ellipsis'],
[
/@at_cpp2_unnamed_declaration_head/,
{token: 'identifier.definition', switchTo: 'parse_cpp2_declaration_signature.$S2.$S3'},
],
];
cppfront.tokenizer.parse_cpp2_declaration = [
[/./, {token: '@rematch', switchTo: 'parse_cpp2_declaration_head.$S2.$S3'}],
];
}
setupDeclarationParsers();
// To not parse a Cpp1 label as a Cpp2 declaration, within a Cpp1 block, don't parse Cpp2.
cppfront.tokenizer.root.unshift(
[
/^(\s*)(@at_cpp2_identifier(?=@at_cpp2_unnamed_declaration_head))/,
{
cases: {
'$S2!=cpp1': ['', {token: '@rematch', next: 'parse_cpp2_declaration.definition'}],
'$2~(public|protected|private|default)': ['', {token: 'keyword.$2'}],
'@': ['', 'identifier'],
},
},
],
[
/^\s*@at_cpp2_top_level_declaration_head/,
{
cases: {
'$S2!=cpp1': {token: '@rematch', next: 'parse_cpp2_declaration.definition'},
'@': 'label.cpp1',
},
},
],
[/{/, 'delimiter.curly', 'root.cpp1'],
[/}/, 'delimiter.curly', 'root'],
);
return cppfront;
// [Monaco's C++]: https://github.com/microsoft/monaco-editor/blob/main/src/basic-languages/cpp/cpp.ts
}
monaco.languages.register({id: 'cpp2-cppfront'});
monaco.languages.setLanguageConfiguration('cpp2-cppfront', cpp.conf);
monaco.languages.setMonarchTokensProvider('cpp2-cppfront', definition());