mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-03 22:02:51 +01:00
We've talked about adding the title to the filename several times over the years and it seems really valuable. This requires us to compute a "slug" for the title spelling that can be part of the filename. Beyond that, we crossed 7000 recently, and so it seems likely that we will need to add digits sooner rather than later here, so this goes ahead and moves us to 6 digits so we don't have to adjust again for a reasonable length of time. To implement this and ensure we can sustain it going forward this adds a tool to our pre-commit that validates (and corrects if needed) the filename. In order to update everything and keep links working, there are a _lot_ of changes, but the most interesting for direct review are in `proposals/scripts`. Assisted-by: Antigravity with Gemini --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk>
532 lines
14 KiB
JavaScript
532 lines
14 KiB
JavaScript
/*
|
|
* 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
|
|
*/
|
|
|
|
// This grammar is more permissive than toolchain because it is geared towards
|
|
// editor use.
|
|
|
|
function repeat_sep1(thing, sep) {
|
|
return seq(thing, repeat(seq(sep, thing)));
|
|
}
|
|
|
|
function comma_sep(thing) {
|
|
// Trailing comma is only allowed if there is at least one element.
|
|
return optional(seq(repeat_sep1(thing, ','), optional(',')));
|
|
}
|
|
|
|
// This is based on toolchain/parser/precedence.cpp
|
|
const PREC = {
|
|
TermPrefix: 12,
|
|
TermPostfix: 12,
|
|
NumericPrefix: 11,
|
|
NumericPostfix: 11,
|
|
Multiplicative: 10,
|
|
Additive: 9,
|
|
BitwisePrefix: 8,
|
|
BitwiseAnd: 7,
|
|
BitwiseOr: 7,
|
|
BitwiseXor: 7,
|
|
BitShift: 7,
|
|
TypePostfix: 6,
|
|
LogicalPrefix: 5,
|
|
Relational: 4,
|
|
LogicalAnd: 3,
|
|
LogicalOr: 3,
|
|
IfExpression: 2,
|
|
WhereClause: 1,
|
|
};
|
|
|
|
module.exports = grammar({
|
|
name: 'carbon',
|
|
|
|
word: ($) => $.ident,
|
|
|
|
conflicts: ($) => [
|
|
[$.paren_pattern, $.paren_expression],
|
|
[$.struct_literal, $.struct_type_literal],
|
|
[$._pattern_without_expression, $._simple_expression],
|
|
],
|
|
|
|
extras: ($) => [/\s/, $.comment],
|
|
|
|
// NOTE: This must match the order in src/scanner.c, names are not used for matching.
|
|
externals: ($) => [$.binary_star, $.postfix_star, $.string],
|
|
|
|
rules: {
|
|
source_file: ($) =>
|
|
seq(
|
|
optional($.package_directive),
|
|
repeat($.import_directive),
|
|
repeat($.declaration)
|
|
),
|
|
|
|
api_or_impl: ($) => choice('api', 'impl'),
|
|
|
|
library_path: ($) => seq('library', $.string),
|
|
|
|
package_directive: ($) =>
|
|
seq('package', $.ident, optional($.library_path), $.api_or_impl, ';'),
|
|
|
|
import_directive: ($) =>
|
|
seq('import', $.ident, optional($.library_path), ';'),
|
|
|
|
comment: ($) => token(seq('//', /.*/)),
|
|
|
|
// NOTE: this must be before ident rule to increase its priority.
|
|
// https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p002015.md#syntax
|
|
numeric_type_literal: ($) => /[iuf][1-9][0-9]*/,
|
|
|
|
ident: ($) => /[A-Za-z_][A-Za-z0-9_]*/,
|
|
|
|
bool_literal: ($) => choice('true', 'false'),
|
|
|
|
numeric_literal: ($) => {
|
|
// This is using variables because rules are not allowed in
|
|
// token.immediate and token.
|
|
// https://github.com/tree-sitter/tree-sitter/issues/449
|
|
const decimal_integer_literal = choice('0', /[1-9](_?[0-9])*/);
|
|
const hex_digits = /[0-9A-F](_?[0-9A-F])*/;
|
|
const binary_integer_literal = /0b[01](_?[01])*/;
|
|
const hex_integer_literal = seq('0x', token.immediate(hex_digits));
|
|
|
|
const decimal_real_number_literal = seq(
|
|
decimal_integer_literal,
|
|
token.immediate(/\.[0-9](_?[0-9])*/),
|
|
optional(
|
|
seq(
|
|
token.immediate(/e[+-]?/),
|
|
token.immediate(decimal_integer_literal)
|
|
)
|
|
)
|
|
);
|
|
|
|
const hex_real_number_literal = seq(
|
|
hex_integer_literal,
|
|
token.immediate('.'),
|
|
token.immediate(hex_digits),
|
|
optional(
|
|
seq(
|
|
token.immediate(/p[+-]?/),
|
|
token.immediate(decimal_integer_literal)
|
|
)
|
|
)
|
|
);
|
|
|
|
return token(
|
|
choice(
|
|
decimal_integer_literal,
|
|
binary_integer_literal,
|
|
hex_integer_literal,
|
|
decimal_real_number_literal,
|
|
hex_real_number_literal
|
|
)
|
|
);
|
|
},
|
|
|
|
array_literal: ($) =>
|
|
seq(
|
|
'[',
|
|
field('type', $._expression),
|
|
';',
|
|
optional(field('size', $._expression)),
|
|
']'
|
|
),
|
|
|
|
struct_literal: ($) =>
|
|
seq('{', comma_sep(seq($.designator, '=', $._expression)), '}'),
|
|
|
|
struct_type_literal: ($) =>
|
|
seq('{', comma_sep(seq($.designator, ':', $._expression)), '}'),
|
|
|
|
builtin_type: ($) => choice('Self', 'String', 'bool', 'type'),
|
|
|
|
literal: ($) =>
|
|
choice(
|
|
$.bool_literal,
|
|
$.numeric_literal,
|
|
$.numeric_type_literal,
|
|
$.string,
|
|
$.struct_literal,
|
|
$.struct_type_literal
|
|
),
|
|
|
|
binding_lhs: ($) => choice($.ident, '_'),
|
|
|
|
paren_pattern: ($) =>
|
|
seq(
|
|
'(',
|
|
comma_sep(choice($._pattern_without_expression, $._expression)),
|
|
')'
|
|
),
|
|
|
|
_pattern_without_expression: ($) =>
|
|
choice(
|
|
'auto',
|
|
seq(optional('ref'), 'self', optional(seq(':', $._expression))),
|
|
seq($.binding_lhs, ':', $._expression),
|
|
seq($.binding_lhs, ':!', $._expression),
|
|
seq('template', $.binding_lhs, ':!', $._expression),
|
|
seq('var', $._pattern),
|
|
$.paren_pattern,
|
|
// alternative patterns
|
|
// example: Optional(i32).Some(x: i32)
|
|
seq($._simple_expression, $.paren_pattern)
|
|
),
|
|
|
|
_pattern: ($) =>
|
|
choice($._pattern_without_expression, $._simple_expression),
|
|
|
|
unary_prefix_expression: ($) => {
|
|
const table = [
|
|
[PREC.NumericPrefix, '-'],
|
|
[PREC.NumericPrefix, '--'],
|
|
[PREC.NumericPrefix, '++'],
|
|
[PREC.BitwisePrefix, '^'],
|
|
[PREC.LogicalPrefix, 'not'],
|
|
];
|
|
|
|
return choice(
|
|
...table.map(([precedence, operator]) =>
|
|
prec(
|
|
precedence,
|
|
seq(field('operator', operator), field('value', $._expression))
|
|
)
|
|
)
|
|
);
|
|
},
|
|
|
|
binary_expression: ($) => {
|
|
const table = [
|
|
[PREC.LogicalAnd, 'and'],
|
|
[PREC.LogicalOr, 'or'],
|
|
[PREC.BitwiseAnd, '&'],
|
|
[PREC.BitwiseOr, '|'],
|
|
[PREC.BitwiseXor, '^'],
|
|
[PREC.BitShift, choice('<<', '>>')],
|
|
[PREC.Relational, choice('==', '!=', '<', '<=', '>', '>=')],
|
|
[PREC.Additive, choice('+', '-')],
|
|
[PREC.Multiplicative, choice($.binary_star, '/', '%')],
|
|
];
|
|
|
|
return choice(
|
|
...table.map(([precedence, operator]) =>
|
|
prec.left(
|
|
precedence,
|
|
seq(
|
|
field('left', $._expression),
|
|
field('operator', operator),
|
|
field('right', $._expression)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
},
|
|
|
|
// This should be non-associative but conflicts are not allowed in tree-sitter
|
|
as_expression: ($) => prec.left(seq($._expression, 'as', $._expression)),
|
|
|
|
ref_expression: ($) =>
|
|
prec.right(PREC.TermPrefix, seq('&', $._simple_expression)),
|
|
|
|
deref_expression: ($) =>
|
|
prec.right(PREC.TermPrefix, seq('*', $._simple_expression)),
|
|
|
|
fn_type_expression: ($) =>
|
|
prec.left(seq('__Fn', $.paren_expression, '->', $._simple_expression)),
|
|
|
|
if_expression: ($) =>
|
|
prec(
|
|
PREC.IfExpression,
|
|
seq('if', $._expression, 'then', $._expression, 'else', $._expression)
|
|
),
|
|
|
|
paren_expression: ($) => seq('(', comma_sep($._expression), ')'),
|
|
|
|
index_expression: ($) =>
|
|
prec(
|
|
PREC.TermPostfix,
|
|
seq($._simple_expression, '[', $._expression, ']')
|
|
),
|
|
|
|
designator: ($) => seq('.', choice('base', $.ident)),
|
|
|
|
postfix_expression: ($) =>
|
|
prec(
|
|
PREC.TermPostfix,
|
|
seq(
|
|
$._simple_expression,
|
|
choice(
|
|
'++',
|
|
'--',
|
|
$.designator,
|
|
seq('->', $.ident),
|
|
seq(choice('.', '->'), '(', $._expression, ')')
|
|
)
|
|
)
|
|
),
|
|
|
|
where_clause: ($) =>
|
|
choice(
|
|
seq($._simple_expression, '==', $._simple_expression),
|
|
seq($._simple_expression, 'impls', $._simple_expression),
|
|
seq($._simple_expression, '=', $._simple_expression),
|
|
prec.left(
|
|
PREC.WhereClause + 1,
|
|
seq($.where_clause, 'and', $.where_clause)
|
|
)
|
|
),
|
|
|
|
where_expression: ($) =>
|
|
prec.left(PREC.WhereClause, seq($._expression, 'where', $.where_clause)),
|
|
|
|
call_expression: ($) =>
|
|
prec(PREC.TermPostfix, seq($._simple_expression, $.paren_expression)),
|
|
|
|
pointer_expression: ($) =>
|
|
prec(PREC.TypePostfix, seq($._simple_expression, $.postfix_star)),
|
|
|
|
_simple_expression: ($) =>
|
|
choice(
|
|
$.array_literal,
|
|
$.builtin_type,
|
|
$.call_expression,
|
|
$.deref_expression,
|
|
$.fn_type_expression,
|
|
$.ident,
|
|
$.index_expression,
|
|
$.literal,
|
|
$.paren_expression,
|
|
$.pointer_expression,
|
|
$.postfix_expression,
|
|
$.ref_expression,
|
|
'self',
|
|
'.Self',
|
|
$.designator
|
|
),
|
|
|
|
_expression: ($) =>
|
|
choice(
|
|
$.as_expression,
|
|
$.binary_expression,
|
|
$.if_expression,
|
|
$.unary_prefix_expression,
|
|
$.where_expression,
|
|
$._simple_expression
|
|
),
|
|
|
|
var_declaration: ($) =>
|
|
seq(
|
|
seq(optional('static'), 'var'),
|
|
$._pattern_without_expression,
|
|
optional(seq('=', $._expression)),
|
|
';'
|
|
),
|
|
|
|
let_declaration: ($) =>
|
|
seq('let', $._pattern_without_expression, '=', $._expression, ';'),
|
|
|
|
assign_statement: ($) =>
|
|
seq($._expression, $._assign_operator, $._expression, ';'),
|
|
|
|
_assign_operator: ($) =>
|
|
choice('=', '+=', '/=', '*=', '%=', '-=', '&=', '|=', '^=', '<<=', '>>='),
|
|
|
|
match_clause: ($) =>
|
|
seq(choice(seq('case', $._pattern), 'default'), '=>', $.block),
|
|
|
|
match_statement: ($) =>
|
|
seq('match', '(', $._expression, ')', '{', repeat($.match_clause), '}'),
|
|
|
|
returned_var_statement: ($) => seq('returned', $.var_declaration),
|
|
|
|
while_statement: ($) => seq('while', '(', $._expression, ')', $.block),
|
|
|
|
break_statement: ($) => seq('break', ';'),
|
|
|
|
continue_statement: ($) => seq('continue', ';'),
|
|
|
|
return_statement: ($) =>
|
|
seq('return', optional(choice('var', $._expression)), ';'),
|
|
|
|
if_statement: ($) =>
|
|
seq('if', '(', $._expression, ')', $.block, optional($.else)),
|
|
|
|
else: ($) => choice(seq('else', $.if_statement), seq('else', $.block)),
|
|
|
|
for_statement: ($) =>
|
|
seq('for', '(', $._pattern, 'in', $._expression, ')', $.block),
|
|
|
|
statement: ($) =>
|
|
choice(
|
|
seq($._expression, ';'),
|
|
$.assign_statement,
|
|
$.var_declaration,
|
|
$.let_declaration,
|
|
$.match_statement,
|
|
$.returned_var_statement,
|
|
$.if_statement,
|
|
$.while_statement,
|
|
$.break_statement,
|
|
$.continue_statement,
|
|
$.return_statement,
|
|
$.for_statement
|
|
),
|
|
|
|
block: ($) => seq('{', repeat($.statement), '}'),
|
|
|
|
declared_name: ($) => repeat_sep1($.ident, '.'),
|
|
|
|
generic_binding: ($) =>
|
|
seq(optional('template'), $.ident, ':!', $._expression),
|
|
|
|
deduced_param: ($) => $.generic_binding,
|
|
|
|
deduced_params: ($) => seq('[', comma_sep($.deduced_param), ']'),
|
|
|
|
return_type: ($) => seq('->', choice('auto', $._expression)),
|
|
|
|
function_declaration: ($) =>
|
|
seq(
|
|
optional(choice('abstract', 'virtual', 'impl')),
|
|
'fn',
|
|
$.declared_name,
|
|
optional($.deduced_params),
|
|
$.paren_pattern,
|
|
optional($.return_type),
|
|
choice($.block, ';')
|
|
),
|
|
|
|
namespace_declaration: ($) => seq('namespace', $.declared_name, ';'),
|
|
|
|
alias_declaration: ($) =>
|
|
seq('alias', $.declared_name, '=', $._expression, ';'),
|
|
|
|
type_params: ($) => $.paren_pattern,
|
|
|
|
interface_body_item: ($) =>
|
|
choice(
|
|
$.function_declaration,
|
|
seq('let', $.generic_binding, ';'),
|
|
seq('extend', $._expression, ';'),
|
|
seq('require', $._expression, 'impls', $._expression, ';')
|
|
),
|
|
|
|
interface_body: ($) => seq('{', repeat($.interface_body_item), '}'),
|
|
|
|
interface_declaration: ($) =>
|
|
seq(
|
|
'interface',
|
|
$.declared_name,
|
|
optional($.deduced_params),
|
|
optional($.type_params),
|
|
choice(';', $.interface_body)
|
|
),
|
|
|
|
constraint_declaration: ($) =>
|
|
seq(
|
|
'constraint',
|
|
$.declared_name,
|
|
optional($.deduced_params),
|
|
optional($.type_params),
|
|
choice(';', $.interface_body)
|
|
),
|
|
|
|
impl_body_item: ($) => choice($.function_declaration, $.alias_declaration),
|
|
|
|
impl_body: ($) => seq('{', repeat($.impl_body_item), '}'),
|
|
|
|
impl_declaration: ($) =>
|
|
seq(
|
|
'impl',
|
|
optional(seq('forall', $.deduced_params)),
|
|
optional($._expression),
|
|
'as',
|
|
$._expression,
|
|
$.impl_body
|
|
),
|
|
|
|
extend_impl_declaration: ($) =>
|
|
seq('extend', 'impl', 'as', $._expression, $.impl_body),
|
|
|
|
extend_base_declaration: ($) =>
|
|
seq('extend', 'base', ':', $._expression, ';'),
|
|
|
|
destructor_declaration: ($) =>
|
|
seq(
|
|
optional(choice('virtual', 'impl')),
|
|
'destructor',
|
|
optional($.deduced_params),
|
|
choice($.block, ';')
|
|
),
|
|
|
|
class_body_item: ($) =>
|
|
choice(
|
|
$.declaration,
|
|
$.extend_base_declaration,
|
|
$.extend_impl_declaration,
|
|
$.mix_declaration,
|
|
$.destructor_declaration
|
|
),
|
|
|
|
class_body: ($) => seq('{', repeat($.class_body_item), '}'),
|
|
|
|
class_declaration: ($) =>
|
|
seq(
|
|
optional(choice('base', 'abstract')),
|
|
'class',
|
|
$.declared_name,
|
|
optional($.deduced_params),
|
|
optional($.type_params),
|
|
choice(';', $.class_body)
|
|
),
|
|
|
|
choice_declaration: ($) =>
|
|
seq(
|
|
'choice',
|
|
$.declared_name,
|
|
optional($.type_params),
|
|
'{',
|
|
comma_sep(seq($.ident, optional($.paren_expression))),
|
|
'}'
|
|
),
|
|
|
|
empty_declaration: ($) => ';',
|
|
|
|
declaration: ($) =>
|
|
choice(
|
|
$.empty_declaration,
|
|
$.namespace_declaration,
|
|
$.var_declaration,
|
|
$.let_declaration,
|
|
$.function_declaration,
|
|
$.alias_declaration,
|
|
$.interface_declaration,
|
|
$.constraint_declaration,
|
|
$.impl_declaration,
|
|
$.class_declaration,
|
|
$.choice_declaration,
|
|
$.mixin_declaration,
|
|
$.match_first_declaration
|
|
),
|
|
|
|
// Explorer only experimental featurues
|
|
mix_declaration: ($) => seq('__mix', $._expression, ';'),
|
|
|
|
mixin_declaration: ($) =>
|
|
seq(
|
|
'__mixin',
|
|
$.declared_name,
|
|
optional($.type_params),
|
|
optional(seq('for', $._expression)),
|
|
'{',
|
|
repeat(choice($.function_declaration, $.mix_declaration)),
|
|
'}'
|
|
),
|
|
|
|
match_first_declaration: ($) =>
|
|
seq('__match_first', '{', repeat($.impl_declaration), '}'),
|
|
},
|
|
});
|