aboutsummaryrefslogblamecommitdiffstats
path: root/runnerline_print.cpp
blob: f2628e1b8c83ddfa0344b85446e5fb297ee9c6a4 (plain) (tree)














































































































































































































































































































































































                                                                             
// 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 <cassert>
#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
Un proyecto texto-plano.xyz