mirror of https://github.com/Wilfred/difftastic/
597 lines
11 KiB
JavaScript
597 lines
11 KiB
JavaScript
const PREC = {
|
|
assign: 1,
|
|
infix: 2,
|
|
new: 3,
|
|
prefix: 3,
|
|
call: 4,
|
|
field: 4,
|
|
}
|
|
|
|
module.exports = grammar({
|
|
name: 'scala',
|
|
|
|
extras: $ => [
|
|
/\s/,
|
|
$.comment
|
|
],
|
|
|
|
externals: $ => [
|
|
$._automatic_semicolon,
|
|
$._simple_string,
|
|
$._string_start,
|
|
$._string_middle,
|
|
$._string_end,
|
|
$._multiline_string_start,
|
|
$._multiline_string_middle,
|
|
$._multiline_string_end,
|
|
'else',
|
|
],
|
|
|
|
inline: $ => [
|
|
$._pattern,
|
|
$._semicolon,
|
|
$._definition,
|
|
$._type_identifier,
|
|
],
|
|
|
|
rules: {
|
|
compilation_unit: $ => repeat($._definition),
|
|
|
|
_definition: $ => choice(
|
|
$.package_clause,
|
|
$.package_object,
|
|
$.class_definition,
|
|
$.import_declaration,
|
|
$.object_definition,
|
|
$.trait_definition,
|
|
$.val_definition,
|
|
$.val_declaration,
|
|
$.var_definition,
|
|
$.var_declaration,
|
|
$.type_definition,
|
|
$.function_definition,
|
|
$.function_declaration
|
|
),
|
|
|
|
package_clause: $ => seq(
|
|
'package',
|
|
$.package_identifier,
|
|
// This is slightly more permissive than the EBNF in that it allows any
|
|
// kind of delcaration inside of the package blocks. As we're more
|
|
// concerned with the structure rather than the validity of the program
|
|
// we'll allow it.
|
|
optional($.template_body)
|
|
),
|
|
|
|
package_identifier: $ => sep1(
|
|
'.', $.identifier
|
|
),
|
|
|
|
package_object: $ => seq(
|
|
'package',
|
|
'object',
|
|
$._object_definition
|
|
),
|
|
|
|
import_declaration: $ => seq(
|
|
'import',
|
|
choice($.stable_identifier, $.identifier),
|
|
optional(seq(
|
|
'.',
|
|
choice(
|
|
$.wildcard,
|
|
$.import_selectors
|
|
)
|
|
))
|
|
),
|
|
|
|
import_selectors: $ => seq(
|
|
'{',
|
|
commaSep1(choice(
|
|
$.identifier,
|
|
$.renamed_identifier
|
|
)),
|
|
'}'
|
|
),
|
|
|
|
renamed_identifier: $ => seq(
|
|
$.identifier,
|
|
'=>',
|
|
choice($.identifier, $.wildcard)
|
|
),
|
|
|
|
object_definition: $ => seq(
|
|
optional('case'),
|
|
'object',
|
|
$._object_definition
|
|
),
|
|
|
|
_object_definition: $ => seq(
|
|
$.identifier,
|
|
optional($.extends_clause),
|
|
optional($.template_body),
|
|
),
|
|
|
|
class_definition: $ => seq(
|
|
optional($.modifiers),
|
|
optional('case'),
|
|
'class',
|
|
$.identifier,
|
|
optional($.type_parameters),
|
|
optional($.class_parameters),
|
|
optional($.extends_clause),
|
|
optional($.template_body)
|
|
),
|
|
|
|
trait_definition: $ => seq(
|
|
'trait',
|
|
$.identifier,
|
|
optional($.type_parameters),
|
|
optional($.extends_clause),
|
|
$.template_body
|
|
),
|
|
|
|
type_parameters: $ => seq(
|
|
'[',
|
|
commaSep1($._type_parameter),
|
|
']'
|
|
),
|
|
|
|
_type_parameter: $ => choice(
|
|
'_',
|
|
$.covariant_type_parameter,
|
|
$.contravariant_type_parameter,
|
|
$.identifier // invariant type parameter
|
|
),
|
|
|
|
covariant_type_parameter: $ => seq(
|
|
'+',
|
|
$.identifier
|
|
),
|
|
|
|
contravariant_type_parameter: $ => seq(
|
|
'-',
|
|
$.identifier
|
|
),
|
|
|
|
template_body: $ => seq(
|
|
'{',
|
|
repeat($._definition),
|
|
'}'
|
|
),
|
|
|
|
val_definition: $ => seq(
|
|
optional($.modifiers),
|
|
'val',
|
|
$._pattern,
|
|
optional(seq(':', $._type)),
|
|
'=',
|
|
$._expression
|
|
),
|
|
|
|
val_declaration: $ => seq(
|
|
optional($.modifiers),
|
|
'val',
|
|
commaSep1($.identifier),
|
|
':',
|
|
$._type
|
|
),
|
|
|
|
var_declaration: $ => seq(
|
|
optional($.modifiers),
|
|
'var',
|
|
commaSep1($.identifier),
|
|
':',
|
|
$._type
|
|
),
|
|
|
|
var_definition: $ => seq(
|
|
optional($.modifiers),
|
|
'var',
|
|
$._pattern,
|
|
optional(seq(':', $._type)),
|
|
'=',
|
|
$._expression
|
|
),
|
|
|
|
type_definition: $ => seq(
|
|
optional($.modifiers),
|
|
'type',
|
|
$._type_identifier,
|
|
optional($.type_parameters),
|
|
'=',
|
|
$._type
|
|
),
|
|
|
|
function_definition: $ => seq(
|
|
optional($.modifiers),
|
|
'def',
|
|
$.identifier,
|
|
optional($.type_parameters),
|
|
optional($.parameters),
|
|
optional(seq(':', $._type)),
|
|
choice(
|
|
seq('=', $._expression),
|
|
$.block
|
|
)
|
|
),
|
|
|
|
function_declaration: $ => seq(
|
|
optional($.modifiers),
|
|
'def',
|
|
$.identifier,
|
|
optional($.type_parameters),
|
|
optional($.parameters),
|
|
optional(seq(':', $._type))
|
|
),
|
|
|
|
modifiers: $ => repeat1(choice(
|
|
'abstract',
|
|
'final',
|
|
'sealed',
|
|
'implicit',
|
|
'lazy',
|
|
'override',
|
|
'private',
|
|
'protected'
|
|
)),
|
|
|
|
extends_clause: $ => seq(
|
|
'extends',
|
|
$._type,
|
|
optional($.arguments)
|
|
),
|
|
|
|
class_parameters: $ => seq(
|
|
'(',
|
|
commaSep($.class_parameter),
|
|
')'
|
|
),
|
|
|
|
parameters: $ => seq(
|
|
'(',
|
|
commaSep($.parameter),
|
|
')'
|
|
),
|
|
|
|
class_parameter: $ => seq(
|
|
optional(choice('val', 'var')),
|
|
$.identifier,
|
|
optional(seq(':', $._type)),
|
|
optional(seq('=', $._expression))
|
|
),
|
|
|
|
parameter: $ => seq(
|
|
$.identifier,
|
|
optional(seq(':', choice($.lazy_parameter_type, $._type))),
|
|
optional(seq('=', $._expression))
|
|
),
|
|
|
|
lazy_parameter_type: $ => seq(
|
|
'=>',
|
|
$._type
|
|
),
|
|
|
|
block: $ => seq(
|
|
'{',
|
|
optional(seq(
|
|
sep1($._semicolon, choice(
|
|
$._expression,
|
|
$._definition
|
|
)),
|
|
optional($._semicolon),
|
|
)),
|
|
'}'
|
|
),
|
|
|
|
// Types
|
|
|
|
_type: $ => choice(
|
|
$.function_type,
|
|
$.generic_type,
|
|
$.compound_type,
|
|
$.infix_type,
|
|
$.stable_type_identifier,
|
|
$._type_identifier
|
|
),
|
|
|
|
compound_type: $ => prec.left(PREC.infix, seq(
|
|
$._type,
|
|
'with',
|
|
$._type
|
|
)),
|
|
|
|
infix_type: $ => prec.left(PREC.infix, seq(
|
|
$._type,
|
|
choice($.identifier, $.operator_identifier),
|
|
$._type
|
|
)),
|
|
|
|
stable_type_identifier: $ => seq(
|
|
choice($.identifier, $.stable_identifier),
|
|
'.',
|
|
$._type_identifier
|
|
),
|
|
|
|
stable_identifier: $ => seq(
|
|
choice($.identifier, $.stable_identifier),
|
|
'.',
|
|
$.identifier
|
|
),
|
|
|
|
generic_type: $ => seq(
|
|
choice(
|
|
$._type_identifier,
|
|
$.stable_type_identifier
|
|
),
|
|
$.type_arguments
|
|
),
|
|
|
|
function_type: $ => seq(
|
|
$.parameter_types,
|
|
'=>',
|
|
$._type
|
|
),
|
|
|
|
parameter_types: $ => seq(
|
|
'(',
|
|
commaSep($._type),
|
|
')'
|
|
),
|
|
|
|
_type_identifier: $ => alias($.identifier, $.type_identifier),
|
|
|
|
// Patterns
|
|
|
|
_pattern: $ => choice(
|
|
$.identifier,
|
|
$.capture_pattern,
|
|
$.tuple_pattern,
|
|
$.case_class_pattern,
|
|
$.parenthesized_pattern,
|
|
$.alternative_pattern,
|
|
$.typed_pattern,
|
|
$.number,
|
|
$.string,
|
|
$.wildcard
|
|
),
|
|
|
|
case_class_pattern: $ => seq(
|
|
choice($._type_identifier, $.stable_type_identifier),
|
|
'(',
|
|
commaSep($._pattern),
|
|
')'
|
|
),
|
|
|
|
capture_pattern: $ => prec(PREC.infix, seq(
|
|
$.identifier,
|
|
'@',
|
|
$._pattern
|
|
)),
|
|
|
|
alternative_pattern: $ => prec.left(seq(
|
|
$._pattern,
|
|
'|',
|
|
$._pattern
|
|
)),
|
|
|
|
typed_pattern: $ => prec(-1, seq(
|
|
$._pattern,
|
|
':',
|
|
$._type
|
|
)),
|
|
|
|
tuple_pattern: $ => seq(
|
|
'(',
|
|
$._pattern,
|
|
repeat1(seq(',', $._pattern)),
|
|
')'
|
|
),
|
|
|
|
parenthesized_pattern: $ => seq(
|
|
'(',
|
|
$._pattern,
|
|
')'
|
|
),
|
|
|
|
// Expressions
|
|
|
|
_expression: $ => choice(
|
|
$.if_expression,
|
|
$.match_expression,
|
|
$.try_expression,
|
|
$.call_expression,
|
|
$.generic_function,
|
|
$.assignment_expression,
|
|
$.parenthesized_expression,
|
|
$.string_transform_expression,
|
|
$.field_expression,
|
|
$.instance_expression,
|
|
$.infix_expression,
|
|
$.prefix_expression,
|
|
$.tuple_expression,
|
|
$.case_block,
|
|
$.block,
|
|
$.identifier,
|
|
$.number,
|
|
$.string
|
|
),
|
|
|
|
if_expression: $ => prec.right(seq(
|
|
'if',
|
|
$.parenthesized_expression,
|
|
$._expression,
|
|
optional(seq(
|
|
'else',
|
|
$._expression
|
|
))
|
|
)),
|
|
|
|
match_expression: $ => seq(
|
|
$._expression,
|
|
'match',
|
|
$.case_block
|
|
),
|
|
|
|
try_expression: $ => prec.right(seq(
|
|
'try',
|
|
$._expression,
|
|
optional($.catch_clause),
|
|
optional($.finally_clause)
|
|
)),
|
|
|
|
catch_clause: $ => prec.right(seq('catch', $.case_block)),
|
|
|
|
finally_clause: $ => prec.right(seq('finally', $._expression)),
|
|
|
|
case_block: $ => choice(
|
|
prec(-1, seq('{', '}')),
|
|
seq('{', repeat1($.case_clause), '}')
|
|
),
|
|
|
|
case_clause: $ => seq(
|
|
'case',
|
|
$._pattern,
|
|
optional($.guard),
|
|
'=>',
|
|
sep(';', $._expression)
|
|
),
|
|
|
|
guard: $ => seq(
|
|
'if',
|
|
$._expression
|
|
),
|
|
|
|
assignment_expression: $ => prec.right(PREC.assign, seq(
|
|
$._expression,
|
|
'=',
|
|
$._expression
|
|
)),
|
|
|
|
generic_function: $ => prec(PREC.call, seq(
|
|
$._expression,
|
|
$.type_arguments
|
|
)),
|
|
|
|
call_expression: $ => prec(PREC.call, seq(
|
|
$._expression,
|
|
$.arguments,
|
|
optional(choice($.block, $.case_block))
|
|
)),
|
|
|
|
field_expression: $ => prec(PREC.field, seq(
|
|
$._expression,
|
|
'.',
|
|
$.identifier
|
|
)),
|
|
|
|
instance_expression: $ => prec(PREC.new,seq(
|
|
'new',
|
|
$._expression
|
|
)),
|
|
|
|
infix_expression: $ => prec.left(PREC.infix, seq(
|
|
$._expression,
|
|
choice($.identifier, $.operator_identifier),
|
|
$._expression
|
|
)),
|
|
|
|
prefix_expression: $ => prec(PREC.prefix, seq(
|
|
choice('+', '-', '!', '~'),
|
|
$._expression
|
|
)),
|
|
|
|
tuple_expression: $ => seq(
|
|
'(',
|
|
$._expression,
|
|
repeat1(seq(',', $._expression)),
|
|
')'
|
|
),
|
|
|
|
parenthesized_expression: $ => seq(
|
|
'(',
|
|
$._expression,
|
|
')'
|
|
),
|
|
|
|
type_arguments: $ => seq(
|
|
'[',
|
|
commaSep1($._type),
|
|
']'
|
|
),
|
|
|
|
arguments: $ => seq(
|
|
'(',
|
|
commaSep($._expression),
|
|
')'
|
|
),
|
|
|
|
identifier: $ => /[a-zA-Z_]\w*/,
|
|
|
|
wildcard: $ => '_',
|
|
|
|
operator_identifier: $ => /[^\s\w\(\)\[\]'"`.;,]+/,
|
|
|
|
number: $ => /\d+/,
|
|
|
|
string_transform_expression: $ => seq(
|
|
$.identifier,
|
|
$.string
|
|
),
|
|
|
|
string: $ => choice(
|
|
$._simple_string,
|
|
seq(
|
|
$._string_start,
|
|
$.interpolation,
|
|
repeat(seq(
|
|
$._string_middle,
|
|
$.interpolation,
|
|
)),
|
|
$._string_end
|
|
),
|
|
seq(
|
|
$._multiline_string_start,
|
|
$.interpolation,
|
|
repeat(seq(
|
|
$._multiline_string_middle,
|
|
$.interpolation,
|
|
)),
|
|
$._multiline_string_end
|
|
)
|
|
),
|
|
|
|
interpolation: $ => seq('$', choice($.identifier, $.block)),
|
|
|
|
_semicolon: $ => choice(
|
|
';',
|
|
$._automatic_semicolon
|
|
),
|
|
|
|
comment: $ => token(choice(
|
|
seq('//', /.*/),
|
|
seq(
|
|
'/*',
|
|
/[^*]*\*+([^/*][^*]*\*+)*/,
|
|
'/'
|
|
)
|
|
))
|
|
}
|
|
})
|
|
|
|
function commaSep(rule) {
|
|
return optional(commaSep1(rule))
|
|
}
|
|
|
|
function commaSep1(rule) {
|
|
return seq(rule, repeat(seq(',', rule)))
|
|
}
|
|
|
|
function sep(delimiter, rule) {
|
|
return optional(sep1(delimiter, rule))
|
|
}
|
|
|
|
function sep1(delimiter, rule) {
|
|
return seq(rule, repeat(seq(delimiter, rule)))
|
|
}
|