diff options
Diffstat (limited to 'using.cpp')
-rw-r--r-- | using.cpp | 582 |
1 files changed, 582 insertions, 0 deletions
diff --git a/using.cpp b/using.cpp new file mode 100644 index 0000000..18b7bd6 --- /dev/null +++ b/using.cpp @@ -0,0 +1,582 @@ +// using.cpp +// Revision 9-jan-2005 + +#include "using.h" +#include "util.h" +#include "trace.h" + +#include <sstream> +#include <iomanip> +#include <memory> +using std::auto_ptr; +#include <cstdio> +using std::sprintf; +#include <cstdlib> +#include <cmath> +#include <stdexcept> +#include <algorithm> + +#include <cassert> +#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 <unsigned char> ('#') ]= true; + charformat [static_cast <unsigned char> ('.') ]= true; + charformat [static_cast <unsigned char> ('+') ]= true; + charformat [static_cast <unsigned char> ('*') ]= true; + charformat [static_cast <unsigned char> ('$') ]= true; + charformat [static_cast <unsigned char> (poundsign) ]= true; + charformat [static_cast <unsigned char> (eurosign) ]= true; + charformat [static_cast <unsigned char> ('\\') ]= true; + charformat [static_cast <unsigned char> ('&') ]= true; + charformat [static_cast <unsigned char> ('!') ]= true; + return true; +} + +bool initedformat= initformat (); + +bool ischarformat (char c) +{ + return charformat [static_cast <unsigned char> (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 <int> (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 <int> + (std::floor (std::log10 (fabs (n) ) ) ) + + - static_cast <int> (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 <UsingLiteral *> (v.back () ) ) != NULL) + { + prev->addstr (dynamic_cast <const UsingLiteral &> (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 <char> (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 |