├── .prettierignore ├── test └── cases │ ├── expressions │ ├── is.hack │ ├── num.hack │ ├── clone.hack │ ├── print.hack │ ├── as.hack │ ├── enum-class-labels.hack │ ├── pipe-scoped.hack │ ├── vec.hack │ ├── shape.hack │ ├── varray.hack │ ├── darray.hack │ ├── dict.hack │ ├── function-call-pipe.hack │ ├── keyset.hack │ ├── subscript-as.hack │ ├── tuple.hack │ ├── update.hack │ ├── await.hack │ ├── function-call-scoped.hack │ ├── xhp-as-arg-v3.hack │ ├── xhp-as-arg-v4.hack │ ├── xhp-classname.hack │ ├── anonymous-function-with-capabilities.hack │ ├── function-call-selection.hack │ ├── prefix-unary.hack │ ├── prefixed-strings.hack │ ├── xhp-class-v3.hack │ ├── xhp-class-v4.hack │ ├── lambda-with-capabilities.hack │ ├── weird-selection.hack │ ├── cast.hack │ ├── xhp-brace.hack │ ├── xhp-enum.hack │ ├── anonymous-function.hack │ ├── binary-null-as.hack │ ├── function-call.hack │ ├── function-pointers.hack │ ├── lambda.hack │ ├── clone.exp │ ├── include.hack │ ├── pipe.hack │ ├── print.exp │ ├── require.hack │ ├── anonymous-function-use.hack │ ├── comments.hack │ ├── selection.hack │ ├── yield.hack │ ├── is.exp │ ├── num.exp │ ├── async.hack │ ├── collection.hack │ ├── shape.exp │ ├── ternary.hack │ ├── anonymous-function-type.hack │ ├── xhp-comment.hack │ ├── shape-type-keys.hack │ ├── list.hack │ ├── namespace-keyword.hack │ ├── lambda-attribute.hack │ ├── xhp-string-not-comment.hack │ ├── selection-with-as.hack │ ├── comments.exp │ ├── xhp-spread.hack │ ├── vec.exp │ ├── keyset.exp │ ├── selection-brace.hack │ ├── as.exp │ ├── function-call-lambda.hack │ ├── xhp-class-v4.exp │ ├── binary-combined.hack │ ├── type-arguments.hack │ ├── xhp-class-v3.exp │ ├── safe-selection.hack │ ├── await.exp │ ├── anonymous-function-with-capabilities.exp │ ├── pipe-scoped.exp │ ├── xhp-classname.exp │ ├── binary.hack │ ├── darray.exp │ ├── dict.exp │ ├── binary-with-strings.hack │ ├── cast.exp │ ├── prefixed-strings.exp │ ├── xhp-as-arg-v4.exp │ ├── xhp-as-arg-v3.exp │ ├── xhp-comment.exp │ ├── update.exp │ ├── xhp-enum.exp │ ├── enum-class-labels.exp │ ├── subscript-as.exp │ ├── lambda-with-lambda-arg.hack │ ├── lambda-with-capabilities.exp │ ├── function-call-scoped.exp │ ├── assignment.hack │ ├── function-call-selection.exp │ ├── include.exp │ ├── require.exp │ ├── augmented-assignment.hack │ ├── function-call-pipe.exp │ ├── binary-null-as.exp │ ├── xhp-attribute.hack │ ├── xhp-brace.exp │ ├── new.hack │ ├── yield.exp │ ├── prefix-unary.exp │ ├── selection-with-keyword.hack │ ├── anonymous-function.exp │ ├── anonymous-function-type.exp │ ├── anonymous-function-use.exp │ ├── weird-selection.exp │ ├── async.exp │ ├── type-arguments.exp │ ├── function-call.exp │ ├── list.exp │ ├── collection.exp │ ├── function-pointers.exp │ ├── xhp-string-not-comment.exp │ ├── shape-type-keys.exp │ ├── lambda.exp │ ├── lambda-with-lambda-arg.exp │ ├── selection-brace.exp │ ├── pipe.exp │ ├── ternary.exp │ ├── lambda-attribute.exp │ ├── selection.exp │ ├── namespace-keyword.exp │ ├── selection-with-as.exp │ ├── xhp-spread.exp │ ├── function-call-lambda.exp │ ├── safe-selection.exp │ ├── binary-with-strings.exp │ ├── binary-combined.exp │ └── new.exp │ ├── statements │ ├── throw.hack │ ├── switch.hack │ ├── echo.hack │ ├── if.hack │ ├── while-identifier.hack │ ├── try-finally.hack │ ├── using.hack │ ├── while.hack │ ├── binary-foreach.hack │ ├── do.hack │ ├── unset.hack │ ├── using-assignment.hack │ ├── throw.exp │ ├── using-simple.hack │ ├── while-nested.hack │ ├── if-else.hack │ ├── using-sequence.hack │ ├── foreach-await.hack │ ├── switch-case.hack │ ├── switch-default.hack │ ├── if-nested.hack │ ├── try.hack │ ├── switch.exp │ ├── do-nested.hack │ ├── echo.exp │ ├── if-else-if.hack │ ├── for.hack │ ├── try-finally.exp │ ├── try-catch-finally.hack │ ├── using.exp │ ├── concurrent.hack │ ├── unset.exp │ ├── binary-foreach.exp │ ├── switch-default.exp │ ├── while-identifier.exp │ ├── try-nested.hack │ ├── do.exp │ ├── foreach-await.exp │ ├── if.exp │ ├── while.exp │ ├── switch-case.exp │ ├── foreach.hack │ ├── if-else.exp │ ├── use.hack │ ├── as-foreach.exp │ ├── as-foreach.hack │ ├── using-sequence.exp │ ├── using-simple.exp │ ├── while-nested.exp │ ├── try.exp │ ├── try-catch-finally.exp │ ├── if-else-if.exp │ ├── if-nested.exp │ ├── do-nested.exp │ ├── foreach.exp │ ├── for.exp │ ├── concurrent.exp │ └── try-nested.exp │ ├── declarations │ ├── type-alias.hack │ ├── namespace-brace.hack │ ├── namespace-semicolon.hack │ ├── namespace-without-name.hack │ ├── qualified-namespace-brace.hack │ ├── qualified-namespace-semicolon.hack │ ├── function-soft-variadic.hack │ ├── interface-where.hack │ ├── trait-where.hack │ ├── newtype-alias.hack │ ├── reify.hack │ ├── attribute-function.hack │ ├── tuple-type.hack │ ├── enum-type-constraint.hack │ ├── function-inout.hack │ ├── function-type-parameters.hack │ ├── repeating-type-parameter-constraint.hack │ ├── like-type-modifier.hack │ ├── type-alias.exp │ ├── namespace-without-name.exp │ ├── const.hack │ ├── attribute-type-parameter.hack │ ├── function-where.hack │ ├── require-implements-and-extends.hack │ ├── namespace-semicolon.exp │ ├── async-functions.hack │ ├── class-parameter-visibility.hack │ ├── enum.hack │ ├── namespace-brace.exp │ ├── qualified-namespace-semicolon.exp │ ├── class-where.hack │ ├── function-type-specifier.hack │ ├── type-alias-type-parameters.hack │ ├── empty-function.hhi │ ├── class-type-parameters.hack │ ├── trait.hack │ ├── class.hack │ ├── interface.hack │ ├── qualified-namespace-brace.exp │ ├── context-function-types.hack │ ├── shape-type-specifier.hack │ ├── attribute.hack │ ├── attribute-type.hack │ ├── use-trait.hack │ ├── function-where-tricky.hack │ ├── type-const.hack │ ├── function.hack │ ├── method.hack │ ├── newtype-alias.exp │ ├── contexts.hack │ ├── enum-class.hack │ ├── enum-type-constraint.exp │ ├── class-const-ctx.hack │ ├── property.hack │ ├── class-const.hack │ ├── function-soft-variadic.exp │ ├── repeating-type-parameter-constraint.exp │ ├── attribute-function.exp │ ├── function-inout.exp │ ├── function-type-parameters.exp │ ├── reify.exp │ ├── shape-type-specifier.exp │ ├── like-type-modifier.exp │ ├── attribute-type-parameter.exp │ ├── function-type-specifier.exp │ ├── const.exp │ ├── enum.exp │ ├── tuple-type.exp │ ├── class-parameter-visibility.exp │ ├── async-functions.exp │ ├── trait-where.exp │ ├── interface-where.exp │ ├── require-implements-and-extends.exp │ ├── empty-function.exp │ ├── context-function-types.exp │ ├── keyword-as-class.hack │ ├── enum-class.exp │ ├── function-where.exp │ ├── method.exp │ ├── attribute.exp │ ├── xhp-class-attribute.hack │ ├── contexts.exp │ ├── function.exp │ ├── class-const-ctx.exp │ ├── type-const.exp │ ├── type-alias-type-parameters.exp │ ├── attribute-type.exp │ ├── use-trait.exp │ ├── class-where.exp │ ├── property.exp │ ├── function-where-tricky.exp │ ├── class-type-parameters.exp │ ├── trait.exp │ ├── interface.exp │ ├── class.exp │ └── class-const.exp │ └── literals │ ├── heredoc-simple.hack │ ├── nowdoc-simple.hack │ ├── heredoc-dollar-no-lead-space.hack │ ├── heredoc-empty.hack │ ├── heredoc-dollar.exp │ ├── heredoc-dollar.hack │ ├── heredoc-simple.exp │ ├── integers.hack │ ├── nowdoc-simple.exp │ ├── nowdoc-no-interpolation.hack │ ├── heredoc-concat.hack │ ├── nowdoc-no-interpolation.exp │ ├── heredoc-dollar-no-lead-space.exp │ ├── heredoc-consecutive.hack │ ├── heredoc-double-quote.hack │ ├── heredoc-braced-simple.hack │ ├── heredoc-braced-call.hack │ ├── heredoc-braced-mix.hack │ ├── heredoc-empty.exp │ ├── heredoc-almost-concat.exp │ ├── heredoc-consecutive.exp │ ├── heredoc-dollar-embedded-var.exp │ ├── double-quoted-strings.hack │ ├── heredoc-concat.exp │ ├── single-quoted-strings.hack │ ├── heredoc-almost.hack │ ├── heredoc-double-quote.exp │ ├── heredoc-almost-concat.hack │ ├── heredoc-almost.exp │ ├── heredoc-braced-selection.hack │ ├── heredoc-braced-subscript.hack │ ├── heredoc-dollar-embedded-var.hack │ ├── heredoc-braced-almost.hack │ ├── floats.hack │ ├── heredoc-braced-almost.exp │ ├── integers.exp │ ├── heredoc-variable.hack │ ├── heredoc-variable.exp │ ├── double-quoted-strings.exp │ ├── single-quoted-strings.exp │ ├── heredoc-braced-simple.exp │ ├── heredoc-braced-mix.exp │ ├── heredoc-braced-call.exp │ ├── floats.exp │ ├── heredoc-braced-subscript.exp │ └── heredoc-braced-selection.exp ├── .npmrc ├── .rubocop.yml ├── diagram.png ├── .github ├── workflows │ ├── validate.sh │ ├── validate.yml │ └── ci.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.md │ └── bug_report.md ├── PULL_REQUEST_TEMPLATE.md └── CODE_OF_CONDUCT.md ├── .npmignore ├── .clang-format ├── CONTRIBUTING-ARCHIVED.md ├── bin ├── require_ruby ├── require_sed ├── require_fd ├── test-corpus ├── test-dir ├── test-dir-quiet ├── docker │ ├── run │ ├── Dockerfile │ └── build ├── fetch-examples ├── generate-parser ├── ts-query ├── hh-errors ├── generate-corpus ├── test-examples └── ts-errors ├── .gitignore ├── .gitattributes ├── queries └── highlights.scm ├── bindings ├── node │ ├── index.js │ └── binding.cc └── rust │ ├── build.rs │ └── lib.rs ├── Cargo.toml ├── .prettierrc ├── binding.gyp ├── LICENSE └── package.json /.prettierignore: -------------------------------------------------------------------------------- 1 | src/ 2 | package.json 3 | -------------------------------------------------------------------------------- /test/cases/expressions/is.hack: -------------------------------------------------------------------------------- 1 | $var is int; 2 | -------------------------------------------------------------------------------- /test/cases/expressions/num.hack: -------------------------------------------------------------------------------- 1 | $x is num; 2 | -------------------------------------------------------------------------------- /test/cases/statements/throw.hack: -------------------------------------------------------------------------------- 1 | throw 1; 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | engine-strict=true 3 | -------------------------------------------------------------------------------- /test/cases/expressions/clone.hack: -------------------------------------------------------------------------------- 1 | clone 'clone'; 2 | -------------------------------------------------------------------------------- /test/cases/expressions/print.hack: -------------------------------------------------------------------------------- 1 | print 'print'; 2 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | Style/PerlBackrefs: 2 | Enabled: false 3 | -------------------------------------------------------------------------------- /test/cases/declarations/type-alias.hack: -------------------------------------------------------------------------------- 1 | type I = int; 2 | -------------------------------------------------------------------------------- /test/cases/statements/switch.hack: -------------------------------------------------------------------------------- 1 | switch ($arg) {default:} 2 | -------------------------------------------------------------------------------- /test/cases/expressions/as.hack: -------------------------------------------------------------------------------- 1 | $var as int; 2 | $var ?as int; 3 | -------------------------------------------------------------------------------- /test/cases/expressions/enum-class-labels.hack: -------------------------------------------------------------------------------- 1 | E#B; 2 | f(#B); 3 | -------------------------------------------------------------------------------- /test/cases/expressions/pipe-scoped.hack: -------------------------------------------------------------------------------- 1 | C::class |> $$::CONST; 2 | -------------------------------------------------------------------------------- /test/cases/expressions/vec.hack: -------------------------------------------------------------------------------- 1 | vec[]; 2 | vec[1, 1., true]; 3 | -------------------------------------------------------------------------------- /test/cases/statements/echo.hack: -------------------------------------------------------------------------------- 1 | echo 1; 2 | 3 | echo 1, 2, 3; 4 | -------------------------------------------------------------------------------- /test/cases/statements/if.hack: -------------------------------------------------------------------------------- 1 | if (0) 1; 2 | 3 | if (0) { 1; } 4 | -------------------------------------------------------------------------------- /test/cases/statements/while-identifier.hack: -------------------------------------------------------------------------------- 1 | while (id < 1) {} 2 | -------------------------------------------------------------------------------- /test/cases/declarations/namespace-brace.hack: -------------------------------------------------------------------------------- 1 | namespace Space { 2 | } 3 | -------------------------------------------------------------------------------- /test/cases/declarations/namespace-semicolon.hack: -------------------------------------------------------------------------------- 1 | namespace Space; 2 | -------------------------------------------------------------------------------- /test/cases/declarations/namespace-without-name.hack: -------------------------------------------------------------------------------- 1 | namespace { 2 | } 3 | -------------------------------------------------------------------------------- /test/cases/expressions/shape.hack: -------------------------------------------------------------------------------- 1 | shape( 2 | 'field' => 1, 3 | ); 4 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-simple.hack: -------------------------------------------------------------------------------- 1 | << null, 2 => 1]; 3 | -------------------------------------------------------------------------------- /test/cases/expressions/dict.hack: -------------------------------------------------------------------------------- 1 | dict[]; 2 | dict[false => null, 2.3 => 1]; 3 | -------------------------------------------------------------------------------- /test/cases/expressions/function-call-pipe.hack: -------------------------------------------------------------------------------- 1 | $$(); 2 | 3 | func() |> $$($$); 4 | -------------------------------------------------------------------------------- /test/cases/expressions/keyset.hack: -------------------------------------------------------------------------------- 1 | keyset[]; 2 | keyset[1, null, .1, true]; 3 | -------------------------------------------------------------------------------- /test/cases/expressions/subscript-as.hack: -------------------------------------------------------------------------------- 1 | vec[] as int[0]; 2 | 3 | 1 as int[0]; 4 | -------------------------------------------------------------------------------- /test/cases/expressions/tuple.hack: -------------------------------------------------------------------------------- 1 | tuple(); 2 | tuple(1); 3 | tuple(0.1, true); 4 | -------------------------------------------------------------------------------- /test/cases/expressions/update.hack: -------------------------------------------------------------------------------- 1 | ++$var; 2 | --$var; 3 | $var++; 4 | $var--; 5 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-dollar-no-lead-space.hack: -------------------------------------------------------------------------------- 1 | <<); 2 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-as-arg-v4.hack: -------------------------------------------------------------------------------- 1 | TestXHP::display(); 2 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-classname.hack: -------------------------------------------------------------------------------- 1 | assert_func(:page:subpage:test::class); 2 | -------------------------------------------------------------------------------- /test/cases/statements/throw.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (throw_statement 3 | (integer))) 4 | -------------------------------------------------------------------------------- /test/cases/expressions/anonymous-function-with-capabilities.hack: -------------------------------------------------------------------------------- 1 | function()[C]: void {}; 2 | -------------------------------------------------------------------------------- /test/cases/expressions/function-call-selection.hack: -------------------------------------------------------------------------------- 1 | $arg?->$arg(); 2 | 3 | arg->arg(); 4 | -------------------------------------------------------------------------------- /test/cases/expressions/prefix-unary.hack: -------------------------------------------------------------------------------- 1 | !1; 2 | ~1; 3 | -1; 4 | +1; 5 | 6 | ! ~ - +1; 7 | -------------------------------------------------------------------------------- /test/cases/declarations/function-soft-variadic.hack: -------------------------------------------------------------------------------- 1 | function func(<<__Soft>> int ...$arg1) {} 2 | -------------------------------------------------------------------------------- /test/cases/declarations/interface-where.hack: -------------------------------------------------------------------------------- 1 | interface C extends B where T1 as T2 {} 2 | -------------------------------------------------------------------------------- /test/cases/declarations/trait-where.hack: -------------------------------------------------------------------------------- 1 | trait C implements A where T1 super T3 {} 2 | -------------------------------------------------------------------------------- /test/cases/expressions/prefixed-strings.hack: -------------------------------------------------------------------------------- 1 | re"re"; 2 | 3 | b"b"; 4 | 5 | r0eb " r 0 eb "; 6 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-class-v3.hack: -------------------------------------------------------------------------------- 1 | xhp class :a:m_b { 2 | } 3 | 4 | new :a:m_b:b_m(); 5 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-class-v4.hack: -------------------------------------------------------------------------------- 1 | xhp class a:m_b { 2 | } 3 | 4 | new a:m_b:b_m(); 5 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-dollar.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (heredoc))) 4 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-dollar.hack: -------------------------------------------------------------------------------- 1 | << {} 2 | 3 | function func(): void {} 4 | -------------------------------------------------------------------------------- /test/cases/expressions/lambda-with-capabilities.hack: -------------------------------------------------------------------------------- 1 | ()[] ==> {}; 2 | ()[io] ==> {echo "output"; }; 3 | -------------------------------------------------------------------------------- /test/cases/literals/nowdoc-no-interpolation.hack: -------------------------------------------------------------------------------- 1 | <<<'EOF' 2 | Nowdoc $var Nodoc {$var} 3 | EOF; 4 | -------------------------------------------------------------------------------- /test/cases/declarations/attribute-function.hack: -------------------------------------------------------------------------------- 1 | function func(<<__Soft>> int $int): <<__Soft>> int {} 2 | -------------------------------------------------------------------------------- /test/cases/declarations/tuple-type.hack: -------------------------------------------------------------------------------- 1 | function func((dict, int) $arg): ?(int, C, B\A) {} 2 | -------------------------------------------------------------------------------- /test/cases/expressions/weird-selection.hack: -------------------------------------------------------------------------------- 1 | c::c?->c; 2 | 3 | ($var + $var)?->c; 4 | 5 | $func()?->c; 6 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-concat.hack: -------------------------------------------------------------------------------- 1 | <<> inout int $arg1, inout int $arg2) {} 2 | -------------------------------------------------------------------------------- /test/cases/declarations/function-type-parameters.hack: -------------------------------------------------------------------------------- 1 | function func(): void {} 2 | -------------------------------------------------------------------------------- /test/cases/declarations/repeating-type-parameter-constraint.hack: -------------------------------------------------------------------------------- 1 | function func(): void {} 2 | -------------------------------------------------------------------------------- /test/cases/expressions/cast.hack: -------------------------------------------------------------------------------- 1 | (int)'int'; 2 | (float)'float'; 3 | (string)'string'; 4 | (array)'array'; 5 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-brace.hack: -------------------------------------------------------------------------------- 1 | return 2 | Hi! {$this} is a {$this->test()}! 3 | ; 4 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-enum.hack: -------------------------------------------------------------------------------- 1 | class :a { 2 | attribute enum {'a', 'b', 1} denum = 1 @required; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-dollar-no-lead-space.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (heredoc))) 4 | -------------------------------------------------------------------------------- /test/cases/statements/if-else.hack: -------------------------------------------------------------------------------- 1 | if (0) 1; else 0; 2 | 3 | if (0) { 4 | 1; 5 | } else { 6 | 0; 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/statements/using-sequence.hack: -------------------------------------------------------------------------------- 1 | using ($new = new Object(), $file = new File('using', '+using')) {} 2 | -------------------------------------------------------------------------------- /test/cases/declarations/like-type-modifier.hack: -------------------------------------------------------------------------------- 1 | function func(~?(function(int): bool) $func): ~int {} 2 | -------------------------------------------------------------------------------- /test/cases/expressions/anonymous-function.hack: -------------------------------------------------------------------------------- 1 | $f1 = function($p1) {}; 2 | 3 | $f2 = function($p1, $p2): int {}; 4 | -------------------------------------------------------------------------------- /test/cases/expressions/binary-null-as.hack: -------------------------------------------------------------------------------- 1 | $var['key'] ?? 1 as nonnull; 2 | 3 | $var['key'] ?? null as nonnull; 4 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-consecutive.hack: -------------------------------------------------------------------------------- 1 | << $v) {} 4 | -------------------------------------------------------------------------------- /.github/workflows/validate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -n "$(git status --porcelain)" ]; then 4 | exit 1 5 | fi 6 | -------------------------------------------------------------------------------- /test/cases/declarations/type-alias.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (alias_declaration 3 | (identifier) 4 | (type_specifier))) 5 | -------------------------------------------------------------------------------- /test/cases/expressions/function-call.hack: -------------------------------------------------------------------------------- 1 | func(); 2 | 3 | func(1, inout $arg, ...null); 4 | 5 | $arg[func()](); 6 | -------------------------------------------------------------------------------- /test/cases/expressions/function-pointers.hack: -------------------------------------------------------------------------------- 1 | MyClass::bar<>; 2 | foo<>; 3 | \Foo\Bar\Baz\derp<>; 4 | fizz; 5 | -------------------------------------------------------------------------------- /test/cases/statements/switch-case.hack: -------------------------------------------------------------------------------- 1 | switch ($arg) { 2 | case 1 + 1: 3 | return 1 + 1; 4 | break; 5 | } 6 | -------------------------------------------------------------------------------- /test/cases/declarations/namespace-without-name.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (namespace_declaration 3 | body: (compound_statement))) 4 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-double-quote.hack: -------------------------------------------------------------------------------- 1 | <<<"EOF" 2 | Heredoc 3 | EOF; 4 | 5 | <<<"EOF" 6 | Heredoc $var 7 | EOF; 8 | 9 | -------------------------------------------------------------------------------- /test/cases/statements/switch-default.hack: -------------------------------------------------------------------------------- 1 | switch ($arg) { 2 | default: 3 | break; 4 | case 1: 5 | break; 6 | } 7 | -------------------------------------------------------------------------------- /test/cases/declarations/const.hack: -------------------------------------------------------------------------------- 1 | const int C1 = 1; 2 | const int C2 = 1, C3 = 1; 3 | const C4 = 1; 4 | const C5 = 1, C6 = 1; 5 | -------------------------------------------------------------------------------- /test/cases/expressions/lambda.hack: -------------------------------------------------------------------------------- 1 | (int $x): int ==> $x + 1; 2 | (int $x) ==> $x + 1; 3 | ($x) ==> $x + 1; 4 | $x ==> $x + 1; 5 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-braced-simple.hack: -------------------------------------------------------------------------------- 1 | <<func} 4 | {$var["key"]} 5 | {$func("arg")} 6 | EOF; 7 | -------------------------------------------------------------------------------- /test/cases/declarations/attribute-type-parameter.hack: -------------------------------------------------------------------------------- 1 | class C<<> reify T> {} 2 | 3 | function func<<> T>(): void {} 4 | -------------------------------------------------------------------------------- /test/cases/expressions/clone.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (prefix_unary_expression 4 | operand: (string)))) 5 | -------------------------------------------------------------------------------- /test/cases/expressions/include.hack: -------------------------------------------------------------------------------- 1 | include_once(__DIR__.'/../vendor/autoload.hack'); 2 | include(__DIR__.'/../vendor/autoload.hack'); 3 | -------------------------------------------------------------------------------- /test/cases/expressions/pipe.hack: -------------------------------------------------------------------------------- 1 | vec[1, 2] 2 | |> Vec\map($$, $var ==> $var + 1) 3 | |> $_ ==> { 4 | return $$; 5 | }($$); 6 | -------------------------------------------------------------------------------- /test/cases/expressions/print.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (prefix_unary_expression 4 | operand: (string)))) 5 | -------------------------------------------------------------------------------- /test/cases/expressions/require.hack: -------------------------------------------------------------------------------- 1 | require_once(__DIR__.'/../vendor/autoload.hack'); 2 | require(__DIR__.'/../vendor/autoload.hack'); 3 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-braced-call.hack: -------------------------------------------------------------------------------- 1 | <<var()} 4 | {$func()["key"]()} 5 | {$$()} 6 | EOF; 7 | -------------------------------------------------------------------------------- /test/cases/statements/if-nested.hack: -------------------------------------------------------------------------------- 1 | if (0) if (1) 0; else 0; 2 | 3 | if (0) { 4 | if (1) 0; 5 | } else { 6 | if (1) { 0; } 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/declarations/function-where.hack: -------------------------------------------------------------------------------- 1 | function func1(): T where T as int {} 2 | 3 | function func2(): T where vec = int {} 4 | -------------------------------------------------------------------------------- /test/cases/declarations/require-implements-and-extends.hack: -------------------------------------------------------------------------------- 1 | trait T { 2 | require implements I; 3 | require extends C; 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/expressions/anonymous-function-use.hack: -------------------------------------------------------------------------------- 1 | $f3 = function($p1) use ($p2,) {}; 2 | 3 | $f4 = function($p1): int use ($p2, $p3) {}; 4 | -------------------------------------------------------------------------------- /test/cases/expressions/comments.hack: -------------------------------------------------------------------------------- 1 | // a ? : / */ // + re"" 2 | 3 | /* a ? : / */ 1 + 1; // + re"" 4 | 5 | /*\ 6 | *\/ 1 + 1 7 | */ 8 | 9 | -------------------------------------------------------------------------------- /test/cases/expressions/selection.hack: -------------------------------------------------------------------------------- 1 | $var->$var; 2 | C->$var; 3 | C->C; 4 | C\C->$var[1]->C; 5 | $var[0]->$var[0]; 6 | $var[0]->var[0]; 7 | -------------------------------------------------------------------------------- /test/cases/expressions/yield.hack: -------------------------------------------------------------------------------- 1 | function func(): void { 2 | yield 'yield'; 3 | yield yield 'yield' => 1; 4 | yield 1 => 'yield'; 5 | } 6 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-braced-mix.hack: -------------------------------------------------------------------------------- 1 | <<func} 3 | {$var["key" ]}x {$func()}y 4 | x{$func("arg") } {$var} 5 | EOF; 6 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-empty.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (heredoc)) 4 | (expression_statement 5 | (heredoc))) 6 | -------------------------------------------------------------------------------- /test/cases/statements/try.hack: -------------------------------------------------------------------------------- 1 | try { 2 | } catch (Type $var) { 3 | } 4 | 5 | try { 6 | } catch (Type $var1) { 7 | } catch (Type $var2) { 8 | } 9 | -------------------------------------------------------------------------------- /test/cases/declarations/namespace-semicolon.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (namespace_declaration 3 | name: (qualified_identifier 4 | (identifier)))) 5 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-almost-concat.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (heredoc)) 4 | (expression_statement 5 | (heredoc))) 6 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-consecutive.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (heredoc)) 4 | (expression_statement 5 | (heredoc))) 6 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-dollar-embedded-var.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (comment) 3 | (expression_statement 4 | (heredoc 5 | (variable)))) 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | build 3 | bin 4 | examples 5 | tmp 6 | 7 | .vscode 8 | .prettierignore 9 | .prettierrc 10 | .rubocop.yml 11 | .clang-format 12 | -------------------------------------------------------------------------------- /test/cases/declarations/async-functions.hack: -------------------------------------------------------------------------------- 1 | async function func0(): void {} 2 | 3 | async function func1() {} 4 | 5 | async ($x) ==> $x + 1; 6 | -------------------------------------------------------------------------------- /test/cases/expressions/is.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (is_expression 4 | left: (variable) 5 | right: (type_specifier)))) 6 | -------------------------------------------------------------------------------- /test/cases/expressions/num.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (is_expression 4 | left: (variable) 5 | right: (type_specifier)))) 6 | -------------------------------------------------------------------------------- /test/cases/statements/switch.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (switch_statement 3 | value: (parenthesized_expression 4 | (variable)) 5 | (switch_default))) 6 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | ColumnLimit: 100 3 | AlignAfterOpenBracket: AlwaysBreak 4 | BinPackParameters: false 5 | BinPackArguments: false 6 | 7 | -------------------------------------------------------------------------------- /test/cases/declarations/class-parameter-visibility.hack: -------------------------------------------------------------------------------- 1 | class C { 2 | public function __construct(<<__Soft>> private int $prop = 1, string ...$name) {} 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/expressions/async.hack: -------------------------------------------------------------------------------- 1 | async { 2 | }; 3 | 4 | async { 5 | concurrent { 6 | await func1(); 7 | await func2(); 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /test/cases/expressions/collection.hack: -------------------------------------------------------------------------------- 1 | Vector {}; 2 | HH\Vector {1, 2, 3}; 3 | 4 | \HH\Map {}; 5 | Map {'foo' => 1}; 6 | 7 | Set {}; 8 | Set {'foo', 'bar'}; 9 | -------------------------------------------------------------------------------- /test/cases/expressions/shape.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (shape 4 | (field_initializer 5 | (string) 6 | (integer))))) 7 | -------------------------------------------------------------------------------- /test/cases/expressions/ternary.hack: -------------------------------------------------------------------------------- 1 | $var ? true : false; 2 | $var ?: false; 3 | $var == 1 ? $var ?: 3 : $var > 2 ? 2 : 1; 4 | $var == 1 ?: $var < 3 ? 3 : 2; 5 | -------------------------------------------------------------------------------- /test/cases/literals/double-quoted-strings.hack: -------------------------------------------------------------------------------- 1 | ""; 2 | " "; 3 | "\""; 4 | "\\"; 5 | "\?"; 6 | "'"; 7 | " \'\\\?'"; 8 | b" \'\\\?'"; 9 | re" \'\\\?'"; 10 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-concat.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (binary_expression 4 | left: (heredoc) 5 | right: (heredoc)))) 6 | -------------------------------------------------------------------------------- /test/cases/literals/single-quoted-strings.hack: -------------------------------------------------------------------------------- 1 | ''; 2 | ' '; 3 | '\''; 4 | '\\'; 5 | '\?'; 6 | '"'; 7 | ' \'\\\?"'; 8 | b' \'\\\?"'; 9 | re' \'\\\?"'; 10 | -------------------------------------------------------------------------------- /test/cases/statements/do-nested.hack: -------------------------------------------------------------------------------- 1 | do do 1; while (0); while (0); 2 | 3 | do { do 1; while (0); } while (0); 4 | 5 | do { do { 1; } while (0); } while (0); 6 | -------------------------------------------------------------------------------- /test/cases/statements/echo.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (echo_statement 3 | (integer)) 4 | (echo_statement 5 | (integer) 6 | (integer) 7 | (integer))) 8 | -------------------------------------------------------------------------------- /test/cases/expressions/anonymous-function-type.hack: -------------------------------------------------------------------------------- 1 | function((function(int...): int) $function): ((function(int): int), (function(): void)) use ($function) {}; 2 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-comment.hack: -------------------------------------------------------------------------------- 1 | return 2 | 4 | Te{$this}st 5 | 6 | ; 7 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-almost.hack: -------------------------------------------------------------------------------- 1 | << 0; $var++) 1; 4 | 5 | for ($var = 1, $var / 0; $var++, $var > 0; $var--, $var = rand()) 1; 6 | -------------------------------------------------------------------------------- /test/cases/statements/try-finally.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (try_statement 3 | body: (compound_statement) 4 | (finally_clause 5 | body: (compound_statement)))) 6 | -------------------------------------------------------------------------------- /CONTRIBUTING-ARCHIVED.md: -------------------------------------------------------------------------------- 1 | # ARCHIVED 2 | 3 | This project is `Archived` and is no longer actively maintained; 4 | We are not accepting contributions or Pull Requests. 5 | 6 | -------------------------------------------------------------------------------- /test/cases/declarations/enum.hack: -------------------------------------------------------------------------------- 1 | enum Enum: int {} 2 | 3 | <> 4 | enum Enum : int { 5 | F1 = 1; 6 | F2 = 8; 7 | F3 = C::CONST; 8 | F4 = 'a'.'b'; 9 | } 10 | -------------------------------------------------------------------------------- /test/cases/declarations/namespace-brace.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (namespace_declaration 3 | name: (qualified_identifier 4 | (identifier)) 5 | body: (compound_statement))) 6 | -------------------------------------------------------------------------------- /test/cases/declarations/qualified-namespace-semicolon.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (namespace_declaration 3 | name: (qualified_identifier 4 | (identifier) 5 | (identifier)))) 6 | -------------------------------------------------------------------------------- /test/cases/expressions/shape-type-keys.hack: -------------------------------------------------------------------------------- 1 | type square = shape( 2 | new C() => C, 3 | fun() => int, 4 | 'streng' => string, 5 | (() ==> $var ==> $var)() => string, 6 | ); 7 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-almost-concat.hack: -------------------------------------------------------------------------------- 1 | << extends B implements A where T2 = T3 { 2 | private function __construct(T1 $param) where ?T1 super vec, {} 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/expressions/list.hack: -------------------------------------------------------------------------------- 1 | list($a) = tuple('a'); 2 | list($a,) = tuple('a','b'); 3 | list($a,,$c) = tuple('a','b','c'); 4 | list(,$b,,,$e,) = tuple('a','b','c','d','e','f'); 5 | -------------------------------------------------------------------------------- /bin/require_ruby: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if ! ruby --version | grep -qE "^ruby (2\.[7-]|[3-]\.)"; then 4 | echo "Ruby 2.7 or later is required for this script to work." 5 | exit 1 6 | fi 7 | -------------------------------------------------------------------------------- /test/cases/declarations/function-type-specifier.hack: -------------------------------------------------------------------------------- 1 | type func = (function(int, ?string,): string); 2 | 3 | function func((function(inout int): string) $func): (function(int): string) {} 4 | -------------------------------------------------------------------------------- /test/cases/declarations/type-alias-type-parameters.hack: -------------------------------------------------------------------------------- 1 | type I1 = I2; 2 | 3 | // Only newtype can have a type constraint. 4 | newtype I2 as N = I1; 5 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-almost.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (heredoc)) 4 | (expression_statement 5 | (heredoc)) 6 | (expression_statement 7 | (heredoc))) 8 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-braced-selection.hack: -------------------------------------------------------------------------------- 1 | <<var->var} 3 | {$var->var()->var} 4 | {$var->var->var()} 5 | {$var->var["key"]->var} 6 | {$var->var->var["key"]} 7 | EOF; 8 | -------------------------------------------------------------------------------- /test/cases/declarations/empty-function.hhi: -------------------------------------------------------------------------------- 1 | namespace { 2 | <<__PHPStdLib, __Pure>> 3 | function is_bool($var): bool; 4 | <<__PHPStdLib, __Pure>> 5 | function is_int($var): bool; 6 | } 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Code of Conduct 4 | url: https://slackhq.github.io/code-of-conduct 5 | about: Code of Conduct 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | *.log 4 | .DS_Store 5 | .vscode/settings.json 6 | examples 7 | package-lock.json 8 | tmp 9 | Cargo.lock 10 | /target/ 11 | 12 | test/cases/**/*.json 13 | -------------------------------------------------------------------------------- /test/cases/declarations/class-type-parameters.hack: -------------------------------------------------------------------------------- 1 | abstract final class F> extends B implements A\B, C\D { 2 | function method(): Tc {} 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/expressions/namespace-keyword.hack: -------------------------------------------------------------------------------- 1 | function func1(namespace\a\b $arg): namespace\a\b {} 2 | function func2(a\namespace\b $arg): a\namespace\b {} 3 | namespace\a\b(); 4 | a\namespace\b(); 5 | -------------------------------------------------------------------------------- /test/cases/declarations/trait.hack: -------------------------------------------------------------------------------- 1 | <> 2 | trait F> implements A\B, C\D { 3 | function method(): Tc {} 4 | } 5 | 6 | -------------------------------------------------------------------------------- /test/cases/expressions/lambda-attribute.hack: -------------------------------------------------------------------------------- 1 | // HHVM requires parens to parse correctly 🤔 Tree-sitter doesn't. 2 | (<>(int $x): int ==> $x + 1); 3 | 4 | (<> $x ==> {}); 5 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-braced-subscript.hack: -------------------------------------------------------------------------------- 1 | <<var} 5 | {$var["key"]->var()} 6 | {$var["key"][$var]} 7 | {$var[$var->func()][$var()]} 8 | EOF; 9 | -------------------------------------------------------------------------------- /test/cases/statements/try-catch-finally.hack: -------------------------------------------------------------------------------- 1 | try { 2 | } catch (Type $var) { 3 | } finally { 4 | } 5 | 6 | try { 7 | } catch (Type $var1) { 8 | } catch (Type $var2) { 9 | } finally { 10 | } 11 | -------------------------------------------------------------------------------- /test/cases/declarations/class.hack: -------------------------------------------------------------------------------- 1 | <> 2 | class F> extends B implements A\B, C\D { 3 | function method(): Tc {} 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/declarations/interface.hack: -------------------------------------------------------------------------------- 1 | <> 2 | interface F> extends B, A\B, C\D { 3 | function method(): Tc {} 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-string-not-comment.hack: -------------------------------------------------------------------------------- 1 | # ; 2 | 3 | 4 | # 5 | ; 6 | 7 | // ; 8 | 9 | #hello ; 10 | 11 | #hello; 12 | -------------------------------------------------------------------------------- /test/cases/statements/using.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (using_statement 3 | (integer) 4 | (compound_statement)) 5 | (using_statement 6 | (await_modifier) 7 | (integer) 8 | (compound_statement))) 9 | -------------------------------------------------------------------------------- /test/cases/declarations/qualified-namespace-brace.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (namespace_declaration 3 | name: (qualified_identifier 4 | (identifier) 5 | (identifier)) 6 | body: (compound_statement))) 7 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-dollar-embedded-var.hack: -------------------------------------------------------------------------------- 1 | // This tests that the parser is correctly interprets when a variable is embedded between two string literals 2 | 3 | <<test; 2 | 3 | $var as nonnull->test(); 4 | 5 | ($var as nonnull)->test; 6 | 7 | $var->test as nonnull; 8 | 9 | $var->test as nonnull->test2; 10 | -------------------------------------------------------------------------------- /test/cases/declarations/context-function-types.hack: -------------------------------------------------------------------------------- 1 | function has_fn_args( 2 | (function (): void) $no_list, 3 | (function ()[io, rand]: void) $list, 4 | (function ()[]: void) $empty_list, 5 | )[ctx $list]: void {} 6 | 7 | -------------------------------------------------------------------------------- /test/cases/declarations/shape-type-specifier.hack: -------------------------------------------------------------------------------- 1 | type circle = shape( 2 | ?'int' => int, 3 | 'shape' => shape( 4 | 'int' => ?int, 5 | ... 6 | ), 7 | ... 8 | ); 9 | 10 | type nothing = shape(); 11 | -------------------------------------------------------------------------------- /test/cases/expressions/comments.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (comment) 3 | (comment) 4 | (expression_statement 5 | (binary_expression 6 | left: (integer) 7 | right: (integer))) 8 | (comment) 9 | (comment)) 10 | -------------------------------------------------------------------------------- /test/cases/declarations/attribute.hack: -------------------------------------------------------------------------------- 1 | <> 2 | class C { 3 | <> 4 | function method() {} 5 | } 6 | 7 | <> 8 | function func() { 9 | } 10 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-spread.hack: -------------------------------------------------------------------------------- 1 |
; 2 |
; 3 |
{$this}
; 4 |
{$this}
; 5 | -------------------------------------------------------------------------------- /test/cases/declarations/attribute-type.hack: -------------------------------------------------------------------------------- 1 | <> 2 | newtype T1 = ?shape( 3 | ?'int' => int 4 | ); 5 | 6 | <> 7 | type T2 = (function(T1): string); 8 | 9 | <> 10 | newtype T3 as int = int; 11 | -------------------------------------------------------------------------------- /test/cases/declarations/use-trait.hack: -------------------------------------------------------------------------------- 1 | class C { 2 | use A; 3 | 4 | use B; 5 | 6 | use C, D { D as E; } 7 | 8 | use F, G>, H { 9 | H::methodG insteadof G; 10 | G::methodH insteadof H; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/cases/expressions/vec.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (array 4 | (array_type))) 5 | (expression_statement 6 | (array 7 | (array_type) 8 | (integer) 9 | (float) 10 | (true)))) 11 | -------------------------------------------------------------------------------- /test/cases/statements/unset.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (unset_statement) 3 | (unset_statement 4 | (subscript_expression 5 | (variable) 6 | (string)) 7 | (subscript_expression 8 | (variable) 9 | (integer)))) 10 | -------------------------------------------------------------------------------- /test/cases/declarations/function-where-tricky.hack: -------------------------------------------------------------------------------- 1 | function func1() where T = int, {} 2 | 3 | function func2() where ?vec = int, T super int {} 4 | 5 | // Optional comma. Why tho? 6 | function func3() where ?vec = int T super int {} 7 | -------------------------------------------------------------------------------- /test/cases/statements/binary-foreach.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (foreach_statement 3 | collection: (binary_expression 4 | left: (variable) 5 | right: (array 6 | (array_type))) 7 | value: (variable) 8 | body: (compound_statement))) 9 | -------------------------------------------------------------------------------- /test/cases/declarations/type-const.hack: -------------------------------------------------------------------------------- 1 | class C { 2 | const type T1; 3 | const type T2 = int; 4 | <> 5 | const type T3 as int; 6 | const type T4<<> T3> as int = arraykey; 7 | abstract const type T5 as ?int = ?arraykey; 8 | } 9 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-braced-almost.hack: -------------------------------------------------------------------------------- 1 | <<var} 5 | EOF; 6 | 7 | <<func} 13 | EOF; 14 | 15 | <<{$primary_key}; 2 | 3 | $dynamic_item->{$fun->test()}; 4 | 5 | $dynamic_item->{$primary_key}->{$primary_key2}->{$primary_key3}; 6 | 7 | $dynamic_item->{$primary_key}->$primary_key2->{$primary_key3}; 8 | -------------------------------------------------------------------------------- /bin/require_sed: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$sed" ] && exit 0 4 | 5 | sed="$(command -v gsed || command -v sed)" 6 | 7 | if ! $sed --version 2>/dev/null | grep -q "GNU sed"; then 8 | echo "GNU sed is required for this script to work." 9 | exit 1 10 | fi 11 | -------------------------------------------------------------------------------- /test/cases/declarations/function.hack: -------------------------------------------------------------------------------- 1 | function func0(): void {} 2 | 3 | function func1(...) {} 4 | 5 | function func2($arg) {} 6 | 7 | function func3(int $arg) {} 8 | 9 | function func4(int $arg): void {} 10 | 11 | function func5(int $arg1, bool $arg2): void {} 12 | -------------------------------------------------------------------------------- /test/cases/declarations/method.hack: -------------------------------------------------------------------------------- 1 | abstract class C { 2 | static function method1(): void {} 3 | public function method2(): void {} 4 | function method3(): void {} 5 | abstract public static function method4(); 6 | final public static function method5(): void {} 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/expressions/as.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (as_expression 4 | left: (variable) 5 | right: (type_specifier))) 6 | (expression_statement 7 | (as_expression 8 | left: (variable) 9 | right: (type_specifier)))) 10 | -------------------------------------------------------------------------------- /test/cases/expressions/function-call-lambda.hack: -------------------------------------------------------------------------------- 1 | // (lambda (call (awaitable))) 2 | $var = $arg ==> async { return $arg; }(1, ...vec[1,2,3]); 3 | 4 | // (call (lambda)) 5 | $var = async $arg ==> { return $arg; }(1, ...vec[1,2,3]); 6 | 7 | ($arg ==> $arg)(func(), inout $arg); 8 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-class-v4.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | (xhp_modifier) 4 | name: (xhp_identifier) 5 | body: (member_declarations)) 6 | (expression_statement 7 | (new_expression 8 | (xhp_identifier) 9 | (arguments)))) 10 | -------------------------------------------------------------------------------- /test/cases/statements/switch-default.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (switch_statement 3 | value: (parenthesized_expression 4 | (variable)) 5 | (switch_default 6 | (break_statement)) 7 | (switch_case 8 | value: (integer) 9 | (break_statement)))) 10 | -------------------------------------------------------------------------------- /test/cases/expressions/binary-combined.hack: -------------------------------------------------------------------------------- 1 | 1 ?? 2 | 1 || 3 | 1 && 4 | 1 | 5 | 1 ^ 6 | ~ 1 & 7 | 1 == 8 | 1 != 9 | 1 === 10 | ! 1 !== 11 | 1 < 12 | 1 > 13 | 1 <= 14 | 1 >= 15 | 1 <=> 16 | 1 << 17 | 1 >> 18 | + 1 + 19 | - 1 - 20 | 1 . 21 | 1 * 22 | 1 / 23 | 1 % 24 | 1 ** 1; 25 | -------------------------------------------------------------------------------- /test/cases/expressions/type-arguments.hack: -------------------------------------------------------------------------------- 1 | funcshion, int>(); 2 | 3 | funcshion<>(); 4 | 5 | new Cluss(); 6 | 7 | // Weird. Hack allows empty type arguments on function calls but not class instantiation. 8 | // new Cluss<>(); 9 | // new Cluss>(); 10 | -------------------------------------------------------------------------------- /test/cases/statements/while-identifier.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (while_statement 3 | condition: (parenthesized_expression 4 | (binary_expression 5 | left: (qualified_identifier 6 | (identifier)) 7 | right: (integer))) 8 | body: (compound_statement))) 9 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-class-v3.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | (xhp_modifier) 4 | name: (xhp_class_identifier) 5 | body: (member_declarations)) 6 | (expression_statement 7 | (new_expression 8 | (xhp_class_identifier) 9 | (arguments)))) 10 | -------------------------------------------------------------------------------- /test/cases/declarations/newtype-alias.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (alias_declaration 3 | (identifier) 4 | (type_specifier)) 5 | (alias_declaration 6 | (identifier) 7 | as: (type_specifier) 8 | (type_specifier 9 | (qualified_identifier 10 | (identifier))))) 11 | -------------------------------------------------------------------------------- /test/cases/expressions/safe-selection.hack: -------------------------------------------------------------------------------- 1 | $var?->$var ?? $var ?->$var ? $var?-> $var : $var ?-> $var; 2 | 3 | $var[$var?->$var]?->$var[$var?->$var] ?? 4 | $var[$var ?->$var] ?->$var[$var ?->$var] 5 | ? $var[$var?-> $var]?-> $var[$var?-> $var] 6 | : $var[$var ?-> $var] ?-> $var[$var ?-> $var]; 7 | -------------------------------------------------------------------------------- /test/cases/declarations/contexts.hack: -------------------------------------------------------------------------------- 1 | function no_listed_contexts(): void {} 2 | function empty_context()[]: void {} 3 | function one_context()[C]: void {} 4 | function many_context()[C1, C2, Cn]: void {} 5 | function throws_foo_exception()[policied]: void { 6 | throw new FooException(); 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/expressions/await.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (function_declaration 3 | name: (identifier) 4 | (parameters) 5 | return_type: (type_specifier) 6 | body: (compound_statement 7 | (expression_statement 8 | (prefix_unary_expression 9 | operand: (string)))))) 10 | -------------------------------------------------------------------------------- /test/cases/declarations/enum-class.hack: -------------------------------------------------------------------------------- 1 | class ExampleClass {} 2 | 3 | abstract enum class AbstractEnum : mixed { 4 | int X = 42; 5 | string S = 'foo'; 6 | abstract ExampleClass C; 7 | } 8 | 9 | enum class Enum : mixed extends AbstractEnum { 10 | ExampleClass C = new ExampleClass(); 11 | } 12 | -------------------------------------------------------------------------------- /test/cases/declarations/enum-type-constraint.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (enum_declaration 3 | name: (identifier) 4 | type: (type_specifier) 5 | as: (type_specifier) 6 | (enumerator 7 | (identifier) 8 | (integer)) 9 | (enumerator 10 | (identifier) 11 | (integer)))) 12 | -------------------------------------------------------------------------------- /test/cases/expressions/anonymous-function-with-capabilities.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (anonymous_function_expression 4 | (parameters) 5 | (capability_list 6 | (capability 7 | (identifier))) 8 | (type_specifier) 9 | (compound_statement)))) 10 | -------------------------------------------------------------------------------- /test/cases/literals/floats.hack: -------------------------------------------------------------------------------- 1 | 12.01; 2 | .01; 3 | 12.; 4 | 12.01e12; 5 | 12.01E12; 6 | 12.e12; 7 | 12.E12; 8 | 12.01e-12; 9 | 12.01E-12; 10 | 12.e-12; 11 | 12.E-12; 12 | .01E-12; 13 | 12.01e+12; 14 | 12.01E+12; 15 | 12.e+12; 16 | 12.E+12; 17 | .01E+12; 18 | 1E12; 19 | 1e12; 20 | 1E-12; 21 | 1e+12; 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /examples/* linguist-vendored 2 | 3 | # Explicitly list files in src so scanner.cc and scanner.c are not included. 4 | /src/tree_sitter/* -diff linguist-vendored 5 | /src/grammar.json -diff linguist-generated 6 | /src/node-types.json -diff linguist-generated 7 | /src/parser.c -diff linguist-generated 8 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-braced-almost.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (heredoc)) 4 | (expression_statement 5 | (heredoc 6 | (variable))) 7 | (expression_statement 8 | (heredoc 9 | (variable))) 10 | (expression_statement 11 | (heredoc 12 | (variable)))) 13 | -------------------------------------------------------------------------------- /bin/require_fd: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -n "$fd" ] && exit 0 4 | 5 | if [[ "$(uname)" == Darwin* ]]; then 6 | fd="$(command -v fd || echo "")" 7 | else 8 | fd="$(command -v fdfind || echo "")" 9 | fi 10 | 11 | if [ -z "$fd" ]; then 12 | echo "fd is required for this script to work." 13 | exit 1 14 | fi 15 | -------------------------------------------------------------------------------- /test/cases/declarations/class-const-ctx.hack: -------------------------------------------------------------------------------- 1 | abstract class WithConstant { 2 | abstract const ctx CAnotherOne as [io]; 3 | abstract const ctx COne super [defaults]; 4 | abstract const ctx CMany super [defaults] as [io, rand]; 5 | const ctx C = [defaults]; 6 | const ctx CWithBound super [defaults] = [io]; 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/expressions/pipe-scoped.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (binary_expression 4 | left: (scoped_identifier 5 | (qualified_identifier 6 | (identifier)) 7 | (identifier)) 8 | right: (scoped_identifier 9 | (pipe_variable) 10 | (identifier))))) 11 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-classname.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (call_expression 4 | function: (qualified_identifier 5 | (identifier)) 6 | (arguments 7 | (argument 8 | (scoped_identifier 9 | (xhp_class_identifier) 10 | (identifier))))))) 11 | -------------------------------------------------------------------------------- /test/cases/expressions/binary.hack: -------------------------------------------------------------------------------- 1 | 1 ?? 1; 2 | 1 || 1; 3 | 1 && 1; 4 | 1 | 1; 5 | 1 ^ 1; 6 | 1 & 1; 7 | 1 == 1; 8 | 1 != 1; 9 | 1 === 1; 10 | 1 !== 1; 11 | 1 < 1; 12 | 1 > 1; 13 | 1 <= 1; 14 | 1 >= 1; 15 | 1 <=> 1; 16 | 1 << 1; 17 | 1 >> 1; 18 | 1 + 1; 19 | 1 - 1; 20 | 1 . 1; 21 | 1 * 1; 22 | 1 / 1; 23 | 1 % 1; 24 | 1 ** 1; 25 | -------------------------------------------------------------------------------- /test/cases/expressions/darray.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (array 4 | (array_type))) 5 | (expression_statement 6 | (array 7 | (array_type) 8 | (element_initializer 9 | (false) 10 | (null)) 11 | (element_initializer 12 | (integer) 13 | (integer))))) 14 | -------------------------------------------------------------------------------- /test/cases/expressions/dict.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (array 4 | (array_type))) 5 | (expression_statement 6 | (array 7 | (array_type) 8 | (element_initializer 9 | (false) 10 | (null)) 11 | (element_initializer 12 | (float) 13 | (integer))))) 14 | -------------------------------------------------------------------------------- /test/cases/declarations/property.hack: -------------------------------------------------------------------------------- 1 | <<__ConsistentConstruct>> 2 | class C { 3 | static $var1 = 1; 4 | public $var2 = 1; 5 | static public $var3 = 1; 6 | public static $var4 = 1; 7 | 8 | public $var5; 9 | public static $var6; 10 | 11 | public int $var7; 12 | <<__LateInit>> 13 | public static int $var8; 14 | } 15 | -------------------------------------------------------------------------------- /test/cases/statements/try-nested.hack: -------------------------------------------------------------------------------- 1 | try { 2 | try { 3 | } catch (Namespce\Type $var) { 4 | } finally { 5 | } 6 | } catch (Namespce\Type $var) { 7 | try { 8 | } catch (Namespce\Type $var) { 9 | } finally { 10 | } 11 | } finally { 12 | try { 13 | } catch (Namespce\Type $var) { 14 | } finally { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/cases/expressions/binary-with-strings.hack: -------------------------------------------------------------------------------- 1 | '??' ?? 2 | '||' || 3 | '&&' && 4 | '|' | 5 | '^' ^ 6 | '&' & 7 | '==' == 8 | '!=' != 9 | '===' === 10 | '!==' !== 11 | '<' < 12 | '>' > 13 | '<=' <= 14 | '>=' >= 15 | '<=>' <=> 16 | '<<' << 17 | '>>' >> 18 | '+' + 19 | '-' - 20 | '.' . 21 | '*' * 22 | '/' / 23 | '%' % 24 | '**' ** '??'; 25 | -------------------------------------------------------------------------------- /test/cases/statements/do.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (do_statement 3 | body: (expression_statement 4 | (integer)) 5 | condition: (parenthesized_expression 6 | (integer))) 7 | (do_statement 8 | body: (compound_statement 9 | (expression_statement 10 | (integer))) 11 | condition: (parenthesized_expression 12 | (integer)))) 13 | -------------------------------------------------------------------------------- /test/cases/statements/foreach-await.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (foreach_statement 3 | collection: (variable) 4 | (await_modifier) 5 | value: (variable) 6 | body: (compound_statement)) 7 | (foreach_statement 8 | collection: (variable) 9 | (await_modifier) 10 | key: (variable) 11 | value: (variable) 12 | body: (compound_statement))) 13 | -------------------------------------------------------------------------------- /test/cases/statements/if.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (if_statement 3 | condition: (parenthesized_expression 4 | (integer)) 5 | body: (expression_statement 6 | (integer))) 7 | (if_statement 8 | condition: (parenthesized_expression 9 | (integer)) 10 | body: (compound_statement 11 | (expression_statement 12 | (integer))))) 13 | -------------------------------------------------------------------------------- /test/cases/expressions/cast.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (cast_expression 4 | value: (string))) 5 | (expression_statement 6 | (cast_expression 7 | value: (string))) 8 | (expression_statement 9 | (cast_expression 10 | value: (string))) 11 | (expression_statement 12 | (cast_expression 13 | value: (string)))) 14 | -------------------------------------------------------------------------------- /bin/test-corpus: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source bin/require_fd 6 | 7 | bin/generate-parser 8 | bin/generate-corpus 9 | 10 | printf "\033[1mRunning Tree-sitter corpus tests...\033[0m\n" 11 | npx tree-sitter test 12 | 13 | printf "\n" 14 | 15 | printf "\033[1mGetting Hacklang corpus errors...\033[0m\n" 16 | bin/hh-errors "$($fd '\.(hack|php)$' test/cases)" 17 | -------------------------------------------------------------------------------- /test/cases/expressions/prefixed-strings.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (prefixed_string 4 | prefix: (identifier) 5 | (string))) 6 | (expression_statement 7 | (prefixed_string 8 | prefix: (identifier) 9 | (string))) 10 | (expression_statement 11 | (prefixed_string 12 | prefix: (identifier) 13 | (string)))) 14 | -------------------------------------------------------------------------------- /test/cases/statements/while.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (while_statement 3 | condition: (parenthesized_expression 4 | (integer)) 5 | body: (expression_statement 6 | (integer))) 7 | (while_statement 8 | condition: (parenthesized_expression 9 | (integer)) 10 | body: (compound_statement 11 | (expression_statement 12 | (integer))))) 13 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-as-arg-v4.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (call_expression 4 | function: (scoped_identifier 5 | (qualified_identifier 6 | (identifier)) 7 | (identifier)) 8 | (arguments 9 | (argument 10 | (xhp_expression 11 | (xhp_open_close 12 | (xhp_identifier)))))))) 13 | -------------------------------------------------------------------------------- /test/cases/literals/integers.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (integer)) 4 | (expression_statement 5 | (integer)) 6 | (expression_statement 7 | (integer)) 8 | (expression_statement 9 | (integer)) 10 | (expression_statement 11 | (integer)) 12 | (expression_statement 13 | (integer)) 14 | (expression_statement 15 | (integer))) 16 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-as-arg-v3.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (call_expression 4 | function: (scoped_identifier 5 | (qualified_identifier 6 | (identifier)) 7 | (identifier)) 8 | (arguments 9 | (argument 10 | (xhp_expression 11 | (xhp_open_close 12 | (xhp_class_identifier)))))))) 13 | -------------------------------------------------------------------------------- /test/cases/declarations/class-const.hack: -------------------------------------------------------------------------------- 1 | abstract class C { 2 | abstract const A\B C01; 3 | const A\B C02 = A\B::C0; 4 | const C03 = A\B::C0; 5 | 6 | const int C1 = 1; 7 | const int C2 = 1, C3 = 1; 8 | const C4 = 1; 9 | const C5 = 1, C6 = 1; 10 | 11 | abstract const int C7; 12 | abstract const int C8, C9; 13 | abstract const CA; 14 | abstract const CB, CC; 15 | } 16 | -------------------------------------------------------------------------------- /test/cases/declarations/function-soft-variadic.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (function_declaration 3 | name: (identifier) 4 | (parameters 5 | (parameter 6 | (attribute_modifier 7 | (qualified_identifier 8 | (identifier))) 9 | type: (type_specifier) 10 | (variadic_modifier) 11 | name: (variable))) 12 | body: (compound_statement))) 13 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-comment.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (return_statement 3 | (xhp_expression 4 | (xhp_open 5 | (xhp_identifier)) 6 | (xhp_string) 7 | (xhp_comment) 8 | (xhp_string) 9 | (braced_expression 10 | (variable)) 11 | (xhp_string) 12 | (xhp_comment) 13 | (xhp_string) 14 | (xhp_close 15 | (xhp_identifier))))) 16 | -------------------------------------------------------------------------------- /test/cases/expressions/update.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (prefix_unary_expression 4 | operand: (variable))) 5 | (expression_statement 6 | (prefix_unary_expression 7 | operand: (variable))) 8 | (expression_statement 9 | (postfix_unary_expression 10 | (variable))) 11 | (expression_statement 12 | (postfix_unary_expression 13 | (variable)))) 14 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-enum.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | name: (xhp_class_identifier) 4 | body: (member_declarations 5 | (xhp_attribute_declaration 6 | (xhp_class_attribute 7 | type: (xhp_enum_type 8 | (string) 9 | (string) 10 | (integer)) 11 | name: (xhp_identifier) 12 | default: (integer)))))) 13 | -------------------------------------------------------------------------------- /test/cases/statements/switch-case.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (switch_statement 3 | value: (parenthesized_expression 4 | (variable)) 5 | (switch_case 6 | value: (binary_expression 7 | left: (integer) 8 | right: (integer)) 9 | (return_statement 10 | (binary_expression 11 | left: (integer) 12 | right: (integer))) 13 | (break_statement)))) 14 | -------------------------------------------------------------------------------- /bin/test-dir: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source bin/require_fd 6 | 7 | # Also see test-dir-quiet 8 | 9 | # Note: By default, fd ignores hidden directories, 10 | # hidden files, and .gitignore patterns 11 | # To change this behavior, use --hidden and/or --no-ignore with fd calls 12 | 13 | { fd . -e php "$@" | xargs -r egrep -l "^<\?hh" & fd . -e hack "$@"; } | xargs -r -n 256 bin/ts-errors 14 | -------------------------------------------------------------------------------- /test/cases/statements/foreach.hack: -------------------------------------------------------------------------------- 1 | foreach ($c as $v) {} 2 | 3 | foreach (varray[] as $k => $v[0]) {} 4 | 5 | foreach (darray[] as list($a[vec[] as int[0]], $b)) {} 6 | 7 | // HHVM can't parse an as-expression in the collection position, but 8 | // tree-sitter-hack can 💪. Commenting out because bin/test-corpus runs tests for both. 9 | // foreach (darray[] as dict as $v) {} 10 | -------------------------------------------------------------------------------- /test/cases/expressions/enum-class-labels.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (enum_class_label 4 | (qualified_identifier 5 | (identifier)) 6 | (identifier))) 7 | (expression_statement 8 | (call_expression 9 | (qualified_identifier 10 | (identifier)) 11 | (arguments 12 | (argument 13 | (enum_class_label 14 | (identifier))))))) 15 | -------------------------------------------------------------------------------- /test/cases/expressions/subscript-as.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (subscript_expression 4 | (as_expression 5 | left: (array 6 | (array_type)) 7 | right: (type_specifier)) 8 | (integer))) 9 | (expression_statement 10 | (subscript_expression 11 | (as_expression 12 | left: (integer) 13 | right: (type_specifier)) 14 | (integer)))) 15 | -------------------------------------------------------------------------------- /bin/test-dir-quiet: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source bin/require_fd 6 | 7 | # Also see test-dir 8 | 9 | # Note: By default, fd ignores hidden directories, 10 | # hidden files, and .gitignore patterns 11 | # To change this behavior, use --hidden and/or --no-ignore with fd calls 12 | 13 | { fd . -e php "$@" | xargs -r egrep -l "^<\?hh" & fd . -e hack "$@"; } | xargs -r -n 256 npx tree-sitter parse --quiet 14 | -------------------------------------------------------------------------------- /test/cases/expressions/lambda-with-lambda-arg.hack: -------------------------------------------------------------------------------- 1 | ((function(): void) $test): void ==> { 2 | }; 3 | 4 | ( ( /*test*/ function ( ) : void ) $test ) : void ==> { 5 | }; 6 | 7 | ( ( function ( ) /*test*/ : void ) $test ) : void ==> { 8 | }; 9 | 10 | // TODO: The line below should not error as it is valid Hack. 11 | // See PR #8 for details. 12 | // ( ( function /*test*/ ( ) : void ) $test ) : void ==> { 13 | // }; 14 | -------------------------------------------------------------------------------- /test/cases/expressions/lambda-with-capabilities.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (lambda_expression 4 | (parameters) 5 | (capability_list) 6 | (compound_statement))) 7 | (expression_statement 8 | (lambda_expression 9 | (parameters) 10 | (capability_list 11 | (capability 12 | (identifier))) 13 | (compound_statement 14 | (echo_statement 15 | (string)))))) 16 | -------------------------------------------------------------------------------- /test/cases/declarations/repeating-type-parameter-constraint.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (function_declaration 3 | name: (identifier) 4 | (type_parameters 5 | (type_parameter 6 | name: (identifier) 7 | constraint_type: (type_specifier 8 | (qualified_identifier 9 | (identifier))) 10 | constraint_type: (type_specifier))) 11 | (parameters) 12 | return_type: (type_specifier) 13 | body: (compound_statement))) 14 | -------------------------------------------------------------------------------- /test/cases/expressions/function-call-scoped.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (call_expression 4 | function: (scoped_identifier 5 | (qualified_identifier 6 | (identifier)) 7 | (variable)) 8 | (arguments))) 9 | (expression_statement 10 | (call_expression 11 | function: (scoped_identifier 12 | (qualified_identifier 13 | (identifier)) 14 | (identifier)) 15 | (arguments)))) 16 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-variable.hack: -------------------------------------------------------------------------------- 1 | // This test ensures that variables within heredocs are interpreted as such. 2 | // ÿ is 255 in unicode which is a valid variable identifier. 3 | 4 | <<>= 1; 8 | 9 | list($var, $bar) += tuple(1, 1); 10 | list($var[], $bar) -= tuple(1, 1); 11 | list($var[1], $bar[]) *= tuple(1, 1); 12 | list($var['key'], $bar[1]) /= tuple(1, 1); 13 | list($var[$var['key']], $bar['key']) %= tuple(1, 1); 14 | list($var[$var['key']][], $var[$var['key']],) **= tuple(1, 1); 15 | -------------------------------------------------------------------------------- /test/cases/expressions/function-call-pipe.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (call_expression 4 | function: (pipe_variable) 5 | (arguments))) 6 | (expression_statement 7 | (binary_expression 8 | left: (call_expression 9 | function: (qualified_identifier 10 | (identifier)) 11 | (arguments)) 12 | right: (call_expression 13 | function: (pipe_variable) 14 | (arguments 15 | (argument 16 | (pipe_variable))))))) 17 | -------------------------------------------------------------------------------- /test/cases/statements/if-else.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (if_statement 3 | condition: (parenthesized_expression 4 | (integer)) 5 | body: (expression_statement 6 | (integer)) 7 | else: (expression_statement 8 | (integer))) 9 | (if_statement 10 | condition: (parenthesized_expression 11 | (integer)) 12 | body: (compound_statement 13 | (expression_statement 14 | (integer))) 15 | else: (compound_statement 16 | (expression_statement 17 | (integer))))) 18 | -------------------------------------------------------------------------------- /test/cases/statements/use.hack: -------------------------------------------------------------------------------- 1 | use const Space\Const\C; 2 | use function Space\Func\F as E; 3 | use type Space\Type\T; 4 | use namespace Space\Name\N as M; 5 | 6 | use namespace Space\Name2\N2, Space\Nothing\N3 as N8, type Space\Type2\N4,; 7 | 8 | use namespace Space\Name\N10\{A as A2, B\}; 9 | use namespace Space\Name\{\C, Slash as Forward}; 10 | 11 | use \What\Is\This\{function A as A2, B, const H\S\L as stdlib, function F}; 12 | 13 | use type \{kind,}; 14 | use Q\B\{kind2,}; 15 | use type Q\B\{kind3,}; 16 | 17 | -------------------------------------------------------------------------------- /test/cases/expressions/binary-null-as.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (binary_expression 4 | left: (subscript_expression 5 | (variable) 6 | (string)) 7 | right: (as_expression 8 | left: (integer) 9 | right: (type_specifier)))) 10 | (expression_statement 11 | (binary_expression 12 | left: (subscript_expression 13 | (variable) 14 | (string)) 15 | right: (as_expression 16 | left: (null) 17 | right: (type_specifier))))) 18 | -------------------------------------------------------------------------------- /test/cases/statements/as-foreach.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (comment) 3 | (foreach_statement 4 | collection: (parenthesized_expression 5 | (subscript_expression 6 | (as_expression 7 | left: (variable) 8 | right: (type_specifier)))) 9 | value: (variable) 10 | body: (compound_statement)) 11 | (comment) 12 | (foreach_statement 13 | collection: (variable) 14 | (ERROR 15 | (array 16 | (array_type))) 17 | value: (variable) 18 | body: (compound_statement))) 19 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-attribute.hack: -------------------------------------------------------------------------------- 1 | ; 2 | 3 | ; 4 | 5 | 'derp'}} 15 | myshape={shape('foo' => 'herp', 'bar' => 'derp')} 16 | />; 17 | -------------------------------------------------------------------------------- /test/cases/statements/as-foreach.hack: -------------------------------------------------------------------------------- 1 | /** 2 | * This test captures that without parenthesis, you might expect this test's code to 3 | * parse like this 4 | * 5 | * foreach (($array as vec[]) as $item) {} 6 | * 7 | * but ends up parsing like this (which makes it a parse error). 8 | * 9 | * foreach ($array as (vec[] as $item)) {} 10 | */ 11 | 12 | foreach (($array as vec[]) as $item) {} 13 | 14 | // Our expectation test for the code below intentionally includes an ERROR. 15 | foreach ($array as vec[] as $item) {} 16 | -------------------------------------------------------------------------------- /bin/docker/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" 6 | IMAGE_NAME="tree-sitter-hack" 7 | CONTAINER_NAME="tree-sitter-hack" 8 | 9 | case "$1" in 10 | "") 11 | args="/bin/bash" 12 | ;; 13 | 14 | install) 15 | args="npm $@" 16 | ;; 17 | 18 | build | reset | test*) 19 | args="npm run $@" 20 | ;; 21 | 22 | node-gyp*) 23 | args="npx $@" 24 | ;; 25 | 26 | *) 27 | args="$@" 28 | ;; 29 | 30 | esac 31 | 32 | bash -c "docker exec -it $CONTAINER_NAME $args" 33 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-brace.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (return_statement 3 | (xhp_expression 4 | (xhp_open 5 | (xhp_identifier)) 6 | (xhp_string) 7 | (braced_expression 8 | (variable)) 9 | (xhp_string) 10 | (braced_expression 11 | (call_expression 12 | function: (selection_expression 13 | (variable) 14 | (qualified_identifier 15 | (identifier))) 16 | (arguments))) 17 | (xhp_string) 18 | (xhp_close 19 | (xhp_identifier))))) 20 | -------------------------------------------------------------------------------- /bindings/node/index.js: -------------------------------------------------------------------------------- 1 | try { 2 | module.exports = require("../../build/Release/tree_sitter_hack_binding"); 3 | } catch (error1) { 4 | if (error1.code !== 'MODULE_NOT_FOUND') { 5 | throw error1; 6 | } 7 | try { 8 | module.exports = require("../../build/Debug/tree_sitter_hack_binding"); 9 | } catch (error2) { 10 | if (error2.code !== 'MODULE_NOT_FOUND') { 11 | throw error2; 12 | } 13 | throw error1 14 | } 15 | } 16 | 17 | try { 18 | module.exports.nodeTypeInfo = require("../../src/node-types.json"); 19 | } catch (_) {} 20 | -------------------------------------------------------------------------------- /test/cases/statements/using-sequence.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (using_statement 3 | (binary_expression 4 | left: (variable) 5 | right: (new_expression 6 | (qualified_identifier 7 | (identifier)) 8 | (arguments))) 9 | (binary_expression 10 | left: (variable) 11 | right: (new_expression 12 | (qualified_identifier 13 | (identifier)) 14 | (arguments 15 | (argument 16 | (string)) 17 | (argument 18 | (string))))) 19 | (compound_statement))) 20 | -------------------------------------------------------------------------------- /test/cases/statements/using-simple.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (function_declaration 3 | name: (identifier) 4 | (parameters) 5 | return_type: (type_specifier) 6 | body: (compound_statement 7 | (using_statement 8 | (expression_statement 9 | (binary_expression 10 | left: (variable) 11 | right: (call_expression 12 | function: (scoped_identifier 13 | (qualified_identifier 14 | (identifier)) 15 | (identifier)) 16 | (arguments)))))))) 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Summary 2 | 3 | Describe the goal of this PR. Mention any related Issue numbers. 4 | 5 | ### Requirements (place an `x` in each `[ ]`) 6 | 7 | * [ ] I've added tests for any new code and ran `npm run test-corpus` to make sure all tests pass. 8 | * [ ] I've read and understood the [Contributing Guidelines](https://github.com/slackhq/tree-sitter-hack/blob/main/.github/CONTRIBUTING.md) and have done my best effort to follow them. 9 | * [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct). 10 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-variable.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (comment) 3 | (comment) 4 | (expression_statement 5 | (heredoc 6 | (variable))) 7 | (expression_statement 8 | (heredoc 9 | (variable))) 10 | (expression_statement 11 | (heredoc 12 | (variable))) 13 | (expression_statement 14 | (heredoc 15 | (variable))) 16 | (expression_statement 17 | (heredoc 18 | (variable))) 19 | (expression_statement 20 | (heredoc 21 | (variable))) 22 | (expression_statement 23 | (heredoc 24 | (variable) 25 | (variable)))) 26 | -------------------------------------------------------------------------------- /test/cases/declarations/shape-type-specifier.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (alias_declaration 3 | (identifier) 4 | (shape_type_specifier 5 | (field_specifier 6 | (optional_modifier) 7 | (string) 8 | (type_specifier)) 9 | (field_specifier 10 | (string) 11 | (shape_type_specifier 12 | (field_specifier 13 | (string) 14 | (type_specifier 15 | (nullable_modifier))) 16 | (open_modifier))) 17 | (open_modifier))) 18 | (alias_declaration 19 | (identifier) 20 | (shape_type_specifier))) 21 | -------------------------------------------------------------------------------- /test/cases/literals/double-quoted-strings.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (string)) 4 | (expression_statement 5 | (string)) 6 | (expression_statement 7 | (string)) 8 | (expression_statement 9 | (string)) 10 | (expression_statement 11 | (string)) 12 | (expression_statement 13 | (string)) 14 | (expression_statement 15 | (string)) 16 | (expression_statement 17 | (prefixed_string 18 | prefix: (identifier) 19 | (string))) 20 | (expression_statement 21 | (prefixed_string 22 | prefix: (identifier) 23 | (string)))) 24 | -------------------------------------------------------------------------------- /test/cases/literals/single-quoted-strings.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (string)) 4 | (expression_statement 5 | (string)) 6 | (expression_statement 7 | (string)) 8 | (expression_statement 9 | (string)) 10 | (expression_statement 11 | (string)) 12 | (expression_statement 13 | (string)) 14 | (expression_statement 15 | (string)) 16 | (expression_statement 17 | (prefixed_string 18 | prefix: (identifier) 19 | (string))) 20 | (expression_statement 21 | (prefixed_string 22 | prefix: (identifier) 23 | (string)))) 24 | -------------------------------------------------------------------------------- /test/cases/expressions/new.hack: -------------------------------------------------------------------------------- 1 | // https://github.com/facebook/hhvm/blob/a114ef79b4673c35755297ed570d23a72969ba5f/hphp/hack/test/full_fidelity/cases/test_object_creation_errors.php 2 | 3 | $p1 = new Point(); 4 | $p1 = new Point(12); 5 | $p1 = new $PointClassVar->$pointClassName(); 6 | 7 | // Fails in Hack but not in Tree-sitter. Should fail in Tree-sitter. 8 | // $p1 = new Point::Point(12); 9 | 10 | $p1 = new Point::$pointVar(12); 11 | $p1 = new self::$pointVar(12); 12 | $p1 = new $point(12); 13 | $p1 = new Point(12); 14 | $p1 = new (function_that_returns_class_name())(12); 15 | $p1 = "Point" |> new $$(12); 16 | -------------------------------------------------------------------------------- /test/cases/expressions/yield.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (function_declaration 3 | name: (identifier) 4 | (parameters) 5 | return_type: (type_specifier) 6 | body: (compound_statement 7 | (expression_statement 8 | (yield_expression 9 | (string))) 10 | (expression_statement 11 | (yield_expression 12 | (yield_expression 13 | (element_initializer 14 | (string) 15 | (integer))))) 16 | (expression_statement 17 | (yield_expression 18 | (element_initializer 19 | (integer) 20 | (string))))))) 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Description 11 | 12 | Describe your request here. 13 | 14 | ### Requirements (place an `x` in each of the `[ ]`) 15 | * [ ] I've read and understood the [Contributing guidelines](../CONTRIBUTING.md) and have done my best effort to follow them. 16 | * [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct). 17 | * [ ] I've searched for any related issues and avoided creating a duplicate issue. -------------------------------------------------------------------------------- /test/cases/declarations/like-type-modifier.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (function_declaration 3 | name: (identifier) 4 | (type_parameters 5 | (type_parameter 6 | name: (identifier) 7 | constraint_type: (type_specifier 8 | (like_modifier)))) 9 | (parameters 10 | (parameter 11 | type: (function_type_specifier 12 | (like_modifier) 13 | (nullable_modifier) 14 | (type_specifier) 15 | return_type: (type_specifier)) 16 | name: (variable))) 17 | return_type: (type_specifier 18 | (like_modifier)) 19 | body: (compound_statement))) 20 | -------------------------------------------------------------------------------- /test/cases/statements/while-nested.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (while_statement 3 | condition: (parenthesized_expression 4 | (integer)) 5 | body: (while_statement 6 | condition: (parenthesized_expression 7 | (integer)) 8 | body: (expression_statement 9 | (integer)))) 10 | (while_statement 11 | condition: (parenthesized_expression 12 | (integer)) 13 | body: (compound_statement 14 | (while_statement 15 | condition: (parenthesized_expression 16 | (integer)) 17 | body: (compound_statement 18 | (expression_statement 19 | (integer))))))) 20 | -------------------------------------------------------------------------------- /bin/fetch-examples: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Taken from https://git.io/JJOXZ 6 | 7 | function fetch() { 8 | path="examples/$1" 9 | url=$2 10 | sha=$3 11 | 12 | if [ ! -d "$path" ]; then 13 | git clone --depth 1 --branch "$sha" "https://github.com/$url" "$path" 14 | fi 15 | 16 | pushd "$path" >/dev/null 17 | git fetch --depth 1 origin "$sha" && git reset --hard "$sha" 18 | popd >/dev/null 19 | } 20 | 21 | mkdir -p examples 22 | 23 | fetch "hack-sql-fake" "slackhq/hack-sql-fake" "v4.0.5" 24 | fetch "hack-json-schema" "slackhq/hack-json-schema" "v4.27.1" 25 | fetch "hhvm" "facebook/hhvm" "HHVM-4.67.0" 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-sitter-hack" 3 | description = "hack grammar for the tree-sitter parsing library" 4 | version = "0.0.4" 5 | keywords = ["incremental", "parsing", "hack"] 6 | categories = ["parsing", "text-editors"] 7 | repository = "https://github.com/slackhq/tree-sitter-hack" 8 | edition = "2018" 9 | license = "MIT" 10 | 11 | build = "bindings/rust/build.rs" 12 | include = [ 13 | "bindings/rust/*", 14 | "grammar.js", 15 | "queries/*", 16 | "src/*", 17 | ] 18 | 19 | [lib] 20 | path = "bindings/rust/lib.rs" 21 | 22 | [dependencies] 23 | tree-sitter = "0.20.6" 24 | 25 | [build-dependencies] 26 | cc = "1.0" 27 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-braced-simple.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (heredoc 4 | (embedded_braced_expression 5 | (variable)) 6 | (embedded_braced_expression 7 | (selection_expression 8 | (variable) 9 | (qualified_identifier 10 | (identifier)))) 11 | (embedded_braced_expression 12 | (subscript_expression 13 | (variable) 14 | (string))) 15 | (embedded_braced_expression 16 | (call_expression 17 | function: (variable) 18 | (arguments 19 | (argument 20 | (string)))))))) 21 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "trailingComma": "all", 7 | "singleQuote": true, 8 | "overrides": [ 9 | { 10 | "files": ["*.json", ".prettierrc"], 11 | "options": { 12 | "parser": "json5", 13 | "quoteProps": "preserve", 14 | "singleQuote": false, 15 | "trailingComma": "all", 16 | }, 17 | }, 18 | { 19 | "files": [".vscode/tasks.json"], 20 | "options": { 21 | "parser": "json", 22 | "quoteProps": "preserve", 23 | "singleQuote": false, 24 | }, 25 | }, 26 | ], 27 | } 28 | -------------------------------------------------------------------------------- /test/cases/expressions/prefix-unary.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (prefix_unary_expression 4 | operand: (integer))) 5 | (expression_statement 6 | (prefix_unary_expression 7 | operand: (integer))) 8 | (expression_statement 9 | (prefix_unary_expression 10 | operand: (integer))) 11 | (expression_statement 12 | (prefix_unary_expression 13 | operand: (integer))) 14 | (expression_statement 15 | (prefix_unary_expression 16 | operand: (prefix_unary_expression 17 | operand: (prefix_unary_expression 18 | operand: (prefix_unary_expression 19 | operand: (integer))))))) 20 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "tree_sitter_hack_binding", 5 | "include_dirs": [ 6 | "type(); 2 | 3 | $this->newtype(); 4 | 5 | $this->shape(); 6 | 7 | $this->tuple; 8 | 9 | $this->clone(); 10 | 11 | $this->print(); 12 | 13 | $this->new(); 14 | 15 | $this->namespace(); 16 | 17 | $this->clone()->new()->print()->$item; 18 | 19 | $this->bool(); 20 | $this->float(); 21 | $this->int(); 22 | $this->string(); 23 | $this->arraykey(); 24 | $this->void(); 25 | $this->nonnull(); 26 | $this->null(); 27 | $this->mixed(); 28 | $this->dynamic(); 29 | $this->noreturn(); 30 | 31 | $this->array(); 32 | $this->varray(); 33 | $this->darray(); 34 | $this->vect(); 35 | $this->dict(); 36 | $this->keyset(); 37 | -------------------------------------------------------------------------------- /bin/generate-parser: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | while [ $# -gt 0 ]; do 6 | case "$1" in 7 | --force) 8 | FORCE=1 9 | shift 10 | ;; 11 | *) 12 | break 13 | ;; 14 | esac 15 | done 16 | 17 | mkdir -p tmp 18 | 19 | CACHED_SHA=$(cat 'tmp/grammar.js.sha' 2>/dev/null || true) 20 | GRAMMAR_SHA=$(sha256sum 'grammar.js' | cut -d' ' -f1) 21 | 22 | # Exit if grammar.js hasn't changed since last time we generated parser. 23 | if [ "$FORCE" != 1 ] && [ "$CACHED_SHA" = "$GRAMMAR_SHA" ]; then 24 | exit 25 | fi 26 | 27 | printf "Generating parser...\n" 28 | 29 | npx tree-sitter generate 30 | 31 | printf "$GRAMMAR_SHA" >'tmp/grammar.js.sha' 32 | -------------------------------------------------------------------------------- /test/cases/expressions/anonymous-function.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (binary_expression 4 | left: (variable) 5 | right: (anonymous_function_expression 6 | (parameters 7 | (parameter 8 | name: (variable))) 9 | body: (compound_statement)))) 10 | (expression_statement 11 | (binary_expression 12 | left: (variable) 13 | right: (anonymous_function_expression 14 | (parameters 15 | (parameter 16 | name: (variable)) 17 | (parameter 18 | name: (variable))) 19 | return_type: (type_specifier) 20 | body: (compound_statement))))) 21 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | name: validate 2 | on: 3 | pull_request: 4 | branches: 5 | - "**" 6 | push: 7 | branches: 8 | - "main" 9 | jobs: 10 | check-build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v2 15 | with: 16 | node-version: 14 17 | - run: npm install 18 | - run: npm run build 19 | - name: Generate corpus 20 | run: "${GITHUB_WORKSPACE}/bin/generate-corpus" 21 | shell: bash 22 | - name: Check for changes 23 | run: "${GITHUB_WORKSPACE}/.github/workflows/validate.sh" 24 | shell: bash 25 | -------------------------------------------------------------------------------- /bin/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM hhvm/hhvm:4.68-latest 2 | 3 | ENV NODE_VERSION 14.7.0 4 | ENV NVM_DIR /usr/local/nvm 5 | 6 | WORKDIR /tree-sitter-hack 7 | 8 | RUN \ 9 | apt-get update -y && \ 10 | apt-get install -y build-essential ruby fd-find 11 | 12 | RUN \ 13 | mkdir -p ${NVM_DIR} && \ 14 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash && \ 15 | . ${NVM_DIR}/nvm.sh && \ 16 | nvm install ${NODE_VERSION} && \ 17 | ln -s $(which node) /usr/local/bin && \ 18 | ln -s $(which npm) /usr/local/bin && \ 19 | ln -s $(which npx) /usr/local/bin && \ 20 | printf "unsafe-perm = true\n" >.npmrc 21 | 22 | RUN mkdir -p /mnt/tree-sitter-hack 23 | -------------------------------------------------------------------------------- /test/cases/expressions/anonymous-function-type.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (anonymous_function_expression 4 | (parameters 5 | (parameter 6 | type: (function_type_specifier 7 | (type_specifier) 8 | (variadic_modifier) 9 | return_type: (type_specifier)) 10 | name: (variable))) 11 | return_type: (tuple_type_specifier 12 | (function_type_specifier 13 | (type_specifier) 14 | return_type: (type_specifier)) 15 | (function_type_specifier 16 | return_type: (type_specifier))) 17 | (use_clause 18 | (variable)) 19 | body: (compound_statement)))) 20 | -------------------------------------------------------------------------------- /test/cases/declarations/attribute-type-parameter.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | name: (identifier) 4 | (type_parameters 5 | (type_parameter 6 | (attribute_modifier 7 | (qualified_identifier 8 | (identifier))) 9 | (reify_modifier) 10 | name: (identifier))) 11 | body: (member_declarations)) 12 | (function_declaration 13 | name: (identifier) 14 | (type_parameters 15 | (type_parameter 16 | (attribute_modifier 17 | (qualified_identifier 18 | (identifier))) 19 | name: (identifier))) 20 | (parameters) 21 | return_type: (type_specifier) 22 | body: (compound_statement))) 23 | -------------------------------------------------------------------------------- /test/cases/declarations/function-type-specifier.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (alias_declaration 3 | (identifier) 4 | (function_type_specifier 5 | (type_specifier) 6 | (type_specifier 7 | (nullable_modifier)) 8 | return_type: (type_specifier))) 9 | (function_declaration 10 | name: (identifier) 11 | (parameters 12 | (parameter 13 | type: (function_type_specifier 14 | (inout_modifier) 15 | (type_specifier) 16 | return_type: (type_specifier)) 17 | name: (variable))) 18 | return_type: (function_type_specifier 19 | (type_specifier) 20 | return_type: (type_specifier)) 21 | body: (compound_statement))) 22 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build/test 2 | on: 3 | pull_request: 4 | branches: 5 | - "**" 6 | push: 7 | branches: 8 | - "main" 9 | jobs: 10 | test_ubuntu: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v2 15 | with: 16 | node-version: 14 17 | - run: npm install 18 | - run: npm test 19 | test_macos: 20 | runs-on: macos-latest 21 | steps: 22 | - uses: actions/checkout@v2 23 | - uses: actions/setup-node@v2 24 | with: 25 | node-version: 14 26 | - run: brew install gnu-sed 27 | - run: npm install 28 | - run: npm test 29 | -------------------------------------------------------------------------------- /test/cases/statements/try.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (try_statement 3 | body: (compound_statement) 4 | (catch_clause 5 | type: (type_specifier 6 | (qualified_identifier 7 | (identifier))) 8 | name: (variable) 9 | body: (compound_statement))) 10 | (try_statement 11 | body: (compound_statement) 12 | (catch_clause 13 | type: (type_specifier 14 | (qualified_identifier 15 | (identifier))) 16 | name: (variable) 17 | body: (compound_statement)) 18 | (catch_clause 19 | type: (type_specifier 20 | (qualified_identifier 21 | (identifier))) 22 | name: (variable) 23 | body: (compound_statement)))) 24 | -------------------------------------------------------------------------------- /test/cases/declarations/const.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (const_declaration 3 | type: (type_specifier) 4 | (const_declarator 5 | name: (identifier) 6 | value: (integer))) 7 | (const_declaration 8 | type: (type_specifier) 9 | (const_declarator 10 | name: (identifier) 11 | value: (integer)) 12 | (const_declarator 13 | name: (identifier) 14 | value: (integer))) 15 | (const_declaration 16 | (const_declarator 17 | name: (identifier) 18 | value: (integer))) 19 | (const_declaration 20 | (const_declarator 21 | name: (identifier) 22 | value: (integer)) 23 | (const_declarator 24 | name: (identifier) 25 | value: (integer)))) 26 | -------------------------------------------------------------------------------- /test/cases/declarations/enum.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (enum_declaration 3 | name: (identifier) 4 | type: (type_specifier)) 5 | (enum_declaration 6 | (attribute_modifier 7 | (qualified_identifier 8 | (identifier))) 9 | name: (identifier) 10 | type: (type_specifier) 11 | (enumerator 12 | (identifier) 13 | (integer)) 14 | (enumerator 15 | (identifier) 16 | (integer)) 17 | (enumerator 18 | (identifier) 19 | (scoped_identifier 20 | (qualified_identifier 21 | (identifier)) 22 | (identifier))) 23 | (enumerator 24 | (identifier) 25 | (binary_expression 26 | left: (string) 27 | right: (string))))) 28 | -------------------------------------------------------------------------------- /test/cases/declarations/tuple-type.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (function_declaration 3 | name: (identifier) 4 | (parameters 5 | (parameter 6 | type: (tuple_type_specifier 7 | (type_specifier 8 | (type_arguments 9 | (type_specifier) 10 | (type_specifier))) 11 | (type_specifier)) 12 | name: (variable))) 13 | return_type: (tuple_type_specifier 14 | (nullable_modifier) 15 | (type_specifier) 16 | (type_specifier 17 | (qualified_identifier 18 | (identifier))) 19 | (type_specifier 20 | (qualified_identifier 21 | (identifier) 22 | (identifier)))) 23 | body: (compound_statement))) 24 | -------------------------------------------------------------------------------- /test/cases/declarations/class-parameter-visibility.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | name: (identifier) 4 | body: (member_declarations 5 | (method_declaration 6 | (visibility_modifier) 7 | name: (identifier) 8 | (parameters 9 | (parameter 10 | (attribute_modifier 11 | (qualified_identifier 12 | (identifier))) 13 | (visibility_modifier) 14 | type: (type_specifier) 15 | name: (variable) 16 | default_value: (integer)) 17 | (parameter 18 | type: (type_specifier) 19 | (variadic_modifier) 20 | name: (variable))) 21 | body: (compound_statement))))) 22 | -------------------------------------------------------------------------------- /test/cases/expressions/anonymous-function-use.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (binary_expression 4 | left: (variable) 5 | right: (anonymous_function_expression 6 | (parameters 7 | (parameter 8 | name: (variable))) 9 | (use_clause 10 | (variable)) 11 | body: (compound_statement)))) 12 | (expression_statement 13 | (binary_expression 14 | left: (variable) 15 | right: (anonymous_function_expression 16 | (parameters 17 | (parameter 18 | name: (variable))) 19 | return_type: (type_specifier) 20 | (use_clause 21 | (variable) 22 | (variable)) 23 | body: (compound_statement))))) 24 | -------------------------------------------------------------------------------- /test/cases/expressions/weird-selection.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (selection_expression 4 | (scoped_identifier 5 | (qualified_identifier 6 | (identifier)) 7 | (identifier)) 8 | (qualified_identifier 9 | (identifier)))) 10 | (expression_statement 11 | (selection_expression 12 | (parenthesized_expression 13 | (binary_expression 14 | left: (variable) 15 | right: (variable))) 16 | (qualified_identifier 17 | (identifier)))) 18 | (expression_statement 19 | (selection_expression 20 | (call_expression 21 | function: (variable) 22 | (arguments)) 23 | (qualified_identifier 24 | (identifier))))) 25 | -------------------------------------------------------------------------------- /bindings/rust/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let src_dir = std::path::Path::new("src"); 3 | 4 | let mut c_config = cc::Build::new(); 5 | c_config.include(src_dir); 6 | c_config 7 | .flag_if_supported("-Wno-unused-parameter") 8 | .flag_if_supported("-Wno-unused-but-set-variable") 9 | .flag_if_supported("-Wno-trigraphs"); 10 | let parser_path = src_dir.join("parser.c"); 11 | c_config.file(&parser_path); 12 | 13 | let scanner_path = src_dir.join("scanner.c"); 14 | c_config.file(&scanner_path); 15 | println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); 16 | 17 | c_config.compile("parser"); 18 | println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); 19 | } 20 | -------------------------------------------------------------------------------- /test/cases/declarations/async-functions.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (function_declaration 3 | (async_modifier) 4 | name: (identifier) 5 | (parameters) 6 | return_type: (type_specifier) 7 | body: (compound_statement)) 8 | (function_declaration 9 | (async_modifier) 10 | name: (identifier) 11 | (type_parameters 12 | (type_parameter 13 | name: (identifier) 14 | constraint_type: (type_specifier))) 15 | (parameters) 16 | body: (compound_statement)) 17 | (expression_statement 18 | (lambda_expression 19 | (async_modifier) 20 | (parameters 21 | (parameter 22 | name: (variable))) 23 | body: (binary_expression 24 | left: (variable) 25 | right: (integer))))) 26 | -------------------------------------------------------------------------------- /test/cases/declarations/trait-where.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (trait_declaration 3 | name: (identifier) 4 | (type_parameters 5 | (type_parameter 6 | name: (identifier))) 7 | (implements_clause 8 | (type_specifier 9 | (qualified_identifier 10 | (identifier)) 11 | (type_arguments 12 | (type_specifier 13 | (qualified_identifier 14 | (identifier)))))) 15 | (where_clause 16 | (where_constraint 17 | constraint_left_type: (type_specifier 18 | (qualified_identifier 19 | (identifier))) 20 | constraint_right_type: (type_specifier 21 | (qualified_identifier 22 | (identifier))))) 23 | body: (member_declarations))) 24 | -------------------------------------------------------------------------------- /test/cases/declarations/interface-where.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (interface_declaration 3 | name: (identifier) 4 | (type_parameters 5 | (type_parameter 6 | name: (identifier))) 7 | (extends_clause 8 | (type_specifier 9 | (qualified_identifier 10 | (identifier)) 11 | (type_arguments 12 | (type_specifier 13 | (qualified_identifier 14 | (identifier)))))) 15 | (where_clause 16 | (where_constraint 17 | constraint_left_type: (type_specifier 18 | (qualified_identifier 19 | (identifier))) 20 | constraint_right_type: (type_specifier 21 | (qualified_identifier 22 | (identifier))))) 23 | body: (member_declarations))) 24 | -------------------------------------------------------------------------------- /test/cases/declarations/require-implements-and-extends.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (trait_declaration 3 | name: (identifier) 4 | (type_parameters 5 | (type_parameter 6 | name: (identifier))) 7 | body: (member_declarations 8 | (require_implements_clause 9 | (type_specifier 10 | (qualified_identifier 11 | (identifier)) 12 | (type_arguments 13 | (type_specifier 14 | (qualified_identifier 15 | (identifier)))))) 16 | (require_extends_clause 17 | (type_specifier 18 | (qualified_identifier 19 | (identifier)) 20 | (type_arguments 21 | (type_specifier 22 | (qualified_identifier 23 | (identifier))))))))) 24 | -------------------------------------------------------------------------------- /bin/docker/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" 6 | IMAGE_NAME="tree-sitter-hack" 7 | CONTAINER_NAME="tree-sitter-hack" 8 | 9 | docker build --rm $DIR -t $IMAGE_NAME 10 | 11 | if docker ps -a --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then 12 | docker stop $CONTAINER_NAME >/dev/null 13 | docker rm $CONTAINER_NAME >/dev/null 14 | fi 15 | 16 | docker run -td \ 17 | --name $CONTAINER_NAME \ 18 | -v $(realpath .):/mnt/tree-sitter-hack:rw \ 19 | $IMAGE_NAME 20 | 21 | docker exec -it $CONTAINER_NAME sh -c 'ln -s /mnt/tree-sitter-hack/* /tree-sitter-hack' 22 | docker exec -it $CONTAINER_NAME sh -c 'rm -rf build node_modules package-lock.json' 23 | docker exec -it $CONTAINER_NAME sh -c 'npm install' 24 | -------------------------------------------------------------------------------- /bin/ts-query: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source bin/require_sed 6 | 7 | # Make Tree-sitter output look like a query. 8 | # 9 | # $ npx tree-sitter parse $@ 10 | # (script [0, 0] - [1, 0] 11 | # (while_statement [0, 0] - [0, 12] 12 | # condition: (parenthesized_expression [0, 6] - [0, 9] 13 | # (integer [0, 7] - [0, 8])) 14 | # body: (expression_statement [0, 10] - [0, 12] 15 | # (integer [0, 10] - [0, 11])))) 16 | # 17 | # $ bin/ts-query $@ 18 | # (script 19 | # (while_statement 20 | # condition: (parenthesized_expression 21 | # (integer)) 22 | # body: (expression_statement 23 | # (integer)))) 24 | 25 | bin/generate-parser 1>/dev/null 26 | 27 | npx tree-sitter parse $@ | $sed -e 's/ \[.*\]//' 28 | -------------------------------------------------------------------------------- /test/cases/expressions/async.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (awaitable_expression 4 | (compound_statement))) 5 | (expression_statement 6 | (awaitable_expression 7 | (compound_statement 8 | (concurrent_statement 9 | (compound_statement 10 | (expression_statement 11 | (prefix_unary_expression 12 | operand: (call_expression 13 | function: (qualified_identifier 14 | (identifier)) 15 | (arguments)))) 16 | (expression_statement 17 | (prefix_unary_expression 18 | operand: (call_expression 19 | function: (qualified_identifier 20 | (identifier)) 21 | (arguments)))))))))) 22 | -------------------------------------------------------------------------------- /test/cases/declarations/empty-function.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (namespace_declaration 3 | body: (compound_statement 4 | (function_declaration 5 | (attribute_modifier 6 | (qualified_identifier 7 | (identifier)) 8 | (qualified_identifier 9 | (identifier))) 10 | name: (identifier) 11 | (parameters 12 | (parameter 13 | name: (variable))) 14 | return_type: (type_specifier)) 15 | (function_declaration 16 | (attribute_modifier 17 | (qualified_identifier 18 | (identifier)) 19 | (qualified_identifier 20 | (identifier))) 21 | name: (identifier) 22 | (parameters 23 | (parameter 24 | name: (variable))) 25 | return_type: (type_specifier))))) 26 | -------------------------------------------------------------------------------- /test/cases/expressions/type-arguments.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (call_expression 4 | function: (qualified_identifier 5 | (identifier)) 6 | (type_arguments 7 | (type_specifier 8 | (qualified_identifier 9 | (identifier)) 10 | (type_arguments 11 | (type_specifier))) 12 | (type_specifier)) 13 | (arguments))) 14 | (expression_statement 15 | (call_expression 16 | function: (qualified_identifier 17 | (identifier)) 18 | (type_arguments) 19 | (arguments))) 20 | (expression_statement 21 | (new_expression 22 | (qualified_identifier 23 | (identifier)) 24 | (type_arguments 25 | (type_specifier)) 26 | (arguments))) 27 | (comment) 28 | (comment) 29 | (comment)) 30 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-braced-mix.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (heredoc 4 | (embedded_braced_expression 5 | (variable)) 6 | (embedded_braced_expression 7 | (selection_expression 8 | (variable) 9 | (qualified_identifier 10 | (identifier)))) 11 | (embedded_braced_expression 12 | (subscript_expression 13 | (variable) 14 | (string))) 15 | (embedded_braced_expression 16 | (call_expression 17 | function: (variable) 18 | (arguments))) 19 | (embedded_braced_expression 20 | (call_expression 21 | function: (variable) 22 | (arguments 23 | (argument 24 | (string))))) 25 | (embedded_braced_expression 26 | (variable))))) 27 | -------------------------------------------------------------------------------- /test/cases/declarations/context-function-types.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (function_declaration 3 | (identifier) 4 | (parameters 5 | (parameter 6 | (function_type_specifier 7 | (type_specifier)) 8 | (variable)) 9 | (parameter 10 | (function_type_specifier 11 | (capability_list 12 | (capability 13 | (identifier)) 14 | (capability 15 | (identifier))) 16 | (type_specifier)) 17 | (variable)) 18 | (parameter 19 | (function_type_specifier 20 | (capability_list) 21 | (type_specifier)) 22 | (variable))) 23 | (capability_list 24 | (capability 25 | (variable))) 26 | (type_specifier) 27 | (compound_statement))) 28 | -------------------------------------------------------------------------------- /test/cases/statements/try-catch-finally.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (try_statement 3 | body: (compound_statement) 4 | (catch_clause 5 | type: (type_specifier 6 | (qualified_identifier 7 | (identifier))) 8 | name: (variable) 9 | body: (compound_statement)) 10 | (finally_clause 11 | body: (compound_statement))) 12 | (try_statement 13 | body: (compound_statement) 14 | (catch_clause 15 | type: (type_specifier 16 | (qualified_identifier 17 | (identifier))) 18 | name: (variable) 19 | body: (compound_statement)) 20 | (catch_clause 21 | type: (type_specifier 22 | (qualified_identifier 23 | (identifier))) 24 | name: (variable) 25 | body: (compound_statement)) 26 | (finally_clause 27 | body: (compound_statement)))) 28 | -------------------------------------------------------------------------------- /test/cases/statements/if-else-if.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (if_statement 3 | condition: (parenthesized_expression 4 | (integer)) 5 | body: (expression_statement 6 | (integer)) 7 | condition: (parenthesized_expression 8 | (integer)) 9 | body: (expression_statement 10 | (integer)) 11 | condition: (parenthesized_expression 12 | (integer)) 13 | body: (expression_statement 14 | (integer)) 15 | else: (expression_statement 16 | (integer))) 17 | (if_statement 18 | condition: (parenthesized_expression 19 | (integer)) 20 | body: (compound_statement) 21 | condition: (parenthesized_expression 22 | (integer)) 23 | body: (compound_statement) 24 | condition: (parenthesized_expression 25 | (integer)) 26 | body: (compound_statement) 27 | else: (compound_statement))) 28 | -------------------------------------------------------------------------------- /bin/hh-errors: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Print files with hh_parse errors and filter out the rest. 6 | 7 | hh_parse --show-file-name --full-fidelity-errors-all $@ | 8 | grep -v 'A \.php file must begin with' | 9 | grep -v 'Nested ternary expressions inside ternary expressions are ambiguous' | 10 | 11 | # Only print file paths that have errors 12 | ruby -e "$( 13 | cat <<-RUBY 14 | path = nil 15 | 16 | ARGF.each_line do |line| 17 | # Consume lines until we have something that does *not* look like a file path. 18 | next path = line if line =~ /(^.*\.(php|hack))\s/ 19 | 20 | unless path.nil? 21 | # Print the file path before we print any errors. 22 | puts path 23 | # Only print the file path once. 24 | path = nil 25 | end 26 | 27 | puts line 28 | end 29 | RUBY 30 | )" 31 | -------------------------------------------------------------------------------- /test/cases/statements/if-nested.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (if_statement 3 | condition: (parenthesized_expression 4 | (integer)) 5 | body: (if_statement 6 | condition: (parenthesized_expression 7 | (integer)) 8 | body: (expression_statement 9 | (integer)) 10 | else: (expression_statement 11 | (integer)))) 12 | (if_statement 13 | condition: (parenthesized_expression 14 | (integer)) 15 | body: (compound_statement 16 | (if_statement 17 | condition: (parenthesized_expression 18 | (integer)) 19 | body: (expression_statement 20 | (integer)))) 21 | else: (compound_statement 22 | (if_statement 23 | condition: (parenthesized_expression 24 | (integer)) 25 | body: (compound_statement 26 | (expression_statement 27 | (integer))))))) 28 | -------------------------------------------------------------------------------- /test/cases/expressions/function-call.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (call_expression 4 | function: (qualified_identifier 5 | (identifier)) 6 | (arguments))) 7 | (expression_statement 8 | (call_expression 9 | function: (qualified_identifier 10 | (identifier)) 11 | (type_arguments 12 | (type_specifier)) 13 | (arguments 14 | (argument 15 | (integer)) 16 | (argument 17 | (inout_modifier) 18 | (variable)) 19 | (argument 20 | (variadic_modifier) 21 | (null))))) 22 | (expression_statement 23 | (call_expression 24 | function: (subscript_expression 25 | (variable) 26 | (call_expression 27 | function: (qualified_identifier 28 | (identifier)) 29 | (arguments))) 30 | (arguments)))) 31 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-braced-call.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (heredoc 4 | (embedded_braced_expression 5 | (call_expression 6 | function: (call_expression 7 | function: (variable) 8 | (arguments)) 9 | (arguments))) 10 | (embedded_braced_expression 11 | (call_expression 12 | function: (selection_expression 13 | (call_expression 14 | function: (variable) 15 | (arguments)) 16 | (qualified_identifier 17 | (identifier))) 18 | (arguments))) 19 | (embedded_braced_expression 20 | (call_expression 21 | function: (subscript_expression 22 | (call_expression 23 | function: (variable) 24 | (arguments)) 25 | (string)) 26 | (arguments)))))) 27 | -------------------------------------------------------------------------------- /test/cases/statements/do-nested.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (do_statement 3 | body: (do_statement 4 | body: (expression_statement 5 | (integer)) 6 | condition: (parenthesized_expression 7 | (integer))) 8 | condition: (parenthesized_expression 9 | (integer))) 10 | (do_statement 11 | body: (compound_statement 12 | (do_statement 13 | body: (expression_statement 14 | (integer)) 15 | condition: (parenthesized_expression 16 | (integer)))) 17 | condition: (parenthesized_expression 18 | (integer))) 19 | (do_statement 20 | body: (compound_statement 21 | (do_statement 22 | body: (compound_statement 23 | (expression_statement 24 | (integer))) 25 | condition: (parenthesized_expression 26 | (integer)))) 27 | condition: (parenthesized_expression 28 | (integer)))) 29 | -------------------------------------------------------------------------------- /test/cases/expressions/list.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (binary_expression 4 | left: (list_expression 5 | (variable)) 6 | right: (tuple 7 | (string)))) 8 | (expression_statement 9 | (binary_expression 10 | left: (list_expression 11 | (variable)) 12 | right: (tuple 13 | (string) 14 | (string)))) 15 | (expression_statement 16 | (binary_expression 17 | left: (list_expression 18 | (variable) 19 | (variable)) 20 | right: (tuple 21 | (string) 22 | (string) 23 | (string)))) 24 | (expression_statement 25 | (binary_expression 26 | left: (list_expression 27 | (variable) 28 | (variable)) 29 | right: (tuple 30 | (string) 31 | (string) 32 | (string) 33 | (string) 34 | (string) 35 | (string))))) 36 | -------------------------------------------------------------------------------- /test/cases/expressions/collection.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (collection 4 | (qualified_identifier 5 | (identifier)))) 6 | (expression_statement 7 | (collection 8 | (qualified_identifier 9 | (identifier) 10 | (identifier)) 11 | (integer) 12 | (integer) 13 | (integer))) 14 | (expression_statement 15 | (collection 16 | (qualified_identifier 17 | (identifier) 18 | (identifier)))) 19 | (expression_statement 20 | (collection 21 | (qualified_identifier 22 | (identifier)) 23 | (element_initializer 24 | (string) 25 | (integer)))) 26 | (expression_statement 27 | (collection 28 | (qualified_identifier 29 | (identifier)))) 30 | (expression_statement 31 | (collection 32 | (qualified_identifier 33 | (identifier)) 34 | (string) 35 | (string)))) 36 | -------------------------------------------------------------------------------- /test/cases/statements/foreach.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (foreach_statement 3 | collection: (variable) 4 | value: (variable) 5 | body: (compound_statement)) 6 | (foreach_statement 7 | collection: (array 8 | (array_type)) 9 | key: (variable) 10 | value: (subscript_expression 11 | (variable) 12 | (integer)) 13 | body: (compound_statement)) 14 | (foreach_statement 15 | collection: (array 16 | (array_type) 17 | (type_arguments 18 | (type_specifier) 19 | (type_specifier))) 20 | value: (list_expression 21 | (subscript_expression 22 | (variable) 23 | (subscript_expression 24 | (as_expression 25 | left: (array 26 | (array_type)) 27 | right: (type_specifier)) 28 | (integer))) 29 | (variable)) 30 | body: (compound_statement)) 31 | (comment) 32 | (comment) 33 | (comment)) 34 | -------------------------------------------------------------------------------- /test/cases/expressions/function-pointers.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (function_pointer 4 | (scoped_identifier 5 | (qualified_identifier 6 | (identifier)) 7 | (identifier)) 8 | (type_arguments))) 9 | (expression_statement 10 | (function_pointer 11 | (qualified_identifier 12 | (identifier)) 13 | (type_arguments))) 14 | (expression_statement 15 | (function_pointer 16 | (qualified_identifier 17 | (identifier) 18 | (identifier) 19 | (identifier) 20 | (identifier)) 21 | (type_arguments))) 22 | (expression_statement 23 | (function_pointer 24 | (qualified_identifier 25 | (identifier)) 26 | (type_arguments 27 | (type_specifier) 28 | (type_specifier 29 | (qualified_identifier 30 | (identifier))))))) 31 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-string-not-comment.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (xhp_expression 4 | (xhp_open 5 | (xhp_identifier)) 6 | (xhp_string) 7 | (xhp_close 8 | (xhp_identifier)))) 9 | (expression_statement 10 | (xhp_expression 11 | (xhp_open 12 | (xhp_identifier)) 13 | (xhp_string) 14 | (xhp_close 15 | (xhp_identifier)))) 16 | (expression_statement 17 | (xhp_expression 18 | (xhp_open 19 | (xhp_identifier)) 20 | (xhp_string) 21 | (xhp_close 22 | (xhp_identifier)))) 23 | (expression_statement 24 | (xhp_expression 25 | (xhp_open 26 | (xhp_identifier)) 27 | (xhp_string) 28 | (xhp_close 29 | (xhp_identifier)))) 30 | (expression_statement 31 | (xhp_expression 32 | (xhp_open 33 | (xhp_identifier)) 34 | (xhp_string) 35 | (xhp_close 36 | (xhp_identifier))))) 37 | -------------------------------------------------------------------------------- /bindings/node/binding.cc: -------------------------------------------------------------------------------- 1 | #include "tree_sitter/parser.h" 2 | #include 3 | #include "nan.h" 4 | 5 | using namespace v8; 6 | 7 | extern "C" TSLanguage * tree_sitter_hack(); 8 | 9 | namespace { 10 | 11 | NAN_METHOD(New) {} 12 | 13 | void Init(Local exports, Local module) { 14 | Local tpl = Nan::New(New); 15 | tpl->SetClassName(Nan::New("Language").ToLocalChecked()); 16 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 17 | 18 | Local constructor = Nan::GetFunction(tpl).ToLocalChecked(); 19 | Local instance = constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked(); 20 | Nan::SetInternalFieldPointer(instance, 0, tree_sitter_hack()); 21 | 22 | Nan::Set(instance, Nan::New("name").ToLocalChecked(), Nan::New("hack").ToLocalChecked()); 23 | Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance); 24 | } 25 | 26 | NODE_MODULE(tree_sitter_hack_binding, Init) 27 | 28 | } // namespace 29 | -------------------------------------------------------------------------------- /test/cases/declarations/keyword-as-class.hack: -------------------------------------------------------------------------------- 1 | // Allowed keywords: https://github.com/facebook/hhvm/blob/1101ea73b0b4693e858235aa54611e12408f9edc/hphp/hack/test/full_fidelity/cases/keyword_as_class_allowed.php 2 | // Unsupported keywords: https://github.com/facebook/hhvm/blob/d7dc631ce6010a7ee484830b9e5c32bbd7b26cdb/hphp/hack/test/full_fidelity/cases/keyword_as_class_reserved.php 3 | class attribute {} 4 | class binary {} 5 | class category {} 6 | class children {} 7 | class define {} 8 | class enum {} 9 | class fallthrough {} 10 | class from {} 11 | class is {} 12 | class let {} 13 | class newtype {} 14 | class Object {} 15 | class object {} 16 | class super {} 17 | class type {} 18 | class where {} 19 | 20 | new attribute(); 21 | new binary(); 22 | new category(); 23 | new children(); 24 | new define(); 25 | new enum(); 26 | new fallthrough(); 27 | new from(); 28 | new is(); 29 | new let(); 30 | new newtype(); 31 | new Object(); 32 | new object(); 33 | new super(); 34 | new type(); 35 | new where(); 36 | -------------------------------------------------------------------------------- /test/cases/literals/floats.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (float)) 4 | (expression_statement 5 | (float)) 6 | (expression_statement 7 | (float)) 8 | (expression_statement 9 | (float)) 10 | (expression_statement 11 | (float)) 12 | (expression_statement 13 | (float)) 14 | (expression_statement 15 | (float)) 16 | (expression_statement 17 | (float)) 18 | (expression_statement 19 | (float)) 20 | (expression_statement 21 | (float)) 22 | (expression_statement 23 | (float)) 24 | (expression_statement 25 | (float)) 26 | (expression_statement 27 | (float)) 28 | (expression_statement 29 | (float)) 30 | (expression_statement 31 | (float)) 32 | (expression_statement 33 | (float)) 34 | (expression_statement 35 | (float)) 36 | (expression_statement 37 | (float)) 38 | (expression_statement 39 | (float)) 40 | (expression_statement 41 | (float)) 42 | (expression_statement 43 | (float))) 44 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Introduction 4 | 5 | Diversity and inclusion make our community strong. We encourage participation from the most varied and diverse backgrounds possible and want to be very clear about where we stand. 6 | 7 | Our goal is to maintain a safe, helpful and friendly community for everyone, regardless of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other defining characteristic. 8 | 9 | This code and related procedures also apply to unacceptable behavior occurring outside the scope of community activities, in all community venues (online and in-person) as well as in all one-on-one communications, and anywhere such behavior has the potential to adversely affect the safety and well-being of community members. 10 | 11 | For more information on our code of conduct, please visit [https://slackhq.github.io/code-of-conduct](https://slackhq.github.io/code-of-conduct) 12 | -------------------------------------------------------------------------------- /test/cases/declarations/enum-class.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | (identifier) 4 | (member_declarations)) 5 | (abstract_enum_class_declaration 6 | (identifier) 7 | (type_specifier) 8 | (typed_enumerator 9 | (type_specifier) 10 | (enumerator 11 | (identifier) 12 | (integer))) 13 | (typed_enumerator 14 | (type_specifier) 15 | (enumerator 16 | (identifier) 17 | (string))) 18 | (type_specifier 19 | (qualified_identifier 20 | (identifier))) 21 | (identifier)) 22 | (enum_class_declaration 23 | (identifier) 24 | (type_specifier) 25 | (extends_clause 26 | (type_specifier 27 | (qualified_identifier 28 | (identifier)))) 29 | (typed_enumerator 30 | (type_specifier 31 | (qualified_identifier 32 | (identifier))) 33 | (enumerator 34 | (identifier) 35 | (new_expression 36 | (qualified_identifier 37 | (identifier)) 38 | (arguments)))))) 39 | -------------------------------------------------------------------------------- /test/cases/statements/for.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (for_statement 3 | body: (expression_statement 4 | (integer))) 5 | (for_statement 6 | (binary_expression 7 | left: (variable) 8 | right: (integer)) 9 | (binary_expression 10 | left: (variable) 11 | right: (integer)) 12 | (postfix_unary_expression 13 | (variable)) 14 | body: (expression_statement 15 | (integer))) 16 | (for_statement 17 | (binary_expression 18 | left: (variable) 19 | right: (integer)) 20 | (binary_expression 21 | left: (variable) 22 | right: (integer)) 23 | (postfix_unary_expression 24 | (variable)) 25 | (binary_expression 26 | left: (variable) 27 | right: (integer)) 28 | (postfix_unary_expression 29 | (variable)) 30 | (binary_expression 31 | left: (variable) 32 | right: (call_expression 33 | function: (qualified_identifier 34 | (identifier)) 35 | (arguments))) 36 | body: (expression_statement 37 | (integer)))) 38 | -------------------------------------------------------------------------------- /test/cases/expressions/shape-type-keys.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (alias_declaration 3 | (identifier) 4 | (shape_type_specifier 5 | (field_specifier 6 | (new_expression 7 | (qualified_identifier 8 | (identifier)) 9 | (arguments)) 10 | (type_specifier 11 | (qualified_identifier 12 | (identifier)))) 13 | (field_specifier 14 | (call_expression 15 | function: (qualified_identifier 16 | (identifier)) 17 | (arguments)) 18 | (type_specifier)) 19 | (field_specifier 20 | (string) 21 | (type_specifier)) 22 | (field_specifier 23 | (call_expression 24 | function: (parenthesized_expression 25 | (lambda_expression 26 | (parameters) 27 | body: (lambda_expression 28 | (parameters 29 | (parameter 30 | name: (variable))) 31 | body: (variable)))) 32 | (arguments)) 33 | (type_specifier))))) 34 | -------------------------------------------------------------------------------- /test/cases/expressions/lambda.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (lambda_expression 4 | (parameters 5 | (parameter 6 | type: (type_specifier) 7 | name: (variable))) 8 | return_type: (type_specifier) 9 | body: (binary_expression 10 | left: (variable) 11 | right: (integer)))) 12 | (expression_statement 13 | (lambda_expression 14 | (parameters 15 | (parameter 16 | type: (type_specifier) 17 | name: (variable))) 18 | body: (binary_expression 19 | left: (variable) 20 | right: (integer)))) 21 | (expression_statement 22 | (lambda_expression 23 | (parameters 24 | (parameter 25 | name: (variable))) 26 | body: (binary_expression 27 | left: (variable) 28 | right: (integer)))) 29 | (expression_statement 30 | (lambda_expression 31 | (parameters 32 | (parameter 33 | name: (variable))) 34 | body: (binary_expression 35 | left: (variable) 36 | right: (integer))))) 37 | -------------------------------------------------------------------------------- /test/cases/expressions/lambda-with-lambda-arg.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (lambda_expression 4 | (parameters 5 | (parameter 6 | type: (function_type_specifier 7 | return_type: (type_specifier)) 8 | name: (variable))) 9 | return_type: (type_specifier) 10 | body: (compound_statement))) 11 | (expression_statement 12 | (lambda_expression 13 | (parameters 14 | (parameter 15 | type: (function_type_specifier 16 | (comment) 17 | return_type: (type_specifier)) 18 | name: (variable))) 19 | return_type: (type_specifier) 20 | body: (compound_statement))) 21 | (expression_statement 22 | (lambda_expression 23 | (parameters 24 | (parameter 25 | type: (function_type_specifier 26 | (comment) 27 | return_type: (type_specifier)) 28 | name: (variable))) 29 | return_type: (type_specifier) 30 | body: (compound_statement))) 31 | (comment) 32 | (comment) 33 | (comment) 34 | (comment)) 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Describe the bug 11 | A clear and concise description of what the bug is. 12 | 13 | ### Requirements (place an `x` in each of the `[ ]`)** 14 | * [ ] I've read and understood the [Contributing guidelines](../CONTRIBUTING.md) and have done my best effort to follow them. 15 | * [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct). 16 | * [ ] I've searched for any related issues and avoided creating a duplicate issue. 17 | 18 | ### To Reproduce 19 | Steps to reproduce the behavior: 20 | 21 | ### Expected behavior 22 | A clear and concise description of what you expected to happen. 23 | 24 | #### Screenshots 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | #### Reproducible in: 28 | 29 | {project_name} version: 30 | 31 | {platform_name} version: 32 | 33 | OS version(s): 34 | 35 | #### Additional context 36 | Add any other context about the problem here. -------------------------------------------------------------------------------- /test/cases/expressions/selection-brace.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (selection_expression 4 | (variable) 5 | (braced_expression 6 | (variable)))) 7 | (expression_statement 8 | (selection_expression 9 | (variable) 10 | (braced_expression 11 | (call_expression 12 | function: (selection_expression 13 | (variable) 14 | (qualified_identifier 15 | (identifier))) 16 | (arguments))))) 17 | (expression_statement 18 | (selection_expression 19 | (selection_expression 20 | (selection_expression 21 | (variable) 22 | (braced_expression 23 | (variable))) 24 | (braced_expression 25 | (variable))) 26 | (braced_expression 27 | (variable)))) 28 | (expression_statement 29 | (selection_expression 30 | (selection_expression 31 | (selection_expression 32 | (variable) 33 | (braced_expression 34 | (variable))) 35 | (variable)) 36 | (braced_expression 37 | (variable))))) 38 | -------------------------------------------------------------------------------- /test/cases/expressions/pipe.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (binary_expression 4 | left: (binary_expression 5 | left: (array 6 | (array_type) 7 | (integer) 8 | (integer)) 9 | right: (call_expression 10 | function: (qualified_identifier 11 | (identifier) 12 | (identifier)) 13 | (arguments 14 | (argument 15 | (pipe_variable)) 16 | (argument 17 | (lambda_expression 18 | (parameters 19 | (parameter 20 | name: (variable))) 21 | body: (binary_expression 22 | left: (variable) 23 | right: (integer))))))) 24 | right: (call_expression 25 | function: (lambda_expression 26 | (parameters 27 | (parameter 28 | name: (variable))) 29 | body: (compound_statement 30 | (return_statement 31 | (pipe_variable)))) 32 | (arguments 33 | (argument 34 | (pipe_variable))))))) 35 | -------------------------------------------------------------------------------- /test/cases/declarations/function-where.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (function_declaration 3 | name: (identifier) 4 | (type_parameters 5 | (type_parameter 6 | name: (identifier))) 7 | (parameters) 8 | return_type: (type_specifier 9 | (qualified_identifier 10 | (identifier))) 11 | (where_clause 12 | (where_constraint 13 | constraint_left_type: (type_specifier 14 | (qualified_identifier 15 | (identifier))) 16 | constraint_right_type: (type_specifier))) 17 | body: (compound_statement)) 18 | (function_declaration 19 | name: (identifier) 20 | (type_parameters 21 | (type_parameter 22 | name: (identifier))) 23 | (parameters) 24 | return_type: (type_specifier 25 | (qualified_identifier 26 | (identifier))) 27 | (where_clause 28 | (where_constraint 29 | constraint_left_type: (type_specifier 30 | (type_arguments 31 | (type_specifier 32 | (qualified_identifier 33 | (identifier))))) 34 | constraint_right_type: (type_specifier))) 35 | body: (compound_statement))) 36 | -------------------------------------------------------------------------------- /test/cases/expressions/ternary.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (ternary_expression 4 | condition: (variable) 5 | consequence: (true) 6 | alternative: (false))) 7 | (expression_statement 8 | (binary_expression 9 | left: (variable) 10 | right: (false))) 11 | (expression_statement 12 | (ternary_expression 13 | condition: (ternary_expression 14 | condition: (binary_expression 15 | left: (variable) 16 | right: (integer)) 17 | consequence: (binary_expression 18 | left: (variable) 19 | right: (integer)) 20 | alternative: (binary_expression 21 | left: (variable) 22 | right: (integer))) 23 | consequence: (integer) 24 | alternative: (integer))) 25 | (expression_statement 26 | (ternary_expression 27 | condition: (binary_expression 28 | left: (binary_expression 29 | left: (variable) 30 | right: (integer)) 31 | right: (binary_expression 32 | left: (variable) 33 | right: (integer))) 34 | consequence: (integer) 35 | alternative: (integer)))) 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Antonio de Jesus Ochoa Solano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/cases/declarations/method.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | (abstract_modifier) 4 | name: (identifier) 5 | body: (member_declarations 6 | (method_declaration 7 | (static_modifier) 8 | name: (identifier) 9 | (parameters) 10 | return_type: (type_specifier) 11 | body: (compound_statement)) 12 | (method_declaration 13 | (visibility_modifier) 14 | name: (identifier) 15 | (parameters) 16 | return_type: (type_specifier) 17 | body: (compound_statement)) 18 | (method_declaration 19 | name: (identifier) 20 | (parameters) 21 | return_type: (type_specifier) 22 | body: (compound_statement)) 23 | (method_declaration 24 | (abstract_modifier) 25 | (visibility_modifier) 26 | (static_modifier) 27 | name: (identifier) 28 | (parameters)) 29 | (method_declaration 30 | (final_modifier) 31 | (visibility_modifier) 32 | (static_modifier) 33 | name: (identifier) 34 | (parameters) 35 | return_type: (type_specifier) 36 | body: (compound_statement))))) 37 | -------------------------------------------------------------------------------- /test/cases/declarations/attribute.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | (attribute_modifier 4 | (qualified_identifier 5 | (identifier)) 6 | (qualified_identifier 7 | (identifier)) 8 | (arguments 9 | (argument 10 | (integer)) 11 | (argument 12 | (integer)))) 13 | name: (identifier) 14 | body: (member_declarations 15 | (method_declaration 16 | (attribute_modifier 17 | (qualified_identifier 18 | (identifier)) 19 | (qualified_identifier 20 | (identifier)) 21 | (arguments 22 | (argument 23 | (integer)) 24 | (argument 25 | (integer)))) 26 | name: (identifier) 27 | (parameters) 28 | body: (compound_statement)))) 29 | (function_declaration 30 | (attribute_modifier 31 | (qualified_identifier 32 | (identifier)) 33 | (arguments 34 | (argument 35 | (qualified_identifier 36 | (identifier)))) 37 | (qualified_identifier 38 | (identifier))) 39 | name: (identifier) 40 | (parameters) 41 | body: (compound_statement))) 42 | -------------------------------------------------------------------------------- /test/cases/declarations/xhp-class-attribute.hack: -------------------------------------------------------------------------------- 1 | class :a { 2 | attribute int extra_attr; 3 | // XHP identifiers are optional for this case of attribute transfer. 4 | attribute :XHP:HTML:div; 5 | } 6 | 7 | // DEPRECATED: 8 | // Before XHP namespace support (in XHP-Lib v3), 9 | // a special category keyword could be used instead of an interface. 10 | // Note: An XHP class cannot have multiple category or children declarations. 11 | class :a { 12 | category %foo:bar; 13 | } 14 | class :a { 15 | category %name1, %name2; 16 | } 17 | 18 | // Also, a special children keyword with a regex-like syntax could be used. 19 | // See https://github.com/hhvm/xhp-lib/blob/v3.x/tests/ChildRuleTest.php 20 | class :a { 21 | children (:div); 22 | } 23 | class :a { 24 | children any; 25 | } 26 | class :a { 27 | children (:bar*, :baz?, pcdata); 28 | } 29 | class :a { 30 | children (:div*); 31 | } 32 | class :a { 33 | children (:div+); 34 | } 35 | class :a { 36 | children (:div, :div); 37 | } 38 | class :a { 39 | children (:div | :code); 40 | } 41 | class :a { 42 | children (:div | (:code+)); 43 | } 44 | class :a { 45 | children (:div | :code | :p); 46 | } 47 | class :a { 48 | children (%flow); 49 | } 50 | -------------------------------------------------------------------------------- /test/cases/statements/concurrent.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (concurrent_statement 3 | (compound_statement 4 | (expression_statement 5 | (prefix_unary_expression 6 | operand: (call_expression 7 | function: (qualified_identifier 8 | (identifier)) 9 | (arguments)))) 10 | (expression_statement 11 | (prefix_unary_expression 12 | operand: (call_expression 13 | function: (qualified_identifier 14 | (identifier)) 15 | (arguments)))))) 16 | (concurrent_statement 17 | (compound_statement 18 | (expression_statement 19 | (prefix_unary_expression 20 | operand: (call_expression 21 | function: (qualified_identifier 22 | (identifier)) 23 | (arguments)))) 24 | (expression_statement 25 | (prefix_unary_expression 26 | operand: (awaitable_expression 27 | (compound_statement 28 | (expression_statement 29 | (prefix_unary_expression 30 | operand: (call_expression 31 | function: (qualified_identifier 32 | (identifier)) 33 | (arguments))))))))))) 34 | -------------------------------------------------------------------------------- /test/cases/declarations/contexts.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (function_declaration 3 | (identifier) 4 | (parameters) 5 | (type_specifier) 6 | (compound_statement)) 7 | (function_declaration 8 | (identifier) 9 | (parameters) 10 | (capability_list) 11 | (type_specifier) 12 | (compound_statement)) 13 | (function_declaration 14 | (identifier) 15 | (parameters) 16 | (capability_list 17 | (capability 18 | (identifier))) 19 | (type_specifier) 20 | (compound_statement)) 21 | (function_declaration 22 | (identifier) 23 | (parameters) 24 | (capability_list 25 | (capability 26 | (identifier)) 27 | (capability 28 | (identifier)) 29 | (capability 30 | (identifier))) 31 | (type_specifier) 32 | (compound_statement)) 33 | (function_declaration 34 | (identifier) 35 | (parameters) 36 | (capability_list 37 | (capability 38 | (identifier) 39 | (type_parameters 40 | (type_parameter 41 | (identifier))))) 42 | (type_specifier) 43 | (compound_statement 44 | (throw_statement 45 | (new_expression 46 | (qualified_identifier 47 | (identifier)) 48 | (arguments)))))) 49 | -------------------------------------------------------------------------------- /test/cases/declarations/function.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (function_declaration 3 | name: (identifier) 4 | (parameters) 5 | return_type: (type_specifier) 6 | body: (compound_statement)) 7 | (function_declaration 8 | name: (identifier) 9 | (parameters 10 | (variadic_modifier)) 11 | body: (compound_statement)) 12 | (function_declaration 13 | name: (identifier) 14 | (parameters 15 | (parameter 16 | name: (variable))) 17 | body: (compound_statement)) 18 | (function_declaration 19 | name: (identifier) 20 | (parameters 21 | (parameter 22 | type: (type_specifier) 23 | name: (variable))) 24 | body: (compound_statement)) 25 | (function_declaration 26 | name: (identifier) 27 | (parameters 28 | (parameter 29 | type: (type_specifier) 30 | name: (variable))) 31 | return_type: (type_specifier) 32 | body: (compound_statement)) 33 | (function_declaration 34 | name: (identifier) 35 | (parameters 36 | (parameter 37 | type: (type_specifier) 38 | name: (variable)) 39 | (parameter 40 | type: (type_specifier) 41 | name: (variable))) 42 | return_type: (type_specifier) 43 | body: (compound_statement))) 44 | -------------------------------------------------------------------------------- /test/cases/expressions/lambda-attribute.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (comment) 3 | (expression_statement 4 | (parenthesized_expression 5 | (lambda_expression 6 | (attribute_modifier 7 | (qualified_identifier 8 | (identifier)) 9 | (arguments 10 | (argument 11 | (integer))) 12 | (qualified_identifier 13 | (identifier)) 14 | (arguments 15 | (argument 16 | (integer)) 17 | (argument 18 | (integer)))) 19 | (parameters 20 | (parameter 21 | type: (type_specifier) 22 | name: (variable))) 23 | return_type: (type_specifier) 24 | body: (binary_expression 25 | left: (variable) 26 | right: (integer))))) 27 | (expression_statement 28 | (parenthesized_expression 29 | (lambda_expression 30 | (attribute_modifier 31 | (qualified_identifier 32 | (identifier)) 33 | (qualified_identifier 34 | (identifier)) 35 | (arguments 36 | (argument 37 | (integer)))) 38 | (parameters 39 | (parameter 40 | name: (variable))) 41 | body: (compound_statement))))) 42 | -------------------------------------------------------------------------------- /test/cases/expressions/selection.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (selection_expression 4 | (variable) 5 | (variable))) 6 | (expression_statement 7 | (selection_expression 8 | (qualified_identifier 9 | (identifier)) 10 | (variable))) 11 | (expression_statement 12 | (selection_expression 13 | (qualified_identifier 14 | (identifier)) 15 | (qualified_identifier 16 | (identifier)))) 17 | (expression_statement 18 | (selection_expression 19 | (subscript_expression 20 | (selection_expression 21 | (qualified_identifier 22 | (identifier) 23 | (identifier)) 24 | (variable)) 25 | (integer)) 26 | (qualified_identifier 27 | (identifier)))) 28 | (expression_statement 29 | (subscript_expression 30 | (selection_expression 31 | (subscript_expression 32 | (variable) 33 | (integer)) 34 | (variable)) 35 | (integer))) 36 | (expression_statement 37 | (subscript_expression 38 | (selection_expression 39 | (subscript_expression 40 | (variable) 41 | (integer)) 42 | (qualified_identifier 43 | (identifier))) 44 | (integer)))) 45 | -------------------------------------------------------------------------------- /test/cases/expressions/namespace-keyword.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (function_declaration 3 | name: (identifier) 4 | (parameters 5 | (parameter 6 | type: (type_specifier 7 | (qualified_identifier 8 | (identifier) 9 | (identifier))) 10 | name: (variable))) 11 | return_type: (type_specifier 12 | (qualified_identifier 13 | (identifier) 14 | (identifier))) 15 | body: (compound_statement)) 16 | (function_declaration 17 | name: (identifier) 18 | (parameters 19 | (parameter 20 | type: (type_specifier 21 | (qualified_identifier 22 | (identifier) 23 | (identifier) 24 | (identifier))) 25 | name: (variable))) 26 | return_type: (type_specifier 27 | (qualified_identifier 28 | (identifier) 29 | (identifier) 30 | (identifier))) 31 | body: (compound_statement)) 32 | (expression_statement 33 | (call_expression 34 | function: (qualified_identifier 35 | (identifier) 36 | (identifier)) 37 | (arguments))) 38 | (expression_statement 39 | (call_expression 40 | function: (qualified_identifier 41 | (identifier) 42 | (identifier) 43 | (identifier)) 44 | (arguments)))) 45 | -------------------------------------------------------------------------------- /test/cases/declarations/class-const-ctx.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | (abstract_modifier) 4 | (identifier) 5 | (member_declarations 6 | (context_const_declaration 7 | (abstract_modifier) 8 | (identifier) 9 | (capability_list 10 | (capability 11 | (identifier)))) 12 | (context_const_declaration 13 | (abstract_modifier) 14 | (identifier) 15 | (capability_list 16 | (capability 17 | (identifier)))) 18 | (context_const_declaration 19 | (abstract_modifier) 20 | (identifier) 21 | (capability_list 22 | (capability 23 | (identifier))) 24 | (capability_list 25 | (capability 26 | (identifier)) 27 | (capability 28 | (identifier)))) 29 | (context_const_declaration 30 | (identifier) 31 | (capability_list 32 | (capability 33 | (identifier)))) 34 | (context_const_declaration 35 | (identifier) 36 | (capability_list 37 | (capability 38 | (identifier))) 39 | (capability_list 40 | (capability 41 | (identifier))))))) 42 | -------------------------------------------------------------------------------- /test/cases/expressions/selection-with-as.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (selection_expression 4 | (as_expression 5 | left: (variable) 6 | right: (type_specifier)) 7 | (qualified_identifier 8 | (identifier)))) 9 | (expression_statement 10 | (call_expression 11 | function: (selection_expression 12 | (as_expression 13 | left: (variable) 14 | right: (type_specifier)) 15 | (qualified_identifier 16 | (identifier))) 17 | (arguments))) 18 | (expression_statement 19 | (selection_expression 20 | (parenthesized_expression 21 | (as_expression 22 | left: (variable) 23 | right: (type_specifier))) 24 | (qualified_identifier 25 | (identifier)))) 26 | (expression_statement 27 | (as_expression 28 | left: (selection_expression 29 | (variable) 30 | (qualified_identifier 31 | (identifier))) 32 | right: (type_specifier))) 33 | (expression_statement 34 | (selection_expression 35 | (as_expression 36 | left: (selection_expression 37 | (variable) 38 | (qualified_identifier 39 | (identifier))) 40 | right: (type_specifier)) 41 | (qualified_identifier 42 | (identifier))))) 43 | -------------------------------------------------------------------------------- /test/cases/declarations/type-const.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | name: (identifier) 4 | body: (member_declarations 5 | (type_const_declaration 6 | name: (identifier)) 7 | (type_const_declaration 8 | name: (identifier) 9 | type: (type_specifier)) 10 | (type_const_declaration 11 | (attribute_modifier 12 | (qualified_identifier 13 | (identifier)) 14 | (arguments 15 | (argument 16 | (integer))) 17 | (qualified_identifier 18 | (identifier)) 19 | (arguments 20 | (argument 21 | (integer)) 22 | (argument 23 | (integer)))) 24 | name: (identifier) 25 | as: (type_specifier)) 26 | (type_const_declaration 27 | name: (identifier) 28 | (type_parameters 29 | (type_parameter 30 | (attribute_modifier 31 | (qualified_identifier 32 | (identifier))) 33 | name: (identifier))) 34 | as: (type_specifier) 35 | type: (type_specifier)) 36 | (type_const_declaration 37 | (abstract_modifier) 38 | name: (identifier) 39 | as: (type_specifier 40 | (nullable_modifier)) 41 | type: (type_specifier 42 | (nullable_modifier)))))) 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-hacklang", 3 | "version": "0.0.4", 4 | "description": "Hack grammar for tree-sitter", 5 | "main": "bindings/node", 6 | "keywords": [ 7 | "parser", 8 | "lexer", 9 | "hacklang", 10 | "hhvm" 11 | ], 12 | "author": "Antonio de Jesus Ochoa Solano", 13 | "license": "MIT", 14 | "homepage": "https://github.com/slackhq/tree-sitter-hack#readme", 15 | "bugs": { 16 | "url": "https://github.com/slackhq/tree-sitter-hack/issues" 17 | }, 18 | "engines": { 19 | "node": ">=14.7.0" 20 | }, 21 | "dependencies": { 22 | "nan": "^2.14.1" 23 | }, 24 | "devDependencies": { 25 | "tree-sitter-cli": "~0.20.6" 26 | }, 27 | "scripts": { 28 | "build": "bin/generate-parser --force && node-gyp build", 29 | "test": "bin/generate-corpus && tree-sitter test", 30 | "test-corpus": "bin/test-corpus", 31 | "test-examples": "bin/test-examples", 32 | "reset": "rm -rf build node_modules package-lock.json tmp/grammar.js.sha && npm install && npm run build" 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "git+https://github.com/slackhq/tree-sitter-hack.git" 37 | }, 38 | "tree-sitter": [ 39 | { 40 | "scope": "source.hack", 41 | "file-types": [ 42 | "hack" 43 | ], 44 | "first-line-regex": "^((<\\?hh.*)|(#!.+ hhvm))" 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /test/cases/declarations/type-alias-type-parameters.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (alias_declaration 3 | (identifier) 4 | (type_parameters 5 | (type_parameter 6 | name: (identifier) 7 | constraint_type: (type_specifier 8 | (qualified_identifier 9 | (identifier))) 10 | constraint_type: (type_specifier 11 | (qualified_identifier 12 | (identifier))))) 13 | (type_specifier 14 | (qualified_identifier 15 | (identifier)) 16 | (type_arguments 17 | (type_specifier 18 | (qualified_identifier 19 | (identifier)))))) 20 | (comment) 21 | (alias_declaration 22 | (identifier) 23 | (type_parameters 24 | (type_parameter 25 | name: (identifier) 26 | constraint_type: (type_specifier 27 | (qualified_identifier 28 | (identifier))) 29 | constraint_type: (type_specifier 30 | (qualified_identifier 31 | (identifier))))) 32 | as: (type_specifier 33 | (qualified_identifier 34 | (identifier)) 35 | (type_arguments 36 | (type_specifier 37 | (qualified_identifier 38 | (identifier))))) 39 | (type_specifier 40 | (qualified_identifier 41 | (identifier)) 42 | (type_arguments 43 | (type_specifier 44 | (qualified_identifier 45 | (identifier))))))) 46 | -------------------------------------------------------------------------------- /test/cases/declarations/attribute-type.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (alias_declaration 3 | (attribute_modifier 4 | (qualified_identifier 5 | (identifier))) 6 | (identifier) 7 | (shape_type_specifier 8 | (nullable_modifier) 9 | (field_specifier 10 | (optional_modifier) 11 | (string) 12 | (type_specifier)))) 13 | (alias_declaration 14 | (attribute_modifier 15 | (qualified_identifier 16 | (identifier)) 17 | (arguments 18 | (argument 19 | (integer))) 20 | (qualified_identifier 21 | (identifier)) 22 | (arguments 23 | (argument 24 | (integer)) 25 | (argument 26 | (integer)))) 27 | (identifier) 28 | (function_type_specifier 29 | (type_specifier 30 | (qualified_identifier 31 | (identifier))) 32 | return_type: (type_specifier))) 33 | (alias_declaration 34 | (attribute_modifier 35 | (qualified_identifier 36 | (identifier)) 37 | (arguments 38 | (argument 39 | (integer))) 40 | (qualified_identifier 41 | (identifier)) 42 | (qualified_identifier 43 | (identifier)) 44 | (arguments 45 | (argument 46 | (integer)) 47 | (argument 48 | (integer)) 49 | (argument 50 | (integer)))) 51 | (identifier) 52 | as: (type_specifier) 53 | (type_specifier))) 54 | -------------------------------------------------------------------------------- /test/cases/statements/try-nested.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (try_statement 3 | body: (compound_statement 4 | (try_statement 5 | body: (compound_statement) 6 | (catch_clause 7 | type: (type_specifier 8 | (qualified_identifier 9 | (identifier) 10 | (identifier))) 11 | name: (variable) 12 | body: (compound_statement)) 13 | (finally_clause 14 | body: (compound_statement)))) 15 | (catch_clause 16 | type: (type_specifier 17 | (qualified_identifier 18 | (identifier) 19 | (identifier))) 20 | name: (variable) 21 | body: (compound_statement 22 | (try_statement 23 | body: (compound_statement) 24 | (catch_clause 25 | type: (type_specifier 26 | (qualified_identifier 27 | (identifier) 28 | (identifier))) 29 | name: (variable) 30 | body: (compound_statement)) 31 | (finally_clause 32 | body: (compound_statement))))) 33 | (finally_clause 34 | body: (compound_statement 35 | (try_statement 36 | body: (compound_statement) 37 | (catch_clause 38 | type: (type_specifier 39 | (qualified_identifier 40 | (identifier) 41 | (identifier))) 42 | name: (variable) 43 | body: (compound_statement)) 44 | (finally_clause 45 | body: (compound_statement))))))) 46 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-braced-subscript.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (heredoc 4 | (embedded_braced_expression 5 | (subscript_expression 6 | (variable))) 7 | (embedded_braced_expression 8 | (subscript_expression 9 | (variable) 10 | (string))) 11 | (embedded_braced_expression 12 | (selection_expression 13 | (call_expression 14 | function: (subscript_expression 15 | (variable) 16 | (string)) 17 | (arguments)) 18 | (qualified_identifier 19 | (identifier)))) 20 | (embedded_braced_expression 21 | (call_expression 22 | function: (selection_expression 23 | (subscript_expression 24 | (variable) 25 | (string)) 26 | (qualified_identifier 27 | (identifier))) 28 | (arguments))) 29 | (embedded_braced_expression 30 | (subscript_expression 31 | (subscript_expression 32 | (variable) 33 | (string)) 34 | (variable))) 35 | (embedded_braced_expression 36 | (subscript_expression 37 | (subscript_expression 38 | (variable) 39 | (call_expression 40 | function: (selection_expression 41 | (variable) 42 | (qualified_identifier 43 | (identifier))) 44 | (arguments))) 45 | (call_expression 46 | function: (variable) 47 | (arguments))))))) 48 | -------------------------------------------------------------------------------- /test/cases/declarations/use-trait.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | name: (identifier) 4 | body: (member_declarations 5 | (trait_use_clause 6 | (type_specifier 7 | (qualified_identifier 8 | (identifier)))) 9 | (trait_use_clause 10 | (type_specifier 11 | (qualified_identifier 12 | (identifier)) 13 | (type_arguments 14 | (type_specifier)))) 15 | (trait_use_clause 16 | (type_specifier 17 | (qualified_identifier 18 | (identifier))) 19 | (type_specifier 20 | (qualified_identifier 21 | (identifier))) 22 | (trait_alias_clause 23 | (identifier) 24 | (identifier))) 25 | (trait_use_clause 26 | (type_specifier 27 | (qualified_identifier 28 | (identifier))) 29 | (type_specifier 30 | (qualified_identifier 31 | (identifier)) 32 | (type_arguments 33 | (type_specifier 34 | (type_arguments 35 | (type_specifier))))) 36 | (type_specifier 37 | (qualified_identifier 38 | (identifier))) 39 | (trait_select_clause 40 | (qualified_identifier 41 | (identifier)) 42 | (identifier) 43 | (qualified_identifier 44 | (identifier))) 45 | (trait_select_clause 46 | (qualified_identifier 47 | (identifier)) 48 | (identifier) 49 | (qualified_identifier 50 | (identifier))))))) 51 | -------------------------------------------------------------------------------- /test/cases/expressions/xhp-spread.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (xhp_expression 4 | (xhp_open 5 | (xhp_identifier) 6 | (xhp_attribute 7 | (xhp_spread_expression 8 | (variable)))) 9 | (xhp_close 10 | (xhp_identifier)))) 11 | (expression_statement 12 | (xhp_expression 13 | (xhp_open 14 | (xhp_identifier) 15 | (xhp_attribute 16 | (xhp_spread_expression 17 | (variable))) 18 | (xhp_attribute 19 | (xhp_identifier) 20 | (string)) 21 | (xhp_attribute 22 | (xhp_identifier) 23 | (string))) 24 | (xhp_close 25 | (xhp_identifier)))) 26 | (expression_statement 27 | (xhp_expression 28 | (xhp_open 29 | (xhp_identifier) 30 | (xhp_attribute 31 | (xhp_identifier) 32 | (string)) 33 | (xhp_attribute 34 | (xhp_spread_expression 35 | (variable))) 36 | (xhp_attribute 37 | (xhp_identifier) 38 | (string))) 39 | (braced_expression 40 | (variable)) 41 | (xhp_close 42 | (xhp_identifier)))) 43 | (expression_statement 44 | (xhp_expression 45 | (xhp_open 46 | (xhp_identifier) 47 | (xhp_attribute 48 | (xhp_identifier) 49 | (string)) 50 | (xhp_attribute 51 | (xhp_identifier) 52 | (string)) 53 | (xhp_attribute 54 | (xhp_spread_expression 55 | (variable)))) 56 | (braced_expression 57 | (variable)) 58 | (xhp_close 59 | (xhp_identifier))))) 60 | -------------------------------------------------------------------------------- /test/cases/declarations/class-where.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | name: (identifier) 4 | (type_parameters 5 | (type_parameter 6 | name: (identifier))) 7 | (extends_clause 8 | (type_specifier 9 | (qualified_identifier 10 | (identifier)) 11 | (type_arguments 12 | (type_specifier 13 | (qualified_identifier 14 | (identifier)))))) 15 | (implements_clause 16 | (type_specifier 17 | (qualified_identifier 18 | (identifier)) 19 | (type_arguments 20 | (type_specifier 21 | (qualified_identifier 22 | (identifier)))))) 23 | (where_clause 24 | (where_constraint 25 | constraint_left_type: (type_specifier 26 | (qualified_identifier 27 | (identifier))) 28 | constraint_right_type: (type_specifier 29 | (qualified_identifier 30 | (identifier))))) 31 | body: (member_declarations 32 | (method_declaration 33 | (visibility_modifier) 34 | name: (identifier) 35 | (parameters 36 | (parameter 37 | type: (type_specifier 38 | (qualified_identifier 39 | (identifier))) 40 | name: (variable))) 41 | (where_clause 42 | (where_constraint 43 | constraint_left_type: (type_specifier 44 | (nullable_modifier) 45 | (qualified_identifier 46 | (identifier))) 47 | constraint_right_type: (type_specifier 48 | (type_arguments 49 | (type_specifier))))) 50 | body: (compound_statement))))) 51 | -------------------------------------------------------------------------------- /test/cases/declarations/property.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | (attribute_modifier 4 | (qualified_identifier 5 | (identifier))) 6 | name: (identifier) 7 | body: (member_declarations 8 | (property_declaration 9 | (static_modifier) 10 | (property_declarator 11 | name: (variable) 12 | value: (integer))) 13 | (property_declaration 14 | (visibility_modifier) 15 | (property_declarator 16 | name: (variable) 17 | value: (integer))) 18 | (property_declaration 19 | (static_modifier) 20 | (visibility_modifier) 21 | (property_declarator 22 | name: (variable) 23 | value: (integer))) 24 | (property_declaration 25 | (visibility_modifier) 26 | (static_modifier) 27 | (property_declarator 28 | name: (variable) 29 | value: (integer))) 30 | (property_declaration 31 | (visibility_modifier) 32 | (property_declarator 33 | name: (variable))) 34 | (property_declaration 35 | (visibility_modifier) 36 | (static_modifier) 37 | (property_declarator 38 | name: (variable))) 39 | (property_declaration 40 | (visibility_modifier) 41 | type: (type_specifier) 42 | (property_declarator 43 | name: (variable))) 44 | (property_declaration 45 | (attribute_modifier 46 | (qualified_identifier 47 | (identifier))) 48 | (visibility_modifier) 49 | (static_modifier) 50 | type: (type_specifier) 51 | (property_declarator 52 | name: (variable)))))) 53 | -------------------------------------------------------------------------------- /bin/generate-corpus: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source bin/require_sed 6 | 7 | # This script creates corpus *.txt files expected by `tree-sitter test` in the format 8 | # expected [0] based on *.php and *.exp files in test/cases. 9 | # 10 | # [0] https://tree-sitter.github.io/tree-sitter/creating-parsers#:~:text=Return%20statements 11 | 12 | export LC_ALL="C" # Necessary for sorting in a consistent fashion 13 | 14 | for cases_path in test/cases/*; do 15 | corpus_name=$(basename $cases_path) 16 | corpus_path="test/corpus/$corpus_name.txt" 17 | 18 | echo "Generating $corpus_path" 19 | 20 | cases="$(ls -d $cases_path/* | grep -E '^[^.]*\.exp$' | $sed -Ee 's/.exp$//' | sort -bu)" 21 | 22 | printf "" >$corpus_path 23 | first=1 24 | 25 | for case in $cases; do 26 | exp="$case.exp" 27 | 28 | if [[ -f "$case.php" ]]; then 29 | code="$case.php" 30 | elif [[ -f "$case.hack" ]]; then 31 | code="$case.hack" 32 | elif [[ -f "$case.hhi" ]]; then 33 | code="$case.hhi" 34 | else 35 | printf "Source file not found for $exp\n" 36 | exit 1 37 | fi 38 | 39 | # Use the test case file name as the description 40 | description=$(printf "$(basename $case)" | $sed -e 's/^\(.\)/\u\1/g' -e 's/-/ /g') 41 | 42 | if [[ $first -eq 0 ]]; then 43 | printf "\n" >>$corpus_path 44 | else 45 | first=0 46 | fi 47 | 48 | printf "==========================\n" >>$corpus_path 49 | printf "$description\n" >>$corpus_path 50 | printf "==========================\n\n" >>$corpus_path 51 | 52 | cat $code >>$corpus_path 53 | 54 | printf "\n---\n\n" >>$corpus_path 55 | 56 | cat $exp >>$corpus_path 57 | done 58 | done 59 | -------------------------------------------------------------------------------- /test/cases/literals/heredoc-braced-selection.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (heredoc 4 | (embedded_braced_expression 5 | (selection_expression 6 | (selection_expression 7 | (variable) 8 | (qualified_identifier 9 | (identifier))) 10 | (qualified_identifier 11 | (identifier)))) 12 | (embedded_braced_expression 13 | (selection_expression 14 | (call_expression 15 | function: (selection_expression 16 | (variable) 17 | (qualified_identifier 18 | (identifier))) 19 | (arguments)) 20 | (qualified_identifier 21 | (identifier)))) 22 | (embedded_braced_expression 23 | (call_expression 24 | function: (selection_expression 25 | (selection_expression 26 | (variable) 27 | (qualified_identifier 28 | (identifier))) 29 | (qualified_identifier 30 | (identifier))) 31 | (arguments))) 32 | (embedded_braced_expression 33 | (selection_expression 34 | (subscript_expression 35 | (selection_expression 36 | (variable) 37 | (qualified_identifier 38 | (identifier))) 39 | (string)) 40 | (qualified_identifier 41 | (identifier)))) 42 | (embedded_braced_expression 43 | (subscript_expression 44 | (selection_expression 45 | (selection_expression 46 | (variable) 47 | (qualified_identifier 48 | (identifier))) 49 | (qualified_identifier 50 | (identifier))) 51 | (string)))))) 52 | -------------------------------------------------------------------------------- /test/cases/declarations/function-where-tricky.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (function_declaration 3 | name: (identifier) 4 | (type_parameters 5 | (type_parameter 6 | name: (identifier))) 7 | (parameters) 8 | (where_clause 9 | (where_constraint 10 | constraint_left_type: (type_specifier 11 | (qualified_identifier 12 | (identifier))) 13 | constraint_right_type: (type_specifier))) 14 | body: (compound_statement)) 15 | (function_declaration 16 | name: (identifier) 17 | (type_parameters 18 | (type_parameter 19 | name: (identifier))) 20 | (parameters) 21 | (where_clause 22 | (where_constraint 23 | constraint_left_type: (type_specifier 24 | (nullable_modifier) 25 | (type_arguments 26 | (type_specifier 27 | (qualified_identifier 28 | (identifier))))) 29 | constraint_right_type: (type_specifier)) 30 | (where_constraint 31 | constraint_left_type: (type_specifier 32 | (qualified_identifier 33 | (identifier))) 34 | constraint_right_type: (type_specifier))) 35 | body: (compound_statement)) 36 | (comment) 37 | (function_declaration 38 | name: (identifier) 39 | (type_parameters 40 | (type_parameter 41 | name: (identifier))) 42 | (parameters) 43 | (where_clause 44 | (where_constraint 45 | constraint_left_type: (type_specifier 46 | (nullable_modifier) 47 | (type_arguments 48 | (type_specifier 49 | (qualified_identifier 50 | (identifier))))) 51 | constraint_right_type: (type_specifier)) 52 | (where_constraint 53 | constraint_left_type: (type_specifier 54 | (qualified_identifier 55 | (identifier))) 56 | constraint_right_type: (type_specifier))) 57 | body: (compound_statement))) 58 | -------------------------------------------------------------------------------- /test/cases/expressions/function-call-lambda.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (comment) 3 | (expression_statement 4 | (binary_expression 5 | left: (variable) 6 | right: (lambda_expression 7 | (parameters 8 | (parameter 9 | name: (variable))) 10 | body: (call_expression 11 | function: (awaitable_expression 12 | (compound_statement 13 | (return_statement 14 | (variable)))) 15 | (arguments 16 | (argument 17 | (integer)) 18 | (argument 19 | (variadic_modifier) 20 | (array 21 | (array_type) 22 | (integer) 23 | (integer) 24 | (integer)))))))) 25 | (comment) 26 | (expression_statement 27 | (binary_expression 28 | left: (variable) 29 | right: (call_expression 30 | function: (lambda_expression 31 | (async_modifier) 32 | (parameters 33 | (parameter 34 | name: (variable))) 35 | body: (compound_statement 36 | (return_statement 37 | (variable)))) 38 | (arguments 39 | (argument 40 | (integer)) 41 | (argument 42 | (variadic_modifier) 43 | (array 44 | (array_type) 45 | (integer) 46 | (integer) 47 | (integer))))))) 48 | (expression_statement 49 | (call_expression 50 | function: (parenthesized_expression 51 | (lambda_expression 52 | (parameters 53 | (parameter 54 | name: (variable))) 55 | body: (variable))) 56 | (arguments 57 | (argument 58 | (call_expression 59 | function: (qualified_identifier 60 | (identifier)) 61 | (arguments))) 62 | (argument 63 | (inout_modifier) 64 | (variable)))))) 65 | -------------------------------------------------------------------------------- /test/cases/expressions/safe-selection.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (ternary_expression 4 | condition: (binary_expression 5 | left: (selection_expression 6 | (variable) 7 | (variable)) 8 | right: (selection_expression 9 | (variable) 10 | (variable))) 11 | consequence: (selection_expression 12 | (variable) 13 | (variable)) 14 | alternative: (selection_expression 15 | (variable) 16 | (variable)))) 17 | (expression_statement 18 | (ternary_expression 19 | condition: (binary_expression 20 | left: (subscript_expression 21 | (selection_expression 22 | (subscript_expression 23 | (variable) 24 | (selection_expression 25 | (variable) 26 | (variable))) 27 | (variable)) 28 | (selection_expression 29 | (variable) 30 | (variable))) 31 | right: (subscript_expression 32 | (selection_expression 33 | (subscript_expression 34 | (variable) 35 | (selection_expression 36 | (variable) 37 | (variable))) 38 | (variable)) 39 | (selection_expression 40 | (variable) 41 | (variable)))) 42 | consequence: (subscript_expression 43 | (selection_expression 44 | (subscript_expression 45 | (variable) 46 | (selection_expression 47 | (variable) 48 | (variable))) 49 | (variable)) 50 | (selection_expression 51 | (variable) 52 | (variable))) 53 | alternative: (subscript_expression 54 | (selection_expression 55 | (subscript_expression 56 | (variable) 57 | (selection_expression 58 | (variable) 59 | (variable))) 60 | (variable)) 61 | (selection_expression 62 | (variable) 63 | (variable)))))) 64 | -------------------------------------------------------------------------------- /test/cases/declarations/class-type-parameters.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | (abstract_modifier) 4 | (final_modifier) 5 | name: (identifier) 6 | (type_parameters 7 | (type_parameter 8 | name: (identifier) 9 | constraint_type: (type_specifier 10 | (qualified_identifier 11 | (identifier)))) 12 | (type_parameter 13 | name: (identifier) 14 | constraint_type: (type_specifier 15 | (qualified_identifier 16 | (identifier)) 17 | (type_arguments 18 | (type_specifier 19 | (qualified_identifier 20 | (identifier))) 21 | (type_specifier 22 | (qualified_identifier 23 | (identifier))))))) 24 | (extends_clause 25 | (type_specifier 26 | (qualified_identifier 27 | (identifier)))) 28 | (implements_clause 29 | (type_specifier 30 | (qualified_identifier 31 | (identifier) 32 | (identifier)) 33 | (type_arguments 34 | (type_specifier 35 | (qualified_identifier 36 | (identifier))) 37 | (type_specifier 38 | (qualified_identifier 39 | (identifier))))) 40 | (type_specifier 41 | (qualified_identifier 42 | (identifier) 43 | (identifier)))) 44 | body: (member_declarations 45 | (method_declaration 46 | name: (identifier) 47 | (type_parameters 48 | (type_parameter 49 | name: (identifier) 50 | constraint_type: (type_specifier 51 | (qualified_identifier 52 | (identifier)))) 53 | (type_parameter 54 | name: (identifier) 55 | constraint_type: (type_specifier 56 | (qualified_identifier 57 | (identifier))))) 58 | (parameters) 59 | return_type: (type_specifier 60 | (qualified_identifier 61 | (identifier))) 62 | body: (compound_statement))))) 63 | -------------------------------------------------------------------------------- /test/cases/expressions/binary-with-strings.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (binary_expression 4 | left: (string) 5 | right: (binary_expression 6 | left: (binary_expression 7 | left: (string) 8 | right: (binary_expression 9 | left: (string) 10 | right: (string))) 11 | right: (binary_expression 12 | left: (binary_expression 13 | left: (string) 14 | right: (string)) 15 | right: (binary_expression 16 | left: (binary_expression 17 | left: (binary_expression 18 | left: (binary_expression 19 | left: (string) 20 | right: (string)) 21 | right: (string)) 22 | right: (string)) 23 | right: (binary_expression 24 | left: (binary_expression 25 | left: (binary_expression 26 | left: (binary_expression 27 | left: (binary_expression 28 | left: (string) 29 | right: (string)) 30 | right: (string)) 31 | right: (string)) 32 | right: (string)) 33 | right: (binary_expression 34 | left: (binary_expression 35 | left: (string) 36 | right: (string)) 37 | right: (binary_expression 38 | left: (binary_expression 39 | left: (binary_expression 40 | left: (string) 41 | right: (string)) 42 | right: (string)) 43 | right: (binary_expression 44 | left: (binary_expression 45 | left: (binary_expression 46 | left: (string) 47 | right: (string)) 48 | right: (string)) 49 | right: (binary_expression 50 | left: (string) 51 | right: (string)))))))))))) 52 | -------------------------------------------------------------------------------- /bin/test-examples: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source bin/require_fd 6 | 7 | while [[ $# -gt 0 ]]; do 8 | case $1 in 9 | --filter) 10 | filter=$2 11 | shift 12 | shift 13 | ;; 14 | --count) 15 | count=1 16 | shift 17 | ;; 18 | --name-only) 19 | name_only=1 20 | shift 21 | ;; 22 | *) 23 | break 24 | ;; 25 | esac 26 | done 27 | 28 | function filter-hack() { 29 | grep -E --color=never '.*\.(hack|php)$' | sort -u 30 | } 31 | 32 | function find-hack() { 33 | $fd '\.(hack|php)$' "$@" | sort -u 34 | } 35 | 36 | function print-results() { 37 | if [[ "$count" -eq 1 ]]; then 38 | failures="$(cat /dev/stdin | filter-hack | wc -l | tr -d ' ')" 39 | 40 | # Very important 41 | if [[ "$filures" -eq 1 ]]; then 42 | echo "$failures failure" 43 | else 44 | echo "$failures failures" 45 | fi 46 | 47 | elif [[ "$name_only" -eq 1 ]]; then 48 | filter-hack 49 | else 50 | cat /dev/stdin 51 | fi 52 | } 53 | 54 | hhvm_failures="examples/hhvm-failures.txt" 55 | 56 | hhvm_tests="examples/hhvm/hphp/hack/test" 57 | 58 | # The HHVM repo has tests that verify intentional errors. We aren't doing that (yet). 59 | # Filter out intentionally failing Hack files. 60 | if ! test -f "$hhvm_failures"; then 61 | printf "\033[1mGetting known HHVM failures...\033[0m\n" 62 | 63 | find-hack $hhvm_tests | 64 | xargs -n 256 bin/hh-errors 2>/dev/null | 65 | filter-hack >$hhvm_failures 66 | 67 | echo "$(wc -l <$hhvm_failures | tr -d ' ') known HHVM failures" 68 | fi 69 | 70 | printf "\033[1mGetting Tree-sitter examples errors...\033[0m\n" 71 | 72 | find-hack $(ls -d examples/*/ | grep -v 'examples/hhvm') | 73 | xargs -r bin/ts-errors | 74 | print-results 75 | 76 | comm -13 <(sort $hhvm_failures) <(find-hack $hhvm_tests | grep -E "$filter") | 77 | # Looks interesting, but I think too experimental to support yet? 78 | grep -v 'examples/hhvm/hphp/hack/test/pocket_universes' | 79 | grep -v 'examples/hhvm/hphp/hack/test/typecheck/goto' | 80 | xargs -r -n 256 bin/ts-errors | 81 | print-results 82 | -------------------------------------------------------------------------------- /bindings/rust/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides hack language support for the [tree-sitter][] parsing library. 2 | //! 3 | //! Typically, you will use the [language][language func] function to add this language to a 4 | //! tree-sitter [Parser][], and then use the parser to parse some code: 5 | //! 6 | //! ``` 7 | //! let code = ""; 8 | //! let mut parser = tree_sitter::Parser::new(); 9 | //! parser.set_language(tree_sitter_hack::language()).expect("Error loading hack grammar"); 10 | //! let tree = parser.parse(code, None).unwrap(); 11 | //! ``` 12 | //! 13 | //! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html 14 | //! [language func]: fn.language.html 15 | //! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html 16 | //! [tree-sitter]: https://tree-sitter.github.io/ 17 | 18 | use tree_sitter::Language; 19 | 20 | extern "C" { 21 | fn tree_sitter_hack() -> Language; 22 | } 23 | 24 | /// Get the tree-sitter [Language][] for this grammar. 25 | /// 26 | /// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html 27 | pub fn language() -> Language { 28 | unsafe { tree_sitter_hack() } 29 | } 30 | 31 | /// The content of the [`node-types.json`][] file for this grammar. 32 | /// 33 | /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types 34 | pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json"); 35 | 36 | // Uncomment these to include any queries that this grammar contains 37 | 38 | // pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm"); 39 | // pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm"); 40 | // pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm"); 41 | // pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm"); 42 | 43 | #[cfg(test)] 44 | mod tests { 45 | #[test] 46 | fn test_can_load_grammar() { 47 | let mut parser = tree_sitter::Parser::new(); 48 | parser 49 | .set_language(super::language()) 50 | .expect("Error loading hack language"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/cases/declarations/trait.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (trait_declaration 3 | (attribute_modifier 4 | (qualified_identifier 5 | (identifier)) 6 | (arguments 7 | (argument 8 | (scoped_identifier 9 | (qualified_identifier 10 | (identifier)) 11 | (identifier)))) 12 | (qualified_identifier 13 | (identifier)) 14 | (arguments 15 | (argument 16 | (integer)))) 17 | name: (identifier) 18 | (type_parameters 19 | (type_parameter 20 | name: (identifier) 21 | constraint_type: (type_specifier 22 | (qualified_identifier 23 | (identifier)))) 24 | (type_parameter 25 | name: (identifier) 26 | constraint_type: (type_specifier 27 | (qualified_identifier 28 | (identifier)) 29 | (type_arguments 30 | (type_specifier 31 | (qualified_identifier 32 | (identifier))) 33 | (type_specifier 34 | (qualified_identifier 35 | (identifier))))))) 36 | (implements_clause 37 | (type_specifier 38 | (qualified_identifier 39 | (identifier) 40 | (identifier)) 41 | (type_arguments 42 | (type_specifier 43 | (qualified_identifier 44 | (identifier))) 45 | (type_specifier 46 | (qualified_identifier 47 | (identifier))))) 48 | (type_specifier 49 | (qualified_identifier 50 | (identifier) 51 | (identifier)))) 52 | body: (member_declarations 53 | (method_declaration 54 | name: (identifier) 55 | (type_parameters 56 | (type_parameter 57 | name: (identifier) 58 | constraint_type: (type_specifier 59 | (qualified_identifier 60 | (identifier)))) 61 | (type_parameter 62 | name: (identifier) 63 | constraint_type: (type_specifier 64 | (qualified_identifier 65 | (identifier))))) 66 | (parameters) 67 | return_type: (type_specifier 68 | (qualified_identifier 69 | (identifier))) 70 | body: (compound_statement))))) 71 | -------------------------------------------------------------------------------- /test/cases/expressions/binary-combined.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (expression_statement 3 | (binary_expression 4 | left: (integer) 5 | right: (binary_expression 6 | left: (binary_expression 7 | left: (integer) 8 | right: (binary_expression 9 | left: (integer) 10 | right: (integer))) 11 | right: (binary_expression 12 | left: (binary_expression 13 | left: (integer) 14 | right: (prefix_unary_expression 15 | operand: (integer))) 16 | right: (binary_expression 17 | left: (binary_expression 18 | left: (binary_expression 19 | left: (binary_expression 20 | left: (integer) 21 | right: (integer)) 22 | right: (integer)) 23 | right: (prefix_unary_expression 24 | operand: (integer))) 25 | right: (binary_expression 26 | left: (binary_expression 27 | left: (binary_expression 28 | left: (binary_expression 29 | left: (binary_expression 30 | left: (integer) 31 | right: (integer)) 32 | right: (integer)) 33 | right: (integer)) 34 | right: (integer)) 35 | right: (binary_expression 36 | left: (binary_expression 37 | left: (integer) 38 | right: (integer)) 39 | right: (binary_expression 40 | left: (binary_expression 41 | left: (binary_expression 42 | left: (prefix_unary_expression 43 | operand: (integer)) 44 | right: (prefix_unary_expression 45 | operand: (integer))) 46 | right: (integer)) 47 | right: (binary_expression 48 | left: (binary_expression 49 | left: (binary_expression 50 | left: (integer) 51 | right: (integer)) 52 | right: (integer)) 53 | right: (binary_expression 54 | left: (integer) 55 | right: (integer)))))))))))) 56 | -------------------------------------------------------------------------------- /test/cases/declarations/interface.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (interface_declaration 3 | (attribute_modifier 4 | (qualified_identifier 5 | (identifier)) 6 | (arguments 7 | (argument 8 | (scoped_identifier 9 | (qualified_identifier 10 | (identifier)) 11 | (identifier)))) 12 | (qualified_identifier 13 | (identifier)) 14 | (arguments 15 | (argument 16 | (integer)))) 17 | name: (identifier) 18 | (type_parameters 19 | (type_parameter 20 | name: (identifier) 21 | constraint_type: (type_specifier 22 | (qualified_identifier 23 | (identifier)))) 24 | (type_parameter 25 | name: (identifier) 26 | constraint_type: (type_specifier 27 | (qualified_identifier 28 | (identifier)) 29 | (type_arguments 30 | (type_specifier 31 | (qualified_identifier 32 | (identifier))) 33 | (type_specifier 34 | (qualified_identifier 35 | (identifier))))))) 36 | (extends_clause 37 | (type_specifier 38 | (qualified_identifier 39 | (identifier))) 40 | (type_specifier 41 | (qualified_identifier 42 | (identifier) 43 | (identifier)) 44 | (type_arguments 45 | (type_specifier 46 | (qualified_identifier 47 | (identifier))) 48 | (type_specifier 49 | (qualified_identifier 50 | (identifier))))) 51 | (type_specifier 52 | (qualified_identifier 53 | (identifier) 54 | (identifier)))) 55 | body: (member_declarations 56 | (method_declaration 57 | name: (identifier) 58 | (type_parameters 59 | (type_parameter 60 | name: (identifier) 61 | constraint_type: (type_specifier 62 | (qualified_identifier 63 | (identifier)))) 64 | (type_parameter 65 | name: (identifier) 66 | constraint_type: (type_specifier 67 | (qualified_identifier 68 | (identifier))))) 69 | (parameters) 70 | return_type: (type_specifier 71 | (qualified_identifier 72 | (identifier))) 73 | body: (compound_statement))))) 74 | -------------------------------------------------------------------------------- /test/cases/declarations/class.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | (attribute_modifier 4 | (qualified_identifier 5 | (identifier)) 6 | (arguments 7 | (argument 8 | (scoped_identifier 9 | (qualified_identifier 10 | (identifier)) 11 | (identifier)))) 12 | (qualified_identifier 13 | (identifier)) 14 | (arguments 15 | (argument 16 | (integer)))) 17 | name: (identifier) 18 | (type_parameters 19 | (type_parameter 20 | name: (identifier) 21 | constraint_type: (type_specifier 22 | (qualified_identifier 23 | (identifier)))) 24 | (type_parameter 25 | name: (identifier) 26 | constraint_type: (type_specifier 27 | (qualified_identifier 28 | (identifier)) 29 | (type_arguments 30 | (type_specifier 31 | (qualified_identifier 32 | (identifier))) 33 | (type_specifier 34 | (qualified_identifier 35 | (identifier))))))) 36 | (extends_clause 37 | (type_specifier 38 | (qualified_identifier 39 | (identifier)))) 40 | (implements_clause 41 | (type_specifier 42 | (qualified_identifier 43 | (identifier) 44 | (identifier)) 45 | (type_arguments 46 | (type_specifier 47 | (qualified_identifier 48 | (identifier))) 49 | (type_specifier 50 | (qualified_identifier 51 | (identifier))))) 52 | (type_specifier 53 | (qualified_identifier 54 | (identifier) 55 | (identifier)))) 56 | body: (member_declarations 57 | (method_declaration 58 | name: (identifier) 59 | (type_parameters 60 | (type_parameter 61 | name: (identifier) 62 | constraint_type: (type_specifier 63 | (qualified_identifier 64 | (identifier)))) 65 | (type_parameter 66 | name: (identifier) 67 | constraint_type: (type_specifier 68 | (qualified_identifier 69 | (identifier))))) 70 | (parameters) 71 | return_type: (type_specifier 72 | (qualified_identifier 73 | (identifier))) 74 | body: (compound_statement))))) 75 | -------------------------------------------------------------------------------- /bin/ts-errors: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | source bin/require_ruby 6 | 7 | # Find errors in `tree-sitter parse` output and print them in a format easily consumable 8 | # by VSCode. The Hacklang parser already has an error format easily consumable by VSCode 9 | # so we match that format. 10 | # 11 | # $ bin/ts-error $@ 12 | # examples/hack-sql-fake/src/QueryContext.php 13 | # (5,1)-(5,8) ERROR 14 | # (10,3)-(10,6) MISING 15 | # examples/hack-sql-fake/src/SQLCommandProcessor.php 16 | # (2,3)-(2,4) ERROR 17 | # 18 | 19 | bin/generate-parser 20 | 21 | # Filter down to files with errors. 22 | npx tree-sitter parse --quiet $@ | 23 | cut -f1 | 24 | 25 | # Get full errors for failing files. 26 | xargs -r npx tree-sitter parse | 27 | 28 | # Format errors and print file paths first followed by errors. 29 | ruby -e " 30 | errors = [] 31 | 32 | ARGF.each_line do |line| 33 | # (ERROR [101, 0] - [101, 14] 34 | # file.hack 0 ms (MISSING \";\" [229, 19] - [229, 19]) 35 | next if line !~ /\((ERROR|MISSING)( .*)? \[(\d+), (\d+)\] - \[(\d+), (\d+)\]/ 36 | 37 | errors << [ 38 | \$1, # type 39 | \$2, # message 40 | \$3.to_i, # line 41 | \$4.to_i, # column 42 | \$5.to_i, # endLine 43 | \$6.to_i, # endColumn 44 | ] 45 | 46 | # Tree-sitter includes the file path at the end of the parser output. 47 | next if line !~ /(^.*\.(php|hack))\s/ 48 | 49 | puts \$1 50 | 51 | source = File.read(\$1).split(\"\n\") << [''] # In case of trailing newline. 52 | 53 | errors.uniq.each do |type, message, line, column, endLine, endColumn| 54 | # Use source code as the error message for now. 55 | if message.nil? 56 | if line == endLine 57 | message = source[line][column..endColumn] 58 | else 59 | message = [ 60 | source[line][column..], 61 | *source[line + 1...endLine], 62 | source[endLine][..endColumn] 63 | ].join('\n')[0..90] 64 | end 65 | else 66 | message = \"#{type} #{message&.strip}\".strip 67 | end 68 | 69 | # Increment numbers to match VSCode's 1-indexing. Use Hack error format. 70 | puts \"(#{line + 1},#{column + 1})-(#{endLine + 1},#{endColumn + 1}) #{message}\" 71 | end 72 | 73 | errors = [] 74 | end 75 | " 76 | -------------------------------------------------------------------------------- /test/cases/declarations/class-const.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (class_declaration 3 | (abstract_modifier) 4 | name: (identifier) 5 | body: (member_declarations 6 | (const_declaration 7 | (abstract_modifier) 8 | type: (type_specifier 9 | (qualified_identifier 10 | (identifier) 11 | (identifier))) 12 | (const_declarator 13 | name: (identifier))) 14 | (const_declaration 15 | type: (type_specifier 16 | (qualified_identifier 17 | (identifier) 18 | (identifier))) 19 | (const_declarator 20 | name: (identifier) 21 | value: (scoped_identifier 22 | (qualified_identifier 23 | (identifier) 24 | (identifier)) 25 | (identifier)))) 26 | (const_declaration 27 | (const_declarator 28 | name: (identifier) 29 | value: (scoped_identifier 30 | (qualified_identifier 31 | (identifier) 32 | (identifier)) 33 | (identifier)))) 34 | (const_declaration 35 | type: (type_specifier) 36 | (const_declarator 37 | name: (identifier) 38 | value: (integer))) 39 | (const_declaration 40 | type: (type_specifier) 41 | (const_declarator 42 | name: (identifier) 43 | value: (integer)) 44 | (const_declarator 45 | name: (identifier) 46 | value: (integer))) 47 | (const_declaration 48 | (const_declarator 49 | name: (identifier) 50 | value: (integer))) 51 | (const_declaration 52 | (const_declarator 53 | name: (identifier) 54 | value: (integer)) 55 | (const_declarator 56 | name: (identifier) 57 | value: (integer))) 58 | (const_declaration 59 | (abstract_modifier) 60 | type: (type_specifier) 61 | (const_declarator 62 | name: (identifier))) 63 | (const_declaration 64 | (abstract_modifier) 65 | type: (type_specifier) 66 | (const_declarator 67 | name: (identifier)) 68 | (const_declarator 69 | name: (identifier))) 70 | (const_declaration 71 | (abstract_modifier) 72 | (const_declarator 73 | name: (identifier))) 74 | (const_declaration 75 | (abstract_modifier) 76 | (const_declarator 77 | name: (identifier)) 78 | (const_declarator 79 | name: (identifier)))))) 80 | -------------------------------------------------------------------------------- /test/cases/expressions/new.exp: -------------------------------------------------------------------------------- 1 | (script 2 | (comment) 3 | (expression_statement 4 | (binary_expression 5 | left: (variable) 6 | right: (new_expression 7 | (qualified_identifier 8 | (identifier)) 9 | (arguments)))) 10 | (expression_statement 11 | (binary_expression 12 | left: (variable) 13 | right: (new_expression 14 | (qualified_identifier 15 | (identifier)) 16 | (arguments 17 | (argument 18 | (integer)))))) 19 | (expression_statement 20 | (binary_expression 21 | left: (variable) 22 | right: (new_expression 23 | (selection_expression 24 | (variable) 25 | (variable)) 26 | (arguments)))) 27 | (comment) 28 | (comment) 29 | (expression_statement 30 | (binary_expression 31 | left: (variable) 32 | right: (new_expression 33 | (scoped_identifier 34 | (qualified_identifier 35 | (identifier)) 36 | (variable)) 37 | (arguments 38 | (argument 39 | (integer)))))) 40 | (expression_statement 41 | (binary_expression 42 | left: (variable) 43 | right: (new_expression 44 | (scoped_identifier 45 | (scope_identifier) 46 | (variable)) 47 | (arguments 48 | (argument 49 | (integer)))))) 50 | (expression_statement 51 | (binary_expression 52 | left: (variable) 53 | right: (new_expression 54 | (variable) 55 | (arguments 56 | (argument 57 | (integer)))))) 58 | (expression_statement 59 | (binary_expression 60 | left: (variable) 61 | right: (new_expression 62 | (qualified_identifier 63 | (identifier)) 64 | (type_arguments 65 | (type_specifier)) 66 | (arguments 67 | (argument 68 | (integer)))))) 69 | (expression_statement 70 | (binary_expression 71 | left: (variable) 72 | right: (new_expression 73 | (parenthesized_expression 74 | (call_expression 75 | function: (qualified_identifier 76 | (identifier)) 77 | (arguments))) 78 | (arguments 79 | (argument 80 | (integer)))))) 81 | (expression_statement 82 | (binary_expression 83 | left: (variable) 84 | right: (binary_expression 85 | left: (string) 86 | right: (new_expression 87 | (pipe_variable) 88 | (arguments 89 | (argument 90 | (integer)))))))) 91 | --------------------------------------------------------------------------------