zengl v1.3.2 , 修复字符串脚本解析时可能出现的结尾字符死循环问题,将用户自定义的函数指针在用户API接口部分统一设置为void *指针类型,这样一方面可以解决v1.3.1所产生的函数指针兼容问题,另一方面有利于以后扩展和调整用户自定义函数的参数 ,linux中的makefile添加静态编译...

    v1.3.2版本的下载地址如下:

    github项目地址为:https://github.com/zenglong/zengl_language/  选择右侧的Download Zip或Clone in Desktop

    百度盘共享链接地址:http://pan.baidu.com/s/1dDikAKL 这是一个zip压缩包,解压后可以看到linux ,windows,mac和android目录,分别为三个PC操作系统和Android系统下的源码和编译器配置,windows目录中既包含用于vs2008的sln解决方案,还包含用于vc6的dsw工作空间,android目录中包含android工程文件,需要android ndk来编译和调试,该zip压缩包中还包括apk安装包,安装该apk后,可以在手机或平板(平板没测试)上编写运行简单的zengl脚本(只有几个基础的模块函数,其他模块函数需要自行在C源码中添加)。

    sourceforge.net上的项目地址为:https://sourceforge.net/projects/zengl/files/ (里面有所有历史版本的压缩包,包括v1.3.2的zip包,该包里也含有apk安装包)。

    在linux目录中的makefile有两种编译方式,一种动态库的编译方式:默认的make生成的就是动态库,由于生成动态库libzengl.so后,会copy这个.so文件到/usr/lib目录中,所以动态库的方式需要root权限,还有一种是make static来生成静态库,静态库就不需要root权限,动态库的好处是生成的用户可执行文件体积小,在内存中只有一个副本,但是如果多个程序使用不同版本的zengl接口的话,就容易出问题。而静态库的方式会生成libzengl.a文件,该文件会直接链接进可执行文件中,内存中可以同时存在多个版本的副本,不过静态库会增加可执行文件的体积。(make之前最好都make clean清理一下之前的中间文件)

    mac目录中只包含makefile文件和测试用的zengl脚本文件,所以在mac下直接make即可,需要清理中间文件时,可以使用make clean来清理。

    有关android的编译执行过程可以参考v1.3.1里的文章 (因为android是从1.3.1开始引入的),需要说明的是,android里的源代码都是utf8的,所以如果在eclipse中出现乱码,可以在windows菜单的Preferences中将General --> Workspace里面的Text file encoding修改为UTF-8 ,还可以将Preferences中C/C++ --> Debug中的Character encoding也改为UTF8 。

    下面看下v1.3.2所做的修改(这些信息可以在readme或linux的usage.txt中查看到,也可以直接在上面的github链接中查看到):

    zengl v1.3.2 , 修复字符串脚本解析时可能出现的结尾字符死循环问题。
 
    将用户自定义的函数指针在用户API接口部分统一设置为void *指针类型,这样一方面可以解决v1.3.1所产生的函数指针兼容问题,另一方面有利于以后扩展和调整用户自定义函数的参数。

    删除zengl_exportPublicDefs.h文件,将该文件里的内容移入zengl_exportfuns.h,用户文件部分就只需包含zengl_exportfuns.h一个头文件即可,同时也不影响之前版本的用户程序部分。

    linux中的makefile添加静态编译,在make clean清理完之前的中间文件后,使用make static就可以生成libzengl.a静态库,静态库就不需要root权限,普通的make生成的默认是动态库。

    作者:zenglong
    时间:2013年12月31日
    官网:www.zengl.com

    之前的版本如果对main.c的main函数里的run_str做如下调整:

char * run_str =
        "//this is a test for zenglApi_RunStr \n"
        "inc 'test2.zl';\n"
        "a = 135; \n"
        "b=2;\n"
        "print 'a-b is '+(a-b); \n"
        "bltLoadScript('test3.zl'); a";

    上面将run_str的最后一个换行符修改为字符a,这如果是在文件型脚本中时就会提示语法错误,但是在使用zenglApi_RunStr解析执行run_str字符串脚本时就会陷入死循环,所以需要对zengl_main.c里的zengl_getNextchar函数做如下调整:

