187 lines
3.7 KiB
JavaScript
187 lines
3.7 KiB
JavaScript
const PREC = {
|
|
DEFAULT: 1,
|
|
PATCH: 2,
|
|
AND: 3,
|
|
CALL: 7,
|
|
PATH: 8,
|
|
};
|
|
|
|
function commaSep(rule) {
|
|
return optional(seq(rule, repeat(seq(',', rule)), optional(',')));
|
|
}
|
|
|
|
function semiSep(rule) {
|
|
return optional(seq(rule, repeat(seq(';', rule)), optional(';')));
|
|
}
|
|
|
|
module.exports = grammar({
|
|
name: 'decodal',
|
|
|
|
extras: $ => [
|
|
/[\s\uFEFF\u2060\u200B]/,
|
|
$.comment,
|
|
],
|
|
|
|
word: $ => $.identifier,
|
|
|
|
conflicts: $ => [
|
|
[$._expression, $.parameter],
|
|
],
|
|
|
|
rules: {
|
|
source_file: $ => repeat($._statement),
|
|
|
|
_statement: $ => choice(
|
|
seq($.field_definition, optional(';')),
|
|
seq($._expression, optional(';')),
|
|
),
|
|
|
|
comment: _ => token(seq('#', /.*/)),
|
|
|
|
_expression: $ => choice(
|
|
$.literal,
|
|
$.identifier,
|
|
$.regex_literal,
|
|
$.comparison_constraint,
|
|
$.object,
|
|
$.array,
|
|
$.let_expression,
|
|
$.function_expression,
|
|
$.match_expression,
|
|
$.import_expression,
|
|
$.parenthesized_expression,
|
|
$.call_expression,
|
|
$.path_expression,
|
|
$.binary_expression,
|
|
$.default_expression,
|
|
),
|
|
|
|
literal: $ => choice(
|
|
$.string,
|
|
$.integer,
|
|
$.float,
|
|
$.boolean,
|
|
),
|
|
|
|
boolean: _ => choice('true', 'false'),
|
|
|
|
string: _ => token(seq(
|
|
'"',
|
|
repeat(choice(/[^"\\\n]/, /\\./)),
|
|
'"',
|
|
)),
|
|
|
|
integer: _ => token(/[0-9]+/),
|
|
float: _ => token(/[0-9]+\.[0-9]+/),
|
|
|
|
identifier: _ => /[A-Za-z][A-Za-z0-9_]*/,
|
|
|
|
regex_literal: _ => token(seq(
|
|
'/',
|
|
repeat1(choice(/[^\/\\\n]/, /\\./)),
|
|
'/',
|
|
)),
|
|
|
|
object: $ => seq(
|
|
'{',
|
|
semiSep($.field_definition),
|
|
'}',
|
|
),
|
|
|
|
field_definition: $ => seq(
|
|
field('path', $.field_path),
|
|
'=',
|
|
field('value', $._expression),
|
|
),
|
|
|
|
field_path: $ => prec(10, seq(
|
|
$.identifier,
|
|
repeat(seq('.', $.identifier)),
|
|
)),
|
|
|
|
array: $ => seq(
|
|
'[',
|
|
commaSep($._expression),
|
|
']',
|
|
),
|
|
|
|
let_expression: $ => seq(
|
|
'let',
|
|
repeat(seq($.field_definition, ';')),
|
|
'in',
|
|
field('body', $._expression),
|
|
),
|
|
|
|
function_expression: $ => prec.right(seq(
|
|
'(',
|
|
commaSep($.parameter),
|
|
')',
|
|
'=>',
|
|
field('body', $._expression),
|
|
)),
|
|
|
|
parameter: $ => seq(
|
|
field('name', $.identifier),
|
|
optional(seq(':', field('constraint', $._expression))),
|
|
),
|
|
|
|
match_expression: $ => seq(
|
|
'match',
|
|
field('scrutinee', $._expression),
|
|
'{',
|
|
semiSep($.match_arm),
|
|
'}',
|
|
),
|
|
|
|
match_arm: $ => seq(
|
|
field('pattern', choice('_', $._expression)),
|
|
':',
|
|
field('body', $._expression),
|
|
),
|
|
|
|
import_expression: $ => seq(
|
|
'import',
|
|
field('specifier', $.string),
|
|
),
|
|
|
|
parenthesized_expression: $ => seq('(', $._expression, ')'),
|
|
|
|
call_expression: $ => prec.left(PREC.CALL, seq(
|
|
field('function', $._expression),
|
|
'(',
|
|
commaSep($._expression),
|
|
')',
|
|
)),
|
|
|
|
path_expression: $ => prec.left(PREC.PATH, seq(
|
|
field('object', $._expression),
|
|
'.',
|
|
field('field', $.identifier),
|
|
)),
|
|
|
|
comparison_constraint: $ => prec(6, seq(
|
|
field('operator', choice('>', '>=', '<', '<=')),
|
|
field('value', choice($.integer, $.float)),
|
|
)),
|
|
|
|
binary_expression: $ => choice(
|
|
prec.left(PREC.AND, seq(
|
|
field('left', $._expression),
|
|
field('operator', '&'),
|
|
field('right', $._expression),
|
|
)),
|
|
prec.left(PREC.PATCH, seq(
|
|
field('left', $._expression),
|
|
field('operator', token(prec(2, '//'))),
|
|
field('right', $._expression),
|
|
)),
|
|
),
|
|
|
|
default_expression: $ => prec.right(PREC.DEFAULT, seq(
|
|
field('base', $._expression),
|
|
'default',
|
|
field('fallback', $._expression),
|
|
)),
|
|
},
|
|
});
|