// using.cpp // Revision 9-jan-2005 #include "using.h" #include "util.h" #include "trace.h" #include #include #include using std::auto_ptr; #include using std::sprintf; #include #include #include #include #include #define ASSERT assert using namespace blassic::file; //************************************** // Auxiliary functions //************************************** namespace { const char poundsign= '\xA3'; #ifdef _Windows const char eurosign= '\x80'; #else const char eurosign= '\xA4'; #endif bool charformat [256]= { false }; bool initformat () { charformat [static_cast ('#') ]= true; charformat [static_cast ('.') ]= true; charformat [static_cast ('+') ]= true; charformat [static_cast ('*') ]= true; charformat [static_cast ('$') ]= true; charformat [static_cast (poundsign) ]= true; charformat [static_cast (eurosign) ]= true; charformat [static_cast ('\\') ]= true; charformat [static_cast ('&') ]= true; charformat [static_cast ('!') ]= true; return true; } bool initedformat= initformat (); bool ischarformat (char c) { return charformat [static_cast (c) ]; } } //************************************** // Using //************************************** Using::~Using () { } bool Using::isliteral () const { return false; } void Using::putliteral (BlFile &) const { throw ErrMismatch; } void Using::putnumeric (BlFile &, BlNumber) const { throw ErrMismatch; } void Using::putstring (BlFile &, const std::string &) const { throw ErrMismatch; } //************************************** // UsingLiteral //************************************** UsingLiteral::UsingLiteral (std::istream & is) { TRACEFUNC (tr, "UsingLiteral::UsingLiteral"); init (is); } UsingLiteral::UsingLiteral (std::istream & is, const std::string & strinit) : s (strinit) { TRACEFUNC (tr, "UsingLiteral::UsingLiteral"); init (is); } UsingLiteral::UsingLiteral (const std::string & strinit) : s (strinit) { TRACEFUNC (tr, "UsingLiteral::UsingLiteral"); } void UsingLiteral::init (std::istream & is) { TRACEFUNC (tr, "UsingLiteral::init"); char c; while (is >> c && ! ischarformat (c) ) { s+= c; } ASSERT (! s.empty () ); TRMESSAGE (tr, std::string ("Literal is: \"") + s + '"'); if (is) is.unget (); } bool UsingLiteral::isliteral () const { return true; } void UsingLiteral::addstr (const std::string & stradd) { s+= stradd; } void UsingLiteral::putliteral (BlFile & out) const { out << s; } void UsingLiteral::putnumeric (BlFile &, BlNumber) const { ASSERT (false); throw ErrBlassicInternal; } void UsingLiteral::putstring (BlFile &, const std::string &) const { ASSERT (false); throw ErrBlassicInternal; } //************************************** // UsingNumeric //************************************** UsingNumeric::UsingNumeric (std::istream & is, std::string & tail) : invalid (true), digit (0), decimal (0), scientific (0), milliards (false), putsign (false), signatend (false), blankpositive (false), asterisk (false), dollar (false), pound (false), euro (false) { TRACEFUNC (tr, "UsingNumeric::UsingNumeric"); char c; is >> c; ASSERT (is); if (c == '+') { putsign= true; s+= c; is >> c; } if (is) { switch (c) { case '*': s+= c; if (! (is >> c) ) return; if (c != '*') { TRMESSAGE (tr, std::string (1, c) + " following *"); is.unget (); return; } asterisk= true; digit+= 2; if (is >> c) { switch (c) { case '$': dollar= true; s+= c; is >> c; break; case poundsign: pound= true; s+= c; is >> c; break; case eurosign: euro= true; s+= c; is >> c; break; } } break; case '$': case poundsign: case eurosign: s+= c; { char sign= c; if (! (is >> c) ) return; if (c != sign) { is.unget (); return; } ++digit; switch (sign) { case '$': dollar= true; break; case poundsign: pound= true; break; case eurosign: euro= true; break; } s+= c; } is >> c; break; } } while (is && c == '#') { invalid= false; ++digit; s+= c; is >> c; } if (is) { if (c == '.') { s+= c; while (is >> c && c == '#') { invalid= false; ++decimal; s+= c; } } } if (is) { if (c == '^') { ++scientific; s+= c; if (invalid) return; while (scientific < 5 && is >> c && c == '^') ++scientific; if (scientific == 5) is >> c; if (scientific < 4) { tail= std::string (scientific, '^'); scientific= 0; } else scientific-= 2; } } if (is && (c == '+' || c == '-') ) { s+= c; if (invalid) return; putsign= true; signatend= true; if (c == '-') blankpositive= true; is >> c; } if (is) is.unget (); } namespace { class UsingOverflow { }; } namespace { // Workaround for a problem in gcc double zero= 0.0; } void UsingNumeric::putliteral (BlFile &) const { ASSERT (false); throw ErrBlassicInternal; } void UsingNumeric::putnumeric (BlFile & out, BlNumber n) const { try { int negative; if (scientific > 0) { size_t d= digit; if (n < zero && ! putsign) --d; #if 0 int prec= d + decimal; int dec; char * aux= ecvt (n, prec, & dec, & negative); int e= dec - static_cast (d); //if (scientific == 0 && e != 0) // throw UsingOverflow (); std::string stre= util::to_string (abs (e) ); int ezeroes= scientific - stre.size (); if (ezeroes < 0) throw UsingOverflow (); if (putsign && ! signatend) { out << (negative ? '-' : '+'); } if (! putsign && negative) out << '-'; out << (std::string (aux, d) + '.' + std::string (aux + d) ) << 'E' << (e < 0 ? '-' : '+') << std::string (ezeroes, '0') << stre; #else int prec= d + decimal - 1; //std::cout << '(' << d << ',' << prec << ')' << // std::flush; char buffer [64]; sprintf (buffer, "%+.*e", prec, n); //std::cout << '[' << buffer << ']' << std::flush; negative= buffer [0] == '-'; std::string aux= std::string (1, buffer [1]) + (buffer + 3); std::string::size_type pose= aux.find ('e'); if (pose != std::string::npos) aux.erase (pose); int e= static_cast (std::floor (std::log10 (fabs (n) ) ) ) + - static_cast (d) + 1; std::string stre= util::to_string (abs (e) ); int ezeroes= scientific - stre.size (); if (ezeroes < 0) throw UsingOverflow (); if (putsign && ! signatend) { out << (negative ? '-' : '+'); } if (! putsign && negative) out << '-'; out << (aux.substr (0, d) + '.' + aux.substr (d) ) << 'E' << (e < 0 ? '-' : '+') << std::string (ezeroes, '0') << stre; #endif } else { int numdig= 0; negative= n < 0; if (negative) n= -n; if (n != 0) numdig= int (std::log10 (n * 10 ) ); if (numdig < 0) numdig= 0; size_t w= numdig; if (digit > w) w= digit; size_t maxw= digit; if (decimal > 0) { w+= decimal + 1; maxw+= decimal + 1; } if (negative && ! putsign) { --w; --maxw; } std::ostringstream oss; if (decimal > 0) oss.setf (std::ios::showpoint); // Some implementations lack the fixed manipulator. oss.setf (std::ios::fixed, std::ios::floatfield); oss << std::setw (w) << //std::setprecision (decimal + numdig) << std::setprecision (decimal) << //std::fixed << n; std::string strn= oss.str (); if (digit == 0 && strn [0] == '0') strn.erase (0, 1); if (strn.size () > maxw) throw UsingOverflow (); if (dollar || pound || euro) { char sign= dollar ? '$' : pound ? poundsign : eurosign; strn.insert (strn.find_first_not_of (" "), 1, sign); } if ( (putsign && ! signatend) || (! putsign && negative) ) { std::string::size_type possign= strn.find_first_not_of (" "); if (possign == std::string::npos) possign= 0; strn.insert (possign, 1, negative ? '-' : '+'); } if (asterisk) { for (size_t i= 0, l= strn.find_first_not_of (" "); i < l; ++i) { strn [i]= '*'; } } out << strn; } if (signatend) { ASSERT (putsign); out << (negative ? '-' : blankpositive ? ' ' : '+' ); } } catch (UsingOverflow &) { out << '%' << n; } } //************************************** // UsingString //************************************** UsingString::UsingString (std::istream & is) : n (0) { char c; is >> c; ASSERT (is); switch (c) { case '\\': n= 1; while (is >> c && c == ' ') ++n; if (is) { if (c == '\\') ++n; else is.unget (); } break; case '&': // Nothing in particular break; case '!': n= 1; break; default: throw ErrBlassicInternal; } } void UsingString::putliteral (BlFile &) const { ASSERT (false); throw ErrBlassicInternal; } void UsingString::putstring (BlFile & out, const std::string & str) const { if (n > 0) out << str.substr (0, n); else out << str; } //************************************** // VectorUsing //************************************** VectorUsing::VectorUsing () { } VectorUsing::~VectorUsing () { std::for_each (v.begin (), v.end (), delete_it); } void VectorUsing::push_back (const Using & u) { UsingLiteral * prev; if (! v.empty () && u.isliteral () && (prev= dynamic_cast (v.back () ) ) != NULL) { prev->addstr (dynamic_cast (u).str () ); return; } v.push_back (u.clone () ); } //************************************** // parseusing //************************************** void parseusing (const std::string & str, VectorUsing & vu) { TRACEFUNC (tr, "parseusing"); std::istringstream is (str); is.unsetf (std::ios::skipws); int c; while ( (c= is.peek () ) != EOF) { switch (static_cast (c) ) { case '#': case '.': case '+': case '*': case '$': case poundsign: case eurosign: { std::string tail; UsingNumeric un (is, tail); if (un.isvalid () ) vu.push_back (un); else { UsingLiteral ul (un.str () ); vu.push_back (ul); } if (! tail.empty () ) { UsingLiteral ul (is, tail); vu.push_back (ul); } } break; case '\\': case '&': case '!': { UsingString us (is); vu.push_back (us); } break; default: { UsingLiteral ul (is); vu.push_back (ul); } } } } // End of using.cpp