PASCAL语言子集的词法、语法分析器之实现

针对简单的文法(PASCAL语言子集),制作相应的词法分析器和递归下降的语法分析器。

       文法要求如下:

1、  关键字、标识符、数字等:

1.begin         2.if             3.then         4.while       5.do            6.end          10.标识符           11.数字

13.+             14.-            15.*            16./             17.:             18.:=          20.<                     21.<>

22.<=          23.>           24.>=         25.=           26.;             27.(            28.)

2、  文法规则:

程序 → begin 语句串 end

语句串 → 语句 { ; 语句 }

语句 → 赋值语句 | 条件语句 | 循环语句

赋值语句 → 变量 := 表达式

条件语句 → if 条件 then ( 语句 | 程序 )

循环语句 → while 条件 do ( 语句 | 程序 )

表达式 → 项 { + 项 | - 项 }

条件 → 表达式 关系符 表达式

关系符 → < | <> | <= | > | >= | =

项 → 因子 { * 因子 | / 因子 }

因子 → 变量 | 数字 | ( 表达式 )

变量 → 标识符


一、  词法分析器

词法分析器的任务是清除源文件中多余的空格、换行、制表符等,识别文法符号。按顺序输出识别的标识符及其种别编号,供语法分析器调用。

 

代码如下:

 

#include<stdio.h>

#include<string.h>

#include<stdlib.h>

#define BOOL int

#define TRUE 1

#define FALSE 0

#define MAXSIZE 50

 

 

typedef char datatype;

 

typedef struct                              /*定义缓冲队列*/

{

  datatype data[MAXSIZE*2];

  int front,rear;

}Queue;

void setnull(Queue *q)                      /*队列初始化*/

{

  q->front = MAXSIZE*2 - 1;

  q->rear = MAXSIZE*2 - 1;

}

BOOL empty(Queue *q)                   /*判队空*/

{

  if(q->front==q->rear)

       return TRUE;

  return FALSE;

}

BOOL full(Queue *q)                         /*判队满*/

{

  if(q->front == (q->rear+1) % (MAXSIZE*2))

       return TRUE;

  return FALSE;

}

int quantity(Queue *q)                      /*求队列中元素个数*/

{

  int n;

  n = q->rear - q->front;

    if(n<0)

       n += MAXSIZE*2;

  return n;

}

datatype front(Queue *q)               /*取队头元素*/

{

  int n;

  if(empty(q))

       return 0;

  n = (q->front+1) % (MAXSIZE*2);

  return q->data[n];

}

BOOL enqueue(Queue *q,datatype x)      /*入队*/

{

  if(full(q))

       return FALSE;

  q->rear = ++q->rear % (MAXSIZE*2);

  q->data[q->rear]=x;

  return TRUE;

}

datatype dequeue(Queue *q)                  /*出队*/

{

  if(empty(q))

       return 0;

  q->front = ++q->front % (MAXSIZE*2);

  return q->data[q->front];

}

 

char token[MAXSIZE];

char* rwtab[6]={"begin","if","then","while","do","end"};

int syn;

Queue prog;

 

BOOL letter(char ch)                   /*判断是否字母*/

{

  if(ch>='a'&&ch<='z' || ch>='A'&&ch<='Z')

       return TRUE;

  return FALSE;

}

BOOL digit(char ch)                         /*判断是否数字*/

{

  if(ch>='0'&&ch<='9')

       return TRUE;

  return FALSE;

}

void saner()                                /*扫描器*/

