Browse Source

on the way to better errors

master
FelixBrendel 7 years ago
parent
commit
dc40059d90
12 changed files with 247 additions and 115 deletions
  1. +1
    -1
      .dir-locals.el
  2. +42
    -0
      src/defines.cpp
  3. +2
    -2
      src/env.cpp
  4. +57
    -28
      src/error.cpp
  5. +48
    -28
      src/eval.cpp
  6. +9
    -2
      src/forward_decls.cpp
  7. +9
    -9
      src/io.cpp
  8. +28
    -2
      src/memory.cpp
  9. +20
    -15
      src/parse.cpp
  10. +5
    -6
      src/slime.h
  11. +25
    -22
      src/structs.cpp
  12. +1
    -0
      todo.org

+ 1
- 1
.dir-locals.el View File

@@ -36,7 +36,7 @@
(font-lock-add-keywords 'c++-mode
'(("\\<\\(if_debug\\|if_windows\\|if_linux\\|defer\\|proc\\)\\>" .
font-lock-keyword-face)))))))
(c++-mode . ((eval . (company-clang-set-prefix "main.cpp"))
(c++-mode . ((eval . (company-clang-set-prefix "slime.h"))
(eval . (flycheck-mode 0))
(eval . (rainbow-mode 0))
)))

+ 42
- 0
src/defines.cpp View File

@@ -122,6 +122,48 @@ struct {
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define defer auto TOKENPASTE2(__deferred_lambda_call, __COUNTER__) = deferrer << [&]

/*
Usage of the create_error_macros:

*/
#define __create_error(keyword, ...) \
create_error( \
__FILE__, __LINE__, \
Memory::get_or_create_lisp_object_keyword(keyword), \
__VA_ARGS__)


#define create_out_of_memory_error(...) \
__create_error("out-of-memory", __VA_ARGS__)

#define create_generic_error(...) \
__create_error("generic", __VA_ARGS__)

#define create_parsing_error(...) \
__create_error("parsing-error", __VA_ARGS__)

#define create_symbol_undefined_error(...) \
__create_error("symbol-undefined", __VA_ARGS__)

#define create_type_missmatch_error(expected, actual) \
__create_error("type-missmatch", \
"Type missmatch: expected %s, got %s", \
expected, actual)

#define create_wrong_number_of_arguments_error(expected, actual) \
__create_error("wrong-number-of-arguments", \
"Wrong number of arguments: expected %d, got %d", \
expected, actual)


#define assert_type(_node, _type) \
do { \
if (_node->type != _type) { \
create_type_missmatch_error("symbol", Lisp_Object_Type_to_string(_node->type)); \
return nullptr; \
} \
} while(0)

#define console_normal "\x1B[0m"
#define console_red "\x1B[31m"
#define console_green "\x1B[32m"


+ 2
- 2
src/env.cpp View File

@@ -45,8 +45,8 @@ proc lookup_symbol(Lisp_Object* node, Environment* env) -> Lisp_Object* {
return Memory::t;
}

printf("%s\n", Memory::get_c_str(identifier));
create_error(Error_Type::Symbol_Not_Defined, node->sourceCodeLocation);
create_symbol_undefined_error("The symbol '%s' is not defined.", &identifier->data);
return nullptr;
}



+ 57
- 28
src/error.cpp View File

@@ -7,40 +7,69 @@ proc delete_error() -> void {
}
}

