tabry 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/tabry/shells/bash.rb +12 -5
- data/sh/bash/README.md +2 -1
- data/tabry.gemspec +19 -2
- data/treesitter/Cargo.toml +26 -0
- data/treesitter/README.md +4 -0
- data/treesitter/binding.gyp +19 -0
- data/treesitter/bindings/node/binding.cc +28 -0
- data/treesitter/bindings/node/index.js +19 -0
- data/treesitter/bindings/rust/build.rs +40 -0
- data/treesitter/bindings/rust/lib.rs +52 -0
- data/treesitter/corpus/arg.txt +96 -0
- data/treesitter/corpus/at.txt +79 -0
- data/treesitter/corpus/comment.txt +13 -0
- data/treesitter/corpus/desc.txt +25 -0
- data/treesitter/corpus/examples_from_language_reference.txt +410 -0
- data/treesitter/corpus/flag.txt +48 -0
- data/treesitter/corpus/flag_desc_inline.txt +37 -0
- data/treesitter/corpus/opts.txt +21 -0
- data/treesitter/corpus/rapture.txt +61 -0
- data/treesitter/grammar.js +171 -0
- data/treesitter/jest_fixtures/examples_from_language_reference/argument_titles.yml +8 -0
- data/treesitter/jest_fixtures/examples_from_language_reference/arguments_and_possible_options__arg_.yml +23 -0
- data/treesitter/jest_fixtures/examples_from_language_reference/flags__flag__flagarg__reqd_flagarg_.yml +37 -0
- data/treesitter/jest_fixtures/examples_from_language_reference/getting_started.yml +13 -0
- data/treesitter/jest_fixtures/examples_from_language_reference/includes.yml +57 -0
- data/treesitter/jest_fixtures/examples_from_language_reference/multi_line_descriptions.yml +7 -0
- data/treesitter/jest_fixtures/examples_from_language_reference/optional_args_and_varargs__opt_arg__varargs__opt_varargs_.yml +16 -0
- data/treesitter/jest_fixtures/examples_from_language_reference/options.yml +24 -0
- data/treesitter/jest_fixtures/examples_from_language_reference/subcommands__sub__1.yml +23 -0
- data/treesitter/jest_fixtures/examples_from_language_reference/subcommands__sub__2.yml +15 -0
- data/treesitter/package.json +21 -0
- data/treesitter/parser_compile.sh +1 -0
- data/treesitter/src/grammar.json +615 -0
- data/treesitter/src/node-types.json +563 -0
- data/treesitter/src/parser.c +4706 -0
- data/treesitter/src/tree_sitter/parser.h +223 -0
- data/treesitter/tabry-compile.js +394 -0
- data/treesitter/tabry-compile.test.js +51 -0
- metadata +36 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 853b43dede9ba1a93e3581eefd4dbe883ec0cbef4cf493776183df0d363fed3e
|
4
|
+
data.tar.gz: f8bb31ead64b56da0f0bbabe051219fca8e40ade773b730b4295d990b5fe03b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 463f392cad04097474a34abd6bf78d9bf3dfb88a7af8867f3879bcce514ec07c5d8d9acddcb01e8cb6dbb286545085ccd72fdaeb2686d7e1f738eae2405752b4
|
7
|
+
data.tar.gz: 1f877f562de0e3b3d08421b087cc66d00efc2875807da22b88fec70068aaa55b512840eb12e3414a9813e6932017a7737cb5ad5e91df19024627095536cb1275
|
data/lib/tabry/shells/bash.rb
CHANGED
@@ -1,17 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Used to generate a tab-completion function for a Tabry CLI using absolute
|
4
|
+
# paths to the tabry-bash script in this repo and to the Tabry JSON/YML file.
|
5
|
+
# Using uniquely-named tab-completion functions and absolute paths means you can
|
6
|
+
# have different Tabry-based CLIs using different versions of Tabry without any
|
7
|
+
# conflicts.
|
8
|
+
# See sh/bash/README.md and "Adding Tab Completion to your CLI" in main README
|
9
|
+
|
1
10
|
module Tabry
|
2
11
|
module Shells
|
3
12
|
module Bash
|
4
13
|
# NOTE! This code uses sh/bash/tabry_bash_core.sh and is described in
|
5
14
|
# sh/bash/README.md; see that README for more info
|
6
15
|
def self.generate(cmd_name, tabry_file_path)
|
7
|
-
capitalized_cmd_name = cmd_name.upcase.gsub(/[^a-zA-Z0-9_]/,
|
16
|
+
capitalized_cmd_name = cmd_name.upcase.gsub(/[^a-zA-Z0-9_]/, "_")
|
8
17
|
tabry_file = Shellwords.escape(File.expand_path(tabry_file_path))
|
9
18
|
path_to_tabry = Shellwords.escape(File.expand_path("#{__dir__}/../../../"))
|
10
19
|
|
11
20
|
core = File.read("#{__dir__}/../../../sh/bash/tabry_bash_core.sh")
|
12
21
|
core.gsub! "_tabry_completions_internal()", "_tabry_#{capitalized_cmd_name}_completions_internal()"
|
13
22
|
|
14
|
-
|
23
|
+
<<~END_BASH_CODE_TEMPLATE + core
|
15
24
|
# The following Autocomplete is for a Tabry-powered command. It was
|
16
25
|
# generated by the command itself. See the documentation located in
|
17
26
|
# #{path_to_tabry}/sh/bash/README.md
|
@@ -19,10 +28,8 @@ module Tabry
|
|
19
28
|
TABRY_IMPORTS_PATH=#{tabry_file} _tabry_#{capitalized_cmd_name}_completions_internal #{path_to_tabry}/bin/tabry-bash
|
20
29
|
}
|
21
30
|
complete -F _tabry_#{capitalized_cmd_name}_completions #{Shellwords.escape cmd_name}
|
22
|
-
|
31
|
+
END_BASH_CODE_TEMPLATE
|
23
32
|
end
|
24
33
|
end
|
25
34
|
end
|
26
35
|
end
|
27
|
-
|
28
|
-
|
data/sh/bash/README.md
CHANGED
@@ -23,11 +23,12 @@ This file and bash function contained within is used in two ways:
|
|
23
23
|
added on, to make tab completion specific for the program. This is generated
|
24
24
|
for each command when the user runs "mycommand completion bash", which calls
|
25
25
|
`Tabry::Shells::Bash.generate`. A new function for each CLI so different CLIs
|
26
|
-
can use different versions of
|
26
|
+
can use different versions of tabry without interfering with each other.
|
27
27
|
The name `_tabry_completions_internal_` is replaced in
|
28
28
|
`lib/tabry/shells/bash.rb` in the `generate` method, so if modifying
|
29
29
|
`tabry_bash_core.sh`, you should if make sure the substitution still works
|
30
30
|
properly.
|
31
|
+
See also "Adding Tab Completion to your CLI" in the main README.
|
31
32
|
* You can also use Tabry to add tab completion to other non-Tabry CLIs. In this
|
32
33
|
case, the `_tabry_completions_internal_` function is used as-is; it is
|
33
34
|
sourced from `tabry_bash.sh`. See `tabry_bash.sh` for details and
|
data/tabry.gemspec
CHANGED
@@ -6,14 +6,31 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "tabry"
|
9
|
-
s.version = "0.1.
|
9
|
+
s.version = "0.1.2"
|
10
10
|
s.summary = "Tab completion and CLIs extraordinaire"
|
11
11
|
s.authors = ["Evan Battaglia"]
|
12
12
|
s.email = "battaglia.evan@gmail.com"
|
13
13
|
s.homepage = "https://github.com/evanbattaglia/tabry"
|
14
14
|
s.license = "MIT"
|
15
15
|
|
16
|
-
|
16
|
+
treesitter_files = %w[
|
17
|
+
binding.gyp
|
18
|
+
bindings/**/*
|
19
|
+
Cargo.toml
|
20
|
+
corpus/**/*
|
21
|
+
grammar.js
|
22
|
+
jest_fixtures/**/*
|
23
|
+
package.json
|
24
|
+
parser_compile.sh
|
25
|
+
README.md
|
26
|
+
src/**/*
|
27
|
+
tabry-compile.js
|
28
|
+
tabry-compile.test.js
|
29
|
+
]
|
30
|
+
|
31
|
+
s.files = Dir.glob("{lib,bin,sh,spec}/**/*") + %w[tabry.gemspec] +
|
32
|
+
treesitter_files.map { |path| Dir.glob("treesitter/#{path}") }
|
33
|
+
|
17
34
|
# TODO: more cargo cult possibly:
|
18
35
|
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
36
|
s.require_paths = ["lib"]
|
@@ -0,0 +1,26 @@
|
|
1
|
+
[package]
|
2
|
+
name = "tree-sitter-tabry"
|
3
|
+
description = "tabry grammar for the tree-sitter parsing library"
|
4
|
+
version = "0.0.1"
|
5
|
+
keywords = ["incremental", "parsing", "tabry"]
|
6
|
+
categories = ["parsing", "text-editors"]
|
7
|
+
repository = "https://github.com/tree-sitter/tree-sitter-tabry"
|
8
|
+
edition = "2018"
|
9
|
+
license = "MIT"
|
10
|
+
|
11
|
+
build = "bindings/rust/build.rs"
|
12
|
+
include = [
|
13
|
+
"bindings/rust/*",
|
14
|
+
"grammar.js",
|
15
|
+
"queries/*",
|
16
|
+
"src/*",
|
17
|
+
]
|
18
|
+
|
19
|
+
[lib]
|
20
|
+
path = "bindings/rust/lib.rs"
|
21
|
+
|
22
|
+
[dependencies]
|
23
|
+
tree-sitter = "~0.20.0"
|
24
|
+
|
25
|
+
[build-dependencies]
|
26
|
+
cc = "1.0"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
{
|
2
|
+
"targets": [
|
3
|
+
{
|
4
|
+
"target_name": "tree_sitter_tabry_binding",
|
5
|
+
"include_dirs": [
|
6
|
+
"<!(node -e \"require('nan')\")",
|
7
|
+
"src"
|
8
|
+
],
|
9
|
+
"sources": [
|
10
|
+
"bindings/node/binding.cc",
|
11
|
+
"src/parser.c",
|
12
|
+
# If your language uses an external scanner, add it here.
|
13
|
+
],
|
14
|
+
"cflags_c": [
|
15
|
+
"-std=c99",
|
16
|
+
]
|
17
|
+
}
|
18
|
+
]
|
19
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#include "tree_sitter/parser.h"
|
2
|
+
#include <node.h>
|
3
|
+
#include "nan.h"
|
4
|
+
|
5
|
+
using namespace v8;
|
6
|
+
|
7
|
+
extern "C" TSLanguage * tree_sitter_tabry();
|
8
|
+
|
9
|
+
namespace {
|
10
|
+
|
11
|
+
NAN_METHOD(New) {}
|
12
|
+
|
13
|
+
void Init(Local<Object> exports, Local<Object> module) {
|
14
|
+
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
|
15
|
+
tpl->SetClassName(Nan::New("Language").ToLocalChecked());
|
16
|
+
tpl->InstanceTemplate()->SetInternalFieldCount(1);
|
17
|
+
|
18
|
+
Local<Function> constructor = Nan::GetFunction(tpl).ToLocalChecked();
|
19
|
+
Local<Object> instance = constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked();
|
20
|
+
Nan::SetInternalFieldPointer(instance, 0, tree_sitter_tabry());
|
21
|
+
|
22
|
+
Nan::Set(instance, Nan::New("name").ToLocalChecked(), Nan::New("tabry").ToLocalChecked());
|
23
|
+
Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance);
|
24
|
+
}
|
25
|
+
|
26
|
+
NODE_MODULE(tree_sitter_tabry_binding, Init)
|
27
|
+
|
28
|
+
} // namespace
|
@@ -0,0 +1,19 @@
|
|
1
|
+
try {
|
2
|
+
module.exports = require("../../build/Release/tree_sitter_tabry_binding");
|
3
|
+
} catch (error1) {
|
4
|
+
if (error1.code !== 'MODULE_NOT_FOUND') {
|
5
|
+
throw error1;
|
6
|
+
}
|
7
|
+
try {
|
8
|
+
module.exports = require("../../build/Debug/tree_sitter_tabry_binding");
|
9
|
+
} catch (error2) {
|
10
|
+
if (error2.code !== 'MODULE_NOT_FOUND') {
|
11
|
+
throw error2;
|
12
|
+
}
|
13
|
+
throw error1
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
try {
|
18
|
+
module.exports.nodeTypeInfo = require("../../src/node-types.json");
|
19
|
+
} catch (_) {}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
fn main() {
|
2
|
+
let src_dir = std::path::Path::new("src");
|
3
|
+
|
4
|
+
let mut c_config = cc::Build::new();
|
5
|
+
c_config.include(&src_dir);
|
6
|
+
c_config
|
7
|
+
.flag_if_supported("-Wno-unused-parameter")
|
8
|
+
.flag_if_supported("-Wno-unused-but-set-variable")
|
9
|
+
.flag_if_supported("-Wno-trigraphs");
|
10
|
+
let parser_path = src_dir.join("parser.c");
|
11
|
+
c_config.file(&parser_path);
|
12
|
+
|
13
|
+
// If your language uses an external scanner written in C,
|
14
|
+
// then include this block of code:
|
15
|
+
|
16
|
+
/*
|
17
|
+
let scanner_path = src_dir.join("scanner.c");
|
18
|
+
c_config.file(&scanner_path);
|
19
|
+
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
|
20
|
+
*/
|
21
|
+
|
22
|
+
c_config.compile("parser");
|
23
|
+
println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap());
|
24
|
+
|
25
|
+
// If your language uses an external scanner written in C++,
|
26
|
+
// then include this block of code:
|
27
|
+
|
28
|
+
/*
|
29
|
+
let mut cpp_config = cc::Build::new();
|
30
|
+
cpp_config.cpp(true);
|
31
|
+
cpp_config.include(&src_dir);
|
32
|
+
cpp_config
|
33
|
+
.flag_if_supported("-Wno-unused-parameter")
|
34
|
+
.flag_if_supported("-Wno-unused-but-set-variable");
|
35
|
+
let scanner_path = src_dir.join("scanner.cc");
|
36
|
+
cpp_config.file(&scanner_path);
|
37
|
+
cpp_config.compile("scanner");
|
38
|
+
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
|
39
|
+
*/
|
40
|
+
}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
//! This crate provides tabry language support for the [tree-sitter][] parsing library.
|
2
|
+
//!
|
3
|
+
//! Typically, you will use the [language][language func] function to add this language to a
|
4
|
+
//! tree-sitter [Parser][], and then use the parser to parse some code:
|
5
|
+
//!
|
6
|
+
//! ```
|
7
|
+
//! let code = "";
|
8
|
+
//! let mut parser = tree_sitter::Parser::new();
|
9
|
+
//! parser.set_language(tree_sitter_tabry::language()).expect("Error loading tabry grammar");
|
10
|
+
//! let tree = parser.parse(code, None).unwrap();
|
11
|
+
//! ```
|
12
|
+
//!
|
13
|
+
//! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
|
14
|
+
//! [language func]: fn.language.html
|
15
|
+
//! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html
|
16
|
+
//! [tree-sitter]: https://tree-sitter.github.io/
|
17
|
+
|
18
|
+
use tree_sitter::Language;
|
19
|
+
|
20
|
+
extern "C" {
|
21
|
+
fn tree_sitter_tabry() -> Language;
|
22
|
+
}
|
23
|
+
|
24
|
+
/// Get the tree-sitter [Language][] for this grammar.
|
25
|
+
///
|
26
|
+
/// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
|
27
|
+
pub fn language() -> Language {
|
28
|
+
unsafe { tree_sitter_tabry() }
|
29
|
+
}
|
30
|
+
|
31
|
+
/// The content of the [`node-types.json`][] file for this grammar.
|
32
|
+
///
|
33
|
+
/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types
|
34
|
+
pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json");
|
35
|
+
|
36
|
+
// Uncomment these to include any queries that this grammar contains
|
37
|
+
|
38
|
+
// pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm");
|
39
|
+
// pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm");
|
40
|
+
// pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm");
|
41
|
+
// pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm");
|
42
|
+
|
43
|
+
#[cfg(test)]
|
44
|
+
mod tests {
|
45
|
+
#[test]
|
46
|
+
fn test_can_load_grammar() {
|
47
|
+
let mut parser = tree_sitter::Parser::new();
|
48
|
+
parser
|
49
|
+
.set_language(super::language())
|
50
|
+
.expect("Error loading tabry language");
|
51
|
+
}
|
52
|
+
}
|
@@ -0,0 +1,96 @@
|
|
1
|
+
==============
|
2
|
+
Arg with two opts
|
3
|
+
==============
|
4
|
+
|
5
|
+
arg {
|
6
|
+
opts const "hello \"world\""
|
7
|
+
opts const abc
|
8
|
+
}
|
9
|
+
|
10
|
+
---
|
11
|
+
|
12
|
+
(source_file
|
13
|
+
(arg_statement
|
14
|
+
(arg_type)
|
15
|
+
(block
|
16
|
+
(opts_const_statement (string))
|
17
|
+
(opts_const_statement (string)))))
|
18
|
+
|
19
|
+
==============
|
20
|
+
Named args
|
21
|
+
==============
|
22
|
+
|
23
|
+
arg foo
|
24
|
+
arg foo @whatever
|
25
|
+
arg foo @whatever {
|
26
|
+
opts const abc
|
27
|
+
}
|
28
|
+
|
29
|
+
---
|
30
|
+
|
31
|
+
(source_file
|
32
|
+
(arg_statement
|
33
|
+
(arg_type)
|
34
|
+
(arg_name_list (string)))
|
35
|
+
(arg_statement
|
36
|
+
(arg_type)
|
37
|
+
(arg_name_list (string))
|
38
|
+
(at_identifier))
|
39
|
+
(arg_statement
|
40
|
+
(arg_type)
|
41
|
+
(arg_name_list (string))
|
42
|
+
(at_identifier)
|
43
|
+
(block
|
44
|
+
(opts_const_statement (string)))))
|
45
|
+
|
46
|
+
============
|
47
|
+
Name and description
|
48
|
+
============
|
49
|
+
|
50
|
+
arg foo "my description"
|
51
|
+
|
52
|
+
---
|
53
|
+
|
54
|
+
(source_file
|
55
|
+
(arg_statement
|
56
|
+
(arg_type)
|
57
|
+
(arg_name_list (string))
|
58
|
+
(string)))
|
59
|
+
|
60
|
+
============
|
61
|
+
Names and description
|
62
|
+
============
|
63
|
+
|
64
|
+
arg (foo bar) "my description"
|
65
|
+
|
66
|
+
---
|
67
|
+
|
68
|
+
(source_file
|
69
|
+
(arg_statement
|
70
|
+
(arg_type)
|
71
|
+
(arg_name_list (string) (string))
|
72
|
+
(string)))
|
73
|
+
|
74
|
+
============
|
75
|
+
Optional arg
|
76
|
+
============
|
77
|
+
|
78
|
+
opt arg
|
79
|
+
opt arg foo @whatever {
|
80
|
+
opts const abc
|
81
|
+
}
|
82
|
+
|
83
|
+
---
|
84
|
+
|
85
|
+
(source_file
|
86
|
+
(arg_statement
|
87
|
+
(arg_modifier)
|
88
|
+
(arg_type))
|
89
|
+
(arg_statement
|
90
|
+
(arg_modifier)
|
91
|
+
(arg_type)
|
92
|
+
(arg_name_list (string))
|
93
|
+
(at_identifier)
|
94
|
+
(block
|
95
|
+
(opts_const_statement (string)))))
|
96
|
+
|
@@ -0,0 +1,79 @@
|
|
1
|
+
==========
|
2
|
+
At include
|
3
|
+
==========
|
4
|
+
|
5
|
+
sub ok {
|
6
|
+
arg {
|
7
|
+
include @foo
|
8
|
+
opts const abc
|
9
|
+
}
|
10
|
+
}
|
11
|
+
|
12
|
+
---
|
13
|
+
|
14
|
+
(source_file
|
15
|
+
(sub_statement
|
16
|
+
(sub_name_list (string))
|
17
|
+
(block
|
18
|
+
(arg_statement
|
19
|
+
(arg_type)
|
20
|
+
(block
|
21
|
+
(include_statement
|
22
|
+
(at_identifier))
|
23
|
+
(opts_const_statement
|
24
|
+
(string)))))))
|
25
|
+
|
26
|
+
=============
|
27
|
+
At definition
|
28
|
+
=============
|
29
|
+
|
30
|
+
defopts @bar {
|
31
|
+
opts const def
|
32
|
+
}
|
33
|
+
|
34
|
+
defargs @foo {
|
35
|
+
arg
|
36
|
+
}
|
37
|
+
|
38
|
+
---
|
39
|
+
|
40
|
+
(source_file
|
41
|
+
(defopts_statement
|
42
|
+
(at_identifier)
|
43
|
+
(block
|
44
|
+
(opts_const_statement (string))))
|
45
|
+
(defargs_statement
|
46
|
+
(at_identifier)
|
47
|
+
(block
|
48
|
+
(arg_statement
|
49
|
+
(arg_type)))))
|
50
|
+
|
51
|
+
=================
|
52
|
+
Inline at include
|
53
|
+
=================
|
54
|
+
|
55
|
+
sub whatever @foo {
|
56
|
+
flagarg foo @wombat
|
57
|
+
arg @bar {
|
58
|
+
opts const abc
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
---
|
63
|
+
|
64
|
+
(source_file
|
65
|
+
(sub_statement
|
66
|
+
(sub_name_list (string))
|
67
|
+
(at_identifier)
|
68
|
+
(block
|
69
|
+
(flagarg_statement
|
70
|
+
(flag_name_list (string))
|
71
|
+
(at_identifier))
|
72
|
+
(arg_statement
|
73
|
+
(arg_type)
|
74
|
+
(at_identifier)
|
75
|
+
(block
|
76
|
+
(opts_const_statement
|
77
|
+
(string)))))))
|
78
|
+
|
79
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
==========
|
2
|
+
Desc
|
3
|
+
==========
|
4
|
+
|
5
|
+
sub ok {
|
6
|
+
desc foo
|
7
|
+
arg {
|
8
|
+
desc "bar"
|
9
|
+
}
|
10
|
+
}
|
11
|
+
|
12
|
+
---
|
13
|
+
|
14
|
+
(source_file
|
15
|
+
(sub_statement
|
16
|
+
(sub_name_list
|
17
|
+
(string))
|
18
|
+
(block
|
19
|
+
(desc_statement
|
20
|
+
(string))
|
21
|
+
(arg_statement
|
22
|
+
(arg_type)
|
23
|
+
(block
|
24
|
+
(desc_statement
|
25
|
+
(string)))))))
|