Compare commits
20 Commits
Release-2.
...
Release-2.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d2d752ecd4 | ||
![]() |
a18c701866 | ||
![]() |
9871604a48 | ||
![]() |
b1d12fdc91 | ||
![]() |
24e717d532 | ||
![]() |
1568fedebd | ||
![]() |
c88578d537 | ||
![]() |
9827345213 | ||
![]() |
c51d14fb13 | ||
![]() |
480761c1f7 | ||
![]() |
12e909d9aa | ||
![]() |
b1e892487f | ||
![]() |
720eabcb16 | ||
![]() |
1fde71f3f4 | ||
![]() |
90f8b77171 | ||
![]() |
315d7521a7 | ||
![]() |
ff177b5eaf | ||
![]() |
46fd7e9a58 | ||
![]() |
edd274ccce | ||
![]() |
a5b2ec3006 |
@@ -29,125 +29,6 @@
|
|||||||
#define CHAISCRIPT_MODULE_EXPORT extern "C"
|
#define CHAISCRIPT_MODULE_EXPORT extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace chaiscript
|
|
||||||
{
|
|
||||||
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,
|
|
||||||
Expression, Comparison, Additive, Multiplicative, Negate, Not, 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 }; };
|
|
||||||
|
|
||||||
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",
|
|
||||||
"Expression", "Comparison", "Additive", "Multiplicative", "Negate", "Not", "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"};
|
|
||||||
|
|
||||||
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<struct Token> 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<TokenPtr> 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<std::string>(where.line) + ", " +
|
|
||||||
boost::lexical_cast<std::string>(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<std::string>(where->start.line) + ", " +
|
|
||||||
boost::lexical_cast<std::string>(where->start.column) + ")"),
|
|
||||||
reason(why), start_position(where->start), end_position(where->end), filename(where->filename) {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~Eval_Error() throw() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) { }
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Special type indicating a call to 'break'
|
|
||||||
*/
|
|
||||||
struct Break_Loop {
|
|
||||||
TokenPtr location;
|
|
||||||
|
|
||||||
Break_Loop(const TokenPtr where) : location(where) { }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "language/chaiscript_eval.hpp"
|
#include "language/chaiscript_eval.hpp"
|
||||||
#include "language/chaiscript_engine.hpp"
|
#include "language/chaiscript_engine.hpp"
|
||||||
|
|
||||||
|
65
include/chaiscript/dispatchkit/bind_first.hpp
Normal file
65
include/chaiscript/dispatchkit/bind_first.hpp
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// This file is distributed under the BSD License.
|
||||||
|
// See "license.txt" for details.
|
||||||
|
// Copyright 2009, Jonathan Turner (jturner@minnow-lang.org)
|
||||||
|
// and Jason Turner (lefticus@gmail.com)
|
||||||
|
// http://www.chaiscript.com
|
||||||
|
|
||||||
|
#include <boost/preprocessor.hpp>
|
||||||
|
#include <boost/preprocessor/arithmetic/inc.hpp>
|
||||||
|
|
||||||
|
#define param(z,n,text) BOOST_PP_CAT(text, BOOST_PP_INC(n))
|
||||||
|
|
||||||
|
#ifndef BOOST_PP_IS_ITERATING
|
||||||
|
#ifndef __bind_first_hpp__
|
||||||
|
#define __bind_first_hpp__
|
||||||
|
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/ref.hpp>
|
||||||
|
|
||||||
|
#define BOOST_PP_ITERATION_LIMITS ( 0, 8 )
|
||||||
|
#define BOOST_PP_FILENAME_1 <chaiscript/dispatchkit/bind_first.hpp>
|
||||||
|
|
||||||
|
#include BOOST_PP_ITERATE()
|
||||||
|
|
||||||
|
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# define n BOOST_PP_ITERATION()
|
||||||
|
# define m BOOST_PP_INC(n)
|
||||||
|
|
||||||
|
namespace chaiscript
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename Ret, typename O, typename Class BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, typename Param) >
|
||||||
|
boost::function<Ret (BOOST_PP_ENUM_PARAMS(n, Param))>
|
||||||
|
bind_first(Ret (Class::*f)(BOOST_PP_ENUM_PARAMS(n, Param)), const O &o)
|
||||||
|
{
|
||||||
|
return boost::bind(f, o BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM(n, param, _));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Ret, typename O, typename Class BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, typename Param) >
|
||||||
|
boost::function<Ret (BOOST_PP_ENUM_PARAMS(n, Param))>
|
||||||
|
bind_first(Ret (Class::*f)(BOOST_PP_ENUM_PARAMS(n, Param))const, const O &o)
|
||||||
|
{
|
||||||
|
return boost::bind(f, o BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM(n, param, _));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Ret,typename O BOOST_PP_COMMA_IF(m) BOOST_PP_ENUM_PARAMS(m, typename Param) >
|
||||||
|
boost::function<Ret (BOOST_PP_ENUM(n, param, Param))>
|
||||||
|
bind_first(Ret (*f)(BOOST_PP_ENUM_PARAMS(m, Param)), const O &o)
|
||||||
|
{
|
||||||
|
return boost::bind(f, o BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM(n, param, _));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Ret,typename O BOOST_PP_COMMA_IF(m) BOOST_PP_ENUM_PARAMS(m, typename Param) >
|
||||||
|
boost::function<Ret (BOOST_PP_ENUM(n, param, Param))>
|
||||||
|
bind_first(const boost::function<Ret (BOOST_PP_ENUM_PARAMS(m, Param))> &f, const O &o)
|
||||||
|
{
|
||||||
|
return boost::bind(f, o BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM(n, param, _));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@@ -50,6 +50,18 @@ namespace chaiscript
|
|||||||
return p1 % p2;
|
return p1 % p2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Ret, typename P1, typename P2>
|
||||||
|
Ret shift_left(P1 p1, P2 p2)
|
||||||
|
{
|
||||||
|
return p1 << p2;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Ret, typename P1, typename P2>
|
||||||
|
Ret shift_right(P1 p1, P2 p2)
|
||||||
|
{
|
||||||
|
return p1 >> p2;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename P1, typename P2>
|
template<typename P1, typename P2>
|
||||||
P1 &assign(P1 &p1, const P2 &p2)
|
P1 &assign(P1 &p1, const P2 &p2)
|
||||||
{
|
{
|
||||||
@@ -717,6 +729,8 @@ namespace chaiscript
|
|||||||
opers_arithmetic_pod(m);
|
opers_arithmetic_pod(m);
|
||||||
|
|
||||||
m->add(fun(&detail::modulus<int, int, int>), "%");
|
m->add(fun(&detail::modulus<int, int, int>), "%");
|
||||||
|
m->add(fun(&detail::shift_left<int, int, int>), "<<");
|
||||||
|
m->add(fun(&detail::shift_right<int, int, int>), ">>");
|
||||||
|
|
||||||
m->add(fun(&print), "print_string");
|
m->add(fun(&print), "print_string");
|
||||||
m->add(fun(&println), "println_string");
|
m->add(fun(&println), "println_string");
|
||||||
|
@@ -23,6 +23,7 @@
|
|||||||
#include "type_info.hpp"
|
#include "type_info.hpp"
|
||||||
#include "proxy_functions.hpp"
|
#include "proxy_functions.hpp"
|
||||||
#include "proxy_constructors.hpp"
|
#include "proxy_constructors.hpp"
|
||||||
|
#include "dynamic_object.hpp"
|
||||||
#include "../chaiscript_threading.hpp"
|
#include "../chaiscript_threading.hpp"
|
||||||
|
|
||||||
namespace chaiscript
|
namespace chaiscript
|
||||||
@@ -344,11 +345,11 @@ namespace chaiscript
|
|||||||
// Is it in the stack?
|
// Is it in the stack?
|
||||||
for (int i = stack.get<1>().size()-1; i >= 0; --i)
|
for (int i = stack.get<1>().size()-1; i >= 0; --i)
|
||||||
{
|
{
|
||||||
std::map<std::string, Boxed_Value>::const_iterator itr = (stack.get<1>())[i].find(name);
|
std::map<std::string, Boxed_Value>::const_iterator stackitr = (stack.get<1>())[i].find(name);
|
||||||
if (itr != (stack.get<1>())[i].end())
|
if (stackitr != (stack.get<1>())[i].end())
|
||||||
{
|
{
|
||||||
cache[name] = itr->second;
|
cache[name] = stackitr->second;
|
||||||
return itr->second;
|
return stackitr->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,13 +575,23 @@ namespace chaiscript
|
|||||||
/**
|
/**
|
||||||
* return true if the Boxed_Value matches the registered type by name
|
* return true if the Boxed_Value matches the registered type by name
|
||||||
*/
|
*/
|
||||||
bool is_type(const std::string &user_typename, Boxed_Value r) const
|
bool is_type(Boxed_Value r, const std::string &user_typename) const
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return get_type(user_typename) == r.get_type_info();
|
if (get_type(user_typename).bare_equal(r.get_type_info()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
} catch (const std::range_error &) {
|
} catch (const std::range_error &) {
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const Dynamic_Object &d = boxed_cast<const Dynamic_Object &>(r);
|
||||||
|
return d.get_type_name() == user_typename;
|
||||||
|
} catch (const std::bad_cast &) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string type_name(Boxed_Value obj) const
|
std::string type_name(Boxed_Value obj) const
|
||||||
|
@@ -359,7 +359,7 @@ namespace chaiscript
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Proxy_Function_Impl(const boost::function<Func> &f)
|
Proxy_Function_Impl(const boost::function<Func> &f)
|
||||||
: Proxy_Function_Base(build_param_type_list((Func *)(0))),
|
: Proxy_Function_Base(build_param_type_list(static_cast<Func *>(0))),
|
||||||
m_f(f), m_dummy_func(0)
|
m_f(f), m_dummy_func(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@@ -4,15 +4,15 @@
|
|||||||
// and Jason Turner (lefticus@gmail.com)
|
// and Jason Turner (lefticus@gmail.com)
|
||||||
// http://www.chaiscript.com
|
// http://www.chaiscript.com
|
||||||
|
|
||||||
#include <boost/preprocessor.hpp>
|
|
||||||
|
|
||||||
#ifndef BOOST_PP_IS_ITERATING
|
|
||||||
#ifndef __register_function_hpp__
|
#ifndef __register_function_hpp__
|
||||||
#define __register_function_hpp__
|
#define __register_function_hpp__
|
||||||
|
|
||||||
#include "dispatchkit.hpp"
|
#include "dispatchkit.hpp"
|
||||||
|
#include "bind_first.hpp"
|
||||||
#include <boost/function.hpp>
|
#include <boost/function.hpp>
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/function_types/components.hpp>
|
||||||
|
#include <boost/function_types/function_type.hpp>
|
||||||
|
|
||||||
namespace chaiscript
|
namespace chaiscript
|
||||||
{
|
{
|
||||||
@@ -28,75 +28,63 @@ namespace chaiscript
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Proxy_Function fun_helper(const boost::function<T> &f)
|
boost::function<T> mk_boost_fun(const boost::function<T> &f)
|
||||||
{
|
{
|
||||||
return Proxy_Function(new Proxy_Function_Impl<T>(f));
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
boost::function<
|
||||||
|
typename boost::function_types::function_type<boost::function_types::components<T> >::type
|
||||||
|
> mk_boost_fun(T t)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
boost::function<
|
||||||
|
typename boost::function_types::function_type<boost::function_types::components<T> >::type
|
||||||
|
>(t);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Automatically create a get_member helper function for an object
|
|
||||||
* to allow for runtime dispatched access to public data members
|
|
||||||
* for example, the case of std::pair<>::first and std::pair<>::second
|
|
||||||
*/
|
|
||||||
template<typename T, typename Class>
|
template<typename T, typename Class>
|
||||||
Proxy_Function fun_helper(T Class::* m)
|
Proxy_Function fun_helper(T Class::* m)
|
||||||
{
|
{
|
||||||
return fun_helper(boost::function<T& (Class *)>(boost::bind(&detail::get_member<T, Class>, m, _1)));
|
return fun_helper(boost::function<T& (Class *)>(boost::bind(&detail::get_member<T, Class>, m, _1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Proxy_Function fun_helper(const boost::function<T> &f)
|
||||||
|
{
|
||||||
|
return Proxy_Function(new Proxy_Function_Impl<T>(f));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#define BOOST_PP_ITERATION_LIMITS ( 0, 10 )
|
template<typename T>
|
||||||
#define BOOST_PP_FILENAME_1 <chaiscript/dispatchkit/register_function.hpp>
|
Proxy_Function fun(const boost::function<T> &f)
|
||||||
#include BOOST_PP_ITERATE()
|
{
|
||||||
|
return detail::fun_helper(f);
|
||||||
|
}
|
||||||
|
|
||||||
namespace chaiscript
|
|
||||||
{
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Proxy_Function fun(T t)
|
Proxy_Function fun(T t)
|
||||||
{
|
{
|
||||||
return detail::fun_helper(t);
|
return detail::fun_helper(detail::mk_boost_fun(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T, typename Q>
|
||||||
|
Proxy_Function fun(T t, const Q &q)
|
||||||
|
{
|
||||||
|
return detail::fun_helper(bind_first(t, q));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename Q, typename R>
|
||||||
|
Proxy_Function fun(T t, const Q &q, const R &r)
|
||||||
|
{
|
||||||
|
return detail::fun_helper(bind_first(bind_first(t, q), r));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# endif
|
|
||||||
#else
|
|
||||||
# define n BOOST_PP_ITERATION()
|
|
||||||
|
|
||||||
namespace chaiscript
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Register a global function of n parameters with name
|
|
||||||
*/
|
|
||||||
template<typename Ret BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, typename Param)>
|
|
||||||
Proxy_Function fun_helper(Ret (*f)(BOOST_PP_ENUM_PARAMS(n, Param)))
|
|
||||||
{
|
|
||||||
return fun_helper(boost::function<Ret (BOOST_PP_ENUM_PARAMS(n, Param))>(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a class method of n parameters with name
|
|
||||||
*/
|
|
||||||
template<typename Ret, typename Class BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, typename Param)>
|
|
||||||
Proxy_Function fun_helper(Ret (Class::*f)(BOOST_PP_ENUM_PARAMS(n, Param)))
|
|
||||||
{
|
|
||||||
return fun_helper(boost::function<Ret (Class* BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, Param))>(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a const class method of n parameters with name
|
|
||||||
*/
|
|
||||||
template<typename Ret, typename Class BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, typename Param)>
|
|
||||||
Proxy_Function fun_helper(Ret (Class::*f)(BOOST_PP_ENUM_PARAMS(n, Param))const)
|
|
||||||
{
|
|
||||||
return fun_helper(boost::function<Ret (const Class* BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n, Param))>(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
135
include/chaiscript/language/chaiscript_common.hpp
Normal file
135
include/chaiscript/language/chaiscript_common.hpp
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
// This file is distributed under the BSD License.
|
||||||
|
// See "license.txt" for details.
|
||||||
|
// Copyright 2009, Jonathan Turner (jturner@minnow-lang.org)
|
||||||
|
// and Jason Turner (lefticus@gmail.com)
|
||||||
|
// http://www.chaiscript.com
|
||||||
|
|
||||||
|
#ifndef _CHAISCRIPT_COMMON_HPP
|
||||||
|
#define _CHAISCRIPT_COMMON_HPP
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef BOOST_HAS_DECLSPEC
|
||||||
|
#define CHAISCRIPT_MODULE_EXPORT extern "C" __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define CHAISCRIPT_MODULE_EXPORT extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace chaiscript
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
Expression, Comparison, Additive, Multiplicative, Negate, Not, 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 }; };
|
||||||
|
|
||||||
|
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",
|
||||||
|
"Expression", "Comparison", "Additive", "Multiplicative", "Negate", "Not", "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"};
|
||||||
|
|
||||||
|
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<struct Token> 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<TokenPtr> 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<std::string>(where.line) + ", " +
|
||||||
|
boost::lexical_cast<std::string>(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<std::string>(where->start.line) + ", " +
|
||||||
|
boost::lexical_cast<std::string>(where->start.column) + ")"),
|
||||||
|
reason(why), start_position(where->start), end_position(where->end), filename(where->filename) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Eval_Error() throw() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special type indicating a call to 'break'
|
||||||
|
*/
|
||||||
|
struct Break_Loop {
|
||||||
|
TokenPtr location;
|
||||||
|
|
||||||
|
Break_Loop(const TokenPtr where) : location(where) { }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _CHAISCRIPT_COMMON_HPP */
|
||||||
|
|
@@ -10,6 +10,8 @@
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
#include <chaiscript/language/chaiscript_common.hpp>
|
||||||
|
|
||||||
#ifdef _POSIX_VERSION
|
#ifdef _POSIX_VERSION
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#else
|
#else
|
||||||
@@ -18,8 +20,8 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "chaiscript_prelude.hpp"
|
#include <chaiscript/language/chaiscript_prelude.hpp>
|
||||||
#include "chaiscript_parser.hpp"
|
#include <chaiscript/language/chaiscript_parser.hpp>
|
||||||
|
|
||||||
namespace chaiscript
|
namespace chaiscript
|
||||||
{
|
{
|
||||||
@@ -49,6 +51,9 @@ namespace chaiscript
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DLModule(const DLModule &); // Explicitly unimplemented copy constructor
|
||||||
|
DLModule &operator=(const DLModule &); // Explicitly unimplemented assignment operator
|
||||||
|
|
||||||
~DLModule()
|
~DLModule()
|
||||||
{
|
{
|
||||||
dlclose(m_data);
|
dlclose(m_data);
|
||||||
@@ -295,8 +300,8 @@ namespace chaiscript
|
|||||||
/**
|
/**
|
||||||
* Evaluates the given boxed string, used during eval() inside of a script
|
* Evaluates the given boxed string, used during eval() inside of a script
|
||||||
*/
|
*/
|
||||||
const Boxed_Value internal_eval(const std::vector<Boxed_Value> &vals) {
|
const Boxed_Value internal_eval(const std::string &e) {
|
||||||
return do_eval(boxed_cast<std::string>(vals.at(0)), "__EVAL__", true);
|
return do_eval(e, "__EVAL__", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void use(const std::string &filename)
|
void use(const std::string &filename)
|
||||||
@@ -486,42 +491,29 @@ namespace chaiscript
|
|||||||
engine.add_reserved_word("false");
|
engine.add_reserved_word("false");
|
||||||
engine.add_reserved_word("_");
|
engine.add_reserved_word("_");
|
||||||
|
|
||||||
|
|
||||||
add(Bootstrap::bootstrap());
|
add(Bootstrap::bootstrap());
|
||||||
|
|
||||||
engine.add(fun(boost::function<void ()>(boost::bind(&Eval_Engine::dump_system, boost::ref(engine)))), "dump_system");
|
engine.add(fun(&Eval_Engine::dump_system, boost::ref(engine)), "dump_system");
|
||||||
engine.add(fun(boost::function<void (Boxed_Value)>(boost::bind(&Eval_Engine::dump_object, boost::ref(engine), _1))), "dump_object");
|
engine.add(fun(&Eval_Engine::dump_object, boost::ref(engine)), "dump_object");
|
||||||
engine.add(fun(boost::function<bool (Boxed_Value, const std::string &)>(boost::bind(&Eval_Engine::is_type, boost::ref(engine), _2, _1))),
|
engine.add(fun(&Eval_Engine::is_type, boost::ref(engine)), "is_type");
|
||||||
"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(boost::function<std::string (Boxed_Value)>(boost::bind(&Eval_Engine::type_name, boost::ref(engine), _1))),
|
|
||||||
"type_name");
|
|
||||||
engine.add(fun(boost::function<bool (const std::string &)>(boost::bind(&Eval_Engine::function_exists, boost::ref(engine), _1))),
|
|
||||||
"function_exists");
|
|
||||||
|
|
||||||
|
|
||||||
engine.add(fun(boost::function<void (const std::string &)>(
|
typedef void (ChaiScript_System<Eval_Engine>::*load_mod_1)(const std::string&);
|
||||||
boost::bind(static_cast<void (ChaiScript_System<Eval_Engine>::*)(const std::string&)>(
|
typedef void (ChaiScript_System<Eval_Engine>::*load_mod_2)(const std::string&, const std::string&);
|
||||||
&ChaiScript_System<Eval_Engine>::load_module), boost::ref(*this), _1))),
|
|
||||||
"load_module");
|
engine.add(fun(static_cast<load_mod_1>(&ChaiScript_System<Eval_Engine>::load_module), this), "load_module");
|
||||||
|
engine.add(fun(static_cast<load_mod_2>(&ChaiScript_System<Eval_Engine>::load_module), this), "load_module");
|
||||||
|
|
||||||
engine.add(fun(boost::function<void (const std::string &, const std::string &)>(
|
|
||||||
boost::bind(static_cast<void (ChaiScript_System<Eval_Engine>::*)(const std::string&, const std::string&)>(
|
|
||||||
&ChaiScript_System<Eval_Engine>::load_module), boost::ref(*this), _1, _2))),
|
|
||||||
"load_module");
|
|
||||||
|
|
||||||
add(vector_type<std::vector<Boxed_Value> >("Vector"));
|
add(vector_type<std::vector<Boxed_Value> >("Vector"));
|
||||||
add(string_type<std::string>("string"));
|
add(string_type<std::string>("string"));
|
||||||
add(map_type<std::map<std::string, Boxed_Value> >("Map"));
|
add(map_type<std::map<std::string, Boxed_Value> >("Map"));
|
||||||
add(pair_type<std::pair<Boxed_Value, Boxed_Value > >("Pair"));
|
add(pair_type<std::pair<Boxed_Value, Boxed_Value > >("Pair"));
|
||||||
|
|
||||||
engine.add(fun(boost::function<void (const std::string &)>(boost::bind(&ChaiScript_System<Eval_Engine>::use, this, _1))), "use");
|
engine.add(fun(&ChaiScript_System<Eval_Engine>::use, this), "use");
|
||||||
|
engine.add(fun(&ChaiScript_System<Eval_Engine>::internal_eval, this), "eval");
|
||||||
engine.add(Proxy_Function(
|
|
||||||
new Dynamic_Proxy_Function(boost::bind(&ChaiScript_System<Eval_Engine>::internal_eval, boost::ref(*this), _1), 1)), "eval");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
do_eval(chaiscript_prelude, "standard prelude");
|
do_eval(chaiscript_prelude, "standard prelude");
|
||||||
|
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
#include <chaiscript/language/chaiscript_common.hpp>
|
||||||
|
|
||||||
namespace chaiscript
|
namespace chaiscript
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -150,22 +152,8 @@ namespace chaiscript
|
|||||||
*/
|
*/
|
||||||
template <typename Eval_System>
|
template <typename Eval_System>
|
||||||
Boxed_Value eval_single_quoted_string(Eval_System &ss, const TokenPtr &node) {
|
Boxed_Value eval_single_quoted_string(Eval_System &ss, const TokenPtr &node) {
|
||||||
/*
|
|
||||||
if (node->text.size() == 1) {
|
|
||||||
//return Boxed_Value(char(node->text[0]));
|
|
||||||
return const_var(char(node->text[0]));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//return Boxed_Value(char((int)node->text[0] * 0xff + (int)node->text[0]));
|
|
||||||
return const_var(char((int)node->text[0] * 0xff + (int)node->text[0]));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (!node->is_cached) {
|
if (!node->is_cached) {
|
||||||
cache_const(ss, node,
|
cache_const(ss, node, const_var(char(node->text[0])));
|
||||||
node->text.size() == 1 ?
|
|
||||||
const_var(char(node->text[0])) :
|
|
||||||
const_var(char((int)node->text[0] * 0xff + (int)node->text[0])));
|
|
||||||
}
|
}
|
||||||
return node->cached_value;
|
return node->cached_value;
|
||||||
}
|
}
|
||||||
@@ -175,10 +163,10 @@ namespace chaiscript
|
|||||||
*/
|
*/
|
||||||
template <typename Eval_System>
|
template <typename Eval_System>
|
||||||
Boxed_Value eval_equation(Eval_System &ss, const TokenPtr &node) {
|
Boxed_Value eval_equation(Eval_System &ss, const TokenPtr &node) {
|
||||||
unsigned int i;
|
int i;
|
||||||
Boxed_Value retval = eval_token(ss, node->children.back());
|
Boxed_Value retval = eval_token(ss, node->children.back());
|
||||||
if (node->children.size() > 1) {
|
if (node->children.size() > 1) {
|
||||||
for (i = node->children.size()-3; ((int)i) >= 0; i -= 2) {
|
for (i = node->children.size()-3; i >= 0; i -= 2) {
|
||||||
if (node->children[i+1]->text == "=") {
|
if (node->children[i+1]->text == "=") {
|
||||||
Boxed_Value lhs = eval_token(ss, node->children[i]);
|
Boxed_Value lhs = eval_token(ss, node->children[i]);
|
||||||
|
|
||||||
@@ -550,7 +538,6 @@ namespace chaiscript
|
|||||||
*/
|
*/
|
||||||
template <typename Eval_System>
|
template <typename Eval_System>
|
||||||
Boxed_Value eval_dot_access(Eval_System &ss, const TokenPtr &node) {
|
Boxed_Value eval_dot_access(Eval_System &ss, const TokenPtr &node) {
|
||||||
std::vector<std::pair<std::string, Proxy_Function > > fn;
|
|
||||||
Dispatch_Engine::Stack prev_stack = ss.get_stack();
|
Dispatch_Engine::Stack prev_stack = ss.get_stack();
|
||||||
Dispatch_Engine::Stack new_stack = ss.new_stack();
|
Dispatch_Engine::Stack new_stack = ss.new_stack();
|
||||||
unsigned int i, j;
|
unsigned int i, j;
|
||||||
@@ -586,9 +573,7 @@ namespace chaiscript
|
|||||||
throw Eval_Error(ee.reason, node->children[i]);
|
throw Eval_Error(ee.reason, node->children[i]);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
//fn = ss.get_function(fun_name);
|
|
||||||
ss.set_stack(new_stack);
|
ss.set_stack(new_stack);
|
||||||
//retval = dispatch(fn, plb);
|
|
||||||
retval = (*boxed_cast<Const_Proxy_Function >(fn))(plb);
|
retval = (*boxed_cast<Const_Proxy_Function >(fn))(plb);
|
||||||
ss.set_stack(prev_stack);
|
ss.set_stack(prev_stack);
|
||||||
}
|
}
|
||||||
@@ -1115,6 +1100,7 @@ namespace chaiscript
|
|||||||
case (Token_Type::Comparison) :
|
case (Token_Type::Comparison) :
|
||||||
case (Token_Type::Additive) :
|
case (Token_Type::Additive) :
|
||||||
case (Token_Type::Multiplicative) :
|
case (Token_Type::Multiplicative) :
|
||||||
|
case (Token_Type::Shift) :
|
||||||
return eval_comp_add_mul(ss, node);
|
return eval_comp_add_mul(ss, node);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "chaiscript_prelude.hpp"
|
#include "chaiscript_prelude.hpp"
|
||||||
|
|
||||||
@@ -30,6 +31,9 @@ namespace chaiscript
|
|||||||
singleline_comment = "//";
|
singleline_comment = "//";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChaiScript_Parser(const ChaiScript_Parser &); // explicitly unimplemented copy constructor
|
||||||
|
ChaiScript_Parser &operator=(const ChaiScript_Parser &); // explicitly unimplemented assignment operator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints the parsed tokens as a tree
|
* Prints the parsed tokens as a tree
|
||||||
*/
|
*/
|
||||||
@@ -68,7 +72,7 @@ namespace chaiscript
|
|||||||
*/
|
*/
|
||||||
void build_match(Token_Type::Type match_type, int match_start) {
|
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
|
//so we want to take everything to the right of this and make them children
|
||||||
if (match_start != (int)match_stack.size()) {
|
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));
|
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());
|
t->children.assign(match_stack.begin() + (match_start), match_stack.end());
|
||||||
match_stack.erase(match_stack.begin() + (match_start), match_stack.end());
|
match_stack.erase(match_stack.begin() + (match_start), match_stack.end());
|
||||||
@@ -140,6 +144,8 @@ namespace chaiscript
|
|||||||
*/
|
*/
|
||||||
bool Float_() {
|
bool Float_() {
|
||||||
bool retval = false;
|
bool retval = false;
|
||||||
|
std::string::iterator start = input_pos;
|
||||||
|
|
||||||
if ((input_pos != input_end) && (((*input_pos >= '0') && (*input_pos <= '9')) || (*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')) {
|
while ((input_pos != input_end) && (*input_pos >= '0') && (*input_pos <= '9')) {
|
||||||
++input_pos;
|
++input_pos;
|
||||||
@@ -161,10 +167,47 @@ namespace chaiscript
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a number from the input, detecting if it's an integer or floating point
|
* Reads a number from the input, detecting if it's an integer or floating point
|
||||||
*/
|
*/
|
||||||
@@ -172,13 +215,25 @@ namespace chaiscript
|
|||||||
SkipWS();
|
SkipWS();
|
||||||
|
|
||||||
if (!capture) {
|
if (!capture) {
|
||||||
return Float_();
|
return Hex_() || Float_();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::string::iterator start = input_pos;
|
std::string::iterator start = input_pos;
|
||||||
int prev_col = col;
|
int prev_col = col;
|
||||||
int prev_line = line;
|
int prev_line = line;
|
||||||
if ((input_pos != input_end) && (((*input_pos >= '0') && (*input_pos <= '9')) || (*input_pos == '.')) ) {
|
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);
|
||||||
|
int temp_int;
|
||||||
|
ss >> std::hex >> temp_int;
|
||||||
|
|
||||||
|
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_()) {
|
if (Float_()) {
|
||||||
std::string match(start, input_pos);
|
std::string match(start, input_pos);
|
||||||
TokenPtr t(new Token(match, Token_Type::Float, filename, prev_line, prev_col, line, col));
|
TokenPtr t(new Token(match, Token_Type::Float, filename, prev_line, prev_col, line, col));
|
||||||
@@ -187,8 +242,20 @@ namespace chaiscript
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::string match(start, input_pos);
|
std::string match(start, input_pos);
|
||||||
TokenPtr t(new Token(match, Token_Type::Int, filename, prev_line, prev_col, line, col));
|
if ((match.size() > 0) && (match[0] == '0')) {
|
||||||
match_stack.push_back(t);
|
std::stringstream ss(match);
|
||||||
|
int temp_int;
|
||||||
|
ss >> std::oct >> temp_int;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TokenPtr t(new Token(match, Token_Type::Int, filename, prev_line, prev_col, line, col));
|
||||||
|
match_stack.push_back(t);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -211,7 +278,32 @@ namespace chaiscript
|
|||||||
++col;
|
++col;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,10 +321,19 @@ namespace chaiscript
|
|||||||
int prev_col = col;
|
int prev_col = col;
|
||||||
int prev_line = line;
|
int prev_line = line;
|
||||||
if (Id_()) {
|
if (Id_()) {
|
||||||
std::string match(start, input_pos);
|
if (*start == '`') {
|
||||||
TokenPtr t(new Token(match, Token_Type::Id, filename, prev_line, prev_col, line, col));
|
//Id Literal
|
||||||
match_stack.push_back(t);
|
std::string match(start+1, input_pos-1);
|
||||||
return true;
|
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 {
|
else {
|
||||||
return false;
|
return false;
|
||||||
@@ -935,16 +1036,6 @@ namespace chaiscript
|
|||||||
*/
|
*/
|
||||||
bool Try() {
|
bool Try() {
|
||||||
bool retval = false;
|
bool retval = false;
|
||||||
bool is_annotated = 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();
|
int prev_stack_top = match_stack.size();
|
||||||
|
|
||||||
@@ -1206,7 +1297,7 @@ namespace chaiscript
|
|||||||
std::string::iterator prev_pos = input_pos;
|
std::string::iterator prev_pos = input_pos;
|
||||||
|
|
||||||
unsigned int prev_stack_top = match_stack.size();
|
unsigned int prev_stack_top = match_stack.size();
|
||||||
if (Id(true) || Id_Literal()) {
|
if (Id(true)) {
|
||||||
retval = true;
|
retval = true;
|
||||||
bool has_more = true;
|
bool has_more = true;
|
||||||
|
|
||||||
@@ -1326,53 +1417,6 @@ namespace chaiscript
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads an identifier literal of the special form `<name>` from input
|
|
||||||
*/
|
|
||||||
bool Id_Literal() {
|
|
||||||
bool retval = false;
|
|
||||||
|
|
||||||
SkipWS();
|
|
||||||
|
|
||||||
if ((input_pos != input_end) && (*input_pos == '`')) {
|
|
||||||
retval = true;
|
|
||||||
|
|
||||||
int prev_col = col;
|
|
||||||
int prev_line = line;
|
|
||||||
|
|
||||||
++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;
|
|
||||||
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);
|
|
||||||
++input_pos;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a unary prefixed expression from input
|
* Reads a unary prefixed expression from input
|
||||||
*/
|
*/
|
||||||
@@ -1442,11 +1486,11 @@ namespace chaiscript
|
|||||||
|
|
||||||
int prev_stack_top = match_stack.size();
|
int prev_stack_top = match_stack.size();
|
||||||
|
|
||||||
if (Additive()) {
|
if (Shift()) {
|
||||||
retval = true;
|
retval = true;
|
||||||
if (Symbol(">=", true) || Symbol(">", true) || Symbol("<=", true) || Symbol("<", true) || Symbol("==", true) || Symbol("!=", true)) {
|
if (Symbol(">=", true) || Symbol(">", true) || Symbol("<=", true) || Symbol("<", true) || Symbol("==", true) || Symbol("!=", true)) {
|
||||||
do {
|
do {
|
||||||
if (!Additive()) {
|
if (!Shift()) {
|
||||||
throw Eval_Error("Incomplete comparison expression", File_Position(line, col), filename);
|
throw Eval_Error("Incomplete comparison expression", File_Position(line, col), filename);
|
||||||
}
|
}
|
||||||
} while (retval && (Symbol(">=", true) || Symbol(">", true) || Symbol("<=", true) || Symbol("<", true) || Symbol("==", true) || Symbol("!=", true)));
|
} while (retval && (Symbol(">=", true) || Symbol(">", true) || Symbol("<=", true) || Symbol("<", true) || Symbol("==", true) || Symbol("!=", true)));
|
||||||
@@ -1530,6 +1574,30 @@ namespace chaiscript
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Top-level expression, parses a string of binary boolean operators from input
|
||||||
|
*/
|
||||||
|
bool Shift() {
|
||||||
|
bool retval = false;
|
||||||
|
|
||||||
|
int prev_stack_top = match_stack.size();
|
||||||
|
|
||||||
|
if (Additive()) {
|
||||||
|
retval = true;
|
||||||
|
if (Symbol("<<", true) || Symbol(">>", true)) {
|
||||||
|
do {
|
||||||
|
if (!Additive()) {
|
||||||
|
throw Eval_Error("Incomplete shift expression", File_Position(line, col), filename);
|
||||||
|
}
|
||||||
|
} while (retval && (Symbol("<<", true) || Symbol(">>", true)));
|
||||||
|
|
||||||
|
build_match(Token_Type::Shift, prev_stack_top);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top-level expression, parses a string of binary boolean operators from input
|
* Top-level expression, parses a string of binary boolean operators from input
|
||||||
*/
|
*/
|
||||||
@@ -1712,6 +1780,11 @@ namespace chaiscript
|
|||||||
retval = true;
|
retval = true;
|
||||||
saw_eol = true;
|
saw_eol = true;
|
||||||
}
|
}
|
||||||
|
else if (Block()) {
|
||||||
|
has_more = true;
|
||||||
|
retval = true;
|
||||||
|
saw_eol = true;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
has_more = false;
|
has_more = false;
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,9 @@
|
|||||||
#define CODE_STRING(x, y) #x ", " #y
|
#define CODE_STRING(x, y) #x ", " #y
|
||||||
|
|
||||||
#define chaiscript_prelude CODE_STRING(\
|
#define chaiscript_prelude CODE_STRING(\
|
||||||
|
def lt(l, r) { if (call_exists(`<`, l, r)) { l < r } else { type_name(l) < type_name(r) } } \n\
|
||||||
|
def gt(l, r) { if (call_exists(`>`, l, r)) { l > r } else { type_name(l) > type_name(r) } } \n\
|
||||||
|
def eq(l, r) { if (call_exists(`==`, l, r)) { l == r } else { false } } \n\
|
||||||
def new(x) { eval(type_name(x))(); } \n\
|
def new(x) { eval(type_name(x))(); } \n\
|
||||||
def clone(x) : function_exists(type_name(x)) && call_exists(eval(type_name(x)), x) { eval(type_name(x))(x); } \n\
|
def clone(x) : function_exists(type_name(x)) && call_exists(eval(type_name(x)), x) { eval(type_name(x))(x); } \n\
|
||||||
# to_string for Pair()\n\
|
# to_string for Pair()\n\
|
||||||
@@ -89,6 +92,15 @@ def back_inserter(container) { \n\
|
|||||||
bind(push_back, container, _); \n\
|
bind(push_back, container, _); \n\
|
||||||
}\n\
|
}\n\
|
||||||
\n\
|
\n\
|
||||||
|
def contains(container, item, compare_func) : call_exists(range, container) { \n\
|
||||||
|
var t_range = range(container); \n\
|
||||||
|
while (!t_range.empty()) { \n\
|
||||||
|
if ( compare_func(t_range.front(), item) ) { return true; } \n\
|
||||||
|
t_range.pop_front(); \n\
|
||||||
|
} \n\
|
||||||
|
return false; \n\
|
||||||
|
} \n\
|
||||||
|
def contains(container, item) { return contains(container, item, eq) } \n\
|
||||||
def map(container, func, inserter) : call_exists(range, container) { \n\
|
def map(container, func, inserter) : call_exists(range, container) { \n\
|
||||||
var range = range(container); \n\
|
var range = range(container); \n\
|
||||||
while (!range.empty()) { \n\
|
while (!range.empty()) { \n\
|
||||||
@@ -269,37 +281,49 @@ def zip(x, y) { \n\
|
|||||||
zip_with(collate, x, y); \n\
|
zip_with(collate, x, y); \n\
|
||||||
}\n\
|
}\n\
|
||||||
# Returns the position of the second value string in the first value string\n\
|
# Returns the position of the second value string in the first value string\n\
|
||||||
def find(str, substr) { \n\
|
def string::find(substr) : is_type(substr, "string") { \n\
|
||||||
int(find(str, substr, size_t(0))); \n\
|
int(find(this, substr, size_t(0))); \n\
|
||||||
} \n\
|
} \n\
|
||||||
# Returns the position of last match of the second value string in the first value string\n\
|
# Returns the position of last match of the second value string in the first value string\n\
|
||||||
def rfind(str, substr) { \n\
|
def string::rfind(substr) : is_type(substr, "string") { \n\
|
||||||
int(rfind(str, substr, size_t(-1))); \n\
|
int(rfind(this, substr, size_t(-1))); \n\
|
||||||
} \n\
|
} \n\
|
||||||
# Returns the position of the first match of elements in the second value string in the first value string\n\
|
# Returns the position of the first match of elements in the second value string in the first value string\n\
|
||||||
def find_first_of(str, list) { \n\
|
def string::find_first_of(list) : is_type(list, "string") { \n\
|
||||||
int(find_first_of(str, list, size_t(0))); \n\
|
int(find_first_of(this, list, size_t(0))); \n\
|
||||||
} \n\
|
} \n\
|
||||||
# Returns the position of the last match of elements in the second value string in the first value string\n\
|
# Returns the position of the last match of elements in the second value string in the first value string\n\
|
||||||
def find_last_of(str, list) { \n\
|
def string::find_last_of(list) : is_type(list, "string") { \n\
|
||||||
int(find_last_of(str, list, size_t(-1))); \n\
|
int(find_last_of(this, list, size_t(-1))); \n\
|
||||||
} \n\
|
} \n\
|
||||||
# Returns the position of the first non-matching element in the second value string in the first value string\n\
|
# Returns the position of the first non-matching element in the second value string in the first value string\n\
|
||||||
def find_first_not_of(str, list) { \n\
|
def string::find_first_not_of(list) : is_type(list, "string") { \n\
|
||||||
int(find_first_not_of(str, list, size_t(0))); \n\
|
int(find_first_not_of(this, list, size_t(0))); \n\
|
||||||
} \n\
|
} \n\
|
||||||
# Returns the position of the last non-matching element in the second value string in the first value string\n\
|
# Returns the position of the last non-matching element in the second value string in the first value string\n\
|
||||||
def find_last_not_of(str, list) { \n\
|
def string::find_last_not_of(list) : is_type(list, "string") { \n\
|
||||||
int(find_last_not_of(str, list, size_t(-1))); \n\
|
int(find_last_not_of(this, list, size_t(-1))); \n\
|
||||||
} \n\
|
} \n\
|
||||||
def ltrim(str) { \n\
|
def string::ltrim() { \n\
|
||||||
drop_while(str, fun(x) { x == ' ' || x == '\t' }); \n\
|
drop_while(this, fun(x) { x == ' ' || x == '\t' }); \n\
|
||||||
} \n\
|
} \n\
|
||||||
def rtrim(str) { \n\
|
def string::rtrim() { \n\
|
||||||
reverse(drop_while(reverse(str), fun(x) { x == ' ' || x == '\t' })); \n\
|
reverse(drop_while(reverse(this), fun(x) { x == ' ' || x == '\t' })); \n\
|
||||||
} \n\
|
} \n\
|
||||||
def trim(str) { \n\
|
def string::trim() { \n\
|
||||||
ltrim(rtrim(str)); \n\
|
ltrim(rtrim(this)); \n\
|
||||||
} \
|
} \n\
|
||||||
|
def find(container, value, compare_func) : call_exists(range, container) && is_type(compare_func, "function") { \n\
|
||||||
|
var range = range(container); \n\
|
||||||
|
while (!range.empty()) { \n\
|
||||||
|
if (compare_func(range.front(), value)) { \n\
|
||||||
|
return range; \n\
|
||||||
|
} else { \n\
|
||||||
|
range.pop_front(); \n\
|
||||||
|
} \n\
|
||||||
|
} \n\
|
||||||
|
return range; \n\
|
||||||
|
} \n\
|
||||||
|
def find(container, value) { return find(container, value, eq) } \
|
||||||
)
|
)
|
||||||
#endif /* CHAISCRIPT_PRELUDE_HPP_ */
|
#endif /* CHAISCRIPT_PRELUDE_HPP_ */
|
||||||
|
@@ -22,6 +22,10 @@ void log(const std::string &module, const std::string &msg)
|
|||||||
std::cout << "[" << boost::posix_time::microsec_clock::local_time() << "] <" << module << "> " << msg << std::endl;
|
std::cout << "[" << boost::posix_time::microsec_clock::local_time() << "] <" << module << "> " << msg << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bound_log(const std::string &msg)
|
||||||
|
{
|
||||||
|
log(msg);
|
||||||
|
}
|
||||||
|
|
||||||
void hello_world(const chaiscript::Boxed_Value &o)
|
void hello_world(const chaiscript::Boxed_Value &o)
|
||||||
{
|
{
|
||||||
@@ -71,6 +75,9 @@ int main(int argc, char *argv[]) {
|
|||||||
System system;
|
System system;
|
||||||
chai.add(var(&system), "system");
|
chai.add(var(&system), "system");
|
||||||
|
|
||||||
|
//Add a bound callback method
|
||||||
|
chai.add(fun(&System::add_callback, system), "add_callback_bound");
|
||||||
|
|
||||||
//Register the two methods of the System structure.
|
//Register the two methods of the System structure.
|
||||||
chai.add(fun(&System::add_callback), "add_callback");
|
chai.add(fun(&System::add_callback), "add_callback");
|
||||||
chai.add(fun(&System::do_callbacks), "do_callbacks");
|
chai.add(fun(&System::do_callbacks), "do_callbacks");
|
||||||
@@ -103,7 +110,7 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
//Finally, it is possible to register any boost::function as a system function, in this
|
//Finally, it is possible to register any boost::function as a system function, in this
|
||||||
//way, we can, for instance add a bound member function to the system
|
//way, we can, for instance add a bound member function to the system
|
||||||
chai.add(fun(boost::function<void ()>(boost::bind(&System::do_callbacks, boost::ref(system), "Bound Test"))), "do_callbacks");
|
chai.add(fun(&System::do_callbacks, boost::ref(system), "Bound Test"), "do_callbacks");
|
||||||
|
|
||||||
//Call bound version of do_callbacks
|
//Call bound version of do_callbacks
|
||||||
chai("do_callbacks()");
|
chai("do_callbacks()");
|
||||||
@@ -145,6 +152,7 @@ int main(int argc, char *argv[]) {
|
|||||||
// Test ability to register a function that excepts a shared_ptr version of a type
|
// Test ability to register a function that excepts a shared_ptr version of a type
|
||||||
chai("take_shared_ptr(\"Hello World as a shared_ptr\");");
|
chai("take_shared_ptr(\"Hello World as a shared_ptr\");");
|
||||||
|
|
||||||
|
chai.add(fun(&bound_log, std::string("Msg")), "BoundFun");
|
||||||
|
|
||||||
//Dynamic objects test
|
//Dynamic objects test
|
||||||
chai.add(chaiscript::Proxy_Function(new Dynamic_Object_Function("TestType", fun(&hello_world))), "hello_world");
|
chai.add(chaiscript::Proxy_Function(new Dynamic_Object_Function("TestType", fun(&hello_world))), "hello_world");
|
||||||
|
1
unittests/block_start.chai
Normal file
1
unittests/block_start.chai
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{print("hello")}
|
1
unittests/block_start.txt
Normal file
1
unittests/block_start.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
hello
|
2
unittests/number_formats.chai
Normal file
2
unittests/number_formats.chai
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
print(012)
|
||||||
|
print(0x1f)
|
2
unittests/number_formats.txt
Normal file
2
unittests/number_formats.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
10
|
||||||
|
31
|
8
unittests/operator_overload.chai
Normal file
8
unittests/operator_overload.chai
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
def Bob::`+`(y) { this.x + y.x }
|
||||||
|
def Bob::Bob() { }
|
||||||
|
attr Bob::x
|
||||||
|
var b = Bob()
|
||||||
|
var c = Bob()
|
||||||
|
b.x = 4
|
||||||
|
c.x = 5
|
||||||
|
print(b+c)
|
1
unittests/operator_overload.txt
Normal file
1
unittests/operator_overload.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
9
|
8
unittests/operator_overload2.chai
Normal file
8
unittests/operator_overload2.chai
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
def Bob::Bob() { }
|
||||||
|
attr Bob::x
|
||||||
|
def `-`(a, b) : is_type(a, "Bob") && is_type(b, "Bob") { a.x - b.x }
|
||||||
|
var b = Bob()
|
||||||
|
var c = Bob()
|
||||||
|
b.x = 4
|
||||||
|
c.x = 5
|
||||||
|
print(b-c)
|
1
unittests/operator_overload2.txt
Normal file
1
unittests/operator_overload2.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
-1
|
5
unittests/range_contains.chai
Normal file
5
unittests/range_contains.chai
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
var v = [1,2,"hi", "world", 5.5]
|
||||||
|
print(v.contains(5.5));
|
||||||
|
print(v.contains(0));
|
||||||
|
print(v.contains(1, lt));
|
||||||
|
print(v.contains(2, `==`));
|
4
unittests/range_contains.txt
Normal file
4
unittests/range_contains.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
true
|
||||||
|
false
|
||||||
|
false
|
||||||
|
true
|
5
unittests/range_find.chai
Normal file
5
unittests/range_find.chai
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
var v = [2, 1, "Hi", 5.5]
|
||||||
|
var r = v.find("Hi");
|
||||||
|
print(r);
|
||||||
|
var r2 = v.find(2, `<`);
|
||||||
|
print(r2);
|
2
unittests/range_find.txt
Normal file
2
unittests/range_find.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[Hi, 5.5]
|
||||||
|
[1, Hi, 5.5]
|
1
unittests/shift.chai
Normal file
1
unittests/shift.chai
Normal file
@@ -0,0 +1 @@
|
|||||||
|
print(2 << 2)
|
1
unittests/shift.txt
Normal file
1
unittests/shift.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
8
|
Reference in New Issue
Block a user