mirror of https://github.com/Wilfred/difftastic/
Use tree-sitter-bash from crates.io
parent
7e8974e295
commit
f9d663ea9f
@ -1 +0,0 @@
|
||||
../tree-sitter-bash/queries/highlights.scm
|
||||
@ -1 +0,0 @@
|
||||
tree-sitter-bash/src
|
||||
@ -1,20 +0,0 @@
|
||||
module.exports = {
|
||||
'env': {
|
||||
'commonjs': true,
|
||||
'es2021': true,
|
||||
},
|
||||
'extends': 'google',
|
||||
'overrides': [
|
||||
],
|
||||
'parserOptions': {
|
||||
'ecmaVersion': 'latest',
|
||||
'sourceType': 'module',
|
||||
},
|
||||
'rules': {
|
||||
'indent': ['error', 2, {'SwitchCase': 1}],
|
||||
'max-len': [
|
||||
'error',
|
||||
{'code': 120, 'ignoreComments': true, 'ignoreUrls': true, 'ignoreStrings': true},
|
||||
],
|
||||
},
|
||||
};
|
||||
@ -1,6 +0,0 @@
|
||||
/examples/* linguist-vendored
|
||||
|
||||
+src/tree_sitter/* linguist-generated
|
||||
src/grammar.json linguist-generated
|
||||
src/node-types.json linguist-generated
|
||||
src/parser.c linguist-generated
|
||||
@ -1,37 +0,0 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm install
|
||||
- run: npm test
|
||||
test_windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm install
|
||||
- run: npm run-script test-windows
|
||||
@ -1,22 +0,0 @@
|
||||
name: Fuzz Parser
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- src/scanner.c
|
||||
pull_request:
|
||||
paths:
|
||||
- src/scanner.c
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Parser fuzzing
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: vigoux/tree-sitter-fuzz-action@v1
|
||||
with:
|
||||
language: bash
|
||||
external-scanner: src/scanner.c
|
||||
time: 60
|
||||
@ -1,19 +0,0 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- "**"
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install modules
|
||||
run: npm install
|
||||
- name: Run ESLint
|
||||
run: npm run lint
|
||||
@ -1,93 +0,0 @@
|
||||
name: Pack
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- released
|
||||
|
||||
env:
|
||||
NODE_PREBUILD_CMD: npx prebuild -t 10.0.0 -t 12.0.0 -t 14.0.0 -t 16.0.0 -t 18.0.0 -t 20.0.0 --strip -u ${{ secrets.GH_TOKEN }}
|
||||
ELECTRON_PREBUILD_CMD: npx prebuild -r electron -t 3.0.0 -t 4.0.0 -t 5.0.0 -t 6.0.0 -t 7.0.0 -t 8.0.0 -t 9.0.0 -t 10.0.0 -t 11.0.0 -t 12.0.0 -t 13.0.0 -t 14.0.0 -t 15.0.0 -t 16.0.0 -t 17.0.0 -t 18.0.0 -t 19.0.0 -t 20.0.0 -t 21.0.0 -t 22.0.0 -t 23.0.0 -t 24.0.0 -t 25.0.0 --strip -u ${{ secrets.GH_TOKEN }}
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- macos-latest
|
||||
- ubuntu-latest
|
||||
node:
|
||||
- 10
|
||||
- 12
|
||||
- 14
|
||||
- 16
|
||||
- 18
|
||||
- 20
|
||||
fail-fast: false
|
||||
name: Testing Node ${{ matrix.node }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
- run: npm install
|
||||
- run: npm test
|
||||
|
||||
test-windows:
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- windows-2019
|
||||
node:
|
||||
- 10
|
||||
- 12
|
||||
- 14
|
||||
- 16
|
||||
- 18
|
||||
- 20
|
||||
fail-fast: false
|
||||
name: Testing Node ${{ matrix.node }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
- run: npm install
|
||||
- run: npm run test-windows
|
||||
|
||||
prebuild:
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- windows-2019
|
||||
- macos-latest
|
||||
- ubuntu-latest
|
||||
fail-fast: false
|
||||
name: Prebuild for ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [test, test-windows]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
- run: npm install
|
||||
- if: runner.os == 'macOS'
|
||||
run: ${{ env.NODE_PREBUILD_CMD }} --arch arm64
|
||||
- if: runner.os == 'macOS'
|
||||
run: ${{ env.ELECTRON_PREBUILD_CMD }} --arch arm64
|
||||
- run: ${{ env.NODE_PREBUILD_CMD }}
|
||||
- run: ${{ env.ELECTRON_PREBUILD_CMD }}
|
||||
@ -1,103 +0,0 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["CI"]
|
||||
branches:
|
||||
- master
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get previous commit SHA
|
||||
id: get_previous_commit
|
||||
run: |
|
||||
LATEST_TAG=$(git describe --tags --abbrev=0)
|
||||
if [[ -z "$LATEST_TAG" ]]; then
|
||||
echo "No tag found. Failing..."
|
||||
exit 1
|
||||
fi
|
||||
echo "latest_tag=${LATEST_TAG#v}" >> "$GITHUB_ENV" # Remove 'v' prefix from the tag
|
||||
|
||||
- name: Check if version changed and is greater than the previous
|
||||
id: version_check
|
||||
run: |
|
||||
# Compare the current version with the version from the previous commit
|
||||
PREVIOUS_NPM_VERSION=${{ env.latest_tag }}
|
||||
CURRENT_NPM_VERSION=$(jq -r '.version' package.json)
|
||||
CURRENT_CARGO_VERSION=$(awk -F '"' '/^version/ {print $2}' Cargo.toml)
|
||||
if [[ "$CURRENT_NPM_VERSION" != "$CURRENT_CARGO_VERSION" ]]; then # Cargo.toml and package.json versions must match
|
||||
echo "Mismatch: NPM version ($CURRENT_NPM_VERSION) and Cargo.toml version ($CURRENT_CARGO_VERSION)"
|
||||
echo "version_changed=false" >> "$GITHUB_ENV"
|
||||
else
|
||||
if [[ "$PREVIOUS_NPM_VERSION" == "$CURRENT_NPM_VERSION" ]]; then
|
||||
echo "version_changed=" >> "$GITHUB_ENV"
|
||||
else
|
||||
IFS='.' read -ra PREVIOUS_VERSION_PARTS <<< "$PREVIOUS_NPM_VERSION"
|
||||
IFS='.' read -ra CURRENT_VERSION_PARTS <<< "$CURRENT_NPM_VERSION"
|
||||
VERSION_CHANGED=false
|
||||
for i in "${!PREVIOUS_VERSION_PARTS[@]}"; do
|
||||
if [[ ${CURRENT_VERSION_PARTS[i]} -gt ${PREVIOUS_VERSION_PARTS[i]} ]]; then
|
||||
VERSION_CHANGED=true
|
||||
break
|
||||
elif [[ ${CURRENT_VERSION_PARTS[i]} -lt ${PREVIOUS_VERSION_PARTS[i]} ]]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
echo "version_changed=$VERSION_CHANGED" >> "$GITHUB_ENV"
|
||||
echo "current_version=${CURRENT_NPM_VERSION}" >> "$GITHUB_ENV"
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Display result
|
||||
run: |
|
||||
echo "Version bump detected: ${{ env.version_changed }}"
|
||||
|
||||
- name: Fail if version is lower
|
||||
if: env.version_changed == 'false'
|
||||
run: exit 1
|
||||
|
||||
- name: Setup Node
|
||||
if: env.version_changed == 'true'
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- name: Publish to NPM
|
||||
if: env.version_changed == 'true'
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
||||
run: npm publish
|
||||
|
||||
- name: Setup Rust
|
||||
if: env.version_changed == 'true'
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- name: Publish to Crates.io
|
||||
if: env.version_changed == 'true'
|
||||
uses: katyo/publish-crates@v2
|
||||
with:
|
||||
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
|
||||
- name: Tag versions
|
||||
if: env.version_changed == 'true'
|
||||
run: |
|
||||
git checkout master
|
||||
git config user.name github-actions[bot]
|
||||
git config user.email github-actions[bot]@users.noreply.github.com
|
||||
git tag -d "v${{ env.current_version }}" || true
|
||||
git push origin --delete "v${{ env.current_version }}" || true
|
||||
git tag -a "v${{ env.current_version }}" -m "Version ${{ env.current_version }}"
|
||||
git push origin "v${{ env.current_version }}"
|
||||
@ -1,7 +0,0 @@
|
||||
Cargo.lock
|
||||
package-lock.json
|
||||
node_modules
|
||||
build
|
||||
*.log
|
||||
/examples/*/
|
||||
/target/
|
||||
@ -1,6 +0,0 @@
|
||||
/test
|
||||
/examples
|
||||
/build
|
||||
/script
|
||||
/target
|
||||
bindings/rust
|
||||
@ -1,24 +0,0 @@
|
||||
[package]
|
||||
name = "tree-sitter-bash"
|
||||
description = "Bash grammar for tree-sitter"
|
||||
version = "0.20.2"
|
||||
authors = ["Max Brunsfeld <maxbrunsfeld@gmail.com"]
|
||||
license = "MIT"
|
||||
readme = "bindings/rust/README.md"
|
||||
keywords = ["incremental", "parsing", "bash"]
|
||||
categories = ["parsing", "text-editors"]
|
||||
repository = "https://github.com/tree-sitter/tree-sitter-bash"
|
||||
edition = "2021"
|
||||
autoexamples = false
|
||||
|
||||
build = "bindings/rust/build.rs"
|
||||
include = ["bindings/rust/*", "grammar.js", "queries/*", "src/*"]
|
||||
|
||||
[lib]
|
||||
path = "bindings/rust/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
tree-sitter = "~0.20.10"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "~1.0.82"
|
||||
@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Max Brunsfeld
|
||||
|
||||
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,39 +0,0 @@
|
||||
// swift-tools-version:5.3
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "TreeSitterBash",
|
||||
products: [
|
||||
.library(name: "TreeSitterBash", targets: ["TreeSitterBash"]),
|
||||
],
|
||||
dependencies: [],
|
||||
targets: [
|
||||
.target(name: "TreeSitterBash",
|
||||
path: ".",
|
||||
exclude: [
|
||||
"binding.gyp",
|
||||
"bindings",
|
||||
"Cargo.toml",
|
||||
"corpus",
|
||||
"grammar.js",
|
||||
"LICENSE",
|
||||
"Makefile",
|
||||
"package.json",
|
||||
"README.md",
|
||||
"script",
|
||||
".travis.yml",
|
||||
"appveyor.yaml",
|
||||
"src/grammar.json",
|
||||
"src/node-types.json",
|
||||
],
|
||||
sources: [
|
||||
"src/parser.c",
|
||||
"src/scanner.c",
|
||||
],
|
||||
resources: [
|
||||
.copy("queries")
|
||||
],
|
||||
publicHeadersPath: "bindings/swift",
|
||||
cSettings: [.headerSearchPath("src")])
|
||||
]
|
||||
)
|
||||
@ -1,32 +0,0 @@
|
||||
# tree-sitter-bash
|
||||
|
||||
[](https://github.com/tree-sitter/tree-sitter-c/actions/workflows/ci.yml)
|
||||
|
||||
Bash grammar for [tree-sitter](https://github.com/tree-sitter/tree-sitter).
|
||||
|
||||
## Development
|
||||
|
||||
Install the dependencies:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
Build and run the tests:
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
npm run test
|
||||
```
|
||||
|
||||
Run the build and tests in watch mode:
|
||||
|
||||
```sh
|
||||
npm run test:watch
|
||||
```
|
||||
|
||||
### References
|
||||
|
||||
- [Bash man page](http://man7.org/linux/man-pages/man1/bash.1.html#SHELL_GRAMMAR)
|
||||
- [Shell command language specification](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html)
|
||||
- [mvdnan/sh - a shell parser in go](https://github.com/mvdan/sh)
|
||||
@ -1,28 +0,0 @@
|
||||
image: Visual Studio 2015
|
||||
|
||||
environment:
|
||||
NODEJS_VERSION: "16"
|
||||
PREBUILD_UPLOAD:
|
||||
secure: oNyyLE7/Oq3TUGZPz6DkLFPUuQzc8FiFS1iuPp7LZ2fyOP/UF4Np4NzJmWcXVyY/
|
||||
|
||||
platform:
|
||||
- x64
|
||||
- x86
|
||||
|
||||
install:
|
||||
- ps: Install-Product node $env:NODEJS_VERSION $env:Platform
|
||||
- node --version
|
||||
- npm --version
|
||||
- npm install
|
||||
|
||||
test_script:
|
||||
- npm run test-windows
|
||||
|
||||
build: off
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /^v.*$/
|
||||
|
||||
deploy_script: IF "%APPVEYOR_REPO_TAG%" == "true" (npm run pre-build && npm run pre-build:upload -u %PREBUILD_UPLOAD%)
|
||||
@ -1,22 +0,0 @@
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "tree_sitter_bash_binding",
|
||||
"include_dirs": [
|
||||
"<!(node -e \"require('nan')\")",
|
||||
"src"
|
||||
],
|
||||
"sources": [
|
||||
"src/parser.c",
|
||||
"bindings/node/binding.cc",
|
||||
"src/scanner.c",
|
||||
],
|
||||
"cflags_c": [
|
||||
"-std=c99",
|
||||
]
|
||||
}
|
||||
],
|
||||
'variables': {
|
||||
'openssl_fips': '',
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
#include "tree_sitter/parser.h"
|
||||
#include <node.h>
|
||||
#include "nan.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
extern "C" TSLanguage * tree_sitter_bash();
|
||||
|
||||
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_bash());
|
||||
|
||||
Nan::Set(instance, Nan::New("name").ToLocalChecked(), Nan::New("bash").ToLocalChecked());
|
||||
Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance);
|
||||
}
|
||||
|
||||
NODE_MODULE(tree_sitter_bash_binding, Init)
|
||||
|
||||
} // namespace
|
||||
@ -1,19 +0,0 @@
|
||||
try {
|
||||
module.exports = require("../../build/Release/tree_sitter_bash_binding");
|
||||
} catch (error1) {
|
||||
if (error1.code !== 'MODULE_NOT_FOUND') {
|
||||
throw error1;
|
||||
}
|
||||
try {
|
||||
module.exports = require("../../build/Debug/tree_sitter_bash_binding");
|
||||
} catch (error2) {
|
||||
if (error2.code !== 'MODULE_NOT_FOUND') {
|
||||
throw error2;
|
||||
}
|
||||
throw error1
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
module.exports.nodeTypeInfo = require("../../src/node-types.json");
|
||||
} catch (_) {}
|
||||
@ -1,35 +0,0 @@
|
||||
# tree-sitter-bash
|
||||
|
||||
This crate provides a Bash grammar for the [tree-sitter][] parsing library.
|
||||
To use this crate, add it to the `[dependencies]` section of your `Cargo.toml`
|
||||
file. (Note that you will probably also need to depend on the
|
||||
[`tree-sitter`][tree-sitter crate] crate to use the parsed result in any useful
|
||||
way.)
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tree-sitter = "0.20.10"
|
||||
tree-sitter-bash = "0.20.2"
|
||||
```
|
||||
|
||||
Typically, you will use the [language][language func] function to add this
|
||||
grammar to a tree-sitter [Parser][], and then use the parser to parse some code:
|
||||
|
||||
```rust
|
||||
let code = r#"
|
||||
echo "Hello, world!"
|
||||
echo "Goodbye, world!"
|
||||
"#;
|
||||
let mut parser = Parser::new();
|
||||
parser.set_language(tree_sitter_bash::language()).expect("Error loading Bash grammar");
|
||||
let parsed = parser.parse(code, None);
|
||||
```
|
||||
|
||||
If you have any questions, please reach out to us in the [tree-sitter
|
||||
discussions] page.
|
||||
|
||||
[language func]: https://docs.rs/tree-sitter-bash/*/tree_sitter_bash/fn.language.html
|
||||
[Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html
|
||||
[tree-sitter]: https://tree-sitter.github.io/
|
||||
[tree-sitter crate]: https://crates.io/crates/tree-sitter
|
||||
[tree-sitter discussions]: https://github.com/tree-sitter/tree-sitter/discussions
|
||||
@ -1,20 +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);
|
||||
|
||||
let scanner_path = src_dir.join("scanner.c");
|
||||
c_config.file(&scanner_path);
|
||||
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
|
||||
|
||||
c_config.compile("parser");
|
||||
println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap());
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
//! This crate provides Bash 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_bash::language()).expect("Error loading Bash 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_bash() -> 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_bash() }
|
||||
}
|
||||
|
||||
/// 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: &str = include_str!("../../src/node-types.json");
|
||||
|
||||
// Uncomment these to include any queries that this grammar contains
|
||||
|
||||
pub const HIGHLIGHT_QUERY: &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 Bash grammar");
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
#ifndef TREE_SITTER_BASH_H_
|
||||
#define TREE_SITTER_BASH_H_
|
||||
|
||||
typedef struct TSLanguage TSLanguage;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern TSLanguage *tree_sitter_bash();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TREE_SITTER_BASH_H_
|
||||
@ -1,143 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$(uname)" == 'Darwin' ]; then
|
||||
OS='Mac'
|
||||
elif [ "$(expr substr $(uname -s) 1 5)" == 'Linux' ]; then
|
||||
OS='Linux'
|
||||
else
|
||||
echo "Your platform ($(uname -a)) is not supported."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(basename $0)" == 'atom-beta' ]; then
|
||||
BETA_VERSION=true
|
||||
else
|
||||
BETA_VERSION=
|
||||
fi
|
||||
|
||||
export ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT=true
|
||||
|
||||
while getopts ":wtfvh-:" opt; do
|
||||
case "$opt" in
|
||||
-)
|
||||
case "${OPTARG}" in
|
||||
wait)
|
||||
WAIT=1
|
||||
;;
|
||||
help|version)
|
||||
REDIRECT_STDERR=1
|
||||
EXPECT_OUTPUT=1
|
||||
;;
|
||||
foreground|benchmark|benchmark-test|test)
|
||||
EXPECT_OUTPUT=1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
w)
|
||||
WAIT=1
|
||||
;;
|
||||
h|v)
|
||||
REDIRECT_STDERR=1
|
||||
EXPECT_OUTPUT=1
|
||||
;;
|
||||
f|t)
|
||||
EXPECT_OUTPUT=1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ $REDIRECT_STDERR ]; then
|
||||
exec 2> /dev/null
|
||||
fi
|
||||
|
||||
if [ $EXPECT_OUTPUT ]; then
|
||||
export ELECTRON_ENABLE_LOGGING=1
|
||||
fi
|
||||
|
||||
if [ $OS == 'Mac' ]; then
|
||||
if [ -L "$0" ]; then
|
||||
SCRIPT="$(readlink "$0")"
|
||||
else
|
||||
SCRIPT="$0"
|
||||
fi
|
||||
ATOM_APP="$(dirname "$(dirname "$(dirname "$(dirname "$SCRIPT")")")")"
|
||||
if [ "$ATOM_APP" == . ]; then
|
||||
unset ATOM_APP
|
||||
else
|
||||
ATOM_PATH="$(dirname "$ATOM_APP")"
|
||||
ATOM_APP_NAME="$(basename "$ATOM_APP")"
|
||||
fi
|
||||
|
||||
if [ -n "$BETA_VERSION" ]; then
|
||||
ATOM_EXECUTABLE_NAME="Atom Beta"
|
||||
else
|
||||
ATOM_EXECUTABLE_NAME="Atom"
|
||||
fi
|
||||
|
||||
if [ -z "${ATOM_PATH}" ]; then
|
||||
# If ATOM_PATH isn't set, check /Applications and then ~/Applications for Atom.app
|
||||
if [ -x "/Applications/$ATOM_APP_NAME" ]; then
|
||||
ATOM_PATH="/Applications"
|
||||
elif [ -x "$HOME/Applications/$ATOM_APP_NAME" ]; then
|
||||
ATOM_PATH="$HOME/Applications"
|
||||
else
|
||||
# We haven't found an Atom.app, use spotlight to search for Atom
|
||||
ATOM_PATH="$(mdfind "kMDItemCFBundleIdentifier == 'com.github.atom'" | grep -v ShipIt | head -1 | xargs -0 dirname)"
|
||||
|
||||
# Exit if Atom can't be found
|
||||
if [ ! -x "$ATOM_PATH/$ATOM_APP_NAME" ]; then
|
||||
echo "Cannot locate ${ATOM_APP_NAME}, it is usually located in /Applications. Set the ATOM_PATH environment variable to the directory containing ${ATOM_APP_NAME}."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $EXPECT_OUTPUT ]; then
|
||||
"$ATOM_PATH/$ATOM_APP_NAME/Contents/MacOS/$ATOM_EXECUTABLE_NAME" --executed-from="$(pwd)" --pid=$$ "$@"
|
||||
exit $?
|
||||
else
|
||||
open -a "$ATOM_PATH/$ATOM_APP_NAME" -n --args --executed-from="$(pwd)" --pid=$$ --path-environment="$PATH" "$@"
|
||||
fi
|
||||
elif [ $OS == 'Linux' ]; then
|
||||
SCRIPT=$(readlink -f "$0")
|
||||
USR_DIRECTORY=$(readlink -f $(dirname $SCRIPT)/..)
|
||||
|
||||
if [ -n "$BETA_VERSION" ]; then
|
||||
ATOM_PATH="$USR_DIRECTORY/share/atom-beta/atom"
|
||||
else
|
||||
ATOM_PATH="$USR_DIRECTORY/share/atom/atom"
|
||||
fi
|
||||
|
||||
ATOM_HOME="${ATOM_HOME:-$HOME/.atom}"
|
||||
mkdir -p "$ATOM_HOME"
|
||||
|
||||
: ${TMPDIR:=/tmp}
|
||||
|
||||
[ -x "$ATOM_PATH" ] || ATOM_PATH="$TMPDIR/atom-build/Atom/atom"
|
||||
|
||||
if [ $EXPECT_OUTPUT ]; then
|
||||
"$ATOM_PATH" --executed-from="$(pwd)" --pid=$$ "$@"
|
||||
exit $?
|
||||
else
|
||||
(
|
||||
nohup "$ATOM_PATH" --executed-from="$(pwd)" --pid=$$ "$@" > "$ATOM_HOME/nohup.out" 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
cat "$ATOM_HOME/nohup.out"
|
||||
exit $?
|
||||
fi
|
||||
) &
|
||||
fi
|
||||
fi
|
||||
|
||||
# Exits this process when Atom is used as $EDITOR
|
||||
on_die() {
|
||||
exit 0
|
||||
}
|
||||
trap 'on_die' SIGQUIT SIGTERM
|
||||
|
||||
# If the wait flag is set, don't exit this process until Atom tells it to.
|
||||
if [ $WAIT ]; then
|
||||
while true; do
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
@ -1,165 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# look for old 0.x cruft, and get rid of it.
|
||||
# Should already be sitting in the npm folder.
|
||||
|
||||
# This doesn't have to be quite as cross-platform as install.sh.
|
||||
# There are some bash-isms, because maintaining *two*
|
||||
# fully-portable posix/bourne sh scripts is too much for
|
||||
# one project with a sane maintainer.
|
||||
|
||||
# If readlink isn't available, then this is just too tricky.
|
||||
# However, greadlink is fine, so Solaris can join the party, too.
|
||||
readlink="readlink"
|
||||
which $readlink >/dev/null 2>/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
readlink="greadlink"
|
||||
which $readlink >/dev/null 2>/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Can't find the readlink or greadlink command. Aborting."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "x$npm_config_prefix" != "x" ]; then
|
||||
PREFIXES=$npm_config_prefix
|
||||
else
|
||||
node="$NODE"
|
||||
if [ "x$node" = "x" ]; then
|
||||
node=`which node`
|
||||
fi
|
||||
if [ "x$node" = "x" ]; then
|
||||
echo "Can't find node to determine prefix. Aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
PREFIX=`dirname $node`
|
||||
PREFIX=`dirname $PREFIX`
|
||||
echo "cleanup prefix=$PREFIX"
|
||||
PREFIXES=$PREFIX
|
||||
|
||||
altprefix=`"$node" -e process.installPrefix`
|
||||
if [ "x$altprefix" != "x" ] && [ "x$altprefix" != "x$PREFIX" ]; then
|
||||
echo "altprefix=$altprefix"
|
||||
PREFIXES="$PREFIX $altprefix"
|
||||
fi
|
||||
fi
|
||||
|
||||
# now prefix is where npm would be rooted by default
|
||||
# go hunting.
|
||||
|
||||
packages=
|
||||
for prefix in $PREFIXES; do
|
||||
packages="$packages
|
||||
"`ls "$prefix"/lib/node/.npm 2>/dev/null | grep -v .cache`
|
||||
done
|
||||
|
||||
packages=`echo $packages`
|
||||
|
||||
filelist=()
|
||||
fid=0
|
||||
|
||||
for prefix in $PREFIXES; do
|
||||
# remove any links into the .npm dir, or links to
|
||||
# version-named shims/symlinks.
|
||||
for folder in share/man bin lib/node; do
|
||||
find $prefix/$folder -type l | while read file; do
|
||||
target=`$readlink $file | grep '/\.npm/'`
|
||||
if [ "x$target" != "x" ]; then
|
||||
# found one!
|
||||
filelist[$fid]="$file"
|
||||
let 'fid++'
|
||||
# also remove any symlinks to this file.
|
||||
base=`basename "$file"`
|
||||
base=`echo "$base" | awk -F@ '{print $1}'`
|
||||
if [ "x$base" != "x" ]; then
|
||||
find "`dirname $file`" -type l -name "$base"'*' \
|
||||
| while read l; do
|
||||
target=`$readlink "$l" | grep "$base"`
|
||||
if [ "x$target" != "x" ]; then
|
||||
filelist[$fid]="$1"
|
||||
let 'fid++'
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Scour for shim files. These are relics of 0.2 npm installs.
|
||||
# note: grep -r is not portable.
|
||||
find $prefix/$folder -type f \
|
||||
| xargs grep -sl '// generated by npm' \
|
||||
| while read file; do
|
||||
filelist[$fid]="$file"
|
||||
let 'fid++'
|
||||
done
|
||||
done
|
||||
|
||||
# now remove the package modules, and the .npm folder itself.
|
||||
if [ "x$packages" != "x" ]; then
|
||||
for pkg in $packages; do
|
||||
filelist[$fid]="$prefix/lib/node/$pkg"
|
||||
let 'fid++'
|
||||
for i in $prefix/lib/node/$pkg\@*; do
|
||||
filelist[$fid]="$i"
|
||||
let 'fid++'
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
for folder in lib/node/.npm lib/npm share/npm; do
|
||||
if [ -d $prefix/$folder ]; then
|
||||
filelist[$fid]="$prefix/$folder"
|
||||
let 'fid++'
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# now actually clean, but only if there's anything TO clean
|
||||
if [ "${#filelist[@]}" -gt 0 ]; then
|
||||
echo ""
|
||||
echo "This script will find and eliminate any shims, symbolic"
|
||||
echo "links, and other cruft that was installed by npm 0.x."
|
||||
echo ""
|
||||
|
||||
if [ "x$packages" != "x" ]; then
|
||||
echo "The following packages appear to have been installed with"
|
||||
echo "an old version of npm, and will be removed forcibly:"
|
||||
for pkg in $packages; do
|
||||
echo " $pkg"
|
||||
done
|
||||
echo "Make a note of these. You may want to install them"
|
||||
echo "with npm 1.0 when this process is completed."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
OK=
|
||||
if [ "x$1" = "x-y" ]; then
|
||||
OK="yes"
|
||||
fi
|
||||
|
||||
while [ "$OK" != "y" ] && [ "$OK" != "yes" ] && [ "$OK" != "no" ]; do
|
||||
echo "Is this OK?"
|
||||
echo " enter 'yes' or 'no'"
|
||||
echo " or 'show' to see a list of files "
|
||||
read OK
|
||||
if [ "x$OK" = "xshow" ] || [ "x$OK" = "xs" ]; then
|
||||
for i in "${filelist[@]}"; do
|
||||
echo "$i"
|
||||
done
|
||||
fi
|
||||
done
|
||||
if [ "$OK" = "no" ]; then
|
||||
echo "Aborting"
|
||||
exit 1
|
||||
fi
|
||||
for i in "${filelist[@]}"; do
|
||||
rm -rf "$i"
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo 'All clean!'
|
||||
|
||||
exit 0
|
||||
@ -1,119 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [[ $DEBUG != "" ]]; then
|
||||
set -x
|
||||
fi
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
||||
if ! [ -x node_modules/.bin/marked-man ]; then
|
||||
ps=0
|
||||
if [ -f .building_marked-man ]; then
|
||||
pid=$(cat .building_marked-man)
|
||||
ps=$(ps -p $pid | grep $pid | wc -l) || true
|
||||
fi
|
||||
|
||||
if [ -f .building_marked-man ] && [ $ps != 0 ]; then
|
||||
while [ -f .building_marked-man ]; do
|
||||
sleep 1
|
||||
done
|
||||
else
|
||||
# a race to see which make process will be the one to install marked-man
|
||||
echo $$ > .building_marked-man
|
||||
sleep 1
|
||||
if [ $(cat .building_marked-man) == $$ ]; then
|
||||
make node_modules/.bin/marked-man
|
||||
rm .building_marked-man
|
||||
else
|
||||
while [ -f .building_marked-man ]; do
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! [ -x node_modules/.bin/marked ]; then
|
||||
ps=0
|
||||
if [ -f .building_marked ]; then
|
||||
pid=$(cat .building_marked)
|
||||
ps=$(ps -p $pid | grep $pid | wc -l) || true
|
||||
fi
|
||||
|
||||
if [ -f .building_marked ] && [ $ps != 0 ]; then
|
||||
while [ -f .building_marked ]; do
|
||||
sleep 1
|
||||
done
|
||||
else
|
||||
# a race to see which make process will be the one to install marked
|
||||
echo $$ > .building_marked
|
||||
sleep 1
|
||||
if [ $(cat .building_marked) == $$ ]; then
|
||||
make node_modules/.bin/marked
|
||||
rm .building_marked
|
||||
else
|
||||
while [ -f .building_marked ]; do
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
src=$1
|
||||
dest=$2
|
||||
name=$(basename ${src%.*})
|
||||
date=$(date -u +'%Y-%m-%d %H:%M:%S')
|
||||
version=$(node cli.js -v)
|
||||
|
||||
mkdir -p $(dirname $dest)
|
||||
|
||||
html_replace_tokens () {
|
||||
local url=$1
|
||||
sed "s|@NAME@|$name|g" \
|
||||
| sed "s|@DATE@|$date|g" \
|
||||
| sed "s|@URL@|$url|g" \
|
||||
| sed "s|@VERSION@|$version|g" \
|
||||
| perl -p -e 's/<h1([^>]*)>([^\(]*\([0-9]\)) -- (.*?)<\/h1>/<h1>\2<\/h1> <p>\3<\/p>/g' \
|
||||
| perl -p -e 's/npm-npm/npm/g' \
|
||||
| perl -p -e 's/([^"-])(npm-)?README(?!\.html)(\(1\))?/\1<a href="..\/..\/doc\/README.html">README<\/a>/g' \
|
||||
| perl -p -e 's/<title><a href="[^"]+README.html">README<\/a><\/title>/<title>README<\/title>/g' \
|
||||
| perl -p -e 's/([^"-])([^\(> ]+)(\(1\))/\1<a href="..\/cli\/\2.html">\2\3<\/a>/g' \
|
||||
| perl -p -e 's/([^"-])([^\(> ]+)(\(3\))/\1<a href="..\/api\/\2.html">\2\3<\/a>/g' \
|
||||
| perl -p -e 's/([^"-])([^\(> ]+)(\(5\))/\1<a href="..\/files\/\2.html">\2\3<\/a>/g' \
|
||||
| perl -p -e 's/([^"-])([^\(> ]+)(\(7\))/\1<a href="..\/misc\/\2.html">\2\3<\/a>/g' \
|
||||
| perl -p -e 's/\([1357]\)<\/a><\/h1>/<\/a><\/h1>/g' \
|
||||
| (if [ $(basename $(dirname $dest)) == "doc" ]; then
|
||||
perl -p -e 's/ href="\.\.\// href="/g'
|
||||
else
|
||||
cat
|
||||
fi)
|
||||
}
|
||||
|
||||
man_replace_tokens () {
|
||||
sed "s|@VERSION@|$version|g" \
|
||||
| perl -p -e 's/(npm\\-)?([a-zA-Z\\\.\-]*)\(1\)/npm help \2/g' \
|
||||
| perl -p -e 's/(npm\\-)?([a-zA-Z\\\.\-]*)\(([57])\)/npm help \3 \2/g' \
|
||||
| perl -p -e 's/(npm\\-)?([a-zA-Z\\\.\-]*)\(3\)/npm apihelp \2/g' \
|
||||
| perl -p -e 's/npm\(1\)/npm help npm/g' \
|
||||
| perl -p -e 's/npm\(3\)/npm apihelp npm/g'
|
||||
}
|
||||
|
||||
case $dest in
|
||||
*.[1357])
|
||||
./node_modules/.bin/marked-man --roff $src \
|
||||
| man_replace_tokens > $dest
|
||||
exit $?
|
||||
;;
|
||||
*.html)
|
||||
url=${dest/html\//}
|
||||
(cat html/dochead.html && \
|
||||
cat $src | ./node_modules/.bin/marked &&
|
||||
cat html/docfoot.html)\
|
||||
| html_replace_tokens $url \
|
||||
> $dest
|
||||
exit $?
|
||||
;;
|
||||
*)
|
||||
echo "Invalid destination type: $dest" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@ -1,270 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# A word about this shell script:
|
||||
#
|
||||
# It must work everywhere, including on systems that lack
|
||||
# a /bin/bash, map 'sh' to ksh, ksh97, bash, ash, or zsh,
|
||||
# and potentially have either a posix shell or bourne
|
||||
# shell living at /bin/sh.
|
||||
#
|
||||
# See this helpful document on writing portable shell scripts:
|
||||
# http://www.gnu.org/s/hello/manual/autoconf/Portable-Shell.html
|
||||
#
|
||||
# The only shell it won't ever work on is cmd.exe.
|
||||
|
||||
if [ "x$0" = "xsh" ]; then
|
||||
# run as curl | sh
|
||||
# on some systems, you can just do cat>npm-install.sh
|
||||
# which is a bit cuter. But on others, &1 is already closed,
|
||||
# so catting to another script file won't do anything.
|
||||
# Follow Location: headers, and fail on errors
|
||||
curl -f -L -s https://www.npmjs.org/install.sh > npm-install-$$.sh
|
||||
ret=$?
|
||||
if [ $ret -eq 0 ]; then
|
||||
(exit 0)
|
||||
else
|
||||
rm npm-install-$$.sh
|
||||
echo "Failed to download script" >&2
|
||||
exit $ret
|
||||
fi
|
||||
sh npm-install-$$.sh
|
||||
ret=$?
|
||||
rm npm-install-$$.sh
|
||||
exit $ret
|
||||
fi
|
||||
|
||||
# See what "npm_config_*" things there are in the env,
|
||||
# and make them permanent.
|
||||
# If this fails, it's not such a big deal.
|
||||
configures="`env | grep 'npm_config_' | sed -e 's|^npm_config_||g'`"
|
||||
|
||||
npm_config_loglevel="error"
|
||||
if [ "x$npm_debug" = "x" ]; then
|
||||
(exit 0)
|
||||
else
|
||||
echo "Running in debug mode."
|
||||
echo "Note that this requires bash or zsh."
|
||||
set -o xtrace
|
||||
set -o pipefail
|
||||
npm_config_loglevel="verbose"
|
||||
fi
|
||||
export npm_config_loglevel
|
||||
|
||||
# make sure that node exists
|
||||
node=`which node 2>&1`
|
||||
ret=$?
|
||||
if [ $ret -eq 0 ] && [ -x "$node" ]; then
|
||||
(exit 0)
|
||||
else
|
||||
echo "npm cannot be installed without node.js." >&2
|
||||
echo "Install node first, and then try again." >&2
|
||||
echo "" >&2
|
||||
echo "Maybe node is installed, but not in the PATH?" >&2
|
||||
echo "Note that running as sudo can change envs." >&2
|
||||
echo ""
|
||||
echo "PATH=$PATH" >&2
|
||||
exit $ret
|
||||
fi
|
||||
|
||||
# set the temp dir
|
||||
TMP="${TMPDIR}"
|
||||
if [ "x$TMP" = "x" ]; then
|
||||
TMP="/tmp"
|
||||
fi
|
||||
TMP="${TMP}/npm.$$"
|
||||
rm -rf "$TMP" || true
|
||||
mkdir "$TMP"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "failed to mkdir $TMP" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BACK="$PWD"
|
||||
|
||||
ret=0
|
||||
tar="${TAR}"
|
||||
if [ -z "$tar" ]; then
|
||||
tar="${npm_config_tar}"
|
||||
fi
|
||||
if [ -z "$tar" ]; then
|
||||
tar=`which tar 2>&1`
|
||||
ret=$?
|
||||
fi
|
||||
|
||||
if [ $ret -eq 0 ] && [ -x "$tar" ]; then
|
||||
echo "tar=$tar"
|
||||
echo "version:"
|
||||
$tar --version
|
||||
ret=$?
|
||||
fi
|
||||
|
||||
if [ $ret -eq 0 ]; then
|
||||
(exit 0)
|
||||
else
|
||||
echo "No suitable tar program found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Try to find a suitable make
|
||||
# If the MAKE environment var is set, use that.
|
||||
# otherwise, try to find gmake, and then make.
|
||||
# If no make is found, then just execute the necessary commands.
|
||||
|
||||
# XXX For some reason, make is building all the docs every time. This
|
||||
# is an annoying source of bugs. Figure out why this happens.
|
||||
MAKE=NOMAKE
|
||||
|
||||
if [ "x$MAKE" = "x" ]; then
|
||||
make=`which gmake 2>&1`
|
||||
if [ $? -eq 0 ] && [ -x "$make" ]; then
|
||||
(exit 0)
|
||||
else
|
||||
make=`which make 2>&1`
|
||||
if [ $? -eq 0 ] && [ -x "$make" ]; then
|
||||
(exit 0)
|
||||
else
|
||||
make=NOMAKE
|
||||
fi
|
||||
fi
|
||||
else
|
||||
make="$MAKE"
|
||||
fi
|
||||
|
||||
if [ -x "$make" ]; then
|
||||
(exit 0)
|
||||
else
|
||||
# echo "Installing without make. This may fail." >&2
|
||||
make=NOMAKE
|
||||
fi
|
||||
|
||||
# If there's no bash, then don't even try to clean
|
||||
if [ -x "/bin/bash" ]; then
|
||||
(exit 0)
|
||||
else
|
||||
clean="no"
|
||||
fi
|
||||
|
||||
node_version=`"$node" --version 2>&1`
|
||||
ret=$?
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "You need node to run this program." >&2
|
||||
echo "node --version reports: $node_version" >&2
|
||||
echo "with exit code = $ret" >&2
|
||||
echo "Please install node before continuing." >&2
|
||||
exit $ret
|
||||
fi
|
||||
|
||||
t="${npm_install}"
|
||||
if [ -z "$t" ]; then
|
||||
# switch based on node version.
|
||||
# note that we can only use strict sh-compatible patterns here.
|
||||
case $node_version in
|
||||
0.[01234567].* | v0.[01234567].*)
|
||||
echo "You are using an outdated and unsupported version of" >&2
|
||||
echo "node ($node_version). Please update node and try again." >&2
|
||||
exit 99
|
||||
;;
|
||||
*)
|
||||
echo "install npm@latest"
|
||||
t="latest"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# need to echo "" after, because Posix sed doesn't treat EOF
|
||||
# as an implied end of line.
|
||||
url=`(curl -SsL https://registry.npmjs.org/npm/$t; echo "") \
|
||||
| sed -e 's/^.*tarball":"//' \
|
||||
| sed -e 's/".*$//'`
|
||||
|
||||
ret=$?
|
||||
if [ "x$url" = "x" ]; then
|
||||
ret=125
|
||||
# try without the -e arg to sed.
|
||||
url=`(curl -SsL https://registry.npmjs.org/npm/$t; echo "") \
|
||||
| sed 's/^.*tarball":"//' \
|
||||
| sed 's/".*$//'`
|
||||
ret=$?
|
||||
if [ "x$url" = "x" ]; then
|
||||
ret=125
|
||||
fi
|
||||
fi
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "Failed to get tarball url for npm/$t" >&2
|
||||
exit $ret
|
||||
fi
|
||||
|
||||
|
||||
echo "fetching: $url" >&2
|
||||
|
||||
cd "$TMP" \
|
||||
&& curl -SsL "$url" \
|
||||
| $tar -xzf - \
|
||||
&& cd "$TMP"/* \
|
||||
&& (ver=`"$node" bin/read-package-json.js package.json version`
|
||||
isnpm10=0
|
||||
if [ $ret -eq 0 ]; then
|
||||
if [ -d node_modules ]; then
|
||||
if "$node" node_modules/semver/bin/semver -v "$ver" -r "1"
|
||||
then
|
||||
isnpm10=1
|
||||
fi
|
||||
else
|
||||
if "$node" bin/semver -v "$ver" -r ">=1.0"; then
|
||||
isnpm10=1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
ret=0
|
||||
if [ $isnpm10 -eq 1 ] && [ -f "scripts/clean-old.sh" ]; then
|
||||
if [ "x$skipclean" = "x" ]; then
|
||||
(exit 0)
|
||||
else
|
||||
clean=no
|
||||
fi
|
||||
if [ "x$clean" = "xno" ] \
|
||||
|| [ "x$clean" = "xn" ]; then
|
||||
echo "Skipping 0.x cruft clean" >&2
|
||||
ret=0
|
||||
elif [ "x$clean" = "xy" ] || [ "x$clean" = "xyes" ]; then
|
||||
NODE="$node" /bin/bash "scripts/clean-old.sh" "-y"
|
||||
ret=$?
|
||||
else
|
||||
NODE="$node" /bin/bash "scripts/clean-old.sh" </dev/tty
|
||||
ret=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "Aborted 0.x cleanup. Exiting." >&2
|
||||
exit $ret
|
||||
fi) \
|
||||
&& (if [ "x$configures" = "x" ]; then
|
||||
(exit 0)
|
||||
else
|
||||
echo "./configure $configures"
|
||||
echo "$configures" > npmrc
|
||||
fi) \
|
||||
&& (if [ "$make" = "NOMAKE" ]; then
|
||||
(exit 0)
|
||||
elif "$make" uninstall install; then
|
||||
(exit 0)
|
||||
else
|
||||
make="NOMAKE"
|
||||
fi
|
||||
if [ "$make" = "NOMAKE" ]; then
|
||||
"$node" cli.js rm npm -gf
|
||||
"$node" cli.js install -gf
|
||||
fi) \
|
||||
&& cd "$BACK" \
|
||||
&& rm -rf "$TMP" \
|
||||
&& echo "It worked"
|
||||
|
||||
ret=$?
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "It failed" >&2
|
||||
fi
|
||||
exit $ret
|
||||
@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# script for creating a zip and tarball for inclusion in node
|
||||
|
||||
unset CDPATH
|
||||
|
||||
set -e
|
||||
|
||||
rm -rf release *.tgz || true
|
||||
mkdir release
|
||||
node ./cli.js pack --loglevel error >/dev/null
|
||||
mv *.tgz release
|
||||
cd release
|
||||
tar xzf *.tgz
|
||||
|
||||
mkdir node_modules
|
||||
mv package node_modules/npm
|
||||
|
||||
# make the zip for windows users
|
||||
cp node_modules/npm/bin/*.cmd .
|
||||
zipname=npm-$(node ../cli.js -v).zip
|
||||
zip -q -9 -r -X "$zipname" *.cmd node_modules
|
||||
|
||||
# make the tar for node's deps
|
||||
cd node_modules
|
||||
tarname=npm-$(node ../../cli.js -v).tgz
|
||||
tar czf "$tarname" npm
|
||||
|
||||
cd ..
|
||||
mv "node_modules/$tarname" .
|
||||
|
||||
rm -rf *.cmd
|
||||
rm -rf node_modules
|
||||
|
||||
echo "release/$tarname"
|
||||
echo "release/$zipname"
|
||||
@ -1,26 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Change the cli shebang to point at the specified node
|
||||
# Useful for when the program is moved around after install.
|
||||
# Also used by the default 'make install' in node to point
|
||||
# npm at the newly installed node, rather than the first one
|
||||
# in the PATH, which would be the default otherwise.
|
||||
|
||||
# bash /path/to/npm/scripts/relocate.sh $nodepath
|
||||
# If $nodepath is blank, then it'll use /usr/bin/env
|
||||
|
||||
dir="$(dirname "$(dirname "$0")")"
|
||||
cli="$dir"/bin/npm-cli.js
|
||||
tmp="$cli".tmp
|
||||
|
||||
node="$1"
|
||||
if [ "x$node" = "x" ]; then
|
||||
node="/usr/bin/env node"
|
||||
fi
|
||||
node="#!$node"
|
||||
|
||||
sed -e 1d "$cli" > "$tmp"
|
||||
echo "$node" > "$cli"
|
||||
cat "$tmp" >> "$cli"
|
||||
rm "$tmp"
|
||||
chmod ogu+x $cli
|
||||
@ -1,9 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
git log --reverse --format='%aN <%aE>' | perl -wnE '
|
||||
BEGIN {
|
||||
say "# Authors sorted by whether or not they\x27re me";
|
||||
}
|
||||
|
||||
print $seen{$_} = $_ unless $seen{$_}
|
||||
' > AUTHORS
|
||||
@ -1,876 +0,0 @@
|
||||
/**
|
||||
* @file Bash grammar for tree-sitter
|
||||
* @author Max Brunsfeld
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/* eslint-disable arrow-parens */
|
||||
/* eslint-disable camelcase */
|
||||
/* eslint-disable-next-line spaced-comment */
|
||||
/// <reference types="tree-sitter-cli/dsl" />
|
||||
// @ts-check
|
||||
|
||||
const SPECIAL_CHARACTERS = [
|
||||
'\'', '"',
|
||||
'<', '>',
|
||||
'{', '}',
|
||||
'\\[', '\\]',
|
||||
'(', ')',
|
||||
'`', '$',
|
||||
'|', '&', ';',
|
||||
'\\',
|
||||
'\\s',
|
||||
];
|
||||
|
||||
module.exports = grammar({
|
||||
name: 'bash',
|
||||
|
||||
conflicts: $ => [
|
||||
[$._expression, $.command_name],
|
||||
[$.command, $.variable_assignments],
|
||||
[$.compound_statement],
|
||||
[$.redirected_statement, $.command],
|
||||
[$.redirected_statement, $.command_substitution],
|
||||
[$._expansion_body],
|
||||
],
|
||||
|
||||
inline: $ => [
|
||||
$._statement,
|
||||
$._terminator,
|
||||
$._literal,
|
||||
$._statements2,
|
||||
$._primary_expression,
|
||||
$._simple_variable_name,
|
||||
$._multiline_variable_name,
|
||||
$._special_variable_name,
|
||||
$._c_word,
|
||||
$._statement_not_subshell,
|
||||
],
|
||||
|
||||
externals: $ => [
|
||||
$.heredoc_start,
|
||||
$.simple_heredoc_body,
|
||||
$._heredoc_body_beginning,
|
||||
$._heredoc_body_middle,
|
||||
$.heredoc_end,
|
||||
$.file_descriptor,
|
||||
$._empty_value,
|
||||
$._concat,
|
||||
$.variable_name, // Variable name followed by an operator like '=' or '+='
|
||||
$.regex,
|
||||
$._regex_no_slash,
|
||||
$._regex_no_space,
|
||||
$.extglob_pattern,
|
||||
$._bare_dollar,
|
||||
$._brace_start,
|
||||
'}',
|
||||
']',
|
||||
'<<',
|
||||
'<<-',
|
||||
'\n',
|
||||
],
|
||||
|
||||
extras: $ => [
|
||||
$.comment,
|
||||
/\s/,
|
||||
/\\\r?\n/,
|
||||
/\\( |\t|\v|\f)/,
|
||||
],
|
||||
|
||||
supertypes: $ => [
|
||||
$._statement,
|
||||
$._expression,
|
||||
$._primary_expression,
|
||||
],
|
||||
|
||||
word: $ => $.word,
|
||||
|
||||
rules: {
|
||||
program: $ => optional($._statements),
|
||||
|
||||
_statements: $ => prec(1, seq(
|
||||
repeat(seq(
|
||||
$._statement,
|
||||
$._terminator,
|
||||
)),
|
||||
$._statement,
|
||||
optional($._terminator),
|
||||
)),
|
||||
|
||||
_statements2: $ => repeat1(seq(
|
||||
$._statement,
|
||||
$._terminator,
|
||||
)),
|
||||
|
||||
_terminated_statement: $ => seq(
|
||||
$._statement_not_subshell,
|
||||
$._terminator,
|
||||
),
|
||||
|
||||
// Statements
|
||||
|
||||
_statement: $ => choice(
|
||||
$._statement_not_subshell,
|
||||
$.subshell,
|
||||
),
|
||||
|
||||
_statement_not_subshell: $ => choice(
|
||||
$.redirected_statement,
|
||||
$.variable_assignment,
|
||||
$.variable_assignments,
|
||||
$.command,
|
||||
$.declaration_command,
|
||||
$.unset_command,
|
||||
$.test_command,
|
||||
$.negated_command,
|
||||
$.for_statement,
|
||||
$.c_style_for_statement,
|
||||
$.while_statement,
|
||||
$.if_statement,
|
||||
$.case_statement,
|
||||
$.pipeline,
|
||||
$.list,
|
||||
$.compound_statement,
|
||||
$.function_definition,
|
||||
),
|
||||
|
||||
redirected_statement: $ => prec.dynamic(-1, prec(-1, choice(
|
||||
seq(
|
||||
field('body', $._statement),
|
||||
field('redirect', repeat1(choice(
|
||||
$.file_redirect,
|
||||
$.heredoc_redirect,
|
||||
$.herestring_redirect,
|
||||
))),
|
||||
),
|
||||
repeat1($.file_redirect),
|
||||
))),
|
||||
|
||||
for_statement: $ => seq(
|
||||
choice('for', 'select'),
|
||||
field('variable', $._simple_variable_name),
|
||||
optional(seq(
|
||||
'in',
|
||||
field('value', repeat1($._literal)),
|
||||
)),
|
||||
$._terminator,
|
||||
field('body', $.do_group),
|
||||
),
|
||||
|
||||
c_style_for_statement: $ => seq(
|
||||
'for',
|
||||
'((',
|
||||
choice($._for_body),
|
||||
'))',
|
||||
optional(';'),
|
||||
field('body', choice(
|
||||
$.do_group,
|
||||
$.compound_statement,
|
||||
)),
|
||||
),
|
||||
_for_body: $ => seq(
|
||||
field('initializer', commaSep($._c_expression)),
|
||||
$._c_terminator,
|
||||
field('condition', commaSep($._c_expression)),
|
||||
$._c_terminator,
|
||||
field('update', commaSep($._c_expression)),
|
||||
),
|
||||
|
||||
_c_expression: $ => choice(
|
||||
$._c_expression_not_assignment,
|
||||
alias($._c_variable_assignment, $.variable_assignment),
|
||||
),
|
||||
_c_expression_not_assignment: $ => choice(
|
||||
$._c_word,
|
||||
$.simple_expansion,
|
||||
$.expansion,
|
||||
$.number,
|
||||
$.string,
|
||||
alias($._c_unary_expression, $.unary_expression),
|
||||
alias($._c_binary_expression, $.binary_expression),
|
||||
alias($._c_postfix_expression, $.postfix_expression),
|
||||
alias($._c_parenthesized_expression, $.parenthesized_expression),
|
||||
$.command_substitution,
|
||||
),
|
||||
|
||||
_c_variable_assignment: $ => seq(
|
||||
alias($._c_word, $.variable_name),
|
||||
'=',
|
||||
$._c_expression,
|
||||
),
|
||||
_c_unary_expression: $ => prec.left(seq(
|
||||
choice('++', '--'),
|
||||
$._c_expression_not_assignment,
|
||||
)),
|
||||
_c_binary_expression: $ => prec.right(seq(
|
||||
$._c_expression_not_assignment,
|
||||
choice(
|
||||
'+=', '-=', '*=', '/=', '%=', '**=',
|
||||
'<<=', '>>=', '&=', '^=', '|=',
|
||||
'==', '!=', '<=', '>=', '&&', '||',
|
||||
'<<', '>>',
|
||||
'+', '-', '*', '/', '%', '**',
|
||||
'<', '>',
|
||||
),
|
||||
$._c_expression_not_assignment,
|
||||
)),
|
||||
_c_postfix_expression: $ => seq(
|
||||
$._c_expression_not_assignment,
|
||||
choice('++', '--'),
|
||||
),
|
||||
_c_parenthesized_expression: $ => seq(
|
||||
'(',
|
||||
commaSep1($._c_expression),
|
||||
')',
|
||||
),
|
||||
_c_word: $ => alias(/[a-zA-Z_][a-zA-Z0-9_]*/, $.word),
|
||||
|
||||
while_statement: $ => seq(
|
||||
choice('while', 'until'),
|
||||
field('condition', repeat1($._terminated_statement)),
|
||||
field('body', $.do_group),
|
||||
),
|
||||
|
||||
do_group: $ => seq(
|
||||
'do',
|
||||
optional($._statements2),
|
||||
'done',
|
||||
),
|
||||
|
||||
if_statement: $ => seq(
|
||||
'if',
|
||||
field('condition', $._terminated_statement),
|
||||
'then',
|
||||
optional($._statements2),
|
||||
repeat($.elif_clause),
|
||||
optional($.else_clause),
|
||||
'fi',
|
||||
),
|
||||
|
||||
elif_clause: $ => seq(
|
||||
'elif',
|
||||
$._terminated_statement,
|
||||
'then',
|
||||
optional($._statements2),
|
||||
),
|
||||
|
||||
else_clause: $ => seq(
|
||||
'else',
|
||||
optional($._statements2),
|
||||
),
|
||||
|
||||
case_statement: $ => seq(
|
||||
'case',
|
||||
field('value', $._literal),
|
||||
optional($._terminator),
|
||||
'in',
|
||||
optional($._terminator),
|
||||
optional(seq(
|
||||
repeat($.case_item),
|
||||
alias($.last_case_item, $.case_item),
|
||||
)),
|
||||
'esac',
|
||||
),
|
||||
|
||||
case_item: $ => seq(
|
||||
choice(
|
||||
seq(
|
||||
optional('('),
|
||||
field('value', choice($._literal, $.extglob_pattern)),
|
||||
repeat(seq('|', field('value', choice($._literal, $.extglob_pattern)))),
|
||||
')',
|
||||
),
|
||||
),
|
||||
optional($._statements),
|
||||
prec(1, choice(
|
||||
field('termination', ';;'),
|
||||
field('fallthrough', choice(';&', ';;&')),
|
||||
)),
|
||||
),
|
||||
|
||||
last_case_item: $ => seq(
|
||||
optional('('),
|
||||
field('value', choice($._literal, $.extglob_pattern)),
|
||||
repeat(seq('|', field('value', choice($._literal, $.extglob_pattern)))),
|
||||
')',
|
||||
optional($._statements),
|
||||
optional(prec(1, ';;')),
|
||||
),
|
||||
|
||||
function_definition: $ => prec.right(seq(
|
||||
choice(
|
||||
seq(
|
||||
'function',
|
||||
field('name', $.word),
|
||||
optional(seq('(', ')')),
|
||||
),
|
||||
seq(
|
||||
field('name', $.word),
|
||||
'(', ')',
|
||||
),
|
||||
),
|
||||
field(
|
||||
'body',
|
||||
choice(
|
||||
$.compound_statement,
|
||||
$.subshell,
|
||||
$.test_command),
|
||||
),
|
||||
optional($.file_redirect),
|
||||
)),
|
||||
|
||||
compound_statement: $ => seq(
|
||||
'{',
|
||||
optional(choice($._statements2, seq($._statement, $._terminator))),
|
||||
token(prec(-1, '}')),
|
||||
),
|
||||
|
||||
subshell: $ => seq(
|
||||
'(',
|
||||
$._statements,
|
||||
')',
|
||||
),
|
||||
|
||||
pipeline: $ => prec.left(1, seq(
|
||||
$._statement,
|
||||
choice('|', '|&'),
|
||||
$._statement,
|
||||
)),
|
||||
|
||||
list: $ => prec.left(-1, seq(
|
||||
$._statement,
|
||||
choice('&&', '||'),
|
||||
$._statement,
|
||||
)),
|
||||
|
||||
// Commands
|
||||
|
||||
negated_command: $ => seq(
|
||||
'!',
|
||||
choice(
|
||||
prec(2, $.command),
|
||||
prec(1, $.variable_assignment),
|
||||
$.test_command,
|
||||
$.subshell,
|
||||
),
|
||||
),
|
||||
|
||||
test_command: $ => seq(
|
||||
choice(
|
||||
seq('[', optional(choice($._expression, $.redirected_statement)), ']'),
|
||||
seq('[[', $._expression, ']]'),
|
||||
seq('(', '(', optional($._expression), '))'),
|
||||
),
|
||||
),
|
||||
|
||||
declaration_command: $ => prec.left(seq(
|
||||
choice('declare', 'typeset', 'export', 'readonly', 'local'),
|
||||
repeat(choice(
|
||||
$._literal,
|
||||
$._simple_variable_name,
|
||||
$.variable_assignment,
|
||||
)),
|
||||
)),
|
||||
|
||||
unset_command: $ => prec.left(seq(
|
||||
choice('unset', 'unsetenv'),
|
||||
repeat(choice(
|
||||
$._literal,
|
||||
$._simple_variable_name,
|
||||
)),
|
||||
)),
|
||||
|
||||
command: $ => prec.left(seq(
|
||||
repeat(choice(
|
||||
$.variable_assignment,
|
||||
$.file_redirect,
|
||||
)),
|
||||
field('name', $.command_name),
|
||||
repeat(field('argument', choice(
|
||||
$._literal,
|
||||
alias($._bare_dollar, '$'),
|
||||
seq(
|
||||
choice('=~', '=='),
|
||||
choice($._literal, $.regex),
|
||||
),
|
||||
))),
|
||||
)),
|
||||
|
||||
command_name: $ => $._literal,
|
||||
|
||||
variable_assignment: $ => seq(
|
||||
field('name', choice(
|
||||
$.variable_name,
|
||||
$.subscript,
|
||||
)),
|
||||
choice(
|
||||
'=',
|
||||
'+=',
|
||||
),
|
||||
field('value', choice(
|
||||
$._literal,
|
||||
$.array,
|
||||
$._empty_value,
|
||||
alias($._comment_word, $.word),
|
||||
)),
|
||||
),
|
||||
|
||||
variable_assignments: $ => seq($.variable_assignment, repeat1($.variable_assignment)),
|
||||
|
||||
subscript: $ => seq(
|
||||
field('name', $.variable_name),
|
||||
'[',
|
||||
field('index', choice($._literal, $.binary_expression, $.unary_expression, $.parenthesized_expression)),
|
||||
optional($._concat),
|
||||
']',
|
||||
optional($._concat),
|
||||
),
|
||||
|
||||
file_redirect: $ => prec.left(seq(
|
||||
field('descriptor', optional($.file_descriptor)),
|
||||
choice('<', '>', '>>', '&>', '&>>', '<&', '>&', '>|'),
|
||||
field('destination', repeat1($._literal)),
|
||||
)),
|
||||
|
||||
heredoc_redirect: $ => seq(
|
||||
field('descriptor', optional($.file_descriptor)),
|
||||
choice('<<', '<<-'),
|
||||
$.heredoc_start,
|
||||
optional(seq(
|
||||
choice(alias($._heredoc_pipeline, $.pipeline), $.file_redirect),
|
||||
)),
|
||||
'\n',
|
||||
choice($._heredoc_body, $._simple_heredoc_body),
|
||||
),
|
||||
|
||||
_heredoc_pipeline: $ => seq(
|
||||
choice('|', '|&'),
|
||||
$._statement,
|
||||
),
|
||||
|
||||
_heredoc_body: $ => seq(
|
||||
$.heredoc_body,
|
||||
$.heredoc_end,
|
||||
),
|
||||
|
||||
heredoc_body: $ => seq(
|
||||
$._heredoc_body_beginning,
|
||||
repeat(choice(
|
||||
$.expansion,
|
||||
$.simple_expansion,
|
||||
$.command_substitution,
|
||||
$._heredoc_body_middle,
|
||||
)),
|
||||
),
|
||||
|
||||
_simple_heredoc_body: $ => seq(
|
||||
$.simple_heredoc_body,
|
||||
$.heredoc_end,
|
||||
),
|
||||
|
||||
herestring_redirect: $ => seq(
|
||||
field('descriptor', optional($.file_descriptor)),
|
||||
'<<<',
|
||||
$._literal,
|
||||
),
|
||||
|
||||
// Expressions
|
||||
|
||||
_expression: $ => choice(
|
||||
$._literal,
|
||||
$.unary_expression,
|
||||
$.ternary_expression,
|
||||
$.binary_expression,
|
||||
$.postfix_expression,
|
||||
$.parenthesized_expression,
|
||||
),
|
||||
|
||||
binary_expression: $ => prec.left(2, choice(
|
||||
seq(
|
||||
field('left', $._expression),
|
||||
field('operator', choice(
|
||||
'=', '==', '=~', '!=',
|
||||
'+', '-', '+=', '-=',
|
||||
'*', '/', '*=', '/=',
|
||||
'%', '%=', '**',
|
||||
'<', '>', '<=', '>=',
|
||||
'||', '&&',
|
||||
'<<', '>>', '<<=', '>>=',
|
||||
'&', '|', '^',
|
||||
'&=', '|=', '^=',
|
||||
$.test_operator,
|
||||
)),
|
||||
field('right', $._expression),
|
||||
),
|
||||
seq(
|
||||
field('left', $._expression),
|
||||
field('operator', choice('==', '=~', '!=')),
|
||||
field('right', alias($._regex_no_space, $.regex)),
|
||||
),
|
||||
)),
|
||||
|
||||
ternary_expression: $ => prec.left(
|
||||
seq(
|
||||
field('condition', $._expression),
|
||||
'?',
|
||||
field('consequence', $._expression),
|
||||
':',
|
||||
field('alternative', $._expression),
|
||||
),
|
||||
),
|
||||
|
||||
unary_expression: $ => choice(
|
||||
prec(1, seq(
|
||||
token(prec(1, choice('-', '+', '~', '++', '--'))),
|
||||
$._expression,
|
||||
)),
|
||||
prec.right(1, seq(
|
||||
choice('!', $.test_operator),
|
||||
$._expression,
|
||||
)),
|
||||
),
|
||||
|
||||
postfix_expression: $ => seq(
|
||||
$._expression,
|
||||
choice('++', '--'),
|
||||
),
|
||||
|
||||
parenthesized_expression: $ => seq(
|
||||
'(',
|
||||
$._expression,
|
||||
')',
|
||||
),
|
||||
|
||||
// Literals
|
||||
|
||||
_literal: $ => choice(
|
||||
$.concatenation,
|
||||
$._primary_expression,
|
||||
alias(prec(-2, repeat1($._special_character)), $.word),
|
||||
),
|
||||
|
||||
_primary_expression: $ => choice(
|
||||
$.word,
|
||||
alias($.test_operator, $.word),
|
||||
$.string,
|
||||
$.raw_string,
|
||||
$.translated_string,
|
||||
$.ansi_c_string,
|
||||
$.number,
|
||||
$.expansion,
|
||||
$.simple_expansion,
|
||||
$.command_substitution,
|
||||
$.process_substitution,
|
||||
$.arithmetic_expansion,
|
||||
$.brace_expression,
|
||||
),
|
||||
|
||||
arithmetic_expansion: $ => seq(choice('$((', '(('), commaSep1($._arithmetic_expression), '))'),
|
||||
|
||||
brace_expression: $ => seq(
|
||||
alias($._brace_start, '{'),
|
||||
alias(token.immediate(/\d+/), $.number),
|
||||
token.immediate('..'),
|
||||
alias(token.immediate(/\d+/), $.number),
|
||||
token.immediate('}'),
|
||||
),
|
||||
|
||||
_arithmetic_expression: $ => choice(
|
||||
$._arithmetic_literal,
|
||||
alias($._arithmetic_unary_expression, $.unary_expression),
|
||||
alias($._arithmetic_ternary_expression, $.ternary_expression),
|
||||
alias($._arithmetic_binary_expression, $.binary_expression),
|
||||
alias($._arithmetic_postfix_expression, $.postfix_expression),
|
||||
alias($._arithmetic_parenthesized_expression, $.parenthesized_expression),
|
||||
),
|
||||
|
||||
_arithmetic_literal: $ => prec(1, choice(
|
||||
$.number,
|
||||
$.test_operator,
|
||||
$.subscript,
|
||||
$.simple_expansion,
|
||||
$.expansion,
|
||||
$._simple_variable_name,
|
||||
$.variable_name,
|
||||
)),
|
||||
|
||||
_arithmetic_binary_expression: $ => prec.left(2, choice(
|
||||
seq(
|
||||
field('left', $._arithmetic_expression),
|
||||
field('operator', choice(
|
||||
'=', '==', '=~', '!=',
|
||||
'+', '-', '+=', '-=',
|
||||
'*', '/', '*=', '/=',
|
||||
'%', '%=', '**',
|
||||
'<', '>', '<=', '>=',
|
||||
'||', '&&',
|
||||
'<<', '>>', '<<=', '>>=',
|
||||
'&', '|', '^',
|
||||
'&=', '|=', '^=',
|
||||
$.test_operator,
|
||||
)),
|
||||
field('right', $._arithmetic_expression),
|
||||
),
|
||||
)),
|
||||
|
||||
_arithmetic_ternary_expression: $ => prec.left(
|
||||
seq(
|
||||
field('condition', $._arithmetic_expression),
|
||||
'?',
|
||||
field('consequence', $._arithmetic_expression),
|
||||
':',
|
||||
field('alternative', $._arithmetic_expression),
|
||||
),
|
||||
),
|
||||
|
||||
_arithmetic_unary_expression: $ => choice(
|
||||
prec(3, seq(
|
||||
token(prec(1, choice('-', '+', '~', '++', '--'))),
|
||||
$._arithmetic_expression,
|
||||
)),
|
||||
prec.right(3, seq(
|
||||
'!',
|
||||
$._arithmetic_expression,
|
||||
)),
|
||||
),
|
||||
|
||||
_arithmetic_postfix_expression: $ => seq(
|
||||
$._arithmetic_expression,
|
||||
choice('++', '--'),
|
||||
),
|
||||
|
||||
_arithmetic_parenthesized_expression: $ => seq(
|
||||
'(',
|
||||
$._arithmetic_expression,
|
||||
')',
|
||||
),
|
||||
|
||||
|
||||
concatenation: $ => prec(-1, seq(
|
||||
choice(
|
||||
$._primary_expression,
|
||||
alias($._special_character, $.word),
|
||||
),
|
||||
repeat1(seq(
|
||||
choice($._concat, alias(/`\s*`/, '``')),
|
||||
choice(
|
||||
$._primary_expression,
|
||||
alias($._special_character, $.word),
|
||||
alias($._comment_word, $.word),
|
||||
),
|
||||
)),
|
||||
optional(seq($._concat, '$')),
|
||||
)),
|
||||
|
||||
_special_character: _ => token(prec(-1, choice('{', '}', '[', ']'))),
|
||||
|
||||
string: $ => seq(
|
||||
'"',
|
||||
repeat(seq(
|
||||
choice(
|
||||
seq(optional('$'), $._string_content),
|
||||
$.expansion,
|
||||
$.simple_expansion,
|
||||
$.command_substitution,
|
||||
$.arithmetic_expansion,
|
||||
),
|
||||
optional($._concat),
|
||||
)),
|
||||
optional('$'),
|
||||
'"',
|
||||
),
|
||||
|
||||
_string_content: _ => token(prec(-1, /([^"`$\\]|\\(.|\r?\n))+/)),
|
||||
|
||||
translated_string: $ => seq('$', $.string),
|
||||
|
||||
array: $ => seq(
|
||||
'(',
|
||||
repeat($._literal),
|
||||
')',
|
||||
),
|
||||
|
||||
raw_string: _ => /'[^']*'/,
|
||||
|
||||
ansi_c_string: _ => /\$'([^']|\\')*'/,
|
||||
|
||||
number: $ => choice(
|
||||
/-?(0x)?[0-9]+(#[0-9A-Za-z@_]+)?/,
|
||||
// the base can be an expansion
|
||||
seq(/-?(0x)?[0-9]+#/, $.expansion),
|
||||
),
|
||||
|
||||
simple_expansion: $ => seq(
|
||||
'$',
|
||||
choice(
|
||||
$._simple_variable_name,
|
||||
$._multiline_variable_name,
|
||||
$._special_variable_name,
|
||||
alias('!', $.special_variable_name),
|
||||
alias('#', $.special_variable_name),
|
||||
),
|
||||
),
|
||||
|
||||
string_expansion: $ => seq('$', $.string),
|
||||
|
||||
expansion: $ => seq(
|
||||
'${',
|
||||
repeat(choice('#', '!', '=')),
|
||||
optional($._expansion_body),
|
||||
'}',
|
||||
),
|
||||
_expansion_body: $ => choice(
|
||||
seq(
|
||||
choice($.variable_name, $._special_variable_name),
|
||||
choice(
|
||||
seq(
|
||||
field('operator', choice('=', ':=', '-', ':-', '+', ':+', '?', ':?')),
|
||||
repeat(choice($._literal, $.array)),
|
||||
),
|
||||
seq(
|
||||
field('operator', choice('#', '##', '%', '%%')),
|
||||
choice($.regex, alias(')', $.regex), $.string, $.raw_string),
|
||||
),
|
||||
seq(
|
||||
choice('/', '//', '/#', '/%'),
|
||||
alias($._regex_no_slash, $.regex),
|
||||
// This can be elided
|
||||
optional(seq(
|
||||
'/',
|
||||
optional(seq(
|
||||
$._literal,
|
||||
optional('/'),
|
||||
)),
|
||||
)),
|
||||
),
|
||||
seq(
|
||||
choice(',', ',,', '^', '^^'),
|
||||
$.regex,
|
||||
),
|
||||
seq(
|
||||
':',
|
||||
choice($._simple_variable_name, $.number, $.arithmetic_expansion, $.expansion, '\n'),
|
||||
optional(seq(
|
||||
':',
|
||||
optional(choice($._simple_variable_name, $.number, $.arithmetic_expansion, '\n')),
|
||||
)),
|
||||
),
|
||||
seq(
|
||||
'@',
|
||||
field('operator', choice('U', 'u', 'L', 'Q', 'E', 'P', 'A', 'K', 'a', 'k')),
|
||||
),
|
||||
),
|
||||
),
|
||||
seq(
|
||||
choice(
|
||||
$.subscript,
|
||||
$._simple_variable_name,
|
||||
$._special_variable_name,
|
||||
$.command_substitution,
|
||||
),
|
||||
optional(seq(
|
||||
choice(
|
||||
alias(token(prec(1, ',')), ','),
|
||||
alias(token(prec(1, ',,')), ',,'),
|
||||
alias(token(prec(1, '^')), '^'),
|
||||
alias(token(prec(1, '^^')), '^^'),
|
||||
),
|
||||
optional($.regex),
|
||||
)),
|
||||
repeat(prec.right(choice(
|
||||
$._literal, $.array,
|
||||
':', ':?', '=', ':-', '%', '-', '#', ';', '|', '(', ')', '<', '>',
|
||||
))),
|
||||
),
|
||||
),
|
||||
|
||||
command_substitution: $ => choice(
|
||||
seq('$(', $._statements, ')'),
|
||||
seq('$(', $.file_redirect, ')'),
|
||||
prec(1, seq('`', $._statements, '`')),
|
||||
seq('$`', $._statements, '`'),
|
||||
),
|
||||
|
||||
process_substitution: $ => seq(
|
||||
choice('<(', '>('),
|
||||
$._statements,
|
||||
')',
|
||||
),
|
||||
|
||||
comment: _ => token(prec(-10, /#.*/)),
|
||||
_comment_word: _ => token(prec(-9, seq(
|
||||
choice(
|
||||
noneOf(...SPECIAL_CHARACTERS),
|
||||
seq('\\', noneOf('\\s')),
|
||||
),
|
||||
repeat(choice(
|
||||
noneOf(...SPECIAL_CHARACTERS),
|
||||
seq('\\', noneOf('\\s')),
|
||||
'\\ ',
|
||||
)),
|
||||
))),
|
||||
|
||||
_simple_variable_name: $ => alias(/\w+/, $.variable_name),
|
||||
_multiline_variable_name: $ => alias(
|
||||
token(prec(-1, /(\w|\\\r?\n)+/)),
|
||||
$.variable_name,
|
||||
),
|
||||
|
||||
_special_variable_name: $ => alias(choice('*', '@', '?', '-', '$', '0', '_'), $.special_variable_name),
|
||||
|
||||
word: _ => token(seq(
|
||||
choice(
|
||||
noneOf('#', ...SPECIAL_CHARACTERS),
|
||||
seq('\\', noneOf('\\s')),
|
||||
),
|
||||
repeat(choice(
|
||||
noneOf(...SPECIAL_CHARACTERS),
|
||||
seq('\\', noneOf('\\s')),
|
||||
'\\ ',
|
||||
)),
|
||||
)),
|
||||
|
||||
test_operator: _ => token(prec(1, seq('-', /[a-zA-Z]+/))),
|
||||
|
||||
_c_terminator: _ => choice(';', '\n', '&'),
|
||||
_terminator: _ => choice(';', ';;', '\n', '&'),
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns a regular expression that matches any character except the ones
|
||||
* provided.
|
||||
*
|
||||
* @param {...string} characters
|
||||
*
|
||||
* @return {RegExp}
|
||||
*
|
||||
*/
|
||||
function noneOf(...characters) {
|
||||
const negatedString = characters.map(c => c == '\\' ? '\\\\' : c).join('');
|
||||
return new RegExp('[^' + negatedString + ']');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rule to optionally match one or more of the rules separated by a comma
|
||||
*
|
||||
* @param {RuleOrLiteral} rule
|
||||
*
|
||||
* @return {ChoiceRule}
|
||||
*
|
||||
*/
|
||||
function commaSep(rule) {
|
||||
return optional(commaSep1(rule));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rule to match one or more of the rules separated by a comma
|
||||
*
|
||||
* @param {RuleOrLiteral} rule
|
||||
*
|
||||
* @return {SeqRule}
|
||||
*
|
||||
*/
|
||||
function commaSep1(rule) {
|
||||
return seq(rule, repeat(seq(',', rule)));
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
{
|
||||
"name": "tree-sitter-bash",
|
||||
"version": "0.20.2",
|
||||
"description": "Bash grammar for tree-sitter",
|
||||
"main": "bindings/node",
|
||||
"keywords": [
|
||||
"parser",
|
||||
"lexer",
|
||||
"bash"
|
||||
],
|
||||
"author": "Max Brunsfeld",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nan": "^2.17.0",
|
||||
"prebuild-install": "^7.1.1",
|
||||
"web-tree-sitter": "^0.20.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.47.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"node-gyp": "^9.4.0",
|
||||
"prebuild": "^11.0.4",
|
||||
"tree-sitter-cli": "^0.20.8"
|
||||
},
|
||||
"overrides": {
|
||||
"prebuild": {
|
||||
"node-gyp": "$node-gyp"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"install": "prebuild-install || node-gyp rebuild",
|
||||
"pre-build": "prebuild -t 14.0.0 -t 16.0.0 -t 18.0.0 -t 20.0.0 --strip && prebuild -r electron -t 16.0.0 -t 17.0.0 -t 18.0.0 -t 19.0.0 -t 20.0.0 -t 21.0.0 -t 22.0.0 -t 23.0.0 -t 23.0.0 --strip",
|
||||
"pre-build:upload": "prebuild --upload-all",
|
||||
"build": "tree-sitter generate && node-gyp build",
|
||||
"build-wasm": "tree-sitter build-wasm",
|
||||
"lint": "eslint grammar.js",
|
||||
"parse": "tree-sitter parse",
|
||||
"test": "tree-sitter test && script/parse-examples",
|
||||
"test-windows": "tree-sitter test"
|
||||
},
|
||||
"repository": "https://github.com/tree-sitter/tree-sitter-bash",
|
||||
"tree-sitter": [
|
||||
{
|
||||
"scope": "source.bash",
|
||||
"file-types": [
|
||||
"sh",
|
||||
"bash"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
[
|
||||
(string)
|
||||
(raw_string)
|
||||
(heredoc_body)
|
||||
(heredoc_start)
|
||||
] @string
|
||||
|
||||
(command_name) @function
|
||||
|
||||
(variable_name) @property
|
||||
|
||||
[
|
||||
"case"
|
||||
"do"
|
||||
"done"
|
||||
"elif"
|
||||
"else"
|
||||
"esac"
|
||||
"export"
|
||||
"fi"
|
||||
"for"
|
||||
"function"
|
||||
"if"
|
||||
"in"
|
||||
"select"
|
||||
"then"
|
||||
"unset"
|
||||
"until"
|
||||
"while"
|
||||
] @keyword
|
||||
|
||||
(comment) @comment
|
||||
|
||||
(function_definition name: (word) @function)
|
||||
|
||||
(file_descriptor) @number
|
||||
|
||||
[
|
||||
(command_substitution)
|
||||
(process_substitution)
|
||||
(expansion)
|
||||
]@embedded
|
||||
|
||||
[
|
||||
"$"
|
||||
"&&"
|
||||
">"
|
||||
">>"
|
||||
"<"
|
||||
"|"
|
||||
] @operator
|
||||
|
||||
(
|
||||
(command (_) @constant)
|
||||
(#match? @constant "^-")
|
||||
)
|
||||
@ -1,23 +0,0 @@
|
||||
examples/bash/tests/arith.tests
|
||||
examples/bash/tests/array.tests
|
||||
examples/bash/tests/assoc.tests
|
||||
examples/bash/tests/case.tests
|
||||
examples/bash/tests/comsub-posix.tests
|
||||
examples/bash/tests/comsub.tests
|
||||
examples/bash/tests/cond.tests
|
||||
examples/bash/tests/errors.tests
|
||||
examples/bash/tests/exp.tests
|
||||
examples/bash/tests/extglob.tests
|
||||
examples/bash/tests/glob.tests
|
||||
examples/bash/tests/heredoc.tests
|
||||
examples/bash/tests/histexp.tests
|
||||
examples/bash/tests/more-exp.tests
|
||||
examples/bash/tests/new-exp.tests
|
||||
examples/bash/tests/posixexp.tests
|
||||
examples/bash/tests/posixexp2.tests
|
||||
examples/bash/tests/posixpat.tests
|
||||
examples/bash/tests/printf.tests
|
||||
examples/bash/tests/quote.tests
|
||||
examples/bash/tests/quotearray.tests
|
||||
examples/bash/tests/redir.tests
|
||||
examples/bash/tests/test.tests
|
||||
@ -1,49 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
function clone_repo() {
|
||||
owner=$1
|
||||
name=$2
|
||||
url=$3
|
||||
sha=$4
|
||||
|
||||
path=examples/$name
|
||||
if [ ! -d "$path" ]; then
|
||||
echo "Cloning $owner/$name"
|
||||
git clone "$url" "$path"
|
||||
fi
|
||||
|
||||
pushd "$path" >/dev/null
|
||||
actual_sha=$(git rev-parse HEAD)
|
||||
if [ "$actual_sha" != "$sha" ]; then
|
||||
echo "Updating $owner/$name to $sha"
|
||||
git fetch
|
||||
git reset --hard "$sha"
|
||||
fi
|
||||
popd >/dev/null
|
||||
}
|
||||
|
||||
clone_repo Bash-it bash-it https://github.com/Bash-it/bash-it e38696a0acfdb6e4fbeb6963801c417d6ca7e9a7
|
||||
clone_repo bash bash https://git.savannah.gnu.org/git/bash.git ec8113b9861375e4e17b3307372569d429dec814
|
||||
|
||||
known_failures="$(cat script/known-failures.txt)"
|
||||
|
||||
# shellcheck disable=2046
|
||||
tree-sitter parse -q \
|
||||
examples/bash-it/**/*.bash \
|
||||
examples/bash-it/**/*.sh \
|
||||
examples/bash/tests/*.tests \
|
||||
examples/*.sh \
|
||||
$(for failure in $known_failures; do echo "!${failure}"; done)
|
||||
|
||||
example_count=$(find examples -name '*.bash' -o -name '*.sh' -o -name '*.tests' | wc -l)
|
||||
failure_count=$(wc -w <<<"$known_failures")
|
||||
success_count=$((example_count - failure_count))
|
||||
success_percent=$(bc -l <<<"100*${success_count}/${example_count}")
|
||||
|
||||
printf \
|
||||
"Successfully parsed %d of %d example files (%.1f%%)\n" \
|
||||
"$success_count" "$example_count" "$success_percent"
|
||||
@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
shopt -s globstar
|
||||
|
||||
tree-sitter parse -q -t \
|
||||
examples/bash-it/**/*.bash \
|
||||
examples/bash-it/**/*.sh \
|
||||
examples/bash/tests/*.tests \
|
||||
examples/*.sh \
|
||||
| egrep 'ERROR|MISSING' \
|
||||
| tee >(cut -d' ' -f1 | sort > script/known-failures.txt)
|
||||
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,745 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <tree_sitter/parser.h>
|
||||
#include <wctype.h>
|
||||
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#define STRING_RESIZE(vec, _cap) \
|
||||
void *tmp = realloc((vec).data, ((_cap) + 1) * sizeof((vec).data[0])); \
|
||||
assert(tmp != NULL); \
|
||||
(vec).data = tmp; \
|
||||
memset((vec).data + (vec).len, 0, \
|
||||
(((_cap) + 1) - (vec).len) * sizeof((vec).data[0])); \
|
||||
(vec).cap = (_cap);
|
||||
|
||||
#define STRING_GROW(vec, _cap) \
|
||||
if ((vec).cap < (_cap)) { \
|
||||
STRING_RESIZE((vec), (_cap)); \
|
||||
}
|
||||
|
||||
#define STRING_PUSH(vec, el) \
|
||||
if ((vec).cap == (vec).len) { \
|
||||
STRING_RESIZE((vec), MAX(16, (vec).len * 2)); \
|
||||
} \
|
||||
(vec).data[(vec).len++] = (el);
|
||||
|
||||
#define STRING_FREE(vec) \
|
||||
if ((vec).data != NULL) \
|
||||
free((vec).data);
|
||||
|
||||
#define STRING_CLEAR(vec) \
|
||||
{ \
|
||||
(vec).len = 0; \
|
||||
memset((vec).data, 0, (vec).cap * sizeof(char)); \
|
||||
}
|
||||
|
||||
enum TokenType {
|
||||
HEREDOC_START,
|
||||
SIMPLE_HEREDOC_BODY,
|
||||
HEREDOC_BODY_BEGINNING,
|
||||
HEREDOC_BODY_MIDDLE,
|
||||
HEREDOC_END,
|
||||
FILE_DESCRIPTOR,
|
||||
EMPTY_VALUE,
|
||||
CONCAT,
|
||||
VARIABLE_NAME,
|
||||
REGEX,
|
||||
REGEX_NO_SLASH,
|
||||
REGEX_NO_SPACE,
|
||||
EXTGLOB_PATTERN,
|
||||
BARE_DOLLAR,
|
||||
BRACE_START,
|
||||
CLOSING_BRACE,
|
||||
CLOSING_BRACKET,
|
||||
HEREDOC_ARROW,
|
||||
HEREDOC_ARROW_DASH,
|
||||
NEWLINE,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t cap;
|
||||
uint32_t len;
|
||||
char *data;
|
||||
} String;
|
||||
|
||||
static String string_new() {
|
||||
return (String){.cap = 16, .len = 0, .data = calloc(1, sizeof(char) * 17)};
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
bool heredoc_is_raw;
|
||||
bool started_heredoc;
|
||||
bool heredoc_allows_indent;
|
||||
String heredoc_delimiter;
|
||||
String current_leading_word;
|
||||
} Scanner;
|
||||
|
||||
static inline void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
|
||||
|
||||
static inline void skip(TSLexer *lexer) { lexer->advance(lexer, true); }
|
||||
|
||||
static inline bool in_error_recovery(const bool *valid_symbols) {
|
||||
return valid_symbols[HEREDOC_START] && valid_symbols[HEREDOC_END] &&
|
||||
valid_symbols[FILE_DESCRIPTOR] && valid_symbols[EMPTY_VALUE] &&
|
||||
valid_symbols[CONCAT] && valid_symbols[REGEX];
|
||||
}
|
||||
|
||||
static inline void reset(Scanner *scanner) {
|
||||
scanner->heredoc_is_raw = false;
|
||||
scanner->started_heredoc = false;
|
||||
scanner->heredoc_allows_indent = false;
|
||||
STRING_CLEAR(scanner->heredoc_delimiter);
|
||||
}
|
||||
|
||||
static unsigned serialize(Scanner *scanner, char *buffer) {
|
||||
if (scanner->heredoc_delimiter.len + 3 >=
|
||||
TREE_SITTER_SERIALIZATION_BUFFER_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
buffer[0] = (char)scanner->heredoc_is_raw;
|
||||
buffer[1] = (char)scanner->started_heredoc;
|
||||
buffer[2] = (char)scanner->heredoc_allows_indent;
|
||||
memcpy(&buffer[3], scanner->heredoc_delimiter.data,
|
||||
scanner->heredoc_delimiter.len);
|
||||
return scanner->heredoc_delimiter.len + 3;
|
||||
}
|
||||
|
||||
static void deserialize(Scanner *scanner, const char *buffer, unsigned length) {
|
||||
if (length == 0) {
|
||||
reset(scanner);
|
||||
} else {
|
||||
scanner->heredoc_is_raw = buffer[0];
|
||||
scanner->started_heredoc = buffer[1];
|
||||
scanner->heredoc_allows_indent = buffer[2];
|
||||
scanner->heredoc_delimiter.len = length - 3;
|
||||
STRING_GROW(scanner->heredoc_delimiter, scanner->heredoc_delimiter.len);
|
||||
memcpy(scanner->heredoc_delimiter.data, &buffer[3],
|
||||
scanner->heredoc_delimiter.len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume a "word" in POSIX parlance, and returns it unquoted.
|
||||
*
|
||||
* This is an approximate implementation that doesn't deal with any
|
||||
* POSIX-mandated substitution, and assumes the default value for
|
||||
* IFS.
|
||||
*/
|
||||
static bool advance_word(TSLexer *lexer, String *unquoted_word) {
|
||||
bool empty = true;
|
||||
|
||||
int32_t quote = 0;
|
||||
if (lexer->lookahead == '\'' || lexer->lookahead == '"') {
|
||||
quote = lexer->lookahead;
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
while (lexer->lookahead &&
|
||||
!(quote ? lexer->lookahead == quote : iswspace(lexer->lookahead))) {
|
||||
if (lexer->lookahead == '\\') {
|
||||
advance(lexer);
|
||||
if (!lexer->lookahead) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
empty = false;
|
||||
STRING_PUSH(*unquoted_word, lexer->lookahead);
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
if (quote && lexer->lookahead == quote) {
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
return !empty;
|
||||
}
|
||||
|
||||
static bool scan_heredoc_start(Scanner *scanner, TSLexer *lexer) {
|
||||
while (iswspace(lexer->lookahead)) {
|
||||
skip(lexer);
|
||||
}
|
||||
|
||||
lexer->result_symbol = HEREDOC_START;
|
||||
scanner->heredoc_is_raw = lexer->lookahead == '\'' ||
|
||||
lexer->lookahead == '"' ||
|
||||
lexer->lookahead == '\\';
|
||||
scanner->started_heredoc = false;
|
||||
STRING_CLEAR(scanner->heredoc_delimiter);
|
||||
|
||||
bool found_delimiter = advance_word(lexer, &scanner->heredoc_delimiter);
|
||||
if (!found_delimiter)
|
||||
STRING_CLEAR(scanner->heredoc_delimiter);
|
||||
return found_delimiter;
|
||||
}
|
||||
|
||||
static bool scan_heredoc_end_identifier(Scanner *scanner, TSLexer *lexer) {
|
||||
STRING_CLEAR(scanner->current_leading_word);
|
||||
// Scan the first 'n' characters on this line, to see if they match the
|
||||
// heredoc delimiter
|
||||
int32_t size = 0;
|
||||
while (lexer->lookahead != '\0' && lexer->lookahead != '\n' &&
|
||||
((int32_t)scanner->heredoc_delimiter.data[size++]) ==
|
||||
lexer->lookahead &&
|
||||
scanner->current_leading_word.len < scanner->heredoc_delimiter.len) {
|
||||
STRING_PUSH(scanner->current_leading_word, lexer->lookahead);
|
||||
advance(lexer);
|
||||
}
|
||||
return strcmp(scanner->current_leading_word.data,
|
||||
scanner->heredoc_delimiter.data) == 0;
|
||||
}
|
||||
|
||||
static bool scan_heredoc_content(Scanner *scanner, TSLexer *lexer,
|
||||
enum TokenType middle_type,
|
||||
enum TokenType end_type) {
|
||||
bool did_advance = false;
|
||||
|
||||
for (;;) {
|
||||
switch (lexer->lookahead) {
|
||||
case '\0': {
|
||||
if (lexer->eof(lexer) && did_advance) {
|
||||
reset(scanner);
|
||||
lexer->result_symbol = end_type;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case '\\': {
|
||||
did_advance = true;
|
||||
advance(lexer);
|
||||
advance(lexer);
|
||||
break;
|
||||
}
|
||||
|
||||
case '$': {
|
||||
if (scanner->heredoc_is_raw) {
|
||||
did_advance = true;
|
||||
advance(lexer);
|
||||
break;
|
||||
}
|
||||
if (did_advance) {
|
||||
lexer->result_symbol = middle_type;
|
||||
scanner->started_heredoc = true;
|
||||
return true;
|
||||
}
|
||||
if (middle_type == HEREDOC_BODY_BEGINNING &&
|
||||
lexer->get_column(lexer) == 0) {
|
||||
lexer->result_symbol = middle_type;
|
||||
scanner->started_heredoc = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case '\n': {
|
||||
if (!did_advance) {
|
||||
skip(lexer);
|
||||
} else {
|
||||
advance(lexer);
|
||||
}
|
||||
did_advance = true;
|
||||
if (scanner->heredoc_allows_indent) {
|
||||
while (iswspace(lexer->lookahead)) {
|
||||
advance(lexer);
|
||||
}
|
||||
}
|
||||
lexer->result_symbol =
|
||||
scanner->started_heredoc ? middle_type : end_type;
|
||||
lexer->mark_end(lexer);
|
||||
if (scan_heredoc_end_identifier(scanner, lexer)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
if (lexer->get_column(lexer) == 0) {
|
||||
// an alternative is to check the starting column of the
|
||||
// heredoc body and track that statefully
|
||||
while (iswspace(lexer->lookahead)) {
|
||||
skip(lexer);
|
||||
}
|
||||
if (end_type != SIMPLE_HEREDOC_BODY) {
|
||||
lexer->result_symbol = middle_type;
|
||||
if (scan_heredoc_end_identifier(scanner, lexer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (end_type == SIMPLE_HEREDOC_BODY) {
|
||||
lexer->result_symbol = end_type;
|
||||
lexer->mark_end(lexer);
|
||||
if (scan_heredoc_end_identifier(scanner, lexer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
did_advance = true;
|
||||
advance(lexer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool scan(Scanner *scanner, TSLexer *lexer, const bool *valid_symbols) {
|
||||
if (valid_symbols[CONCAT]) {
|
||||
if (!(lexer->lookahead == 0 || iswspace(lexer->lookahead) ||
|
||||
lexer->lookahead == '\\' || lexer->lookahead == '>' ||
|
||||
lexer->lookahead == '<' || lexer->lookahead == ')' ||
|
||||
lexer->lookahead == '(' || lexer->lookahead == ';' ||
|
||||
lexer->lookahead == '&' || lexer->lookahead == '|' ||
|
||||
(lexer->lookahead == '}' && valid_symbols[CLOSING_BRACE]) ||
|
||||
(lexer->lookahead == ']' && valid_symbols[CLOSING_BRACKET]))) {
|
||||
lexer->result_symbol = CONCAT;
|
||||
// This sucks
|
||||
if (lexer->lookahead == '`') {
|
||||
lexer->mark_end(lexer);
|
||||
advance(lexer);
|
||||
while (lexer->lookahead != '`' && !lexer->eof(lexer)) {
|
||||
advance(lexer);
|
||||
}
|
||||
if (lexer->eof(lexer)) {
|
||||
return false;
|
||||
}
|
||||
if (lexer->lookahead == '`') {
|
||||
advance(lexer);
|
||||
}
|
||||
return iswspace(lexer->lookahead) || lexer->eof(lexer);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_symbols[BARE_DOLLAR] && !in_error_recovery(valid_symbols)) {
|
||||
while (iswspace(lexer->lookahead)) {
|
||||
skip(lexer);
|
||||
}
|
||||
|
||||
if (lexer->lookahead == '$') {
|
||||
advance(lexer);
|
||||
lexer->result_symbol = BARE_DOLLAR;
|
||||
return iswspace(lexer->lookahead) || lexer->eof(lexer);
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_symbols[EMPTY_VALUE]) {
|
||||
if (iswspace(lexer->lookahead) || lexer->eof(lexer) ||
|
||||
lexer->lookahead == ';' || lexer->lookahead == '&') {
|
||||
lexer->result_symbol = EMPTY_VALUE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((valid_symbols[HEREDOC_BODY_BEGINNING] ||
|
||||
valid_symbols[SIMPLE_HEREDOC_BODY]) &&
|
||||
scanner->heredoc_delimiter.len > 0 && !scanner->started_heredoc &&
|
||||
!in_error_recovery(valid_symbols)) {
|
||||
return scan_heredoc_content(scanner, lexer, HEREDOC_BODY_BEGINNING,
|
||||
SIMPLE_HEREDOC_BODY);
|
||||
}
|
||||
|
||||
if (valid_symbols[HEREDOC_END]) {
|
||||
if (scan_heredoc_end_identifier(scanner, lexer)) {
|
||||
reset(scanner);
|
||||
lexer->result_symbol = HEREDOC_END;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_symbols[HEREDOC_BODY_MIDDLE] &&
|
||||
scanner->heredoc_delimiter.len > 0 && scanner->started_heredoc &&
|
||||
!in_error_recovery(valid_symbols)) {
|
||||
return scan_heredoc_content(scanner, lexer, HEREDOC_BODY_MIDDLE,
|
||||
HEREDOC_END);
|
||||
}
|
||||
|
||||
if (valid_symbols[HEREDOC_START] && !in_error_recovery(valid_symbols)) {
|
||||
return scan_heredoc_start(scanner, lexer);
|
||||
}
|
||||
|
||||
if ((valid_symbols[VARIABLE_NAME] || valid_symbols[FILE_DESCRIPTOR] ||
|
||||
valid_symbols[HEREDOC_ARROW]) &&
|
||||
!valid_symbols[REGEX_NO_SLASH]) {
|
||||
for (;;) {
|
||||
if (lexer->lookahead == ' ' || lexer->lookahead == '\t' ||
|
||||
lexer->lookahead == '\r' ||
|
||||
(lexer->lookahead == '\n' && !valid_symbols[NEWLINE])) {
|
||||
skip(lexer);
|
||||
} else if (lexer->lookahead == '\\') {
|
||||
skip(lexer);
|
||||
if (lexer->lookahead == '\r') {
|
||||
skip(lexer);
|
||||
}
|
||||
if (lexer->lookahead == '\n') {
|
||||
skip(lexer);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no '*', '@', '?', '-', '$', '0', '_'
|
||||
if (lexer->lookahead == '*' || lexer->lookahead == '@' ||
|
||||
lexer->lookahead == '?' || lexer->lookahead == '-' ||
|
||||
lexer->lookahead == '$' || lexer->lookahead == '0' ||
|
||||
lexer->lookahead == '_') {
|
||||
lexer->mark_end(lexer);
|
||||
advance(lexer);
|
||||
if (lexer->lookahead == '=' || lexer->lookahead == '[' ||
|
||||
lexer->lookahead == ':' || lexer->lookahead == '-' ||
|
||||
lexer->lookahead == '%' || lexer->lookahead == '#' ||
|
||||
lexer->lookahead == '/')
|
||||
return false;
|
||||
}
|
||||
|
||||
if (valid_symbols[HEREDOC_ARROW] && lexer->lookahead == '<') {
|
||||
advance(lexer);
|
||||
if (lexer->lookahead == '<') {
|
||||
advance(lexer);
|
||||
if (lexer->lookahead == '-') {
|
||||
advance(lexer);
|
||||
scanner->heredoc_allows_indent = true;
|
||||
lexer->result_symbol = HEREDOC_ARROW_DASH;
|
||||
} else if (lexer->lookahead == '<' || lexer->lookahead == '=') {
|
||||
return false;
|
||||
} else {
|
||||
scanner->heredoc_allows_indent = false;
|
||||
lexer->result_symbol = HEREDOC_ARROW;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_number = true;
|
||||
if (iswdigit(lexer->lookahead)) {
|
||||
advance(lexer);
|
||||
} else if (iswalpha(lexer->lookahead) || lexer->lookahead == '_') {
|
||||
is_number = false;
|
||||
advance(lexer);
|
||||
} else {
|
||||
if (lexer->lookahead == '{') {
|
||||
goto brace_start;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (iswdigit(lexer->lookahead)) {
|
||||
advance(lexer);
|
||||
} else if (iswalpha(lexer->lookahead) || lexer->lookahead == '_') {
|
||||
is_number = false;
|
||||
advance(lexer);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_number && valid_symbols[FILE_DESCRIPTOR] &&
|
||||
(lexer->lookahead == '>' || lexer->lookahead == '<')) {
|
||||
lexer->result_symbol = FILE_DESCRIPTOR;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (valid_symbols[VARIABLE_NAME]) {
|
||||
if (lexer->lookahead == '+') {
|
||||
lexer->mark_end(lexer);
|
||||
advance(lexer);
|
||||
if (lexer->lookahead == '=' || lexer->lookahead == ':' ||
|
||||
valid_symbols[CLOSING_BRACE]) {
|
||||
lexer->result_symbol = VARIABLE_NAME;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (lexer->lookahead == '=' || lexer->lookahead == '[' ||
|
||||
lexer->lookahead == ':' || lexer->lookahead == '%' ||
|
||||
(lexer->lookahead == '#' && !is_number) ||
|
||||
lexer->lookahead == '/') {
|
||||
lexer->mark_end(lexer);
|
||||
lexer->result_symbol = VARIABLE_NAME;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lexer->lookahead == '?') {
|
||||
lexer->mark_end(lexer);
|
||||
advance(lexer);
|
||||
lexer->result_symbol = VARIABLE_NAME;
|
||||
return isalpha(lexer->lookahead);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (valid_symbols[REGEX] || valid_symbols[REGEX_NO_SLASH] ||
|
||||
valid_symbols[REGEX_NO_SPACE] && !in_error_recovery(valid_symbols)) {
|
||||
if (valid_symbols[REGEX] || valid_symbols[REGEX_NO_SPACE]) {
|
||||
while (iswspace(lexer->lookahead)) {
|
||||
skip(lexer);
|
||||
}
|
||||
}
|
||||
|
||||
if (lexer->lookahead != '"' && lexer->lookahead != '\'' ||
|
||||
(lexer->lookahead == '$' && valid_symbols[REGEX_NO_SLASH])) {
|
||||
typedef struct {
|
||||
bool done;
|
||||
bool advanced_once;
|
||||
bool found_non_alnumdollarunderdash;
|
||||
uint32_t paren_depth;
|
||||
uint32_t bracket_depth;
|
||||
uint32_t brace_depth;
|
||||
} State;
|
||||
|
||||
lexer->mark_end(lexer);
|
||||
|
||||
State state = {false, false, false, 0, 0, 0};
|
||||
while (!state.done) {
|
||||
switch (lexer->lookahead) {
|
||||
case '\0':
|
||||
return false;
|
||||
case '(':
|
||||
state.paren_depth++;
|
||||
break;
|
||||
case '[':
|
||||
state.bracket_depth++;
|
||||
break;
|
||||
case '{':
|
||||
state.brace_depth++;
|
||||
break;
|
||||
case ')':
|
||||
if (state.paren_depth == 0) {
|
||||
state.done = true;
|
||||
}
|
||||
state.paren_depth--;
|
||||
break;
|
||||
case ']':
|
||||
if (state.bracket_depth == 0) {
|
||||
state.done = true;
|
||||
}
|
||||
state.bracket_depth--;
|
||||
break;
|
||||
case '}':
|
||||
if (state.brace_depth == 0) {
|
||||
state.done = true;
|
||||
}
|
||||
state.brace_depth--;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!state.done) {
|
||||
if (valid_symbols[REGEX]) {
|
||||
bool was_space = iswspace(lexer->lookahead);
|
||||
advance(lexer);
|
||||
state.advanced_once = true;
|
||||
if (!was_space) {
|
||||
lexer->mark_end(lexer);
|
||||
}
|
||||
} else if (valid_symbols[REGEX_NO_SLASH]) {
|
||||
if (lexer->lookahead == '/') {
|
||||
lexer->mark_end(lexer);
|
||||
lexer->result_symbol = REGEX_NO_SLASH;
|
||||
return true;
|
||||
}
|
||||
if (lexer->lookahead == '\\') {
|
||||
advance(lexer);
|
||||
if (!lexer->eof(lexer)) {
|
||||
advance(lexer);
|
||||
}
|
||||
} else {
|
||||
bool was_space = iswspace(lexer->lookahead);
|
||||
advance(lexer);
|
||||
if (!was_space) {
|
||||
lexer->mark_end(lexer);
|
||||
}
|
||||
}
|
||||
} else if (valid_symbols[REGEX_NO_SPACE]) {
|
||||
if (lexer->lookahead == '\\') {
|
||||
state.found_non_alnumdollarunderdash = true;
|
||||
advance(lexer);
|
||||
if (!lexer->eof(lexer)) {
|
||||
advance(lexer);
|
||||
}
|
||||
} else {
|
||||
if (iswspace(lexer->lookahead)) {
|
||||
lexer->mark_end(lexer);
|
||||
lexer->result_symbol = REGEX_NO_SPACE;
|
||||
return state.found_non_alnumdollarunderdash;
|
||||
}
|
||||
/* state. = true; */
|
||||
if (!iswalnum(lexer->lookahead) &&
|
||||
lexer->lookahead != '$' &&
|
||||
lexer->lookahead != '-' &&
|
||||
lexer->lookahead != '_') {
|
||||
state.found_non_alnumdollarunderdash = true;
|
||||
}
|
||||
advance(lexer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lexer->result_symbol =
|
||||
valid_symbols[REGEX_NO_SLASH] ? REGEX_NO_SLASH
|
||||
: valid_symbols[REGEX_NO_SPACE] ? REGEX_NO_SPACE
|
||||
: REGEX;
|
||||
if (valid_symbols[REGEX] && !state.advanced_once) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_symbols[EXTGLOB_PATTERN]) {
|
||||
// first skip ws, then check for ? * + @ !
|
||||
while (iswspace(lexer->lookahead)) {
|
||||
skip(lexer);
|
||||
}
|
||||
|
||||
if (lexer->lookahead == '?' || lexer->lookahead == '*' ||
|
||||
lexer->lookahead == '+' || lexer->lookahead == '@' ||
|
||||
lexer->lookahead == '!') {
|
||||
lexer->mark_end(lexer);
|
||||
advance(lexer);
|
||||
|
||||
if (lexer->lookahead != '(') {
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
bool done;
|
||||
uint32_t paren_depth;
|
||||
uint32_t bracket_depth;
|
||||
uint32_t brace_depth;
|
||||
} State;
|
||||
|
||||
State state = {false, 0, 0, 0};
|
||||
while (!state.done) {
|
||||
switch (lexer->lookahead) {
|
||||
case '\0':
|
||||
return false;
|
||||
case '(':
|
||||
state.paren_depth++;
|
||||
break;
|
||||
case '[':
|
||||
state.bracket_depth++;
|
||||
break;
|
||||
case '{':
|
||||
state.brace_depth++;
|
||||
break;
|
||||
case ')':
|
||||
if (state.paren_depth == 0) {
|
||||
state.done = true;
|
||||
}
|
||||
state.paren_depth--;
|
||||
break;
|
||||
case ']':
|
||||
if (state.bracket_depth == 0) {
|
||||
state.done = true;
|
||||
}
|
||||
state.bracket_depth--;
|
||||
break;
|
||||
case '}':
|
||||
if (state.brace_depth == 0) {
|
||||
state.done = true;
|
||||
}
|
||||
state.brace_depth--;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!state.done) {
|
||||
bool was_space = iswspace(lexer->lookahead);
|
||||
advance(lexer);
|
||||
if (!was_space) {
|
||||
lexer->mark_end(lexer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lexer->result_symbol = EXTGLOB_PATTERN;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
brace_start:
|
||||
if (valid_symbols[BRACE_START] && !in_error_recovery(valid_symbols)) {
|
||||
while (iswspace(lexer->lookahead)) {
|
||||
skip(lexer);
|
||||
}
|
||||
|
||||
if (lexer->lookahead != '{') {
|
||||
return false;
|
||||
}
|
||||
|
||||
advance(lexer);
|
||||
lexer->mark_end(lexer);
|
||||
|
||||
while (isdigit(lexer->lookahead)) {
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
if (lexer->lookahead != '.') {
|
||||
return false;
|
||||
}
|
||||
advance(lexer);
|
||||
|
||||
if (lexer->lookahead != '.') {
|
||||
return false;
|
||||
}
|
||||
advance(lexer);
|
||||
|
||||
while (isdigit(lexer->lookahead)) {
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
if (lexer->lookahead != '}') {
|
||||
return false;
|
||||
}
|
||||
|
||||
lexer->result_symbol = BRACE_START;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void *tree_sitter_bash_external_scanner_create() {
|
||||
Scanner *scanner = calloc(1, sizeof(Scanner));
|
||||
scanner->heredoc_delimiter = string_new();
|
||||
scanner->current_leading_word = string_new();
|
||||
return scanner;
|
||||
}
|
||||
|
||||
bool tree_sitter_bash_external_scanner_scan(void *payload, TSLexer *lexer,
|
||||
const bool *valid_symbols) {
|
||||
Scanner *scanner = (Scanner *)payload;
|
||||
return scan(scanner, lexer, valid_symbols);
|
||||
}
|
||||
|
||||
unsigned tree_sitter_bash_external_scanner_serialize(void *payload,
|
||||
char *state) {
|
||||
Scanner *scanner = (Scanner *)payload;
|
||||
return serialize(scanner, state);
|
||||
}
|
||||
|
||||
void tree_sitter_bash_external_scanner_deserialize(void *payload,
|
||||
const char *state,
|
||||
unsigned length) {
|
||||
Scanner *scanner = (Scanner *)payload;
|
||||
deserialize(scanner, state, length);
|
||||
}
|
||||
|
||||
void tree_sitter_bash_external_scanner_destroy(void *payload) {
|
||||
Scanner *scanner = (Scanner *)payload;
|
||||
STRING_FREE(scanner->heredoc_delimiter);
|
||||
STRING_FREE(scanner->current_leading_word);
|
||||
free(scanner);
|
||||
}
|
||||
@ -1,224 +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
|
||||
|
||||
#ifndef TREE_SITTER_API_H_
|
||||
typedef uint16_t TSStateId;
|
||||
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;
|
||||
const TSStateId *primary_state_ids;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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; \
|
||||
eof = lexer->eof(lexer);
|
||||
|
||||
#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,491 +0,0 @@
|
||||
===============================
|
||||
Commands
|
||||
===============================
|
||||
|
||||
whoami
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(command (command_name (word))))
|
||||
|
||||
===============================
|
||||
Commands with arguments
|
||||
===============================
|
||||
|
||||
cat file1.txt
|
||||
git diff --word-diff=color -- file1.txt file2.txt
|
||||
echo $sing\
|
||||
levar
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(command (command_name (word)) (word))
|
||||
(command (command_name (word)) (word) (word) (word) (word) (word))
|
||||
(command (command_name (word)) (simple_expansion (variable_name)) (word)))
|
||||
|
||||
===============================
|
||||
Quoted command names
|
||||
===============================
|
||||
|
||||
"$a/$b" c
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(command
|
||||
(command_name (string (simple_expansion (variable_name)) (simple_expansion (variable_name))))
|
||||
(word)))
|
||||
|
||||
===============================
|
||||
Commands with numeric arguments
|
||||
===============================
|
||||
|
||||
exit 1
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(command (command_name (word)) (number)))
|
||||
|
||||
===================================
|
||||
Commands with environment variables
|
||||
===================================
|
||||
|
||||
VAR1=1 ./script/test
|
||||
VAR1=a VAR2="ok" git diff --word-diff=color
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(command
|
||||
(variable_assignment (variable_name) (number))
|
||||
(command_name (word)))
|
||||
(command
|
||||
(variable_assignment (variable_name) (word))
|
||||
(variable_assignment (variable_name) (string))
|
||||
(command_name (word))
|
||||
(word)
|
||||
(word)))
|
||||
|
||||
===================================
|
||||
Empty environment variables
|
||||
===================================
|
||||
|
||||
VAR1=
|
||||
VAR2= echo
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(variable_assignment (variable_name))
|
||||
(command (variable_assignment (variable_name)) (command_name (word))))
|
||||
|
||||
===============================
|
||||
File redirects
|
||||
===============================
|
||||
|
||||
whoami > /dev/null
|
||||
cat a b > /dev/null
|
||||
2>&1 whoami
|
||||
echo "foobar" >&2
|
||||
[ ! command -v go &>/dev/null ] && return
|
||||
|
||||
if [ ]; then
|
||||
>aa >bb
|
||||
fi
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(redirected_statement
|
||||
(command (command_name (word)))
|
||||
(file_redirect (word)))
|
||||
(redirected_statement
|
||||
(command (command_name (word)) (word) (word))
|
||||
(file_redirect (word)))
|
||||
(command
|
||||
(file_redirect (file_descriptor) (number))
|
||||
(command_name (word)))
|
||||
(redirected_statement
|
||||
(command (command_name (word)) (string))
|
||||
(file_redirect (number)))
|
||||
(list
|
||||
(test_command
|
||||
(redirected_statement
|
||||
(negated_command
|
||||
(command (command_name (word)) (word) (word)))
|
||||
(file_redirect (word))))
|
||||
(command (command_name (word))))
|
||||
(if_statement
|
||||
(test_command)
|
||||
(redirected_statement
|
||||
(file_redirect (word))
|
||||
(file_redirect (word)))))
|
||||
|
||||
===============================
|
||||
File redirects (noclobber override)
|
||||
===============================
|
||||
|
||||
whoami >| /dev/null
|
||||
cat a b >| /dev/null
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(redirected_statement
|
||||
(command (command_name (word)))
|
||||
(file_redirect (word)))
|
||||
(redirected_statement
|
||||
(command (command_name (word)) (word) (word))
|
||||
(file_redirect (word))))
|
||||
|
||||
===============================
|
||||
Heredoc redirects
|
||||
===============================
|
||||
|
||||
node <<JS
|
||||
console.log("hi")
|
||||
JS
|
||||
|
||||
bash -c <<JS
|
||||
echo hi
|
||||
JS
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(redirected_statement
|
||||
(command
|
||||
(command_name
|
||||
(word)))
|
||||
(heredoc_redirect
|
||||
(heredoc_start)
|
||||
(simple_heredoc_body)
|
||||
(heredoc_end)))
|
||||
(redirected_statement
|
||||
(command
|
||||
(command_name
|
||||
(word))
|
||||
(word))
|
||||
(heredoc_redirect
|
||||
(heredoc_start)
|
||||
(simple_heredoc_body)
|
||||
(heredoc_end))))
|
||||
|
||||
===============================
|
||||
Heredocs with variables
|
||||
===============================
|
||||
|
||||
node <<JS
|
||||
a $B ${C}
|
||||
JS
|
||||
|
||||
exit
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(redirected_statement
|
||||
(command
|
||||
(command_name
|
||||
(word)))
|
||||
(heredoc_redirect
|
||||
(heredoc_start)
|
||||
(heredoc_body
|
||||
(simple_expansion
|
||||
(variable_name))
|
||||
(expansion
|
||||
(variable_name)))
|
||||
(heredoc_end)))
|
||||
(command
|
||||
(command_name
|
||||
(word))))
|
||||
|
||||
=================================
|
||||
Heredocs with file redirects
|
||||
=================================
|
||||
|
||||
cat <<EOF > $tmpfile
|
||||
a $B ${C}
|
||||
EOF
|
||||
|
||||
wc -l $tmpfile
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(redirected_statement
|
||||
(command
|
||||
(command_name
|
||||
(word)))
|
||||
(heredoc_redirect
|
||||
(heredoc_start)
|
||||
(file_redirect
|
||||
(simple_expansion
|
||||
(variable_name)))
|
||||
(heredoc_body
|
||||
(simple_expansion
|
||||
(variable_name))
|
||||
(expansion
|
||||
(variable_name)))
|
||||
(heredoc_end)))
|
||||
(command
|
||||
(command_name
|
||||
(word))
|
||||
(word)
|
||||
(simple_expansion
|
||||
(variable_name))))
|
||||
|
||||
=================================
|
||||
Heredocs with pipes
|
||||
=================================
|
||||
|
||||
one <<EOF | grep two
|
||||
three
|
||||
EOF
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(redirected_statement
|
||||
(command
|
||||
(command_name
|
||||
(word)))
|
||||
(heredoc_redirect
|
||||
(heredoc_start)
|
||||
(pipeline
|
||||
(command
|
||||
(command_name
|
||||
(word))
|
||||
(word)))
|
||||
(simple_heredoc_body)
|
||||
(heredoc_end))))
|
||||
|
||||
======================================
|
||||
Heredocs with escaped expansions
|
||||
======================================
|
||||
|
||||
cat << EOF
|
||||
DEV_NAME=\$(lsblk)
|
||||
EOF
|
||||
|
||||
---
|
||||
|
||||
(program (redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end))))
|
||||
|
||||
======================================
|
||||
Quoted Heredocs
|
||||
======================================
|
||||
|
||||
cat << 'EOF'
|
||||
a=$b
|
||||
EOF
|
||||
|
||||
cat << "EOF"
|
||||
a=$b
|
||||
EOF
|
||||
|
||||
cat <<"END OF FILE"
|
||||
hello,
|
||||
world
|
||||
END OF FILE
|
||||
|
||||
cat << \EOF
|
||||
EOF
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end)))
|
||||
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end)))
|
||||
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end)))
|
||||
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end))))
|
||||
|
||||
==========================================
|
||||
Heredocs with indented closing delimiters
|
||||
==========================================
|
||||
|
||||
usage() {
|
||||
cat <<-EOF
|
||||
Usage: ${0##*/} FOO BAR
|
||||
EOF
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(function_definition
|
||||
(word)
|
||||
(compound_statement
|
||||
(redirected_statement
|
||||
(command (command_name (word)))
|
||||
(heredoc_redirect (heredoc_start) (heredoc_body (expansion (special_variable_name) (regex))) (heredoc_end))))))
|
||||
|
||||
==========================================
|
||||
Heredocs with empty bodies
|
||||
==========================================
|
||||
|
||||
node <<JS
|
||||
JS
|
||||
|
||||
node << 'SJ'
|
||||
SJ
|
||||
|
||||
usage() {
|
||||
cat <<-EOF
|
||||
EOF
|
||||
}
|
||||
|
||||
node << 'EOF' > temp
|
||||
EOF
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(redirected_statement
|
||||
body: (command
|
||||
name: (command_name
|
||||
(word)))
|
||||
redirect: (heredoc_redirect
|
||||
(heredoc_start)
|
||||
(simple_heredoc_body)
|
||||
(heredoc_end)))
|
||||
(redirected_statement
|
||||
body: (command
|
||||
name: (command_name
|
||||
(word)))
|
||||
redirect: (heredoc_redirect
|
||||
(heredoc_start)
|
||||
(simple_heredoc_body)
|
||||
(heredoc_end)))
|
||||
(function_definition
|
||||
name: (word)
|
||||
body: (compound_statement
|
||||
(redirected_statement
|
||||
body: (command
|
||||
name: (command_name
|
||||
(word)))
|
||||
redirect: (heredoc_redirect
|
||||
(heredoc_start)
|
||||
(simple_heredoc_body)
|
||||
(heredoc_end)))))
|
||||
(redirected_statement
|
||||
body: (command
|
||||
name: (command_name
|
||||
(word)))
|
||||
redirect: (heredoc_redirect
|
||||
(heredoc_start)
|
||||
(file_redirect
|
||||
destination: (word))
|
||||
(simple_heredoc_body)
|
||||
(heredoc_end))))
|
||||
|
||||
==========================================
|
||||
Heredocs with weird characters
|
||||
==========================================
|
||||
|
||||
node <<_DELIMITER_WITH_UNDERSCORES_
|
||||
Hello.
|
||||
_DELIMITER_WITH_UNDERSCORES_
|
||||
|
||||
node <<'```'
|
||||
Hello.
|
||||
```
|
||||
|
||||
node <<!HEREDOC!
|
||||
Hello.
|
||||
!HEREDOC!
|
||||
|
||||
node <<\'
|
||||
Hello.
|
||||
'
|
||||
|
||||
node <<\\
|
||||
Hello.
|
||||
\
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end)))
|
||||
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end)))
|
||||
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end)))
|
||||
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end)))
|
||||
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start) (simple_heredoc_body) (heredoc_end))))
|
||||
|
||||
==========================================
|
||||
Herestrings
|
||||
==========================================
|
||||
|
||||
node <<< foo
|
||||
|
||||
while read -u 3 entry; do
|
||||
echo $entry
|
||||
done 3<<<"$ENTRIES"
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(redirected_statement (command (command_name (word))) (herestring_redirect (word)))
|
||||
(redirected_statement
|
||||
(while_statement
|
||||
(command (command_name (word)) (word) (number) (word))
|
||||
(do_group (command (command_name (word)) (simple_expansion (variable_name)))))
|
||||
(herestring_redirect (file_descriptor) (string (simple_expansion (variable_name))))))
|
||||
|
||||
==========================================
|
||||
Subscripts
|
||||
==========================================
|
||||
|
||||
echo ${a[1 + 2]}
|
||||
|
||||
echo ${b[1234 % 2]}
|
||||
|
||||
${words[++counter]}
|
||||
|
||||
${array[(($number+1))]}
|
||||
|
||||
${array[((number+1))]}
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(command
|
||||
(command_name (word))
|
||||
(expansion
|
||||
(subscript (variable_name) (binary_expression (number) (number)))))
|
||||
(command
|
||||
(command_name (word))
|
||||
(expansion
|
||||
(subscript (variable_name) (binary_expression (number) (number)))))
|
||||
(command
|
||||
(command_name
|
||||
(expansion
|
||||
(subscript (variable_name) (unary_expression (word))))))
|
||||
(command
|
||||
(command_name
|
||||
(expansion
|
||||
(subscript
|
||||
(variable_name)
|
||||
(arithmetic_expansion (binary_expression (simple_expansion (variable_name)) (number)))))))
|
||||
(command
|
||||
(command_name
|
||||
(expansion
|
||||
(subscript
|
||||
(variable_name)
|
||||
(arithmetic_expansion (binary_expression (variable_name) (number))))))))
|
||||
|
||||
==========================================
|
||||
Bare $
|
||||
==========================================
|
||||
|
||||
echo $
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(command
|
||||
(command_name
|
||||
(word))))
|
||||
@ -1,13 +0,0 @@
|
||||
================================
|
||||
Variables with CRLF line endings
|
||||
================================
|
||||
|
||||
A=one
|
||||
|
||||
B=two
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(variable_assignment (variable_name) (word))
|
||||
(variable_assignment (variable_name) (word)))
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,108 +0,0 @@
|
||||
===============================
|
||||
Comments
|
||||
===============================
|
||||
|
||||
#!/bin/bash
|
||||
# hi
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(comment)
|
||||
(comment))
|
||||
|
||||
===============================
|
||||
Escaped newlines
|
||||
===============================
|
||||
|
||||
abc \
|
||||
d \
|
||||
e
|
||||
|
||||
f=g \
|
||||
h=i \
|
||||
j \
|
||||
--k
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(command
|
||||
(command_name
|
||||
(word))
|
||||
(word)
|
||||
(word))
|
||||
(command
|
||||
(variable_assignment
|
||||
(variable_name)
|
||||
(word))
|
||||
(variable_assignment
|
||||
(variable_name)
|
||||
(word))
|
||||
(command_name
|
||||
(word))
|
||||
(word)))
|
||||
|
||||
=============================
|
||||
escaped newline immediately after a char
|
||||
=============================
|
||||
|
||||
echo a \
|
||||
b
|
||||
|
||||
echo a\
|
||||
b
|
||||
|
||||
echo a\
|
||||
b\
|
||||
c
|
||||
|
||||
|
||||
-----------------------------
|
||||
|
||||
(program
|
||||
(command
|
||||
(command_name
|
||||
(word))
|
||||
(word)
|
||||
(word))
|
||||
(command
|
||||
(command_name
|
||||
(word))
|
||||
(word)
|
||||
(word))
|
||||
(command
|
||||
(command_name
|
||||
(word))
|
||||
(word)
|
||||
(word)
|
||||
(word)))
|
||||
|
||||
=============================
|
||||
Escaped whitespace
|
||||
=============================
|
||||
|
||||
echo 1 \ 2 \ 3
|
||||
|
||||
---
|
||||
|
||||
(program
|
||||
(command
|
||||
(command_name
|
||||
(word))
|
||||
(number)
|
||||
(number)
|
||||
(number)))
|
||||
|
||||
====================================
|
||||
Files without trailing terminators
|
||||
====================================
|
||||
|
||||
echo hi
|
||||
---
|
||||
|
||||
(program
|
||||
(command
|
||||
(command_name
|
||||
(word))
|
||||
(word)))
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue