1use crate::error::ParserResult;
2use std::collections::{HashMap, HashSet};
3use tree_sitter::Node;
4
5pub struct ParseContext<'a> {
6 pub source: &'a [u8],
7 pub symbols: HashMap<String, String>,
8 doc_consumed: HashSet<usize>,
9}
10
11impl<'a> ParseContext<'a> {
12 pub fn new(source: &'a [u8]) -> Self {
13 Self {
14 source,
15 symbols: HashMap::new(),
16 doc_consumed: HashSet::new(),
17 }
18 }
19
20 pub fn node_text(&self, node: &Node) -> ParserResult<&str> {
21 Ok(node.utf8_text(self.source)?)
22 }
23
24 pub fn take_doc_comment(&mut self, node: &Node) -> Option<String> {
25 let start = node.start_byte();
26 if self.doc_consumed.contains(&start) {
27 return None;
28 }
29 let doc = self.extract_doc_comment(start);
30 if doc.is_some() {
31 self.doc_consumed.insert(start);
32 }
33 doc
34 }
35
36 fn extract_doc_comment(&self, start: usize) -> Option<String> {
37 if start == 0 {
38 return None;
39 }
40 let src = self.source;
41 let mut line_end = if start > 0 && src[start - 1] == b'\n' {
42 start - 1
43 } else {
44 start
45 };
46 let mut lines = Vec::new();
47 let mut first = true;
48 loop {
49 let mut line_start = 0;
50 if line_end > 0 {
51 let mut i = line_end;
52 while i > 0 && src[i - 1] != b'\n' {
53 i -= 1;
54 }
55 line_start = i;
56 }
57 let mut line = &src[line_start..line_end];
58 if line.ends_with(b"\r") {
59 line = &line[..line.len() - 1];
60 }
61 if line.iter().all(|b| b.is_ascii_whitespace()) {
62 if first {
63 if line_start == 0 {
64 break;
65 }
66 line_end = line_start - 1;
67 first = false;
68 continue;
69 }
70 break;
71 }
72 first = false;
73 let mut idx = 0;
74 while idx < line.len() && line[idx].is_ascii_whitespace() {
75 idx += 1;
76 }
77 let trimmed = &line[idx..];
78 if trimmed.starts_with(b"///") {
79 let mut content = &trimmed[3..];
80 if content.first() == Some(&b' ') {
81 content = &content[1..];
82 }
83 lines.push(String::from_utf8_lossy(content).to_string());
84 if line_start == 0 {
85 break;
86 }
87 line_end = line_start - 1;
88 continue;
89 }
90 break;
91 }
92 if lines.is_empty() {
93 None
94 } else {
95 lines.reverse();
96 Some(lines.join("\n"))
97 }
98 }
99}
100
101pub trait FromTreeSitter<'a>: Sized {
102 fn from_node(node: Node<'a>, context: &mut ParseContext<'a>) -> ParserResult<Self>;
103}
104
105impl<'a> FromTreeSitter<'a> for String {
106 fn from_node(node: Node<'a>, context: &mut ParseContext<'a>) -> ParserResult<Self> {
107 Ok(context.node_text(&node)?.to_string())
108 }
109}
110
111impl<'a, T> FromTreeSitter<'a> for Box<T>
112where
113 T: FromTreeSitter<'a>,
114{
115 fn from_node(node: Node<'a>, context: &mut ParseContext<'a>) -> ParserResult<Self> {
116 Ok(Box::new(T::from_node(node, context)?))
117 }
118}
119
120pub fn parser_text(text: &str) -> ParserResult<crate::typed_ast::Specification> {
121 use crate::typed_ast::Specification;
122
123 let mut parser = tree_sitter::Parser::new();
124 parser.set_language(&tree_sitter_idl::language()).unwrap();
125
126 let tree = parser.parse(text, None).ok_or_else(|| {
127 crate::error::ParseError::TreeSitterError("Failed to parse text".to_string())
128 })?;
129
130 let root_node = tree.root_node();
131 if root_node.has_error() {
132 return Err(crate::error::ParseError::TreeSitterError(
133 "Failed to parse text".to_string(),
134 ));
135 }
136 let mut context = ParseContext::new(text.as_bytes());
137
138 Specification::from_node(root_node, &mut context)
139}
140
141#[cfg(test)]
142mod tests {
143 use super::parser_text;
144 use crate::typed_ast::{
145 AnnotationAppl, AnnotationName, AnnotationParams, Definition, TemplateTypeSpec,
146 TypeDclInner, TypeDeclaratorInner, TypeSpec,
147 };
148
149 #[test]
150 fn parse_template_type_spec() {
151 let typed = parser_text(
152 r#"
153 module m {
154 typedef Vec<long> MyVec;
155 };
156 "#,
157 )
158 .expect("parse should succeed");
159
160 let module = match &typed.0[0] {
161 Definition::ModuleDcl(module) => module,
162 other => panic!("expected module, got {other:?}"),
163 };
164 let type_dcl = match &module.definition[0] {
165 Definition::TypeDcl(type_dcl) => type_dcl,
166 other => panic!("expected type declaration, got {other:?}"),
167 };
168 let typedef = match &type_dcl.decl {
169 TypeDclInner::TypedefDcl(typedef) => typedef,
170 other => panic!("expected typedef, got {other:?}"),
171 };
172 let template = match &typedef.decl.ty {
173 TypeDeclaratorInner::TemplateTypeSpec(TemplateTypeSpec::TemplateType(template)) => {
174 template
175 }
176 other => panic!("expected template_type, got {other:?}"),
177 };
178 assert_eq!(template.ident.0, "Vec");
179 assert_eq!(template.args.len(), 1);
180 assert!(matches!(
181 template.args[0],
182 TypeSpec::SimpleTypeSpec(crate::typed_ast::SimpleTypeSpec::BaseTypeSpec(
183 crate::typed_ast::BaseTypeSpec::IntegerType(_)
184 ))
185 ));
186 }
187
188 #[test]
189 fn parse_doc_comments_as_doc_annotation() {
190 let typed = parser_text(
191 r#"
192 /// module doc
193 module m {
194 /// struct doc
195 struct S {
196 /// field doc
197 long x;
198 };
199 };
200 "#,
201 )
202 .expect("parse should succeed");
203
204 let module = match &typed.0[0] {
205 Definition::ModuleDcl(module) => module,
206 other => panic!("expected module, got {other:?}"),
207 };
208 assert_has_doc(&module.annotations, "\"module doc\"");
209
210 let type_dcl = match &module.definition[0] {
211 Definition::TypeDcl(type_dcl) => type_dcl,
212 other => panic!("expected type declaration, got {other:?}"),
213 };
214 assert_has_doc(&type_dcl.annotations, "\"struct doc\"");
215
216 let struct_def = match &type_dcl.decl {
217 TypeDclInner::ConstrTypeDcl(crate::typed_ast::ConstrTypeDcl::StructDcl(
218 crate::typed_ast::StructDcl::StructDef(def),
219 )) => def,
220 other => panic!("expected struct def, got {other:?}"),
221 };
222 let member = &struct_def.member[0];
223 assert_has_doc(&member.annotations, "\"field doc\"");
224 }
225
226 fn assert_has_doc(annotations: &[AnnotationAppl], expected: &str) {
227 let doc = annotations.iter().find_map(|anno| match &anno.name {
228 AnnotationName::Builtin(name) if name == "doc" => match &anno.params {
229 Some(AnnotationParams::Raw(raw)) => Some(raw.as_str()),
230 _ => None,
231 },
232 _ => None,
233 });
234 assert_eq!(doc, Some(expected));
235 }
236}