LCOV - code coverage report
Current view: top level - src/lex - Lexer.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 196 197 99.5 %
Date: 2025-03-25 01:19:55 Functions: 8 8 100.0 %
Branches: 297 429 69.2 %

           Branch data     Line data    Source code
       1                 :            : #include "lex/Lexer.hpp"
       2                 :            : 
       3                 :            : #include <cctype>
       4                 :            : 
       5                 :            : 
       6                 :            : #define UNDO(CHR) { in.putback(c_); c_ = CHR; pos_.column--; }
       7                 :            : 
       8         [ +  - ]:       2149 : 
       9         [ +  - ]:       2149 : using namespace m;
      10         [ +  - ]:       2149 : using namespace m::ast;
      11         [ +  - ]:       2149 : 
      12         [ +  - ]:       2149 : 
      13         [ +  - ]:       4298 : void Lexer::initialize_keywords()
      14         [ +  - ]:       2149 : {
      15         [ +  - ]:       2149 : #define M_KEYWORD(tok, text) keywords_.emplace(pool(#text), TK_##tok);
      16         [ +  - ]:       2149 : #include <mutable/tables/Keywords.tbl>
      17         [ +  - ]:       2149 : #undef M_KEYWORD
      18         [ +  - ]:       4298 : }
      19         [ +  - ]:       2149 : 
      20         [ +  - ]:      29324 : Token Lexer::next()
      21         [ +  - ]:       2149 : {
      22         [ +  - ]:       2149 :     /* skip whitespaces and comments */
      23         [ +  - ]:      29324 :     for (;;) {
      24   [ +  -  +  +  :      55805 :         switch (c_) {
                   +  + ]
      25   [ +  -  +  - ]:       6146 :             case EOF: return Token(pos_, pool("EOF"), TK_EOF);
      26         [ +  - ]:      28628 :             case ' ': case '\t': case '\v': case '\f': case '\n': case '\r': step(); continue;
      27         [ +  - ]:       2149 : 
      28         [ +  - ]:       2149 :             case '-': {
      29         [ +  - ]:       2236 :                 step();
      30   [ +  -  +  + ]:       2236 :                 if (c_ == '-') {
      31         [ +  - ]:       2149 :                     /* read comment */
      32   [ +  -  +  +  :       2201 :                     do step(); while (c_ != EOF and c_ != '\n');
                   +  + ]
      33         [ +  - ]:       2151 :                     continue;
      34         [ +  - ]:       2149 :                 } else {
      35         [ +  - ]:       2149 :                     /* TK_MINUS */
      36         [ +  - ]:       2234 :                     UNDO('-');
      37         [ +  - ]:       2234 :                     goto after;
      38         [ +  - ]:       2149 :                 }
      39         [ +  - ]:       2149 :             }
      40         [ +  - ]:       2149 : 
      41         [ +  - ]:      25242 :             default: goto after;
      42         [ +  - ]:       2149 :         }
      43         [ +  - ]:       2149 :     }
      44         [ +  - ]:       2149 : after:
      45         [ +  - ]:       2149 : 
      46         [ +  - ]:      25327 :     start_ = pos_;
      47         [ +  - ]:      25327 :     buf_.clear();
      48         [ +  - ]:       2149 : 
      49   [ +  -  +  +  :      25327 :     switch (c_) {
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
                +  +  + ]
      50         [ +  - ]:       2149 :         case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
      51         [ +  - ]:       5688 :             return read_number();
      52         [ +  - ]:       2149 : 
      53         [ +  - ]:       2149 :         case '"':
      54         [ +  - ]:       2312 :             return read_string_literal();
      55         [ +  - ]:       2149 : 
      56         [ +  - ]:       2149 :         case 'd': {
      57         [ +  - ]:       2263 :             step();
      58   [ +  -  +  + ]:       2263 :             if (c_ == '\'') {
      59         [ +  - ]:       2215 :                 buf_.push_back('d'); // add prefix 'd'
      60         [ +  - ]:       2215 :                 return read_date_or_datetime();
      61         [ +  - ]:       2149 :             } else {
      62         [ +  - ]:       2197 :                 UNDO('d');
      63         [ +  - ]:       2197 :                 return read_keyword_or_identifier();
      64         [ +  - ]:       2149 :             }
      65         [ +  - ]:       2149 :         }
      66         [ +  - ]:       2149 : 
      67         [ +  - ]:       2149 :         /* Punctuators */
      68         [ -  + ]:       2149 : #define LEX(chr, text, tt, SUB) case chr: step(); switch (c_) { SUB } return Token(start_, pool(text), tt);
      69                 :            : #define GUESS(first, SUB) case first: step(); switch (c_) { SUB } UNDO(first); break;
      70         [ +  - ]:       1251 :         LEX('(', "(", TK_LPAR, );
      71         [ +  - ]:       1231 :         LEX(')', ")", TK_RPAR, );
      72         [ +  - ]:          7 :         LEX('~', "~", TK_TILDE, );
      73         [ +  - ]:         80 :         LEX('+', "+", TK_PLUS, );
      74         [ +  - ]:         86 :         LEX('-', "-", TK_MINUS, );
      75         [ +  - ]:        360 :         LEX('*', "*", TK_ASTERISK, );
      76         [ +  - ]:         15 :         LEX('/', "/", TK_SLASH, );
      77         [ +  - ]:          6 :         LEX('%', "%", TK_PERCENT, );
      78         [ +  - ]:        409 :         LEX('=', "=", TK_EQUAL, );
      79   [ +  +  +  - ]:         29 :         GUESS('!',
      80                 :            :             LEX('=', "!=", TK_BANG_EQUAL, ) );
      81   [ +  +  +  -  :         54 :         LEX('<', "<", TK_LESS,
                   +  - ]
      82                 :            :             LEX('=', "<=", TK_LESS_EQUAL, ) );
      83   [ +  +  +  -  :         46 :         LEX('>', ">", TK_GREATER,
                   +  - ]
      84                 :            :             LEX('=', ">=", TK_GREATER_EQUAL, ) );
      85         [ +  - ]:       3080 :         LEX(',', ",", TK_COMMA, );
      86         [ +  - ]:       1364 :         LEX(';', ";", TK_SEMICOL, );
      87   [ +  +  +  -  :        845 :         LEX('.', ".", TK_DOT,
                +  -  + ]
      88                 :            :             LEX('.', "..", TK_DOTDOT, )
      89                 :            :             case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
      90                 :            :                 UNDO('.');
      91                 :            :                 return read_number(););
      92                 :            :         case '\\':
      93                 :         22 :             return read_instruction();
      94                 :            : 
      95                 :            : #undef LEX
      96                 :            : #undef GUESS
      97                 :            : 
      98                 :            :         default: /* fallthrough */;
      99                 :      10478 :     }
     100                 :            : 
     101   [ +  +  +  + ]:      10480 :     if ('_' == c_ or is_alpha(c_)) return read_keyword_or_identifier();
     102                 :            : 
     103                 :         41 :     push();
     104                 :         41 :     const auto str = internalize();
     105   [ +  -  +  -  :         41 :     diag.e(start_) << "illegal character '" << str << "'\n";
             +  -  +  - ]
     106   [ +  -  +  - ]:         41 :     return Token(start_, std::move(str), TK_ERROR);
     107                 :      27175 : }
     108                 :            : 
     109                 :            : 
     110                 :            : /*====================================================================================================================*/
     111                 :            : //
     112                 :            : //  Lexer functions
     113                 :            : //
     114                 :            : /*====================================================================================================================*/
     115                 :            : 
     116                 :      10487 : Token Lexer::read_keyword_or_identifier()
     117                 :            : {
     118   [ +  +  +  + ]:      53156 :     while ('_' == c_ or is_alnum(c_))
     119                 :      42669 :         push();
     120                 :      10487 :     const auto str = internalize();
     121         [ +  - ]:      10487 :     auto it = keywords_.find(str);
     122   [ +  +  +  -  :      10487 :     if (it == keywords_.end()) return Token(start_, std::move(str), TK_IDENTIFIER);
                   +  - ]
     123   [ +  -  +  - ]:       5918 :     else return Token(start_, std::move(str), it->second);
     124                 :      10487 : }
     125                 :            : 
     126                 :       3629 : Token Lexer::read_number()
     127                 :            : {
     128                 :       3629 :     bool is_float = false;
     129                 :       3629 :     bool empty = true;
     130                 :            :     enum { Oct, Dec, Hex } is, has;
     131                 :            : 
     132                 :            :     /*-- Prefix ----------------------*/
     133                 :       3629 :     is = Dec;
     134         [ +  + ]:       3629 :     if ('0' == c_) { is = Oct; empty = false; push(); }
     135   [ +  +  +  + ]:       3629 :     if ('x' == c_ || 'X' == c_) { is = Hex; empty = true; push(); }
     136                 :       3725 :     has = is;
     137                 :            : 
     138                 :            :     /*-- sequence before dot ---------*/
     139                 :      10781 :     for (;;) {
     140   [ +  +  +  + ]:      10781 :         if      (is == Oct && is_oct(c_)) /* OK */;
     141   [ +  +  +  + ]:      10725 :         else if (is == Oct && is_dec(c_)) has = Dec;
     142   [ +  +  +  + ]:      10724 :         else if (is == Dec && is_dec(c_)) /* OK */;
     143   [ +  +  +  + ]:       4036 :         else if (is == Hex && is_hex(c_)) /* OK */;
     144                 :       3725 :         else    break;
     145                 :       7056 :         empty = false;
     146                 :       7056 :         push();
     147                 :            :     }
     148                 :            : 
     149                 :            :     /*-- the dot ---------------------*/
     150         [ +  + ]:       3725 :     if ('.' == c_) {
     151                 :        100 :         push();
     152                 :        100 :         is_float = true;
     153         [ +  + ]:        100 :         if (is  == Oct) is  = Dec; // there are no octal floating point constants
     154         [ +  + ]:        100 :         if (has == Oct) has = Dec;
     155                 :            : 
     156                 :            :         /*-- sequence after dot ------*/
     157   [ +  +  +  +  :        396 :         if      (is == Dec) { if (is_dec(c_)) empty = false; while (is_dec(c_)) push(); }
                   +  + ]
     158   [ +  +  +  + ]:         77 :         else { M_insist(is == Hex); if (is_hex(c_)) empty = false; while (is_hex(c_)) push(); }
     159                 :        100 :     }
     160                 :            : 
     161                 :            :     /*-- exponent part ---------------*/
     162   [ +  +  +  +  :       3767 :     if ((is == Oct && ('e' == c_ || 'E' == c_)) ||
                   +  + ]
     163   [ +  +  +  + ]:       6983 :         (is == Dec && ('e' == c_ || 'E' == c_)) ||
     164   [ +  +  +  + ]:       3577 :         (is == Hex && ('p' == c_ || 'P' == c_))) {
     165                 :       6894 :         push();
     166                 :       6894 :         is_float = true;
     167         [ +  + ]:       6894 :         if (is  == Oct) is  = Dec; // there are no octal floating point constants
     168         [ +  + ]:         16 :         if (has == Oct) has = Dec;
     169   [ +  +  +  + ]:         16 :         if ('-' == c_ || '+' == c_) push();
     170                 :         16 :         empty = true;
     171         [ +  + ]:         82 :         while (is_dec(c_)) { empty = false; push(); }
     172                 :         16 :     }
     173                 :            : 
     174   [ +  +  +  + ]:       3545 :     if (empty or is != has) {
     175                 :          6 :         const auto str = internalize();
     176   [ +  -  +  -  :          6 :         diag.e(start_) << "invalid number '" << str << "'\n";
             +  -  +  - ]
     177   [ +  -  +  - ]:          6 :         return Token(start_, std::move(str), TK_ERROR);
     178                 :          6 :     }
     179                 :            :     TokenType tt;
     180   [ -  +  +  + ]:       3539 :     switch (is) {
     181                 :         88 :         case Oct: tt = TK_OCT_INT; break;
     182                 :       3406 :         case Dec: tt = is_float ? TK_DEC_FLOAT : TK_DEC_INT; break;
     183                 :         45 :         case Hex: tt = is_float ? TK_HEX_FLOAT : TK_HEX_INT; break;
     184                 :            :     }
     185         [ +  - ]:       3539 :     return Token(start_, internalize(), tt);
     186                 :       3545 : }
     187                 :            : 
     188                 :        163 : Token Lexer::read_string_literal()
     189                 :            : {
     190                 :        163 :     push(); // initial '"'
     191                 :        163 :     bool invalid = false;
     192   [ +  +  +  + ]:       1275 :     while (EOF != c_ and '"' != c_) {
     193         [ +  + ]:       1112 :         if (c_ == '\\') { // escape character
     194                 :          6 :             push();
     195         [ +  + ]:          6 :             switch (c_) {
     196                 :            :                 default:
     197                 :            :                     /* invalid escape sequence */
     198                 :          1 :                     invalid = true;
     199                 :            :                     /* fallthrough */
     200                 :            :                 case '"':
     201                 :            :                 case '\\':
     202                 :            :                 case 'n':
     203                 :            :                 case 't':
     204                 :            :                     /* valid escape sequence */
     205                 :          6 :                     push();
     206                 :          6 :             }
     207                 :          6 :         } else {
     208                 :       1106 :             push();
     209                 :            :         }
     210                 :            :     }
     211                 :            : 
     212         [ +  + ]:        163 :     if ('"' != c_) {
     213                 :          1 :         const auto str = internalize();
     214   [ +  -  +  -  :          1 :         diag.e(start_) << "unterminated string literal '" << str << "'\n";
             +  -  +  - ]
     215   [ +  -  -  + ]:          1 :         return Token(start_, std::move(str), TK_ERROR);
     216                 :          1 :     }
     217                 :            : 
     218                 :        162 :     push(); // terminal '"'
     219                 :        162 :     const auto str = internalize();
     220                 :            : 
     221         [ +  + ]:        162 :     if (invalid) {
     222   [ +  -  +  -  :          1 :         diag.e(start_) << "invalid escape sequence in string literal '" << str << "'\n";
             +  -  +  - ]
     223   [ +  -  -  + ]:          1 :         return Token(start_, std::move(str), TK_ERROR);
     224                 :            :     }
     225                 :            : 
     226   [ +  -  +  - ]:        161 :     return Token(start_, std::move(str), TK_STRING_LITERAL);
     227                 :        163 : }
     228                 :            : 
     229                 :         66 : Token Lexer::read_date_or_datetime()
     230                 :            : {
     231                 :         66 :     push(); // initial '''
     232                 :         66 :     bool invalid = false;
     233                 :         66 :     bool datetime = false;
     234                 :            : 
     235                 :            : #define DIGITS(num) for (auto i = 0; i < num; ++i) if (is_dec(c_)) push(); else invalid = true;
     236                 :         66 :     accept('-'); // for years BC
     237   [ +  +  +  + ]:        330 :     DIGITS(4); // year
     238                 :         66 :     invalid &= accept('-');
     239   [ +  +  +  + ]:        198 :     DIGITS(2); // month
     240                 :         66 :     invalid &= accept('-');
     241   [ +  +  +  + ]:        198 :     DIGITS(2); // day
     242                 :            : 
     243         [ +  + ]:         66 :     if (accept(' ')) {
     244                 :         33 :         datetime = true;
     245   [ +  +  +  + ]:         99 :         DIGITS(2) // hours
     246                 :         33 :         invalid &= accept(':');
     247   [ +  +  +  + ]:         99 :         DIGITS(2); // minutes
     248                 :         33 :         invalid &= accept(':');
     249   [ +  +  +  + ]:         99 :         DIGITS(2); // seconds
     250                 :         33 :     }
     251                 :            : #undef DIGITS
     252                 :            : 
     253         [ +  + ]:         66 :     if ('\'' != c_) {
     254                 :         11 :         const auto str = internalize();
     255   [ +  -  +  -  :         11 :         diag.e(start_) << "unterminated " << (datetime ? "datetime" : "date") << " '" << str << "'\n";
          +  -  +  -  +  
                -  +  - ]
     256   [ +  -  -  + ]:         11 :         return Token(start_, std::move(str), TK_ERROR);
     257                 :         11 :     }
     258                 :            : 
     259                 :         55 :     push(); // terminal '''
     260                 :         55 :     const auto str = internalize();
     261                 :            : 
     262         [ +  + ]:         55 :     if (invalid) {
     263   [ +  -  +  -  :          6 :         diag.e(start_) << "invalid symbol in " << (datetime ? "datetime" : "date") << " '" << str << "'\n";
          +  -  +  -  +  
                -  +  - ]
     264   [ +  -  -  + ]:          6 :         return Token(start_, std::move(str), TK_ERROR);
     265                 :            :     }
     266                 :            : 
     267   [ +  -  +  - ]:         49 :     return Token(start_, std::move(str), datetime ? TK_DATE_TIME : TK_DATE);
     268                 :         66 : }
     269                 :            : 
     270                 :         22 : Token Lexer::read_instruction()
     271                 :            : {
     272                 :         22 :     push(); // initial '\'
     273   [ +  +  +  + ]:        319 :     while (';' != c_ and EOF != c_)
     274                 :        297 :         push();
     275         [ +  - ]:         22 :     return Token(start_, internalize(), TK_INSTRUCTION);
     276                 :          0 : }

Generated by: LCOV version 1.16