├── .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