Add 'vendor/tree-sitter-bash/' from commit '4094e3a0405aabb1314c0c41f29e38e70af86fa5'

git-subtree-dir: vendor/tree-sitter-bash
git-subtree-mainline: 7d1bf1e5f4
git-subtree-split: 4094e3a040
pull/70/head
Wilfred Hughes 2021-11-24 23:33:05 +07:00
commit 678c1c5773
40 changed files with 138841 additions and 0 deletions

@ -0,0 +1,2 @@
/src/** linguist-vendored
/examples/* linguist-vendored

@ -0,0 +1,22 @@
name: Build & Test
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build-and-test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 14
- run: npm install
- run: node_modules/.bin/tree-sitter test

@ -0,0 +1,7 @@
node_modules
build
*.log
package-lock.json
test.sh
Cargo.lock
target

@ -0,0 +1,3 @@
[submodule "examples/bash-it"]
path = examples/bash-it
url = https://github.com/Bash-it/bash-it.git

@ -0,0 +1,6 @@
corpus
build
examples
script
target
Cargo.lock

@ -0,0 +1,34 @@
language: node_js
sudo: false
node_js:
- node
os:
- linux
matrix:
include:
- os: osx
env: COMPILER=clang++
osx_image: xcode9.2
compiler: clang
- os: linux
branches:
only:
- master
- /^v.*$/
deploy:
provider: script
script: npm run pre-build && npm run pre-build:upload -u ${PREBUILD_UPLOAD}
skip_cleanup: true
on:
all_branches: true
tags: true
env:
global:
secure: jAdfI37gohXm5VvR5h2AoWVwUMS9hUPxZIP4VR2iOkTSZLaaZczvL04IRNv3bQY6WUYxcVB6xoKbOPMKE1cHvXM8L2W6G1InPxU14P6fKd2MGWxH6PWmCoXHWFQ1AzWkYsMgMokHT2vs4iZ2bLHE9uV3RVuAISO/APsw7AxEpEijljvPkL+Se0nTnCfTbw4ObGxEMRW7TnjKa2W7K8m4QbbGd+r9CzCxBakjdIePQx81IUAAIGwphkY/avNJeUvR1XLnAM38K9Yj0ioSfeV6/QshK/28DLmvilq9sV4LDLVlhgP6h1FB4HW3PEVvYWnWBKa9cUdIAIncya2ibvu2cRHYVbud4Ho6lMCk/QUuiPYLCfnOfa4byVj3DInOY1yCpu+YnqjVOUHV5wQLEvnArLCuJa7dlJSmYquzVr5NkOz9gMmFAyuznL3YdJYY98QH/5GO5FwE8jXiYMoQ+hW52gUB6vZaJqYJu+IwyEhVXj8SVyV3Z77fFmJpdo0FX8R4Mm/3ucVXfWAXifFQbosRM4czVJ3RNTn2Xwf5Vp0ayih4huhwB9reByNPnYJABNUQpiAS3ZRsd+fcmMstkHZOk9EpSq33kubqcN+kub3sGNOJTt9243FOd6BghNNjhC2lqTWZsefyJUuL3Xm5YjJrJWfyif6RPnJZHinn3jfn464=

@ -0,0 +1,29 @@
[package]
name = "tree-sitter-bash"
description = "bash grammar for the tree-sitter parsing library"
version = "0.19.0"
keywords = ["incremental", "parsing", "bash"]
categories = ["parsing", "text-editors"]
repository = "https://github.com/tree-sitter/tree-sitter-bash"
edition = "2018"
license = "MIT"
authors = [
"Max Brunsfeld <maxbrunsfeld@gmail.com",
]
build = "bindings/rust/build.rs"
include = [
"bindings/rust/*",
"grammar.js",
"queries/*",
"src/*",
]
[lib]
path = "bindings/rust/lib.rs"
[dependencies]
tree-sitter = "0.19"
[build-dependencies]
cc = "1.0"

@ -0,0 +1,21 @@
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.

@ -0,0 +1,28 @@
tree-sitter-bash
================
[![Build Status](https://travis-ci.org/tree-sitter/tree-sitter-bash.svg?branch=master)](https://travis-ci.org/tree-sitter/tree-sitter-bash)
[![Build status](https://ci.appveyor.com/api/projects/status/9mbap95nldligssx/branch/master?svg=true)](https://ci.appveyor.com/project/maxbrunsfeld/tree-sitter-bash/branch/master)
Bash grammar for [tree-sitter](https://github.com/tree-sitter/tree-sitter).
### Development
Install the dependencies:
npm install
Build and run the tests:
npm run build
npm run test
Run the build and tests in watch mode:
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)

@ -0,0 +1,28 @@
image: Visual Studio 2015
environment:
NODEJS_VERSION: "8"
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%)

@ -0,0 +1,19 @@
{
"targets": [
{
"target_name": "tree_sitter_bash_binding",
"include_dirs": [
"<!(node -e \"require('nan')\")",
"src"
],
"sources": [
"src/parser.c",
"bindings/node/binding.cc",
"src/scanner.cc",
],
"cflags_c": [
"-std=c99",
]
}
]
}

@ -0,0 +1,28 @@
#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

@ -0,0 +1,19 @@
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 (_) {}

@ -0,0 +1,25 @@
fn main() {
let src_dir = std::path::Path::new("src");
let mut c_config = cc::Build::new();
c_config.include(&src_dir);
c_config
.flag_if_supported("-Wno-unused-parameter")
.flag_if_supported("-Wno-unused-but-set-variable")
.flag_if_supported("-Wno-trigraphs");
let parser_path = src_dir.join("parser.c");
c_config.file(&parser_path);
c_config.compile("parser");
println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap());
let mut cpp_config = cc::Build::new();
cpp_config.cpp(true);
cpp_config.include(&src_dir);
cpp_config
.flag_if_supported("-Wno-unused-parameter")
.flag_if_supported("-Wno-unused-but-set-variable");
let scanner_path = src_dir.join("scanner.cc");
cpp_config.file(&scanner_path);
cpp_config.compile("scanner");
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
}

@ -0,0 +1,52 @@
//! 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: &'static str = include_str!("../../src/node-types.json");
// Uncomment these to include any queries that this grammar contains
// pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm");
// pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm");
// pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm");
// pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm");
#[cfg(test)]
mod tests {
#[test]
fn test_can_load_grammar() {
let mut parser = tree_sitter::Parser::new();
parser
.set_language(super::language())
.expect("Error loading bash language");
}
}

@ -0,0 +1,267 @@
===============================
Commands
===============================
whoami
---
(program
(command (command_name (word))))
===============================
Commands with arguments
===============================
cat file1.txt
git diff --word-diff=color -- file1.txt file2.txt
---
(program
(command (command_name (word)) (word))
(command (command_name (word)) (word) (word) (word) (word) (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)) (word)))
===================================
Commands with environment variables
===================================
VAR1=1 ./script/test
VAR1=a VAR2="ok" git diff --word-diff=color
---
(program
(command
(variable_assignment (variable_name) (word))
(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
---
(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) (word))
(command_name (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)))
(heredoc_body)
(redirected_statement
(command (command_name (word)) (word))
(heredoc_redirect (heredoc_start)))
(heredoc_body))
===============================
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)))
(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)))
(command
(command_name (word))
(word)
(simple_expansion (variable_name))))
=================================
Heredocs with pipes
================================
one <<EOF | grep two
three
EOF
---
(program
(pipeline
(redirected_statement
(command (command_name (word)))
(heredoc_redirect (heredoc_start)))
(command (command_name (word)) (word)))
(heredoc_body))
======================================
Heredocs with escaped expansions
======================================
cat << EOF
DEV_NAME=\$(lsblk)
EOF
---
(program (redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start))) (heredoc_body))
======================================
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))) (heredoc_body)
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start))) (heredoc_body (simple_expansion (variable_name)))
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start))) (heredoc_body)
(redirected_statement (command (command_name (word))) (heredoc_redirect (heredoc_start))) (heredoc_body))
==========================================
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) (word))))))

@ -0,0 +1,13 @@
================================
Variables with CRLF line endings
================================
A=one
B=two
---
(program
(variable_assignment (variable_name) (word))
(variable_assignment (variable_name) (word)))

@ -0,0 +1,372 @@
=============================
Literal words
=============================
echo a
echo a b
---
(program
(command (command_name (word)) (word))
(command (command_name (word)) (word) (word)))
=============================
Words with special characters
=============================
echo {o[k]}
echo }}}
echo ]]] ===
---
(program
(command (command_name (word)) (concatenation (word) (word)))
(command (command_name (word)) (concatenation))
(command (command_name (word)) (concatenation) (word)))
=============================
Simple variable expansions
=============================
echo $abc
---
(program
(command (command_name (word)) (simple_expansion (variable_name))))
=============================
Special variable expansions
=============================
echo $# $* $@ $!
---
(program
(command
(command_name (word))
(simple_expansion (special_variable_name))
(simple_expansion (special_variable_name))
(simple_expansion (special_variable_name))
(simple_expansion (special_variable_name))))
=============================
Variable expansions
=============================
echo ${}
echo ${#}
echo ${var1#*#}
echo ${!abc}
echo ${abc}
echo ${abc:-def}
echo ${abc:- }
echo ${abc:
}
---
(program
(command (command_name (word)) (expansion))
(command (command_name (word)) (expansion))
(command (command_name (word)) (expansion (variable_name) (word)))
(command (command_name (word)) (expansion (variable_name)))
(command (command_name (word)) (expansion (variable_name)))
(command (command_name (word)) (expansion (variable_name) (word)))
(command (command_name (word)) (expansion (variable_name)))
(command (command_name (word)) (expansion (variable_name))))
===================================
Variable expansions with operators
===================================
A="${B[0]# }"
C="${D/#* -E /}"
F="${G%% *}"
---
(program
(variable_assignment
(variable_name)
(string (expansion (subscript (variable_name) (word)))))
(variable_assignment
(variable_name)
(string (expansion (variable_name) (regex))))
(variable_assignment
(variable_name)
(string (expansion (variable_name) (word) (word)))))
===================================
Variable expansions in strings
===================================
A="${A:-$B/c}"
A="${b=$c/$d}"
---
(program
(variable_assignment
(variable_name)
(string
(expansion
(variable_name)
(concatenation (simple_expansion (variable_name)) (word)))))
(variable_assignment
(variable_name)
(string
(expansion
(variable_name)
(concatenation
(simple_expansion (variable_name))
(word)
(simple_expansion (variable_name)))))))
===================================
Variable expansions with regexes
===================================
A=${B//:;;/$'\n'}
# escaped space
C=${D/;\ *;|}
---
(program
(variable_assignment (variable_name) (expansion (variable_name) (regex)))
(comment)
(variable_assignment (variable_name) (expansion (variable_name) (regex))))
===================================
Other variable expansion operators
===================================
cat ${BAR} ${ABC=def} ${GHI:?jkl}
[ "$a" != "${a#[Bc]}" ]
---
(program
(command
(command_name (word))
(expansion (variable_name))
(expansion (variable_name) (word))
(expansion (variable_name) (word)))
(test_command
(binary_expression
(string (simple_expansion (variable_name)))
(string (expansion (variable_name) (concatenation (word)))))))
=============================
Words ending with '$'
=============================
grep ^${var}$
---
(program (command
(command_name (word))
(concatenation (word) (expansion (variable_name)))))
=============================
Command substitutions
=============================
echo `echo hi`
echo `echo hi; echo there`
echo $(echo $(echo hi))
echo $(< some-file)
---
(program
(command
(command_name (word))
(command_substitution (command (command_name (word)) (word))))
(command
(command_name (word))
(command_substitution (command (command_name (word)) (word)) (command (command_name (word)) (word))))
(command
(command_name (word))
(command_substitution (command
(command_name (word))
(command_substitution (command
(command_name (word))
(word))))))
(command
(command_name (word))
(command_substitution (file_redirect (word)))))
=============================
Process substitutions
=============================
wc -c <(echo abc && echo def)
wc -c <(echo abc; echo def)
echo abc > >(wc -c)
---
(program
(command
(command_name (word))
(word)
(process_substitution (list
(command (command_name (word)) (word))
(command (command_name (word)) (word)))))
(command
(command_name (word))
(word)
(process_substitution
(command (command_name (word)) (word))
(command (command_name (word)) (word))))
(redirected_statement
(command
(command_name (word))
(word))
(file_redirect (process_substitution
(command (command_name (word)) (word))))))
=============================
Single quoted strings
=============================
echo 'a b' 'c d'
---
(program
(command (command_name (word)) (raw_string) (raw_string)))
=============================
Double quoted strings
=============================
echo "a" "b"
echo "a ${b} c" "d $e"
---
(program
(command (command_name (word))
(string)
(string))
(command (command_name (word))
(string (expansion (variable_name)))
(string (simple_expansion (variable_name)))))
=========================================
Strings containing command substitutions
=========================================
find "`dirname $file`" -name "$base"'*'
---
(program
(command
(command_name (word))
(string (command_substitution (command (command_name (word)) (simple_expansion (variable_name)))))
(word)
(concatenation
(string (simple_expansion (variable_name)))
(raw_string))))
=========================================
Strings containing escape sequence
=========================================
echo "\"The great escape\`\${var}"
---
(program (command (command_name (word)) (string)))
======================================
Strings containing special characters
======================================
echo "s/$/'/"
echo "#"
echo "s$"
---
(program
(command (command_name (word)) (string))
(command (command_name (word)) (string))
(command (command_name (word)) (string)))
========================================
Strings with ANSI-C quoting
========================================
echo $'Here\'s johnny!\r\n'
---
(program (command (command_name (word)) (ansii_c_string)))
=========================================
Arrays and array expansions
=========================================
a=()
b=(1 2 3)
echo ${a[@]}
echo ${#b[@]}
a[$i]=50
a+=(foo "bar" $(baz))
---
(program
(variable_assignment (variable_name) (array))
(variable_assignment (variable_name) (array (word) (word) (word)))
(command (command_name (word)) (expansion (subscript (variable_name) (word))))
(command (command_name (word)) (expansion (subscript (variable_name) (word))))
(variable_assignment
(subscript (variable_name) (simple_expansion (variable_name)))
(word))
(variable_assignment
(variable_name)
(array
(word)
(string)
(command_substitution (command (command_name (word)))))))
==============================
Escaped characters in strings
==============================
echo -ne "\033k$1\033\\" > /dev/stderr
---
(program
(redirected_statement
(command (command_name (word)) (word) (string (simple_expansion (variable_name))))
(file_redirect (word))))
================================================================================
Words containing bare '#'
================================================================================
curl -# localhost #comment without space
nix build nixpkgs#hello -v # comment with space
--------------------------------------------------------------------------------
(program
(command (command_name (word)) (word) (word))
(comment)
(command (command_name (word)) (word) (word) (word))
(comment))

@ -0,0 +1,96 @@
===============================
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)) (word) (word) (word)))
====================================
Files without trailing terminators
====================================
echo hi
---
(program (command (command_name (word)) (word)))

@ -0,0 +1,610 @@
===================================
Pipelines
===================================
whoami | cat
cat foo | grep -v bar
---
(program
(pipeline
(command
name: (command_name (word)))
(command
name: (command_name (word))))
(pipeline
(command
name: (command_name (word))
argument: (word))
(command
name: (command_name (word))
argument: (word)
argument: (word))))
===================================
Lists
===================================
a | b && c && d; d e f || e g
---
(program
(list
(list
(pipeline
(command (command_name (word)))
(command (command_name (word))))
(command (command_name (word))))
(command (command_name (word))))
(list
(command (command_name (word)) (word) (word))
(command (command_name (word)) (word))))
====================================
While statements
====================================
while something happens; do
echo a
echo b
done
---
(program
(while_statement
condition: (command
name: (command_name (word))
argument: (word))
body: (do_group
(command name: (command_name (word)) argument: (word))
(command name: (command_name (word)) argument: (word)))))
====================================
Until statements
====================================
until something happens; do
echo a
echo b
done
---
(program
(while_statement
condition: (command
name: (command_name (word))
argument: (word))
body: (do_group
(command name: (command_name (word)) argument: (word))
(command name: (command_name (word)) argument: (word)))))
====================================
While statements with IO redirects
====================================
while read line; do
echo $line
done < <(cat file)
---
(program
(redirected_statement
body: (while_statement
condition: (command
name: (command_name (word))
argument: (word))
body: (do_group
(command
name: (command_name (word))
argument: (simple_expansion (variable_name)))))
redirect: (file_redirect
destination: (process_substitution (command
name: (command_name (word))
argument: (word))))))
====================================
For statements
====================================
for a in 1 2 $(seq 5 10); do
echo $a
done
for ARG; do
echo $ARG
ARG=''
done
---
(program
(for_statement
variable: (variable_name)
value: (word)
value: (word)
value: (command_substitution (command
name: (command_name (word))
argument: (word)
argument: (word)))
body: (do_group
(command
name: (command_name (word))
argument: (simple_expansion (variable_name)))))
(for_statement
variable: (variable_name)
body: (do_group
(command
name: (command_name (word))
argument: (simple_expansion (variable_name)))
(variable_assignment
name: (variable_name)
value: (raw_string)))))
====================================
Select statements
====================================
select choice in X Y $(ls); do
echo $choice
break
done
select ARG; do
echo $ARG
ARG=''
done
---
(program
(for_statement
(variable_name)
(word)
(word)
(command_substitution (command (command_name (word))))
(do_group
(command
(command_name (word))
(simple_expansion (variable_name)))
(command (command_name (word)))))
(for_statement
(variable_name)
(do_group
(command
(command_name (word))
(simple_expansion (variable_name)))
(variable_assignment (variable_name) (raw_string)))))
====================================
C-style for statements
====================================
for (( c=1; c<=5; c++ ))
do
echo $c
done
for (( c=1; c<=5; c++ )) {
echo $c
}
for (( ; ; ))
do
echo 'forever'
done
---
(program
(c_style_for_statement
(word)
(binary_expression (word) (word))
(word)
(do_group
(command (command_name (word)) (simple_expansion (variable_name)))))
(c_style_for_statement
(word)
(binary_expression (word) (word))
(word)
(compound_statement
(command (command_name (word)) (simple_expansion (variable_name)))))
(c_style_for_statement
(do_group
(command (command_name (word)) (raw_string)))))
====================================
If statements
====================================
if cat some_file | grep -v ok; then
echo one
elif cat other_file | grep -v ok; then
echo two
else
exit
fi
---
(program
(if_statement
(pipeline
(command (command_name (word)) (word))
(command (command_name (word)) (word) (word)))
(command (command_name (word)) (word))
(elif_clause
(pipeline
(command (command_name (word)) (word))
(command (command_name (word)) (word) (word)))
(command (command_name (word)) (word)))
(else_clause
(command (command_name (word))))))
====================================
If statements with conditional expressions
====================================
if [ "$(uname)" == 'Darwin' ]; then
echo one
fi
---
(program
(if_statement
(test_command (binary_expression
(string (command_substitution (command (command_name (word)))))
(raw_string)))
(command (command_name (word)) (word))))
====================================
Case statements
====================================
case "opt" in
a)
echo a
;;
b)
echo b
;&
c)
echo c;;
esac
case "$Z" in
ab*|cd*) ef
esac
case $dest in
*.[1357])
exit $?
;;
esac
---
(program
(case_statement (string)
(case_item (word)
(command (command_name (word)) (word)))
(case_item (word)
(command (command_name (word)) (word)))
(case_item (word)
(command (command_name (word)) (word))))
(case_statement (string (simple_expansion (variable_name)))
(case_item (word) (word)
(command (command_name (word)))))
(case_statement (simple_expansion (variable_name))
(case_item (concatenation (word) (word))
(command (command_name (word)) (simple_expansion (special_variable_name))))))
=============================
Test commands
=============================
if [[ "$lsb_dist" != 'Ubuntu' || $(ver_to_int "$lsb_release") < $(ver_to_int '14.04') ]]; then
return 1
fi
---
(program
(if_statement
(test_command (binary_expression
(binary_expression
(binary_expression
(string (simple_expansion (variable_name)))
(raw_string))
(command_substitution (command
(command_name (word))
(string (simple_expansion (variable_name))))))
(command_substitution (command (command_name (word)) (raw_string)))))
(command (command_name (word)) (word))))
=============================
Test commands with ternary
=============================
if (( 1 < 2 ? 1 : 2 )); then
return 1
fi
---
(program
(if_statement
(test_command
(ternary_expression
(binary_expression (word) (word))
(word) (word)))
(command
(command_name (word)) (word))))
=============================
Test commands with regexes
=============================
[[ "35d8b" =~ ^[0-9a-fA-F] ]]
[[ $CMD =~ (^|;)update_terminal_cwd($|;) ]]
[[ ! " ${completions[*]} " =~ " $alias_cmd " ]]
! [[ "$a" =~ ^a|b\ *c|d$ ]]
[[ "$1" =~ ^${var}${var}*=..* ]]
[[ "$1" =~ ^\-${var}+ ]]
[[ ${var1} == *${var2}* ]]
[[ "$server" =~ [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} ]]
[[ "$primary_wins" =~ ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) ]]
---
(program
(test_command
(binary_expression
(string)
(regex)))
(test_command
(binary_expression
(simple_expansion (variable_name))
(regex)))
(test_command
(unary_expression
(binary_expression
(string (expansion (subscript (variable_name) (word))))
(string (simple_expansion (variable_name))))))
(negated_command
(test_command
(binary_expression
(string (simple_expansion (variable_name)))
(regex))))
(test_command
(binary_expression
(string (simple_expansion (variable_name)))
(regex)))
(test_command
(binary_expression
(string (simple_expansion (variable_name)))
(regex)))
(test_command
(binary_expression
(expansion (variable_name))
(regex)))
(test_command
(binary_expression
(string (simple_expansion (variable_name)))
(regex)))
(test_command
(binary_expression
(string (simple_expansion (variable_name)))
(regex))))
===============================
Subshells
===============================
(
./start-server --port=80
) &
---
(program
(subshell (command (command_name (word)) (word))))
===============================
Function definitions
===============================
do_something() {
echo ok
}
run_subshell_command() (
true
)
run_test_command() [[ -e foo ]]
function do_something_else() {
a | xargs -I{} find xml/{} -type f
}
function do_yet_another_thing {
echo ok
} 2>&1
---
(program
(function_definition
(word)
(compound_statement (command (command_name (word)) (word))))
(function_definition
(word)
(subshell (command (command_name (word)))))
(function_definition
(word)
(test_command (unary_expression (test_operator) (word))))
(function_definition
(word)
(compound_statement
(pipeline
(command (command_name (word)))
(command
(command_name (word))
(concatenation (word))
(word)
(concatenation (word))
(word)
(word)))))
(redirected_statement (function_definition
(word)
(compound_statement (command (command_name (word)) (word))))
(file_redirect (file_descriptor) (word))))
=========================================
Variable declaration: declare & typeset
=========================================
declare var1
typeset -i -r var2=42 var3=10
---
(program
(declaration_command (variable_name))
(declaration_command (word) (word)
(variable_assignment (variable_name) (word))
(variable_assignment (variable_name) (word))))
=========================================
Variable declaration: readonly
=========================================
readonly var1
readonly var2=42
---
(program
(declaration_command (variable_name))
(declaration_command (variable_assignment (variable_name) (word))))
=========================================
Variable declaration: local
=========================================
local a=42 b
local -r c
---
(program
(declaration_command
(variable_assignment (variable_name) (word))
(variable_name))
(declaration_command
(word)
(variable_name)))
=========================================
Variable declaration: export
=========================================
export PATH
export FOOBAR PATH="$PATH:/usr/foobar/bin"
---
(program
(declaration_command (variable_name))
(declaration_command
(variable_name)
(variable_assignment (variable_name) (string (simple_expansion (variable_name))))))
===========================================================
Variable declaration: command substitution with semi-colon
===========================================================
_path=$(
while statement; do
cd ".."
done;
echo $PWD
)
---
(program
(variable_assignment (variable_name)
(command_substitution
(while_statement
(command (command_name (word)))
(do_group (command (command_name (word)) (string))))
(command (command_name (word)) (simple_expansion (variable_name))))))
===========================================
Expressions passed to declaration commands
===========================================
export "$(echo ${key} | tr [:lower:] [:upper:])=${p_key#*=}"
---
(program
(declaration_command
(string
(command_substitution
(pipeline
(command (command_name (word)) (expansion (variable_name)))
(command (command_name (word)) (concatenation (word)) (concatenation (word)))))
(expansion (variable_name) (word)))))
=========================================
Unset commands
=========================================
unset A
unset "$variable_name"
unsetenv -f ONE TWO
---
(program
(unset_command (variable_name))
(unset_command (string (simple_expansion (variable_name))))
(unset_command (word) (variable_name) (variable_name)))
===========================================
Compound statements
===========================================
a () {
ls || { echo "b"; return 0; }
echo c
}
{ echo "a"
echo "b"
} >&2
---
(program
(function_definition (word) (compound_statement
(list
(command (command_name (word)))
(compound_statement
(command (command_name (word)) (string))
(command (command_name (word)) (word))))
(command (command_name (word)) (word))))
(redirected_statement
(compound_statement (command (command_name (word)) (string)) (command (command_name (word)) (string)))
(file_redirect (word))))

@ -0,0 +1,143 @@
#!/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

@ -0,0 +1 @@
Subproject commit c3d9c46ef832f808c929a22b36c8ef6dd45cb98a

@ -0,0 +1,165 @@
#!/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

@ -0,0 +1,119 @@
#!/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

@ -0,0 +1,270 @@
#!/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

@ -0,0 +1,36 @@
#!/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"

@ -0,0 +1,26 @@
#!/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

@ -0,0 +1,138 @@
#!/usr/bin/env bash
set -e
function usage {
cat <<-EOF
USAGE
$0 [-dgGhv] [-f focus-string] [-s seed]
OPTIONS
-h print this message
-b run make under scan-build static analyzer
-d run tests in a debugger (either lldb or gdb)
-g run tests with valgrind's memcheck tool
-G run tests with valgrind's memcheck tool, including a full leak check
-v run tests with verbose output
-f run only tests whose description contain the given string
-s set the seed used to control random behavior
-z pipe tests' stderr to \`dot(1)\` to render an SVG log
EOF
}
profile=
leak_check=no
mode=normal
verbose=
args=()
target=tests
export BUILDTYPE=Test
cmd="out/${BUILDTYPE}/${target}"
run_scan_build=
if [ "$(uname -s)" == "Darwin" ]; then
export LINK="clang++ -fsanitize=address"
fi
while getopts "bdf:s:gGhpvS" option; do
case ${option} in
h)
usage
exit
;;
d)
mode=debug
;;
g)
mode=valgrind
;;
G)
mode=valgrind
leak_check=full
;;
p)
profile=true
;;
f)
args+=("--only=${OPTARG}")
;;
v)
verbose=true
;;
s)
export TREE_SITTER_SEED=${OPTARG}
;;
S)
mode=SVG
;;
b)
run_scan_build=true
;;
esac
done
if [[ -n $verbose ]]; then
args+=("--reporter=spec")
else
args+=("--reporter=singleline")
fi
if [[ -n "$run_scan_build" ]]; then
. script/util/scan-build.sh
scan_build make -j2 $target
else
make -j2 $target
fi
args=${args:-""}
if [[ -n $profile ]]; then
export CPUPROFILE=/tmp/${target}-$(date '+%s').prof
fi
case ${mode} in
valgrind)
valgrind \
--suppressions=./script/util/valgrind.supp \
--dsymutil=yes \
--leak-check=${leak_check} \
$cmd "${args[@]}" 2>&1 | \
grep --color -E '\w+_tests?.cc:\d+|$'
;;
debug)
if which -s lldb; then
lldb $cmd -- "${args[@]}"
elif which -s gdb; then
gdb $cmd -- "${args[@]}"
else
echo "No debugger found"
exit 1
fi
;;
SVG)
echo "<!DOCTYPE html><style>svg { width: 100%; margin-bottom: 20px; }</style>" > index.html
$cmd "${args[@]}" 2> >(grep -v 'Assertion failed' | dot -Tsvg >> index.html)
echo "Wrote index.html"
;;
normal)
time $cmd "${args[@]}"
;;
esac
if [[ -n $profile ]]; then
pprof $cmd $CPUPROFILE
fi

@ -0,0 +1,9 @@
#!/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

@ -0,0 +1,559 @@
const SPECIAL_CHARACTERS = [
"'", '"',
'<', '>',
'{', '}',
'\\[', '\\]',
'(', ')',
'`', '$',
'|', '&', ';',
'\\',
'\\s',
];
module.exports = grammar({
name: 'bash',
inline: $ => [
$._statement,
$._terminator,
$._literal,
$._statements2,
$._primary_expression,
$._simple_variable_name,
$._special_variable_name,
],
externals: $ => [
$.heredoc_start,
$._simple_heredoc_body,
$._heredoc_body_beginning,
$._heredoc_body_middle,
$._heredoc_body_end,
$.file_descriptor,
$._empty_value,
$._concat,
$.variable_name, // Variable name followed by an operator like '=' or '+='
$.regex,
'}',
']',
'<<',
'<<-',
'\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,
optional(seq('\n', $.heredoc_body)),
$._terminator
)),
$._statement,
optional(seq('\n', $.heredoc_body)),
optional($._terminator)
)),
_statements2: $ => repeat1(seq(
$._statement,
optional(seq('\n', $.heredoc_body)),
$._terminator
)),
_terminated_statement: $ => seq(
$._statement,
$._terminator
),
// Statements
_statement: $ => choice(
$.redirected_statement,
$.variable_assignment,
$.command,
$.declaration_command,
$.unset_command,
$.test_command,
$.negated_command,
$.for_statement,
$.c_style_for_statement,
$.while_statement,
$.if_statement,
$.case_statement,
$.pipeline,
$.list,
$.subshell,
$.compound_statement,
$.function_definition
),
redirected_statement: $ => prec(-1, seq(
field('body', $._statement),
field('redirect', repeat1(choice(
$.file_redirect,
$.heredoc_redirect,
$.herestring_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',
'((',
field('initializer', optional($._expression)),
$._terminator,
field('condition', optional($._expression)),
$._terminator,
field('update', optional($._expression)),
'))',
optional(';'),
field('body', choice(
$.do_group,
$.compound_statement
))
),
while_statement: $ => seq(
choice('while', 'until'),
field('condition', $._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',
$._terminator,
optional(seq(
repeat($.case_item),
alias($.last_case_item, $.case_item),
)),
'esac'
),
case_item: $ => seq(
field('value', $._literal),
repeat(seq('|', field('value', $._literal))),
')',
optional($._statements),
prec(1, choice(
field('termination', ';;'),
field('fallthrough', choice(';&', ';;&'))
))
),
last_case_item: $ => seq(
field('value', $._literal),
repeat(seq('|', field('value', $._literal))),
')',
optional($._statements),
optional(prec(1, ';;'))
),
function_definition: $ => seq(
choice(
seq(
'function',
field('name', $.word),
optional(seq('(', ')'))
),
seq(
field('name', $.word),
'(', ')'
)
),
field(
'body',
choice(
$.compound_statement,
$.subshell,
$.test_command)
)
),
compound_statement: $ => seq(
'{',
optional($._statements2),
'}'
),
subshell: $ => seq(
'(',
$._statements,
')'
),
pipeline: $ => prec.left(1, seq(
$._statement,
choice('|', '|&'),
$._statement
)),
list: $ => prec.left(-1, seq(
$._statement,
choice('&&', '||'),
$._statement
)),
// Commands
negated_command: $ => seq(
'!',
choice(
$.command,
$.test_command,
$.subshell
)
),
test_command: $ => seq(
choice(
seq('[', $._expression, ']'),
seq('[[', $._expression, ']]'),
seq('((', $._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,
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
))
),
subscript: $ => seq(
field('name', $.variable_name),
'[',
field('index', $._literal),
optional($._concat),
']',
optional($._concat)
),
file_redirect: $ => prec.left(seq(
field('descriptor', optional($.file_descriptor)),
choice('<', '>', '>>', '&>', '&>>', '<&', '>&', '>|'),
field('destination', $._literal)
)),
heredoc_redirect: $ => seq(
choice('<<', '<<-'),
$.heredoc_start
),
heredoc_body: $ => choice(
$._simple_heredoc_body,
seq(
$._heredoc_body_beginning,
repeat(choice(
$.expansion,
$.simple_expansion,
$.command_substitution,
$._heredoc_body_middle
)),
$._heredoc_body_end
)
),
herestring_redirect: $ => seq(
'<<<',
$._literal
),
// Expressions
_expression: $ => choice(
$._literal,
$.unary_expression,
$.ternary_expression,
$.binary_expression,
$.postfix_expression,
$.parenthesized_expression
),
binary_expression: $ => prec.left(choice(
seq(
field('left', $._expression),
field('operator', choice(
'=', '==', '=~', '!=',
'+', '-', '+=', '-=',
'<', '>', '<=', '>=',
'||', '&&',
$.test_operator
)),
field('right', $._expression)
),
seq(
field('left', $._expression),
field('operator', choice('==', '=~')),
field('right', $.regex)
)
)),
ternary_expression: $ => prec.left(
seq(
field('condition', $._expression),
'?',
field('consequence', $._expression),
':',
field('alternative', $._expression),
)
),
unary_expression: $ => prec.right(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,
$.string,
$.raw_string,
$.ansii_c_string,
$.expansion,
$.simple_expansion,
$.string_expansion,
$.command_substitution,
$.process_substitution
),
concatenation: $ => prec(-1, seq(
choice(
$._primary_expression,
$._special_character,
),
repeat1(prec(-1, seq(
$._concat,
choice(
$._primary_expression,
$._special_character,
)
))),
optional(seq($._concat, '$'))
)),
_special_character: $ => token(prec(-1, choice('{', '}', '[', ']'))),
string: $ => seq(
'"',
repeat(seq(
choice(
seq(optional('$'), $._string_content),
$.expansion,
$.simple_expansion,
$.command_substitution
),
optional($._concat)
)),
optional('$'),
'"'
),
_string_content: $ => token(prec(-1, /([^"`$\\]|\\(.|\r?\n))+/)),
array: $ => seq(
'(',
repeat($._literal),
')'
),
raw_string: $ => /'[^']*'/,
ansii_c_string: $ => /\$'([^']|\\')*'/,
simple_expansion: $ => seq(
'$',
choice(
$._simple_variable_name,
$._special_variable_name,
alias('!', $.special_variable_name),
alias('#', $.special_variable_name)
)
),
string_expansion: $ => seq('$', choice($.string, $.raw_string)),
expansion: $ => seq(
'${',
optional(choice('#', '!')),
optional(choice(
seq(
$.variable_name,
'=',
optional($._literal)
),
seq(
choice(
$.subscript,
$._simple_variable_name,
$._special_variable_name
),
optional(seq(
token(prec(1, '/')),
optional($.regex)
)),
repeat(choice(
$._literal,
':', ':?', '=', ':-', '%', '-', '#'
))
),
)),
'}'
),
command_substitution: $ => choice(
seq('$(', $._statements, ')'),
seq('$(', $.file_redirect, ')'),
prec(1, seq('`', $._statements, '`'))
),
process_substitution: $ => seq(
choice('<(', '>('),
$._statements,
')'
),
comment: $ => token(prec(-10, /#.*/)),
_simple_variable_name: $ => alias(/\w+/, $.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]+/))),
_terminator: $ => choice(';', ';;', '\n', '&')
}
});
function noneOf(...characters) {
const negatedString = characters.map(c => c == '\\' ? '\\\\' : c).join('')
return new RegExp('[^' + negatedString + ']')
}

