V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
monkeyNik
V2EX  ›  C

用 C 实现轻量级表达式完成策略定制化和模板内容生成

  •  
  •   monkeyNik · 44 天前 · 507 次点击
    这是一个创建于 44 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本文介绍开源 C 语言库Melon表达式组件,该组件实现了一个轻量级表达式,允许开发者定制化属于自己的专属变量和函数解析器。该模块可以用于文本模板内容替换以及一些简单的指令模式编程场景(例如编写策略)。

    下面是该组件支持的语法:

    abc   --这是一个变量
    "abc" --这是一个字符串常量
    'abc' --这也是字符串常量
    1     --整数
    1.2   --浮点数
    0xa   --十六进制整数
    0311  --八进制整数
    
    concat(abc, bcd) --这是一个函数,参数有两个,都是变量
    concat(abc, "bcd") --这是一个函数,参数有两个,一个是变量,一个是常量
    concat(1, "bcd") --两个参数都是常量
    concat("abc", concat(bcd, "efg")) --这个例子展示了函数嵌套调用
    concat("abc", concat(bcd, "efg")) aaa concat("bcd", concat(efg, "hij")) --这个例子展示运行多个表达式
    

    简单来说,表达式语法支持三种类型内容:

    • 常量
    • 变量
    • 函数

    并且函数支持嵌套调用。

    此外,可以一次执行多个表达式,所有表达式的执行结果为最后一个表达式的结果。

    我们分别对着三种类型分别说明:

    常量

    这个比较好理解,就是字面量,主要支持:字符串、整数和浮点数。其中,整数支持十进制写法、八进制写法和十六进制写法。

    变量

    变量顾名思义,就是其值可变。但由于表达式比较简单,且应用场景与常规编程语言不同,因此不是通过=来进行赋值的,而是通过回调函数,由使用者决定该返回何值作为该变量的值。

    函数

    与变量一样,函数的行为完全由回调函数决定,也就是说由使用者自行定制。

    例子

    我们看一个示例

    #include "mln_expr.h"
    #include "mln_log.h"
    #include <stdio.h>
    
    static mln_expr_val_t *func_expr_handler(mln_string_t *name, int is_func, mln_array_t *args, void *data)
    {
        mln_expr_val_t *v, *p;
        int i;
        mln_string_t *s1 = NULL, *s2, *s3;
    
        if (!is_func)
            return mln_expr_val_new(mln_expr_type_string, name, NULL);
    
        for (i = 0, v = p = mln_array_elts(args); i < mln_array_nelts(args); v = p + (++i)) {
            if (s1 == NULL) {
                s1 = mln_string_ref(v->data.s);
                continue;
            }
            s2 = v->data.s;
            s3 = mln_string_strcat(s1, s2);
            mln_string_free(s1);
            s1 = s3;
        }
    
        v = mln_expr_val_new(mln_expr_type_string, s1, NULL);
        mln_string_free(s1);
    
        return v;
    }
    
    int main(void)
    {
        mln_string_t func_exp = mln_string("concat('abc', concat(aaa, 'bbb')) ccc concat('eee', concat(bbb, 'fff'))");
    
        mln_expr_val_t *v;
    
        v = mln_expr_run(&func_exp, func_expr_handler, NULL);
        if (v == NULL) {
            mln_log(error, "run failed\n");
            return -1;
        }
        mln_log(debug, "%d %S\n", v->type, v->data.s);
        mln_expr_val_free(v);
    
        return 0;
    }
    

    这是一个综合一点的例子,这个例子中包含了函数嵌套调用、变量、多表达式执行。

    表达式中的变量和函数都由func_expr_handler这个回调函数来解析。对于变量,回调函数直接返回变量的名字作为变量的值。而对于函数,回调函数则是将函数参数拼接成一个字符串作为函数的返回值。

    由于本例中存在三个表达式:

    • concat('abc', concat(aaa, 'bbb'))
    • ccc
    • concat('eee', concat(bbb, 'fff'))

    前面我们说到过,mln_expr_run的返回值是最后一个表达式的值,所以最终终端的输出就是:

    eeebbbfff
    

    也正如这个例子所示,表达式组件只是提供了一种对文本格式的规范,而具体有哪些函数和变量都完全交给回调函数来决定,也就是交给了使用者决定。并且这个组件并不像完整的编程语言那样功能繁重,因此比较适合一些小型功能整合或者模板替换之类的场景。

    模板替换可以参考 web 前端的那些模板,例如:twig 、mustache 等。

    小功能整合举个例子,例如在对某种网络通信中,我们需要对报文提取某些字段,然后对字段处理,然后再做验证。那么提取、处理、验证就可以被封装成三个函数。这三个函数是三种行为,而不是策略。我们可以将这三种行为应设成表达式组件中的三个函数,然后我们就可以通过对这三个函数的组合应用来实现策略。对于策略的改变,我们并不需要修改 C 代码,只需要将策略的文本内容(也就是这些表达式)做一些修改即可。

    感谢阅读!

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   829 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 22:45 · PVG 06:45 · LAX 15:45 · JFK 18:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.