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 clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com 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 clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.