编译原理项目

PLO编译器分析

PL0文法

  • Program → Block.
    program

  • Block → [ConstDecl][VarDecl][ProcDecl] Stmt
    程序块的基本结构是:常量定义、变量定义、过程定义、语句

  • ConstDecl → const ConstDef {, ConstDef} ;
    常量定义

  • ConstDef → ident = number

  • VarDecl → var ident {, ident} ;
    变量定义

  • ProcDecl → procedure ident ; Block ; {procedure ident ; Block ;}
    过程定义
    block

  • Stmt → ident := Exp | call ident | begin Stmt {; Stmt} end | if Cond then Stmt | while Cond do Stmt | ε
    赋值语句、调用语句、begin-end块语句、判断语句、循环语句
    stament

  • Cond → odd Exp | Exp RelOp Exp
    条件表达式

  • RelOp → = | <> | < | > | <= | >=
    关系运算符
    condition

  • Exp → [+ | − ] Term {+ Term | − Term}
    表达式
    expression

  • Term → Factor {∗ Factor | / Factor}

    term

  • Factor → ident | number | ( Exp )
    因子
    factor

  • ident
    字母开头的字母/数字串

  • numbers
    无符号整数

PL0指令集定义

PL/0的目标代码放在一个固定的存储数组中,而其中所需的数据组织成一个栈的形式存放。
它的中间语言是一种栈机器代码,其指令集结构如下。

指令/F 含义 L A
LIT 将常数置于栈顶 0 常量
LOD 将变量的值置于栈顶 层次差 数据地址
STO 将栈顶的值赋予某变量 层次差 数据地址
CAL 过程调用 层次差 程序地址
INT 在数据栈中分配空间,t寄存器增加A 0 常量
JPC,JMP 条件/无条件转移 0 程序地址
OPR 一组算术或逻辑运算指令 0 运算类别

PL0编译器执行过程

  1. 读文件
  2. 编译器初始化
    包括:对保留字表( word )、保留字表中每一个保留字对应的 symbol 类型( wsym )、部分符号对应的 symbol 类型表( ssym )、类 PCODE 指令助记符表( mnemonic )、声明开始集合( declbegsys )、表达式开始集合( statbegsys )、项开始符号集合( facbegsys )以及一些全局变量的初始化
  3. 首次调用getsym()进行词法分析
  4. 调用block()过程,包括词法分析和语法分析
  5. 判断当前单词是否为’.’,不是则错误
  6. 判断源程序是否存在错误,是则错误
  7. 没有出错则调用解释过程interpret()
  8. 程序结束

阶段一代码

{PL0编译程序}

program  PL0;
{带有代码生成的PL0编译程序}
{label  99;}
const
  norw = 11; {保留字的个数}
  txmax = 100; {标识符表长度}
  nmax = 14; {数字的最大位数}
  al = 10; {标识符的长度}
  amax = 2047; {最大地址}
  levmax = 3; {程序体嵌套的最大深度}
  cxmax = 200; {生成目标代码数组的大小}

type
  {枚举类型常用自然语言中含义清楚、明了的单词(看成代码)来表示“顺序关系”,
  是一种顺序类型,是根据说明中的排列先后顺序,才具有0,1,2…n的序号关系,
  可用来作循环变量初值和终值,也可用来作数组下标。
  但枚举类型不是数值常量或字符常量,不能进行算术运算,
  只能作为“序号关系”来使用。}
  symbol = (nul, ident, number, plus, minus, times, slash, oddsym,
  eql, neq, lss, leq, gtr, geq, lparen, rparen, comma, semicolon,
  period, becomes, beginsym, endsym, ifsym, thensym,
  whilesym, dosym, callsym, constsym, varsym, procsym );
  {在正常情况下,如果系统按4字节对齐,
  那么尽管前面的A只需要一个字节,
  但是随后的三个字节是空着的,B从下一个四字节的边界开始分配。}
  alfa = packed array [1..al] of char;
  objects = (constant, variable, procedures);

  {Pascal系统把具有共同特征的同一有序类型的对象汇集在一起,形成一个集合,
  可将集合类型的所有元素作为一个整体进行集合运算}

  symset = set of symbol;
  fct = (lit, opr, lod, sto, cal, int, jmp, jpc); {functions}

  {PASCAL系统定义了记录类型,可用来表示不同类型的数据。}
  instruction = packed record
    f : fct;  {功能码}
    l : 0..levmax; {相对层数}
    a : 0..amax; {相对地址}
  end;
  {LIT 0,a : 取常数a
  OPR 0,a : 执行运算a
  LOD l,a : 取层差为l的层﹑相对地址为a的变量
  STO l,a : 存到层差为l的层﹑相对地址为a的变量
  CAL l,a : 调用层差为l的过程
  INT 0,a : t寄存器增加a
  JMP 0,a : 转移到指令地址a处
  JPC 0,a : 条件转移到指令地址a处 }

