经过多个[日常提交 for v1.6.0]的commits,完成了采用循环加模拟栈的方式,来代替旧的递归调用zengl_AsmGenCodes函数来生成汇编指令的方式,递归函数调用的方式虽然操作简单,但是弊端就是当递归层次很多时,就可能会发生内存栈溢出的情况。因此,为了解决这个问题,引入了ZENGL_ASM_LOOP_STACKLIST_TYPE的模拟栈结构...

    页面导航: 项目下载地址:

    zengl language v1.6.0 - v1.7.1 源代码的相关地址:https://github.com/zenglong/zengl_language/

    zenglServer v0.1.0 源代码的相关地址:https://github.com/zenglong/zenglServer

zengl v1.6.0:

    经过多个[日常提交 for v1.6.0]的commits,完成了采用循环加模拟栈的方式,来代替旧的递归调用zengl_AsmGenCodes函数来生成汇编指令的方式,递归函数调用的方式虽然操作简单,但是弊端就是当递归层次很多时,就可能会发生内存栈溢出的情况。因此,为了解决这个问题,引入了ZENGL_ASM_LOOP_STACKLIST_TYPE的模拟栈结构,再配合汇编输出主循环,就可以代替旧的递归调用方式,采用模拟栈的方式时,需要用到两个动态数组:一个用于存储节点信息(组成一个节点路径,通过节点路径来确定下一次要执行的汇编输出函数),一个用于存储C函数中可能会用到的一些局部变量等(由于我们没有采用递归方式,因此,C函数中的一些局部变量就需要保存在自己的模拟栈中),此外,还需要充分发挥goto跳转语句的作用,在一个节点的汇编输出函数里执行完一段代码后,会将下一次要执行的节点压入模拟栈,并返回到汇编输出主循环,由主循环进入到下一个节点的汇编输出函数,当该节点的汇编指令生成完毕后,通过模拟栈中的节点路径,就会返回到上一次的节点的汇编输出函数中,此时,就需要goto语句来跳过已经执行过了的部分,并继续执行剩下的代码。

    v1.6.0版本主要修改的是zengl_assemble.c文件,该文件将生成各语句的汇编指令的执行过程都分离到了各个函数中:

...................................................................

/**
该文件为汇编输出的处理文件。
*/

#include "zengl_global.h"

static ZL_VOID zengl_AsmGCLoopStackInit(ZL_VOID * VM_ARG); // 汇编模拟堆栈的初始化

// zengl_AsmGenCode_Fun函数用于生成fun...endfun代码块对应的汇编指令
static ZENGL_STATES zengl_AsmGenCode_Fun(ZL_VOID * VM_ARG, ZENGL_STATES state, ZENGL_ASM_LOOP_STACK_TYPE ** loopStackTopArg);

// zengl_AsmGenCode_Comma函数用于逗号运算符的汇编指令
static ZENGL_STATES zengl_AsmGenCode_Comma(ZL_VOID * VM_ARG, ZENGL_STATES state, ZENGL_ASM_LOOP_STACK_TYPE ** loopStackTopArg);

// 生成与操作因子相关的汇编指令
static ZENGL_STATES zengl_AsmGenCode_Factor(ZL_VOID * VM_ARG, ZENGL_STATES state, ZENGL_ASM_LOOP_STACK_TYPE ** loopStackTopArg);

// 生成赋值运算符的汇编指令
static ZENGL_STATES zengl_AsmGenCode_Assign(ZL_VOID * VM_ARG, ZENGL_STATES state, ZENGL_ASM_LOOP_STACK_TYPE ** loopStackTopArg);

// 生成大多数双目运算符,如:按位运算符,逻辑运算符,比较运算符,加减乘除取余等运算符的汇编指令
static ZENGL_STATES zengl_AsmGenCode_ForTwo(ZL_VOID * VM_ARG, ZENGL_STATES state, ZENGL_ASM_LOOP_STACK_TYPE ** loopStackTopArg);

// 生成负号运算符的汇编指令
static ZENGL_STATES zengl_AsmGenCode_Negative(ZL_VOID * VM_ARG, ZENGL_STATES state, ZENGL_ASM_LOOP_STACK_TYPE ** loopStackTopArg);

// 生成 按位取反运算符 或者 取反运算符 的汇编指令
static ZENGL_STATES zengl_AsmGenCode_Reverse(ZL_VOID * VM_ARG, ZENGL_STATES state, ZENGL_ASM_LOOP_STACK_TYPE ** loopStackTopArg);

// 生成 引用运算符的处理 的汇编指令
static ZENGL_STATES zengl_AsmGenCode_Address(ZL_VOID * VM_ARG, ZENGL_STATES state, ZENGL_ASM_LOOP_STACK_TYPE ** loopStackTopArg);

//zengl_AsmGCElif函数用于生成elif代码块对应的汇编指令
static ZENGL_STATES zengl_AsmGCElif(ZL_VOID * VM_ARG,ZENGL_AST_CHILD_NODE_TYPE * ifchnum,ZL_INT num, ZENGL_ASM_LOOP_STACK_TYPE ** loopStackTopArg);

// 生成 if-elif-else 控制语句的汇编指令
static ZENGL_STATES zengl_AsmGenCode_If_Elif_Else(ZL_VOID * VM_ARG, ZENGL_STATES state, ZENGL_ASM_LOOP_STACK_TYPE ** loopStackTopArg);

// 生成 ++和--运算符 的汇编指令
static ZENGL_STATES zengl_AsmGenCode_PP_MM(ZL_VOID * VM_ARG, ZENGL_STATES state, ZENGL_ASM_LOOP_STACK_TYPE ** loopStackTopArg);

// 生成 打印语句 的汇编指令
static ZENGL_STATES zengl_AsmGenCode_Print(ZL_VOID * VM_ARG, ZENGL_STATES state, ZENGL_ASM_LOOP_STACK_TYPE ** loopStackTopArg);

// 生成 for...endfor 循环控制语句的汇编指令
static ZENGL_STATES zengl_AsmGenCode_For(ZL_VOID * VM_ARG, ZENGL_STATES state, ZENGL_ASM_LOOP_STACK_TYPE ** loopStackTopArg);

...................................................................



    下面是zengl_AsmGenCode_Print的函数代码:

