treeedbgen_souffle/
gen.rs

1// TODO(lb): Refactor this horrible mess!
2// TODO(#14): Support "narrow" relational schema
3// TODO(#17): Configurable case conventions
4// TODO(lb): Optional extra spaces
5
6use std::io;
7use std::io::Write;
8
9use heck::ToUpperCamelCase;
10use thiserror::Error;
11use treeedbgen::Node;
12
13// TODO(lb): Add indices for each field
14fn 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        // Union type of all types this field could be
43        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            // TODO(lb)
47            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        // TODO(#18): Configurable field prefix/suffix
73        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!(w, ".output {}(IO=stdout)", field_relation_name)?;
81        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    // TODO(#20): Other inline relations like this one
215    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        // writeln!(w, ".output {}field(IO=stdout)", config.relation_prefix)?;
257    }
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}