v1.9.1版本可以在fun定义的函数参数中使用负数作为参数的默认值,之前的版本如果想使用负数作为参数的默认值的话,只能使用常量宏的形式。
页面导航:
zengl language v1.9.1的源代码的相关地址:https://github.com/zenglong/zengl_language 当前版本对应的tag标签为:v1.9.1
v1.9.1版本可以在fun定义的函数参数中使用负数作为参数的默认值,之前的版本如果想使用负数作为参数的默认值的话,只能使用常量宏的形式,通过def关键字定义一个负数的常量宏,然后在参数列表中使用该常量宏作为默认值来实现。当前版本就可以直接使用负数作为函数参数的默认值。
当前版本在test_scripts/v1.9.1的目录中增加了test.zl的测试脚本:
fun test(a = -3, b = 2) print 'a: ' + a; print 'b: ' + b; print 'a+b: ' + (a+b); endfun test();
上面的test.zl脚本在命令行中的执行结果类似如下:
[root@192 linux]# ./zengl test_scripts/v1.9.1/test.zl run(编译执行中)... stat cache file: "caches/1_9_1_8_dv01_009ae4a0ba863db1c19c5f4950b7e2d5" failed, maybe no cache file [recompile] a: -3 b: 2 a+b: -1 write zengl cache to file "caches/1_9_1_8_dv01_009ae4a0ba863db1c19c5f4950b7e2d5" success run finished(编译执行结束) [root@192 linux]#
为了让脚本函数的参数能使用负数作为默认值,当前版本主要对zengl_symbol.c文件中的zengl_SymScanFunArg的C函数进行了调整:
/* 使用AST扫描堆栈来扫描语法树中函数的参数 */ ZL_VOID zengl_SymScanFunArg(ZL_VOID * VM_ARG,ZL_INT nodenum) { ZENGL_COMPILE_TYPE * compile = &((ZENGL_VM_TYPE *)VM_ARG)->compile; ZENGL_RUN_TYPE * run = &((ZENGL_VM_TYPE *)VM_ARG)->run; ZENGL_AST_SCAN_STACK_TYPE tmpstack; ZENGL_AST_NODE_TYPE * nodes = compile->AST_nodes.nodes; ZL_INT * chnum; ZL_INT chnum_0, chnum_1; ZL_BOOL is_negative; ZENGL_RUN_INST_OP_DATA_TYPE tmpInstType = ZL_R_DT_NONE; ZL_DOUBLE tmpInstData = 0; if(compile->AST_TreeScanStackList.count != 0) compile->exit(VM_ARG,ZL_ERR_CP_SYM_AST_TREE_SCAN_STACK_NOT_EMPTY); if(nodenum == -1) //如果函数参数列表是空的则返回 return; compile->push_AST_TreeScanStack(VM_ARG,nodenum); //先将节点压入栈,形成扫描节点路径的起始点。 do{ tmpstack = compile->pop_AST_TreeScanStack(VM_ARG,ZL_FALSE); //返回前面压入栈的节点信息。参数ZL_FALSE表示只返回信息,暂不将节点从堆栈中删除。 if(tmpstack.nodenum < 0) //如-1之类的空节点则跳过 { compile->pop_AST_TreeScanStack(VM_ARG,ZL_TRUE); continue; } if(tmpstack.curchild == 0) //curchild为0时,表示还没开始扫描子节点,就对当前节点进行处理。 { nodenum = tmpstack.nodenum; switch(nodes[nodenum].toktype) { case ZL_TK_ID: if(compile->SymInsertHashTableForLocal(VM_ARG,nodenum,ZL_SYM_ENUM_LOCAL_TYPE_ARG) == ZL_TRUE) //如果是脚本函数的参数,就将其加入到SymLocalTable局部变量符号表动态数组中,并将其标示为ZL_SYM_ENUM_LOCAL_TYPE_ARG compile->gencode_struct.localID++; break; case ZL_TK_COMMA: break; case ZL_TK_ASSIGN: chnum = nodes[nodenum].childs.childnum; is_negative = ZL_FALSE; chnum_0 = chnum[0]; if(nodes[chnum[1]].toktype == ZL_TK_NEGATIVE && nodes[chnum[1]].childs.count == 1) { chnum_1 = nodes[chnum[1]].childs.childnum[0]; is_negative = ZL_TRUE; } else { chnum_1 = chnum[1]; } switch(nodes[chnum_1].toktype) { case ZL_TK_NUM: tmpInstType = ZL_R_DT_NUM; tmpInstData = (ZL_DOUBLE)ZENGL_SYS_STR_TO_LONG_NUM(compile->TokenStringPoolGetPtr(VM_ARG,nodes[chnum_1].strindex)); break; case ZL_TK_FLOAT: tmpInstType = ZL_R_DT_FLOAT; tmpInstData = ZENGL_SYS_STR_TO_FLOAT(compile->TokenStringPoolGetPtr(VM_ARG,nodes[chnum_1].strindex)); break; case ZL_TK_STR: tmpInstType = ZL_R_DT_STR; tmpInstData = (ZL_DOUBLE)((ZL_LONG)compile->TokenStringPoolGetPtr(VM_ARG,nodes[chnum_1].strindex)); break; default: compile->parser_curnode = nodenum; compile->parser_errorExit(VM_ARG,ZL_ERR_CP_SYNTAX_FUN_ARGLIST_INVALID_TOKEN); break; } if((tmpInstType == ZL_R_DT_NUM || tmpInstType == ZL_R_DT_FLOAT) && is_negative) { tmpInstData = -tmpInstData; } run->AddInst(VM_ARG,compile->gencode_struct.pc++,nodenum, ZL_R_IT_ARG_SET , ZL_R_DT_ARGMEM , compile->gencode_struct.localID, tmpInstType , tmpInstData); //对应汇编指令 类似 "ARG_SET arg(%d) 123" 当某个参数没有被赋值时,设置的默认值 if(compile->SymInsertHashTableForLocal(VM_ARG,chnum_0,ZL_SYM_ENUM_LOCAL_TYPE_ARG) == ZL_TRUE) compile->gencode_struct.localID++; break; default: //compile->parser_curnode = nodenum; //compile->parser_errorExit(VM_ARG,ZL_ERR_CP_SYNTAX_FUN_ARGLIST_INVALID_TOKEN); break; } } if(tmpstack.curchild < tmpstack.childcnt) //如果curchild小于childcnt子节点数,就说明子节点没处理完,则继续处理子节点信息。 { if(tmpstack.curchild < ZL_AST_CHILD_NODE_SIZE) //根据curchild索引判断是基本子节点还是扩展子节点。 nodenum = nodes[tmpstack.nodenum].childs.childnum[tmpstack.curchild]; else nodenum = nodes[tmpstack.nodenum].childs.extchilds[tmpstack.curchild - ZL_AST_CHILD_NODE_SIZE]; compile->AST_TreeScanStackList.stacks[compile->AST_TreeScanStackList.count - 1].curchild++; //将curchild加一,下次就会处理下一个子节点信息。 compile->push_AST_TreeScanStack(VM_ARG,nodenum); //将子节点压入栈 continue; //continue后会跳到do...while开头,然后pop_TreeStack,从而根据前面压入栈的子节点信息进行处理。 } else compile->pop_AST_TreeScanStack(VM_ARG,ZL_TRUE); //如果没有子节点或者子节点也已扫描完,则弹出当前节点。 }while(compile->AST_TreeScanStackList.count > 0); //如果堆栈中还有元素,说明还有节点没处理完,只有当堆栈里的元素个数为0时则表示所有AST里的节点都处理完了,就可以跳出循环返回了。 }
上面C代码在扫描脚本函数的参数时,如果检测到了ZL_TK_NEGATIVE的Token(也就是负数的token)的话,如果参数默认值又是整数或浮点数的话,就会通过tmpInstData = -tmpInstData;语句将参数默认值的负值存入虚拟汇编指令中。从而实现在脚本函数参数中使用负数作为缺省值的功能。
做一个决定,并不难,难的是付诸行动,并且坚持到底。