{

  int i;

  char ch;

  for(i=0;i<50;i++)

       token[i]=0;

  i=0;

  do                                        /*去除多余空格、换行及制表符*/

  {

       ch=dequeue(&prog);

  }while(ch==' ' || ch=='\n' || ch=='\t');

  if(letter(ch))                            /*识别标识符(编号10)*/

  {

       while(1)

       {

           token[i++]=ch;

           ch=front(&prog);

           if(letter(ch) || digit(ch))

                dequeue(&prog);

           else

                break;

       }

       token[i]='\0';

       syn=10;

       for(i=0;i<6;i++)

           if(!strcmp(token,rwtab[i]))

                syn=i+1;                    /*识别关键字(编号1到6)*/

  }

  else if(digit(ch))                   /*识别无符号整数(编号11)*/

  {

       while(1)

       {

           token[i++]=ch;

           ch=front(&prog);

           if(digit(ch))

                dequeue(&prog);

           else

                break;

       }

       token[i]='\0';

       syn=11;

  }

  else

       switch(ch)

       {

       case '#':                        /*识别结束符‘#’(编号0)*/

           syn=0;

           token[i++]='#';

           token[i]='\0';

           break;

       case '+':                        /*识别‘+’(编号13)*/

           syn=13;

           token[i++]='+';

           token[i]='\0';

           break;

       case '-':                        /*识别‘-’(编号14)*/

           syn=14;

           token[i++]='-';

           token[i]='\0';

           break;

       case '*':                        /*识别‘*’(编号15)*/

           syn=15;

           token[i++]='*';

           token[i]='\0';

           break;

       case '/':                        /*识别‘/’(编号16)*/

           syn=16;

           token[i++]='/';

           token[i]='\0';

           break;

       case ':':

           token[i++]=':';

           ch=front(&prog);

           switch(ch)

           {

           case '=':                   /*识别‘:=’(编号18)*/

                syn=18;

                token[i++]='=';

                token[i]='\0';

                dequeue(&prog);

                break;

           default:                    /*识别‘:’(编号17)*/

                syn=17;

                token[i]='\0';

                break;

           }

           break;

       case '<':

           token[i++]='<';

           ch=front(&prog);

           switch(ch)

           {

           case '>':                   /*识别‘<>’(编号21)*/

                syn=21;

                token[i++]='>';

                token[i]='\0';

                dequeue(&prog);

                break;

           case '=':                   /*识别‘<=’(编号22)*/

                syn=22;

                token[i++]='=';

                token[i]='\0';

                dequeue(&prog);

                break;

           default:                    /*识别‘<’(编号20)*/

                syn=20;

                token[i]='\0';

                break;

           }

           break;

       case '>':

           token[i++]='>';

           ch=front(&prog);

           switch(ch)

           {

           case '=':                   /*识别‘>=’(编号24)*/

                syn=24;

                token[i++]='=';

                token[i]='\0';

                dequeue(&prog);

                break;

           default:                    /*识别‘>’(编号23)*/

                syn=23;

                token[i]='\0';

                break;

           }

           break;

       case '=':                        /*识别‘=’(编号25)*/

           syn=25;

           token[i++]='=';

           token[i]='\0';

           break;

       case ';':                        /*识别‘;’(编号26)*/

           syn=26;

           token[i++]=';';

           token[i]='\0';

           break;

       case '(':                        /*识别‘(’(编号27)*/

           syn=27;

           token[i++]='(';

           token[i]='\0';

           break;

       case ')':                        /*识别‘)’(编号28)*/

           syn=28;

           token[i++]=')';

           token[i]='\0';

           break;

       default:                         /*出错!*/

           syn=-1;

           break;

       }

}

 

main(int argc,char* argv[])

{

  FILE *in,*out;

  int i;

  char ch;

  const char ofname[]="scaned.txt";

  setnull(&prog);                           /*缓冲队列初始化*/

  switch(argc)

  {

  case 2:

       if(!(in=fopen(argv[1],"r")))

       {

           printf("The file is not exist!");

           exit(1);

       }

       out=fopen(ofname,"w");

       break;

  case 3:

       if(!(in=fopen(argv[1],"r")))

       {

           printf("The file is not exist!");

           exit(1);

       }

       out=fopen(argv[2],"w");

       break;

  }

  do

  {

       switch(argc)

       {

       case 1:

           do

           {

                ch=getchar();

                enqueue(&prog,ch);

           }while(ch!='#' && !full(&prog));

           if(!(out=fopen(ofname,"a")))

                out=fopen(ofname,"w");

           break;

       case 2:

           do

           {

                ch=fgetc(in);

                enqueue(&prog,ch);

           }while(ch!='#' && !full(&prog));         

           if(ch=='#')

                fclose(in);

           break;

       case 3:

           do

           {

                ch=fgetc(in);

                enqueue(&prog,ch);

           }while(ch!='#' && !full(&prog));         

           if(ch=='#')

                fclose(in);

           break;

       default:

           printf("Input error!!");

           break;

       }

       do

       {

           saner();

           switch(syn)

           {

           case 0:

                fputc('#',out);

                fputc(',',out);

                fputc('0',out);

                fputc('\n',out);

                break;

           case -1:

                fprintf(out,"Error!\n");

                break;

           default:

                i=0;

                do

                {

                     fputc(token[i++],out);

                }while(token[i]!='\0');

                fputc(',',out);

                i=syn/10;

                if(i!=0)

                     fputc(i+48,out);

                fputc(syn%10+48,out);

                fputc('\n',out);

                break;

           }

       }while(syn!=0 && (quantity(&prog) > MAXSIZE || ch=='#'));

  }while(ch!='#');

  fclose(out);

}

 

 
二、  语法分析器