// 生成 打印语句 的汇编指令
static ZENGL_STATES zengl_AsmGenCode_Print(ZL_VOID * VM_ARG, ZENGL_STATES state, ZENGL_ASM_LOOP_STACK_TYPE ** loopStackTopArg)
{
    ZENGL_COMPILE_TYPE * compile = &((ZENGL_VM_TYPE *)VM_ARG)->compile;
    ZENGL_RUN_TYPE * run = &((ZENGL_VM_TYPE *)VM_ARG)->run;
    ZENGL_AST_NODE_TYPE * nodes = compile->AST_nodes.nodes;
    ZENGL_ASM_LOOP_STACK_TYPE * loopStackTop = (*loopStackTopArg);
    ZL_INT nodenum;
    ZL_INT orig_nodenum;
    ZL_INT * chnum;
    ZENGL_RUN_INST_OP_DATA inst_op_data;
    if(loopStackTop == ZL_NULL) {
        loopStackTop = & compile->AsmGCLoopStackList.stacks[compile->AsmGCLoopStackList.count - 1];
    }
    nodenum = loopStackTop->nodenum;
    orig_nodenum = loopStackTop->orig_nodenum;
    (*loopStackTopArg) = ZL_NULL;

    chnum = nodes[orig_nodenum].childs.childnum;
    if(nodenum == -1)
    {
        nodenum = orig_nodenum;
        goto finish_express;
    }

    if(nodes[nodenum].childs.count == 1)
    {
        loopStackTop->state = state;
        switch(nodes[chnum[0]].toktype)
        {
        case ZL_TK_ID: //打印变量等标示符
            inst_op_data = compile->SymLookupID(VM_ARG,chnum[0]);
            run->AddInst(VM_ARG, compile->gencode_struct.pc++, nodenum,
                ZL_R_IT_PRINT , ZL_R_DT_NONE , 0,
                inst_op_data.type , inst_op_data.val.mem); //对应汇编指令 类似 "PRINT (%d)"
            break;
        case ZL_TK_NUM:
            inst_op_data.val.num = ZENGL_SYS_STR_TO_LONG_NUM(compile->TokenStringPoolGetPtr(VM_ARG,nodes[chnum[0]].strindex));
            run->AddInst(VM_ARG,compile->gencode_struct.pc++,nodenum,
                ZL_R_IT_PRINT , ZL_R_DT_NONE , 0,
                ZL_R_DT_NUM,inst_op_data.val.num); //对应汇编指令 类似 "PRINT 123"
            break;
        case ZL_TK_FLOAT:
            inst_op_data.val.floatnum = ZENGL_SYS_STR_TO_FLOAT(compile->TokenStringPoolGetPtr(VM_ARG,nodes[chnum[0]].strindex));
            run->AddInst(VM_ARG,compile->gencode_struct.pc++,nodenum,
                ZL_R_IT_PRINT , ZL_R_DT_NONE , 0,
                ZL_R_DT_FLOAT,inst_op_data.val.floatnum); //对应汇编指令 类似 "PRINT 3.1415926"
            break;
        case ZL_TK_STR:
            inst_op_data.val.num = (ZL_LONG)compile->TokenStringPoolGetPtr(VM_ARG,nodes[chnum[0]].strindex);
            run->AddInst(VM_ARG,compile->gencode_struct.pc++,nodenum,
                ZL_R_IT_PRINT , ZL_R_DT_NONE , 0,
                ZL_R_DT_STR,inst_op_data.val.num); //对应汇编指令 类似 [PRINT "hello world"]
            break;
        default:
            if(ZENGL_AST_ISTOKEXPRESS(chnum[0]))
            {
                //compile->AsmGenCodes(VM_ARG,chnum[0]); // TODO
                loopStackTop->nodenum = -1;
                zengl_AsmGCLoopStackPush(VM_ARG, chnum[0], ZL_ST_START); // 将chnum[0]压入栈,返回后会对chnum[0]对应的节点执行生成汇编指令的操作
                return ZL_ST_START;
finish_express:
                run->AddInst(VM_ARG,compile->gencode_struct.pc++,nodenum,
                    ZL_R_IT_PRINT , ZL_R_DT_NONE , 0,
                    ZL_R_DT_REG,ZL_R_RT_AX); //对应汇编指令 "PRINT AX"
            }
            else
            {
                compile->parser_curnode = chnum[0];
                compile->parser_errorExit(VM_ARG,ZL_ERR_CP_SYNTAX_ASM_INVALID_TOKEN_CAN_NOT_GEN_CODE);
            }
            break;
        } //switch(nodes[chnum[0]].toktype)
        //state = ZL_ST_DOWN; // TODO
        return zengl_AsmGCLoopStackFinishTop(VM_ARG, orig_nodenum);
    }
    else
    {
        compile->parser_curnode = nodenum;
        compile->parser_errorExit(VM_ARG,ZL_ERR_CP_SYNTAX_ASM_PRINT_MUST_HAVE_ONE_CHILD);
    }
}


    上面代码中的loopStackTop是模拟栈的栈顶指针,栈顶存储了当前要处理的节点的相关信息,例如在该函数中,栈顶要处理的节点,就是print语句对应的AST节点。当print语句的子节点是一个表达式时,上面代码就会将chnum[0]即子节点的节点号压入模拟栈,使其成为新的栈顶,然后返回。在外部主循环将该子节点的汇编指令生成好后,会将子节点对应的栈顶弹出,从而让print语句重新成为栈顶,并再次进入当前函数,接着通过goto finish_express;语句跳过已经执行过了的部分,并直接生成PRINT AX指令。从而完成print语句的汇编指令生成过程。

    与模拟栈相关的结构体定义在zengl_global.h头文件中:

typedef struct _ZENGL_ASM_LOOP_STACK_TYPE{
    ZL_INT nodenum; // 压入模拟栈的AST节点号
    ZL_INT orig_nodenum; // 原始节点号
    ZL_INT extData[1]; // 压入栈的额外数据
    ZL_INT stackValIndex; // 较多的额外数据保存到stackVals中,stackValIndex为stackVals中的字节索引值,之所以存储索引值,是因为stackVals在扩容时,其指针有可能会发生改变,因此只能存储索引,再由索引确定具体的位置
    ZL_INT stackValCnt; // 保存在stackVals中的额外数据的尺寸大小(以字节为单位)
    ZENGL_STATES state; // 压入模拟栈的状态码
}ZENGL_ASM_LOOP_STACK_TYPE; // 汇编模拟堆栈(配合循环来替代递归调用)中每个元素的结构定义

typedef struct _ZENGL_ASM_LOOP_STACKLIST_TYPE{
    ZL_BOOL isInit;
    ZL_BOOL isInitStackVals;
    ZL_INT size;
    ZL_INT stackValSize;
    ZL_INT count;
    ZL_INT stackValCount;
    ZENGL_ASM_LOOP_STACK_TYPE * stacks; // 模拟栈动态数组的指针值
    ZL_BYTE * stackVals; // 使用模拟栈代替递归函数调用时,如果某个汇编输出函数中包含较多的局部变量时,就需要将这些局部变量的值保存到stackVals中,这样在下次通过模拟栈进入汇编输出函数时,才能恢复正确的局部变量值。较少的局部变量可以保存在stacks里的extData中
}ZENGL_ASM_LOOP_STACKLIST_TYPE; // 通过设置模拟栈,来解决zengl_AsmGenCodes递归调用过多而可能导致的内存栈溢出问题


    v1.6.0的代码是在win7下通过vs2008来进行开发调试的,并在ubuntu 16.04版本中,通过gcc 5.4.0的版本进行了编译测试。因此,目前只测试了windows环境以及linux环境(vc6没去测试,高版本的visual  studio理论上应该可以直接导入vs2008进行测试,我只在vs2012中简单的测试了下,可以通过设置test.zl或者test4.zl或者game_21_point.zl的命令行参数来测试)。至于其他系统环境,如mac,android之类的系统因为时间关系,就没去测试了。

zengl v1.6.1:

    修复mac osx 64位系统下的segment fault错误,当前版本主要修改的是zenglrun_main.c文件:

