panproto_parse/languages/
common.rs1use panproto_schema::{Protocol, Schema};
13
14use crate::error::ParseError;
15use crate::registry::AstParser;
16use crate::theory_extract::{ExtractedTheoryMeta, extract_theory_from_node_types};
17use crate::walker::{AstWalker, WalkerConfig};
18
19pub struct LanguageParser {
24 protocol_name: String,
26 extensions: Vec<&'static str>,
28 language: tree_sitter::Language,
30 theory_meta: ExtractedTheoryMeta,
32 protocol: Protocol,
34 walker_config: WalkerConfig,
36}
37
38impl LanguageParser {
39 pub fn from_language(
45 protocol_name: &str,
46 extensions: Vec<&'static str>,
47 language: tree_sitter::Language,
48 node_types_json: &[u8],
49 walker_config: WalkerConfig,
50 ) -> Result<Self, ParseError> {
51 let theory_name = format!("Th{}FullAST", capitalize_first(protocol_name));
52 let theory_meta = extract_theory_from_node_types(&theory_name, node_types_json)?;
53 let protocol = build_full_ast_protocol(protocol_name, &theory_name);
54
55 Ok(Self {
56 protocol_name: protocol_name.to_owned(),
57 extensions,
58 language,
59 theory_meta,
60 protocol,
61 walker_config,
62 })
63 }
64}
65
66impl AstParser for LanguageParser {
67 fn protocol_name(&self) -> &str {
68 &self.protocol_name
69 }
70
71 fn parse(&self, source: &[u8], file_path: &str) -> Result<Schema, ParseError> {
72 let mut parser = tree_sitter::Parser::new();
73 parser
74 .set_language(&self.language)
75 .map_err(|e| ParseError::TreeSitterParse {
76 path: format!("{file_path}: set_language failed: {e}"),
77 })?;
78
79 let tree = parser
80 .parse(source, None)
81 .ok_or_else(|| ParseError::TreeSitterParse {
82 path: format!("{file_path}: parse returned None (timeout or cancellation)"),
83 })?;
84
85 let walker = AstWalker::new(
86 source,
87 &self.theory_meta,
88 &self.protocol,
89 self.walker_config.clone(),
90 );
91
92 walker.walk(&tree, file_path)
93 }
94
95 fn emit(&self, schema: &Schema) -> Result<Vec<u8>, ParseError> {
96 emit_from_schema(schema, &self.protocol_name)
106 }
107
108 fn supported_extensions(&self) -> &[&str] {
109 &self.extensions
110 }
111
112 fn theory_meta(&self) -> &ExtractedTheoryMeta {
113 &self.theory_meta
114 }
115}
116
117fn emit_from_schema(schema: &Schema, protocol: &str) -> Result<Vec<u8>, ParseError> {
128 let mut fragments: Vec<(usize, String)> = Vec::new();
131
132 for name in schema.vertices.keys() {
133 if let Some(constraints) = schema.constraints.get(name) {
134 let start_byte = constraints
136 .iter()
137 .find(|c| c.sort.as_ref() == "start-byte")
138 .and_then(|c| c.value.parse::<usize>().ok());
139
140 let literal = constraints
142 .iter()
143 .find(|c| c.sort.as_ref() == "literal-value")
144 .map(|c| c.value.clone());
145
146 if let (Some(start), Some(text)) = (start_byte, literal) {
147 fragments.push((start, text));
148 }
149
150 for c in constraints {
153 let sort_str = c.sort.as_ref();
154 if sort_str.starts_with("interstitial-") {
155 let pos_sort = format!("{sort_str}-start-byte");
158 let pos = constraints
159 .iter()
160 .find(|c2| c2.sort.as_ref() == pos_sort.as_str())
161 .and_then(|c2| c2.value.parse::<usize>().ok());
162
163 if let Some(p) = pos {
164 fragments.push((p, c.value.clone()));
165 }
166 }
167 }
168 }
169 }
170
171 if fragments.is_empty() {
172 return Err(ParseError::EmitFailed {
173 protocol: protocol.to_owned(),
174 reason: "schema has no text fragments".to_owned(),
175 });
176 }
177
178 fragments.sort_by_key(|(pos, _)| *pos);
180
181 let mut output = Vec::new();
184 let mut cursor = 0;
185
186 for (pos, text) in &fragments {
187 if *pos >= cursor {
188 output.extend_from_slice(text.as_bytes());
189 cursor = pos + text.len();
190 }
191 }
192
193 Ok(output)
194}
195
196fn build_full_ast_protocol(protocol_name: &str, theory_name: &str) -> Protocol {
201 Protocol {
202 name: protocol_name.into(),
203 schema_theory: theory_name.into(),
204 instance_theory: format!("{theory_name}Instance"),
205 schema_composition: None,
206 instance_composition: None,
207 obj_kinds: vec![],
208 edge_rules: vec![],
209 constraint_sorts: vec![
210 "literal-value".into(),
211 "literal-type".into(),
212 "operator".into(),
213 "visibility".into(),
214 "mutability".into(),
215 "async".into(),
216 "static".into(),
217 "generator".into(),
218 "comment".into(),
219 "indent".into(),
220 "trailing-comma".into(),
221 "semicolon".into(),
222 "blank-lines-before".into(),
223 "start-byte".into(),
224 "end-byte".into(),
225 ],
226 has_order: true,
227 has_coproducts: false,
228 has_recursion: true,
229 has_causal: false,
230 nominal_identity: false,
231 has_defaults: false,
232 has_coercions: false,
233 has_mergers: false,
234 has_policies: false,
235 }
236}
237
238fn capitalize_first(s: &str) -> String {
240 let mut chars = s.chars();
241 chars.next().map_or_else(String::new, |c| {
242 c.to_uppercase().collect::<String>() + chars.as_str()
243 })
244}