编译器(解释器)编写指南-编写编译器(解释器)的工具-LEX

原创 2003年01月29日 17:09:00

作者:Riceball(riceballl@hotmail.com)

关键字:   编译器, 解释器, LEX, YACC, 编译原理, 正则表达式, Pascal

预备知识:编译原理,正则表达式,Pascal

本文并不希望深入透彻的讲解编译原理,而是讲解如何利用工具(生成编译器的编译器)去编写编译器。如果你完全不知道编译什么东西,那么请看懂了编译原理,在看此文,本文不是为初学者准备的。

一、什么是编译器(解释器)
编译器是将一种计算机语言翻译为另一种计算机语言的程序。编译器将源程序(source language)
编写的程序作为输入,翻译产生用目标语言(target language)编写的等价程序。源程序一般为高级语言(high-level language),如Pascal 或Delphi,而目标语言则是汇编语言或目标机器的目标代码(object code),有时也称作机器代码(machine code)
  源程序→ 编译器→ 目标程序
解释器也是同编译器一样的一种语言翻译程序。它与编译器的不同之处在于:它立即执行源程序而不是生成目标代码。从原理上讲,任何程序设计语言都可以被解释或被编译。

(1) 扫描程序(scanner)
由扫描程序(Scanner)阅读源程序(通常以字符流的形式表示),进行词法分析(Lexical analysis),它将源程序翻译成单词ID(Token),放入单词ID(Token)表中。在此过程中,扫描程序会进行简单的拼写检查。

单词ID(Token):
  一个Token可以有若干种类型,典型的有:关键字(keyword),例如 if 和 while;标识符(identifier)是由用户定义的变量名,过程名等,它们通常由字母和数字组成并由一个字母开头;特殊符号(special symbol)如算术符号+和*、一些多字符符号,如 >= 和<>。TokenType 为枚举数据类型。实际上就是整数值,每一个数值代表一种单词ID类型。
  TTokenType = (ttNone, ttStrVal, ttIntVal, ttFloatVal,
    //ttNAME 就是标识符 Identifier 或关键字
    ttNAME,
    ttSWITCH,
    ttVAR, ttCONST, ttTYPE, ttRECORD, ttARRAY, ttDOT, ttDOTDOT, ttOF,
    ttTRY, ttEXCEPT, ttRAISE, ttFINALLY, ttON, ttREAD, ttWRITE, ttPROPERTY,
    ttPROCEDURE, ttFUNCTION, ttCONSTRUCTOR, ttDESTRUCTOR, ttCLASS, ttNIL, ttIS,
    ttAS,
    ttVIRTUAL, ttOVERRIDE, ttREINTRODUCE, ttINHERITED, ttABSTRACT,
    ttEXTERNAL, ttFORWARD, ttIN,
    ttBEGIN, ttEND, ttBREAK, ttCONTINUE, ttEXIT,
    ttIF, ttTHEN, ttELSE, ttWHILE, ttREPEAT, ttUNTIL, ttFOR, ttTO, ttDOWNTO, ttDO,
    ttCASE,
    ttTRUE, ttFALSE, ttAND, ttOR, ttXOR, ttDIV, ttMOD, ttNOT, ttPLUS, ttMINUS,
    ttTIMES, ttDIVIDE,
    ttEQ, ttNOTEQ, ttGTR, ttGTREQ, ttLESS, ttLESSEQ, ttSEMI, ttCOMMA, ttCOLON,
    ttASSIGN,
    ttBLEFT, ttBRIGHT, ttALEFT, ttARIGHT, ttCRIGHT,
    ttDEFAULT,

    // Tokens for compatibility to Delphi
    ttPRIVATE, ttPROTECTED, ttPUBLIC, ttPUBLISHED,
    ttREGISTER, ttPASCAL, ttCDECL, ttSTDCALL, ttFASTCALL);

  TTokenTypes = set of TTokenType;

为了表示 Token 的内容,一般我们这样定义Token:
  TToken = Record
    TokenType: TTokenType;
    TokenValue: Variant;
  end;

TP LEX
TP Lex 是词法分析(Lexical analysis)扫描器源程序的生成器,它用于创建Pascal(TurboPascal, Delohi)扫描器子过程。
TP Lex 分析 LEX 文件(默认扩展名为.L),产生词法分析(Lexical analysis)扫描器过程,输出pascal源程序文件。如果 LEX 文件在分析过程发现错误,错误信息将会被写入相应的列表文件(扩展名为.lst)。
创建的 pascal 源文件程序将包含词法分析(Lexical analysis)扫描器过程:yylex。
  function yylex : Integer;
你应该在你的主程序中调用该过程进行词法分析。每调用一次,yylex 的返回值为当前分析的Token类型值。当文件结束时,yylex 的返回值为0。

yylex 过程的代码模板在 yylex.cod 文件中。TP Lex 需要该文件构建生成 pascal 源程序文件。该文件必须在当前目录或TP LEX所在目录下。另外生成好的源程序需要 LexLib.pas 文件进行编译。

用法:
  lex [options] lex-file[.l] [output-file[.pas]]

