码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-10-23 15:57:55      阅读:337      评论:0      收藏:0      [点我收藏+]

标签:

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

http://www.cppblog.com/vczh/archive/2014/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 {
  9     Unknown,
 10     Number,
 11     Comment,
 12     Operator,
 13     Identifier,
 14     String
 15 };
 16 
 17 enum class ParseState
 18 {
 19     Begin,
 20     InComment,
 21     //InCommentEnd,
 22     InInteger,
 23     InFloat,
 24     InOperator,
 25     InIdentifier,
 26     InString,
 27     InStringEscaping,
 28     //InStringEnd
 29 };
 30 struct Token
 31 {
 32     int line;
 33     int column;
 34     TokenType type;
 35     std::wstring value;
 36     Token(): Token(0, 0, TokenType::Unknown, L"", 0)
 37     {
 38     }
 39     Token(int _line, int _column, TokenType _type, const wchar_t* str, int len)
 40         : line(_line)
 41         , column(_column)
 42         , type(_type)
 43         , value(str, len)
 44     {
 45     }
 46     void Reset()
 47     {
 48         line = 0;
 49         column = 0;
 50         type = TokenType::Unknown;
 51         value.clear();
 52     }
 53     void Push(const wchar_t ch)
 54     {
 55         value.push_back(ch);
 56     }
 57 };
 58 
 59 class TokenStream
 60 {
 61 public:
 62     static std::vector<Token> Parse(std::wstring& source)
 63     {
 64         const wchar_t* reading = source.c_str();
 65         ParseState state = ParseState::Begin;
 66         std::vector<Token> tokenList;
 67         int lineNumber = 1;
 68         int columnNumber = 1;
 69         Token token;
 70         while (*reading)
 71         {
 72             switch (state)
 73             {
 74             case ParseState::Begin:
 75             {
 76                 switch (*reading)
 77                 {
 78                 case+:case-:case*:case/:case(:case):case<:case>:case=:case;:
 79                 case^:case&:case.:
 80                 {
 81                     tokenList.push_back(Token(lineNumber, columnNumber, TokenType::Operator, reading, 1));
 82                     token.Reset();
 83                     reading++;
 84                     columnNumber++;
 85                     break;
 86                 }
 87                 case0:case1:case2:case3:case4:case5:case6:case7:case8:case9:
 88                 {
 89                     state = ParseState::InInteger;
 90                     token.type = TokenType::Number;
 91                     token.column = columnNumber;
 92                     token.line = lineNumber;
 93                     token.Push(*reading);
 94                     reading++;
 95                     columnNumber++;
 96                     break;
 97                 }
 98                 casea:caseb:casec:cased:casee:casef:caseg:caseh:casei:casej:casek:casel:casem:casen:caseo:casep:caseq:caser:cases:
 99                 caset:caseu:casev:casew:casex:casey:casez:caseA:caseB:caseC:caseD:caseE:caseF:caseG:caseH:caseI:caseJ:caseK:caseL:
100                 caseM:caseN:caseO:caseP:caseQ:caseR:caseS:caseT:caseU:caseV:caseW:caseX:caseY:caseZ:case_:
101                 {
102                     state = ParseState::InIdentifier;
103                     token.type = TokenType::Identifier;
104                     token.column = columnNumber;
105                     token.line = lineNumber;
106                     token.Push(*reading);
107                     reading++;
108                     columnNumber++;
109                     break;
110                 }
111                 case#:
112                 {
113                     state = ParseState::InComment;
114                     token.type = TokenType::Comment;
115                     token.column = columnNumber;
116                     token.line = lineNumber;
117                     reading++;
118                     columnNumber++;
119                     break;
120                 }
121                 case":
122                 {
123                     state = ParseState::InString;
124                     token.type = TokenType::String;
125                     token.column = columnNumber;
126                     token.line = lineNumber;
127                     reading++;
128                     columnNumber++;
129                     break;
130                 }
131                 case\n:
132                 {
133                     reading++;
134                     lineNumber++;
135                     columnNumber = 1;
136                     break;
137                 }
138                 case :
139                 {
140                     reading++;
141                     columnNumber++;
142                     break;
143                 }
144                 default:
145                 {
146                     throw std::exception("parse error");
147                     break;
148                 }
149                 }
150                 break;
151             } //case ParseState::Begin
152             case ParseState::InInteger:
153             {
154                 switch (*reading)
155                 {
156                 case0:case1:case2:case3:case4:case5:case6:case7:case8:case9:
157                 {
158                     token.Push(*reading);
159                     reading++;
160                     columnNumber++;
161                     break;
162                 }
163                 case.:
164                 {
165                     state = ParseState::InFloat;
166                     token.type = TokenType::Number;
167                     token.Push(*reading);
168                     reading++;
169                     columnNumber++;
170                     break;
171                 }
172                 default:
173                 {
174                     tokenList.push_back(token);
175                     token.Reset();
176                     state = ParseState::Begin;
177                     break;
178                 }
179                 }
180                 break;
181             } //case ParseState::InInteger
182             case ParseState::InComment:
183             {
184                 if (*reading == \n || *reading == #)
185                 {
186                     //state = ParseState::InCommentEnd;
187                     if (*reading == \n)
188                     {
189                         lineNumber++;
190                     }
191                     reading++;
192                     columnNumber = 1;
193                     tokenList.push_back(token);
194                     token.Reset();
195                     state = ParseState::Begin;
196                 }
197                 else
198                 {
199                     token.Push(*reading);
200                     reading++;
201                     columnNumber++;
202                 }
203                 break;
204             } //ParseState::InComment
205             case ParseState::InIdentifier:
206             {
207                 switch (*reading)
208                 {
209                 casea:caseb:casec:cased:casee:casef:caseg:caseh:casei:casej:casek:casel:casem:casen:caseo:casep:caseq:caser:cases:
210                 caset:caseu:casev:casew:casex:casey:casez:caseA:caseB:caseC:caseD:caseE:caseF:caseG:caseH:caseI:caseJ:caseK:caseL:
211                 caseM:caseN:caseO:caseP:caseQ:caseR:caseS:caseT:caseU:caseV:caseW:caseX:caseY:caseZ:case_:
212                 {
213                     token.Push(*reading);
214                     reading++;
215                     columnNumber++;
216                     break;
217                 }
218                 default:
219                 {
220                     tokenList.push_back(token);
221                     token.Reset();
222                     state = ParseState::Begin;
223                     break;
224                 }
225                 }
226                 break;
227             } //ParseState::InIdentifier
228             case ParseState::InString:
229             {
230                 switch (*reading)
231                 {
232                 case \\:
233                 {
234                     state = ParseState::InStringEscaping;
235                     reading++;
236                     columnNumber++;
237                     break;
238                 }
239                 case ":
240                 {
241                     //state = ParseState::InStringEnd;
242                     reading++;
243                     columnNumber++;
244                     tokenList.push_back(token);
245                     token.Reset();
246                     state = ParseState::Begin;
247                     break;
248                 }
249                 case \n:
250                 {
251                     throw std::exception("parse error in string");
252                     break;
253                 }
254                 default:
255                 {
256                     token.Push(*reading);
257                     reading++;
258                     columnNumber++;
259                     break;
260                 }
261                 }
262                 break;
263             } // ParseState::InString
264             case ParseState::InFloat:
265             {
266                 switch (*reading)
267                 {
268                 case0:case1:case2:case3:case4:case5:case6:case7:case8:case9:
269                 {
270                     token.type = TokenType::Number;
271                     token.Push(*reading);
272                     reading++;
273                     columnNumber++;
274                     break;
275                 }
276                 default:
277                 {
278                     tokenList.push_back(token);
279                     token.Reset();
280                     state = ParseState::Begin;
281                     break;
282                 }
283                 }
284                 break;
285             } //ParseState::InFloat
286             case ParseState::InStringEscaping:
287             {
288                 switch (*reading)
289                 {
290                 case n:
291                 {
292                     state = ParseState::InString;
293                     token.Push(\n);
294                     reading++;
295                     lineNumber++;
296                     break;
297                 }
298                 case \\:
299                 {
300                     state = ParseState::InString;
301                     token.Push(\\);
302                     reading++;
303                     lineNumber++;
304                     break;
305                 }
306                 case ":
307                 {
308                     state = ParseState::InString;
309                     token.Push(");
310                     reading++;
311                     lineNumber++;
312                     break;
313                 }
314                 default:
315                 {
316                     throw std::exception("parse error in escaping");
317                     break;
318                 }
319                 }
320                 break;
321             } //ParseState::InStringEscaping
322             } //switch state
323         }
324         return tokenList;
325     }
326 };
327 
328 #endif // !TOKEN_STREAM

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

 

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

标签:

原文地址:http://www.cnblogs.com/pointer-smq/p/4904531.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!