词法分析器C++实现
单词分类表
大概可以分为以下几个大类:
1.标识符 有程序设计者自己定义但是必须符合标识符的定义规范
2.关键字
3.常量
4.运算符
5.分界符
单词符号 种类 类别号 单词符号 种类 类别号
int 关键字 1 + 运算符 21
float 关键字 2 - 运算符 22
long 关键字 3 * 运算符 23
short 关键字 4 / 运算符 24
double 关键字 5 = 运算符 25
bool 关键字 6 == 运算符 26
char 关键字 7 < 运算符 27
if 关键字 8 > 运算符 28
else 关键字 9 <= 运算符 29
while 关键字 10 >= 运算符 30
do 关键字 11 ++ 运算符 31
for 关键字 12 – 运算符 32
try 关键字 13 += 运算符 33
void 关键字 14 -= 运算符 34
case 关键字 15 ( 分界符 35
switch 关键字 16 ) 分界符 36
return 关键字 17 { 分界符 37
throw 关键字 18 } 分界符 38
static 关键字 19 [ 分界符 39
main 关键字 20 ] 分界符 40
, 分界符 41
; 分界符 42
整形常量 整形常量 43
浮点型常量 浮点型常量 44
标识符 标识符 45
单词结构文法G[S]
S->关键字|运算符|分界符|整型常量|浮点型常量|标识符
关键字->int|float|long|short|double|bool|char|if|else|while|do|for|try|void|case|switch|return|throw|static
运算符->+|-||/|=|==|<|>|<=|>=|++|–|+=|-=
分界符->(|)|{|}|[|]|,|;|//|/|/
digit->0|1|2|3|4|5|6|7|8|9
整型常量->digit (digit)
浮点型常量->digit(digit).digit(digit)
letter->a|b|c|d…|z|A|B|…|Z|_
标识符->letter(letter|digit)*
单词状态转化图
错误处理
-
单词拼写错误
当前状态与当前输入符号在转换表中的对应项为空 -
非法字符
不属于标识符定义规范同时也不属于定义字符的,例如@ ~ $ 中文字符等若检测到了非法字符,则程序报错并给出具体报错位置;若检测到单词拼写错误,则报错并给出报错的具体位置,程序终止
代码部分
#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
vector< pair<string, int> > v;
set<string> keys; //存放关键字的集合
string keysArray[] = { "int","float","long","short","double","bool","char","if","else","while","do","for","try",
"void","case","switch","return","throw","static","main"};
string token = ""; //用于存储单词
enum TokenCode
{
/*未定义字符例如\t \n等*/
TK_UNDEF = 0,
/* 关键字*/
KW_INT = 1, //int 关键字
KW_FLOAT, //float关键字
KW_LONG, //long关键字
KW_SHORT, //short关键字
KW_DOUBLE, //double关键字
KW_BOOL, //bool关键字
KW_CHAR, //char关键字
KW_IF, //if关键字
KW_ELSE, //else关键字
KW_WHILE, //while关键字
KW_DO, //do关键字
KW_FOR, //for关键字
KW_TRY, //try关键字
KW_VOID, //void关键字
KW_CASE, //case关键字
KW_SWITCH, //switch关键字
KW_RETURN, //return关键字
KW_THROW, //throw关键字
KW_STATIC, //static关键字
KW_MAIN, //main关键字‘
/*运算符*/
TK_PLUS, //运算符+
TK_MINUS, //运算符-
TK_MUL, //运算符*
TK_DIV, //运算符 /
TK_ASSIGN, //赋值语句
TK_EQ, //等于号
TK_LT, //小于
TK_GT, //大于
TK_LEQ, //小于等于
TK_GEQ, //大于等于
TK_PPLUS, //++
TK_MMINUS, //--
TK_PEQ, //+=
TK_MEQ, //-=
/*分界符*/
TK_OPENPA, // (
TK_CLOSEPA, // )
TK_BEGIN, // {
TK_END, // }
TK_OPENBR, //[
TK_CLOSEBR, //]
TK_COMMA, //逗号
TK_SEMOCLON, //分号;
TK_INT, //整型常量
TK_DOUBLE, //浮点型常量
/*标识符*/
TK_IDENT, //标识符
};
/* 报错信息
*/
enum WrongInfo {
WI_UDSG = 1, //未定义的符号 例如 # @等
WI_WID, //错误的标识符声明
};
TokenCode code = TK_UNDEF; //记录单词的种别码
WrongInfo err = WI_UDSG; //记录报错的信息
struct Position {
int column; //列
int line; //行
};
bool isLetter(char c) {
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) return true;
return false;
}
bool isDigit(char c) {
if (c >= '0' && c <= '9') {
return true;
}
return false;
}
int getKeyById(string token) {
int keysArray_size = sizeof(keysArray)/sizeof(keysArray[0]);
for (int i = 0; i < keysArray_size; i++) {
if (token==keysArray[i]) {
return i + 1;
}
}
}
void printToken(TokenCode code,Position pos) {
string op = " "; //表示分界符和运算符
if((code>=1 && code <= 20) || code == 45 || code == 43 || code == 44){
//switch (code) {
//case KW_INT:
//case KW_FLOAT:
//case KW_LONG:
//case KW_SHORT:
//case KW_DOUBLE:
//case KW_BOOL:
//case KW_CHAR:
//case KW_IF:
//case KW_ELSE:
//case KW_WHILE:
//case KW_DO:
//case KW_FOR:
//case KW_TRY:
//case KW_VOID:
//case KW_CASE:
//case KW_SWITCH:
//case KW_RETURN:
//case KW_THROW:
//case KW_STATIC:
//case KW_MAIN:
//case TK_IDENT:
//case TK_INT:
//case TK_DOUBLE:
cout << '<' << code << ',' << token << '>' << endl;
//}
}
else if (code >= 21 && code <= 42) {
switch (code) {
case TK_PLUS: op = "+";
break;
case TK_MINUS: op = "-";
break;
case TK_MUL: op = "*";
break;
case TK_DIV: op = "/";
break;
case TK_ASSIGN: op = "=";
break;
case TK_EQ: op = "==";
break;
case TK_LT: op = "<";
break;
case TK_GT: op = ">";
break;
case TK_LEQ: op = "<=";
break;
case TK_GEQ: op = ">=";
break;
case TK_PPLUS: op = "++";
break;
case TK_MMINUS: op = "--";
break;
case TK_PEQ: op = "+=";
break;
case TK_MEQ: op = "-=";
break;
case TK_OPENPA: op = "(";
break;
case TK_CLOSEPA: op = ")";
break;
case TK_BEGIN: op = "{";
break;
case TK_END: op = "}";
break;
case TK_OPENBR: op = "[";
break;
case TK_CLOSEBR: op = "]";
break;
case TK_COMMA: op = ",";
break;
case TK_SEMOCLON: op = ";";
break;
}
cout << '<' << code << ',' << op << '>' << endl;
}
else if (code == 0) {
switch(err){
case WI_UDSG: cout << "Wrong! Undefined signal! Error happen at line" << pos.line << endl;
break;
case WI_WID: {
err = WI_UDSG;
cout << "WRONG Identifier declare! Error happen at line" << pos.line << endl;
break;
}
}
}
}
void lexicalAnalysis() {
ifstream ifs;
ifs.open("test.txt", ios::in);
char byte = 0;
Position cur = { 0, 1}; // #表示未定义字符
while ((byte=ifs.get()) != EOF) {
if (byte == ' ' || byte == '\t') {
// 遇到空格跳过
if (byte == '\t') {
cur.column += 4;
}
else {
cur.column += 1;
}
continue;
}
else if (byte == '\n') {
//遇到换行符
cur.column = 1;
cur.line += 1;
continue;
}
else if (isLetter(byte)) {
//标识符或者是关键字
token = ""; //token初始化
do{
token.push_back(byte);
cur.column += 1; //当前的字符位置行不变,列+1
byte = ifs.get(); //获取下一个字符
} while (isLetter(byte) || isDigit(byte) || byte == '_'); //循环结束说明当前token中已经存储一个完整的单词
ifs.seekg(-1, ios::cur); //往前退一个字母
set<string>::iterator pos = keys.find(token); //查找token是否是关键字
if (pos != keys.end()) {
//在keys中找到了 说明token是关键字
code = TokenCode(getKeyById(token));
}
else {
code = TK_IDENT; //若找不到说明是标识符
}
}
else if (isDigit(byte)) { //浮点型常数和整型常数以数字开头
/*
* 这里没有考虑其他进制的情况以及都没有考虑的报错问题
*/
token = ""; //token初始化
bool other_flag = false; //表示是其他的进制表示方法
if (byte == '0') { //判断除十进制以外的其他进制
token.push_back(byte); //将0压入token串里
char nextByte = ifs.get();
if (nextByte == 'b' || nextByte == 'B' || nextByte == 'x' || nextByte == 'X') {
//若是上述的字母出现在0后面是正确的,压入这些字母继续判断
other_flag = true;
token.push_back(nextByte);
byte = ifs.get();
}
else
ifs.seekg(-1, ios::cur);
}
bool isDouble = false; //判断是否是浮点数
while (isDigit(byte)) {
token.push_back(byte);
byte = ifs.get();
if (byte == '.' && isDouble == false) { //检查出该字符是. 并且是第一次检查出
//小数点的下一位也是数字
if (isDigit(ifs.get())) {
isDouble = true;
token.push_back(byte); //将小数点存入token中
ifs.seekg(-1, ios::cur); //将超前读取的小数点后一位数字重新读取
byte = ifs.get();
}
}
}
ifs.seekg(-1, ios::cur);
if (isDouble) {
code = TK_DOUBLE; //单词为浮点型
}
else {
code = TK_INT; //单词为整型
}
//接下来判断该单词是否是一个错误的标识符(不能以数字开头)
byte = ifs.get();
if (!isDigit(byte) && byte != ' ' && byte != '\t'&& byte!=';'&& !other_flag) {
code = TK_UNDEF;
err = WI_WID;
}
else {
ifs.seekg(-1, ios::cur);
}
}
else {
//最后一种情况是运算符和分界符
switch (byte) {
case '+':
{
byte = ifs.get(); //读取下一个字符
if (byte == '+')
code = TK_PPLUS; // ++
else if (byte == '=')
code = TK_PEQ; // +=
else {
code = TK_PLUS; //+
ifs.seekg(-1, ios::cur); //其他情况下需要重新读取下一个字符
}
}
break;
case '-':
{
byte = ifs.get(); //读取下一个字符
if (byte == '=')
code = TK_MEQ; // -=
else if (byte == '-')
code = TK_MMINUS; //--
else {
code = TK_MINUS; //-
ifs.seekg(01, ios::cur);
}
}
break;
case '*': code = TK_MUL;
break;
case '/':
{
if (ifs.get() == '/') {
while(ifs.get()!='\n'){
;
}
ifs.seekg(-1, ios::cur);
break;
}
else {
code = TK_DIV;
ifs.seekg(-1, ios::cur);
break;
}
}
case '=':
{
byte = ifs.get(); //读取下一个字符
if (byte == '=') { // ==
code = TK_EQ; // 判断是否相等符号
}
else {
code = TK_ASSIGN; //单词为=
ifs.seekg(-1, ios::cur); //回退一个字符
}
}
break;
case '<':
{
byte = ifs.get(); //读取下一个字符
if (byte == '=') { // 小于等于
code = TK_LEQ;
}
else {
code = TK_LT; //小于
ifs.seekg(-1, ios::cur); //回退一个字符
}
}
break;
case '>':
{
byte = ifs.get(); //读取下一个字符
if (byte == '=') {
code = TK_GEQ; //大于等于
}
else {
code = TK_GT; //大于
ifs.seekg(-1, ios::cur); //回退一个字符
}
}
break;
// 分界符
case '(': code = TK_OPENPA;
break;
case ')': code = TK_CLOSEPA;
break;
case '{': code = TK_BEGIN;
break;
case '}': code = TK_END;
break;
case '[': code = TK_OPENBR;
break;
case ']': code = TK_CLOSEBR;
break;
case ',': code = TK_COMMA;
break;
case ';': code = TK_SEMOCLON;
break;
//未识别符号
default: code = TK_UNDEF;
}
}
printToken(code,cur);
}
}
int main() {
int keysArray_size = sizeof(keysArray) / sizeof(keysArray[0]);
//初始化关键字集合
for (int i = 0; i < keysArray_size; i++) {
keys.insert(keysArray[i]);
}
ifstream ifs;
ifs.open("test.txt", ios::in);
if (!ifs.is_open()) {
cout << "文件打开失败" << endl;
return 0;
}
lexicalAnalysis();
return 0;
}