According to Martin Fowler:

semantic-layerIn the context of a DSL, a semantic model is an in-memory representation, usually an object model, of the same subject that the DSL describes. The Semantic Model increases the flexibility in parsing and also in execution. You can execute the Semantic Model directly or you can use code generation. If you’re using code generation you can base that code generation off the Semantic Model which completely decouples it from parsing.

In this lesson, after parsing the user submission through the DSL parse rule, we will store into an intermediate Object Model based on Rebol’s Object which can be easily persisted (this is the equivalent of an UML-XMI storage but with the advantage of being human-readable as Rebol’s Object is close to Json format though invented before).

Copy and paste the code below from previous lessons (see “Create your own Java Code Generator with DSL in 15 minutes [part 3]“) in Rebol’s Console:


rule: ["Create " ["a " | ] copy Language to " class" thru " with " copy attributes-list to end (generate-code)]

generate-code: func[][
    attributes: parse/all attributes-list ","
    switch Language [
        "C#" [generate-code-csharp attributes]
        "java" [generate-code-java attributes]
    ] /default [print ["Language" Language "not yet implemented"]]
]

Now copy and paste:


phrase: "Create a C# class with Id, First Name, Last Name, Birth Date"
parse phrase rule

or if you prefer java:


phrase: "Create a java class with Id, First Name, Last Name, Birth Date"
parse phrase rule

If you type attributes in Console it would show a block of strings:


>> attributes
== ["Id" " First Name" " Last Name" " Birth Date"]

We would like to create a Rebol Object representation of these attributes. This is very easy to do as we have already seen how to transform a flow of strings into a Rebol’s Object in this article “How to create an object dynamically from a persistent storage and save it back (Application Configuration File)“. So the new generate-code function will be:


generate-code: func[][
    attributes: parse/all attributes-list ","

    ;Start dynamic object building
    string-list: {}
    foreach attribute attributes [
        attribute: trim/all attribute; remove all whitespace
        append string-list join attribute [": " newline]
    ]
    Object: construct load string-list
    ;End dynamic object building

    switch Language [
        "C#" [generate-code-csharp attributes]
        "java" [generate-code-java attributes]
    ] /default [print ["Language" Language "not yet implemented"]]
]

Copy and paste again to test the new generate-code function:


phrase: "Create a C# class with Id, First Name, Last Name, Birth Date"
parse phrase rule

or if you prefer java:


phrase: "Create a java class with Id, First Name, Last Name, Birth Date"
parse phrase rule

Now look at the Object content by typing Probe Object:


>> probe Object
make object! [
    Id: none
    FirstName: none
    LastName: none
    BirthDate: none
]

To iterate through this Object’s Members we can use the members function we built in this article “Rebol’s Reflection based on its Language Homoiconicity Property (Code-Data duality property)“:


members: func[Object /local block][
    block: copy []
    foreach element pick to-block mold object 3 [
        if set-word? element [
            append block element
        ]
    ]
    block
]

If you use it on Object you would get:


>> members-list: members Object
== [
    Id:
    FirstName:
    LastName:
    BirthDate:
]
>>
>> pick members Object 1
== Id:
>> pick members Object 2
== FirstName:
>> pick members Object 3
== LastName:
>> pick members Object 4
== BirthDate:
>>

Let’s ask the type of each member and store it in the Object:


foreach member members-list [
    attribute-type: if/else ((attribute-answer: ask ["attribute-type for" member "(ex. string) : " ]) = "") ["string"][attribute-answer]
    set in Object (to-word member) attribute-type
]
probe Object

This will output:


make object! [
    Id: "string"
    FirstName: "string"
    LastName: "string"
    BirthDate: "string"
]
>>

To persist the Object to File just write it:


write %object.txt Object

and to reload it to Object, use construct load as already seen in “How to create an object dynamically from a persistent storage and save it back (Application Configuration File)“:


Object: construct load %object.txt

We will change the generate-code function to this below (instead of attributes we pass the whole Object):


generate-code: func[][
    attributes: parse/all attributes-list ","

    ;Start dynamic object building
    string-list: {}
    foreach attribute attributes [
        attribute: trim/all attribute; remove all whitespace
        append string-list join attribute [": " newline]
    ]
    Object: construct load string-list
    ;End dynamic object building

foreach member members-list [
    attribute-type: if/else ((attribute-answer: ask ["attribute-type for" member "(ex. string) : " ]) = "") ["string"][attribute-answer]
    set in Object (to-word member) attribute-type
]

    switch Language [
        "C#" [generate-code-csharp Object]; Object passing
        "java" [generate-code-java Object]; Object passing
    ] /default [print ["Language" Language "not yet implemented"]]
]

In the generate-code-csharp and generate-code-java we will change these former lines:


        attribute-name: trim/all attribute; remove all whitespaces
        attribute-type: if/else ((answer: ask ["attribute-type for" attribute "(ex. String) : " ]) = "") ["String"][answer]