/*
从文件或字符串脚本中获取下一个字符
*/
ZL_CHAR zengl_getNextchar(ZL_VOID * VM_ARG)
{
......................

    else //字符串脚本
    {
        if(source->cur >= source->run_str_len)
        {
            source->cur++;
            return ZL_FILE_EOF;
        }
        else
            tmpch = source->run_str[source->cur++];
    }

......................
}

    之前的版本在解析到字符串结尾时少了一个source->cur++步骤,所以一旦遇到ungetchar函数就会将cur游标减一,从而造成死循环,对于字符串中间的字符由于都有一个tmpch = source->run_str[source->cur++]的cur游标加一的操作,所以中间的字符都可以正确解析。文件型脚本在解析时是通过feof的C标准函数来判断文件是否读到结尾的,与cur游标无关,所以不会出现这个问题。

    打开zengl_exportfuns.h文件,可以看到,之前版本里的zengl_exportPublicDefs.h文件主体内容都转移到了zengl_exportfuns.h单一文件中,这样用户程序端就只需含有一个头文件即可,例如android目录里的jni中就只有一个zengl_exportfuns.h头文件,多余的zengl_exportPublicDefs.h头文件则被删除,同时用户端代码不需要做调整,因为用户端一直以来都只需包含zengl_exportfuns.h头文件即可。

    另外之前的v1.3.1版本因为调整了用户自定义函数的参数,例如main.c里的main_userdef_info等都多了一个VM_ARG虚拟机指针参数,从而导致和v1.3.0及之前的版本都不兼容,v1.3.2版本将用户自定义的函数指针统一设置为void *指针类型,这样就可以解决和v1.3.0及之前版本的不兼容问题,例如在该版本的zengl_exportfuns.h头文件里:

......................
typedef struct _ZENGL_EXPORT_VM_MAIN_ARGS{
    ZL_EXP_VOID * userdef_info; //用户自定义的显示普通信息的函数,用户可以自定义信息的打印和输出方式。
    ZL_EXP_VOID * userdef_compile_error; //用户自定义的显示错误信息的函数。
    ZL_EXP_VOID * userdef_run_info; //用户自定义的解释器中显示普通信息的函数,用户可以自定义信息的打印和输出方式。
    ZL_EXP_VOID * userdef_run_print; //用户自定义的解释器PRINT之类的指令对应的打印输出方式。
    ZL_EXP_VOID * userdef_run_error; //用户自定义的解释器中显示错误信息的函数。
    ZL_EXP_VOID * userdef_module_init; //用户自定义的全局模块初始化函数,在run解释器入口处会被调用
    ZL_EXP_INT flags; //用户自定义的一些编译器或解释器的选项
}ZENGL_EXPORT_VM_MAIN_ARGS;
......................

    上面的结构体中用户自定义函数指针都改为了void *类型,可以接受任意类型的用户函数指针,所以基于v1.3.0及之前版本的用户程序直接使用v1.3.2的zengl,不会出现1.3.1那样的C警告信息或C++编译错误信息。

    这些void *类型在zengl内部会自动转为正确的函数指针类型:

/*编译器初始化*/
ZL_VOID zengl_init(ZL_VOID * VM_ARG,ZL_CHAR * script_file,ZENGL_EXPORT_VM_MAIN_ARGS * vm_main_args)
{
.............................

    compile->userdef_info = (ZL_VM_API_INFO_FUN_HANDLE)vm_main_args->userdef_info;
    compile->userdef_compile_error = (ZL_VM_API_INFO_FUN_HANDLE)vm_main_args->userdef_compile_error;

.............................
}

    用户自定义函数传递给void *类型的vm_main_args->userdef_info,该userdef_info在内部需要时,又会转为ZL_VM_API_INFO_FUN_HANDLE实际的函数指针类型。

    void指针类型的方式还方便以后添加更多的参数时可以向下兼容。

    linux的makefile文件里在原来动态库编译的基础上,还添加了静态库编译方式:

