#![forbid(unsafe_op_in_unsafe_fn)]
#![deny(private_interfaces)]
#![cfg_attr(feature = "strict_api", deny(unreachable_pub))]
#![cfg_attr(not(feature = "strict_api"), warn(unreachable_pub))]
#![cfg_attr(feature = "strict_docs", deny(missing_docs))]
#![cfg_attr(not(feature = "strict_docs"), allow(missing_docs))]
#![allow(clippy::missing_safety_doc)] #![allow(clippy::needless_range_loop)] #![allow(clippy::only_used_in_recursion)]
pub mod __private;
pub mod concurrency_caps;
pub mod external_scanner;
pub mod external_scanner_ffi;
pub mod ffi;
pub mod field_tree;
pub mod lex;
pub mod linecol;
pub mod pool;
pub mod ts_format;
pub use ffi::TSSymbol;
pub type SymbolId = TSSymbol;
pub use parser_selection::{
ParserBackend, ParserFeatureProfile, bdd_progress_report_for_current_profile,
bdd_status_line_for_current_profile, current_backend_for, parser_feature_profile_for_runtime,
runtime_governance_snapshot,
};
#[cfg(feature = "pure-rust")]
pub use glr_incremental::{Edit, GLRToken, IncrementalGLRParser};
#[cfg(feature = "legacy-parsers")]
pub mod incremental;
#[cfg(feature = "legacy-parsers")]
pub mod incremental_v2;
#[cfg(feature = "legacy-parsers")]
pub mod incremental_v3;
pub mod lexer;
pub mod scanner_registry;
pub mod scanners;
#[cfg(feature = "pure-rust")]
pub mod parser {
pub use super::parser_v4::*;
}
pub mod error_recovery;
pub mod error_reporting;
#[cfg(feature = "legacy-parsers")]
pub mod glr; #[cfg(feature = "pure-rust")]
pub mod glr_forest;
#[cfg(feature = "pure-rust")]
pub mod glr_incremental;
pub mod glr_lexer;
pub mod glr_parser;
pub mod glr_query;
pub mod glr_tree_bridge;
pub mod glr_validation;
#[cfg(feature = "pure-rust")]
pub mod tree_bridge;
#[cfg(feature = "pure-rust")]
pub mod decoder;
#[cfg(feature = "pure-rust")]
pub mod grammar_json;
pub mod optimizations;
#[cfg(feature = "legacy-parsers")]
mod parser_v2;
#[cfg(feature = "legacy-parsers")]
mod parser_v3;
pub mod arena_allocator;
pub mod node;
pub mod parser_selection;
#[cfg(feature = "pure-rust")]
pub mod parser_v4;
pub mod pure_external_scanner;
pub mod pure_incremental;
pub mod pure_parser;
#[cfg(feature = "pure-rust")]
pub mod query;
pub mod stack_pool;
pub mod tree_node_data;
#[cfg(feature = "serialization")]
pub mod serialization;
pub mod subtree;
#[cfg(feature = "pure-rust")]
pub mod unified_parser;
pub mod visitor;
pub mod simd_lexer {
pub use super::simd_lexer_v2::*;
}
mod simd_lexer_v2;
#[cfg(feature = "ts-compat")]
pub mod ts_compat;
#[cfg(feature = "ts-compat")]
pub use adze_glr_core;
#[cfg(feature = "ts-compat")]
pub use adze_ir;
#[cfg(all(
feature = "pure-rust",
not(feature = "tree-sitter-standard"),
not(feature = "tree-sitter-c2rust")
))]
mod tree_sitter_compat;
#[cfg(all(
not(feature = "tree-sitter-standard"),
not(feature = "tree-sitter-c2rust"),
not(feature = "pure-rust")
))]
pub mod tree_sitter_compat;
use std::ops::Deref;
pub use adze_macro::*;
#[cfg(all(
feature = "tree-sitter-standard",
not(feature = "tree-sitter-c2rust"),
not(feature = "pure-rust")
))]
pub use tree_sitter;
#[cfg(all(feature = "tree-sitter-c2rust", not(feature = "pure-rust")))]
pub use tree_sitter_runtime_c2rust as tree_sitter;
#[cfg(all(
not(feature = "tree-sitter-standard"),
not(feature = "tree-sitter-c2rust"),
not(feature = "pure-rust")
))]
pub use tree_sitter_compat as tree_sitter;
#[cfg(feature = "pure-rust")]
pub mod tree_sitter {
pub use crate::pure_incremental::{Edit, Tree};
pub use crate::pure_parser::Point;
pub use crate::pure_parser::{ParseResult, ParsedNode as Node};
pub use crate::pure_parser::{Parser, TSLanguage as Language};
pub const LANGUAGE_VERSION: u32 = 15;
pub const MIN_COMPATIBLE_LANGUAGE_VERSION: u32 = 13;
}
pub mod sealed {
pub trait Sealed {}
impl<T> Sealed for T {}
}
pub trait Extract<Output>: sealed::Sealed {
type LeafFn: ?Sized;
const HAS_CONFLICTS: bool = false;
#[cfg(feature = "pure-rust")]
const GRAMMAR_NAME: &'static str = "unknown";
#[cfg(feature = "pure-rust")]
const GRAMMAR_JSON: &'static str = "{}";
#[cfg(not(feature = "pure-rust"))]
fn extract(
node: Option<crate::tree_sitter::Node>,
source: &[u8],
last_idx: usize,
leaf_fn: Option<&Self::LeafFn>,
) -> Output;
#[cfg(feature = "pure-rust")]
fn extract(
node: Option<&crate::pure_parser::ParsedNode>,
source: &[u8],
last_idx: usize,
leaf_fn: Option<&Self::LeafFn>,
) -> Output;
}
pub trait ExtractDefault<Output>: Extract<Output> {
fn extract_default(last_idx: usize) -> Output;
}
pub struct WithLeaf<L> {
_phantom: std::marker::PhantomData<L>,
}
impl<L> Extract<L> for WithLeaf<L> {
type LeafFn = dyn Fn(&str) -> L;
#[cfg(not(feature = "pure-rust"))]
fn extract(
node: Option<crate::tree_sitter::Node>,
source: &[u8],
_last_idx: usize,
leaf_fn: Option<&Self::LeafFn>,
) -> L {
let text = node
.and_then(|n| n.utf8_text(source).ok())
.unwrap_or_default();
if let Some(f) = leaf_fn {
f(text)
} else {
panic!(
"Leaf extraction failed: no transform function provided for type that requires one."
)
}
}
#[cfg(feature = "pure-rust")]
fn extract(
node: Option<&crate::pure_parser::ParsedNode>,
source: &[u8],
_last_idx: usize,
leaf_fn: Option<&Self::LeafFn>,
) -> L {
let text = node
.and_then(|n| {
let text = &source[n.start_byte..n.end_byte];
std::str::from_utf8(text).ok()
})
.unwrap_or_default();
if let Some(f) = leaf_fn {
f(text)
} else {
panic!(
"Leaf extraction failed: no transform function provided for type that requires one."
)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn index_valid_span() {
let source = "hello world";
let span = Spanned {
value: (),
span: (0, 5),
};
assert_eq!(&source[span], "hello");
}
#[test]
fn index_edge_cases() {
let source = "hello";
let span = Spanned {
value: (),
span: (0, 0),
};
assert_eq!(&source[span], "");
let span = Spanned {
value: (),
span: (5, 5),
};
assert_eq!(&source[span], "");
let span = Spanned {
value: (),
span: (0, 5),
};
assert_eq!(&source[span], "hello");
}
#[test]
#[should_panic(expected = "Invalid span")]
fn index_invalid_span_panics() {
let source = "hello";
let span = Spanned {
value: (),
span: (0, 10),
};
let _ = &source[span];
}
#[test]
fn index_mut_valid_span() {
let mut source = String::from("hello world");
let span = Spanned {
value: (),
span: (6, 11),
};
source.as_mut_str()[span].make_ascii_uppercase();
assert_eq!(source, "hello WORLD");
}
#[test]
#[should_panic(expected = "Invalid span")]
fn index_mut_invalid_span_panics() {
let mut source = String::from("hello");
let span = Spanned {
value: (),
span: (6, 7),
};
let s = source.as_mut_str();
let _ = &mut s[span];
}
#[test]
fn validate_span_valid() {
assert!(validate_span((0, 5), 10).is_ok());
assert!(validate_span((0, 0), 5).is_ok());
assert!(validate_span((5, 5), 5).is_ok());
assert!(validate_span((2, 8), 10).is_ok());
}
#[test]
fn validate_span_start_greater_than_end() {
let result = validate_span((5, 3), 10);
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(error.reason, SpanErrorReason::StartGreaterThanEnd);
assert_eq!(error.span, (5, 3));
assert_eq!(error.source_len, 10);
}
#[test]
fn validate_span_start_out_of_bounds() {
let result = validate_span((11, 12), 10);
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(error.reason, SpanErrorReason::StartOutOfBounds);
assert_eq!(error.span, (11, 12));
assert_eq!(error.source_len, 10);
}
#[test]
fn validate_span_end_out_of_bounds() {
let result = validate_span((5, 11), 10);
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(error.reason, SpanErrorReason::EndOutOfBounds);
assert_eq!(error.span, (5, 11));
assert_eq!(error.source_len, 10);
}
#[test]
fn span_error_display() {
let error = SpanError {
span: (5, 3),
source_len: 10,
reason: SpanErrorReason::StartGreaterThanEnd,
};
assert_eq!(error.to_string(), "Invalid span 5..3: start (5) > end (3)");
let error = SpanError {
span: (11, 12),
source_len: 10,
reason: SpanErrorReason::StartOutOfBounds,
};
assert_eq!(
error.to_string(),
"Invalid span 11..12: start (11) > source length (10)"
);
let error = SpanError {
span: (5, 11),
source_len: 10,
reason: SpanErrorReason::EndOutOfBounds,
};
assert_eq!(
error.to_string(),
"Invalid span 5..11: end (11) > source length (10)"
);
}
#[test]
#[should_panic(expected = "Invalid span: Invalid span 12..15: start (12) > source length (5)")]
fn index_start_out_of_bounds_detailed_error() {
let source = "hello";
let span = Spanned {
value: (),
span: (12, 15),
};
let _ = &source[span];
}
#[test]
#[should_panic(expected = "Invalid span: Invalid span 2..10: end (10) > source length (5)")]
fn index_end_out_of_bounds_detailed_error() {
let source = "hello";
let span = Spanned {
value: (),
span: (2, 10),
};
let _ = &source[span];
}
#[test]
#[should_panic(expected = "Invalid span: Invalid span 5..3: start (5) > end (3)")]
fn index_start_greater_than_end_detailed_error() {
let source = "hello world";
let span = Spanned {
value: (),
span: (5, 3),
};
let _ = &source[span];
}
#[test]
#[should_panic(expected = "Invalid span: Invalid span 7..9: start (7) > source length (5)")]
fn index_mut_start_out_of_bounds_detailed_error() {
let mut source = String::from("hello");
let span = Spanned {
value: (),
span: (7, 9),
};
let s = source.as_mut_str();
let _ = &mut s[span];
}
}
impl Extract<()> for () {
type LeafFn = ();
#[cfg(not(feature = "pure-rust"))]
fn extract(
_node: Option<crate::tree_sitter::Node>,
_source: &[u8],
_last_idx: usize,
_leaf_fn: Option<&Self::LeafFn>,
) {
}
#[cfg(feature = "pure-rust")]
fn extract(
_node: Option<&crate::pure_parser::ParsedNode>,
_source: &[u8],
_last_idx: usize,
_leaf_fn: Option<&Self::LeafFn>,
) {
}
}
impl<T: Extract<U>, U> Extract<Option<U>> for Option<T> {
type LeafFn = T::LeafFn;
#[cfg(not(feature = "pure-rust"))]
fn extract(
node: Option<crate::tree_sitter::Node>,
source: &[u8],
last_idx: usize,
leaf_fn: Option<&Self::LeafFn>,
) -> Option<U> {
node.map(|n| T::extract(Some(n), source, last_idx, leaf_fn))
}
#[cfg(feature = "pure-rust")]
fn extract(
node: Option<&crate::pure_parser::ParsedNode>,
source: &[u8],
last_idx: usize,
leaf_fn: Option<&Self::LeafFn>,
) -> Option<U> {
node.map(|n| T::extract(Some(n), source, last_idx, leaf_fn))
}
}
impl<T: Extract<U>, U> Extract<Box<U>> for Box<T> {
type LeafFn = T::LeafFn;
#[cfg(not(feature = "pure-rust"))]
fn extract(
node: Option<crate::tree_sitter::Node>,
source: &[u8],
last_idx: usize,
leaf_fn: Option<&Self::LeafFn>,
) -> Box<U> {
Box::new(T::extract(node, source, last_idx, leaf_fn))
}
#[cfg(feature = "pure-rust")]
fn extract(
node: Option<&crate::pure_parser::ParsedNode>,
source: &[u8],
last_idx: usize,
leaf_fn: Option<&Self::LeafFn>,
) -> Box<U> {
Box::new(T::extract(node, source, last_idx, leaf_fn))
}
}
impl<T: Extract<U>, U> Extract<Vec<U>> for Vec<T> {
type LeafFn = T::LeafFn;
#[cfg(not(feature = "pure-rust"))]
fn extract(
node: Option<crate::tree_sitter::Node>,
source: &[u8],
mut last_idx: usize,
leaf_fn: Option<&Self::LeafFn>,
) -> Vec<U> {
node.map(|node| {
let mut cursor = node.walk();
let mut out = vec![];
if cursor.goto_first_child() {
loop {
let n = cursor.node();
if cursor.field_name().is_some() || n.is_named() {
out.push(T::extract(Some(n), source, last_idx, leaf_fn));
}
last_idx = n.end_byte();
if !cursor.goto_next_sibling() {
break;
}
}
}
out
})
.unwrap_or_default()
}
#[cfg(feature = "pure-rust")]
fn extract(
node: Option<&crate::pure_parser::ParsedNode>,
source: &[u8],
last_idx: usize,
leaf_fn: Option<&Self::LeafFn>,
) -> Vec<U> {
node.map(|node| {
let mut out = vec![];
fn flatten_repeat1<T: Extract<U>, U>(
node: &crate::pure_parser::ParsedNode,
source: &[u8],
mut last_idx: usize,
leaf_fn: Option<&T::LeafFn>,
out: &mut Vec<U>,
) {
if node.children.len() == 2
&& !node.children.is_empty()
&& node.children[0].symbol == node.symbol
{
flatten_repeat1::<T, U>(&node.children[0], source, last_idx, leaf_fn, out);
out.push(T::extract(
Some(&node.children[1]),
source,
node.children[0].end_byte,
leaf_fn,
));
} else if node.children.len() == 1 {
out.push(T::extract(
Some(&node.children[0]),
source,
last_idx,
leaf_fn,
));
} else {
for child in &node.children {
out.push(T::extract(Some(child), source, last_idx, leaf_fn));
last_idx = child.end_byte;
}
}
}
flatten_repeat1::<T, U>(node, source, last_idx, leaf_fn, &mut out);
out
})
.unwrap_or_default()
}
}
#[derive(Clone, Debug)]
pub struct Spanned<T> {
pub value: T,
pub span: (usize, usize),
}
impl<T> Deref for Spanned<T> {
type Target = T;
fn deref(&self) -> &T {
&self.value
}
}
impl<T: Extract<U>, U> Extract<Spanned<U>> for Spanned<T> {
type LeafFn = T::LeafFn;
#[cfg(not(feature = "pure-rust"))]
fn extract(
node: Option<crate::tree_sitter::Node>,
source: &[u8],
last_idx: usize,
leaf_fn: Option<&Self::LeafFn>,
) -> Spanned<U> {
Spanned {
value: T::extract(node, source, last_idx, leaf_fn),
span: node
.map(|n| (n.start_byte(), n.end_byte()))
.unwrap_or((last_idx, last_idx)),
}
}
#[cfg(feature = "pure-rust")]
fn extract(
node: Option<&crate::pure_parser::ParsedNode>,
source: &[u8],
last_idx: usize,
leaf_fn: Option<&Self::LeafFn>,
) -> Spanned<U> {
Spanned {
value: T::extract(node, source, last_idx, leaf_fn),
span: node
.map(|n| (n.start_byte, n.end_byte))
.unwrap_or((last_idx, last_idx)),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SpanError {
pub span: (usize, usize),
pub source_len: usize,
pub reason: SpanErrorReason,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SpanErrorReason {
StartGreaterThanEnd,
StartOutOfBounds,
EndOutOfBounds,
}
impl std::fmt::Display for SpanError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (start, end) = self.span;
match self.reason {
SpanErrorReason::StartGreaterThanEnd => {
write!(
f,
"Invalid span {start}..{end}: start ({start}) > end ({end})"
)
}
SpanErrorReason::StartOutOfBounds => {
write!(
f,
"Invalid span {start}..{end}: start ({start}) > source length ({})",
self.source_len
)
}
SpanErrorReason::EndOutOfBounds => {
write!(
f,
"Invalid span {start}..{end}: end ({end}) > source length ({})",
self.source_len
)
}
}
}
}
impl std::error::Error for SpanError {}
fn validate_span(span: (usize, usize), source_len: usize) -> Result<(), SpanError> {
let (start, end) = span;
if start > end {
return Err(SpanError {
span,
source_len,
reason: SpanErrorReason::StartGreaterThanEnd,
});
}
if start > source_len {
return Err(SpanError {
span,
source_len,
reason: SpanErrorReason::StartOutOfBounds,
});
}
if end > source_len {
return Err(SpanError {
span,
source_len,
reason: SpanErrorReason::EndOutOfBounds,
});
}
Ok(())
}
impl<T> std::ops::Index<Spanned<T>> for str {
type Output = str;
fn index(&self, span: Spanned<T>) -> &Self::Output {
let (start, end) = span.span;
let source_len = self.len();
if let Err(error) = validate_span(span.span, source_len) {
panic!("Invalid span: {}", error);
}
&self[start..end]
}
}
impl<T> std::ops::IndexMut<Spanned<T>> for str {
fn index_mut(&mut self, span: Spanned<T>) -> &mut Self::Output {
let (start, end) = span.span;
let source_len = self.len();
if let Err(error) = validate_span(span.span, source_len) {
panic!("Invalid span: {}", error);
}
&mut self[start..end]
}
}
impl Extract<String> for String {
type LeafFn = ();
#[cfg(not(feature = "pure-rust"))]
fn extract(
node: Option<tree_sitter::Node>,
source: &[u8],
_last_idx: usize,
_leaf_fn: Option<&Self::LeafFn>,
) -> String {
node.and_then(|n| n.utf8_text(source).ok())
.unwrap_or_default()
.to_string()
}
#[cfg(feature = "pure-rust")]
fn extract(
node: Option<&crate::pure_parser::ParsedNode>,
source: &[u8],
_last_idx: usize,
_leaf_fn: Option<&Self::LeafFn>,
) -> String {
node.and_then(|n| {
let text = &source[n.start_byte..n.end_byte];
std::str::from_utf8(text).ok()
})
.unwrap_or_default()
.to_string()
}
}
macro_rules! impl_extract_for_primitive {
($t:ty) => {
impl Extract<$t> for $t {
type LeafFn = ();
#[cfg(not(feature = "pure-rust"))]
fn extract(
node: Option<crate::tree_sitter::Node>,
source: &[u8],
_last_idx: usize,
_leaf_fn: Option<&Self::LeafFn>,
) -> $t {
node.and_then(|n| n.utf8_text(source).ok())
.and_then(|s| s.parse().ok())
.unwrap_or_default()
}
#[cfg(feature = "pure-rust")]
fn extract(
node: Option<&crate::pure_parser::ParsedNode>,
source: &[u8],
_last_idx: usize,
_leaf_fn: Option<&Self::LeafFn>,
) -> $t {
node.and_then(|n| {
let text = &source[n.start_byte..n.end_byte];
std::str::from_utf8(text).ok()
})
.and_then(|s| s.parse().ok())
.unwrap_or_default()
}
}
};
}
impl_extract_for_primitive!(i8);
impl_extract_for_primitive!(i16);
impl_extract_for_primitive!(i32);
impl_extract_for_primitive!(i64);
impl_extract_for_primitive!(i128);
impl_extract_for_primitive!(isize);
impl_extract_for_primitive!(u8);
impl_extract_for_primitive!(u16);
impl_extract_for_primitive!(u32);
impl_extract_for_primitive!(u64);
impl_extract_for_primitive!(u128);
impl_extract_for_primitive!(usize);
impl_extract_for_primitive!(f32);
impl_extract_for_primitive!(f64);
impl_extract_for_primitive!(bool);
pub mod errors {
#[derive(Debug)]
pub enum ParseErrorReason {
UnexpectedToken(String),
FailedNode(Vec<ParseError>),
MissingToken(String),
}
#[derive(Debug)]
pub struct ParseError {
pub reason: ParseErrorReason,
pub start: usize,
pub end: usize,
}
#[cfg(not(feature = "pure-rust"))]
pub fn collect_parsing_errors(
node: &crate::tree_sitter::Node,
source: &[u8],
errors: &mut Vec<ParseError>,
) {
if node.is_error() {
if node.child(0).is_some() {
let mut inner_errors = vec![];
let mut cursor = node.walk();
node.children(&mut cursor)
.for_each(|c| collect_parsing_errors(&c, source, &mut inner_errors));
errors.push(ParseError {
reason: ParseErrorReason::FailedNode(inner_errors),
start: node.start_byte(),
end: node.end_byte(),
})
} else {
match node.utf8_text(source) {
Ok(contents) if !contents.is_empty() => errors.push(ParseError {
reason: ParseErrorReason::UnexpectedToken(contents.to_string()),
start: node.start_byte(),
end: node.end_byte(),
}),
Ok(_) | Err(_) => errors.push(ParseError {
reason: ParseErrorReason::FailedNode(vec![]),
start: node.start_byte(),
end: node.end_byte(),
}),
}
}
} else if node.is_missing() {
errors.push(ParseError {
reason: ParseErrorReason::MissingToken(node.kind().to_string()),
start: node.start_byte(),
end: node.end_byte(),
})
} else if node.has_error() {
let mut cursor = node.walk();
node.children(&mut cursor)
.for_each(|c| collect_parsing_errors(&c, source, errors));
}
}
#[cfg(feature = "pure-rust")]
pub fn collect_parsing_errors(
node: &crate::pure_parser::ParsedNode,
source: &[u8],
errors: &mut Vec<ParseError>,
) {
if false {
let contents =
std::str::from_utf8(&source[node.start_byte..node.end_byte]).unwrap_or("");
if !contents.is_empty() {
errors.push(ParseError {
reason: ParseErrorReason::UnexpectedToken(contents.to_string()),
start: node.start_byte,
end: node.end_byte,
})
}
}
for child in &node.children {
collect_parsing_errors(child, source, errors);
}
}
}