C语言实现词法分析器

问题描述:

用C或C++语言编写一个简单的词法分析程序,扫描C语言小子集的源程序,根据给定的词法规则,识别单词,填写相应的表。如果产生词法错误,则显示错误信息、位置,并试图从错误中恢复。简单的恢复方法是忽略该字符(或单词)重新开始扫描。

相关词法规则

<标识符>::=<字母>
<标识符>::=<标识符><字母>
<标识符>::=<标识符><数字>
<常量>::=<无符号整数>
<无符号整数>::=<数字序列>
<数字序列>::=<数字序列><数字>
<数字序列>::=<数字>
<字母>::=a|b|c|……|x|y|z
<数字>::=0|1|2|3|4|5|6|7|8|9
<加法运算符>::=+|-
<乘法运算符>::=*|/
<关系运算符>::=<|>|!=|>=|<=|==
<分界符>::=,|;|(|)|{|}
<保留字>::=main|int|if|else|while|do

编写词法分析程序的步骤:

(1)确定所要翻译的语言(或其子集)。

C语言

(2)设计属性字,及各类表格,如标识符表、常量表、符号及其机内表示对照表等。

与词法分析有关的表格:

1. 字符表

保留字:main,int,if,else,while,do
字母(全小写):a|b|c|……|x|y|z
数字:0,1,2,3,4,5,6,7,8,9
运算符和界符:<,>,!=,>=,<=,==,,,;,(,),{,}

2. 特定单词机内表示表

3.画出总控流程图及各个子程序的流程图。

4. 程序

输入:一个存放C语言程序的s.txt文件
输出:存放以(单词,种别码)形式输出的result.txt文件

需要6个数组:

1. 存储关键字 key[6]
2. 存储对应下标关键字的种别码 keyNum[6]
3. 存储运算符和界符 symbol[17]
4. 存储运算符对应下标的种别码 symbolNum[17]
5. 存储从文件中取出的每个字符(不包括括号)letter[1000]

主要函数:

TakeWord();

功能:将文件letter[]中每个字符进行提取,找出关键字,输出种别码

Num作为全局变量保存提取到字符的哪个下标

1. 先提取一个字符,如果是字母,进入case1,调用identifier(),不断的提取字母或数字进行连接,没连接一个字符用int isKeyWord()程序(返回关键字种别码)判断是否为关键字,是就退出函数返回string,不是就继续执行函数,直到连接的字符不再是字母或数字,即此时字符串为标识符

2. 如果是数字,进入case 2,调用Number()函数,不断进行字符串连接,知道下一个连接字符不再是数字

3. 如果是符号,进入case 3,调用symbolStr()函数,如果是=,>,<,!,则要继续进行下个字符判断,其余符号可以直接返回

其他辅助函数:

int isSymbol()判断运算符和界符,并返回种别码
bool isNum() 判断是否为数字
bool isLetter()判断是否为字母
int isKeyWord()判断是否为关键字,是返回种别码
int typeword()返回单个字符的类型
string identifier()标识符的连接
string symbolStr()符号和界符的连接
string Number()数字的连接
void print()输出

程序:

#include <iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
using namespace std;

//关键字
string key[6]={"main","int","if","else","while","do"};
//关键字的种别码
int keyNum[6]={1,2,3,4,5,6};
//运算符和界符
string symbol[17]={"<",">","!=",">=","<=","==",",",";","(",")","{","}","+","-","*","/","="};
//char symbol[12]={'<','>','!=','>=','<=','==',',',';','(',')','{','}'};
//运算符和界符的种别码
int symbolNum[17]={7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23};
//存放文件取出的字符
string letter[1000];
//将字符转换为单词
string words[1000];
int length; //保存程序中字符的数目
int num;

int isSymbol(string s){ //判断运算符和界符
 int i;
 for(i=0;i<17;i++){
  if(s==symbol[i])
   return symbolNum[i];
 }
 return 0;
} 

//判断是否为数字
bool isNum(string s){
 if(s>="0" && s<="9")
  return true;
 return false;
}

//判断是否为字母
bool isLetter(string s)
{
 if(s>="a" && s<="z")
  return true;
 return false;
}

