编译原理头歌实验:实验1《词法分析程序设计与实现》(C语言版)

任务描述

本关任务:加深对词法分析器的工作过程的理解;加强对词法分析方法的掌握;能够采用一种编程语言实现简单的词法分析程序;能够使用自己编写的分析程序对简单的程序段进行词法分析。

相关知识

为了完成本关任务,你需要掌握:词法分析程序设计与实现。

词法分析的基本知识

词法分析程序( Lexical analyzer,简称 Lexer ),负责从左到右逐个字符地对源程序进行扫描和分解,根据语言的词法规则识别出一个个的单词符号。

因此一个词法分析程序应具备如下功能:

  • 从左至右扫描构成源程序的字符流

  • 识别出有词法意义的单词

  • 返回单词记录,或词法错误信息

由上可知词法分析中的一个重要环节为识别单词符号类型,为了便于语法分析,通常将单词符号分为五类。

  • 标识符 用来命名程序中出现的变量、数组、函数、过程、标号等,通常是一个字母开头的字母数字串,如 length,nextch 等。

  • 基本字 也可以成为关键字或保留字。如 if,while,for,do,goto 等。他们具有标识符的形式,但他们不是由用户而是由语言定义的,其意义是约定的。多数语言中规定,他们不能作为标识符或者标识符的前缀,即用户不能使用它们来定义用户使用的名字,故我们称它为保留字,这些语言如 Pascal 和 C 等。但也有的语言允许将基本字作为标识符或者标识符的前缀,这类语言如 Fortran 等。

  • 常数 包括各种类型的常数,如整型、实型、字符型、布尔型等。如:53.1415926aTRUE等都是常数。

  • 运算符 算术运算符+-×÷;关系运算符<,<=,>,>=,==,!=以及逻辑运算符&&()||或者!等。

  • 界符 如等单字界符和/,/,//等双字界符,空白符等。

在进行词法分析后,识别出来的单词应该采用某种中间表示形式,以便为编译后续阶段方便地引用。通常一个单词用一个二元式来表示: (单词类别,单词的属性) 其中,第一元用于区分单词所属的类别,以整数编码表示。第二元用于区分该类别中的哪一个单词符号,即单词符号的值。

实验步骤

由一个词法分析程序应具备的功能来看,我们的程序具有如下要求:

  • 对单词的构词规则有明确的定义;
  • 编写的分析程序能够正确识别源程序中的单词符号;
  • 识别出的单词以<种别码,值>的形式保存在符号表中,正确设计和维护符号表;
  • 对于源程序中的词法错误,能够做出简单的错误处理,给出简单的错误提示,保证顺利完成整个源程序的词法分析;

下面我们进行实验代码的步骤分析。

  1. 定义目标语言的可用符号表和构词规则。
    我们需要对五种单词符号进行识别分析,这里将单词符号分为三大块进行识别。首先判断字符是否为关键字或者标识符,并与已定义好的关键字进行比较,从而判断为关键字或者标识符;然后是数字的识别;最后是其他字符的判断,它们被一一定义好的判断进行识别,这样所有的字符便被识别出来了。标示符和关键字的判断

     
      
    1. if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) //可能是标示符或者关键字

    字符与关键字的区别通过对比得出:

     
      
    1. if (strcmp(token, rwtab1[n]) == 0)
    2. {
    3. syn = 2;
    4. break;
    5. }
    6. else if (strcmp(token, rwtab[n]) == 0) {
    7. syn = 1;
    8. break;
    9. }
    10. }

    对于数字的识别:

     
      
    1. else if ((ch >= '0' && ch <= '9')) //数字

    其他字符的识别,他们被一一定义进行识别:

     
      
    1. else switch (ch) //其他字符
    2. {
    3. case'<':m = 0; token[m++] = ch;
    4. ch = prog[p++];
    5. if (ch == '>')
    6. {
    7. syn = 4;
    8. token[m++] = ch;
    9. }
    10. else if (ch == '=')
    11. {
    12. syn = 4;
    13. token[m++] = ch;
    14. }
    15. else
    16. {
    17. syn = 4;
    18. p--;
    19. }
    20. break;
    21. case'>':m = 0; token[m++] = ch;
    22. ch = prog[p++];
    23. if (ch == '=')
    24. {
    25. syn = 4;
    26. token[m++] = ch;
    27. }
    28. else
    29. {
    30. syn = 4;
    31. p--;
    32. }
    33. break;
    34. case':':m = 0; token[m++] = ch;
    35. ch = prog[p++];
    36. if (ch == '=')
    37. {
    38. syn = 4;
    39. token[m++] = ch;
    40. }
    41. else
    42. {
    43. syn = 4;
    44. p--;
    45. }
    46. break;
    47. case'*':syn = 4; token[0] = ch; break;
    48. case'/':syn = 4; token[0] = ch; break;
    49. case'+':syn = 4; token[0] = ch; break;
    50. case'-':syn = 4; token[0] = ch; break;
    51. case'=':syn = 4; token[0] = ch; break;
    52. case';':syn = 5; token[0] = ch; break;
    53. case',':syn = 5; token[0] = ch; break;
    54. case'(':syn = 5; token[0] = ch; break;
    55. case')':syn = 5; token[0] = ch; break;
    56. case'{':syn = 5; token[0] = ch; break;
    57. case'}':syn = 5; token[0] = ch; break;
    58. case'#':syn = 0; token[0] = ch; break;
    59. case'\n':syn = -2; break;
    60. default: syn = -1; break;
    61. }
    62. }
  2. 依次读入源程序符号,对源程序进行单词切分和识别,直到源程序结束。
    字符的输入我们使用cin.get() 获取,并切分保存在 prog中:

     
      
    1. p = 0;
    2. row = 1;
    3. cout << "Please input string:" << endl;
    4. do
    5. {
    6. cin.get(ch);
    7. prog[p++] = ch;
    8. } while (ch != '#');
    9. p = 0;
  3. 对正确的单词,按照它的种别以<种别码,值>的形式保存在符号表中;

  4. 对不正确的单词,做出错误处理。 单词识别后,我们对返回的符号按3,4的规则进行输出:

     
      
    1. {
    2. scaner();
    3. switch (syn)
    4. {
    5. case 0: break;
    6. case 3: cout << "(" << syn << "," << sum << ")" << endl; break;
    7. case -1: cout << "Error in row " << row << "!" << endl; break;
    8. case -2: row = row++; break;
    9. default: cout << "(" << syn << "," << token << ")" << endl; break;
    10. }
    11. } while (syn != 0);

编程要求

根据提示,在右侧编辑器补充代码标示符、数字符及其他字符符号的识别程序后,点击评测运行程序,系统会自动进行结果对比。

测试说明

平台会对你编写的代码进行测试:

测试输入:

 
  1. using namespace std;
  2. int main()
  3. {
  4. int year;
  5. cout << "hello" << endl;
  6. return 0;
  7. }
  8. #

代码:

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <stack>
#include <iomanip>
using namespace std;
string V[100][2]; //存储拆分后的文法
int vi = 0; //存储拆分后有多少行
int t = 0;
int s = 0;
int l;
int r;
string FIRSTVT[20][2]; //存储firstvt集
string LASTVT[20][2]; //存储lastvt集
string str; //存储输入文法
string str_a = "#"; // 下堆栈
string str_b; // 剩余输入串
string analysis_table[100][5]; // 文法分析过程
char table[100][100]; // 算符优先关系表
void init_string(string &a) // 删除字符串的第一个元素
{
    for (int i = 1; i <= a.length(); ++i)
    {
        a[i - 1] = a[i];
    }
}
bool is_CHAR(char c) // 判断是否为大写字母
{
    if (c >= 'A' && c <= 'Z')
    {
        return true;
    }
    else
    {
        return false;
    }
}
bool is_in(int i, string x) // 判断从字符串x从最好一个开始算起连续的i个字符是否含有非大写字母
{
    bool flag = false;
    for (int j = 0; j < i; j++)
    {
        if (!is_CHAR(x[x.length() - j - 1]))
        {
            flag = true;
        }
    }
    return flag;
}
void split(string a) // 拆分文法,使其不含有|
{
    for (int i = 3; i < a.length(); ++i)
    {
        V[vi][0] = a[0];
        while (a[i] != '|' && i < a.length())
        {
            V[vi][1] += a[i];
            i++;
        }
        vi++;
    }
}
void read_file(string file_path) //按行读取文件
{
    fstream f(file_path);
    vector<string> words;
    string line;
    while (getline(f, line))
    {
        words.push_back(line);
    }
    cout << "输入文法:" << endl;
    for (int i = 0; i < words.size(); i++)
    {
        cout << words[i] << endl;
        split(words[i]);
    }
}
int find_index(char a) //寻找字符a在firstvt或者lastvt中的位置
{
    for (int i = 0; i < t; ++i)
    {
        if (FIRSTVT[i][0][0] == a)
        {
            return i;
        }
    }
    return -1;
}
int find_table_index(char a) //寻找字符a在算符优先关系表中的位置
{
    for (int i = 0; i <= s; ++i)
    {
        if (table[i][0] == a)
        {
            return i;
        }
    }
    return -1;
}
void get_start() //获取非终结符
{
    for (int i = 0; i < vi; ++i)
    {
        bool flag = true;
        for (int j = 0; j < t; ++j)
        {
            if (FIRSTVT[j][0] == V[i][0])
            {
                flag = false;
            }
        }
        if (flag)
        {
            FIRSTVT[t][0] = V[i][0];
            LASTVT[t][0] = V[i][0];
            t++;
        }
    }
}
/********Beign********/
/*构造firstvt,lastvt*/
void add_firstvt(string b, int a) //判断字符串b是否在序号为a的firstvt中,没有则加入
{
    for(int i = 0;i < b.size();i++){
        bool flag = 1;
        char c = b[i];
        if(c >= 'A' && c <= 'Z'){
            continue;
        }
        for(int j = 0;j < FIRSTVT[a][1].size();j++){
            if(c == FIRSTVT[a][1][j]){
                flag = 0;
            }
        }
        if(flag){
            FIRSTVT[a][1] += c;
        }
    }
}
void add_firstvt(char c, int a) //判断字符c是否在序号为a的firstvt中,没有则加入
{
    bool flag = 1;
	for (int i = 0; i < FIRSTVT[a][1].size(); i++)
	{
		if (c >= 'A' && c <= 'Z'){
			continue;
		}
		if (c == FIRSTVT[a][1][i]){
			flag = 0;
		}
	}
	if (flag){
		FIRSTVT[a][1] += c;
	}
}
void add_lastvt(string b, int a) //判断字符串b是否在序号为a的lastvt中,没有则加入
{
    for(int i = 0;i < b.size();i++){
        bool flag = 1;
        char c = b[i];
        if(c >= 'A' && c <= 'Z'){
            continue;
        }
        for(int j = 0;j < LASTVT[a][1].size();j++){
            if(c == LASTVT[a][1][j]){
                flag = 0;
            }
        }
        if(flag){
            LASTVT[a][1] += c;
        }
    }
}
void add_lastvt(char c, int a) //判断字符串c是否在序号为a的lastvt中,没有则加入
{
    bool flag = 1;
	for (int i = 0; i < LASTVT[a][1].size(); i++)
	{
		if (c >= 'A' && c <= 'Z'){
			continue;
		}
		if (c == LASTVT[a][1][i]){
			flag = 0;
		}
	}
	if (flag){
		LASTVT[a][1] += c;
	}
}
string get_cur_firstvt(char c, int a) //获取当前字符的firstvt,并放入序号为a的firstvt中
{
    string temp;
	for (int i = 0; i < vi; i++)
	{
		if (c == V[i][0][0])
		{
			if (!(V[i][1][0] >= 'A' && V[i][1][0] <= 'Z'))
			{
				add_firstvt(V[i][1][0], a);
			}
			else
			{
				if (c != V[i][1][0])
				{
					temp = get_cur_firstvt(V[i][1][0], find_index(V[i][1][0]));
					add_firstvt(temp, a);
				}
				if (V[i][1].length() > 2)
				{
					if (!(V[i][1][1] <= 'Z' && V[i][1][1] >= 'A'))
					{
						add_firstvt(V[i][1][1], a);
					}
				}
			}
		}
	}
	return FIRSTVT[a][1];
}
string get_cur_lastvt(char c, int a) //获取当前字符的lastvt,并放入序号为a的lastvt中
{
    string temp;
	for (int i = 0; i < vi; ++i)
	{
		int s = V[i][1].length();
		if (c == V[i][0][0])
		{
			if (!(V[i][1][s - 1] <= 'Z' && V[i][1][s - 1] >= 'A'))
			{
				add_lastvt(V[i][1][s - 1], a);
			}
			else
			{
				if (c != V[i][1][s - 1])
				{
					temp = get_cur_lastvt(V[i][1][s - 1], find_index(V[i][1][0]));
					add_lastvt(temp, a);
				}
				if (V[i][1].length() > 2)
				{
					if (!(V[i][1][s - 2] <= 'Z' && V[i][1][s - 2] >= 'A'))
					{
						add_lastvt(V[i][1][s - 2], a);
					}
				}
			}
		}
	}
	return LASTVT[a][1];
}
/*********End*********/
void get_firstvt() //获取所有文法的firstvt
{
    for (int i = 0; i < t; i++)
    {
        get_cur_firstvt(FIRSTVT[i][0][0], i);
    }
}
void get_lastvt() //获取所有文法的lastvt
{
    for (int i = 0; i < t; i++)
    {
        get_cur_lastvt(LASTVT[i][0][0], i);
    }
}
void print_firstvt(string t, string a) //打印非终极符为t的firstvt
{
    cout << "FIRSTVT(" << t << ") = {";
    for (int i = 0; i < a.length(); ++i)
    {
        if (i == a.length() - 1)
        {
            cout << "\"" << a[i] << "\"";
        }
        else
        {
            cout << "\"" << a[i] << "\"" << ", ";
        }
    }
    cout << "}" << endl;
}
void print_lastvt(string t, string a) //打印非终结符为t的lastvt
{
    cout << "LASTVT(" << t << ") = {";
    for (int i = 0; i < a.length(); ++i)
    {
        if (i == a.length() - 1)
        {
            cout << "\"" << a[i] << "\"";
        }
        else
        {
            cout << "\"" << a[i] << "\"" << ", ";
        }
    }
    cout << "}" << endl;
}
/********Beign********/
/*构造算符优先表*/

void init_table() //初始化算符优先关系表
{
    table[0][0] = '\\';
	for (int i = 0; i < vi; ++i)
	{
		for (int j = 0; j < V[i][1].length(); ++j)
		{
			bool flag = true;
			for (int k = 0; k < s + 1; ++k)
			{
				if (table[k + 1][0] == V[i][1][j] || (V[i][1][j] <= 'Z' && V[i][1][j] >= 'A'))
				{
					flag = false;
				}
			}
			if (flag)
			{
				table[s + 1][0] = V[i][1][j];
				table[0][s + 1] = V[i][1][j];
				s++;
			}
		}
	}
	for (int l = 1; l < s + 1; ++l)
	{
		for (int i = 1; i < s + 1; ++i)
		{
			table[l][i] = ' ';
		}
	}
}

void get_table() //生成算符优先关系表
{
    for (int i = 0; i < vi; ++i)
	{
		for (int j = 0; j < V[i][1].length(); ++j)
		{
			//ab
			if (!(V[i][1][j] <= 'Z' && V[i][1][j] >= 'A') && !(V[i][1][j + 1] <= 'Z' && V[i][1][j + 1] >= 'A') &&
				j + 1 < V[i][1].length())
			{
				table[find_table_index(V[i][1][j])][find_table_index(V[i][1][j + 1])] = '=';
			}
			//aQb
			if ((!(V[i][1][j] <= 'Z' && V[i][1][j] >= 'A')) && (V[i][1][j + 1] <= 'Z' && V[i][1][j + 1] >= 'A') &&
				(!(V[i][1][j + 2] <= 'Z' && V[i][1][j + 2] >= 'A')) && j + 2 < V[i][1].length())
			{
				table[find_table_index(V[i][1][j])][find_table_index(V[i][1][j + 2])] = '=';
			}
			//aQ
			if ((!(V[i][1][j] <= 'Z' && V[i][1][j] >= 'A')) && (V[i][1][j + 1] <= 'Z' && V[i][1][j + 1] >= 'A') &&
				j + 1 < V[i][1].length())
			{
				for (int k = 0; k < FIRSTVT[find_index(V[i][1][j + 1])][1].length(); ++k)
				{
					table[find_table_index(V[i][1][j])][find_table_index(
							FIRSTVT[find_index(V[i][1][j + 1])][1][k])] = '<';
				}
			}
			//Qa
			if ((V[i][1][j] <= 'Z' && V[i][1][j] >= 'A') && !(V[i][1][j + 1] <= 'Z' && V[i][1][j + 1] >= 'A') &&
				j + 1 < V[i][1].length())
			{
				for (int k = 0; k < LASTVT[find_index(V[i][1][j])][1].length(); ++k)
				{
					table[find_table_index(LASTVT[find_index(V[i][1][j])][1][k])][find_table_index(
							V[i][1][j + 1])] = '>';
				}
			}
		}
	}
}
/*********End*********/
void print_table() //打印算符优先关系表
{
    for (int i = 0; i < s + 1; ++i)
    {
        for (int j = 0; j < s + 1; ++j)
        {
            cout << table[i][j] << " ";
        }
        cout << endl;
    }
}
char get_relationship(char a, char b) //获取终结符a,b的优先关系
{
    return table[find_table_index(a)][find_table_index(b)];
}
bool is_reduce() //判断是否可以规约
{
    for (int i = 0; i < vi; ++i)
    {
        int count = 0;
        int f = str_a.length() - 1;
        for (int j = V[i][1].length() - 1; j >= 0 && f >= 0; j--, f--)
        {
            if (is_in(V[i][1].length(), str_a))
            {
                if (is_CHAR(str_a[f]) && is_CHAR(V[i][1][j]))
                {
                    count++;
                }
                else if (str_a[f] == V[i][1][j])
                {
                    count++;
                }
            }
            else
            {
                continue;
            }
        }
        if (count == V[i][1].length())
        {
            r = i;
            return true;
        }
    }
    return false;
}
void analyze_input_string() // 生成算符优先文法的分析过程
{
    analysis_table[0][0] = "步骤";
    analysis_table[0][1] = "下堆栈";
    analysis_table[0][2] = "优先关系";
    analysis_table[0][3] = "剩余输入串";
    analysis_table[0][4] = "移进或规约";
    str_b = str;
    char relationship;
    l = 1;
    int x;
    stringstream ss;
    while (true)
    {
        ss << l;
        int index = str_a.length() - 1;
        analysis_table[l][0] = ss.str();
        analysis_table[l][3] = str_b;
        analysis_table[l][1] = str_a;
        ss.clear();
        ss.str("");
        if (is_CHAR(str_a[index]))
        {
            for (int i = str_a.length() - 1; i >= 0; i--)
            {
                if (!is_CHAR(str_a[i]))
                {
                    index = i;
                    break;
                }
            }
        }
        relationship = get_relationship(str_a[index], str_b[0]);
        analysis_table[l][2] = relationship;
        if (relationship == '=')
        {
            if (str_a[index] == '#' && str_b[0] == '#')
            {
                analysis_table[l][4] = "完成";
                break;
            }
            else
            {
                analysis_table[l][4] = "移进";
                str_a += str_b[0];
                analysis_table[l + 1][1] = str_a;
                init_string(str_b);
            }
        }
        else if (relationship == '<')
        {
            analysis_table[l][4] = "移进";
            str_a += str_b[0];
            analysis_table[l + 1][1] = str_a;
            init_string(str_b);
        }
        else if (relationship == '>')
        {
            if (is_reduce())
            {
                analysis_table[l][4] = "规约";
                str_a[str_a.length() - V[r][1].length()] = V[r][0][0];
                str_a.erase(str_a.length() - V[r][1].length() + 1, V[r][1].length() - 1);
            }
            else
            {
                cout << "输入串非法" << endl;
                exit(-1);
            }
        }
        l++;
    }
}
void print_analyze_process() //打印算符优先文法的分析过程
{
    cout << "算符优先分析过程" << endl;
    cout << setw(12) << analysis_table[0][0] << setw(16) << analysis_table[0][1] << setw(16) << analysis_table[0][2]
         << setw(24)
         << analysis_table[0][3] << setw(20)
         << analysis_table[0][4] << endl;
    for (int i = 1; i <= l; ++i)
    {
        cout.width(10);
        cout << analysis_table[i][0];
        cout.width(12);
        cout << analysis_table[i][1];
        cout.width(10);
        cout << analysis_table[i][2];
        cout.width(20);
        cout << analysis_table[i][3];
        cout << analysis_table[i][4];
        cout << endl;
    }
}
int main(int argv, char *arg[])
{
    cout.setf(std::ios::left);
    read_file("/data/workspace/myshixun/in.txt");
    cout << "拆分后文法:" << endl;
    for (int i = 0; i < vi; ++i)
    {
        cout << V[i][0] << "->" << V[i][1] << endl;
    }
    cout << "非终结符:" << endl;
    get_start();
    for (int j = 0; j < t; ++j)
    {
        cout << FIRSTVT[j][0] << endl;
    }
    cout << "FIRSTVT:" << endl;
    get_firstvt();
    for (int k = 0; k < t; ++k)
    {
        print_firstvt(FIRSTVT[k][0], FIRSTVT[k][1]);
    }
    cout << "LASTVT:" << endl;
    get_lastvt();
    for (int k = 0; k < t; ++k)
    {
        print_lastvt(LASTVT[k][0], LASTVT[k][1]);
    }
    cout << "算符优先关系表" << endl;
    init_table();
    get_table();
    print_table();
    cout << "请输入文法并以#结束:" << endl;
    cin >> str;
    analyze_input_string();
    print_analyze_process();
    return 0;
}

