LCOV - code coverage report
Current view: top level - tests - catch_pbql_lexer.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 98.9 % 541 535
Test Date: 2025-06-19 11:28:46 Functions: 100.0 % 8 8
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright (c) 2019-2025  Made to Order Software Corp.  All Rights Reserved
       2              : //
       3              : // https://snapwebsites.org/project/prinbee
       4              : // contact@m2osw.com
       5              : //
       6              : // This program is free software: you can redistribute it and/or modify
       7              : // it under the terms of the GNU General Public License as published by
       8              : // the Free Software Foundation, either version 3 of the License, or
       9              : // (at your option) any later version.
      10              : //
      11              : // This program is distributed in the hope that it will be useful,
      12              : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13              : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14              : // GNU General Public License for more details.
      15              : //
      16              : // You should have received a copy of the GNU General Public License
      17              : // along with this program.  If not, see <https://www.gnu.org/licenses/>.
      18              : 
      19              : // prinbee
      20              : //
      21              : #include    <prinbee/pbql/lexer.h>
      22              : 
      23              : #include    <prinbee/exception.h>
      24              : 
      25              : 
      26              : // self
      27              : //
      28              : #include    "catch_main.h"
      29              : 
      30              : 
      31              : // libuf8
      32              : //
      33              : #include    <libutf8/libutf8.h>
      34              : 
      35              : 
      36              : // snaplogger
      37              : //
      38              : #include    <snaplogger/message.h>
      39              : 
      40              : 
      41              : // snapdev
      42              : //
      43              : //#include    <snapdev/hexadecimal_string.h>
      44              : //#include    <snapdev/math.h>
      45              : //#include    <snapdev/ostream_int128.h>
      46              : 
      47              : 
      48              : // C++
      49              : //
      50              : #include    <bitset>
      51              : //#include    <fstream>
      52              : //#include    <iomanip>
      53              : 
      54              : 
      55              : // C
      56              : //
      57              : //#include    <sys/stat.h>
      58              : //#include    <sys/types.h>
      59              : 
      60              : 
      61              : // last include
      62              : //
      63              : #include    <snapdev/poison.h>
      64              : 
      65              : 
      66              : 
      67              : namespace
      68              : {
      69              : 
      70              : 
      71              : 
      72              : 
      73              : 
      74              : } // no name namespace
      75              : 
      76              : 
      77              : 
      78            9 : CATCH_TEST_CASE("lexer", "[lexer] [pbql]")
      79              : {
      80           11 :     CATCH_START_SECTION("lexer: verify tokens")
      81              :     {
      82              :         class script_t
      83              :         {
      84              :         public:
      85              :             struct value_t
      86              :             {
      87              :                 std::string     f_string = std::string();
      88              :                 //uint512_t       f_integer = uint512_t(); -- in this test, we do not want to verify super large numbers
      89              :                 std::int64_t    f_integer = 0;
      90              :                 long double     f_floating_point = 0.0;
      91              :             };
      92              : 
      93           28 :             void add_line(prinbee::pbql::token_t token, std::string const & s)
      94              :             {
      95           28 :                 append_to_script(s);
      96           28 :                 f_tokens.push_back(token);
      97           28 :                 f_values.push_back(value_t());
      98           28 :             }
      99              : 
     100           12 :             void add_line(prinbee::pbql::token_t token, std::string const & s, std::string string)
     101              :             {
     102           12 :                 append_to_script(s);
     103           12 :                 f_tokens.push_back(token);
     104           12 :                 value_t v;
     105           12 :                 v.f_string = string;
     106           12 :                 f_values.push_back(v);
     107           24 :             }
     108              : 
     109           14 :             void add_line(prinbee::pbql::token_t token, std::string const & s, std::int64_t integer)
     110              :             {
     111           14 :                 append_to_script(s);
     112           14 :                 value_t v;
     113           14 :                 f_tokens.push_back(token);
     114           14 :                 v.f_integer = integer;
     115           14 :                 f_values.push_back(v);
     116           28 :             }
     117              : 
     118            4 :             void add_line(prinbee::pbql::token_t token, std::string const & s, long double floating_point)
     119              :             {
     120            4 :                 append_to_script(s);
     121            4 :                 f_tokens.push_back(token);
     122            4 :                 value_t v;
     123            4 :                 v.f_floating_point = floating_point;
     124            4 :                 f_values.push_back(v);
     125            8 :             }
     126              : 
     127            1 :             void tokenize() const
     128              :             {
     129            3 :                 std::string const filename("./lexer-tokens.pbql");
     130            1 :                 prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(f_script, filename));
     131            1 :                 prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     132            1 :                 lexer->set_input(in);
     133              : 
     134            1 :                 CATCH_REQUIRE(f_tokens.size() == f_values.size());
     135           59 :                 for(std::size_t idx(0); idx < f_tokens.size(); ++idx)
     136              :                 {
     137           58 :                     prinbee::pbql::node::pointer_t n(lexer->get_next_token());
     138           58 :                     CATCH_REQUIRE(n->get_token() == f_tokens[idx]);
     139              : 
     140           58 :                     prinbee::pbql::location const & loc(n->get_location());
     141           58 :                     CATCH_REQUIRE(loc.get_filename() == filename);
     142           58 :                     CATCH_REQUIRE(loc.get_column() == 1); // poor test for columns...
     143           58 :                     CATCH_REQUIRE(loc.get_line() == static_cast<int>(idx + 1));
     144              : 
     145           58 :                     CATCH_REQUIRE(n->get_string() == f_values[idx].f_string);
     146           58 :                     CATCH_REQUIRE(n->get_integer() == f_values[idx].f_integer);
     147           58 :                     bool const float_nearly_equal(SNAP_CATCH2_NAMESPACE::nearly_equal(n->get_floating_point(), f_values[idx].f_floating_point, 0.0L));
     148           58 :                     if(!float_nearly_equal)
     149              :                     {
     150            0 :                         SNAP_LOG_FATAL
     151            0 :                             << "floating points are not equal "
     152            0 :                             << n->get_floating_point()
     153            0 :                             << " vs "
     154            0 :                             << f_values[idx].f_floating_point
     155              :                             << SNAP_LOG_SEND;
     156              :                     }
     157           58 :                     CATCH_REQUIRE(float_nearly_equal);
     158           58 :                     CATCH_REQUIRE(n->get_parent() == nullptr);
     159           58 :                     CATCH_REQUIRE(n->get_children_size() == 0);
     160           58 :                 }
     161              : 
     162              :                 // after that we always get an end of file token
     163              :                 //
     164           11 :                 for(std::size_t count(0); count < 10; ++count)
     165              :                 {
     166           10 :                     prinbee::pbql::node::pointer_t n(lexer->get_next_token());
     167           10 :                     CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_EOF);
     168              : 
     169           10 :                     prinbee::pbql::location const & loc(n->get_location());
     170           10 :                     CATCH_REQUIRE(loc.get_filename() == filename);
     171           10 :                     CATCH_REQUIRE(loc.get_column() == 1);
     172           10 :                     CATCH_REQUIRE(loc.get_line() == static_cast<int>(f_tokens.size() + 1)); // EOF ends up on the last line + 1
     173              : 
     174           10 :                     CATCH_REQUIRE(n->get_string() == std::string());
     175           10 :                     CATCH_REQUIRE(n->get_integer() == 0);
     176           10 :                     CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(n->get_floating_point(), 0.0L, 0.0L));
     177           10 :                     CATCH_REQUIRE(n->get_parent() == nullptr);
     178           10 :                     CATCH_REQUIRE(n->get_children_size() == 0);
     179           10 :                 }
     180            2 :             }
     181              : 
     182              :         private:
     183           58 :             void append_to_script(std::string const & s)
     184              :             {
     185           58 :                 f_script += s;
     186           58 :                 if((rand() & 1) == 0)
     187              :                 {
     188           34 :                     f_script += '\r';
     189              :                 }
     190           58 :                 f_script += '\n';
     191           58 :             }
     192              : 
     193              :             std::string                         f_script = std::string();
     194              :             std::vector<prinbee::pbql::token_t> f_tokens = std::vector<prinbee::pbql::token_t>();
     195              :             std::vector<value_t>                f_values = std::vector<value_t>();
     196              :         };
     197              : 
     198            1 :         script_t script;
     199              : 
     200              :         // WARNING: the '#' on the very first line / column is a special case
     201              :         //          so try something else first
     202              :         //
     203            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_MODULO,             "%");
     204            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_BITWISE_XOR,        "#");
     205            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_BITWISE_AND,        "&");
     206            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_OPEN_PARENTHESIS,   "(");
     207            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_CLOSE_PARENTHESIS,  ")");
     208            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_MULTIPLY,           "*");
     209            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_PLUS,               "+");
     210            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_COMMA,              ",");
     211            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_MINUS,              "-");
     212            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_DIVIDE,             "/");
     213            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_COLON,              ":");
     214            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_SEMI_COLON,         ";");
     215            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_EQUAL,              "=");
     216            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_ABSOLUTE_VALUE,     "@");
     217            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_POWER,              "^");
     218            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_BITWISE_OR,         "|");
     219            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_REGULAR_EXPRESSION, "~");
     220            5 :         script.add_line(prinbee::pbql::token_t::TOKEN_IDENTIFIER,         "identifier",          "identifier");
     221            5 :         script.add_line(prinbee::pbql::token_t::TOKEN_IDENTIFIER,         "CAPS",                "CAPS");
     222            5 :         script.add_line(prinbee::pbql::token_t::TOKEN_IDENTIFIER,         "_123",                "_123");
     223            5 :         script.add_line(prinbee::pbql::token_t::TOKEN_STRING,             "'\\no e\\fect'",      "\\no e\\fect");
     224            5 :         script.add_line(prinbee::pbql::token_t::TOKEN_STRING,             "e'string\\n'",        "string\n");
     225            5 :         script.add_line(prinbee::pbql::token_t::TOKEN_STRING,             "E'string\\r'",        "string\r");
     226            5 :         script.add_line(prinbee::pbql::token_t::TOKEN_STRING,             "e'\\b\\f\\n\\r\\t'",  "\b\f\n\r\t");
     227            5 :         script.add_line(prinbee::pbql::token_t::TOKEN_STRING,             "e'\\a\\g\\m\\s\\\\'", "agms\\");
     228            5 :         script.add_line(prinbee::pbql::token_t::TOKEN_STRING,             "e'\\58only 5'",       "\058only 5");
     229            5 :         script.add_line(prinbee::pbql::token_t::TOKEN_STRING,             "e'\\339only 33'",     "\339only 33");
     230            5 :         script.add_line(prinbee::pbql::token_t::TOKEN_STRING,             "e'\\xfg only f'",     "\xfg only f");
     231            5 :         script.add_line(prinbee::pbql::token_t::TOKEN_STRING,             "e'\\xf: only f'",     "\xf: only f");
     232            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER,            "123",                 123L);
     233            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER,            "0b11001010",          0xCAL);
     234            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER,            "0B11001010",          0xCAL);
     235            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER,            "b'11001010'",         0xCAL);
     236            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER,            "B'11011110'",         0xDEL);
     237            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER,            "0711",                711L); // this is not octal in SQL
     238            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER,            "0o345",               0345L);
     239            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER,            "0O346",               0346L);
     240            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER,            "o'365'",              0365L);
     241            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER,            "O'645'",              0645L);
     242            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER,            "0xa9d1b1f",           0xa9d1b1fL);
     243            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER,            "0Xa3d1f1c",           0xa3d1f1cL);
     244            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER,            "x'a9d3b3f'",          0xa9d3b3fL);
     245            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_INTEGER,            "X'a9d9d1f'",          0xa9d9d1fL);
     246            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_FLOATING_POINT,     "5.12309",             5.12309L);
     247            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_FLOATING_POINT,     "5.12309E3",           5123.09L);
     248            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_FLOATING_POINT,     "7.83213e+3",          7832.13L);
     249            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_FLOATING_POINT,     "7841.93e-3",          7.84193L);
     250            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_NOT_EQUAL,          "<>");
     251            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_LESS,               "<");
     252            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_LESS_EQUAL,         "<=");
     253            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_GREATER,            ">");
     254            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_GREATER_EQUAL,      ">=");
     255            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_SQUARE_ROOT,        "|/");
     256            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_CUBE_ROOT,          "||/");
     257            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_SCOPE,              "::");
     258            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_SHIFT_LEFT,         "<<");
     259            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_SHIFT_RIGHT,        ">>");
     260            3 :         script.add_line(prinbee::pbql::token_t::TOKEN_STRING_CONCAT,      "||");
     261              : 
     262            1 :         script.tokenize();
     263            1 :     }
     264           10 :     CATCH_END_SECTION()
     265              : 
     266           11 :     CATCH_START_SECTION("lexer: binary 0 to 255")
     267              :     {
     268          257 :         for(int v(0); v < 256; ++v)
     269              :         {
     270          256 :             std::bitset<8> binary(v);
     271          256 :             std::stringstream ss;
     272          256 :             ss << "0b" << binary;
     273              : 
     274          256 :             prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(ss.str(), "./lexer-binary.pbql"));
     275          256 :             prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     276          256 :             lexer->set_input(in);
     277              : 
     278          256 :             prinbee::pbql::node::pointer_t n(lexer->get_next_token());
     279          256 :             CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_INTEGER);
     280          256 :             CATCH_REQUIRE(n->get_integer() == v);
     281          256 :         }
     282              :     }
     283           10 :     CATCH_END_SECTION()
     284              : 
     285           11 :     CATCH_START_SECTION("lexer: octal characters 1 to 255")
     286              :     {
     287          256 :         for(int c(1); c < 256; ++c)
     288              :         {
     289          255 :             int const zeroes(c < 8 ? rand() % 3 : (c < 64 ? rand() % 2 : 0));
     290          255 :             std::stringstream ss;
     291          255 :             ss << (rand() & 1 ? 'e' : 'E')
     292          255 :                << "'\\"
     293          255 :                << std::oct
     294         1020 :                << std::string(zeroes, '0')
     295              :                << c
     296          510 :                << '\'';
     297              : 
     298          255 :             prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(ss.str(), "./lexer-octal-char.pbql"));
     299          255 :             prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     300          255 :             lexer->set_input(in);
     301              : 
     302          255 :             prinbee::pbql::node::pointer_t n(lexer->get_next_token());
     303          255 :             CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_STRING);
     304          255 :             if(c < 0x80)
     305              :             {
     306          381 :                 CATCH_REQUIRE(n->get_string() == std::string(1, c));
     307              :             }
     308              :             else
     309              :             {
     310          128 :                 std::string const expected(libutf8::to_u8string(static_cast<char32_t>(c)));
     311          128 :                 CATCH_REQUIRE(n->get_string() == expected);
     312          128 :             }
     313          255 :         }
     314              :     }
     315           10 :     CATCH_END_SECTION()
     316              : 
     317           11 :     CATCH_START_SECTION("lexer: hexadecimal characters 1 to 255")
     318              :     {
     319          256 :         for(int c(1); c < 256; ++c)
     320              :         {
     321          255 :             std::stringstream ss;
     322          255 :             if((rand() & 1) != 0)
     323              :             {
     324          114 :                 ss << std::uppercase;
     325              :             }
     326          255 :             ss << (rand() & 1 ? 'e' : 'E')
     327              :                << "'\\"
     328          255 :                << (rand() & 1 ? 'x' : 'X')
     329          255 :                << std::hex
     330          255 :                << (c < 16 ? ((rand() & 1) != 0 ? "" : "0") : "")
     331              :                << c
     332          255 :                << '\'';
     333              : 
     334          255 :             prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(ss.str(), "./lexer-hexadecimal-char.pbql"));
     335          255 :             prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     336          255 :             lexer->set_input(in);
     337              : 
     338          255 :             prinbee::pbql::node::pointer_t n(lexer->get_next_token());
     339          255 :             CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_STRING);
     340          255 :             if(c < 0x80)
     341              :             {
     342          381 :                 CATCH_REQUIRE(n->get_string() == std::string(1, c));
     343              :             }
     344              :             else
     345              :             {
     346          128 :                 std::string const expected(libutf8::to_u8string(static_cast<char32_t>(c)));
     347          128 :                 CATCH_REQUIRE(n->get_string() == expected);
     348          128 :             }
     349          255 :         }
     350              :     }
     351           10 :     CATCH_END_SECTION()
     352              : 
     353           11 :     CATCH_START_SECTION("lexer: 4 digits unicode characters")
     354              :     {
     355        63488 :         for(int c(1); c < 0x10000; ++c)
     356              :         {
     357              :             // skip surrogates
     358              :             //
     359        63487 :             if(c == 0xD800)
     360              :             {
     361            1 :                 c = 0xE000;
     362              :             }
     363        63487 :             std::stringstream ss;
     364        63487 :             if((rand() & 1) != 0)
     365              :             {
     366        31684 :                 ss << std::uppercase;
     367              :             }
     368        63487 :             ss << (rand() & 1 ? 'e' : 'E')
     369        63487 :                << "'\\u"
     370        63487 :                << std::hex
     371              :                << std::setfill('0')
     372        63487 :                << std::setw(4)
     373              :                << c
     374        63487 :                << '\'';
     375              : 
     376        63487 :             prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(ss.str(), "./lexer-plane0-unicode-char.pbql"));
     377        63487 :             prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     378        63487 :             lexer->set_input(in);
     379              : 
     380        63487 :             prinbee::pbql::node::pointer_t n(lexer->get_next_token());
     381        63487 :             CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_STRING);
     382        63487 :             if(c < 0x80)
     383              :             {
     384          381 :                 CATCH_REQUIRE(n->get_string() == std::string(1, c));
     385              :             }
     386              :             else
     387              :             {
     388        63360 :                 std::string const expected(libutf8::to_u8string(static_cast<char32_t>(c)));
     389        63360 :                 CATCH_REQUIRE(n->get_string() == expected);
     390        63360 :             }
     391        63487 :         }
     392              :     }
     393           10 :     CATCH_END_SECTION()
     394              : 
     395           11 :     CATCH_START_SECTION("lexer: 8 digits unicode characters")
     396              :     {
     397         1001 :         for(int count(0); count < 1000; ++count)
     398              :         {
     399         1000 :             int const c(SNAP_CATCH2_NAMESPACE::random_char(SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_UNICODE));
     400         1000 :             std::stringstream ss;
     401         1000 :             if((rand() & 1) != 0)
     402              :             {
     403          507 :                 ss << std::uppercase;
     404              :             }
     405         1000 :             ss << (rand() & 1 ? 'e' : 'E')
     406         1000 :                << "'\\U"
     407         1000 :                << std::hex
     408              :                << std::setfill('0')
     409         1000 :                << std::setw(8)
     410              :                << c
     411         1000 :                << '\'';
     412              : 
     413         1000 :             prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(ss.str(), "./lexer-any-unicode-char.pbql"));
     414         1000 :             prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     415         1000 :             lexer->set_input(in);
     416              : 
     417         1000 :             prinbee::pbql::node::pointer_t n(lexer->get_next_token());
     418         1000 :             CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_STRING);
     419         1000 :             if(c < 0x80)
     420              :             {
     421            0 :                 CATCH_REQUIRE(n->get_string() == std::string(1, c));
     422              :             }
     423              :             else
     424              :             {
     425         1000 :                 std::string const expected(libutf8::to_u8string(static_cast<char32_t>(c)));
     426         1000 :                 CATCH_REQUIRE(n->get_string() == expected);
     427         1000 :             }
     428         1000 :         }
     429              :     }
     430           10 :     CATCH_END_SECTION()
     431              : 
     432           11 :     CATCH_START_SECTION("lexer: hash comment at top of file (1 line)")
     433              :     {
     434            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("# hash at the start is viewed as a comment!\nthis is not", "./lexer-hash-comment.pbql"));
     435            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     436            1 :         lexer->set_input(in);
     437              : 
     438            1 :         prinbee::pbql::node::pointer_t n(lexer->get_next_token());
     439            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     440            1 :         CATCH_REQUIRE(n->get_string() == "this");
     441            1 :         prinbee::pbql::location const & loc(n->get_location());
     442            1 :         CATCH_REQUIRE(loc.get_line() == 2);
     443              : 
     444            1 :         n = lexer->get_next_token();
     445            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     446            1 :         CATCH_REQUIRE(n->get_string() == "is");
     447              : 
     448            1 :         n = lexer->get_next_token();
     449            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     450            1 :         CATCH_REQUIRE(n->get_string() == "not");
     451              : 
     452            1 :         n = lexer->get_next_token();
     453            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_EOF);
     454            1 :     }
     455           10 :     CATCH_END_SECTION()
     456              : 
     457           11 :     CATCH_START_SECTION("lexer: hash comment at top of file (3 lines)")
     458              :     {
     459            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(
     460              :                 "#!/usr/bin/pbql -e\n"
     461              :                 "# initialization script for website tables\n"
     462              :                 "# and some default system data\n"
     463            1 :                 "CREATE TABLE /* C-like comment */ magic;", "./lexer-hash-comment.pbql"));
     464            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     465            1 :         lexer->set_input(in);
     466              : 
     467            1 :         prinbee::pbql::node::pointer_t n(lexer->get_next_token());
     468            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     469            1 :         CATCH_REQUIRE(n->get_string() == "CREATE");
     470            1 :         prinbee::pbql::location const * loc(&n->get_location());
     471            1 :         CATCH_REQUIRE(loc->get_line() == 4);
     472            1 :         CATCH_REQUIRE(loc->get_column() == 2); // this is a known bug... the getc()+ungetc() generate a location bug
     473              : 
     474            1 :         n = lexer->get_next_token();
     475            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     476            1 :         CATCH_REQUIRE(n->get_string() == "TABLE");
     477            1 :         loc = &n->get_location();
     478            1 :         CATCH_REQUIRE(loc->get_line() == 4);
     479            1 :         CATCH_REQUIRE(loc->get_column() == 8);
     480              : 
     481            1 :         n = lexer->get_next_token();
     482            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     483            1 :         CATCH_REQUIRE(n->get_string() == "magic");
     484            1 :         loc = &n->get_location();
     485            1 :         CATCH_REQUIRE(loc->get_line() == 4);
     486            1 :         CATCH_REQUIRE(loc->get_column() == 35);
     487              : 
     488            1 :         n = lexer->get_next_token();
     489            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_SEMI_COLON);
     490            1 :         loc = &n->get_location();
     491            1 :         CATCH_REQUIRE(loc->get_line() == 4);
     492            1 :         CATCH_REQUIRE(loc->get_column() == 41); // same bug as above, we've read the ';' then did an ungetc() which does not correct the column
     493              : 
     494            1 :         n = lexer->get_next_token();
     495            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_EOF);
     496            1 :     }
     497           10 :     CATCH_END_SECTION()
     498              : 
     499           11 :     CATCH_START_SECTION("lexer: dash-dash comment")
     500              :     {
     501            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(
     502              :               "/* copyright notices\n"
     503              :               " * often go here\n"
     504              :               " */\n"
     505              :               "CREATE TABLE test ( -- list of columns below\n"
     506              :               "  name TEXT,\n"
     507              :               "  -- the name above should be limited in length\r\n"
     508              :               "  email TEXT, -- email should include an '@' character\n"
     509              :               "  address TEXT,\n"
     510              :               "-- comment from the start of the line\r"
     511              :               "  age INTEGER /* and C-like /* comments can be */ nested */\n"
     512              :               "); /*** multi-asterisks ***/\n"
     513              :               "-- vim: comment\n"
     514            1 :             , "./lexer-hash-comment.pbql"));
     515            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     516            1 :         lexer->set_input(in);
     517              : 
     518              :         // "CREATE TABLE test ("
     519            1 :         prinbee::pbql::node::pointer_t n(lexer->get_next_token());
     520            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     521            1 :         CATCH_REQUIRE(n->get_string() == "CREATE");
     522            1 :         prinbee::pbql::location const * loc(&n->get_location());
     523            1 :         CATCH_REQUIRE(loc->get_line() == 4);
     524            1 :         CATCH_REQUIRE(loc->get_column() == 1);
     525              : 
     526            1 :         n = lexer->get_next_token();
     527            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     528            1 :         CATCH_REQUIRE(n->get_string() == "TABLE");
     529            1 :         loc = &n->get_location();
     530            1 :         CATCH_REQUIRE(loc->get_line() == 4);
     531            1 :         CATCH_REQUIRE(loc->get_column() == 8);
     532              : 
     533            1 :         n = lexer->get_next_token();
     534            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     535            1 :         CATCH_REQUIRE(n->get_string() == "test");
     536            1 :         loc = &n->get_location();
     537            1 :         CATCH_REQUIRE(loc->get_line() == 4);
     538            1 :         CATCH_REQUIRE(loc->get_column() == 14);
     539              : 
     540            1 :         n = lexer->get_next_token();
     541            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_OPEN_PARENTHESIS);
     542            1 :         loc = &n->get_location();
     543            1 :         CATCH_REQUIRE(loc->get_line() == 4);
     544            1 :         CATCH_REQUIRE(loc->get_column() == 19);
     545              : 
     546              :         // "name TEXT,"
     547            1 :         n = lexer->get_next_token();
     548            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     549            1 :         CATCH_REQUIRE(n->get_string() == "name");
     550            1 :         loc = &n->get_location();
     551            1 :         CATCH_REQUIRE(loc->get_line() == 5);
     552            1 :         CATCH_REQUIRE(loc->get_column() == 3);
     553              : 
     554            1 :         n = lexer->get_next_token();
     555            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     556            1 :         CATCH_REQUIRE(n->get_string() == "TEXT");
     557            1 :         loc = &n->get_location();
     558            1 :         CATCH_REQUIRE(loc->get_line() == 5);
     559            1 :         CATCH_REQUIRE(loc->get_column() == 8);
     560              : 
     561            1 :         n = lexer->get_next_token();
     562            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_COMMA);
     563            1 :         loc = &n->get_location();
     564            1 :         CATCH_REQUIRE(loc->get_line() == 5);
     565            1 :         CATCH_REQUIRE(loc->get_column() == 13);
     566              : 
     567              :         // "email TEXT,"
     568            1 :         n = lexer->get_next_token();
     569            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     570            1 :         CATCH_REQUIRE(n->get_string() == "email");
     571            1 :         loc = &n->get_location();
     572            1 :         CATCH_REQUIRE(loc->get_line() == 7);
     573            1 :         CATCH_REQUIRE(loc->get_column() == 3);
     574              : 
     575            1 :         n = lexer->get_next_token();
     576            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     577            1 :         CATCH_REQUIRE(n->get_string() == "TEXT");
     578            1 :         loc = &n->get_location();
     579            1 :         CATCH_REQUIRE(loc->get_line() == 7);
     580            1 :         CATCH_REQUIRE(loc->get_column() == 9);
     581              : 
     582            1 :         n = lexer->get_next_token();
     583            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_COMMA);
     584            1 :         loc = &n->get_location();
     585            1 :         CATCH_REQUIRE(loc->get_line() == 7);
     586            1 :         CATCH_REQUIRE(loc->get_column() == 14);
     587              : 
     588              :         // "address TEXT,"
     589            1 :         n = lexer->get_next_token();
     590            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     591            1 :         CATCH_REQUIRE(n->get_string() == "address");
     592            1 :         loc = &n->get_location();
     593            1 :         CATCH_REQUIRE(loc->get_line() == 8);
     594            1 :         CATCH_REQUIRE(loc->get_column() == 3);
     595              : 
     596            1 :         n = lexer->get_next_token();
     597            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     598            1 :         CATCH_REQUIRE(n->get_string() == "TEXT");
     599            1 :         loc = &n->get_location();
     600            1 :         CATCH_REQUIRE(loc->get_line() == 8);
     601            1 :         CATCH_REQUIRE(loc->get_column() == 11);
     602              : 
     603            1 :         n = lexer->get_next_token();
     604            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_COMMA);
     605            1 :         loc = &n->get_location();
     606            1 :         CATCH_REQUIRE(loc->get_line() == 8);
     607            1 :         CATCH_REQUIRE(loc->get_column() == 16);
     608              : 
     609              :         // "age INTEGER"
     610            1 :         n = lexer->get_next_token();
     611            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     612            1 :         CATCH_REQUIRE(n->get_string() == "age");
     613            1 :         loc = &n->get_location();
     614            1 :         CATCH_REQUIRE(loc->get_line() == 10);
     615            1 :         CATCH_REQUIRE(loc->get_column() == 3);
     616              : 
     617            1 :         n = lexer->get_next_token();
     618            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_IDENTIFIER);
     619            1 :         CATCH_REQUIRE(n->get_string() == "INTEGER");
     620            1 :         loc = &n->get_location();
     621            1 :         CATCH_REQUIRE(loc->get_line() == 10);
     622            1 :         CATCH_REQUIRE(loc->get_column() == 7);
     623              : 
     624              :         // ");"
     625            1 :         n = lexer->get_next_token();
     626            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_CLOSE_PARENTHESIS);
     627            1 :         loc = &n->get_location();
     628            1 :         CATCH_REQUIRE(loc->get_line() == 11);
     629            1 :         CATCH_REQUIRE(loc->get_column() == 1);
     630              : 
     631            1 :         n = lexer->get_next_token();
     632            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_SEMI_COLON);
     633            1 :         loc = &n->get_location();
     634            1 :         CATCH_REQUIRE(loc->get_line() == 11);
     635            1 :         CATCH_REQUIRE(loc->get_column() == 2);
     636              : 
     637              :         // EOF
     638            1 :         n = lexer->get_next_token();
     639            1 :         CATCH_REQUIRE(n->get_token() == prinbee::pbql::token_t::TOKEN_EOF);
     640            1 :     }
     641           10 :     CATCH_END_SECTION()
     642            9 : }
     643              : 
     644              : 
     645           18 : CATCH_TEST_CASE("lexer_error", "[lexer] [pbql] [error]")
     646              : {
     647           20 :     CATCH_START_SECTION("lexer_error: missing input")
     648              :     {
     649            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     650            4 :         CATCH_REQUIRE_THROWS_MATCHES(
     651              :                   lexer->get_next_token()
     652              :                 , prinbee::logic_error
     653              :                 , Catch::Matchers::ExceptionMessage(
     654              :                           "logic_error: input missing."));
     655            1 :     }
     656           19 :     CATCH_END_SECTION()
     657              : 
     658           20 :     CATCH_START_SECTION("lexer_error: invalid string (EOF)")
     659              :     {
     660            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("'string not ended", "./lexer-bad-string.pbql"));
     661            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     662            1 :         lexer->set_input(in);
     663              : 
     664            4 :         CATCH_REQUIRE_THROWS_MATCHES(
     665              :                   lexer->get_next_token()
     666              :                 , prinbee::invalid_token
     667              :                 , Catch::Matchers::ExceptionMessage(
     668              :                           "prinbee_exception: unclosed string."));
     669            1 :     }
     670           19 :     CATCH_END_SECTION()
     671              : 
     672           20 :     CATCH_START_SECTION("lexer_error: invalid string (\\n)")
     673              :     {
     674            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("'string\ncut 1'", "./lexer-bad-string.pbql"));
     675            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     676            1 :         lexer->set_input(in);
     677              : 
     678            4 :         CATCH_REQUIRE_THROWS_MATCHES(
     679              :                   lexer->get_next_token()
     680              :                 , prinbee::invalid_token
     681              :                 , Catch::Matchers::ExceptionMessage(
     682              :                           "prinbee_exception: string cannot include a newline or carriage return character."));
     683            1 :     }
     684           19 :     CATCH_END_SECTION()
     685              : 
     686           20 :     CATCH_START_SECTION("lexer_error: invalid string (\\r)")
     687              :     {
     688            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("'string\rcut 2'", "./lexer-bad-string.pbql"));
     689            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     690            1 :         lexer->set_input(in);
     691              : 
     692            4 :         CATCH_REQUIRE_THROWS_MATCHES(
     693              :                   lexer->get_next_token()
     694              :                 , prinbee::invalid_token
     695              :                 , Catch::Matchers::ExceptionMessage(
     696              :                           "prinbee_exception: string cannot include a newline or carriage return character."));
     697            1 :     }
     698           19 :     CATCH_END_SECTION()
     699              : 
     700           20 :     CATCH_START_SECTION("lexer_error: invalid string (\\r\\n)")
     701              :     {
     702            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("'string\rcut 3'", "./lexer-bad-string.pbql"));
     703            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     704            1 :         lexer->set_input(in);
     705              : 
     706            4 :         CATCH_REQUIRE_THROWS_MATCHES(
     707              :                   lexer->get_next_token()
     708              :                 , prinbee::invalid_token
     709              :                 , Catch::Matchers::ExceptionMessage(
     710              :                           "prinbee_exception: string cannot include a newline or carriage return character."));
     711            1 :     }
     712           19 :     CATCH_END_SECTION()
     713              : 
     714           20 :     CATCH_START_SECTION("lexer_error: invalid string (escaped characters)")
     715              :     {
     716              :         struct invalid_escape_t
     717              :         {
     718              :             char const * const      f_invalid_sequence = nullptr;
     719              :             int                     f_count = 0;
     720              :             int                     f_expected_count = 0;
     721              :         };
     722            1 :         constexpr invalid_escape_t const invalid_escapes[] =
     723              :         {
     724              :             { "\\xvoid",        0, 2 },
     725              :             { "\\uvoid",        0, 4 },
     726              :             { "\\u1",           1, 4 },
     727              :             { "\\u21",          2, 4 },
     728              :             { "\\u311",         3, 4 },
     729              :             { "\\Uvoid",        0, 8 },
     730              :             { "\\U1",           1, 8 },
     731              :             { "\\U21",          2, 8 },
     732              :             { "\\U311",         3, 8 },
     733              :             { "\\U4111",        4, 8 },
     734              :             { "\\U51111",       5, 8 },
     735              :             { "\\U611111",      6, 8 },
     736              :             { "\\U7111111",     7, 8 },
     737              :         };
     738           14 :         for(auto const & e : invalid_escapes)
     739              :         {
     740           39 :             std::string str("e'str: ");
     741           13 :             str += e.f_invalid_sequence;
     742           13 :             str += '\'';
     743           13 :             prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(str, "./lexer-bad-escape-sequence.pbql"));
     744           13 :             prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     745           13 :             lexer->set_input(in);
     746              : 
     747           26 :             CATCH_REQUIRE_THROWS_MATCHES(
     748              :                       lexer->get_next_token()
     749              :                     , prinbee::invalid_token
     750              :                     , Catch::Matchers::ExceptionMessage(
     751              :                               "prinbee_exception: lexer::get_next_token() -- escape sequence needed "
     752              :                             + std::to_string(e.f_expected_count)
     753              :                             + " digits; found "
     754              :                             + std::to_string(e.f_count)
     755              :                             + " instead."));
     756           13 :         }
     757              :     }
     758           19 :     CATCH_END_SECTION()
     759              : 
     760           20 :     CATCH_START_SECTION("lexer_error: \\0 is not allowed in strings")
     761              :     {
     762              :         struct invalid_escape_t
     763              :         {
     764              :             char const * const      f_invalid_sequence = nullptr;
     765              :         };
     766            1 :         constexpr invalid_escape_t const invalid_escapes[] =
     767              :         {
     768              :             { "octal null \\0 -- size of 1"         },
     769              :             { "octal null \\00 -- size of 2"        },
     770              :             { "octal null \\000 -- size of 3"       },
     771              :             { "hexadecimal null \\x0 --size of 1"   },
     772              :             { "hexadecimal null \\x00 --size of 2"  },
     773              :             { "unicode \\u0000 -- size of 4"        },
     774              :             { "unicode \\U00000000 -- size of 8"    },
     775              :         };
     776            8 :         for(auto const & e : invalid_escapes)
     777              :         {
     778           21 :             std::string str("e'str: ");
     779            7 :             str += e.f_invalid_sequence;
     780            7 :             str += '\'';
     781            7 :             prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(str, "./lexer-bad-null-character.pbql"));
     782            7 :             prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     783            7 :             lexer->set_input(in);
     784              : 
     785           28 :             CATCH_REQUIRE_THROWS_MATCHES(
     786              :                       lexer->get_next_token()
     787              :                     , prinbee::unexpected_token
     788              :                     , Catch::Matchers::ExceptionMessage(
     789              :                               "prinbee_exception: lexer::get_next_token() -- the NULL character is not allowed in strings."));
     790            7 :         }
     791              :     }
     792           19 :     CATCH_END_SECTION()
     793              : 
     794           20 :     CATCH_START_SECTION("lexer_error: missing */")
     795              :     {
     796            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("/* C-Like comment must end with '*' and '/'", "./lexer-bad-c-comment.pbql"));
     797            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     798            1 :         lexer->set_input(in);
     799              : 
     800            4 :         CATCH_REQUIRE_THROWS_MATCHES(
     801              :                   lexer->get_next_token()
     802              :                 , prinbee::invalid_token
     803              :                 , Catch::Matchers::ExceptionMessage(
     804              :                           "prinbee_exception: end of script reached within a C-like comment (i.e. '*/' not found; depth: 1)."));
     805            1 :     }
     806           19 :     CATCH_END_SECTION()
     807              : 
     808           20 :     CATCH_START_SECTION("lexer_error: invalid floating point")
     809              :     {
     810            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("7041.03e", "./lexer-bad-string.pbql"));
     811            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     812            1 :         lexer->set_input(in);
     813              : 
     814            4 :         CATCH_REQUIRE_THROWS_MATCHES(
     815              :                   lexer->get_next_token()
     816              :                 , prinbee::invalid_number
     817              :                 , Catch::Matchers::ExceptionMessage(
     818              :                           "prinbee_exception: invalid floating point number (7041.03e)."));
     819            1 :     }
     820           19 :     CATCH_END_SECTION()
     821              : 
     822           20 :     CATCH_START_SECTION("lexer_error: empty binary number")
     823              :     {
     824            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("0b", "./lexer-bad-binary.pbql"));
     825            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     826            1 :         lexer->set_input(in);
     827              : 
     828            4 :         CATCH_REQUIRE_THROWS_MATCHES(
     829              :                   lexer->get_next_token()
     830              :                 , prinbee::invalid_number
     831              :                 , Catch::Matchers::ExceptionMessage(
     832              :                           "prinbee_exception: a binary number needs at least one digit."));
     833            1 :     }
     834           19 :     CATCH_END_SECTION()
     835              : 
     836           20 :     CATCH_START_SECTION("lexer_error: binary does not accept 8 or 9")
     837              :     {
     838            9 :         for(int digit(2); digit < 10; ++digit)
     839              :         {
     840            8 :             std::string const bin("0b" + std::to_string(digit));
     841            8 :             prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(bin, "./lexer-bad-binary.pbql"));
     842            8 :             prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     843            8 :             lexer->set_input(in);
     844              : 
     845           32 :             CATCH_REQUIRE_THROWS_MATCHES(
     846              :                       lexer->get_next_token()
     847              :                     , prinbee::invalid_number
     848              :                     , Catch::Matchers::ExceptionMessage(
     849              :                               "prinbee_exception: a binary number only supports binary digits (0 and 1)."));
     850            8 :         }
     851              :     }
     852           19 :     CATCH_END_SECTION()
     853              : 
     854           20 :     CATCH_START_SECTION("lexer_error: binary string not ending with quote")
     855              :     {
     856            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("b'101 missing closing quote", "./lexer-bad-binary.pbql"));
     857            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     858            1 :         lexer->set_input(in);
     859              : 
     860            4 :         CATCH_REQUIRE_THROWS_MATCHES(
     861              :                   lexer->get_next_token()
     862              :                 , prinbee::invalid_number
     863              :                 , Catch::Matchers::ExceptionMessage(
     864              :                           "prinbee_exception: a binary string needs to end with a quote (')."));
     865            1 :     }
     866           19 :     CATCH_END_SECTION()
     867              : 
     868           20 :     CATCH_START_SECTION("lexer_error: empty octal number")
     869              :     {
     870            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("0o", "./lexer-bad-octal.pbql"));
     871            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     872            1 :         lexer->set_input(in);
     873              : 
     874            4 :         CATCH_REQUIRE_THROWS_MATCHES(
     875              :                   lexer->get_next_token()
     876              :                 , prinbee::invalid_number
     877              :                 , Catch::Matchers::ExceptionMessage(
     878              :                           "prinbee_exception: an octal number needs at least one digit after the \"0o\"."));
     879            1 :     }
     880           19 :     CATCH_END_SECTION()
     881              : 
     882           20 :     CATCH_START_SECTION("lexer_error: octal does not accept 8 or 9")
     883              :     {
     884            1 :         char const * const bad_octal[] = {
     885              :             "0o8",
     886              :             "0o9",
     887              :         };
     888            3 :         for(int idx(0); idx < 2; ++idx)
     889              :         {
     890            2 :             prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(bad_octal[idx], "./lexer-bad-octal.pbql"));
     891            2 :             prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     892            2 :             lexer->set_input(in);
     893              : 
     894            8 :             CATCH_REQUIRE_THROWS_MATCHES(
     895              :                       lexer->get_next_token()
     896              :                     , prinbee::invalid_number
     897              :                     , Catch::Matchers::ExceptionMessage(
     898              :                               "prinbee_exception: an octal number cannot include digits 8 or 9."));
     899            2 :         }
     900              :     }
     901           19 :     CATCH_END_SECTION()
     902              : 
     903           20 :     CATCH_START_SECTION("lexer_error: octal string not ending with quote")
     904              :     {
     905            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("o'123 missing closing quote", "./lexer-bad-octal.pbql"));
     906            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     907            1 :         lexer->set_input(in);
     908              : 
     909            4 :         CATCH_REQUIRE_THROWS_MATCHES(
     910              :                   lexer->get_next_token()
     911              :                 , prinbee::invalid_number
     912              :                 , Catch::Matchers::ExceptionMessage(
     913              :                           "prinbee_exception: an octal string needs to end with a quote (')."));
     914            1 :     }
     915           19 :     CATCH_END_SECTION()
     916              : 
     917           20 :     CATCH_START_SECTION("lexer_error: empty hexadecimal number")
     918              :     {
     919            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("0x", "./lexer-bad-hexadecimal.pbql"));
     920            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     921            1 :         lexer->set_input(in);
     922              : 
     923            4 :         CATCH_REQUIRE_THROWS_MATCHES(
     924              :                   lexer->get_next_token()
     925              :                 , prinbee::invalid_number
     926              :                 , Catch::Matchers::ExceptionMessage(
     927              :                           "prinbee_exception: a hexadecimal number needs at least one digit after the \"0x\"."));
     928            1 :     }
     929           19 :     CATCH_END_SECTION()
     930              : 
     931           20 :     CATCH_START_SECTION("lexer_error: hexadecimal string not ending with quote")
     932              :     {
     933            1 :         prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>("x'f1a3 missing closing quote", "./lexer-bad-hexadecimal.pbql"));
     934            1 :         prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     935            1 :         lexer->set_input(in);
     936              : 
     937            4 :         CATCH_REQUIRE_THROWS_MATCHES(
     938              :                   lexer->get_next_token()
     939              :                 , prinbee::invalid_number
     940              :                 , Catch::Matchers::ExceptionMessage(
     941              :                           "prinbee_exception: a hexadecimal string needs to end with a quote (')."));
     942            1 :     }
     943           19 :     CATCH_END_SECTION()
     944              : 
     945           20 :     CATCH_START_SECTION("lexer_error: unsupported characters")
     946              :     {
     947            1 :         char const unsupported_characters[] = {
     948              :             '`',
     949              :             '!',
     950              :             '$',
     951              :             '{',
     952              :             '}',
     953              :             '"',
     954              :             '?',
     955              :         };
     956              : 
     957           16 :         for(std::size_t idx(0); idx < std::size(unsupported_characters); ++idx)
     958              :         {
     959            7 :             char const buf[2] = {
     960            7 :                 unsupported_characters[idx],
     961              :                 '\0',
     962            7 :             };
     963            7 :             prinbee::pbql::input::pointer_t in(std::make_shared<prinbee::pbql::input>(buf, "./lexer-bad-character.pbql"));
     964            7 :             prinbee::pbql::lexer::pointer_t lexer(std::make_shared<prinbee::pbql::lexer>());
     965            7 :             lexer->set_input(in);
     966              : 
     967           28 :             CATCH_REQUIRE_THROWS_MATCHES(
     968              :                       lexer->get_next_token()
     969              :                     , prinbee::unexpected_token
     970              :                     , Catch::Matchers::ExceptionMessage(
     971              :                               std::string("prinbee_exception: unexpected token (")
     972              :                             + buf
     973              :                             + ")."));
     974            7 :         }
     975              :     }
     976           19 :     CATCH_END_SECTION()
     977           18 : }
     978              : 
     979              : 
     980              : 
     981              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

Snap C++ | List of projects | List of versions