mirror of https://github.com/Wilfred/difftastic/
parent
85f2d7aa4b
commit
7291900b56
@ -1 +0,0 @@
|
||||
tree-sitter-hack/src
|
||||
@ -1,6 +0,0 @@
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 100
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
BinPackParameters: false
|
||||
BinPackArguments: false
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
/examples/* linguist-vendored
|
||||
|
||||
# Explicitly list files in src so scanner.cc and scanner.c are not included.
|
||||
/src/tree_sitter/* linguist-vendored
|
||||
/src/binding.cc linguist-vendored
|
||||
/src/grammar.json linguist-vendored
|
||||
/src/node-types.json linguist-vendored
|
||||
/src/parser.c linguist-vendored
|
||||
|
||||
/src/tree_sitter/* binary
|
||||
/src/binding.cc binary
|
||||
/src/grammar.json binary
|
||||
/src/node-types.json binary
|
||||
/src/parser.c binary
|
||||
@ -1,11 +0,0 @@
|
||||
# Code of Conduct
|
||||
|
||||
## Introduction
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
For more information on our code of conduct, please visit [https://slackhq.github.io/code-of-conduct](https://slackhq.github.io/code-of-conduct)
|
||||
@ -1,60 +0,0 @@
|
||||
# Contributors Guide
|
||||
|
||||
Interested in contributing? Awesome! Before you do though, please read our
|
||||
[Code of Conduct](https://slackhq.github.io/code-of-conduct). We take it very seriously, and expect that you will as
|
||||
well.
|
||||
|
||||
There are many ways you can contribute! :heart:
|
||||
|
||||
### Bug Reports and Fixes :bug:
|
||||
- If you find a bug, please search for it in the [Issues](https://github.com/slackhq/tree-sitter-hack/issues), and if it isn't already tracked,
|
||||
[create a new issue](https://github.com/slackhq/tree-sitter-hack/issues/new). Fill out the "Bug Report" section of the issue template. Even if an Issue is closed, feel free to comment and add details, it will still
|
||||
be reviewed.
|
||||
- Issues that have already been identified as a bug (note: able to reproduce) will be labelled `bug`.
|
||||
- If you'd like to submit a fix for a bug, [send a Pull Request](#creating_a_pull_request) and mention the Issue number.
|
||||
- Include tests that isolate the bug and verifies that it was fixed.
|
||||
|
||||
### New Features :bulb:
|
||||
- If you'd like to add new functionality to this project, describe the problem you want to solve in a [new Issue](https://github.com/slackhq/tree-sitter-hack/issues/new).
|
||||
- Issues that have been identified as a feature request will be labelled `enhancement`.
|
||||
- If you'd like to implement the new feature, please wait for feedback from the project
|
||||
maintainers before spending too much time writing the code. In some cases, `enhancement`s may
|
||||
not align well with the project objectives at the time.
|
||||
|
||||
### Tests :mag:, Documentation :books:, Miscellaneous :sparkles:
|
||||
- If you'd like to improve the tests, you want to make the documentation clearer, you have an
|
||||
alternative implementation of something that may have advantages over the way its currently
|
||||
done, or you have any other change, we would be happy to hear about it!
|
||||
- If its a trivial change, go ahead and [send a Pull Request](#creating_a_pull_request) with the changes you have in mind.
|
||||
- If not, [open an Issue](https://github.com/slackhq/tree-sitter-hack/issues/new) to discuss the idea first.
|
||||
|
||||
If you're new to our project and looking for some way to make your first contribution, look for
|
||||
Issues labelled `good first contribution`.
|
||||
|
||||
## Requirements
|
||||
|
||||
For your contribution to be accepted:
|
||||
|
||||
- [x] You must have signed the [Contributor License Agreement (CLA)](https://cla-assistant.io/slackhq/tree-sitter-hack).
|
||||
- [x] The test suite must be complete and pass.
|
||||
- [x] The changes must be approved by code review.
|
||||
- [x] Commits should be atomic and messages must be descriptive. Related issues should be mentioned by Issue number.
|
||||
|
||||
If the contribution doesn't meet the above criteria, you may fail our automated checks or a maintainer will discuss it with you. You can continue to improve a Pull Request by adding commits to the branch from which the PR was created.
|
||||
|
||||
[Interested in knowing more about about pull requests at Slack?](https://slack.engineering/on-empathy-pull-requests-979e4257d158#.awxtvmb2z)
|
||||
|
||||
## Creating a Pull Request
|
||||
|
||||
1. :fork_and_knife: Fork the repository on GitHub.
|
||||
2. :runner: Clone/fetch your fork to your local development machine. It's a good idea to run the tests just
|
||||
to make sure everything is in order.
|
||||
3. :herb: Create a new branch and check it out.
|
||||
4. :crystal_ball: Make your changes and commit them locally. Magic happens here!
|
||||
5. :arrow_heading_up: Push your new branch to your fork. (e.g. `git push username fix-issue-16`).
|
||||
6. :inbox_tray: Open a Pull Request on github.com from your new branch on your fork to `main` in this
|
||||
repository.
|
||||
|
||||
## Maintainers
|
||||
|
||||
There are more details about processes and workflow in the [Maintainer's Guide](./maintainers_guide.md).
|
||||
@ -1,36 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Describe the bug
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
### Requirements (place an `x` in each of the `[ ]`)**
|
||||
* [ ] I've read and understood the [Contributing guidelines](../CONTRIBUTING.md) and have done my best effort to follow them.
|
||||
* [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct).
|
||||
* [ ] I've searched for any related issues and avoided creating a duplicate issue.
|
||||
|
||||
### To Reproduce
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
### Expected behavior
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
#### Screenshots
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
#### Reproducible in:
|
||||
|
||||
{project_name} version:
|
||||
|
||||
{platform_name} version:
|
||||
|
||||
OS version(s):
|
||||
|
||||
#### Additional context
|
||||
Add any other context about the problem here.
|
||||
@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Code of Conduct
|
||||
url: https://slackhq.github.io/code-of-conduct
|
||||
about: Code of Conduct
|
||||
@ -1,17 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[FEATURE]"
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Description
|
||||
|
||||
Describe your request here.
|
||||
|
||||
### Requirements (place an `x` in each of the `[ ]`)
|
||||
* [ ] I've read and understood the [Contributing guidelines](../CONTRIBUTING.md) and have done my best effort to follow them.
|
||||
* [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct).
|
||||
* [ ] I've searched for any related issues and avoided creating a duplicate issue.
|
||||
@ -1,8 +0,0 @@
|
||||
### Summary
|
||||
|
||||
Describe the goal of this PR. Mention any related Issue numbers.
|
||||
|
||||
### Requirements (place an `x` in each `[ ]`)
|
||||
|
||||
* [ ] 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.
|
||||
* [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct).
|
||||
@ -1,69 +0,0 @@
|
||||
# Maintainers Guide
|
||||
|
||||
This document describes tools, tasks and workflow that one needs to be familiar with in order to effectively maintain
|
||||
this project. If you use this package within your own software as is but don't plan on modifying it, this guide is
|
||||
**not** for you.
|
||||
|
||||
## Tools (optional)
|
||||
|
||||
> Are there any build tools, dependencies, or other programs someone maintaining this project
|
||||
> needs to be familiar with?
|
||||
|
||||
## Tasks
|
||||
|
||||
### Testing
|
||||
|
||||
> How do you run the tests?
|
||||
|
||||
### Generating Documentation (optional)
|
||||
|
||||
> If the documentation is generated from source, how does someone run the generation?
|
||||
> Are the docs published on a website (GitHub Pages)?
|
||||
|
||||
### Releasing
|
||||
|
||||
> A description of the process to make a release for this project. Do not share any secrets here.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Versioning and Tags
|
||||
|
||||
> Does this project use semver? What does the numbering system look like? Are releases tagged in git?
|
||||
|
||||
### Branches
|
||||
|
||||
> Describe any specific branching workflow. For example:
|
||||
> `main` is where active development occurs.
|
||||
> Long running branches named feature branches are occasionally created for collaboration on a feature that has a large scope (because everyone cannot push commits to another person's open Pull Request)
|
||||
> At some point in the future after a major version increment, there may be maintenance branches
|
||||
> for older major versions.
|
||||
|
||||
### Issue Management
|
||||
|
||||
Labels are used to run issues through an organized workflow. Here are the basic definitions:
|
||||
|
||||
* `bug`: A confirmed bug report. A bug is considered confirmed when reproduction steps have been
|
||||
documented and the issue has been reproduced.
|
||||
* `enhancement`: A feature request for something this package might not already do.
|
||||
* `docs`: An issue that is purely about documentation work.
|
||||
* `tests`: An issue that is purely about testing work.
|
||||
* `needs feedback`: An issue that may have claimed to be a bug but was not reproducible, or was otherwise missing some information.
|
||||
* `discussion`: An issue that is purely meant to hold a discussion. Typically the maintainers are looking for feedback in this issues.
|
||||
* `question`: An issue that is like a support request because the user's usage was not correct.
|
||||
* `semver:major|minor|patch`: Metadata about how resolving this issue would affect the version number.
|
||||
* `security`: An issue that has special consideration for security reasons.
|
||||
* `good first contribution`: An issue that has a well-defined relatively-small scope, with clear expectations. It helps when the testing approach is also known.
|
||||
* `duplicate`: An issue that is functionally the same as another issue. Apply this only if you've linked the other issue by number.
|
||||
|
||||
> You may want to add more labels for subsystems of your project, depending on how complex it is.
|
||||
|
||||
**Triage** is the process of taking new issues that aren't yet "seen" and marking them with a basic
|
||||
level of information with labels. An issue should have **one** of the following labels applied:
|
||||
`bug`, `enhancement`, `question`, `needs feedback`, `docs`, `tests`, or `discussion`.
|
||||
|
||||
Issues are closed when a resolution has been reached. If for any reason a closed issue seems
|
||||
relevant once again, reopening is great and better than creating a duplicate issue.
|
||||
|
||||
## Everything else
|
||||
|
||||
When in doubt, find the other maintainers and ask.
|
||||
@ -1,28 +0,0 @@
|
||||
name: Build/test
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "**"
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
jobs:
|
||||
test_ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
- run: npm install
|
||||
- run: npm test
|
||||
test_macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
- run: brew install gnu-sed
|
||||
- run: npm install
|
||||
- run: npm test
|
||||
@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
exit 1
|
||||
fi
|
||||
@ -1,24 +0,0 @@
|
||||
name: validate
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "**"
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
jobs:
|
||||
check-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
- run: npm install
|
||||
- run: npm run build
|
||||
- name: Generate corpus
|
||||
run: "${GITHUB_WORKSPACE}/bin/generate-corpus"
|
||||
shell: bash
|
||||
- name: Check for changes
|
||||
run: "${GITHUB_WORKSPACE}/.github/workflows/validate.sh"
|
||||
shell: bash
|
||||
@ -1,12 +0,0 @@
|
||||
node_modules/
|
||||
build/
|
||||
*.log
|
||||
.DS_Store
|
||||
.vscode/settings.json
|
||||
examples
|
||||
package-lock.json
|
||||
tmp
|
||||
Cargo.lock
|
||||
/target/
|
||||
|
||||
test/cases/**/*.json
|
||||
@ -1,11 +0,0 @@
|
||||
test
|
||||
build
|
||||
bin
|
||||
examples
|
||||
tmp
|
||||
|
||||
.vscode
|
||||
.prettierignore
|
||||
.prettierrc
|
||||
.rubocop.yml
|
||||
.clang-format
|
||||
@ -1,2 +0,0 @@
|
||||
package-lock=false
|
||||
engine-strict=true
|
||||
@ -1,2 +0,0 @@
|
||||
src/
|
||||
package.json
|
||||
@ -1,27 +0,0 @@
|
||||
{
|
||||
"arrowParens": "avoid",
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"trailingComma": "all",
|
||||
"singleQuote": true,
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.json", ".prettierrc"],
|
||||
"options": {
|
||||
"parser": "json5",
|
||||
"quoteProps": "preserve",
|
||||
"singleQuote": false,
|
||||
"trailingComma": "all",
|
||||
},
|
||||
},
|
||||
{
|
||||
"files": [".vscode/tasks.json"],
|
||||
"options": {
|
||||
"parser": "json",
|
||||
"quoteProps": "preserve",
|
||||
"singleQuote": false,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -1,2 +0,0 @@
|
||||
Style/PerlBackrefs:
|
||||
Enabled: false
|
||||
@ -1,153 +0,0 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"presentation": {
|
||||
"showReuseMessage": false,
|
||||
"clear": true,
|
||||
"focus": false,
|
||||
"reveal": "silent"
|
||||
},
|
||||
"tasks": [
|
||||
{
|
||||
"label": "ts-parse",
|
||||
"type": "shell",
|
||||
"command": "F=${file}; bin/generate-parser && { npx tree-sitter parse $F>\"${F%.*}.exp\"; code -r \"${F%.*}.exp\"; }",
|
||||
"detail": "npx tree-sitter generate && tree-sitter parse ${file}",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "ts-query",
|
||||
"type": "shell",
|
||||
"command": "F=${file}; parsed=$(bin/ts-query $F) && echo \"$parsed\">\"${F%.*}.exp\" && code -r \"${F%.*}.exp\"",
|
||||
"detail": "bin/ts-query ${file} (implictly runs `npx tree-sitter generate`)",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "hh-json",
|
||||
"type": "shell",
|
||||
"command": "F=${file}; bin/hh-json $F>\"${F%.*}.json\"; code -r \"${F%.*}.json\"",
|
||||
"detail": "bin/hh-json ${file}",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "hh-errors",
|
||||
"type": "shell",
|
||||
"command": "bin/hh-errors ${file}",
|
||||
"detail": "bin/hh-errors ${file}",
|
||||
"problemMatcher": {
|
||||
"fileLocation": "absolute",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^([^\\s].*)$",
|
||||
"file": 1
|
||||
},
|
||||
{
|
||||
"regexp": "^\\((\\d+),(\\d+)\\)-\\((\\d+),(\\d+)\\) (.*)$",
|
||||
"loop": true,
|
||||
"line": 1,
|
||||
"column": 2,
|
||||
"endLine": 3,
|
||||
"endColumn": 4,
|
||||
"message": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "ts-errors",
|
||||
"type": "shell",
|
||||
"command": "bin/ts-errors ${file}",
|
||||
"detail": "bin/ts-errors ${file}",
|
||||
"problemMatcher": {
|
||||
"fileLocation": "absolute",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^([^\\s].*)$",
|
||||
"file": 1
|
||||
},
|
||||
{
|
||||
"regexp": "^\\((\\d+),(\\d+)\\)-\\((\\d+),(\\d+)\\) (.*)$",
|
||||
"loop": true,
|
||||
"line": 1,
|
||||
"column": 2,
|
||||
"endLine": 3,
|
||||
"endColumn": 4,
|
||||
"message": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"group": "test",
|
||||
"label": "npm: test",
|
||||
"detail": "bin/test-corpus && bin/test-examples",
|
||||
"problemMatcher": {
|
||||
"fileLocation": "relative",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^([^\\s].*)$",
|
||||
"file": 1
|
||||
},
|
||||
{
|
||||
"regexp": "^\\((\\d+),(\\d+)\\)-\\((\\d+),(\\d+)\\) (.*)$",
|
||||
"loop": true,
|
||||
"line": 1,
|
||||
"column": 2,
|
||||
"endLine": 3,
|
||||
"endColumn": 4,
|
||||
"message": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test-corpus",
|
||||
"label": "npm: test-corpus",
|
||||
"detail": "bin/test-corpus",
|
||||
"problemMatcher": {
|
||||
"fileLocation": "relative",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^([^\\s].*)$",
|
||||
"file": 1
|
||||
},
|
||||
{
|
||||
"regexp": "^\\((\\d+),(\\d+)\\)-\\((\\d+),(\\d+)\\) (.*)$",
|
||||
"loop": true,
|
||||
"line": 1,
|
||||
"column": 2,
|
||||
"endLine": 3,
|
||||
"endColumn": 4,
|
||||
"message": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test-examples",
|
||||
"label": "npm: test-examples",
|
||||
"detail": "bin/test-examples",
|
||||
"problemMatcher": {
|
||||
"fileLocation": "relative",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^([^\\s].*)$",
|
||||
"file": 1
|
||||
},
|
||||
{
|
||||
"regexp": "^\\((\\d+),(\\d+)\\)-\\((\\d+),(\\d+)\\) (.*)$",
|
||||
"loop": true,
|
||||
"line": 1,
|
||||
"column": 2,
|
||||
"endLine": 3,
|
||||
"endColumn": 4,
|
||||
"message": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
[package]
|
||||
name = "tree-sitter-hack"
|
||||
description = "hack grammar for the tree-sitter parsing library"
|
||||
version = "0.0.4"
|
||||
keywords = ["incremental", "parsing", "hack"]
|
||||
categories = ["parsing", "text-editors"]
|
||||
repository = "https://github.com/slackhq/tree-sitter-hack"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
|
||||
build = "bindings/rust/build.rs"
|
||||
include = [
|
||||
"bindings/rust/*",
|
||||
"grammar.js",
|
||||
"queries/*",
|
||||
"src/*",
|
||||
]
|
||||
|
||||
[lib]
|
||||
path = "bindings/rust/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
tree-sitter = "0.19.3"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Antonio de Jesus Ochoa Solano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@ -1,91 +0,0 @@
|
||||
# tree-sitter-hack
|
||||
|
||||

|
||||
|
||||
At Slack proactively securing our systems is a top priority. One way we achieve this is by automating the detection of vulnerabilities with static code analysis scanning. Although an abundance of tools exist for scanning the majority of programming languages, our codebase is overwhelmingly written in [Hack](https://hacklang.org/) - a language not widely used outside of Slack. Rather than building our own tool from scratch, we are extending the functionality of an open source static analysis tool, [Semgrep](https://github.com/returntocorp/semgrep), to be compatible with Hack. But how do we teach Semgrep the Hack programming language?
|
||||
|
||||
Like all human languages, programming languages have a structure to them known as grammar. Grammar rules are used to create a parser which converts source code into a concrete syntax tree (CST) which is a structural representation of the code. [Tree-Sitter](https://github.com/tree-sitter/tree-sitter) is a fast and robust library that can generate a CST from our Hack grammar rules. This CST has many use cases such as robust syntax highlighting, code folding, linting, etc. Most importantly, Semgrep uses this CST to understand Hack on a semantic level. This semantic understanding in conjunction with Semgrep rules can detect vulnerabilities in source code. This process is demonstrated by the following diagram.
|
||||
|
||||

|
||||
|
||||
In summary, we use tree-sitter-hack to teach Semgrep the Hack programming language.
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
$ git clone https://github.com/slackhq/tree-sitter-hack
|
||||
$ cd tree-sitter-hack
|
||||
$ npm install
|
||||
```
|
||||
## Usage
|
||||
|
||||
```
|
||||
$ echo 'function main(): void { print "wyd, world\\n"; }' > script.hack
|
||||
$ npx tree-sitter generate
|
||||
$ npx tree-sitter parse script.hack
|
||||
(script [0, 0] - [3, 0]
|
||||
(function_declaration [0, 0] - [2, 1]
|
||||
name: (identifier [0, 9] - [0, 13])
|
||||
(parameters [0, 13] - [0, 15])
|
||||
return_type: (primitive_type [0, 17] - [0, 21])
|
||||
body: (compound_statement [0, 22] - [2, 1]
|
||||
(expression_statement [1, 2] - [1, 23]
|
||||
(print_expression [1, 2] - [1, 22]
|
||||
(string [1, 8] - [1, 22]))))))
|
||||
```
|
||||
|
||||
## Testing
|
||||
```
|
||||
$ npx tree-sitter generate
|
||||
$ bin/test-corpus
|
||||
```
|
||||
|
||||
## Scripts
|
||||
|
||||
**`bin/generate-parser`**
|
||||
|
||||
Wrapper around `tree-sitter generate` that skips parser generation if `grammar.js` hasn't changed since last run.
|
||||
|
||||
**`bin/generate-corpus`**
|
||||
|
||||
Unlike most other Tree-sitter projects, we breakout test cases into separate files (see [`test/cases`](test/cases)). This is done so editors have an easier time syntax highlighting test cases. But also I find individual files easier to navigate than the `corpus.txt` files used by Tree-sitter.
|
||||
|
||||
We use `bin/generate-corpus` to generate the `test/corpus/case1.txt` from individual `test/cases` files so we can still use `tree-sitter test`.
|
||||
|
||||
**`bin/test-corpus`**
|
||||
|
||||
Run `bin/generate-corpus` and `bin/generate-parser` before running `tree-sitter test`.
|
||||
|
||||
**`bin/test-dir`**
|
||||
|
||||
Run `bin/ts-errors` on all files with `.hack` or `.php` extension in the given directory recursively.
|
||||
|
||||
```
|
||||
$ ./bin/test-dir hhvm/hphp/hack/test
|
||||
examples/hhvm/hphp/hack/test/error_formatting_highlighted/zero_width_syntax_err.php
|
||||
(3,11)-(3,18) extends
|
||||
examples/hhvm/hphp/hack/test/autocomplete/not_shape_key_string.php
|
||||
(3,1)-(6,1) function foo(): string {\n return "AUTO332\n}\n
|
||||
(4,10)-(6,1) "AUTO332\n}\n
|
||||
...
|
||||
```
|
||||
|
||||
## Note
|
||||
|
||||
npm doesn't allow packages with the word "hack" in their registry which is why the repo
|
||||
name does not match the package name.
|
||||
|
||||
> Unfortunately, the word "hack" triggers our spam detection and can't be used in package names. We recommend choosing other keywords that highlight your package's functionality.
|
||||
|
||||
## References
|
||||
|
||||
There's no published official Hacklang language spec so we have to make do.
|
||||
|
||||
- [HHVM Blog](https://hhvm.com/blog/) - Good source of language [deprecations](https://hhvm.com/blog/2019/10/01/deprecating-references.html) and [changes](https://hhvm.com/blog/2020/07/06/hhvm-4.65.html#breaking-changes).
|
||||
- [Hack Documentation](https://docs.hhvm.com/hack/) - Source at [hhvm/user-documentation](https://github.com/hhvm/user-documentation).
|
||||
- [Hack Parser Source](https://github.com/facebook/hhvm/tree/4da98da2f5ddc0989d3d150dddc1b06ee4087440/hphp/hack/src/parser)
|
||||
- [tree-sitter-javascript](https://github.com/tree-sitter/tree-sitter-javascript) and [tree-sitter-php](https://github.com/tree-sitter/tree-sitter-php) - Used to guide writing [`grammar.js`](grammar.js)
|
||||
- Deprecated [Hack Language Specification](https://github.com/facebookarchive/hack-langspec)
|
||||
- [PHP Language Specification](https://github.com/php/php-langspec) -
|
||||
Does not apply 100% to Hacklang, but still a good reference.
|
||||
- Bug icon in diagram made by Freepik
|
||||
@ -1,22 +0,0 @@
|
||||
FROM hhvm/hhvm:4.68-latest
|
||||
|
||||
ENV NODE_VERSION 14.7.0
|
||||
ENV NVM_DIR /usr/local/nvm
|
||||
|
||||
WORKDIR /tree-sitter-hack
|
||||
|
||||
RUN \
|
||||
apt-get update -y && \
|
||||
apt-get install -y build-essential ruby fd-find
|
||||
|
||||
RUN \
|
||||
mkdir -p ${NVM_DIR} && \
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash && \
|
||||
. ${NVM_DIR}/nvm.sh && \
|
||||
nvm install ${NODE_VERSION} && \
|
||||
ln -s $(which node) /usr/local/bin && \
|
||||
ln -s $(which npm) /usr/local/bin && \
|
||||
ln -s $(which npx) /usr/local/bin && \
|
||||
printf "unsafe-perm = true\n" >.npmrc
|
||||
|
||||
RUN mkdir -p /mnt/tree-sitter-hack
|
||||
@ -1,23 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
|
||||
IMAGE_NAME="tree-sitter-hack"
|
||||
CONTAINER_NAME="tree-sitter-hack"
|
||||
|
||||
docker build --rm $DIR -t $IMAGE_NAME
|
||||
|
||||
if docker ps -a --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then
|
||||
docker stop $CONTAINER_NAME >/dev/null
|
||||
docker rm $CONTAINER_NAME >/dev/null
|
||||
fi
|
||||
|
||||
docker run -td \
|
||||
--name $CONTAINER_NAME \
|
||||
-v $(realpath .):/mnt/tree-sitter-hack:rw \
|
||||
$IMAGE_NAME
|
||||
|
||||
docker exec -it $CONTAINER_NAME sh -c 'ln -s /mnt/tree-sitter-hack/* /tree-sitter-hack'
|
||||
docker exec -it $CONTAINER_NAME sh -c 'rm -rf build node_modules package-lock.json'
|
||||
docker exec -it $CONTAINER_NAME sh -c 'npm install'
|
||||
@ -1,32 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
|
||||
IMAGE_NAME="tree-sitter-hack"
|
||||
CONTAINER_NAME="tree-sitter-hack"
|
||||
|
||||
case "$1" in
|
||||
"")
|
||||
args="/bin/bash"
|
||||
;;
|
||||
|
||||
install)
|
||||
args="npm $@"
|
||||
;;
|
||||
|
||||
build | reset | test*)
|
||||
args="npm run $@"
|
||||
;;
|
||||
|
||||
node-gyp*)
|
||||
args="npx $@"
|
||||
;;
|
||||
|
||||
*)
|
||||
args="$@"
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
bash -c "docker exec -it $CONTAINER_NAME $args"
|
||||
@ -1,25 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Taken from https://git.io/JJOXZ
|
||||
|
||||
function fetch() {
|
||||
path="examples/$1"
|
||||
url=$2
|
||||
sha=$3
|
||||
|
||||
if [ ! -d "$path" ]; then
|
||||
git clone --depth 1 --branch "$sha" "https://github.com/$url" "$path"
|
||||
fi
|
||||
|
||||
pushd "$path" >/dev/null
|
||||
git fetch --depth 1 origin "$sha" && git reset --hard "$sha"
|
||||
popd >/dev/null
|
||||
}
|
||||
|
||||
mkdir -p examples
|
||||
|
||||
fetch "hack-sql-fake" "slackhq/hack-sql-fake" "v4.0.5"
|
||||
fetch "hack-json-schema" "slackhq/hack-json-schema" "v4.27.1"
|
||||
fetch "hhvm" "facebook/hhvm" "HHVM-4.67.0"
|
||||
@ -1,56 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source bin/require_sed
|
||||
|
||||
# This script creates corpus *.txt files expected by `tree-sitter test` in the format
|
||||
# expected [0] based on *.php and *.exp files in test/cases.
|
||||
#
|
||||
# [0] https://tree-sitter.github.io/tree-sitter/creating-parsers#:~:text=Return%20statements
|
||||
|
||||
for cases_path in test/cases/*; do
|
||||
corpus_name=$(basename $cases_path)
|
||||
corpus_path="test/corpus/$corpus_name.txt"
|
||||
|
||||
echo "Generating $corpus_path"
|
||||
|
||||
cases="$(ls -d $cases_path/* | grep -E '^[^.]*\.exp$' | $sed -Ee 's/.exp$//' | sort -bu)"
|
||||
|
||||
printf "" >$corpus_path
|
||||
first=1
|
||||
|
||||
for case in $cases; do
|
||||
exp="$case.exp"
|
||||
|
||||
if [[ -f "$case.php" ]]; then
|
||||
code="$case.php"
|
||||
elif [[ -f "$case.hack" ]]; then
|
||||
code="$case.hack"
|
||||
elif [[ -f "$case.hhi" ]]; then
|
||||
code="$case.hhi"
|
||||
else
|
||||
printf "Source file not found for $exp\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Use the test case file name as the description
|
||||
description=$(printf "$(basename $case)" | $sed -e 's/^\(.\)/\u\1/g' -e 's/-/ /g')
|
||||
|
||||
if [[ $first -eq 0 ]]; then
|
||||
printf "\n" >>$corpus_path
|
||||
else
|
||||
first=0
|
||||
fi
|
||||
|
||||
printf "==========================\n" >>$corpus_path
|
||||
printf "$description\n" >>$corpus_path
|
||||
printf "==========================\n\n" >>$corpus_path
|
||||
|
||||
cat $code >>$corpus_path
|
||||
|
||||
printf "\n---\n\n" >>$corpus_path
|
||||
|
||||
cat $exp >>$corpus_path
|
||||
done
|
||||
done
|
||||
@ -1,31 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--force)
|
||||
FORCE=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
mkdir -p tmp
|
||||
|
||||
CACHED_SHA=$(cat 'tmp/grammar.js.sha' 2>/dev/null || true)
|
||||
GRAMMAR_SHA=$(sha256sum 'grammar.js' | cut -d' ' -f1)
|
||||
|
||||
# Exit if grammar.js hasn't changed since last time we generated parser.
|
||||
if [ "$FORCE" != 1 ] && [ "$CACHED_SHA" = "$GRAMMAR_SHA" ]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
printf "Generating parser...\n"
|
||||
|
||||
npx tree-sitter generate
|
||||
|
||||
printf "$GRAMMAR_SHA" >'tmp/grammar.js.sha'
|
||||
@ -1,30 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Print files with hh_parse errors and filter out the rest.
|
||||
|
||||
hh_parse --show-file-name --full-fidelity-errors-all $@ |
|
||||
grep -v 'A \.php file must begin with' |
|
||||
grep -v 'Nested ternary expressions inside ternary expressions are ambiguous' |
|
||||
|
||||
# Only print file paths that have errors
|
||||
ruby -e "$(
|
||||
cat <<-RUBY
|
||||
path = nil
|
||||
|
||||
ARGF.each_line do |line|
|
||||
# Consume lines until we have something that does *not* look like a file path.
|
||||
next path = line if line =~ /(^.*\.(php|hack))\s/
|
||||
|
||||
unless path.nil?
|
||||
# Print the file path before we print any errors.
|
||||
puts path
|
||||
# Only print the file path once.
|
||||
path = nil
|
||||
end
|
||||
|
||||
puts line
|
||||
end
|
||||
RUBY
|
||||
)"
|
||||
@ -1,102 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Parse Hack source and filter noisy node objects.
|
||||
|
||||
hh_parse --full-fidelity-text-json $@ |
|
||||
jq '.parse_tree' |
|
||||
|
||||
# Remove low-value elements and simplify name tokens.
|
||||
jq "$(
|
||||
cat <<-JQ
|
||||
# Return true if an element is low value.
|
||||
def trivia:
|
||||
(. == [] or . == {}) or
|
||||
type == "object" and
|
||||
(
|
||||
.kind == "markup_section" or
|
||||
.kind == "whitespace" or
|
||||
.kind == "end_of_file" or
|
||||
.kind == "end_of_line" or
|
||||
.kind == "missing" or
|
||||
(
|
||||
.kind == "token" and
|
||||
|
||||
# Note: parenthesis matter here.
|
||||
(.token.kind | IN(";", ":", ",", "[", "]", "{", "}", "(", ")", "<<", ">>"))
|
||||
)
|
||||
);
|
||||
|
||||
# Transform/replace some objects with a simplified version.
|
||||
def transform:
|
||||
|
||||
# {
|
||||
# "kind": "token",
|
||||
# "token": {
|
||||
# "kind": "final",
|
||||
# "text": "final"
|
||||
# }
|
||||
# }
|
||||
|
||||
if type == "object" and
|
||||
.kind == "token" and
|
||||
.token.kind == .token.text
|
||||
|
||||
then .token.text
|
||||
|
||||
# {
|
||||
# "kind": "token",
|
||||
# "token": {
|
||||
# "kind": "XHP_class_name",
|
||||
# "text": ":head"
|
||||
# }
|
||||
# }
|
||||
|
||||
elif type == "object" and
|
||||
.kind == "token" and
|
||||
has("token") and
|
||||
(.token | keys) == ["kind", "text"]
|
||||
|
||||
then {(.token.kind): .token.text}
|
||||
|
||||
# {
|
||||
# "kind": "list_item",
|
||||
# "list_item": {
|
||||
# "kind": "...",
|
||||
# }
|
||||
# }
|
||||
|
||||
elif type == "object" and .kind == "list_item" and has("list_item")
|
||||
then .list_item
|
||||
|
||||
|
||||
# "...": {
|
||||
# "kind": "list",
|
||||
# "elements": [
|
||||
# {}
|
||||
# ]
|
||||
# }
|
||||
|
||||
elif type == "object" and .kind == "list" and has("elements")
|
||||
then .elements
|
||||
|
||||
else .
|
||||
end;
|
||||
|
||||
walk(
|
||||
if type == "object" then
|
||||
|
||||
# Remove keys with low-value values.
|
||||
with_entries(select(.value | trivia | not)) | map_values(transform)
|
||||
|
||||
elif type == "array" then
|
||||
|
||||
# Remove low-value array elements.
|
||||
map(select(trivia | not)) | map(transform)
|
||||
|
||||
else .
|
||||
end
|
||||
)
|
||||
JQ
|
||||
)"
|
||||
@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
[ -n "$fd" ] && exit 0
|
||||
|
||||
if [[ "$(uname)" == Darwin* ]]; then
|
||||
fd="$(command -v fd || echo "")"
|
||||
else
|
||||
fd="$(command -v fdfind || echo "")"
|
||||
fi
|
||||
|
||||
if [ -z "$fd" ]; then
|
||||
echo "fd is required for this script to work."
|
||||
exit 1
|
||||
fi
|
||||
@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if ! ruby --version | grep -qE "^ruby (2\.[7-]|[3-]\.)"; then
|
||||
echo "Ruby 2.7 or later is required for this script to work."
|
||||
exit 1
|
||||
fi
|
||||
@ -1,10 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
[ -n "$sed" ] && exit 0
|
||||
|
||||
sed="$(command -v gsed || command -v sed)"
|
||||
|
||||
if ! $sed --version 2>/dev/null | grep -q "GNU sed"; then
|
||||
echo "GNU sed is required for this script to work."
|
||||
exit 1
|
||||
fi
|
||||
@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source bin/require_fd
|
||||
|
||||
bin/generate-parser
|
||||
bin/generate-corpus
|
||||
|
||||
printf "\033[1mRunning Tree-sitter corpus tests...\033[0m\n"
|
||||
npx tree-sitter test
|
||||
|
||||
printf "\n"
|
||||
|
||||
printf "\033[1mGetting Hacklang corpus errors...\033[0m\n"
|
||||
bin/hh-errors "$($fd '\.(hack|php)$' test/cases)"
|
||||
@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source bin/require_fd
|
||||
|
||||
# Note: By default, fd ignores hidden directories,
|
||||
# hidden files, and .gitignore patterns
|
||||
# To change this behavior, use --hidden and/or --no-ignore with fd calls
|
||||
|
||||
{ fd . -e php "$@" | xargs -r egrep -l "^<\?hh" & fd . -e hack "$@"; } | xargs -r -n 256 bin/ts-errors
|
||||
@ -1,81 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source bin/require_fd
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--filter)
|
||||
filter=$2
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--count)
|
||||
count=1
|
||||
shift
|
||||
;;
|
||||
--name-only)
|
||||
name_only=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
function filter-hack() {
|
||||
grep -E --color=never '.*\.(hack|php)$' | sort -u
|
||||
}
|
||||
|
||||
function find-hack() {
|
||||
$fd '\.(hack|php)$' "$@" | sort -u
|
||||
}
|
||||
|
||||
function print-results() {
|
||||
if [[ "$count" -eq 1 ]]; then
|
||||
failures="$(cat /dev/stdin | filter-hack | wc -l | tr -d ' ')"
|
||||
|
||||
# Very important
|
||||
if [[ "$filures" -eq 1 ]]; then
|
||||
echo "$failures failure"
|
||||
else
|
||||
echo "$failures failures"
|
||||
fi
|
||||
|
||||
elif [[ "$name_only" -eq 1 ]]; then
|
||||
filter-hack
|
||||
else
|
||||
cat /dev/stdin
|
||||
fi
|
||||
}
|
||||
|
||||
hhvm_failures="examples/hhvm-failures.txt"
|
||||
|
||||
hhvm_tests="examples/hhvm/hphp/hack/test"
|
||||
|
||||
# The HHVM repo has tests that verify intentional errors. We aren't doing that (yet).
|
||||
# Filter out intentionally failing Hack files.
|
||||
if ! test -f "$hhvm_failures"; then
|
||||
printf "\033[1mGetting known HHVM failures...\033[0m\n"
|
||||
|
||||
find-hack $hhvm_tests |
|
||||
xargs -n 256 bin/hh-errors 2>/dev/null |
|
||||
filter-hack >$hhvm_failures
|
||||
|
||||
echo "$(wc -l <$hhvm_failures | tr -d ' ') known HHVM failures"
|
||||
fi
|
||||
|
||||
printf "\033[1mGetting Tree-sitter examples errors...\033[0m\n"
|
||||
|
||||
find-hack $(ls -d examples/*/ | grep -v 'examples/hhvm') |
|
||||
xargs -r bin/ts-errors |
|
||||
print-results
|
||||
|
||||
comm -13 <(sort $hhvm_failures) <(find-hack $hhvm_tests | grep -E "$filter") |
|
||||
# Looks interesting, but I think too experimental to support yet?
|
||||
grep -v 'examples/hhvm/hphp/hack/test/pocket_universes' |
|
||||
grep -v 'examples/hhvm/hphp/hack/test/typecheck/goto' |
|
||||
xargs -r -n 256 bin/ts-errors |
|
||||
print-results
|
||||
@ -1,75 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source bin/require_ruby
|
||||
|
||||
# Find errors in `tree-sitter parse` output and print them in a format easily consumable
|
||||
# by VSCode. The Hacklang parser already has an error format easily consumable by VSCode
|
||||
# so we match that format.
|
||||
#
|
||||
# $ bin/ts-error $@
|
||||
# examples/hack-sql-fake/src/QueryContext.php
|
||||
# (5,1)-(5,8) ERROR
|
||||
# (10,3)-(10,6) MISING
|
||||
# examples/hack-sql-fake/src/SQLCommandProcessor.php
|
||||
# (2,3)-(2,4) ERROR
|
||||
#
|
||||
|
||||
bin/generate-parser
|
||||
|
||||
# Filter down to files with errors.
|
||||
npx tree-sitter parse --quiet $@ |
|
||||
cut -f1 |
|
||||
|
||||
# Get full errors for failing files.
|
||||
xargs -r npx tree-sitter parse |
|
||||
|
||||
# Format errors and print file paths first followed by errors.
|
||||
ruby -e "
|
||||
errors = []
|
||||
|
||||
ARGF.each_line do |line|
|
||||
# (ERROR [101, 0] - [101, 14]
|
||||
# file.hack 0 ms (MISSING \";\" [229, 19] - [229, 19])
|
||||
next if line !~ /\((ERROR|MISSING)( .*)? \[(\d+), (\d+)\] - \[(\d+), (\d+)\]/
|
||||
|
||||
errors << [
|
||||
\$1, # type
|
||||
\$2, # message
|
||||
\$3.to_i, # line
|
||||
\$4.to_i, # column
|
||||
\$5.to_i, # endLine
|
||||
\$6.to_i, # endColumn
|
||||
]
|
||||
|
||||
# Tree-sitter includes the file path at the end of the parser output.
|
||||
next if line !~ /(^.*\.(php|hack))\s/
|
||||
|
||||
puts \$1
|
||||
|
||||
source = File.read(\$1).split(\"\n\") << [''] # In case of trailing newline.
|
||||
|
||||
errors.uniq.each do |type, message, line, column, endLine, endColumn|
|
||||
# Use source code as the error message for now.
|
||||
if message.nil?
|
||||
if line == endLine
|
||||
message = source[line][column..endColumn]
|
||||
else
|
||||
message = [
|
||||
source[line][column..],
|
||||
*source[line + 1...endLine],
|
||||
source[endLine][..endColumn]
|
||||
].join('\n')[0..90]
|
||||
end
|
||||
else
|
||||
message = \"#{type} #{message&.strip}\".strip
|
||||
end
|
||||
|
||||
# Increment numbers to match VSCode's 1-indexing. Use Hack error format.
|
||||
puts \"(#{line + 1},#{column + 1})-(#{endLine + 1},#{endColumn + 1}) #{message}\"
|
||||
end
|
||||
|
||||
errors = []
|
||||
end
|
||||
"
|
||||
@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
source bin/require_sed
|
||||
|
||||
# Make Tree-sitter output look like a query.
|
||||
#
|
||||
# $ npx tree-sitter parse $@
|
||||
# (script [0, 0] - [1, 0]
|
||||
# (while_statement [0, 0] - [0, 12]
|
||||
# condition: (parenthesized_expression [0, 6] - [0, 9]
|
||||
# (integer [0, 7] - [0, 8]))
|
||||
# body: (expression_statement [0, 10] - [0, 12]
|
||||
# (integer [0, 10] - [0, 11]))))
|
||||
#
|
||||
# $ bin/ts-query $@
|
||||
# (script
|
||||
# (while_statement
|
||||
# condition: (parenthesized_expression
|
||||
# (integer))
|
||||
# body: (expression_statement
|
||||
# (integer))))
|
||||
|
||||
bin/generate-parser 1>/dev/null
|
||||
|
||||
npx tree-sitter parse $@ | $sed -e 's/ \[.*\]//'
|
||||
@ -1,26 +0,0 @@
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "tree_sitter_hack_binding",
|
||||
"include_dirs": [
|
||||
"<!(node -e \"require('nan')\")",
|
||||
"src"
|
||||
],
|
||||
"sources": [
|
||||
"src/parser.c",
|
||||
"bindings/node/binding.cc",
|
||||
"src/scanner.cc"
|
||||
],
|
||||
"cflags_c": [
|
||||
"-std=c99",
|
||||
"-Wno-trigraphs"
|
||||
],
|
||||
"xcode_settings": {
|
||||
# Augmented assignment coalesce ??= looks like a C trigraph. Ignore trigraphs.
|
||||
"OTHER_CFLAGS": [
|
||||
"-Wno-trigraphs"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
#include "tree_sitter/parser.h"
|
||||
#include <node.h>
|
||||
#include "nan.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
extern "C" TSLanguage * tree_sitter_hack();
|
||||
|
||||
namespace {
|
||||
|
||||
NAN_METHOD(New) {}
|
||||
|
||||
void Init(Local<Object> exports, Local<Object> module) {
|
||||
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
|
||||
tpl->SetClassName(Nan::New("Language").ToLocalChecked());
|
||||
tpl->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
|
||||
Local<Function> constructor = Nan::GetFunction(tpl).ToLocalChecked();
|
||||
Local<Object> instance = constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked();
|
||||
Nan::SetInternalFieldPointer(instance, 0, tree_sitter_hack());
|
||||
|
||||
Nan::Set(instance, Nan::New("name").ToLocalChecked(), Nan::New("hack").ToLocalChecked());
|
||||
Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance);
|
||||
}
|
||||
|
||||
NODE_MODULE(tree_sitter_hack_binding, Init)
|
||||
|
||||
} // namespace
|
||||
@ -1,19 +0,0 @@
|
||||
try {
|
||||
module.exports = require("../../build/Release/tree_sitter_hack_binding");
|
||||
} catch (error1) {
|
||||
if (error1.code !== 'MODULE_NOT_FOUND') {
|
||||
throw error1;
|
||||
}
|
||||
try {
|
||||
module.exports = require("../../build/Debug/tree_sitter_hack_binding");
|
||||
} catch (error2) {
|
||||
if (error2.code !== 'MODULE_NOT_FOUND') {
|
||||
throw error2;
|
||||
}
|
||||
throw error1
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
module.exports.nodeTypeInfo = require("../../src/node-types.json");
|
||||
} catch (_) {}
|
||||
@ -1,25 +0,0 @@
|
||||
fn main() {
|
||||
let src_dir = std::path::Path::new("src");
|
||||
|
||||
let mut c_config = cc::Build::new();
|
||||
c_config.include(&src_dir);
|
||||
c_config
|
||||
.flag_if_supported("-Wno-unused-parameter")
|
||||
.flag_if_supported("-Wno-unused-but-set-variable")
|
||||
.flag_if_supported("-Wno-trigraphs");
|
||||
let parser_path = src_dir.join("parser.c");
|
||||
c_config.file(&parser_path);
|
||||
c_config.compile("parser");
|
||||
println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap());
|
||||
|
||||
let mut cpp_config = cc::Build::new();
|
||||
cpp_config.cpp(true);
|
||||
cpp_config.include(&src_dir);
|
||||
cpp_config
|
||||
.flag_if_supported("-Wno-unused-parameter")
|
||||
.flag_if_supported("-Wno-unused-but-set-variable");
|
||||
let scanner_path = src_dir.join("scanner.cc");
|
||||
cpp_config.file(&scanner_path);
|
||||
cpp_config.compile("scanner");
|
||||
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
//! This crate provides hack language support for the [tree-sitter][] parsing library.
|
||||
//!
|
||||
//! Typically, you will use the [language][language func] function to add this language to a
|
||||
//! tree-sitter [Parser][], and then use the parser to parse some code:
|
||||
//!
|
||||
//! ```
|
||||
//! let code = "";
|
||||
//! let mut parser = tree_sitter::Parser::new();
|
||||
//! parser.set_language(tree_sitter_hack::language()).expect("Error loading hack grammar");
|
||||
//! let tree = parser.parse(code, None).unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
|
||||
//! [language func]: fn.language.html
|
||||
//! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html
|
||||
//! [tree-sitter]: https://tree-sitter.github.io/
|
||||
|
||||
use tree_sitter::Language;
|
||||
|
||||
extern "C" {
|
||||
fn tree_sitter_hack() -> Language;
|
||||
}
|
||||
|
||||
/// Get the tree-sitter [Language][] for this grammar.
|
||||
///
|
||||
/// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
|
||||
pub fn language() -> Language {
|
||||
unsafe { tree_sitter_hack() }
|
||||
}
|
||||
|
||||
/// The content of the [`node-types.json`][] file for this grammar.
|
||||
///
|
||||
/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types
|
||||
pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json");
|
||||
|
||||
// Uncomment these to include any queries that this grammar contains
|
||||
|
||||
// pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm");
|
||||
// pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm");
|
||||
// pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm");
|
||||
// pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm");
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn test_can_load_grammar() {
|
||||
let mut parser = tree_sitter::Parser::new();
|
||||
parser
|
||||
.set_language(super::language())
|
||||
.expect("Error loading hack language");
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 54 KiB |
File diff suppressed because it is too large
Load Diff
@ -1,38 +0,0 @@
|
||||
{
|
||||
"name": "tree-sitter-hacklang",
|
||||
"version": "0.0.4",
|
||||
"description": "Hack grammar for tree-sitter",
|
||||
"main": "bindings/node",
|
||||
"keywords": [
|
||||
"parser",
|
||||
"lexer",
|
||||
"hacklang",
|
||||
"hhvm"
|
||||
],
|
||||
"author": "Antonio de Jesus Ochoa Solano",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/slackhq/tree-sitter-hack#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/slackhq/tree-sitter-hack/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.7.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"nan": "^2.14.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tree-sitter-cli": "^0.19.5"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "bin/generate-parser --force && node-gyp build",
|
||||
"test": "bin/generate-corpus && tree-sitter test",
|
||||
"test-corpus": "bin/test-corpus",
|
||||
"test-examples": "bin/test-examples",
|
||||
"reset": "rm -rf build node_modules package-lock.json tmp/grammar.js.sha && npm install && npm run build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/slackhq/tree-sitter-hack.git"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,370 +0,0 @@
|
||||
#include <tree_sitter/parser.h>
|
||||
|
||||
#include <cwctype>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* Debugging helper macros. Example output:
|
||||
*
|
||||
* > HEREDOC_START
|
||||
* scan_start() <-
|
||||
* next E
|
||||
* next O
|
||||
* next F
|
||||
* del EOF
|
||||
* set HEREDOC_START
|
||||
* stop \n
|
||||
* next \n
|
||||
* scan_delimiter() <-
|
||||
* scan_delimiter() -> false
|
||||
* scan_start() -> true
|
||||
*
|
||||
* > HEREDOC_START_NEWLINE HEREDOC_BODY HEREDOC_END_NEWLINE HEREDOC_END
|
||||
* scan_body() <-
|
||||
* next \n
|
||||
* stop $
|
||||
* scan_delimiter() <-
|
||||
* scan_delimiter() -> false
|
||||
* set HEREDOC_START_NEWLINE
|
||||
* scan_body() -> true
|
||||
*/
|
||||
|
||||
#define debug 0
|
||||
|
||||
#define print(...) \
|
||||
if (debug) printf(__VA_ARGS__)
|
||||
|
||||
#define peek() lexer->lookahead
|
||||
|
||||
#define next() \
|
||||
{ \
|
||||
print("next %s\n", str(peek()).c_str()); \
|
||||
lexer->advance(lexer, false); \
|
||||
}
|
||||
|
||||
#define skip() \
|
||||
{ \
|
||||
print("skip %s\n", str(peek()).c_str()); \
|
||||
lexer->advance(lexer, true); \
|
||||
}
|
||||
|
||||
#define stop() \
|
||||
{ \
|
||||
print("stop %s\n", str(peek()).c_str()); \
|
||||
lexer->mark_end(lexer); \
|
||||
}
|
||||
|
||||
#define set(symbol) \
|
||||
{ \
|
||||
print("set %s\n", TokenTypes[symbol]); \
|
||||
lexer->result_symbol = symbol; \
|
||||
}
|
||||
|
||||
#define ret(function, result) \
|
||||
print("%s() -> %s\n", function, result ? "true" : "false"); \
|
||||
return result;
|
||||
|
||||
namespace {
|
||||
|
||||
using std::string;
|
||||
|
||||
enum TokenType {
|
||||
HEREDOC_START,
|
||||
HEREDOC_START_NEWLINE,
|
||||
HEREDOC_BODY,
|
||||
HEREDOC_END_NEWLINE,
|
||||
HEREDOC_END,
|
||||
};
|
||||
|
||||
const char *TokenTypes[] = {
|
||||
"HEREDOC_START", //
|
||||
"HEREDOC_START_NEWLINE", //
|
||||
"HEREDOC_BODY", //
|
||||
"HEREDOC_END_NEWLINE", //
|
||||
"HEREDOC_END", //
|
||||
};
|
||||
|
||||
static string str(int32_t chr) {
|
||||
switch (chr) {
|
||||
case '\n':
|
||||
return "\\n";
|
||||
case '\r':
|
||||
return "\\r";
|
||||
case '\t':
|
||||
return "\\t";
|
||||
case ' ':
|
||||
return "\\s";
|
||||
case '\0':
|
||||
return "\\0";
|
||||
default:
|
||||
if (iswspace(chr)) {
|
||||
return "\\s";
|
||||
}
|
||||
|
||||
string str;
|
||||
str += chr;
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
struct Scanner {
|
||||
unsigned serialize(char *buffer) {
|
||||
if (delimiter.length() + 2 >= TREE_SITTER_SERIALIZATION_BUFFER_SIZE) return 0;
|
||||
buffer[0] = is_nowdoc;
|
||||
buffer[1] = did_start;
|
||||
buffer[2] = did_end;
|
||||
delimiter.copy(&buffer[3], delimiter.length());
|
||||
return delimiter.length() + 3;
|
||||
}
|
||||
|
||||
void deserialize(const char *buffer, unsigned length) {
|
||||
if (length == 0) {
|
||||
is_nowdoc = false;
|
||||
did_start = false;
|
||||
did_end = false;
|
||||
delimiter.clear();
|
||||
} else {
|
||||
is_nowdoc = buffer[0];
|
||||
did_start = buffer[1];
|
||||
did_end = buffer[2];
|
||||
delimiter.assign(&buffer[3], &buffer[length]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: if we return false for a scan, variable value changes are overwritten with the values of
|
||||
* the last successful scan. https://tree-sitter.github.io/tree-sitter/creating-parsers#serialize
|
||||
*/
|
||||
bool scan(TSLexer *lexer, const bool *expected) {
|
||||
print("\n> ");
|
||||
if (expected[HEREDOC_START]) print("%s ", TokenTypes[HEREDOC_START]);
|
||||
if (expected[HEREDOC_START_NEWLINE]) print("%s ", TokenTypes[HEREDOC_START_NEWLINE]);
|
||||
if (expected[HEREDOC_BODY]) print("%s ", TokenTypes[HEREDOC_BODY]);
|
||||
if (expected[HEREDOC_END_NEWLINE]) print("%s ", TokenTypes[HEREDOC_END_NEWLINE]);
|
||||
if (expected[HEREDOC_END]) print("%s ", TokenTypes[HEREDOC_END]);
|
||||
print("\n");
|
||||
|
||||
if (expected[HEREDOC_BODY] || expected[HEREDOC_END]) {
|
||||
return scan_body(lexer);
|
||||
}
|
||||
|
||||
if (expected[HEREDOC_START]) {
|
||||
return scan_start(lexer);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool scan_start(TSLexer *lexer) {
|
||||
print("scan_start() <-\n");
|
||||
|
||||
while (iswspace(peek())) skip();
|
||||
|
||||
is_nowdoc = peek() == '\'';
|
||||
delimiter.clear();
|
||||
|
||||
int32_t quote = 0;
|
||||
if (is_nowdoc || peek() == '"') {
|
||||
quote = peek();
|
||||
next();
|
||||
}
|
||||
|
||||
if (iswalpha(peek()) || peek() == '_') {
|
||||
delimiter += peek();
|
||||
next();
|
||||
|
||||
while (iswalnum(peek()) || peek() == '_') {
|
||||
delimiter += peek();
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
print("del %s\n", delimiter.c_str());
|
||||
|
||||
if (peek() == quote) {
|
||||
next();
|
||||
} else if (quote != 0) {
|
||||
// Opening quote exists, but we found no matching closing quote.
|
||||
ret("scan_start", false);
|
||||
}
|
||||
|
||||
// A valid delimiter must end with a newline with no whitespace in between.
|
||||
if (peek() != '\n' || delimiter.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
set(HEREDOC_START);
|
||||
stop();
|
||||
next();
|
||||
|
||||
if (scan_delimiter(lexer)) {
|
||||
if (peek() == ';') next();
|
||||
if (peek() == '\n') {
|
||||
// <<<EOF\n
|
||||
// EOF; ^^ able to detect did_end
|
||||
did_end = true;
|
||||
}
|
||||
}
|
||||
|
||||
ret("scan_start", true);
|
||||
}
|
||||
|
||||
bool scan_delimiter(TSLexer *lexer) {
|
||||
print("scan_delimiter() <-\n");
|
||||
for (unsigned long index = 0; index < delimiter.length(); index++) {
|
||||
if (delimiter[index] == peek()) {
|
||||
next();
|
||||
} else {
|
||||
ret("scan_delimiter", false);
|
||||
}
|
||||
}
|
||||
ret("scan_delimiter", true);
|
||||
}
|
||||
|
||||
bool scan_body(TSLexer *lexer) {
|
||||
print("scan_body() <-\n");
|
||||
|
||||
bool did_advance = false;
|
||||
|
||||
for (;;) {
|
||||
if (peek() == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (peek() == '\\') {
|
||||
next();
|
||||
next();
|
||||
did_advance = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((peek() == '{' || peek() == '$') && !is_nowdoc) {
|
||||
stop();
|
||||
if (peek() == '{') next();
|
||||
if (peek() == '$') {
|
||||
next();
|
||||
|
||||
if (is_identifier_start_char(peek())) {
|
||||
set(HEREDOC_BODY);
|
||||
ret("scan_body", did_advance);
|
||||
}
|
||||
}
|
||||
|
||||
did_advance = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (did_end || peek() == '\n') {
|
||||
if (did_advance) {
|
||||
// <<<EOF
|
||||
// x \n
|
||||
// EOF; ^^ able to detect did_end
|
||||
stop();
|
||||
next();
|
||||
} else if (peek() == '\n') {
|
||||
if (did_end) {
|
||||
// Detected did_end in a previous HEREDOC_BODY or HEREDOC_START scan. Can skip newline.
|
||||
//
|
||||
// <<<EOF\n
|
||||
// EOF; ^^ detected did_end during HEREDOC_START scan
|
||||
///
|
||||
// <<<EOF
|
||||
// x \n
|
||||
// EOF; ^^ detected did_end during HEREDOC_BODY scan
|
||||
skip();
|
||||
} else {
|
||||
// Did not detect did_end in a previous scan. Newline could be HEREDOC_START_NEWLINE,
|
||||
// HEREDOC_BODY, HEREDOC_END_NEWLINE.
|
||||
//
|
||||
// <<<EOF\n
|
||||
// x ^^ HEREDOC_START_NEWLINE
|
||||
// EOF;
|
||||
//
|
||||
// <<<EOF
|
||||
// $variable\n
|
||||
// x ^^ HEREDOC_BODY
|
||||
// EOF;
|
||||
//
|
||||
// <<<EOF
|
||||
// $variable\n
|
||||
// EOF; ^^ HEREDOC_END_NEWLINE
|
||||
next();
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
if (scan_delimiter(lexer)) {
|
||||
if (!did_advance && did_end) stop();
|
||||
|
||||
if (peek() == ';') next();
|
||||
if (peek() == '\n') {
|
||||
if (did_advance) {
|
||||
set(HEREDOC_BODY);
|
||||
did_start = true;
|
||||
did_end = true;
|
||||
} else if (did_end) {
|
||||
set(HEREDOC_END);
|
||||
delimiter.clear();
|
||||
is_nowdoc = false;
|
||||
did_start = false;
|
||||
did_end = false;
|
||||
} else {
|
||||
set(HEREDOC_END_NEWLINE);
|
||||
did_start = true;
|
||||
did_end = true;
|
||||
}
|
||||
ret("scan_body", true);
|
||||
}
|
||||
} else if (!did_start && !did_advance) {
|
||||
did_start = true;
|
||||
set(HEREDOC_START_NEWLINE);
|
||||
ret("scan_body", true);
|
||||
}
|
||||
|
||||
did_advance = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
next();
|
||||
did_advance = true;
|
||||
}
|
||||
}
|
||||
|
||||
// This function returns true if c is a valid starting character of a name/identifier
|
||||
bool is_identifier_start_char(int32_t c) {
|
||||
return (c == '_') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || (128 <= c && c <= 255);
|
||||
}
|
||||
|
||||
string delimiter;
|
||||
bool is_nowdoc;
|
||||
bool did_start;
|
||||
bool did_end;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
void *tree_sitter_hack_external_scanner_create() { return new Scanner(); }
|
||||
|
||||
bool tree_sitter_hack_external_scanner_scan(void *payload, TSLexer *lexer, const bool *expected) {
|
||||
Scanner *scanner = static_cast<Scanner *>(payload);
|
||||
return scanner->scan(lexer, expected);
|
||||
}
|
||||
|
||||
unsigned tree_sitter_hack_external_scanner_serialize(void *payload, char *state) {
|
||||
Scanner *scanner = static_cast<Scanner *>(payload);
|
||||
return scanner->serialize(state);
|
||||
}
|
||||
|
||||
void tree_sitter_hack_external_scanner_deserialize(
|
||||
void *payload, const char *state, unsigned length) {
|
||||
Scanner *scanner = static_cast<Scanner *>(payload);
|
||||
scanner->deserialize(state, length);
|
||||
}
|
||||
|
||||
void tree_sitter_hack_external_scanner_destroy(void *payload) {
|
||||
Scanner *scanner = static_cast<Scanner *>(payload);
|
||||
delete scanner;
|
||||
}
|
||||
}
|
||||
@ -1,223 +0,0 @@
|
||||
#ifndef TREE_SITTER_PARSER_H_
|
||||
#define TREE_SITTER_PARSER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ts_builtin_sym_error ((TSSymbol)-1)
|
||||
#define ts_builtin_sym_end 0
|
||||
#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
|
||||
|
||||
typedef uint16_t TSStateId;
|
||||
|
||||
#ifndef TREE_SITTER_API_H_
|
||||
typedef uint16_t TSSymbol;
|
||||
typedef uint16_t TSFieldId;
|
||||
typedef struct TSLanguage TSLanguage;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
TSFieldId field_id;
|
||||
uint8_t child_index;
|
||||
bool inherited;
|
||||
} TSFieldMapEntry;
|
||||
|
||||
typedef struct {
|
||||
uint16_t index;
|
||||
uint16_t length;
|
||||
} TSFieldMapSlice;
|
||||
|
||||
typedef struct {
|
||||
bool visible;
|
||||
bool named;
|
||||
bool supertype;
|
||||
} TSSymbolMetadata;
|
||||
|
||||
typedef struct TSLexer TSLexer;
|
||||
|
||||
struct TSLexer {
|
||||
int32_t lookahead;
|
||||
TSSymbol result_symbol;
|
||||
void (*advance)(TSLexer *, bool);
|
||||
void (*mark_end)(TSLexer *);
|
||||
uint32_t (*get_column)(TSLexer *);
|
||||
bool (*is_at_included_range_start)(const TSLexer *);
|
||||
bool (*eof)(const TSLexer *);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
TSParseActionTypeShift,
|
||||
TSParseActionTypeReduce,
|
||||
TSParseActionTypeAccept,
|
||||
TSParseActionTypeRecover,
|
||||
} TSParseActionType;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t type;
|
||||
TSStateId state;
|
||||
bool extra;
|
||||
bool repetition;
|
||||
} shift;
|
||||
struct {
|
||||
uint8_t type;
|
||||
uint8_t child_count;
|
||||
TSSymbol symbol;
|
||||
int16_t dynamic_precedence;
|
||||
uint16_t production_id;
|
||||
} reduce;
|
||||
uint8_t type;
|
||||
} TSParseAction;
|
||||
|
||||
typedef struct {
|
||||
uint16_t lex_state;
|
||||
uint16_t external_lex_state;
|
||||
} TSLexMode;
|
||||
|
||||
typedef union {
|
||||
TSParseAction action;
|
||||
struct {
|
||||
uint8_t count;
|
||||
bool reusable;
|
||||
} entry;
|
||||
} TSParseActionEntry;
|
||||
|
||||
struct TSLanguage {
|
||||
uint32_t version;
|
||||
uint32_t symbol_count;
|
||||
uint32_t alias_count;
|
||||
uint32_t token_count;
|
||||
uint32_t external_token_count;
|
||||
uint32_t state_count;
|
||||
uint32_t large_state_count;
|
||||
uint32_t production_id_count;
|
||||
uint32_t field_count;
|
||||
uint16_t max_alias_sequence_length;
|
||||
const uint16_t *parse_table;
|
||||
const uint16_t *small_parse_table;
|
||||
const uint32_t *small_parse_table_map;
|
||||
const TSParseActionEntry *parse_actions;
|
||||
const char * const *symbol_names;
|
||||
const char * const *field_names;
|
||||
const TSFieldMapSlice *field_map_slices;
|
||||
const TSFieldMapEntry *field_map_entries;
|
||||
const TSSymbolMetadata *symbol_metadata;
|
||||
const TSSymbol *public_symbol_map;
|
||||
const uint16_t *alias_map;
|
||||
const TSSymbol *alias_sequences;
|
||||
const TSLexMode *lex_modes;
|
||||
bool (*lex_fn)(TSLexer *, TSStateId);
|
||||
bool (*keyword_lex_fn)(TSLexer *, TSStateId);
|
||||
TSSymbol keyword_capture_token;
|
||||
struct {
|
||||
const bool *states;
|
||||
const TSSymbol *symbol_map;
|
||||
void *(*create)(void);
|
||||
void (*destroy)(void *);
|
||||
bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist);
|
||||
unsigned (*serialize)(void *, char *);
|
||||
void (*deserialize)(void *, const char *, unsigned);
|
||||
} external_scanner;
|
||||
};
|
||||
|
||||
/*
|
||||
* Lexer Macros
|
||||
*/
|
||||
|
||||
#define START_LEXER() \
|
||||
bool result = false; \
|
||||
bool skip = false; \
|
||||
bool eof = false; \
|
||||
int32_t lookahead; \
|
||||
goto start; \
|
||||
next_state: \
|
||||
lexer->advance(lexer, skip); \
|
||||
start: \
|
||||
skip = false; \
|
||||
lookahead = lexer->lookahead;
|
||||
|
||||
#define ADVANCE(state_value) \
|
||||
{ \
|
||||
state = state_value; \
|
||||
goto next_state; \
|
||||
}
|
||||
|
||||
#define SKIP(state_value) \
|
||||
{ \
|
||||
skip = true; \
|
||||
state = state_value; \
|
||||
goto next_state; \
|
||||
}
|
||||
|
||||
#define ACCEPT_TOKEN(symbol_value) \
|
||||
result = true; \
|
||||
lexer->result_symbol = symbol_value; \
|
||||
lexer->mark_end(lexer);
|
||||
|
||||
#define END_STATE() return result;
|
||||
|
||||
/*
|
||||
* Parse Table Macros
|
||||
*/
|
||||
|
||||
#define SMALL_STATE(id) id - LARGE_STATE_COUNT
|
||||
|
||||
#define STATE(id) id
|
||||
|
||||
#define ACTIONS(id) id
|
||||
|
||||
#define SHIFT(state_value) \
|
||||
{{ \
|
||||
.shift = { \
|
||||
.type = TSParseActionTypeShift, \
|
||||
.state = state_value \
|
||||
} \
|
||||
}}
|
||||
|
||||
#define SHIFT_REPEAT(state_value) \
|
||||
{{ \
|
||||
.shift = { \
|
||||
.type = TSParseActionTypeShift, \
|
||||
.state = state_value, \
|
||||
.repetition = true \
|
||||
} \
|
||||
}}
|
||||
|
||||
#define SHIFT_EXTRA() \
|
||||
{{ \
|
||||
.shift = { \
|
||||
.type = TSParseActionTypeShift, \
|
||||
.extra = true \
|
||||
} \
|
||||
}}
|
||||
|
||||
#define REDUCE(symbol_val, child_count_val, ...) \
|
||||
{{ \
|
||||
.reduce = { \
|
||||
.type = TSParseActionTypeReduce, \
|
||||
.symbol = symbol_val, \
|
||||
.child_count = child_count_val, \
|
||||
__VA_ARGS__ \
|
||||
}, \
|
||||
}}
|
||||
|
||||
#define RECOVER() \
|
||||
{{ \
|
||||
.type = TSParseActionTypeRecover \
|
||||
}}
|
||||
|
||||
#define ACCEPT_INPUT() \
|
||||
{{ \
|
||||
.type = TSParseActionTypeAccept \
|
||||
}}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TREE_SITTER_PARSER_H_
|
||||
@ -1,25 +0,0 @@
|
||||
(script
|
||||
(function_declaration
|
||||
(async_modifier)
|
||||
name: (identifier)
|
||||
(parameters)
|
||||
return_type: (type_specifier)
|
||||
body: (compound_statement))
|
||||
(function_declaration
|
||||
(async_modifier)
|
||||
name: (identifier)
|
||||
(type_parameters
|
||||
(type_parameter
|
||||
name: (identifier)
|
||||
constraint_type: (type_specifier)))
|
||||
(parameters)
|
||||
body: (compound_statement))
|
||||
(expression_statement
|
||||
(lambda_expression
|
||||
(async_modifier)
|
||||
(parameters
|
||||
(parameter
|
||||
name: (variable)))
|
||||
body: (binary_expression
|
||||
left: (variable)
|
||||
right: (integer)))))
|
||||
@ -1,5 +0,0 @@
|
||||
async function func0(): void {}
|
||||
|
||||
async function func1<T1 as int>() {}
|
||||
|
||||
async ($x) ==> $x + 1;
|
||||
@ -1,15 +0,0 @@
|
||||
(script
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(parameters
|
||||
(parameter
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
type: (type_specifier)
|
||||
name: (variable)))
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
return_type: (type_specifier)
|
||||
body: (compound_statement)))
|
||||
@ -1 +0,0 @@
|
||||
function func(<<__Soft>> int $int): <<__Soft>> int {}
|
||||
@ -1,22 +0,0 @@
|
||||
(script
|
||||
(class_declaration
|
||||
name: (identifier)
|
||||
(type_parameters
|
||||
(type_parameter
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
(reify_modifier)
|
||||
name: (identifier)))
|
||||
body: (member_declarations))
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(type_parameters
|
||||
(type_parameter
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
name: (identifier)))
|
||||
(parameters)
|
||||
return_type: (type_specifier)
|
||||
body: (compound_statement)))
|
||||
@ -1,3 +0,0 @@
|
||||
class C<<<Reify>> reify T> {}
|
||||
|
||||
function func<<<Reify>> T>(): void {}
|
||||
@ -1,53 +0,0 @@
|
||||
(script
|
||||
(alias_declaration
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
(identifier)
|
||||
(shape_type_specifier
|
||||
(nullable_modifier)
|
||||
(field_specifier
|
||||
(optional_modifier)
|
||||
(string)
|
||||
(type_specifier))))
|
||||
(alias_declaration
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(arguments
|
||||
(argument
|
||||
(integer)))
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(arguments
|
||||
(argument
|
||||
(integer))
|
||||
(argument
|
||||
(integer))))
|
||||
(identifier)
|
||||
(function_type_specifier
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
return_type: (type_specifier)))
|
||||
(alias_declaration
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(arguments
|
||||
(argument
|
||||
(integer)))
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(arguments
|
||||
(argument
|
||||
(integer))
|
||||
(argument
|
||||
(integer))
|
||||
(argument
|
||||
(integer))))
|
||||
(identifier)
|
||||
as: (type_specifier)
|
||||
(type_specifier)))
|
||||
@ -1,10 +0,0 @@
|
||||
<<A1>>
|
||||
newtype T1 = ?shape(
|
||||
?'int' => int
|
||||
);
|
||||
|
||||
<<A3(1), A2(2,3,)>>
|
||||
type T2 = (function(T1): string);
|
||||
|
||||
<<A4(1), A5, A6(1,3,4)>>
|
||||
newtype T3 as int = int;
|
||||
@ -1,41 +0,0 @@
|
||||
(script
|
||||
(class_declaration
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(arguments
|
||||
(argument
|
||||
(integer))
|
||||
(argument
|
||||
(integer))))
|
||||
name: (identifier)
|
||||
body: (member_declarations
|
||||
(method_declaration
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(arguments
|
||||
(argument
|
||||
(integer))
|
||||
(argument
|
||||
(integer))))
|
||||
name: (identifier)
|
||||
(parameters)
|
||||
body: (compound_statement))))
|
||||
(function_declaration
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(arguments
|
||||
(argument
|
||||
(qualified_identifier
|
||||
(identifier))))
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
name: (identifier)
|
||||
(parameters)
|
||||
body: (compound_statement)))
|
||||
@ -1,9 +0,0 @@
|
||||
<<Attribute, Attribute(1, 2),>>
|
||||
class C {
|
||||
<<Attribute, Attribute(1, 2,)>>
|
||||
function method() {}
|
||||
}
|
||||
|
||||
<<Attribute(C,), Attribute>>
|
||||
function func() {
|
||||
}
|
||||
@ -1,79 +0,0 @@
|
||||
(script
|
||||
(class_declaration
|
||||
(abstract_modifier)
|
||||
name: (identifier)
|
||||
body: (member_declarations
|
||||
(const_declaration
|
||||
(abstract_modifier)
|
||||
type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)
|
||||
(identifier)))
|
||||
(const_declarator
|
||||
name: (identifier)))
|
||||
(const_declaration
|
||||
type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)
|
||||
(identifier)))
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (scoped_identifier
|
||||
(qualified_identifier
|
||||
(identifier)
|
||||
(identifier))
|
||||
(identifier))))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (scoped_identifier
|
||||
(qualified_identifier
|
||||
(identifier)
|
||||
(identifier))
|
||||
(identifier))))
|
||||
(const_declaration
|
||||
type: (type_specifier)
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
type: (type_specifier)
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer))
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer))
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(abstract_modifier)
|
||||
type: (type_specifier)
|
||||
(const_declarator
|
||||
name: (identifier)))
|
||||
(const_declaration
|
||||
(abstract_modifier)
|
||||
type: (type_specifier)
|
||||
(const_declarator
|
||||
name: (identifier))
|
||||
(const_declarator
|
||||
name: (identifier)))
|
||||
(const_declaration
|
||||
(abstract_modifier)
|
||||
(const_declarator
|
||||
name: (identifier)))
|
||||
(const_declaration
|
||||
(abstract_modifier)
|
||||
(const_declarator
|
||||
name: (identifier))
|
||||
(const_declarator
|
||||
name: (identifier))))))
|
||||
@ -1,15 +0,0 @@
|
||||
abstract class C {
|
||||
abstract const A\B C01;
|
||||
const A\B C02 = A\B::C0;
|
||||
const C03 = A\B::C0;
|
||||
|
||||
const int C1 = 1;
|
||||
const int C2 = 1, C3 = 1;
|
||||
const C4 = 1;
|
||||
const C5 = 1, C6 = 1;
|
||||
|
||||
abstract const int C7;
|
||||
abstract const int C8, C9;
|
||||
abstract const CA;
|
||||
abstract const CB, CC;
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
(script
|
||||
(class_declaration
|
||||
name: (identifier)
|
||||
body: (member_declarations
|
||||
(method_declaration
|
||||
(visibility_modifier)
|
||||
name: (identifier)
|
||||
(parameters
|
||||
(parameter
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
(visibility_modifier)
|
||||
type: (type_specifier)
|
||||
name: (variable)
|
||||
default_value: (integer))
|
||||
(parameter
|
||||
type: (type_specifier)
|
||||
(variadic_modifier)
|
||||
name: (variable)))
|
||||
body: (compound_statement)))))
|
||||
@ -1,3 +0,0 @@
|
||||
class C {
|
||||
public function __construct(<<__Soft>> private int $prop = 1, string ...$name) {}
|
||||
}
|
||||
@ -1,62 +0,0 @@
|
||||
(script
|
||||
(class_declaration
|
||||
(abstract_modifier)
|
||||
(final_modifier)
|
||||
name: (identifier)
|
||||
(type_parameters
|
||||
(type_parameter
|
||||
name: (identifier)
|
||||
constraint_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier))))
|
||||
(type_parameter
|
||||
name: (identifier)
|
||||
constraint_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(type_arguments
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))))))
|
||||
(extends_clause
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier))))
|
||||
(implements_clause
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)
|
||||
(identifier))
|
||||
(type_arguments
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))))
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)
|
||||
(identifier))))
|
||||
body: (member_declarations
|
||||
(method_declaration
|
||||
name: (identifier)
|
||||
(type_parameters
|
||||
(type_parameter
|
||||
name: (identifier)
|
||||
constraint_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier))))
|
||||
(type_parameter
|
||||
name: (identifier)
|
||||
constraint_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))))
|
||||
(parameters)
|
||||
return_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
body: (compound_statement)))))
|
||||
@ -1,3 +0,0 @@
|
||||
abstract final class F<Ta as A, Tb super B<A, C>> extends B implements A\B<A, C>, C\D {
|
||||
function method<Ta as A, Tb super B>(): Tc {}
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
(script
|
||||
(class_declaration
|
||||
name: (identifier)
|
||||
(type_parameters
|
||||
(type_parameter
|
||||
name: (identifier)))
|
||||
(extends_clause
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(type_arguments
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier))))))
|
||||
(implements_clause
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(type_arguments
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier))))))
|
||||
(where_clause
|
||||
(where_constraint
|
||||
constraint_left_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
constraint_right_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))))
|
||||
body: (member_declarations
|
||||
(method_declaration
|
||||
(visibility_modifier)
|
||||
name: (identifier)
|
||||
(parameters
|
||||
(parameter
|
||||
type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
name: (variable)))
|
||||
(where_clause
|
||||
(where_constraint
|
||||
constraint_left_type: (type_specifier
|
||||
(nullable_modifier)
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
constraint_right_type: (type_specifier
|
||||
(type_arguments
|
||||
(type_specifier)))))
|
||||
body: (compound_statement)))))
|
||||
@ -1,3 +0,0 @@
|
||||
class C <T1> extends B<T2> implements A<T3> where T2 = T3 {
|
||||
private function __construct(T1 $param) where ?T1 super vec<int>, {}
|
||||
}
|
||||
@ -1,74 +0,0 @@
|
||||
(script
|
||||
(class_declaration
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(arguments
|
||||
(argument
|
||||
(scoped_identifier
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(identifier))))
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(arguments
|
||||
(argument
|
||||
(integer))))
|
||||
name: (identifier)
|
||||
(type_parameters
|
||||
(type_parameter
|
||||
name: (identifier)
|
||||
constraint_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier))))
|
||||
(type_parameter
|
||||
name: (identifier)
|
||||
constraint_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(type_arguments
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))))))
|
||||
(extends_clause
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier))))
|
||||
(implements_clause
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)
|
||||
(identifier))
|
||||
(type_arguments
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))))
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)
|
||||
(identifier))))
|
||||
body: (member_declarations
|
||||
(method_declaration
|
||||
name: (identifier)
|
||||
(type_parameters
|
||||
(type_parameter
|
||||
name: (identifier)
|
||||
constraint_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier))))
|
||||
(type_parameter
|
||||
name: (identifier)
|
||||
constraint_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))))
|
||||
(parameters)
|
||||
return_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
body: (compound_statement)))))
|
||||
@ -1,4 +0,0 @@
|
||||
<<Attribute(R::class), Attribute(1,),>>
|
||||
class F<Ta as A, Tb super B<A, C>> extends B implements A\B<A, C>, C\D {
|
||||
function method<Ta as A, Tb super B>(): Tc {}
|
||||
}
|
||||
@ -1,92 +0,0 @@
|
||||
(script
|
||||
(comment)
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(comment)
|
||||
(const_declaration
|
||||
type: (type_specifier)
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer))))
|
||||
@ -1,29 +0,0 @@
|
||||
// Kinda wish this wasn't allowed. Seems like asking for trouble.
|
||||
|
||||
const type = 1;
|
||||
const newtype = 1;
|
||||
|
||||
// 🤦
|
||||
const int int = 1;
|
||||
|
||||
const bool = 1;
|
||||
const float = 1;
|
||||
const int = 1;
|
||||
const string = 1;
|
||||
const arraykey = 1;
|
||||
const void = 1;
|
||||
const nonnull = 1;
|
||||
const null = 1;
|
||||
const mixed = 1;
|
||||
const dynamic = 1;
|
||||
const noreturn = 1;
|
||||
|
||||
const array = 1;
|
||||
const varray = 1;
|
||||
const darray = 1;
|
||||
const vec = 1;
|
||||
const dict = 1;
|
||||
const keyset = 1;
|
||||
|
||||
const tuple = 1;
|
||||
const shape = 1;
|
||||
@ -1,25 +0,0 @@
|
||||
(script
|
||||
(const_declaration
|
||||
type: (type_specifier)
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
type: (type_specifier)
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer))
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer)))
|
||||
(const_declaration
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer))
|
||||
(const_declarator
|
||||
name: (identifier)
|
||||
value: (integer))))
|
||||
@ -1,4 +0,0 @@
|
||||
const int C1 = 1;
|
||||
const int C2 = 1, C3 = 1;
|
||||
const C4 = 1;
|
||||
const C5 = 1, C6 = 1;
|
||||
@ -1,25 +0,0 @@
|
||||
(script
|
||||
(namespace_declaration
|
||||
body: (compound_statement
|
||||
(function_declaration
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
name: (identifier)
|
||||
(parameters
|
||||
(parameter
|
||||
name: (variable)))
|
||||
return_type: (type_specifier))
|
||||
(function_declaration
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
name: (identifier)
|
||||
(parameters
|
||||
(parameter
|
||||
name: (variable)))
|
||||
return_type: (type_specifier)))))
|
||||
@ -1,6 +0,0 @@
|
||||
namespace {
|
||||
<<__PHPStdLib, __Pure>>
|
||||
function is_bool($var): bool;
|
||||
<<__PHPStdLib, __Pure>>
|
||||
function is_int($var): bool;
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
(script
|
||||
(enum_declaration
|
||||
name: (identifier)
|
||||
type: (type_specifier)
|
||||
as: (type_specifier)
|
||||
(enumerator
|
||||
(identifier)
|
||||
(integer))
|
||||
(enumerator
|
||||
(identifier)
|
||||
(integer))))
|
||||
@ -1,4 +0,0 @@
|
||||
enum Enum: int as int {
|
||||
F1 = 1;
|
||||
F2 = 8;
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
(script
|
||||
(enum_declaration
|
||||
name: (identifier)
|
||||
type: (type_specifier))
|
||||
(enum_declaration
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
name: (identifier)
|
||||
type: (type_specifier)
|
||||
(enumerator
|
||||
(identifier)
|
||||
(integer))
|
||||
(enumerator
|
||||
(identifier)
|
||||
(integer))
|
||||
(enumerator
|
||||
(identifier)
|
||||
(scoped_identifier
|
||||
(qualified_identifier
|
||||
(identifier))
|
||||
(identifier)))
|
||||
(enumerator
|
||||
(identifier)
|
||||
(binary_expression
|
||||
left: (string)
|
||||
right: (string)))))
|
||||
@ -1,9 +0,0 @@
|
||||
enum Enum: int {}
|
||||
|
||||
<<Beenum>>
|
||||
enum Enum : int {
|
||||
F1 = 1;
|
||||
F2 = 8;
|
||||
F3 = C::CONST;
|
||||
F4 = 'a'.'b';
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
(script
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(parameters
|
||||
(parameter
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
(inout_modifier)
|
||||
type: (type_specifier)
|
||||
name: (variable))
|
||||
(parameter
|
||||
(inout_modifier)
|
||||
type: (type_specifier)
|
||||
name: (variable)))
|
||||
body: (compound_statement)))
|
||||
@ -1 +0,0 @@
|
||||
function func(<<__Soft>> inout int $arg1, inout int $arg2) {}
|
||||
@ -1,12 +0,0 @@
|
||||
(script
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(parameters
|
||||
(parameter
|
||||
(attribute_modifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
type: (type_specifier)
|
||||
(variadic_modifier)
|
||||
name: (variable)))
|
||||
body: (compound_statement)))
|
||||
@ -1 +0,0 @@
|
||||
function func(<<__Soft>> int ...$arg1) {}
|
||||
@ -1,15 +0,0 @@
|
||||
(script
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(type_parameters
|
||||
(type_parameter
|
||||
name: (identifier))
|
||||
(type_parameter
|
||||
name: (identifier)
|
||||
constraint_type: (type_specifier))
|
||||
(type_parameter
|
||||
name: (identifier)
|
||||
constraint_type: (type_specifier)))
|
||||
(parameters)
|
||||
return_type: (type_specifier)
|
||||
body: (compound_statement)))
|
||||
@ -1 +0,0 @@
|
||||
function func<Ta, Tb as int, Td super int>(): void {}
|
||||
@ -1,21 +0,0 @@
|
||||
(script
|
||||
(alias_declaration
|
||||
(identifier)
|
||||
(function_type_specifier
|
||||
(type_specifier)
|
||||
(type_specifier
|
||||
(nullable_modifier))
|
||||
return_type: (type_specifier)))
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(parameters
|
||||
(parameter
|
||||
type: (function_type_specifier
|
||||
(inout_modifier)
|
||||
(type_specifier)
|
||||
return_type: (type_specifier))
|
||||
name: (variable)))
|
||||
return_type: (function_type_specifier
|
||||
(type_specifier)
|
||||
return_type: (type_specifier))
|
||||
body: (compound_statement)))
|
||||
@ -1,3 +0,0 @@
|
||||
type func = (function(int, ?string,): string);
|
||||
|
||||
function func((function(inout int): string) $func): (function(int): string) {}
|
||||
@ -1,57 +0,0 @@
|
||||
(script
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(type_parameters
|
||||
(type_parameter
|
||||
name: (identifier)))
|
||||
(parameters)
|
||||
(where_clause
|
||||
(where_constraint
|
||||
constraint_left_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
constraint_right_type: (type_specifier)))
|
||||
body: (compound_statement))
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(type_parameters
|
||||
(type_parameter
|
||||
name: (identifier)))
|
||||
(parameters)
|
||||
(where_clause
|
||||
(where_constraint
|
||||
constraint_left_type: (type_specifier
|
||||
(nullable_modifier)
|
||||
(type_arguments
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))))
|
||||
constraint_right_type: (type_specifier))
|
||||
(where_constraint
|
||||
constraint_left_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
constraint_right_type: (type_specifier)))
|
||||
body: (compound_statement))
|
||||
(comment)
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(type_parameters
|
||||
(type_parameter
|
||||
name: (identifier)))
|
||||
(parameters)
|
||||
(where_clause
|
||||
(where_constraint
|
||||
constraint_left_type: (type_specifier
|
||||
(nullable_modifier)
|
||||
(type_arguments
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))))
|
||||
constraint_right_type: (type_specifier))
|
||||
(where_constraint
|
||||
constraint_left_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
constraint_right_type: (type_specifier)))
|
||||
body: (compound_statement)))
|
||||
@ -1,6 +0,0 @@
|
||||
function func1<T>() where T = int, {}
|
||||
|
||||
function func2<T>() where ?vec<T> = int, T super int {}
|
||||
|
||||
# Optional comma. Why tho?
|
||||
function func3<T>() where ?vec<T> = int T super int {}
|
||||
@ -1,35 +0,0 @@
|
||||
(script
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(type_parameters
|
||||
(type_parameter
|
||||
name: (identifier)))
|
||||
(parameters)
|
||||
return_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
(where_clause
|
||||
(where_constraint
|
||||
constraint_left_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
constraint_right_type: (type_specifier)))
|
||||
body: (compound_statement))
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(type_parameters
|
||||
(type_parameter
|
||||
name: (identifier)))
|
||||
(parameters)
|
||||
return_type: (type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))
|
||||
(where_clause
|
||||
(where_constraint
|
||||
constraint_left_type: (type_specifier
|
||||
(type_arguments
|
||||
(type_specifier
|
||||
(qualified_identifier
|
||||
(identifier)))))
|
||||
constraint_right_type: (type_specifier)))
|
||||
body: (compound_statement)))
|
||||
@ -1,3 +0,0 @@
|
||||
function func1<T>(): T where T as int {}
|
||||
|
||||
function func2<T>(): T where vec<T> = int {}
|
||||
@ -1,43 +0,0 @@
|
||||
(script
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(parameters)
|
||||
return_type: (type_specifier)
|
||||
body: (compound_statement))
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(parameters
|
||||
(variadic_modifier))
|
||||
body: (compound_statement))
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(parameters
|
||||
(parameter
|
||||
name: (variable)))
|
||||
body: (compound_statement))
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(parameters
|
||||
(parameter
|
||||
type: (type_specifier)
|
||||
name: (variable)))
|
||||
body: (compound_statement))
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(parameters
|
||||
(parameter
|
||||
type: (type_specifier)
|
||||
name: (variable)))
|
||||
return_type: (type_specifier)
|
||||
body: (compound_statement))
|
||||
(function_declaration
|
||||
name: (identifier)
|
||||
(parameters
|
||||
(parameter
|
||||
type: (type_specifier)
|
||||
name: (variable))
|
||||
(parameter
|
||||
type: (type_specifier)
|
||||
name: (variable)))
|
||||
return_type: (type_specifier)
|
||||
body: (compound_statement)))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue