600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > 编译原理 - 1 手撸状态机词法分析器

编译原理 - 1 手撸状态机词法分析器

时间:2022-08-11 15:16:25

相关推荐

编译原理 - 1 手撸状态机词法分析器

感谢vczh轮子叔的坑了的教程,我的编译原理第一次入了个门,词法分析写完了,今后可以看看书继续往下学了。

/vczh/archive//03/02/206014.html

词法分析,就是对于一段代码,把他们分割成一个个的token,同时记录他们的行列号,丢掉不必要的信息,这个词法分析器很简单,简单的状态机就能胜任,用正则就没有自己造轮子的快感了,所以要自己手撸状态机拆token出来。

模仿vczh的语言,我的语言包括了以下要素

标识符:大小写字母和下划线构成的连续串[a-zA-Z_]

数字:自然书写的整数和浮点数,整数部分为0的浮点数必须要用0.开头

运算符:单个符号

注释:#开头,#或\n结尾的任意内容,也就是说注释最多是一整行

字符串:双引号"开头结尾的任意内容,支持\\,\n两种转义字符。

这样简单的语言,用正则表达式处理也很简单,但是为了贯彻自己造轮子的原则,我们手写一波状态机

首先把状态机的图画出来,就像这样(因为没有钱买visio,所以只能用一个叫lucidchart的在线应用画图了)

虚线表示这个状态可以直接跳到End状态,而所谓End状态就是Start状态,所以到了有虚线的状态,可以看情况直接跳到Start,并且有些状态也可以直接优化掉,比如InStringEnd, InOperator等,遇到这样的状态,直接输出一个token,然后把state设为Start就好了。

这样的状态机是不是很简单,更加复杂的一些状态,也可以手动构造正则表达式,然后用vczh的另一篇教程手写出状态机来。

忘了说Token的结构了,这个很简单

struct Token{int line;int column;TokenType type;std::wstring value;Token(): Token(0, 0, TokenType::Unknown, L"", 0){}Token(int _line, int _column, TokenType _type, const wchar_t* str, int len): line(_line), column(_column), type(_type), value(str, len){}void Reset(){line = 0;column = 0;type = TokenType::Unknown;value.clear();}void Push(const wchar_t ch){value.push_back(ch);}};

那个Reset和Push纯粹是因为我偷懒,把这个第一个版本的代码写得很烂,只能用这种不科学的封装来减少一下代码的重复了。

有了状态机和具体的状态:

enum class TokenType{Unknown,Number,Comment,Operator,Identifier,String};enum class ParseState{Begin,InComment,//InCommentEnd, InInteger,InFloat,InOperator,InIdentifier,InString,InStringEscaping,//InStringEnd};

可以构造状态机了,模板是这样的:

while (*reading){switch (state){case 某个状态:{switch (*reading){case 某个或某几个字符:#根据状态机里当前状态能接受和不能接受的字符,修改新的状态,读入字符,reading++,或者直接抛异常break;}}}

第一个版本的代码写得很烂,纯粹是能用的程序,放在这里纯粹是显摆一下,之后我会考虑重构什么的,然后再来更新这篇文章,而且没有写单元测试,正确性纯粹是肉眼检查,报错信息也很不友善,这一点下一个版本的代码再改。

更新,稍微优化了一下,重复代码少了不少,我很喜欢,还能接着改

下面贴代码:

TokenStream.h

1 #pragma once 2 #ifndef TOKEN_STREAM 3 #define TOKEN_STREAM 4 5 #include <string> 6 #include <vector> 7 enum class TokenType 8 { 9Unknown, 10Number, 11Comment, 12Operator, 13Identifier, 14String 15 }; 16 17 enum class ParseState 18 { 19Begin, 20InComment, 21//InCommentEnd, 22InInteger, 23InFloat, 24InOperator, 25InIdentifier, 26InString, 27InStringEscaping, 28//InStringEnd 29 }; 30 struct Token 31 { 32int line; 33int column; 34TokenType type; 35std::wstring value; 36Token(int _line, int _column, TokenType _type, const wchar_t* begin, const wchar_t* end) 37 : line(_line) 38 , column(_column) 39 , type(_type) 40 , value(begin, end) 41{ 42 if (type == TokenType::String) 43 { 44 for (size_t i = 0; i < value.length(); i++) 45 { 46 if (value[i] == '\\') 47 { 48 if (i + 1 < value.length()) 49 { 50switch (value[i + 1]) 51{ 52case'\\': 53{ 54 value.replace(i, 2, 1, '\\'); 55 break; 56} 57case'n': 58{ 59 value.replace(i, 2, 1, '\n'); 60 break; 61} 62case'\"': 63{ 64 value.replace(i, 2, 1, '\"'); 65 break; 66} 67default: 68{ 69 throw std::exception("error in escaping"); 70 break; 71} 72} 73 } 74 else 75 { 76throw std::exception("error in escaping"); 77 } 78 } 79 } 80 } 81} 82 }; 83 84 class TokenStream 85 { 86 private: 87struct ParsingState 88{ 89 ParseState State; 90 int Line; 91 int Column; 92 const wchar_t* TokenStart; 93 const wchar_t* TokenEnd; 94}; 95std::vector<Token> tokenList; 9697void PushToken(ParsingState& state, TokenType type) 98{ 99 tokenList.push_back(Token(state.Line, state.Column, type, state.TokenStart, state.TokenEnd));100}101 public:102std::vector<Token> Parse(std::wstring& source)103{104 ParsingState state{ParseState::Begin, 0, 0, source.c_str(), source.c_str()};105 int lineNumber = 1;106 int columnNumber = 1;107 108 while (*state.TokenEnd)109 {110 switch (state.State)111 {112 case ParseState::Begin:113 {114 state.TokenStart = state.TokenEnd;115 state.Column = columnNumber;116 state.Line = lineNumber;117 switch (*state.TokenEnd)118 {119 case'+':case'-':case'*':case'/':case'(':case')':case'<':case'>':case'=':case';':120 case'^':case'&':case'.':121 {122 state.TokenEnd++;123 PushToken(state, TokenType::Operator);124 state.TokenEnd--;125 break;126 }127 case'0':case'1':case'2':case'3':case'4':case'5':case'6':case'7':case'8':case'9':128 {129 state.State = ParseState::InInteger;130 break;131 }132 case'a':case'b':case'c':case'd':case'e':case'f':case'g':case'h':case'i':case'j':case'k':case'l':case'm':case'n':case'o':case'p':case'q':case'r':case's':133 case't':case'u':case'v':case'w':case'x':case'y':case'z':case'A':case'B':case'C':case'D':case'E':case'F':case'G':case'H':case'I':case'J':case'K':case'L':134 case'M':case'N':case'O':case'P':case'Q':case'R':case'S':case'T':case'U':case'V':case'W':case'X':case'Y':case'Z':case'_':135 {136 state.State = ParseState::InIdentifier;137 break;138 }139 case'#':140 {141 state.TokenStart++;142 state.State = ParseState::InComment;143 break;144 }145 case'"':146 {147 state.TokenStart++;148 state.State = ParseState::InString;149 break;150 }151 case'\n':152 {153 lineNumber++;154 columnNumber = 0;155 break;156 }157 case' ':158 {159 break;160 }161 default:162 {163 throw std::exception("parse error");164 break;165 }166 }167 break;168 } //case ParseState::Begin169 case ParseState::InInteger:170 {171 switch (*state.TokenEnd)172 {173 case'0':case'1':case'2':case'3':case'4':case'5':case'6':case'7':case'8':case'9':174 {175 break;176 }177 case'.':178 {179 state.State = ParseState::InFloat;180 break;181 }182 default:183 {184 state.State = ParseState::Begin;185 PushToken(state, TokenType::Number);186 state.TokenEnd--;187 columnNumber--;188 break;189 }190 }191 break;192 } //case ParseState::InInteger193 case ParseState::InComment:194 {195 if (*state.TokenEnd == '\n' || *state.TokenEnd == '#')196 {197 state.State = ParseState::Begin;198 if (*state.TokenEnd == '\n')199 {200lineNumber++;201 }202 columnNumber = 1;203 PushToken(state, TokenType::Comment);204 }205 break;206 } //ParseState::InComment207 case ParseState::InIdentifier:208 {209 switch (*state.TokenEnd)210 {211 case'a':case'b':case'c':case'd':case'e':case'f':case'g':case'h':case'i':case'j':case'k':case'l':case'm':case'n':case'o':case'p':case'q':case'r':case's':212 case't':case'u':case'v':case'w':case'x':case'y':case'z':case'A':case'B':case'C':case'D':case'E':case'F':case'G':case'H':case'I':case'J':case'K':case'L':213 case'M':case'N':case'O':case'P':case'Q':case'R':case'S':case'T':case'U':case'V':case'W':case'X':case'Y':case'Z':case'_':214 {215 break;216 }217 default:218 {219 state.State = ParseState::Begin;220 PushToken(state, TokenType::Identifier);221 columnNumber--;222 state.TokenEnd--;223 break;224 }225 }226 break;227 } //ParseState::InIdentifier228 case ParseState::InString:229 {230 switch (*state.TokenEnd)231 {232 case '\\':233 {234 state.State = ParseState::InStringEscaping;235 break;236 }237 case '"':238 {239 state.State = ParseState::Begin;240 PushToken(state, TokenType::String);241 break;242 }243 case '\n':244 {245 throw std::exception("parse error in string");246 break;247 }248 default:249 {250 break;251 }252 }253 break;254 } // ParseState::InString255 case ParseState::InFloat:256 {257 switch (*state.TokenEnd)258 {259 case'0':case'1':case'2':case'3':case'4':case'5':case'6':case'7':case'8':case'9':260 {261 break;262 }263 default:264 {265 PushToken(state, TokenType::Number);266 columnNumber--;267 state.TokenEnd--;268 state.State = ParseState::Begin;269 break;270 }271 }272 break;273 } //ParseState::InFloat274 case ParseState::InStringEscaping:275 {276 state.State = ParseState::InString;277 switch (*state.TokenEnd)278 {279 case'n':case'\\':case'"':280 {281 break;282 }283 default:284 {285 throw std::exception("parse error in escaping");286 break;287 }288 }289 break;290 } //ParseState::InStringEscaping291 } //switch state292 state.TokenEnd++;293 columnNumber++;294 }295 return tokenList;296}297 };298 299 #endif // !TOKEN_STREAM

300行的大函数,我也是醉了

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。