Can now parse lambda lists
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
@setlocal
|
||||
pushd %~dp0
|
||||
|
||||
set exeName=lisp.exe
|
||||
set exeName=slime.exe
|
||||
set binDir=bin
|
||||
|
||||
mkdir quickbuild 2>nul
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@echo off
|
||||
pushd %~dp0\vs
|
||||
start lisp.sln
|
||||
start slime.sln
|
||||
popd
|
||||
|
||||
@@ -4,6 +4,6 @@ pushd %~dp0\bin
|
||||
call ..\build.bat
|
||||
if %errorlevel% == 0 (
|
||||
echo ---------- Running ----------
|
||||
call timecmd lisp.exe
|
||||
call timecmd slime.exe
|
||||
)
|
||||
popd
|
||||
|
||||
@@ -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 {
|
||||
Lambda_List* parameters;
|
||||
Pair* form;
|
||||
char* docstring;
|
||||
char** identifiers;
|
||||
int next_index;
|
||||
int length;
|
||||
} Positional_Arguments;
|
||||
|
||||
typedef struct {
|
||||
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] ->
|
||||
|
||||
@@ -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
@@ -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
@@ -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?)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 &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;">&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;">&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;">&key</span> b <span style="color: #268bd2;">&key</span> c <span style="color: #268bd2;">&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;">&key</span> b c <span style="color: #268bd2;">&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;">&key</span> b c <span style="color: #268bd2;">&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;">&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;">&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;">&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;">&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"><lambda list> -> <positional argument>*
|
||||
[:keys ( <keyword arguments> [:defaults-to <value>])*]
|
||||
[(:rest <rest argument>)|(:body <body argument>)]
|
||||
</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>
|
||||
|
||||
>> <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
|
||||
|
||||
|
||||
>> <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>
|
||||
>> <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>
|
||||
>> 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">>> <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">>> <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>
|
||||
>> <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"> </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>
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
@@ -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">
|
||||
Reference in New Issue
Block a user