{全局变量定义}
var
  output : text;{输出文件}
  input : text;{源代码文件}

  ch : char; {最近读到的字符}
  sym : symbol; {最近读到的符号}
  id : alfa; {最近读到的标识符}
  num : integer; {最近读到的数}
  cc : integer; {character count当前行的字符计数}
  ll : integer; {line lengt 当前行的长度}
  kk, err : integer;
  cx : integer; {code index 代码数组的当前下标}
  line : array [1..81] of char;{缓冲一行代码}
  a : alfa;
  code : array [0..cxmax] of instruction;{保存编译后的代码,要输出}
  word : array [1..norw] of alfa;
  wsym : array [1..norw] of symbol;
  ssym : array [char] of symbol;
  mnemonic : array [fct] of packed array [1..5] of char;
  declbegsys, statbegsys, facbegsys : symset;
  {声明开始,表达式形式,项开始的集合}
  table : array [0..txmax] of {符号表,相当于一个类,最多txmax(100)个符号}
         record
           name : alfa;{元素名}
           case kind : objects of
                constant : (val : integer);{如果是变量则保存常量的值}
                variable, procedures : (level, adr : integer)
                {如果是变量或者过程,保留层数和偏移地址}
end;

{定义错误程序,n是错误类型}
{character count}
procedure error (n : integer);
begin
  writeln('****', ' ' : cc - 1, '↑', n : 2);
  err := err + 1
end {error};

procedure getsym;
var  i, j, k : integer;
procedure  getch;
begin
  {cc 当前行的字符计数}
  {ll 当前行的长度}
  if cc = ll then{表示读完,一开始初始化为0}
  begin
    if eof(input) then
    begin
      write('PROGRAM INCOMPLETE');
      writeln;
      halt(0)
    end;
    ll := 0; cc := 0;
    write(cx : 5, ' ');{输出代码地址}
    while not eoln(input) do
    begin
      ll := ll + 1;{行缓冲区+1}
      read(input, ch);
      write(ch);
      line[ll] := ch
    end;
    writeln;
    readln(input);
    ll := ll + 1;
    line[ll] := ' '
    {read(line[ll])}{读下一行}
  end;
  cc := cc + 1;
  ch := line[cc]
end {getch};

begin {getsym}
  while ch = ' ' do getch;{空字符跳过}
  {如果是字母开头}
  if ch in ['a'..'z'] then
    begin {标识符或保留字} k := 0;
    repeat
      if k < al then
      begin k:= k + 1; a[k] := ch
      end;
      getch
    until not (ch in ['a'..'z', '0'..'9']);
    if k >= kk
      then kk := k {kk是上一个标识符的长度}
      else
        repeat a[kk] := ' '; kk := kk - 1;
        until kk = k;{删除上一个标识符在a中的字符}

    id := a;  i := 1;  j := norw;
    {二分查找保留字表}
    repeat  k := (i+j) div 2;
      if id <= word[k] then j := k - 1;
      if id >= word[k] then i := k + 1;
    until i > j;
    if i - 1 > j
    then sym := wsym[k] {把保留字的类型给sym}
    else sym := ident;{未找到,说明是标识符}
    end
  {如果是数字开头}
  else if ch in ['0'..'9'] then
  begin k := 0;  num := 0;  sym := number;
  repeat
    num := 10*num + (ord(ch)-ord('0'));
    k := k + 1;  getch;
  until not (ch in ['0'..'9']);
  if k > nmax then  error(30) {大于数字的最大位数 14}
  end

  {字符是:}
  else if ch = ':' then
  begin  getch;
    if ch = '=' then
    begin
      sym := becomes;{表示赋值}
      getch
    end
    else
      sym := nul;{非法}
  end

  {标点符号}
  else
  begin  sym := ssym[ch];  getch end

end {getsym};

{目标代码的生成}
{Fct, L, A}
procedure gen(x : fct; y, z : integer);
begin
  if cx > cxmax then
  begin write('PROGRAM TOO LONG');
    writeln;
    halt(0)
  end;
  with code[cx] do
    begin
      f := x;  l := y;  a := z
    end;
  cx := cx + 1
end {gen};

{测试字符的合法性,目的是跳过所有的非法字符,使得能继续工作}
procedure test(s1, s2 : symset; n : integer);
begin
  if not (sym in s1) then
    begin
      error(n);
      s1 := s1 + s2;
      while not (sym in s1) do getsym end
end {test};

{处理的主程序}
{fsys用来恢复错误的单词集合}
procedure  block(lev, tx : integer; fsys : symset);
var
  dx : integer; {本过程数据空间分配下标}
  tx0 : integer; {本过程标识表起始下标}
  cx0 : integer; {本过程代码起始下标}

procedure  enter(k : objects);
begin {把objects填入符号表中}
  tx := tx +1;
  with table[tx] do
    begin  name := id;  kind := k;
    case k of
      constant :
        begin
          {amx = 2047,最大地址}
          if num > amax then begin error(30); num := 0 end;
          val := num
        end;
      variable :
        begin
     
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值