创造新语言(3)——添加语义处理程序

好久没有更新了,主要是研究了一段时间的C++的面向对象的设计方式和更好的架构程序的思路。其实今天这部分代码早已完成,但希望能够更好的给大家讲解如何做一个可用的新编程语言。

上次说到Bison的语义分析功能,但并没有添加对应的语义处理功能,我们这次就构建一个描述语法的抽象语法树。

首先Bison的语义处理功能是十分方便的,只要在后面添加C++的语义动作代码就可以了,但注意,$$ $1 $2 $3则是内部变量,$$表示归结元素,其余表示产生式中的元素,(脚本也是其中的$)。

而这些元素具体是什么变量,则是根据我们之前定义的元素变量名而定的。

所以我们的处理器变成了这个样子:

/* parser.y */
%{
#include <stdio.h>
#include "State.h"

extern int yylex();
extern int yylineno;
extern char* yytext;
extern int yyleng;
extern State* root;

void yyerror(const char *s);

%}

%union {
    State *s;
    char *str = NULL;
}

%token <str> ID STRING SCRIPT

%type <s> list item bnf_item bnf_list symbol_list symbol name

%start list

%%

/* 总的混合bnf和脚本的列表 */
list : item { $$ = new State();  $$->AddChildrenState($1); root = $$; }
     | list item { $1->AddChildrenState($2); $$ = $1; }
     ;

/* 可以是bnf或脚本 */
item : bnf_item { $$ = $1; }
	 | SCRIPT { $$ = new State(); $$->state_type = script; $$->script = $1; }
     ;

/* 一行bnf的定义 */
bnf_item : symbol ‘=‘ bnf_list ‘;‘ { $1->AddChildrenState($3); $$ = $1; $$->isList = false; }
         ;

/* bnf后面的部分 */
bnf_list : symbol_list { $$ = $1; }
		 | bnf_list ‘|‘ symbol_list { $1->AddBrotherState($3); $$ = $1; }
         ;

/* 一条bnf项的列表 */
symbol_list : symbol { $$ = new State(); $$->state_type = temporality; $$->AddChildrenState($1); }
			| symbol_list symbol { $1->AddChildrenState($2); $$ = $1; }
            ;

/* 可用的bnf符号 */
symbol : ‘<‘ name ‘>‘ { $$ = $2; $$->state_type = statement; }
       | ‘[‘ name ‘]‘ { $$ = $2; $$->state_type = terminal; }
       | ‘e‘ { $$ = new State(); $$->state_type = epsilon; }
       | STRING { $$ = new State(); $$->state_type = constant; $$->state_const = $1; }
	   | SCRIPT { $$ = new State(); $$->state_type = script; $$->script = $1; }
       ;

/* 名字,并且可以定义实例名 */
name : ID  { $$ = new State();
			 $$->state_class = $1; }
     | ID ‘:‘ ID  { $$ = new State();
	 				$$->state_class = $1;
                    $$->state_var = $3; }
     ;
%%

void yyerror(const char* s){
    fprintf(stderr, "%s \n", s);
    fprintf(stderr, "line %d: ", yylineno);
    fprintf(stderr, "error %s \n", yytext);
}

%type <s> 就是根据union中的元素名定义,将当前节点绑定上一个对应的成员名。

这些动作代码十分重要,但也思路很清晰,就是将当前的逻辑结构转成抽象语法树,保存起来,供我们下一步的运算使用。

之后我们只需要解析这个ast,就能知道我们的BNF语法的语法结构了。

我们做的目前,并没有一个很令人惊讶的结论,但我们是在自己编写一个可靠的编译器,词法分析器,语法分析器都将是我们手工编写的,而这些代码,正是解析语法分析器配置语法的第一步。

不用太过心急,因为,想实现一个可用的LALR语法分析器,工作量还是较大的,但我还是希望,能将其最重要的部分分享给大家。

那么下面,我们就解析一下生成的ast,将BNF范式提取出来吧。

这是一个BNFParser类:

/*
* @Author: sxf
* @Date:   2015-04-17 10:05:26
* @Last Modified by:   sxf
* @Last Modified time: 2015-04-17 11:06:18
*/

#ifndef BNF_PARSER_H
#define BNF_PARSER_H

#include "afx.h"
#include "State.h"

class BNFParser {

public:

    State* Analysis(const char* filename);

    // for debug
    void printTree();

private:
    State* state_root;

    // for debug
    void printNode(State* s,int d);
};

#endif // BNF_PARSER_H
/*
* @Author: sxf
* @Date:   2015-04-17 10:30:02
* @Last Modified by:   sxf
* @Last Modified time: 2015-04-17 14:06:04
*/

#include "BNFParser.h"
#include "parser.hpp"
#include <stdio.h>
extern FILE* yyin;
State* root = NULL;

State* BNFParser::Analysis(const char* filename) {
    /* open the file and change the yyin stream. */
    FILE* file_in;
    if ((file_in=fopen(filename,"r"))==NULL) {
        printf("error on open %s file!",filename);
        getchar();
        return NULL;
    }
    yyin = file_in;
    yyparse();
    state_root = root;
    /* you should close the file. */
    fclose(file_in);
    return root;
}

void BNFParser::printNode(State* s,int d)
{
    if (s == NULL) return;

    for (int i = 0; i<d; ++i)
        printf("    ");
    if (s->state_type == statement || s->state_type == terminal)
        printf("%s %s",s->state_class,s->state_var);
    if (s->state_type == constant)
        printf("%s",s->state_const);
    if (s->state_type == temporality)
        printf("temp node");
    if (!s->isList)
        printf(" (not list)");
    if (s->Repeatable >0 )
    {
        if (s->Repeatable == 1) printf(" ?");
        if (s->Repeatable == 2) printf(" +");
        if (s->Repeatable == 3) printf(" *");
    }

    printf("\n");
    printNode(s->children,d+1);
    printNode(s->brother,d);
}

