defmacro added, doc improvements
This commit is contained in:
parent
249af03f60
commit
f1d102130f
|
|
@ -57,6 +57,7 @@ make
|
|||
```
|
||||
cp build/ml /usr/local/bin/ml
|
||||
cp stdlib/*.lsp /usr/local/var/mlisp/
|
||||
cp doc/*.md /usr/local/var/mlisp/
|
||||
```
|
||||
or
|
||||
```
|
||||
|
|
@ -69,22 +70,22 @@ utils/local_install.sh
|
|||
- (read-url "https://api.nasdaq.com/api/calendar/dividends/") ; hangs in sslclient.cpp line 132
|
||||
|
||||
### TODO
|
||||
- add functions from stdlib into doc
|
||||
- string-case, save-csv fix doc
|
||||
- add test for >>> (range 1.5 (inc 5.5)) => (1.500000 2.500000 3.500000 4.500000 5.500000)
|
||||
- better formating of help
|
||||
- unify -f and -run options
|
||||
- add debug support, at least call stack
|
||||
- multiline editing (see kilocpp editor)
|
||||
- execute system command should capture stderr
|
||||
- add some mem stats to benchmark
|
||||
- add mem stats to benchmark
|
||||
- order of arguments in functions like member and filter - filter swap lambda and list
|
||||
- better output of !e in repl
|
||||
|
||||
#### Doc
|
||||
- fix an error in printing multiline examples in doc
|
||||
- string-case, save-csv fix doc
|
||||
- add functions from stdlib into doc
|
||||
- fix man entry on (member) example and retval
|
||||
- doc for set!
|
||||
- doc for doc::??? functions
|
||||
|
||||
#### Code
|
||||
- tcpnet should use RAII
|
||||
|
|
|
|||
16
debug.lsp
16
debug.lsp
|
|
@ -1,3 +1,13 @@
|
|||
(def l '(nil 1 2 3))
|
||||
(print (filter (lambda (e) (eval e)) l))
|
||||
(print (filter (lambda (eval e) (print e)) l))
|
||||
|
||||
(defn concat-lists (seq1 seq2)
|
||||
(do (def l seq1)
|
||||
(dotimes i (len seq2)
|
||||
(set! l (push l (index seq2 i))))
|
||||
))
|
||||
|
||||
(print (concat-lists '(1 2 3) '(4 5 6)))
|
||||
;; (1 2 3 4 5 6)
|
||||
|
||||
; pridat example
|
||||
; >>> (doc::man "push")
|
||||
; (push list element) - Add an item to the end of a list
|
||||
28
doc/Doc.md
28
doc/Doc.md
|
|
@ -28,6 +28,7 @@
|
|||
|`(do a b c ...)`|`do` takes a list of s-expressions and evaluates them in the order they were given (in the current scope), and then returns the result of the last s-expression.|This special form allows lambda functions to have multi-step bodies.|Language|
|
||||
|`(scope a b c ...)`|`scope` takes a list of s-expressions and evaluates them in the order they were given _in a new scope_, and then returns the result of the last s-expression.|This special form allows the user to evaluate blocks of code in new scopes.|Language|
|
||||
|`(defn name params body)`|`defn` evaluates none of its arguments.|This special form allows the user to conveniently define functions.|Language|
|
||||
|`(defmacro name params body)`|`defmacro` evaluates none of its arguments.|This special form allows the user to conveniently define macros.|Language|
|
||||
|`(def name value)`|`def` evaluates the `value` argument, which is then assigned to `name` in the current scope.|This special form allows the user to bind atoms to values in a scope.|Language|
|
||||
|`(lambda params body)`|`lambda` evaluates none of its arguments.|This special form allows the user to define anonymous functions.|Language|
|
||||
|`(quote x)`|`quote` evaluates none of its arguments.|This is equivalent to the `'expr` syntactic sugar.|Language|
|
||||
|
|
@ -36,6 +37,7 @@
|
|||
|`(and ...)`|`and` evaluates logical and on its list argument.|`and` evaluates only until its true, it stops when becomes false.|Language|
|
||||
|`(or ...)`|`or` evaluates logical or on its list argument.|`or` evaluates while its false, it stops when becomes true.|Language|
|
||||
|`(benchmark msg_string ...)`|`benchmark` takes a list of s-expressions and evaluates them in the order they were given (in the current scope), and then returns the result of the last s-expression and prints msg_string and timing info.||Language|
|
||||
|`(set! x)`|`set!` ...|....|Language|
|
||||
|
||||
## Library
|
||||
|Signature|Description|Return values|Section|
|
||||
|
|
@ -68,7 +70,9 @@
|
|||
|`(flatten list)`|Flattens a list to single level|`>>> (flatten '(1 (2 2) 3)) => (1 2 2 3)`|List manipulation|
|
||||
|`(take list count)`|Returns sublist with count element of list|`>>> (take '(1 2 3 4) 3) => (1 2 3)`|List manipulation|
|
||||
|`(make-list len)`|Return list with len nil elements|`(make-list 3) => (nil nil nil)`|List manipulation|
|
||||
|`(make-list-of len value)`|Return list with len value elements||List manipulation|
|
||||
|`(make-list-of size value)`|Makes list with size elements of values|`>>> (make-list-of 5 0) => (0 0 0 0 0)`|List manipulation|
|
||||
|`(concat-lists seq1 seq2)`|Appends all elements from seq2 to seq1|`>>> (concat-lists '(1 2 3) '(4 5 6)) => (1 2 3 4 5 6)`|List manipulation|
|
||||
|`(take lst size)`|Makes sublist of a lst with size length|`>>> (take '(1 2 3 4 5 6) 3) => (1 2 3)`|List manipulation|
|
||||
|`(map ..)`|Returns list that is the result of executing lambda on its elements|`(map (lambda (x) (+ x 10)) '(1 2 3 4 5 6)) => (11 12 13 14 15 16)`|List manipulation|
|
||||
|`(filter lambda list)`|Returns list with elements for which passed lambda returns true|`(filter (lambda (x) (> x 2)) '(1 2 3 4 5)) => (3 4 5)`|List manipulation|
|
||||
|`(reduce lambda acumulator list)`|Reduces list|`>>> (reduce (lambda (x y) (+ (* x 10) y)) 0 '(1 2 3 4)) => 1234`|List manipulation|
|
||||
|
|
@ -133,20 +137,31 @@
|
|||
|`(string value)`|Cast int or float item to a string|`>>> (string 3.14) => "3.14"`|Type casting|
|
||||
|`(eval <exp>)`|Eval returns the value of the second evaluation|`>>> (eval '(+ 1 2)) => 3`|Language|
|
||||
|`(type e)`|Returns data type of e|`>>> (type (+ 1 2)) => "int"`|Type casting|
|
||||
|`(list? e)`|Returns true if type of e is list|`>>> (list? 12) => nil >>> (list? '(1 2)) => #t`|Type casting|
|
||||
|`(empty-list? e)`|Returns true if type of e is empty list|`>>> (empty-list? '(1 2)) => nil >>> (empty-list? '()) => #t`|Type casting|
|
||||
|`(string? e)`|Returns true if type of e is string|`>>> (string? "str") => #t`|Type casting|
|
||||
|`(int? e)`|Returns true if type of e is integer|`>>> (int? "str") => nil >>> (int? 9) => #t`|Type casting|
|
||||
|`(float? e)`|Returns true if type of e is float|`>>> (float? 9) => nil >>> (float? 9.0) => #t`|Type casting|
|
||||
|`(nil? e)`|Returns true if type of e is nil|`>>> (nil? 9.0) => nil >>> (nil? nil) => #t >>> (nil? '()) => nil`|Type casting|
|
||||
|`(true? e)`|Returns true if type of e is #t||Type casting|
|
||||
|`(parse ..)`|Parses string to be evaluated|`>>> (eval (first (parse "(+ 1 2)"))) => 3`|Language|
|
||||
|`(make-list-of size value)`|Makes list with size elements of values|`>>> (make-list-of 5 0) => (0 0 0 0 0)`|List manipulation|
|
||||
|`(make-list size)`|Makes list of nil values with size length|`>>> (make-list 5) => (nil nil nil nil nil)`|List manipulation|
|
||||
|`(empty-list? lst)`|Return true is lst is list with zero elements|`>>> (empty-list? '()) => 1`|Type casting|
|
||||
|`(quick-sort-by list cmp)`|Returns list sorted by comparsion function||Language|
|
||||
|`(quick-sort list)`|Return sorted list|`>>> (quick-sort '(2 4 6 1 7 3 3 9 5)) => (1 2 3 3 4 5 6 7 9)`|Language|
|
||||
|`(quick-sort-reverse list)`|Return reverse sorted list|`>>> (quick-sort-reverse '(2 4 6 1 7 3 3 9 5)) => (9 7 6 5 4 3 3 2 1)`|Language|
|
||||
|`(not c)`|Logical NOT of c|`>>> (not 1) => nil`|Logical|
|
||||
|`(not c)`|Logical NOT of c|`>>> (not 1) => nil`|Language|
|
||||
|`(neg n)`|Negates number|`>>> (neg -5) => 5`|Language|
|
||||
|`(is-pos? n)`|Returns true if n is positive number||Language|
|
||||
|`(is-neg? n)`|Returns true if n is negative number||Language|
|
||||
|`(dec n)`|Return n decremented by 1|`>>> (dec 5) => 4`|Language|
|
||||
|`(inc n)`|Return n incremented by 1|`>>> (inc 5) => 6`|Language|
|
||||
|`(sleep time)`|Pauses execution for time interval of seconds||System|
|
||||
|`(function? e)`|Returns true if type of e is lambda, macro or builtin|`>>> (function? index) => #t`|Language|
|
||||
|`(unless test v)`|If test is not truthy then v is evaluated. This is macro.|`>>> (unless (> 1 2) "1 < 2") => "1 < 2"`|Language|
|
||||
|`(dotimes v n body)`|Evaluates body n-times. For each eveluation v is set to value between 0 and n minus one|`(dotimes i 10 (print "i :" i))`|Language|
|
||||
|`(doc::man "func_name")`|Prints short help for func_name||Language|
|
||||
|`(doc::appropos "string")`|Looks for functions to be related passed string. String can be more words separated by space||Language|
|
||||
|`(doc::look "string")`|Alias for appropos||Language|
|
||||
|`(doc::lookup "string")`|Alias for appropos||Language|
|
||||
|`(get-env var)`|Return environment variable var|`>>> (get-env "HOME") => "/Users/vaclavt"`|System|
|
||||
|`(second list)`|Returns second element of list|`>>> (second '(1 2 3 4 5 6 7)) => 2`|List manipulation|
|
||||
|`(third list)`|Returns third element of list|`>>> (third '(1 2 3 4 5 6 7)) => 3`|List manipulation|
|
||||
|
|
@ -160,5 +175,4 @@
|
|||
|`(thread-sleep milisecons)`|Sleeps thread for given amount of miliseconds||Threading|
|
||||
|`(threads-join)`|Wait for all running threads to finish||Threading|
|
||||
|`(try block catch_block [finally_block])`|Evaluates block and if an exception is thrown, evaluates catch_block.Eventually in both cases evals finally_block. Return evaluated last expression from block if no exception or last expression from catch_block, if exception is thrown from block. Variable ml-exception in catch block is available with astring describing exception||Exceptions|
|
||||
|`(throw-exception exp_desc)`|Throws an exception with exp_desc describing what happened ||Exceptions|
|
||||
|`(xx ..)`|Desc|example|section|
|
||||
|`(throw-exception exp_desc)`|Throws an exception with exp_desc describing what happened ||Exceptions|
|
||||
130
ml.cpp
130
ml.cpp
|
|
@ -25,6 +25,7 @@
|
|||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <numeric>
|
||||
|
||||
|
||||
#define TOO_FEW_ARGS "too few arguments to function"
|
||||
|
|
@ -47,6 +48,8 @@
|
|||
#define INDEX_OUT_OF_RANGE "index out of range"
|
||||
#define MALFORMED_PROGRAM "malformed program"
|
||||
#define NOT_IMPLEMENTED_YET_ERROR "not implemented yet"
|
||||
#define DIVISION_BY_ZERO "division by zero"
|
||||
#define INVALID_HEADER_FORMAT "invalid header format"
|
||||
|
||||
|
||||
#define STRING_TYPE "string"
|
||||
|
|
@ -123,7 +126,7 @@ MlValue MlValue::nil() {
|
|||
}
|
||||
|
||||
// Construct a lambda function
|
||||
MlValue::MlValue(const std::vector<MlValue> ¶ms, MlValue ret, MlEnvironment const &env) : type(LAMBDA) {
|
||||
MlValue::MlValue(const std::vector<MlValue> ¶ms, MlValue ret, MlEnvironment const &env, const Type ftype) : type(ftype) {
|
||||
// We store the params and the result in the list member
|
||||
// instead of having dedicated members. This is to save memory.
|
||||
list.emplace_back(params);
|
||||
|
|
@ -162,7 +165,8 @@ std::vector<std::string> MlValue::get_used_atoms() {
|
|||
result.push_back(as_atom());
|
||||
return result;
|
||||
case LAMBDA:
|
||||
// If this is a lambda, get the list of used atoms in the body
|
||||
case MACRO:
|
||||
// If this is a lambda or macro, get the list of used atoms in the body
|
||||
// of the expression.
|
||||
return list[1].get_used_atoms();
|
||||
case LIST:
|
||||
|
|
@ -184,6 +188,10 @@ bool MlValue::is_builtin() const {
|
|||
return type == BUILTIN;
|
||||
}
|
||||
|
||||
bool MlValue::is_macro() const {
|
||||
return type == MACRO;
|
||||
}
|
||||
|
||||
bool MlValue::is_number() const {
|
||||
return type == INT || type == FLOAT;
|
||||
}
|
||||
|
|
@ -318,8 +326,9 @@ bool MlValue::operator==(MlValue other) const {
|
|||
// data in the str member.
|
||||
return str == other.str;
|
||||
case LAMBDA:
|
||||
case MACRO:
|
||||
case LIST:
|
||||
// Both lambdas and lists store their
|
||||
// All lambdas, macros and lists store their
|
||||
// data in the list member.
|
||||
return list == other.list;
|
||||
case QUOTE:
|
||||
|
|
@ -526,9 +535,10 @@ std::string MlValue::get_type_name() const {
|
|||
return STRING_TYPE;
|
||||
case BUILTIN:
|
||||
case LAMBDA:
|
||||
case MACRO:
|
||||
// Instead of differentiating between
|
||||
// lambda and builtin types, we group them together.
|
||||
// This is because they are both callable.
|
||||
// lambda, macro and builtin types, we group them together.
|
||||
// This is because they are all callable.
|
||||
return FUNCTION_TYPE;
|
||||
case NIL:
|
||||
return NIL_TYPE;
|
||||
|
|
@ -541,7 +551,6 @@ std::string MlValue::get_type_name() const {
|
|||
}
|
||||
|
||||
std::string MlValue::display() const {
|
||||
std::string result;
|
||||
switch (type) {
|
||||
case QUOTE:
|
||||
return "'" + list[0].debug();
|
||||
|
|
@ -554,17 +563,14 @@ std::string MlValue::display() const {
|
|||
case STRING:
|
||||
return str;
|
||||
case LAMBDA:
|
||||
for (size_t i = 0; i < list.size(); i++) {
|
||||
result += list[i].debug();
|
||||
if (i < list.size() - 1) result += " ";
|
||||
}
|
||||
return "(lambda " + result + ")";
|
||||
return "(lambda " + std::accumulate(list.begin(), list.end(), std::string(), [](const std::string &a, const MlValue &b) -> std::string
|
||||
{return a + (a.empty() ? "" : " ") + b.debug(); }) + ")";
|
||||
case MACRO:
|
||||
return "(macro " + std::accumulate(list.begin(), list.end(), std::string(), [](const std::string &a, const MlValue &b) -> std::string
|
||||
{return a + (a.empty() ? "" : " ") + b.debug(); }) + ")";
|
||||
case LIST:
|
||||
for (size_t i = 0; i < list.size(); i++) {
|
||||
result += list[i].debug();
|
||||
if (i < list.size() - 1) result += " ";
|
||||
}
|
||||
return "(" + result + ")";
|
||||
return "(" + std::accumulate(list.begin(), list.end(), std::string(), [](const std::string &a, const MlValue &b) -> std::string
|
||||
{return a + (a.empty() ? "" : " ") + b.debug(); }) + ")";
|
||||
case BUILTIN:
|
||||
return "<" + str + " at " + std::to_string(long(stack_data.b)) + ">";
|
||||
case NIL:
|
||||
|
|
@ -580,41 +586,15 @@ std::string MlValue::display() const {
|
|||
std::string MlValue::debug() const {
|
||||
std::string result;
|
||||
switch (type) {
|
||||
case QUOTE:
|
||||
return "'" + list[0].debug();
|
||||
case ATOM:
|
||||
return str;
|
||||
case INT:
|
||||
return std::to_string(stack_data.i);
|
||||
case FLOAT:
|
||||
return std::to_string(stack_data.f);
|
||||
case STRING:
|
||||
result.reserve(str.size());
|
||||
for (char c : str) {
|
||||
if (c == '"') result += "\\\"";
|
||||
else result.push_back(c);
|
||||
}
|
||||
return "\"" + result + "\"";
|
||||
case LAMBDA:
|
||||
for (size_t i = 0; i < list.size(); i++) {
|
||||
result += list[i].debug();
|
||||
if (i < list.size() - 1) result += " ";
|
||||
}
|
||||
return "(lambda " + result + ")";
|
||||
case LIST:
|
||||
for (size_t i = 0; i < list.size(); i++) {
|
||||
result += list[i].debug();
|
||||
if (i < list.size() - 1) result += " ";
|
||||
}
|
||||
return "(" + result + ")";
|
||||
case BUILTIN:
|
||||
return "<" + str + " at " + std::to_string(long(stack_data.b)) + ">";
|
||||
case NIL:
|
||||
return "nil";
|
||||
case TRUE:
|
||||
return "#t";
|
||||
default:
|
||||
// This should never be reached.
|
||||
throw MlError(*this, MlEnvironment(), INTERNAL_ERROR);
|
||||
return display();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -646,19 +626,15 @@ const char * MlError::what() const noexcept {
|
|||
}
|
||||
|
||||
void MlEnvironment::combine(MlEnvironment const &other) {
|
||||
// Normally, I would use the `insert` method of the `map` class,
|
||||
// but it doesn't overwrite previously declared values for keys.
|
||||
auto itr = other.defs.begin();
|
||||
for (; itr != other.defs.end(); itr++) {
|
||||
// Iterate through the keys and assign each value.
|
||||
// We need to overwrite previously declared values for keys.
|
||||
for (auto itr = other.defs.begin(); itr != other.defs.end(); itr++) {
|
||||
defs[itr->first] = itr->second;
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, MlEnvironment const &e) {
|
||||
auto itr = e.defs.begin();
|
||||
os << "{ ";
|
||||
for (; itr != e.defs.end(); itr++) {
|
||||
for (auto itr = e.defs.begin(); itr != e.defs.end(); itr++) {
|
||||
os << '\'' << itr->first << "' : " << itr->second.debug() << ", ";
|
||||
}
|
||||
return os << "}";
|
||||
|
|
@ -687,8 +663,11 @@ void MlEnvironment::setX(const std::string &name, const MlValue& value) {
|
|||
MlValue MlValue::apply(std::vector<MlValue> args, MlEnvironment &env) {
|
||||
MlEnvironment e;
|
||||
std::vector<MlValue> params;
|
||||
MlValue macro_eval;
|
||||
|
||||
switch (type) {
|
||||
case LAMBDA:
|
||||
case MACRO:
|
||||
// Get the list of parameter atoms
|
||||
params = list[0].list;
|
||||
if (params.size() != args.size())
|
||||
|
|
@ -708,8 +687,17 @@ MlValue MlValue::apply(std::vector<MlValue> args, MlEnvironment &env) {
|
|||
e.set(params[i].str, args[i]);
|
||||
}
|
||||
|
||||
// Evaluate the function body with the function scope
|
||||
return list[1].eval(e);
|
||||
if (type == LAMBDA) {
|
||||
// Evaluate the function body with the function scope
|
||||
return list[1].eval(e);
|
||||
} else {
|
||||
// macro evals twice
|
||||
macro_eval = list[1].eval(e);
|
||||
if (MlPerfMon::instance().isDebugOn())
|
||||
std::cout << std::endl << "DEBUG macro 1st eval: " << macro_eval.debug() << std::endl << std::endl;
|
||||
|
||||
return macro_eval.eval(e);
|
||||
}
|
||||
case BUILTIN:
|
||||
// Here, we call the builtin function with the current scope.
|
||||
// This allows us to write special forms without syntactic sugar.
|
||||
|
|
@ -740,11 +728,11 @@ MlValue MlValue::eval(MlEnvironment &env) {
|
|||
args = std::vector<MlValue>(list.begin() + 1, list.end());
|
||||
|
||||
function = list[0].eval(env);
|
||||
if (function.type == BUILTIN || function.type == LAMBDA) {
|
||||
if (function.type == BUILTIN || function.type == LAMBDA || function.type == MACRO) {
|
||||
// Only evaluate our arguments if it's not builtin!
|
||||
// Builtin functions can be special forms, so we
|
||||
// leave them to evaluate their arguments.
|
||||
if (!function.is_builtin())
|
||||
if (!function.is_builtin() && !function.is_macro())
|
||||
for (auto & arg : args)
|
||||
arg = arg.eval(env);
|
||||
|
||||
|
|
@ -785,7 +773,6 @@ MlValue parse(std::string &s, int &ptr) {
|
|||
if (s[ptr] == ';')
|
||||
throw std::runtime_error(INTERNAL_ERROR);
|
||||
|
||||
|
||||
if (s.empty()) {
|
||||
return MlValue();
|
||||
|
||||
|
|
@ -906,8 +893,7 @@ MlValue run(const std::string &code, MlEnvironment &env) {
|
|||
if (parsed.empty())
|
||||
return MlValue::nil();
|
||||
|
||||
// Iterate over the expressions and evaluate them
|
||||
// in this environment.
|
||||
// Iterate over the expressions and evaluate them in this environment.
|
||||
for (size_t i = 0; i < parsed.size() - 1; i++)
|
||||
parsed[i].eval(env);
|
||||
|
||||
|
|
@ -936,7 +922,7 @@ MlValue lambda(std::vector<MlValue> args, MlEnvironment &env) {
|
|||
if (args[0].get_type_name() != LIST_TYPE)
|
||||
throw MlError(MlValue("lambda", lambda), env, INVALID_LAMBDA);
|
||||
|
||||
return MlValue(args[0].as_list(), args[1], env);
|
||||
return MlValue(args[0].as_list(), args[1], env, MlValue::Type::LAMBDA);
|
||||
}
|
||||
|
||||
// if-else (SPECIAL FORM)
|
||||
|
|
@ -992,7 +978,21 @@ MlValue defun(std::vector<MlValue> args, MlEnvironment &env) {
|
|||
if (args[1].get_type_name() != LIST_TYPE)
|
||||
throw MlError(MlValue("defn", defun), env, INVALID_LAMBDA);
|
||||
|
||||
MlValue f = MlValue(args[1].as_list(), args[2], env);
|
||||
MlValue f = MlValue(args[1].as_list(), args[2], env, MlValue::Type::LAMBDA);
|
||||
env.set(args[0].display(), f);
|
||||
return f;
|
||||
}
|
||||
|
||||
// Define a macro with parameters and a result expression (SPECIAL FORM)
|
||||
MlValue defmacro(std::vector<MlValue> args, MlEnvironment &env)
|
||||
{
|
||||
if (args.size() != 3)
|
||||
throw MlError(MlValue("defmacro", defmacro), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
|
||||
|
||||
if (args[1].get_type_name() != LIST_TYPE)
|
||||
throw MlError(MlValue("defmacro", defmacro), env, INVALID_LAMBDA);
|
||||
|
||||
MlValue f = MlValue(args[1].as_list(), args[2], env, MlValue::Type::MACRO);
|
||||
env.set(args[0].display(), f);
|
||||
return f;
|
||||
}
|
||||
|
|
@ -1213,8 +1213,10 @@ MlValue read_url(std::vector<MlValue> args, MlEnvironment &env) {
|
|||
// headers
|
||||
if (args.size() > 1) {
|
||||
for (const auto &hdr_val_pair: args[1].as_list()) {
|
||||
// TODO check its 2 string elements list
|
||||
const auto &pair = hdr_val_pair.as_list();
|
||||
if (pair.size() != 2)
|
||||
throw MlError(MlValue("read_url", read_url), env, INVALID_HEADER_FORMAT);
|
||||
|
||||
headers[pair[0].as_string()] = pair[1].as_string();
|
||||
}
|
||||
}
|
||||
|
|
@ -1466,7 +1468,7 @@ MlValue divide(std::vector<MlValue> args, MlEnvironment &env) {
|
|||
|
||||
if ((args[1].get_type_name() == "int" && args[1] == 0l) ||
|
||||
(args[1].get_type_name() == "float" && args[1] == 0.0))
|
||||
throw std::invalid_argument("divide by zero.");
|
||||
throw std::invalid_argument(DIVISION_BY_ZERO);
|
||||
|
||||
return args[0] / args[1];
|
||||
}
|
||||
|
|
@ -1952,7 +1954,6 @@ MlValue range(std::vector<MlValue> args, MlEnvironment &env) {
|
|||
|
||||
// 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();
|
||||
|
||||
|
|
@ -2110,6 +2111,7 @@ std::map <const std::string, Builtin> builtin_funcs
|
|||
std::make_pair("scope", builtin::scope),
|
||||
std::make_pair("quote", builtin::quote),
|
||||
std::make_pair("defn", builtin::defun),
|
||||
std::make_pair("defmacro", builtin::defmacro),
|
||||
std::make_pair("and", builtin::do_and),
|
||||
std::make_pair("or", builtin::do_or),
|
||||
std::make_pair("set!", builtin::setx),
|
||||
|
|
@ -2225,7 +2227,7 @@ std::map <const std::string, Builtin> builtin_funcs
|
|||
// Get the value associated with this name in this scope
|
||||
MlValue MlEnvironment::get(const std::string &name) const {
|
||||
// PERF, here can be a few of for fast access
|
||||
if (name == "defe") return MlValue("def", builtin::define);
|
||||
if (name == "def") return MlValue("def", builtin::define);
|
||||
if (name == "if") return MlValue("if", builtin::if_then_else);
|
||||
if (name == "lambda") return MlValue("lambda", builtin::lambda);
|
||||
|
||||
|
|
|
|||
32
ml.h
32
ml.h
|
|
@ -102,7 +102,21 @@ typedef MlValue (*Builtin)(std::vector<MlValue>, MlEnvironment &);
|
|||
class MlPerfMon;
|
||||
|
||||
class MlValue {
|
||||
|
||||
public:
|
||||
enum Type {
|
||||
QUOTE,
|
||||
ATOM,
|
||||
INT,
|
||||
FLOAT,
|
||||
LIST,
|
||||
STRING,
|
||||
LAMBDA,
|
||||
MACRO,
|
||||
BUILTIN,
|
||||
NIL,
|
||||
TRUE
|
||||
} type;
|
||||
|
||||
public:
|
||||
MlValue(); // Constructs a nil value
|
||||
MlValue(long i);
|
||||
|
|
@ -116,7 +130,7 @@ public:
|
|||
static MlValue string(const std::string &s);
|
||||
static MlValue nil();
|
||||
|
||||
MlValue(const std::vector<MlValue> ¶ms, MlValue ret, MlEnvironment const &env); // Construct a lambda function
|
||||
MlValue(const std::vector<MlValue> ¶ms, MlValue ret, MlEnvironment const &env, const Type ftype); // Construct a lambda or macro function
|
||||
MlValue(const std::string &name, Builtin b); // Construct a builtin function
|
||||
|
||||
std::vector<std::string> get_used_atoms();
|
||||
|
|
@ -128,6 +142,7 @@ public:
|
|||
MlValue eval(MlEnvironment &env);
|
||||
|
||||
bool is_builtin() const;
|
||||
bool is_macro() const;
|
||||
bool is_number() const;
|
||||
bool is_string() const;
|
||||
bool is_list() const;
|
||||
|
|
@ -175,19 +190,6 @@ public:
|
|||
friend std::ostream &operator<<(std::ostream &os, MlValue const &v);
|
||||
|
||||
private:
|
||||
enum {
|
||||
QUOTE,
|
||||
ATOM,
|
||||
INT,
|
||||
FLOAT,
|
||||
LIST,
|
||||
STRING,
|
||||
LAMBDA,
|
||||
BUILTIN,
|
||||
NIL,
|
||||
TRUE
|
||||
} type;
|
||||
|
||||
union {
|
||||
long i;
|
||||
double f;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ public:
|
|||
|
||||
void turnOn();
|
||||
void debugOn();
|
||||
bool isDebugOn() { return debugStacktraceOn; }
|
||||
|
||||
void add_method_call(const MlValue &function, const std::vector<MlValue> &args);
|
||||
void end_method_call();
|
||||
|
|
|
|||
|
|
@ -86,6 +86,9 @@
|
|||
(defn doc::appropos (which)
|
||||
(doc::look which))
|
||||
|
||||
(defn doc::lookup (which)
|
||||
(doc::look which))
|
||||
|
||||
;(defn doc::section (which)
|
||||
; (print (term-red "implement me!")))
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,26 @@
|
|||
(defn tenth (l) (index l 9))
|
||||
(defn nth (i l) (index l (- i 1)))
|
||||
|
||||
|
||||
(defn list? (e) (= (type e) "list"))
|
||||
(defn empty-list? (e) (and (list? e) (= (len e) 0)))
|
||||
(defn string? (e) (= (type e) "string"))
|
||||
(defn int? (e) (= (type e) "int"))
|
||||
(defn float? (e) (= (type e) "float"))
|
||||
(defn nil? (e) (= (type e) "nil"))
|
||||
(defn true? (e) (= (type e) "#t"))
|
||||
(defn function? (e) (= (type e) "function"))
|
||||
|
||||
|
||||
(defmacro unless (test v)
|
||||
(list 'if (list 'not test) v))
|
||||
|
||||
(defmacro dotimes (v n body)
|
||||
(list 'for v '(range 0 (eval n))
|
||||
body
|
||||
))
|
||||
|
||||
|
||||
; return 1 when list contains item otherwise nil
|
||||
(defn member (lst itm)
|
||||
(do
|
||||
|
|
@ -92,8 +112,6 @@
|
|||
(defn make-list (size)
|
||||
(make-list-of size nil))
|
||||
|
||||
(defn empty-list? (lst) (and (= (type lst) "list") (= (len lst) 0)))
|
||||
|
||||
(defn uniq (lst)
|
||||
(do
|
||||
(def rslt '())
|
||||
|
|
@ -126,6 +144,12 @@
|
|||
(if (> (len lst) n)
|
||||
(map (lambda (i) (index lst i)) (range 0 n))
|
||||
lst))
|
||||
|
||||
(defn concat-lists (seq1 seq2)
|
||||
(do (def l seq1)
|
||||
(dotimes i (len seq2)
|
||||
(set! l (push l (index seq2 i))))
|
||||
))
|
||||
|
||||
(defn quick-sort-by (l cmp)
|
||||
(if (<= (len l) 1)
|
||||
|
|
@ -253,5 +277,6 @@
|
|||
)
|
||||
))
|
||||
|
||||
; load and init do system
|
||||
|
||||
; load and init doc system
|
||||
(include "/usr/local/var/mlisp/doc.lsp")
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
</dict>
|
||||
</dict>
|
||||
<key>match</key>
|
||||
<string>(?:\()((?i:defn|def|lambda)+)</string>
|
||||
<string>(?:\()((?i:defn|defmacro|def|lambda)+)</string>
|
||||
<key>name</key>
|
||||
<string>meta.function.lisp</string>
|
||||
</dict>
|
||||
|
|
|
|||
Loading…
Reference in New Issue