module.exports = grammar({
name: 'cpon',
extras: $ => [
$.comment,
/\s/,
],
rules: {
document: $ => $._value,
_value: $ => seq(
optional($.meta_map),
choice(
$.map,
$.imap,
$.array,
$.number,
$.float,
$.datetime,
$.string,
$.hex_blob,
$.esc_blob,
$.boolean,
$.null,
),
),
meta_map: $ => seq('<', optionalCommaSep($.meta_pair), '>'),
meta_pair: $ => seq(
field('key', choice($.string, $.number, $.float)),
':',
field('value', $._value),
),
map: $ => seq('{', optionalCommaSep($.pair), '}'),
pair: $ => seq(
field('key', $.string),
':',
field('value', $._value),
),
imap: $ => seq('i', '{', optionalCommaSep($.ipair), '}'),
ipair: $ => seq(
field('key', choice($.number, $.float)),
':',
field('value', $._value),
),
array: $ => seq('[', optionalCommaSep($._value), ']'),
string: $ => seq(
'"',
repeat(choice(
$.string_content,
$._escape_sequence,
)),
'"',
),
string_content: _ => token(prec(-1, /[^"\\]+/)),
_escape_sequence: $ =>
choice(
prec(2, token.immediate(seq('\\', /[^abfnrtvxu'\"\\\?]/))),
prec(1, $.escape_sequence),
),
escape_sequence: _ => token.immediate(seq(
'\\',
choice(
/[^xu0-7]/,
/[0-7]{1,3}/,
/x[0-9a-fA-F]{2}/,
/u[0-9a-fA-F]{4}/,
/u{[0-9a-fA-F]+}/,
))),
number: _ => {
const hex_literal = seq(
choice('0x', '0X'),
/[\da-fA-F]+u?/,
);
const int_literal = /\d+u?/;
const decimal_digits = /\d+/;
const signed_integer = seq(optional(choice('-', '+')), decimal_digits);
const decimal_integer_literal =choice(
'0',
seq(/[1-9]/, optional(decimal_digits)),
);
const decimal_literal = choice(
seq(optional(choice('-', '+')), decimal_integer_literal),
decimal_digits,
signed_integer,
);
return token(choice(
hex_literal,
decimal_literal,
int_literal,
));
},
float: _ => /[+-]?(\d+(\.\d+)?|\.\d+)([Ee][+-]?\d+)?/,
datetime: _ => seq(
'd',
'"',
/(\d{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T(0[0-9]|1[0-9]|2[0123]):([012345][0-9]):([012345][0-9])(\.\d{3})?(Z|[+-](0[1-9]|1[012])([012345][0-9])?)?/,
'"',
),
boolean: _ => choice('true', 'false'),
null: _ => 'null',
hex_blob: _ => seq(
'x',
'"',
/([0-9a-fA-F]{2})*/,
'"',
),
esc_blob: $ => seq(
'b',
'"',
repeat(choice(
$.string_content,
$._escape_sequence,
)),
'"',
),
comment: _ => token(choice(
seq('//', /.*/),
seq(
'/*',
/[^*]*\*+([^/*][^*]*\*+)*/,
'/',
),
)),
},
});
function optionalCommaSep1(rule) {
return seq(rule, repeat(seq(repeat(','), rule)), repeat(','));
}
function optionalCommaSep(rule) {
return optional(optionalCommaSep1(rule));
}