shen.clj

0.1.4


Shen is a portable functional programming language by Mark Tarver

dependencies

clojure
1.4.0-beta5

dev dependencies

org.clojure/tools.trace
0.7.2-20120223.025622-2
marginalia
0.7.0
lein-difftest
1.3.7



(this space intentionally left almost blank)
 
(ns shen.install
  (:use [clojure.java.io :only (file reader writer)]
        [clojure.pprint :only (pprint)])
  (:require [clojure.string :as string]
            [shen.primitives])
  (:import [java.io StringReader PushbackReader FileNotFoundException]
           [java.util.regex Pattern])
  (:gen-class))
(def shen-namespaces '[sys writer declarations core load macros prolog reader sequent
                       toplevel track t-star printer yacc types])
(def kl-dir (->> ["../../K Lambda" "shen/klambda"]
                 (map file) (filter #(.exists %)) first))
(def cleanup-symbols-pattern
  (re-pattern (str "(\\s+|\\()("
                   (string/join "|" (map #(Pattern/quote %) [":" ";" "{" "}" ":-" ":="
                                                             "/." "@p" "@s" "@v"
                                                             "shen-@s-macro"
                                                             "shen-@v-help"
                                                             "shen-i/o-macro"
                                                             "shen-put/get-macro"
                                                             "XV/Y"]))
                   ")(\\s*\\)|\\s+?)"
                   "(?!~)")))
(defn cleanup-symbols
  [kl] (string/replace kl
                       cleanup-symbols-pattern
                       "$1(intern \"$2\")$3"))
(defn read-kl [kl]
  (with-open [r (PushbackReader. (StringReader. (cleanup-symbols kl)))]
    (doall
     (take-while (complement nil?)
                 (repeatedly #(read r false nil))))))
(defn read-kl-file [file]
  (try
    (cons `(clojure.core/comment ~(str file)) (read-kl (slurp file)))
    (catch Exception e
      (println file e))))
(defn header [ns]
  `(~'ns ~ns
     (:refer-clojure :only [])
     (:use [shen.primitives])
     (:require [clojure.core :as ~'core])
     (:gen-class)))
(def missing-declarations '#{shen-kl-to-lisp FORMAT READ-CHAR})
(defn declarations [clj]
  (into missing-declarations
        (map second (filter #(= 'defun (first %)) clj))))
(defn write-clj-file [dir name forms]
  (with-open [w (writer (file dir (str name ".clj")))]
    (binding [*out* w]
      (doseq [f forms]
        (pprint f)
        (println)))))
(defn project-version []
  (-> (slurp "project.clj") read-string (nth 2)))
(defn kl-to-clj
  ([] (kl-to-clj kl-dir
                 *compile-path*))
  ([dir to-dir]
     (.mkdirs (file to-dir))
     (let [shen (mapcat read-kl-file
                        (map #(file dir (str % ".kl")) shen-namespaces))
           dcl (declarations shen)]
       (write-clj-file to-dir "shen"
                       (concat [(header 'shen)]
                               [`(clojure.core/declare ~@(filter symbol? dcl))]
                               ['(core/intern 'shen.globals (core/with-meta '*language* {:dynamic true}) "Clojure")]
                               [(concat '(core/intern 'shen.globals (core/with-meta '*port* {:dynamic true}))
                                        [(project-version)])]
                               (map #(shen.primitives/shen-kl-to-clj %)
                                    (remove string? shen))
                               ['(clojure.core/load "shen/overwrite")]
                               ['(core/defn -main [] (shen-shen))])))))
(defn install []
  (try
    (require 'shen)
    (catch FileNotFoundException _
      (println "Creating shen.clj")
      (kl-to-clj))))
(defn swank [port]
  (try
    (require 'swank.swank)
    (with-out-str
      ((resolve 'swank.swank/start-repl) port))
    (println "Swank connection opened on" port)
    (catch FileNotFoundException _)))
(defn -main []
  (install)
  (require 'shen)
  (binding [*ns* (the-ns 'shen)]
    (swank 4005)
    ((resolve 'shen/-main))))
(when *compile-files*
  (install))
(defn repl? []
  (->> (Thread/currentThread) .getStackTrace seq
       (map str) (some (partial re-find #"clojure.main.repl"))))
(when (repl?)
  (-main))
 

src/shen/overwrite.clj

(ns shen
  (:refer-clojure :only [])
  (:use [shen.primitives])
  (:require [clojure.core :as core]))
(set '*language* "Clojure")
(set '*implementation* (core/str "Clojure " (core/clojure-version)
                                          " [jvm "(System/getProperty "java.version")"]"))
(set '*porters* "Håkan Råberg")
(set '*stinput* core/*in*)
(set '*home-directory* (System/getProperty "user.dir"))
(shen-initialise_environment)
(defun
 (intern "@p")
 (V706 V707)
 (core/object-array ['shen-tuple V706 V707]))
(defun
 variable?
 (V702)
 (and (core/symbol? V702) (Character/isUpperCase (.charAt (core/name V702) 0))))
(defun
 boolean?
 (V746)
 (core/condp = V746
             true true
             false true
             (intern "true") true
             (intern "false") true
             false))
(defun
  macroexpand
  (V510)
  (let
      Y
    (shen-compose (core/drop-while core/nil?
                                   (core/map #(core/when-let [m (core/ns-resolve 'shen %)] @m)
                                             (value '*macros*))) V510)
    (if (= V510 Y) V510 (shen-walk macroexpand Y))))

Based on Shen Mode by Eric Schulte. - Shen functions taken largely from the Qi documentation by Dr. Mark Tarver.

(def ^:private shen-doc
  `((* "number --> number --> number" "Number multiplication.")
    (+ "number --> number --> number" "Number addition.")
    (- "number --> number --> number" "Number subtraction.")
    (/ "number --> number --> number" "Number division.")
    (~(intern "/.") "_" "Abstraction builder, receives a variable and an expression; does the job of --> in the lambda calculus.")
    (< "number --> number --> boolean" "Less than.")
    (<-vector nil nil)
    (<= "number --> number --> boolean" "Less than or equal to.")
    (<e> nil nil)
    (= "A --> A --> boolean" "Equal to.")
    (== "A --> B --> boolean" "Equal to.")
    (> "number --> number --> boolean" "Greater than.")
    (>= "number --> number --> boolean" "Greater than or equal to.")
    (~(intern "@p") "_" "Takes two inputs and forms an ordered pair.")
    (~(intern "@s") "_" "Takes two or more inputs and forms a string.")
    (~(intern "@v") "_" "Takes two or more inputs and forms a vector.")
    (abort nil "throw a simple error")
    (adjoin nil "add arg1 to list arg2 if not already a member")
    (and "boolean --> boolean --> boolean" "Boolean and.")
    (append "(list A) --> (list A) --> (list A)" "Appends two lists into one list.")
    (apply "(A --> B) --> (A --> B)" "Applies a function to an input.")
    (arity nil nil)
    (assoc nil nil)
    (assoc-type "symbol --> variable --> symbol" "Associates a Qi type (first input) with  Lisp type (second input)..")
    (average nil "return the average of two numbers")
    (bind nil nil)
    (boolean? "A --> boolean" "Recognisor for booleans.")
    (bound? nil "check is a symbol is bound")
    (byte->string nil "return the string represented by bytes")
    (call nil nil)
    (cd "string --> string" "Changes the home directory. (cd \"My Programs\") will cause (load \"hello_world.txt\") to load MyPrograms/hello_world.txt.   (cd \"\")  is the default.")
    (character? "A --> boolean" "Recognisor for characters.")
    (compile nil nil)
    (complex? "A --> boolean" "Recognisor for complex numbers.")
    (concat "symbol --> symbol --> symbol" "Concatenates two symbols.")
    (congruent? "A --> A --> boolean" "Retrns true if objects are identical or else if they are strings or characters which are identical differing at most in case or numbers of equal value (e.g. 1 and 1.0) or tuples composed of congruent elements.")
    (cons "_" "A special form that takes an object e of type A and a list l of type (list A) and produces a list of type (list A) by adding e to the front of  l.")
    (cons? "--> boolean" "Returns true iff the input is a non-empty list.")
    (core nil nil)
    (cut nil nil)
    (debug "A --> string" "The input is ignored and debugging is returned; but all terminal output is echoed to the file debug.txt until the undebug function is executed.")
    (declare "_" "Takes a function name f and a type t expressed as a list and gives f the type t.")
    (define "_" "Define a function, takes a name, an optional type and a pattern matching body.")
    (delete-file "string --> string" "The file named in the string is deleted and the string returned.")
    (destroy "_" "Receives the name of a function and removes it and its type from the environment.")
    (difference "(list A) --> (list A) --> (list A)" "Subtracts the elements of the second list from the first")
    (do "_" "A special form: receives n well-typed expressions and evaluates each one, returning the normal form of the last one.")
    (dump "string --> string" "Dumps all user-generated Lisp from the file f denoted by the argument into a file f.lsp.")
    (echo "string --> string" "Echoes all terminal input/output to a file named by string (which is either appended to if it exists or created if not) until the command (echo \"\") is received which switches echo off.")
    (element? "A -> (list A) --> boolean" "Returns true iff the first input is an element in the second.")
    (empty? "--> boolean" "Returns true iff the input is [].")
    (error "_" "A special form: takes a string followed by n (n --> 0) expressions. Prints error string.")
    (eval "_" "Evaluates the input.")
    (explode "A --> (list character)" "Explodes an object to a list of characters.")
    (fail nil nil)
    (fix "(A --> A) --> (A --> A)" "Applies a function to generate a fixpoint.")
    (float? "A --> boolean" "Recognisor for floating point numbers.")
    (floor nil nil)
    (format nil "takes a stream, a format string and args, formats and prints to the stream")
    (freeze "A --> (lazy A)" "Returns a frozen version of its input.")
    (fst "(A * B) --> A" "Returns the first element of a tuple.")
    (fwhen nil nil)
    (gensym "_" "Generates a fresh symbol or variable from a string..")
    (get nil "gets property arg2 from object arg1")
    (get-array "(array A) --> (list number) --> A --> A" "3-place function that takes an array of elements of type A, an index to that array as a list of natural numbers and an expression E of type A.  If an object is stored at the index, then it is returned, otherwise the normal form of E is returned.")
    (get-prop "_" "3-place function that takes a symbol S, a pointer P (which can be a string, symbol or number), and an expression E of any kind and returns the value pointed by P from S  (if one exists) or the normal form of E otherwise.")
    (hash nil "hash an object")
    (hdv nil nil)
    (head "(list A) --> A" "Returns the first element of a list.")
    (identical nil nil)
    (if "boolean --> A --> A" "takes a boolean b and two expressions x and y and evaluates x if b evaluates to true and evaluates y if b evaluates to false.")
    (if-with-checking "string --> (list A)" "If type checking is enabled, raises the string as an error otherwise returns the empty list..")
    (if-without-checking "string --> (list A)" "If type checking is disabled, raises the string as an error otherwise returns the empty list.")
    (include "(list symbol) --> (list symbol)" "Includes the datatype theories or synonyms for use in type checking.")
    (include-all-but "(list symbol) --> (list symbol)" "Includes all loaded datatype theories and synonyms for use in type checking apart from those entered.")
    (inferences "A --> number" "The input is ignored. Returns the number of logical inferences executed since the last call to the top level.")
    (input "_" "0-place function. Takes a user input i and returns the normal form of i.")
    (input+ "_" "Special form. Takes inputs of the form : <expr>. Where d(<expr>) is the type denoted by the choice of expression (e.g. \"number\" denotes the type number). Takes a user input i and returns the normal form of i given i is of the type d(<expr>).")
    (integer? "A --> boolean" "Recognisor for integers.")
    (interror nil nil)
    (intersection "(list A) --> (list A) --> (list A)" "Computes the intersection of two lists.")
    (intmake-string nil nil)
    (intoutput nil nil)
    (lambda "_" "Lambda operator from lambda calculus.")
    (length "(list A) --> integer" "Returns the number of elements in a list.")
    (let nil nil)
    (limit nil nil)
    (lineread "_" "Top level reader of read-evaluate-print loop. Reads elements into a list.  lineread terminates with carriage return when brackets are balanced.  ^ aborts lineread.")
    (list "A .. A --> (list A)" "A special form. Assembles n (n  --> 0) inputs into a list.")
    (load "string --> symbol" "Takes a file name and loads the file, returning loaded as a symbol.")
    (macroexpand nil nil)
    (make-string "string A1 - An --> string" "A special form: takes a string followed by n (n --> 0) well-typed expressions; assembles and returns a string.")
    (map "(A --> B) --> (list A) --> (list B)" "The first input is applied to each member of the second input and the results consed into one list..")
    (mapcan "(A --> (list B)) --> (list A) --> (list B)" "The first input is applied to each member of the second input and the results appended into one list.")
    (maxinferences "number --> number" "Returns the input and as a side-effect, sets a global variable to a number that limits the maximum number of inferences that can be expended on attempting to typecheck a program.  The default is 1,000,000.")
    (mod nil "arg1 mod arg2")
    (newsym "symbol --> symbol" "Generates a fresh symbol from a symbol.")
    (newvar "variable --> variable" "Generates a fresh variable from a variable")
    (nl nil nil)
    (not "boolean --> boolean" "Boolean not.")
    (nth "number --> (list A) --> A" "Gets the nth element of a list numbered from 1.")
    (number? "A --> boolean" "Recognisor for numbers.")
    (occurences "A --> B --> number" "Returns the number of times the first argument occurs in the second.")
    (occurrences nil "returns the number of occurrences of arg1 in arg2")
    (occurs-check "symbol --> boolean" "Receives either + or - and enables/disables occur checking in Prolog,    datatype definitions and rule closures.   The default is +.")
    (opaque "symbol --> symbol" "Applied to a Lisp macro makes it opaque to Qi.")
    (or "boolean -->  (boolean --> boolean)" "Boolean or.")
    (output "string A1 - An --> string" "A special form: takes a string followed by n (n --> 0) well-typed expressions; prints a message to the screen and returns an object of type string (the string \"done\").")
    (preclude "(list symbol) --> (list symbol)" "Removes the mentioned datatype theories and synonyms from use in type checking.")
    (preclude-all-but "(list symbol) --> (list symbol)" "Removes all the datatype theories and synonyms from use in type checking apart from the ones given.")
    (print "A --> A" "Takes an object and prints it, returning it as a result.")
    (profile "(A --> B) --> (A --> B)" "Takes a function represented by a function name and inserts profiling code returning the function as an output.")
    (profile-results "A --> symbol" "The input is ignored.  Returns a list of profiled functions and their     timings since  profile-results was last used.")
    (ps "_" "Receives a symbol  denoting a Qi function and prints the Lisp source    code associated with the function.")
    (put nil "puts value of arg3 as property arg2 in object arg1")
    (put-array "(array A) --> (list number) --> A --> A" "3-place function that takes an array of elements of type A, an index to that array as a list of natural numbers and an expression E of type A.  The normal form of E is stored at that index and then returned.")
    (put-prop "_" "3-place function that takes a symbol S, a pointer P (a string symbol or number), and an expression E. The pointer P is set to point from S to the normal form of E which is then returned.")
    (quit "_" "0-place function that exits Qi.")
    (random "number --> number" "Given a positive number n, generates a random number between 0 and    n-1.")
    (rational? "A --> boolean" "Recognisor for rational numbers.")
    (read nil nil)
    (read-char "A --> character" "The input is discarded and the character typed by the user is returned.")
    (read-chars-as-stringlist "(list character) --> (character -->  boolean) -->  (list string)" "Returns a list of strings whose components are taken from the character list. The second input acts as a tokeniser.  Thus (read-chars-as-stringlist [#\\H #\\i #\\Space #\\P #\\a #\\t]  (/. X (= X #\\Space))) will produce [\"Hi\" \"Pat\"].")
    (read-file "string --> (list unit)" "Returns the contents of an ASCII file designated by a string.  Returns a list of units,  where unit is an unspecified type.")
    (read-file-as-charlist "string --> (list character)" "Returns the list of characters from the contents of an ASCII file designated by a string.")
    (read-file-as-string nil nil)
    (real? "A --> boolean" "Recognisor for real numbers.")
    (remove "A --> (list A) --> (list A)" "Removes all occurrences of an element from a list.")
    (return nil nil)
    (reverse "(list A)--> ?(list A)" "Reverses a list.")
    (round "number--> ?number" "Rounds a number.")
    (save "_" "0 place function. Saves a Qi image.")
    (snd "(A * B) --> B" "Returns the second element of a tuple.")
    (specialise "symbol --> symbol" "Receives the name of a function and turns it into a special form. Special forms are not curried during evaluation or compilation.")
    (speed "number --> number" "Receives a value 0 to 3 and sets the performance of the generated Lisp code, returning its input.  0 is the lowest setting.")
    (spy "symbol --> boolean" "Receives either + or - and respectively enables/disables tracing the    operation of T*.")
    (sqrt "number --> number" "Returns the square root of a number.")
    (step "symbol --> boolean" "Receives either + or - and enables/disables stepping in the trace.")
    (stinput nil nil)
    (string? "A --> boolean" "Recognisor for strings.")
    (strong-warning "symbol --> boolean" "Takes + or -; if + then warnings are treated as error messages.")
    (subst nil nil)
    (sugar "symbol --> (A --> B) --> number --> (A --> B)" "Receives either in or out as first argument, a function f and an integer    greater than 0 and returns f as a result.  The function f is placed on the    sugaring list at a position determined by the number.")
    (sugar-list "symbol --> (list symbol)" "Receives either in or out as first argument, and returns the list of sugar    functions.")
    (sum nil "sum a list of numbers")
    (symbol? "A --> boolean" "Recognisor for symbols.")
    (systemf nil nil)
    (tail "(list A) --> (list A)" "Returns all but the first element of a non-empty list.")
    (tc "symbol --> boolean" "Receives either + or - and respectively enables/disables static typing.")
    (tc? nil "return true if type checking")
    (thaw "(lazy A) --> A" "Receives a frozen input and evaluates it to get the unthawed result..")
    (time "A --> A" "Prints the run time for the evaluation of its input and returns its normal form.")
    (tlv nil nil)
    (track "symbol --> symbol" "Tracks the I/O behaviour of a function.")
    (transparent "symbol --> symbol" "Applied to a Lisp macro makes it transparent to Qi.")
    (tuple? "A --> boolean" "Recognisor for tuples.")
    (type "_" "Returns a type for its input (if any) or false if the input has no type.")
    (unassoc-type "symbol --> symbol" "Removes any associations with the Qi type in the type association table.")
    (undebug "A --> string" "The input is ignored, undebugging is returned and all terminal output is closed to the file debug.txt.")
    (unify nil nil)
    (unify! nil nil)
    (union "(list A) --> (list A) --> (list A)" "Forms the union of two lists.")
    (unprofile "(A --> B) --> (A --> B)" "Unprofiles a function.")
    (unspecialise "symbol --> symbol" "Receives the name of a function and deletes its special form status.")
    (unsugar "symbol --> (A --> B) --> (A --> B)" "Receives either out or in and the name of a function and removes its status as a sugar function.")
    (untrack "symbol --> symbol" "Untracks a function.")
    (value "_" "Applied to a symbol, returns the global value assigned to it.")
    (variable? "A --> boolean" "Applied to a variable, returns true.")
    (vector nil nil)
    (vector-> nil nil)
    (vector? nil nil)
    (version "string --> string" "Changes the version string displayed on startup.")
    (warn "string --> string" "Prints the string as a warning and returns \"done\".  See strong-warning")
    (write-to-file "string --> A --> string" "Writes the second input into a file named in the first input. If the file does not exist, it is created, else it is overwritten. If the second input is a string then it is written to the file without the enclosing quotes.  The first input is returned.")
    (y-or-n? "string --> boolean" "Prints the string as a question and returns true for y and false for n.")))
(core/doseq [[fn sig doc] shen-doc
             :let [v (core/resolve fn)]
             :when v]
            (core/alter-meta! v core/merge {:doc doc
                                            :arglists (core/list (core/read-string (core/str "[" sig "]")))}))
 
(ns shen.primitives
  (:require [clojure.core :as core]
            [clojure.set :as set]
            [clojure.string :as string]
            [clojure.walk :as walk]
            [clojure.java.io :as io])
  (:refer-clojure :exclude [set intern let pr type cond cons str number? string? defmacro
                            + - * / > < >= <= = and or])
  (:import [java.io Reader Writer InputStream OutputStream PrintWriter OutputStreamWriter]
           [java.util Arrays])
  (:gen-class))
(create-ns 'shen.globals)
(def string? core/string?)
(def number? core/number?)
(core/defmacro and
  ([x] `(fn [y#] (core/and ~x y#)))
  ([x & xs] `(core/and ~x ~@xs)))
(core/defmacro or
  ([x] `(fn [y#] (core/or ~x y#)))
  ([x & xs] `(core/or ~x ~@xs)))
(defn ^:private and-fn
  ([x] (and x))
  ([x y] (and x y)))
(defn ^:private or-fn
  ([x] (or x))
  ([x y] (or x y)))
(defn ^:private partials [name parameters]
  (for [p (map #(take % parameters) (range 1 (count parameters)))]
    `(~(vec p) (partial ~name ~@p))))
(core/defmacro defun [F X & Y]
  (core/let [F (if (seq? F) (eval F) F)]
            `(do
               (defn ^:dynamic ~F
                 ~@(partials F X)
                 (~(vec X) ~@Y))
               ~F)))
(def ^:private array-class (Class/forName "[Ljava.lang.Object;"))
(defn =
  ([X] (partial = X))
  ([X Y]
     (core/cond
      (and (identical? array-class (class X))
           (identical? array-class (class Y))) (Arrays/equals #^"[Ljava.lang.Object;" X
                                                              #^"[Ljava.lang.Object;" Y)
           (and (number? X) (number? Y)) (== X Y)
           :else (core/= X Y))))
(defn /
  ([X] (partial / X))
  ([X Y] (core/let [r (clojure.core// X Y)]
                   (if (ratio? r) (double r) r))))
(defn ^:private alias-op [op real-op]
  (eval `(defun ~op ~'[X Y] (~real-op ~'X ~'Y))))
(doseq [op '[+ - *]]
  (alias-op op (symbol "clojure.core" (core/str (name op) "'"))))
(doseq [op '[> < >= <=]]
  (alias-op op (symbol "clojure.core" (name op))))
(defn ^:private interned? [X]
  (and (seq? X) (= 'intern (first X))))
(def ^:private slash-dot (symbol "/."))
(defn ^:private recur?
  ([path] (partial recur? path))
  ([path fn]
     (or (= 'cond (last (drop-last path)))
         (set/superset? '#{defun cond if do let}
                        (core/set path)))))
(defn ^:private maybe-apply [kl path]
  (if (= 'cond (last path)) kl
      (list 'function kl)))
(defn shen-kl-to-clj
  ([kl] (shen-kl-to-clj kl #{} [] :unknown))
  ([kl scope] (shen-kl-to-clj kl scope [] :no-recur))
  ([kl scope path fn]
     (condp some [kl]
       scope kl
       symbol? (condp = (name kl)
                 "true" true
                 "false" false
                 (list 'quote kl))
       seq? (core/let [[fst snd trd & rst] kl
                       fn (if ('#{defun} fst) snd
                              fn)
                       scope (condp get fst
                               '#{defun} (into scope trd)
                               '#{let lambda} (conj scope snd)
                               scope)
                       fst (condp some [fst]
                             (every-pred
                              #{fn}
                              (recur? path)) 'recur
                             (some-fn
                              interned?
                              scope) (maybe-apply fst path)
                              seq? (maybe-apply (shen-kl-to-clj fst scope) path)
                              (if (= 'cond (last path))
                                (shen-kl-to-clj fst scope)
                                fst))
                       path (conj path fst)
                       snd (condp get fst
                             '#{defun let lambda} snd
                             '#{if} (shen-kl-to-clj snd scope)
                             (shen-kl-to-clj snd scope path fn))
                       trd (condp get fst
                             '#{defun} trd
                             '#{let} (shen-kl-to-clj trd scope)
                             (shen-kl-to-clj trd scope path fn))]
                      (take-while (complement nil?)
                                  (concat [fst snd trd]
                                          (map #(shen-kl-to-clj % scope path fn) rst))))
       kl)))
(defn intern [String]
  (symbol (condp = String
            "/" "/"
            "/." slash-dot
            (string/replace String "/" "-slash-"))))
(core/defmacro cond [& CS]
  `(core/cond ~@(apply concat CS)))
(defn set* [X Y ns]
  @(core/intern (the-ns ns)
                (with-meta X {:dynamic true :declared true})
                Y))
(defn set
  ([X] (partial set X))
  ([X Y] (set* X Y 'shen.globals)))
(defn ^:private value* [X ns]
  (core/let [v (and (symbol? X) (ns-resolve ns X))]
            (condp = X
              'and and-fn
              'or or-fn
              @v)))
(defn value [X] (value* X 'shen.globals))
(defn function [fn]
  (if (fn? fn) fn
      (value* fn 'shen)))
(defn simple-error [String]
  (throw (RuntimeException. ^String String)))
(core/defmacro trap-error [X F]
  `(try
     ~X
     (catch Exception e#
       (~F e#))))
(defn error-to-string [E]
  (if (instance? Throwable E)
    (or (.getMessage ^Throwable E) (core/str E))
    (throw (IllegalArgumentException. ^String (core/str E " is not an exception")))))
(defn ^:private pair [X Y] [X Y])
(defn ^:private pair? [X]
  (and (vector? X) (= 2 (count X))))
(defn cons [X Y]
  (if (and (coll? Y)
           (not (pair? Y)))
    (core/cons X Y)
    (pair X Y)))
(defn hd [X] (first X))
(defn tl [X]
  (if (pair? X)
    (second X)
    (rest X)))
(defn fail! [] (assert false))
(defn cons? [X]
  (and (coll? X) (not (empty? X))))
(defn str [X]
  (if-not (coll? X) (core/pr-str X)
          (throw (IllegalArgumentException.
                  (core/str X " is not an atom; str cannot convert it to a string.")))))
(defn ^:private vec-to-cons [[fst & rst]]
  (if fst (list 'cons fst (vec-to-cons rst))
      ()))
(defn ^:private cleanup-clj [clj]
  (condp some [clj]
    vector? (recur (vec-to-cons clj))
    coll? (if ('#{clojure.core/deref} (first clj))
            (symbol (core/str "@" (second clj)))
            clj)
    '#{λ} slash-dot
    char? (intern clj)
    clj))
(defn ^:private define* [name body]
  (core/let [kl ((function 'shen-shen->kl) name body)]
            (binding [*ns* (the-ns 'shen)]
              ((function 'eval) kl)
              name)))
(defn ^:private shen-elim-define [X]
  (if (seq? X)
    (if ('#{define} (first X)) (define* (second X) (drop 2 X))
        (map shen-elim-define X))
    X))
(defn eval-shen* [body]
  (core/let [body (walk/postwalk cleanup-clj body)]
            (binding [*ns* (the-ns 'shen)]
              (->> body
                   (map (function 'eval))
                   last))))
(core/defmacro eval-shen [& body]
  `(eval-shen* '~body))
(core/defmacro 神 [& body]
  `(eval-shen ~@body))
(core/defmacro define [name & body]
  `(core/let [fn# (eval-shen ~(concat ['define name] body))]
             (defn ~(with-meta name {:dynamic true})
               [& ~'args] (apply (function fn#) ~'args))))
(doseq [[name args] '{defmacro [name] defprolog [name] prolog? [] package [name exceptions]}]
  (eval
   `(core/defmacro ~name [~@args & ~'body]
      `(eval-shen ~(concat ['~name ~@args] ~'body)))))
(def ^:private missing-symbol-pattern #"Unable to resolve symbol: (.+) in this context")
(defn ^:private missing-symbol [s]
  (when-let [[_ sym] (re-find missing-symbol-pattern (or s ))] sym))
(defn ^:private fn-to-symbol [fn]
  (-> fn class .getName
      (string/replace "_" "-")
      (string/split #"\$")
      last symbol))
(defn ^:private cleanup-return [x]
  (or (when (fn? x)
        (core/let [name (fn-to-symbol x)]
                  (when (fn? (ns-resolve 'shen name)) name)))
      x))
(defn ^:private eval-and-declare-missing [kl]
  (binding [*ns* (the-ns 'shen)]
    (try
      (cleanup-return (eval kl))
      (catch RuntimeException e
        (if-let [s (missing-symbol (.getMessage e))]
          (do
            (set* (symbol s) nil 'shen)
            (eval-and-declare-missing kl))
          (throw e))))))
(defn eval-without-macros [X]
  (core/let [kl (shen-kl-to-clj (shen-elim-define (cleanup-clj X)))]
            (eval-and-declare-missing kl)))
(core/defmacro lambda [X Y]
  `(fn [~X & XS#] (core/let [result# ~Y]
                            (if XS# (apply result# XS#)
                                result#))))
(core/defmacro λ [X Y]
  `(lambda ~X ~Y))
(core/defmacro let [X Y Z]
  (core/let [X-safe (if (seq? X) (gensym (eval X)) X)
             Z (if (seq? X) (walk/postwalk
                             #(if (= X %) X-safe %) Z) Z)]
            `(core/let [~X-safe ~Y]
                       ~Z)))
(core/defmacro freeze [X]
 `(fn [] ~X))
(defn thaw [X] (X))
(defn absvector [N]
  (doto (object-array (int N)) (Arrays/fill 'fail!)))
(defn absvector? [X]
  (identical? array-class (core/class X)))
(defn <-address [#^"[Ljava.lang.Object;" Vector N]
  (aget Vector (int N)))
(defn address-> [#^"[Ljava.lang.Object;" Vector N Value]
  (aset Vector (int N) Value)
  Vector)
(defn n->string [N]
  (core/str (char N)))
(defn string->n [S]
  (core/int (first S)))
(def byte->string n->string)
(defmulti pr (fn [_ S] (class S)))
(defmethod pr Reader [X ^Reader S]
  (if (= *in* S)
    (pr X *out*)
    (throw (IllegalArgumentException. (str S)))))
(defmethod pr OutputStream [X ^OutputStream S]
  (pr X (OutputStreamWriter. S)))
(defmethod pr Writer [X ^Writer S]
  (binding [*out* S]
    (print X)
    (flush)
    X))
(defmulti read-byte class)
(defmethod read-byte InputStream [^InputStream S]
  (.read S))
(defmethod read-byte Reader [^Reader S]
  (.read S))
(defn open [Type String Direction]
  (condp = Type
    'file
    (core/let [Path (io/file (value '*home-directory*) String)]
              (condp = Direction
                'in (io/input-stream Path)
                'out (io/output-stream Path)
                (throw (IllegalArgumentException. "invalid direction"))))
    (throw (IllegalArgumentException. "invalid stream type"))))
(defn type [X MyType]
  (cast MyType X))
(defn close [^java.io.Closeable Stream]
  (.close Stream))
(defn pos [X N]
  (core/str (get X N)))
(defn tlstr [X]
  (subs X 1))
(defn cn
  ([Str1] (partial cn Str1))
  ([Str1 Str2]
     (core/let [strings (replace {() } [Str1 Str2])]
               (when-let [no-string (first (remove string? strings))]
                 (throw (IllegalArgumentException. (core/str no-string " is not a string"))))
               (apply core/str strings))))
(def ^:private internal-start-time (System/currentTimeMillis))
(defn get-time [Time]
  (if (= Time 'run)
    (/ (- (System/currentTimeMillis) internal-start-time) 1000)
    (throw (IllegalArgumentException.
            (core/str "get-time does not understand the parameter " Time)))))
(defmethod print-method array-class [o ^Writer w]
  (print-method (vec o) w))
(defn ^:private read-bytes [s]
  ((function (intern "@p")) (map int s) ()))
(defn parse-shen [s]
  (core/let [<st_input> (function 'shen-<st_input>)
             snd (function 'snd)]
            (-> s read-bytes <st_input> snd)))
(defn parse-and-eval-shen [s]
  (eval-shen* (parse-shen s)))
(defn reset-macros! []
  (set '*macros* (filter #(re-find #"shen-" (name %)) (value '*macros*))))
(defn exit
  ([] (exit 0))
  ([status] (System/exit status)))