浏览代码

Can now parse lambda lists

master
FelixBrendel 7 年前
父节点
当前提交
f6c2219bd5
共有 15 个文件被更改,包括 1013 次插入48 次删除
  1. +0
    -0
      bin/test.slime
  2. +1
    -1
      build.bat
  3. +1
    -1
      debug.bat
  4. +1
    -1
      run.bat
  5. +69
    -12
      src/ast.c
  6. +2
    -0
      src/error.c
  7. +190
    -6
      src/eval.c
  8. +1
    -1
      src/init.c
  9. +1
    -0
      src/io.c
  10. +7
    -6
      src/parse.c
  11. +1
    -1
      test.bat
  12. +539
    -0
      todo.html
  13. +197
    -17
      todo.org
  14. +1
    -1
      vs/slime.sln
  15. +2
    -1
      vs/slime.vcxproj

bin/test.lsp → bin/test.slime 查看文件


+ 1
- 1
build.bat 查看文件

@@ -2,7 +2,7 @@
@setlocal
pushd %~dp0

set exeName=lisp.exe
set exeName=slime.exe
set binDir=bin

mkdir quickbuild 2>nul


+ 1
- 1
debug.bat 查看文件

@@ -1,4 +1,4 @@
@echo off
pushd %~dp0\vs
start lisp.sln
start slime.sln
popd

+ 1
- 1
run.bat 查看文件

@@ -4,6 +4,6 @@ pushd %~dp0\bin
call ..\build.bat
if %errorlevel% == 0 (
echo ---------- Running ----------
call timecmd lisp.exe
call timecmd slime.exe
)
popd

+ 69
- 12
src/ast.c 查看文件

@@ -36,14 +36,77 @@ typedef struct {
} Pair;

typedef struct {
Pair* regular_params;
Pair* keyword_params;
} Lambda_List;
struct Ast_Node** data;
int length;
int next_index;
} Ast_Node_Array_List;


typedef struct {
char** identifiers;
int next_index;
int length;
} Positional_Arguments;

typedef struct {
Lambda_List* parameters;
Pair* form;
char* docstring;
char** identifiers;
// values[i] will be nullptr if no defalut value was declared for
// key identifiers[i]
Ast_Node_Array_List* values;
int next_index;
int length;
} Keyword_Arguments;


Ast_Node_Array_List* create_Ast_Node_Array_List(int initial_length);
void append_to_Ast_Node_Array_List(Ast_Node_Array_List* list, struct Ast_Node* node);

Positional_Arguments* create_positional_argument_list(int initial_capacity) {
Positional_Arguments* ret = new(Positional_Arguments);
ret->identifiers = (char**)malloc(initial_capacity * sizeof(char*));
ret->next_index = 0;
ret->length = initial_capacity;
return ret;
}

void append_to_positional_argument_list(Positional_Arguments* args, char* identifier) {
if (args->next_index == args->length) {
args->length *= 2;
args->identifiers = (char**)realloc(args->identifiers, args->length * sizeof(char*));
}
args->identifiers[args->next_index++] = identifier;
}

Keyword_Arguments* create_keyword_argument_list(int initial_capacity) {
Keyword_Arguments* ret = new(Keyword_Arguments);
ret->identifiers = (char**)malloc(initial_capacity * sizeof(char*));
ret->values = create_Ast_Node_Array_List(initial_capacity);
ret->next_index = 0;
ret->length = initial_capacity;
return ret;
}

void append_to_keyword_argument_list(Keyword_Arguments* args,
char* identifier,
struct Ast_Node* default_value)
{
if (args->next_index == args->length) {
args->length *= 2;
args->identifiers = (char**)realloc(args->identifiers, args->length * sizeof(char*));
}

args->identifiers[args->next_index++] = identifier;
append_to_Ast_Node_Array_List(args->values, default_value);
}


typedef struct {
char* docstring;
Positional_Arguments* positional_arguments;
Keyword_Arguments* keyword_arguments;
// rest_argument will be nullptr if no rest argument is declared
char* rest_argument;
struct Ast_Node* body; // implicit prog
} Function;

