Golang通用代码生成器Generis:支持泛型、自由格式宏、条件编译及HTML模板转换

Golang通用代码生成器Generis:支持泛型、自由格式宏、条件编译及HTML模板转换 在此通知大家,我刚发布了 Generis 的首个版本。这是一个轻量级代码预处理器,为 Go 语言添加了以下功能:

  • 泛型
  • 自由形式宏
  • 条件编译
  • HTML 模板
  • Allman 风格转换

https://github.com/senselogic/GENERIS

其功能与 Ego 和 Genny 类似,但实现为类似 C++ 的自由形式预处理器。

对于喜欢纯粹惯用方式开发 Go 代码的人来说可能完全没用,显然我不是这样的人…

示例:

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 )
        );
}

更多关于Golang通用代码生成器Generis:支持泛型、自由格式宏、条件编译及HTML模板转换的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

无意冒犯,但既然已经有了Python,为什么还要写新的Python呢?

更多关于Golang通用代码生成器Generis:支持泛型、自由格式宏、条件编译及HTML模板转换的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我认为宏功能很不错。唯一不理解的是为什么要创建一个宏来将var改为local,并在语句末尾引入分号?这一点我实在无法理解

“local :” 宏只是自由格式宏的一个示例。其唯一目的是展示你可以使用自定义分隔符来定义宏 🙂

我使用分号不仅是出于个人偏好,还因为这有助于行连接器识别语句的结束位置。但如果你不使用 “–join” 选项来启用 Allman 风格缩进,就不需要任何分号,因为 Go 编译器会自动为你添加它们。

实际上我不得不使用Go来开发Web服务器,这让我非常怀念Crystal的诸多特性(泛型、宏、混入、模块等)。因此我开发了Generis,让这些特性也能在Go开发中使用。

我知道通过接口和反射,用符合Go语言习惯的代码就能实现任何功能。我完全没有批评那些坚持Go语言纯粹性、不想用泛型等特性"污染"代码的人。

但这些类似Crystal的附加特性虽然并非必需,对我来说却极其方便。更重要的是,它们还有个额外优势:生成的代码通常比基于接口/反射的等效实现运行速度要快得多。

所以我把Generis开源发布了,这样其他人也能使用它。不过我很清楚,大多数Go开发者可能对它不感兴趣,这我完全理解。

Generis 看起来是一个功能强大的 Go 代码预处理器,它通过宏和预处理指令扩展了 Go 语言的能力。以下是对示例代码中关键功能的解析:

1. 宏定义与替换

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

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

这些宏提供了语法糖,比如 WriteLine("hello") 会被替换为 log.Println("hello")local count : int; 会被展开为 var count int

2. 泛型栈实现

#define DeclareStack( {{type}}, {{name}} )
#as
    type {{name}}Stack struct {
        ElementArray []{{type}};
    }
    
    func ( stack * {{name}}Stack ) Push( element {{type}} ) {
        stack.ElementArray = append( stack.ElementArray, element );
    }
#end

DeclareStack( string )
DeclareStack( int32 )

通过宏生成类型安全的栈实现,DeclareStack(string) 会创建 StringStack 类型及其相关方法。

3. 条件编译

#define DebugMode
#as true

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

DebugMode 定义为 true 时,调试输出语句会被包含在最终代码中。

4. HTML 模板集成

#write response_writer
    <!DOCTYPE html>
    <html>
        <body>
            <% if ( boolean ) { %>
                <%= "URL : " + request.URL.Path %>
                <%@ natural %>
            <% } %>
        </body>
    </html>
#end

这个特性允许在 Go 代码中直接嵌入 HTML 模板,支持条件判断、变量输出等模板语法。

5. 类型安全的栈操作

local integer_stack : Int32Stack;
integer_stack.Push( 10 );
integer_stack.Push( 20 );

<% for !integer_stack.IsEmpty() { %>
    <%# integer_stack.Pop() %>
<% } %>

生成的栈类型提供类型安全的方法调用,在 HTML 模板中可以遍历栈内容。

Generis 通过预处理阶段将这些高级特性转换为标准的 Go 代码,为开发者提供了更灵活的代码组织方式,同时保持了编译时的类型安全。这种方案特别适合需要大量重复代码模式或希望在 Go 中使用更丰富元编程功能的场景。

回到顶部