Answering myself, the solution has been to do a (use 'mw-engine.utils) in
the immediate evaluation environment - just in the same file isn't enough.
So I've written a function
(defn compile-rule
"Parse this `rule-text`, a string conforming to the grammar of MicroWorld
rules,
into Clojure source, and then compile it into an anonymous
function object, getting round the problem of binding mw-engine.utils in
the compiling environment."
[rule-text]
(do
(use 'mw-engine.utils)
(eval (parse-rule rule-text))))
This works!
On Saturday, 5 July 2014 14:10:33 UTC+1, Simon Brooke wrote:
>
> I am trying to write a domain-specific production rule language which is
> compiled down at run-time into Clojure code. It's intended that the rule
> language should be usable by primary school children with little help.
>
> So far it's going extremely well, except for one problem. If I run it in
> the REPL, like this, it works:
>
>
> user=> (use 'mw-parser.core :reload)
> nil
> user=> (use 'mw-engine.utils)
> nil
> user=> (parse-rule "if state is forest and fertility is between 55 and 75
> then state should be climax")
> (fn [cell world] (if (and (= (:state cell) :forest) (or (< 55 (get-int
> cell :fertility) 75) (> 55 (get-int cell :fertility) 75))) (merge cell
> {:state :climax})))
> user=> (eval *1)
> #<user$eval1839$fn__1840 user$eval1839$fn__1840@7a52b16b>
> user=> (apply *1 (list {:state :forest :fertility 60} nil))
> {:state :climax, :fertility 60}
> user=>
>
>
> However, I have a test as follows:
>
> ns mw-parser.core-test
> (:use mw-engine.utils)
> (:require [clojure.test :refer :all]
> [mw-parser.core :refer :all]))
>
> (deftest rules-tests
> (testing "if altitude is less than 100 and state is forest then state
> should be climax and deer should be 3"
> (is (parse-rule "if altitude is less than 100 and state is
> forest then state should be climax and deer should be 3"))
> (is (let [cell (apply (eval (parse-rule "if altitude is less
> than 100 and state is forest then state should be climax and deer should be
> 3"))
> (list {:state :forest :altitude 99} nil))]
> (and (= (:state cell) :climax) (= (:deer cell) 3))))
> ))
>
> This fails as follows:
>
> simon@engraver:~/workspace/mw-parser$ lein test
>
> lein test mw-parser.core-test
>
> lein test :only mw-parser.core-test/rules-tests
>
> ERROR in (rules-tests) (Compiler.java:6380)
> if altitude is less than 100 and state is forest then state should be
> climax and deer should be 3
> expected: (let [cell (apply (eval (parse-rule "if altitude is less than
> 100 and state is forest then state should be climax and deer should be 3"))
> (list {:state :forest, :altitude 99} nil))] (and (= (:state cell) :climax)
> (= (:deer cell) 3)))
> actual: clojure.lang.Compiler$CompilerException:
> java.lang.RuntimeException: Unable to resolve symbol: get-int in this
> context, compiling:(/tmp/form-init4592216274934008360.clj:1:6384)
>
>
> The function get-int is in mw-engine.utils, and is:
>
> (defn get-int
> "Get the value of a property expected to be an integer from a map; if
> not present (or not an integer) return 0.
>
> * `map` a map;
> * `key` a symbol or keyword, presumed to be a key into the `map`."
> [map key]
> (cond map
> (let [v (map key)]
> (cond (and v (integer? v)) v
> true 0))
>
> true (throw (Exception. "No map passed?"))))
>
>
> I'm trying to understand why this function is not available in the test
> environment when the anonymous function generated from the rule text is
> compiled and applied. This matters, because I expect the users of the
> system to add rules via a web form, so they won't have a REPL.
>
> As you can see I've specified that mw-engine.utils is used by the test
> file, but that does not apparently make the namespace available in eval. It
> is the eval step, not the apply step, that fails, I've verified that by
> trying:
>
> user=> (use 'mw-parser.core :reload)
> nil
> user=> (parse-rule "if state is forest and fertility is between 55 and 75
> then state should be climax")
> (fn [cell world] (if (and (= (:state cell) :forest) (or (< 55 (get-int
> cell :fertility) 75) (> 55 (get-int cell :fertility) 75))) (merge cell
> {:state :climax})))
> user=> (eval *1)
>
> CompilerException java.lang.RuntimeException: Unable to resolve symbol:
> get-int in this context,
> compiling:(/tmp/form-init2345285067501587397.clj:1:1)
> user=>
>
>
> (Note that on this occasion I didn't include the (use 'mw-engine.utils)
> step)
>
> Any assistance gratefully received!
>
>
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.