Compiler code generation strategies other than string templating

(The question is not really specific to Rust. Asking here since a quite a few FMs here seem to be familiar with PL programming and, anyways, the tooling/platform used in this case is Rust.)

I am attempting to write a small DSL which emits (compiles to?) SQL DDL statements. I am using pest-parser to generate the parser. This parser is used to transform code in the DSL into Rust data-structures (structs). The data in these structs is then used to emit the target code using askama templates which are pre-defined.

The approach is working fine but it is getting tedious with lots of if, else branching and looping as I extend the language with more capabilities.

What are ways in which I can better handle code-generation keeping maintainability and ability to extend in perspective?

You could build the Rust code as an AST and then render that to a string. See syn and quote.

It will be great if you can elaborate? Just to be clear: neither the source nor the target language is Rust. Rust is the "glue language" in that it reads the source language and transforms it to the target language.

Oh oops. I misread your post. I thought Rust was the target language.

My point still stands but you'd need to write a Rust implementation of the target language's AST.

Alternatively, using someone else's AST crate might be a viable option. sqlparser's AST types seem to have implementions of Display that can write out queries/statements.

1 Like

Thanks! This is almost what I am doing as of now. The main difference being the authors of sqlparse have used the write! macro whereas I am using a full blown string template library.

And not surprisingly, the authors there have similar problem as noted in the comment at the top of the function linked in your post. :sweat_smile:

// Clippy thinks this function is too complicated, but it is painful to
// split up without extracting structs for each Statement variant.

Nevertheless, will investigate further into transforming my structs into their AST.

1 Like

@17cupsofcoffee Thanks! It turned out be a solid suggestion. I just need to transform between trees. This is cleaner and way less prone to bugs than custom conditionals or templates. :+1:

The flow now looks like:

  • DSL is parsed into a local AST used for semantic validation
  • The local AST is then transformed into sqlparser::ast::Statement
  • Then all that is needed is format! to generate the query

A shout-out to the people (not sure if they hangout here) behind the library sqlparser as well. It's pretty neat and structured in a way that enables such a use case very elegantly even though it's not the main goal of the library.