tomhrr / dale

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

Bug: Macroexpansion: Inner macros get expanded before outer macros #157

Closed porky11 closed 6 years ago

porky11 commented 7 years ago

Macros should get the unexpanded DNodes as parameters. If the arguments have to be expanded, they should expand them oneself. Here some example, where this may cause problems:

(import macros)

(using-namespace std.macros

(def defn (macro intern (name linkage type args drop-first-expression rest)
  (def list (var auto \ (get-varargs-list mc (- (arg-count mc) 5) rest)))
  (qq def (uq name) (fn (uq linkage) (uq type) (uq args) (uql list)))))

(def a (macro intern (a)
  (print a)
  (printf "\n")
  (qq b float)))

(defn a intern void ((a int)) ;;variable name a gets expanded before defn gets expanded
  (a this-should-not-be-printed) ;;this gets expanded and prints something, instead of just being dropped
  (printf "int\n"))

(def b (macro intern (rest)
  (printf "macro?\n")
  (qq printf "not-int\n")))

(def main (fn extern-c void (void)
  (a 1) ;;should print a line with "int", instead "not-int" will be printed
  (a 1.0))) ;;should print "not-int", instead "int" will be printed
)
porky11 commented 7 years ago

Some question: Is it even possible to expand macros manually currently? (something like macroexpand or macroexpand-1)? This may be useful when writing macros for calling functions and macros in different ways (like for #155). Currently safe wont be called for macros, when It would be done, there needs to be defined a version for every macro, else(safe (let ((x \ 1)) body))will expand to(safe-let (safe ((x \ 1)) (safe body)). When macro-expanding is possible, only for special forms, special behaviour of which parameters will be setsafe` needs to be called. The macros could be expanded then, and handeled like safe specialforms and function calls.

tomhrr commented 6 years ago

Thanks for this. There was a problem with macros receiving evaluated arguments, rather than unevaluated arguments, which has now been fixed. The output of the test program is now like so:

$ dalec test.dt
int
this-should-not-be-printed
1.0
macro?
$ ./a.out
int
not-int

The reason that 'this-should-not-be-printed' appears is that each function/macro argument is fully evaluated during the dispatch process, so as to determine its type. The macro documentation has been updated to clarify this point.