Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

14 KiB

Issues

DONE #001

CLOSED: [2018-10-26 Fr 22:36]

(define seq (list 1 2 3))
(when 1 (printf seq))

The problem is, that when calls apply and the params of apply are named fun and seq. So when we refer to seq in printf then we are refering to the apply's argument names. Maybe a good solution to that is, that a child environment can never look up something in a parent macro environment, but will always pass through

stack depth is macro environment defined symbols scope
0 no seq nil printf apply etc
1 yes test body (when 1 printf seq)
2 yes fun seq (apply prog body)
3 no (printf seq)

DONE #002

CLOSED: [2018-10-27 Sa 13:50]

  (define l (list 1 2 3))
  (extend l 4)

  ;; === Environment ===
  ;; current: in (apply)
  ;; fun -> prog
  ;; seq -> body
  ;; parent (hidden): in (when)
  ;;     test -> (pair? seq)
  ;;     body -> ((define e (end seq)) (mutate e (pair (first e) elem)))
  ;;     parent: in (extend)
  ;;         seq  -> (1.000000 2.000000 3.000000)
  ;;         elem -> 4.000000
  ;;         parent: (top level)
  ;;             nil                -> nil
  ;;             defun              -> [macro]
  ;;             defmacro           -> [macro]
  ;;             pe                 -> [macro]
  ;;             nil?               -> [lambda]
  ;;             number?            -> [lambda]
  ;;             symbol?            -> [lambda]
  ;;             keyword?           -> [lambda]
  ;;             pair?              -> [lambda]
  ;;             string?            -> [lambda]
  ;;             dynamic-function?  -> [lambda]
  ;;             dynamic-macro?     -> [lambda]
  ;;             built-in-function? -> [lambda]
  ;;             apply              -> [macro]
  ;;             when               -> [macro]
  ;;             unless             -> [macro]
  ;;             end                -> [lambda]
  ;;             last               -> [lambda]
  ;;             extend             -> [lambda]
  ;;             append             -> [lambda]
  ;;             n-times            -> [macro]
  ;;             range              -> [lambda]
  ;;             map                -> [lambda]
  ;;             reduce             -> [lambda]
  ;;             reduce-binary      -> [lambda]
  ;;             filter             -> [lambda]
  ;;             printf-quoted      -> [macro]
  ;;             printf             -> [lambda]
  ;;             l                  -> (1.000000 2.000000 3.000000)

  ;; M (apply fun -> prog seq -> body)
  ;; M (when test -> (pair? seq) body -> ((define e (end seq)) (mutate e (pair (first e) elem))))
  ;;   (extend seq -> (1.000000 2.000000 3.000000) elem -> 4.000000)

The problem is that a funciton g called by a function f has access to the environment of f. This is a real problem.

(defun f ()    1)
(defun g ()  (f))
(defun h (f) (g))
(h 4)

What should happen:

  • (h 1) calls (g)

  • (g) calls (f)

  • (f) retunrs 1

What actually happens:

  • (h 1) calls (g)

  • (g) tries to call (f) but f is not a funciton but 4

DONE #003

CLOSED: [2018-10-27 Sa 15:22]

  (defmacro n-times (times action)
    (unless (<= (eval times) 0)
      (breakpoint)
      (eval action)
      (macro-define args (pair (pair - (pair (eval times) (pair 1 nil))) (pair action nil)))
      ;;  [o|o] --------------------------> [o|o] -> nil
      ;;   |                                 |
      ;;   V                                 V
      ;;  [o|o] -> [o|o] -> [o|o]-> nil    action
      ;;   |        |        |
      ;;   V        V        V
      ;;   -      times      1
      (eval (pair n-times args))))
  • let should never have access to parenting macro because (while 2 test) -> 2.00000

    • Should it have access to the grandad macro?

    • no, (when 2 (when 3 test)) -> 2

  • in the example, n-times is a macro, that calls unless, that itself is a macro that calls the content of n-times in a let. So of course the contents of n-times wont have acess to the parameters to n-times, which is bad.

So right now the only way I see to fix this is to make maco environments accessable again, but pay attention on how to name macro parameters, to avoid collision, maybe a naming convention would help, maybe prefix with @

Solutions:

First instinct to prevent that, is that if a parent environment is a function then just pass it and go for their parent etc. Then we need let of a way to create environments that are neither macro- nor funciton environmetns. They will be still different though.

Defining

