// runnerline_print.cpp // Revision 7-feb-2005 #include "runnerline_impl.h" #include "using.h" #include "sysvar.h" #include "util.h" using util::to_string; #include "trace.h" #include #define ASSERT assert namespace sysvar= blassic::sysvar; using namespace blassic::file; #define requiretoken(c) if (token.code == c) ; else throw ErrSyntax #define expecttoken(c) \ do { \ gettoken (); \ if (token.code != c) throw ErrSyntax; \ } while (0) void RunnerLineImpl::print_using (BlFile & out) { TRACEFUNC (tr, "RunnerLineImpl::print_using"); std::string format= expectstring (); VectorUsing usingf; parseusing (format, usingf); if (token.code == ',' || token.code == ';') gettoken (); const size_t l= usingf.size (); size_t ind= 0; Using * pf= usingf [ind]; for (;;) { if (ind == 0 && pf->isliteral () ) { pf->putliteral (out); ind= (ind + 1) % l; if (ind == 0) throw ErrFunctionCall; pf= usingf [ind]; } BlResult result; eval (result); switch (result.type () ) { case VarNumber: pf->putnumeric (out, result.number () ); break; case VarInteger: pf->putnumeric (out, result.number () ); break; case VarString: pf->putstring (out, result.str () ); break; default: ASSERT (false); throw ErrBlassicInternal; } ind= (ind + 1) % l; pf= usingf [ind]; if (ind != 0 && pf->isliteral () ) { pf->putliteral (out); ind= (ind + 1) % l; // Seen unnecessary, and is erroneous. //if (ind == 0) // throw ErrFunctionCall; pf= usingf [ind]; } if (endsentence () ) { //out << '\n'; out.endline (); break; } if (token.code == ';' || token.code == ',') { gettoken (); if (endsentence () ) break; } } } namespace { class ChannelSave { BlFile & bf; bool inversesaved; bool inversevalue; bool inksaved; int inkvalue; bool papersaved; int papervalue; bool brightsaved; bool brightvalue; public: ChannelSave (BlFile & bf) : bf (bf), inversesaved (false), inksaved (false), papersaved (false), brightsaved (false) { TRACEFUNC (tr, "ChannelSave::ChannelSave"); } ~ChannelSave () { TRACEFUNC (tr, "ChannelSave::~ChannelSave"); if (inversesaved) bf.inverse (inversevalue); if (brightsaved) bf.bright (brightvalue); if (inksaved) bf.setcolor (inkvalue); if (papersaved) bf.setbackground (papervalue); } void inverse (bool inv) { if (! inversesaved) { inversevalue= bf.getinverse (); inversesaved= true; } bf.inverse (inv); } void ink (int color) { if (! inksaved) { inkvalue= bf.getcolor (); inksaved= true; } bf.setcolor (color); } void paper (int color) { if (! papersaved) { papervalue= bf.getbackground (); papersaved= true; } bf.setbackground (color); } void bright (bool br) { if (! brightsaved) { brightvalue= bf.getbright (); brightsaved= true; } bf.bright (br); } }; } // namespace bool RunnerLineImpl::do_PRINT () { TRACEFUNC (tr, "RunnerLineImpl::do_PRINT"); // Same function used for PRINT and LPRINT, differing only // in the default channel. BlChannel channel= (token.code == keyLPRINT) ? PrinterChannel : DefaultChannel; gettoken (); if (token.code == '#') { channel= expectchannel (); // Allow ';' for Spectrum compatibility. if (token.code == ',' || token.code == ';') gettoken (); else require_endsentence (); } TRMESSAGE (tr, "Channel: " + to_string (channel) ); BlFile & out= getfile (channel); TRMESSAGE (tr, "Text window: " + to_string (out.istextwindow () ) ); TRMESSAGE (tr, "File: " + to_string (out.isfile () ) ); ChannelSave channelsave (out); if (token.code == '@') { BlResult result; expect (result); BlInteger pos= result.integer (); requiretoken (','); gettoken (); #if 0 BlInteger row= (pos / 32) + 1; BlInteger col= (pos % 32) + 1; if (graphics::ingraphicsmode () ) graphics::locate (row, col); else locate (row, col); #else out.gotoxy (pos % 32, pos / 32); #endif } if (endsentence () ) { //out << '\n'; out.endline (); return false; } bool spacebeforenumber= sysvar::hasFlags1 (sysvar::SpaceBefore); BlResult result; size_t n; bool ended= false; do { switch (token.code) { case ',': case ';': // Separators can be before any statement, break; case keyUSING: print_using (out); ended= true; break; case keyTAB: //getparenarg (result); // Not required to improve Sinclair ZX compatibility. expect (result); n= result.integer (); if (! sysvar::hasFlags1 (sysvar::TabStyle) ) n-= 1; out.tab (n); break; case keySPC: getparenarg (result); n= result.integer (); //out << std::string (n, ' '); out.putspaces (n); break; case keyAT: out.flush (); { expect (result); //BlInteger row= result.integer () + 1; BlInteger row= result.integer (); requiretoken (','); expect (result); //BlInteger col= result.integer () + 1; BlInteger col= result.integer (); #if 0 if (graphics::ingraphicsmode () ) graphics::locate (row, col); else locate (row, col); #else out.gotoxy (col, row); #endif } break; case keyINK: out.flush (); { BlInteger color= expectinteger (); channelsave.ink (color); } break; case keyPAPER: out.flush (); { BlInteger color= expectinteger (); channelsave.paper (color); } break; case keyINVERSE: out.flush (); { BlInteger inv= expectinteger (); channelsave.inverse (inv); } break; case keyBRIGHT: out.flush (); { BlInteger br= expectinteger (); channelsave.bright (br); } break; default: eval (result); switch (result.type () ) { case VarString: { TRMESSAGE (tr, "string"); std::string txt= result.str (); TRMESSAGE (tr, txt); //out << result.str (); out << txt; } break; case VarNumber: { BlNumber n= result.number (); if (spacebeforenumber && n >= 0) out << ' '; out << n; } break; case VarInteger: { BlInteger n= result.integer (); if (spacebeforenumber && n >= 0) out << ' '; out << n; } break; default: ASSERT (false); throw ErrBlassicInternal; } } if (ended) break; if (endsentence () ) { //out << '\n'; out.endline (); break; } #if 0 if (token.code != ';' && token.code != ',') throw ErrSyntax; if (token.code == ',') out.tab (); gettoken (); #else // Now separator is not required switch (token.code) { case ',': out.tab (); gettoken (); break; case ';': gettoken (); break; default: ; // Nothing } #endif } while (! endsentence () ); TRMESSAGE (tr, "Flushing channel"); out.flush (); return false; } // End of runnerline_print.cpp