diff --git a/grammar.txt b/grammar.txt new file mode 100644 index 00000000..e65ba70f --- /dev/null +++ b/grammar.txt @@ -0,0 +1,1271 @@ +/** + * @file JavaScript grammar for tree-sitter + * @author Max Brunsfeld + * @author Amaan Qureshi + * @license MIT + */ + +/// +// @ts-check + +module.exports = grammar({ + name: 'javascript', + + externals: $ => [ + $._automatic_semicolon, + $._template_chars, + $._ternary_qmark, + $.html_comment, + '||', + // We use escape sequence and regex pattern to tell the scanner if we're currently inside a string or template string, in which case + // it should NOT parse html comments. + $.escape_sequence, + $.regex_pattern, + ], + + extras: $ => [ + $.comment, + $.html_comment, + /[\s\p{Zs}\uFEFF\u2028\u2029\u2060\u200B]/, + ], + + supertypes: $ => [ + $.statement, + $.declaration, + $.expression, + $.primary_expression, + $.pattern, + ], + + inline: $ => [ + $._call_signature, + $._formal_parameter, + $._expressions, + $._semicolon, + $._identifier, + $._reserved_identifier, + $._jsx_attribute, + $._jsx_element_name, + $._jsx_child, + $._jsx_element, + $._jsx_attribute_name, + $._jsx_attribute_value, + $._jsx_identifier, + $._lhs_expression, + ], + + precedences: $ => [ + [ + 'member', + 'call', + $.update_expression, + 'unary_void', + 'binary_exp', + 'binary_times', + 'binary_plus', + 'binary_shift', + 'binary_compare', + 'binary_relation', + 'binary_equality', + 'bitwise_and', + 'bitwise_xor', + 'bitwise_or', + 'logical_and', + 'logical_or', + 'ternary', + $.sequence_expression, + $.arrow_function, + ], + ['assign', $.primary_expression], + ['member', 'new', 'call', $.expression], + ['declaration', 'literal'], + [$.primary_expression, $.statement_block, 'object'], + [$.import_statement, $.import], + [$.export_statement, $.primary_expression], + [$.lexical_declaration, $.primary_expression], + ], + + conflicts: $ => [ + [$.primary_expression, $._property_name], + [$.primary_expression, $._property_name, $.arrow_function], + [$.primary_expression, $.arrow_function], + [$.primary_expression, $.method_definition], + [$.primary_expression, $.rest_pattern], + [$.primary_expression, $.pattern], + [$.primary_expression, $._for_header], + [$.array, $.array_pattern], + [$.object, $.object_pattern], + [$.assignment_expression, $.pattern], + [$.assignment_expression, $.object_assignment_pattern], + [$.labeled_statement, $._property_name], + [$.computed_property_name, $.array], + [$.binary_expression, $._initializer], + [$.class_static_block, $._property_name], + ], + + word: $ => $.identifier, + + rules: { + program: $ => seq( + optional($.hash_bang_line), + repeat($.statement), + ), + + hash_bang_line: _ => /#!.*/, + + // + // Export declarations + // + + export_statement: $ => choice( + seq( + 'export', + choice( + seq('*', $._from_clause), + seq($.namespace_export, $._from_clause), + seq($.export_clause, $._from_clause), + $.export_clause, + ), + $._semicolon, + ), + seq( + repeat(field('decorator', $.decorator)), + 'export', + choice( + field('declaration', $.declaration), + seq( + 'default', + choice( + field('declaration', $.declaration), + seq( + field('value', $.expression), + $._semicolon, + ), + ), + ), + ), + ), + ), + + namespace_export: $ => seq( + '*', 'as', $._module_export_name, + ), + + export_clause: $ => seq( + '{', + commaSep($.export_specifier), + optional(','), + '}', + ), + + export_specifier: $ => seq( + field('name', $._module_export_name), + optional(seq( + 'as', + field('alias', $._module_export_name), + )), + ), + + _module_export_name: $ => choice( + $.identifier, + $.string, + ), + + declaration: $ => choice( + $.function_declaration, + $.generator_function_declaration, + $.class_declaration, + $.lexical_declaration, + $.variable_declaration, + ), + + // + // Import declarations + // + + import: _ => token('import'), + + import_statement: $ => seq( + 'import', + choice( + seq($.import_clause, $._from_clause), + field('source', $.string), + ), + optional($.import_attribute), + $._semicolon, + ), + + import_clause: $ => choice( + $.namespace_import, + $.named_imports, + seq( + $.identifier, + optional(seq( + ',', + choice( + $.namespace_import, + $.named_imports, + ), + )), + ), + ), + + _from_clause: $ => seq( + 'from', field('source', $.string), + ), + + namespace_import: $ => seq( + '*', 'as', $.identifier, + ), + + named_imports: $ => seq( + '{', + commaSep($.import_specifier), + optional(','), + '}', + ), + + import_specifier: $ => choice( + field('name', $.identifier), + seq( + field('name', $._module_export_name), + 'as', + field('alias', $.identifier), + ), + ), + + import_attribute: $ => seq('with', $.object), + + // + // Statements + // + + statement: $ => choice( + $.export_statement, + $.import_statement, + $.debugger_statement, + $.expression_statement, + $.declaration, + $.statement_block, + + $.if_statement, + $.switch_statement, + $.for_statement, + $.for_in_statement, + $.while_statement, + $.do_statement, + $.try_statement, + $.with_statement, + + $.break_statement, + $.continue_statement, + $.return_statement, + $.throw_statement, + $.empty_statement, + $.labeled_statement, + ), + + expression_statement: $ => seq( + $._expressions, + $._semicolon, + ), + + variable_declaration: $ => seq( + 'var', + commaSep1($.variable_declarator), + $._semicolon, + ), + + lexical_declaration: $ => seq( + field('kind', choice('let', 'const')), + commaSep1($.variable_declarator), + $._semicolon, + ), + + variable_declarator: $ => seq( + field('name', choice($.identifier, $._destructuring_pattern)), + optional($._initializer), + ), + + statement_block: $ => prec.right(seq( + '{', + repeat($.statement), + '}', + optional($._automatic_semicolon), + )), + + else_clause: $ => seq('else', $.statement), + + if_statement: $ => prec.right(seq( + 'if', + field('condition', $.parenthesized_expression), + field('consequence', $.statement), + optional(field('alternative', $.else_clause)), + )), + + switch_statement: $ => seq( + 'switch', + field('value', $.parenthesized_expression), + field('body', $.switch_body), + ), + + for_statement: $ => seq( + 'for', + '(', + field('initializer', choice( + $.lexical_declaration, + $.variable_declaration, + $.expression_statement, + $.empty_statement, + )), + field('condition', choice( + $.expression_statement, + $.empty_statement, + )), + field('increment', optional($._expressions)), + ')', + field('body', $.statement), + ), + + for_in_statement: $ => seq( + 'for', + optional('await'), + $._for_header, + field('body', $.statement), + ), + + _for_header: $ => seq( + '(', + choice( + field('left', choice( + $._lhs_expression, + $.parenthesized_expression, + )), + seq( + field('kind', 'var'), + field('left', choice( + $.identifier, + $._destructuring_pattern, + )), + optional($._initializer), + ), + seq( + field('kind', choice('let', 'const')), + field('left', choice( + $.identifier, + $._destructuring_pattern, + )), + ), + ), + field('operator', choice('in', 'of')), + field('right', $._expressions), + ')', + ), + + while_statement: $ => seq( + 'while', + field('condition', $.parenthesized_expression), + field('body', $.statement), + ), + + do_statement: $ => prec.right(seq( + 'do', + field('body', $.statement), + 'while', + field('condition', $.parenthesized_expression), + optional($._semicolon), + )), + + try_statement: $ => seq( + 'try', + field('body', $.statement_block), + optional(field('handler', $.catch_clause)), + optional(field('finalizer', $.finally_clause)), + ), + + with_statement: $ => seq( + 'with', + field('object', $.parenthesized_expression), + field('body', $.statement), + ), + + break_statement: $ => seq( + 'break', + field('label', optional(alias($.identifier, $.statement_identifier))), + $._semicolon, + ), + + continue_statement: $ => seq( + 'continue', + field('label', optional(alias($.identifier, $.statement_identifier))), + $._semicolon, + ), + + debugger_statement: $ => seq( + 'debugger', + $._semicolon, + ), + + return_statement: $ => seq( + 'return', + optional($._expressions), + $._semicolon, + ), + + throw_statement: $ => seq( + 'throw', + $._expressions, + $._semicolon, + ), + + empty_statement: _ => ';', + + labeled_statement: $ => prec.dynamic(-1, seq( + field('label', alias(choice($.identifier, $._reserved_identifier), $.statement_identifier)), + ':', + field('body', $.statement), + )), + + // + // Statement components + // + + switch_body: $ => seq( + '{', + repeat(choice($.switch_case, $.switch_default)), + '}', + ), + + switch_case: $ => seq( + 'case', + field('value', $._expressions), + ':', + field('body', repeat($.statement)), + ), + + switch_default: $ => seq( + 'default', + ':', + field('body', repeat($.statement)), + ), + + catch_clause: $ => seq( + 'catch', + optional(seq('(', field('parameter', choice($.identifier, $._destructuring_pattern)), ')')), + field('body', $.statement_block), + ), + + finally_clause: $ => seq( + 'finally', + field('body', $.statement_block), + ), + + parenthesized_expression: $ => seq( + '(', + $._expressions, + ')', + ), + + // + // Expressions + // + _expressions: $ => choice( + $.expression, + $.sequence_expression, + ), + + expression: $ => choice( + $.primary_expression, + $.glimmer_template, + $._jsx_element, + $.assignment_expression, + $.augmented_assignment_expression, + $.await_expression, + $.unary_expression, + $.binary_expression, + $.ternary_expression, + $.update_expression, + $.new_expression, + $.yield_expression, + ), + + primary_expression: $ => choice( + $.subscript_expression, + $.member_expression, + $.parenthesized_expression, + $._identifier, + alias($._reserved_identifier, $.identifier), + $.this, + $.super, + $.number, + $.string, + $.template_string, + $.regex, + $.true, + $.false, + $.null, + $.object, + $.array, + $.function_expression, + $.arrow_function, + $.generator_function, + $.class, + $.meta_property, + $.call_expression, + ), + + yield_expression: $ => prec.right(seq( + 'yield', + choice( + seq('*', $.expression), + optional($.expression), + ))), + + object: $ => prec('object', seq( + '{', + commaSep(optional(choice( + $.pair, + $.spread_element, + $.method_definition, + alias( + choice($.identifier, $._reserved_identifier), + $.shorthand_property_identifier, + ), + ))), + '}', + )), + + object_pattern: $ => prec('object', seq( + '{', + commaSep(optional(choice( + $.pair_pattern, + $.rest_pattern, + $.object_assignment_pattern, + alias( + choice($.identifier, $._reserved_identifier), + $.shorthand_property_identifier_pattern, + ), + ))), + '}', + )), + + assignment_pattern: $ => seq( + field('left', $.pattern), + '=', + field('right', $.expression), + ), + + object_assignment_pattern: $ => seq( + field('left', choice( + alias(choice($._reserved_identifier, $.identifier), $.shorthand_property_identifier_pattern), + $._destructuring_pattern, + )), + '=', + field('right', $.expression), + ), + + array: $ => seq( + '[', + commaSep(optional(choice( + $.expression, + $.spread_element, + ))), + ']', + ), + + array_pattern: $ => seq( + '[', + commaSep(optional(choice( + $.pattern, + $.assignment_pattern, + ))), + ']', + ), + + glimmer_template: $ => choice( + seq( + field('open_tag', $.glimmer_opening_tag), + field('content', repeat($._glimmer_template_content)), + field('close_tag', $.glimmer_closing_tag), + ), + // empty template has no content + // + seq( + field('open_tag', $.glimmer_opening_tag), + field('close_tag', $.glimmer_closing_tag), + ), + ), + + _glimmer_template_content: _ => /.{1,}/, + glimmer_opening_tag: _ => seq(''), + + _jsx_element: $ => choice($.jsx_element, $.jsx_self_closing_element), + + jsx_element: $ => seq( + field('open_tag', $.jsx_opening_element), + repeat($._jsx_child), + field('close_tag', $.jsx_closing_element), + ), + + // Should not contain new lines and should not start or end with a space + jsx_text: _ => choice( + /[^{}<>\n& ]([^{}<>\n&]*[^{}<>\n& ])?/, + /\/\/[^\n]*/, + ), + + // An entity can be named, numeric (decimal), or numeric (hexadecimal). The + // longest entity name is 29 characters long, and the HTML spec says that + // no more will ever be added. + html_character_reference: _ => /&(#([xX][0-9a-fA-F]{1,6}|[0-9]{1,5})|[A-Za-z]{1,30});/, + + jsx_expression: $ => seq( + '{', + optional(choice( + $.expression, + $.sequence_expression, + $.spread_element, + )), + '}', + ), + + _jsx_child: $ => choice( + $.jsx_text, + $.html_character_reference, + $._jsx_element, + $.jsx_expression, + ), + + jsx_opening_element: $ => prec.dynamic(-1, seq( + '<', + optional(seq( + field('name', $._jsx_element_name), + repeat(field('attribute', $._jsx_attribute)), + )), + '>', + )), + + jsx_identifier: _ => /[a-zA-Z_$][a-zA-Z\d_$]*-[a-zA-Z\d_$\-]*/, + + _jsx_identifier: $ => choice( + alias($.jsx_identifier, $.identifier), + $.identifier, + ), + + nested_identifier: $ => prec('member', seq( + field('object', choice($.identifier, alias($.nested_identifier, $.member_expression))), + '.', + field('property', alias($.identifier, $.property_identifier)), + )), + + jsx_namespace_name: $ => seq($._jsx_identifier, ':', $._jsx_identifier), + + _jsx_element_name: $ => choice( + $._jsx_identifier, + alias($.nested_identifier, $.member_expression), + $.jsx_namespace_name, + ), + + jsx_closing_element: $ => seq( + '', + ), + + jsx_self_closing_element: $ => seq( + '<', + field('name', $._jsx_element_name), + repeat(field('attribute', $._jsx_attribute)), + '/>', + ), + + _jsx_attribute: $ => choice($.jsx_attribute, $.jsx_expression), + + _jsx_attribute_name: $ => choice(alias($._jsx_identifier, $.property_identifier), $.jsx_namespace_name), + + jsx_attribute: $ => seq( + $._jsx_attribute_name, + optional(seq( + '=', + $._jsx_attribute_value, + )), + ), + + _jsx_string: $ => choice( + seq( + '"', + repeat(choice( + alias($.unescaped_double_jsx_string_fragment, $.string_fragment), + $.html_character_reference, + )), + '"', + ), + seq( + '\'', + repeat(choice( + alias($.unescaped_single_jsx_string_fragment, $.string_fragment), + $.html_character_reference, + )), + '\'', + ), + ), + + // Workaround to https://github.com/tree-sitter/tree-sitter/issues/1156 + // We give names to the token() constructs containing a regexp + // so as to obtain a node in the CST. + // + unescaped_double_jsx_string_fragment: _ => token.immediate(prec(1, /([^"&]|&[^#A-Za-z])+/)), + + // same here + unescaped_single_jsx_string_fragment: _ => token.immediate(prec(1, /([^'&]|&[^#A-Za-z])+/)), + + _jsx_attribute_value: $ => choice( + alias($._jsx_string, $.string), + $.jsx_expression, + $._jsx_element, + ), + + class: $ => prec('literal', seq( + repeat(field('decorator', $.decorator)), + 'class', + field('name', optional($.identifier)), + optional($.class_heritage), + field('body', $.class_body), + )), + + class_declaration: $ => prec('declaration', seq( + repeat(field('decorator', $.decorator)), + 'class', + field('name', $.identifier), + optional($.class_heritage), + field('body', $.class_body), + optional($._automatic_semicolon), + )), + + class_heritage: $ => seq('extends', $.expression), + + function_expression: $ => prec('literal', seq( + optional('async'), + 'function', + field('name', optional($.identifier)), + $._call_signature, + field('body', $.statement_block), + )), + + function_declaration: $ => prec.right('declaration', seq( + optional('async'), + 'function', + field('name', $.identifier), + $._call_signature, + field('body', $.statement_block), + optional($._automatic_semicolon), + )), + + generator_function: $ => prec('literal', seq( + optional('async'), + 'function', + '*', + field('name', optional($.identifier)), + $._call_signature, + field('body', $.statement_block), + )), + + generator_function_declaration: $ => prec.right('declaration', seq( + optional('async'), + 'function', + '*', + field('name', $.identifier), + $._call_signature, + field('body', $.statement_block), + optional($._automatic_semicolon), + )), + + arrow_function: $ => seq( + optional('async'), + choice( + field('parameter', choice( + alias($._reserved_identifier, $.identifier), + $.identifier, + )), + $._call_signature, + ), + '=>', + field('body', choice( + $.expression, + $.statement_block, + )), + ), + + // Override + _call_signature: $ => field('parameters', $.formal_parameters), + _formal_parameter: $ => choice($.pattern, $.assignment_pattern), + + optional_chain: _ => '?.', + + call_expression: $ => choice( + prec('call', seq( + field('function', choice($.expression, $.import)), + field('arguments', choice($.arguments, $.template_string)), + )), + prec('member', seq( + field('function', $.primary_expression), + field('optional_chain', $.optional_chain), + field('arguments', $.arguments), + )), + ), + + new_expression: $ => prec.right('new', seq( + 'new', + field('constructor', choice($.primary_expression, $.new_expression)), + field('arguments', optional(prec.dynamic(1, $.arguments))), + )), + + await_expression: $ => prec('unary_void', seq( + 'await', + $.expression, + )), + + member_expression: $ => prec('member', seq( + field('object', choice($.expression, $.primary_expression, $.import)), + choice('.', field('optional_chain', $.optional_chain)), + field('property', choice( + $.private_property_identifier, + alias($.identifier, $.property_identifier))), + )), + + subscript_expression: $ => prec.right('member', seq( + field('object', choice($.expression, $.primary_expression)), + optional(field('optional_chain', $.optional_chain)), + '[', field('index', $._expressions), ']', + )), + + _lhs_expression: $ => choice( + $.member_expression, + $.subscript_expression, + $._identifier, + alias($._reserved_identifier, $.identifier), + $._destructuring_pattern, + ), + + assignment_expression: $ => prec.right('assign', seq( + field('left', choice($.parenthesized_expression, $._lhs_expression)), + '=', + field('right', $.expression), + )), + + _augmented_assignment_lhs: $ => choice( + $.member_expression, + $.subscript_expression, + alias($._reserved_identifier, $.identifier), + $.identifier, + $.parenthesized_expression, + ), + + augmented_assignment_expression: $ => prec.right('assign', seq( + field('left', $._augmented_assignment_lhs), + field('operator', choice('+=', '-=', '*=', '/=', '%=', '^=', '&=', '|=', '>>=', '>>>=', + '<<=', '**=', '&&=', '||=', '??=')), + field('right', $.expression), + )), + + _initializer: $ => seq( + '=', + field('value', $.expression), + ), + + _destructuring_pattern: $ => choice( + $.object_pattern, + $.array_pattern, + ), + + spread_element: $ => seq('...', $.expression), + + ternary_expression: $ => prec.right('ternary', seq( + field('condition', $.expression), + alias($._ternary_qmark, '?'), + field('consequence', $.expression), + ':', + field('alternative', $.expression), + )), + + binary_expression: $ => choice( + ...[ + ['&&', 'logical_and'], + ['||', 'logical_or'], + ['>>', 'binary_shift'], + ['>>>', 'binary_shift'], + ['<<', 'binary_shift'], + ['&', 'bitwise_and'], + ['^', 'bitwise_xor'], + ['|', 'bitwise_or'], + ['+', 'binary_plus'], + ['-', 'binary_plus'], + ['*', 'binary_times'], + ['/', 'binary_times'], + ['%', 'binary_times'], + ['**', 'binary_exp', 'right'], + ['<', 'binary_relation'], + ['<=', 'binary_relation'], + ['==', 'binary_equality'], + ['===', 'binary_equality'], + ['!=', 'binary_equality'], + ['!==', 'binary_equality'], + ['>=', 'binary_relation'], + ['>', 'binary_relation'], + ['??', 'ternary'], + ['instanceof', 'binary_relation'], + ['in', 'binary_relation'], + ].map(([operator, precedence, associativity]) => + (associativity === 'right' ? prec.right : prec.left)(precedence, seq( + field('left', operator === 'in' ? choice($.expression, $.private_property_identifier) : $.expression), + field('operator', operator), + field('right', $.expression), + )), + ), + ), + + unary_expression: $ => prec.left('unary_void', seq( + field('operator', choice('!', '~', '-', '+', 'typeof', 'void', 'delete')), + field('argument', $.expression), + )), + + update_expression: $ => prec.left(choice( + seq( + field('argument', $.expression), + field('operator', choice('++', '--')), + ), + seq( + field('operator', choice('++', '--')), + field('argument', $.expression), + ), + )), + + sequence_expression: $ => prec.right(commaSep1($.expression)), + + // + // Primitives + // + + string: $ => choice( + seq( + '"', + repeat(choice( + alias($.unescaped_double_string_fragment, $.string_fragment), + $.escape_sequence, + )), + '"', + ), + seq( + '\'', + repeat(choice( + alias($.unescaped_single_string_fragment, $.string_fragment), + $.escape_sequence, + )), + '\'', + ), + ), + + // Workaround to https://github.com/tree-sitter/tree-sitter/issues/1156 + // We give names to the token() constructs containing a regexp + // so as to obtain a node in the CST. + // + unescaped_double_string_fragment: _ => token.immediate(prec(1, /[^"\\\r\n]+/)), + + // same here + unescaped_single_string_fragment: _ => token.immediate(prec(1, /[^'\\\r\n]+/)), + + 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]+\}/, + /[\r?][\n\u2028\u2029]/, + ), + )), + + // http://stackoverflow.com/questions/13014947/regex-to-match-a-c-style-multiline-comment/36328890#36328890 + comment: _ => token(choice( + seq('//', /[^\r\n\u2028\u2029]*/), + seq( + '/*', + /[^*]*\*+([^/*][^*]*\*+)*/, + '/', + ), + )), + + template_string: $ => seq( + '`', + repeat(choice( + alias($._template_chars, $.string_fragment), + $.escape_sequence, + $.template_substitution, + )), + '`', + ), + + template_substitution: $ => seq( + '${', + $._expressions, + '}', + ), + + regex: $ => seq( + '/', + field('pattern', $.regex_pattern), + token.immediate(prec(1, '/')), + optional(field('flags', $.regex_flags)), + ), + + regex_pattern: _ => token.immediate(prec(-1, + repeat1(choice( + seq( + '[', + repeat(choice( + seq('\\', /./), // escaped character + /[^\]\n\\]/, // any character besides ']' or '\n' + )), + ']', + ), // square-bracket-delimited character class + seq('\\', /./), // escaped character + /[^/\\\[\n]/, // any character besides '[', '\', '/', '\n' + )), + )), + + regex_flags: _ => token.immediate(/[a-z]+/), + + number: _ => { + const hexLiteral = seq( + choice('0x', '0X'), + /[\da-fA-F](_?[\da-fA-F])*/, + ); + + const decimalDigits = /\d(_?\d)*/; + const signedInteger = seq(optional(choice('-', '+')), decimalDigits); + const exponentPart = seq(choice('e', 'E'), signedInteger); + + const binaryLiteral = seq(choice('0b', '0B'), /[0-1](_?[0-1])*/); + + const octalLiteral = seq(choice('0o', '0O'), /[0-7](_?[0-7])*/); + + const bigintLiteral = seq(choice(hexLiteral, binaryLiteral, octalLiteral, decimalDigits), 'n'); + + const decimalIntegerLiteral = choice( + '0', + seq(optional('0'), /[1-9]/, optional(seq(optional('_'), decimalDigits))), + ); + + const decimalLiteral = choice( + seq(decimalIntegerLiteral, '.', optional(decimalDigits), optional(exponentPart)), + seq('.', decimalDigits, optional(exponentPart)), + seq(decimalIntegerLiteral, exponentPart), + seq(decimalDigits), + ); + + return token(choice( + hexLiteral, + decimalLiteral, + binaryLiteral, + octalLiteral, + bigintLiteral, + )); + }, + + // 'undefined' is syntactically a regular identifier in JavaScript. + // However, its main use is as the read-only global variable whose + // value is [undefined], for which there's no literal representation + // unlike 'null'. We gave it its own rule so it's easy to + // highlight in text editors and other applications. + _identifier: $ => choice( + $.undefined, + $.identifier, + ), + + identifier: _ => { + // eslint-disable-next-line max-len + const alpha = /[^\x00-\x1F\s\p{Zs}0-9:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\}/; + // eslint-disable-next-line max-len + const alphanumeric = /[^\x00-\x1F\s\p{Zs}:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\}/; + return token(seq(alpha, repeat(alphanumeric))); + }, + + private_property_identifier: _ => { + // eslint-disable-next-line max-len + const alpha = /[^\x00-\x1F\s\p{Zs}0-9:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\}/; + // eslint-disable-next-line max-len + const alphanumeric = /[^\x00-\x1F\s\p{Zs}:;`"'@#.,|^&<=>+\-*/\\%?!~()\[\]{}\uFEFF\u2060\u200B\u2028\u2029]|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\}/; + return token(seq('#', alpha, repeat(alphanumeric))); + }, + + meta_property: _ => seq('new', '.', 'target'), + + this: _ => 'this', + super: _ => 'super', + true: _ => 'true', + false: _ => 'false', + null: _ => 'null', + undefined: _ => 'undefined', + + // + // Expression components + // + + arguments: $ => seq( + '(', + commaSep(optional(choice($.expression, $.spread_element))), + ')', + ), + + decorator: $ => seq( + '@', + choice( + $.identifier, + alias($.decorator_member_expression, $.member_expression), + alias($.decorator_call_expression, $.call_expression), + ), + ), + + decorator_member_expression: $ => prec('member', seq( + field('object', choice( + $.identifier, + alias($.decorator_member_expression, $.member_expression), + )), + '.', + field('property', alias($.identifier, $.property_identifier)), + )), + + decorator_call_expression: $ => prec('call', seq( + field('function', choice( + $.identifier, + alias($.decorator_member_expression, $.member_expression), + )), + field('arguments', $.arguments), + )), + + class_body: $ => seq( + '{', + repeat(choice( + seq(field('member', $.method_definition), optional(';')), + seq(field('member', $.field_definition), $._semicolon), + field('member', $.class_static_block), + field('template', $.glimmer_template), + ';', + )), + '}', + ), + + field_definition: $ => seq( + repeat(field('decorator', $.decorator)), + optional('static'), + field('property', $._property_name), + optional($._initializer), + ), + + formal_parameters: $ => seq( + '(', + optional(seq( + commaSep1($._formal_parameter), + optional(','), + )), + ')', + ), + + class_static_block: $ => seq( + 'static', + optional($._automatic_semicolon), + field('body', $.statement_block), + ), + + // This negative dynamic precedence ensures that during error recovery, + // unfinished constructs are generally treated as literal expressions, + // not patterns. + pattern: $ => prec.dynamic(-1, choice( + $._lhs_expression, + $.rest_pattern, + )), + + rest_pattern: $ => prec.right(seq( + '...', + $._lhs_expression, + )), + + method_definition: $ => seq( + repeat(field('decorator', $.decorator)), + optional(choice( + 'static', + alias(token(seq('static', /\s+/, 'get', /\s*\n/)), 'static get'), + )), + optional('async'), + optional(choice('get', 'set', '*')), + field('name', $._property_name), + field('parameters', $.formal_parameters), + field('body', $.statement_block), + ), + + pair: $ => seq( + field('key', $._property_name), + ':', + field('value', $.expression), + ), + + pair_pattern: $ => seq( + field('key', $._property_name), + ':', + field('value', choice($.pattern, $.assignment_pattern)), + ), + + _property_name: $ => choice( + alias( + choice($.identifier, $._reserved_identifier), + $.property_identifier, + ), + $.private_property_identifier, + $.string, + $.number, + $.computed_property_name, + ), + + computed_property_name: $ => seq( + '[', + $.expression, + ']', + ), + + _reserved_identifier: _ => choice( + 'get', + 'set', + 'async', + 'static', + 'export', + 'let', + ), + + _semicolon: $ => choice($._automatic_semicolon, ';'), + }, +}); + +/** + * Creates a rule to match one or more of the rules separated by a comma + * + * @param {Rule} rule + * + * @return {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 {Rule} rule + * + * @return {ChoiceRule} + * + */ +function commaSep(rule) { + return optional(commaSep1(rule)); +} diff --git a/src/rust/Grammar.re b/src/rust/Grammar.re deleted file mode 100644 index 1962a7b0..00000000 --- a/src/rust/Grammar.re +++ /dev/null @@ -1,639 +0,0 @@ -/* Notes about deviations from the Rust grammar - - * Rust defines if expressions recursively - we unfortunately cannot and had to do a combination of alts and stars - * Rust disallows struct expressions from the scrutinee type (all exp except struct expression) - we assume this is a parser limitation and allow generic exp - * Block exp - rust references block exp in other "sorts" (items) - we instead "rebuild" the block exp definition (bracket, stat, bracket) for each sort where it is used - * Rust comparison operators "require parentheses for associativity" - using tylr assoc=none for this - * Rust has optional semicolons after block exps for a statement - we are always requiring semicolons to simplify so we don't need to discriminate b/w exp with block and exp without block - * Rust allows for optional commas following exp_with_block in a match statement - for consistency sake we will reqire comma in match regardless of exp_with_block or exp_without_block - * Rust has a separate "type no bounds" which only allows for no bound or single bounded types. This is used only in reference types and raw pointer types. Our current guess is that this is a type system limitation so we are (for now) ignoring it and simply allowing any type where rust only allows "type no bounds" - * Rust defines statement as an alt one option of which is just "Item" - to allow for this in our grammar definition we are making item a submodule of statement and inlining all the forms of item into statement's operand. However, the item _module form requires "Item.atom" so we will keep Item as a tylr sort. - */ - -open Util; - -module Sym = { - include Sym; - type t = Sym.t(Label.t, Sort.t); -}; -module Regex = { - include Regex; - type t = Regex.t(Sym.t); -}; -open Regex; - -let plus = (reg: Regex.t) => seq([reg, star(reg)]); - -let p = (~a: option(Dir.t)=?, r: t) => (a, r); - -let t = (lbl: Label.t) => Regex.atom(Sym.t(lbl)); -let nt = (srt: Sort.t) => Regex.atom(Sym.nt(srt)); - -let c = (~p=Padding.none, s) => t(Label.const(~padding=p, s)); - -//TODO: abi should actually be a string literal but need to wait on David for that -let abi = t(Id_lower); - -//Path exps -let typ_atom = nt(Sort.of_str("Typ")); - -let path_ident_segment = - alt([ - t(Id_lower), - c("super"), - c("self"), - c("Self"), - c("crate"), - c("$crate"), - ]); - -let typ_path_fn_inputs = - seq([typ_atom, star(seq([c(","), typ_atom])), opt(c(","))]); -let typ_path_fn = - seq([ - c("("), - opt(typ_path_fn_inputs), - c(")"), - opt(seq([c("->"), typ_atom])), - ]); -let typ_path_segment = - seq([path_ident_segment, opt(seq([c("::"), typ_path_fn]))]); -let typ_path = - seq([ - opt(c("::")), - typ_path_segment, - star(seq([c("::"), typ_path_segment])), - ]); - -let path_exp_segment = seq([path_ident_segment]); -let path_in_exp = - seq([ - opt(c("::")), - path_exp_segment, - star(seq([c("::"), path_exp_segment])), - ]); - -let qualified_path_typ = - seq([ - c("<"), - nt(Sort.of_str("Typ")), - opt(seq([c("as"), typ_path])), - c(">"), - ]); - -let qualified_path_in_exp = - seq([qualified_path_typ, plus(seq([c("::"), path_exp_segment]))]); -let path_exp = alt([path_in_exp, qualified_path_in_exp]); - -module type SORT = { - let atom: unit => Regex.t; - let sort: unit => Sort.t; - let tbl: unit => Prec.Table.t(Regex.t); -}; - -module rec Stat: SORT = { - let sort = () => Sort.of_str("Stat"); - let atom = () => nt(sort()); - - let block_exp = seq([c("{"), plus(atom()), c("}")]); - - let let_stat = - seq([ - c("let"), - Pat.atom(), - opt(seq([c(":"), Typ.atom()])), - opt(seq([c("="), Exp.atom(), opt(seq([c("else"), block_exp]))])), - c(";"), - ]); - - let operand = alt([let_stat, Item.atom()]); - let tbl = () => [p(seq([Exp.atom(), c(";")])), p(operand)]; -} -and Item: SORT = { - let sort = () => Sort.of_str("Item"); - let atom = () => nt(sort()); - - [@warning "-32"] - let comma_sep = seq([atom(), star(seq([c(","), atom()]))]); - let block_exp = seq([c("{"), plus(atom()), c("}")]); - - //Functions! - let func_qualifier = alt([c("const"), c("async"), c("unsafe")]); - - let self_param = - alt([ - //Shorthand self - seq([c("&"), opt(c("mut")), c("self")]), - //Typed self - seq([opt(c("mut")), c("self"), c(":"), Typ.atom()]), - ]); - let func_param = - alt([ - seq([t(Id_lower), c(":"), alt([Typ.atom(), c("...")])]), - c("..."), - ]); - let func_params = - alt([ - self_param, - seq([ - opt(seq([self_param, c(",")])), - func_param, - star(seq([c(","), func_param])), - opt(c(",")), - ]), - ]); - let func_return_typ = seq([c("->"), Typ.atom()]); - - let func = - seq([ - opt(func_qualifier), - c("fn"), - t(Id_lower), - c("("), - opt(func_params), - c(")"), - func_return_typ, - alt([block_exp, c(";")]), - ]); - //End functions - - //Crates - let extern_crate = - seq([ - c("extern"), - c("crate"), - alt([t(Id_lower), c("self")]), - opt(seq([c("as"), alt([t(Id_lower), c("_")])])), - c(";"), - ]); - - //Module - let _module = - alt([ - seq([opt(c("unsafe")), c("mod"), t(Id_lower), c("l")]), - seq([ - opt(c("unsafe")), - c("mod"), - t(Id_lower), - c("{"), - star(atom()), - c("}"), - ]), - ]); - - let typ_alias = - seq([ - c("type"), - t(Id_lower), - opt(seq([c("="), Typ.atom()])), - c(";"), - ]); - - //Struct - let struct_field = seq([t(Id_lower), c(":"), Typ.atom()]); - let struct_fields = - seq([struct_field, star(seq([c(","), struct_field])), opt(c(","))]); - let struct_struct = - seq([ - c("struct"), - t(Id_lower), - alt([seq([c("{"), opt(struct_fields), c("}")]), c(";")]), - ]); - - let tuple_field = Typ.atom(); - let tuple_fields = - seq([tuple_field, star(seq([c(","), tuple_field])), opt(c(","))]); - let tuple_struct = - seq([ - c("struct"), - t(Id_lower), - c("("), - opt(tuple_fields), - c(")"), - c(";"), - ]); - let _struct = alt([struct_struct, tuple_struct]); - - //Enums - let enum_item_tuple = seq([c("("), opt(tuple_fields), c(")")]); - let enum_item_struct = seq([c("{"), opt(struct_fields), c("}")]); - let enum_item = - seq([ - t(Id_lower), - opt(alt([enum_item_tuple, enum_item_struct])), - opt(seq([c("="), Exp.atom()])), - ]); - let enum_items = - seq([enum_item, star(seq([c(","), enum_item])), opt(c(","))]); - let enum = - seq([c("enum"), t(Id_lower), c("{"), opt(enum_items), c("}")]); - - //Unions - let union = - seq([c("union"), t(Id_lower), c("{"), struct_fields, c("}")]); - - //Consts - let const = - seq([ - c("const"), - alt([t(Id_lower), c("_")]), - c(":"), - Typ.atom(), - opt(seq([c("="), Exp.atom()])), - c(";"), - ]); - - //static - let static_item = - seq([ - c("static"), - opt(c("mut")), - t(Id_lower), - c(":"), - opt(seq([c("="), Exp.atom()])), - c(";"), - ]); - - //Traits - let associated_item = alt([typ_alias, const, func]); - let trait = - seq([ - opt(c("unsafe")), - c("trait"), - t(Id_lower), - c("{"), - star(associated_item), - c("}"), - ]); - - //Implementations - let inherent_impl = - seq([c("impl"), c("{"), star(associated_item), c("}")]); - - let trait_impl = - seq([ - opt(c("unsafe")), - c("impl"), - opt(c("!")), - typ_path, - c("for"), - Typ.atom(), - ]); - - let impl = alt([inherent_impl, trait_impl]); - - //Externs - let extern_item = alt([static_item, func]); - let extern_block = - seq([ - opt(c("unsafe")), - c("extern"), - opt(abi), - c("{"), - star(extern_item), - c("}"), - ]); - - let operand = - alt([ - extern_block, - impl, - trait, - static_item, - const, - union, - enum, - _struct, - typ_alias, - func, - extern_crate, - _module, - ]); - - let tbl = () => [p(operand)]; -} -and Typ: SORT = { - let sort = () => Sort.of_str("Typ"); - let atom = () => nt(sort()); - - let typ_path_fn_inputs = - seq([atom(), star(seq([c(","), atom()])), opt(c(","))]); - let typ_path_fn = - seq([ - c("("), - opt(typ_path_fn_inputs), - c(")"), - opt(seq([c("->"), atom()])), - ]); - let typ_path_segment = - seq([path_ident_segment, opt(seq([c("::"), typ_path_fn]))]); - let typ_path = - seq([ - opt(c("::")), - typ_path_segment, - star(seq([c("::"), typ_path_segment])), - ]); - - let paren_typ = seq([c("("), atom(), c(")")]); - - let trait_bound = seq([opt(c("?")), typ_path]); - //TODO: lifetimes - let typ_param_bound = trait_bound; //Lifetime | TraitBound - - let typ_param_bounds = - seq([ - typ_param_bound, - star(seq([c("+"), typ_param_bound])), - opt(c("+")), - ]); - - let impl_trait_typ = seq([c("impl"), typ_param_bounds]); - let impl_trait_typ_one_bound = seq([c("impl"), trait_bound]); - - let trait_obj_typ_one_bound = seq([opt(c("dyn")), trait_bound]); - let trait_obj_typ = seq([opt(c("dyn")), typ_param_bounds]); - - let tuple_typ = - alt([ - seq([c("("), c(")")]), - seq([c("("), plus(seq([atom(), c(",")])), opt(atom()), c(")")]), - ]); - - let never_typ = c("!"); - - let raw_pointer_typ = - seq([c("*"), alt([c("mut"), c("const")]), atom()]); - - //TODO: lifetimes - let reference_typ = seq([c("&"), opt(c("mut")), atom()]); - - let array_typ = seq([c("["), atom(), c(";"), Exp.atom(), c("]")]); - let slice_typ = seq([c("["), atom(), c("]")]); - - let inferred_typ = c("_"); - - let qualified_path_typ = - seq([c("<"), atom(), opt(seq([c("as"), typ_path])), c(">")]); - let qualified_path_in_typ = - seq([qualified_path_typ, plus(seq([c("::"), typ_path_segment]))]); - - let fun_typ_qualifiers = - seq([opt(c("unsafe")), opt(seq([c("extern"), opt(abi)]))]); - - let maybe_named_param = - seq([opt(seq([alt([t(Id_lower), c("_")]), c(":")])), atom()]); - let maybe_named_fun_params_variadic = - seq([ - star(seq([maybe_named_param, c(",")])), - maybe_named_param, - c(","), - c("..."), - ]); - let maybe_named_fun_params = - seq([ - maybe_named_param, - star(seq([c(","), maybe_named_param])), - opt(c(",")), - ]); - let fun_params_maybe_named_variadic = - alt([maybe_named_fun_params, maybe_named_fun_params_variadic]); - - let bare_fun_return_typ = seq([c("->"), atom()]); - - let bare_fun_typ = - seq([ - fun_typ_qualifiers, - c("fn"), - c("("), - opt(fun_params_maybe_named_variadic), - opt(bare_fun_return_typ), - ]); - - let operand = - alt([ - impl_trait_typ, - trait_obj_typ, - paren_typ, - impl_trait_typ_one_bound, - trait_obj_typ_one_bound, - typ_path, - tuple_typ, - never_typ, - raw_pointer_typ, - reference_typ, - array_typ, - slice_typ, - inferred_typ, - qualified_path_in_typ, - bare_fun_typ, - ]); - - let tbl = () => [p(operand)]; -} -and Exp: SORT = { - let sort = () => Sort.of_str("Exp"); - let atom = () => nt(sort()); - - [@warning "-32"] - let comma_sep = seq([atom(), star(seq([c(","), atom()]))]); - - let block_exp = seq([c("{"), Stat.atom(), c("}")]); - - let lone_if_exp = seq([c("if"), atom(), block_exp]); - let if_exp = - seq([ - lone_if_exp, - opt( - seq([c("else"), star(seq([lone_if_exp, c("else")])), block_exp]), - ), - //NOTE: initial idea for non-recurisve if decl; limited by ambiguity with the "else"s in the molds - // star(seq([c("else"), exp, block_exp])), - // opt(seq([c("else"), block_exp])), - ]); - - let loop_exp = [ - //Infinite loop - seq([c("loop"), block_exp]), - //Predicate (while) loops - seq([c("while"), atom(), block_exp]), - //Predicate pattern (while let) loops - seq([c("while"), c("let"), Pat.atom(), c("="), Exp.atom(), block_exp]), - //iterator (for) loops - seq([c("for"), Pat.atom(), c("in"), atom(), block_exp]), - ]; - - let match_arm = seq([Pat.atom(), opt(seq([c("if"), atom()]))]); - - let match_arms = - seq([ - star(seq([match_arm, c("=>"), atom(), c(",")])), - match_arm, - c("=>"), - atom(), - opt(c(",")), - ]); - - let match_exp = - seq([c("match"), atom(), c("{"), opt(match_arms), c("}")]); - - let exp_with_block = [match_exp] @ loop_exp @ [if_exp, block_exp]; - - let operand = - alt( - [ - t(Int_lit), - t(Float_lit), - t(Id_lower), - c("break"), - c("continue"), - //Function call - seq([atom(), c("("), opt(comma_sep), c(")")]), - //Parenthetical expression - seq([c("("), atom(), c(")")]), - //Arrays - seq([c("["), atom(), opt(star(atom())), c("]")]), - ] - @ exp_with_block, - ); - - let tokc_alt = ss => alt(List.map(c, ss)); - let unary_op = tokc_alt(["&", "&mut", "*", "-", "!"]); - let mult_ops = tokc_alt(["*", "/", "%"]); - let add_ops = tokc_alt(["+", "-"]); - - let compare_ops = tokc_alt(["==", "!=", ">", "<", ">=", "<="]); - - let assignment_ops = - tokc_alt([ - "=", - "+=", - "-=", - "*=", - "/=", - "%=", - "&=", - "|=", - "^=", - "<<=", - ">>=", - ]); - - //NOTE: tbl goes from weak -> strong precedence - //NOTE: exp without block > exp with block (prec) - let tbl = () => [ - //return - p(seq([c("return"), atom()])), - //assignment ops - p(seq([atom(), assignment_ops, atom()])), - //bool or - p(seq([atom(), c("||"), atom()])), - //bool and - p(seq([atom(), c("&&"), atom()])), - //comparison ops - p(seq([atom(), compare_ops, atom()])), - //add - p(~a=L, seq([atom(), add_ops, atom()])), - //mult - p(~a=L, seq([atom(), mult_ops, atom()])), - //type cast exp - p(~a=L, seq([atom(), c("as"), Typ.atom()])), - //unary operators - p(seq([unary_op, atom()])), - //question mark op - p(seq([c("?"), atom()])), - p(operand), - ]; -} -and Pat: SORT = { - let sort = () => Sort.of_str("Pat"); - let atom = () => nt(sort()); - - let ident_pat = - seq([ - opt(c("ref")), - opt(c("mut")), - t(Id_lower), - opt(seq([c("@"), atom()])), - ]); - - //NOTE: actual rust grammar has a star(outer_attr) then c(..) but since we don't have metaprogramming or attrs, struct_pat_et_cetera is just .. for us - let struct_pat_et_cetera = c(".."); - - let struct_pat_field = - alt([ - seq([t(Int_lit), c(":"), atom()]), - seq([t(Id_lower), c("l"), atom()]), - seq([opt(c("ref")), opt(c("mut")), t(Id_lower)]), - ]); - let struct_pat_fields = - seq([struct_pat_field, star(seq([c(","), struct_pat_field]))]); - let struct_pat_elements = - alt([ - seq([ - struct_pat_fields, - opt(alt([c(","), seq([c(","), struct_pat_et_cetera])])), - ]), - struct_pat_et_cetera, - ]); - let struct_pat = - seq([path_in_exp, c("{"), opt(struct_pat_elements), c("}")]); - - let tuple_struct_items = - seq([atom(), star(seq([c(","), atom()])), opt(c(","))]); - let tuple_struct_pat = - seq([path_in_exp, c("("), opt(tuple_struct_items), c(")")]); - - let rest_pat = c(".."); - let tuple_pat_items = - alt([ - seq([atom(), c(",")]), - rest_pat, - seq([atom(), star(seq([c(","), atom(), opt(c(","))]))]), - ]); - let tuple_pat = seq([c("("), tuple_pat_items, c(")")]); - - let group_pat = seq([c("("), atom(), c(")")]); - - let slice_pat_items = - seq([atom(), star(seq([c(","), atom()])), opt(c(","))]); - let slice_pat = seq([c("["), opt(slice_pat_items), c("]")]); - - let path_pat = path_exp; - - let operand = - alt([ - c("true"), - c("false"), - //TODO: char literal, string literal - seq([opt(c("-")), t(Int_lit)]), - seq([opt(c("-")), t(Float_lit)]), - //Wild - c("_"), - //Rest - rest_pat, - //Reference - seq([alt([c("&"), c("&&")]), opt(c("mut")), atom()]), - ident_pat, - struct_pat, - tuple_struct_pat, - tuple_pat, - group_pat, - slice_pat, - path_pat, - ]); - - let tbl = () => [p(operand)]; -}; - -type t = Sort.Map.t(Prec.Table.t(Regex.t)); -let v = - //TODO: ask david is this a valid way to do the item? - [ - Typ.(sort(), tbl()), - Pat.(sort(), tbl()), - Exp.(sort(), tbl()), - Stat.(sort(), tbl()), - Item.(sort(), tbl()), - ] - |> List.to_seq - |> Sort.Map.of_seq; diff --git a/src/ts/Grammar.re b/src/ts/Grammar.re new file mode 100644 index 00000000..bab2f670 --- /dev/null +++ b/src/ts/Grammar.re @@ -0,0 +1,132 @@ +/* Notes about deviations from the TS grammar + JS/TS don't always differentiate b/w ints and float (generic number type) - currently defining with an alt([Int_lit, Float_lit]) but in future will add `Num` label to lexer + + */ + +open Util; + +module Sym = { + include Sym; + type t = Sym.t(Label.t, Sort.t); +}; +module Regex = { + include Regex; + type t = Regex.t(Sym.t); +}; +open Regex; + +let plus = (reg: Regex.t) => seq([reg, star(reg)]); + +let p = (~a: option(Dir.t)=?, r: t) => (a, r); + +let t = (lbl: Label.t) => Regex.atom(Sym.t(lbl)); +let nt = (srt: Sort.t) => Regex.atom(Sym.nt(srt)); + + +let c = (~p=Padding.none, s) => t(Label.const(~padding=p, s)); +// let kw = (~l=true, ~r=true, ~indent=true) => +// c(~p=Padding.kw(~l, ~r, ~indent, ())); +// let op = (~l=true, ~r=true, ~indent=true) => +// c(~p=Padding.op(~l, ~r, ~indent, ())); +// let brc = (side: Dir.t) => c(~p=Padding.brc(side)); + + +module type SORT = { + let atom: unit => Regex.t; + let sort: unit => Sort.t; + let tbl: unit => Prec.Table.t(Regex.t); +}; + +module rec Stat: SORT = { + let sort = () => Sort.of_str("Stat"); + let atom = () => nt(sort()); + + let operand = alt([ + ]); + let tbl = () => [p(seq([Exp.atom(), c(";")])), p(operand)]; +} +and Module: SORT = { + let sort = () => Sort.of_str("Module"); + let atom = () => nt(sort()); + + // [@warning "-32"] + // let comma_sep = seq([atom(), star(seq([c(","), atom()]))]); + + let class_dec = seq([]) + let func_dec = seq([]) + + + let operand = alt([func_dec, class_dec, Stat.atom()]); + + let tbl = () => []; +} +and Typ: SORT = { + let sort = () => Sort.of_str("Typ"); + let atom = () => nt(sort()); + + // let operand = alt([]); + + let tbl = () => []; +} +and Exp: SORT = { + let sort = () => Sort.of_str("Exp"); + let atom = () => nt(sort()); + + // [@warning "-32"] + // let comma_sep = seq([atom(), star(seq([c(","), atom()]))]); + + let stat_block = seq([c("{"), Stat.atom(), c("}")]) + + + let number = alt([t(Int_lit), t(Float_lit)]) + + + //TODO + let method_def = seq([opt(c("static"))]) + + + let prop_name = alt([t(Id_lower), number]) + let pair_exp = seq([prop_name, c(":"), atom()]) + + //TODO: build out options + let object_exp = seq([c("{"), alt([ + pair_exp, + method_def, + ]), c("}")]) + + let primary_expression = alt([ + //Ident + t(Id_lower), + //Literals + number, + //TODO + // t(String_lit) + c("true"), + c("false"), + c("null"), + //Keywords + c("this"), + c("super"), + //Paren + seq([c("("), atom(), c(")")]), + object_exp, + ]) + + let operand = alt([ + primary_expression, + ]); + + //NOTE: tbl goes from weak -> strong precedence + //NOTE: exp without block > exp with block (prec) + let tbl = () => []; +}; +type t = Sort.Map.t(Prec.Table.t(Regex.t)); +let v = + [ + Typ.(sort(), tbl()), + Exp.(sort(), tbl()), + Stat.(sort(), tbl()), + Module.(sort(), tbl()), + ] + |> List.to_seq + |> Sort.Map.of_seq; diff --git a/src/rust/Sort.re b/src/ts/Sort.re similarity index 74% rename from src/rust/Sort.re rename to src/ts/Sort.re index dc05e797..7f38c193 100644 --- a/src/rust/Sort.re +++ b/src/ts/Sort.re @@ -1,29 +1,26 @@ [@deriving (show({with_path: false}), sexp, yojson, ord)] type t = | Exp - | Pat | Stat - | Item + | Module | Typ; -let root = Item; -let all = [Exp, Pat, Typ, Item, Stat]; +let root = Module; +let all = [Exp, Typ, Module, Stat]; let to_str = fun | Typ => "Typ" - | Pat => "Pat" - | Item => "Item" + | Module => "Item" | Stat => "Stat" | Exp => "Exp"; let of_str = fun | "Typ" => Typ - | "Pat" => Pat | "Exp" => Exp | "Stat" => Stat - | "Item" => Item + | "Module" => Module | _ => raise(Invalid_argument("Sort.of_string: unrecognized sort")); module Ord = { diff --git a/src/rust/dune b/src/ts/dune similarity index 100% rename from src/rust/dune rename to src/ts/dune