/*
    添加汇编指令,使用ZL_DOUBLE传值,double占8个字节,可以代表8字节以内的值,再强制转换类型即可
*/
ZL_VOID zenglrun_AddInst(ZL_VOID * VM_ARG,ZL_INT pc,ZL_INT nodenum,ZENGL_RUN_INST_TYPE type,
                         ZENGL_RUN_INST_OP_DATA_TYPE dest_type , ZL_DOUBLE dest_val ,
                         ZENGL_RUN_INST_OP_DATA_TYPE src_type , ZL_DOUBLE src_val)
{
    ZENGL_RUN_TYPE * run = &((ZENGL_VM_TYPE *)VM_ARG)->run;
    //ZL_INT tmpint; // 不再使用
    if(!run->inst_list.isInit)
        run->initInstList(VM_ARG);
    if(run->inst_list.count == run->inst_list.size)
    {
        run->inst_list.size += ZL_R_INST_LIST_SIZE;
        run->inst_list.insts = run->memReAlloc(VM_ARG,run->inst_list.insts,run->inst_list.size * sizeof(ZENGL_RUN_INST_LIST_MEMBER),
                                                &run->inst_list.mempool_index);
        ZENGL_SYS_MEM_SET((run->inst_list.insts + (run->inst_list.size - ZL_R_INST_LIST_SIZE)),0,
                    ZL_R_INST_LIST_SIZE * sizeof(ZENGL_RUN_INST_LIST_MEMBER));
    }
    if(run->inst_list.insts[run->inst_list.count].isvalid == ZL_FALSE)
    {
        run->inst_list.insts[run->inst_list.count].pc = pc;
        run->inst_list.insts[run->inst_list.count].nodenum = nodenum;
        run->inst_list.insts[run->inst_list.count].type = type;
        run->inst_list.insts[run->inst_list.count].dest.type = dest_type;
        switch(dest_type)
        {
        case ZL_R_DT_REG:
            run->inst_list.insts[run->inst_list.count].dest.val.reg = (ZENGL_RUN_REG_TYPE)dest_val;
            break;
        case ZL_R_DT_ARGMEM:
        case ZL_R_DT_LOCMEM:
        case ZL_R_DT_MEM:
            run->inst_list.insts[run->inst_list.count].dest.val.mem = (ZL_INT)dest_val;
            break;
        case ZL_R_DT_NUM:
            run->inst_list.insts[run->inst_list.count].dest.val.num = (ZL_INT)dest_val;
            break;
        case ZL_R_DT_FLOAT:
            run->inst_list.insts[run->inst_list.count].dest.val.floatnum = dest_val;
            break;
        case ZL_R_DT_STR:
            // tmpint = (ZL_INT)dest_val; // mac osx 64位系统中会发生32位截断,导致得不到正确的指针,因此下面直接将dest_val转为long再转为char *
            run->inst_list.insts[run->inst_list.count].dest.val.str_Index = run->InstDataStringPoolAdd(VM_ARG,(ZL_CHAR * )((ZL_LONG)dest_val)); //64位下需先转为long,再转为指针
            break;
        default:
            run->inst_list.insts[run->inst_list.count].dest.val.num = (ZL_INT)dest_val;
            break;
        }
        run->inst_list.insts[run->inst_list.count].src.type = src_type;
        switch(src_type)
        {
        case ZL_R_DT_REG:
            run->inst_list.insts[run->inst_list.count].src.val.reg = (ZENGL_RUN_REG_TYPE)src_val;
            break;
        case ZL_R_DT_ARGMEM:
        case ZL_R_DT_LOCMEM:
        case ZL_R_DT_MEM:
            run->inst_list.insts[run->inst_list.count].src.val.mem = (ZL_INT)src_val;
            break;
        case ZL_R_DT_NUM:
            run->inst_list.insts[run->inst_list.count].src.val.num = (ZL_INT)src_val;
            break;
        case ZL_R_DT_FLOAT:
            run->inst_list.insts[run->inst_list.count].src.val.floatnum = src_val;
            break;
        case ZL_R_DT_STR:
            // tmpint = (ZL_INT)src_val; // mac osx 64位系统中会发生32位截断,导致得不到正确的指针,因此下面直接将src_val转为long再转为char *
            run->inst_list.insts[run->inst_list.count].src.val.str_Index = run->InstDataStringPoolAdd(VM_ARG,(ZL_CHAR * )((ZL_LONG)src_val)); //64位下需先转为long,再转为指针
            break;
        default:
            run->inst_list.insts[run->inst_list.count].src.val.num = (ZL_INT)src_val;
            break;
        }
        run->inst_list.insts[run->inst_list.count].isvalid = ZL_TRUE;
        run->inst_list.count++;
    }
}


zengl v1.7.0:

    实现了哈希数组,可以在数组中使用字符串来作为key访问数组成员。例如:test_scripts/hash_array.zl中的a['name'],a['job']等。也可以用于多维度的数组成员,例如:test_scripts/game_21_fun_use_hash_array.zl测试脚本中的user['poker', 'totalpoint'],类似于PHP中的user['poker']['totalpoint'],以及user['poker', 'pokerlist', user['poker', 'count']-1]类似于PHP中的user['poker']['pokerlist'][user['poker']['count']-1],在test_scripts中的game_21_use_hash_array.zl和game_21_fun_use_hash_array.zl通过哈希数组来代替class(类),来完成21点小游戏。

    需要注意的是:只要是字符串key,就会在数组中创建一个哈希表,并将该字符串与某个具体的索引值关联起来,例如:a['name']可以和索引值0关联,那么既可以通过a['name']来访问第一个成员,也可以通过a[0]来访问第一个成员。同时a['4']与a[4]也可以是两个完全不同的成员,'4'作为字符串可以对应索引3等,而整数索引4只能对应第5个成员。

    可以在脚本调试时,使用p命令来查看key与索引值的对应关系:

>>> debug input:p user
user :array or class obj:
[0]{name} User
[1]{money} <array or class obj type> begin:
  [0] 0
  [1] 200
  [2] 0
[1]{money} <array or class obj type> end
[2]{poker} <array or class obj type> begin:
  [0]{pokerlist} <array or class obj type> begin:
    [0] 7
  [0]{pokerlist} <array or class obj type> end
  [1]{totalpoint} 7
  [2]{count} 1
[2]{poker} <array or class obj type> end

>>> debug input:


    上面的例子中,name对应索引值0,money对应索引值1,poker对应索引值2等。

    v1.7.0的版本只在windows的vs2008中,ubuntu 16.0.4中(使用gcc 5.4)以及在Mac OS X 10.10 Yosemite中(使用Clang编译器)进行了编译测试。其他的系统如:android,zenglOX等,因为时间关系没去处理。

    该版本和哈希数组相关的C文件为zenglrun_hash_array.c:

/**
 * 该文件中主要存放与内存块的哈希数组相关的函数
 */

#include "zengl_global.h"

/**
 * 根据字符串key计算哈希值
 */
static ZL_INT zenglrun_getHashCode(ZL_CHAR * key)
{
    ZL_INT temp = 0;
    ZL_INT i = 0;
    while (key[i] != ZL_STRNULL)
    {
        temp = ((temp << ZL_R_HASH_CODE_SHIFT) | key[i]);
        ++i;
    }
    return temp;
}

/**
 * 初始化哈希字符串池
 */
static ZL_VOID zenglrun_initHashStrPool(ZL_VOID * VM_ARG, ZENGL_RUN_HASH_STR_POOL * str_pool)
{
    ZENGL_RUN_TYPE * run = &((ZENGL_VM_TYPE *)VM_ARG)->run;
    if(str_pool->ptr != ZL_NULL)
        return;
    str_pool->count = 2;
    str_pool->size = ZL_R_HASH_STR_POOL_SIZE;
    str_pool->ptr = (ZL_CHAR *)run->memAlloc(VM_ARG, str_pool->size * sizeof(ZL_CHAR), &str_pool->mempool_index);
    if(str_pool->ptr == ZL_NULL)
        run->exit(VM_ARG,ZL_ERR_RUN_HASH_ARRAY_MEM_ALLOC_FAILED);
    ZENGL_SYS_MEM_SET(str_pool->ptr, 0, str_pool->size * sizeof(ZL_CHAR));
}

