/** * @file JSON grammar for tree-sitter * @author Max Brunsfeld * @author Amaan Qureshi * @license MIT */ /// // @ts-check module.exports = grammar({ name: 'json', extras: $ => [ /\s/, $.comment, ], supertypes: $ => [ $._value, ], rules: { document: $ => repeat($._value), _value: $ => choice( $.object, $.array, $.number, $.string, $.true, $.false, $.null, ), object: $ => seq( '{', commaSep($.pair), '}', ), pair: $ => seq( field('key', $.string), ':', field('value', $._value), ), array: $ => seq( '[', commaSep($._value), ']', ), string: $ => choice( seq('"', '"'), seq('"', $._string_content, '"'), ), _string_content: $ => repeat1(choice( $.string_content, $.escape_sequence, )), string_content: _ => token.immediate(prec(1, /[^\\"\n]+/)), escape_sequence: _ => token.immediate(seq( '\\', /(\"|\\|\/|b|f|n|r|t|u)/, )), number: _ => { const decimalDigits = /\d+/; const signedInteger = seq(optional('-'), decimalDigits); const exponentPart = seq(choice('e', 'E'), signedInteger); const decimalIntegerLiteral = seq( optional('-'), choice( '0', seq(/[1-9]/, optional(decimalDigits)), ), ); const decimalLiteral = choice( seq(decimalIntegerLiteral, '.', optional(decimalDigits), optional(exponentPart)), seq(decimalIntegerLiteral, optional(exponentPart)), ); return token(decimalLiteral); }, true: _ => 'true', false: _ => 'false', null: _ => 'null', comment: _ => token(choice( seq('//', /.*/), seq( '/*', /[^*]*\*+([^/*][^*]*\*+)*/, '/', ), )), }, }); /** * Creates a rule to match one or more of the rules separated by a comma * * @param {RuleOrLiteral} rule * * @returns {SeqRule} */ function commaSep1(rule) { return seq(rule, repeat(seq(',', rule))); } /** * Creates a rule to optionally match one or more of the rules separated by a comma * * @param {RuleOrLiteral} rule * * @returns {ChoiceRule} */ function commaSep(rule) { return optional(commaSep1(rule)); }