proc create_error(Error_Type type, Source_Code_Location* location) -> void {
proc create_error(const char* c_file_name, int c_file_line, Lisp_Object* type, String* message) -> void {
if_debug {
printf("Error created in:\n%s:%d\n", c_file_name, c_file_line);
}

delete_error();
debug_break();

error = new(Error);
error->type = type;
error->location = location;
error->message = message;
}

proc Error_Type_to_string(Error_Type type) -> const char* {
switch (type) {
case Error_Type::Assertion_Error: return "Assertion failed";
case Error_Type::File_Not_Found: return "File not found";
case Error_Type::Ill_Formed_Arguments: return "Evaluation-error: Ill formed arguments";
case Error_Type::Ill_Formed_Lambda_List: return "Evaluation-error: Ill formed lambda list";
case Error_Type::Ill_Formed_List: return "Evaluation-error: Ill formed list";
case Error_Type::Not_A_Function: return "Evaluation-error: Not a function";
case Error_Type::Not_Yet_Implemented: return "Evaluation-error: Not yet implemented";
case Error_Type::Symbol_Not_Defined: return "Evaluation-error: Symbol not defined";
case Error_Type::Syntax_Error: return "Syntax Error";
case Error_Type::Trailing_Garbage: return "Evaluation-error: Trailing garbage following expression";
case Error_Type::Type_Missmatch: return "Evaluation-error: Type Missmatch";
case Error_Type::Unbalanced_Parenthesis: return "Parsing-error: Unbalanced parenthesis";
case Error_Type::Unexpected_Eof: return "Parsing-error: Unexpected EOF";
case Error_Type::Unknown_Keyword_Argument: return "Evaluation-error: Unknown keyword argument";
case Error_Type::Wrong_Number_Of_Arguments: return "Evaluation-error: Wrong number of arguments";
case Error_Type::Out_Of_Memory: return "Runtime-error: Out of memory";
default: return "this error type doesn't have a desciption..";
}
}

proc assert_type(Lisp_Object* node, Lisp_Object_Type type) -> void {
if (!node)
create_error(Error_Type::Unknown_Error, nullptr);
if (node->type == type) return;
create_error(Error_Type::Type_Missmatch, node->sourceCodeLocation);
proc create_error(const char* c_file_name, int c_file_line, Lisp_Object* type, const char* format, ...) -> void {
// HACK(Felix): the length of all error strings is 200!!!!!!!!!!

int length = 200;
String* formatted_string = Memory::create_string("", length);

int written_length;
va_list args;
va_start(args, format);
written_length = vsnprintf(&formatted_string->data, length, format, args);
va_end(args);

formatted_string->length = written_length;

create_error(c_file_name, c_file_line, type, formatted_string);
}

// proc Error_Type_to_string(Error_Type type) -> const char* {
// switch (type) {
// case Error_Type::Assertion_Error: return "Assertion failed";
// case Error_Type::File_Not_Found: return "File not found";
// case Error_Type::Ill_Formed_Arguments: return "Evaluation-error: Ill formed arguments";
// case Error_Type::Ill_Formed_Lambda_List: return "Evaluation-error: Ill formed lambda list";
// case Error_Type::Ill_Formed_List: return "Evaluation-error: Ill formed list";
// case Error_Type::Not_A_Function: return "Evaluation-error: Not a function";
// case Error_Type::Not_Yet_Implemented: return "Evaluation-error: Not yet implemented";
// case Error_Type::Symbol_Not_Defined: return "Evaluation-error: Symbol not defined";
// case Error_Type::Syntax_Error: return "Syntax Error";
// case Error_Type::Trailing_Garbage: return "Evaluation-error: Trailing garbage following expression";
// case Error_Type::Type_Missmatch: return "Evaluation-error: Type Missmatch";
// case Error_Type::Unbalanced_Parenthesis: return "Parsing-error: Unbalanced parenthesis";
// case Error_Type::Unexpected_Eof: return "Parsing-error: Unexpected EOF";
// case Error_Type::Unknown_Keyword_Argument: return "Evaluation-error: Unknown keyword argument";
// case Error_Type::Wrong_Number_Of_Arguments: return "Evaluation-error: Wrong number of arguments";
// case Error_Type::Out_Of_Memory: return "Runtime-error: Out of memory";
// default: return "this error type doesn't have a desciption..";
// }
// }

// proc assert_type(Lisp_Object* node, Lisp_Object_Type type) -> void {
// if (!node) {
// create_generic_error(
// "The node where the type should have"
// "been checked was nullptr.");
// return;
// }

// if (node->type != type)
// create_type_missmatch_error(
// ""
// );
// }

+ 48
- 28
src/eval.cpp View File

@@ -5,18 +5,17 @@ proc apply_arguments_to_function(Lisp_Object* arguments, Function* function) ->

// positional arguments
for (int i = 0; i < function->positional_arguments->next_index; ++i) {
if (arguments->type == Lisp_Object_Type::Pair) {
// TODO(Felix): here we create new lisp_object_symbols from
// their identifiers but before we converted them to
// strings from symbols... Wo maybe just use the symbols?
define_symbol(
Memory::get_or_create_lisp_object_symbol(function->positional_arguments->identifiers[i]),
arguments->value.pair.first, new_env);
} else {

create_error(Error_Type::Ill_Formed_Arguments, arguments->sourceCodeLocation);
if (arguments->type != Lisp_Object_Type::Pair) {
create_wrong_number_of_arguments_error(function->positional_arguments->next_index, i);
return nullptr;
}
// TODO(Felix): here we create new lisp_object_symbols from
// their identifiers but before we converted them to
// strings from symbols... Wo maybe just use the symbols?
define_symbol(
Memory::get_or_create_lisp_object_symbol(function->positional_arguments->identifiers[i]),
arguments->value.pair.first, new_env);

arguments = arguments->value.pair.rest;
}

@@ -46,8 +45,9 @@ proc apply_arguments_to_function(Lisp_Object* arguments, Function* function) ->
// necessary keywords then we have to count the rest
// as :rest here, instead od always creating an error
// (special case with default variables)
puts("??");
create_error(Error_Type::Ill_Formed_Arguments, arguments->sourceCodeLocation);
create_generic_error(
"The function does not take the keyword argument ':%s'",
&(arguments->value.pair.first->value.identifier));
return nullptr;
}

@@ -61,7 +61,9 @@ proc apply_arguments_to_function(Lisp_Object* arguments, Function* function) ->
// necessary keywords then we have to count the rest
// as :rest here, instead od always creating an error
// (special case with default variables)
create_error(Error_Type::Ill_Formed_Arguments, arguments->sourceCodeLocation);
create_generic_error(
"The function already read the keyword argument ':%s'",
&(arguments->value.pair.first->value.identifier));
return nullptr;
}
}
@@ -70,7 +72,9 @@ proc apply_arguments_to_function(Lisp_Object* arguments, Function* function) ->
// not already read in, is there a next element to actually
// set it to?
if (arguments->value.pair.rest->type != Lisp_Object_Type::Pair) {
create_error(Error_Type::Ill_Formed_Arguments, arguments->sourceCodeLocation);
create_generic_error(
"Attempting to set the keyword argument ':%s', but no value was supplied.",
&(arguments->value.pair.first->value.identifier));
return nullptr;
}