/**
 * 添加字符串到哈希字符串池中
 */
static ZL_INT zenglrun_HashStrPoolAddString(ZL_VOID * VM_ARG, ZENGL_RUN_HASH_STR_POOL * str_pool, ZL_CHAR * str)
{
    ZENGL_RUN_TYPE * run = &((ZENGL_VM_TYPE *)VM_ARG)->run;
    ZL_INT len;
    ZL_INT i,j;
    if(str_pool->ptr == ZL_NULL)
        zenglrun_initHashStrPool(VM_ARG, str_pool);
    if(str == ZL_NULL)
        return -1;
    len = ZENGL_SYS_STRLEN(str);
    if(str_pool->count == str_pool->size ||
        str_pool->count + len + 1 > str_pool->size)
    {
        str_pool->size += ZL_R_HASH_STR_POOL_SIZE;
        str_pool->ptr = (ZL_CHAR *)run->memReAlloc(VM_ARG, str_pool->ptr, str_pool->size * sizeof(ZL_CHAR), &str_pool->mempool_index);
        ZENGL_SYS_MEM_SET(str_pool->ptr + (str_pool->size - ZL_R_HASH_STR_POOL_SIZE), 0, ZL_R_HASH_STR_POOL_SIZE * sizeof(ZL_CHAR));
    }
    for(i=str_pool->count,j=0;i < str_pool->size && j < len;i++,j++)
    {
        str_pool->ptr[i] = str[j];
    }
    str_pool->ptr[i] = ZL_STRNULL;
    i = str_pool->count;
    str_pool->count += len + 1;
    return i;
}

/**
 * 初始化哈希数组的哈希表
 */
static ZL_VOID zenglrun_initHashCodeTable(ZL_VOID * VM_ARG, ZENGL_RUN_HASH_CODE_TABLE * hash_code_table)
{
    ZENGL_RUN_TYPE * run = &((ZENGL_VM_TYPE *)VM_ARG)->run;
    if(hash_code_table->members != ZL_NULL)
        return;
    hash_code_table->count = 0;
    hash_code_table->size = ZL_R_HASH_CODE_TABLE_SIZE;
    hash_code_table->last_index = 0;
    hash_code_table->members = (ZENGL_RUN_HASH_CODE_TABLE_MEMBER *)run->memAlloc(VM_ARG, hash_code_table->size * sizeof(ZENGL_RUN_HASH_CODE_TABLE_MEMBER),
                                        &hash_code_table->mempool_index);
    if(hash_code_table->members == ZL_NULL)
        run->exit(VM_ARG,ZL_ERR_RUN_HASH_ARRAY_MEM_ALLOC_FAILED);
    ZENGL_SYS_MEM_SET(hash_code_table->members, 0, hash_code_table->size * sizeof(ZENGL_RUN_HASH_CODE_TABLE_MEMBER));
}

/**
 * 将字符串key添加到哈希数组的哈希表中
 */
static ZL_VOID zenglrun_addKeyToHashCodeTable(ZL_VOID * VM_ARG, ZENGL_RUN_HASH_CODE_TABLE * hash_code_table, ZENGL_RUN_HASH_STR_POOL * str_pool,
                                                ZL_CHAR * key, ZL_INT str_len,
                                                ZL_INT hash_code, ZL_INT memblock_index)
{
    ZENGL_RUN_TYPE * run = &((ZENGL_VM_TYPE *)VM_ARG)->run;
    if(hash_code_table->members == ZL_NULL)
            zenglrun_initHashCodeTable(VM_ARG, hash_code_table);
    if(hash_code_table->count == hash_code_table->size)
    {
        hash_code_table->size += ZL_R_HASH_CODE_TABLE_SIZE;
        hash_code_table->members = (ZENGL_RUN_HASH_CODE_TABLE_MEMBER *)run->memReAlloc(VM_ARG,
                            hash_code_table->members,
                            hash_code_table->size * sizeof(ZENGL_RUN_HASH_CODE_TABLE_MEMBER),
                            &hash_code_table->mempool_index);
        ZENGL_SYS_MEM_SET((hash_code_table->members + (hash_code_table->size - ZL_R_HASH_CODE_TABLE_SIZE)), 0,
                ZL_R_HASH_CODE_TABLE_SIZE * sizeof(ZENGL_RUN_HASH_CODE_TABLE_MEMBER));
    }
    hash_code_table->members[hash_code_table->count].hash_code = hash_code;
    hash_code_table->members[hash_code_table->count].str_len = str_len;
    hash_code_table->members[hash_code_table->count].str_offset = zenglrun_HashStrPoolAddString(VM_ARG, str_pool, key);
    hash_code_table->members[hash_code_table->count].memblock_index = memblock_index;
    hash_code_table->members[hash_code_table->count].hits = 0;
    hash_code_table->count++;
}

/**
 * 从哈希数组的哈希表中,根据字符串key获取对应的索引值
 */
ZL_INT zenglrun_getIndexFromHashCodeTable(ZL_VOID * VM_ARG, ZENGL_RUN_VIRTUAL_MEM_LIST * memblock, ZL_CHAR * key)
{
    ZENGL_RUN_TYPE * run = &((ZENGL_VM_TYPE *)VM_ARG)->run;
    ZENGL_RUN_HASH_CODE_TABLE * hash_code_table = &(memblock->hash_array.hash_code_table);
    ZENGL_RUN_HASH_STR_POOL * str_pool = &(memblock->hash_array.str_pool);
    ZENGL_RUN_HASH_CODE_TABLE_MEMBER tmp_member; // 根据hits交换成员时,需要用到的临时变量
    ZL_INT hash_code,i,str_len,min_hits,min_hits_index,ret_memblock_index;
    ZL_CHAR * str_pool_ptr;
    if(str_pool->ptr == ZL_NULL)
        zenglrun_initHashStrPool(VM_ARG, str_pool);
    hash_code = zenglrun_getHashCode(key);
    str_len = ZENGL_SYS_STRLEN(key);
    min_hits_index = 0;
    // 对min_hits进行初始化
    if(hash_code_table->count > 0)
        min_hits = hash_code_table->members[0].hits;
    else
        min_hits = 0;
    for(i=0;i < hash_code_table->count;i++)
    {
        // 检测最小的hits及对应的索引
        if(min_hits > hash_code_table->members[i].hits) {
            min_hits = hash_code_table->members[i].hits;
            min_hits_index = i;
        }
        if(hash_code_table->members[i].hash_code != hash_code)
            continue;
        if(hash_code_table->members[i].str_len != str_len)
            continue;
        str_pool_ptr = str_pool->ptr + hash_code_table->members[i].str_offset;
        if(ZENGL_SYS_STRCMP(key, str_pool_ptr) != 0)
            continue;
        hash_code_table->members[i].hits += 1;
        ret_memblock_index = hash_code_table->members[i].memblock_index; // 将要返回的内存块索引赋值给ret_memblock_index,防止受到下面数据交换的影响
        // 如果当前选中的哈希表成员的命中次数比前面的最少命中成员的命中次数要大的话,就交换这两个成员,以提高当前成员下次响应的速度
        if(hash_code_table->members[i].hits > min_hits && i > min_hits_index) {
            tmp_member = hash_code_table->members[min_hits_index];
            hash_code_table->members[min_hits_index] = hash_code_table->members[i];
            hash_code_table->members[i] = tmp_member;
        }
        return ret_memblock_index;
    }
    zenglrun_addKeyToHashCodeTable(VM_ARG, hash_code_table, str_pool, key, str_len, hash_code, hash_code_table->last_index);
    return hash_code_table->last_index++;
}

