I'm trying to figure out if I can use Red (or Rebol) to implement a simple DSL. I want to compile my DSL source code for another language, perhaps Red or C # or both, instead of directly interpreting and executing it.
DSL has only a few simple statements, plus an if / else statement. Statements can be grouped into rules. The rule will be translated into a function definition, with each operator representing an equivalent operator in the target language.
The parsing capability in Red / Rebol is great and allows me to implement the parser very easily - in fact it is just a definition of the grammar itself.
However, I could not find examples of how to take the following steps, especially by processing the if statement and translating it to another source code. Translating an if statement seems like a good example of something minimal, but still a bit complicated - because in Red, having a different one, means that you need to change the if value to another, and not just an optional optional other.
Traditionally, during parsing, I created an abstract syntax tree, and then had functions for working in AST and generating new source code. Should I follow the same approach or is there some other more idiomatic way in red?
I experimented using the collect / keep command in my analysis rules to return a block of nested blocks, which actually forms an AST. Another approach would be to store data in specific objects representing different instructions, etc.
I am still going to collect / store when a new block is created and what will be saved. I would also like my parsing rules to be as clean as possible, since few other code was interwoven in it. Therefore, I still do not know how best to add the Red Code in parentheses in the parsing rules. Adding code too early can cause the red code to execute even if the rule ultimately fails. Adding code too late means that the code may not execute in the expected order, especially when working with multilevel instructions such as if, which may contain other statements.
So, in particular, any help on how to translate my DSL example to red source code will be greatly appreciated. Also, any links to implementing DSL like this in Red or Rebol would be great! :)
Here are my parsing rules: -
Red [ Purpose: example rules for parsing a simple language ] SimpleLanguageParser: make object! [ Expr: [string! | integer! | block!] Data: ['Person.AGE | 'Person.INCOME] WriteMessageToLog: ['write 'message 'to 'log Expr] SetData: ['set 'data Data '= Expr] IfStatement: ['if Expr [any Statement] opt ['else [any Statement]] 'endif] Statement: [WriteMessageToLog | SetData | IfStatement] Rule: [ 'rule word! [any Statement] 'endrule ] AnySimpLeLanguage: [Rule | [any Statement]] ] SL: function [slInput] [ parse slInput SimpleLanguageParser/AnySimpleLanguage ]
An example of some source in DSL: -
RULE TooYoung IF [Person.Age < 15] WRITE MESSAGE TO LOG "too young to earn an income" SET DATA Person.Income = 0 ELSE WRITE MESSAGE TO LOG "old enough" ENDIF ENDRULE
Translation into red source code: -
TooYoung: function [] [ either Person.Age < 15 [ WriteMessageToLog "too young to earn an income" Person.Income: 0 ] [ WriteMessageToLog "old enough" ] ]
Data i.e. Person.Age, Person.Income, and the WriteMessageToLog function are all that were previously defined. Please note: for simplicity, I left Expr as a block! etc., and not define Expr in more detail in the DSL itself. In addition, setting Person.Income in a function does not work the way it is encoded, since it sets local, but now it's fine :)