into:


    attributes: members Object
    ;...
        attribute-name: pick members Object counter
        attribute-type: get in Object to-word attribute-name

In final:


generate-code-csharp: func[Object][

    attributes: members Object

    namespace: if/else ((answer: ask "namespace (ex.:MyApp): ") = "") ["MyApp"][answer]
    class-name: if/else ((answer: ask "class-name (ex.: Cperson): ") = "") ["CPerson"][answer]
    private-prefix: "_"
    code-template: {using System;

// Generated by http://www.reboltutorial.com
// Template based on http://www.csharpfriends.com/demos/csharp_class_generator.aspx

namespace <%namespace%>
{
    public class <%class-name%>
    {
        // private members
        <%private-members%>

        // empty constructor
        public <%class-name%> ()
        {
        }

        // full constructor
        public <%class-name%> (<%private-constructor-arguments-list%>)
        {
            <%private-constructor-body%>
        }

       // public accessors
        <%public-accessors%>
    }
}
}; end of C# class template

private-members-template: {<%attribute-type%> <%private-prefix%><%attribute-name%>;}

private-constructor-arguments-list-template: {<%attribute-type%> <%attribute-name%>}
private-constructor-body-template: {this.<%private-prefix%><%attribute-name%> = <%attribute-name%>;}
public-accessors-template: {public <%attribute-type%> <%attribute-name%> {
            get { return <%private-prefix%><%attribute-name%>;}
            set { <%private-prefix%><%attribute-name%> = value; }
        }

}

    private-members: ""
    private-constructor-arguments-list: ""
    private-constructor-body: ""
    public-accessors: ""

    n: length? attributes
    counter: 0
    foreach attribute attributes [
        counter: counter + 1
        attribute-name: to-string pick attributes counter
        attribute-type: get in Object to-word attribute-name

        if (counter > 1) [
            append private-members "        "
            append private-constructor-body "            "
            append public-accessors "        "
        ]

        append private-members build-markup private-members-template
        append private-constructor-arguments-list build-markup private-constructor-arguments-list-template
        append private-constructor-body build-markup private-constructor-body-template
        append public-accessors build-markup public-accessors-template

        if (counter < n) [
            append private-members newline
            append private-constructor-arguments-list ", "
            append private-constructor-body newline
        ]
    ]
    source-code: build-markup code-template
    write clipboard:// source-code
    Print "source code copied into clipboard ..."
    input
]


generate-code-java: func[Object][

    attributes: members Object

    package: if/else ((answer: ask "package (ex.:MyApp): ") = "") ["MyApp"][answer]
    class-name: if/else ((answer: ask "class-name (ex.: Cperson): ") = "") ["CPerson"][answer]
    private-prefix: "_"
    code-template: {// Generated by http://www.reboltutorial.com

package <%package%>;

    public class <%class-name%>
    {
        // private members
        <%private-members%>

        // empty constructor
        public <%class-name%> ()
        {
        }

        // full constructor
        public <%class-name%> (<%private-constructor-arguments-list%>)
        {
            <%private-constructor-body%>
        }

       // public accessors
        <%public-accessors%>
    }

}; end of Java class template

private-members-template: {<%attribute-type%> <%private-prefix%><%attribute-name%>;}

private-constructor-arguments-list-template: {<%attribute-type%> <%attribute-name%>}
private-constructor-body-template: {<%private-prefix%><%attribute-name%> = <%attribute-name%>;}
public-accessors-template: {//Accessors for <%attribute-name%>
        public <%attribute-type%> get<%attribute-name%>() {
            return <%private-prefix%><%attribute-name%>;
        }
        public void set<%attribute-name%>(<%attribute-type%> <%attribute-name%>) {
            <%private-prefix%><%attribute-name%>=<%attribute-name%>;
        }
}

    private-members: ""
    private-constructor-arguments-list: ""
    private-constructor-body: ""
    public-accessors: ""

    n: length? attributes
    counter: 0
    foreach attribute attributes [

        counter: counter + 1
        attribute-name: to-string pick attributes counter
        attribute-type: get in Object to-word attribute-name

        if (counter > 1) [
            append private-members "        "
            append private-constructor-body "            "
            append public-accessors "        "
        ]

        append private-members build-markup private-members-template
        append private-constructor-arguments-list build-markup private-constructor-arguments-list-template
        append private-constructor-body build-markup private-constructor-body-template
        append public-accessors build-markup public-accessors-template

        if (counter < n) [
            append private-members newline
            append private-constructor-arguments-list ", "
            append private-constructor-body newline
        ]
    ]
    source-code: build-markup code-template
    write clipboard:// source-code
    Print "source code copied into clipboard ..."
    input
]

Test the final code:


phrase: "Create a C# class with Id, First Name, Last Name, Birth Date"
parse phrase rule

or if you prefer java:


phrase: "Create a java class with Id, First Name, Last Name, Birth Date"
parse phrase rule

Bookmark and Share