v1.9.1版本可以在fun定义的函数参数中使用负数作为参数的默认值,之前的版本如果想使用负数作为参数的默认值的话,只能使用常量宏的形式。

    页面导航:

项目下载地址:

    zengl language v1.9.1的源代码的相关地址:https://github.com/zenglong/zengl_language  当前版本对应的tag标签为:v1.9.1

zengl v1.9.1:

    v1.9.1版本可以在fun定义的函数参数中使用负数作为参数的默认值,之前的版本如果想使用负数作为参数的默认值的话,只能使用常量宏的形式,通过def关键字定义一个负数的常量宏,然后在参数列表中使用该常量宏作为默认值来实现。当前版本就可以直接使用负数作为函数参数的默认值。

test.zl测试脚本:

    当前版本在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;语句将参数默认值的负值存入虚拟汇编指令中。从而实现在脚本函数参数中使用负数作为缺省值的功能。

结束语:

    做一个决定,并不难,难的是付诸行动,并且坚持到底。

 

上下篇

下一篇: 暂无

上一篇: zengl v1.9.0 增加self特殊类名,增加zenglApi_SetDefLookupHandle等接口

相关文章

zengl v1.8.2 修复内存泄漏

zengl v1.7.3 获取数组之类的内存块中非NONE成员的数量

zengl v1.4.0 调试接口 zengl_SDL项目

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

zengl编程语言v0.0.17单行多行注释

zengl编程语言v0.0.22实现switch...case