@ -0,0 +1,37 @@
{
"name": "tree-sitter-bash",
"version": "0.19.0",
"description": "Bash grammar for tree-sitter",
"main": "bindings/node",
"keywords": [
"parser",
"lexer"
],
"author": "Max Brunsfeld",
"license": "MIT",
"dependencies": {
"nan": "^2.14.0",
"prebuild-install": "^5.3.3"
},
"devDependencies": {
"prebuild": "^10.0.0",
"tree-sitter-cli": "^0.19.1"
},
"scripts": {
"install": "prebuild-install || node-gyp rebuild",
"pre-build": "prebuild --all --strip --verbose",
"pre-build:upload": "prebuild --upload-all",
"test": "tree-sitter test && script/parse-examples.sh",
"test-windows": "tree-sitter test"
},
"repository": "https://github.com/tree-sitter/tree-sitter-bash",
"tree-sitter": [
{
"scope": "source.bash",
"file-types": [
"sh",
"bash"
]
}
]
}

@ -0,0 +1,54 @@
[
(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"
"unset"
"while"
"then"
] @keyword
(comment) @comment
(function_definition name: (word) @function)
(file_descriptor) @number
[
(command_substitution)
(process_substitution)
(expansion)
]@embedded
[
"$"
"&&"
">"
">>"
"<"
"|"
] @operator
(
(command (_) @constant)
(#match? @constant "^-")
)

@ -0,0 +1,15 @@
examples/bash-it/completion/available/docker-compose.completion.bash
examples/bash-it/completion/available/docker-machine.completion.bash
examples/bash-it/completion/available/docker.completion.bash
examples/bash-it/completion/available/drush.completion.bash
examples/bash-it/completion/available/git.completion.bash
examples/bash-it/completion/available/svn.completion.bash
examples/bash-it/completion/available/tmux.completion.bash
examples/bash-it/plugins/available/extract.plugin.bash
examples/bash-it/plugins/available/git.plugin.bash
examples/bash-it/plugins/available/go.plugin.bash
examples/bash-it/themes/colors.theme.bash
examples/bash-it/themes/doubletime/doubletime.theme.bash
examples/bash-it/themes/hawaii50/hawaii50.theme.bash
examples/bash-it/themes/nwinkler_random_colors/nwinkler_random_colors.theme.bash
examples/bash-it/themes/powerline/powerline.base.bash

@ -0,0 +1,8 @@
#!/bin/bash
known_failures="$(cat script/known-failures.txt)"
tree-sitter parse -q -t \
examples/**/*.bash \
examples/**/*.sh \
$(for failure in $known_failures; do echo "!${failure}"; done)

@ -0,0 +1,7 @@
#!/bin/bash
tree-sitter parse -q -t \
examples/**/*.sh \
examples/**/*.bash \
| 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

@ -0,0 +1,398 @@
#include <tree_sitter/parser.h>
#include <string>
#include <cwctype>
namespace {
using std::string;
enum TokenType {
HEREDOC_START,
SIMPLE_HEREDOC_BODY,
HEREDOC_BODY_BEGINNING,
HEREDOC_BODY_MIDDLE,
HEREDOC_BODY_END,
FILE_DESCRIPTOR,
EMPTY_VALUE,
CONCAT,
VARIABLE_NAME,
REGEX,
CLOSING_BRACE,
CLOSING_BRACKET,
HEREDOC_ARROW,
HEREDOC_ARROW_DASH,
NEWLINE,
};
struct Scanner {
void skip(TSLexer *lexer) {
lexer->advance(lexer, true);
}
void advance(TSLexer *lexer) {
lexer->advance(lexer, false);
}
unsigned serialize(char *buffer) {
if (heredoc_delimiter.length() + 3 >= TREE_SITTER_SERIALIZATION_BUFFER_SIZE) return 0;
buffer[0] = heredoc_is_raw;
buffer[1] = started_heredoc;
buffer[2] = heredoc_allows_indent;
heredoc_delimiter.copy(&buffer[3], heredoc_delimiter.length());
return heredoc_delimiter.length() + 3;
}
void deserialize(const char *buffer, unsigned length) {
if (length == 0) {
heredoc_is_raw = false;
started_heredoc = false;
heredoc_allows_indent = false;
heredoc_delimiter.clear();
} else {
heredoc_is_raw = buffer[0];
started_heredoc = buffer[1];
heredoc_allows_indent = buffer[2];
heredoc_delimiter.assign(&buffer[3], &buffer[length]);
}
}
bool scan_heredoc_start(TSLexer *lexer) {
while (iswspace(lexer->lookahead)) skip(lexer);
lexer->result_symbol = HEREDOC_START;
heredoc_is_raw = lexer->lookahead == '\'';
started_heredoc = false;
heredoc_delimiter.clear();
if (lexer->lookahead == '\\') {
advance(lexer);
}
int32_t quote = 0;
if (heredoc_is_raw || lexer->lookahead == '"') {
quote = lexer->lookahead;
advance(lexer);
}
while (iswalpha(lexer->lookahead) || (quote != 0 && iswspace(lexer->lookahead))) {
heredoc_delimiter += lexer->lookahead;
advance(lexer);
}
if (lexer->lookahead == quote) {
advance(lexer);
}
return !heredoc_delimiter.empty();
}
bool scan_heredoc_end_identifier(TSLexer *lexer) {
current_leading_word.clear();
// Scan the first 'n' characters on this line, to see if they match the heredoc delimiter
while (
lexer->lookahead != '\0' &&
lexer->lookahead != '\n' &&
current_leading_word.length() < heredoc_delimiter.length()
) {
current_leading_word += lexer->lookahead;
advance(lexer);
}
return current_leading_word == heredoc_delimiter;
}
bool scan_heredoc_content(TSLexer *lexer, TokenType middle_type, TokenType end_type) {
bool did_advance = false;
for (;;) {
switch (lexer->lookahead) {
case '\0': {
if (did_advance) {
heredoc_is_raw = false;
started_heredoc = false;
heredoc_allows_indent = false;
heredoc_delimiter.clear();
lexer->result_symbol = end_type;
return true;
} else {
return false;
}
}
case '\\': {
did_advance = true;
advance(lexer);
advance(lexer);
break;
}
case '$': {
if (heredoc_is_raw) {
did_advance = true;
advance(lexer);
break;
} else if (did_advance) {
lexer->result_symbol = middle_type;
started_heredoc = true;
return true;
} else {
return false;
}
}
case '\n': {
did_advance = true;
advance(lexer);
if (heredoc_allows_indent) {
while (iswspace(lexer->lookahead)) {
advance(lexer);
}
}
if (scan_heredoc_end_identifier(lexer)) {
heredoc_is_raw = false;
started_heredoc = false;
heredoc_allows_indent = false;
heredoc_delimiter.clear();
lexer->result_symbol = end_type;
return true;
}
break;
}
default: {
did_advance = true;
advance(lexer);
break;
}
}
}
}
bool scan(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 == '`' ||
lexer->lookahead == '#' ||
(lexer->lookahead == '}' && valid_symbols[CLOSING_BRACE]) ||
(lexer->lookahead == ']' && valid_symbols[CLOSING_BRACKET])
)) {
lexer->result_symbol = CONCAT;
return true;
}
}
if (valid_symbols[EMPTY_VALUE]) {
if (iswspace(lexer->lookahead)) {
lexer->result_symbol = EMPTY_VALUE;
return true;
}
}
if (valid_symbols[HEREDOC_BODY_BEGINNING] && !heredoc_delimiter.empty() && !started_heredoc) {
return scan_heredoc_content(lexer, HEREDOC_BODY_BEGINNING, SIMPLE_HEREDOC_BODY);
}
if (valid_symbols[HEREDOC_BODY_MIDDLE] && !heredoc_delimiter.empty() && started_heredoc) {
return scan_heredoc_content(lexer, HEREDOC_BODY_MIDDLE, HEREDOC_BODY_END);
}
if (valid_symbols[HEREDOC_START]) {
return scan_heredoc_start(lexer);
}
if (valid_symbols[VARIABLE_NAME] || valid_symbols[FILE_DESCRIPTOR] || valid_symbols[HEREDOC_ARROW]) {
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;
}
}
if (valid_symbols[HEREDOC_ARROW] && lexer->lookahead == '<') {
advance(lexer);
if (lexer->lookahead == '<') {
advance(lexer);
if (lexer->lookahead == '-') {
advance(lexer);
heredoc_allows_indent = true;
lexer->result_symbol = HEREDOC_ARROW_DASH;
} else if (lexer->lookahead == '<') {
return false;
} else {
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 {
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->result_symbol = VARIABLE_NAME;
return true;
} else {
return false;
}
} else if (lexer->lookahead == '=' || lexer->lookahead == '[') {
lexer->result_symbol = VARIABLE_NAME;
return true;
}
}
return false;
}
if (valid_symbols[REGEX]) {
while (iswspace(lexer->lookahead)) skip(lexer);
if (
lexer->lookahead != '"' &&
lexer->lookahead != '\'' &&
lexer->lookahead != '$'
) {
struct State {
bool done;
uint32_t paren_depth;
uint32_t bracket_depth;
uint32_t brace_depth;
};
lexer->mark_end(lexer);
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 = REGEX;
return true;
}
}
return false;
}
string heredoc_delimiter;
bool heredoc_is_raw;
bool started_heredoc;
bool heredoc_allows_indent;
string current_leading_word;
};
}
extern "C" {
void *tree_sitter_bash_external_scanner_create() {
return new Scanner();
}
bool tree_sitter_bash_external_scanner_scan(void *payload, TSLexer *lexer,
const bool *valid_symbols) {
Scanner *scanner = static_cast<Scanner *>(payload);
return scanner->scan(lexer, valid_symbols);
}
unsigned tree_sitter_bash_external_scanner_serialize(void *payload, char *state) {
Scanner *scanner = static_cast<Scanner *>(payload);
return scanner->serialize(state);
}
void tree_sitter_bash_external_scanner_deserialize(void *payload, const char *state, unsigned length) {
Scanner *scanner = static_cast<Scanner *>(payload);
scanner->deserialize(state, length);
}
void tree_sitter_bash_external_scanner_destroy(void *payload) {
Scanner *scanner = static_cast<Scanner *>(payload);
delete scanner;
}
}

