1use std::io;
7use std::io::Write;
8
9use heck::ToUpperCamelCase;
10use thiserror::Error;
11use treeedbgen::Node;
12
13fn node_with_fields(
15 config: &PrivGenConfig,
16 w: &mut impl Write,
17 node: &Node,
18) -> Result<String, io::Error> {
19 let rel_name = &node.ty;
20 let type_name = node.ty.to_upper_camel_case();
21 writeln!(w, ".type {}{} <: symbol", config.type_prefix, type_name)?;
22 writeln!(
23 w,
24 ".decl {}{}(x: {}{})",
25 config.relation_prefix, rel_name, config.type_prefix, type_name
26 )?;
27 if config.printsize {
28 writeln!(w, ".printsize {}{}", config.relation_prefix, rel_name)?;
29 }
30 writeln!(
31 w,
32 "{}{}(as(x, {}{})) :- {}node(x, \"{}\", _, _, _, _, _, _, _, _, _, _, _).",
33 config.relation_prefix,
34 rel_name,
35 config.type_prefix,
36 type_name,
37 config.relation_prefix,
38 node.ty
39 )?;
40
41 for (field_name, field) in &node.fields {
42 let mut named_types: Vec<_> = field.types.iter().filter(|t| t.named).collect();
44 let field_type_name: String;
45 if named_types.is_empty() {
46 continue;
48 } else if named_types.len() == 1 {
49 let t = named_types.pop().unwrap();
50 field_type_name = format!("{}{}", config.type_prefix, t.ty.to_upper_camel_case());
51 } else {
52 field_type_name = format!(
53 "{}Field{}{}",
54 config.type_prefix,
55 node.ty.to_upper_camel_case(),
56 field_name.to_upper_camel_case(),
57 );
58 write!(w, ".type {} = ", field_type_name)?;
59 let mut types = Vec::new();
60 for t in &field.types {
61 if t.named {
62 types.push(format!(
63 "{}{}",
64 config.type_prefix,
65 t.ty.to_upper_camel_case()
66 ));
67 }
68 }
69 writeln!(w, "{}", types.join(" | "))?;
70 }
71
72 let field_relation_name =
74 format!("{}{}_{}_f", config.relation_prefix, &node.ty, field_name);
75 writeln!(
76 w,
77 ".decl {}(x: {}{}, y: {})",
78 field_relation_name, config.type_prefix, type_name, field_type_name
79 )?;
80 writeln!(
82 w,
83 "{}(x, as(y, {})) :- {}{}(x), {}field(x, \"{}\", y).",
84 field_relation_name,
85 field_type_name,
86 config.relation_prefix,
87 rel_name,
88 config.relation_prefix,
89 field_name,
90 )?;
91 }
92 Ok(format!("{}{}", config.type_prefix, type_name))
93}
94
95fn node_with_subtypes(
96 config: &PrivGenConfig,
97 w: &mut impl Write,
98 node: &Node,
99) -> Result<(), io::Error> {
100 if node.subtypes.is_empty() {
101 return Ok(());
102 }
103 write!(
104 w,
105 ".type {}{} = ",
106 config.type_prefix,
107 node.ty.to_upper_camel_case()
108 )?;
109 let mut types = Vec::new();
110 for subtype in &node.subtypes {
111 if subtype.named {
112 types.push(format!(
113 "{}{}",
114 config.type_prefix,
115 subtype.ty.to_upper_camel_case()
116 ));
117 }
118 }
119 writeln!(w, "{}", types.join(" | "))?;
120 Ok(())
121}
122
123fn gen_nodes(
124 config: &PrivGenConfig,
125 w: &mut impl Write,
126 nodes: Vec<Node>,
127) -> Result<Vec<String>, io::Error> {
128 let mut types = Vec::new();
129 for node in &nodes {
130 if node.named {
131 assert!(node.subtypes.is_empty() || node.fields.is_empty());
132 if !node.subtypes.is_empty() {
133 node_with_subtypes(config, w, node)?;
134 } else {
135 types.push(node_with_fields(config, w, node)?);
136 }
137 writeln!(w)?;
138 }
139 }
140 Ok(types)
141}
142
143#[derive(Error, Debug)]
144pub enum GenError {
145 #[error("I/O error")]
146 Io(#[from] io::Error),
147 #[error("JSON parsing error")]
148 Json(#[from] serde_json::Error),
149}
150
151pub struct GenConfig {
152 pub printsize: bool,
153 pub prefix: Option<String>,
154}
155
156struct PrivGenConfig {
157 pub printsize: bool,
158 pub relation_prefix: String,
159 pub type_prefix: String,
160}
161
162impl PrivGenConfig {
163 fn new(config: &GenConfig) -> Self {
164 PrivGenConfig {
165 printsize: config.printsize,
166 relation_prefix: if let Some(pfx) = &config.prefix {
167 format!("{}_", pfx)
168 } else {
169 "".to_owned()
170 },
171 type_prefix: config
172 .prefix
173 .clone()
174 .unwrap_or_else(|| "".to_string())
175 .to_upper_camel_case(),
176 }
177 }
178}
179
180fn declare_node(config: &PrivGenConfig, w: &mut impl Write) -> Result<(), GenError> {
181 writeln!(w, ".type {}NodeKind <: symbol", config.type_prefix)?;
182 writeln!(w, ".type {}IsNamed <: symbol", config.type_prefix)?;
183 writeln!(w, ".type {}IsError <: symbol", config.type_prefix)?;
184 writeln!(w, ".type {}IsExtra <: symbol", config.type_prefix)?;
185 writeln!(w, ".type {}IsMissing <: symbol", config.type_prefix)?;
186 writeln!(w, ".type {}StartByte <: number", config.type_prefix)?;
187 writeln!(w, ".type {}EndByte <: number", config.type_prefix)?;
188 writeln!(w, ".type {}StartRow <: number", config.type_prefix)?;
189 writeln!(w, ".type {}StartCol <: number", config.type_prefix)?;
190 writeln!(w, ".type {}EndRow <: number", config.type_prefix)?;
191 writeln!(w, ".type {}EndCol <: number", config.type_prefix)?;
192 writeln!(w, ".type {}NodeText <: symbol", config.type_prefix)?;
193 writeln!(
194 w,
195 ".decl {}node({})",
196 config.relation_prefix,
197 vec![
198 format!("id: {}Node", config.type_prefix),
199 format!("kind: {}NodeKind", config.type_prefix),
200 format!("is_named: {}IsNamed", config.type_prefix),
201 format!("is_extra: {}IsExtra", config.type_prefix),
202 format!("is_error: {}IsError", config.type_prefix),
203 format!("is_missing: {}IsMissing", config.type_prefix),
204 format!("start_byte: {}StartByte", config.type_prefix),
205 format!("end_byte: {}EndByte", config.type_prefix),
206 format!("start_row: {}StartRow", config.type_prefix),
207 format!("start_col: {}StartCol", config.type_prefix),
208 format!("end_row: {}EndRow", config.type_prefix),
209 format!("end_col: {}EndCol", config.type_prefix),
210 format!("text: {}NodeText", config.type_prefix),
211 ]
212 .join(", ")
213 )?;
214 writeln!(
216 w,
217 ".decl {}node_text(x: {}Node, y: {}NodeText) inline",
218 config.relation_prefix, config.type_prefix, config.type_prefix,
219 )?;
220 writeln!(
221 w,
222 "{}node_text(x, y) :- {}node(x, _, _, _, _, _, _, _, _, _, _, _, y).",
223 config.relation_prefix, config.relation_prefix,
224 )?;
225 writeln!(
226 w,
227 ".input {}node(IO=file, filename=\"node.csv\", rfc4180=true)",
228 config.relation_prefix,
229 )?;
230 if config.printsize {
231 writeln!(w, ".printsize {}node", config.relation_prefix)?;
232 }
233 Ok(())
234}
235
236fn declare_field(config: &PrivGenConfig, w: &mut impl Write) -> Result<(), GenError> {
237 writeln!(w, ".type {}GrammarFieldName <: symbol", config.type_prefix)?;
238 writeln!(
239 w,
240 ".decl {}field({})",
241 config.relation_prefix,
242 vec![
243 format!("parent: {}Node", config.type_prefix),
244 format!("name: {}GrammarFieldName", config.type_prefix),
245 format!("child: {}Node", config.type_prefix),
246 ]
247 .join(", ")
248 )?;
249 writeln!(
250 w,
251 ".input {}field(IO=file, filename=\"field.csv\", rfc4180=true)",
252 config.relation_prefix,
253 )?;
254 if config.printsize {
255 writeln!(w, ".printsize {}field", config.relation_prefix)?;
256 }
258 Ok(())
259}
260
261pub fn gen(
262 config: &GenConfig,
263 w: &mut impl Write,
264 node_types_json_str: &str,
265) -> Result<(), GenError> {
266 writeln!(
267 w,
268 "// NOTE: This file was generated by treeedb v{}. Do not edit!",
269 env!("CARGO_PKG_VERSION")
270 )?;
271 let config = PrivGenConfig::new(config);
272 let types = gen_nodes(&config, w, treeedbgen::nodes(node_types_json_str)?)?;
273
274 writeln!(
275 w,
276 ".type {}Node = {}",
277 config.type_prefix,
278 types.join(" | ")
279 )?;
280
281 declare_node(&config, w)?;
282 declare_field(&config, w)?;
283
284 Ok(())
285}