Merge branch 'master' into wasm-language

This commit is contained in:
Max Brunsfeld 2023-10-27 11:57:04 +01:00
commit f4e2f68f14
161 changed files with 10293 additions and 4253 deletions

View file

@ -1,14 +1,15 @@
[package]
name = "tree-sitter"
description = "Rust bindings to the Tree-sitter parsing library"
version = "0.20.9"
version = "0.20.10"
authors = ["Max Brunsfeld <maxbrunsfeld@gmail.com>"]
edition = "2018"
edition = "2021"
license = "MIT"
readme = "binding_rust/README.md"
keywords = ["incremental", "parsing"]
categories = ["api-bindings", "parsing", "text-editors"]
repository = "https://github.com/tree-sitter/tree-sitter"
rust-version.workspace = true
build = "binding_rust/build.rs"
@ -25,7 +26,7 @@ include = [
wasm = ["wasmtime", "wasmtime-c-api"]
[dependencies]
regex = "1"
regex = "1.9.1"
[dependencies.wasmtime]
git = "https://github.com/maxbrunsfeld/wasmtime"
@ -41,7 +42,8 @@ optional = true
default-features = false
[build-dependencies]
cc = "^1.0.58"
bindgen = { version = "^0.66.1", optional = true }
cc = "^1.0.79"
[lib]
path = "binding_rust/lib.rs"

View file

@ -1,5 +1,4 @@
Subdirectories
--------------
## Subdirectories
* [`src`](./src) - C source code for the Tree-sitter library
* [`include`](./include) - C headers for the Tree-sitter library

View file

@ -1,12 +1,13 @@
# Rust Tree-sitter
[![Build Status](https://travis-ci.org/tree-sitter/tree-sitter.svg?branch=master)](https://travis-ci.org/tree-sitter/tree-sitter)
[![Build status](https://ci.appveyor.com/api/projects/status/vtmbd6i92e97l55w/branch/master?svg=true)](https://ci.appveyor.com/project/maxbrunsfeld/tree-sitter/branch/master)
[![Crates.io](https://img.shields.io/crates/v/tree-sitter.svg)](https://crates.io/crates/tree-sitter)
[![crates.io badge]][crates.io]
[crates.io]: https://crates.io/crates/tree-sitter
[crates.io badge]: https://img.shields.io/crates/v/tree-sitter.svg?color=%23B48723
Rust bindings to the [Tree-sitter][] parsing library.
### Basic Usage
## Basic Usage
First, create a parser:
@ -16,22 +17,6 @@ use tree_sitter::{Parser, Language};
let mut parser = Parser::new();
```
Tree-sitter languages consist of generated C code. To make sure they're properly compiled and linked, you can create a [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html) like the following (assuming `tree-sitter-javascript` is in your root directory):
```rust
use std::path::PathBuf;
fn main() {
let dir: PathBuf = ["tree-sitter-javascript", "src"].iter().collect();
cc::Build::new()
.include(&dir)
.file(dir.join("parser.c"))
.file(dir.join("scanner.c"))
.compile("tree-sitter-javascript");
}
```
Add the `cc` crate to your `Cargo.toml` under `[build-dependencies]`:
```toml
@ -39,15 +24,18 @@ Add the `cc` crate to your `Cargo.toml` under `[build-dependencies]`:
cc="*"
```
To then use languages from rust, you must declare them as `extern "C"` functions and invoke them with `unsafe`. Then you can assign them to the parser.
Then, add a language as a dependency:
```toml
[dependencies]
tree-sitter = "0.20.10"
tree-sitter-rust = "0.20.3"
```
To then use a language, you assign them to the parser.
```rust
extern "C" { fn tree_sitter_c() -> Language; }
extern "C" { fn tree_sitter_rust() -> Language; }
extern "C" { fn tree_sitter_javascript() -> Language; }
let language = unsafe { tree_sitter_rust() };
parser.set_language(language).unwrap();
parser.set_language(tree_sitter_rust::language()).expect("Error loading Rust grammar");
```
Now you can parse source code:
@ -64,7 +52,8 @@ assert_eq!(root_node.end_position().column, 12);
### Editing
Once you have a syntax tree, you can update it when your source code changes. Passing in the previous edited tree makes `parse` run much more quickly:
Once you have a syntax tree, you can update it when your source code changes.
Passing in the previous edited tree makes `parse` run much more quickly:
```rust
let new_source_code = "fn test(a: u32) {}"
@ -83,7 +72,8 @@ let new_tree = parser.parse(new_source_code, Some(&tree));
### Text Input
The source code to parse can be provided either as a string, a slice, a vector, or as a function that returns a slice. The text can be encoded as either UTF8 or UTF16:
The source code to parse can be provided either as a string, a slice, a vector,
or as a function that returns a slice. The text can be encoded as either UTF8 or UTF16:
```rust
// Store some source code in an array of lines.

File diff suppressed because it is too large Load diff

View file

@ -17,6 +17,9 @@ fn main() {
}
}
#[cfg(feature = "bindgen")]
generate_bindings();
let mut config = cc::Build::new();
println!("cargo:rerun-if-env-changed=CARGO_FEATURE_WASM");
@ -33,7 +36,8 @@ fn main() {
config
.flag_if_supported("-std=c99")
.flag_if_supported("-Wno-unused-parameter")
.flag_if_supported("-fvisibility=hidden")
.flag_if_supported("-Wshadow")
.include(src_path)
.include(src_path.join("wasm"))
.include("include")
@ -41,6 +45,45 @@ fn main() {
.compile("tree-sitter");
}
#[cfg(feature = "bindgen")]
fn generate_bindings() {
const HEADER_PATH: &str = "include/tree_sitter/api.h";
println!("cargo:rerun-if-changed={}", HEADER_PATH);
let no_copy = [
"TSInput",
"TSLanguage",
"TSLogger",
"TSLookaheadIterator",
"TSParser",
"TSTree",
"TSQuery",
"TSQueryCursor",
"TSQueryCapture",
"TSQueryMatch",
"TSQueryPredicateStep",
];
let bindings = bindgen::Builder::default()
.header(HEADER_PATH)
.layout_tests(false)
.allowlist_type("^TS.*")
.allowlist_function("^ts_.*")
.allowlist_var("^TREE_SITTER.*")
.no_copy(no_copy.join("|"))
.prepend_enum_name(false)
.generate()
.expect("Failed to generate bindings");
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let bindings_rs = out_dir.join("bindings.rs");
bindings.write_to_file(&bindings_rs).expect(&*format!(
"Failed to write bindings into path: {bindings_rs:?}"
));
}
fn which(exe_name: impl AsRef<Path>) -> Option<PathBuf> {
env::var_os("PATH").and_then(|paths| {
env::split_paths(&paths).find_map(|dir| {

View file

@ -2,8 +2,153 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#[cfg(feature = "bindgen")]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
#[cfg(not(feature = "bindgen"))]
include!("./bindings.rs");
extern "C" {
pub(crate) fn dup(fd: std::os::raw::c_int) -> std::os::raw::c_int;
}
use crate::{
Language, LookaheadIterator, Node, Parser, Query, QueryCursor, QueryError, Tree, TreeCursor,
};
use std::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull, str};
impl Language {
/// Reconstructs a [`Language`] from a raw pointer.
///
/// # Safety
///
/// `ptr` must be non-null.
pub unsafe fn from_raw(ptr: *const TSLanguage) -> Language {
Language(ptr)
}
/// Consumes the [`Language`], returning a raw pointer to the underlying C structure.
pub fn into_raw(self) -> *const TSLanguage {
ManuallyDrop::new(self).0
}
}
impl Parser {
/// Reconstructs a [`Parser`] from a raw pointer.
///
/// # Safety
///
/// `ptr` must be non-null.
pub unsafe fn from_raw(ptr: *mut TSParser) -> Parser {
Parser(NonNull::new_unchecked(ptr))
}
/// Consumes the [`Parser`], returning a raw pointer to the underlying C structure.
///
/// # Safety
///
/// It's a caller responsibility to adjust parser's state
/// like disable logging or dot graphs printing if this
/// may cause issues like use after free.
pub fn into_raw(self) -> *mut TSParser {
ManuallyDrop::new(self).0.as_ptr()
}
}
impl Tree {
/// Reconstructs a [`Tree`] from a raw pointer.
///
/// # Safety
///
/// `ptr` must be non-null.
pub unsafe fn from_raw(ptr: *mut TSTree) -> Tree {
Tree(NonNull::new_unchecked(ptr))
}
/// Consumes the [`Tree`], returning a raw pointer to the underlying C structure.
pub fn into_raw(self) -> *mut TSTree {
ManuallyDrop::new(self).0.as_ptr()
}
}
impl<'tree> Node<'tree> {
/// Reconstructs a [`Node`] from a raw pointer.
///
/// # Safety
///
/// `ptr` must be non-null.
pub unsafe fn from_raw(raw: TSNode) -> Node<'tree> {
Node(raw, PhantomData)
}
/// Consumes the [`Node`], returning a raw pointer to the underlying C structure.
pub fn into_raw(self) -> TSNode {
ManuallyDrop::new(self).0
}
}
impl<'a> TreeCursor<'a> {
/// Reconstructs a [`TreeCursor`] from a raw pointer.
///
/// # Safety
///
/// `ptr` must be non-null.
pub unsafe fn from_raw(raw: TSTreeCursor) -> TreeCursor<'a> {
TreeCursor(raw, PhantomData)
}
/// Consumes the [`TreeCursor`], returning a raw pointer to the underlying C structure.
pub fn into_raw(self) -> TSTreeCursor {
ManuallyDrop::new(self).0
}
}
impl Query {
/// Reconstructs a [`Query`] from a raw pointer.
///
/// # Safety
///
/// `ptr` must be non-null.
pub unsafe fn from_raw(ptr: *mut TSQuery, source: &str) -> Result<Query, QueryError> {
Query::from_raw_parts(ptr, source)
}
/// Consumes the [`Query`], returning a raw pointer to the underlying C structure.
pub fn into_raw(self) -> *mut TSQuery {
ManuallyDrop::new(self).ptr.as_ptr()
}
}
impl QueryCursor {
/// Reconstructs a [`QueryCursor`] from a raw pointer.
///
/// # Safety
///
/// `ptr` must be non-null.
pub unsafe fn from_raw(ptr: *mut TSQueryCursor) -> QueryCursor {
QueryCursor {
ptr: NonNull::new_unchecked(ptr),
}
}
/// Consumes the [`QueryCursor`], returning a raw pointer to the underlying C structure.
pub fn into_raw(self) -> *mut TSQueryCursor {
ManuallyDrop::new(self).ptr.as_ptr()
}
}
impl LookaheadIterator {
/// Reconstructs a [`LookaheadIterator`] from a raw pointer.
///
/// # Safety
///
/// `ptr` must be non-null.
pub unsafe fn from_raw(ptr: *mut TSLookaheadIterator) -> LookaheadIterator {
LookaheadIterator(NonNull::new_unchecked(ptr))
}
/// Consumes the [`LookaheadIterator`], returning a raw pointer to the underlying C structure.
pub fn into_raw(self) -> *mut TSLookaheadIterator {
ManuallyDrop::new(self).0.as_ptr()
}
}

File diff suppressed because it is too large Load diff

View file

@ -37,6 +37,8 @@ impl<T: Copy> ExactSizeIterator for CBufferIter<T> {}
impl<T> Drop for CBufferIter<T> {
fn drop(&mut self) {
unsafe { (FREE_FN)(self.ptr as *mut c_void) };
if !self.ptr.is_null() {
unsafe { (FREE_FN)(self.ptr as *mut c_void) };
}
}
}

View file

@ -1,16 +1,18 @@
Web Tree-sitter
===============
# Web Tree-sitter
[![Build Status](https://travis-ci.org/tree-sitter/tree-sitter.svg?branch=master)](https://travis-ci.org/tree-sitter/tree-sitter)
[![npmjs.com badge]][npmjs.com]
[npmjs.com]: https://www.npmjs.org/package/web-tree-sitter
[npmjs.com badge]: https://img.shields.io/npm/v/web-tree-sitter.svg?color=%23BF4A4A
WebAssembly bindings to the [Tree-sitter](https://github.com/tree-sitter/tree-sitter) parsing library.
### Setup
You can download the the `tree-sitter.js` and `tree-sitter.wasm` files from [the latest GitHub release](https://github.com/tree-sitter/tree-sitter/releases/latest) and load them using a standalone script:
You can download the `tree-sitter.js` and `tree-sitter.wasm` files from [the latest GitHub release](https://github.com/tree-sitter/tree-sitter/releases/latest) and load them using a standalone script:
```html
<script src="/the/path/to/tree-sitter.js"/>
<script src="/the/path/to/tree-sitter.js"></script>
<script>
const Parser = window.TreeSitter;
@ -118,7 +120,7 @@ First install `tree-sitter-cli` and the tree-sitter language for which to genera
npm install --save-dev tree-sitter-cli tree-sitter-javascript
```
Then just use tree-sitter cli tool to generate the `.wasm`.
Then just use tree-sitter cli tool to generate the `.wasm`.
```sh
npx tree-sitter build-wasm node_modules/tree-sitter-javascript
@ -149,13 +151,13 @@ const Parser = require('web-tree-sitter');
##### Loading the .wasm file
`web-tree-sitter` needs to load the `tree-sitter.wasm` file. By default, it assumes that this file is available in the
`web-tree-sitter` needs to load the `tree-sitter.wasm` file. By default, it assumes that this file is available in the
same path as the JavaScript code. Therefore, if the code is being served from `http://localhost:3000/bundle.js`, then
the wasm file should be at `http://localhost:3000/tree-sitter.wasm`.
For server side frameworks like NextJS, this can be tricky as pages are often served from a path such as
For server side frameworks like NextJS, this can be tricky as pages are often served from a path such as
`http://localhost:3000/_next/static/chunks/pages/index.js`. The loader will therefore look for the wasm file at
`http://localhost:3000/_next/static/chunks/pages/tree-sitter.wasm`. The solution is to pass a `locateFile` function in
`http://localhost:3000/_next/static/chunks/pages/tree-sitter.wasm`. The solution is to pass a `locateFile` function in
the `moduleOptions` argument to `Parser.init()`:
```javascript
@ -166,15 +168,15 @@ await Parser.init({
});
```
`locateFile` takes in two parameters, `scriptName`, i.e. the wasm file name, and `scriptDirectory`, i.e. the directory
`locateFile` takes in two parameters, `scriptName`, i.e. the wasm file name, and `scriptDirectory`, i.e. the directory
where the loader expects the script to be. It returns the path where the loader will look for the wasm file. In the NextJS
case, we want to return just the `scriptName` so that the loader will look at `http://localhost:3000/tree-sitter.wasm`
and not `http://localhost:3000/_next/static/chunks/pages/tree-sitter.wasm`.
##### `Can't resolve 'fs' in 'node_modules/web-tree-sitter'`
Most bundlers will notice that the `tree-sitter.js` file is attempting to import `fs`, i.e. node's file system library.
Since this doesn't exist in the browser, the bundlers will get confused. For webpack you can fix this by adding the
Most bundlers will notice that the `tree-sitter.js` file is attempting to import `fs`, i.e. node's file system library.
Since this doesn't exist in the browser, the bundlers will get confused. For webpack you can fix this by adding the
following to your webpack config:
```javascript

View file

@ -243,6 +243,13 @@ void ts_tree_cursor_reset_wasm(const TSTree *tree) {
marshal_cursor(&cursor);
}
void ts_tree_cursor_reset_to_wasm(const TSTree *_dst, const TSTree *_src) {
TSTreeCursor cursor = unmarshal_cursor(TRANSFER_BUFFER, _dst);
TSTreeCursor src = unmarshal_cursor(&TRANSFER_BUFFER[3], _src);
ts_tree_cursor_reset_to(&cursor, &src);
marshal_cursor(&cursor);
}
bool ts_tree_cursor_goto_first_child_wasm(const TSTree *tree) {
TSTreeCursor cursor = unmarshal_cursor(TRANSFER_BUFFER, tree);
bool result = ts_tree_cursor_goto_first_child(&cursor);
@ -250,6 +257,13 @@ bool ts_tree_cursor_goto_first_child_wasm(const TSTree *tree) {
return result;
}
bool ts_tree_cursor_goto_last_child_wasm(const TSTree *tree) {
TSTreeCursor cursor = unmarshal_cursor(TRANSFER_BUFFER, tree);
bool result = ts_tree_cursor_goto_last_child(&cursor);
marshal_cursor(&cursor);
return result;
}
bool ts_tree_cursor_goto_next_sibling_wasm(const TSTree *tree) {
TSTreeCursor cursor = unmarshal_cursor(TRANSFER_BUFFER, tree);
bool result = ts_tree_cursor_goto_next_sibling(&cursor);
@ -257,6 +271,13 @@ bool ts_tree_cursor_goto_next_sibling_wasm(const TSTree *tree) {
return result;
}
bool ts_tree_cursor_goto_previous_sibling_wasm(const TSTree *tree) {
TSTreeCursor cursor = unmarshal_cursor(TRANSFER_BUFFER, tree);
bool result = ts_tree_cursor_goto_previous_sibling(&cursor);
marshal_cursor(&cursor);
return result;
}
bool ts_tree_cursor_goto_parent_wasm(const TSTree *tree) {
TSTreeCursor cursor = unmarshal_cursor(TRANSFER_BUFFER, tree);
bool result = ts_tree_cursor_goto_parent(&cursor);
@ -270,6 +291,12 @@ uint16_t ts_tree_cursor_current_node_type_id_wasm(const TSTree *tree) {
return ts_node_symbol(node);
}
uint16_t ts_tree_cursor_current_node_state_id_wasm(const TSTree *tree) {
TSTreeCursor cursor = unmarshal_cursor(TRANSFER_BUFFER, tree);
TSNode node = ts_tree_cursor_current_node(&cursor);
return ts_node_parse_state(node);
}
bool ts_tree_cursor_current_node_is_named_wasm(const TSTree *tree) {
TSTreeCursor cursor = unmarshal_cursor(TRANSFER_BUFFER, tree);
TSNode node = ts_tree_cursor_current_node(&cursor);
@ -334,6 +361,16 @@ uint16_t ts_node_symbol_wasm(const TSTree *tree) {
return ts_node_symbol(node);
}
const char *ts_node_field_name_for_child_wasm(const TSTree *tree, uint32_t index) {
TSNode node = unmarshal_node(tree);
return ts_node_field_name_for_child(node, index);
}
uint16_t ts_node_grammar_symbol_wasm(const TSTree *tree) {
TSNode node = unmarshal_node(tree);
return ts_node_grammar_symbol(node);
}
uint32_t ts_node_child_count_wasm(const TSTree *tree) {
TSNode node = unmarshal_node(tree);
return ts_node_child_count(node);
@ -579,11 +616,26 @@ int ts_node_has_error_wasm(const TSTree *tree) {
return ts_node_has_error(node);
}
int ts_node_is_error_wasm(const TSTree *tree) {
TSNode node = unmarshal_node(tree);
return ts_node_is_error(node);
}
int ts_node_is_missing_wasm(const TSTree *tree) {
TSNode node = unmarshal_node(tree);
return ts_node_is_missing(node);
}
uint16_t ts_node_parse_state_wasm(const TSTree *tree) {
TSNode node = unmarshal_node(tree);
return ts_node_parse_state(node);
}
uint16_t ts_node_next_parse_state_wasm(const TSTree *tree) {
TSNode node = unmarshal_node(tree);
return ts_node_next_parse_state(node);
}
/******************/
/* Section - Query */
/******************/

View file

@ -1,6 +1,7 @@
const C = Module;
const INTERNAL = {};
const SIZE_OF_INT = 4;
const SIZE_OF_CURSOR = 3 * SIZE_OF_INT;
const SIZE_OF_NODE = 5 * SIZE_OF_INT;
const SIZE_OF_POINT = 2 * SIZE_OF_INT;
const SIZE_OF_RANGE = 2 * SIZE_OF_INT + 2 * SIZE_OF_POINT;
@ -208,10 +209,19 @@ class Node {
return C._ts_node_symbol_wasm(this.tree[0]);
}
get grammarId() {
marshalNode(this);
return C._ts_node_grammar_symbol_wasm(this.tree[0]);
}
get type() {
return this.tree.language.types[this.typeId] || 'ERROR';
}
get grammarType() {
return this.tree.language.types[this.grammarId] || 'ERROR';
}
get endPosition() {
marshalNode(this);
C._ts_node_end_point_wasm(this.tree[0]);
@ -227,6 +237,16 @@ class Node {
return getText(this.tree, this.startIndex, this.endIndex);
}
get parseState() {
marshalNode(this);
return C._ts_node_parse_state_wasm(this.tree[0]);
}
get nextParseState() {
marshalNode(this);
return C._ts_node_next_parse_state_wasm(this.tree[0]);
}
isNamed() {
marshalNode(this);
return C._ts_node_is_named_wasm(this.tree[0]) === 1;
@ -242,6 +262,11 @@ class Node {
return C._ts_node_has_changes_wasm(this.tree[0]) === 1;
}
isError() {
marshalNode(this);
return C._ts_node_is_error_wasm(this.tree[0]) === 1;
}
isMissing() {
marshalNode(this);
return C._ts_node_is_missing_wasm(this.tree[0]) === 1;
@ -257,6 +282,17 @@ class Node {
return unmarshalNode(this.tree);
}
fieldNameForChild(index) {
marshalNode(this);
const address = C._ts_node_field_name_for_child_wasm(this.tree[0], index);
if (!address) {
return null;
}
const result = AsciiToString(address);
// must not free, the string memory is owned by the language
return result;
}
namedChild(index) {
marshalNode(this);
C._ts_node_named_child_wasm(this.tree[0], index);
@ -505,6 +541,13 @@ class TreeCursor {
unmarshalTreeCursor(this);
}
resetTo(cursor) {
marshalTreeCursor(this, TRANSFER_BUFFER);
marshalTreeCursor(cursor, TRANSFER_BUFFER + SIZE_OF_CURSOR);
C._ts_tree_cursor_reset_to_wasm(this.tree[0], cursor.tree[0]);
unmarshalTreeCursor(this);
}
get nodeType() {
return this.tree.language.types[this.nodeTypeId] || 'ERROR';
}
@ -514,6 +557,11 @@ class TreeCursor {
return C._ts_tree_cursor_current_node_type_id_wasm(this.tree[0]);
}
get nodeStateId() {
marshalTreeCursor(this);
return C._ts_tree_cursor_current_node_state_id_wasm(this.tree[0]);
}
get nodeId() {
marshalTreeCursor(this);
return C._ts_tree_cursor_current_node_id_wasm(this.tree[0]);
@ -580,6 +628,13 @@ class TreeCursor {
return result === 1;
}
gotoLastChild() {
marshalTreeCursor(this);
const result = C._ts_tree_cursor_goto_last_child_wasm(this.tree[0]);
unmarshalTreeCursor(this);
return result === 1;
}
gotoNextSibling() {
marshalTreeCursor(this);
const result = C._ts_tree_cursor_goto_next_sibling_wasm(this.tree[0]);
@ -587,6 +642,13 @@ class TreeCursor {
return result === 1;
}
gotoPreviousSibling() {
marshalTreeCursor(this);
const result = C._ts_tree_cursor_goto_previous_sibling_wasm(this.tree[0]);
unmarshalTreeCursor(this);
return result === 1;
}
gotoParent() {
marshalTreeCursor(this);
const result = C._ts_tree_cursor_goto_parent_wasm(this.tree[0]);
@ -624,6 +686,10 @@ class Language {
return this.fields.length - 1;
}
get stateCount() {
return C._ts_language_state_count(this[0]);
}
fieldIdForName(fieldName) {
const result = this.fields.indexOf(fieldName);
if (result !== -1) {
@ -663,6 +729,15 @@ class Language {
return C._ts_language_type_is_visible_wasm(this[0], typeId) ? true : false;
}
nextState(stateId, typeId) {
return C._ts_language_next_state(this[0], stateId, typeId);
}
lookaheadIterator(stateId) {
const address = C._ts_lookahead_iterator_new(this[0], stateId);
if (address) return new LookaheadIterable(INTERNAL, address, this);
}
query(source) {
const sourceLength = lengthBytesUTF8(source);
const sourceAddress = C._malloc(sourceLength + 1);
@ -766,7 +841,13 @@ class Language {
}
const operator = steps[0].value;
let isPositive = true;
let matchAll = true;
switch (operator) {
case 'any-not-eq?':
isPositive = false;
matchAll = false;
case 'any-eq?':
matchAll = false;
case 'not-eq?':
isPositive = false;
case 'eq?':
@ -780,28 +861,36 @@ class Language {
const captureName1 = steps[1].name;
const captureName2 = steps[2].name;
textPredicates[i].push(function(captures) {
let node1, node2
let nodes_1 = [];
let nodes_2 = [];
for (const c of captures) {
if (c.name === captureName1) node1 = c.node;
if (c.name === captureName2) node2 = c.node;
if (c.name === captureName1) nodes_1.push(c.node);
if (c.name === captureName2) nodes_2.push(c.node);
}
if(node1 === undefined || node2 === undefined) return true;
return (node1.text === node2.text) === isPositive;
return matchAll
? nodes_1.every(n1 => nodes_2.some(n2 => n1.text === n2.text)) === isPositive
: nodes_1.some(n1 => nodes_2.some(n2 => n1.text === n2.text)) === isPositive;
});
} else {
const captureName = steps[1].name;
const stringValue = steps[2].value;
textPredicates[i].push(function(captures) {
let nodes = [];
for (const c of captures) {
if (c.name === captureName) {
return (c.node.text === stringValue) === isPositive;
};
if (c.name === captureName) nodes.push(c.node);
}
return true;
return matchAll
? nodes.every(n => n.text === stringValue) === isPositive
: nodes.some(n => n.text === stringValue) === isPositive;
});
}
break;
case 'not-any-match?':
isPositive = false;
matchAll = false;
case 'any-match?':
matchAll = false;
case 'not-match?':
isPositive = false;
case 'match?':
@ -817,10 +906,14 @@ class Language {
const captureName = steps[1].name;
const regex = new RegExp(steps[2].value);
textPredicates[i].push(function(captures) {
const nodes = [];
for (const c of captures) {
if (c.name === captureName) return regex.test(c.node.text) === isPositive;
if (c.name === captureName) nodes.push(c.node.text);
}
return true;
if (nodes.length === 0) return !isPositive;
return matchAll
? nodes.every(text => regex.test(text)) === isPositive
: nodes.some(text => regex.test(text)) === isPositive;
});
break;
@ -848,6 +941,32 @@ class Language {
properties[i][steps[1].value] = steps[2] ? steps[2].value : null;
break;
case 'not-any-of?':
isPositive = false;
case 'any-of?':
if (steps.length < 2) throw new Error(
`Wrong number of arguments to \`#${operator}\` predicate. Expected at least 1. Got ${steps.length - 1}.`
);
if (steps[1].type !== 'capture') throw new Error(
`First argument of \`#${operator}\` predicate must be a capture. Got "${steps[1].value}".`
);
for (let i = 2; i < steps.length; i++) {
if (steps[i].type !== 'string') throw new Error(
`Arguments to \`#${operator}\` predicate must be a strings.".`
);
}
captureName = steps[1].name;
const values = steps.slice(2).map(s => s.value);
textPredicates[i].push(function(captures) {
const nodes = [];
for (const c of captures) {
if (c.name === captureName) nodes.push(c.node.text);
}
if (nodes.length === 0) return !isPositive;
return nodes.every(text => values.includes(text)) === isPositive;
});
break;
default:
predicates[i].push({operator, operands: steps.slice(1)});
}
@ -918,6 +1037,53 @@ class Language {
}
}
class LookaheadIterable {
constructor(internal, address, language) {
assertInternal(internal);
this[0] = address;
this.language = language;
}
get currentTypeId() {
return C._ts_lookahead_iterator_current_symbol(this[0]);
}
get currentType() {
return this.language.types[this.currentTypeId] || 'ERROR'
}
delete() {
C._ts_lookahead_iterator_delete(this[0]);
this[0] = 0;
}
resetState(stateId) {
return C._ts_lookahead_iterator_reset_state(this[0], stateId);
}
reset(language, stateId) {
if (C._ts_lookahead_iterator_reset(this[0], language[0], stateId)) {
this.language = language;
return true;
}
return false;
}
[Symbol.iterator]() {
const self = this;
return {
next() {
if (C._ts_lookahead_iterator_next(self[0])) {
return { done: false, value: self.currentType };
}
return { done: true, value: "" };
}
};
}
}
class Query {
constructor(
internal, address, captureNames, textPredicates, predicates,

View file

@ -4,22 +4,27 @@
"_malloc",
"_realloc",
"__ZNKSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4copyEPcmm",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEm",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9__grow_byEmmmmmm",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9push_backEc",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev",
"__ZNSt3__212basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE9push_backEw",
"__ZNKSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4copyEPcmm",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE7reserveEm",
"__ZNSt3__212basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEE6resizeEmw",
"__ZNSt3__212basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev",
"__ZNSt3__212basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEED2Ev",
"__ZdlPv",
"__Znwm",
"___cxa_atexit",
"_abort",
"_isalpha",
"_isspace",
"_iswalnum",
"_iswalpha",
"_iswblank",
"_iswdigit",
"_iswlower",
"_iswupper",
"_iswspace",
"_memchr",
"_memcmp",
@ -27,7 +32,12 @@
"_memmove",
"_memset",
"_strlen",
"_strcmp",
"_strncpy",
"_tolower",
"_towlower",
"_towupper",
"_stderr",
"_ts_init",
"_ts_language_field_count",
@ -35,10 +45,13 @@
"_ts_language_type_is_named_wasm",
"_ts_language_type_is_visible_wasm",
"_ts_language_symbol_count",
"_ts_language_state_count",
"_ts_language_symbol_for_name",
"_ts_language_symbol_name",
"_ts_language_symbol_type",
"_ts_language_version",
"_ts_language_next_state",
"_ts_node_field_name_for_child_wasm",
"_ts_node_child_by_field_id_wasm",
"_ts_node_child_count_wasm",
"_ts_node_child_wasm",
@ -50,8 +63,11 @@
"_ts_node_end_point_wasm",
"_ts_node_has_changes_wasm",
"_ts_node_has_error_wasm",
"_ts_node_is_error_wasm",
"_ts_node_is_missing_wasm",
"_ts_node_is_named_wasm",
"_ts_node_parse_state_wasm",
"_ts_node_next_parse_state_wasm",
"_ts_node_named_child_count_wasm",
"_ts_node_named_child_wasm",
"_ts_node_named_children_wasm",
@ -65,6 +81,7 @@
"_ts_node_start_index_wasm",
"_ts_node_start_point_wasm",
"_ts_node_symbol_wasm",
"_ts_node_grammar_symbol_wasm",
"_ts_node_to_string_wasm",
"_ts_parser_delete",
"_ts_parser_enable_logger_wasm",
@ -90,19 +107,29 @@
"_ts_tree_cursor_current_node_is_missing_wasm",
"_ts_tree_cursor_current_node_is_named_wasm",
"_ts_tree_cursor_current_node_type_id_wasm",
"_ts_tree_cursor_current_node_state_id_wasm",
"_ts_tree_cursor_current_node_wasm",
"_ts_tree_cursor_delete_wasm",
"_ts_tree_cursor_end_index_wasm",
"_ts_tree_cursor_end_position_wasm",
"_ts_tree_cursor_goto_first_child_wasm",
"_ts_tree_cursor_goto_last_child_wasm",
"_ts_tree_cursor_goto_next_sibling_wasm",
"_ts_tree_cursor_goto_previous_sibling_wasm",
"_ts_tree_cursor_goto_parent_wasm",
"_ts_tree_cursor_new_wasm",
"_ts_tree_cursor_reset_wasm",
"_ts_tree_cursor_reset_to_wasm",
"_ts_tree_cursor_start_index_wasm",
"_ts_tree_cursor_start_position_wasm",
"_ts_tree_delete",
"_ts_tree_edit_wasm",
"_ts_tree_get_changed_ranges_wasm",
"_ts_tree_root_node_wasm"
"_ts_tree_root_node_wasm",
"_ts_lookahead_iterator_new",
"_ts_lookahead_iterator_delete",
"_ts_lookahead_iterator_reset_state",
"_ts_lookahead_iterator_reset",
"_ts_lookahead_iterator_next",
"_ts_lookahead_iterator_current_symbol"
]

View file

@ -1,6 +1,6 @@
{
"name": "web-tree-sitter",
"version": "0.20.7",
"version": "0.20.8",
"description": "Tree-sitter bindings for the web",
"main": "tree-sitter.js",
"types": "tree-sitter-web.d.ts",
@ -27,8 +27,8 @@
},
"homepage": "https://github.com/tree-sitter/tree-sitter/tree/master/lib/binding_web",
"devDependencies": {
"chai": "^4.2.0",
"mocha": "^6.1.4",
"terser": "^3.17.0"
"chai": "^4.3.7",
"mocha": "^10.2.0",
"terser": "^5.16.6"
}
}

View file

@ -42,3 +42,46 @@ describe("Language", () => {
});
});
});
describe("Lookahead iterator", () => {
let lookahead;
let state;
before(async () => {
let Parser;
({ JavaScript, Parser } = await require("./helper"));
const parser = new Parser().setLanguage(JavaScript);
const tree = parser.parse("function fn() {}");
parser.delete();
const cursor = tree.walk();
assert(cursor.gotoFirstChild());
assert(cursor.gotoFirstChild());
state = cursor.currentNode().nextParseState;
lookahead = JavaScript.lookaheadIterator(state);
assert.exists(lookahead);
});
after(() => {
lookahead.delete();
});
const expected = ["identifier", "comment", "(", "*", "formal_parameters"];
it("should iterate over valid symbols in the state", () => {
const symbols = Array.from(lookahead);
assert.includeMembers(symbols, expected);
assert.lengthOf(symbols, expected.length);
});
it("should reset to the initial state", () => {
assert(lookahead.resetState(state));
const symbols = Array.from(lookahead);
assert.includeMembers(symbols, expected);
assert.lengthOf(symbols, expected.length);
});
it("should reset", () => {
assert(lookahead.reset(JavaScript, state));
const symbols = Array.from(lookahead);
assert.includeMembers(symbols, expected);
assert.lengthOf(symbols, expected.length);
});
});

