Generis, a versatile Go code generator for generics, free-form macros, conditional compilation, HTML templating and Allman style conversion


(Ecstatic Coder) #1

Just to inform you that I’ve just released a first version of Generis, a lightweight code preprocessor adding the following features to the Go language :

  • Generics.
  • Free-form macros.
  • Conditional compilation.
  • HTML templating.
  • Allman style conversion.

https://github.com/senselogic/GENERIS

It’s similar in function to both Ego and Genny, but implemented as a free-form C++ like preprocessor.

Probably of no use at all for anyone who likes to develop Go code in a purely idiomatic way, which obviously I’m not…

Sample :

package main;

// -- IMPORTS

import (
    "html"
    "io"
    "log"
    "net/http"
    "strconv"
    );

// -- DEFINITIONS

#define DebugMode
#as true

// ~~

#define HttpPort
#as 8080

// ~~

#define WriteLine( {{text}} )
#as log.Println( {{text}} )

// ~~

#define local {{variable}} : {{type}};
#as var {{variable}} {{type}};

// ~~

#define DeclareStack( {{type}}, {{name}} )
#as
    // -- TYPES

    type {{name}}Stack struct
    {
        ElementArray []{{type}};
    }

    // -- INQUIRIES

    func ( stack * {{name}}Stack ) IsEmpty(
        ) bool
    {
        return len( stack.ElementArray ) == 0;
    }

    // -- OPERATIONS

    func ( stack * {{name}}Stack ) Push(
        element {{type}}
        )
    {
        stack.ElementArray = append( stack.ElementArray, element );
    }

    // ~~

    func ( stack * {{name}}Stack ) Pop(
        ) {{type}}
    {
        local
            element : {{type}};

        element = stack.ElementArray[ len( stack.ElementArray ) - 1 ];

        stack.ElementArray = stack.ElementArray[ : len( stack.ElementArray ) - 1 ];

        return element;
    }
#end

// ~~

#define DeclareStack( {{type}} )
#as DeclareStack( {{type}}, {{type:PascalCase}} )

// -- TYPES

DeclareStack( string )
DeclareStack( int32 )

// -- FUNCTIONS

func HandleRootPage(
    response_writer http.ResponseWriter,
    request * http.Request
    )
{
    local
        boolean : bool;
    local
        natural : uint;
    local
        integer : int;
    local
        real : float64;
    local
        escaped_text,
        text : string;
    local
        integer_stack : Int32Stack;

    boolean = true;
    natural = 10;
    integer = 20;
    real = 30.0;
    text = "text";
    escaped_text = "<escaped text/>";

    integer_stack.Push( 10 );
    integer_stack.Push( 20 );
    integer_stack.Push( 30 );

    #write response_writer
        <!DOCTYPE html>
        <html lang="en">
            <head>
                <meta charset="utf-8">
                <title><%= request.URL.Path %></title>
            </head>
            <body>
                <% if ( boolean ) { %>
                    <%= "URL : " + request.URL.Path %>
                    <br/>
                    <%@ natural %>
                    <%# integer %>
                    <%& real %>
                    <br/>
                    <%~ text %>
                    <%= escaped_text %>
                    <%= "<%% ignored %%>" %>
                    <%% ignored %%>
                <% } %>
                <br/>
                Stack :
                <br/>
                <% for !integer_stack.IsEmpty() { %>
                    <%# integer_stack.Pop() %>
                <% } %>
            </body>
        </html>
    #end
}

// ~~

func main()
{
    http.HandleFunc( "/", HandleRootPage );

    #if DebugMode
        WriteLine( "Listening on http://localhost:HttpPort" );
    #end

    log.Fatal(
        http.ListenAndServe( ":HttpPort", nil )
        );
}

(Curtis Allyn Green) #2

Not to be rude but why write new python when there is already python?


(Ecstatic Coder) #3

Actually I had to use Go for a web server, and I missed a lot several Crystal features (generics, macros, mixins, modules, etc), so I’ve implemented Generis to be able to use them too for Go development.

I know that you can develop anything just with interfaces and reflection using idiomatic Go code, and I’m not criticizing Go purists at all for not wanting to pollute their code with generics, etc.

But those additional Crystal-like features, while not required, are incredibly convenient to me, and moreover have the added benefit that the generated code often runs significantly faster than its interface/reflection based equivalent.

So I’ve released Generis in open source so that other people can use it too, but I definitely know that most gophers won’t see any interest in using it, which is fine by me.


(Johan Dahl) #4

I think the macros are nice. The only thing I don’t understand is why you make a macro to change var into local and introduce semicolons at the end of statements? This I just don’t get


(Ecstatic Coder) #5

The “local :” macro is just an example of a free-form macro. Its only purpose is to show you can define macros with custom delimiters :slight_smile:

And I use semicolons not only by personal preference, but also because this helps the line joiner see where the statements end. But if you don’t use the “–join” option to be able to use Allman style indentation, you don’t need any semicolon, as the Go compiler will automatically add them for you.