tomhrr / dale

Lisp-flavoured C
BSD 3-Clause "New" or "Revised" License
1.02k stars 48 forks source link

Extending the way, initializer lists (like they exist for arrays and structs) can be written #139

Open porky11 opened 7 years ago

porky11 commented 7 years ago

It should be possible to define your own macros as the value for setf. Simple examle is an easy way for initializing array-like types You just would like to write something like (def x (var my-array-like-type (array 1 2 3))) This is only possible for (array-of n T), but not for the Array and Vector concept type. For arrays you could write (def x (var (Array int 3) ((elements (array 1 2 3))))) which is no big problem, but for vectors, this is more complicated, and when writing macros, you would have to proivide some information like the vector-element-type, which will lead to much verbrosity for complex structs, so it seems useful to have the posibility to write some macro, that has access to the type-information of the struct-fields similar to (array …) and ((field1 val1) …) as default initializer forms for arrays and structs. This could be extended by some forms like setf-macro-init and setf-macro-assign (not sure, if both macros are neccessary here) When this macro is defined over the type, and expands to a non-null (p DNode), the macro-expansion will be called instead of init-functions and setf-functions.

See some simple example, only possible for defining vars, which allows setting a Array conept type via (array a b c):

(import cstdio)
(import macros)
(import array)

(std.concepts.instantiate Array int 3)

(using-namespace std.macros

(def setf-macro-init (macro extern ((value (Array int 3)) form)
  (if (!= 0 (@:@ form is-list))
    (let ((list-node \ (@:@ form list-node)))
      (if (and (= 0 (@:@ list-node is-list))
               (= 0 (strncmp (@:@ list-node token-str) "array" (cast 5 size))))
        (qq setf (: (uq value) elements) (uq form))
        (qq setv (uq value) (uq form))))
    (qq setv (uq value) (uq form)))))

(def defvar (macro intern (name linkage type value)
  (if (exists-macro mc (qq setf-macro-init (uq type) int))
    (qq do
      (def (uq name) (var (uq linkage) (uq type)))
      (setf-macro-init (uq name) (uq value)))
    (qq def (uq name) (var (uq linkage) (uq type) (uq value))))))
)

(def main (fn extern-c void (void)
  (defvar arr1 auto (Array int 3) (array 1 2 3))  ;;use the new version
  (defvar arr2 auto (Array int 3) ((elements (array 4 5 6))))  ;;old assignment still works
  (printf "%i,%i,%i\n" (@$ arr1 0) (@$ arr1 1) (@$ arr1 2))
  (printf "%i,%i,%i\n" (@$ arr2 0) (@$ arr2 1) (@$ arr2 2))
))

This would simpify initializing and setting concept-types #89, and allow to define behaviour for initializing structs with incomplete parameter lists, like using default values or setting all vars to zero by default #124

porky11 commented 7 years ago

when trying to implement this behaviour as macros, the problem is, that adding this and setf are not enough, becuase macros depending on them may have to be rewritten. (like (let …) and incf) with the new function names. This may be avoided if it is possible to override core forms directly in other namespaces, so that the expansions of the existing macros are expanded like my own macros (see also #130) But one of the init functions will still be called, even if setf-macro-init is defined, so I'd almost have to reimplement most of the default behaviour when I don't want this too (my own default init function, and never define the original one). Maybe it would also make sence, if the behaviour of setf and variable definitions was implemented as macro, so it would be easier to reimplement it. Maybe just by using a setf-expansion-hook and a variable-definition-hook or similar.

tomhrr commented 6 years ago

I think most of what's needed here can be got by way of the existing language. It's possible to define an init procedure (which may be a macro) that can take the name of the variable as its argument: in such an instance, the compiler will assume that the variable is fully initialised by that call. (This was previously undocumented, but there are now some notes about it on the initialisation/destruction page.) That does mean the form has to be (init {var-name} ...) instead of just ..., but that's a fairly small cost as against the complexity of the alternative approaches.