语法分析器的任务是根据词法分析的结果判断是否符合文法规则,并以一定形式输出语法树。(这里按逆波兰式输出。其中符号“!”代表条件语句运算符;符号“@”代表循环语句运算符)

代码如下:

 

#include<stdio.h>

#include<stdlib.h>

#include<conio.h>

#define BOOL int

#define TRUE 1

#define FALSE 0

#define MAXSIZE 50

 

typedef struct

{

    int no;

    char str[MAXSIZE];

}Element;

 

Element ch;

FILE *in,*out;

 

void scan();      /* 扫描 */

void error(int error); /* 报错 */

void P();         /* 程序 */         /* P → begin S end */

void S();         /* 语句串 */       /* S → SS { ; SS } */

void SS();        /* 语句 */         /* SS → S1 | S2 | S3 */

void S1();        /* 赋值语句 */     /* S1 → V := E */

void S2();        /* 条件语句 */     /* S2 → if SS2 then ( SS | P ) */

void S3();        /* 循环语句 */     /* S3 → while SS2 do ( SS | P ) */

void E();         /* 表达式 */       /* E → T { + T | - T } */

void SS2();       /* 条件 */         /* SS2 → E R E */

void R();         /* 关系符 */       /* R → < | <> | <= | > | >= | = */

void T();         /* 项 */           /* T → F { * F | / F } */

void F();         /* 因子 */         /* F → V | N | ( E ) */

void V();         /* 变量 */         /* V → W */

void W();         /* 标识符 */

void N();         /* 数字 */

 

void scan()

{

    char buffer;

    int i=0;

    int temp=0;

    do

    {

         buffer=fgetc(in);

         ch.str[i++]=buffer;

    }while(buffer!=',');

    while(TRUE)

    {

         buffer=fgetc(in);

         if(buffer!='\n')

         {

             temp=10*temp+buffer-48;

         }

         else

             break;

    }

    ch.no = temp;

    ch.str[--i]='\0';

}

 

void error(int n)

{

    switch(n)

    {

    case 1:

         printf("标识符begin错误!");

         break;

    case 2:

         printf("标识符if错误!");

         break;

    case 3:

         printf("标识符then错误!");

         break;

    case 4:

         printf("标识符while错误!");

         break;

    case 5:

         printf("标识符do错误!");

         break;

    case 6:

         printf("标识符end错误!");

         break;

    case 7:

         printf("不是有效的句子!");

         break;

    case 8:

         printf("判断语句出错!");

         break;

    case 9:

         printf("循环语句出错!");

         break;

    case 10:

         printf("不是正确的标识符!");

         break;

    case 11:

         printf("条件表达式错误!");

         break;

    case 12:

         printf("算术表达式错误!");

         break;

    case 18:

         printf("赋值语句错误!");

         break;

    case 28:

         printf("缺少')'!");

         break;

    default:

         printf("Compile failed!");

         break;

    }

    getch();

    exit(1);

}

 

void P()

{

    if(ch.no==1)

    {

         scan();

         S();

         if(ch.no==6)

             scan();

         else

             error(6);

    }

    else

         error(1);

}

 

void S()

{

    SS();

    while(ch.no==26)

    {

         scan();

         SS();

    }

}

 

void SS()

{

    switch(ch.no)

    {

    case 10:

         S1();

         break;

    case 2:

         S2();

         break;

    case 4:

         S3();

         break;

    default:

         error(7);

         break;

    }

}

 

void S1()

{

    V();

    if(ch.no==18)

    {

         scan();

         E();

         fputc(':',out);

         fputc('=',out);

         fputc(' ',out);

    }

    else

         error(18);

}

 

void S2()

{

    if(ch.no==2)

    {

         scan();

         SS2();

         if(ch.no==3)

         {

             scan();

             switch(ch.no)

             {

             case 10:

             case 2:

             case 4:

                  SS();

                  break;

             case 1:

                  P();

                  break;

             default:

                  error(8);

                  break;

             }

             fputc('!',out);

             fputc(' ',out);

         }

         else

             error(3);

    }

    else

         error(2);

}

 

void S3()

{

    if(ch.no==4)

    {

         scan();

         SS2();

         if(ch.no==5)

         {

             scan();

             switch(ch.no)

             {

             case 10:

             case 2:

             case 4:

                  SS();

                  break;

             case 1:

                  P();

                  break;

             default:

                  error(9);

                  break;

             }

             fputc('@',out);

             fputc(' ',out);

         }

         else

             error(5);

    }

    else

         error(4);

}

 

void V()

{

    W();

}

 

void E()