typedef enum {
@@ -134,12 +197,6 @@ struct Ast_Node {
// was forward declarated
typedef struct Ast_Node Ast_Node;

typedef struct {
Ast_Node** data;
int length;
int next_index;
} Ast_Node_Array_List;

typedef struct {
Ast_Node_Array_List* positional_arguments;
// TODO(Felix): Really use hashmap (keyword[sting] ->


+ 2
- 0
src/error.c 查看文件

@@ -1,6 +1,7 @@
typedef enum {
Error_Type_Ill_Formed_List,
Error_Type_Ill_Formed_Arguments,
Error_Type_Ill_Formed_Lambda_List,
Error_Type_Wrong_Number_Of_Arguments,
Error_Type_Unknown_Keyword_Argument,
Error_Type_Type_Missmatch,
@@ -40,6 +41,7 @@ char* Error_Type_to_string(Error_Type type) {
switch (type) {
case Error_Type_Ill_Formed_List: return "Evaluation-error: Ill formed list";
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_Unknown_Keyword_Argument: return "Evaluation-error: Unknown keyword argument";
case Error_Type_Not_A_Function: return "Evaluation-error: Not a function";
case Error_Type_Symbol_Not_Defined: return "Evaluation-error: Symbol not defined";


+ 190
- 6
src/eval.c 查看文件

@@ -1,7 +1,140 @@
/* Ast_Node* apply_to_lambda () {} */

Ast_Node* eval_expr(Ast_Node* node, Environment* env);
bool is_truthy (Ast_Node* expression, Environment* env);
Ast_Node* apply_arguments_to_function (Ast_Node* arguments, Ast_Node* function) {
printf("calling function, yey\n");

return create_ast_node_nil();
}

/**
This parses the argument specification of funcitons into their
Function struct. It dois this by allocating new
positional_arguments, keyword_arguments and rest_argument and
filling it in
*/
void parse_argument_list(Ast_Node* arguments, Function* function) {
// first init the fields
function->positional_arguments = create_positional_argument_list(16);
function->keyword_arguments = create_keyword_argument_list(16);
function->rest_argument = nullptr;

// okay let's try to read some positional arguments
while (arguments->type == Ast_Node_Type_Pair) {
if (arguments->value.pair->first->type == Ast_Node_Type_Keyword) {
if (string_equal(arguments->value.pair->first->value.keyword->identifier, "keys") ||
string_equal(arguments->value.pair->first->value.keyword->identifier, "rest"))
break;
else {
create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
return;
}
}

if (arguments->value.pair->first->type != Ast_Node_Type_Symbol) {
create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
return;
}

// okay wow we found an actual symbol
append_to_positional_argument_list(
function->positional_arguments,
arguments->value.pair->first->value.symbol->identifier);

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

// okay we are done with positional arguments, lets check for
// keywords,
if (arguments->type != Ast_Node_Type_Pair) {
if (arguments->type != Ast_Node_Type_Nil)
create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
return;
}

if (arguments->value.pair->first->type == Ast_Node_Type_Keyword &&
string_equal(arguments->value.pair->first->value.keyword->identifier, "keys"))
{
arguments = arguments->value.pair->rest;
if (arguments->type != Ast_Node_Type_Pair ||
arguments->value.pair->first->type != Ast_Node_Type_Symbol)
{
create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
return;
}

while (arguments->type == Ast_Node_Type_Pair) {
if (arguments->value.pair->first->type == Ast_Node_Type_Keyword) {
if (string_equal(arguments->value.pair->first->value.keyword->identifier, "rest"))
break;
else {
create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
return;
}
}

if (arguments->value.pair->first->type != Ast_Node_Type_Symbol) {
create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
return;
}

// we found a symbol (arguments->value.pair->first) for
// the keyword args! Let's check if the next arguement is
// :defaults-to
Ast_Node* next = arguments->value.pair->rest;
if (next->type == Ast_Node_Type_Pair &&
next->value.pair->first->type == Ast_Node_Type_Keyword &&
string_equal(next->value.pair->first->value.keyword->identifier,
"defaults-to"))
{
// check if there is a next argument too, otherwise it
// would be an error
next = next->value.pair->rest;
if (next->type == Ast_Node_Type_Pair) {
append_to_keyword_argument_list(function->keyword_arguments,
arguments->value.pair->first->value.symbol->identifier,
next->value.pair->first);
arguments = next->value.pair->rest;
} else {
create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
return;
}
} else {
// No :defaults-to, so just add it to the list
append_to_keyword_argument_list(function->keyword_arguments,
arguments->value.pair->first->value.symbol->identifier,
nullptr);
arguments = next;
}
}
}


// Now we are also done with keyword arguments, lets check for
// if there is a rest argument
if (arguments->type != Ast_Node_Type_Pair) {
if (arguments->type != Ast_Node_Type_Nil)
create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
return;
}

if (arguments->value.pair->first->type == Ast_Node_Type_Keyword &&
string_equal(arguments->value.pair->first->value.keyword->identifier, "rest"))
{
arguments = arguments->value.pair->rest;
if (arguments->type != Ast_Node_Type_Pair ||
arguments->value.pair->first->type != Ast_Node_Type_Symbol)
{
create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
return;
}
function->rest_argument = arguments->value.pair->first->value.symbol->identifier;
if (arguments->value.pair->rest->type != Ast_Node_Type_Nil) {
create_error(Error_Type_Ill_Formed_Lambda_List, arguments);
}
} else {
printf("this should not happen?");
create_error(Error_Type_Unknown_Error, arguments);
}
}


int list_length(Ast_Node* node) {
@@ -56,6 +189,9 @@ Ast_Node* copy_list(Ast_Node* node) {
return result;
}

Ast_Node* eval_expr(Ast_Node* node, Environment* env);
bool is_truthy (Ast_Node* expression, Environment* env);

Parsed_Arguments* eval_and_parse_arguments(Ast_Node* arguments, Environment* env) {
// TODO(Felix): if arguments is nil then maybe return a constant
// Parsed_Arguments_nil?
@@ -70,9 +206,9 @@ Parsed_Arguments* eval_and_parse_arguments(Ast_Node* arguments, Environment* env
Parse_State parse_state = Expecting_Anything;

Parsed_Arguments* result = new(Parsed_Arguments);
result->positional_arguments = create_Ast_Node_Array_List();
result->keyword_keys = create_Ast_Node_Array_List();
result->keyword_values = create_Ast_Node_Array_List();
result->positional_arguments = create_Ast_Node_Array_List(16);
result->keyword_keys = create_Ast_Node_Array_List(16);
result->keyword_values = create_Ast_Node_Array_List(16);

Ast_Node* current_head = copy_list(arguments);
Ast_Node* current_evaluated_head;
@@ -171,6 +307,47 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) {
// check for special form
if (operator->type == Ast_Node_Type_Built_In_Function) {
switch (operator->value.built_in_function->type) {
case Built_In_Lambda: {
/*
* (lambda ())
* (lambda (x d) (+ 1 2) (- 1 2) (* 1 2))
*/
try {
arguments_length = list_length(arguments);
}
if (arguments_length == 0)
report_error(Error_Type_Wrong_Number_Of_Arguments);


Function* function = new(Function);
// if parameters were specified
if (arguments->value.pair->first->type != Ast_Node_Type_Nil) {
try {
assert_type(arguments->value.pair->first, Ast_Node_Type_Pair);
}
try {
parse_argument_list(arguments->value.pair->first, function);
}
}

arguments = arguments->value.pair->rest;
// if there is a docstring, use it
if (arguments->value.pair->first->type == Ast_Node_Type_String) {
function->docstring = arguments->value.pair->first->value.string->value;
arguments = arguments->value.pair->rest;
}

// we are now in the function body, just wrap it in an
// implicit prog
function->body = create_ast_node_pair(
create_ast_node_built_in_function("prog"),
arguments);

Ast_Node* ret = new(Ast_Node);
ret->type = Ast_Node_Type_Function;
ret->value.function = function;
return ret;
}
case Built_In_And: {
bool result = true;
while (arguments->type != Ast_Node_Type_Nil) {
@@ -454,6 +631,13 @@ Ast_Node* eval_expr(Ast_Node* node, Environment* env) {

// assume it's lambda function and evaluate the arguments
arguments = eval_arguments(arguments, env, &arguments_length);
if (operator->type == Ast_Node_Type_Function) {
Ast_Node* result;
try {
result = apply_arguments_to_function(arguments, operator);
}
return result;
}
}

default:


+ 1
- 1
src/init.c 查看文件

@@ -14,5 +14,5 @@ void init () {
#endif

// TODO(Felix): Init the constants here (Bulit-in function
// ast_nodes, nil, ..more?)
// ast_nodes, nil, type_keywords ..more?)
}

+ 1
- 0
src/io.c 查看文件

@@ -69,6 +69,7 @@ void print(Ast_Node* node) {
} break;
case (Ast_Node_Type_Function): {
printf("[lambda]");
/* print(node->value.function->body); */
} break;
case (Ast_Node_Type_Built_In_Function): {
printf("[built-in-function %s]", Built_In_Name_to_string(node->value.built_in_function->type));


+ 7
- 6
src/parse.c 查看文件

@@ -1,22 +1,23 @@
Ast_Node_Array_List* create_Ast_Node_Array_List() {
Ast_Node_Array_List* create_Ast_Node_Array_List(int initial_length) {
Ast_Node_Array_List* ret = new (Ast_Node_Array_List);

// create one with 16 entries first
ret->length = 16;
ret->data = (Ast_Node**)malloc(ret->length * sizeof(Ast_Node));
ret->length = initial_length;
ret->data = (struct Ast_Node**)malloc(initial_length * sizeof(struct Ast_Node));
ret->next_index = 0;

return ret;
}

void append_to_Ast_Node_Array_List(Ast_Node_Array_List* list, Ast_Node* node) {
void append_to_Ast_Node_Array_List(Ast_Node_Array_List* list, struct Ast_Node* node) {
if (list->next_index == list->length) {
list->length *= 2;
list->data = (Ast_Node**)realloc(list->data, list->length * sizeof(Ast_Node));
list->data = (struct Ast_Node**)realloc(list->data, list->length * sizeof(struct Ast_Node));
}
list->data[list->next_index++] = node;
}


void eat_comment_line(char* text, int* index_in_text) {
// safety check if we are actually starting a comment here
if (text[*index_in_text] != ';')
@@ -237,7 +238,7 @@ Ast_Node* parse_single_expression(char* text) {
}

Ast_Node_Array_List* parse_program(char* text) {
Ast_Node_Array_List* program = create_Ast_Node_Array_List();
Ast_Node_Array_List* program = create_Ast_Node_Array_List(16);

int index_in_text = 0;



+ 1
- 1
test.bat 查看文件

@@ -4,7 +4,7 @@ pushd %~dp0\bin
call ..\build.bat
if %errorlevel% == 0 (
echo ---------- Testing ----------
call timecmd lisp.exe test.lsp
call timecmd slime.exe test.slime
)

popd

+ 539
- 0
todo.html 查看文件

@@ -0,0 +1,539 @@
<!doctype html>
<html lang="en">
<head>
<title></title>
<!-- 2018-10-14 So 16:55 -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="generator" content="Org-mode">
<meta name="author" content="Felix Brendel">

<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.min.js"></script>
<style type="text/css">
/* org mode styles on top of twbs */

html {
position: relative;
min-height: 100%;
}

body {
font-size: 18px;
margin-bottom: 105px;
}

footer {
position: absolute;
bottom: 0;
width: 100%;
height: 101px;
background-color: #f5f5f5;
}

footer > div {
padding: 10px;
}

footer p {
margin: 0 0 5px;
text-align: center;
font-size: 16px;
}

#table-of-contents {
margin-top: 20px;
margin-bottom: 20px;
}

blockquote p {
font-size: 18px;
}

pre {
font-size: 16px;
}

.footpara {
display: inline-block;
}

figcaption {
font-size: 16px;
color: #666;
font-style: italic;
padding-bottom: 15px;
}

/* from twbs docs */

.bs-docs-sidebar.affix {
position: static;
}
@media (min-width: 768px) {
.bs-docs-sidebar {
padding-left: 20px;
}
}

/* All levels of nav */
.bs-docs-sidebar .nav > li > a {
display: block;
padding: 4px 20px;
font-size: 14px;
font-weight: 500;
color: #999;
}
.bs-docs-sidebar .nav > li > a:hover,
.bs-docs-sidebar .nav > li > a:focus {
padding-left: 19px;
color: #A1283B;
text-decoration: none;
background-color: transparent;
border-left: 1px solid #A1283B;
}
.bs-docs-sidebar .nav > .active > a,
.bs-docs-sidebar .nav > .active:hover > a,
.bs-docs-sidebar .nav > .active:focus > a {
padding-left: 18px;
font-weight: bold;
color: #A1283B;
background-color: transparent;
border-left: 2px solid #A1283B;
}

/* Nav: second level (shown on .active) */
.bs-docs-sidebar .nav .nav {
display: none; /* Hide by default, but at >768px, show it */
padding-bottom: 10px;
}
.bs-docs-sidebar .nav .nav > li > a {
padding-top: 1px;
padding-bottom: 1px;
padding-left: 30px;
font-size: 12px;
font-weight: normal;
}
.bs-docs-sidebar .nav .nav > li > a:hover,
.bs-docs-sidebar .nav .nav > li > a:focus {
padding-left: 29px;
}
.bs-docs-sidebar .nav .nav > .active > a,
.bs-docs-sidebar .nav .nav > .active:hover > a,
.bs-docs-sidebar .nav .nav > .active:focus > a {
padding-left: 28px;
font-weight: 500;
}

/* Nav: third level (shown on .active) */
.bs-docs-sidebar .nav .nav .nav {
padding-bottom: 10px;
}
.bs-docs-sidebar .nav .nav .nav > li > a {
padding-top: 1px;
padding-bottom: 1px;
padding-left: 40px;
font-size: 12px;
font-weight: normal;
}
.bs-docs-sidebar .nav .nav .nav > li > a:hover,
.bs-docs-sidebar .nav .nav .nav > li > a:focus {
padding-left: 39px;
}
.bs-docs-sidebar .nav .nav .nav > .active > a,
.bs-docs-sidebar .nav .nav .nav > .active:hover > a,
.bs-docs-sidebar .nav .nav .nav > .active:focus > a {
padding-left: 38px;
font-weight: 500;
}

/* Show and affix the side nav when space allows it */
@media (min-width: 992px) {
.bs-docs-sidebar .nav > .active > ul {
display: block;
}
/* Widen the fixed sidebar */
.bs-docs-sidebar.affix,
.bs-docs-sidebar.affix-bottom {
width: 213px;
}
.bs-docs-sidebar.affix {
position: fixed; /* Undo the static from mobile first approach */
top: 20px;
}
.bs-docs-sidebar.affix-bottom {
position: absolute; /* Undo the static from mobile first approach */
}
.bs-docs-sidebar.affix .bs-docs-sidenav,.bs-docs-sidebar.affix-bottom .bs-docs-sidenav {
margin-top: 0;
margin-bottom: 0
}
}
@media (min-width: 1200px) {
/* Widen the fixed sidebar again */
.bs-docs-sidebar.affix-bottom,
.bs-docs-sidebar.affix {
width: 263px;
}
}
</style>
<script type="text/javascript">
$(function() {
'use strict';

$('.bs-docs-sidebar li').first().addClass('active');

$(document.body).scrollspy({target: '.bs-docs-sidebar'});

$('.bs-docs-sidebar').affix();
});
</script>
</head>
<body>
<div id="content" class="container">
<div class="row"><div class="col-md-9"><h1 class="title"></h1>
<div id="outline-container-sec-1" class="outline-2">
<h2 id="sec-1"><span class="section-number-2">1</span> Arguments</h2>
<div class="outline-text-2" id="text-1">
<p>
In Emacs lisp, keyword arguments must be passed in the same order as they were
defined in, so <code>((lambda (a &amp;key b c) (+ a b c)) 1 3 :b 2)</code> will preduce an
error, because <code>b</code> was passed as last argument but was defined as second argument.
</p>
<div class="org-src-container">

<pre class="src src-emacs-lisp"><span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a b<span style="color: #b58900;">)</span> <span style="color: #b58900;">(</span>+ a b<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> 1 2<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">3</span>
<span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a <span style="color: #268bd2;">&amp;key</span> b<span style="color: #b58900;">)</span> <span style="color: #b58900;">(</span>+ a b<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> 1 <span style="color: #6c71c4;">:b</span> 2<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">3</span>
<span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a <span style="color: #268bd2;">&amp;key</span> b<span style="color: #b58900;">)</span> <span style="color: #b58900;">(</span>+ a b<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> <span style="color: #6c71c4;">:b</span> 2 1<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">error</span>
</pre>
</div>


<p>
But if mulitiple keyword arguments are defined, their ordering does not matter.
</p>
<div class="org-src-container">

<pre class="src src-emacs-lisp"><span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a <span style="color: #268bd2;">&amp;key</span> b <span style="color: #268bd2;">&amp;key</span> c <span style="color: #268bd2;">&amp;key</span> d<span style="color: #b58900;">)</span> <span style="color: #b58900;">(</span>+ a b c d<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> 1 <span style="color: #6c71c4;">:d</span> 4 <span style="color: #6c71c4;">:c</span> 3 <span style="color: #6c71c4;">:b</span> 2<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">10</span>
</pre>
</div>

<p>
It is even possible to have a non keyword argument bewtween keyword arguments
and when calling, switch the order.
</p>
<div class="org-src-container">

<pre class="src src-emacs-lisp"><span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a <span style="color: #268bd2;">&amp;key</span> b c <span style="color: #268bd2;">&amp;key</span> d<span style="color: #b58900;">)</span> <span style="color: #b58900;">(</span>+ a b c d<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> 1 <span style="color: #6c71c4;">:d</span> 4 3 <span style="color: #6c71c4;">:b</span> 2<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">10</span>
<span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a <span style="color: #268bd2;">&amp;key</span> b c <span style="color: #268bd2;">&amp;key</span> d<span style="color: #b58900;">)</span> <span style="color: #b58900;">(</span>+ a b c d<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> 1 <span style="color: #6c71c4;">:d</span> 4 <span style="color: #6c71c4;">:b</span> 2 3<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">error</span>
</pre>
</div>

<p>
However somehow it is not possible to flip the ordering at the first argument
</p>
<div class="org-src-container">

<pre class="src src-emacs-lisp"><span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a <span style="color: #268bd2;">&amp;key</span> b<span style="color: #b58900;">)</span> <span style="color: #b58900;">(</span>+ a b<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> 2 <span style="color: #6c71c4;">:b</span> 1<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">3</span>
<span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a <span style="color: #268bd2;">&amp;key</span> b<span style="color: #b58900;">)</span> <span style="color: #b58900;">(</span>+ a b<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> <span style="color: #6c71c4;">:b</span> 2 1<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">error</span>
<span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span><span style="color: #268bd2;">&amp;key</span> a b<span style="color: #b58900;">)</span> <span style="color: #b58900;">(</span>+ a b<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> <span style="color: #6c71c4;">:a</span> 2 1<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">3</span>
<span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span><span style="color: #268bd2;">&amp;key</span> a b<span style="color: #b58900;">)</span> <span style="color: #b58900;">(</span>+ a b<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> 1 <span style="color: #6c71c4;">:a</span> 2<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">error</span>
</pre>
</div>

<p>
So it seems, positional arguments have to be at the exact position they were
defined in, but keyword arguments can appear in any order, but at the places
where keyword arguments have been defined.
</p>
</div>

<div id="outline-container-sec-1-1" class="outline-3">
<h3 id="sec-1-1"><span class="section-number-3">1.1</span> Idea</h3>
<div class="outline-text-3" id="text-1-1">
<p>
Every argument can be set using keys, but keyword arguments always come strictly
after positional arguments
</p>

<div class="org-src-container">

<pre class="src src-emacs-lisp"><span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a b c d<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> 1 2 3 4<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">ok</span>
<span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a b c d<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> 1 2 <span style="color: #6c71c4;">:c</span> 2 <span style="color: #6c71c4;">:d</span> 1<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">ok</span>
<span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a b c d<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> 1 2 <span style="color: #6c71c4;">:d</span> 1 <span style="color: #6c71c4;">:c</span> 2<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">ok</span>
<span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a b c d<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> <span style="color: #6c71c4;">:d</span> 1 1 2 <span style="color: #6c71c4;">:c</span> 2<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">error, keyword arguments must come last</span>
<span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a b c d<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> 1 2 <span style="color: #6c71c4;">:c</span> 2 <span style="color: #6c71c4;">:d</span> 1 <span style="color: #6c71c4;">:a</span> 2<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">error, 'a' was passed two times</span>

<span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a b c d<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> 1 2 <span style="color: #6c71c4;">:d</span> 1<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">error, c was not passed, but does not have a default value</span>
<span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a b c <span style="color: #6c71c4;">:default</span> 10 d<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> 1 2 <span style="color: #6c71c4;">:d</span> 1<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">okay again</span>
</pre>
</div>

<p>
The drawback then is that you can never pass a keyword as a positional argument,
because it will always try to assign the next argument to the variable defined
by the keyword. You could still pass it as a keyword argument though. This is
especially annoying because we want to use keywords as type identifiers.
</p>

<div class="org-src-container">

<pre class="src src-emacs-lisp"><span style="color: #839496;">(</span>= <span style="color: #2aa198;">(</span>type <span style="color: #2aa198;">"hello"</span><span style="color: #2aa198;">)</span> <span style="color: #6c71c4;">:string</span><span style="color: #839496;">)</span>
</pre>
</div>


<div class="org-src-container">

<pre class="src src-emacs-lisp"><span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>i-wanna-get-a-kw<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> <span style="color: #6c71c4;">:hey</span><span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">error, unmatched value for keyword argument</span>
<span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>i-wanna-get-a-kw<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> <span style="color: #6c71c4;">:i-wanna-get-a-kw</span> <span style="color: #6c71c4;">:hey</span><span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">this would work again</span>
</pre>
</div>

<p>
Other Idea is to mark the place in the parameter list where keyword argumetns
start and arguments before that are positional and will not be stuffed into
key-value pairs and arguments after that will be keyword arguments
</p>

<div class="org-src-container">

<pre class="src src-ebnf">&lt;lambda list&gt; -&gt; &lt;positional argument&gt;*
[:keys ( &lt;keyword arguments&gt; [:defaults-to &lt;value&gt;])*]
[(:rest &lt;rest argument&gt;)|(:body &lt;body argument&gt;)]
</pre>
</div>

<div class="org-src-container">

<pre class="src src-emacs-lisp"> <span style="color: #839496;">(</span>= <span style="color: #2aa198;">(</span>type <span style="color: #2aa198;">"hello"</span><span style="color: #2aa198;">)</span> <span style="color: #6c71c4;">:string</span><span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">this will work again because (=) does</span>
<span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">not use keyword arguments</span>
<span style="color: #839496;">(</span><span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>i-wanna-get-a-kw<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span> <span style="color: #6c71c4;">:hey</span><span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">would work because of the same reason</span>

<span style="color: #839496;">(</span>define fun
<span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span><span style="color: #6c71c4;">:keys</span> a b c d<span style="color: #b58900;">)</span>
<span style="color: #b58900;">(</span>+ a b c d<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span><span style="color: #839496;">)</span>

<span style="color: #839496;">(</span>fun 1 2 3 4<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">okay</span>
<span style="color: #839496;">(</span>fun 1 2 <span style="color: #6c71c4;">:d</span> 3 <span style="color: #6c71c4;">:c</span> 4<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">okay</span>
<span style="color: #839496;">(</span>fun 1 2 <span style="color: #6c71c4;">:d</span> 3 <span style="color: #6c71c4;">:c</span> 4 <span style="color: #6c71c4;">:a</span> 2<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">error, 'a' was passed two times</span>
<span style="color: #839496;">(</span>fun <span style="color: #6c71c4;">:c</span> 1 <span style="color: #6c71c4;">:b</span> 2 <span style="color: #6c71c4;">:d</span> 3 <span style="color: #6c71c4;">:a</span> 4<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">okay</span>
<span style="color: #839496;">(</span>fun <span style="color: #6c71c4;">:c</span> 1 <span style="color: #6c71c4;">:b</span> 2 <span style="color: #6c71c4;">:d</span> 3 4<span style="color: #839496;">)</span> <span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">error, positional argument after keyword</span>
<span style="color: #49974A; font-style: italic;">;; </span><span style="color: #49974A; font-style: italic;">and fun does not accept rest parameter</span>

<span style="color: #839496;">(</span>define fun2
<span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span><span style="color: #6c71c4;">:quoted</span> op a b <span style="color: #6c71c4;">:keys</span> c d <span style="color: #6c71c4;">:rest</span> r<span style="color: #b58900;">)</span>
<span style="color: #b58900;">(</span>print r<span style="color: #b58900;">)</span>
<span style="color: #b58900;">(</span>op a b c d<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span><span style="color: #839496;">)</span>

&gt;&gt; <span style="color: #839496;">(</span>fun2 * 1 2 <span style="color: #6c71c4;">:c</span> 3 <span style="color: #6c71c4;">:d</span> 4 <span style="color: #2aa198;">"this"</span> <span style="color: #2aa198;">"can"</span> <span style="color: #6c71c4;">:be</span> <span style="color: #2aa198;">"whatever"</span><span style="color: #839496;">)</span>
<span style="color: #839496;">(</span><span style="color: #2aa198;">"this"</span> <span style="color: #2aa198;">"can"</span> <span style="color: #6c71c4;">:be</span> <span style="color: #2aa198;">"whatever"</span><span style="color: #839496;">)</span>
24


&gt;&gt; <span style="color: #839496;">(</span>define print-before-eval <span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span><span style="color: #6c71c4;">:body</span> expr<span style="color: #b58900;">)</span>
<span style="color: #b58900;">(</span>print expr<span style="color: #b58900;">)</span>
<span style="color: #b58900;">(</span>eval expr<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span><span style="color: #839496;">)</span>
&gt;&gt; <span style="color: #839496;">(</span>define r <span style="color: #2aa198;">(</span>print-before-eval <span style="color: #b58900;">(</span>+ 1 2 3<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span><span style="color: #839496;">)</span>
<span style="color: #839496;">(</span>+ 1 2 3<span style="color: #839496;">)</span>
&gt;&gt; r
6

</pre>
</div>

<p>
Wait this does not work either because you won't be able to pass keywords into
rest.
</p>

<p>
<b>Unrealated Idea: Distinguish between functions and macros</b>
</p>

<p>
The difference is really simple:
</p>
<dl class="org-dl">
<dt> Function </dt><dd>like a lambda, but no argument is automatically quoted
</dd>
<dt> Macro </dt><dd>like a lambda, but every argument is automatically quoted
</dd>
</dl>

<p>
This is important, because a macro should not introduce a new environment, but
use the one it lives in.
</p>

<div class="org-src-container">

<pre class="src src-emacs-lisp">&gt;&gt; <span style="color: #839496;">(</span>define print-before-eval <span style="color: #2aa198;">(</span>macro <span style="color: #b58900;">(</span><span style="color: #6c71c4;">:rest</span> expr<span style="color: #b58900;">)</span>
<span style="color: #b58900;">(</span>print expr<span style="color: #b58900;">)</span>
<span style="color: #b58900;">(</span>eval expr<span style="color: #b58900;">)</span><span style="color: #2aa198;">)</span>

</pre>
</div>
</div>
</div>

<div id="outline-container-sec-1-2" class="outline-3">
<h3 id="sec-1-2"><span class="section-number-3">1.2</span> the actual problem</h3>
<div class="outline-text-3" id="text-1-2">
<p>
We have some requirements to the arguments:
</p>
<ul class="org-ul">
<li>positional arguments (always required, no default value)
</li>
<li>keyword arguments (always come after positionals), can be not passed if there
is a default value
</li>
<li>We want to be able to have a rest paramter, where everything is dumped in to
that exceeds the other parameters
</li>
</ul>

<p>
<b>The problem is, we have no way of knowing if the current keyword we are
reading is still part of the keyword block or part of the rest paramter.</b>
</p>

<p>
Consider:
</p>

<div class="org-src-container">

<pre class="src src-emacs-lisp">&gt;&gt; <span style="color: #839496;">(</span>define fun <span style="color: #2aa198;">(</span><span style="color: #859900;">lambda</span> <span style="color: #b58900;">(</span>a b <span style="color: #6c71c4;">:key</span> c d <span style="color: #6c71c4;">:defaults-to</span> 4 <span style="color: #6c71c4;">:rest</span><span style="color: #b58900;">)</span> nil<span style="color: #2aa198;">)</span><span style="color: #839496;">)</span>
&gt;&gt; <span style="color: #839496;">(</span>fun 1 2 <span style="color: #6c71c4;">:c</span> 3 <span style="color: #6c71c4;">:d</span> 5<span style="color: #839496;">)</span>
</pre>
</div>
<p>
What should this be evaluated to?
</p>

<table class="table table-striped table-bordered table-hover table-condensed">


<colgroup>
<col class="left">

<col class="right">

<col class="right">

<col class="right">

<col class="right">

<col class="left">
</colgroup>
<thead>
<tr>
<th scope="col" class="text-left">&#xa0;</th>
<th scope="col" class="text-right">a</th>
<th scope="col" class="text-right">b</th>
<th scope="col" class="text-right">c</th>
<th scope="col" class="text-right">d</th>
<th scope="col" class="text-left">rest</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-left">v1</td>
<td class="text-right">1</td>
<td class="text-right">2</td>
<td class="text-right">3</td>
<td class="text-right">5</td>
<td class="text-left">nil</td>
</tr>

<tr>
<td class="text-left">v2</td>
<td class="text-right">1</td>
<td class="text-right">2</td>
<td class="text-right">3</td>
<td class="text-right">4</td>
<td class="text-left">(:d 5)</td>
</tr>
</tbody>
</table>

<p>
It is ambiguous. It is however important to note that this can only happen when
there are optinal keyword arguments (keyword arguments with default values).
Otherwise we could always know which variables are set and wich have not been
set to see if we are in an errornious state.
</p>
</div>
</div>

<div id="outline-container-sec-1-3" class="outline-3">
<h3 id="sec-1-3"><span class="section-number-3">1.3</span> Solution</h3>
<div class="outline-text-3" id="text-1-3">
<p>
We can resolve that problem by always making the the decision of chosing <code>v1</code>
between <code>v1</code> and <code>v1</code>. This means, when we are expecting to read kwargs, and we
encounter an kwarg, try to apply it (this can fail, for two reasons:
</p>

<ol class="org-ol">
<li>The keyword has no following value
</li>
<li>The keyword is not a keyword argument in the funciton
</li>
</ol>

<p>
), if it fails treat it as part of the rest arguments, and read all the
following arguments into the rest arguments, or if it works, use it as the
keyword argument.
</p>
</div>
</div>

<div id="outline-container-sec-1-4" class="outline-3">
<h3 id="sec-1-4"><span class="section-number-3">1.4</span> Implementation</h3>
<div class="outline-text-3" id="text-1-4">
<div class="org-src-container">

<pre class="src src-c"><span style="color: #859900;">typedef</span> <span style="color: #859900;">struct</span> <span style="color: #839496;">{</span>
<span style="color: #268bd2;">char</span>* <span style="color: #6c71c4;">docstring</span>;
<span style="color: #268bd2;">char</span>** <span style="color: #6c71c4;">positional_arguments</span>;
<span style="color: #859900;">struct</span> <span style="color: #2aa198;">{</span>
<span style="color: #268bd2;">char</span>* <span style="color: #6c71c4;">identifier</span>;
<span style="color: #268bd2;">Ast_node</span>* <span style="color: #6c71c4;">default_value</span>; <span style="color: #49974A; font-style: italic;">// </span><span style="color: #49974A; font-style: italic;">will be nullptr if no defalut value was declared</span>
<span style="color: #2aa198;">}</span>* <span style="color: #6c71c4;">keyword_arguments</span>;
<span style="color: #268bd2;">char</span>* <span style="color: #6c71c4;">rest_argument</span>; <span style="color: #49974A; font-style: italic;">// </span><span style="color: #49974A; font-style: italic;">will be nullptr if no rest argument is declared</span>
<span style="color: #268bd2;">Ast_Node_array_list</span> <span style="color: #6c71c4;">body</span>;
<span style="color: #839496;">}</span> <span style="color: #268bd2;">lambda</span>;
</pre>
</div>
</div>
</div>
</div>
</div><div class="col-md-3"><nav id="table-of-contents">
<div id="text-table-of-contents" class="bs-docs-sidebar">
<ul class="nav">
<li><a href="#sec-1">1. Arguments</a>
<ul class="nav">
<li><a href="#sec-1-1">1.1. Idea</a></li>
<li><a href="#sec-1-2">1.2. the actual problem</a></li>
<li><a href="#sec-1-3">1.3. Solution</a></li>
<li><a href="#sec-1-4">1.4. Implementation</a></li>
</ul>
</li>
</ul>
</div>
</nav>
</div></div></div>
<footer id="postamble" class="">
<div><p class="author">Author: Felix Brendel</p>
<p class="date">Created: 2018-10-14 So 16:55</p>
<p class="creator"><a href="http://www.gnu.org/software/emacs/">Emacs</a> 26.0.91 (<a href="http://orgmode.org">Org-mode</a> 9.1.13)</p>
</div>
</footer>
</body>
</html>

+ 197
- 17
todo.org 查看文件

@@ -1,3 +1,196 @@
* Arguments

In Emacs lisp, keyword arguments must be passed in the same order as they were
defined in, so =((lambda (a &key b c) (+ a b c)) 1 3 :b 2)= will preduce an
error, because =b= was passed as last argument but was defined as second argument.
#+begin_src emacs-lisp
((lambda (a b) (+ a b)) 1 2) ;; 3
((lambda (a &key b) (+ a b)) 1 :b 2) ;; 3
((lambda (a &key b) (+ a b)) :b 2 1) ;; error
#+end_src


But if mulitiple keyword arguments are defined, their ordering does not matter.
#+begin_src emacs-lisp
((lambda (a &key b &key c &key d) (+ a b c d)) 1 :d 4 :c 3 :b 2) ;; 10
#+end_src

It is even possible to have a non keyword argument bewtween keyword arguments
and when calling, switch the order.
#+begin_src emacs-lisp
((lambda (a &key b c &key d) (+ a b c d)) 1 :d 4 3 :b 2) ;; 10
((lambda (a &key b c &key d) (+ a b c d)) 1 :d 4 :b 2 3) ;; error
#+end_src

However somehow it is not possible to flip the ordering at the first argument
#+begin_src emacs-lisp
((lambda (a &key b) (+ a b)) 2 :b 1) ;; 3
((lambda (a &key b) (+ a b)) :b 2 1) ;; error
((lambda (&key a b) (+ a b)) :a 2 1) ;; 3
((lambda (&key a b) (+ a b)) 1 :a 2) ;; error
#+end_src

So it seems, positional arguments have to be at the exact position they were
defined in, but keyword arguments can appear in any order, but at the places
where keyword arguments have been defined.

** Idea

Every argument can be set using keys, but keyword arguments always come strictly
after positional arguments

#+begin_src emacs-lisp
((lambda (a b c d)) 1 2 3 4) ;; ok
((lambda (a b c d)) 1 2 :c 2 :d 1) ;; ok
((lambda (a b c d)) 1 2 :d 1 :c 2) ;; ok
((lambda (a b c d)) :d 1 1 2 :c 2) ;; error, keyword arguments must come last
((lambda (a b c d)) 1 2 :c 2 :d 1 :a 2) ;; error, 'a' was passed two times

((lambda (a b c d)) 1 2 :d 1) ;; error, c was not passed, but does not have a default value
((lambda (a b c :default 10 d)) 1 2 :d 1) ;; okay again
#+end_src

The drawback then is that you can never pass a keyword as a positional argument,
because it will always try to assign the next argument to the variable defined
by the keyword. You could still pass it as a keyword argument though. This is
especially annoying because we want to use keywords as type identifiers.

#+begin_src emacs-lisp
(= (type "hello") :string)
#+end_src


#+begin_src emacs-lisp
((lambda (i-wanna-get-a-kw)) :hey) ;; error, unmatched value for keyword argument
((lambda (i-wanna-get-a-kw)) :i-wanna-get-a-kw :hey) ;; this would work again
#+end_src

Other Idea is to mark the place in the parameter list where keyword argumetns
start and arguments before that are positional and will not be stuffed into
key-value pairs and arguments after that will be keyword arguments

#+begin_src ebnf
<lambda list> -> <positional argument>*
[:keys ( <keyword arguments> [:defaults-to <value>])*]
[(:rest <rest argument>)|(:body <body argument>)]
#+end_src

#+begin_src emacs-lisp
(= (type "hello") :string) ;; this will work again because (=) does
;; not use keyword arguments
((lambda (i-wanna-get-a-kw)) :hey) ;; would work because of the same reason

(define fun
(lambda (:keys a b c d)
(+ a b c d)))

(fun 1 2 3 4) ;; okay
(fun 1 2 :d 3 :c 4) ;; okay
(fun 1 2 :d 3 :c 4 :a 2) ;; error, 'a' was passed two times
(fun :c 1 :b 2 :d 3 :a 4) ;; okay
(fun :c 1 :b 2 :d 3 4) ;; error, positional argument after keyword
;; and fun does not accept rest parameter

(define fun2
(lambda (:quoted op a b :keys c d :rest r)
(print r)
(op a b c d)))

>> (fun2 * 1 2 :c 3 :d 4 "this" "can" :be "whatever")
("this" "can" :be "whatever")
24


>> (define print-before-eval (lambda (:body expr)
(print expr)
(eval expr)))
>> (define r (print-before-eval (+ 1 2 3)))
(+ 1 2 3)
>> r
6

#+end_src

Wait this does not work either because you won't be able to pass keywords into
rest.

*Unrealated Idea: Distinguish between functions and macros*

The difference is really simple:
- Function :: like a lambda, but no argument is automatically quoted
- Macro :: like a lambda, but every argument is automatically quoted

This is important, because a macro should not introduce a new environment, but
use the one it lives in.

#+begin_src emacs-lisp

>> (define print-before-eval (macro (:rest expr)
(print expr)
(eval expr))

#+end_src

** the actual problem

We have some requirements to the arguments:
- positional arguments (always required, no default value)
- keyword arguments (always come after positionals), can be not passed if there
is a default value
- We want to be able to have a rest paramter, where everything is dumped in to
that exceeds the other parameters

*The problem is, we have no way of knowing if the current keyword we are
reading is still part of the keyword block or part of the rest paramter.*

Consider:

#+begin_src emacs-lisp
>> (define fun (lambda (a b :key c d :defaults-to 4 :rest) nil))
>> (fun 1 2 :c 3 :d 5)


(defun haha (par1 par2)
(+ 1 2))
#+end_src
What should this be evaluated to?

| | a | b | c | d | rest |
|----+---+---+---+---+--------|
| v1 | 1 | 2 | 3 | 5 | nil |
| v2 | 1 | 2 | 3 | 4 | (:d 5) |

It is ambiguous. It is however important to note that this can only happen when
there are optinal keyword arguments (keyword arguments with default values).
Otherwise we could always know which variables are set and wich have not been
set to see if we are in an errornious state.

** Solution
We can resolve that problem by always making the the decision of chosing =v1=
between =v1= and =v1=. This means, when we are expecting to read kwargs, and we
encounter an kwarg, try to apply it (this can fail, for two reasons:

1. The keyword has no following value
2. The keyword is not a keyword argument in the funciton

), if it fails treat it as part of the rest arguments, and read all the
following arguments into the rest arguments, or if it works, use it as the
keyword argument.

** Implementation

#+begin_src c
typedef struct {
char* docstring;
char** positional_arguments;
struct {
char* identifier;
Ast_node* default_value; // will be nullptr if no defalut value was declared
}* keyword_arguments;
char* rest_argument; // will be nullptr if no rest argument is declared
Ast_Node_array_list body;
} lambda;
#+end_src
* TODO =assert_equal_type= macro in testing
* DONE use an enum for builtin identifiers
CLOSED: [2018-10-11 Do 17:15]
@@ -25,9 +218,9 @@
CLOSED: [2018-10-05 Fr 22:21]
** DONE not
CLOSED: [2018-10-05 Fr 22:21]
#+begin_src emacs-lisp
#+begin_src
(not 1) ;; == (not . (1 . nil))
(not (expression 1 3)) ;; == (not . ((expression . (1 . (3 . nil))) . nil))
(not (expression 1 3)) ;; == (not . ((expression . (1 . (3 . nil))) . nil) . nil)
(not) ;; == (not . nil)
(not 1 2) ;; == (not . (1 . (2 . nil)))
#+end_src
@@ -62,14 +255,14 @@
** DONE list
CLOSED: [2018-10-08 Mo 21:06]

#+begin_src emacs-lisp
#+begin_src
(quote (cons 2 (cons 3 (cons 4 ()))))
;; (cons 2 (cons 3 (cons 4 nil)))
(quote (2 . (3 . (4 . ()))))
;; (2 3 4)
#+end_src

#+begin_src emacs-lisp
#+begin_src
(list (cons 2 (cons 3 (cons 4 ()))))
;; ((2 3 4))
(list (quote(2 . (3 . (4 . ())))))
@@ -91,16 +284,3 @@
- pair (cons-cell)
- lambda function lambda
- built-in function

* Arbeitsweise

1) Parse to AST
2) Start with namespace only containing the built-ins
3) every AST node has its own namespace,
inherting the one from its parent (pointer /
references) because when a function sets a
variable within an upper namespace it should
infact change the environment even if the
function exited

(progn (setq x 4)(* x 2))

vs/lisp.sln → vs/slime.sln 查看文件

@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2005
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lisp", "lisp.vcxproj", "{1A47A3ED-871F-4CB4-875B-8CAA385B1771}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slime", "slime.vcxproj", "{1A47A3ED-871F-4CB4-875B-8CAA385B1771}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

vs/lisp.vcxproj → vs/slime.vcxproj 查看文件

@@ -29,8 +29,9 @@
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{1A47A3ED-871F-4CB4-875B-8CAA385B1771}</ProjectGuid>
<RootNamespace>lisp</RootNamespace>
<RootNamespace>slime</RootNamespace>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
<ProjectName>slime</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">

正在加载...
取消
保存