<think>好,我现在需要帮用户完成这个C语言词法分析器的代码。用户给出的代码框架中有三个部分需要补充:标识符或键字的识别、数字的识别,以及其他字符的识别。同时主函数中的输入和输出部分也需要完善。我得仔细分析每个部分应该怎么处理。 首先看scaner函数的标识符部分。用户提到,这里需要识别标识符或者键字。思路应该是这样的:当遇到的字符是字母或下划线时,继续读取后续的字母、数字或下划线,组成一个标识符。然后检查这个标识符是否是保留字,比如if、int等。如果是,就设置相应的syn值。比如syn可能是1到8对应不同的键字,而如果是用户定义的标识符,syn可能是10。那具体怎么实现呢?比如,循环读取字符,直到不是字母或数字为止,然后将tokenrwtab中的键字比较。如果匹配到,设置syn为对应的值,否则是标识符。 然后是数字的识别部分。这里需要处理整数和小数的情况吗?根据题目中的例子,可能只需要整数。所以当遇到数字时,继续读取后续的数字,组合成一个整数,然后sum保存这个数值,syn设置为20之类的。需要注意字符转数字的处理,比如ch - '0'。 接下来是其他字符的识别,比如运算符和界符。比如,当遇到+、-、*、/这些符号时,可能需要判断是否是单字符还是双字符的,比如++或者+=。例如,当遇到+时,需要查看下一个字符是否是+或者=,如果是,则组成双字符运算符,syn设为对应的值,比如++是22,而+=可能对应其他值。如果不是,就单独处理为+。同样,分界符如;、,、括号等需要处理。比如,如果是单个字符,直接设置syn对应的值。这部分可能需要多个if-else判断,处理不同的情况。 主函数部分,用户需要输入源代码,并循环调用scaner函数直到结束。输入部分可能需要读取多行,直到遇到某个结束符,比如#。或者根据代码中的处理,比如用gets或者fgets读取输入到prog数组中。然后p初始化为0,循环调用scaner,直到处理完整个prog数组。输出部分需要根据syn的值输出相应的单词种类和内容。比如,syn为10是标识符,输出对应的token;syn为20是数字,输出sum的值;其他符号则输出对应的符号名称。 现在,我需要具体考虑各个部分的代码实现。比如,在标识符处理部分,首先判断ch是否是字母或下划线,如果是,进入循环,将后续的字母数字或下划线加入token数组。然后比较token是否在rwtab或者rwtab1中。例如,遍历rwtab数组,如果找到匹配,则syn设置为n+1(比如rwtab[0]是if,对应syn=1),否则syn设为10。如果是用户定义的标识符,可能rwtab1中的处理类似,但用户提供的代码中可能没有这部分,需要看题目要求。 数字部分,当ch是数字时,进入循环,收集所有连续的数字字符,转换为整数存入sum。syn设置为20。比如,sum = sum *10 + (ch - '0'),然后继续读下一个字符。 其他字符部分,需要处理各种符号。例如: - +可能是+、++、+=的情况。先读入当前字符,然后预读下一个字符,判断是否能组成双字符符号。比如,如果当前是+,下一个也是+,则p++,syn设为22。否则如果是=,syn设为23,否则就是单独的+,syn设为21。 类似地处理其他符号,比如-、*、/、=、<、>等。此外,还需要处理括号、分号、逗号等单字符符号,比如(对应syn为26,)为27,{为28,}为29,,为30,;为31,[为32,]为33等。需要注意转义字符,比如双引号可能需要处理。 在编写这部分代码时,需要按照优先级处理双字符符号,比如先判断是否可能组成双字符符号,否则处理为单字符。 主函数中的输入可能需要用gets或者fgets读取字符串到prog数组,以#作为结束符?或者用户输入多行,然后以某种方式结束。比如,p初始化为0,然后循环处理每个字符直到遇到'\0'或者某个终止符。 输出部分,每次调用scaner后,根据syn的值输出对应的类型。例如: - syn 1-8是键字,输出对应的token; - syn 10是标识符,输出; - syn 20是数字,输出sum; - 其他syn对应不同的符号,比如+对应运算符等。 可能需要一个switch语句或者多个if条件来输出对应的信息。 现在,结合用户提供的代码框架,填充各部分的具体实现。 在标识符部分: if (ch是字母或下划线) { 循环读取字母、数字或下划线,组成token; 检查token是否是键字(比较rwtab和rwtab1); 设置syn的值; } 例如: if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_') { token[m++] = ch; while ((prog[p] >= 'a' && prog[p] <= 'z') || (prog[p] >= 'A' && prog[p] <= 'Z') || prog[p] >= '0' && prog[p] <= '9' || prog[p] == '_') { ch = prog[p++]; token[m++] = ch; } token[m] = '\0'; // 确保字符串结束 syn = 10; // 默认标识符 for (n = 0; n < 8; n++) { if (strcmp(token, rwtab[n]) == 0) { syn = n + 1; // syn从1开始对应键字 break; } if (strcmp(token, rwtab1[n]) == 0) { // 可能用户定义的键字? syn = n + 33; // 比如假设用户定义的从33开始 break; } } m = 0; // 重置token索引 } 不过用户提供的rwtab1数组可能代表用户定义的标识符?比如main和其他变量名?可能题目中的要求不同,需要看具体情况。比如,在用户代码中,可能rwtab1中的单词作为特定的标识符处理,syn不同。但原题中的scaner函数可能没有处理rwtab1的部分,需要确认题目要求。根据用户给出的代码框架,可能只需要处理rwtab中的键字,而其他标识符作为syn=10。所以可能rwtab1的部分可能不需要处理,或者可能用户需要将main作为键字?这需要看具体的题目要求。但用户提供的原始代码可能这部分未完成,可能需要根据常见的词法分析器设计来处理。 例如,在常见情况下,键字如if、int等是保留字,而main可能作为函数名,属于标识符。但原代码中的rwtab1可能用于其他用途,可能用户需要将main视为键字?或者可能原题中的rwtab和rwtab1是不同的键字列表。可能题目中的要求是,当token是rwtab中的单词时syn为1-8,如果是rwtab1中的则为9-16?或者可能rwtab1是用户定义的变量?这需要更多信息。但根据用户给出的代码,可能这部分需要自己判断。可能原题中的rwtab1并未在scaner中使用,可能用户需要将main视为键字,比如syn为9?或者可能原题中的scaner函数需要同时检查rwtab和rwtab1? 不过根据用户提供的代码框架,scaner函数中的第一个循环是: for (n=0;n<8;n++) token[n]=NULL; 这可能只是初始化token数组的前8个字符为NULL,但不够,因为token可能有更长的长度。可能需要用memset或类似方法清空整个数组。不过用户可能在循环前已经将token初始化为空。或者可能token的大小是20,足够处理标识符。 在标识符处理部分,用户可能需要将tokenrwtab中的键字比较。例如,如果是if,syn设为1,int是2,等等。而如果是main,可能属于rwtab1中的,所以需要比较这两个数组。例如: syn = 10; // 默认标识符 for (n=0;n<8;n++) { if (strcmp(token, rwtab[n]) == 0) { syn = n+1; break; } } if (syn == 10) { // 如果不在rwtab中,检查是否在rwtab1中 for (n=0;n<8;n++) { if (strcmp(token, rwtab1[n]) == 0) { syn = n + 9; // 假设rwtab1的syn从9开始 break; } } } 不过这可能因题目要求而异。原题可能需要将main视为键字吗?比如,可能原题中的示例输入输出中,main对应的syn值不同。假设题目要求将main视为键字,那么需要将rwtab1中的词作为另一组键字处理。但如果没有,可能不需要处理rwtab1,用户可能只需要处理rwtab中的键字,其余视为标识符。需要根据题目的测试用例来判断。例如,如果用户提供的代码中的测试输入中有main函数,那么可能需要处理main作为键字,否则可能不需要。这部分可能需要根据题目要求来调整。 假设在题目中,rwtab是键字,而rwtab1中的是系统预定义的函数名或变量名,或者可能不需要处理。可能用户在此处的代码中需要将main视为标识符。因此,在标识符处理部分,只需要比较rwtab中的键字。这可能更合理。因此,在代码中,当token是rwtab中的元素时,syn设为n+1(如1-8),否则设为10(标识符)。 接下来是数字识别部分: 如果当前字符是数字,那么收集所有连续的数字字符,转换为整数sum。例如: else if (ch >= '0' && ch <= '9') { sum = 0; while (ch >= '0' && ch <= '9') { sum = sum * 10 + (ch - '0'); ch = prog[p++]; } p--; // 因为循环中p已经自增,但最后一个字符不是数字,需要回退 syn = 20; } 这里需要注意,当循环结束时,p可能指向下一个非数字字符,因此需要p--,因为外层循环中的p会在scaner函数的最后再次自增吗?原scaner函数的结构中,每次调用scaner时,p会自增。例如,在初始时,ch = prog[p++],所以p已经指向下一个字符。在数字处理中,当读取完所有数字后,p可能已经指向下一个非数字字符,但此时需要将p减1,因为在处理完数字后,主循环中的p会在下一次scaner调用时再次自增。例如,假设当前字符是数字,进入处理: ch = prog[p++]; // 初始的ch是数字,p自增 然后进入循环: while (prog[p]是数字) { ch = prog[p++]; sum = sum *10 + ... } 但这样可能会导致p超过当前字符的位置。或者可能应该用另一种结构。例如: 当发现当前字符是数字时,开始处理: sum = 0; do { sum = sum *10 + (ch - '0'); ch = prog[p++]; } while (ch >= '0' && ch <= '9'); p--; // 因为最后一个ch不是数字,需要回退p的位置 syn = 20; 这样,在进入数字处理时,p已经被初始的ch = prog[p++]自增过一次。例如,假设初始p=0,读取ch=prog[0],p变为1。如果这个字符是数字,进入循环处理,读取prog[1]、prog[2]等,直到遇到非数字。此时,p的值已经指向非数字字符的下一个位置。例如,假设数字是123,prog[0]='1',p=1;进入循环,ch=prog[1]='2',p=2;然后ch=prog[2]='3',p=3;然后循环条件不满足,退出。此时,p=3。然后p--,变为2。这样,在scaner函数结束时,下一个字符是prog[2+1]=prog[3]?可能逻辑有问题。或者可能应该调整p的处理方式。 或者,在数字处理时,初始的ch已经被读取,是第一个数字字符。然后,在循环中继续读取下一个字符: sum = ch - '0'; while (prog[p
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值