//判断是否为关键字,是返回种别码
int isKeyWord(string s){
 int i;
 for(i=0;i<6;i++){
  if(s==key[i])
   return keyNum[i];
 }
 return 0;
}

//返回单个字符的类型
int typeword(string str){
 if(str>="a" && str<="z") // 字母
  return 1;

 if(str>="0" && str<="9") //数字
  return 2;

 if(str==">"||str=="="||str=="<"||str=="!"||str==","||str==";"||str=="("||str==")"||str=="{"||str=="}"
  ||str=="+"||str=="-"||str=="*"||str=="/") //判断运算符和界符
  return 3; 

}

string identifier(string s,int n){
 int j=n+1;
 int flag=1;

 while(flag){
  if(isNum(letter[j]) || isLetter(letter[j])){
   s=(s+letter[j]).c_str();
   if(isKeyWord(s)){
    j++;
    num=j;
    return s;
   }
   j++;
  }
  else{
   flag=0;
  }
 } 

 num=j;
 return s;
}

string symbolStr(string s,int n){
 int j=n+1;
 string str=letter[j];
 if(str==">"||str=="="||str=="<"||str=="!") {
  s=(s+letter[j]).c_str();
  j++;
 }
 num=j;
 return s;
}

string Number(string s,int n){
 int j=n+1;
 int flag=1;

 while(flag){
  if(isNum(letter[j])){
   s=(s+letter[j]).c_str();
   j++;
  }
  else{
   flag=0;
  }
 }

 num=j;
 return s;
}

void print(string s,int n){
 cout<<"("<<s<<","<<n<<")"<<endl;
}

void TakeWord(){ //取单词
 int k;

 for(num=0;num<length;){
  string str1,str;
  str=letter[num];
  k=typeword(str);
  switch(k){
   case 1:
    {
     str1=identifier(str,num);
     if(isKeyWord(str1))
      print(str1,isKeyWord(str1));
     else
      print(str1,0);
     break;
    }

   case 2:
    {
     str1=Number(str,num);
     print(str1,24);
     break;
    }

   case 3:
    {
     str1=symbolStr(str,num);
     print(str1,isSymbol(str1));
     break;
    }

  }

 }
}

int main(){
 char w;
 int i,j;

 freopen("s.txt","r",stdin);
 freopen("result.txt","w",stdout); //从控制台输出,而不是文本输出

 length=0;
 while(cin>>w){
  if(w!=' '){
   letter[length]=w;
   length++;
  } //去掉程序中的空格
 }

 TakeWord();
// for(j=0;j<length;j++){
//  cout<<letter[j]<<endl;
// } 

 fclose(stdin);//关闭文件
 fclose(stdout);//关闭文件
 return 0;
} 

运行结果:

s.txt