@@ -107,7 +111,10 @@ proc apply_arguments_to_function(Lisp_Object* arguments, Function* function) ->
if (function->keyword_arguments->values->data[i] == nullptr) {
// if this one does not have a default value
if (!was_set) {
create_error(Error_Type::Ill_Formed_Arguments, arguments->sourceCodeLocation);
create_generic_error(
"There was no value supplied for the required "
"keyword argument ':%s'.",
&defined_keyword->data);
return nullptr;
}
} else {
@@ -135,7 +142,9 @@ proc apply_arguments_to_function(Lisp_Object* arguments, Function* function) ->
arguments, new_env);
} else {
// rest was not declared but additional arguments were found
create_error(Error_Type::Ill_Formed_Arguments, arguments->sourceCodeLocation);
create_generic_error(
"A rest argument was not declared "
"but the function was called with additional arguments.");
return nullptr;
}
}
@@ -180,13 +189,17 @@ proc parse_argument_list(Lisp_Object* arguments, Function* function) -> void {
string_equal(arguments->value.pair.first->value.identifier, "rest"))
break;
else {
create_error(Error_Type::Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
create_parsing_error("A non recognized marker was found "
"in the lambda list: ':%s'",
&arguments->value.pair.first->value.identifier);
return;
}
}

if (arguments->value.pair.first->type != Lisp_Object_Type::Symbol) {
create_error(Error_Type::Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
create_parsing_error("Only symbols and keywords can be "
"parsed here, but found '%s'",
Lisp_Object_Type_to_string(arguments->value.pair.first->type));
return;
}

@@ -202,7 +215,7 @@ proc parse_argument_list(Lisp_Object* arguments, Function* function) -> void {
// keywords,
if (arguments->type != Lisp_Object_Type::Pair) {
if (arguments->type != Lisp_Object_Type::Nil)
create_error(Error_Type::Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
create_parsing_error("The lambda list must be nil terminated.");
return;
}

@@ -210,25 +223,32 @@ proc parse_argument_list(Lisp_Object* arguments, Function* function) -> void {
string_equal(arguments->value.pair.first->value.identifier, "keys"))
{
arguments = arguments->value.pair.rest;
if (arguments->type != Lisp_Object_Type::Pair ||
arguments->value.pair.first->type != Lisp_Object_Type::Symbol)
{
create_error(Error_Type::Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
return;
if (arguments->type != Lisp_Object_Type::Pair) {
create_parsing_error("Actual keys have to follow the :keys indicator.");
}
// if (arguments->value.pair.first->type != Lisp_Object_Type::Symbol) {
// create_parsing_error(
// "Only symbols can be parsed here, but found '%s'.",
// Lisp_Object_Type_to_string(arguments->value.pair.first->type));
// return;
// }

while (arguments->type == Lisp_Object_Type::Pair) {
if (arguments->value.pair.first->type == Lisp_Object_Type::Keyword) {
if (string_equal(arguments->value.pair.first->value.identifier, "rest"))
break;
else {
create_error(Error_Type::Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
create_parsing_error(
"Only the :rest keyword can be parsed here, but got ':%s'.",
&arguments->value.pair.first->value.identifier->data);
return;
}
}

if (arguments->value.pair.first->type != Lisp_Object_Type::Symbol) {
create_error(Error_Type::Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
create_parsing_error(
"Only symbols can be parsed here, but found '%s'.",
Lisp_Object_Type_to_string(arguments->value.pair.first->type));
return;
}

@@ -250,7 +270,7 @@ proc parse_argument_list(Lisp_Object* arguments, Function* function) -> void {
next->value.pair.first);
arguments = next->value.pair.rest;
} else {
create_error(Error_Type::Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
create_parsing_error("Expecting a value after 'defaults-to'");
return;
}
} else {
@@ -268,7 +288,7 @@ proc parse_argument_list(Lisp_Object* arguments, Function* function) -> void {
// if there is a rest argument
if (arguments->type != Lisp_Object_Type::Pair) {
if (arguments->type != Lisp_Object_Type::Nil)
create_error(Error_Type::Ill_Formed_Lambda_List, arguments->sourceCodeLocation);
create_parsing_error("The lambda list must be nil terminated.");
return;
}



+ 9
- 2
src/forward_decls.cpp View File

@@ -1,6 +1,8 @@
proc assert_type(Lisp_Object*, Lisp_Object_Type) -> void;
// proc assert_type(Lisp_Object*, Lisp_Object_Type) -> void;
proc built_in_load(String*, Environment*) -> Lisp_Object*;
proc create_error(Error_Type, Source_Code_Location*) -> void;
proc create_error(const char* c_file_name, int c_file_line, Lisp_Object* type, String* message) -> void;
proc create_error(const char* c_file_name, int c_file_line, Lisp_Object* type, const char* format, ...) -> void;
proc create_error(Lisp_Object* type, const char* message, const char* c_file_name, int c_file_line) -> void;
proc eval_arguments(Lisp_Object*, Environment*, int*) -> Lisp_Object*;
proc eval_expr(Lisp_Object*, Environment*) -> Lisp_Object*;
proc is_truthy (Lisp_Object*, Environment*) -> bool;
@@ -8,3 +10,8 @@ proc list_length(Lisp_Object*) -> int;
proc load_built_ins_into_environment(Environment*) -> void;
proc parse_argument_list(Lisp_Object*, Function*) -> void;
proc print_environment(Environment*) -> void;

proc Lisp_Object_Type_to_string(Lisp_Object_Type type) -> const char*;
namespace Memory {
proc get_or_create_lisp_object_keyword(const char* identifier) -> Lisp_Object*;
}

+ 9
- 9
src/io.cpp View File

@@ -281,19 +281,19 @@ proc print(Lisp_Object* node, bool print_quotes = false, FILE* file = stdout) ->


proc print_error_location() -> void {
if (error->location) {
printf("%s (line %d, position %d)",
Memory::get_c_str(error->location->file),
error->location->line,
error->location->column);
} else {
printf("no source code location avaliable");
}
// if (error->location) {
// printf("%s (line %d, position %d)",
// Memory::get_c_str(error->location->file),
// error->location->line,
// error->location->column);
// } else {
// printf("no source code location avaliable");
// }
}

proc log_error() -> void {
printf("%s%s%s\n", console_red,
Error_Type_to_string(error->type),
Memory::get_c_str(error->message),
console_normal);
printf(" in: %s", console_cyan);
print_error_location();


+ 28
- 2
src/memory.cpp View File

@@ -3,7 +3,7 @@ namespace Memory {
// ------------------
// lisp_object
// ------------------
int object_memory_size;
int object_memory_size;
Int_Array_List* free_spots_in_object_memory;
Lisp_Object* object_memory;
int next_index_in_object_memory = 0;
@@ -82,13 +82,33 @@ namespace Memory {
return create_string(str, (int)strlen(str));
}

// proc create_string_formatted (const char* format, ...) -> String* {
// // HACK(Felix): the length of all strings is 200!!!!!!!!!!
// // HACK(Felix): the length of all strings is 200!!!!!!!!!!
// int length = 200;
// String* ret = create_string("", length);

// int written_length;
// va_list args;
// va_start(args, format);
// written_length = vsnprintf(&ret->data, length, format, args);
// va_end(args);

// ret->length = written_length;
// return ret;
// }

proc create_lisp_object() -> Lisp_Object* {
int index;
// if we have no free spots then append at the end
if (free_spots_in_object_memory->next_index == 0) {
// if we still have space
if (object_memory_size == next_index_in_object_memory) {
create_error(Error_Type::Out_Of_Memory, nullptr);
create_out_of_memory_error(
"There is not enough space in the lisp object"
"memory to allocate additional lisp objects. "
"Maybe try increasing the Memory size when "
"calling Memory::init()");
return nullptr;
}
index = next_index_in_object_memory++;
@@ -162,6 +182,9 @@ namespace Memory {
}

proc get_or_create_lisp_object_symbol(const char* identifier) -> Lisp_Object* {
// TODO(Felix): This is really bad: we create a new string
// even if the symbol/keyword is already existing, just to
// check IF it exists and then never deleting it.
return get_or_create_lisp_object_symbol(
Memory::create_string(identifier));
}
@@ -177,6 +200,9 @@ namespace Memory {
}

proc get_or_create_lisp_object_keyword(const char* keyword) -> Lisp_Object* {
// TODO(Felix): This is really bad: we create a new string
// even if the symbol/keyword is already existing, just to
// check IF it exists and then never deleting it.
return get_or_create_lisp_object_keyword(
Memory::create_string(keyword));
}


+ 20
- 15
src/parse.cpp View File

@@ -166,15 +166,15 @@ namespace Parser {
// we found the end of the string
text[*index_in_text+string_length] = '\0';

String* string = Memory::create_string("", string_length);

if (!unescape_string(text+(*index_in_text))) {
create_error(
Error_Type::Unknown_Error,
create_source_code_location(parser_file, parser_line, parser_col));
create_parsing_error(
"The string '%s' at %s:%d:%d could not be unescaped.",
text+(*index_in_text), parser_file, parser_line, parser_col);
return nullptr;
}

String* string = Memory::create_string("", string_length);

// TODO(Felix): manually copy to parse control sequences
// correctly without the need to unescape the string, also
// better for keeping track of the encountered new lines and
@@ -314,7 +314,9 @@ namespace Parser {

eat_until_code(text, index_in_text);
if (text[(*index_in_text)] == '\0') {
create_error(Error_Type::Unexpected_Eof, create_source_code_location(parser_file, parser_line, parser_col));
create_parsing_error(
"Unexpected EOF in %s:%d:%d",
parser_file, parser_line, parser_col);
return nullptr;
}

@@ -337,7 +339,9 @@ namespace Parser {
eat_until_code(text, index_in_text);

if (text[(*index_in_text)] != ')')
create_error(Error_Type::Syntax_Error, create_source_code_location(parser_file, parser_line, parser_col));
create_parsing_error(
"Expected ')' after the element after the '.' in %s:%d:%d",
create_source_code_location(parser_file, parser_line, parser_col));
++parser_col;
++(*index_in_text);
break;
@@ -362,14 +366,11 @@ namespace Parser {

// (define-syntax defun (name args :rest body) (...))
if (arguments_length < 2) {
create_error(Error_Type::Wrong_Number_Of_Arguments, expression->sourceCodeLocation);
create_wrong_number_of_arguments_error(3, arguments_length);
return nullptr;
}

if (arguments->value.pair.first->type != Lisp_Object_Type::Symbol) {
create_error(Error_Type::Type_Missmatch, expression->sourceCodeLocation);
return nullptr;
}
assert_type(arguments->value.pair.first, Lisp_Object_Type::Symbol);

// extract the name
Lisp_Object* symbol_for_macro = arguments->value.pair.first;
@@ -429,7 +430,8 @@ namespace Parser {
// that, when an entry is not found immidiately, we
// know for sure that it does not exist in the table.

create_error(Error_Type::Not_Yet_Implemented, expression->sourceCodeLocation);
create_generic_error("deleting macros has not yet be implemented,"
"and I don't know if it is a good idea to do so.");
return nullptr;
} else {
// if threre is a macro named like this, then macroexpand
@@ -478,7 +480,9 @@ namespace Parser {
eat_until_code(text, &index_in_text);
if (text[(index_in_text)] == '\0')
return result;
create_error(Error_Type::Trailing_Garbage, create_source_code_location(parser_file, parser_line, parser_col));

create_parsing_error("Trainling garbage after expression at %s:%d:%d",
parser_file, parser_line, parser_col);
return nullptr;
}

@@ -573,7 +577,8 @@ namespace Parser {
} break;
default:
/* syntax error */
create_error(Error_Type::Syntax_Error, create_source_code_location(parser_file, parser_line, parser_col));
create_parsing_error("Garbage in file scope at %s:%d:%d",
parser_file, parser_line, parser_col);
return nullptr;
}
}


+ 5
- 6
src/slime.h View File

@@ -6,11 +6,13 @@
#include <string.h>
#include <cmath>
#include <ctype.h>
// #include <type_traits>
#include <stdarg.h>
#include <functional>

#undef _CRT_SECURE_NO_DEPRECATE
#undef _CRT_SECURE_NO_WARNINGS

namespace Slime {
// namespace Slime {
#include "./defines.cpp"
#include "./structs.cpp"
#include "./forward_decls.cpp"
@@ -24,7 +26,4 @@ namespace Slime {
#include "./built_ins.cpp"
#include "./testing.cpp"
#include "./undefines.cpp"
}

#undef _CRT_SECURE_NO_DEPRECATE
#undef _CRT_SECURE_NO_WARNINGS
// }

+ 25
- 22
src/structs.cpp View File

@@ -26,25 +26,25 @@ enum struct Function_Type {
Macro
};

enum struct Error_Type {
Assertion_Error,
File_Not_Found,
Ill_Formed_Arguments,
Ill_Formed_Lambda_List,
Ill_Formed_List,
Not_A_Function,
Not_Yet_Implemented,
Out_Of_Memory,
Symbol_Not_Defined,
Syntax_Error,
Trailing_Garbage,
Type_Missmatch,
Unbalanced_Parenthesis,
Unexpected_Eof,
Unknown_Error,
Unknown_Keyword_Argument,
Wrong_Number_Of_Arguments,
};
// enum struct Error_Type {
// Assertion_Error,
// File_Not_Found,
// Ill_Formed_Arguments,
// Ill_Formed_Lambda_List,
// Ill_Formed_List,
// Not_A_Function,
// Not_Yet_Implemented,
// Out_Of_Memory,
// Symbol_Not_Defined,
// Syntax_Error,
// Trailing_Garbage,
// Type_Missmatch,
// Unbalanced_Parenthesis,
// Unexpected_Eof,
// Unknown_Error,
// Unknown_Keyword_Argument,
// Wrong_Number_Of_Arguments,
// };

enum struct Log_Level {
None,
@@ -141,12 +141,15 @@ struct Environment {

int capacity;
int next_index;

// TODO(Felix): Use a hashmap here.
char** keys;
char** keys;
Lisp_Object** values;
};

struct Error {
Error_Type type;
Source_Code_Location* location;
Lisp_Object* position;
// type has to be a keyword
Lisp_Object* type;
String* message;
};

+ 1
- 0
todo.org View File

@@ -1,3 +1,4 @@
* TODO rename sliem to plisk
* TODO BUG 1: eval dot notation
#+BEGIN_SRC lisp
(eval `(+ . ,(list 1 2 3)))


Loading…
Cancel
Save