/**
 * 从哈希数组的哈希表中,根据索引值来获取对应的字符串key
 */
ZL_CHAR * zenglrun_getKeyFromHashCodeTable(ZL_VOID * VM_ARG, ZENGL_RUN_VIRTUAL_MEM_LIST * memblock, ZL_INT memblock_index)
{
    ZENGL_RUN_TYPE * run = &((ZENGL_VM_TYPE *)VM_ARG)->run;
    ZENGL_RUN_HASH_CODE_TABLE * hash_code_table = &(memblock->hash_array.hash_code_table);
    ZENGL_RUN_HASH_STR_POOL * str_pool = &(memblock->hash_array.str_pool);
    ZL_INT i;
    for(i=0;i < hash_code_table->count;i++)
    {
        if(hash_code_table->members[i].memblock_index == memblock_index)
        {
            return str_pool->ptr + hash_code_table->members[i].str_offset;
        }
    }
    return ZL_NULL;
}


    与哈希数组相关的结构体和宏主要定义在zenglrun_hash_array.h头文件中:

/*
   Copyright 2017 zenglong (made in china)

   For more information, please see www.zengl.com

   This file is part of zengl language.

   zengl language is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   zengl language is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with zengl language,the copy is in the file licence.txt.  If not,
   see <http://www.gnu.org/licenses/>.
*/

#ifndef _ZENGL_HASH_ARRAY_H_
#define _ZENGL_HASH_ARRAY_H_

#include "zengl_global_header.h"

#define ZL_R_HASH_CODE_TABLE_SIZE 10 // 哈希数组的初始化及动态扩容的大小
#define ZL_R_HASH_STR_POOL_SIZE 120  // 哈希字符串池的初始化及动态扩容的大小
#define ZL_R_HASH_CODE_SHIFT 4  // 哈希值的计算因子

typedef struct _ZENGL_RUN_HASH_CODE_TABLE_MEMBER{
    ZL_INT hash_code; // 字符串的hash code
    ZL_INT str_len;   // 字符串的长度
    ZL_INT str_offset; // 字符串在字符串池中的字节偏移值
    ZL_INT memblock_index; // 内存块中,字符串对应的真实的索引值
    ZL_INT hits; //命中次数,命中次数越多,在哈希表中越靠前
}ZENGL_RUN_HASH_CODE_TABLE_MEMBER; // 哈希数组中单个成员的定义

typedef struct _ZENGL_RUN_HASH_CODE_TABLE{
    ZL_INT size; // 哈希表对应的动态数组的尺寸
    ZL_INT count; // 哈希表中存储的元素个数
    ZL_INT last_index; // 记录最后一个哈希表成员所对应的索引值
    ZL_INT mempool_index; //下面的members指针在内存池中的索引
    ZENGL_RUN_HASH_CODE_TABLE_MEMBER * members; // 存放具体的哈希数组成员
}ZENGL_RUN_HASH_CODE_TABLE; // 哈希数组的结构定义

typedef struct _ZENGL_RUN_HASH_STR_POOL{
    ZL_INT size; // 字符串池的尺寸
    ZL_INT count; // 字符串池中实际包含的字符数
    ZL_INT mempool_index; //下面的ptr指针在内存池中的索引
    ZL_CHAR * ptr; // 存放所有字符串
}ZENGL_RUN_HASH_STR_POOL; // 哈希数组中的字符串池

typedef struct _ZENGL_RUN_HASH_ARRAY{
    ZENGL_RUN_HASH_CODE_TABLE hash_code_table; // 哈希数组中的哈希表
    ZENGL_RUN_HASH_STR_POOL str_pool; // 哈希数组中的字符串都存储在str_pool中
}ZENGL_RUN_HASH_ARRAY;

#endif/* _ZENGL_HASH_ARRAY_H_ */


    如果读者希望在android中测试新的版本的话,需要自行在相关的makefile文件(Android.mk)中加入zenglrun_hash_array.c和zenglrun_hash_array.h等。

zengl v1.7.1:

    v1.7.1的版本,添加了RETURN汇编指令,修复了从脚本函数返回数组时可能会报的内存错误,增加了两个与哈希数组相关的API接口函数。

    从该版本开始,RET指令会将AX寄存器的值(即函数的返回值)重置为0,因此,如果没有使用return明确返回某个值时,脚本函数的返回值默认是0,对于return语句,当其后没有跟随任何表达式时,如:return;语句,那么依然是生成RET汇编指令,即返回值默认是0,如果return后面跟随了表达式的话,例如:return b;语句,那么生成的会是RETURN汇编指令,对于RETURN指令,它不会将AX寄存器的值重置为0,因此,RETURN指令可以将return语句后面的表达式的值作为结果返回。

    之前的版本,由于RET指令没有重置AX寄存器的值,因此,脚本函数的最后一条语句的结果会被隐式的作为整个函数的结果返回,隐式返回的结果可能不是想要的结果,还会导致某些被释放掉的数组等内存块被隐式的返回,从而导致未知的内存错误。

    此外,之前的版本,如果在某个脚本函数里设置了某个局部变量为数组等内存块时,如果将该局部变量进行return返回的话,会直接报内存错误,这是因为所有的局部变量在脚本函数返回时,如果这些局部变量是数组之类的内存块的话,系统内部会将这些内存块的引用计数值减一,如果减到0就释放掉这些数组,从而导致返回的数组其实已经被释放掉了。

    从当前版本开始,如果某个局部变量是数组等内存块时,可以直接用return语句返回,系统内部在释放局部变量对应的内存块时,会检查这些局部变量对应的内存块是否是AX寄存器返回的内存块,如果是AX寄存器返回的内存块的话,就只将内存块的引用计数值减一,但是并不会执行具体的释放内存块的操作,这样,外部调用者就可以使用脚本函数返回的数组,而不会报内存错误了。可以在test_scripts/v1.7.1/test.zl测试脚本中看到从脚本函数返回数组的例子:

use builtin;

fun test()
    a = array();
    a['name'] = 'zengl';
    return a;
endfun

b = test();
//print b;
print b['name'];


    当前版本还增加了两个与哈希数组相关的API接口函数:zenglApi_AddMemBlockRefCount 和 zenglApi_SetMemBlockByHashKey,可以在zenglApi.c文件中看到这两个接口函数的定义:

/*API接口,增加数组等内存块的引用计数值(如果add_refcount是负数,就是减少引用计数值),返回0表示成功,返回-1表示失败*/
ZL_EXPORT ZL_EXP_INT zenglApi_AddMemBlockRefCount(ZL_EXP_VOID * VM_ARG,ZENGL_EXPORT_MEMBLOCK * memblock,ZL_EXP_INT add_refcount)
{
    ZENGL_VM_TYPE * VM = (ZENGL_VM_TYPE *)VM_ARG;
    ZENGL_RUN_VIRTUAL_MEM_LIST * mem_list;
    ZL_CHAR * ApiName = "zenglApi_AddMemBlockRefCount";
    if(VM->signer != ZL_VM_SIGNER) //通过虚拟机签名判断是否是有效的虚拟机
        return -1;
    switch(VM->ApiState)
    {
    case ZL_API_ST_MOD_FUN_HANDLE:
        break;
    default:
        VM->run.SetApiErrorEx(VM_ARG,ZL_ERR_VM_API_INVALID_CALL_POSITION, ApiName , ApiName);
        return -1;
        break;
    }
    if(memblock == ZL_NULL)
        return -1;
    mem_list = (ZENGL_RUN_VIRTUAL_MEM_LIST *)memblock->ptr;
    mem_list->refcount += add_refcount;
    return 0;
}

