• 编译原理


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

    http://www.cppblog.com/vczh/archive/2014/03/02/206014.html

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

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

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

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

      运算符:单个符号

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

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

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

    首先把状态机的图画出来,就像这样(因为没有钱买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(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                     {
     50                         switch (value[i + 1])
     51                         {
     52                         case'\':
     53                         {
     54                             value.replace(i, 2, 1, '\');
     55                             break;
     56                         }
     57                         case'n':
     58                         {
     59                             value.replace(i, 2, 1, '
    ');
     60                             break;
     61                         }
     62                         case'"':
     63                         {
     64                             value.replace(i, 2, 1, '"');
     65                             break;
     66                         }
     67                         default:
     68                         {
     69                             throw std::exception("error in escaping");
     70                             break;
     71                         }
     72                         }
     73                     }
     74                     else
     75                     {
     76                         throw std::exception("error in escaping");
     77                     }
     78                 }
     79             }
     80         }
     81     }
     82 };
     83 
     84 class TokenStream
     85 {
     86 private:
     87     struct ParsingState
     88     {
     89         ParseState State;
     90         int Line;
     91         int Column;
     92         const wchar_t* TokenStart;
     93         const wchar_t* TokenEnd;
     94     };
     95     std::vector<Token> tokenList;
     96     
     97     void PushToken(ParsingState& state, TokenType type)
     98     {
     99         tokenList.push_back(Token(state.Line, state.Column, type, state.TokenStart, state.TokenEnd));
    100     }
    101 public:
    102     std::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'
    ':
    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::Begin
    169             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::InInteger
    193             case ParseState::InComment:
    194             {
    195                 if (*state.TokenEnd == '
    ' || *state.TokenEnd == '#')
    196                 {
    197                     state.State = ParseState::Begin;
    198                     if (*state.TokenEnd == '
    ')
    199                     {
    200                         lineNumber++;
    201                     }
    202                     columnNumber = 1;
    203                     PushToken(state, TokenType::Comment);
    204                 }
    205                 break;
    206             } //ParseState::InComment
    207             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::InIdentifier
    228             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 '
    ':
    244                 {
    245                     throw std::exception("parse error in string");
    246                     break;
    247                 }
    248                 default:
    249                 {
    250                     break;
    251                 }
    252                 }
    253                 break;
    254             } // ParseState::InString
    255             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::InFloat
    274             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::InStringEscaping
    291             } //switch state
    292             state.TokenEnd++;
    293             columnNumber++;
    294         }
    295         return tokenList;
    296     }
    297 };
    298 
    299 #endif // !TOKEN_STREAM

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

  • 相关阅读:
    django 基础2 Djanos三件套+form三要素+ORM
    django 基础 1 web框架原理
    pg_dump: [archiver (db)] query failed: ERROR: out of shared memory HINT: You might need to increase max_locks_per_transaction. IN ACCESS SHARE MODE
    Ubuntu 16.04——配置Nginx及Https服务
    使用Nginx设置基本HTTP身份验证
    关于报错 :MySQLdb._exceptions.OperationalError: (2026, 'SSL connection error: SSL_CTX_set_tmp_dh failed')
    redis报错 Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check
    Sublime插件Emmet的安装及Tab补全代码问题解决
    Python编程基础[函数和面向对象](三)
    Python编程基础变量赋值,字典,切片,[条件语句if 循环语句 for,while](二)
  • 原文地址:https://www.cnblogs.com/pointer-smq/p/4904531.html
Copyright © 2020-2023  润新知