# Mu - 015literal_noninteger.cc

     1 //: Support literal non-integers.
     2
     3 :(scenarios load)
     4 :(scenario noninteger_literal)
     5 def main [
     6   1:number <- copy 3.14159
     7 ]
     8 +parse:   ingredient: {3.14159: "literal-fractional-number"}
     9
    10 :(after "Parsing reagent(string s)")
    11 if (is_noninteger(s)) {
    12   name = s;
    13   type = new type_tree("literal-fractional-number", 0);
    14   set_value(to_double(s));
    15   return;
    16 }
    17
    18 :(code)
    19 bool is_noninteger(const string& s) {
    20   return s.find_first_not_of("0123456789-.") == string::npos  // no other characters
    21       && s.find_first_of("0123456789") != string::npos  // at least one digit
    22       && s.find('-', 1) == string::npos  // '-' only at first position
    23       && std::count(s.begin(), s.end(), '.') == 1;  // exactly one decimal point
    24 }
    25
    26 double to_double(string n) {
    27   char* end = NULL;
    28   // safe because string.c_str() is guaranteed to be null-terminated
    29   double result = strtod(n.c_str(), &end);
    30   assert(*end == '\0');
    31   return result;
    32 }
    33
    34 void test_is_noninteger() {
    35   CHECK(!is_noninteger("1234"));
    36   CHECK(!is_noninteger("1a2"));
    37   CHECK(is_noninteger("234.0"));
    38   CHECK(!is_noninteger("..."));
    39   CHECK(!is_noninteger("."));
    40   CHECK(is_noninteger("2."));
    41   CHECK(is_noninteger(".2"));
    42   CHECK(is_noninteger("-.2"));
    43   CHECK(is_noninteger("-2."));
    44   CHECK(!is_noninteger("--.2"));
    45   CHECK(!is_noninteger(".-2"));
    46   CHECK(!is_noninteger("..2"));
    47 }