#makefile for zengl language

CC = gcc
AR = ar

ARFLAGS = rc
CFLAGS = -g3 -ggdb -O0 -std=c99
CDEFINES = -D ZL_LANG_EN_WITH_CH
SHARED_CFLAGS = $(CFLAGS) -shared -fvisibility=hidden -fPIC
STATIC_CFLAGS = $(CFLAGS) -fvisibility=hidden -fPIC

ZENGL_LIB_CFLAGS_LIB = -lzengl

SRCS = zengl_main.c zengl_parser.c zengl_symbol.c zengl_locals.c zengl_assemble.c zengl_ld.c zenglrun_main.c zenglrun_func.c zenglApi.c zenglApi_BltModFuns.c
OBJS = zengl_main.o zengl_parser.o zengl_symbol.o zengl_locals.o zengl_assemble.o zengl_ld.o zenglrun_main.o zenglrun_func.o zenglApi.o zenglApi_BltModFuns.o 
RUNOBJS = main.o

zengl: $(RUNOBJS) encrypt.o libzengl.so
		$(CC) $(CFLAGS) -o zengl $(RUNOBJS) $(ZENGL_LIB_CFLAGS_LIB)
		$(CC) $(CFLAGS) -o encrypt encrypt.o $(ZENGL_LIB_CFLAGS_LIB)

st_zengl: $(RUNOBJS) encrypt.o libzengl.a
		$(CC) $(CFLAGS) -o zengl $(RUNOBJS) libzengl.a
		$(CC) $(CFLAGS) -o encrypt encrypt.o libzengl.a

libzengl.a: $(OBJS)
		$(AR) $(ARFLAGS) libzengl.a $(OBJS)

$(OBJS): $(SRCS)  zengl_global.h zengl_locals.h zengl_exportfuns.h
		$(CC) $(CDEFINES) $(STATIC_CFLAGS) -c $(SRCS)

libzengl.so : $(SRCS) zengl_global.h zengl_locals.h zengl_exportfuns.h
		$(CC) $(CDEFINES) $(SHARED_CFLAGS) -o libzengl.so $(SRCS)
		cp libzengl.so /usr/lib
		
main.o: main.c zengl_exportfuns.h
		$(CC) $(CFLAGS) -c main.c

encrypt.o: encrypt.c zengl_exportfuns.h
		$(CC) $(CFLAGS) -c encrypt.c

clean:
		rm -fv libzengl.so
		rm -fv zengl
		rm -fv encrypt
		rm -fv main.o
		rm -fv encrypt.o
		rm -fv libzengl.a
		rm -fv *.o

all: zengl libzengl.so
static: st_zengl



    上面代码中红色和蓝色标记的就是为编译静态库而增加的部分。

    在make static时,make工具就会生成st_zengl伪目标,在生成过程中,会将每个zengl开头的.c文件都编译为.o目标文件,然后使用ar工具生成libzengl.a静态库,最后生成zengl和encrypt可执行文件时,就将libzengl.a静态库链接进程序中。

    因为静态库多了.a和很多.o文件,所以clean时就多了两条rm操作。

    有关该版本的其他改动其查看github里相关的diff信息。

    OK,到这里,休息,休息一下 o(∩_∩)o~~
上下篇

下一篇: zengl v1.4.0 调试接口 zengl_SDL项目

上一篇: v1.3.1 Android编译执行zengl脚本

相关文章

zengl v1.2.0 嵌入式编程语言

开发自己的编程语言-zengl编程语言v0.0.2初始化抽象语法树

zengl v1.8.2 修复内存泄漏

zengl编程语言 v1.0.3 完善文件出错信息

zengl v1.9.1 函数参数可以使用负数作为默认值

zengl编程语言v0.0.16数组,21点扑克小游戏