| #ifndef __RUNTIME_H␊ |
| #define __RUNTIME_H␊ |
| ␊ |
| ␊ |
| #include "common.h"␊ |
| ␊ |
| ␊ |
| // --- charset ------------------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| class charset␊ |
| {␊ |
| public:␊ |
| typedef uinteger word;␊ |
| enum␊ |
| {␊ |
| BITS = 256,␊ |
| BYTES = BITS / 8,␊ |
| WORDS = BYTES / int(sizeof(word))␊ |
| };␊ |
| ␊ |
| protected:␊ |
| typedef uint8_t uchar;␊ |
| ␊ |
| uchar data[BYTES];␊ |
| ␊ |
| public:␊ |
| charset() throw() { clear(); }␊ |
| charset(const charset& s) throw() { assign(s); }␊ |
| charset(const char* setinit) throw() { assign(setinit); }␊ |
| ␊ |
| void assign(const charset& s) throw();␊ |
| void assign(const char* setinit) throw();␊ |
| bool empty() const throw();␊ |
| void clear() throw() { memset(data, 0, BYTES); }␊ |
| void fill() { memset(data, -1, BYTES); }␊ |
| void include(int b) throw() { data[uchar(b) / 8] |= uchar(1 << (uchar(b) % 8)); }␊ |
| void include(int min, int max) throw(); ␊ |
| void exclude(int b) { data[uchar(b) / 8] &= uchar(~(1 << (uchar(b) % 8))); }␊ |
| void unite(const charset& s);␊ |
| void subtract(const charset& s);␊ |
| void intersect(const charset& s);␊ |
| void invert();␊ |
| bool contains(int b) const { return (data[uchar(b) / 8] & (1 << (uchar(b) % 8))) != 0; }␊ |
| bool compare(const charset& s) const { return memcmp(data, s.data, BYTES); }␊ |
| bool eq(const charset& s) const { return compare(s) == 0; }␊ |
| bool le(const charset& s) const;␊ |
| ␊ |
| charset& operator= (const charset& s) { assign(s); return *this; }␊ |
| charset& operator+= (const charset& s) { unite(s); return *this; }␊ |
| charset& operator+= (int b) { include(b); return *this; }␊ |
| charset operator+ (const charset& s) const { charset t = *this; return t += s; }␊ |
| charset operator+ (int b) const { charset t = *this; return t += b; }␊ |
| charset& operator-= (const charset& s) { subtract(s); return *this; }␊ |
| charset& operator-= (int b) { exclude(b); return *this; }␊ |
| charset operator- (const charset& s) const { charset t = *this; return t -= s; }␊ |
| charset operator- (int b) const { charset t = *this; return t -= b; }␊ |
| charset& operator*= (const charset& s) { intersect(s); return *this; }␊ |
| charset operator* (const charset& s) const { charset t = *this; return t *= s; }␊ |
| charset operator~ () const { charset t = *this; t.invert(); return t; }␊ |
| bool operator== (const charset& s) const { return compare(s) == 0; }␊ |
| bool operator!= (const charset& s) const { return compare(s) != 0; }␊ |
| bool operator<= (const charset& s) const { return le(s); }␊ |
| bool operator>= (const charset& s) const { return s.le(*this); }␊ |
| bool operator[] (int b) const { return contains(b); }␊ |
| };␊ |
| ␊ |
| ␊ |
| // --- object -------------------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| // object: reference-counted memory block with a virtual destructor␊ |
| ␊ |
| class object␊ |
| {␊ |
| object(const object&) throw();␊ |
| void operator= (const object&) throw();␊ |
| ␊ |
| protected:␊ |
| atomicint _refcount;␊ |
| ␊ |
| bool _release();␊ |
| ␊ |
| public:␊ |
| ␊ |
| void _mkstatic()␊ |
| {␊ |
| // Prevent this object from being free'd by release() and also from␊ |
| // being counted against memory leaks.␊ |
| _refcount = 1;␊ |
| #ifdef DEBUG␊ |
| pdecrement(&object::allocated);␊ |
| #endif␊ |
| }␊ |
| ␊ |
| void* operator new(size_t self);␊ |
| void* operator new(size_t self, memint extra);␊ |
| void operator delete(void*);␊ |
| ␊ |
| // Dirty trick that duplicates an object and hopefully preserves the␊ |
| // dynamic type (actually the VMT). Only 'self' bytes is copied; 'extra'␊ |
| // remains uninitialized.␊ |
| object* _dup(size_t self, memint extra);␊ |
| ␊ |
| void _assignto(object*& p) throw();␊ |
| static object* reallocate(object* p, size_t self, memint extra);␊ |
| ␊ |
| static atomicint allocated; // used only in DEBUG mode␊ |
| ␊ |
| bool isunique() const { return _refcount == 1; }␊ |
| atomicint release() throw();␊ |
| object* grab() throw() { pincrement(&_refcount); return this; }␊ |
| template <class T>␊ |
| T* grab() { object::grab(); return (T*)(this); }␊ |
| template <class T>␊ |
| void assignto(T*& p) throw() { _assignto((object*&)p); }␊ |
| ␊ |
| object() throw(): _refcount(0) { }␊ |
| virtual ~object() throw();␊ |
| };␊ |
| ␊ |
| ␊ |
| void _del_obj(object* o);␊ |
| ␊ |
| ␊ |
| #ifdef SHN_FASTER␊ |
| inline atomicint object::release()␊ |
| {␊ |
| if (this == NULL)␊ |
| return 0;␊ |
| assert(_refcount > 0);␊ |
| atomicint r = pdecrement(&_refcount);␊ |
| if (r == 0)␊ |
| _del_obj(this);␊ |
| return r;␊ |
| }␊ |
| #endif␊ |
| ␊ |
| ␊ |
| // objptr: "smart" pointer␊ |
| ␊ |
| template <class T>␊ |
| class objptr␊ |
| {␊ |
| protected:␊ |
| T* obj;␊ |
| public:␊ |
| objptr() : obj(NULL) { }␊ |
| objptr(const objptr& p) : obj(p.obj) { if (obj) obj->grab(); }␊ |
| objptr(T* o) : obj(o) { if (o) o->grab(); }␊ |
| ~objptr() { obj->release(); }␊ |
| void clear() { obj->release(); obj = NULL; }␊ |
| bool empty() const { return obj == NULL; }␊ |
| bool isunique() const { return empty() || obj->isunique(); }␊ |
| bool operator== (const objptr& p) { return obj == p.obj; }␊ |
| bool operator!= (const objptr& p) { return obj != p.obj; }␊ |
| bool operator== (T* o) { return obj == o; }␊ |
| bool operator!= (T* o) { return obj != o; }␊ |
| void operator= (const objptr& p) throw() { p.obj->assignto(obj); }␊ |
| void operator= (T* o) throw() { o->assignto(obj); }␊ |
| T& operator* () { return *obj; }␊ |
| const T& operator* () const { return *obj; }␊ |
| T* operator-> () const { return obj; }␊ |
| operator T*() const { return obj; }␊ |
| T* get() const { return obj; }␊ |
| ␊ |
| // Internal␊ |
| void _init() { obj = NULL; }␊ |
| void _init(T* o) { obj = o; if (o) o->grab(); }␊ |
| void _fin() { obj->release(); }␊ |
| void _reinit(T* o) { obj = o; }␊ |
| };␊ |
| ␊ |
| ␊ |
| class Type; // defined in typesys.h␊ |
| class fifo;␊ |
| ␊ |
| // rtobject: a ref-counted object with runtime type information␊ |
| ␊ |
| class rtobject: public object␊ |
| {␊ |
| private:␊ |
| Type* _type;␊ |
| public:␊ |
| rtobject(Type* t) throw(): _type(t) { }␊ |
| ~rtobject() throw();␊ |
| Type* getType() const { return _type; }␊ |
| void setType(Type* t) { assert(_type == NULL); _type = t; }␊ |
| void clearType() throw() { _type = NULL; }␊ |
| virtual bool empty() const = 0;␊ |
| virtual void dump(fifo&) const = 0;␊ |
| };␊ |
| ␊ |
| ␊ |
| // --- container ----------------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| // container: resizable ref-counted container of POD data; also base for␊ |
| // non-POD containers that override finalize() and copy() (and the dtor)␊ |
| ␊ |
| class container: public object␊ |
| {␊ |
| protected:␊ |
| memint _capacity;␊ |
| memint _size;␊ |
| // char _data[0];␊ |
| ␊ |
| public:␊ |
| // Note: allocate() creates an instance of 'container' while reallocate()␊ |
| // never does that and thus it can be used for descendant classes too.␊ |
| static container* allocate(memint cap, memint siz) throw(); // (*)␊ |
| static container* reallocate(container* p, memint newsize);␊ |
| ␊ |
| // Creates a duplicate of a given container without copying the data;␊ |
| // leaves actual copying to the virtual method copy()␊ |
| container* _dup(memint cap, memint siz);␊ |
| ␊ |
| // TODO: compact()␊ |
| ␊ |
| static memint _calc_prealloc(memint);␊ |
| container(memint cap, memint siz) throw()␊ |
| : object(), _capacity(cap), _size(siz) { }␊ |
| ␊ |
| static void overflow();␊ |
| static void idxerr();␊ |
| static void keyerr();␊ |
| ␊ |
| ~container() throw();␊ |
| virtual void finalize(void*, memint) throw();␊ |
| virtual void copy(void* dest, const void* src, memint) throw();␊ |
| ␊ |
| char* data() const { return (char*)(this + 1); }␊ |
| char* data(memint i) const { return data() + i; }␊ |
| char* end() const { return data(_size); }␊ |
| static container* cont(char* d) { return ((container*)d) - 1; }␊ |
| memint size() const { return _size; }␊ |
| void set_size(memint newsize)␊ |
| { assert(newsize > 0 && newsize <= _capacity); _size = newsize; }␊ |
| void dec_size() { assert(_size > 0); _size--; }␊ |
| memint capacity() const { return _capacity; }␊ |
| };␊ |
| ␊ |
| ␊ |
| // --- bytevec ------------------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| // bytevec: byte vector, implements copy-on-write; the structure itself␊ |
| // occupies only sizeof(void*); base class for strings and vectors␊ |
| ␊ |
| class bytevec␊ |
| {␊ |
| friend class variant;␊ |
| friend class CodeGen;␊ |
| ␊ |
| friend void test_bytevec();␊ |
| friend void test_podvec();␊ |
| ␊ |
| protected:␊ |
| objptr<container> obj;␊ |
| ␊ |
| typedef container* (*alloc_func)(memint cap, memint siz);␊ |
| ␊ |
| void chkidx(memint i) const { if (umemint(i) >= umemint(size())) container::idxerr(); }␊ |
| void chkidxa(memint i) const { if (umemint(i) > umemint(size())) container::idxerr(); }␊ |
| static void chknonneg(memint v) { if (v < 0) container::overflow(); } ␊ |
| void chknz() const { if (empty()) container::idxerr(); }␊ |
| bool _isunique() const { return empty() || obj->isunique(); }␊ |
| void _dounique();␊ |
| char* mkunique() { if (!obj->isunique()) _dounique(); return obj->data(); }␊ |
| char* _init(memint len) throw(); // (*)␊ |
| void _init(memint len, char fill) throw(); // (*)␊ |
| void _init(const char*, memint) throw(); // (*)␊ |
| void _init(const bytevec& v) throw() { obj._init(v.obj); }␊ |
| char* _init(memint pos, memint len, alloc_func) throw();␊ |
| void _init(const bytevec& v, memint pos, memint len, alloc_func) throw();␊ |
| ␊ |
| char* _insert(memint pos, memint len, alloc_func);␊ |
| void _insert(memint pos, const bytevec&, alloc_func);␊ |
| char* _append(memint len, alloc_func);␊ |
| void _erase(memint pos, memint len);␊ |
| void _pop(memint len);␊ |
| char* _resize(memint newsize, alloc_func);␊ |
| ␊ |
| public:␊ |
| bytevec() throw(): obj() { }␊ |
| bytevec(const bytevec& v) throw() { _init(v); }␊ |
| bytevec(const char* buf, memint len) throw() { _init(buf, len); } // (*)␊ |
| bytevec(memint len, char fill) throw() { _init(len, fill); } // (*)␊ |
| ~bytevec() throw() { }␊ |
| ␊ |
| void operator= (const bytevec& v) throw() { obj = v.obj; }␊ |
| bool operator== (const bytevec& v) const { return obj == v.obj; }␊ |
| void assign(const char*, memint);␊ |
| void clear();␊ |
| ␊ |
| bool empty() const { return obj.empty(); }␊ |
| memint size() const { return empty() ? 0 : obj->size(); }␊ |
| memint capacity() const { return empty() ? 0 : obj->capacity(); }␊ |
| const char* data() const { return obj->data(); }␊ |
| const char* data(memint i) const { return obj->data(i); }␊ |
| const char* at(memint i) const { chkidx(i); return obj->data(i); }␊ |
| char* atw(memint i) { chkidx(i); return mkunique() + i; }␊ |
| const char* begin() const { return empty() ? NULL : obj->data(); }␊ |
| const char* end() const { return empty() ? NULL : obj->end(); }␊ |
| const char* back(memint i) const { chkidxa(i); return obj->end() - i; }␊ |
| const char* back() const { return back(1); }␊ |
| char* backw(memint i) { chkidxa(i); return obj->end() - i; }␊ |
| char* backw() { return backw(1); }␊ |
| ␊ |
| void insert(memint pos, const char* buf, memint len); // (*)␊ |
| void insert(memint pos, const bytevec& s) // (*)␊ |
| { _insert(pos, s, container::allocate); }␊ |
| void append(const char* buf, memint len); // (*)␊ |
| void append(const bytevec& s);␊ |
| void erase(memint pos, memint len);␊ |
| void pop(memint len) { if (len > 0) _pop(len); }␊ |
| char* resize(memint newsize) { return _resize(newsize, container::allocate); } // (*)␊ |
| void resize(memint, char); // (*)␊ |
| ␊ |
| // Mostly used internally␊ |
| template <class T>␊ |
| const T* data(memint i) const { return (T*)data(i * sizeof(T)); }␊ |
| template <class T>␊ |
| const T* at(memint i) const { return (T*)at(i * sizeof(T)); }␊ |
| template <class T>␊ |
| T* atw(memint i) { return (T*)atw(i * sizeof(T)); }␊ |
| template <class T>␊ |
| const T* begin() const { return (T*)begin(); }␊ |
| template <class T>␊ |
| const T* end() const { return (T*)end(); }␊ |
| template <class T>␊ |
| const T* back() const { return (T*)back(sizeof(T)); }␊ |
| template <class T>␊ |
| const T* back(memint i) const { return (T*)back(sizeof(T) * i); }␊ |
| template <class T>␊ |
| T* backw() { return (T*)backw(sizeof(T)); }␊ |
| template <class T>␊ |
| T* backw(memint i) { return (T*)backw(sizeof(T) * i); }␊ |
| template <class T>␊ |
| void pop_back() { pop(sizeof(T)); }␊ |
| template <class T>␊ |
| void pop_back(T& t) { t = *back<T>(); pop_back<T>(); }␊ |
| };␊ |
| ␊ |
| // (*) -- works only with the POD container; should be overridden or hidden in␊ |
| // descendant classes. The rest works magically on any descendant of␊ |
| // 'container'. Pure magic. I like this!␊ |
| ␊ |
| ␊ |
| // --- str ----------------------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| class str: public bytevec␊ |
| {␊ |
| protected:␊ |
| friend void test_string();␊ |
| ␊ |
| void _init(const char*) throw();␊ |
| void _init(char c) throw() { bytevec::_init(&c, 1); }␊ |
| ␊ |
| public:␊ |
| str() throw(): bytevec() { }␊ |
| str(const str& s)throw(): bytevec(s) { }␊ |
| str(const char* buf, memint len) throw(): bytevec(buf, len) { }␊ |
| str(const char* s) throw() { _init(s); }␊ |
| str(memint len, char fill) throw() { bytevec::_init(len, fill); }␊ |
| str(char c) throw() { _init(c); }␊ |
| ␊ |
| const char* c_str(); // can actually modify the object␊ |
| void push_back(char c) { *_append(1, container::allocate) = c; }␊ |
| void push_front(char c) { *_insert(0, 1, container::allocate) = c; }␊ |
| char operator[] (memint i) const { return *data(i); }␊ |
| char at(memint i) const { return *bytevec::at(i); }␊ |
| char back() const { return *bytevec::back(); }␊ |
| void replace(memint pos, char c) { *bytevec::atw(pos) = c; }␊ |
| void insert(memint pos, char c) { *_insert(pos, 1, container::allocate) = c; }␊ |
| void insert(memint pos, const str& s) { bytevec::insert(pos, s); }␊ |
| void insert(memint pos, const char* s);␊ |
| void operator= (const char* c);␊ |
| void operator= (char c);␊ |
| void replace(memint pos, memint len, const str& s);␊ |
| ␊ |
| enum { npos = -1 };␊ |
| memint find(char c) const;␊ |
| memint rfind(char c) const;␊ |
| ␊ |
| memint compare(const char*, memint) const;␊ |
| memint compare(const str& s) const { return compare(s.data(), s.size()); }␊ |
| bool operator== (const char* s) const;␊ |
| bool operator== (const str& s) const { return compare(s.data(), s.size()) == 0; }␊ |
| bool operator== (char c) const { return size() == 1 && *data() == c; }␊ |
| bool operator!= (const char* s) const { return !(*this == s); }␊ |
| bool operator!= (const str& s) const { return !(*this == s); }␊ |
| bool operator!= (char c) const { return !(*this == c); }␊ |
| ␊ |
| void operator+= (const char* s);␊ |
| void operator+= (const str& s) { append(s); }␊ |
| void operator+= (char c) { push_back(c); }␊ |
| str operator+ (const char* s) const { str r = *this; r += s; return r; }␊ |
| str operator+ (const str& s) const { str r = *this; r += s; return r; }␊ |
| str operator+ (char c) const { str r = *this; r += c; return r; }␊ |
| str substr(memint pos, memint len) const;␊ |
| str substr(memint pos) const;␊ |
| };␊ |
| ␊ |
| ␊ |
| inline str operator+ (const char* s1, const str& s2)␊ |
| { str r = s1; r += s2; return r; }␊ |
| ␊ |
| inline str operator+ (char c, const str& s2)␊ |
| { str r = c; r += s2; return r; }␊ |
| ␊ |
| ␊ |
| // --- string utilities ---------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| str _to_string(large value, int base, int width, char fill);␊ |
| str _to_string(large);␊ |
| template<class T>␊ |
| inline str to_string(const T& value, int base, int width = 0, char fill = '0')␊ |
| { return _to_string(large(value), base, width, fill); }␊ |
| template<class T>␊ |
| inline str to_string(const T& value)␊ |
| { return _to_string(large(value)); }␊ |
| ␊ |
| ularge from_string(const char*, bool* error, bool* overflow, int base = 10);␊ |
| ␊ |
| str remove_filename_path(const str&);␊ |
| str remove_filename_ext(const str&);␊ |
| str to_printable(char);␊ |
| str to_printable(const str&);␊ |
| str to_quoted(char c);␊ |
| str to_quoted(const str&);␊ |
| str to_displayable(const str&); // shortens to 40 chars + "..."␊ |
| ␊ |
| ␊ |
| // --- podvec -------------------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| template <class T>␊ |
| struct comparator␊ |
| { memint operator() (const T& a, const T& b) { return memint(a - b); } };␊ |
| ␊ |
| template <>␊ |
| struct comparator<str>␊ |
| { memint operator() (const str& a, const str& b) { return a.compare(b); } };␊ |
| ␊ |
| template <>␊ |
| struct comparator<const char*>␊ |
| { memint operator() (const char* a, const char* b) { return strcmp(a, b); } };␊ |
| ␊ |
| ␊ |
| // Vector template for POD elements (int, pointers, et al). Used internally␊ |
| // by the compiler itself. Also podvec is a basis for the universal vector.␊ |
| // This hopefully generates minimal static code.␊ |
| ␊ |
| template <class T>␊ |
| class podvec: protected bytevec␊ |
| {␊ |
| friend void test_podvec();␊ |
| ␊ |
| protected:␊ |
| enum { Tsize = int(sizeof(T)) };␊ |
| typedef bytevec parent;␊ |
| ␊ |
| public:␊ |
| podvec() throw(): parent() { }␊ |
| podvec(const podvec& v) throw(): parent(v) { }␊ |
| ␊ |
| bool empty() const { return parent::empty(); }␊ |
| memint size() const { return parent::size() / Tsize; }␊ |
| bool operator== (const podvec& v) const { return parent::operator==(v); }␊ |
| const T& operator[] (memint i) const { return *parent::data<T>(i); }␊ |
| const T& at(memint i) const { return *parent::at<T>(i); }␊ |
| T& atw(memint i) { return *parent::atw<T>(i); }␊ |
| const T& back() const { return *parent::back<T>(); }␊ |
| const T& back(memint i) const { return *parent::back<T>(i); }␊ |
| T& backw() { return *parent::backw<T>(); }␊ |
| T& backw(memint i) { return *parent::backw<T>(i); }␊ |
| const T* begin() const { return parent::begin<T>(); }␊ |
| const T* end() const { return parent::end<T>(); }␊ |
| void clear() { parent::clear(); }␊ |
| void operator= (const podvec& v) throw() { parent::operator= (v); }␊ |
| void push_back(const T& t) { new(_append(Tsize, container::allocate)) T(t); } // (*)␊ |
| void pop_back() { parent::pop_back<T>(); }␊ |
| void pop_back(T& t) { parent::pop_back<T>(t); }␊ |
| void append(const podvec& v) { parent::append(v); }␊ |
| void insert(memint pos, const T& t) { new(parent::_insert(pos * Tsize, Tsize, container::allocate)) T(t); } // (*)␊ |
| void insert(memint pos, const podvec& v) { parent::_insert(pos * Tsize, v, container::allocate); } // (*)␊ |
| void replace(memint pos, const T& t) { *parent::atw<T>(pos) = t; }␊ |
| void erase(memint pos, memint len) { parent::_erase(pos * Tsize, len * Tsize); }␊ |
| void erase(memint pos) { parent::_erase(pos * Tsize, Tsize); }␊ |
| ␊ |
| // If you keep the vector sorted, the following will provide set-like␊ |
| // functionality:␊ |
| bool find(const T& item) const␊ |
| {␊ |
| memint index;␊ |
| return bsearch(item, index);␊ |
| }␊ |
| ␊ |
| bool find_insert(const T& item) // (*)␊ |
| {␊ |
| memint index;␊ |
| if (!bsearch(item, index))␊ |
| {␊ |
| insert(index, item);␊ |
| return true;␊ |
| }␊ |
| else␊ |
| return false;␊ |
| }␊ |
| ␊ |
| void find_erase(const T& item)␊ |
| {␊ |
| memint index;␊ |
| if (bsearch(item, index))␊ |
| erase(index);␊ |
| else␊ |
| container::keyerr();␊ |
| }␊ |
| ␊ |
| // Internal method, but should be public for technical reasons␊ |
| bool bsearch(const T& elem, memint& idx) const␊ |
| {␊ |
| comparator<T> comp;␊ |
| idx = 0;␊ |
| memint low = 0;␊ |
| memint high = size() - 1;␊ |
| while (low <= high) ␊ |
| {␊ |
| idx = (low + high) / 2;␊ |
| memint c = comp(operator[](idx), elem);␊ |
| if (c < 0)␊ |
| low = idx + 1;␊ |
| else if (c > 0)␊ |
| high = idx - 1;␊ |
| else␊ |
| return true;␊ |
| }␊ |
| idx = low;␊ |
| return false;␊ |
| }␊ |
| };␊ |
| ␊ |
| ␊ |
| // --- vector -------------------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| template <class T>␊ |
| class vector: public podvec<T>␊ |
| {␊ |
| protected:␊ |
| enum { Tsize = int(sizeof(T)) };␊ |
| typedef podvec<T> parent;␊ |
| typedef T* Tptr;␊ |
| typedef Tptr& Tref;␊ |
| ␊ |
| class cont: public container␊ |
| {␊ |
| protected:␊ |
| ␊ |
| void finalize(void* p, memint len) throw()␊ |
| {␊ |
| (char*&)p += len - Tsize;␊ |
| for ( ; len; len -= Tsize, Tref(p)--)␊ |
| Tptr(p)->~T();␊ |
| }␊ |
| ␊ |
| void copy(void* dest, const void* src, memint len) throw()␊ |
| {␊ |
| for ( ; len; len -= Tsize, Tref(dest)++, Tref(src)++)␊ |
| new(dest) T(*Tptr(src));␊ |
| }␊ |
| ␊ |
| cont(memint cap, memint siz) throw(): container(cap, siz) { }␊ |
| ␊ |
| public:␊ |
| static container* allocate(memint cap, memint siz) throw()␊ |
| { return new(cap) cont(cap, siz); }␊ |
| ␊ |
| ~cont() throw()␊ |
| { if (_size) { finalize(data(), _size); _size = 0; } }␊ |
| };␊ |
| ␊ |
| vector(const vector& v, memint pos, memint len)␊ |
| { parent::_init(v, pos * Tsize, len * Tsize, cont::allocate); }␊ |
| ␊ |
| public:␊ |
| vector() throw(): parent() { }␊ |
| ␊ |
| // Override stuff that requires allocation of 'vector::cont'␊ |
| void insert(memint pos, const T& t)␊ |
| { new(bytevec::_insert(pos * Tsize, Tsize, cont::allocate)) T(t); }␊ |
| void insert(memint pos, const vector& v)␊ |
| { bytevec::_insert(pos * Tsize, v, cont::allocate); }␊ |
| void push_back(const T& t)␊ |
| { new(bytevec::_append(Tsize, cont::allocate)) T(t); }␊ |
| void resize(memint); // not implemented␊ |
| ␊ |
| void replace(memint pos, memint len, const vector& v)␊ |
| {␊ |
| parent::erase(pos, len);␊ |
| insert(pos, v);␊ |
| }␊ |
| ␊ |
| void grow(memint extra_items)␊ |
| {␊ |
| memint extra_mem = extra_items * Tsize;␊ |
| char* p = bytevec::_resize(bytevec::size() + extra_mem, cont::allocate);␊ |
| memset(p, 0, extra_mem);␊ |
| }␊ |
| ␊ |
| vector subvec(memint pos, memint len) const␊ |
| {␊ |
| if (pos == 0 && len == parent::size())␊ |
| return *this;␊ |
| return vector(*this, pos, len);␊ |
| }␊ |
| ␊ |
| // Give a chance to alternative constructors, e.g. str can be constructed␊ |
| // from (const char*). Without these templates below temp objects are␊ |
| // created and then copied into the vector. Though these are somewhat␊ |
| // dangerous too.␊ |
| template <class U>␊ |
| void insert(memint pos, const U& u)␊ |
| { new(bytevec::_insert(pos * Tsize, Tsize, cont::allocate)) T(u); }␊ |
| template <class U>␊ |
| void push_back(const U& u)␊ |
| { new(bytevec::_append(Tsize, cont::allocate)) T(u); }␊ |
| template <class U>␊ |
| void replace(memint i, const U& u)␊ |
| { parent::atw(i) = u; }␊ |
| ␊ |
| bool find_insert(const T& item)␊ |
| {␊ |
| if (parent::empty())␊ |
| { push_back(item); return true; }␊ |
| else␊ |
| return parent::find_insert(item);␊ |
| }␊ |
| };␊ |
| ␊ |
| ␊ |
| // This is a clone of vector<> but declared separately for overloaded variant␊ |
| // constructors. (Is there a better way?)␊ |
| template <class T>␊ |
| class set: public vector<T>␊ |
| {␊ |
| protected:␊ |
| enum { Tsize = int(sizeof(T)) };␊ |
| typedef vector<T> parent;␊ |
| typedef T* Tptr;␊ |
| typedef Tptr& Tref;␊ |
| public:␊ |
| set() throw(): parent() { }␊ |
| };␊ |
| ␊ |
| ␊ |
| // --- dict ---------------------------------------------------------------- //␊ |
| ␊ |
| // dict: internally a dict variable is a pointer to dictobj which in its turn␊ |
| // contains two separate vectors for keys and for values. This way we ␊ |
| // (1) re-use the existing instances of certain templates␊ |
| // (2) more importantly, we simplify methods of getting the keys or values ␊ |
| // as vectors and reusing them on the ref-count basis␊ |
| ␊ |
| template <class Tkey, class Tval>␊ |
| class dict␊ |
| {␊ |
| friend class variant;␊ |
| ␊ |
| protected:␊ |
| ␊ |
| void chkidx(memint i) const { if (umemint(i) >= umemint(size())) container::idxerr(); }␊ |
| ␊ |
| class dictobj: public object␊ |
| {␊ |
| public:␊ |
| vector<Tkey> keys;␊ |
| vector<Tval> values;␊ |
| dictobj(): keys(), values() { }␊ |
| dictobj(const dictobj& d): object(), keys(d.keys), values(d.values) { }␊ |
| };␊ |
| ␊ |
| objptr<dictobj> obj;␊ |
| ␊ |
| void _mkunique()␊ |
| { if (!obj.empty() && !obj.isunique()) obj = new dictobj(*obj); }␊ |
| ␊ |
| bool _bsearch(const Tkey& k, memint& i) const␊ |
| { i = 0; return !empty() && obj->keys.bsearch(k, i); }␊ |
| ␊ |
| public:␊ |
| dict() throw() : obj() { }␊ |
| dict(const dict& d) throw() : obj(d.obj) { }␊ |
| ~dict() throw() { }␊ |
| ␊ |
| dict(const Tkey& k, const Tval& v) throw()␊ |
| : obj(new dictobj())␊ |
| {␊ |
| obj->keys.push_back(k);␊ |
| obj->values.push_back(v);␊ |
| }␊ |
| ␊ |
| bool empty() const { return obj.empty(); }␊ |
| memint size() const { return !empty() ? obj->keys.size() : 0; }␊ |
| bool operator== (const dict& d) const { return obj == d.obj; }␊ |
| bool operator!= (const dict& d) const { return obj != d.obj; }␊ |
| ␊ |
| void clear() { obj.clear(); }␊ |
| void operator= (const dict& d) { obj = d.obj; }␊ |
| ␊ |
| const Tkey& key(memint i) const { chkidx(i); return obj->keys[i]; }␊ |
| const Tval& value(memint i) const { chkidx(i); return obj->values[i]; }␊ |
| ␊ |
| const vector<Tkey>& keys() const { return empty() ? vector<Tkey>() : obj->keys; }␊ |
| const vector<Tval>& values() const { return empty() ? vector<Tval>() : obj->values; }␊ |
| ␊ |
| void replace(memint i, const Tval& v)␊ |
| {␊ |
| chkidx(i);␊ |
| _mkunique();␊ |
| obj->values.replace(i, v);␊ |
| }␊ |
| ␊ |
| void erase(memint i)␊ |
| {␊ |
| chkidx(i);␊ |
| _mkunique();␊ |
| obj->keys.erase(i);␊ |
| obj->values.erase(i);␊ |
| if (obj->keys.empty())␊ |
| clear();␊ |
| }␊ |
| ␊ |
| const Tval* find(const Tkey& k) const␊ |
| {␊ |
| memint i;␊ |
| if (_bsearch(k, i))␊ |
| return &obj->values[i];␊ |
| else␊ |
| return NULL;␊ |
| }␊ |
| ␊ |
| bool find_key(const Tkey& k) const␊ |
| { memint i; return _bsearch(k, i); }␊ |
| ␊ |
| void find_replace(const Tkey& k, const Tval& v)␊ |
| {␊ |
| memint i;␊ |
| if (!_bsearch(k, i))␊ |
| {␊ |
| if (empty())␊ |
| obj = new dictobj();␊ |
| else␊ |
| _mkunique();␊ |
| obj->keys.insert(i, k);␊ |
| obj->values.insert(i, v);␊ |
| }␊ |
| else␊ |
| replace(i, v);␊ |
| assert(obj->keys.size() == obj->values.size());␊ |
| }␊ |
| ␊ |
| void find_erase(const Tkey& k)␊ |
| {␊ |
| memint i;␊ |
| if (_bsearch(k, i))␊ |
| erase(i);␊ |
| else␊ |
| container::keyerr();␊ |
| }␊ |
| ␊ |
| #ifdef DEBUG␊ |
| // for unit tests only␊ |
| struct item_type␊ |
| {␊ |
| const Tkey& key;␊ |
| Tval& value;␊ |
| item_type(const Tkey& k, Tval& v): key(k), value(v) { }␊ |
| };␊ |
| ␊ |
| item_type at(memint i) const␊ |
| { chkidx(i); return item_type(obj->keys[i], obj->values.atw(i)); }␊ |
| #endif␊ |
| };␊ |
| ␊ |
| ␊ |
| // --- ordset -------------------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| class ordset␊ |
| {␊ |
| friend class variant;␊ |
| protected:␊ |
| static charset empty_charset;␊ |
| struct setobj: public object␊ |
| {␊ |
| charset set;␊ |
| setobj(): set() { }␊ |
| setobj(const setobj& s): object(), set(s.set) { }␊ |
| };␊ |
| objptr<setobj> obj;␊ |
| charset& _getunique();␊ |
| public:␊ |
| ordset() throw() : obj() { }␊ |
| ordset(const ordset& s) throw() : obj(s.obj) { }␊ |
| ordset(integer v) throw();␊ |
| ordset(integer l, integer r) throw();␊ |
| ~ordset() throw() { }␊ |
| bool empty() const { return obj.empty() || obj->set.empty(); }␊ |
| memint compare(const ordset& s) const;␊ |
| bool operator== (const ordset& s) const { return compare(s) == 0; }␊ |
| bool operator!= (const ordset& s) const { return compare(s) != 0; }␊ |
| void clear() { obj.clear(); }␊ |
| void operator= (const ordset& s) { obj = s.obj; }␊ |
| bool find(integer v) const { return !obj.empty() && obj->set[int(v)]; }␊ |
| void find_insert(integer v);␊ |
| void find_insert(integer l, integer h);␊ |
| void find_erase(integer v);␊ |
| const charset& get_charset() const { return obj.empty() ? empty_charset : obj->set; }␊ |
| };␊ |
| ␊ |
| ␊ |
| // --- range --------------------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| class range␊ |
| {␊ |
| friend class variant;␊ |
| protected:␊ |
| struct rangeobj: public object␊ |
| {␊ |
| integer left;␊ |
| integer right;␊ |
| rangeobj(integer l, integer r): left(l), right(r) { }␊ |
| };␊ |
| objptr<rangeobj> obj;␊ |
| public:␊ |
| range(integer, integer) throw();␊ |
| ~range() throw();␊ |
| bool empty() const␊ |
| { return obj.empty(); }␊ |
| integer left() const␊ |
| { return obj->left; }␊ |
| integer right() const␊ |
| { return obj->right; }␊ |
| bool contains(integer v)␊ |
| { return !obj.empty() && (v >= obj->left && v <= obj->right); }␊ |
| memint compare(const range& r) const;␊ |
| bool operator ==(const range& r) const;␊ |
| };␊ |
| ␊ |
| ␊ |
| // --- object collections -------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| // extern template class podvec<object*>;␊ |
| ␊ |
| ␊ |
| class objvec_impl: public podvec<object*>␊ |
| {␊ |
| protected:␊ |
| typedef podvec<object*> parent;␊ |
| public:␊ |
| objvec_impl() throw(): parent() { }␊ |
| objvec_impl(const objvec_impl& s) throw(): parent(s) { }␊ |
| void release_all() throw();␊ |
| };␊ |
| ␊ |
| ␊ |
| template <class T>␊ |
| class objvec: public objvec_impl␊ |
| {␊ |
| protected:␊ |
| typedef objvec_impl parent;␊ |
| public:␊ |
| objvec() throw(): parent() { }␊ |
| objvec(const objvec& s) throw(): parent(s) { }␊ |
| T* operator[] (memint i) const { return cast<T*>(parent::operator[](i)); }␊ |
| T* at(memint i) const { return cast<T*>(parent::at(i)); }␊ |
| T* back() const { return cast<T*>(parent::back()); }␊ |
| T* back(memint i) const { return cast<T*>(parent::back(i)); }␊ |
| T* push_back(T* t) { parent::push_back(t); return t; }␊ |
| void insert(memint pos, T* t) { parent::insert(pos, t); }␊ |
| void replace(memint pos, T* t) { parent::replace(pos, t); }␊ |
| };␊ |
| ␊ |
| ␊ |
| class symbol: public object␊ |
| {␊ |
| public:␊ |
| str const name;␊ |
| symbol(const str& s) throw(): name(s) { }␊ |
| symbol(const char* s) throw(): name(s) { }␊ |
| ~symbol() throw();␊ |
| };␊ |
| ␊ |
| ␊ |
| class symtbl_impl: public objvec<symbol>␊ |
| {␊ |
| protected:␊ |
| typedef objvec<symbol> parent;␊ |
| bool bsearch(const str& key, memint& index) const;␊ |
| public:␊ |
| symtbl_impl() throw(): parent() { }␊ |
| symtbl_impl(const symtbl_impl& s) throw();␊ |
| symbol* find(const str& name) const; // NULL or symbol*␊ |
| bool add(symbol*);␊ |
| bool replace(symbol*);␊ |
| };␊ |
| ␊ |
| ␊ |
| template <class T>␊ |
| class symtbl: public symtbl_impl␊ |
| {␊ |
| protected:␊ |
| typedef symtbl_impl parent;␊ |
| public:␊ |
| symtbl() throw(): parent() { }␊ |
| memint size() const { return parent::size(); }␊ |
| T* find(const str& name) const { return cast<T*>(parent::find(name)); }␊ |
| bool add(T* t) { return parent::add(t); }␊ |
| bool replace(T* t) { return parent::replace(t); }␊ |
| void release_all() throw() { parent::release_all(); }␊ |
| };␊ |
| ␊ |
| ␊ |
| // --- Exceptions ---------------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| // For dynamically generated strings␊ |
| class emessage: public exception␊ |
| {␊ |
| public:␊ |
| str msg;␊ |
| emessage(const emessage&) throw(); // not defined␊ |
| emessage(const str& _msg) throw();␊ |
| emessage(const char* _msg) throw();␊ |
| ~emessage() throw();␊ |
| const char* what() throw();␊ |
| };␊ |
| ␊ |
| ␊ |
| // TODO: define these as separate classes␊ |
| typedef emessage econtainer;␊ |
| typedef emessage evariant;␊ |
| typedef emessage efifo;␊ |
| ␊ |
| ␊ |
| // UNIX system errors␊ |
| class esyserr: public emessage␊ |
| {␊ |
| public:␊ |
| esyserr(int icode, const str& iArg = "") throw();␊ |
| ~esyserr() throw();␊ |
| };␊ |
| ␊ |
| ␊ |
| void nullptrerr();␊ |
| ␊ |
| template <class T>␊ |
| inline T* CHKPTR(T* p)␊ |
| { if (p == NULL) nullptrerr(); return p; }␊ |
| ␊ |
| ␊ |
| // --- variant ------------------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| class variant;␊ |
| class reference;␊ |
| class funcptr;␊ |
| class stateobj;␊ |
| struct podvar;␊ |
| ␊ |
| typedef vector<variant> varvec;␊ |
| typedef set<variant> varset;␊ |
| typedef dict<variant, variant> vardict;␊ |
| ␊ |
| ␊ |
| class variant␊ |
| {␊ |
| friend void test_variant();␊ |
| friend void initRuntime();␊ |
| ␊ |
| private:␊ |
| void _init(void*); // compiler traps␊ |
| void _init(const void*);␊ |
| void _init(object*);␊ |
| void _init(bool);␊ |
| ␊ |
| public:␊ |
| enum Type␊ |
| { VOID, ORD, REAL, VARPTR,␊ |
| STR, RANGE, VEC, SET, ORDSET, DICT, REF, RTOBJ,␊ |
| ANYOBJ = STR };␊ |
| ␊ |
| struct _Void { int dummy; }; ␊ |
| static _Void null;␊ |
| ␊ |
| protected:␊ |
| Type type;␊ |
| union _val_union␊ |
| {␊ |
| integer _all; // should be the biggest in this union␊ |
| integer _ord; // int, char and bool␊ |
| real _real; // not implemented in the VM yet␊ |
| variant* _ptr; // POD pointer to a variant␊ |
| object* _obj; // str, vector, set, map and their variants␊ |
| reference* _ref; // reference object␊ |
| rtobject* _rtobj; // runtime objects with the "type" field␊ |
| } val;␊ |
| ␊ |
| void _req(Type t) const { if (type != t) _type_err(); }␊ |
| void _req_anyobj() const { if (!is_anyobj()) _type_err(); }␊ |
| #ifdef DEBUG␊ |
| void _dbg(Type t) const { _req(t); }␊ |
| void _dbg_anyobj() const { _req_anyobj(); }␊ |
| #else␊ |
| void _dbg(Type) const { }␊ |
| void _dbg_anyobj() const { }␊ |
| #endif␊ |
| void _init() throw() { type = VOID; val._all = 0; }␊ |
| void _init(_Void) throw() { _init(); }␊ |
| void _init(Type t) throw() { type = t; val._all = 0; }␊ |
| void _init(char v) throw() { type = ORD; val._ord = uchar(v); }␊ |
| void _init(uchar v) throw() { type = ORD; val._ord = v; }␊ |
| void _init(int v) throw() { type = ORD; val._ord = v; }␊ |
| #ifdef SHN_64␊ |
| void _init(large v) throw() { type = ORD; val._ord = v; }␊ |
| #endif␊ |
| void _init(real v) throw() { type = REAL; val._real = v; }␊ |
| void _init(variant* v) throw() { type = VARPTR; val._ptr = v; }␊ |
| void _init(Type t, object* o) throw() { type = t; val._obj = o; if (o) o->grab(); }␊ |
| void _init(const str& v) throw() { _init(STR, v.obj); }␊ |
| void _init(const char* s) throw() { type = STR; ::new(&val._obj) str(s); }␊ |
| void _init(const range& v) throw() { _init(RANGE, v.obj); }␊ |
| void _init(integer l, integer r) throw() { type = RANGE; ::new(&val._obj) range(l, r); }␊ |
| void _init(const varvec& v) throw() { _init(VEC, v.obj); }␊ |
| void _init(const varset& v) throw() { _init(SET, v.obj); }␊ |
| void _init(const ordset& v) throw() { _init(ORDSET, v.obj); }␊ |
| void _init(const vardict& v) throw() { _init(DICT, v.obj); }␊ |
| void _init(reference* o) throw();␊ |
| void _init(rtobject* o) throw() { _init(RTOBJ, o); }␊ |
| void _init(fifo* o) throw();␊ |
| void _init(stateobj* o) throw();␊ |
| void _init(const variant& v) throw();␊ |
| void _init(const podvar* v) throw();␊ |
| ␊ |
| void _fin() throw() { if (is_anyobj()) val._obj->release(); }␊ |
| ␊ |
| public:␊ |
| variant() throw() { _init(); }␊ |
| variant(Type t) throw() { _init(t); }␊ |
| variant(integer l, integer r) throw() { _init(l, r); }␊ |
| variant(const variant& v) throw() { _init(v); }␊ |
| template <class T>␊ |
| variant(const T& v) throw() { _init(v); }␊ |
| variant(Type t, object* o) throw() { _init(t, o); }␊ |
| ~variant() throw() { _fin(); }␊ |
| ␊ |
| template <class T>␊ |
| void operator= (const T& v) throw() { _fin(); _init(v); }␊ |
| void operator= (const variant& v) throw();␊ |
| void clear() throw() { _fin(); _init(); }␊ |
| bool empty() const;␊ |
| ␊ |
| memint compare(const variant&) const;␊ |
| bool operator== (const variant&) const;␊ |
| bool operator!= (const variant& v) const { return !(operator==(v)); }␊ |
| ␊ |
| Type getType() const { return Type(type); }␊ |
| bool is(Type t) const { return type == t; }␊ |
| bool is_str() const { return type == STR; }␊ |
| bool is_vec() const { return type == VEC; }␊ |
| bool is_null() const { return type == VOID; }␊ |
| bool is_anyobj() const throw() { return type >= ANYOBJ; }␊ |
| bool is_null_obj() const { return is_anyobj() && val._obj == NULL; }␊ |
| ␊ |
| // Fast "unsafe" access methods; checked for correctness only in DEBUG mode␊ |
| bool _bool() const { _dbg(ORD); return val._ord; }␊ |
| uchar _uchar() const { _dbg(ORD); return (uchar)val._ord; }␊ |
| integer _int() const { _dbg(ORD); return val._ord; }␊ |
| variant* _ptr() const { _dbg(VARPTR); return val._ptr; }␊ |
| const str& _str() const { _dbg(STR); return *(str*)&val._obj; }␊ |
| const range& _range() const { _dbg(RANGE); return *(range*)&val._obj; }␊ |
| const varvec& _vec() const { _dbg(VEC); return *(varvec*)&val._obj; }␊ |
| const varset& _set() const { _dbg(SET); return *(varset*)&val._obj; }␊ |
| const ordset& _ordset() const { _dbg(ORDSET); return *(ordset*)&val._obj; }␊ |
| const vardict& _dict() const { _dbg(DICT); return *(vardict*)&val._obj; }␊ |
| reference* _ref() const { _dbg(REF); return CHKPTR(val._ref); }␊ |
| rtobject* _rtobj() const { _dbg(RTOBJ); return val._rtobj; }␊ |
| stateobj* _stateobj() const;␊ |
| funcptr* _funcptr() const;␊ |
| fifo* _fifo() const; // checks for NULL␊ |
| object* _anyobj() const { _dbg_anyobj(); return val._obj; }␊ |
| integer& _int() { _dbg(ORD); return val._ord; }␊ |
| str& _str() { _dbg(STR); return *(str*)&val._obj; }␊ |
| range& _range() { _dbg(RANGE); return *(range*)&val._obj; }␊ |
| varvec& _vec() { _dbg(VEC); return *(varvec*)&val._obj; }␊ |
| varset& _set() { _dbg(SET); return *(varset*)&val._obj; }␊ |
| ordset& _ordset() { _dbg(ORDSET); return *(ordset*)&val._obj; }␊ |
| vardict& _dict() { _dbg(DICT); return *(vardict*)&val._obj; }␊ |
| ␊ |
| // Safer access methods; may throw␊ |
| bool as_bool() const { _req(ORD); return _bool(); }␊ |
| uchar as_uchar() const { _req(ORD); return _uchar(); }␊ |
| integer as_ord() const { _req(ORD); return _int(); }␊ |
| variant* as_ptr() const { _req(VARPTR); return val._ptr; }␊ |
| const str& as_str() const { _req(STR); return _str(); }␊ |
| const range& as_range() const { _req(RANGE); return _range(); }␊ |
| const varvec& as_vec() const { _req(VEC); return _vec(); }␊ |
| const varset& as_set() const { _req(SET); return _set(); }␊ |
| const ordset& as_ordset() const { _req(ORDSET); return _ordset(); }␊ |
| const vardict& as_dict() const { _req(DICT); return _dict(); }␊ |
| reference* as_ref() const { _req(REF); return val._ref; }␊ |
| rtobject* as_rtobj() const { _req(RTOBJ); return _rtobj(); }␊ |
| object* as_anyobj() const { _req_anyobj(); return val._obj; }␊ |
| integer& as_ord() { _req(ORD); return _int(); }␊ |
| str& as_str() { _req(STR); return _str(); }␊ |
| range& as_range() { _req(RANGE); return _range(); }␊ |
| varvec& as_vec() { _req(VEC); return _vec(); }␊ |
| varset& as_set() { _req(SET); return _set(); }␊ |
| ordset& as_ordset() { _req(ORDSET); return _ordset(); }␊ |
| vardict& as_dict() { _req(DICT); return _dict(); }␊ |
| ␊ |
| static void _type_err();␊ |
| static void _range_err();␊ |
| };␊ |
| ␊ |
| ␊ |
| #ifdef SHN_FASTER␊ |
| inline void variant::_init(const variant& v) throw()␊ |
| {␊ |
| type = v.type;␊ |
| val = v.val;␊ |
| if (is_anyobj() && val._obj)␊ |
| val._obj->grab();␊ |
| }␊ |
| ␊ |
| ␊ |
| inline void variant::operator= (const variant& v) throw()␊ |
| {␊ |
| if (type != v.type || val._all != v.val._all)␊ |
| { _fin(); _init(v); }␊ |
| }␊ |
| #endif␊ |
| ␊ |
| ␊ |
| struct podvar { char data[sizeof(variant)]; };␊ |
| ␊ |
| inline void variant::_init(const podvar* v) throw()␊ |
| { *(podvar*)this = *v; }␊ |
| ␊ |
| template <>␊ |
| struct comparator<variant>␊ |
| { memint operator() (const variant& a, const variant& b) { return a.compare(b); } };␊ |
| ␊ |
| /*␊ |
| extern template class vector<variant>;␊ |
| extern template class set<variant>;␊ |
| extern template class dict<variant, variant>;␊ |
| extern template class podvec<variant>;␊ |
| */␊ |
| ␊ |
| // --- runtime objects ----------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| // reference: is a ref-counted object that encapsulates a variant; this way a␊ |
| // variant can be shared between multiple other variables and mimic␊ |
| // "references" as commonly defined in other languages␊ |
| ␊ |
| class reference: public object␊ |
| {␊ |
| public:␊ |
| variant var;␊ |
| reference() throw() { }␊ |
| reference(const variant& v) throw(): var(v) { }␊ |
| reference(const podvar* v) throw(): var(v) { }␊ |
| ~reference() throw();␊ |
| };␊ |
| ␊ |
| ␊ |
| inline void variant::_init(reference* o) throw() { _init(REF, o); }␊ |
| ␊ |
| ␊ |
| class State; // defined in typesys.h␊ |
| class CodeSeg; // defined in vm.h␊ |
| ␊ |
| ␊ |
| // sateobj: a run-time ref-counted object, actually a structure with variant␊ |
| // member fields. The actual size is passed to operator new() defined below.␊ |
| ␊ |
| class stateobj: public rtobject␊ |
| {␊ |
| friend class State;␊ |
| typedef rtobject parent;␊ |
| friend void runRabbitRun(variant*, stateobj*, stateobj*, variant*, CodeSeg*);␊ |
| ␊ |
| protected:␊ |
| #ifdef DEBUG␊ |
| memint varcount;␊ |
| static void idxerr();␊ |
| #endif␊ |
| stateobj(State*) throw(); // defined in typesys.h as an inline function␊ |
| ␊ |
| // Get zeroed memory so that the destructor works correctly even if the␊ |
| // constructor failed in the middle. A zeroed variant is a null variant.␊ |
| void* operator new(size_t s, memint extra)␊ |
| {␊ |
| #ifdef DEBUG␊ |
| pincrement(&object::allocated);␊ |
| #endif␊ |
| return pmemcalloc(s + extra * sizeof(variant));␊ |
| }␊ |
| ␊ |
| // In place operator new for stateobj: for creating pseudo-objects on the stack␊ |
| void* operator new(size_t, void* p)␊ |
| {␊ |
| #ifdef DEBUG␊ |
| pincrement(&object::allocated);␊ |
| #endif␊ |
| // memset(pchar(p) + s, 0, extra * sizeof(variant));␊ |
| return p;␊ |
| }␊ |
| ␊ |
| public:␊ |
| ~stateobj() throw();␊ |
| State* getType() const { return (State*)parent::getType(); }␊ |
| ␊ |
| bool empty() const; // override␊ |
| void dump(fifo&) const; // override␊ |
| ␊ |
| variant* member(memint index)␊ |
| {␊ |
| #ifdef DEBUG␊ |
| if (umemint(index) >= umemint(varcount))␊ |
| idxerr();␊ |
| #endif␊ |
| return (variant*)(this + 1) + index;␊ |
| }␊ |
| ␊ |
| void collapse();␊ |
| };␊ |
| ␊ |
| ␊ |
| inline void variant::_init(stateobj* o) throw() { _init(RTOBJ, o); }␊ |
| inline stateobj* variant::_stateobj() const { return cast<stateobj*>(_rtobj()); }␊ |
| ␊ |
| ␊ |
| class funcptr: public rtobject␊ |
| {␊ |
| public:␊ |
| stateobj* dataseg;␊ |
| objptr<stateobj> outer;␊ |
| State* state;␊ |
| funcptr(stateobj* dataseg, stateobj* outer, State* state) throw();␊ |
| ~funcptr() throw();␊ |
| bool empty() const;␊ |
| void dump(fifo&) const;␊ |
| };␊ |
| ␊ |
| inline funcptr* variant::_funcptr() const { return cast<funcptr*>(_rtobj()); }␊ |
| ␊ |
| ␊ |
| class rtstack: protected bytevec␊ |
| {␊ |
| public:␊ |
| rtstack(memint maxSize) throw();␊ |
| variant* base()␊ |
| { return (variant*)begin(); }␊ |
| };␊ |
| ␊ |
| ␊ |
| // --- FIFO ---------------------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| const int _varsize = int(sizeof(variant));␊ |
| ␊ |
| ␊ |
| // The abstract FIFO interface. There are 2 modes of operation: variant FIFO␊ |
| // and character FIFO. Destruction of variants is basically not handled by␊ |
| // this class to give more flexibility to implementations (e.g. there may be␊ |
| // buffers shared between 2 fifos or other container objects). If you implement,␊ |
| // say, only input methods, the default output methods will throw an exception␊ |
| // with a message "FIFO is read-only", and vice versa. Iterators may be␊ |
| // implemented in descendant classes but are not supported by default.␊ |
| // Powerful text parsing methods are provided that work on any derived FIFO␊ |
| // implementation (see "Characetr FIFO operations" below).␊ |
| class fifo: public rtobject␊ |
| {␊ |
| friend void runRabbitRun(variant*, stateobj*, stateobj*, variant*, CodeSeg*);␊ |
| ␊ |
| fifo& operator<< (bool); // compiler traps␊ |
| fifo& operator<< (void*);␊ |
| fifo& operator<< (object*);␊ |
| fifo& operator<< (rtobject* o); // { o->dump(*this); return *this; }␊ |
| ␊ |
| protected:␊ |
| memint max_token; // 4096␊ |
| bool _is_char_fifo;␊ |
| ␊ |
| static void _empty_err();␊ |
| static void _full_err();␊ |
| static void _wronly_err();␊ |
| static void _rdonly_err();␊ |
| static void _fifo_type_err();␊ |
| static void _token_err();␊ |
| void _req(bool req_char) const { if (req_char != _is_char_fifo) _fifo_type_err(); }␊ |
| void _req_non_empty() const;␊ |
| void _req_non_empty(bool _char) const;␊ |
| ␊ |
| // Minimal set of methods required for both character and variant FIFO␊ |
| // operations. Implementations should guarantee variants will never be␊ |
| // fragmented, so that a buffer returned by get_tail() always contains at␊ |
| // least sizeof(variant) bytes (8, 12 or 16 bytes depending on the config.␊ |
| // and platform) in variant mode, or at least 1 byte in character mode.␊ |
| virtual const char* get_tail(); // Get a pointer to tail data␊ |
| virtual const char* get_tail(memint*); // ... also return the length␊ |
| virtual void deq_bytes(memint); // Discard n consecutive bytes returned by get_tail()␊ |
| virtual variant* enq_var(); // Reserve uninitialized space for a variant␊ |
| virtual void enq_char(char); // Push one char, char fifo only␊ |
| virtual memint enq_chars(const char*, memint); // Push arbitrary number of bytes, return actual number, char fifo only␊ |
| ␊ |
| void _token(const charset& chars, str* result);␊ |
| void deq_var(variant*); // dequeue variant to uninitialized area, for internal use␊ |
| ␊ |
| public:␊ |
| fifo(Type*, bool is_char) throw();␊ |
| ~fifo() throw();␊ |
| ␊ |
| enum { CHAR_ALL = MEMINT_MAX - 2, CHAR_SOME = MEMINT_MAX - 1 };␊ |
| ␊ |
| void dump(fifo&) const;␊ |
| ␊ |
| bool empty() const; // override, throws␊ |
| virtual void flush(); // empty, overridden in file fifos␊ |
| virtual str get_name() const = 0;␊ |
| ␊ |
| // Main FIFO operations, work on both char and variant fifos; for char␊ |
| // fifos the variant is read as either a char or a string.␊ |
| void var_enq(const variant&);␊ |
| void var_preview(variant&);␊ |
| void var_deq(variant&);␊ |
| void var_eat();␊ |
| ␊ |
| // Character FIFO operations␊ |
| bool is_char_fifo() const { return _is_char_fifo; }␊ |
| int preview(); // returns -1 on eof␊ |
| char look()␊ |
| { int c = preview(); if (c == -1) _empty_err(); return c; }␊ |
| uchar get();␊ |
| bool get_if(char c);␊ |
| str deq(memint); // CHAR_ALL, CHAR_SOME can be specified␊ |
| str deq(const charset& c) { str s; _token(c, &s); return s; }␊ |
| str token(const charset& c) { return deq(c); } // alias␊ |
| void eat(const charset& c) { _token(c, NULL); }␊ |
| void skip(const charset& c) { eat(c); } // alias␊ |
| str line();␊ |
| bool eol();␊ |
| void skip_eol();␊ |
| bool eof() const { return empty(); }␊ |
| ␊ |
| memint enq(const char* p, memint count) { return enq_chars(p, count); }␊ |
| void enq(const char* s);␊ |
| void enq(const str& s);␊ |
| void enq(char c) { enq_char(c); }␊ |
| void enq(uchar c) { enq_char(c); }␊ |
| void enq(large i);␊ |
| void enq(const varvec&);␊ |
| ␊ |
| fifo& operator<< (const char* s) { enq(s); return *this; }␊ |
| fifo& operator<< (const str& s) { enq(s); return *this; }␊ |
| fifo& operator<< (char c) { enq(c); return *this; }␊ |
| fifo& operator<< (uchar c) { enq(c); return *this; }␊ |
| fifo& operator<< (large i) { enq(large(i)); return *this; }␊ |
| fifo& operator<< (int i) { enq(large(i)); return *this; }␊ |
| fifo& operator<< (long i) { enq(large(i)); return *this; }␊ |
| fifo& operator<< (size_t i) { enq(large(i)); return *this; }␊ |
| };␊ |
| ␊ |
| const char endl = '\n';␊ |
| extern charset non_eol_chars;␊ |
| ␊ |
| inline void variant::_init(fifo* f) throw() { _init(RTOBJ, f); }␊ |
| inline fifo* variant::_fifo() const { return cast<fifo*>(CHKPTR(_rtobj())); }␊ |
| ␊ |
| ␊ |
| // The memfifo class implements a linked list of "chunks" in memory, where␊ |
| // each chunk is the size of 32 * sizeof(variant). Both enqueue and deqeue␊ |
| // operations are O(1), and memory usage is better than that of a plain linked ␊ |
| // list of elements, as "next" pointers are kept for bigger chunks of elements␊ |
| // rather than for each element. Can be used both for variants and chars. This␊ |
| // class "owns" variants, i.e. proper construction and desrtuction is done.␊ |
| class memfifo: public fifo␊ |
| {␊ |
| public:␊ |
| #ifdef DEBUG␊ |
| static int CHUNK_SIZE; // settable from unit tests␊ |
| #else␊ |
| enum { CHUNK_SIZE = 32 * _varsize };␊ |
| #endif␊ |
| ␊ |
| protected:␊ |
| struct chunk: noncopyable␊ |
| {␊ |
| chunk* next;␊ |
| char data[0];␊ |
| #ifdef DEBUG␊ |
| chunk() throw(): next(NULL) { pincrement(&object::allocated); }␊ |
| ~chunk() throw() { pdecrement(&object::allocated); }␊ |
| #else␊ |
| chunk() throw(): next(NULL) { }␊ |
| #endif␊ |
| void* operator new(size_t) { return ::pmemalloc(sizeof(chunk) + CHUNK_SIZE); }␊ |
| void operator delete(void* p) { ::pmemfree(p); }␊ |
| };␊ |
| ␊ |
| chunk* head; // in␊ |
| chunk* tail; // out␊ |
| int head_offs;␊ |
| int tail_offs;␊ |
| ␊ |
| void enq_chunk();␊ |
| void deq_chunk();␊ |
| ␊ |
| // Overrides␊ |
| const char* get_tail();␊ |
| const char* get_tail(memint*);␊ |
| void deq_bytes(memint);␊ |
| variant* enq_var();␊ |
| void enq_char(char);␊ |
| memint enq_chars(const char*, memint);␊ |
| ␊ |
| char* enq_space(memint);␊ |
| memint enq_avail();␊ |
| ␊ |
| public:␊ |
| memfifo(Type*, bool is_char) throw();␊ |
| ~memfifo() throw();␊ |
| ␊ |
| void clear();␊ |
| bool empty() const; // override␊ |
| str get_name() const; // override␊ |
| };␊ |
| ␊ |
| ␊ |
| // Buffer read event handler (write events aren't implemented yet)␊ |
| class bufevent: public object␊ |
| {␊ |
| public:␊ |
| virtual void event(char* buf, memint tail, memint head) = 0;␊ |
| };␊ |
| ␊ |
| ␊ |
| // This is an abstract buffered fifo class. Implementations should validate the␊ |
| // buffer in the overridden empty() and flush() methods, for input and output␊ |
| // fifos respectively. To simplify things, buffifo objects are not supposed to␊ |
| // be reusable, i.e. once the end of file is reached, the implementation is not␊ |
| // required to reset its state. Variant fifo implementations should guarantee␊ |
| // at least sizeof(variant) bytes in calls to get_tail() and enq_var().␊ |
| class buffifo: public fifo␊ |
| {␊ |
| protected:␊ |
| char* buffer;␊ |
| memint bufsize;␊ |
| memint bufhead;␊ |
| memint buftail;␊ |
| memint buforig;␊ |
| ␊ |
| bufevent* event;␊ |
| ␊ |
| const char* get_tail();␊ |
| const char* get_tail(memint*);␊ |
| void deq_bytes(memint);␊ |
| variant* enq_var();␊ |
| void enq_char(char);␊ |
| memint enq_chars(const char*, memint);␊ |
| ␊ |
| char* enq_space(memint);␊ |
| memint enq_avail();␊ |
| ␊ |
| void call_bufevent() const;␊ |
| ␊ |
| public:␊ |
| buffifo(Type*, bool is_char) throw();␊ |
| ~buffifo() throw();␊ |
| ␊ |
| bool empty() const; // throws efifowronly␊ |
| void flush(); // throws efifordonly␊ |
| ␊ |
| memint tellg() const { return buforig + buftail; }␊ |
| memint tellp() const { return buforig + bufhead; }␊ |
| bufevent* set_bufevent(bufevent*);␊ |
| };␊ |
| ␊ |
| ␊ |
| // Analog of std::strstream; a buffifo-based implementation that uses␊ |
| // a 'str' object for storing data.␊ |
| class strfifo: public buffifo␊ |
| {␊ |
| protected:␊ |
| str string;␊ |
| void clear();␊ |
| public:␊ |
| strfifo(Type*) throw();␊ |
| strfifo(Type*, const str&) throw();␊ |
| ~strfifo() throw();␊ |
| bool empty() const; // override␊ |
| void flush(); // override␊ |
| str get_name() const; // override␊ |
| str all() const;␊ |
| };␊ |
| ␊ |
| ␊ |
| // TODO: varfifo, a variant vector wrapper based on buffifo␊ |
| ␊ |
| class intext: public buffifo␊ |
| {␊ |
| public:␊ |
| #ifdef DEBUG␊ |
| static int BUF_SIZE; // settable from unit tests␊ |
| #else␊ |
| enum { BUF_SIZE = 4096 * sizeof(integer) };␊ |
| #endif␊ |
| ␊ |
| protected:␊ |
| str file_name;␊ |
| str filebuf;␊ |
| int _fd;␊ |
| bool _eof;␊ |
| ␊ |
| void error(int code); // throws esyserr␊ |
| void doopen();␊ |
| void doread();␊ |
| ␊ |
| public:␊ |
| intext(Type*, const str& fn) throw();␊ |
| ~intext() throw();␊ |
| ␊ |
| bool empty() const; // override␊ |
| str get_name() const; // override␊ |
| void open() { empty(); /* attempt to open */ }␊ |
| };␊ |
| ␊ |
| ␊ |
| class outtext: public buffifo␊ |
| {␊ |
| protected:␊ |
| enum { BUF_SIZE = 2048 * sizeof(integer) };␊ |
| ␊ |
| str file_name;␊ |
| str filebuf;␊ |
| int _fd;␊ |
| bool _err;␊ |
| ␊ |
| void error(int code); // throws esyserr␊ |
| ␊ |
| public:␊ |
| outtext(Type*, const str& fn) throw();␊ |
| ~outtext() throw();␊ |
| ␊ |
| void flush(); // override␊ |
| str get_name() const; // override␊ |
| void open() { flush(); /* attempt to open */ }␊ |
| };␊ |
| ␊ |
| ␊ |
| // Standard input/output object, a two-way fifo. In case of stderr it is write-only.␊ |
| class stdfile: public intext␊ |
| {␊ |
| protected:␊ |
| int _ofd;␊ |
| void enq_char(char);␊ |
| memint enq_chars(const char*, memint);␊ |
| public:␊ |
| stdfile(int infd, int outfd) throw();␊ |
| ~stdfile() throw();␊ |
| };␊ |
| ␊ |
| ␊ |
| extern stdfile sio;␊ |
| extern stdfile serr;␊ |
| ␊ |
| ␊ |
| // System utilities␊ |
| ␊ |
| ␊ |
| bool isFile(const char*);␊ |
| ␊ |
| ␊ |
| // ------------------------------------------------------------------------- //␊ |
| ␊ |
| ␊ |
| typedef vector<str> strvec;␊ |
| // extern template class vector<str>;␊ |
| ␊ |
| ␊ |
| void initRuntime();␊ |
| void doneRuntime();␊ |
| ␊ |
| ␊ |
| #endif // __RUNTIME_H␊ |