//////////////////////////////////////////////////////////////////////////////// /// LANGUAGE OPTIONS /////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Comment this define out to drop support for libm functions #ifdef HAS_LIBM #include #else #define NO_LIBM_SUPPORT "no libm support" #endif // Comment this define out to drop support for standard library functions. // This allows the program to run without a runtime. #define USE_STD #ifdef USE_STD #include #include #include #include #include std::string read_file_contents(std::string filename) { std::ifstream f; f.open(filename.c_str()); if (!f) throw std::runtime_error("could not open file"); f.seekg(0, std::ios::end); std::string contents; contents.reserve(f.tellg()); f.seekg(0, std::ios::beg); contents.assign(std::istreambuf_iterator(f), std::istreambuf_iterator()); f.close(); return contents; } #else #define NO_STD "no standard library support" #endif //////////////////////////////////////////////////////////////////////////////// /// REQUIRED INCLUDES ////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include "csvparser.h" #include "sslclient.h" //////////////////////////////////////////////////////////////////////////////// /// ERROR MESSAGES ///////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// #define TOO_FEW_ARGS "too few arguments to function" #define TOO_MANY_ARGS "too many arguments to function" #define INVALID_ARGUMENT "invalid argument" #define MISMATCHED_TYPES "mismatched types" #define CALL_NON_FUNCTION "called non-function" #define UNKNOWN_ERROR "unknown exception" #define INVALID_LAMBDA "invalid lambda" #define INVALID_BIN_OP "invalid binary operation" #define INVALID_ORDER "cannot order expression" #define BAD_CAST "cannot cast" #define ATOM_NOT_DEFINED "atom not defined" #define EVAL_EMPTY_LIST "evaluated empty list" #define INTERNAL_ERROR "interal virtual machine error" #define INDEX_OUT_OF_RANGE "index out of range" #define MALFORMED_PROGRAM "malformed program" //////////////////////////////////////////////////////////////////////////////// /// TYPE NAMES ///////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// #define STRING_TYPE "string" #define INT_TYPE "int" #define FLOAT_TYPE "float" #define UNIT_TYPE "unit" #define FUNCTION_TYPE "function" #define ATOM_TYPE "atom" #define QUOTE_TYPE "quote" #define LIST_TYPE "list" //////////////////////////////////////////////////////////////////////////////// /// HELPER FUNCTIONS /////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Convert an object to a string using a stringstream conveniently #define to_string( x ) static_cast((std::ostringstream() << std::dec << x )).str() // Replace a substring with a replacement string in a source string void replace_substring(std::string &src, std::string substr, std::string replacement) { size_t i=0; for (i=src.find(substr, i); i!=std::string::npos; i=src.find(substr, i)) { src.replace(i, substr.size(), replacement); i += replacement.size(); } } // Is this character a valid lisp symbol character bool is_symbol(char ch) { return (isalpha(ch) || ispunct(ch)) && ch != '(' && ch != ')' && ch != '"' && ch != '\''; } // std::regex int_underscored_regex("[0-9][0-9_]+[0-9]"); std::regex int_regex("[0-9]+"); std::regex double_regex("[0-9]+\\.[0-9]+"); // Is string representing int value bool is_string_int(const std::string &str) { return std::regex_match(str, int_regex); } // Is string representing float value bool is_string_float(const std::string &str) { return std::regex_match(str, double_regex); } //////////////////////////////////////////////////////////////////////////////// /// LISP CONSTRUCTS //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Forward declaration for Environment class definition class Value; // An instance of a function's scope. class Environment { public: // Default constructor Environment() : parent_scope(NULL) {} // Does this environment, or its parent environment, // have this atom in scope? // This is only used to determine which atoms to capture when // creating a lambda function. bool has(std::string name) const; // Get the value associated with this name in this scope Value get(std::string name) const; // Set the value associated with this name in this scope void set(std::string name, Value value); void combine(Environment const &other); void set_parent_scope(Environment *parent) { parent_scope = parent; } // Output this scope in readable form to a stream. friend std::ostream &operator<<(std::ostream &os, Environment const &v); private: // The definitions in the scope. std::map defs; Environment *parent_scope; }; // An exception thrown by the lisp class Error { public: // Create an error with the value that caused the error, // the scope where the error was found, and the message. Error(const Value &v, Environment const &env, const char *msg); // Copy constructor is needed to prevent double frees Error(Error const &other); ~Error(); // Get the printable error description. std::string description(); private: Value *cause; Environment env; const char *msg; }; // The type for a builtin function, which takes a list of values, // and the environment to run the function in. typedef Value (*Builtin)(std::vector, Environment &); class Value { public: //////////////////////////////////////////////////////////////////////////////// /// CONSTRUCTORS /////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Constructs a unit value Value() : type(UNIT) {} // Constructs an integer Value(int i) : type(INT) { stack_data.i = i; } // Constructs a floating point value Value(double f) : type(FLOAT) { stack_data.f = f; } // Constructs a list Value(const std::vector &list) : type(LIST), list(list) {} // Construct a quoted value static Value quote(Value quoted) { Value result; result.type = QUOTE; // The first position in the list is // used to store the quoted expression. result.list.push_back(quoted); return result; } // Construct an atom static Value atom(const std::string &s) { Value result; result.type = ATOM; // We use the `str` member to store the atom. result.str = s; return result; } // Construct a string static Value string(const std::string &s) { Value result; result.type = STRING; // We use the `str` member to store the string. result.str = s; return result; } // Construct a lambda function Value(const std::vector ¶ms, Value ret, Environment const &env) : type(LAMBDA) { // We store the params and the result in the list member // instead of having dedicated members. This is to save memory. list.push_back(Value(params)); list.push_back(ret); // Lambdas capture only variables that they know they will use. std::vector used_atoms = ret.get_used_atoms(); for (size_t i=0; i get_used_atoms() { std::vector result, tmp; switch (type) { case QUOTE: // The data for a quote is stored in the // first slot of the list member. return list[0].get_used_atoms(); case ATOM: // If this is an atom, add it to the list // of used atoms in this expression. result.push_back(as_atom()); return result; case LAMBDA: // If this is a lambda, get the list of used atoms in the body // of the expression. return list[1].get_used_atoms(); case LIST: // If this is a list, add each of the atoms used in all // of the elements in the list. for (size_t i=0; i args, Environment &env); // Evaluate this value as lisp code. Value eval(Environment &env); bool is_number() const { return type == INT || type == FLOAT; } // Get the "truthy" boolean value of this value. bool as_bool() const { return *this != Value(0); } // Get this item's integer value int as_int() const { return cast_to_int().stack_data.i; } // Get this item's floating point value double as_float() const { return cast_to_int().stack_data.f; } // Get this item's string value std::string as_string() const { // If this item is not a string, throw a cast error. if (type != STRING) throw Error(*this, Environment(), BAD_CAST); return str; } // Get this item's atom value std::string as_atom() const { // If this item is not an atom, throw a cast error. if (type != ATOM) throw Error(*this, Environment(), BAD_CAST); return str; } // Get this item's list value std::vector as_list() const { // If this item is not a list, throw a cast error. if (type != LIST) throw Error(*this, Environment(), BAD_CAST); return list; } // Push an item to the end of this list void push(Value val) { // If this item is not a list, you cannot push to it. // Throw an error. if (type != LIST) throw Error(*this, Environment(), MISMATCHED_TYPES); list.push_back(val); } // Push an item from the end of this list Value pop() { // If this item is not a list, you cannot pop from it. // Throw an error. if (type != LIST) throw Error(*this, Environment(), MISMATCHED_TYPES); // Remember the last item in the list Value result = list[list.size()-1]; // Remove it from this instance list.pop_back(); // Return the remembered value return result; } //////////////////////////////////////////////////////////////////////////////// /// TYPECASTING METHODS //////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Cast this to an integer value Value cast_to_int() const { switch (type) { case INT: return *this; case FLOAT: return Value(int(stack_data.f)); // Only ints and floats can be cast to an int default: throw Error(*this, Environment(), BAD_CAST); } } // Cast this to a floating point value Value cast_to_float() const { switch (type) { case FLOAT: return *this; case INT: return Value(float(stack_data.i)); // Only ints and floats can be cast to a float default: throw Error(*this, Environment(), BAD_CAST); } } //////////////////////////////////////////////////////////////////////////////// /// COMPARISON OPERATIONS ////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// bool operator==(Value other) const { // If either of these values are floats, promote the // other to a float, and then compare for equality. if (type == FLOAT && other.type == INT) return *this == other.cast_to_float(); else if (type == INT && other.type == FLOAT) return this->cast_to_float() == other; // If the values types aren't equal, then they cannot be equal. else if (type != other.type) return false; switch (type) { case FLOAT: return stack_data.f == other.stack_data.f; case INT: return stack_data.i == other.stack_data.i; case BUILTIN: return stack_data.b == other.stack_data.b; case STRING: case ATOM: // Both atoms and strings store their // data in the str member. return str == other.str; case LAMBDA: case LIST: // Both lambdas and lists store their // data in the list member. return list == other.list; case QUOTE: // The values for quotes are stored in the // first slot of the list member. return list[0] == other.list[0]; default: return true; } } bool operator!=(const Value &other) const { return !(*this == other); } //////////////////////////////////////////////////////////////////////////////// /// ORDERING OPERATIONS //////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// bool operator>=(const Value &other) const { return !(*this < other); } bool operator<=(const Value &other) const { return (*this == other) || (*this < other); } bool operator>(const Value &other) const { return !(*this <= other); } bool operator<(const Value &other) const { // Other type must be a float or an int if (other.type != FLOAT && other.type != INT) throw Error(*this, Environment(), INVALID_BIN_OP); switch (type) { case FLOAT: // If this is a float, promote the other value to a float and compare. return stack_data.f < other.cast_to_float().stack_data.f; case INT: // If the other value is a float, promote this value to a float and compare. if (other.type == FLOAT) return cast_to_float().stack_data.f < other.stack_data.f; // Otherwise compare the integer values else return stack_data.i < other.stack_data.i; default: // Only allow comparisons between integers and floats throw Error(*this, Environment(), INVALID_ORDER); } } //////////////////////////////////////////////////////////////////////////////// /// ARITHMETIC OPERATIONS ////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // This function adds two lisp values, and returns the lisp value result. Value operator+(const Value &other) const { // If the other value's type is the unit type, // don't even bother continuing. // Unit types consume all arithmetic operations. if (other.type == UNIT) return other; // Other type must be a float or an int if ((is_number() || other.is_number()) && !(is_number() && other.is_number())) throw Error(*this, Environment(), INVALID_BIN_OP); switch (type) { case FLOAT: // If one is a float, promote the other by default and do // float addition. return Value(stack_data.f + other.cast_to_float().stack_data.f); case INT: // If the other type is a float, go ahead and promote this expression // before continuing with the addition. if (other.type == FLOAT) return Value(cast_to_float() + other.stack_data.f); // Otherwise, do integer addition. else return Value(stack_data.i + other.stack_data.i); case STRING: // If the other value is also a string, do the concat if (other.type == STRING) return Value::string(str + other.str); // We throw an error if we try to concat anything of non-string type else throw Error(*this, Environment(), INVALID_BIN_OP); case LIST: // If the other value is also a list, do the concat if (other.type == LIST) { // Maintain the value that will be returned Value result = *this; // Add each item in the other list to the end of this list for (size_t i=0; i"; case UNIT: return "@"; default: // We don't know how to display whatever type this is. // This isn't the users fault, this is just unhandled. // This should never be reached. throw Error(*this, Environment(), INTERNAL_ERROR); } } std::string debug() const { std::string result; switch (type) { case QUOTE: return "'" + list[0].debug(); case ATOM: return str; case INT: return to_string(stack_data.i); case FLOAT: return to_string(stack_data.f); case STRING: for (size_t i=0; i"; case UNIT: return "@"; default: // We don't know how to debug whatever type this is. // This isn't the users fault, this is just unhandled. // This should never be reached. throw Error(*this, Environment(), INTERNAL_ERROR); } } friend std::ostream &operator<<(std::ostream &os, Value const &v) { return os << v.display(); } private: enum { QUOTE, ATOM, INT, FLOAT, LIST, STRING, LAMBDA, BUILTIN, UNIT } type; union { int i; double f; Builtin b; } stack_data; std::string str; std::vector list; Environment lambda_scope; }; Error::Error(const Value &v, Environment const &env, const char *msg) : env(env), msg(msg) { cause = new Value; *cause = v; } Error::Error(Error const &other) : env(other.env), msg(other.msg) { cause = new Value(*other.cause); } Error::~Error() { delete cause; } std::string Error::description() { return "error: the expression `" + cause->debug() + "` failed in scope " + to_string(env) + " with message \"" + msg + "\""; } void Environment::combine(Environment const &other) { // Normally, I would use the `insert` method of the `map` class, // but it doesn't overwrite previously declared values for keys. std::map::const_iterator itr = other.defs.begin(); for (; itr!=other.defs.end(); itr++) { // Iterate through the keys and assign each value. defs[itr->first] = itr->second; } } std::ostream &operator<<(std::ostream &os, Environment const &e) { std::map::const_iterator itr = e.defs.begin(); os << "{ "; for (; itr != e.defs.end(); itr++) { os << '\'' << itr->first << "' : " << itr->second.debug() << ", "; } return os << "}"; } void Environment::set(std::string name, Value value) { defs[name] = value; } Value Value::apply(std::vector args, Environment &env) { Environment e; std::vector params; switch (type) { case LAMBDA: // Get the list of parameter atoms params = list[0].list; if (params.size() != args.size()) throw Error(Value(args), env, args.size() > params.size()? TOO_MANY_ARGS : TOO_FEW_ARGS ); // Get the captured scope from the lambda e = lambda_scope; // And make this scope the parent scope e.set_parent_scope(&env); // Iterate through the list of parameters and // insert the arguments into the scope. for (size_t i=0; i args; Value function; Environment e; switch (type) { case QUOTE: return list[0]; case ATOM: return env.get(str); case LIST: if (list.size() < 1) throw Error(*this, env, EVAL_EMPTY_LIST); args = std::vector(list.begin() + 1, list.end()); // Only evaluate our arguments if it's not builtin! // Builtin functions can be special forms, so we // leave them to evaluate their arguments. function = list[0].eval(env); if (!function.is_builtin()) for (size_t i=0; i()); while (s[ptr] != ')') result.push(parse(s, ptr)); skip_whitespace(s, ++ptr); return result; } else if (isdigit(s[ptr]) || (s[ptr] == '-' && isdigit(s[ptr + 1]))) { // If this is a number bool negate = s[ptr] == '-'; if (negate) ptr++; int save_ptr = ptr; while (isdigit(s[ptr]) || s[ptr] == '.') ptr++; std::string n = s.substr(save_ptr, ptr); skip_whitespace(s, ptr); if (n.find('.') != std::string::npos) return Value((negate? -1 : 1) * atof(n.c_str())); else return Value((negate? -1 : 1) * atoi(n.c_str())); } else if (s[ptr] == '\"') { // If this is a string int n = 1; while (s[ptr + n] != '\"') { if (ptr + n >= int(s.length())) throw std::runtime_error(MALFORMED_PROGRAM); if (s[ptr + n] == '\\') n++; n++; } std::string x = s.substr(ptr+1, n-1); ptr += n+1; skip_whitespace(s, ptr); // Iterate over the characters in the string, and // replace escaped characters with their intended values. for (size_t i=0; i parse(std::string s) { int i=0, last_i=-1; std::vector result; // While the parser is making progress (while the pointer is moving right) // and the pointer hasn't reached the end of the string, while (last_i != i && i <= int(s.length()-1)) { // Parse another expression and add it to the list. last_i = i; result.push_back(parse(s, i)); } // If the whole string wasn't parsed, the program must be bad. if (i < int(s.length())) throw std::runtime_error(MALFORMED_PROGRAM); // Return the list of values parsed. return result; } // Execute code in an environment Value run(const std::string &code, Environment &env) { // Parse the code std::vector parsed = parse(code); // Iterate over the expressions and evaluate them // in this environment. for (size_t i=0; i &args, Environment &env) { for (size_t i=0; i args, Environment &env) { if (args.size() < 2) throw Error(Value("lambda", lambda), env, TOO_FEW_ARGS); if (args[0].get_type_name() != LIST_TYPE) throw Error(Value("lambda", lambda), env, INVALID_LAMBDA); return Value(args[0].as_list(), args[1], env); } // if-else (SPECIAL FORM) Value if_then_else(std::vector args, Environment &env) { if (args.size() != 3) throw Error(Value("if", if_then_else), env, args.size() > 3? TOO_MANY_ARGS : TOO_FEW_ARGS); if (args[0].eval(env).as_bool()) return args[1].eval(env); else return args[2].eval(env); } // Define a variable with a value (SPECIAL FORM) Value define(std::vector args, Environment &env) { if (args.size() != 2) throw Error(Value("define", define), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); Value result = args[1].eval(env); env.set(args[0].display(), result); return result; } // Define a function with parameters and a result expression (SPECIAL FORM) Value defun(std::vector args, Environment &env) { if (args.size() != 3) throw Error(Value("defun", defun), env, args.size() > 3? TOO_MANY_ARGS : TOO_FEW_ARGS); if (args[1].get_type_name() != LIST_TYPE) throw Error(Value("defun", defun), env, INVALID_LAMBDA); Value f = Value(args[1].as_list(), args[2], env); env.set(args[0].display(), f); return f; } // Loop over a list of expressions with a condition (SPECIAL FORM) Value while_loop(std::vector args, Environment &env) { Value acc; while (args[0].eval(env).as_bool()) { for (size_t i=1; i args, Environment &env) { Value acc; std::vector list = args[1].eval(env).as_list(); for (size_t i=0; i args, Environment &env) { Value acc; for (size_t i=0; i args, Environment &env) { Environment e = env; Value acc; for (size_t i=0; i args, Environment &env) { std::vector v; for (size_t i=0; i args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); std::exit(args.size() < 1? 0 : args[0].cast_to_int().as_int()); return Value(); } // Print several values and return the last one Value print(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() < 1) throw Error(Value("print", print), env, TOO_FEW_ARGS); Value acc; for (size_t i=0; i args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() > 1) throw Error(Value("input", input), env, TOO_MANY_ARGS); if (!args.empty()) std::cout << args[0]; std::string s; std::getline(std::cin, s); return Value::string(s); } // Get a random number between two numbers inclusively Value random(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 2) throw Error(Value("random", random), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); int low = args[0].as_int(), high = args[1].as_int(); return Value(rand()%(high-low+1) + low); } // Get the contents of a file Value parse_csv(std::vector args, Environment &env) { eval_args(args, env); // TODO add support for more params specifying options if (args.size() != 1) throw Error(Value("read-csv", parse_csv), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); // PERF optimize it for memory usage and performance CsvParser csv(true); std::vector< std::vector > parsed_data; // TODO some default size here csv.parseCSV(args[0].as_string(), parsed_data); int rows = parsed_data.size(); int cols = rows > 0 ? parsed_data[0].size() : 0; std::vector result; if (rows > 0 && cols > 0) { for (int r = 0; r < rows; r++) { std::vector row; for (int c = 0; c < cols; c++) { std::string value = parsed_data[r][c]; if (is_string_int(value)) { row.push_back(Value(stoi(value))); } if (is_string_float(value)) { row.push_back(Value(std::stod(value))); } else { row.push_back(Value::string(value)); } } result.push_back(row); } } return Value(result); } // Get the contents of a file Value read_file(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 1) throw Error(Value("read-file", read_file), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); return Value::string(read_file_contents(args[0].as_string())); } // Write a string to a file Value write_file(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 2) throw Error(Value("write-file", write_file), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); std::ofstream f; // The first argument is the file name f.open(args[0].as_string().c_str()); // The second argument is the contents of the file to write Value result = Value((f << args[1].as_string())? 1 : 0); f.close(); return result; } // Read URL to (code content) Value read_url(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); // PERF optimize it for memory usage and performance // TODO handle second parameter (headers) if (args.size() != 1) throw Error(Value("read_url", write_file), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); std::unordered_map headers = {}; HttpClient client; if (args.size() == 2) { // do magick here // for (auto i = map.begin(); i != map.end(); ++i) { // headers[i->first] = i->second.getString(); // } } std::pair result = client.doGetRequest(args[0].as_string(), headers); std::vector lst; lst.push_back(Value(result.first)); lst.push_back(Value::string(result.second)); return lst; } // Read a file and execute its code Value include(std::vector args, Environment &env) { // Import is technically not a special form, it's more of a macro. // We can evaluate our arguments. eval_args(args, env); if (args.size() != 1) throw Error(Value("include", include), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); Environment e; Value result = run(read_file_contents(args[0].as_string()), e); env.combine(e); return result; } #endif // Evaluate a value as code Value eval(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 1) throw Error(Value("eval", eval), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); else return args[0].eval(env); } // Create a list of values Value list(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); return Value(args); } // Sum multiple values Value sum(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() < 2) throw Error(Value("+", sum), env, TOO_FEW_ARGS); Value acc = args[0]; for (size_t i=1; i args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 2) throw Error(Value("-", subtract), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); return args[0] - args[1]; } // Multiply several values Value product(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() < 2) throw Error(Value("*", product), env, TOO_FEW_ARGS); Value acc = args[0]; for (size_t i=1; i args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 2) throw Error(Value("/", divide), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); return args[0] / args[1]; } // Get the remainder of values Value remainder(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 2) throw Error(Value("%", remainder), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); return args[0] % args[1]; } // Are two values equal? Value eq(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 2) throw Error(Value("=", eq), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); return Value(int(args[0] == args[1])); } // Are two values not equal? Value neq(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 2) throw Error(Value("!=", neq), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); return Value(int(args[0] != args[1])); } // Is one number greater than another? Value greater(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 2) throw Error(Value(">", greater), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); return Value(int(args[0] > args[1])); } // Is one number less than another? Value less(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 2) throw Error(Value("<", less), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); return Value(int(args[0] < args[1])); } // Is one number greater than or equal to another? Value greater_eq(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 2) throw Error(Value(">=", greater_eq), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); return Value(int(args[0] >= args[1])); } // Is one number less than or equal to another? Value less_eq(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 2) throw Error(Value("<=", less_eq), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); return Value(int(args[0] <= args[1])); } // Get the type name of a value Value get_type_name(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 1) throw Error(Value("type", get_type_name), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); return Value::string(args[0].get_type_name()); } // Cast an item to a float Value cast_to_float(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 1) throw Error(Value(FLOAT_TYPE, cast_to_float), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); return args[0].cast_to_float(); } // Cast an item to an int Value cast_to_int(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 1) throw Error(Value(INT_TYPE, cast_to_int), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); return args[0].cast_to_int(); } // Index a list Value index(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 2) throw Error(Value("index", index), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); std::vector list = args[0].as_list(); int i = args[1].as_int(); if (list.empty() || i >= list.size()) throw Error(list, env, INDEX_OUT_OF_RANGE); return list[i]; } // Insert a value into a list Value insert(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 3) throw Error(Value("insert", insert), env, args.size() > 3? TOO_MANY_ARGS : TOO_FEW_ARGS); std::vector list = args[0].as_list(); int i = args[1].as_int(); if (i > list.size()) throw Error(list, env, INDEX_OUT_OF_RANGE); list.insert(list.begin() + args[1].as_int(), args[2]); return Value(list); } // Remove a value at an index from a list Value remove(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 2) throw Error(Value("remove", remove), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); std::vector list = args[0].as_list(); int i = args[1].as_int(); if (list.empty() || i >= list.size()) throw Error(list, env, INDEX_OUT_OF_RANGE); list.erase(list.begin() + i); return Value(list); } // Get the length of a list Value len(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 1) throw Error(Value("len", len), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS ); return Value(int(args[0].as_list().size())); } // Add an item to the end of a list Value push(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() == 0) throw Error(Value("push", push), env, TOO_FEW_ARGS); for (size_t i=1; i args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 1) throw Error(Value("pop", pop), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); return args[0].pop(); } Value head(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 1) throw Error(Value("head", head), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); std::vector list = args[0].as_list(); if (list.empty()) throw Error(Value("head", head), env, INDEX_OUT_OF_RANGE); return list[0]; } Value tail(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 1) throw Error(Value("tail", tail), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); std::vector result, list = args[0].as_list(); for (size_t i = 1; i args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 1) throw Error(Value("parse", parse), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); if (args[0].get_type_name() != STRING_TYPE) throw Error(args[0], env, INVALID_ARGUMENT); std::vector parsed = ::parse(args[0].as_string()); // if (parsed.size() == 1) // return parsed[0]; // else return Value(parsed); return Value(parsed); } Value replace(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 3) throw Error(Value("replace", replace), env, args.size() > 3? TOO_MANY_ARGS : TOO_FEW_ARGS); std::string src = args[0].as_string(); replace_substring(src, args[1].as_string(), args[2].as_string()); return Value::string(src); } Value display(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 1) throw Error(Value("display", display), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); return Value::string(args[0].display()); } Value debug(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); if (args.size() != 1) throw Error(Value("debug", debug), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); return Value::string(args[0].debug()); } Value map_list(std::vector args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); std::vector result, l=args[1].as_list(), tmp; for (size_t i=0; i args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); std::vector result, l=args[1].as_list(), tmp; for (size_t i=0; i args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); std::vector l=args[2].as_list(), tmp; Value acc = args[1]; for (size_t i=0; i args, Environment &env) { // Is not a special form, so we can evaluate our args. eval_args(args, env); std::vector result; Value low = args[0], high = args[1]; if (low.get_type_name() != INT_TYPE && low.get_type_name() != FLOAT_TYPE) throw Error(low, env, MISMATCHED_TYPES); if (high.get_type_name() != INT_TYPE && high.get_type_name() != FLOAT_TYPE) throw Error(high, env, MISMATCHED_TYPES); if (low >= high) return Value(result); while (low < high) { result.push_back(low); low = low + Value(1); } return Value(result); } } void repl(Environment &env) { #ifdef USE_STD std::string code; std::string input; Value tmp; std::vector parsed; while (true) { std::cout << ">>> "; std::getline(std::cin, input); if (input == "!quit" || input == "!q") break; else if (input == "!env" || input == "!e") std::cout << env << std::endl; else if (input == "!export" || input == "!x") { std::cout << "File to export to: "; std::getline(std::cin, input); std::ofstream f; f.open(input.c_str(), std::ofstream::out); f << code; f.close(); } else if (input != "") { try { tmp = run(input, env); std::cout << " => " << tmp.debug() << std::endl; code += input + "\n"; } catch (Error &e) { std::cerr << e.description() << std::endl; } catch (std::runtime_error &e) { std::cerr << e.what() << std::endl; } } } #endif } // Does this environment, or its parent environment, have a variable? bool Environment::has(std::string name) const { // Find the value in the map std::map::const_iterator itr = defs.find(name); if (itr != defs.end()) // If it was found return true; else if (parent_scope != NULL) // If it was not found in the current environment, // try to find it in the parent environment return parent_scope->has(name); else return false; } // Get the value associated with this name in this scope Value Environment::get(std::string name) const { // Meta operations if (name == "eval") return Value("eval", builtin::eval); if (name == "type") return Value("type", builtin::get_type_name); if (name == "parse") return Value("parse", builtin::parse); // Special forms if (name == "do") return Value("do", builtin::do_block); if (name == "if") return Value("if", builtin::if_then_else); if (name == "for") return Value("for", builtin::for_loop); if (name == "while") return Value("while", builtin::while_loop); if (name == "scope") return Value("scope", builtin::scope); if (name == "quote") return Value("quote", builtin::quote); if (name == "defun") return Value("defun", builtin::defun); if (name == "define") return Value("define", builtin::define); if (name == "lambda") return Value("lambda", builtin::lambda); // Comparison operations if (name == "=") return Value("=", builtin::eq); if (name == "!=") return Value("!=", builtin::neq); if (name == ">") return Value(">", builtin::greater); if (name == "<") return Value("<", builtin::less); if (name == ">=") return Value(">=", builtin::greater_eq); if (name == "<=") return Value("<=", builtin::less_eq); // Arithmetic operations if (name == "+") return Value("+", builtin::sum); if (name == "-") return Value("-", builtin::subtract); if (name == "*") return Value("*", builtin::product); if (name == "/") return Value("/", builtin::divide); if (name == "%") return Value("%", builtin::remainder); // List operations if (name == "list") return Value("list", builtin::list); if (name == "insert") return Value("insert", builtin::insert); if (name == "index") return Value("index", builtin::index); if (name == "remove") return Value("remove", builtin::remove); if (name == "len") return Value("len", builtin::len); if (name == "push") return Value("push", builtin::push); if (name == "pop") return Value("pop", builtin::pop); if (name == "head") return Value("head", builtin::head); if (name == "tail") return Value("tail", builtin::tail); if (name == "first") return Value("first", builtin::head); if (name == "last") return Value("last", builtin::pop); if (name == "range") return Value("range", builtin::range); // Functional operations if (name == "map") return Value("map", builtin::map_list); if (name == "filter") return Value("filter", builtin::filter_list); if (name == "reduce") return Value("reduce", builtin::reduce_list); // IO operations #ifdef USE_STD if (name == "exit") return Value("exit", builtin::exit); if (name == "quit") return Value("quit", builtin::exit); if (name == "print") return Value("print", builtin::print); if (name == "input") return Value("input", builtin::input); if (name == "random") return Value("random", builtin::random); if (name == "include") return Value("include", builtin::include); if (name == "parse-csv") return Value("parse-csv", builtin::parse_csv); if (name == "read-file") return Value("read-file", builtin::read_file); if (name == "write-file") return Value("write-file", builtin::write_file); if (name == "read-url") return Value("read-url", builtin::read_url); #endif // String operations if (name == "debug") return Value("debug", builtin::debug); if (name == "replace") return Value("replace", builtin::replace); if (name == "display") return Value("display", builtin::display); // Casting operations if (name == "int") return Value("int", builtin::cast_to_int); if (name == "float") return Value("float", builtin::cast_to_float); // Constants if (name == "endl") return Value::string("\n"); std::map::const_iterator itr = defs.find(name); if (itr != defs.end()) return itr->second; else if (parent_scope != NULL) { itr = parent_scope->defs.find(name); if (itr != parent_scope->defs.end()) return itr->second; else return parent_scope->get(name); } throw Error(Value::atom(name), *this, ATOM_NOT_DEFINED); } int main(int argc, const char **argv) { Environment env; std::vector args; for (int i=0; i