PL0编译器扩展

该博客旨在介绍PL/0编译器的扩展功能,包括增加带else子句的条件语句和参数传递。实验目的涉及理解编译程序的逻辑过程,如词法分析、语法分析、语义分析及目标代码生成。文章讨论了如何修改源代码以支持这些扩展,并提出了一个问题:在给定的PL/0代码中,增加else子句后的变量x的值及其合理性分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实验四 pl0编译器的扩展

一、实验目的和内容
1. 理解语法、语义分析程序为中心的单遍编译程序组织方法;
2. 理解编译程序的基本逻辑过程(词法分析、语法分析、语义分析及目标代码的生成;
3. 理解编译过程中的符号表、内存管理、错误处理的基本方法;

二、扩展要求(每项50分)
修改PL/0编译程序和类P-code解释程序的源代码,以支持对PL/0语言所进行的如下扩充,并调试通过:
1. 给PL/0语言增加带else子句的条件语句
<条件语句>::=IF <条件>THEN <语句>[ELSE <语句>]
问题:按照你的修改方式,如下PL/0代码执行后变量x的值是什么?你认为这样的结果是否合理?为什么?

  1. 增加参数
    为函数或过程增加参数,实现传值方式。
    PROCEDURE<过程名>(<形式参数表>);

    FUNCTION<函数名>(<形式参数表>);
    形式参数表的语法格式和变量声明完全一致。
/*
 * PL/0 complier program for win32 platform (implemented in C)
 *
 * The program has been test on Visual C++ 6.0,  Visual C++.NET and
 * Visual C++.NET 2003,  on Win98,  WinNT,  Win2000,  WinXP and Win2003
 *
 */

typedef enum {
    false,
    true
} bool;


#define norw 14     /* 关键字个数 */
#define txmax 100   /* 名字表容量 */
#define nmax 14     /* number的最大位数 */
#define al 10       /* 符号的最大长度 */
#define amax 2047   /* 地址上界*/
#define levmax 3    /* 最大允许过程嵌套声明层数 [0,  levmax]*/
#define cxmax 500   /* 最多的虚拟机代码数 */

/* 符号 */
enum 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,
    writesym,    readsym,   dosym,      callsym,   constsym,
    varsym,      procsym, elsesym
};
#define symnum 33

/* 名字表中的类型 */
enum object {
    constant,
    variable,
    procedur,
    parametre,//参数类

};
struct prod   //临时记录语法分析时函数参数的个数和名字
{
    char id[al+1];
};
struct prod pnow[15];
int  prodn=0;


/* 虚拟机代码 */
enum fct {
    lit,     opr,     lod,
    sto,     cal,     inte,
    jmp,     jpc,
};
#define fctnum 81

/* 虚拟机代码结构 */
struct instruction
{
    enum fct f; /* 虚拟机代码指令 */
    int l;      /* 引用层与声明层的层次差 */
    int a;      /* 根据f的不同而不同 */
};

FILE* fas;  /* 输出名字表 */
FILE* fa;   /* 输出虚拟机代码 */
FILE* fa1;  /* 输出源文件及其各行对应的首地址 */
FILE* fa2;  /* 输出结果 */
bool listswitch;    /* 显示虚拟机代码与否 */
bool tableswitch;   /* 显示名字表与否 */
char ch;            /* 获取字符的缓冲区,getch 使用 */
enum symbol sym;    /* 当前的符号 */
char id[al+1];      /* 当前ident, 多出的一个字节用于存放0 */
int num;            /* 当前number */
int cc, ll;          /* getch使用的计数器,cc表示当前字符(ch)的位置 */
int cx;             /* 虚拟机代码指针, 取值范围[0, cxmax-1]*/
char line[81];      /* 读取行缓冲区 */
char a[al+1];       /* 临时符号, 多出的一个字节用于存放0 */
struct instruction code[cxmax]; /* 存放虚拟机代码的数组 */
char word[norw][al];        /* 保留字 */
enum symbol wsym[norw];     /* 保留字对应的符号值 */
enum symbol ssym[256];      /* 单字符的符号值 */
char mnemonic[fctnum][5];   /* 虚拟机代码指令名称 */
bool declbegsys[symnum];    /* 表示声明开始的符号集合 */
bool statbegsys[symnum];    /* 表示语句开始的符号集合 */
bool facbegsys[symnum];     /* 表示因子开始的符号集合 */

/* 名字表结构 */
struct tablestruct
{
    char name[al];      /* 名字 */
    enum object kind;   /* 类型:const, var, array or procedure */
    int val;            /* 数值,仅const使用 */
    int level;          /* 所处层,仅const不使用 */
    int adr;            /* 地址,仅const不使用 */
    int size;           /* 需要分配的数据区空间, 仅procedure使用 */
    int n;             /*参数个数*/
};

struct tablestruct table[txmax]; /* 名字表 */

FILE* fin;
FILE* fout;
char fname[al];
int err; /* 错误计数器 */