Result.txt

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 判断两颗二叉树是否相似的两种方法

    名称:判断两个二叉树是否相似 说明:此处的两个方法一个是非递归,一个是递归算法.其实两个算法的本质思路是一样的就是,判断位置相同的两个结点是否同时为空或同时不为空.只是具体的实现不一样. 对于层次遍历法:此处不小心用错了,本应该用队列来当作排列下一层元素的.歪打正着,此处用栈也可以,只是判断的结点顺序不一样.队列的话,是从每一层的左端到右端.栈的话,是从右端到左端.在此处都没影响.我去,有发现一点,要从右到左访问一层的元素的话,应该用栈. 对于递归,看起来比非递归要简单不少.基本的思路很简单,要

  • C语言项目全正整数后再计算的三种参考解答方法

    [项目-全正整数后再计算] 输入3个正整数,其中任一数不是正整数,程序输出Invalid number!,然后结束运行.当第1个数为奇数时,计算后两数之和,当第1个数为偶数时,计算第2数减去第3数的差.无论哪种情形,当结果超过10时按如下示例输出,否则什么也不输出. 示例 1: Enter number 1: 2 Enter number 2: -7 Invalid number! 示例2: Enter number 1: 17 Enter number 2: 3 Enter number 3:

  • C语言项目爬楼梯的两种实现方法参考

    [项目-爬楼梯] 楼梯有n阶台阶,上楼可以一步上1阶,也可以一步上2阶,编一程序计算共有多少种不同的走法? [参考解答(递归法)] 基础:楼梯有一个台阶,只有一种走法(一步登上去):两个台阶,有2种走法(一步上去,或分两次上去): 递推:有n个台阶时,设有count(n)种走法,最后一步走1个台阶,有count(n-1)种走法:最后一步走2个台阶,有count(n-2)种走法.于是count(n)=count(n-1)+count(n-2). 可见,此问题的数学模型竟然是斐波那契数. #incl

  • C语言程序打豆豆(函数版)

    [项目] 设计一个程序,能重复地在显示下面的信息: 1. 吃饭 2. 睡觉 3. 打豆豆 0. 退出 请选择(0-3): 根据用户输入的选项,输出一句提示性的话语(将来会对应实现某个功能).输入0,则退出. 要求将各功能定义专门的函数. 参考解答: #include <stdio.h> #define EAT '1' #define SLEEP '2' #define HITDOUDOU '3' #define CRY '4' #define WITHDRAW '0' char getChoi

  • C语言数组a和&a的区别讲解

    面试经典题目 #include "stdio.h" int main() { int a[5] = { 1,2,3,4,5 }; int *ptr = (int *)(&a + 1); printf("%d,%d", *(a + 1), *(ptr - 1)); /*getchar是用VS编写方便查看输出*/ getchar(); return 0; } 请思考一下上面的输出结果,如果你非常自信了,可以不用往下看 题目剖析 这个题目主要考察&a 和 

  • C语言二维数组几种常用的表示方法

    名称:二维数组的几种表示方法 说明:常用的有以下几种二维数组的表示方法: (1).第一种是普通的二维数组的表示方法. (2).第二种是用一维数组来表示二维数组,从显示的元素地址可以看出,二维数组和一维数组表示的二维数组在内存中的储存方式其实是一样的,不过使用二维数组看起来要简单些,只要不用进行地址转换. (3).第三种表示是用指针数组.本例中的c[i]中的元素其实是地址.这种方法对各个元素大小不一样的情况下比较适用.如:假定有若干个不等长字符串需要我们处理,如果使用a [i ][j]结构,则j必

  • 剑指offer之C语言不修改数组找出重复的数字

    1  题目 不修改数组找出重复的数字 在一个长度为N+1的数组里面的所有数字都在范围1~N范围内,所以数组至少有一个数字是重复的,请找出重复数字,但是不能修改输入的数组. 2  思路 思路1: 我们开辟一个新的数组,初始化为0,然后把原始数组每个数据的值作为下标,把新数组通过这个下标数据取出来,如果取出来是1,就说明这个下标数据重复了,如果不是,我们直接放进去,然后进行新数组值进行++操作. 思路2: 比如数据1 2 2 3 4 5 6 7, 我们先找到中间的值(1 + 7) / 2 = 4;然

  • C++稀疏矩阵的各种基本运算并实现加法乘法

    代码: #include <iostream> #include<malloc.h> #include<cstdio> using namespace std; #define M 4 #define N 4 #define MaxSize 100 typedef int ElemType; typedef struct { int r; int c; ElemType d;///元素值 } TupNode; ///三元组定义 typedef struct { int

  • Dijkstra算法最短路径的C++实现与输出路径

    某个源点到其余各顶点的最短路径 这个算法最开始心里怕怕的,不知道为什么,花了好长时间弄懂了,也写了一遍,又遇到时还是出错了,今天再次写它,心里没那么怕了,耐心研究,懂了之后会好开心的,哈哈 Dijkstra算法: 图G 如图:若要求从顶点1到其余各顶点的最短路径,该咋求: 迪杰斯特拉提出"按最短路径长度递增的次序"产生最短路径. 首先,在所有的这些最短路径中,长度最短的这条路径必定只有一条弧,且它的权值是从源点出发的所有弧上权的最小值,例如:在图G中,从源点1出发有3条弧,其中以弧(1

  • C语言测试n的阶乘和x的n次方

    题目描述 输入一个正数x和一个正整数n,求下列算式的值.要求定义两个调用函数:fact(n)计算n的阶乘:mypow(x,n)计算x的n次幂(即xn),两个函数的返回值类型是double. ×输出保留4位小数. 输入 x n 输出 数列和 样例输入 2.0 3 样例输出 1.3333 答案 /************************************************************************* > File Name: 2.c > Author: &

随机推荐