上一节完成了单条语句的抽象语法树的构建,而且单条表达式里还只能使用变量标示符,不能使用数字,本节将完成多条语句的AST的构建工作,并可以在表达式里使用数字,还为语法树的每个节点元素增加了行...

   上一节完成了单条语句的抽象语法树的构建,而且单条表达式里还只能使用变量标示符,不能使用数字,本节将完成多条语句的AST的构建工作,并可以在表达式里使用数字,还为语法树的每个节点元素增加了行列号,方便调试分析。

  本节v0.0.4版本的代码下载地址: http://pan.baidu.com/share/link?shareid=68799&uk=940392313   (此为百度云盘的共享链接地址)访问该地址可以看到三个文件: zengl_language_v0.0.4_forLinux.tar.gz (此为Linux版的源代码,里面有makefile 和 usage.txt说明文档),v0.0.4-v0.0.3-diffs.txt (此为0.0.4版和0.0.3版源代码的比较文件,通过该文件可以知道两个版本的代码变化情况), zengl_lang_v0.0.4_forXP.rar  (此为XP系统下的vs2008的解决方案和源代码)。 本来代码是打算放在sourceforge上的,但是sourceforge被墙的厉害,所以先放到百度盘上。

  我在SourceForge.net上开了个project,地址为:https://sourceforge.net/projects/zengl/files/  从里面可以看到各个版本的代码压缩包,例如本节的zengl_language_v0.0.4_forLinux.tar.gz,v0.0.4-v0.0.3-diffs.txt,zengl_lang_v0.0.4_forXP.rar

  本节的代码文件和上一节一样,还是三个文件,先来看下main.c源代码的变化: TokenString增加了两个字段,在global.h中可以看到:

typedef struct{
    char * str;
    int size;
    int count;
    int cur;
    int start_line;  //每个token节点对应的第一个字符的行号
    int start_col;  //每个token节点应的第一个字符的列号
}TokenStr_Type;

在main.c的getToken函数里

case START:
                makeTokenStrLineCol(line_no,col_no);  //每次START开始扫描一个新的token时,就将当前的行列号line_no,col_no通过makeTokenStrLineCol函数来设置每个token的起始行列号。line_no 和 col_no 是两个记录着当前行列号的全局变量
                if(ch==' ' || ch=='t' || ch=='n')
                {
                    if(ch == 'n')  //当遇到换行符时,就增加当前的行号,并将列号设为0
                    {
                        line_no++;
                        col_no=0;
                    }
                    continue;
                }

在parser.c中

void AddNode(enum TOKENTYPE token)
{
        ...... //中间代码省略
        AST_nodes.nodes[AST_nodes.count].line_no = TokenString.start_line;  //在向语法树添加新节点时,设置新节点的行号
        AST_nodes.nodes[AST_nodes.count].col_no = TokenString.start_col;  //在向语法树添加新节点时,设置新节点的列号
        AST_nodes.count++;
    }
}

parser.c里增加了一个syntax_error函数用于打印语法错误的信息。

void syntax_error(char * err)
{
    printf(err);  //打印相关的语法错误信息
    printf(": ");
    printNode(AST_nodes.nodes + curnode,FALSE);  //打印和错误相关的语法树节点信息
    myexit(" exit n");  //退出并释放内存池等资源
}

parser.c 的printNode函数增加了打印行列号的程式:

void printNode(Node_Type *node,bool ischild)
{

          ......  //省略中间代码

          printf("line:%d,col:%d n",node->line_no,node->col_no);  //将节点的行列号打印出来。

          ......  //省略中间代码

}

parser.c 的buildAST函数通过循环来构建多个表达式语法树结构的链表结构。

void buildAST()
{
    int p = -1;
    while(curnode < AST_nodes.count-1)
    {
        if(curnode == -1)
        {
            AST_nodes.rootnode = express();  //ASTnodes.rootnode 指向语法树链表结构的根节点。
            p = AST_nodes.rootnode;
        }
        else
        {
            AST_nodes.nodes[p].nextnode = express();  //通过每个节点的nextnode将多个语法树结构的顶端节点相互连接在一起来构建一个c语言的链表结构。
            p = AST_nodes.nodes[p].nextnode;
        }
    }
}

例如在本节中的test.zl测试代码中有三条语句:

a = 5;
b = 3;
c = a + b * 15;

这三条语句通过buildAST函数的循环构建的语法树结构如下所示:

 

buildAST函数通过express函数来构建每个表达式的AST,再通过根节点和next节点相互连接来构建出AST的链表结构。

在本版本的express函数里,加入了NUM枚举,例如 if(ISTOKTYPEOR(ID,NUM)) 等,这样就既可以扫描变量标示符,又可以扫描数字。

该版本的源码就讲到这,其他函数的分析请自行研究源代码。

为了方便查看上个版本和当前版本的不同,所以从git提取了信息存放在v0.0.4-v0.0.3-diffs.txt文件里,从该文件里可以看出两个版本的代码变化情况。下载地址前面已经给出了。

最后linux下的用户在解压Linux版代码后,根据usage.txt的说明,先make clean 清理原来生成的二进制文件,再make生成当前环境下的可执行文件,最后./zengl test.zl 来测试,当然前提是安装了gcc编译器

在./zengl test.zl测试时会打印输出详细的AST抽象语法树的组成详情,包括标示符等所在的行列号信息。

本例的抽象语法树等很多高级的原理都可以在《龙书》中找到。

最后的最后,如果转载请注明来源 http://www.zengl.com   , OK , 先到这里,休息,休息一下 O(∩_∩)O~

上下篇

下一篇: zengl编程语言v0.0.5构建符号表汇编代码和虚拟机

上一篇: zengl编程语言v0.0.3构建抽象语法树

相关文章

zengl编程语言v0.0.8第二代语法解析函数

zengl v1.4.0 调试接口 zengl_SDL项目

开发自己的编程语言-zengl编程语言v0.0.1词法扫描器的实现

zengl编程语言v0.0.18初始化数组函数

zengl v1.8.2 修复内存泄漏

zengl v1.2.0 嵌入式编程语言