简单编译器之词法分析 1 名词解释 2 大体流程 3 具体实现
编译器大致可以分为前端和后端,前端执行分析,后端执行合成。
前端又可大致分为词法分析、语法分析和语义分析,本篇文章就是要实现一个简单的词法分析程序。
使用语言为python 2.5。
本文不会详细的解释涉及的名词概念,只会大致的描述一下。
词法单词:语言的文法单位,如int、float等等。
正则表达式:一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。
NFA:全名非确定有限自动机,是一种需要从一个状态出发的多条标有相同符号的边进行选择的状态机。
DFA:全名确定有限自动机,与NFA不同的是,不会有从同一状态出发的两条边标有相同的记号。
2 大体流程
在写词法分析器之前首先确定其作用,词法分析器的主要任务是读入源文件的输入字符,将它们组成字符,生成并输出一个词法单元序列,每个词法单元对应一个词素。
除了识别词素之外,词法分析器还会过滤空白和注释,输出错误信息等等。
一个经典的词法分析流程为:
1. 写出词法单词相应的正则表达式,并转换为NFA;
2. 根据NFA写出对应的DFA;
3. 精简DFA,并且合并所有单词的DFA。
4. 根据完整的DFA开始搬砖。
3 具体实现
本文对应的词法分析器使用的保留字是if、num、int、id、+、=、==、(、)、{、}、"、;。
保留字都很简单,其中int的DFA为:
把全部保留字的DFA合体就得到下面的图:
图出来后就可以建立相应的状态机了:
import string import Util myfile = open('F:\code\test.gogo','r') line = '('; while line: #清除制表符等等 line = myfile.readline().lstrip(); line += ' '; #初始状态 state = 1; #pos到posEnd之间的即为当前适配的单词 pos = 0; posEnd = 0; if line == '': break while pos < len(line): token = line[posEnd]; if state == 1: if Util.isBlank(token): pos = posEnd + 1; posEnd = pos; elif token == 'i': state = 2; posEnd += 1; elif Util.isletter(token) and (token != 'i'): state = 5; posEnd += 1; elif token == ';': Util.printToken(';', line[posEnd]); posEnd += 1; pos = posEnd; elif token == '(': Util.printToken('(', line[posEnd]); posEnd += 1; pos = posEnd; elif token == ')': Util.printToken(')', line[posEnd]); posEnd += 1; pos = posEnd; elif token == '{': Util.printToken('{', line[posEnd]); posEnd += 1; pos = posEnd; elif token == '}': Util.printToken('}', line[posEnd]); pos = posEnd; posEnd += 1; elif token.isdigit(): state = 7; posEnd += 1; elif token == '=': state = 14; posEnd += 1; elif token == ' ': break; else: Util.printError(line[pos:posEnd]); break; elif state == 2: if token == 'n': state = 3; posEnd += 1; elif token == 'f': state = 16; posEnd += 1; elif (token.isdigit() or Util.isletter(token)) and token != 'n' and token != 'f': state = 6; posEnd += 1; else: Util.printToken('id', line[pos:posEnd]); pos = posEnd; state = 1; elif state == 5: if token.isdigit() or Util.isletter(token): state = 5; posEnd += 1; else: Util.printToken('id', line[pos:posEnd]); pos = posEnd; state = 1; elif state == 3: if token == 't': state = 4; posEnd += 1; elif token.isdigit() or Util.isletter(token) and token != 't': state = 6; posEnd += 1; elif state == 6: if token.isdigit() or Util.isletter(token): state = 6; posEnd += 1; else: Util.printToken('id', line[pos:posEnd]); pos = posEnd; state = 1; elif state == 4: if token.isdigit() or Util.isletter(token): state = 6; posEnd += 1; else: Util.printToken('int',line[pos:posEnd]); pos = posEnd; state = 1; elif state == 7: if token.isdigit(): posEnd += 1; elif Util.isletter(token): Util.printError(line); else: Util.printToken('digit',line[pos:posEnd]); pos = posEnd; state = 1; elif state == 14: if token == '=': state = 15; posEnd += 1; else: Util.printToken('=',line[pos:posEnd]); pos = posEnd; state = 1; elif state == 15: if token == '=': Util.printError(line[pos:posEnd]); break; else: Util.printToken('==',line[pos:posEnd]); pos = posEnd; state = 1; elif state == 16: if Util.isletter(token) or token.isdigit(): state = 6; posEnd += 1; else: Util.printToken('if',line[pos:posEnd]); pos = posEnd; state = 1;
当输入为:
if(1 == 1) { int p = 1; }
输出单词序列为:
if if,( (,digit 1,== ==,digit 1,) ),{ {,int int,id p,= =,digit 1,; ;,} }