@ -0,0 +1,223 @@
#ifndef TREE_SITTER_PARSER_H_
#define TREE_SITTER_PARSER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#define ts_builtin_sym_error ((TSSymbol)-1)
#define ts_builtin_sym_end 0
#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
typedef uint16_t TSStateId;
#ifndef TREE_SITTER_API_H_
typedef uint16_t TSSymbol;
typedef uint16_t TSFieldId;
typedef struct TSLanguage TSLanguage;
#endif
typedef struct {
TSFieldId field_id;
uint8_t child_index;
bool inherited;
} TSFieldMapEntry;
typedef struct {
uint16_t index;
uint16_t length;
} TSFieldMapSlice;
typedef struct {
bool visible;
bool named;
bool supertype;
} TSSymbolMetadata;
typedef struct TSLexer TSLexer;
struct TSLexer {
int32_t lookahead;
TSSymbol result_symbol;
void (*advance)(TSLexer *, bool);
void (*mark_end)(TSLexer *);
uint32_t (*get_column)(TSLexer *);
bool (*is_at_included_range_start)(const TSLexer *);
bool (*eof)(const TSLexer *);
};
typedef enum {
TSParseActionTypeShift,
TSParseActionTypeReduce,
TSParseActionTypeAccept,
TSParseActionTypeRecover,
} TSParseActionType;
typedef union {
struct {
uint8_t type;
TSStateId state;
bool extra;
bool repetition;
} shift;
struct {
uint8_t type;
uint8_t child_count;
TSSymbol symbol;
int16_t dynamic_precedence;
uint16_t production_id;
} reduce;
uint8_t type;
} TSParseAction;
typedef struct {
uint16_t lex_state;
uint16_t external_lex_state;
} TSLexMode;
typedef union {
TSParseAction action;
struct {
uint8_t count;
bool reusable;
} entry;
} TSParseActionEntry;
struct TSLanguage {
uint32_t version;
uint32_t symbol_count;
uint32_t alias_count;
uint32_t token_count;
uint32_t external_token_count;
uint32_t state_count;
uint32_t large_state_count;
uint32_t production_id_count;
uint32_t field_count;
uint16_t max_alias_sequence_length;
const uint16_t *parse_table;
const uint16_t *small_parse_table;
const uint32_t *small_parse_table_map;
const TSParseActionEntry *parse_actions;
const char * const *symbol_names;
const char * const *field_names;
const TSFieldMapSlice *field_map_slices;
const TSFieldMapEntry *field_map_entries;
const TSSymbolMetadata *symbol_metadata;
const TSSymbol *public_symbol_map;
const uint16_t *alias_map;
const TSSymbol *alias_sequences;
const TSLexMode *lex_modes;
bool (*lex_fn)(TSLexer *, TSStateId);
bool (*keyword_lex_fn)(TSLexer *, TSStateId);
TSSymbol keyword_capture_token;
struct {
const bool *states;
const TSSymbol *symbol_map;
void *(*create)(void);
void (*destroy)(void *);
bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist);
unsigned (*serialize)(void *, char *);
void (*deserialize)(void *, const char *, unsigned);
} external_scanner;
};
/*
* Lexer Macros
*/
#define START_LEXER() \
bool result = false; \
bool skip = false; \
bool eof = false; \
int32_t lookahead; \
goto start; \
next_state: \
lexer->advance(lexer, skip); \
start: \
skip = false; \
lookahead = lexer->lookahead;
#define ADVANCE(state_value) \
{ \
state = state_value; \
goto next_state; \
}
#define SKIP(state_value) \
{ \
skip = true; \
state = state_value; \
goto next_state; \
}
#define ACCEPT_TOKEN(symbol_value) \
result = true; \
lexer->result_symbol = symbol_value; \
lexer->mark_end(lexer);
#define END_STATE() return result;
/*
* Parse Table Macros
*/
#define SMALL_STATE(id) id - LARGE_STATE_COUNT
#define STATE(id) id
#define ACTIONS(id) id
#define SHIFT(state_value) \
{{ \
.shift = { \
.type = TSParseActionTypeShift, \
.state = state_value \
} \
}}
#define SHIFT_REPEAT(state_value) \
{{ \
.shift = { \
.type = TSParseActionTypeShift, \
.state = state_value, \
.repetition = true \
} \
}}
#define SHIFT_EXTRA() \
{{ \
.shift = { \
.type = TSParseActionTypeShift, \
.extra = true \
} \
}}
#define REDUCE(symbol_val, child_count_val, ...) \
{{ \
.reduce = { \
.type = TSParseActionTypeReduce, \
.symbol = symbol_val, \
.child_count = child_count_val, \
__VA_ARGS__ \
}, \
}}
#define RECOVER() \
{{ \
.type = TSParseActionTypeRecover \
}}
#define ACCEPT_INPUT() \
{{ \
.type = TSParseActionTypeAccept \
}}
#ifdef __cplusplus
}
#endif
#endif // TREE_SITTER_PARSER_H_