const letter = /[a-zA-Z]/,
decimal_digit = /[0-9]/,
octal_digit = /[0-7]/,
hex_digit = /[0-9A-Fa-f]/;
function comma_separate(x) {
return seq(x, repeat(seq(",", x)));
}
const FIELD_KEY = {
array_type: "array_type",
array_type_fixed_length: "array_type_fixed_length",
array_value: "array_value",
array_value_item: "array_value_item",
attribute_name: "attribute_name",
documentation: "documentation",
enum_int_constant: "enum_int_constant",
enum_key: "enum_key",
enum_name: "enum_name",
enum_type: "enum_type",
enum_val_decl: "enum_val_decl",
field_and_value: "field_and_value",
field_declaration: "field_declaration",
field_key: "field_key",
field_type: "field_type",
field_value: "field_value",
field_with_type: "field_with_type",
field_without_type: "field_without_type",
file_extension_constant: "file_extension_constant",
file_identifier_constant: "file_identifier_constant",
full_ident: "full_ident",
include_name: "include_name",
metadata: "metadata",
namespace_ident: "namespace_ident",
object_key: "object_key",
object_value: "object_value",
root_type_ident: "root_type_ident",
rpc_method: "rpc_method",
rpc_method_name: "rpc_method_name",
rpc_name: "rpc_name",
rpc_parameter: "rpc_parameter",
rpc_return_type: "rpc_return_type",
scalar_value: "scalar_value",
single_value: "single_value",
string_constant: "string_constant",
table_or_struct_declaration: "table_or_struct_declaration",
table_or_struct_name: "table_or_struct_name",
union_field_decl: "union_field_decl",
union_field_key: "union_field_key",
union_field_value: "union_field_value",
union_name: "union_name",
};
module.exports = grammar({
name: "flatbuffers",
extras: ($) => [$.comment, /\s/],
rules: {
source_file: ($) =>
seq(
repeat($.include),
optional(
repeat(
choice(
$.namespace_decl,
$.type_decl,
$.enum_decl,
$.union_decl,
$.root_decl,
$.file_extension_decl,
$.file_identifier_decl,
$.attribute_decl,
$.rpc_decl,
$.object,
),
),
),
),
documentation: ($) => token(seq("///", /.*/)),
include: ($) =>
seq("include", field(FIELD_KEY.include_name, $.string_constant), ";"),
namespace_decl: ($) =>
seq("namespace", field(FIELD_KEY.namespace_ident, $.full_ident), ";"),
attribute_decl: ($) =>
seq(
"attribute",
choice(
field(FIELD_KEY.attribute_name, $.identifier),
seq('"', field(FIELD_KEY.attribute_name, $.identifier), '"'),
),
";",
),
type_decl: ($) =>
seq(
repeat(field(FIELD_KEY.documentation, $.documentation)),
field(FIELD_KEY.table_or_struct_declaration, choice("table", "struct")),
field(FIELD_KEY.table_or_struct_name, $.identifier),
optional(field(FIELD_KEY.metadata, $.metadata)),
"{",
repeat(field(FIELD_KEY.field_declaration, $.field_decl)),
"}",
),
enum_decl: ($) =>
seq(
repeat(field(FIELD_KEY.documentation, $.documentation)),
seq(
"enum",
field(FIELD_KEY.enum_name, $.identifier),
":",
field(FIELD_KEY.enum_type, $.type),
),
optional(field(FIELD_KEY.metadata, $.metadata)),
"{",
comma_separate(field(FIELD_KEY.enum_val_decl, $.enum_val_decl)),
optional(","),
"}",
),
union_decl: ($) =>
seq(
repeat(field(FIELD_KEY.documentation, $.documentation)),
"union",
field(FIELD_KEY.union_name, $.identifier),
optional(field(FIELD_KEY.metadata, $.metadata)),
"{",
choice(
seq(
field(FIELD_KEY.union_field_decl, $.union_field_decl),
repeat(
seq(",", field(FIELD_KEY.union_field_decl, $.union_field_decl)),
),
optional(","),
),
field(
FIELD_KEY.field_without_type,
seq($.full_ident, repeat(seq(",", $.full_ident)), optional(",")),
),
),
"}",
),
object: ($) =>
seq(
"{",
comma_separate(
repeat(field(FIELD_KEY.documentation, $.documentation)),
field(FIELD_KEY.object_key, $.identifier),
":",
field(FIELD_KEY.object_value, $.value),
),
"}",
),
root_decl: ($) =>
seq("root_type", field(FIELD_KEY.root_type_ident, $.identifier), ";"),
file_extension_decl: ($) =>
seq(
"file_extension",
field(FIELD_KEY.file_extension_constant, $.string_constant),
";",
),
file_identifier_decl: ($) =>
seq(
"file_identifier",
field(FIELD_KEY.file_identifier_constant, $.string_constant),
";",
),
value: ($) =>
choice(
field(FIELD_KEY.single_value, $.single_value),
field(FIELD_KEY.object_value, $.object),
field(
FIELD_KEY.array_value,
seq(
"[",
comma_separate(field(FIELD_KEY.array_value_item, $.value)),
"]",
),
),
),
enum_val_decl: ($) =>
seq(
repeat(field(FIELD_KEY.documentation, $.documentation)),
field(FIELD_KEY.enum_key, $.identifier),
optional(seq("=", field(FIELD_KEY.enum_int_constant, $.int_constant))),
optional(field(FIELD_KEY.metadata, $.metadata)),
),
field_decl: ($) =>
seq(
repeat(field(FIELD_KEY.documentation, $.documentation)),
field(FIELD_KEY.field_key, $.identifier),
":",
field(FIELD_KEY.field_type, $.type),
optional(
seq(
"=",
field(FIELD_KEY.field_value, $.value),
),
),
optional(field(FIELD_KEY.metadata, $.metadata)),
";",
),
union_field_decl: ($) =>
seq(
repeat(field(FIELD_KEY.documentation, $.documentation)),
field(FIELD_KEY.union_field_key, $.identifier),
":",
field(FIELD_KEY.union_field_value, $.type),
),
type: ($) =>
choice(
"bool",
"byte",
"ubyte",
"short",
"ushort",
"int",
"uint",
"float",
"long",
"ulong",
"double",
"int8",
"uint8",
"int16",
"uint16",
"int32",
"uint32",
"int64",
"uint64",
"float32",
"float64",
"string",
field(FIELD_KEY.full_ident, $.full_ident),
seq(
"[",
field(FIELD_KEY.array_type, $.type),
optional(
seq(":", field(FIELD_KEY.array_type_fixed_length, $.int_lit)),
),
"]",
),
),
rpc_decl: ($) =>
seq(
repeat(field(FIELD_KEY.documentation, $.documentation)),
"rpc_service",
field(FIELD_KEY.rpc_name, $.identifier),
"{",
field(FIELD_KEY.rpc_method, repeat($.rpc_method)),
"}",
),
rpc_method: ($) =>
seq(
repeat(field(FIELD_KEY.documentation, $.documentation)),
field(FIELD_KEY.rpc_method_name, $.identifier),
"(",
field(FIELD_KEY.rpc_parameter, $.identifier),
")",
":",
field(FIELD_KEY.rpc_return_type, $.identifier),
optional(field(FIELD_KEY.metadata, $.metadata)),
";",
),
metadata: ($) =>
seq(
"(",
comma_separate(field(FIELD_KEY.field_and_value, $.field_and_value)),
")",
),
field_and_value: ($) =>
seq(
field(FIELD_KEY.field_key, $.identifier),
optional(seq(":", field(FIELD_KEY.field_value, $.single_value))),
),
single_value: ($) =>
choice(
field(FIELD_KEY.scalar_value, $.scalar),
field(FIELD_KEY.string_constant, $.string_constant),
field(FIELD_KEY.full_ident, $.full_ident),
),
identifier: ($) =>
token(seq(letter, optional(repeat(choice(letter, decimal_digit, "_"))))),
full_ident: ($) =>
seq($.identifier, optional(repeat(seq(".", $.identifier)))),
scalar: ($) => choice($.float_constant, $.int_constant, $.bool_constant),
bool_constant: ($) => choice($.true, $.false),
int_constant: ($) => seq(optional($.plus_minus_constant), $.int_lit),
float_constant: ($) => seq(optional($.plus_minus_constant), $.float_lit),
plus_minus_constant: ($) => choice($.plus_token, $.minus_token),
plus_token: ($) => token("+"),
minus_token: ($) => token("-"),
true: ($) => "true",
false: ($) => "false",
int_lit: ($) => choice($.decimal_lit, $.hex_lit),
decimal_lit: ($) =>
choice(token(seq(/[1-9]/, repeat(decimal_digit))), token("0")),
hex_lit: ($) =>
token(seq("0", choice("x", "X"), hex_digit, repeat(hex_digit))),
float_lit: ($) => {
const decimals = seq(decimal_digit, repeat(decimal_digit));
const exponent = seq(
choice("e", "E"),
optional(choice("+", "-")),
decimals,
);
return choice(
choice($.inf_token, $.nan_token),
token(
choice(
seq(decimals, ".", optional(decimals), optional(exponent)),
seq(decimals, exponent),
seq(".", decimals, optional(exponent)),
),
),
);
},
inf_token: ($) => choice(token("infinity"), token("inf")),
nan_token: ($) => token("nan"),
string_constant: ($) =>
seq(
'"',
repeat(choice(token.immediate(prec(1, /[^"\\]+/)), $.escape_sequence)),
'"',
),
escape_sequence: ($) =>
token.immediate(
seq(
"\\",
choice(
/[^xuU]/,
/\d{2,3}/,
/x[0-9a-fA-F]{2,}/,
/u[0-9a-fA-F]{4}/,
/U[0-9a-fA-F]{8}/,
),
),
),
comment: ($) =>
token(
choice(
prec(1, seq("//", /.*/)),
seq("/*", /[^*]*\*+([^/*][^*]*\*+)*/, "/"),
),
),
},
});