............................................................

/*通过字符串key来设置数组等内存块的成员,该接口会先将字符串转为对应的索引值,再调用SetMemBlock接口去执行具体的设置工作*/
ZL_EXPORT ZL_EXP_INT zenglApi_SetMemBlockByHashKey(ZL_EXP_VOID * VM_ARG,ZENGL_EXPORT_MEMBLOCK * memblock,ZL_EXP_CHAR * key,ZENGL_EXPORT_MOD_FUN_ARG * retval)
{
    ZENGL_VM_TYPE * VM = (ZENGL_VM_TYPE *)VM_ARG;
    ZL_INT index;
    ZL_CHAR * ApiName = "zenglApi_SetMemBlockByHashKey";
    if(VM->signer != ZL_VM_SIGNER) //通过虚拟机签名判断是否是有效的虚拟机
        return -1;
    switch(VM->ApiState)
    {
    case ZL_API_ST_MOD_FUN_HANDLE:
        break;
    default:
        VM->run.SetApiErrorEx(VM_ARG,ZL_ERR_VM_API_INVALID_CALL_POSITION, ApiName , ApiName);
        return -1;
        break;
    }
    if(retval == ZL_NULL)
        return 0;
    index = zenglrun_getIndexFromHashCodeTable(VM_ARG, (ZENGL_RUN_VIRTUAL_MEM_LIST *)memblock->ptr, key);
    return zenglApi_SetMemBlock(VM_ARG, memblock, (index + 1), retval);
}


    其中,zenglApi_AddMemBlockRefCount接口函数可以在自定义的模块函数中,用于手动增加或减少某个内存块的引用计数值,通过手动增加数组等内存块的引用计数值,可以让这些数组在zengl脚本的整个生命周期中都一直存在,而不会被自动释放掉。

    而zenglApi_SetMemBlockByHashKey接口函数,则可以在自定义的模块函数中,使用字符串作为key来设置哈希数组的成员。

    在main.c中的main_builtin_get_extraArray函数(对应的脚本模块函数名为:bltGetExtraArray),可以看到这两个接口函数的具体用法:

/*bltGetExtraArray模块函数,获取测试用的哈希数组(如果不存在则创建该数组),如果已经存在则直接返回该数组*/
ZL_EXP_VOID main_builtin_get_extraArray(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
    ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
    MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");

    // 如果没有创建过哈希数组,则创建哈希数组,并用字符串作为key设置数组成员,由于下面是根据ptr是否等于NULL来判断是否创建过的,
    // 因此,需要在脚本执行前,事先将ptr的值显示的设置为NULL,否则,如果my_data的来源是局部变量的话,ptr的默认初始值就不会是NULL
    if(my_data->extra_memblock.ptr == ZL_EXP_NULL) {
        if(zenglApi_CreateMemBlock(VM_ARG,&my_data->extra_memblock,0) == -1) {
            zenglApi_Exit(VM_ARG,zenglApi_GetErrorString(VM_ARG));
        }
        // 手动增加该内存块的引用计数值,使其能够在脚本执行的整个生命周期中都一直存在,而不会被释放掉
        // 当数组之类的内存块在脚本函数中被赋值给局部变量后,在脚本函数返回时,会释放掉所有局部变量对应的内存块(除非某个局部变量被return返回),
        // 通过手动增加内存块的引用计数值,这样,脚本函数返回时,内存块的引用计数值就不会被减为0,也就不会被释放掉
        zenglApi_AddMemBlockRefCount(VM_ARG,&my_data->extra_memblock,1);

        arg.type = ZL_EXP_FAT_STR;
        arg.val.str = "zengl";
        // 使用zenglApi_SetMemBlockByHashKey接口将字符串作为key设置哈希数组的成员
        zenglApi_SetMemBlockByHashKey(VM_ARG, &my_data->extra_memblock, "name", &arg);

        arg.type = ZL_EXP_FAT_STR;
        arg.val.str = "programmer";
        zenglApi_SetMemBlockByHashKey(VM_ARG, &my_data->extra_memblock, "job", &arg);
        
        // 将创建的哈希数组作为结果返回
        zenglApi_SetRetValAsMemBlock(VM_ARG,&my_data->extra_memblock);
    }
    else {
        // 如果之前已经创建过哈希数组,就直接将该数组返回
        zenglApi_SetRetValAsMemBlock(VM_ARG,&my_data->extra_memblock);
    }
}


    该模块函数内部会先创建一个内存块,然后使用zenglApi_AddMemBlockRefCount来增加该内存块的引用计数,接着使用zenglApi_SetMemBlockByHashKey设置了两个数组成员,一个key为"name",对应的值为"zengl",另一个key为"job",对应的值为"programmer"。在test_scripts/v1.7.1/test2.zl测试脚本中,就通过bltGetExtraArray模块函数得到了这个哈希数组,并将数组里的"name"成员和"job"成员显示了出来,另外,在test2.zl中的SetExtraArray脚本函数里,当使用b = bltGetExtraArray();语句将该模块函数返回的数组赋值给了局部变量后,脚本函数返回时,并不会将该局部变量对应的数组给释放掉,这是因为,bltGetExtraArray模块函数在创建数组时,手动增加了该数组对应的内存块的引用计数值,使该引用计数值不会被减为0,而被释放掉。下面是test2.zl测试脚本的代码:

use builtin;

e = bltGetExtraArray();
print e['name'] + ': ' + e['job'];

SetExtraArray();

print e['name'] + ': ' + e['job'];

fun SetExtraArray()
    b = bltGetExtraArray();
    b['job'] = 'play game';
endfun


    此外,bltGetExtraArray模块函数只会在第一次调用时创建数组,之后再调用该模块函数时,它会将之前创建过的数组直接返回。

    v1.7.1的版本只在windows,linux,mac osx系统中进行过基本测试,android和zenglOX暂时没去处理。

zenglServer v0.1.0:

    zenglServer是一个http server,它除了用于响应静态文件外,最主要的目的在于接受外部http请求,并执行zengl动态脚本,并将脚本执行的结果反馈给浏览器之类的客户端。目前只是实验项目,仅供学习研究为目的。

    zenglServer只能在linux系统中进行编译和测试。要编译的话,直接在根目录中输入make命令即可:

zengl@zengl-ubuntu:~/zenglServer$ make
cd zengl/linux && make libzengl.a
make[1]: Entering directory '/home/zengl/zenglServer/zengl/linux'
gcc -D ZL_LANG_EN_WITH_CH -g3 -ggdb -O0 -std=c99 -fvisibility=hidden -fPIC -c zengl_main.c zengl_parser.c zengl_symbol.c zengl_locals.c zengl_assemble.c zengl_ld.c zenglrun_main.c zenglrun_func.c zenglrun_hash_array.c zenglApi.c zenglApi_BltModFuns.c zenglDebug.c
ar rc libzengl.a zengl_main.o zengl_parser.o zengl_symbol.o zengl_locals.o zengl_assemble.o zengl_ld.o zenglrun_main.o zenglrun_func.o zenglrun_hash_array.o zenglApi.o zenglApi_BltModFuns.o zenglDebug.o
make[1]: Leaving directory '/home/zengl/zenglServer/zengl/linux'
gcc -g3 -ggdb -O0 -std=c99 main.c http_parser.c module_request.c dynamic_string.c main.h http_parser.h module_request.h dynamic_string.h zengl/linux/zengl_exportfuns.h -o zenglServer zengl/linux/libzengl.a -lpthread
zengl@zengl-ubuntu:~/zenglServer$


    第一次编译时,它会先进入zengl/linux目录,编译生成libzengl.a的静态库文件,该静态库主要用于执行zengl脚本。

    如果要删除编译生成的文件的话,直接输入make clean即可。

    在根目录中,有一个config.zl的默认配置文件(使用zengl脚本语法编写),该配置文件里定义了zenglServer需要绑定的端口号,需要启动的进程数,线程数等:

debug_mode = 1;
// zl_debug_log = "zl_debug.log"; // zengl脚本的调试日志,可以输出相关的虚拟汇编指令

port = 8083; // 绑定的端口

if(!debug_mode)
    process_num = 3; // 进程数
    thread_num_per_process = 3; // 线程数
else
    print '*** config is in debug mode ***';
    process_num = 1; // 进程数
    thread_num_per_process = 1; // 线程数
endif

webroot = "my_webroot"; // web根目录


    在编译成功后,直接运行生成好的zenglServer可执行文件即可:

zengl@zengl-ubuntu:~/zenglServer$ ./zenglServer
use default config: config.zl
*** config is in debug mode ***
run config.zl complete, config:
port: 8083 process_num: 1 thread_num_per_process: 1
webroot: my_webroot
bind done
accept sem initialized.


    默认绑定的端口号为:8083,打开你的浏览器,输入 http://<your ip address>:8083,<your ip address>表示zenglServer所在的linux系统的ip地址,假设为:192.168.1.104,那么输入 http://192.168.1.104:8083,应该可以看到Hello World!静态页面,同时终端里面会有如下输出:

zengl@zengl-ubuntu:~/zenglServer$ ./zenglServer

.................................................

Connection accepted, accept pid: 5131 tid: 5132
-----------------------------------
Thu Jul  6 16:54:25 2017
recv [client_socket_fd:5]: 50[50] 50[100] 50[150] 50[200] 50[250] 50[300] 43[343]

url: /
url_path: /
close client_socket_fd: 5
===============================


    可以看到请求的url资源路径,处理该请求的pid(进程ID)和tid(线程ID)

    在浏览器中输入测试用的表单地址:http://192.168.1.104:8083/form.html,在表单中随便填些东西,点击Submit提交按钮,交由test.zl测试脚本去处理,处理后会返回类似如下的测试结果(下面是作者的Firefox浏览器显示的结果):

a is 20 end
user agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0
other_headers, user agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0
request body: title=hello&description=world&content=zengl+program&sb=Submit


    在test.zl测试脚本中,获取了当前浏览器的UA信息,以及请求的body(主体数据)。test.zl的脚本代码如下(位于my_webroot目录中):

use request;

a = 5 + 15;
print '<!Doctype html>';
print '<html><body>';
print 'a is ' + a + " end<br/>";

headers = rqtGetHeaders();
print 'user agent: ' + headers['User-Agent'] + '<br/>';

other_headers = rqtGetHeaders();
print 'other_headers, user agent: ' + other_headers['User-Agent'] + '<br/>';

query_string = rqtGetQueryAsString();
if(query_string)
    print 'query string: ' + query_string + '<br/>';
    querys = rqtGetQuery();
    if(querys['name'])
        print 'querys[\'name\']: ' + querys['name'] + '<br/>';
    endif
    if(querys['job'])
        print 'querys[\'job\']: ' + querys['job'] + '<br/>';
    endif
endif

body = rqtGetBody();
if(body)
    print 'request body: ' + body;
endif

print '</body></html>';


    还可以直接在浏览器地址中输入test.zl并附加一些查询字符串,脚本会将查询字符串自动解析为哈希数组。

    在浏览器中输入:http://192.168.1.104:8083/test.zl?name=zengl&job=programmer,反馈结果如下:

a is 20 end
user agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0
other_headers, user agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0
query string: name=zengl&job=programmer
querys['name']: zengl
querys['job']: programmer


    每个请求的HTTP头信息都会写入到recv.log的日志文件中,打开日志文件,应该可以看到类似如下的信息:

........................................................

Thu Jul  6 17:02:20 2017

GET /test.zl?name=zengl&job=programmer HTTP/1.1
Host: 192.168.1.104:8083
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

........................................................


    要退出zenglServer,直接在终端按Ctrl+C即可。

    zenglServer有几个可选的命令行参数,可以使用-h查看帮助信息:

zengl@zengl-ubuntu:~/zenglServer$ ./zenglServer -h
usage: ./zenglServer [options]
-v           show version
-c <config file>    set config file
-h            show this help
zengl@zengl-ubuntu:~/zenglServer$


    通过-v可以查看zenglServer的版本号以及所使用的zengl脚本语言的版本号,-c可以指定需要加载的配置文件(配置文件必须使用zengl脚本语法编写):

zengl@zengl-ubuntu:~/zenglServer$ ./zenglServer -v
zenglServer version: v0.1.0
zengl language version: v1.7.1
zengl@zengl-ubuntu:~/zenglServer$ ./zenglServer -c config.zl
use config: config.zl
*** config is in debug mode ***
run config.zl complete, config:
port: 8083 process_num: 1 thread_num_per_process: 1
webroot: my_webroot
bind done
accept sem initialized.


    zenglServer是在Ubuntu 16.04 LTS x86-64系统中进行的开发测试(GCC版本号为:5.4.0),并且在CentOS 7 x86-64 (gcc: 4.8.5)中进行了简单的测试。

    zenglServer的C源代码中,加入了必要的注释信息,读者可以通过阅读源码的相关注释来理解代码,例如,和request模块相关的文件module_request.c的代码如下:

/*
 * module_request.c
 *
 *  Created on: 2017-6-15
 *      Author: zengl
 */

#include "main.h"
#include "module_request.h"
#include <string.h>

/**
 * rqtGetHeaders模块函数,将请求头中的field和value字符串组成名值对,存储到哈希数组中,
 * 并将该数组作为结果返回,例如:
 * headers = rqtGetHeaders();
 * print 'user agent: ' + headers['User-Agent'] + '<br/>';
 * 该例子通过模块函数,获取到头部信息,并通过headers['User-Agent']来获取到浏览器的UA信息
 * 该模块函数只会在脚本第一次调用时,创建哈希数组,之后再调用该函数时,就会直接将之前创建的数组返回
 */
ZL_EXP_VOID module_request_GetHeaders(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
    ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
    MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
    MY_PARSER_DATA * my_parser_data = my_data->my_parser_data;

    // 如果没有创建过哈希数组,则创建哈希数组,并将请求头中所有的field与value构成的名值对,存储到哈希数组中
    if(my_data->headers_memblock.ptr == ZL_EXP_NULL) {
        if(zenglApi_CreateMemBlock(VM_ARG,&my_data->headers_memblock,0) == -1) {
            zenglApi_Exit(VM_ARG,zenglApi_GetErrorString(VM_ARG));
        }
        zenglApi_AddMemBlockRefCount(VM_ARG,&my_data->headers_memblock,1); // 手动增加该内存块的引用计数值,使其不会在脚本函数返回时,被释放掉。
        // 所有的field和value字符串,都依次存储在request_header动态字符串中,通过'\0'字符串终止符分隔开
        if(my_parser_data->request_header.str != PTR_NULL && my_parser_data->request_header.count > 0) {
            ZL_EXP_CHAR * tmp = my_parser_data->request_header.str;
            ZL_EXP_CHAR * end = my_parser_data->request_header.str + my_parser_data->request_header.count;
            do{
                ZL_EXP_CHAR * field = tmp;
                ZL_EXP_CHAR * value = field + strlen(field) + 1;
                if(field >= end || value >= end) {
                    break;
                }
                arg.type = ZL_EXP_FAT_STR;
                arg.val.str = value;
                zenglApi_SetMemBlockByHashKey(VM_ARG, &my_data->headers_memblock, field, &arg);
                tmp = value + strlen(value) + 1;
            }while(1);
        }
        zenglApi_SetRetValAsMemBlock(VM_ARG,&my_data->headers_memblock);
    }
    else {
        // 如果之前已经创建过哈希数组,就直接将该数组返回
        zenglApi_SetRetValAsMemBlock(VM_ARG,&my_data->headers_memblock);
    }
}

