From 6cf67823112d02a0d1d27fffd3e2ebc73ad9adeb Mon Sep 17 00:00:00 2001 From: lanrona001 Date: Sat, 21 Jul 2007 11:27:46 -0400 Subject: [PATCH] Adding all the files for an integer version of Funky: Groovy --- lib/Funky/Groovy.cpp | 171 +++++++++++++++++++++ lib/Funky/Groovy.h | 161 ++++++++++++++++++++ lib/Funky/Private/Evaluator.h | 258 ++++++++++++++++++++++++++++++++ lib/Funky/Private/FunctionPointer.h | 84 +++++++++++ lib/Funky/Private/Grammar.h | 106 +++++++++++++ lib/Funky/Private/Language.h | 99 ++++++++++++ lib/Funky/Private/LiteralParserPolicy.h | 47 ++++++ lib/Funky/Private/ParsedFunction.h | 67 +++++++++ lib/Funky/Private/ParserIDs.h | 24 +++ lib/Funky/Private/StoredFunction.h | 55 +++++++ lib/Funky/Private/StoredFunctions.h | 52 +++++++ lib/Funky/Private/extractFunctions__.h | 106 +++++++++++++ test/lib/Funky/Groovy.cpp | 199 ++++++++++++++++++++++++ test/lib/Funky/Groovy.h | 112 ++++++++++++++ 14 files changed, 1541 insertions(+) create mode 100755 lib/Funky/Groovy.cpp create mode 100755 lib/Funky/Groovy.h create mode 100755 lib/Funky/Private/Evaluator.h create mode 100755 lib/Funky/Private/FunctionPointer.h create mode 100755 lib/Funky/Private/Grammar.h create mode 100755 lib/Funky/Private/Language.h create mode 100755 lib/Funky/Private/LiteralParserPolicy.h create mode 100755 lib/Funky/Private/ParsedFunction.h create mode 100755 lib/Funky/Private/ParserIDs.h create mode 100755 lib/Funky/Private/StoredFunction.h create mode 100755 lib/Funky/Private/StoredFunctions.h create mode 100755 lib/Funky/Private/extractFunctions__.h create mode 100755 test/lib/Funky/Groovy.cpp create mode 100755 test/lib/Funky/Groovy.h diff --git a/lib/Funky/Groovy.cpp b/lib/Funky/Groovy.cpp new file mode 100755 index 0000000..06ee4a1 --- /dev/null +++ b/lib/Funky/Groovy.cpp @@ -0,0 +1,171 @@ +/* Funky: a light-weight embeddable programming language + * Copyright (c) 2007, Ronald Landheer-Cieslak + * All rights reserved + * + * This is free software. You may distribute it and/or modify it and + * distribute modified forms provided that the following terms are met: + * + * * Redistributions of the source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution; + * * None of the names of the authors of this software may be used to endorse + * or promote this software, derived software or any distribution of this + * software or any distribution of which this software is part, without + * prior written permission from the authors involved; + * * Unless you have received a written statement from Ronald Landheer-Cieslak + * that says otherwise, the terms of the GNU General Public License, as + * published by the Free Software Foundation, version 2 or (at your option) + * any later version, also apply. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "Groovy.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Exceptions/FunctionCallError.h" +#include "Exceptions/ParseError.h" +#include "Private/StoredFunction.h" +#include "Private/StoredFunctions.h" +#include "Private/Grammar.h" +#include "Private/Evaluator.h" +#include "Private/FunctionPointer.h" +#include "Private/ParsedFunction.h" +#include "Private/Language.h" +#include "Private/extractFunctions__.h" + +namespace Funky +{ + using namespace Private; + + struct Groovy::Data + { + // This is the type of a node in the parse tree + typedef boost::spirit::tree_node< boost::spirit::node_val_data<> > Node; + + // the value to return to the caller, from run() or eval() + int retval_; + // the parameters passed to the script + Arguments parameters_; + + // the functions installed by either the user or the parser. This is the equivalent of a symbol table + StoredFunctions< Arguments > functions_; + // the main function - the last statement in the script + Node main_; + }; + + Groovy::Groovy() + : data_(new Data), + grammar_(new Grammar< int >()) + { + // install the built-in functions + installFunction("test", Function(boost::bind(&Language::test__< int >, _1)), 1); + installFunction("or", Function(boost::bind(&Language::or__< int >, _1)), 2); + installFunction("and", Function(boost::bind(&Language::and__< int >, _1)), 2); + installFunction("not", Function(boost::bind(&Language::not__< int >, _1)), 1); + installFunction("add", Function(boost::bind(&Language::add__< int >, _1)), 2); + installFunction("neg", Function(boost::bind(&Language::neg__< int >, _1)), 1); + installFunction("mul", Function(boost::bind(&Language::mul__< int >, _1)), 2); + installFunction("div", Function(boost::bind(&Language::div__< int >, _1)), 2); + } + + Groovy::~Groovy() + { + delete grammar_; + delete data_; + } + + void Groovy::parse(const std::string & program_text) + { + boost::spirit::tree_parse_info<> parse_result; + + try + { + parse_result = boost::spirit::ast_parse(program_text.c_str(), *grammar_, boost::spirit::space_p); + } + catch (const boost::spirit::parser_error< Exceptions::ParseError::Reason > & e) + { + throw Exceptions::ParseError(e.descriptor, static_cast< unsigned int >(e.where - program_text.c_str())); + } + if (!parse_result.full) + { + throw Exceptions::ParseError(Exceptions::ParseError::incomplete_parse__, parse_result.length); + } + else + { /* all is well so far */ } + + /* Once the text is parsed, we have a parse tree. We now need to extract, + * from that tree, any functions we may have found in order to put them in + * the symbol table (data_->functions_) and the last statement, which is + * the one that will be run by run(), which we put in data_->main_. */ + extractFunctions__(parse_result, data_->functions_, data_->main_); + } + + int Groovy::run(const Arguments & _arguments/* = Arguments()*/) + { + Arguments arguments(_arguments); + Evaluator< Arguments > evaluator( data_->functions_ ); + evaluator(data_->main_, &arguments); + if (evaluator.value_.size() != 1) + throw Exceptions::FunctionCallError(Exceptions::FunctionCallError::return_value_count_mismatch__, 1, static_cast< int >(evaluator.value_.size())); + else + { /* all is well */ } + return evaluator.value_.front(); + } + + int Groovy::eval(const std::string & program_text, const Arguments & arguments) + { + parse(program_text); + return run(arguments); + } + + void Groovy::installFunction(const std::string & function_name, const Function & function, int expected_args/* = any__*/) + { + StoredFunctions< Arguments >::iterator where(data_->functions_.find(function_name)); + if (where != data_->functions_.end()) + data_->functions_.erase(where); + else + { /* not a duplicate definition */ } + + data_->functions_.insert(StoredFunctions< Arguments >::value_type(function_name, boost::shared_ptr< StoredFunction< Arguments > >(new FunctionPointer< Arguments >(function, expected_args)))); + } + + void Groovy::installFunction(const std::string & function_name, const MultiReturnFunction & function, int expected_args/* = any__*/, int expected_returns/* = any__*/) + { + StoredFunctions< Arguments >::iterator where(data_->functions_.find(function_name)); + if (where != data_->functions_.end()) + data_->functions_.erase(where); + else + { /* not a duplicate definition */ } + + data_->functions_.insert(StoredFunctions< Arguments >::value_type(function_name, boost::shared_ptr< StoredFunction< Arguments > >(new FunctionPointer< Arguments >(function, expected_args, expected_returns)))); + } + + void Groovy::removeFunction(const std::string & function_name) + { + StoredFunctions< Arguments >::iterator where(data_->functions_.find(function_name)); + if (where != data_->functions_.end()) + data_->functions_.erase(where); + else + { /* function not found - no-op */ } + } +} diff --git a/lib/Funky/Groovy.h b/lib/Funky/Groovy.h new file mode 100755 index 0000000..ab848e3 --- /dev/null +++ b/lib/Funky/Groovy.h @@ -0,0 +1,161 @@ +/* Funky: a light-weight embeddable programming language + * Copyright (c) 2007, Ronald Landheer-Cieslak + * All rights reserved + * + * This is free software. You may distribute it and/or modify it and + * distribute modified forms provided that the following terms are met: + * + * * Redistributions of the source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution; + * * None of the names of the authors of this software may be used to endorse + * or promote this software, derived software or any distribution of this + * software or any distribution of which this software is part, without + * prior written permission from the authors involved; + * * Unless you have received a written statement from Ronald Landheer-Cieslak + * that says otherwise, the terms of the GNU General Public License, as + * published by the Free Software Foundation, version 2 or (at your option) + * any later version, also apply. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _funky_groovy_h +#define _funky_groovy_h + +#include "Details/prologue.h" + +#include +#include +#include + +namespace Funky +{ + namespace Private + { + template < typename LiteralType > + struct Grammar; + } + /** Groovy: a tiny, embeddable functional programming language. + * This class implements a tiny, embeddable programming language that has + * the following features: + * \li you can install your own functions in the language + * \li simple arithmatic functions are built-in + * \li you can pass any number of arguments to the script + * The languages syntax is very lisp-like: a statement is written in brackets + * and consists of a comma-separated list, the first entry in which is a + * function name (all others being its arguments). The following functions + * are built-in: + * \li test: returns 1 if its argument is non-zero, 0 otherwise. + * \li or: returns 1 if either of its two arguments is non-zero, 0 otherwise. + * \li and: returns 1 if both of its arguments are non-zero, 0 otherwise + * \li not: returns 1 if its argument is 0, 0 otherwise + * \li add: returns the sum of its two arguments + * \li neg: returns the negative equivalent of its argument + * \li mul: returns the product of its two arguments + * \li div: returns the result of dividing its two arguments + * For example, the following script: + * \code + * (add, (neg, (add, 6, 6)), 12) + * \endcode + * will return 0: it will first add 6 to 6, resulting in 12. It will then + * negate 12, resulting in -12, to which it will add 12, resulting in 0. + * + * You may have noticed that there is no "sub" function built-in. That's because + * you don't need one: you can easily subtract like this: (add, \@0, (neg, \@1)) + * which very nicely introduces two features of this little language: there's + * nothing there you don't need (but some things you do need may be missing - + * for which reason you can always add to the language) and you can pass + * parameters to your scripts and reference them using the @ sign and the index + * of the parameter you want in the parameters you've passed, starting at 0. + */ + class FUNKY_API Groovy + { + public : + //! The type of the arguments as they are passed around + typedef std::vector< int > Arguments; + //! Signature of an installable function: return a int and take a vector of ints as parameter + typedef boost::function1< int, std::vector< int > > Function; + //! Signature of an installable function that returns more than one value + typedef boost::function1< std::vector< int >, std::vector< int > > MultiReturnFunction; + enum { any__ = -1 }; + + //! Construct a interpreter without any functions installed (except the built-in ones) + Groovy(); + //! Destroy the interpreter and free all associated resources + ~Groovy(); + + /** Parse a given script for later evaluation. + * Once successfully parsed, you can use the run function to run the + * script. Both steps can be done in a single go using the eval + * function, which will parse and then run the script with the given + * arguments. + * note that parsing a script that defines functions will alter the + * state of the interpreter in that it will add functions to the + * internal function table, and those functions may replace the ones + * already there (so if you want to override functions define in the + * scripts, you should install them after parsing the script. Likewise + * if you want a script to be able to override your built-in functions, + * install your functions before parsing the script). + * + * \param program_text the script to parse + * \throws Exceptions::ParseError if an error occurs during parsing */ + void parse(const std::string & program_text); + + /** Run a previously parsed script. Note that execution will start at + * the last statement parsed by the parser. + * + * \param arguments the arguments to pass to the script. + * \throws Exceptions::FunctionCallError if one of the functions does + * not behave as expected */ + int run(const Arguments & arguments = Arguments()); + + /** Evaluate a given script with the given arguments. + * \param program_text the script to evaluate + * \param arguments the arguments to pass to the script + * \return the result of the script + * \throws Exceptions::ParseError if an error occurs during parsing + * \throws Exceptions::FunctionCallError if one of the functions does + * not behave as expected */ + int eval(const std::string & program_text, const Arguments & arguments = Arguments()); + + /** Install a function in the interpreter, callable by a script. + * \param function_name the name of the function to install + * \param function a pointer to the function to call when invoked by the script + * \param expected_args the expected number of arguments the function can be called with, or -1 if any amount is allowed */ + void installFunction(const std::string & function_name, const Function & function, int expected_args = any__); + /** Install a function in the interpreter, callable by a script. + * \param function_name the name of the function to install + * \param function a pointer to the function to call when invoked by the script + * \param expected_args the expected number of arguments the function can be called with, or -1 if any amount is allowed + * \param expected_returns the number of values the function should be expected to return, or -1 if any amount is allowed */ + void installFunction(const std::string & function_name, const MultiReturnFunction & function, int expected_args = any__, int expected_returns = any__); + /** Remove (uninstall) a previously installed function from the interpreter. + * \param function_name the function to remove */ + void removeFunction(const std::string & function_name); + + private : + struct FunctionInfo; + struct Data; + + // Neither CopyConstructible nor Assignable + Groovy(const Groovy &); + Groovy &operator=(const Groovy &); + + Data * data_; + Private::Grammar< int > * grammar_; + }; +} + +#endif diff --git a/lib/Funky/Private/Evaluator.h b/lib/Funky/Private/Evaluator.h new file mode 100755 index 0000000..71491f3 --- /dev/null +++ b/lib/Funky/Private/Evaluator.h @@ -0,0 +1,258 @@ +/* Funky: a light-weight embeddable programming language + * Copyright (c) 2007, Ronald Landheer-Cieslak + * All rights reserved + * + * This is free software. You may distribute it and/or modify it and + * distribute modified forms provided that the following terms are met: + * + * * Redistributions of the source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution; + * * None of the names of the authors of this software may be used to endorse + * or promote this software, derived software or any distribution of this + * software or any distribution of which this software is part, without + * prior written permission from the authors involved; + * * Unless you have received a written statement from Ronald Landheer-Cieslak + * that says otherwise, the terms of the GNU General Public License, as + * published by the Free Software Foundation, version 2 or (at your option) + * any later version, also apply. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _funky_private_evaluator_h +#define _funky_private_evaluator_h + +#include "StoredFunctions.h" + +namespace Funky +{ + namespace Private + { + template < typename Arguments > + struct Evaluator + { + typedef boost::spirit::tree_match< const char * >::tree_iterator TreeIterator; // This is the type of an iterator with which we can traverse the parse tree + typedef boost::spirit::tree_node< boost::spirit::node_val_data<> > Node; // This is the type of a node in the parse tree + typedef std::stack< std::pair< Node, Arguments * > > Stack; + + Evaluator(const StoredFunctions< Arguments > & functions) + : functions_(functions), + value_(0) + { /* no-op */ } + + Evaluator & operator()(const Node & node, Arguments * arguments) + { + stack_.push(std::make_pair(node, arguments)); + Loki::ScopeGuard stack_guard(Loki::MakeObjGuard(stack_, &Stack::pop)); + + Arguments args; // these are the ones we will pass to the next function we call + + if (stack_.top().first.value.id() == function_name_id__) + { + std::string function_to_call(stack_.top().first.value.begin(), stack_.top().first.value.end()); + // look up the function + typename StoredFunctions< Arguments >::const_iterator which(functions_.find(function_to_call)); + if (which == functions_.end() && function_to_call != "shift") + throw Exceptions::FunctionCallError(Exceptions::FunctionCallError::unknown_function__); + else if (function_to_call == "shift") + { + if (stack_.top().second->empty()) + throw Exceptions::FunctionCallError(Exceptions::FunctionCallError::no_more_arguments__); + else + { + value_ = Arguments(1, stack_.top().second->at(0)); + stack_.top().second->erase(stack_.top().second->begin()); + } + } + else + { + Arguments no_args; + value_ = which->second->call(*this, &no_args); + } + } + else + { // function call with arguments + TreeIterator curr(stack_.top().first.children.begin()); + + /* normally, the first node we find is always the name of a + * function to call. After that, we will find any number of + * arguments to pass to it, but those arguments can be either + * literals, parameters passed to us, or statements. If they + * are statements, we will need to evaluate them. If they are + * literals, we have the value we want; if they are parameters, + * they are in the stack's current top. */ + // get the name of the next function to call + assert(curr->value.id() == function_name_id__); + std::string function_name(curr->value.begin(), curr->value.end()); + typename StoredFunctions< Arguments >::const_iterator which(functions_.find(function_name)); + enum { custom__, if__, defined__ } function_type(custom__); + if (which == functions_.end()) + { + /* three possibilities: either it's an "if", for which we + * have a built-in version right here; or it's a "defined", + * in which case there should be one argument, which should be + * a parameter, or it's an unknown function, in which case we + * throw an exception. + * Note that we only start checking whether it's an "if" or + * a "defined" *after" we haven't found the function in the + * function map: we leave the possibility to the user to + * override both built-in operators, just like we leave + * him the possibility to override any built-in function. + * There is no such thing as a reserved word in Funky. */ + if (function_name == "if") + function_type = if__; + else if (function_name == "defined") + function_type = defined__; + else + throw Exceptions::FunctionCallError(Exceptions::FunctionCallError::unknown_function__); + } + else + { /* we know this function - now call it */ } + ++curr; + assert(curr->value.id() == comma_separated_list_id__); + TreeIterator end(curr->children.end()); + TreeIterator curr_arg(curr->children.begin()); + unsigned int argument_index(0); + bool evaluate_second(true); + bool evaluate_third(true); + if (function_type == if__ && std::distance(curr_arg, end) != 6 /* counting commas */) + throw Exceptions::FunctionCallError(Exceptions::FunctionCallError::argument_count_mismatch__, 3, std::distance(curr_arg, end) / 2); + else + { /* all is well so far */ } + if (function_type == defined__ && std::distance(curr_arg, end) != 2 /* counting commas */) + throw Exceptions::FunctionCallError(Exceptions::FunctionCallError::argument_count_mismatch__, 1, std::distance(curr_arg, end) / 2); + else + { /* all is well so far */ } + do + { + assert(curr_arg == end || *(curr_arg->value.begin()) == ','); + if (curr_arg != end) + { + ++curr_arg; + assert(curr_arg != end); + if ((argument_index == 1 && !evaluate_second) || + (argument_index == 2 && !evaluate_third)) + { /* skip this argument */ } + else + { + switch (curr_arg->value.id().to_long()) + { + case literal_id__ : + if (function_type == defined__) + args.push_back(1); // literals are always defined + else + args.push_back(boost::lexical_cast< typename Arguments::value_type >(std::string(curr_arg->value.begin(), curr_arg->value.end()))); + break; + case parameter_id__ : + { + std::string parameter_id(std::string(curr_arg->value.begin(), curr_arg->value.end())); + assert(parameter_id.size() > 1); + if (parameter_id != "@@") + { + unsigned int index(boost::lexical_cast(parameter_id.substr(1))); + if (stack_.top().second->size() <= index && function_type != defined__) + throw Exceptions::FunctionCallError(Exceptions::FunctionCallError::argument_out_of_range__, stack_.top().second->size() - 1, index); + else if (function_type == defined__ && stack_.top().second->size() <= index) + args.push_back(0); + else if (function_type == defined__) + args.push_back(1); + else + args.push_back(stack_.top().second->at(index)); + } + else + { + if (function_type == defined__) + args.push_back(!stack_.top().second->empty()); + else // copy all arguments + std::copy(stack_.top().second->begin(), stack_.top().second->end(), std::back_inserter(args)); + } + } + break; + case statement_id__ : + { + // can't pass a statement to defined + if (function_type == defined__) + throw Exceptions::FunctionCallError(Exceptions::FunctionCallError::unexpected_statement__); + else + { /* not a defined call */ } + Arguments temp_args((*this)(*curr_arg, stack_.top().second).value_); + std::copy(temp_args.begin(), temp_args.end(), std::back_inserter(args)); + } + break; + case function_name_id__ : // a function call without arguments + // can't pass a function call to defined + if (function_type == defined__) + throw Exceptions::FunctionCallError(Exceptions::FunctionCallError::unexpected_statement__); + else + { /* not a defined call */ } + { + std::string function_to_call(curr_arg->value.begin(), curr_arg->value.end()); + // look up the function + typename StoredFunctions< Arguments >::const_iterator which(functions_.find(function_to_call)); + if (which == functions_.end() && function_to_call != "shift") + throw Exceptions::FunctionCallError(Exceptions::FunctionCallError::unknown_function__); + else if (function_to_call == "shift") + { + if (stack_.top().second->empty()) + throw Exceptions::FunctionCallError(Exceptions::FunctionCallError::no_more_arguments__); + else + { + args.push_back(stack_.top().second->at(0)); + stack_.top().second->erase(stack_.top().second->begin()); + } + } + else + { + Arguments no_args; + Arguments returned_args(which->second->call(*this, &no_args)); + std::copy(returned_args.begin(), returned_args.end(), std::back_inserter(args)); + } + } + break; + default : + throw std::logic_error("Unexpected node type"); + } + } + ++curr_arg; + ++argument_index; + if (function_type == if__ && argument_index == 1) + { + // this is where we make the choice whether or not to evaluate the next, second argument + if (!args.back()) + evaluate_second = false; + else + evaluate_third = false; + } + } + else + { /* at the end */ } + } while (curr_arg != end); + if (function_type != if__ && function_type != defined__) + value_ = which->second->call(*this, &args); + else + value_ = Arguments(1, args.back()); + } + return *this; + } + + const StoredFunctions< Arguments > & functions_; + Stack stack_; + Arguments value_; + }; + } +} + +#endif + diff --git a/lib/Funky/Private/FunctionPointer.h b/lib/Funky/Private/FunctionPointer.h new file mode 100755 index 0000000..b87953a --- /dev/null +++ b/lib/Funky/Private/FunctionPointer.h @@ -0,0 +1,84 @@ +/* Funky: a light-weight embeddable programming language + * Copyright (c) 2007, Ronald Landheer-Cieslak + * All rights reserved + * + * This is free software. You may distribute it and/or modify it and + * distribute modified forms provided that the following terms are met: + * + * * Redistributions of the source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution; + * * None of the names of the authors of this software may be used to endorse + * or promote this software, derived software or any distribution of this + * software or any distribution of which this software is part, without + * prior written permission from the authors involved; + * * Unless you have received a written statement from Ronald Landheer-Cieslak + * that says otherwise, the terms of the GNU General Public License, as + * published by the Free Software Foundation, version 2 or (at your option) + * any later version, also apply. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _funky_private_functionpointer_h +#define _funky_private_functionpointer_h + +namespace Funky +{ + namespace Private + { + // Installed function - installed by the user of our class or by the class itself for built-in functions + template < typename Arguments > + struct FunctionPointer : public StoredFunction< Arguments > + { + FunctionPointer( const boost::function1< typename Arguments::value_type, Arguments > & ptr, int expected_args) + : sr_ptr_(ptr), + expected_args_(expected_args), + expected_returns_(1) + { /* no-op */ } + + FunctionPointer( const boost::function1< Arguments, Arguments > & ptr, int expected_args, int expected_returns) + : mr_ptr_(ptr), + expected_args_(expected_args), + expected_returns_(expected_returns) + { /* no-op */ } + + virtual Arguments call(Evaluator< Arguments > & evaluator, Arguments * arguments) + { + if (expected_args_ != -1 && arguments->size() != expected_args_) + throw Exceptions::FunctionCallError(Exceptions::FunctionCallError::argument_count_mismatch__, expected_args_, static_cast< int >(arguments->size())); + else + { /* argument count OK */ } + if (!sr_ptr_.empty()) + return Arguments(1, sr_ptr_(*arguments)); + else + { + Arguments retval(mr_ptr_(*arguments)); + if (expected_returns_ != -1 && retval.size() != expected_returns_) + throw Exceptions::FunctionCallError(Exceptions::FunctionCallError::return_value_count_mismatch__, expected_returns_, static_cast< int >(retval.size())); + else + { /* all is well */ } + return retval; + } + } + + boost::function1< typename Arguments::value_type, Arguments > sr_ptr_; + boost::function1< Arguments, Arguments > mr_ptr_; + int expected_args_; // -1 for any number or arguments + int expected_returns_; // -1 for any number or arguments + }; + } +} + +#endif diff --git a/lib/Funky/Private/Grammar.h b/lib/Funky/Private/Grammar.h new file mode 100755 index 0000000..177b736 --- /dev/null +++ b/lib/Funky/Private/Grammar.h @@ -0,0 +1,106 @@ +/* Funky: a light-weight embeddable programming language + * Copyright (c) 2007, Ronald Landheer-Cieslak + * All rights reserved + * + * This is free software. You may distribute it and/or modify it and + * distribute modified forms provided that the following terms are met: + * + * * Redistributions of the source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution; + * * None of the names of the authors of this software may be used to endorse + * or promote this software, derived software or any distribution of this + * software or any distribution of which this software is part, without + * prior written permission from the authors involved; + * * Unless you have received a written statement from Ronald Landheer-Cieslak + * that says otherwise, the terms of the GNU General Public License, as + * published by the Free Software Foundation, version 2 or (at your option) + * any later version, also apply. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _funky_private_grammar_h +#define _funky_private_grammar_h + +#include +#include +#include "LiteralParserPolicy.h" +#include "ParserIDs.h" + +namespace Funky +{ + namespace Private + { + // This implements the grammar to be used by the parser + template < typename LiteralType > + struct Grammar : public boost::spirit::grammar< Grammar< LiteralType > > + { + typedef LiteralParserPolicy< LiteralType > LiteralParserPolicy_; + + template + struct definition + { + definition(Grammar const& self) + : expect_function_name_(Exceptions::ParseError::function_name_expected__), + expect_left_parenthesis_(Exceptions::ParseError::left_parenthesis_expected__), + expect_right_parenthesis_(Exceptions::ParseError::right_parenthesis_expected__), + expect_literal_parameter_or_statement_(Exceptions::ParseError::literal_parameter_or_statement_expected__) + { + using namespace boost::spirit; + + literal_ = literal_parser_policy_.literal_ + ; + parameter_ = leaf_node_d[lexeme_d[ ch_p('@') >> (+digit_p | ch_p('@'))]] + ; + function_name_ = leaf_node_d[lexeme_d[alpha_p >> *( alnum_p )]] + ; + comma_separated_list_ = *( ch_p(',') >> expect_literal_parameter_or_statement_( literal_ | parameter_ | statement_ ) ) + ; + statement_ = inner_node_d[ch_p('(') >> expect_function_name_(function_name_) >> comma_separated_list_ >> expect_right_parenthesis_(ch_p(')'))] + ; + function_definition_ = inner_node_d[expect_left_parenthesis_(ch_p('(')) >> function_definition_token_ >> expect_function_name_(function_name_) >> !function_param_count_ >> ch_p(',') >> statement_ >> expect_right_parenthesis_(ch_p(')'))] + ; + function_definition_token_ = ch_p('!') + ; + function_param_count_ = ch_p('-') >> root_node_d[lexeme_d[ uint_p ]] + ; + script_ = root_node_d[*function_definition_ >> statement_] + ; + } + + boost::spirit::rule< ScannerT, boost::spirit::parser_context<>, boost::spirit::parser_tag< script_id__ > > const& start() const { return script_; } + + boost::spirit::rule< ScannerT, boost::spirit::parser_context<>, boost::spirit::parser_tag< script_id__ > > script_; + boost::spirit::rule< ScannerT, boost::spirit::parser_context<>, boost::spirit::parser_tag< statement_id__ > > statement_; + boost::spirit::rule< ScannerT, boost::spirit::parser_context<>, boost::spirit::parser_tag< function_definition_id__ > > function_definition_; + boost::spirit::rule< ScannerT, boost::spirit::parser_context<>, boost::spirit::parser_tag< comma_separated_list_id__ > > comma_separated_list_; + boost::spirit::rule< ScannerT, boost::spirit::parser_context<>, boost::spirit::parser_tag< function_name_id__ > > function_name_; + boost::spirit::rule< ScannerT, boost::spirit::parser_context<>, boost::spirit::parser_tag< parameter_id__ > > parameter_; + boost::spirit::rule< ScannerT, boost::spirit::parser_context<>, boost::spirit::parser_tag< literal_id__ > > literal_; + boost::spirit::rule< ScannerT, boost::spirit::parser_context<>, boost::spirit::parser_tag< function_definition_token_id__ > > function_definition_token_; + boost::spirit::rule< ScannerT, boost::spirit::parser_context<>, boost::spirit::parser_tag< function_param_count_id__ > > function_param_count_; + + typename Grammar::LiteralParserPolicy_::definition< ScannerT > literal_parser_policy_; + + boost::spirit::assertion< Exceptions::ParseError::Reason > expect_function_name_; + boost::spirit::assertion< Exceptions::ParseError::Reason > expect_left_parenthesis_; + boost::spirit::assertion< Exceptions::ParseError::Reason > expect_right_parenthesis_; + boost::spirit::assertion< Exceptions::ParseError::Reason > expect_literal_parameter_or_statement_; + }; + }; + } +} + +#endif diff --git a/lib/Funky/Private/Language.h b/lib/Funky/Private/Language.h new file mode 100755 index 0000000..9bdd50e --- /dev/null +++ b/lib/Funky/Private/Language.h @@ -0,0 +1,99 @@ +/* Funky: a light-weight embeddable programming language + * Copyright (c) 2007, Ronald Landheer-Cieslak + * All rights reserved + * + * This is free software. You may distribute it and/or modify it and + * distribute modified forms provided that the following terms are met: + * + * * Redistributions of the source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution; + * * None of the names of the authors of this software may be used to endorse + * or promote this software, derived software or any distribution of this + * software or any distribution of which this software is part, without + * prior written permission from the authors involved; + * * Unless you have received a written statement from Ronald Landheer-Cieslak + * that says otherwise, the terms of the GNU General Public License, as + * published by the Free Software Foundation, version 2 or (at your option) + * any later version, also apply. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _funky_private_language_h +#define _funky_private_language_h + +namespace Funky +{ + namespace Private + { + namespace Language + { + template < typename T > + T test__( const std::vector< T > & param ) + { + assert(param.size() == 1); + return param[0] != 0; + } + + template < typename T > + T or__( const std::vector< T > & param ) + { + assert(param.size() == 2); + return (param[0] || param[1]); + } + + template < typename T > + T and__( const std::vector< T > & param ) + { + assert(param.size() == 2); + return (param[0] && param[1]); + } + + template < typename T > + T not__( const std::vector< T > & param ) + { + assert(param.size() == 1); + return !param[0]; + } + + template < typename T > + T add__( const std::vector< T > & param ) + { + assert(param.size() == 2); + return param[0] + param[1]; + } + template < typename T > + T neg__( const std::vector< T > & param ) + { + assert(param.size() == 1); + return 0 - param[0]; + } + template < typename T > + T mul__( const std::vector< T > & param ) + { + assert(param.size() == 2); + return param[0] * param[1]; + } + template < typename T > + T div__( const std::vector< T > & param ) + { + assert(param.size() == 2); + return param[0] / param[1]; + } + } + } +} + +#endif diff --git a/lib/Funky/Private/LiteralParserPolicy.h b/lib/Funky/Private/LiteralParserPolicy.h new file mode 100755 index 0000000..de448b4 --- /dev/null +++ b/lib/Funky/Private/LiteralParserPolicy.h @@ -0,0 +1,47 @@ +#ifndef _funky_private_literalparserpolicy_h +#define _funky_private_literalparserpolicy_h + +#include "ParserIDs.h" + +namespace Funky +{ + namespace Private + { + template < typename LiteralType > + struct LiteralParserPolicy; + + template <> + struct LiteralParserPolicy< double > + { + template < typename ScannerT > + struct definition + { + definition() + { + using namespace boost::spirit; + literal_ = leaf_node_d[lexeme_d[real_p]] + ; + }; + boost::spirit::rule< ScannerT, boost::spirit::parser_context<>, boost::spirit::parser_tag< literal_id__ > > literal_; + }; + }; + + template <> + struct LiteralParserPolicy< int > + { + template < typename ScannerT > + struct definition + { + definition() + { + using namespace boost::spirit; + literal_ = leaf_node_d[lexeme_d[int_p]] + ; + }; + boost::spirit::rule< ScannerT, boost::spirit::parser_context<>, boost::spirit::parser_tag< literal_id__ > > literal_; + }; + }; + } +} + +#endif diff --git a/lib/Funky/Private/ParsedFunction.h b/lib/Funky/Private/ParsedFunction.h new file mode 100755 index 0000000..86aa327 --- /dev/null +++ b/lib/Funky/Private/ParsedFunction.h @@ -0,0 +1,67 @@ +/* Funky: a light-weight embeddable programming language + * Copyright (c) 2007, Ronald Landheer-Cieslak + * All rights reserved + * + * This is free software. You may distribute it and/or modify it and + * distribute modified forms provided that the following terms are met: + * + * * Redistributions of the source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution; + * * None of the names of the authors of this software may be used to endorse + * or promote this software, derived software or any distribution of this + * software or any distribution of which this software is part, without + * prior written permission from the authors involved; + * * Unless you have received a written statement from Ronald Landheer-Cieslak + * that says otherwise, the terms of the GNU General Public License, as + * published by the Free Software Foundation, version 2 or (at your option) + * any later version, also apply. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _funky_private_parsedfunction_h +#define _funky_private_parsedfunction_h + +namespace Funky +{ + namespace Private + { + // Parsed function. The result of a (!..., (...)) + template < typename Arguments > + struct ParsedFunction : public StoredFunction< Arguments > + { + typedef boost::spirit::tree_node< boost::spirit::node_val_data<> > Node; // This is the type of a node in the parse tree + + ParsedFunction(int expected_args, Node node) + : expected_args_(expected_args), + node_(node) + { /* no-op */ } + + virtual Arguments call(Evaluator< Arguments > & evaluator, Arguments * arguments) + { + if (expected_args_ != -1 && arguments->size() != expected_args_) + throw Exceptions::FunctionCallError(Exceptions::FunctionCallError::argument_count_mismatch__, expected_args_, static_cast< int >(arguments->size())); + else + { /* argument count OK */ } + return evaluator(node_, arguments).value_; + } + + int expected_args_; + Node node_; + }; + } +} + +#endif diff --git a/lib/Funky/Private/ParserIDs.h b/lib/Funky/Private/ParserIDs.h new file mode 100755 index 0000000..9e736c4 --- /dev/null +++ b/lib/Funky/Private/ParserIDs.h @@ -0,0 +1,24 @@ +#ifndef _funky_private_parserids_h +#define _funky_private_parserids_h + +namespace Funky +{ + namespace Private + { + /* Some rules have special identifiers, with which we can recognize them when we walk through the parse tree */ + enum + { + script_id__ = 1, + statement_id__, + function_definition_id__, + comma_separated_list_id__, + function_name_id__, + parameter_id__, + literal_id__, + function_definition_token_id__, + function_param_count_id__ + }; + } +} + +#endif diff --git a/lib/Funky/Private/StoredFunction.h b/lib/Funky/Private/StoredFunction.h new file mode 100755 index 0000000..82331cc --- /dev/null +++ b/lib/Funky/Private/StoredFunction.h @@ -0,0 +1,55 @@ +/* Funky: a light-weight embeddable programming language + * Copyright (c) 2007, Ronald Landheer-Cieslak + * All rights reserved + * + * This is free software. You may distribute it and/or modify it and + * distribute modified forms provided that the following terms are met: + * + * * Redistributions of the source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution; + * * None of the names of the authors of this software may be used to endorse + * or promote this software, derived software or any distribution of this + * software or any distribution of which this software is part, without + * prior written permission from the authors involved; + * * Unless you have received a written statement from Ronald Landheer-Cieslak + * that says otherwise, the terms of the GNU General Public License, as + * published by the Free Software Foundation, version 2 or (at your option) + * any later version, also apply. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _funky_private_storedfunction_h +#define _funky_private_storedfunction_h + +namespace Funky +{ + namespace Private + { + template < typename Arguments > + struct Evaluator; + + // Base class for a stored function. Both a generated (parsed) function and a function pointer derive from this common "interface" + template < typename Arguments > + struct StoredFunction + { + virtual ~StoredFunction() {} // be polymorphic + virtual Arguments call(Evaluator< Arguments > & evaluator, Arguments * arguments) = 0; + }; + + } +} + +#endif diff --git a/lib/Funky/Private/StoredFunctions.h b/lib/Funky/Private/StoredFunctions.h new file mode 100755 index 0000000..7b6dd35 --- /dev/null +++ b/lib/Funky/Private/StoredFunctions.h @@ -0,0 +1,52 @@ +/* Funky: a light-weight embeddable programming language + * Copyright (c) 2007, Ronald Landheer-Cieslak + * All rights reserved + * + * This is free software. You may distribute it and/or modify it and + * distribute modified forms provided that the following terms are met: + * + * * Redistributions of the source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution; + * * None of the names of the authors of this software may be used to endorse + * or promote this software, derived software or any distribution of this + * software or any distribution of which this software is part, without + * prior written permission from the authors involved; + * * Unless you have received a written statement from Ronald Landheer-Cieslak + * that says otherwise, the terms of the GNU General Public License, as + * published by the Free Software Foundation, version 2 or (at your option) + * any later version, also apply. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _funky_private_storedfunctions_h +#define _funky_private_storedfunctions_h + +namespace Funky +{ + namespace Private + { + /* This is just a clever typedef: it is a work-around for the fact that + * the following code isn't legal: + * template < typename Arguments > + * typedef std::map< std::string, boost::shared_ptr< StoredFunction< Arguments > > > StoredFunctions; + * but practically has the same semantics, except that it defines a real type. */ + template < typename Arguments > + struct StoredFunctions : std::map< std::string, boost::shared_ptr< StoredFunction< Arguments > > > + { /* nothing here */ }; + } +} + +#endif diff --git a/lib/Funky/Private/extractFunctions__.h b/lib/Funky/Private/extractFunctions__.h new file mode 100755 index 0000000..0ec0f9a --- /dev/null +++ b/lib/Funky/Private/extractFunctions__.h @@ -0,0 +1,106 @@ +/* Funky: a light-weight embeddable programming language + * Copyright (c) 2007, Ronald Landheer-Cieslak + * All rights reserved + * + * This is free software. You may distribute it and/or modify it and + * distribute modified forms provided that the following terms are met: + * + * * Redistributions of the source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution; + * * None of the names of the authors of this software may be used to endorse + * or promote this software, derived software or any distribution of this + * software or any distribution of which this software is part, without + * prior written permission from the authors involved; + * * Unless you have received a written statement from Ronald Landheer-Cieslak + * that says otherwise, the terms of the GNU General Public License, as + * published by the Free Software Foundation, version 2 or (at your option) + * any later version, also apply. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +namespace Funky +{ + namespace Private + { + template < typename Arguments > + void extractFunctions__( + const boost::spirit::tree_parse_info<> & parse_result, + StoredFunctions< Arguments > & functions, + boost::spirit::tree_node< boost::spirit::node_val_data<> > & main) + { + // This is the type of an iterator with which we can traverse the parse tree + typedef boost::spirit::tree_match< const char * >::const_tree_iterator TreeIterator; + + TreeIterator root_node(parse_result.trees.begin()); + assert(root_node != parse_result.trees.end()); + // we only have a script_id__ if there is at least one function before the first statement. Otherwise, the root node is a statement_id__ + if (root_node->value.id() == script_id__) + { + // for each function definition, + for (TreeIterator current_function(root_node->children.begin()); current_function != root_node->children.end(); ++current_function) + { + if (current_function->value.id() == function_definition_id__) + { + TreeIterator function_definition_node(current_function->children.begin()); + assert( + std::distance(function_definition_node, current_function->children.end()) == 4 || + std::distance(function_definition_node, current_function->children.end()) == 5); + assert(function_definition_node->value.id() == function_definition_token_id__); + ++function_definition_node; + + // extract the name + assert(function_definition_node->value.id() == function_name_id__); + std::string function_name(function_definition_node->value.begin(), function_definition_node->value.end()); + ++function_definition_node; + // extract the number of expected arguments, if known + int expected_parameter_count(-1); + if (function_definition_node->value.id() == function_param_count_id__) + { + expected_parameter_count = boost::lexical_cast< int >( + std::string(function_definition_node->value.begin(), function_definition_node->value.end())); + ++function_definition_node; + } + else + { /* unknown parameter count */ } + + ++function_definition_node; // (the colon) + // extract the statement + // store the statement in our function map, as a tree + boost::shared_ptr< ParsedFunction< Arguments > > function(new ParsedFunction< Arguments >(expected_parameter_count, *function_definition_node)); + typename StoredFunctions< Arguments >::iterator where(functions.find(function_name)); + if (where != functions.end()) + functions.erase(where); + else + { /* not a duplicate definition */ } + functions.insert(typename StoredFunctions< Arguments >::value_type(function_name, function)); + } + else + { + assert(current_function->value.id() == statement_id__); + main = *current_function; + } + } + } + else + { + assert( + root_node->value.id() == statement_id__ || + root_node->value.id() == function_name_id__ /* call without arguments */); + main = *root_node; + } + } + } +} diff --git a/test/lib/Funky/Groovy.cpp b/test/lib/Funky/Groovy.cpp new file mode 100755 index 0000000..dbaf90d --- /dev/null +++ b/test/lib/Funky/Groovy.cpp @@ -0,0 +1,199 @@ +/* Funky: a light-weight embeddable programming language + * Copyright (c) 2007, Ronald Landheer-Cieslak + * All rights reserved + * + * This is free software. You may distribute it and/or modify it and + * distribute modified forms provided that the following terms are met: + * + * * Redistributions of the source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution; + * * None of the names of the authors of this software may be used to endorse + * or promote this software, derived software or any distribution of this + * software or any distribution of which this software is part, without + * prior written permission from the authors involved; + * * Unless you have received a written statement from Ronald Landheer-Cieslak + * that says otherwise, the terms of the GNU General Public License, as + * published by the Free Software Foundation, version 2 or (at your option) + * any later version, also apply. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "Groovy.h" +#include "Stubs/Functor.h" +#include + +CPPUNIT_TEST_SUITE_REGISTRATION( GroovyTest ); + +void GroovyTest::setUp() +{ + parms_[0] = 0; + parms_[1] = 1; + parms_[2] = 2; +} + +//! Clean up after the test run. +void GroovyTest::tearDown() +{ +} + +void GroovyTest::tryScript01(){CPPUNIT_ASSERT(groovy_.eval("(test, 1)"));} +void GroovyTest::tryScript02(){CPPUNIT_ASSERT(groovy_.eval("(add, 1, (neg, 1))") == 0);} +void GroovyTest::tryScript03(){CPPUNIT_ASSERT(groovy_.eval("(add, 1, (neg, @0))", std::vector< int >(parms_, parms_ + 1)) == 1);} +void GroovyTest::tryScript04(){CPPUNIT_ASSERT(groovy_.eval("(add, (neg, (add, 6, 6)), 12)") == 0);} +void GroovyTest::tryScript05(){CPPUNIT_ASSERT(groovy_.eval("(add, (add, 6, 6), -12)") == 0);} +void GroovyTest::tryScript06(){CPPUNIT_ASSERT(groovy_.eval("(!sub-2, (add, @0, (neg, @1)))(sub, 1, 1)") == 0);} +void GroovyTest::tryScript07(){CPPUNIT_ASSERT(groovy_.eval("(!sub, (add, @0, (neg, @1)))(sub, 1, 1)") == 0);} +void GroovyTest::tryScript08(){CPPUNIT_ASSERT(groovy_.eval("(if, 0, 1, 2)") == 2);} +void GroovyTest::tryScript09(){CPPUNIT_ASSERT(groovy_.eval("(if, 1, 1, 2)") == 1);} +void GroovyTest::tryScript10(){CPPUNIT_ASSERT(groovy_.eval("(add, (shift), (add, (shift), (shift)))", std::vector< int >(parms_, parms_ + 3)) == 3);} +void GroovyTest::tryScript11(){CPPUNIT_ASSERT(groovy_.eval("(shift)", std::vector< int >(parms_, parms_ + 3)) == 0);} +void GroovyTest::tryScript12(){CPPUNIT_ASSERT(groovy_.eval("(defined, @3)", std::vector< int >(parms_, parms_ + 3)) == 0);} +void GroovyTest::tryScript13(){CPPUNIT_ASSERT(groovy_.eval("(defined, @2)", std::vector< int >(parms_, parms_ + 3)) == 1);} +void GroovyTest::tryScript14(){CPPUNIT_ASSERT(groovy_.eval("(defined, @1)", std::vector< int >(parms_, parms_ + 3)) == 1);} +void GroovyTest::tryScript15(){CPPUNIT_ASSERT(groovy_.eval("(defined, @0)", std::vector< int >(parms_, parms_ + 3)) == 1);} +void GroovyTest::tryScript16(){CPPUNIT_ASSERT(groovy_.eval("(!sub, (add, @0, (if, (defined, @1), @1, 0)))(sub, 1)") == 1);} +void GroovyTest::tryScript17(){CPPUNIT_ASSERT(groovy_.eval("(shift)", std::vector< int >(parms_ + 1, parms_ + 3)) == 1);} +void GroovyTest::tryScript18(){CPPUNIT_ASSERT(groovy_.eval("(add, @@)", std::vector< int >(parms_ + 1, parms_ + 3)) == 3);} +void GroovyTest::tryScript19() +{ + CPPUNIT_ASSERT(groovy_.eval( + "(!sum, " + " (if, (defined, @0)," + " (add," + " (shift)," + " (if, (defined, @0), (sum, @@), 0))," + " 0" + " )" + ")" + "(sum, @@)", + std::vector< int >(parms_, parms_ + 3)) == 3); +} + +void GroovyTest::tryScript20() +{ + groovy_.installFunction("S", Stubs::Functor< std::vector< int > >(), 2, 2); + CPPUNIT_ASSERT(groovy_.eval("(add, (S, @@))", std::vector< int >(parms_ + 1, parms_ + 3)) == 3); +} + +void GroovyTest::tryParseError01() +{ + CPPUNIT_ASSERT_THROW(groovy_.eval("("), Funky::Exceptions::ParseError); +} + +void GroovyTest::tryParseError02() +{ + bool caught(false); + try + { + groovy_.eval("(0)"); + } + catch (const Funky::Exceptions::ParseError & e) + { + caught = true; + CPPUNIT_ASSERT(e.reason_ == Funky::Exceptions::ParseError::function_name_expected__); + } + CPPUNIT_ASSERT(caught); +} + +void GroovyTest::tryParseError03() +{ + bool caught(false); + try + { + groovy_.eval("(!,0)"); + } + catch (const Funky::Exceptions::ParseError & e) + { + caught = true; + CPPUNIT_ASSERT(e.reason_ == Funky::Exceptions::ParseError::function_name_expected__); + } + CPPUNIT_ASSERT(caught); +} + +void GroovyTest::tryParseError04() +{ + bool caught(false); + try + { + groovy_.eval("!,0)"); + } + catch (const Funky::Exceptions::ParseError & e) + { + caught = true; + CPPUNIT_ASSERT(e.reason_ == Funky::Exceptions::ParseError::left_parenthesis_expected__); + } + CPPUNIT_ASSERT(caught); +} + +void GroovyTest::tryParseError05() +{ + bool caught(false); + try + { + groovy_.eval("(foo,)"); + } + catch (const Funky::Exceptions::ParseError & e) + { + caught = true; + CPPUNIT_ASSERT(e.reason_ == Funky::Exceptions::ParseError::literal_parameter_or_statement_expected__); + } + CPPUNIT_ASSERT(caught); +} + +void GroovyTest::tryParseError06() +{ + bool caught(false); + try + { + groovy_.eval("(foo,0"); + } + catch (const Funky::Exceptions::ParseError & e) + { + caught = true; + CPPUNIT_ASSERT(e.reason_ == Funky::Exceptions::ParseError::right_parenthesis_expected__); + } + CPPUNIT_ASSERT(caught); +} + +void GroovyTest::tryParseError07() +{ + bool caught(false); + try + { + groovy_.eval("(!sub-2, (add, @0, (neg, @1)))"); + } + catch (const Funky::Exceptions::ParseError & e) + { + caught = true; + CPPUNIT_ASSERT(e.reason_ == Funky::Exceptions::ParseError::left_parenthesis_expected__); + } + CPPUNIT_ASSERT(caught); +} + +void GroovyTest::tryParseError08() +{ + bool caught(false); + try + { + groovy_.eval("(test, 1.0)"); + } + catch (const Funky::Exceptions::ParseError & e) + { + caught = true; + CPPUNIT_ASSERT(e.reason_ == Funky::Exceptions::ParseError::right_parenthesis_expected__); + } + CPPUNIT_ASSERT(caught); +} diff --git a/test/lib/Funky/Groovy.h b/test/lib/Funky/Groovy.h new file mode 100755 index 0000000..8abedc3 --- /dev/null +++ b/test/lib/Funky/Groovy.h @@ -0,0 +1,112 @@ +/* Funky: a light-weight embeddable programming language + * Copyright (c) 2007, Ronald Landheer-Cieslak + * All rights reserved + * + * This is free software. You may distribute it and/or modify it and + * distribute modified forms provided that the following terms are met: + * + * * Redistributions of the source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution; + * * None of the names of the authors of this software may be used to endorse + * or promote this software, derived software or any distribution of this + * software or any distribution of which this software is part, without + * prior written permission from the authors involved; + * * Unless you have received a written statement from Ronald Landheer-Cieslak + * that says otherwise, the terms of the GNU General Public License, as + * published by the Free Software Foundation, version 2 or (at your option) + * any later version, also apply. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _funky_tests_groovytest_h +#define _funky_tests_groovytest_h + +#include +#include + +class GroovyTest : public CPPUNIT_NS::TestFixture +{ +// Tests registration + CPPUNIT_TEST_SUITE( GroovyTest ); + CPPUNIT_TEST(tryScript01); + CPPUNIT_TEST(tryScript02); + CPPUNIT_TEST(tryScript03); + CPPUNIT_TEST(tryScript04); + CPPUNIT_TEST(tryScript05); + CPPUNIT_TEST(tryScript06); + CPPUNIT_TEST(tryScript07); + CPPUNIT_TEST(tryScript08); + CPPUNIT_TEST(tryScript09); + CPPUNIT_TEST(tryScript10); + CPPUNIT_TEST(tryScript11); + CPPUNIT_TEST(tryScript12); + CPPUNIT_TEST(tryScript13); + CPPUNIT_TEST(tryScript14); + CPPUNIT_TEST(tryScript15); + CPPUNIT_TEST(tryScript16); + CPPUNIT_TEST(tryScript17); + CPPUNIT_TEST(tryScript18); + CPPUNIT_TEST(tryScript19); + CPPUNIT_TEST(tryScript20); + CPPUNIT_TEST(tryParseError01); + CPPUNIT_TEST(tryParseError02); + CPPUNIT_TEST(tryParseError03); + CPPUNIT_TEST(tryParseError04); + CPPUNIT_TEST(tryParseError05); + CPPUNIT_TEST(tryParseError06); + CPPUNIT_TEST(tryParseError07); + CPPUNIT_TEST(tryParseError08); + CPPUNIT_TEST_SUITE_END(); + +public: + virtual void setUp(); + virtual void tearDown(); + +protected: + void tryScript01(); + void tryScript02(); + void tryScript03(); + void tryScript04(); + void tryScript05(); + void tryScript06(); + void tryScript07(); + void tryScript08(); + void tryScript09(); + void tryScript10(); + void tryScript11(); + void tryScript12(); + void tryScript13(); + void tryScript14(); + void tryScript15(); + void tryScript16(); + void tryScript17(); + void tryScript18(); + void tryScript19(); + void tryScript20(); + void tryParseError01(); + void tryParseError02(); + void tryParseError03(); + void tryParseError04(); + void tryParseError05(); + void tryParseError06(); + void tryParseError07(); + void tryParseError08(); +private: + Funky::Groovy groovy_; + int parms_[3]; +}; + +#endif -- 2.11.4.GIT