Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit 227409fe8547272ef5e3be81c545d9922ec5e9ac @kumarshantanu committed Aug 29, 2012
Showing with 941 additions and 0 deletions.
  1. +12 −0 .gitignore
  2. +9 −0 CHANGES.md
  3. +137 −0 README.md
  4. +3 −0 doc/intro.md
  5. +30 −0 project.clj
  6. +15 −0 run-tests.html
  7. +12 −0 run-tests.js
  8. +74 −0 src/quiddity/core.clj
  9. +148 −0 src/quiddity/lib.clj
  10. +11 −0 test-cljs/quiddity/run_tests.cljs
  11. +123 −0 test/quiddity/core_test.clj
  12. +36 −0 test/quiddity/lib_jvm_test.clj
  13. +331 −0 test/quiddity/lib_test.clj
@@ -0,0 +1,12 @@
+/target
+/lib
+/classes
+/checkouts
+pom.xml
+*.jar
+*.class
+.lein-deps-sum
+.lein-failures
+.lein-plugins
+.classpath
+.project
@@ -0,0 +1,9 @@
+# Changes and TODO
+
+## 2012-August-?? / 0.1.0
+
+* S-Expression evaluation
+* Clojure and ClojureScript support
+* 'Evaluator' to make up for lack of support for
+ * Special forms
+ * Macros
137 README.md
@@ -0,0 +1,137 @@
+# quiddity
+
+An [S-Expression](http://en.wikipedia.org/wiki/S-expression) evaluation library
+for Clojure and ClojureScript. Unlike Clojure's `eval`, _Quiddity_ lets you work
+with limited forms using user-specified _environment_.
+
+
+## Usage
+
+This library is not on Clojars yet.
+
+Leiningen dependency: `[quiddity "0.1.0-SNAPSHOT"]`
+
+
+### Evaluation
+
+You can invoke Quiddity evaluation with `quiddity.core/evaluate`:
+
+Syntax:
+
+```clojure
+(evaluate form env err-h)
+```
+
+`form` a valid Clojure form
+`env` user-specified environment -- a collection of maps for value lookup
+`err-h` error handler (function that accepts error message and deals with it)
+
+Clojure example:
+
+```clojure
+(evaluate (read-string "(inc (dec (+ n 10)))") [{:+ + :inc inc :dec dec :n 20}]
+ #(throw (RuntimeException. %)))
+```
+
+ClojureScript example:
+
+```clojure
+(evaluate (read-string "(inc (dec (+ n 10)))") [{:+ + :inc inc :dec dec :n 20}]
+ #(throw (js/Error %)))
+```
+
+
+#### How lookup works
+
+The _symbol_ values are looked up in each of the maps in `env`. When not found
+in any of them, the error handler is called with the error message as argument.
+
+The expression symbols can be present in `env` as keys, or their corresponding
+keywords as the keys. For example, `(foo 42)` will look for both `'foo` and
+`:foo` as a key in `env`.
+
+
+### Limitation
+
+Quiddity supports only limited forms in the S-expressions it evaluates. The
+following are not supported (note that shortcuts to special forms and macros
+eg. `'`, `#'`, `#(...)` etc. are automatically expanded by the reader):
+
+* Special forms
+* Macros
+* Destructuring
+* Creating functions
+
+
+### Evaluator
+
+In order to make up for some of the limitations listed above, Quiddity supports
+something called an _evaluator_. An evaluator is an annotated function that
+
+* accepts `env` as first argument
+* accepts remaining arguments as listed in the expression
+* is responsible for evaluating the arguments
+
+Evaluators may appear at any levels of the S-expression. You can use
+`quiddity.core/make-evaluator`, eg. `(make-evaluator fn-object)` to annotate a
+function as an evaluator. Evaluators are passed as part of `env` during
+evaluation just like other functions. However, they are treated specially
+during invocation.
+
+The following evaluators are provided in `quiddity.lib/all` (map):
+
+* Special forms equivalent
+ * `if`
+ * `do`
+ * `quote`
+* Macros equivalent
+ * `if-not`
+ * `when`
+ * `when-not`
+ * `let`
+ * `for-each` (same as `for` without `:let`, `:when`, `:while` forms)
+ * `and`
+ * `or`
+
+
+## Development
+
+### Running tests
+
+Clean older stuff if any (required for ClojureScript testing)
+
+```
+$ lein dev clean
+```
+
+Run the tests
+
+```
+lein dev test # test with Clojure & ClojureScript (needs `phantomjs` installed)
+lein all test # test with all supported Clojure versions
+```
+
+
+### Building a JAR (and installing to local repo)
+
+Assuming the line `:hooks [leiningen.cljsbuild]` in `project.clj` is
+commented out:
+
+```
+lein jar # create the JAR file
+lein install # install JAR to local Maven repo
+```
+
+
+## Getting in touch
+
+On Twitter: [@kumarshantanu](https://twitter.com/kumarshantanu)
+
+E-mail: kumar(dot)shantanu at gmail.com
+
+
+## License
+
+Copyright © 2012 Shantanu Kumar
+
+Distributed under the Eclipse Public License, the same as Clojure.
@@ -0,0 +1,3 @@
+# Introduction to quiddity
+
+TODO: write [great documentation](http://jacobian.org/writing/great-documentation/what-to-write/)
@@ -0,0 +1,30 @@
+(defproject quiddity "0.1.0-SNAPSHOT"
+ :description "An S-Expression evaluation library for Clojure and ClojureScript"
+ :url "http://example.com/FIXME"
+ :license {:name "Eclipse Public License"
+ :url "http://www.eclipse.org/legal/epl-v10.html"}
+ ;; :dependencies [[org.clojure/clojure "1.4.0"]]
+ :warn-on-reflection true
+ :plugins [[lein-cljsbuild "0.2.6"]]
+ :profiles {:tst {:dependencies [[clip-test "0.1.0-SNAPSHOT"]]}
+ :jst {:source-paths ["src" "test"]
+ ;; Enable the lein hooks for: clean, compile, test, and jar.
+ :hooks [leiningen.cljsbuild]
+ :cljsbuild {:crossovers [quiddity.core quiddity.lib
+ quiddity.core-test quiddity.lib-test
+ clip-test.internal]
+ ;; Command for running the unit tests in CLJS
+ ;; $ lein cljsbuild test
+ :test-commands {"unit" ["phantomjs"
+ "run-tests.js"]}
+ :builds {:test {:source-path "test-cljs"
+ :compiler
+ {:output-to "target/quiddity-test.js"
+ ;; :optimizations nil
+ :pretty-print true}}}}}
+ :1.2 {:dependencies [[org.clojure/clojure "1.2.1"]]}
+ :1.3 {:dependencies [[org.clojure/clojure "1.3.0"]]}
+ :1.4 {:dependencies [[org.clojure/clojure "1.4.0"]]}
+ :1.5 {:dependencies [[org.clojure/clojure "1.5.0-alpha4"]]}}
+ :aliases {"all" ["with-profile" "1.2,tst:1.3,tst:1.4,tst:1.5,tst"]
+ "dev" ["with-profile" "1.4,tst,jst"]})
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <META http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <head>
+ <title>Running tests for quiddity</title>
+ </head>
+ <body>
+ <script language='javascript' src="target/quiddity-test.js"></script>
+ <script language='javascript'>
+ quiddity.run_tests.run();
+ </script>
+ </body>
+</html>
@@ -0,0 +1,12 @@
+try {
+ phantom.injectJs('target/quiddity-test.js');
+ console.log('Injected target/quiddity-test.js');
+ quiddity.run_tests.run();
+} catch (e) {
+ console.log('Found exception');
+ console.log(e);
+ console.log(e.fileName);
+ console.log(e.trace);
+} finally {
+ phantom.exit();
+}
@@ -0,0 +1,74 @@
+(ns quiddity.core)
+
+
+(def ^{:dynamic true
+ :doc "Error handler that accepts error message and deals with it"}
+ *error-handler* (fn [message]
+ (assert (not "Must override *error-handler* before use"))))
+
+
+(defn env-get
+ [k maps]
+ (let [r (some (fn [m]
+ (or (and (symbol? k)
+ (let [kw (keyword k)]
+ (and (contains? m kw) [(get m kw)])))
+ (and (contains? m k) [(get m k)])))
+ maps)]
+ (if r (first r)
+ (*error-handler* (format "No such key '%s' in env keys %s" (str k)
+ (pr-str (map keys maps)))))))
+
+
+(declare evaluate)
+
+
+(defn realize-coll
+ [form maps]
+ (if (map? form)
+ (let [ks (realize-coll (keys form) maps)
+ vs (realize-coll (vals form) maps)]
+ (zipmap ks vs))
+ (let [coll-fn (cond
+ (vector? form) vec
+ (set? form) set
+ :otherwise list*)]
+ (coll-fn (map #(evaluate % maps) form)))))
+
+
+(defn evaluator?
+ [f]
+ (and (vector? f)
+ (true? (:quiddity-evaluator? (meta f)))))
+
+
+(defn make-evaluator
+ [x]
+ (if (evaluator? x) x
+ (->> {:quiddity-evaluator? true}
+ (with-meta [x]))))
+
+
+(defn evaluate
+ "Evaluate S-expression using specified env `maps`"
+ ([form maps]
+ {:pre [(every? map? maps)]}
+ (cond
+ ;; symbol, hence lookup in env
+ (symbol? form) (env-get form maps)
+ ;; function call
+ (and (or (list? form)
+ (seq? form))
+ (seq form)) (let [func (evaluate (first form) maps)
+ tail (rest form)]
+ (if (evaluator? func)
+ (apply (first func) maps tail)
+ (apply func (realize-coll tail maps))))
+ ;; any collection
+ (coll? form) (realize-coll form maps)
+ ;; constant
+ :otherwise form))
+ ([form maps error-handler]
+ {:pre [(fn? error-handler)]}
+ (binding [*error-handler* error-handler]
+ (evaluate form maps))))
Oops, something went wrong.

0 comments on commit 227409f

Please sign in to comment.