Second-词法分析

Second-词法分析

编译器阶段

  • 源程序 -> 编译器 -> 目标程序
  • 编译器: 前端 -> 中间表示 -> 后端
    • 前端: 词法分析器 -> 记号 -> 语法分析器
    • 中间表示: 抽象语法树
    • 后端: 语义分析器
  • 词法分析器: 一段程序代码,主要功能是把字符流转变为记号流
  • 词法分析器列子:
    • 字符流输入:if (x > 5)
    • 词法分析结果:IF LPAREN IDENT(x) GT INT(5) RPAREN
    • 词法分析步骤:
    • 把字符流转变为编译器内部定义的数据结构,编码所能识别出的词法单元
    • 读入i,识别i之后把它转换为数据结构:k=IDENT;lexeme=i;
    • 读入f,识别f之后把它转换为数据结构:k=IDENT;lexeme=f;
    • 读入空格,转到终态,查表识别关键字,返回关键字:IF;
    • 读入(,识别(之后把它转换为数据结构:k=LPAREN;lexeme=nil;
    • 读入x,识别x之后把它转换为数据结构:k=IDENT;lexeme=x;
    • 读入空格,转到终态,查表识别关键字,返回标识符:IDENT(x);
    • ……
  • 记号的数据结构定义
enum kind {
    IF, LPAREN, ID, ...
    };

struct token {
    enum kind k;
    char *lexeme;
    }

词法分析器的实现方法

  • 两种方案:

    1. 手工编码:需要如何转换自己编写代码实现

      • 相对复杂、且易于出错
      • 目前非常流行的实现方法:GCC,LLVM…
    2. 词法分析器的生成器:输入其声明式的规范,即可自动生成词法分析器实现代码
      • 可快速原型、代码量较少
      • 但较难控制细节

词法分析器的手工编码

状态转换图

  • 识别高级语言的单词符号
  • 状态转换图算法
token nextToken()
    c = getChar();
    switch(c)
        case ‘<‘: c = getChar();
                  switch(c)
                  case ‘=‘: return LE;
                  case ‘>‘: return NE;
                  default: rollback();return LT;
        case ‘=‘: return EQ;
        case ‘>‘: c = nextChar();
                  switch(c)
                  ...
  • 标识符转换图

  • 初始状态下的字符是字母或下划线,转换到1状态,如果是字母或数字或下划线,执行闭包,如果为其他字符则进入终态,返回标记号,回退其他字符到初始态进行下一轮识别
  • 关键字表算法
    • 对给定语言中所有的关键字,构造关键字构成的哈希表H
    • 对所有的标识符合关键字,先统一按标识符的状态转换图进行识别
    • 识别完后,进一步查表H看是否是关键字
    • 通过合理的构造哈希表H(完美哈希),可以O(1)时间完成

词法分析器的自动生成

正则表达式

  • 对状态转换图的概念加以形式化,便于词法分析器的自动生成
  • 对给定的字符集∑={c1,c2,c3…,cn}
  • 归纳定义:其中前两种是基本,后三种是归纳
    • 空串ε是正则表达式
    • 对于任意c∈∑,c是正则表达式
    • 选择 M|N = {M,N}
    • 连接 MN = {mn|m∈M,n∈N}
    • 闭包 M* = {ε,M,MM,MMM,…}
  • 正则表达式的形式表示,其中前两种是基本,后三种是归纳
 1.  e -> ε
 2.  | c (c∈∑)
 3.  | e | e
 4.  | e e
 5.  | e*`
  • 问题:对于给定字符集∑={a,b},可以写出哪些正则表达式
    1. ε
    2. a,b
    3. ε|ε, ε|a,...
    4. εa,εb,ab,εε,a(ε|a),a(ε|b),...
    5. (a(ε|a))*,ε闭包...
  • 例子:关键字 ∑= ASCII
if: i ε ∑, f ε ∑,i与f之间存在一个连接符,连接后依然是正则表达式
int: i ε ∑, n ε ∑,t ε ∑, 它们之间存在两个连接符,由正则表达式的归纳可知它们依然是正则表达式
  • 例子:标识符

  • 用正则表达式表示:(26+26+1)(26+26+10+1)
  • (a|b|…|z|A|B|…|Z|下划线)(a|b|…|z|A|B|…|Z|_|1|2|…|0)
    • 语法糖:都可以用正则表达式求得,但是比正则表达式容易使用

有限状态自动机(FA)

  • 更一般化得状态转换图,分为DFANFA
  • 输入的字符串 -> FA -> {Yes, No}
  • M = (∑,S,q0,F,δ) -> (字母表,状态集,初始状态,终结状态集,转移函数)
  • DFA自动机例子: 对于任意的字符,最多有一个状态可以转移

  • 字母表:{a,b}
  • 状态集:{0,1,2}
  • 初始状态:0
  • 终结状态集:{2}
  • 转移函数:
{(q0,a)->q1,(q0,b)->q0,
 (q1,a)->q2,(q1,b)->q1,
 (q2,a)->q2,(q2,b)->q2,
 ...
 }
  • NFA自动机例子: 对于任意的字符,有多于一个状态可以转移

- 字母表:{a,b}

- 状态集:{0,1,}

- 初始状态:0

- 终结状态集:{1}

- 函数转移

{(q0,a)->{q0,q1},
 (q0,b)->{q1},
 (q1,b)->{q0,q1},
 ...
 }
  • DFA的实现

RE -> NFA

  • 自动生成

    • 声明式的规范 -> NFA -> 词法分析器DFA
    • RE (Thompson算法)-> NFA(子集构造算法) -> DFA(最小化算法) -> 词法分析器代码
  • Thompson算法
    • 对基本的RE的直接构造(e -> ε; e -> c)
    • 对复合的RE递归构造(复合:a(b|c)*,递归成最基本的再直接构造)
  • 例子:a(b|c)*

  • 从左到右逐个拆分
    • 最后结果M{{a,b,c},{0-9},0,9,δ}

NFA -> DFA

  • 子集构造算法

q0 <- eps_closure (n0)  //求n0状态的ε_闭包 -> q0 = {n0};
Q <- {q0}               // Q = {q0};
workList <- q0
while (workList != [])        //当工作表不为空
    remove q from workList      //取出工作表一个元素
    foreach (character c)       // 对256个字符做循环
    t <- e-closure(delta(q,c))  // 求变节点,再求节点闭包
    D[q,c] <- t                 // (q0,c) -> q1
    if (t\not\in Q)
      add t to Q and workList   // 如果子集合t没有包含在集合Q上,则把它加到Q
  • 不动点算法,能够运行终止Q={q0,q1,q2,q3,…}元素有限,子集数为2^n
  • 最坏情况下的时间复杂度为O(2^n)
  • 在实际中不常发生,因为并不是每个子集都会出现
  • 算法步骤:
    1. 在起始状态q0下读入∑里的任意一个字符,能够到达的节点状态,再求该节点的ε_闭包,以上描述的两部分求出的范围便是q1子集
    2. 在q1子集的元素中读入∑里的任意一个字符,能够到达的节点状态,再求该节点的ε_闭包,以上描述的两部分求出的范围便是q2子集
    3. 继续求直至没有字母表的字符没有
    4. Q={q0,q1,q2,q3,…}
      • ε_闭包的计算:深度优先,子集首先包括了节点自身
/** 深度优先时间复杂度:O(N) */
// 全局变量,集合,空集
set closure = {};

void eps_closure (x)
    closure += {x}          // 把x加进集合
    forreach (y: x--ε--> y) // x通过边ε到达y
    if (!visited(y))        // 如果y没走过,递归走y
      eps_closure (y)

// 如果一开始有多个节点,则求多个节点的闭包之后求并集

DFA的最小化:简化后的边、状态越少,所需要占用的资源越小

  • 算法:Hopcroft:基于等价类的思想

// S:一个状态的集合,split:切分
split(S)
    foreach (character c)
        if (c can split S)
          split S into T1,T2,...,Tk

hopcroft ()
    split all nodes into N,A // 把所有切分为两个不可相容的状态,接受和不可接受状态,
    while (set is still changes)
        split(s)
  • 例1

  • 例2

DFA的代码表示

  • 转移表—邻接矩阵:状态、字符
  • 哈希表
  • 词法分析驱动代码
  • 最长匹配
  • 跳转表
  • 具体选择哪一种要取决于实际中,对时间空间的权衡
时间: 2024-08-26 14:48:11

Second-词法分析的相关文章

词法分析实验报告

词法分析实验报告 一.        实验目的 编制一个词法分析器,通过该词法分析程序的设计实例,进一步了解词法分析程序构造的一些细节. 二.        实验内容和要求 实验内容: 对字符串表示的源程序,从左到右进行扫描和分解.根据词法规则,识别出一个一个具有独立意义的单词符号,以供语法分析之用,若发现词法错误,则返回出错信息. 实验要求: 输入:源程序字符串 输出:二元组(种别,单词本身) 待分析语言的词法规则 三. 实验方法.步骤及结果测试 源程序名:压缩包文件(rar或zip)中源程序

词法分析

词法分析 #include<stdio.h> #include<string.h> void main() { int i=0,j,k=0,state=1,f=0,linenum=1; char a[11][10]={"const","var","call","begin","if","while","do","odd",&

实验报告-词法分析.c

实验一.词法分析程序 商业软件工程   简梓茵  201506110132 一.        实验目的   熟悉词法分析的形式. 二.        实验内容和要求 对输入的每个关键字或者字符进行词法分析,给出该关键字对应的排序序号 三.        实验方法.步骤及结果测试   1.      源程序名:wordanalyse.c 2.      原理分析及流程图 3.      主要程序段及其解释: #include <stdio.h> #include <string.h>

实验一词法分析程序

一.        实验目的 编制一个词法分析程序 二.        实验内容和要求 输入:源程序字符串 输出:二元组(种别,单词本身) 待分析语言的词法规则 三.        实验方法.步骤及结果测试 1.      源程序名:压缩包文件(rar或zip)中源程序名 cifafenxi.c  cifafenxi.exe 2.      原理分析及流程图 3.      主要程序段及其解释: do { scanf("%s\n",input); i=0; while(input[i]

词法分析程序实验报告

实验一词法分析实验 商业软件工程2班  黄铭坤  201506110147 一.        实验目的        编制一个词法分析程序. 二.        实验内容和要求 输入:源程序字符串 输出:二元组(种别,单词本身) 待分析语言的词法规则 三.        实验方法.步骤及结果测试 1.      源程序名:词法分析.c 可执行程序名:词法分析.exe 2.      原理分析及流程图 (1)   关键字: begin  if  then  while  do  end (2) 

词法分析程序新

词法分析程序的功能:提示用户输入一段需分析的源程序,然后程序读入该源程序的字符串进行词法分析,最后输出词法分析的结果显示在屏幕上.符号与种别码对照表:单词符号 种别码 单词符号 种别码begin     1        :          17if           2        :=        18then      3        <          20while      4       <=        22do         5 end       6    

词法分析2

#include <stdio.h> #include <string.h> #define max 100 char a[max],b[8],ch; int syn,p,m,n,sum; char word[][6]={"begin","if","then","while","do","end"}; void mor() { sum=0; ch=a[p++];

词法分析程序(编译原理)

一:词法分析程序的功能: 输入单词串,以'begin'开始,以'#'结束.如果是文法正确的句子,则输出成功的信息:'输入单词串成功',否则返回错误提示. 例: 输入 begin a:=345 end # 输出 输入单词串成功 二:符号与种码对照表 单词符号 种别码 单词符号 种别码 begin 1 : 17 if 2 := 18 then 3 <  20 while 4 <= 21 do 5 <>  22 end 6 >  23 l(l|d)* 10 >= 24 dd*

简单的词法分析程序

词法分析程序完成的是编译第一阶段的工作,它的主要任务是从左至右逐个字符地对源程序进行扫描,产生一个个单词序列,用于语法分析. 我写的简单代码实现如下: #include<stdio.h>#include<string.h>#define N 80char word[6][10]={"begin","if","then","while","do","end"};vo

第十九篇 js高级知识---词法分析和AO 链

上面一篇文章说了js的作用域链,这一节算是对上面的延申,有一个典型的例子,首先看原来的一段代码: var name = "test"; function t() { var b = 1; if (b === 1) { var name = "test1"; } else { var name = "test2"; } console.log(name); } t(); 这段代码的结果是test1 这个肯定是没有什么问题了,但是如果把代码作如下修改