// 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 * as 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. // - 'identifier.definition' // - 'identifier.use' // - 'keyword.identifier.definition' // - 'keyword.identifier.use' // Declaration/use of an _identifier_. // - 'type.contextual' // - 'keyword.type.contextual' // Identifier that is contextually a type. // - 'keyword.contract-kind' // - 'keyword.this-specifier' // - 'keyword.parameter-direction' // _contract-kind_/_this-specifier_/_parameter-direction_. // Generic parsers. function parseCpp2Balanced(delimiters, delimiter, opener, closer) { 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.$S3` is the parser. [/|<=|<|>>=|>>|>=|>|\+\+|\+=|\+|--|-=|->|-|/.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'}, }, }, ], [ /_\b/, { cases: { '$S2==definition': {token: 'keyword.identifier.definition', next: '@pop'}, // Anonymous definition. '@': {token: '@rematch', switchTo: 'parse_cpp2_non_operator_identifier.$S2'}, }, }, ], [ /@at_cpp2_contract_kind/, { cases: { '$S2==contract_kind': {token: 'keyword.contract-kind', 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'}, '@': {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_type_qualifier = /const\b|\*/; cppfront.tokenizer.parse_cpp2_type_qualifier_seq = [ {include: '@whitespace'}, [/const\b/, 'keyword'], [/\*/, 'delimiter'], [/./, '@rematch', '@pop'], ]; // Curated list to highlight a subset of keyword-types as a keyword. cppfront.at_cpp2_keyword_type = /(?:[iu](?:8|16|32|64)|void|bool|char|double|float|longdouble)\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|_\b/, 'keyword.type.contextual', '@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_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_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', ]); cppfront.tokenizer.parse_cpp2_template_argument_list = [ [/>|<=>|<|>|<=|>=|==|!=|&|\^|\||&&|\|\|/; 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/, 'keyword.using'], [/namespace\b/, 'keyword.namespace'], [/./, {token: '@rematch', switchTo: 'parse_cpp2_id_expression'}], ]; 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.tokenizer.parse_cpp2_meta_functions_list = [ [/[^@]/, '@rematch', '@pop'], [ /(@)(\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'}, ], [ /(@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', ]); cppfront.tokenizer.parse_cpp2_parameter_declaration_list = [ [ /./, {token: '@rematch', switchTo: 'parse_cpp2_balanced_punctuators.parse_cpp2_parameter_declaration_seq'}, ], ]; 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_full_function_type = [ {include: '@whitespace'}, [/throws\b/, 'keyword'], [/->/, '@rematch', 'parse_cpp2_return_list'], [/@at_cpp2_contract_kind/, '@rematch', 'parse_cpp2_contract'], [/./, '@rematch', '@pop'], ]; cppfront.at_cpp2_function_type_id_tail = /throws\b|->|@at_cpp2_contract_kind/; cppfront.tokenizer.parse_cpp2_terse_function = [ {include: '@whitespace'}, [/\(/, '@rematch', 'parse_cpp2_parameter_declaration_list'], [/@at_cpp2_function_type_id_tail/, {token: '@rematch', switchTo: 'parse_cpp2_full_function_type'}], [/requires\b|==?|;/, '@rematch', '@pop'], [/@at_cpp2_expression/, {token: '@rematch', switchTo: 'parse_cpp2_expression'}], [/./, '@rematch', '@pop'], ]; cppfront.tokenizer.parse_cpp2_function_type = [ [/./, {token: '@rematch', switchTo: 'parse_cpp2_terse_function'}], ]; 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'], [/