Просмотр исходного кода

Fixed a bug where cs was accessed out of bouds, gensyms, 'and and 'or are now macros

master
Felix Brendel 6 лет назад
Родитель
Сommit
e22ffb7354
5 измененных файлов: 162 добавлений и 197 удалений
  1. +33
    -0
      bin/pre.slime
  2. +10
    -32
      src/built_ins.cpp
  3. +5
    -1
      src/env.cpp
  4. +113
    -164
      src/eval.cpp
  5. +1
    -0
      src/forward_decls.cpp

+ 33
- 0
bin/pre.slime Просмотреть файл

@@ -25,6 +25,39 @@
(define-syntax (mac a) (list + 1 1)) (define-syntax (mac a) (list + 1 1))
(define-syntax (add . args) (pair '+ args)) (define-syntax (add . args) (pair '+ args))
(define-syntax (and . args)
;; (and cond1 cond2 (cond3 args))
;; ->
;; (if cond1
;; (if cond2
;; (let ((g (cond3 args)))
;; (if g
;; g
;; ()))
;; ())
;; ())
(if args
`(,if ,(first args)
,(apply and (rest args))
())
t))
(define-syntax (or . args)
;; (or cond1 cond2 (cond3 args))
;; ->
;; (if cond1
;; t
;; (if cond2
;; t
;; (if (cond3 args)
;; t
;; ())))
(if args
`(,if ,(first args)
t
,(apply or (rest args)))
()))
(define-syntax (when condition . body) (define-syntax (when condition . body)
"Special form for when multiple actions should be done if a "Special form for when multiple actions should be done if a
condition is true. condition is true.


+ 10
- 32
src/built_ins.cpp Просмотреть файл

@@ -543,6 +543,14 @@ namespace Slime {
return Memory::create_lisp_object(x); return Memory::create_lisp_object(x);
}; };
define((gensym), "TODO") {
profile_with_name("(gensym)");
Lisp_Object* node;
try node = Memory::create_lisp_object();
Memory::set_type(node, Lisp_Object_Type::Symbol);
node->value.symbol = Memory::create_string("gensym");
return node;
};
define_special((bound? var), "TODO") { define_special((bound? var), "TODO") {
profile_with_name("(bound?)"); profile_with_name("(bound?)");
fetch(var); fetch(var);
@@ -807,40 +815,10 @@ namespace Slime {
expr = unquoteSomeExpressions(unquoteSomeExpressions, expr); expr = unquoteSomeExpressions(unquoteSomeExpressions, expr);
return expr; return expr;
}; };
define_special((and . args), "TODO") {
profile_with_name("(and)");
fetch(args);
bool result = true;
in_caller_env {
for_lisp_list (args) {
try result &= is_truthy(it);
if (!result)
return Memory::nil;
}
}
return Memory::t;
};
define_special((or . args), "TODO") {
profile_with_name("(or)");
fetch(args);
bool result = false;
in_caller_env {
for_lisp_list (args) {
try result |= is_truthy(it);
if (result)
return Memory::t;
}
}
return Memory::nil;
};
define_special((not test), "TODO") {
define((not test), "TODO") {
profile_with_name("(not)"); profile_with_name("(not)");
fetch(test); fetch(test);
bool truthy;
in_caller_env {
try truthy = is_truthy(test);
}
return (truthy) ? Memory::nil : Memory::t;
return is_truthy(test) ? Memory::nil : Memory::t;
}; };
// // // defun("while", "TODO", __LINE__, cLambda { // // // defun("while", "TODO", __LINE__, cLambda {
// // // try arguments_length = list_length(arguments); // // // try arguments_length = list_length(arguments);


+ 5
- 1
src/env.cpp Просмотреть файл

@@ -1,7 +1,11 @@
namespace Slime { namespace Slime {
proc define_symbol(Lisp_Object* symbol, Lisp_Object* value) -> void { proc define_symbol(Lisp_Object* symbol, Lisp_Object* value) -> void {
profile_with_comment(&symbol->value.symbol->data); profile_with_comment(&symbol->value.symbol->data);
Environment* env = get_current_environment();
define_symbol(symbol, value, get_current_environment());
}
proc define_symbol(Lisp_Object* symbol, Lisp_Object* value, Environment* env) -> void {
profile_with_comment(&symbol->value.symbol->data);
env->hm.set_object((void*)symbol, value); env->hm.set_object((void*)symbol, value);
} }


+ 113
- 164
src/eval.cpp Просмотреть файл

@@ -1,38 +1,18 @@
namespace Slime { namespace Slime {
proc create_extended_environment_for_function_application_nrc( proc create_extended_environment_for_function_application_nrc(
// TODO(Felix): pass cs as value as soon as we got rid of
// destructors, to prevent destroying it on scope exit
Array_List<Lisp_Object*>* cs,
Lisp_Object* function, Lisp_Object* function,
int arg_start, int arg_start,
int arg_end) -> Environment* int arg_end) -> Environment*
{ {
profile_this(); profile_this();
using namespace Globals::Current_Execution;
int arg_pos = arg_start;
int index_of_next_arg = arg_start;
bool is_c_function = function->value.function->is_c; bool is_c_function = function->value.function->is_c;
Environment* new_env = Memory::create_child_environment(function->value.function->parent_environment);
Environment* env = Memory::create_child_environment(function->value.function->parent_environment);
Arguments* arg_spec = &function->value.function->args; Arguments* arg_spec = &function->value.function->args;
// NOTE(Felix): Step 1.
// - setting the parent environment
// - setting the arg_spec
// - potentially evaluating the arguments
// NOTE(Felix): Even though we will return the environment at the
// end, for defining symbols here for the parameters, it has to be
// on the envi stack.
push_environment(new_env);
defer {
pop_environment();
};
// NOTE(Felix): Step 2.
// Reading the argument spec and fill in the environment
// for the function call
Lisp_Object* sym, *val; // used as temp storage to use `try`
Array_List<Lisp_Object*> read_in_keywords; Array_List<Lisp_Object*> read_in_keywords;
read_in_keywords.alloc(); read_in_keywords.alloc();
defer { defer {
@@ -41,201 +21,173 @@ namespace Slime {
int obligatory_keywords_count = 0; int obligatory_keywords_count = 0;
int read_obligatory_keywords_count = 0; int read_obligatory_keywords_count = 0;
Lisp_Object* next_arg = cs->data[arg_pos];
proc read_positional_args = [&] {
for (int i = 0; i < arg_spec->positional.symbols.next_index; ++i) {
sym = arg_spec->positional.symbols.data[i];
if (arg_pos == arg_end) {
create_parsing_error(
"Not enough positional args supplied. Needed: %d suppied, %d.\n"
"Next missing arg is '%s'",
arg_spec->positional.symbols.next_index, arg_end-arg_pos,
&sym->value.symbol->data);
return;
}
// NOTE(Felix): We have to copy all the arguments,
// otherwise we change the program code.
// XXX(Felix): To C functions we pass by reference.
// TODO(Felix): Why did we decide this??
if (is_c_function) {
define_symbol(sym, next_arg);
} else {
define_symbol(
sym,
Memory::copy_lisp_object_except_pairs(next_arg));
}
assert("cs access index out of range",
arg_pos+1 < cs->next_index);
next_arg = cs->data[++arg_pos];
Lisp_Object* sym;
Lisp_Object* val;
// read positionals
for (int i = 0; i < arg_spec->positional.symbols.next_index; ++i) {
if (index_of_next_arg == arg_end) {
create_parsing_error(
"Not enough positional args supplied. Needed: %d suppied, %d.\n"
"Next missing arg is '%s'",
arg_spec->positional.symbols.next_index, arg_end-index_of_next_arg,
&arg_spec->positional.symbols.data[i]->value.symbol->data);
return nullptr;
} }
};
proc read_keyword_args = [&] {
// debug_break();
// keyword arguments: use all given ones and keep track of the
// added ones (array list), if end of parameters in encountered or
// something that is not a keyword is encountered or a keyword
// that is not recognized is encoutered, jump out of the loop.
if (arg_pos == arg_end) {
return;
// NOTE(Felix): We have to copy all the arguments,
// otherwise we change the program code. To C functions we
// pass by reference for better performance and trust them
// to not mutate the arguments because we expect c
// programmers to know what they are doing. Bold claim I
// know.
if (is_c_function) {
define_symbol(arg_spec->positional.symbols.data[i], cs.data[index_of_next_arg], env);
} else {
define_symbol(arg_spec->positional.symbols.data[i], Memory::copy_lisp_object_except_pairs(cs.data[index_of_next_arg]), env);
} }
++index_of_next_arg;
}
// if there are some left read keywords and rest
if (index_of_next_arg != arg_end) {
// find out how many keyword args we /have/ to read // find out how many keyword args we /have/ to read
for (int i = 0; i < arg_spec->keyword.values.next_index; ++i) { for (int i = 0; i < arg_spec->keyword.values.next_index; ++i) {
if (arg_spec->keyword.values.data[i] == nullptr) if (arg_spec->keyword.values.data[i] == nullptr)
++obligatory_keywords_count; ++obligatory_keywords_count;
else
break;
} }
while (Memory::get_type(next_arg) == Lisp_Object_Type::Keyword) {
while (Memory::get_type(cs.data[index_of_next_arg]) == Lisp_Object_Type::Keyword) {
// check if this one is even an accepted keyword // check if this one is even an accepted keyword
bool accepted = false; bool accepted = false;
for (int i = 0; i < arg_spec->keyword.keywords.next_index; ++i) { for (int i = 0; i < arg_spec->keyword.keywords.next_index; ++i) {
if (next_arg == arg_spec->keyword.keywords.data[i])
{
if (cs.data[index_of_next_arg] == arg_spec->keyword.keywords.data[i]) {
accepted = true; accepted = true;
break; break;
} }
} }
if (!accepted) { if (!accepted) {
// NOTE(Felix): if we are actually done with all the
// necessary keywords then we have to count the rest
// as :rest here, instead od always creating an error
// (special case with default variables)
// if we read all we need then we are done here
if (read_obligatory_keywords_count == obligatory_keywords_count) if (read_obligatory_keywords_count == obligatory_keywords_count)
return;
break;
// otherwise we would have to read more but there
// was a not accepted kwarg, so signal the error
create_generic_error( create_generic_error(
"The function does not take the keyword argument ':%s'\n" "The function does not take the keyword argument ':%s'\n"
"and not all required keyword arguments have been read\n" "and not all required keyword arguments have been read\n"
"in to potentially count it as the rest argument.", "in to potentially count it as the rest argument.",
&(next_arg->value.symbol->data));
return;
&(cs.data[index_of_next_arg]->value.symbol->data));
return nullptr;
} }
// check if it was already read in
// This is an accepted kwarg; check if it was already
// read in
for (int i = 0; i < read_in_keywords.next_index; ++i) { for (int i = 0; i < read_in_keywords.next_index; ++i) {
if (next_arg == read_in_keywords.data[i])
if (cs.data[index_of_next_arg] == read_in_keywords.data[i])
{ {
// NOTE(Felix): if we are actually done with all the
// necessary keywords then we have to count the rest
// as :rest here, instead od always creating an error
// (special case with default variables)
// if we already read it in but also finished
// all other kwargs, then count it as rest and
// be done here
if (read_obligatory_keywords_count == obligatory_keywords_count) if (read_obligatory_keywords_count == obligatory_keywords_count)
return;
goto kw_done;
// If there are some kwargs left to be read
// in, it is an error
create_generic_error( create_generic_error(
"The function already read the keyword argument ':%s'", "The function already read the keyword argument ':%s'",
&(next_arg->value.symbol->data));
return;
&(cs.data[index_of_next_arg]->value.symbol->data));
return nullptr;
} }
} }
// okay so we found a keyword that has to be read in and was // okay so we found a keyword that has to be read in and was
// not already read in, is there a next element to actually // not already read in, is there a next element to actually
// set it to? // set it to?
if (arg_pos == arg_end) {
if (index_of_next_arg+1 == arg_end) {
create_generic_error( create_generic_error(
"Attempting to set the keyword argument ':%s', but no value was supplied.", "Attempting to set the keyword argument ':%s', but no value was supplied.",
&(next_arg->value.symbol->data));
return;
&(cs.data[index_of_next_arg]->value.symbol->data));
return nullptr;
} }
// if not set it and then add it to the array list // if not set it and then add it to the array list
Lisp_Object* key = next_arg;
try_void sym = Memory::get_symbol(key->value.symbol);
next_arg = cs->data[++arg_pos];
Lisp_Object* key = cs.data[index_of_next_arg];
try sym = Memory::get_symbol(key->value.symbol);
++index_of_next_arg;
// NOTE(Felix): It seems we do not need to evaluate the argument here...
if (is_c_function) { if (is_c_function) {
try_void define_symbol(sym, next_arg);
try define_symbol(sym, cs.data[index_of_next_arg], env);
} else { } else {
try_void define_symbol(sym,
Memory::copy_lisp_object_except_pairs(next_arg));
try define_symbol(sym, Memory::copy_lisp_object_except_pairs(cs.data[index_of_next_arg]), env);
} }
read_in_keywords.append(key); read_in_keywords.append(key);
++read_obligatory_keywords_count; ++read_obligatory_keywords_count;
// overstep both for next one
next_arg = cs->data[++arg_pos];
++index_of_next_arg;
if (arg_pos == arg_end) {
if (index_of_next_arg == arg_end) {
break; break;
} }
} }
};
}
proc check_keyword_args = [&]() -> void {
// check if all necessary keywords have been read in
for (int i = 0; i < arg_spec->keyword.values.next_index; ++i) {
auto defined_keyword = arg_spec->keyword.keywords.data[i];
bool was_set = false;
for (int j = 0; j < read_in_keywords.next_index; ++j) {
if (read_in_keywords.data[j] == defined_keyword) {
was_set = true;
break;
}
}
if (arg_spec->keyword.values.data[i] == nullptr) {
// if this one does not have a default value
if (!was_set) {
create_generic_error(
"There was no value supplied for the required "
"keyword argument ':%s'.",
&defined_keyword->value.symbol->data);
return;
}
} else {
// this one does have a default value, lets see if we have
// to use it or if the user supplied his own
if (!was_set) {
try_void sym = Memory::get_symbol(defined_keyword->value.symbol);
if (is_c_function) {
try_void val = arg_spec->keyword.values.data[i];
} else {
try_void val = Memory::copy_lisp_object_except_pairs(arg_spec->keyword.values.data[i]);
}
define_symbol(sym, val);
}
kw_done:
// check keywords for completeness
for (int i = 0; i < arg_spec->keyword.values.next_index; ++i) {
auto defined_keyword = arg_spec->keyword.keywords.data[i];
bool was_set = false;
for (int j = 0; j < read_in_keywords.next_index; ++j) {
if (read_in_keywords.data[j] == defined_keyword) {
was_set = true;
break;
} }
} }
};
proc read_rest_arg = [&]() -> void {
if (arg_pos == arg_end) {
if (arg_spec->rest) {
define_symbol(arg_spec->rest, Memory::nil);
if (arg_spec->keyword.values.data[i] == nullptr) {
// if this one does not have a default value
if (!was_set) {
create_generic_error(
"There was no value supplied for the required "
"keyword argument ':%s'.",
&defined_keyword->value.symbol->data);
return nullptr;
} }
} else { } else {
if (arg_spec->rest) {
Lisp_Object* list;
try_void list = Memory::create_list(next_arg);
Lisp_Object* head = list;
for (++arg_pos;arg_pos < arg_end; ++arg_pos) {
next_arg = cs->data[arg_pos];
try_void head->value.pair.rest = Memory::create_list(next_arg);
head = head->value.pair.rest;
// this one does have a default value, lets see if we have
// to use it or if the user supplied his own
if (!was_set) {
try sym = Memory::get_symbol(defined_keyword->value.symbol);
if (is_c_function) {
try val = arg_spec->keyword.values.data[i];
} else {
try val = Memory::copy_lisp_object_except_pairs(arg_spec->keyword.values.data[i]);
} }
define_symbol(arg_spec->rest, list);
} else {
// rest was not declared but additional arguments were found
create_generic_error(
"A rest argument was not declared "
"but the function was called with additional arguments.");
return;
define_symbol(sym, val, env);
} }
} }
};
}
try read_positional_args();
try read_keyword_args();
try check_keyword_args();
try read_rest_arg();
// read in rest arg
if (index_of_next_arg == arg_end) {
if (arg_spec->rest) {
define_symbol(arg_spec->rest, Memory::nil, env);
}
} else {
if (arg_spec->rest) {
Lisp_Object* list;
try list = Memory::create_list(cs.data[index_of_next_arg]);
Lisp_Object* head = list;
for (++index_of_next_arg;index_of_next_arg < arg_end; ++index_of_next_arg) {
try head->value.pair.rest = Memory::create_list(cs.data[index_of_next_arg]);
head = head->value.pair.rest;
}
define_symbol(arg_spec->rest, list, env);
} else {
// rest was not declared but additional arguments were found
create_generic_error(
"A rest argument was not declared "
"but the function was called with additional arguments.");
return nullptr;
}
}
return new_env;
return env;
} }
@@ -507,7 +459,7 @@ namespace Slime {
Environment* extended_env; Environment* extended_env;
try extended_env = create_extended_environment_for_function_application_nrc( try extended_env = create_extended_environment_for_function_application_nrc(
&cs, function, am+1, cs.next_index);
function, am+1, cs.next_index);
cs.next_index = am; cs.next_index = am;
push_environment(extended_env); push_environment(extended_env);
if (function->value.function->is_c) { if (function->value.function->is_c) {
@@ -558,11 +510,8 @@ namespace Slime {
return cs.data[--cs.next_index]; return cs.data[--cs.next_index];
} }
proc is_truthy(Lisp_Object* expression) -> bool {
Lisp_Object* result;
try result = eval_expr(expression);
return result != Memory::nil;
inline proc is_truthy(Lisp_Object* expression) -> bool {
return expression != Memory::nil;
} }
proc interprete_file (char* file_name) -> Lisp_Object* { proc interprete_file (char* file_name) -> Lisp_Object* {


+ 1
- 0
src/forward_decls.cpp Просмотреть файл

@@ -16,6 +16,7 @@ namespace Slime {
Lisp_Object* lookup_symbol(Lisp_Object* symbol, Environment*); Lisp_Object* lookup_symbol(Lisp_Object* symbol, Environment*);
void define_symbol(Lisp_Object* symbol, Lisp_Object* value); void define_symbol(Lisp_Object* symbol, Lisp_Object* value);
void define_symbol(Lisp_Object* symbol, Lisp_Object* value, Environment* env);
char* lisp_object_to_string(Lisp_Object* node, bool print_repr = true); char* lisp_object_to_string(Lisp_Object* node, bool print_repr = true);
void print(Lisp_Object* node, bool print_repr = false, FILE* file = stdout); void print(Lisp_Object* node, bool print_repr = false, FILE* file = stdout);
void print_environment(Environment*); void print_environment(Environment*);


Загрузка…
Отмена
Сохранить