PL/0语言词法及语法分析系统的设计与实现

  • 来源: 互联网 作者: rocket   2008-03-18/13:23


  • 摘要:本文介绍了一个PL/0语言的词法及语法分析系统的设计与实现
    关键词:循环分支 递归下降 管道 输出重定向

    现在的编译系统都是IDE(Integrated Development Environment)和编译器独立实现,他们之间通过管道通信,本系统也采用这一方法来实现。我首先给出本文中的PL/0语言的文法:

    PL/0语言的BNF描述(扩充的巴克斯范式表示法)
    <prog> → program <id>;<block>
    
    <block> → [<condecl>][<vardecl>][<proc>]<body>
    
    <condecl> → const <const>{,<const>}
    
    <const> → <id>:=<integer>
    
    <vardecl> → var <id>{,<id>}
    
    <proc> → procedure <id>(<id>{,<id>});<block>{;<proc>}
    
    <body> → begin <statement>{;<statement>}end
    
    <statement> → <id> := <exp>
    
                   |if <lexp> then <statement>[else <statement>]
    
                   |while <lexp> do <statement>
    
                   |call <id>[(<exp>{,<exp>})]
    
                   |<body>
    
                   |read (<id>{,<id>})
    
                   |write (<exp>{,<exp>})
    
    <lexp> → <exp> <lop> <exp>|odd <exp>
    
    <exp> → [+|-]<term>{<aop><term>}
    
    <term> → <factor>{<mop><factor>}
    
    <factor>→<id>|<integer>|(<exp>)
    
    <lop> → =|<>|<|<=|>|>=
    
    <aop> → +|-
    
    <mop> → *|/
    
    <id> → l{l|d}   (注:l表示字母)
    
    <integer> → d{d}      
    注释:
    <prog>:程序 ;<block>:块、程序体 ;<condecl>:常量说明 ;<const>:常量;
    
    <vardecl>:变量说明 ;<proc>:分程序 ; <body>:复合语句 ;<statement>:语句;
    
    <exp>:表达式 ;<lexp>:条件 ;<term>:项 ; <factor>:因子 ;<aop>:加法运算符;
    
    <mop>:乘法运算符; <lop>:关系运算符
    
    odd:判断表达式的奇偶性。      
    下面我们先来看看词法及语法分析器的设计与实现。词法分析采用循环分支方法实现,语法分析采用递归下降来实现。它们的程序流程图如下:



    下面我们来实现这个两个分析器。这两个分析器采用一个类CCompiler来实现,这个类的定义如下:
    //编译类
    class CCompiler
    
    {
    
    public:
    
    	CCompiler();
    
    	virtual ~CCompiler();
    
    
    
    public:
    
    	void Compile(char *szFile);//编译,公共接口
    
    	vector<SYNTAXERR> GetSyntaxErr(){return m_vectorSyntaxErr;};//得到语法错误
    
    
    
    protected:
    
    	bool LexAnalysis(char *szStr);	//词法分析
    
    	bool IsOprSym(char *szStr);	//是否为运算符
    
    	bool IsBndSym(char *szStr);	//是否为界符
    
    	bool IsKeyWord(char *szStr);	//是否为关键字
    
    	bool IsInSymbolTab(char *szStr);    //是否已在符号表中
    
    	char* JumpNoMatterChar(char *szStr);//跳过空格,回车,换行符,Tab
    
    
    
    	void OutSymbolTab(char *szFile);//输出符号表到文件
    
    
    
    	void SyntaxAnalysis();//语法分析
    
    	void SyntaxAnalysis_Prog();
    
    
    
    	bool SyntaxAnalysis_Mop();
    
    	bool SyntaxAnalysis_Integer();
    
    	bool SyntaxAnalysis_Aop();
    
    	bool SyntaxAnalysis_Lop();
    
    
    
    	int SyntaxAnalysis_Id();
    
    
    
    	int SyntaxAnalysis_Block();	
    
    	int SyntaxAnalysis_Body();
    
    	int SyntaxAnalysis_Factor();
    
    	int SyntaxAnalysis_Term();
    
    	int SyntaxAnalysis_Lexp();
    
    	int SyntaxAnalysis_Exp();
    
    	int SyntaxAnalysis_Statement();
    
    	int SyntaxAnalysis_Const();
    
    	int SyntaxAnalysis_Proc();
    
    	int SyntaxAnalysis_Vardecl();
    
    	int SyntaxAnalysis_Condecl();
    
    	
    
    protected:
    
    	int m_iVecotrSymbolSize;             //符号表大小
    
    	int m_iCurPointer;                   //符号表中当前指针	
    
    	vector<LEXPROPERTYVS> m_vectorSymbol;//符号表
    
    	vector<SYNTAXERR> m_vectorSyntaxErr; //语法错误代码
    
    };      
    其中:函数bool LexAnalysis(char *szStr);是对输入字符串szStr采用循环分支方法进行词法分析,分析出来的符号放在符号表m_vectorSymbol中,这个符号表采用向量这个数据结构来表示。词法分析得出符号表后,即进入语法分析阶段,语法分析由函数void SyntaxAnalysis();完成。下面这些函数是各非终结符对应的递归子程序。
    bool SyntaxAnalysis_Mop();
    
    bool SyntaxAnalysis_Integer();
    
    bool SyntaxAnalysis_Aop();
    
    bool SyntaxAnalysis_Lop();
    
    
    
    int SyntaxAnalysis_Id();
    
    
    
    int SyntaxAnalysis_Block(); 
    
    int SyntaxAnalysis_Body();
    
    int SyntaxAnalysis_Factor();
    
    int SyntaxAnalysis_Term();
    
    int SyntaxAnalysis_Lexp();
    
    int SyntaxAnalysis_Exp();
    
    int SyntaxAnalysis_Statement();
    
    int SyntaxAnalysis_Const();
    
    int SyntaxAnalysis_Proc();
    
    int SyntaxAnalysis_Vardecl();
    
    int SyntaxAnalysis_Condecl();

    以上我介绍了词法及语法分析核心的设计实现,下面我简单介绍下IDE的实现和IDE与分析核心之间的通信。本系统的IDE与分析核心之间采用管道通信,代码如下:

    DWORD dwThreadID;
    
    ::CreateThread(0,0,CompileThread,this,0,&dwThreadID);//创建进程

    进程创建后调用进程函数,
    //进程函数

    DWORD WINAPI CompileThread(LPVOID pParam)
    
    {
    
    	CCompileSysView *pView=(CCompileSysView*)pParam;
    
    
    
    	pView->GetCompileResult();
    
    
    
    	return 0;
    
    }      
    进程函数调用类的自身函数GetCompileResult();得到分析核心的输出结果,这个函数的实现如下:
    void CCompileSysView::GetCompileResult()
    
    {
    
    SECURITY_ATTRIBUTES sa;
    
     	HANDLE hRead,hWrite;
    
    	CString strFile;
    
    	CString strOut;
    
     	
    
    	strFile.Format("..\\pl\\pl.exe ");//指定分析核心程序的路径
    
        
    
             //当前文件作为参数传给分析核心程序,防止这个文件名中含有空格,故用双引号""将文件名括住
    
    	strFile=strFile+(char)34+m_szCurFile+(char)34;
    
    
    
     	sa。nLength=sizeof(SECURITY_ATTRIBUTES);
    
     	sa。lpSecurityDescriptor=NULL;
    
     	sa。bInheritHandle=TRUE;
    
     	if(!CreatePipe(&hRead,&hWrite,&sa,0))//创建管道进行通信
    
     	{
    
     		MessageBox("Error On CreatePipe()");
    
     		return;
    
     	}
    
     
    
     	STARTUPINFO si;
    
     	PROCESS_INFORMATION pi;
    
     
    
     	si。cb=sizeof(STARTUPINFO);
    
     	GetStartupInfo(&si);
    
     	si。hStdError=hWrite;
    
     	si。hStdOutput=hWrite;//输出重定向到文件
    
     	si。wShowWindow=SW_HIDE;
    
     	si。dwFlags=STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    
        
    
             //创建进程启动分析核心程序
    
     	if(!CreateProcess(NULL,(LPSTR)(LPCTSTR)strFile,NULL,NULL,TRUE,NULL,NULL,NULL,&si,&pi)) 
    
     	{
    
     		MessageBox("Error on CreateProcess()");
    
     		return;
    
     	}
    
     	CloseHandle(hWrite);
    
     	
    
     	char buffer[4096]={0};
    
     	DWORD bytesRead;
    
     
    
     	while(true)
    
     	{
    
     		if(!ReadFile(hRead,buffer,4095,&bytesRead,NULL))
    
     			break;
    
     		strOut+=buffer;
    
    		m_pwndOutBar->SetColorRichEditText(strOut);//将输出结果显示出来
    
     		Sleep(500);
    
     	}
    
    }      
    这样就完成了整个分析系统的设计与实现。下面我们来看看整个系统是怎样运行的。我们先来看看这个系统的运行界面:



    程序运行后,出现如图所示的界面,首先设置分析程序的路径,方法是:点菜单IDE环境(I),设置,会出现如下图所示的对话框:



    在编辑框中输入分析器所在路径即可(默认分析器和源文件在一个目录下)。设置好以后,就可以在代码编辑区输入代码了,或者点“打开”打开文件,然后点击工具栏“启动”(也可按快捷键F7)按钮进行分析,分析完以后,词法分析结果会在“分析结果显示区”显示,词法和语法分析信息会在"输出信息显示区"显示。

    已知的 bug 说明
    由于时间关系,现有如下Bug本人未能调试出来,若有高手调试出来的话,还望告知。
    1. PL.exe 有大量内存泄漏,但是本人在 CCompiler 的析构函数中用如下代码释放内存,不知为何出错:

     


    评论 {{userinfo.comments}}

    {{money}}

    {{question.question}}

    A {{question.A}}
    B {{question.B}}
    C {{question.C}}
    D {{question.D}}
    提交

    驱动号 更多