LCOV - code coverage report
Current view: top level - tests - catch_lexer.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 3503 3507 99.9 %
Date: 2023-11-01 21:56:19 Functions: 20 20 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2015-2022  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/csspp
       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 2 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 along
      17             : // with this program; if not, write to the Free Software Foundation, Inc.,
      18             : // 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      19             : 
      20             : /** \file
      21             :  * \brief Test the lexer.cpp file.
      22             :  *
      23             :  * This test runs a battery of tests agains the lexer.cpp file to ensure
      24             :  * full coverage and many edge cases as expected by CSS 3.
      25             :  *
      26             :  * Note that CSS 3 is fully compatible with CSS 1 and 2.1. However, it does
      27             :  * not support exactly the same character sets. Also our version only supports
      28             :  * UTF-8 as input.
      29             :  */
      30             : 
      31             : // csspp
      32             : //
      33             : #include    <csspp/exception.h>
      34             : #include    <csspp/lexer.h>
      35             : #include    <csspp/unicode_range.h>
      36             : 
      37             : 
      38             : // self
      39             : //
      40             : #include    "catch_main.h"
      41             : 
      42             : 
      43             : // C++
      44             : //
      45             : #include    <iomanip>
      46             : #include    <iostream>
      47             : #include    <sstream>
      48             : 
      49             : 
      50             : // C
      51             : //
      52             : #include    <math.h>
      53             : #include    <string.h>
      54             : 
      55             : 
      56             : // last include
      57             : //
      58             : #include    <snapdev/poison.h>
      59             : 
      60             : 
      61             : 
      62             : namespace
      63             : {
      64             : 
      65             : } // no name namespace
      66             : 
      67             : 
      68             : 
      69             : 
      70           1 : CATCH_TEST_CASE("UTF-8 conversions", "[lexer] [unicode]")
      71             : {
      72           1 :     std::stringstream ss;
      73           3 :     csspp::position pos("test.css");
      74           1 :     csspp::lexer l(ss, pos);
      75             : 
      76             :     // first check all valid characters encoding
      77     1114113 :     for(csspp::wide_char_t i(0); i < 0x110000; ++i)
      78             :     {
      79     1114112 :         if((i >= 0xD000 && i <= 0xDFFF) // surrogate
      80     1110016 :         || (i & 0xFFFF) == 0xFFFE       // invalid
      81     1109999 :         || (i & 0xFFFF) == 0xFFFF)      // invalid
      82             :         {
      83        4130 :             continue;
      84             :         }
      85     1109982 :         std::string const str(l.wctomb(i));
      86     1109982 :         csspp::wide_char_t const wc(l.mbtowc(str.c_str()));
      87             : 
      88     1109982 :         CATCH_REQUIRE(wc == i);
      89     1109982 :     }
      90             : 
      91             :     // make sure the test for the buffer size works as expected
      92           6 :     for(size_t i(0); i < 5; ++i)
      93             :     {
      94           5 :         char buf[6];
      95           5 :         csspp::wide_char_t const wc(rand() % 0x1FFFFF);
      96           5 :         CATCH_REQUIRE_THROWS_AS(l.wctomb(wc, buf, i), csspp::csspp_exception_overflow);
      97             :     }
      98             : 
      99             :     // make sure surrogates are not allowed
     100        2049 :     for(csspp::wide_char_t i(0xD800); i <= 0xDFFF; ++i)
     101             :     {
     102        2048 :         char buf[6];
     103        2048 :         buf[0] = '?';
     104        2048 :         l.wctomb(i, buf, sizeof(buf) / sizeof(buf[0]));
     105        2048 :         CATCH_REQUIRE(buf[0] == '\0'); // make sure we get an empty string on errors
     106        2048 :         VERIFY_ERRORS("test.css(1): error: surrogate characters cannot be encoded in UTF-8.\n");
     107             :     }
     108             : 
     109             :     // page 0 -- error is slightly different
     110             :     {
     111           1 :         char buf[6];
     112             :         // test FFFE
     113           1 :         buf[0] = '?';
     114           1 :         l.wctomb(0xFFFE, buf, sizeof(buf) / sizeof(buf[0]));
     115           1 :         CATCH_REQUIRE(buf[0] == '\0'); // make sure we get an empty string on errors
     116           1 :         VERIFY_ERRORS("test.css(1): error: characters 0xFFFE and 0xFFFF are not valid.\n");
     117             : 
     118             :         // test FFFF
     119           1 :         buf[0] = '?';
     120           1 :         l.wctomb(0xFFFF, buf, sizeof(buf) / sizeof(buf[0]));
     121           1 :         CATCH_REQUIRE(buf[0] == '\0'); // make sure we get an empty string on errors
     122           1 :         VERIFY_ERRORS("test.css(1): error: characters 0xFFFE and 0xFFFF are not valid.\n");
     123             :     }
     124             : 
     125             :     // page 1 to 16
     126          17 :     for(csspp::wide_char_t page(1); page <= 0x10; ++page)
     127             :     {
     128          16 :         char buf[6];
     129             :         // test <page>FFFE
     130          16 :         csspp::wide_char_t wc((page << 16) | 0xFFFE);
     131          16 :         buf[0] = '?';
     132          16 :         l.wctomb(wc, buf, sizeof(buf) / sizeof(buf[0]));
     133          16 :         CATCH_REQUIRE(buf[0] == '\0'); // make sure we get an empty string on errors
     134          16 :         VERIFY_ERRORS("test.css(1): error: any characters that end with 0xFFFE or 0xFFFF are not valid.\n");
     135             : 
     136             :         // test <page>FFFF
     137          16 :         wc = (page << 16) | 0xFFFF;
     138          16 :         buf[0] = '?';
     139          16 :         l.wctomb(wc, buf, sizeof(buf) / sizeof(buf[0]));
     140          16 :         CATCH_REQUIRE(buf[0] == '\0'); // make sure we get an empty string on errors
     141          16 :         VERIFY_ERRORS("test.css(1): error: any characters that end with 0xFFFE or 0xFFFF are not valid.\n");
     142             :     }
     143             : 
     144             :     // test 1,000 characters with a number that's too large
     145        1001 :     for(int i(0); i < 1000; ++i)
     146             :     {
     147        1000 :         csspp::wide_char_t wc(0);
     148             :         do
     149             :         {
     150             :             // make sure we get a 32 bit value, hitting all possible bits
     151        1000 :             wc = rand() ^ (rand() << 16);
     152             :         }
     153        1000 :         while(static_cast<csspp::wide_uchar_t>(wc) < 0x110000);
     154        1000 :         char buf[6];
     155        1000 :         buf[0] = '?';
     156        1000 :         l.wctomb(wc, buf, sizeof(buf) / sizeof(buf[0]));
     157        1000 :         CATCH_REQUIRE(buf[0] == '\0'); // make sure we get an empty string on errors
     158        1000 :         VERIFY_ERRORS("test.css(1): error: character too large, it cannot be encoded in UTF-8.\n");
     159             :     }
     160             : 
     161             :     // check that bytes 0xF8 and over generate an error
     162           9 :     for(int i(0xF8); i < 0x100; ++i)
     163             :     {
     164           8 :         char buf[6];
     165           8 :         buf[0] = i;
     166           8 :         buf[1] = static_cast<char>(0x80);
     167           8 :         buf[2] = static_cast<char>(0x80);
     168           8 :         buf[3] = static_cast<char>(0x80);
     169           8 :         buf[4] = static_cast<char>(0x80);
     170           8 :         buf[5] = 0;
     171           8 :         CATCH_REQUIRE(l.mbtowc(buf) == 0xFFFD);
     172           8 :         std::stringstream errmsg;
     173           8 :         errmsg << "test.css(1): error: byte U+" << std::hex << i << " not valid in a UTF-8 stream.\n";
     174           8 :         VERIFY_ERRORS(errmsg.str());
     175           8 :     }
     176             : 
     177             :     // continuation bytes at the start
     178          65 :     for(int i(0x80); i < 0xC0; ++i)
     179             :     {
     180          64 :         char buf[6];
     181          64 :         buf[0] = i;
     182          64 :         buf[1] = static_cast<char>(0x80);
     183          64 :         buf[2] = static_cast<char>(0x80);
     184          64 :         buf[3] = static_cast<char>(0x80);
     185          64 :         buf[4] = static_cast<char>(0x80);
     186          64 :         buf[5] = 0;
     187          64 :         CATCH_REQUIRE(l.mbtowc(buf) == 0xFFFD);
     188          64 :         std::stringstream errmsg;
     189          64 :         errmsg << "test.css(1): error: byte U+" << std::hex << i << " not valid to introduce a UTF-8 encoded character.\n";
     190          64 :         VERIFY_ERRORS(errmsg.str());
     191          64 :     }
     192             : 
     193             :     // not enough bytes ('\0' too soon)
     194          57 :     for(int i(0xC0); i < 0xF8; ++i)
     195             :     {
     196          56 :         char buf[6];
     197          56 :         buf[0] = i;
     198          56 :         buf[1] = 0;
     199          56 :         CATCH_REQUIRE(l.mbtowc(buf) == 0xFFFD);
     200          56 :         std::stringstream errmsg;
     201          56 :         errmsg << "test.css(1): error: sequence of bytes too short to represent a valid UTF-8 encoded character.\n";
     202          56 :         VERIFY_ERRORS(errmsg.str());
     203          56 :     }
     204             : 
     205             :     // too many bytes ('\0' missing), and
     206             :     // invalid sequence (a char replace with an invalid code)
     207     1113985 :     for(csspp::wide_char_t i(128); i < 0x110000; ++i)
     208             :     {
     209     1113984 :         if((i >= 0xD000 && i <= 0xDFFF) // surrogate
     210     1109888 :         || (i & 0xFFFF) == 0xFFFE       // invalid
     211     1109871 :         || (i & 0xFFFF) == 0xFFFF)      // invalid
     212             :         {
     213        4130 :             continue;
     214             :         }
     215     1109854 :         std::string const str(l.wctomb(i));
     216             : 
     217             :         // the sequence is one too many bytes
     218             :         {
     219     1109854 :             std::string const too_long(str + static_cast<char>(rand() % 64 + 0x80)); // add one byte
     220     1109854 :             CATCH_REQUIRE(l.mbtowc(too_long.c_str()) == 0xFFFD);
     221     1109854 :             std::stringstream errmsg;
     222     1109854 :             errmsg << "test.css(1): error: sequence of bytes too long, it cannot represent a valid UTF-8 encoded character.\n";
     223     1109854 :             VERIFY_ERRORS(errmsg.str());
     224     1109854 :         }
     225             : 
     226             :         // one of the bytes in the sequence is not between 0x80 and 0xBF
     227             :         // this only works if the sequence is at least 2 bytes
     228             :         // (i.e. that starts when 'i' is 128 or more)
     229             :         {
     230     1109854 :             size_t const p(rand() % (str.length() - 1));
     231     1109854 :             if(p + 1 >= str.length())
     232             :             {
     233           0 :                 std::cerr << "computed " << p + 1 << " for a string of length " << str.length() << "\n";
     234           0 :                 throw std::logic_error("test computed a position that's out of range.");
     235             :             }
     236     1109854 :             std::string wrong_sequence(str);
     237     1109854 :             int c(rand() % (255 - 64) + 1); // '\0' is not good for this test
     238     1109854 :             if(c >= 0x80)
     239             :             {
     240      371475 :                 c += 64;
     241             :             }
     242     1109854 :             wrong_sequence[p + 1] = static_cast<char>(c);
     243     1109854 :             CATCH_REQUIRE(l.mbtowc(wrong_sequence.c_str()) == 0xFFFD);
     244     1109854 :             std::stringstream errmsg;
     245     1109854 :             errmsg << "test.css(1): error: invalid sequence of bytes, it cannot represent a valid UTF-8 encoded character.\n";
     246     1109854 :             VERIFY_ERRORS(errmsg.str());
     247     1109854 :         }
     248     1109854 :     }
     249             : 
     250             :     // no error left over
     251           1 :     VERIFY_ERRORS("");
     252           2 : }
     253             : 
     254           1 : CATCH_TEST_CASE("Invalid characters", "[lexer] [invalid]")
     255             : {
     256             :     // 0xFFFD
     257             :     {
     258           1 :         std::stringstream ss;
     259           3 :         csspp::position pos("test.css");
     260           1 :         csspp::lexer l(ss, pos);
     261           1 :         ss << l.wctomb(0xFFFD);
     262             : 
     263             :         // so far, no error
     264           1 :         VERIFY_ERRORS("");
     265             : 
     266             :         // EOF
     267           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     268             : 
     269             :         // make sure we got the expected error
     270           1 :         VERIFY_ERRORS("test.css(1): error: invalid input character: U+fffd.\n");
     271           1 :     }
     272             : 
     273             :     // '\0'
     274             :     {
     275           1 :         std::stringstream ss;
     276           1 :         ss << '\0';
     277           3 :         csspp::position pos("test.css");
     278           1 :         csspp::lexer l(ss, pos);
     279             : 
     280             :         // so far, no error
     281           1 :         VERIFY_ERRORS("");
     282             : 
     283             :         // EOF
     284           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     285             : 
     286             :         // make sure we got the expected error
     287           1 :         VERIFY_ERRORS("test.css(1): error: invalid input character: U+fffd.\n");
     288           1 :     }
     289             : 
     290             :     // '^', '<', etc.
     291         128 :     for(int i(1); i < 128; ++i)
     292             :     {
     293             :         // test all that are invalid so we have an if to skip on all
     294             :         // characters that can represent the beginning of a valid token
     295         127 :         if(
     296             :             // whitespace
     297             :                i != ' '
     298         126 :             && i != '\t'
     299         125 :             && i != '\n'
     300         124 :             && i != '\r'
     301         123 :             && i != '\f'
     302             :             // identifiers / numbers
     303         122 :             && (i < '0' || i > '9')
     304         112 :             && (i < 'A' || i > 'Z')
     305          86 :             && (i < 'a' || i > 'z')
     306          60 :             && i != '_'
     307          59 :             && i != '-'
     308          58 :             && i != '+'
     309          57 :             && i != '.'
     310          56 :             && i != '\\'
     311          55 :             && i != '@'
     312          54 :             && i != '#'
     313             :             // delimiters
     314          53 :             && i != '*'
     315          52 :             && i != '/'
     316          51 :             && i != '='
     317          50 :             && i != '('
     318          49 :             && i != ')'
     319          48 :             && i != '{'
     320          47 :             && i != '}'
     321          46 :             && i != '['
     322          45 :             && i != ']'
     323          44 :             && i != ','
     324          43 :             && i != ';'
     325          42 :             && i != ':'
     326          41 :             && i != '<'
     327          40 :             && i != '>'
     328          39 :             && i != '$'
     329          38 :             && i != '!'
     330          37 :             && i != '|'
     331          36 :             && i != '&'
     332          35 :             && i != '~'
     333          34 :             && i != '%'
     334          33 :             && i != '?'
     335             :             // string
     336          32 :             && i != '"'
     337          31 :             && i != '\''
     338             :         )
     339             :         {
     340          30 :             std::stringstream ss;
     341          30 :             ss << static_cast<char>(i);
     342          90 :             csspp::position pos("test.css");
     343          30 :             csspp::lexer l(ss, pos);
     344             : 
     345             :             // so far, no error
     346          30 :             VERIFY_ERRORS("");
     347             : 
     348             :             // EOF
     349          30 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     350             : 
     351             :             // make sure we got the expected error
     352          30 :             std::stringstream errmsg;
     353          30 :             errmsg << "test.css(1): error: invalid input character: U+" << std::hex << static_cast<int>(i) << "." << std::endl;
     354          30 :             VERIFY_ERRORS(errmsg.str());
     355          30 :         }
     356             :     }
     357             : 
     358             :     // invalid UTF-8 sequence (i.e. too long)
     359             :     {
     360           1 :         std::stringstream ss;
     361             :         ss << static_cast<char>(0xF7)
     362             :            << static_cast<char>(0x80)
     363             :            << static_cast<char>(0x80)
     364             :            << static_cast<char>(0x80)
     365             :            << static_cast<char>(0x80)
     366             :            << static_cast<char>(0x80)
     367             :            << static_cast<char>(0x80)
     368             :            << static_cast<char>(0x80)
     369           1 :            << static_cast<char>(0x80);
     370           3 :         csspp::position pos("test.css");
     371           1 :         csspp::lexer l(ss, pos);
     372             : 
     373             :         // so far, no error
     374           1 :         VERIFY_ERRORS("");
     375             : 
     376           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     377             : 
     378             :         // make sure we got the expected errors
     379           1 :         VERIFY_ERRORS(
     380             :                 "test.css(1): error: too many follow bytes, it cannot represent a valid UTF-8 character.\n"
     381             :                 "test.css(1): error: invalid input character: U+fffd.\n"
     382             :             );
     383           1 :     }
     384             : 
     385             :     // invalid UTF-8 sequence (i.e. too long, followed by a comment and a string)
     386             :     {
     387           1 :         std::stringstream ss;
     388             :         ss << static_cast<char>(0xF7)
     389             :            << static_cast<char>(0x80)
     390             :            << static_cast<char>(0x80)
     391             :            << static_cast<char>(0x80)
     392             :            << static_cast<char>(0x80)
     393             :            << static_cast<char>(0x80)
     394             :            << static_cast<char>(0x80)
     395             :            << static_cast<char>(0x80)
     396             :            << static_cast<char>(0x80)
     397             :            << "// plus a comment to @preserve\r\n"
     398           1 :            << "' and  a  string '";
     399           3 :         csspp::position pos("test.css");
     400           1 :         csspp::lexer l(ss, pos);
     401             : 
     402             :         // so far, no error
     403           1 :         VERIFY_ERRORS("");
     404             : 
     405             :         // comment
     406             :         {
     407           1 :             csspp::node::pointer_t comment(l.next_token());
     408           1 :             CATCH_REQUIRE(comment->is(csspp::node_type_t::COMMENT));
     409           1 :             CATCH_REQUIRE(comment->get_string() == "plus a comment to @preserve");
     410           1 :             CATCH_REQUIRE(comment->get_integer() == 0); // C++ comment
     411           1 :             csspp::position const & npos(comment->get_position());
     412           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
     413           1 :             CATCH_REQUIRE(npos.get_page() == 1);
     414           1 :             CATCH_REQUIRE(npos.get_line() == 1);
     415           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
     416             : 
     417             :             // make sure we got the expected errors
     418           1 :             VERIFY_ERRORS(
     419             :                     "test.css(1): error: too many follow bytes, it cannot represent a valid UTF-8 character.\n"
     420             :                     "test.css(1): error: invalid input character: U+fffd.\n"
     421             :                     "test.css(1): warning: C++ comments should not be preserved as they are not supported by most CSS parsers.\n"
     422             :                 );
     423           1 :         }
     424             : 
     425             :         // whitespace
     426             :         {
     427           1 :             csspp::node::pointer_t whitespace(l.next_token());
     428           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
     429           1 :             csspp::position const & npos(whitespace->get_position());
     430           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
     431           1 :             CATCH_REQUIRE(npos.get_page() == 1);
     432           1 :             CATCH_REQUIRE(npos.get_line() == 2);
     433           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
     434           1 :         }
     435             : 
     436             :         // string
     437             :         {
     438           1 :             csspp::node::pointer_t string(l.next_token());
     439           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::STRING));
     440           1 :             CATCH_REQUIRE(string->get_string() == " and  a  string ");
     441           1 :             csspp::position const & npos(string->get_position());
     442           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
     443           1 :             CATCH_REQUIRE(npos.get_page() == 1);
     444           1 :             CATCH_REQUIRE(npos.get_line() == 2);
     445           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
     446           1 :         }
     447             : 
     448             :         // EOF
     449           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     450           1 :     }
     451             : 
     452             :     // invalid UTF-8 sequence (i.e. incorrect introducer)
     453          66 :     for(int i(0x80); i <= 0xC0; ++i)
     454             :     {
     455             :         // ends with EOF
     456             :         {
     457          65 :             std::stringstream ss;
     458          64 :             ss << static_cast<char>(i == 0xC0 ? 0xFF : i)
     459             :                << static_cast<char>(0x80)
     460             :                << static_cast<char>(0x80)
     461             :                << static_cast<char>(0x80)
     462             :                << static_cast<char>(0x80)
     463             :                << static_cast<char>(0x80)
     464             :                << static_cast<char>(0x80)
     465             :                << static_cast<char>(0x80)
     466          65 :                << static_cast<char>(0x80);
     467         195 :             csspp::position pos("test.css");
     468          65 :             csspp::lexer l(ss, pos);
     469             : 
     470             :             // so far, no error
     471          65 :             VERIFY_ERRORS("");
     472             : 
     473          65 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     474             : 
     475             :             // make sure we got the expected errors
     476          65 :             std::stringstream errmsg;
     477          65 :             errmsg << "test.css(1): error: unexpected byte in input buffer: U+"
     478          65 :                    << std::hex << (i == 0xC0 ? 0xFF : i)
     479          65 :                    << ".\ntest.css(1): error: invalid input character: U+fffd.\n";
     480          65 :             VERIFY_ERRORS(errmsg.str());
     481          65 :         }
     482             : 
     483             :         // ends with comment and string
     484             :         {
     485          65 :             std::stringstream ss;
     486          64 :             ss << static_cast<char>(i == 0xC0 ? 0xFF : i)
     487             :                << static_cast<char>(0x80)
     488             :                << static_cast<char>(0x80)
     489             :                << static_cast<char>(0x80)
     490             :                << static_cast<char>(0x80)
     491             :                << static_cast<char>(0x80)
     492             :                << static_cast<char>(0x80)
     493             :                << static_cast<char>(0x80)
     494             :                << static_cast<char>(0x80)
     495             :                << "// plus one comment to @preserve\r\n"
     496          65 :                << "' and  that  string '";
     497         195 :             csspp::position pos("test.css");
     498          65 :             csspp::lexer l(ss, pos);
     499             : 
     500             :             // so far, no error
     501          65 :             VERIFY_ERRORS("");
     502             : 
     503             :             // comment
     504             :             {
     505          65 :                 csspp::node::pointer_t comment(l.next_token());
     506          65 :                 CATCH_REQUIRE(comment->is(csspp::node_type_t::COMMENT));
     507          65 :                 CATCH_REQUIRE(comment->get_string() == "plus one comment to @preserve");
     508          65 :                 CATCH_REQUIRE(comment->get_integer() == 0); // C++ comment
     509          65 :                 csspp::position const & npos(comment->get_position());
     510          65 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
     511          65 :                 CATCH_REQUIRE(npos.get_page() == 1);
     512          65 :                 CATCH_REQUIRE(npos.get_line() == 1);
     513          65 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
     514          65 :             }
     515             : 
     516             :             // whitespace
     517             :             {
     518          65 :                 csspp::node::pointer_t whitespace(l.next_token());
     519          65 :                 CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
     520          65 :                 csspp::position const & npos(whitespace->get_position());
     521          65 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
     522          65 :                 CATCH_REQUIRE(npos.get_page() == 1);
     523          65 :                 CATCH_REQUIRE(npos.get_line() == 2);
     524          65 :                 CATCH_REQUIRE(npos.get_total_line() == 2);
     525          65 :             }
     526             : 
     527             :             // string
     528             :             {
     529          65 :                 csspp::node::pointer_t string(l.next_token());
     530          65 :                 CATCH_REQUIRE(string->is(csspp::node_type_t::STRING));
     531          65 :                 CATCH_REQUIRE(string->get_string() == " and  that  string ");
     532          65 :                 csspp::position const & npos(string->get_position());
     533          65 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
     534          65 :                 CATCH_REQUIRE(npos.get_page() == 1);
     535          65 :                 CATCH_REQUIRE(npos.get_line() == 2);
     536          65 :                 CATCH_REQUIRE(npos.get_total_line() == 2);
     537          65 :             }
     538             : 
     539             :             // EOF
     540          65 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     541             : 
     542             :             // make sure we got the expected errors
     543          65 :             std::stringstream errmsg;
     544          65 :             errmsg << "test.css(1): error: unexpected byte in input buffer: U+"
     545          65 :                    << std::hex << (i == 0xC0 ? 0xFF : i)
     546             :                    << ".\ntest.css(1): error: invalid input character: U+fffd.\n"
     547          65 :                    << "test.css(1): warning: C++ comments should not be preserved as they are not supported by most CSS parsers.\n";
     548          65 :             VERIFY_ERRORS(errmsg.str());
     549          65 :         }
     550             :     }
     551             : 
     552             :     // no error left over
     553           1 :     VERIFY_ERRORS("");
     554           1 : }
     555             : 
     556           1 : CATCH_TEST_CASE("Simple tokens", "[lexer] [basics] [delimiters]")
     557             : {
     558             :     // ' ' -> WHITESPACE
     559             :     {
     560           1 :         char const * whitespaces = " \t\n\r\f";
     561           1 :         size_t len(strlen(whitespaces));
     562             : 
     563             :         // try evey single whitespace by itself
     564           6 :         for(size_t i(0); i < len; ++i)
     565             :         {
     566           5 :             std::stringstream ss;
     567          15 :             csspp::position pos("test.css");
     568           5 :             csspp::lexer l(ss, pos);
     569           5 :             ss << whitespaces[i];
     570             : 
     571             :             // so far, no error
     572           5 :             VERIFY_ERRORS("");
     573             : 
     574           5 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::WHITESPACE));
     575           5 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     576             : 
     577             :             // make sure we got the expected error
     578           5 :             VERIFY_ERRORS("");
     579           5 :         }
     580             : 
     581             :         // try 1,000 combo of 3 to 12 whitespaces
     582        1001 :         for(size_t i(0); i < 1000; ++i)
     583             :         {
     584        1000 :             std::stringstream ss;
     585        3000 :             csspp::position pos("test.css");
     586        1000 :             csspp::lexer l(ss, pos);
     587        1000 :             size_t const count(rand() % 10 + 3);
     588        8542 :             for(size_t j(0); j < count; ++j)
     589             :             {
     590        7542 :                 int const k(rand() % len);
     591        7542 :                 ss << whitespaces[k];
     592             :             }
     593             : 
     594             :             // so far, no error
     595        1000 :             VERIFY_ERRORS("");
     596             : 
     597        1000 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::WHITESPACE));
     598        1000 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     599             : 
     600             :             // make sure we got the expected error
     601        1000 :             VERIFY_ERRORS("");
     602        1000 :         }
     603             :     }
     604             : 
     605             :     // '=' -> EQUAL
     606             :     {
     607           1 :         std::stringstream ss;
     608           3 :         csspp::position pos("test.css");
     609           1 :         csspp::lexer l(ss, pos);
     610           1 :         ss << "=";
     611             : 
     612             :         // so far, no error
     613           1 :         VERIFY_ERRORS("");
     614             : 
     615           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EQUAL));
     616           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     617             : 
     618             :         // make sure we got the expected error
     619           1 :         VERIFY_ERRORS("");
     620           1 :     }
     621             : 
     622             :     // '==' -> EQUAL + warning
     623             :     {
     624           1 :         std::stringstream ss;
     625           3 :         csspp::position pos("test.css");
     626           1 :         csspp::lexer l(ss, pos);
     627           1 :         ss << "==";
     628             : 
     629             :         // so far, no error
     630           1 :         VERIFY_ERRORS("");
     631             : 
     632           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EQUAL));
     633           1 :         VERIFY_ERRORS("test.css(1): warning: we accepted '==' instead of '=' in an expression, you probably want to change the operator to just '=', though.\n");
     634           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     635             : 
     636             :         // make sure we got the expected error
     637           1 :         VERIFY_ERRORS("");
     638           1 :     }
     639             : 
     640             :     // ',' -> COMMA
     641             :     {
     642           1 :         std::stringstream ss;
     643           3 :         csspp::position pos("test.css");
     644           1 :         csspp::lexer l(ss, pos);
     645           1 :         ss << ",";
     646             : 
     647             :         // so far, no error
     648           1 :         VERIFY_ERRORS("");
     649             : 
     650           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::COMMA));
     651           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     652             : 
     653             :         // make sure we got the expected error
     654           1 :         VERIFY_ERRORS("");
     655           1 :     }
     656             : 
     657             :     // ':' -> COLON
     658             :     {
     659           1 :         std::stringstream ss;
     660           3 :         csspp::position pos("test.css");
     661           1 :         csspp::lexer l(ss, pos);
     662           1 :         ss << ":";
     663             : 
     664             :         // so far, no error
     665           1 :         VERIFY_ERRORS("");
     666             : 
     667           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::COLON));
     668           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     669             : 
     670             :         // make sure we got the expected error
     671           1 :         VERIFY_ERRORS("");
     672           1 :     }
     673             : 
     674             :     // ';' -> SEMICOLON
     675             :     {
     676           1 :         std::stringstream ss;
     677           3 :         csspp::position pos("test.css");
     678           1 :         csspp::lexer l(ss, pos);
     679           1 :         ss << ";";
     680             : 
     681             :         // so far, no error
     682           1 :         VERIFY_ERRORS("");
     683             : 
     684           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::SEMICOLON));
     685           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     686             : 
     687             :         // make sure we got the expected error
     688           1 :         VERIFY_ERRORS("");
     689           1 :     }
     690             : 
     691             :     // '.' -> PERIOD
     692             :     {
     693           1 :         std::stringstream ss;
     694           3 :         csspp::position pos("test.css");
     695           1 :         csspp::lexer l(ss, pos);
     696           1 :         ss << ".";
     697             : 
     698             :         // so far, no error
     699           1 :         VERIFY_ERRORS("");
     700             : 
     701           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::PERIOD));
     702           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     703             : 
     704             :         // make sure we got the expected error
     705           1 :         VERIFY_ERRORS("");
     706           1 :     }
     707             : 
     708             :     // '$' -> DOLLAR
     709             :     {
     710           1 :         std::stringstream ss;
     711           3 :         csspp::position pos("test.css");
     712           1 :         csspp::lexer l(ss, pos);
     713           1 :         ss << "$";
     714             : 
     715             :         // so far, no error
     716           1 :         VERIFY_ERRORS("");
     717             : 
     718           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::DOLLAR));
     719           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     720             : 
     721             :         // make sure we got the expected error
     722           1 :         VERIFY_ERRORS("");
     723           1 :     }
     724             : 
     725             :     // '?' -> CONDITIONAL
     726             :     {
     727           1 :         std::stringstream ss;
     728           3 :         csspp::position pos("test.css");
     729           1 :         csspp::lexer l(ss, pos);
     730           1 :         ss << "?";
     731             : 
     732             :         // so far, no error
     733           1 :         VERIFY_ERRORS("");
     734             : 
     735           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::CONDITIONAL));
     736           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     737             : 
     738             :         // make sure we got the expected error
     739           1 :         VERIFY_ERRORS("");
     740           1 :     }
     741             : 
     742             :     // '%' -> MODULO
     743             :     {
     744           1 :         std::stringstream ss;
     745           3 :         csspp::position pos("test.css");
     746           1 :         csspp::lexer l(ss, pos);
     747           1 :         ss << "%";
     748             : 
     749             :         // so far, no error
     750           1 :         VERIFY_ERRORS("");
     751             : 
     752           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::MODULO));
     753           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     754             : 
     755             :         // make sure we got the expected error
     756           1 :         VERIFY_ERRORS("");
     757           1 :     }
     758             : 
     759             :     // '/' -> DIVIDE
     760             :     {
     761           1 :         std::stringstream ss;
     762           3 :         csspp::position pos("test.css");
     763           1 :         csspp::lexer l(ss, pos);
     764           1 :         ss << "/";
     765             : 
     766             :         // so far, no error
     767           1 :         VERIFY_ERRORS("");
     768             : 
     769           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::DIVIDE));
     770           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     771             : 
     772             :         // make sure we got the expected error
     773           1 :         VERIFY_ERRORS("");
     774           1 :     }
     775             : 
     776             :     // '*' -> MULTIPLY
     777             :     {
     778           1 :         std::stringstream ss;
     779           3 :         csspp::position pos("test.css");
     780           1 :         csspp::lexer l(ss, pos);
     781           1 :         ss << "*";
     782             : 
     783             :         // so far, no error
     784           1 :         VERIFY_ERRORS("");
     785             : 
     786           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::MULTIPLY));
     787           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     788             : 
     789             :         // make sure we got the expected error
     790           1 :         VERIFY_ERRORS("");
     791           1 :     }
     792             : 
     793             :     // '**' -> POWER
     794             :     {
     795           1 :         std::stringstream ss;
     796           3 :         csspp::position pos("test.css");
     797           1 :         csspp::lexer l(ss, pos);
     798           1 :         ss << '*' << '*';
     799             : 
     800             :         // so far, no error
     801           1 :         VERIFY_ERRORS("");
     802             : 
     803           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::POWER));
     804           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     805             : 
     806             :         // make sure we got the expected error
     807           1 :         VERIFY_ERRORS("");
     808           1 :     }
     809             : 
     810             :     // '&' -> REFERENCE
     811             :     {
     812           1 :         std::stringstream ss;
     813           3 :         csspp::position pos("test.css");
     814           1 :         csspp::lexer l(ss, pos);
     815           1 :         ss << "&";
     816             : 
     817             :         // so far, no error
     818           1 :         VERIFY_ERRORS("");
     819             : 
     820           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::REFERENCE));
     821           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     822             : 
     823             :         // make sure we got the expected error
     824           1 :         VERIFY_ERRORS("");
     825           1 :     }
     826             : 
     827             :     // '&&' -> AND
     828             :     {
     829           1 :         std::stringstream ss;
     830           3 :         csspp::position pos("test.css");
     831           1 :         csspp::lexer l(ss, pos);
     832           1 :         ss << '&' << '&';
     833             : 
     834             :         // so far, no error
     835           1 :         VERIFY_ERRORS("");
     836             : 
     837           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::AND));
     838           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     839             : 
     840             :         // make sure we got the expected error
     841           1 :         VERIFY_ERRORS("");
     842           1 :     }
     843             : 
     844             :     // '~' -> PRECEDED
     845             :     {
     846           1 :         std::stringstream ss;
     847           3 :         csspp::position pos("test.css");
     848           1 :         csspp::lexer l(ss, pos);
     849           1 :         ss << "~";
     850             : 
     851             :         // so far, no error
     852           1 :         VERIFY_ERRORS("");
     853             : 
     854           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::PRECEDED));
     855           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     856             : 
     857             :         // make sure we got the expected error
     858           1 :         VERIFY_ERRORS("");
     859           1 :     }
     860             : 
     861             :     // '+' -> ADD
     862             :     {
     863           1 :         std::stringstream ss;
     864           3 :         csspp::position pos("test.css");
     865           1 :         csspp::lexer l(ss, pos);
     866           1 :         ss << "+";
     867             : 
     868             :         // so far, no error
     869           1 :         VERIFY_ERRORS("");
     870             : 
     871           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::ADD));
     872           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     873             : 
     874             :         // make sure we got the expected error
     875           1 :         VERIFY_ERRORS("");
     876           1 :     }
     877             : 
     878             :     // '-' -> SUBTRACT
     879             :     {
     880           1 :         std::stringstream ss;
     881           3 :         csspp::position pos("test.css");
     882           1 :         csspp::lexer l(ss, pos);
     883           1 :         ss << "-";
     884             : 
     885             :         // so far, no error
     886           1 :         VERIFY_ERRORS("");
     887             : 
     888           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::SUBTRACT));
     889           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     890             : 
     891             :         // make sure we got the expected error
     892           1 :         VERIFY_ERRORS("");
     893           1 :     }
     894             : 
     895             :     // '|' -> SCOPE
     896             :     {
     897           1 :         std::stringstream ss;
     898           3 :         csspp::position pos("test.css");
     899           1 :         csspp::lexer l(ss, pos);
     900           1 :         ss << "|";
     901             : 
     902             :         // so far, no error
     903           1 :         VERIFY_ERRORS("");
     904             : 
     905           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::SCOPE));
     906           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     907             : 
     908             :         // make sure we got the expected error
     909           1 :         VERIFY_ERRORS("");
     910           1 :     }
     911             : 
     912             :     // '>' -> GREATER_THAN
     913             :     {
     914           1 :         std::stringstream ss;
     915           3 :         csspp::position pos("test.css");
     916           1 :         csspp::lexer l(ss, pos);
     917           1 :         ss << ">";
     918             : 
     919             :         // so far, no error
     920           1 :         VERIFY_ERRORS("");
     921             : 
     922           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::GREATER_THAN));
     923           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     924             : 
     925             :         // make sure we got the expected error
     926           1 :         VERIFY_ERRORS("");
     927           1 :     }
     928             : 
     929             :     // '>=' -> GREATER_EQUAL
     930             :     {
     931           1 :         std::stringstream ss;
     932           3 :         csspp::position pos("test.css");
     933           1 :         csspp::lexer l(ss, pos);
     934           1 :         ss << ">=";
     935             : 
     936             :         // so far, no error
     937           1 :         VERIFY_ERRORS("");
     938             : 
     939           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::GREATER_EQUAL));
     940           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     941             : 
     942             :         // make sure we got the expected error
     943           1 :         VERIFY_ERRORS("");
     944           1 :     }
     945             : 
     946             :     // "<"
     947             :     {
     948           1 :         std::stringstream ss;
     949           1 :         ss << '<';
     950           3 :         csspp::position pos("test.css");
     951           1 :         csspp::lexer l(ss, pos);
     952             : 
     953             :         // so far, no error
     954           1 :         VERIFY_ERRORS("");
     955             : 
     956           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::LESS_THAN));
     957           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     958             : 
     959             :         // make sure we got the expected error
     960           1 :         VERIFY_ERRORS("");
     961           1 :     }
     962             : 
     963             :     // "<="
     964             :     {
     965           1 :         std::stringstream ss;
     966           1 :         ss << '<' << '=';
     967           3 :         csspp::position pos("test.css");
     968           1 :         csspp::lexer l(ss, pos);
     969             : 
     970             :         // so far, no error
     971           1 :         VERIFY_ERRORS("");
     972             : 
     973           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::LESS_EQUAL));
     974           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     975             : 
     976             :         // make sure we got the expected error
     977           1 :         VERIFY_ERRORS("");
     978           1 :     }
     979             : 
     980             :     // "<!" -- (special case because of "<!--")
     981             :     {
     982           1 :         std::stringstream ss;
     983           1 :         ss << '<' << '!';
     984           3 :         csspp::position pos("test.css");
     985           1 :         csspp::lexer l(ss, pos);
     986             : 
     987             :         // so far, no error
     988           1 :         VERIFY_ERRORS("");
     989             : 
     990           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::LESS_THAN));
     991           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EXCLAMATION));
     992           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
     993             : 
     994             :         // make sure we got the expected error
     995           1 :         VERIFY_ERRORS("");
     996           1 :     }
     997             : 
     998             :     // "<!-" -- (special case because of "<!--")
     999             :     {
    1000           1 :         std::stringstream ss;
    1001           1 :         ss << '<' << '!' << '-';
    1002           3 :         csspp::position pos("test.css");
    1003           1 :         csspp::lexer l(ss, pos);
    1004             : 
    1005             :         // so far, no error
    1006           1 :         VERIFY_ERRORS("");
    1007             : 
    1008             :         // The '<' is returned as LESS_THAN
    1009             :         // The '!' is returned as EXCLAMATION
    1010             :         // The '-' is returned as SUBTRACT
    1011           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::LESS_THAN));
    1012           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EXCLAMATION));
    1013           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::SUBTRACT));
    1014           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1015             : 
    1016             :         // make sure we got the expected error
    1017           1 :         VERIFY_ERRORS("");
    1018           1 :     }
    1019             : 
    1020             :     // "!"
    1021             :     {
    1022           1 :         std::stringstream ss;
    1023           1 :         ss << '!';
    1024           3 :         csspp::position pos("test.css");
    1025           1 :         csspp::lexer l(ss, pos);
    1026             : 
    1027             :         // so far, no error
    1028           1 :         VERIFY_ERRORS("");
    1029             : 
    1030             :         // The '!' is returned as EXCLAMATION
    1031           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EXCLAMATION));
    1032           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1033             : 
    1034             :         // make sure we got the expected error
    1035           1 :         VERIFY_ERRORS("");
    1036           1 :     }
    1037             : 
    1038             :     // "!="
    1039             :     {
    1040           1 :         std::stringstream ss;
    1041           1 :         ss << '!' << '=';
    1042           3 :         csspp::position pos("test.css");
    1043           1 :         csspp::lexer l(ss, pos);
    1044             : 
    1045             :         // so far, no error
    1046           1 :         VERIFY_ERRORS("");
    1047             : 
    1048             :         // The '!=' is returned as NOT_EQUAL
    1049           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::NOT_EQUAL));
    1050           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1051             : 
    1052             :         // make sure we got the expected error
    1053           1 :         VERIFY_ERRORS("");
    1054           1 :     }
    1055             : 
    1056             :     // '(' -> OPEN_PARENTHESIS
    1057             :     {
    1058           1 :         std::stringstream ss;
    1059           3 :         csspp::position pos("test.css");
    1060           1 :         csspp::lexer l(ss, pos);
    1061           1 :         ss << "(";
    1062             : 
    1063             :         // so far, no error
    1064           1 :         VERIFY_ERRORS("");
    1065             : 
    1066           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::OPEN_PARENTHESIS));
    1067           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1068             : 
    1069             :         // make sure we got the expected error
    1070           1 :         VERIFY_ERRORS("");
    1071           1 :     }
    1072             : 
    1073             :     // ')' -> OPEN_PARENTHESIS
    1074             :     {
    1075           1 :         std::stringstream ss;
    1076           3 :         csspp::position pos("test.css");
    1077           1 :         csspp::lexer l(ss, pos);
    1078           1 :         ss << ")";
    1079             : 
    1080             :         // so far, no error
    1081           1 :         VERIFY_ERRORS("");
    1082             : 
    1083           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::CLOSE_PARENTHESIS));
    1084           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1085             : 
    1086             :         // make sure we got the expected error
    1087           1 :         VERIFY_ERRORS("");
    1088           1 :     }
    1089             : 
    1090             :     // '{' -> OPEN_CURLYBRACKET
    1091             :     {
    1092           1 :         std::stringstream ss;
    1093           3 :         csspp::position pos("test.css");
    1094           1 :         csspp::lexer l(ss, pos);
    1095           1 :         ss << "{";
    1096             : 
    1097             :         // so far, no error
    1098           1 :         VERIFY_ERRORS("");
    1099             : 
    1100           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::OPEN_CURLYBRACKET));
    1101           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1102             : 
    1103             :         // make sure we got the expected error
    1104           1 :         VERIFY_ERRORS("");
    1105           1 :     }
    1106             : 
    1107             :     // '}' -> OPEN_CURLYBRACKET
    1108             :     {
    1109           1 :         std::stringstream ss;
    1110           3 :         csspp::position pos("test.css");
    1111           1 :         csspp::lexer l(ss, pos);
    1112           1 :         ss << "}";
    1113             : 
    1114             :         // so far, no error
    1115           1 :         VERIFY_ERRORS("");
    1116             : 
    1117           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::CLOSE_CURLYBRACKET));
    1118           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1119             : 
    1120             :         // make sure we got the expected error
    1121           1 :         VERIFY_ERRORS("");
    1122           1 :     }
    1123             : 
    1124             :     // '[' -> OPEN_SQUAREBRACKET
    1125             :     {
    1126           1 :         std::stringstream ss;
    1127           3 :         csspp::position pos("test.css");
    1128           1 :         csspp::lexer l(ss, pos);
    1129           1 :         ss << "[";
    1130             : 
    1131             :         // so far, no error
    1132           1 :         VERIFY_ERRORS("");
    1133             : 
    1134           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::OPEN_SQUAREBRACKET));
    1135           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1136             : 
    1137             :         // make sure we got the expected error
    1138           1 :         VERIFY_ERRORS("");
    1139           1 :     }
    1140             : 
    1141             :     // ']' -> OPEN_SQUAREBRACKET
    1142             :     {
    1143           1 :         std::stringstream ss;
    1144           3 :         csspp::position pos("test.css");
    1145           1 :         csspp::lexer l(ss, pos);
    1146           1 :         ss << "]";
    1147             : 
    1148             :         // so far, no error
    1149           1 :         VERIFY_ERRORS("");
    1150             : 
    1151           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::CLOSE_SQUAREBRACKET));
    1152           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1153             : 
    1154             :         // make sure we got the expected error
    1155           1 :         VERIFY_ERRORS("");
    1156           1 :     }
    1157             : 
    1158             :     // '<!--' -> CDO
    1159             :     {
    1160           1 :         std::stringstream ss;
    1161           3 :         csspp::position pos("test.css");
    1162           1 :         csspp::lexer l(ss, pos);
    1163           1 :         ss << "<!--";
    1164             : 
    1165             :         // so far, no error
    1166           1 :         VERIFY_ERRORS("");
    1167             : 
    1168           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::CDO));
    1169           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1170             : 
    1171             :         // make sure we got the expected error
    1172           1 :         VERIFY_ERRORS("");
    1173           1 :     }
    1174             : 
    1175             :     // '<!--' -> CDC
    1176             :     {
    1177           1 :         std::stringstream ss;
    1178           3 :         csspp::position pos("test.css");
    1179           1 :         csspp::lexer l(ss, pos);
    1180           1 :         ss << "-->";
    1181             : 
    1182             :         // so far, no error
    1183           1 :         VERIFY_ERRORS("");
    1184             : 
    1185           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::CDC));
    1186           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1187             : 
    1188             :         // make sure we got the expected error
    1189           1 :         VERIFY_ERRORS("");
    1190           1 :     }
    1191             : 
    1192             :     // '^=' -> PREFIX_MATCH
    1193             :     {
    1194           1 :         std::stringstream ss;
    1195           3 :         csspp::position pos("test.css");
    1196           1 :         csspp::lexer l(ss, pos);
    1197           1 :         ss << "^=";
    1198             : 
    1199             :         // so far, no error
    1200           1 :         VERIFY_ERRORS("");
    1201             : 
    1202           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::PREFIX_MATCH));
    1203           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1204             : 
    1205             :         // make sure we got the expected error
    1206           1 :         VERIFY_ERRORS("");
    1207           1 :     }
    1208             : 
    1209             :     // '|=' -> DASH_MATCH
    1210             :     {
    1211           1 :         std::stringstream ss;
    1212           3 :         csspp::position pos("test.css");
    1213           1 :         csspp::lexer l(ss, pos);
    1214           1 :         ss << "|=";
    1215             : 
    1216             :         // so far, no error
    1217           1 :         VERIFY_ERRORS("");
    1218             : 
    1219           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::DASH_MATCH));
    1220           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1221             : 
    1222             :         // make sure we got the expected error
    1223           1 :         VERIFY_ERRORS("");
    1224           1 :     }
    1225             : 
    1226             :     // '$=' -> SUFFIX_MATCH
    1227             :     {
    1228           1 :         std::stringstream ss;
    1229           3 :         csspp::position pos("test.css");
    1230           1 :         csspp::lexer l(ss, pos);
    1231           1 :         ss << "$=";
    1232             : 
    1233             :         // so far, no error
    1234           1 :         VERIFY_ERRORS("");
    1235             : 
    1236           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::SUFFIX_MATCH));
    1237           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1238             : 
    1239             :         // make sure we got the expected error
    1240           1 :         VERIFY_ERRORS("");
    1241           1 :     }
    1242             : 
    1243             :     // '~=' -> INCLUDE_MATCH
    1244             :     {
    1245           1 :         std::stringstream ss;
    1246           3 :         csspp::position pos("test.css");
    1247           1 :         csspp::lexer l(ss, pos);
    1248           1 :         ss << "~=";
    1249             : 
    1250             :         // so far, no error
    1251           1 :         VERIFY_ERRORS("");
    1252             : 
    1253           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::INCLUDE_MATCH));
    1254           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1255             : 
    1256             :         // make sure we got the expected error
    1257           1 :         VERIFY_ERRORS("");
    1258           1 :     }
    1259             : 
    1260             :     // '*=' -> SUBSTRING_MATCH
    1261             :     {
    1262           1 :         std::stringstream ss;
    1263           3 :         csspp::position pos("test.css");
    1264           1 :         csspp::lexer l(ss, pos);
    1265           1 :         ss << "*=";
    1266             : 
    1267             :         // so far, no error
    1268           1 :         VERIFY_ERRORS("");
    1269             : 
    1270           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::SUBSTRING_MATCH));
    1271           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1272             : 
    1273             :         // make sure we got the expected error
    1274           1 :         VERIFY_ERRORS("");
    1275           1 :     }
    1276             : 
    1277             :     // ':=' -> ASSIGNMENT
    1278             :     {
    1279           1 :         std::stringstream ss;
    1280           3 :         csspp::position pos("test.css");
    1281           1 :         csspp::lexer l(ss, pos);
    1282           1 :         ss << ":=";
    1283             : 
    1284             :         // so far, no error
    1285           1 :         VERIFY_ERRORS("");
    1286             : 
    1287           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::ASSIGNMENT));
    1288           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1289             : 
    1290             :         // make sure we got the expected error
    1291           1 :         VERIFY_ERRORS("");
    1292           1 :     }
    1293             : 
    1294             :     // '||' -> COLUMN
    1295             :     {
    1296           1 :         std::stringstream ss;
    1297           3 :         csspp::position pos("test.css");
    1298           1 :         csspp::lexer l(ss, pos);
    1299           1 :         ss << "||";
    1300             : 
    1301             :         // so far, no error
    1302           1 :         VERIFY_ERRORS("");
    1303             : 
    1304           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::COLUMN));
    1305           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1306             : 
    1307             :         // make sure we got the expected error
    1308           1 :         VERIFY_ERRORS("");
    1309           1 :     }
    1310             : 
    1311             :     // A "special" sequence div+.alpha
    1312             :     {
    1313           1 :         std::stringstream ss;
    1314           3 :         csspp::position pos("test.css");
    1315           1 :         csspp::lexer l(ss, pos);
    1316           1 :         ss << "div+.alpha";
    1317             : 
    1318             :         // so far, no error
    1319           1 :         VERIFY_ERRORS("");
    1320             : 
    1321           1 :         csspp::node::pointer_t div(l.next_token());
    1322           1 :         CATCH_REQUIRE(div->is(csspp::node_type_t::IDENTIFIER));
    1323           1 :         CATCH_REQUIRE(div->get_string() == "div");
    1324             : 
    1325           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::ADD));
    1326             : 
    1327           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::PERIOD));
    1328             : 
    1329           1 :         csspp::node::pointer_t alpha(l.next_token());
    1330           1 :         CATCH_REQUIRE(alpha->is(csspp::node_type_t::IDENTIFIER));
    1331           1 :         CATCH_REQUIRE(alpha->get_string() == "alpha");
    1332             : 
    1333           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1334             : 
    1335             :         // make sure we got the expected error
    1336           1 :         VERIFY_ERRORS("");
    1337           1 :     }
    1338             : 
    1339             :     // A "special" sequence div -.alpha
    1340             :     {
    1341           1 :         std::stringstream ss;
    1342           3 :         csspp::position pos("test.css");
    1343           1 :         csspp::lexer l(ss, pos);
    1344           1 :         ss << "div -.alpha";
    1345             : 
    1346             :         // so far, no error
    1347           1 :         VERIFY_ERRORS("");
    1348             : 
    1349           1 :         csspp::node::pointer_t div(l.next_token());
    1350           1 :         CATCH_REQUIRE(div->is(csspp::node_type_t::IDENTIFIER));
    1351           1 :         CATCH_REQUIRE(div->get_string() == "div");
    1352             : 
    1353           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::WHITESPACE));
    1354             : 
    1355           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::SUBTRACT));
    1356             : 
    1357           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::PERIOD));
    1358             : 
    1359           1 :         csspp::node::pointer_t alpha(l.next_token());
    1360           1 :         CATCH_REQUIRE(alpha->is(csspp::node_type_t::IDENTIFIER));
    1361           1 :         CATCH_REQUIRE(alpha->get_string() == "alpha");
    1362             : 
    1363           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1364             : 
    1365             :         // make sure we got the expected error
    1366           1 :         VERIFY_ERRORS("");
    1367           1 :     }
    1368             : 
    1369             :     // no error left over
    1370           1 :     VERIFY_ERRORS("");
    1371           1 : }
    1372             : 
    1373           1 : CATCH_TEST_CASE("Newline", "[lexer] [newline] [characters]")
    1374             : {
    1375             :     // we have a special case with '\r' followed by a character
    1376             :     // other than '\n'
    1377     1113985 :     for(csspp::wide_char_t i(0x80); i < 0x110000; ++i)
    1378             :     {
    1379     1113984 :         switch(i)
    1380             :         {
    1381          35 :         case 0xFFFD:
    1382             :         case 0x00FFFE:
    1383             :         case 0x00FFFF:
    1384             :         case 0x01FFFE:
    1385             :         case 0x01FFFF:
    1386             :         case 0x02FFFE:
    1387             :         case 0x02FFFF:
    1388             :         case 0x03FFFE:
    1389             :         case 0x03FFFF:
    1390             :         case 0x04FFFE:
    1391             :         case 0x04FFFF:
    1392             :         case 0x05FFFE:
    1393             :         case 0x05FFFF:
    1394             :         case 0x06FFFE:
    1395             :         case 0x06FFFF:
    1396             :         case 0x07FFFE:
    1397             :         case 0x07FFFF:
    1398             :         case 0x08FFFE:
    1399             :         case 0x08FFFF:
    1400             :         case 0x09FFFE:
    1401             :         case 0x09FFFF:
    1402             :         case 0x0AFFFE:
    1403             :         case 0x0AFFFF:
    1404             :         case 0x0BFFFE:
    1405             :         case 0x0BFFFF:
    1406             :         case 0x0CFFFE:
    1407             :         case 0x0CFFFF:
    1408             :         case 0x0DFFFE:
    1409             :         case 0x0DFFFF:
    1410             :         case 0x0EFFFE:
    1411             :         case 0x0EFFFF:
    1412             :         case 0x0FFFFE:
    1413             :         case 0x0FFFFF:
    1414             :         case 0x10FFFE:
    1415             :         case 0x10FFFF:
    1416             :             // skip on characters that are either invalid or generate
    1417             :             // a "problem" (i.e. spaces get trimmed)
    1418          35 :             continue;
    1419             : 
    1420     1113949 :         default:
    1421     1113949 :             if(i >= 0xD800 &&  i <= 0xDFFF)
    1422             :             {
    1423        2048 :                 continue;
    1424             :             }
    1425     1111901 :             break;
    1426             : 
    1427             :         }
    1428             : 
    1429     2223802 :         std::stringstream ss;
    1430     4447604 :         csspp::position pos("test.css");
    1431     2223802 :         csspp::lexer l(ss, pos);
    1432     2223802 :         ss << '\r' << l.wctomb(i)
    1433     2223802 :            << '\n' << l.wctomb(i)
    1434     2223802 :            << '\f' << l.wctomb(i)
    1435     2223802 :            << "\r\n" << l.wctomb(i)
    1436     5559505 :            << "\n\r" << l.wctomb(i);
    1437             : 
    1438     2223802 :         std::stringstream out;
    1439     1111901 :         out << l.wctomb(i);
    1440             : 
    1441             :         // character on the second line (\r char)
    1442             :         {
    1443             :             // check the whitespace
    1444     1111901 :             csspp::node::pointer_t whitespace(l.next_token());
    1445     1111901 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    1446     1111901 :             csspp::position const & npos(whitespace->get_position());
    1447     1111901 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1448     1111901 :             CATCH_REQUIRE(npos.get_page() == 1);
    1449     1111901 :             CATCH_REQUIRE(npos.get_line() == 1);
    1450     1111901 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    1451     1111901 :         }
    1452             : 
    1453             :         // make sure the next character is viewed as an identifier
    1454             :         // just as expected
    1455             :         {
    1456     1111901 :             csspp::node::pointer_t identifier(l.next_token());
    1457     1111901 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    1458     1111901 :             CATCH_REQUIRE(identifier->get_string() == out.str());
    1459     1111901 :             csspp::position const & npos(identifier->get_position());
    1460     1111901 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1461     1111901 :             CATCH_REQUIRE(npos.get_page() == 1);
    1462     1111901 :             CATCH_REQUIRE(npos.get_line() == 2);
    1463     1111901 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    1464     1111901 :         }
    1465             : 
    1466             :         // character on the second line (\n char)
    1467             :         {
    1468             :             // check the whitespace
    1469     1111901 :             csspp::node::pointer_t whitespace(l.next_token());
    1470     1111901 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    1471     1111901 :             csspp::position const & npos(whitespace->get_position());
    1472     1111901 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1473     1111901 :             CATCH_REQUIRE(npos.get_page() == 1);
    1474     1111901 :             CATCH_REQUIRE(npos.get_line() == 3);
    1475     1111901 :             CATCH_REQUIRE(npos.get_total_line() == 3);
    1476     1111901 :         }
    1477             : 
    1478             :         // make sure the next character is viewed as an identifier
    1479             :         // just as expected
    1480             :         {
    1481     1111901 :             csspp::node::pointer_t identifier(l.next_token());
    1482     1111901 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    1483     1111901 :             CATCH_REQUIRE(identifier->get_string() == out.str());
    1484     1111901 :             csspp::position const & npos(identifier->get_position());
    1485     1111901 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1486     1111901 :             CATCH_REQUIRE(npos.get_page() == 1);
    1487     1111901 :             CATCH_REQUIRE(npos.get_line() == 3);
    1488     1111901 :             CATCH_REQUIRE(npos.get_total_line() == 3);
    1489     1111901 :         }
    1490             : 
    1491             :         // character on the second line (\f char)
    1492             :         {
    1493             :             // check the whitespace
    1494     1111901 :             csspp::node::pointer_t whitespace(l.next_token());
    1495     1111901 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    1496     1111901 :             csspp::position const & npos(whitespace->get_position());
    1497     1111901 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1498     1111901 :             CATCH_REQUIRE(npos.get_page() == 2);
    1499     1111901 :             CATCH_REQUIRE(npos.get_line() == 1);
    1500     1111901 :             CATCH_REQUIRE(npos.get_total_line() == 3);
    1501     1111901 :         }
    1502             : 
    1503             :         // make sure the next character is viewed as an identifier
    1504             :         // just as expected
    1505             :         {
    1506     1111901 :             csspp::node::pointer_t identifier(l.next_token());
    1507     1111901 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    1508     1111901 :             CATCH_REQUIRE(identifier->get_string() == out.str());
    1509     1111901 :             csspp::position const & npos(identifier->get_position());
    1510     1111901 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1511     1111901 :             CATCH_REQUIRE(npos.get_page() == 2);
    1512     1111901 :             CATCH_REQUIRE(npos.get_line() == 1);
    1513     1111901 :             CATCH_REQUIRE(npos.get_total_line() == 3);
    1514     1111901 :         }
    1515             : 
    1516             :         // character on the second line (\r\n char)
    1517             :         {
    1518             :             // check the whitespace
    1519     1111901 :             csspp::node::pointer_t whitespace(l.next_token());
    1520     1111901 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    1521     1111901 :             csspp::position const & npos(whitespace->get_position());
    1522     1111901 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1523     1111901 :             CATCH_REQUIRE(npos.get_page() == 2);
    1524     1111901 :             CATCH_REQUIRE(npos.get_line() == 2);
    1525     1111901 :             CATCH_REQUIRE(npos.get_total_line() == 4);
    1526     1111901 :         }
    1527             : 
    1528             :         // make sure the next character is viewed as an identifier
    1529             :         // just as expected
    1530             :         {
    1531     1111901 :             csspp::node::pointer_t identifier(l.next_token());
    1532     1111901 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    1533     1111901 :             CATCH_REQUIRE(identifier->get_string() == out.str());
    1534     1111901 :             csspp::position const & npos(identifier->get_position());
    1535     1111901 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1536     1111901 :             CATCH_REQUIRE(npos.get_page() == 2);
    1537     1111901 :             CATCH_REQUIRE(npos.get_line() == 2);
    1538     1111901 :             CATCH_REQUIRE(npos.get_total_line() == 4);
    1539     1111901 :         }
    1540             : 
    1541             :         // character on the second line (\n\r char)
    1542             :         {
    1543             :             // check the whitespace
    1544     1111901 :             csspp::node::pointer_t whitespace(l.next_token());
    1545     1111901 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    1546     1111901 :             csspp::position const & npos(whitespace->get_position());
    1547     1111901 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1548     1111901 :             CATCH_REQUIRE(npos.get_page() == 2);
    1549     1111901 :             CATCH_REQUIRE(npos.get_line() == 3);
    1550     1111901 :             CATCH_REQUIRE(npos.get_total_line() == 5);
    1551     1111901 :         }
    1552             : 
    1553             :         // make sure the next character is viewed as an identifier
    1554             :         // just as expected
    1555             :         {
    1556     1111901 :             csspp::node::pointer_t identifier(l.next_token());
    1557     1111901 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    1558     1111901 :             CATCH_REQUIRE(identifier->get_string() == out.str());
    1559     1111901 :             csspp::position const & npos(identifier->get_position());
    1560     1111901 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1561     1111901 :             CATCH_REQUIRE(npos.get_page() == 2);
    1562     1111901 :             CATCH_REQUIRE(npos.get_line() == 4);
    1563     1111901 :             CATCH_REQUIRE(npos.get_total_line() == 6);
    1564     1111901 :         }
    1565             : 
    1566     1111901 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1567             :     }
    1568             : 
    1569             :     // no error left over
    1570           1 :     VERIFY_ERRORS("");
    1571           1 : }
    1572             : 
    1573           1 : CATCH_TEST_CASE("C-like comments", "[lexer] [comment]")
    1574             : {
    1575             :     // a comment without @preserve gets lost
    1576             :     {
    1577           1 :         std::stringstream ss;
    1578           1 :         ss << "/* test simple comment */";
    1579           3 :         csspp::position pos("test.css");
    1580           1 :         csspp::lexer l(ss, pos);
    1581             : 
    1582           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1583           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1584           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1585           1 :     }
    1586             : 
    1587             :     // one simple comment
    1588             :     {
    1589           1 :         std::stringstream ss;
    1590           1 :         ss << "/* test simple comment @preserve */";
    1591           3 :         csspp::position pos("test.css");
    1592           1 :         csspp::lexer l(ss, pos);
    1593             : 
    1594             :         // comment
    1595             :         {
    1596           1 :             csspp::node::pointer_t comment(l.next_token());
    1597           1 :             CATCH_REQUIRE(comment->is(csspp::node_type_t::COMMENT));
    1598           1 :             CATCH_REQUIRE(comment->get_string() == "test simple comment @preserve");
    1599           1 :             CATCH_REQUIRE(comment->get_integer() == 1); // C-like comment
    1600           1 :             csspp::position const & npos(comment->get_position());
    1601           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1602           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    1603           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    1604           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    1605           1 :         }
    1606             : 
    1607           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1608           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1609           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1610           1 :     }
    1611             : 
    1612             :     // an unterminated simple comment
    1613             :     {
    1614           1 :         std::stringstream ss;
    1615           1 :         ss << "/* test simple comment @preserve";
    1616           3 :         csspp::position pos("test.css");
    1617           1 :         csspp::lexer l(ss, pos);
    1618             : 
    1619             :         // comment
    1620             :         {
    1621           1 :             csspp::node::pointer_t comment(l.next_token());
    1622           1 :             CATCH_REQUIRE(comment->is(csspp::node_type_t::COMMENT));
    1623           1 :             CATCH_REQUIRE(comment->get_string() == "test simple comment @preserve");
    1624           1 :             CATCH_REQUIRE(comment->get_integer() == 1); // C-like comment
    1625           1 :             csspp::position const & npos(comment->get_position());
    1626           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1627           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    1628           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    1629           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    1630           1 :         }
    1631             : 
    1632           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1633           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1634           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1635             : 
    1636           1 :         VERIFY_ERRORS("test.css(1): error: unclosed C-like comment at the end of your document.\n");
    1637           1 :     }
    1638             : 
    1639             :     // a comment on multiple lines
    1640             :     {
    1641           1 :         std::stringstream ss;
    1642           1 :         ss << "/* test\na\r\nmulti-line\fcomment\n\rtoo @preserve */\n";
    1643           3 :         csspp::position pos("test.css");
    1644           1 :         csspp::lexer l(ss, pos);
    1645             : 
    1646             :         // comment
    1647             :         {
    1648           1 :             csspp::node::pointer_t comment(l.next_token());
    1649           1 :             CATCH_REQUIRE(comment->is(csspp::node_type_t::COMMENT));
    1650           1 :             CATCH_REQUIRE(comment->get_string() == "test\na\nmulti-line\ncomment\n\ntoo @preserve");
    1651           1 :             CATCH_REQUIRE(comment->get_integer() == 1); // C-like comment
    1652           1 :             csspp::position const & npos(comment->get_position());
    1653           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1654           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    1655           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    1656           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    1657           1 :         }
    1658             : 
    1659             :         // whitespace
    1660             :         {
    1661           1 :             csspp::node::pointer_t whitespace(l.next_token());
    1662           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    1663           1 :             csspp::position const & npos(whitespace->get_position());
    1664           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1665           1 :             CATCH_REQUIRE(npos.get_page() == 2);
    1666           1 :             CATCH_REQUIRE(npos.get_line() == 3);
    1667           1 :             CATCH_REQUIRE(npos.get_total_line() == 5);
    1668           1 :         }
    1669             : 
    1670             :         // EOF
    1671           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1672           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1673           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1674           1 :     }
    1675             : 
    1676             :     // one multi-line comment followed by another simple comment
    1677             :     {
    1678           1 :         std::stringstream ss;
    1679           1 :         ss << "/* test\na\r\nmulti-line\fcomment\n\rtoo @preserve */\n/* with a second comment @preserve */";
    1680           3 :         csspp::position pos("test.css");
    1681           1 :         csspp::lexer l(ss, pos);
    1682             : 
    1683             :         // 1st comment
    1684             :         {
    1685           1 :             csspp::node::pointer_t comment1(l.next_token());
    1686           1 :             CATCH_REQUIRE(comment1->is(csspp::node_type_t::COMMENT));
    1687           1 :             CATCH_REQUIRE(comment1->get_string() == "test\na\nmulti-line\ncomment\n\ntoo @preserve");
    1688           1 :             CATCH_REQUIRE(comment1->get_integer() == 1); // C-like comment
    1689           1 :             csspp::position const & npos(comment1->get_position());
    1690           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1691           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    1692           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    1693           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    1694           1 :         }
    1695             : 
    1696             :         // whitespace in between
    1697             :         {
    1698           1 :             csspp::node::pointer_t whitespace(l.next_token());
    1699           1 :             csspp::position const & npos(whitespace->get_position());
    1700           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1701           1 :             CATCH_REQUIRE(npos.get_page() == 2);
    1702           1 :             CATCH_REQUIRE(npos.get_line() == 3);
    1703           1 :             CATCH_REQUIRE(npos.get_total_line() == 5);
    1704           1 :         }
    1705             : 
    1706             :         // 2nd comment
    1707             :         {
    1708           1 :             csspp::node::pointer_t comment(l.next_token());
    1709           1 :             CATCH_REQUIRE(comment->is(csspp::node_type_t::COMMENT));
    1710           1 :             CATCH_REQUIRE(comment->get_string() == "with a second comment @preserve");
    1711           1 :             CATCH_REQUIRE(comment->get_integer() == 1); // C-like comment
    1712           1 :             csspp::position const & npos(comment->get_position());
    1713           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1714           1 :             CATCH_REQUIRE(npos.get_page() == 2);
    1715           1 :             CATCH_REQUIRE(npos.get_line() == 4);
    1716           1 :             CATCH_REQUIRE(npos.get_total_line() == 6);
    1717           1 :         }
    1718             : 
    1719             :         // EOF
    1720           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1721           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1722           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1723           1 :     }
    1724             : 
    1725             :     // test with all types of characters that are considered valid by
    1726             :     // our code
    1727             :     {
    1728     1114112 :         for(csspp::wide_char_t i(1); i < 0x110000; ++i)
    1729             :         {
    1730     1114111 :             switch(i)
    1731             :             {
    1732          40 :             case ' ':
    1733             :             case '\t':
    1734             :             case '\n':
    1735             :             case '\r':
    1736             :             case '\f':
    1737             :             case '\0':
    1738             :             case 0xFFFD:
    1739             :             case 0x00FFFE:
    1740             :             case 0x00FFFF:
    1741             :             case 0x01FFFE:
    1742             :             case 0x01FFFF:
    1743             :             case 0x02FFFE:
    1744             :             case 0x02FFFF:
    1745             :             case 0x03FFFE:
    1746             :             case 0x03FFFF:
    1747             :             case 0x04FFFE:
    1748             :             case 0x04FFFF:
    1749             :             case 0x05FFFE:
    1750             :             case 0x05FFFF:
    1751             :             case 0x06FFFE:
    1752             :             case 0x06FFFF:
    1753             :             case 0x07FFFE:
    1754             :             case 0x07FFFF:
    1755             :             case 0x08FFFE:
    1756             :             case 0x08FFFF:
    1757             :             case 0x09FFFE:
    1758             :             case 0x09FFFF:
    1759             :             case 0x0AFFFE:
    1760             :             case 0x0AFFFF:
    1761             :             case 0x0BFFFE:
    1762             :             case 0x0BFFFF:
    1763             :             case 0x0CFFFE:
    1764             :             case 0x0CFFFF:
    1765             :             case 0x0DFFFE:
    1766             :             case 0x0DFFFF:
    1767             :             case 0x0EFFFE:
    1768             :             case 0x0EFFFF:
    1769             :             case 0x0FFFFE:
    1770             :             case 0x0FFFFF:
    1771             :             case 0x10FFFE:
    1772             :             case 0x10FFFF:
    1773             :                 // skip on characters that are either invalid or generate
    1774             :                 // a "problem" (i.e. spaces get trimmed)
    1775          40 :                 continue;
    1776             : 
    1777     1114071 :             default:
    1778     1114071 :                 if(i >= 0xD800 &&  i <= 0xDFFF)
    1779             :                 {
    1780        2048 :                     continue;
    1781             :                 }
    1782     1112023 :                 break;
    1783             : 
    1784             :             }
    1785             : 
    1786     2224046 :             std::stringstream ss;
    1787     1112023 :             char mb[6];
    1788     4448092 :             csspp::position pos("test.css");
    1789     2224046 :             csspp::lexer l(ss, pos);
    1790     1112023 :             l.wctomb(i, mb, sizeof(mb) / sizeof(mb[0]));
    1791             : //std::cerr << "testing with " << i << "\n";
    1792             : //for(int j(0); mb[j] != '\0'; ++j) std::cerr << " " << j << ". " << std::hex << static_cast<int>(static_cast<unsigned char>(mb[j])) << std::dec << "\n";
    1793     3336069 :             std::string cmt("character: ");
    1794     1112023 :             cmt += mb;
    1795     1112023 :             ss << "/* " << cmt << " @preserve */";
    1796             : 
    1797             :             // comment
    1798             :             {
    1799     1112023 :                 csspp::node::pointer_t comment(l.next_token());
    1800     1112023 :                 CATCH_REQUIRE(comment->is(csspp::node_type_t::COMMENT));
    1801     1112023 :                 CATCH_REQUIRE(comment->get_string() == cmt + " @preserve");
    1802     1112023 :                 CATCH_REQUIRE(comment->get_integer() == 1); // C-like comment
    1803     1112023 :                 csspp::position const & npos(comment->get_position());
    1804     1112023 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    1805     1112023 :                 CATCH_REQUIRE(npos.get_page() == 1);
    1806     1112023 :                 CATCH_REQUIRE(npos.get_line() == 1);
    1807     1112023 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    1808     1112023 :             }
    1809             : 
    1810     1112023 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1811     1112023 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1812     1112023 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1813             :         }
    1814             :     }
    1815             : 
    1816             :     // no error left over
    1817           1 :     VERIFY_ERRORS("");
    1818           1 : }
    1819             : 
    1820           1 : CATCH_TEST_CASE("C++ comments", "[lexer] [comment]")
    1821             : {
    1822             :     // a comment without @preserve gets lost
    1823             :     {
    1824           1 :         std::stringstream ss;
    1825           1 :         ss << "// test simple comment\r\n";
    1826           3 :         csspp::position pos("test.css");
    1827           1 :         csspp::lexer l(ss, pos);
    1828             : 
    1829             :         // whitespace
    1830             :         {
    1831           1 :             csspp::node::pointer_t whitespace(l.next_token());
    1832           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    1833           1 :             csspp::position const & npos(whitespace->get_position());
    1834           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1835           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    1836           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    1837           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    1838           1 :         }
    1839             : 
    1840             :         // EOF
    1841           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1842           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1843           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1844             : 
    1845             :         // no error left over
    1846           1 :         VERIFY_ERRORS("");
    1847           1 :     }
    1848             : 
    1849             :     // one simple comment
    1850             :     {
    1851           1 :         std::stringstream ss;
    1852           1 :         ss << "// test simple comment @preserve\r\n";
    1853           3 :         csspp::position pos("test.css");
    1854           1 :         csspp::lexer l(ss, pos);
    1855             : 
    1856             :         // comment
    1857             :         {
    1858           1 :             csspp::node::pointer_t comment(l.next_token());
    1859           1 :             CATCH_REQUIRE(comment->is(csspp::node_type_t::COMMENT));
    1860           1 :             CATCH_REQUIRE(comment->get_string() == "test simple comment @preserve");
    1861           1 :             CATCH_REQUIRE(comment->get_integer() == 0); // C++ comment
    1862           1 :             csspp::position const & npos(comment->get_position());
    1863           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1864           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    1865           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    1866           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    1867             : 
    1868           1 :             VERIFY_ERRORS("test.css(1): warning: C++ comments should not be preserved as they are not supported by most CSS parsers.\n");
    1869           1 :         }
    1870             : 
    1871             :         // whitespace
    1872             :         {
    1873           1 :             csspp::node::pointer_t whitespace(l.next_token());
    1874           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    1875           1 :             csspp::position const & npos(whitespace->get_position());
    1876           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1877           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    1878           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    1879           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    1880           1 :         }
    1881             : 
    1882             :         // EOF
    1883           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1884           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1885           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1886             : 
    1887             :         // no error left over
    1888           1 :         VERIFY_ERRORS("");
    1889           1 :     }
    1890             : 
    1891             :     // a C++ comment on multiple lines is just a comment
    1892             :     // that is followed by a number of other C++ comments
    1893             :     {
    1894           1 :         std::stringstream ss;
    1895           1 :         ss << "// test\n// a\r\n// multi-line\f//comment\r//\ttoo @preserve\n";
    1896           3 :         csspp::position pos("test.css");
    1897           1 :         csspp::lexer l(ss, pos);
    1898             : 
    1899             :         // comment
    1900             :         {
    1901           1 :             csspp::node::pointer_t comment(l.next_token());
    1902           1 :             CATCH_REQUIRE(comment->is(csspp::node_type_t::COMMENT));
    1903           1 :             CATCH_REQUIRE(comment->get_string() == "test\na\nmulti-line\ncomment\ntoo @preserve");
    1904           1 :             CATCH_REQUIRE(comment->get_integer() == 0); // C++ comment
    1905           1 :             csspp::position const & npos(comment->get_position());
    1906           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1907           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    1908           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    1909           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    1910             : 
    1911           1 :             VERIFY_ERRORS("test.css(1): warning: C++ comments should not be preserved as they are not supported by most CSS parsers.\n");
    1912           1 :         }
    1913             : 
    1914             :         // whitespace
    1915             :         {
    1916           1 :             csspp::node::pointer_t whitespace(l.next_token());
    1917           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    1918           1 :             csspp::position const & npos(whitespace->get_position());
    1919           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1920           1 :             CATCH_REQUIRE(npos.get_page() == 2);
    1921           1 :             CATCH_REQUIRE(npos.get_line() == 3);
    1922           1 :             CATCH_REQUIRE(npos.get_total_line() == 5);
    1923           1 :         }
    1924             : 
    1925             :         // EOF
    1926           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1927           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1928           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1929             : 
    1930             :         // no error left over
    1931           1 :         VERIFY_ERRORS("");
    1932           1 :     }
    1933             : 
    1934             :     // one multi-line comment followed by another simple comment
    1935             :     {
    1936           1 :         std::stringstream ss;
    1937           1 :         ss << "// test\n//\ta\r\n//multi-line\f// comment\r\n// too @preserve\r\n\r\n// with a second comment @preserve";
    1938           3 :         csspp::position pos("test.css");
    1939           1 :         csspp::lexer l(ss, pos);
    1940             : 
    1941             :         // 1st comment
    1942             :         {
    1943           1 :             csspp::node::pointer_t comment(l.next_token());
    1944           1 :             CATCH_REQUIRE(comment->is(csspp::node_type_t::COMMENT));
    1945           1 :             CATCH_REQUIRE(comment->get_string() == "test\na\nmulti-line\ncomment\ntoo @preserve");
    1946           1 :             CATCH_REQUIRE(comment->get_integer() == 0); // C++ comment
    1947           1 :             csspp::position const & npos(comment->get_position());
    1948           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1949           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    1950           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    1951           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    1952             : 
    1953           1 :             VERIFY_ERRORS("test.css(1): warning: C++ comments should not be preserved as they are not supported by most CSS parsers.\n");
    1954           1 :         }
    1955             : 
    1956             :         // whitespace
    1957             :         {
    1958           1 :             csspp::node::pointer_t whitespace(l.next_token());
    1959           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    1960           1 :             csspp::position const & npos(whitespace->get_position());
    1961           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1962           1 :             CATCH_REQUIRE(npos.get_page() == 2);
    1963           1 :             CATCH_REQUIRE(npos.get_line() == 4);
    1964           1 :             CATCH_REQUIRE(npos.get_total_line() == 6);
    1965           1 :         }
    1966             : 
    1967             :         // 2nd comment
    1968             :         {
    1969           1 :             csspp::node::pointer_t comment(l.next_token());
    1970           1 :             CATCH_REQUIRE(comment->is(csspp::node_type_t::COMMENT));
    1971           1 :             CATCH_REQUIRE(comment->get_string() == "with a second comment @preserve");
    1972           1 :             CATCH_REQUIRE(comment->get_integer() == 0); // C++ comment
    1973           1 :             csspp::position const & npos(comment->get_position());
    1974           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    1975           1 :             CATCH_REQUIRE(npos.get_page() == 2);
    1976           1 :             CATCH_REQUIRE(npos.get_line() == 4);
    1977           1 :             CATCH_REQUIRE(npos.get_total_line() == 6);
    1978             : 
    1979           1 :             VERIFY_ERRORS("test.css(4): warning: C++ comments should not be preserved as they are not supported by most CSS parsers.\n");
    1980           1 :         }
    1981             : 
    1982             :         // EOF
    1983           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1984           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1985           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    1986             : 
    1987             :         // no error left over
    1988           1 :         VERIFY_ERRORS("");
    1989           1 :     }
    1990             : 
    1991             :     // one comment nearly multi-line
    1992             :     {
    1993           1 :         std::stringstream ss;
    1994           1 :         ss << "// test comment and @preserve\n/ divide";
    1995           3 :         csspp::position pos("test.css");
    1996           1 :         csspp::lexer l(ss, pos);
    1997             : 
    1998             :         // comment
    1999             :         {
    2000           1 :             csspp::node::pointer_t comment(l.next_token());
    2001           1 :             CATCH_REQUIRE(comment->is(csspp::node_type_t::COMMENT));
    2002           1 :             CATCH_REQUIRE(comment->get_string() == "test comment and @preserve");
    2003           1 :             CATCH_REQUIRE(comment->get_integer() == 0); // C++ comment
    2004           1 :             csspp::position const & npos(comment->get_position());
    2005           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2006           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2007           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2008           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2009             : 
    2010           1 :             VERIFY_ERRORS("test.css(1): warning: C++ comments should not be preserved as they are not supported by most CSS parsers.\n");
    2011           1 :         }
    2012             : 
    2013             :         // whitespace
    2014             :         {
    2015           1 :             csspp::node::pointer_t whitespace(l.next_token());
    2016           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    2017           1 :             csspp::position const & npos(whitespace->get_position());
    2018           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2019           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2020           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    2021           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    2022           1 :         }
    2023             : 
    2024             :         // divide
    2025             :         {
    2026           1 :             csspp::node::pointer_t comment(l.next_token());
    2027           1 :             CATCH_REQUIRE(comment->is(csspp::node_type_t::DIVIDE));
    2028           1 :             csspp::position const & npos(comment->get_position());
    2029           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2030           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2031           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    2032           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    2033           1 :         }
    2034             : 
    2035             :         // whitespace
    2036             :         {
    2037           1 :             csspp::node::pointer_t whitespace(l.next_token());
    2038           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    2039           1 :             csspp::position const & npos(whitespace->get_position());
    2040           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2041           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2042           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    2043           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    2044           1 :         }
    2045             : 
    2046             :         // identifier
    2047             :         {
    2048           1 :             csspp::node::pointer_t identifier(l.next_token());
    2049           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    2050           1 :             CATCH_REQUIRE(identifier->get_string() == "divide");
    2051           1 :             csspp::position const & npos(identifier->get_position());
    2052           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2053           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2054           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    2055           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    2056           1 :         }
    2057             : 
    2058             :         // EOF
    2059           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2060           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2061           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2062             : 
    2063             :         // no error left over
    2064           1 :         VERIFY_ERRORS("");
    2065           1 :     }
    2066             : 
    2067             :     // test with all types of characters that are considered valid by
    2068             :     // our code
    2069             :     {
    2070     1114112 :         for(csspp::wide_char_t i(1); i < 0x110000; ++i)
    2071             :         {
    2072     1114111 :             switch(i)
    2073             :             {
    2074          40 :             case ' ':
    2075             :             case '\t':
    2076             :             case '\n':
    2077             :             case '\r':
    2078             :             case '\f':
    2079             :             case '\0':
    2080             :             case 0xFFFD:
    2081             :             case 0x00FFFE:
    2082             :             case 0x00FFFF:
    2083             :             case 0x01FFFE:
    2084             :             case 0x01FFFF:
    2085             :             case 0x02FFFE:
    2086             :             case 0x02FFFF:
    2087             :             case 0x03FFFE:
    2088             :             case 0x03FFFF:
    2089             :             case 0x04FFFE:
    2090             :             case 0x04FFFF:
    2091             :             case 0x05FFFE:
    2092             :             case 0x05FFFF:
    2093             :             case 0x06FFFE:
    2094             :             case 0x06FFFF:
    2095             :             case 0x07FFFE:
    2096             :             case 0x07FFFF:
    2097             :             case 0x08FFFE:
    2098             :             case 0x08FFFF:
    2099             :             case 0x09FFFE:
    2100             :             case 0x09FFFF:
    2101             :             case 0x0AFFFE:
    2102             :             case 0x0AFFFF:
    2103             :             case 0x0BFFFE:
    2104             :             case 0x0BFFFF:
    2105             :             case 0x0CFFFE:
    2106             :             case 0x0CFFFF:
    2107             :             case 0x0DFFFE:
    2108             :             case 0x0DFFFF:
    2109             :             case 0x0EFFFE:
    2110             :             case 0x0EFFFF:
    2111             :             case 0x0FFFFE:
    2112             :             case 0x0FFFFF:
    2113             :             case 0x10FFFE:
    2114             :             case 0x10FFFF:
    2115             :                 // skip on characters that are either invalid or generate
    2116             :                 // a "problem" (i.e. spaces get trimmed)
    2117          40 :                 continue;
    2118             : 
    2119     1114071 :             default:
    2120     1114071 :                 if(i >= 0xD800 &&  i <= 0xDFFF)
    2121             :                 {
    2122        2048 :                     continue;
    2123             :                 }
    2124     1112023 :                 break;
    2125             : 
    2126             :             }
    2127             : 
    2128     2224046 :             std::stringstream ss;
    2129     1112023 :             char mb[6];
    2130     4448092 :             csspp::position pos("test.css");
    2131     2224046 :             csspp::lexer l(ss, pos);
    2132     1112023 :             l.wctomb(i, mb, sizeof(mb) / sizeof(mb[0]));
    2133             : //std::cerr << "testing with " << i << "\n";
    2134             : //for(int j(0); mb[j] != '\0'; ++j) std::cerr << " " << j << ". " << std::hex << static_cast<int>(static_cast<unsigned char>(mb[j])) << std::dec << "\n";
    2135     3336069 :             std::string cmt("character: ");
    2136     1112023 :             cmt += mb;
    2137     1112023 :             ss << "// " << cmt << " @preserve";
    2138             : 
    2139             :             // comment
    2140             :             {
    2141     1112023 :                 csspp::node::pointer_t comment(l.next_token());
    2142     1112023 :                 CATCH_REQUIRE(comment->is(csspp::node_type_t::COMMENT));
    2143     1112023 :                 CATCH_REQUIRE(comment->get_string() == cmt + " @preserve");
    2144     1112023 :                 CATCH_REQUIRE(comment->get_integer() == 0); // C++ comment
    2145     1112023 :                 csspp::position const & npos(comment->get_position());
    2146     1112023 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    2147     1112023 :                 CATCH_REQUIRE(npos.get_page() == 1);
    2148     1112023 :                 CATCH_REQUIRE(npos.get_line() == 1);
    2149     1112023 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    2150             : 
    2151     1112023 :                 VERIFY_ERRORS("test.css(1): warning: C++ comments should not be preserved as they are not supported by most CSS parsers.\n");
    2152     1112023 :             }
    2153             : 
    2154     1112023 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2155     1112023 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2156     1112023 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2157             :         }
    2158             : 
    2159             :         // no error left over
    2160           1 :         VERIFY_ERRORS("");
    2161             :     }
    2162           1 : }
    2163             : 
    2164           1 : CATCH_TEST_CASE("Strings", "[lexer] [string]")
    2165             : {
    2166             :     // one simple string with "
    2167             :     {
    2168           1 :         std::stringstream ss;
    2169           1 :         ss << "\"";
    2170           1 :         size_t const len(rand() % 20 + 20);
    2171           1 :         std::string word;
    2172          32 :         for(size_t i(0); i < len; ++i)
    2173             :         {
    2174             :             // simple ascii letters
    2175          31 :             int const c(rand() % 26 + 'a');
    2176          31 :             ss << static_cast<char>(c);
    2177          31 :             word += static_cast<char>(c);
    2178             :         }
    2179           1 :         ss << "\"";
    2180           3 :         csspp::position pos("test.css");
    2181           1 :         csspp::lexer l(ss, pos);
    2182             : 
    2183             :         // string
    2184             :         {
    2185           1 :             csspp::node::pointer_t string(l.next_token());
    2186           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::STRING));
    2187           1 :             CATCH_REQUIRE(string->get_string() == word);
    2188           1 :             csspp::position const & npos(string->get_position());
    2189           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2190           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2191           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2192           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2193           1 :         }
    2194             : 
    2195           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2196             : 
    2197             :         // no error left over
    2198           1 :         VERIFY_ERRORS("");
    2199           1 :     }
    2200             : 
    2201             :     // one simple string with " and including '
    2202             :     {
    2203           1 :         std::stringstream ss;
    2204           1 :         ss << "\"c'est un teste\"";
    2205           3 :         csspp::position pos("test.css");
    2206           1 :         csspp::lexer l(ss, pos);
    2207             : 
    2208             :         // string
    2209             :         {
    2210           1 :             csspp::node::pointer_t string(l.next_token());
    2211           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::STRING));
    2212           1 :             CATCH_REQUIRE(string->get_string() == "c'est un teste");
    2213           1 :             csspp::position const & npos(string->get_position());
    2214           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2215           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2216           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2217           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2218           1 :         }
    2219             : 
    2220           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2221             : 
    2222             :         // no error left over
    2223           1 :         VERIFY_ERRORS("");
    2224           1 :     }
    2225             : 
    2226             :     // one simple string with '
    2227             :     {
    2228           1 :         std::stringstream ss;
    2229           1 :         ss << "'";
    2230           1 :         size_t const len(rand() % 20 + 20);
    2231           1 :         std::string word;
    2232          23 :         for(size_t i(0); i < len; ++i)
    2233             :         {
    2234             :             // simple ascii letters
    2235          22 :             int const c(rand() % 26 + 'a');
    2236          22 :             ss << static_cast<char>(c);
    2237          22 :             word += static_cast<char>(c);
    2238             :         }
    2239           1 :         ss << "'";
    2240           3 :         csspp::position pos("test.css");
    2241           1 :         csspp::lexer l(ss, pos);
    2242             : 
    2243             :         // string
    2244             :         {
    2245           1 :             csspp::node::pointer_t string(l.next_token());
    2246           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::STRING));
    2247           1 :             CATCH_REQUIRE(string->get_string() == word);
    2248           1 :             csspp::position const & npos(string->get_position());
    2249           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2250           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2251           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2252           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2253           1 :         }
    2254             : 
    2255           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2256             : 
    2257             :         // no error left over
    2258           1 :         VERIFY_ERRORS("");
    2259           1 :     }
    2260             : 
    2261             :     // one simple string with ' including "
    2262             :     {
    2263           1 :         std::stringstream ss;
    2264           1 :         ss << "'This \"word\" sounds wrong!'";
    2265           3 :         csspp::position pos("test.css");
    2266           1 :         csspp::lexer l(ss, pos);
    2267             : 
    2268             :         // string
    2269             :         {
    2270           1 :             csspp::node::pointer_t string(l.next_token());
    2271           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::STRING));
    2272           1 :             CATCH_REQUIRE(string->get_string() == "This \"word\" sounds wrong!");
    2273           1 :             csspp::position const & npos(string->get_position());
    2274           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2275           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2276           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2277           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2278           1 :         }
    2279             : 
    2280           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2281             : 
    2282             :         // no error left over
    2283           1 :         VERIFY_ERRORS("");
    2284           1 :     }
    2285             : 
    2286             :     // string with escaped characters
    2287             :     {
    2288     1114113 :         for(csspp::wide_char_t i(0); i < 0x110000; ++i)
    2289             :         {
    2290     1114112 :             switch(i)
    2291             :             {
    2292          41 :             case '\0':
    2293             :             case ' ':
    2294             :             case '\t':
    2295             :             case '\n':
    2296             :             case '\r':
    2297             :             case '\f':
    2298             :             case 0xFFFD:
    2299             :             case 0x00FFFE:
    2300             :             case 0x00FFFF:
    2301             :             case 0x01FFFE:
    2302             :             case 0x01FFFF:
    2303             :             case 0x02FFFE:
    2304             :             case 0x02FFFF:
    2305             :             case 0x03FFFE:
    2306             :             case 0x03FFFF:
    2307             :             case 0x04FFFE:
    2308             :             case 0x04FFFF:
    2309             :             case 0x05FFFE:
    2310             :             case 0x05FFFF:
    2311             :             case 0x06FFFE:
    2312             :             case 0x06FFFF:
    2313             :             case 0x07FFFE:
    2314             :             case 0x07FFFF:
    2315             :             case 0x08FFFE:
    2316             :             case 0x08FFFF:
    2317             :             case 0x09FFFE:
    2318             :             case 0x09FFFF:
    2319             :             case 0x0AFFFE:
    2320             :             case 0x0AFFFF:
    2321             :             case 0x0BFFFE:
    2322             :             case 0x0BFFFF:
    2323             :             case 0x0CFFFE:
    2324             :             case 0x0CFFFF:
    2325             :             case 0x0DFFFE:
    2326             :             case 0x0DFFFF:
    2327             :             case 0x0EFFFE:
    2328             :             case 0x0EFFFF:
    2329             :             case 0x0FFFFE:
    2330             :             case 0x0FFFFF:
    2331             :             case 0x10FFFE:
    2332             :             case 0x10FFFF:
    2333             :                 // skip on characters that are either invalid or generate
    2334             :                 // a "problem" (i.e. spaces get trimmed)
    2335          41 :                 continue;
    2336             : 
    2337     1114071 :             default:
    2338     1114071 :                 if(i >= 0xD800 &&  i <= 0xDFFF)
    2339             :                 {
    2340        2048 :                     continue;
    2341             :                 }
    2342     1112023 :                 break;
    2343             : 
    2344             :             }
    2345             : 
    2346             :             // note that we test with sensible characters first
    2347             : 
    2348     2224046 :             std::stringstream ss;
    2349     1112023 :             if(rand() % 1 == 0)
    2350             :             {
    2351             :                 // make sure to also test uppercase once in a while
    2352     1112023 :                 ss << std::uppercase;
    2353             :             }
    2354     1112023 :             ss << "'escape character #" << std::dec << i
    2355     1112023 :                << " as: \\" << std::hex << i
    2356     1112023 :                << " to see whether it works'";
    2357     4448092 :             csspp::position pos("test.css");
    2358     2224046 :             csspp::lexer l(ss, pos);
    2359             : 
    2360             :             // string
    2361             :             {
    2362     1112023 :                 csspp::node::pointer_t string(l.next_token());
    2363     1112023 :                 CATCH_REQUIRE(string->is(csspp::node_type_t::STRING));
    2364     1112023 :                 std::stringstream out;
    2365     1112023 :                 out << "escape character #" << std::dec << i
    2366     2224046 :                     << " as: " << l.wctomb(i)
    2367             :                     << (i < 0x100000 ? "" : " ") // space gets eaten if less than 6 characters in escape sequence
    2368     2224046 :                     << "to see whether it works";
    2369     1112023 :                 CATCH_REQUIRE(string->get_string() == out.str());
    2370     1112023 :                 csspp::position const & npos(string->get_position());
    2371     1112023 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    2372     1112023 :                 CATCH_REQUIRE(npos.get_page() == 1);
    2373     1112023 :                 CATCH_REQUIRE(npos.get_line() == 1);
    2374     1112023 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    2375     1112023 :             }
    2376             : 
    2377     1112023 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2378             : 
    2379             :             // no error left over
    2380     1112023 :             VERIFY_ERRORS("");
    2381             :         }
    2382             :     }
    2383             : 
    2384             :     // unterminated string before EOF
    2385             :     {
    2386           1 :         std::stringstream ss;
    2387           1 :         ss << "'No terminator";
    2388           3 :         csspp::position pos("test.css");
    2389           1 :         csspp::lexer l(ss, pos);
    2390             : 
    2391             :         // string
    2392             :         {
    2393           1 :             csspp::node::pointer_t string(l.next_token());
    2394           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::STRING));
    2395           1 :             CATCH_REQUIRE(string->get_string() == "No terminator");
    2396           1 :             csspp::position const & npos(string->get_position());
    2397           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2398           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2399           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2400           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2401             : 
    2402           1 :             VERIFY_ERRORS("test.css(1): error: found an unterminated string.\n");
    2403           1 :         }
    2404             : 
    2405           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2406             : 
    2407             :         // no error left over
    2408           1 :         VERIFY_ERRORS("");
    2409           1 :     }
    2410             : 
    2411             :     // unterminated string before \n
    2412             :     {
    2413           1 :         std::stringstream ss;
    2414           1 :         ss << "'No terminator\nto that string";
    2415           3 :         csspp::position pos("test.css");
    2416           1 :         csspp::lexer l(ss, pos);
    2417             : 
    2418             :         // string
    2419             :         {
    2420           1 :             csspp::node::pointer_t string(l.next_token());
    2421           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::STRING));
    2422           1 :             CATCH_REQUIRE(string->get_string() == "No terminator");
    2423           1 :             csspp::position const & npos(string->get_position());
    2424           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2425           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2426           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2427           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2428             : 
    2429           1 :             VERIFY_ERRORS("test.css(1): error: found an unterminated string with an unescaped newline.\n");
    2430           1 :         }
    2431             : 
    2432             :         // whitespace
    2433             :         {
    2434           1 :             csspp::node::pointer_t string(l.next_token());
    2435           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::WHITESPACE));
    2436           1 :             csspp::position const & npos(string->get_position());
    2437           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2438           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2439           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    2440           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    2441           1 :         }
    2442             : 
    2443             :         // identifier
    2444             :         {
    2445           1 :             csspp::node::pointer_t string(l.next_token());
    2446           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    2447           1 :             CATCH_REQUIRE(string->get_string() == "to");
    2448           1 :             csspp::position const & npos(string->get_position());
    2449           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2450           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2451           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    2452           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    2453           1 :         }
    2454             : 
    2455             :         // whitespace
    2456             :         {
    2457           1 :             csspp::node::pointer_t string(l.next_token());
    2458           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::WHITESPACE));
    2459           1 :             csspp::position const & npos(string->get_position());
    2460           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2461           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2462           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    2463           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    2464           1 :         }
    2465             : 
    2466             :         // identifier
    2467             :         {
    2468           1 :             csspp::node::pointer_t string(l.next_token());
    2469           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    2470           1 :             CATCH_REQUIRE(string->get_string() == "that");
    2471           1 :             csspp::position const & npos(string->get_position());
    2472           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2473           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2474           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    2475           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    2476           1 :         }
    2477             : 
    2478             :         // whitespace
    2479             :         {
    2480           1 :             csspp::node::pointer_t string(l.next_token());
    2481           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::WHITESPACE));
    2482           1 :             csspp::position const & npos(string->get_position());
    2483           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2484           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2485           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    2486           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    2487           1 :         }
    2488             : 
    2489             :         // identifier
    2490             :         {
    2491           1 :             csspp::node::pointer_t string(l.next_token());
    2492           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    2493           1 :             CATCH_REQUIRE(string->get_string() == "string");
    2494           1 :             csspp::position const & npos(string->get_position());
    2495           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2496           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2497           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    2498           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    2499           1 :         }
    2500             : 
    2501           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2502             : 
    2503             :         // no error left over
    2504           1 :         VERIFY_ERRORS("");
    2505           1 :     }
    2506             : 
    2507             :     // special escapes in a string: \ + <EOF>
    2508             :     // (same as just EOF above: unterminated string)
    2509             :     {
    2510           1 :         std::stringstream ss;
    2511           1 :         ss << "'No terminator\\";
    2512           3 :         csspp::position pos("test.css");
    2513           1 :         csspp::lexer l(ss, pos);
    2514             : 
    2515             :         // string
    2516             :         {
    2517           1 :             csspp::node::pointer_t string(l.next_token());
    2518           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::STRING));
    2519           1 :             CATCH_REQUIRE(string->get_string() == "No terminator");
    2520           1 :             csspp::position const & npos(string->get_position());
    2521           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2522           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2523           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2524           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2525             : 
    2526           1 :             VERIFY_ERRORS("test.css(1): error: found an unterminated string.\n");
    2527           1 :         }
    2528             : 
    2529           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2530             : 
    2531             :         // no error left over
    2532           1 :         VERIFY_ERRORS("");
    2533           1 :     }
    2534             : 
    2535             :     // special escapes in a string: \ + '\n'
    2536             :     // (this is actually legal!)
    2537             :     {
    2538           1 :         std::stringstream ss;
    2539           1 :         ss << "'Line ncontinues on\\\nthe next line.'";
    2540           3 :         csspp::position pos("test.css");
    2541           1 :         csspp::lexer l(ss, pos);
    2542             : 
    2543             :         // string
    2544             :         {
    2545           1 :             csspp::node::pointer_t string(l.next_token());
    2546           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::STRING));
    2547           1 :             CATCH_REQUIRE(string->get_string() == "Line ncontinues on\nthe next line.");
    2548           1 :             csspp::position const & npos(string->get_position());
    2549           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2550           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2551           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2552           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2553           1 :         }
    2554             : 
    2555           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2556             : 
    2557             :         // no error left over
    2558           1 :         VERIFY_ERRORS("");
    2559           1 :     }
    2560             : 
    2561             :     // special escapes in a string: \ + <FFFD>
    2562             :     {
    2563           1 :         std::stringstream ss;
    2564           3 :         csspp::position pos("test.css");
    2565           1 :         csspp::lexer l(ss, pos);
    2566           1 :         ss << "'Bad Escape \\" << l.wctomb(0xFFFD) << " String'";
    2567             : 
    2568             :         // string
    2569             :         {
    2570           1 :             csspp::node::pointer_t string(l.next_token());
    2571           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::STRING));
    2572           1 :             CATCH_REQUIRE(string->get_string() == "Bad Escape  String");
    2573           1 :             csspp::position const & npos(string->get_position());
    2574           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2575           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2576           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2577             : 
    2578           1 :             VERIFY_ERRORS("test.css(1): error: invalid character after a \\ character.\n");
    2579           1 :         }
    2580             : 
    2581           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2582             : 
    2583             :         // no error left over
    2584           1 :         VERIFY_ERRORS("");
    2585           1 :     }
    2586             : 
    2587             :     // escapes in a string: \ + <number too large>
    2588       31352 :     for(int i(0x110000); i < 0x01000000; i += rand() % 1000 + 1)
    2589             :     {
    2590       31351 :         std::stringstream ss;
    2591       94053 :         csspp::position pos("test.css");
    2592       31351 :         csspp::lexer l(ss, pos);
    2593       31351 :         ss << "'Bad Escape \\" << std::hex << i << " String'";
    2594             : 
    2595             :         // string
    2596             :         {
    2597       31351 :             csspp::node::pointer_t string(l.next_token());
    2598       31351 :             CATCH_REQUIRE(string->is(csspp::node_type_t::STRING));
    2599       31351 :             CATCH_REQUIRE(string->get_string() == "Bad Escape  String");
    2600       31351 :             csspp::position const & npos(string->get_position());
    2601       31351 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2602       31351 :             CATCH_REQUIRE(npos.get_page() == 1);
    2603       31351 :             CATCH_REQUIRE(npos.get_line() == 1);
    2604       31351 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2605             : 
    2606       31351 :             VERIFY_ERRORS("test.css(1): error: escape character too large for Unicode.\n");
    2607       31351 :         }
    2608             : 
    2609       31351 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2610             : 
    2611             :         // no error left over
    2612       31351 :         VERIFY_ERRORS("");
    2613       31351 :     }
    2614           1 : }
    2615             : 
    2616           1 : CATCH_TEST_CASE("Identifiers", "[lexer] [identifier]")
    2617             : {
    2618             :     // a few simple identifiers
    2619          11 :     for(int count(0); count < 10; ++count)
    2620             :     {
    2621          10 :         std::stringstream ss;
    2622          10 :         size_t const len(rand() % 20 + 20);
    2623          10 :         std::string word;
    2624         289 :         for(size_t i(0); i < len; ++i)
    2625             :         {
    2626             :             // simple ascii letters
    2627         279 :             int const c(rand() % 26 + 'a');
    2628         279 :             ss << static_cast<char>(c);
    2629         279 :             word += static_cast<char>(c);
    2630             :         }
    2631          30 :         csspp::position pos("test.css");
    2632          10 :         csspp::lexer l(ss, pos);
    2633             : 
    2634             :         // identifier
    2635             :         {
    2636          10 :             csspp::node::pointer_t string(l.next_token());
    2637          10 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    2638          10 :             CATCH_REQUIRE(string->get_string() == word);
    2639          10 :             csspp::position const & npos(string->get_position());
    2640          10 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2641          10 :             CATCH_REQUIRE(npos.get_page() == 1);
    2642          10 :             CATCH_REQUIRE(npos.get_line() == 1);
    2643          10 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2644          10 :         }
    2645             : 
    2646          10 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2647          10 :     }
    2648             : 
    2649             :     // a few simple identifiers starting with '_'
    2650          11 :     for(int count(0); count < 10; ++count)
    2651             :     {
    2652          10 :         std::stringstream ss;
    2653          10 :         size_t const len(rand() % 20 + 20);
    2654          20 :         std::string word("_");
    2655          10 :         ss << '_';
    2656         325 :         for(size_t i(0); i < len; ++i)
    2657             :         {
    2658             :             // simple ascii letters
    2659         315 :             int const c(rand() % 26 + 'a');
    2660         315 :             ss << static_cast<char>(c);
    2661         315 :             word += static_cast<char>(c);
    2662             :         }
    2663          30 :         csspp::position pos("test.css");
    2664          10 :         csspp::lexer l(ss, pos);
    2665             : 
    2666             :         // identifier
    2667             :         {
    2668          10 :             csspp::node::pointer_t string(l.next_token());
    2669          10 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    2670          10 :             CATCH_REQUIRE(string->get_string() == word);
    2671          10 :             csspp::position const & npos(string->get_position());
    2672          10 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2673          10 :             CATCH_REQUIRE(npos.get_page() == 1);
    2674          10 :             CATCH_REQUIRE(npos.get_line() == 1);
    2675          10 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2676          10 :         }
    2677             : 
    2678          10 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2679          10 :     }
    2680             : 
    2681             :     // a few simple identifiers starting with '@'
    2682          11 :     for(int count(0); count < 10; ++count)
    2683             :     {
    2684          10 :         std::stringstream ss;
    2685          10 :         size_t const len(rand() % 20 + 20);
    2686          10 :         std::string word;
    2687          10 :         ss << '@';
    2688         295 :         for(size_t i(0); i < len; ++i)
    2689             :         {
    2690             :             // simple ascii letters
    2691         285 :             int const c(rand() % 26 + 'a');
    2692         285 :             ss << static_cast<char>(c);
    2693         285 :             word += static_cast<char>(c);
    2694             :         }
    2695          30 :         csspp::position pos("test.css");
    2696          10 :         csspp::lexer l(ss, pos);
    2697             : 
    2698             :         // identifier
    2699             :         {
    2700          10 :             csspp::node::pointer_t string(l.next_token());
    2701          10 :             CATCH_REQUIRE(string->is(csspp::node_type_t::AT_KEYWORD));
    2702          10 :             CATCH_REQUIRE(string->get_string() == word);
    2703          10 :             csspp::position const & npos(string->get_position());
    2704          10 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2705          10 :             CATCH_REQUIRE(npos.get_page() == 1);
    2706          10 :             CATCH_REQUIRE(npos.get_line() == 1);
    2707          10 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2708          10 :         }
    2709             : 
    2710          10 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2711          10 :     }
    2712             : 
    2713             :     // try with empty '@' characters (i.e. invalid identifiers)
    2714             :     {
    2715           1 :         std::stringstream ss;
    2716           1 :         ss << "@*@";
    2717           3 :         csspp::position pos("test.css");
    2718           1 :         csspp::lexer l(ss, pos);
    2719             : 
    2720             :         // prefix-match
    2721             :         {
    2722           1 :             csspp::node::pointer_t string(l.next_token());
    2723           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::MULTIPLY));
    2724           1 :             csspp::position const & npos(string->get_position());
    2725           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2726           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2727           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2728           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2729             : 
    2730           1 :             VERIFY_ERRORS("test.css(1): error: found an empty identifier.\n");
    2731           1 :         }
    2732             : 
    2733             :         // EOF
    2734             :         {
    2735           1 :             csspp::node::pointer_t eof(l.next_token());
    2736           1 :             CATCH_REQUIRE(eof->is(csspp::node_type_t::EOF_TOKEN));
    2737             : 
    2738           1 :             VERIFY_ERRORS("test.css(1): error: found an empty identifier.\n");
    2739           1 :         }
    2740           1 :     }
    2741             : 
    2742             :     // identifiers starting with '-'
    2743          11 :     for(int count(0); count < 10; ++count)
    2744             :     {
    2745          10 :         std::stringstream ss;
    2746          10 :         size_t const len(rand() % 20 + 20);
    2747          10 :         std::string word;
    2748          10 :         word += '-';
    2749          10 :         ss << '-';
    2750         300 :         for(size_t i(0); i < len; ++i)
    2751             :         {
    2752             :             // simple ascii letters
    2753         290 :             int const c(rand() % 26 + 'a');
    2754         290 :             ss << static_cast<char>(c);
    2755         290 :             word += static_cast<char>(c);
    2756             :         }
    2757          30 :         csspp::position pos("test.css");
    2758          10 :         csspp::lexer l(ss, pos);
    2759             : 
    2760             :         // identifier
    2761             :         {
    2762          10 :             csspp::node::pointer_t string(l.next_token());
    2763          10 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    2764          10 :             CATCH_REQUIRE(string->get_string() == word);
    2765          10 :             csspp::position const & npos(string->get_position());
    2766          10 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2767          10 :             CATCH_REQUIRE(npos.get_page() == 1);
    2768          10 :             CATCH_REQUIRE(npos.get_line() == 1);
    2769          10 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2770          10 :         }
    2771             : 
    2772          10 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2773          10 :     }
    2774             : 
    2775             :     // identifiers cannot start with '--'
    2776             :     {
    2777           1 :         std::stringstream ss;
    2778           1 :         ss << "--not-double-dash\n-\\-double-dash\n-\\----quintuple-dash-----\r\n";
    2779           3 :         csspp::position pos("test.css");
    2780           1 :         csspp::lexer l(ss, pos);
    2781             : 
    2782             :         // subtract
    2783             :         {
    2784           1 :             csspp::node::pointer_t string(l.next_token());
    2785           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::SUBTRACT));
    2786           1 :             csspp::position const & npos(string->get_position());
    2787           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2788           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2789           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2790           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2791           1 :         }
    2792             : 
    2793             :         // identifier
    2794             :         {
    2795           1 :             csspp::node::pointer_t string(l.next_token());
    2796           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    2797           1 :             CATCH_REQUIRE(string->get_string() == "-not-double-dash");
    2798           1 :             csspp::position const & npos(string->get_position());
    2799           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2800           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2801           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2802           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2803           1 :         }
    2804             : 
    2805             :         // whitespace
    2806             :         {
    2807           1 :             csspp::node::pointer_t whitespace(l.next_token());
    2808           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    2809           1 :             csspp::position const & npos(whitespace->get_position());
    2810           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2811           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2812           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    2813           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    2814           1 :         }
    2815             : 
    2816             :         // identifier
    2817             :         {
    2818           1 :             csspp::node::pointer_t string(l.next_token());
    2819           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    2820           1 :             CATCH_REQUIRE(string->get_string() == "--double-dash");
    2821           1 :             csspp::position const & npos(string->get_position());
    2822           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2823           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2824           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    2825           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    2826           1 :         }
    2827             : 
    2828             :         // whitespace
    2829             :         {
    2830           1 :             csspp::node::pointer_t whitespace(l.next_token());
    2831           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    2832           1 :             csspp::position const & npos(whitespace->get_position());
    2833           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2834           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2835           1 :             CATCH_REQUIRE(npos.get_line() == 3);
    2836           1 :             CATCH_REQUIRE(npos.get_total_line() == 3);
    2837           1 :         }
    2838             : 
    2839             :         // identifier
    2840             :         {
    2841           1 :             csspp::node::pointer_t string(l.next_token());
    2842           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    2843           1 :             CATCH_REQUIRE(string->get_string() == "-----quintuple-dash-----");
    2844           1 :             csspp::position const & npos(string->get_position());
    2845           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2846           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2847           1 :             CATCH_REQUIRE(npos.get_line() == 3);
    2848           1 :             CATCH_REQUIRE(npos.get_total_line() == 3);
    2849           1 :         }
    2850             : 
    2851             :         // whitespace
    2852             :         {
    2853           1 :             csspp::node::pointer_t whitespace(l.next_token());
    2854           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    2855           1 :             csspp::position const & npos(whitespace->get_position());
    2856           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2857           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2858           1 :             CATCH_REQUIRE(npos.get_line() == 4);
    2859           1 :             CATCH_REQUIRE(npos.get_total_line() == 4);
    2860           1 :         }
    2861             : 
    2862             :         // EOF
    2863           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2864           1 :     }
    2865             : 
    2866             :     // identifiers starting with an escape (\)
    2867             :     {
    2868           1 :         std::stringstream ss;
    2869             :         ss << "\\41lexis\n"
    2870             :            << "\\42 abar\n"
    2871           1 :            << "\\000043arlos\n";
    2872           3 :         csspp::position pos("test.css");
    2873           1 :         csspp::lexer l(ss, pos);
    2874             : 
    2875             :         // identifier
    2876             :         {
    2877           1 :             csspp::node::pointer_t string(l.next_token());
    2878           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    2879           1 :             CATCH_REQUIRE(string->get_string() == "Alexis"); // identifiers are forced to lowercase since they are case insensitive
    2880           1 :             csspp::position const & npos(string->get_position());
    2881           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2882           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2883           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2884           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2885           1 :         }
    2886             : 
    2887             :         // whitespace
    2888             :         {
    2889           1 :             csspp::node::pointer_t whitespace(l.next_token());
    2890           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    2891           1 :             csspp::position const & npos(whitespace->get_position());
    2892           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2893           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2894           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    2895           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    2896           1 :         }
    2897             : 
    2898             :         // identifier
    2899             :         {
    2900           1 :             csspp::node::pointer_t string(l.next_token());
    2901           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    2902           1 :             CATCH_REQUIRE(string->get_string() == "Babar"); // prove the space is eaten as expected
    2903           1 :             csspp::position const & npos(string->get_position());
    2904           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2905           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2906           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    2907           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    2908           1 :         }
    2909             : 
    2910             :         // whitespace
    2911             :         {
    2912           1 :             csspp::node::pointer_t whitespace(l.next_token());
    2913           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    2914           1 :             csspp::position const & npos(whitespace->get_position());
    2915           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2916           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2917           1 :             CATCH_REQUIRE(npos.get_line() == 3);
    2918           1 :             CATCH_REQUIRE(npos.get_total_line() == 3);
    2919           1 :         }
    2920             : 
    2921             :         // identifier
    2922             :         {
    2923           1 :             csspp::node::pointer_t string(l.next_token());
    2924           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    2925           1 :             CATCH_REQUIRE(string->get_string() == "Carlos"); // prove the space is not required with 6 digits
    2926           1 :             csspp::position const & npos(string->get_position());
    2927           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2928           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2929           1 :             CATCH_REQUIRE(npos.get_line() == 3);
    2930           1 :             CATCH_REQUIRE(npos.get_total_line() == 3);
    2931           1 :         }
    2932             : 
    2933             :         // whitespace
    2934             :         {
    2935           1 :             csspp::node::pointer_t whitespace(l.next_token());
    2936           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    2937           1 :             csspp::position const & npos(whitespace->get_position());
    2938           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2939           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2940           1 :             CATCH_REQUIRE(npos.get_line() == 4);
    2941           1 :             CATCH_REQUIRE(npos.get_total_line() == 4);
    2942           1 :         }
    2943             : 
    2944             :         // EOF
    2945           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2946           1 :     }
    2947             : 
    2948             :     // identifier with an empty escape (\ + <EOF>)
    2949             :     {
    2950           1 :         std::stringstream ss;
    2951           1 :         ss << "This\\";
    2952           3 :         csspp::position pos("test.css");
    2953           1 :         csspp::lexer l(ss, pos);
    2954             : 
    2955             :         // identifier
    2956             :         {
    2957           1 :             csspp::node::pointer_t string(l.next_token());
    2958           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    2959           1 :             CATCH_REQUIRE(string->get_string() == "This");
    2960           1 :             csspp::position const & npos(string->get_position());
    2961           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2962           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2963           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2964           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2965             : 
    2966           1 :             VERIFY_ERRORS("test.css(1): error: found EOF right after \\.\n");
    2967           1 :         }
    2968             : 
    2969             :         // EOF
    2970           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    2971           1 :     }
    2972             : 
    2973             :     // empty identifier with an empty escape (\ + <EOF>)
    2974             :     {
    2975           1 :         std::stringstream ss;
    2976           1 :         ss << "This \\";
    2977           3 :         csspp::position pos("test.css");
    2978           1 :         csspp::lexer l(ss, pos);
    2979             : 
    2980             :         // identifier
    2981             :         {
    2982           1 :             csspp::node::pointer_t string(l.next_token());
    2983           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    2984           1 :             CATCH_REQUIRE(string->get_string() == "This");
    2985           1 :             csspp::position const & npos(string->get_position());
    2986           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2987           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2988           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    2989           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    2990           1 :         }
    2991             : 
    2992             :         // whitespace
    2993             :         {
    2994           1 :             csspp::node::pointer_t whitespace(l.next_token());
    2995           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    2996           1 :             csspp::position const & npos(whitespace->get_position());
    2997           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    2998           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    2999           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3000           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3001           1 :         }
    3002             : 
    3003             :         // EOF
    3004           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3005             : 
    3006           1 :         VERIFY_ERRORS(
    3007             :                 "test.css(1): error: found EOF right after \\.\n"
    3008             :                 "test.css(1): error: found an empty identifier.\n"
    3009             :             );
    3010           1 :     }
    3011             : 
    3012             :     // identifier with an escape followed by a newline (\ + <new-line>)
    3013             :     {
    3014           1 :         std::stringstream ss;
    3015           1 :         ss << "This\\\nThat";
    3016           3 :         csspp::position pos("test.css");
    3017           1 :         csspp::lexer l(ss, pos);
    3018             : 
    3019             :         // identifier
    3020             :         {
    3021           1 :             csspp::node::pointer_t string(l.next_token());
    3022           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    3023           1 :             CATCH_REQUIRE(string->get_string() == "This");
    3024           1 :             csspp::position const & npos(string->get_position());
    3025           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3026           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3027           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3028           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3029             : 
    3030           1 :             VERIFY_ERRORS(
    3031             :                     "test.css(1): error: spurious newline character after a \\ character outside of a string.\n"
    3032             :                 );
    3033           1 :         }
    3034             : 
    3035             :         // whitespace -- this one gets lost and we do not care much
    3036             :         //{
    3037             :         //    csspp::node::pointer_t whitespace(l.next_token());
    3038             :         //    CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    3039             :         //    csspp::position const & npos(whitespace->get_position());
    3040             :         //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    3041             :         //    CATCH_REQUIRE(npos.get_page() == 1);
    3042             :         //    CATCH_REQUIRE(npos.get_line() == 1);
    3043             :         //    CATCH_REQUIRE(npos.get_total_line() == 1);
    3044             :         //}
    3045             : 
    3046             :         // identifier
    3047             :         {
    3048           1 :             csspp::node::pointer_t string(l.next_token());
    3049           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    3050           1 :             CATCH_REQUIRE(string->get_string() == "That");
    3051           1 :             csspp::position const & npos(string->get_position());
    3052           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3053           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3054           1 :             CATCH_REQUIRE(npos.get_line() == 2);
    3055           1 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    3056           1 :         }
    3057             : 
    3058             :         // EOF
    3059           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3060           1 :     }
    3061             : 
    3062             :     // identifiers written between parenthesis and "don't do that"
    3063          11 :     for(int count(0); count < 10; ++count)
    3064             :     {
    3065          10 :         std::stringstream ss;
    3066          10 :         size_t const len(rand() % 20 + 20);
    3067          10 :         std::string word;
    3068          10 :         word += "(";
    3069          10 :         ss << "\\(";
    3070         313 :         for(size_t i(0); i < len; ++i)
    3071             :         {
    3072             :             // simple ascii letters
    3073         303 :             int const c(rand() % 26 + 'a');
    3074         303 :             if(c > 'f' && rand() % 5 == 0)
    3075             :             {
    3076             :                 // add some random escape characters
    3077          51 :                 ss << "\\";
    3078             :             }
    3079         303 :             ss << static_cast<char>(c);
    3080         303 :             word += static_cast<char>(c);
    3081             :         }
    3082          10 :         word += ")";
    3083          10 :         ss << "\\)\n\\\"don\\'t\\ do\\ that\\\"";
    3084          30 :         csspp::position pos("test.css");
    3085          10 :         csspp::lexer l(ss, pos);
    3086             : 
    3087             :         // identifier
    3088             :         {
    3089          10 :             csspp::node::pointer_t string(l.next_token());
    3090          10 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    3091          10 :             CATCH_REQUIRE(string->get_string() == word);
    3092          10 :             csspp::position const & npos(string->get_position());
    3093          10 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3094          10 :             CATCH_REQUIRE(npos.get_page() == 1);
    3095          10 :             CATCH_REQUIRE(npos.get_line() == 1);
    3096          10 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3097          10 :         }
    3098             : 
    3099             :         // whitespace
    3100             :         {
    3101          10 :             csspp::node::pointer_t whitespace(l.next_token());
    3102          10 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    3103          10 :             csspp::position const & npos(whitespace->get_position());
    3104          10 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3105          10 :             CATCH_REQUIRE(npos.get_page() == 1);
    3106          10 :             CATCH_REQUIRE(npos.get_line() == 2);
    3107          10 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    3108          10 :         }
    3109             : 
    3110             :         // identifier
    3111             :         {
    3112          10 :             csspp::node::pointer_t string(l.next_token());
    3113          10 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    3114          10 :             CATCH_REQUIRE(string->get_string() == "\"don't do that\""); // yes, it's possible
    3115          10 :             csspp::position const & npos(string->get_position());
    3116          10 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3117          10 :             CATCH_REQUIRE(npos.get_page() == 1);
    3118          10 :             CATCH_REQUIRE(npos.get_line() == 2);
    3119          10 :             CATCH_REQUIRE(npos.get_total_line() == 2);
    3120          10 :         }
    3121             : 
    3122          10 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3123          10 :     }
    3124             : 
    3125             :     // identifier with an escape followed by 0xFFFD (\ + <FFFD>)
    3126             :     {
    3127           1 :         std::stringstream ss;
    3128           3 :         csspp::position pos("test.css");
    3129           1 :         csspp::lexer l(ss, pos);
    3130           1 :         ss << "This\\" << l.wctomb(0xFFFD) << "\\ ID";
    3131             : 
    3132             :         // identifier
    3133             :         {
    3134           1 :             csspp::node::pointer_t string(l.next_token());
    3135           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    3136           1 :             CATCH_REQUIRE(string->get_string() == "This");
    3137           1 :             csspp::position const & npos(string->get_position());
    3138           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3139           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3140           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3141           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3142             : 
    3143           1 :             VERIFY_ERRORS("test.css(1): error: invalid character after a \\ character.\n");
    3144           1 :         }
    3145             : 
    3146             :         // identifier
    3147             :         {
    3148           1 :             csspp::node::pointer_t string(l.next_token());
    3149           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    3150           1 :             CATCH_REQUIRE(string->get_string() == " ID");
    3151           1 :             csspp::position const & npos(string->get_position());
    3152           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3153           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3154           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3155           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3156           1 :         }
    3157             : 
    3158             :         // EOF
    3159           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3160           1 :     }
    3161             : 
    3162             :     // identifier with an escape followed by "0" (\0)
    3163             :     {
    3164           1 :         std::stringstream ss;
    3165           3 :         csspp::position pos("test.css");
    3166           1 :         csspp::lexer l(ss, pos);
    3167           1 :         ss << "This\\0ID";
    3168             : 
    3169             :         // identifier
    3170             :         {
    3171           1 :             csspp::node::pointer_t string(l.next_token());
    3172           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    3173           1 :             CATCH_REQUIRE(string->get_string() == "This");
    3174           1 :             csspp::position const & npos(string->get_position());
    3175           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3176           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3177           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3178           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3179             : 
    3180           1 :             VERIFY_ERRORS("test.css(1): error: escape character '\\0' is not acceptable in CSS.\n");
    3181           1 :         }
    3182             : 
    3183             :         // identifier
    3184             :         {
    3185           1 :             csspp::node::pointer_t string(l.next_token());
    3186           1 :             CATCH_REQUIRE(string->is(csspp::node_type_t::IDENTIFIER));
    3187           1 :             CATCH_REQUIRE(string->get_string() == "ID");
    3188           1 :             csspp::position const & npos(string->get_position());
    3189           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3190           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3191           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3192           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3193           1 :         }
    3194             : 
    3195             :         // EOF
    3196           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3197           1 :     }
    3198             : 
    3199             :     // no error left over
    3200           1 :     VERIFY_ERRORS("");
    3201           1 : }
    3202             : 
    3203           4 : CATCH_TEST_CASE("Urls", "[lexer] [identifier] [url] [function]")
    3204             : {
    3205             :     // a few simple URLs
    3206           4 :     CATCH_START_SECTION("simple URLs")
    3207             :     {
    3208          11 :         for(int count(0); count < 10; ++count)
    3209             :         {
    3210          10 :             std::stringstream ss;
    3211          10 :             size_t const len(rand() % 20 + 20);
    3212          10 :             std::string word;
    3213          10 :             ss << "url(";
    3214          10 :             size_t const leading(count == 0 ? 0 : rand() % 10);
    3215          45 :             for(size_t i(0); i < leading; ++i)
    3216             :             {
    3217          35 :                 ss << ' ';
    3218             :             }
    3219         293 :             for(size_t i(0); i < len; ++i)
    3220             :             {
    3221             :                 // simple ascii letters
    3222         283 :                 int const c(rand() % 26 + 'a');
    3223         283 :                 ss << static_cast<char>(c);
    3224         283 :                 word += static_cast<char>(c);
    3225             :             }
    3226          10 :             size_t const trailing(count == 9 ? 0 : rand() % 10);
    3227          38 :             for(size_t i(0); i < trailing; ++i)
    3228             :             {
    3229          28 :                 ss << ' ';
    3230             :             }
    3231          10 :             ss << ")";
    3232          30 :             csspp::position pos("test.css");
    3233          10 :             csspp::lexer l(ss, pos);
    3234             : 
    3235             :             // url
    3236             :             {
    3237          10 :                 csspp::node::pointer_t string(l.next_token());
    3238          10 :                 CATCH_REQUIRE(string->is(csspp::node_type_t::URL));
    3239          10 :                 CATCH_REQUIRE(string->get_string() == word);
    3240          10 :                 csspp::position const & npos(string->get_position());
    3241          10 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    3242          10 :                 CATCH_REQUIRE(npos.get_page() == 1);
    3243          10 :                 CATCH_REQUIRE(npos.get_line() == 1);
    3244          10 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    3245          10 :             }
    3246             : 
    3247          10 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3248          10 :         }
    3249             : 
    3250           1 :         VERIFY_ERRORS("");
    3251             :     }
    3252           4 :     CATCH_END_SECTION()
    3253             : 
    3254             :     // a few simple quoted URLs
    3255           4 :     CATCH_START_SECTION("simple quoted URLs")
    3256             :     {
    3257          11 :         for(int count(0); count < 10; ++count)
    3258             :         {
    3259          10 :             std::stringstream ss;
    3260          10 :             size_t const len(rand() % 20 + 20);
    3261          10 :             std::string word;
    3262          10 :             ss << "url(";
    3263          10 :             size_t const leading(rand() % 10);
    3264          61 :             for(size_t i(0); i < leading; ++i)
    3265             :             {
    3266          51 :                 ss << ' ';
    3267             :             }
    3268          10 :             char const quote("\"'"[rand() % 2]);
    3269          10 :             ss << quote;
    3270         308 :             for(size_t i(0); i < len; ++i)
    3271             :             {
    3272             :                 // simple ascii letters
    3273         298 :                 int const c(rand() % 26 + 'a');
    3274         298 :                 ss << static_cast<char>(c);
    3275         298 :                 word += static_cast<char>(c);
    3276             :             }
    3277          10 :             ss << quote;
    3278          10 :             size_t const trailing(rand() % 10);
    3279          47 :             for(size_t i(0); i < trailing; ++i)
    3280             :             {
    3281          37 :                 ss << ' ';
    3282             :             }
    3283          10 :             ss << ")";
    3284          30 :             csspp::position pos("test.css");
    3285          10 :             csspp::lexer l(ss, pos);
    3286             : 
    3287             :             // url
    3288             :             {
    3289          10 :                 csspp::node::pointer_t string(l.next_token());
    3290          10 :                 CATCH_REQUIRE(string->is(csspp::node_type_t::URL));
    3291          10 :                 CATCH_REQUIRE(string->get_string() == word);
    3292          10 :                 csspp::position const & npos(string->get_position());
    3293          10 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    3294          10 :                 CATCH_REQUIRE(npos.get_page() == 1);
    3295          10 :                 CATCH_REQUIRE(npos.get_line() == 1);
    3296          10 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    3297          10 :             }
    3298             : 
    3299          10 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3300          10 :         }
    3301             : 
    3302           1 :         VERIFY_ERRORS("");
    3303             :     }
    3304           4 :     CATCH_END_SECTION()
    3305             : 
    3306             :     // an invalid URL with EOF too soon
    3307           4 :     CATCH_START_SECTION("invalid URL with EOF too soon")
    3308             :     {
    3309          11 :         for(int count(0); count < 10; ++count)
    3310             :         {
    3311          10 :             std::stringstream ss;
    3312          10 :             size_t const len(rand() % 20 + 20);
    3313          10 :             std::string word;
    3314          10 :             ss << "url(";
    3315          10 :             size_t const leading(count == 0 ? 0 : rand() % 10);
    3316          43 :             for(size_t i(0); i < leading; ++i)
    3317             :             {
    3318          33 :                 ss << ' ';
    3319             :             }
    3320         305 :             for(size_t i(0); i < len; ++i)
    3321             :             {
    3322             :                 // simple ascii letters
    3323         295 :                 int const c(rand() % 26 + 'a');
    3324         295 :                 ss << static_cast<char>(c);
    3325         295 :                 word += static_cast<char>(c);
    3326             :             }
    3327          30 :             csspp::position pos("test.css");
    3328          10 :             csspp::lexer l(ss, pos);
    3329             : 
    3330             :             // url
    3331             :             {
    3332          10 :                 csspp::node::pointer_t string(l.next_token());
    3333          10 :                 CATCH_REQUIRE(string->is(csspp::node_type_t::URL));
    3334          10 :                 CATCH_REQUIRE(string->get_string() == word);
    3335          10 :                 csspp::position const & npos(string->get_position());
    3336          10 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    3337          10 :                 CATCH_REQUIRE(npos.get_page() == 1);
    3338          10 :                 CATCH_REQUIRE(npos.get_line() == 1);
    3339          10 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    3340             : 
    3341          10 :                 VERIFY_ERRORS(
    3342             :                         "test.css(1): error: found an invalid URL, one with forbidden characters.\n"
    3343             :                     );
    3344          10 :             }
    3345             : 
    3346          10 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3347          10 :         }
    3348             : 
    3349           1 :         VERIFY_ERRORS("");
    3350             :     }
    3351           4 :     CATCH_END_SECTION()
    3352             : 
    3353             :     // an invalid URL with '"', "'", '(', and non-printable
    3354           4 :     CATCH_START_SECTION("invalid URL with various unacceptable characters")
    3355             :     {
    3356           1 :         char const invalid_chars[] = "\"'(\x8\xb\xe\xf\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f";
    3357          26 :         for(size_t ic(0); ic < sizeof(invalid_chars) / sizeof(invalid_chars[0]); ++ic)
    3358             :         {
    3359         275 :             for(int count(0); count < 10; ++count)
    3360             :             {
    3361         250 :                 std::stringstream ss;
    3362         750 :                 csspp::position pos("test.css");
    3363         250 :                 csspp::lexer l(ss, pos);
    3364         250 :                 std::string word;
    3365         250 :                 ss << "url(";
    3366         250 :                 size_t const leading(count == 0 ? 0 : rand() % 10);
    3367        1224 :                 for(size_t i(0); i < leading; ++i)
    3368             :                 {
    3369         974 :                     ss << ' ';
    3370             :                 }
    3371         250 :                 size_t const len(rand() % 20 + 20);
    3372        7674 :                 for(size_t i(0); i < len; ++i)
    3373             :                 {
    3374             :                     // simple ascii letters
    3375        7424 :                     int const c(rand() % 26 + 'a');
    3376        7424 :                     ss << static_cast<char>(c);
    3377        7424 :                     word += static_cast<char>(c);
    3378             :                 }
    3379         250 :                 size_t const trailing(count & 1 ? 0 : rand() % 10);
    3380         838 :                 for(size_t i(0); i < trailing; ++i)
    3381             :                 {
    3382         588 :                     ss << ' ';
    3383             :                 }
    3384         250 :                 if(invalid_chars[ic])
    3385             :                 {
    3386         240 :                     ss << invalid_chars[ic];
    3387             :                 }
    3388             :                 else
    3389             :                 {
    3390             :                     // we reached the NULL, insert 0xFFFD instead
    3391          10 :                     ss << l.wctomb(0xFFFD);
    3392             :                 }
    3393             : 
    3394             :                 // url
    3395             :                 {
    3396         250 :                     csspp::node::pointer_t string(l.next_token());
    3397         250 :                     CATCH_REQUIRE(string->is(csspp::node_type_t::URL));
    3398         250 :                     CATCH_REQUIRE(string->get_string() == word);
    3399         250 :                     csspp::position const & npos(string->get_position());
    3400         250 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    3401         250 :                     CATCH_REQUIRE(npos.get_page() == 1);
    3402         250 :                     CATCH_REQUIRE(npos.get_line() == 1);
    3403         250 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    3404             : 
    3405         250 :                     VERIFY_ERRORS(
    3406             :                             trailing == 0
    3407             :                                 ? "test.css(1): error: found an invalid URL, one with forbidden characters.\n"
    3408             :                                 : "test.css(1): error: found an invalid URL, one which includes spaces or has a missing ')'.\n"
    3409             :                         );
    3410         250 :                 }
    3411             : 
    3412         250 :                 CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3413         250 :             }
    3414             :         }
    3415             : 
    3416           1 :         VERIFY_ERRORS("");
    3417             :     }
    3418           4 :     CATCH_END_SECTION()
    3419             : 
    3420             :     // no error left over
    3421           4 :     VERIFY_ERRORS("");
    3422           4 : }
    3423             : 
    3424           1 : CATCH_TEST_CASE("Functions", "[lexer] [identifier] [function]")
    3425             : {
    3426             :     // a few simple functions
    3427          11 :     for(int count(0); count < 10; ++count)
    3428             :     {
    3429          10 :         std::stringstream ss;
    3430          10 :         size_t const len(rand() % 20 + 20);
    3431          10 :         std::string word;
    3432         307 :         for(size_t i(0); i < len; ++i)
    3433             :         {
    3434             :             // simple ascii letters
    3435         297 :             int const c(rand() % 26 + 'a');
    3436         297 :             ss << static_cast<char>(c);
    3437         297 :             word += static_cast<char>(c);
    3438             :         }
    3439          10 :         ss << "(123)";
    3440          30 :         csspp::position pos("test.css");
    3441          10 :         csspp::lexer l(ss, pos);
    3442             : 
    3443             :         // function
    3444             :         {
    3445          10 :             csspp::node::pointer_t string(l.next_token());
    3446          10 :             CATCH_REQUIRE(string->is(csspp::node_type_t::FUNCTION));
    3447          10 :             CATCH_REQUIRE(string->get_string() == word);
    3448          10 :             csspp::position const & npos(string->get_position());
    3449          10 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3450          10 :             CATCH_REQUIRE(npos.get_page() == 1);
    3451          10 :             CATCH_REQUIRE(npos.get_line() == 1);
    3452          10 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3453          10 :         }
    3454             : 
    3455             :         // number
    3456             :         {
    3457          10 :             csspp::node::pointer_t string(l.next_token());
    3458          10 :             CATCH_REQUIRE(string->is(csspp::node_type_t::INTEGER));
    3459          10 :             CATCH_REQUIRE(string->get_integer() == 123);
    3460          10 :             csspp::position const & npos(string->get_position());
    3461          10 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3462          10 :             CATCH_REQUIRE(npos.get_page() == 1);
    3463          10 :             CATCH_REQUIRE(npos.get_line() == 1);
    3464          10 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3465          10 :         }
    3466             : 
    3467             :         // parenthesis
    3468             :         {
    3469          10 :             csspp::node::pointer_t string(l.next_token());
    3470          10 :             CATCH_REQUIRE(string->is(csspp::node_type_t::CLOSE_PARENTHESIS));
    3471          10 :             csspp::position const & npos(string->get_position());
    3472          10 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3473          10 :             CATCH_REQUIRE(npos.get_page() == 1);
    3474          10 :             CATCH_REQUIRE(npos.get_line() == 1);
    3475          10 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3476          10 :         }
    3477             : 
    3478          10 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3479          10 :     }
    3480             : 
    3481             :     // no error left over
    3482           1 :     VERIFY_ERRORS("");
    3483           1 : }
    3484             : 
    3485           1 : CATCH_TEST_CASE("Numbers", "[lexer] [number]")
    3486             : {
    3487             :     // a few simple integers
    3488       20002 :     for(int i(-10000); i <= 10000; ++i)
    3489             :     {
    3490       20001 :         std::stringstream ss;
    3491       20001 :         ss << i;
    3492       60003 :         csspp::position pos("test.css");
    3493       20001 :         csspp::lexer l(ss, pos);
    3494             : 
    3495             :         // sign
    3496             :         //if(i < 0)
    3497             :         //{
    3498             :         //    csspp::node::pointer_t integer(l.next_token());
    3499             :         //    CATCH_REQUIRE(integer->is(csspp::node_type_t::SUBTRACT));
    3500             :         //    csspp::position const & npos(integer->get_position());
    3501             :         //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    3502             :         //    CATCH_REQUIRE(npos.get_page() == 1);
    3503             :         //    CATCH_REQUIRE(npos.get_line() == 1);
    3504             :         //    CATCH_REQUIRE(npos.get_total_line() == 1);
    3505             :         //}
    3506             : 
    3507             :         // integer
    3508             :         {
    3509       20001 :             csspp::node::pointer_t integer(l.next_token());
    3510       20001 :             CATCH_REQUIRE(integer->is(csspp::node_type_t::INTEGER));
    3511       20001 :             CATCH_REQUIRE(integer->get_integer() == i);
    3512       20001 :             csspp::position const & npos(integer->get_position());
    3513       20001 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3514       20001 :             CATCH_REQUIRE(npos.get_page() == 1);
    3515       20001 :             CATCH_REQUIRE(npos.get_line() == 1);
    3516       20001 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3517       20001 :         }
    3518             : 
    3519       20001 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3520       20001 :     }
    3521             : 
    3522             :     // a few simple numbers (decimal / decimal)
    3523             :     // no exponent, but uses a sign
    3524      199477 :     for(int i(0); i < 1000000000; i += rand() % 10000 + 1)
    3525             :     {
    3526      199476 :         std::stringstream ss;
    3527      199476 :         bool const negative(rand() % 1 == 0);
    3528      199476 :         char const *sign(negative ? "-" : (rand() % 5 == 0 ? "+" : ""));
    3529      199476 :         ss << sign << i / 1000;
    3530      199476 :         bool const floating_point(i % 1000 != 0);
    3531      199476 :         if(floating_point)
    3532             :         {
    3533      199274 :             ss << "." << std::setw(3) << std::setfill('0') << i % 1000;
    3534             :         }
    3535      598428 :         csspp::position pos("test.css");
    3536      199476 :         csspp::lexer l(ss, pos);
    3537             : 
    3538             :         //if(negative)
    3539             :         //{
    3540             :         //    csspp::node::pointer_t subtract(l.next_token());
    3541             :         //    CATCH_REQUIRE(subtract->is(csspp::node_type_t::SUBTRACT));
    3542             :         //    csspp::position const & npos(subtract->get_position());
    3543             :         //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    3544             :         //    CATCH_REQUIRE(npos.get_page() == 1);
    3545             :         //    CATCH_REQUIRE(npos.get_line() == 1);
    3546             :         //    CATCH_REQUIRE(npos.get_total_line() == 1);
    3547             :         //}
    3548             :         //else if(*sign == '+')
    3549             :         //{
    3550             :         //    csspp::node::pointer_t subtract(l.next_token());
    3551             :         //    CATCH_REQUIRE(subtract->is(csspp::node_type_t::ADD));
    3552             :         //    csspp::position const & npos(subtract->get_position());
    3553             :         //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    3554             :         //    CATCH_REQUIRE(npos.get_page() == 1);
    3555             :         //    CATCH_REQUIRE(npos.get_line() == 1);
    3556             :         //    CATCH_REQUIRE(npos.get_total_line() == 1);
    3557             :         //}
    3558             : 
    3559      199476 :         if(floating_point)
    3560             :         {
    3561             :             // decimal number
    3562      199274 :             csspp::node::pointer_t string(l.next_token());
    3563      199274 :             CATCH_REQUIRE(string->is(csspp::node_type_t::DECIMAL_NUMBER));
    3564             : //std::cerr << "*** from [" << ss.str()
    3565             : //          << "] or [" << static_cast<csspp::decimal_number_t>(i) / 1000.0
    3566             : //          << "] we got [" << string->get_decimal_number()
    3567             : //          << "] |" << fabs(string->get_decimal_number())
    3568             : //          << "| (sign: " << (negative ? "-" : "+") << ")\n";
    3569      199274 :             CATCH_REQUIRE(fabs(fabs(string->get_decimal_number()) - static_cast<csspp::decimal_number_t>(i) / 1000.0) < 0.00001);
    3570      199274 :             if(negative)
    3571             :             {
    3572      199274 :                 CATCH_REQUIRE(string->get_decimal_number() <= 0.0);
    3573             :             }
    3574             :             else
    3575             :             {
    3576           0 :                 CATCH_REQUIRE(string->get_decimal_number() >= 0.0);
    3577             :             }
    3578      199274 :             csspp::position const & npos(string->get_position());
    3579      199274 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3580      199274 :             CATCH_REQUIRE(npos.get_page() == 1);
    3581      199274 :             CATCH_REQUIRE(npos.get_line() == 1);
    3582      199274 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3583      199274 :         }
    3584             :         else
    3585             :         {
    3586             :             // integer
    3587         202 :             csspp::node::pointer_t string(l.next_token());
    3588         202 :             CATCH_REQUIRE(string->is(csspp::node_type_t::INTEGER));
    3589         202 :             CATCH_REQUIRE(string->get_integer() == (negative ? -1 : 1) * i / 1000);
    3590         202 :             csspp::position const & npos(string->get_position());
    3591         202 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3592         202 :             CATCH_REQUIRE(npos.get_page() == 1);
    3593         202 :             CATCH_REQUIRE(npos.get_line() == 1);
    3594         202 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3595         202 :         }
    3596             : 
    3597      199476 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3598      199476 :     }
    3599             : 
    3600             :     // a few simple decimals with an exponent
    3601       20002 :     for(int i(-10000); i <= 10000; ++i)
    3602             :     {
    3603             :         // numbers starting with a digit (-0.3e5)
    3604             :         {
    3605       20001 :             std::stringstream ss;
    3606       20001 :             auto generate_sign = [](){
    3607       20001 :                     switch(rand() % 3)
    3608             :                     {
    3609        6756 :                     case 0:
    3610        6756 :                         return "-";
    3611             : 
    3612        6716 :                     case 1:
    3613        6716 :                         return "+";
    3614             : 
    3615        6529 :                     default: // case 2:
    3616        6529 :                         return "";
    3617             : 
    3618             :                     }
    3619             :                 };
    3620       20001 :             char const *exponent_sign(generate_sign());
    3621       20001 :             int const e(rand() % 100);
    3622       20001 :             char const *sign(i < 0 ? "-" : (rand() % 5 == 0 ? "+" : ""));
    3623       20001 :             ss << sign
    3624       20001 :                << abs(i) / 1000
    3625       20001 :                << "." << std::setw(3) << std::setfill('0') << abs(i) % 1000
    3626       20001 :                << std::setw(0) << std::setfill('\0') << "e" << exponent_sign << e;
    3627       60003 :             csspp::position pos("test.css");
    3628       20001 :             csspp::lexer l(ss, pos);
    3629       20001 :             csspp::decimal_number_t our_number(static_cast<csspp::decimal_number_t>(i) / 1000.0 * pow(10.0, e * (strcmp(exponent_sign, "-") == 0 ? -1 : 1)));
    3630             : //std::cerr << "*** from [" << ss.str()
    3631             : //          << "] or [" << our_number
    3632             : //          << "] sign & exponent [" << exponent_sign << "|" << e
    3633             : //          << "] ... ";
    3634             : 
    3635             :             // sign
    3636             :             //if(i < 0)
    3637             :             //{
    3638             :             //    csspp::node::pointer_t subtract(l.next_token());
    3639             :             //    CATCH_REQUIRE(subtract->is(csspp::node_type_t::SUBTRACT));
    3640             :             //    csspp::position const & npos(subtract->get_position());
    3641             :             //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    3642             :             //    CATCH_REQUIRE(npos.get_page() == 1);
    3643             :             //    CATCH_REQUIRE(npos.get_line() == 1);
    3644             :             //    CATCH_REQUIRE(npos.get_total_line() == 1);
    3645             :             //}
    3646             :             //else if(*sign == '+')
    3647             :             //{
    3648             :             //    csspp::node::pointer_t subtract(l.next_token());
    3649             :             //    CATCH_REQUIRE(subtract->is(csspp::node_type_t::ADD));
    3650             :             //    csspp::position const & npos(subtract->get_position());
    3651             :             //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    3652             :             //    CATCH_REQUIRE(npos.get_page() == 1);
    3653             :             //    CATCH_REQUIRE(npos.get_line() == 1);
    3654             :             //    CATCH_REQUIRE(npos.get_total_line() == 1);
    3655             :             //}
    3656             : 
    3657             :             // decimal number
    3658             :             {
    3659       20001 :                 csspp::node::pointer_t decimal_number(l.next_token());
    3660             : //std::cerr << "*** type is " << static_cast<int>(decimal_number->get_type()) << " ***\n";
    3661       20001 :                 CATCH_REQUIRE(decimal_number->is(csspp::node_type_t::DECIMAL_NUMBER));
    3662       20001 :                 csspp::decimal_number_t const result(fabs(decimal_number->get_decimal_number()));
    3663       20001 :                 csspp::decimal_number_t const abs_number(fabs(our_number));
    3664       20001 :                 csspp::decimal_number_t const delta(fabs(result - abs_number));
    3665       20001 :                 csspp::decimal_number_t const diff(delta / pow(10.0, e * (strcmp(exponent_sign, "-") == 0 ? -1 : 1)));
    3666             : 
    3667             : //std::cerr << "we got [" << result
    3668             : //          << "| vs |" << abs_number
    3669             : //          << "| diff = " << diff
    3670             : //          << "\n";
    3671             : //csspp::decimal_number_t r(fabs(decimal_number->get_decimal_number()));
    3672             : //csspp::decimal_number_t q(fabs(our_number));
    3673             : //std::cerr << std::hex
    3674             : //          << *reinterpret_cast<int64_t *>(&r) << "\n"
    3675             : //          << *reinterpret_cast<int64_t *>(&q) << " " << (r - q) << " -> " << diff << "\n";
    3676       20001 :                 CATCH_REQUIRE(diff < 0.00001);
    3677       20001 :                 if(*sign == '-')
    3678             :                 {
    3679       10000 :                     CATCH_REQUIRE(decimal_number->get_decimal_number() <= 0);
    3680             :                 }
    3681             :                 else
    3682             :                 {
    3683       10001 :                     CATCH_REQUIRE(decimal_number->get_decimal_number() >= 0);
    3684             :                 }
    3685       20001 :                 csspp::position const & npos(decimal_number->get_position());
    3686       20001 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    3687       20001 :                 CATCH_REQUIRE(npos.get_page() == 1);
    3688       20001 :                 CATCH_REQUIRE(npos.get_line() == 1);
    3689       20001 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    3690       20001 :             }
    3691             : 
    3692       20001 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3693       20001 :         }
    3694             : 
    3695             :         // numbers starting with a period (.25e3)
    3696       20001 :         if(i >= -999 && i <= 999)
    3697             :         {
    3698        1999 :             std::stringstream ss;
    3699        1999 :             auto generate_sign = [](){
    3700        1999 :                     switch(rand() % 3)
    3701             :                     {
    3702         651 :                     case 0:
    3703         651 :                         return "-";
    3704             : 
    3705         707 :                     case 1:
    3706         707 :                         return "+";
    3707             : 
    3708         641 :                     default: // case 2:
    3709         641 :                         return "";
    3710             : 
    3711             :                     }
    3712             :                 };
    3713        1999 :             char const *exponent_sign(generate_sign());
    3714        1999 :             int const e(rand() % 100);
    3715        1999 :             char const *sign(i < 0 ? "-" : (rand() % 5 == 0 ? "+" : ""));
    3716             :             ss << sign
    3717             :                //<< abs(i) / 1000 -- this would always be '0' here
    3718        1999 :                << "." << std::setw(3) << std::setfill('0') << abs(i) % 1000
    3719        1999 :                << std::setw(0) << std::setfill('\0') << "e" << exponent_sign << e;
    3720        5997 :             csspp::position pos("test.css");
    3721        1999 :             csspp::lexer l(ss, pos);
    3722        1999 :             csspp::decimal_number_t our_number(static_cast<csspp::decimal_number_t>(i) / 1000.0 * pow(10.0, e * (strcmp(exponent_sign, "-") == 0 ? -1 : 1)));
    3723             : //std::cerr << "*** from [" << ss.str()
    3724             : //          << "] or [" << our_number
    3725             : //          << "] sign & exponent [" << exponent_sign << "|" << e
    3726             : //          << "] ... ";
    3727             : 
    3728             :             // sign
    3729             :             //if(i < 0)
    3730             :             //{
    3731             :             //    csspp::node::pointer_t subtract(l.next_token());
    3732             :             //    CATCH_REQUIRE(subtract->is(csspp::node_type_t::SUBTRACT));
    3733             :             //    csspp::position const & npos(subtract->get_position());
    3734             :             //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    3735             :             //    CATCH_REQUIRE(npos.get_page() == 1);
    3736             :             //    CATCH_REQUIRE(npos.get_line() == 1);
    3737             :             //    CATCH_REQUIRE(npos.get_total_line() == 1);
    3738             :             //}
    3739             :             //else if(*sign == '+')
    3740             :             //{
    3741             :             //    csspp::node::pointer_t subtract(l.next_token());
    3742             :             //    CATCH_REQUIRE(subtract->is(csspp::node_type_t::ADD));
    3743             :             //    csspp::position const & npos(subtract->get_position());
    3744             :             //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    3745             :             //    CATCH_REQUIRE(npos.get_page() == 1);
    3746             :             //    CATCH_REQUIRE(npos.get_line() == 1);
    3747             :             //    CATCH_REQUIRE(npos.get_total_line() == 1);
    3748             :             //}
    3749             : 
    3750             :             // decimal number
    3751             :             {
    3752        1999 :                 csspp::node::pointer_t decimal_number(l.next_token());
    3753        1999 :                 CATCH_REQUIRE(decimal_number->is(csspp::node_type_t::DECIMAL_NUMBER));
    3754        1999 :                 csspp::decimal_number_t const result(fabs(decimal_number->get_decimal_number()));
    3755        1999 :                 csspp::decimal_number_t const abs_number(fabs(our_number));
    3756        1999 :                 csspp::decimal_number_t const delta(fabs(result - abs_number));
    3757        1999 :                 csspp::decimal_number_t const diff(delta / pow(10.0, e * (strcmp(exponent_sign, "-") == 0 ? -1 : 1)));
    3758             : 
    3759             : //std::cerr << "we got [" << result
    3760             : //          << "| vs |" << abs_number
    3761             : //          << "| diff = " << diff
    3762             : //          << "\n";
    3763             : //csspp::decimal_number_t r(fabs(decimal_number->get_decimal_number()));
    3764             : //csspp::decimal_number_t q(fabs(our_number));
    3765             : //std::cerr << std::hex
    3766             : //          << *reinterpret_cast<int64_t *>(&r) << "\n"
    3767             : //          << *reinterpret_cast<int64_t *>(&q) << " " << (r - q) << " -> " << diff << "\n";
    3768        1999 :                 CATCH_REQUIRE(diff < 0.00001);
    3769        1999 :                 if(*sign == '-')
    3770             :                 {
    3771         999 :                     CATCH_REQUIRE(decimal_number->get_decimal_number() <= 0);
    3772             :                 }
    3773             :                 else
    3774             :                 {
    3775        1000 :                     CATCH_REQUIRE(decimal_number->get_decimal_number() >= 0);
    3776             :                 }
    3777        1999 :                 csspp::position const & npos(decimal_number->get_position());
    3778        1999 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    3779        1999 :                 CATCH_REQUIRE(npos.get_page() == 1);
    3780        1999 :                 CATCH_REQUIRE(npos.get_line() == 1);
    3781        1999 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    3782        1999 :             }
    3783             : 
    3784        1999 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3785        1999 :         }
    3786             :     }
    3787             : 
    3788             :     // check maximum 64 bit number
    3789             :     {
    3790           1 :         std::stringstream ss;
    3791             :         // largest 64 bit positive number is 9223372036854775807
    3792           1 :         ss << "perfect: 9223372036854775807";
    3793           3 :         csspp::position pos("test.css");
    3794           1 :         csspp::lexer l(ss, pos);
    3795             : 
    3796             :         // identifier
    3797             :         {
    3798           1 :             csspp::node::pointer_t identifier(l.next_token());
    3799           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    3800           1 :             CATCH_REQUIRE(identifier->get_string() == "perfect");
    3801           1 :             csspp::position const & npos(identifier->get_position());
    3802           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3803           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3804           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3805           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3806           1 :         }
    3807             : 
    3808             :         // colon
    3809             :         {
    3810           1 :             csspp::node::pointer_t colon(l.next_token());
    3811           1 :             CATCH_REQUIRE(colon->is(csspp::node_type_t::COLON));
    3812           1 :             csspp::position const & npos(colon->get_position());
    3813           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3814           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3815           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3816           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3817           1 :         }
    3818             : 
    3819             :         // whitespace
    3820             :         {
    3821           1 :             csspp::node::pointer_t whitespace(l.next_token());
    3822           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    3823           1 :             csspp::position const & npos(whitespace->get_position());
    3824           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3825           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3826           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3827           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3828           1 :         }
    3829             : 
    3830             :         // decimal number
    3831             :         {
    3832           1 :             csspp::node::pointer_t integer(l.next_token());
    3833           1 :             CATCH_REQUIRE(integer->is(csspp::node_type_t::INTEGER));
    3834           1 :             CATCH_REQUIRE(integer->get_integer() == 9223372036854775807LL);
    3835           1 :             csspp::position const & npos(integer->get_position());
    3836           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3837           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3838           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3839           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3840           1 :         }
    3841             : 
    3842           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3843           1 :     }
    3844             : 
    3845             :     // check a number so large that it fails
    3846             :     {
    3847           1 :         std::stringstream ss;
    3848             :         // largest 64 bit positive number is 9223372036854775807
    3849           1 :         ss << "too\\ large: 10000000000000000000";
    3850           3 :         csspp::position pos("test.css");
    3851           1 :         csspp::lexer l(ss, pos);
    3852             : 
    3853             :         // identifier
    3854             :         {
    3855           1 :             csspp::node::pointer_t identifier(l.next_token());
    3856           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    3857           1 :             CATCH_REQUIRE(identifier->get_string() == "too large");
    3858           1 :             csspp::position const & npos(identifier->get_position());
    3859           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3860           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3861           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3862           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3863           1 :         }
    3864             : 
    3865             :         // colon
    3866             :         {
    3867           1 :             csspp::node::pointer_t colon(l.next_token());
    3868           1 :             CATCH_REQUIRE(colon->is(csspp::node_type_t::COLON));
    3869           1 :             csspp::position const & npos(colon->get_position());
    3870           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3871           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3872           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3873           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3874           1 :         }
    3875             : 
    3876             :         // whitespace
    3877             :         {
    3878           1 :             csspp::node::pointer_t whitespace(l.next_token());
    3879           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    3880           1 :             csspp::position const & npos(whitespace->get_position());
    3881           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3882           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3883           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3884           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3885           1 :         }
    3886             : 
    3887             :         // integer
    3888             :         {
    3889           1 :             csspp::node::pointer_t integer(l.next_token());
    3890           1 :             CATCH_REQUIRE(integer->is(csspp::node_type_t::INTEGER));
    3891             :             //CATCH_REQUIRE(integer->get_integer() == ???); -- there is an overflow so we decide not to replicate it here
    3892           1 :             csspp::position const & npos(integer->get_position());
    3893           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3894           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3895           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3896           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3897             : 
    3898           1 :             VERIFY_ERRORS("test.css(1): error: integral part too large for a number.\n");
    3899           1 :         }
    3900             : 
    3901           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3902           1 :     }
    3903             : 
    3904             :     // check a decimal part that's too large
    3905             :     // (we limit those to 8 digits at this time)
    3906             :     {
    3907           1 :         std::stringstream ss;
    3908           1 :         ss << "decimal_part_too_long: 1.000000000000000000000";
    3909           3 :         csspp::position pos("test.css");
    3910           1 :         csspp::lexer l(ss, pos);
    3911             : 
    3912             :         // identifier
    3913             :         {
    3914           1 :             csspp::node::pointer_t identifier(l.next_token());
    3915           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    3916           1 :             CATCH_REQUIRE(identifier->get_string() == "decimal_part_too_long");
    3917           1 :             csspp::position const & npos(identifier->get_position());
    3918           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3919           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3920           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3921           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3922           1 :         }
    3923             : 
    3924             :         // colon
    3925             :         {
    3926           1 :             csspp::node::pointer_t colon(l.next_token());
    3927           1 :             CATCH_REQUIRE(colon->is(csspp::node_type_t::COLON));
    3928           1 :             csspp::position const & npos(colon->get_position());
    3929           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3930           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3931           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3932           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3933           1 :         }
    3934             : 
    3935             :         // whitespace
    3936             :         {
    3937           1 :             csspp::node::pointer_t whitespace(l.next_token());
    3938           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    3939           1 :             csspp::position const & npos(whitespace->get_position());
    3940           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3941           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3942           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3943           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3944           1 :         }
    3945             : 
    3946             :         // decimal number
    3947             :         {
    3948           1 :             csspp::node::pointer_t decimal_number(l.next_token());
    3949           1 :             CATCH_REQUIRE(decimal_number->is(csspp::node_type_t::DECIMAL_NUMBER));
    3950             :             //CATCH_REQUIRE(decimal_number->get_decimal_number() == ???); -- there may be an overflow so we decide not to replicate it here
    3951           1 :             csspp::position const & npos(decimal_number->get_position());
    3952           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3953           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3954           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3955           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3956             : 
    3957           1 :             VERIFY_ERRORS("test.css(1): error: fraction too large for a decimal number.\n");
    3958           1 :         }
    3959             : 
    3960           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    3961           1 :     }
    3962             : 
    3963             :     // decimal number with no digits in the decimal fraction part is an error
    3964             :     {
    3965           1 :         std::stringstream ss;
    3966           1 :         ss << "font-size: 154.;";
    3967           3 :         csspp::position pos("test.css");
    3968           1 :         csspp::lexer l(ss, pos);
    3969             : 
    3970             :         // identifier
    3971             :         {
    3972           1 :             csspp::node::pointer_t identifier(l.next_token());
    3973           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    3974           1 :             CATCH_REQUIRE(identifier->get_string() == "font-size");
    3975           1 :             csspp::position const & npos(identifier->get_position());
    3976           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3977           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3978           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3979           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3980           1 :         }
    3981             : 
    3982             :         // colon
    3983             :         {
    3984           1 :             csspp::node::pointer_t colon(l.next_token());
    3985           1 :             CATCH_REQUIRE(colon->is(csspp::node_type_t::COLON));
    3986           1 :             csspp::position const & npos(colon->get_position());
    3987           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3988           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    3989           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    3990           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    3991           1 :         }
    3992             : 
    3993             :         // whitespace
    3994             :         {
    3995           1 :             csspp::node::pointer_t whitespace(l.next_token());
    3996           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    3997           1 :             csspp::position const & npos(whitespace->get_position());
    3998           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    3999           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4000           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4001           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4002           1 :         }
    4003             : 
    4004             :         // decimal number
    4005             :         {
    4006           1 :             csspp::node::pointer_t decimal_number(l.next_token());
    4007           1 :             CATCH_REQUIRE(decimal_number->is(csspp::node_type_t::DECIMAL_NUMBER));
    4008           1 :             CATCH_REQUIRE(decimal_number->get_decimal_number() == 154.0_a);
    4009           1 :             csspp::position const & npos(decimal_number->get_position());
    4010           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4011           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4012           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4013           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4014             : 
    4015           1 :             VERIFY_ERRORS("test.css(1): error: decimal number must have at least one digit after the decimal point.\n");
    4016           1 :         }
    4017             : 
    4018             :         // semi-colon
    4019             :         {
    4020           1 :             csspp::node::pointer_t whitespace(l.next_token());
    4021           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::SEMICOLON));
    4022           1 :             csspp::position const & npos(whitespace->get_position());
    4023           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4024           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4025           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4026           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4027           1 :         }
    4028             : 
    4029           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    4030           1 :     }
    4031             : 
    4032             :     // try parsing an expression
    4033             :     {
    4034           1 :         std::stringstream ss;
    4035           1 :         ss << "font-size: 154*3;";
    4036           3 :         csspp::position pos("test.css");
    4037           1 :         csspp::lexer l(ss, pos);
    4038             : 
    4039             :         // identifier
    4040             :         {
    4041           1 :             csspp::node::pointer_t identifier(l.next_token());
    4042           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    4043           1 :             CATCH_REQUIRE(identifier->get_string() == "font-size");
    4044           1 :             csspp::position const & npos(identifier->get_position());
    4045           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4046           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4047           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4048           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4049           1 :         }
    4050             : 
    4051             :         // colon
    4052             :         {
    4053           1 :             csspp::node::pointer_t colon(l.next_token());
    4054           1 :             CATCH_REQUIRE(colon->is(csspp::node_type_t::COLON));
    4055           1 :             csspp::position const & npos(colon->get_position());
    4056           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4057           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4058           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4059           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4060           1 :         }
    4061             : 
    4062             :         // whitespace
    4063             :         {
    4064           1 :             csspp::node::pointer_t whitespace(l.next_token());
    4065           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    4066           1 :             csspp::position const & npos(whitespace->get_position());
    4067           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4068           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4069           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4070           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4071           1 :         }
    4072             : 
    4073             :         // integer
    4074             :         {
    4075           1 :             csspp::node::pointer_t integer(l.next_token());
    4076           1 :             CATCH_REQUIRE(integer->is(csspp::node_type_t::INTEGER));
    4077           1 :             CATCH_REQUIRE(integer->get_integer() == 154);
    4078           1 :             csspp::position const & npos(integer->get_position());
    4079           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4080           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4081           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4082           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4083           1 :         }
    4084             : 
    4085             :         // multiply
    4086             :         {
    4087           1 :             csspp::node::pointer_t integer(l.next_token());
    4088           1 :             CATCH_REQUIRE(integer->is(csspp::node_type_t::MULTIPLY));
    4089           1 :             csspp::position const & npos(integer->get_position());
    4090           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4091           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4092           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4093           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4094           1 :         }
    4095             : 
    4096             :         // integer
    4097             :         {
    4098           1 :             csspp::node::pointer_t integer(l.next_token());
    4099           1 :             CATCH_REQUIRE(integer->is(csspp::node_type_t::INTEGER));
    4100           1 :             CATCH_REQUIRE(integer->get_integer() == 3);
    4101           1 :             csspp::position const & npos(integer->get_position());
    4102           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4103           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4104           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4105           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4106           1 :         }
    4107             : 
    4108             :         // semi-colon
    4109             :         {
    4110           1 :             csspp::node::pointer_t whitespace(l.next_token());
    4111           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::SEMICOLON));
    4112           1 :             csspp::position const & npos(whitespace->get_position());
    4113           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4114           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4115           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4116           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4117           1 :         }
    4118             : 
    4119           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    4120           1 :     }
    4121             : 
    4122             :     // test with an exponent that's too large
    4123             :     {
    4124           1 :         std::stringstream ss;
    4125           1 :         ss << "font-size: 154e-1024;";
    4126           3 :         csspp::position pos("test.css");
    4127           1 :         csspp::lexer l(ss, pos);
    4128             : 
    4129             :         // identifier
    4130             :         {
    4131           1 :             csspp::node::pointer_t identifier(l.next_token());
    4132           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    4133           1 :             CATCH_REQUIRE(identifier->get_string() == "font-size");
    4134           1 :             csspp::position const & npos(identifier->get_position());
    4135           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4136           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4137           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4138           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4139           1 :         }
    4140             : 
    4141             :         // colon
    4142             :         {
    4143           1 :             csspp::node::pointer_t colon(l.next_token());
    4144           1 :             CATCH_REQUIRE(colon->is(csspp::node_type_t::COLON));
    4145           1 :             csspp::position const & npos(colon->get_position());
    4146           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4147           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4148           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4149           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4150           1 :         }
    4151             : 
    4152             :         // whitespace
    4153             :         {
    4154           1 :             csspp::node::pointer_t whitespace(l.next_token());
    4155           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    4156           1 :             csspp::position const & npos(whitespace->get_position());
    4157           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4158           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4159           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4160           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4161           1 :         }
    4162             : 
    4163             :         // decimal number
    4164             :         {
    4165           1 :             csspp::node::pointer_t decimal_number(l.next_token());
    4166           1 :             CATCH_REQUIRE(decimal_number->is(csspp::node_type_t::DECIMAL_NUMBER));
    4167             :             //CATCH_REQUIRE(decimal_number->get_decimal_number() == ...); -- this is not a valid number
    4168           1 :             csspp::position const & npos(decimal_number->get_position());
    4169           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4170           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4171           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4172           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4173             : 
    4174           1 :             VERIFY_ERRORS("test.css(1): error: exponent too large for a decimal number.\n");
    4175           1 :         }
    4176             : 
    4177             :         // semi-colon
    4178             :         {
    4179           1 :             csspp::node::pointer_t whitespace(l.next_token());
    4180           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::SEMICOLON));
    4181           1 :             csspp::position const & npos(whitespace->get_position());
    4182           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4183           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4184           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4185           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4186           1 :         }
    4187             : 
    4188           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    4189           1 :     }
    4190             : 
    4191             :     // no error left over
    4192           1 :     VERIFY_ERRORS("");
    4193           1 : }
    4194             : 
    4195           1 : CATCH_TEST_CASE("Dimensions", "[lexer] [number] [dimension] [identifier]")
    4196             : {
    4197             :     // well known dimensions with integers
    4198             :     {
    4199           1 :         char const *dimensions[] = {
    4200             :             "em",       // character em
    4201             :             "px",       // pixel
    4202             :             "pt"        // point
    4203             :         };
    4204       20002 :         for(int i(-10000); i <= 10000; ++i)
    4205             :         {
    4206       80004 :             for(size_t j(0); j < sizeof(dimensions) / sizeof(dimensions[0]); ++j)
    4207             :             {
    4208       60003 :                 std::stringstream ss;
    4209       60003 :                 ss << i << dimensions[j];
    4210             :                 // direct escape?
    4211       60003 :                 if(dimensions[j][0] > 'f')
    4212             :                 {
    4213       40002 :                     ss << "," << i << "\\" << dimensions[j];
    4214             :                 }
    4215      180009 :                 ss << "," << i << "\\" << std::hex << static_cast<int>(dimensions[j][0]) << " " << std::string(dimensions[j]).substr(1)
    4216      120006 :                    << "," << std::dec << i << " " << dimensions[j]; // prove it does not work when we have a space
    4217      180009 :                 csspp::position pos("test.css");
    4218       60003 :                 csspp::lexer l(ss, pos);
    4219             : 
    4220             :                 // sign
    4221             :                 //if(i < 0)
    4222             :                 //{
    4223             :                 //    csspp::node::pointer_t integer(l.next_token());
    4224             :                 //    CATCH_REQUIRE(integer->is(csspp::node_type_t::SUBTRACT));
    4225             :                 //    csspp::position const & npos(integer->get_position());
    4226             :                 //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    4227             :                 //    CATCH_REQUIRE(npos.get_page() == 1);
    4228             :                 //    CATCH_REQUIRE(npos.get_line() == 1);
    4229             :                 //    CATCH_REQUIRE(npos.get_total_line() == 1);
    4230             :                 //}
    4231             : 
    4232             :                 // dimension
    4233             :                 {
    4234             :                     // a dimension is an integer or a decimal number
    4235             :                     // with a string expressing the dimension
    4236       60003 :                     csspp::node::pointer_t dimension(l.next_token());
    4237       60003 :                     CATCH_REQUIRE(dimension->is(csspp::node_type_t::INTEGER));
    4238       60003 :                     CATCH_REQUIRE(dimension->get_integer() == i);
    4239       60003 :                     CATCH_REQUIRE(dimension->get_string() == dimensions[j]);
    4240       60003 :                     csspp::position const & npos(dimension->get_position());
    4241       60003 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4242       60003 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4243       60003 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4244       60003 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4245       60003 :                 }
    4246             : 
    4247             :                 // direct escape?
    4248       60003 :                 if(dimensions[j][0] > 'f')
    4249             :                 {
    4250             :                     // comma
    4251             :                     {
    4252       40002 :                         csspp::node::pointer_t comma(l.next_token());
    4253       40002 :                         CATCH_REQUIRE(comma->is(csspp::node_type_t::COMMA));
    4254       40002 :                         csspp::position const & npos(comma->get_position());
    4255       40002 :                         CATCH_REQUIRE(npos.get_filename() == "test.css");
    4256       40002 :                         CATCH_REQUIRE(npos.get_page() == 1);
    4257       40002 :                         CATCH_REQUIRE(npos.get_line() == 1);
    4258       40002 :                         CATCH_REQUIRE(npos.get_total_line() == 1);
    4259       40002 :                     }
    4260             : 
    4261             :                     // sign
    4262             :                     //if(i < 0)
    4263             :                     //{
    4264             :                     //    csspp::node::pointer_t integer(l.next_token());
    4265             :                     //    CATCH_REQUIRE(integer->is(csspp::node_type_t::SUBTRACT));
    4266             :                     //    csspp::position const & npos(integer->get_position());
    4267             :                     //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    4268             :                     //    CATCH_REQUIRE(npos.get_page() == 1);
    4269             :                     //    CATCH_REQUIRE(npos.get_line() == 1);
    4270             :                     //    CATCH_REQUIRE(npos.get_total_line() == 1);
    4271             :                     //}
    4272             : 
    4273             :                     // dimension
    4274             :                     {
    4275             :                         // a dimension is an integer or a decimal number
    4276             :                         // with a string expressing the dimension
    4277       40002 :                         csspp::node::pointer_t dimension(l.next_token());
    4278       40002 :                         CATCH_REQUIRE(dimension->is(csspp::node_type_t::INTEGER));
    4279       40002 :                         CATCH_REQUIRE(dimension->get_integer() == i);
    4280       40002 :                         CATCH_REQUIRE(dimension->get_string() == dimensions[j]);
    4281       40002 :                         csspp::position const & npos(dimension->get_position());
    4282       40002 :                         CATCH_REQUIRE(npos.get_filename() == "test.css");
    4283       40002 :                         CATCH_REQUIRE(npos.get_page() == 1);
    4284       40002 :                         CATCH_REQUIRE(npos.get_line() == 1);
    4285       40002 :                         CATCH_REQUIRE(npos.get_total_line() == 1);
    4286       40002 :                     }
    4287             :                 }
    4288             : 
    4289             :                 // comma
    4290             :                 {
    4291       60003 :                     csspp::node::pointer_t comma(l.next_token());
    4292       60003 :                     CATCH_REQUIRE(comma->is(csspp::node_type_t::COMMA));
    4293       60003 :                     csspp::position const & npos(comma->get_position());
    4294       60003 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4295       60003 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4296       60003 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4297       60003 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4298       60003 :                 }
    4299             : 
    4300             :                 // sign
    4301             :                 //if(i < 0)
    4302             :                 //{
    4303             :                 //    csspp::node::pointer_t integer(l.next_token());
    4304             :                 //    CATCH_REQUIRE(integer->is(csspp::node_type_t::SUBTRACT));
    4305             :                 //    csspp::position const & npos(integer->get_position());
    4306             :                 //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    4307             :                 //    CATCH_REQUIRE(npos.get_page() == 1);
    4308             :                 //    CATCH_REQUIRE(npos.get_line() == 1);
    4309             :                 //    CATCH_REQUIRE(npos.get_total_line() == 1);
    4310             :                 //}
    4311             : 
    4312             :                 // dimension
    4313             :                 {
    4314             :                     // a dimension is an integer or a decimal number
    4315             :                     // with a string expressing the dimension
    4316       60003 :                     csspp::node::pointer_t dimension(l.next_token());
    4317       60003 :                     CATCH_REQUIRE(dimension->is(csspp::node_type_t::INTEGER));
    4318       60003 :                     CATCH_REQUIRE(dimension->get_integer() == i);
    4319       60003 :                     CATCH_REQUIRE(dimension->get_string() == dimensions[j]);
    4320       60003 :                     csspp::position const & npos(dimension->get_position());
    4321       60003 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4322       60003 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4323       60003 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4324       60003 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4325       60003 :                 }
    4326             : 
    4327             :                 // comma
    4328             :                 {
    4329       60003 :                     csspp::node::pointer_t comma(l.next_token());
    4330       60003 :                     CATCH_REQUIRE(comma->is(csspp::node_type_t::COMMA));
    4331       60003 :                     csspp::position const & npos(comma->get_position());
    4332       60003 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4333       60003 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4334       60003 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4335       60003 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4336       60003 :                 }
    4337             : 
    4338             :                 // sign
    4339             :                 //if(i < 0)
    4340             :                 //{
    4341             :                 //    csspp::node::pointer_t integer(l.next_token());
    4342             :                 //    CATCH_REQUIRE(integer->is(csspp::node_type_t::SUBTRACT));
    4343             :                 //    csspp::position const & npos(integer->get_position());
    4344             :                 //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    4345             :                 //    CATCH_REQUIRE(npos.get_page() == 1);
    4346             :                 //    CATCH_REQUIRE(npos.get_line() == 1);
    4347             :                 //    CATCH_REQUIRE(npos.get_total_line() == 1);
    4348             :                 //}
    4349             : 
    4350             :                 // integer (separated!)
    4351             :                 {
    4352       60003 :                     csspp::node::pointer_t integer(l.next_token());
    4353       60003 :                     CATCH_REQUIRE(integer->is(csspp::node_type_t::INTEGER));
    4354       60003 :                     CATCH_REQUIRE(integer->get_integer() == i);
    4355       60003 :                     CATCH_REQUIRE(integer->get_string() == "");
    4356       60003 :                     csspp::position const & npos(integer->get_position());
    4357       60003 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4358       60003 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4359       60003 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4360       60003 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4361       60003 :                 }
    4362             : 
    4363             :                 // whitespace
    4364             :                 {
    4365       60003 :                     csspp::node::pointer_t whitespace(l.next_token());
    4366       60003 :                     CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    4367       60003 :                     csspp::position const & npos(whitespace->get_position());
    4368       60003 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4369       60003 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4370       60003 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4371       60003 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4372       60003 :                 }
    4373             : 
    4374             :                 // "dimension" (as a separate identifier)
    4375             :                 {
    4376             :                     // a dimension is an integer or a decimal number
    4377             :                     // with a string expressing the dimension
    4378       60003 :                     csspp::node::pointer_t dimension(l.next_token());
    4379       60003 :                     CATCH_REQUIRE(dimension->is(csspp::node_type_t::IDENTIFIER));
    4380       60003 :                     CATCH_REQUIRE(dimension->get_string() == dimensions[j]);
    4381       60003 :                     csspp::position const & npos(dimension->get_position());
    4382       60003 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4383       60003 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4384       60003 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4385       60003 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4386       60003 :                 }
    4387       60003 :                 CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    4388       60003 :             }
    4389             :         }
    4390             :     }
    4391             : 
    4392             :     // well known dimensions with decimal numbers
    4393             :     {
    4394           1 :         char const *dimensions[] = {
    4395             :             "em",       // character em
    4396             :             "px",       // pixel
    4397             :             "pt"        // point
    4398             :         };
    4399        2002 :         for(int i(-1000); i <= 1000; ++i)
    4400             :         {
    4401        8004 :             for(size_t j(0); j < sizeof(dimensions) / sizeof(dimensions[0]); ++j)
    4402             :             {
    4403        6003 :                 std::stringstream ss;
    4404        6003 :                 ss << (i < 0 ? "-" : "") << abs(i) / 100 << "." << std::setw(2) << std::setfill('0') << abs(i % 100) << dimensions[j]
    4405        6003 :                    << "," << (i < 0 ? "-" : "") << abs(i) / 100 << "." << std::setw(2) << abs(i % 100) << " " << dimensions[j]; // prove it does not work when we have a space
    4406       18009 :                 csspp::position pos("test.css");
    4407        6003 :                 csspp::lexer l(ss, pos);
    4408             : 
    4409             :                 // sign
    4410             :                 //if(i < 0)
    4411             :                 //{
    4412             :                 //    csspp::node::pointer_t integer(l.next_token());
    4413             :                 //    CATCH_REQUIRE(integer->is(csspp::node_type_t::SUBTRACT));
    4414             :                 //    csspp::position const & npos(integer->get_position());
    4415             :                 //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    4416             :                 //    CATCH_REQUIRE(npos.get_page() == 1);
    4417             :                 //    CATCH_REQUIRE(npos.get_line() == 1);
    4418             :                 //    CATCH_REQUIRE(npos.get_total_line() == 1);
    4419             :                 //}
    4420             : 
    4421             :                 // dimension
    4422             :                 {
    4423             :                     // a dimension is an integer or a decimal number
    4424             :                     // with a string expressing the dimension
    4425        6003 :                     csspp::node::pointer_t dimension(l.next_token());
    4426        6003 :                     CATCH_REQUIRE(dimension->is(csspp::node_type_t::DECIMAL_NUMBER));
    4427        6003 :                     CATCH_REQUIRE(fabs(dimension->get_decimal_number() - i / 100.0) < 0.00001);
    4428        6003 :                     CATCH_REQUIRE(dimension->get_string() == dimensions[j]);
    4429        6003 :                     csspp::position const & npos(dimension->get_position());
    4430        6003 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4431        6003 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4432        6003 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4433        6003 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4434        6003 :                 }
    4435             : 
    4436             :                 // comma
    4437             :                 {
    4438        6003 :                     csspp::node::pointer_t comma(l.next_token());
    4439        6003 :                     CATCH_REQUIRE(comma->is(csspp::node_type_t::COMMA));
    4440        6003 :                     csspp::position const & npos(comma->get_position());
    4441        6003 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4442        6003 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4443        6003 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4444        6003 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4445        6003 :                 }
    4446             : 
    4447             :                 // sign
    4448             :                 //if(i < 0)
    4449             :                 //{
    4450             :                 //    csspp::node::pointer_t integer(l.next_token());
    4451             :                 //    CATCH_REQUIRE(integer->is(csspp::node_type_t::SUBTRACT));
    4452             :                 //    csspp::position const & npos(integer->get_position());
    4453             :                 //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    4454             :                 //    CATCH_REQUIRE(npos.get_page() == 1);
    4455             :                 //    CATCH_REQUIRE(npos.get_line() == 1);
    4456             :                 //    CATCH_REQUIRE(npos.get_total_line() == 1);
    4457             :                 //}
    4458             : 
    4459             :                 // decimal number (separated!)
    4460             :                 {
    4461        6003 :                     csspp::node::pointer_t decimal_number(l.next_token());
    4462        6003 :                     CATCH_REQUIRE(decimal_number->is(csspp::node_type_t::DECIMAL_NUMBER));
    4463        6003 :                     CATCH_REQUIRE(fabs(decimal_number->get_decimal_number() - i / 100.0) < 0.00001);
    4464        6003 :                     CATCH_REQUIRE(decimal_number->get_string() == "");
    4465        6003 :                     csspp::position const & npos(decimal_number->get_position());
    4466        6003 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4467        6003 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4468        6003 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4469        6003 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4470        6003 :                 }
    4471             : 
    4472             :                 // whitespace
    4473             :                 {
    4474        6003 :                     csspp::node::pointer_t whitespace(l.next_token());
    4475        6003 :                     CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    4476        6003 :                     csspp::position const & npos(whitespace->get_position());
    4477        6003 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4478        6003 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4479        6003 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4480        6003 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4481        6003 :                 }
    4482             : 
    4483             :                 // "dimension" (as a separate identifier)
    4484             :                 {
    4485             :                     // a dimension is an integer or a decimal number
    4486             :                     // with a string expressing the dimension
    4487        6003 :                     csspp::node::pointer_t dimension(l.next_token());
    4488        6003 :                     CATCH_REQUIRE(dimension->is(csspp::node_type_t::IDENTIFIER));
    4489        6003 :                     CATCH_REQUIRE(dimension->get_string() == dimensions[j]);
    4490        6003 :                     csspp::position const & npos(dimension->get_position());
    4491        6003 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4492        6003 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4493        6003 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4494        6003 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4495        6003 :                 }
    4496        6003 :                 CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    4497        6003 :             }
    4498             :         }
    4499             :     }
    4500             : 
    4501             :     // decimal numbers that start with "." or "-."
    4502             :     {
    4503           1 :         char const *dimensions[] = {
    4504             :             "em",       // character em
    4505             :             "px",       // pixel
    4506             :             "pt"        // point
    4507             :         };
    4508         200 :         for(int i(-99); i <= 99; ++i)
    4509             :         {
    4510         796 :             for(size_t j(0); j < sizeof(dimensions) / sizeof(dimensions[0]); ++j)
    4511             :             {
    4512         597 :                 std::stringstream ss;
    4513         597 :                 ss << (i < 0 ? "-" : "") << "." << std::setw(2) << std::setfill('0') << abs(i) << dimensions[j]
    4514         597 :                    << "," << (i < 0 ? "-" : "") << "." << std::setw(2) << abs(i) << " " << dimensions[j]; // prove it does not work when we have a space
    4515        1791 :                 csspp::position pos("test.css");
    4516         597 :                 csspp::lexer l(ss, pos);
    4517             : 
    4518             :                 // sign
    4519             :                 //if(i < 0)
    4520             :                 //{
    4521             :                 //    csspp::node::pointer_t integer(l.next_token());
    4522             :                 //    CATCH_REQUIRE(integer->is(csspp::node_type_t::SUBTRACT));
    4523             :                 //    csspp::position const & npos(integer->get_position());
    4524             :                 //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    4525             :                 //    CATCH_REQUIRE(npos.get_page() == 1);
    4526             :                 //    CATCH_REQUIRE(npos.get_line() == 1);
    4527             :                 //    CATCH_REQUIRE(npos.get_total_line() == 1);
    4528             :                 //}
    4529             : 
    4530             :                 // dimension
    4531             :                 {
    4532             :                     // a dimension is an integer or a decimal number
    4533             :                     // with a string expressing the dimension
    4534         597 :                     csspp::node::pointer_t dimension(l.next_token());
    4535         597 :                     CATCH_REQUIRE(dimension->is(csspp::node_type_t::DECIMAL_NUMBER));
    4536         597 :                     CATCH_REQUIRE(fabs(dimension->get_decimal_number() - i / 100.0) < 0.00001);
    4537         597 :                     CATCH_REQUIRE(dimension->get_string() == dimensions[j]);
    4538         597 :                     csspp::position const & npos(dimension->get_position());
    4539         597 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4540         597 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4541         597 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4542         597 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4543         597 :                 }
    4544             : 
    4545             :                 // comma
    4546             :                 {
    4547         597 :                     csspp::node::pointer_t comma(l.next_token());
    4548         597 :                     CATCH_REQUIRE(comma->is(csspp::node_type_t::COMMA));
    4549         597 :                     csspp::position const & npos(comma->get_position());
    4550         597 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4551         597 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4552         597 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4553         597 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4554         597 :                 }
    4555             : 
    4556             :                 // sign
    4557             :                 //if(i < 0)
    4558             :                 //{
    4559             :                 //    csspp::node::pointer_t integer(l.next_token());
    4560             :                 //    CATCH_REQUIRE(integer->is(csspp::node_type_t::SUBTRACT));
    4561             :                 //    csspp::position const & npos(integer->get_position());
    4562             :                 //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    4563             :                 //    CATCH_REQUIRE(npos.get_page() == 1);
    4564             :                 //    CATCH_REQUIRE(npos.get_line() == 1);
    4565             :                 //    CATCH_REQUIRE(npos.get_total_line() == 1);
    4566             :                 //}
    4567             : 
    4568             :                 // decimal number (separated!)
    4569             :                 {
    4570         597 :                     csspp::node::pointer_t decimal_number(l.next_token());
    4571         597 :                     CATCH_REQUIRE(decimal_number->is(csspp::node_type_t::DECIMAL_NUMBER));
    4572         597 :                     CATCH_REQUIRE(fabs(decimal_number->get_decimal_number() - i / 100.0) < 0.00001);
    4573         597 :                     CATCH_REQUIRE(decimal_number->get_string() == "");
    4574         597 :                     csspp::position const & npos(decimal_number->get_position());
    4575         597 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4576         597 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4577         597 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4578         597 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4579         597 :                 }
    4580             : 
    4581             :                 // whitespace
    4582             :                 {
    4583         597 :                     csspp::node::pointer_t whitespace(l.next_token());
    4584         597 :                     CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    4585         597 :                     csspp::position const & npos(whitespace->get_position());
    4586         597 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4587         597 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4588         597 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4589         597 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4590         597 :                 }
    4591             : 
    4592             :                 // "dimension" (as a separate identifier)
    4593             :                 {
    4594             :                     // a dimension is an integer or a decimal number
    4595             :                     // with a string expressing the dimension
    4596         597 :                     csspp::node::pointer_t dimension(l.next_token());
    4597         597 :                     CATCH_REQUIRE(dimension->is(csspp::node_type_t::IDENTIFIER));
    4598         597 :                     CATCH_REQUIRE(dimension->get_string() == dimensions[j]);
    4599         597 :                     csspp::position const & npos(dimension->get_position());
    4600         597 :                     CATCH_REQUIRE(npos.get_filename() == "test.css");
    4601         597 :                     CATCH_REQUIRE(npos.get_page() == 1);
    4602         597 :                     CATCH_REQUIRE(npos.get_line() == 1);
    4603         597 :                     CATCH_REQUIRE(npos.get_total_line() == 1);
    4604         597 :                 }
    4605         597 :                 CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    4606         597 :             }
    4607             :         }
    4608             :     }
    4609             : 
    4610             :     // invalid escape character
    4611             :     {
    4612           1 :         std::stringstream ss;
    4613           1 :         ss << "1.25e\\\n";
    4614           3 :         csspp::position pos("test.css");
    4615           1 :         csspp::lexer l(ss, pos);
    4616             : 
    4617             :         // dimension
    4618             :         {
    4619             :             // a dimension is an integer or a decimal number
    4620             :             // with a string expressing the dimension
    4621           1 :             csspp::node::pointer_t dimension(l.next_token());
    4622           1 :             CATCH_REQUIRE(dimension->is(csspp::node_type_t::DECIMAL_NUMBER));
    4623           1 :             CATCH_REQUIRE(fabs(dimension->get_decimal_number() - 1.25) < 0.00001);
    4624           1 :             CATCH_REQUIRE(dimension->get_string() == "e");
    4625           1 :             csspp::position const & npos(dimension->get_position());
    4626           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4627           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4628           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4629           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4630           1 :         }
    4631             : 
    4632           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    4633             : 
    4634           1 :         VERIFY_ERRORS("test.css(1): error: spurious newline character after a \\ character outside of a string.\n");
    4635           1 :     }
    4636             : 
    4637             :     // no error left over
    4638           1 :     VERIFY_ERRORS("");
    4639           1 : }
    4640             : 
    4641           1 : CATCH_TEST_CASE("Percent", "[lexer] [number] [percent]")
    4642             : {
    4643             :     // percent with integers, converts to decimal number anyway
    4644             :     {
    4645       20002 :         for(int i(-10000); i <= 10000; ++i)
    4646             :         {
    4647       20001 :             std::stringstream ss;
    4648             :             ss << i << "%"
    4649       20001 :                << "," << i << "\\%"
    4650       20001 :                << "," << i << "\\25"
    4651       20001 :                << "," << i << " " << "%"; // and when spaced, it becomes MODULO
    4652       60003 :             csspp::position pos("test.css");
    4653       20001 :             csspp::lexer l(ss, pos);
    4654             : 
    4655             :             // sign
    4656             :             //if(i < 0)
    4657             :             //{
    4658             :             //    csspp::node::pointer_t subtract(l.next_token());
    4659             :             //    CATCH_REQUIRE(subtract->is(csspp::node_type_t::SUBTRACT));
    4660             :             //    csspp::position const & npos(subtract->get_position());
    4661             :             //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    4662             :             //    CATCH_REQUIRE(npos.get_page() == 1);
    4663             :             //    CATCH_REQUIRE(npos.get_line() == 1);
    4664             :             //    CATCH_REQUIRE(npos.get_total_line() == 1);
    4665             :             //}
    4666             : 
    4667             :             // percent
    4668             :             {
    4669       20001 :                 csspp::node::pointer_t percent(l.next_token());
    4670       20001 :                 CATCH_REQUIRE(percent->is(csspp::node_type_t::PERCENT));
    4671       20001 :                 CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(percent->get_decimal_number(), static_cast<csspp::decimal_number_t>(i) / 100.0, 0.0));
    4672       20001 :                 csspp::position const & npos(percent->get_position());
    4673       20001 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    4674       20001 :                 CATCH_REQUIRE(npos.get_page() == 1);
    4675       20001 :                 CATCH_REQUIRE(npos.get_line() == 1);
    4676       20001 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    4677       20001 :             }
    4678             : 
    4679             :             // comma
    4680             :             {
    4681       20001 :                 csspp::node::pointer_t comma(l.next_token());
    4682       20001 :                 CATCH_REQUIRE(comma->is(csspp::node_type_t::COMMA));
    4683       20001 :                 csspp::position const & npos(comma->get_position());
    4684       20001 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    4685       20001 :                 CATCH_REQUIRE(npos.get_page() == 1);
    4686       20001 :                 CATCH_REQUIRE(npos.get_line() == 1);
    4687       20001 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    4688       20001 :             }
    4689             : 
    4690             :             // sign
    4691             :             //if(i < 0)
    4692             :             //{
    4693             :             //    csspp::node::pointer_t subtract(l.next_token());
    4694             :             //    CATCH_REQUIRE(subtract->is(csspp::node_type_t::SUBTRACT));
    4695             :             //    csspp::position const & npos(subtract->get_position());
    4696             :             //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    4697             :             //    CATCH_REQUIRE(npos.get_page() == 1);
    4698             :             //    CATCH_REQUIRE(npos.get_line() == 1);
    4699             :             //    CATCH_REQUIRE(npos.get_total_line() == 1);
    4700             :             //}
    4701             : 
    4702             :             // dimension (because '%' written '\%' is not a PERCENT...)
    4703             :             {
    4704       20001 :                 csspp::node::pointer_t integer(l.next_token());
    4705       20001 :                 CATCH_REQUIRE(integer->is(csspp::node_type_t::INTEGER));
    4706       20001 :                 CATCH_REQUIRE(integer->get_integer() == i);
    4707       20001 :                 CATCH_REQUIRE(integer->get_string() == "%");
    4708       20001 :                 csspp::position const & npos(integer->get_position());
    4709       20001 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    4710       20001 :                 CATCH_REQUIRE(npos.get_page() == 1);
    4711       20001 :                 CATCH_REQUIRE(npos.get_line() == 1);
    4712       20001 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    4713       20001 :             }
    4714             : 
    4715             :             // comma
    4716             :             {
    4717       20001 :                 csspp::node::pointer_t comma(l.next_token());
    4718       20001 :                 CATCH_REQUIRE(comma->is(csspp::node_type_t::COMMA));
    4719       20001 :                 csspp::position const & npos(comma->get_position());
    4720       20001 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    4721       20001 :                 CATCH_REQUIRE(npos.get_page() == 1);
    4722       20001 :                 CATCH_REQUIRE(npos.get_line() == 1);
    4723       20001 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    4724       20001 :             }
    4725             : 
    4726             :             // sign
    4727             :             //if(i < 0)
    4728             :             //{
    4729             :             //    csspp::node::pointer_t subtract(l.next_token());
    4730             :             //    CATCH_REQUIRE(subtract->is(csspp::node_type_t::SUBTRACT));
    4731             :             //    csspp::position const & npos(subtract->get_position());
    4732             :             //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    4733             :             //    CATCH_REQUIRE(npos.get_page() == 1);
    4734             :             //    CATCH_REQUIRE(npos.get_line() == 1);
    4735             :             //    CATCH_REQUIRE(npos.get_total_line() == 1);
    4736             :             //}
    4737             : 
    4738             :             // dimension (again \25 is not a PERCENT)
    4739             :             {
    4740       20001 :                 csspp::node::pointer_t dimension(l.next_token());
    4741       20001 :                 CATCH_REQUIRE(dimension->is(csspp::node_type_t::INTEGER));
    4742       20001 :                 CATCH_REQUIRE(dimension->get_integer() == i);
    4743       20001 :                 CATCH_REQUIRE(dimension->get_string() == "%");
    4744       20001 :                 csspp::position const & npos(dimension->get_position());
    4745       20001 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    4746       20001 :                 CATCH_REQUIRE(npos.get_page() == 1);
    4747       20001 :                 CATCH_REQUIRE(npos.get_line() == 1);
    4748       20001 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    4749       20001 :             }
    4750             : 
    4751             :             // comma
    4752             :             {
    4753       20001 :                 csspp::node::pointer_t comma(l.next_token());
    4754       20001 :                 CATCH_REQUIRE(comma->is(csspp::node_type_t::COMMA));
    4755       20001 :                 csspp::position const & npos(comma->get_position());
    4756       20001 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    4757       20001 :                 CATCH_REQUIRE(npos.get_page() == 1);
    4758       20001 :                 CATCH_REQUIRE(npos.get_line() == 1);
    4759       20001 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    4760       20001 :             }
    4761             : 
    4762             :             // sign
    4763             :             //if(i < 0)
    4764             :             //{
    4765             :             //    csspp::node::pointer_t subtract(l.next_token());
    4766             :             //    CATCH_REQUIRE(subtract->is(csspp::node_type_t::SUBTRACT));
    4767             :             //    csspp::position const & npos(subtract->get_position());
    4768             :             //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    4769             :             //    CATCH_REQUIRE(npos.get_page() == 1);
    4770             :             //    CATCH_REQUIRE(npos.get_line() == 1);
    4771             :             //    CATCH_REQUIRE(npos.get_total_line() == 1);
    4772             :             //}
    4773             : 
    4774             :             // integer (separated!)
    4775             :             {
    4776       20001 :                 csspp::node::pointer_t integer(l.next_token());
    4777       20001 :                 CATCH_REQUIRE(integer->is(csspp::node_type_t::INTEGER));
    4778       20001 :                 CATCH_REQUIRE(integer->get_integer() == i);
    4779       20001 :                 CATCH_REQUIRE(integer->get_string() == "");
    4780       20001 :                 CATCH_REQUIRE(integer->get_string() == "");
    4781       20001 :                 csspp::position const & npos(integer->get_position());
    4782       20001 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    4783       20001 :                 CATCH_REQUIRE(npos.get_page() == 1);
    4784       20001 :                 CATCH_REQUIRE(npos.get_line() == 1);
    4785       20001 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    4786       20001 :             }
    4787             : 
    4788             :             // whitespace
    4789             :             {
    4790       20001 :                 csspp::node::pointer_t whitespace(l.next_token());
    4791       20001 :                 CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    4792       20001 :                 csspp::position const & npos(whitespace->get_position());
    4793       20001 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    4794       20001 :                 CATCH_REQUIRE(npos.get_page() == 1);
    4795       20001 :                 CATCH_REQUIRE(npos.get_line() == 1);
    4796       20001 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    4797       20001 :             }
    4798             : 
    4799             :             // "percent" by itself is MODULO
    4800             :             {
    4801       20001 :                 CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::MODULO));
    4802       20001 :                 CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    4803             : 
    4804       20001 :                 VERIFY_ERRORS("");
    4805             :             }
    4806       20001 :         }
    4807             :     }
    4808             : 
    4809             :     // percent directly with decimal numbers
    4810             :     {
    4811        2002 :         for(int i(-1000); i <= 1000; ++i)
    4812             :         {
    4813        2001 :             std::stringstream ss;
    4814        2001 :             ss << (i < 0 ? "-" : "") << abs(i) / 100 << "." << std::setw(2) << std::setfill('0') << abs(i % 100) << "%"
    4815        2001 :                << "," << (i < 0 ? "-" : "") << abs(i) / 100 << "." << std::setw(2) << abs(i % 100) << " " << "%"; // prove it does not work when we have a space
    4816        6003 :             csspp::position pos("test.css");
    4817        2001 :             csspp::lexer l(ss, pos);
    4818             : 
    4819             :             // sign
    4820             :             //if(i < 0)
    4821             :             //{
    4822             :             //    csspp::node::pointer_t subtract(l.next_token());
    4823             :             //    CATCH_REQUIRE(subtract->is(csspp::node_type_t::SUBTRACT));
    4824             :             //    csspp::position const & npos(subtract->get_position());
    4825             :             //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    4826             :             //    CATCH_REQUIRE(npos.get_page() == 1);
    4827             :             //    CATCH_REQUIRE(npos.get_line() == 1);
    4828             :             //    CATCH_REQUIRE(npos.get_total_line() == 1);
    4829             :             //}
    4830             : 
    4831             :             // percent
    4832             :             {
    4833        2001 :                 csspp::node::pointer_t percent(l.next_token());
    4834        2001 :                 CATCH_REQUIRE(percent->is(csspp::node_type_t::PERCENT));
    4835        2001 :                 CATCH_REQUIRE(fabs(percent->get_decimal_number() - i / 10000.0) < 0.00001);
    4836        2001 :                 csspp::position const & npos(percent->get_position());
    4837        2001 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    4838        2001 :                 CATCH_REQUIRE(npos.get_page() == 1);
    4839        2001 :                 CATCH_REQUIRE(npos.get_line() == 1);
    4840        2001 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    4841        2001 :             }
    4842             : 
    4843             :             // comma
    4844             :             {
    4845        2001 :                 csspp::node::pointer_t comma(l.next_token());
    4846        2001 :                 CATCH_REQUIRE(comma->is(csspp::node_type_t::COMMA));
    4847        2001 :                 csspp::position const & npos(comma->get_position());
    4848        2001 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    4849        2001 :                 CATCH_REQUIRE(npos.get_page() == 1);
    4850        2001 :                 CATCH_REQUIRE(npos.get_line() == 1);
    4851        2001 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    4852        2001 :             }
    4853             : 
    4854             :             // sign
    4855             :             //if(i < 0)
    4856             :             //{
    4857             :             //    csspp::node::pointer_t subtract(l.next_token());
    4858             :             //    CATCH_REQUIRE(subtract->is(csspp::node_type_t::SUBTRACT));
    4859             :             //    csspp::position const & npos(subtract->get_position());
    4860             :             //    CATCH_REQUIRE(npos.get_filename() == "test.css");
    4861             :             //    CATCH_REQUIRE(npos.get_page() == 1);
    4862             :             //    CATCH_REQUIRE(npos.get_line() == 1);
    4863             :             //    CATCH_REQUIRE(npos.get_total_line() == 1);
    4864             :             //}
    4865             : 
    4866             :             // decimal number (separated!)
    4867             :             {
    4868        2001 :                 csspp::node::pointer_t decimal_number(l.next_token());
    4869        2001 :                 CATCH_REQUIRE(decimal_number->is(csspp::node_type_t::DECIMAL_NUMBER));
    4870        2001 :                 CATCH_REQUIRE(fabs(decimal_number->get_decimal_number() - i / 100.0) < 0.00001);
    4871        2001 :                 CATCH_REQUIRE(decimal_number->get_string() == "");
    4872        2001 :                 csspp::position const & npos(decimal_number->get_position());
    4873        2001 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    4874        2001 :                 CATCH_REQUIRE(npos.get_page() == 1);
    4875        2001 :                 CATCH_REQUIRE(npos.get_line() == 1);
    4876        2001 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    4877        2001 :             }
    4878             : 
    4879             :             // whitespace
    4880             :             {
    4881        2001 :                 csspp::node::pointer_t whitespace(l.next_token());
    4882        2001 :                 CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    4883        2001 :                 csspp::position const & npos(whitespace->get_position());
    4884        2001 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    4885        2001 :                 CATCH_REQUIRE(npos.get_page() == 1);
    4886        2001 :                 CATCH_REQUIRE(npos.get_line() == 1);
    4887        2001 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    4888        2001 :             }
    4889             : 
    4890             :             // "percent" by itself is MODULO
    4891             :             {
    4892        2001 :                 CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::MODULO));
    4893        2001 :                 CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    4894             : 
    4895        2001 :                 VERIFY_ERRORS("");
    4896             :             }
    4897        2001 :         }
    4898             :     }
    4899             : 
    4900             :     // no error left over
    4901           1 :     VERIFY_ERRORS("");
    4902           1 : }
    4903             : 
    4904           1 : CATCH_TEST_CASE("Unicode range", "[lexer] [unicode]")
    4905             : {
    4906             :     // a small test to make sure we get U or u as identifiers when
    4907             :     // the + is not followed by the right character
    4908             :     {
    4909           1 :         std::stringstream ss;
    4910           1 :         ss << "U+U or u+u";
    4911           3 :         csspp::position pos("test.css");
    4912           1 :         csspp::lexer l(ss, pos);
    4913             : 
    4914             :         // identifier
    4915             :         {
    4916           1 :             csspp::node::pointer_t identifier(l.next_token());
    4917           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    4918           1 :             CATCH_REQUIRE(identifier->get_string() == "U");
    4919           1 :             csspp::position const & npos(identifier->get_position());
    4920           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4921           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4922           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4923           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4924           1 :         }
    4925             : 
    4926             :         // add
    4927             :         {
    4928           1 :             csspp::node::pointer_t add(l.next_token());
    4929           1 :             CATCH_REQUIRE(add->is(csspp::node_type_t::ADD));
    4930           1 :             csspp::position const & npos(add->get_position());
    4931           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4932           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4933           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4934           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4935           1 :         }
    4936             : 
    4937             :         // identifier
    4938             :         {
    4939           1 :             csspp::node::pointer_t identifier(l.next_token());
    4940           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    4941           1 :             CATCH_REQUIRE(identifier->get_string() == "U");
    4942           1 :             csspp::position const & npos(identifier->get_position());
    4943           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4944           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4945           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4946           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4947           1 :         }
    4948             : 
    4949             :         // whitespace
    4950             :         {
    4951           1 :             csspp::node::pointer_t whitespace(l.next_token());
    4952           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    4953           1 :             csspp::position const & npos(whitespace->get_position());
    4954           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4955           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4956           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4957           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4958           1 :         }
    4959             : 
    4960             :         // identifier
    4961             :         {
    4962           1 :             csspp::node::pointer_t identifier(l.next_token());
    4963           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    4964           1 :             CATCH_REQUIRE(identifier->get_string() == "or");
    4965           1 :             csspp::position const & npos(identifier->get_position());
    4966           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4967           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4968           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4969           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4970           1 :         }
    4971             : 
    4972             :         // whitespace
    4973             :         {
    4974           1 :             csspp::node::pointer_t whitespace(l.next_token());
    4975           1 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::WHITESPACE));
    4976           1 :             csspp::position const & npos(whitespace->get_position());
    4977           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4978           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4979           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4980           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4981           1 :         }
    4982             : 
    4983             :         // identifier
    4984             :         {
    4985           1 :             csspp::node::pointer_t identifier(l.next_token());
    4986           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    4987           1 :             CATCH_REQUIRE(identifier->get_string() == "u");
    4988           1 :             csspp::position const & npos(identifier->get_position());
    4989           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    4990           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    4991           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    4992           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    4993           1 :         }
    4994             : 
    4995             :         // add
    4996             :         {
    4997           1 :             csspp::node::pointer_t add(l.next_token());
    4998           1 :             CATCH_REQUIRE(add->is(csspp::node_type_t::ADD));
    4999           1 :             csspp::position const & npos(add->get_position());
    5000           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5001           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5002           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5003           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5004           1 :         }
    5005             : 
    5006             :         // identifier
    5007             :         {
    5008           1 :             csspp::node::pointer_t identifier(l.next_token());
    5009           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    5010           1 :             CATCH_REQUIRE(identifier->get_string() == "u");
    5011           1 :             csspp::position const & npos(identifier->get_position());
    5012           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5013           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5014           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5015           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5016           1 :         }
    5017             : 
    5018           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5019           1 :     }
    5020             : 
    5021             :     // one value (U+<value>)
    5022             :     // two values with the same number (U+<value>-<value>)
    5023             :     // two values with the same number, but force 6 digits each (U+<value>-<value>)
    5024             :     // check the first 64Kb (plan 0) and then randomize
    5025             :     {
    5026       65537 :         for(csspp::wide_char_t unicode(0); unicode < 65536; ++unicode)
    5027             :         {
    5028       65536 :             std::stringstream ss;
    5029       65536 :             ss << (rand() & 1 ? "U" : "u") << "+" << std::hex << unicode
    5030       65536 :                << "," << (rand() & 1 ? "U" : "u") << "+" << std::hex << unicode << "-" << unicode
    5031       65536 :                << "," << (rand() & 1 ? "U" : "u") << "+" << std::hex << std::setw(6) << std::setfill('0') << unicode << "-" << std::setw(6) << unicode << std::setfill('\0');
    5032      196608 :             csspp::position pos("test.css");
    5033       65536 :             csspp::lexer l(ss, pos);
    5034             : 
    5035       65536 :             csspp_test::our_unicode_range_t range(unicode, unicode);
    5036             : 
    5037             :             // unicode range
    5038             :             {
    5039       65536 :                 csspp::node::pointer_t unicode_range(l.next_token());
    5040       65536 :                 CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5041       65536 :                 CATCH_REQUIRE(unicode_range->get_integer() == static_cast<csspp::integer_t>(range.get_range()));
    5042       65536 :                 csspp::position const & npos(unicode_range->get_position());
    5043       65536 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    5044       65536 :                 CATCH_REQUIRE(npos.get_page() == 1);
    5045       65536 :                 CATCH_REQUIRE(npos.get_line() == 1);
    5046       65536 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    5047       65536 :             }
    5048             : 
    5049             :             // comma
    5050             :             {
    5051       65536 :                 csspp::node::pointer_t comma(l.next_token());
    5052       65536 :                 CATCH_REQUIRE(comma->is(csspp::node_type_t::COMMA));
    5053       65536 :                 csspp::position const & npos(comma->get_position());
    5054       65536 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    5055       65536 :                 CATCH_REQUIRE(npos.get_page() == 1);
    5056       65536 :                 CATCH_REQUIRE(npos.get_line() == 1);
    5057       65536 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    5058       65536 :             }
    5059             : 
    5060             :             // unicode range
    5061             :             {
    5062       65536 :                 csspp::node::pointer_t unicode_range(l.next_token());
    5063       65536 :                 CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5064       65536 :                 CATCH_REQUIRE(unicode_range->get_integer() == static_cast<csspp::integer_t>(range.get_range()));
    5065       65536 :                 csspp::position const & npos(unicode_range->get_position());
    5066       65536 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    5067       65536 :                 CATCH_REQUIRE(npos.get_page() == 1);
    5068       65536 :                 CATCH_REQUIRE(npos.get_line() == 1);
    5069       65536 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    5070       65536 :             }
    5071             : 
    5072             :             // comma
    5073             :             {
    5074       65536 :                 csspp::node::pointer_t comma(l.next_token());
    5075       65536 :                 CATCH_REQUIRE(comma->is(csspp::node_type_t::COMMA));
    5076       65536 :                 csspp::position const & npos(comma->get_position());
    5077       65536 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    5078       65536 :                 CATCH_REQUIRE(npos.get_page() == 1);
    5079       65536 :                 CATCH_REQUIRE(npos.get_line() == 1);
    5080       65536 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    5081       65536 :             }
    5082             : 
    5083             :             // unicode range
    5084             :             {
    5085       65536 :                 csspp::node::pointer_t unicode_range(l.next_token());
    5086       65536 :                 CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5087       65536 :                 CATCH_REQUIRE(unicode_range->get_integer() == static_cast<csspp::integer_t>(range.get_range()));
    5088       65536 :                 csspp::position const & npos(unicode_range->get_position());
    5089       65536 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    5090       65536 :                 CATCH_REQUIRE(npos.get_page() == 1);
    5091       65536 :                 CATCH_REQUIRE(npos.get_line() == 1);
    5092       65536 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    5093       65536 :             }
    5094             : 
    5095       65536 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5096       65536 :         }
    5097             : 
    5098        1001 :         for(int i(0); i < 1000; ++i)
    5099             :         {
    5100        1000 :             csspp::wide_char_t unicode(rand() % 0x110000);
    5101        1000 :             std::stringstream ss;
    5102        1000 :             ss << (rand() & 1 ? "U" : "u") << "+" << std::hex << unicode
    5103        1000 :                << "," << (rand() & 1 ? "U" : "u") << "+" << std::hex << unicode << "-" << unicode
    5104        1000 :                << "," << (rand() & 1 ? "U" : "u") << "+" << std::hex << std::setw(6) << std::setfill('0') << unicode << "-" << std::setw(6) << unicode << std::setfill('\0');
    5105        3000 :             csspp::position pos("test.css");
    5106        1000 :             csspp::lexer l(ss, pos);
    5107             : 
    5108        1000 :             csspp_test::our_unicode_range_t range(unicode, unicode);
    5109             : 
    5110             :             // unicode range
    5111             :             {
    5112        1000 :                 csspp::node::pointer_t unicode_range(l.next_token());
    5113        1000 :                 CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5114        1000 :                 CATCH_REQUIRE(unicode_range->get_integer() == static_cast<csspp::integer_t>(range.get_range()));
    5115        1000 :                 csspp::position const & npos(unicode_range->get_position());
    5116        1000 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    5117        1000 :                 CATCH_REQUIRE(npos.get_page() == 1);
    5118        1000 :                 CATCH_REQUIRE(npos.get_line() == 1);
    5119        1000 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    5120        1000 :             }
    5121             : 
    5122             :             // comma
    5123             :             {
    5124        1000 :                 csspp::node::pointer_t comma(l.next_token());
    5125        1000 :                 CATCH_REQUIRE(comma->is(csspp::node_type_t::COMMA));
    5126        1000 :                 csspp::position const & npos(comma->get_position());
    5127        1000 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    5128        1000 :                 CATCH_REQUIRE(npos.get_page() == 1);
    5129        1000 :                 CATCH_REQUIRE(npos.get_line() == 1);
    5130        1000 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    5131        1000 :             }
    5132             : 
    5133             :             // unicode range
    5134             :             {
    5135        1000 :                 csspp::node::pointer_t unicode_range(l.next_token());
    5136        1000 :                 CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5137        1000 :                 CATCH_REQUIRE(unicode_range->get_integer() == static_cast<csspp::integer_t>(range.get_range()));
    5138        1000 :                 csspp::position const & npos(unicode_range->get_position());
    5139        1000 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    5140        1000 :                 CATCH_REQUIRE(npos.get_page() == 1);
    5141        1000 :                 CATCH_REQUIRE(npos.get_line() == 1);
    5142        1000 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    5143        1000 :             }
    5144             : 
    5145             :             // comma
    5146             :             {
    5147        1000 :                 csspp::node::pointer_t comma(l.next_token());
    5148        1000 :                 CATCH_REQUIRE(comma->is(csspp::node_type_t::COMMA));
    5149        1000 :                 csspp::position const & npos(comma->get_position());
    5150        1000 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    5151        1000 :                 CATCH_REQUIRE(npos.get_page() == 1);
    5152        1000 :                 CATCH_REQUIRE(npos.get_line() == 1);
    5153        1000 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    5154        1000 :             }
    5155             : 
    5156             :             // unicode range
    5157             :             {
    5158        1000 :                 csspp::node::pointer_t unicode_range(l.next_token());
    5159        1000 :                 CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5160        1000 :                 CATCH_REQUIRE(unicode_range->get_integer() == static_cast<csspp::integer_t>(range.get_range()));
    5161        1000 :                 csspp::position const & npos(unicode_range->get_position());
    5162        1000 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    5163        1000 :                 CATCH_REQUIRE(npos.get_page() == 1);
    5164        1000 :                 CATCH_REQUIRE(npos.get_line() == 1);
    5165        1000 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    5166        1000 :             }
    5167             : 
    5168        1000 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5169        1000 :         }
    5170             :     }
    5171             : 
    5172             :     // test that we recover the identifier right after a Unicode Range
    5173             :     {
    5174           1 :         std::stringstream ss;
    5175           1 :         csspp::wide_char_t unicode(rand() % 0x110000);
    5176           1 :         ss << (rand() & 1 ? "U" : "u") << "+" << std::hex << std::setw(6) << std::setfill('0') << unicode << "Alexis";
    5177           3 :         csspp::position pos("test.css");
    5178           1 :         csspp::lexer l(ss, pos);
    5179             : 
    5180           1 :         csspp_test::our_unicode_range_t range(unicode, unicode);
    5181             : 
    5182             :         // unicode range
    5183             :         {
    5184           1 :             csspp::node::pointer_t unicode_range(l.next_token());
    5185           1 :             CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5186           1 :             CATCH_REQUIRE(unicode_range->get_integer() == static_cast<csspp::integer_t>(range.get_range()));
    5187           1 :             csspp::position const & npos(unicode_range->get_position());
    5188           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5189           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5190           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5191           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5192           1 :         }
    5193             : 
    5194             :         // identifier
    5195             :         {
    5196           1 :             csspp::node::pointer_t identifier(l.next_token());
    5197           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    5198           1 :             CATCH_REQUIRE(identifier->get_string() == "Alexis");
    5199           1 :             csspp::position const & npos(identifier->get_position());
    5200           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5201           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5202           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5203           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5204           1 :         }
    5205             : 
    5206           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5207           1 :     }
    5208             : 
    5209             :     // test that we recover the number right after a Unicode Range
    5210             :     {
    5211           1 :         std::stringstream ss;
    5212           1 :         csspp::wide_char_t unicode(rand() % 0x110000);
    5213           1 :         ss << (rand() & 1 ? "U" : "u") << "+" << std::hex << std::setw(6) << std::setfill('0') << unicode << "123";
    5214           3 :         csspp::position pos("test.css");
    5215           1 :         csspp::lexer l(ss, pos);
    5216             : 
    5217           1 :         csspp_test::our_unicode_range_t range(unicode, unicode);
    5218             : 
    5219             :         // unicode range
    5220             :         {
    5221           1 :             csspp::node::pointer_t unicode_range(l.next_token());
    5222           1 :             CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5223           1 :             CATCH_REQUIRE(unicode_range->get_integer() == static_cast<csspp::integer_t>(range.get_range()));
    5224           1 :             csspp::position const & npos(unicode_range->get_position());
    5225           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5226           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5227           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5228           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5229           1 :         }
    5230             : 
    5231             :         // integer
    5232             :         {
    5233           1 :             csspp::node::pointer_t integer(l.next_token());
    5234           1 :             CATCH_REQUIRE(integer->is(csspp::node_type_t::INTEGER));
    5235           1 :             CATCH_REQUIRE(integer->get_integer() == 123);
    5236           1 :             csspp::position const & npos(integer->get_position());
    5237           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5238           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5239           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5240           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5241           1 :         }
    5242             : 
    5243           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5244           1 :     }
    5245             : 
    5246             :     // try various masks
    5247             :     {
    5248             :         // mask of 6 needs to be tested once
    5249           1 :         std::stringstream ss;
    5250           1 :         ss << (rand() & 1 ? "U" : "u") << "+??????";
    5251           3 :         csspp::position pos("test.css");
    5252           1 :         csspp::lexer l(ss, pos);
    5253             : 
    5254           1 :         csspp_test::our_unicode_range_t range(0, 0x1FFFFF);
    5255             : 
    5256             :         // unicode range
    5257             :         {
    5258           1 :             csspp::node::pointer_t unicode_range(l.next_token());
    5259           1 :             CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5260           1 :             CATCH_REQUIRE(unicode_range->get_integer() == static_cast<csspp::integer_t>(range.get_range()));
    5261           1 :             csspp::position const & npos(unicode_range->get_position());
    5262           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5263           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5264           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5265           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5266           1 :         }
    5267             : 
    5268           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5269           1 :     }
    5270           4 :     for(int i(0); i < 3; i++)
    5271             :     {
    5272             :         // mask of 5 can be checked three times: ?????, 0?????, 1?????
    5273           3 :         int const mask_count(5);
    5274           3 :         int const mask((1 << (mask_count * 4)) - 1);
    5275           3 :         std::stringstream ss;
    5276           3 :         csspp::wide_char_t unicode(i == 2 ? 0x100000 : 0);
    5277           3 :         ss << std::hex << std::setw(6) << std::setfill('0') << unicode;
    5278           3 :         std::string unicode_str(ss.str());
    5279           3 :         ss.str("");
    5280          18 :         for(size_t p(unicode_str.length() - mask_count); p < unicode_str.length(); ++p)
    5281             :         {
    5282             :             // replace by the mask (i.e. '?')
    5283          15 :             unicode_str[p] = '?';
    5284             :         }
    5285           3 :         if(i == 0)
    5286             :         {
    5287             :             // remove leading zeroes
    5288           2 :             while(unicode_str.front() == '0')
    5289             :             {
    5290           1 :                 unicode_str.erase(unicode_str.begin());
    5291             :             }
    5292             :         }
    5293           3 :         ss << (rand() & 1 ? "U" : "u") << "+" << unicode_str;
    5294           9 :         csspp::position pos("test.css");
    5295           3 :         csspp::lexer l(ss, pos);
    5296             : 
    5297           3 :         csspp_test::our_unicode_range_t range(unicode, unicode | mask);
    5298             : 
    5299             :         // unicode range
    5300             :         {
    5301           3 :             csspp::node::pointer_t unicode_range(l.next_token());
    5302           3 :             CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5303           3 :             CATCH_REQUIRE(unicode_range->get_integer() == static_cast<csspp::integer_t>(range.get_range()));
    5304           3 :             csspp::position const & npos(unicode_range->get_position());
    5305           3 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5306           3 :             CATCH_REQUIRE(npos.get_page() == 1);
    5307           3 :             CATCH_REQUIRE(npos.get_line() == 1);
    5308           3 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5309           3 :         }
    5310             : 
    5311           3 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5312           3 :     }
    5313          18 :     for(int i(0); i < 0x11; ++i)
    5314             :     {
    5315             :         // mask of 4 needs to be tested from 0x00 to 0x10
    5316             :         // (we could also check with the leading zero and without
    5317             :         // for each value, but that's not too important)
    5318          17 :         int const mask_count(4);
    5319          17 :         int const mask((1 << (mask_count * 4)) - 1);
    5320          17 :         std::stringstream ss;
    5321          17 :         csspp::wide_char_t unicode(i << 16); //(rand() % 0x110000) & ~mask);
    5322          17 :         ss << std::hex << std::setw(6) << std::setfill('0') << unicode;
    5323          17 :         std::string unicode_str(ss.str());
    5324          17 :         ss.str("");
    5325          85 :         for(size_t p(unicode_str.length() - mask_count); p < unicode_str.length(); ++p)
    5326             :         {
    5327             :             // replace by the mask (i.e. '?')
    5328          68 :             unicode_str[p] = '?';
    5329             :         }
    5330          17 :         if(rand() % 3 != 0)
    5331             :         {
    5332             :             // remove leading zeroes
    5333          24 :             while(unicode_str.front() == '0')
    5334             :             {
    5335          12 :                 unicode_str.erase(unicode_str.begin());
    5336             :             }
    5337             :         }
    5338          17 :         ss << (rand() & 1 ? "U" : "u") << "+" << unicode_str;
    5339          51 :         csspp::position pos("test.css");
    5340          17 :         csspp::lexer l(ss, pos);
    5341             : 
    5342          17 :         csspp_test::our_unicode_range_t range(unicode, unicode | mask);
    5343             : 
    5344             :         // unicode range
    5345             :         {
    5346          17 :             csspp::node::pointer_t unicode_range(l.next_token());
    5347          17 :             CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5348          17 :             CATCH_REQUIRE(unicode_range->get_integer() == static_cast<csspp::integer_t>(range.get_range()));
    5349          17 :             csspp::position const & npos(unicode_range->get_position());
    5350          17 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5351          17 :             CATCH_REQUIRE(npos.get_page() == 1);
    5352          17 :             CATCH_REQUIRE(npos.get_line() == 1);
    5353          17 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5354          17 :         }
    5355             : 
    5356          17 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5357          17 :     }
    5358        1001 :     for(int i(0); i < 1000; ++i) //1433656549
    5359             :     {
    5360             :         // a few random mask of 1 to 3 '?'
    5361        1000 :         int const mask_count(rand() % 3 + 1);
    5362        1000 :         int const mask((1 << (mask_count * 4)) - 1);
    5363        1000 :         std::stringstream ss;
    5364        1000 :         csspp::wide_char_t unicode((rand() % 0x110000) & ~mask);
    5365        1000 :         ss << std::hex << std::setw(6) << std::setfill('0') << unicode;
    5366        1000 :         std::string unicode_str(ss.str());
    5367        1000 :         ss.str("");
    5368        3002 :         for(size_t p(unicode_str.length() - mask_count); p < unicode_str.length(); ++p)
    5369             :         {
    5370             :             // replace by the mask (i.e. '?')
    5371        2002 :             unicode_str[p] = '?';
    5372             :         }
    5373        1000 :         if(rand() % 3 != 0)
    5374             :         {
    5375             :             // remove leading zeroes
    5376        1388 :             while(unicode_str.front() == '0')
    5377             :             {
    5378         700 :                 unicode_str.erase(unicode_str.begin());
    5379             :             }
    5380             :         }
    5381        1000 :         ss << (rand() & 1 ? "U" : "u") << "+" << unicode_str;
    5382        3000 :         csspp::position pos("test.css");
    5383        1000 :         csspp::lexer l(ss, pos);
    5384             : 
    5385        1000 :         csspp_test::our_unicode_range_t range(unicode, unicode | mask);
    5386             : 
    5387             :         // unicode range
    5388             :         {
    5389        1000 :             csspp::node::pointer_t unicode_range(l.next_token());
    5390        1000 :             CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5391        1000 :             CATCH_REQUIRE(unicode_range->get_integer() == static_cast<csspp::integer_t>(range.get_range()));
    5392        1000 :             csspp::position const & npos(unicode_range->get_position());
    5393        1000 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5394        1000 :             CATCH_REQUIRE(npos.get_page() == 1);
    5395        1000 :             CATCH_REQUIRE(npos.get_line() == 1);
    5396        1000 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5397        1000 :         }
    5398             : 
    5399        1000 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5400        1000 :     }
    5401             : 
    5402             :     // test simple range (start only) with values that are too large
    5403        1001 :     for(int i(0); i < 1000; i++)
    5404             :     {
    5405             :         // check a start unicode value too large
    5406             :         {
    5407        1000 :             std::stringstream ss;
    5408             :             // an invalid value which is exactly 6 hexadecimal digits
    5409        1000 :             csspp::wide_char_t unicode(rand() % (0x1000000 - 0x110000) + 0x110000);
    5410        1000 :             ss << (rand() & 1 ? "U" : "u") << "+" << std::hex << unicode;
    5411        3000 :             csspp::position pos("test.css");
    5412        1000 :             csspp::lexer l(ss, pos);
    5413             : 
    5414             :             // unicode range
    5415             :             {
    5416        1000 :                 csspp::node::pointer_t unicode_range(l.next_token());
    5417        1000 :                 CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5418             :                 //CATCH_REQUIRE(unicode_range->get_integer() == range.f_range); -- there was an overflow
    5419        1000 :                 csspp::position const & npos(unicode_range->get_position());
    5420        1000 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    5421        1000 :                 CATCH_REQUIRE(npos.get_page() == 1);
    5422        1000 :                 CATCH_REQUIRE(npos.get_line() == 1);
    5423        1000 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    5424             : 
    5425        1000 :                 VERIFY_ERRORS("test.css(1): error: unicode character too large, range is U+000000 to U+10FFFF.\n");
    5426        1000 :             }
    5427             : 
    5428        1000 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5429        1000 :         }
    5430             : 
    5431             :         // check with the second unicode too large
    5432             :         {
    5433        1000 :             std::stringstream ss;
    5434             :             // an invalid value which is exactly 6 hexadecimal digits
    5435        1000 :             csspp::wide_char_t unicode_start(rand() % 0x110000);
    5436        1000 :             csspp::wide_char_t unicode_end(rand() % (0x1000000 - 0x110000) + 0x110000);
    5437        1000 :             ss << (rand() & 1 ? "U" : "u") << "+" << std::hex << unicode_start << "-" << unicode_end;
    5438        3000 :             csspp::position pos("test.css");
    5439        1000 :             csspp::lexer l(ss, pos);
    5440             : 
    5441             :             // unicode range
    5442             :             {
    5443        1000 :                 csspp::node::pointer_t unicode_range(l.next_token());
    5444        1000 :                 CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5445             :                 //CATCH_REQUIRE(unicode_range->get_integer() == range.f_range); -- there was an overflow
    5446        1000 :                 csspp::position const & npos(unicode_range->get_position());
    5447        1000 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    5448        1000 :                 CATCH_REQUIRE(npos.get_page() == 1);
    5449        1000 :                 CATCH_REQUIRE(npos.get_line() == 1);
    5450        1000 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    5451             : 
    5452        1000 :                 VERIFY_ERRORS("test.css(1): error: unicode character too large, range is U+000000 to U+10FFFF.\n");
    5453        1000 :             }
    5454             : 
    5455        1000 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5456        1000 :         }
    5457             : 
    5458             :         // check with a mask which is too large
    5459             :         {
    5460             :             // WARNING: this test works with a mask of 1 to 4 x '?'
    5461             :             //          with 5, you would have to make sure that the first
    5462             :             //          Unicode digit it 2 or more
    5463             :             //          with 6, it never fails (and it is tested earlier)
    5464             :             //
    5465        1000 :             int const mask_count(rand() % 3 + 1);
    5466        1000 :             int const mask((1 << (mask_count * 4)) - 1);
    5467        1000 :             std::stringstream ss;
    5468             :             // an invalid value which is exactly 6 hexadecimal digits
    5469        1000 :             csspp::wide_char_t const unicode((rand() % (0x1000000 - 0x110000) + 0x110000) & ~mask);
    5470        1000 :             ss << std::hex << unicode;
    5471        1000 :             std::string unicode_str(ss.str());
    5472        1000 :             ss.str("");
    5473        3018 :             for(size_t p(unicode_str.length() - mask_count); p < unicode_str.length(); ++p)
    5474             :             {
    5475             :                 // replace by the mask (i.e. '?')
    5476        2018 :                 unicode_str[p] = '?';
    5477             :             }
    5478        1000 :             ss << (rand() & 1 ? "U" : "u") << "+" << unicode_str;
    5479        3000 :             csspp::position pos("test.css");
    5480        1000 :             csspp::lexer l(ss, pos);
    5481             : 
    5482             :             // unicode range
    5483             :             {
    5484        1000 :                 csspp::node::pointer_t unicode_range(l.next_token());
    5485        1000 :                 CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5486             :                 //CATCH_REQUIRE(unicode_range->get_integer() == range.f_range); -- there was an overflow, what could we check?
    5487        1000 :                 csspp::position const & npos(unicode_range->get_position());
    5488        1000 :                 CATCH_REQUIRE(npos.get_filename() == "test.css");
    5489        1000 :                 CATCH_REQUIRE(npos.get_page() == 1);
    5490        1000 :                 CATCH_REQUIRE(npos.get_line() == 1);
    5491        1000 :                 CATCH_REQUIRE(npos.get_total_line() == 1);
    5492             : 
    5493        1000 :                 VERIFY_ERRORS("test.css(1): error: unicode character too large, range is U+000000 to U+10FFFF.\n");
    5494        1000 :             }
    5495             : 
    5496        1000 :             CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5497        1000 :         }
    5498             :     }
    5499             : 
    5500             :     // actual range: in order and not in order
    5501        1001 :     for(int i(0); i < 1000; i++)
    5502             :     {
    5503        1000 :         std::stringstream ss;
    5504             :         // an invalid value which is exactly 6 hexadecimal digits
    5505        1000 :         csspp::wide_char_t unicode_start(rand() % 0x110000);
    5506        1000 :         csspp::wide_char_t unicode_end(rand() % 0x110000);
    5507             :         // avoid equality (already tested!)
    5508        1000 :         while(unicode_end == unicode_start)
    5509             :         {
    5510           0 :             unicode_end = rand() % 0x110000;
    5511             :         }
    5512             :         // make sure start is smaller
    5513        1000 :         if(unicode_start > unicode_end)
    5514             :         {
    5515         503 :             std::swap(unicode_start, unicode_end);
    5516             :         }
    5517             :         // test both: valid and invalid ranges
    5518        1000 :         ss << (rand() & 1 ? "U" : "u") << "+" << std::hex << unicode_start << "-" << unicode_end
    5519        1000 :            << "," << (rand() & 1 ? "U" : "u") << "+" << unicode_end << "-" << unicode_start;
    5520        3000 :         csspp::position pos("test.css");
    5521        1000 :         csspp::lexer l(ss, pos);
    5522             : 
    5523        1000 :         csspp_test::our_unicode_range_t range(unicode_start, unicode_end);
    5524             : 
    5525             :         // unicode range
    5526             :         {
    5527        1000 :             csspp::node::pointer_t unicode_range(l.next_token());
    5528        1000 :             CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5529        1000 :             CATCH_REQUIRE(unicode_range->get_integer() == static_cast<csspp::integer_t>(range.get_range()));
    5530        1000 :             csspp::position const & npos(unicode_range->get_position());
    5531        1000 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5532        1000 :             CATCH_REQUIRE(npos.get_page() == 1);
    5533        1000 :             CATCH_REQUIRE(npos.get_line() == 1);
    5534        1000 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5535        1000 :         }
    5536             : 
    5537             :         // comma
    5538             :         {
    5539        1000 :             csspp::node::pointer_t whitespace(l.next_token());
    5540        1000 :             CATCH_REQUIRE(whitespace->is(csspp::node_type_t::COMMA));
    5541        1000 :             csspp::position const & npos(whitespace->get_position());
    5542        1000 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5543        1000 :             CATCH_REQUIRE(npos.get_page() == 1);
    5544        1000 :             CATCH_REQUIRE(npos.get_line() == 1);
    5545        1000 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5546        1000 :         }
    5547             : 
    5548             :         // unicode range
    5549             :         {
    5550        1000 :             csspp::node::pointer_t unicode_range(l.next_token());
    5551        1000 :             CATCH_REQUIRE(unicode_range->is(csspp::node_type_t::UNICODE_RANGE));
    5552             :             //CATCH_REQUIRE(unicode_range->get_integer() == range.f_range); -- we get an error, we know what the range is, but we do not want to assume so in the test
    5553        1000 :             csspp::position const & npos(unicode_range->get_position());
    5554        1000 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5555        1000 :             CATCH_REQUIRE(npos.get_page() == 1);
    5556        1000 :             CATCH_REQUIRE(npos.get_line() == 1);
    5557        1000 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5558             : 
    5559        1000 :             VERIFY_ERRORS("test.css(1): error: unicode range cannot have a start character larger than the end character.\n");
    5560        1000 :         }
    5561             : 
    5562        1000 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5563        1000 :     }
    5564             : 
    5565             :     // no error left over
    5566           1 :     VERIFY_ERRORS("");
    5567           1 : }
    5568             : 
    5569           1 : CATCH_TEST_CASE("Hash", "[lexer] [hash]")
    5570             : {
    5571             :     // test a standard hash
    5572             :     {
    5573           1 :         std::stringstream ss;
    5574           3 :         csspp::position pos("test.css");
    5575           1 :         csspp::lexer l(ss, pos);
    5576           1 :         ss << "#-escape\\=33-";
    5577             : 
    5578             :         // hash
    5579             :         {
    5580           1 :             csspp::node::pointer_t identifier(l.next_token());
    5581           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::HASH));
    5582           1 :             CATCH_REQUIRE(identifier->get_string() == "-escape=33-");
    5583           1 :             csspp::position const & npos(identifier->get_position());
    5584           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5585           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5586           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5587           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5588           1 :         }
    5589             : 
    5590           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5591           1 :     }
    5592             : 
    5593             :     // generate a set of simple and valid hashes
    5594        1001 :     for(int i(0); i < 1000; ++i)
    5595             :     {
    5596        1000 :         std::stringstream ss;
    5597        3000 :         csspp::position pos("test.css");
    5598        1000 :         csspp::lexer l(ss, pos);
    5599        1000 :         ss << '#';
    5600        1000 :         int count(rand() % 30 + 1);
    5601        1000 :         std::string word;
    5602       16396 :         for(int j(0); j < count; ++j)
    5603             :         {
    5604       15396 :             csspp::wide_char_t c(0);
    5605             :             for(;;)
    5606             :             {
    5607       15427 :                 c = rand() % 0x110000;
    5608       15427 :                 if((c >= 'A' && c <= 'Z')
    5609       15427 :                 || (c >= 'a' && c <= 'z')
    5610       15427 :                 || (c >= '0' && c <= '9')
    5611       15427 :                 || c == '_'
    5612       15427 :                 || c == '-'
    5613       15427 :                 ||   (c > 0x80
    5614       15427 :                    && c != 0xFFFD
    5615       15427 :                    && (c < 0xD800 || c > 0xDFFF)
    5616       15396 :                    && ((c & 0xFFFF) != 0xFFFE)
    5617       15396 :                    && ((c & 0xFFFF) != 0xFFFF))
    5618             :                 )
    5619             :                 {
    5620             :                     break;
    5621             :                 }
    5622             :             }
    5623       15396 :             word += l.wctomb(c);
    5624             :         }
    5625        1000 :         ss << word;
    5626             : 
    5627             :         // hash
    5628             :         {
    5629        1000 :             csspp::node::pointer_t identifier(l.next_token());
    5630        1000 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::HASH));
    5631        1000 :             CATCH_REQUIRE(identifier->get_string() == word);
    5632        1000 :             csspp::position const & npos(identifier->get_position());
    5633        1000 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5634        1000 :             CATCH_REQUIRE(npos.get_page() == 1);
    5635        1000 :             CATCH_REQUIRE(npos.get_line() == 1);
    5636        1000 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5637        1000 :         }
    5638             : 
    5639        1000 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5640        1000 :     }
    5641             : 
    5642             :     // test a standard hash
    5643             :     {
    5644           1 :         std::stringstream ss;
    5645           3 :         csspp::position pos("test.css");
    5646           1 :         csspp::lexer l(ss, pos);
    5647           1 :         ss << "#-escape\\0 33-";
    5648             : 
    5649             :         // hash
    5650             :         {
    5651           1 :             csspp::node::pointer_t hash(l.next_token());
    5652           1 :             CATCH_REQUIRE(hash->is(csspp::node_type_t::HASH));
    5653           1 :             CATCH_REQUIRE(hash->get_string() == "-escape");
    5654           1 :             csspp::position const & npos(hash->get_position());
    5655           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5656           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5657           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5658           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5659             : 
    5660           1 :             VERIFY_ERRORS("test.css(1): error: escape character '\\0' is not acceptable in CSS.\n");
    5661           1 :         }
    5662             : 
    5663             :         // integer
    5664             :         {
    5665           1 :             csspp::node::pointer_t identifier(l.next_token());
    5666           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::INTEGER));
    5667           1 :             CATCH_REQUIRE(identifier->get_integer() == 33);
    5668           1 :             csspp::position const & npos(identifier->get_position());
    5669           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5670           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5671           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5672           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5673           1 :         }
    5674             : 
    5675             :         // subtract
    5676             :         {
    5677           1 :             csspp::node::pointer_t subtract(l.next_token());
    5678           1 :             CATCH_REQUIRE(subtract->is(csspp::node_type_t::SUBTRACT));
    5679           1 :             csspp::position const & npos(subtract->get_position());
    5680           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5681           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5682           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5683           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5684           1 :         }
    5685             : 
    5686           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5687           1 :     }
    5688           1 : }
    5689             : 
    5690           1 : CATCH_TEST_CASE("Invalid hash", "[lexer] [hash]")
    5691             : {
    5692             :     // test an empty hash
    5693             :     {
    5694           1 :         std::stringstream ss;
    5695           3 :         csspp::position pos("test.css");
    5696           1 :         csspp::lexer l(ss, pos);
    5697           1 :         ss << "empty # here";
    5698             : 
    5699             :         // identifier (empty)
    5700             :         {
    5701           1 :             csspp::node::pointer_t hash(l.next_token());
    5702           1 :             CATCH_REQUIRE(hash->is(csspp::node_type_t::IDENTIFIER));
    5703           1 :             CATCH_REQUIRE(hash->get_string() == "empty");
    5704           1 :             csspp::position const & npos(hash->get_position());
    5705           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5706           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5707           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5708           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5709             : 
    5710           1 :             VERIFY_ERRORS("");
    5711           1 :         }
    5712             : 
    5713             :         // whitespace
    5714             :         {
    5715           1 :             csspp::node::pointer_t hash(l.next_token());
    5716           1 :             CATCH_REQUIRE(hash->is(csspp::node_type_t::WHITESPACE));
    5717           1 :             csspp::position const & npos(hash->get_position());
    5718           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5719           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5720           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5721           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5722             : 
    5723           1 :             VERIFY_ERRORS("");
    5724           1 :         }
    5725             : 
    5726             :         // hash
    5727             :         // '#" by itself generates an error and nothing is returned
    5728             : 
    5729             :         // whitespace
    5730             :         {
    5731           1 :             csspp::node::pointer_t hash(l.next_token());
    5732           1 :             CATCH_REQUIRE(hash->is(csspp::node_type_t::WHITESPACE));
    5733           1 :             csspp::position const & npos(hash->get_position());
    5734           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5735           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5736           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5737           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5738             : 
    5739           1 :             VERIFY_ERRORS("test.css(1): error: '#' by itself is not valid.\n");
    5740           1 :         }
    5741             : 
    5742             :         // identifier (here)
    5743             :         {
    5744           1 :             csspp::node::pointer_t identifier(l.next_token());
    5745           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::IDENTIFIER));
    5746           1 :             CATCH_REQUIRE(identifier->get_string() == "here");
    5747           1 :             csspp::position const & npos(identifier->get_position());
    5748           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5749           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5750           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5751           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5752           1 :         }
    5753             : 
    5754           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5755           1 :     }
    5756             : 
    5757             :     // no error left over
    5758           1 :     VERIFY_ERRORS("");
    5759           1 : }
    5760             : 
    5761           1 : CATCH_TEST_CASE("Placeholders", "[lexer] [hash]")
    5762             : {
    5763             :     // test a standard placeholder
    5764             :     {
    5765           1 :         std::stringstream ss;
    5766           3 :         csspp::position pos("test.css");
    5767           1 :         csspp::lexer l(ss, pos);
    5768           1 :         ss << "%es-cape\\=33-";
    5769             : 
    5770             :         // hash
    5771             :         {
    5772           1 :             csspp::node::pointer_t identifier(l.next_token());
    5773           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::PLACEHOLDER));
    5774           1 :             CATCH_REQUIRE(identifier->get_string() == "es-cape=33-");
    5775           1 :             csspp::position const & npos(identifier->get_position());
    5776           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5777           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5778           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5779           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5780           1 :         }
    5781             : 
    5782           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5783           1 :     }
    5784             : 
    5785             :     // generate a set of simple and valid placeholders
    5786        1001 :     for(int i(0); i < 1000; ++i)
    5787             :     {
    5788        1000 :         std::stringstream ss;
    5789        3000 :         csspp::position pos("test.css");
    5790        1000 :         csspp::lexer l(ss, pos);
    5791        1000 :         ss << '%';
    5792        1000 :         int count(rand() % 30 + 1);
    5793        1000 :         std::string word;
    5794        1000 :         std::string lword;
    5795       16546 :         for(int j(0); j < count; ++j)
    5796             :         {
    5797       15546 :             csspp::wide_char_t c(0);
    5798             :             for(;;)
    5799             :             {
    5800       15572 :                 c = rand() % 0x110000;
    5801       15572 :                 if((c >= 'A' && c <= 'Z')
    5802       15571 :                 || (c >= 'a' && c <= 'z')
    5803       15571 :                 || (c >= '0' && c <= '9')
    5804       15570 :                 || c == '_'
    5805       15570 :                 || (c == '-' && j != 0)
    5806       15570 :                 ||   (c > 0x80
    5807       15569 :                    && c != 0xFFFD
    5808       15569 :                    && (c < 0xD800 || c > 0xDFFF)
    5809       15544 :                    && ((c & 0xFFFF) != 0xFFFE)
    5810       15544 :                    && ((c & 0xFFFF) != 0xFFFF))
    5811             :                 )
    5812             :                 {
    5813             :                     break;
    5814             :                 }
    5815             :             }
    5816       15546 :             word += l.wctomb(c);
    5817       15546 :             lword += l.wctomb(std::tolower(c));
    5818             :         }
    5819        1000 :         ss << word;
    5820             : 
    5821             :         // placeholder
    5822             :         {
    5823        1000 :             csspp::node::pointer_t identifier(l.next_token());
    5824        1000 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::PLACEHOLDER));
    5825        1000 :             CATCH_REQUIRE(identifier->get_string() == word);
    5826        1000 :             CATCH_REQUIRE(identifier->get_lowercase_string() == lword);
    5827        1000 :             csspp::position const & npos(identifier->get_position());
    5828        1000 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5829        1000 :             CATCH_REQUIRE(npos.get_page() == 1);
    5830        1000 :             CATCH_REQUIRE(npos.get_line() == 1);
    5831        1000 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5832        1000 :         }
    5833             : 
    5834        1000 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5835        1000 :     }
    5836             : 
    5837             :     // test a standard placeholder
    5838             :     {
    5839           1 :         std::stringstream ss;
    5840           3 :         csspp::position pos("test.css");
    5841           1 :         csspp::lexer l(ss, pos);
    5842           1 :         ss << "%es-cape\\0 33-";
    5843             : 
    5844             :         // placeholder
    5845             :         {
    5846           1 :             csspp::node::pointer_t hash(l.next_token());
    5847           1 :             CATCH_REQUIRE(hash->is(csspp::node_type_t::PLACEHOLDER));
    5848           1 :             CATCH_REQUIRE(hash->get_string() == "es-cape");
    5849           1 :             csspp::position const & npos(hash->get_position());
    5850           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5851           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5852           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5853           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5854             : 
    5855           1 :             VERIFY_ERRORS("test.css(1): error: escape character '\\0' is not acceptable in CSS.\n");
    5856           1 :         }
    5857             : 
    5858             :         // integer
    5859             :         {
    5860           1 :             csspp::node::pointer_t identifier(l.next_token());
    5861           1 :             CATCH_REQUIRE(identifier->is(csspp::node_type_t::INTEGER));
    5862           1 :             CATCH_REQUIRE(identifier->get_integer() == 33);
    5863           1 :             csspp::position const & npos(identifier->get_position());
    5864           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5865           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5866           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5867           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5868           1 :         }
    5869             : 
    5870             :         // subtract
    5871             :         {
    5872           1 :             csspp::node::pointer_t subtract(l.next_token());
    5873           1 :             CATCH_REQUIRE(subtract->is(csspp::node_type_t::SUBTRACT));
    5874           1 :             csspp::position const & npos(subtract->get_position());
    5875           1 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5876           1 :             CATCH_REQUIRE(npos.get_page() == 1);
    5877           1 :             CATCH_REQUIRE(npos.get_line() == 1);
    5878           1 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5879           1 :         }
    5880             : 
    5881           1 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5882           1 :     }
    5883             : 
    5884             :     // no error left over
    5885           1 :     VERIFY_ERRORS("");
    5886           1 : }
    5887             : 
    5888           1 : CATCH_TEST_CASE("Variables", "[lexer] [variable]")
    5889             : {
    5890             :     // test variables
    5891        1001 :     for(int i(0); i < 1000; ++i)
    5892             :     {
    5893        1000 :         std::stringstream ss;
    5894        3000 :         csspp::position pos("test.css");
    5895        1000 :         csspp::lexer l(ss, pos);
    5896        1000 :         std::string word;
    5897        1000 :         std::string lword;
    5898        1000 :         int size(rand() % 20 + 1);
    5899       11253 :         for(int j(0); j < size; ++j)
    5900             :         {
    5901             :             // only valid characters
    5902       10253 :             char c(rand() % (10 + 26 + 26 + 2));
    5903       10253 :             if(c < 10)
    5904             :             {
    5905        1602 :                 c += '0';
    5906        1602 :                 lword += c;
    5907             :             }
    5908        8651 :             else if(c < 10 + 26)
    5909             :             {
    5910        4124 :                 c += 'A' - 10;
    5911        4124 :                 lword += c + 0x20;
    5912             :             }
    5913        4527 :             else if(c < 10 + 26 + 26)
    5914             :             {
    5915        4206 :                 c += 'a' - 10 - 26;
    5916        4206 :                 lword += c;
    5917             :             }
    5918         321 :             else if(c < 10 + 26 + 26 + 1)
    5919             :             {
    5920         166 :                 c = '-';
    5921         166 :                 lword += '_'; // '-' == '_' in variable names
    5922             :             }
    5923             :             else
    5924             :             {
    5925         155 :                 c = '_';
    5926         155 :                 lword += '_';
    5927             :             }
    5928       10253 :             word += c;
    5929             :         }
    5930        1000 :         ss << "$" << word;
    5931             : 
    5932             :         // variable
    5933             :         {
    5934        1000 :             csspp::node::pointer_t variable(l.next_token());
    5935        1000 :             CATCH_REQUIRE(variable->is(csspp::node_type_t::VARIABLE));
    5936        1000 :             CATCH_REQUIRE(variable->get_string() == lword);
    5937        1000 :             csspp::position const & npos(variable->get_position());
    5938        1000 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5939        1000 :             CATCH_REQUIRE(npos.get_page() == 1);
    5940        1000 :             CATCH_REQUIRE(npos.get_line() == 1);
    5941        1000 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    5942        1000 :         }
    5943             : 
    5944        1000 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    5945             : 
    5946             :         // no error left over
    5947        1000 :         VERIFY_ERRORS("");
    5948        1000 :     }
    5949             : 
    5950             :     // test variable functions
    5951        1001 :     for(int i(0); i < 1000; ++i)
    5952             :     {
    5953        1000 :         std::stringstream ss;
    5954        3000 :         csspp::position pos("test.css");
    5955        1000 :         csspp::lexer l(ss, pos);
    5956        1000 :         std::string word;
    5957        1000 :         std::string lword;
    5958        1000 :         int size(rand() % 20 + 1);
    5959       11545 :         for(int j(0); j < size; ++j)
    5960             :         {
    5961             :             // only valid characters
    5962       10545 :             char c(rand() % (10 + 26 + 26 + 2));
    5963       10545 :             if(c < 10)
    5964             :             {
    5965        1622 :                 c += '0';
    5966        1622 :                 lword += c;
    5967             :             }
    5968        8923 :             else if(c < 10 + 26)
    5969             :             {
    5970        4286 :                 c += 'A' - 10;
    5971        4286 :                 lword += c + 0x20;
    5972             :             }
    5973        4637 :             else if(c < 10 + 26 + 26)
    5974             :             {
    5975        4283 :                 c += 'a' - 10 - 26;
    5976        4283 :                 lword += c;
    5977             :             }
    5978         354 :             else if(c < 10 + 26 + 26 + 1)
    5979             :             {
    5980         176 :                 c = '-';
    5981         176 :                 lword += '_'; // '-' == '_' in variable names
    5982             :             }
    5983             :             else
    5984             :             {
    5985         178 :                 c = '_';
    5986         178 :                 lword += '_';
    5987             :             }
    5988       10545 :             word += c;
    5989             :         }
    5990        1000 :         ss << "$" << word << "(args)";
    5991             : 
    5992             :         // variable function
    5993             :         {
    5994        1000 :             csspp::node::pointer_t variable(l.next_token());
    5995        1000 :             CATCH_REQUIRE(variable->is(csspp::node_type_t::VARIABLE_FUNCTION));
    5996        1000 :             CATCH_REQUIRE(variable->get_string() == lword);
    5997        1000 :             csspp::position const & npos(variable->get_position());
    5998        1000 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    5999        1000 :             CATCH_REQUIRE(npos.get_page() == 1);
    6000        1000 :             CATCH_REQUIRE(npos.get_line() == 1);
    6001        1000 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    6002        1000 :         }
    6003             : 
    6004             :         // args
    6005             :         {
    6006        1000 :             csspp::node::pointer_t variable(l.next_token());
    6007        1000 :             CATCH_REQUIRE(variable->is(csspp::node_type_t::IDENTIFIER));
    6008        1000 :             CATCH_REQUIRE(variable->get_string() == "args");
    6009        1000 :             csspp::position const & npos(variable->get_position());
    6010        1000 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    6011        1000 :             CATCH_REQUIRE(npos.get_page() == 1);
    6012        1000 :             CATCH_REQUIRE(npos.get_line() == 1);
    6013        1000 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    6014        1000 :         }
    6015             : 
    6016             :         // ')'
    6017             :         {
    6018        1000 :             csspp::node::pointer_t variable(l.next_token());
    6019        1000 :             CATCH_REQUIRE(variable->is(csspp::node_type_t::CLOSE_PARENTHESIS));
    6020        1000 :             csspp::position const & npos(variable->get_position());
    6021        1000 :             CATCH_REQUIRE(npos.get_filename() == "test.css");
    6022        1000 :             CATCH_REQUIRE(npos.get_page() == 1);
    6023        1000 :             CATCH_REQUIRE(npos.get_line() == 1);
    6024        1000 :             CATCH_REQUIRE(npos.get_total_line() == 1);
    6025        1000 :         }
    6026             : 
    6027        1000 :         CATCH_REQUIRE(l.next_token()->is(csspp::node_type_t::EOF_TOKEN));
    6028             : 
    6029             :         // no error left over
    6030        1000 :         VERIFY_ERRORS("");
    6031        1000 :     }
    6032           1 : }
    6033             : 
    6034             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14