From: david Date: Thu, 18 Jul 2002 20:27:46 +0000 (+0000) Subject: Added JavaScript interpreter, repackaged by Tony Peden. It's not tied X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=c8efd0a465ca02a03972ea7a8407780780ccaef8;p=simgear.git Added JavaScript interpreter, repackaged by Tony Peden. It's not tied into anything yet, but it builds OK inside SimGear. --- diff --git a/configure.in b/configure.in index 5340e219..8493bcb4 100644 --- a/configure.in +++ b/configure.in @@ -343,6 +343,7 @@ AC_OUTPUT( \ simgear/bucket/Makefile \ simgear/debug/Makefile \ simgear/ephemeris/Makefile \ + simgear/interpreter/Makefile \ simgear/io/Makefile \ simgear/magvar/Makefile \ simgear/math/Makefile \ diff --git a/simgear/Makefile.am b/simgear/Makefile.am index b8c05870..af82b4d7 100644 --- a/simgear/Makefile.am +++ b/simgear/Makefile.am @@ -20,6 +20,7 @@ include_HEADERS = \ SUBDIRS = \ xml \ + interpreter \ debug \ misc \ bucket \ diff --git a/simgear/interpreter/.cvsignore b/simgear/interpreter/.cvsignore new file mode 100644 index 00000000..e9955884 --- /dev/null +++ b/simgear/interpreter/.cvsignore @@ -0,0 +1,3 @@ +.deps +Makefile +Makefile.in diff --git a/simgear/interpreter/Makefile.am b/simgear/interpreter/Makefile.am new file mode 100644 index 00000000..153f6fc6 --- /dev/null +++ b/simgear/interpreter/Makefile.am @@ -0,0 +1,37 @@ +includedir = @includedir@/js + +lib_LIBRARIES = libsginterp.a + +include_HEADERS = \ + interpreter.hxx + +libsginterp_a_SOURCES = \ + interpreter.cxx interpreter.hxx \ + ixlib_javascript.hh \ + js_array.cc \ + js_expression.cc \ + js_interpreter.cc \ + js_value.cc \ + js_declaration.cc \ + js_instruction.cc \ + js_library.cc \ + lex.javascript.cc ixlib_token_lex.hh ixlib_token_javascript.hh \ + scanner.cc ixlib_scanner.hh ixlib_scanjs.hh \ + exbase.cc \ + numeric.cc ixlib_numeric.hh \ + numconv.cc ixlib_numconv.hh \ + re.cc ixlib_re.hh ixlib_re_impl.hh \ + exgen.cc ixlib_exgen.hh \ + string.cc ixlib_string.hh \ + ixlib_base.hh \ + ixlib_exbase.hh \ + ixlib_garbage.hh \ + ixlib_i18n.hh \ + ixlib_js_internals.hh \ + ixlib_random.hh + +if OLD_AUTOMAKE +INCLUDES += -I$(top_srcdir) +else +INCLUDES = -I$(top_srcdir) +endif diff --git a/simgear/interpreter/exbase.cc b/simgear/interpreter/exbase.cc new file mode 100644 index 00000000..458bc208 --- /dev/null +++ b/simgear/interpreter/exbase.cc @@ -0,0 +1,77 @@ +// ---------------------------------------------------------------------------- +// Description : Exception handling +// ---------------------------------------------------------------------------- +// (c) Copyright 1996 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#include +#include +#include + + + + +using namespace ixion; + + + + +// Description forms ---------------------------------------------------------- +#define T_DESCRIPTION1 "[%s%04X] %s" +#define T_DESCRIPTION2 "[%s%04X] %s <%s>" +#define T_DESCRIPTION3 "[%s%04X] %s <%s,%d>" +#define T_DESCRIPTION1I "[%s%04X] %s (%s)" +#define T_DESCRIPTION2I "[%s%04X] %s (%s) <%s>" +#define T_DESCRIPTION3I "[%s%04X] %s (%s) <%s,%d>" + + + + +// base_exception ------------------------------------------------------------- +char base_exception::RenderBuffer[EX_INFOMAX+1+100]; + + + + +base_exception::base_exception(TErrorCode error,char const *info,char *module, + TIndex line,char *category) +: Error(error),Module(module),Line(line),Category(category) { + HasInfo = (info!=NULL); + if (info) { + if (strlen(info)>EX_INFOMAX) { + strncpy(Info,info,EX_INFOMAX); + Info[EX_INFOMAX] = '\0'; + } + else strcpy(Info,info); + } + } + + + + +char const *base_exception::what() const throw () { + if (HasInfo) { + if (Module) { + if (Line) + sprintf(RenderBuffer,T_DESCRIPTION3I,Category,Error,getText(),Info,Module,Line); + else + sprintf(RenderBuffer,T_DESCRIPTION2I,Category,Error,getText(),Info,Module); + } + else + sprintf(RenderBuffer,T_DESCRIPTION1I,Category,Error,getText(),Info); + } + else { + if (Module) { + if (Line) + sprintf(RenderBuffer,T_DESCRIPTION3,Category,Error,getText(),Module,Line); + else + sprintf(RenderBuffer,T_DESCRIPTION2,Category,Error,getText(),Module); + } + else + sprintf(RenderBuffer,T_DESCRIPTION1,Category,Error,getText()); + } + return RenderBuffer; + } diff --git a/simgear/interpreter/exgen.cc b/simgear/interpreter/exgen.cc new file mode 100644 index 00000000..3aeaed76 --- /dev/null +++ b/simgear/interpreter/exgen.cc @@ -0,0 +1,46 @@ +// ---------------------------------------------------------------------------- +// Description : Generic exceptions +// ---------------------------------------------------------------------------- +// (c) Copyright 1996 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#include "ixlib_i18n.hh" +#include +#include + + + + +using namespace ixion; + + + + +static char *(PlainText[]) = { + N_("Unable to evaluate expression"), + N_("Function not yet implemented"), + N_("General error"), + N_("NULL pointer encountered"), + N_("Invalid parameter"), + N_("Index out of range"), + N_("Buffer overrun"), + N_("Buffer underrun"), + N_("Item not found"), + N_("Invalid operation"), + N_("Dimension mismatch"), + N_("Operation cancelled"), + N_("Unable to operate on empty set"), + N_("Unable to remove GC entry"), + N_("Unable to protect non-freeable entry") + }; + + + + +// generic_exception ---------------------------------------------------------- +char const *generic_exception::getText() const { + return _(PlainText[Error]); + } diff --git a/simgear/interpreter/interpreter.cxx b/simgear/interpreter/interpreter.cxx new file mode 100644 index 00000000..b850390b --- /dev/null +++ b/simgear/interpreter/interpreter.cxx @@ -0,0 +1,9 @@ +#include "interpreter.hxx" + +SGInterpreter::SGInterpreter () +{ +} + +SGInterpreter::~SGInterpreter () +{ +} diff --git a/simgear/interpreter/interpreter.hxx b/simgear/interpreter/interpreter.hxx new file mode 100644 index 00000000..c8bc923b --- /dev/null +++ b/simgear/interpreter/interpreter.hxx @@ -0,0 +1,13 @@ +#ifndef __INTERPRETER_HXX +#define __INTERPRETER_HXX 1 + +class SGInterpreter +{ +public: + + SGInterpreter (); + virtual ~SGInterpreter (); + +}; + +#endif diff --git a/simgear/interpreter/ixlib_base.hh b/simgear/interpreter/ixlib_base.hh new file mode 100644 index 00000000..cd772c27 --- /dev/null +++ b/simgear/interpreter/ixlib_base.hh @@ -0,0 +1,107 @@ +/* ---------------------------------------------------------------------------- + Description : iXiONmedia library base declarations + ---------------------------------------------------------------------------- + (c) Copyright 1996 by iXiONmedia, all rights reserved. + ---------------------------------------------------------------------------- + This header must be C-safe for autoconf purposes. + */ + + + + +#ifndef IXLIB_BASE +#define IXLIB_BASE + + +#undef HAVE_CONFIG_H + +#ifdef HAVE_CONFIG_H +#include +#undef PACKAGE +#undef VERSION +#endif + + + + +#ifdef __cplusplus +namespace ixion { + extern "C" { +#endif +/* Aliases -------------------------------------------------------------------- +*/ + const double Pi = 3.141592653589793285; + const double Euler = 2.718281828; + const double Gravity = 9.8065; // m/s^2 + const double UniGravity = 6.673e-11; // m^3/kg s^2 + const double Epsilon0 = 8.8542e-12; // F/m + const double Mu0 = 1.2566e-6; // H/m + const double LightSpeed = 2.9972e8; // m/s + const double Planck = 6.6261e-34; // Js + + + + +/* STL Helper macro ----------------------------------------------------------- +*/ +#define FOREACH(VAR,LIST,LISTTYPE) \ + for (LISTTYPE::iterator VAR = (LIST).begin(),last = (LIST).end();VAR != last;VAR++) +#define FOREACH_CONST(VAR,LIST,LISTTYPE) \ + for (LISTTYPE::const_iterator VAR = (LIST).begin(),last = (LIST).end();VAR != last;VAR++) + + + + +/* Nomenclature typedefs ------------------------------------------------------ +*/ + typedef unsigned char TUnsigned8; + typedef unsigned short TUnsigned16; + typedef unsigned long TUnsigned32; + typedef unsigned long long TUnsigned64; + + typedef signed char TSigned8; + typedef signed short TSigned16; + typedef signed long TSigned32; + typedef signed long long TSigned64; + + typedef TSigned8 TDelta8; + typedef TSigned16 TDelta16; + typedef TSigned32 TDelta32; + typedef TSigned64 TDelta64; + typedef signed TDelta; + + typedef TUnsigned8 TSize8; + typedef TUnsigned16 TSize16; + typedef TUnsigned32 TSize32; + typedef TUnsigned64 TSize64; + typedef unsigned TSize; + + typedef TUnsigned8 TIndex8; + typedef TUnsigned16 TIndex16; + typedef TUnsigned32 TIndex32; + typedef TUnsigned64 TIndex64; + typedef unsigned TIndex; + + typedef TUnsigned8 TByte; + + + + + int ixlibGetMajorVersion(); + int ixlibGetMinorVersion(); + int ixlibGetMicroVersion(); + + void ixlibInitI18n(); + + + + + #ifdef __cplusplus + } + } + #endif + + + + +#endif diff --git a/simgear/interpreter/ixlib_exbase.hh b/simgear/interpreter/ixlib_exbase.hh new file mode 100644 index 00000000..0d26c93d --- /dev/null +++ b/simgear/interpreter/ixlib_exbase.hh @@ -0,0 +1,72 @@ +// ---------------------------------------------------------------------------- +// Description : Exception handling +// ---------------------------------------------------------------------------- +// (c) Copyright 1996 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_EXBASE +#define IXLIB_EXBASE + + + + +#include +#include + + + + +// constants ------------------------------------------------------------------ +#define EX_INFOMAX 255 + + + + +// throw macro ---------------------------------------------------------------- +#define EX_THROW(TYPE,CODE)\ + throw ::ixion::TYPE##_exception(CODE,NULL,__FILE__,__LINE__); +#define EX_THROWINFO(TYPE,CODE,INFO)\ + throw ::ixion::TYPE##_exception(CODE,(char const *) INFO,__FILE__,__LINE__); +#define EX_CATCHCODE(TYPE,CODE,HANDLER)\ + catch (TYPE##_exception &ex) { \ + if (ex.Error != CODE) throw; \ + HANDLER \ + } +#define EX_CONVERT(TYPE,CODE,DESTTYPE,DESTCODE)\ + catch (TYPE##_exception &ex) { \ + if (ex.Error != CODE) throw; \ + throw DESTTYPE##_exception(DESTCODE,ex.Info,__FILE__,__LINE__); \ + } + + + + +// xBaseException ------------------------------------------------------------- +namespace ixion { + typedef unsigned int TErrorCode; + + + + + struct base_exception : public std::exception { + TErrorCode Error; + char *Module; + TIndex Line; + char *Category; + bool HasInfo; + char Info[EX_INFOMAX+1]; + static char RenderBuffer[EX_INFOMAX+1+100]; + + base_exception(TErrorCode error,char const *info = NULL,char *module = NULL, + TIndex line = 0,char *category = NULL); + char const *what() const throw (); + virtual const char *getText() const = 0; + }; + } + + + +#endif diff --git a/simgear/interpreter/ixlib_exgen.hh b/simgear/interpreter/ixlib_exgen.hh new file mode 100644 index 00000000..54d7fc23 --- /dev/null +++ b/simgear/interpreter/ixlib_exgen.hh @@ -0,0 +1,67 @@ +// ---------------------------------------------------------------------------- +// Description : Generic exceptions +// ---------------------------------------------------------------------------- +// (c) Copyright 1996 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_EXGEN +#define IXLIB_EXGEN + + + + +#include + + + + +// Error codes ---------------------------------------------------------------- +#define EC_CANNOTEVALUATE 0 +#define EC_NOTYETIMPLEMENTED 1 +#define EC_ERROR 2 +#define EC_NULLPOINTER 3 +#define EC_INVALIDPAR 4 +#define EC_INDEX 5 +#define EC_BUFFEROVERFLOW 6 +#define EC_BUFFERUNDERFLOW 7 +#define EC_ITEMNOTFOUND 8 +#define EC_INVALIDOP 9 +#define EC_DIMENSIONMISMATCH 10 +#define EC_CANCELLED 11 +#define EC_EMPTYSET 12 +#define EC_CANNOTREMOVEFROMGC 13 +#define EC_REMAININGREF 14 + +#define ECMEM_GENERAL 0 + + + +// Throw macro ---------------------------------------------------------------- +#define EXGEN_THROW(CODE)\ + EX_THROW(generic,CODE) +#define EXGEN_THROWINFO(CODE,INFO)\ + EX_THROWINFO(generic,CODE,INFO) +#define EXGEN_NYI\ + EXGEN_THROW(EC_NOTYETIMPLEMENTED) + + + + +namespace ixion { +// generic_exception ---------------------------------------------------------- + struct generic_exception : public base_exception { + generic_exception(TErrorCode error,char const *info = NULL,char *module = NULL, + TIndex line = 0) + : base_exception(error,info,module,line,"GEN") { + } + virtual char const *getText() const; + }; + } + + + + +#endif diff --git a/simgear/interpreter/ixlib_garbage.hh b/simgear/interpreter/ixlib_garbage.hh new file mode 100644 index 00000000..80584b75 --- /dev/null +++ b/simgear/interpreter/ixlib_garbage.hh @@ -0,0 +1,491 @@ +// ---------------------------------------------------------------------------- +// Description : Garbage collection +// ---------------------------------------------------------------------------- +// (c) Copyright 2000 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_GARBAGE +#define IXLIB_GARBAGE + + + + +#include +#include +#include + + + + +namespace ixion { + template + class delete_deallocator { + public: + void operator()(T const *instance) { + delete instance; + } + }; + template + class delete_array_deallocator { + public: + void operator()(T const *instance) { + delete[] instance; + } + }; + + template > + class reference_manager; + + template + class ref_base { + protected: + T *Instance; + + public: + ref_base(T *instance = NULL) + : Instance(instance) { + } + ref_base(ref_base const &src) + : Instance(src.Instance) { + } + + // comparison + bool operator==(ref_base const &op2) const { + return Instance == op2.Instance; + } + + // smart pointer nitty-gritty + T &operator*() const { + return *Instance; + } + T *operator->() const { + return Instance; + } + T *operator+(TIndex index) const { + return Instance + index; + } + T &operator[](TIndex index) const { + return Instance[index]; + } + + // methods + T *get() const { + return Instance; + } + }; + + + + + template + class ref; + template + class no_free_ref; + + + + + template + class reference_manager_keeper { + public: + // *** FIXME should be private, but cannot be + // (partial specializations cannot be declared friends) + static reference_manager Manager; + }; + + + + + + /** + An object that acts like a reference-counted pointer to an object. + The corresponding reference_manager is identified implicitly through + static references. + + Example: + + IXLIB_GARBAGE_DECLARE_MANAGER(int) + + int main() { + ref my_int = new int(5); + *my_int = 17; + ref another_int = my_int; + *another_int = 12; + + *my_int == 12; // true + } + + */ + template + class ref : public ref_base { + public: + // we have to have an explicit copy constructor, otherwise the + // compiler generates one, which is *ahem* - fatal + ref(ref const &src) + : ref_base(src) { + reference_manager_keeper::Manager.addReference(Instance); + } + template + ref(ref const &src) + : ref_base(src.get()) { + reference_manager_keeper::Manager.addReference(Instance); + } + template + ref(no_free_ref const &src) + : ref_base(src.get()) { + reference_manager_keeper::Manager.addReference(Instance); + } + ref(T *instance = NULL) + : ref_base(instance) { + reference_manager_keeper::Manager.addReference(Instance); + } + ~ref() { + reference_manager_keeper::Manager.freeReference(Instance); + } + + ref &operator=(ref const &src) { + set(src.get()); + return *this; + } + ref &operator=(T *ptr) { + set(ptr); + return *this; + } + + // methods + void release() { + reference_manager_keeper::Manager.freeReference(Instance); + Instance = NULL; + } + void set(T *instance) { + if (instance == Instance) return; + + reference_manager_keeper::Manager.freeReference(Instance); + Instance = instance; + reference_manager_keeper::Manager.addReference(Instance); + } + T *releaseFromGCArena() { + T *oldinst = Instance; + reference_manager_keeper::Manager.forgetReference(Instance); + Instance = NULL; + return oldinst; + } + }; + + + + + /** + An object that acts like a reference-counted pointer to an object. + However, the referenced object is not freed if the no_free_ref + is the last reference to the object to go out of scope. + + This is useful to pass objects allocated e.g. on the stack along + inside ref's, while making sure they aren't freed. + (which would most probably lead to disaster) + + no_free_ref's are mostly a hack, but there are situations where you cannot + avoid them. But even so, you should try not to use them where possible. + + The corresponding reference_manager is identified implicitly through + static references. + */ + template + class no_free_ref : public ref_base{ + public: + // we have to have an explicit copy constructor, otherwise the + // compiler generates one, which is *ahem* - fatal + no_free_ref(no_free_ref const &src) + : ref_base(src) { + reference_manager_keeper::Manager.addNoFreeReference(Instance); + } + template + no_free_ref(ref const &src) + : ref_base(src.get()) { + reference_manager_keeper::Manager.addNoFreeReference(Instance); + } + template + no_free_ref(no_free_ref const &src) + : ref_base(src.get()) { + reference_manager_keeper::Manager.addNoFreeReference(Instance); + } + no_free_ref(T *instance = NULL) + : ref_base(instance) { + reference_manager_keeper::Manager.addNoFreeReference(Instance); + } + ~no_free_ref() { + reference_manager_keeper::Manager.removeNoFreeReference(Instance); + } + + // assignment + no_free_ref &operator=(no_free_ref const &src) { + set(src.get()); + return *this; + } + no_free_ref &operator=(T *ptr) { + set(ptr); + return *this; + } + + // methods + void release() { + reference_manager_keeper::Manager.removeNoFreeReference(Instance); + Instance = NULL; + } + void set(T *instance) { + if (instance == Instance) return; + + reference_manager_keeper::Manager.removeNoFreeReference(Instance); + Instance = instance; + reference_manager_keeper::Manager.addNoFreeReference(Instance); + } + T *releaseFromGCArena() { + T *oldinst = Instance; + reference_manager_keeper::Manager.forgetReference(Instance); + Instance = NULL; + return oldinst; + } + }; + + + + + /** + An object that acts like a reference-counted pointer to an object. + The corresponding reference_manager is identified explicitly. + */ + template + class dynamic_ref : public ref_base { + protected: + reference_manager &Manager; + + public: + dynamic_ref(dynamic_ref const &src) + : ref_base(src),Manager(src.Manager) { + Manager.addReference(Instance); + } + dynamic_ref(reference_manager &mgr,T *instance = NULL) + : ref_base(instance),Manager(mgr) { + Manager.addReference(Instance); + } + ~dynamic_ref() { + Manager.freeReference(Instance); + } + + // assignment + dynamic_ref &operator=(dynamic_ref const &src) { + set(src.get()); + return *this; + } + dynamic_ref &operator=(T *ptr) { + set(ptr); + return *this; + } + + // methods + void release() { + Manager.freeReference(Instance); + Instance = NULL; + } + void set(T *instance) { + if (instance == Instance) return; + + Manager.freeReference(Instance); + Instance = instance; + Manager.addReference(Instance); + } + T *releaseFromGCArena() { + T *oldinst = Instance; + Manager.forgetReference(Instance); + Instance = NULL; + return oldinst; + } + }; + + + + + /** + An object that acts like a reference-counted pointer to an object. + However, the referenced object is not freed if the no_free_ref + is the last reference to the object to go out of scope. + + This is useful to pass objects allocated e.g. on the stack along + inside ref's, while making sure they aren't freed. + (which would most probably lead to disaster) + + no_free_ref's are mostly a hack, but there are situations where you cannot + avoid them. But even so, you should try not to use them where possible. + + The corresponding reference_manager is identified explicitly. + */ + template + class no_free_dynamic_ref : public ref_base { + protected: + reference_manager &Manager; + + public: + no_free_dynamic_ref(no_free_dynamic_ref const &src) + : ref_base(src),Manager(src.Manager) { + Manager.addNoFreeReference(Instance); + } + no_free_dynamic_ref(reference_manager &mgr,T *instance = NULL) + : ref_base(instance),Manager(mgr) { + Manager.addNoFreeReference(Instance); + } + ~no_free_dynamic_ref() { + Manager.removeNoFreeReference(Instance); + } + + // assignment + no_free_dynamic_ref &operator=(no_free_dynamic_ref const &src) { + set(src.get()); + return *this; + } + no_free_dynamic_ref &operator=(T *ptr) { + set(ptr); + return *this; + } + + // methods + void release() { + Manager.removeNoFreeReference(Instance); + Instance = NULL; + } + void set(T *instance) { + if (instance == Instance) return; + + Manager.removeNoFreeReference(Instance); + Instance = instance; + Manager.addNoFreeReference(Instance); + } + T *releaseFromGCArena() { + T *oldinst = Instance; + Manager.forgetReference(Instance); + Instance = NULL; + return oldinst; + } + }; + + + + + template + class reference_manager { + protected: + + struct instance_data { + T const *Instance; + TSize ReferenceCount,NoFreeReferenceCount; + instance_data *Next,*Previous; + }; + + class pointer_hash { + public: + }; + + typedef unsigned hash_value; + static hash_value const HASH_MAX = 0x3ff; + + instance_data *Instances[HASH_MAX+1]; + Deallocator Dealloc; + + public: + reference_manager(Deallocator const &dealloc = Deallocator()) + : Dealloc(dealloc) { + for (hash_value hv = 0;hv <= HASH_MAX;hv++) + Instances[hv] = NULL; + } + + // *** FIXME should be + // protected: + // but cannot because partial specializations cannot be declared friends + void addReference(T const *instance) { + if (!instance) return; + instance_data *data = getHashEntry(instance); + data->ReferenceCount++; + } + void freeReference(T const *instance) { + if (!instance) return; + instance_data *data = getHashEntry(instance); + if (--data->ReferenceCount == 0 && data->NoFreeReferenceCount == 0) { + removeHashEntry(data); + Dealloc(instance); + } + } + void addNoFreeReference(T const *instance) { + if (!instance) return; + instance_data *data = getHashEntry(instance); + data->NoFreeReferenceCount++; + } + void removeNoFreeReference(T const *instance) { + if (!instance) return; + instance_data *data = getHashEntry(instance); + if (--data->NoFreeReferenceCount == 0) { + if (data->ReferenceCount != 0) + EXGEN_THROW(EC_REMAININGREF) + removeHashEntry(data); + } + } + void forgetReference(T const *instance) { + if (!instance) return; + instance_data *data = getHashEntry(instance); + if (data->ReferenceCount != 1) + EXGEN_THROW(EC_CANNOTREMOVEFROMGC) + removeHashEntry(data); + } + + private: + hash_value hash(T const *ptr) const { + unsigned u = reinterpret_cast(ptr); + return (u ^ (u >> 8) ^ (u >> 16) ^ (u >> 24)) & HASH_MAX; + } + instance_data *getHashEntry(T const *instance) { + instance_data *data = Instances[hash(instance)]; + while (data) { + if (data->Instance == instance) return data; + data = data->Next; + } + + // not found, add new at front + instance_data *link = Instances[hash(instance)]; + data = new instance_data; + + data->Instance = instance; + data->ReferenceCount = 0; + data->NoFreeReferenceCount = 0; + data->Previous = NULL; + data->Next = link; + if (link) link->Previous = data; + Instances[hash(instance)] = data; + return data; + } + void removeHashEntry(instance_data *data) { + instance_data *prev = data->Previous; + if (prev) { + prev->Next = data->Next; + if (data->Next) data->Next->Previous = prev; + delete data; + } + else { + Instances[hash(data->Instance)] = data->Next; + if (data->Next) data->Next->Previous = NULL; + delete data; + } + } + }; + + + + + #define IXLIB_GARBAGE_DECLARE_MANAGER(TYPE) \ + ixion::reference_manager ixion::reference_manager_keeper::Manager; + } + + + + +#endif diff --git a/simgear/interpreter/ixlib_i18n.hh b/simgear/interpreter/ixlib_i18n.hh new file mode 100644 index 00000000..ad121dda --- /dev/null +++ b/simgear/interpreter/ixlib_i18n.hh @@ -0,0 +1,23 @@ +// ---------------------------------------------------------------------------- +// Description : ixlib internationalization wrapper +// ---------------------------------------------------------------------------- +// (c) Copyright 2001 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_I18N + + + + +#include +#include +#define _(String) gettext(String) +#define N_(String) (String) + + + + +#endif diff --git a/simgear/interpreter/ixlib_javascript.hh b/simgear/interpreter/ixlib_javascript.hh new file mode 100644 index 00000000..f5df525b --- /dev/null +++ b/simgear/interpreter/ixlib_javascript.hh @@ -0,0 +1,380 @@ +// ---------------------------------------------------------------------------- +// Description : Javascript interpreter +// ---------------------------------------------------------------------------- +// (c) Copyright 2000 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_JAVASCRIPT +#define IXLIB_JAVASCRIPT + + + + +#include +#if __GNUC__ < 3 + #include +#else + #include +#endif +#include +#include +#include +#include + + + + +// Error codes ---------------------------------------------------------------- +#define ECJS_UNTERMINATED_COMMENT 0 +#define ECJS_CANNOT_CONVERT 1 +#define ECJS_INVALID_OPERATION 2 +#define ECJS_UNEXPECTED 3 +#define ECJS_UNEXPECTED_EOF 4 +#define ECJS_CANNOT_MODIFY_RVALUE 5 +#define ECJS_UNKNOWN_IDENTIFIER 6 +#define ECJS_UNKNOWN_OPERATOR 7 +#define ECJS_INVALID_NON_LOCAL_EXIT 8 +#define ECJS_INVALID_NUMBER_OF_ARGUMENTS 9 +#define ECJS_INVALID_TOKEN 10 +#define ECJS_CANNOT_REDECLARE 11 +#define ECJS_DOUBLE_CONSTRUCTION 12 +#define ECJS_NO_SUPERCLASS 13 +#define ECJS_DIVISION_BY_ZERO 14 + + + + +// helpful macros ------------------------------------------------------------- +#define IXLIB_JS_ASSERT_PARAMETERS(NAME,ARGMIN,ARGMAX) \ + if (parameters.size() < ARGMIN || parameters.size() > ARGMAX) \ + EXJS_THROWINFO(ECJS_INVALID_NUMBER_OF_ARGUMENTS,NAME) + +#define IXLIB_JS_IF_METHOD(NAME,ARGMIN,ARGMAX) \ + if (identifier == NAME) \ + if (parameters.size() < ARGMIN || parameters.size() > ARGMAX) \ + EXJS_THROWINFO(ECJS_INVALID_NUMBER_OF_ARGUMENTS,NAME) \ + else + +#define IXLIB_JS_DECLARE_FUNCTION(NAME) \ + namespace { \ + class NAME : public value { \ + public: \ + value_type getType() const { \ + return VT_FUNCTION; \ + } \ + ixion::ref call(parameter_list const ¶meters); \ + }; \ + } \ + ixion::ref NAME::call(parameter_list const ¶meters) + +#define IXLIB_JS_CONVERT_PARAMETERS_0 \ + + + + +// Exception throw macros ----------------------------------------------------- +#define EXJS_THROW(CODE)\ + EX_THROW(javascript,CODE) +#define EXJS_THROWINFO(CODE,INFO)\ + EX_THROWINFO(javascript,CODE,INFO) +#define EXJS_THROW_NO_LOCATION(CODE)\ + EX_THROW(no_location_javascript,CODE) +#define EXJS_THROWINFO_NO_LOCATION(CODE,INFO)\ + EX_THROWINFO(no_location_javascript,CODE,INFO) +#define EXJS_THROWINFOLOCATION(CODE,INFO,LOCATION)\ + throw ixion::javascript_exception(CODE,LOCATION,INFO,__FILE__,__LINE__); +#define EXJS_THROWINFOTOKEN(CODE,INFO,TOKEN)\ + EXJS_THROWINFOLOCATION(CODE,INFO,code_location(TOKEN)) +#define EXJS_THROWINFOEXPRESSION(CODE,INFO,EXPR)\ + EXJS_THROWINFOLOCATION(CODE,INFO,(EXPR).getCodeLocation()) + + + + + +namespace ixion { + namespace javascript { + struct code_location; + } + + // exceptions --------------------------------------------------------------- + struct no_location_javascript_exception : public base_exception { + no_location_javascript_exception(TErrorCode error,char const *info = NULL,char *module = NULL, + TIndex line = 0) + : base_exception(error,info,module,line,"JS") { + } + virtual char *getText() const; + }; + + + + + struct javascript_exception : public base_exception { + javascript_exception(TErrorCode error,char const *info = NULL,char *module = NULL, + TIndex line = 0) + : base_exception(error,info,module,line,"JS") { + } + javascript_exception(TErrorCode error,javascript::code_location const &loc,char const *info = 0,char *module = NULL, + TIndex line = 0); + javascript_exception(no_location_javascript_exception const &half_ex,javascript::code_location const &loc); + virtual char *getText() const; + }; + + + + + // javascript --------------------------------------------------------------- + /** + This code tries to be an implementation of ECMAScript 4, as available at + http://www.mozilla.org/js/language/ + Note that ES4 is still in the process of standardization. + + It is meant to behave like an ES4 interpreter in strict mode, none + of the backward-compatible braindead-isms like newline semicolon + insertion and other stuff will ever be implemented. + + This is the list of its shortcomings: +
    +
  • exceptions +
  • namespaces,packages +
  • constness +
  • Number/String constructor and class methods +
  • real regexp's +
  • the methods listed in FIXME's (js_library.cc js_value.cc) +
  • cannot cross-assign predefined methods [won't be] +
  • Grammatical semicolon insertion [won't be] +
  • type declaration [won't be] +
+ + Be advised that a javascript value that is passed to you through the + interpreter, e.g. as a call parameter, may not be of the type that + you expect. For example, in "var x = 4; f(x);", what comes in as + the parameter x into f is a wrapper value that adds assign()ability + to a value that is wrapped inside. The advised solution to get the + object that you expect is to call eliminateWrappers() on the potentially + wrapped value. + */ + namespace javascript { + class value; + class list_scope; + struct context { + ref DeclarationScope; + ref LookupScope; + + context(ref scope); + context(ref scope); + context(ref decl_scope,ref lookup_scope); + }; + + class expression; + + class value { + public: + enum operator_id { + // unary, modifying + OP_PRE_INCREMENT,OP_POST_INCREMENT, + OP_PRE_DECREMENT,OP_POST_DECREMENT, + // unary, non-modifying + OP_UNARY_PLUS,OP_UNARY_MINUS, + OP_LOG_NOT,OP_BIN_NOT, + // binary, modifying + OP_PLUS_ASSIGN,OP_MINUS_ASSIGN, + OP_MUTLIPLY_ASSIGN,OP_DIVIDE_ASSIGN,OP_MODULO_ASSIGN, + OP_BIT_AND_ASSIGN,OP_BIT_OR_ASSIGN,OP_BIT_XOR_ASSIGN, + OP_LEFT_SHIFT_ASSIGN,OP_RIGHT_SHIFT_ASSIGN, + // binary, non-modifying + OP_PLUS,OP_MINUS, + OP_MULTIPLY,OP_DIVIDE,OP_MODULO, + OP_BIT_AND,OP_BIT_OR,OP_BIT_XOR, + OP_LEFT_SHIFT,OP_RIGHT_SHIFT, + OP_LOGICAL_OR,OP_LOGICAL_AND, + OP_EQUAL,OP_NOT_EQUAL,OP_IDENTICAL,OP_NOT_IDENTICAL, + OP_LESS_EQUAL,OP_GREATER_EQUAL,OP_LESS,OP_GREATER, + // special + OP_ASSIGN, + }; + + enum value_type { + VT_UNDEFINED,VT_NULL, + VT_INTEGER,VT_FLOATING_POINT,VT_STRING, + VT_FUNCTION,VT_OBJECT,VT_BUILTIN,VT_HOST, + VT_SCOPE,VT_BOUND_METHOD,VT_TYPE + }; + typedef std::vector > parameter_list; + + virtual ~value() { + } + + virtual value_type getType() const = 0; + virtual std::string toString() const; + virtual int toInt() const; + virtual double toFloat() const; + virtual bool toBoolean() const; + // toString is meant as a type conversion, whereas stringify + // is for debuggers and the like + virtual std::string stringify() const; + + virtual ref eliminateWrappers(); + virtual ref duplicate(); + + virtual ref lookup(std::string const &identifier); + virtual ref subscript(value const &index); + virtual ref call(parameter_list const ¶meters); + virtual ref callAsMethod(ref instance,parameter_list const ¶meters); + virtual ref construct(parameter_list const ¶meters); + virtual ref assign(ref op2); + + virtual ref operatorUnary(operator_id op) const; + virtual ref operatorBinary(operator_id op,ref op2) const; + virtual ref operatorBinaryShortcut(operator_id op,expression const &op2,context const &ctx) const; + virtual ref operatorUnaryModifying(operator_id op); + virtual ref operatorBinaryModifying(operator_id op,ref op2); + + static operator_id token2operator(scanner::token const &token,bool unary = false,bool prefix = false); + static std::string operator2string(operator_id op); + static std::string valueType2string(value_type vt); + }; + + // obviously, any value can have methods, but with this neat little + // interface implementing methods has just become easier. + class value_with_methods : public value { + class bound_method : public value { + std::string Identifier; + ref Parent; + + public: + bound_method(std::string const &identifier,ref parent); + + value_type getType() const { + return VT_BOUND_METHOD; + } + + ref duplicate(); + ref call(parameter_list const ¶meters); + }; + + public: + ref lookup(std::string const &identifier); + virtual ref callMethod(std::string const &identifier,parameter_list const ¶meters) = 0; + }; + + // obviously, any value can already represent a scope ("lookup" member!). + // the list_scope class is an explicit scope that can "swallow" + // (=unite with) other scopes and keeps a list of registered members + class list_scope : public value { + protected: + typedef std::hash_map,string_hash> member_map; + typedef std::vector > swallowed_list; + + member_map MemberMap; + swallowed_list SwallowedList; + + public: + value_type getType() const { + return VT_SCOPE; + } + + ref lookup(std::string const &identifier); + + void unite(ref scope); + void separate(ref scope); + void clearScopes(); + + bool hasMember(std::string const &name) const; + void addMember(std::string const &name,ref member); + void removeMember(std::string const &name); + void clearMembers(); + + void clear(); + }; + + class js_array : public value_with_methods { + private: + typedef value_with_methods super; + + protected: + typedef std::vector > value_array; + value_array Array; + + public: + js_array() { + } + js_array(TSize size); + js_array(value_array::const_iterator first,value_array::const_iterator last) + : Array(first,last) { + } + js_array(js_array const &src) + : Array(src.Array) { + } + + value_type getType() const { + return VT_BUILTIN; + } + + std::string stringify() const; + + ref duplicate(); + + ref lookup(std::string const &identifier); + ref subscript(value const &index); + ref callMethod(std::string const &identifier,parameter_list const ¶meters); + + TSize size() const { + return Array.size(); + } + void resize(TSize size); + ref &operator[](TIndex idx); + void push_back(ref val); + }; + + class expression; + + ref makeUndefined(); + ref makeNull(); + ref makeValue(signed long val); + ref makeConstant(signed long val); + ref makeValue(signed int val); + ref makeConstant(signed int val); + ref makeValue(unsigned long val); + ref makeConstant(unsigned long val); + ref makeValue(unsigned int val); + ref makeConstant(unsigned int val); + ref makeValue(double val); + ref makeConstant(double val); + ref makeValue(std::string const &val); + ref makeConstant(std::string const &val); + ref makeArray(TSize size = 0); + ref makeLValue(ref target); + ref wrapConstant(ref val); + + class interpreter { + public: + ref RootScope; + + public: + interpreter(); + ~interpreter(); + + ref parse(std::string const &str); + ref parse(std::istream &istr); + ref execute(std::string const &str); + ref execute(std::istream &istr); + ref execute(ref expr); + + private: + ref evaluateCatchExits(ref expr); + }; + + void addGlobal(interpreter &ip); + void addMath(interpreter &ip); + void addStandardLibrary(interpreter &ip); + } + } + + + + +#endif diff --git a/simgear/interpreter/ixlib_js_internals.hh b/simgear/interpreter/ixlib_js_internals.hh new file mode 100644 index 00000000..f1c1642c --- /dev/null +++ b/simgear/interpreter/ixlib_js_internals.hh @@ -0,0 +1,760 @@ +// ---------------------------------------------------------------------------- +// Description : Javascript interpreter +// ---------------------------------------------------------------------------- +// (c) Copyright 2000 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_JS_INTERNALS +#define IXLIB_JS_INTERNALS + + + + +#include + + + + +using namespace std; + + + + +namespace ixion { + namespace javascript { + struct code_location { + TIndex Line; + + code_location(scanner::token &tok); + explicit code_location(TIndex line); + string stringify() const; + }; + + struct return_exception { + ref ReturnValue; + code_location Location; + + return_exception(ref retval,code_location const &loc) + : ReturnValue(retval),Location(loc) { + } + }; + + struct break_exception { + bool HasLabel; + string Label; + code_location Location; + + break_exception(bool has_label,string const &label,code_location const &loc) + : HasLabel(has_label),Label(label),Location(loc) { + } + }; + + struct continue_exception { + bool HasLabel; + string Label; + code_location Location; + + continue_exception(bool has_label,string const &label,code_location const &loc) + : HasLabel(has_label),Label(label),Location(loc) { + } + }; + + // values ----------------------------------------------------------------- + class null : public value { + private: + typedef value super; + + public: + value_type getType() const; + bool toBoolean() const; + + ref duplicate(); + }; + + class const_floating_point : public value_with_methods { + private: + typedef value_with_methods super; + + protected: + double Value; + + public: + const_floating_point(double value); + + value_type getType() const; + int toInt() const; + double toFloat() const; + bool toBoolean() const; + string toString() const; + + ref duplicate(); + + ref callMethod(string const &identifier,parameter_list const ¶meters); + + ref operatorUnary(operator_id op) const; + ref operatorBinary(operator_id op,ref op2) const; + }; + + class floating_point : public const_floating_point { + private: + typedef const_floating_point super; + + public: + floating_point(double value); + + ref operatorUnaryModifying(operator_id op); + ref operatorBinaryModifying(operator_id op,ref op2); + }; + + class const_integer : public value_with_methods { + private: + typedef value_with_methods super; + + protected: + long Value; + + public: + const_integer(long value); + + value_type getType() const; + int toInt() const; + double toFloat() const; + bool toBoolean() const; + string toString() const; + + ref duplicate(); + + ref callMethod(string const &identifier,parameter_list const ¶meters); + + ref operatorUnary(operator_id op) const; + ref operatorBinary(operator_id op,ref op2) const; + }; + + class integer : public const_integer { + private: + typedef const_integer super; + + public: + integer(long value); + + ref operatorUnaryModifying(operator_id op); + ref operatorBinaryModifying(operator_id op,ref op2); + }; + + class js_string : public value_with_methods { + private: + typedef value_with_methods super; + + protected: + string Value; + + public: + js_string(string const &value); + + value_type getType() const; + string toString() const; + bool toBoolean() const; + string stringify() const; + + ref duplicate(); + + ref lookup(string const &identifier); + ref callMethod(string const &identifier,parameter_list const ¶meters); + + ref operatorBinary(operator_id op,ref op2) const; + ref operatorBinaryModifying(operator_id op,ref op2); + }; + + class lvalue : public value { + protected: + ref Reference; + + public: + lvalue(ref ref); + + value_type getType() const; + string toString() const; + int toInt() const; + double toFloat() const; + bool toBoolean() const; + string stringify() const; + + ref eliminateWrappers(); + ref duplicate(); + + ref lookup(string const &identifier); + ref subscript(value const &index); + ref call(parameter_list const ¶meters); + ref callAsMethod(ref instance,parameter_list const ¶meters); + ref construct(parameter_list const ¶meters); + ref assign(ref op2); + + ref operatorUnary(operator_id op) const; + ref operatorBinary(operator_id op,ref op2) const; + ref operatorBinaryShortcut(operator_id op,expression const &op2,context const &ctx) const; + ref operatorUnaryModifying(operator_id op); + ref operatorBinaryModifying(operator_id op,ref op2); + }; + + class constant_wrapper : public value { + protected: + ref Constant; + + public: + constant_wrapper(ref val); + + value_type getType() const; + string toString() const; + int toInt() const; + double toFloat() const; + bool toBoolean() const; + string stringify() const; + + ref eliminateWrappers(); + ref duplicate(); + + ref lookup(string const &identifier); + ref subscript(value const &index); + ref call(parameter_list const ¶meters) const; + ref callAsMethod(ref instance,parameter_list const ¶meters); + ref construct(parameter_list const ¶meters); + ref assign(ref value); + + ref operatorUnary(operator_id op) const; + ref operatorBinary(operator_id op,ref op2) const; + ref operatorBinaryShortcut(operator_id op,expression const &op2,context const &ctx) const; + ref operatorUnaryModifying(operator_id op); + ref operatorBinaryModifying(operator_id op,ref op2); + }; + + class callable_with_parameters : public value { + public: + typedef vector parameter_name_list; + + protected: + parameter_name_list ParameterNameList; + + public: + callable_with_parameters(parameter_name_list const &pnames); + + void addParametersTo(list_scope &scope,parameter_list const ¶meters) const; + static ref evaluateBody(expression &body,context const &ctx); + }; + + class function : public callable_with_parameters { + typedef callable_with_parameters super; + ref Body; + ref LexicalScope; + + public: + function(parameter_name_list const &pnames,ref body,ref lex_scope); + + value_type getType() const{ + return VT_FUNCTION; + } + + ref duplicate(); + + ref call(parameter_list const ¶meters); + }; + + class method : public callable_with_parameters { + typedef callable_with_parameters super; + ref Body; + ref LexicalScope; + + public: + method(parameter_name_list const &pnames,ref body,ref lex_scope); + + value_type getType() const{ + return VT_FUNCTION; + } + + ref duplicate(); + + ref callAsMethod(ref instance,parameter_list const ¶meters); + }; + + class constructor : public callable_with_parameters { + typedef callable_with_parameters super; + ref Body; + ref LexicalScope; + + public: + constructor(parameter_name_list const &pnames,ref body,ref lex_scope); + + value_type getType() const{ + return VT_FUNCTION; + } + + ref duplicate(); + ref callAsMethod(ref instance,parameter_list const ¶meters); + }; + + class js_class : public value { + class super_instance_during_construction : public value { + // this object constructs the superclass + // a) if it is called, by calling the super constructor + // with the aprropriate parameters + // b) implicitly with no super constructor arguments, + // if the super object is referenced explicitly + + ref SuperClass; + ref SuperClassInstance; + + public: + super_instance_during_construction(ref super_class); + + value_type getType() const { + return VT_OBJECT; + } + + ref call(parameter_list const ¶meters); + ref lookup(string const &identifier); + + ref getSuperClassInstance(); + }; + + typedef vector > declaration_list; + + ref LexicalScope; + ref SuperClass; + ref Constructor; + ref StaticMethodScope; + ref MethodScope; + ref StaticVariableScope; + declaration_list VariableList; + + public: + js_class(ref lex_scope,ref super_class,ref constructor, + ref static_method_scope,ref method_scope, + ref static_variable_scope,declaration_list const &variable_list); + + value_type getType() const { + return VT_TYPE; + } + + ref duplicate(); + ref lookup(string const &identifier); + ref lookupLocal(string const &identifier); + ref construct(parameter_list const ¶meters); + }; + + class js_class_instance : public value { + class bound_method : public value { + ref Instance; + ref Method; + + public: + bound_method(ref instance,ref method); + + value_type getType() const { + return VT_BOUND_METHOD; + } + + ref call(parameter_list const ¶meters); + }; + + ref SuperClassInstance; + ref Class; + ref MethodScope; + ref VariableScope; + + public: + js_class_instance(ref cls,ref method_scope, + ref variable_scope); + + void setSuperClassInstance(ref super_class_instance); + + value_type getType() const { + return VT_OBJECT; + } + + ref duplicate(); + ref lookup(string const &identifier); + }; + + class js_array_constructor : public value { + public: + value_type getType() const { + return VT_TYPE; + } + + ref duplicate(); + ref construct(parameter_list const ¶meters); + }; + + // expressions ---------------------------------------------------------- + class expression { + code_location Location; + + public: + expression(code_location const &loc); + virtual ~expression(); + virtual ref evaluate(context const &ctx) const = 0; + + code_location const &getCodeLocation() const { + return Location; + } + }; + + class constant : public expression { + ref Value; + public: + constant(ref val,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class unary_operator : public expression { + value::operator_id Operator; + ref Operand; + + public: + unary_operator(value::operator_id opt,ref opn,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class modifying_unary_operator : public expression { + value::operator_id Operator; + ref Operand; + + public: + modifying_unary_operator(value::operator_id opt,ref opn,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class binary_operator : public expression { + value::operator_id Operator; + ref Operand1; + ref Operand2; + + public: + binary_operator(value::operator_id opt,ref opn1,ref opn2,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class binary_shortcut_operator : public expression { + value::operator_id Operator; + ref Operand1; + ref Operand2; + + public: + binary_shortcut_operator(value::operator_id opt,ref opn1,ref opn2,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class modifying_binary_operator : public expression { + value::operator_id Operator; + ref Operand1; + ref Operand2; + + public: + modifying_binary_operator(value::operator_id opt,ref opn1,ref opn2,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class ternary_operator : public expression { + ref Operand1; + ref Operand2; + ref Operand3; + + public: + ternary_operator(ref opn1,ref opn2,ref opn3,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class subscript_operation : public expression { + ref Operand1; + ref Operand2; + + public: + subscript_operation(ref opn1,ref opn2,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class lookup_operation : public expression { + ref Operand; + string Identifier; + + public: + lookup_operation(string const &id,code_location const &loc); + lookup_operation(ref opn,string const &id,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class assignment : public expression { + ref Operand1; + ref Operand2; + + public: + assignment(ref opn1,ref opn2,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class basic_call : public expression { + public: + typedef vector > parameter_expression_list; + typedef vector > parameter_value_list; + + private: + parameter_expression_list ParameterExpressionList; + + public: + basic_call(parameter_expression_list const &pexps,code_location const &loc); + void makeParameterValueList(context const &ctx,parameter_value_list &pvalues) const; + }; + + class function_call : public basic_call { + typedef basic_call super; + ref Function; + + public: + function_call(ref fun,parameter_expression_list const &pexps,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class construction : public basic_call { + typedef basic_call super; + ref Class; + + public: + construction(ref cls,parameter_expression_list const &pexps,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + // declarations ----------------------------------------------------------- + class variable_declaration : public expression { + protected: + string Identifier; + ref DefaultValue; + + public: + variable_declaration(string const &id,ref def_value,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class constant_declaration : public expression { + protected: + string Identifier; + ref DefaultValue; + + public: + constant_declaration(string const &id,ref def_value,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class function_declaration : public expression { + public: + typedef function::parameter_name_list parameter_name_list; + + private: + string Identifier; + parameter_name_list ParameterNameList; + ref Body; + + public: + function_declaration(string const &id,parameter_name_list const &pnames, + ref body,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class method_declaration : public expression { + public: + typedef method::parameter_name_list parameter_name_list; + + private: + string Identifier; + parameter_name_list ParameterNameList; + ref Body; + + public: + method_declaration(string const &id,parameter_name_list const &pnames, + ref body,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class constructor_declaration : public expression { + public: + typedef method::parameter_name_list parameter_name_list; + + private: + parameter_name_list ParameterNameList; + ref Body; + + public: + constructor_declaration(parameter_name_list const &pnames, + ref body,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class js_class_declaration : public expression { + typedef vector > declaration_list; + + string Identifier; + ref SuperClass; + ref ConstructorDeclaration; + declaration_list StaticMethodList; + declaration_list MethodList; + declaration_list StaticVariableList; + declaration_list VariableList; + + public: + js_class_declaration(string const &id,ref superclass, + code_location const &loc); + + ref evaluate(context const &ctx) const; + + void setConstructor(ref decl); + void addStaticMethod(ref decl); + void addMethod(ref decl); + void addStaticVariable(ref decl); + void addVariable(ref decl); + }; + + // instructions --------------------------------------------------------- + class instruction_list : public expression { + typedef vector > expression_list; + expression_list ExpressionList; + + public: + instruction_list(code_location const &loc) + : expression(loc) { + } + ref evaluate(context const &ctx) const; + void add(ref expr); + }; + + class scoped_instruction_list : public instruction_list { + public: + scoped_instruction_list(code_location const &loc) + : instruction_list(loc) { + } + ref evaluate(context const &ctx) const; + }; + + class js_if : public expression { + ref Conditional; + ref IfExpression; + ref IfNotExpression; + + public: + js_if(ref cond,ref ifex,ref ifnotex,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class js_while : public expression { + ref Conditional; + ref LoopExpression; + bool HasLabel; + string Label; + + public: + js_while(ref cond,ref loopex,code_location const &loc); + js_while(ref cond,ref loopex,string const &Label,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class js_do_while : public expression { + ref Conditional; + ref LoopExpression; + bool HasLabel; + string Label; + + public: + js_do_while(ref cond,ref loopex,code_location const &loc); + js_do_while(ref cond,ref loopex,string const &Label,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class js_for : public expression { + ref Initialization; + ref Conditional; + ref Update; + ref LoopExpression; + bool HasLabel; + string Label; + + public: + js_for(ref init,ref cond,ref update,ref loop,code_location const &loc); + js_for(ref init,ref cond,ref update,ref loop,string const &label,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class js_for_in : public expression { + ref Iterator; + ref Array; + ref LoopExpression; + bool HasLabel; + string Label; + + public: + js_for_in(ref iter,ref array,ref loop,code_location const &loc); + js_for_in(ref iter,ref array,ref loop,string const &label,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class js_return : public expression { + ref ReturnValue; + + public: + js_return(ref retval,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class js_break : public expression { + bool HasLabel; + string Label; + + public: + js_break(code_location const &loc); + js_break(string const &label,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class js_continue : public expression { + bool HasLabel; + string Label; + + public: + js_continue(code_location const &loc); + js_continue(string const &label,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class break_label : public expression { + string Label; + ref Expression; + + public: + break_label(string const &label,ref expr,code_location const &loc); + ref evaluate(context const &ctx) const; + }; + + class js_switch : public expression { + bool HasLabel; + string Label; + ref Discriminant; + + struct case_label { + ref DiscriminantValue; + ref Expression; + }; + typedef vector case_list; + case_list CaseList; + + public: + js_switch(ref discriminant,code_location const &loc); + js_switch(ref discriminant,string const &label,code_location const &loc); + ref evaluate(context const &ctx) const; + void addCase(ref dvalue,ref expr); + }; + } + } + + + + +#endif diff --git a/simgear/interpreter/ixlib_numconv.hh b/simgear/interpreter/ixlib_numconv.hh new file mode 100644 index 00000000..e4853572 --- /dev/null +++ b/simgear/interpreter/ixlib_numconv.hh @@ -0,0 +1,62 @@ +// ---------------------------------------------------------------------------- +// Description : Numeric conversions +// ---------------------------------------------------------------------------- +// (c) Copyright 1999 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_NUMCONV +#define IXLIB_NUMCONV + + + + +#include +#include + + + + +// Macros --------------------------------------------------------------------- +#define IXLIB_NUMCHARS "0123456789ABCDEF" + + + + +// Functions ------------------------------------------------------------------ +namespace ixion { + std::string float2dec(double value); + std::string float2dec(double value,unsigned int precision); + std::string unsigned2base(unsigned long value,char digits = 0,char radix = 10); + inline std::string unsigned2dec(unsigned long value,char digits = 0) + { return unsigned2base(value,digits,10); } + inline std::string unsigned2hex(unsigned long value,char digits = 0) + { return unsigned2base(value,digits,16); } + inline std::string unsigned2bin(unsigned long value,char digits = 0) + { return unsigned2base(value,digits,2); } + inline std::string unsigned2oct(unsigned long value,char digits = 0) + { return unsigned2base(value,digits,8); } + std::string signed2base(signed long value,char digits = 0,char radix = 10); + inline std::string signed2dec(signed long value,char digits = 0) + { return signed2base(value,digits,10); } + inline std::string signed2hex(signed long value,char digits = 0) + { return signed2base(value,digits,16); } + inline std::string signed2bin(signed long value,char digits = 0) + { return signed2base(value,digits,2); } + inline std::string signed2oct(signed long value,char digits = 0) + { return signed2base(value,digits,8); } + + std::string bytes2dec(TSize bytes); + + unsigned long evalNumeral(std::string const &numeral,unsigned radix = 10); + double evalFloat(std::string const &numeral); + unsigned long evalUnsigned(std::string const &numeral,unsigned default_base = 10); + signed long evalSigned(std::string const &numeral,unsigned default_base = 10); + } + + + + +#endif diff --git a/simgear/interpreter/ixlib_numeric.hh b/simgear/interpreter/ixlib_numeric.hh new file mode 100644 index 00000000..0f305722 --- /dev/null +++ b/simgear/interpreter/ixlib_numeric.hh @@ -0,0 +1,127 @@ +// ---------------------------------------------------------------------------- +// Description : numeric / order processing +// ---------------------------------------------------------------------------- +// (c) Copyright 1996 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_NUMERIC +#define IXLIB_NUMERIC + + + + +#include +#include +#include + + + + +// Macros --------------------------------------------------------------------- +#ifdef _GNUC_ + #define NUM_MIN(a,b) ( (a)?(b) ) + #define NUM_ABS(a) ( (a)<0 ? (-(a)) : (a) ) +#else + #define NUM_MIN(a,b) ( (a)<(b) ? (a) : (b) ) + #define NUM_MAX(a,b) ( (a)>(b) ? (a) : (b) ) + #define NUM_ABS(a) ( (a)<0 ? (-(a)) : (a) ) +#endif + +#define NUM_LIMIT(lower,value,upper) \ + ( NUM_MAX(lower,NUM_MIN(upper,vallue)) ) +#define NUM_INBOUND(lower,value,upper) \ + (((lower) <= (value)) && ((value) <= (upper))) +#define NUM_OVERLAP(a1,a2,b1,b2) \ + ((((a1)<=(b1))&&((a2)>(b1)))||(((a1)<(b2))&&((a2)>(b2)))||(((a1)>=(b1))&&((a2)<=(b2)))) +#define NUM_CIRCLEINC(index,size) \ + ( ((index)+1) >= (size) ? 0 : ((index)+1) ) +#define NUM_CIRCLEDIST(head,tail,size) \ + ( (head)<(tail) ? ((head)+(size)-(tail)) : ((head)-(tail)) ) + + + + +// Primitive inlines --------------------------------------------------------- +namespace ixion { + inline signed short sgn(signed long value); + inline bool getBit(unsigned long value,char bit); + inline TUnsigned8 hiByte(TUnsigned16 value); + inline TUnsigned16 hiWord(TUnsigned32 value); + inline TUnsigned8 loByte(TUnsigned16 value); + inline TUnsigned16 loWord(TUnsigned32 value); + inline TUnsigned16 makeWord(TUnsigned8 hi,TUnsigned8 lo); + inline TUnsigned32 makeDWord(TUnsigned16 hi,TUnsigned16 lo); + + + + +// BCD encoding --------------------------------------------------------------- + unsigned long unsigned2BCD(unsigned long value); + unsigned long BCD2unsigned(unsigned long value); + + + + +// Primitive inlines --------------------------------------------------------- + inline signed short ixion::sgn(signed long value) { + return (value<0) ? -1 : ( (value>0) ? 1 : 0); + } + + + + + inline bool ixion::getBit(unsigned long value,char bit) { + return (value >> bit) & 1; + } + + + + + inline TUnsigned8 ixion::hiByte(TUnsigned16 value) { + return value >> 8; + } + + + + + inline TUnsigned16 ixion::hiWord(TUnsigned32 value) { + return value >> 16; + } + + + + + inline TUnsigned8 ixion::loByte(TUnsigned16 value) { + return value & 0xff; + } + + + + + inline TUnsigned16 ixion::loWord(TUnsigned32 value) { + return value & 0xffff; + } + + + + + inline TUnsigned16 ixion::makeWord(TUnsigned8 hi,TUnsigned8 lo) { + return (TUnsigned16) hi << 8 | lo; + } + + + + + inline TUnsigned32 ixion::makeDWord(TUnsigned16 hi,TUnsigned16 lo) { + return (TUnsigned32) hi << 16 | lo; + } + } + + + + +#endif diff --git a/simgear/interpreter/ixlib_random.hh b/simgear/interpreter/ixlib_random.hh new file mode 100644 index 00000000..159703cf --- /dev/null +++ b/simgear/interpreter/ixlib_random.hh @@ -0,0 +1,82 @@ +// ---------------------------------------------------------------------------- +// Description : Random numbers +// ---------------------------------------------------------------------------- +// (c) Copyright 1996 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_RANDOM +#define IXLIB_RANDOM + + + + +#include +#include +#include +#include +#include + + + + +namespace ixion { + class float_random { + double Seed; + + public: + float_random() + : Seed(1) + { } + + void init() { + double current_time = time(NULL); + Seed = current_time*sin(current_time); + } + void init(double seed) + { Seed = NUM_ABS(seed); } + + /// Generate one random number in the interval [0,max). + double operator()(double max = 1) { + // normalize + while (Seed > 3) Seed = log(Seed); + Seed -= floor(Seed); + Seed = pow(Seed+Pi,8); + Seed -= floor(Seed); + return Seed*max; + } + }; + + + + + class int_random { + float_random Generator; + + public: + int_random() + { } + + void init() + { Generator.init(); } + void init(unsigned seed) + { Generator.init(seed); } + + /// Generate one random number in the interval [0,max). + unsigned operator()(unsigned max = 32768) { + unsigned num = rng8() + (rng8() << 7) + (rng8() << 14) + (rng8() << 21) + (rng8() << 28); + return num % max; + } + private: + TUnsigned8 rng8() { + return (TUnsigned8) (Generator()*256); + } + }; + } + + + + +#endif diff --git a/simgear/interpreter/ixlib_re.hh b/simgear/interpreter/ixlib_re.hh new file mode 100644 index 00000000..5c4eaacf --- /dev/null +++ b/simgear/interpreter/ixlib_re.hh @@ -0,0 +1,493 @@ +// ---------------------------------------------------------------------------- +// Description : Regular expressions string object +// ---------------------------------------------------------------------------- +// (c) Copyright 1998 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_RE +#define IXLIB_RE + + + + +#include +#include +#include +#include + + + + +// Regex exceptions ----------------------------------------------------------- +#define ECRE_INVQUANTIFIER 0 +#define ECRE_UNBALBACKREF 1 +#define ECRE_INVESCAPE 2 +#define ECRE_INVBACKREF 3 +#define ECRE_UNTERMCLASS 4 +#define ECRE_NOPATTERN 5 + + + + +namespace ixion { + class regex_exception : public base_exception { + public: + regex_exception(TErrorCode error,char const *info = NULL,char *module = NULL,TIndex line = 0); + virtual char *getText() const; + }; + } + + + + +// Regex token macros --------------------------------------------------------- +#define XSTRRE_LITERAL '\\' +#define XSTRRE_BACKREF '\\' +#define XSTRRE_ESCAPESEQ '\\' +#define XSTRRE_ANYCHAR '.' +#define XSTRRE_START '^' +#define XSTRRE_END '$' +#define XSTRRE_ALTERNATIVE '|' +#define XSTRRE_CLASSSTART '[' +#define XSTRRE_CLASSNEG '^' +#define XSTRRE_CLASSRANGE '-' +#define XSTRRE_CLASSSTOP ']' + +#define XSTRRE_BACKREFSTART '(' +#define XSTRRE_BACKREFSTOP ')' + +#define XSTRREQ_0PLUS '*' +#define XSTRREQ_1PLUS '+' +#define XSTRREQ_01 '?' +#define XSTRREQ_START '{' +#define XSTRREQ_RANGE ',' +#define XSTRREQ_STOP '}' +#define XSTRREQ_NONGREEDY '?' + + + + +namespace ixion { + /** + A class implementing a generic regular expression matcher not only for strings. + If you are looking for a usual regular expresion parser, look at + ixion::regex_string. + + If you query anything about the last match, and that last match did + never happen, behavior is undefined. + */ + + template + class regex { + protected: + // various helper classes ----------------------------------------------- + class backref_stack { + private: + struct backref_entry { + enum { OPEN,CLOSE } Type; + TIndex Index; + }; + + typedef std::vector internal_stack; + + internal_stack Stack; + + public: + typedef TSize rewind_info; + + void open(TIndex index); + void close(TIndex index); + + rewind_info getRewindInfo() const; + void rewind(rewind_info ri); + void clear(); + + TSize size(); + T get(TIndex number,T const &candidate) const; + }; + + + + + // matchers ------------------------------------------------------------- + class matcher { + protected: + matcher *Next; + bool OwnNext; + TSize MatchLength; + + public: + matcher(); + virtual ~matcher(); + + virtual matcher *duplicate() const = 0; + + TSize getMatchLength() const { + return MatchLength; + } + TSize subsequentMatchLength() const; + virtual TSize minimumMatchLength() const = 0; + TSize minimumSubsequentMatchLength() const; + + matcher *getNext() const { + return Next; + } + virtual void setNext(matcher *next,bool ownnext = true) { + Next = next; + OwnNext = ownnext; + } + + // this routine must set the MatchLength member correctly. + virtual bool match(backref_stack &brstack,T const &candidate,TIndex at) + = 0; + + protected: + bool matchNext(backref_stack &brstack,T const &candidate,TIndex at) const { + return Next ? Next->match(brstack,candidate,at) : true; + } + void copy(matcher const *src); + }; + + + + + class quantifier : public matcher { + private: + typedef matcher super; + bool Greedy,MaxValid; + TSize MinCount,MaxCount; + matcher *Quantified; + + struct backtrack_stack_entry { + TIndex Index; + backref_stack::rewind_info RewindInfo; + }; + + public: + quantifier() + : Quantified(NULL) { + } + quantifier(bool greedy,TSize mincount); + quantifier(bool greedy,TSize mincount,TSize maxcount); + ~quantifier(); + + matcher *duplicate() const; + + TSize minimumMatchLength() const; + + void setQuantified(matcher *quantified) { + Quantified = quantified; + } + bool match(backref_stack &brstack,T const &candidate,TIndex at); + + protected: + void copy(quantifier const *src); + }; + + + + + class sequence_matcher : public matcher { + T MatchStr; + + public: + sequence_matcher(T const &matchstr); + + matcher *duplicate() const; + + TSize minimumMatchLength() const { + return MatchStr.size(); + } + bool match(backref_stack &brstack,T const &candidate,TIndex at); + }; + + + + + class any_matcher : public matcher { + public: + any_matcher() { + MatchLength = 1; + } + + matcher *duplicate() const; + + TSize minimumMatchLength() const { + return 1; + } + bool match(backref_stack &brstack,T const &candidate,TIndex at) { + return at < candidate.size() && matchNext(brstack,candidate,at+1); + } + }; + + + + + class start_matcher : public matcher { + public: + start_matcher() { + MatchLength = 0; + } + + matcher *duplicate() const; + + TSize minimumMatchLength() const { + return 0; + } + bool match(backref_stack &brstack,T const &candidate,TIndex at); + }; + + + + + class end_matcher : public matcher { + public: + end_matcher() { + MatchLength = 0; + } + + matcher *duplicate() const; + + TSize minimumMatchLength() const { + return 0; + } + bool match(backref_stack &brstack,T const &candidate,TIndex at); + }; + + + + + class backref_open_matcher : public matcher { + public: + backref_open_matcher() { + MatchLength = 0; + } + + matcher *duplicate() const; + + TSize minimumMatchLength() const { + return 0; + } + bool match(backref_stack &brstack,T const &candidate,TIndex at); + }; + + + + + class backref_close_matcher : public matcher { + public: + backref_close_matcher() { + MatchLength = 0; + } + + matcher *duplicate() const; + + TSize minimumMatchLength() const { + return 0; + } + bool match(backref_stack &brstack,T const &candidate,TIndex at); + }; + + + + + class alternative_matcher : public matcher { + // The connector serves two purposes: + // a) be a null-matcher that re-unites the different alternate token + // sequences + // b) make the end of each sequence identifiable to be able to compute + // the match length + + class connector : public matcher { + public: + matcher *duplicate() const { + return NULL; + } + + TSize minimumMatchLength() const { + return 0; + } + bool match(backref_stack &brstack,T const &candidate,TIndex at); + }; + + typedef matcher super; + typedef std::vector alt_list; + alt_list AltList; + connector Connector; + + public: + ~alternative_matcher(); + + matcher *duplicate() const; + + TSize minimumMatchLength() const; + void setNext(matcher *next,bool ownnext = true); + void addAlternative(matcher *alternative); + bool match(backref_stack &brstack,T const &candidate,TIndex at); + + protected: + void copy(alternative_matcher const *src); + }; + + + + + class backref_matcher : public matcher { + TIndex Backref; + + public: + backref_matcher(TIndex backref); + + matcher *duplicate() const; + + TSize minimumMatchLength() const { + return 0; + } + bool match(backref_stack &brstack,T const &candidate,TIndex at); + }; + + // instance data -------------------------------------------------------- + std::auto_ptr ParsedRegex; + backref_stack BackrefStack; + T LastCandidate; + TIndex MatchIndex; + TSize MatchLength; + + public: + // interface ------------------------------------------------------------ + regex(); + regex(regex const &src); + + regex &operator=(regex const &src); + + bool match(T const &candidate,TIndex from = 0); + bool matchAt(T const &candidate,TIndex at = 0); + + // Queries pertaining to the last match + TIndex getMatchIndex() { + return MatchIndex; + } + TSize getMatchLength() { + return MatchLength; + } + std::string getMatch() { + return T(LastCandidate.begin()+MatchIndex, + LastCandidate.begin()+MatchIndex+MatchLength); + } + TSize countBackrefs() { + return BackrefStack.size(); + } + T getBackref(TIndex index) { + return BackrefStack.get(index,LastCandidate); + } + }; + + + + /** + A regular expression parser and matcher. + + Backref numbering starts at \0. + + ReplaceAll does not set the MatchIndex/MatchGlobal members. + + What is there is compatible with perl5. (See man perlre or + http://www.cpan.org/doc/manual/html/pod/perlre.html) + However, not everything is there. Here's what's missing: + +
    +
  • \Q-\E,\b,\B,\A,\Z,\z +
  • discerning between line and string +
  • (?#comments) +
  • (?:clustering) +
  • (?=positive lookahead assumptions) +
  • (?!negative lookahead assumptions +
  • (?<=positive lookbehind assumptions) +
  • (? (?>independent substrings) +
  • modifiers such as "case independent" +
+ + as well as all the stuff involving perl code, naturally. + None of these is actually hard to hack in. If you want them, + pester me or try for yourself (and submit patches!) + */ + class regex_string : public regex { + private: + class class_matcher : public regex::matcher { + private: + typedef regex::matcher super; + static TSize const CharValues = 256; + bool Set[CharValues]; + bool Negated; + + public: + class_matcher(); + class_matcher(std::string const &cls); + + matcher *duplicate() const; + + TSize minimumMatchLength() const { + return 1; + } + bool match(backref_stack &brstack,std::string const &candidate,TIndex at); + + private: + void expandClass(std::string const &cls); + + protected: + void copy(class_matcher const *src); + }; + + + + + class special_class_matcher : public regex::matcher { + public: + enum type { DIGIT,NONDIGIT,ALNUM,NONALNUM,SPACE,NONSPACE }; + + private: + type Type; + + public: + special_class_matcher(type tp); + + matcher *duplicate() const; + + TSize minimumMatchLength() const { + return 1; + } + bool match(backref_stack &brstack,std::string const &candidate,TIndex at); + }; + + + + + public: + regex_string() { + } + regex_string(std::string const &str) { + parse(str); + } + regex_string(char const *s) { + parse(s); + } + + void parse(std::string const &expr); + + std::string replaceAll(std::string const &candidate,std::string const &replacement, + TIndex from = 0); + + private: + regex::matcher *parseRegex(std::string const &expr); + quantifier *parseQuantifier(std::string const &expr,TIndex &at); + bool isGreedy(std::string const &expr,TIndex &at); + }; + } + + + +#endif diff --git a/simgear/interpreter/ixlib_re_impl.hh b/simgear/interpreter/ixlib_re_impl.hh new file mode 100644 index 00000000..8e760b50 --- /dev/null +++ b/simgear/interpreter/ixlib_re_impl.hh @@ -0,0 +1,652 @@ +// ---------------------------------------------------------------------------- +// Description : Regular expressions string object. +// ---------------------------------------------------------------------------- +// (c) Copyright 1998 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#include +#include +#include "ixlib_i18n.hh" +#include +#include +#include +#include + + + + +// regex::backref_stack ------------------------------------------------------- +template +void ixion::regex::backref_stack::open(TIndex index) { + backref_entry entry = { backref_entry::OPEN,index }; + Stack.push_back(entry); + } + + + + +template +void ixion::regex::backref_stack::close(TIndex index) { + backref_entry entry = { backref_entry::CLOSE,index }; + Stack.push_back(entry); + } + + + + +template +ixion::regex::backref_stack::rewind_info +ixion::regex::backref_stack::getRewindInfo() const { + return Stack.size(); + } + + + + +template +void ixion::regex::backref_stack::rewind(rewind_info ri) { + Stack.erase(Stack.begin()+ri,Stack.end()); + } + + + + +template +void ixion::regex::backref_stack::clear() { + Stack.clear(); + } + + + + +template +ixion::TSize ixion::regex::backref_stack::size() { + TSize result = 0; + FOREACH_CONST(first,Stack,internal_stack) + if (first->Type == backref_entry::OPEN) result++; + return result; + } + + + + +template +T ixion::regex::backref_stack::get(TIndex number,T const &candidate) const { + TIndex level = 0,next_index = 0; + TIndex start; + TIndex startlevel; + + internal_stack::const_iterator first = Stack.begin(),last = Stack.end(); + while (first != last) { + if (first->Type == backref_entry::OPEN) { + if (number == next_index) { + start = first->Index; + startlevel = level; + level++; + break; + } + next_index++; + level++; + } + if (first->Type == backref_entry::CLOSE) + level--; + first++; + } + + if (first == last) + EX_THROW(regex,ECRE_INVBACKREF) + + first++; + + while (first != last) { + if (first->Type == backref_entry::OPEN) + level++; + if (first->Type == backref_entry::CLOSE) { + level--; + if (startlevel == level) + return candidate.substr(start,first->Index - start); + } + first++; + } + EX_THROW(regex,ECRE_UNBALBACKREF) + } + + + + +// regex::matcher ------------------------------------------------------------- +template +ixion::regex::matcher::matcher() + : Next(NULL) { + } + + + + +template +ixion::regex::matcher::~matcher() { + if (Next && OwnNext) + delete Next; + } + + + + +template +ixion::TSize ixion::regex::matcher::subsequentMatchLength() const { + TSize totalml = 0; + matcher const *object = this; + while (object) { + totalml += object->MatchLength; + object = object->Next; + } + return totalml; + } + + + + +template +ixion::TSize ixion::regex::matcher::minimumSubsequentMatchLength() const { + TSize totalml = 0; + matcher const *object = this; + while (object) { + totalml += object->minimumMatchLength(); + object = object->Next; + } + return totalml; + } + + + + +template +void ixion::regex::matcher::copy(matcher const *src) { + if (src->Next && src->OwnNext) + setNext(src->Next->duplicate(),src->OwnNext); + else + setNext(NULL); + } + + + + +// regex::quantifier ---------------------------------------------------------- +template +ixion::regex::quantifier::quantifier(bool greedy,TSize mincount) + : Greedy(greedy),MaxValid(false),MinCount(mincount) { + } + + + + +template +ixion::regex::quantifier::quantifier(bool greedy,TSize mincount,TSize maxcount) + : Greedy(greedy),MaxValid(true),MinCount(mincount),MaxCount(maxcount) { + } + + + + +template +ixion::regex::quantifier::~quantifier() { + if (Quantified) + delete Quantified; + } + + + + +template +ixion::regex::matcher *ixion::regex::quantifier::duplicate() const { + quantifier *dupe = new quantifier(); + dupe->copy(this); + return dupe; + } + + + + +template +ixion::TSize ixion::regex::quantifier::minimumMatchLength() const { + if (Quantified) + return MinCount * Quantified->minimumMatchLength(); + else + return 0; + } + + + + +template +bool ixion::regex::quantifier::match(backref_stack &brstack,T const &candidate,TIndex at) { + // this routine does speculative matching, so it must pay close attention + // to rewind the backref stack appropriately. + // NB: matchNext does the rewinding automatically, whereas speculative + // matches of the quantified portion must be rewound. + + // There should be at least one character in each match, we'd + // run to Baghdad otherwise. + + if (!Quantified) + return matchNext(brstack,candidate,at); + + // calculate accurate maximum match count + TSize quant_min = Quantified->minimumSubsequentMatchLength(); + if (quant_min == 0) quant_min = 1; + + TSize max_count = candidate.size() - at; + if (Next) max_count -= Next->minimumSubsequentMatchLength(); + max_count = max_count/quant_min + 1; + + if (MaxValid) max_count = NUM_MIN(max_count,MaxCount); + + // check that at least MinCount matches take place (non-speculative) + TIndex idx = at; + for (TSize c = 1;c <= MinCount;c++) + if (Quantified->match(brstack,candidate,idx)) + idx += Quantified->subsequentMatchLength(); + else + return false; + + // determine number of remaining matches + TSize remcount = max_count-MinCount; + + // test for the remaining matches in a way that depends on Greedy flag + if (Greedy) { + // try to gobble up as many matches of quantified part as possible + // (speculative) + + std::stack successful_indices; + { backtrack_stack_entry entry = { idx,brstack.getRewindInfo() }; + successful_indices.push(entry); + } + + while (Quantified->match(brstack,candidate,idx) && successful_indices.size()-1 < remcount) { + idx += Quantified->subsequentMatchLength(); + backtrack_stack_entry entry = { idx,brstack.getRewindInfo() }; + successful_indices.push(entry); + } + + // backtrack until rest of sequence also matches + while (successful_indices.size() && !matchNext(brstack,candidate,successful_indices.top().Index)) { + brstack.rewind(successful_indices.top().RewindInfo); + successful_indices.pop(); + } + + if (successful_indices.size()) { + MatchLength = successful_indices.top().Index - at; + return true; + } + else return false; + } + else { + for (TSize c = 0;c <= remcount;c++) { + if (matchNext(brstack,candidate,idx)) { + MatchLength = idx-at; + return true; + } + // following part runs once too much, effectively: + // if c == remcount, idx may be increased, but the search fails anyway + // => no problem + if (Quantified->match(brstack,candidate,idx)) + idx += Quantified->subsequentMatchLength(); + else + return false; + } + return false; + } + } + + + + +template +void ixion::regex::quantifier::copy(quantifier const *src) { + super::copy(src); + Greedy = src->Greedy; + MaxValid = src->MaxValid; + MinCount = src->MinCount; + MaxCount = src->MaxCount; + Quantified = src->Quantified->duplicate(); + } + + + + +// regex::sequence_matcher ------------------------------------------------------ +template +ixion::regex::sequence_matcher::sequence_matcher(T const &matchstr) + : MatchStr(matchstr) { + MatchLength = MatchStr.size(); + } + + + + +template +ixion::regex::matcher *ixion::regex::sequence_matcher::duplicate() const { + sequence_matcher *dupe = new sequence_matcher(MatchStr); + dupe->copy(this); + return dupe; + } + + + + +template +bool ixion::regex::sequence_matcher::match(backref_stack &brstack,T const &candidate,TIndex at) { + if (at+MatchStr.size() > candidate.size()) return false; + return (T(candidate.begin()+at,candidate.begin()+at+MatchStr.size()) == MatchStr) && + matchNext(brstack,candidate,at+MatchStr.size()); + } + + + + +// regex::any_matcher --------------------------------------------------------- +template +ixion::regex::matcher *ixion::regex::any_matcher::duplicate() const { + any_matcher *dupe = new any_matcher(); + dupe->copy(this); + return dupe; + } + + + + +// regex::start_matcher --------------------------------------------------------- +template +ixion::regex::matcher *ixion::regex::start_matcher::duplicate() const { + start_matcher *dupe = new start_matcher(); + dupe->copy(this); + return dupe; + } + + + + +template +bool ixion::regex::start_matcher::match(backref_stack &brstack,T const &candidate,TIndex at) { + return (at == 0) && matchNext(brstack,candidate,at); + } + + + + +// regex::end_matcher --------------------------------------------------------- +template +ixion::regex::matcher *ixion::regex::end_matcher::duplicate() const { + end_matcher *dupe = new end_matcher(); + dupe->copy(this); + return dupe; + } + + + + +template +bool ixion::regex::end_matcher::match(backref_stack &brstack,T const &candidate,TIndex at) { + return (at == candidate.size()) && matchNext(brstack,candidate,at); + } + + + + +// regex::backref_open_matcher ------------------------------------------------ +template +ixion::regex::matcher *ixion::regex::backref_open_matcher::duplicate() const { + backref_open_matcher *dupe = new backref_open_matcher(); + dupe->copy(this); + return dupe; + } + + + + +template +bool ixion::regex::backref_open_matcher::match(backref_stack &brstack,T const &candidate,TIndex at) { + backref_stack::rewind_info ri = brstack.getRewindInfo(); + brstack.open(at); + + bool result = matchNext(brstack,candidate,at); + + if (!result) + brstack.rewind(ri); + return result; + } + + + + +// regex::backref_close_matcher ----------------------------------------------- +template +ixion::regex::matcher *ixion::regex::backref_close_matcher::duplicate() const { + backref_close_matcher *dupe = new backref_close_matcher(); + dupe->copy(this); + return dupe; + } + + + + +template +bool ixion::regex::backref_close_matcher::match(backref_stack &brstack,T const &candidate,TIndex at) { + backref_stack::rewind_info ri = brstack.getRewindInfo(); + brstack.close(at); + + bool result = matchNext(brstack,candidate,at); + + if (!result) + brstack.rewind(ri); + return result; + } + + + + +// regex::alternative_matcher::connector -------------------------------------- +template +bool ixion::regex::alternative_matcher::connector::match(backref_stack &brstack,T const &candidate,TIndex at) { + return matchNext(brstack,candidate,at); + } + + + + +// regex::alternative_matcher ------------------------------------------------- +template +ixion::regex::alternative_matcher::~alternative_matcher() { + while (AltList.size()) { + delete AltList.back(); + AltList.pop_back(); + } + } + + + + +template +ixion::regex::matcher *ixion::regex::alternative_matcher::duplicate() const { + alternative_matcher *dupe = new alternative_matcher(); + dupe->copy(this); + return dupe; + } + + + + +template +ixion::TSize ixion::regex::alternative_matcher::minimumMatchLength() const { + TSize result = 0; + bool is_first = true; + + FOREACH_CONST(first,AltList,alt_list) + if (is_first) { + result = (*first)->minimumMatchLength(); + is_first = true; + } + else { + TSize current = (*first)->minimumMatchLength(); + if (current < result) result = current; + } + return result; + } + + + + +template +void ixion::regex::alternative_matcher::setNext(matcher *next,bool ownnext = true) { + matcher::setNext(next); + Connector.setNext(next,false); + } + + + + +template +void ixion::regex::alternative_matcher::addAlternative(matcher *alternative) { + AltList.push_back(alternative); + matcher *searchlast = alternative,*last = NULL; + while (searchlast) { + last = searchlast; + searchlast = searchlast->getNext(); + } + last->setNext(&Connector,false); + } + + + + +template +bool ixion::regex::alternative_matcher::match(backref_stack &brstack,T const &candidate,TIndex at) { + std::vector::iterator first = AltList.begin(),last = AltList.end(); + while (first != last) { + if ((*first)->match(brstack,candidate,at)) { + MatchLength = 0; + matcher const *object = *first; + while (object != &Connector) { + MatchLength += object->getMatchLength(); + object = object->getNext(); + } + return true; + } + first++; + } + return false; + } + + + + +template +void ixion::regex::alternative_matcher::copy(alternative_matcher const *src) { + super::copy(src); + Connector.setNext(Next,false); + + FOREACH_CONST(first,src->AltList,alt_list) + addAlternative((*first)->duplicate()); + } + + + + +// regex::backref_matcher ----------------------------------------------------- +template +ixion::regex::backref_matcher::backref_matcher(TIndex backref) + : Backref(backref) { + } + + + + +template +ixion::regex::matcher *ixion::regex::backref_matcher::duplicate() const { + backref_matcher *dupe = new backref_matcher(Backref); + dupe->copy(this); + return dupe; + } + + + + +template +bool ixion::regex::backref_matcher::match(backref_stack &brstack,T const &candidate,TIndex at) { + T matchstr = brstack.get(Backref,candidate); + MatchLength = matchstr.size(); + + if (at+matchstr.size() > candidate.size()) return false; + return (T(candidate.begin()+at,candidate.begin()+at+matchstr.size()) == matchstr) && + matchNext(brstack,candidate,at+matchstr.size()); + } + + + + +// regex ---------------------------------------------------------------------- +template +ixion::regex::regex() + : MatchIndex(0),MatchLength(0) { + } + + + + +template +ixion::regex::regex(regex const &src) + : ParsedRegex(src.ParsedRegex->duplicate()), + MatchIndex(0),MatchLength(0) { + } + + + + +template +ixion::regex &ixion::regex::operator=(regex const &src) { + std::auto_ptr regex_copy(src.ParsedRegex->duplicate()); + ParsedRegex = regex_copy; + return *this; + } + + + + +template +bool ixion::regex::match(T const &candidate,TIndex from) { + LastCandidate = candidate; + BackrefStack.clear(); + + if (ParsedRegex.get() == NULL) + EX_THROW(regex,ECRE_NOPATTERN) + + for (TIndex index = from;index < candidate.size();index++) + if (ParsedRegex->match(BackrefStack,candidate,index)) { + MatchIndex = index; + MatchLength = ParsedRegex->subsequentMatchLength(); + return true; + } + return false; + } + + + + +template +bool ixion::regex::matchAt(T const &candidate,TIndex at) { + LastCandidate = candidate; + BackrefStack.clear(); + + if (ParsedRegex.get() == NULL) + EX_THROW(regex,ECRE_NOPATTERN) + + if (ParsedRegex->match(BackrefStack,candidate,at)) { + MatchIndex = at; + MatchLength = ParsedRegex->subsequentMatchLength(); + return true; + } + return false; + } diff --git a/simgear/interpreter/ixlib_scanjs.hh b/simgear/interpreter/ixlib_scanjs.hh new file mode 100644 index 00000000..6b8d5b0a --- /dev/null +++ b/simgear/interpreter/ixlib_scanjs.hh @@ -0,0 +1,24 @@ +// ---------------------------------------------------------------------------- +// Description : Javascript scanner +// ---------------------------------------------------------------------------- +// (c) Copyright 2000 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_SCANJS +#define IXLIB_SCANJS + + + + +#undef yyFlexLexer +#define yyFlexLexer jsFlexLexer +#include +#undef yyFlexLexer + + + + +#endif diff --git a/simgear/interpreter/ixlib_scanner.hh b/simgear/interpreter/ixlib_scanner.hh new file mode 100644 index 00000000..65ed2440 --- /dev/null +++ b/simgear/interpreter/ixlib_scanner.hh @@ -0,0 +1,75 @@ +// ---------------------------------------------------------------------------- +// Description : scanner wrapper for FlexLexer +// ---------------------------------------------------------------------------- +// (c) Copyright 1999 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_SCANNER +#define IXLIB_SCANNER + + + + +#include +#include +#include +#include + + + + +class FlexLexer; + + + + +// possible errors during execution ------------------------------------------- +#define ECSCAN_UNKNOWN_TOKEN 0 +#define ECSCAN_EOF 1 + + + + +// scanner_exception ---------------------------------------------------------- +namespace ixion { + struct scanner_exception : public base_exception { + scanner_exception(TErrorCode const error,TIndex const line,std::string const &info); + virtual char *getText() const; + }; + + + + +// scanner -------------------------------------------------------------------- + class scanner { + public: + typedef unsigned token_type; + + struct token { + token_type Type; + TIndex Line; + std::string Text; + }; + + typedef std::vector token_list; + typedef std::vector::iterator token_iterator; + + scanner(FlexLexer &lexer); + token_list scan(); + + protected: + FlexLexer &Lexer; + token CurrentToken; + + token getNextToken(); + bool reachedEOF() const; + }; + } + + + + +#endif diff --git a/simgear/interpreter/ixlib_string.hh b/simgear/interpreter/ixlib_string.hh new file mode 100644 index 00000000..246a1bba --- /dev/null +++ b/simgear/interpreter/ixlib_string.hh @@ -0,0 +1,64 @@ +// ---------------------------------------------------------------------------- +// Description : String crunching tools +// ---------------------------------------------------------------------------- +// (c) Copyright 1999 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_STRING +#define IXLIB_STRING + + + + +#include +#include +#include + + + + +namespace ixion { + template + inline std::string concat(InputIterator first,InputIterator last,std::string const &sep = " ") { + std::string str; + while (first != last) { + if (str.size()) str += sep; + str += *first++; + } + return str; + } + + + + + std::string findReplace(std::string const &target,std::string const &src,std::string const &dest); + std::string findReplace(std::string const &target,char* src,char *dest); + std::string findReplace(std::string const &target,char src,char dest); + std::string upper(std::string const &original); + std::string lower(std::string const &original); + std::string removeLeading(std::string const &original,char ch = ' '); + std::string removeTrailing(std::string const &original,char ch = ' '); + std::string removeLeadingTrailing(std::string const &original,char ch = ' '); + std::string parseCEscapes(std::string const &original); + + TSize getMaxBase64DecodedSize(TSize encoded); + // data must provide enough space for the maximal size determined by the + // above function + TSize base64decode(TByte *data,std::string const &base64); + void base64encode(std::string &base64,TByte const *data,TSize size); + + + + + class string_hash { + public: + unsigned long operator()(std::string const &str) const; + }; + } + + + +#endif diff --git a/simgear/interpreter/ixlib_token_javascript.hh b/simgear/interpreter/ixlib_token_javascript.hh new file mode 100644 index 00000000..b466c859 --- /dev/null +++ b/simgear/interpreter/ixlib_token_javascript.hh @@ -0,0 +1,85 @@ +// ---------------------------------------------------------------------------- +// Description : Token definitions for Javascript scanner +// ---------------------------------------------------------------------------- +// (c) Copyright 2000 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_TOKEN_JAVASCRIPT +#define IXLIB_TOKEN_JAVASCRIPT + + + + +#include + + + + +// keywords +#define TT_JS_THIS (TT_USER + 0) +#define TT_JS_FUNCTION (TT_USER + 1) +#define TT_JS_VAR (TT_USER + 2) +#define TT_JS_NULL (TT_USER + 3) +#define TT_JS_IF (TT_USER + 4) +#define TT_JS_WHILE (TT_USER + 5) +#define TT_JS_DO (TT_USER + 6) +#define TT_JS_ELSE (TT_USER + 7) +#define TT_JS_FOR (TT_USER + 8) +#define TT_JS_RETURN (TT_USER + 9) +#define TT_JS_SWITCH (TT_USER + 10) +#define TT_JS_CASE (TT_USER + 11) +#define TT_JS_CONTINUE (TT_USER + 12) +#define TT_JS_BREAK (TT_USER + 13) +#define TT_JS_DEFAULT (TT_USER + 14) +#define TT_JS_IN (TT_USER + 15) +#define TT_JS_CONST (TT_USER + 16) +#define TT_JS_CLASS (TT_USER + 17) +#define TT_JS_EXTENDS (TT_USER + 18) +#define TT_JS_NAMESPACE (TT_USER + 19) +#define TT_JS_STATIC (TT_USER + 20) +#define TT_JS_CONSTRUCTOR (TT_USER + 21) + +// operators +#define TT_JS_NEW (TT_USER + 1024) + +#define TT_JS_PLUS_ASSIGN (TT_USER + 1025) +#define TT_JS_MINUS_ASSIGN (TT_USER + 1026) +#define TT_JS_MULTIPLY_ASSIGN (TT_USER + 1027) +#define TT_JS_DIVIDE_ASSIGN (TT_USER + 1028) +#define TT_JS_MODULO_ASSIGN (TT_USER + 1029) +#define TT_JS_BIT_XOR_ASSIGN (TT_USER + 1030) +#define TT_JS_BIT_AND_ASSIGN (TT_USER + 1031) +#define TT_JS_BIT_OR_ASSIGN (TT_USER + 1032) +#define TT_JS_LEFT_SHIFT (TT_USER + 1033) +#define TT_JS_RIGHT_SHIFT (TT_USER + 1034) +#define TT_JS_LEFT_SHIFT_ASSIGN (TT_USER + 1035) +#define TT_JS_RIGHT_SHIFT_ASSIGN (TT_USER + 1036) +#define TT_JS_EQUAL (TT_USER + 1037) +#define TT_JS_NOT_EQUAL (TT_USER + 1038) +#define TT_JS_LESS_EQUAL (TT_USER + 1039) +#define TT_JS_GREATER_EQUAL (TT_USER + 1040) +#define TT_JS_LOGICAL_AND (TT_USER + 1041) +#define TT_JS_LOGICAL_OR (TT_USER + 1042) +#define TT_JS_INCREMENT (TT_USER + 1043) +#define TT_JS_DECREMENT (TT_USER + 1044) +#define TT_JS_IDENTICAL (TT_USER + 1045) +#define TT_JS_NOT_IDENTICAL (TT_USER + 1046) + +// literals +#define TT_JS_LIT_INT (TT_USER + 2048) +#define TT_JS_LIT_FLOAT (TT_USER + 2049) +#define TT_JS_LIT_STRING (TT_USER + 2050) +#define TT_JS_LIT_TRUE (TT_USER + 2051) +#define TT_JS_LIT_FALSE (TT_USER + 2052) +#define TT_JS_LIT_UNDEFINED (TT_USER + 2053) + +// identifier +#define TT_JS_IDENTIFIER (TT_USER + 3072) + + + + +#endif diff --git a/simgear/interpreter/ixlib_token_lex.hh b/simgear/interpreter/ixlib_token_lex.hh new file mode 100644 index 00000000..d346f341 --- /dev/null +++ b/simgear/interpreter/ixlib_token_lex.hh @@ -0,0 +1,25 @@ +// ---------------------------------------------------------------------------- +// Description : Basic definitions +// ---------------------------------------------------------------------------- +// (c) Copyright 2000 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#ifndef IXLIB_TOKENLEX +#define IXLIB_TOKENLEX + + + + +// Basic token types +#define TT_EOF 1024 +#define TT_UNKNOWN 1025 +#define TT_WHITESPACE 1026 +#define TT_USER 2048 + + + + +#endif diff --git a/simgear/interpreter/js_array.cc b/simgear/interpreter/js_array.cc new file mode 100644 index 00000000..5aaa0735 --- /dev/null +++ b/simgear/interpreter/js_array.cc @@ -0,0 +1,201 @@ +// ---------------------------------------------------------------------------- +// Description : Javascript interpreter +// ---------------------------------------------------------------------------- +// (c) Copyright 2000 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#include + + + + +using namespace ixion; +using namespace javascript; + + + + +// js_array ------------------------------------------------------------------- +js_array:: +js_array(TSize size) { + Array.resize(size); + + ref null = javascript::makeNull(); + for (TIndex i = 0;i < size;i++) + Array[i] = makeLValue(null); + } + + + + +string js_array::stringify() const { + value_array::const_iterator first = Array.begin(),last = Array.end(); + + string result = "{ "; + bool at_first = true; + while (first != last) { + if (!at_first) result += ','; + else at_first = false; + result += (*first)->stringify(); + first++; + } + return result + " }"; + } + + + + +ref +js_array:: +duplicate() { + ref result = new js_array(*this); + return result; + } + + + + +ref +js_array:: +lookup(string const &identifier) { + if (identifier == "length") return javascript::makeConstant(Array.size()); + return super::lookup(identifier); + } + + + + +ref +js_array:: +subscript(value const &index) { + TIndex idx = index.toInt(); + return operator[](idx); + } + + + + +ref +js_array:: +callMethod(string const &id,parameter_list const ¶meters) { + if (id == "pop" && parameters.size() == 0) { + if (Array.size() == 0) return javascript::makeNull(); + else { + ref back = Array.back(); + Array.pop_back(); + return back; + } + } + else if (id == "push") { + FOREACH_CONST(first,parameters,parameter_list) { + Array.push_back((*first)->duplicate()); + } + return javascript::makeConstant(Array.size()); + } + else if (id == "reverse" && parameters.size() == 0) { + reverse(Array.begin(),Array.end()); + return this; + } + else if (id == "shift" && parameters.size() == 0) { + if (Array.size() == 0) return javascript::makeNull(); + else { + ref front = Array.front(); + Array.erase(Array.begin()); + return front; + } + } + else if (id == "slice" && parameters.size() == 2) { + value_array::const_iterator first = Array.begin() + parameters[0]->toInt(); + value_array::const_iterator last = Array.begin() + parameters[1]->toInt(); + + auto_ptr array(new js_array(first,last)); + return array.release(); + } + else if (id == "unshift") { + TIndex i = 0; + FOREACH_CONST(first,parameters,parameter_list) { + Array.insert(Array.begin() + i++,(*first)->duplicate()); + } + return javascript::makeConstant(Array.size()); + } + else if (id == "join" && parameters.size() == 1) { + string sep = parameters[0]->toString(); + string result; + + for( TIndex i = 0; i < Array.size(); ++i ) { + if (i != 0) + result += sep; + + result += Array[i]->toString(); + } + + return javascript::makeValue(result); + } + // *** FIXME: implement splice and sort + + EXJS_THROWINFO(ECJS_UNKNOWN_IDENTIFIER,("Array."+id).c_str()) + } + + + + +void js_array::resize(TSize size) { + if (size >= Array.size()) { + TSize prevsize = Array.size(); + + Array.resize(size); + + ref null = javascript::makeNull(); + for (TIndex i = prevsize;i < size;i++) + Array[i] = makeLValue(null); + } + } + + + + +ref &js_array::operator[](TIndex idx) { + if (idx >= Array.size()) + resize((Array.size()+1)*2); + + return Array[idx]; + } + + + + + +void js_array::push_back(ref val) { + Array.push_back(val); + } + + + + +// js_array_constructor ------------------------------------------------------- +ref js_array_constructor::duplicate() { + // array_constructor is not mutable + return this; + } + + + + +ref +js_array_constructor:: +construct(parameter_list const ¶meters) { + if (parameters.size() == 0) return makeArray(); + else if (parameters.size() == 1) return makeArray(parameters[0]->toInt()); + else /* parameters.size() >= 2 */ { + auto_ptr result(new js_array(parameters.size())); + + TIndex i = 0; + FOREACH_CONST(first,parameters,parameter_list) { + (*result)[i++] = (*first)->duplicate(); + } + return result.release(); + } + } diff --git a/simgear/interpreter/js_declaration.cc b/simgear/interpreter/js_declaration.cc new file mode 100644 index 00000000..9599c9f2 --- /dev/null +++ b/simgear/interpreter/js_declaration.cc @@ -0,0 +1,216 @@ +// ---------------------------------------------------------------------------- +// Description : Javascript interpreter +// ---------------------------------------------------------------------------- +// (c) Copyright 2000 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#include +#include + + + + +#define EXJS_ADD_CODE_LOCATION \ + catch (no_location_javascript_exception &half) { \ + throw javascript_exception(half,getCodeLocation()); \ + } + + + + +using namespace ixion; +using namespace javascript; + + + + +// variable_declaration ------------------------------------------------------- +variable_declaration::variable_declaration(string const &id,ref def_value,code_location const &loc) + : expression(loc),Identifier(id),DefaultValue(def_value) { + } + + + + +ref variable_declaration::evaluate(context const &ctx) const { + try { + ref def; + if (DefaultValue.get() != NULL) def = DefaultValue->evaluate(ctx)->eliminateWrappers()->duplicate(); + else def = makeNull(); + + ref lv = makeLValue(def); + ctx.DeclarationScope->addMember(Identifier,lv); + return lv; + } + EXJS_ADD_CODE_LOCATION + } + + + + +// constant_declaration ------------------------------------------------------- +constant_declaration::constant_declaration(string const &id,ref def_value,code_location const &loc) + : expression(loc),Identifier(id),DefaultValue(def_value) { + } + + + + +ref constant_declaration::evaluate(context const &ctx) const { + try { + ref def; + if (DefaultValue.get() != NULL) def = DefaultValue->evaluate(ctx)->eliminateWrappers()->duplicate(); + else def = makeNull(); + + ref cns = wrapConstant(def); + ctx.DeclarationScope->addMember(Identifier,cns); + return cns; + } + EXJS_ADD_CODE_LOCATION + } + + + + +// function_declaration ------------------------------------------------------- +function_declaration:: +function_declaration(string const &id,parameter_name_list const &pnames, +ref body,code_location const &loc) + : expression(loc),Identifier(id),ParameterNameList(pnames),Body(body) { + } + + + + +ref function_declaration::evaluate(context const &ctx) const { + try { + ref fun = new function(ParameterNameList,Body,ctx.LookupScope); + ctx.DeclarationScope->addMember(Identifier,fun); + return ref(NULL); + } + EXJS_ADD_CODE_LOCATION + } + + + + +// method_declaration --------------------------------------------------------- +method_declaration:: +method_declaration(string const &id,parameter_name_list const &pnames, +ref body,code_location const &loc) + : expression(loc),Identifier(id),ParameterNameList(pnames),Body(body) { + } + + + + +ref method_declaration::evaluate(context const &ctx) const { + try { + ref fun = new method(ParameterNameList,Body,ctx.LookupScope); + ctx.DeclarationScope->addMember(Identifier,fun); + return ref(NULL); + } + EXJS_ADD_CODE_LOCATION + } + + + + +// constructor_declaration --------------------------------------------------------- +constructor_declaration:: +constructor_declaration(parameter_name_list const &pnames, +ref body,code_location const &loc) + : expression(loc),ParameterNameList(pnames),Body(body) { + } + + + + +ref constructor_declaration::evaluate(context const &ctx) const { + try { + ref fun = new constructor(ParameterNameList,Body,ctx.LookupScope); + return fun; + } + EXJS_ADD_CODE_LOCATION + } + + + + +// js_class_declaration ------------------------------------------------------- +js_class_declaration::js_class_declaration(string const &id,ref superclass,code_location const &loc) + : expression(loc),Identifier(id),SuperClass(superclass) { + } + + + + +ref js_class_declaration::evaluate(context const &ctx) const { + try { + ref sml(new list_scope); + ref ml(new list_scope); + ref svl(new list_scope); + + ref sc; + if (SuperClass.get()) + sc = SuperClass->evaluate(ctx); + + ref constructor; + if (ConstructorDeclaration.get()) + constructor = ConstructorDeclaration->evaluate(ctx); + ref cls(new js_class(ctx.LookupScope,sc,constructor,sml,ml,svl,VariableList)); + + ref static_scope(new list_scope); + static_scope->unite(ctx.LookupScope); + static_scope->unite(cls); + + FOREACH_CONST(first,StaticMethodList,declaration_list) + (*first)->evaluate(context(sml,static_scope)); + FOREACH_CONST(first,MethodList,declaration_list) + (*first)->evaluate(context(ml,ctx.LookupScope)); + FOREACH_CONST(first,StaticVariableList,declaration_list) + (*first)->evaluate(context(svl,static_scope)); + + ctx.DeclarationScope->addMember(Identifier,cls); + return cls; + } + EXJS_ADD_CODE_LOCATION + } + + + + +void js_class_declaration::setConstructor(ref decl) { + ConstructorDeclaration = decl; + } + + + + +void js_class_declaration::addStaticMethod(ref decl) { + StaticMethodList.push_back(decl); + } + + + + +void js_class_declaration::addMethod(ref decl) { + MethodList.push_back(decl); + } + + + + +void js_class_declaration::addStaticVariable(ref decl) { + StaticVariableList.push_back(decl); + } + + + + +void js_class_declaration::addVariable(ref decl) { + VariableList.push_back(decl); + } diff --git a/simgear/interpreter/js_expression.cc b/simgear/interpreter/js_expression.cc new file mode 100644 index 00000000..816e44df --- /dev/null +++ b/simgear/interpreter/js_expression.cc @@ -0,0 +1,310 @@ +// ---------------------------------------------------------------------------- +// Description : Javascript interpreter +// ---------------------------------------------------------------------------- +// (c) Copyright 2000 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#include +#include + + + + +#define EXJS_ADD_CODE_LOCATION \ + catch (no_location_javascript_exception &half) { \ + throw javascript_exception(half,getCodeLocation()); \ + } + + + + +using namespace ixion; +using namespace javascript; + + + + +// expression ----------------------------------------------------------------- +expression::expression(code_location const &loc) + : Location(loc) { + } + + + + +expression::~expression() { + } + + + + +// constant ------------------------------------------------------------------- +constant::constant(ref val,code_location const &loc) + : expression(loc),Value(val) { + } + + + + +ref +constant:: +evaluate(context const &ctx) const { + return Value; + } + + + + +// unary_operator ------------------------------------------------- +unary_operator::unary_operator(value::operator_id opt,ref opn,code_location const &loc) + : expression(loc),Operator(opt),Operand(opn) { + } + + + + +ref +unary_operator:: +evaluate(context const &ctx) const { + try { + return Operand->evaluate(ctx)->operatorUnary(Operator); + } + EXJS_ADD_CODE_LOCATION + } + + + + +// modifying_unary_operator --------------------------------------------------- +modifying_unary_operator:: +modifying_unary_operator(value::operator_id opt,ref opn,code_location const &loc) + : expression(loc),Operator(opt),Operand(opn) { + } + + + + +ref +modifying_unary_operator:: +evaluate(context const &ctx) const { + try { + return Operand->evaluate(ctx)->operatorUnaryModifying(Operator); + } + EXJS_ADD_CODE_LOCATION + } + + + + +// binary_operator ------------------------------------------------------------ +binary_operator::binary_operator(value::operator_id opt,ref opn1,ref opn2,code_location const &loc) + : expression(loc),Operator(opt),Operand1(opn1),Operand2(opn2) { + } + + + + +ref binary_operator::evaluate(context const &ctx) const { + try { + return Operand1->evaluate(ctx)->operatorBinary(Operator,Operand2->evaluate(ctx)); + } + EXJS_ADD_CODE_LOCATION + } + + + + +// binary_shortcut_operator --------------------------------------------------- +binary_shortcut_operator::binary_shortcut_operator(value::operator_id opt,ref opn1,ref opn2,code_location const &loc) + : expression(loc),Operator(opt),Operand1(opn1),Operand2(opn2) { + } + + + + +ref binary_shortcut_operator::evaluate(context const &ctx) const { + try { + return Operand1->evaluate(ctx)->operatorBinaryShortcut(Operator,*Operand2,ctx); + } + EXJS_ADD_CODE_LOCATION + } + + + + +// modifying_binary_operator -------------------------------------- +modifying_binary_operator:: +modifying_binary_operator(value::operator_id opt,ref opn1,ref opn2,code_location const &loc) + : expression(loc),Operator(opt),Operand1(opn1),Operand2(opn2) { + } + + + + +ref +modifying_binary_operator:: +evaluate(context const &ctx) const { + try { + return Operand1->evaluate(ctx)->operatorBinaryModifying(Operator,Operand2->evaluate(ctx)); + } + EXJS_ADD_CODE_LOCATION + } + + + + +// ternary_operator ----------------------------------------------------------- +ternary_operator:: +ternary_operator(ref opn1,ref opn2,ref opn3,code_location const &loc) + : expression(loc),Operand1(opn1),Operand2(opn2),Operand3(opn3) { + } + + + + +ref +ternary_operator:: +evaluate(context const &ctx) const { + try { + if (Operand1->evaluate(ctx)->toBoolean()) + return Operand2->evaluate(ctx); + else + return Operand3->evaluate(ctx); + } + EXJS_ADD_CODE_LOCATION + } + + + + +// subscript_operation -------------------------------------------------------- +subscript_operation::subscript_operation(ref opn1,ref opn2,code_location const &loc) + : expression(loc),Operand1(opn1),Operand2(opn2) { + } + + + + +ref subscript_operation::evaluate(context const &ctx) const { + try { + ref op2 = Operand2->evaluate(ctx); + return Operand1->evaluate(ctx)->subscript(*op2); + } + EXJS_ADD_CODE_LOCATION + } + + + + +// lookup_operation ----------------------------------------------------------- +lookup_operation::lookup_operation(string const &id,code_location const &loc) + : expression(loc),Identifier(id) { + } + + + + +lookup_operation::lookup_operation(ref opn,string const &id,code_location const &loc) + : expression(loc),Operand(opn),Identifier(id) { + } + + + + +ref lookup_operation::evaluate(context const &ctx) const { + try { + ref scope(ctx.LookupScope); + if (Operand.get() != NULL) + scope = Operand->evaluate(ctx); + return scope->lookup(Identifier); + } + EXJS_ADD_CODE_LOCATION + } + + + + +// assignment ----------------------------------------------------------------- +assignment:: +assignment(ref opn1,ref opn2,code_location const &loc) + : expression(loc),Operand1(opn1),Operand2(opn2) { + } + + + + +ref +assignment::evaluate(context const &ctx) const { + try { + return Operand1->evaluate(ctx)->assign(Operand2->evaluate(ctx)->eliminateWrappers()->duplicate()); + } + EXJS_ADD_CODE_LOCATION + } + + + + +// basic_call ----------------------------------------------------------------- +basic_call::basic_call(parameter_expression_list const &pexps,code_location const &loc) + : expression(loc),ParameterExpressionList(pexps) { + } + + + + +void basic_call::makeParameterValueList(context const &ctx,parameter_value_list &pvalues) const { + FOREACH_CONST(first,ParameterExpressionList,parameter_expression_list) { + pvalues.push_back((*first)->evaluate(ctx)); + } + } + + + + +// function_call -------------------------------------------------------------- +function_call::function_call(ref fun,parameter_expression_list const &pexps,code_location const &loc) + : super(pexps,loc),Function(fun) { + } + + + + +ref function_call::evaluate(context const &ctx) const { + try { + ref func_value = Function->evaluate(ctx); + + value::parameter_list pvalues; + makeParameterValueList(ctx,pvalues); + ref result = func_value->call(pvalues); + + if (result.get() == NULL) return makeNull(); + else return result; + } + EXJS_ADD_CODE_LOCATION + } + + + + +// construction --------------------------------------------------------------- +construction::construction(ref cls,parameter_expression_list const &pexps,code_location const &loc) + : super(pexps,loc),Class(cls) { + } + + + + +ref construction::evaluate(context const &ctx) const { + try { + ref class_value = Class->evaluate(ctx); + + value::parameter_list pvalues; + makeParameterValueList(ctx,pvalues); + + return class_value->construct(pvalues); + } + EXJS_ADD_CODE_LOCATION + } diff --git a/simgear/interpreter/js_instruction.cc b/simgear/interpreter/js_instruction.cc new file mode 100644 index 00000000..212954d1 --- /dev/null +++ b/simgear/interpreter/js_instruction.cc @@ -0,0 +1,413 @@ +// ---------------------------------------------------------------------------- +// Description : Javascript interpreter +// ---------------------------------------------------------------------------- +// (c) Copyright 2000 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#include +#include + + + + +using namespace ixion; +using namespace javascript; + + + + +// instruction_list ----------------------------------------------------------- +ref +instruction_list::evaluate(context const &ctx) const { + ref result; + FOREACH_CONST(first,ExpressionList,expression_list) + result = (*first)->evaluate(ctx); + return result; + } + + + + +void instruction_list::add(ref expr) { + ExpressionList.push_back(expr); + } + + + + +// scoped_instruction_list ---------------------------------------- +ref scoped_instruction_list::evaluate(context const &ctx) const { + ref scope = new list_scope; + scope->unite(ctx.LookupScope); + + ref result = instruction_list::evaluate(context(scope)); + if (result.get()) return result->duplicate(); + return ref(NULL); + + // ATTENTION: this is a scope cancellation point. + } + + + + +// js_if ---------------------------------------------------------------------- +js_if::js_if(ref cond,ref ifex,ref ifnotex,code_location const &loc) + : expression(loc),Conditional(cond),IfExpression(ifex),IfNotExpression(ifnotex) { + } + + + + +ref js_if::evaluate(context const &ctx) const { + if (Conditional->evaluate(ctx)->toBoolean()) + return IfExpression->evaluate(ctx); + else + if (IfNotExpression.get()) + return IfNotExpression->evaluate(ctx); + else + return ref(NULL); + } + + + + +// js_while ------------------------------------------------------------------- +js_while::js_while(ref cond,ref loopex,code_location const &loc) + : expression(loc),Conditional(cond),LoopExpression(loopex),HasLabel(false) { + } + + + + +js_while::js_while(ref cond,ref loopex,string const &label,code_location const &loc) + : expression(loc),Conditional(cond),LoopExpression(loopex),HasLabel(true),Label(label) { + } + + + + +ref js_while::evaluate(context const &ctx) const { + ref result; + while (Conditional->evaluate(ctx)->toBoolean()) { + try { + result = LoopExpression->evaluate(ctx); + } + catch (break_exception &be) { + if (!be.HasLabel || (HasLabel && be.HasLabel && be.Label == Label)) + break; + else throw; + } + catch (continue_exception &ce) { + if (!ce.HasLabel || (HasLabel && ce.HasLabel && ce.Label == Label)) + continue; + else throw; + } + } + return result; + } + + + + +// js_do_while ---------------------------------------------------------------- +js_do_while::js_do_while(ref cond,ref loopex,code_location const &loc) + : expression(loc),Conditional(cond),LoopExpression(loopex),HasLabel(false) { + } + + + + +js_do_while::js_do_while(ref cond,ref loopex,string const &label,code_location const &loc) + : expression(loc),Conditional(cond),LoopExpression(loopex),HasLabel(true),Label(label) { + } + + + + +ref js_do_while::evaluate(context const &ctx) const { + ref result; + do { + try { + result = LoopExpression->evaluate(ctx); + } + catch (break_exception &be) { + if (!be.HasLabel || (HasLabel && be.HasLabel && be.Label == Label)) + break; + else throw; + } + catch (continue_exception &ce) { + if (!ce.HasLabel || (HasLabel && ce.HasLabel && ce.Label == Label)) + continue; + else throw; + } + } while (Conditional->evaluate(ctx)->toBoolean()); + return result; + } + + + + +// js_for --------------------------------------------------------------------- +js_for::js_for(ref init,ref cond,ref update,ref loop,code_location const &loc) + : expression(loc),Initialization(init),Conditional(cond),Update(update), + LoopExpression(loop),HasLabel(false) { + } + + + + +js_for::js_for(ref init,ref cond,ref update,ref loop,string const &label,code_location const &loc) + : expression(loc),Initialization(init),Conditional(cond),Update(update),LoopExpression(loop), + HasLabel(true),Label(label) { + } + + + + +ref js_for::evaluate(context const &ctx) const { + ref scope = new list_scope; + scope->unite(ctx.LookupScope); + context inner_context(scope); + + ref result; + for (Initialization->evaluate(inner_context);Conditional->evaluate(inner_context)->toBoolean(); + Update->evaluate(inner_context)) { + try { + result = LoopExpression->evaluate(inner_context); + } + catch (break_exception &be) { + if (!be.HasLabel || (HasLabel && be.HasLabel && be.Label == Label)) + break; + else throw; + } + catch (continue_exception &ce) { + if (!ce.HasLabel || (HasLabel && ce.HasLabel && ce.Label == Label)) + continue; + else throw; + } + } + return result; + } + + + + +// js_for_in ------------------------------------------------------------------ +js_for_in::js_for_in(ref iter,ref array,ref loop,code_location const &loc) + : expression(loc),Iterator(iter),Array(array),LoopExpression(loop),HasLabel(false) { + } + + + + +js_for_in::js_for_in(ref iter,ref array,ref loop,string const &label,code_location const &loc) + : expression(loc),Iterator(iter),Array(array),LoopExpression(loop), + HasLabel(true),Label(label) { + } + + + + +ref js_for_in::evaluate(context const &ctx) const { + ref scope = new list_scope; + scope->unite(ctx.LookupScope); + context inner_context(scope); + + ref result; + ref iterator = Iterator->evaluate(inner_context); + ref array = Array->evaluate(inner_context); + + TSize size = array->lookup("length")->toInt(); + + for (TIndex i = 0;i < size;i++) { + try { + iterator->assign(array->subscript(*makeConstant(i))); + result = LoopExpression->evaluate(inner_context); + } + catch (break_exception &be) { + if (!be.HasLabel || (HasLabel && be.HasLabel && be.Label == Label)) + break; + else throw; + } + catch (continue_exception &ce) { + if (!ce.HasLabel || (HasLabel && ce.HasLabel && ce.Label == Label)) + continue; + else throw; + } + } + if (result.get()) return result->duplicate(); + return ref(NULL); + + // ATTENTION: this is a scope cancellation point. + } + + + + +// js_return ------------------------------------------------------------------ +js_return::js_return(ref retval,code_location const &loc) + : expression(loc),ReturnValue(retval) { + } + + + + +ref js_return::evaluate(context const &ctx) const { + ref retval; + if (ReturnValue.get()) + retval = ReturnValue->evaluate(ctx); + + throw return_exception(retval,getCodeLocation()); + } + + + + +// js_break ------------------------------------------------------------------- +js_break::js_break(code_location const &loc) + : expression(loc),HasLabel(false) { + } + + + + +js_break::js_break(string const &label,code_location const &loc) + : expression(loc),HasLabel(true),Label(label) { + } + + + + +ref js_break::evaluate(context const &ctx) const { + throw break_exception(HasLabel,Label,getCodeLocation()); + } + + + + +// js_continue ---------------------------------------------------------------- +js_continue::js_continue(code_location const &loc) + : expression(loc),HasLabel(false) { + } + + + + +js_continue::js_continue(string const &label,code_location const &loc) + : expression(loc),HasLabel(true),Label(label) { + } + + + + +ref js_continue::evaluate(context const &ctx) const { + throw continue_exception(HasLabel,Label,getCodeLocation()); + } + + + + +// break_label ---------------------------------------------------------------- +break_label::break_label(string const &label,ref expr,code_location const &loc) + : expression(loc),Label(label),Expression(expr) { + } + + + + +ref +break_label::evaluate(context const &ctx) const { + try { + return Expression->evaluate(ctx); + } + catch (break_exception &be) { + if (be.HasLabel && be.Label == Label) return ref(NULL); + else throw; + } + } + + + + +// js_switch ----------------------------------------------------------------- +js_switch::js_switch(ref discriminant,code_location const &loc) + : expression(loc),HasLabel(false),Discriminant(discriminant) { + } + + + + +js_switch::js_switch(ref discriminant,string const &label,code_location const &loc) + : expression(loc),HasLabel(true),Label(label),Discriminant(discriminant) { + } + + + + +ref +js_switch:: +evaluate(context const &ctx) const { + ref scope = new list_scope; + scope->unite(ctx.LookupScope); + context inner_context(scope); + + ref discr = Discriminant->evaluate(inner_context); + + case_list::const_iterator expr,def; + bool expr_found = false,def_found = false; + FOREACH_CONST(first,CaseList,case_list) { + if (first->DiscriminantValue.get()) { + if (first->DiscriminantValue->evaluate(inner_context)-> + operatorBinary(value::OP_EQUAL,Discriminant->evaluate(inner_context))->toBoolean()) { + expr = first; + expr_found = true; + break; + } + } + else { + if (!def_found) { + def = first; + def_found = true; + } + } + } + + try { + case_list::const_iterator exec,last = CaseList.end(); + if (expr_found) + exec = expr; + else if (def_found) + exec = def; + else + return ref(NULL); + + ref result; + while (exec != last) { + result = exec->Expression->evaluate(inner_context); + exec++; + } + if (result.get()) return result->duplicate(); + return ref(NULL); + } + catch (break_exception &be) { + if (!be.HasLabel || (HasLabel && be.HasLabel && be.Label == Label)) + return ref(NULL); + else + throw; + } + + // ATTENTION: this is a scope cancellation point. + } + + + + +void js_switch::addCase(ref dvalue,ref expr) { + case_label cl; + cl.DiscriminantValue = dvalue; + cl.Expression = expr; + CaseList.push_back(cl); + } diff --git a/simgear/interpreter/js_interpreter.cc b/simgear/interpreter/js_interpreter.cc new file mode 100644 index 00000000..c4059cae --- /dev/null +++ b/simgear/interpreter/js_interpreter.cc @@ -0,0 +1,1133 @@ +// ---------------------------------------------------------------------------- +// Description : Javascript interpreter +// ---------------------------------------------------------------------------- +// (c) Copyright 2000 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#include +#include "ixlib_i18n.hh" +#include +#include +#include +#include +#include + + + + +// tools ---------------------------------------------------------------------- +#define ADVANCE \ + first++; \ + if (first == last) EXJS_THROW(ECJS_UNEXPECTED_EOF) +#define EXPECT(WHAT,STRINGIFIED) \ + if (first == last) EXJS_THROW(ECJS_UNEXPECTED_EOF) \ + if (first->Type != WHAT) \ + EXJS_THROWINFOTOKEN(ECJS_UNEXPECTED,("'"+first->Text+"' "+_("instead of ")+STRINGIFIED).c_str(),*first) + + + + +static char *dummy_i18n_referencer = N_("instead of "); + + + +using namespace ixion; +using namespace javascript; + + + + +namespace { + ref + makeConstantExpression(ref val,code_location const &loc) { + ref result(new constant(val,loc)); + return result; + } + } + + + + +// garbage collectors --------------------------------------------------------- +IXLIB_GARBAGE_DECLARE_MANAGER(value) +IXLIB_GARBAGE_DECLARE_MANAGER(expression) + + + + +// exception texts ------------------------------------------------------------ +static char *(PlainText[]) ={ + N_("Unterminated comment"), + N_("Cannot convert"), + N_("Invalid operation"), + N_("Unexpected token encountered"), + N_("Unexpected end of file"), + N_("Cannot modify rvalue"), + N_("Unknown identifier"), + N_("Unknown operator"), + N_("Invalid non-local exit"), + N_("Invalid number of arguments"), + N_("Invalid token encountered"), + N_("Cannot redeclare identifier"), + N_("Constructor called on constructed object"), + N_("No superclass available"), + N_("Division by zero"), + }; + + + + +// no_location_javascript_exception ------------------------------------------- +char *no_location_javascript_exception::getText() const { + return _(PlainText[Error]); + } + + + + +// javascript_exception ------------------------------------------------------- +javascript_exception::javascript_exception(TErrorCode error,code_location const &loc,char const *info,char *module = NULL, + TIndex line = 0) +: base_exception(error, NULL, module, line, "JS") { + HasInfo = true; + try { + string temp = loc.stringify(); + if (info) { + temp += ": "; + temp += info; + } + strcpy(Info,temp.c_str()); + } + catch (...) { } + } + + + +javascript_exception::javascript_exception(no_location_javascript_exception const &half_ex,javascript::code_location const &loc) +: base_exception(half_ex.Error,NULL,half_ex.Module,half_ex.Line,half_ex.Category) { + HasInfo = true; + try { + string temp = loc.stringify() + ": " + half_ex.Info; + strcpy(Info,temp.c_str()); + } + catch (...) { } + } + + + + +char *javascript_exception::getText() const { + return _(PlainText[Error]); + } + + + + +// code_location -------------------------------------------------------------- +code_location::code_location(scanner::token &tok) + : Line(tok.Line) { + } + + + + +code_location::code_location(TIndex line) + : Line(line) { + } + + + + +string code_location::stringify() const { + return "l"+unsigned2dec(Line); + } + + + + +// context -------------------------------------------------------------------- +context::context(ref scope) + : DeclarationScope(scope),LookupScope(scope) { + } + + + + +context::context(ref scope) + : LookupScope(scope) { + } + + + + +context::context(ref decl_scope,ref lookup_scope) + : DeclarationScope(decl_scope),LookupScope(lookup_scope) { + } + + + + +// parser --------------------------------------------------------------------- +namespace { + ref + parseInstructionList(scanner::token_iterator &first,scanner::token_iterator const &last,bool scoped); + ref + parseSwitch(scanner::token_iterator &first,scanner::token_iterator const &last,string const &label); + ref + parseVariableDeclaration(scanner::token_iterator &first,scanner::token_iterator const &last); + ref + parseConstantDeclaration(scanner::token_iterator &first,scanner::token_iterator const &last); + ref + parseFunctionDeclaration(scanner::token_iterator &first,scanner::token_iterator const &last); + ref + parseMethodDeclaration(scanner::token_iterator &first,scanner::token_iterator const &last); + ref + parseConstructorDeclaration(scanner::token_iterator &first,scanner::token_iterator const &last,string const &class_id); + ref + parseClassDeclaration(scanner::token_iterator &first,scanner::token_iterator const &last); + ref + parseInstruction(scanner::token_iterator &first,scanner::token_iterator const &last); + ref + parseExpression(scanner::token_iterator &first,scanner::token_iterator const &last,int precedence = 0); + + + + + ref + parseInstructionList(scanner::token_iterator &first,scanner::token_iterator const &last,bool scoped) { + auto_ptr ilist( + scoped + ? new scoped_instruction_list(*first) + : new instruction_list(*first)); + + while (first != last && first->Type != '}') { + ref expr = parseInstruction(first,last); + if (expr.get() != NULL) + ilist->add(expr); + } + return ilist.release(); + } + + + + + ref + parseSwitch(scanner::token_iterator &first,scanner::token_iterator const &last,string const &label) { + code_location loc(*first); + + ADVANCE + EXPECT('(',_("'(' in switch statement")) + ADVANCE + + ref discr = parseExpression(first,last); + EXPECT(')',_("')' in switch statement")) + ADVANCE + + EXPECT('{',_("'{' in switch statement")) + ADVANCE + + auto_ptr sw; + if (label.size()) { + auto_ptr tsw(new js_switch(discr,label,loc)); + sw = tsw; + } + else { + auto_ptr tsw(new js_switch(discr,loc)); + sw = tsw; + } + + ref dvalue; + auto_ptr ilist; + + while (first != last && first->Type != '}') { + if (first->Type == TT_JS_CASE) { + if (ilist.get()) + sw->addCase(dvalue,ilist.release()); + + auto_ptr tilist(new instruction_list(*first)); + ilist = tilist; + + ADVANCE + + dvalue = parseExpression(first,last); + EXPECT(':',_("':' in case label")) + ADVANCE + } + else if (first->Type == TT_JS_DEFAULT) { + if (ilist.get()) + sw->addCase(dvalue,ilist.release()); + + auto_ptr tilist(new instruction_list(*first)); + ilist = tilist; + + ADVANCE + dvalue = NULL; + EXPECT(':',_("':' in default label")) + ADVANCE + } + else { + ref expr = parseInstruction(first,last); + if (ilist.get() && expr.get() != NULL) + ilist->add(expr); + } + } + + if (ilist.get()) + sw->addCase(dvalue,ilist.release()); + + EXPECT('}',_("'}' in switch statement")) + ADVANCE + + return sw.release(); + } + + + + + ref + parseVariableDeclaration(scanner::token_iterator &first,scanner::token_iterator const &last) { + code_location loc(*first); + + EXPECT(TT_JS_IDENTIFIER,_("variable identifier")) + string id = first->Text; + ADVANCE + + ref def; + if (first->Type == '=') { + ADVANCE + def = parseExpression(first,last); + } + ref result = new variable_declaration(id,def,loc); + + if (first->Type == ',') { + auto_ptr ilist(new instruction_list(*first)); + ilist->add(result); + + while (first->Type == ',') { + ADVANCE + + code_location loc(*first); + + EXPECT(TT_JS_IDENTIFIER,_("variable identifier")) + id = first->Text; + ADVANCE + + if (first->Type == '=') { + ADVANCE + def = parseExpression(first,last); + } + ref decl = new variable_declaration(id,def,loc); + ilist->add(decl); + } + result = ilist.release(); + } + + return result; + } + + + + + ref + parseConstantDeclaration(scanner::token_iterator &first,scanner::token_iterator const &last) { + code_location loc(*first); + + EXPECT(TT_JS_IDENTIFIER,_("constant identifier")) + string id = first->Text; + ADVANCE + + ref def; + EXPECT('=',_("initializer for constant")) + ADVANCE + def = parseExpression(first,last); + + ref result = new constant_declaration(id,def,loc); + + if (first->Type == ',') { + auto_ptr ilist(new instruction_list(*first)); + ilist->add(result); + + while (first->Type == ',') { + ADVANCE + + code_location loc(*first); + + EXPECT(TT_JS_IDENTIFIER,_("constant identifier")) + id = first->Text; + ADVANCE + + EXPECT('=',_("initializer for constant")) + ADVANCE + def = parseExpression(first,last); + + ref decl = new constant_declaration(id,def,loc); + ilist->add(decl); + } + result = ilist.release(); + } + + return result; + } + + + + + ref + parseFunctionDeclaration(scanner::token_iterator &first,scanner::token_iterator const &last) { + code_location loc(*first); + + EXPECT(TT_JS_IDENTIFIER,_("function identifier")) + string id = first->Text; + ADVANCE + + EXPECT('(',_("'(' in function declaration")) + ADVANCE + + function::parameter_name_list pnames; + + while (first->Type != ')') { + EXPECT(TT_JS_IDENTIFIER,_("parameter identifier")) + pnames.push_back(first->Text); + ADVANCE + + if (first->Type == ',') { + ADVANCE + } + } + EXPECT(')',_("')' in function declaration")) + ADVANCE + + EXPECT('{',_("'{' in function definition")) + ADVANCE + + ref body = parseInstructionList(first,last,false); + + EXPECT('}',"'}' in method definition") + first++; + + ref result = new function_declaration(id,pnames,body,loc); + return result; + } + + + + + ref + parseMethodDeclaration(scanner::token_iterator &first,scanner::token_iterator const &last) { + code_location loc(*first); + + EXPECT(TT_JS_IDENTIFIER,_("method identifier")) + string id = first->Text; + ADVANCE + + EXPECT('(',_("'(' in method declaration")) + ADVANCE + + method::parameter_name_list pnames; + + while (first->Type != ')') { + EXPECT(TT_JS_IDENTIFIER,_("parameter identifier")) + pnames.push_back(first->Text); + ADVANCE + + if (first->Type == ',') { + ADVANCE + } + } + EXPECT(')',_("')' in method declaration")) + ADVANCE + + EXPECT('{',_("'{' in method definition")) + ADVANCE + + ref body = parseInstructionList(first,last,false); + + EXPECT('}',"'}' in method definition") + first++; + + ref result = new method_declaration(id,pnames,body,loc); + return result; + } + + + + + ref + parseConstructorDeclaration(scanner::token_iterator &first,scanner::token_iterator const &last,string const &class_id) { + code_location loc(*first); + + EXPECT(TT_JS_IDENTIFIER,_("constructor identifier")) + if (first->Text != class_id) { + EXJS_THROWINFOTOKEN(ECJS_UNEXPECTED,("'"+first->Text+"' "+_("instead of ")+_("class identifier")).c_str(),*first) + } + ADVANCE + + EXPECT('(',_("'(' in constructor declaration")) + ADVANCE + + method::parameter_name_list pnames; + + while (first->Type != ')') { + EXPECT(TT_JS_IDENTIFIER,_("parameter identifier")) + pnames.push_back(first->Text); + ADVANCE + + if (first->Type == ',') { + ADVANCE + } + } + EXPECT(')',_("')' in constructor declaration")) + ADVANCE + + EXPECT('{',_("'{' in constructor definition")) + ADVANCE + + ref body = parseInstructionList(first,last,false); + + EXPECT('}',"'}' in constructor definition") + first++; + + ref result = new constructor_declaration(pnames,body,loc); + return result; + } + + + + + ref + parseClassDeclaration(scanner::token_iterator &first,scanner::token_iterator const &last) { + code_location loc(*first); + + EXPECT(TT_JS_IDENTIFIER,_("class identifier")) + string id = first->Text; + ADVANCE + + ref superclass; + if (first->Type == TT_JS_EXTENDS) { + ADVANCE + superclass = parseExpression(first,last); + } + + EXPECT('{',_("'{' in class declaration")) + ADVANCE + + auto_ptr decl(new js_class_declaration(id,superclass,loc)); + + while (first != last && first->Type != '}') { + if (first->Type == TT_JS_STATIC) { + ADVANCE + + if (first->Type == TT_JS_FUNCTION) { + ADVANCE + decl->addStaticMethod(parseFunctionDeclaration(first,last)); + } + else { + if (first->Type == TT_JS_CONST) { + ADVANCE + decl->addStaticVariable(parseConstantDeclaration(first,last)); + } + else { + ADVANCE + decl->addStaticVariable(parseVariableDeclaration(first,last)); + } + EXPECT(';',"';'") + first++; + } + } + else { + if (first->Type == TT_JS_FUNCTION) { + ADVANCE + decl->addMethod(parseMethodDeclaration(first,last)); + } + else if (first->Type == TT_JS_CONSTRUCTOR) { + ADVANCE + + EXPECT(TT_JS_FUNCTION,_("'function' keyword")) + ADVANCE + + decl->setConstructor(parseConstructorDeclaration(first,last,id)); + } + else { + if (first->Type == TT_JS_CONST) { + ADVANCE + decl->addVariable(parseConstantDeclaration(first,last)); + } + else { + ADVANCE + decl->addVariable(parseVariableDeclaration(first,last)); + } + EXPECT(';',"';'") + first++; + } + } + } + + EXPECT('}',"'}' in class declaration") + first++; + + return decl.release(); + } + + + + + ref + parseInstruction(scanner::token_iterator &first,scanner::token_iterator const &last) { + if (first == last) EXJS_THROW(ECJS_UNEXPECTED_EOF) + + string label; + if (first+1 != last && first[1].Type == ':') { + label = first->Text; + ADVANCE + ADVANCE + } + + ref result; + + if (first->Type == '{') { + ADVANCE + result = parseInstructionList(first,last,true); + EXPECT('}',"'}'") + first++; + } + else if (first->Type == TT_JS_VAR) { + ADVANCE + + result = parseVariableDeclaration(first,last); + + EXPECT(';',"';'") + first++; + } + else if (first->Type == TT_JS_CONST) { + ADVANCE + + result = parseConstantDeclaration(first,last); + + EXPECT(';',"';'") + first++; + } + else if (first->Type == TT_JS_FUNCTION) { + ADVANCE + result = parseFunctionDeclaration(first,last); + } + else if (first->Type == TT_JS_CLASS) { + ADVANCE + result = parseClassDeclaration(first,last); + } + else if (first->Type == TT_JS_IF) { + code_location loc(*first); + ADVANCE + + EXPECT('(',_("'(' in if statement")) + ADVANCE + + ref cond = parseExpression(first,last); + EXPECT(')',_("')' in if statement")) + first++; + ref if_expr = parseInstruction(first,last); + ref else_expr; + + if (first != last && first->Type == TT_JS_ELSE) { + ADVANCE + else_expr = parseInstruction(first,last); + } + result = new js_if(cond,if_expr,else_expr,loc); + } + else if (first->Type == TT_JS_SWITCH) { + result = parseSwitch(first,last,label); + } + else if (first->Type == TT_JS_WHILE) { + code_location loc(*first); + + ADVANCE + EXPECT('(',_("'(' in while statement")) + ADVANCE + + ref cond = parseExpression(first,last); + EXPECT(')',_("')' in while statement")) + first++; + + ref loop_expr = parseInstruction(first,last); + + if (label.size()) { + result = new js_while(cond,loop_expr,label,loc); + label = ""; + } + else + result = new js_while(cond,loop_expr,loc); + } + else if (first->Type == TT_JS_DO) { + code_location loc(*first); + + ADVANCE + ref loop_expr = parseInstruction(first,last); + + EXPECT(TT_JS_WHILE,_("'while' in do-while")) + ADVANCE + + EXPECT('(',_("'(' in do-while statement")) + ADVANCE + + ref cond = parseExpression(first,last); + EXPECT(')',_("')' in do-while statement")) + first++; + + if (label.size()) { + result = new js_do_while(cond,loop_expr,label,loc); + label = ""; + } + else + result = new js_do_while(cond,loop_expr,loc); + } + else if (first->Type == TT_JS_FOR) { + code_location loc(*first); + + ADVANCE + + EXPECT('(',_("'(' in for statement")) + ADVANCE + + ref init_expr; + if (first->Type == TT_JS_VAR) { + ADVANCE + + init_expr = parseVariableDeclaration(first,last); + } + else + init_expr = parseExpression(first,last); + + if (first->Type == TT_JS_IN) { + // for (iterator in list) + ADVANCE + ref array_expr = parseExpression(first,last); + + EXPECT(')',_("')' in for statement")) + first++; + + ref loop_expr = parseInstruction(first,last); + + if (label.size()) { + result = new js_for_in(init_expr,array_expr,loop_expr,label,loc); + label = ""; + } + else + result = new js_for_in(init_expr,array_expr,loop_expr,label,loc); + } + else { + // for (;;) ... + EXPECT(';',_("';' in for statement")) + ADVANCE + + ref cond_expr = parseExpression(first,last); + + EXPECT(';',_("';' in for statement")) + ADVANCE + + ref update_expr = parseExpression(first,last); + + EXPECT(')',_("')' in for statement")) + first++; + + ref loop_expr = parseInstruction(first,last); + + if (label.size()) { + result = new js_for(init_expr,cond_expr,update_expr,loop_expr,label,loc); + label = ""; + } + else + result = new js_for(init_expr,cond_expr,update_expr,loop_expr,loc); + } + } + else if (first->Type == TT_JS_RETURN) { + code_location loc(*first); + ADVANCE + + if (first->Type != ';') + result = new js_return(parseExpression(first,last),loc); + else + result = new js_return(makeConstantExpression(makeNull(),loc),loc); + + EXPECT(';',"';'") + first++; + } + else if (first->Type == TT_JS_BREAK) { + code_location loc(*first); + ADVANCE + + if (first->Type != ';') { + EXPECT(TT_JS_IDENTIFIER,_("break label")) + result = new js_break(first->Text,loc); + ADVANCE + } + else + result = new js_break(loc); + + EXPECT(';',"';'") + first++; + } + else if (first->Type == TT_JS_CONTINUE) { + code_location loc(*first); + ADVANCE + + if (first->Type != ';') { + EXPECT(TT_JS_IDENTIFIER,_("continue label")) + result = new js_continue(first->Text,loc); + ADVANCE + } + else + result = new js_continue(loc); + + EXPECT(';',"';'") + first++; + } + else if (first->Type == ';') { + first++; + result = makeConstantExpression(ref(NULL),*first); + } + else { + // was nothing else, must be expression + result = parseExpression(first,last); + EXPECT(';',"';'") + first++; + } + + if (label.size()) { + result = new break_label(label,result,*first); + } + + return result; + } + + + + + namespace { + int const + PREC_COMMA = 10, // , (if implemented) + PREC_THROW = 20, // throw (if implemented) + PREC_ASSIGN = 30, // += and friends [ok] + PREC_CONDITIONAL = 40, // ?: [ok] + PREC_LOG_OR = 50, // || [ok] + PREC_LOG_AND = 60, // && [ok] + PREC_BIT_OR = 70, // | [ok] + PREC_BIT_XOR = 80, // ^ [ok] + PREC_BIT_AND = 90, // & [ok] + PREC_EQUAL = 100, // == != === !=== [ok] + PREC_COMP = 110, // < <= > >= [ok] + PREC_SHIFT = 120, // << >> [ok] + PREC_ADD = 130, // + - [ok] + PREC_MULT = 140, // * / % [ok] + PREC_UNARY = 160, // new + - ++ -- ! ~ + PREC_FUNCALL = 170, // () + PREC_SUBSCRIPT = 180, // [] . + PREC_TERM = 200; // literal identifier + } + + + + + ref + parseExpression(scanner::token_iterator &first,scanner::token_iterator const &last,int precedence) { + /* + precedence: + the called routine will continue parsing as long as the encountered + operators all have precedence greater than or equal to the given parameter. + */ + + // an EOF condition cannot legally occur inside + // or at the end of an expression. + + if (first == last) EXJS_THROW(ECJS_UNEXPECTED_EOF) + + ref expr; + + // parse prefix unaries ----------------------------------------------------- + if (precedence <= PREC_UNARY) { + if (first->Type == TT_JS_NEW) { + code_location loc(*first); + ADVANCE + + ref cls = parseExpression(first,last,PREC_SUBSCRIPT); + + EXPECT('(',_("'(' in 'new' expression")) + ADVANCE + + function_call::parameter_expression_list pexps; + while (first->Type != ')') { + pexps.push_back(parseExpression(first,last)); + + if (first->Type == ',') { + ADVANCE + } + } + + EXPECT(')',_("')' in 'new' expression")) + ADVANCE + + expr = new construction(cls,pexps,loc); + } + if (first->Type == TT_JS_INCREMENT || first->Type == TT_JS_DECREMENT) { + code_location loc(*first); + value::operator_id op = value::token2operator(*first,true,true); + ADVANCE + expr = new modifying_unary_operator(op,parseExpression(first,last,PREC_UNARY),loc); + } + if (first->Type == '+' ||first->Type == '-' + || first->Type == '!' || first->Type == '~') { + code_location loc(*first); + value::operator_id op = value::token2operator(*first,true,true); + ADVANCE + expr = new unary_operator(op,parseExpression(first,last,PREC_UNARY),loc); + } + } + + // parse parentheses -------------------------------------------------------- + if (first->Type == '(') { + ADVANCE + expr = parseExpression(first,last); + EXPECT(')',"')'") + ADVANCE + } + + // parse term --------------------------------------------------------------- + if (expr.get() == NULL && precedence <= PREC_TERM) { + if (first->Type == TT_JS_LIT_INT) { + expr = makeConstantExpression(makeConstant(evalUnsigned(first->Text)),*first); + ADVANCE + } + else if (first->Type == TT_JS_LIT_FLOAT) { + expr = makeConstantExpression(makeConstant(evalFloat(first->Text)),*first); + ADVANCE + } + else if (first->Type == TT_JS_LIT_STRING) { + expr = makeConstantExpression(makeConstant(parseCEscapes( + first->Text.substr(1,first->Text.size()-2) + )),*first); + ADVANCE + } + else if (first->Type == TT_JS_LIT_TRUE) { + expr = makeConstantExpression(makeConstant(true),*first); + ADVANCE + } + else if (first->Type == TT_JS_LIT_FALSE) { + expr = makeConstantExpression(makeConstant(false),*first); + ADVANCE + } + else if (first->Type == TT_JS_LIT_UNDEFINED) { + expr = makeConstantExpression(makeUndefined(),*first); + ADVANCE + } + else if (first->Type == TT_JS_IDENTIFIER) { + expr = new lookup_operation(first->Text,*first); + ADVANCE + } + else if (first->Type == TT_JS_NULL) { + expr = makeConstantExpression(makeNull(),*first); + ADVANCE + } + } + + if (expr.get() == NULL) + EXJS_THROWINFOTOKEN(ECJS_UNEXPECTED,("'"+first->Text+"' instead of expression").c_str(),*first) + + bool parsed_something; + do { + parsed_something = false; + + // parse postfix "subscripts" --------------------------------------------- + if (first->Type == '(' && precedence <= PREC_FUNCALL) { + code_location loc(*first); + ADVANCE + + function_call::parameter_expression_list pexps; + while (first->Type != ')') { + pexps.push_back(parseExpression(first,last)); + + if (first->Type == ',') { + ADVANCE + } + } + + EXPECT(')',_("')' in function call")) + ADVANCE + + expr = new function_call(expr,pexps,loc); + parsed_something = true; + } + // parse postfix unary ---------------------------------------------------- + else if ((first->Type == TT_JS_INCREMENT || first->Type == TT_JS_DECREMENT) && precedence <= PREC_UNARY) { + value::operator_id op = value::token2operator(*first,true); + expr = new modifying_unary_operator(op,expr,*first); + parsed_something = true; + ADVANCE + } + // parse special binary operators ----------------------------------------- + else if (first->Type == '.' && precedence <= PREC_SUBSCRIPT) { + ADVANCE + expr = new lookup_operation(expr,first->Text,*first); + ADVANCE + parsed_something = true; + } + else if (first->Type == '[' && precedence <= PREC_SUBSCRIPT) { + code_location loc(*first); + ADVANCE + + ref index = parseExpression(first,last); + + EXPECT(']',_("']' in subscript")) + ADVANCE + + expr = new subscript_operation(expr,index,loc); + + parsed_something = true; + } + + // parse regular binary operators ----------------------------------------- + #define BINARY_OP(PRECEDENCE,EXPRESSION,TYPE) \ + else if ((EXPRESSION) && precedence <= PRECEDENCE) { \ + code_location loc(*first); \ + value::operator_id op = value::token2operator(*first); \ + ADVANCE \ + ref right = parseExpression(first,last,PRECEDENCE); \ + expr = new TYPE##_operator(op,expr,right,loc); \ + parsed_something = true; \ + } + + BINARY_OP(PREC_MULT,first->Type == '*' || first->Type == '/' || first->Type == '%',binary) + BINARY_OP(PREC_ADD,first->Type == '+' || first->Type == '-',binary) + BINARY_OP(PREC_SHIFT,first->Type == TT_JS_LEFT_SHIFT || first->Type == TT_JS_RIGHT_SHIFT,binary) + BINARY_OP(PREC_COMP,first->Type == '>' || first->Type == '<' + || first->Type == TT_JS_LESS_EQUAL || first->Type == TT_JS_GREATER_EQUAL + || first->Type == TT_JS_IDENTICAL || first->Type == TT_JS_NOT_IDENTICAL,binary) + BINARY_OP(PREC_EQUAL,first->Type == TT_JS_EQUAL || first->Type == TT_JS_NOT_EQUAL,binary) + BINARY_OP(PREC_BIT_AND,first->Type == '&',binary) + BINARY_OP(PREC_BIT_XOR,first->Type == '^',binary) + BINARY_OP(PREC_BIT_OR,first->Type == '|',binary) + BINARY_OP(PREC_LOG_AND,first->Type == TT_JS_LOGICAL_AND,binary_shortcut) + BINARY_OP(PREC_LOG_OR,first->Type == TT_JS_LOGICAL_OR,binary_shortcut) + else if (first->Type == '?') { + code_location loc(*first); + ADVANCE + ref op2 = parseExpression(first,last); + if (first->Type != ':') + EXJS_THROWINFO(ECJS_UNEXPECTED,(first->Text+" instead of ':'").c_str()) + ADVANCE + ref op3 = parseExpression(first,last,PREC_CONDITIONAL); + expr = new ternary_operator(expr,op2,op3,loc); + parsed_something = true; + } + else if (first->Type == '=' && precedence <= PREC_ASSIGN) { + code_location loc(*first); + ADVANCE + ref op2 = parseExpression(first,last); + expr = new assignment(expr,op2,loc); + parsed_something = true; + } + BINARY_OP(PREC_ASSIGN,first->Type == TT_JS_PLUS_ASSIGN + || first->Type == TT_JS_MINUS_ASSIGN + || first->Type == TT_JS_MULTIPLY_ASSIGN + || first->Type == TT_JS_DIVIDE_ASSIGN + || first->Type == TT_JS_MODULO_ASSIGN + || first->Type == TT_JS_BIT_XOR_ASSIGN + || first->Type == TT_JS_BIT_AND_ASSIGN + || first->Type == TT_JS_BIT_OR_ASSIGN + || first->Type == TT_JS_LEFT_SHIFT_ASSIGN + || first->Type == TT_JS_RIGHT_SHIFT_ASSIGN,modifying_binary) + } while (parsed_something); + + return expr; + } + } + + + + +// interpreter ----------------------------------------------------------------- +interpreter::interpreter() { + RootScope = new list_scope; + + ref ac = new js_array_constructor(); + RootScope->addMember("Array",ac); + } + + + + +interpreter::~interpreter() { + } + + + + +ref interpreter::parse(string const &str) { + // *** FIXME: this works around a bug in istrstream + if (str.size() == 0) { + return ref(NULL); + } + istrstream strm(str.data(),str.size()); + return parse(strm); + } + + + + +ref interpreter::parse(istream &istr) { + jsFlexLexer lexer(&istr); + scanner scanner(lexer); + + scanner::token_list tokenlist = scanner.scan(); + scanner::token_iterator text = tokenlist.begin(); + return parseInstructionList(text,tokenlist.end(),false); + } + + + + +ref interpreter::execute(string const &str) { + return execute(parse(str)); + } + + + + +ref interpreter::execute(istream &istr) { + return execute(parse(istr)); + } + + + + +ref interpreter::execute(ref expr) { + if (expr.get() == NULL) return ref(NULL); + return evaluateCatchExits(expr); + } + + + + +ref interpreter::evaluateCatchExits(ref expr) { + ref result; + try { + context ctx(RootScope); + result = expr->evaluate(ctx); + } + catch (return_exception &re) { + EXJS_THROWINFOLOCATION(ECJS_INVALID_NON_LOCAL_EXIT,"return",re.Location) + } + catch (break_exception &be) { + if (be.HasLabel) + EXJS_THROWINFOLOCATION(ECJS_INVALID_NON_LOCAL_EXIT,("break "+be.Label).c_str(),be.Location) + else + EXJS_THROWINFOLOCATION(ECJS_INVALID_NON_LOCAL_EXIT,"break",be.Location) + } + catch (continue_exception &ce) { + if (ce.HasLabel) + EXJS_THROWINFOLOCATION(ECJS_INVALID_NON_LOCAL_EXIT,("continue "+ce.Label).c_str(),ce.Location) + else + EXJS_THROWINFOLOCATION(ECJS_INVALID_NON_LOCAL_EXIT,"continue",ce.Location) + } + return result; + } diff --git a/simgear/interpreter/js_library.cc b/simgear/interpreter/js_library.cc new file mode 100644 index 00000000..51703d5d --- /dev/null +++ b/simgear/interpreter/js_library.cc @@ -0,0 +1,259 @@ +// ---------------------------------------------------------------------------- +// Description : Javascript interpreter library +// ---------------------------------------------------------------------------- +// (c) Copyright 2000 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + +#include +#include +#include +#include +#include +#include +#include + + + + +using namespace ixion; +using namespace javascript; + + + + +namespace { + class eval : public value { + protected: + interpreter &Interpreter; + + public: + value_type getType() const { + return VT_FUNCTION; + } + eval(interpreter &interpreter) + : Interpreter(interpreter) { + } + ref call(parameter_list const ¶meters); + }; + + class Math : public value_with_methods { + private: + typedef value_with_methods super; + + protected: + float_random RNG; + + public: + value_type getType() const { + return VT_BUILTIN; + } + + ref duplicate() const; + + ref lookup(string const &identifier); + ref callMethod(string const &identifier,parameter_list const ¶meters); + }; + } + + + + +// eval ----------------------------------------------------------------------- +ref +eval:: +call(parameter_list const ¶meters) { + if (parameters.size() != 1) { + EXJS_THROWINFO(ECJS_INVALID_NUMBER_OF_ARGUMENTS,"eval") + } + if (parameters[0]->getType() != VT_STRING) return parameters[0]; + return Interpreter.execute(parameters[0]->toString()); + } + + + + +// parseInt ------------------------------------------------------------------- +IXLIB_JS_DECLARE_FUNCTION(parseInt) { + if (parameters.size() != 1 && parameters.size() != 2) { + EXJS_THROWINFO(ECJS_INVALID_NUMBER_OF_ARGUMENTS,"parseInt") + } + unsigned radix = 10; + if (parameters.size() == 2) + radix = parameters[1]->toInt(); + return makeConstant(evalSigned(parameters[0]->toString(),radix)); + } + + + + +// parseFloat ----------------------------------------------------------------- +IXLIB_JS_DECLARE_FUNCTION(parseFloat) { + if (parameters.size() != 1) { + EXJS_THROWINFO(ECJS_INVALID_NUMBER_OF_ARGUMENTS,"parseFloat") + } + return makeConstant(evalFloat(parameters[0]->toString())); + } + + + + +// isNaN ---------------------------------------------------------------------- +#ifdef ADVANCED_MATH_AVAILABLE +IXLIB_JS_DECLARE_FUNCTION(isNaN) { + if (parameters.size() != 1) { + EXJS_THROWINFO(ECJS_INVALID_NUMBER_OF_ARGUMENTS,"isNaN") + } + int classification = fpclassify(parameters[0]->toFloat()); + return makeConstant(classification == FP_NAN); + } +#endif + + + + +// isFinite ------------------------------------------------------------------- +#ifdef ADVANCED_MATH_AVAILABLE +IXLIB_JS_DECLARE_FUNCTION(isFinite) { + if (parameters.size() != 1) { + EXJS_THROWINFO(ECJS_INVALID_NUMBER_OF_ARGUMENTS,"isFinite") + } + int classification = fpclassify(parameters[0]->toFloat()); + return makeConstant(classification != FP_NAN && classification != FP_INFINITE); + } +#endif + + + + +// Math ----------------------------------------------------------------------- +ref Math::duplicate() const { + // Math is not mutable + return const_cast(this); + } + + + + +ref Math::lookup(string const &identifier) { + #define MATH_CONSTANT(NAME,VALUE) \ + if (identifier == NAME) return makeConstant(VALUE); + + MATH_CONSTANT("E",2.7182818284590452354) + MATH_CONSTANT("LN10",2.30258509299404568402) + MATH_CONSTANT("LN2",0.69314718055994530942) + MATH_CONSTANT("LOG2E",1.4426950408889634074) + MATH_CONSTANT("LOG10E,",0.43429448190325182765) + MATH_CONSTANT("PI",3.14159265358979323846) + MATH_CONSTANT("SQRT1_2",0.70710678118654752440) + MATH_CONSTANT("SQRT2",1.41421356237309504880) + + return super::lookup(identifier); + } + + + + +ref Math::callMethod(string const &identifier,parameter_list const ¶meters) { + #define MATH_FUNCTION(NAME,C_NAME) \ + if (identifier == NAME) { \ + if (parameters.size() != 1) { \ + EXJS_THROWINFO(ECJS_INVALID_NUMBER_OF_ARGUMENTS,"Math." NAME) \ + } \ + return makeConstant(C_NAME(parameters[0]->toFloat())); \ + } + + MATH_FUNCTION("abs",NUM_ABS) + MATH_FUNCTION("acos",acos) + MATH_FUNCTION("asin",asin) + MATH_FUNCTION("atan",atan) + MATH_FUNCTION("ceil",ceil) + MATH_FUNCTION("cos",cos) + MATH_FUNCTION("exp",exp) + MATH_FUNCTION("floor",floor) + MATH_FUNCTION("log",log) + #ifdef ADVANCED_MATH_AVAILABLE + MATH_FUNCTION("round",round) + #endif + MATH_FUNCTION("sin",sin) + MATH_FUNCTION("sqrt",sqrt) + MATH_FUNCTION("tan",tan) + if (identifier == "atan2") { + if (parameters.size() != 2) { + EXJS_THROWINFO(ECJS_INVALID_NUMBER_OF_ARGUMENTS,"Math.atan2") + } + return makeConstant(atan2(parameters[0]->toFloat(),parameters[1]->toFloat())); + } + if (identifier == "pow") { + if (parameters.size() != 2) { + EXJS_THROWINFO(ECJS_INVALID_NUMBER_OF_ARGUMENTS,"Math.pow") + } + return makeConstant(pow(parameters[0]->toFloat(),parameters[1]->toFloat())); + } + if (identifier == "random") { + if (parameters.size() != 0) { + EXJS_THROWINFO(ECJS_INVALID_NUMBER_OF_ARGUMENTS,"Math.random") + } + return makeConstant(RNG()); + } + // *** FIXME this is non-compliant, but there is no equivalent standard function + if (identifier == "initRandom") { + if (parameters.size() >= 2) { + EXJS_THROWINFO(ECJS_INVALID_NUMBER_OF_ARGUMENTS,"Math.initRandom") + } + if (parameters.size() == 0) + RNG.init(); + else if (parameters.size() == 1) + RNG.init(parameters[0]->toFloat()); + return makeNull(); + } + + // *** FIXME: implement max, min + EXJS_THROWINFO(ECJS_UNKNOWN_IDENTIFIER,("Math." + identifier).c_str()) + } + + + + +// external interface functions ----------------------------------------------- +#define ADD_GLOBAL_OBJECT(NAME,TYPE) \ + { ref x = new TYPE(); \ + ip.RootScope->addMember(NAME,x); \ + } + + + + +void javascript::addGlobal(interpreter &ip) { + ref ev = new eval(ip); + ip.RootScope->addMember("eval",ev); + + ADD_GLOBAL_OBJECT("parseInt",parseInt) + ADD_GLOBAL_OBJECT("parseFloat",parseFloat) + #ifdef ADVANCED_MATH_AVAILABLE + ADD_GLOBAL_OBJECT("isNaN",isNaN) + ADD_GLOBAL_OBJECT("isFinite",isFinite) + #endif + + // *** FIXME hope this is portable + float zero = 0; + ip.RootScope->addMember("NaN",makeConstant(0.0/zero)); + ip.RootScope->addMember("Infinity",makeConstant(1.0/zero)); + ip.RootScope->addMember("undefined",makeUndefined()); + } + + + + +void javascript::addMath(interpreter &ip) { + ADD_GLOBAL_OBJECT("Math",Math) + } + + + + +void javascript::addStandardLibrary(interpreter &ip) { + addGlobal(ip); + addMath(ip); + } diff --git a/simgear/interpreter/js_value.cc b/simgear/interpreter/js_value.cc new file mode 100644 index 00000000..204eb272 --- /dev/null +++ b/simgear/interpreter/js_value.cc @@ -0,0 +1,1967 @@ +// ---------------------------------------------------------------------------- +// Description : Javascript interpreter +// ---------------------------------------------------------------------------- +// (c) Copyright 2000 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; +using namespace ixion; +using namespace javascript; + + + + +// value ---------------------------------------------------------------------- +string value::toString() const { + EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_CONVERT, + (valueType2string(getType())+string(_("-> string"))).c_str()) + } + + + + +int value::toInt() const { + EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_CONVERT, + (valueType2string(getType())+_(" -> int")).c_str()) + } + + + + +double value::toFloat() const { + EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_CONVERT, + (valueType2string(getType())+_(" -> float")).c_str()) + } + + + + +bool value::toBoolean() const { + EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_CONVERT, + (valueType2string(getType())+_(" -> bool")).c_str()) + } + + + + +string value::stringify() const { + try { + return toString(); + } + catch (...) { + return string("#<")+valueType2string(getType())+">"; + } + } + + + + +ref value::eliminateWrappers() { + return this; + } + + + + +ref value::duplicate() { + EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, + (valueType2string(getType())+_(": duplication")).c_str()) + } + + + + +ref +value::lookup(string const &identifier) { + EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, + (valueType2string(getType())+_(": lookup of ")+identifier).c_str()) + } + + + + +ref +value::subscript(value const &index) { + EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, + (valueType2string(getType())+_(": subscript")).c_str()) + } + + + + +ref +value::call(parameter_list const ¶meters) { + EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, + (valueType2string(getType())+_(": call")).c_str()) + } + + + + +ref +value::callAsMethod(ref instance,parameter_list const ¶meters) { + EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, + (valueType2string(getType())+_(": call as method")).c_str()) + } + + + + +ref +value::construct(parameter_list const ¶meters) { + EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, + (valueType2string(getType())+_(": construction")).c_str()) + } + + + + +ref value::assign(ref op2) { + EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, + (valueType2string(getType())+_(": assignment")).c_str()) + } + + + + +ref +value::operatorUnary(operator_id op) const { + EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, + (valueType2string(getType())+_(": operator ")+operator2string(op)).c_str()) + } + + + + +ref +value::operatorBinary(operator_id op,ref op2) const { + if (op == OP_EQUAL) { + if (getType() == VT_NULL) + return makeConstant(op2->getType() == VT_NULL); + if (op2->getType() == VT_NULL) + return makeConstant(getType() == VT_NULL); + } + if (op == OP_NOT_EQUAL) { + if (getType() == VT_NULL) + return makeConstant(op2->getType() != VT_NULL); + if (op2->getType() == VT_NULL) + return makeConstant(getType() != VT_NULL); + } + EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, + (valueType2string(getType())+" "+string(operator2string(op))+" "+valueType2string(op2->getType())).c_str()) + } + + + + +ref +value::operatorBinaryShortcut(operator_id op,expression const &op2,context const &ctx) const { + if (op == OP_LOGICAL_OR) + return makeConstant(toBoolean() || op2.evaluate(ctx)->toBoolean()); + if (op == OP_LOGICAL_AND) + return makeConstant(toBoolean() && op2.evaluate(ctx)->toBoolean()); + EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, + (string(operator2string(op))+_(" on ")+valueType2string(getType())).c_str()) + } + + + + +ref +value::operatorUnaryModifying(operator_id op) { + EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, + (string(operator2string(op))+_(" on ")+valueType2string(getType())).c_str()) + } + + + + +ref +value::operatorBinaryModifying(operator_id op,ref op2) { + EXJS_THROWINFO_NO_LOCATION(ECJS_INVALID_OPERATION, + (valueType2string(getType())+" "+string(operator2string(op))+" "+valueType2string(op2->getType())).c_str()) + } + + + + +value::operator_id +value::token2operator(scanner::token const &token,bool unary,bool prefix) { + switch (token.Type) { + case TT_JS_INCREMENT: return prefix ? OP_PRE_INCREMENT : OP_POST_INCREMENT; + case TT_JS_DECREMENT: return prefix ? OP_PRE_DECREMENT : OP_POST_DECREMENT; + case '+': return unary ? OP_UNARY_PLUS : OP_PLUS; + case '-': return unary ? OP_UNARY_MINUS : OP_MINUS; + case '!': return OP_LOG_NOT; + case '~': return OP_BIN_NOT; + case TT_JS_PLUS_ASSIGN: return OP_PLUS_ASSIGN; + case TT_JS_MINUS_ASSIGN: return OP_MINUS_ASSIGN; + case TT_JS_MULTIPLY_ASSIGN: return OP_MUTLIPLY_ASSIGN; + case TT_JS_DIVIDE_ASSIGN: return OP_DIVIDE_ASSIGN; + case TT_JS_MODULO_ASSIGN: return OP_MODULO_ASSIGN; + case TT_JS_BIT_AND_ASSIGN: return OP_BIT_AND_ASSIGN; + case TT_JS_BIT_OR_ASSIGN: return OP_BIT_OR_ASSIGN; + case TT_JS_BIT_XOR_ASSIGN: return OP_BIT_XOR_ASSIGN; + case TT_JS_LEFT_SHIFT_ASSIGN: return OP_LEFT_SHIFT_ASSIGN; + case TT_JS_RIGHT_SHIFT_ASSIGN: return OP_RIGHT_SHIFT_ASSIGN; + case '*': return OP_MULTIPLY; + case '/': return OP_DIVIDE; + case '%': return OP_MODULO; + case '&': return OP_BIT_AND; + case '|': return OP_BIT_OR; + case '^': return OP_BIT_XOR; + case TT_JS_LEFT_SHIFT: return OP_LEFT_SHIFT; + case TT_JS_RIGHT_SHIFT: return OP_RIGHT_SHIFT; + case TT_JS_LOGICAL_OR: return OP_LOGICAL_OR; + case TT_JS_LOGICAL_AND: return OP_LOGICAL_AND; + case TT_JS_EQUAL: return OP_EQUAL; + case TT_JS_NOT_EQUAL: return OP_NOT_EQUAL; + case TT_JS_IDENTICAL: return OP_IDENTICAL; + case TT_JS_NOT_IDENTICAL: return OP_NOT_IDENTICAL; + case TT_JS_LESS_EQUAL: return OP_LESS_EQUAL; + case TT_JS_GREATER_EQUAL: return OP_GREATER_EQUAL; + case '<': return OP_LESS; + case '>': return OP_GREATER; + default: EXJS_THROWINFO(ECJS_UNKNOWN_OPERATOR,token.Text.c_str()) + } + } + + + + +string +value::operator2string(operator_id op) { + switch (op) { + case OP_PRE_INCREMENT: return _("prefix ++"); + case OP_POST_INCREMENT: return _("postfix ++"); + case OP_PRE_DECREMENT: return _("prefix --"); + case OP_POST_DECREMENT: return _("postfix ++"); + + case OP_UNARY_PLUS: return _("unary +"); + case OP_UNARY_MINUS: return _("unary -"); + case OP_LOG_NOT: return "!"; + case OP_BIN_NOT: return "~"; + + case OP_PLUS_ASSIGN: return "+="; + case OP_MINUS_ASSIGN: return "-="; + case OP_MUTLIPLY_ASSIGN: return "*="; + case OP_DIVIDE_ASSIGN: return "/="; + case OP_MODULO_ASSIGN: return "%="; + + case OP_BIT_AND_ASSIGN: return "&="; + case OP_BIT_OR_ASSIGN: return "|="; + case OP_BIT_XOR_ASSIGN: return "^="; + case OP_LEFT_SHIFT_ASSIGN: return "<<="; + case OP_RIGHT_SHIFT_ASSIGN: return ">>="; + + case OP_PLUS: return "+"; + case OP_MINUS: return "-"; + case OP_MULTIPLY: return "*"; + case OP_DIVIDE: return "/"; + case OP_MODULO: return "%"; + + case OP_BIT_AND: return "&"; + case OP_BIT_OR: return "|"; + case OP_BIT_XOR: return "^"; + case OP_LEFT_SHIFT: return "<<"; + case OP_RIGHT_SHIFT: return ">>"; + + case OP_LOGICAL_OR: return "|"; + case OP_LOGICAL_AND: return "&"; + case OP_EQUAL: return "=="; + case OP_NOT_EQUAL: return "!="; + case OP_IDENTICAL: return "==="; + case OP_NOT_IDENTICAL: return "!=="; + case OP_LESS_EQUAL: return "<="; + case OP_GREATER_EQUAL: return ">="; + case OP_LESS: return "<"; + case OP_GREATER: return ">"; + + case OP_ASSIGN: return "="; + default: EXJS_THROW(ECJS_UNKNOWN_OPERATOR) + } + } + + + + +string value::valueType2string(value_type vt) { + switch (vt) { + case VT_UNDEFINED: return _("undefined"); + case VT_NULL: return _("null"); + case VT_INTEGER: return _("integer"); + case VT_FLOATING_POINT: return _("floating point"); + case VT_STRING: return _("string"); + case VT_FUNCTION: return _("function"); + case VT_OBJECT: return _("object"); + case VT_BUILTIN: return _("built-in object"); + case VT_HOST: return _("host object"); + case VT_SCOPE: return _("scope"); + case VT_BOUND_METHOD: return _("bound method"); + case VT_TYPE: return _("type"); + default: return _("unknown value type"); + } + } + + + + +// value_with_methods --------------------------------------------------------- +value_with_methods::bound_method:: +bound_method(string const &identifier,ref parent) + : Identifier(identifier),Parent(parent) { + } + + + + +ref +value_with_methods::bound_method::duplicate() { + // bound_methods are immutable + return this; + } + + + + +ref +value_with_methods::bound_method:: +call(parameter_list const ¶meters) { + return Parent->callMethod(Identifier,parameters); + } + + + + +ref +value_with_methods:: +lookup(string const &identifier) { + ref result = new bound_method(identifier,this); + return result; + } + + + + +// null ----------------------------------------------------------------------- +value::value_type null::getType() const { + return VT_NULL; + } + + + + +bool null::toBoolean() const { + return false; + } + + + + +ref null::duplicate() { + return makeNull(); + } + + + + +// const_floating_point ------------------------------------------------------- +const_floating_point::const_floating_point(double value) + : Value(value) { + } + + + + +value::value_type const_floating_point::getType() const { + return VT_FLOATING_POINT; + } + + + + +int const_floating_point::toInt() const { + return (int) Value; + } + + + + +double const_floating_point::toFloat() const { + return (double) Value; + } + + + + +bool const_floating_point::toBoolean() const { + return (bool) Value; + } + + + + +string const_floating_point::toString() const { + return float2dec(Value); + } + + + + +ref const_floating_point::duplicate() { + return makeValue(Value); + } + + + + +ref +const_floating_point:: +callMethod(string const &identifier,parameter_list const ¶meters) { + IXLIB_JS_IF_METHOD("toInt",0,0) + return makeConstant((signed long) Value); + IXLIB_JS_IF_METHOD("toFloat",0,0) + return makeConstant(Value); + IXLIB_JS_IF_METHOD("toString",0,1) { + unsigned radix = 10; + if (parameters.size() == 1) radix = parameters[0]->toInt(); + if (radix == 10) + return makeConstant(float2dec(Value)); + else + return makeConstant(signed2base((int) Value,0,radix)); + } + IXLIB_JS_IF_METHOD("toFixed",0,1) { + unsigned digits = 0; + if (parameters.size() == 1) digits = parameters[0]->toInt(); + + char buffer[1024]; + sprintf(buffer,("%."+unsigned2dec(digits)+"f").c_str(),Value); + return makeConstant(buffer); + } + IXLIB_JS_IF_METHOD("toExponential",0,1) { + char buffer[1024]; + if (parameters.size() == 1) + sprintf(buffer,("%."+unsigned2dec(parameters[0]->toInt())+"e").c_str(),Value); + else + sprintf(buffer,"%e",Value); + return makeConstant(buffer); + } + IXLIB_JS_IF_METHOD("toPrecision",0,1) { + if (parameters.size() == 1) + return makeConstant(float2dec(Value,parameters[0]->toInt())); + else + return makeConstant(float2dec(Value)); + } + EXJS_THROWINFO_NO_LOCATION(ECJS_UNKNOWN_IDENTIFIER,("float." + identifier).c_str()) + } + + + + +ref +const_floating_point::operatorUnary(operator_id op) const { + switch (op) { + case OP_UNARY_PLUS: return makeConstant(+Value); + case OP_UNARY_MINUS: return makeConstant(-Value); + case OP_LOG_NOT: return makeConstant(!Value); + case OP_BIN_NOT: return makeConstant(~ (long) Value); + default: + return super::operatorUnary(op); + } + } + + + + +ref +const_floating_point::operatorBinary(operator_id op,ref op2) const { + #define FLOAT_OPERATOR(OP) \ + if (op2->getType() == VT_FLOATING_POINT || op2->getType() == VT_INTEGER) \ + return makeConstant(Value OP op2->toFloat()); + #define PASS_UP \ + return super::operatorBinary(op,op2); + + switch (op) { + case OP_PLUS: + FLOAT_OPERATOR(+) + PASS_UP + case OP_MINUS: + FLOAT_OPERATOR(-) + PASS_UP + case OP_MULTIPLY: + FLOAT_OPERATOR(*) + PASS_UP + case OP_DIVIDE: + if (op2->getType() == VT_FLOATING_POINT || op2->getType() == VT_INTEGER) { + double op2value = op2->toFloat(); + if (op2value == 0) + EXJS_THROW(ECJS_DIVISION_BY_ZERO) + return makeConstant(Value / op2value); + } + PASS_UP + case OP_EQUAL: + FLOAT_OPERATOR(==) + PASS_UP + case OP_NOT_EQUAL: + FLOAT_OPERATOR(!=) + PASS_UP + case OP_LESS_EQUAL: + FLOAT_OPERATOR(<=) + PASS_UP + case OP_GREATER_EQUAL: + FLOAT_OPERATOR(>=) + PASS_UP + case OP_LESS: + FLOAT_OPERATOR(<) + PASS_UP + case OP_GREATER: + FLOAT_OPERATOR(>) + PASS_UP + default: + return super::operatorBinary(op,op2); + } + #undef FLOAT_OPERATOR + #undef PASS_UP + } + + + + +// floating_point ------------------------------------------------------------- +floating_point::floating_point(double val) + : const_floating_point(val) { + } + + + + +ref +floating_point::operatorUnaryModifying(operator_id op) { + switch (op) { + case OP_PRE_INCREMENT: + Value++; + return ref(this); + case OP_POST_INCREMENT: + // *** FIXME this should be an lvalue + return makeConstant(Value++); + case OP_PRE_DECREMENT: + Value--; + return ref(this); + case OP_POST_DECREMENT: + // *** FIXME this should be an lvalue + return makeConstant(Value--); + default: + return super::operatorUnaryModifying(op); + } + } + + + + +ref +floating_point::operatorBinaryModifying(operator_id op,ref op2) { + int val; + switch (op) { + case OP_PLUS_ASSIGN: + Value += op2->toFloat(); + return ref(this); + case OP_MINUS_ASSIGN: + Value -= op2->toFloat(); + return ref(this); + case OP_MUTLIPLY_ASSIGN: + Value *= op2->toFloat(); + return ref(this); + case OP_DIVIDE_ASSIGN: { + double op2value = op2->toFloat(); + if (op2value == 0) + EXJS_THROW(ECJS_DIVISION_BY_ZERO) + Value /= op2value; + return ref(this); + } + case OP_MODULO_ASSIGN: + val = (int) Value; + val %= (int) op2->toFloat(); + Value = val; + return ref(this); + case OP_BIT_AND_ASSIGN: + val = (int) Value; + val &= (int) op2->toFloat(); + Value = val; + return ref(this); + case OP_BIT_OR_ASSIGN: + val = (int) Value; + val |= (int) op2->toFloat(); + Value = val; + return ref(this); + case OP_BIT_XOR_ASSIGN: + val = (int) Value; + val ^= (int) op2->toFloat(); + Value = val; + return ref(this); + case OP_LEFT_SHIFT_ASSIGN: + val = (int) Value; + val <<= (int) op2->toFloat(); + Value = val; + return ref(this); + case OP_RIGHT_SHIFT_ASSIGN: + val = (int) Value; + val >>= (int) op2->toFloat(); + Value = val; + return ref(this); + default: + return super::operatorBinaryModifying(op,op2); + } + } + + + + +// const_integer -------------------------------------------------------------- +const_integer::const_integer(long value) + : Value(value) { + } + + + + +value::value_type const_integer::getType() const { + return VT_INTEGER; + } + + + + +int const_integer::toInt() const { + return (int) Value; + } + + + + +double const_integer::toFloat() const { + return (double) Value; + } + + + + +bool const_integer::toBoolean() const { + return (bool) Value; + } + + + + +string const_integer::toString() const { + return signed2dec(Value); + } + + + + +ref const_integer::duplicate() { + return makeValue(Value); + } + + + + +ref +const_integer:: +callMethod(string const &identifier,parameter_list const ¶meters) { + IXLIB_JS_IF_METHOD("toInt",0,0) + return makeConstant(Value); + IXLIB_JS_IF_METHOD("toFloat",0,0) + return makeConstant((double) Value); + IXLIB_JS_IF_METHOD("toString",0,1) { + unsigned radix = 10; + if (parameters.size() == 1) radix = parameters[0]->toInt(); + return makeConstant(signed2base(Value,0,radix)); + } + EXJS_THROWINFO_NO_LOCATION(ECJS_UNKNOWN_IDENTIFIER,("integer." + identifier).c_str()) + } + + + + +ref +const_integer::operatorUnary(operator_id op) const { + switch (op) { + case OP_UNARY_PLUS: return makeConstant(+Value); + case OP_UNARY_MINUS: return makeConstant(-Value); + case OP_LOG_NOT: return makeConstant(!Value); + case OP_BIN_NOT: return makeConstant(~ (long) Value); + default: + return super::operatorUnary(op); + } + } + + + + +ref +const_integer::operatorBinary(operator_id op,ref op2) const { + #define FLOAT_OPERATOR(OP) \ + if (op2->getType() == VT_FLOATING_POINT) \ + return makeConstant(toFloat() OP op2->toFloat()); + #define INT_OPERATOR(OP) \ + if (op2->getType() == VT_INTEGER) \ + return makeConstant(Value OP op2->toInt()); + #define PASS_UP \ + return super::operatorBinary(op,op2); + + switch (op) { + case OP_PLUS: + FLOAT_OPERATOR(+) + INT_OPERATOR(+) + PASS_UP + case OP_MINUS: + FLOAT_OPERATOR(-) + INT_OPERATOR(-) + PASS_UP + case OP_MULTIPLY: + FLOAT_OPERATOR(*) + INT_OPERATOR(*) + PASS_UP + case OP_DIVIDE: + if (op2->toFloat() == 0) + EXJS_THROW(ECJS_DIVISION_BY_ZERO) + FLOAT_OPERATOR(/) + INT_OPERATOR(/) + PASS_UP + case OP_MODULO: + INT_OPERATOR(%) + PASS_UP + case OP_BIT_AND: + INT_OPERATOR(&) + PASS_UP + case OP_BIT_OR: + INT_OPERATOR(|) + PASS_UP + case OP_BIT_XOR: + INT_OPERATOR(^) + PASS_UP + case OP_LEFT_SHIFT: + INT_OPERATOR(<<) + PASS_UP + case OP_RIGHT_SHIFT: + INT_OPERATOR(>>) + PASS_UP + case OP_EQUAL: + FLOAT_OPERATOR(==) + INT_OPERATOR(==) + PASS_UP + case OP_NOT_EQUAL: + FLOAT_OPERATOR(!=) + INT_OPERATOR(!=) + PASS_UP + case OP_LESS_EQUAL: + FLOAT_OPERATOR(<=) + INT_OPERATOR(<=) + PASS_UP + case OP_GREATER_EQUAL: + FLOAT_OPERATOR(>=) + INT_OPERATOR(>=) + PASS_UP + case OP_LESS: + FLOAT_OPERATOR(<) + INT_OPERATOR(<) + PASS_UP + case OP_GREATER: + FLOAT_OPERATOR(>) + INT_OPERATOR(>) + PASS_UP + default: + PASS_UP + } + #undef FLOAT_OPERATOR + #undef INT_OPERATOR + #undef PASS_UP + } + + + + +// integer -------------------------------------------------------------------- +integer::integer(long val) + : const_integer(val) { + } + + + + +ref +integer::operatorUnaryModifying(operator_id op) { + switch (op) { + case OP_PRE_INCREMENT: + Value++; + return ref(this); + case OP_POST_INCREMENT: + // *** FIXME this should be an lvalue + return makeConstant(Value++); + case OP_PRE_DECREMENT: + Value--; + return ref(this); + case OP_POST_DECREMENT: + // *** FIXME this should be an lvalue + return makeConstant(Value--); + default: + return super::operatorUnaryModifying(op); + } + } + + + + +ref +integer::operatorBinaryModifying(operator_id op,ref op2) { + int val; + int op2value = op2->toInt(); + switch (op) { + case OP_PLUS_ASSIGN: + Value += op2value; + return ref(this); + case OP_MINUS_ASSIGN: + Value -= op2value; + return ref(this); + case OP_MUTLIPLY_ASSIGN: + Value *= op2value; + return ref(this); + case OP_DIVIDE_ASSIGN: + if (op2value == 0) + EXJS_THROW(ECJS_DIVISION_BY_ZERO) + Value /= op2value; + return ref(this); + case OP_MODULO_ASSIGN: + val = Value; + val %= op2value; + Value = val; + return ref(this); + case OP_BIT_AND_ASSIGN: + val = Value; + val &= op2value; + Value = val; + return ref(this); + case OP_BIT_OR_ASSIGN: + val = Value; + val |= op2value; + Value = val; + return ref(this); + case OP_BIT_XOR_ASSIGN: + val = Value; + val ^= op2value; + Value = val; + return ref(this); + case OP_LEFT_SHIFT_ASSIGN: + val = Value; + val <<= op2value; + Value = val; + return ref(this); + case OP_RIGHT_SHIFT_ASSIGN: + val = Value; + val >>= op2value; + Value = val; + return ref(this); + default: + return super::operatorBinaryModifying(op,op2); + } + } + + + + +// js_string ------------------------------------------------------------------ +js_string::js_string(string const &value) + : Value(value) { + } + + + + +value::value_type js_string::getType() const { + return VT_STRING; + } + + + + +string js_string::toString() const { + return Value; + } + + + + +bool js_string::toBoolean() const { + return Value.size() == 0; + } + + + + +string js_string::stringify() const { + return '"'+Value+'"'; + } + + + + +ref js_string::duplicate() { + return makeValue(Value); + } + + + + +ref js_string::lookup(string const &identifier) { + if (identifier == "length") return makeConstant(Value.size()); + return super::lookup(identifier); + } + + + + +ref js_string::callMethod(string const &identifier,parameter_list const ¶meters) { + IXLIB_JS_IF_METHOD("toString",0,0) + return makeConstant(Value); + IXLIB_JS_IF_METHOD("charAt",1,1) + return makeConstant(string(1,Value.at(parameters[0]->toInt()))); + IXLIB_JS_IF_METHOD("charCodeAt",1,1) + return makeConstant(Value.at(parameters[0]->toInt())); + if (identifier == "concat") { + string result = Value; + FOREACH_CONST(first,parameters,parameter_list) + Value += (*first)->toString(); + return makeConstant(Value); + } + IXLIB_JS_IF_METHOD("indexOf",1,2) { + string::size_type startpos = 0; + if (parameters.size() == 2) startpos = parameters[1]->toInt(); + string::size_type result = Value.find(parameters[0]->toString(),startpos); + if (result == string::npos) return makeConstant(-1); + else return makeConstant(result); + } + IXLIB_JS_IF_METHOD("lastIndexOf",1,2) { + string::size_type startpos = string::npos; + if (parameters.size() == 2) startpos = parameters[1]->toInt(); + string::size_type result = Value.rfind(parameters[0]->toString(),startpos); + if (result == string::npos) return makeConstant(-1); + else return makeConstant(result); + } + // *** FIXME we need proper regexps + IXLIB_JS_IF_METHOD("match",1,1) { + regex_string re(parameters[0]->toString()); + return makeConstant(re.matchAt(Value,0)); + } + IXLIB_JS_IF_METHOD("replace",2,2) { + regex_string re(parameters[0]->toString()); + return makeConstant(re.replaceAll(Value,parameters[1]->toString())); + } + IXLIB_JS_IF_METHOD("search",1,1) { + regex_string re(parameters[0]->toString()); + return makeConstant(re.match(Value)); + } + IXLIB_JS_IF_METHOD("slice",2,2) { + TIndex start = parameters[0]->toInt(),end = parameters[1]->toInt(); + return makeConstant(string(Value,start,end-start)); + } + + /* + Allow 2 parameters here, the first one is the separator and the second + one is the maximum allowed number of elements of the resulting array + if the function is called without a parameter the string is split + into a character array. + The second parameter sets the maximum number of elements of the + resulting array. + */ + + IXLIB_JS_IF_METHOD("split", 0, 2 ) { + string::size_type start = 0, last = 0; + int count = 0; + int max = -1; + + if (parameters.size() == 2) + max = parameters[1]->toInt(); + + string sep; + + auto_ptr result(new js_array(0)); + + if (parameters.size() == 0 || (sep = parameters[0]->toString()).empty()) { + for (string::size_type i = 0; i < Value.size(); ++i) { + string s; + s += Value[i]; + result->push_back(makeValue(s)); + } + } + else { + while (true) { + if (max > 0) + count++; + + if (count >= max && max > 0) { + result->push_back(makeValue(Value.substr(last))); + break; + } + + start = Value.find(sep, last); + + if (start == string::npos) { + result->push_back(makeValue(Value.substr(last))); + break; + } + + result->push_back(makeValue(Value.substr(last, start - last))); + last = start + sep.size(); + } + } + + return result.release(); + } + + IXLIB_JS_IF_METHOD("substring",2,2) { + TIndex start = parameters[0]->toInt(),end = parameters[1]->toInt(); + if (start > end) swap(start,end); + return makeConstant(string(Value,start,end-start)); + } + IXLIB_JS_IF_METHOD("toLowerCase",0,0) { + return makeConstant(lower(Value)); + } + IXLIB_JS_IF_METHOD("toUpperCase",0,0) { + return makeConstant(upper(Value)); + } + EXJS_THROWINFO_NO_LOCATION(ECJS_UNKNOWN_IDENTIFIER,("String." + identifier).c_str()) + } + + + + +ref js_string::operatorBinary(operator_id op,ref op2) const { + switch (op) { + case OP_PLUS: return makeConstant(Value+op2->toString()); + case OP_EQUAL: + if (op2->getType() == VT_STRING) return makeConstant(Value == op2->toString()); + else return super::operatorBinary(op,op2); + case OP_NOT_EQUAL: + if (op2->getType() == VT_STRING) return makeConstant(Value != op2->toString()); + else return super::operatorBinary(op,op2); + case OP_LESS_EQUAL: + if (op2->getType() == VT_STRING) return makeConstant(Value <= op2->toString()); + else return super::operatorBinary(op,op2); + case OP_GREATER_EQUAL: + if (op2->getType() == VT_STRING) return makeConstant(Value >= op2->toString()); + else return super::operatorBinary(op,op2); + case OP_LESS: + if (op2->getType() == VT_STRING) return makeConstant(Value < op2->toString()); + else return super::operatorBinary(op,op2); + case OP_GREATER: + if (op2->getType() == VT_STRING) return makeConstant(Value > op2->toString()); + else return super::operatorBinary(op,op2); + default: + return super::operatorBinary(op,op2); + } + } + + + + +ref js_string::operatorBinaryModifying(operator_id op,ref op2) { + switch (op) { + case OP_PLUS_ASSIGN: + Value += op2->toString(); + return ref(this); + default: + return super::operatorBinaryModifying(op,op2); + } + } + + + + +// lvalue --------------------------------------------------------------------- +lvalue::lvalue(ref ref) + : Reference(ref) { + } + + + + +value::value_type lvalue::getType() const { + return Reference->getType(); + } + + + + +string lvalue::toString() const { + return Reference->toString(); + } + + + + +int lvalue::toInt() const { + return Reference->toInt(); + } + + + + +double lvalue::toFloat() const { + return Reference->toFloat(); + } + + + + +bool lvalue::toBoolean() const { + return Reference->toBoolean(); + } + + + + +string lvalue::stringify() const { + return Reference->stringify(); + } + + + + +ref lvalue::eliminateWrappers() { + return Reference.get(); + } + + + + +ref lvalue::duplicate() { + return makeLValue(Reference->duplicate()); + } + + + + +ref lvalue::lookup(string const &identifier) { + return Reference->lookup(identifier); + } + + + + +ref lvalue::subscript(value const &index) { + return Reference->subscript(index); + } + + + + +ref lvalue::call(parameter_list const ¶meters) { + return Reference->call(parameters); + } + + + + +ref +lvalue::callAsMethod(ref instance,parameter_list const ¶meters) { + return Reference->callAsMethod(instance,parameters); + } + + + + +ref +lvalue::construct(parameter_list const ¶meters) { + return Reference->construct(parameters); + } + + + + +ref lvalue::assign(ref op2) { + Reference = op2; + return this; + } + + + + +ref lvalue::operatorUnary(operator_id op) const { + return Reference->operatorUnary(op); + } + + + + +ref lvalue::operatorBinary(operator_id op,ref op2) const { + return Reference->operatorBinary(op,op2); + } + + + + +ref lvalue::operatorBinaryShortcut(operator_id op,expression const &op2,context const &ctx) const { + return Reference->operatorBinaryShortcut(op,op2,ctx); + } + + + + +#define LVALUE_RETURN(VALUE) \ + ref __result = VALUE; \ + if (__result.get() == Reference.get()) \ + return this; \ + else \ + return __result; + + + + +ref lvalue::operatorUnaryModifying(operator_id op) { + LVALUE_RETURN(Reference->operatorUnaryModifying(op)) + } + + + + +ref lvalue::operatorBinaryModifying(operator_id op,ref op2) { + LVALUE_RETURN(Reference->operatorBinaryModifying(op,op2)) + } + + + + +// constant_wrapper ----------------------------------------------------------- +constant_wrapper::constant_wrapper(ref val) + : Constant(val) { + } + + + + +value::value_type constant_wrapper::getType() const { + return Constant->getType(); + } + + + + +string constant_wrapper::toString() const { + return Constant->toString(); + } + + + + +int constant_wrapper::toInt() const { + return Constant->toInt(); + } + + + + +double constant_wrapper::toFloat() const { + return Constant->toFloat(); + } + + + + +bool constant_wrapper::toBoolean() const { + return Constant->toBoolean(); + } + + + + +string constant_wrapper::stringify() const { + return Constant->stringify(); + } + + + + +ref +constant_wrapper::eliminateWrappers() { + return Constant.get(); + } + + + + +ref +constant_wrapper::duplicate() { + return wrapConstant(Constant->duplicate()); + } + + + + +ref +constant_wrapper::lookup(string const &identifier) { + return Constant->lookup(identifier); + } + + + + +ref +constant_wrapper::subscript(value const &index) { + return Constant->subscript(index); + } + + + + +ref +constant_wrapper::call(parameter_list const ¶meters) const { + return Constant->call(parameters); + } + + + + +ref +constant_wrapper::callAsMethod(ref instance,parameter_list const ¶meters) { + return Constant->callAsMethod(instance,parameters); + } + + + + +ref +constant_wrapper::construct(parameter_list const ¶meters) { + return Constant->construct(parameters); + } + + + + +ref +constant_wrapper::assign(ref value) { + EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_MODIFY_RVALUE,_("by assignment")) + } + + + + +ref +constant_wrapper::operatorUnary(operator_id op) const { + return Constant->operatorUnary(op); + } + + + + +ref +constant_wrapper::operatorBinary(operator_id op,ref op2) const { + return Constant->operatorBinary(op,op2); + } + + + + +ref +constant_wrapper::operatorBinaryShortcut(operator_id op,expression const &op2,context const &ctx) const { + return Constant->operatorBinaryShortcut(op,op2,ctx); + } + + + +ref +constant_wrapper::operatorUnaryModifying(operator_id op) { + EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_MODIFY_RVALUE,operator2string(op).c_str()) + } + + + +ref +constant_wrapper::operatorBinaryModifying(operator_id op,ref op2) { + EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_MODIFY_RVALUE,operator2string(op).c_str()) + } + + + + +// list_scope ----------------------------------------------------------------- +ref +list_scope::lookup(string const &identifier) { + member_map::iterator item = MemberMap.find(identifier); + if (item != MemberMap.end()) + return item->second; + + FOREACH_CONST(first,SwallowedList,swallowed_list) { + try { + return (*first)->lookup(identifier); + } + catch (...) { } + } + EXJS_THROWINFO_NO_LOCATION(ECJS_UNKNOWN_IDENTIFIER,identifier.c_str()) + } + + + + +void list_scope::unite(ref scope) { + SwallowedList.push_back(scope); + } + + + + +void list_scope::separate(ref scope) { + FOREACH(first,SwallowedList,swallowed_list) { + if (*first == scope) { + SwallowedList.erase(first); + return; + } + } + EXGEN_THROW(EC_ITEMNOTFOUND) + } + + + + +void list_scope::clearScopes() { + SwallowedList.clear(); + } + + + + +bool list_scope::hasMember(string const &name) const { + member_map::const_iterator item = MemberMap.find(name); + return item != MemberMap.end(); + } + + + + +void list_scope::addMember(string const &name,ref member) { + if (hasMember(name)) + EXJS_THROWINFO_NO_LOCATION(ECJS_CANNOT_REDECLARE,name.c_str()) + + MemberMap[name] = member; + } + + + + +void list_scope::removeMember(string const &name) { + MemberMap.erase(name); + } + + + + +void list_scope::clearMembers() { + MemberMap.clear(); + } + + + + +void list_scope::clear() { + clearScopes(); + clearMembers(); + } + + + + +// callable_with_parameters --------------------------------------------------- +callable_with_parameters::callable_with_parameters(parameter_name_list const &pnames) + : ParameterNameList(pnames) { + } + + + + +void callable_with_parameters::addParametersTo(list_scope &scope,parameter_list const ¶meters) const { + parameter_list::const_iterator + firstp = parameters.begin(), + lastp = parameters.end(); + + FOREACH_CONST(first,ParameterNameList,parameter_name_list) { + if (firstp == lastp) + scope.addMember(*first,makeLValue(makeNull())); + else + scope.addMember(*first,makeLValue((*firstp)->eliminateWrappers()->duplicate())); + + firstp++; + } + } + + + + +ref callable_with_parameters::evaluateBody(expression &body,context const &ctx) { + ref result; + + try { + result = body.evaluate(ctx); + } + catch (return_exception &fr) { + result = fr.ReturnValue; + } + catch (break_exception &be) { + if (be.HasLabel) + EXJS_THROWINFOLOCATION(ECJS_INVALID_NON_LOCAL_EXIT,("break "+be.Label).c_str(),be.Location) + else + EXJS_THROWINFOLOCATION(ECJS_INVALID_NON_LOCAL_EXIT,"break",be.Location) + } + catch (continue_exception &ce) { + if (ce.HasLabel) + EXJS_THROWINFOLOCATION(ECJS_INVALID_NON_LOCAL_EXIT,("continue "+ce.Label).c_str(),ce.Location) + else + EXJS_THROWINFOLOCATION(ECJS_INVALID_NON_LOCAL_EXIT,"continue",ce.Location) + } + if (result.get()) return result->eliminateWrappers()->duplicate(); + return ref(NULL); + } + + + + +// function ------------------------------------------------------------------- +function::function(parameter_name_list const &pnames,ref body,ref lex_scope) + : super(pnames),Body(body),LexicalScope(lex_scope) { + } + + + + +ref function::duplicate() { + // functions are not mutable + return this; + } + + + + +ref +function:: +call(parameter_list const ¶meters) { + ref scope = new list_scope; + scope->unite(LexicalScope); + + addParametersTo(*scope,parameters); + return evaluateBody(*Body,context(scope)); + + // ATTENTION: this is a scope cancellation point. + } + + + + +// method --------------------------------------------------------------------- +method::method(parameter_name_list const &pnames,ref body,ref lex_scope) + : super(pnames),Body(body),LexicalScope(lex_scope) { + } + + + + +ref method::duplicate() { + // methods are not mutable + return this; + } + + + + +ref +method:: +callAsMethod(ref instance,parameter_list const ¶meters) { + ref scope = new list_scope; + scope->unite(instance); + scope->unite(LexicalScope); + scope->addMember("this",instance); + + addParametersTo(*scope,parameters); + return evaluateBody(*Body,context(scope)); + + // ATTENTION: this is a scope cancellation point. + } + + + + +// constructor ---------------------------------------------------------------- +constructor::constructor(parameter_name_list const &pnames,ref body,ref lex_scope) + : super(pnames),Body(body),LexicalScope(lex_scope) { + } + + + + +ref constructor::duplicate() { + // constructors are not mutable + return this; + } + + + + +ref +constructor:: +callAsMethod(ref instance,parameter_list const ¶meters) { + ref scope = new list_scope; + scope->unite(LexicalScope); + scope->unite(instance); + scope->addMember("this",instance); + + addParametersTo(*scope,parameters); + return evaluateBody(*Body,context(scope)); + + // ATTENTION: this is a scope cancellation point. + } + + + + +// js_class ------------------------------------------------------------------- +js_class::super_instance_during_construction::super_instance_during_construction( +ref super_class) + : SuperClass(super_class) { + } + + + + +ref js_class::super_instance_during_construction::call(parameter_list const ¶meters) { + if (SuperClassInstance.get()) + EXJS_THROW_NO_LOCATION(ECJS_DOUBLE_CONSTRUCTION) + + SuperClassInstance = SuperClass->construct(parameters); + return SuperClassInstance; + } + + + + +ref js_class::super_instance_during_construction::lookup(string const &identifier) { + return getSuperClassInstance()->lookup(identifier); + } + + + + +ref js_class::super_instance_during_construction::getSuperClassInstance() { + if (SuperClassInstance.get()) + return SuperClassInstance; + + SuperClassInstance = SuperClass->construct(parameter_list()); + return SuperClassInstance; + } + + + + +js_class::js_class(ref lex_scope,ref super_class,ref constructor, +ref static_method_scope,ref method_scope, +ref static_variable_scope,declaration_list const &variable_list) + : LexicalScope(lex_scope),SuperClass(super_class),Constructor(constructor), + StaticMethodScope(static_method_scope), + MethodScope(method_scope),StaticVariableScope(static_variable_scope), + VariableList(variable_list) { + } + + + + +ref js_class::duplicate() { + // classes are not mutable + return this; + } + + + + +ref js_class::lookup(string const &identifier) { + try { + return lookupLocal(identifier); + } + catch (...) { } + + if (SuperClass.get()) + return SuperClass->lookup(identifier); + + EXJS_THROWINFO_NO_LOCATION(ECJS_UNKNOWN_IDENTIFIER,identifier.c_str()) + } + + + + +ref js_class::lookupLocal(string const &identifier) { + try { + return StaticMethodScope->lookup(identifier); + } + catch (...) { } + + return StaticVariableScope->lookup(identifier); + } + + + + +ref js_class::construct(parameter_list const ¶meters) { + ref vl(new list_scope); + + ref instance( + new js_class_instance(this,MethodScope,vl)); + + ref construction_scope(new list_scope); + construction_scope->unite(LexicalScope); + construction_scope->unite(instance); + + FOREACH_CONST(first,VariableList,declaration_list) + (*first)->evaluate(context(vl,construction_scope)); + + ref temp_super; + if (SuperClass.get()) { + temp_super = new super_instance_during_construction(SuperClass); + vl->addMember("super",temp_super); + } + + if (Constructor.get()) + Constructor->callAsMethod(instance,parameters); + + if (temp_super.get()) { + ref super = temp_super->getSuperClassInstance(); + vl->removeMember("super"); + instance->setSuperClassInstance(super); + vl->addMember("super",super); + } + return instance; + } + + + + +// js_class_instance ---------------------------------------------------------- +js_class_instance::bound_method::bound_method(ref instance,ref method) + : Instance(instance),Method(method) { + } + + + + +ref js_class_instance::bound_method::call(parameter_list const ¶meters) { + return Method->callAsMethod(Instance,parameters); + } + + + + +js_class_instance::js_class_instance(ref cls, +ref method_scope,ref variable_scope) + : Class(cls),MethodScope(method_scope),VariableScope(variable_scope) { + } + + + + +void js_class_instance::setSuperClassInstance(ref super_class_instance) { + SuperClassInstance = super_class_instance; + } + + + + +ref js_class_instance::duplicate() { + return this; + } + + + + +ref js_class_instance::lookup(string const &identifier) { + try { + ref method = MethodScope->lookup(identifier); + ref bound = new bound_method(this,method); + return bound; + } + catch (...) { } + + try { + return VariableScope->lookup(identifier); + } + catch (...) { } + + try { + return Class->lookupLocal(identifier); + } + catch (...) { } + + if (SuperClassInstance.get()) + return SuperClassInstance->lookup(identifier); + + EXJS_THROWINFO_NO_LOCATION(ECJS_UNKNOWN_IDENTIFIER,identifier.c_str()) + } + + + + +// value creation ------------------------------------------------------------- +ref +javascript::makeUndefined() { + // *** FIXME: this is non-compliant + ref result(new null()); + return result; + } + + + + +ref +javascript::makeNull() { + ref result(new null()); + return result; + } + + + + +ref +javascript::makeValue(signed int val) { + ref result(new integer(val)); + return result; + } + + + + +ref +javascript::makeConstant(signed int val) { + ref result(new const_integer(val)); + return result; + } + + + + +ref +javascript::makeValue(unsigned int val) { + ref result(new integer(val)); + return result; + } + + + + +ref +javascript::makeConstant(unsigned int val) { + ref result(new const_integer(val)); + return result; + } + + + + +ref +javascript::makeValue(signed long val) { + ref result(new integer(val)); + return result; + } + + + + +ref +javascript::makeConstant(signed long val) { + ref result(new const_integer(val)); + return result; + } + + + + +ref +javascript::makeValue(unsigned long val) { + ref result(new integer(val)); + return result; + } + + + + +ref +javascript::makeConstant(unsigned long val) { + ref result(new const_integer(val)); + return result; + } + + + + +ref +javascript::makeValue(double val) { + ref result(new floating_point(val)); + return result; + } + + + + +ref +javascript::makeConstant(double val) { + ref result(new const_floating_point(val)); + return result; + } + + + + +ref +javascript::makeValue(string const &val) { + ref result(new js_string(val)); + return result; + } + + + + +ref +javascript::makeConstant(string const &val) { + return wrapConstant(makeValue(val)); + } + + + + +ref javascript::makeArray(TSize size) { + auto_ptr result(new js_array(size)); + return result.release(); + } + + + + +ref +javascript::makeLValue(ref target) { + ref result = new lvalue(target); + return result; + } + + + + +ref +javascript::wrapConstant(ref val) { + ref result(new constant_wrapper(val)); + return result; + } diff --git a/simgear/interpreter/lex.javascript.cc b/simgear/interpreter/lex.javascript.cc new file mode 100644 index 00000000..7156dc63 --- /dev/null +++ b/simgear/interpreter/lex.javascript.cc @@ -0,0 +1,2031 @@ +#define yyFlexLexer jsFlexLexer + +#line 4 "lex.javascript.cc" +/* A lexical scanner generated by flex */ + +/* Scanner skeleton version: + * $Header$ + */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 + + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include +#include +using std::istream; +using std::ostream; +#include + +/* Use prototypes in function declarations. */ +#define YY_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include +#include +#define YY_USE_CONST +#define YY_USE_PROTOS +#endif + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +extern int yyleng; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * yyless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the yyless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + *yy_cp = yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int yy_size_t; + + +struct yy_buffer_state + { + istream* yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + + +static void *yy_flex_alloc YY_PROTO(( yy_size_t )); +static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t )); +static void yy_flex_free YY_PROTO(( void * )); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (yy_current_buffer->yy_at_bol) + + +#define YY_USES_REJECT + +#define yywrap() 1 +#define YY_SKIP_YYWRAP +typedef unsigned char YY_CHAR; +#define yytext_ptr yytext + +#include + + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 86 +#define YY_END_OF_BUFFER 87 +static yyconst short int yy_acclist[314] = + { 0, + 87, 85, 86, 84, 85, 86, 84, 86, 26, 85, + 86, 85, 86, 21, 85, 86, 23, 85, 86, 85, + 86, 13, 85, 86, 14, 85, 86, 19, 85, 86, + 17, 85, 86, 30, 85, 86, 18, 85, 86, 31, + 85, 86, 20, 85, 86, 80, 85, 86, 80, 85, + 86, 16, 85, 86, 10, 85, 86, 28, 85, 86, + 27, 85, 86, 29, 85, 86, 15, 85, 86, 83, + 85, 86, 11, 85, 86, 12, 85, 86, 22, 85, + 86, 83, 85, 86, 83, 85, 86, 83, 85, 86, + 83, 85, 86, 83, 85, 86, 83, 85, 86, 83, + + 85, 86, 83, 85, 86, 83, 85, 86, 83, 85, + 86, 83, 85, 86, 83, 85, 86, 83, 85, 86, + 8, 85, 86, 24, 85, 86, 9, 85, 86, 25, + 85, 86, 3, 86, 4, 86, 3, 86, 7, 86, + 6, 86, 6, 7, 86, 84, 47, 82, 36, 50, + 38, 82, 34, 52, 32, 53, 33, 81, 1, 5, + 35, 81, 80, 80, 40, 48, 46, 49, 41, 83, + 37, 83, 83, 83, 83, 83, 61, 83, 83, 83, + 83, 83, 83, 59, 83, 73, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 39, 51, + + 2, 6, 45, 81, 80, 42, 44, 43, 83, 83, + 83, 83, 83, 83, 83, 83, 63, 83, 83, 83, + 54, 83, 83, 83, 83, 83, 83, 83, 83, 57, + 83, 83, 81, 83, 66, 83, 83, 83, 83, 83, + 62, 83, 83, 83, 83, 83, 58, 83, 83, 83, + 83, 55, 83, 70, 83, 83, 83, 68, 83, 75, + 83, 74, 83, 83, 83, 83, 71, 83, 83, 83, + 83, 83, 83, 83, 60, 83, 83, 83, 83, 83, + 83, 83, 64, 83, 78, 83, 65, 83, 83, 83, + 83, 69, 83, 76, 83, 83, 83, 83, 83, 67, + + 83, 56, 83, 83, 83, 83, 77, 83, 72, 83, + 83, 79, 83 + } ; + +static yyconst short int yy_accept[225] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 2, 4, 7, + 9, 12, 14, 17, 20, 22, 25, 28, 31, 34, + 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, + 67, 70, 73, 76, 79, 82, 85, 88, 91, 94, + 97, 100, 103, 106, 109, 112, 115, 118, 121, 124, + 127, 130, 133, 135, 137, 139, 141, 143, 146, 147, + 148, 148, 149, 149, 150, 151, 152, 152, 153, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 164, 164, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 179, 180, 181, + + 182, 183, 184, 186, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 204, 204, 204, 204, 204, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 219, 220, 221, 223, 224, 225, 226, 227, 228, 229, + 230, 232, 233, 233, 233, 233, 233, 233, 234, 235, + 237, 238, 239, 240, 241, 243, 244, 245, 246, 247, + 249, 250, 251, 252, 254, 256, 257, 258, 258, 258, + 258, 258, 260, 262, 264, 265, 266, 267, 269, 270, + 271, 272, 273, 274, 275, 277, 278, 279, 280, 281, + + 282, 283, 285, 287, 289, 290, 291, 292, 294, 296, + 297, 298, 299, 300, 302, 304, 305, 306, 307, 309, + 311, 312, 314, 314 + } ; + +static yyconst int yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 5, 6, 1, 1, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, + 19, 19, 19, 19, 19, 20, 20, 21, 22, 23, + 24, 25, 26, 1, 27, 27, 27, 27, 28, 27, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 30, 29, 29, + 31, 32, 33, 34, 29, 1, 35, 36, 37, 38, + + 39, 40, 29, 41, 42, 29, 43, 44, 45, 46, + 47, 48, 29, 49, 50, 51, 52, 53, 54, 55, + 29, 29, 56, 57, 58, 59, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst int yy_meta[60] = + { 0, + 1, 1, 2, 1, 1, 3, 1, 1, 4, 1, + 1, 1, 1, 1, 1, 1, 1, 5, 5, 6, + 1, 1, 1, 1, 1, 4, 6, 6, 7, 7, + 1, 4, 1, 1, 5, 5, 6, 6, 6, 5, + 7, 7, 7, 7, 7, 8, 7, 7, 8, 7, + 8, 7, 8, 7, 8, 1, 1, 1, 1 + } ; + +static yyconst short int yy_base[234] = + { 0, + 0, 0, 57, 58, 59, 61, 393, 394, 64, 69, + 368, 68, 367, 67, 67, 394, 394, 366, 64, 394, + 63, 61, 72, 74, 87, 394, 394, 59, 365, 61, + 394, 0, 394, 394, 364, 338, 73, 62, 66, 76, + 72, 92, 347, 44, 73, 339, 349, 342, 394, 73, + 394, 394, 394, 394, 365, 394, 121, 129, 132, 357, + 110, 394, 119, 394, 394, 394, 130, 131, 127, 394, + 394, 394, 394, 394, 129, 394, 394, 394, 132, 157, + 170, 146, 0, 175, 356, 394, 355, 394, 354, 0, + 394, 338, 326, 340, 328, 333, 0, 322, 320, 326, + + 320, 322, 0, 0, 322, 312, 321, 313, 328, 320, + 319, 308, 321, 309, 315, 394, 394, 394, 150, 394, + 137, 0, 183, 0, 192, 160, 198, 0, 394, 394, + 394, 321, 316, 304, 133, 318, 313, 312, 300, 0, + 312, 309, 0, 303, 294, 294, 293, 293, 303, 302, + 0, 296, 181, 213, 211, 245, 203, 206, 289, 0, + 281, 275, 283, 260, 0, 265, 264, 228, 228, 0, + 227, 233, 237, 0, 0, 231, 231, 135, 202, 149, + 195, 0, 0, 220, 222, 223, 228, 0, 220, 213, + 214, 222, 217, 215, 0, 204, 203, 196, 196, 197, + + 207, 0, 0, 0, 193, 201, 198, 0, 0, 190, + 198, 189, 155, 0, 0, 158, 134, 123, 0, 0, + 70, 0, 394, 285, 293, 301, 309, 313, 319, 325, + 329, 331, 333 + } ; + +static yyconst short int yy_def[234] = + { 0, + 223, 1, 224, 224, 225, 225, 223, 223, 223, 223, + 223, 226, 223, 223, 227, 223, 223, 223, 223, 223, + 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 228, 223, 223, 223, 228, 228, 228, 228, 228, + 228, 228, 228, 228, 228, 228, 228, 228, 223, 223, + 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, + 226, 223, 229, 223, 223, 223, 227, 227, 230, 223, + 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 223, 231, 223, 223, 223, 223, 223, 223, 228, + 223, 228, 228, 228, 228, 228, 228, 228, 228, 228, + + 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 228, 228, 228, 228, 223, 223, 223, 223, 223, + 226, 232, 227, 233, 223, 223, 223, 231, 223, 223, + 223, 228, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 228, 226, 226, 227, 227, 223, 223, 228, 228, + 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 228, 228, 228, 228, 228, 228, 226, 226, 227, + 227, 228, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, + + 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 228, 228, 228, 228, 228, 228, 228, 228, 228, + 228, 228, 0, 223, 223, 223, 223, 223, 223, 223, + 223, 223, 223 + } ; + +static yyconst short int yy_nxt[454] = + { 0, + 8, 9, 10, 9, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, + 26, 27, 28, 29, 30, 31, 32, 32, 32, 32, + 33, 8, 34, 35, 32, 36, 37, 38, 39, 40, + 32, 41, 32, 32, 32, 42, 32, 32, 43, 44, + 45, 46, 47, 48, 32, 49, 50, 51, 52, 54, + 54, 57, 58, 57, 58, 59, 59, 59, 55, 55, + 59, 59, 59, 62, 65, 68, 71, 73, 75, 75, + 75, 85, 86, 76, 88, 89, 74, 72, 77, 79, + 66, 80, 80, 81, 109, 78, 116, 110, 69, 63, + + 96, 82, 79, 83, 84, 84, 84, 93, 97, 98, + 100, 103, 82, 111, 82, 62, 94, 104, 222, 95, + 99, 112, 101, 119, 119, 82, 105, 102, 83, 117, + 106, 119, 119, 59, 59, 59, 121, 121, 68, 68, + 62, 63, 62, 107, 123, 123, 75, 75, 75, 75, + 75, 75, 119, 119, 153, 153, 125, 68, 126, 125, + 126, 69, 69, 127, 127, 127, 63, 125, 63, 221, + 125, 220, 79, 122, 80, 80, 81, 127, 127, 127, + 69, 124, 162, 163, 82, 79, 62, 81, 81, 81, + 79, 68, 84, 84, 84, 82, 219, 82, 178, 178, + + 155, 155, 82, 68, 157, 218, 157, 62, 82, 158, + 158, 158, 63, 82, 69, 127, 127, 127, 62, 68, + 158, 158, 158, 158, 158, 158, 69, 217, 180, 180, + 179, 179, 179, 63, 216, 215, 214, 213, 212, 179, + 179, 211, 69, 210, 63, 209, 208, 179, 179, 179, + 179, 179, 179, 68, 207, 206, 205, 204, 203, 202, + 201, 200, 181, 181, 181, 199, 198, 197, 196, 195, + 194, 181, 181, 193, 192, 191, 69, 190, 189, 181, + 181, 181, 181, 181, 181, 53, 53, 53, 53, 53, + 53, 53, 53, 56, 56, 56, 56, 56, 56, 56, + + 56, 61, 188, 61, 61, 61, 61, 61, 61, 67, + 187, 186, 67, 67, 67, 67, 67, 90, 90, 90, + 90, 61, 61, 61, 185, 184, 61, 67, 67, 67, + 183, 182, 67, 128, 128, 154, 154, 156, 156, 177, + 176, 175, 174, 173, 172, 171, 170, 169, 168, 167, + 166, 165, 164, 161, 160, 159, 152, 151, 150, 149, + 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, + 138, 137, 136, 135, 134, 133, 132, 131, 130, 129, + 120, 118, 115, 114, 113, 108, 92, 91, 87, 70, + 64, 60, 223, 7, 223, 223, 223, 223, 223, 223, + + 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 223, 223 + } ; + +static yyconst short int yy_chk[454] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, + 4, 5, 5, 6, 6, 9, 9, 9, 3, 4, + 10, 10, 10, 12, 14, 15, 19, 21, 22, 22, + 22, 28, 28, 23, 30, 30, 21, 19, 23, 24, + 14, 24, 24, 24, 44, 23, 50, 44, 15, 12, + + 38, 24, 25, 24, 25, 25, 25, 37, 38, 39, + 40, 41, 24, 45, 25, 61, 37, 41, 221, 37, + 39, 45, 40, 57, 57, 25, 42, 40, 24, 50, + 42, 58, 58, 59, 59, 59, 63, 63, 67, 68, + 178, 61, 121, 42, 69, 69, 75, 75, 75, 79, + 79, 79, 119, 119, 121, 121, 75, 180, 82, 79, + 82, 67, 68, 82, 82, 82, 178, 75, 121, 218, + 79, 217, 80, 63, 80, 80, 80, 126, 126, 126, + 180, 69, 135, 135, 80, 81, 153, 81, 81, 81, + 84, 123, 84, 84, 84, 80, 216, 81, 153, 153, + + 123, 123, 84, 181, 125, 213, 125, 179, 81, 125, + 125, 125, 153, 84, 123, 127, 127, 127, 154, 155, + 157, 157, 157, 158, 158, 158, 181, 212, 155, 155, + 154, 154, 154, 179, 211, 210, 207, 206, 205, 154, + 154, 201, 155, 200, 154, 199, 198, 154, 154, 154, + 154, 154, 154, 156, 197, 196, 194, 193, 192, 191, + 190, 189, 156, 156, 156, 187, 186, 185, 184, 177, + 176, 156, 156, 173, 172, 171, 156, 169, 168, 156, + 156, 156, 156, 156, 156, 224, 224, 224, 224, 224, + 224, 224, 224, 225, 225, 225, 225, 225, 225, 225, + + 225, 226, 167, 226, 226, 226, 226, 226, 226, 227, + 166, 164, 227, 227, 227, 227, 227, 228, 228, 228, + 228, 229, 229, 229, 163, 162, 229, 230, 230, 230, + 161, 159, 230, 231, 231, 232, 232, 233, 233, 152, + 150, 149, 148, 147, 146, 145, 144, 142, 141, 139, + 138, 137, 136, 134, 133, 132, 115, 114, 113, 112, + 111, 110, 109, 108, 107, 106, 105, 102, 101, 100, + 99, 98, 96, 95, 94, 93, 92, 89, 87, 85, + 60, 55, 48, 47, 46, 43, 36, 35, 29, 18, + 13, 11, 7, 223, 223, 223, 223, 223, 223, 223, + + 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 223, 223 + } ; + +#define REJECT \ +{ \ +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ \ +yy_cp = yy_full_match; /* restore poss. backed-over text */ \ +++yy_lp; \ +goto find_rule; \ +} +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "lex.javascript.yy" +#define INITIAL 0 +/* -------- definitions ------- */ +#line 6 "lex.javascript.yy" +#include +#include + +using namespace ixion; +using namespace javascript; +/* higher-level entities ------------------------------------------------------ +*/ +/* literals ------------------------------------------------------------------- +*/ +/* Contexts ------------------------------------------------------------------- +*/ +#define Comment 1 + +#define LineComment 2 + +/* Rules ---------------------------------------------------------------------- +*/ +#line 569 "lex.javascript.cc" + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap YY_PROTO(( void )); +#else +extern int yywrap YY_PROTO(( void )); +#endif +#endif + + +#ifndef yytext_ptr +static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int )); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen YY_PROTO(( yyconst char * )); +#endif + +#ifndef YY_NO_INPUT +#endif + +#if YY_STACK_USED +static int yy_start_stack_ptr = 0; +static int yy_start_stack_depth = 0; +static int *yy_start_stack = 0; +#ifndef YY_NO_PUSH_STATE +static void yy_push_state YY_PROTO(( int new_state )); +#endif +#ifndef YY_NO_POP_STATE +static void yy_pop_state YY_PROTO(( void )); +#endif +#ifndef YY_NO_TOP_STATE +static int yy_top_state YY_PROTO(( void )); +#endif + +#else +#define YY_NO_PUSH_STATE 1 +#define YY_NO_POP_STATE 1 +#define YY_NO_TOP_STATE 1 +#endif + +#ifdef YY_MALLOC_DECL +YY_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +#define ECHO LexerOutput( yytext, yyleng ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( (result = LexerInput( (char *) buf, max_size )) < 0 ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) LexerError( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL int yyFlexLexer::yylex() +#endif + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +YY_DECL + { + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 72 "lex.javascript.yy" + + +#line 699 "lex.javascript.cc" + + if ( yy_init ) + { + yy_init = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = &cin; + + if ( ! yyout ) + yyout = &cout; + + if ( ! yy_current_buffer ) + yy_current_buffer = + yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; + yy_state_ptr = yy_state_buf; + *yy_state_ptr++ = yy_current_state; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 224 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + *yy_state_ptr++ = yy_current_state; + ++yy_cp; + } + while ( yy_current_state != 223 ); + +yy_find_action: + yy_current_state = *--yy_state_ptr; + yy_lp = yy_accept[yy_current_state]; +find_rule: /* we branch to this label when backing up */ + for ( ; ; ) /* until we find what rule we matched */ + { + if ( yy_lp && yy_lp < yy_accept[yy_current_state + 1] ) + { + yy_act = yy_acclist[yy_lp]; + { + yy_full_match = yy_cp; + break; + } + } + --yy_cp; + yy_current_state = *--yy_state_ptr; + yy_lp = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + if ( yy_act != YY_END_OF_BUFFER ) + { + int yyl; + for ( yyl = 0; yyl < yyleng; ++yyl ) + if ( yytext[yyl] == '\n' ) + ++yylineno; + } + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( yy_act ) + { /* beginning of action switch */ +case 1: +YY_RULE_SETUP +#line 74 "lex.javascript.yy" +BEGIN(Comment); + YY_BREAK +case 2: +YY_RULE_SETUP +#line 75 "lex.javascript.yy" +BEGIN(INITIAL); + YY_BREAK +case YY_STATE_EOF(Comment): +#line 76 "lex.javascript.yy" +EXJS_THROW(ECJS_UNTERMINATED_COMMENT) + YY_BREAK +case 3: +YY_RULE_SETUP +#line 77 "lex.javascript.yy" +/* nothing */ + YY_BREAK +case 4: +YY_RULE_SETUP +#line 78 "lex.javascript.yy" +/* nothing */ + YY_BREAK +case 5: +YY_RULE_SETUP +#line 79 "lex.javascript.yy" +BEGIN(LineComment); + YY_BREAK +case 6: +YY_RULE_SETUP +#line 80 "lex.javascript.yy" +BEGIN(INITIAL); + YY_BREAK +case 7: +YY_RULE_SETUP +#line 81 "lex.javascript.yy" +/* nothing */ + YY_BREAK +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(LineComment): +#line 83 "lex.javascript.yy" +return TT_EOF; + YY_BREAK +case 8: +YY_RULE_SETUP +#line 85 "lex.javascript.yy" +return '{'; + YY_BREAK +case 9: +YY_RULE_SETUP +#line 86 "lex.javascript.yy" +return '}'; + YY_BREAK +case 10: +YY_RULE_SETUP +#line 87 "lex.javascript.yy" +return ';'; + YY_BREAK +case 11: +YY_RULE_SETUP +#line 89 "lex.javascript.yy" +return '['; + YY_BREAK +case 12: +YY_RULE_SETUP +#line 90 "lex.javascript.yy" +return ']'; + YY_BREAK +case 13: +YY_RULE_SETUP +#line 91 "lex.javascript.yy" +return '('; + YY_BREAK +case 14: +YY_RULE_SETUP +#line 92 "lex.javascript.yy" +return ')'; + YY_BREAK +case 15: +YY_RULE_SETUP +#line 93 "lex.javascript.yy" +return '?'; + YY_BREAK +case 16: +YY_RULE_SETUP +#line 94 "lex.javascript.yy" +return ':'; + YY_BREAK +case 17: +YY_RULE_SETUP +#line 95 "lex.javascript.yy" +return '+'; + YY_BREAK +case 18: +YY_RULE_SETUP +#line 96 "lex.javascript.yy" +return '-'; + YY_BREAK +case 19: +YY_RULE_SETUP +#line 97 "lex.javascript.yy" +return '*'; + YY_BREAK +case 20: +YY_RULE_SETUP +#line 98 "lex.javascript.yy" +return '/'; + YY_BREAK +case 21: +YY_RULE_SETUP +#line 99 "lex.javascript.yy" +return '%'; + YY_BREAK +case 22: +YY_RULE_SETUP +#line 100 "lex.javascript.yy" +return '^'; + YY_BREAK +case 23: +YY_RULE_SETUP +#line 101 "lex.javascript.yy" +return '&'; + YY_BREAK +case 24: +YY_RULE_SETUP +#line 102 "lex.javascript.yy" +return '|'; + YY_BREAK +case 25: +YY_RULE_SETUP +#line 103 "lex.javascript.yy" +return '~'; + YY_BREAK +case 26: +YY_RULE_SETUP +#line 104 "lex.javascript.yy" +return '!'; + YY_BREAK +case 27: +YY_RULE_SETUP +#line 105 "lex.javascript.yy" +return '='; + YY_BREAK +case 28: +YY_RULE_SETUP +#line 106 "lex.javascript.yy" +return '<'; + YY_BREAK +case 29: +YY_RULE_SETUP +#line 107 "lex.javascript.yy" +return '>'; + YY_BREAK +case 30: +YY_RULE_SETUP +#line 108 "lex.javascript.yy" +return ','; + YY_BREAK +case 31: +YY_RULE_SETUP +#line 109 "lex.javascript.yy" +return '.'; + YY_BREAK +case 32: +YY_RULE_SETUP +#line 110 "lex.javascript.yy" +return TT_JS_PLUS_ASSIGN; + YY_BREAK +case 33: +YY_RULE_SETUP +#line 111 "lex.javascript.yy" +return TT_JS_MINUS_ASSIGN; + YY_BREAK +case 34: +YY_RULE_SETUP +#line 112 "lex.javascript.yy" +return TT_JS_MULTIPLY_ASSIGN; + YY_BREAK +case 35: +YY_RULE_SETUP +#line 113 "lex.javascript.yy" +return TT_JS_DIVIDE_ASSIGN; + YY_BREAK +case 36: +YY_RULE_SETUP +#line 114 "lex.javascript.yy" +return TT_JS_MODULO_ASSIGN; + YY_BREAK +case 37: +YY_RULE_SETUP +#line 115 "lex.javascript.yy" +return TT_JS_BIT_XOR_ASSIGN; + YY_BREAK +case 38: +YY_RULE_SETUP +#line 116 "lex.javascript.yy" +return TT_JS_BIT_AND_ASSIGN; + YY_BREAK +case 39: +YY_RULE_SETUP +#line 117 "lex.javascript.yy" +return TT_JS_BIT_OR_ASSIGN; + YY_BREAK +case 40: +YY_RULE_SETUP +#line 118 "lex.javascript.yy" +return TT_JS_LEFT_SHIFT; + YY_BREAK +case 41: +YY_RULE_SETUP +#line 119 "lex.javascript.yy" +return TT_JS_RIGHT_SHIFT; + YY_BREAK +case 42: +YY_RULE_SETUP +#line 120 "lex.javascript.yy" +return TT_JS_LEFT_SHIFT_ASSIGN; + YY_BREAK +case 43: +YY_RULE_SETUP +#line 121 "lex.javascript.yy" +return TT_JS_RIGHT_SHIFT_ASSIGN; + YY_BREAK +case 44: +YY_RULE_SETUP +#line 122 "lex.javascript.yy" +return TT_JS_IDENTICAL; + YY_BREAK +case 45: +YY_RULE_SETUP +#line 123 "lex.javascript.yy" +return TT_JS_NOT_IDENTICAL; + YY_BREAK +case 46: +YY_RULE_SETUP +#line 124 "lex.javascript.yy" +return TT_JS_EQUAL; + YY_BREAK +case 47: +YY_RULE_SETUP +#line 125 "lex.javascript.yy" +return TT_JS_NOT_EQUAL; + YY_BREAK +case 48: +YY_RULE_SETUP +#line 126 "lex.javascript.yy" +return TT_JS_LESS_EQUAL; + YY_BREAK +case 49: +YY_RULE_SETUP +#line 127 "lex.javascript.yy" +return TT_JS_GREATER_EQUAL; + YY_BREAK +case 50: +YY_RULE_SETUP +#line 128 "lex.javascript.yy" +return TT_JS_LOGICAL_AND; + YY_BREAK +case 51: +YY_RULE_SETUP +#line 129 "lex.javascript.yy" +return TT_JS_LOGICAL_OR; + YY_BREAK +case 52: +YY_RULE_SETUP +#line 130 "lex.javascript.yy" +return TT_JS_INCREMENT; + YY_BREAK +case 53: +YY_RULE_SETUP +#line 131 "lex.javascript.yy" +return TT_JS_DECREMENT; + YY_BREAK +case 54: +YY_RULE_SETUP +#line 133 "lex.javascript.yy" +return TT_JS_NEW; + YY_BREAK +case 55: +YY_RULE_SETUP +#line 135 "lex.javascript.yy" +return TT_JS_THIS; + YY_BREAK +case 56: +YY_RULE_SETUP +#line 136 "lex.javascript.yy" +return TT_JS_FUNCTION; + YY_BREAK +case 57: +YY_RULE_SETUP +#line 137 "lex.javascript.yy" +return TT_JS_VAR; + YY_BREAK +case 58: +YY_RULE_SETUP +#line 138 "lex.javascript.yy" +return TT_JS_NULL; + YY_BREAK +case 59: +YY_RULE_SETUP +#line 139 "lex.javascript.yy" +return TT_JS_IF; + YY_BREAK +case 60: +YY_RULE_SETUP +#line 140 "lex.javascript.yy" +return TT_JS_WHILE; + YY_BREAK +case 61: +YY_RULE_SETUP +#line 141 "lex.javascript.yy" +return TT_JS_DO; + YY_BREAK +case 62: +YY_RULE_SETUP +#line 142 "lex.javascript.yy" +return TT_JS_ELSE; + YY_BREAK +case 63: +YY_RULE_SETUP +#line 143 "lex.javascript.yy" +return TT_JS_FOR; + YY_BREAK +case 64: +YY_RULE_SETUP +#line 144 "lex.javascript.yy" +return TT_JS_RETURN; + YY_BREAK +case 65: +YY_RULE_SETUP +#line 145 "lex.javascript.yy" +return TT_JS_SWITCH; + YY_BREAK +case 66: +YY_RULE_SETUP +#line 146 "lex.javascript.yy" +return TT_JS_CASE; + YY_BREAK +case 67: +YY_RULE_SETUP +#line 147 "lex.javascript.yy" +return TT_JS_CONTINUE; + YY_BREAK +case 68: +YY_RULE_SETUP +#line 148 "lex.javascript.yy" +return TT_JS_BREAK; + YY_BREAK +case 69: +YY_RULE_SETUP +#line 149 "lex.javascript.yy" +return TT_JS_DEFAULT; + YY_BREAK +case 70: +YY_RULE_SETUP +#line 150 "lex.javascript.yy" +return TT_JS_LIT_TRUE; + YY_BREAK +case 71: +YY_RULE_SETUP +#line 151 "lex.javascript.yy" +return TT_JS_LIT_FALSE; + YY_BREAK +case 72: +YY_RULE_SETUP +#line 152 "lex.javascript.yy" +return TT_JS_LIT_UNDEFINED; + YY_BREAK +case 73: +YY_RULE_SETUP +#line 153 "lex.javascript.yy" +return TT_JS_IN; + YY_BREAK +case 74: +YY_RULE_SETUP +#line 154 "lex.javascript.yy" +return TT_JS_CONST; + YY_BREAK +case 75: +YY_RULE_SETUP +#line 155 "lex.javascript.yy" +return TT_JS_CLASS; + YY_BREAK +case 76: +YY_RULE_SETUP +#line 156 "lex.javascript.yy" +return TT_JS_EXTENDS; + YY_BREAK +case 77: +YY_RULE_SETUP +#line 157 "lex.javascript.yy" +return TT_JS_NAMESPACE; + YY_BREAK +case 78: +YY_RULE_SETUP +#line 158 "lex.javascript.yy" +return TT_JS_STATIC; + YY_BREAK +case 79: +YY_RULE_SETUP +#line 159 "lex.javascript.yy" +return TT_JS_CONSTRUCTOR; + YY_BREAK +case 80: +YY_RULE_SETUP +#line 161 "lex.javascript.yy" +return TT_JS_LIT_INT; + YY_BREAK +case 81: +YY_RULE_SETUP +#line 162 "lex.javascript.yy" +return TT_JS_LIT_FLOAT; + YY_BREAK +case 82: +YY_RULE_SETUP +#line 163 "lex.javascript.yy" +return TT_JS_LIT_STRING; + YY_BREAK +case 83: +YY_RULE_SETUP +#line 165 "lex.javascript.yy" +return TT_JS_IDENTIFIER; + YY_BREAK +case 84: +YY_RULE_SETUP +#line 167 "lex.javascript.yy" +/* nothing */ + YY_BREAK +case 85: +YY_RULE_SETUP +#line 168 "lex.javascript.yy" +EXJS_THROWINFOLOCATION(ECJS_INVALID_TOKEN,YYText(),code_location(lineno())) + YY_BREAK +case 86: +YY_RULE_SETUP +#line 169 "lex.javascript.yy" +ECHO; + YY_BREAK +#line 1229 "lex.javascript.cc" + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yy_n_chars = yy_current_buffer->yy_n_chars; + yy_current_buffer->yy_input_file = yyin; + yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = + yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of yylex */ + +yyFlexLexer::yyFlexLexer( istream* arg_yyin, ostream* arg_yyout ) + { + yyin = arg_yyin; + yyout = arg_yyout; + yy_c_buf_p = 0; + yy_init = 1; + yy_start = 0; + yy_flex_debug = 0; + yylineno = 1; // this will only get updated if %option yylineno + + yy_did_buffer_switch_on_eof = 0; + + yy_looking_for_trail_begin = 0; + yy_more_flag = 0; + yy_more_len = 0; + yy_more_offset = yy_prev_more_offset = 0; + + yy_start_stack_ptr = yy_start_stack_depth = 0; + yy_start_stack = 0; + + yy_current_buffer = 0; + +#ifdef YY_USES_REJECT + yy_state_buf = new yy_state_type[YY_BUF_SIZE + 2]; +#else + yy_state_buf = 0; +#endif + } + +yyFlexLexer::~yyFlexLexer() + { + delete yy_state_buf; + yy_delete_buffer( yy_current_buffer ); + } + +void yyFlexLexer::switch_streams( istream* new_in, ostream* new_out ) + { + if ( new_in ) + { + yy_delete_buffer( yy_current_buffer ); + yy_switch_to_buffer( yy_create_buffer( new_in, YY_BUF_SIZE ) ); + } + + if ( new_out ) + yyout = new_out; + } + +#ifdef YY_INTERACTIVE +int yyFlexLexer::LexerInput( char* buf, int /* max_size */ ) +#else +int yyFlexLexer::LexerInput( char* buf, int max_size ) +#endif + { + if ( yyin->eof() || yyin->fail() ) + return 0; + +#ifdef YY_INTERACTIVE + yyin->get( buf[0] ); + + if ( yyin->eof() ) + return 0; + + if ( yyin->bad() ) + return -1; + + return 1; + +#else + (void) yyin->read( buf, max_size ); + + if ( yyin->bad() ) + return -1; + else + return yyin->gcount(); +#endif + } + +void yyFlexLexer::LexerOutput( const char* buf, int size ) + { + (void) yyout->write( buf, size ); + } + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +int yyFlexLexer::yy_get_next_buffer() + { + register char *dest = yy_current_buffer->yy_ch_buf; + register char *source = yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( yy_current_buffer->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_current_buffer->yy_n_chars = yy_n_chars = 0; + + else + { + int num_to_read = + yy_current_buffer->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = yy_current_buffer; + + int yy_c_buf_p_offset = + (int) (yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yy_flex_realloc( (void *) b->yy_ch_buf, + b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = yy_current_buffer->yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; + + return ret_val; + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +yy_state_type yyFlexLexer::yy_get_previous_state() + { + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = yy_start; + yy_state_ptr = yy_state_buf; + *yy_state_ptr++ = yy_current_state; + + for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 224 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + *yy_state_ptr++ = yy_current_state; + } + + return yy_current_state; + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +yy_state_type yyFlexLexer::yy_try_NUL_trans( yy_state_type yy_current_state ) + { + register int yy_is_jam; + + register YY_CHAR yy_c = 1; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 224 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 223); + if ( ! yy_is_jam ) + *yy_state_ptr++ = yy_current_state; + + return yy_is_jam ? 0 : yy_current_state; + } + + +void yyFlexLexer::yyunput( int c, register char* yy_bp ) + { + register char *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yy_n_chars + 2; + register char *dest = &yy_current_buffer->yy_ch_buf[ + yy_current_buffer->yy_buf_size + 2]; + register char *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + yy_current_buffer->yy_n_chars = + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + if ( c == '\n' ) + --yylineno; + + yytext_ptr = yy_bp; + yy_hold_char = *yy_cp; + yy_c_buf_p = yy_cp; + } + + +int yyFlexLexer::yyinput() + { + int c; + + *yy_c_buf_p = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* This was really a NUL. */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = yy_c_buf_p - yytext_ptr; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /* fall through */ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + return EOF; + + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ + *yy_c_buf_p = '\0'; /* preserve yytext */ + yy_hold_char = *++yy_c_buf_p; + + if ( c == '\n' ) + ++yylineno; + + return c; + } + + +void yyFlexLexer::yyrestart( istream* input_file ) + { + if ( ! yy_current_buffer ) + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +void yyFlexLexer::yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +void yyFlexLexer::yy_load_buffer_state() + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +YY_BUFFER_STATE yyFlexLexer::yy_create_buffer( istream* file, int size ) + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; + } + + +void yyFlexLexer::yy_delete_buffer( YY_BUFFER_STATE b ) + { + if ( ! b ) + return; + + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yy_flex_free( (void *) b->yy_ch_buf ); + + yy_flex_free( (void *) b ); + } + + +extern "C" int isatty YY_PROTO(( int )); +void yyFlexLexer::yy_init_buffer( YY_BUFFER_STATE b, istream* file ) + + { + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + b->yy_is_interactive = 0; + } + + +void yyFlexLexer::yy_flush_buffer( YY_BUFFER_STATE b ) + { + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == yy_current_buffer ) + yy_load_buffer_state(); + } + + +#ifndef YY_NO_SCAN_BUFFER +#endif + + +#ifndef YY_NO_SCAN_STRING +#endif + + +#ifndef YY_NO_SCAN_BYTES +#endif + + +#ifndef YY_NO_PUSH_STATE +void yyFlexLexer::yy_push_state( int new_state ) + { + if ( yy_start_stack_ptr >= yy_start_stack_depth ) + { + yy_size_t new_size; + + yy_start_stack_depth += YY_START_STACK_INCR; + new_size = yy_start_stack_depth * sizeof( int ); + + if ( ! yy_start_stack ) + yy_start_stack = (int *) yy_flex_alloc( new_size ); + + else + yy_start_stack = (int *) yy_flex_realloc( + (void *) yy_start_stack, new_size ); + + if ( ! yy_start_stack ) + YY_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + yy_start_stack[yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); + } +#endif + + +#ifndef YY_NO_POP_STATE +void yyFlexLexer::yy_pop_state() + { + if ( --yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yy_start_stack[yy_start_stack_ptr]); + } +#endif + + +#ifndef YY_NO_TOP_STATE +int yyFlexLexer::yy_top_state() + { + return yy_start_stack[yy_start_stack_ptr - 1]; + } +#endif + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + + +void yyFlexLexer::LexerError( yyconst char msg[] ) + { + cerr << msg << '\n'; + exit( YY_EXIT_FAILURE ); + } + + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yytext[yyleng] = yy_hold_char; \ + yy_c_buf_p = yytext + n; \ + yy_hold_char = *yy_c_buf_p; \ + *yy_c_buf_p = '\0'; \ + yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef yytext_ptr +#ifdef YY_USE_PROTOS +static void yy_flex_strncpy( char *s1, yyconst char *s2, int n ) +#else +static void yy_flex_strncpy( s1, s2, n ) +char *s1; +yyconst char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef YY_NEED_STRLEN +#ifdef YY_USE_PROTOS +static int yy_flex_strlen( yyconst char *s ) +#else +static int yy_flex_strlen( s ) +yyconst char *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef YY_USE_PROTOS +static void *yy_flex_alloc( yy_size_t size ) +#else +static void *yy_flex_alloc( size ) +yy_size_t size; +#endif + { + return (void *) malloc( size ); + } + +#ifdef YY_USE_PROTOS +static void *yy_flex_realloc( void *ptr, yy_size_t size ) +#else +static void *yy_flex_realloc( ptr, size ) +void *ptr; +yy_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); + } + +#ifdef YY_USE_PROTOS +static void yy_flex_free( void *ptr ) +#else +static void yy_flex_free( ptr ) +void *ptr; +#endif + { + free( ptr ); + } + +#if YY_MAIN +int main() + { + yylex(); + return 0; + } +#endif +#line 169 "lex.javascript.yy" diff --git a/simgear/interpreter/lex.javascript.yy b/simgear/interpreter/lex.javascript.yy new file mode 100644 index 00000000..ed14795d --- /dev/null +++ b/simgear/interpreter/lex.javascript.yy @@ -0,0 +1,168 @@ +/* -------- definitions ------- */ + +%option c++ yylineno noyywrap prefix="js" outfile="lex.javascript.cc" batch + +%{ +#include +#include + +using namespace ixion; +using namespace javascript; +%} + +WHITESPACE [ \t\n\r] + +DIGIT [0-9] +DIGIT_NZ [1-9] +DIGIT_OCT [0-7] +DIGIT_HEX [0-9a-fA-F] +DIGIT_SEQ {DIGIT}+ + +NONDIGIT [_a-zA-Z] +ID_COMPONENT [_a-zA-Z0-9] + +ESCAPE_SIMPLE \\['"?\\abfnrtv] +ESCAPE_OCTAL \\{DIGIT_OCT}{1,3} +ESCAPE_HEX \\x{DIGIT_HEX}{1,2} +ESCAPE {ESCAPE_SIMPLE}|{ESCAPE_OCTAL}|{ESCAPE_HEX} +S_CHAR [^"\\\n]|{ESCAPE} + +SIGN \+|\- +SIGNopt {SIGN}? + + + + +/* higher-level entities ------------------------------------------------------ +*/ +IDENTIFIER {NONDIGIT}{ID_COMPONENT}* + + + + +/* literals ------------------------------------------------------------------- +*/ +LIT_DECIMAL {DIGIT_NZ}{DIGIT}* +LIT_OCTAL 0{DIGIT_OCT}* +LIT_HEX 0[xX]{DIGIT_HEX}+ +LIT_INT ({LIT_DECIMAL}|{LIT_OCTAL}|{LIT_HEX}) + +LIT_STRING \"{S_CHAR}*\"|\'{S_CHAR}*\' + +LIT_FRACTION {DIGIT_SEQ}?\.{DIGIT_SEQ}|{DIGIT_SEQ}\. +LIT_EXPONENT [eE]{SIGNopt}{DIGIT_SEQ} +LIT_FLOAT {LIT_FRACTION}{LIT_EXPONENT}?|{DIGIT_SEQ}{LIT_EXPONENT} + + + + + + +/* Contexts ------------------------------------------------------------------- +*/ + +%x Comment +%x LineComment + + + + +/* Rules ---------------------------------------------------------------------- +*/ +%% + +\/\* BEGIN(Comment); +\*\/ BEGIN(INITIAL); +<> EXJS_THROW(ECJS_UNTERMINATED_COMMENT) +. /* nothing */ +\n /* nothing */ +\/\/ BEGIN(LineComment); +[\n\r]+ BEGIN(INITIAL); +. /* nothing */ + +<> return TT_EOF; + +\{ return '{'; +\} return '}'; +\; return ';'; + +\[ return '['; +\] return ']'; +\( return '('; +\) return ')'; +\? return '?'; +\: return ':'; +\+ return '+'; +\- return '-'; +\* return '*'; +\/ return '/'; +\% return '%'; +\^ return '^'; +\& return '&'; +\| return '|'; +\~ return '~'; +\! return '!'; +\= return '='; +\< return '<'; +\> return '>'; +\, return ','; +\. return '.'; +\+\= return TT_JS_PLUS_ASSIGN; +\-\= return TT_JS_MINUS_ASSIGN; +\*\= return TT_JS_MULTIPLY_ASSIGN; +\/\= return TT_JS_DIVIDE_ASSIGN; +\%\= return TT_JS_MODULO_ASSIGN; +\^\= return TT_JS_BIT_XOR_ASSIGN; +\&\= return TT_JS_BIT_AND_ASSIGN; +\|\= return TT_JS_BIT_OR_ASSIGN; +\<\< return TT_JS_LEFT_SHIFT; +\>\> return TT_JS_RIGHT_SHIFT; +\<\<\= return TT_JS_LEFT_SHIFT_ASSIGN; +\>\>\= return TT_JS_RIGHT_SHIFT_ASSIGN; +\=\=\= return TT_JS_IDENTICAL; +\!\=\= return TT_JS_NOT_IDENTICAL; +\=\= return TT_JS_EQUAL; +\!\= return TT_JS_NOT_EQUAL; +\<\= return TT_JS_LESS_EQUAL; +\>\= return TT_JS_GREATER_EQUAL; +\&\& return TT_JS_LOGICAL_AND; +\|\| return TT_JS_LOGICAL_OR; +\+\+ return TT_JS_INCREMENT; +\-\- return TT_JS_DECREMENT; + +new return TT_JS_NEW; + +this return TT_JS_THIS; +function return TT_JS_FUNCTION; +var return TT_JS_VAR; +null return TT_JS_NULL; +if return TT_JS_IF; +while return TT_JS_WHILE; +do return TT_JS_DO; +else return TT_JS_ELSE; +for return TT_JS_FOR; +return return TT_JS_RETURN; +switch return TT_JS_SWITCH; +case return TT_JS_CASE; +continue return TT_JS_CONTINUE; +break return TT_JS_BREAK; +default return TT_JS_DEFAULT; +true return TT_JS_LIT_TRUE; +false return TT_JS_LIT_FALSE; +undefined return TT_JS_LIT_UNDEFINED; +in return TT_JS_IN; +const return TT_JS_CONST; +class return TT_JS_CLASS; +extends return TT_JS_EXTENDS; +namespace return TT_JS_NAMESPACE; +static return TT_JS_STATIC; +constructor return TT_JS_CONSTRUCTOR; + +{LIT_INT} return TT_JS_LIT_INT; +{LIT_FLOAT} return TT_JS_LIT_FLOAT; +{LIT_STRING} return TT_JS_LIT_STRING; + +{IDENTIFIER} return TT_JS_IDENTIFIER; + +{WHITESPACE}+ /* nothing */ +. EXJS_THROWINFOLOCATION(ECJS_INVALID_TOKEN,YYText(),code_location(lineno())) diff --git a/simgear/interpreter/main.cc b/simgear/interpreter/main.cc new file mode 100644 index 00000000..455e83cc --- /dev/null +++ b/simgear/interpreter/main.cc @@ -0,0 +1,41 @@ +#include +#include +#include + +#include + +using namespace ixion; +using namespace ixion::javascript; + +IXLIB_JS_DECLARE_FUNCTION(write) +{ + if (parameters.size() != 1) { + EXJS_THROWINFO(ECJS_INVALID_NUMBER_OF_ARGUMENTS, "write"); + } + std::cout << parameters[0]->toString(); + return makeConstant(parameters[0]->toString()); +} + +int main (int ac, char ** av) { + interpreter *jsint = new interpreter(); + addStandardLibrary(*jsint); + ref x = new write(); + jsint->RootScope->addMember("write", x); + + if (ac == 1) { + std::cerr << "Usage: " << av[0] << "" << std::endl; + exit(1); + } + for (int i = 1; i < ac; i++) { + std::ifstream input(av[i]); + try { + ref result = jsint->execute(input); + std::cout << av[i] << " returned " << result->stringify() << std::endl; + } catch (base_exception &ex) { + std::cerr << ex.getText() << ex.what() << std::endl; + } + input.close(); + } + delete jsint; +} + diff --git a/simgear/interpreter/numconv.cc b/simgear/interpreter/numconv.cc new file mode 100644 index 00000000..44930323 --- /dev/null +++ b/simgear/interpreter/numconv.cc @@ -0,0 +1,144 @@ +// ---------------------------------------------------------------------------- +// Description : Numeric conversions +// ---------------------------------------------------------------------------- +// (c) Copyright 1999 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#include +#include +#include + + + + +using namespace std; +using namespace ixion; + + + + +// data objects -------------------------------------------------------------- +static string numChars = IXLIB_NUMCHARS; + + + + +// exported subroutines ------------------------------------------------------- +string ixion::float2dec(double value) { + char buf[255]; + sprintf((char *)&buf,"%f",value); + return string(buf); + } + + + + +string ixion::float2dec(double value, unsigned int precision) { + char buf[255]; + string cmd("%."); + + cmd += unsigned2dec(precision) + "f"; + sprintf((char *)&buf,cmd.c_str(),value); + return string(buf); + } + + + + +string ixion::unsigned2base(unsigned long value,char digits,char radix) { + string temp; + do { + temp = numChars[value % radix]+temp; + value /= radix; + if (digits) digits--; + } while (value || digits); + return temp; + } + + + + +string ixion::signed2base(signed long value,char digits,char radix) { + if (value < 0) return "-"+unsigned2base(-value,digits,radix); + else return unsigned2base(value,digits,radix); + } + + + + +string ixion::bytes2dec(TSize bytes) { + if (bytes>(TSize) 10*1024*1024) + return unsigned2dec(bytes / ((TSize) 1024*1024))+" MB"; + if (bytes>(TSize) 10*1024) + return unsigned2dec(bytes / ((TSize) 1024))+" KB"; + return unsigned2dec(bytes)+" Byte"; + } + + + + +unsigned long ixion::evalNumeral(string const &numeral,unsigned radix) { + string numstr = upper(numeral); + + if (numstr.size() == 0) return 0; + unsigned long value = 0, mulvalue = 1; + TIndex index = numstr.size()-1; + + do { + string::size_type digvalue = numChars.find(numstr[index]); + if (digvalue == string::npos) + EXGEN_THROWINFO(EC_CANNOTEVALUATE,numstr.c_str()) + value += mulvalue * digvalue; + mulvalue *= radix; + } while (index--); + + return value; + } + + + + +double ixion::evalFloat(string const &numeral) { + double result; + int count = sscanf(numeral.c_str(), "%le", &result); + if (count == 0) EXGEN_THROWINFO(EC_CANNOTEVALUATE,numeral.c_str()) + else return result; + } + + + + +unsigned long ixion::evalUnsigned(string const &numeral,unsigned default_base) { + if (numeral.size() == 0) return 0; + + if (numeral.substr(0,2) == "0X" || numeral.substr(0,2) == "0x") + return evalNumeral(numeral.substr(2),0x10); + if (numeral.substr(0,1) == "$") + return evalNumeral(numeral.substr(1),0x10); + + char lastchar = numeral[numeral.size()-1]; + if (lastchar == 'H' || lastchar == 'h') return evalNumeral(numeral.substr(0,numeral.size()-1),0x10); + if (lastchar == 'B' || lastchar == 'b') return evalNumeral(numeral.substr(0,numeral.size()-1),2); + if (lastchar == 'D' || lastchar == 'd') return evalNumeral(numeral.substr(0,numeral.size()-1),10); + if (lastchar == 'O' || lastchar == 'o') return evalNumeral(numeral.substr(0,numeral.size()-1),8); + + return evalNumeral(numeral,default_base); + } + + + + +signed long ixion::evalSigned(string const &numeral,unsigned default_base) { + if (numeral.size() == 0) return 0; + if (numeral[0] == '-') + return - (signed long) evalUnsigned(numeral.substr(1),default_base); + else { + if (numeral[0] == '+') + return evalUnsigned(numeral.substr(1),default_base); + else + return evalUnsigned(numeral,default_base); + } + } diff --git a/simgear/interpreter/numeric.cc b/simgear/interpreter/numeric.cc new file mode 100644 index 00000000..44c75719 --- /dev/null +++ b/simgear/interpreter/numeric.cc @@ -0,0 +1,38 @@ +// ---------------------------------------------------------------------------- +// Description : Numeric / order processing +// ---------------------------------------------------------------------------- +// (c) Copyright 1998 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#include + + + + +// BCD encoding --------------------------------------------------------------- +unsigned long ixion::unsigned2BCD(unsigned long value) +{ + unsigned long bcdvalue = 0,bcdshift = 0; + while (value) { + bcdvalue += (value % 10) << bcdshift; + bcdshift += 4; + value /= 10; + } + return bcdvalue; +} + + + + +unsigned long ixion::BCD2unsigned(unsigned long value) +{ + unsigned long decvalue = 0; + for (unsigned long i = 1;value;i *= 10) { + decvalue += (value & 0xf) * i; + value >>= 4; + } + return decvalue; +} diff --git a/simgear/interpreter/re.cc b/simgear/interpreter/re.cc new file mode 100644 index 00000000..19e8acf1 --- /dev/null +++ b/simgear/interpreter/re.cc @@ -0,0 +1,427 @@ +// ---------------------------------------------------------------------------- +// Description : Regular expressions string object. +// ---------------------------------------------------------------------------- +// (c) Copyright 1998 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#include +#include +#include "ixlib_i18n.hh" +#include +#include +#include +#include +#include + + + + +using namespace std; +using namespace ixion; + + + + +// Template instantiations ---------------------------------------------------- +template regex; + + + + +// Error texts ---------------------------------------------------------------- +static char *RegexPlainText[] = { + N_("Invalid quantifier"), + N_("Unbalanced backreference"), + N_("Invalid escape sequence"), + N_("Invalid backreference"), + N_("Unterminated character class"), + N_("Unable to match without expression"), + }; + + + + +// regex_exception ------------------------------------------------------------ +regex_exception::regex_exception(TErrorCode error, + char const *info,char *module,TIndex line) +: base_exception(error,info,module,line,"RE") { + } + + + + +char *regex_exception::getText() const { + return _(RegexPlainText[Error]); + } + + + + +// regex_string::class_matcher ------------------------------------------------ +regex_string::class_matcher::class_matcher() + : Negated(false) { + MatchLength = 1; + } + + + + +regex_string::class_matcher::class_matcher(string const &cls) + : Negated(false) { + MatchLength = 1; + + if (cls.size() && cls[0] == XSTRRE_CLASSNEG) { + expandClass(cls.substr(1)); + Negated = true; + } + else + expandClass(cls); + } + + + + +ixion::regex::matcher *ixion::regex_string::class_matcher::duplicate() const { + class_matcher *dupe = new class_matcher(); + dupe->copy(this); + return dupe; + } + + + + +bool regex_string::class_matcher::match(backref_stack &brstack,string const &candidate,TIndex at) { + if (at >= candidate.size()) return false; + + bool result = Set[candidate[at]]; + if (Negated) result = !result; + + return result && matchNext(brstack,candidate,at+1); + } + + + + +void regex_string::class_matcher::expandClass(string const &cls) { + memset(&Set,0,sizeof(Set)); + + if (cls.size() == 0) return; + Set[cls[0]] = true; + char lastchar = cls[0]; + + for (TIndex index = 1;index < cls.size();index++) { + if ((cls[index] == XSTRRE_CLASSRANGE) && (index < cls.size()-1)) { + for (char ch = lastchar+1;ch < cls[index+1];ch++) + Set[ch] = true; + } + else Set[cls[index]] = true; + lastchar = cls[index]; + } + } + + + + +void ixion::regex_string::class_matcher::copy(class_matcher const *src) { + super::copy(src); + for (TIndex i = 0;i < CharValues;i++) + Set[i] = src->Set[i]; + Negated = src->Negated; + } + + + + +// regex_string::special_class_matcher ---------------------------------------- +regex_string::special_class_matcher::special_class_matcher(type tp) + : Type(tp) { + MatchLength = 1; + } + + + + +ixion::regex::matcher *ixion::regex_string::special_class_matcher::duplicate() const { + special_class_matcher *dupe = new special_class_matcher(Type); + dupe->copy(this); + return dupe; + } + + + + +bool regex_string::special_class_matcher::match(backref_stack &brstack,string const &candidate,TIndex at) { + if (at >= candidate.size()) return false; + enum type { DIGIT,NONDIGIT,ALNUM,NONALNUM,SPACE,NONSPACE }; + + bool result; + switch (Type) { + case DIGIT: result = isdigit(candidate[at]); + break; + case NONDIGIT: result = !isdigit(candidate[at]); + break; + case ALNUM: result = isalnum(candidate[at]); + break; + case NONALNUM: result = !isalnum(candidate[at]); + break; + case SPACE: result = isspace(candidate[at]); + break; + case NONSPACE: result = !isspace(candidate[at]); + break; + default: + EX_THROW(regex,ECRE_INVESCAPE) + } + + return result && matchNext(brstack,candidate,at+1); + } + + + + +// regex_string --------------------------------------------------------------- +void regex_string::parse(string const &expr) { + auto_ptr new_re(parseRegex(expr)); + ParsedRegex = new_re; + } + + + + +string regex_string::replaceAll(string const &candidate,string const &replacement,TIndex from) { + string result; + string tempreplacement; + + LastCandidate = candidate; + if (ParsedRegex.get() == NULL) + EX_THROW(regex,ECRE_NOPATTERN) + + for (TIndex index = from;index < candidate.size();) { + BackrefStack.clear(); + if (ParsedRegex->match(BackrefStack,candidate,index)) { + TIndex matchlength = ParsedRegex->subsequentMatchLength(); + tempreplacement = replacement; + + TSize backrefs = BackrefStack.size(); + for (TIndex i = 0;i < backrefs;i++) + tempreplacement = findReplace(tempreplacement,XSTRRE_BACKREF+unsigned2dec(i), + BackrefStack.get(i,LastCandidate)); + + result += tempreplacement; + index += matchlength; + } + else result += candidate[index++]; + } + return result; + } + + + + +regex_string::matcher *regex_string::parseRegex(string const &expr) { + if (!expr.size()) return NULL; + TIndex index = 0; + matcher *firstobject,*lastobject = NULL; + alternative_matcher *alternative = NULL; + + while (index < expr.size()) { + matcher *object = NULL; + quantifier *quantifier = NULL; + bool quantified = true; + char ch; + + // several objects may be inserted in one loop run + switch (expr[index++]) { + // case XSTRRE_BACKREF: (dupe) + // case XSTRRE_ESCAPESEQ: (dupe) + case XSTRRE_LITERAL: { + if (index >= expr.size()) EX_THROW(regex,ECRE_INVESCAPE) + + ch = expr[index++]; + if (isdigit(ch)) + object = new backref_matcher(ch-'0'); + else { + switch (ch) { + case 'd': object = new special_class_matcher(special_class_matcher::DIGIT); + break; + case 'D': object = new special_class_matcher(special_class_matcher::NONDIGIT); + break; + case 'w': object = new special_class_matcher(special_class_matcher::ALNUM); + break; + case 'W': object = new special_class_matcher(special_class_matcher::NONALNUM); + break; + case 's': object = new special_class_matcher(special_class_matcher::SPACE); + break; + case 'S': object = new special_class_matcher(special_class_matcher::NONSPACE); + break; + default: object = new sequence_matcher(string(1,ch)); + } + } + break; + } + case XSTRRE_ANYCHAR: + object = new any_matcher; + break; + case XSTRRE_START: + quantified = false; + object = new start_matcher; + break; + case XSTRRE_END: + quantified = false; + object = new end_matcher; + break; + case XSTRRE_ALTERNATIVE: { + if (!alternative) + alternative = new alternative_matcher; + alternative->addAlternative(firstobject); + firstobject = NULL; + lastobject = NULL; + break; + } + case XSTRRE_CLASSSTART: { + TIndex classend = expr.find(XSTRRE_CLASSSTOP,index); + if (classend == string::npos) + EX_THROW(regex,ECRE_UNTERMCLASS) + object = new class_matcher(expr.substr(index,classend-index)); + + index = classend+1; + break; + } + case XSTRRE_BACKREFSTART: { + matcher *parsed; + + TSize brlevel = 1; + for (TIndex searchstop = index;searchstop < expr.size();searchstop++) { + if ((expr[searchstop] == XSTRRE_BACKREFSTART) && + (expr[searchstop-1] != XSTRRE_LITERAL)) + brlevel++; + if ((expr[searchstop] == XSTRRE_BACKREFSTOP) && + (expr[searchstop-1] != XSTRRE_LITERAL)) { + brlevel--; + if (brlevel == 0) { + parsed = parseRegex(expr.substr(index,searchstop-index)); + if (!parsed) EX_THROW(regex,ECRE_INVBACKREF) + + index = searchstop+1; + break; + } + } + } + + if (!parsed) EX_THROW(regex,ECRE_UNBALBACKREF) + + object = new backref_open_matcher; + object->setNext(parsed); + + matcher *closer = new backref_close_matcher; + + matcher *searchlast = parsed,*foundlast; + while (searchlast) { + foundlast = searchlast; + searchlast = searchlast->getNext(); + } + foundlast->setNext(closer); + + break; + } + case XSTRRE_BACKREFSTOP: + EX_THROW(regex,ECRE_UNBALBACKREF) + default: + object = new sequence_matcher(expr.substr(index-1,1)); + break; + } + + if (object) { + if (quantified) quantifier = parseQuantifier(expr,index); + if (quantifier) { + quantifier->setQuantified(object); + if (lastobject) lastobject->setNext(quantifier); + else firstobject = quantifier; + } + else { + if (lastobject) lastobject->setNext(object); + else firstobject = object; + } + } + + // we need this for the alternative matcher, which also inserts + // its connector + matcher *searchlast = quantifier ? quantifier : object; + while (searchlast) { + lastobject = searchlast; + searchlast = searchlast->getNext(); + } + } + if (alternative) { + alternative->addAlternative(firstobject); + return alternative; + } + else return firstobject; + } + + + + +regex_string::quantifier *regex_string::parseQuantifier(string const &expr,TIndex &at) { + quantifier *quant = NULL; + if (at == expr.size()) return NULL; + if (expr[at] == XSTRREQ_0PLUS) { + quant = new quantifier(isGreedy(expr,++at),0); + return quant; + } + if (expr[at] == XSTRREQ_1PLUS) { + quant = new quantifier(isGreedy(expr,++at),1); + return quant; + } + if (expr[at] == XSTRREQ_01) { + quant = new quantifier(isGreedy(expr,++at),0,1); + return quant; + } + if (expr[at] == XSTRREQ_START) { + TSize min,max; + + at++; + TIndex endindex; + endindex = expr.find(XSTRREQ_STOP,at); + if (endindex == string::npos) + EXGEN_THROW(ECRE_INVQUANTIFIER) + + string quantspec = expr.substr(at,endindex-at); + at = endindex+1; + + try { + string::size_type rangeindex = quantspec.find(XSTRREQ_RANGE); + if (rangeindex == string::npos) { + min = evalUnsigned(quantspec); + quant = new quantifier(isGreedy(expr,at),min,min); + } + else if (rangeindex == quantspec.size()-1) { + min = evalUnsigned(quantspec.substr(0,rangeindex)); + quant = new quantifier(isGreedy(expr,at),min); + } + else { + min = evalUnsigned(quantspec.substr(0,rangeindex)); + max = evalUnsigned(quantspec.substr(rangeindex+1)); + quant = new quantifier(isGreedy(expr,at),min,max); + } + return quant; + } + EX_CONVERT(generic,EC_CANNOTEVALUATE,regex,ECRE_INVQUANTIFIER) + } + return NULL; + } + + + + +bool regex_string::isGreedy(string const &expr,TIndex &at) +{ + if (at == expr.size()) return true; + if (expr[at] == XSTRREQ_NONGREEDY) { + at++; + return false; + } + else return true; +} diff --git a/simgear/interpreter/scanner.cc b/simgear/interpreter/scanner.cc new file mode 100644 index 00000000..127d929f --- /dev/null +++ b/simgear/interpreter/scanner.cc @@ -0,0 +1,108 @@ +// ---------------------------------------------------------------------------- +// Description : Scanner for xTextFile +// ---------------------------------------------------------------------------- +// (c) Copyright 1999 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#include +#include "ixlib_i18n.hh" +#include +#include +#include + + + + +using namespace std; +using namespace ixion; + + + + +// Plain text rendering table ------------------------------------------------- +static char *(PlainText[]) = { + N_("Unknown token"), + N_("End of input") + }; + + + + +// scanner_exception ---------------------------------------------------------- +scanner_exception::scanner_exception(TErrorCode error, TIndex line, +string const &info) +: base_exception(error, NULL, NULL, 0, "SCAN") { + HasInfo = true; + try { + string temp = "line "; + temp += unsigned2dec(line); + if (info != "") { + temp += " : "; + temp += info; + } + strcpy(Info, temp.c_str()); + } + catch (...) { } + } + + + + +char *scanner_exception::getText() const { + return PlainText[Error]; + } + + + + +// scanner -------------------------------------------------------------------- +scanner::scanner(FlexLexer &lexer) +: Lexer(lexer) { + } + + + + +scanner::token_list scanner::scan() { + CurrentToken.Type = Lexer.yylex(); + CurrentToken.Line = Lexer.lineno(); + CurrentToken.Text = Lexer.YYText(); + + token_list tokenlist; + while (!reachedEOF()) { + tokenlist.push_back(getNextToken()); + } + return tokenlist; + } + + + + +scanner::token scanner::getNextToken() { + if (!reachedEOF()) { + token lasttoken = CurrentToken; + + CurrentToken.Type = Lexer.yylex(); + CurrentToken.Line = Lexer.lineno(); + CurrentToken.Text = Lexer.YYText(); + + if (CurrentToken.Type == TT_UNKNOWN) + throw scanner_exception(ECSCAN_UNKNOWN_TOKEN,CurrentToken.Line,CurrentToken.Text); + else return lasttoken; + } + throw scanner_exception(ECSCAN_UNKNOWN_TOKEN, CurrentToken.Line, ""); + } + + + + +bool scanner::reachedEOF() const { + return (CurrentToken.Type == TT_EOF); + } + + + + diff --git a/simgear/interpreter/string.cc b/simgear/interpreter/string.cc new file mode 100644 index 00000000..83b99f11 --- /dev/null +++ b/simgear/interpreter/string.cc @@ -0,0 +1,317 @@ +// ---------------------------------------------------------------------------- +// Description : String object +// ---------------------------------------------------------------------------- +// (c) Copyright 1999 by iXiONmedia, all rights reserved. +// ---------------------------------------------------------------------------- + + + + +#include +#include +#include +#include + + + + +using namespace std; +using namespace ixion; + + + + +// String utility functions --------------------------------------------------- +string ixion::findReplace(string const &target,string const &src,string const &dest) { + string result = target; + TIndex foundpos = string::npos; + TIndex n = src.size(); + while ((foundpos = result.find(src)) != string::npos) + result.replace(foundpos,n,dest); + return result; + } + + + + +string ixion::findReplace(string const &target,char* src,char *dest) { + string result = target; + TSize foundpos = string::npos; + TSize n = strlen(src); + while ((foundpos = result.find(src)) != string::npos) + result.replace(foundpos,n,dest); + return result; + } + + + + +string ixion::findReplace(string const &target,char src,char dest) { + string result = target; + string::iterator first = result.begin(),last = result.end(); + + while (first != last) { + if (*first == src) *first = dest; + first++; + } + return result; + } + + + + +string ixion::upper(string const &original) { + string temp(original); + string::iterator first = temp.begin(),last = temp.end(); + + while (first != last) { + *first = toupper(*first); + first++; + } + return temp; + } + + + + +string ixion::lower(string const &original) { + string temp(original); + string::iterator first = temp.begin(),last = temp.end(); + + while (first != last) { + *first = tolower(*first); + first++; + } + return temp; + } + + + + +string ixion::removeLeading(string const &original,char ch) { + string copy(original); + string::iterator first = copy.begin(), last = copy.end(); + + while (first != last && *first == ch) first++; + if (first != copy.begin()) copy.erase(copy.begin(),first); + return copy; + } + + + + +string ixion::removeTrailing(string const &original,char ch) { + string copy(original); + string::iterator first = copy.begin(), last = copy.end(); + + if (first != last) { + last--; + while (first != last && *last == ch) last--; + if (*last != ch) last++; + } + + if (last != copy.end()) copy.erase(last,copy.end()); + return copy; + } + + + + +string ixion::removeLeadingTrailing(string const &original,char ch) { + string copy(original); + string::iterator first = copy.begin(), last = copy.end(); + + while (first != last && *first == ch) first++; + if (first != copy.begin()) copy.erase(copy.begin(),first); + + first = copy.begin(); + last = copy.end(); + + if (first != last) { + last--; + while (first != last && *last == ch) last--; + if (*last != ch) last++; + } + + if (last != copy.end()) copy.erase(last,copy.end()); + return copy; + } + + + + +string ixion::parseCEscapes(string const &original) { + string result = ""; + string::const_iterator first = original.begin(),last = original.end(); + while (first != last) { + if (*first == '\\') { + first++; + if (first == last) { + result += '\\'; + break; + } + + #define GET_TEMP_STRING(LENGTH) \ + if (original.end()-first < LENGTH) \ + EXGEN_THROWINFO(EC_INVALIDPAR,"invalid escape sequence") \ + tempstring = string(first,first+LENGTH); \ + first += LENGTH; + + char value; + string tempstring; + switch (*first) { + case 'b': result += '\b'; first++; break; + case 'f': result += '\f'; first++; break; + case 'n': result += '\n'; first++; break; + case 't': result += '\t'; first++; break; + case 'v': result += '\v'; first++; break; + case 'X': + case 'x': first++; + GET_TEMP_STRING(2) + value = evalNumeral(tempstring,16); + result += value; + break; + case 'u': first++; + GET_TEMP_STRING(4) + value = evalNumeral(tempstring,16); + result += value; + break; + case '0': + GET_TEMP_STRING(3) + value = evalNumeral(tempstring,8); + result += value; + break; + default: result += *first++; + } + } + else result += *first++; + } + return result; + } + + + + +namespace { + TByte const B64_INVALID = 0xff; + TByte const B64_PAD = 0xfe; + char const B64_PAD_CHAR = '='; + char Base64EncodeTable[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + TByte Base64DecodeTable[] = { // based at 0 + // see test/invertmap.js on how to generate this table + B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID, + B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID, + B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID, + B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID, + B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID, + B64_INVALID,B64_INVALID,B64_INVALID,62,B64_INVALID,B64_INVALID,B64_INVALID,63,52,53,54, + 55,56,57,58,59,60,61,B64_INVALID,B64_INVALID,B64_INVALID,B64_PAD,B64_INVALID, + B64_INVALID,B64_INVALID,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18, + 19,20,21,22,23,24,25,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID, + B64_INVALID,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43, + 44,45,46,47,48,49,50,51,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID,B64_INVALID, + }; + + } + + + + +TSize ixion::getMaxBase64DecodedSize(TSize encoded) { + return ((encoded+3)/4)*3; + } + + + + +TSize ixion::base64decode(TByte *data,string const &base64) { + string::const_iterator first = base64.begin(),last = base64.end(); + + TByte *data_start = data; + + TUnsigned32 block; + TByte a,b,c,d; + + while (first != last) { + a = Base64DecodeTable[*(first++)]; + b = Base64DecodeTable[*(first++)]; + c = Base64DecodeTable[*(first++)]; + d = Base64DecodeTable[*(first++)]; + if (c == B64_PAD) { + block = a << 3*6 | b << 2*6; + *data++ = (block >> 16) & 0xff; + } + else if (d == B64_PAD) { + block = a << 3*6 | b << 2*6 | c << 1*6; + *data++ = (block >> 16) & 0xff; + *data++ = (block >> 8) & 0xff; + } + else { + block = a << 3*6 | b << 2*6 | c << 1*6 | d << 0*6; + *data++ = (block >> 16) & 0xff; + *data++ = (block >> 8) & 0xff; + *data++ = (block >> 0) & 0xff; + } + } + return data-data_start; + } + + + + +void ixion::base64encode(string &base64,TByte const *data,TSize size) { + base64.resize((size+2)/3*4); + + TUnsigned32 block; + TByte a,b,c,d; + + TByte const *end = data+size; + string::iterator first = base64.begin(); + while (data < end) + if (data+1 == end) { + block = data[0] << 16; + a = (block >> 3*6) & 0x3f; + b = (block >> 2*6) & 0x3f; + *first++ = Base64EncodeTable[a]; + *first++ = Base64EncodeTable[b]; + *first++ = B64_PAD_CHAR; + *first++ = B64_PAD_CHAR; + data++; + } + else if (data+2 == end) { + block = data[0] << 16 | data[1] << 8; + a = (block >> 3*6) & 0x3f; + b = (block >> 2*6) & 0x3f; + c = (block >> 1*6) & 0x3f; + *first++ = Base64EncodeTable[a]; + *first++ = Base64EncodeTable[b]; + *first++ = Base64EncodeTable[c]; + *first++ = B64_PAD_CHAR; + data += 2; + } + else { + block = data[0] << 16 | data[1] << 8 | data[2]; + a = (block >> 3*6) & 0x3f; + b = (block >> 2*6) & 0x3f; + c = (block >> 1*6) & 0x3f; + d = (block >> 0*6) & 0x3f; + *first++ = Base64EncodeTable[a]; + *first++ = Base64EncodeTable[b]; + *first++ = Base64EncodeTable[c]; + *first++ = Base64EncodeTable[d]; + data += 3; + } + } + + + + +// string_hash ---------------------------------------------------------------- +unsigned long ixion::string_hash::operator()(string const &str) const { + // the sgi stl uses the same hash algorithm + unsigned long h = 0; + FOREACH_CONST(first,str,string) + h = 5*h + *first; + return h; + }