View file

@ -268,6 +268,24 @@ describe("Node", () => {
});
});
describe(".isError()", () => {
it("returns true if the node is an error", () => {
tree = parser.parse("2 * * 3");
const node = tree.rootNode;
assert.equal(
node.toString(),
'(program (expression_statement (binary_expression left: (number) (ERROR) right: (number))))'
);
const multi = node.firstChild.firstChild;
assert(multi.hasError());
assert(!multi.children[0].isError());
assert(!multi.children[1].isError());
assert(multi.children[2].isError());
assert(!multi.children[3].isError());
});
});
describe(".isMissing()", () => {
it("returns true if the node is missing from the source and was inserted via error recovery", () => {
tree = parser.parse("(2 ||)");
@ -308,6 +326,34 @@ describe("Node", () => {
);
});
describe(".parseState, .nextParseState", () => {
const text = "10 / 5";
it("returns node parse state ids", async () => {
tree = await parser.parse(text)
const quotientNode = tree.rootNode.firstChild.firstChild;
const [numerator, slash, denominator] = quotientNode.children;
assert.equal(tree.rootNode.parseState, 0);
// parse states will change on any change to the grammar so test that it
// returns something instead
assert.isAbove(numerator.parseState, 0);
assert.isAbove(slash.parseState, 0);
assert.isAbove(denominator.parseState, 0);
})
it("returns next parse state equal to the language", async () => {
tree = await parser.parse(text);
const quotientNode = tree.rootNode.firstChild.firstChild;
quotientNode.children.forEach(node => {
assert.equal(
node.nextParseState,
JavaScript.nextState(node.parseState, node.grammarId)
);
});
});
});
describe('.descendantsOfType(type, min, max)', () => {
it('finds all of the descendants of the given type in the given range', () => {
tree = parser.parse("a + 1 * b * 2 + c + 3");
@ -408,4 +454,20 @@ describe("Node", () => {
assert(!node1.equals(node2));
});
});
describe('.fieldNameForChild(index)', () => {
it('returns the field of a child or null', () => {
tree = parser.parse('let a = 5');
const noField = tree.rootNode.fieldNameForChild(0);
const name = tree.rootNode.firstChild.children[1].fieldNameForChild(0);
const value = tree.rootNode.firstChild.children[1].fieldNameForChild(2);
const overflow = tree.rootNode.firstChild.children[1].fieldNameForChild(3);
assert.equal(noField, null);
assert.equal(name, 'name');
assert.equal(value, 'value');
assert.equal(overflow, null);
});
});
});

View file

@ -127,19 +127,19 @@ describe("Parser", () => {
it("can use the bash parser", async () => {
parser.setLanguage(await Parser.Language.load(languageURL('bash')));
tree = parser.parse("FOO=bar echo <<EOF 2> err.txt > hello.txt \nhello\nEOF");
tree = parser.parse("FOO=bar echo <<EOF 2> err.txt > hello.txt \nhello${FOO}\nEOF");
assert.equal(
tree.rootNode.toString(),
'(program (redirected_statement ' +
'body: (command ' +
'(variable_assignment ' +
'name: (variable_name) ' +
'value: (word)) ' +
'name: (command_name (word))) ' +
'redirect: (heredoc_redirect (heredoc_start)) ' +
'redirect: (file_redirect descriptor: (file_descriptor) destination: (word)) ' +
'redirect: (file_redirect destination: (word))) ' +
'(heredoc_body))'
'(program ' +
'(redirected_statement ' +
'body: (command ' +
'(variable_assignment name: (variable_name) value: (word)) ' +
'name: (command_name (word))) ' +
'redirect: (heredoc_redirect (heredoc_start) ' +
'redirect: (file_redirect descriptor: (file_descriptor) destination: (word)) ' +
'redirect: (file_redirect destination: (word)) ' +
'(heredoc_body ' +
'(expansion (variable_name)) (heredoc_content)) (heredoc_end))))'
);
}).timeout(5000);
@ -153,7 +153,7 @@ describe("Parser", () => {
'type: (primitive_type) ' +
'declarator: (init_declarator ' +
'declarator: (pointer_declarator declarator: (identifier)) ' +
'value: (raw_string_literal))))'
'value: (raw_string_literal delimiter: (raw_string_delimiter) (raw_string_content) (raw_string_delimiter)))))'
);
}).timeout(5000);

View file

@ -244,6 +244,50 @@ describe("Tree", () => {
endIndex: 13
});
{
const copy = tree.walk();
copy.resetTo(cursor);
assert(copy.gotoPreviousSibling());
assertCursorState(copy, {
nodeType: '+',
nodeIsNamed: false,
startPosition: {row: 0, column: 6},
endPosition: {row: 0, column: 7},
startIndex: 6,
endIndex: 7
});
assert(copy.gotoPreviousSibling());
assertCursorState(copy, {
nodeType: 'binary_expression',
nodeIsNamed: true,
startPosition: {row: 0, column: 0},
endPosition: {row: 0, column: 5},
startIndex: 0,
endIndex: 5
});
assert(copy.gotoLastChild());
assertCursorState(copy, {
nodeType: "identifier",
nodeIsNamed: true,
startPosition: {row: 0, column: 4},
endPosition: {row: 0, column: 5},
startIndex: 4,
endIndex: 5
})
assert(copy.gotoParent());
assert(copy.gotoParent());
assert.equal(copy.nodeType, 'binary_expression')
assert(copy.gotoParent());
assert.equal(copy.nodeType, 'expression_statement')
assert(copy.gotoParent());
assert.equal(copy.nodeType, 'program')
assert(!copy.gotoParent());
}
// const childIndex = cursor.gotoFirstChildForIndex(12);
// assertCursorState(cursor, {
// nodeType: 'identifier',

View file

@ -55,10 +55,14 @@ declare module 'web-tree-sitter' {
) => string | null;
export interface SyntaxNode {
id: number;
typeId: number;
grammarId: number;
tree: Tree;
type: string;
grammarType: string;
text: string;
parseState: number;
nextParseState: number;
startPosition: Point;
endPosition: Point;
startIndex: number;
@ -80,6 +84,7 @@ declare module 'web-tree-sitter' {
hasChanges(): boolean;
hasError(): boolean;
equals(other: SyntaxNode): boolean;
isError(): boolean;
isMissing(): boolean;
isNamed(): boolean;
toString(): string;
@ -104,6 +109,7 @@ declare module 'web-tree-sitter' {
export interface TreeCursor {
nodeType: string;
nodeTypeId: number;
nodeStateId: number;
nodeText: string;
nodeId: number;
nodeIsNamed: boolean;
@ -114,14 +120,17 @@ declare module 'web-tree-sitter' {
endIndex: number;
reset(node: SyntaxNode): void;
resetTo(cursor: TreeCursor): void;
delete(): void;
currentNode(): SyntaxNode;
currentFieldId(): number;
currentFieldName(): string;
gotoParent(): boolean;
gotoFirstChild(): boolean;
gotoLastChild(): boolean;
gotoFirstChildForIndex(index: number): boolean;
gotoNextSibling(): boolean;
gotoPreviousSibling(): boolean;
}
export interface Tree {
@ -141,6 +150,7 @@ declare module 'web-tree-sitter' {
readonly version: number;
readonly fieldCount: number;
readonly stateCount: number;
readonly nodeTypeCount: number;
fieldNameForId(fieldId: number): string | null;
@ -149,7 +159,20 @@ declare module 'web-tree-sitter' {
nodeTypeForId(typeId: number): string | null;
nodeTypeIsNamed(typeId: number): boolean;
nodeTypeIsVisible(typeId: number): boolean;
nextState(stateId: number, typeId: number): number;
query(source: string): Query;
lookaheadIterator(stateId: number): LookaheadIterable | null;
}
class LookaheadIterable {
readonly language: Language;
readonly currentTypeId: number;
readonly currentType: string;
delete(): void;
resetState(stateId: number): boolean;
reset(language: Language, stateId: number): boolean;
[Symbol.iterator](): Iterator<string>;
}
interface QueryCapture {

File diff suppressed because it is too large Load diff

View file

@ -13,9 +13,8 @@ extern "C" {
#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 TSStateId;
typedef uint16_t TSSymbol;
typedef uint16_t TSFieldId;
typedef struct TSLanguage TSLanguage;
@ -140,7 +139,8 @@ struct TSLanguage {
lexer->advance(lexer, skip); \
start: \
skip = false; \
lookahead = lexer->lookahead;
lookahead = lexer->lookahead; \
eof = lexer->eof(lexer);
#define ADVANCE(state_value) \
{ \
@ -166,7 +166,7 @@ struct TSLanguage {
* Parse Table Macros
*/
#define SMALL_STATE(id) id - LARGE_STATE_COUNT
#define SMALL_STATE(id) ((id) - LARGE_STATE_COUNT)
#define STATE(id) id
@ -176,7 +176,7 @@ struct TSLanguage {
{{ \
.shift = { \
.type = TSParseActionTypeShift, \
.state = state_value \
.state = (state_value) \
} \
}}
@ -184,7 +184,7 @@ struct TSLanguage {
{{ \
.shift = { \
.type = TSParseActionTypeShift, \
.state = state_value, \
.state = (state_value), \
.repetition = true \
} \
}}

View file

@ -5,7 +5,7 @@ static void *ts_malloc_default(size_t size) {
void *result = malloc(size);
if (size > 0 && !result) {
fprintf(stderr, "tree-sitter failed to allocate %zu bytes", size);
exit(1);
abort();
}
return result;
}
@ -14,7 +14,7 @@ static void *ts_calloc_default(size_t count, size_t size) {
void *result = calloc(count, size);
if (count > 0 && !result) {
fprintf(stderr, "tree-sitter failed to allocate %zu bytes", count * size);
exit(1);
abort();
}
return result;
}
@ -23,7 +23,7 @@ static void *ts_realloc_default(void *buffer, size_t size) {
void *result = realloc(buffer, size);
if (size > 0 && !result) {
fprintf(stderr, "tree-sitter failed to reallocate %zu bytes", size);
exit(1);
abort();
}
return result;
}
@ -35,10 +35,10 @@ void *(*ts_current_realloc)(void *, size_t) = ts_realloc_default;
void (*ts_current_free)(void *) = free;
void ts_set_allocator(
void *(*new_malloc)(size_t),
void *(*new_calloc)(size_t, size_t),
void *(*new_realloc)(void *, size_t),
void (*new_free)(void *)
void *(*new_malloc)(size_t size),
void *(*new_calloc)(size_t count, size_t size),
void *(*new_realloc)(void *ptr, size_t size),
void (*new_free)(void *ptr)
) {
ts_current_malloc = new_malloc ? new_malloc : ts_malloc_default;
ts_current_calloc = new_calloc ? new_calloc : ts_calloc_default;

View file

@ -25,8 +25,8 @@ extern "C" {
#define array_new() \
{ NULL, 0, 0 }
#define array_get(self, index) \
(assert((uint32_t)index < (self)->size), &(self)->contents[index])
#define array_get(self, _index) \
(assert((uint32_t)(_index) < (self)->size), &(self)->contents[_index])
#define array_front(self) array_get(self, 0)
@ -38,7 +38,7 @@ extern "C" {
array__reserve((VoidArray *)(self), array__elem_size(self), new_capacity)
// Free any memory allocated for this array.
#define array_delete(self) array__delete((VoidArray *)self)
#define array_delete(self) array__delete((VoidArray *)(self))
#define array_push(self, element) \
(array__grow((VoidArray *)(self), 1, array__elem_size(self)), \
@ -65,19 +65,19 @@ extern "C" {
// Remove `old_count` elements from the array starting at the given `index`. At
// the same index, insert `new_count` new elements, reading their values from the
// `new_contents` pointer.
#define array_splice(self, index, old_count, new_count, new_contents) \
#define array_splice(self, _index, old_count, new_count, new_contents) \
array__splice( \
(VoidArray *)(self), array__elem_size(self), index, \
(VoidArray *)(self), array__elem_size(self), _index, \
old_count, new_count, new_contents \
)
// Insert one `element` into the array at the given `index`.
#define array_insert(self, index, element) \
array__splice((VoidArray *)(self), array__elem_size(self), index, 0, 1, &element)
#define array_insert(self, _index, element) \
array__splice((VoidArray *)(self), array__elem_size(self), _index, 0, 1, &(element))
// Remove one `element` from the array at the given `index`.
#define array_erase(self, index) \
array__erase((VoidArray *)(self), array__elem_size(self), index)
#define array_erase(self, _index) \
array__erase((VoidArray *)(self), array__elem_size(self), _index)
#define array_pop(self) ((self)->contents[--(self)->size])
@ -95,23 +95,23 @@ extern "C" {
// out-parameter is set to true. Otherwise, `index` is set to an index where
// `needle` should be inserted in order to preserve the sorting, and `exists`
// is set to false.
#define array_search_sorted_with(self, compare, needle, index, exists) \
array__search_sorted(self, 0, compare, , needle, index, exists)
#define array_search_sorted_with(self, compare, needle, _index, _exists) \
array__search_sorted(self, 0, compare, , needle, _index, _exists)
// Search a sorted array for a given `needle` value, using integer comparisons
// of a given struct field (specified with a leading dot) to determine the order.
//
// See also `array_search_sorted_with`.
#define array_search_sorted_by(self, field, needle, index, exists) \
array__search_sorted(self, 0, _compare_int, field, needle, index, exists)
#define array_search_sorted_by(self, field, needle, _index, _exists) \
array__search_sorted(self, 0, compare_int, field, needle, _index, _exists)
// Insert a given `value` into a sorted array, using the given `compare`
// callback to determine the order.
#define array_insert_sorted_with(self, compare, value) \
do { \
unsigned index, exists; \
array_search_sorted_with(self, compare, &(value), &index, &exists); \
if (!exists) array_insert(self, index, value); \
unsigned _index, _exists; \
array_search_sorted_with(self, compare, &(value), &_index, &_exists); \
if (!_exists) array_insert(self, _index, value); \
} while (0)
// Insert a given `value` into a sorted array, using integer comparisons of
@ -120,9 +120,9 @@ extern "C" {
// See also `array_search_sorted_by`.
#define array_insert_sorted_by(self, field, value) \
do { \
unsigned index, exists; \
array_search_sorted_by(self, field, (value) field, &index, &exists); \
if (!exists) array_insert(self, index, value); \
unsigned _index, _exists; \
array_search_sorted_by(self, field, (value) field, &_index, &_exists); \
if (!_exists) array_insert(self, _index, value); \
} while (0)
// Private
@ -132,10 +132,12 @@ typedef Array(void) VoidArray;
#define array__elem_size(self) sizeof(*(self)->contents)
static inline void array__delete(VoidArray *self) {
ts_free(self->contents);
self->contents = NULL;
self->size = 0;
self->capacity = 0;
if (self->contents) {
ts_free(self->contents);
self->contents = NULL;
self->size = 0;
self->capacity = 0;
}
}
static inline void array__erase(VoidArray *self, size_t element_size,
@ -217,28 +219,28 @@ static inline void array__splice(VoidArray *self, size_t element_size,
}
// A binary search routine, based on Rust's `std::slice::binary_search_by`.
#define array__search_sorted(self, start, compare, suffix, needle, index, exists) \
#define array__search_sorted(self, start, compare, suffix, needle, _index, _exists) \
do { \
*(index) = start; \
*(exists) = false; \
uint32_t size = (self)->size - *(index); \
*(_index) = start; \
*(_exists) = false; \
uint32_t size = (self)->size - *(_index); \
if (size == 0) break; \
int comparison; \
while (size > 1) { \
uint32_t half_size = size / 2; \
uint32_t mid_index = *(index) + half_size; \
uint32_t mid_index = *(_index) + half_size; \
comparison = compare(&((self)->contents[mid_index] suffix), (needle)); \
if (comparison <= 0) *(index) = mid_index; \
if (comparison <= 0) *(_index) = mid_index; \
size -= half_size; \
} \
comparison = compare(&((self)->contents[*(index)] suffix), (needle)); \
if (comparison == 0) *(exists) = true; \
else if (comparison < 0) *(index) += 1; \
comparison = compare(&((self)->contents[*(_index)] suffix), (needle)); \
if (comparison == 0) *(_exists) = true; \
else if (comparison < 0) *(_index) += 1; \
} while (0)
// Helper macro for the `_sorted_by` routines below. This takes the left (existing)
// parameter by reference in order to work with the generic sorting function above.
#define _compare_int(a, b) ((int)*(a) - (int)(b))
#define compare_int(a, b) ((int)*(a) - (int)(b))
#ifdef __cplusplus
}

View file

@ -1,6 +1,7 @@
#ifndef TREE_SITTER_ATOMIC_H_
#define TREE_SITTER_ATOMIC_H_
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
@ -47,11 +48,19 @@ static inline size_t atomic_load(const volatile size_t *p) {
}
static inline uint32_t atomic_inc(volatile uint32_t *p) {
return __sync_add_and_fetch(p, 1u);
#ifdef __ATOMIC_RELAXED
return __atomic_add_fetch(p, 1U, __ATOMIC_SEQ_CST);
#else
return __sync_add_and_fetch(p, 1U);
#endif
}
static inline uint32_t atomic_dec(volatile uint32_t *p) {
return __sync_sub_and_fetch(p, 1u);
#ifdef __ATOMIC_RELAXED
return __atomic_sub_fetch(p, 1U, __ATOMIC_SEQ_CST);
#else
return __sync_sub_and_fetch(p, 1U);
#endif
}
#endif

View file

@ -1,6 +1,7 @@
#ifndef TREE_SITTER_CLOCK_H_
#define TREE_SITTER_CLOCK_H_
#include <stdbool.h>
#include <stdint.h>
typedef uint64_t TSDuration;
@ -82,6 +83,10 @@ static inline TSClock clock_after(TSClock base, TSDuration duration) {
TSClock result = base;
result.tv_sec += duration / 1000000;
result.tv_nsec += (duration % 1000000) * 1000;
if (result.tv_nsec >= 1000000000) {
result.tv_nsec -= 1000000000;
++(result.tv_sec);
}
return result;
}

View file

@ -210,7 +210,7 @@ static void iterator_ascend(Iterator *self) {
static bool iterator_descend(Iterator *self, uint32_t goal_position) {
if (self->in_padding) return false;
bool did_descend;
bool did_descend = false;
do {
did_descend = false;
TreeCursorEntry entry = *array_back(&self->cursor.stack);

View file

@ -7,6 +7,10 @@ uint32_t ts_language_symbol_count(const TSLanguage *self) {
return self->symbol_count + self->alias_count;
}
uint32_t ts_language_state_count(const TSLanguage *self) {
return self->state_count;
}
uint32_t ts_language_version(const TSLanguage *self) {
return self->version;
}
@ -56,6 +60,28 @@ TSSymbol ts_language_public_symbol(
return self->public_symbol_map[symbol];
}
TSStateId ts_language_next_state(
const TSLanguage *self,
TSStateId state,
TSSymbol symbol
) {
if (symbol == ts_builtin_sym_error || symbol == ts_builtin_sym_error_repeat) {
return 0;
} else if (symbol < self->token_count) {
uint32_t count;
const TSParseAction *actions = ts_language_actions(self, state, symbol, &count);
if (count > 0) {
TSParseAction action = actions[count - 1];
if (action.type == TSParseActionTypeShift) {
return action.shift.extra ? state : action.shift.state;
}
}
return 0;
} else {
return ts_language_lookup(self, state, symbol);
}
}
const char *ts_language_symbol_name(
const TSLanguage *self,
TSSymbol symbol
@ -78,7 +104,7 @@ TSSymbol ts_language_symbol_for_name(
bool is_named
) {
if (!strncmp(string, "ERROR", length)) return ts_builtin_sym_error;
uint32_t count = ts_language_symbol_count(self);
uint16_t count = (uint16_t)ts_language_symbol_count(self);
for (TSSymbol i = 0; i < count; i++) {
TSSymbolMetadata metadata = ts_language_symbol_metadata(self, i);
if ((!metadata.visible && !metadata.supertype) || metadata.named != is_named) continue;
@ -121,7 +147,7 @@ TSFieldId ts_language_field_id_for_name(
const char *name,
uint32_t name_length
) {
uint32_t count = ts_language_field_count(self);
uint16_t count = (uint16_t)ts_language_field_count(self);
for (TSSymbol i = 1; i < count + 1; i++) {
switch (strncmp(name, self->field_names[i], name_length)) {
case 0:
@ -135,3 +161,48 @@ TSFieldId ts_language_field_id_for_name(
}
return 0;
}
TSLookaheadIterator *ts_lookahead_iterator_new(const TSLanguage *self, TSStateId state) {
if (state >= self->state_count) return NULL;
LookaheadIterator *iterator = ts_malloc(sizeof(LookaheadIterator));
*iterator = ts_language_lookaheads(self, state);
return (TSLookaheadIterator *)iterator;
}
void ts_lookahead_iterator_delete(TSLookaheadIterator *self) {
ts_free(self);
}
bool ts_lookahead_iterator_reset_state(TSLookaheadIterator * self, TSStateId state) {
LookaheadIterator *iterator = (LookaheadIterator *)self;
if (state >= iterator->language->state_count) return false;
*iterator = ts_language_lookaheads(iterator->language, state);
return true;
}
const TSLanguage *ts_lookahead_iterator_language(const TSLookaheadIterator *self) {
const LookaheadIterator *iterator = (const LookaheadIterator *)self;
return iterator->language;
}
bool ts_lookahead_iterator_reset(TSLookaheadIterator *self, const TSLanguage *language, TSStateId state) {
if (state >= language->state_count) return false;
LookaheadIterator *iterator = (LookaheadIterator *)self;
*iterator = ts_language_lookaheads(language, state);
return true;
}
bool ts_lookahead_iterator_next(TSLookaheadIterator *self) {
LookaheadIterator *iterator = (LookaheadIterator *)self;
return ts_lookahead_iterator__next(iterator);
}
TSSymbol ts_lookahead_iterator_current_symbol(const TSLookaheadIterator *self) {
const LookaheadIterator *iterator = (const LookaheadIterator *)self;
return iterator->symbol;
}
const char *ts_lookahead_iterator_current_symbol_name(const TSLookaheadIterator *self) {
const LookaheadIterator *iterator = (const LookaheadIterator *)self;
return ts_language_symbol_name(iterator->language, iterator->symbol);
}

View file

@ -38,6 +38,8 @@ TSSymbolMetadata ts_language_symbol_metadata(const TSLanguage *, TSSymbol);
TSSymbol ts_language_public_symbol(const TSLanguage *, TSSymbol);
TSStateId ts_language_next_state(const TSLanguage *self, TSStateId state, TSSymbol symbol);
static inline bool ts_language_is_symbol_external(const TSLanguage *self, TSSymbol symbol) {
return 0 < symbol && symbol < self->external_token_count + 1;
}
@ -83,7 +85,7 @@ static inline uint16_t ts_language_lookup(
for (unsigned i = 0; i < group_count; i++) {
uint16_t section_value = *(data++);
uint16_t symbol_count = *(data++);
for (unsigned i = 0; i < symbol_count; i++) {
for (unsigned j = 0; j < symbol_count; j++) {
if (*(data++) == symbol) return section_value;
}
}
@ -134,7 +136,7 @@ static inline LookaheadIterator ts_language_lookaheads(
};
}
static inline bool ts_lookahead_iterator_next(LookaheadIterator *self) {
static inline bool ts_lookahead_iterator__next(LookaheadIterator *self) {
// For small parse states, valid symbols are listed explicitly,
// grouped by their value. There's no need to look up the actions
// again until moving to the next group.
@ -178,28 +180,6 @@ static inline bool ts_lookahead_iterator_next(LookaheadIterator *self) {
return true;
}
static inline TSStateId ts_language_next_state(
const TSLanguage *self,
TSStateId state,
TSSymbol symbol
) {
if (symbol == ts_builtin_sym_error || symbol == ts_builtin_sym_error_repeat) {
return 0;
} else if (symbol < self->token_count) {
uint32_t count;
const TSParseAction *actions = ts_language_actions(self, state, symbol, &count);
if (count > 0) {
TSParseAction action = actions[count - 1];
if (action.type == TSParseActionTypeShift) {
return action.shift.extra ? state : action.shift.state;
}
}
return 0;
} else {
return ts_language_lookup(self, state, symbol);
}
}
// Whether the state is a "primary state". If this returns false, it indicates that there exists
// another state that behaves identically to this one with respect to query analysis.
static inline bool ts_language_state_is_primary(
@ -269,17 +249,17 @@ static inline void ts_language_aliases_for_symbol(
*start = &self->public_symbol_map[original_symbol];
*end = *start + 1;
unsigned i = 0;
unsigned idx = 0;
for (;;) {
TSSymbol symbol = self->alias_map[i++];
TSSymbol symbol = self->alias_map[idx++];
if (symbol == 0 || symbol > original_symbol) break;
uint16_t count = self->alias_map[i++];
uint16_t count = self->alias_map[idx++];
if (symbol == original_symbol) {
*start = &self->alias_map[i];
*end = &self->alias_map[i + count];
*start = &self->alias_map[idx];
*end = &self->alias_map[idx + count];
break;
}
i += count;
idx += count;
}
}
@ -289,21 +269,21 @@ static inline void ts_language_write_symbol_as_dot_string(
TSSymbol symbol
) {
const char *name = ts_language_symbol_name(self, symbol);
for (const char *c = name; *c; c++) {
switch (*c) {
for (const char *chr = name; *chr; chr++) {
switch (*chr) {
case '"':
case '\\':
fputc('\\', f);
fputc(*c, f);
fputc(*chr, f);
break;
case '\n':
fputs("\\n", f);
break;
case '\t':
fputs("\\n", f);
fputs("\\t", f);
break;
default:
fputc(*c, f);
fputc(*chr, f);
break;
}
}

View file

@ -172,7 +172,9 @@ static void ts_lexer__do_advance(Lexer *self, bool skip) {
self->current_position.bytes >= current_range->end_byte ||
current_range->end_byte == current_range->start_byte
) {
self->current_included_range_index++;
if (self->current_included_range_index < self->included_range_count) {
self->current_included_range_index++;
}
if (self->current_included_range_index < self->included_range_count) {
current_range++;
self->current_position = (Length) {
@ -209,11 +211,11 @@ static void ts_lexer__advance(TSLexer *_self, bool skip) {
if (!self->chunk) return;
if (skip) {
LOG("skip", self->data.lookahead);
LOG("skip", self->data.lookahead)
} else {
LOG("consume", self->data.lookahead);
LOG("consume", self->data.lookahead)
}
ts_lexer__do_advance(self, skip);
}
@ -245,9 +247,9 @@ static void ts_lexer__mark_end(TSLexer *_self) {
static uint32_t ts_lexer__get_column(TSLexer *_self) {
Lexer *self = (Lexer *)_self;
uint32_t goal_byte = self->current_position.bytes;
self->did_get_column = true;
self->current_position.bytes -= self->current_position.extent.column;
self->current_position.extent.column = 0;
@ -257,10 +259,13 @@ static uint32_t ts_lexer__get_column(TSLexer *_self) {
}
uint32_t result = 0;
ts_lexer__get_lookahead(self);
while (self->current_position.bytes < goal_byte && !ts_lexer__eof(_self) && self->chunk) {
ts_lexer__do_advance(self, false);
result++;
if (!ts_lexer__eof(_self)) {
ts_lexer__get_lookahead(self);
while (self->current_position.bytes < goal_byte && self->chunk) {
result++;
ts_lexer__do_advance(self, false);
if (ts_lexer__eof(_self)) break;
}
}
return result;

View file

@ -237,6 +237,8 @@ static inline TSNode ts_node__prev_sibling(TSNode self, bool include_anonymous)
return earlier_node;
} else {
node = earlier_node;
earlier_node = ts_node__null();
earlier_node_is_relevant = false;
}
}
@ -423,6 +425,19 @@ const char *ts_node_type(TSNode self) {
return ts_language_symbol_name(self.tree->language, symbol);
}
const TSLanguage *ts_node_language(TSNode self) {
return self.tree->language;
}
TSSymbol ts_node_grammar_symbol(TSNode self) {
return ts_subtree_symbol(ts_node__subtree(self));
}
const char *ts_node_grammar_type(TSNode self) {
TSSymbol symbol = ts_subtree_symbol(ts_node__subtree(self));
return ts_language_symbol_name(self.tree->language, symbol);
}
char *ts_node_string(TSNode self) {
return ts_subtree_string(ts_node__subtree(self), self.tree->language, false);
}
@ -458,6 +473,29 @@ bool ts_node_has_error(TSNode self) {
return ts_subtree_error_cost(ts_node__subtree(self)) > 0;
}
bool ts_node_is_error(TSNode self) {
TSSymbol symbol = ts_node_symbol(self);
return symbol == ts_builtin_sym_error;
}
uint32_t ts_node_descendant_count(TSNode self) {
return ts_subtree_visible_descendant_count(ts_node__subtree(self)) + 1;
}
TSStateId ts_node_parse_state(TSNode self) {
return ts_subtree_parse_state(ts_node__subtree(self));
}
TSStateId ts_node_next_parse_state(TSNode self) {
const TSLanguage *language = self.tree->language;
uint16_t state = ts_node_parse_state(self);
if (state == TS_TREE_STATE_NONE) {
return TS_TREE_STATE_NONE;
}
uint16_t symbol = ts_node_grammar_symbol(self);
return ts_language_next_state(language, state, symbol);
}
TSNode ts_node_parent(TSNode self) {
TSNode node = ts_tree_root_node(self.tree);
uint32_t end_byte = ts_node_end_byte(self);
@ -569,24 +607,58 @@ recur:
return ts_node__null();
}
const char *ts_node_field_name_for_child(TSNode self, uint32_t child_index) {
const TSFieldMapEntry *field_map_start = NULL, *field_map_end = NULL;
if (!ts_node_child_count(self)) {
static inline const char *ts_node__field_name_from_language(TSNode self, uint32_t structural_child_index) {
const TSFieldMapEntry *field_map, *field_map_end;
ts_language_field_map(
self.tree->language,
ts_node__subtree(self).ptr->production_id,
&field_map,
&field_map_end
);
for (; field_map != field_map_end; field_map++) {
if (!field_map->inherited && field_map->child_index == structural_child_index) {
return self.tree->language->field_names[field_map->field_id];
}
}
return NULL;
}
}
ts_language_field_map(
self.tree->language,
ts_node__subtree(self).ptr->production_id,
&field_map_start,
&field_map_end
);
const char *ts_node_field_name_for_child(TSNode self, uint32_t child_index) {
TSNode result = self;
bool did_descend = true;
const char *inherited_field_name = NULL;
for (const TSFieldMapEntry *i = field_map_start; i < field_map_end; i++) {
if (i->child_index == child_index) {
return self.tree->language->field_names[i->field_id];
while (did_descend) {
did_descend = false;
TSNode child;
uint32_t index = 0;
NodeChildIterator iterator = ts_node_iterate_children(&result);
while (ts_node_child_iterator_next(&iterator, &child)) {
if (ts_node__is_relevant(child, true)) {
if (index == child_index) {
const char *field_name = ts_node__field_name_from_language(result, iterator.structural_child_index - 1);
if (field_name) return field_name;
return inherited_field_name;
}
index++;
} else {
uint32_t grandchild_index = child_index - index;
uint32_t grandchild_count = ts_node__relevant_child_count(child, true);
if (grandchild_index < grandchild_count) {
const char *field_name = ts_node__field_name_from_language(result, iterator.structural_child_index - 1);
if (field_name) inherited_field_name = field_name;
did_descend = true;
result = child;
child_index = grandchild_index;
break;
}
index += grandchild_count;
}
}
}
return NULL;
}

View file

@ -134,10 +134,10 @@ typedef struct {
static const char *ts_string_input_read(
void *_self,
uint32_t byte,
TSPoint pt,
TSPoint point,
uint32_t *length
) {
(void)pt;
(void)point;
TSStringInput *self = (TSStringInput *)_self;
if (byte >= self->length) {
*length = 0;
@ -161,9 +161,9 @@ static void ts_parser__log(TSParser *self) {
if (self->dot_graph_file) {
fprintf(self->dot_graph_file, "graph {\nlabel=\"");
for (char *c = &self->lexer.debug_buffer[0]; *c != 0; c++) {
if (*c == '"' || *c == '\\') fputc('\\', self->dot_graph_file);
fputc(*c, self->dot_graph_file);
for (char *chr = &self->lexer.debug_buffer[0]; *chr != 0; chr++) {
if (*chr == '"' || *chr == '\\') fputc('\\', self->dot_graph_file);
fputc(*chr, self->dot_graph_file);
}
fprintf(self->dot_graph_file, "\"\n}\n\n");
}
@ -957,19 +957,19 @@ static StackVersion ts_parser__reduce(
if (next_slice.version != slice.version) break;
i++;
SubtreeArray children = next_slice.subtrees;
ts_subtree_array_remove_trailing_extras(&children, &self->trailing_extras2);
SubtreeArray next_slice_children = next_slice.subtrees;
ts_subtree_array_remove_trailing_extras(&next_slice_children, &self->trailing_extras2);
if (ts_parser__select_children(
self,
ts_subtree_from_mut(parent),
&children
&next_slice_children
)) {
ts_subtree_array_clear(&self->tree_pool, &self->trailing_extras);
ts_subtree_release(&self->tree_pool, ts_subtree_from_mut(parent));
array_swap(&self->trailing_extras, &self->trailing_extras2);
parent = ts_subtree_new_node(
symbol, &children, production_id, self->language
symbol, &next_slice_children, production_id, self->language
);
} else {
array_clear(&self->trailing_extras2);
@ -1080,8 +1080,8 @@ static bool ts_parser__do_all_potential_reductions(
if (version >= version_count) break;
bool merged = false;
for (StackVersion i = initial_version_count; i < version; i++) {
if (ts_stack_merge(self->stack, i, version)) {
for (StackVersion j = initial_version_count; j < version; j++) {
if (ts_stack_merge(self->stack, j, version)) {
merged = true;
break;
}
@ -1104,8 +1104,8 @@ static bool ts_parser__do_all_potential_reductions(
for (TSSymbol symbol = first_symbol; symbol < end_symbol; symbol++) {
TableEntry entry;
ts_language_table_entry(self->language, state, symbol, &entry);
for (uint32_t i = 0; i < entry.action_count; i++) {
TSParseAction action = entry.actions[i];
for (uint32_t j = 0; j < entry.action_count; j++) {
TSParseAction action = entry.actions[j];
switch (action.type) {
case TSParseActionTypeShift:
case TSParseActionTypeRecover:
@ -1127,8 +1127,8 @@ static bool ts_parser__do_all_potential_reductions(
}
StackVersion reduction_version = STACK_VERSION_NONE;
for (uint32_t i = 0; i < self->reduce_actions.size; i++) {
ReduceAction action = self->reduce_actions.contents[i];
for (uint32_t j = 0; j < self->reduce_actions.size; j++) {
ReduceAction action = self->reduce_actions.contents[j];
reduction_version = ts_parser__reduce(
self, version, action.symbol, action.count,
@ -1414,7 +1414,7 @@ static void ts_parser__handle_error(
TSStateId state = ts_stack_state(self->stack, v);
for (
TSSymbol missing_symbol = 1;
missing_symbol < self->language->token_count;
missing_symbol < (uint16_t)self->language->token_count;
missing_symbol++
) {
TSStateId state_after_missing_symbol = ts_language_next_state(
@ -1905,7 +1905,11 @@ void ts_parser_print_dot_graphs(TSParser *self, int fd) {
}
if (fd >= 0) {
#ifdef _WIN32
self->dot_graph_file = _fdopen(fd, "a");
#else
self->dot_graph_file = fdopen(fd, "a");
#endif
} else {
self->dot_graph_file = NULL;
}
@ -2039,8 +2043,16 @@ TSTree *ts_parser_parse(
}
}
// After advancing each version of the stack, re-sort the versions by their cost,
// removing any versions that are no longer worth pursuing.
unsigned min_error_cost = ts_parser__condense_stack(self);
// If there's already a finished parse tree that's better than any in-progress version,
// then terminate parsing. Clear the parse stack to remove any extra references to subtrees
// within the finished tree, ensuring that these subtrees can be safely mutated in-place
// for rebalancing.
if (self->finished_tree.ptr && ts_subtree_error_cost(self->finished_tree) < min_error_cost) {
ts_stack_clear(self->stack);
break;
}

File diff suppressed because it is too large Load diff

View file

@ -120,6 +120,20 @@ recur:
}
}
/// Get the number of nodes in the subtree, for the purpose of measuring
/// how much progress has been made by a given version of the stack.
static uint32_t stack__subtree_node_count(Subtree subtree) {
uint32_t count = ts_subtree_visible_descendant_count(subtree);
if (ts_subtree_visible(subtree)) count++;
// Count intermediate error nodes even though they are not visible,
// because a stack version's node count is used to check whether it
// has made any progress since the last time it encountered an error.
if (ts_subtree_symbol(subtree) == ts_builtin_sym_error_repeat) count++;
return count;
}
static StackNode *stack_node_new(
StackNode *previous_node,
Subtree subtree,
@ -152,7 +166,7 @@ static StackNode *stack_node_new(
if (subtree.ptr) {
node->error_cost += ts_subtree_error_cost(subtree);
node->position = length_add(node->position, ts_subtree_total_size(subtree));
node->node_count += ts_subtree_node_count(subtree);
node->node_count += stack__subtree_node_count(subtree);
node->dynamic_precedence += ts_subtree_dynamic_precedence(subtree);
}
} else {
@ -239,7 +253,7 @@ static void stack_node_add_link(
if (link.subtree.ptr) {
ts_subtree_retain(link.subtree);
node_count += ts_subtree_node_count(link.subtree);
node_count += stack__subtree_node_count(link.subtree);
dynamic_precedence += ts_subtree_dynamic_precedence(link.subtree);
}
@ -305,7 +319,7 @@ static void ts_stack__add_slice(
array_push(&self->slices, slice);
}
inline StackSliceArray stack__iter(
static StackSliceArray stack__iter(
Stack *self,
StackVersion version,
StackCallback callback,
@ -316,7 +330,7 @@ inline StackSliceArray stack__iter(
array_clear(&self->iterators);
StackHead *head = array_get(&self->heads, version);
StackIterator iterator = {
StackIterator new_iterator = {
.node = head->node,
.subtrees = array_new(),
.subtree_count = 0,
@ -326,10 +340,10 @@ inline StackSliceArray stack__iter(
bool include_subtrees = false;
if (goal_subtree_count >= 0) {
include_subtrees = true;
array_reserve(&iterator.subtrees, (uint32_t)ts_subtree_alloc_size(goal_subtree_count) / sizeof(Subtree));
array_reserve(&new_iterator.subtrees, (uint32_t)ts_subtree_alloc_size(goal_subtree_count) / sizeof(Subtree));
}
array_push(&self->iterators, iterator);
array_push(&self->iterators, new_iterator);
while (self->iterators.size > 0) {
for (uint32_t i = 0, size = self->iterators.size; i < size; i++) {
@ -505,7 +519,7 @@ inline StackAction pop_count_callback(void *payload, const StackIterator *iterat
}
StackSliceArray ts_stack_pop_count(Stack *self, StackVersion version, uint32_t count) {
return stack__iter(self, version, pop_count_callback, &count, count);
return stack__iter(self, version, pop_count_callback, &count, (int)count);
}
inline StackAction pop_pending_callback(void *payload, const StackIterator *iterator) {

View file

@ -56,10 +56,10 @@ const char *ts_external_scanner_state_data(const ExternalScannerState *self) {
}
}
bool ts_external_scanner_state_eq(const ExternalScannerState *a, const char *buffer, unsigned length) {
bool ts_external_scanner_state_eq(const ExternalScannerState *self, const char *buffer, unsigned length) {
return
a->length == length &&
memcmp(ts_external_scanner_state_data(a), buffer, length) == 0;
self->length == length &&
memcmp(ts_external_scanner_state_data(self), buffer, length) == 0;
}
// SubtreeArray
@ -348,7 +348,7 @@ void ts_subtree_balance(Subtree self, SubtreePool *pool, const TSLanguage *langu
Subtree child2 = ts_subtree_children(tree)[tree.ptr->child_count - 1];
long repeat_delta = (long)ts_subtree_repeat_depth(child1) - (long)ts_subtree_repeat_depth(child2);
if (repeat_delta > 0) {
unsigned n = repeat_delta;
unsigned n = (unsigned)repeat_delta;
for (unsigned i = n / 2; i > 0; i /= 2) {
ts_subtree__compress(tree, i, language, &pool->tree_stack);
n -= i;
@ -376,7 +376,7 @@ void ts_subtree_summarize_children(
self.ptr->visible_child_count = 0;
self.ptr->error_cost = 0;
self.ptr->repeat_depth = 0;
self.ptr->node_count = 1;
self.ptr->visible_descendant_count = 0;
self.ptr->has_external_tokens = false;
self.ptr->depends_on_column = false;
self.ptr->has_external_scanner_state_change = false;
@ -435,14 +435,16 @@ void ts_subtree_summarize_children(
}
self.ptr->dynamic_precedence += ts_subtree_dynamic_precedence(child);
self.ptr->node_count += ts_subtree_node_count(child);
self.ptr->visible_descendant_count += ts_subtree_visible_descendant_count(child);
if (alias_sequence && alias_sequence[structural_index] != 0 && !ts_subtree_extra(child)) {
self.ptr->visible_descendant_count++;
self.ptr->visible_child_count++;
if (ts_language_symbol_metadata(language, alias_sequence[structural_index]).named) {
self.ptr->named_child_count++;
}
} else if (ts_subtree_visible(child)) {
self.ptr->visible_descendant_count++;
self.ptr->visible_child_count++;
if (ts_subtree_named(child)) self.ptr->named_child_count++;
} else if (grandchild_count > 0) {
@ -513,7 +515,7 @@ MutableSubtree ts_subtree_new_node(
size_t new_byte_size = ts_subtree_alloc_size(children->size);
if (children->capacity * sizeof(Subtree) < new_byte_size) {
children->contents = ts_realloc(children->contents, new_byte_size);
children->capacity = new_byte_size / sizeof(Subtree);
children->capacity = (uint32_t)(new_byte_size / sizeof(Subtree));
}
SubtreeHeapData *data = (SubtreeHeapData *)&children->contents[children->size];
@ -529,7 +531,7 @@ MutableSubtree ts_subtree_new_node(
.fragile_right = fragile,
.is_keyword = false,
{{
.node_count = 0,
.visible_descendant_count = 0,
.production_id = production_id,
.first_leaf = {.symbol = 0, .parse_state = 0},
}}
@ -641,24 +643,24 @@ static inline void ts_subtree_set_has_changes(MutableSubtree *self) {
}
}
Subtree ts_subtree_edit(Subtree self, const TSInputEdit *edit, SubtreePool *pool) {
Subtree ts_subtree_edit(Subtree self, const TSInputEdit *input_edit, SubtreePool *pool) {
typedef struct {
Subtree *tree;
Edit edit;
} StackEntry;
} EditEntry;
Array(StackEntry) stack = array_new();
array_push(&stack, ((StackEntry) {
Array(EditEntry) stack = array_new();
array_push(&stack, ((EditEntry) {
.tree = &self,
.edit = (Edit) {
.start = {edit->start_byte, edit->start_point},
.old_end = {edit->old_end_byte, edit->old_end_point},
.new_end = {edit->new_end_byte, edit->new_end_point},
.start = {input_edit->start_byte, input_edit->start_point},
.old_end = {input_edit->old_end_byte, input_edit->old_end_point},
.new_end = {input_edit->new_end_byte, input_edit->new_end_point},
},
}));
while (stack.size) {
StackEntry entry = array_pop(&stack);
EditEntry entry = array_pop(&stack);
Edit edit = entry.edit;
bool is_noop = edit.old_end.bytes == edit.start.bytes && edit.new_end.bytes == edit.start.bytes;
bool is_pure_insertion = edit.old_end.bytes == edit.start.bytes;
@ -786,7 +788,7 @@ Subtree ts_subtree_edit(Subtree self, const TSInputEdit *edit, SubtreePool *pool
}
// Queue processing of this child's subtree.
array_push(&stack, ((StackEntry) {
array_push(&stack, ((EditEntry) {
.tree = child,
.edit = child_edit,
}));
@ -811,24 +813,24 @@ Subtree ts_subtree_last_external_token(Subtree tree) {
return tree;
}
static size_t ts_subtree__write_char_to_string(char *s, size_t n, int32_t c) {
if (c == -1)
return snprintf(s, n, "INVALID");
else if (c == '\0')
return snprintf(s, n, "'\\0'");
else if (c == '\n')
return snprintf(s, n, "'\\n'");
else if (c == '\t')
return snprintf(s, n, "'\\t'");
else if (c == '\r')
return snprintf(s, n, "'\\r'");
else if (0 < c && c < 128 && isprint(c))
return snprintf(s, n, "'%c'", c);
static size_t ts_subtree__write_char_to_string(char *str, size_t n, int32_t chr) {
if (chr == -1)
return snprintf(str, n, "INVALID");
else if (chr == '\0')
return snprintf(str, n, "'\\0'");
else if (chr == '\n')
return snprintf(str, n, "'\\n'");
else if (chr == '\t')
return snprintf(str, n, "'\\t'");
else if (chr == '\r')
return snprintf(str, n, "'\\r'");
else if (0 < chr && chr < 128 && isprint(chr))
return snprintf(str, n, "'%c'", chr);
else
return snprintf(s, n, "%d", c);
return snprintf(str, n, "%d", chr);
}
static const char *ROOT_FIELD = "__ROOT__";
static const char *const ROOT_FIELD = "__ROOT__";
static size_t ts_subtree__write_to_string(
Subtree self, char *string, size_t limit,
@ -900,17 +902,17 @@ static size_t ts_subtree__write_to_string(
0, false, NULL
);
} else {
TSSymbol alias_symbol = alias_sequence
TSSymbol subtree_alias_symbol = alias_sequence
? alias_sequence[structural_child_index]
: 0;
bool alias_is_named = alias_symbol
? ts_language_symbol_metadata(language, alias_symbol).named
bool subtree_alias_is_named = subtree_alias_symbol
? ts_language_symbol_metadata(language, subtree_alias_symbol).named
: false;
const char *child_field_name = is_visible ? NULL : field_name;
for (const TSFieldMapEntry *i = field_map; i < field_map_end; i++) {
if (!i->inherited && i->child_index == structural_child_index) {
child_field_name = language->field_names[i->field_id];
for (const TSFieldMapEntry *map = field_map; map < field_map_end; map++) {
if (!map->inherited && map->child_index == structural_child_index) {
child_field_name = language->field_names[map->field_id];
break;
}
}
@ -918,7 +920,7 @@ static size_t ts_subtree__write_to_string(
cursor += ts_subtree__write_to_string(
child, *writer, limit,
language, include_all,
alias_symbol, alias_is_named, child_field_name
subtree_alias_symbol, subtree_alias_is_named, child_field_name
);
structural_child_index++;
}
@ -969,6 +971,7 @@ void ts_subtree__print_dot_graph(const Subtree *self, uint32_t start_offset,
"error-cost: %u\n"
"has-changes: %u\n"
"depends-on-column: %u\n"
"descendant-count: %u\n"
"repeat-depth: %u\n"
"lookahead-bytes: %u",
start_offset, end_offset,
@ -976,6 +979,7 @@ void ts_subtree__print_dot_graph(const Subtree *self, uint32_t start_offset,
ts_subtree_error_cost(*self),
ts_subtree_has_changes(*self),
ts_subtree_depends_on_column(*self),
ts_subtree_visible_descendant_count(*self),
ts_subtree_repeat_depth(*self),
ts_subtree_lookahead_bytes(*self)
);
@ -992,12 +996,12 @@ void ts_subtree__print_dot_graph(const Subtree *self, uint32_t start_offset,
ts_subtree_production_id(*self);
for (uint32_t i = 0, n = ts_subtree_child_count(*self); i < n; i++) {
const Subtree *child = &ts_subtree_children(*self)[i];
TSSymbol alias_symbol = 0;
TSSymbol subtree_alias_symbol = 0;
if (!ts_subtree_extra(*child) && child_info_offset) {
alias_symbol = language->alias_sequences[child_info_offset];
subtree_alias_symbol = language->alias_sequences[child_info_offset];
child_info_offset++;
}
ts_subtree__print_dot_graph(child, child_start_offset, language, alias_symbol, f);
ts_subtree__print_dot_graph(child, child_start_offset, language, subtree_alias_symbol, f);
fprintf(f, "tree_%p -> tree_%p [tooltip=%u]\n", (void *)self, (void *)child, i);
child_start_offset += ts_subtree_total_bytes(*child);
}
@ -1024,12 +1028,12 @@ const ExternalScannerState *ts_subtree_external_scanner_state(Subtree self) {
}
}
bool ts_subtree_external_scanner_state_eq(Subtree a, Subtree b) {
const ExternalScannerState *state_a = ts_subtree_external_scanner_state(a);
const ExternalScannerState *state_b = ts_subtree_external_scanner_state(b);
bool ts_subtree_external_scanner_state_eq(Subtree self, Subtree other) {
const ExternalScannerState *state_self = ts_subtree_external_scanner_state(self);
const ExternalScannerState *state_other = ts_subtree_external_scanner_state(other);
return ts_external_scanner_state_eq(
state_a,
ts_external_scanner_state_data(state_b),
state_b->length
state_self,
ts_external_scanner_state_data(state_other),
state_other->length
);
}

View file

@ -135,7 +135,7 @@ typedef struct {
struct {
uint32_t visible_child_count;
uint32_t named_child_count;
uint32_t node_count;
uint32_t visible_descendant_count;
int32_t dynamic_precedence;
uint16_t repeat_depth;
uint16_t production_id;
@ -175,7 +175,7 @@ typedef struct {
void ts_external_scanner_state_init(ExternalScannerState *, const char *, unsigned);
const char *ts_external_scanner_state_data(const ExternalScannerState *);
bool ts_external_scanner_state_eq(const ExternalScannerState *a, const char *, unsigned);
bool ts_external_scanner_state_eq(const ExternalScannerState *self, const char *, unsigned);
void ts_external_scanner_state_delete(ExternalScannerState *self);
void ts_subtree_array_copy(SubtreeArray, SubtreeArray *);
@ -212,7 +212,7 @@ Subtree ts_subtree_last_external_token(Subtree);
const ExternalScannerState *ts_subtree_external_scanner_state(Subtree self);
bool ts_subtree_external_scanner_state_eq(Subtree, Subtree);
#define SUBTREE_GET(self, name) (self.data.is_inline ? self.data.name : self.ptr->name)
#define SUBTREE_GET(self, name) ((self).data.is_inline ? (self).data.name : (self).ptr->name)
static inline TSSymbol ts_subtree_symbol(Subtree self) { return SUBTREE_GET(self, symbol); }
static inline bool ts_subtree_visible(Subtree self) { return SUBTREE_GET(self, visible); }
@ -291,8 +291,16 @@ static inline uint32_t ts_subtree_repeat_depth(Subtree self) {
return self.data.is_inline ? 0 : self.ptr->repeat_depth;
}
static inline uint32_t ts_subtree_node_count(Subtree self) {
return (self.data.is_inline || self.ptr->child_count == 0) ? 1 : self.ptr->node_count;
static inline uint32_t ts_subtree_is_repetition(Subtree self) {
return self.data.is_inline
? 0
: !self.ptr->named && !self.ptr->visible && self.ptr->child_count != 0;
}
static inline uint32_t ts_subtree_visible_descendant_count(Subtree self) {
return (self.data.is_inline || self.ptr->child_count == 0)
? 0
: self.ptr->visible_descendant_count;
}
static inline uint32_t ts_subtree_visible_child_count(Subtree self) {

View file

@ -98,23 +98,23 @@ TSRange *ts_tree_included_ranges(const TSTree *self, uint32_t *length) {
return ranges;
}
TSRange *ts_tree_get_changed_ranges(const TSTree *self, const TSTree *other, uint32_t *count) {
TSRange *ts_tree_get_changed_ranges(const TSTree *old_tree, const TSTree *new_tree, uint32_t *length) {
TreeCursor cursor1 = {NULL, array_new()};
TreeCursor cursor2 = {NULL, array_new()};
ts_tree_cursor_init(&cursor1, ts_tree_root_node(self));
ts_tree_cursor_init(&cursor2, ts_tree_root_node(other));
ts_tree_cursor_init(&cursor1, ts_tree_root_node(old_tree));
ts_tree_cursor_init(&cursor2, ts_tree_root_node(new_tree));
TSRangeArray included_range_differences = array_new();
ts_range_array_get_changed_ranges(
self->included_ranges, self->included_range_count,
other->included_ranges, other->included_range_count,
old_tree->included_ranges, old_tree->included_range_count,
new_tree->included_ranges, new_tree->included_range_count,
&included_range_differences
);
TSRange *result;
*count = ts_subtree_get_changed_ranges(
&self->root, &other->root, &cursor1, &cursor2,
self->language, &included_range_differences, &result
*length = ts_subtree_get_changed_ranges(
&old_tree->root, &new_tree->root, &cursor1, &cursor2,
old_tree->language, &included_range_differences, &result
);
array_delete(&included_range_differences);
@ -123,6 +123,21 @@ TSRange *ts_tree_get_changed_ranges(const TSTree *self, const TSTree *other, uin
return result;
}
void ts_tree_print_dot_graph(const TSTree *self, FILE *file) {
ts_subtree_print_dot_graph(self->root, self->language, file);
#ifdef _WIN32
void ts_tree_print_dot_graph(const TSTree *self, int fd) {
(void)self;
(void)fd;
}
#else
#include <unistd.h>
void ts_tree_print_dot_graph(const TSTree *self, int file_descriptor) {
FILE *file = fdopen(dup(file_descriptor), "a");
ts_subtree_print_dot_graph(self->root, self->language, file);
fclose(file);
}
#endif

View file

@ -1,6 +1,8 @@
#ifndef TREE_SITTER_TREE_H_
#define TREE_SITTER_TREE_H_
#include "./subtree.h"
#ifdef __cplusplus
extern "C" {
#endif

View file

@ -10,26 +10,50 @@ typedef struct {
Length position;
uint32_t child_index;
uint32_t structural_child_index;
uint32_t descendant_index;
const TSSymbol *alias_sequence;
} CursorChildIterator;
// CursorChildIterator
static inline bool ts_tree_cursor_is_entry_visible(const TreeCursor *self, uint32_t index) {
TreeCursorEntry *entry = &self->stack.contents[index];
if (index == 0 || ts_subtree_visible(*entry->subtree)) {
return true;
} else if (!ts_subtree_extra(*entry->subtree)) {
TreeCursorEntry *parent_entry = &self->stack.contents[index - 1];
return ts_language_alias_at(
self->tree->language,
parent_entry->subtree->ptr->production_id,
entry->structural_child_index
);
} else {
return false;
}
}
static inline CursorChildIterator ts_tree_cursor_iterate_children(const TreeCursor *self) {
TreeCursorEntry *last_entry = array_back(&self->stack);
if (ts_subtree_child_count(*last_entry->subtree) == 0) {
return (CursorChildIterator) {NULL_SUBTREE, self->tree, length_zero(), 0, 0, NULL};
return (CursorChildIterator) {NULL_SUBTREE, self->tree, length_zero(), 0, 0, 0, NULL};
}
const TSSymbol *alias_sequence = ts_language_alias_sequence(
self->tree->language,
last_entry->subtree->ptr->production_id
);
uint32_t descendant_index = last_entry->descendant_index;
if (ts_tree_cursor_is_entry_visible(self, self->stack.size - 1)) {
descendant_index += 1;
}
return (CursorChildIterator) {
.tree = self->tree,
.parent = *last_entry->subtree,
.position = last_entry->position,
.child_index = 0,
.structural_child_index = 0,
.descendant_index = descendant_index,
.alias_sequence = alias_sequence,
};
}
@ -46,14 +70,22 @@ static inline bool ts_tree_cursor_child_iterator_next(
.position = self->position,
.child_index = self->child_index,
.structural_child_index = self->structural_child_index,
.descendant_index = self->descendant_index,
};
*visible = ts_subtree_visible(*child);
bool extra = ts_subtree_extra(*child);
if (!extra && self->alias_sequence) {
*visible |= self->alias_sequence[self->structural_child_index];
if (!extra) {
if (self->alias_sequence) {
*visible |= self->alias_sequence[self->structural_child_index];
}
self->structural_child_index++;
}
self->descendant_index += ts_subtree_visible_descendant_count(*child);
if (*visible) {
self->descendant_index += 1;
}
self->position = length_add(self->position, ts_subtree_size(*child));
self->child_index++;
@ -65,6 +97,57 @@ static inline bool ts_tree_cursor_child_iterator_next(
return true;
}
// Return a position that, when `b` is added to it, yields `a`. This
// can only be computed if `b` has zero rows. Otherwise, this function
// returns `LENGTH_UNDEFINED`, and the caller needs to recompute
// the position some other way.
static inline Length length_backtrack(Length a, Length b) {
if (length_is_undefined(a) || b.extent.row != 0) {
return LENGTH_UNDEFINED;
}
Length result;
result.bytes = a.bytes - b.bytes;
result.extent.row = a.extent.row;
result.extent.column = a.extent.column - b.extent.column;
return result;
}
static inline bool ts_tree_cursor_child_iterator_previous(
CursorChildIterator *self,
TreeCursorEntry *result,
bool *visible
) {
// this is mostly a reverse `ts_tree_cursor_child_iterator_next` taking into
// account unsigned underflow
if (!self->parent.ptr || (int8_t)self->child_index == -1) return false;
const Subtree *child = &ts_subtree_children(self->parent)[self->child_index];
*result = (TreeCursorEntry) {
.subtree = child,
.position = self->position,
.child_index = self->child_index,
.structural_child_index = self->structural_child_index,
};
*visible = ts_subtree_visible(*child);
bool extra = ts_subtree_extra(*child);
if (!extra && self->alias_sequence) {
*visible |= self->alias_sequence[self->structural_child_index];
self->structural_child_index--;
}
self->position = length_backtrack(self->position, ts_subtree_padding(*child));
self->child_index--;
// unsigned can underflow so compare it to child_count
if (self->child_index < self->parent.ptr->child_count) {
Subtree previous_child = ts_subtree_children(self->parent)[self->child_index];
Length size = ts_subtree_size(previous_child);
self->position = length_backtrack(self->position, size);
}
return true;
}
// TSTreeCursor - lifecycle
TSTreeCursor ts_tree_cursor_new(TSNode node) {
@ -88,6 +171,7 @@ void ts_tree_cursor_init(TreeCursor *self, TSNode node) {
},
.child_index = 0,
.structural_child_index = 0,
.descendant_index = 0,
}));
}
@ -98,74 +182,84 @@ void ts_tree_cursor_delete(TSTreeCursor *_self) {
// TSTreeCursor - walking the tree
bool ts_tree_cursor_goto_first_child(TSTreeCursor *_self) {
TreeCursorStep ts_tree_cursor_goto_first_child_internal(TSTreeCursor *_self) {
TreeCursor *self = (TreeCursor *)_self;
bool did_descend;
do {
did_descend = false;
bool visible;
TreeCursorEntry entry;
CursorChildIterator iterator = ts_tree_cursor_iterate_children(self);
while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) {
if (visible) {
array_push(&self->stack, entry);
return true;
}
if (ts_subtree_visible_child_count(*entry.subtree) > 0) {
array_push(&self->stack, entry);
did_descend = true;
break;
}
bool visible;
TreeCursorEntry entry;
CursorChildIterator iterator = ts_tree_cursor_iterate_children(self);
while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) {
if (visible) {
array_push(&self->stack, entry);
return TreeCursorStepVisible;
}
} while (did_descend);
if (ts_subtree_visible_child_count(*entry.subtree) > 0) {
array_push(&self->stack, entry);
return TreeCursorStepHidden;
}
}
return TreeCursorStepNone;
}
bool ts_tree_cursor_goto_first_child(TSTreeCursor *self) {
for (;;) {
switch (ts_tree_cursor_goto_first_child_internal(self)) {
case TreeCursorStepHidden:
continue;
case TreeCursorStepVisible:
return true;
default:
return false;
}
}
return false;
}
int64_t ts_tree_cursor_goto_first_child_for_byte(TSTreeCursor *_self, uint32_t goal_byte) {
TreeCursorStep ts_tree_cursor_goto_last_child_internal(TSTreeCursor *_self) {
TreeCursor *self = (TreeCursor *)_self;
uint32_t initial_size = self->stack.size;
uint32_t visible_child_index = 0;
bool visible;
TreeCursorEntry entry;
CursorChildIterator iterator = ts_tree_cursor_iterate_children(self);
if (!iterator.parent.ptr || iterator.parent.ptr->child_count == 0) return TreeCursorStepNone;
bool did_descend;
do {
did_descend = false;
bool visible;
TreeCursorEntry entry;
CursorChildIterator iterator = ts_tree_cursor_iterate_children(self);
while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) {
uint32_t end_byte = entry.position.bytes + ts_subtree_size(*entry.subtree).bytes;
bool at_goal = end_byte >= goal_byte;
uint32_t visible_child_count = ts_subtree_visible_child_count(*entry.subtree);
if (at_goal) {
if (visible) {
array_push(&self->stack, entry);
return visible_child_index;
}
if (visible_child_count > 0) {
array_push(&self->stack, entry);
did_descend = true;
break;
}
} else if (visible) {
visible_child_index++;
} else {
visible_child_index += visible_child_count;
}
TreeCursorEntry last_entry;
TreeCursorStep last_step = TreeCursorStepNone;
while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) {
if (visible) {
last_entry = entry;
last_step = TreeCursorStepVisible;
}
} while (did_descend);
else if (ts_subtree_visible_child_count(*entry.subtree) > 0) {
last_entry = entry;
last_step = TreeCursorStepHidden;
}
}
if (last_entry.subtree) {
array_push(&self->stack, last_entry);
return last_step;
}
self->stack.size = initial_size;
return -1;
return TreeCursorStepNone;
}
int64_t ts_tree_cursor_goto_first_child_for_point(TSTreeCursor *_self, TSPoint goal_point) {
bool ts_tree_cursor_goto_last_child(TSTreeCursor *self) {
for (;;) {
switch (ts_tree_cursor_goto_last_child_internal(self)) {
case TreeCursorStepHidden:
continue;
case TreeCursorStepVisible:
return true;
default:
return false;
}
}
return false;
}
static inline int64_t ts_tree_cursor_goto_first_child_for_byte_and_point(
TSTreeCursor *_self,
uint32_t goal_byte,
TSPoint goal_point
) {
TreeCursor *self = (TreeCursor *)_self;
uint32_t initial_size = self->stack.size;
uint32_t visible_child_index = 0;
@ -178,8 +272,8 @@ int64_t ts_tree_cursor_goto_first_child_for_point(TSTreeCursor *_self, TSPoint g
TreeCursorEntry entry;
CursorChildIterator iterator = ts_tree_cursor_iterate_children(self);
while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) {
TSPoint end_point = point_add(entry.position.extent, ts_subtree_size(*entry.subtree).extent);
bool at_goal = point_gte(end_point, goal_point);
Length entry_end = length_add(entry.position, ts_subtree_size(*entry.subtree));
bool at_goal = entry_end.bytes >= goal_byte && point_gte(entry_end.extent, goal_point);
uint32_t visible_child_count = ts_subtree_visible_child_count(*entry.subtree);
if (at_goal) {
if (visible) {
@ -203,7 +297,17 @@ int64_t ts_tree_cursor_goto_first_child_for_point(TSTreeCursor *_self, TSPoint g
return -1;
}
bool ts_tree_cursor_goto_next_sibling(TSTreeCursor *_self) {
int64_t ts_tree_cursor_goto_first_child_for_byte(TSTreeCursor *self, uint32_t goal_byte) {
return ts_tree_cursor_goto_first_child_for_byte_and_point(self, goal_byte, POINT_ZERO);
}
int64_t ts_tree_cursor_goto_first_child_for_point(TSTreeCursor *self, TSPoint goal_point) {
return ts_tree_cursor_goto_first_child_for_byte_and_point(self, 0, goal_point);
}
TreeCursorStep ts_tree_cursor_goto_sibling_internal(
TSTreeCursor *_self,
bool (*advance)(CursorChildIterator *, TreeCursorEntry *, bool *)) {
TreeCursor *self = (TreeCursor *)_self;
uint32_t initial_size = self->stack.size;
@ -213,52 +317,161 @@ bool ts_tree_cursor_goto_next_sibling(TSTreeCursor *_self) {
iterator.child_index = entry.child_index;
iterator.structural_child_index = entry.structural_child_index;
iterator.position = entry.position;
iterator.descendant_index = entry.descendant_index;
bool visible = false;
ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible);
advance(&iterator, &entry, &visible);
if (visible && self->stack.size + 1 < initial_size) break;
while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) {
while (advance(&iterator, &entry, &visible)) {
if (visible) {
array_push(&self->stack, entry);
return true;
return TreeCursorStepVisible;
}
if (ts_subtree_visible_child_count(*entry.subtree)) {
array_push(&self->stack, entry);
ts_tree_cursor_goto_first_child(_self);
return true;
return TreeCursorStepHidden;
}
}
}
self->stack.size = initial_size;
return false;
return TreeCursorStepNone;
}
TreeCursorStep ts_tree_cursor_goto_next_sibling_internal(TSTreeCursor *_self) {
return ts_tree_cursor_goto_sibling_internal(_self, ts_tree_cursor_child_iterator_next);
}
bool ts_tree_cursor_goto_next_sibling(TSTreeCursor *self) {
switch (ts_tree_cursor_goto_next_sibling_internal(self)) {
case TreeCursorStepHidden:
ts_tree_cursor_goto_first_child(self);
return true;
case TreeCursorStepVisible:
return true;
default:
return false;
}
}
TreeCursorStep ts_tree_cursor_goto_previous_sibling_internal(TSTreeCursor *_self) {
// since subtracting across row loses column information, we may have to
// restore it
TreeCursor *self = (TreeCursor *)_self;
// for that, save current position before traversing
Length position = array_back(&self->stack)->position;
TreeCursorStep step = ts_tree_cursor_goto_sibling_internal(
_self, ts_tree_cursor_child_iterator_previous);
if (step == TreeCursorStepNone)
return step;
// if length is already valid, there's no need to recompute it
if (!length_is_undefined(array_back(&self->stack)->position))
return step;
// restore position from the parent node
const TreeCursorEntry *parent = &self->stack.contents[self->stack.size - 2];
position = parent->position;
uint32_t child_index = array_back(&self->stack)->child_index;
const Subtree *children = ts_subtree_children((*(parent->subtree)));
if (child_index > 0) {
// skip first child padding since its position should match the position of the parent
position = length_add(position, ts_subtree_size(children[0]));
for (uint32_t i = 1; i < child_index; ++i) {
position = length_add(position, ts_subtree_total_size(children[i]));
}
position = length_add(position, ts_subtree_padding(children[child_index]));
}
array_back(&self->stack)->position = position;
return step;
}
bool ts_tree_cursor_goto_previous_sibling(TSTreeCursor *self) {
switch (ts_tree_cursor_goto_previous_sibling_internal(self)) {
case TreeCursorStepHidden:
ts_tree_cursor_goto_last_child(self);
return true;
case TreeCursorStepVisible:
return true;
default:
return false;
}
}
bool ts_tree_cursor_goto_parent(TSTreeCursor *_self) {
TreeCursor *self = (TreeCursor *)_self;
for (unsigned i = self->stack.size - 2; i + 1 > 0; i--) {
TreeCursorEntry *entry = &self->stack.contents[i];
if (ts_subtree_visible(*entry->subtree)) {
if (ts_tree_cursor_is_entry_visible(self, i)) {
self->stack.size = i + 1;
return true;
}
if (i > 0 && !ts_subtree_extra(*entry->subtree)) {
TreeCursorEntry *parent_entry = &self->stack.contents[i - 1];
if (ts_language_alias_at(
self->tree->language,
parent_entry->subtree->ptr->production_id,
entry->structural_child_index
)) {
self->stack.size = i + 1;
return true;
}
}
}
return false;
}
void ts_tree_cursor_goto_descendant(
TSTreeCursor *_self,
uint32_t goal_descendant_index
) {
TreeCursor *self = (TreeCursor *)_self;
// Ascend to the lowest ancestor that contains the goal node.
for (;;) {
uint32_t i = self->stack.size - 1;
TreeCursorEntry *entry = &self->stack.contents[i];
uint32_t next_descendant_index =
entry->descendant_index +
(ts_tree_cursor_is_entry_visible(self, i) ? 1 : 0) +
ts_subtree_visible_descendant_count(*entry->subtree);
if (
(entry->descendant_index <= goal_descendant_index) &&
(next_descendant_index > goal_descendant_index)
) {
break;
} else if (self->stack.size <= 1) {
return;
} else {
self->stack.size--;
}
}
// Descend to the goal node.
bool did_descend = true;
do {
did_descend = false;
bool visible;
TreeCursorEntry entry;
CursorChildIterator iterator = ts_tree_cursor_iterate_children(self);
if (iterator.descendant_index > goal_descendant_index) {
return;
}
while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) {
if (iterator.descendant_index > goal_descendant_index) {
array_push(&self->stack, entry);
if (visible && entry.descendant_index == goal_descendant_index) {
return;
} else {
did_descend = true;
break;
}
}
}
} while (did_descend);
}
uint32_t ts_tree_cursor_current_descendant_index(const TSTreeCursor *_self) {
const TreeCursor *self = (const TreeCursor *)_self;
TreeCursorEntry *last_entry = array_back(&self->stack);
return last_entry->descendant_index;
}
TSNode ts_tree_cursor_current_node(const TSTreeCursor *_self) {
const TreeCursor *self = (const TreeCursor *)_self;
TreeCursorEntry *last_entry = array_back(&self->stack);
@ -377,9 +590,9 @@ void ts_tree_cursor_current_status(
// Look for a field name associated with the current node.
if (!*field_id) {
for (const TSFieldMapEntry *i = field_map; i < field_map_end; i++) {
if (!i->inherited && i->child_index == entry->structural_child_index) {
*field_id = i->field_id;
for (const TSFieldMapEntry *map = field_map; map < field_map_end; map++) {
if (!map->inherited && map->child_index == entry->structural_child_index) {
*field_id = map->field_id;
break;
}
}
@ -387,10 +600,10 @@ void ts_tree_cursor_current_status(
// Determine if the current node can have later siblings with the same field name.
if (*field_id) {
for (const TSFieldMapEntry *i = field_map; i < field_map_end; i++) {
for (const TSFieldMapEntry *map = field_map; map < field_map_end; map++) {
if (
i->field_id == *field_id &&
i->child_index > entry->structural_child_index
map->field_id == *field_id &&
map->child_index > entry->structural_child_index
) {
*can_have_later_siblings_with_this_field = true;
break;
@ -401,6 +614,17 @@ void ts_tree_cursor_current_status(
}
}
uint32_t ts_tree_cursor_current_depth(const TSTreeCursor *_self) {
const TreeCursor *self = (const TreeCursor *)_self;
uint32_t depth = 0;
for (unsigned i = 1; i < self->stack.size; i++) {
if (ts_tree_cursor_is_entry_visible(self, i)) {
depth++;
}
}
return depth;
}
TSNode ts_tree_cursor_parent_node(const TSTreeCursor *_self) {
const TreeCursor *self = (const TreeCursor *)_self;
for (int i = (int)self->stack.size - 2; i >= 0; i--) {
@ -437,17 +661,10 @@ TSFieldId ts_tree_cursor_current_field_id(const TSTreeCursor *_self) {
TreeCursorEntry *parent_entry = &self->stack.contents[i - 1];
// Stop walking up when another visible node is found.
if (i != self->stack.size - 1) {
if (ts_subtree_visible(*entry->subtree)) break;
if (
!ts_subtree_extra(*entry->subtree) &&
ts_language_alias_at(
self->tree->language,
parent_entry->subtree->ptr->production_id,
entry->structural_child_index
)
) break;
}
if (
i != self->stack.size - 1 &&
ts_tree_cursor_is_entry_visible(self, i)
) break;
if (ts_subtree_extra(*entry->subtree)) break;
@ -457,9 +674,9 @@ TSFieldId ts_tree_cursor_current_field_id(const TSTreeCursor *_self) {
parent_entry->subtree->ptr->production_id,
&field_map, &field_map_end
);
for (const TSFieldMapEntry *i = field_map; i < field_map_end; i++) {
if (!i->inherited && i->child_index == entry->structural_child_index) {
return i->field_id;
for (const TSFieldMapEntry *map = field_map; map < field_map_end; map++) {
if (!map->inherited && map->child_index == entry->structural_child_index) {
return map->field_id;
}
}
}
@ -485,3 +702,11 @@ TSTreeCursor ts_tree_cursor_copy(const TSTreeCursor *_cursor) {
array_push_all(&copy->stack, &cursor->stack);
return res;
}
void ts_tree_cursor_reset_to(TSTreeCursor *_dst, const TSTreeCursor *_src) {
const TreeCursor *cursor = (const TreeCursor *)_src;
TreeCursor *copy = (TreeCursor *)_dst;
copy->tree = cursor->tree;
array_clear(&copy->stack);
array_push_all(&copy->stack, &cursor->stack);
}

View file

@ -8,6 +8,7 @@ typedef struct {
Length position;
uint32_t child_index;
uint32_t structural_child_index;
uint32_t descendant_index;
} TreeCursorEntry;
typedef struct {
@ -15,6 +16,12 @@ typedef struct {
Array(TreeCursorEntry) stack;
} TreeCursor;
typedef enum {
TreeCursorStepNone,
TreeCursorStepHidden,
TreeCursorStepVisible,
} TreeCursorStep;
void ts_tree_cursor_init(TreeCursor *, TSNode);
void ts_tree_cursor_current_status(
const TSTreeCursor *,
@ -26,6 +33,15 @@ void ts_tree_cursor_current_status(
unsigned *
);
TreeCursorStep ts_tree_cursor_goto_first_child_internal(TSTreeCursor *);
TreeCursorStep ts_tree_cursor_goto_next_sibling_internal(TSTreeCursor *);
static inline Subtree ts_tree_cursor_current_subtree(const TSTreeCursor *_self) {
const TreeCursor *self = (const TreeCursor *)_self;
TreeCursorEntry *last_entry = array_back(&self->stack);
return *last_entry->subtree;
}
TSNode ts_tree_cursor_parent_node(const TSTreeCursor *);
#endif // TREE_SITTER_TREE_CURSOR_H_