├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── pom.xml └── src ├── main ├── clojure │ └── mikera │ │ └── cljutils │ │ ├── arrays.clj │ │ ├── bytes.clj │ │ ├── concurrent.clj │ │ ├── core.clj │ │ ├── error.clj │ │ ├── expression.clj │ │ ├── find.clj │ │ ├── hex.clj │ │ ├── logic.clj │ │ ├── loops.clj │ │ ├── macros.clj │ │ ├── mouse.clj │ │ ├── namespace.clj │ │ ├── reader.clj │ │ ├── text.clj │ │ └── vectors.clj └── java │ └── mikera │ └── cljutils │ ├── Clojure.java │ ├── Error.java │ └── FastSeq.java └── test ├── clojure └── mikera │ └── cljutils │ ├── benchmarks.clj │ ├── dummy.clj │ ├── import.clj │ ├── pulled.clj │ ├── test.txt │ ├── test_arrays.clj │ ├── test_bytes.clj │ ├── test_concurrent.clj │ ├── test_core.clj │ ├── test_error.clj │ ├── test_expression.clj │ ├── test_find.clj │ ├── test_hex.clj │ ├── test_logic.clj │ ├── test_loops.clj │ ├── test_macros.clj │ ├── test_namespace.clj │ ├── test_namespace_pulling.clj │ ├── test_reader.clj │ ├── test_text.clj │ └── test_vectors.clj └── java └── mikera └── cljutils ├── AllClojureTests.java ├── DemoApp.java └── TestClojure.java /.gitignore: -------------------------------------------------------------------------------- 1 | /.project 2 | /.settings 3 | /target 4 | /.classpath 5 | /classes 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk7 4 | - openjdk7 5 | - oraclejdk8 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | clojure-utils 2 | ============= 3 | 4 | A library of various small but handy Clojure utility functions 5 | 6 | Of particular interest: 7 | 8 | - **Clojure.java** - Java class of utility functions for calling Clojure from Java 9 | - **arrays.clj** - manipulating Java arrays 10 | - **core.clj** - handy functions that should have been in clojure.core :-) 11 | - **error.clj** - handling errors and exceptions 12 | - **expression.clj** - analysing Clojure expressions 13 | - **find.clj** - searching for items within different types of collection 14 | - **logic.clj** - extra logic functions / macros (e.g. xor, nand) 15 | - **loops.clj** - extra looping constructs and macros 16 | - **macros.clj** - some handy macros 17 | - **namespace.clj** - for merging / managing namespaces 18 | - **test.clj** - tools for handling text strings 19 | - **vectors.clj** - useful functions for working with Clojure persistent vectors 20 | 21 | 22 | ### Clojure calling example 23 | 24 | Perhaps the most useful feature in `clojure-utils` is a set of utility functions 25 | designed to allow easy calling of Clojure code from Java. These are contained 26 | in the `mikera.cljutils.Clojure` class. Usage example: 27 | 28 | ```java 29 | import mikera.cljutils.Clojure; 30 | 31 | public class Demo { 32 | public static void main(String [] args) { 33 | String s = "(+ 1 2)"; 34 | System.out.println("Evaluating Clojure code: "+s); 35 | 36 | Object result = Clojure.eval(s); 37 | System.out.println("=> "+ result); 38 | } 39 | } 40 | ``` 41 | 42 | 43 | ### License 44 | 45 | All the code I have written in clojure-utils is licensed under the LGPL v3. 46 | 47 | - http://www.gnu.org/copyleft/lesser.html 48 | 49 | Exceptions apply to some specific code which has been incorporated from elsewhere under permissive licenses 50 | that allow this: see the source files for more information -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | clojure-utils 4 | net.mikera 5 | 0.8.1-SNAPSHOT 6 | General purpose utility functions for Clojure 7 | 8 | 9 | 10 | GNU Lesser General Public License (LGPL) 11 | http://www.gnu.org/licenses/lgpl.txt 12 | 13 | 14 | 15 | 16 | net.mikera 17 | clojure-pom 18 | 0.6.0 19 | 20 | 21 | 22 | 23 | net.mikera 24 | cljunit 25 | 0.6.0 26 | test 27 | 28 | 29 | org.clojure 30 | clojure 31 | 1.8.0 32 | 33 | 34 | criterium 35 | criterium 36 | 0.4.4 37 | test 38 | 39 | 40 | 41 | 42 | 43 | clojars.org 44 | Clojars repository 45 | https://clojars.org/repo 46 | 47 | 48 | 49 | 50 | scm:git:git@github.com:mikera/${project.artifactId}.git 51 | scm:git:git@github.com:mikera/${project.artifactId}.git 52 | scm:git:git@github.com:mikera/${project.artifactId}.git 53 | HEAD 54 | 55 | -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/arrays.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.arrays 2 | "Utility functions for dealing with Java arrays" 3 | (:use mikera.cljutils.loops) 4 | (:import java.lang.reflect.Array) 5 | (:refer-clojure :exclude [aget])) 6 | 7 | (set! *warn-on-reflection* true) 8 | (set! *unchecked-math* :warn-on-boxed) 9 | 10 | (defn array? 11 | "Returns true if the argument is a Java array, false otherwise" 12 | ([a] 13 | (and a (.isArray (.getClass ^Object a))))) 14 | 15 | (defn aget* 16 | "Returns an element from a Java array, avoiding the usual reflection warning." 17 | ([a index] 18 | (Array/get a (int index)))) 19 | 20 | (defn array-type 21 | "Returns the type of elements in a Java array. Returns nil if the argument is not an array." 22 | ([a] 23 | (.getComponentType (.getClass ^Object a)))) 24 | 25 | (defn typed-array 26 | "Creates a typed Java array of a collection of objects. 27 | 28 | If class is not provided, uses the class of the first object to determine the type of the array." 29 | ([^Class class objects] 30 | (let [cnt (count objects) 31 | ^objects arr (make-array class cnt)] 32 | (doseq-indexed [o objects i] 33 | (aset arr (int i) o)) 34 | arr)) 35 | ([objects] 36 | (typed-array (.getClass ^Object (first objects)) objects))) 37 | 38 | (defn copy-long-array 39 | "Returns a copy of a long array" 40 | (^longs [^longs arr] 41 | (java.util.Arrays/copyOf arr (int (alength arr))))) 42 | 43 | (defn long-range 44 | "Returns a range of longs in a long[] array" 45 | (^longs [end] 46 | (let [end (int end) 47 | ^longs arr (long-array end)] 48 | (dotimes [i end] 49 | (aset arr i (long i))) 50 | arr))) 51 | 52 | (defn long-array-of 53 | "Creates a long array with the specified values." 54 | (^longs [] (long-array 0)) 55 | (^longs [a] 56 | (let [arr (long-array 1)] 57 | (aset arr 0 (long a)) 58 | arr)) 59 | ([a b] 60 | (let [arr (long-array 2)] 61 | (aset arr 0 (long a)) 62 | (aset arr 1 (long b)) 63 | arr)) 64 | ([a b & more] 65 | (let [arr (long-array (+ 2 (count more)))] 66 | (aset arr 0 (long a)) 67 | (aset arr 1 (long b)) 68 | (doseq-indexed [x more i] (aset arr (+ 2 i) (long x))) 69 | arr))) 70 | 71 | (defn object-array-of 72 | "Creates a long array with the specified values." 73 | ([] (object-array 0)) 74 | ([a] 75 | (let [arr (object-array 1)] 76 | (aset arr 0 a) 77 | arr)) 78 | ([a b] 79 | (let [arr (object-array 2)] 80 | (aset arr 0 a) 81 | (aset arr 1 b) 82 | arr)) 83 | ([a b & more] 84 | (let [arr (object-array (+ 2 (count more)))] 85 | (aset arr 0 a) 86 | (aset arr 1 b) 87 | (doseq-indexed [x more i] (aset arr (+ 2 i) x)) 88 | arr))) 89 | -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/bytes.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.bytes 2 | "Utility functions fro working with byte arrays." 3 | (:refer-clojure :exclude [reverse]) 4 | (:import [java.util Arrays]) 5 | (:require [mikera.cljutils.hex :as hex]) 6 | (:require [clojure.string :as str])) 7 | 8 | (set! *warn-on-reflection* true) 9 | (set! *unchecked-math* :warn-on-boxed) 10 | 11 | (def BYTE-ARRAY-CLASS (Class/forName "[B")) 12 | 13 | (defn reverse 14 | (^bytes [^bytes bs] 15 | (let [n (alength bs) 16 | res (byte-array n)] 17 | (dotimes [i n] 18 | (aset res i (aget bs (- n (inc i))))) 19 | res))) 20 | 21 | (defn join 22 | "Concatenates two byte arrays" 23 | (^bytes [^bytes a ^bytes b] 24 | (let [al (int (alength a)) 25 | bl (int (alength b)) 26 | n (int (+ al bl)) 27 | ^bytes res (byte-array n)] 28 | (System/arraycopy a (int 0) res (int 0) al) 29 | (System/arraycopy b (int 0) res (int al) bl) 30 | res))) 31 | 32 | (defn slice 33 | "Slices a byte array with a given start and length" 34 | (^bytes [^bytes a ^long start] 35 | (slice a start (- (alength ^bytes a) start))) 36 | (^bytes [^bytes a ^long start ^long length] 37 | (let [al (int (alength ^bytes a)) 38 | ^bytes res (byte-array length)] 39 | (System/arraycopy a (int start) res (int 0) length) 40 | res))) 41 | 42 | (defn to-hex-string 43 | "Converts a byte array to a string representation , with space as a default separator." 44 | ([^bytes bs] 45 | (to-hex-string bs " ")) 46 | ([^bytes bs separator] 47 | (str/join separator (map #(hex/hex-string-from-byte %) bs)))) 48 | 49 | (defn unchecked-byte-array 50 | "Like clojure.core/byte-array but performs unchecked casts on sequence values." 51 | (^bytes [size-or-seq] 52 | (. clojure.lang.Numbers byte_array 53 | (if (number? size-or-seq) 54 | size-or-seq 55 | (map unchecked-byte size-or-seq )))) 56 | (^bytes [size init-val-or-seq] 57 | (. clojure.lang.Numbers byte_array size 58 | (if (sequential? init-val-or-seq) 59 | (map unchecked-byte init-val-or-seq ) 60 | init-val-or-seq)))) 61 | 62 | (defn bytes= 63 | "Compares two byte arrays for equality." 64 | ([^bytes a ^bytes b] 65 | (Arrays/equals a b))) -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/concurrent.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.concurrent) 2 | 3 | ;; Namespace for concurrency utils 4 | 5 | (set! *warn-on-reflection* true) 6 | (set! *unchecked-math* :warn-on-boxed) 7 | 8 | (defmacro plet [[s1 v1 & bindings] & body] 9 | "Equivalent of let that evaluates bindings in parallel, using a separate thread for each. 10 | 11 | The body is executed after all bindings are computed." 12 | (let [bindings (partition 2 bindings) 13 | syms (map first bindings) 14 | vals (map second bindings)] 15 | `(let [~@(mapcat (fn [var expr] `[~var (future ~expr)]) syms vals) 16 | ~s1 ~v1 17 | ~@(mapcat (fn [sym] `[~sym (deref ~sym)]) syms)] 18 | ~@body))) -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/core.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.core) 2 | 3 | (set! *warn-on-reflection* true) 4 | (set! *unchecked-math* :warn-on-boxed) 5 | 6 | (defn apply-kw 7 | "Applies a function to a set of arguments, where the last argument is a map of 8 | additional keyword arguments. 9 | 10 | Directly provided keywords will override those in the final argument map." 11 | [f & args] 12 | {:pre [(map? (last args))]} 13 | (if-let [kvs (seq (butlast args))] 14 | (apply f (apply concat (apply assoc (last args) kvs))) 15 | (apply f (apply concat (first args))))) 16 | 17 | -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/error.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.error) 2 | 3 | (set! *warn-on-reflection* true) 4 | (set! *unchecked-math* :warn-on-boxed) 5 | 6 | (defmacro error 7 | "Throws an error with the provided message(s). This is a macro in order to try and ensure the 8 | stack trace reports the error at the correct source line number." 9 | ([& messages] 10 | `(throw (mikera.cljutils.Error. (str ~@messages))))) 11 | 12 | (defmacro error? 13 | "Returns true if executing body throws an error, false otherwise." 14 | ([& body] 15 | `(try 16 | ~@body 17 | false 18 | (catch Throwable t# 19 | true)))) 20 | 21 | (defmacro TODO 22 | "Throws a TODO error. This ia a useful macro as it is easy to search for in source code, while 23 | also throwing an error at runtime if encountered." 24 | ([] 25 | `(TODO "Not yet implemented")) 26 | ([message] 27 | `(throw (java.lang.UnsupportedOperationException (str "TODO: " ~message))))) 28 | 29 | (defn stacktrace-str 30 | "Returns a string containing the full stacktrace of an exception" 31 | ([^Throwable e] 32 | (with-out-str (.printStackTrace e (java.io.PrintWriter. ^java.io.Writer *out*))))) 33 | 34 | (defmacro valid 35 | "Asserts that an expression is truthy, throws an error with the provided messages otherwise." 36 | ([body & msgs] 37 | `(or ~body 38 | (error "Validation error:" ~@msgs)))) 39 | 40 | (defmacro try-or 41 | "An exception-handling version of the 'or' macro. 42 | Trys expressions in sequence until one produces a result that is neither false nor an exception. 43 | Useful for providing a default value in the case of errors." 44 | ([exp & alternatives] 45 | (if-let [as (seq alternatives)] 46 | `(or (try ~exp (catch Throwable t# (try-or ~@as)))) 47 | exp))) -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/expression.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.expression 2 | (:import clojure.lang.Compiler)) 3 | 4 | (set! *warn-on-reflection* true) 5 | (set! *unchecked-math* :warn-on-boxed) 6 | 7 | (defmacro constant [body] 8 | "Evaluates a constant expression at compile time" 9 | (eval body)) 10 | 11 | ; Copyright (c) Chris Houser, Dec 2008. All rights reserved. 12 | ; The use and distribution terms for this software are covered by the 13 | ; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) 14 | ; which can be found in the file CPL.TXT at the root of this distribution. 15 | ; By using this software in any fashion, you are agreeing to be bound by 16 | ; the terms of this license. 17 | ; You must not remove this notice, or any other, from this software. 18 | 19 | (defn expression-info 20 | "Uses the Clojure compiler to analyze the given s-expr. Returns 21 | a map with keys :class and :primitive? indicating what the 22 | compiler concluded about the return value of the expression. 23 | Returns nil if no type info can be determined at compile-time. 24 | 25 | Example: (expression-info '(+ (int 5) (float 10))) 26 | Returns: {:class float, :primitive? true}" 27 | [expr] 28 | (let [^clojure.lang.Compiler$FnExpr fn-ast (Compiler/analyze clojure.lang.Compiler$C/EXPRESSION 29 | `(fn [] ~expr)) 30 | ^clojure.lang.Compiler$FnMethod meth (first (.methods fn-ast)) 31 | ^clojure.lang.Compiler$BodyExpr expr-ast (.body meth)] 32 | ;;(println (class fn-ast)) 33 | ;;(println (class meth)) 34 | ;;(println (class expr-ast)) 35 | (when (.hasJavaClass expr-ast) 36 | {:class (.getJavaClass expr-ast) 37 | :primitive? (.isPrimitive (.getJavaClass expr-ast))}))) -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/find.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.find 2 | "Namespace for utility functions that find values in collections." 3 | (:require [mikera.cljutils.arrays :as arrays]) 4 | (:refer-clojure :exclude [indexed?])) 5 | 6 | (set! *warn-on-reflection* true) 7 | (set! *unchecked-math* :warn-on-boxed) 8 | 9 | (defn indexed? 10 | "Returns true if the collection is Indexed (i.e. an instance of clojure.lang.Indexed). 11 | This function is identical to clojure.core/indexed? from Clojure 1.9 onwards, but maintained 12 | here for compatibility purposes" 13 | ([coll] 14 | (instance? clojure.lang.Indexed coll))) 15 | 16 | (defn find-first 17 | "Searches a collection and returns the first item for which pred is true, or nil if not found. 18 | 19 | An optional start position may be provided, which defaults to zero (starts searching from begining 20 | of the collection). 21 | 22 | Like 'clojure.core/some', except it returns the value from the collection (rather than the result of 23 | applying the predicate to the value). This is often more useful. 24 | 25 | Note that it is possible to find and return a nil value if the collection contains nils." 26 | ([pred coll] 27 | (find-first pred coll 0)) 28 | ([pred coll ^long start] 29 | (cond 30 | (indexed? coll) 31 | (let [c (count coll) 32 | ^clojure.lang.Indexed icoll coll] 33 | (loop [i start] 34 | (if (< i c) 35 | (let [v (.nth icoll i)] 36 | (if (pred v) v (recur (inc i)))) 37 | nil))) 38 | (arrays/array? coll) 39 | (let [c (count coll)] 40 | (loop [i start] 41 | (if (< i c) 42 | (let [v (arrays/aget* coll (int i))] 43 | (if (pred v) v (recur (inc i)))) 44 | nil))) 45 | :else ;; default to treating as sequence 46 | (loop [s (drop start coll)] 47 | (when s 48 | (let [v (first s)] 49 | (if (pred v) 50 | v 51 | (recur (next s))))))))) 52 | 53 | (defn find-index 54 | "Searches a collection and returns the index of the first item for which pred is true. 55 | An optional start position may be provided (defaults to 0) 56 | 57 | Returns nil if not found" 58 | ([pred coll] 59 | (find-index pred coll 0)) 60 | ([pred coll ^long start] 61 | (if (indexed? coll) 62 | (let [c (count coll)] 63 | (loop [i start] 64 | (if (< i c) 65 | (if (pred (nth coll i)) i (recur (inc i))) 66 | nil))) 67 | (loop [i start s (drop start coll)] 68 | (when s 69 | (if (pred (first s)) i (recur (inc i) (next s)))))))) 70 | 71 | (defn find-position 72 | "Searches a collection and returns the (long) index of the item's position. 73 | An optional start position may be provided (defaults to 0) 74 | 75 | Returns nil if not found" 76 | ([item coll] 77 | (find-position item coll 0)) 78 | ([item coll ^long start] 79 | (cond 80 | (indexed? coll) 81 | (let [c (count coll) 82 | ^clojure.lang.Indexed icoll coll] 83 | (loop [i start] 84 | (if (< i c) 85 | (let [v (.nth icoll i)] 86 | (if (= item v) i (recur (inc i)))) 87 | nil))) 88 | (arrays/array? coll) 89 | (let [c (count coll)] 90 | (loop [i start] 91 | (if (< i c) 92 | (let [v (arrays/aget* coll (int i))] 93 | (if (= item v) i (recur (inc i)))) 94 | nil))) 95 | :else ;; default to treating as sequence 96 | (loop [i start 97 | s (seq (drop start coll))] 98 | (when s 99 | (let [v (first s)] 100 | (if (= item v) 101 | i 102 | (recur (inc i) (next s))))))))) 103 | 104 | (defn eager-filter 105 | "Filters a collection eagerly, returning a sequence. 106 | Like clojure.core/filterv except the result is an ISeq (or nil if empty)" 107 | ([pred coll] 108 | (seq (filterv pred coll)))) 109 | -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/hex.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.hex 2 | "Utilities for handling hexadecimal strings." 3 | (:require [clojure.string :as str]) 4 | (:require [mikera.cljutils.text :as text])) 5 | 6 | (set! *warn-on-reflection* true) 7 | (set! *unchecked-math* :warn-on-boxed) 8 | 9 | (defn hex-string 10 | "Converts an an integer value to a hexadecimal string representing the unsigned value. 11 | The length of the output depends on the value of the integer." 12 | ([n] 13 | (cond 14 | (instance? Long n) (Long/toHexString (unchecked-long n)) 15 | (instance? Integer n) (java.lang.Integer/toHexString (unchecked-int n)) 16 | (instance? Character n) (.substring 17 | (java.lang.Integer/toHexString 18 | (unchecked-int (char n))) 19 | 0 4) 20 | (instance? Byte n) (java.lang.Integer/toHexString (unchecked-byte n)) 21 | :else (Long/toHexString (unchecked-long n)))) 22 | ([n zero-pad-length] 23 | (text/pad-left (hex-string n) zero-pad-length "0"))) 24 | 25 | (defn hex-string-from-long 26 | "Converts an long value to a hexadecimal string representing the unsigned value of the long." 27 | ([^long n] 28 | (Long/toHexString n)) 29 | ([^long n zero-pad-length] 30 | (text/pad-left (hex-string-from-long n) zero-pad-length "0"))) 31 | 32 | (defn hex-string-from-byte 33 | "Converts an byte value to a hexadecimal string representing the unsigned value of the byte." 34 | ([b] 35 | (let [hs (Long/toHexString (+ 256 (long b))) 36 | n (.length hs)] 37 | (.substring hs (int (- n 2)))))) 38 | 39 | (defn bytes-from-hex-string 40 | "Converts a string of hex digits into a byte array." 41 | ([^String s] 42 | (let [s (str/replace s #"\s+" "") 43 | ^String s (str/replace s "0x" "") 44 | cc (.length s) 45 | n (quot cc 2) 46 | res (byte-array n)] 47 | (dotimes [i n] 48 | (aset res i (byte (+ (bit-and 0xF0 (bit-shift-left (Character/getNumericValue (char (.charAt s (int (* 2 i))))) 4)) 49 | (bit-and 0x0F (long (Character/getNumericValue (.charAt s (int (+ (* 2 i) 1)))))))))) 50 | res))) -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/logic.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.logic) 2 | 3 | (set! *warn-on-reflection* true) 4 | (set! *unchecked-math* :warn-on-boxed) 5 | 6 | (defmacro nand 7 | ([] false) 8 | ([x] `(not ~x)) 9 | ([x y] `(not (and ~x ~y))) 10 | ([x y & more] `(not (and ~x ~y ~@more)))) 11 | 12 | (defn xor 13 | "Returns the logical xor of a set of values, considered as booleans" 14 | (^Boolean [] false) 15 | (^Boolean [x] (boolean x)) 16 | (^Boolean [x y] (if x (not y) (boolean y))) 17 | (^Boolean [x y & more] 18 | (loop [p (xor x y) ss (seq more)] 19 | (if ss 20 | (recur (if (first ss) (not p) p) (next ss)) 21 | p)))) 22 | 23 | (defn and* 24 | "Returns the logical and of a set of values, considered as booleans" 25 | (^Boolean [] true) 26 | (^Boolean [x] (boolean x)) 27 | (^Boolean [x y] (if x (boolean y) false)) 28 | (^Boolean [x y & more] 29 | (loop [p (and* x y) ss (seq more)] 30 | (if p 31 | (if ss (recur (first ss) (next ss)) true) 32 | false)))) 33 | 34 | (defn or* 35 | "Returns the logical or of a set of values, considered as booleans" 36 | (^Boolean [] false) 37 | (^Boolean [x] (boolean x)) 38 | (^Boolean [x y] (if x true (boolean y))) 39 | (^Boolean [x y & more] 40 | (loop [p (or* x y) ss (seq more)] 41 | (if p 42 | true 43 | (if ss (recur (first ss) (next ss)) false))))) -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/loops.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.loops 2 | (:use mikera.cljutils.error) 3 | (:import [mikera.cljutils FastSeq])) 4 | 5 | (set! *warn-on-reflection* true) 6 | (set! *unchecked-math* :warn-on-boxed) 7 | 8 | (defmacro loop-results 9 | "Like loop, but returns a seq of all loop results rather than the final value." 10 | ;; is this a good idea? often will have a "nil" at end of list at loop termination.... 11 | [[bindings] body] 12 | (or (vector? bindings) (error "Loop bindings must be a vector")) 13 | (or (even? (count bindings) (error "Loop bindings require an even number of forms"))) 14 | (TODO)) 15 | 16 | (defmacro for-loop 17 | "Runs an imperative for loop, binding sym to init, running code as long as check is true, 18 | updating sym according to change" 19 | ([[sym init check change :as params] & code] 20 | (cond 21 | (not (vector? params)) 22 | (throw (Error. "Binding form must be a vector for for-loop")) 23 | (not= 4 (count params)) 24 | (throw (Error. "Binding form must have exactly 4 arguments in for-loop")) 25 | :default 26 | `(loop [~sym ~init value# nil] 27 | (if ~check 28 | (recur ~change (do ~@code)) 29 | value#))))) 30 | 31 | (defmacro doseq-indexed 32 | "loops over a set of values, binding index-sym to the 0-based index of each value" 33 | ([[val-sym values index-sym] & code] 34 | `(loop [vals# (seq ~values) 35 | ~index-sym (long 0)] 36 | (if vals# 37 | (let [~val-sym (first vals#)] 38 | ~@code 39 | (recur (next vals#) (inc ~index-sym))) 40 | nil)))) 41 | 42 | (defmacro dovec 43 | "Performs an operation for each element of an indexed vector. Binds i to the index at each element." 44 | ([[sym v :as bindings] & body] 45 | (when-not (vector? bindings) (error "dovec requires a binding vector")) 46 | (when-not (symbol? sym) (error "dovec binding requires a symbol")) 47 | `(let [v# ~v 48 | c# (count v#)] 49 | (loop [~'i 0] 50 | (if (< ~'i c#) 51 | (let [~sym (v# ~'i)] 52 | ~@body 53 | (recur (inc ~'i))) 54 | nil))))) 55 | 56 | (defn eager-map 57 | "Like map, but eagerly runs the function over the whole sequence. 58 | Equivalent to (doall (map ....)) but marginally faster." 59 | ;; TODO fast implementation for Indexed collections 60 | ([f xs] 61 | (if-let [xs (seq xs)] 62 | (let [result (FastSeq. (f (first xs)))] 63 | (loop [xs xs 64 | r result] 65 | (if-let [nxs (next xs)] 66 | (do (set! (._next r) (FastSeq. (f (first nxs)))) 67 | (recur nxs (._next r))) 68 | result)))))) 69 | 70 | (defmacro or-loop 71 | "Evaluates body repeatedly up to a given number of times, until it returns a truthy value. 72 | Returns nil if a truthy value is not found." 73 | ([[max-times :as bindings] & body] 74 | (when-not (vector? bindings) (error "or-loop requires a binding vector")) 75 | `(loop [tries# ~max-times] 76 | (if (<= tries# 0) nil 77 | (if-let [res# (do ~@body)] 78 | res# 79 | (recur (dec tries#))))))) 80 | 81 | (defmacro dotimes-results 82 | "Like dotimes, but retuns a seq of the results of each iteration." 83 | ([[sym n :as binding] & body] 84 | (or (vector? binding) (error "Must have a binding vector!")) 85 | (or (== 2 (count binding)) (error "Binding vector must have 2 elements!")) 86 | (or (symbol? sym) (error "First binding argument must be a symbol.")) 87 | `(let [n# (long ~n)] 88 | (loop [~sym 0 ^FastSeq fs# nil ^FastSeq hfs# nil] 89 | (if (< ~sym n#) 90 | (let [^FastSeq nfs# (FastSeq. (do ~@body) nil)] 91 | (when hfs# (set! (. hfs# ~'_next) nfs#)) 92 | (recur (unchecked-inc ~sym) (or fs# nfs#) nfs#)) 93 | fs#))))) -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/macros.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.macros) 2 | 3 | (set! *warn-on-reflection* true) 4 | (set! *unchecked-math* :warn-on-boxed) 5 | 6 | ; TODO: figure out why not working wiith Clojure 1.6.0? 7 | ;(when (and (>= (:major *clojure-version*) 1) (< (:minor *clojure-version*) 5)) 8 | ; (defmacro as-> 9 | ; "Binds name to expr, evaluates the first form in the lexical context 10 | ; of that binding, then binds name to that result, repeating for each 11 | ; successive form, returning the result of the last form." 12 | ; [expr name & forms] 13 | ; `(let [~name ~expr 14 | ; ~@(interleave (repeat name) forms)] 15 | ; ~name))) 16 | 17 | (defmacro and-as-> 18 | "Similar to as->, except wraps an implicit and around each step: returns nil/false 19 | as soon as any expression returns nil/false." 20 | ([expr sym & body] 21 | (if (seq body) 22 | `(as-> ~expr ~sym 23 | ~(first body) 24 | ~@(map (fn [b] `(and ~sym ~b)) (next body))) 25 | expr))) -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/mouse.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.mouse 2 | "Utility functions for reading the position of the mouse pointer." 3 | (:import [java.awt MouseInfo PointerInfo Point])) 4 | 5 | (set! *warn-on-reflection* true) 6 | (set! *unchecked-math* :warn-on-boxed) 7 | 8 | (defn- mouse-point 9 | (^Point [] 10 | (.getLocation (MouseInfo/getPointerInfo)))) 11 | 12 | (defn mouse-pos 13 | "Polls the current mouse position relative to the screen" 14 | ([] 15 | (let [pt (mouse-point)] 16 | [(long (.x pt)) (long (.y pt))]))) 17 | 18 | (defn mouse-x 19 | "Polls the current mouse x-position relative to the screen" 20 | (^long [] 21 | (let [pt (mouse-point)] 22 | (long (.x pt))))) 23 | 24 | (defn mouse-y 25 | "Polls the current mouse y-position relative to the screen" 26 | (^long [] 27 | (let [pt (mouse-point)] 28 | (long (.y pt))))) -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/namespace.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.namespace 2 | "Utilities for working with Clojure namespaces" 3 | (:refer-clojure :exclude [import])) 4 | 5 | (set! *warn-on-reflection* true) 6 | (set! *unchecked-math* :warn-on-boxed) 7 | 8 | ;; The use and distribution terms for this software are covered by the 9 | ;; Eclipse Public License 1.0 10 | ;; (http://opensource.org/licenses/eclipse-1.0.php) which can be found 11 | ;; in the file epl-v10.html at the root of this distribution. By using 12 | ;; this software in any fashion, you are agreeing to be bound by the 13 | ;; terms of this license. You must not remove this notice, or any 14 | ;; other, from this software. 15 | 16 | ;; import macros originally from ztellman/potemkin 17 | 18 | (defmacro import-fn 19 | "Given a function in another namespace, defines a function with the same name in the 20 | current namespace. Argument lists, doc-strings, and original line-numbers are preserved." 21 | [sym] 22 | (let [vr (resolve sym) 23 | m (meta vr) 24 | nspace (:name m) 25 | n (:name m) 26 | arglists (:arglists m) 27 | doc (:doc m) 28 | protocol (:protocol m)] 29 | (when-not vr 30 | (throw (IllegalArgumentException. (str "Don't recognize " sym)))) 31 | (when (:macro m) 32 | (throw (IllegalArgumentException. (str "Calling import-fn on a macro: " sym)))) 33 | `(do 34 | (def ~(with-meta n {:protocol protocol}) (deref ~vr)) 35 | (alter-meta! (var ~n) assoc 36 | :doc ~doc 37 | :arglists ~(list 'quote arglists) 38 | :file ~(:file m) 39 | :line ~(:line m)) 40 | ~vr))) 41 | 42 | (defmacro import-macro 43 | "Given a macro in another namespace, defines a macro with the same name in the 44 | current namespace. Argument lists, doc-strings, and original line-numbers are preserved." 45 | [sym] 46 | (let [vr (resolve sym) 47 | m (meta vr) 48 | n (:name m) 49 | nspace (:ns m) 50 | arglists (:arglists m) 51 | doc (:doc m)] 52 | (when-not vr 53 | (throw (IllegalArgumentException. (str "Don't recognize " sym)))) 54 | (when-not (:macro m) 55 | (throw (IllegalArgumentException. (str "Calling import-macro on a non-macro: " sym)))) 56 | `(do 57 | (def ~n ~(resolve sym)) 58 | (alter-meta! (var ~n) assoc 59 | :doc ~doc 60 | :arglists ~(list 'quote arglists) 61 | :file ~(:file m) 62 | :line ~(:line m)) 63 | (.setMacro (var ~n)) 64 | ~vr))) 65 | 66 | (defmacro import-def 67 | "Given a regular def'd var from another namespace, defined a new var with the 68 | same name in the current namespace." 69 | [sym] 70 | (let [vr (resolve sym) 71 | m (meta vr) 72 | n (:name m) 73 | n (if (:dynamic m) (with-meta n {:dynamic true}) n) 74 | nspace (:ns m) 75 | doc (:doc m)] 76 | (when-not vr 77 | (throw (IllegalArgumentException. (str "Don't recognize " sym)))) 78 | `(do 79 | (def ~n @~vr) 80 | (alter-meta! (var ~n) assoc 81 | :doc ~doc 82 | :file ~(:file m) 83 | :line ~(:line m)) 84 | ~vr))) 85 | 86 | (defmacro import [sym] 87 | (let [vr (resolve sym) 88 | m (meta vr)] 89 | (cond 90 | (:macro m) `(import-macro ~sym) 91 | (:arglists m) `(import-fn ~sym) 92 | :default `(import-def ~sym)))) 93 | 94 | ;; pull macros modified from: 95 | ;; http://stackoverflow.com/questions/4732134 96 | 97 | (defmacro pull 98 | "Pulls one or more symbols from another namespace" 99 | ([ns-qualified-sym] 100 | `(pull ~(symbol (namespace ns-qualified-sym)) ~(symbol (name ns-qualified-sym)))) 101 | ([ns vlist] 102 | (let [vlist (if (coll? vlist) vlist [vlist])] 103 | `(do ~@(for [sym vlist] 104 | (let [full-sym (symbol (str ns) (str sym))] 105 | `(import ~full-sym))))))) 106 | 107 | (defmacro pull-all 108 | "Pulls in all public symbols from another namespace" 109 | ([ns] 110 | (require ns) 111 | `(pull ~ns ~(for [[sym var] (ns-publics ns)] sym)))) 112 | 113 | ;; nstools functions 114 | ;; by Conrad hinsen 115 | ;; licensed under EPL 116 | 117 | (defn like 118 | "Take over all refers, aliases, and imports from the namespace named by 119 | ns-sym into the current namespace. Use :like in the ns+ macro in preference 120 | to calling this directly." 121 | [ns-sym] 122 | (require ns-sym) 123 | (doseq [[alias-sym other-ns] (ns-aliases ns-sym)] 124 | (alias alias-sym (ns-name other-ns))) 125 | (doseq [[sym ref] (ns-refers ns-sym)] 126 | (ns-unmap *ns* sym) 127 | (. *ns* (refer sym ref)) 128 | (doseq [[sym ref] (ns-imports ns-sym)] 129 | (. *ns* (importClass ref))))) 130 | 131 | (defn clone 132 | "Take over all refers, aliases, and imports from the namespace named by 133 | ns-sym into the current namespace, and add refers to all vars interned. 134 | Use :clone in the ns+ macro in preference to calling this directly." 135 | [ns-sym] 136 | (like ns-sym) 137 | (use ns-sym)) 138 | 139 | (defn from 140 | "Add refers to syms from ns-sym to the current namespace, replacing 141 | existing refers if necessary. If :all is given instead of syms, 142 | all symbols from ns-sym are referred to, making this equivalent 143 | to (use ns-sym). Use :from in the ns+ macro in preference to calling 144 | this directly." 145 | [ns-sym & syms] 146 | (if (= syms '(:all)) 147 | (use ns-sym) 148 | (do 149 | (require ns-sym) 150 | (doseq [sym syms] 151 | (ns-unmap *ns* sym) 152 | (. *ns* (refer sym (ns-resolve ns-sym sym))))))) 153 | 154 | (defn remove-from-ns 155 | "Remove symbols from the namespace. Use :remove in the ns+ macro in 156 | preference to calling this directly." 157 | [& syms] 158 | (doseq [sym syms] 159 | (ns-unmap *ns* sym))) 160 | 161 | ;; with-ns macros 162 | ;; by Stuart Sierra, http://stuartsierra.com/ 163 | ;; March 28, 2009 164 | ;; licensed under EPL 165 | 166 | (defmacro with-ns 167 | "Evaluates body in another namespace. ns is either a namespace 168 | object or a symbol. This makes it possible to define functions in 169 | namespaces other than the current one." 170 | [ns & body] 171 | `(binding [*ns* (the-ns '~ns)] 172 | ~@(map (fn [form] `(eval '~form)) body))) 173 | 174 | (defmacro with-temp-ns 175 | "Evaluates body in an anonymous namespace, which is then immediately 176 | removed. The temporary namespace will 'refer' clojure.core." 177 | [& body] 178 | `(try 179 | (create-ns 'sym#) 180 | (let [result# (with-ns sym# 181 | (clojure.core/refer-clojure) 182 | ~@body)] 183 | result#) 184 | (finally (remove-ns 'sym#)))) 185 | 186 | (defmacro with-environment 187 | "Evaluates body in a temporary namespace that merges several other namespaces" 188 | ([namespaces & body] 189 | `(with-temp-ns 190 | ~@(for [ns namespaces] 191 | `(pull-all ~ns)) 192 | ~@body))) 193 | 194 | (defmacro with-merged-environment 195 | "Evaluates body in a temporary namespace that merges several other namespaces" 196 | ([namespaces & body] 197 | `(with-environment ~namespaces 198 | (pull-all ~(ns-name *ns*)) 199 | ~@body))) 200 | 201 | (defmacro deferred 202 | "Loads and runs a function dynamically to defer loading the namespace. 203 | Usage: \"(deferred clojure.core/+ 1 2 3)\" returns 6. There's no issue 204 | calling require multiple times on an ns." 205 | [fully-qualified-func & args] 206 | (let [func (symbol (name fully-qualified-func)) 207 | space (symbol (namespace fully-qualified-func))] 208 | `(do (require '~space) 209 | (let [v# (ns-resolve '~space '~func)] 210 | (v# ~@args))))) -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/reader.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.reader) 2 | 3 | (set! *warn-on-reflection* true) 4 | (set! *unchecked-math* :warn-on-boxed) 5 | 6 | (defn string-reader 7 | "Returns a reader for the given String" 8 | ([^String s] 9 | (clojure.lang.LineNumberingPushbackReader. (java.io.StringReader. s)))) 10 | -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/text.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.text 2 | "Utilities for working with text strings") 3 | 4 | (set! *warn-on-reflection* true) 5 | (set! *unchecked-math* :warn-on-boxed) 6 | 7 | (defn repeat-char 8 | "Repeats a character a given numbers of times and returns the concatenated String" 9 | (^String [num char] 10 | (apply str (repeat num char)))) 11 | 12 | (defn truncate 13 | "Truncates a string to a given maximum length." 14 | (^String [^String s ^long length] 15 | (let [slen (.length s)] 16 | (if (<= slen length) 17 | s 18 | (.substring s 0 length))))) 19 | 20 | (defn truncate-dotted 21 | "Truncates a string to a specified length, putting dots at the end if length is exceeded" 22 | (^String [^String s length & {:keys [num-dots] 23 | :or {num-dots 3}}] 24 | (let [length (long length) 25 | num-dots (long num-dots) 26 | num-dots (int (min num-dots length)) 27 | slen (.length s) 28 | ss (min slen (- length num-dots))] 29 | (if (< slen length) 30 | s 31 | (str (.substring s 0 ss) (repeat-char num-dots \.)))))) 32 | 33 | (defn pad-right 34 | "Pads a string on the right with the given char. Char defaults to space if not specified." 35 | (^String [^String string ^long min-length] 36 | (pad-right string min-length \space)) 37 | (^String [^String string min-length char] 38 | (let [min-length (long min-length) 39 | slen (.length string)] 40 | (if (< slen min-length ) 41 | (str string (repeat-char (- min-length slen) char)) 42 | string)))) 43 | 44 | (defn pad-left 45 | "Pads a string on the left with the given char. Char defaults to space if not specified." 46 | (^String [^String string ^long min-length] 47 | (pad-left string min-length \space)) 48 | (^String [^String string ^long min-length char] 49 | (let [slen (.length string)] 50 | (if (< slen min-length ) 51 | (str (repeat-char (- min-length slen) char) string) 52 | string)))) 53 | 54 | (defn pad-centre 55 | "Pads a string on the both sides with the given char. Char defaults to space if not specified." 56 | (^String [^String string ^long min-length] 57 | (pad-left string min-length \space)) 58 | (^String [^String string ^long min-length char] 59 | (let [slen (.length string)] 60 | (if (< slen min-length ) 61 | (str (repeat-char (- min-length slen) char) string) 62 | string)))) 63 | 64 | (defn capitalise ^String [^String s] 65 | (if (> (count s) 0) 66 | (str (Character/toUpperCase (.charAt s 0)) (.substring s 1)) 67 | s)) 68 | 69 | (defn take-lines 70 | "Takes a specified number of lines from a file, and returns a sequence of these lines as Strings" 71 | ([n file] 72 | (with-open [rdr (clojure.java.io/reader file)] 73 | (doall (take n (line-seq rdr)))))) -------------------------------------------------------------------------------- /src/main/clojure/mikera/cljutils/vectors.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.vectors 2 | "Utility functions for working with Clojure persistent vectors" 3 | (:use mikera.cljutils.error) 4 | (:import [clojure.lang IPersistentVector])) 5 | 6 | (set! *warn-on-reflection* true) 7 | (set! *unchecked-math* :warn-on-boxed) 8 | 9 | (defn find-identical-position 10 | "Searches a vector for an identical item and returns the index, or -1 if not found. 11 | Mainly used to pick out the position of a thing within a specific location" 12 | ^long [item ^IPersistentVector vector] 13 | (let [c (count vector)] 14 | (loop [i (int 0)] 15 | (if (>= i c) 16 | -1 17 | (if (identical? item (.nth vector i)) i (recur (inc i))))))) 18 | 19 | (defn vector-without 20 | "Cuts a value from a specific position in a vector" 21 | [^IPersistentVector vector ^long i] 22 | (let [c (count vector) 23 | ni (inc i)] 24 | (cond 25 | (== c 1) (if (== i 0) [] (error "Index of out range: " i)) 26 | (== ni c) (subvec vector 0 i) 27 | :else (vec (concat (subvec vector 0 i) (subvec vector ni c)))))) 28 | 29 | (defn remove-from-vector 30 | "Removes a specific object from a vector. Throws an error if the object is not found." 31 | [item ^IPersistentVector vector] 32 | (let [i (find-identical-position item vector)] 33 | (when (< i 0) (error "item not found!")) 34 | (vector-without vector i))) -------------------------------------------------------------------------------- /src/main/java/mikera/cljutils/Clojure.java: -------------------------------------------------------------------------------- 1 | package mikera.cljutils; 2 | 3 | import clojure.lang.RT; 4 | import clojure.lang.Symbol; 5 | import clojure.lang.Var; 6 | 7 | /** 8 | * Class containing static utility methods for Java->Clojure interop 9 | * 10 | * @author Mike 11 | * 12 | */ 13 | public class Clojure { 14 | public static final Var REQUIRE=var("clojure.core", "require"); 15 | public static final Var META=var("clojure.core", "meta"); 16 | public static final Var EVAL=var("clojure.core", "eval"); 17 | public static final Var READ_STRING=var("clojure.core", "read-string"); 18 | 19 | /** 20 | * Require a namespace by name, loading it if necessary. 21 | * 22 | * Calls clojure.core/require 23 | * 24 | * @param nsName 25 | * @return 26 | */ 27 | public static Object require(String nsName) { 28 | return REQUIRE.invoke(Symbol.intern(nsName)); 29 | } 30 | 31 | public static Object readString(String s) { 32 | return READ_STRING.invoke(s); 33 | } 34 | 35 | /** 36 | * Looks up a var by name in the clojure.core namespace. 37 | * 38 | * The var can subsequently be invoked if it is a function. 39 | * 40 | * @param varName 41 | * @return 42 | */ 43 | public static Var var(String varName) { 44 | return var("clojure.core",varName); 45 | } 46 | 47 | /** 48 | * Looks up a var by name in the given namespace. 49 | * 50 | * The var can subsequently be invoked if it is a function. 51 | 52 | * @param nsName 53 | * @param varName 54 | * @return 55 | */ 56 | public static Var var(String nsName, String varName) { 57 | return RT.var(nsName,varName); 58 | } 59 | 60 | /** 61 | * Evaluates a String, which should contain valid Clojure code. 62 | * 63 | * @param string 64 | * @return 65 | */ 66 | public static Object eval(String string) { 67 | return EVAL.invoke(readString(string)); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/mikera/cljutils/Error.java: -------------------------------------------------------------------------------- 1 | package mikera.cljutils; 2 | 3 | /** 4 | * A custom runtime exception class 5 | * @author Mike 6 | * 7 | */ 8 | @SuppressWarnings("serial") 9 | public class Error extends RuntimeException { 10 | public Error(String message) { 11 | super(message); 12 | } 13 | 14 | public Error(Throwable e) { 15 | super(e); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/mikera/cljutils/FastSeq.java: -------------------------------------------------------------------------------- 1 | package mikera.cljutils; 2 | 3 | import java.util.*; 4 | 5 | import clojure.lang.IPersistentCollection; 6 | import clojure.lang.ISeq; 7 | import clojure.lang.PersistentList; 8 | import clojure.lang.RT; 9 | 10 | /* 11 | * A fast, lightweight sequence class designed for quick construction of lists 12 | * 13 | * In particular, the list is mutable so you can cut / reshape sequences. Exploit 14 | * this "feature" with caution, for obvious reasons. Clojure isn't expecting lists 15 | * to be mutated. 16 | * 17 | * In practice, this means that you should only ever mutate the FastSeq while 18 | * it is being constructed. 19 | */ 20 | 21 | public final class FastSeq implements clojure.lang.ISeq, clojure.lang.Sequential, Collection { 22 | public Object _first; 23 | public FastSeq _next; 24 | public Object x; 25 | 26 | public FastSeq() { 27 | this(null,null); 28 | } 29 | 30 | public FastSeq(Object o) { 31 | this (o,null); 32 | } 33 | 34 | public FastSeq(Object o, FastSeq next) { 35 | _first=o; 36 | this._next=next; 37 | } 38 | 39 | @Override 40 | public int count() { 41 | int result=0; 42 | FastSeq head=_next; 43 | while (head!=null) { 44 | head=head._next; 45 | result++; 46 | } 47 | return result; 48 | } 49 | 50 | @Override 51 | public IPersistentCollection empty() { 52 | return null; 53 | } 54 | 55 | @Override 56 | public boolean equiv(Object o) { 57 | ISeq oseq=RT.seq(o); 58 | FastSeq head=this; 59 | while (head!=null) { 60 | if (oseq==null) return false; 61 | if (!clojure.lang.Numbers.equiv(head._first, oseq.first())) return false; 62 | head=head._next; 63 | oseq=oseq.next(); 64 | } 65 | return (o==null); 66 | } 67 | 68 | @Override 69 | public ISeq seq() { 70 | return this; 71 | } 72 | 73 | @Override 74 | public Object first() { 75 | return _first; 76 | } 77 | 78 | @Override 79 | public ISeq next() { 80 | return _next; 81 | } 82 | 83 | @Override 84 | public ISeq more() { 85 | return (_next==null)?PersistentList.EMPTY:_next; 86 | } 87 | 88 | @Override 89 | public ISeq cons(Object o) { 90 | return new FastSeq(o, this); 91 | } 92 | 93 | @Override 94 | public int size() { 95 | return count(); 96 | } 97 | 98 | @Override 99 | public boolean isEmpty() { 100 | return false; 101 | } 102 | 103 | @Override 104 | public boolean contains(Object o) { 105 | FastSeq head=this; 106 | while (head!=null) { 107 | if (o.equals(head._first)) return true; 108 | head=head._next; 109 | } 110 | return false; 111 | } 112 | 113 | public class FastSeqIterator implements Iterator { 114 | FastSeq head=FastSeq.this; 115 | 116 | @Override 117 | public boolean hasNext() { 118 | return head!=null; 119 | } 120 | 121 | @Override 122 | public Object next() { 123 | Object result=head._first; 124 | head=head._next; 125 | return result; 126 | } 127 | 128 | @Override 129 | public void remove() { 130 | throw new UnsupportedOperationException(); 131 | } 132 | } 133 | 134 | @Override 135 | public Iterator iterator() { 136 | return new FastSeqIterator(); 137 | } 138 | 139 | @Override 140 | public Object[] toArray() { 141 | Object[] arr=new Object[count()]; 142 | int i=0; 143 | for (Object o:this) { 144 | arr[i++]=o; 145 | } 146 | return arr; 147 | } 148 | 149 | @SuppressWarnings("unchecked") 150 | @Override 151 | public Object[] toArray(Object[] a) { 152 | int i=0; 153 | for (Object o:this) { 154 | a[i++]=o; 155 | } 156 | return a; 157 | } 158 | 159 | @Override 160 | public boolean add(Object e) { 161 | throw new UnsupportedOperationException(); 162 | } 163 | 164 | @Override 165 | public boolean remove(Object o) { 166 | throw new UnsupportedOperationException(); 167 | } 168 | 169 | @Override 170 | public boolean containsAll(Collection c) { 171 | for (Object o:c) { 172 | if (!contains (o)) return false; 173 | } 174 | return true; 175 | } 176 | 177 | @Override 178 | public boolean addAll(Collection c) { 179 | throw new UnsupportedOperationException(); 180 | } 181 | 182 | @Override 183 | public boolean removeAll(Collection c) { 184 | throw new UnsupportedOperationException(); 185 | } 186 | 187 | @Override 188 | public boolean retainAll(Collection c) { 189 | throw new UnsupportedOperationException(); 190 | } 191 | 192 | @Override 193 | public void clear() { 194 | throw new UnsupportedOperationException(); 195 | } 196 | 197 | } 198 | -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/benchmarks.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.benchmarks 2 | (:use [mikera.cljutils loops]) 3 | (:require [criterium.core :as c])) 4 | 5 | (defn benchmarks [] 6 | ;; eager-map with long sequence 7 | (let [] 8 | (c/quick-bench (eager-map inc (range 1000)))) 9 | ;; 77 us 10 | 11 | (let [] 12 | (c/quick-bench (doall (map inc (range 1000))))) 13 | ;; 79 us 14 | 15 | ;; eager-map with short sequence 16 | (let [] 17 | (c/quick-bench (eager-map inc (range 10)))) 18 | ;; 877 ns 19 | 20 | (let [] 21 | (c/quick-bench (doall (map inc (range 10))))) 22 | ;; 1159 ns 23 | 24 | 25 | ) -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/dummy.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.dummy 2 | (:require [mikera.cljutils.namespace :as n]) 3 | (:require [mikera.cljutils.import :as imp])) 4 | 5 | (def dummy-bar 6 | "dummy-bar in mikera.cljutils.dummy namespace" 9) 7 | 8 | (n/from 'mikera.cljutils.import 'clone-foo) 9 | 10 | ;; (n/import mikera.cljutils.import [import-foo]) 11 | 12 | (n/import mikera.cljutils.import/import-foo) 13 | (n/pull mikera.cljutils.import [import-bar]) 14 | (n/pull mikera.cljutils.import import-func) 15 | (n/pull mikera.cljutils.import/import-macro) 16 | 17 | (n/pull-all mikera.cljutils.pulled) 18 | 19 | (defn test-stuff [] 20 | (meta #'mikera.cljutils.import/import-foo) 21 | (meta #'import-foo) 22 | (meta #'dummy-bar) ;; gets metadata inluding docstring 23 | ) 24 | -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/import.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.import) 2 | 3 | (def clone-foo 4 | "Cloneable value that stays in the import namespace") 5 | 6 | (def import-foo 7 | "Docstring for import-foo in mikera.cljutils.import namespace" 10) 8 | 9 | (def import-bar 10 | "Docstring for import-foo in mikera.cljutils.import namespace" 5) 11 | 12 | (def this-foo 13 | "Imported value that should get overrideen by with-environment test" 14 | 1000000) 15 | 16 | (defn import-func 17 | "Docstring for import-func in mikera.cljutils.import namespace" 18 | ([] 19 | 0) 20 | ([x] 21 | 1)) 22 | 23 | (defmacro import-macro 24 | "Docstring for import-macro in mikera.cljutils.import namespace" 25 | ([] 26 | "import-macro called with zero args") 27 | ([x] 28 | "import-macro called with one arg")) -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/pulled.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.pulled) 2 | 3 | (def pulled-foo 4 | "Docstring for pulled-foo in mikera.cljutils.pulled namespace" 10) 5 | 6 | (defn pulled-func 7 | "Docstring for pulled-func in mikera.cljutils.pulled namespace" 8 | ([] 9 | 0) 10 | ([x] 11 | 1)) 12 | 13 | (defmacro pulled-macro 14 | "Docstring for pulled-macro in mikera.cljutils.pulled namespace" 15 | ([] 16 | "pulled-macro called with zero args") 17 | ([x] 18 | "pulled-macro called with one arg")) -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test.txt: -------------------------------------------------------------------------------- 1 | test1 2 | test2 -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_arrays.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-arrays 2 | (:use clojure.test) 3 | (:use [mikera.cljutils arrays])) 4 | 5 | (deftest test-types-array 6 | (is (typed-array [1 2 3]))) 7 | 8 | (deftest test-array? 9 | (is (array? (int-array [1 2 3]))) 10 | (is (array? (object-array [1 2 3]))) 11 | (is (not (array? :foo)))) 12 | 13 | (deftest test-array-type 14 | (is (identical? Integer/TYPE (array-type (int-array [1 2 3])))) 15 | (is (identical? Object (array-type (object-array [1 2 3])))) 16 | (is (nil? (array-type :foo)))) 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_bytes.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-bytes 2 | (:use clojure.test) 3 | (:use mikera.cljutils.bytes) 4 | (:refer-clojure :exclude [reverse])) 5 | 6 | (deftest test-join 7 | (is (= (seq (unchecked-byte-array [0 1 2 3 0 1])) 8 | (seq (join (unchecked-byte-array (range 4)) (unchecked-byte-array (range 2))))))) 9 | 10 | (deftest test-slice 11 | (is (= (seq (unchecked-byte-array [1 2])) 12 | (seq (slice (unchecked-byte-array (range 4)) 1 2)))) 13 | (is (= (seq (unchecked-byte-array [1 2 3])) 14 | (seq (slice (unchecked-byte-array (range 4)) 1))))) 15 | 16 | (deftest test-to-hex 17 | (is (= "01 02" (to-hex-string (unchecked-byte-array (range 1 3)))))) 18 | 19 | (deftest test= 20 | (is (bytes= (unchecked-byte-array (range 4)) 21 | (unchecked-byte-array (range 4)))) 22 | (is (not (bytes= (unchecked-byte-array (range 4)) 23 | (unchecked-byte-array (range 5)))))) -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_concurrent.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-concurrent 2 | (:use clojure.test) 3 | (:use mikera.cljutils.concurrent)) 4 | 5 | (deftest test-effects 6 | (let [a (atom 0)] 7 | (plet [t1 (swap! a inc) 8 | t2 (swap! a inc) 9 | t3 (swap! a inc)] 10 | (is (== 3 @a))))) 11 | 12 | -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_core.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-core 2 | (:use clojure.test) 3 | (:use [mikera.cljutils core])) 4 | 5 | (defn kws [& {:keys [] 6 | :as options}] 7 | options) 8 | 9 | (deftest test-apply-kw 10 | (is (= {:a 2} (apply-kw kws {:a 2}))) 11 | (is (= {:a 2} (apply-kw kws :a 2 {}))) 12 | (is (= {:a 2} (apply-kw kws :a 2 {:a 4}))) 13 | (is (= {:a 2 :b 3} (apply-kw kws :a 2 {:b 3})))) 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_error.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-error 2 | (:use clojure.test) 3 | (:use [mikera.cljutils error])) 4 | 5 | (deftest test-error 6 | (testing "Error" 7 | (is (thrown? Throwable (error "foo"))))) 8 | 9 | (deftest test-error? 10 | (testing "error?" 11 | (is (error? (error "foo"))) 12 | (is (not (error? (+ 1 2)))))) 13 | 14 | (deftest test-stacktrace-str 15 | (let [ss (stacktrace-str (Throwable.))] 16 | (is (string? ss)) 17 | (is (< 0 (count ss))))) 18 | 19 | -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_expression.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-expression 2 | (:use clojure.test) 3 | (:use [mikera.cljutils expression])) 4 | 5 | (deftest test-ex-info 6 | (testing "primitive maths" 7 | (let [e (expression-info '(+ 1.0 2.0))] 8 | (is (= (Double/TYPE) (:class e))) 9 | (is (:primitive? e))))) 10 | -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_find.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-find 2 | (:use clojure.test) 3 | (:use [mikera.cljutils find])) 4 | 5 | (deftest test-find-first 6 | (testing "find a number" 7 | (is (= 3 (find-first number? [:foo :bar 3 :baz 4 5 :bif]))) 8 | (is (= 4 (find-first number? [:foo :bar 3 :baz 4 5 :bif] 3))) 9 | (is (nil? (find-first number? [:foo :bar 3 :baz 4 5 :bif] 6))) 10 | (is (= 3 (find-first number? '(:foo :bar 3 :baz 4 5 :bif))))) 11 | (testing "find in empty coll" 12 | (is (nil? (find-first number? []))) 13 | (is (nil? (find-first number? '()))))) 14 | 15 | (deftest test-find-index 16 | (testing "find a number" 17 | (is (== 2 (find-index number? [:foo :bar 3 :baz 4 5 :bif]))) 18 | (is (== 4 (find-index number? [:foo :bar 3 :baz 4 5 :bif] 4))) 19 | (is (nil? (find-index number? [:foo :bar 3 :baz 4 5 :bif] 6))) 20 | (is (== 2 (find-index number? '(:foo :bar 3 :baz 4 5 :bif)))) 21 | (is (nil? (find-index number? [:foo :bar :bif]))))) 22 | 23 | (deftest test-find-position 24 | (is (== 2 (find-position 4 [0 2 4 6 8]))) 25 | (is (== 2 (find-position 4 (long-array [0 2 4 6 8])))) 26 | (is (nil? (find-position 5 [0 2 4 6 8]))) 27 | (is (nil? (find-position 5 (object-array [0 2 4 6 8]))))) 28 | 29 | (deftest test-indexed 30 | (is (indexed? [1 2 3 4])) 31 | (is (not (indexed? '(1 2 3 4))))) 32 | 33 | (deftest tets-eager-filter 34 | (is (= [1 3] (eager-filter odd? [1 2 3 4]))) 35 | (is (nil? (eager-filter odd? [2 4])))) 36 | -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_hex.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-hex 2 | (:use clojure.test) 3 | (:use mikera.cljutils.hex)) 4 | 5 | (deftest test-to-hex-string 6 | (is (= "cafebabe" (hex-string 3405691582))) 7 | (is (= "10" (hex-string 16))) 8 | (is (= "00000010" (hex-string 16 8)))) 9 | 10 | (deftest test-hex-from-string 11 | (is (= (seq (bytes-from-hex-string "0xFF 0x00")) [-1 0]))) 12 | -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_logic.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-logic 2 | (:use clojure.test) 3 | (:use [mikera.cljutils logic])) 4 | 5 | (deftest test-xor 6 | (testing "XOR" 7 | (is (xor 1 nil)) 8 | (is (xor 1 nil 1 1)) 9 | (is (xor 1)) 10 | (is (not (xor))) 11 | (is (not (xor true false false true false false true true))))) 12 | 13 | (deftest test-nand 14 | (testing "NAND" 15 | (is (not (nand true true))) 16 | (is (nand nil false)) 17 | (is (not (nand 1))) 18 | (is (not (nand 1 2))) 19 | (is (not (nand 1 2 3))) 20 | (is (not (nand 1 2 3 4)))) 21 | (is (nand 1 2 nil 3 4))) 22 | 23 | (deftest test-and* 24 | (testing "AND" 25 | (is (identical? true (and* 1))) 26 | (is (and* 1 2)) 27 | (is (and* 1 1 1 1)) 28 | (is (and* 1)) 29 | (is (and*)) 30 | (is (not (and* nil))) 31 | (is (not (and* 1 1 1 1 1 1 nil 1 1 1 1))))) 32 | 33 | (deftest test-or* 34 | (testing "OR" 35 | (is (identical? false (or* nil))) 36 | (is (or* 1 2)) 37 | (is (or* 1 1 1 1)) 38 | (is (or* 1)) 39 | (is (not (or*))) 40 | (is (not (or* nil))) 41 | (is (or* 1 1 1 1 1 1 nil 1 1 1 1)))) 42 | -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_loops.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-loops 2 | (:use clojure.test) 3 | (:use [mikera.cljutils loops]) 4 | (:require [mikera.cljutils.error :refer [error]])) 5 | 6 | (deftest test-for-loop 7 | (testing "Ten iterations" 8 | (let [c (atom 0)] 9 | (for-loop [i 0 (< i 10) (inc i)] 10 | (swap! c inc)) 11 | (is (= @c 10))))) 12 | 13 | (deftest test-doseq-indexed 14 | (testing "doseq-indexed with small vector" 15 | (let [c (atom 0) 16 | ic (atom 0)] 17 | (doseq-indexed [x [1 2 3 4] i] 18 | (swap! c + x) 19 | (swap! ic + i)) 20 | (is (= @c 10)) 21 | (is (= @ic 6))))) 22 | 23 | (deftest test-dovec 24 | (testing "Side effects" 25 | (let [r (atom 0) 26 | vs (atom #{})] 27 | (dovec [v [:a :b :c]] 28 | (swap! r + i) 29 | (swap! vs conj v)) 30 | (is (= #{:a :b :c} @vs)) 31 | (is (== 3 @r)))) 32 | (testing "Empty collection" 33 | (dovec [v []] (error "Shouldn't happen!")) 34 | (dovec [v nil] (error "Shouldn't happen!")))) 35 | 36 | (deftest test-eager-map 37 | (testing "Eager map" 38 | (is (= [1 2 3] (eager-map inc [0 1 2]))) 39 | (is (nil? (eager-map inc []))) 40 | (is (nil? (eager-map inc nil))))) 41 | 42 | (deftest test-dotimes-results 43 | (is (nil? (dotimes-results [i 0] 6))) 44 | (is (= [0 1 2 3] (dotimes-results [i 4] i))) 45 | (is (= [0] (dotimes-results [i 1] i)))) 46 | 47 | (deftest test-or-loop 48 | (is (nil? (or-loop [10] nil))) 49 | (is (= 10 (or-loop [10] 10))) 50 | (is (nil? (or-loop [0] 10)))) -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_macros.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-macros 2 | (:use clojure.test) 3 | (:use [mikera.cljutils macros])) 4 | 5 | (deftest test-as 6 | (is (= 1 (as-> 1 x x x)))) 7 | 8 | (deftest test-and-as 9 | (testing "Basic results" 10 | (is (= 1 (and-as-> 1 x))) 11 | (is (= nil (and-as-> nil x))) 12 | (is (= nil (and-as-> nil x 13 | x 14 | x))) 15 | (is (= 1 (and-as-> nil x 16 | 1)))) 17 | (testing "Sequential results" 18 | (is (= 3 (and-as-> 1 x 19 | (inc x) 20 | (inc x)))) 21 | (is (= nil (and-as-> 1 x 22 | (inc x) 23 | nil 24 | (inc x))))) 25 | (testing "Falsiness" 26 | (is (= false (and-as-> 1 x 27 | false 28 | true))) 29 | (is (= nil (and-as-> false x 30 | true 31 | nil)))) 32 | (testing "Conditional" 33 | (is (= 3 (and-as-> 1 x (inc x) (if (== 2 x) x nil) (inc x)))))) -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_namespace.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-namespace 2 | (:use clojure.test) 3 | (:require [mikera.cljutils.namespace :as n]) 4 | (:require [mikera.cljutils.dummy])) 5 | 6 | (n/clone 'mikera.cljutils.dummy) 7 | 8 | (deftest test-with-ns 9 | (testing "use existing ns" 10 | (is (= 9 (n/with-ns 'mikera.cljutils.dummy dummy-bar)))) 11 | (testing "cloned within ns" 12 | (is (= 9 dummy-bar)))) 13 | 14 | (deftest test-namespacing 15 | (testing "objects in their original namespace" 16 | (is (= "mikera.cljutils.dummy" (str (.ns (var mikera.cljutils.dummy/dummy-bar))))) 17 | (is (= "mikera.cljutils.dummy" (str (.ns (ns-resolve 'mikera.cljutils.dummy 'dummy-bar)))))) 18 | (testing "pulled objects exist in new namespace" 19 | (is (= "mikera.cljutils.dummy" (str (.ns (var mikera.cljutils.dummy/import-foo))))) 20 | (is (= "mikera.cljutils.dummy" (str (.ns (var mikera.cljutils.dummy/import-func))))) 21 | (is (= "mikera.cljutils.dummy" (str (.ns (var mikera.cljutils.dummy/import-macro)))))) 22 | (testing "refered objects stay in original namespace var" 23 | (is (= "mikera.cljutils.import" (str (.ns (ns-resolve 'mikera.cljutils.dummy 'clone-foo))))))) 24 | 25 | (deftest test-imported-stuff 26 | (testing "function" 27 | (is (= 0 (mikera.cljutils.dummy/import-func))) 28 | (is (= 1 (mikera.cljutils.dummy/import-func "foo")))) 29 | (testing "macro" 30 | (is (string? (mikera.cljutils.dummy/import-macro))) 31 | (is (:macro (meta #'mikera.cljutils.dummy/import-macro))))) 32 | 33 | (deftest test-clones-imports 34 | (is (ns-interns *ns*) 'dummy-bar) 35 | (is (ns-interns 'mikera.cljutils.dummy) 'dummy-bar)) 36 | 37 | (deftest test-with-ns 38 | (testing "dummy ns" 39 | (is (= 10 (n/with-ns mikera.cljutils.dummy import-foo)))) 40 | (testing "dummy ns function" 41 | (is (= 0 (n/with-ns mikera.cljutils.dummy (import-func)))))) 42 | 43 | (deftest test-with-temp-ns 44 | (testing "temp env" 45 | (is (= 2 (n/with-temp-ns (+ 1 1)))))) 46 | 47 | (def this-foo 99) 48 | 49 | (deftest test-with-environment 50 | (testing "dummy env" 51 | (is (= 0 (n/with-environment [mikera.cljutils.dummy] (import-func))))) 52 | (testing "combined env" 53 | (is (= 20 (n/with-environment [mikera.cljutils.import mikera.cljutils.pulled] 54 | (+ import-foo pulled-foo))))) 55 | (testing "merged env" 56 | (is (= 119 (n/with-merged-environment [mikera.cljutils.import mikera.cljutils.pulled] 57 | (+ import-foo pulled-foo this-foo)))))) 58 | -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_namespace_pulling.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-namespace-pulling 2 | (:use clojure.test) 3 | (:use mikera.cljutils.dummy)) 4 | 5 | ;; the dummy namespace contains pulled / imported functions 6 | ;; should able to :use this namespace and get at them! 7 | 8 | (deftest test-imported-stuff 9 | (testing "regular def" 10 | (is (= 10 pulled-foo)) 11 | (is (= 10 import-foo)) 12 | (is (= 5 import-bar))) 13 | (testing "function" 14 | (is (= 0 (import-func))) 15 | (is (= 0 (pulled-func))) 16 | (is (= 1 (import-func "foo")))) 17 | (testing "macro" 18 | (is (:macro (meta #'import-macro))) 19 | (is (:macro (meta #'pulled-macro))) 20 | (is (string? (import-macro))) 21 | (is (string? (pulled-macro))) 22 | )) 23 | -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_reader.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-reader 2 | (:use clojure.test) 3 | (:use mikera.cljutils.reader)) 4 | 5 | (deftest test-read 6 | (is (= [1 2] (read (string-reader "[1 2]"))))) 7 | 8 | -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_text.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-text 2 | (:use clojure.test) 3 | (:use [mikera.cljutils text]) 4 | (:require [clojure.java io])) 5 | 6 | (deftest test-text-padding 7 | (testing "dotted truncation" 8 | (is (= "a..." (truncate-dotted "aardvark" 4))) 9 | (is (= "aa.." (truncate-dotted "aardvark" 4 :num-dots 2))) 10 | (is (= "aardvark" (truncate-dotted "aardvark" 40 :num-dots 2))) 11 | (is (= "" (truncate-dotted "aardvark" 0 :num-dots 2)))) 12 | (testing "right padding" 13 | (is (= "a " (pad-right "a" 3))) 14 | (is (= "a--" (pad-right "a" 3 \-))) 15 | (is (= "armadillo" (pad-right "armadillo" 3)))) 16 | (testing "left padding" 17 | (is (= " a" (pad-left "a" 3))) 18 | (is (= "--a" (pad-left "a" 3 \-))) 19 | (is (= "armadillo" (pad-left "armadillo" 3)))) 20 | (testing "truncate" 21 | (is (= "" (truncate "hello" 0))) 22 | (is (= "he" (truncate "hello" 2))) 23 | (is (= "hello" (truncate "hello" 20))))) 24 | 25 | (deftest test-cap 26 | (is (= "You" (capitalise "you")))) 27 | 28 | (deftest test-lines 29 | (is (= ["test1"] (take-lines 1 (clojure.java.io/resource "mikera/cljutils/test.txt"))))) -------------------------------------------------------------------------------- /src/test/clojure/mikera/cljutils/test_vectors.clj: -------------------------------------------------------------------------------- 1 | (ns mikera.cljutils.test-vectors 2 | (:use clojure.test) 3 | (:use [mikera.cljutils vectors])) 4 | 5 | (deftest test-without 6 | (is (= [1 3] (vector-without [1 2 3] 1))) 7 | (is (= [1 2] (vector-without [1 2 3] 2))) 8 | (is (= [] (vector-without [1] 0)))) 9 | 10 | 11 | (deftest test-vector-without 12 | (let [a 1 b 2 c 3] 13 | (= [a b] (vector-without [a b c] 2)) 14 | (= [a c] (vector-without [a b c] 1)) 15 | (= [b c] (vector-without [a b c] 0)) 16 | (= [] (vector-without [a] 0)))) 17 | -------------------------------------------------------------------------------- /src/test/java/mikera/cljutils/AllClojureTests.java: -------------------------------------------------------------------------------- 1 | package mikera.cljutils; 2 | 3 | import mikera.cljunit.ClojureTest; 4 | 5 | public class AllClojureTests extends ClojureTest { 6 | 7 | @Override 8 | public String filter() { 9 | return "mikera.cljutils"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/mikera/cljutils/DemoApp.java: -------------------------------------------------------------------------------- 1 | package mikera.cljutils; 2 | 3 | import mikera.cljutils.Clojure; 4 | 5 | public class DemoApp { 6 | public static void main(String [] args) { 7 | // create a string containing a valid Clojure expression 8 | String s = "(+ 1 2)"; 9 | 10 | System.out.println("Evaluating Clojure code: " + s); 11 | 12 | // evaluate the expression 13 | Object result = Clojure.eval(s); 14 | 15 | System.out.println("=> "+ result); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/mikera/cljutils/TestClojure.java: -------------------------------------------------------------------------------- 1 | package mikera.cljutils; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | public class TestClojure { 8 | @Test public void testClojureRun() { 9 | assertEquals((long)2,Clojure.eval("(+ 1 1)")); 10 | assertEquals((long)3,Clojure.eval("(+ 1 2)")); 11 | } 12 | } 13 | --------------------------------------------------------------------------------