diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 844469a..9d69120 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -9,133 +9,133 @@ namespace chaiscript { - typedef ModulePtr (*Create_Module_Func)(); + typedef ModulePtr (*Create_Module_Func)(); + /** + * Types of AST nodes available to the parser and eval + */ + class Token_Type { public: enum Type { Error, Int, Float, Id, Char, Str, Eol, Fun_Call, Inplace_Fun_Call, Arg_List, Variable, Equation, Var_Decl, + Comparison, Additive, Multiplicative, Array_Call, Dot_Access, Quoted_String, Single_Quoted_String, + Lambda, Block, Def, While, If, For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Map_Pair, Value_Range, + Inline_Range, Annotation, Try, Catch, Finally, Method, Attr_Decl, Shift, Equality, Bitwise_And, Bitwise_Xor, Bitwise_Or, + Logical_And, Logical_Or}; }; + + namespace + { /** - * Types of AST nodes available to the parser and eval + * Helper lookup to get the name of each node type */ - class Token_Type { public: enum Type { Error, Int, Float, Id, Char, Str, Eol, Fun_Call, Inplace_Fun_Call, Arg_List, Variable, Equation, Var_Decl, - Comparison, Additive, Multiplicative, Array_Call, Dot_Access, Quoted_String, Single_Quoted_String, - Lambda, Block, Def, While, If, For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Map_Pair, Value_Range, - Inline_Range, Annotation, Try, Catch, Finally, Method, Attr_Decl, Shift, Equality, Bitwise_And, Bitwise_Xor, Bitwise_Or, - Logical_And, Logical_Or}; }; + const char *token_type_to_string(int tokentype) { + const char *token_types[] = { "Internal Parser Error", "Int", "Float", "Id", "Char", "Str", "Eol", "Fun_Call", "Inplace_Fun_Call", "Arg_List", "Variable", "Equation", "Var_Decl", + "Comparison", "Additive", "Multiplicative", "Array_Call", "Dot_Access", "Quoted_String", "Single_Quoted_String", + "Lambda", "Block", "Def", "While", "If", "For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Map_Pair", "Value_Range", + "Inline_Range", "Annotation", "Try", "Catch", "Finally", "Method", "Attr_Decl", "Shift", "Equality", "Bitwise_And", "Bitwise_Xor", "Bitwise_Or", + "Logical_And", "Logical_Or"}; - namespace - { - /** - * Helper lookup to get the name of each node type - */ - const char *token_type_to_string(int tokentype) { - const char *token_types[] = { "Internal Parser Error", "Int", "Float", "Id", "Char", "Str", "Eol", "Fun_Call", "Inplace_Fun_Call", "Arg_List", "Variable", "Equation", "Var_Decl", - "Comparison", "Additive", "Multiplicative", "Array_Call", "Dot_Access", "Quoted_String", "Single_Quoted_String", - "Lambda", "Block", "Def", "While", "If", "For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Map_Pair", "Value_Range", - "Inline_Range", "Annotation", "Try", "Catch", "Finally", "Method", "Attr_Decl", "Shift", "Equality", "Bitwise_And", "Bitwise_Xor", "Bitwise_Or", - "Logical_And", "Logical_Or"}; + return token_types[tokentype]; + } + } - return token_types[tokentype]; - } + /** + * Convenience type for file positions + */ + struct File_Position { + int line; + int column; + + File_Position(int file_line, int file_column) + : line(file_line), column(file_column) { } + + File_Position() : line(0), column(0) { } + }; + + typedef boost::shared_ptr TokenPtr; + + /** + * The struct that doubles as both a parser token and an AST node + */ + struct Token { + std::string text; + int identifier; + const char *filename; + File_Position start, end; + bool is_cached; + Boxed_Value cached_value; + + std::vector children; + TokenPtr annotation; + + Token(const std::string &token_text, int id, const char *fname) : + text(token_text), identifier(id), filename(fname), is_cached(false) { } + + Token(const std::string &token_text, int id, const char *fname, int start_line, int start_col, int end_line, int end_col) : + text(token_text), identifier(id), filename(fname), is_cached(false) { + + start.line = start_line; + start.column = start_col; + end.line = end_line; + end.column = end_col; + } + }; + + /** + * Errors generated during parsing or evaluation + */ + struct Eval_Error : public std::runtime_error { + std::string reason; + File_Position start_position; + File_Position end_position; + const char *filename; + + Eval_Error(const std::string &why, const File_Position &where, const char *fname) : + std::runtime_error("Error: \"" + why + "\" " + + (std::string(fname) != "__EVAL__" ? ("in '" + std::string(fname) + "' ") : "during evaluation ") + + + "at (" + boost::lexical_cast(where.line) + ", " + + boost::lexical_cast(where.column) + ")"), + reason(why), start_position(where), end_position(where), filename(fname) + { } + + Eval_Error(const std::string &why, const TokenPtr &where) + : std::runtime_error("Error: \"" + why + "\" " + + (std::string(where->filename) != "__EVAL__" ? ("in '" + std::string(where->filename) + "' ") : "during evaluation ") + + "at (" + boost::lexical_cast(where->start.line) + ", " + + boost::lexical_cast(where->start.column) + ")"), + reason(why), start_position(where->start), end_position(where->end), filename(where->filename) { } - /** - * Convenience type for file positions - */ - struct File_Position { - int line; - int column; + virtual ~Eval_Error() throw() {} + }; - File_Position(int file_line, int file_column) - : line(file_line), column(file_column) { } + /** + * Errors generated when loading a file + */ + struct File_Not_Found_Error : public std::runtime_error { + File_Not_Found_Error(const std::string &filename) + : std::runtime_error("File Not Found: " + filename) + { } - File_Position() : line(0), column(0) { } - }; - - typedef boost::shared_ptr TokenPtr; - - /** - * The struct that doubles as both a parser token and an AST node - */ - struct Token { - std::string text; - int identifier; - const char *filename; - File_Position start, end; - bool is_cached; - Boxed_Value cached_value; - - std::vector children; - TokenPtr annotation; - - Token(const std::string &token_text, int id, const char *fname) : - text(token_text), identifier(id), filename(fname), is_cached(false) { } - - Token(const std::string &token_text, int id, const char *fname, int start_line, int start_col, int end_line, int end_col) : - text(token_text), identifier(id), filename(fname), is_cached(false) { - - start.line = start_line; - start.column = start_col; - end.line = end_line; - end.column = end_col; - } - }; - - /** - * Errors generated during parsing or evaluation - */ - struct Eval_Error : public std::runtime_error { - std::string reason; - File_Position start_position; - File_Position end_position; - const char *filename; - - Eval_Error(const std::string &why, const File_Position &where, const char *fname) : - std::runtime_error("Error: \"" + why + "\" " + - (std::string(fname) != "__EVAL__" ? ("in '" + std::string(fname) + "' ") : "during evaluation ") + - + "at (" + boost::lexical_cast(where.line) + ", " + - boost::lexical_cast(where.column) + ")"), - reason(why), start_position(where), end_position(where), filename(fname) - { } - - Eval_Error(const std::string &why, const TokenPtr &where) - : std::runtime_error("Error: \"" + why + "\" " + - (std::string(where->filename) != "__EVAL__" ? ("in '" + std::string(where->filename) + "' ") : "during evaluation ") + - "at (" + boost::lexical_cast(where->start.line) + ", " + - boost::lexical_cast(where->start.column) + ")"), - reason(why), start_position(where->start), end_position(where->end), filename(where->filename) { - } - - virtual ~Eval_Error() throw() {} - }; - - /** - * Errors generated when loading a file - */ - struct File_Not_Found_Error : public std::runtime_error { - File_Not_Found_Error(const std::string &filename) - : std::runtime_error("File Not Found: " + filename) - { } - - virtual ~File_Not_Found_Error() throw() {} - }; + virtual ~File_Not_Found_Error() throw() {} + }; - /** - * Special type for returned values - */ - struct Return_Value { - Boxed_Value retval; - TokenPtr location; + /** + * Special type for returned values + */ + struct Return_Value { + Boxed_Value retval; + TokenPtr location; - Return_Value(const Boxed_Value &return_value, const TokenPtr where) : retval(return_value), location(where) { } - }; + Return_Value(const Boxed_Value &return_value, const TokenPtr where) : retval(return_value), location(where) { } + }; - /** - * Special type indicating a call to 'break' - */ - struct Break_Loop { - TokenPtr location; + /** + * Special type indicating a call to 'break' + */ + struct Break_Loop { + TokenPtr location; - Break_Loop(const TokenPtr where) : location(where) { } - }; + Break_Loop(const TokenPtr where) : location(where) { } + }; } #endif /* _CHAISCRIPT_COMMON_HPP */ diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 5d66b1c..9677ea0 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -27,426 +27,426 @@ namespace chaiscript { - struct load_module_error : std::runtime_error + struct load_module_error : std::runtime_error + { + load_module_error(const std::string &reason) throw() + : std::runtime_error(reason) { - load_module_error(const std::string &reason) throw() - : std::runtime_error(reason) - { - } + } - virtual ~load_module_error() throw() - { - } - }; + virtual ~load_module_error() throw() + { + } + }; #ifdef _POSIX_VERSION - struct Loadable_Module + struct Loadable_Module + { + struct DLModule { - struct DLModule - { - DLModule(const std::string &t_filename) - : m_data(dlopen(t_filename.c_str(), RTLD_NOW)) - { - if (!m_data) - { - throw load_module_error(dlerror()); - } - } + DLModule(const std::string &t_filename) + : m_data(dlopen(t_filename.c_str(), RTLD_NOW)) + { + if (!m_data) + { + throw load_module_error(dlerror()); + } + } - DLModule(const DLModule &); // Explicitly unimplemented copy constructor - DLModule &operator=(const DLModule &); // Explicitly unimplemented assignment operator + DLModule(const DLModule &); // Explicitly unimplemented copy constructor + DLModule &operator=(const DLModule &); // Explicitly unimplemented assignment operator - ~DLModule() - { - dlclose(m_data); - } + ~DLModule() + { + dlclose(m_data); + } - void *m_data; - }; - - template - struct DLSym - { - DLSym(DLModule &t_mod, const std::string &t_symbol) - : m_symbol(reinterpret_cast(dlsym(t_mod.m_data, t_symbol.c_str()))) - { - if (!m_symbol) - { - throw load_module_error(dlerror()); - } - } - - T m_symbol; - }; - - Loadable_Module(const std::string &t_module_name, const std::string &t_filename) - : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name), - m_moduleptr(m_func.m_symbol()) - { - } - - DLModule m_dlmodule; - DLSym m_func; - ModulePtr m_moduleptr; + void *m_data; }; + + template + struct DLSym + { + DLSym(DLModule &t_mod, const std::string &t_symbol) + : m_symbol(reinterpret_cast(dlsym(t_mod.m_data, t_symbol.c_str()))) + { + if (!m_symbol) + { + throw load_module_error(dlerror()); + } + } + + T m_symbol; + }; + + Loadable_Module(const std::string &t_module_name, const std::string &t_filename) + : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name), + m_moduleptr(m_func.m_symbol()) + { + } + + DLModule m_dlmodule; + DLSym m_func; + ModulePtr m_moduleptr; + }; #else #ifdef WIN32 - struct Loadable_Module + struct Loadable_Module + { + template + static std::wstring towstring(const T &str) { - template - static std::wstring towstring(const T &str) - { - return std::wstring(str.begin(), str.end()); - } + return std::wstring(str.begin(), str.end()); + } - template - static std::string tostring(const T &str) - { - return std::string(str.begin(), str.end()); - } + template + static std::string tostring(const T &str) + { + return std::string(str.begin(), str.end()); + } #ifdef _UNICODE - template - static std::wstring toproperstring(const T &str) - { - return towstring(str); - } -#else - template - static std::string toproperstring(const T &str) - { - return tostring(str); - } -#endif - - static std::string GetErrorMessage(DWORD err) - { -#ifdef _UNICODE - typedef LPWSTR StringType; - std::wstring retval = L"Unknown Error"; -#else - typedef LPSTR StringType; - std::string retval = "Unknown Error"; -#endif - StringType lpMsgBuf = 0; - - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - err, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (StringType)&lpMsgBuf, - 0, NULL ); - - if (lpMsgBuf) - { - retval = lpMsgBuf; - } - - LocalFree(lpMsgBuf); - return tostring(retval); - } - - struct DLModule - { - DLModule(const std::string &t_filename) - : m_data(LoadLibrary(toproperstring(t_filename).c_str())) - { - if (!m_data) - { - throw load_module_error(GetErrorMessage(GetLastError())); - } - } - - ~DLModule() - { - FreeLibrary(m_data); - } - - HMODULE m_data; - }; - - template - struct DLSym - { - DLSym(DLModule &t_mod, const std::string &t_symbol) - : m_symbol(reinterpret_cast(GetProcAddress(t_mod.m_data, t_symbol.c_str()))) - { - if (!m_symbol) - { - throw load_module_error(GetErrorMessage(GetLastError())); - } - } - - T m_symbol; - }; - - Loadable_Module(const std::string &t_module_name, const std::string &t_filename) - : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name), - m_moduleptr(m_func.m_symbol()) - { - } - - DLModule m_dlmodule; - DLSym m_func; - ModulePtr m_moduleptr; - }; - -#else - struct Loadable_Module + template + static std::wstring toproperstring(const T &str) { - Loadable_Module(const std::string &, const std::string &) + return towstring(str); + } +#else + template + static std::string toproperstring(const T &str) + { + return tostring(str); + } +#endif + + static std::string GetErrorMessage(DWORD err) + { +#ifdef _UNICODE + typedef LPWSTR StringType; + std::wstring retval = L"Unknown Error"; +#else + typedef LPSTR StringType; + std::string retval = "Unknown Error"; +#endif + StringType lpMsgBuf = 0; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (StringType)&lpMsgBuf, + 0, NULL ); + + if (lpMsgBuf) { - throw load_module_error("Loadable module support not available for your platform"); + retval = lpMsgBuf; } - ModulePtr get() - { - throw load_module_error("Loadable module support not available for your platform"); - } + LocalFree(lpMsgBuf); + return tostring(retval); + } + + struct DLModule + { + DLModule(const std::string &t_filename) + : m_data(LoadLibrary(toproperstring(t_filename).c_str())) + { + if (!m_data) + { + throw load_module_error(GetErrorMessage(GetLastError())); + } + } + + ~DLModule() + { + FreeLibrary(m_data); + } + + HMODULE m_data; }; + + template + struct DLSym + { + DLSym(DLModule &t_mod, const std::string &t_symbol) + : m_symbol(reinterpret_cast(GetProcAddress(t_mod.m_data, t_symbol.c_str()))) + { + if (!m_symbol) + { + throw load_module_error(GetErrorMessage(GetLastError())); + } + } + + T m_symbol; + }; + + Loadable_Module(const std::string &t_module_name, const std::string &t_filename) + : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name), + m_moduleptr(m_func.m_symbol()) + { + } + + DLModule m_dlmodule; + DLSym m_func; + ModulePtr m_moduleptr; + }; + +#else + struct Loadable_Module + { + Loadable_Module(const std::string &, const std::string &) + { + throw load_module_error("Loadable module support not available for your platform"); + } + + ModulePtr get() + { + throw load_module_error("Loadable module support not available for your platform"); + } + }; #endif #endif - typedef boost::shared_ptr Loadable_Module_Ptr; + typedef boost::shared_ptr Loadable_Module_Ptr; - template - class ChaiScript_System { + template + class ChaiScript_System { #ifndef CHAISCRIPT_NO_THREADS - mutable boost::shared_mutex mutex; - mutable boost::recursive_mutex use_mutex; + mutable boost::shared_mutex mutex; + mutable boost::recursive_mutex use_mutex; #endif - std::set loaded_files; - std::map loaded_modules; - std::set active_loaded_modules; + std::set loaded_files; + std::map loaded_modules; + std::set active_loaded_modules; - std::vector modulepaths; - std::vector usepaths; + std::vector modulepaths; + std::vector usepaths; - Eval_Engine engine; + Eval_Engine engine; - /** - * Evaluates the given string in by parsing it and running the results through the evaluator - */ - Boxed_Value do_eval(const std::string &input, const std::string &filename = "__EVAL__", bool internal = false) { - ChaiScript_Parser parser; + /** + * Evaluates the given string in by parsing it and running the results through the evaluator + */ + Boxed_Value do_eval(const std::string &input, const std::string &filename = "__EVAL__", bool internal = false) { + ChaiScript_Parser parser; - if (!internal) - { - engine.sync_cache(); + if (!internal) + { + engine.sync_cache(); + } + + //debug_print(tokens); + Boxed_Value value; + + // Keep a cache of all loaded filenames and use the char * from this cache to pass + // to the parser. This is so that the parser does not have the overhead of passing + // around and copying strings + // + if (filename != "__EVAL__") + { +#ifndef CHAISCRIPT_NO_THREADS + boost::unique_lock l(mutex); +#endif + loaded_files.insert(filename); + try { + if (parser.parse(input, loaded_files.find(filename)->c_str())) { +#ifndef CHAISCRIPT_NO_THREADS + l.unlock(); +#endif + //parser.show_match_stack(); + value = eval_token(engine, parser.ast()); } - - //debug_print(tokens); - Boxed_Value value; - - // Keep a cache of all loaded filenames and use the char * from this cache to pass - // to the parser. This is so that the parser does not have the overhead of passing - // around and copying strings - // - if (filename != "__EVAL__") - { + } + catch (const Return_Value &rv) { + value = rv.retval; + } + } else { + try { #ifndef CHAISCRIPT_NO_THREADS - boost::unique_lock l(mutex); + boost::shared_lock l(mutex); #endif - loaded_files.insert(filename); - try { - if (parser.parse(input, loaded_files.find(filename)->c_str())) { + const char *fname = loaded_files.find("__EVAL__")->c_str(); #ifndef CHAISCRIPT_NO_THREADS - l.unlock(); -#endif - //parser.show_match_stack(); - value = eval_token(engine, parser.ast()); - } - } - catch (const Return_Value &rv) { - value = rv.retval; - } - } else { - try { -#ifndef CHAISCRIPT_NO_THREADS - boost::shared_lock l(mutex); -#endif - const char *fname = loaded_files.find("__EVAL__")->c_str(); -#ifndef CHAISCRIPT_NO_THREADS - l.unlock(); + l.unlock(); #endif - if (parser.parse(input, fname)) { - //parser.show_match_stack(); - value = eval_token(engine, parser.ast()); - } - } - catch (const Return_Value &rv) { - value = rv.retval; - } - } - - if (!internal) - { - engine.sync_cache(); - } - - return value; + if (parser.parse(input, fname)) { + //parser.show_match_stack(); + value = eval_token(engine, parser.ast()); + } } - - /** - * Evaluates the given boxed string, used during eval() inside of a script - */ - const Boxed_Value internal_eval(const std::string &e) { - return do_eval(e, "__EVAL__", true); + catch (const Return_Value &rv) { + value = rv.retval; } + } - void use(const std::string &filename) + if (!internal) { - for (size_t i = 0; i < usepaths.size(); ++i) - { + engine.sync_cache(); + } - try { + return value; + } - const std::string appendedpath = usepaths[i] + filename; + /** + * Evaluates the given boxed string, used during eval() inside of a script + */ + const Boxed_Value internal_eval(const std::string &e) { + return do_eval(e, "__EVAL__", true); + } + + void use(const std::string &filename) + { + for (size_t i = 0; i < usepaths.size(); ++i) + { + + try { + + const std::string appendedpath = usepaths[i] + filename; #ifndef CHAISCRIPT_NO_THREADS - boost::lock_guard l(use_mutex); - boost::shared_lock l2(mutex); + boost::lock_guard l(use_mutex); + boost::shared_lock l2(mutex); #endif - if (loaded_files.count(appendedpath) == 0) + if (loaded_files.count(appendedpath) == 0) { #ifndef CHAISCRIPT_NO_THREADS l2.unlock(); #endif eval_file(appendedpath); } else { - engine.sync_cache(); - } - } catch (const File_Not_Found_Error &) { - if (i == usepaths.size() - 1) + engine.sync_cache(); + } + } catch (const File_Not_Found_Error &) { + if (i == usepaths.size() - 1) { throw File_Not_Found_Error(filename); } - // failed to load, try the next path - } - + // failed to load, try the next path } + + } + } + + + public: + ChaiScript_System(const std::vector &t_modulepaths = std::vector(), + const std::vector &t_usepaths = std::vector()) + : modulepaths(t_modulepaths), usepaths(t_usepaths) { + if (modulepaths.empty()) + { + modulepaths.push_back(""); } - - public: - ChaiScript_System(const std::vector &t_modulepaths = std::vector(), - const std::vector &t_usepaths = std::vector()) - : modulepaths(t_modulepaths), usepaths(t_usepaths) { - if (modulepaths.empty()) - { - modulepaths.push_back(""); - } - - if (usepaths.empty()) - { - usepaths.push_back(""); - } - - loaded_files.insert("__EVAL__"); // Make sure the default name is already registered - build_eval_system(); + if (usepaths.empty()) + { + usepaths.push_back(""); } - /** - * Adds a shared object, that can be used by all threads, to the system - */ - ChaiScript_System &add_global_const(const Boxed_Value &bv, const std::string &name) - { - engine.add_global_const(bv, name); - return *this; - } + loaded_files.insert("__EVAL__"); // Make sure the default name is already registered + build_eval_system(); + } - struct State - { - std::set loaded_files; - typename Eval_Engine::State engine_state; - std::set active_loaded_modules; - }; + /** + * Adds a shared object, that can be used by all threads, to the system + */ + ChaiScript_System &add_global_const(const Boxed_Value &bv, const std::string &name) + { + engine.add_global_const(bv, name); + return *this; + } - /** - * Returns a state object that represents the current - * set of loaded files, the set of global variables and - * the set of initialized functions - */ - State get_state() - { + struct State + { + std::set loaded_files; + typename Eval_Engine::State engine_state; + std::set active_loaded_modules; + }; + + /** + * Returns a state object that represents the current + * set of loaded files, the set of global variables and + * the set of initialized functions + */ + State get_state() + { #ifndef CHAISCRIPT_NO_THREADS - boost::lock_guard l(use_mutex); - boost::shared_lock l2(mutex); + boost::lock_guard l(use_mutex); + boost::shared_lock l2(mutex); #endif - State s; - s.loaded_files = loaded_files; - s.engine_state = engine.get_state(); - s.active_loaded_modules = active_loaded_modules; - return s; - } + State s; + s.loaded_files = loaded_files; + s.engine_state = engine.get_state(); + s.active_loaded_modules = active_loaded_modules; + return s; + } - /** - * Restores the state from a saved State object. - */ - void set_state(const State &t_state) - { + /** + * Restores the state from a saved State object. + */ + void set_state(const State &t_state) + { #ifndef CHAISCRIPT_NO_THREADS - boost::lock_guard l(use_mutex); - boost::shared_lock l2(mutex); + boost::lock_guard l(use_mutex); + boost::shared_lock l2(mutex); #endif - loaded_files = t_state.loaded_files; - active_loaded_modules = t_state.active_loaded_modules; - engine.set_state(t_state.engine_state); - } + loaded_files = t_state.loaded_files; + active_loaded_modules = t_state.active_loaded_modules; + engine.set_state(t_state.engine_state); + } - /** - * Adds an object to the system: type, function, object - */ - template - ChaiScript_System &add(const T &t, const std::string &name) - { - engine.add(t, name); - return *this; - } + /** + * Adds an object to the system: type, function, object + */ + template + ChaiScript_System &add(const T &t, const std::string &name) + { + engine.add(t, name); + return *this; + } - /** - * Adds a module object to the system - */ - ChaiScript_System &add(const ModulePtr &p) + /** + * Adds a module object to the system + */ + ChaiScript_System &add(const ModulePtr &p) + { + p->apply(*this, this->get_eval_engine()); + return *this; + } + + /** + * Load a dynamic library containing a chaiscript module + */ + void load_module(const std::string &t_module_name) + { + std::vector prefixes; + prefixes.push_back("lib"); + prefixes.push_back(""); + + std::vector postfixes; + postfixes.push_back(".dll"); + postfixes.push_back(".so"); + postfixes.push_back(""); + + for (size_t i = 0; i < modulepaths.size(); ++i) { - p->apply(*this, this->get_eval_engine()); - return *this; - } - - /** - * Load a dynamic library containing a chaiscript module - */ - void load_module(const std::string &t_module_name) - { - std::vector prefixes; - prefixes.push_back("lib"); - prefixes.push_back(""); - - std::vector postfixes; - postfixes.push_back(".dll"); - postfixes.push_back(".so"); - postfixes.push_back(""); - - for (size_t i = 0; i < modulepaths.size(); ++i) + for (size_t j = 0; j < prefixes.size(); ++j) { - for (size_t j = 0; j < prefixes.size(); ++j) - { - for (size_t k = 0; k < postfixes.size(); ++k) + for (size_t k = 0; k < postfixes.size(); ++k) { try { std::string name = modulepaths[i] + prefixes[j] + t_module_name + postfixes[k]; @@ -456,176 +456,177 @@ namespace chaiscript // Try next set } } - } } - - throw load_module_error("Unable to find module: " + t_module_name); } - /** - * Load a dynamic library and provide the file name to load it from - */ - void load_module(const std::string &t_module_name, const std::string &t_filename) - { + throw load_module_error("Unable to find module: " + t_module_name); + } + + /** + * Load a dynamic library and provide the file name to load it from + */ + void load_module(const std::string &t_module_name, const std::string &t_filename) + { #ifndef CHAISCRIPT_NO_THREADS - boost::lock_guard l(use_mutex); + boost::lock_guard l(use_mutex); #endif - if (loaded_modules.count(t_module_name) == 0) - { - Loadable_Module_Ptr lm(new Loadable_Module(t_module_name, t_filename)); - loaded_modules[t_module_name] = lm; - active_loaded_modules.insert(t_module_name); - add(lm->m_moduleptr); - } else if (active_loaded_modules.count(t_module_name) == 0) { - active_loaded_modules.insert(t_module_name); - add(loaded_modules[t_module_name]->m_moduleptr); - } else { - engine.sync_cache(); - } - } - - - /** - * Helper for calling script code as if it were native C++ code - * example: - * boost::function f = build_functor(chai, "func(x, y){x+y}"); - * \return a boost::function representing the passed in script - * \param[in] script Script code to build a function from - */ - template - boost::function functor(const std::string &script) - { - return chaiscript::functor(eval(script)); - } - - /** - * Evaluate a string via eval method - */ - Boxed_Value operator()(const std::string &script) + if (loaded_modules.count(t_module_name) == 0) { - return do_eval(script); - } + Loadable_Module_Ptr lm(new Loadable_Module(t_module_name, t_filename)); + loaded_modules[t_module_name] = lm; + active_loaded_modules.insert(t_module_name); + add(lm->m_moduleptr); + } else if (active_loaded_modules.count(t_module_name) == 0) { + active_loaded_modules.insert(t_module_name); + add(loaded_modules[t_module_name]->m_moduleptr); + } else { + engine.sync_cache(); + } + } - /** - * Returns the current evaluation engine - */ - Eval_Engine &get_eval_engine() { - return engine; - } + /** + * Helper for calling script code as if it were native C++ code + * example: + * boost::function f = build_functor(chai, "func(x, y){x+y}"); + * \return a boost::function representing the passed in script + * \param[in] script Script code to build a function from + */ + template + boost::function functor(const std::string &script) + { + return chaiscript::functor(eval(script)); + } - /** - * Prints the contents of an AST node, including its children, recursively - */ - void debug_print(TokenPtr t, std::string prepend = "") { - std::cout << prepend << "(" << token_type_to_string(t->identifier) << ") " << t->text << " : " << t->start.line << ", " << t->start.column << std::endl; - for (unsigned int j = 0; j < t->children.size(); ++j) { - debug_print(t->children[j], prepend + " "); - } - } - - /** - * Helper function for loading a file - */ - std::string load_file(const std::string &filename) { - std::ifstream infile (filename.c_str(), std::ios::in | std::ios::ate); - - if (!infile.is_open()) { - throw File_Not_Found_Error(filename); - } - - std::streampos size = infile.tellg(); - infile.seekg(0, std::ios::beg); - - std::vector v(size); - infile.read(&v[0], size); - - std::string ret_val (v.empty() ? std::string() : std::string (v.begin(), v.end()).c_str()); - - return ret_val; - } - - /** - * Builds all the requirements for ChaiScript, including its evaluator and a run of its prelude. - */ - void build_eval_system() { - using namespace bootstrap; - engine.add_reserved_word("def"); - engine.add_reserved_word("fun"); - engine.add_reserved_word("while"); - engine.add_reserved_word("for"); - engine.add_reserved_word("if"); - engine.add_reserved_word("else"); - engine.add_reserved_word("&&"); - engine.add_reserved_word("||"); - engine.add_reserved_word(","); - engine.add_reserved_word(":="); - engine.add_reserved_word("var"); - engine.add_reserved_word("return"); - engine.add_reserved_word("break"); - engine.add_reserved_word("true"); - engine.add_reserved_word("false"); - engine.add_reserved_word("_"); - - add(Bootstrap::bootstrap()); - - engine.add(fun(&Eval_Engine::dump_system, boost::ref(engine)), "dump_system"); - engine.add(fun(&Eval_Engine::dump_object, boost::ref(engine)), "dump_object"); - engine.add(fun(&Eval_Engine::is_type, boost::ref(engine)), "is_type"); - engine.add(fun(&Eval_Engine::type_name, boost::ref(engine)), "type_name"); - engine.add(fun(&Eval_Engine::function_exists, boost::ref(engine)), "function_exists"); - - engine.add(fun(&Eval_Engine::get_type_name, boost::ref(engine)), "name"); + /** + * Evaluate a string via eval method + */ + Boxed_Value operator()(const std::string &script) + { + return do_eval(script); + } - typedef void (ChaiScript_System::*load_mod_1)(const std::string&); - typedef void (ChaiScript_System::*load_mod_2)(const std::string&, const std::string&); + /** + * Returns the current evaluation engine + */ + Eval_Engine &get_eval_engine() { + return engine; + } - engine.add(fun(static_cast(&ChaiScript_System::load_module), this), "load_module"); - engine.add(fun(static_cast(&ChaiScript_System::load_module), this), "load_module"); + /** + * Prints the contents of an AST node, including its children, recursively + */ + void debug_print(TokenPtr t, std::string prepend = "") { + std::cout << prepend << "(" << token_type_to_string(t->identifier) << ") " + << t->text << " : " << t->start.line << ", " << t->start.column << std::endl; + for (unsigned int j = 0; j < t->children.size(); ++j) { + debug_print(t->children[j], prepend + " "); + } + } + + /** + * Helper function for loading a file + */ + std::string load_file(const std::string &filename) { + std::ifstream infile (filename.c_str(), std::ios::in | std::ios::ate); + + if (!infile.is_open()) { + throw File_Not_Found_Error(filename); + } + + std::streampos size = infile.tellg(); + infile.seekg(0, std::ios::beg); + + std::vector v(size); + infile.read(&v[0], size); + + std::string ret_val (v.empty() ? std::string() : std::string (v.begin(), v.end()).c_str()); + + return ret_val; + } + + /** + * Builds all the requirements for ChaiScript, including its evaluator and a run of its prelude. + */ + void build_eval_system() { + using namespace bootstrap; + engine.add_reserved_word("def"); + engine.add_reserved_word("fun"); + engine.add_reserved_word("while"); + engine.add_reserved_word("for"); + engine.add_reserved_word("if"); + engine.add_reserved_word("else"); + engine.add_reserved_word("&&"); + engine.add_reserved_word("||"); + engine.add_reserved_word(","); + engine.add_reserved_word(":="); + engine.add_reserved_word("var"); + engine.add_reserved_word("return"); + engine.add_reserved_word("break"); + engine.add_reserved_word("true"); + engine.add_reserved_word("false"); + engine.add_reserved_word("_"); + + add(Bootstrap::bootstrap()); + + engine.add(fun(&Eval_Engine::dump_system, boost::ref(engine)), "dump_system"); + engine.add(fun(&Eval_Engine::dump_object, boost::ref(engine)), "dump_object"); + engine.add(fun(&Eval_Engine::is_type, boost::ref(engine)), "is_type"); + engine.add(fun(&Eval_Engine::type_name, boost::ref(engine)), "type_name"); + engine.add(fun(&Eval_Engine::function_exists, boost::ref(engine)), "function_exists"); + + engine.add(fun(&Eval_Engine::get_type_name, boost::ref(engine)), "name"); - add(vector_type >("Vector")); - add(string_type("string")); - add(map_type >("Map")); - add(pair_type >("Pair")); + typedef void (ChaiScript_System::*load_mod_1)(const std::string&); + typedef void (ChaiScript_System::*load_mod_2)(const std::string&, const std::string&); - engine.add(fun(&ChaiScript_System::use, this), "use"); - engine.add(fun(&ChaiScript_System::internal_eval, this), "eval"); + engine.add(fun(static_cast(&ChaiScript_System::load_module), this), "load_module"); + engine.add(fun(static_cast(&ChaiScript_System::load_module), this), "load_module"); - do_eval(chaiscript_prelude, "standard prelude"); - } + add(vector_type >("Vector")); + add(string_type("string")); + add(map_type >("Map")); + add(pair_type >("Pair")); - template - T eval(const std::string &input) - { - return boxed_cast(do_eval(input)); - } + engine.add(fun(&ChaiScript_System::use, this), "use"); + engine.add(fun(&ChaiScript_System::internal_eval, this), "eval"); - Boxed_Value eval(const std::string &input) - { - return do_eval(input); - } - /** - * Loads the file specified by filename, evaluates it, and returns the result - */ - Boxed_Value eval_file(const std::string &filename) { - return do_eval(load_file(filename), filename); - } + do_eval(chaiscript_prelude, "standard prelude"); + } - /** - * Loads the file specified by filename, evaluates it, and returns the as the specified type - */ - template - T eval_file(const std::string &filename) { - return boxed_cast(do_eval(load_file(filename), filename)); - } - }; + template + T eval(const std::string &input) + { + return boxed_cast(do_eval(input)); + } - typedef ChaiScript_System ChaiScript; + Boxed_Value eval(const std::string &input) + { + return do_eval(input); + } + + /** + * Loads the file specified by filename, evaluates it, and returns the result + */ + Boxed_Value eval_file(const std::string &filename) { + return do_eval(load_file(filename), filename); + } + + /** + * Loads the file specified by filename, evaluates it, and returns the as the specified type + */ + template + T eval_file(const std::string &filename) { + return boxed_cast(do_eval(load_file(filename), filename)); + } + }; + + typedef ChaiScript_System ChaiScript; } #endif /* CHAISCRIPT_ENGINE_HPP_ */ diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index c528089..dc48fe9 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -13,1129 +13,1131 @@ namespace chaiscript { - /** - * Helper function that will set up the scope around a function call, including handling the named function parameters - */ - template - const Boxed_Value eval_function (Eval_System &ss, const TokenPtr &node, const std::vector ¶m_names, const std::vector &vals) { - ss.new_scope(); + /** + * Helper function that will set up the scope around a function call, including handling the named function parameters + */ + template + const Boxed_Value eval_function (Eval_System &ss, const TokenPtr &node, const std::vector ¶m_names, const std::vector &vals) { + ss.new_scope(); - for (unsigned int i = 0; i < param_names.size(); ++i) { - ss.add_object(param_names[i], vals[i]); - } - - try { - Boxed_Value retval(eval_token(ss, node)); - ss.pop_scope(); - return retval; - } catch (const Return_Value &rv) { - ss.pop_scope(); - return rv.retval; - } catch (...) { - ss.pop_scope(); - throw; - } + for (unsigned int i = 0; i < param_names.size(); ++i) { + ss.add_object(param_names[i], vals[i]); } - /** - * Evaluates the top-level file node - */ - template - Boxed_Value eval_file(Eval_System &ss, const TokenPtr &node) { - const unsigned int size = node->children.size(); - for (unsigned int i = 0; i < size; ++i) { - const Boxed_Value &retval = eval_token(ss, node->children[i]); - if (i + 1 == size) - { - return retval; - } - } - return Boxed_Value(); + try { + Boxed_Value retval(eval_token(ss, node)); + ss.pop_scope(); + return retval; + } catch (const Return_Value &rv) { + ss.pop_scope(); + return rv.retval; + } catch (...) { + ss.pop_scope(); + throw; } + } - template - void cache_const(Eval_System &/*ss*/, const TokenPtr &node, const Boxed_Value &value) { - node->cached_value = value; - node->is_cached = true; + /** + * Evaluates the top-level file node + */ + template + Boxed_Value eval_file(Eval_System &ss, const TokenPtr &node) { + const unsigned int size = node->children.size(); + for (unsigned int i = 0; i < size; ++i) { + const Boxed_Value &retval = eval_token(ss, node->children[i]); + if (i + 1 == size) + { + return retval; + } } - /** - * Evaluates a variable or function name identifier - */ - template - Boxed_Value eval_id(Eval_System &ss, const TokenPtr &node) { + return Boxed_Value(); + } + + template + void cache_const(Eval_System &/*ss*/, const TokenPtr &node, const Boxed_Value &value) { + node->cached_value = value; + node->is_cached = true; + } + /** + * Evaluates a variable or function name identifier + */ + template + Boxed_Value eval_id(Eval_System &ss, const TokenPtr &node) { + + if (node->text == "true") { + //return const_var(true); + if (!node->is_cached) { + cache_const(ss, node, const_var(true)); + } + return node->cached_value; + } + else if (node->text == "false") { + //return const_var(false); + if (!node->is_cached) { + cache_const(ss, node, const_var(false)); + } + return node->cached_value; + } + else if (node->text == "Infinity") { + //return const_var(false); + if (!node->is_cached) { + cache_const(ss, node, const_var(std::numeric_limits::infinity())); + } + return node->cached_value; + } + else if (node->text == "NaN") { + //return const_var(false); + if (!node->is_cached) { + cache_const(ss, node, const_var(std::numeric_limits::quiet_NaN())); + } + return node->cached_value; + } + else { + try { + return ss.get_object(node->text); + } + catch (std::exception &) { + throw Eval_Error("Can not find object: " + node->text, node); + } + } + } + + /** + * Evaluates a floating point number + */ + template + Boxed_Value eval_float(Eval_System &ss, const TokenPtr &node) { + //return const_var(double(atof(node->text.c_str()))); + + if (!node->is_cached) { + cache_const(ss, node, const_var(double(atof(node->text.c_str())))); + } + return node->cached_value; + } + + /** + * Evaluates an integer + */ + template + Boxed_Value eval_int(Eval_System &ss, const TokenPtr &node) { + //return const_var(atoi(node->text.c_str())); + + if (!node->is_cached) { + cache_const(ss, node, const_var(int(atoi(node->text.c_str())))); + } + return node->cached_value; + } + + /** + * Evaluates a quoted string + */ + template + Boxed_Value eval_quoted_string(Eval_System &ss, const TokenPtr &node) { + //return const_var(node->text); + + /* + if ((node->text.size() > 0) && (node->text[0] == '$')) { + node->text.erase(0, 1); + Param_List_Builder plb; + plb << node->text; + + return ss.call_function("eval", plb); + } + */ + if (!node->is_cached) { + cache_const(ss, node, const_var(node->text)); + } + return node->cached_value; + } + + /** + * Evaluates a char group + */ + template + Boxed_Value eval_single_quoted_string(Eval_System &ss, const TokenPtr &node) { + if (!node->is_cached) { + cache_const(ss, node, const_var(char(node->text[0]))); + } + return node->cached_value; + } + + /** + * Evaluates a string of equations in reverse order so that the right-most side has precedence + */ + template + Boxed_Value eval_equation(Eval_System &ss, const TokenPtr &node) { + int i; + Boxed_Value retval = eval_token(ss, node->children.back()); + if (node->children.size() > 1) { + for (i = node->children.size()-3; i >= 0; i -= 2) { + if (node->children[i+1]->text == "=") { + Boxed_Value lhs = eval_token(ss, node->children[i]); + + try { + if (lhs.is_undef()) + { + retval = ss.call_function("clone", retval); + } - if (node->text == "true") { - //return const_var(true); - if (!node->is_cached) { - cache_const(ss, node, const_var(true)); - } - return node->cached_value; - } - else if (node->text == "false") { - //return const_var(false); - if (!node->is_cached) { - cache_const(ss, node, const_var(false)); - } - return node->cached_value; - } - else if (node->text == "Infinity") { - //return const_var(false); - if (!node->is_cached) { - cache_const(ss, node, const_var(std::numeric_limits::infinity())); - } - return node->cached_value; - } - else if (node->text == "NaN") { - //return const_var(false); - if (!node->is_cached) { - cache_const(ss, node, const_var(std::numeric_limits::quiet_NaN())); - } - return node->cached_value; - } - else { try { - return ss.get_object(node->text); - } - catch (std::exception &) { - throw Eval_Error("Can not find object: " + node->text, node); - } - } - } - - /** - * Evaluates a floating point number - */ - template - Boxed_Value eval_float(Eval_System &ss, const TokenPtr &node) { - //return const_var(double(atof(node->text.c_str()))); - - if (!node->is_cached) { - cache_const(ss, node, const_var(double(atof(node->text.c_str())))); - } - return node->cached_value; - } - - /** - * Evaluates an integer - */ - template - Boxed_Value eval_int(Eval_System &ss, const TokenPtr &node) { - //return const_var(atoi(node->text.c_str())); - - if (!node->is_cached) { - cache_const(ss, node, const_var(int(atoi(node->text.c_str())))); - } - return node->cached_value; - } - - /** - * Evaluates a quoted string - */ - template - Boxed_Value eval_quoted_string(Eval_System &ss, const TokenPtr &node) { - //return const_var(node->text); - - /* - if ((node->text.size() > 0) && (node->text[0] == '$')) { - node->text.erase(0, 1); - Param_List_Builder plb; - plb << node->text; - - return ss.call_function("eval", plb); - } - */ - if (!node->is_cached) { - cache_const(ss, node, const_var(node->text)); - } - return node->cached_value; - } - - /** - * Evaluates a char group - */ - template - Boxed_Value eval_single_quoted_string(Eval_System &ss, const TokenPtr &node) { - if (!node->is_cached) { - cache_const(ss, node, const_var(char(node->text[0]))); - } - return node->cached_value; - } - - /** - * Evaluates a string of equations in reverse order so that the right-most side has precedence - */ - template - Boxed_Value eval_equation(Eval_System &ss, const TokenPtr &node) { - int i; - Boxed_Value retval = eval_token(ss, node->children.back()); - if (node->children.size() > 1) { - for (i = node->children.size()-3; i >= 0; i -= 2) { - if (node->children[i+1]->text == "=") { - Boxed_Value lhs = eval_token(ss, node->children[i]); - - try { - if (lhs.is_undef()) - { - retval = ss.call_function("clone", retval); - } - - try { - retval = ss.call_function(node->children[i+1]->text, lhs, retval); - } - catch(const dispatch_error &){ - throw Eval_Error(std::string("Mismatched types in equation") + (lhs.is_const()?", lhs is const.":"."), node->children[i+1]); - } - } - catch(const dispatch_error &){ - throw Eval_Error("Can not clone right hand side of equation", node->children[i+1]); - } - } - else if (node->children[i+1]->text == ":=") { - Boxed_Value lhs = eval_token(ss, node->children[i]); - if (lhs.is_undef() || type_match(lhs, retval)) { - lhs.assign(retval); - } - else { - throw Eval_Error("Mismatched types in equation", node->children[i+1]); - } - } - else { - try { - retval = ss.call_function(node->children[i+1]->text, eval_token(ss, node->children[i]), retval); - } - catch(const dispatch_error &){ - throw Eval_Error("Can not find appropriate '" + node->children[i+1]->text + "'", node->children[i+1]); - } - } - } - } - return retval; - } - - /** - * Evaluates a variable declaration - */ - template - Boxed_Value eval_var_decl(Eval_System &ss, const TokenPtr &node) { - try { - ss.add_object(node->children[0]->text, Boxed_Value()); - } - catch (reserved_word_error &) { - throw Eval_Error("Reserved word used as variable '" + node->children[0]->text + "'", node); - } - return ss.get_object(node->children[0]->text); - } - - /** - * Evaluates an attribute declaration - */ - template - Boxed_Value eval_attr_decl(Eval_System &ss, const TokenPtr &node) { - try { - ss.add(fun(boost::function(boost::bind(&Dynamic_Object_Attribute::func, node->children[0]->text, - node->children[1]->text, _1))), node->children[1]->text); - - } - catch (reserved_word_error &) { - throw Eval_Error("Reserved word used as attribute '" + node->children[1]->text + "'", node); - } - return Boxed_Value(); - } - - /** - * Evaluates binary boolean operators. Respects short-circuiting rules. - */ - template - Boxed_Value eval_logical(Eval_System &ss, const TokenPtr &node) { - unsigned int i; - - Boxed_Value retval(eval_token(ss, node->children[0])); - if (node->children.size() > 1) { - for (i = 1; i < node->children.size(); i += 2) { - bool lhs; - try { - lhs = boxed_cast(retval); - } - catch (const bad_boxed_cast &) { - throw Eval_Error("Condition not boolean", node); - } - if (node->children[i]->text == "&&") { - if (lhs) { - retval = eval_token(ss, node->children[i+1]); - } - else { - retval = Boxed_Value(false); - } - } - else if (node->children[i]->text == "||") { - if (lhs) { - retval = Boxed_Value(true); - } - else { - retval = eval_token(ss, node->children[i+1]); - } - } - } - } - return retval; - } - - /** - * Evaluates comparison, additions, and multiplications and their relatives - */ - template - Boxed_Value eval_comp_add_mul(Eval_System &ss, const TokenPtr &node) { - unsigned int i; - - Boxed_Value retval = eval_token(ss, node->children[0]); - for (i = 1; i < node->children.size(); i += 2) { - try { - retval = ss.call_function(node->children[i]->text, retval, eval_token(ss, node->children[i + 1])); + retval = ss.call_function(node->children[i+1]->text, lhs, retval); } catch(const dispatch_error &){ - throw Eval_Error("Can not find appropriate '" + node->children[i]->text + "'", node->children[i]); + throw Eval_Error(std::string("Mismatched types in equation") + (lhs.is_const()?", lhs is const.":"."), node->children[i+1]); } + } + catch(const dispatch_error &){ + throw Eval_Error("Can not clone right hand side of equation", node->children[i+1]); + } } - - return retval; - } - - /** - * Evaluates an array lookup - */ - template - Boxed_Value eval_array_call(Eval_System &ss, const TokenPtr &node) { - unsigned int i; - - Boxed_Value retval = eval_token(ss, node->children[0]); - for (i = 1; i < node->children.size(); ++i) { - try { - retval = ss.call_function("[]", retval, eval_token(ss, node->children[i])); - } - catch(std::out_of_range &) { - throw Eval_Error("Out of bounds exception", node); - } - catch(const dispatch_error &){ - throw Eval_Error("Can not find appropriate array lookup '[]' " + node->children[i]->text, node->children[i]); - } - } - - return retval; - } - - /** - * Evaluates any unary prefix - */ - template - Boxed_Value eval_prefix(Eval_System &ss, const TokenPtr &node) { - try { - return ss.call_function(node->children[0]->text, eval_token(ss, node->children[1])); - } - catch(std::exception &){ - throw Eval_Error("Can not find appropriate unary '" + node->children[0]->text + "'", node->children[0]); - } - } - - /** - * Evaluates (and generates) an inline array initialization - */ - template - Boxed_Value eval_inline_array(Eval_System &ss, const TokenPtr &node) { - unsigned int i; - - try { - Boxed_Value retval = ss.call_function("Vector"); - if (node->children.size() > 0) { - for (i = 0; i < node->children[0]->children.size(); ++i) { - try { - ss.call_function("push_back", retval, eval_token(ss, node->children[0]->children[i])); - } - catch (const dispatch_error &) { - throw Eval_Error("Can not find appropriate 'push_back'", node->children[0]->children[i]); - } - } - } - - return retval; - } - catch (const dispatch_error &) { - throw Eval_Error("Can not find appropriate 'Vector()'", node); - } - - } - - /** - * Evaluates (and generates) an inline range initialization - */ - template - Boxed_Value eval_inline_range(Eval_System &ss, const TokenPtr &node) { - try { - return ss.call_function("generate_range", - eval_token(ss, node->children[0]->children[0]->children[0]), - eval_token(ss, node->children[0]->children[0]->children[1])); - } - catch (const dispatch_error &) { - throw Eval_Error("Unable to generate range vector", node); - } - } - - /** - * Evaluates (and generates) an inline map initialization - */ - template - Boxed_Value eval_inline_map(Eval_System &ss, const TokenPtr &node) { - unsigned int i; - - try { - Boxed_Value retval = ss.call_function("Map"); - for (i = 0; i < node->children[0]->children.size(); ++i) { - try { - Boxed_Value slot - = ss.call_function("[]", retval, eval_token(ss, node->children[0]->children[i]->children[0])); - ss.call_function("=", slot, eval_token(ss, node->children[0]->children[i]->children[1])); - } - catch (const dispatch_error &) { - throw Eval_Error("Can not find appropriate '=' for map init", node->children[0]->children[i]); - } - } - return retval; - } - catch (const dispatch_error &) { - throw Eval_Error("Can not find appropriate 'Map()'", node); - } - } - - /** - * Evaluates a function call, starting with its arguments. Handles resetting the scope to the previous one after the call. - */ - template - Boxed_Value eval_fun_call(Eval_System &ss, const TokenPtr &node) { - Param_List_Builder plb; - Dispatch_Engine::Stack prev_stack = ss.get_stack(); - Dispatch_Engine::Stack new_stack = ss.new_stack(); - unsigned int i; - - if ((node->children.size() > 1) && (node->children[1]->identifier == Token_Type::Arg_List)) { - for (i = 0; i < node->children[1]->children.size(); ++i) { - plb << eval_token(ss, node->children[1]->children[i]); - } - } - - try { - Boxed_Value fn = eval_token(ss, node->children[0]); - - try { - ss.set_stack(new_stack); - const Boxed_Value &retval = (*boxed_cast(fn))(plb); - ss.set_stack(prev_stack); - return retval; - } - catch(const dispatch_error &e){ - ss.set_stack(prev_stack); - throw Eval_Error(std::string(e.what()) + " with function '" + node->children[0]->text + "'", node->children[0]); - } - catch(Return_Value &rv) { - ss.set_stack(prev_stack); - return rv.retval; - } - catch(...) { - ss.set_stack(prev_stack); - throw; - } - } - catch(Eval_Error &ee) { - ss.set_stack(prev_stack); - throw Eval_Error(ee.reason, node->children[0]); - } - - } - - /** - * Evaluates a function call, starting with its arguments. Does NOT change scope. - */ - template - Boxed_Value eval_inplace_fun_call(Eval_System &ss, const TokenPtr &node) { - Param_List_Builder plb; - unsigned int i; - - if ((node->children.size() > 1) && (node->children[1]->identifier == Token_Type::Arg_List)) { - for (i = 0; i < node->children[1]->children.size(); ++i) { - plb << eval_token(ss, node->children[1]->children[i]); - } - } - - try { - Boxed_Value fn = eval_token(ss, node->children[0]); - - try { - return (*boxed_cast(fn))(plb); - } - catch(const dispatch_error &e){ - throw Eval_Error(std::string(e.what()) + " with function '" + node->children[0]->text + "'", node->children[0]); - } - catch(Return_Value &rv) { - return rv.retval; - } - catch(...) { - throw; - } - } - catch(Eval_Error &ee) { - throw Eval_Error(ee.reason, node->children[0]); - } - - } - - /** - * Evaluates a method/attributes invocation - */ - template - Boxed_Value eval_dot_access(Eval_System &ss, const TokenPtr &node) { - Dispatch_Engine::Stack prev_stack = ss.get_stack(); - Dispatch_Engine::Stack new_stack = ss.new_stack(); - unsigned int i, j; - - //todo: Please extract a single way of doing function calls between this and eval_fun_call - - Boxed_Value retval(eval_token(ss, node->children[0])); - if (node->children.size() > 1) { - for (i = 2; i < node->children.size(); i+=2) { - Param_List_Builder plb; - plb << retval; - - if (node->children[i]->children.size() > 1) { - for (j = 0; j < node->children[i]->children[1]->children.size(); ++j) { - plb << eval_token(ss, node->children[i]->children[1]->children[j]); - } - } - - std::string fun_name; - std::vector > funs; - if (node->children[i]->identifier == Token_Type::Fun_Call) { - fun_name = node->children[i]->children[0]->text; - } - else { - fun_name = node->children[i]->text; - } - - try { - ss.set_stack(new_stack); - retval = ss.call_function(fun_name, plb); - ss.set_stack(prev_stack); - } - catch(const dispatch_error &e){ - ss.set_stack(prev_stack); - throw Eval_Error(std::string(e.what()), node->children[i]); - } - catch(Return_Value &rv) { - ss.set_stack(prev_stack); - retval = rv.retval; - } - catch(...) { - ss.set_stack(prev_stack); - throw; - } - } - } - - return retval; - } - - /** - * Evaluates an if/elseif/else block - */ - template - Boxed_Value eval_try(Eval_System &ss, const TokenPtr &node) { - Boxed_Value retval; - - ss.new_scope(); - try { - retval = eval_token(ss, node->children[0]); - } - catch (const Eval_Error &) { - if (node->children.back()->identifier == Token_Type::Finally) { - eval_token(ss, node->children.back()->children[0]); - } - ss.pop_scope(); - throw; - } - catch (const std::exception &e) { - Boxed_Value except = Boxed_Value(boost::ref(e)); - - unsigned int end_point = node->children.size(); - if (node->children.back()->identifier == Token_Type::Finally) { - end_point = node->children.size() - 1; - } - for (unsigned int i = 1; i < end_point; ++i) { - TokenPtr catch_block = node->children[i]; - - if (catch_block->children.size() == 1) { - //No variable capture, no guards - retval = eval_token(ss, catch_block->children[0]); - break; - } - else if (catch_block->children.size() == 2) { - //Variable capture, no guards - ss.add_object(catch_block->children[0]->text, except); - retval = eval_token(ss, catch_block->children[1]); - break; - } - else if (catch_block->children.size() == 3) { - //Variable capture, no guards - ss.add_object(catch_block->children[0]->text, except); - - bool guard; - try { - guard = boxed_cast(eval_token(ss, catch_block->children[1])); - } catch (const bad_boxed_cast &) { - if (node->children.back()->identifier == Token_Type::Finally) { - eval_token(ss, node->children.back()->children[0]); - } - ss.pop_scope(); - throw Eval_Error("Guard condition not boolean", catch_block->children[1]); - } - if (guard) { - retval = eval_token(ss, catch_block->children[2]); - break; - } - } - else { - if (node->children.back()->identifier == Token_Type::Finally) { - eval_token(ss, node->children.back()->children[0]); - } - ss.pop_scope(); - throw Eval_Error("Internal error: catch block size unrecognized", catch_block); - } - } - } - catch (Boxed_Value &bv) { - Boxed_Value except = bv; - for (unsigned int i = 1; i < node->children.size(); ++i) { - TokenPtr catch_block = node->children[i]; - - if (catch_block->children.size() == 1) { - //No variable capture, no guards - retval = eval_token(ss, catch_block->children[0]); - break; - } - else if (catch_block->children.size() == 2) { - //Variable capture, no guards - ss.add_object(catch_block->children[0]->text, except); - retval = eval_token(ss, catch_block->children[1]); - break; - } - else if (catch_block->children.size() == 3) { - //Variable capture, no guards - ss.add_object(catch_block->children[0]->text, except); - - bool guard; - try { - guard = boxed_cast(eval_token(ss, catch_block->children[1])); - } catch (const bad_boxed_cast &) { - if (node->children.back()->identifier == Token_Type::Finally) { - eval_token(ss, node->children.back()->children[0]); - } - ss.pop_scope(); - throw Eval_Error("Guard condition not boolean", catch_block->children[1]); - } - if (guard) { - retval = eval_token(ss, catch_block->children[2]); - break; - } - } - else { - if (node->children.back()->identifier == Token_Type::Finally) { - eval_token(ss, node->children.back()->children[0]); - } - ss.pop_scope(); - throw Eval_Error("Internal error: catch block size unrecognized", catch_block); - } - } - } - catch (...) { - if (node->children.back()->identifier == Token_Type::Finally) { - eval_token(ss, node->children.back()->children[0]); - } - ss.pop_scope(); - throw; - } - - if (node->children.back()->identifier == Token_Type::Finally) { - retval = eval_token(ss, node->children.back()->children[0]); - } - - ss.pop_scope(); - - return retval; - } - - /** - * Evaluates an if/elseif/else block - */ - template - Boxed_Value eval_if(Eval_System &ss, const TokenPtr &node) { - unsigned int i; - - bool cond; - try { - cond = boxed_cast(eval_token(ss, node->children[0])); - } - catch (const bad_boxed_cast &) { - throw Eval_Error("If condition not boolean", node->children[0]); - } - if (cond) { - return eval_token(ss, node->children[1]); + else if (node->children[i+1]->text == ":=") { + Boxed_Value lhs = eval_token(ss, node->children[i]); + if (lhs.is_undef() || type_match(lhs, retval)) { + lhs.assign(retval); + } + else { + throw Eval_Error("Mismatched types in equation", node->children[i+1]); + } } else { - if (node->children.size() > 2) { - i = 2; - while ((!cond) && (i < node->children.size())) { - if (node->children[i]->text == "else") { - return eval_token(ss, node->children[i+1]); - } - else if (node->children[i]->text == "else if") { - try { - cond = boxed_cast(eval_token(ss, node->children[i+1])); - } - catch (const bad_boxed_cast &) { - throw Eval_Error("'else if' condition not boolean", node->children[i+1]); - } - if (cond) { - return eval_token(ss, node->children[i+2]); - } - } - i = i + 3; - } - } + try { + retval = ss.call_function(node->children[i+1]->text, eval_token(ss, node->children[i]), retval); + } + catch(const dispatch_error &){ + throw Eval_Error("Can not find appropriate '" + node->children[i+1]->text + "'", node->children[i+1]); + } } - - return Boxed_Value(false); + } } + return retval; + } - /** - * Evaluates a while block - */ - template - Boxed_Value eval_while(Eval_System &ss, const TokenPtr &node) { - bool cond; + /** + * Evaluates a variable declaration + */ + template + Boxed_Value eval_var_decl(Eval_System &ss, const TokenPtr &node) { + try { + ss.add_object(node->children[0]->text, Boxed_Value()); + } + catch (reserved_word_error &) { + throw Eval_Error("Reserved word used as variable '" + node->children[0]->text + "'", node); + } + return ss.get_object(node->children[0]->text); + } - ss.new_scope(); + /** + * Evaluates an attribute declaration + */ + template + Boxed_Value eval_attr_decl(Eval_System &ss, const TokenPtr &node) { + try { + ss.add(fun(boost::function(boost::bind(&Dynamic_Object_Attribute::func, node->children[0]->text, + node->children[1]->text, _1))), node->children[1]->text); + } + catch (reserved_word_error &) { + throw Eval_Error("Reserved word used as attribute '" + node->children[1]->text + "'", node); + } + return Boxed_Value(); + } + + /** + * Evaluates binary boolean operators. Respects short-circuiting rules. + */ + template + Boxed_Value eval_logical(Eval_System &ss, const TokenPtr &node) { + unsigned int i; + + Boxed_Value retval(eval_token(ss, node->children[0])); + if (node->children.size() > 1) { + for (i = 1; i < node->children.size(); i += 2) { + bool lhs; try { - cond = boxed_cast(eval_token(ss, node->children[0])); + lhs = boxed_cast(retval); } catch (const bad_boxed_cast &) { - ss.pop_scope(); - throw Eval_Error("While condition not boolean", node->children[0]); + throw Eval_Error("Condition not boolean", node); } - while (cond) { - try { - eval_token(ss, node->children[1]); - try { - cond = boxed_cast(eval_token(ss, node->children[0])); - } - catch (const bad_boxed_cast &) { - ss.pop_scope(); - throw Eval_Error("While condition not boolean", node->children[0]); - } - } - catch (Break_Loop &) { - cond = false; - } + if (node->children[i]->text == "&&") { + if (lhs) { + retval = eval_token(ss, node->children[i+1]); + } + else { + retval = Boxed_Value(false); + } } - ss.pop_scope(); - return Boxed_Value(); + else if (node->children[i]->text == "||") { + if (lhs) { + retval = Boxed_Value(true); + } + else { + retval = eval_token(ss, node->children[i+1]); + } + } + } + } + return retval; + } + + /** + * Evaluates comparison, additions, and multiplications and their relatives + */ + template + Boxed_Value eval_comp_add_mul(Eval_System &ss, const TokenPtr &node) { + unsigned int i; + + Boxed_Value retval = eval_token(ss, node->children[0]); + for (i = 1; i < node->children.size(); i += 2) { + try { + retval = ss.call_function(node->children[i]->text, retval, eval_token(ss, node->children[i + 1])); + } + catch(const dispatch_error &){ + throw Eval_Error("Can not find appropriate '" + node->children[i]->text + "'", node->children[i]); + } } - /** - * Evaluates a for block, including the for's conditions, from left to right - */ - template - Boxed_Value eval_for(Eval_System &ss, const TokenPtr &node) { - bool cond; + return retval; + } - ss.new_scope(); + /** + * Evaluates an array lookup + */ + template + Boxed_Value eval_array_call(Eval_System &ss, const TokenPtr &node) { + unsigned int i; + + Boxed_Value retval = eval_token(ss, node->children[0]); + for (i = 1; i < node->children.size(); ++i) { + try { + retval = ss.call_function("[]", retval, eval_token(ss, node->children[i])); + } + catch(std::out_of_range &) { + throw Eval_Error("Out of bounds exception", node); + } + catch(const dispatch_error &){ + throw Eval_Error("Can not find appropriate array lookup '[]' " + node->children[i]->text, node->children[i]); + } + } + + return retval; + } + + /** + * Evaluates any unary prefix + */ + template + Boxed_Value eval_prefix(Eval_System &ss, const TokenPtr &node) { + try { + return ss.call_function(node->children[0]->text, eval_token(ss, node->children[1])); + } + catch(std::exception &){ + throw Eval_Error("Can not find appropriate unary '" + node->children[0]->text + "'", node->children[0]); + } + } + + /** + * Evaluates (and generates) an inline array initialization + */ + template + Boxed_Value eval_inline_array(Eval_System &ss, const TokenPtr &node) { + unsigned int i; + + try { + Boxed_Value retval = ss.call_function("Vector"); + if (node->children.size() > 0) { + for (i = 0; i < node->children[0]->children.size(); ++i) { + try { + ss.call_function("push_back", retval, eval_token(ss, node->children[0]->children[i])); + } + catch (const dispatch_error &) { + throw Eval_Error("Can not find appropriate 'push_back'", node->children[0]->children[i]); + } + } + } + + return retval; + } + catch (const dispatch_error &) { + throw Eval_Error("Can not find appropriate 'Vector()'", node); + } + + } + + /** + * Evaluates (and generates) an inline range initialization + */ + template + Boxed_Value eval_inline_range(Eval_System &ss, const TokenPtr &node) { + try { + return ss.call_function("generate_range", + eval_token(ss, node->children[0]->children[0]->children[0]), + eval_token(ss, node->children[0]->children[0]->children[1])); + } + catch (const dispatch_error &) { + throw Eval_Error("Unable to generate range vector", node); + } + } + + /** + * Evaluates (and generates) an inline map initialization + */ + template + Boxed_Value eval_inline_map(Eval_System &ss, const TokenPtr &node) { + unsigned int i; + + try { + Boxed_Value retval = ss.call_function("Map"); + for (i = 0; i < node->children[0]->children.size(); ++i) { + try { + Boxed_Value slot + = ss.call_function("[]", retval, eval_token(ss, node->children[0]->children[i]->children[0])); + ss.call_function("=", slot, eval_token(ss, node->children[0]->children[i]->children[1])); + } + catch (const dispatch_error &) { + throw Eval_Error("Can not find appropriate '=' for map init", node->children[0]->children[i]); + } + } + return retval; + } + catch (const dispatch_error &) { + throw Eval_Error("Can not find appropriate 'Map()'", node); + } + } + + /** + * Evaluates a function call, starting with its arguments. Handles resetting the scope to the previous one after the call. + */ + template + Boxed_Value eval_fun_call(Eval_System &ss, const TokenPtr &node) { + Param_List_Builder plb; + Dispatch_Engine::Stack prev_stack = ss.get_stack(); + Dispatch_Engine::Stack new_stack = ss.new_stack(); + unsigned int i; + + if ((node->children.size() > 1) && (node->children[1]->identifier == Token_Type::Arg_List)) { + for (i = 0; i < node->children[1]->children.size(); ++i) { + plb << eval_token(ss, node->children[1]->children[i]); + } + } + + try { + Boxed_Value fn = eval_token(ss, node->children[0]); + + try { + ss.set_stack(new_stack); + const Boxed_Value &retval = (*boxed_cast(fn))(plb); + ss.set_stack(prev_stack); + return retval; + } + catch(const dispatch_error &e){ + ss.set_stack(prev_stack); + throw Eval_Error(std::string(e.what()) + " with function '" + node->children[0]->text + "'", node->children[0]); + } + catch(Return_Value &rv) { + ss.set_stack(prev_stack); + return rv.retval; + } + catch(...) { + ss.set_stack(prev_stack); + throw; + } + } + catch(Eval_Error &ee) { + ss.set_stack(prev_stack); + throw Eval_Error(ee.reason, node->children[0]); + } + + } + + /** + * Evaluates a function call, starting with its arguments. Does NOT change scope. + */ + template + Boxed_Value eval_inplace_fun_call(Eval_System &ss, const TokenPtr &node) { + Param_List_Builder plb; + unsigned int i; + + if ((node->children.size() > 1) && (node->children[1]->identifier == Token_Type::Arg_List)) { + for (i = 0; i < node->children[1]->children.size(); ++i) { + plb << eval_token(ss, node->children[1]->children[i]); + } + } + + try { + Boxed_Value fn = eval_token(ss, node->children[0]); + + try { + return (*boxed_cast(fn))(plb); + } + catch(const dispatch_error &e){ + throw Eval_Error(std::string(e.what()) + " with function '" + node->children[0]->text + "'", node->children[0]); + } + catch(Return_Value &rv) { + return rv.retval; + } + catch(...) { + throw; + } + } + catch(Eval_Error &ee) { + throw Eval_Error(ee.reason, node->children[0]); + } + + } + + /** + * Evaluates a method/attributes invocation + */ + template + Boxed_Value eval_dot_access(Eval_System &ss, const TokenPtr &node) { + Dispatch_Engine::Stack prev_stack = ss.get_stack(); + Dispatch_Engine::Stack new_stack = ss.new_stack(); + unsigned int i, j; + + //todo: Please extract a single way of doing function calls between this and eval_fun_call + + Boxed_Value retval(eval_token(ss, node->children[0])); + if (node->children.size() > 1) { + for (i = 2; i < node->children.size(); i+=2) { + Param_List_Builder plb; + plb << retval; + + if (node->children[i]->children.size() > 1) { + for (j = 0; j < node->children[i]->children[1]->children.size(); ++j) { + plb << eval_token(ss, node->children[i]->children[1]->children[j]); + } + } + + std::string fun_name; + std::vector > funs; + if (node->children[i]->identifier == Token_Type::Fun_Call) { + fun_name = node->children[i]->children[0]->text; + } + else { + fun_name = node->children[i]->text; + } try { - if (node->children.size() == 4) { - eval_token(ss, node->children[0]); - cond = boxed_cast(eval_token(ss, node->children[1])); - } - else { - cond = boxed_cast(eval_token(ss, node->children[0])); - } + ss.set_stack(new_stack); + retval = ss.call_function(fun_name, plb); + ss.set_stack(prev_stack); } - catch (const bad_boxed_cast &) { + catch(const dispatch_error &e){ + ss.set_stack(prev_stack); + throw Eval_Error(std::string(e.what()), node->children[i]); + } + catch(Return_Value &rv) { + ss.set_stack(prev_stack); + retval = rv.retval; + } + catch(...) { + ss.set_stack(prev_stack); + throw; + } + } + } + + return retval; + } + + /** + * Evaluates an if/elseif/else block + */ + template + Boxed_Value eval_try(Eval_System &ss, const TokenPtr &node) { + Boxed_Value retval; + + ss.new_scope(); + try { + retval = eval_token(ss, node->children[0]); + } + catch (const Eval_Error &) { + if (node->children.back()->identifier == Token_Type::Finally) { + eval_token(ss, node->children.back()->children[0]); + } + ss.pop_scope(); + throw; + } + catch (const std::exception &e) { + Boxed_Value except = Boxed_Value(boost::ref(e)); + + unsigned int end_point = node->children.size(); + if (node->children.back()->identifier == Token_Type::Finally) { + end_point = node->children.size() - 1; + } + for (unsigned int i = 1; i < end_point; ++i) { + TokenPtr catch_block = node->children[i]; + + if (catch_block->children.size() == 1) { + //No variable capture, no guards + retval = eval_token(ss, catch_block->children[0]); + break; + } + else if (catch_block->children.size() == 2) { + //Variable capture, no guards + ss.add_object(catch_block->children[0]->text, except); + retval = eval_token(ss, catch_block->children[1]); + break; + } + else if (catch_block->children.size() == 3) { + //Variable capture, no guards + ss.add_object(catch_block->children[0]->text, except); + + bool guard; + try { + guard = boxed_cast(eval_token(ss, catch_block->children[1])); + } catch (const bad_boxed_cast &) { + if (node->children.back()->identifier == Token_Type::Finally) { + eval_token(ss, node->children.back()->children[0]); + } ss.pop_scope(); - throw Eval_Error("For condition not boolean", node); + throw Eval_Error("Guard condition not boolean", catch_block->children[1]); + } + if (guard) { + retval = eval_token(ss, catch_block->children[2]); + break; + } } - while (cond) { + else { + if (node->children.back()->identifier == Token_Type::Finally) { + eval_token(ss, node->children.back()->children[0]); + } + ss.pop_scope(); + throw Eval_Error("Internal error: catch block size unrecognized", catch_block); + } + } + } + catch (Boxed_Value &bv) { + Boxed_Value except = bv; + for (unsigned int i = 1; i < node->children.size(); ++i) { + TokenPtr catch_block = node->children[i]; + + if (catch_block->children.size() == 1) { + //No variable capture, no guards + retval = eval_token(ss, catch_block->children[0]); + break; + } + else if (catch_block->children.size() == 2) { + //Variable capture, no guards + ss.add_object(catch_block->children[0]->text, except); + retval = eval_token(ss, catch_block->children[1]); + break; + } + else if (catch_block->children.size() == 3) { + //Variable capture, no guards + ss.add_object(catch_block->children[0]->text, except); + + bool guard; + try { + guard = boxed_cast(eval_token(ss, catch_block->children[1])); + } catch (const bad_boxed_cast &) { + if (node->children.back()->identifier == Token_Type::Finally) { + eval_token(ss, node->children.back()->children[0]); + } + ss.pop_scope(); + throw Eval_Error("Guard condition not boolean", catch_block->children[1]); + } + if (guard) { + retval = eval_token(ss, catch_block->children[2]); + break; + } + } + else { + if (node->children.back()->identifier == Token_Type::Finally) { + eval_token(ss, node->children.back()->children[0]); + } + ss.pop_scope(); + throw Eval_Error("Internal error: catch block size unrecognized", catch_block); + } + } + } + catch (...) { + if (node->children.back()->identifier == Token_Type::Finally) { + eval_token(ss, node->children.back()->children[0]); + } + ss.pop_scope(); + throw; + } + + if (node->children.back()->identifier == Token_Type::Finally) { + retval = eval_token(ss, node->children.back()->children[0]); + } + + ss.pop_scope(); + + return retval; + } + + /** + * Evaluates an if/elseif/else block + */ + template + Boxed_Value eval_if(Eval_System &ss, const TokenPtr &node) { + unsigned int i; + + bool cond; + try { + cond = boxed_cast(eval_token(ss, node->children[0])); + } + catch (const bad_boxed_cast &) { + throw Eval_Error("If condition not boolean", node->children[0]); + } + if (cond) { + return eval_token(ss, node->children[1]); + } + else { + if (node->children.size() > 2) { + i = 2; + while ((!cond) && (i < node->children.size())) { + if (node->children[i]->text == "else") { + return eval_token(ss, node->children[i+1]); + } + else if (node->children[i]->text == "else if") { try { - if (node->children.size() == 4) { - eval_token(ss, node->children[3]); - eval_token(ss, node->children[2]); - cond = boxed_cast(eval_token(ss, node->children[1])); - } - else { - eval_token(ss, node->children[2]); - eval_token(ss, node->children[1]); - cond = boxed_cast(eval_token(ss, node->children[0])); - } + cond = boxed_cast(eval_token(ss, node->children[i+1])); } catch (const bad_boxed_cast &) { - ss.pop_scope(); - throw Eval_Error("For condition not boolean", node); + throw Eval_Error("'else if' condition not boolean", node->children[i+1]); } - catch (Break_Loop &) { - cond = false; + if (cond) { + return eval_token(ss, node->children[i+2]); } + } + i = i + 3; } - ss.pop_scope(); - return Boxed_Value(); + } } - /** - * Evaluates a function definition - */ - template - Boxed_Value eval_def(Eval_System &ss, const TokenPtr &node) { - unsigned int i; + return Boxed_Value(false); + } - std::vector param_names; - std::string annotation = node->annotation?node->annotation->text:""; - boost::shared_ptr guard; - size_t numparams = 0; - std::string function_name = node->children[0]->text; - TokenPtr guardnode; + /** + * Evaluates a while block + */ + template + Boxed_Value eval_while(Eval_System &ss, const TokenPtr &node) { + bool cond; - if ((node->children.size() > 2) && (node->children[1]->identifier == Token_Type::Arg_List)) { - numparams = node->children[1]->children.size(); - for (i = 0; i < numparams; ++i) { - param_names.push_back(node->children[1]->children[i]->text); - } - - if (node->children.size() > 3) { - guardnode = node->children[2]; - } - } - else { - //no parameters - numparams = 0; - - if (node->children.size() > 2) { - guardnode = node->children[1]; - } - } - - if (guardnode) { - guard = boost::shared_ptr - (new Dynamic_Proxy_Function(boost::bind(&eval_function, - boost::ref(ss), guardnode, - param_names, _1), numparams)); - } + ss.new_scope(); + try { + cond = boxed_cast(eval_token(ss, node->children[0])); + } + catch (const bad_boxed_cast &) { + ss.pop_scope(); + throw Eval_Error("While condition not boolean", node->children[0]); + } + while (cond) { + try { + eval_token(ss, node->children[1]); try { - ss.add(Proxy_Function - (new Dynamic_Proxy_Function(boost::bind(&eval_function, - boost::ref(ss), node->children.back(), - param_names, _1), numparams, - annotation, guard)), function_name); + cond = boxed_cast(eval_token(ss, node->children[0])); } - catch (reserved_word_error &) { - throw Eval_Error("Reserved word used as function name '" + function_name + "'", node); + catch (const bad_boxed_cast &) { + ss.pop_scope(); + throw Eval_Error("While condition not boolean", node->children[0]); } - return Boxed_Value(); + } + catch (Break_Loop &) { + cond = false; + } } + ss.pop_scope(); + return Boxed_Value(); + } - /** - * Evaluates a function definition - */ - template - Boxed_Value eval_def_method(Eval_System &ss, const TokenPtr &node) { - //TODO: Merge with eval_def cleanly someday? - unsigned int i; + /** + * Evaluates a for block, including the for's conditions, from left to right + */ + template + Boxed_Value eval_for(Eval_System &ss, const TokenPtr &node) { + bool cond; - std::vector param_names; - std::string annotation = node->annotation?node->annotation->text:""; - boost::shared_ptr guard; - size_t numparams; - std::string class_name = node->children[0]->text; - std::string function_name = node->children[1]->text; - TokenPtr guardnode; + ss.new_scope(); - //The first param of a method is always the implied this ptr. - param_names.push_back("this"); - - if ((node->children.size() > 3) && (node->children[2]->identifier == Token_Type::Arg_List)) { - for (i = 0; i < node->children[2]->children.size(); ++i) { - param_names.push_back(node->children[2]->children[i]->text); - } - - if (node->children.size() > 4) { - guardnode = node->children[3]; - } + try { + if (node->children.size() == 4) { + eval_token(ss, node->children[0]); + cond = boxed_cast(eval_token(ss, node->children[1])); + } + else { + cond = boxed_cast(eval_token(ss, node->children[0])); + } + } + catch (const bad_boxed_cast &) { + ss.pop_scope(); + throw Eval_Error("For condition not boolean", node); + } + while (cond) { + try { + if (node->children.size() == 4) { + eval_token(ss, node->children[3]); + eval_token(ss, node->children[2]); + cond = boxed_cast(eval_token(ss, node->children[1])); } else { - //no parameters - - if (node->children.size() > 3) { - guardnode = node->children[2]; - } + eval_token(ss, node->children[2]); + eval_token(ss, node->children[1]); + cond = boxed_cast(eval_token(ss, node->children[0])); } - - numparams = param_names.size(); - - if (guardnode) { - guard = boost::shared_ptr - (new Dynamic_Proxy_Function(boost::bind(&eval_function, - boost::ref(ss), guardnode, - param_names, _1), numparams)); - } - - try { - if (function_name == class_name) { - ss.add(Proxy_Function - (new Dynamic_Object_Constructor(class_name, Proxy_Function(new Dynamic_Proxy_Function(boost::bind(&eval_function, - boost::ref(ss), node->children.back(), - param_names, _1), numparams, - annotation, guard)))), function_name); - - } - else { - boost::optional ti; - try { - ti = ss.get_type(class_name); - } catch (const std::range_error &) { - // No biggie, the type name is just not known - } - ss.add(Proxy_Function - (new Dynamic_Object_Function(class_name, Proxy_Function(new Dynamic_Proxy_Function(boost::bind(&eval_function, - boost::ref(ss), node->children.back(), - param_names, _1), numparams, - annotation, guard)), ti)), function_name); - - } - } - catch (reserved_word_error &) { - throw Eval_Error("Reserved word used as method name '" + function_name + "'", node); - } - return Boxed_Value(); - } - - /** - * Evaluates a lambda (anonymous function) - */ - template - Boxed_Value eval_lambda(Eval_System &ss, const TokenPtr &node) { - unsigned int i; - - std::vector param_names; - size_t numparams = 0; - - if ((node->children.size() > 0) && (node->children[0]->identifier == Token_Type::Arg_List)) { - numparams = node->children[0]->children.size(); - for (i = 0; i < numparams; ++i) { - param_names.push_back(node->children[0]->children[i]->text); - } - - } - else { - //no parameters - numparams = 0; - } - - return Boxed_Value(Proxy_Function( - new Dynamic_Proxy_Function( - boost::bind(&eval_function, boost::ref(ss), node->children.back(), param_names, _1), numparams))); - } - - /** - * Evaluates a scoped block. Handles resetting the scope after the block has completed. - */ - template - Boxed_Value eval_block(Eval_System &ss, const TokenPtr &node) { - unsigned int i; - unsigned int num_children = node->children.size(); - - ss.new_scope(); - for (i = 0; i < num_children; ++i) { - try { - const Boxed_Value &retval = eval_token(ss, node->children[i]); - - if (i + 1 == num_children) - { - ss.pop_scope(); - return retval; - } - } - catch (const chaiscript::Return_Value &/*rv*/) { - ss.pop_scope(); - throw; - } - catch (...) { - ss.pop_scope(); - throw; - } - } - + } + catch (const bad_boxed_cast &) { ss.pop_scope(); - return Boxed_Value(); + throw Eval_Error("For condition not boolean", node); + } + catch (Break_Loop &) { + cond = false; + } + } + ss.pop_scope(); + return Boxed_Value(); + } + + /** + * Evaluates a function definition + */ + template + Boxed_Value eval_def(Eval_System &ss, const TokenPtr &node) { + unsigned int i; + + std::vector param_names; + std::string annotation = node->annotation?node->annotation->text:""; + boost::shared_ptr guard; + size_t numparams = 0; + std::string function_name = node->children[0]->text; + TokenPtr guardnode; + + if ((node->children.size() > 2) && (node->children[1]->identifier == Token_Type::Arg_List)) { + numparams = node->children[1]->children.size(); + for (i = 0; i < numparams; ++i) { + param_names.push_back(node->children[1]->children[i]->text); + } + + if (node->children.size() > 3) { + guardnode = node->children[2]; + } + } + else { + //no parameters + numparams = 0; + + if (node->children.size() > 2) { + guardnode = node->children[1]; + } } - /** - * Evaluates a return statement - */ - template - Boxed_Value eval_return(Eval_System &ss, const TokenPtr &node) { - if (node->children.size() > 0) { - throw Return_Value(eval_token(ss, node->children[0]), node); - } - else { - throw Return_Value(Boxed_Value(), node); + if (guardnode) { + guard = boost::shared_ptr + (new Dynamic_Proxy_Function(boost::bind(&eval_function, + boost::ref(ss), guardnode, + param_names, _1), numparams)); + } + + try { + ss.add(Proxy_Function + (new Dynamic_Proxy_Function(boost::bind(&eval_function, + boost::ref(ss), node->children.back(), + param_names, _1), numparams, + annotation, guard)), function_name); + } + catch (reserved_word_error &) { + throw Eval_Error("Reserved word used as function name '" + function_name + "'", node); + } + return Boxed_Value(); + } + + /** + * Evaluates a function definition + */ + template + Boxed_Value eval_def_method(Eval_System &ss, const TokenPtr &node) { + //TODO: Merge with eval_def cleanly someday? + unsigned int i; + + std::vector param_names; + std::string annotation = node->annotation?node->annotation->text:""; + boost::shared_ptr guard; + size_t numparams; + std::string class_name = node->children[0]->text; + std::string function_name = node->children[1]->text; + TokenPtr guardnode; + + //The first param of a method is always the implied this ptr. + param_names.push_back("this"); + + if ((node->children.size() > 3) && (node->children[2]->identifier == Token_Type::Arg_List)) { + for (i = 0; i < node->children[2]->children.size(); ++i) { + param_names.push_back(node->children[2]->children[i]->text); + } + + if (node->children.size() > 4) { + guardnode = node->children[3]; + } + } + else { + //no parameters + + if (node->children.size() > 3) { + guardnode = node->children[2]; + } + } + + numparams = param_names.size(); + + if (guardnode) { + guard = boost::shared_ptr + (new Dynamic_Proxy_Function(boost::bind(&eval_function, + boost::ref(ss), guardnode, + param_names, _1), numparams)); + } + + try { + if (function_name == class_name) { + ss.add(Proxy_Function + (new Dynamic_Object_Constructor(class_name, Proxy_Function + (new Dynamic_Proxy_Function(boost::bind(&eval_function, + boost::ref(ss), node->children.back(), + param_names, _1), numparams, + annotation, guard)))), function_name); + + } + else { + boost::optional ti; + try { + ti = ss.get_type(class_name); + } catch (const std::range_error &) { + // No biggie, the type name is just not known } + ss.add(Proxy_Function + (new Dynamic_Object_Function(class_name, Proxy_Function + (new Dynamic_Proxy_Function(boost::bind(&eval_function, + boost::ref(ss), node->children.back(), + param_names, _1), numparams, + annotation, guard)), ti)), function_name); + + } + } + catch (reserved_word_error &) { + throw Eval_Error("Reserved word used as method name '" + function_name + "'", node); + } + return Boxed_Value(); + } + + /** + * Evaluates a lambda (anonymous function) + */ + template + Boxed_Value eval_lambda(Eval_System &ss, const TokenPtr &node) { + unsigned int i; + + std::vector param_names; + size_t numparams = 0; + + if ((node->children.size() > 0) && (node->children[0]->identifier == Token_Type::Arg_List)) { + numparams = node->children[0]->children.size(); + for (i = 0; i < numparams; ++i) { + param_names.push_back(node->children[0]->children[i]->text); + } + + } + else { + //no parameters + numparams = 0; } - /** - * Evaluates a break statement - */ - template - Boxed_Value eval_break(Eval_System &, const TokenPtr &node) { - throw Break_Loop(node); + return Boxed_Value(Proxy_Function(new Dynamic_Proxy_Function + (boost::bind(&eval_function, boost::ref(ss), node->children.back(), param_names, _1), + numparams))); + } + + /** + * Evaluates a scoped block. Handles resetting the scope after the block has completed. + */ + template + Boxed_Value eval_block(Eval_System &ss, const TokenPtr &node) { + unsigned int i; + unsigned int num_children = node->children.size(); + + ss.new_scope(); + for (i = 0; i < num_children; ++i) { + try { + const Boxed_Value &retval = eval_token(ss, node->children[i]); + + if (i + 1 == num_children) + { + ss.pop_scope(); + return retval; + } + } + catch (const chaiscript::Return_Value &/*rv*/) { + ss.pop_scope(); + throw; + } + catch (...) { + ss.pop_scope(); + throw; + } } - /** - * Top-level evaluation dispatch for all AST node types - */ - template - Boxed_Value eval_token(Eval_System &ss, const TokenPtr &node) { - switch (node->identifier) { - case (Token_Type::File) : - return eval_file(ss, node); - break; + ss.pop_scope(); + return Boxed_Value(); + } - case (Token_Type::Id) : - return eval_id(ss, node); - break; - - case (Token_Type::Float) : - return eval_float(ss, node); - break; - - case (Token_Type::Int) : - return eval_int(ss, node); - break; - - case (Token_Type::Quoted_String) : - return eval_quoted_string(ss, node); - break; - - case (Token_Type::Single_Quoted_String) : - return eval_single_quoted_string(ss, node); - break; - - case (Token_Type::Equation) : - return eval_equation(ss, node); - break; - - case (Token_Type::Var_Decl) : - return eval_var_decl(ss, node); - break; - - case (Token_Type::Logical_And) : - case (Token_Type::Logical_Or) : - return eval_logical(ss, node); - break; - - case (Token_Type::Bitwise_And) : - case (Token_Type::Bitwise_Xor) : - case (Token_Type::Bitwise_Or) : - case (Token_Type::Comparison) : - case (Token_Type::Equality) : - case (Token_Type::Additive) : - case (Token_Type::Multiplicative) : - case (Token_Type::Shift) : - return eval_comp_add_mul(ss, node); - break; - - case (Token_Type::Array_Call) : - return eval_array_call(ss, node); - break; - - case (Token_Type::Prefix) : - return eval_prefix(ss, node); - break; - - case (Token_Type::Inline_Array) : - return eval_inline_array(ss, node); - break; - - case (Token_Type::Inline_Range) : - return eval_inline_range(ss, node); - break; - - case (Token_Type::Inline_Map) : - return eval_inline_map(ss, node); - break; - - case (Token_Type::Fun_Call) : - return eval_fun_call(ss, node); - break; - - case (Token_Type::Inplace_Fun_Call) : - return eval_inplace_fun_call(ss, node); - break; - - case (Token_Type::Dot_Access) : - return eval_dot_access(ss, node); - break; - - case(Token_Type::Try) : - return eval_try(ss, node); - break; - - case(Token_Type::If) : - return eval_if(ss, node); - break; - - case(Token_Type::While) : - return eval_while(ss, node); - break; - - case(Token_Type::For) : - return eval_for(ss, node); - break; - - case (Token_Type::Def) : - return eval_def(ss, node); - break; - - case (Token_Type::Method) : - return eval_def_method(ss, node); - break; - - case (Token_Type::Attr_Decl) : - return eval_attr_decl(ss, node); - break; - - case (Token_Type::Lambda) : - return eval_lambda(ss, node); - break; - - case (Token_Type::Block) : - return eval_block(ss, node); - break; - - case (Token_Type::Return) : - return eval_return(ss, node); - break; - - case (Token_Type::Break) : - return eval_break(ss, node); - break; - - default : - return Boxed_Value(); - } + /** + * Evaluates a return statement + */ + template + Boxed_Value eval_return(Eval_System &ss, const TokenPtr &node) { + if (node->children.size() > 0) { + throw Return_Value(eval_token(ss, node->children[0]), node); } + else { + throw Return_Value(Boxed_Value(), node); + } + } + + /** + * Evaluates a break statement + */ + template + Boxed_Value eval_break(Eval_System &, const TokenPtr &node) { + throw Break_Loop(node); + } + + /** + * Top-level evaluation dispatch for all AST node types + */ + template + Boxed_Value eval_token(Eval_System &ss, const TokenPtr &node) { + switch (node->identifier) { + case (Token_Type::File) : + return eval_file(ss, node); + break; + + case (Token_Type::Id) : + return eval_id(ss, node); + break; + + case (Token_Type::Float) : + return eval_float(ss, node); + break; + + case (Token_Type::Int) : + return eval_int(ss, node); + break; + + case (Token_Type::Quoted_String) : + return eval_quoted_string(ss, node); + break; + + case (Token_Type::Single_Quoted_String) : + return eval_single_quoted_string(ss, node); + break; + + case (Token_Type::Equation) : + return eval_equation(ss, node); + break; + + case (Token_Type::Var_Decl) : + return eval_var_decl(ss, node); + break; + + case (Token_Type::Logical_And) : + case (Token_Type::Logical_Or) : + return eval_logical(ss, node); + break; + + case (Token_Type::Bitwise_And) : + case (Token_Type::Bitwise_Xor) : + case (Token_Type::Bitwise_Or) : + case (Token_Type::Comparison) : + case (Token_Type::Equality) : + case (Token_Type::Additive) : + case (Token_Type::Multiplicative) : + case (Token_Type::Shift) : + return eval_comp_add_mul(ss, node); + break; + + case (Token_Type::Array_Call) : + return eval_array_call(ss, node); + break; + + case (Token_Type::Prefix) : + return eval_prefix(ss, node); + break; + + case (Token_Type::Inline_Array) : + return eval_inline_array(ss, node); + break; + + case (Token_Type::Inline_Range) : + return eval_inline_range(ss, node); + break; + + case (Token_Type::Inline_Map) : + return eval_inline_map(ss, node); + break; + + case (Token_Type::Fun_Call) : + return eval_fun_call(ss, node); + break; + + case (Token_Type::Inplace_Fun_Call) : + return eval_inplace_fun_call(ss, node); + break; + + case (Token_Type::Dot_Access) : + return eval_dot_access(ss, node); + break; + + case(Token_Type::Try) : + return eval_try(ss, node); + break; + + case(Token_Type::If) : + return eval_if(ss, node); + break; + + case(Token_Type::While) : + return eval_while(ss, node); + break; + + case(Token_Type::For) : + return eval_for(ss, node); + break; + + case (Token_Type::Def) : + return eval_def(ss, node); + break; + + case (Token_Type::Method) : + return eval_def_method(ss, node); + break; + + case (Token_Type::Attr_Decl) : + return eval_attr_decl(ss, node); + break; + + case (Token_Type::Lambda) : + return eval_lambda(ss, node); + break; + + case (Token_Type::Block) : + return eval_block(ss, node); + break; + + case (Token_Type::Return) : + return eval_return(ss, node); + break; + + case (Token_Type::Break) : + return eval_break(ss, node); + break; + + default : + return Boxed_Value(); + } + } } #endif /* CHAISCRIPT_EVAL_HPP_ */ diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 5c967d8..02a31ba 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -18,1845 +18,1860 @@ namespace chaiscript { - class ChaiScript_Parser { + class ChaiScript_Parser { - std::string::iterator input_pos, input_end; - int line, col; - std::string multiline_comment_begin, multiline_comment_end; - std::string singleline_comment; - const char *filename; - std::vector match_stack; + std::string::iterator input_pos, input_end; + int line, col; + std::string multiline_comment_begin, multiline_comment_end; + std::string singleline_comment; + const char *filename; + std::vector match_stack; - std::vector > operator_matches; - std::vector operators; + std::vector > operator_matches; + std::vector operators; - public: - ChaiScript_Parser() { - multiline_comment_begin = "/*"; - multiline_comment_end = "*/"; - singleline_comment = "//"; + public: + ChaiScript_Parser() { + multiline_comment_begin = "/*"; + multiline_comment_end = "*/"; + singleline_comment = "//"; - setup_operators(); - } + setup_operators(); + } - ChaiScript_Parser(const ChaiScript_Parser &); // explicitly unimplemented copy constructor - ChaiScript_Parser &operator=(const ChaiScript_Parser &); // explicitly unimplemented assignment operator + ChaiScript_Parser(const ChaiScript_Parser &); // explicitly unimplemented copy constructor + ChaiScript_Parser &operator=(const ChaiScript_Parser &); // explicitly unimplemented assignment operator - void setup_operators() { - using namespace boost::assign; + void setup_operators() { + using namespace boost::assign; - operators.push_back(Token_Type::Logical_Or); - std::vector logical_or; - logical_or += "||"; - operator_matches.push_back(logical_or); + operators.push_back(Token_Type::Logical_Or); + std::vector logical_or; + logical_or += "||"; + operator_matches.push_back(logical_or); - operators.push_back(Token_Type::Logical_And); - std::vector logical_and; - logical_and += "&&"; - operator_matches.push_back(logical_and); + operators.push_back(Token_Type::Logical_And); + std::vector logical_and; + logical_and += "&&"; + operator_matches.push_back(logical_and); - operators.push_back(Token_Type::Bitwise_Or); - std::vector bitwise_or; - bitwise_or += "|"; - operator_matches.push_back(bitwise_or); + operators.push_back(Token_Type::Bitwise_Or); + std::vector bitwise_or; + bitwise_or += "|"; + operator_matches.push_back(bitwise_or); - operators.push_back(Token_Type::Bitwise_Xor); - std::vector bitwise_xor; - bitwise_xor += "^"; - operator_matches.push_back(bitwise_xor); + operators.push_back(Token_Type::Bitwise_Xor); + std::vector bitwise_xor; + bitwise_xor += "^"; + operator_matches.push_back(bitwise_xor); - operators.push_back(Token_Type::Bitwise_And); - std::vector bitwise_and; - bitwise_and += "&"; - operator_matches.push_back(bitwise_and); + operators.push_back(Token_Type::Bitwise_And); + std::vector bitwise_and; + bitwise_and += "&"; + operator_matches.push_back(bitwise_and); - operators.push_back(Token_Type::Equality); - std::vector equality; - equality += "==", "!="; - operator_matches.push_back(equality); + operators.push_back(Token_Type::Equality); + std::vector equality; + equality += "==", "!="; + operator_matches.push_back(equality); - operators.push_back(Token_Type::Comparison); - std::vector comparison; - comparison += "<", "<=", ">", ">="; - operator_matches.push_back(comparison); + operators.push_back(Token_Type::Comparison); + std::vector comparison; + comparison += "<", "<=", ">", ">="; + operator_matches.push_back(comparison); - operators.push_back(Token_Type::Shift); - std::vector shift; - shift += "<<", ">>"; - operator_matches.push_back(shift); + operators.push_back(Token_Type::Shift); + std::vector shift; + shift += "<<", ">>"; + operator_matches.push_back(shift); - operators.push_back(Token_Type::Additive); - std::vector additive; - additive += "+", "-"; - operator_matches.push_back(additive); + operators.push_back(Token_Type::Additive); + std::vector additive; + additive += "+", "-"; + operator_matches.push_back(additive); - operators.push_back(Token_Type::Multiplicative); - std::vector multiplicative; - multiplicative += "*", "/", "%"; - operator_matches.push_back(multiplicative); + operators.push_back(Token_Type::Multiplicative); + std::vector multiplicative; + multiplicative += "*", "/", "%"; + operator_matches.push_back(multiplicative); - operators.push_back(Token_Type::Dot_Access); - std::vector dot_access; - dot_access += "."; - operator_matches.push_back(dot_access); + operators.push_back(Token_Type::Dot_Access); + std::vector dot_access; + dot_access += "."; + operator_matches.push_back(dot_access); + } + /** + * Prints the parsed tokens as a tree + */ + void debug_print(TokenPtr t, std::string prepend = "") { + std::cout << prepend << "(" << token_type_to_string(t->identifier) << ") " << t->text << " : " << t->start.line << ", " << t->start.column << std::endl; + for (unsigned int j = 0; j < t->children.size(); ++j) { + debug_print(t->children[j], prepend + " "); + } + } + + /** + * Shows the current stack of matched tokens + */ + void show_match_stack() { + for (unsigned int i = 0; i < match_stack.size(); ++i) { + debug_print(match_stack[i]); + } + } + + /** + * Clears the stack of matched tokens + */ + void clear_match_stack() { + match_stack.clear(); + } + + /** + * Returns the front-most AST node + */ + TokenPtr ast() { + return match_stack.front(); + } + + /** + * Helper function that collects tokens from a starting position to the top of the stack into a new AST node + */ + void build_match(Token_Type::Type match_type, int match_start) { + //so we want to take everything to the right of this and make them children + if (match_start != int(match_stack.size())) { + TokenPtr t(new Token("", match_type, filename, match_stack[match_start]->start.line, match_stack[match_start]->start.column, line, col)); + t->children.assign(match_stack.begin() + (match_start), match_stack.end()); + match_stack.erase(match_stack.begin() + (match_start), match_stack.end()); + match_stack.push_back(t); + } + else { + //todo: fix the fact that a successful match that captured no tokens does't have any real start position + TokenPtr t(new Token("", match_type, filename, line, col, line, col)); + match_stack.push_back(t); + } + } + + /** + * Does ranged char check + */ + inline bool charBetween(char start, char end) { + if ((*input_pos >= start) && (*input_pos <= end)) { + return true; + } + else { + return false; + } + } + + /** + * Skips any multi-line or single-line comment + */ + bool SkipComment() { + bool retval = false; + + if (Symbol_(multiline_comment_begin.c_str())) { + while (input_pos != input_end) { + if (Symbol_(multiline_comment_end.c_str())) { + break; + } + else if (!Eol_()) { + ++col; + ++input_pos; + } } - /** - * Prints the parsed tokens as a tree - */ - void debug_print(TokenPtr t, std::string prepend = "") { - std::cout << prepend << "(" << token_type_to_string(t->identifier) << ") " << t->text << " : " << t->start.line << ", " << t->start.column << std::endl; - for (unsigned int j = 0; j < t->children.size(); ++j) { - debug_print(t->children[j], prepend + " "); + retval = true; + } + else if (Symbol_(singleline_comment.c_str())) { + while (input_pos != input_end) { + if (Symbol_("\r\n") || Char_('\n')) { + ++line; + col = 1; + break; + } + else { + ++col; + ++input_pos; + } + } + retval = true; + } + return retval; + } + + /** + * Skips ChaiScript whitespace, which means space and tab, but not cr/lf + */ + bool SkipWS() { + bool retval = false; + while (input_pos != input_end) { + if ((*input_pos == ' ') || (*input_pos == '\t')) { + ++input_pos; + ++col; + retval = true; + } + else if (SkipComment()) { + retval = true; + } + else { + break; + } + } + return retval; + } + + /** + * Reads a floating point value from input, without skipping initial whitespace + */ + bool Float_() { + bool retval = false; + std::string::iterator start = input_pos; + + if ((input_pos != input_end) && (charBetween('0', '9') || (*input_pos == '.'))) { + while ((input_pos != input_end) && charBetween('0', '9')) { + ++input_pos; + ++col; + } + if ((input_pos != input_end) && (*input_pos == '.')) { + ++input_pos; + ++col; + if ((input_pos != input_end) && charBetween('0', '9')) { + retval = true; + while ((input_pos != input_end) && charBetween('0', '9')) { + ++input_pos; + ++col; } + } + else { + --input_pos; + --col; + } } + } + return retval; + } - /** - * Shows the current stack of matched tokens - */ - void show_match_stack() { - for (unsigned int i = 0; i < match_stack.size(); ++i) { - debug_print(match_stack[i]); + /** + * Reads a floating point value from input, without skipping initial whitespace + */ + bool Hex_() { + bool retval = false; + if ((input_pos != input_end) && (*input_pos == '0')) { + ++input_pos; + ++col; + + if ((input_pos != input_end) && ((*input_pos == 'x') || (*input_pos == 'X'))) { + ++input_pos; + ++col; + if ((input_pos != input_end) && (charBetween('0', '9') || + charBetween('a', 'f') || + charBetween('A', 'F'))) { + retval = true; + while ((input_pos != input_end) && (charBetween('0', '9') || + charBetween('a', 'f') || + charBetween('A', 'F'))) { + ++input_pos; + ++col; } + } + else { + --input_pos; + --col; + } } - - /** - * Clears the stack of matched tokens - */ - void clear_match_stack() { - match_stack.clear(); + else { + --input_pos; + --col; } + } - /** - * Returns the front-most AST node - */ - TokenPtr ast() { - return match_stack.front(); + return retval; + } + + /** + * Reads a floating point value from input, without skipping initial whitespace + */ + bool Binary_() { + bool retval = false; + if ((input_pos != input_end) && (*input_pos == '0')) { + ++input_pos; + ++col; + + if ((input_pos != input_end) && ((*input_pos == 'b') || (*input_pos == 'B'))) { + ++input_pos; + ++col; + if ((input_pos != input_end) && charBetween('0', '1')) { + retval = true; + while ((input_pos != input_end) && charBetween('0', '1')) { + ++input_pos; + ++col; + } + } + else { + --input_pos; + --col; + } } + else { + --input_pos; + --col; + } + } - /** - * Helper function that collects tokens from a starting position to the top of the stack into a new AST node - */ - void build_match(Token_Type::Type match_type, int match_start) { - //so we want to take everything to the right of this and make them children - if (match_start != int(match_stack.size())) { - TokenPtr t(new Token("", match_type, filename, match_stack[match_start]->start.line, match_stack[match_start]->start.column, line, col)); - t->children.assign(match_stack.begin() + (match_start), match_stack.end()); - match_stack.erase(match_stack.begin() + (match_start), match_stack.end()); - match_stack.push_back(t); + return retval; + } + + /** + * Reads a number from the input, detecting if it's an integer or floating point + */ + bool Num(bool capture = false) { + SkipWS(); + + if (!capture) { + return Hex_() || Float_(); + } + else { + std::string::iterator start = input_pos; + int prev_col = col; + int prev_line = line; + if ((input_pos != input_end) && (charBetween('0', '9') || (*input_pos == '.')) ) { + if (Hex_()) { + std::string match(start, input_pos); + std::stringstream ss(match); + unsigned int temp_int; + ss >> std::hex >> temp_int; + + std::ostringstream out_int; + out_int << int(temp_int); + TokenPtr t(new Token(out_int.str(), Token_Type::Int, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + return true; + } + if (Binary_()) { + std::string match(start, input_pos); + int temp_int = 0; + unsigned int pos = 0, end = match.length(); + + while ((pos < end) && (pos < (2 + sizeof(int) * 8))) { + temp_int <<= 1; + if (match[pos] == '1') { + temp_int += 1; + } + ++pos; + } + + std::ostringstream out_int; + out_int << temp_int; + TokenPtr t(new Token(out_int.str(), Token_Type::Int, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + return true; + } + if (Float_()) { + std::string match(start, input_pos); + TokenPtr t(new Token(match, Token_Type::Float, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + return true; + } + else { + std::string match(start, input_pos); + if ((match.size() > 0) && (match[0] == '0')) { + std::stringstream ss(match); + unsigned int temp_int; + ss >> std::oct >> temp_int; + + std::ostringstream out_int; + out_int << int(temp_int); + TokenPtr t(new Token(out_int.str(), Token_Type::Int, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); } else { - //todo: fix the fact that a successful match that captured no tokens does't have any real start position - TokenPtr t(new Token("", match_type, filename, line, col, line, col)); - match_stack.push_back(t); + TokenPtr t(new Token(match, Token_Type::Int, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); } + return true; + } } - - /** - * Skips any multi-line or single-line comment - */ - bool SkipComment() { - bool retval = false; - - if (Symbol_(multiline_comment_begin.c_str())) { - while (input_pos != input_end) { - if (Symbol_(multiline_comment_end.c_str())) { - break; - } - else if (!Eol_()) { - ++col; - ++input_pos; - } - } - retval = true; - } - else if (Symbol_(singleline_comment.c_str())) { - while (input_pos != input_end) { - if (Symbol_("\r\n") || Char_('\n')) { - ++line; - col = 1; - break; - } - else { - ++col; - ++input_pos; - } - } - retval = true; - } - return retval; + else { + return false; } + } + } - /** - * Skips ChaiScript whitespace, which means space and tab, but not cr/lf - */ - bool SkipWS() { - bool retval = false; - while (input_pos != input_end) { - if ((*input_pos == ' ') || (*input_pos == '\t')) { - ++input_pos; - ++col; - retval = true; - } - else if (SkipComment()) { - retval = true; - } - else { - break; - } - } - return retval; + /** + * Reads an identifier from input which conforms to C's identifier naming conventions, without skipping initial whitespace + */ + bool Id_() { + bool retval = false; + if ((input_pos != input_end) && (charBetween('A', 'Z') || (*input_pos == '_') || charBetween('a', 'z'))) { + retval = true; + while ((input_pos != input_end) && (charBetween('A', 'Z') || (*input_pos == '_') || + charBetween('a', 'z') || charBetween('0', '9'))) { + ++input_pos; + ++col; } - - /** - * Reads a floating point value from input, without skipping initial whitespace - */ - bool Float_() { - bool retval = false; - std::string::iterator start = input_pos; - - if ((input_pos != input_end) && (((*input_pos >= '0') && (*input_pos <= '9')) || (*input_pos == '.'))) { - while ((input_pos != input_end) && (*input_pos >= '0') && (*input_pos <= '9')) { - ++input_pos; - ++col; - } - if ((input_pos != input_end) && (*input_pos == '.')) { - ++input_pos; - ++col; - if ((input_pos != input_end) && (*input_pos >= '0') && (*input_pos <= '9')) { - retval = true; - while ((input_pos != input_end) && (*input_pos >= '0') && (*input_pos <= '9')) { - ++input_pos; - ++col; - } - } - else { - --input_pos; - --col; - } - } - } - return retval; - } - - /** - * Reads a floating point value from input, without skipping initial whitespace - */ - bool Hex_() { - bool retval = false; - if ((input_pos != input_end) && (*input_pos == '0')) { - ++input_pos; - ++col; - - if ((input_pos != input_end) && ((*input_pos == 'x') || (*input_pos == 'X'))) { - ++input_pos; - ++col; - if ((input_pos != input_end) && (((*input_pos >= '0') && (*input_pos <= '9')) || - ((*input_pos >= 'a') && (*input_pos <= 'f')) || - ((*input_pos >= 'A') && (*input_pos <= 'F')))) { - retval = true; - while ((input_pos != input_end) && (((*input_pos >= '0') && (*input_pos <= '9')) || - ((*input_pos >= 'a') && (*input_pos <= 'f')) || - ((*input_pos >= 'A') && (*input_pos <= 'F')))) { - ++input_pos; - ++col; - } - } - else { - --input_pos; - --col; - } - } - else { - --input_pos; - --col; - } - } - - return retval; - } - - /** - * Reads a floating point value from input, without skipping initial whitespace - */ - bool Binary_() { - bool retval = false; - if ((input_pos != input_end) && (*input_pos == '0')) { - ++input_pos; - ++col; - - if ((input_pos != input_end) && ((*input_pos == 'b') || (*input_pos == 'B'))) { - ++input_pos; - ++col; - if ((input_pos != input_end) && ((*input_pos >= '0') && (*input_pos <= '1'))) { - retval = true; - while ((input_pos != input_end) && ((*input_pos >= '0') && (*input_pos <= '1'))) { - ++input_pos; - ++col; - } - } - else { - --input_pos; - --col; - } - } - else { - --input_pos; - --col; - } - } - - return retval; - } - - /** - * Reads a number from the input, detecting if it's an integer or floating point - */ - bool Num(bool capture = false) { - SkipWS(); - - if (!capture) { - return Hex_() || Float_(); - } - else { - std::string::iterator start = input_pos; - int prev_col = col; - int prev_line = line; - if ((input_pos != input_end) && (((*input_pos >= '0') && (*input_pos <= '9')) || (*input_pos == '.')) ) { - if (Hex_()) { - std::string match(start, input_pos); - std::stringstream ss(match); - unsigned int temp_int; - ss >> std::hex >> temp_int; - - std::ostringstream out_int; - out_int << int(temp_int); - TokenPtr t(new Token(out_int.str(), Token_Type::Int, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - return true; - } - if (Binary_()) { - std::string match(start, input_pos); - int temp_int = 0; - unsigned int pos = 0, end = match.length(); - - while ((pos < end) && (pos < (2 + sizeof(int) * 8))) { - temp_int <<= 1; - if (match[pos] == '1') { - temp_int += 1; - } - ++pos; - } - - std::ostringstream out_int; - out_int << temp_int; - TokenPtr t(new Token(out_int.str(), Token_Type::Int, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - return true; - } - if (Float_()) { - std::string match(start, input_pos); - TokenPtr t(new Token(match, Token_Type::Float, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - return true; - } - else { - std::string match(start, input_pos); - if ((match.size() > 0) && (match[0] == '0')) { - std::stringstream ss(match); - unsigned int temp_int; - ss >> std::oct >> temp_int; - - std::ostringstream out_int; - out_int << int(temp_int); - TokenPtr t(new Token(out_int.str(), Token_Type::Int, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - } - else { - TokenPtr t(new Token(match, Token_Type::Int, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - } - return true; - } - } - else { - return false; - } - } - } - - /** - * Reads an identifier from input which conforms to C's identifier naming conventions, without skipping initial whitespace - */ - bool Id_() { - bool retval = false; - if ((input_pos != input_end) && (((*input_pos >= 'A') && (*input_pos <= 'Z')) || (*input_pos == '_') || ((*input_pos >= 'a') && (*input_pos <= 'z')))) { - retval = true; - while ((input_pos != input_end) && (((*input_pos >= 'A') && (*input_pos <= 'Z')) || (*input_pos == '_') || ((*input_pos >= 'a') && (*input_pos <= 'z')) - || ((*input_pos >= '0') && (*input_pos <= '9')))) { - ++input_pos; - ++col; - } - } - else if ((input_pos != input_end) && (*input_pos == '`')) { - retval = true; - ++col; - ++input_pos; - std::string::iterator start = input_pos; + } + else if ((input_pos != input_end) && (*input_pos == '`')) { + retval = true; + ++col; + ++input_pos; + std::string::iterator start = input_pos; - while ((input_pos != input_end) && (*input_pos != '`')) { - if (Eol()) { - throw Eval_Error("Carriage return in identifier literal", File_Position(line, col), filename); - } - else { - ++input_pos; - ++col; - } - } - - if (start == input_pos) { - throw Eval_Error("Missing contents of identifier literal", File_Position(line, col), filename); - } - else if (input_pos == input_end) { - throw Eval_Error("Incomplete identifier literal", File_Position(line, col), filename); - } - - ++col; - ++input_pos; - } - return retval; + while ((input_pos != input_end) && (*input_pos != '`')) { + if (Eol()) { + throw Eval_Error("Carriage return in identifier literal", File_Position(line, col), filename); + } + else { + ++input_pos; + ++col; + } } - /** - * Reads (and potentially captures) an identifier from input - */ - bool Id(bool capture = false) { - SkipWS(); + if (start == input_pos) { + throw Eval_Error("Missing contents of identifier literal", File_Position(line, col), filename); + } + else if (input_pos == input_end) { + throw Eval_Error("Incomplete identifier literal", File_Position(line, col), filename); + } - if (!capture) { - return Id_(); + ++col; + ++input_pos; + } + return retval; + } + + /** + * Reads (and potentially captures) an identifier from input + */ + bool Id(bool capture = false) { + SkipWS(); + + if (!capture) { + return Id_(); + } + else { + std::string::iterator start = input_pos; + int prev_col = col; + int prev_line = line; + if (Id_()) { + if (*start == '`') { + //Id Literal + std::string match(start+1, input_pos-1); + TokenPtr t(new Token(match, Token_Type::Id, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + return true; + } + else { + std::string match(start, input_pos); + TokenPtr t(new Token(match, Token_Type::Id, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + return true; + } + } + else { + return false; + } + } + } + + /** + * Checks for a node annotation of the form "#" + */ + bool Annotation() { + SkipWS(); + std::string::iterator start = input_pos; + int prev_col = col; + int prev_line = line; + if (Symbol_("#")) { + do { + while (input_pos != input_end) { + if (Eol_()) { + break; } else { - std::string::iterator start = input_pos; - int prev_col = col; - int prev_line = line; - if (Id_()) { - if (*start == '`') { - //Id Literal - std::string match(start+1, input_pos-1); - TokenPtr t(new Token(match, Token_Type::Id, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - return true; - } - else { - std::string match(start, input_pos); - TokenPtr t(new Token(match, Token_Type::Id, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - return true; - } - } - else { - return false; - } + ++col; + ++input_pos; } - } + } + } while (Symbol("#")); - /** - * Checks for a node annotation of the form "#" - */ - bool Annotation() { - SkipWS(); - std::string::iterator start = input_pos; - int prev_col = col; - int prev_line = line; - if (Symbol_("#")) { - do { - while (input_pos != input_end) { - if (Eol_()) { - break; - } - else { - ++col; - ++input_pos; - } - } - } while (Symbol("#")); + std::string match(start, input_pos); + TokenPtr t(new Token(match, Token_Type::Annotation, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + return true; + } + else { + return false; + } + } - std::string match(start, input_pos); - TokenPtr t(new Token(match, Token_Type::Annotation, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - return true; + /** + * Reads a quoted string from input, without skipping initial whitespace + */ + bool Quoted_String_() { + bool retval = false; + char prev_char = 0; + if ((input_pos != input_end) && (*input_pos == '\"')) { + retval = true; + prev_char = *input_pos; + ++input_pos; + ++col; + + while ((input_pos != input_end) && ((*input_pos != '\"') || ((*input_pos == '\"') && (prev_char == '\\')))) { + if (!Eol_()) { + if (prev_char == '\\') { + prev_char = 0; } else { - return false; + prev_char = *input_pos; } + ++input_pos; + ++col; + } } - /** - * Reads a quoted string from input, without skipping initial whitespace - */ - bool Quoted_String_() { - bool retval = false; - char prev_char = 0; - if ((input_pos != input_end) && (*input_pos == '\"')) { - retval = true; - prev_char = *input_pos; - ++input_pos; - ++col; + if (input_pos != input_end) { + ++input_pos; + ++col; + } + else { + throw Eval_Error("Unclosed quoted string", File_Position(line, col), filename); + } + } + return retval; + } - while ((input_pos != input_end) && ((*input_pos != '\"') || ((*input_pos == '\"') && (prev_char == '\\')))) { - if (!Eol_()) { - if (prev_char == '\\') { - prev_char = 0; - } - else { - prev_char = *input_pos; - } - ++input_pos; - ++col; - } - } + /** + * Reads (and potentially captures) a quoted string from input. Translates escaped sequences. + */ + bool Quoted_String(bool capture = false) { + SkipWS(); - if (input_pos != input_end) { - ++input_pos; - ++col; + if (!capture) { + return Quoted_String_(); + } + else { + std::string::iterator start = input_pos; + int prev_col = col; + int prev_line = line; + if (Quoted_String_()) { + std::string match; + bool is_escaped = false; + bool is_interpolated = false; + bool saw_interpolation_marker = false; + int prev_stack_top = match_stack.size(); + + //for (std::string::iterator s = start + 1, end = input_pos - 1; s != end; ++s) { + std::string::iterator s = start + 1, end = input_pos - 1; + + while (s != end) { + if (saw_interpolation_marker) { + if (*s == '{') { + //We've found an interpolation point + + if (is_interpolated) { + //If we've seen previous interpolation, add on instead of making a new one + TokenPtr plus(new Token("+", Token_Type::Str, filename, prev_line, prev_col, line, col)); + match_stack.push_back(plus); + + TokenPtr t(new Token(match, Token_Type::Quoted_String, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + + build_match(Token_Type::Additive, prev_stack_top); } else { - throw Eval_Error("Unclosed quoted string", File_Position(line, col), filename); + TokenPtr t(new Token(match, Token_Type::Quoted_String, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); } - } - return retval; - } - /** - * Reads (and potentially captures) a quoted string from input. Translates escaped sequences. - */ - bool Quoted_String(bool capture = false) { - SkipWS(); + //We've finished with the part of the string up to this point, so clear it + match = ""; - if (!capture) { - return Quoted_String_(); + TokenPtr plus(new Token("+", Token_Type::Str, filename, prev_line, prev_col, line, col)); + match_stack.push_back(plus); + + std::string eval_match; + + ++s; + while ((*s != '}') && (s != end)) { + eval_match.push_back(*s); + ++s; + } + if (*s == '}') { + is_interpolated = true; + ++s; + + int tostr_stack_top = match_stack.size(); + + TokenPtr tostr(new Token("to_string", Token_Type::Id, filename, prev_line, prev_col, line, col)); + match_stack.push_back(tostr); + + int ev_stack_top = match_stack.size(); + + TokenPtr ev(new Token("eval", Token_Type::Id, filename, prev_line, prev_col, line, col)); + match_stack.push_back(ev); + + int arg_stack_top = match_stack.size(); + + TokenPtr t(new Token(eval_match, Token_Type::Quoted_String, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + + build_match(Token_Type::Arg_List, arg_stack_top); + + build_match(Token_Type::Inplace_Fun_Call, ev_stack_top); + + build_match(Token_Type::Arg_List, ev_stack_top); + + build_match(Token_Type::Fun_Call, tostr_stack_top); + + build_match(Token_Type::Additive, prev_stack_top); + } + else { + throw Eval_Error("Unclosed in-string eval", File_Position(prev_line, prev_col), filename); + } + } + else { + match.push_back('$'); + } + saw_interpolation_marker = false; } else { - std::string::iterator start = input_pos; - int prev_col = col; - int prev_line = line; - if (Quoted_String_()) { - std::string match; - bool is_escaped = false; - bool is_interpolated = false; - bool saw_interpolation_marker = false; - int prev_stack_top = match_stack.size(); - - //for (std::string::iterator s = start + 1, end = input_pos - 1; s != end; ++s) { - std::string::iterator s = start + 1, end = input_pos - 1; - - while (s != end) { - if (saw_interpolation_marker) { - if (*s == '{') { - //We've found an interpolation point - - if (is_interpolated) { - //If we've seen previous interpolation, add on instead of making a new one - TokenPtr plus(new Token("+", Token_Type::Str, filename, prev_line, prev_col, line, col)); - match_stack.push_back(plus); - - TokenPtr t(new Token(match, Token_Type::Quoted_String, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - - build_match(Token_Type::Additive, prev_stack_top); - } - else { - TokenPtr t(new Token(match, Token_Type::Quoted_String, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - } - - //We've finished with the part of the string up to this point, so clear it - match = ""; - - TokenPtr plus(new Token("+", Token_Type::Str, filename, prev_line, prev_col, line, col)); - match_stack.push_back(plus); - - std::string eval_match; - - ++s; - while ((*s != '}') && (s != end)) { - eval_match.push_back(*s); - ++s; - } - if (*s == '}') { - is_interpolated = true; - ++s; - - int tostr_stack_top = match_stack.size(); - - TokenPtr tostr(new Token("to_string", Token_Type::Id, filename, prev_line, prev_col, line, col)); - match_stack.push_back(tostr); - - int ev_stack_top = match_stack.size(); - - TokenPtr ev(new Token("eval", Token_Type::Id, filename, prev_line, prev_col, line, col)); - match_stack.push_back(ev); - - int arg_stack_top = match_stack.size(); - - TokenPtr t(new Token(eval_match, Token_Type::Quoted_String, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - - build_match(Token_Type::Arg_List, arg_stack_top); - - build_match(Token_Type::Inplace_Fun_Call, ev_stack_top); - - build_match(Token_Type::Arg_List, ev_stack_top); - - build_match(Token_Type::Fun_Call, tostr_stack_top); - - build_match(Token_Type::Additive, prev_stack_top); - } - else { - throw Eval_Error("Unclosed in-string eval", File_Position(prev_line, prev_col), filename); - } - } - else { - match.push_back('$'); - } - saw_interpolation_marker = false; - } - else { - if (*s == '\\') { - if (is_escaped) { - match.push_back('\\'); - is_escaped = false; - } - else { - is_escaped = true; - } - } - else { - if (is_escaped) { - switch (*s) { - case ('b') : match.push_back('\b'); break; - case ('f') : match.push_back('\f'); break; - case ('n') : match.push_back('\n'); break; - case ('r') : match.push_back('\r'); break; - case ('t') : match.push_back('\t'); break; - case ('\'') : match.push_back('\''); break; - case ('\"') : match.push_back('\"'); break; - case ('$') : match.push_back('$'); break; - default: throw Eval_Error("Unknown escaped sequence in string", File_Position(prev_line, prev_col), filename); - } - } - else if (*s == '$') { - saw_interpolation_marker = true; - } - else { - match.push_back(*s); - } - is_escaped = false; - } - ++s; - } - } - if (is_interpolated) { - TokenPtr plus(new Token("+", Token_Type::Str, filename, prev_line, prev_col, line, col)); - match_stack.push_back(plus); - - TokenPtr t(new Token(match, Token_Type::Quoted_String, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - - build_match(Token_Type::Additive, prev_stack_top); - } - else { - TokenPtr t(new Token(match, Token_Type::Quoted_String, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - } - return true; + if (*s == '\\') { + if (is_escaped) { + match.push_back('\\'); + is_escaped = false; } else { - return false; + is_escaped = true; } - } - } - - /** - * Reads a character group from input, without skipping initial whitespace - */ - bool Single_Quoted_String_() { - bool retval = false; - char prev_char = 0; - if ((input_pos != input_end) && (*input_pos == '\'')) { - retval = true; - prev_char = *input_pos; - ++input_pos; - ++col; - - while ((input_pos != input_end) && ((*input_pos != '\'') || ((*input_pos == '\'') && (prev_char == '\\')))) { - if (!Eol_()) { - if (prev_char == '\\') { - prev_char = 0; - } - else { - prev_char = *input_pos; - } - ++input_pos; - ++col; - } + } + else { + if (is_escaped) { + switch (*s) { + case ('b') : match.push_back('\b'); break; + case ('f') : match.push_back('\f'); break; + case ('n') : match.push_back('\n'); break; + case ('r') : match.push_back('\r'); break; + case ('t') : match.push_back('\t'); break; + case ('\'') : match.push_back('\''); break; + case ('\"') : match.push_back('\"'); break; + case ('$') : match.push_back('$'); break; + default: throw Eval_Error("Unknown escaped sequence in string", File_Position(prev_line, prev_col), filename); + } } - - if (input_pos != input_end) { - ++input_pos; - ++col; + else if (*s == '$') { + saw_interpolation_marker = true; } else { - throw Eval_Error("Unclosed single-quoted string", File_Position(line, col), filename); + match.push_back(*s); } + is_escaped = false; + } + ++s; } - return retval; + } + if (is_interpolated) { + TokenPtr plus(new Token("+", Token_Type::Str, filename, prev_line, prev_col, line, col)); + match_stack.push_back(plus); + + TokenPtr t(new Token(match, Token_Type::Quoted_String, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + + build_match(Token_Type::Additive, prev_stack_top); + } + else { + TokenPtr t(new Token(match, Token_Type::Quoted_String, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + } + return true; } + else { + return false; + } + } + } - /** - * Reads (and potentially captures) a char group from input. Translates escaped sequences. - */ - bool Single_Quoted_String(bool capture = false) { - SkipWS(); + /** + * Reads a character group from input, without skipping initial whitespace + */ + bool Single_Quoted_String_() { + bool retval = false; + char prev_char = 0; + if ((input_pos != input_end) && (*input_pos == '\'')) { + retval = true; + prev_char = *input_pos; + ++input_pos; + ++col; - if (!capture) { - return Single_Quoted_String_(); + while ((input_pos != input_end) && ((*input_pos != '\'') || ((*input_pos == '\'') && (prev_char == '\\')))) { + if (!Eol_()) { + if (prev_char == '\\') { + prev_char = 0; } else { - std::string::iterator start = input_pos; - int prev_col = col; - int prev_line = line; - if (Single_Quoted_String_()) { - std::string match; - bool is_escaped = false; - for (std::string::iterator s = start + 1, end = input_pos - 1; s != end; ++s) { - if (*s == '\\') { - if (is_escaped) { - match.push_back('\\'); - is_escaped = false; - } - else { - is_escaped = true; - } - } - else { - if (is_escaped) { - switch (*s) { - case ('b') : match.push_back('\b'); break; - case ('f') : match.push_back('\f'); break; - case ('n') : match.push_back('\n'); break; - case ('r') : match.push_back('\r'); break; - case ('t') : match.push_back('\t'); break; - case ('\'') : match.push_back('\''); break; - case ('\"') : match.push_back('\"'); break; - default: throw Eval_Error("Unknown escaped sequence in string", File_Position(prev_line, prev_col), filename); - } - } - else { - match.push_back(*s); - } - is_escaped = false; - } - } - TokenPtr t(new Token(match, Token_Type::Single_Quoted_String, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - return true; - } - else { - return false; - } + prev_char = *input_pos; } + ++input_pos; + ++col; + } } - /** - * Reads a char from input if it matches the parameter, without skipping initial whitespace - */ - bool Char_(char c) { - bool retval = false; - if ((input_pos != input_end) && (*input_pos == c)) { - ++input_pos; - ++col; - retval = true; - } - - return retval; + if (input_pos != input_end) { + ++input_pos; + ++col; } + else { + throw Eval_Error("Unclosed single-quoted string", File_Position(line, col), filename); + } + } + return retval; + } - /** - * Reads (and potentially captures) a char from input if it matches the parameter - */ - bool Char(char c, bool capture = false) { - SkipWS(); + /** + * Reads (and potentially captures) a char group from input. Translates escaped sequences. + */ + bool Single_Quoted_String(bool capture = false) { + SkipWS(); - if (!capture) { - return Char_(c); + if (!capture) { + return Single_Quoted_String_(); + } + else { + std::string::iterator start = input_pos; + int prev_col = col; + int prev_line = line; + if (Single_Quoted_String_()) { + std::string match; + bool is_escaped = false; + for (std::string::iterator s = start + 1, end = input_pos - 1; s != end; ++s) { + if (*s == '\\') { + if (is_escaped) { + match.push_back('\\'); + is_escaped = false; + } + else { + is_escaped = true; + } } else { - std::string::iterator start = input_pos; - int prev_col = col; - int prev_line = line; - if (Char_(c)) { - std::string match(start, input_pos); - TokenPtr t(new Token(match, Token_Type::Char, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - return true; - } - else { - return false; + if (is_escaped) { + switch (*s) { + case ('b') : match.push_back('\b'); break; + case ('f') : match.push_back('\f'); break; + case ('n') : match.push_back('\n'); break; + case ('r') : match.push_back('\r'); break; + case ('t') : match.push_back('\t'); break; + case ('\'') : match.push_back('\''); break; + case ('\"') : match.push_back('\"'); break; + default: throw Eval_Error("Unknown escaped sequence in string", File_Position(prev_line, prev_col), filename); } + } + else { + match.push_back(*s); + } + is_escaped = false; } + } + TokenPtr t(new Token(match, Token_Type::Single_Quoted_String, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + return true; } - - /** - * Reads a string from input if it matches the parameter, without skipping initial whitespace - */ - bool Keyword_(const char *s) { - bool retval = false; - int len = strlen(s); - - if ((input_end - input_pos) >= len) { - std::string::iterator tmp = input_pos; - for (int i = 0; i < len; ++i) { - if (*tmp != s[i]) { - return false; - } - ++tmp; - } - retval = true; - input_pos = tmp; - col += len; - } - - return retval; + else { + return false; } + } + } - /** - * Reads (and potentially captures) a string from input if it matches the parameter - */ - bool Keyword(const char *s, bool capture = false) { - SkipWS(); + /** + * Reads a char from input if it matches the parameter, without skipping initial whitespace + */ + bool Char_(char c) { + bool retval = false; + if ((input_pos != input_end) && (*input_pos == c)) { + ++input_pos; + ++col; + retval = true; + } - if (!capture) { - std::string::iterator start = input_pos; - int prev_col = col; - int prev_line = line; - bool retval = Keyword_(s); - if (retval) { - //todo: fix this. Hacky workaround for preventing substring matches - if ((input_pos != input_end) && (((*input_pos >= 'A') && (*input_pos <= 'Z')) || (*input_pos == '_') || ((*input_pos >= 'a') && (*input_pos <= 'z')) - || ((*input_pos >= '0') && (*input_pos <= '9')))) { - input_pos = start; - col = prev_col; - line = prev_line; - return false; - } - return true; - } - else { - return retval; - } - } - else { - std::string::iterator start = input_pos; - int prev_col = col; - int prev_line = line; - if (Keyword_(s)) { - //todo: fix this. Hacky workaround for preventing substring matches - if ((input_pos != input_end) && (((*input_pos >= 'A') && (*input_pos <= 'Z')) || (*input_pos == '_') || ((*input_pos >= 'a') && (*input_pos <= 'z')) - || ((*input_pos >= '0') && (*input_pos <= '9')))) { - input_pos = start; - col = prev_col; - line = prev_line; - return false; - } - std::string match(start, input_pos); - TokenPtr t(new Token(match, Token_Type::Str, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - return true; - } - else { - return false; - } - } + return retval; + } + + /** + * Reads (and potentially captures) a char from input if it matches the parameter + */ + bool Char(char c, bool capture = false) { + SkipWS(); + + if (!capture) { + return Char_(c); + } + else { + std::string::iterator start = input_pos; + int prev_col = col; + int prev_line = line; + if (Char_(c)) { + std::string match(start, input_pos); + TokenPtr t(new Token(match, Token_Type::Char, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + return true; } - - /** - * Reads a symbol group from input if it matches the parameter, without skipping initial whitespace - */ - bool Symbol_(const char *s) { - bool retval = false; - int len = strlen(s); - - if ((input_end - input_pos) >= len) { - std::string::iterator tmp = input_pos; - for (int i = 0; i < len; ++i) { - if (*tmp != s[i]) { - return false; - } - ++tmp; - } - retval = true; - input_pos = tmp; - col += len; - } - - return retval; + else { + return false; } - - /** - * Reads (and potentially captures) a symbol group from input if it matches the parameter - */ - bool Symbol(const char *s, bool capture = false, bool disallow_prevention=false) { - SkipWS(); - - if (!capture) { - std::string::iterator start = input_pos; - int prev_col = col; - int prev_line = line; - bool retval = Symbol_(s); - if (retval) { - //todo: fix this. Hacky workaround for preventing substring matches - if ((input_pos != input_end) && (disallow_prevention == false) && ((*input_pos == '+') || (*input_pos == '-') || (*input_pos == '*') || (*input_pos == '/') - || (*input_pos == '|') || (*input_pos == '&') || (*input_pos == '^') || (*input_pos == '=') || (*input_pos == '.') || (*input_pos == '<') || (*input_pos == '>'))) { - input_pos = start; - col = prev_col; - line = prev_line; - return false; - } - return true; - } - else { - return retval; - } - } - else { - std::string::iterator start = input_pos; - int prev_col = col; - int prev_line = line; - if (Symbol_(s)) { - //todo: fix this. Hacky workaround for preventing substring matches - if ((input_pos != input_end) && (disallow_prevention == false) && ((*input_pos == '+') || (*input_pos == '-') || (*input_pos == '*') || (*input_pos == '/') - || (*input_pos == '|') || (*input_pos == '&') || (*input_pos == '^') || (*input_pos == '=') || (*input_pos == '.') || (*input_pos == '<') || (*input_pos == '>'))) { - input_pos = start; - col = prev_col; - line = prev_line; - return false; - } - else { - std::string match(start, input_pos); - TokenPtr t(new Token(match, Token_Type::Str, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - return true; - } - } - else { - return false; - } - } - } - - /** - * Reads an end-of-line group from input, without skipping initial whitespace - */ - bool Eol_() { - bool retval = false; - - if ((input_pos != input_end) && (Symbol_("\r\n") || Char_('\n'))) { - retval = true; - ++line; - col = 1; - } - else if ((input_pos != input_end) && Char_(';')) { - retval = true; - } - - return retval; - } - - /** - * Reads (and potentially captures) an end-of-line group from input - */ - bool Eol(bool capture = false) { - SkipWS(); - - if (!capture) { - return Eol_(); - } - else { - std::string::iterator start = input_pos; - int prev_col = col; - int prev_line = line; - if (Eol_()) { - std::string match(start, input_pos); - TokenPtr t(new Token(match, Token_Type::Eol, filename, prev_line, prev_col, line, col)); - match_stack.push_back(t); - return true; - } - else { - return false; - } - } - } - - /** - * Reads a comma-separated list of values from input - */ - bool Arg_List() { - bool retval = false; - - int prev_stack_top = match_stack.size(); - - if (Equation()) { - retval = true; - while (Eol()); - if (Char(',')) { - do { - while (Eol()); - if (!Equation()) { - throw Eval_Error("Unexpected value in parameter list", match_stack.back()); - } - } while (retval && Char(',')); - } - build_match(Token_Type::Arg_List, prev_stack_top); - } - - return retval; - } - - /** - * Reads possible special container values, including ranges and map_pairs - */ - bool Container_Arg_List() { - bool retval = false; - - int prev_stack_top = match_stack.size(); - - if (Value_Range()) { - retval = true; - build_match(Token_Type::Arg_List, prev_stack_top); - } - else if (Map_Pair()) { - retval = true; - while (Eol()); - if (Char(',')) { - do { - while (Eol()); - if (!Map_Pair()) { - throw Eval_Error("Unexpected value in container", match_stack.back()); - } - } while (retval && Char(',')); - } - build_match(Token_Type::Arg_List, prev_stack_top); - } - - return retval; - - } - - /** - * Reads a lambda (anonymous function) from input - */ - bool Lambda() { - bool retval = false; - - int prev_stack_top = match_stack.size(); - - if (Keyword("fun")) { - retval = true; - - if (Char('(')) { - Arg_List(); - if (!Char(')')) { - throw Eval_Error("Incomplete anonymous function", File_Position(line, col), filename); - } - } - - while (Eol()); - - if (!Block()) { - throw Eval_Error("Incomplete anonymous function", File_Position(line, col), filename); - } - - build_match(Token_Type::Lambda, prev_stack_top); - } - - return retval; - } - - /** - * Reads a function definition from input - */ - bool Def() { - bool retval = false; - bool is_annotated = false; - bool is_method = false; - TokenPtr annotation; - - if (Annotation()) { - while (Eol_()); - annotation = match_stack.back(); - match_stack.pop_back(); - is_annotated = true; - } - - int prev_stack_top = match_stack.size(); - - if (Keyword("def")) { - retval = true; - - if (!Id(true)) { - throw Eval_Error("Missing function name in definition", File_Position(line, col), filename); - } - - if (Symbol("::", false)) { - //We're now a method - is_method = true; - - if (!Id(true)) { - throw Eval_Error("Missing method name in definition", File_Position(line, col), filename); - } - } - - if (Char('(')) { - Arg_List(); - if (!Char(')')) { - throw Eval_Error("Incomplete function definition", File_Position(line, col), filename); - } - } - - while (Eol()); - - if (Char(':')) { - if (!Operator()) { - throw Eval_Error("Missing guard expression for function", File_Position(line, col), filename); - } - } - - while (Eol()); - if (!Block()) { - throw Eval_Error("Incomplete function definition", File_Position(line, col), filename); - } - - if (is_method) { - build_match(Token_Type::Method, prev_stack_top); - } - else { - build_match(Token_Type::Def, prev_stack_top); - } - - if (is_annotated) { - match_stack.back()->annotation = annotation; - } - } - - return retval; - } - - /** - * Reads a function definition from input - */ - bool Try() { - bool retval = false; - - int prev_stack_top = match_stack.size(); - - if (Keyword("try")) { - retval = true; - - while (Eol()); - - if (!Block()) { - throw Eval_Error("Incomplete 'try' block", File_Position(line, col), filename); - } - - bool has_matches = true; - while (has_matches) { - while (Eol()); - has_matches = false; - if (Keyword("catch", false)) { - int catch_stack_top = match_stack.size(); - if (Char('(')) { - if (!(Id(true) && Char(')'))) { - throw Eval_Error("Incomplete 'catch' expression", File_Position(line, col), filename); - } - if (Char(':')) { - if (!Operator()) { - throw Eval_Error("Missing guard expression for catch", File_Position(line, col), filename); - } - } - } - - while (Eol()); - - if (!Block()) { - throw Eval_Error("Incomplete 'catch' block", File_Position(line, col), filename); - } - build_match(Token_Type::Catch, catch_stack_top); - has_matches = true; - } - } - while (Eol()); - if (Keyword("finally", false)) { - int finally_stack_top = match_stack.size(); - - while (Eol()); - - if (!Block()) { - throw Eval_Error("Incomplete 'finally' block", File_Position(line, col), filename); - } - build_match(Token_Type::Finally, finally_stack_top); - } - - build_match(Token_Type::Try, prev_stack_top); - } - - return retval; - } - - /** - * Reads an if/elseif/else block from input - */ - bool If() { - bool retval = false; - - int prev_stack_top = match_stack.size(); - - if (Keyword("if")) { - retval = true; - - if (!Char('(')) { - throw Eval_Error("Incomplete 'if' expression", File_Position(line, col), filename); - } - - if (!(Operator() && Char(')'))) { - throw Eval_Error("Incomplete 'if' expression", File_Position(line, col), filename); - } - - while (Eol()); - - if (!Block()) { - throw Eval_Error("Incomplete 'if' block", File_Position(line, col), filename); - } - - bool has_matches = true; - while (has_matches) { - while (Eol()); - has_matches = false; - if (Keyword("else", true)) { - if (Keyword("if")) { - match_stack.back()->text = "else if"; - if (!Char('(')) { - throw Eval_Error("Incomplete 'else if' expression", File_Position(line, col), filename); - } - - if (!(Operator() && Char(')'))) { - throw Eval_Error("Incomplete 'else if' expression", File_Position(line, col), filename); - } - - while (Eol()); - - if (!Block()) { - throw Eval_Error("Incomplete 'else if' block", File_Position(line, col), filename); - } - has_matches = true; - } - else { - while (Eol()); - - if (!Block()) { - throw Eval_Error("Incomplete 'else' block", File_Position(line, col), filename); - } - has_matches = true; - } - } - } - - build_match(Token_Type::If, prev_stack_top); - } - - return retval; - } - - /** - * Reads a while block from input - */ - bool While() { - bool retval = false; - - int prev_stack_top = match_stack.size(); - - if (Keyword("while")) { - retval = true; - - if (!Char('(')) { - throw Eval_Error("Incomplete 'while' expression", File_Position(line, col), filename); - } - - if (!(Operator() && Char(')'))) { - throw Eval_Error("Incomplete 'while' expression", File_Position(line, col), filename); - } - - while (Eol()); - - if (!Block()) { - throw Eval_Error("Incomplete 'while' block", File_Position(line, col), filename); - } - - build_match(Token_Type::While, prev_stack_top); - } - - return retval; - } - - /** - * Reads the C-style for conditions from input - */ - bool For_Guards() { - Equation(); - - if (Char(';') && Operator() && Char(';') && Equation()) { - return true; - } - else { - throw Eval_Error("Incomplete conditions in 'for' loop", File_Position(line, col), filename); - } - } - - /** - * Reads a for block from input - */ - bool For() { - bool retval = false; - - int prev_stack_top = match_stack.size(); - - if (Keyword("for")) { - retval = true; - - if (!Char('(')) { - throw Eval_Error("Incomplete 'for' expression", File_Position(line, col), filename); - } - - if (!(For_Guards() && Char(')'))) { - throw Eval_Error("Incomplete 'for' expression", File_Position(line, col), filename); - } - - while (Eol()); - - if (!Block()) { - throw Eval_Error("Incomplete 'for' block", File_Position(line, col), filename); - } - - build_match(Token_Type::For, prev_stack_top); - } - - return retval; - } - - /** - * Reads a curly-brace C-style block from input - */ - bool Block() { - bool retval = false; - - int prev_stack_top = match_stack.size(); - - if (Char('{')) { - retval = true; - - Statements(); - if (!Char('}')) { - throw Eval_Error("Incomplete block", File_Position(line, col), filename); - } - - build_match(Token_Type::Block, prev_stack_top); - } - - return retval; - } - - /** - * Reads a return statement from input - */ - bool Return() { - bool retval = false; - - int prev_stack_top = match_stack.size(); - - if (Keyword("return")) { - retval = true; - - Operator(); - build_match(Token_Type::Return, prev_stack_top); - } - - return retval; - } - - /** - * Reads a break statement from input - */ - bool Break() { - bool retval = false; - - int prev_stack_top = match_stack.size(); - - if (Keyword("break")) { - retval = true; - - build_match(Token_Type::Break, prev_stack_top); - } - - return retval; - } - - /** - * Reads an identifier, then proceeds to check if it's a function or array call - */ - bool Id_Fun_Array() { - bool retval = false; - std::string::iterator prev_pos = input_pos; - - unsigned int prev_stack_top = match_stack.size(); - if (Id(true)) { - retval = true; - bool has_more = true; - - while (has_more) { - has_more = false; - - if (Char('(')) { - has_more = true; - - Arg_List(); - if (!Char(')')) { - throw Eval_Error("Incomplete function call", File_Position(line, col), filename); - } - - build_match(Token_Type::Fun_Call, prev_stack_top); - } - else if (Char('[')) { - has_more = true; - - if (!(Operator() && Char(']'))) { - throw Eval_Error("Incomplete array access", File_Position(line, col), filename); - } - - build_match(Token_Type::Array_Call, prev_stack_top); - } - } - } - - return retval; - } - - /** - * Reads a variable declaration from input - */ - bool Var_Decl() { - bool retval = false; - - int prev_stack_top = match_stack.size(); - - if (Keyword("var")) { - retval = true; - - if (!Id(true)) { - throw Eval_Error("Incomplete variable declaration", File_Position(line, col), filename); - } - - build_match(Token_Type::Var_Decl, prev_stack_top); - } - else if (Keyword("attr")) { - retval = true; - - if (!Id(true)) { - throw Eval_Error("Incomplete attribute declaration", File_Position(line, col), filename); - } - if (!Symbol("::", false)) { - throw Eval_Error("Incomplete attribute declaration", File_Position(line, col), filename); - } - if (!Id(true)) { - throw Eval_Error("Missing attribute name in definition", File_Position(line, col), filename); - } - - - build_match(Token_Type::Attr_Decl, prev_stack_top); - } - - return retval; - } - - /** - * Reads an expression surrounded by parentheses from input - */ - bool Paren_Expression() { - bool retval = false; - - if (Char('(')) { - retval = true; - if (!Operator()) { - throw Eval_Error("Incomplete expression", File_Position(line, col), filename); - } - if (!Char(')')) { - throw Eval_Error("Missing closing parenthesis", File_Position(line, col), filename); - } - } - return retval; - } - - /** - * Reads, and identifies, a short-form container initialization from input - */ - bool Inline_Container() { - bool retval = false; - - unsigned int prev_stack_top = match_stack.size(); - - if (Char('[')) { - retval = true; - Container_Arg_List(); - if (!Char(']')) { - throw Eval_Error("Missing closing square bracket", File_Position(line, col), filename); - } - if ((prev_stack_top != match_stack.size()) && (match_stack.back()->children.size() > 0)) { - if (match_stack.back()->children[0]->identifier == Token_Type::Value_Range) { - build_match(Token_Type::Inline_Range, prev_stack_top); - } - else if (match_stack.back()->children[0]->identifier == Token_Type::Map_Pair) { - build_match(Token_Type::Inline_Map, prev_stack_top); - } - else { - build_match(Token_Type::Inline_Array, prev_stack_top); - } - } - else { - build_match(Token_Type::Inline_Array, prev_stack_top); - } - } - - return retval; - } - - /** - * Reads a unary prefixed expression from input - */ - bool Prefix() { - bool retval = false; - - int prev_stack_top = match_stack.size(); - - if (Symbol("++", true)) { - retval = true; - - if (!Operator(operators.size()-1)) { - throw Eval_Error("Incomplete '++' expression", File_Position(line, col), filename); - } - - build_match(Token_Type::Prefix, prev_stack_top); - } - else if (Symbol("--", true)) { - retval = true; - - if (!Operator(operators.size()-1)) { - throw Eval_Error("Incomplete '--' expression", File_Position(line, col), filename); - } - - build_match(Token_Type::Prefix, prev_stack_top); - } - else if (Char('-', true)) { - retval = true; - - if (!Operator(operators.size()-1)) { - throw Eval_Error("Incomplete unary '-' expression", File_Position(line, col), filename); - } - - build_match(Token_Type::Prefix, prev_stack_top); - } - else if (Char('+', true)) { - retval = true; - - if (!Operator(operators.size()-1)) { - throw Eval_Error("Incomplete unary '+' expression", File_Position(line, col), filename); - } - - build_match(Token_Type::Prefix, prev_stack_top); - } - else if (Char('!', true)) { - retval = true; - - if (!Operator(operators.size()-1)) { - throw Eval_Error("Incomplete '!' expression", File_Position(line, col), filename); - } - - build_match(Token_Type::Prefix, prev_stack_top); - } - else if (Char('~', true)) { - retval = true; - - if (!Operator(operators.size()-1)) { - throw Eval_Error("Incomplete '~' expression", File_Position(line, col), filename); - } - - build_match(Token_Type::Prefix, prev_stack_top); - } - - return retval; - } - - /** - * Parses any of a group of 'value' style token groups from input - */ - bool Value() { - if (Var_Decl() || Lambda() || Id_Fun_Array() || Num(true) || Prefix() || Quoted_String(true) || Single_Quoted_String(true) || - Paren_Expression() || Inline_Container()) { - return true; - } - else { - return false; - } - } - - bool Operator_Helper(int precedence) { - for (unsigned int i = 0; i < operator_matches[precedence].size(); ++i) { - if (Symbol(operator_matches[precedence][i].c_str(), true)) { - return true; - } - } + } + } + + /** + * Reads a string from input if it matches the parameter, without skipping initial whitespace + */ + bool Keyword_(const char *s) { + bool retval = false; + int len = strlen(s); + + if ((input_end - input_pos) >= len) { + std::string::iterator tmp = input_pos; + for (int i = 0; i < len; ++i) { + if (*tmp != s[i]) { return false; + } + ++tmp; + } + retval = true; + input_pos = tmp; + col += len; + } + + return retval; + } + + /** + * Reads (and potentially captures) a string from input if it matches the parameter + */ + bool Keyword(const char *s, bool capture = false) { + SkipWS(); + + if (!capture) { + std::string::iterator start = input_pos; + int prev_col = col; + int prev_line = line; + bool retval = Keyword_(s); + if (retval) { + //todo: fix this. Hacky workaround for preventing substring matches + if ((input_pos != input_end) && (charBetween('A', 'Z') || (*input_pos == '_') || charBetween('a', 'z') + || charBetween('0', '9'))) { + input_pos = start; + col = prev_col; + line = prev_line; + return false; + } + return true; + } + else { + return retval; + } + } + else { + std::string::iterator start = input_pos; + int prev_col = col; + int prev_line = line; + if (Keyword_(s)) { + //todo: fix this. Hacky workaround for preventing substring matches + if ((input_pos != input_end) && (charBetween('A', 'Z') || (*input_pos == '_') || + charBetween('a', 'z') || charBetween('0', '9'))) { + input_pos = start; + col = prev_col; + line = prev_line; + return false; + } + std::string match(start, input_pos); + TokenPtr t(new Token(match, Token_Type::Str, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + return true; + } + else { + return false; + } + } + } + + /** + * Reads a symbol group from input if it matches the parameter, without skipping initial whitespace + */ + bool Symbol_(const char *s) { + bool retval = false; + int len = strlen(s); + + if ((input_end - input_pos) >= len) { + std::string::iterator tmp = input_pos; + for (int i = 0; i < len; ++i) { + if (*tmp != s[i]) { + return false; + } + ++tmp; + } + retval = true; + input_pos = tmp; + col += len; + } + + return retval; + } + + /** + * Reads (and potentially captures) a symbol group from input if it matches the parameter + */ + bool Symbol(const char *s, bool capture = false, bool disallow_prevention=false) { + SkipWS(); + + if (!capture) { + std::string::iterator start = input_pos; + int prev_col = col; + int prev_line = line; + bool retval = Symbol_(s); + if (retval) { + //todo: fix this. Hacky workaround for preventing substring matches + if ((input_pos != input_end) && (disallow_prevention == false) && + ((*input_pos == '+') || (*input_pos == '-') || (*input_pos == '*') || (*input_pos == '/') || + (*input_pos == '|') || (*input_pos == '&') || (*input_pos == '^') || (*input_pos == '=') || + (*input_pos == '.') || (*input_pos == '<') || (*input_pos == '>'))) { + input_pos = start; + col = prev_col; + line = prev_line; + return false; + } + return true; + } + else { + return retval; + } + } + else { + std::string::iterator start = input_pos; + int prev_col = col; + int prev_line = line; + if (Symbol_(s)) { + //todo: fix this. Hacky workaround for preventing substring matches + if ((input_pos != input_end) && (disallow_prevention == false) && + ((*input_pos == '+') || (*input_pos == '-') || (*input_pos == '*') || (*input_pos == '/') || + (*input_pos == '|') || (*input_pos == '&') || (*input_pos == '^') || (*input_pos == '=') || + (*input_pos == '.') || (*input_pos == '<') || (*input_pos == '>'))) { + input_pos = start; + col = prev_col; + line = prev_line; + return false; + } + else { + std::string match(start, input_pos); + TokenPtr t(new Token(match, Token_Type::Str, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + return true; + } + } + else { + return false; + } + } + } + + /** + * Reads an end-of-line group from input, without skipping initial whitespace + */ + bool Eol_() { + bool retval = false; + + if ((input_pos != input_end) && (Symbol_("\r\n") || Char_('\n'))) { + retval = true; + ++line; + col = 1; + } + else if ((input_pos != input_end) && Char_(';')) { + retval = true; + } + + return retval; + } + + /** + * Reads (and potentially captures) an end-of-line group from input + */ + bool Eol(bool capture = false) { + SkipWS(); + + if (!capture) { + return Eol_(); + } + else { + std::string::iterator start = input_pos; + int prev_col = col; + int prev_line = line; + if (Eol_()) { + std::string match(start, input_pos); + TokenPtr t(new Token(match, Token_Type::Eol, filename, prev_line, prev_col, line, col)); + match_stack.push_back(t); + return true; + } + else { + return false; + } + } + } + + /** + * Reads a comma-separated list of values from input + */ + bool Arg_List() { + bool retval = false; + + int prev_stack_top = match_stack.size(); + + if (Equation()) { + retval = true; + while (Eol()); + if (Char(',')) { + do { + while (Eol()); + if (!Equation()) { + throw Eval_Error("Unexpected value in parameter list", match_stack.back()); + } + } while (retval && Char(',')); + } + build_match(Token_Type::Arg_List, prev_stack_top); + } + + return retval; + } + + /** + * Reads possible special container values, including ranges and map_pairs + */ + bool Container_Arg_List() { + bool retval = false; + + int prev_stack_top = match_stack.size(); + + if (Value_Range()) { + retval = true; + build_match(Token_Type::Arg_List, prev_stack_top); + } + else if (Map_Pair()) { + retval = true; + while (Eol()); + if (Char(',')) { + do { + while (Eol()); + if (!Map_Pair()) { + throw Eval_Error("Unexpected value in container", match_stack.back()); + } + } while (retval && Char(',')); + } + build_match(Token_Type::Arg_List, prev_stack_top); + } + + return retval; + + } + + /** + * Reads a lambda (anonymous function) from input + */ + bool Lambda() { + bool retval = false; + + int prev_stack_top = match_stack.size(); + + if (Keyword("fun")) { + retval = true; + + if (Char('(')) { + Arg_List(); + if (!Char(')')) { + throw Eval_Error("Incomplete anonymous function", File_Position(line, col), filename); + } } - bool Operator(unsigned int precedence = 0) { - bool retval = false; + while (Eol()); - int prev_stack_top = match_stack.size(); + if (!Block()) { + throw Eval_Error("Incomplete anonymous function", File_Position(line, col), filename); + } - if (precedence < operators.size()) { - if (Operator(precedence+1)) { - retval = true; - if (Operator_Helper(precedence)) { - do { - if (!Operator(precedence+1)) { - throw Eval_Error("Incomplete " + std::string(token_type_to_string(operators[precedence])) + " expression", - File_Position(line, col), filename); - } - } while (Operator_Helper(precedence)); + build_match(Token_Type::Lambda, prev_stack_top); + } - build_match(operators[precedence], prev_stack_top); - } + return retval; + } + + /** + * Reads a function definition from input + */ + bool Def() { + bool retval = false; + bool is_annotated = false; + bool is_method = false; + TokenPtr annotation; + + if (Annotation()) { + while (Eol_()); + annotation = match_stack.back(); + match_stack.pop_back(); + is_annotated = true; + } + + int prev_stack_top = match_stack.size(); + + if (Keyword("def")) { + retval = true; + + if (!Id(true)) { + throw Eval_Error("Missing function name in definition", File_Position(line, col), filename); + } + + if (Symbol("::", false)) { + //We're now a method + is_method = true; + + if (!Id(true)) { + throw Eval_Error("Missing method name in definition", File_Position(line, col), filename); + } + } + + if (Char('(')) { + Arg_List(); + if (!Char(')')) { + throw Eval_Error("Incomplete function definition", File_Position(line, col), filename); + } + } + + while (Eol()); + + if (Char(':')) { + if (!Operator()) { + throw Eval_Error("Missing guard expression for function", File_Position(line, col), filename); + } + } + + while (Eol()); + if (!Block()) { + throw Eval_Error("Incomplete function definition", File_Position(line, col), filename); + } + + if (is_method) { + build_match(Token_Type::Method, prev_stack_top); + } + else { + build_match(Token_Type::Def, prev_stack_top); + } + + if (is_annotated) { + match_stack.back()->annotation = annotation; + } + } + + return retval; + } + + /** + * Reads a function definition from input + */ + bool Try() { + bool retval = false; + + int prev_stack_top = match_stack.size(); + + if (Keyword("try")) { + retval = true; + + while (Eol()); + + if (!Block()) { + throw Eval_Error("Incomplete 'try' block", File_Position(line, col), filename); + } + + bool has_matches = true; + while (has_matches) { + while (Eol()); + has_matches = false; + if (Keyword("catch", false)) { + int catch_stack_top = match_stack.size(); + if (Char('(')) { + if (!(Id(true) && Char(')'))) { + throw Eval_Error("Incomplete 'catch' expression", File_Position(line, col), filename); + } + if (Char(':')) { + if (!Operator()) { + throw Eval_Error("Missing guard expression for catch", File_Position(line, col), filename); } + } + } + + while (Eol()); + + if (!Block()) { + throw Eval_Error("Incomplete 'catch' block", File_Position(line, col), filename); + } + build_match(Token_Type::Catch, catch_stack_top); + has_matches = true; + } + } + while (Eol()); + if (Keyword("finally", false)) { + int finally_stack_top = match_stack.size(); + + while (Eol()); + + if (!Block()) { + throw Eval_Error("Incomplete 'finally' block", File_Position(line, col), filename); + } + build_match(Token_Type::Finally, finally_stack_top); + } + + build_match(Token_Type::Try, prev_stack_top); + } + + return retval; + } + + /** + * Reads an if/elseif/else block from input + */ + bool If() { + bool retval = false; + + int prev_stack_top = match_stack.size(); + + if (Keyword("if")) { + retval = true; + + if (!Char('(')) { + throw Eval_Error("Incomplete 'if' expression", File_Position(line, col), filename); + } + + if (!(Operator() && Char(')'))) { + throw Eval_Error("Incomplete 'if' expression", File_Position(line, col), filename); + } + + while (Eol()); + + if (!Block()) { + throw Eval_Error("Incomplete 'if' block", File_Position(line, col), filename); + } + + bool has_matches = true; + while (has_matches) { + while (Eol()); + has_matches = false; + if (Keyword("else", true)) { + if (Keyword("if")) { + match_stack.back()->text = "else if"; + if (!Char('(')) { + throw Eval_Error("Incomplete 'else if' expression", File_Position(line, col), filename); + } + + if (!(Operator() && Char(')'))) { + throw Eval_Error("Incomplete 'else if' expression", File_Position(line, col), filename); + } + + while (Eol()); + + if (!Block()) { + throw Eval_Error("Incomplete 'else if' block", File_Position(line, col), filename); + } + has_matches = true; } else { - return Value(); - } + while (Eol()); - return retval; + if (!Block()) { + throw Eval_Error("Incomplete 'else' block", File_Position(line, col), filename); + } + has_matches = true; + } + } } - /** - * Reads a pair of values used to create a map initialization from input - */ - bool Map_Pair() { - bool retval = false; + build_match(Token_Type::If, prev_stack_top); + } - int prev_stack_top = match_stack.size(); + return retval; + } - if (Operator()) { - retval = true; - if (Symbol(":")) { - do { - if (!Operator()) { - throw Eval_Error("Incomplete map pair", File_Position(line, col), filename); - } - } while (retval && Symbol(":")); + /** + * Reads a while block from input + */ + bool While() { + bool retval = false; - build_match(Token_Type::Map_Pair, prev_stack_top); - } - } + int prev_stack_top = match_stack.size(); - return retval; + if (Keyword("while")) { + retval = true; + + if (!Char('(')) { + throw Eval_Error("Incomplete 'while' expression", File_Position(line, col), filename); } - /** - * Reads a pair of values used to create a range initialization from input - */ - bool Value_Range() { - bool retval = false; - - unsigned int prev_stack_top = match_stack.size(); - std::string::iterator prev_pos = input_pos; - int prev_col = col; - - if (Operator()) { - if (Symbol("..")) { - retval = true; - if (!Operator()) { - throw Eval_Error("Incomplete value range", File_Position(line, col), filename); - } - - build_match(Token_Type::Value_Range, prev_stack_top); - } - else { - input_pos = prev_pos; - col = prev_col; - while (prev_stack_top != match_stack.size()) { - match_stack.pop_back(); - } - } - } - - return retval; + if (!(Operator() && Char(')'))) { + throw Eval_Error("Incomplete 'while' expression", File_Position(line, col), filename); } - /** - * Parses a string of binary equation operators - */ - bool Equation() { - bool retval = false; + while (Eol()); - int prev_stack_top = match_stack.size(); - - if (Operator()) { - retval = true; - if (Symbol("=", true, true) || Symbol(":=", true, true) || Symbol("+=", true, true) || - Symbol("-=", true, true) || Symbol("*=", true, true) || Symbol("/=", true, true) || - Symbol("%=", true, true) || Symbol("<<=", true, true) || Symbol(">>=", true, true) || - Symbol("&=", true, true) || Symbol("^=", true, true) || Symbol("|=", true, true)) { - if (!Equation()) { - throw Eval_Error("Incomplete equation", match_stack.back()); - } - - build_match(Token_Type::Equation, prev_stack_top); - } - } - - return retval; + if (!Block()) { + throw Eval_Error("Incomplete 'while' block", File_Position(line, col), filename); } - /** - * Top level parser, starts parsing of all known parses - */ - bool Statements() { - bool retval = false; + build_match(Token_Type::While, prev_stack_top); + } - bool has_more = true; - bool saw_eol = true; + return retval; + } - while (has_more) { - has_more = false; - if (Def()) { - if (!saw_eol) { - throw Eval_Error("Two function definitions missing line separator", match_stack.back()); - } - has_more = true; - retval = true; - saw_eol = true; - } - else if (Try()) { - if (!saw_eol) { - throw Eval_Error("Two function definitions missing line separator", match_stack.back()); - } - has_more = true; - retval = true; - saw_eol = true; - } - else if (If()) { - if (!saw_eol) { - throw Eval_Error("Two function definitions missing line separator", match_stack.back()); - } - has_more = true; - retval = true; - saw_eol = true; - } - else if (While()) { - if (!saw_eol) { - throw Eval_Error("Two function definitions missing line separator", match_stack.back()); - } - has_more = true; - retval = true; - saw_eol = true; - } - else if (For()) { - if (!saw_eol) { - throw Eval_Error("Two function definitions missing line separator", match_stack.back()); - } - has_more = true; - retval = true; - saw_eol = true; - } - else if (Return()) { - if (!saw_eol) { - throw Eval_Error("Two expressions missing line separator", match_stack.back()); - } - has_more = true; - retval = true; - saw_eol = false; - } - else if (Break()) { - if (!saw_eol) { - throw Eval_Error("Two expressions missing line separator", match_stack.back()); - } - has_more = true; - retval = true; - saw_eol = false; - } - else if (Equation()) { - if (!saw_eol) { - throw Eval_Error("Two expressions missing line separator", match_stack.back()); - } - has_more = true; - retval = true; - saw_eol = false; - } - else if (Eol()) { - has_more = true; - retval = true; - saw_eol = true; - } - else if (Block()) { - has_more = true; - retval = true; - saw_eol = true; - } - else { - has_more = false; - } - } + /** + * Reads the C-style for conditions from input + */ + bool For_Guards() { + Equation(); - return retval; + if (Char(';') && Operator() && Char(';') && Equation()) { + return true; + } + else { + throw Eval_Error("Incomplete conditions in 'for' loop", File_Position(line, col), filename); + } + } + + /** + * Reads a for block from input + */ + bool For() { + bool retval = false; + + int prev_stack_top = match_stack.size(); + + if (Keyword("for")) { + retval = true; + + if (!Char('(')) { + throw Eval_Error("Incomplete 'for' expression", File_Position(line, col), filename); } - /** - * Parses the given input string, tagging parsed tokens with the given filename. - */ - bool parse(std::string input, const char *fname) { - input_pos = input.begin(); - input_end = input.end(); - line = 1; col = 1; - filename = fname; - - if ((input.size() > 1) && (input[0] == '#') && (input[1] == '!')) { - while ((input_pos != input_end) && (!Eol())) { - ++input_pos; - } - } - - if (Statements()) { - if (input_pos != input_end) { - throw Eval_Error("Unparsed input", File_Position(line, col), fname); - } - else { - build_match(Token_Type::File, 0); - return true; - } - } - else { - return false; - } + if (!(For_Guards() && Char(')'))) { + throw Eval_Error("Incomplete 'for' expression", File_Position(line, col), filename); } - }; + while (Eol()); + + if (!Block()) { + throw Eval_Error("Incomplete 'for' block", File_Position(line, col), filename); + } + + build_match(Token_Type::For, prev_stack_top); + } + + return retval; + } + + /** + * Reads a curly-brace C-style block from input + */ + bool Block() { + bool retval = false; + + int prev_stack_top = match_stack.size(); + + if (Char('{')) { + retval = true; + + Statements(); + if (!Char('}')) { + throw Eval_Error("Incomplete block", File_Position(line, col), filename); + } + + build_match(Token_Type::Block, prev_stack_top); + } + + return retval; + } + + /** + * Reads a return statement from input + */ + bool Return() { + bool retval = false; + + int prev_stack_top = match_stack.size(); + + if (Keyword("return")) { + retval = true; + + Operator(); + build_match(Token_Type::Return, prev_stack_top); + } + + return retval; + } + + /** + * Reads a break statement from input + */ + bool Break() { + bool retval = false; + + int prev_stack_top = match_stack.size(); + + if (Keyword("break")) { + retval = true; + + build_match(Token_Type::Break, prev_stack_top); + } + + return retval; + } + + /** + * Reads an identifier, then proceeds to check if it's a function or array call + */ + bool Id_Fun_Array() { + bool retval = false; + std::string::iterator prev_pos = input_pos; + + unsigned int prev_stack_top = match_stack.size(); + if (Id(true)) { + retval = true; + bool has_more = true; + + while (has_more) { + has_more = false; + + if (Char('(')) { + has_more = true; + + Arg_List(); + if (!Char(')')) { + throw Eval_Error("Incomplete function call", File_Position(line, col), filename); + } + + build_match(Token_Type::Fun_Call, prev_stack_top); + } + else if (Char('[')) { + has_more = true; + + if (!(Operator() && Char(']'))) { + throw Eval_Error("Incomplete array access", File_Position(line, col), filename); + } + + build_match(Token_Type::Array_Call, prev_stack_top); + } + } + } + + return retval; + } + + /** + * Reads a variable declaration from input + */ + bool Var_Decl() { + bool retval = false; + + int prev_stack_top = match_stack.size(); + + if (Keyword("var")) { + retval = true; + + if (!Id(true)) { + throw Eval_Error("Incomplete variable declaration", File_Position(line, col), filename); + } + + build_match(Token_Type::Var_Decl, prev_stack_top); + } + else if (Keyword("attr")) { + retval = true; + + if (!Id(true)) { + throw Eval_Error("Incomplete attribute declaration", File_Position(line, col), filename); + } + if (!Symbol("::", false)) { + throw Eval_Error("Incomplete attribute declaration", File_Position(line, col), filename); + } + if (!Id(true)) { + throw Eval_Error("Missing attribute name in definition", File_Position(line, col), filename); + } + + + build_match(Token_Type::Attr_Decl, prev_stack_top); + } + + return retval; + } + + /** + * Reads an expression surrounded by parentheses from input + */ + bool Paren_Expression() { + bool retval = false; + + if (Char('(')) { + retval = true; + if (!Operator()) { + throw Eval_Error("Incomplete expression", File_Position(line, col), filename); + } + if (!Char(')')) { + throw Eval_Error("Missing closing parenthesis", File_Position(line, col), filename); + } + } + return retval; + } + + /** + * Reads, and identifies, a short-form container initialization from input + */ + bool Inline_Container() { + bool retval = false; + + unsigned int prev_stack_top = match_stack.size(); + + if (Char('[')) { + retval = true; + Container_Arg_List(); + if (!Char(']')) { + throw Eval_Error("Missing closing square bracket", File_Position(line, col), filename); + } + if ((prev_stack_top != match_stack.size()) && (match_stack.back()->children.size() > 0)) { + if (match_stack.back()->children[0]->identifier == Token_Type::Value_Range) { + build_match(Token_Type::Inline_Range, prev_stack_top); + } + else if (match_stack.back()->children[0]->identifier == Token_Type::Map_Pair) { + build_match(Token_Type::Inline_Map, prev_stack_top); + } + else { + build_match(Token_Type::Inline_Array, prev_stack_top); + } + } + else { + build_match(Token_Type::Inline_Array, prev_stack_top); + } + } + + return retval; + } + + /** + * Reads a unary prefixed expression from input + */ + bool Prefix() { + bool retval = false; + + int prev_stack_top = match_stack.size(); + + if (Symbol("++", true)) { + retval = true; + + if (!Operator(operators.size()-1)) { + throw Eval_Error("Incomplete '++' expression", File_Position(line, col), filename); + } + + build_match(Token_Type::Prefix, prev_stack_top); + } + else if (Symbol("--", true)) { + retval = true; + + if (!Operator(operators.size()-1)) { + throw Eval_Error("Incomplete '--' expression", File_Position(line, col), filename); + } + + build_match(Token_Type::Prefix, prev_stack_top); + } + else if (Char('-', true)) { + retval = true; + + if (!Operator(operators.size()-1)) { + throw Eval_Error("Incomplete unary '-' expression", File_Position(line, col), filename); + } + + build_match(Token_Type::Prefix, prev_stack_top); + } + else if (Char('+', true)) { + retval = true; + + if (!Operator(operators.size()-1)) { + throw Eval_Error("Incomplete unary '+' expression", File_Position(line, col), filename); + } + + build_match(Token_Type::Prefix, prev_stack_top); + } + else if (Char('!', true)) { + retval = true; + + if (!Operator(operators.size()-1)) { + throw Eval_Error("Incomplete '!' expression", File_Position(line, col), filename); + } + + build_match(Token_Type::Prefix, prev_stack_top); + } + else if (Char('~', true)) { + retval = true; + + if (!Operator(operators.size()-1)) { + throw Eval_Error("Incomplete '~' expression", File_Position(line, col), filename); + } + + build_match(Token_Type::Prefix, prev_stack_top); + } + + return retval; + } + + /** + * Parses any of a group of 'value' style token groups from input + */ + bool Value() { + if (Var_Decl() || Lambda() || Id_Fun_Array() || Num(true) || Prefix() || Quoted_String(true) || Single_Quoted_String(true) || + Paren_Expression() || Inline_Container()) { + return true; + } + else { + return false; + } + } + + bool Operator_Helper(int precedence) { + for (unsigned int i = 0; i < operator_matches[precedence].size(); ++i) { + if (Symbol(operator_matches[precedence][i].c_str(), true)) { + return true; + } + } + return false; + } + + bool Operator(unsigned int precedence = 0) { + bool retval = false; + + int prev_stack_top = match_stack.size(); + + if (precedence < operators.size()) { + if (Operator(precedence+1)) { + retval = true; + if (Operator_Helper(precedence)) { + do { + if (!Operator(precedence+1)) { + throw Eval_Error("Incomplete " + std::string(token_type_to_string(operators[precedence])) + " expression", + File_Position(line, col), filename); + } + } while (Operator_Helper(precedence)); + + build_match(operators[precedence], prev_stack_top); + } + } + } + else { + return Value(); + } + + return retval; + } + + /** + * Reads a pair of values used to create a map initialization from input + */ + bool Map_Pair() { + bool retval = false; + + int prev_stack_top = match_stack.size(); + + if (Operator()) { + retval = true; + if (Symbol(":")) { + do { + if (!Operator()) { + throw Eval_Error("Incomplete map pair", File_Position(line, col), filename); + } + } while (retval && Symbol(":")); + + build_match(Token_Type::Map_Pair, prev_stack_top); + } + } + + return retval; + } + + /** + * Reads a pair of values used to create a range initialization from input + */ + bool Value_Range() { + bool retval = false; + + unsigned int prev_stack_top = match_stack.size(); + std::string::iterator prev_pos = input_pos; + int prev_col = col; + + if (Operator()) { + if (Symbol("..")) { + retval = true; + if (!Operator()) { + throw Eval_Error("Incomplete value range", File_Position(line, col), filename); + } + + build_match(Token_Type::Value_Range, prev_stack_top); + } + else { + input_pos = prev_pos; + col = prev_col; + while (prev_stack_top != match_stack.size()) { + match_stack.pop_back(); + } + } + } + + return retval; + } + + /** + * Parses a string of binary equation operators + */ + bool Equation() { + bool retval = false; + + int prev_stack_top = match_stack.size(); + + if (Operator()) { + retval = true; + if (Symbol("=", true, true) || Symbol(":=", true, true) || Symbol("+=", true, true) || + Symbol("-=", true, true) || Symbol("*=", true, true) || Symbol("/=", true, true) || + Symbol("%=", true, true) || Symbol("<<=", true, true) || Symbol(">>=", true, true) || + Symbol("&=", true, true) || Symbol("^=", true, true) || Symbol("|=", true, true)) { + if (!Equation()) { + throw Eval_Error("Incomplete equation", match_stack.back()); + } + + build_match(Token_Type::Equation, prev_stack_top); + } + } + + return retval; + } + + /** + * Top level parser, starts parsing of all known parses + */ + bool Statements() { + bool retval = false; + + bool has_more = true; + bool saw_eol = true; + + while (has_more) { + has_more = false; + if (Def()) { + if (!saw_eol) { + throw Eval_Error("Two function definitions missing line separator", match_stack.back()); + } + has_more = true; + retval = true; + saw_eol = true; + } + else if (Try()) { + if (!saw_eol) { + throw Eval_Error("Two function definitions missing line separator", match_stack.back()); + } + has_more = true; + retval = true; + saw_eol = true; + } + else if (If()) { + if (!saw_eol) { + throw Eval_Error("Two function definitions missing line separator", match_stack.back()); + } + has_more = true; + retval = true; + saw_eol = true; + } + else if (While()) { + if (!saw_eol) { + throw Eval_Error("Two function definitions missing line separator", match_stack.back()); + } + has_more = true; + retval = true; + saw_eol = true; + } + else if (For()) { + if (!saw_eol) { + throw Eval_Error("Two function definitions missing line separator", match_stack.back()); + } + has_more = true; + retval = true; + saw_eol = true; + } + else if (Return()) { + if (!saw_eol) { + throw Eval_Error("Two expressions missing line separator", match_stack.back()); + } + has_more = true; + retval = true; + saw_eol = false; + } + else if (Break()) { + if (!saw_eol) { + throw Eval_Error("Two expressions missing line separator", match_stack.back()); + } + has_more = true; + retval = true; + saw_eol = false; + } + else if (Equation()) { + if (!saw_eol) { + throw Eval_Error("Two expressions missing line separator", match_stack.back()); + } + has_more = true; + retval = true; + saw_eol = false; + } + else if (Eol()) { + has_more = true; + retval = true; + saw_eol = true; + } + else if (Block()) { + has_more = true; + retval = true; + saw_eol = true; + } + else { + has_more = false; + } + } + + return retval; + } + + /** + * Parses the given input string, tagging parsed tokens with the given filename. + */ + bool parse(std::string input, const char *fname) { + input_pos = input.begin(); + input_end = input.end(); + line = 1; col = 1; + filename = fname; + + if ((input.size() > 1) && (input[0] == '#') && (input[1] == '!')) { + while ((input_pos != input_end) && (!Eol())) { + ++input_pos; + } + } + + if (Statements()) { + if (input_pos != input_end) { + throw Eval_Error("Unparsed input", File_Position(line, col), fname); + } + else { + build_match(Token_Type::File, 0); + return true; + } + } + else { + return false; + } + } + + }; } - #endif /* CHAISCRIPT_PARSER_HPP_ */