Attempting to use clojure match like so:
(defn eval-arraklisp
"not sure yet"
[tree scope]
(match tree
[:SYM sym] (lookup sym scope)
[:NUM num] (read-string num)
[:FUN & params body] [:FUN & body]
[:CALL [:SYM sym] & args] (call-sym sym scope args)
[:CALL [:FUN & params body] & args]))
I get the following on the (match tree line:
#error {
:cause No method in multimethod 'to-source' for dispatch value: :clojure.core.match/rest
:via
[{:type clojure.lang.Compiler$CompilerException
Can't figure out why.
The problem is in following line:
[:FUN & params body] [:FUN & body]
There are two symbols in rest section of match, in particular params and body, but it is not allowed to have more than one. This causes error you are getting.
Probably, you should rewrite this match clause as follows:
[:FUN params & body] [:FUN & body]
Also, as noted by #Andre, you should review the return value of this match, because & symbol is definitely redundant there.
Related
I'm writing a function to parse out IRC RFC2813 messages into their constituent parts. This consists of two functions, one to split the message via regex, and another to modify the return to handle certain special cases.
(let [test-privmsg ":m#m.net PRIVMSG #mychannel :Hiya, buddy."])
(defn ircMessageToMap [arg]
"Convert an IRC message to a map based on a regex"
(println (str "IRCMapifying " arg))
(zipmap [:raw :prefix :type :destination :message]
(re-matches #"^(?:[:](\S+) )?(\S+)(?: (?!:)(.+?))?(?: [:](.+))?$"
arg
)
)
)
(defn stringToIRCMessage [arg]
"Parses a string as an IRC protocol message, returning a map"
(let [r (doall (ircMesgToMap arg))])
(println (str "Back from the wizard with " r))
(cond
;Reformat PING messages to work around regex shortcomings
(= (get r :prefix) "PING") (do
(assoc r :type (get r :prefix))
(assoc r :prefix nil)
)
;Other special cases here
:else r)
)
The problem I'm running into is that the stringToIRCMessage function doesn't appear to be realizing the return value of ircMesgToMap. If I evaluate (stringToIRCMessage test-privmsg), the println statement gives me:
Back from the wizard with Unbound: #'irc1.core/r
..but the "IRCMapifying" result from ircMessageToMap appears on the console beforehand indicating that it was evaluated correctly.
The doall was an attempt to force the result to be realized in the middle of the function - it had no effect.
How should I rewrite this stringToIRCMessage function to get the r variable usable?
The parens are wrong in your let statement.
Should look like this:
(let [r (doall (ircMesgToMap arg)) ]
(println (str "Back from the wizard with " r))
(cond
;Reformat PING messages to work around regex shortcomings
(= (get r :prefix) "PING") (do
(assoc r :type (get r :prefix))
(assoc r :prefix nil)
)
;Other special cases here
:else r))
I am getting the following stacktrace when running the command: lein run "this is the other different thing" "this,different,other"
Stacktrace
Exception in thread "main" java.lang.NullPointerException, compiling:(/private/var/folders/y8/6lt_81xn47d4n2k641z52rg00000gn/T/form-init8328218573408236617.clj:1:125)
at clojure.lang.Compiler.load(Compiler.java:7391)
at clojure.lang.Compiler.loadFile(Compiler.java:7317)
at clojure.main$load_script.invokeStatic(main.clj:275)
at clojure.main$init_opt.invokeStatic(main.clj:277)
at clojure.main$init_opt.invoke(main.clj:277)
at clojure.main$initialize.invokeStatic(main.clj:308)
at clojure.main$null_opt.invokeStatic(main.clj:342)
at clojure.main$null_opt.invoke(main.clj:339)
at clojure.main$main.invokeStatic(main.clj:421)
at clojure.main$main.doInvoke(main.clj:384)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.main.main(main.java:37)
Caused by: java.lang.NullPointerException
at clojure.string$replace.invokeStatic(string.clj:101)
at clojure.string$replace.invoke(string.clj:75)
at redact.core$redact_doc.invokeStatic(core.clj:12)
at redact.core$redact_doc.invoke(core.clj:7)
at redact.core$_main.invokeStatic(core.clj:54)
at redact.core$_main.doInvoke(core.clj:50)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:383)
at user$eval5.invokeStatic(form-init8328218573408236617.clj:1)
at user$eval5.invoke(form-init8328218573408236617.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6917)
at clojure.lang.Compiler.load(Compiler.java:7379)
... 14 more
And here is my code:
(ns redact.core
(:gen-class)
(:require [clojure.java.io :as io]
[clojure.string :as str]
))
(defn redact-doc
;; Reads the file line by line and redacts all the matched words
([target stoplist]
(if (empty? stoplist)
(str/trim target)
(redact-doc (str/replace target (re-pattern (str "\\s(" (first stoplist) ")(\\s|$)")) " REDACTED ") (rest stoplist))))
)
(defn get-target-text
;; Takes a vector of args and returns a String of a text file or and sentances
([args] (get-target-text args ""))
([args result]
(if (empty? args)
result
(get-target-text (rest args) (if (boolean (re-find #"(.+\.[^csv\s])" (first args)))
(str result (slurp (first args)))
(if (not (boolean (re-find #"(.+\.csv|.+,.+)" (first args))))
(if (boolean (re-find #"\s" (str/trim (first args))))
(str result (first args) " ")))))))
)
(defn read-csv
;; Takes in a filename and returns a vector of the csv values
[file-name]
(str/split (with-open [rdr (io/reader file-name)]
(doall (reduce str (line-seq rdr)))) #","))
(defn gen-stoplist
;; Generates the stoplist for words to be redacted
([args] (gen-stoplist args []))
([args stoplist]
(if (empty? args)
stoplist
(gen-stoplist (rest args) (if (boolean (re-find #"(.+\.csv)" (first args)))
(into [] (concat stoplist (read-csv (first args))))
(if (boolean (re-find #"(.+\..[^csv\s])" (first args)))
stoplist
(if (boolean (re-find #"(.*,.*)" (first args)))
(into [] (concat stoplist (str/split (first args) #",")))
(if (boolean (re-find #"(\s)" (str/trim (first args))))
stoplist
(into [] (concat stoplist [(first args)] ))))))))))
(defn -main
([& args]
(def stoplist (gen-stoplist args))
(def target-text (get-target-text args))
(println (redact-doc target-text stoplist)))
)
I have been staring at this trying to figure out what is causing the issue. I have tested all of the methods independently on the REPL and they all seem to work but the (-main) method is throwing a null pointer exception on the str/replace call....just not sure why. Any help you can give is much appreciated. Thanks!
There is a fair bit about your code which is not really correct. My guess is
that our getting that call because your calling a function which is expecting a
value and is getting a nil passed in - my guess would be one of the string
functions.
your function definitions are not quite right. If your function only has a
single 'signature' then you don't need the additional brackets. You should also
use let bindings inside rather than def. e.g.
(defn -main
[& args]
(let [stoplist (gen-stoplist args)
target-text (get-target-text args))]
(println (redact-doc target-text stoplist)))
Your code is not passing what you think to gen-stoplist or get-target-text. I
suspect the null pointer is because of the call to str/trim being passed a nil
rather than a string.
My suggestion would be to open a repl and interact with it using some println in
your functions to look at what is getting parsed in.
I wrote a short function for debugging:
(defn printvar
"Print information about given variables in `name : value` pairs"
[& vars]
(dorun (map #(println (name %) ":" (eval %)) vars)))
Then I tried to test it:
(defn -main [arg1 arg2]
(def moustache true) (def answer 42) (def ocelots-are "awesome!")
(printvar 'moustache 'answer 'ocelots-are)
(printvar 'arg1 'arg2))
But ran into some really confusing behaviour:
$ lein repl
> (-main "one" "two")
# moustache : true
# answer : 42
# ocelots-are : awesome!
# CompilerException java.lang.RuntimeException: Unable to resolve symbol: arg1 in this context, compiling:(/tmp/form-init4449285856851838419.clj:1:1)
$ lein run "one" "two"
# Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: moustache in this context, compiling:(/tmp/form-init4557344131005109247.clj:1:113)
Experimenting a bit more, I discovered this:
(defn -main [arg1 arg2]
(meta #'arg1))
# Exception in thread "main" java.lang.RuntimeException: Unable to resolve var: arg1 in this context, compiling:(dict_compress/core.clj:9:11)
(defn -main [arg1 arg2]
(def arg1 arg1)
(meta #'arg1))
# {:ns #<Namespace dict-compress.core>, :name arg1, :file dict_compress/core.clj, :column 2, :line 10}
Now I'm totally confused.
What exactly are you passing when you do (f 'var) and (f var)?
Why are there different behaviours when run from the REPL versus directly?
What's the difference between a received argument versus a defined variable?
How can I fix my code?
Am I going about debugging the wrong way?
Inside printvar the def'ed vars moustache answer and ocelots-are are correctly printed because def defines them as "globals".
Meaning there is a moustache var that the printvar function can "see".
Think about it this way, this works:
(def moustache 43)
(defn printvar []
(println moustache)
(defn main [arg1]
(printvar))
This doesn't work:
(defn printvar []
(println arg1))
(defn main [arg1]
(printvar))
Which is exactly what you're doing, passing the parameter name to eval does nothing for the parameter scope (printvar won't be able to see it).
A couple of issues with your code:
You shouldn't be defing inside a function, local bindings are defined with let
If you want to eval you need to consider scope of what you're evaling.
Just to elaborate on #Guillermo's comment, here is a macro that does the printing of any variable, locally or globally bound.
(defmacro printvar
([])
([v & more]
`(let [v# ~v]
(println '~v "=" v#)
(when (seq '~more)
(printvar ~#more)))))
With this you can try the sequence :
user> (def glob-var "foo")
#'user/glob-var
user> (defn -main [loc1 loc2]
(printvar glob-var loc1 loc2))
#'user/-main
user> (-main "bar" 42)
glob-var = foo
loc1 = bar
loc2 = 42
nil
user>
I wrote a macro to handle http response
(defmacro defhandler
[name & args]
(let [[docstring args] (if (string? (first args))
[(first args) (next args)]
[nil args])
args (apply hash-map :execute-if true (vec args))]
`(do
(def ~name
(with-meta (fn [scope# promise#]
(let [e# (:execute-if ~args)
ei# (if (fn? e#)
(e# scope#)
(boolean e#))]
(when ei#
(.then promise# (fn [result#]
(let [{:strs [http-status# value#]} result#
the-func# ((keyword http-status#) ~args)]
(the-func# scope# value#))))))) {:structure ~args}))
(alter-meta! (var ~name) assoc :doc ~docstring))))
So I can do
(defhandler my-handler
:200 (fn [$scope value] (set! (.-content $scope) value)))
But that throws "UnmatchedDelimiter" at line 1, but if I try with a named function:
(defn my-func [$scope value] (set! (.-content $scope) value))
(defhandler my-handler
:200 my-func)
It works ok. I'm just curious, is that a normal behaviour?
That is not the behavior I see when I try your example, nor does it seem very likely. I suggest checking that the forms you pasted here are exactly the ones that produce an error; I suspect your actual anonymous function included one too many )s.
I have run into this problem... I have two macros that look very similar
(import java.lang.management.ManagementFactory)
(defmacro with-thread-manager [tm & body]
{:pre [(and (vector? tm) (= 1 (count tm)))]}
`(let [~(first tm) (ManagementFactory/getThreadMXBean)]
~#body))
(defmacro with-os-manager [tm & body]
{:pre [(and (vector? tm) (= 1 (count tm)))]}
`(let [~(first tm) (ManagementFactory/getOperatingSystemMXBean)]
~#body))
they are used as follows:
(defn thread-count []
(with-thread-manager [tm] (.getThreadCount tm)))
(thread-count)
;; => 12
(defn application-cpu-time []
(with-os-manager [osm] (.getProcessCpuTime osm)))
(application-cpu-time)
;; => 71260000000
I wish to generalise the two with-*-manager into another macro so that I can simpify them like this:
(defmanagementblock with-thread-manager (ManagementFactory/getThreadMXBean))
(defmanagementblock with-os-manager (ManagementFactory/getOperatingSystemMXBean))
so the easiest way I knew was to change the macro a little bit
(defmacro with-thread-manager [tm & body]
{:pre [(and (vector? tm) (= 1 (count tm)))]}
(apply list 'let [(first tm) '(ManagementFactory/getThreadMXBean)]
body))
and to write the block:
(defmacro defmanageblock [name MANAGER]
(list 'defmacro name '[tm & body]
'{:pre [(and (vector? tm) (= 1 (count tm)))]}
(list 'apply 'list ''let (vector '(first tm) 'MANAGER)
'body)))
everything goes well except the MANAGER does not quote properly. I've tried a bunch of quoting and unquoting option like ' , ` , ~' and many other variations of it. but it does not give the right value.
One reasonable solution, if you don't mind keeping track of a few scopes in your head:
(defmacro defmanagedblock [name mgr]
`(defmacro ~name [tm# & body#]
{:pre [(and (vector? tm#)) (= 1 (count tm#))]}
`(let [~(first tm#) ~'~mgr]
~#body#)))
If you define defmanageblock to be a function that takes a symbol describing the factory to use which returns the s-expression as a list then you would have two macros that call functions instead of nested macros . In most cases doing the real code generation work in functions rather than macros makes the code easier to reason about and test