tree_sitter_perl_next/
lib.rs1use tree_sitter_language::LanguageFn;
4
5unsafe extern "C" {
6 fn tree_sitter_perl() -> *const ();
7}
8
9pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_perl) };
11
12pub const NODE_TYPES: &str = include_str!("node-types.json");
14
15pub const HIGHLIGHTS_QUERY: &str = include_str!("../queries/highlights.scm");
17
18pub const INJECTIONS_QUERY: &str = include_str!("../queries/injections.scm");
20
21#[cfg(test)]
22mod tests {
23 #[test]
24 fn test_can_load_grammar() {
25 let mut parser = tree_sitter::Parser::new();
26 parser
27 .set_language(&super::LANGUAGE.into())
28 .expect("Error loading Perl parser");
29 }
30
31 #[test]
32 fn test_can_parse_perl() {
33 let mut parser = tree_sitter::Parser::new();
34 parser
35 .set_language(&super::LANGUAGE.into())
36 .expect("Error loading Perl parser");
37
38 let code = r#"
39#!/usr/bin/perl
40use strict;
41use warnings;
42
43my $name = "World";
44my @items = (1, 2, 3);
45my %lookup = (a => 1, b => 2);
46
47sub greet {
48 my ($who) = @_;
49 print "Hello, $who!\n";
50}
51
52greet($name);
53
54for my $item (@items) {
55 print "$item\n";
56}
57"#;
58
59 let tree = parser.parse(code, None).unwrap();
60 let root = tree.root_node();
61 assert!(
62 !root.has_error(),
63 "Parse tree has errors: {}",
64 root.to_sexp()
65 );
66 }
67
68 #[test]
69 fn test_highlights_query_is_valid() {
70 let language: tree_sitter::Language = super::LANGUAGE.into();
71 tree_sitter::Query::new(&language, super::HIGHLIGHTS_QUERY)
72 .expect("HIGHLIGHTS_QUERY should be a valid query for the Perl grammar");
73 }
74
75 #[test]
76 fn test_highlights_query_matches_perl_nodes() {
77 use tree_sitter::{Parser, Query, QueryCursor, StreamingIterator as _};
78
79 let language: tree_sitter::Language = super::LANGUAGE.into();
80 let mut parser = Parser::new();
81 parser.set_language(&language).unwrap();
82
83 let code = r#"my $x = "hello";"#;
84 let tree = parser.parse(code, None).unwrap();
85
86 let query = Query::new(&language, super::HIGHLIGHTS_QUERY).unwrap();
87 let mut cursor = QueryCursor::new();
88 let mut matches = cursor.matches(&query, tree.root_node(), code.as_bytes());
89
90 let mut capture_names: Vec<String> = vec![];
91 while let Some(m) = matches.next() {
92 for cap in m.captures {
93 let name = query.capture_names()[cap.index as usize].to_string();
94 if !capture_names.contains(&name) {
95 capture_names.push(name);
96 }
97 }
98 }
99
100 assert!(
101 capture_names.contains(&"keyword".to_string()),
102 "should match 'my' as @keyword"
103 );
104 assert!(
105 capture_names.contains(&"variable".to_string()),
106 "should match $x as @variable"
107 );
108 assert!(
109 capture_names.contains(&"string".to_string()),
110 "should match string literal as @string"
111 );
112 }
113
114 #[test]
115 fn test_injections_query_is_valid() {
116 let language: tree_sitter::Language = super::LANGUAGE.into();
117 tree_sitter::Query::new(&language, super::INJECTIONS_QUERY)
118 .expect("INJECTIONS_QUERY should be a valid query for the Perl grammar");
119 }
120
121 #[test]
122 fn test_injections_query_matches_substitution_with_e_modifier() {
123 use tree_sitter::{Parser, Query, QueryCursor, StreamingIterator as _};
124
125 let language: tree_sitter::Language = super::LANGUAGE.into();
126 let mut parser = Parser::new();
127 parser.set_language(&language).unwrap();
128
129 let code = r#"$x =~ s/foo/uc($1)/e;"#;
130 let tree = parser.parse(code, None).unwrap();
131
132 let query = Query::new(&language, super::INJECTIONS_QUERY).unwrap();
133 let mut cursor = QueryCursor::new();
134 let mut matches = cursor.matches(&query, tree.root_node(), code.as_bytes());
135
136 let mut found_perl_injection = false;
137 while let Some(m) = matches.next() {
138 for prop in query.property_settings(m.pattern_index) {
139 if prop.key.as_ref() == "injection.language"
140 && let Some(val) = &prop.value
141 && val.as_ref() == "perl"
142 {
143 found_perl_injection = true;
144 }
145 }
146 }
147
148 assert!(
149 found_perl_injection,
150 "should inject Perl for s///e replacement"
151 );
152 }
153}