void BNFParser::printTree()
{
    printNode(state_root,0);
}

这样,再写一个main函数,就可以测试我们的代码是否正确了,大家可用用下面的代码生成一下测试看看

    bison -o Parser.cpp parser.y
时间: 2024-10-10 18:17:40

创造新语言(3)——添加语义处理程序的相关文章

创造新语言(2)——用Lex&amp;Yacc构建简单的分析程序

昨天我们开始设计了一门新语言,制定了基本的开发架构,今天我们就先来了解一下,两个非常好用的工具,编译器前端构建的神器--Lex&Yacc,这两个工具在linux下叫做flex和bison. Lex是词法分析器构建工具,我们安装对应的词法规则书写,那么就能够为我们生成对应的词法分析器,自动帮我们分好token,而分词工作,一直是编译系统的基础任务. 我们今天,先来尝试编写一个BNF语法的解析器,将我们的BNF解析成代码可以识别的数据格式,BNF的格式大概是这样: {{ do_init() }} #

创造新语言(1)——确定架构

最近写C代码很不爽,感觉很多地方十分冗余,希望能够改进一下,于是诞生了这个想法,利用学过的编译原理的前端知识,构建一门新语言的编译器,然后将新语言翻译成为标准的C89. 这样,即保障了可用性,同时又大大减轻了后端的工作量,何乐而不为呢? 我说明一下选择C89的原因,首先,C语言的扩展不多,主要是C++和Objective-C.编译到的目标语言平台,如果是C++,太重了,难以做操作系统级的编程,但如果想让编译器设计的十分轻巧,那么就要尽量回避重型语言. Java也是一个不错的选择,但其平台上已经有

Swift 新语言开发

全书目录: 一.Welcome to Swift 二.Language Guide 三.Language Reference /* 译者的废话: 几个小时前熬夜看了WWDC,各种激动,今年很有料啊!当看到Swift出来的时候,瞬间傻眼,又要学习新语言了.这篇文章来自苹果官方的<The Swift Programming Language>一书,500页左右,在苹果官网有下载.Swift大家都没实际用过,本翻译一定是有各种错漏的,各位多多包涵,我会不断更新修正的. --(博客园.新浪微博)葛布林

新语言学习

用过的语言按时序算:c++.lua(粗浅脚本).erlang.c#(unity).lua.go.印象最深的是erlang,因为特别吃亏,嗯. 那会连多线程.多进程都不怎么了解,虽然看了erlang的进程通信模型,mail_box.gen_server原理,但不理解什么时候要它们.还有递归为主的编码方式,也不大习惯. …… 以上不是本是要说的重点:技术上的东西,时间够,多用用就熟络了,且项目组对新人也有足够的宽容度. 更紧要的是那些“安全性”上的东西,尤其对已发布的项目而言. 自己以往编码,把几乎

Java8新语言特性

Java8简明指南 欢迎来到Java8简明指南.本教程将一步一步指导你通过所有新语言特性.由短而简单的代码示例,带你了解如何使用默认接口方法,lambda表达式,方法引用和可重复注解.本文的最后你会熟悉最新的API的变化如Stream,Fcuntional,Map API扩展和新的日期API. 接口的默认方法 在Java8中,利用default关键字使我们能够添加非抽象方法实现的接口.此功能也被称为扩展方法,这里是我们的第一个例子: interface Formula { double calc

博客园新语言代码高亮以及OpenLiveWriter插件开发

参考: 新语言代码高亮及Windows Live Writer插件开发 最近学习Swift,在博客上写笔记的时候一直觉得Swift代码的高亮不太令人满意,因为博客园的编辑器上传代码模板中没有包括Swift在内的新语言. 偶然看到了上面这篇参考博文,为我提供了一些解决思路,决定一试. 1.HighLight.js 下载地址:https://highlightjs.org/ 在下载页面选择自己需要适配的编程语言,然后下载得到一个压缩包,解压后内容如下: highlight.pack.js中包含的对选

在C#6.0中的新语言特性

孙广东    2015.6.5 What's New in C# 6: http://channel9.msdn.com/Shows/Visual-Studio-Toolbox/Whats-New-in-C-6 Cross Platform Development系列:http://channel9.msdn.com/Shows/CZSK-videa/Cross-Platform-Development-1-Introduction Developer Productivity: What's

Atitit.aticmd&#160;v4&#160;&#160;新特性q39&#160;添加定时器释放功能

Atitit.aticmd v4  新特性q39 添加定时器释放功能 V1  实现兰cmd V2 标准输入,标准输出,标准错误与重新定向 V3  stdout stderr统一重新定向 V4  添加定时器释放功能 V5   兼容性的.net java php V6   bigfile output的solu  ,arg sh all total ret... 作者:: 绰号:老哇的爪子 ( 全名::Attilax Akbar Al Rapanui 阿提拉克斯 阿克巴 阿尔 拉帕努伊 ) 汉字名:

c#给用户控件添加事件处理程序

1.首先在usercontrol后台添加如下代码: public partial class MyControl: UserControl { //添加事件代理       public event EventHandler AX; //在需要响应的事件中添加 private void MyControl_MouseClick(object sender, MouseEventArgs e)        {            if (AX != null)            {