mirror of
https://github.com/kanaka/mal.git
synced 2024-09-11 13:55:55 +03:00
mal: implement macro without metadata
Support for metadata becomes optional.
Support for fn? becomes optional again, reverting 5e5d4892
.
This commit is contained in:
parent
aac5cf7bf5
commit
28b63c0ca6
20
mal/core.mal
20
mal/core.mal
@ -1,13 +1,17 @@
|
||||
(def! _fn? (fn* [x]
|
||||
(if (fn? x)
|
||||
(not (get (meta x) "ismacro"))
|
||||
false)))
|
||||
(def! _macro_magic
|
||||
:_I_tell_ya_this_is_a_MAL_macro_mark_my_words)
|
||||
|
||||
(def! _macro_wrap (fn* [f]
|
||||
[_macro_magic f]))
|
||||
|
||||
(def! _macro_unwrap (fn* [x]
|
||||
(if (vector? x)
|
||||
(if (= (first x) _macro_magic)
|
||||
(nth x 1)))))
|
||||
|
||||
(def! _macro? (fn* [x]
|
||||
(if (fn? x)
|
||||
(if (get (meta x) "ismacro")
|
||||
(if (_macro_unwrap x)
|
||||
true
|
||||
false)
|
||||
false)))
|
||||
|
||||
(def! core_ns
|
||||
@ -22,7 +26,7 @@
|
||||
['symbol? symbol?]
|
||||
['keyword keyword]
|
||||
['keyword? keyword?]
|
||||
['fn? _fn?]
|
||||
['fn? fn?]
|
||||
['macro? _macro?]
|
||||
|
||||
['pr-str pr-str]
|
||||
|
@ -29,12 +29,13 @@
|
||||
(let* [a0 (first ast)]
|
||||
(if (symbol? a0)
|
||||
(if (env-find env a0)
|
||||
(_macro? (env-get env a0))))))))
|
||||
(_macro_unwrap (env-get env a0))))))))
|
||||
|
||||
(def! MACROEXPAND (fn* [ast env]
|
||||
(if (is-macro-call ast env)
|
||||
(MACROEXPAND (apply (env-get env (first ast)) (rest ast)) env)
|
||||
ast)))
|
||||
(let* [m (is-macro-call ast env)]
|
||||
(if m
|
||||
(MACROEXPAND (apply m (rest ast)) env)
|
||||
ast))))
|
||||
|
||||
(def! eval-ast (fn* [ast env]
|
||||
;; (do (prn "eval-ast" ast "/" (keys env)) )
|
||||
@ -84,10 +85,7 @@
|
||||
(EVAL (QUASIQUOTE (nth ast 1)) env)
|
||||
|
||||
(= 'defmacro! a0)
|
||||
(let* [f (EVAL (nth ast 2) env)
|
||||
m (meta f)
|
||||
mac (with-meta f (assoc (if m m {}) "ismacro" true))]
|
||||
(env-set env (nth ast 1) mac))
|
||||
(env-set env (nth ast 1) (_macro_wrap (EVAL (nth ast 2) env)))
|
||||
|
||||
(= 'macroexpand a0)
|
||||
(MACROEXPAND (nth ast 1) env)
|
||||
|
@ -29,12 +29,13 @@
|
||||
(let* [a0 (first ast)]
|
||||
(if (symbol? a0)
|
||||
(if (env-find env a0)
|
||||
(_macro? (env-get env a0))))))))
|
||||
(_macro_unwrap (env-get env a0))))))))
|
||||
|
||||
(def! MACROEXPAND (fn* [ast env]
|
||||
(if (is-macro-call ast env)
|
||||
(MACROEXPAND (apply (env-get env (first ast)) (rest ast)) env)
|
||||
ast)))
|
||||
(let* [m (is-macro-call ast env)]
|
||||
(if m
|
||||
(MACROEXPAND (apply m (rest ast)) env)
|
||||
ast))))
|
||||
|
||||
(def! eval-ast (fn* [ast env]
|
||||
;; (do (prn "eval-ast" ast "/" (keys env)) )
|
||||
@ -84,10 +85,7 @@
|
||||
(EVAL (QUASIQUOTE (nth ast 1)) env)
|
||||
|
||||
(= 'defmacro! a0)
|
||||
(let* [f (EVAL (nth ast 2) env)
|
||||
m (meta f)
|
||||
mac (with-meta f (assoc (if m m {}) "ismacro" true))]
|
||||
(env-set env (nth ast 1) mac))
|
||||
(env-set env (nth ast 1) (_macro_wrap (EVAL (nth ast 2) env)))
|
||||
|
||||
(= 'macroexpand a0)
|
||||
(MACROEXPAND (nth ast 1) env)
|
||||
|
@ -29,12 +29,13 @@
|
||||
(let* [a0 (first ast)]
|
||||
(if (symbol? a0)
|
||||
(if (env-find env a0)
|
||||
(_macro? (env-get env a0))))))))
|
||||
(_macro_unwrap (env-get env a0))))))))
|
||||
|
||||
(def! MACROEXPAND (fn* [ast env]
|
||||
(if (is-macro-call ast env)
|
||||
(MACROEXPAND (apply (env-get env (first ast)) (rest ast)) env)
|
||||
ast)))
|
||||
(let* [m (is-macro-call ast env)]
|
||||
(if m
|
||||
(MACROEXPAND (apply m (rest ast)) env)
|
||||
ast))))
|
||||
|
||||
(def! eval-ast (fn* [ast env]
|
||||
;; (do (prn "eval-ast" ast "/" (keys env)) )
|
||||
@ -84,10 +85,7 @@
|
||||
(EVAL (QUASIQUOTE (nth ast 1)) env)
|
||||
|
||||
(= 'defmacro! a0)
|
||||
(let* [f (EVAL (nth ast 2) env)
|
||||
m (meta f)
|
||||
mac (with-meta f (assoc (if m m {}) "ismacro" true))]
|
||||
(env-set env (nth ast 1) mac))
|
||||
(env-set env (nth ast 1) (_macro_wrap (EVAL (nth ast 2) env)))
|
||||
|
||||
(= 'macroexpand a0)
|
||||
(MACROEXPAND (nth ast 1) env)
|
||||
|
@ -1521,27 +1521,6 @@ diff -urp ../process/step9_try.txt ../process/stepA_mal.txt
|
||||
entered by the user is returned as a string. If the user sends an
|
||||
end-of-file (usually Ctrl-D), then nil is returned.
|
||||
|
||||
* Add meta-data support to mal functions by adding a new metadata
|
||||
attribute on mal functions that refers to another mal value/type
|
||||
(nil by default). Add the following metadata related core functions:
|
||||
* `meta`: this takes a single mal function argument and returns the
|
||||
value of the metadata attribute.
|
||||
* `with-meta`: this function takes two arguments. The first argument
|
||||
is a mal function and the second argument is another mal
|
||||
value/type to set as metadata. A copy of the mal function is
|
||||
returned that has its `meta` attribute set to the second argument.
|
||||
Note that it is important that the environment and macro attribute
|
||||
of mal function are retained when it is copied.
|
||||
* Add a reader-macro that expands the token "^" to
|
||||
return a new list that contains the symbol "with-meta" and the
|
||||
result of reading the next next form (2nd argument) (`read_form`) and the
|
||||
next form (1st argument) in that order
|
||||
(metadata comes first with the ^ macro and the function second).
|
||||
* If you implemented as `defmacro!` to mutate an existing function
|
||||
without copying it, you can now use the function copying mechanism
|
||||
used for metadata to make functions immutable even in the
|
||||
defmacro! case...
|
||||
|
||||
* Add a new "\*host-language\*" (symbol) entry to your REPL
|
||||
environment. The value of this entry should be a mal string
|
||||
containing the name of the current implementation.
|
||||
@ -1552,6 +1531,7 @@ diff -urp ../process/step9_try.txt ../process/stepA_mal.txt
|
||||
"(println (str \"Mal [\" \*host-language\* \"]\"))".
|
||||
|
||||
* Ensure that the REPL environment contains definitions for `time-ms`,
|
||||
`meta`, `with-meta`, `fn?`
|
||||
`string?`, `number?`, `seq`, and `conj`. It doesn't really matter
|
||||
what they do at this stage: they just need to be defined. Making
|
||||
them functions that raise a "not implemented" exception would be
|
||||
@ -1610,8 +1590,29 @@ implementation.
|
||||
|
||||
#### Optional additions
|
||||
|
||||
* Add metadata support to other composite data types (lists, vectors
|
||||
and hash-maps), and to native functions.
|
||||
* Add meta-data support to composite data types (lists, vectors
|
||||
and hash-maps), and to functions (native or not), by adding a new
|
||||
metadata attribute that refers to another mal value/type
|
||||
(nil by default). Add the following metadata related core functions
|
||||
(and remove any stub versions):
|
||||
* `meta`: this takes a single mal function argument and returns the
|
||||
value of the metadata attribute.
|
||||
* `with-meta`: this function takes two arguments. The first argument
|
||||
is a mal function and the second argument is another mal
|
||||
value/type to set as metadata. A copy of the mal function is
|
||||
returned that has its `meta` attribute set to the second argument.
|
||||
Note that it is important that the environment and macro attribute
|
||||
of mal function are retained when it is copied.
|
||||
* Add a reader-macro that expands the token "^" to
|
||||
return a new list that contains the symbol "with-meta" and the
|
||||
result of reading the next next form (2nd argument) (`read_form`) and the
|
||||
next form (1st argument) in that order
|
||||
(metadata comes first with the ^ macro and the function second).
|
||||
* If you implemented as `defmacro!` to mutate an existing function
|
||||
without copying it, you can now use the function copying mechanism
|
||||
used for metadata to make functions immutable even in the
|
||||
defmacro! case...
|
||||
|
||||
* Add the following new core functions (and remove any stub versions):
|
||||
* `time-ms`: takes no arguments and returns the number of
|
||||
milliseconds since epoch (00:00:00 UTC January 1, 1970), or, if
|
||||
|
@ -24,6 +24,33 @@
|
||||
;; ------- (Needed for self-hosting) -------
|
||||
|
||||
;;
|
||||
;;
|
||||
;; Testing hash-map evaluation and atoms (i.e. an env)
|
||||
(def! e (atom {"+" +}))
|
||||
(swap! e assoc "-" -)
|
||||
( (get @e "+") 7 8)
|
||||
;=>15
|
||||
( (get @e "-") 11 8)
|
||||
;=>3
|
||||
(swap! e assoc "foo" (list))
|
||||
(get @e "foo")
|
||||
;=>()
|
||||
(swap! e assoc "bar" '(1 2 3))
|
||||
(get @e "bar")
|
||||
;=>(1 2 3)
|
||||
|
||||
;; Testing for presence of optional functions
|
||||
(do (list time-ms string? number? seq conj meta with-meta fn?) nil)
|
||||
;=>nil
|
||||
|
||||
;; ------------------------------------------------------------------
|
||||
|
||||
;>>> soft=True
|
||||
;>>> optional=True
|
||||
;;
|
||||
;; ------- Optional Functionality --------------
|
||||
;; ------- (Not needed for self-hosting) -------
|
||||
|
||||
;; Testing metadata on functions
|
||||
|
||||
;;
|
||||
@ -64,20 +91,6 @@
|
||||
(meta +)
|
||||
;=>nil
|
||||
|
||||
;; Testing fn? function
|
||||
(fn? +)
|
||||
;=>true
|
||||
(fn? (fn* () 0))
|
||||
;=>true
|
||||
(fn? cond)
|
||||
;=>false
|
||||
(fn? "+")
|
||||
;=>false
|
||||
(fn? :+)
|
||||
;=>false
|
||||
(fn? ^{"ismacro" true} (fn* () 0))
|
||||
;=>true
|
||||
|
||||
;;
|
||||
;; Make sure closures and metadata co-exist
|
||||
(def! gen-plusX (fn* (x) (with-meta (fn* (b) (+ x b)) {"meta" 1})))
|
||||
@ -94,33 +107,6 @@
|
||||
(meta plus8)
|
||||
;=>{"meta" 1}
|
||||
|
||||
;;
|
||||
;; Testing hash-map evaluation and atoms (i.e. an env)
|
||||
(def! e (atom {"+" +}))
|
||||
(swap! e assoc "-" -)
|
||||
( (get @e "+") 7 8)
|
||||
;=>15
|
||||
( (get @e "-") 11 8)
|
||||
;=>3
|
||||
(swap! e assoc "foo" (list))
|
||||
(get @e "foo")
|
||||
;=>()
|
||||
(swap! e assoc "bar" '(1 2 3))
|
||||
(get @e "bar")
|
||||
;=>(1 2 3)
|
||||
|
||||
;; Testing for presence of optional functions
|
||||
(do (list time-ms string? number? seq conj) nil)
|
||||
;=>nil
|
||||
|
||||
;; ------------------------------------------------------------------
|
||||
|
||||
;>>> soft=True
|
||||
;>>> optional=True
|
||||
;;
|
||||
;; ------- Optional Functionality --------------
|
||||
;; ------- (Not needed for self-hosting) -------
|
||||
|
||||
;;
|
||||
;; Testing string? function
|
||||
(string? "")
|
||||
@ -152,6 +138,20 @@
|
||||
|
||||
(def! add1 (fn* (x) (+ x 1)))
|
||||
|
||||
;; Testing fn? function
|
||||
(fn? +)
|
||||
;=>true
|
||||
(fn? add1)
|
||||
;=>true
|
||||
(fn? cond)
|
||||
;=>false
|
||||
(fn? "+")
|
||||
;=>false
|
||||
(fn? :+)
|
||||
;=>false
|
||||
(fn? ^{"ismacro" true} (fn* () 0))
|
||||
;=>true
|
||||
|
||||
;; Testing macro? function
|
||||
(macro? cond)
|
||||
;=>true
|
||||
|
Loading…
Reference in New Issue
Block a user