/**
 * rqtGetBody模块函数,用于返回请求的body(主体数据),如果没有请求主体数据,则返回空字符串,
 * 例如:
 * body = rqtGetBody();
 * if(body)
 *         print 'request body: ' + body;
 * endif
 * 对于application/x-www-form-urlencoded类型(表单提交时Content-Type的默认类型)的post请求,该例子可能显示的结果为:
 * request body: title=hello&description=world&content=test&sb=Submit
 */
ZL_EXP_VOID module_request_GetBody(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
    MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
    MY_PARSER_DATA * my_parser_data = my_data->my_parser_data;

    if(my_parser_data->request_body.str != PTR_NULL && my_parser_data->request_body.count > 0) {
        zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_STR, my_parser_data->request_body.str, 0, 0);
    }
    else {
        zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_STR, "", 0, 0);
    }
}

/**
 * rqtGetQueryAsString模块函数,用于获取url资源路径中,查询字符串的原始字符串值,
 * 例如:
 * query_string = rqtGetQueryAsString();
 * print 'query string: ' + query_string + '<br/>';
 * 对于 GET /test.zl?name=zengl&job=programmer HTTP/1.1 的http请求,
 * 上面例子显示的结果就是:query string: name=zengl&job=programmer
 * 如果没有查询字符串,那么该模块函数就会返回空字符串
 */
ZL_EXP_VOID module_request_GetQueryAsString(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
    MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
    MY_PARSER_DATA * my_parser_data = my_data->my_parser_data;
    struct http_parser_url * url_parser = &my_parser_data->url_parser;

    if((url_parser->field_set & (1 << UF_QUERY)) && (url_parser->field_data[UF_QUERY].len > 0)) {
        ZL_EXP_INT end_pos = url_parser->field_data[UF_QUERY].off + url_parser->field_data[UF_QUERY].len;
        ZL_EXP_CHAR orig_char = my_parser_data->request_url.str[end_pos];
        my_parser_data->request_url.str[end_pos] = STR_NULL;
        zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_STR, my_parser_data->request_url.str + url_parser->field_data[UF_QUERY].off, 0, 0);
        if(orig_char != STR_NULL) {
            my_parser_data->request_url.str[end_pos] = orig_char;
        }
    }
    else {
        zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_STR, "", 0, 0);
    }
}

/**
 * rqtGetQuery模块函数,用于返回查询字符串的哈希数组形式
 * 例如:
 * querys = rqtGetQuery();
 * print 'querys[\'name\']: ' + querys['name'] + '<br/>';
 * print 'querys[\'job\']: ' + querys['job'] + '<br/>';
 * 对于 GET /test.zl?name=zengl&job=programmer HTTP/1.1 的http请求,
 * 上面例子显示的结果就是:
 * querys['name']: zengl
 * querys['job']: programmer
 * 该模块函数只会在第一次调用时,创建哈希数组,之后再调用该模块函数时,就会直接将之前创建过的数组返回
 */
ZL_EXP_VOID module_request_GetQuery(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
    ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
    MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
    MY_PARSER_DATA * my_parser_data = my_data->my_parser_data;
    struct http_parser_url * url_parser = &my_parser_data->url_parser;

    if(my_data->query_memblock.ptr == ZL_EXP_NULL) {
        if(zenglApi_CreateMemBlock(VM_ARG,&my_data->query_memblock,0) == -1) {
            zenglApi_Exit(VM_ARG,zenglApi_GetErrorString(VM_ARG));
        }
        zenglApi_AddMemBlockRefCount(VM_ARG,&my_data->query_memblock,1); // 手动增加该内存块的引用计数值,使其不会在脚本函数返回时,被释放掉。
        if((url_parser->field_set & (1 << UF_QUERY)) && (url_parser->field_data[UF_QUERY].len > 0)) {
            ZL_EXP_CHAR * q = my_parser_data->request_url.str + url_parser->field_data[UF_QUERY].off;
            ZL_EXP_INT q_len = url_parser->field_data[UF_QUERY].len;
            ZL_EXP_INT k = -1;
            ZL_EXP_INT v = -1;
            for(ZL_EXP_INT i = 0; i <= q_len; i++) {
                if(k == -1 && q[i] != '=' && q[i] != '&') {
                    k = i;
                }
                switch(q[i]) {
                case '=':
                    v = i + 1;
                    break;
                case '&':
                case '#':
                case STR_NULL:
                    if(k >= 0 && v > 0) {
                        ZL_EXP_CHAR prev_v_char = q[v - 1];
                        ZL_EXP_CHAR current_char = q[i];
                        q[i] = q[v - 1] = STR_NULL;
                        arg.type = ZL_EXP_FAT_STR;
                        arg.val.str = &q[v];
                        zenglApi_SetMemBlockByHashKey(VM_ARG, &my_data->query_memblock, &q[k], &arg);
                        q[v - 1] = prev_v_char;
                        if(current_char != STR_NULL)
                            q[i] = current_char;
                        k = v = -1;
                    }
                    else {
                        k = v = -1;
                    }
                    break;
                }
            }
        }
        zenglApi_SetRetValAsMemBlock(VM_ARG,&my_data->query_memblock);
    }
    else {
        zenglApi_SetRetValAsMemBlock(VM_ARG,&my_data->query_memblock);
    }
}

/**
 * request模块的初始化函数,里面设置了与该模块相关的各个模块函数及其相关的处理句柄
 */
ZL_EXP_VOID module_request_init(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT moduleID)
{
    zenglApi_SetModFunHandle(VM_ARG,moduleID,"rqtGetHeaders",module_request_GetHeaders);
    zenglApi_SetModFunHandle(VM_ARG,moduleID,"rqtGetBody",module_request_GetBody);
    zenglApi_SetModFunHandle(VM_ARG,moduleID,"rqtGetQueryAsString",module_request_GetQueryAsString);
    zenglApi_SetModFunHandle(VM_ARG,moduleID,"rqtGetQuery",module_request_GetQuery);
}


结束语:

    有些事如果你现在不去做 想做的时候也许已经来不及了

——  《刑事侦缉档案 第一部》
 
上下篇

下一篇: zenglServer v0.1.1 url解码,新增builtin模块实现数组成员的迭代操作

上一篇: zengl v1.5.0 移植到zenglOX系统

相关文章

zengl编程语言 v1.0.2 修复BUG

zengl编程语言v1.0.5 编译,执行,源码调试一体化,开发俄罗斯方块

zengl编程语言v0.0.15变量引用

v1.3.1 Android编译执行zengl脚本

zengl编程语言v0.0.7

zengl编程语言v0.0.4构建多语句抽象语法树