aboutsummaryrefslogtreecommitdiffstats
path: root/program.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'program.cpp')
-rw-r--r--program.cpp1611
1 files changed, 1611 insertions, 0 deletions
diff --git a/program.cpp b/program.cpp
new file mode 100644
index 0000000..2d24c29
--- /dev/null
+++ b/program.cpp
@@ -0,0 +1,1611 @@
+// program.cpp
+// Revision 24-apr-2009
+
+#include "program.h"
+
+#include "keyword.h"
+#include "error.h"
+#include "sysvar.h"
+#include "util.h"
+using util::to_string;
+#include "trace.h"
+
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+#include <algorithm>
+#include <cctype>
+#include <sstream>
+
+using std::string;
+// For debugging.
+using std::cerr;
+using std::endl;
+using std::flush;
+
+#include <cassert>
+#define ASSERT assert
+
+#include <string.h>
+
+#ifndef USE_HASH_MAP
+
+#include <map>
+#define MAP std::map
+
+#else
+
+#if __GNUC__ < 3
+#include <hash_map>
+#define N_MAP std
+#else
+#include <ext/hash_map>
+#define N_MAP __gnu_cxx
+#endif
+
+#define MAP N_MAP::hash_map
+
+namespace N_MAP {
+
+template <> struct hash <std::string>
+{
+ hash () : hashstr (hash <const char *> () ) { }
+ size_t operator () (const std::string & str) const
+ { return hashstr (str.c_str () ); }
+private:
+ hash <const char *> hashstr;
+};
+
+} // namespace N_MAP
+
+#endif
+
+#ifdef __BORLANDC__
+#pragma warn -inl
+#endif
+
+namespace sysvar= blassic::sysvar;
+using namespace blassic::file;
+
+//**********************************************************
+// Program
+//**********************************************************
+
+Program::Program ()
+{
+}
+
+Program::~Program ()
+{
+}
+
+//**********************************************************
+// Auxiliar
+//**********************************************************
+
+namespace {
+
+typedef size_t Position;
+
+inline BlLineNumber getLineNumber (const BlChar * p)
+{
+ return peek32 (p);
+}
+
+inline BlLineNumber getLineNumberAt (const BlChar * p)
+{
+ return peek32 (p);
+}
+
+inline void setLineNumber (BlChar * p, BlLineNumber n)
+{
+ poke32 (p, n);
+}
+
+inline BlLineLength getLineLength (BlChar * p)
+{
+ return peek32 (p);
+}
+
+inline BlLineLength getLineLengthAt (BlChar * p)
+{
+ return peek32 (p + sizeof (BlLineNumber) );
+}
+
+inline void setLineLength (BlChar * p, BlLineLength n)
+{
+ poke32 (p, n);
+}
+
+inline BlChar * getLineContentAt (BlChar * p)
+{
+ return p + sizeof (BlLineNumber) + sizeof (BlLineLength);
+}
+
+inline BlChar * getNextLineAt (BlChar * p)
+{
+ return p + getLineLengthAt (p) +
+ sizeof (BlLineNumber) + sizeof (BlLineLength);
+}
+
+inline void getLineAt (BlChar * p, CodeLine & codeline)
+{
+ codeline.assign (getLineContentAt (p),
+ getLineNumberAt (p),
+ getLineLengthAt (p) );
+}
+
+// Used in renum.
+typedef MAP <BlLineNumber, BlLineNumber> MapLine;
+
+} // namespace
+
+//**********************************************************
+// ProgramImpl
+//**********************************************************
+
+//#define OLD_LABEL_CACHE
+
+//#define CACHE_ALL_LINES
+
+class ProgramImpl : public Program {
+public:
+ ProgramImpl ();
+ ~ProgramImpl ();
+ BlChar * programptr () { return program; }
+
+ BlLineNumber getlabel (const std::string & str);
+ CodeLine getfirstline ();
+ void getnextline (CodeLine & codeline);
+ void getline (BlLineNumber num, CodeLine & codeline);
+ void getline (ProgramPos pos, CodeLine & codeline);
+ void do_insert (const CodeLine & codeline);
+ void insert (const CodeLine & codeline);
+ void deletelines (BlLineNumber iniline, BlLineNumber endline);
+ void listline (const CodeLine & codeline, BlFile & out) const;
+ void list (BlLineNumber iniline, BlLineNumber endline,
+ BlFile & out) const;
+ void save (const std::string & name) const;
+ void load (const std::string & name);
+ void load (std::istream & is);
+ void merge (const std::string & name,
+ BlLineNumber inidel, BlLineNumber enddel);
+ void renew ();
+ void renum (BlLineNumber blnNew, BlLineNumber blnOld,
+ BlLineNumber blnInc, BlLineNumber blnStop);
+private:
+ BlChar * program;
+ typedef size_t ProgSize;
+ ProgSize size;
+
+ static const size_t BLOCK= 16 * 1024;
+ static size_t blockrounded (size_t size);
+ void resize (ProgSize newsize);
+ void transfer_content (ProgramImpl & other);
+
+ //typedef MAP <BlLineNumber, Position> linecache_t;
+ typedef MAP <BlLineNumber, BlChar *> linecache_t;
+ linecache_t linecache;
+
+ typedef MAP <string, BlLineNumber> labelcache_t;
+ labelcache_t labelcache;
+
+public:
+ // Public to allow use for LabelCacheGuard.
+ void clear_label_cache ();
+private:
+ void clear_cache ();
+
+ #ifndef NDEBUG
+ size_t linecache_hits;
+ size_t linecache_fails;
+ size_t labelcache_hits;
+ size_t labelcache_fails;
+ #endif
+
+ #ifndef OLD_LABEL_CACHE
+ bool labelcache_inited;
+ void generate_label_cache ();
+ #endif
+
+ #ifdef CACHE_ALL_LINES
+ bool line_cache_inited;
+ void generate_line_cache ();
+ #endif
+
+ void changeline (Position pos, const MapLine & mapline);
+ void setlabel (const std::string & label, CodeLine & codeline);
+ void getlineinpos (Position pos, CodeLine & codeline) const;
+ CodeLine getlineinpos (Position pos) const;
+ Position nextline (Position pos) const;
+ BlLineLength sizeline (Position) const;
+ BlLineNumber numline (Position pos) const;
+ const BlChar * linecontent (Position pos) const;
+ BlChar * linecontent (Position pos);
+ void loadtext (std::istream & is);
+ void loadbinary (std::istream & is);
+};
+
+ProgramImpl::ProgramImpl () :
+ program (0),
+ size (0)
+
+ #ifndef NDEBUG
+ ,
+ linecache_hits (0),
+ linecache_fails (0),
+ labelcache_hits (0),
+ labelcache_fails (0)
+ #endif
+
+ #ifndef OLD_LABEL_CACHE
+ ,
+ labelcache_inited (false)
+ #endif
+
+ #ifdef CACHE_ALL_LINES
+ ,
+ line_cache_inited (false)
+ #endif
+{
+ TRACEFUNC (tr, "ProgramImpl::ProgramImpl");
+
+ resize (0);
+}
+
+ProgramImpl::~ProgramImpl ()
+{
+ TRACEFUNC (tr, "ProgramImpl::~ProgramImpl");
+
+ if (program)
+ free (program);
+
+ #ifndef NDEBUG
+ std::ostringstream oss;
+ oss << "Line cache: hits " << linecache_hits <<
+ ", fails " << linecache_fails <<
+ " Label cache: hits " << labelcache_hits <<
+ ", fails " << labelcache_fails;
+ TRMESSAGE (tr, oss.str () );
+ #endif
+}
+
+Program * newProgram ()
+{
+ return new ProgramImpl;
+}
+
+namespace {
+
+class ProgramTail {
+ BlLineNumber n;
+ BlLineLength l;
+ ProgramTail ()
+ {
+ setLineNumber (reinterpret_cast <BlChar *> (& n),
+ LineEndProgram);
+ setLineLength (reinterpret_cast <BlChar *> (& l), 0);
+ }
+public:
+ static const ProgramTail tail;
+};
+
+const ProgramTail ProgramTail::tail;
+
+} // namespace
+
+inline size_t ProgramImpl::blockrounded (size_t size)
+{
+ return ( (size + sizeof (ProgramTail) + BLOCK - 1) / BLOCK) * BLOCK;
+}
+
+void ProgramImpl::resize (ProgSize newsize)
+{
+ // No need to clear cache here, the callers
+ // must do it.
+
+ ProgSize newblock= blockrounded (newsize);
+ if (newblock != blockrounded (size) || ! program)
+ {
+ BlChar * newprog=
+ (BlChar *) realloc (program, newblock);
+ if (! newprog)
+ throw ErrOutMemory;
+ program= newprog;
+ }
+ size= newsize;
+ memcpy (program + size, & ProgramTail::tail, sizeof (ProgramTail) );
+}
+
+void ProgramImpl::transfer_content (ProgramImpl & other)
+{
+ // No need to clear cache here, the callers
+ // must do it.
+
+ if (this == & other)
+ {
+ ASSERT (0);
+ throw ErrBlassicInternal;
+ }
+ program= other.program;
+ size= other.size;
+ other.program= NULL;
+ other.size= 0;
+}
+
+void ProgramImpl::renew ()
+{
+ TRACEFUNC (tr, "ProgramImpl::renew");
+
+ clear_cache ();
+ resize (0);
+}
+
+inline BlLineLength ProgramImpl::sizeline (Position pos) const
+{
+ BlChar * aux= program + pos + sizeof (BlLineNumber);
+ return getLineLength (aux);
+}
+
+inline Position ProgramImpl::nextline (Position pos) const
+{
+ return pos + sizeline (pos) +
+ sizeof (BlLineNumber) + sizeof (BlLineLength);
+}
+
+inline BlLineNumber ProgramImpl::numline (Position pos) const
+{
+ return getLineNumber (program + pos);
+}
+
+inline const BlChar * ProgramImpl::linecontent (Position pos) const
+{
+ return program + pos + sizeof (BlLineNumber) + sizeof (BlLineLength);
+}
+
+inline BlChar * ProgramImpl::linecontent (Position pos)
+{
+ return program + pos + sizeof (BlLineNumber) + sizeof (BlLineLength);
+}
+
+inline CodeLine ProgramImpl::getfirstline ()
+{
+ //if (size == 0)
+ // return CodeLine ();
+ return CodeLine (
+ //program + sizeof (BlLineNumber) + sizeof (BlLineLength),
+ linecontent (0),
+ numline (0), sizeline (0) );
+}
+
+inline void ProgramImpl::getnextline (CodeLine & codeline)
+{
+ //if (codeline.number () == 0)
+ if (codeline.number () == LineDirectCommand)
+ {
+ //codeline.assign (0, 0, 0);
+ codeline.assign (0, LineEndProgram, 0);
+ return;
+ }
+
+ #if 0
+
+ Position pos= codeline.content () - program;
+ pos+= codeline.length ();
+ if (pos >= size)
+ {
+ //codeline.assign (0, 0, 0);
+ codeline.assign (0, LineEndProgram, 0);
+ return;
+ }
+ codeline.assign (linecontent (pos), numline (pos), sizeline (pos) );
+
+ #else
+
+ // Testing a micro optimization.
+
+ BlChar * p= const_cast <BlChar *>
+ (codeline.content () + codeline.length () );
+ //codeline.assign (p + sizeof (BlLineNumber) + sizeof (BlLineLength),
+ // getLineNumber (p),
+ // getLineLength (p + sizeof (BlLineNumber) ) );
+
+ getLineAt (p, codeline);
+
+ #endif
+}
+
+void ProgramImpl::clear_label_cache ()
+{
+ TRACEFUNC (tr, "ProgramImpl::clear_label_cache");
+
+ labelcache.clear ();
+ #ifndef OLD_LABEL_CACHE
+ labelcache_inited= false;
+ #endif
+}
+
+void ProgramImpl::clear_cache ()
+{
+ TRACEFUNC (tr, "ProgramImpl::clear_cache");
+
+ clear_label_cache ();
+ linecache.clear ();
+
+ #ifdef CACHE_ALL_LINES
+ line_cache_inited= false;
+ #endif
+}
+
+void ProgramImpl::setlabel (const std::string & label,
+ CodeLine & codeline)
+{
+ BlLineNumber n (codeline.number () );
+
+ #if 0
+ labelcache [label]= n;
+ #else
+ std::pair <labelcache_t::iterator, bool> r =
+ labelcache.insert (std::make_pair (label, n) );
+ if (! r.second)
+ {
+ if (showdebuginfo () )
+ cerr << "Duplicate label '" << label <<
+ "' in lines " << r.first->second <<
+ " and " << n <<
+ endl;
+ throw ErrDuplicateLabel;
+ }
+ #endif
+
+ // Put it in the line number cache, to avoid one search.
+ #ifndef CACHE_ALL_LINES
+ //linecache [n]= codeline.content () - program
+ // - sizeof (BlLineNumber)
+ // - sizeof (BlLineLength);
+ linecache [n]= const_cast <BlChar *> (codeline.content () ) -
+ sizeof (BlLineNumber) - sizeof (BlLineLength);
+ #endif
+}
+
+#ifdef OLD_LABEL_CACHE
+
+inline BlLineNumber ProgramImpl::getlabel (const std::string & label)
+{
+ labelcache_t::iterator it= labelcache.find (label);
+ if (it != labelcache.end () )
+ {
+ #ifndef NDEBUG
+ ++labelcache_hits;
+ #endif
+ return it->second;
+ }
+ else
+ {
+ #ifndef NDEBUG
+ ++labelcache_fails;
+ #endif
+ }
+ CodeLine codeline= getfirstline ();
+ CodeLine::Token token;
+ //while (codeline.number () != 0)
+ while (codeline.number () != LineEndProgram)
+ {
+ codeline.gettoken (token);
+ if (token.code == keyLABEL)
+ {
+ codeline.gettoken (token);
+ if (label == token.str)
+ {
+ setlabel (label, codeline);
+ return codeline.number ();
+ }
+ }
+ getnextline (codeline);
+ }
+ //return 0;
+ return LineEndProgram;
+}
+
+#else
+
+// New label cache
+
+namespace {
+
+class LabelCacheGuard {
+public:
+ LabelCacheGuard (ProgramImpl & pin);
+ ~LabelCacheGuard ();
+ void release ();
+private:
+ ProgramImpl & pi;
+ bool released;
+};
+
+LabelCacheGuard::LabelCacheGuard (ProgramImpl & pin) :
+ pi (pin),
+ released (false)
+{ }
+
+LabelCacheGuard::~LabelCacheGuard ()
+{
+ if (! released)
+ pi.clear_label_cache ();
+}
+
+void LabelCacheGuard::release ()
+{
+ released= true;
+}
+
+} // namespace
+
+void ProgramImpl::generate_label_cache ()
+{
+ TRACEFUNC (tr, "ProgramImpl::generate_label_cache");
+
+ LabelCacheGuard guard (* this);
+
+ CodeLine codeline= getfirstline ();
+ CodeLine::Token token;
+ while (codeline.number () != LineEndProgram)
+ {
+ codeline.gettoken (token);
+ if (token.code == keyLABEL)
+ {
+ codeline.gettoken (token);
+ if (token.code != keyIDENTIFIER)
+ {
+ if (showdebuginfo () )
+ cerr << "Invalid LABEL "
+ "in line " <<
+ codeline.number () <<
+ endl;
+ throw ErrSyntax;
+ }
+ setlabel (token.str, codeline);
+ }
+ getnextline (codeline);
+ }
+
+ guard.release ();
+ labelcache_inited= true;
+}
+
+inline BlLineNumber ProgramImpl::getlabel (const std::string & label)
+{
+ if (! labelcache_inited)
+ {
+ #ifndef NDEBUG
+ ++labelcache_fails;
+ #endif
+ generate_label_cache ();
+ }
+
+ labelcache_t::iterator it= labelcache.find (label);
+ if (it != labelcache.end () )
+ {
+ #ifndef NDEBUG
+ ++labelcache_hits;
+ #endif
+ return it->second;
+ }
+ else
+ {
+ #ifndef NDEBUG
+ ++labelcache_fails;
+ #endif
+ //return 0;
+ return LineEndProgram;
+ }
+}
+
+#endif
+
+#if 0
+inline BlLineNumber ProgramImpl::getnextnum (CodeLine & line)
+{
+ if (line.number () == 0)
+ return 0;
+ Position pos= line.content () - program;
+ pos+= line.length ();
+ if (pos >= size)
+ return 0;
+ return numline (pos);
+}
+#endif
+
+inline void ProgramImpl::getlineinpos (Position pos, CodeLine & codeline)
+ const
+{
+ codeline.assign (linecontent (pos), numline (pos), sizeline (pos) );
+}
+
+CodeLine ProgramImpl::getlineinpos (Position pos) const
+{
+ return CodeLine
+ (linecontent (pos), numline (pos), sizeline (pos) );
+}
+
+
+#ifdef CACHE_ALL_LINES
+
+void ProgramImpl::generate_line_cache ()
+{
+ TRACEFUNC (tr, "ProgramImpl::generate_line_cache");
+
+ //for (Position pos= 0; pos < size; pos= nextline (pos) )
+ //{
+ // linecache [numline (pos) ]= pos;
+ //}
+ for (BlChar * p= program; ; p= getNextLineAt (p) )
+ {
+ BlLineNumber n= getLineNumberAt (p);
+ linecache [n]= p;
+ // The end mark is also included in the cache.
+ if (n > BlMaxLineNumber)
+ break;
+ }
+
+ line_cache_inited= true;
+}
+
+#endif
+
+inline void ProgramImpl::getline (BlLineNumber num, CodeLine & codeline)
+{
+ //TRACEFUNC (tr, "ProgramImpl::getline");
+
+ #ifdef CACHE_ALL_LINES
+ if (! line_cache_inited)
+ generate_line_cache ();
+ #endif
+
+ //Position pos= 0;
+ BlChar * p= program;
+
+ if (num != LineBeginProgram)
+ {
+ #ifndef CACHE_ALL_LINES
+
+ linecache_t::iterator it= linecache.find (num);
+ if (it != linecache.end () )
+ {
+ //pos= it->second;
+ p= it->second;
+ #ifndef NDEBUG
+ ++linecache_hits;
+ #endif
+ }
+ else
+ {
+ #ifndef NDEBUG
+ ++linecache_fails;
+ //TRMESSAGE (tr, "Line " + to_string (num) +
+ // " missing in cache");
+ #endif
+
+ //while (pos < size && numline (pos) < num)
+ // pos= nextline (pos);
+ //if (pos >= size)
+ //{
+ // codeline.assign (0, LineEndProgram, 0);
+ // return;
+ //}
+ while (getLineNumberAt (p) < num)
+ p= getNextLineAt (p);
+
+ //linecache [num]= pos;
+ linecache [num]= p;
+ }
+
+ #else
+
+ linecache_t::iterator it= linecache.lower_bound (num);
+// if (it == linecache.end () )
+// {
+// #ifndef NDEBUG
+// ++linecache_fails;
+// #endif
+// //codeline.assign (0, 0, 0);
+// codeline.assign (0, LineEndProgram, 0);
+// return;
+// }
+ #ifndef NDEBUG
+ ++linecache_hits;
+ #endif
+ //pos= it->second;
+ p= it->second;
+
+ #endif
+ }
+
+ //getlineinpos (pos, codeline);
+ getLineAt (p, codeline);
+}
+
+void ProgramImpl::getline (ProgramPos pos, CodeLine & codeline)
+{
+ BlLineNumber n= pos.getnum ();
+ getline (n, codeline);
+ if (codeline.number () == n)
+ {
+ BlChunk ch= pos.getchunk ();
+ if (ch != 0)
+ codeline.gotochunk (ch);
+ }
+}
+
+void ProgramImpl::do_insert (const CodeLine & codeline)
+{
+ Position pos= 0;
+ while (pos < size && numline (pos) < codeline.number () )
+ pos= nextline (pos);
+
+ const BlChar * strnew= codeline.content ();
+ BlLineLength linesize= codeline.length () +
+ sizeof (BlLineNumber) + sizeof (BlLineLength);
+
+ //#define USE_PADDING
+
+ #ifdef USE_PADDING
+ BlLineLength paddedsize= linesize;
+ unsigned int pad= linesize % 4;
+ if (pad > 0)
+ {
+ pad= 4 - pad;
+ paddedsize+= pad;
+ }
+ #endif
+
+ //unsigned long osize= size;
+ ProgSize newsize= size;
+
+ #ifndef USE_PADDING
+ Position destpos= pos + linesize;
+ #else
+ Position destpos= pos + paddedsize;
+ #endif
+
+ Position origpos;
+ if (pos < size && numline (pos) == codeline.number () )
+ {
+ origpos= nextline (pos);
+ //destpos= pos + sizenew;
+ newsize+= codeline.length () - sizeline (pos);
+ //size+= codeline.length () - sizeline (pos);
+ }
+ else
+ {
+ origpos= pos;
+ //destpos= pos + sizenew;
+
+ #ifndef USE_PADDING
+ newsize+= linesize;
+ //size+= linesize;
+ #else
+ newsize+= paddedsize;
+ //size+= paddedsize;
+ #endif
+ }
+
+ if (destpos > origpos)
+ {
+ ASSERT (newsize > size);
+ //ASSERT (size > osize);
+
+ #if 0
+ size_t newblock= blockrounded (size);
+ if (newblock != blockrounded (osize) )
+ {
+ unsigned char * newprog=
+ (unsigned char *) realloc (program, newblock);
+ if (! newprog)
+ throw ErrOutMemory;
+ program= newprog;
+ }
+ #else
+
+ ProgSize osize= size;
+ resize (newsize);
+
+ #endif
+
+ if (pos < osize)
+ {
+ memmove (program + destpos,
+ program + origpos,
+ osize - origpos);
+ }
+ }
+ else if (destpos < origpos)
+ {
+ ASSERT (newsize < size);
+ //ASSERT (size < osize);
+ memmove (program + destpos,
+ program + origpos,
+ size - origpos);
+ //osize - origpos);
+
+ #if 0
+ size_t newblock= blockrounded (size);
+ if (newblock != blockrounded (osize) )
+ {
+ unsigned char * newprog=
+ (unsigned char *) realloc (program, newblock);
+ if (! newprog)
+ throw ErrOutMemory;
+ program= newprog;
+ }
+ #else
+
+ resize (newsize);
+
+ #endif
+ }
+
+ setLineNumber (program + pos, codeline.number () );
+
+ #ifndef USE_PADDING
+ setLineLength (program + pos + sizeof (BlLineNumber),
+ codeline.length () );
+ #else
+ setLineLength (program + pos + sizeof (BlLineNumber),
+ codeline.length () + pad);
+ #endif
+
+ memcpy (linecontent (pos), strnew, codeline.length () );
+
+ #ifdef USE_PADDING
+ if (pad > 0)
+ memset (linecontent (pos) + codeline.length (), 0, pad);
+ #endif
+}
+
+void ProgramImpl::insert (const CodeLine & codeline)
+{
+ clear_cache ();
+ do_insert (codeline);
+}
+
+void ProgramImpl::deletelines
+ (BlLineNumber iniline, BlLineNumber endline)
+{
+ TRACEFUNC (tr, "ProgramImpl::deletelines");
+
+ if ( (iniline > BlMaxLineNumber && iniline != LineBeginProgram) ||
+ (endline > BlMaxLineNumber && endline != LineEndProgram) )
+ {
+ ASSERT (false);
+ throw ErrBlassicInternal;
+ }
+
+ if (iniline == LineBeginProgram && endline == LineEndProgram)
+ {
+ renew ();
+ }
+ else
+ {
+ // Evaluate initial position.
+
+ Position pos= 0;
+ if (iniline != LineBeginProgram)
+ {
+ while (pos < size && numline (pos) < iniline)
+ pos= nextline (pos);
+ }
+ if (pos >= size)
+ return;
+
+ // Evaluate final position.
+
+ Position posend= pos;
+ while (posend < size && numline (posend) <= endline)
+ posend= nextline (posend);
+ if (posend == pos)
+ return;
+
+ #if 0
+ cout << "Deleting from " << numline (pos) << " to ";
+ if (posend < size)
+ cout << "(not including) " << numline (posend);
+ else
+ cout << "the end";
+ cout << endl;
+ #endif
+
+ if (posend < size)
+ memmove (program + pos,
+ program + posend,
+ size - posend);
+ //size_t osize= size;
+ //size-= posend - pos;
+ ProgSize newsize= size - posend + pos;
+
+ if (newsize > 0)
+ {
+ clear_cache ();
+ #if 0
+ size_t newblock= blockrounded (newsize);
+ if (newblock != blockrounded (size) )
+ realloc (program, newblock);
+ #else
+
+ resize (newsize);
+
+ #endif
+ }
+ else
+ {
+ renew ();
+ }
+ }
+}
+
+void ProgramImpl::listline (const CodeLine & codeline, BlFile & out)
+ const
+{
+ BlLineNumber number= codeline.number ();
+ BlLineLength linesize= codeline.length ();
+ out << /*std::setw (7) << */ number << ' ';
+ const BlChar * aux= codeline.content ();
+ std::string line;
+ for (unsigned long i= 0; i < linesize; ++i)
+ {
+ unsigned char c= aux [i];
+ if (c == '\0') // Skip garbage
+ break;
+ if (iskey (c) )
+ {
+ BlCode s= c;
+ s<<= 8;
+ s|= aux [++i];
+ line+= decodekeyword (s);
+ }
+ else if (c == INTEGER_PREFIX)
+ {
+ //BlInteger n;
+ //n= * (BlInteger *) (aux + i + 1);
+ BlInteger n= peek32 (aux + i + 1);
+ std::ostringstream oss;
+ oss << n;
+ line+= oss.str ();
+ i+= 4;
+ }
+ else if (c == '"')
+ {
+ line+= c;
+ while ( (c= aux [++i]) != 0)
+ if (c == '"')
+ line+= "\"\"";
+ else
+ line+= c;
+ line+= '"';
+ }
+ else if (c == '\t')
+ {
+ const size_t l= line.size ();
+ line.insert (l, 8 - l % 8, ' ');
+ }
+ else
+ line+= c;
+ }
+ out << line;
+ out.endline ();
+}
+
+void ProgramImpl::list
+ (BlLineNumber iniline, BlLineNumber endline, BlFile & out) const
+{
+ TRACEFUNC (tr, "ProgramImpl::list");
+
+ if ( (iniline > BlMaxLineNumber && iniline != LineBeginProgram) ||
+ (endline > BlMaxLineNumber && endline != LineEndProgram) )
+ {
+ ASSERT (false);
+ throw ErrBlassicInternal;
+ }
+
+ Position pos= 0;
+ if (iniline != LineBeginProgram)
+ {
+ while (pos < size && numline (pos) < iniline)
+ pos= nextline (pos);
+ }
+ while (pos < size)
+ {
+ BlLineNumber number= numline (pos);
+ if (number > endline || number == LineEndProgram)
+ break;
+ listline (getlineinpos (pos), out);
+ pos= nextline (pos);
+ if (fInterrupted)
+ break;
+ }
+}
+
+namespace {
+
+bool hasblassicextension (const std::string & name)
+{
+ std::string::size_type l= name.size ();
+ if (l < 4)
+ return false;
+ std::string ext= name.substr (l - 4);
+ //#ifdef _Windows
+ std::transform (ext.begin (), ext.end (), ext.begin (), tolower);
+ //#endif
+ if (ext == ".blc" || ext == ".bas")
+ return true;
+ return false;
+}
+
+void openblassicprogram (std::ifstream & is, const std::string & name)
+{
+ const std::ios::openmode mode= std::ios::binary | std::ios::in;
+ is.open (name.c_str (), mode);
+ if (! is)
+ {
+ if (! hasblassicextension (name) )
+ {
+ std::string namex= name;
+ namex+= ".blc";
+ is.clear ();
+ is.open (namex.c_str (), mode);
+ if (! is.is_open () )
+ {
+ namex= name;
+ namex+= ".bas";
+ is.clear ();
+ is.open (namex.c_str (), mode);
+ if (! is.is_open () )
+ throw ErrFileNotFound;
+ }
+ }
+ else
+ throw ErrFileNotFound;
+ }
+}
+
+const char signature []=
+ { 'B', 'l', 'a', 's', 's', 'i', 'c', '\0' };
+const size_t lsig= sizeof (signature);
+
+bool isblassicbinary (std::istream & is)
+{
+ char magicstring [lsig];
+ is.read (magicstring, lsig);
+ if (! is || memcmp (magicstring, signature, lsig) != 0)
+ return false;
+ return true;
+}
+
+inline void checkread (std::istream & is, size_t readed)
+{
+ if (! is || size_t (is.gcount () ) != readed)
+ throw ErrFileRead;
+}
+
+typedef BlUint32 EndianType;
+const EndianType endian_mark= 0x12345678;
+
+class TextLoader {
+public:
+ TextLoader (ProgramImpl & program) :
+ program (program),
+ nextnumline (sysvar::get32 (sysvar::AutoInit) ),
+ incnumline (sysvar::get32 (sysvar::AutoInc) ),
+ maxnumline (BlMaxLineNumber - incnumline)
+ { }
+ bool directive (std::string str);
+ void load (std::istream & is);
+private:
+ ProgramImpl & program;
+ BlLineNumber nextnumline;
+ BlLineNumber incnumline;
+ BlLineNumber maxnumline;
+};
+
+bool TextLoader::directive (std::string str)
+{
+ TRACEFUNC (tr, "TextLoader::directive");
+
+ static std::string include ("include");
+ static const std::string::size_type linc= include.size ();
+ if (str.substr (1, linc) == include)
+ {
+ str.erase (0, linc + 1);
+ std::string::size_type l= str.find_first_not_of (" \t");
+ if (l > 0)
+ str.erase (0, l);
+ if (str.empty () )
+ return false;
+ if (str [0] == '"')
+ {
+ l= str.find ('"', 1);
+ str= str.substr (1, l - 1);
+ }
+ else if (str [0] == '<')
+ {
+ l= str.find ('>', 1);
+ str= str.substr (1, l - 1);
+ }
+ else
+ {
+ l= str.find_first_of (" \t");
+ if (l != std::string::npos)
+ str.erase (l);
+ }
+ TRMESSAGE (tr, str);
+ std::ifstream is;
+ openblassicprogram (is, str);
+ load (is);
+ return true;
+ }
+ return false;
+}
+
+void TextLoader::load (std::istream & is)
+{
+ TRACEFUNC (tr, "TextLoader::load");
+
+ bool blankcomment= sysvar::hasFlags2 (sysvar::BlankComment);
+
+ std::string str;
+ std::getline (is, str);
+ if (!str.empty () && str [0] == '#')
+ {
+ str.erase ();
+ std::getline (is, str);
+ }
+ CodeLine codeline;
+ bool fExhausted= false;
+ for ( ; is; std::getline (is, str) )
+ {
+ if (! str.empty () )
+ {
+ // EOF char on windows
+ if (str [0] == '\x1A')
+ break;
+
+ if (str [str.size () - 1] == '\r')
+ str.erase (str.size () - 1);
+ }
+
+ // Quick & dirty implemantation of #include
+ if (! str.empty () && str [0] == '#' && directive (str) )
+ continue;
+
+ if (str.empty () && blankcomment)
+ str= "'";
+
+ codeline.scan (str);
+ //if (codeline.number () == 0)
+ if (codeline.number () == LineDirectCommand)
+ {
+ if (fExhausted)
+ {
+ TRMESSAGE (tr, "Line exhausted");
+ throw ErrLineExhausted;
+ }
+ codeline.setnumber (nextnumline);
+ fExhausted= nextnumline > maxnumline;
+ nextnumline+= incnumline;
+ }
+ else
+ {
+ fExhausted= codeline.number () > maxnumline;
+ nextnumline= codeline.number () + incnumline;
+ }
+ if (codeline.length () > 0)
+ program.do_insert (codeline);
+ }
+}
+
+} // namespace
+
+void ProgramImpl::save (const std::string & name) const
+{
+ TRACEFUNC (tr, "ProgramImpl::save");
+
+ std::ofstream os (name.c_str (), std::ios::binary | std::ios::out);
+
+ // Blassic signature.
+ if (! os)
+ return;
+ os.write (signature, lsig);
+
+ // Endian mark.
+ ASSERT (sizeof (endian_mark) == 4);
+ os.write ( (char *) & endian_mark, 4);
+
+ // Size.
+ BlChar caux [4];
+ poke32 (caux, static_cast <BlUint32> (size) );
+ os.write ( (char *) caux, 4);
+
+ // Program body.
+ os.write ( (char *) program, size);
+ if (! os)
+ throw ErrFileWrite;
+}
+
+void ProgramImpl::loadtext (std::istream & is)
+{
+ TRACEFUNC (tr, "ProgramImpl::loadtext");
+
+ TextLoader loader (* this);
+ loader.load (is);
+}
+
+void ProgramImpl::loadbinary (std::istream & is)
+{
+ TRACEFUNC (tr, "ProgramImpl::loadbinary");
+
+ // This was intended to check endianess, but is
+ // currently unused.
+ EndianType endian_check;
+ ASSERT (sizeof endian_check == 4);
+
+ is.read ( (char *) & endian_check, 4);
+ checkread (is, 4);
+
+ #define SHOW_ENDIAN_CHECK
+ #ifdef SHOW_ENDIAN_CHECK
+ if (showdebuginfo () )
+ {
+ std::ostringstream oss;
+ oss << "Endian check: " << std::hex << endian_check;
+ cerr << oss.str () << endl;
+ }
+ #ifndef NDEBUG
+ {
+ std::ostringstream oss;
+ oss << "Endian check: " << std::hex << endian_check;
+ TRMESSAGE (tr, oss.str () );
+ }
+ #endif
+ #endif
+
+ // Get the program size.
+ BlChar caux [4];
+ is.read ( (char *) caux, 4);
+ checkread (is, 4);
+ unsigned long newsize= peek32 (caux);
+
+ // Get program body.
+ if (newsize > 0)
+ {
+ //size_t newblock= blockrounded (newsize);
+ //util::auto_alloc <BlChar> newprog (newblock);
+ //is.read (reinterpret_cast <char *> (newprog.data () ),
+ // newsize);
+
+ ProgramImpl other;
+ other.resize (newsize);
+ is.read (reinterpret_cast <char *> (other.program), newsize);
+
+ checkread (is, newsize);
+
+ //renew ();
+ //program= newprog;
+ //newprog.release ();
+ //size= newsize;
+
+ clear_cache ();
+ transfer_content (other);
+ }
+ else
+ renew ();
+}
+
+void ProgramImpl::load (const std::string & name)
+{
+ TRACEFUNC (tr, "ProgramImpl::load (const string &)");
+
+ std::ifstream (is);
+ openblassicprogram (is, name);
+
+ if (! isblassicbinary (is) )
+ {
+ renew ();
+ is.clear ();
+ is.seekg (0);
+ loadtext (is);
+ }
+ else
+ {
+ loadbinary (is);
+ }
+}
+
+void ProgramImpl::load (std::istream & is)
+{
+ TRACEFUNC (tr, "ProgramImpl::load (istream &)");
+
+ renew ();
+ loadtext (is);
+}
+
+void ProgramImpl::merge (const std::string & name,
+ BlLineNumber inidel,
+ BlLineNumber enddel)
+{
+ TRACEFUNC (tr, "ProgramImpl::merge");
+
+ bool dellines= true;
+ if (inidel == LineNoDelete)
+ {
+ if (enddel != LineNoDelete)
+ {
+ ASSERT (false);
+ throw ErrBlassicInternal;
+ }
+ dellines= false;
+ }
+ else
+ {
+ if (enddel == LineNoDelete)
+ {
+ ASSERT (false);
+ throw ErrBlassicInternal;
+ }
+ if ( (inidel > BlMaxLineNumber &&
+ inidel != LineBeginProgram) ||
+ (enddel > BlMaxLineNumber &&
+ enddel != LineEndProgram) )
+ {
+ ASSERT (false);
+ throw ErrBlassicInternal;
+ }
+ }
+
+ clear_cache ();
+
+ #if 0
+ std::ifstream is;
+ openblassicprogram (is, name);
+
+ ProgramImpl inload;
+ if (! isblassicbinary (is) )
+ {
+ is.clear ();
+ is.seekg (0);
+ inload.loadtext (is);
+ }
+ else
+ {
+ inload.loadbinary (is);
+ }
+ is.close ();
+ #else
+
+ ProgramImpl inload;
+ inload.load (name);
+
+ #endif
+
+ if (dellines)
+ deletelines (inidel, enddel);
+
+ for (CodeLine codeline= inload.getfirstline ();
+ //codeline.number () != 0;
+ codeline.number () != LineEndProgram;
+ //codeline= inload.getnextline (codeline)
+ inload.getnextline (codeline)
+ )
+ {
+ do_insert (codeline);
+ }
+}
+
+namespace {
+
+bool iscodewithnumber (BlCode code)
+{
+ return (code == keyGOTO || code == keyGOSUB || code == keyRUN ||
+ code == keyRESTORE || code == keyRESUME ||
+ code == keyDELETE ||
+ code == keyLIST || code == keyLLIST ||
+ code == keyEDIT ||
+ code == keyTHEN || code == keyELSE);
+}
+
+void changenumber (BlChar * pos, const MapLine & mapline)
+{
+ BlLineNumber old= getLineNumber (pos);
+ //cerr << " Find " << old << flush;
+ MapLine::const_iterator it= mapline.find (old);
+ if (it != mapline.end () )
+ {
+ //cerr << " Changed " << it->second << flush;
+ setLineNumber (pos, it->second);
+ }
+}
+
+} // namespace
+
+void ProgramImpl::changeline (Position pos, const MapLine & mapline)
+{
+ const BlLineLength l= sizeline (pos);
+ BlChar * s= linecontent (pos);
+ BlLineLength p= 0;
+ BlChar c;
+ while (p < l)
+ {
+ c= s [p];
+ if (iskey (c) )
+ {
+ BlCode code= BlCode ( (BlCode (c) << 8 ) ) |
+ BlCode (s [p+1] );
+ p+= 2;
+ //cerr << "Key " << decodekeyword (code) << flush;
+ if (iscodewithnumber (code) )
+ {
+ //cerr << " analyzing" << flush;
+ for (;;)
+ {
+ while (p < l && isspace (s [p] ) )
+ ++p;
+ if (p >= l)
+ break;
+ c= s [p];
+ if (c == INTEGER_PREFIX)
+ {
+ BlChar * const pos= s + p + 1;
+ changenumber (pos, mapline);
+ p+= 1 + sizeof (BlInteger);
+ }
+ else if (c == ':')
+ {
+ ++p;
+ break;
+ }
+ else if (c == '"')
+ {
+ while (s [++p] != '\0')
+ continue;
+ ++p;
+ }
+ else if (iskey (c) )
+ p+= 2;
+ else if (c == '\'')
+ {
+ p= l;
+ break;
+ }
+ else ++p;
+ }
+ //cerr << endl;
+ }
+ //else cerr << endl;
+ }
+ else if (c == INTEGER_PREFIX)
+ p+= 1 + sizeof (BlInteger);
+ else if (c == '"')
+ {
+ while (s [++p] != '\0')
+ continue;
+ ++p;
+ }
+ else if (c == '\'')
+ break;
+ else ++p;
+ }
+}
+
+void ProgramImpl::renum (BlLineNumber blnNew, BlLineNumber blnOld,
+ BlLineNumber blnInc, BlLineNumber blnStop)
+{
+ TRACEFUNC (tr, "ProgramImpl::renum");
+ TRMESSAGE (tr, "args: " + to_string (blnNew) + ", " +
+ to_string (blnOld) + ", " +
+ to_string (blnInc) + ", " +
+ to_string (blnStop) );
+
+ if (blnNew > BlMaxLineNumber || blnInc > BlMaxLineNumber ||
+ (blnOld > BlMaxLineNumber && blnOld != LineBeginProgram) ||
+ (blnStop > BlMaxLineNumber && blnStop != LineEndProgram) )
+ {
+ ASSERT (false);
+ throw ErrBlassicInternal;
+ }
+
+ bool showinfo= showdebuginfo ();
+
+ if (size == 0)
+ {
+ if (showinfo)
+ cerr << "Trying to renum but program is empty" <<
+ endl;
+ return;
+ }
+ if (blnInc == 0)
+ throw ErrImproperArgument;
+
+ // Find first line to renum.
+
+ Position pos= 0;
+ BlLineNumber previous= LineBeginProgram;
+ if (blnOld != LineBeginProgram)
+ {
+ TRMESSAGE (tr, "Searching for " + to_string (blnOld) );
+ while (pos < size && numline (pos) < blnOld)
+ {
+ previous= numline (pos);
+ //cerr << "Skipping line " << previous << endl;
+ pos= nextline (pos);
+ }
+ TRMESSAGE (tr, "Found " + to_string (numline (pos) ) );
+ }
+ if (previous != LineBeginProgram && previous >= blnNew)
+ throw ErrImproperArgument;
+
+ // Evaluate changes required.
+
+ MapLine mapline;
+ BlLineNumber actual;
+ BlLineNumber blnMax= BlMaxLineNumber - blnInc;
+ bool overflow= false;
+ for ( ; pos < size; pos= nextline (pos) )
+ {
+ actual= numline (pos);
+ if (actual >= blnStop)
+ {
+ TRMESSAGE (tr, "Stop line reached");
+ if (previous >= blnStop)
+ {
+ if (showinfo)
+ cerr << "Renumbered line " <<
+ previous <<
+ " is greater than " <<
+ blnStop <<
+ endl;
+ throw ErrImproperArgument;
+ }
+ break;
+ }
+ if (actual != blnNew)
+ {
+ if (overflow)
+ throw ErrLineExhausted;
+ TRMESSAGE (tr, "changing " + to_string (actual) +
+ " by " + to_string (blnNew) );
+ mapline [actual]= blnNew;
+ }
+ previous= blnNew;
+ if (blnNew > blnMax)
+ overflow= true;
+ else
+ blnNew+= blnInc;
+ }
+
+ if (mapline.empty () )
+ {
+ if (showinfo)
+ cerr << "renum: no changes needed" << endl;
+ return;
+ }
+
+ // Do the changes.
+
+ clear_cache ();
+
+ MapLine::iterator it, mapend= mapline.end ();
+ for (pos= 0; pos < size; pos= nextline (pos) )
+ {
+ actual= numline (pos);
+ it= mapline.find (actual);
+ if (it != mapend)
+ {
+ //cerr << "Changing line " << actual <<
+ // " by " << it->second << endl;
+ setLineNumber (program + pos, it->second);
+ }
+ changeline (pos, mapline);
+ }
+}
+
+// End of program.cpp
Un proyecto texto-plano.xyz