tree-sitter description
This commit is contained in:
@@ -0,0 +1,594 @@
|
||||
/// <reference types="tree-sitter-cli/dsl" />
|
||||
// @ts-check
|
||||
|
||||
const PREC = {
|
||||
RANGE: 1,
|
||||
FALLBACK: 2,
|
||||
OR: 3,
|
||||
AND: 4,
|
||||
COMPARE: 5,
|
||||
SUM: 6,
|
||||
PRODUCT: 7,
|
||||
PREFIX: 8,
|
||||
POSTFIX: 9,
|
||||
};
|
||||
|
||||
module.exports = grammar({
|
||||
name: 'brolang',
|
||||
|
||||
word: $ => $.identifier,
|
||||
|
||||
extras: $ => [/[ \t\r]/, $.comment],
|
||||
|
||||
conflicts: $ => [
|
||||
[$.expression, $.qualified_identifier],
|
||||
[$.constant_declaration, $.variable_declaration, $.expression],
|
||||
[$.function_declaration],
|
||||
[$.if_statement],
|
||||
[$.enum_literal],
|
||||
[$.array_type],
|
||||
[$.array_type, $.expression],
|
||||
[$.array_type, $.array_literal],
|
||||
[$.expression_statement, $.parenthesized_expression],
|
||||
],
|
||||
|
||||
rules: {
|
||||
source_file: $ => repeat(choice($._newline, $._top_level_declaration)),
|
||||
|
||||
_top_level_declaration: $ => choice(
|
||||
$.import_declaration,
|
||||
$.function_declaration,
|
||||
$.type_declaration,
|
||||
$.global_constant_declaration,
|
||||
$.global_variable_declaration,
|
||||
),
|
||||
|
||||
import_declaration: $ => seq(
|
||||
optional(seq(field('alias', $.identifier), '::', repeat($._newline))),
|
||||
'import',
|
||||
repeat($._newline),
|
||||
field('path', $.string),
|
||||
),
|
||||
|
||||
function_declaration: $ => seq(
|
||||
field('name', $.identifier),
|
||||
field('kind', choice('func', 'c_func')),
|
||||
field('parameters', $.parameter_list),
|
||||
repeat($._newline),
|
||||
field('result', $.type),
|
||||
optional(seq('!', field('error', $._error_type))),
|
||||
optional(choice(
|
||||
field('body', $.block),
|
||||
seq(repeat1($._newline), field('body', $.block)),
|
||||
)),
|
||||
),
|
||||
|
||||
type_declaration: $ => prec(1, seq(
|
||||
field('name', $.identifier),
|
||||
'::',
|
||||
repeat($._newline),
|
||||
field('value', choice(
|
||||
$.struct_type,
|
||||
$.c_struct_type,
|
||||
$.union_type,
|
||||
$.enum_type,
|
||||
$.opaque_type,
|
||||
$.distinct_type,
|
||||
$.alias_type,
|
||||
)),
|
||||
)),
|
||||
|
||||
global_constant_declaration: $ => seq(
|
||||
field('name', $.identifier),
|
||||
optional(field('type', $.type)),
|
||||
'::',
|
||||
repeat($._newline),
|
||||
field('value', $._value),
|
||||
),
|
||||
|
||||
global_variable_declaration: $ => seq(
|
||||
field('name', $.identifier),
|
||||
optional(field('type', $.type)),
|
||||
'=',
|
||||
repeat($._newline),
|
||||
field('value', $._value),
|
||||
),
|
||||
|
||||
struct_type: $ => seq('struct', repeat($._newline), $.record_body),
|
||||
c_struct_type: $ => seq('c_struct', repeat($._newline), $.record_body),
|
||||
opaque_type: _ => 'opaque',
|
||||
distinct_type: $ => seq('distinct', field('type', $.type)),
|
||||
alias_type: $ => seq('alias', field('type', $.type)),
|
||||
|
||||
union_type: $ => seq(
|
||||
'union',
|
||||
optional(seq('(', choice('enum', $.type), ')')),
|
||||
repeat($._newline),
|
||||
$.record_body,
|
||||
),
|
||||
|
||||
enum_type: $ => seq(
|
||||
'enum',
|
||||
optional(seq('(', field('backing', $.type), ')')),
|
||||
repeat($._newline),
|
||||
$.enum_body,
|
||||
),
|
||||
|
||||
record_body: $ => seq(
|
||||
'{',
|
||||
repeat(choice($._newline, seq($.record_field, optional(',')))),
|
||||
'}',
|
||||
),
|
||||
|
||||
record_field: $ => seq(
|
||||
field('name', $.identifier),
|
||||
field('type', choice($.type, $.struct_type)),
|
||||
),
|
||||
|
||||
enum_body: $ => seq(
|
||||
'{',
|
||||
repeat(choice($._newline, seq($.enum_member, optional(',')))),
|
||||
'}',
|
||||
),
|
||||
|
||||
enum_member: $ => seq(
|
||||
field('name', $.identifier),
|
||||
optional(seq('=', optional('-'), field('value', $.integer))),
|
||||
),
|
||||
|
||||
parameter_list: $ => seq(
|
||||
'(',
|
||||
commaSep($, choice($.parameter, '...')),
|
||||
')',
|
||||
),
|
||||
|
||||
parameter: $ => seq(
|
||||
optional('$'),
|
||||
field('name', choice($.identifier, $.sink)),
|
||||
repeat(seq(',', repeat($._newline), field('name', choice($.identifier, $.sink)))),
|
||||
field('type', $.type),
|
||||
),
|
||||
|
||||
type: $ => prec.left(seq(
|
||||
$._type_atom,
|
||||
repeat(seq('|', $._type_atom)),
|
||||
)),
|
||||
|
||||
_type_atom: $ => choice(
|
||||
$.builtin_type,
|
||||
$.named_type,
|
||||
$.optional_type,
|
||||
$.pointer_type,
|
||||
$.array_type,
|
||||
$.function_type,
|
||||
),
|
||||
|
||||
named_type: $ => prec.right(seq(
|
||||
$.qualified_identifier,
|
||||
optional($.argument_list),
|
||||
)),
|
||||
|
||||
optional_type: $ => seq('?', $.type),
|
||||
|
||||
pointer_type: $ => seq(
|
||||
choice('@', '*'),
|
||||
optional('mut'),
|
||||
$.type,
|
||||
),
|
||||
|
||||
array_type: $ => seq(
|
||||
'[',
|
||||
repeat($._newline),
|
||||
optional(choice(
|
||||
seq('*', ';', field('sentinel', $._type_constant)),
|
||||
seq(';', field('sentinel', $._type_constant)),
|
||||
seq(
|
||||
field('length', choice($.sink, $.expression)),
|
||||
optional(seq(';', field('sentinel', $._type_constant))),
|
||||
),
|
||||
)),
|
||||
repeat($._newline),
|
||||
']',
|
||||
optional('mut'),
|
||||
field('element', $.type),
|
||||
),
|
||||
|
||||
function_type: $ => prec.right(seq(
|
||||
choice('func', 'c_func'),
|
||||
$.parameter_list,
|
||||
repeat($._newline),
|
||||
field('result', $.type),
|
||||
optional(seq('!', field('error', $._error_type))),
|
||||
)),
|
||||
|
||||
_error_type: $ => choice($.type, $.enum_type, $.union_type),
|
||||
_type_constant: $ => seq(optional('-'), choice($.integer, $.character)),
|
||||
|
||||
builtin_type: _ => choice(
|
||||
'void', 'anyopaque', 'bool', 'int', 'float', 'range',
|
||||
'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64',
|
||||
'isize', 'usize', 'f32', 'f64',
|
||||
'c_char', 'c_schar', 'c_uchar', 'c_short', 'c_ushort',
|
||||
'c_int', 'c_uint', 'c_long', 'c_ulong', 'c_longlong',
|
||||
'c_ulonglong', 'c_float', 'c_double', 'c_longdouble',
|
||||
),
|
||||
|
||||
block: $ => seq(
|
||||
'{',
|
||||
repeat(choice($._newline, $.statement)),
|
||||
'}',
|
||||
),
|
||||
|
||||
statement: $ => choice(
|
||||
$.constant_declaration,
|
||||
$.variable_declaration,
|
||||
$.assignment_statement,
|
||||
$.return_statement,
|
||||
$.yield_statement,
|
||||
$.if_statement,
|
||||
$.while_statement,
|
||||
$.for_statement,
|
||||
$.match_statement,
|
||||
$.break_statement,
|
||||
$.continue_statement,
|
||||
$.defer_statement,
|
||||
$.labeled_block,
|
||||
$.block,
|
||||
$.expression_statement,
|
||||
),
|
||||
|
||||
constant_declaration: $ => seq(
|
||||
field('name', choice($.identifier, $.sink)),
|
||||
optional(field('type', $.type)),
|
||||
'::',
|
||||
repeat($._newline),
|
||||
field('value', $._value),
|
||||
),
|
||||
|
||||
variable_declaration: $ => seq(
|
||||
field('name', choice($.identifier, $.sink)),
|
||||
field('type', $.type),
|
||||
'=',
|
||||
repeat($._newline),
|
||||
field('value', $._value),
|
||||
),
|
||||
|
||||
assignment_statement: $ => seq(
|
||||
field('left', $.expression),
|
||||
field('operator', choice('=', '+=', '-=', '*=', '/=')),
|
||||
repeat($._newline),
|
||||
field('right', $._value),
|
||||
),
|
||||
|
||||
expression_statement: $ => $.expression,
|
||||
|
||||
return_statement: $ => seq(
|
||||
'return',
|
||||
repeat($._newline),
|
||||
field('value', $._value),
|
||||
),
|
||||
|
||||
yield_statement: $ => seq(
|
||||
'yield',
|
||||
repeat($._newline),
|
||||
optional(seq(':', field('label', $.identifier))),
|
||||
field('value', $._value),
|
||||
),
|
||||
|
||||
break_statement: $ => seq('break', optional(seq(':', field('label', $.identifier)))),
|
||||
continue_statement: $ => seq('continue', optional(seq(':', field('label', $.identifier)))),
|
||||
|
||||
defer_statement: $ => seq('defer', repeat($._newline), field('body', $.statement)),
|
||||
|
||||
labeled_block: $ => seq(
|
||||
field('label', $.identifier),
|
||||
':',
|
||||
repeat($._newline),
|
||||
$.block,
|
||||
),
|
||||
|
||||
if_statement: $ => seq(
|
||||
'if',
|
||||
repeat($._newline),
|
||||
field('condition', $.expression),
|
||||
optional($.capture_list),
|
||||
repeat($._newline),
|
||||
field('consequence', $._branch_body),
|
||||
optional(seq(
|
||||
repeat($._newline),
|
||||
'else',
|
||||
repeat($._newline),
|
||||
field('alternative', $._branch_body),
|
||||
)),
|
||||
),
|
||||
|
||||
capture_list: $ => seq(
|
||||
'|',
|
||||
commaSep1($, choice($.identifier, $.sink)),
|
||||
optional(seq(':', field('guard', $.expression))),
|
||||
'|',
|
||||
),
|
||||
|
||||
while_statement: $ => seq(
|
||||
'while',
|
||||
repeat($._newline),
|
||||
field('condition', $.expression),
|
||||
optional(seq(':', repeat($._newline), field('update', choice(
|
||||
$.assignment_statement,
|
||||
$.expression_statement,
|
||||
seq('(', repeat($._newline), choice($.assignment_statement, $.expression_statement), repeat($._newline), ')'),
|
||||
)))),
|
||||
repeat($._newline),
|
||||
optional(seq(field('label', $.identifier), ':', repeat($._newline))),
|
||||
field('body', $.block),
|
||||
),
|
||||
|
||||
for_statement: $ => seq(
|
||||
'for',
|
||||
repeat($._newline),
|
||||
field('iterable', $.expression),
|
||||
repeat($._newline),
|
||||
'|',
|
||||
optional('@'),
|
||||
field('item', $.identifier),
|
||||
optional(seq(',', field('index', $.identifier))),
|
||||
'|',
|
||||
repeat($._newline),
|
||||
optional(seq(field('label', $.identifier), ':', repeat($._newline))),
|
||||
field('body', $.block),
|
||||
),
|
||||
|
||||
match_statement: $ => seq(
|
||||
'match',
|
||||
repeat($._newline),
|
||||
field('subject', $.expression),
|
||||
repeat($._newline),
|
||||
'{',
|
||||
repeat(choice($._newline, $.match_arm)),
|
||||
'}',
|
||||
),
|
||||
|
||||
match_arm: $ => seq(
|
||||
field('pattern', choice('else', commaSep1($, $.expression))),
|
||||
optional($.match_capture),
|
||||
':',
|
||||
repeat($._newline),
|
||||
field('body', $._branch_body),
|
||||
),
|
||||
|
||||
match_capture: $ => seq('|', optional('@'), field('name', choice($.identifier, $.sink)), '|'),
|
||||
|
||||
_branch_body: $ => $.statement,
|
||||
|
||||
_value: $ => choice(
|
||||
$.labeled_block,
|
||||
$.block,
|
||||
$.if_statement,
|
||||
$.while_statement,
|
||||
$.for_statement,
|
||||
$.match_statement,
|
||||
$.expression,
|
||||
),
|
||||
|
||||
expression: $ => choice(
|
||||
$.binary_expression,
|
||||
$.catch_expression,
|
||||
$.unary_expression,
|
||||
$.field_expression,
|
||||
$.call_expression,
|
||||
$.index_expression,
|
||||
$.slice_expression,
|
||||
$.postfix_expression,
|
||||
$.struct_literal,
|
||||
$.comptime_block,
|
||||
$.function_literal,
|
||||
$.struct_type,
|
||||
$.array_type,
|
||||
$.enum_literal,
|
||||
$.array_literal,
|
||||
$.parenthesized_expression,
|
||||
$.identifier,
|
||||
$.sink,
|
||||
$.builtin_type,
|
||||
$.integer,
|
||||
$.float,
|
||||
$.string,
|
||||
$.multiline_string,
|
||||
$.character,
|
||||
$.boolean,
|
||||
$.none,
|
||||
$.undefined,
|
||||
),
|
||||
|
||||
binary_expression: $ => choice(
|
||||
prec.left(PREC.RANGE, seq(field('left', $.expression), field('operator', choice('..', '..=')), repeat($._newline), field('right', $.expression))),
|
||||
prec.left(PREC.FALLBACK, seq(field('left', $.expression), field('operator', choice('orelse', 'catch')), repeat($._newline), field('right', $.expression))),
|
||||
prec.left(PREC.OR, seq(field('left', $.expression), 'or', repeat($._newline), field('right', $.expression))),
|
||||
prec.left(PREC.AND, seq(field('left', $.expression), 'and', repeat($._newline), field('right', $.expression))),
|
||||
prec.left(PREC.COMPARE, seq(field('left', $.expression), field('operator', choice('==', '!=', '<', '<=', '>', '>=')), repeat($._newline), field('right', $.expression))),
|
||||
prec.left(PREC.SUM, seq(field('left', $.expression), field('operator', choice('+', '-')), repeat($._newline), field('right', $.expression))),
|
||||
prec.left(PREC.PRODUCT, seq(field('left', $.expression), field('operator', choice('*', '/')), repeat($._newline), field('right', $.expression))),
|
||||
),
|
||||
|
||||
catch_expression: $ => prec.left(PREC.FALLBACK, seq(
|
||||
field('value', $.expression),
|
||||
'catch',
|
||||
'|',
|
||||
field('name', choice($.identifier, $.sink)),
|
||||
'|',
|
||||
repeat($._newline),
|
||||
field('body', $.block),
|
||||
)),
|
||||
|
||||
unary_expression: $ => prec(PREC.PREFIX, seq(
|
||||
field('operator', choice('-', '&', '!', '$', 'try')),
|
||||
repeat($._newline),
|
||||
field('operand', $.expression),
|
||||
)),
|
||||
|
||||
field_expression: $ => prec.left(PREC.POSTFIX, seq(
|
||||
field('value', $.expression),
|
||||
'.',
|
||||
field('field', $.identifier),
|
||||
)),
|
||||
|
||||
call_expression: $ => prec.left(PREC.POSTFIX, seq(
|
||||
field('function', $.expression),
|
||||
field('arguments', $.argument_list),
|
||||
)),
|
||||
|
||||
argument_list: $ => prec(PREC.POSTFIX, seq('(', commaSep($, $.expression), ')')),
|
||||
|
||||
index_expression: $ => prec.left(PREC.POSTFIX, seq(
|
||||
field('value', $.expression),
|
||||
'[',
|
||||
repeat($._newline),
|
||||
field('index', $.expression),
|
||||
repeat($._newline),
|
||||
']',
|
||||
)),
|
||||
|
||||
slice_expression: $ => prec.left(PREC.POSTFIX, seq(
|
||||
field('value', $.expression),
|
||||
'[',
|
||||
repeat($._newline),
|
||||
optional(field('start', $.expression)),
|
||||
'..',
|
||||
optional(field('end', $.expression)),
|
||||
repeat($._newline),
|
||||
']',
|
||||
)),
|
||||
|
||||
postfix_expression: $ => prec.left(PREC.POSTFIX, seq(
|
||||
field('value', $.expression),
|
||||
field('operator', choice('?', '^')),
|
||||
)),
|
||||
|
||||
struct_literal: $ => prec(PREC.POSTFIX, seq(
|
||||
field('type', $.qualified_identifier),
|
||||
optional($.argument_list),
|
||||
field('fields', $.initializer_list),
|
||||
)),
|
||||
|
||||
initializer_list: $ => seq(
|
||||
'{',
|
||||
choice(
|
||||
repeat($._newline),
|
||||
seq(
|
||||
repeat($._newline),
|
||||
$.field_initializer,
|
||||
repeat(seq(repeat($._newline), ',', repeat($._newline), $.field_initializer)),
|
||||
optional(seq(repeat($._newline), ',')),
|
||||
repeat($._newline),
|
||||
),
|
||||
),
|
||||
'}',
|
||||
),
|
||||
|
||||
field_initializer: $ => seq(
|
||||
field('name', $.identifier),
|
||||
optional(seq('=', repeat($._newline), field('value', $.expression))),
|
||||
),
|
||||
|
||||
enum_literal: $ => seq(
|
||||
'.',
|
||||
field('name', $.identifier),
|
||||
optional($.variant_payload),
|
||||
),
|
||||
|
||||
variant_payload: $ => seq(
|
||||
'{',
|
||||
choice(
|
||||
repeat($._newline),
|
||||
seq(
|
||||
repeat($._newline),
|
||||
choice(
|
||||
$.expression,
|
||||
seq(
|
||||
$.keyed_field_initializer,
|
||||
repeat(seq(repeat($._newline), ',', repeat($._newline), $.keyed_field_initializer)),
|
||||
optional(seq(repeat($._newline), ',')),
|
||||
),
|
||||
),
|
||||
repeat($._newline),
|
||||
),
|
||||
),
|
||||
'}',
|
||||
),
|
||||
|
||||
keyed_field_initializer: $ => seq(
|
||||
field('name', $.identifier),
|
||||
'=',
|
||||
repeat($._newline),
|
||||
field('value', $.expression),
|
||||
),
|
||||
|
||||
array_literal: $ => seq('[', commaSep($, $.expression), ']'),
|
||||
|
||||
parenthesized_expression: $ => seq(
|
||||
'(',
|
||||
repeat($._newline),
|
||||
$.expression,
|
||||
repeat($._newline),
|
||||
')',
|
||||
),
|
||||
|
||||
comptime_block: $ => seq('$', repeat($._newline), $.block),
|
||||
|
||||
function_literal: $ => seq(
|
||||
'func',
|
||||
$.parameter_list,
|
||||
repeat($._newline),
|
||||
field('result', $.type),
|
||||
optional(seq('!', field('error', $._error_type))),
|
||||
repeat($._newline),
|
||||
field('body', $.block),
|
||||
),
|
||||
|
||||
qualified_identifier: $ => prec.right(seq(
|
||||
field('qualifier', $.identifier),
|
||||
optional(seq('.', field('name', $.identifier))),
|
||||
)),
|
||||
|
||||
boolean: _ => choice('true', 'false'),
|
||||
none: _ => 'none',
|
||||
undefined: _ => 'undefined',
|
||||
sink: _ => '_',
|
||||
|
||||
identifier: _ => /[A-Za-z_][A-Za-z0-9_]*/,
|
||||
integer: _ => /[0-9]+/,
|
||||
float: _ => token(prec(1, /[0-9]+\.[0-9]+/)),
|
||||
|
||||
string: $ => seq(
|
||||
'"',
|
||||
repeat(choice($.string_content, $.escape_sequence)),
|
||||
'"',
|
||||
),
|
||||
string_content: _ => token.immediate(prec(1, /[^"\\\n]+/)),
|
||||
escape_sequence: _ => token.immediate(/\\(?:\\|"|n|r|t|0)/),
|
||||
multiline_string: _ => token(/`[^\n]*(?:\n[ \t]*`[^\n]*)*/),
|
||||
character: _ => token(/'(?:[^'\\\n]|\\(?:\\|'|n|r|t|0))'/),
|
||||
comment: _ => token(seq('#', /[^\n]*/)),
|
||||
_newline: _ => /\n/,
|
||||
},
|
||||
});
|
||||
|
||||
function commaSep($, rule) {
|
||||
return choice(
|
||||
repeat($._newline),
|
||||
seq(
|
||||
repeat($._newline),
|
||||
rule,
|
||||
repeat(seq(repeat($._newline), ',', repeat($._newline), rule)),
|
||||
optional(seq(repeat($._newline), ',')),
|
||||
repeat($._newline),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function commaSep1($, rule) {
|
||||
return seq(
|
||||
rule,
|
||||
repeat(seq(repeat($._newline), ',', repeat($._newline), rule)),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "tree-sitter-brolang",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"generate": "tree-sitter generate",
|
||||
"test": "tree-sitter test"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
(comment) @comment
|
||||
|
||||
[
|
||||
(string)
|
||||
(multiline_string)
|
||||
] @string
|
||||
|
||||
(character) @string
|
||||
(escape_sequence) @escape
|
||||
|
||||
[
|
||||
(integer)
|
||||
(float)
|
||||
] @number
|
||||
|
||||
(boolean) @constant.builtin
|
||||
|
||||
[
|
||||
(none)
|
||||
(undefined)
|
||||
] @constant.builtin
|
||||
|
||||
(builtin_type) @type.builtin
|
||||
(named_type) @type
|
||||
|
||||
(type_declaration name: (identifier) @type)
|
||||
(function_declaration name: (identifier) @function)
|
||||
(parameter name: (identifier) @variable.parameter)
|
||||
|
||||
(call_expression function: (expression (identifier) @function))
|
||||
(call_expression function: (expression (field_expression field: (identifier) @function)))
|
||||
(field_expression field: (identifier) @property)
|
||||
(field_initializer name: (identifier) @property)
|
||||
(keyed_field_initializer name: (identifier) @property)
|
||||
(record_field name: (identifier) @property)
|
||||
(enum_member name: (identifier) @property)
|
||||
(enum_literal name: (identifier) @property)
|
||||
|
||||
(import_declaration alias: (identifier) @module)
|
||||
(opaque_type) @keyword
|
||||
|
||||
[
|
||||
"func"
|
||||
"c_func"
|
||||
"struct"
|
||||
"c_struct"
|
||||
"union"
|
||||
"enum"
|
||||
"distinct"
|
||||
"alias"
|
||||
"import"
|
||||
"return"
|
||||
"try"
|
||||
"catch"
|
||||
"mut"
|
||||
"orelse"
|
||||
"and"
|
||||
"or"
|
||||
"if"
|
||||
"while"
|
||||
"for"
|
||||
"break"
|
||||
"continue"
|
||||
"defer"
|
||||
"yield"
|
||||
"match"
|
||||
"else"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"::"
|
||||
"="
|
||||
"+="
|
||||
"-="
|
||||
"*="
|
||||
"/="
|
||||
"=="
|
||||
"!="
|
||||
"<"
|
||||
"<="
|
||||
">"
|
||||
">="
|
||||
"+"
|
||||
"-"
|
||||
"*"
|
||||
"/"
|
||||
"!"
|
||||
"&"
|
||||
"?"
|
||||
"^"
|
||||
".."
|
||||
"..="
|
||||
"|"
|
||||
] @operator
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
","
|
||||
"."
|
||||
":"
|
||||
";"
|
||||
] @punctuation.delimiter
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
||||
#ifndef TREE_SITTER_ALLOC_H_
|
||||
#define TREE_SITTER_ALLOC_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Allow clients to override allocation functions
|
||||
#ifdef TREE_SITTER_REUSE_ALLOCATOR
|
||||
|
||||
extern void *(*ts_current_malloc)(size_t size);
|
||||
extern void *(*ts_current_calloc)(size_t count, size_t size);
|
||||
extern void *(*ts_current_realloc)(void *ptr, size_t size);
|
||||
extern void (*ts_current_free)(void *ptr);
|
||||
|
||||
#ifndef ts_malloc
|
||||
#define ts_malloc ts_current_malloc
|
||||
#endif
|
||||
#ifndef ts_calloc
|
||||
#define ts_calloc ts_current_calloc
|
||||
#endif
|
||||
#ifndef ts_realloc
|
||||
#define ts_realloc ts_current_realloc
|
||||
#endif
|
||||
#ifndef ts_free
|
||||
#define ts_free ts_current_free
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifndef ts_malloc
|
||||
#define ts_malloc malloc
|
||||
#endif
|
||||
#ifndef ts_calloc
|
||||
#define ts_calloc calloc
|
||||
#endif
|
||||
#ifndef ts_realloc
|
||||
#define ts_realloc realloc
|
||||
#endif
|
||||
#ifndef ts_free
|
||||
#define ts_free free
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TREE_SITTER_ALLOC_H_
|
||||
@@ -0,0 +1,347 @@
|
||||
#ifndef TREE_SITTER_ARRAY_H_
|
||||
#define TREE_SITTER_ARRAY_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "./alloc.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4101)
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
#endif
|
||||
|
||||
#define Array(T) \
|
||||
struct { \
|
||||
T *contents; \
|
||||
uint32_t size; \
|
||||
uint32_t capacity; \
|
||||
}
|
||||
|
||||
/// Initialize an array.
|
||||
#define array_init(self) \
|
||||
((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL)
|
||||
|
||||
/// Create an empty array.
|
||||
#define array_new() \
|
||||
{ NULL, 0, 0 }
|
||||
|
||||
/// Get a pointer to the element at a given `index` in the array.
|
||||
#define array_get(self, _index) \
|
||||
(assert((uint32_t)(_index) < (self)->size), &(self)->contents[_index])
|
||||
|
||||
/// Get a pointer to the first element in the array.
|
||||
#define array_front(self) array_get(self, 0)
|
||||
|
||||
/// Get a pointer to the last element in the array.
|
||||
#define array_back(self) array_get(self, (self)->size - 1)
|
||||
|
||||
/// Clear the array, setting its size to zero. Note that this does not free any
|
||||
/// memory allocated for the array's contents.
|
||||
#define array_clear(self) ((self)->size = 0)
|
||||
|
||||
/// Reserve `new_capacity` elements of space in the array. If `new_capacity` is
|
||||
/// less than the array's current capacity, this function has no effect.
|
||||
#define array_reserve(self, new_capacity) \
|
||||
((self)->contents = _array__reserve( \
|
||||
(void *)(self)->contents, &(self)->capacity, \
|
||||
array_elem_size(self), new_capacity) \
|
||||
)
|
||||
|
||||
/// Free any memory allocated for this array. Note that this does not free any
|
||||
/// memory allocated for the array's contents.
|
||||
#define array_delete(self) _array__delete((self), (void *)(self)->contents, sizeof(*self))
|
||||
|
||||
/// Push a new `element` onto the end of the array.
|
||||
#define array_push(self, element) \
|
||||
do { \
|
||||
(self)->contents = _array__grow( \
|
||||
(void *)(self)->contents, (self)->size, &(self)->capacity, \
|
||||
1, array_elem_size(self) \
|
||||
); \
|
||||
(self)->contents[(self)->size++] = (element); \
|
||||
} while(0)
|
||||
|
||||
/// Increase the array's size by `count` elements.
|
||||
/// New elements are zero-initialized.
|
||||
#define array_grow_by(self, count) \
|
||||
do { \
|
||||
if ((count) == 0) break; \
|
||||
(self)->contents = _array__grow( \
|
||||
(self)->contents, (self)->size, &(self)->capacity, \
|
||||
count, array_elem_size(self) \
|
||||
); \
|
||||
memset((self)->contents + (self)->size, 0, (count) * array_elem_size(self)); \
|
||||
(self)->size += (count); \
|
||||
} while (0)
|
||||
|
||||
/// Append all elements from one array to the end of another.
|
||||
#define array_push_all(self, other) \
|
||||
array_extend((self), (other)->size, (other)->contents)
|
||||
|
||||
/// Append `count` elements to the end of the array, reading their values from the
|
||||
/// `contents` pointer.
|
||||
#define array_extend(self, count, other_contents) \
|
||||
(self)->contents = _array__splice( \
|
||||
(void*)(self)->contents, &(self)->size, &(self)->capacity, \
|
||||
array_elem_size(self), (self)->size, 0, count, other_contents \
|
||||
)
|
||||
|
||||
/// Remove `old_count` elements from the array starting at the given `index`. At
|
||||
/// the same index, insert `new_count` new elements, reading their values from the
|
||||
/// `new_contents` pointer.
|
||||
#define array_splice(self, _index, old_count, new_count, new_contents) \
|
||||
(self)->contents = _array__splice( \
|
||||
(void *)(self)->contents, &(self)->size, &(self)->capacity, \
|
||||
array_elem_size(self), _index, old_count, new_count, new_contents \
|
||||
)
|
||||
|
||||
/// Insert one `element` into the array at the given `index`.
|
||||
#define array_insert(self, _index, element) \
|
||||
(self)->contents = _array__splice( \
|
||||
(void *)(self)->contents, &(self)->size, &(self)->capacity, \
|
||||
array_elem_size(self), _index, 0, 1, &(element) \
|
||||
)
|
||||
|
||||
/// Remove one element from the array at the given `index`.
|
||||
#define array_erase(self, _index) \
|
||||
_array__erase((void *)(self)->contents, &(self)->size, array_elem_size(self), _index)
|
||||
|
||||
/// Pop the last element off the array, returning the element by value.
|
||||
#define array_pop(self) ((self)->contents[--(self)->size])
|
||||
|
||||
/// Assign the contents of one array to another, reallocating if necessary.
|
||||
#define array_assign(self, other) \
|
||||
(self)->contents = _array__assign( \
|
||||
(void *)(self)->contents, &(self)->size, &(self)->capacity, \
|
||||
(const void *)(other)->contents, (other)->size, array_elem_size(self) \
|
||||
)
|
||||
|
||||
/// Swap one array with another
|
||||
#define array_swap(self, other) \
|
||||
do { \
|
||||
struct Swap swapped_contents = _array__swap( \
|
||||
(void *)(self)->contents, &(self)->size, &(self)->capacity, \
|
||||
(void *)(other)->contents, &(other)->size, &(other)->capacity \
|
||||
); \
|
||||
(self)->contents = swapped_contents.self_contents; \
|
||||
(other)->contents = swapped_contents.other_contents; \
|
||||
} while (0)
|
||||
|
||||
/// Get the size of the array contents
|
||||
#define array_elem_size(self) (sizeof *(self)->contents)
|
||||
|
||||
/// Search a sorted array for a given `needle` value, using the given `compare`
|
||||
/// callback to determine the order.
|
||||
///
|
||||
/// If an existing element is found to be equal to `needle`, then the `index`
|
||||
/// out-parameter is set to the existing value's index, and the `exists`
|
||||
/// out-parameter is set to true. Otherwise, `index` is set to an index where
|
||||
/// `needle` should be inserted in order to preserve the sorting, and `exists`
|
||||
/// is set to false.
|
||||
#define array_search_sorted_with(self, compare, needle, _index, _exists) \
|
||||
_array__search_sorted(self, 0, compare, , needle, _index, _exists)
|
||||
|
||||
/// Search a sorted array for a given `needle` value, using integer comparisons
|
||||
/// of a given struct field (specified with a leading dot) to determine the order.
|
||||
///
|
||||
/// See also `array_search_sorted_with`.
|
||||
#define array_search_sorted_by(self, field, needle, _index, _exists) \
|
||||
_array__search_sorted(self, 0, _compare_int, field, needle, _index, _exists)
|
||||
|
||||
/// Insert a given `value` into a sorted array, using the given `compare`
|
||||
/// callback to determine the order.
|
||||
#define array_insert_sorted_with(self, compare, value) \
|
||||
do { \
|
||||
unsigned _index, _exists; \
|
||||
array_search_sorted_with(self, compare, &(value), &_index, &_exists); \
|
||||
if (!_exists) array_insert(self, _index, value); \
|
||||
} while (0)
|
||||
|
||||
/// Insert a given `value` into a sorted array, using integer comparisons of
|
||||
/// a given struct field (specified with a leading dot) to determine the order.
|
||||
///
|
||||
/// See also `array_search_sorted_by`.
|
||||
#define array_insert_sorted_by(self, field, value) \
|
||||
do { \
|
||||
unsigned _index, _exists; \
|
||||
array_search_sorted_by(self, field, (value) field, &_index, &_exists); \
|
||||
if (!_exists) array_insert(self, _index, value); \
|
||||
} while (0)
|
||||
|
||||
// Private
|
||||
|
||||
// Pointers to individual `Array` fields (rather than the entire `Array` itself)
|
||||
// are passed to the various `_array__*` functions below to address strict aliasing
|
||||
// violations that arises when the _entire_ `Array` struct is passed as `Array(void)*`.
|
||||
//
|
||||
// The `Array` type itself was not altered as a solution in order to avoid breakage
|
||||
// with existing consumers (in particular, parsers with external scanners).
|
||||
|
||||
/// This is not what you're looking for, see `array_delete`.
|
||||
static inline void _array__delete(void *self, void *contents, size_t self_size) {
|
||||
if (contents) ts_free(contents);
|
||||
if (self) memset(self, 0, self_size);
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_erase`.
|
||||
static inline void _array__erase(void* self_contents, uint32_t *size,
|
||||
size_t element_size, uint32_t index) {
|
||||
assert(index < *size);
|
||||
char *contents = (char *)self_contents;
|
||||
memmove(contents + index * element_size, contents + (index + 1) * element_size,
|
||||
(*size - index - 1) * element_size);
|
||||
(*size)--;
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_reserve`.
|
||||
static inline void *_array__reserve(void *contents, uint32_t *capacity,
|
||||
size_t element_size, uint32_t new_capacity) {
|
||||
void *new_contents = contents;
|
||||
if (new_capacity > *capacity) {
|
||||
if (contents) {
|
||||
new_contents = ts_realloc(contents, new_capacity * element_size);
|
||||
} else {
|
||||
new_contents = ts_malloc(new_capacity * element_size);
|
||||
}
|
||||
*capacity = new_capacity;
|
||||
}
|
||||
return new_contents;
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_assign`.
|
||||
static inline void *_array__assign(void* self_contents, uint32_t *self_size, uint32_t *self_capacity,
|
||||
const void *other_contents, uint32_t other_size, size_t element_size) {
|
||||
void *new_contents = _array__reserve(self_contents, self_capacity, element_size, other_size);
|
||||
*self_size = other_size;
|
||||
memcpy(new_contents, other_contents, *self_size * element_size);
|
||||
return new_contents;
|
||||
}
|
||||
|
||||
struct Swap {
|
||||
void *self_contents;
|
||||
void *other_contents;
|
||||
};
|
||||
|
||||
/// This is not what you're looking for, see `array_swap`.
|
||||
// static inline void _array__swap(Array *self, Array *other) {
|
||||
static inline struct Swap _array__swap(void *self_contents, uint32_t *self_size, uint32_t *self_capacity,
|
||||
void *other_contents, uint32_t *other_size, uint32_t *other_capacity) {
|
||||
void *new_self_contents = other_contents;
|
||||
uint32_t new_self_size = *other_size;
|
||||
uint32_t new_self_capacity = *other_capacity;
|
||||
|
||||
void *new_other_contents = self_contents;
|
||||
*other_size = *self_size;
|
||||
*other_capacity = *self_capacity;
|
||||
|
||||
*self_size = new_self_size;
|
||||
*self_capacity = new_self_capacity;
|
||||
|
||||
struct Swap out = {
|
||||
.self_contents = new_self_contents,
|
||||
.other_contents = new_other_contents,
|
||||
};
|
||||
return out;
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_push` or `array_grow_by`.
|
||||
static inline void *_array__grow(void *contents, uint32_t size, uint32_t *capacity,
|
||||
uint32_t count, size_t element_size) {
|
||||
void *new_contents = contents;
|
||||
uint32_t new_size = size + count;
|
||||
if (new_size > *capacity) {
|
||||
uint32_t new_capacity = *capacity * 2;
|
||||
if (new_capacity < 8) new_capacity = 8;
|
||||
if (new_capacity < new_size) new_capacity = new_size;
|
||||
new_contents = _array__reserve(contents, capacity, element_size, new_capacity);
|
||||
}
|
||||
return new_contents;
|
||||
}
|
||||
|
||||
/// This is not what you're looking for, see `array_splice`.
|
||||
static inline void *_array__splice(void *self_contents, uint32_t *size, uint32_t *capacity,
|
||||
size_t element_size,
|
||||
uint32_t index, uint32_t old_count,
|
||||
uint32_t new_count, const void *elements) {
|
||||
uint32_t new_size = *size + new_count - old_count;
|
||||
uint32_t old_end = index + old_count;
|
||||
uint32_t new_end = index + new_count;
|
||||
assert(old_end <= *size);
|
||||
|
||||
void *new_contents = _array__reserve(self_contents, capacity, element_size, new_size);
|
||||
|
||||
char *contents = (char *)new_contents;
|
||||
if (*size > old_end) {
|
||||
memmove(
|
||||
contents + new_end * element_size,
|
||||
contents + old_end * element_size,
|
||||
(*size - old_end) * element_size
|
||||
);
|
||||
}
|
||||
if (new_count > 0) {
|
||||
if (elements) {
|
||||
memcpy(
|
||||
(contents + index * element_size),
|
||||
elements,
|
||||
new_count * element_size
|
||||
);
|
||||
} else {
|
||||
memset(
|
||||
(contents + index * element_size),
|
||||
0,
|
||||
new_count * element_size
|
||||
);
|
||||
}
|
||||
}
|
||||
*size += new_count - old_count;
|
||||
|
||||
return new_contents;
|
||||
}
|
||||
|
||||
/// A binary search routine, based on Rust's `std::slice::binary_search_by`.
|
||||
/// This is not what you're looking for, see `array_search_sorted_with` or `array_search_sorted_by`.
|
||||
#define _array__search_sorted(self, start, compare, suffix, needle, _index, _exists) \
|
||||
do { \
|
||||
*(_index) = start; \
|
||||
*(_exists) = false; \
|
||||
uint32_t size = (self)->size - *(_index); \
|
||||
if (size == 0) break; \
|
||||
int comparison; \
|
||||
while (size > 1) { \
|
||||
uint32_t half_size = size / 2; \
|
||||
uint32_t mid_index = *(_index) + half_size; \
|
||||
comparison = compare(&((self)->contents[mid_index] suffix), (needle)); \
|
||||
if (comparison <= 0) *(_index) = mid_index; \
|
||||
size -= half_size; \
|
||||
} \
|
||||
comparison = compare(&((self)->contents[*(_index)] suffix), (needle)); \
|
||||
if (comparison == 0) *(_exists) = true; \
|
||||
else if (comparison < 0) *(_index) += 1; \
|
||||
} while (0)
|
||||
|
||||
/// Helper macro for the `_sorted_by` routines below. This takes the left (existing)
|
||||
/// parameter by reference in order to work with the generic sorting function above.
|
||||
#define _compare_int(a, b) ((int)*(a) - (int)(b))
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TREE_SITTER_ARRAY_H_
|
||||
@@ -0,0 +1,286 @@
|
||||
#ifndef TREE_SITTER_PARSER_H_
|
||||
#define TREE_SITTER_PARSER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ts_builtin_sym_error ((TSSymbol)-1)
|
||||
#define ts_builtin_sym_end 0
|
||||
#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
|
||||
|
||||
#ifndef TREE_SITTER_API_H_
|
||||
typedef uint16_t TSStateId;
|
||||
typedef uint16_t TSSymbol;
|
||||
typedef uint16_t TSFieldId;
|
||||
typedef struct TSLanguage TSLanguage;
|
||||
typedef struct TSLanguageMetadata {
|
||||
uint8_t major_version;
|
||||
uint8_t minor_version;
|
||||
uint8_t patch_version;
|
||||
} TSLanguageMetadata;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
TSFieldId field_id;
|
||||
uint8_t child_index;
|
||||
bool inherited;
|
||||
} TSFieldMapEntry;
|
||||
|
||||
// Used to index the field and supertype maps.
|
||||
typedef struct {
|
||||
uint16_t index;
|
||||
uint16_t length;
|
||||
} TSMapSlice;
|
||||
|
||||
typedef struct {
|
||||
bool visible;
|
||||
bool named;
|
||||
bool supertype;
|
||||
} TSSymbolMetadata;
|
||||
|
||||
typedef struct TSLexer TSLexer;
|
||||
|
||||
struct TSLexer {
|
||||
int32_t lookahead;
|
||||
TSSymbol result_symbol;
|
||||
void (*advance)(TSLexer *, bool);
|
||||
void (*mark_end)(TSLexer *);
|
||||
uint32_t (*get_column)(TSLexer *);
|
||||
bool (*is_at_included_range_start)(const TSLexer *);
|
||||
bool (*eof)(const TSLexer *);
|
||||
void (*log)(const TSLexer *, const char *, ...);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
TSParseActionTypeShift,
|
||||
TSParseActionTypeReduce,
|
||||
TSParseActionTypeAccept,
|
||||
TSParseActionTypeRecover,
|
||||
} TSParseActionType;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t type;
|
||||
TSStateId state;
|
||||
bool extra;
|
||||
bool repetition;
|
||||
} shift;
|
||||
struct {
|
||||
uint8_t type;
|
||||
uint8_t child_count;
|
||||
TSSymbol symbol;
|
||||
int16_t dynamic_precedence;
|
||||
uint16_t production_id;
|
||||
} reduce;
|
||||
uint8_t type;
|
||||
} TSParseAction;
|
||||
|
||||
typedef struct {
|
||||
uint16_t lex_state;
|
||||
uint16_t external_lex_state;
|
||||
} TSLexMode;
|
||||
|
||||
typedef struct {
|
||||
uint16_t lex_state;
|
||||
uint16_t external_lex_state;
|
||||
uint16_t reserved_word_set_id;
|
||||
} TSLexerMode;
|
||||
|
||||
typedef union {
|
||||
TSParseAction action;
|
||||
struct {
|
||||
uint8_t count;
|
||||
bool reusable;
|
||||
} entry;
|
||||
} TSParseActionEntry;
|
||||
|
||||
typedef struct {
|
||||
int32_t start;
|
||||
int32_t end;
|
||||
} TSCharacterRange;
|
||||
|
||||
struct TSLanguage {
|
||||
uint32_t abi_version;
|
||||
uint32_t symbol_count;
|
||||
uint32_t alias_count;
|
||||
uint32_t token_count;
|
||||
uint32_t external_token_count;
|
||||
uint32_t state_count;
|
||||
uint32_t large_state_count;
|
||||
uint32_t production_id_count;
|
||||
uint32_t field_count;
|
||||
uint16_t max_alias_sequence_length;
|
||||
const uint16_t *parse_table;
|
||||
const uint16_t *small_parse_table;
|
||||
const uint32_t *small_parse_table_map;
|
||||
const TSParseActionEntry *parse_actions;
|
||||
const char * const *symbol_names;
|
||||
const char * const *field_names;
|
||||
const TSMapSlice *field_map_slices;
|
||||
const TSFieldMapEntry *field_map_entries;
|
||||
const TSSymbolMetadata *symbol_metadata;
|
||||
const TSSymbol *public_symbol_map;
|
||||
const uint16_t *alias_map;
|
||||
const TSSymbol *alias_sequences;
|
||||
const TSLexerMode *lex_modes;
|
||||
bool (*lex_fn)(TSLexer *, TSStateId);
|
||||
bool (*keyword_lex_fn)(TSLexer *, TSStateId);
|
||||
TSSymbol keyword_capture_token;
|
||||
struct {
|
||||
const bool *states;
|
||||
const TSSymbol *symbol_map;
|
||||
void *(*create)(void);
|
||||
void (*destroy)(void *);
|
||||
bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist);
|
||||
unsigned (*serialize)(void *, char *);
|
||||
void (*deserialize)(void *, const char *, unsigned);
|
||||
} external_scanner;
|
||||
const TSStateId *primary_state_ids;
|
||||
const char *name;
|
||||
const TSSymbol *reserved_words;
|
||||
uint16_t max_reserved_word_set_size;
|
||||
uint32_t supertype_count;
|
||||
const TSSymbol *supertype_symbols;
|
||||
const TSMapSlice *supertype_map_slices;
|
||||
const TSSymbol *supertype_map_entries;
|
||||
TSLanguageMetadata metadata;
|
||||
};
|
||||
|
||||
static inline bool set_contains(const TSCharacterRange *ranges, uint32_t len, int32_t lookahead) {
|
||||
uint32_t index = 0;
|
||||
uint32_t size = len - index;
|
||||
while (size > 1) {
|
||||
uint32_t half_size = size / 2;
|
||||
uint32_t mid_index = index + half_size;
|
||||
const TSCharacterRange *range = &ranges[mid_index];
|
||||
if (lookahead >= range->start && lookahead <= range->end) {
|
||||
return true;
|
||||
} else if (lookahead > range->end) {
|
||||
index = mid_index;
|
||||
}
|
||||
size -= half_size;
|
||||
}
|
||||
const TSCharacterRange *range = &ranges[index];
|
||||
return (lookahead >= range->start && lookahead <= range->end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lexer Macros
|
||||
*/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define UNUSED __pragma(warning(suppress : 4101))
|
||||
#else
|
||||
#define UNUSED __attribute__((unused))
|
||||
#endif
|
||||
|
||||
#define START_LEXER() \
|
||||
bool result = false; \
|
||||
bool skip = false; \
|
||||
UNUSED \
|
||||
bool eof = false; \
|
||||
int32_t lookahead; \
|
||||
goto start; \
|
||||
next_state: \
|
||||
lexer->advance(lexer, skip); \
|
||||
start: \
|
||||
skip = false; \
|
||||
lookahead = lexer->lookahead;
|
||||
|
||||
#define ADVANCE(state_value) \
|
||||
{ \
|
||||
state = state_value; \
|
||||
goto next_state; \
|
||||
}
|
||||
|
||||
#define ADVANCE_MAP(...) \
|
||||
{ \
|
||||
static const uint16_t map[] = { __VA_ARGS__ }; \
|
||||
for (uint32_t i = 0; i < sizeof(map) / sizeof(map[0]); i += 2) { \
|
||||
if (map[i] == lookahead) { \
|
||||
state = map[i + 1]; \
|
||||
goto next_state; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SKIP(state_value) \
|
||||
{ \
|
||||
skip = true; \
|
||||
state = state_value; \
|
||||
goto next_state; \
|
||||
}
|
||||
|
||||
#define ACCEPT_TOKEN(symbol_value) \
|
||||
result = true; \
|
||||
lexer->result_symbol = symbol_value; \
|
||||
lexer->mark_end(lexer);
|
||||
|
||||
#define END_STATE() return result;
|
||||
|
||||
/*
|
||||
* Parse Table Macros
|
||||
*/
|
||||
|
||||
#define SMALL_STATE(id) ((id) - LARGE_STATE_COUNT)
|
||||
|
||||
#define STATE(id) id
|
||||
|
||||
#define ACTIONS(id) id
|
||||
|
||||
#define SHIFT(state_value) \
|
||||
{{ \
|
||||
.shift = { \
|
||||
.type = TSParseActionTypeShift, \
|
||||
.state = (state_value) \
|
||||
} \
|
||||
}}
|
||||
|
||||
#define SHIFT_REPEAT(state_value) \
|
||||
{{ \
|
||||
.shift = { \
|
||||
.type = TSParseActionTypeShift, \
|
||||
.state = (state_value), \
|
||||
.repetition = true \
|
||||
} \
|
||||
}}
|
||||
|
||||
#define SHIFT_EXTRA() \
|
||||
{{ \
|
||||
.shift = { \
|
||||
.type = TSParseActionTypeShift, \
|
||||
.extra = true \
|
||||
} \
|
||||
}}
|
||||
|
||||
#define REDUCE(symbol_name, children, precedence, prod_id) \
|
||||
{{ \
|
||||
.reduce = { \
|
||||
.type = TSParseActionTypeReduce, \
|
||||
.symbol = symbol_name, \
|
||||
.child_count = children, \
|
||||
.dynamic_precedence = precedence, \
|
||||
.production_id = prod_id \
|
||||
}, \
|
||||
}}
|
||||
|
||||
#define RECOVER() \
|
||||
{{ \
|
||||
.type = TSParseActionTypeRecover \
|
||||
}}
|
||||
|
||||
#define ACCEPT_INPUT() \
|
||||
{{ \
|
||||
.type = TSParseActionTypeAccept \
|
||||
}}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TREE_SITTER_PARSER_H_
|
||||
@@ -0,0 +1,97 @@
|
||||
==================
|
||||
Core syntax
|
||||
==================
|
||||
|
||||
io :: import "@std/io"
|
||||
|
||||
Status :: enum {
|
||||
ok
|
||||
bad
|
||||
}
|
||||
|
||||
Pair :: struct {
|
||||
left i32
|
||||
right i32
|
||||
}
|
||||
|
||||
sum func(a, b i32) i32 ! Status {
|
||||
result :: a + b
|
||||
if result > 0 {
|
||||
return result
|
||||
} else {
|
||||
return .bad
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
(source_file
|
||||
(import_declaration
|
||||
(identifier)
|
||||
(string
|
||||
(string_content)))
|
||||
(type_declaration
|
||||
(identifier)
|
||||
(enum_type
|
||||
(enum_body
|
||||
(enum_member
|
||||
(identifier))
|
||||
(enum_member
|
||||
(identifier)))))
|
||||
(type_declaration
|
||||
(identifier)
|
||||
(struct_type
|
||||
(record_body
|
||||
(record_field
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type)))
|
||||
(record_field
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type))))))
|
||||
(function_declaration
|
||||
(identifier)
|
||||
(parameter_list
|
||||
(parameter
|
||||
(identifier)
|
||||
(identifier)
|
||||
(type
|
||||
(builtin_type))))
|
||||
(type
|
||||
(builtin_type))
|
||||
(type
|
||||
(named_type
|
||||
(qualified_identifier
|
||||
(identifier))))
|
||||
(block
|
||||
(statement
|
||||
(constant_declaration
|
||||
(identifier)
|
||||
(expression
|
||||
(binary_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(identifier))))))
|
||||
(statement
|
||||
(if_statement
|
||||
(expression
|
||||
(binary_expression
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(integer))))
|
||||
(statement
|
||||
(block
|
||||
(statement
|
||||
(return_statement
|
||||
(expression
|
||||
(identifier))))))
|
||||
(statement
|
||||
(block
|
||||
(statement
|
||||
(return_statement
|
||||
(expression
|
||||
(enum_literal
|
||||
(identifier))))))))))))
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"grammars": [
|
||||
{
|
||||
"name": "brolang",
|
||||
"camelcase": "Brolang",
|
||||
"scope": "source.bro",
|
||||
"path": ".",
|
||||
"file-types": ["bro", "hon"],
|
||||
"highlights": "queries/highlights.scm"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"version": "0.1.0",
|
||||
"description": "Tree-sitter grammar for Brolang",
|
||||
"authors": [{"name": "Brolang contributors"}]
|
||||
},
|
||||
"bindings": {
|
||||
"c": false,
|
||||
"go": false,
|
||||
"java": false,
|
||||
"node": false,
|
||||
"python": false,
|
||||
"rust": false,
|
||||
"swift": false,
|
||||
"zig": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user