/* 当函数中会发生fatal error时,返回-1告知调用它的函数,最终退出程序 */
#define getsymdo                      if(-1 == getsym()) return -1
#define getchdo                       if(-1 == getch()) return -1
#define testdo(a, b, c)               if(-1 == test(a, b, c)) return -1
#define gendo(a, b, c)                if(-1 == gen(a, b, c)) return -1
#define expressiondo(a, b, c)         if(-1 == expression(a, b, c)) return -1
#define factordo(a, b, c)             if(-1 == factor(a, b, c)) return -1
#define termdo(a, b, c)               if(-1 == term(a, b, c)) return -1
#define conditiondo(a, b, c)          if(-1 == condition(a, b, c)) return -1
#define statementdo(a, b, c)          if(-1 == statement(a, b, c)) return -1
#define constdeclarationdo(a, b, c)   if(-1 == constdeclaration(a, b, c)) return -1
#define vardeclarationdo(a, b, c)     if(-1 == vardeclaration(a, b, c)) return -1

void error(int n);
int getsym();
int getch();
void init();
int gen(enum fct x, int y, int z);
int test(bool* s1, bool* s2, int n);
int inset(int e, bool* s);
int addset(bool* sr, bool* s1, bool* s2, int n);
int subset(bool* sr, bool* s1, bool* s2, int n);
int mulset(bool* sr, bool* s1, bool* s2, int n);
int block(int lev, int tx, bool* fsys);
void interpret();
int factor(bool* fsys, int* ptx, int lev);
int term(bool* fsys, int* ptx, int lev);
int condition(bool* fsys, int* ptx, int lev);
int expression(bool* fsys, int* ptx, int lev);
int statement(bool* fsys, int* ptx, int lev);
void listcode(int cx0);
int vardeclaration(int* ptx, int lev, int* pdx);
int constdeclaration(int* ptx, int lev, int* pdx);
int position(char* idt, int tx);
void enter(enum object k, int* ptx, int lev, int* pdx);
int base(int l, int* s, int b);
/*
 * PL/0 complier program for win32 platform (implemented in C)
 *
 * The program has been test on Visual C++ 6.0, Visual C++.NET and
 * Visual C++.NET 2003, on Win98, WinNT, Win2000, WinXP and Win2003
 *
 * 使用方法:
 * 运行后输入PL/0源程序文件?
 * 回答是否输出虚拟机代码
 * 回答是否输出名字表
 * fa.tmp输出虚拟机代码
 * fa1.tmp输出源文件及其各行对应的首地址
 * fa2.tmp输出结?
 * fas.tmp输出名字表
 */

#include <stdio.h>

#include "pl0.h"
#include "string.h"

/* 解释执行时使用的栈 */
#define stacksize 500


int main()
{
    bool nxtlev[symnum];

    printf("Input pl/0 file?   ");
    scanf("%s", fname);     /* 输入文件名 */

    fin = fopen(fname, "r");

    if (fin)
    {
        printf("List object code?(Y/N)");   /* 是否输出虚拟机代码 */
        scanf("%s", fname);
        listswitch = (fname[0]=='y' || fname[0]=='Y');

        printf("List symbol table?(Y/N)");  /* 是否输出名字表 */
        scanf("%s", fname);
        tableswitch = (fname[0]=='y' || fname[0]=='Y');

        fa1 = fopen("fa1.tmp", "w");
        fprintf(fa1,"Input pl/0 file?   ");
        fprintf(fa1,"%s\n",fname);

        init();     /* 初始化 */

        err = 0;
        cc = cx = ll = 0;
        ch = ' ';

        if(-1 != getsym())
        {
            fa = fopen("fa.tmp", "w");
            fas = fopen("fas.tmp", "w");
            addset(nxtlev, declbegsys, statbegsys, symnum);
            nxtlev[period] = true;

            if(-1 == block(0, 0, nxtlev))   /* 调用编译程序 */
            {
                fclose(fa);
                fclose(fa1);
                fclose(fas);
                fclose(fin);
                printf("\n");
                return 0;
            }
            fclose(fa);
            fclose(fa1);
            fclose(fas);

            if (sym != period)
            {
                error(9);
            }

            if (err == 0)
            {
                fa2 = fopen("fa2.tmp", "w");
                interpret();    /* 调用解释执行程序 */
                fclose(fa2);
            }
            else
            {
                printf("Errors in pl/0 program");
            }
        }

        fclose(fin);
    }
    else
    {
        printf("Can't open file!\n");
    }

    printf("\n");
    return 0;
}

/*
* 初始化
*/
void init()
{
    int i;

    /* 设置单字符符号 */
    for (i=0; i<=255; i++)
    {
        ssym[i] = nul;
    }
    ssym['+'] = plus;
    ssym['-'] = minus;
    ssym['*'] = times;
    ssym['/'] = slash;
    ssym['('] = lparen;
    ssym[
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值