fixes & enhandcements
(benchmark code..) implemented repl completion very first version (string xx) added some builtin renames a bit of comments a bit of cocumentation (sleep interval) added
This commit is contained in:
205
ml.cpp
205
ml.cpp
@@ -3,13 +3,13 @@
|
||||
#include "ml_io.h"
|
||||
#include "ml_date.h"
|
||||
#include "ml_string.h"
|
||||
#include "ml_util.h"
|
||||
|
||||
#include "clib/csvparser.h"
|
||||
#include "clib/sslclient.h"
|
||||
#include "clib/json11.h"
|
||||
#include "clib/printf.h"
|
||||
|
||||
#include "linenoise.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
@@ -19,7 +19,7 @@
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <ctime>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#define TOO_FEW_ARGS "too few arguments to function"
|
||||
#define TOO_MANY_ARGS "too many arguments to function"
|
||||
@@ -257,6 +257,20 @@ MlValue MlValue::cast_to_float() const {
|
||||
}
|
||||
}
|
||||
|
||||
// Cast this to a string
|
||||
MlValue MlValue::cast_to_string() const {
|
||||
switch (type) {
|
||||
case INT:
|
||||
return MlValue::string(to_string(stack_data.i));
|
||||
case FLOAT:
|
||||
return MlValue::string(to_string(stack_data.f));
|
||||
case STRING:
|
||||
return *this;
|
||||
default:
|
||||
throw MlError(*this, MlEnvironment(), BAD_CAST);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool MlValue::operator==(MlValue other) const {
|
||||
// If either of these values are floats, promote the
|
||||
@@ -1110,6 +1124,7 @@ namespace builtin {
|
||||
return MlValue(now());
|
||||
}
|
||||
|
||||
// Converts date to formated string.
|
||||
MlValue date_to_str(std::vector<MlValue> args, MlEnvironment &env) {
|
||||
eval_args(args, env);
|
||||
|
||||
@@ -1119,6 +1134,7 @@ namespace builtin {
|
||||
return MlValue::string(date_to_string(args[0].as_int(), args[1].as_string()));
|
||||
}
|
||||
|
||||
// Converst string to time of secs since epoch
|
||||
MlValue str_to_date(std::vector<MlValue> args, MlEnvironment &env) {
|
||||
eval_args(args, env);
|
||||
|
||||
@@ -1129,6 +1145,7 @@ namespace builtin {
|
||||
return MlValue(string_to_date(args[0].as_string(), args[1].as_string()));
|
||||
}
|
||||
|
||||
// Add number of units to date. A unit is one of 'year', 'month', 'day', 'hour', 'minute' or 'second'
|
||||
MlValue date_add(std::vector<MlValue> args, MlEnvironment &env) {
|
||||
eval_args(args, env);
|
||||
|
||||
@@ -1353,7 +1370,17 @@ namespace builtin {
|
||||
return args[0].cast_to_int();
|
||||
}
|
||||
|
||||
// Index a list
|
||||
// Cast an item to a string
|
||||
MlValue cast_to_string(std::vector<MlValue> args, MlEnvironment &env) {
|
||||
eval_args(args, env);
|
||||
|
||||
if (args.size() != 1)
|
||||
throw MlError(MlValue(STRING_TYPE, cast_to_string), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
|
||||
|
||||
return args[0].cast_to_string();
|
||||
}
|
||||
|
||||
// Index a list
|
||||
MlValue index(std::vector<MlValue> args, MlEnvironment &env) {
|
||||
eval_args(args, env);
|
||||
|
||||
@@ -1474,22 +1501,24 @@ namespace builtin {
|
||||
return MlValue(parsed);
|
||||
}
|
||||
|
||||
MlValue replace(std::vector<MlValue> args, MlEnvironment &env) {
|
||||
// Replace a substring with a replacement string in a source string
|
||||
MlValue string_replace(std::vector<MlValue> args, MlEnvironment &env) {
|
||||
eval_args(args, env);
|
||||
|
||||
if (args.size() != 3)
|
||||
throw MlError(MlValue("replace", replace), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
|
||||
throw MlError(MlValue("string-replace", string_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 MlValue::string(src);
|
||||
}
|
||||
|
||||
MlValue regex_search(std::vector<MlValue> args, MlEnvironment &env) {
|
||||
// Returns true if where contains regex
|
||||
MlValue string_regex(std::vector<MlValue> args, MlEnvironment &env) {
|
||||
eval_args(args, env);
|
||||
|
||||
if (args.size() != 2) // if (args.size() < 2 || args.size() > 3)
|
||||
throw MlError(MlValue("regex_search", regex_search), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
|
||||
throw MlError(MlValue("string-regex?", string_regex), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
|
||||
|
||||
return MlValue(regexp_search(args[0].as_string(), args[1].as_string()));
|
||||
}
|
||||
@@ -1608,53 +1637,39 @@ namespace builtin {
|
||||
}
|
||||
return MlValue(result);
|
||||
}
|
||||
|
||||
// Benchmarks a block of expressions in the current environment (SPECIAL FORM)
|
||||
MlValue benchmark(std::vector<MlValue> args, MlEnvironment &env) {
|
||||
// TODO add some memory stats
|
||||
using namespace std::chrono;
|
||||
high_resolution_clock::time_point t1 = high_resolution_clock::now();
|
||||
|
||||
MlValue acc;
|
||||
for (size_t i = 1; i < args.size(); i++)
|
||||
acc = args[i].eval(env);
|
||||
|
||||
high_resolution_clock::time_point t2 = high_resolution_clock::now();
|
||||
duration<double, std::milli> time_span = t2 - t1;
|
||||
|
||||
std::cout << args[0].as_string() << " " << time_span.count() << " ms" << std::endl;
|
||||
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
|
||||
// void completion(const char *buf, linenoiseCompletions *lc) {
|
||||
// if (buf[0] == 'h') {
|
||||
// linenoiseAddCompletion(lc,"hello");
|
||||
// linenoiseAddCompletion(lc,"hello there");
|
||||
// }
|
||||
// }
|
||||
|
||||
// char *hints(const char *buf, int *color, int *bold) {
|
||||
// if (!strcasecmp(buf,"hello")) {
|
||||
// *color = 35;
|
||||
// *bold = 0;
|
||||
// return " World";
|
||||
// }
|
||||
// return NULL;
|
||||
// }
|
||||
|
||||
void repl(MlEnvironment &env) {
|
||||
std::string code;
|
||||
std::string input;
|
||||
MlValue tmp;
|
||||
std::vector<MlValue> parsed;
|
||||
char *line;
|
||||
|
||||
// TODO not portable and in function
|
||||
std::string history_file;
|
||||
std::string file{"/.ml_history.txt"};
|
||||
const char *t = std::getenv("HOME");
|
||||
if (t == nullptr)
|
||||
history_file = "/tmp/" + file;
|
||||
else
|
||||
history_file = std::string{t} + "/" + file;
|
||||
|
||||
// linenoiseHistorySetMaxLen(500);
|
||||
// linenoiseSetCompletionCallback(completion);
|
||||
// linenoiseSetHintsCallback(hints);
|
||||
|
||||
linenoiseSetMultiLine(1);
|
||||
linenoiseHistoryLoad(history_file.c_str());
|
||||
setup_linenoise(env);
|
||||
|
||||
while (true) {
|
||||
// std::cout << ">>> ";
|
||||
// std::getline(std::cin, input);
|
||||
char *line = linenoise(">>> ");
|
||||
if (line == nullptr) break;
|
||||
|
||||
line = linenoise(">>> ");
|
||||
linenoiseHistoryAdd(line);
|
||||
linenoise_line_read(line);
|
||||
|
||||
input = std::string(line);
|
||||
|
||||
@@ -1680,23 +1695,11 @@ void repl(MlEnvironment &env) {
|
||||
}
|
||||
}
|
||||
|
||||
linenoiseHistorySave(history_file.c_str());
|
||||
close_linenoise();
|
||||
}
|
||||
|
||||
void load_std_lib(MlEnvironment &env) {
|
||||
std::string loader =
|
||||
R"( (define ___lib_path '("/usr/local/var/mlisp"))
|
||||
(for d ___lib_path
|
||||
(if (is-dir? d)
|
||||
(for f (ls-dir d)
|
||||
(if (regex-search? f "^.*\.l(i)?sp$")
|
||||
(include (+ d "/" f))
|
||||
'())
|
||||
)
|
||||
'()))
|
||||
)";
|
||||
|
||||
run(loader, env);
|
||||
run(STDLIB_LOADER, env);
|
||||
}
|
||||
|
||||
// Does this environment, or its parent environment, have a variable?
|
||||
@@ -1730,6 +1733,7 @@ MlValue MlEnvironment::get(const std::string &name) const {
|
||||
if (name == "defun") return MlValue("defun", builtin::defun);
|
||||
if (name == "define") return MlValue("define", builtin::define);
|
||||
if (name == "lambda") return MlValue("lambda", builtin::lambda);
|
||||
if (name == "benchmark") return MlValue("benchmark", builtin::benchmark);
|
||||
|
||||
// Comparison operations
|
||||
if (name == "=") return MlValue("=", builtin::eq);
|
||||
@@ -1794,13 +1798,14 @@ MlValue MlEnvironment::get(const std::string &name) const {
|
||||
if (name == "debug") return MlValue("debug", builtin::debug);
|
||||
if (name == "sprintf") return MlValue("sprintf", builtin::sprintf);
|
||||
if (name == "display") return MlValue("display", builtin::display);
|
||||
if (name == "replace") return MlValue("replace", builtin::replace);
|
||||
if (name == "regex-search?") return MlValue("regex-search?", builtin::regex_search);
|
||||
if (name == "string-replace") return MlValue("string-replace", builtin::string_replace);
|
||||
if (name == "string-regex?") return MlValue("string-regex?", builtin::string_regex);
|
||||
if (name == "string-pad") return MlValue("string-pad", builtin::string_pad);
|
||||
|
||||
// Casting operations
|
||||
if (name == "int") return MlValue("int", builtin::cast_to_int);
|
||||
if (name == "float") return MlValue("float", builtin::cast_to_float);
|
||||
if (name == "string") return MlValue("string", builtin::cast_to_string);
|
||||
|
||||
// Constants
|
||||
if (name == "endl") return MlValue::string("\n");
|
||||
@@ -1816,9 +1821,41 @@ MlValue MlEnvironment::get(const std::string &name) const {
|
||||
throw MlError(MlValue::atom(name), *this, ATOM_NOT_DEFINED);
|
||||
}
|
||||
|
||||
// Get vector of executables in this scope
|
||||
std::vector<std::string> MlEnvironment::get_lambdas_list() const {
|
||||
std::vector<std::string> lambdas {128};
|
||||
|
||||
for (auto it = defs.begin(); it != defs.end(); it++) {
|
||||
if (it->second.get_type_name()==FUNCTION_TYPE) {
|
||||
lambdas.push_back(it->first);
|
||||
}
|
||||
}
|
||||
std::vector<std::string> commands {
|
||||
"eval", "type", "parse", "do", "if", "for", "while", "scope", "quote", "defun",
|
||||
"define", "lambda", "benchmark", "=", "!=", ">", "<", ">=", "<=", "+", "-", "*", "/", "%",
|
||||
"list", "insert", "index", "remove", "len", "push", "pop", "head", "tail", "first", "last",
|
||||
"range", "map", "filter", "reduce", "exit", "quit", "print", "input", "random", "include",
|
||||
"read-file", "write-file", "read-url", "system-cmd", "ls-dir", "is-file?", "is-dir?",
|
||||
"parse-csv", "parse-json", "get-universal-time", "date-to-str", "str-to-date", "date-add", "debug",
|
||||
"sprintf", "display", "string-replace", "string-regex?", "string-pad", "int", "float", "string" };
|
||||
|
||||
lambdas.insert(end(lambdas), begin(commands), end(commands));
|
||||
return lambdas;
|
||||
}
|
||||
|
||||
bool cmdOptionExists(char **begin, char **end, const std::string &option) { return std::find(begin, end, option) != end; }
|
||||
|
||||
std::vector<std::string> getCmdOption(char *argv[], int argc, const std::string &option) {
|
||||
std::vector<std::string> tokens;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (option == argv[i] && i + 1 < argc) {
|
||||
i++;
|
||||
tokens.push_back(std::string(argv[i]));
|
||||
}
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
MlEnvironment env;
|
||||
std::vector<MlValue> args;
|
||||
@@ -1828,29 +1865,35 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
srand(time(NULL));
|
||||
try {
|
||||
load_std_lib(env);
|
||||
// for xcode profiling
|
||||
// run(read_file_contents("/Users/vaclavt/Development/mlisp/tests/test.lsp"), env);
|
||||
|
||||
// help
|
||||
if (cmdOptionExists(argv, argv + argc, "-h")) {
|
||||
std::cout << "Usage:\n\t-h print this help\n\t-f source_file - executes code in file\n\t-c code - runs passed code\n\t-i runs repl\n\t-v prints version string\n\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// version
|
||||
if (cmdOptionExists(argv, argv + argc, "-v")) {
|
||||
std::cout << VERSION << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argc == 1 || (argc == 2 && std::string(argv[1]) == "-i"))
|
||||
// skip loading std lib
|
||||
if (!cmdOptionExists(argv, argv + argc, "-b")) {
|
||||
load_std_lib(env);
|
||||
}
|
||||
// help
|
||||
if (cmdOptionExists(argv, argv + argc, "-h")) {
|
||||
std::cout << "Usage:\n\t-h print this help\n\t-f source_file - executes code in file\n\t-c code - runs passed code\n\t-i runs repl\n\t-b skip stdlib loading\n\t-v prints version string\n\n";
|
||||
return 0;
|
||||
}
|
||||
// version
|
||||
if (cmdOptionExists(argv, argv + argc, "-v")) {
|
||||
std::cout << VERSION << std::endl;
|
||||
return 0;
|
||||
}
|
||||
// passed code
|
||||
if (cmdOptionExists(argv, argv + argc, "-c")) {
|
||||
std::vector<std::string> codes = getCmdOption(argv, argc, "-c");
|
||||
for (size_t i = 0; i < codes.size(); i++)
|
||||
run(codes[i], env);
|
||||
// run files
|
||||
} else if (cmdOptionExists(argv, argv + argc, "-f")) {
|
||||
std::vector<std::string> files = getCmdOption(argv, argc, "-f");
|
||||
for (size_t i = 0; i < files.size(); i++)
|
||||
run(read_file_contents(files[i]), env);
|
||||
// repl
|
||||
} else {
|
||||
repl(env);
|
||||
else if (argc == 3 && std::string(argv[1]) == "-c")
|
||||
run(argv[2], env);
|
||||
else if (argc == 3 && std::string(argv[1]) == "-f")
|
||||
run(read_file_contents(argv[2]), env);
|
||||
else std::cerr << "invalid arguments" << std::endl;
|
||||
}
|
||||
|
||||
} catch (MlError &e) {
|
||||
std::cerr << e.description() << std::endl;
|
||||
} catch (std::runtime_error &e) {
|
||||
|
||||
Reference in New Issue
Block a user