Options(参数)
-------
-v  "详尽(Verbose):" 在该参数下,Lex 在生成词法分析器的同时,将生成一个可读的说明文件,扩展名`.lst'.
-o  "优化(Optimize):" Lex 将优化 DFA 表,产生一个最小的 DFA.

 

如何编写 LEX 文件(.L)
LEX 文件(.L)分为三个部分,每个部分之间用“%%”隔开:
定义部分(definitions)
%%
规则部分(rules)
%%
辅助过程部分(auxiliary procedures)

三个部分可以都是空的也没有关系,以行为单位作为语句的分隔符。

定义部分(definitions)
定义部分出现在第1个双百分号之前。定义部分(definitions)可以包含以下的元素:
- 正则表达式一般定义格式:
 定义的表达式名称(name)   替换的结果(substitution)

正则表达式的名字也得在该部分定义。这个名字的定义写在另一行的第1列,且其后(后面有一个或多个空格)是它所表示的正则表达式。定义的名称(name)必须是一个合法的标识符(第一位必须是字母,第二位可以是字母或数字)
替换的结果(substitution)是一个LEX正则表达式,你也可以在正则表达式中引用前面定义好的表达式名称,只要将该名称用花括号("{}")扩起即可。例如,带符号数字的定义:
Number           [0-9]+
signedNumber   ("+"|"-")? {Number}

- 开始(start)状态按照如下的格式书写:
     %start name ...
  这用来指定规则的启动条件(详见规则部分)。%start 关键字可以被简写为 %s 或 %S.

- “%{”与“%}”对
  插入在 “%{”与“%}”对中间的是函数外部的任意Pascal源代码(请注意这些字符的顺序)。

规则部分(rules)
它们由一连串带有Pascal代码的正则表达式组成;当匹配相对应的正则表达式时,后面的Pascal代码(动作)就会被执行。规则的格式如下:
  正则表达式(expression)      语句(statement);
注意:语句(statement)必须是单独的一个Pascal语句,最后以分号结尾(如果有多个语句使用Begin...End)。语句(statement)可以分成多行书写,不过后续行必须首先至少留一个空格或tab,用来指示该行是属于上一行的。使用“|”表示该表达式执行的动作和下一个表达式执行的动作(语句)一样。例如,Pascal的注释:
"(*"             |
"{"             begin
                      repeat
                            c := get_char;
                            case c of
                               '}' : ;
                               '*' : begin
                                c := get_char;
                                if c=')' then exit else unget_char(c)
                              end;
                              #0 : begin
                                     commenteof;
                                     exit;
                                   end;
                             end;
                         until false
                     end;

TP Lex 库单元提供了一系列有用的变量和过程,你可以在你编写的动作(语句)中使用。如:yytext 变量返回匹配的字符串。yyleng 变量返回匹配的字符串长度。

在规则部分中的“%{”与“%}”对,中间插入的Pascal源代码,被当作是动作的局部变量(过程)出现。
 
辅助过程部分(auxiliary procedures)
辅助过程部分可以包含Pascal源程序,如辅助过程或主程序,该部分会被简单的放在文件的末尾。

基于Lex 和 Yacc 的 C 语言编译器

最近由于项目需要,看了点关于编译原理和编译器等方面的资料,特别是词法分析和语法分析部分,现做一下小结。       一、编译器及其工作流程        编 译器,是将便于人编写,阅读,维护的高...
  • Chinamming
  • Chinamming
  • 2013年11月26日 23:07
  • 7026

编译原理课设——《TINY编译器》,用lex生成词法扫描实现编译器

《TINY编译器》源码在书上附录B,它并没有使用lex和
  • gaixm
  • gaixm
  • 2014年06月20日 09:19
  • 1180

小白说编译原理-2-lex基本用法

lex词法分析器概述1,使用lex语言书写一套正规表达式的规则,命名为lex.l 2,由lex编译器负责将lex.l编译为lex.h和lex.cpp,这两个文件包含了lex定义的规则 3,再使用标...
  • lipeng08
  • lipeng08
  • 2016年04月23日 09:55
  • 2234

自己写编译器学习总结

如何写一个简单的编译器? https://www.zhihu.com/question/36756224 初学编译原理,想写一个简单的编译器。 是时候亮出我的 LL 语言了,全称:Lambda L...
  • bcbobo21cn
  • bcbobo21cn
  • 2017年05月04日 23:04
  • 1577

编译原理动手实操,用java实现一个简易编译器1-词法解析入门

味道怎样,咬一口就知道,手感如何,摸一把就晓得。编译原理缺的不是理论概念,而是能够动手实践的流程,代码,很多原理用话语怎么讲都难以明了,但跑一遍代码,基本就水落石出。本文本着动手实操(念第一声)的原则...
  • tyler_download
  • tyler_download
  • 2016年02月17日 09:53
  • 6335

一个简单的C语言编译器

源代码: // // #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #define WIN32_LEAN_AN...
  • zxxSsdsd
  • zxxSsdsd
  • 2013年08月07日 18:32
  • 4608

用 JavaScript 写一个超小型编译器

前几天看到 Github 上一个非常好的编译器 Demo: thejameskyle/the-super-tiny-compiler: Possibly the smallest compile...
  • hj7jay
  • hj7jay
  • 2016年08月04日 10:52
  • 1303

学习较底层编程:动手写一个C语言编译器

动手编写一个编译器,学习一下较为底层的编程方式,是一种学习计算机到底是如何工作的非常有效方法。 编译器通常被看作是十分复杂的工程。事实上,编写一个产品级的编译器也确实是一个庞大的任务。但是写一个小巧...
  • mclaren_csdn
  • mclaren_csdn
  • 2014年10月24日 18:14
  • 1068

如何编写你自己的编译器

1、Introduction 简介 This on-line document accompanies the slides and other material available for t...
  • han_miao
  • han_miao
  • 2014年01月17日 12:08
  • 1403

手把手教你做一个 C 语言编译器(9):总结

恭喜你完成了自己的 C 语言编译器,本章中我们发一发牢骚,说一说编写编译器值得注意的一些问题;编写编译器时遇到的一些难题。 本系列: 手把手教你做一个 C 语言编译器(0):前言手把手教你...
  • benpaobagzb
  • benpaobagzb
  • 2016年03月07日 23:48
  • 761
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:编译器(解释器)编写指南-编写编译器(解释器)的工具-LEX
举报原因:
原因补充:

(最多只允许输入30个字)