{

    T();

    while(ch.no==13)

    {

         scan();

         T();

         fputc('+',out);

         fputc(' ',out);

    }

    while(ch.no==14)

    {

         scan();

         T();

         fputc('-',out);

         fputc(' ',out);

    }

}

 

void SS2()

{

    int temp;

    E();

    temp=ch.no;

    R();

    E();

    switch(temp)

    {

    case 20:

         fputc('<',out);

         fputc(' ',out);

         break;

    case 21:

         fputc('<',out);

         fputc('>',out);

         fputc(' ',out);

         break;

    case 22:

         fputc('<',out);

         fputc('=',out);

         fputc(' ',out);

         break;

    case 23:

         fputc('>',out);

         fputc(' ',out);

         break;

    case 24:

         fputc('>',out);

         fputc('=',out);

         fputc(' ',out);

         break;

    case 25:

         fputc('=',out);

         fputc(' ',out);

         break;

    }

}

 

void R()

{

    switch(ch.no)

    {

    case 20:

         scan();

         break;

    case 21:

         scan();

         break;

    case 22:

         scan();

         break;

    case 23:

         scan();

         break;

    case 24:

         scan();

         break;

    case 25:

         scan();

         break;

    default:

         error(11);

         break;

    }

}

 

void W()

{

    int i=0;

    if(ch.no==10)

    {

         while(ch.str[i]!='\0')

             fputc(ch.str[i++],out);

         fputc(' ',out);

         scan();

    }

    else

         error(10);

}

 

void T()

{

    F();

    while(ch.no==15)

    {

         scan();

         F();

         fputc('*',out);

         fputc(' ',out);

    }

    while(ch.no==16)

    {

         scan();

         F();

         fputc('/',out);

         fputc(' ',out);

    }

}

 

void F()

{

    switch(ch.no)

    {

    case 10:

         V();

         break;

    case 11:

         N();

         break;

    case 27:

         scan();

         E();

         if(ch.no==28)

             scan();

         else

             error(28);

         break;

    default:

         error(12);

         break;

    }

}

 

void N()

{

    int i=0;

    if(ch.no==11)

    {

         while(ch.str[i]!='\0')

             fputc(ch.str[i++],out);

         fputc(' ',out);

         scan();

    }

    else

         error(12);

}

 

void main()

