phile/dalgen/
mod.rs

1//
2// dalgen/mod.rs
3// The PHiLe Compiler
4//
5// Created by Arpad Goretity (H2CO3)
6// on 02/06/2017
7//
8
9//! DALGen, the backend of the PHiLe compiler. This is the part
10//! that generates actual code in your programming language of
11//! choice for a Database Abstraction Layer from optimized SQIR.
12
13mod rust;
14mod c;
15mod cxx;
16mod objc;
17mod swift;
18mod go;
19mod js;
20mod python;
21mod ruby;
22mod java;
23mod csharp;
24mod haskell;
25
26use std::io;
27use std::rc::Rc;
28use std::cell::RefCell;
29use heck::{ SnakeCase, ShoutySnakeCase, MixedCase, CamelCase };
30use error::Result;
31use sqir::*;
32
33
34/// The database engine flavor for which a Database Abstraction Layer will be generated.
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
36pub enum DatabaseEngine {
37    /// SQLite, a very lightweight, embeddable, relational database.
38    SQLite3,
39    /// MongoDB, a JSON document store.
40    MongoDB,
41    /// MariaDB, a GPL fork of the MySQL RMDBS.
42    MariaDB,
43}
44
45/// The programming language in which the DAL will be implemented.
46#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
47pub enum Language {
48    /// Rust 1.x.
49    Rust,
50    /// C. 89/90? 99? 11? I don't know yet.
51    C,
52    /// Modern C++ (11, 14, 17).
53    CXX,
54    /// Modern Objective-C (2.0).
55    ObjectiveC,
56    /// Swift. I don't write a version number because it will change anyway.
57    Swift,
58    /// Go 1.x.
59    Go,
60    /// ECMAScript, if we are being technical.
61    JavaScript,
62    /// Python. 2 or 3? I don't know yet.
63    Python,
64    /// Ruby.
65    Ruby,
66    /// Java. Yeah, seriously.
67    Java,
68    /// C#
69    CSharp,
70    /// Haskell. Unicorns!
71    Haskell,
72}
73
74/// The strategy with which the generated DAL will query the DB.
75#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
76pub enum DatabaseAccessMode {
77    /// POD, or Plain Old Object: objects returned from the DAL are **immutable** and do **not**
78    /// automatically synchronize their state with the DB. This may be better for performance.
79    Pod,
80    /// Active Records: objects returned by the DAL are **mutable** and they **automatically
81    /// synchronize** their state with the DB. May be better for application-level consistency.
82    ActiveRecord,
83}
84
85/// The `default()` database access mode is POD.
86impl Default for DatabaseAccessMode {
87    fn default() -> Self {
88        DatabaseAccessMode::Pod
89    }
90}
91
92/// The rewriting strategy applied to various kinds of named program elements,
93/// such as function, variable, and type names.
94/// In the `CodegenParams` struct, optional instances of this transform are
95/// specified, where `None` means "default for the programming language".
96#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
97pub enum NameTransform {
98    /// Don't touch it!
99    Identity,
100    /// `lower_snake_case`. Isn't SnakeCase spelled with camel case ironic, by the way?
101    LowerSnakeCase,
102    /// `UPPER_SNAKE_CASE`. Use this only when you are frustrated and want to yell.
103    UpperSnakeCase,
104    /// `lowerCamelCase`.
105    LowerCamelCase,
106    /// `UpperCamelCase`, also known as `PascalCase`.
107    UpperCamelCase,
108}
109
110/// A bunch of centralized settings governing the behavior of DALGen.
111#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
112pub struct CodegenParams {
113    /// The database flavor to be targeted. See the docs of `DatabaseEngine`.
114    pub database: DatabaseEngine,
115    /// The programming language to be targeted. See the docs of `Language`.
116    pub language: Language,
117    /// Database access strategy. See the docs for `DatabaseAccessMode`.
118    pub database_access_mode: Option<DatabaseAccessMode>,
119    /// Namespace name. This will be used in different ways for different
120    /// programming languages. For example, in Go, it will be the package
121    /// name, and is thus **mandatory**.
122    pub namespace: Option<String>,
123    /// The transform to be applied to names of user-defined types.
124    pub type_name_transform: Option<NameTransform>,
125    /// The transform to be applied to fields of `struct`s and `class`es.
126    pub field_name_transform: Option<NameTransform>,
127    /// The transform to be applied to variants of `enum`s.
128    pub variant_name_transform: Option<NameTransform>,
129    /// The transform to be applied to names of user-defined functions.
130    pub func_name_transform: Option<NameTransform>,
131    /// The transform to be applied to the name of the namespace.
132    pub namespace_transform: Option<NameTransform>,
133}
134
135/// Functions of this type are expected to yield a (possibly cached)
136/// `io::Write` object that DALGen can write to. The cache key,
137/// specified as the string parameter of the function, is sometimes
138/// a file name derived from the `impl` of a user-defined type.
139///
140/// TODO(H2CO3): rewrite this using `RcCell` once custom smart pointers
141/// can point to trait objects, i.e. when `CoerceUnsized` and `Unsize`
142/// are stabilized. See [issue #27732](https://github.com/rust-lang/rust/issues/27732).
143pub type WriterProvider = FnMut(&str) -> Result<Rc<RefCell<io::Write>>>;
144
145
146/// Given the SQIR representation of a program, and some configuration parameters,
147/// generates a Database Abstraction Layer and writes the code into `io::Write`s.
148///
149/// # Arguments:
150///
151/// * `sqir`: Optimized SQIR intermediate representation for a PHiLe program.
152/// * `params`: code generation parameters; see the docs for `CodegenParams`.
153/// * `wp`: a provider of `io::Write`s that will accumulate generated code.
154///
155/// # Return value:
156///
157/// * `Ok(())`, if code generation completed successfully.
158/// * `Err(Error)`, if an error occurred. This is typically an I/O error (`Error::IO`)
159///   or an `Error::Semantic` induced by some special requirement of the backend
160///   (e.g. a missing namespace when generating Go code).
161pub fn generate_dal(sqir: &Sqir, params: &CodegenParams, wp: &mut WriterProvider) -> Result<()> {
162    use self::Language::*;
163
164    match params.language {
165        Rust       => rust   ::generate_dal(sqir, params, wp),
166        C          => c      ::generate_dal(sqir, params, wp),
167        CXX        => cxx    ::generate_dal(sqir, params, wp),
168        ObjectiveC => objc   ::generate_dal(sqir, params, wp),
169        Swift      => swift  ::generate_dal(sqir, params, wp),
170        Go         => go     ::generate_dal(sqir, params, wp),
171        JavaScript => js     ::generate_dal(sqir, params, wp),
172        Python     => python ::generate_dal(sqir, params, wp),
173        Ruby       => ruby   ::generate_dal(sqir, params, wp),
174        Java       => java   ::generate_dal(sqir, params, wp),
175        CSharp     => csharp ::generate_dal(sqir, params, wp),
176        Haskell    => haskell::generate_dal(sqir, params, wp),
177    }
178}
179
180//
181// Name Transforms
182//
183
184fn transform_type_name(name: &str, params: &CodegenParams) -> String {
185    transform_name(
186        name,
187        params.type_name_transform,
188        params.language,
189        default_type_name_transform,
190    )
191}
192
193fn transform_field_name(name: &str, params: &CodegenParams) -> String {
194    transform_name(
195        name,
196        params.field_name_transform,
197        params.language,
198        default_field_name_transform,
199    )
200}
201
202fn transform_variant_name(name: &str, params: &CodegenParams) -> String {
203    transform_name(
204        name,
205        params.variant_name_transform,
206        params.language,
207        default_variant_name_transform,
208    )
209}
210
211fn transform_func_name(name: &str, params: &CodegenParams) -> String {
212    transform_name(
213        name,
214        params.func_name_transform,
215        params.language,
216        default_func_name_transform,
217    )
218}
219
220fn transform_namespace(name: &str, params: &CodegenParams) -> String {
221    transform_name(
222        name,
223        params.namespace_transform,
224        params.language,
225        default_namespace_transform,
226    )
227}
228
229fn transform_name<D>(
230    name: &str,
231    transform: Option<NameTransform>,
232    lang: Language,
233    default: D,
234) -> String
235    where D: FnOnce(Language) -> NameTransform {
236
237    use self::NameTransform::*;
238
239    match transform.unwrap_or_else(|| default(lang)) {
240        Identity       => name.to_owned(),
241        LowerSnakeCase => name.to_snake_case(),
242        UpperSnakeCase => name.to_shouty_snake_case(),
243        LowerCamelCase => name.to_mixed_case(),
244        UpperCamelCase => name.to_camel_case(),
245    }
246}
247
248fn default_type_name_transform(lang: Language) -> NameTransform {
249    use self::Language::*;
250    use self::NameTransform::*;
251
252    match lang {
253        Rust       => UpperCamelCase,
254        C          => UpperCamelCase,
255        CXX        => UpperCamelCase,
256        ObjectiveC => UpperCamelCase,
257        Swift      => UpperCamelCase,
258        Go         => UpperCamelCase,
259        JavaScript => UpperCamelCase,
260        Python     => UpperCamelCase,
261        Ruby       => UpperCamelCase,
262        Java       => UpperCamelCase,
263        CSharp     => UpperCamelCase,
264        Haskell    => UpperCamelCase,
265    }
266}
267
268fn default_field_name_transform(lang: Language) -> NameTransform {
269    use self::Language::*;
270    use self::NameTransform::*;
271
272    match lang {
273        Rust       => LowerSnakeCase,
274        C          => LowerSnakeCase,
275        CXX        => LowerSnakeCase,
276        ObjectiveC => LowerCamelCase,
277        Swift      => LowerCamelCase,
278        Go         => UpperCamelCase,
279        JavaScript => LowerCamelCase,
280        Python     => LowerSnakeCase,
281        Ruby       => LowerSnakeCase,
282        Java       => LowerCamelCase,
283        CSharp     => UpperCamelCase,
284        Haskell    => LowerCamelCase,
285    }
286}
287
288fn default_variant_name_transform(lang: Language) -> NameTransform {
289    use self::Language::*;
290    use self::NameTransform::*;
291
292    match lang {
293        Rust       => UpperCamelCase,
294        C          => UpperCamelCase,
295        CXX        => UpperCamelCase,
296        ObjectiveC => UpperCamelCase,
297        Swift      => LowerCamelCase, // new Swift enums suck :-(
298        Go         => UpperCamelCase,
299        JavaScript => UpperCamelCase,
300        Python     => UpperSnakeCase,
301        Ruby       => UpperSnakeCase,
302        Java       => UpperSnakeCase,
303        CSharp     => UpperCamelCase,
304        Haskell    => UpperCamelCase,
305    }
306}
307
308fn default_func_name_transform(lang: Language) -> NameTransform {
309    use self::Language::*;
310    use self::NameTransform::*;
311
312    match lang {
313        Rust       => LowerSnakeCase,
314        C          => LowerSnakeCase,
315        CXX        => LowerSnakeCase,
316        ObjectiveC => LowerCamelCase,
317        Swift      => LowerCamelCase,
318        Go         => UpperCamelCase,
319        JavaScript => LowerCamelCase,
320        Python     => LowerSnakeCase,
321        Ruby       => LowerSnakeCase,
322        Java       => LowerCamelCase,
323        CSharp     => UpperCamelCase,
324        Haskell    => LowerCamelCase,
325    }
326}
327
328fn default_namespace_transform(lang: Language) -> NameTransform {
329    use self::Language::*;
330    use self::NameTransform::*;
331
332    // TODO(H2CO3): adjust these according to idiomatic use in each language
333    match lang {
334        Rust       => Identity,
335        C          => Identity,
336        CXX        => Identity,
337        ObjectiveC => Identity,
338        Swift      => Identity,
339        Go         => Identity,
340        JavaScript => Identity,
341        Python     => Identity,
342        Ruby       => Identity,
343        Java       => Identity,
344        CSharp     => Identity,
345        Haskell    => Identity,
346    }
347}