Can now parse lambda lists
This commit is contained in:
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user