创造新语言(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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值