{

    const char* input="scaned.txt";

    const char* output="compiled.txt";

    if(!(in=fopen(input,"r")))

         error(-1);

    out=fopen(output,"w");

    scan();

    P();

    if(ch.no==0)

    {

         printf("Success!");

         fputc('#',out);

    }

    else

         error(-1);

    fclose(in);

    fclose(out);

    getch();

}

 

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
用C语言编写一个只包含部分保留字和单词符号(见语言子集L)的PASCAL语言的扫描器(词法分析器)。 1、该词法分析器的任务: (1)滤掉源程序中的无用成分,如空格; (2)输出分类记号供语法分析器使用,并产生两个表格:常数表和标识符表,表的数据结构和包含内容可参看书中第八章符号表; (3)识别非法输入,并将其标记为“出错记号”。 2、该词法分析器的功能: 以在下面段落中定义的PASCAL语言子集的源程序作为词法分析程序的输入数据。词法分析器打开该文件后,自文件头开始扫描源程序字符,发现符合“单词”定义的源程序字符串时,将它翻译成固定长度的单词内部表示,并查填适当的信息表,一旦发现不符合“单词”定义的源程序字符串时,给出错误提示。经过词法分析后,源程序字符串(源程序的外部表示)被翻译成具有等长信息的单词串(源程序的内部表示),并产生两个表格:常数表和标识符表,它们分别包含了源程序中的所有常数和所有标识符。 3、PASCAL语言子集L----保留字不区分大小写 L={ VAR,BEGIN,ELSE,END,IF,THEN,REAL,INTEGER,=,:=,-,+,*,; ,. }∪{常数}∪{标识符} 4、实验要求: (1)词法分析器构造:正规式设计、DFA设计、代码编写; (2)进行词法分析器的测试:测试例程(一小段程序)、测试结果与测试结果分析。 (3)实验报告格式自拟;实验报告中要对实验要求中正规式设计、DFA设计、代码编写、测试例程(3中定义的PASCAL语言子集的源程序段)、测试结果与测试结果分析逐项给予说明。 例子: 本例中单词符号(记号)的种类: 1、保留字; 2、分隔符; 3、运算符; 4、等符 5、常数; 6、标识符 (单词符号的分类可以自己规定,只是要在实验报告中给出说明) 测试例程PASCAL程序清单如下: BEGIN IF I=1 THEN ^ A:=I+1 ELSE *& IF I=2 THEN A:=I+11; END. # 运行词法分析程序后,显示如下结果如下: BEGIN (1,1) IF (1,4) I (6,0) = (4,2) 1 (5,0) THEN (1,5) ^ error, not a word A (6,1) := (2,2) I (6,0) + (3,0) 1 (5,0) ELSE (1,2) * (3,2) & error, not a word IF (1,4) I (6,0) = (4,2) 2 (5,1) THEN (1,5) A (6,1) := (2,2) I (6,0) + (3,0) 11 (5,2) ; (2,1) END (1,3) . (2,3) # error, not a word over 常数表CT:1,2 标识符表LT:I,A
⒈ 题目 编写前述PASCAL子集词法分析程序。 1)主程序设计考虑,(参阅后面给出的程序框架) 主程序的说明部分为各种表格和变量安排空间。 数组k为关键字表,每个数组元素存放一个关键字。采用定长的方式,较短的关键字后面补空格。 P 数组存放分界符。为了简单起见,分界符、算术运算符和关系运算符都放在p表中(学生编程时,应建立算术运算符表和关系运算符表,并且各有类号),合并成一类。 id 和ci 数组分别存放标识符和常数。 instring 数组为输入源程序的单词缓存。 outtoken 记录为输出内部表示缓存。 还有一些为造表填表设置的变量。 主程序开始后,先以人工方式输入关键字,造 k 表;再输入分界符等造 p 表。 主程序的工作部分设计成便于调试的循环结构。每个循环处理一个单词;接收键盘上送来的一个单词;调用词法分析过程;输出每个单词的内部码。 2)词法分析过程考虑 该过程取名为 lexical,它根据输入单词的第一个字符(有时还需读第二个字符),判断单词类,产生类号:以字符k表示关键字;i表示标识符;c 表示常数;p 表示分界符;s 表示运算符(学生编程时类号分别为1,2,3,4,5)。 对于标识符和常数,需分别与标识符表和常数表中已登记的元素相比较,如表中已有该元素,则记录其在表中的位置,如未出现过,将标识符按顺序填入数组 id 中,将常数变为二进制形式存入数组中 ci 中,并记录其在表中的位置。 lexical 过程中嵌有两个小过程:一个名为 getchar,其功能为从 instring 中按顺序取出一个字符,并将其指针 pint 加 1 ;另一个名为 error,当出现错误时,调用这个过程,输出错误编号。 将词法分析程序设计成独(入口)立一遍扫描源程序的结构。其流程图见图5-1。 图5-1 词法分析程序流程图 ⒉ 要求 ⑴ 所有识别出的单词都用两个字节的等长表示,称为内部码。第一个字节为 t ,第二个字节为 i 。 t 为单词的种类。关键字的 t=1;分界符的 t=2;算术运算符的 t=3;关系运算符的 t=4;无符号数的 t=5;标识符的 t=6。i 为该单词在各自表中的指针或内部码值。表 5-1 为关键字表;表 5-2 为分界符表;表 5-3 为算术运算符的 i 值;表 5-4 为关系运算符的 i 值。 表5-1 关键字表 表5-2 分界符表 指针1 关键字 指针1 分界符 0 BEGIN 0 , 1 DO 1 ; 2 ELSE 2 . 3 END 3 := 4 IF 4 ( 5 THEN 5 ) 6 VAR 7 WHILE 表5-3 算术运算符 表5-4 关系运算符 i 值 算术运算符 i 值 关系运算符 00H < 10H + 01H 21H / 04H >= 05H 常数表和标识符表是在编译过程中建立起来的。其 i 值是根据它们在源程序中出现的顺序确定的。 ⑵ 常数分析程序、关键字和标识符分析程序、其他单词分析程序请参阅范例自行设计。 ⑶ 本实践题可通过扩充下面给出的程序框架完成。 PROGRAM plexical(input,output); LABEL l; CONST keylen=10; identlen=10; TYPE //定义的类型 tstring=ARRAY[1..identlen] OF char; outreco=RECORD//记录为输出内部表示缓存。 ty: char; point: integer; END; {outreco} VAR cip,ip,pint,i,j,l,m,errorx:integer; charl:CHAR; ci:ARRAY[1..10] OF integer; k,id:ARRAY[1..keylen] OF tstring; token:tstring; //标志符 outtoken:outreco; instring:ARRAY[1..10]OF char; p:ARRAY[1..16] OF ARRAY [1..2] OF char; PROCEDURE lexical; VAR l,m,num:integer; b: boolean; PROCEDURE

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值