In a macro environment define will still behave differently than in a function environment. If you use define in a macro environment, it will actually be defined in the closest non-macro environment. If you actually want to define something in a macro environment, then use macro-define. However define will do the same if you are in a function or let environment.

Looking up

If the symbol is found on the active environment then use it. If not: Go up the environments untily ou find a let-environment and recurse on that.

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.

  ((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

But if mulitiple keyword arguments are defined, their ordering does not matter.

  ((lambda (a &key b &key c &key d) (+ a b c d)) 1 :d 4 :c 3 :b 2) ;; 10

It is even possible to have a non keyword argument bewtween keyword arguments and when calling, switch the order.

  ((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

However somehow it is not possible to flip the ordering at the first argument

  ((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

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

  ((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

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.

(= (type "hello") :string)
  ((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

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

<lambda list> -> <positional argument>*
                 [:keys ( <keyword arguments> [:defaults-to <value>])*]
                 [(:rest <rest argument>)|(:body <body argument>)]
  (= (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

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.


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

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:

  >> (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))

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

  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;

DONE use an enum for builtin identifiers

CLOSED: [2018-10-11 Do 17:15]

DONE Print escaped chracters correctly

CLOSED: [2018-10-27 Sa 18:16]

TODO [A] t ast node type, universal source of truth

TODO [A] source code locations for errors

TODO [A] String error messages

TODO [A] Rename macro to special or something

TODO [B] assert_equal_type macro in testing

TODO [B] dont create new nils or builtins, but store one of each globally

TODO [B] make keywords unique (binary tree)

TODO [B] store all ast nodes in a huge arena

TODO [B] Auto doc generation

TODO [C] backquoting

Build-in forms [29/30]

TODO info

DONE let

CLOSED: [2018-10-27 Sa 15:30]

DONE +

CLOSED: [2018-09-18 Di 12:14]

DONE -

CLOSED: [2018-09-18 Di 12:14]

DONE *

CLOSED: [2018-09-18 Di 12:14]

DONE /

CLOSED: [2018-09-18 Di 12:14]

DONE >

CLOSED: [2018-10-26 Fr 23:30]

DONE >=

CLOSED: [2018-10-26 Fr 23:30]

DONE <

CLOSED: [2018-10-26 Fr 23:30]

DONE >=

CLOSED: [2018-10-26 Fr 23:30]

DONE =

CLOSED: [2018-10-21 So 00:25]

DONE if

CLOSED: [2018-09-18 Di 12:14]

DONE and (short circuiting)

CLOSED: [2018-10-05 Fr 22:21]

DONE or

CLOSED: [2018-10-05 Fr 22:21]

DONE not

CLOSED: [2018-10-05 Fr 22:21]

(not 1)                  ;; == (not . (1 . nil))
(not (expression 1 3))   ;; == (not . ((expression . (1 . (3 . nil))) . nil) . nil)
(not)                    ;; == (not . nil)
(not 1 2)                ;; == (not . (1 . (2 . nil)))
t

DONE first (car)

CLOSED: [2018-10-08 Mo 20:28]

DONE rest (cdr)

CLOSED: [2018-10-08 Mo 20:28]

DONE pair (cons)

CLOSED: [2018-10-08 Mo 20:28]

DONE load (import)

CLOSED: [2018-10-21 So 00:25]

DONE define

CLOSED: [2018-10-08 Mo 20:28]

DONE mutate

CLOSED: [2018-10-25 Do 19:40]

DONE lambda

CLOSED: [2018-10-21 So 00:25]

DONE macro

CLOSED: [2018-10-25 Do 19:40]

DONE prog

CLOSED: [2018-10-21 So 00:25]

DONE eval

CLOSED: [2018-10-08 Mo 20:28]

(setq condition (quote (= a 10))) (setq a 10) (eval condition) (setq a 11) (eval condition)

DONE quote

CLOSED: [2018-10-08 Mo 20:28]

DONE list

CLOSED: [2018-10-08 Mo 21:06]

  (quote (cons 2 (cons 3 (cons 4 ()))))
  ;; (cons 2 (cons 3 (cons 4 nil)))
  (quote (2 . (3 . (4 . ()))))
  ;; (2 3 4)
  (list (cons 2 (cons 3 (cons 4 ()))))
  ;; ((2 3 4))
  (list (quote(2 . (3 . (4 . ())))))

DONE print

CLOSED: [2018-10-08 Mo 20:28]

DONE read

CLOSED: [2018-10-08 Mo 20:28]

DONE type

CLOSED: [2018-10-08 Mo 21:30]