)])`.
pprint is being developed by Tom Faulhaber (to mail me you can use
my first name at my domain which is infolace.com).
As with the rest of Clojure, the pretty printer is licensed under the
[http://opensource.org/licenses/eclipse-1.0.php Eclipse Public License 1.0].
Future development is guided by those using it, so send feedback about
what's working and not working for you and what you'd like to see in the
pretty printer.
## Pretty Printing Basics
Pretty printing is primarily implemented with the function
pprint. pprint takes a single argument and formats it according to the
settings of several special variables.
Generally, the defaults are fine for pretty printing and you can
simply use:
(pprint obj)
to print your object. If you wish to write to
another stream besides `*`out`*`, you can use:
(write obj :pretty true :stream foo)
where foo is the stream to which you wish to write. (The write
function has a lot more options which are not yet documented. Stay
tuned.)
When at the REPL, the pp macro pretty prints the last output
value. This is useful when you get something too complex to read
comfortably. Just type:
user=> (pp)
and you'll get a pretty printed version of the last thing output (the
magic variable `*`1).
## Dispatch tables and code formatting
The behavior of the pretty printer can be finely controlled through
the use of _dispatch tables_ that contain descriptions for how
different structures should be formatted.
Using custom dispatch tables, the pretty printer can create formatted
output for data structures that is customized for the
application. This allows pretty printing to be baked into any
structured output. For information and examples, see below in
[#Custom_Dispatch_Functions Custom Dispatch Functions].
The pretty printer comes with two pre-defined dispatch tables to cover
the most common situations:
`*`simple-dispatch`*` - supports basic representation of data in various
Clojure structures: seqs, maps, vectors, etc. in a fairly statndard
way. When structures need to be broken across lines, following lines
are indented to line up with the first element. `*`simple-dispatch`*` is
the default and is good for showing the output of most operations.
`*`code-dispatch`*` - has special representation for various structures
found in code: defn, condp, binding vectors, anonymous functions,
etc. This dispatch indents following lines of a list one more space as
appropriate for a function/argument type of list.
An example formatted with code dispatch:
user=> (def code '(defn cl-format
"An implementation of a Common Lisp compatible format function"
[stream format-in & args] (let [compiled-format (if (string? format-in)
(compile-format format-in) format-in) navigator (init-navigator args)]
(execute-format stream compiled-format navigator))))
#'user/code
user=> (with-pprint-dispatch *code-dispatch* (pprint code))
(defn cl-format
"An implementation of a Common Lisp compatible format function"
[stream format-in & args]
(let [compiled-format (if (string? format-in)
(compile-format format-in)
format-in)
navigator (init-navigator args)]
(execute-format stream compiled-format navigator)))
nil
user=>
There are three ways to set the current dispatch: set it to a specific
table permanantly with set-pprint-dispatch, bind it with
with-pprint-dispatch (as shown in the example above), or use the
:dispatch keyword argument to write.
## Control variables
The operation of pretty printing is also controlled by a set of variables
that control general parameters of how the pretty printer makes
decisions. The current list is as follows:
*`*`print-pretty`*`*: Default: *true*
Bind to true if you want write to use pretty printing. (pprint and pp automatically
bind this to true.)
*`*`print-right-margin`*`*: Default: *72*
Pretty printing will try to avoid anything going beyond this column.
*`*`print-miser-width`*`*: Default: *40*
The column at which to enter miser style. Depending on the dispatch table,
miser style add newlines in more places to try to keep lines short allowing for further
levels of nesting. For example, in the code dispatch table, the pretty printer will
insert a newline between the "if" and its condition when in miser style.
*`*`print-suppress-namespaces`*`*: Default: *false*
Don't print namespaces with symbols. This is particularly useful when
pretty printing the results of macro expansions
*`*`print-level`*`*: Default: *nil*
As with the regular Clojure print function, this variable controls the
depth of structure that is printed. The argument itself is level 0,
the first level of a collection is level 1, etc. When the structure
gets deeper than the specified `*`print-level`*`, a hash sign (#) is
printed.
For example:
user=> (binding [*print-level* 2] (pprint '(a b (c d) ((e) ((f d) g)))))
(a b (c d) (# #))
nil
user=>
*`*`print-length`*`*: Default: *nil*
As with the regular Clojure print function, this variable controls the
number of items that are printed at each layer of structure. When a
layer has too many items, elipses (...) are displayed.
For example:
user=> (defn foo [x] (for [i (range x) ] (range 1 (- x (dec i)))))
#'user/foo
user=> (binding [*print-length* 6] (pprint (foo 10)))
((1 2 3 4 5 6 ...)
(1 2 3 4 5 6 ...)
(1 2 3 4 5 6 ...)
(1 2 3 4 5 6 ...)
(1 2 3 4 5 6)
(1 2 3 4 5)
...)
nil
user=>
## Custom Dispatch Functions
Using custom dispatch, you can easily create your own formatted output
for structured data. Examples included with the pretty printer show
how to use custom dispatch to translate simple Clojure structures into
nicely formatted JSON and XML.
### Basic Concepts of Pretty Printing
In order to create custom dispatch functions, you need to understand
the fundamentals of pretty printing. The clojure pretty printer is
based on the XP pretty printer algorithm (used in many Lisps including
Common Lisp) which supports sophisticated decision-making about line
breaking and indentation with reasonable performance even for very
large structures. The XP algorithm is documented in the paper,
[http://dspace.mit.edu/handle/1721.1/6504 XP. A Common Lisp Pretty
Printing System].
The Clojure implementation of XP is similar in spirit to the Common
Lisp implementation, but the details of the interface are somewhat
different. The result is that writing custom dispatch in Clojure is
more "Clojure-y."
There are three key concepts to understand when creating custom pretty
printing functions: _logical blocks_, _conditional newlines_, and
_indentation_.
A _logical block_ marks a set of output that should be thought about
as a single unit by the pretty printer. Logical blocks can contain
other logical blocks (that is, they nest). As a simple example, when
printing list structure, every sublist will typically be a logical
block.
_Conditional newlines_ tell the pretty printer where it can insert
line breaks and how to make the decisions about when to do it. There
are four types of conditional newline:
* Linear newlines tell the pretty printer to insert a newline in a
place whenever the enclosing logical block won't fit on a single
line. Linear newlines are an all-or-nothing proposition; if the
logical block doesn't fit on a single line, *all* the linear
newlines are emitted as actual newlines.
* Fill newlines tell the pretty printer that it should fit as many
chunks of the logical block as possible on this line and then emit
a newline.
* Mandatory newlines tell the pretty printer to emit a newline
regardless of where it is in the output line.
* Miser newlines tell the pretty printer to emit a newline if the
output column is in the miser region (as defined by the pretty
printer variable `*`pprint-miser-width`*`). This allows you to
define special behavior as the output gets heavily nested near the
right margin.
_Indentation_ commands allow you to specify how wrapped lines should
be indented. Indentation can be relative to either the start column of
the current logical block or the current column position of the output.
(This section is still incomplete...)
## Current limitations and future plans
This is an early version release of the pretty printer and there is
plenty that is yet to come.
Here are some examples:
* Support all the types and forms in Clojure (most of the way there now).
* Support for limiting pretty printing based on line counts.
* Support for circular and shared substructure detection.
* Finishing the integration with the format function (support for ~/ and tabular pretty printing).
* Performance! (Not much thought has been made to making this go fast, but there are a bunch of pretty obvious speedups to be had.)
* Handle Java objects intelligently
Please let me know about anything that's not working right, anything that
should work differently, or the feature you think should be at the top
of my list.
clojure1.2_1.2.1+dfsg.orig/epl-v10.html 0000664 0000000 0000000 00000031165 11575623476 0017437 0 ustar 00root root 0000000 0000000
Eclipse Public License - Version 1.0
Eclipse Public License - v 1.0
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR
DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a) in the case of the initial Contributor, the initial
code and documentation distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program
originate from and are distributed by that particular Contributor. A
Contribution 'originates' from a Contributor if it was added to the
Program by such Contributor itself or anyone acting on such
Contributor's behalf. Contributions do not include additions to the
Program which: (i) are separate modules of software distributed in
conjunction with the Program under their own license agreement, and (ii)
are not derivative works of the Program.
"Contributor" means any person or entity that distributes
the Program.
"Licensed Patents" mean patent claims licensable by a
Contributor which are necessarily infringed by the use or sale of its
Contribution alone or when combined with the Program.
"Program" means the Contributions distributed in accordance
with this Agreement.
"Recipient" means anyone who receives the Program under
this Agreement, including all Contributors.
2. GRANT OF RIGHTS
a) Subject to the terms of this Agreement, each
Contributor hereby grants Recipient a non-exclusive, worldwide,
royalty-free copyright license to reproduce, prepare derivative works
of, publicly display, publicly perform, distribute and sublicense the
Contribution of such Contributor, if any, and such derivative works, in
source code and object code form.
b) Subject to the terms of this Agreement, each
Contributor hereby grants Recipient a non-exclusive, worldwide,
royalty-free patent license under Licensed Patents to make, use, sell,
offer to sell, import and otherwise transfer the Contribution of such
Contributor, if any, in source code and object code form. This patent
license shall apply to the combination of the Contribution and the
Program if, at the time the Contribution is added by the Contributor,
such addition of the Contribution causes such combination to be covered
by the Licensed Patents. The patent license shall not apply to any other
combinations which include the Contribution. No hardware per se is
licensed hereunder.
c) Recipient understands that although each Contributor
grants the licenses to its Contributions set forth herein, no assurances
are provided by any Contributor that the Program does not infringe the
patent or other intellectual property rights of any other entity. Each
Contributor disclaims any liability to Recipient for claims brought by
any other entity based on infringement of intellectual property rights
or otherwise. As a condition to exercising the rights and licenses
granted hereunder, each Recipient hereby assumes sole responsibility to
secure any other intellectual property rights needed, if any. For
example, if a third party patent license is required to allow Recipient
to distribute the Program, it is Recipient's responsibility to acquire
that license before distributing the Program.
d) Each Contributor represents that to its knowledge it
has sufficient copyright rights in its Contribution, if any, to grant
the copyright license set forth in this Agreement.
3. REQUIREMENTS
A Contributor may choose to distribute the Program in object code
form under its own license agreement, provided that:
a) it complies with the terms and conditions of this
Agreement; and
b) its license agreement:
i) effectively disclaims on behalf of all Contributors
all warranties and conditions, express and implied, including warranties
or conditions of title and non-infringement, and implied warranties or
conditions of merchantability and fitness for a particular purpose;
ii) effectively excludes on behalf of all Contributors
all liability for damages, including direct, indirect, special,
incidental and consequential damages, such as lost profits;
iii) states that any provisions which differ from this
Agreement are offered by that Contributor alone and not by any other
party; and
iv) states that source code for the Program is available
from such Contributor, and informs licensees how to obtain it in a
reasonable manner on or through a medium customarily used for software
exchange.
When the Program is made available in source code form:
a) it must be made available under this Agreement; and
b) a copy of this Agreement must be included with each
copy of the Program.
Contributors may not remove or alter any copyright notices contained
within the Program.
Each Contributor must identify itself as the originator of its
Contribution, if any, in a manner that reasonably allows subsequent
Recipients to identify the originator of the Contribution.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain
responsibilities with respect to end users, business partners and the
like. While this license is intended to facilitate the commercial use of
the Program, the Contributor who includes the Program in a commercial
product offering should do so in a manner which does not create
potential liability for other Contributors. Therefore, if a Contributor
includes the Program in a commercial product offering, such Contributor
("Commercial Contributor") hereby agrees to defend and
indemnify every other Contributor ("Indemnified Contributor")
against any losses, damages and costs (collectively "Losses")
arising from claims, lawsuits and other legal actions brought by a third
party against the Indemnified Contributor to the extent caused by the
acts or omissions of such Commercial Contributor in connection with its
distribution of the Program in a commercial product offering. The
obligations in this section do not apply to any claims or Losses
relating to any actual or alleged intellectual property infringement. In
order to qualify, an Indemnified Contributor must: a) promptly notify
the Commercial Contributor in writing of such claim, and b) allow the
Commercial Contributor to control, and cooperate with the Commercial
Contributor in, the defense and any related settlement negotiations. The
Indemnified Contributor may participate in any such claim at its own
expense.
For example, a Contributor might include the Program in a commercial
product offering, Product X. That Contributor is then a Commercial
Contributor. If that Commercial Contributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Contributor's responsibility
alone. Under this section, the Commercial Contributor would have to
defend claims against the other Contributors related to those
performance claims and warranties, and if a court requires any other
Contributor to pay any damages as a result, the Commercial Contributor
must pay those damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION,
ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
responsible for determining the appropriateness of using and
distributing the Program and assumes all risks associated with its
exercise of rights under this Agreement , including but not limited to
the risks and costs of program errors, compliance with applicable laws,
damage to or loss of data, programs or equipment, and unavailability or
interruption of operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. GENERAL
If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further action
by the parties hereto, such provision shall be reformed to the minimum
extent necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against any entity
(including a cross-claim or counterclaim in a lawsuit) alleging that the
Program itself (excluding combinations of the Program with other
software or hardware) infringes such Recipient's patent(s), then such
Recipient's rights granted under Section 2(b) shall terminate as of the
date such litigation is filed.
All Recipient's rights under this Agreement shall terminate if it
fails to comply with any of the material terms or conditions of this
Agreement and does not cure such failure in a reasonable period of time
after becoming aware of such noncompliance. If all Recipient's rights
under this Agreement terminate, Recipient agrees to cease use and
distribution of the Program as soon as reasonably practicable. However,
Recipient's obligations under this Agreement and any licenses granted by
Recipient relating to the Program shall continue and survive.
Everyone is permitted to copy and distribute copies of this
Agreement, but in order to avoid inconsistency the Agreement is
copyrighted and may only be modified in the following manner. The
Agreement Steward reserves the right to publish new versions (including
revisions) of this Agreement from time to time. No one other than the
Agreement Steward has the right to modify this Agreement. The Eclipse
Foundation is the initial Agreement Steward. The Eclipse Foundation may
assign the responsibility to serve as the Agreement Steward to a
suitable separate entity. Each new version of the Agreement will be
given a distinguishing version number. The Program (including
Contributions) may always be distributed subject to the version of the
Agreement under which it was received. In addition, after a new version
of the Agreement is published, Contributor may elect to distribute the
Program (including its Contributions) under the new version. Except as
expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
rights or licenses to the intellectual property of any Contributor under
this Agreement, whether expressly, by implication, estoppel or
otherwise. All rights in the Program not expressly granted under this
Agreement are reserved.
This Agreement is governed by the laws of the State of New York and
the intellectual property laws of the United States of America. No party
to this Agreement will bring a legal action under this Agreement more
than one year after the cause of action arose. Each party waives its
rights to a jury trial in any resulting litigation.
clojure1.2_1.2.1+dfsg.orig/pom-template.xml 0000664 0000000 0000000 00000001474 11575623476 0020513 0 ustar 00root root 0000000 0000000
4.0.0
org.clojure
clojure
clojure
@clojure-version@
http://clojure.org/
Clojure core environment and runtime library.
Eclipse Public License 1.0
http://opensource.org/licenses/eclipse-1.0.php
repo
clojure1.2_1.2.1+dfsg.orig/readme.txt 0000664 0000000 0000000 00000004401 11575623476 0017354 0 ustar 00root root 0000000 0000000 * Clojure
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
Docs: http://clojure.org
Feedback: http://groups.google.com/group/clojure
To Run: java -cp clojure.jar clojure.main
To Build: ant
--------------------------------------------------------------------------
This program uses the ASM bytecode engineering library which is distributed
with the following notice:
Copyright (c) 2000-2005 INRIA, France Telecom
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE. clojure1.2_1.2.1+dfsg.orig/release.sh 0000664 0000000 0000000 00000002035 11575623476 0017333 0 ustar 00root root 0000000 0000000 #!/bin/bash
if [ -z $3 ]; then
echo 'Usage:
(checkout onto correct branch)
(edit changes.txt)
(optional: set CLOJURE_DEPLOY_URL for nonstandard location)
release.sh (qualifier)'
exit 0
fi
MAJOR_VERSION=$1
MINOR_VERSION=$2
INCREMENTAL_VERSION=$3
QUALIFIER=$4
echo "clojure.version.major=$MAJOR_VERSION
clojure.version.minor=$MINOR_VERSION
clojure.version.incremental=$INCREMENTAL_VERSION
clojure.version.qualifier=$QUALIFIER
clojure.version.interim=false" >src/clj/clojure/version.properties
if [ -z $QUALIFIER ]; then
VERSION="$MAJOR_VERSION.$MINOR_VERSION.$INCREMENTAL_VERSION"
else
VERSION="$MAJOR_VERSION.$MINOR_VERSION.$INCREMENTAL_VERSION-$QUALIFIER"
fi
git commit -a -m "[Automated release] Clojure $VERSION"
git tag -a -m "$VERSION" $VERSION
if [ -z $CLOJURE_DEPLOY_URL ]; then
CLOJURE_DEPLOY_URL=scp://build.clojure.org/srv/www/releases
fi
ant release -Ddeployment.url=$CLOJURE_DEPLOY_URL
echo "Build is complete. git push if you are satisfied with the result."
clojure1.2_1.2.1+dfsg.orig/src/ 0000775 0000000 0000000 00000000000 11575623476 0016146 5 ustar 00root root 0000000 0000000 clojure1.2_1.2.1+dfsg.orig/src/clj/ 0000775 0000000 0000000 00000000000 11575623476 0016716 5 ustar 00root root 0000000 0000000 clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/ 0000775 0000000 0000000 00000000000 11575623476 0020361 5 ustar 00root root 0000000 0000000 clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/core.clj 0000664 0000000 0000000 00000563073 11575623476 0022021 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns ^{:doc "The core Clojure language."
:author "Rich Hickey"}
clojure.core)
(def unquote)
(def unquote-splicing)
(def
^{:arglists '([& items])
:doc "Creates a new list containing the items."
:added "1.0"}
list (. clojure.lang.PersistentList creator))
(def
^{:arglists '([x seq])
:doc "Returns a new seq where x is the first element and seq is
the rest."
:added "1.0"}
cons (fn* cons [x seq] (. clojure.lang.RT (cons x seq))))
;during bootstrap we don't have destructuring let, loop or fn, will redefine later
(def
^{:macro true
:added "1.0"}
let (fn* let [&form &env & decl] (cons 'let* decl)))
(def
^{:macro true
:added "1.0"}
loop (fn* loop [&form &env & decl] (cons 'loop* decl)))
(def
^{:macro true
:added "1.0"}
fn (fn* fn [&form &env & decl]
(.withMeta ^clojure.lang.IObj (cons 'fn* decl)
(.meta ^clojure.lang.IMeta &form))))
(def
^{:arglists '([coll])
:doc "Returns the first item in the collection. Calls seq on its
argument. If coll is nil, returns nil."
:added "1.0"}
first (fn first [coll] (. clojure.lang.RT (first coll))))
(def
^{:arglists '([coll])
:tag clojure.lang.ISeq
:doc "Returns a seq of the items after the first. Calls seq on its
argument. If there are no more items, returns nil."
:added "1.0"}
next (fn next [x] (. clojure.lang.RT (next x))))
(def
^{:arglists '([coll])
:tag clojure.lang.ISeq
:doc "Returns a possibly empty seq of the items after the first. Calls seq on its
argument."
:added "1.0"}
rest (fn rest [x] (. clojure.lang.RT (more x))))
(def
^{:arglists '([coll x] [coll x & xs])
:doc "conj[oin]. Returns a new collection with the xs
'added'. (conj nil item) returns (item). The 'addition' may
happen at different 'places' depending on the concrete type."
:added "1.0"}
conj (fn conj
([coll x] (. clojure.lang.RT (conj coll x)))
([coll x & xs]
(if xs
(recur (conj coll x) (first xs) (next xs))
(conj coll x)))))
(def
^{:doc "Same as (first (next x))"
:arglists '([x])
:added "1.0"}
second (fn second [x] (first (next x))))
(def
^{:doc "Same as (first (first x))"
:arglists '([x])
:added "1.0"}
ffirst (fn ffirst [x] (first (first x))))
(def
^{:doc "Same as (next (first x))"
:arglists '([x])
:added "1.0"}
nfirst (fn nfirst [x] (next (first x))))
(def
^{:doc "Same as (first (next x))"
:arglists '([x])
:added "1.0"}
fnext (fn fnext [x] (first (next x))))
(def
^{:doc "Same as (next (next x))"
:arglists '([x])
:added "1.0"}
nnext (fn nnext [x] (next (next x))))
(def
^{:arglists '([coll])
:doc "Returns a seq on the collection. If the collection is
empty, returns nil. (seq nil) returns nil. seq also works on
Strings, native Java arrays (of reference types) and any objects
that implement Iterable."
:tag clojure.lang.ISeq
:added "1.0"}
seq (fn seq [coll] (. clojure.lang.RT (seq coll))))
(def
^{:arglists '([^Class c x])
:doc "Evaluates x and tests if it is an instance of the class
c. Returns true or false"
:added "1.0"}
instance? (fn instance? [^Class c x] (. c (isInstance x))))
(def
^{:arglists '([x])
:doc "Return true if x implements ISeq"
:added "1.0"}
seq? (fn seq? [x] (instance? clojure.lang.ISeq x)))
(def
^{:arglists '([x])
:doc "Return true if x is a Character"
:added "1.0"}
char? (fn char? [x] (instance? Character x)))
(def
^{:arglists '([x])
:doc "Return true if x is a String"
:added "1.0"}
string? (fn string? [x] (instance? String x)))
(def
^{:arglists '([x])
:doc "Return true if x implements IPersistentMap"
:added "1.0"}
map? (fn map? [x] (instance? clojure.lang.IPersistentMap x)))
(def
^{:arglists '([x])
:doc "Return true if x implements IPersistentVector"
:added "1.0"}
vector? (fn vector? [x] (instance? clojure.lang.IPersistentVector x)))
(def
^{:arglists '([map key val] [map key val & kvs])
:doc "assoc[iate]. When applied to a map, returns a new map of the
same (hashed/sorted) type, that contains the mapping of key(s) to
val(s). When applied to a vector, returns a new vector that
contains val at index. Note - index must be <= (count vector)."
:added "1.0"}
assoc
(fn assoc
([map key val] (. clojure.lang.RT (assoc map key val)))
([map key val & kvs]
(let [ret (assoc map key val)]
(if kvs
(recur ret (first kvs) (second kvs) (nnext kvs))
ret)))))
;;;;;;;;;;;;;;;;; metadata ;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def
^{:arglists '([obj])
:doc "Returns the metadata of obj, returns nil if there is no metadata."
:added "1.0"}
meta (fn meta [x]
(if (instance? clojure.lang.IMeta x)
(. ^clojure.lang.IMeta x (meta)))))
(def
^{:arglists '([^clojure.lang.IObj obj m])
:doc "Returns an object of the same type and value as obj, with
map m as its metadata."
:added "1.0"}
with-meta (fn with-meta [^clojure.lang.IObj x m]
(. x (withMeta m))))
(def ^{:private true :dynamic true}
assert-valid-fdecl (fn [fdecl]))
(def
^{:private true}
sigs
(fn [fdecl]
(assert-valid-fdecl fdecl)
(let [asig
(fn [fdecl]
(let [arglist (first fdecl)
;elide implicit macro args
arglist (if (clojure.lang.Util/equals '&form (first arglist))
(clojure.lang.RT/subvec arglist 2 (clojure.lang.RT/count arglist))
arglist)
body (next fdecl)]
(if (map? (first body))
(if (next body)
(with-meta arglist (conj (if (meta arglist) (meta arglist) {}) (first body)))
arglist)
arglist)))]
(if (seq? (first fdecl))
(loop [ret [] fdecls fdecl]
(if fdecls
(recur (conj ret (asig (first fdecls))) (next fdecls))
(seq ret)))
(list (asig fdecl))))))
(def
^{:arglists '([coll])
:doc "Return the last item in coll, in linear time"
:added "1.0"}
last (fn last [s]
(if (next s)
(recur (next s))
(first s))))
(def
^{:arglists '([coll])
:doc "Return a seq of all but the last item in coll, in linear time"
:added "1.0"}
butlast (fn butlast [s]
(loop [ret [] s s]
(if (next s)
(recur (conj ret (first s)) (next s))
(seq ret)))))
(def
^{:doc "Same as (def name (fn [params* ] exprs*)) or (def
name (fn ([params* ] exprs*)+)) with any doc-string or attrs added
to the var metadata"
:arglists '([name doc-string? attr-map? [params*] body]
[name doc-string? attr-map? ([params*] body)+ attr-map?])
:added "1.0"}
defn (fn defn [&form &env name & fdecl]
(let [m (if (string? (first fdecl))
{:doc (first fdecl)}
{})
fdecl (if (string? (first fdecl))
(next fdecl)
fdecl)
m (if (map? (first fdecl))
(conj m (first fdecl))
m)
fdecl (if (map? (first fdecl))
(next fdecl)
fdecl)
fdecl (if (vector? (first fdecl))
(list fdecl)
fdecl)
m (if (map? (last fdecl))
(conj m (last fdecl))
m)
fdecl (if (map? (last fdecl))
(butlast fdecl)
fdecl)
m (conj {:arglists (list 'quote (sigs fdecl))} m)
m (let [inline (:inline m)
ifn (first inline)
iname (second inline)]
;; same as: (if (and (= 'fn ifn) (not (symbol? iname))) ...)
(if (if (clojure.lang.Util/equiv 'fn ifn)
(if (instance? clojure.lang.Symbol iname) false true))
;; inserts the same fn name to the inline fn if it does not have one
(assoc m :inline (cons ifn (cons (clojure.lang.Symbol/intern (.concat (.getName name) "__inliner"))
(next inline))))
m))
m (conj (if (meta name) (meta name) {}) m)]
(list 'def (with-meta name m)
(list '.withMeta (cons `fn (cons name fdecl)) (list '.meta (list 'var name)))))))
(. (var defn) (setMacro))
(defn cast
"Throws a ClassCastException if x is not a c, else returns x."
{:added "1.0"}
[^Class c x]
(. c (cast x)))
(defn to-array
"Returns an array of Objects containing the contents of coll, which
can be any Collection. Maps to java.util.Collection.toArray()."
{:tag "[Ljava.lang.Object;"
:added "1.0"}
[coll] (. clojure.lang.RT (toArray coll)))
(defn vector
"Creates a new vector containing the args."
{:added "1.0"}
([] [])
([a] [a])
([a b] [a b])
([a b c] [a b c])
([a b c d] [a b c d])
([a b c d & args]
(. clojure.lang.LazilyPersistentVector (create (cons a (cons b (cons c (cons d args))))))))
(defn vec
"Creates a new vector containing the contents of coll."
{:added "1.0"}
([coll]
(if (instance? java.util.Collection coll)
(clojure.lang.LazilyPersistentVector/create coll)
(. clojure.lang.LazilyPersistentVector (createOwning (to-array coll))))))
(defn hash-map
"keyval => key val
Returns a new hash map with supplied mappings."
{:added "1.0"}
([] {})
([& keyvals]
(. clojure.lang.PersistentHashMap (createWithCheck keyvals))))
(defn hash-set
"Returns a new hash set with supplied keys."
{:added "1.0"}
([] #{})
([& keys]
(clojure.lang.PersistentHashSet/createWithCheck keys)))
(defn sorted-map
"keyval => key val
Returns a new sorted map with supplied mappings."
{:added "1.0"}
([& keyvals]
(clojure.lang.PersistentTreeMap/create keyvals)))
(defn sorted-map-by
"keyval => key val
Returns a new sorted map with supplied mappings, using the supplied comparator."
{:added "1.0"}
([comparator & keyvals]
(clojure.lang.PersistentTreeMap/create comparator keyvals)))
(defn sorted-set
"Returns a new sorted set with supplied keys."
{:added "1.0"}
([& keys]
(clojure.lang.PersistentTreeSet/create keys)))
(defn sorted-set-by
"Returns a new sorted set with supplied keys, using the supplied comparator."
{:added "1.1"}
([comparator & keys]
(clojure.lang.PersistentTreeSet/create comparator keys)))
;;;;;;;;;;;;;;;;;;;;
(defn nil?
"Returns true if x is nil, false otherwise."
{:tag Boolean
:added "1.0"}
[x] (clojure.lang.Util/identical x nil))
(def
^{:doc "Like defn, but the resulting function name is declared as a
macro and will be used as a macro by the compiler when it is
called."
:arglists '([name doc-string? attr-map? [params*] body]
[name doc-string? attr-map? ([params*] body)+ attr-map?])
:added "1.0"}
defmacro (fn [&form &env
name & args]
(let [prefix (loop [p (list name) args args]
(let [f (first args)]
(if (string? f)
(recur (cons f p) (next args))
(if (map? f)
(recur (cons f p) (next args))
p))))
fdecl (loop [fd args]
(if (string? (first fd))
(recur (next fd))
(if (map? (first fd))
(recur (next fd))
fd)))
fdecl (if (vector? (first fdecl))
(list fdecl)
fdecl)
add-implicit-args (fn [fd]
(let [args (first fd)]
(cons (vec (cons '&form (cons '&env args))) (next fd))))
add-args (fn [acc ds]
(if (nil? ds)
acc
(let [d (first ds)]
(if (map? d)
(conj acc d)
(recur (conj acc (add-implicit-args d)) (next ds))))))
fdecl (seq (add-args [] fdecl))
decl (loop [p prefix d fdecl]
(if p
(recur (next p) (cons (first p) d))
d))]
(list 'do
(cons `defn decl)
(list '. (list 'var name) '(setMacro))
(list 'var name)))))
(. (var defmacro) (setMacro))
(defmacro when
"Evaluates test. If logical true, evaluates body in an implicit do."
{:added "1.0"}
[test & body]
(list 'if test (cons 'do body)))
(defmacro when-not
"Evaluates test. If logical false, evaluates body in an implicit do."
{:added "1.0"}
[test & body]
(list 'if test nil (cons 'do body)))
(defn false?
"Returns true if x is the value false, false otherwise."
{:tag Boolean,
:added "1.0"}
[x] (clojure.lang.Util/identical x false))
(defn true?
"Returns true if x is the value true, false otherwise."
{:tag Boolean,
:added "1.0"}
[x] (clojure.lang.Util/identical x true))
(defn not
"Returns true if x is logical false, false otherwise."
{:tag Boolean
:added "1.0"}
[x] (if x false true))
(defn str
"With no args, returns the empty string. With one arg x, returns
x.toString(). (str nil) returns the empty string. With more than
one arg, returns the concatenation of the str values of the args."
{:tag String
:added "1.0"}
([] "")
([^Object x]
(if (nil? x) "" (. x (toString))))
([x & ys]
((fn [^StringBuilder sb more]
(if more
(recur (. sb (append (str (first more)))) (next more))
(str sb)))
(new StringBuilder ^String (str x)) ys)))
(defn symbol?
"Return true if x is a Symbol"
{:added "1.0"}
[x] (instance? clojure.lang.Symbol x))
(defn keyword?
"Return true if x is a Keyword"
{:added "1.0"}
[x] (instance? clojure.lang.Keyword x))
(defn symbol
"Returns a Symbol with the given namespace and name."
{:tag clojure.lang.Symbol
:added "1.0"}
([name] (if (symbol? name) name (clojure.lang.Symbol/intern name)))
([ns name] (clojure.lang.Symbol/intern ns name)))
(defn gensym
"Returns a new symbol with a unique name. If a prefix string is
supplied, the name is prefix# where # is some unique number. If
prefix is not supplied, the prefix is 'G__'."
{:added "1.0"}
([] (gensym "G__"))
([prefix-string] (. clojure.lang.Symbol (intern (str prefix-string (str (. clojure.lang.RT (nextID))))))))
(defmacro cond
"Takes a set of test/expr pairs. It evaluates each test one at a
time. If a test returns logical true, cond evaluates and returns
the value of the corresponding expr and doesn't evaluate any of the
other tests or exprs. (cond) returns nil."
{:added "1.0"}
[& clauses]
(when clauses
(list 'if (first clauses)
(if (next clauses)
(second clauses)
(throw (IllegalArgumentException.
"cond requires an even number of forms")))
(cons 'clojure.core/cond (next (next clauses))))))
(defn keyword
"Returns a Keyword with the given namespace and name. Do not use :
in the keyword strings, it will be added automatically."
{:tag clojure.lang.Keyword
:added "1.0"}
([name] (cond (keyword? name) name
(symbol? name) (clojure.lang.Keyword/intern ^clojure.lang.Symbol name)
(string? name) (clojure.lang.Keyword/intern ^String name)))
([ns name] (clojure.lang.Keyword/intern ns name)))
(defn spread
{:private true}
[arglist]
(cond
(nil? arglist) nil
(nil? (next arglist)) (seq (first arglist))
:else (cons (first arglist) (spread (next arglist)))))
(defn list*
"Creates a new list containing the items prepended to the rest, the
last of which will be treated as a sequence."
{:added "1.0"}
([args] (seq args))
([a args] (cons a args))
([a b args] (cons a (cons b args)))
([a b c args] (cons a (cons b (cons c args))))
([a b c d & more]
(cons a (cons b (cons c (cons d (spread more)))))))
(defn apply
"Applies fn f to the argument list formed by prepending args to argseq."
{:arglists '([f args* argseq])
:added "1.0"}
([^clojure.lang.IFn f args]
(. f (applyTo (seq args))))
([^clojure.lang.IFn f x args]
(. f (applyTo (list* x args))))
([^clojure.lang.IFn f x y args]
(. f (applyTo (list* x y args))))
([^clojure.lang.IFn f x y z args]
(. f (applyTo (list* x y z args))))
([^clojure.lang.IFn f a b c d & args]
(. f (applyTo (cons a (cons b (cons c (cons d (spread args)))))))))
(defn vary-meta
"Returns an object of the same type and value as obj, with
(apply f (meta obj) args) as its metadata."
{:added "1.0"}
[obj f & args]
(with-meta obj (apply f (meta obj) args)))
(defmacro lazy-seq
"Takes a body of expressions that returns an ISeq or nil, and yields
a Seqable object that will invoke the body only the first time seq
is called, and will cache the result and return it on all subsequent
seq calls."
{:added "1.0"}
[& body]
(list 'new 'clojure.lang.LazySeq (list* '^{:once true} fn* [] body)))
(defn ^clojure.lang.ChunkBuffer chunk-buffer [capacity]
(clojure.lang.ChunkBuffer. capacity))
(defn chunk-append [^clojure.lang.ChunkBuffer b x]
(.add b x))
(defn chunk [^clojure.lang.ChunkBuffer b]
(.chunk b))
(defn ^clojure.lang.IChunk chunk-first [^clojure.lang.IChunkedSeq s]
(.chunkedFirst s))
(defn ^clojure.lang.ISeq chunk-rest [^clojure.lang.IChunkedSeq s]
(.chunkedMore s))
(defn ^clojure.lang.ISeq chunk-next [^clojure.lang.IChunkedSeq s]
(.chunkedNext s))
(defn chunk-cons [chunk rest]
(if (clojure.lang.Numbers/isZero (clojure.lang.RT/count chunk))
rest
(clojure.lang.ChunkedCons. chunk rest)))
(defn chunked-seq? [s]
(instance? clojure.lang.IChunkedSeq s))
(defn concat
"Returns a lazy seq representing the concatenation of the elements in the supplied colls."
{:added "1.0"}
([] (lazy-seq nil))
([x] (lazy-seq x))
([x y]
(lazy-seq
(let [s (seq x)]
(if s
(if (chunked-seq? s)
(chunk-cons (chunk-first s) (concat (chunk-rest s) y))
(cons (first s) (concat (rest s) y)))
y))))
([x y & zs]
(let [cat (fn cat [xys zs]
(lazy-seq
(let [xys (seq xys)]
(if xys
(if (chunked-seq? xys)
(chunk-cons (chunk-first xys)
(cat (chunk-rest xys) zs))
(cons (first xys) (cat (rest xys) zs)))
(when zs
(cat (first zs) (next zs)))))))]
(cat (concat x y) zs))))
;;;;;;;;;;;;;;;;at this point all the support for syntax-quote exists;;;;;;;;;;;;;;;;;;;;;;
(defmacro delay
"Takes a body of expressions and yields a Delay object that will
invoke the body only the first time it is forced (with force or deref/@), and
will cache the result and return it on all subsequent force
calls."
{:added "1.0"}
[& body]
(list 'new 'clojure.lang.Delay (list* `^{:once true} fn* [] body)))
(defn delay?
"returns true if x is a Delay created with delay"
{:added "1.0"}
[x] (instance? clojure.lang.Delay x))
(defn force
"If x is a Delay, returns the (possibly cached) value of its expression, else returns x"
{:added "1.0"}
[x] (. clojure.lang.Delay (force x)))
(defmacro if-not
"Evaluates test. If logical false, evaluates and returns then expr,
otherwise else expr, if supplied, else nil."
{:added "1.0"}
([test then] `(if-not ~test ~then nil))
([test then else]
`(if (not ~test) ~then ~else)))
(defn identical?
"Tests if 2 arguments are the same object"
{:inline (fn [x y] `(. clojure.lang.Util identical ~x ~y))
:inline-arities #{2}
:added "1.0"}
([x y] (clojure.lang.Util/identical x y)))
(defn =
"Equality. Returns true if x equals y, false if not. Same as
Java x.equals(y) except it also works for nil, and compares
numbers and collections in a type-independent manner. Clojure's immutable data
structures define equals() (and thus =) as a value, not an identity,
comparison."
{:inline (fn [x y] `(. clojure.lang.Util equiv ~x ~y))
:inline-arities #{2}
:added "1.0"}
([x] true)
([x y] (clojure.lang.Util/equiv x y))
([x y & more]
(if (= x y)
(if (next more)
(recur y (first more) (next more))
(= y (first more)))
false)))
(defn not=
"Same as (not (= obj1 obj2))"
{:tag Boolean
:added "1.0"}
([x] false)
([x y] (not (= x y)))
([x y & more]
(not (apply = x y more))))
(defn compare
"Comparator. Returns a negative number, zero, or a positive number
when x is logically 'less than', 'equal to', or 'greater than'
y. Same as Java x.compareTo(y) except it also works for nil, and
compares numbers and collections in a type-independent manner. x
must implement Comparable"
{
:inline (fn [x y] `(. clojure.lang.Util compare ~x ~y))
:added "1.0"}
[x y] (. clojure.lang.Util (compare x y)))
(defmacro and
"Evaluates exprs one at a time, from left to right. If a form
returns logical false (nil or false), and returns that value and
doesn't evaluate any of the other expressions, otherwise it returns
the value of the last expr. (and) returns true."
{:added "1.0"}
([] true)
([x] x)
([x & next]
`(let [and# ~x]
(if and# (and ~@next) and#))))
(defmacro or
"Evaluates exprs one at a time, from left to right. If a form
returns a logical true value, or returns that value and doesn't
evaluate any of the other expressions, otherwise it returns the
value of the last expression. (or) returns nil."
{:added "1.0"}
([] nil)
([x] x)
([x & next]
`(let [or# ~x]
(if or# or# (or ~@next)))))
;;;;;;;;;;;;;;;;;;; sequence fns ;;;;;;;;;;;;;;;;;;;;;;;
(defn zero?
"Returns true if num is zero, else false"
{
:inline (fn [x] `(. clojure.lang.Numbers (isZero ~x)))
:added "1.0"}
[x] (. clojure.lang.Numbers (isZero x)))
(defn count
"Returns the number of items in the collection. (count nil) returns
0. Also works on strings, arrays, and Java Collections and Maps"
{
:inline (fn [x] `(. clojure.lang.RT (count ~x)))
:added "1.0"}
[coll] (clojure.lang.RT/count coll))
(defn int
"Coerce to int"
{
:inline (fn [x] `(. clojure.lang.RT (intCast ~x)))
:added "1.0"}
[x] (. clojure.lang.RT (intCast x)))
(defn nth
"Returns the value at the index. get returns nil if index out of
bounds, nth throws an exception unless not-found is supplied. nth
also works for strings, Java arrays, regex Matchers and Lists, and,
in O(n) time, for sequences."
{:inline (fn [c i & nf] `(. clojure.lang.RT (nth ~c ~i ~@nf)))
:inline-arities #{2 3}
:added "1.0"}
([coll index] (. clojure.lang.RT (nth coll index)))
([coll index not-found] (. clojure.lang.RT (nth coll index not-found))))
(defn <
"Returns non-nil if nums are in monotonically increasing order,
otherwise false."
{:inline (fn [x y] `(. clojure.lang.Numbers (lt ~x ~y)))
:inline-arities #{2}
:added "1.0"}
([x] true)
([x y] (. clojure.lang.Numbers (lt x y)))
([x y & more]
(if (< x y)
(if (next more)
(recur y (first more) (next more))
(< y (first more)))
false)))
(defn inc
"Returns a number one greater than num."
{:inline (fn [x] `(. clojure.lang.Numbers (inc ~x)))
:added "1.0"}
[x] (. clojure.lang.Numbers (inc x)))
;; reduce is defined again later after InternalReduce loads
(def
^{:arglists '([f coll] [f val coll])
:doc "f should be a function of 2 arguments. If val is not supplied,
returns the result of applying f to the first 2 items in coll, then
applying f to that result and the 3rd item, etc. If coll contains no
items, f must accept no arguments as well, and reduce returns the
result of calling f with no arguments. If coll has only 1 item, it
is returned and f is not called. If val is supplied, returns the
result of applying f to val and the first item in coll, then
applying f to that result and the 2nd item, etc. If coll contains no
items, returns val and f is not called."
:added "1.0"}
reduce
(fn r
([f coll]
(let [s (seq coll)]
(if s
(r f (first s) (next s))
(f))))
([f val coll]
(let [s (seq coll)]
(if s
(if (chunked-seq? s)
(recur f
(.reduce (chunk-first s) f val)
(chunk-next s))
(recur f (f val (first s)) (next s)))
val)))))
(defn reverse
"Returns a seq of the items in coll in reverse order. Not lazy."
{:added "1.0"}
[coll]
(reduce conj () coll))
;;math stuff
(defn +
"Returns the sum of nums. (+) returns 0."
{:inline (fn [x y] `(. clojure.lang.Numbers (add ~x ~y)))
:inline-arities #{2}
:added "1.0"}
([] 0)
([x] (cast Number x))
([x y] (. clojure.lang.Numbers (add x y)))
([x y & more]
(reduce + (+ x y) more)))
(defn *
"Returns the product of nums. (*) returns 1."
{:inline (fn [x y] `(. clojure.lang.Numbers (multiply ~x ~y)))
:inline-arities #{2}
:added "1.0"}
([] 1)
([x] (cast Number x))
([x y] (. clojure.lang.Numbers (multiply x y)))
([x y & more]
(reduce * (* x y) more)))
(defn /
"If no denominators are supplied, returns 1/numerator,
else returns numerator divided by all of the denominators."
{:inline (fn [x y] `(. clojure.lang.Numbers (divide ~x ~y)))
:inline-arities #{2}
:added "1.0"}
([x] (/ 1 x))
([x y] (. clojure.lang.Numbers (divide x y)))
([x y & more]
(reduce / (/ x y) more)))
(defn -
"If no ys are supplied, returns the negation of x, else subtracts
the ys from x and returns the result."
{:inline (fn [& args] `(. clojure.lang.Numbers (minus ~@args)))
:inline-arities #{1 2}
:added "1.0"}
([x] (. clojure.lang.Numbers (minus x)))
([x y] (. clojure.lang.Numbers (minus x y)))
([x y & more]
(reduce - (- x y) more)))
(defn <=
"Returns non-nil if nums are in monotonically non-decreasing order,
otherwise false."
{:inline (fn [x y] `(. clojure.lang.Numbers (lte ~x ~y)))
:inline-arities #{2}
:added "1.0"}
([x] true)
([x y] (. clojure.lang.Numbers (lte x y)))
([x y & more]
(if (<= x y)
(if (next more)
(recur y (first more) (next more))
(<= y (first more)))
false)))
(defn >
"Returns non-nil if nums are in monotonically decreasing order,
otherwise false."
{:inline (fn [x y] `(. clojure.lang.Numbers (gt ~x ~y)))
:inline-arities #{2}
:added "1.0"}
([x] true)
([x y] (. clojure.lang.Numbers (gt x y)))
([x y & more]
(if (> x y)
(if (next more)
(recur y (first more) (next more))
(> y (first more)))
false)))
(defn >=
"Returns non-nil if nums are in monotonically non-increasing order,
otherwise false."
{:inline (fn [x y] `(. clojure.lang.Numbers (gte ~x ~y)))
:inline-arities #{2}
:added "1.0"}
([x] true)
([x y] (. clojure.lang.Numbers (gte x y)))
([x y & more]
(if (>= x y)
(if (next more)
(recur y (first more) (next more))
(>= y (first more)))
false)))
(defn ==
"Returns non-nil if nums all have the same value, otherwise false"
{:inline (fn [x y] `(. clojure.lang.Numbers (equiv ~x ~y)))
:inline-arities #{2}
:added "1.0"}
([x] true)
([x y] (. clojure.lang.Numbers (equiv x y)))
([x y & more]
(if (== x y)
(if (next more)
(recur y (first more) (next more))
(== y (first more)))
false)))
(defn max
"Returns the greatest of the nums."
{:added "1.0"}
([x] x)
([x y] (if (> x y) x y))
([x y & more]
(reduce max (max x y) more)))
(defn min
"Returns the least of the nums."
{:added "1.0"}
([x] x)
([x y] (if (< x y) x y))
([x y & more]
(reduce min (min x y) more)))
(defn dec
"Returns a number one less than num."
{:inline (fn [x] `(. clojure.lang.Numbers (dec ~x)))
:added "1.0"}
[x] (. clojure.lang.Numbers (dec x)))
(defn unchecked-inc
"Returns a number one greater than x, an int or long.
Note - uses a primitive operator subject to overflow."
{:inline (fn [x] `(. clojure.lang.Numbers (unchecked_inc ~x)))
:added "1.0"}
[x] (. clojure.lang.Numbers (unchecked_inc x)))
(defn unchecked-dec
"Returns a number one less than x, an int or long.
Note - uses a primitive operator subject to overflow."
{:inline (fn [x] `(. clojure.lang.Numbers (unchecked_dec ~x)))
:added "1.0"}
[x] (. clojure.lang.Numbers (unchecked_dec x)))
(defn unchecked-negate
"Returns the negation of x, an int or long.
Note - uses a primitive operator subject to overflow."
{:inline (fn [x] `(. clojure.lang.Numbers (unchecked_negate ~x)))
:added "1.0"}
[x] (. clojure.lang.Numbers (unchecked_negate x)))
(defn unchecked-add
"Returns the sum of x and y, both int or long.
Note - uses a primitive operator subject to overflow."
{:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_add ~x ~y)))
:added "1.0"}
[x y] (. clojure.lang.Numbers (unchecked_add x y)))
(defn unchecked-subtract
"Returns the difference of x and y, both int or long.
Note - uses a primitive operator subject to overflow."
{:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_subtract ~x ~y)))
:added "1.0"}
[x y] (. clojure.lang.Numbers (unchecked_subtract x y)))
(defn unchecked-multiply
"Returns the product of x and y, both int or long.
Note - uses a primitive operator subject to overflow."
{:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_multiply ~x ~y)))
:added "1.0"}
[x y] (. clojure.lang.Numbers (unchecked_multiply x y)))
(defn unchecked-divide
"Returns the division of x by y, both int or long.
Note - uses a primitive operator subject to truncation."
{:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_divide ~x ~y)))
:added "1.0"}
[x y] (. clojure.lang.Numbers (unchecked_divide x y)))
(defn unchecked-remainder
"Returns the remainder of division of x by y, both int or long.
Note - uses a primitive operator subject to truncation."
{:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_remainder ~x ~y)))
:added "1.0"}
[x y] (. clojure.lang.Numbers (unchecked_remainder x y)))
(defn pos?
"Returns true if num is greater than zero, else false"
{
:inline (fn [x] `(. clojure.lang.Numbers (isPos ~x)))
:added "1.0"}
[x] (. clojure.lang.Numbers (isPos x)))
(defn neg?
"Returns true if num is less than zero, else false"
{
:inline (fn [x] `(. clojure.lang.Numbers (isNeg ~x)))
:added "1.0"}
[x] (. clojure.lang.Numbers (isNeg x)))
(defn quot
"quot[ient] of dividing numerator by denominator."
{:added "1.0"}
[num div]
(. clojure.lang.Numbers (quotient num div)))
(defn rem
"remainder of dividing numerator by denominator."
{:added "1.0"}
[num div]
(. clojure.lang.Numbers (remainder num div)))
(defn rationalize
"returns the rational value of num"
{:added "1.0"}
[num]
(. clojure.lang.Numbers (rationalize num)))
;;Bit ops
(defn bit-not
"Bitwise complement"
{:inline (fn [x] `(. clojure.lang.Numbers (not ~x)))
:added "1.0"}
[x] (. clojure.lang.Numbers not x))
(defn bit-and
"Bitwise and"
{:inline (fn [x y] `(. clojure.lang.Numbers (and ~x ~y)))
:added "1.0"}
[x y] (. clojure.lang.Numbers and x y))
(defn bit-or
"Bitwise or"
{:inline (fn [x y] `(. clojure.lang.Numbers (or ~x ~y)))
:added "1.0"}
[x y] (. clojure.lang.Numbers or x y))
(defn bit-xor
"Bitwise exclusive or"
{:inline (fn [x y] `(. clojure.lang.Numbers (xor ~x ~y)))
:added "1.0"}
[x y] (. clojure.lang.Numbers xor x y))
(defn bit-and-not
"Bitwise and with complement"
{:added "1.0"}
[x y] (. clojure.lang.Numbers andNot x y))
(defn bit-clear
"Clear bit at index n"
{:added "1.0"}
[x n] (. clojure.lang.Numbers clearBit x n))
(defn bit-set
"Set bit at index n"
{:added "1.0"}
[x n] (. clojure.lang.Numbers setBit x n))
(defn bit-flip
"Flip bit at index n"
{:added "1.0"}
[x n] (. clojure.lang.Numbers flipBit x n))
(defn bit-test
"Test bit at index n"
{:added "1.0"}
[x n] (. clojure.lang.Numbers testBit x n))
(defn bit-shift-left
"Bitwise shift left"
{:inline (fn [x n] `(. clojure.lang.Numbers (shiftLeft ~x ~n)))
:added "1.0"}
[x n] (. clojure.lang.Numbers shiftLeft x n))
(defn bit-shift-right
"Bitwise shift right"
{:inline (fn [x n] `(. clojure.lang.Numbers (shiftRight ~x ~n)))
:added "1.0"}
[x n] (. clojure.lang.Numbers shiftRight x n))
(defn even?
"Returns true if n is even, throws an exception if n is not an integer"
{:added "1.0"}
[n] (zero? (bit-and n 1)))
(defn odd?
"Returns true if n is odd, throws an exception if n is not an integer"
{:added "1.0"}
[n] (not (even? n)))
;;
(defn complement
"Takes a fn f and returns a fn that takes the same arguments as f,
has the same effects, if any, and returns the opposite truth value."
{:added "1.0"}
[f]
(fn
([] (not (f)))
([x] (not (f x)))
([x y] (not (f x y)))
([x y & zs] (not (apply f x y zs)))))
(defn constantly
"Returns a function that takes any number of arguments and returns x."
{:added "1.0"}
[x] (fn [& args] x))
(defn identity
"Returns its argument."
{:added "1.0"}
[x] x)
;;Collection stuff
;;list stuff
(defn peek
"For a list or queue, same as first, for a vector, same as, but much
more efficient than, last. If the collection is empty, returns nil."
{:added "1.0"}
[coll] (. clojure.lang.RT (peek coll)))
(defn pop
"For a list or queue, returns a new list/queue without the first
item, for a vector, returns a new vector without the last item. If
the collection is empty, throws an exception. Note - not the same
as next/butlast."
{:added "1.0"}
[coll] (. clojure.lang.RT (pop coll)))
;;map stuff
(defn contains?
"Returns true if key is present in the given collection, otherwise
returns false. Note that for numerically indexed collections like
vectors and Java arrays, this tests if the numeric key is within the
range of indexes. 'contains?' operates constant or logarithmic time;
it will not perform a linear search for a value. See also 'some'."
{:added "1.0"}
[coll key] (. clojure.lang.RT (contains coll key)))
(defn get
"Returns the value mapped to key, not-found or nil if key not present."
{:inline (fn [m k & nf] `(. clojure.lang.RT (get ~m ~k ~@nf)))
:inline-arities #{2 3}
:added "1.0"}
([map key]
(. clojure.lang.RT (get map key)))
([map key not-found]
(. clojure.lang.RT (get map key not-found))))
(defn dissoc
"dissoc[iate]. Returns a new map of the same (hashed/sorted) type,
that does not contain a mapping for key(s)."
{:added "1.0"}
([map] map)
([map key]
(. clojure.lang.RT (dissoc map key)))
([map key & ks]
(let [ret (dissoc map key)]
(if ks
(recur ret (first ks) (next ks))
ret))))
(defn disj
"disj[oin]. Returns a new set of the same (hashed/sorted) type, that
does not contain key(s)."
{:added "1.0"}
([set] set)
([^clojure.lang.IPersistentSet set key]
(when set
(. set (disjoin key))))
([set key & ks]
(when set
(let [ret (disj set key)]
(if ks
(recur ret (first ks) (next ks))
ret)))))
(defn find
"Returns the map entry for key, or nil if key not present."
{:added "1.0"}
[map key] (. clojure.lang.RT (find map key)))
(defn select-keys
"Returns a map containing only those entries in map whose key is in keys"
{:added "1.0"}
[map keyseq]
(loop [ret {} keys (seq keyseq)]
(if keys
(let [entry (. clojure.lang.RT (find map (first keys)))]
(recur
(if entry
(conj ret entry)
ret)
(next keys)))
ret)))
(defn keys
"Returns a sequence of the map's keys."
{:added "1.0"}
[map] (. clojure.lang.RT (keys map)))
(defn vals
"Returns a sequence of the map's values."
{:added "1.0"}
[map] (. clojure.lang.RT (vals map)))
(defn key
"Returns the key of the map entry."
{:added "1.0"}
[^java.util.Map$Entry e]
(. e (getKey)))
(defn val
"Returns the value in the map entry."
{:added "1.0"}
[^java.util.Map$Entry e]
(. e (getValue)))
(defn rseq
"Returns, in constant time, a seq of the items in rev (which
can be a vector or sorted-map), in reverse order. If rev is empty returns nil"
{:added "1.0"}
[^clojure.lang.Reversible rev]
(. rev (rseq)))
(defn name
"Returns the name String of a string, symbol or keyword."
{:tag String
:added "1.0"}
[^clojure.lang.Named x]
(if (string? x) x (. x (getName))))
(defn namespace
"Returns the namespace String of a symbol or keyword, or nil if not present."
{:tag String
:added "1.0"}
[^clojure.lang.Named x]
(. x (getNamespace)))
(defmacro locking
"Executes exprs in an implicit do, while holding the monitor of x.
Will release the monitor of x in all circumstances."
{:added "1.0"}
[x & body]
`(let [lockee# ~x]
(try
(monitor-enter lockee#)
~@body
(finally
(monitor-exit lockee#)))))
(defmacro ..
"form => fieldName-symbol or (instanceMethodName-symbol args*)
Expands into a member access (.) of the first member on the first
argument, followed by the next member on the result, etc. For
instance:
(.. System (getProperties) (get \"os.name\"))
expands to:
(. (. System (getProperties)) (get \"os.name\"))
but is easier to write, read, and understand."
{:added "1.0"}
([x form] `(. ~x ~form))
([x form & more] `(.. (. ~x ~form) ~@more)))
(defmacro ->
"Threads the expr through the forms. Inserts x as the
second item in the first form, making a list of it if it is not a
list already. If there are more forms, inserts the first form as the
second item in second form, etc."
{:added "1.0"}
([x] x)
([x form] (if (seq? form)
(with-meta `(~(first form) ~x ~@(next form)) (meta form))
(list form x)))
([x form & more] `(-> (-> ~x ~form) ~@more)))
(defmacro ->>
"Threads the expr through the forms. Inserts x as the
last item in the first form, making a list of it if it is not a
list already. If there are more forms, inserts the first form as the
last item in second form, etc."
{:added "1.1"}
([x form] (if (seq? form)
(with-meta `(~(first form) ~@(next form) ~x) (meta form))
(list form x)))
([x form & more] `(->> (->> ~x ~form) ~@more)))
;;multimethods
(def global-hierarchy)
(defmacro defmulti
"Creates a new multimethod with the associated dispatch function.
The docstring and attribute-map are optional.
Options are key-value pairs and may be one of:
:default the default dispatch value, defaults to :default
:hierarchy the isa? hierarchy to use for dispatching
defaults to the global hierarchy"
{:arglists '([name docstring? attr-map? dispatch-fn & options])
:added "1.0"}
[mm-name & options]
(let [docstring (if (string? (first options))
(first options)
nil)
options (if (string? (first options))
(next options)
options)
m (if (map? (first options))
(first options)
{})
options (if (map? (first options))
(next options)
options)
dispatch-fn (first options)
options (next options)
m (assoc m :tag 'clojure.lang.MultiFn)
m (if docstring
(assoc m :doc docstring)
m)
m (if (meta mm-name)
(conj (meta mm-name) m)
m)]
(when (= (count options) 1)
(throw (Exception. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)")))
(let [options (apply hash-map options)
default (get options :default :default)
hierarchy (get options :hierarchy #'global-hierarchy)]
`(let [v# (def ~mm-name)]
(when-not (and (.hasRoot v#) (instance? clojure.lang.MultiFn (deref v#)))
(def ~(with-meta mm-name m)
(new clojure.lang.MultiFn ~(name mm-name) ~dispatch-fn ~default ~hierarchy)))))))
(defmacro defmethod
"Creates and installs a new method of multimethod associated with dispatch-value. "
{:added "1.0"}
[multifn dispatch-val & fn-tail]
`(. ~(with-meta multifn {:tag 'clojure.lang.MultiFn}) addMethod ~dispatch-val (fn ~@fn-tail)))
(defn remove-all-methods
"Removes all of the methods of multimethod."
{:added "1.2"}
[^clojure.lang.MultiFn multifn]
(.reset multifn))
(defn remove-method
"Removes the method of multimethod associated with dispatch-value."
{:added "1.0"}
[^clojure.lang.MultiFn multifn dispatch-val]
(. multifn removeMethod dispatch-val))
(defn prefer-method
"Causes the multimethod to prefer matches of dispatch-val-x over dispatch-val-y
when there is a conflict"
{:added "1.0"}
[^clojure.lang.MultiFn multifn dispatch-val-x dispatch-val-y]
(. multifn preferMethod dispatch-val-x dispatch-val-y))
(defn methods
"Given a multimethod, returns a map of dispatch values -> dispatch fns"
{:added "1.0"}
[^clojure.lang.MultiFn multifn] (.getMethodTable multifn))
(defn get-method
"Given a multimethod and a dispatch value, returns the dispatch fn
that would apply to that value, or nil if none apply and no default"
{:added "1.0"}
[^clojure.lang.MultiFn multifn dispatch-val] (.getMethod multifn dispatch-val))
(defn prefers
"Given a multimethod, returns a map of preferred value -> set of other values"
{:added "1.0"}
[^clojure.lang.MultiFn multifn] (.getPreferTable multifn))
;;;;;;;;; var stuff
(defmacro ^{:private true} assert-args [fnname & pairs]
`(do (when-not ~(first pairs)
(throw (IllegalArgumentException.
~(str fnname " requires " (second pairs)))))
~(let [more (nnext pairs)]
(when more
(list* `assert-args fnname more)))))
(defmacro if-let
"bindings => binding-form test
If test is true, evaluates then with binding-form bound to the value of
test, if not, yields else"
{:added "1.0"}
([bindings then]
`(if-let ~bindings ~then nil))
([bindings then else & oldform]
(assert-args if-let
(and (vector? bindings) (nil? oldform)) "a vector for its binding"
(= 2 (count bindings)) "exactly 2 forms in binding vector")
(let [form (bindings 0) tst (bindings 1)]
`(let [temp# ~tst]
(if temp#
(let [~form temp#]
~then)
~else)))))
(defmacro when-let
"bindings => binding-form test
When test is true, evaluates body with binding-form bound to the value of test"
{:added "1.0"}
[bindings & body]
(assert-args when-let
(vector? bindings) "a vector for its binding"
(= 2 (count bindings)) "exactly 2 forms in binding vector")
(let [form (bindings 0) tst (bindings 1)]
`(let [temp# ~tst]
(when temp#
(let [~form temp#]
~@body)))))
(defn push-thread-bindings
"WARNING: This is a low-level function. Prefer high-level macros like
binding where ever possible.
Takes a map of Var/value pairs. Binds each Var to the associated value for
the current thread. Each call *MUST* be accompanied by a matching call to
pop-thread-bindings wrapped in a try-finally!
(push-thread-bindings bindings)
(try
...
(finally
(pop-thread-bindings)))"
{:added "1.1"}
[bindings]
(clojure.lang.Var/pushThreadBindings bindings))
(defn pop-thread-bindings
"Pop one set of bindings pushed with push-binding before. It is an error to
pop bindings without pushing before."
{:added "1.1"}
[]
(clojure.lang.Var/popThreadBindings))
(defn get-thread-bindings
"Get a map with the Var/value pairs which is currently in effect for the
current thread."
{:added "1.1"}
[]
(clojure.lang.Var/getThreadBindings))
(defmacro binding
"binding => var-symbol init-expr
Creates new bindings for the (already-existing) vars, with the
supplied initial values, executes the exprs in an implicit do, then
re-establishes the bindings that existed before. The new bindings
are made in parallel (unlike let); all init-exprs are evaluated
before the vars are bound to their new values."
{:added "1.0"}
[bindings & body]
(assert-args binding
(vector? bindings) "a vector for its binding"
(even? (count bindings)) "an even number of forms in binding vector")
(let [var-ize (fn [var-vals]
(loop [ret [] vvs (seq var-vals)]
(if vvs
(recur (conj (conj ret `(var ~(first vvs))) (second vvs))
(next (next vvs)))
(seq ret))))]
`(let []
(push-thread-bindings (hash-map ~@(var-ize bindings)))
(try
~@body
(finally
(pop-thread-bindings))))))
(defn with-bindings*
"Takes a map of Var/value pairs. Installs for the given Vars the associated
values as thread-local bindings. Then calls f with the supplied arguments.
Pops the installed bindings after f returned. Returns whatever f returns."
{:added "1.1"}
[binding-map f & args]
(push-thread-bindings binding-map)
(try
(apply f args)
(finally
(pop-thread-bindings))))
(defmacro with-bindings
"Takes a map of Var/value pairs. Installs for the given Vars the associated
values as thread-local bindings. The executes body. Pops the installed
bindings after body was evaluated. Returns the value of body."
{:added "1.1"}
[binding-map & body]
`(with-bindings* ~binding-map (fn [] ~@body)))
(defn bound-fn*
"Returns a function, which will install the same bindings in effect as in
the thread at the time bound-fn* was called and then call f with any given
arguments. This may be used to define a helper function which runs on a
different thread, but needs the same bindings in place."
{:added "1.1"}
[f]
(let [bindings (get-thread-bindings)]
(fn [& args]
(apply with-bindings* bindings f args))))
(defmacro bound-fn
"Returns a function defined by the given fntail, which will install the
same bindings in effect as in the thread at the time bound-fn was called.
This may be used to define a helper function which runs on a different
thread, but needs the same bindings in place."
{:added "1.1"}
[& fntail]
`(bound-fn* (fn ~@fntail)))
(defn find-var
"Returns the global var named by the namespace-qualified symbol, or
nil if no var with that name."
{:added "1.0"}
[sym] (. clojure.lang.Var (find sym)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Refs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn ^{:private true}
setup-reference [^clojure.lang.ARef r options]
(let [opts (apply hash-map options)]
(when (:meta opts)
(.resetMeta r (:meta opts)))
(when (:validator opts)
(.setValidator r (:validator opts)))
r))
(defn agent
"Creates and returns an agent with an initial value of state and
zero or more options (in any order):
:meta metadata-map
:validator validate-fn
:error-handler handler-fn
:error-mode mode-keyword
If metadata-map is supplied, it will be come the metadata on the
agent. validate-fn must be nil or a side-effect-free fn of one
argument, which will be passed the intended new state on any state
change. If the new state is unacceptable, the validate-fn should
return false or throw an exception. handler-fn is called if an
action throws an exception or if validate-fn rejects a new state --
see set-error-handler! for details. The mode-keyword may be either
:continue (the default if an error-handler is given) or :fail (the
default if no error-handler is given) -- see set-error-mode! for
details."
{:added "1.0"}
([state & options]
(let [a (new clojure.lang.Agent state)
opts (apply hash-map options)]
(setup-reference a options)
(when (:error-handler opts)
(.setErrorHandler a (:error-handler opts)))
(.setErrorMode a (or (:error-mode opts)
(if (:error-handler opts) :continue :fail)))
a)))
(defn send
"Dispatch an action to an agent. Returns the agent immediately.
Subsequently, in a thread from a thread pool, the state of the agent
will be set to the value of:
(apply action-fn state-of-agent args)"
{:added "1.0"}
[^clojure.lang.Agent a f & args]
(. a (dispatch f args false)))
(defn send-off
"Dispatch a potentially blocking action to an agent. Returns the
agent immediately. Subsequently, in a separate thread, the state of
the agent will be set to the value of:
(apply action-fn state-of-agent args)"
{:added "1.0"}
[^clojure.lang.Agent a f & args]
(. a (dispatch f args true)))
(defn release-pending-sends
"Normally, actions sent directly or indirectly during another action
are held until the action completes (changes the agent's
state). This function can be used to dispatch any pending sent
actions immediately. This has no impact on actions sent during a
transaction, which are still held until commit. If no action is
occurring, does nothing. Returns the number of actions dispatched."
{:added "1.0"}
[] (clojure.lang.Agent/releasePendingSends))
(defn add-watch
"Alpha - subject to change.
Adds a watch function to an agent/atom/var/ref reference. The watch
fn must be a fn of 4 args: a key, the reference, its old-state, its
new-state. Whenever the reference's state might have been changed,
any registered watches will have their functions called. The watch fn
will be called synchronously, on the agent's thread if an agent,
before any pending sends if agent or ref. Note that an atom's or
ref's state may have changed again prior to the fn call, so use
old/new-state rather than derefing the reference. Note also that watch
fns may be called from multiple threads simultaneously. Var watchers
are triggered only by root binding changes, not thread-local
set!s. Keys must be unique per reference, and can be used to remove
the watch with remove-watch, but are otherwise considered opaque by
the watch mechanism."
{:added "1.0"}
[^clojure.lang.IRef reference key fn] (.addWatch reference key fn))
(defn remove-watch
"Alpha - subject to change.
Removes a watch (set by add-watch) from a reference"
{:added "1.0"}
[^clojure.lang.IRef reference key]
(.removeWatch reference key))
(defn agent-error
"Returns the exception thrown during an asynchronous action of the
agent if the agent is failed. Returns nil if the agent is not
failed."
{:added "1.2"}
[^clojure.lang.Agent a] (.getError a))
(defn restart-agent
"When an agent is failed, changes the agent state to new-state and
then un-fails the agent so that sends are allowed again. If
a :clear-actions true option is given, any actions queued on the
agent that were being held while it was failed will be discarded,
otherwise those held actions will proceed. The new-state must pass
the validator if any, or restart will throw an exception and the
agent will remain failed with its old state and error. Watchers, if
any, will NOT be notified of the new state. Throws an exception if
the agent is not failed."
{:added "1.2"}
[^clojure.lang.Agent a, new-state & options]
(let [opts (apply hash-map options)]
(.restart a new-state (if (:clear-actions opts) true false))))
(defn set-error-handler!
"Sets the error-handler of agent a to handler-fn. If an action
being run by the agent throws an exception or doesn't pass the
validator fn, handler-fn will be called with two arguments: the
agent and the exception."
{:added "1.2"}
[^clojure.lang.Agent a, handler-fn]
(.setErrorHandler a handler-fn))
(defn error-handler
"Returns the error-handler of agent a, or nil if there is none.
See set-error-handler!"
{:added "1.2"}
[^clojure.lang.Agent a]
(.getErrorHandler a))
(defn set-error-mode!
"Sets the error-mode of agent a to mode-keyword, which must be
either :fail or :continue. If an action being run by the agent
throws an exception or doesn't pass the validator fn, an
error-handler may be called (see set-error-handler!), after which,
if the mode is :continue, the agent will continue as if neither the
action that caused the error nor the error itself ever happened.
If the mode is :fail, the agent will become failed and will stop
accepting new 'send' and 'send-off' actions, and any previously
queued actions will be held until a 'restart-agent'. Deref will
still work, returning the state of the agent before the error."
{:added "1.2"}
[^clojure.lang.Agent a, mode-keyword]
(.setErrorMode a mode-keyword))
(defn error-mode
"Returns the error-mode of agent a. See set-error-mode!"
{:added "1.2"}
[^clojure.lang.Agent a]
(.getErrorMode a))
(defn agent-errors
"DEPRECATED: Use 'agent-error' instead.
Returns a sequence of the exceptions thrown during asynchronous
actions of the agent."
{:added "1.0"
:deprecated "1.2"}
[a]
(when-let [e (agent-error a)]
(list e)))
(defn clear-agent-errors
"DEPRECATED: Use 'restart-agent' instead.
Clears any exceptions thrown during asynchronous actions of the
agent, allowing subsequent actions to occur."
{:added "1.0"
:deprecated "1.2"}
[^clojure.lang.Agent a] (restart-agent a (.deref a)))
(defn shutdown-agents
"Initiates a shutdown of the thread pools that back the agent
system. Running actions will complete, but no new actions will be
accepted"
{:added "1.0"}
[] (. clojure.lang.Agent shutdown))
(defn ref
"Creates and returns a Ref with an initial value of x and zero or
more options (in any order):
:meta metadata-map
:validator validate-fn
:min-history (default 0)
:max-history (default 10)
If metadata-map is supplied, it will be come the metadata on the
ref. validate-fn must be nil or a side-effect-free fn of one
argument, which will be passed the intended new state on any state
change. If the new state is unacceptable, the validate-fn should
return false or throw an exception. validate-fn will be called on
transaction commit, when all refs have their final values.
Normally refs accumulate history dynamically as needed to deal with
read demands. If you know in advance you will need history you can
set :min-history to ensure it will be available when first needed (instead
of after a read fault). History is limited, and the limit can be set
with :max-history."
{:added "1.0"}
([x] (new clojure.lang.Ref x))
([x & options]
(let [r ^clojure.lang.Ref (setup-reference (ref x) options)
opts (apply hash-map options)]
(when (:max-history opts)
(.setMaxHistory r (:max-history opts)))
(when (:min-history opts)
(.setMinHistory r (:min-history opts)))
r)))
(defn deref
"Also reader macro: @ref/@agent/@var/@atom/@delay/@future. Within a transaction,
returns the in-transaction-value of ref, else returns the
most-recently-committed value of ref. When applied to a var, agent
or atom, returns its current state. When applied to a delay, forces
it if not already forced. When applied to a future, will block if
computation not complete"
{:added "1.0"}
[^clojure.lang.IDeref ref] (.deref ref))
(defn atom
"Creates and returns an Atom with an initial value of x and zero or
more options (in any order):
:meta metadata-map
:validator validate-fn
If metadata-map is supplied, it will be come the metadata on the
atom. validate-fn must be nil or a side-effect-free fn of one
argument, which will be passed the intended new state on any state
change. If the new state is unacceptable, the validate-fn should
return false or throw an exception."
{:added "1.0"}
([x] (new clojure.lang.Atom x))
([x & options] (setup-reference (atom x) options)))
(defn swap!
"Atomically swaps the value of atom to be:
(apply f current-value-of-atom args). Note that f may be called
multiple times, and thus should be free of side effects. Returns
the value that was swapped in."
{:added "1.0"}
([^clojure.lang.Atom atom f] (.swap atom f))
([^clojure.lang.Atom atom f x] (.swap atom f x))
([^clojure.lang.Atom atom f x y] (.swap atom f x y))
([^clojure.lang.Atom atom f x y & args] (.swap atom f x y args)))
(defn compare-and-set!
"Atomically sets the value of atom to newval if and only if the
current value of the atom is identical to oldval. Returns true if
set happened, else false"
{:added "1.0"}
[^clojure.lang.Atom atom oldval newval] (.compareAndSet atom oldval newval))
(defn reset!
"Sets the value of atom to newval without regard for the
current value. Returns newval."
{:added "1.0"}
[^clojure.lang.Atom atom newval] (.reset atom newval))
(defn set-validator!
"Sets the validator-fn for a var/ref/agent/atom. validator-fn must be nil or a
side-effect-free fn of one argument, which will be passed the intended
new state on any state change. If the new state is unacceptable, the
validator-fn should return false or throw an exception. If the current state (root
value if var) is not acceptable to the new validator, an exception
will be thrown and the validator will not be changed."
{:added "1.0"}
[^clojure.lang.IRef iref validator-fn] (. iref (setValidator validator-fn)))
(defn get-validator
"Gets the validator-fn for a var/ref/agent/atom."
{:added "1.0"}
[^clojure.lang.IRef iref] (. iref (getValidator)))
(defn alter-meta!
"Atomically sets the metadata for a namespace/var/ref/agent/atom to be:
(apply f its-current-meta args)
f must be free of side-effects"
{:added "1.0"}
[^clojure.lang.IReference iref f & args] (.alterMeta iref f args))
(defn reset-meta!
"Atomically resets the metadata for a namespace/var/ref/agent/atom"
{:added "1.0"}
[^clojure.lang.IReference iref metadata-map] (.resetMeta iref metadata-map))
(defn commute
"Must be called in a transaction. Sets the in-transaction-value of
ref to:
(apply fun in-transaction-value-of-ref args)
and returns the in-transaction-value of ref.
At the commit point of the transaction, sets the value of ref to be:
(apply fun most-recently-committed-value-of-ref args)
Thus fun should be commutative, or, failing that, you must accept
last-one-in-wins behavior. commute allows for more concurrency than
ref-set."
{:added "1.0"}
[^clojure.lang.Ref ref fun & args]
(. ref (commute fun args)))
(defn alter
"Must be called in a transaction. Sets the in-transaction-value of
ref to:
(apply fun in-transaction-value-of-ref args)
and returns the in-transaction-value of ref."
{:added "1.0"}
[^clojure.lang.Ref ref fun & args]
(. ref (alter fun args)))
(defn ref-set
"Must be called in a transaction. Sets the value of ref.
Returns val."
{:added "1.0"}
[^clojure.lang.Ref ref val]
(. ref (set val)))
(defn ref-history-count
"Returns the history count of a ref"
{:added "1.1"}
[^clojure.lang.Ref ref]
(.getHistoryCount ref))
(defn ref-min-history
"Gets the min-history of a ref, or sets it and returns the ref"
{:added "1.1"}
([^clojure.lang.Ref ref]
(.getMinHistory ref))
([^clojure.lang.Ref ref n]
(.setMinHistory ref n)))
(defn ref-max-history
"Gets the max-history of a ref, or sets it and returns the ref"
{:added "1.1"}
([^clojure.lang.Ref ref]
(.getMaxHistory ref))
([^clojure.lang.Ref ref n]
(.setMaxHistory ref n)))
(defn ensure
"Must be called in a transaction. Protects the ref from modification
by other transactions. Returns the in-transaction-value of
ref. Allows for more concurrency than (ref-set ref @ref)"
{:added "1.0"}
[^clojure.lang.Ref ref]
(. ref (touch))
(. ref (deref)))
(defmacro sync
"transaction-flags => TBD, pass nil for now
Runs the exprs (in an implicit do) in a transaction that encompasses
exprs and any nested calls. Starts a transaction if none is already
running on this thread. Any uncaught exception will abort the
transaction and flow out of sync. The exprs may be run more than
once, but any effects on Refs will be atomic."
{:added "1.0"}
[flags-ignored-for-now & body]
`(. clojure.lang.LockingTransaction
(runInTransaction (fn [] ~@body))))
(defmacro io!
"If an io! block occurs in a transaction, throws an
IllegalStateException, else runs body in an implicit do. If the
first expression in body is a literal string, will use that as the
exception message."
{:added "1.0"}
[& body]
(let [message (when (string? (first body)) (first body))
body (if message (next body) body)]
`(if (clojure.lang.LockingTransaction/isRunning)
(throw (new IllegalStateException ~(or message "I/O in transaction")))
(do ~@body))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; fn stuff ;;;;;;;;;;;;;;;;
(defn comp
"Takes a set of functions and returns a fn that is the composition
of those fns. The returned fn takes a variable number of args,
applies the rightmost of fns to the args, the next
fn (right-to-left) to the result, etc."
{:added "1.0"}
([f] f)
([f g]
(fn
([] (f (g)))
([x] (f (g x)))
([x y] (f (g x y)))
([x y z] (f (g x y z)))
([x y z & args] (f (apply g x y z args)))))
([f g h]
(fn
([] (f (g (h))))
([x] (f (g (h x))))
([x y] (f (g (h x y))))
([x y z] (f (g (h x y z))))
([x y z & args] (f (g (apply h x y z args))))))
([f1 f2 f3 & fs]
(let [fs (reverse (list* f1 f2 f3 fs))]
(fn [& args]
(loop [ret (apply (first fs) args) fs (next fs)]
(if fs
(recur ((first fs) ret) (next fs))
ret))))))
(defn juxt
"Alpha - name subject to change.
Takes a set of functions and returns a fn that is the juxtaposition
of those fns. The returned fn takes a variable number of args, and
returns a vector containing the result of applying each fn to the
args (left-to-right).
((juxt a b c) x) => [(a x) (b x) (c x)]"
{:added "1.1"}
([f]
(fn
([] [(f)])
([x] [(f x)])
([x y] [(f x y)])
([x y z] [(f x y z)])
([x y z & args] [(apply f x y z args)])))
([f g]
(fn
([] [(f) (g)])
([x] [(f x) (g x)])
([x y] [(f x y) (g x y)])
([x y z] [(f x y z) (g x y z)])
([x y z & args] [(apply f x y z args) (apply g x y z args)])))
([f g h]
(fn
([] [(f) (g) (h)])
([x] [(f x) (g x) (h x)])
([x y] [(f x y) (g x y) (h x y)])
([x y z] [(f x y z) (g x y z) (h x y z)])
([x y z & args] [(apply f x y z args) (apply g x y z args) (apply h x y z args)])))
([f g h & fs]
(let [fs (list* f g h fs)]
(fn
([] (reduce #(conj %1 (%2)) [] fs))
([x] (reduce #(conj %1 (%2 x)) [] fs))
([x y] (reduce #(conj %1 (%2 x y)) [] fs))
([x y z] (reduce #(conj %1 (%2 x y z)) [] fs))
([x y z & args] (reduce #(conj %1 (apply %2 x y z args)) [] fs))))))
(defn partial
"Takes a function f and fewer than the normal arguments to f, and
returns a fn that takes a variable number of additional args. When
called, the returned function calls f with args + additional args."
{:added "1.0"}
([f arg1]
(fn [& args] (apply f arg1 args)))
([f arg1 arg2]
(fn [& args] (apply f arg1 arg2 args)))
([f arg1 arg2 arg3]
(fn [& args] (apply f arg1 arg2 arg3 args)))
([f arg1 arg2 arg3 & more]
(fn [& args] (apply f arg1 arg2 arg3 (concat more args)))))
;;;;;;;;;;;;;;;;;;; sequence fns ;;;;;;;;;;;;;;;;;;;;;;;
(defn sequence
"Coerces coll to a (possibly empty) sequence, if it is not already
one. Will not force a lazy seq. (sequence nil) yields ()"
{:added "1.0"}
[coll]
(if (seq? coll) coll
(or (seq coll) ())))
(defn every?
"Returns true if (pred x) is logical true for every x in coll, else
false."
{:tag Boolean
:added "1.0"}
[pred coll]
(cond
(nil? (seq coll)) true
(pred (first coll)) (recur pred (next coll))
:else false))
(def
^{:tag Boolean
:doc "Returns false if (pred x) is logical true for every x in
coll, else true."
:arglists '([pred coll])
:added "1.0"}
not-every? (comp not every?))
(defn some
"Returns the first logical true value of (pred x) for any x in coll,
else nil. One common idiom is to use a set as pred, for example
this will return :fred if :fred is in the sequence, otherwise nil:
(some #{:fred} coll)"
{:added "1.0"}
[pred coll]
(when (seq coll)
(or (pred (first coll)) (recur pred (next coll)))))
(def
^{:tag Boolean
:doc "Returns false if (pred x) is logical true for any x in coll,
else true."
:arglists '([pred coll])
:added "1.0"}
not-any? (comp not some))
;will be redefed later with arg checks
(defmacro dotimes
"bindings => name n
Repeatedly executes body (presumably for side-effects) with name
bound to integers from 0 through n-1."
{:added "1.0"}
[bindings & body]
(let [i (first bindings)
n (second bindings)]
`(let [n# (int ~n)]
(loop [~i (int 0)]
(when (< ~i n#)
~@body
(recur (inc ~i)))))))
(defn map
"Returns a lazy sequence consisting of the result of applying f to the
set of first items of each coll, followed by applying f to the set
of second items in each coll, until any one of the colls is
exhausted. Any remaining items in other colls are ignored. Function
f should accept number-of-colls arguments."
{:added "1.0"}
([f coll]
(lazy-seq
(when-let [s (seq coll)]
(if (chunked-seq? s)
(let [c (chunk-first s)
size (int (count c))
b (chunk-buffer size)]
(dotimes [i size]
(chunk-append b (f (.nth c i))))
(chunk-cons (chunk b) (map f (chunk-rest s))))
(cons (f (first s)) (map f (rest s)))))))
([f c1 c2]
(lazy-seq
(let [s1 (seq c1) s2 (seq c2)]
(when (and s1 s2)
(cons (f (first s1) (first s2))
(map f (rest s1) (rest s2)))))))
([f c1 c2 c3]
(lazy-seq
(let [s1 (seq c1) s2 (seq c2) s3 (seq c3)]
(when (and s1 s2 s3)
(cons (f (first s1) (first s2) (first s3))
(map f (rest s1) (rest s2) (rest s3)))))))
([f c1 c2 c3 & colls]
(let [step (fn step [cs]
(lazy-seq
(let [ss (map seq cs)]
(when (every? identity ss)
(cons (map first ss) (step (map rest ss)))))))]
(map #(apply f %) (step (conj colls c3 c2 c1))))))
(defn mapcat
"Returns the result of applying concat to the result of applying map
to f and colls. Thus function f should return a collection."
{:added "1.0"}
[f & colls]
(apply concat (apply map f colls)))
(defn filter
"Returns a lazy sequence of the items in coll for which
(pred item) returns true. pred must be free of side-effects."
{:added "1.0"}
([pred coll]
(lazy-seq
(when-let [s (seq coll)]
(if (chunked-seq? s)
(let [c (chunk-first s)
size (count c)
b (chunk-buffer size)]
(dotimes [i size]
(when (pred (.nth c i))
(chunk-append b (.nth c i))))
(chunk-cons (chunk b) (filter pred (chunk-rest s))))
(let [f (first s) r (rest s)]
(if (pred f)
(cons f (filter pred r))
(filter pred r))))))))
(defn remove
"Returns a lazy sequence of the items in coll for which
(pred item) returns false. pred must be free of side-effects."
{:added "1.0"}
[pred coll]
(filter (complement pred) coll))
(defn take
"Returns a lazy sequence of the first n items in coll, or all items if
there are fewer than n."
{:added "1.0"}
[n coll]
(lazy-seq
(when (pos? n)
(when-let [s (seq coll)]
(cons (first s) (take (dec n) (rest s)))))))
(defn take-while
"Returns a lazy sequence of successive items from coll while
(pred item) returns true. pred must be free of side-effects."
{:added "1.0"}
[pred coll]
(lazy-seq
(when-let [s (seq coll)]
(when (pred (first s))
(cons (first s) (take-while pred (rest s)))))))
(defn drop
"Returns a lazy sequence of all but the first n items in coll."
{:added "1.0"}
[n coll]
(let [step (fn [n coll]
(let [s (seq coll)]
(if (and (pos? n) s)
(recur (dec n) (rest s))
s)))]
(lazy-seq (step n coll))))
(defn drop-last
"Return a lazy sequence of all but the last n (default 1) items in coll"
{:added "1.0"}
([s] (drop-last 1 s))
([n s] (map (fn [x _] x) s (drop n s))))
(defn take-last
"Returns a seq of the last n items in coll. Depending on the type
of coll may be no better than linear time. For vectors, see also subvec."
{:added "1.1"}
[n coll]
(loop [s (seq coll), lead (seq (drop n coll))]
(if lead
(recur (next s) (next lead))
s)))
(defn drop-while
"Returns a lazy sequence of the items in coll starting from the first
item for which (pred item) returns nil."
{:added "1.0"}
[pred coll]
(let [step (fn [pred coll]
(let [s (seq coll)]
(if (and s (pred (first s)))
(recur pred (rest s))
s)))]
(lazy-seq (step pred coll))))
(defn cycle
"Returns a lazy (infinite!) sequence of repetitions of the items in coll."
{:added "1.0"}
[coll] (lazy-seq
(when-let [s (seq coll)]
(concat s (cycle s)))))
(defn split-at
"Returns a vector of [(take n coll) (drop n coll)]"
{:added "1.0"}
[n coll]
[(take n coll) (drop n coll)])
(defn split-with
"Returns a vector of [(take-while pred coll) (drop-while pred coll)]"
{:added "1.0"}
[pred coll]
[(take-while pred coll) (drop-while pred coll)])
(defn repeat
"Returns a lazy (infinite!, or length n if supplied) sequence of xs."
{:added "1.0"}
([x] (lazy-seq (cons x (repeat x))))
([n x] (take n (repeat x))))
(defn replicate
"Returns a lazy seq of n xs."
{:added "1.0"}
[n x] (take n (repeat x)))
(defn iterate
"Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free of side-effects"
{:added "1.0"}
[f x] (cons x (lazy-seq (iterate f (f x)))))
(defn range
"Returns a lazy seq of nums from start (inclusive) to end
(exclusive), by step, where start defaults to 0, step to 1, and end
to infinity."
{:added "1.0"}
([] (range 0 Double/POSITIVE_INFINITY 1))
([end] (range 0 end 1))
([start end] (range start end 1))
([start end step]
(lazy-seq
(let [b (chunk-buffer 32)
comp (if (pos? step) < >)]
(loop [i start]
(if (and (< (count b) 32)
(comp i end))
(do
(chunk-append b i)
(recur (+ i step)))
(chunk-cons (chunk b)
(when (comp i end)
(range i end step)))))))))
(defn merge
"Returns a map that consists of the rest of the maps conj-ed onto
the first. If a key occurs in more than one map, the mapping from
the latter (left-to-right) will be the mapping in the result."
{:added "1.0"}
[& maps]
(when (some identity maps)
(reduce #(conj (or %1 {}) %2) maps)))
(defn merge-with
"Returns a map that consists of the rest of the maps conj-ed onto
the first. If a key occurs in more than one map, the mapping(s)
from the latter (left-to-right) will be combined with the mapping in
the result by calling (f val-in-result val-in-latter)."
{:added "1.0"}
[f & maps]
(when (some identity maps)
(let [merge-entry (fn [m e]
(let [k (key e) v (val e)]
(if (contains? m k)
(assoc m k (f (get m k) v))
(assoc m k v))))
merge2 (fn [m1 m2]
(reduce merge-entry (or m1 {}) (seq m2)))]
(reduce merge2 maps))))
(defn zipmap
"Returns a map with the keys mapped to the corresponding vals."
{:added "1.0"}
[keys vals]
(loop [map {}
ks (seq keys)
vs (seq vals)]
(if (and ks vs)
(recur (assoc map (first ks) (first vs))
(next ks)
(next vs))
map)))
(defmacro declare
"defs the supplied var names with no bindings, useful for making forward declarations."
{:added "1.0"}
[& names] `(do ~@(map #(list 'def (vary-meta % assoc :declared true)) names)))
(defn line-seq
"Returns the lines of text from rdr as a lazy sequence of strings.
rdr must implement java.io.BufferedReader."
{:added "1.0"}
[^java.io.BufferedReader rdr]
(when-let [line (.readLine rdr)]
(cons line (lazy-seq (line-seq rdr)))))
(defn comparator
"Returns an implementation of java.util.Comparator based upon pred."
{:added "1.0"}
[pred]
(fn [x y]
(cond (pred x y) -1 (pred y x) 1 :else 0)))
(defn sort
"Returns a sorted sequence of the items in coll. If no comparator is
supplied, uses compare. comparator must
implement java.util.Comparator."
{:added "1.0"}
([coll]
(sort compare coll))
([^java.util.Comparator comp coll]
(if (seq coll)
(let [a (to-array coll)]
(. java.util.Arrays (sort a comp))
(seq a))
())))
(defn sort-by
"Returns a sorted sequence of the items in coll, where the sort
order is determined by comparing (keyfn item). If no comparator is
supplied, uses compare. comparator must
implement java.util.Comparator."
{:added "1.0"}
([keyfn coll]
(sort-by keyfn compare coll))
([keyfn ^java.util.Comparator comp coll]
(sort (fn [x y] (. comp (compare (keyfn x) (keyfn y)))) coll)))
(defn partition
"Returns a lazy sequence of lists of n items each, at offsets step
apart. If step is not supplied, defaults to n, i.e. the partitions
do not overlap. If a pad collection is supplied, use its elements as
necessary to complete last partition upto n items. In case there are
not enough padding elements, return a partition with less than n items."
{:added "1.0"}
([n coll]
(partition n n coll))
([n step coll]
(lazy-seq
(when-let [s (seq coll)]
(let [p (take n s)]
(when (= n (count p))
(cons p (partition n step (drop step s))))))))
([n step pad coll]
(lazy-seq
(when-let [s (seq coll)]
(let [p (take n s)]
(if (= n (count p))
(cons p (partition n step pad (drop step s)))
(list (take n (concat p pad)))))))))
;; evaluation
(defn eval
"Evaluates the form data structure (not text!) and returns the result."
{:added "1.0"}
[form] (. clojure.lang.Compiler (eval form)))
(defmacro doseq
"Repeatedly executes body (presumably for side-effects) with
bindings and filtering as provided by \"for\". Does not retain
the head of the sequence. Returns nil."
{:added "1.0"}
[seq-exprs & body]
(assert-args doseq
(vector? seq-exprs) "a vector for its binding"
(even? (count seq-exprs)) "an even number of forms in binding vector")
(let [step (fn step [recform exprs]
(if-not exprs
[true `(do ~@body)]
(let [k (first exprs)
v (second exprs)]
(if (keyword? k)
(let [steppair (step recform (nnext exprs))
needrec (steppair 0)
subform (steppair 1)]
(cond
(= k :let) [needrec `(let ~v ~subform)]
(= k :while) [false `(when ~v
~subform
~@(when needrec [recform]))]
(= k :when) [false `(if ~v
(do
~subform
~@(when needrec [recform]))
~recform)]))
(let [seq- (gensym "seq_")
chunk- (with-meta (gensym "chunk_")
{:tag 'clojure.lang.IChunk})
count- (gensym "count_")
i- (gensym "i_")
recform `(recur (next ~seq-) nil (int 0) (int 0))
steppair (step recform (nnext exprs))
needrec (steppair 0)
subform (steppair 1)
recform-chunk
`(recur ~seq- ~chunk- ~count- (unchecked-inc ~i-))
steppair-chunk (step recform-chunk (nnext exprs))
subform-chunk (steppair-chunk 1)]
[true
`(loop [~seq- (seq ~v), ~chunk- nil,
~count- (int 0), ~i- (int 0)]
(if (< ~i- ~count-)
(let [~k (.nth ~chunk- ~i-)]
~subform-chunk
~@(when needrec [recform-chunk]))
(when-let [~seq- (seq ~seq-)]
(if (chunked-seq? ~seq-)
(let [c# (chunk-first ~seq-)]
(recur (chunk-rest ~seq-) c#
(int (count c#)) (int 0)))
(let [~k (first ~seq-)]
~subform
~@(when needrec [recform]))))))])))))]
(nth (step nil (seq seq-exprs)) 1)))
(defn dorun
"When lazy sequences are produced via functions that have side
effects, any effects other than those needed to produce the first
element in the seq do not occur until the seq is consumed. dorun can
be used to force any effects. Walks through the successive nexts of
the seq, does not retain the head and returns nil."
{:added "1.0"}
([coll]
(when (seq coll)
(recur (next coll))))
([n coll]
(when (and (seq coll) (pos? n))
(recur (dec n) (next coll)))))
(defn doall
"When lazy sequences are produced via functions that have side
effects, any effects other than those needed to produce the first
element in the seq do not occur until the seq is consumed. doall can
be used to force any effects. Walks through the successive nexts of
the seq, retains the head and returns it, thus causing the entire
seq to reside in memory at one time."
{:added "1.0"}
([coll]
(dorun coll)
coll)
([n coll]
(dorun n coll)
coll))
(defn await
"Blocks the current thread (indefinitely!) until all actions
dispatched thus far, from this thread or agent, to the agent(s) have
occurred. Will block on failed agents. Will never return if
a failed agent is restarted with :clear-actions true."
{:added "1.0"}
[& agents]
(io! "await in transaction"
(when *agent*
(throw (new Exception "Can't await in agent action")))
(let [latch (new java.util.concurrent.CountDownLatch (count agents))
count-down (fn [agent] (. latch (countDown)) agent)]
(doseq [agent agents]
(send agent count-down))
(. latch (await)))))
(defn await1 [^clojure.lang.Agent a]
(when (pos? (.getQueueCount a))
(await a))
a)
(defn await-for
"Blocks the current thread until all actions dispatched thus
far (from this thread or agent) to the agents have occurred, or the
timeout (in milliseconds) has elapsed. Returns nil if returning due
to timeout, non-nil otherwise."
{:added "1.0"}
[timeout-ms & agents]
(io! "await-for in transaction"
(when *agent*
(throw (new Exception "Can't await in agent action")))
(let [latch (new java.util.concurrent.CountDownLatch (count agents))
count-down (fn [agent] (. latch (countDown)) agent)]
(doseq [agent agents]
(send agent count-down))
(. latch (await timeout-ms (. java.util.concurrent.TimeUnit MILLISECONDS))))))
(defmacro dotimes
"bindings => name n
Repeatedly executes body (presumably for side-effects) with name
bound to integers from 0 through n-1."
{:added "1.0"}
[bindings & body]
(assert-args dotimes
(vector? bindings) "a vector for its binding"
(= 2 (count bindings)) "exactly 2 forms in binding vector")
(let [i (first bindings)
n (second bindings)]
`(let [n# (int ~n)]
(loop [~i (int 0)]
(when (< ~i n#)
~@body
(recur (unchecked-inc ~i)))))))
#_(defn into
"Returns a new coll consisting of to-coll with all of the items of
from-coll conjoined."
{:added "1.0"}
[to from]
(let [ret to items (seq from)]
(if items
(recur (conj ret (first items)) (next items))
ret)))
;;;;;;;;;;;;;;;;;;;;; editable collections ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn transient
"Alpha - subject to change.
Returns a new, transient version of the collection, in constant time."
{:added "1.1"}
[^clojure.lang.IEditableCollection coll]
(.asTransient coll))
(defn persistent!
"Alpha - subject to change.
Returns a new, persistent version of the transient collection, in
constant time. The transient collection cannot be used after this
call, any such use will throw an exception."
{:added "1.1"}
[^clojure.lang.ITransientCollection coll]
(.persistent coll))
(defn conj!
"Alpha - subject to change.
Adds x to the transient collection, and return coll. The 'addition'
may happen at different 'places' depending on the concrete type."
{:added "1.1"}
[^clojure.lang.ITransientCollection coll x]
(.conj coll x))
(defn assoc!
"Alpha - subject to change.
When applied to a transient map, adds mapping of key(s) to
val(s). When applied to a transient vector, sets the val at index.
Note - index must be <= (count vector). Returns coll."
{:added "1.1"}
([^clojure.lang.ITransientAssociative coll key val] (.assoc coll key val))
([^clojure.lang.ITransientAssociative coll key val & kvs]
(let [ret (.assoc coll key val)]
(if kvs
(recur ret (first kvs) (second kvs) (nnext kvs))
ret))))
(defn dissoc!
"Alpha - subject to change.
Returns a transient map that doesn't contain a mapping for key(s)."
{:added "1.1"}
([^clojure.lang.ITransientMap map key] (.without map key))
([^clojure.lang.ITransientMap map key & ks]
(let [ret (.without map key)]
(if ks
(recur ret (first ks) (next ks))
ret))))
(defn pop!
"Alpha - subject to change.
Removes the last item from a transient vector. If
the collection is empty, throws an exception. Returns coll"
{:added "1.1"}
[^clojure.lang.ITransientVector coll]
(.pop coll))
(defn disj!
"Alpha - subject to change.
disj[oin]. Returns a transient set of the same (hashed/sorted) type, that
does not contain key(s)."
{:added "1.1"}
([set] set)
([^clojure.lang.ITransientSet set key]
(. set (disjoin key)))
([set key & ks]
(let [ret (disj set key)]
(if ks
(recur ret (first ks) (next ks))
ret))))
;redef into with batch support
(defn into
"Returns a new coll consisting of to-coll with all of the items of
from-coll conjoined."
{:added "1.0"}
[to from]
(if (instance? clojure.lang.IEditableCollection to)
(persistent! (reduce conj! (transient to) from))
(reduce conj to from)))
(defmacro import
"import-list => (package-symbol class-name-symbols*)
For each name in class-name-symbols, adds a mapping from name to the
class named by package.name to the current namespace. Use :import in the ns
macro in preference to calling this directly."
{:added "1.0"}
[& import-symbols-or-lists]
(let [specs (map #(if (and (seq? %) (= 'quote (first %))) (second %) %)
import-symbols-or-lists)]
`(do ~@(map #(list 'clojure.core/import* %)
(reduce (fn [v spec]
(if (symbol? spec)
(conj v (name spec))
(let [p (first spec) cs (rest spec)]
(into v (map #(str p "." %) cs)))))
[] specs)))))
(defn into-array
"Returns an array with components set to the values in aseq. The array's
component type is type if provided, or the type of the first value in
aseq if present, or Object. All values in aseq must be compatible with
the component type. Class objects for the primitive types can be obtained
using, e.g., Integer/TYPE."
{:added "1.0"}
([aseq]
(clojure.lang.RT/seqToTypedArray (seq aseq)))
([type aseq]
(clojure.lang.RT/seqToTypedArray type (seq aseq))))
(defn ^{:private true}
array [& items]
(into-array items))
(defn ^Class class
"Returns the Class of x"
{:added "1.0"}
[^Object x] (if (nil? x) x (. x (getClass))))
(defn type
"Returns the :type metadata of x, or its Class if none"
{:added "1.0"}
[x]
(or (:type (meta x)) (class x)))
(defn num
"Coerce to Number"
{:tag Number
:inline (fn [x] `(. clojure.lang.Numbers (num ~x)))
:added "1.0"}
[x] (. clojure.lang.Numbers (num x)))
(defn long
"Coerce to long"
{:tag Long
:inline (fn [x] `(. clojure.lang.RT (longCast ~x)))
:added "1.0"}
[^Number x] (clojure.lang.RT/longCast x))
(defn float
"Coerce to float"
{:tag Float
:inline (fn [x] `(. clojure.lang.RT (floatCast ~x)))
:added "1.0"}
[^Number x] (clojure.lang.RT/floatCast x))
(defn double
"Coerce to double"
{:tag Double
:inline (fn [x] `(. clojure.lang.RT (doubleCast ~x)))
:added "1.0"}
[^Number x] (clojure.lang.RT/doubleCast x))
(defn short
"Coerce to short"
{:tag Short
:inline (fn [x] `(. clojure.lang.RT (shortCast ~x)))
:added "1.0"}
[^Number x] (clojure.lang.RT/shortCast x))
(defn byte
"Coerce to byte"
{:tag Byte
:inline (fn [x] `(. clojure.lang.RT (byteCast ~x)))
:added "1.0"}
[^Number x] (clojure.lang.RT/byteCast x))
(defn char
"Coerce to char"
{:tag Character
:inline (fn [x] `(. clojure.lang.RT (charCast ~x)))
:added "1.1"}
[x] (. clojure.lang.RT (charCast x)))
(defn boolean
"Coerce to boolean"
{
:inline (fn [x] `(. clojure.lang.RT (booleanCast ~x)))
:added "1.0"}
[x] (clojure.lang.RT/booleanCast x))
(defn number?
"Returns true if x is a Number"
{:added "1.0"}
[x]
(instance? Number x))
(defn integer?
"Returns true if n is an integer"
{:added "1.0"}
[n]
(or (instance? Integer n)
(instance? Long n)
(instance? BigInteger n)
(instance? Short n)
(instance? Byte n)))
(defn mod
"Modulus of num and div. Truncates toward negative infinity."
{:added "1.0"}
[num div]
(let [m (rem num div)]
(if (or (zero? m) (pos? (* num div)))
m
(+ m div))))
(defn ratio?
"Returns true if n is a Ratio"
{:added "1.0"}
[n] (instance? clojure.lang.Ratio n))
(defn numerator
"Returns the numerator part of a Ratio."
{:tag BigInteger
:added "1.2"}
[r]
(.numerator ^clojure.lang.Ratio r))
(defn denominator
"Returns the denominator part of a Ratio."
{:tag BigInteger
:added "1.2"}
[r]
(.denominator ^clojure.lang.Ratio r))
(defn decimal?
"Returns true if n is a BigDecimal"
{:added "1.0"}
[n] (instance? BigDecimal n))
(defn float?
"Returns true if n is a floating point number"
{:added "1.0"}
[n]
(or (instance? Double n)
(instance? Float n)))
(defn rational? [n]
"Returns true if n is a rational number"
{:added "1.0"}
(or (integer? n) (ratio? n) (decimal? n)))
(defn bigint
"Coerce to BigInteger"
{:tag BigInteger
:added "1.0"}
[x] (cond
(instance? BigInteger x) x
(decimal? x) (.toBigInteger ^BigDecimal x)
(ratio? x) (.bigIntegerValue ^clojure.lang.Ratio x)
(number? x) (BigInteger/valueOf (long x))
:else (BigInteger. x)))
(defn bigdec
"Coerce to BigDecimal"
{:tag BigDecimal
:added "1.0"}
[x] (cond
(decimal? x) x
(float? x) (. BigDecimal valueOf (double x))
(ratio? x) (/ (BigDecimal. (.numerator x)) (.denominator x))
(instance? BigInteger x) (BigDecimal. ^BigInteger x)
(number? x) (BigDecimal/valueOf (long x))
:else (BigDecimal. x)))
(def ^{:private true} print-initialized false)
(defmulti print-method (fn [x writer] (type x)))
(defmulti print-dup (fn [x writer] (class x)))
(defn pr-on
{:private true}
[x w]
(if *print-dup*
(print-dup x w)
(print-method x w))
nil)
(defn pr
"Prints the object(s) to the output stream that is the current value
of *out*. Prints the object(s), separated by spaces if there is
more than one. By default, pr and prn print in a way that objects
can be read by the reader"
{:dynamic true
:added "1.0"}
([] nil)
([x]
(pr-on x *out*))
([x & more]
(pr x)
(. *out* (append \space))
(if-let [nmore (next more)]
(recur (first more) nmore)
(apply pr more))))
(defn newline
"Writes a newline to the output stream that is the current value of
*out*"
{:added "1.0"}
[]
(. *out* (append \newline))
nil)
(defn flush
"Flushes the output stream that is the current value of
*out*"
{:added "1.0"}
[]
(. *out* (flush))
nil)
(defn prn
"Same as pr followed by (newline). Observes *flush-on-newline*"
{:added "1.0"}
[& more]
(apply pr more)
(newline)
(when *flush-on-newline*
(flush)))
(defn print
"Prints the object(s) to the output stream that is the current value
of *out*. print and println produce output for human consumption."
{:added "1.0"}
[& more]
(binding [*print-readably* nil]
(apply pr more)))
(defn println
"Same as print followed by (newline)"
{:added "1.0"}
[& more]
(binding [*print-readably* nil]
(apply prn more)))
(defn read
"Reads the next object from stream, which must be an instance of
java.io.PushbackReader or some derivee. stream defaults to the
current value of *in* ."
{:added "1.0"}
([]
(read *in*))
([stream]
(read stream true nil))
([stream eof-error? eof-value]
(read stream eof-error? eof-value false))
([stream eof-error? eof-value recursive?]
(. clojure.lang.LispReader (read stream (boolean eof-error?) eof-value recursive?))))
(defn read-line
"Reads the next line from stream that is the current value of *in* ."
{:added "1.0"}
[]
(if (instance? clojure.lang.LineNumberingPushbackReader *in*)
(.readLine ^clojure.lang.LineNumberingPushbackReader *in*)
(.readLine ^java.io.BufferedReader *in*)))
(defn read-string
"Reads one object from the string s"
{:added "1.0"}
[s] (clojure.lang.RT/readString s))
(defn subvec
"Returns a persistent vector of the items in vector from
start (inclusive) to end (exclusive). If end is not supplied,
defaults to (count vector). This operation is O(1) and very fast, as
the resulting vector shares structure with the original and no
trimming is done."
{:added "1.0"}
([v start]
(subvec v start (count v)))
([v start end]
(. clojure.lang.RT (subvec v start end))))
(defmacro with-open
"bindings => [name init ...]
Evaluates body in a try expression with names bound to the values
of the inits, and a finally clause that calls (.close name) on each
name in reverse order."
{:added "1.0"}
[bindings & body]
(assert-args with-open
(vector? bindings) "a vector for its binding"
(even? (count bindings)) "an even number of forms in binding vector")
(cond
(= (count bindings) 0) `(do ~@body)
(symbol? (bindings 0)) `(let ~(subvec bindings 0 2)
(try
(with-open ~(subvec bindings 2) ~@body)
(finally
(. ~(bindings 0) close))))
:else (throw (IllegalArgumentException.
"with-open only allows Symbols in bindings"))))
(defmacro doto
"Evaluates x then calls all of the methods and functions with the
value of x supplied at the front of the given arguments. The forms
are evaluated in order. Returns x.
(doto (new java.util.HashMap) (.put \"a\" 1) (.put \"b\" 2))"
{:added "1.0"}
[x & forms]
(let [gx (gensym)]
`(let [~gx ~x]
~@(map (fn [f]
(if (seq? f)
`(~(first f) ~gx ~@(next f))
`(~f ~gx)))
forms)
~gx)))
(defmacro memfn
"Expands into code that creates a fn that expects to be passed an
object and any args and calls the named instance method on the
object passing the args. Use when you want to treat a Java method as
a first-class fn."
{:added "1.0"}
[name & args]
`(fn [target# ~@args]
(. target# (~name ~@args))))
(defmacro time
"Evaluates expr and prints the time it took. Returns the value of
expr."
{:added "1.0"}
[expr]
`(let [start# (. System (nanoTime))
ret# ~expr]
(prn (str "Elapsed time: " (/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs"))
ret#))
(import '(java.lang.reflect Array))
(defn alength
"Returns the length of the Java array. Works on arrays of all
types."
{:inline (fn [a] `(. clojure.lang.RT (alength ~a)))
:added "1.0"}
[array] (. clojure.lang.RT (alength array)))
(defn aclone
"Returns a clone of the Java array. Works on arrays of known
types."
{:inline (fn [a] `(. clojure.lang.RT (aclone ~a)))
:added "1.0"}
[array] (. clojure.lang.RT (aclone array)))
(defn aget
"Returns the value at the index/indices. Works on Java arrays of all
types."
{:inline (fn [a i] `(. clojure.lang.RT (aget ~a (int ~i))))
:inline-arities #{2}
:added "1.0"}
([array idx]
(clojure.lang.Reflector/prepRet (. Array (get array idx))))
([array idx & idxs]
(apply aget (aget array idx) idxs)))
(defn aset
"Sets the value at the index/indices. Works on Java arrays of
reference types. Returns val."
{:inline (fn [a i v] `(. clojure.lang.RT (aset ~a (int ~i) ~v)))
:inline-arities #{3}
:added "1.0"}
([array idx val]
(. Array (set array idx val))
val)
([array idx idx2 & idxv]
(apply aset (aget array idx) idx2 idxv)))
(defmacro
^{:private true}
def-aset [name method coerce]
`(defn ~name
{:arglists '([~'array ~'idx ~'val] [~'array ~'idx ~'idx2 & ~'idxv])}
([array# idx# val#]
(. Array (~method array# idx# (~coerce val#)))
val#)
([array# idx# idx2# & idxv#]
(apply ~name (aget array# idx#) idx2# idxv#))))
(def-aset
^{:doc "Sets the value at the index/indices. Works on arrays of int. Returns val."
:added "1.0"}
aset-int setInt int)
(def-aset
^{:doc "Sets the value at the index/indices. Works on arrays of long. Returns val."
:added "1.0"}
aset-long setLong long)
(def-aset
^{:doc "Sets the value at the index/indices. Works on arrays of boolean. Returns val."
:added "1.0"}
aset-boolean setBoolean boolean)
(def-aset
^{:doc "Sets the value at the index/indices. Works on arrays of float. Returns val."
:added "1.0"}
aset-float setFloat float)
(def-aset
^{:doc "Sets the value at the index/indices. Works on arrays of double. Returns val."
:added "1.0"}
aset-double setDouble double)
(def-aset
^{:doc "Sets the value at the index/indices. Works on arrays of short. Returns val."
:added "1.0"}
aset-short setShort short)
(def-aset
^{:doc "Sets the value at the index/indices. Works on arrays of byte. Returns val."
:added "1.0"}
aset-byte setByte byte)
(def-aset
^{:doc "Sets the value at the index/indices. Works on arrays of char. Returns val."
:added "1.0"}
aset-char setChar char)
(defn make-array
"Creates and returns an array of instances of the specified class of
the specified dimension(s). Note that a class object is required.
Class objects can be obtained by using their imported or
fully-qualified name. Class objects for the primitive types can be
obtained using, e.g., Integer/TYPE."
{:added "1.0"}
([^Class type len]
(. Array (newInstance type (int len))))
([^Class type dim & more-dims]
(let [dims (cons dim more-dims)
^"[I" dimarray (make-array (. Integer TYPE) (count dims))]
(dotimes [i (alength dimarray)]
(aset-int dimarray i (nth dims i)))
(. Array (newInstance type dimarray)))))
(defn to-array-2d
"Returns a (potentially-ragged) 2-dimensional array of Objects
containing the contents of coll, which can be any Collection of any
Collection."
{:tag "[[Ljava.lang.Object;"
:added "1.0"}
[^java.util.Collection coll]
(let [ret (make-array (. Class (forName "[Ljava.lang.Object;")) (. coll (size)))]
(loop [i 0 xs (seq coll)]
(when xs
(aset ret i (to-array (first xs)))
(recur (inc i) (next xs))))
ret))
(defn macroexpand-1
"If form represents a macro form, returns its expansion,
else returns form."
{:added "1.0"}
[form]
(. clojure.lang.Compiler (macroexpand1 form)))
(defn macroexpand
"Repeatedly calls macroexpand-1 on form until it no longer
represents a macro form, then returns it. Note neither
macroexpand-1 nor macroexpand expand macros in subforms."
{:added "1.0"}
[form]
(let [ex (macroexpand-1 form)]
(if (identical? ex form)
form
(macroexpand ex))))
(defn create-struct
"Returns a structure basis object."
{:added "1.0"}
[& keys]
(. clojure.lang.PersistentStructMap (createSlotMap keys)))
(defmacro defstruct
"Same as (def name (create-struct keys...))"
{:added "1.0"}
[name & keys]
`(def ~name (create-struct ~@keys)))
(defn struct-map
"Returns a new structmap instance with the keys of the
structure-basis. keyvals may contain all, some or none of the basis
keys - where values are not supplied they will default to nil.
keyvals can also contain keys not in the basis."
{:added "1.0"}
[s & inits]
(. clojure.lang.PersistentStructMap (create s inits)))
(defn struct
"Returns a new structmap instance with the keys of the
structure-basis. vals must be supplied for basis keys in order -
where values are not supplied they will default to nil."
{:added "1.0"}
[s & vals]
(. clojure.lang.PersistentStructMap (construct s vals)))
(defn accessor
"Returns a fn that, given an instance of a structmap with the basis,
returns the value at the key. The key must be in the basis. The
returned function should be (slightly) more efficient than using
get, but such use of accessors should be limited to known
performance-critical areas."
{:added "1.0"}
[s key]
(. clojure.lang.PersistentStructMap (getAccessor s key)))
(defn load-reader
"Sequentially read and evaluate the set of forms contained in the
stream/file"
{:added "1.0"}
[rdr] (. clojure.lang.Compiler (load rdr)))
(defn load-string
"Sequentially read and evaluate the set of forms contained in the
string"
{:added "1.0"}
[s]
(let [rdr (-> (java.io.StringReader. s)
(clojure.lang.LineNumberingPushbackReader.))]
(load-reader rdr)))
(defn set
"Returns a set of the distinct elements of coll."
{:added "1.0"}
[coll] (clojure.lang.PersistentHashSet/create ^clojure.lang.ISeq (seq coll)))
(defn ^{:private true}
filter-key [keyfn pred amap]
(loop [ret {} es (seq amap)]
(if es
(if (pred (keyfn (first es)))
(recur (assoc ret (key (first es)) (val (first es))) (next es))
(recur ret (next es)))
ret)))
(defn find-ns
"Returns the namespace named by the symbol or nil if it doesn't exist."
{:added "1.0"}
[sym] (clojure.lang.Namespace/find sym))
(defn create-ns
"Create a new namespace named by the symbol if one doesn't already
exist, returns it or the already-existing namespace of the same
name."
{:added "1.0"}
[sym] (clojure.lang.Namespace/findOrCreate sym))
(defn remove-ns
"Removes the namespace named by the symbol. Use with caution.
Cannot be used to remove the clojure namespace."
{:added "1.0"}
[sym] (clojure.lang.Namespace/remove sym))
(defn all-ns
"Returns a sequence of all namespaces."
{:added "1.0"}
[] (clojure.lang.Namespace/all))
(defn ^clojure.lang.Namespace the-ns
"If passed a namespace, returns it. Else, when passed a symbol,
returns the namespace named by it, throwing an exception if not
found."
{:added "1.0"}
[x]
(if (instance? clojure.lang.Namespace x)
x
(or (find-ns x) (throw (Exception. (str "No namespace: " x " found"))))))
(defn ns-name
"Returns the name of the namespace, a symbol."
{:added "1.0"}
[ns]
(.getName (the-ns ns)))
(defn ns-map
"Returns a map of all the mappings for the namespace."
{:added "1.0"}
[ns]
(.getMappings (the-ns ns)))
(defn ns-unmap
"Removes the mappings for the symbol from the namespace."
{:added "1.0"}
[ns sym]
(.unmap (the-ns ns) sym))
;(defn export [syms]
; (doseq [sym syms]
; (.. *ns* (intern sym) (setExported true))))
(defn ns-publics
"Returns a map of the public intern mappings for the namespace."
{:added "1.0"}
[ns]
(let [ns (the-ns ns)]
(filter-key val (fn [^clojure.lang.Var v] (and (instance? clojure.lang.Var v)
(= ns (.ns v))
(.isPublic v)))
(ns-map ns))))
(defn ns-imports
"Returns a map of the import mappings for the namespace."
{:added "1.0"}
[ns]
(filter-key val (partial instance? Class) (ns-map ns)))
(defn ns-interns
"Returns a map of the intern mappings for the namespace."
{:added "1.0"}
[ns]
(let [ns (the-ns ns)]
(filter-key val (fn [^clojure.lang.Var v] (and (instance? clojure.lang.Var v)
(= ns (.ns v))))
(ns-map ns))))
(defn refer
"refers to all public vars of ns, subject to filters.
filters can include at most one each of:
:exclude list-of-symbols
:only list-of-symbols
:rename map-of-fromsymbol-tosymbol
For each public interned var in the namespace named by the symbol,
adds a mapping from the name of the var to the var to the current
namespace. Throws an exception if name is already mapped to
something else in the current namespace. Filters can be used to
select a subset, via inclusion or exclusion, or to provide a mapping
to a symbol different from the var's name, in order to prevent
clashes. Use :use in the ns macro in preference to calling this directly."
{:added "1.0"}
[ns-sym & filters]
(let [ns (or (find-ns ns-sym) (throw (new Exception (str "No namespace: " ns-sym))))
fs (apply hash-map filters)
nspublics (ns-publics ns)
rename (or (:rename fs) {})
exclude (set (:exclude fs))
to-do (or (:only fs) (keys nspublics))]
(doseq [sym to-do]
(when-not (exclude sym)
(let [v (nspublics sym)]
(when-not v
(throw (new java.lang.IllegalAccessError
(if (get (ns-interns ns) sym)
(str sym " is not public")
(str sym " does not exist")))))
(. *ns* (refer (or (rename sym) sym) v)))))))
(defn ns-refers
"Returns a map of the refer mappings for the namespace."
{:added "1.0"}
[ns]
(let [ns (the-ns ns)]
(filter-key val (fn [^clojure.lang.Var v] (and (instance? clojure.lang.Var v)
(not= ns (.ns v))))
(ns-map ns))))
(defn alias
"Add an alias in the current namespace to another
namespace. Arguments are two symbols: the alias to be used, and
the symbolic name of the target namespace. Use :as in the ns macro in preference
to calling this directly."
{:added "1.0"}
[alias namespace-sym]
(.addAlias *ns* alias (find-ns namespace-sym)))
(defn ns-aliases
"Returns a map of the aliases for the namespace."
{:added "1.0"}
[ns]
(.getAliases (the-ns ns)))
(defn ns-unalias
"Removes the alias for the symbol from the namespace."
{:added "1.0"}
[ns sym]
(.removeAlias (the-ns ns) sym))
(defn take-nth
"Returns a lazy seq of every nth item in coll."
{:added "1.0"}
[n coll]
(lazy-seq
(when-let [s (seq coll)]
(cons (first s) (take-nth n (drop n s))))))
(defn interleave
"Returns a lazy seq of the first item in each coll, then the second etc."
{:added "1.0"}
([c1 c2]
(lazy-seq
(let [s1 (seq c1) s2 (seq c2)]
(when (and s1 s2)
(cons (first s1) (cons (first s2)
(interleave (rest s1) (rest s2))))))))
([c1 c2 & colls]
(lazy-seq
(let [ss (map seq (conj colls c2 c1))]
(when (every? identity ss)
(concat (map first ss) (apply interleave (map rest ss))))))))
(defn var-get
"Gets the value in the var object"
{:added "1.0"}
[^clojure.lang.Var x] (. x (get)))
(defn var-set
"Sets the value in the var object to val. The var must be
thread-locally bound."
{:added "1.0"}
[^clojure.lang.Var x val] (. x (set val)))
(defmacro with-local-vars
"varbinding=> symbol init-expr
Executes the exprs in a context in which the symbols are bound to
vars with per-thread bindings to the init-exprs. The symbols refer
to the var objects themselves, and must be accessed with var-get and
var-set"
{:added "1.0"}
[name-vals-vec & body]
(assert-args with-local-vars
(vector? name-vals-vec) "a vector for its binding"
(even? (count name-vals-vec)) "an even number of forms in binding vector")
`(let [~@(interleave (take-nth 2 name-vals-vec)
(repeat '(. clojure.lang.Var (create))))]
(. clojure.lang.Var (pushThreadBindings (hash-map ~@name-vals-vec)))
(try
~@body
(finally (. clojure.lang.Var (popThreadBindings))))))
(defn ns-resolve
"Returns the var or Class to which a symbol will be resolved in the
namespace, else nil. Note that if the symbol is fully qualified,
the var/Class to which it resolves need not be present in the
namespace."
{:added "1.0"}
[ns sym]
(clojure.lang.Compiler/maybeResolveIn (the-ns ns) sym))
(defn resolve
"same as (ns-resolve *ns* symbol)"
{:added "1.0"}
[sym] (ns-resolve *ns* sym))
(defn array-map
"Constructs an array-map."
{:added "1.0"}
([] (. clojure.lang.PersistentArrayMap EMPTY))
([& keyvals] (clojure.lang.PersistentArrayMap/createWithCheck (to-array keyvals))))
(defn nthnext
"Returns the nth next of coll, (seq coll) when n is 0."
{:added "1.0"}
[coll n]
(loop [n n xs (seq coll)]
(if (and xs (pos? n))
(recur (dec n) (next xs))
xs)))
;redefine let and loop with destructuring
(defn destructure [bindings]
(let [bents (partition 2 bindings)
pb (fn pb [bvec b v]
(let [pvec
(fn [bvec b val]
(let [gvec (gensym "vec__")]
(loop [ret (-> bvec (conj gvec) (conj val))
n 0
bs b
seen-rest? false]
(if (seq bs)
(let [firstb (first bs)]
(cond
(= firstb '&) (recur (pb ret (second bs) (list `nthnext gvec n))
n
(nnext bs)
true)
(= firstb :as) (pb ret (second bs) gvec)
:else (if seen-rest?
(throw (new Exception "Unsupported binding form, only :as can follow & parameter"))
(recur (pb ret firstb (list `nth gvec n nil))
(inc n)
(next bs)
seen-rest?))))
ret))))
pmap
(fn [bvec b v]
(let [gmap (or (:as b) (gensym "map__"))
defaults (:or b)]
(loop [ret (-> bvec (conj gmap) (conj v)
(conj gmap) (conj `(if (seq? ~gmap) (apply hash-map ~gmap) ~gmap)))
bes (reduce
(fn [bes entry]
(reduce #(assoc %1 %2 ((val entry) %2))
(dissoc bes (key entry))
((key entry) bes)))
(dissoc b :as :or)
{:keys #(keyword (str %)), :strs str, :syms #(list `quote %)})]
(if (seq bes)
(let [bb (key (first bes))
bk (val (first bes))
has-default (contains? defaults bb)]
(recur (pb ret bb (if has-default
(list `get gmap bk (defaults bb))
(list `get gmap bk)))
(next bes)))
ret))))]
(cond
(symbol? b) (-> bvec (conj b) (conj v))
(vector? b) (pvec bvec b v)
(map? b) (pmap bvec b v)
:else (throw (new Exception (str "Unsupported binding form: " b))))))
process-entry (fn [bvec b] (pb bvec (first b) (second b)))]
(if (every? symbol? (map first bents))
bindings
(reduce process-entry [] bents))))
(defmacro let
"Evaluates the exprs in a lexical context in which the symbols in
the binding-forms are bound to their respective init-exprs or parts
therein."
{:added "1.0"}
[bindings & body]
(assert-args let
(vector? bindings) "a vector for its binding"
(even? (count bindings)) "an even number of forms in binding vector")
`(let* ~(destructure bindings) ~@body))
(defn ^{:private true}
maybe-destructured
[params body]
(if (every? symbol? params)
(cons params body)
(loop [params params
new-params []
lets []]
(if params
(if (symbol? (first params))
(recur (next params) (conj new-params (first params)) lets)
(let [gparam (gensym "p__")]
(recur (next params) (conj new-params gparam)
(-> lets (conj (first params)) (conj gparam)))))
`(~new-params
(let ~lets
~@body))))))
;redefine fn with destructuring and pre/post conditions
(defmacro fn
"(fn name? [params* ] exprs*)
(fn name? ([params* ] exprs*)+)
params => positional-params* , or positional-params* & next-param
positional-param => binding-form
next-param => binding-form
name => symbol
Defines a function"
{:added "1.0"}
[& sigs]
(let [name (if (symbol? (first sigs)) (first sigs) nil)
sigs (if name (next sigs) sigs)
sigs (if (vector? (first sigs)) (list sigs) sigs)
psig (fn* [sig]
(let [[params & body] sig
conds (when (and (next body) (map? (first body)))
(first body))
body (if conds (next body) body)
conds (or conds (meta params))
pre (:pre conds)
post (:post conds)
body (if post
`((let [~'% ~(if (< 1 (count body))
`(do ~@body)
(first body))]
~@(map (fn* [c] `(assert ~c)) post)
~'%))
body)
body (if pre
(concat (map (fn* [c] `(assert ~c)) pre)
body)
body)]
(maybe-destructured params body)))
new-sigs (map psig sigs)]
(with-meta
(if name
(list* 'fn* name new-sigs)
(cons 'fn* new-sigs))
(meta &form))))
(defmacro loop
"Evaluates the exprs in a lexical context in which the symbols in
the binding-forms are bound to their respective init-exprs or parts
therein. Acts as a recur target."
{:added "1.0"}
[bindings & body]
(assert-args loop
(vector? bindings) "a vector for its binding"
(even? (count bindings)) "an even number of forms in binding vector")
(let [db (destructure bindings)]
(if (= db bindings)
`(loop* ~bindings ~@body)
(let [vs (take-nth 2 (drop 1 bindings))
bs (take-nth 2 bindings)
gs (map (fn [b] (if (symbol? b) b (gensym))) bs)
bfs (reduce (fn [ret [b v g]]
(if (symbol? b)
(conj ret g v)
(conj ret g v b g)))
[] (map vector bs vs gs))]
`(let ~bfs
(loop* ~(vec (interleave gs gs))
(let ~(vec (interleave bs gs))
~@body)))))))
(defmacro when-first
"bindings => x xs
Same as (when (seq xs) (let [x (first xs)] body))"
{:added "1.0"}
[bindings & body]
(assert-args when-first
(vector? bindings) "a vector for its binding"
(= 2 (count bindings)) "exactly 2 forms in binding vector")
(let [[x xs] bindings]
`(when (seq ~xs)
(let [~x (first ~xs)]
~@body))))
(defmacro lazy-cat
"Expands to code which yields a lazy sequence of the concatenation
of the supplied colls. Each coll expr is not evaluated until it is
needed.
(lazy-cat xs ys zs) === (concat (lazy-seq xs) (lazy-seq ys) (lazy-seq zs))"
{:added "1.0"}
[& colls]
`(concat ~@(map #(list `lazy-seq %) colls)))
(defmacro for
"List comprehension. Takes a vector of one or more
binding-form/collection-expr pairs, each followed by zero or more
modifiers, and yields a lazy sequence of evaluations of expr.
Collections are iterated in a nested fashion, rightmost fastest,
and nested coll-exprs can refer to bindings created in prior
binding-forms. Supported modifiers are: :let [binding-form expr ...],
:while test, :when test.
(take 100 (for [x (range 100000000) y (range 1000000) :while (< y x)] [x y]))"
{:added "1.0"}
[seq-exprs body-expr]
(assert-args for
(vector? seq-exprs) "a vector for its binding"
(even? (count seq-exprs)) "an even number of forms in binding vector")
(let [to-groups (fn [seq-exprs]
(reduce (fn [groups [k v]]
(if (keyword? k)
(conj (pop groups) (conj (peek groups) [k v]))
(conj groups [k v])))
[] (partition 2 seq-exprs)))
err (fn [& msg] (throw (IllegalArgumentException. ^String (apply str msg))))
emit-bind (fn emit-bind [[[bind expr & mod-pairs]
& [[_ next-expr] :as next-groups]]]
(let [giter (gensym "iter__")
gxs (gensym "s__")
do-mod (fn do-mod [[[k v :as pair] & etc]]
(cond
(= k :let) `(let ~v ~(do-mod etc))
(= k :while) `(when ~v ~(do-mod etc))
(= k :when) `(if ~v
~(do-mod etc)
(recur (rest ~gxs)))
(keyword? k) (err "Invalid 'for' keyword " k)
next-groups
`(let [iterys# ~(emit-bind next-groups)
fs# (seq (iterys# ~next-expr))]
(if fs#
(concat fs# (~giter (rest ~gxs)))
(recur (rest ~gxs))))
:else `(cons ~body-expr
(~giter (rest ~gxs)))))]
(if next-groups
#_"not the inner-most loop"
`(fn ~giter [~gxs]
(lazy-seq
(loop [~gxs ~gxs]
(when-first [~bind ~gxs]
~(do-mod mod-pairs)))))
#_"inner-most loop"
(let [gi (gensym "i__")
gb (gensym "b__")
do-cmod (fn do-cmod [[[k v :as pair] & etc]]
(cond
(= k :let) `(let ~v ~(do-cmod etc))
(= k :while) `(when ~v ~(do-cmod etc))
(= k :when) `(if ~v
~(do-cmod etc)
(recur
(unchecked-inc ~gi)))
(keyword? k)
(err "Invalid 'for' keyword " k)
:else
`(do (chunk-append ~gb ~body-expr)
(recur (unchecked-inc ~gi)))))]
`(fn ~giter [~gxs]
(lazy-seq
(loop [~gxs ~gxs]
(when-let [~gxs (seq ~gxs)]
(if (chunked-seq? ~gxs)
(let [c# (chunk-first ~gxs)
size# (int (count c#))
~gb (chunk-buffer size#)]
(if (loop [~gi (int 0)]
(if (< ~gi size#)
(let [~bind (.nth c# ~gi)]
~(do-cmod mod-pairs))
true))
(chunk-cons
(chunk ~gb)
(~giter (chunk-rest ~gxs)))
(chunk-cons (chunk ~gb) nil)))
(let [~bind (first ~gxs)]
~(do-mod mod-pairs)))))))))))]
`(let [iter# ~(emit-bind (to-groups seq-exprs))]
(iter# ~(second seq-exprs)))))
(defmacro comment
"Ignores body, yields nil"
{:added "1.0"}
[& body])
(defmacro with-out-str
"Evaluates exprs in a context in which *out* is bound to a fresh
StringWriter. Returns the string created by any nested printing
calls."
{:added "1.0"}
[& body]
`(let [s# (new java.io.StringWriter)]
(binding [*out* s#]
~@body
(str s#))))
(defmacro with-in-str
"Evaluates body in a context in which *in* is bound to a fresh
StringReader initialized with the string s."
{:added "1.0"}
[s & body]
`(with-open [s# (-> (java.io.StringReader. ~s) clojure.lang.LineNumberingPushbackReader.)]
(binding [*in* s#]
~@body)))
(defn pr-str
"pr to a string, returning it"
{:tag String
:added "1.0"}
[& xs]
(with-out-str
(apply pr xs)))
(defn prn-str
"prn to a string, returning it"
{:tag String
:added "1.0"}
[& xs]
(with-out-str
(apply prn xs)))
(defn print-str
"print to a string, returning it"
{:tag String
:added "1.0"}
[& xs]
(with-out-str
(apply print xs)))
(defn println-str
"println to a string, returning it"
{:tag String
:added "1.0"}
[& xs]
(with-out-str
(apply println xs)))
(defmacro assert
"Evaluates expr and throws an exception if it does not evaluate to
logical true."
{:added "1.0"}
[x]
(when *assert*
`(when-not ~x
(throw (new AssertionError (str "Assert failed: " (pr-str '~x)))))))
(defn test
"test [v] finds fn at key :test in var metadata and calls it,
presuming failure will throw exception"
{:added "1.0"}
[v]
(let [f (:test (meta v))]
(if f
(do (f) :ok)
:no-test)))
(defn re-pattern
"Returns an instance of java.util.regex.Pattern, for use, e.g. in
re-matcher."
{:tag java.util.regex.Pattern
:added "1.0"}
[s] (if (instance? java.util.regex.Pattern s)
s
(. java.util.regex.Pattern (compile s))))
(defn re-matcher
"Returns an instance of java.util.regex.Matcher, for use, e.g. in
re-find."
{:tag java.util.regex.Matcher
:added "1.0"}
[^java.util.regex.Pattern re s]
(. re (matcher s)))
(defn re-groups
"Returns the groups from the most recent match/find. If there are no
nested groups, returns a string of the entire match. If there are
nested groups, returns a vector of the groups, the first element
being the entire match."
{:added "1.0"}
[^java.util.regex.Matcher m]
(let [gc (. m (groupCount))]
(if (zero? gc)
(. m (group))
(loop [ret [] c 0]
(if (<= c gc)
(recur (conj ret (. m (group c))) (inc c))
ret)))))
(defn re-seq
"Returns a lazy sequence of successive matches of pattern in string,
using java.util.regex.Matcher.find(), each such match processed with
re-groups."
{:added "1.0"}
[^java.util.regex.Pattern re s]
(let [m (re-matcher re s)]
((fn step []
(when (. m (find))
(cons (re-groups m) (lazy-seq (step))))))))
(defn re-matches
"Returns the match, if any, of string to pattern, using
java.util.regex.Matcher.matches(). Uses re-groups to return the
groups."
{:added "1.0"}
[^java.util.regex.Pattern re s]
(let [m (re-matcher re s)]
(when (. m (matches))
(re-groups m))))
(defn re-find
"Returns the next regex match, if any, of string to pattern, using
java.util.regex.Matcher.find(). Uses re-groups to return the
groups."
{:added "1.0"}
([^java.util.regex.Matcher m]
(when (. m (find))
(re-groups m)))
([^java.util.regex.Pattern re s]
(let [m (re-matcher re s)]
(re-find m))))
(defn rand
"Returns a random floating point number between 0 (inclusive) and
n (default 1) (exclusive)."
{:added "1.0"}
([] (. Math (random)))
([n] (* n (rand))))
(defn rand-int
"Returns a random integer between 0 (inclusive) and n (exclusive)."
{:added "1.0"}
[n] (int (rand n)))
(defmacro defn-
"same as defn, yielding non-public def"
{:added "1.0"}
[name & decls]
(list* `defn (with-meta name (assoc (meta name) :private true)) decls))
(defn print-doc [v]
(println "-------------------------")
(println (str (ns-name (:ns (meta v))) "/" (:name (meta v))))
(prn (:arglists (meta v)))
(when (:macro (meta v))
(println "Macro"))
(println " " (:doc (meta v))))
(defn find-doc
"Prints documentation for any var whose documentation or name
contains a match for re-string-or-pattern"
{:added "1.0"}
[re-string-or-pattern]
(let [re (re-pattern re-string-or-pattern)]
(doseq [ns (all-ns)
v (sort-by (comp :name meta) (vals (ns-interns ns)))
:when (and (:doc (meta v))
(or (re-find (re-matcher re (:doc (meta v))))
(re-find (re-matcher re (str (:name (meta v)))))))]
(print-doc v))))
(defn special-form-anchor
"Returns the anchor tag on http://clojure.org/special_forms for the
special form x, or nil"
{:added "1.0"}
[x]
(#{'. 'def 'do 'fn 'if 'let 'loop 'monitor-enter 'monitor-exit 'new
'quote 'recur 'set! 'throw 'try 'var} x))
(defn syntax-symbol-anchor
"Returns the anchor tag on http://clojure.org/special_forms for the
special form that uses syntax symbol x, or nil"
{:added "1.0"}
[x]
({'& 'fn 'catch 'try 'finally 'try} x))
(defn print-special-doc
[name type anchor]
(println "-------------------------")
(println name)
(println type)
(println (str " Please see http://clojure.org/special_forms#" anchor)))
(defn print-namespace-doc
"Print the documentation string of a Namespace."
{:added "1.0"}
[nspace]
(println "-------------------------")
(println (str (ns-name nspace)))
(println " " (:doc (meta nspace))))
(defmacro doc
"Prints documentation for a var or special form given its name"
{:added "1.0"}
[name]
(cond
(special-form-anchor `~name)
`(print-special-doc '~name "Special Form" (special-form-anchor '~name))
(syntax-symbol-anchor `~name)
`(print-special-doc '~name "Syntax Symbol" (syntax-symbol-anchor '~name))
:else
(let [nspace (find-ns name)]
(if nspace
`(print-namespace-doc ~nspace)
`(print-doc (var ~name))))))
(defn tree-seq
"Returns a lazy sequence of the nodes in a tree, via a depth-first walk.
branch? must be a fn of one arg that returns true if passed a node
that can have children (but may not). children must be a fn of one
arg that returns a sequence of the children. Will only be called on
nodes for which branch? returns true. Root is the root node of the
tree."
{:added "1.0"}
[branch? children root]
(let [walk (fn walk [node]
(lazy-seq
(cons node
(when (branch? node)
(mapcat walk (children node))))))]
(walk root)))
(defn file-seq
"A tree seq on java.io.Files"
{:added "1.0"}
[dir]
(tree-seq
(fn [^java.io.File f] (. f (isDirectory)))
(fn [^java.io.File d] (seq (. d (listFiles))))
dir))
(defn xml-seq
"A tree seq on the xml elements as per xml/parse"
{:added "1.0"}
[root]
(tree-seq
(complement string?)
(comp seq :content)
root))
(defn special-symbol?
"Returns true if s names a special form"
{:added "1.0"}
[s]
(contains? (. clojure.lang.Compiler specials) s))
(defn var?
"Returns true if v is of type clojure.lang.Var"
{:added "1.0"}
[v] (instance? clojure.lang.Var v))
(defn ^String subs
"Returns the substring of s beginning at start inclusive, and ending
at end (defaults to length of string), exclusive."
{:added "1.0"}
([^String s start] (. s (substring start)))
([^String s start end] (. s (substring start end))))
(defn max-key
"Returns the x for which (k x), a number, is greatest."
{:added "1.0"}
([k x] x)
([k x y] (if (> (k x) (k y)) x y))
([k x y & more]
(reduce #(max-key k %1 %2) (max-key k x y) more)))
(defn min-key
"Returns the x for which (k x), a number, is least."
{:added "1.0"}
([k x] x)
([k x y] (if (< (k x) (k y)) x y))
([k x y & more]
(reduce #(min-key k %1 %2) (min-key k x y) more)))
(defn distinct
"Returns a lazy sequence of the elements of coll with duplicates removed"
{:added "1.0"}
[coll]
(let [step (fn step [xs seen]
(lazy-seq
((fn [[f :as xs] seen]
(when-let [s (seq xs)]
(if (contains? seen f)
(recur (rest s) seen)
(cons f (step (rest s) (conj seen f))))))
xs seen)))]
(step coll #{})))
(defn replace
"Given a map of replacement pairs and a vector/collection, returns a
vector/seq with any elements = a key in smap replaced with the
corresponding val in smap"
{:added "1.0"}
[smap coll]
(if (vector? coll)
(reduce (fn [v i]
(if-let [e (find smap (nth v i))]
(assoc v i (val e))
v))
coll (range (count coll)))
(map #(if-let [e (find smap %)] (val e) %) coll)))
(defmacro dosync
"Runs the exprs (in an implicit do) in a transaction that encompasses
exprs and any nested calls. Starts a transaction if none is already
running on this thread. Any uncaught exception will abort the
transaction and flow out of dosync. The exprs may be run more than
once, but any effects on Refs will be atomic."
{:added "1.0"}
[& exprs]
`(sync nil ~@exprs))
(defmacro with-precision
"Sets the precision and rounding mode to be used for BigDecimal operations.
Usage: (with-precision 10 (/ 1M 3))
or: (with-precision 10 :rounding HALF_DOWN (/ 1M 3))
The rounding mode is one of CEILING, FLOOR, HALF_UP, HALF_DOWN,
HALF_EVEN, UP, DOWN and UNNECESSARY; it defaults to HALF_UP."
{:added "1.0"}
[precision & exprs]
(let [[body rm] (if (= (first exprs) :rounding)
[(next (next exprs))
`((. java.math.RoundingMode ~(second exprs)))]
[exprs nil])]
`(binding [*math-context* (java.math.MathContext. ~precision ~@rm)]
~@body)))
(defn mk-bound-fn
{:private true}
[^clojure.lang.Sorted sc test key]
(fn [e]
(test (.. sc comparator (compare (. sc entryKey e) key)) 0)))
(defn subseq
"sc must be a sorted collection, test(s) one of <, <=, > or
>=. Returns a seq of those entries with keys ek for
which (test (.. sc comparator (compare ek key)) 0) is true"
{:added "1.0"}
([^clojure.lang.Sorted sc test key]
(let [include (mk-bound-fn sc test key)]
(if (#{> >=} test)
(when-let [[e :as s] (. sc seqFrom key true)]
(if (include e) s (next s)))
(take-while include (. sc seq true)))))
([^clojure.lang.Sorted sc start-test start-key end-test end-key]
(when-let [[e :as s] (. sc seqFrom start-key true)]
(take-while (mk-bound-fn sc end-test end-key)
(if ((mk-bound-fn sc start-test start-key) e) s (next s))))))
(defn rsubseq
"sc must be a sorted collection, test(s) one of <, <=, > or
>=. Returns a reverse seq of those entries with keys ek for
which (test (.. sc comparator (compare ek key)) 0) is true"
{:added "1.0"}
([^clojure.lang.Sorted sc test key]
(let [include (mk-bound-fn sc test key)]
(if (#{< <=} test)
(when-let [[e :as s] (. sc seqFrom key false)]
(if (include e) s (next s)))
(take-while include (. sc seq false)))))
([^clojure.lang.Sorted sc start-test start-key end-test end-key]
(when-let [[e :as s] (. sc seqFrom end-key false)]
(take-while (mk-bound-fn sc start-test start-key)
(if ((mk-bound-fn sc end-test end-key) e) s (next s))))))
(defn repeatedly
"Takes a function of no args, presumably with side effects, and
returns an infinite (or length n if supplied) lazy sequence of calls
to it"
{:added "1.0"}
([f] (lazy-seq (cons (f) (repeatedly f))))
([n f] (take n (repeatedly f))))
(defn add-classpath
"DEPRECATED
Adds the url (String or URL object) to the classpath per
URLClassLoader.addURL"
{:added "1.0"
:deprecated "1.1"}
[url]
(println "WARNING: add-classpath is deprecated")
(clojure.lang.RT/addURL url))
(defn hash
"Returns the hash code of its argument"
{:added "1.0"}
[x] (. clojure.lang.Util (hash x)))
(defn interpose
"Returns a lazy seq of the elements of coll separated by sep"
{:added "1.0"}
[sep coll] (drop 1 (interleave (repeat sep) coll)))
(defmacro definline
"Experimental - like defmacro, except defines a named function whose
body is the expansion, calls to which may be expanded inline as if
it were a macro. Cannot be used with variadic (&) args."
{:added "1.0"}
[name & decl]
(let [[pre-args [args expr]] (split-with (comp not vector?) decl)]
`(do
(defn ~name ~@pre-args ~args ~(apply (eval (list `fn args expr)) args))
(alter-meta! (var ~name) assoc :inline (fn ~name ~args ~expr))
(var ~name))))
(defn empty
"Returns an empty collection of the same category as coll, or nil"
{:added "1.0"}
[coll]
(when (instance? clojure.lang.IPersistentCollection coll)
(.empty ^clojure.lang.IPersistentCollection coll)))
(defmacro amap
"Maps an expression across an array a, using an index named idx, and
return value named ret, initialized to a clone of a, then setting
each element of ret to the evaluation of expr, returning the new
array ret."
{:added "1.0"}
[a idx ret expr]
`(let [a# ~a
~ret (aclone a#)]
(loop [~idx (int 0)]
(if (< ~idx (alength a#))
(do
(aset ~ret ~idx ~expr)
(recur (unchecked-inc ~idx)))
~ret))))
(defmacro areduce
"Reduces an expression across an array a, using an index named idx,
and return value named ret, initialized to init, setting ret to the
evaluation of expr at each step, returning ret."
{:added "1.0"}
[a idx ret init expr]
`(let [a# ~a]
(loop [~idx (int 0) ~ret ~init]
(if (< ~idx (alength a#))
(recur (unchecked-inc ~idx) ~expr)
~ret))))
(defn float-array
"Creates an array of floats"
{:inline (fn [& args] `(. clojure.lang.Numbers float_array ~@args))
:inline-arities #{1 2}
:added "1.0"}
([size-or-seq] (. clojure.lang.Numbers float_array size-or-seq))
([size init-val-or-seq] (. clojure.lang.Numbers float_array size init-val-or-seq)))
(defn boolean-array
"Creates an array of booleans"
{:inline (fn [& args] `(. clojure.lang.Numbers boolean_array ~@args))
:inline-arities #{1 2}
:added "1.1"}
([size-or-seq] (. clojure.lang.Numbers boolean_array size-or-seq))
([size init-val-or-seq] (. clojure.lang.Numbers boolean_array size init-val-or-seq)))
(defn byte-array
"Creates an array of bytes"
{:inline (fn [& args] `(. clojure.lang.Numbers byte_array ~@args))
:inline-arities #{1 2}
:added "1.1"}
([size-or-seq] (. clojure.lang.Numbers byte_array size-or-seq))
([size init-val-or-seq] (. clojure.lang.Numbers byte_array size init-val-or-seq)))
(defn char-array
"Creates an array of chars"
{:inline (fn [& args] `(. clojure.lang.Numbers char_array ~@args))
:inline-arities #{1 2}
:added "1.1"}
([size-or-seq] (. clojure.lang.Numbers char_array size-or-seq))
([size init-val-or-seq] (. clojure.lang.Numbers char_array size init-val-or-seq)))
(defn short-array
"Creates an array of shorts"
{:inline (fn [& args] `(. clojure.lang.Numbers short_array ~@args))
:inline-arities #{1 2}
:added "1.1"}
([size-or-seq] (. clojure.lang.Numbers short_array size-or-seq))
([size init-val-or-seq] (. clojure.lang.Numbers short_array size init-val-or-seq)))
(defn double-array
"Creates an array of doubles"
{:inline (fn [& args] `(. clojure.lang.Numbers double_array ~@args))
:inline-arities #{1 2}
:added "1.0"}
([size-or-seq] (. clojure.lang.Numbers double_array size-or-seq))
([size init-val-or-seq] (. clojure.lang.Numbers double_array size init-val-or-seq)))
(defn object-array
"Creates an array of objects"
{:inline (fn [arg] `(. clojure.lang.RT object_array ~arg))
:inline-arities #{1}
:added "1.2"}
([size-or-seq] (. clojure.lang.RT object_array size-or-seq)))
(defn int-array
"Creates an array of ints"
{:inline (fn [& args] `(. clojure.lang.Numbers int_array ~@args))
:inline-arities #{1 2}
:added "1.0"}
([size-or-seq] (. clojure.lang.Numbers int_array size-or-seq))
([size init-val-or-seq] (. clojure.lang.Numbers int_array size init-val-or-seq)))
(defn long-array
"Creates an array of longs"
{:inline (fn [& args] `(. clojure.lang.Numbers long_array ~@args))
:inline-arities #{1 2}
:added "1.0"}
([size-or-seq] (. clojure.lang.Numbers long_array size-or-seq))
([size init-val-or-seq] (. clojure.lang.Numbers long_array size init-val-or-seq)))
(definline booleans
"Casts to boolean[]"
{:added "1.1"}
[xs] `(. clojure.lang.Numbers booleans ~xs))
(definline bytes
"Casts to bytes[]"
{:added "1.1"}
[xs] `(. clojure.lang.Numbers bytes ~xs))
(definline chars
"Casts to chars[]"
{:added "1.1"}
[xs] `(. clojure.lang.Numbers chars ~xs))
(definline shorts
"Casts to shorts[]"
{:added "1.1"}
[xs] `(. clojure.lang.Numbers shorts ~xs))
(definline floats
"Casts to float[]"
{:added "1.0"}
[xs] `(. clojure.lang.Numbers floats ~xs))
(definline ints
"Casts to int[]"
{:added "1.0"}
[xs] `(. clojure.lang.Numbers ints ~xs))
(definline doubles
"Casts to double[]"
{:added "1.0"}
[xs] `(. clojure.lang.Numbers doubles ~xs))
(definline longs
"Casts to long[]"
{:added "1.0"}
[xs] `(. clojure.lang.Numbers longs ~xs))
(import '(java.util.concurrent BlockingQueue LinkedBlockingQueue))
(defn seque
"Creates a queued seq on another (presumably lazy) seq s. The queued
seq will produce a concrete seq in the background, and can get up to
n items ahead of the consumer. n-or-q can be an integer n buffer
size, or an instance of java.util.concurrent BlockingQueue. Note
that reading from a seque can block if the reader gets ahead of the
producer."
{:added "1.0"}
([s] (seque 100 s))
([n-or-q s]
(let [^BlockingQueue q (if (instance? BlockingQueue n-or-q)
n-or-q
(LinkedBlockingQueue. (int n-or-q)))
NIL (Object.) ;nil sentinel since LBQ doesn't support nils
agt (agent (seq s))
fill (fn [s]
(try
(loop [[x & xs :as s] s]
(if s
(if (.offer q (if (nil? x) NIL x))
(recur xs)
s)
(.put q q))) ; q itself is eos sentinel
(catch Exception e
(.put q q)
(throw e))))
drain (fn drain []
(lazy-seq
(let [x (.take q)]
(if (identical? x q) ;q itself is eos sentinel
(do @agt nil) ;touch agent just to propagate errors
(do
(send-off agt fill)
(cons (if (identical? x NIL) nil x) (drain)))))))]
(send-off agt fill)
(drain))))
(defn class?
"Returns true if x is an instance of Class"
{:added "1.0"}
[x] (instance? Class x))
(defn- is-annotation? [c]
(and (class? c)
(.isAssignableFrom java.lang.annotation.Annotation c)))
(defn- is-runtime-annotation? [^Class c]
(boolean
(and (is-annotation? c)
(when-let [^java.lang.annotation.Retention r
(.getAnnotation c java.lang.annotation.Retention)]
(= (.value r) java.lang.annotation.RetentionPolicy/RUNTIME)))))
(defn- descriptor [^Class c] (clojure.asm.Type/getDescriptor c))
(declare process-annotation)
(defn- add-annotation [^clojure.asm.AnnotationVisitor av name v]
(cond
(vector? v) (let [avec (.visitArray av name)]
(doseq [vval v]
(add-annotation avec "value" vval))
(.visitEnd avec))
(symbol? v) (let [ev (eval v)]
(cond
(instance? java.lang.Enum ev)
(.visitEnum av name (descriptor (class ev)) (str ev))
(class? ev) (.visit av name (clojure.asm.Type/getType ev))
:else (throw (IllegalArgumentException.
(str "Unsupported annotation value: " v " of class " (class ev))))))
(seq? v) (let [[nested nv] v
c (resolve nested)
nav (.visitAnnotation av name (descriptor c))]
(process-annotation nav nv)
(.visitEnd nav))
:else (.visit av name v)))
(defn- process-annotation [av v]
(if (map? v)
(doseq [[k v] v]
(add-annotation av (name k) v))
(add-annotation av "value" v)))
(defn- add-annotations
([visitor m] (add-annotations visitor m nil))
([visitor m i]
(doseq [[k v] m]
(when (symbol? k)
(when-let [c (resolve k)]
(when (is-annotation? c)
;this is known duck/reflective as no common base of ASM Visitors
(let [av (if i
(.visitParameterAnnotation visitor i (descriptor c)
(is-runtime-annotation? c))
(.visitAnnotation visitor (descriptor c)
(is-runtime-annotation? c)))]
(process-annotation av v)
(.visitEnd av))))))))
(defn alter-var-root
"Atomically alters the root binding of var v by applying f to its
current value plus any args"
{:added "1.0"}
[^clojure.lang.Var v f & args] (.alterRoot v f args))
(defn bound?
"Returns true if all of the vars provided as arguments have any bound value, root or thread-local.
Implies that deref'ing the provided vars will succeed. Returns true if no vars are provided."
{:added "1.2"}
[& vars]
(every? #(.isBound ^clojure.lang.Var %) vars))
(defn thread-bound?
"Returns true if all of the vars provided as arguments have thread-local bindings.
Implies that set!'ing the provided vars will succeed. Returns true if no vars are provided."
{:added "1.2"}
[& vars]
(every? #(.getThreadBinding ^clojure.lang.Var %) vars))
(defn make-hierarchy
"Creates a hierarchy object for use with derive, isa? etc."
{:added "1.0"}
[] {:parents {} :descendants {} :ancestors {}})
(def ^{:private true}
global-hierarchy (make-hierarchy))
(defn not-empty
"If coll is empty, returns nil, else coll"
{:added "1.0"}
[coll] (when (seq coll) coll))
(defn bases
"Returns the immediate superclass and direct interfaces of c, if any"
{:added "1.0"}
[^Class c]
(when c
(let [i (.getInterfaces c)
s (.getSuperclass c)]
(not-empty
(if s (cons s i) i)))))
(defn supers
"Returns the immediate and indirect superclasses and interfaces of c, if any"
{:added "1.0"}
[^Class class]
(loop [ret (set (bases class)) cs ret]
(if (seq cs)
(let [c (first cs) bs (bases c)]
(recur (into ret bs) (into (disj cs c) bs)))
(not-empty ret))))
(defn isa?
"Returns true if (= child parent), or child is directly or indirectly derived from
parent, either via a Java type inheritance relationship or a
relationship established via derive. h must be a hierarchy obtained
from make-hierarchy, if not supplied defaults to the global
hierarchy"
{:added "1.0"}
([child parent] (isa? global-hierarchy child parent))
([h child parent]
(or (= child parent)
(and (class? parent) (class? child)
(. ^Class parent isAssignableFrom child))
(contains? ((:ancestors h) child) parent)
(and (class? child) (some #(contains? ((:ancestors h) %) parent) (supers child)))
(and (vector? parent) (vector? child)
(= (count parent) (count child))
(loop [ret true i 0]
(if (or (not ret) (= i (count parent)))
ret
(recur (isa? h (child i) (parent i)) (inc i))))))))
(defn parents
"Returns the immediate parents of tag, either via a Java type
inheritance relationship or a relationship established via derive. h
must be a hierarchy obtained from make-hierarchy, if not supplied
defaults to the global hierarchy"
{:added "1.0"}
([tag] (parents global-hierarchy tag))
([h tag] (not-empty
(let [tp (get (:parents h) tag)]
(if (class? tag)
(into (set (bases tag)) tp)
tp)))))
(defn ancestors
"Returns the immediate and indirect parents of tag, either via a Java type
inheritance relationship or a relationship established via derive. h
must be a hierarchy obtained from make-hierarchy, if not supplied
defaults to the global hierarchy"
{:added "1.0"}
([tag] (ancestors global-hierarchy tag))
([h tag] (not-empty
(let [ta (get (:ancestors h) tag)]
(if (class? tag)
(let [superclasses (set (supers tag))]
(reduce into superclasses
(cons ta
(map #(get (:ancestors h) %) superclasses))))
ta)))))
(defn descendants
"Returns the immediate and indirect children of tag, through a
relationship established via derive. h must be a hierarchy obtained
from make-hierarchy, if not supplied defaults to the global
hierarchy. Note: does not work on Java type inheritance
relationships."
{:added "1.0"}
([tag] (descendants global-hierarchy tag))
([h tag] (if (class? tag)
(throw (java.lang.UnsupportedOperationException. "Can't get descendants of classes"))
(not-empty (get (:descendants h) tag)))))
(defn derive
"Establishes a parent/child relationship between parent and
tag. Parent must be a namespace-qualified symbol or keyword and
child can be either a namespace-qualified symbol or keyword or a
class. h must be a hierarchy obtained from make-hierarchy, if not
supplied defaults to, and modifies, the global hierarchy."
{:added "1.0"}
([tag parent]
(assert (namespace parent))
(assert (or (class? tag) (and (instance? clojure.lang.Named tag) (namespace tag))))
(alter-var-root #'global-hierarchy derive tag parent) nil)
([h tag parent]
(assert (not= tag parent))
(assert (or (class? tag) (instance? clojure.lang.Named tag)))
(assert (instance? clojure.lang.Named parent))
(let [tp (:parents h)
td (:descendants h)
ta (:ancestors h)
tf (fn [m source sources target targets]
(reduce (fn [ret k]
(assoc ret k
(reduce conj (get targets k #{}) (cons target (targets target)))))
m (cons source (sources source))))]
(or
(when-not (contains? (tp tag) parent)
(when (contains? (ta tag) parent)
(throw (Exception. (print-str tag "already has" parent "as ancestor"))))
(when (contains? (ta parent) tag)
(throw (Exception. (print-str "Cyclic derivation:" parent "has" tag "as ancestor"))))
{:parents (assoc (:parents h) tag (conj (get tp tag #{}) parent))
:ancestors (tf (:ancestors h) tag td parent ta)
:descendants (tf (:descendants h) parent ta tag td)})
h))))
(declare flatten)
(defn underive
"Removes a parent/child relationship between parent and
tag. h must be a hierarchy obtained from make-hierarchy, if not
supplied defaults to, and modifies, the global hierarchy."
{:added "1.0"}
([tag parent] (alter-var-root #'global-hierarchy underive tag parent) nil)
([h tag parent]
(let [parentMap (:parents h)
childsParents (if (parentMap tag)
(disj (parentMap tag) parent) #{})
newParents (if (not-empty childsParents)
(assoc parentMap tag childsParents)
(dissoc parentMap tag))
deriv-seq (flatten (map #(cons (key %) (interpose (key %) (val %)))
(seq newParents)))]
(if (contains? (parentMap tag) parent)
(reduce #(apply derive %1 %2) (make-hierarchy)
(partition 2 deriv-seq))
h))))
(defn distinct?
"Returns true if no two of the arguments are ="
{:tag Boolean
:added "1.0"}
([x] true)
([x y] (not (= x y)))
([x y & more]
(if (not= x y)
(loop [s #{x y} [x & etc :as xs] more]
(if xs
(if (contains? s x)
false
(recur (conj s x) etc))
true))
false)))
(defn resultset-seq
"Creates and returns a lazy sequence of structmaps corresponding to
the rows in the java.sql.ResultSet rs"
{:added "1.0"}
[^java.sql.ResultSet rs]
(let [rsmeta (. rs (getMetaData))
idxs (range 1 (inc (. rsmeta (getColumnCount))))
keys (map (comp keyword #(.toLowerCase ^String %))
(map (fn [i] (. rsmeta (getColumnLabel i))) idxs))
check-keys
(or (apply distinct? keys)
(throw (Exception. "ResultSet must have unique column labels")))
row-struct (apply create-struct keys)
row-values (fn [] (map (fn [^Integer i] (. rs (getObject i))) idxs))
rows (fn thisfn []
(when (. rs (next))
(cons (apply struct row-struct (row-values)) (lazy-seq (thisfn)))))]
(rows)))
(defn iterator-seq
"Returns a seq on a java.util.Iterator. Note that most collections
providing iterators implement Iterable and thus support seq directly."
{:added "1.0"}
[iter]
(clojure.lang.IteratorSeq/create iter))
(defn enumeration-seq
"Returns a seq on a java.util.Enumeration"
{:added "1.0"}
[e]
(clojure.lang.EnumerationSeq/create e))
(defn format
"Formats a string using java.lang.String.format, see java.util.Formatter for format
string syntax"
{:tag String
:added "1.0"}
[fmt & args]
(String/format fmt (to-array args)))
(defn printf
"Prints formatted output, as per format"
{:added "1.0"}
[fmt & args]
(print (apply format fmt args)))
(declare gen-class)
(defmacro with-loading-context [& body]
`((fn loading# []
(. clojure.lang.Var (pushThreadBindings {clojure.lang.Compiler/LOADER
(.getClassLoader (.getClass ^Object loading#))}))
(try
~@body
(finally
(. clojure.lang.Var (popThreadBindings)))))))
(defmacro ns
"Sets *ns* to the namespace named by name (unevaluated), creating it
if needed. references can be zero or more of: (:refer-clojure ...)
(:require ...) (:use ...) (:import ...) (:load ...) (:gen-class)
with the syntax of refer-clojure/require/use/import/load/gen-class
respectively, except the arguments are unevaluated and need not be
quoted. (:gen-class ...), when supplied, defaults to :name
corresponding to the ns name, :main true, :impl-ns same as ns, and
:init-impl-ns true. All options of gen-class are
supported. The :gen-class directive is ignored when not
compiling. If :gen-class is not supplied, when compiled only an
nsname__init.class will be generated. If :refer-clojure is not used, a
default (refer 'clojure) is used. Use of ns is preferred to
individual calls to in-ns/require/use/import:
(ns foo.bar
(:refer-clojure :exclude [ancestors printf])
(:require (clojure.contrib sql sql.tests))
(:use (my.lib this that))
(:import (java.util Date Timer Random)
(java.sql Connection Statement)))"
{:arglists '([name docstring? attr-map? references*])
:added "1.0"}
[name & references]
(let [process-reference
(fn [[kname & args]]
`(~(symbol "clojure.core" (clojure.core/name kname))
~@(map #(list 'quote %) args)))
docstring (when (string? (first references)) (first references))
references (if docstring (next references) references)
name (if docstring
(vary-meta name assoc :doc docstring)
name)
metadata (when (map? (first references)) (first references))
references (if metadata (next references) references)
name (if metadata
(vary-meta name merge metadata)
name)
gen-class-clause (first (filter #(= :gen-class (first %)) references))
gen-class-call
(when gen-class-clause
(list* `gen-class :name (.replace (str name) \- \_) :impl-ns name :main true (next gen-class-clause)))
references (remove #(= :gen-class (first %)) references)
;ns-effect (clojure.core/in-ns name)
]
`(do
(clojure.core/in-ns '~name)
(with-loading-context
~@(when gen-class-call (list gen-class-call))
~@(when (and (not= name 'clojure.core) (not-any? #(= :refer-clojure (first %)) references))
`((clojure.core/refer '~'clojure.core)))
~@(map process-reference references)))))
(defmacro refer-clojure
"Same as (refer 'clojure.core )"
{:added "1.0"}
[& filters]
`(clojure.core/refer '~'clojure.core ~@filters))
(defmacro defonce
"defs name to have the root value of the expr iff the named var has no root value,
else expr is unevaluated"
{:added "1.0"}
[name expr]
`(let [v# (def ~name)]
(when-not (.hasRoot v#)
(def ~name ~expr))))
;;;;;;;;;;; require/use/load, contributed by Stephen C. Gilardi ;;;;;;;;;;;;;;;;;;
(defonce
^{:private true
:doc "A ref to a sorted set of symbols representing loaded libs"}
*loaded-libs* (ref (sorted-set)))
(defonce
^{:private true
:doc "A stack of paths currently being loaded by this thread"}
*pending-paths* ())
(defonce
^{:private true :doc
"True while a verbose load is pending"}
*loading-verbosely* false)
(defn- throw-if
"Throws an exception with a message if pred is true"
[pred fmt & args]
(when pred
(let [^String message (apply format fmt args)
exception (Exception. message)
raw-trace (.getStackTrace exception)
boring? #(not= (.getMethodName ^StackTraceElement %) "doInvoke")
trace (into-array (drop 2 (drop-while boring? raw-trace)))]
(.setStackTrace exception trace)
(throw exception))))
(defn- libspec?
"Returns true if x is a libspec"
[x]
(or (symbol? x)
(and (vector? x)
(or
(nil? (second x))
(keyword? (second x))))))
(defn- prependss
"Prepends a symbol or a seq to coll"
[x coll]
(if (symbol? x)
(cons x coll)
(concat x coll)))
(defn- root-resource
"Returns the root directory path for a lib"
{:tag String}
[lib]
(str \/
(.. (name lib)
(replace \- \_)
(replace \. \/))))
(defn- root-directory
"Returns the root resource path for a lib"
[lib]
(let [d (root-resource lib)]
(subs d 0 (.lastIndexOf d "/"))))
(declare load)
(defn- load-one
"Loads a lib given its name. If need-ns, ensures that the associated
namespace exists after loading. If require, records the load so any
duplicate loads can be skipped."
[lib need-ns require]
(load (root-resource lib))
(throw-if (and need-ns (not (find-ns lib)))
"namespace '%s' not found after loading '%s'"
lib (root-resource lib))
(when require
(dosync
(commute *loaded-libs* conj lib))))
(defn- load-all
"Loads a lib given its name and forces a load of any libs it directly or
indirectly loads. If need-ns, ensures that the associated namespace
exists after loading. If require, records the load so any duplicate loads
can be skipped."
[lib need-ns require]
(dosync
(commute *loaded-libs* #(reduce conj %1 %2)
(binding [*loaded-libs* (ref (sorted-set))]
(load-one lib need-ns require)
@*loaded-libs*))))
(defn- load-lib
"Loads a lib with options"
[prefix lib & options]
(throw-if (and prefix (pos? (.indexOf (name lib) (int \.))))
"lib names inside prefix lists must not contain periods")
(let [lib (if prefix (symbol (str prefix \. lib)) lib)
opts (apply hash-map options)
{:keys [as reload reload-all require use verbose]} opts
loaded (contains? @*loaded-libs* lib)
load (cond reload-all
load-all
(or reload (not require) (not loaded))
load-one)
need-ns (or as use)
filter-opts (select-keys opts '(:exclude :only :rename))]
(binding [*loading-verbosely* (or *loading-verbosely* verbose)]
(if load
(load lib need-ns require)
(throw-if (and need-ns (not (find-ns lib)))
"namespace '%s' not found" lib))
(when (and need-ns *loading-verbosely*)
(printf "(clojure.core/in-ns '%s)\n" (ns-name *ns*)))
(when as
(when *loading-verbosely*
(printf "(clojure.core/alias '%s '%s)\n" as lib))
(alias as lib))
(when use
(when *loading-verbosely*
(printf "(clojure.core/refer '%s" lib)
(doseq [opt filter-opts]
(printf " %s '%s" (key opt) (print-str (val opt))))
(printf ")\n"))
(apply refer lib (mapcat seq filter-opts))))))
(defn- load-libs
"Loads libs, interpreting libspecs, prefix lists, and flags for
forwarding to load-lib"
[& args]
(let [flags (filter keyword? args)
opts (interleave flags (repeat true))
args (filter (complement keyword?) args)]
; check for unsupported options
(let [supported #{:as :reload :reload-all :require :use :verbose}
unsupported (seq (remove supported flags))]
(throw-if unsupported
(apply str "Unsupported option(s) supplied: "
(interpose \, unsupported))))
; check a load target was specified
(throw-if (not (seq args)) "Nothing specified to load")
(doseq [arg args]
(if (libspec? arg)
(apply load-lib nil (prependss arg opts))
(let [[prefix & args] arg]
(throw-if (nil? prefix) "prefix cannot be nil")
(doseq [arg args]
(apply load-lib prefix (prependss arg opts))))))))
(defn- check-cyclic-dependency
"Detects and rejects non-trivial cyclic load dependencies. The
exception message shows the dependency chain with the cycle
highlighted. Ignores the trivial case of a file attempting to load
itself because that can occur when a gen-class'd class loads its
implementation."
[path]
(when (some #{path} (rest *pending-paths*))
(let [pending (map #(if (= % path) (str "[ " % " ]") %)
(cons path *pending-paths*))
chain (apply str (interpose "->" pending))]
(throw (Exception. (str "Cyclic load dependency: " chain))))))
;; Public
(defn require
"Loads libs, skipping any that are already loaded. Each argument is
either a libspec that identifies a lib, a prefix list that identifies
multiple libs whose names share a common prefix, or a flag that modifies
how all the identified libs are loaded. Use :require in the ns macro
in preference to calling this directly.
Libs
A 'lib' is a named set of resources in classpath whose contents define a
library of Clojure code. Lib names are symbols and each lib is associated
with a Clojure namespace and a Java package that share its name. A lib's
name also locates its root directory within classpath using Java's
package name to classpath-relative path mapping. All resources in a lib
should be contained in the directory structure under its root directory.
All definitions a lib makes should be in its associated namespace.
'require loads a lib by loading its root resource. The root resource path
is derived from the lib name in the following manner:
Consider a lib named by the symbol 'x.y.z; it has the root directory
/x/y/, and its root resource is /x/y/z.clj. The root
resource should contain code to create the lib's namespace (usually by using
the ns macro) and load any additional lib resources.
Libspecs
A libspec is a lib name or a vector containing a lib name followed by
options expressed as sequential keywords and arguments.
Recognized options: :as
:as takes a symbol as its argument and makes that symbol an alias to the
lib's namespace in the current namespace.
Prefix Lists
It's common for Clojure code to depend on several libs whose names have
the same prefix. When specifying libs, prefix lists can be used to reduce
repetition. A prefix list contains the shared prefix followed by libspecs
with the shared prefix removed from the lib names. After removing the
prefix, the names that remain must not contain any periods.
Flags
A flag is a keyword.
Recognized flags: :reload, :reload-all, :verbose
:reload forces loading of all the identified libs even if they are
already loaded
:reload-all implies :reload and also forces loading of all libs that the
identified libs directly or indirectly load via require or use
:verbose triggers printing information about each load, alias, and refer
Example:
The following would load the libraries clojure.zip and clojure.set
abbreviated as 's'.
(require '(clojure zip [set :as s]))"
{:added "1.0"}
[& args]
(apply load-libs :require args))
(defn use
"Like 'require, but also refers to each lib's namespace using
clojure.core/refer. Use :use in the ns macro in preference to calling
this directly.
'use accepts additional options in libspecs: :exclude, :only, :rename.
The arguments and semantics for :exclude, :only, and :rename are the same
as those documented for clojure.core/refer."
{:added "1.0"}
[& args] (apply load-libs :require :use args))
(defn loaded-libs
"Returns a sorted set of symbols naming the currently loaded libs"
{:added "1.0"}
[] @*loaded-libs*)
(defn load
"Loads Clojure code from resources in classpath. A path is interpreted as
classpath-relative if it begins with a slash or relative to the root
directory for the current namespace otherwise."
{:added "1.0"}
[& paths]
(doseq [^String path paths]
(let [^String path (if (.startsWith path "/")
path
(str (root-directory (ns-name *ns*)) \/ path))]
(when *loading-verbosely*
(printf "(clojure.core/load \"%s\")\n" path)
(flush))
(check-cyclic-dependency path)
(when-not (= path (first *pending-paths*))
(binding [*pending-paths* (conj *pending-paths* path)]
(clojure.lang.RT/load (.substring path 1)))))))
(defn compile
"Compiles the namespace named by the symbol lib into a set of
classfiles. The source for the lib must be in a proper
classpath-relative directory. The output files will go into the
directory specified by *compile-path*, and that directory too must
be in the classpath."
{:added "1.0"}
[lib]
(binding [*compile-files* true]
(load-one lib true true))
lib)
;;;;;;;;;;;;; nested associative ops ;;;;;;;;;;;
(defn get-in
"Returns the value in a nested associative structure,
where ks is a sequence of ke(ys. Returns nil if the key is not present,
or the not-found value if supplied."
{:added "1.2"}
([m ks]
(reduce get m ks))
([m ks not-found]
(loop [sentinel (Object.)
m m
ks (seq ks)]
(if ks
(let [m (get m (first ks) sentinel)]
(if (identical? sentinel m)
not-found
(recur sentinel m (next ks))))
m))))
(defn assoc-in
"Associates a value in a nested associative structure, where ks is a
sequence of keys and v is the new value and returns a new nested structure.
If any levels do not exist, hash-maps will be created."
{:added "1.0"}
[m [k & ks] v]
(if ks
(assoc m k (assoc-in (get m k) ks v))
(assoc m k v)))
(defn update-in
"'Updates' a value in a nested associative structure, where ks is a
sequence of keys and f is a function that will take the old value
and any supplied args and return the new value, and returns a new
nested structure. If any levels do not exist, hash-maps will be
created."
{:added "1.0"}
([m [k & ks] f & args]
(if ks
(assoc m k (apply update-in (get m k) ks f args))
(assoc m k (apply f (get m k) args)))))
(defn empty?
"Returns true if coll has no items - same as (not (seq coll)).
Please use the idiom (seq x) rather than (not (empty? x))"
{:added "1.0"}
[coll] (not (seq coll)))
(defn coll?
"Returns true if x implements IPersistentCollection"
{:added "1.0"}
[x] (instance? clojure.lang.IPersistentCollection x))
(defn list?
"Returns true if x implements IPersistentList"
{:added "1.0"}
[x] (instance? clojure.lang.IPersistentList x))
(defn set?
"Returns true if x implements IPersistentSet"
{:added "1.0"}
[x] (instance? clojure.lang.IPersistentSet x))
(defn ifn?
"Returns true if x implements IFn. Note that many data structures
(e.g. sets and maps) implement IFn"
{:added "1.0"}
[x] (instance? clojure.lang.IFn x))
(defn fn?
"Returns true if x implements Fn, i.e. is an object created via fn."
{:added "1.0"}
[x] (instance? clojure.lang.Fn x))
(defn associative?
"Returns true if coll implements Associative"
{:added "1.0"}
[coll] (instance? clojure.lang.Associative coll))
(defn sequential?
"Returns true if coll implements Sequential"
{:added "1.0"}
[coll] (instance? clojure.lang.Sequential coll))
(defn sorted?
"Returns true if coll implements Sorted"
{:added "1.0"}
[coll] (instance? clojure.lang.Sorted coll))
(defn counted?
"Returns true if coll implements count in constant time"
{:added "1.0"}
[coll] (instance? clojure.lang.Counted coll))
(defn reversible?
"Returns true if coll implements Reversible"
{:added "1.0"}
[coll] (instance? clojure.lang.Reversible coll))
(def
^{:doc "bound in a repl thread to the most recent value printed"
:added "1.0"}
*1)
(def
^{:doc "bound in a repl thread to the second most recent value printed"
:added "1.0"}
*2)
(def
^{:doc "bound in a repl thread to the third most recent value printed"
:added "1.0"}
*3)
(def
^{:doc "bound in a repl thread to the most recent exception caught by the repl"
:added "1.0"}
*e)
(defn trampoline
"trampoline can be used to convert algorithms requiring mutual
recursion without stack consumption. Calls f with supplied args, if
any. If f returns a fn, calls that fn with no arguments, and
continues to repeat, until the return value is not a fn, then
returns that non-fn value. Note that if you want to return a fn as a
final value, you must wrap it in some data structure and unpack it
after trampoline returns."
{:added "1.0"}
([f]
(let [ret (f)]
(if (fn? ret)
(recur ret)
ret)))
([f & args]
(trampoline #(apply f args))))
(defn intern
"Finds or creates a var named by the symbol name in the namespace
ns (which can be a symbol or a namespace), setting its root binding
to val if supplied. The namespace must exist. The var will adopt any
metadata from the name symbol. Returns the var."
{:added "1.0"}
([ns ^clojure.lang.Symbol name]
(let [v (clojure.lang.Var/intern (the-ns ns) name)]
(when (meta name) (.setMeta v (meta name)))
v))
([ns name val]
(let [v (clojure.lang.Var/intern (the-ns ns) name val)]
(when (meta name) (.setMeta v (meta name)))
v)))
(defmacro while
"Repeatedly executes body while test expression is true. Presumes
some side-effect will cause test to become false/nil. Returns nil"
{:added "1.0"}
[test & body]
`(loop []
(when ~test
~@body
(recur))))
(defn memoize
"Returns a memoized version of a referentially transparent function. The
memoized version of the function keeps a cache of the mapping from arguments
to results and, when calls with the same arguments are repeated often, has
higher performance at the expense of higher memory use."
{:added "1.0"}
[f]
(let [mem (atom {})]
(fn [& args]
(if-let [e (find @mem args)]
(val e)
(let [ret (apply f args)]
(swap! mem assoc args ret)
ret)))))
(defmacro condp
"Takes a binary predicate, an expression, and a set of clauses.
Each clause can take the form of either:
test-expr result-expr
test-expr :>> result-fn
Note :>> is an ordinary keyword.
For each clause, (pred test-expr expr) is evaluated. If it returns
logical true, the clause is a match. If a binary clause matches, the
result-expr is returned, if a ternary clause matches, its result-fn,
which must be a unary function, is called with the result of the
predicate as its argument, the result of that call being the return
value of condp. A single default expression can follow the clauses,
and its value will be returned if no clause matches. If no default
expression is provided and no clause matches, an
IllegalArgumentException is thrown."
{:added "1.0"}
[pred expr & clauses]
(let [gpred (gensym "pred__")
gexpr (gensym "expr__")
emit (fn emit [pred expr args]
(let [[[a b c :as clause] more]
(split-at (if (= :>> (second args)) 3 2) args)
n (count clause)]
(cond
(= 0 n) `(throw (IllegalArgumentException. (str "No matching clause: " ~expr)))
(= 1 n) a
(= 2 n) `(if (~pred ~a ~expr)
~b
~(emit pred expr more))
:else `(if-let [p# (~pred ~a ~expr)]
(~c p#)
~(emit pred expr more)))))
gres (gensym "res__")]
`(let [~gpred ~pred
~gexpr ~expr]
~(emit gpred gexpr clauses))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; var documentation ;;;;;;;;;;;;;;;;;;;;;;;;;;
(alter-meta! #'*agent* assoc :added "1.0")
(alter-meta! #'in-ns assoc :added "1.0")
(alter-meta! #'load-file assoc :added "1.0")
(defmacro add-doc-and-meta {:private true} [name docstring meta]
`(alter-meta! (var ~name) merge (assoc ~meta :doc ~docstring)))
(add-doc-and-meta *file*
"The path of the file being evaluated, as a String.
Evaluates to nil when there is no file, eg. in the REPL."
{:added "1.0"})
(add-doc-and-meta *command-line-args*
"A sequence of the supplied command line arguments, or nil if
none were supplied"
{:added "1.0"})
(add-doc-and-meta *warn-on-reflection*
"When set to true, the compiler will emit warnings when reflection is
needed to resolve Java method calls or field accesses.
Defaults to false."
{:added "1.0"})
(add-doc-and-meta *compile-path*
"Specifies the directory where 'compile' will write out .class
files. This directory must be in the classpath for 'compile' to
work.
Defaults to \"classes\""
{:added "1.0"})
(add-doc-and-meta *compile-files*
"Set to true when compiling files, false otherwise."
{:added "1.0"})
(add-doc-and-meta *ns*
"A clojure.lang.Namespace object representing the current namespace."
{:added "1.0"})
(add-doc-and-meta *in*
"A java.io.Reader object representing standard input for read operations.
Defaults to System/in, wrapped in a LineNumberingPushbackReader"
{:added "1.0"})
(add-doc-and-meta *out*
"A java.io.Writer object representing standard output for print operations.
Defaults to System/out"
{:added "1.0"})
(add-doc-and-meta *err*
"A java.io.Writer object representing standard error for print operations.
Defaults to System/err, wrapped in a PrintWriter"
{:added "1.0"})
(add-doc-and-meta *flush-on-newline*
"When set to true, output will be flushed whenever a newline is printed.
Defaults to true."
{:added "1.0"})
(add-doc-and-meta *print-meta*
"If set to logical true, when printing an object, its metadata will also
be printed in a form that can be read back by the reader.
Defaults to false."
{:added "1.0"})
(add-doc-and-meta *print-dup*
"When set to logical true, objects will be printed in a way that preserves
their type when read in later.
Defaults to false."
{:added "1.0"})
(add-doc-and-meta *print-readably*
"When set to logical false, strings and characters will be printed with
non-alphanumeric characters converted to the appropriate escape sequences.
Defaults to true"
{:added "1.0"})
(add-doc-and-meta *read-eval*
"When set to logical false, the EvalReader (#=(...)) is disabled in the
read/load in the thread-local binding.
Example: (binding [*read-eval* false] (read-string \"#=(eval (def x 3))\"))
Defaults to true"
{:added "1.0"})
(defn future?
"Returns true if x is a future"
{:added "1.1"}
[x] (instance? java.util.concurrent.Future x))
(defn future-done?
"Returns true if future f is done"
{:added "1.1"}
[^java.util.concurrent.Future f] (.isDone f))
(defmacro letfn
"Takes a vector of function specs and a body, and generates a set of
bindings of functions to their names. All of the names are available
in all of the definitions of the functions, as well as the body.
fnspec ==> (fname [params*] exprs) or (fname ([params*] exprs)+)"
{:added "1.0"}
[fnspecs & body]
`(letfn* ~(vec (interleave (map first fnspecs)
(map #(cons `fn %) fnspecs)))
~@body))
;;;;;;; case ;;;;;;;;;;;;;
(defn- shift-mask [shift mask x]
(-> x (bit-shift-right shift) (bit-and mask)))
(defn- min-hash
"takes a collection of keys and returns [shift mask]"
[keys]
(let [hashes (map hash keys)
cnt (count keys)]
(when-not (apply distinct? hashes)
(throw (IllegalArgumentException. "Hashes must be distinct")))
(or (first
(filter (fn [[s m]]
(apply distinct? (map #(shift-mask s m %) hashes)))
(for [mask (map #(dec (bit-shift-left 1 %)) (range 1 14))
shift (range 0 31)]
[shift mask])))
(throw (IllegalArgumentException. "No distinct mapping found")))))
(defmacro case
"Takes an expression, and a set of clauses.
Each clause can take the form of either:
test-constant result-expr
(test-constant1 ... test-constantN) result-expr
The test-constants are not evaluated. They must be compile-time
literals, and need not be quoted. If the expression is equal to a
test-constant, the corresponding result-expr is returned. A single
default expression can follow the clauses, and its value will be
returned if no clause matches. If no default expression is provided
and no clause matches, an IllegalArgumentException is thrown.
Unlike cond and condp, case does a constant-time dispatch, the
clauses are not considered sequentially. All manner of constant
expressions are acceptable in case, including numbers, strings,
symbols, keywords, and (Clojure) composites thereof. Note that since
lists are used to group multiple constants that map to the same
expression, a vector can be used to match a list if needed. The
test-constants need not be all of the same type."
{:added "1.2"}
[e & clauses]
(let [ge (with-meta (gensym) {:tag Object})
default (if (odd? (count clauses))
(last clauses)
`(throw (IllegalArgumentException. (str "No matching clause: " ~ge))))
cases (partition 2 clauses)
case-map (reduce (fn [m [test expr]]
(if (seq? test)
(into m (zipmap test (repeat expr)))
(assoc m test expr)))
{} cases)
[shift mask] (if (seq case-map) (min-hash (keys case-map)) [0 0])
hmap (reduce (fn [m [test expr :as te]]
(assoc m (shift-mask shift mask (hash test)) te))
(sorted-map) case-map)]
`(let [~ge ~e]
~(condp = (count clauses)
0 default
1 default
`(case* ~ge ~shift ~mask ~(key (first hmap)) ~(key (last hmap)) ~default ~hmap
~(every? keyword? (keys case-map)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; helper files ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(alter-meta! (find-ns 'clojure.core) assoc :doc "Fundamental library of the Clojure language")
(load "core_proxy")
(load "core_print")
(load "genclass")
(load "core_deftype")
(load "core/protocols")
(load "gvec")
;; redefine reduce with internal-reduce
#_(defn reduce
"f should be a function of 2 arguments. If val is not supplied,
returns the result of applying f to the first 2 items in coll, then
applying f to that result and the 3rd item, etc. If coll contains no
items, f must accept no arguments as well, and reduce returns the
result of calling f with no arguments. If coll has only 1 item, it
is returned and f is not called. If val is supplied, returns the
result of applying f to val and the first item in coll, then
applying f to that result and the 2nd item, etc. If coll contains no
items, returns val and f is not called."
{:added "1.0"}
([f coll]
(if-let [s (seq coll)]
(reduce f (first s) (next s))
(f)))
([f val coll]
(let [s (seq coll)]
(clojure.core.protocols/internal-reduce s f val))))
(require '[clojure.java.io :as jio])
(defn- normalize-slurp-opts
[opts]
(if (string? (first opts))
(do
(println "WARNING: (slurp f enc) is deprecated, use (slurp f :encoding enc).")
[:encoding (first opts)])
opts))
(defn slurp
"Reads the file named by f using the encoding enc into a string
and returns it."
{:added "1.0"}
([f & opts]
(let [opts (normalize-slurp-opts opts)
sb (StringBuilder.)]
(with-open [#^java.io.Reader r (apply jio/reader f opts)]
(loop [c (.read r)]
(if (neg? c)
(str sb)
(do
(.append sb (char c))
(recur (.read r)))))))))
(defn spit
"Opposite of slurp. Opens f with writer, writes content, then
closes f. Options passed to clojure.java.io/writer."
{:added "1.2"}
[f content & options]
(with-open [#^java.io.Writer w (apply jio/writer f options)]
(.write w (str content))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; futures (needs proxy);;;;;;;;;;;;;;;;;;
(defn future-call
"Takes a function of no args and yields a future object that will
invoke the function in another thread, and will cache the result and
return it on all subsequent calls to deref/@. If the computation has
not yet finished, calls to deref/@ will block."
{:added "1.1"}
[^Callable f]
(let [fut (.submit clojure.lang.Agent/soloExecutor f)]
(reify
clojure.lang.IDeref
(deref [_] (.get fut))
java.util.concurrent.Future
(get [_] (.get fut))
(get [_ timeout unit] (.get fut timeout unit))
(isCancelled [_] (.isCancelled fut))
(isDone [_] (.isDone fut))
(cancel [_ interrupt?] (.cancel fut interrupt?)))))
(defmacro future
"Takes a body of expressions and yields a future object that will
invoke the body in another thread, and will cache the result and
return it on all subsequent calls to deref/@. If the computation has
not yet finished, calls to deref/@ will block."
{:added "1.1"}
[& body] `(future-call (^{:once true} fn* [] ~@body)))
(defn future-cancel
"Cancels the future, if possible."
{:added "1.1"}
[^java.util.concurrent.Future f] (.cancel f true))
(defn future-cancelled?
"Returns true if future f is cancelled"
{:added "1.1"}
[^java.util.concurrent.Future f] (.isCancelled f))
(defn pmap
"Like map, except f is applied in parallel. Semi-lazy in that the
parallel computation stays ahead of the consumption, but doesn't
realize the entire result unless required. Only useful for
computationally intensive functions where the time of f dominates
the coordination overhead."
{:added "1.0"}
([f coll]
(let [n (+ 2 (.. Runtime getRuntime availableProcessors))
rets (map #(future (f %)) coll)
step (fn step [[x & xs :as vs] fs]
(lazy-seq
(if-let [s (seq fs)]
(cons (deref x) (step xs (rest s)))
(map deref vs))))]
(step rets (drop n rets))))
([f coll & colls]
(let [step (fn step [cs]
(lazy-seq
(let [ss (map seq cs)]
(when (every? identity ss)
(cons (map first ss) (step (map rest ss)))))))]
(pmap #(apply f %) (step (cons coll colls))))))
(defn pcalls
"Executes the no-arg fns in parallel, returning a lazy sequence of
their values"
{:added "1.0"}
[& fns] (pmap #(%) fns))
(defmacro pvalues
"Returns a lazy sequence of the values of the exprs, which are
evaluated in parallel"
{:added "1.0"}
[& exprs]
`(pcalls ~@(map #(list `fn [] %) exprs)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; clojure version number ;;;;;;;;;;;;;;;;;;;;;;
(let [version-stream (.getResourceAsStream (clojure.lang.RT/baseLoader)
"clojure/version.properties")
properties (doto (new java.util.Properties) (.load version-stream))
prop (fn [k] (.getProperty properties (str "clojure.version." k)))
clojure-version {:major (Integer/valueOf ^String (prop "major"))
:minor (Integer/valueOf ^String (prop "minor"))
:incremental (Integer/valueOf ^String (prop "incremental"))
:qualifier (prop "qualifier")}]
(def *clojure-version*
(if (not (= (prop "interim") "false"))
(clojure.lang.RT/assoc clojure-version :interim true)
clojure-version)))
(add-doc-and-meta *clojure-version*
"The version info for Clojure core, as a map containing :major :minor
:incremental and :qualifier keys. Feature releases may increment
:minor and/or :major, bugfix releases will increment :incremental.
Possible values of :qualifier include \"GA\", \"SNAPSHOT\", \"RC-x\" \"BETA-x\""
{:added "1.0"})
(defn
clojure-version
"Returns clojure version as a printable string."
{:added "1.0"}
[]
(str (:major *clojure-version*)
"."
(:minor *clojure-version*)
(when-let [i (:incremental *clojure-version*)]
(str "." i))
(when-let [q (:qualifier *clojure-version*)]
(when (pos? (count q)) (str "-" q)))
(when (:interim *clojure-version*)
"-SNAPSHOT")))
(defn promise
"Alpha - subject to change.
Returns a promise object that can be read with deref/@, and set,
once only, with deliver. Calls to deref/@ prior to delivery will
block. All subsequent derefs will return the same delivered value
without blocking."
{:added "1.1"}
[]
(let [d (java.util.concurrent.CountDownLatch. 1)
v (atom nil)]
(reify
clojure.lang.IDeref
(deref [_] (.await d) @v)
clojure.lang.IFn
(invoke [this x]
(locking d
(if (pos? (.getCount d))
(do (reset! v x)
(.countDown d)
this)
(throw (IllegalStateException. "Multiple deliver calls to a promise"))))))))
(defn deliver
"Alpha - subject to change.
Delivers the supplied value to the promise, releasing any pending
derefs. A subsequent call to deliver on a promise will throw an exception."
{:added "1.1"}
[promise val] (promise val))
(defn flatten
"Takes any nested combination of sequential things (lists, vectors,
etc.) and returns their contents as a single, flat sequence.
(flatten nil) returns nil."
{:added "1.2"}
[x]
(filter (complement sequential?)
(rest (tree-seq sequential? seq x))))
(defn group-by
"Returns a map of the elements of coll keyed by the result of
f on each element. The value at each key will be a vector of the
corresponding elements, in the order they appeared in coll."
{:added "1.2"}
[f coll]
(persistent!
(reduce
(fn [ret x]
(let [k (f x)]
(assoc! ret k (conj (get ret k []) x))))
(transient {}) coll)))
(defn partition-by
"Applies f to each value in coll, splitting it each time f returns
a new value. Returns a lazy seq of partitions."
{:added "1.2"}
[f coll]
(lazy-seq
(when-let [s (seq coll)]
(let [fst (first s)
fv (f fst)
run (cons fst (take-while #(= fv (f %)) (rest s)))]
(cons run (partition-by f (drop (count run) s)))))))
(defn frequencies
"Returns a map from distinct items in coll to the number of times
they appear."
{:added "1.2"}
[coll]
(persistent!
(reduce (fn [counts x]
(assoc! counts x (inc (get counts x 0))))
(transient {}) coll)))
(defn reductions
"Returns a lazy seq of the intermediate values of the reduction (as
per reduce) of coll by f, starting with init."
{:added "1.2"}
([f coll]
(lazy-seq
(if-let [s (seq coll)]
(reductions f (first s) (rest s))
(list (f)))))
([f init coll]
(cons init
(lazy-seq
(when-let [s (seq coll)]
(reductions f (f init (first s)) (rest s)))))))
(defn rand-nth
"Return a random element of the (sequential) collection. Will have
the same performance characteristics as nth for the given
collection."
{:added "1.2"}
[coll]
(nth coll (rand-int (count coll))))
(defn partition-all
"Returns a lazy sequence of lists like partition, but may include
partitions with fewer than n items at the end."
{:added "1.2"}
([n coll]
(partition-all n n coll))
([n step coll]
(lazy-seq
(when-let [s (seq coll)]
(cons (take n s) (partition-all n step (drop step s)))))))
(defn shuffle
"Return a random permutation of coll"
{:added "1.2"}
[coll]
(let [al (java.util.ArrayList. coll)]
(java.util.Collections/shuffle al)
(clojure.lang.RT/vector (.toArray al))))
(defn map-indexed
"Returns a lazy sequence consisting of the result of applying f to 0
and the first item of coll, followed by applying f to 1 and the second
item in coll, etc, until coll is exhausted. Thus function f should
accept 2 arguments, index and item."
{:added "1.2"}
[f coll]
(letfn [(mapi [idx coll]
(lazy-seq
(when-let [s (seq coll)]
(if (chunked-seq? s)
(let [c (chunk-first s)
size (int (count c))
b (chunk-buffer size)]
(dotimes [i size]
(chunk-append b (f (+ idx i) (.nth c i))))
(chunk-cons (chunk b) (mapi (+ idx size) (chunk-rest s))))
(cons (f idx (first s)) (mapi (inc idx) (rest s)))))))]
(mapi 0 coll)))
(defn keep
"Returns a lazy sequence of the non-nil results of (f item). Note,
this means false return values will be included. f must be free of
side-effects."
{:added "1.2"}
([f coll]
(lazy-seq
(when-let [s (seq coll)]
(if (chunked-seq? s)
(let [c (chunk-first s)
size (count c)
b (chunk-buffer size)]
(dotimes [i size]
(let [x (f (.nth c i))]
(when-not (nil? x)
(chunk-append b x))))
(chunk-cons (chunk b) (keep f (chunk-rest s))))
(let [x (f (first s))]
(if (nil? x)
(keep f (rest s))
(cons x (keep f (rest s))))))))))
(defn keep-indexed
"Returns a lazy sequence of the non-nil results of (f index item). Note,
this means false return values will be included. f must be free of
side-effects."
{:added "1.2"}
([f coll]
(letfn [(keepi [idx coll]
(lazy-seq
(when-let [s (seq coll)]
(if (chunked-seq? s)
(let [c (chunk-first s)
size (count c)
b (chunk-buffer size)]
(dotimes [i size]
(let [x (f (+ idx i) (.nth c i))]
(when-not (nil? x)
(chunk-append b x))))
(chunk-cons (chunk b) (keepi (+ idx size) (chunk-rest s))))
(let [x (f idx (first s))]
(if (nil? x)
(keepi (inc idx) (rest s))
(cons x (keepi (inc idx) (rest s)))))))))]
(keepi 0 coll))))
(defn fnil
"Takes a function f, and returns a function that calls f, replacing
a nil first argument to f with the supplied value x. Higher arity
versions can replace arguments in the second and third
positions (y, z). Note that the function f can take any number of
arguments, not just the one(s) being nil-patched."
{:added "1.2"}
([f x]
(fn
([a] (f (if (nil? a) x a)))
([a b] (f (if (nil? a) x a) b))
([a b c] (f (if (nil? a) x a) b c))
([a b c & ds] (apply f (if (nil? a) x a) b c ds))))
([f x y]
(fn
([a b] (f (if (nil? a) x a) (if (nil? b) y b)))
([a b c] (f (if (nil? a) x a) (if (nil? b) y b) c))
([a b c & ds] (apply f (if (nil? a) x a) (if (nil? b) y b) c ds))))
([f x y z]
(fn
([a b] (f (if (nil? a) x a) (if (nil? b) y b)))
([a b c] (f (if (nil? a) x a) (if (nil? b) y b) (if (nil? c) z c)))
([a b c & ds] (apply f (if (nil? a) x a) (if (nil? b) y b) (if (nil? c) z c) ds)))))
(defn- ^{:dynamic true} assert-valid-fdecl
"A good fdecl looks like (([a] ...) ([a b] ...)) near the end of defn."
[fdecl]
(if-let [bad-args (seq (remove #(vector? %) (map first fdecl)))]
(throw (IllegalArgumentException. (str "Parameter declaration " (first bad-args) " should be a vector")))))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/core/ 0000775 0000000 0000000 00000000000 11575623476 0021311 5 ustar 00root root 0000000 0000000 clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/core/protocols.clj 0000664 0000000 0000000 00000004704 11575623476 0024034 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns clojure.core.protocols)
(defprotocol InternalReduce
"Protocol for concrete seq types that can reduce themselves
faster than first/next recursion. Called by clojure.core/reduce."
(internal-reduce [seq f start]))
(extend-protocol InternalReduce
nil
(internal-reduce
[s f val]
val)
;; handles vectors and ranges
clojure.lang.IChunkedSeq
(internal-reduce
[s f val]
(if-let [s (seq s)]
(if (chunked-seq? s)
(recur (chunk-next s)
f
(.reduce (chunk-first s) f val))
(internal-reduce s f val))
val))
clojure.lang.StringSeq
(internal-reduce
[str-seq f val]
(let [s (.s str-seq)]
(loop [i (.i str-seq)
val val]
(if (< i (.length s))
(recur (inc i) (f val (.charAt s i)))
val))))
clojure.lang.ArraySeq
(internal-reduce
[a-seq f val]
(let [^objects arr (.array a-seq)]
(loop [i (.index a-seq)
val val]
(if (< i (alength arr))
(recur (inc i) (f val (aget arr i)))
val))))
java.lang.Object
(internal-reduce
[s f val]
(loop [cls (class s)
s s
f f
val val]
(if-let [s (seq s)]
;; roll over to faster implementation if underlying seq changes type
(if (identical? (class s) cls)
(recur cls (next s) f (f val (first s)))
(internal-reduce s f val))
val))))
(def arr-impl
'(internal-reduce
[a-seq f val]
(let [arr (.array a-seq)]
(loop [i (.index a-seq)
val val]
(if (< i (alength arr))
(recur (inc i) (f val (aget arr i)))
val)))))
(defn- emit-array-impls*
[syms]
(apply
concat
(map
(fn [s]
[(symbol (str "clojure.lang.ArraySeq$ArraySeq_" s))
arr-impl])
syms)))
(defmacro emit-array-impls
[& syms]
`(extend-protocol InternalReduce
~@(emit-array-impls* syms)))
(emit-array-impls int long float double byte char boolean)
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/core_deftype.clj 0000664 0000000 0000000 00000074330 11575623476 0023532 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(in-ns 'clojure.core)
;;;;;;;;;;;;;;;;;;;;;;;;;;;; definterface ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn namespace-munge
"Convert a Clojure namespace name to a legal Java package name."
{:added "1.2"}
[ns]
(.replace (str ns) \- \_))
;for now, built on gen-interface
(defmacro definterface
[name & sigs]
(let [tag (fn [x] (or (:tag (meta x)) Object))
psig (fn [[name [& args]]]
(vector name (vec (map tag args)) (tag name) (map meta args)))
cname (with-meta (symbol (str (namespace-munge *ns*) "." name)) (meta name))]
`(let []
(gen-interface :name ~cname :methods ~(vec (map psig sigs)))
(import ~cname))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;; reify/deftype ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- parse-opts [s]
(loop [opts {} [k v & rs :as s] s]
(if (keyword? k)
(recur (assoc opts k v) rs)
[opts s])))
(defn- parse-impls [specs]
(loop [ret {} s specs]
(if (seq s)
(recur (assoc ret (first s) (take-while seq? (next s)))
(drop-while seq? (next s)))
ret)))
(defn- parse-opts+specs [opts+specs]
(let [[opts specs] (parse-opts opts+specs)
impls (parse-impls specs)
interfaces (-> (map #(if (var? (resolve %))
(:on (deref (resolve %)))
%)
(keys impls))
set
(disj 'Object 'java.lang.Object)
vec)
methods (map (fn [[name params & body]]
(cons name (maybe-destructured params body)))
(apply concat (vals impls)))]
(when-let [bad-opts (seq (remove #{:no-print} (keys opts)))]
(throw (IllegalArgumentException. (apply print-str "Unsupported option(s) -" bad-opts))))
[interfaces methods opts]))
(defmacro reify
"reify is a macro with the following structure:
(reify options* specs*)
Currently there are no options.
Each spec consists of the protocol or interface name followed by zero
or more method bodies:
protocol-or-interface-or-Object
(methodName [args+] body)*
Methods should be supplied for all methods of the desired
protocol(s) and interface(s). You can also define overrides for
methods of Object. Note that the first parameter must be supplied to
correspond to the target object ('this' in Java parlance). Thus
methods for interfaces will take one more argument than do the
interface declarations. Note also that recur calls to the method
head should *not* pass the target object, it will be supplied
automatically and can not be substituted.
The return type can be indicated by a type hint on the method name,
and arg types can be indicated by a type hint on arg names. If you
leave out all hints, reify will try to match on same name/arity
method in the protocol(s)/interface(s) - this is preferred. If you
supply any hints at all, no inference is done, so all hints (or
default of Object) must be correct, for both arguments and return
type. If a method is overloaded in a protocol/interface, multiple
independent method definitions must be supplied. If overloaded with
same arity in an interface you must specify complete hints to
disambiguate - a missing hint implies Object.
recur works to method heads The method bodies of reify are lexical
closures, and can refer to the surrounding local scope:
(str (let [f \"foo\"]
(reify Object
(toString [this] f))))
== \"foo\"
(seq (let [f \"foo\"]
(reify clojure.lang.Seqable
(seq [this] (seq f)))))
== (\\f \\o \\o))"
{:added "1.2"}
[& opts+specs]
(let [[interfaces methods] (parse-opts+specs opts+specs)]
(with-meta `(reify* ~interfaces ~@methods) (meta &form))))
(defn hash-combine [x y]
(clojure.lang.Util/hashCombine x (clojure.lang.Util/hash y)))
(defn munge [s]
((if (symbol? s) symbol str) (clojure.lang.Compiler/munge (str s))))
(defn- imap-cons
[^IPersistentMap this o]
(cond
(instance? java.util.Map$Entry o)
(let [^java.util.Map$Entry pair o]
(.assoc this (.getKey pair) (.getValue pair)))
(instance? clojure.lang.IPersistentVector o)
(let [^clojure.lang.IPersistentVector vec o]
(.assoc this (.nth vec 0) (.nth vec 1)))
:else (loop [this this
o o]
(if (seq o)
(let [^java.util.Map$Entry pair (first o)]
(recur (.assoc this (.getKey pair) (.getValue pair)) (rest o)))
this))))
(defn- emit-defrecord
"Do not use this directly - use defrecord"
{:added "1.2"}
[tagname name fields interfaces methods]
(let [tag (keyword (str *ns*) (str tagname))
classname (with-meta (symbol (str (namespace-munge *ns*) "." name)) (meta name))
interfaces (vec interfaces)
interface-set (set (map resolve interfaces))
methodname-set (set (map first methods))
hinted-fields fields
fields (vec (map #(with-meta % nil) fields))
base-fields fields
fields (conj fields '__meta '__extmap)]
(when (some #{:volatile-mutable :unsynchronized-mutable} (mapcat (comp keys meta) hinted-fields))
(throw (IllegalArgumentException. ":volatile-mutable or :unsynchronized-mutable not supported for record fields")))
(let [gs (gensym)]
(letfn
[(eqhash [[i m]]
[i
(conj m
`(hashCode [this#] (clojure.lang.APersistentMap/mapHash this#))
`(equals [this# ~gs] (clojure.lang.APersistentMap/mapEquals this# ~gs)))])
(iobj [[i m]]
[(conj i 'clojure.lang.IObj)
(conj m `(meta [this#] ~'__meta)
`(withMeta [this# ~gs] (new ~tagname ~@(replace {'__meta gs} fields))))])
(ilookup [[i m]]
[(conj i 'clojure.lang.ILookup 'clojure.lang.IKeywordLookup)
(conj m `(valAt [this# k#] (.valAt this# k# nil))
`(valAt [this# k# else#]
(case k# ~@(mapcat (fn [fld] [(keyword fld) fld])
base-fields)
(get ~'__extmap k# else#)))
`(getLookupThunk [this# k#]
(let [~'gclass (class this#)]
(case k#
~@(let [hinted-target (with-meta 'gtarget {:tag tagname})]
(mapcat
(fn [fld]
[(keyword fld)
`(reify clojure.lang.ILookupThunk
(get [~'thunk ~'gtarget]
(if (identical? (class ~'gtarget) ~'gclass)
(. ~hinted-target ~(keyword fld))
~'thunk)))])
base-fields))
nil))))])
(imap [[i m]]
[(conj i 'clojure.lang.IPersistentMap)
(conj m
`(count [this#] (+ ~(count base-fields) (count ~'__extmap)))
`(empty [this#] (throw (UnsupportedOperationException. (str "Can't create empty: " ~(str classname)))))
`(cons [this# e#] ((var imap-cons) this# e#))
`(equiv [this# ~gs]
(boolean
(or (identical? this# ~gs)
(when (identical? (class this#) (class ~gs))
(let [~gs ~(with-meta gs {:tag tagname})]
(and ~@(map (fn [fld] `(= ~fld (. ~gs ~fld))) base-fields)
(= ~'__extmap (. ~gs ~'__extmap))))))))
`(containsKey [this# k#] (not (identical? this# (.valAt this# k# this#))))
`(entryAt [this# k#] (let [v# (.valAt this# k# this#)]
(when-not (identical? this# v#)
(clojure.lang.MapEntry. k# v#))))
`(seq [this#] (seq (concat [~@(map #(list `new `clojure.lang.MapEntry (keyword %) %) base-fields)]
~'__extmap)))
`(assoc [this# k# ~gs]
(condp identical? k#
~@(mapcat (fn [fld]
[(keyword fld) (list* `new tagname (replace {fld gs} fields))])
base-fields)
(new ~tagname ~@(remove #{'__extmap} fields) (assoc ~'__extmap k# ~gs))))
`(without [this# k#] (if (contains? #{~@(map keyword base-fields)} k#)
(dissoc (with-meta (into {} this#) ~'__meta) k#)
(new ~tagname ~@(remove #{'__extmap} fields)
(not-empty (dissoc ~'__extmap k#))))))])
(ijavamap [[i m]]
[(conj i 'java.util.Map 'java.io.Serializable)
(conj m
`(size [this#] (.count this#))
`(isEmpty [this#] (= 0 (.count this#)))
`(containsValue [this# v#] (boolean (some #{v#} (vals this#))))
`(get [this# k#] (.valAt this# k#))
`(put [this# k# v#] (throw (UnsupportedOperationException.)))
`(remove [this# k#] (throw (UnsupportedOperationException.)))
`(putAll [this# m#] (throw (UnsupportedOperationException.)))
`(clear [this#] (throw (UnsupportedOperationException.)))
`(keySet [this#] (set (keys this#)))
`(values [this#] (vals this#))
`(entrySet [this#] (set this#)))])
]
(let [[i m] (-> [interfaces methods] eqhash iobj ilookup imap ijavamap)]
`(deftype* ~tagname ~classname ~(conj hinted-fields '__meta '__extmap)
:implements ~(vec i)
~@m))))))
(defmacro defrecord
"Alpha - subject to change
(defrecord name [fields*] options* specs*)
Currently there are no options.
Each spec consists of a protocol or interface name followed by zero
or more method bodies:
protocol-or-interface-or-Object
(methodName [args*] body)*
Dynamically generates compiled bytecode for class with the given
name, in a package with the same name as the current namespace, the
given fields, and, optionally, methods for protocols and/or
interfaces.
The class will have the (immutable) fields named by
fields, which can have type hints. Protocols/interfaces and methods
are optional. The only methods that can be supplied are those
declared in the protocols/interfaces. Note that method bodies are
not closures, the local environment includes only the named fields,
and those fields can be accessed directy.
Method definitions take the form:
(methodname [args*] body)
The argument and return types can be hinted on the arg and
methodname symbols. If not supplied, they will be inferred, so type
hints should be reserved for disambiguation.
Methods should be supplied for all methods of the desired
protocol(s) and interface(s). You can also define overrides for
methods of Object. Note that a parameter must be supplied to
correspond to the target object ('this' in Java parlance). Thus
methods for interfaces will take one more argument than do the
interface declarations. Note also that recur calls to the method
head should *not* pass the target object, it will be supplied
automatically and can not be substituted.
In the method bodies, the (unqualified) name can be used to name the
class (for calls to new, instance? etc).
The class will have implementations of several (clojure.lang)
interfaces generated automatically: IObj (metadata support) and
IPersistentMap, and all of their superinterfaces.
In addition, defrecord will define type-and-value-based equality and
hashCode.
When AOT compiling, generates compiled bytecode for a class with the
given name (a symbol), prepends the current ns as the package, and
writes the .class file to the *compile-path* directory.
Two constructors will be defined, one taking the designated fields
followed by a metadata map (nil for none) and an extension field
map (nil for none), and one taking only the fields (using nil for
meta and extension fields)."
{:added "1.2"}
[name [& fields] & opts+specs]
(let [gname name
[interfaces methods opts] (parse-opts+specs opts+specs)
classname (symbol (str (namespace-munge *ns*) "." gname))
tag (keyword (str *ns*) (str name))
hinted-fields fields
fields (vec (map #(with-meta % nil) fields))]
`(let []
~(emit-defrecord name gname (vec hinted-fields) (vec interfaces) methods)
(defmethod print-method ~classname [o# w#]
((var print-defrecord) o# w#))
(import ~classname)
#_(defn ~name
([~@fields] (new ~classname ~@fields nil nil))
([~@fields meta# extmap#] (new ~classname ~@fields meta# extmap#))))))
(defn- print-defrecord [o ^Writer w]
(print-meta o w)
(.write w "#:")
(.write w (.getName (class o)))
(print-map
o
pr-on w))
(defn- emit-deftype*
"Do not use this directly - use deftype"
[tagname name fields interfaces methods]
(let [classname (with-meta (symbol (str (namespace-munge *ns*) "." name)) (meta name))]
`(deftype* ~tagname ~classname ~fields
:implements ~interfaces
~@methods)))
(defmacro deftype
"Alpha - subject to change
(deftype name [fields*] options* specs*)
Currently there are no options.
Each spec consists of a protocol or interface name followed by zero
or more method bodies:
protocol-or-interface-or-Object
(methodName [args*] body)*
Dynamically generates compiled bytecode for class with the given
name, in a package with the same name as the current namespace, the
given fields, and, optionally, methods for protocols and/or
interfaces.
The class will have the (by default, immutable) fields named by
fields, which can have type hints. Protocols/interfaces and methods
are optional. The only methods that can be supplied are those
declared in the protocols/interfaces. Note that method bodies are
not closures, the local environment includes only the named fields,
and those fields can be accessed directy. Fields can be qualified
with the metadata :volatile-mutable true or :unsynchronized-mutable
true, at which point (set! afield aval) will be supported in method
bodies. Note well that mutable fields are extremely difficult to use
correctly, and are present only to facilitate the building of higher
level constructs, such as Clojure's reference types, in Clojure
itself. They are for experts only - if the semantics and
implications of :volatile-mutable or :unsynchronized-mutable are not
immediately apparent to you, you should not be using them.
Method definitions take the form:
(methodname [args*] body)
The argument and return types can be hinted on the arg and
methodname symbols. If not supplied, they will be inferred, so type
hints should be reserved for disambiguation.
Methods should be supplied for all methods of the desired
protocol(s) and interface(s). You can also define overrides for
methods of Object. Note that a parameter must be supplied to
correspond to the target object ('this' in Java parlance). Thus
methods for interfaces will take one more argument than do the
interface declarations. Note also that recur calls to the method
head should *not* pass the target object, it will be supplied
automatically and can not be substituted.
In the method bodies, the (unqualified) name can be used to name the
class (for calls to new, instance? etc).
When AOT compiling, generates compiled bytecode for a class with the
given name (a symbol), prepends the current ns as the package, and
writes the .class file to the *compile-path* directory.
One constructors will be defined, taking the designated fields."
{:added "1.2"}
[name [& fields] & opts+specs]
(let [gname name
[interfaces methods opts] (parse-opts+specs opts+specs)
classname (symbol (str (namespace-munge *ns*) "." gname))
tag (keyword (str *ns*) (str name))
hinted-fields fields
fields (vec (map #(with-meta % nil) fields))]
`(let []
~(emit-deftype* name gname (vec hinted-fields) (vec interfaces) methods)
(import ~classname))))
;;;;;;;;;;;;;;;;;;;;;;; protocols ;;;;;;;;;;;;;;;;;;;;;;;;
(defn- expand-method-impl-cache [^clojure.lang.MethodImplCache cache c f]
(let [cs (into {} (remove (fn [[c e]] (nil? e)) (map vec (partition 2 (.table cache)))))
cs (assoc cs c (clojure.lang.MethodImplCache$Entry. c f))
[shift mask] (min-hash (keys cs))
table (make-array Object (* 2 (inc mask)))
table (reduce (fn [^objects t [c e]]
(let [i (* 2 (int (shift-mask shift mask (hash c))))]
(aset t i c)
(aset t (inc i) e)
t))
table cs)]
(clojure.lang.MethodImplCache. (.protocol cache) (.methodk cache) shift mask table)))
(defn- super-chain [^Class c]
(when c
(cons c (super-chain (.getSuperclass c)))))
(defn- pref
([] nil)
([a] a)
([^Class a ^Class b]
(if (.isAssignableFrom a b) b a)))
(defn find-protocol-impl [protocol x]
(if (instance? (:on-interface protocol) x)
x
(let [c (class x)
impl #(get (:impls protocol) %)]
(or (impl c)
(and c (or (first (remove nil? (map impl (butlast (super-chain c)))))
(when-let [t (reduce pref (filter impl (disj (supers c) Object)))]
(impl t))
(impl Object)))))))
(defn find-protocol-method [protocol methodk x]
(get (find-protocol-impl protocol x) methodk))
(defn- protocol?
[maybe-p]
(boolean (:on-interface maybe-p)))
(defn- implements? [protocol atype]
(and atype (.isAssignableFrom ^Class (:on-interface protocol) atype)))
(defn extends?
"Returns true if atype extends protocol"
{:added "1.2"}
[protocol atype]
(boolean (or (implements? protocol atype)
(get (:impls protocol) atype))))
(defn extenders
"Returns a collection of the types explicitly extending protocol"
{:added "1.2"}
[protocol]
(keys (:impls protocol)))
(defn satisfies?
"Returns true if x satisfies the protocol"
{:added "1.2"}
[protocol x]
(boolean (find-protocol-impl protocol x)))
(defn -cache-protocol-fn [^clojure.lang.AFunction pf x ^Class c ^clojure.lang.IFn interf]
(let [cache (.__methodImplCache pf)
f (if (.isInstance c x)
interf
(find-protocol-method (.protocol cache) (.methodk cache) x))]
(when-not f
(throw (IllegalArgumentException. (str "No implementation of method: " (.methodk cache)
" of protocol: " (:var (.protocol cache))
" found for class: " (if (nil? x) "nil" (.getName (class x)))))))
(set! (.__methodImplCache pf) (expand-method-impl-cache cache (class x) f))
f))
(defn- emit-method-builder [on-interface method on-method arglists]
(let [methodk (keyword method)
gthis (with-meta (gensym) {:tag 'clojure.lang.AFunction})
ginterf (gensym)]
`(fn [cache#]
(let [~ginterf
(fn
~@(map
(fn [args]
(let [gargs (map #(gensym (str "gf__" % "__")) args)
target (first gargs)]
`([~@gargs]
(. ~(with-meta target {:tag on-interface}) ~(or on-method method) ~@(rest gargs)))))
arglists))
^clojure.lang.AFunction f#
(fn ~gthis
~@(map
(fn [args]
(let [gargs (map #(gensym (str "gf__" % "__")) args)
target (first gargs)]
`([~@gargs]
(let [cache# (.__methodImplCache ~gthis)
f# (.fnFor cache# (clojure.lang.Util/classOf ~target))]
(if f#
(f# ~@gargs)
((-cache-protocol-fn ~gthis ~target ~on-interface ~ginterf) ~@gargs))))))
arglists))]
(set! (.__methodImplCache f#) cache#)
f#))))
(defn -reset-methods [protocol]
(doseq [[^clojure.lang.Var v build] (:method-builders protocol)]
(let [cache (clojure.lang.MethodImplCache. protocol (keyword (.sym v)))]
(.bindRoot v (build cache)))))
(defn- assert-same-protocol [protocol-var method-syms]
(doseq [m method-syms]
(let [v (resolve m)
p (:protocol (meta v))]
(when (and v (bound? v) (not= protocol-var p))
(binding [*out* *err*]
(println "Warning: protocol" protocol-var "is overwriting"
(if p
(str "method " (.sym v) " of protocol " (.sym p))
(str "function " (.sym v)))))))))
(defn- emit-protocol [name opts+sigs]
(let [iname (symbol (str (munge (namespace-munge *ns*)) "." (munge name)))
[opts sigs]
(loop [opts {:on (list 'quote iname) :on-interface iname} sigs opts+sigs]
(condp #(%1 %2) (first sigs)
string? (recur (assoc opts :doc (first sigs)) (next sigs))
keyword? (recur (assoc opts (first sigs) (second sigs)) (nnext sigs))
[opts sigs]))
sigs (reduce (fn [m s]
(let [name-meta (meta (first s))
mname (with-meta (first s) nil)
[arglists doc]
(loop [as [] rs (rest s)]
(if (vector? (first rs))
(recur (conj as (first rs)) (next rs))
[(seq as) (first rs)]))]
(when (some #{0} (map count arglists))
(throw (IllegalArgumentException. (str "Protocol fn: " mname " must take at least one arg"))))
(assoc m (keyword mname)
(merge name-meta
{:name (vary-meta mname assoc :doc doc :arglists arglists)
:arglists arglists
:doc doc}))))
{} sigs)
meths (mapcat (fn [sig]
(let [m (munge (:name sig))]
(map #(vector m (vec (repeat (dec (count %))'Object)) 'Object)
(:arglists sig))))
(vals sigs))]
`(do
(defonce ~name {})
(gen-interface :name ~iname :methods ~meths)
(alter-meta! (var ~name) assoc :doc ~(:doc opts))
(#'assert-same-protocol (var ~name) '~(map :name (vals sigs)))
(alter-var-root (var ~name) merge
(assoc ~opts
:sigs '~sigs
:var (var ~name)
:method-map
~(and (:on opts)
(apply hash-map
(mapcat
(fn [s]
[(keyword (:name s)) (keyword (or (:on s) (:name s)))])
(vals sigs))))
:method-builders
~(apply hash-map
(mapcat
(fn [s]
[`(intern *ns* (with-meta '~(:name s) (merge '~s {:protocol (var ~name)})))
(emit-method-builder (:on-interface opts) (:name s) (:on s) (:arglists s))])
(vals sigs)))))
(-reset-methods ~name)
'~name)))
(defmacro defprotocol
"A protocol is a named set of named methods and their signatures:
(defprotocol AProtocolName
;optional doc string
\"A doc string for AProtocol abstraction\"
;method signatures
(bar [this a b] \"bar docs\")
(baz [this a] [this a b] [this a b c] \"baz docs\"))
No implementations are provided. Docs can be specified for the
protocol overall and for each method. The above yields a set of
polymorphic functions and a protocol object. All are
namespace-qualified by the ns enclosing the definition The resulting
functions dispatch on the type of their first argument, which is
required and corresponds to the implicit target object ('this' in
Java parlance). defprotocol is dynamic, has no special compile-time
effect, and defines no new types or classes. Implementations of
the protocol methods can be provided using extend.
defprotocol will automatically generate a corresponding interface,
with the same name as the protocol, i.e. given a protocol:
my.ns/Protocol, an interface: my.ns.Protocol. The interface will
have methods corresponding to the protocol functions, and the
protocol will automatically work with instances of the interface.
Note that you should not use this interface with deftype or
reify, as they support the protocol directly:
(defprotocol P
(foo [this])
(bar-me [this] [this y]))
(deftype Foo [a b c]
P
(foo [this] a)
(bar-me [this] b)
(bar-me [this y] (+ c y)))
(bar-me (Foo. 1 2 3) 42)
=> 45
(foo
(let [x 42]
(reify P
(foo [this] 17)
(bar-me [this] x)
(bar-me [this y] x))))
=> 17"
{:added "1.2"}
[name & opts+sigs]
(emit-protocol name opts+sigs))
(defn extend
"Implementations of protocol methods can be provided using the extend construct:
(extend AType
AProtocol
{:foo an-existing-fn
:bar (fn [a b] ...)
:baz (fn ([a]...) ([a b] ...)...)}
BProtocol
{...}
...)
extend takes a type/class (or interface, see below), and one or more
protocol + method map pairs. It will extend the polymorphism of the
protocol's methods to call the supplied methods when an AType is
provided as the first argument.
Method maps are maps of the keyword-ized method names to ordinary
fns. This facilitates easy reuse of existing fns and fn maps, for
code reuse/mixins without derivation or composition. You can extend
an interface to a protocol. This is primarily to facilitate interop
with the host (e.g. Java) but opens the door to incidental multiple
inheritance of implementation since a class can inherit from more
than one interface, both of which extend the protocol. It is TBD how
to specify which impl to use. You can extend a protocol on nil.
If you are supplying the definitions explicitly (i.e. not reusing
exsting functions or mixin maps), you may find it more convenient to
use the extend-type or extend-protocol macros.
Note that multiple independent extend clauses can exist for the same
type, not all protocols need be defined in a single extend call.
See also:
extends?, satisfies?, extenders"
{:added "1.2"}
[atype & proto+mmaps]
(doseq [[proto mmap] (partition 2 proto+mmaps)]
(when-not (protocol? proto)
(throw (IllegalArgumentException.
(str proto " is not a protocol"))))
(when (implements? proto atype)
(throw (IllegalArgumentException.
(str atype " already directly implements " (:on-interface proto) " for protocol:"
(:var proto)))))
(-reset-methods (alter-var-root (:var proto) assoc-in [:impls atype] mmap))))
(defn- emit-impl [[p fs]]
[p (zipmap (map #(-> % first keyword) fs)
(map #(cons 'fn (drop 1 %)) fs))])
(defn- emit-hinted-impl [c [p fs]]
(let [hint (fn [specs]
(let [specs (if (vector? (first specs))
(list specs)
specs)]
(map (fn [[[target & args] & body]]
(cons (apply vector (vary-meta target assoc :tag c) args)
body))
specs)))]
[p (zipmap (map #(-> % first keyword) fs)
(map #(cons 'fn (hint (drop 1 %))) fs))]))
(defn- emit-extend-type [c specs]
(let [impls (parse-impls specs)]
`(extend ~c
~@(mapcat (partial emit-hinted-impl c) impls))))
(defmacro extend-type
"A macro that expands into an extend call. Useful when you are
supplying the definitions explicitly inline, extend-type
automatically creates the maps required by extend. Propagates the
class as a type hint on the first argument of all fns.
(extend-type MyType
Countable
(cnt [c] ...)
Foo
(bar [x y] ...)
(baz ([x] ...) ([x y & zs] ...)))
expands into:
(extend MyType
Countable
{:cnt (fn [c] ...)}
Foo
{:baz (fn ([x] ...) ([x y & zs] ...))
:bar (fn [x y] ...)})"
{:added "1.2"}
[t & specs]
(emit-extend-type t specs))
(defn- emit-extend-protocol [p specs]
(let [impls (parse-impls specs)]
`(do
~@(map (fn [[t fs]]
`(extend-type ~t ~p ~@fs))
impls))))
(defmacro extend-protocol
"Useful when you want to provide several implementations of the same
protocol all at once. Takes a single protocol and the implementation
of that protocol for one or more types. Expands into calls to
extend-type:
(extend-protocol Protocol
AType
(foo [x] ...)
(bar [x y] ...)
BType
(foo [x] ...)
(bar [x y] ...)
AClass
(foo [x] ...)
(bar [x y] ...)
nil
(foo [x] ...)
(bar [x y] ...))
expands into:
(do
(clojure.core/extend-type AType Protocol
(foo [x] ...)
(bar [x y] ...))
(clojure.core/extend-type BType Protocol
(foo [x] ...)
(bar [x y] ...))
(clojure.core/extend-type AClass Protocol
(foo [x] ...)
(bar [x y] ...))
(clojure.core/extend-type nil Protocol
(foo [x] ...)
(bar [x y] ...)))"
{:added "1.2"}
[p & specs]
(emit-extend-protocol p specs))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/core_print.clj 0000664 0000000 0000000 00000025031 11575623476 0023220 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(in-ns 'clojure.core)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; printing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(import '(java.io Writer))
(def
^{:doc "*print-length* controls how many items of each collection the
printer will print. If it is bound to logical false, there is no
limit. Otherwise, it must be bound to an integer indicating the maximum
number of items of each collection to print. If a collection contains
more items, the printer will print items up to the limit followed by
'...' to represent the remaining items. The root binding is nil
indicating no limit."
:added "1.0"}
*print-length* nil)
(def
^{:doc "*print-level* controls how many levels deep the printer will
print nested objects. If it is bound to logical false, there is no
limit. Otherwise, it must be bound to an integer indicating the maximum
level to print. Each argument to print is at level 0; if an argument is a
collection, its items are at level 1; and so on. If an object is a
collection and is at a level greater than or equal to the value bound to
*print-level*, the printer prints '#' to represent it. The root binding
is nil indicating no limit."
:added "1.0"}
*print-level* nil)
(defn- print-sequential [^String begin, print-one, ^String sep, ^String end, sequence, ^Writer w]
(binding [*print-level* (and (not *print-dup*) *print-level* (dec *print-level*))]
(if (and *print-level* (neg? *print-level*))
(.write w "#")
(do
(.write w begin)
(when-let [xs (seq sequence)]
(if (and (not *print-dup*) *print-length*)
(loop [[x & xs] xs
print-length *print-length*]
(if (zero? print-length)
(.write w "...")
(do
(print-one x w)
(when xs
(.write w sep)
(recur xs (dec print-length))))))
(loop [[x & xs] xs]
(print-one x w)
(when xs
(.write w sep)
(recur xs)))))
(.write w end)))))
(defn- print-meta [o, ^Writer w]
(when-let [m (meta o)]
(when (and (pos? (count m))
(or *print-dup*
(and *print-meta* *print-readably*)))
(.write w "^")
(if (and (= (count m) 1) (:tag m))
(pr-on (:tag m) w)
(pr-on m w))
(.write w " "))))
(defmethod print-method :default [o, ^Writer w]
(print-method (vary-meta o #(dissoc % :type)) w))
(defmethod print-method nil [o, ^Writer w]
(.write w "nil"))
(defmethod print-dup nil [o w] (print-method o w))
(defn print-ctor [o print-args ^Writer w]
(.write w "#=(")
(.write w (.getName ^Class (class o)))
(.write w ". ")
(print-args o w)
(.write w ")"))
(defmethod print-method Object [o, ^Writer w]
(.write w "#<")
(.write w (.getSimpleName (class o)))
(.write w " ")
(.write w (str o))
(.write w ">"))
(defmethod print-method clojure.lang.Keyword [o, ^Writer w]
(.write w (str o)))
(defmethod print-dup clojure.lang.Keyword [o w] (print-method o w))
(defmethod print-method Number [o, ^Writer w]
(.write w (str o)))
(defmethod print-dup Number [o, ^Writer w]
(print-ctor o
(fn [o w]
(print-dup (str o) w))
w))
(defmethod print-dup clojure.lang.Fn [o, ^Writer w]
(print-ctor o (fn [o w]) w))
(prefer-method print-dup clojure.lang.IPersistentCollection clojure.lang.Fn)
(prefer-method print-dup java.util.Map clojure.lang.Fn)
(prefer-method print-dup java.util.Collection clojure.lang.Fn)
(defmethod print-method Boolean [o, ^Writer w]
(.write w (str o)))
(defmethod print-dup Boolean [o w] (print-method o w))
(defn print-simple [o, ^Writer w]
(print-meta o w)
(.write w (str o)))
(defmethod print-method clojure.lang.Symbol [o, ^Writer w]
(print-simple o w))
(defmethod print-dup clojure.lang.Symbol [o w] (print-method o w))
(defmethod print-method clojure.lang.Var [o, ^Writer w]
(print-simple o w))
(defmethod print-dup clojure.lang.Var [^clojure.lang.Var o, ^Writer w]
(.write w (str "#=(var " (.name (.ns o)) "/" (.sym o) ")")))
(defmethod print-method clojure.lang.ISeq [o, ^Writer w]
(print-meta o w)
(print-sequential "(" pr-on " " ")" o w))
(defmethod print-dup clojure.lang.ISeq [o w] (print-method o w))
(defmethod print-dup clojure.lang.IPersistentList [o w] (print-method o w))
(prefer-method print-method clojure.lang.ISeq clojure.lang.IPersistentCollection)
(prefer-method print-dup clojure.lang.ISeq clojure.lang.IPersistentCollection)
(prefer-method print-method clojure.lang.ISeq java.util.Collection)
(prefer-method print-dup clojure.lang.ISeq java.util.Collection)
(defmethod print-dup java.util.Collection [o, ^Writer w]
(print-ctor o #(print-sequential "[" print-dup " " "]" %1 %2) w))
(defmethod print-dup clojure.lang.IPersistentCollection [o, ^Writer w]
(print-meta o w)
(.write w "#=(")
(.write w (.getName ^Class (class o)))
(.write w "/create ")
(print-sequential "[" print-dup " " "]" o w)
(.write w ")"))
(prefer-method print-dup clojure.lang.IPersistentCollection java.util.Collection)
(def ^{:tag String
:doc "Returns escape string for char or nil if none"
:added "1.0"}
char-escape-string
{\newline "\\n"
\tab "\\t"
\return "\\r"
\" "\\\""
\\ "\\\\"
\formfeed "\\f"
\backspace "\\b"})
(defmethod print-method String [^String s, ^Writer w]
(if (or *print-dup* *print-readably*)
(do (.append w \")
(dotimes [n (count s)]
(let [c (.charAt s n)
e (char-escape-string c)]
(if e (.write w e) (.append w c))))
(.append w \"))
(.write w s))
nil)
(defmethod print-dup String [s w] (print-method s w))
(defmethod print-method clojure.lang.IPersistentVector [v, ^Writer w]
(print-meta v w)
(print-sequential "[" pr-on " " "]" v w))
(defn- print-map [m print-one w]
(print-sequential
"{"
(fn [e ^Writer w]
(do (print-one (key e) w) (.append w \space) (print-one (val e) w)))
", "
"}"
(seq m) w))
(defmethod print-method clojure.lang.IPersistentMap [m, ^Writer w]
(print-meta m w)
(print-map m pr-on w))
(defmethod print-dup java.util.Map [m, ^Writer w]
(print-ctor m #(print-map (seq %1) print-dup %2) w))
(defmethod print-dup clojure.lang.IPersistentMap [m, ^Writer w]
(print-meta m w)
(.write w "#=(")
(.write w (.getName (class m)))
(.write w "/create ")
(print-map m print-dup w)
(.write w ")"))
(prefer-method print-dup clojure.lang.IPersistentCollection java.util.Map)
(defmethod print-method clojure.lang.IPersistentSet [s, ^Writer w]
(print-meta s w)
(print-sequential "#{" pr-on " " "}" (seq s) w))
(def ^{:tag String
:doc "Returns name string for char or nil if none"
:added "1.0"}
char-name-string
{\newline "newline"
\tab "tab"
\space "space"
\backspace "backspace"
\formfeed "formfeed"
\return "return"})
(defmethod print-method java.lang.Character [^Character c, ^Writer w]
(if (or *print-dup* *print-readably*)
(do (.append w \\)
(let [n (char-name-string c)]
(if n (.write w n) (.append w c))))
(.append w c))
nil)
(defmethod print-dup java.lang.Character [c w] (print-method c w))
(defmethod print-dup java.lang.Integer [o w] (print-method o w))
(defmethod print-dup java.lang.Double [o w] (print-method o w))
(defmethod print-dup clojure.lang.Ratio [o w] (print-method o w))
(defmethod print-dup java.math.BigDecimal [o w] (print-method o w))
(defmethod print-dup clojure.lang.PersistentHashMap [o w] (print-method o w))
(defmethod print-dup clojure.lang.PersistentHashSet [o w] (print-method o w))
(defmethod print-dup clojure.lang.PersistentVector [o w] (print-method o w))
(defmethod print-dup clojure.lang.LazilyPersistentVector [o w] (print-method o w))
(def primitives-classnames
{Float/TYPE "Float/TYPE"
Integer/TYPE "Integer/TYPE"
Long/TYPE "Long/TYPE"
Boolean/TYPE "Boolean/TYPE"
Character/TYPE "Character/TYPE"
Double/TYPE "Double/TYPE"
Byte/TYPE "Byte/TYPE"
Short/TYPE "Short/TYPE"})
(defmethod print-method Class [^Class c, ^Writer w]
(.write w (.getName c)))
(defmethod print-dup Class [^Class c, ^Writer w]
(cond
(.isPrimitive c) (do
(.write w "#=(identity ")
(.write w ^String (primitives-classnames c))
(.write w ")"))
(.isArray c) (do
(.write w "#=(java.lang.Class/forName \"")
(.write w (.getName c))
(.write w "\")"))
:else (do
(.write w "#=")
(.write w (.getName c)))))
(defmethod print-method java.math.BigDecimal [b, ^Writer w]
(.write w (str b))
(.write w "M"))
(defmethod print-method java.util.regex.Pattern [p ^Writer w]
(.write w "#\"")
(loop [[^Character c & r :as s] (seq (.pattern ^java.util.regex.Pattern p))
qmode false]
(when s
(cond
(= c \\) (let [[^Character c2 & r2] r]
(.append w \\)
(.append w c2)
(if qmode
(recur r2 (not= c2 \E))
(recur r2 (= c2 \Q))))
(= c \") (do
(if qmode
(.write w "\\E\\\"\\Q")
(.write w "\\\""))
(recur r qmode))
:else (do
(.append w c)
(recur r qmode)))))
(.append w \"))
(defmethod print-dup java.util.regex.Pattern [p ^Writer w] (print-method p w))
(defmethod print-dup clojure.lang.Namespace [^clojure.lang.Namespace n ^Writer w]
(.write w "#=(find-ns ")
(print-dup (.name n) w)
(.write w ")"))
(defmethod print-method clojure.lang.IDeref [o ^Writer w]
(print-sequential (format "#<%s@%x%s: "
(.getSimpleName (class o))
(System/identityHashCode o)
(if (and (instance? clojure.lang.Agent o)
(agent-error o))
" FAILED"
""))
pr-on, "", ">", (list (if (and (future? o) (not (future-done? o))) :pending @o)), w))
(def ^{:private true} print-initialized true)
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/core_proxy.clj 0000664 0000000 0000000 00000044672 11575623476 0023261 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(in-ns 'clojure.core)
;;;;;;;;;;;;;;;;;;;;;;;;;;;; proxy ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(import
'(clojure.asm ClassWriter ClassVisitor Opcodes Type)
'(java.lang.reflect Modifier Constructor)
'(clojure.asm.commons Method GeneratorAdapter)
'(clojure.lang IProxy Reflector DynamicClassLoader IPersistentMap PersistentHashMap RT))
(defn method-sig [^java.lang.reflect.Method meth]
[(. meth (getName)) (seq (. meth (getParameterTypes))) (. meth getReturnType)])
(defn- most-specific [rtypes]
(or (some (fn [t] (when (every? #(isa? t %) rtypes) t)) rtypes)
(throw (Exception. "Incompatible return types"))))
(defn- group-by-sig [coll]
"takes a collection of [msig meth] and returns a seq of maps from return-types to meths."
(vals (reduce (fn [m [msig meth]]
(let [rtype (peek msig)
argsig (pop msig)]
(assoc m argsig (assoc (m argsig {}) rtype meth))))
{} coll)))
(defn proxy-name
{:tag String}
[^Class super interfaces]
(let [inames (into (sorted-set) (map #(.getName ^Class %) interfaces))]
(apply str (.replace (str *ns*) \- \_) ".proxy"
(interleave (repeat "$")
(concat
[(.getName super)]
(map #(subs % (inc (.lastIndexOf ^String % "."))) inames)
[(Integer/toHexString (hash inames))])))))
(defn- generate-proxy [^Class super interfaces]
(let [cv (new ClassWriter (. ClassWriter COMPUTE_MAXS))
cname (.replace (proxy-name super interfaces) \. \/) ;(str "clojure/lang/" (gensym "Proxy__"))
ctype (. Type (getObjectType cname))
iname (fn [^Class c] (.. Type (getType c) (getInternalName)))
fmap "__clojureFnMap"
totype (fn [^Class c] (. Type (getType c)))
to-types (fn [cs] (if (pos? (count cs))
(into-array (map totype cs))
(make-array Type 0)))
super-type ^Type (totype super)
imap-type ^Type (totype IPersistentMap)
ifn-type (totype clojure.lang.IFn)
obj-type (totype Object)
sym-type (totype clojure.lang.Symbol)
rt-type (totype clojure.lang.RT)
ex-type (totype java.lang.UnsupportedOperationException)
gen-bridge
(fn [^java.lang.reflect.Method meth ^java.lang.reflect.Method dest]
(let [pclasses (. meth (getParameterTypes))
ptypes (to-types pclasses)
rtype ^Type (totype (. meth (getReturnType)))
m (new Method (. meth (getName)) rtype ptypes)
dtype (totype (.getDeclaringClass dest))
dm (new Method (. dest (getName)) (totype (. dest (getReturnType))) (to-types (. dest (getParameterTypes))))
gen (new GeneratorAdapter (bit-or (. Opcodes ACC_PUBLIC) (. Opcodes ACC_BRIDGE)) m nil nil cv)]
(. gen (visitCode))
(. gen (loadThis))
(dotimes [i (count ptypes)]
(. gen (loadArg i)))
(if (-> dest .getDeclaringClass .isInterface)
(. gen (invokeInterface dtype dm))
(. gen (invokeVirtual dtype dm)))
(. gen (returnValue))
(. gen (endMethod))))
gen-method
(fn [^java.lang.reflect.Method meth else-gen]
(let [pclasses (. meth (getParameterTypes))
ptypes (to-types pclasses)
rtype ^Type (totype (. meth (getReturnType)))
m (new Method (. meth (getName)) rtype ptypes)
gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)
else-label (. gen (newLabel))
end-label (. gen (newLabel))
decl-type (. Type (getType (. meth (getDeclaringClass))))]
(. gen (visitCode))
(if (> (count pclasses) 18)
(else-gen gen m)
(do
(. gen (loadThis))
(. gen (getField ctype fmap imap-type))
(. gen (push (. meth (getName))))
;lookup fn in map
(. gen (invokeStatic rt-type (. Method (getMethod "Object get(Object, Object)"))))
(. gen (dup))
(. gen (ifNull else-label))
;if found
(.checkCast gen ifn-type)
(. gen (loadThis))
;box args
(dotimes [i (count ptypes)]
(. gen (loadArg i))
(. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i))))
;call fn
(. gen (invokeInterface ifn-type (new Method "invoke" obj-type
(into-array (cons obj-type
(replicate (count ptypes) obj-type))))))
;unbox return
(. gen (unbox rtype))
(when (= (. rtype (getSort)) (. Type VOID))
(. gen (pop)))
(. gen (goTo end-label))
;else call supplied alternative generator
(. gen (mark else-label))
(. gen (pop))
(else-gen gen m)
(. gen (mark end-label))))
(. gen (returnValue))
(. gen (endMethod))))]
;start class definition
(. cv (visit (. Opcodes V1_5) (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_SUPER))
cname nil (iname super)
(into-array (map iname (cons IProxy interfaces)))))
;add field for fn mappings
(. cv (visitField (+ (. Opcodes ACC_PRIVATE) (. Opcodes ACC_VOLATILE))
fmap (. imap-type (getDescriptor)) nil nil))
;add ctors matching/calling super's
(doseq [^Constructor ctor (. super (getDeclaredConstructors))]
(when-not (. Modifier (isPrivate (. ctor (getModifiers))))
(let [ptypes (to-types (. ctor (getParameterTypes)))
m (new Method "" (. Type VOID_TYPE) ptypes)
gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)]
(. gen (visitCode))
;call super ctor
(. gen (loadThis))
(. gen (dup))
(. gen (loadArgs))
(. gen (invokeConstructor super-type m))
(. gen (returnValue))
(. gen (endMethod)))))
;add IProxy methods
(let [m (. Method (getMethod "void __initClojureFnMappings(clojure.lang.IPersistentMap)"))
gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)]
(. gen (visitCode))
(. gen (loadThis))
(. gen (loadArgs))
(. gen (putField ctype fmap imap-type))
(. gen (returnValue))
(. gen (endMethod)))
(let [m (. Method (getMethod "void __updateClojureFnMappings(clojure.lang.IPersistentMap)"))
gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)]
(. gen (visitCode))
(. gen (loadThis))
(. gen (dup))
(. gen (getField ctype fmap imap-type))
(.checkCast gen (totype clojure.lang.IPersistentCollection))
(. gen (loadArgs))
(. gen (invokeInterface (totype clojure.lang.IPersistentCollection)
(. Method (getMethod "clojure.lang.IPersistentCollection cons(Object)"))))
(. gen (checkCast imap-type))
(. gen (putField ctype fmap imap-type))
(. gen (returnValue))
(. gen (endMethod)))
(let [m (. Method (getMethod "clojure.lang.IPersistentMap __getClojureFnMappings()"))
gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)]
(. gen (visitCode))
(. gen (loadThis))
(. gen (getField ctype fmap imap-type))
(. gen (returnValue))
(. gen (endMethod)))
;calc set of supers' non-private instance methods
(let [[mm considered]
(loop [mm {} considered #{} c super]
(if c
(let [[mm considered]
(loop [mm mm
considered considered
meths (concat
(seq (. c (getDeclaredMethods)))
(seq (. c (getMethods))))]
(if (seq meths)
(let [^java.lang.reflect.Method meth (first meths)
mods (. meth (getModifiers))
mk (method-sig meth)]
(if (or (considered mk)
(not (or (Modifier/isPublic mods) (Modifier/isProtected mods)))
;(. Modifier (isPrivate mods))
(. Modifier (isStatic mods))
(. Modifier (isFinal mods))
(= "finalize" (.getName meth)))
(recur mm (conj considered mk) (next meths))
(recur (assoc mm mk meth) (conj considered mk) (next meths))))
[mm considered]))]
(recur mm considered (. c (getSuperclass))))
[mm considered]))
ifaces-meths (into {}
(for [^Class iface interfaces meth (. iface (getMethods))
:let [msig (method-sig meth)] :when (not (considered msig))]
{msig meth}))
mgroups (group-by-sig (concat mm ifaces-meths))
rtypes (map #(most-specific (keys %)) mgroups)
mb (map #(vector (%1 %2) (vals (dissoc %1 %2))) mgroups rtypes)
bridge? (reduce into #{} (map second mb))
ifaces-meths (remove bridge? (vals ifaces-meths))
mm (remove bridge? (vals mm))]
;add methods matching supers', if no mapping -> call super
(doseq [[^java.lang.reflect.Method dest bridges] mb
^java.lang.reflect.Method meth bridges]
(gen-bridge meth dest))
(doseq [^java.lang.reflect.Method meth mm]
(gen-method meth
(fn [^GeneratorAdapter gen ^Method m]
(. gen (loadThis))
;push args
(. gen (loadArgs))
;call super
(. gen (visitMethodInsn (. Opcodes INVOKESPECIAL)
(. super-type (getInternalName))
(. m (getName))
(. m (getDescriptor)))))))
;add methods matching interfaces', if no mapping -> throw
(doseq [^java.lang.reflect.Method meth ifaces-meths]
(gen-method meth
(fn [^GeneratorAdapter gen ^Method m]
(. gen (throwException ex-type (. m (getName))))))))
;finish class def
(. cv (visitEnd))
[cname (. cv toByteArray)]))
(defn- get-super-and-interfaces [bases]
(if (. ^Class (first bases) (isInterface))
[Object bases]
[(first bases) (next bases)]))
(defn get-proxy-class
"Takes an optional single class followed by zero or more
interfaces. If not supplied class defaults to Object. Creates an
returns an instance of a proxy class derived from the supplied
classes. The resulting value is cached and used for any subsequent
requests for the same class set. Returns a Class object."
{:added "1.0"}
[& bases]
(let [[super interfaces] (get-super-and-interfaces bases)
pname (proxy-name super interfaces)]
(or (RT/loadClassForName pname)
(let [[cname bytecode] (generate-proxy super interfaces)]
(. ^DynamicClassLoader (deref clojure.lang.Compiler/LOADER) (defineClass pname bytecode [super interfaces]))))))
(defn construct-proxy
"Takes a proxy class and any arguments for its superclass ctor and
creates and returns an instance of the proxy."
{:added "1.0"}
[c & ctor-args]
(. Reflector (invokeConstructor c (to-array ctor-args))))
(defn init-proxy
"Takes a proxy instance and a map of strings (which must
correspond to methods of the proxy superclass/superinterfaces) to
fns (which must take arguments matching the corresponding method,
plus an additional (explicit) first arg corresponding to this, and
sets the proxy's fn map."
{:added "1.0"}
[^IProxy proxy mappings]
(. proxy (__initClojureFnMappings mappings)))
(defn update-proxy
"Takes a proxy instance and a map of strings (which must
correspond to methods of the proxy superclass/superinterfaces) to
fns (which must take arguments matching the corresponding method,
plus an additional (explicit) first arg corresponding to this, and
updates (via assoc) the proxy's fn map. nil can be passed instead of
a fn, in which case the corresponding method will revert to the
default behavior. Note that this function can be used to update the
behavior of an existing instance without changing its identity."
{:added "1.0"}
[^IProxy proxy mappings]
(. proxy (__updateClojureFnMappings mappings)))
(defn proxy-mappings
"Takes a proxy instance and returns the proxy's fn map."
{:added "1.0"}
[^IProxy proxy]
(. proxy (__getClojureFnMappings)))
(defmacro proxy
"class-and-interfaces - a vector of class names
args - a (possibly empty) vector of arguments to the superclass
constructor.
f => (name [params*] body) or
(name ([params*] body) ([params+] body) ...)
Expands to code which creates a instance of a proxy class that
implements the named class/interface(s) by calling the supplied
fns. A single class, if provided, must be first. If not provided it
defaults to Object.
The interfaces names must be valid interface types. If a method fn
is not provided for a class method, the superclass methd will be
called. If a method fn is not provided for an interface method, an
UnsupportedOperationException will be thrown should it be
called. Method fns are closures and can capture the environment in
which proxy is called. Each method fn takes an additional implicit
first arg, which is bound to 'this. Note that while method fns can
be provided to override protected methods, they have no other access
to protected members, nor to super, as these capabilities cannot be
proxied."
{:added "1.0"}
[class-and-interfaces args & fs]
(let [bases (map #(or (resolve %) (throw (Exception. (str "Can't resolve: " %))))
class-and-interfaces)
[super interfaces] (get-super-and-interfaces bases)
compile-effect (when *compile-files*
(let [[cname bytecode] (generate-proxy super interfaces)]
(clojure.lang.Compiler/writeClassFile cname bytecode)))
pc-effect (apply get-proxy-class bases)
pname (proxy-name super interfaces)]
;remember the class to prevent it from disappearing before use
(intern *ns* (symbol pname) pc-effect)
`(let [;pc# (get-proxy-class ~@class-and-interfaces)
p# (new ~(symbol pname) ~@args)] ;(construct-proxy pc# ~@args)]
(init-proxy p#
~(loop [fmap {} fs fs]
(if fs
(let [[sym & meths] (first fs)
meths (if (vector? (first meths))
(list meths)
meths)
meths (map (fn [[params & body]]
(cons (apply vector 'this params) body))
meths)]
(if-not (contains? fmap (name sym))
(recur (assoc fmap (name sym) (cons `fn meths)) (next fs))
(throw (IllegalArgumentException.
(str "Method '" (name sym) "' redefined")))))
fmap)))
p#)))
(defn proxy-call-with-super [call this meth]
(let [m (proxy-mappings this)]
(update-proxy this (assoc m meth nil))
(let [ret (call)]
(update-proxy this m)
ret)))
(defmacro proxy-super
"Use to call a superclass method in the body of a proxy method.
Note, expansion captures 'this"
{:added "1.0"}
[meth & args]
`(proxy-call-with-super (fn [] (. ~'this ~meth ~@args)) ~'this ~(name meth)))
(defn bean
"Takes a Java object and returns a read-only implementation of the
map abstraction based upon its JavaBean properties."
{:added "1.0"}
[^Object x]
(let [c (. x (getClass))
pmap (reduce (fn [m ^java.beans.PropertyDescriptor pd]
(let [name (. pd (getName))
method (. pd (getReadMethod))]
(if (and method (zero? (alength (. method (getParameterTypes)))))
(assoc m (keyword name) (fn [] (clojure.lang.Reflector/prepRet (. method (invoke x nil)))))
m)))
{}
(seq (.. java.beans.Introspector
(getBeanInfo c)
(getPropertyDescriptors))))
v (fn [k] ((pmap k)))
snapshot (fn []
(reduce (fn [m e]
(assoc m (key e) ((val e))))
{} (seq pmap)))]
(proxy [clojure.lang.APersistentMap]
[]
(containsKey [k] (contains? pmap k))
(entryAt [k] (when (contains? pmap k) (new clojure.lang.MapEntry k (v k))))
(valAt ([k] (v k))
([k default] (if (contains? pmap k) (v k) default)))
(cons [m] (conj (snapshot) m))
(count [] (count pmap))
(assoc [k v] (assoc (snapshot) k v))
(without [k] (dissoc (snapshot) k))
(seq [] ((fn thisfn [plseq]
(lazy-seq
(when-let [pseq (seq plseq)]
(cons (new clojure.lang.MapEntry (first pseq) (v (first pseq)))
(thisfn (rest pseq)))))) (keys pmap))))))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/genclass.clj 0000664 0000000 0000000 00000076023 11575623476 0022662 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(in-ns 'clojure.core)
(import '(java.lang.reflect Modifier Constructor)
'(clojure.asm ClassWriter ClassVisitor Opcodes Type)
'(clojure.asm.commons Method GeneratorAdapter)
'(clojure.lang IPersistentMap))
;(defn method-sig [^java.lang.reflect.Method meth]
; [(. meth (getName)) (seq (. meth (getParameterTypes)))])
(defn- non-private-methods [^Class c]
(loop [mm {}
considered #{}
c c]
(if c
(let [[mm considered]
(loop [mm mm
considered considered
meths (seq (concat
(seq (. c (getDeclaredMethods)))
(seq (. c (getMethods)))))]
(if meths
(let [^java.lang.reflect.Method meth (first meths)
mods (. meth (getModifiers))
mk (method-sig meth)]
(if (or (considered mk)
(not (or (Modifier/isPublic mods) (Modifier/isProtected mods)))
;(. Modifier (isPrivate mods))
(. Modifier (isStatic mods))
(. Modifier (isFinal mods))
(= "finalize" (.getName meth)))
(recur mm (conj considered mk) (next meths))
(recur (assoc mm mk meth) (conj considered mk) (next meths))))
[mm considered]))]
(recur mm considered (. c (getSuperclass))))
mm)))
(defn- ctor-sigs [^Class super]
(for [^Constructor ctor (. super (getDeclaredConstructors))
:when (not (. Modifier (isPrivate (. ctor (getModifiers)))))]
(apply vector (. ctor (getParameterTypes)))))
(defn- escape-class-name [^Class c]
(.. (.getSimpleName c)
(replace "[]" "<>")))
(defn- overload-name [mname pclasses]
(if (seq pclasses)
(apply str mname (interleave (repeat \-)
(map escape-class-name pclasses)))
(str mname "-void")))
(defn- ^java.lang.reflect.Field find-field [^Class c f]
(let [start-class c]
(loop [c c]
(if (= c Object)
(throw (new Exception (str "field, " f ", not defined in class, " start-class ", or its ancestors")))
(let [dflds (.getDeclaredFields c)
rfld (first (filter #(= f (.getName ^java.lang.reflect.Field %)) dflds))]
(or rfld (recur (.getSuperclass c))))))))
;(distinct (map first(keys (mapcat non-private-methods [Object IPersistentMap]))))
(def ^{:private true} prim->class
{'int Integer/TYPE
'long Long/TYPE
'float Float/TYPE
'double Double/TYPE
'void Void/TYPE
'short Short/TYPE
'boolean Boolean/TYPE
'byte Byte/TYPE
'char Character/TYPE})
(defn- ^Class the-class [x]
(cond
(class? x) x
(contains? prim->class x) (prim->class x)
:else (let [strx (str x)]
(clojure.lang.RT/classForName
(if (some #{\. \[} strx)
strx
(str "java.lang." strx))))))
;; someday this can be made codepoint aware
(defn- valid-java-method-name
[^String s]
(= s (clojure.lang.Compiler/munge s)))
(defn- validate-generate-class-options
[{:keys [methods]}]
(let [[mname] (remove valid-java-method-name (map (comp str first) methods))]
(when mname (throw (IllegalArgumentException. (str "Not a valid method name: " mname))))))
(defn- generate-class [options-map]
(validate-generate-class-options options-map)
(let [default-options {:prefix "-" :load-impl-ns true :impl-ns (ns-name *ns*)}
{:keys [name extends implements constructors methods main factory state init exposes
exposes-methods prefix load-impl-ns impl-ns post-init]}
(merge default-options options-map)
name-meta (meta name)
name (str name)
super (if extends (the-class extends) Object)
interfaces (map the-class implements)
supers (cons super interfaces)
ctor-sig-map (or constructors (zipmap (ctor-sigs super) (ctor-sigs super)))
cv (new ClassWriter (. ClassWriter COMPUTE_MAXS))
cname (. name (replace "." "/"))
pkg-name name
impl-pkg-name (str impl-ns)
impl-cname (.. impl-pkg-name (replace "." "/") (replace \- \_))
ctype (. Type (getObjectType cname))
iname (fn [^Class c] (.. Type (getType c) (getInternalName)))
totype (fn [^Class c] (. Type (getType c)))
to-types (fn [cs] (if (pos? (count cs))
(into-array (map totype cs))
(make-array Type 0)))
obj-type ^Type (totype Object)
arg-types (fn [n] (if (pos? n)
(into-array (replicate n obj-type))
(make-array Type 0)))
super-type ^Type (totype super)
init-name (str init)
post-init-name (str post-init)
factory-name (str factory)
state-name (str state)
main-name "main"
var-name (fn [s] (clojure.lang.Compiler/munge (str s "__var")))
class-type (totype Class)
rt-type (totype clojure.lang.RT)
var-type ^Type (totype clojure.lang.Var)
ifn-type (totype clojure.lang.IFn)
iseq-type (totype clojure.lang.ISeq)
ex-type (totype java.lang.UnsupportedOperationException)
all-sigs (distinct (concat (map #(let[[m p] (key %)] {m [p]}) (mapcat non-private-methods supers))
(map (fn [[m p]] {(str m) [p]}) methods)))
sigs-by-name (apply merge-with concat {} all-sigs)
overloads (into {} (filter (fn [[m s]] (next s)) sigs-by-name))
var-fields (concat (when init [init-name])
(when post-init [post-init-name])
(when main [main-name])
;(when exposes-methods (map str (vals exposes-methods)))
(distinct (concat (keys sigs-by-name)
(mapcat (fn [[m s]] (map #(overload-name m (map the-class %)) s)) overloads)
(mapcat (comp (partial map str) vals val) exposes))))
emit-get-var (fn [^GeneratorAdapter gen v]
(let [false-label (. gen newLabel)
end-label (. gen newLabel)]
(. gen getStatic ctype (var-name v) var-type)
(. gen dup)
(. gen invokeVirtual var-type (. Method (getMethod "boolean isBound()")))
(. gen ifZCmp (. GeneratorAdapter EQ) false-label)
(. gen invokeVirtual var-type (. Method (getMethod "Object get()")))
(. gen goTo end-label)
(. gen mark false-label)
(. gen pop)
(. gen visitInsn (. Opcodes ACONST_NULL))
(. gen mark end-label)))
emit-unsupported (fn [^GeneratorAdapter gen ^Method m]
(. gen (throwException ex-type (str (. m (getName)) " ("
impl-pkg-name "/" prefix (.getName m)
" not defined?)"))))
emit-forwarding-method
(fn [name pclasses rclass as-static else-gen]
(let [mname (str name)
pmetas (map meta pclasses)
pclasses (map the-class pclasses)
rclass (the-class rclass)
ptypes (to-types pclasses)
rtype ^Type (totype rclass)
m (new Method mname rtype ptypes)
is-overload (seq (overloads mname))
gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (if as-static (. Opcodes ACC_STATIC) 0))
m nil nil cv)
found-label (. gen (newLabel))
else-label (. gen (newLabel))
end-label (. gen (newLabel))]
(add-annotations gen (meta name))
(dotimes [i (count pmetas)]
(add-annotations gen (nth pmetas i) i))
(. gen (visitCode))
(if (> (count pclasses) 18)
(else-gen gen m)
(do
(when is-overload
(emit-get-var gen (overload-name mname pclasses))
(. gen (dup))
(. gen (ifNonNull found-label))
(. gen (pop)))
(emit-get-var gen mname)
(. gen (dup))
(. gen (ifNull else-label))
(when is-overload
(. gen (mark found-label)))
;if found
(.checkCast gen ifn-type)
(when-not as-static
(. gen (loadThis)))
;box args
(dotimes [i (count ptypes)]
(. gen (loadArg i))
(. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i))))
;call fn
(. gen (invokeInterface ifn-type (new Method "invoke" obj-type
(to-types (replicate (+ (count ptypes)
(if as-static 0 1))
Object)))))
;(into-array (cons obj-type
; (replicate (count ptypes) obj-type))))))
;unbox return
(. gen (unbox rtype))
(when (= (. rtype (getSort)) (. Type VOID))
(. gen (pop)))
(. gen (goTo end-label))
;else call supplied alternative generator
(. gen (mark else-label))
(. gen (pop))
(else-gen gen m)
(. gen (mark end-label))))
(. gen (returnValue))
(. gen (endMethod))))
]
;start class definition
(. cv (visit (. Opcodes V1_5) (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_SUPER))
cname nil (iname super)
(when-let [ifc (seq interfaces)]
(into-array (map iname ifc)))))
; class annotations
(add-annotations cv name-meta)
;static fields for vars
(doseq [v var-fields]
(. cv (visitField (+ (. Opcodes ACC_PRIVATE) (. Opcodes ACC_FINAL) (. Opcodes ACC_STATIC))
(var-name v)
(. var-type getDescriptor)
nil nil)))
;instance field for state
(when state
(. cv (visitField (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_FINAL))
state-name
(. obj-type getDescriptor)
nil nil)))
;static init to set up var fields and load init
(let [gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC))
(. Method getMethod "void ()")
nil nil cv)]
(. gen (visitCode))
(doseq [v var-fields]
(. gen push impl-pkg-name)
(. gen push (str prefix v))
(. gen (invokeStatic var-type (. Method (getMethod "clojure.lang.Var internPrivate(String,String)"))))
(. gen putStatic ctype (var-name v) var-type))
(when load-impl-ns
(. gen push "clojure.core")
(. gen push "load")
(. gen (invokeStatic rt-type (. Method (getMethod "clojure.lang.Var var(String,String)"))))
(. gen push (str "/" impl-cname))
(. gen (invokeInterface ifn-type (new Method "invoke" obj-type (to-types [Object]))))
; (. gen push (str (.replace impl-pkg-name \- \_) "__init"))
; (. gen (invokeStatic class-type (. Method (getMethod "Class forName(String)"))))
(. gen pop))
(. gen (returnValue))
(. gen (endMethod)))
;ctors
(doseq [[pclasses super-pclasses] ctor-sig-map]
(let [pclasses (map the-class pclasses)
super-pclasses (map the-class super-pclasses)
ptypes (to-types pclasses)
super-ptypes (to-types super-pclasses)
m (new Method "" (. Type VOID_TYPE) ptypes)
super-m (new Method "" (. Type VOID_TYPE) super-ptypes)
gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)
no-init-label (. gen newLabel)
end-label (. gen newLabel)
no-post-init-label (. gen newLabel)
end-post-init-label (. gen newLabel)
nth-method (. Method (getMethod "Object nth(Object,int)"))
local (. gen newLocal obj-type)]
(. gen (visitCode))
(if init
(do
(emit-get-var gen init-name)
(. gen dup)
(. gen ifNull no-init-label)
(.checkCast gen ifn-type)
;box init args
(dotimes [i (count pclasses)]
(. gen (loadArg i))
(. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i))))
;call init fn
(. gen (invokeInterface ifn-type (new Method "invoke" obj-type
(arg-types (count ptypes)))))
;expecting [[super-ctor-args] state] returned
(. gen dup)
(. gen push 0)
(. gen (invokeStatic rt-type nth-method))
(. gen storeLocal local)
(. gen (loadThis))
(. gen dupX1)
(dotimes [i (count super-pclasses)]
(. gen loadLocal local)
(. gen push i)
(. gen (invokeStatic rt-type nth-method))
(. clojure.lang.Compiler$HostExpr (emitUnboxArg nil gen (nth super-pclasses i))))
(. gen (invokeConstructor super-type super-m))
(if state
(do
(. gen push 1)
(. gen (invokeStatic rt-type nth-method))
(. gen (putField ctype state-name obj-type)))
(. gen pop))
(. gen goTo end-label)
;no init found
(. gen mark no-init-label)
(. gen (throwException ex-type (str impl-pkg-name "/" prefix init-name " not defined")))
(. gen mark end-label))
(if (= pclasses super-pclasses)
(do
(. gen (loadThis))
(. gen (loadArgs))
(. gen (invokeConstructor super-type super-m)))
(throw (new Exception ":init not specified, but ctor and super ctor args differ"))))
(when post-init
(emit-get-var gen post-init-name)
(. gen dup)
(. gen ifNull no-post-init-label)
(.checkCast gen ifn-type)
(. gen (loadThis))
;box init args
(dotimes [i (count pclasses)]
(. gen (loadArg i))
(. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i))))
;call init fn
(. gen (invokeInterface ifn-type (new Method "invoke" obj-type
(arg-types (inc (count ptypes))))))
(. gen pop)
(. gen goTo end-post-init-label)
;no init found
(. gen mark no-post-init-label)
(. gen (throwException ex-type (str impl-pkg-name "/" prefix post-init-name " not defined")))
(. gen mark end-post-init-label))
(. gen (returnValue))
(. gen (endMethod))
;factory
(when factory
(let [fm (new Method factory-name ctype ptypes)
gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC))
fm nil nil cv)]
(. gen (visitCode))
(. gen newInstance ctype)
(. gen dup)
(. gen (loadArgs))
(. gen (invokeConstructor ctype m))
(. gen (returnValue))
(. gen (endMethod))))))
;add methods matching supers', if no fn -> call super
(let [mm (non-private-methods super)]
(doseq [^java.lang.reflect.Method meth (vals mm)]
(emit-forwarding-method (.getName meth) (.getParameterTypes meth) (.getReturnType meth) false
(fn [^GeneratorAdapter gen ^Method m]
(. gen (loadThis))
;push args
(. gen (loadArgs))
;call super
(. gen (visitMethodInsn (. Opcodes INVOKESPECIAL)
(. super-type (getInternalName))
(. m (getName))
(. m (getDescriptor)))))))
;add methods matching interfaces', if no fn -> throw
(reduce (fn [mm ^java.lang.reflect.Method meth]
(if (contains? mm (method-sig meth))
mm
(do
(emit-forwarding-method (.getName meth) (.getParameterTypes meth) (.getReturnType meth) false
emit-unsupported)
(assoc mm (method-sig meth) meth))))
mm (mapcat #(.getMethods ^Class %) interfaces))
;extra methods
(doseq [[mname pclasses rclass :as msig] methods]
(emit-forwarding-method mname pclasses rclass (:static (meta msig))
emit-unsupported))
;expose specified overridden superclass methods
(doseq [[local-mname ^java.lang.reflect.Method m] (reduce (fn [ms [[name _ _] m]]
(if (contains? exposes-methods (symbol name))
(conj ms [((symbol name) exposes-methods) m])
ms)) [] (seq mm))]
(let [ptypes (to-types (.getParameterTypes m))
rtype (totype (.getReturnType m))
exposer-m (new Method (str local-mname) rtype ptypes)
target-m (new Method (.getName m) rtype ptypes)
gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) exposer-m nil nil cv)]
(. gen (loadThis))
(. gen (loadArgs))
(. gen (visitMethodInsn (. Opcodes INVOKESPECIAL)
(. super-type (getInternalName))
(. target-m (getName))
(. target-m (getDescriptor))))
(. gen (returnValue))
(. gen (endMethod)))))
;main
(when main
(let [m (. Method getMethod "void main (String[])")
gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC))
m nil nil cv)
no-main-label (. gen newLabel)
end-label (. gen newLabel)]
(. gen (visitCode))
(emit-get-var gen main-name)
(. gen dup)
(. gen ifNull no-main-label)
(.checkCast gen ifn-type)
(. gen loadArgs)
(. gen (invokeStatic rt-type (. Method (getMethod "clojure.lang.ISeq seq(Object)"))))
(. gen (invokeInterface ifn-type (new Method "applyTo" obj-type
(into-array [iseq-type]))))
(. gen pop)
(. gen goTo end-label)
;no main found
(. gen mark no-main-label)
(. gen (throwException ex-type (str impl-pkg-name "/" prefix main-name " not defined")))
(. gen mark end-label)
(. gen (returnValue))
(. gen (endMethod))))
;field exposers
(doseq [[f {getter :get setter :set}] exposes]
(let [fld (find-field super (str f))
ftype (totype (.getType fld))
static? (Modifier/isStatic (.getModifiers fld))
acc (+ Opcodes/ACC_PUBLIC (if static? Opcodes/ACC_STATIC 0))]
(when getter
(let [m (new Method (str getter) ftype (to-types []))
gen (new GeneratorAdapter acc m nil nil cv)]
(. gen (visitCode))
(if static?
(. gen getStatic ctype (str f) ftype)
(do
(. gen loadThis)
(. gen getField ctype (str f) ftype)))
(. gen (returnValue))
(. gen (endMethod))))
(when setter
(let [m (new Method (str setter) Type/VOID_TYPE (into-array [ftype]))
gen (new GeneratorAdapter acc m nil nil cv)]
(. gen (visitCode))
(if static?
(do
(. gen loadArgs)
(. gen putStatic ctype (str f) ftype))
(do
(. gen loadThis)
(. gen loadArgs)
(. gen putField ctype (str f) ftype)))
(. gen (returnValue))
(. gen (endMethod))))))
;finish class def
(. cv (visitEnd))
[cname (. cv (toByteArray))]))
(defmacro gen-class
"When compiling, generates compiled bytecode for a class with the
given package-qualified :name (which, as all names in these
parameters, can be a string or symbol), and writes the .class file
to the *compile-path* directory. When not compiling, does
nothing. The gen-class construct contains no implementation, as the
implementation will be dynamically sought by the generated class in
functions in an implementing Clojure namespace. Given a generated
class org.mydomain.MyClass with a method named mymethod, gen-class
will generate an implementation that looks for a function named by
(str prefix mymethod) (default prefix: \"-\") in a
Clojure namespace specified by :impl-ns
(defaults to the current namespace). All inherited methods,
generated methods, and init and main functions (see :methods, :init,
and :main below) will be found similarly prefixed. By default, the
static initializer for the generated class will attempt to load the
Clojure support code for the class as a resource from the classpath,
e.g. in the example case, ``org/mydomain/MyClass__init.class``. This
behavior can be controlled by :load-impl-ns
Note that methods with a maximum of 18 parameters are supported.
In all subsequent sections taking types, the primitive types can be
referred to by their Java names (int, float etc), and classes in the
java.lang package can be used without a package qualifier. All other
classes must be fully qualified.
Options should be a set of key/value pairs, all except for :name are optional:
:name aname
The package-qualified name of the class to be generated
:extends aclass
Specifies the superclass, the non-private methods of which will be
overridden by the class. If not provided, defaults to Object.
:implements [interface ...]
One or more interfaces, the methods of which will be implemented by the class.
:init name
If supplied, names a function that will be called with the arguments
to the constructor. Must return [ [superclass-constructor-args] state]
If not supplied, the constructor args are passed directly to
the superclass constructor and the state will be nil
:constructors {[param-types] [super-param-types], ...}
By default, constructors are created for the generated class which
match the signature(s) of the constructors for the superclass. This
parameter may be used to explicitly specify constructors, each entry
providing a mapping from a constructor signature to a superclass
constructor signature. When you supply this, you must supply an :init
specifier.
:post-init name
If supplied, names a function that will be called with the object as
the first argument, followed by the arguments to the constructor.
It will be called every time an object of this class is created,
immediately after all the inherited constructors have completed.
It's return value is ignored.
:methods [ [name [param-types] return-type], ...]
The generated class automatically defines all of the non-private
methods of its superclasses/interfaces. This parameter can be used
to specify the signatures of additional methods of the generated
class. Static methods can be specified with ^{:static true} in the
signature's metadata. Do not repeat superclass/interface signatures
here.
:main boolean
If supplied and true, a static public main function will be generated. It will
pass each string of the String[] argument as a separate argument to
a function called (str prefix main).
:factory name
If supplied, a (set of) public static factory function(s) will be
created with the given name, and the same signature(s) as the
constructor(s).
:state name
If supplied, a public final instance field with the given name will be
created. You must supply an :init function in order to provide a
value for the state. Note that, though final, the state can be a ref
or agent, supporting the creation of Java objects with transactional
or asynchronous mutation semantics.
:exposes {protected-field-name {:get name :set name}, ...}
Since the implementations of the methods of the generated class
occur in Clojure functions, they have no access to the inherited
protected fields of the superclass. This parameter can be used to
generate public getter/setter methods exposing the protected field(s)
for use in the implementation.
:exposes-methods {super-method-name exposed-name, ...}
It is sometimes necessary to call the superclass' implementation of an
overridden method. Those methods may be exposed and referred in
the new method implementation by a local name.
:prefix string
Default: \"-\" Methods called e.g. Foo will be looked up in vars called
prefixFoo in the implementing ns.
:impl-ns name
Default: the name of the current ns. Implementations of methods will be
looked up in this namespace.
:load-impl-ns boolean
Default: true. Causes the static initializer for the generated class
to reference the load code for the implementing namespace. Should be
true when implementing-ns is the default, false if you intend to
load the code via some other method."
{:added "1.0"}
[& options]
(when *compile-files*
(let [options-map (into {} (map vec (partition 2 options)))
[cname bytecode] (generate-class options-map)]
(clojure.lang.Compiler/writeClassFile cname bytecode))))
;;;;;;;;;;;;;;;;;;;; gen-interface ;;;;;;;;;;;;;;;;;;;;;;
;; based on original contribution by Chris Houser
(defn- ^Type asm-type
"Returns an asm Type object for c, which may be a primitive class
(such as Integer/TYPE), any other class (such as Double), or a
fully-qualified class name given as a string or symbol
(such as 'java.lang.String)"
[c]
(if (or (instance? Class c) (prim->class c))
(Type/getType (the-class c))
(let [strx (str c)]
(Type/getObjectType
(.replace (if (some #{\.} strx)
strx
(str "java.lang." strx))
"." "/")))))
(defn- generate-interface
[{:keys [name extends methods]}]
(let [iname (.replace (str name) "." "/")
cv (ClassWriter. ClassWriter/COMPUTE_MAXS)]
(. cv visit Opcodes/V1_5 (+ Opcodes/ACC_PUBLIC
Opcodes/ACC_ABSTRACT
Opcodes/ACC_INTERFACE)
iname nil "java/lang/Object"
(when (seq extends)
(into-array (map #(.getInternalName (asm-type %)) extends))))
(add-annotations cv (meta name))
(doseq [[mname pclasses rclass pmetas] methods]
(let [mv (. cv visitMethod (+ Opcodes/ACC_PUBLIC Opcodes/ACC_ABSTRACT)
(str mname)
(Type/getMethodDescriptor (asm-type rclass)
(if pclasses
(into-array Type (map asm-type pclasses))
(make-array Type 0)))
nil nil)]
(add-annotations mv (meta mname))
(dotimes [i (count pmetas)]
(add-annotations mv (nth pmetas i) i))
(. mv visitEnd)))
(. cv visitEnd)
[iname (. cv toByteArray)]))
(defmacro gen-interface
"When compiling, generates compiled bytecode for an interface with
the given package-qualified :name (which, as all names in these
parameters, can be a string or symbol), and writes the .class file
to the *compile-path* directory. When not compiling, does nothing.
In all subsequent sections taking types, the primitive types can be
referred to by their Java names (int, float etc), and classes in the
java.lang package can be used without a package qualifier. All other
classes must be fully qualified.
Options should be a set of key/value pairs, all except for :name are
optional:
:name aname
The package-qualified name of the class to be generated
:extends [interface ...]
One or more interfaces, which will be extended by this interface.
:methods [ [name [param-types] return-type], ...]
This parameter is used to specify the signatures of the methods of
the generated interface. Do not repeat superinterface signatures
here."
{:added "1.0"}
[& options]
(let [options-map (apply hash-map options)
[cname bytecode] (generate-interface options-map)]
(if *compile-files*
(clojure.lang.Compiler/writeClassFile cname bytecode)
(.defineClass ^DynamicClassLoader (deref clojure.lang.Compiler/LOADER)
(str (:name options-map)) bytecode options))))
(comment
(defn gen-and-load-class
"Generates and immediately loads the bytecode for the specified
class. Note that a class generated this way can be loaded only once
- the JVM supports only one class with a given name per
classloader. Subsequent to generation you can import it into any
desired namespaces just like any other class. See gen-class for a
description of the options."
{:added "1.0"}
[& options]
(let [options-map (apply hash-map options)
[cname bytecode] (generate-class options-map)]
(.. (clojure.lang.RT/getRootClassLoader) (defineClass cname bytecode options))))
)
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/gvec.clj 0000664 0000000 0000000 00000036732 11575623476 0022012 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;;; a generic vector implementation for vectors of primitives
(in-ns 'clojure.core)
;(set! *warn-on-reflection* true)
(deftype VecNode [edit arr])
(def EMPTY-NODE (VecNode. nil (object-array 32)))
(definterface IVecImpl
(^int tailoff [])
(arrayFor [^int i])
(pushTail [^int level ^clojure.core.VecNode parent ^clojure.core.VecNode tailnode])
(popTail [^int level node])
(newPath [edit ^int level node])
(doAssoc [^int level node ^int i val]))
(definterface ArrayManager
(array [^int size])
(^int alength [arr])
(aclone [arr])
(aget [arr ^int i])
(aset [arr ^int i val]))
(deftype ArrayChunk [^clojure.core.ArrayManager am arr ^int off ^int end]
clojure.lang.Indexed
(nth [_ i] (.aget am arr (+ off i)))
(count [_] (- end off))
clojure.lang.IChunk
(dropFirst [_]
(if (= off end)
(throw (IllegalStateException. "dropFirst of empty chunk"))
(new ArrayChunk am arr (inc off) end)))
(reduce [_ f init]
(loop [ret init i off]
(if (< i end)
(recur (f ret (.aget am arr i)) (inc i))
ret)))
)
(deftype VecSeq [^clojure.core.ArrayManager am ^clojure.core.IVecImpl vec anode ^int i ^int offset]
:no-print true
clojure.core.protocols.InternalReduce
(internal-reduce
[_ f val]
(loop [result val
aidx offset]
(if (< aidx (count vec))
(let [node (.arrayFor vec aidx)
result (loop [result result
node-idx (bit-and (int 0x1f) aidx)]
(if (< node-idx (.alength am node))
(recur (f result (.aget am node node-idx)) (inc node-idx))
result))]
(recur result (bit-and (int 0xffe0) (+ aidx (int 32)))))
result)))
clojure.lang.ISeq
(first [_] (.aget am anode offset))
(next [this]
(if (< (inc offset) (.alength am anode))
(new VecSeq am vec anode i (inc offset))
(.chunkedNext this)))
(more [this]
(let [s (.next this)]
(or s (clojure.lang.PersistentList/EMPTY))))
(cons [this o]
(clojure.lang.Cons. o this))
(count [this]
(loop [i 1
s (next this)]
(if s
(if (instance? clojure.lang.Counted s)
(+ i (.count s))
(recur (inc i) (next s)))
i)))
(equiv [this o]
(cond
(identical? this o) true
(or (instance? clojure.lang.Sequential o) (instance? java.util.List o))
(loop [me this
you (seq o)]
(if (nil? me)
(nil? you)
(and (clojure.lang.Util/equiv (first me) (first you))
(recur (next me) (next you)))))
:else false))
(empty [_]
clojure.lang.PersistentList/EMPTY)
clojure.lang.Seqable
(seq [this] this)
clojure.lang.IChunkedSeq
(chunkedFirst [_] (ArrayChunk. am anode offset (.alength am anode)))
(chunkedNext [_]
(let [nexti (+ i (.alength am anode))]
(when (< nexti (count vec))
(new VecSeq am vec (.arrayFor vec nexti) nexti 0))))
(chunkedMore [this]
(let [s (.chunkedNext this)]
(or s (clojure.lang.PersistentList/EMPTY)))))
(defmethod print-method ::VecSeq [v w]
((get (methods print-method) clojure.lang.ISeq) v w))
(deftype Vec [^clojure.core.ArrayManager am ^int cnt ^int shift ^clojure.core.VecNode root tail _meta]
Object
(equals [this o]
(cond
(identical? this o) true
(or (instance? clojure.lang.IPersistentVector o) (instance? java.util.RandomAccess o))
(and (= cnt (count o))
(loop [i (int 0)]
(cond
(= i cnt) true
(.equals (.nth this i) (nth o i)) (recur (inc i))
:else false)))
(or (instance? clojure.lang.Sequential o) (instance? java.util.List o))
(.equals (seq this) (seq o))
:else false))
;todo - cache
(hashCode [this]
(loop [hash (int 1) i (int 0)]
(if (= i cnt)
hash
(let [val (.nth this i)]
(recur (unchecked-add (unchecked-multiply (int 31) hash)
(clojure.lang.Util/hash val))
(inc i))))))
clojure.lang.Counted
(count [_] cnt)
clojure.lang.IMeta
(meta [_] _meta)
clojure.lang.IObj
(withMeta [_ m] (new Vec am cnt shift root tail m))
clojure.lang.Indexed
(nth [this i]
(let [a (.arrayFor this i)]
(.aget am a (bit-and i (int 0x1f)))))
(nth [this i not-found]
(let [z (int 0)]
(if (and (>= i z) (< i (.count this)))
(.nth this i)
not-found)))
clojure.lang.IPersistentCollection
(cons [this val]
(if (< (- cnt (.tailoff this)) (int 32))
(let [new-tail (.array am (inc (.alength am tail)))]
(System/arraycopy tail 0 new-tail 0 (.alength am tail))
(.aset am new-tail (.alength am tail) val)
(new Vec am (inc cnt) shift root new-tail (meta this)))
(let [tail-node (VecNode. (.edit root) tail)]
(if (> (bit-shift-right cnt (int 5)) (bit-shift-left (int 1) shift)) ;overflow root?
(let [new-root (VecNode. (.edit root) (object-array 32))]
(doto ^objects (.arr new-root)
(aset 0 root)
(aset 1 (.newPath this (.edit root) shift tail-node)))
(new Vec am (inc cnt) (+ shift (int 5)) new-root (let [tl (.array am 1)] (.aset am tl 0 val) tl) (meta this)))
(new Vec am (inc cnt) shift (.pushTail this shift root tail-node)
(let [tl (.array am 1)] (.aset am tl 0 val) tl) (meta this))))))
(empty [_] (new Vec am 0 5 EMPTY-NODE (.array am 0) nil))
(equiv [this o]
(cond
(or (instance? clojure.lang.IPersistentVector o) (instance? java.util.RandomAccess o))
(and (= cnt (count o))
(loop [i (int 0)]
(cond
(= i cnt) true
(= (.nth this i) (nth o i)) (recur (inc i))
:else false)))
(or (instance? clojure.lang.Sequential o) (instance? java.util.List o))
(= (seq this) (seq o))
:else false))
clojure.lang.IPersistentStack
(peek [this]
(when (> cnt (int 0))
(.nth this (dec cnt))))
(pop [this]
(cond
(zero? cnt)
(throw (IllegalStateException. "Can't pop empty vector"))
(= 1 cnt)
(new Vec am 0 5 EMPTY-NODE (.array am 0) (meta this))
(> (- cnt (.tailoff this)) 1)
(let [new-tail (.array am (dec (.alength am tail)))]
(System/arraycopy tail 0 new-tail 0 (.alength am new-tail))
(new Vec am (dec cnt) shift root new-tail (meta this)))
:else
(let [new-tail (.arrayFor this (- cnt 2))
new-root ^clojure.core.VecNode (.popTail this shift root)]
(cond
(nil? new-root)
(new Vec am (dec cnt) shift EMPTY-NODE new-tail (meta this))
(and (> shift 5) (nil? (aget ^objects (.arr new-root) 1)))
(new Vec am (dec cnt) (- shift 5) (aget ^objects (.arr new-root) 0) new-tail (meta this))
:else
(new Vec am (dec cnt) shift new-root new-tail (meta this))))))
clojure.lang.IPersistentVector
(assocN [this i val]
(cond
(and (<= (int 0) i) (< i cnt))
(if (>= i (.tailoff this))
(let [new-tail (.array am (.alength am tail))]
(System/arraycopy tail 0 new-tail 0 (.alength am tail))
(.aset am new-tail (bit-and i (int 0x1f)) val)
(new Vec am cnt shift root new-tail (meta this)))
(new Vec am cnt shift (.doAssoc this shift root i val) tail (meta this)))
(= i cnt) (.cons this val)
:else (throw (IndexOutOfBoundsException.))))
clojure.lang.Reversible
(rseq [this]
(if (> (.count this) 0)
(clojure.lang.APersistentVector$RSeq. this (dec (.count this)))
nil))
clojure.lang.Associative
(assoc [this k v]
(if (clojure.lang.Util/isInteger k)
(.assocN this k v)
(throw (IllegalArgumentException. "Key must be integer"))))
(containsKey [this k]
(and (clojure.lang.Util/isInteger k)
(<= 0 (int k))
(< (int k) cnt)))
(entryAt [this k]
(if (.containsKey this k)
(clojure.lang.MapEntry. k (.nth this (int k)))
nil))
clojure.lang.ILookup
(valAt [this k not-found]
(if (clojure.lang.Util/isInteger k)
(let [i (int k)]
(if (and (>= i 0) (< i cnt))
(.nth this i)
not-found))
not-found))
(valAt [this k] (.valAt this k nil))
clojure.lang.IFn
(invoke [this k]
(if (clojure.lang.Util/isInteger k)
(let [i (int k)]
(if (and (>= i 0) (< i cnt))
(.nth this i)
(throw (IndexOutOfBoundsException.))))
(throw (IllegalArgumentException. "Key must be integer"))))
clojure.lang.Seqable
(seq [this]
(if (zero? cnt)
nil
(VecSeq. am this (.arrayFor this 0) 0 0)))
clojure.lang.Sequential ;marker, no methods
clojure.core.IVecImpl
(tailoff [_]
(- cnt (.alength am tail)))
(arrayFor [this i]
(if (and (<= (int 0) i) (< i cnt))
(if (>= i (.tailoff this))
tail
(loop [node root level shift]
(if (zero? level)
(.arr node)
(recur (aget ^objects (.arr node) (bit-and (bit-shift-right i level) (int 0x1f)))
(- level (int 5))))))
(throw (IndexOutOfBoundsException.))))
(pushTail [this level parent tailnode]
(let [subidx (bit-and (bit-shift-right (dec cnt) level) (int 0x1f))
parent ^clojure.core.VecNode parent
ret (VecNode. (.edit parent) (aclone ^objects (.arr parent)))
node-to-insert (if (= level (int 5))
tailnode
(let [child (aget ^objects (.arr parent) subidx)]
(if child
(.pushTail this (- level (int 5)) child tailnode)
(.newPath this (.edit root) (- level (int 5)) tailnode))))]
(aset ^objects (.arr ret) subidx node-to-insert)
ret))
(popTail [this level node]
(let [node ^clojure.core.VecNode node
subidx (bit-and (bit-shift-right (- cnt (int 2)) level) (int 0x1f))]
(cond
(> level 5)
(let [new-child (.popTail this (- level 5) (aget ^objects (.arr node) subidx))]
(if (and (nil? new-child) (zero? subidx))
nil
(let [arr (aclone ^objects (.arr node))]
(aset arr subidx new-child)
(VecNode. (.edit root) arr))))
(zero? subidx) nil
:else (let [arr (aclone ^objects (.arr node))]
(aset arr subidx nil)
(VecNode. (.edit root) arr)))))
(newPath [this edit ^int level node]
(if (zero? level)
node
(let [ret (VecNode. edit (object-array 32))]
(aset ^objects (.arr ret) 0 (.newPath this edit (- level (int 5)) node))
ret)))
(doAssoc [this level node i val]
(let [node ^clojure.core.VecNode node]
(if (zero? level)
;on this branch, array will need val type
(let [arr (.aclone am (.arr node))]
(.aset am arr (bit-and i (int 0x1f)) val)
(VecNode. (.edit node) arr))
(let [arr (aclone ^objects (.arr node))
subidx (bit-and (bit-shift-right i level) (int 0x1f))]
(aset arr subidx (.doAssoc this (- level (int 5)) (aget arr subidx) i val))
(VecNode. (.edit node) arr)))))
java.lang.Comparable
(compareTo [this o]
(if (identical? this o)
0
(let [#^clojure.lang.IPersistentVector v (cast clojure.lang.IPersistentVector o)
vcnt (.count v)]
(cond
(< cnt vcnt) -1
(> cnt vcnt) 1
:else
(loop [i (int 0)]
(if (= i cnt)
0
(let [comp (clojure.lang.Util/compare (.nth this i) (.nth v i))]
(if (= 0 comp)
(recur (inc i))
comp))))))))
java.lang.Iterable
(iterator [this]
(let [i (java.util.concurrent.atomic.AtomicInteger. 0)]
(reify java.util.Iterator
(hasNext [_] (< (.get i) cnt))
(next [_] (.nth this (dec (.incrementAndGet i))))
(remove [_] (throw (UnsupportedOperationException.))))))
java.util.Collection
(contains [this o] (boolean (some #(= % o) this)))
(containsAll [this c] (every? #(.contains this %) c))
(isEmpty [_] (zero? cnt))
(toArray [this] (into-array Object this))
(toArray [this arr]
(if (>= (count arr) cnt)
(do
(dotimes [i cnt]
(aset arr i (.nth this i)))
arr)
(into-array Object this)))
(size [_] cnt)
(add [_ o] (throw (UnsupportedOperationException.)))
(addAll [_ c] (throw (UnsupportedOperationException.)))
(clear [_] (throw (UnsupportedOperationException.)))
(^boolean remove [_ o] (throw (UnsupportedOperationException.)))
(removeAll [_ c] (throw (UnsupportedOperationException.)))
(retainAll [_ c] (throw (UnsupportedOperationException.)))
java.util.List
(get [this i] (.nth this i))
(indexOf [this o]
(loop [i (int 0)]
(cond
(== i cnt) -1
(= o (.nth this i)) i
:else (recur (inc i)))))
(lastIndexOf [this o]
(loop [i (dec cnt)]
(cond
(< i 0) -1
(= o (.nth this i)) i
:else (recur (dec i)))))
(listIterator [this] (.listIterator this 0))
(listIterator [this i]
(let [i (java.util.concurrent.atomic.AtomicInteger. i)]
(reify java.util.ListIterator
(hasNext [_] (< (.get i) cnt))
(hasPrevious [_] (pos? i))
(next [_] (.nth this (dec (.incrementAndGet i))))
(nextIndex [_] (.get i))
(previous [_] (.nth this (.decrementAndGet i)))
(previousIndex [_] (dec (.get i)))
(add [_ e] (throw (UnsupportedOperationException.)))
(remove [_] (throw (UnsupportedOperationException.)))
(set [_ e] (throw (UnsupportedOperationException.))))))
(subList [this a z] (subvec this a z))
(add [_ i o] (throw (UnsupportedOperationException.)))
(addAll [_ i c] (throw (UnsupportedOperationException.)))
(^Object remove [_ ^int i] (throw (UnsupportedOperationException.)))
(set [_ i e] (throw (UnsupportedOperationException.)))
)
(defmethod print-method ::Vec [v w]
((get (methods print-method) clojure.lang.IPersistentVector) v w))
(defmacro mk-am {:private true} [t]
(let [garr (gensym)
tgarr (with-meta garr {:tag (symbol (str t "s"))})]
`(reify clojure.core.ArrayManager
(array [_ size#] (~(symbol (str t "-array")) size#))
(alength [_ ~garr] (alength ~tgarr))
(aclone [_ ~garr] (aclone ~tgarr))
(aget [_ ~garr i#] (aget ~tgarr i#))
(aset [_ ~garr i# val#] (aset ~tgarr i# (~t val#))))))
(def ^{:private true} ams
{:int (mk-am int)
:long (mk-am long)
:float (mk-am float)
:double (mk-am double)
:byte (mk-am byte)
:short (mk-am short)
:char (mk-am char)
:boolean (mk-am boolean)})
(defn vector-of
"Creates a new vector of a single primitive type t, where t is one
of :int :long :float :double :byte :short :char or :boolean. The
resulting vector complies with the interface of vectors in general,
but stores the values unboxed internally."
{:added "1.2"}
[t]
(let [am ^clojure.core.ArrayManager (ams t)]
(Vec. am 0 5 EMPTY-NODE (.array am 0) nil)))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/inspector.clj 0000664 0000000 0000000 00000012711 11575623476 0023063 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns ^{:doc "Graphical object inspector for Clojure data structures."
:author "Rich Hickey"}
clojure.inspector
(:import
(java.awt BorderLayout)
(java.awt.event ActionEvent ActionListener)
(javax.swing.tree TreeModel)
(javax.swing.table TableModel AbstractTableModel)
(javax.swing JPanel JTree JTable JScrollPane JFrame JToolBar JButton SwingUtilities)))
(defn atom? [x]
(not (coll? x)))
(defn collection-tag [x]
(cond
(instance? java.util.Map$Entry x) :entry
(instance? java.util.Map x) :map
(sequential? x) :seq
:else :atom))
(defmulti is-leaf collection-tag)
(defmulti get-child (fn [parent index] (collection-tag parent)))
(defmulti get-child-count collection-tag)
(defmethod is-leaf :default [node]
(atom? node))
(defmethod get-child :default [parent index]
(nth parent index))
(defmethod get-child-count :default [parent]
(count parent))
(defmethod is-leaf :entry [e]
(is-leaf (val e)))
(defmethod get-child :entry [e index]
(get-child (val e) index))
(defmethod get-child-count :entry [e]
(count (val e)))
(defmethod is-leaf :map [m]
false)
(defmethod get-child :map [m index]
(nth (seq m) index))
(defn tree-model [data]
(proxy [TreeModel] []
(getRoot [] data)
(addTreeModelListener [treeModelListener])
(getChild [parent index]
(get-child parent index))
(getChildCount [parent]
(get-child-count parent))
(isLeaf [node]
(is-leaf node))
(valueForPathChanged [path newValue])
(getIndexOfChild [parent child]
-1)
(removeTreeModelListener [treeModelListener])))
(defn old-table-model [data]
(let [row1 (first data)
colcnt (count row1)
cnt (count data)
vals (if (map? row1) vals identity)]
(proxy [TableModel] []
(addTableModelListener [tableModelListener])
(getColumnClass [columnIndex] Object)
(getColumnCount [] colcnt)
(getColumnName [columnIndex]
(if (map? row1)
(name (nth (keys row1) columnIndex))
(str columnIndex)))
(getRowCount [] cnt)
(getValueAt [rowIndex columnIndex]
(nth (vals (nth data rowIndex)) columnIndex))
(isCellEditable [rowIndex columnIndex] false)
(removeTableModelListener [tableModelListener]))))
(defn inspect-tree
"creates a graphical (Swing) inspector on the supplied hierarchical data"
{:added "1.0"}
[data]
(doto (JFrame. "Clojure Inspector")
(.add (JScrollPane. (JTree. (tree-model data))))
(.setSize 400 600)
(.setVisible true)))
(defn inspect-table
"creates a graphical (Swing) inspector on the supplied regular
data, which must be a sequential data structure of data structures
of equal length"
{:added "1.0"}
[data]
(doto (JFrame. "Clojure Inspector")
(.add (JScrollPane. (JTable. (old-table-model data))))
(.setSize 400 600)
(.setVisible true)))
(defmulti list-provider class)
(defmethod list-provider :default [x]
{:nrows 1 :get-value (fn [i] x) :get-label (fn [i] (.getName (class x)))})
(defmethod list-provider java.util.List [c]
(let [v (if (vector? c) c (vec c))]
{:nrows (count v)
:get-value (fn [i] (v i))
:get-label (fn [i] i)}))
(defmethod list-provider java.util.Map [c]
(let [v (vec (sort (map (fn [[k v]] (vector k v)) c)))]
{:nrows (count v)
:get-value (fn [i] ((v i) 1))
:get-label (fn [i] ((v i) 0))}))
(defn list-model [provider]
(let [{:keys [nrows get-value get-label]} provider]
(proxy [AbstractTableModel] []
(getColumnCount [] 2)
(getRowCount [] nrows)
(getValueAt [rowIndex columnIndex]
(cond
(= 0 columnIndex) (get-label rowIndex)
(= 1 columnIndex) (print-str (get-value rowIndex)))))))
(defmulti table-model class)
(defmethod table-model :default [x]
(proxy [AbstractTableModel] []
(getColumnCount [] 2)
(getRowCount [] 1)
(getValueAt [rowIndex columnIndex]
(if (zero? columnIndex)
(class x)
x))))
;(defn make-inspector [x]
; (agent {:frame frame :data x :parent nil :index 0}))
(defn inspect
"creates a graphical (Swing) inspector on the supplied object"
{:added "1.0"}
[x]
(doto (JFrame. "Clojure Inspector")
(.add
(doto (JPanel. (BorderLayout.))
(.add (doto (JToolBar.)
(.add (JButton. "Back"))
(.addSeparator)
(.add (JButton. "List"))
(.add (JButton. "Table"))
(.add (JButton. "Bean"))
(.add (JButton. "Line"))
(.add (JButton. "Bar"))
(.addSeparator)
(.add (JButton. "Prev"))
(.add (JButton. "Next")))
BorderLayout/NORTH)
(.add
(JScrollPane.
(doto (JTable. (list-model (list-provider x)))
(.setAutoResizeMode JTable/AUTO_RESIZE_LAST_COLUMN)))
BorderLayout/CENTER)))
(.setSize 400 400)
(.setVisible true)))
(comment
(load-file "src/inspector.clj")
(refer 'inspector)
(inspect-tree {:a 1 :b 2 :c [1 2 3 {:d 4 :e 5 :f [6 7 8]}]})
(inspect-table [[1 2 3][4 5 6][7 8 9][10 11 12]])
)
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/java/ 0000775 0000000 0000000 00000000000 11575623476 0021302 5 ustar 00root root 0000000 0000000 clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/java/browse.clj 0000664 0000000 0000000 00000003603 11575623476 0023277 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns
^{:author "Christophe Grand",
:doc "Start a web browser from Clojure"}
clojure.java.browse
(:require [clojure.java.shell :as sh])
(:import (java.net URI)))
(defn- macosx? []
(-> "os.name" System/getProperty .toLowerCase
(.startsWith "mac os x")))
(def *open-url-script* (when (macosx?) "/usr/bin/open"))
(defn- open-url-in-browser
"Opens url (a string) in the default system web browser. May not
work on all platforms. Returns url on success, nil if not
supported."
[url]
(try
(when (clojure.lang.Reflector/invokeStaticMethod "java.awt.Desktop"
"isDesktopSupported" (to-array nil))
(-> (clojure.lang.Reflector/invokeStaticMethod "java.awt.Desktop"
"getDesktop" (to-array nil))
(.browse (URI. url)))
url)
(catch ClassNotFoundException e
nil)))
(defn- open-url-in-swing
"Opens url (a string) in a Swing window."
[url]
; the implementation of this function resides in another namespace to be loaded "on demand"
; this fixes a bug on mac os x where the process turns into a GUI app
; see http://code.google.com/p/clojure-contrib/issues/detail?id=32
(require 'clojure.java.browse-ui)
((find-var 'clojure.java.browse-ui/open-url-in-swing) url))
(defn browse-url
"Open url in a browser"
{:added "1.2"}
[url]
(or (open-url-in-browser url)
(when *open-url-script* (sh/sh *open-url-script* (str url)) true)
(open-url-in-swing url)))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/java/browse_ui.clj 0000664 0000000 0000000 00000002532 11575623476 0023774 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns
^{:author "Christophe Grand",
:doc "Helper namespace for clojure.java.browse.
Prevents console apps from becoming GUI unnecessarily."}
clojure.java.browse-ui)
(defn- open-url-in-swing
[url]
(let [htmlpane (javax.swing.JEditorPane. url)]
(.setEditable htmlpane false)
(.addHyperlinkListener htmlpane
(proxy [javax.swing.event.HyperlinkListener] []
(hyperlinkUpdate [#^javax.swing.event.HyperlinkEvent e]
(when (= (.getEventType e) (. javax.swing.event.HyperlinkEvent$EventType ACTIVATED))
(if (instance? javax.swing.text.html.HTMLFrameHyperlinkEvent e)
(-> htmlpane .getDocument (.processHTMLFrameHyperlinkEvent e))
(.setPage htmlpane (.getURL e)))))))
(doto (javax.swing.JFrame.)
(.setContentPane (javax.swing.JScrollPane. htmlpane))
(.setBounds 32 32 700 900)
(.show))))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/java/io.clj 0000664 0000000 0000000 00000035604 11575623476 0022413 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns
^{:author "Stuart Sierra, Chas Emerick, Stuart Halloway",
:doc "This file defines polymorphic I/O utility functions for Clojure."}
clojure.java.io
(:import
(java.io Reader InputStream InputStreamReader PushbackReader
BufferedReader File OutputStream
OutputStreamWriter BufferedWriter Writer
FileInputStream FileOutputStream ByteArrayOutputStream
StringReader ByteArrayInputStream
BufferedInputStream BufferedOutputStream
CharArrayReader Closeable)
(java.net URI URL MalformedURLException Socket)))
(def
^{:doc "Type object for a Java primitive byte array."
:private true
}
byte-array-type (class (make-array Byte/TYPE 0)))
(def
^{:doc "Type object for a Java primitive char array."
:private true}
char-array-type (class (make-array Character/TYPE 0)))
(defprotocol ^{:added "1.2"} Coercions
"Coerce between various 'resource-namish' things."
(^{:tag java.io.File, :added "1.2"} as-file [x] "Coerce argument to a file.")
(^{:tag java.net.URL, :added "1.2"} as-url [x] "Coerce argument to a URL."))
(extend-protocol Coercions
nil
(as-file [_] nil)
(as-url [_] nil)
String
(as-file [s] (File. s))
(as-url [s] (URL. s))
File
(as-file [f] f)
(as-url [f] (.toURL f))
URL
(as-url [u] u)
(as-file [u]
(if (= "file" (.getProtocol u))
(as-file (.getPath u))
(throw (IllegalArgumentException. "Not a file: " u))))
URI
(as-url [u] (.toURL u))
(as-file [u] (as-file (as-url u))))
(defprotocol ^{:added "1.2"} IOFactory
"Factory functions that create ready-to-use, buffered versions of
the various Java I/O stream types, on top of anything that can
be unequivocally converted to the requested kind of stream.
Common options include
:append true to open stream in append mode
:encoding string name of encoding to use, e.g. \"UTF-8\".
Callers should generally prefer the higher level API provided by
reader, writer, input-stream, and output-stream."
(^{:added "1.2"} make-reader [x opts] "Creates a BufferedReader. See also IOFactory docs.")
(^{:added "1.2"} make-writer [x opts] "Creates a BufferedWriter. See also IOFactory docs.")
(^{:added "1.2"} make-input-stream [x opts] "Creates a BufferedInputStream. See also IOFactory docs.")
(^{:added "1.2"} make-output-stream [x opts] "Creates a BufferedOutputStream. See also IOFactory docs."))
(defn ^Reader reader
"Attempts to coerce its argument into an open java.io.Reader.
Default implementations always return a java.io.BufferedReader.
Default implementations are provided for Reader, BufferedReader,
InputStream, File, URI, URL, Socket, byte arrays, character arrays,
and String.
If argument is a String, it tries to resolve it first as a URI, then
as a local file name. URIs with a 'file' protocol are converted to
local file names.
Should be used inside with-open to ensure the Reader is properly
closed."
{:added "1.2"}
[x & opts]
(make-reader x (when opts (apply hash-map opts))))
(defn ^Writer writer
"Attempts to coerce its argument into an open java.io.Writer.
Default implementations always return a java.io.BufferedWriter.
Default implementations are provided for Writer, BufferedWriter,
OutputStream, File, URI, URL, Socket, and String.
If the argument is a String, it tries to resolve it first as a URI, then
as a local file name. URIs with a 'file' protocol are converted to
local file names.
Should be used inside with-open to ensure the Writer is properly
closed."
{:added "1.2"}
[x & opts]
(make-writer x (when opts (apply hash-map opts))))
(defn ^InputStream input-stream
"Attempts to coerce its argument into an open java.io.InputStream.
Default implementations always return a java.io.BufferedInputStream.
Default implementations are defined for OutputStream, File, URI, URL,
Socket, byte array, and String arguments.
If the argument is a String, it tries to resolve it first as a URI, then
as a local file name. URIs with a 'file' protocol are converted to
local file names.
Should be used inside with-open to ensure the InputStream is properly
closed."
{:added "1.2"}
[x & opts]
(make-input-stream x (when opts (apply hash-map opts))))
(defn ^OutputStream output-stream
"Attempts to coerce its argument into an open java.io.OutputStream.
Default implementations always return a java.io.BufferedOutputStream.
Default implementations are defined for OutputStream, File, URI, URL,
Socket, and String arguments.
If the argument is a String, it tries to resolve it first as a URI, then
as a local file name. URIs with a 'file' protocol are converted to
local file names.
Should be used inside with-open to ensure the OutputStream is
properly closed."
{:added "1.2"}
[x & opts]
(make-output-stream x (when opts (apply hash-map opts))))
(defn- ^Boolean append? [opts]
(boolean (:append opts)))
(defn- ^String encoding [opts]
(or (:encoding opts) "UTF-8"))
(defn- buffer-size [opts]
(or (:buffer-size opts) 1024))
(def default-streams-impl
{:make-reader (fn [x opts] (make-reader (make-input-stream x opts) opts))
:make-writer (fn [x opts] (make-writer (make-output-stream x opts) opts))
:make-input-stream (fn [x opts]
(throw (IllegalArgumentException.
(str "Cannot open <" (pr-str x) "> as an InputStream."))))
:make-output-stream (fn [x opts]
(throw (IllegalArgumentException.
(str "Cannot open <" (pr-str x) "> as an OutputStream."))))})
(defn- inputstream->reader
[^InputStream is opts]
(make-reader (InputStreamReader. is (encoding opts)) opts))
(defn- outputstream->writer
[^OutputStream os opts]
(make-writer (OutputStreamWriter. os (encoding opts)) opts))
(extend BufferedInputStream
IOFactory
(assoc default-streams-impl
:make-input-stream (fn [x opts] x)
:make-reader inputstream->reader))
(extend InputStream
IOFactory
(assoc default-streams-impl
:make-input-stream (fn [x opts] (BufferedInputStream. x))
:make-reader inputstream->reader))
(extend Reader
IOFactory
(assoc default-streams-impl
:make-reader (fn [x opts] (BufferedReader. x))))
(extend BufferedReader
IOFactory
(assoc default-streams-impl
:make-reader (fn [x opts] x)))
(extend Writer
IOFactory
(assoc default-streams-impl
:make-writer (fn [x opts] (BufferedWriter. x))))
(extend BufferedWriter
IOFactory
(assoc default-streams-impl
:make-writer (fn [x opts] x)))
(extend OutputStream
IOFactory
(assoc default-streams-impl
:make-output-stream (fn [x opts] (BufferedOutputStream. x))
:make-writer outputstream->writer))
(extend BufferedOutputStream
IOFactory
(assoc default-streams-impl
:make-output-stream (fn [x opts] x)
:make-writer outputstream->writer))
(extend File
IOFactory
(assoc default-streams-impl
:make-input-stream (fn [^File x opts] (make-input-stream (FileInputStream. x) opts))
:make-output-stream (fn [^File x opts] (make-output-stream (FileOutputStream. x (append? opts)) opts))))
(extend URL
IOFactory
(assoc default-streams-impl
:make-input-stream (fn [^URL x opts]
(make-input-stream
(if (= "file" (.getProtocol x))
(FileInputStream. (.getPath x))
(.openStream x)) opts))
:make-output-stream (fn [^URL x opts]
(if (= "file" (.getProtocol x))
(make-output-stream (File. (.getPath x)) opts)
(throw (IllegalArgumentException. (str "Can not write to non-file URL <" x ">")))))))
(extend URI
IOFactory
(assoc default-streams-impl
:make-input-stream (fn [^URI x opts] (make-input-stream (.toURL x) opts))
:make-output-stream (fn [^URI x opts] (make-output-stream (.toURL x) opts))))
(extend String
IOFactory
(assoc default-streams-impl
:make-input-stream (fn [^String x opts]
(try
(make-input-stream (URL. x) opts)
(catch MalformedURLException e
(make-input-stream (File. x) opts))))
:make-output-stream (fn [^String x opts]
(try
(make-output-stream (URL. x) opts)
(catch MalformedURLException err
(make-output-stream (File. x) opts))))))
(extend Socket
IOFactory
(assoc default-streams-impl
:make-input-stream (fn [^Socket x opts] (make-input-stream (.getInputStream x) opts))
:make-output-stream (fn [^Socket x opts] (make-output-stream (.getOutputStream x) opts))))
(extend byte-array-type
IOFactory
(assoc default-streams-impl
:make-input-stream (fn [x opts] (make-input-stream (ByteArrayInputStream. x) opts))))
(extend char-array-type
IOFactory
(assoc default-streams-impl
:make-reader (fn [x opts] (make-reader (CharArrayReader. x) opts))))
(extend Object
IOFactory
default-streams-impl)
(defmulti
#^{:doc "Internal helper for copy"
:private true
:arglists '([input output opts])}
do-copy
(fn [input output opts] [(type input) (type output)]))
(defmethod do-copy [InputStream OutputStream] [#^InputStream input #^OutputStream output opts]
(let [buffer (make-array Byte/TYPE (buffer-size opts))]
(loop []
(let [size (.read input buffer)]
(when (pos? size)
(do (.write output buffer 0 size)
(recur)))))))
(defmethod do-copy [InputStream Writer] [#^InputStream input #^Writer output opts]
(let [#^"[B" buffer (make-array Byte/TYPE (buffer-size opts))]
(loop []
(let [size (.read input buffer)]
(when (pos? size)
(let [chars (.toCharArray (String. buffer 0 size (encoding opts)))]
(do (.write output chars)
(recur))))))))
(defmethod do-copy [InputStream File] [#^InputStream input #^File output opts]
(with-open [out (FileOutputStream. output)]
(do-copy input out opts)))
(defmethod do-copy [Reader OutputStream] [#^Reader input #^OutputStream output opts]
(let [#^"[C" buffer (make-array Character/TYPE (buffer-size opts))]
(loop []
(let [size (.read input buffer)]
(when (pos? size)
(let [bytes (.getBytes (String. buffer 0 size) (encoding opts))]
(do (.write output bytes)
(recur))))))))
(defmethod do-copy [Reader Writer] [#^Reader input #^Writer output opts]
(let [#^"[C" buffer (make-array Character/TYPE (buffer-size opts))]
(loop []
(let [size (.read input buffer)]
(when (pos? size)
(do (.write output buffer 0 size)
(recur)))))))
(defmethod do-copy [Reader File] [#^Reader input #^File output opts]
(with-open [out (FileOutputStream. output)]
(do-copy input out opts)))
(defmethod do-copy [File OutputStream] [#^File input #^OutputStream output opts]
(with-open [in (FileInputStream. input)]
(do-copy in output opts)))
(defmethod do-copy [File Writer] [#^File input #^Writer output opts]
(with-open [in (FileInputStream. input)]
(do-copy in output opts)))
(defmethod do-copy [File File] [#^File input #^File output opts]
(with-open [in (FileInputStream. input)
out (FileOutputStream. output)]
(do-copy in out opts)))
(defmethod do-copy [String OutputStream] [#^String input #^OutputStream output opts]
(do-copy (StringReader. input) output opts))
(defmethod do-copy [String Writer] [#^String input #^Writer output opts]
(do-copy (StringReader. input) output opts))
(defmethod do-copy [String File] [#^String input #^File output opts]
(do-copy (StringReader. input) output opts))
(defmethod do-copy [char-array-type OutputStream] [input #^OutputStream output opts]
(do-copy (CharArrayReader. input) output opts))
(defmethod do-copy [char-array-type Writer] [input #^Writer output opts]
(do-copy (CharArrayReader. input) output opts))
(defmethod do-copy [char-array-type File] [input #^File output opts]
(do-copy (CharArrayReader. input) output opts))
(defmethod do-copy [byte-array-type OutputStream] [#^"[B" input #^OutputStream output opts]
(do-copy (ByteArrayInputStream. input) output opts))
(defmethod do-copy [byte-array-type Writer] [#^"[B" input #^Writer output opts]
(do-copy (ByteArrayInputStream. input) output opts))
(defmethod do-copy [byte-array-type File] [#^"[B" input #^Writer output opts]
(do-copy (ByteArrayInputStream. input) output opts))
(defn copy
"Copies input to output. Returns nil or throws IOException.
Input may be an InputStream, Reader, File, byte[], or String.
Output may be an OutputStream, Writer, or File.
Options are key/value pairs and may be one of
:buffer-size buffer size to use, default is 1024.
:encoding encoding to use if converting between
byte and char streams.
Does not close any streams except those it opens itself
(on a File)."
{:added "1.2"}
[input output & opts]
(do-copy input output (when opts (apply hash-map opts))))
(defn ^String as-relative-path
"Take an as-file-able thing and return a string if it is
a relative path, else IllegalArgumentException."
{:added "1.2"}
[x]
(let [^File f (as-file x)]
(if (.isAbsolute f)
(throw (IllegalArgumentException. (str f " is not a relative path")))
(.getPath f))))
(defn ^File file
"Returns a java.io.File, passing each arg to as-file. Multiple-arg
versions treat the first argument as parent and subsequent args as
children relative to the parent."
{:added "1.2"}
([arg]
(as-file arg))
([parent child]
(File. ^File (as-file parent) ^String (as-relative-path child)))
([parent child & more]
(reduce file (file parent child) more)))
(defn delete-file
"Delete file f. Raise an exception if it fails unless silently is true."
{:added "1.2"}
[f & [silently]]
(or (.delete (file f))
silently
(throw (java.io.IOException. (str "Couldn't delete " f)))))
(defn make-parents
"Given the same arg(s) as for file, creates all parent directories of
the file they represent."
{:added "1.2"}
[f & more]
(.mkdirs (.getParentFile ^File (apply file f more))))
(defn ^URL resource
"Returns the URL for a named resource. Use the context class loader
if no loader is specified."
{:added "1.2"}
([n] (resource n (.getContextClassLoader (Thread/currentThread))))
([n ^ClassLoader loader] (.getResource loader n)))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/java/javadoc.clj 0000664 0000000 0000000 00000006245 11575623476 0023412 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns
^{:author "Christophe Grand, Stuart Sierra",
:doc "A repl helper to quickly open javadocs."}
clojure.java.javadoc
(:use [clojure.java.browse :only (browse-url)] )
(:import
(java.io File)))
(def *feeling-lucky-url* "http://www.google.com/search?btnI=I%27m%20Feeling%20Lucky&q=allinurl:")
(def *feeling-lucky* true)
(def *local-javadocs* (ref (list)))
(def *core-java-api*
(if (= "1.5" (System/getProperty "java.specification.version"))
"http://java.sun.com/j2se/1.5.0/docs/api/"
"http://java.sun.com/javase/6/docs/api/"))
(def *remote-javadocs*
(ref (sorted-map
"java." *core-java-api*
"javax." *core-java-api*
"org.ietf.jgss." *core-java-api*
"org.omg." *core-java-api*
"org.w3c.dom." *core-java-api*
"org.xml.sax." *core-java-api*
"org.apache.commons.codec." "http://commons.apache.org/codec/api-release/"
"org.apache.commons.io." "http://commons.apache.org/io/api-release/"
"org.apache.commons.lang." "http://commons.apache.org/lang/api-release/")))
(defn add-local-javadoc
"Adds to the list of local Javadoc paths."
{:added "1.2"}
[path]
(dosync (commute *local-javadocs* conj path)))
(defn add-remote-javadoc
"Adds to the list of remote Javadoc URLs. package-prefix is the
beginning of the package name that has docs at this URL."
{:added "1.2"}
[package-prefix url]
(dosync (commute *remote-javadocs* assoc package-prefix url)))
(defn- javadoc-url
"Searches for a URL for the given class name. Tries
*local-javadocs* first, then *remote-javadocs*. Returns a string."
{:tag String,
:added "1.2"}
[^String classname]
(let [file-path (.replace classname \. File/separatorChar)
url-path (.replace classname \. \/)]
(if-let [file ^File (first
(filter #(.exists ^File %)
(map #(File. (str %) (str file-path ".html"))
@*local-javadocs*)))]
(-> file .toURI str)
;; If no local file, try remote URLs:
(or (some (fn [[prefix url]]
(when (.startsWith classname prefix)
(str url url-path ".html")))
@*remote-javadocs*)
;; if *feeling-lucky* try a web search
(when *feeling-lucky* (str *feeling-lucky-url* url-path ".html"))))))
(defn javadoc
"Opens a browser window displaying the javadoc for the argument.
Tries *local-javadocs* first, then *remote-javadocs*."
{:added "1.2"}
[class-or-object]
(let [^Class c (if (instance? Class class-or-object)
class-or-object
(class class-or-object))]
(if-let [url (javadoc-url (.getName c))]
(browse-url url)
(println "Could not find Javadoc for" c))))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/java/shell.clj 0000664 0000000 0000000 00000012104 11575623476 0023101 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns
^{:author "Chris Houser, Stuart Halloway",
:doc "Conveniently launch a sub-process providing its stdin and
collecting its stdout"}
clojure.java.shell
(:use [clojure.java.io :only (as-file copy)])
(:import (java.io OutputStreamWriter ByteArrayOutputStream StringWriter)
(java.nio.charset Charset)))
(def *sh-dir* nil)
(def *sh-env* nil)
(defmacro with-sh-dir
"Sets the directory for use with sh, see sh for details."
{:added "1.2"}
[dir & forms]
`(binding [*sh-dir* ~dir]
~@forms))
(defmacro with-sh-env
"Sets the environment for use with sh, see sh for details."
{:added "1.2"}
[env & forms]
`(binding [*sh-env* ~env]
~@forms))
(defn- aconcat
"Concatenates arrays of given type."
[type & xs]
(let [target (make-array type (apply + (map count xs)))]
(loop [i 0 idx 0]
(when-let [a (nth xs i nil)]
(System/arraycopy a 0 target idx (count a))
(recur (inc i) (+ idx (count a)))))
target))
(defn- parse-args
[args]
(let [default-encoding "UTF-8" ;; see sh doc string
default-opts {:out-enc default-encoding :in-enc default-encoding :dir *sh-dir* :env *sh-env*}
[cmd opts] (split-with string? args)]
[cmd (merge default-opts (apply hash-map opts))]))
(defn- ^"[Ljava.lang.String;" as-env-strings
"Helper so that callers can pass a Clojure map for the :env to sh."
[arg]
(cond
(nil? arg) nil
(map? arg) (into-array String (map (fn [[k v]] (str (name k) "=" v)) arg))
true arg))
(defn- stream-to-bytes
[in]
(with-open [bout (ByteArrayOutputStream.)]
(copy in bout)
(.toByteArray bout)))
(defn- stream-to-string
([in] (stream-to-string in (.name (Charset/defaultCharset))))
([in enc]
(with-open [bout (StringWriter.)]
(copy in bout :encoding enc)
(.toString bout))))
(defn- stream-to-enc
[stream enc]
(if (= enc :bytes)
(stream-to-bytes stream)
(stream-to-string stream enc)))
(defn sh
"Passes the given strings to Runtime.exec() to launch a sub-process.
Options are
:in may be given followed by a String or byte array specifying input
to be fed to the sub-process's stdin.
:in-enc option may be given followed by a String, used as a character
encoding name (for example \"UTF-8\" or \"ISO-8859-1\") to
convert the input string specified by the :in option to the
sub-process's stdin. Defaults to UTF-8.
If the :in option provides a byte array, then the bytes are passed
unencoded, and this option is ignored.
:out-enc option may be given followed by :bytes or a String. If a
String is given, it will be used as a character encoding
name (for example \"UTF-8\" or \"ISO-8859-1\") to convert
the sub-process's stdout to a String which is returned.
If :bytes is given, the sub-process's stdout will be stored
in a byte array and returned. Defaults to UTF-8.
:env override the process env with a map (or the underlying Java
String[] if you are a masochist).
:dir override the process dir with a String or java.io.File.
You can bind :env or :dir for multiple operations using with-sh-env
and with-sh-dir.
sh returns a map of
:exit => sub-process's exit code
:out => sub-process's stdout (as byte[] or String)
:err => sub-process's stderr (String via platform default encoding)"
{:added "1.2"}
[& args]
(let [[cmd opts] (parse-args args)
proc (.exec (Runtime/getRuntime)
^"[Ljava.lang.String;" (into-array cmd)
(as-env-strings (:env opts))
(as-file (:dir opts)))
{:keys [in in-enc out-enc]} opts]
(if in
(future
(if (instance? (class (byte-array 0)) in)
(with-open [os (.getOutputStream proc)]
(.write os ^"[B" in))
(with-open [osw (OutputStreamWriter. (.getOutputStream proc) ^String in-enc)]
(.write osw ^String in))))
(.close (.getOutputStream proc)))
(with-open [stdout (.getInputStream proc)
stderr (.getErrorStream proc)]
(let [out (future (stream-to-enc stdout out-enc))
err (future (stream-to-string stderr))
exit-code (.waitFor proc)]
{:exit exit-code :out @out :err @err}))))
(comment
(println (sh "ls" "-l"))
(println (sh "ls" "-l" "/no-such-thing"))
(println (sh "sed" "s/[aeiou]/oo/g" :in "hello there\n"))
(println (sh "cat" :in "x\u25bax\n"))
(println (sh "echo" "x\u25bax"))
(println (sh "echo" "x\u25bax" :out-enc "ISO-8859-1")) ; reads 4 single-byte chars
(println (sh "cat" "myimage.png" :out-enc :bytes)) ; reads binary file into bytes[]
(println (sh "cmd" "/c dir 1>&2"))
)
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/main.clj 0000664 0000000 0000000 00000030077 11575623476 0022006 0 ustar 00root root 0000000 0000000 ;; Copyright (c) Rich Hickey All rights reserved. The use and
;; distribution terms for this software are covered by the Eclipse Public
;; License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can be found
;; in the file epl-v10.html at the root of this distribution. By using this
;; software in any fashion, you are agreeing to be bound by the terms of
;; this license. You must not remove this notice, or any other, from this
;; software.
;; Originally contributed by Stephen C. Gilardi
(ns ^{:doc "Top-level main function for Clojure REPL and scripts."
:author "Stephen C. Gilardi and Rich Hickey"}
clojure.main
(:refer-clojure :exclude [with-bindings])
(:import (clojure.lang Compiler Compiler$CompilerException
LineNumberingPushbackReader RT)))
(declare main)
(defmacro with-bindings
"Executes body in the context of thread-local bindings for several vars
that often need to be set!: *ns* *warn-on-reflection* *math-context*
*print-meta* *print-length* *print-level* *compile-path*
*command-line-args* *1 *2 *3 *e"
[& body]
`(binding [*ns* *ns*
*warn-on-reflection* *warn-on-reflection*
*math-context* *math-context*
*print-meta* *print-meta*
*print-length* *print-length*
*print-level* *print-level*
*compile-path* (System/getProperty "clojure.compile.path" "classes")
*command-line-args* *command-line-args*
*assert* *assert*
*1 nil
*2 nil
*3 nil
*e nil]
~@body))
(defn repl-prompt
"Default :prompt hook for repl"
[]
(printf "%s=> " (ns-name *ns*)))
(defn skip-if-eol
"If the next character on stream s is a newline, skips it, otherwise
leaves the stream untouched. Returns :line-start, :stream-end, or :body
to indicate the relative location of the next character on s. The stream
must either be an instance of LineNumberingPushbackReader or duplicate
its behavior of both supporting .unread and collapsing all of CR, LF, and
CRLF to a single \\newline."
[s]
(let [c (.read s)]
(cond
(= c (int \newline)) :line-start
(= c -1) :stream-end
:else (do (.unread s c) :body))))
(defn skip-whitespace
"Skips whitespace characters on stream s. Returns :line-start, :stream-end,
or :body to indicate the relative location of the next character on s.
Interprets comma as whitespace and semicolon as comment to end of line.
Does not interpret #! as comment to end of line because only one
character of lookahead is available. The stream must either be an
instance of LineNumberingPushbackReader or duplicate its behavior of both
supporting .unread and collapsing all of CR, LF, and CRLF to a single
\\newline."
[s]
(loop [c (.read s)]
(cond
(= c (int \newline)) :line-start
(= c -1) :stream-end
(= c (int \;)) (do (.readLine s) :line-start)
(or (Character/isWhitespace c) (= c (int \,))) (recur (.read s))
:else (do (.unread s c) :body))))
(defn repl-read
"Default :read hook for repl. Reads from *in* which must either be an
instance of LineNumberingPushbackReader or duplicate its behavior of both
supporting .unread and collapsing all of CR, LF, and CRLF into a single
\\newline. repl-read:
- skips whitespace, then
- returns request-prompt on start of line, or
- returns request-exit on end of stream, or
- reads an object from the input stream, then
- skips the next input character if it's end of line, then
- returns the object."
[request-prompt request-exit]
(or ({:line-start request-prompt :stream-end request-exit}
(skip-whitespace *in*))
(let [input (read)]
(skip-if-eol *in*)
input)))
(defn- root-cause
"Returns the initial cause of an exception or error by peeling off all of
its wrappers"
[^Throwable throwable]
(loop [cause throwable]
(if-let [cause (.getCause cause)]
(recur cause)
cause)))
(defn repl-exception
"Returns CompilerExceptions in tact, but only the root cause of other
throwables"
[throwable]
(if (instance? Compiler$CompilerException throwable)
throwable
(root-cause throwable)))
(defn repl-caught
"Default :caught hook for repl"
[e]
(.println *err* (repl-exception e)))
(defn repl
"Generic, reusable, read-eval-print loop. By default, reads from *in*,
writes to *out*, and prints exception summaries to *err*. If you use the
default :read hook, *in* must either be an instance of
LineNumberingPushbackReader or duplicate its behavior of both supporting
.unread and collapsing CR, LF, and CRLF into a single \\newline. Options
are sequential keyword-value pairs. Available options and their defaults:
- :init, function of no arguments, initialization hook called with
bindings for set!-able vars in place.
default: #()
- :need-prompt, function of no arguments, called before each
read-eval-print except the first, the user will be prompted if it
returns true.
default: (if (instance? LineNumberingPushbackReader *in*)
#(.atLineStart *in*)
#(identity true))
- :prompt, function of no arguments, prompts for more input.
default: repl-prompt
- :flush, function of no arguments, flushes output
default: flush
- :read, function of two arguments, reads from *in*:
- returns its first argument to request a fresh prompt
- depending on need-prompt, this may cause the repl to prompt
before reading again
- returns its second argument to request an exit from the repl
- else returns the next object read from the input stream
default: repl-read
- :eval, funtion of one argument, returns the evaluation of its
argument
default: eval
- :print, function of one argument, prints its argument to the output
default: prn
- :caught, function of one argument, a throwable, called when
read, eval, or print throws an exception or error
default: repl-caught"
[& options]
(let [cl (.getContextClassLoader (Thread/currentThread))]
(.setContextClassLoader (Thread/currentThread) (clojure.lang.DynamicClassLoader. cl)))
(let [{:keys [init need-prompt prompt flush read eval print caught]
:or {init #()
need-prompt (if (instance? LineNumberingPushbackReader *in*)
#(.atLineStart ^LineNumberingPushbackReader *in*)
#(identity true))
prompt repl-prompt
flush flush
read repl-read
eval eval
print prn
caught repl-caught}}
(apply hash-map options)
request-prompt (Object.)
request-exit (Object.)
read-eval-print
(fn []
(try
(let [input (read request-prompt request-exit)]
(or (#{request-prompt request-exit} input)
(let [value (eval input)]
(print value)
(set! *3 *2)
(set! *2 *1)
(set! *1 value))))
(catch Throwable e
(caught e)
(set! *e e))))]
(with-bindings
(try
(init)
(catch Throwable e
(caught e)
(set! *e e)))
(use '[clojure.repl :only (source apropos dir)])
(use '[clojure.java.javadoc :only (javadoc)])
(use '[clojure.pprint :only (pp pprint)])
(prompt)
(flush)
(loop []
(when-not
(try (= (read-eval-print) request-exit)
(catch Throwable e
(caught e)
(set! *e e)
nil))
(when (need-prompt)
(prompt)
(flush))
(recur))))))
(defn load-script
"Loads Clojure source from a file or resource given its path. Paths
beginning with @ or @/ are considered relative to classpath."
[^String path]
(if (.startsWith path "@")
(RT/loadResourceScript
(.substring path (if (.startsWith path "@/") 2 1)))
(Compiler/loadFile path)))
(defn- init-opt
"Load a script"
[path]
(load-script path))
(defn- eval-opt
"Evals expressions in str, prints each non-nil result using prn"
[str]
(let [eof (Object.)
reader (LineNumberingPushbackReader. (java.io.StringReader. str))]
(loop [input (read reader false eof)]
(when-not (= input eof)
(let [value (eval input)]
(when-not (nil? value)
(prn value))
(recur (read reader false eof)))))))
(defn- init-dispatch
"Returns the handler associated with an init opt"
[opt]
({"-i" init-opt
"--init" init-opt
"-e" eval-opt
"--eval" eval-opt} opt))
(defn- initialize
"Common initialize routine for repl, script, and null opts"
[args inits]
(in-ns 'user)
(set! *command-line-args* args)
(doseq [[opt arg] inits]
((init-dispatch opt) arg)))
(defn- repl-opt
"Start a repl with args and inits. Print greeting if no eval options were
present"
[[_ & args] inits]
(when-not (some #(= eval-opt (init-dispatch (first %))) inits)
(println "Clojure" (clojure-version)))
(repl :init #(initialize args inits))
(prn)
(System/exit 0))
(defn- script-opt
"Run a script from a file, resource, or standard in with args and inits"
[[path & args] inits]
(with-bindings
(initialize args inits)
(if (= path "-")
(load-reader *in*)
(load-script path))))
(defn- null-opt
"No repl or script opt present, just bind args and run inits"
[args inits]
(with-bindings
(initialize args inits)))
(defn- help-opt
"Print help text for main"
[_ _]
(println (:doc (meta (var main)))))
(defn- main-dispatch
"Returns the handler associated with a main option"
[opt]
(or
({"-r" repl-opt
"--repl" repl-opt
nil null-opt
"-h" help-opt
"--help" help-opt
"-?" help-opt} opt)
script-opt))
(defn- legacy-repl
"Called by the clojure.lang.Repl.main stub to run a repl with args
specified the old way"
[args]
(println "WARNING: clojure.lang.Repl is deprecated.
Instead, use clojure.main like this:
java -cp clojure.jar clojure.main -i init.clj -r args...")
(let [[inits [sep & args]] (split-with (complement #{"--"}) args)]
(repl-opt (concat ["-r"] args) (map vector (repeat "-i") inits))))
(defn- legacy-script
"Called by the clojure.lang.Script.main stub to run a script with args
specified the old way"
[args]
(println "WARNING: clojure.lang.Script is deprecated.
Instead, use clojure.main like this:
java -cp clojure.jar clojure.main -i init.clj script.clj args...")
(let [[inits [sep & args]] (split-with (complement #{"--"}) args)]
(null-opt args (map vector (repeat "-i") inits))))
(defn main
"Usage: java -cp clojure.jar clojure.main [init-opt*] [main-opt] [arg*]
With no options or args, runs an interactive Read-Eval-Print Loop
init options:
-i, --init path Load a file or resource
-e, --eval string Evaluate expressions in string; print non-nil values
main options:
-r, --repl Run a repl
path Run a script from from a file or resource
- Run a script from standard input
-h, -?, --help Print this help message and exit
operation:
- Establishes thread-local bindings for commonly set!-able vars
- Enters the user namespace
- Binds *command-line-args* to a seq of strings containing command line
args that appear after any main option
- Runs all init options in order
- Runs a repl or script if requested
The init options may be repeated and mixed freely, but must appear before
any main option. The appearance of any eval option before running a repl
suppresses the usual repl greeting message: \"Clojure ~(clojure-version)\".
Paths may be absolute or relative in the filesystem or relative to
classpath. Classpath-relative paths have prefix of @ or @/"
[& args]
(try
(if args
(loop [[opt arg & more :as args] args inits []]
(if (init-dispatch opt)
(recur more (conj inits [opt arg]))
((main-dispatch opt) args inits)))
(repl-opt nil nil))
(finally
(flush))))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/parallel.clj 0000664 0000000 0000000 00000021175 11575623476 0022655 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns ^{:doc "DEPRECATED Wrapper of the ForkJoin library (JSR-166)."
:author "Rich Hickey"}
clojure.parallel)
(alias 'parallel 'clojure.parallel)
(comment "
The parallel library wraps the ForkJoin library scheduled for inclusion in JDK 7:
http://gee.cs.oswego.edu/dl/concurrency-interest/index.html
You'll need jsr166y.jar in your classpath in order to use this
library. The basic idea is that Clojure collections, and most
efficiently vectors, can be turned into parallel arrays for use by
this library with the function par, although most of the functions
take collections and will call par if needed, so normally you will
only need to call par explicitly in order to attach bound/filter/map
ops. Parallel arrays support the attachment of bounds, filters and
mapping functions prior to realization/calculation, which happens as
the result of any of several operations on the
array (pvec/psort/pfilter-nils/pfilter-dupes). Rather than perform
composite operations in steps, as would normally be done with
sequences, maps and filters are instead attached and thus composed by
providing ops to par. Note that there is an order sensitivity to the
attachments - bounds precede filters precede mappings. All operations
then happen in parallel, using multiple threads and a sophisticated
work-stealing system supported by fork-join, either when the array is
realized, or to perform aggregate operations like preduce/pmin/pmax
etc. A parallel array can be realized into a Clojure vector using
pvec.
")
(import '(jsr166y.forkjoin ParallelArray ParallelArrayWithBounds ParallelArrayWithFilter
ParallelArrayWithMapping
Ops$Op Ops$BinaryOp Ops$Reducer Ops$Predicate Ops$BinaryPredicate
Ops$IntAndObjectPredicate Ops$IntAndObjectToObject))
(defn- op [f]
(proxy [Ops$Op] []
(op [x] (f x))))
(defn- binary-op [f]
(proxy [Ops$BinaryOp] []
(op [x y] (f x y))))
(defn- int-and-object-to-object [f]
(proxy [Ops$IntAndObjectToObject] []
(op [i x] (f x i))))
(defn- reducer [f]
(proxy [Ops$Reducer] []
(op [x y] (f x y))))
(defn- predicate [f]
(proxy [Ops$Predicate] []
(op [x] (boolean (f x)))))
(defn- binary-predicate [f]
(proxy [Ops$BinaryPredicate] []
(op [x y] (boolean (f x y)))))
(defn- int-and-object-predicate [f]
(proxy [Ops$IntAndObjectPredicate] []
(op [i x] (boolean (f x i)))))
(defn par
"Creates a parallel array from coll. ops, if supplied, perform
on-the-fly filtering or transformations during parallel realization
or calculation. ops form a chain, and bounds must precede filters,
must precede maps. ops must be a set of keyword value pairs of the
following forms:
:bound [start end]
Only elements from start (inclusive) to end (exclusive) will be
processed when the array is realized.
:filter pred
Filter preds remove elements from processing when the array is realized. pred
must be a function of one argument whose return will be processed
via boolean.
:filter-index pred2
pred2 must be a function of two arguments, which will be an element
of the collection and the corresponding index, whose return will be
processed via boolean.
:filter-with [pred2 coll2]
pred2 must be a function of two arguments, which will be
corresponding elements of the 2 collections.
:map f
Map fns will be used to transform elements when the array is
realized. f must be a function of one argument.
:map-index f2
f2 must be a function of two arguments, which will be an element of
the collection and the corresponding index.
:map-with [f2 coll2]
f2 must be a function of two arguments, which will be corresponding
elements of the 2 collections."
([coll]
(if (instance? ParallelArrayWithMapping coll)
coll
(. ParallelArray createUsingHandoff
(to-array coll)
(. ParallelArray defaultExecutor))))
([coll & ops]
(reduce (fn [pa [op args]]
(cond
(= op :bound) (. pa withBounds (args 0) (args 1))
(= op :filter) (. pa withFilter (predicate args))
(= op :filter-with) (. pa withFilter (binary-predicate (args 0)) (par (args 1)))
(= op :filter-index) (. pa withIndexedFilter (int-and-object-predicate args))
(= op :map) (. pa withMapping (parallel/op args))
(= op :map-with) (. pa withMapping (binary-op (args 0)) (par (args 1)))
(= op :map-index) (. pa withIndexedMapping (int-and-object-to-object args))
:else (throw (Exception. (str "Unsupported par op: " op)))))
(par coll)
(partition 2 ops))))
;;;;;;;;;;;;;;;;;;;;; aggregate operations ;;;;;;;;;;;;;;;;;;;;;;
(defn pany
"Returns some (random) element of the coll if it satisfies the bound/filter/map"
[coll]
(. (par coll) any))
(defn pmax
"Returns the maximum element, presuming Comparable elements, unless
a Comparator comp is supplied"
([coll] (. (par coll) max))
([coll comp] (. (par coll) max comp)))
(defn pmin
"Returns the minimum element, presuming Comparable elements, unless
a Comparator comp is supplied"
([coll] (. (par coll) min))
([coll comp] (. (par coll) min comp)))
(defn- summary-map [s]
{:min (.min s) :max (.max s) :size (.size s) :min-index (.indexOfMin s) :max-index (.indexOfMax s)})
(defn psummary
"Returns a map of summary statistics (min. max, size, min-index, max-index,
presuming Comparable elements, unless a Comparator comp is supplied"
([coll] (summary-map (. (par coll) summary)))
([coll comp] (summary-map (. (par coll) summary comp))))
(defn preduce
"Returns the reduction of the realized elements of coll
using function f. Note f will not necessarily be called
consecutively, and so must be commutative. Also note that
(f base an-element) might be performed many times, i.e. base is not
an initial value as with sequential reduce."
[f base coll]
(. (par coll) (reduce (reducer f) base)))
;;;;;;;;;;;;;;;;;;;;; collection-producing operations ;;;;;;;;;;;;;;;;;;;;;;
(defn- pa-to-vec [pa]
(vec (. pa getArray)))
(defn- pall
"Realizes a copy of the coll as a parallel array, with any bounds/filters/maps applied"
[coll]
(if (instance? ParallelArrayWithMapping coll)
(. coll all)
(par coll)))
(defn pvec
"Returns the realized contents of the parallel array pa as a Clojure vector"
[pa] (pa-to-vec (pall pa)))
(defn pdistinct
"Returns a parallel array of the distinct elements of coll"
[coll]
(pa-to-vec (. (pall coll) allUniqueElements)))
;this doesn't work, passes null to reducer?
(defn- pcumulate [coll f init]
(.. (pall coll) (precumulate (reducer f) init)))
(defn psort
"Returns a new vector consisting of the realized items in coll, sorted,
presuming Comparable elements, unless a Comparator comp is supplied"
([coll] (pa-to-vec (. (pall coll) sort)))
([coll comp] (pa-to-vec (. (pall coll) sort comp))))
(defn pfilter-nils
"Returns a vector containing the non-nil (realized) elements of coll"
[coll]
(pa-to-vec (. (pall coll) removeNulls)))
(defn pfilter-dupes
"Returns a vector containing the (realized) elements of coll,
without any consecutive duplicates"
[coll]
(pa-to-vec (. (pall coll) removeConsecutiveDuplicates)))
(comment
(load-file "src/parallel.clj")
(refer 'parallel)
(pdistinct [1 2 3 2 1])
;(pcumulate [1 2 3 2 1] + 0) ;broken, not exposed
(def a (make-array Object 1000000))
(dotimes i (count a)
(aset a i (rand-int i)))
(time (reduce + 0 a))
(time (preduce + 0 a))
(time (count (distinct a)))
(time (count (pdistinct a)))
(preduce + 0 [1 2 3 2 1])
(preduce + 0 (psort a))
(pvec (par [11 2 3 2] :filter-index (fn [x i] (> i x))))
(pvec (par [11 2 3 2] :filter-with [(fn [x y] (> y x)) [110 2 33 2]]))
(psummary ;or pvec/pmax etc
(par [11 2 3 2]
:filter-with [(fn [x y] (> y x))
[110 2 33 2]]
:map #(* % 2)))
(preduce + 0
(par [11 2 3 2]
:filter-with [< [110 2 33 2]]))
(time (reduce + 0 (map #(* % %) (range 1000000))))
(time (preduce + 0 (par (range 1000000) :map-index *)))
(def v (range 1000000))
(time (preduce + 0 (par v :map-index *)))
(time (preduce + 0 (par v :map #(* % %))))
(time (reduce + 0 (map #(* % %) v)))
) clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/pprint.clj 0000664 0000000 0000000 00000003733 11575623476 0022375 0 ustar 00root root 0000000 0000000 ;;; pprint.clj -- Pretty printer and Common Lisp compatible format function (cl-format) for Clojure
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; Author: Tom Faulhaber
;; April 3, 2009
(ns
^{:author "Tom Faulhaber",
:doc "A Pretty Printer for Clojure
clojure.pprint implements a flexible system for printing structured data
in a pleasing, easy-to-understand format. Basic use of the pretty printer is
simple, just call pprint instead of println. More advanced users can use
the building blocks provided to create custom output formats.
Out of the box, pprint supports a simple structured format for basic data
and a specialized format for Clojure source code. More advanced formats,
including formats that don't look like Clojure data at all like XML and
JSON, can be rendered by creating custom dispatch functions.
In addition to the pprint function, this module contains cl-format, a text
formatting function which is fully compatible with the format function in
Common Lisp. Because pretty printing directives are directly integrated with
cl-format, it supports very concise custom dispatch. It also provides
a more powerful alternative to Clojure's standard format function.
See documentation for pprint and cl-format for more information or
complete documentation on the the clojure web site on github.",
:added "1.2"}
clojure.pprint
(:refer-clojure :exclude (deftype)))
(load "pprint/utilities")
(load "pprint/column_writer")
(load "pprint/pretty_writer")
(load "pprint/pprint_base")
(load "pprint/cl_format")
(load "pprint/dispatch")
nil
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/pprint/ 0000775 0000000 0000000 00000000000 11575623476 0021675 5 ustar 00root root 0000000 0000000 clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/pprint/cl_format.clj 0000664 0000000 0000000 00000220524 11575623476 0024342 0 ustar 00root root 0000000 0000000 ;;; cl_format.clj -- part of the pretty printer for Clojure
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; Author: Tom Faulhaber
;; April 3, 2009
;; This module implements the Common Lisp compatible format function as documented
;; in "Common Lisp the Language, 2nd edition", Chapter 22 (available online at:
;; http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/clm/node200.html#SECTION002633000000000000000)
(in-ns 'clojure.pprint)
;;; Forward references
(declare compile-format)
(declare execute-format)
(declare init-navigator)
;;; End forward references
(defn cl-format
"An implementation of a Common Lisp compatible format function. cl-format formats its
arguments to an output stream or string based on the format control string given. It
supports sophisticated formatting of structured data.
Writer is an instance of java.io.Writer, true to output to *out* or nil to output
to a string, format-in is the format control string and the remaining arguments
are the data to be formatted.
The format control string is a string to be output with embedded 'format directives'
describing how to format the various arguments passed in.
If writer is nil, cl-format returns the formatted result string. Otherwise, cl-format
returns nil.
For example:
(let [results [46 38 22]]
(cl-format true \"There ~[are~;is~:;are~]~:* ~d result~:p: ~{~d~^, ~}~%\"
(count results) results))
Prints to *out*:
There are 3 results: 46, 38, 22
Detailed documentation on format control strings is available in the \"Common Lisp the
Language, 2nd edition\", Chapter 22 (available online at:
http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/clm/node200.html#SECTION002633000000000000000)
and in the Common Lisp HyperSpec at
http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm
"
{:added "1.2",
:see-also [["http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/clm/node200.html#SECTION002633000000000000000"
"Common Lisp the Language"]
["http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm"
"Common Lisp HyperSpec"]]}
[writer format-in & args]
(let [compiled-format (if (string? format-in) (compile-format format-in) format-in)
navigator (init-navigator args)]
(execute-format writer compiled-format navigator)))
(def ^{:private true} *format-str* nil)
(defn- format-error [message offset]
(let [full-message (str message \newline *format-str* \newline
(apply str (repeat offset \space)) "^" \newline)]
(throw (RuntimeException. full-message))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Argument navigators manage the argument list
;;; as the format statement moves through the list
;;; (possibly going forwards and backwards as it does so)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defstruct ^{:private true}
arg-navigator :seq :rest :pos )
(defn- init-navigator
"Create a new arg-navigator from the sequence with the position set to 0"
{:skip-wiki true}
[s]
(let [s (seq s)]
(struct arg-navigator s s 0)))
;; TODO call format-error with offset
(defn- next-arg [ navigator ]
(let [ rst (:rest navigator) ]
(if rst
[(first rst) (struct arg-navigator (:seq navigator ) (next rst) (inc (:pos navigator)))]
(throw (new Exception "Not enough arguments for format definition")))))
(defn- next-arg-or-nil [navigator]
(let [rst (:rest navigator)]
(if rst
[(first rst) (struct arg-navigator (:seq navigator ) (next rst) (inc (:pos navigator)))]
[nil navigator])))
;; Get an argument off the arg list and compile it if it's not already compiled
(defn- get-format-arg [navigator]
(let [[raw-format navigator] (next-arg navigator)
compiled-format (if (instance? String raw-format)
(compile-format raw-format)
raw-format)]
[compiled-format navigator]))
(declare relative-reposition)
(defn- absolute-reposition [navigator position]
(if (>= position (:pos navigator))
(relative-reposition navigator (- (:pos navigator) position))
(struct arg-navigator (:seq navigator) (drop position (:seq navigator)) position)))
(defn- relative-reposition [navigator position]
(let [newpos (+ (:pos navigator) position)]
(if (neg? position)
(absolute-reposition navigator newpos)
(struct arg-navigator (:seq navigator) (drop position (:rest navigator)) newpos))))
(defstruct ^{:private true}
compiled-directive :func :def :params :offset)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; When looking at the parameter list, we may need to manipulate
;;; the argument list as well (for 'V' and '#' parameter types).
;;; We hide all of this behind a function, but clients need to
;;; manage changing arg navigator
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TODO: validate parameters when they come from arg list
(defn- realize-parameter [[param [raw-val offset]] navigator]
(let [[real-param new-navigator]
(cond
(contains? #{ :at :colon } param) ;pass flags through unchanged - this really isn't necessary
[raw-val navigator]
(= raw-val :parameter-from-args)
(next-arg navigator)
(= raw-val :remaining-arg-count)
[(count (:rest navigator)) navigator]
true
[raw-val navigator])]
[[param [real-param offset]] new-navigator]))
(defn- realize-parameter-list [parameter-map navigator]
(let [[pairs new-navigator]
(map-passing-context realize-parameter navigator parameter-map)]
[(into {} pairs) new-navigator]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Functions that support individual directives
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Common handling code for ~A and ~S
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare opt-base-str)
(def ^{:private true}
special-radix-markers {2 "#b" 8 "#o", 16 "#x"})
(defn- format-simple-number [n]
(cond
(integer? n) (if (= *print-base* 10)
(str n (if *print-radix* "."))
(str
(if *print-radix* (or (get special-radix-markers *print-base*) (str "#" *print-base* "r")))
(opt-base-str *print-base* n)))
(ratio? n) (str
(if *print-radix* (or (get special-radix-markers *print-base*) (str "#" *print-base* "r")))
(opt-base-str *print-base* (.numerator n))
"/"
(opt-base-str *print-base* (.denominator n)))
:else nil))
(defn- format-ascii [print-func params arg-navigator offsets]
(let [ [arg arg-navigator] (next-arg arg-navigator)
^String base-output (or (format-simple-number arg) (print-func arg))
base-width (.length base-output)
min-width (+ base-width (:minpad params))
width (if (>= min-width (:mincol params))
min-width
(+ min-width
(* (+ (quot (- (:mincol params) min-width 1)
(:colinc params) )
1)
(:colinc params))))
chars (apply str (repeat (- width base-width) (:padchar params)))]
(if (:at params)
(print (str chars base-output))
(print (str base-output chars)))
arg-navigator))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Support for the integer directives ~D, ~X, ~O, ~B and some
;;; of ~R
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- integral?
"returns true if a number is actually an integer (that is, has no fractional part)"
[x]
(cond
(integer? x) true
(decimal? x) (>= (.ulp (.stripTrailingZeros (bigdec 0))) 1) ; true iff no fractional part
(float? x) (= x (Math/floor x))
(ratio? x) (let [^clojure.lang.Ratio r x]
(= 0 (rem (.numerator r) (.denominator r))))
:else false))
(defn- remainders
"Return the list of remainders (essentially the 'digits') of val in the given base"
[base val]
(reverse
(first
(consume #(if (pos? %)
[(rem % base) (quot % base)]
[nil nil])
val))))
;;; TODO: xlated-val does not seem to be used here.
(defn- base-str
"Return val as a string in the given base"
[base val]
(if (zero? val)
"0"
(let [xlated-val (cond
(float? val) (bigdec val)
(ratio? val) (let [^clojure.lang.Ratio r val]
(/ (.numerator r) (.denominator r)))
:else val)]
(apply str
(map
#(if (< % 10) (char (+ (int \0) %)) (char (+ (int \a) (- % 10))))
(remainders base val))))))
(def ^{:private true}
java-base-formats {8 "%o", 10 "%d", 16 "%x"})
(defn- opt-base-str
"Return val as a string in the given base, using clojure.core/format if supported
for improved performance"
[base val]
(let [format-str (get java-base-formats base)]
(if (and format-str (integer? val))
(clojure.core/format format-str val)
(base-str base val))))
(defn- group-by* [unit lis]
(reverse
(first
(consume (fn [x] [(seq (reverse (take unit x))) (seq (drop unit x))]) (reverse lis)))))
(defn- format-integer [base params arg-navigator offsets]
(let [[arg arg-navigator] (next-arg arg-navigator)]
(if (integral? arg)
(let [neg (neg? arg)
pos-arg (if neg (- arg) arg)
raw-str (opt-base-str base pos-arg)
group-str (if (:colon params)
(let [groups (map #(apply str %) (group-by* (:commainterval params) raw-str))
commas (repeat (count groups) (:commachar params))]
(apply str (next (interleave commas groups))))
raw-str)
^String signed-str (cond
neg (str "-" group-str)
(:at params) (str "+" group-str)
true group-str)
padded-str (if (< (.length signed-str) (:mincol params))
(str (apply str (repeat (- (:mincol params) (.length signed-str))
(:padchar params)))
signed-str)
signed-str)]
(print padded-str))
(format-ascii print-str {:mincol (:mincol params) :colinc 1 :minpad 0
:padchar (:padchar params) :at true}
(init-navigator [arg]) nil))
arg-navigator))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Support for english formats (~R and ~:R)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^{:private true}
english-cardinal-units
["zero" "one" "two" "three" "four" "five" "six" "seven" "eight" "nine"
"ten" "eleven" "twelve" "thirteen" "fourteen"
"fifteen" "sixteen" "seventeen" "eighteen" "nineteen"])
(def ^{:private true}
english-ordinal-units
["zeroth" "first" "second" "third" "fourth" "fifth" "sixth" "seventh" "eighth" "ninth"
"tenth" "eleventh" "twelfth" "thirteenth" "fourteenth"
"fifteenth" "sixteenth" "seventeenth" "eighteenth" "nineteenth"])
(def ^{:private true}
english-cardinal-tens
["" "" "twenty" "thirty" "forty" "fifty" "sixty" "seventy" "eighty" "ninety"])
(def ^{:private true}
english-ordinal-tens
["" "" "twentieth" "thirtieth" "fortieth" "fiftieth"
"sixtieth" "seventieth" "eightieth" "ninetieth"])
;; We use "short scale" for our units (see http://en.wikipedia.org/wiki/Long_and_short_scales)
;; Number names from http://www.jimloy.com/math/billion.htm
;; We follow the rules for writing numbers from the Blue Book
;; (http://www.grammarbook.com/numbers/numbers.asp)
(def ^{:private true}
english-scale-numbers
["" "thousand" "million" "billion" "trillion" "quadrillion" "quintillion"
"sextillion" "septillion" "octillion" "nonillion" "decillion"
"undecillion" "duodecillion" "tredecillion" "quattuordecillion"
"quindecillion" "sexdecillion" "septendecillion"
"octodecillion" "novemdecillion" "vigintillion"])
(defn- format-simple-cardinal
"Convert a number less than 1000 to a cardinal english string"
[num]
(let [hundreds (quot num 100)
tens (rem num 100)]
(str
(if (pos? hundreds) (str (nth english-cardinal-units hundreds) " hundred"))
(if (and (pos? hundreds) (pos? tens)) " ")
(if (pos? tens)
(if (< tens 20)
(nth english-cardinal-units tens)
(let [ten-digit (quot tens 10)
unit-digit (rem tens 10)]
(str
(if (pos? ten-digit) (nth english-cardinal-tens ten-digit))
(if (and (pos? ten-digit) (pos? unit-digit)) "-")
(if (pos? unit-digit) (nth english-cardinal-units unit-digit)))))))))
(defn- add-english-scales
"Take a sequence of parts, add scale numbers (e.g., million) and combine into a string
offset is a factor of 10^3 to multiply by"
[parts offset]
(let [cnt (count parts)]
(loop [acc []
pos (dec cnt)
this (first parts)
remainder (next parts)]
(if (nil? remainder)
(str (apply str (interpose ", " acc))
(if (and (not (empty? this)) (not (empty? acc))) ", ")
this
(if (and (not (empty? this)) (pos? (+ pos offset)))
(str " " (nth english-scale-numbers (+ pos offset)))))
(recur
(if (empty? this)
acc
(conj acc (str this " " (nth english-scale-numbers (+ pos offset)))))
(dec pos)
(first remainder)
(next remainder))))))
(defn- format-cardinal-english [params navigator offsets]
(let [[arg navigator] (next-arg navigator)]
(if (= 0 arg)
(print "zero")
(let [abs-arg (if (neg? arg) (- arg) arg) ; some numbers are too big for Math/abs
parts (remainders 1000 abs-arg)]
(if (<= (count parts) (count english-scale-numbers))
(let [parts-strs (map format-simple-cardinal parts)
full-str (add-english-scales parts-strs 0)]
(print (str (if (neg? arg) "minus ") full-str)))
(format-integer ;; for numbers > 10^63, we fall back on ~D
10
{ :mincol 0, :padchar \space, :commachar \, :commainterval 3, :colon true}
(init-navigator [arg])
{ :mincol 0, :padchar 0, :commachar 0 :commainterval 0}))))
navigator))
(defn- format-simple-ordinal
"Convert a number less than 1000 to a ordinal english string
Note this should only be used for the last one in the sequence"
[num]
(let [hundreds (quot num 100)
tens (rem num 100)]
(str
(if (pos? hundreds) (str (nth english-cardinal-units hundreds) " hundred"))
(if (and (pos? hundreds) (pos? tens)) " ")
(if (pos? tens)
(if (< tens 20)
(nth english-ordinal-units tens)
(let [ten-digit (quot tens 10)
unit-digit (rem tens 10)]
(if (and (pos? ten-digit) (not (pos? unit-digit)))
(nth english-ordinal-tens ten-digit)
(str
(if (pos? ten-digit) (nth english-cardinal-tens ten-digit))
(if (and (pos? ten-digit) (pos? unit-digit)) "-")
(if (pos? unit-digit) (nth english-ordinal-units unit-digit))))))
(if (pos? hundreds) "th")))))
(defn- format-ordinal-english [params navigator offsets]
(let [[arg navigator] (next-arg navigator)]
(if (= 0 arg)
(print "zeroth")
(let [abs-arg (if (neg? arg) (- arg) arg) ; some numbers are too big for Math/abs
parts (remainders 1000 abs-arg)]
(if (<= (count parts) (count english-scale-numbers))
(let [parts-strs (map format-simple-cardinal (drop-last parts))
head-str (add-english-scales parts-strs 1)
tail-str (format-simple-ordinal (last parts))]
(print (str (if (neg? arg) "minus ")
(cond
(and (not (empty? head-str)) (not (empty? tail-str)))
(str head-str ", " tail-str)
(not (empty? head-str)) (str head-str "th")
:else tail-str))))
(do (format-integer ;; for numbers > 10^63, we fall back on ~D
10
{ :mincol 0, :padchar \space, :commachar \, :commainterval 3, :colon true}
(init-navigator [arg])
{ :mincol 0, :padchar 0, :commachar 0 :commainterval 0})
(let [low-two-digits (rem arg 100)
not-teens (or (< 11 low-two-digits) (> 19 low-two-digits))
low-digit (rem low-two-digits 10)]
(print (cond
(and (= low-digit 1) not-teens) "st"
(and (= low-digit 2) not-teens) "nd"
(and (= low-digit 3) not-teens) "rd"
:else "th")))))))
navigator))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Support for roman numeral formats (~@R and ~@:R)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^{:private true}
old-roman-table
[[ "I" "II" "III" "IIII" "V" "VI" "VII" "VIII" "VIIII"]
[ "X" "XX" "XXX" "XXXX" "L" "LX" "LXX" "LXXX" "LXXXX"]
[ "C" "CC" "CCC" "CCCC" "D" "DC" "DCC" "DCCC" "DCCCC"]
[ "M" "MM" "MMM"]])
(def ^{:private true}
new-roman-table
[[ "I" "II" "III" "IV" "V" "VI" "VII" "VIII" "IX"]
[ "X" "XX" "XXX" "XL" "L" "LX" "LXX" "LXXX" "XC"]
[ "C" "CC" "CCC" "CD" "D" "DC" "DCC" "DCCC" "CM"]
[ "M" "MM" "MMM"]])
(defn- format-roman
"Format a roman numeral using the specified look-up table"
[table params navigator offsets]
(let [[arg navigator] (next-arg navigator)]
(if (and (number? arg) (> arg 0) (< arg 4000))
(let [digits (remainders 10 arg)]
(loop [acc []
pos (dec (count digits))
digits digits]
(if (empty? digits)
(print (apply str acc))
(let [digit (first digits)]
(recur (if (= 0 digit)
acc
(conj acc (nth (nth table pos) (dec digit))))
(dec pos)
(next digits))))))
(format-integer ;; for anything <= 0 or > 3999, we fall back on ~D
10
{ :mincol 0, :padchar \space, :commachar \, :commainterval 3, :colon true}
(init-navigator [arg])
{ :mincol 0, :padchar 0, :commachar 0 :commainterval 0}))
navigator))
(defn- format-old-roman [params navigator offsets]
(format-roman old-roman-table params navigator offsets))
(defn- format-new-roman [params navigator offsets]
(format-roman new-roman-table params navigator offsets))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Support for character formats (~C)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^{:private true}
special-chars { 8 "Backspace", 9 "Tab", 10 "Newline", 13 "Return", 32 "Space"})
(defn- pretty-character [params navigator offsets]
(let [[c navigator] (next-arg navigator)
as-int (int c)
base-char (bit-and as-int 127)
meta (bit-and as-int 128)
special (get special-chars base-char)]
(if (> meta 0) (print "Meta-"))
(print (cond
special special
(< base-char 32) (str "Control-" (char (+ base-char 64)))
(= base-char 127) "Control-?"
:else (char base-char)))
navigator))
(defn- readable-character [params navigator offsets]
(let [[c navigator] (next-arg navigator)]
(condp = (:char-format params)
\o (cl-format true "\\o~3,'0o" (int c))
\u (cl-format true "\\u~4,'0x" (int c))
nil (pr c))
navigator))
(defn- plain-character [params navigator offsets]
(let [[char navigator] (next-arg navigator)]
(print char)
navigator))
;; Check to see if a result is an abort (~^) construct
;; TODO: move these funcs somewhere more appropriate
(defn- abort? [context]
(let [token (first context)]
(or (= :up-arrow token) (= :colon-up-arrow token))))
;; Handle the execution of "sub-clauses" in bracket constructions
(defn- execute-sub-format [format args base-args]
(second
(map-passing-context
(fn [element context]
(if (abort? context)
[nil context] ; just keep passing it along
(let [[params args] (realize-parameter-list (:params element) context)
[params offsets] (unzip-map params)
params (assoc params :base-args base-args)]
[nil (apply (:func element) [params args offsets])])))
args
format)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Support for real number formats
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TODO - return exponent as int to eliminate double conversion
(defn- float-parts-base
"Produce string parts for the mantissa (normalized 1-9) and exponent"
[^Object f]
(let [^String s (.toLowerCase (.toString f))
exploc (.indexOf s (int \e))]
(if (neg? exploc)
(let [dotloc (.indexOf s (int \.))]
(if (neg? dotloc)
[s (str (dec (count s)))]
[(str (subs s 0 dotloc) (subs s (inc dotloc))) (str (dec dotloc))]))
[(str (subs s 0 1) (subs s 2 exploc)) (subs s (inc exploc))])))
(defn- float-parts
"Take care of leading and trailing zeros in decomposed floats"
[f]
(let [[m ^String e] (float-parts-base f)
m1 (rtrim m \0)
m2 (ltrim m1 \0)
delta (- (count m1) (count m2))
^String e (if (and (pos? (count e)) (= (nth e 0) \+)) (subs e 1) e)]
(if (empty? m2)
["0" 0]
[m2 (- (Integer/valueOf e) delta)])))
(defn- round-str [m e d w]
(if (or d w)
(let [len (count m)
round-pos (if d (+ e d 1))
round-pos (if (and w (< (inc e) (dec w))
(or (nil? round-pos) (< (dec w) round-pos)))
(dec w)
round-pos)
[m1 e1 round-pos len] (if (= round-pos 0)
[(str "0" m) (inc e) 1 (inc len)]
[m e round-pos len])]
(if round-pos
(if (neg? round-pos)
["0" 0 false]
(if (> len round-pos)
(let [round-char (nth m1 round-pos)
^String result (subs m1 0 round-pos)]
(if (>= (int round-char) (int \5))
(let [result-val (Integer/valueOf result)
leading-zeros (subs result 0 (min (prefix-count result \0) (- round-pos 1)))
round-up-result (str leading-zeros
(String/valueOf (+ result-val
(if (neg? result-val) -1 1))))
expanded (> (count round-up-result) (count result))]
[round-up-result e1 expanded])
[result e1 false]))
[m e false]))
[m e false]))
[m e false]))
(defn- expand-fixed [m e d]
(let [m1 (if (neg? e) (str (apply str (repeat (dec (- e)) \0)) m) m)
len (count m1)
target-len (if d (+ e d 1) (inc e))]
(if (< len target-len)
(str m1 (apply str (repeat (- target-len len) \0)))
m1)))
(defn- insert-decimal
"Insert the decimal point at the right spot in the number to match an exponent"
[m e]
(if (neg? e)
(str "." m)
(let [loc (inc e)]
(str (subs m 0 loc) "." (subs m loc)))))
(defn- get-fixed [m e d]
(insert-decimal (expand-fixed m e d) e))
(defn- insert-scaled-decimal
"Insert the decimal point at the right spot in the number to match an exponent"
[m k]
(if (neg? k)
(str "." m)
(str (subs m 0 k) "." (subs m k))))
;; the function to render ~F directives
;; TODO: support rationals. Back off to ~D/~A is the appropriate cases
(defn- fixed-float [params navigator offsets]
(let [w (:w params)
d (:d params)
[arg navigator] (next-arg navigator)
[sign abs] (if (neg? arg) ["-" (- arg)] ["+" arg])
[mantissa exp] (float-parts abs)
scaled-exp (+ exp (:k params))
add-sign (or (:at params) (neg? arg))
append-zero (and (not d) (<= (dec (count mantissa)) scaled-exp))
[rounded-mantissa scaled-exp expanded] (round-str mantissa scaled-exp
d (if w (- w (if add-sign 1 0))))
fixed-repr (get-fixed rounded-mantissa (if expanded (inc scaled-exp) scaled-exp) d)
prepend-zero (= (first fixed-repr) \.)]
(if w
(let [len (count fixed-repr)
signed-len (if add-sign (inc len) len)
prepend-zero (and prepend-zero (not (>= signed-len w)))
append-zero (and append-zero (not (>= signed-len w)))
full-len (if (or prepend-zero append-zero)
(inc signed-len)
signed-len)]
(if (and (> full-len w) (:overflowchar params))
(print (apply str (repeat w (:overflowchar params))))
(print (str
(apply str (repeat (- w full-len) (:padchar params)))
(if add-sign sign)
(if prepend-zero "0")
fixed-repr
(if append-zero "0")))))
(print (str
(if add-sign sign)
(if prepend-zero "0")
fixed-repr
(if append-zero "0"))))
navigator))
;; the function to render ~E directives
;; TODO: support rationals. Back off to ~D/~A is the appropriate cases
;; TODO: define ~E representation for Infinity
(defn- exponential-float [params navigator offsets]
(let [[arg navigator] (next-arg navigator)]
(loop [[mantissa exp] (float-parts (if (neg? arg) (- arg) arg))]
(let [w (:w params)
d (:d params)
e (:e params)
k (:k params)
expchar (or (:exponentchar params) \E)
add-sign (or (:at params) (neg? arg))
prepend-zero (<= k 0)
^Integer scaled-exp (- exp (dec k))
scaled-exp-str (str (Math/abs scaled-exp))
scaled-exp-str (str expchar (if (neg? scaled-exp) \- \+)
(if e (apply str
(repeat
(- e
(count scaled-exp-str))
\0)))
scaled-exp-str)
exp-width (count scaled-exp-str)
base-mantissa-width (count mantissa)
scaled-mantissa (str (apply str (repeat (- k) \0))
mantissa
(if d
(apply str
(repeat
(- d (dec base-mantissa-width)
(if (neg? k) (- k) 0)) \0))))
w-mantissa (if w (- w exp-width))
[rounded-mantissa _ incr-exp] (round-str
scaled-mantissa 0
(cond
(= k 0) (dec d)
(pos? k) d
(neg? k) (dec d))
(if w-mantissa
(- w-mantissa (if add-sign 1 0))))
full-mantissa (insert-scaled-decimal rounded-mantissa k)
append-zero (and (= k (count rounded-mantissa)) (nil? d))]
(if (not incr-exp)
(if w
(let [len (+ (count full-mantissa) exp-width)
signed-len (if add-sign (inc len) len)
prepend-zero (and prepend-zero (not (= signed-len w)))
full-len (if prepend-zero (inc signed-len) signed-len)
append-zero (and append-zero (< full-len w))]
(if (and (or (> full-len w) (and e (> (- exp-width 2) e)))
(:overflowchar params))
(print (apply str (repeat w (:overflowchar params))))
(print (str
(apply str
(repeat
(- w full-len (if append-zero 1 0) )
(:padchar params)))
(if add-sign (if (neg? arg) \- \+))
(if prepend-zero "0")
full-mantissa
(if append-zero "0")
scaled-exp-str))))
(print (str
(if add-sign (if (neg? arg) \- \+))
(if prepend-zero "0")
full-mantissa
(if append-zero "0")
scaled-exp-str)))
(recur [rounded-mantissa (inc exp)]))))
navigator))
;; the function to render ~G directives
;; This just figures out whether to pass the request off to ~F or ~E based
;; on the algorithm in CLtL.
;; TODO: support rationals. Back off to ~D/~A is the appropriate cases
;; TODO: refactor so that float-parts isn't called twice
(defn- general-float [params navigator offsets]
(let [[arg _] (next-arg navigator)
[mantissa exp] (float-parts (if (neg? arg) (- arg) arg))
w (:w params)
d (:d params)
e (:e params)
n (if (= arg 0.0) 0 (inc exp))
ee (if e (+ e 2) 4)
ww (if w (- w ee))
d (if d d (max (count mantissa) (min n 7)))
dd (- d n)]
(if (<= 0 dd d)
(let [navigator (fixed-float {:w ww, :d dd, :k 0,
:overflowchar (:overflowchar params),
:padchar (:padchar params), :at (:at params)}
navigator offsets)]
(print (apply str (repeat ee \space)))
navigator)
(exponential-float params navigator offsets))))
;; the function to render ~$ directives
;; TODO: support rationals. Back off to ~D/~A is the appropriate cases
(defn- dollar-float [params navigator offsets]
(let [[^Double arg navigator] (next-arg navigator)
[mantissa exp] (float-parts (Math/abs arg))
d (:d params) ; digits after the decimal
n (:n params) ; minimum digits before the decimal
w (:w params) ; minimum field width
add-sign (or (:at params) (neg? arg))
[rounded-mantissa scaled-exp expanded] (round-str mantissa exp d nil)
^String fixed-repr (get-fixed rounded-mantissa (if expanded (inc scaled-exp) scaled-exp) d)
full-repr (str (apply str (repeat (- n (.indexOf fixed-repr (int \.))) \0)) fixed-repr)
full-len (+ (count full-repr) (if add-sign 1 0))]
(print (str
(if (and (:colon params) add-sign) (if (neg? arg) \- \+))
(apply str (repeat (- w full-len) (:padchar params)))
(if (and (not (:colon params)) add-sign) (if (neg? arg) \- \+))
full-repr))
navigator))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Support for the '~[...~]' conditional construct in its
;;; different flavors
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ~[...~] without any modifiers chooses one of the clauses based on the param or
;; next argument
;; TODO check arg is positive int
(defn- choice-conditional [params arg-navigator offsets]
(let [arg (:selector params)
[arg navigator] (if arg [arg arg-navigator] (next-arg arg-navigator))
clauses (:clauses params)
clause (if (or (neg? arg) (>= arg (count clauses)))
(first (:else params))
(nth clauses arg))]
(if clause
(execute-sub-format clause navigator (:base-args params))
navigator)))
;; ~:[...~] with the colon reads the next argument treating it as a truth value
(defn- boolean-conditional [params arg-navigator offsets]
(let [[arg navigator] (next-arg arg-navigator)
clauses (:clauses params)
clause (if arg
(second clauses)
(first clauses))]
(if clause
(execute-sub-format clause navigator (:base-args params))
navigator)))
;; ~@[...~] with the at sign executes the conditional if the next arg is not
;; nil/false without consuming the arg
(defn- check-arg-conditional [params arg-navigator offsets]
(let [[arg navigator] (next-arg arg-navigator)
clauses (:clauses params)
clause (if arg (first clauses))]
(if arg
(if clause
(execute-sub-format clause arg-navigator (:base-args params))
arg-navigator)
navigator)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Support for the '~{...~}' iteration construct in its
;;; different flavors
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ~{...~} without any modifiers uses the next argument as an argument list that
;; is consumed by all the iterations
(defn- iterate-sublist [params navigator offsets]
(let [max-count (:max-iterations params)
param-clause (first (:clauses params))
[clause navigator] (if (empty? param-clause)
(get-format-arg navigator)
[param-clause navigator])
[arg-list navigator] (next-arg navigator)
args (init-navigator arg-list)]
(loop [count 0
args args
last-pos -1]
(if (and (not max-count) (= (:pos args) last-pos) (> count 1))
;; TODO get the offset in here and call format exception
(throw (RuntimeException. "%{ construct not consuming any arguments: Infinite loop!")))
(if (or (and (empty? (:rest args))
(or (not (:colon (:right-params params))) (> count 0)))
(and max-count (>= count max-count)))
navigator
(let [iter-result (execute-sub-format clause args (:base-args params))]
(if (= :up-arrow (first iter-result))
navigator
(recur (inc count) iter-result (:pos args))))))))
;; ~:{...~} with the colon treats the next argument as a list of sublists. Each of the
;; sublists is used as the arglist for a single iteration.
(defn- iterate-list-of-sublists [params navigator offsets]
(let [max-count (:max-iterations params)
param-clause (first (:clauses params))
[clause navigator] (if (empty? param-clause)
(get-format-arg navigator)
[param-clause navigator])
[arg-list navigator] (next-arg navigator)]
(loop [count 0
arg-list arg-list]
(if (or (and (empty? arg-list)
(or (not (:colon (:right-params params))) (> count 0)))
(and max-count (>= count max-count)))
navigator
(let [iter-result (execute-sub-format
clause
(init-navigator (first arg-list))
(init-navigator (next arg-list)))]
(if (= :colon-up-arrow (first iter-result))
navigator
(recur (inc count) (next arg-list))))))))
;; ~@{...~} with the at sign uses the main argument list as the arguments to the iterations
;; is consumed by all the iterations
(defn- iterate-main-list [params navigator offsets]
(let [max-count (:max-iterations params)
param-clause (first (:clauses params))
[clause navigator] (if (empty? param-clause)
(get-format-arg navigator)
[param-clause navigator])]
(loop [count 0
navigator navigator
last-pos -1]
(if (and (not max-count) (= (:pos navigator) last-pos) (> count 1))
;; TODO get the offset in here and call format exception
(throw (RuntimeException. "%@{ construct not consuming any arguments: Infinite loop!")))
(if (or (and (empty? (:rest navigator))
(or (not (:colon (:right-params params))) (> count 0)))
(and max-count (>= count max-count)))
navigator
(let [iter-result (execute-sub-format clause navigator (:base-args params))]
(if (= :up-arrow (first iter-result))
(second iter-result)
(recur
(inc count) iter-result (:pos navigator))))))))
;; ~@:{...~} with both colon and at sign uses the main argument list as a set of sublists, one
;; of which is consumed with each iteration
(defn- iterate-main-sublists [params navigator offsets]
(let [max-count (:max-iterations params)
param-clause (first (:clauses params))
[clause navigator] (if (empty? param-clause)
(get-format-arg navigator)
[param-clause navigator])
]
(loop [count 0
navigator navigator]
(if (or (and (empty? (:rest navigator))
(or (not (:colon (:right-params params))) (> count 0)))
(and max-count (>= count max-count)))
navigator
(let [[sublist navigator] (next-arg-or-nil navigator)
iter-result (execute-sub-format clause (init-navigator sublist) navigator)]
(if (= :colon-up-arrow (first iter-result))
navigator
(recur (inc count) navigator)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; The '~< directive has two completely different meanings
;;; in the '~<...~>' form it does justification, but with
;;; ~<...~:>' it represents the logical block operation of the
;;; pretty printer.
;;;
;;; Unfortunately, the current architecture decides what function
;;; to call at form parsing time before the sub-clauses have been
;;; folded, so it is left to run-time to make the decision.
;;;
;;; TODO: make it possible to make these decisions at compile-time.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare format-logical-block)
(declare justify-clauses)
(defn- logical-block-or-justify [params navigator offsets]
(if (:colon (:right-params params))
(format-logical-block params navigator offsets)
(justify-clauses params navigator offsets)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Support for the '~<...~>' justification directive
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- render-clauses [clauses navigator base-navigator]
(loop [clauses clauses
acc []
navigator navigator]
(if (empty? clauses)
[acc navigator]
(let [clause (first clauses)
[iter-result result-str] (binding [*out* (java.io.StringWriter.)]
[(execute-sub-format clause navigator base-navigator)
(.toString *out*)])]
(if (= :up-arrow (first iter-result))
[acc (second iter-result)]
(recur (next clauses) (conj acc result-str) iter-result))))))
;; TODO support for ~:; constructions
(defn- justify-clauses [params navigator offsets]
(let [[[eol-str] new-navigator] (when-let [else (:else params)]
(render-clauses else navigator (:base-args params)))
navigator (or new-navigator navigator)
[else-params new-navigator] (when-let [p (:else-params params)]
(realize-parameter-list p navigator))
navigator (or new-navigator navigator)
min-remaining (or (first (:min-remaining else-params)) 0)
max-columns (or (first (:max-columns else-params))
(get-max-column *out*))
clauses (:clauses params)
[strs navigator] (render-clauses clauses navigator (:base-args params))
slots (max 1
(+ (dec (count strs)) (if (:colon params) 1 0) (if (:at params) 1 0)))
chars (reduce + (map count strs))
mincol (:mincol params)
minpad (:minpad params)
colinc (:colinc params)
minout (+ chars (* slots minpad))
result-columns (if (<= minout mincol)
mincol
(+ mincol (* colinc
(+ 1 (quot (- minout mincol 1) colinc)))))
total-pad (- result-columns chars)
pad (max minpad (quot total-pad slots))
extra-pad (- total-pad (* pad slots))
pad-str (apply str (repeat pad (:padchar params)))]
(if (and eol-str (> (+ (get-column (:base @@*out*)) min-remaining result-columns)
max-columns))
(print eol-str))
(loop [slots slots
extra-pad extra-pad
strs strs
pad-only (or (:colon params)
(and (= (count strs) 1) (not (:at params))))]
(if (seq strs)
(do
(print (str (if (not pad-only) (first strs))
(if (or pad-only (next strs) (:at params)) pad-str)
(if (pos? extra-pad) (:padchar params))))
(recur
(dec slots)
(dec extra-pad)
(if pad-only strs (next strs))
false))))
navigator))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Support for case modification with ~(...~).
;;; We do this by wrapping the underlying writer with
;;; a special writer to do the appropriate modification. This
;;; allows us to support arbitrary-sized output and sources
;;; that may block.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- downcase-writer
"Returns a proxy that wraps writer, converting all characters to lower case"
[^java.io.Writer writer]
(proxy [java.io.Writer] []
(close [] (.close writer))
(flush [] (.flush writer))
(write ([^chars cbuf ^Integer off ^Integer len]
(.write writer cbuf off len))
([x]
(condp = (class x)
String
(let [s ^String x]
(.write writer (.toLowerCase s)))
Integer
(let [c ^Character x]
(.write writer (int (Character/toLowerCase (char c))))))))))
(defn- upcase-writer
"Returns a proxy that wraps writer, converting all characters to upper case"
[^java.io.Writer writer]
(proxy [java.io.Writer] []
(close [] (.close writer))
(flush [] (.flush writer))
(write ([^chars cbuf ^Integer off ^Integer len]
(.write writer cbuf off len))
([x]
(condp = (class x)
String
(let [s ^String x]
(.write writer (.toUpperCase s)))
Integer
(let [c ^Character x]
(.write writer (int (Character/toUpperCase (char c))))))))))
(defn- capitalize-string
"Capitalizes the words in a string. If first? is false, don't capitalize the
first character of the string even if it's a letter."
[s first?]
(let [^Character f (first s)
s (if (and first? f (Character/isLetter f))
(str (Character/toUpperCase f) (subs s 1))
s)]
(apply str
(first
(consume
(fn [s]
(if (empty? s)
[nil nil]
(let [m (re-matcher #"\W\w" s)
match (re-find m)
offset (and match (inc (.start m)))]
(if offset
[(str (subs s 0 offset)
(Character/toUpperCase ^Character (nth s offset)))
(subs s (inc offset))]
[s nil]))))
s)))))
(defn- capitalize-word-writer
"Returns a proxy that wraps writer, captializing all words"
[^java.io.Writer writer]
(let [last-was-whitespace? (ref true)]
(proxy [java.io.Writer] []
(close [] (.close writer))
(flush [] (.flush writer))
(write
([^chars cbuf ^Integer off ^Integer len]
(.write writer cbuf off len))
([x]
(condp = (class x)
String
(let [s ^String x]
(.write writer
^String (capitalize-string (.toLowerCase s) @last-was-whitespace?))
(dosync
(ref-set last-was-whitespace?
(Character/isWhitespace
^Character (nth s (dec (count s)))))))
Integer
(let [c (char x)]
(let [mod-c (if @last-was-whitespace? (Character/toUpperCase ^Character (char x)) c)]
(.write writer (int mod-c))
(dosync (ref-set last-was-whitespace? (Character/isWhitespace ^Character (char x))))))))))))
(defn- init-cap-writer
"Returns a proxy that wraps writer, capitalizing the first word"
[^java.io.Writer writer]
(let [capped (ref false)]
(proxy [java.io.Writer] []
(close [] (.close writer))
(flush [] (.flush writer))
(write ([^chars cbuf ^Integer off ^Integer len]
(.write writer cbuf off len))
([x]
(condp = (class x)
String
(let [s (.toLowerCase ^String x)]
(if (not @capped)
(let [m (re-matcher #"\S" s)
match (re-find m)
offset (and match (.start m))]
(if offset
(do (.write writer
(str (subs s 0 offset)
(Character/toUpperCase ^Character (nth s offset))
(.toLowerCase ^String (subs s (inc offset)))))
(dosync (ref-set capped true)))
(.write writer s)))
(.write writer (.toLowerCase s))))
Integer
(let [c ^Character (char x)]
(if (and (not @capped) (Character/isLetter c))
(do
(dosync (ref-set capped true))
(.write writer (int (Character/toUpperCase c))))
(.write writer (int (Character/toLowerCase c)))))))))))
(defn- modify-case [make-writer params navigator offsets]
(let [clause (first (:clauses params))]
(binding [*out* (make-writer *out*)]
(execute-sub-format clause navigator (:base-args params)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; If necessary, wrap the writer in a PrettyWriter object
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn get-pretty-writer
"Returns the java.io.Writer passed in wrapped in a pretty writer proxy, unless it's
already a pretty writer. Generally, it is unneccesary to call this function, since pprint,
write, and cl-format all call it if they need to. However if you want the state to be
preserved across calls, you will want to wrap them with this.
For example, when you want to generate column-aware output with multiple calls to cl-format,
do it like in this example:
(defn print-table [aseq column-width]
(binding [*out* (get-pretty-writer *out*)]
(doseq [row aseq]
(doseq [col row]
(cl-format true \"~4D~7,vT\" col column-width))
(prn))))
Now when you run:
user> (print-table (map #(vector % (* % %) (* % % %)) (range 1 11)) 8)
It prints a table of squares and cubes for the numbers from 1 to 10:
1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000"
{:added "1.2"}
[writer]
(if (pretty-writer? writer)
writer
(pretty-writer writer *print-right-margin* *print-miser-width*)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Support for column-aware operations ~&, ~T
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn fresh-line
"Make a newline if *out* is not already at the beginning of the line. If *out* is
not a pretty writer (which keeps track of columns), this function always outputs a newline."
{:added "1.2"}
[]
(if (instance? clojure.lang.IDeref *out*)
(if (not (= 0 (get-column (:base @@*out*))))
(prn))
(prn)))
(defn- absolute-tabulation [params navigator offsets]
(let [colnum (:colnum params)
colinc (:colinc params)
current (get-column (:base @@*out*))
space-count (cond
(< current colnum) (- colnum current)
(= colinc 0) 0
:else (- colinc (rem (- current colnum) colinc)))]
(print (apply str (repeat space-count \space))))
navigator)
(defn- relative-tabulation [params navigator offsets]
(let [colrel (:colnum params)
colinc (:colinc params)
start-col (+ colrel (get-column (:base @@*out*)))
offset (if (pos? colinc) (rem start-col colinc) 0)
space-count (+ colrel (if (= 0 offset) 0 (- colinc offset)))]
(print (apply str (repeat space-count \space))))
navigator)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Support for accessing the pretty printer from a format
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TODO: support ~@; per-line-prefix separator
;; TODO: get the whole format wrapped so we can start the lb at any column
(defn- format-logical-block [params navigator offsets]
(let [clauses (:clauses params)
clause-count (count clauses)
prefix (cond
(> clause-count 1) (:string (:params (first (first clauses))))
(:colon params) "(")
body (nth clauses (if (> clause-count 1) 1 0))
suffix (cond
(> clause-count 2) (:string (:params (first (nth clauses 2))))
(:colon params) ")")
[arg navigator] (next-arg navigator)]
(pprint-logical-block :prefix prefix :suffix suffix
(execute-sub-format
body
(init-navigator arg)
(:base-args params)))
navigator))
(defn- set-indent [params navigator offsets]
(let [relative-to (if (:colon params) :current :block)]
(pprint-indent relative-to (:n params))
navigator))
;;; TODO: support ~:T section options for ~T
(defn- conditional-newline [params navigator offsets]
(let [kind (if (:colon params)
(if (:at params) :mandatory :fill)
(if (:at params) :miser :linear))]
(pprint-newline kind)
navigator))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; The table of directives we support, each with its params,
;;; properties, and the compilation function
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; We start with a couple of helpers
(defn- process-directive-table-element [ [ char params flags bracket-info & generator-fn ] ]
[char,
{:directive char,
:params `(array-map ~@params),
:flags flags,
:bracket-info bracket-info,
:generator-fn (concat '(fn [ params offset]) generator-fn) }])
(defmacro ^{:private true}
defdirectives
[ & directives ]
`(def ^{:private true}
directive-table (hash-map ~@(mapcat process-directive-table-element directives))))
(defdirectives
(\A
[ :mincol [0 Integer] :colinc [1 Integer] :minpad [0 Integer] :padchar [\space Character] ]
#{ :at :colon :both} {}
#(format-ascii print-str %1 %2 %3))
(\S
[ :mincol [0 Integer] :colinc [1 Integer] :minpad [0 Integer] :padchar [\space Character] ]
#{ :at :colon :both} {}
#(format-ascii pr-str %1 %2 %3))
(\D
[ :mincol [0 Integer] :padchar [\space Character] :commachar [\, Character]
:commainterval [ 3 Integer]]
#{ :at :colon :both } {}
#(format-integer 10 %1 %2 %3))
(\B
[ :mincol [0 Integer] :padchar [\space Character] :commachar [\, Character]
:commainterval [ 3 Integer]]
#{ :at :colon :both } {}
#(format-integer 2 %1 %2 %3))
(\O
[ :mincol [0 Integer] :padchar [\space Character] :commachar [\, Character]
:commainterval [ 3 Integer]]
#{ :at :colon :both } {}
#(format-integer 8 %1 %2 %3))
(\X
[ :mincol [0 Integer] :padchar [\space Character] :commachar [\, Character]
:commainterval [ 3 Integer]]
#{ :at :colon :both } {}
#(format-integer 16 %1 %2 %3))
(\R
[:base [nil Integer] :mincol [0 Integer] :padchar [\space Character] :commachar [\, Character]
:commainterval [ 3 Integer]]
#{ :at :colon :both } {}
(do
(cond ; ~R is overloaded with bizareness
(first (:base params)) #(format-integer (:base %1) %1 %2 %3)
(and (:at params) (:colon params)) #(format-old-roman %1 %2 %3)
(:at params) #(format-new-roman %1 %2 %3)
(:colon params) #(format-ordinal-english %1 %2 %3)
true #(format-cardinal-english %1 %2 %3))))
(\P
[ ]
#{ :at :colon :both } {}
(fn [params navigator offsets]
(let [navigator (if (:colon params) (relative-reposition navigator -1) navigator)
strs (if (:at params) ["y" "ies"] ["" "s"])
[arg navigator] (next-arg navigator)]
(print (if (= arg 1) (first strs) (second strs)))
navigator)))
(\C
[:char-format [nil Character]]
#{ :at :colon :both } {}
(cond
(:colon params) pretty-character
(:at params) readable-character
:else plain-character))
(\F
[ :w [nil Integer] :d [nil Integer] :k [0 Integer] :overflowchar [nil Character]
:padchar [\space Character] ]
#{ :at } {}
fixed-float)
(\E
[ :w [nil Integer] :d [nil Integer] :e [nil Integer] :k [1 Integer]
:overflowchar [nil Character] :padchar [\space Character]
:exponentchar [nil Character] ]
#{ :at } {}
exponential-float)
(\G
[ :w [nil Integer] :d [nil Integer] :e [nil Integer] :k [1 Integer]
:overflowchar [nil Character] :padchar [\space Character]
:exponentchar [nil Character] ]
#{ :at } {}
general-float)
(\$
[ :d [2 Integer] :n [1 Integer] :w [0 Integer] :padchar [\space Character]]
#{ :at :colon :both} {}
dollar-float)
(\%
[ :count [1 Integer] ]
#{ } {}
(fn [params arg-navigator offsets]
(dotimes [i (:count params)]
(prn))
arg-navigator))
(\&
[ :count [1 Integer] ]
#{ :pretty } {}
(fn [params arg-navigator offsets]
(let [cnt (:count params)]
(if (pos? cnt) (fresh-line))
(dotimes [i (dec cnt)]
(prn)))
arg-navigator))
(\|
[ :count [1 Integer] ]
#{ } {}
(fn [params arg-navigator offsets]
(dotimes [i (:count params)]
(print \formfeed))
arg-navigator))
(\~
[ :n [1 Integer] ]
#{ } {}
(fn [params arg-navigator offsets]
(let [n (:n params)]
(print (apply str (repeat n \~)))
arg-navigator)))
(\newline ;; Whitespace supression is handled in the compilation loop
[ ]
#{:colon :at} {}
(fn [params arg-navigator offsets]
(if (:at params)
(prn))
arg-navigator))
(\T
[ :colnum [1 Integer] :colinc [1 Integer] ]
#{ :at :pretty } {}
(if (:at params)
#(relative-tabulation %1 %2 %3)
#(absolute-tabulation %1 %2 %3)))
(\*
[ :n [1 Integer] ]
#{ :colon :at } {}
(fn [params navigator offsets]
(let [n (:n params)]
(if (:at params)
(absolute-reposition navigator n)
(relative-reposition navigator (if (:colon params) (- n) n)))
)))
(\?
[ ]
#{ :at } {}
(if (:at params)
(fn [params navigator offsets] ; args from main arg list
(let [[subformat navigator] (get-format-arg navigator)]
(execute-sub-format subformat navigator (:base-args params))))
(fn [params navigator offsets] ; args from sub-list
(let [[subformat navigator] (get-format-arg navigator)
[subargs navigator] (next-arg navigator)
sub-navigator (init-navigator subargs)]
(execute-sub-format subformat sub-navigator (:base-args params))
navigator))))
(\(
[ ]
#{ :colon :at :both} { :right \), :allows-separator nil, :else nil }
(let [mod-case-writer (cond
(and (:at params) (:colon params))
upcase-writer
(:colon params)
capitalize-word-writer
(:at params)
init-cap-writer
:else
downcase-writer)]
#(modify-case mod-case-writer %1 %2 %3)))
(\) [] #{} {} nil)
(\[
[ :selector [nil Integer] ]
#{ :colon :at } { :right \], :allows-separator true, :else :last }
(cond
(:colon params)
boolean-conditional
(:at params)
check-arg-conditional
true
choice-conditional))
(\; [:min-remaining [nil Integer] :max-columns [nil Integer]]
#{ :colon } { :separator true } nil)
(\] [] #{} {} nil)
(\{
[ :max-iterations [nil Integer] ]
#{ :colon :at :both} { :right \}, :allows-separator false }
(cond
(and (:at params) (:colon params))
iterate-main-sublists
(:colon params)
iterate-list-of-sublists
(:at params)
iterate-main-list
true
iterate-sublist))
(\} [] #{:colon} {} nil)
(\<
[:mincol [0 Integer] :colinc [1 Integer] :minpad [0 Integer] :padchar [\space Character]]
#{:colon :at :both :pretty} { :right \>, :allows-separator true, :else :first }
logical-block-or-justify)
(\> [] #{:colon} {} nil)
;; TODO: detect errors in cases where colon not allowed
(\^ [:arg1 [nil Integer] :arg2 [nil Integer] :arg3 [nil Integer]]
#{:colon} {}
(fn [params navigator offsets]
(let [arg1 (:arg1 params)
arg2 (:arg2 params)
arg3 (:arg3 params)
exit (if (:colon params) :colon-up-arrow :up-arrow)]
(cond
(and arg1 arg2 arg3)
(if (<= arg1 arg2 arg3) [exit navigator] navigator)
(and arg1 arg2)
(if (= arg1 arg2) [exit navigator] navigator)
arg1
(if (= arg1 0) [exit navigator] navigator)
true ; TODO: handle looking up the arglist stack for info
(if (if (:colon params)
(empty? (:rest (:base-args params)))
(empty? (:rest navigator)))
[exit navigator] navigator)))))
(\W
[]
#{:at :colon :both} {}
(if (or (:at params) (:colon params))
(let [bindings (concat
(if (:at params) [:level nil :length nil] [])
(if (:colon params) [:pretty true] []))]
(fn [params navigator offsets]
(let [[arg navigator] (next-arg navigator)]
(if (apply write arg bindings)
[:up-arrow navigator]
navigator))))
(fn [params navigator offsets]
(let [[arg navigator] (next-arg navigator)]
(if (write-out arg)
[:up-arrow navigator]
navigator)))))
(\_
[]
#{:at :colon :both} {}
conditional-newline)
(\I
[:n [0 Integer]]
#{:colon} {}
set-indent)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Code to manage the parameters and flags associated with each
;;; directive in the format string.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^{:private true}
param-pattern #"^([vV]|#|('.)|([+-]?\d+)|(?=,))")
(def ^{:private true}
special-params #{ :parameter-from-args :remaining-arg-count })
(defn- extract-param [[s offset saw-comma]]
(let [m (re-matcher param-pattern s)
param (re-find m)]
(if param
(let [token-str (first (re-groups m))
remainder (subs s (.end m))
new-offset (+ offset (.end m))]
(if (not (= \, (nth remainder 0)))
[ [token-str offset] [remainder new-offset false]]
[ [token-str offset] [(subs remainder 1) (inc new-offset) true]]))
(if saw-comma
(format-error "Badly formed parameters in format directive" offset)
[ nil [s offset]]))))
(defn- extract-params [s offset]
(consume extract-param [s offset false]))
(defn- translate-param
"Translate the string representation of a param to the internalized
representation"
[[^String p offset]]
[(cond
(= (.length p) 0) nil
(and (= (.length p) 1) (contains? #{\v \V} (nth p 0))) :parameter-from-args
(and (= (.length p) 1) (= \# (nth p 0))) :remaining-arg-count
(and (= (.length p) 2) (= \' (nth p 0))) (nth p 1)
true (new Integer p))
offset])
(def ^{:private true}
flag-defs { \: :colon, \@ :at })
(defn- extract-flags [s offset]
(consume
(fn [[s offset flags]]
(if (empty? s)
[nil [s offset flags]]
(let [flag (get flag-defs (first s))]
(if flag
(if (contains? flags flag)
(format-error
(str "Flag \"" (first s) "\" appears more than once in a directive")
offset)
[true [(subs s 1) (inc offset) (assoc flags flag [true offset])]])
[nil [s offset flags]]))))
[s offset {}]))
(defn- check-flags [def flags]
(let [allowed (:flags def)]
(if (and (not (:at allowed)) (:at flags))
(format-error (str "\"@\" is an illegal flag for format directive \"" (:directive def) "\"")
(nth (:at flags) 1)))
(if (and (not (:colon allowed)) (:colon flags))
(format-error (str "\":\" is an illegal flag for format directive \"" (:directive def) "\"")
(nth (:colon flags) 1)))
(if (and (not (:both allowed)) (:at flags) (:colon flags))
(format-error (str "Cannot combine \"@\" and \":\" flags for format directive \""
(:directive def) "\"")
(min (nth (:colon flags) 1) (nth (:at flags) 1))))))
(defn- map-params
"Takes a directive definition and the list of actual parameters and
a map of flags and returns a map of the parameters and flags with defaults
filled in. We check to make sure that there are the right types and number
of parameters as well."
[def params flags offset]
(check-flags def flags)
(if (> (count params) (count (:params def)))
(format-error
(cl-format
nil
"Too many parameters for directive \"~C\": ~D~:* ~[were~;was~:;were~] specified but only ~D~:* ~[are~;is~:;are~] allowed"
(:directive def) (count params) (count (:params def)))
(second (first params))))
(doall
(map #(let [val (first %1)]
(if (not (or (nil? val) (contains? special-params val)
(instance? (second (second %2)) val)))
(format-error (str "Parameter " (name (first %2))
" has bad type in directive \"" (:directive def) "\": "
(class val))
(second %1))) )
params (:params def)))
(merge ; create the result map
(into (array-map) ; start with the default values, make sure the order is right
(reverse (for [[name [default]] (:params def)] [name [default offset]])))
(reduce #(apply assoc %1 %2) {} (filter #(first (nth % 1)) (zipmap (keys (:params def)) params))) ; add the specified parameters, filtering out nils
flags)) ; and finally add the flags
(defn- compile-directive [s offset]
(let [[raw-params [rest offset]] (extract-params s offset)
[_ [rest offset flags]] (extract-flags rest offset)
directive (first rest)
def (get directive-table (Character/toUpperCase ^Character directive))
params (if def (map-params def (map translate-param raw-params) flags offset))]
(if (not directive)
(format-error "Format string ended in the middle of a directive" offset))
(if (not def)
(format-error (str "Directive \"" directive "\" is undefined") offset))
[(struct compiled-directive ((:generator-fn def) params offset) def params offset)
(let [remainder (subs rest 1)
offset (inc offset)
trim? (and (= \newline (:directive def))
(not (:colon params)))
trim-count (if trim? (prefix-count remainder [\space \tab]) 0)
remainder (subs remainder trim-count)
offset (+ offset trim-count)]
[remainder offset])]))
(defn- compile-raw-string [s offset]
(struct compiled-directive (fn [_ a _] (print s) a) nil { :string s } offset))
(defn- right-bracket [this] (:right (:bracket-info (:def this))))
(defn- separator? [this] (:separator (:bracket-info (:def this))))
(defn- else-separator? [this]
(and (:separator (:bracket-info (:def this)))
(:colon (:params this))))
(declare collect-clauses)
(defn- process-bracket [this remainder]
(let [[subex remainder] (collect-clauses (:bracket-info (:def this))
(:offset this) remainder)]
[(struct compiled-directive
(:func this) (:def this)
(merge (:params this) (tuple-map subex (:offset this)))
(:offset this))
remainder]))
(defn- process-clause [bracket-info offset remainder]
(consume
(fn [remainder]
(if (empty? remainder)
(format-error "No closing bracket found." offset)
(let [this (first remainder)
remainder (next remainder)]
(cond
(right-bracket this)
(process-bracket this remainder)
(= (:right bracket-info) (:directive (:def this)))
[ nil [:right-bracket (:params this) nil remainder]]
(else-separator? this)
[nil [:else nil (:params this) remainder]]
(separator? this)
[nil [:separator nil nil remainder]] ;; TODO: check to make sure that there are no params on ~;
true
[this remainder]))))
remainder))
(defn- collect-clauses [bracket-info offset remainder]
(second
(consume
(fn [[clause-map saw-else remainder]]
(let [[clause [type right-params else-params remainder]]
(process-clause bracket-info offset remainder)]
(cond
(= type :right-bracket)
[nil [(merge-with concat clause-map
{(if saw-else :else :clauses) [clause]
:right-params right-params})
remainder]]
(= type :else)
(cond
(:else clause-map)
(format-error "Two else clauses (\"~:;\") inside bracket construction." offset)
(not (:else bracket-info))
(format-error "An else clause (\"~:;\") is in a bracket type that doesn't support it."
offset)
(and (= :first (:else bracket-info)) (seq (:clauses clause-map)))
(format-error
"The else clause (\"~:;\") is only allowed in the first position for this directive."
offset)
true ; if the ~:; is in the last position, the else clause
; is next, this was a regular clause
(if (= :first (:else bracket-info))
[true [(merge-with concat clause-map { :else [clause] :else-params else-params})
false remainder]]
[true [(merge-with concat clause-map { :clauses [clause] })
true remainder]]))
(= type :separator)
(cond
saw-else
(format-error "A plain clause (with \"~;\") follows an else clause (\"~:;\") inside bracket construction." offset)
(not (:allows-separator bracket-info))
(format-error "A separator (\"~;\") is in a bracket type that doesn't support it."
offset)
true
[true [(merge-with concat clause-map { :clauses [clause] })
false remainder]]))))
[{ :clauses [] } false remainder])))
(defn- process-nesting
"Take a linearly compiled format and process the bracket directives to give it
the appropriate tree structure"
[format]
(first
(consume
(fn [remainder]
(let [this (first remainder)
remainder (next remainder)
bracket (:bracket-info (:def this))]
(if (:right bracket)
(process-bracket this remainder)
[this remainder])))
format)))
(defn- compile-format
"Compiles format-str into a compiled format which can be used as an argument
to cl-format just like a plain format string. Use this function for improved
performance when you're using the same format string repeatedly"
[ format-str ]
; (prlabel compiling format-str)
(binding [*format-str* format-str]
(process-nesting
(first
(consume
(fn [[^String s offset]]
(if (empty? s)
[nil s]
(let [tilde (.indexOf s (int \~))]
(cond
(neg? tilde) [(compile-raw-string s offset) ["" (+ offset (.length s))]]
(zero? tilde) (compile-directive (subs s 1) (inc offset))
true
[(compile-raw-string (subs s 0 tilde) offset) [(subs s tilde) (+ tilde offset)]]))))
[format-str 0])))))
(defn- needs-pretty
"determine whether a given compiled format has any directives that depend on the
column number or pretty printing"
[format]
(loop [format format]
(if (empty? format)
false
(if (or (:pretty (:flags (:def (first format))))
(some needs-pretty (first (:clauses (:params (first format)))))
(some needs-pretty (first (:else (:params (first format))))))
true
(recur (next format))))))
(defn- execute-format
"Executes the format with the arguments."
{:skip-wiki true}
([stream format args]
(let [^java.io.Writer real-stream (cond
(not stream) (java.io.StringWriter.)
(true? stream) *out*
:else stream)
^java.io.Writer wrapped-stream (if (and (needs-pretty format)
(not (pretty-writer? real-stream)))
(get-pretty-writer real-stream)
real-stream)]
(binding [*out* wrapped-stream]
(try
(execute-format format args)
(finally
(if-not (identical? real-stream wrapped-stream)
(.flush wrapped-stream))))
(if (not stream) (.toString real-stream)))))
([format args]
(map-passing-context
(fn [element context]
(if (abort? context)
[nil context]
(let [[params args] (realize-parameter-list
(:params element) context)
[params offsets] (unzip-map params)
params (assoc params :base-args args)]
[nil (apply (:func element) [params args offsets])])))
args
format)
nil))
;;; This is a bad idea, but it prevents us from leaking private symbols
;;; This should all be replaced by really compiled formats anyway.
(def ^{:private true} cached-compile (memoize compile-format))
(defmacro formatter
"Makes a function which can directly run format-in. The function is
fn [stream & args] ... and returns nil unless the stream is nil (meaning
output to a string) in which case it returns the resulting string.
format-in can be either a control string or a previously compiled format."
{:added "1.2"}
[format-in]
`(let [format-in# ~format-in
my-c-c# (var-get (get (ns-interns (the-ns 'clojure.pprint))
'~'cached-compile))
my-e-f# (var-get (get (ns-interns (the-ns 'clojure.pprint))
'~'execute-format))
my-i-n# (var-get (get (ns-interns (the-ns 'clojure.pprint))
'~'init-navigator))
cf# (if (string? format-in#) (my-c-c# format-in#) format-in#)]
(fn [stream# & args#]
(let [navigator# (my-i-n# args#)]
(my-e-f# stream# cf# navigator#)))))
(defmacro formatter-out
"Makes a function which can directly run format-in. The function is
fn [& args] ... and returns nil. This version of the formatter macro is
designed to be used with *out* set to an appropriate Writer. In particular,
this is meant to be used as part of a pretty printer dispatch method.
format-in can be either a control string or a previously compiled format."
{:added "1.2"}
[format-in]
`(let [format-in# ~format-in
cf# (if (string? format-in#) (#'clojure.pprint/cached-compile format-in#) format-in#)]
(fn [& args#]
(let [navigator# (#'clojure.pprint/init-navigator args#)]
(#'clojure.pprint/execute-format cf# navigator#)))))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/pprint/column_writer.clj 0000664 0000000 0000000 00000005154 11575623476 0025265 0 ustar 00root root 0000000 0000000 ;;; column_writer.clj -- part of the pretty printer for Clojure
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; Author: Tom Faulhaber
;; April 3, 2009
;; Revised to use proxy instead of gen-class April 2010
;; This module implements a column-aware wrapper around an instance of java.io.Writer
(in-ns 'clojure.pprint)
(import [clojure.lang IDeref]
[java.io Writer])
(def ^{:private true} *default-page-width* 72)
(defn- get-field [^Writer this sym]
(sym @@this))
(defn- set-field [^Writer this sym new-val]
(alter @this assoc sym new-val))
(defn- get-column [this]
(get-field this :cur))
(defn- get-line [this]
(get-field this :line))
(defn- get-max-column [this]
(get-field this :max))
(defn- set-max-column [this new-max]
(dosync (set-field this :max new-max))
nil)
(defn- get-writer [this]
(get-field this :base))
(defn- c-write-char [^Writer this ^Integer c]
(dosync (if (= c (int \newline))
(do
(set-field this :cur 0)
(set-field this :line (inc (get-field this :line))))
(set-field this :cur (inc (get-field this :cur)))))
(.write ^Writer (get-field this :base) c))
(defn- column-writer
([writer] (column-writer writer *default-page-width*))
([writer max-columns]
(let [fields (ref {:max max-columns, :cur 0, :line 0 :base writer})]
(proxy [Writer IDeref] []
(deref [] fields)
(write
([^chars cbuf ^Integer off ^Integer len]
(let [^Writer writer (get-field this :base)]
(.write writer cbuf off len)))
([x]
(condp = (class x)
String
(let [^String s x
nl (.lastIndexOf s (int \newline))]
(dosync (if (neg? nl)
(set-field this :cur (+ (get-field this :cur) (count s)))
(do
(set-field this :cur (- (count s) nl 1))
(set-field this :line (+ (get-field this :line)
(count (filter #(= % \newline) s)))))))
(.write ^Writer (get-field this :base) s))
Integer
(c-write-char this x))))))))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/pprint/dispatch.clj 0000664 0000000 0000000 00000043147 11575623476 0024177 0 ustar 00root root 0000000 0000000 ;; dispatch.clj -- part of the pretty printer for Clojure
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; Author: Tom Faulhaber
;; April 3, 2009
;; This module implements the default dispatch tables for pretty printing code and
;; data.
(in-ns 'clojure.pprint)
(defn- use-method
"Installs a function as a new method of multimethod associated with dispatch-value. "
[multifn dispatch-val func]
(. multifn addMethod dispatch-val func))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Implementations of specific dispatch table entries
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Handle forms that can be "back-translated" to reader macros
;;; Not all reader macros can be dealt with this way or at all.
;;; Macros that we can't deal with at all are:
;;; ; - The comment character is aborbed by the reader and never is part of the form
;;; ` - Is fully processed at read time into a lisp expression (which will contain concats
;;; and regular quotes).
;;; ~@ - Also fully eaten by the processing of ` and can't be used outside.
;;; , - is whitespace and is lost (like all other whitespace). Formats can generate commas
;;; where they deem them useful to help readability.
;;; ^ - Adding metadata completely disappears at read time and the data appears to be
;;; completely lost.
;;;
;;; Most other syntax stuff is dealt with directly by the formats (like (), [], {}, and #{})
;;; or directly by printing the objects using Clojure's built-in print functions (like
;;; :keyword, \char, or ""). The notable exception is #() which is special-cased.
(def ^{:private true} reader-macros
{'quote "'", 'clojure.core/deref "@",
'var "#'", 'clojure.core/unquote "~"})
(defn- pprint-reader-macro [alis]
(let [^String macro-char (reader-macros (first alis))]
(when (and macro-char (= 2 (count alis)))
(.write ^java.io.Writer *out* macro-char)
(write-out (second alis))
true)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Dispatch for the basic data types when interpreted
;; as data (as opposed to code).
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; TODO: inline these formatter statements into funcs so that we
;;; are a little easier on the stack. (Or, do "real" compilation, a
;;; la Common Lisp)
;;; (def pprint-simple-list (formatter-out "~:<~@{~w~^ ~_~}~:>"))
(defn- pprint-simple-list [alis]
(pprint-logical-block :prefix "(" :suffix ")"
(loop [alis (seq alis)]
(when alis
(write-out (first alis))
(when (next alis)
(.write ^java.io.Writer *out* " ")
(pprint-newline :linear)
(recur (next alis)))))))
(defn- pprint-list [alis]
(if-not (pprint-reader-macro alis)
(pprint-simple-list alis)))
;;; (def pprint-vector (formatter-out "~<[~;~@{~w~^ ~_~}~;]~:>"))
(defn- pprint-vector [avec]
(pprint-logical-block :prefix "[" :suffix "]"
(loop [aseq (seq avec)]
(when aseq
(write-out (first aseq))
(when (next aseq)
(.write ^java.io.Writer *out* " ")
(pprint-newline :linear)
(recur (next aseq)))))))
(def ^{:private true} pprint-array (formatter-out "~<[~;~@{~w~^, ~:_~}~;]~:>"))
;;; (def pprint-map (formatter-out "~<{~;~@{~<~w~^ ~_~w~:>~^, ~_~}~;}~:>"))
(defn- pprint-map [amap]
(pprint-logical-block :prefix "{" :suffix "}"
(loop [aseq (seq amap)]
(when aseq
(pprint-logical-block
(write-out (ffirst aseq))
(.write ^java.io.Writer *out* " ")
(pprint-newline :linear)
(write-out (fnext (first aseq))))
(when (next aseq)
(.write ^java.io.Writer *out* ", ")
(pprint-newline :linear)
(recur (next aseq)))))))
(def ^{:private true} pprint-set (formatter-out "~<#{~;~@{~w~^ ~:_~}~;}~:>"))
;;; TODO: don't block on promise (currently impossible)
(def ^{:private true}
type-map {"core$future_call" "Future",
"core$promise" "Promise"})
(defn- map-ref-type
"Map ugly type names to something simpler"
[name]
(or (when-let [match (re-find #"^[^$]+\$[^$]+" name)]
(type-map match))
name))
(defn- pprint-ideref [o]
(let [prefix (format "#<%s@%x%s: "
(map-ref-type (.getSimpleName (class o)))
(System/identityHashCode o)
(if (and (instance? clojure.lang.Agent o)
(agent-error o))
" FAILED"
""))]
(pprint-logical-block :prefix prefix :suffix ">"
(pprint-indent :block (-> (count prefix) (- 2) -))
(pprint-newline :linear)
(write-out (cond
(and (future? o) (not (future-done? o))) :pending
:else @o)))))
(def ^{:private true} pprint-pqueue (formatter-out "~<<-(~;~@{~w~^ ~_~}~;)-<~:>"))
(defn- pprint-simple-default [obj]
(cond
(.isArray (class obj)) (pprint-array obj)
(and *print-suppress-namespaces* (symbol? obj)) (print (name obj))
:else (pr obj)))
(defmulti
simple-dispatch
"The pretty print dispatch function for simple data structure format."
{:added "1.2" :arglists '[[object]]}
class)
(use-method simple-dispatch clojure.lang.ISeq pprint-list)
(use-method simple-dispatch clojure.lang.IPersistentVector pprint-vector)
(use-method simple-dispatch clojure.lang.IPersistentMap pprint-map)
(use-method simple-dispatch clojure.lang.IPersistentSet pprint-set)
(use-method simple-dispatch clojure.lang.PersistentQueue pprint-pqueue)
(use-method simple-dispatch clojure.lang.IDeref pprint-ideref)
(use-method simple-dispatch nil pr)
(use-method simple-dispatch :default pprint-simple-default)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Dispatch for the code table
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare pprint-simple-code-list)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Format something that looks like a simple def (sans metadata, since the reader
;;; won't give it to us now).
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^{:private true} pprint-hold-first (formatter-out "~:<~w~^ ~@_~w~^ ~_~@{~w~^ ~_~}~:>"))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Format something that looks like a defn or defmacro
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Format the params and body of a defn with a single arity
(defn- single-defn [alis has-doc-str?]
(if (seq alis)
(do
(if has-doc-str?
((formatter-out " ~_"))
((formatter-out " ~@_")))
((formatter-out "~{~w~^ ~_~}") alis))))
;;; Format the param and body sublists of a defn with multiple arities
(defn- multi-defn [alis has-doc-str?]
(if (seq alis)
((formatter-out " ~_~{~w~^ ~_~}") alis)))
;;; TODO: figure out how to support capturing metadata in defns (we might need a
;;; special reader)
(defn- pprint-defn [alis]
(if (next alis)
(let [[defn-sym defn-name & stuff] alis
[doc-str stuff] (if (string? (first stuff))
[(first stuff) (next stuff)]
[nil stuff])
[attr-map stuff] (if (map? (first stuff))
[(first stuff) (next stuff)]
[nil stuff])]
(pprint-logical-block :prefix "(" :suffix ")"
((formatter-out "~w ~1I~@_~w") defn-sym defn-name)
(if doc-str
((formatter-out " ~_~w") doc-str))
(if attr-map
((formatter-out " ~_~w") attr-map))
;; Note: the multi-defn case will work OK for malformed defns too
(cond
(vector? (first stuff)) (single-defn stuff (or doc-str attr-map))
:else (multi-defn stuff (or doc-str attr-map)))))
(pprint-simple-code-list alis)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Format something with a binding form
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- pprint-binding-form [binding-vec]
(pprint-logical-block :prefix "[" :suffix "]"
(loop [binding binding-vec]
(when (seq binding)
(pprint-logical-block binding
(write-out (first binding))
(when (next binding)
(.write ^java.io.Writer *out* " ")
(pprint-newline :miser)
(write-out (second binding))))
(when (next (rest binding))
(.write ^java.io.Writer *out* " ")
(pprint-newline :linear)
(recur (next (rest binding))))))))
(defn- pprint-let [alis]
(let [base-sym (first alis)]
(pprint-logical-block :prefix "(" :suffix ")"
(if (and (next alis) (vector? (second alis)))
(do
((formatter-out "~w ~1I~@_") base-sym)
(pprint-binding-form (second alis))
((formatter-out " ~_~{~w~^ ~_~}") (next (rest alis))))
(pprint-simple-code-list alis)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Format something that looks like "if"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^{:private true} pprint-if (formatter-out "~:<~1I~w~^ ~@_~w~@{ ~_~w~}~:>"))
(defn- pprint-cond [alis]
(pprint-logical-block :prefix "(" :suffix ")"
(pprint-indent :block 1)
(write-out (first alis))
(when (next alis)
(.write ^java.io.Writer *out* " ")
(pprint-newline :linear)
(loop [alis (next alis)]
(when alis
(pprint-logical-block alis
(write-out (first alis))
(when (next alis)
(.write ^java.io.Writer *out* " ")
(pprint-newline :miser)
(write-out (second alis))))
(when (next (rest alis))
(.write ^java.io.Writer *out* " ")
(pprint-newline :linear)
(recur (next (rest alis)))))))))
(defn- pprint-condp [alis]
(if (> (count alis) 3)
(pprint-logical-block :prefix "(" :suffix ")"
(pprint-indent :block 1)
(apply (formatter-out "~w ~@_~w ~@_~w ~_") alis)
(loop [alis (seq (drop 3 alis))]
(when alis
(pprint-logical-block alis
(write-out (first alis))
(when (next alis)
(.write ^java.io.Writer *out* " ")
(pprint-newline :miser)
(write-out (second alis))))
(when (next (rest alis))
(.write ^java.io.Writer *out* " ")
(pprint-newline :linear)
(recur (next (rest alis)))))))
(pprint-simple-code-list alis)))
;;; The map of symbols that are defined in an enclosing #() anonymous function
(def ^{:private true} *symbol-map* {})
(defn- pprint-anon-func [alis]
(let [args (second alis)
nlis (first (rest (rest alis)))]
(if (vector? args)
(binding [*symbol-map* (if (= 1 (count args))
{(first args) "%"}
(into {}
(map
#(vector %1 (str \% %2))
args
(range 1 (inc (count args))))))]
((formatter-out "~<#(~;~@{~w~^ ~_~}~;)~:>") nlis))
(pprint-simple-code-list alis))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; The master definitions for formatting lists in code (that is, (fn args...) or
;;; special forms).
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This is the equivalent of (formatter-out "~:<~1I~@{~w~^ ~_~}~:>"), but is
;;; easier on the stack.
(defn- pprint-simple-code-list [alis]
(pprint-logical-block :prefix "(" :suffix ")"
(pprint-indent :block 1)
(loop [alis (seq alis)]
(when alis
(write-out (first alis))
(when (next alis)
(.write ^java.io.Writer *out* " ")
(pprint-newline :linear)
(recur (next alis)))))))
;;; Take a map with symbols as keys and add versions with no namespace.
;;; That is, if ns/sym->val is in the map, add sym->val to the result.
(defn- two-forms [amap]
(into {}
(mapcat
identity
(for [x amap]
[x [(symbol (name (first x))) (second x)]]))))
(defn- add-core-ns [amap]
(let [core "clojure.core"]
(into {}
(map #(let [[s f] %]
(if (not (or (namespace s) (special-symbol? s)))
[(symbol core (name s)) f]
%))
amap))))
(def ^{:private true} *code-table*
(two-forms
(add-core-ns
{'def pprint-hold-first, 'defonce pprint-hold-first,
'defn pprint-defn, 'defn- pprint-defn, 'defmacro pprint-defn, 'fn pprint-defn,
'let pprint-let, 'loop pprint-let, 'binding pprint-let,
'with-local-vars pprint-let, 'with-open pprint-let, 'when-let pprint-let,
'if-let pprint-let, 'doseq pprint-let, 'dotimes pprint-let,
'when-first pprint-let,
'if pprint-if, 'if-not pprint-if, 'when pprint-if, 'when-not pprint-if,
'cond pprint-cond, 'condp pprint-condp,
'fn* pprint-anon-func,
'. pprint-hold-first, '.. pprint-hold-first, '-> pprint-hold-first,
'locking pprint-hold-first, 'struct pprint-hold-first,
'struct-map pprint-hold-first,
})))
(defn- pprint-code-list [alis]
(if-not (pprint-reader-macro alis)
(if-let [special-form (*code-table* (first alis))]
(special-form alis)
(pprint-simple-code-list alis))))
(defn- pprint-code-symbol [sym]
(if-let [arg-num (sym *symbol-map*)]
(print arg-num)
(if *print-suppress-namespaces*
(print (name sym))
(pr sym))))
(defmulti
code-dispatch
"The pretty print dispatch function for pretty printing Clojure code."
{:added "1.2" :arglists '[[object]]}
class)
(use-method code-dispatch clojure.lang.ISeq pprint-code-list)
(use-method code-dispatch clojure.lang.Symbol pprint-code-symbol)
;; The following are all exact copies of simple-dispatch
(use-method code-dispatch clojure.lang.IPersistentVector pprint-vector)
(use-method code-dispatch clojure.lang.IPersistentMap pprint-map)
(use-method code-dispatch clojure.lang.IPersistentSet pprint-set)
(use-method code-dispatch clojure.lang.PersistentQueue pprint-pqueue)
(use-method code-dispatch clojure.lang.IDeref pprint-ideref)
(use-method code-dispatch nil pr)
(use-method code-dispatch :default pprint-simple-default)
(set-pprint-dispatch simple-dispatch)
;;; For testing
(comment
(with-pprint-dispatch code-dispatch
(pprint
'(defn cl-format
"An implementation of a Common Lisp compatible format function"
[stream format-in & args]
(let [compiled-format (if (string? format-in) (compile-format format-in) format-in)
navigator (init-navigator args)]
(execute-format stream compiled-format navigator)))))
(with-pprint-dispatch code-dispatch
(pprint
'(defn cl-format
[stream format-in & args]
(let [compiled-format (if (string? format-in) (compile-format format-in) format-in)
navigator (init-navigator args)]
(execute-format stream compiled-format navigator)))))
(with-pprint-dispatch code-dispatch
(pprint
'(defn- -write
([this x]
(condp = (class x)
String
(let [s0 (write-initial-lines this x)
s (.replaceFirst s0 "\\s+$" "")
white-space (.substring s0 (count s))
mode (getf :mode)]
(if (= mode :writing)
(dosync
(write-white-space this)
(.col_write this s)
(setf :trailing-white-space white-space))
(add-to-buffer this (make-buffer-blob s white-space))))
Integer
(let [c ^Character x]
(if (= (getf :mode) :writing)
(do
(write-white-space this)
(.col_write this x))
(if (= c (int \newline))
(write-initial-lines this "\n")
(add-to-buffer this (make-buffer-blob (str (char c)) nil))))))))))
(with-pprint-dispatch code-dispatch
(pprint
'(defn pprint-defn [writer alis]
(if (next alis)
(let [[defn-sym defn-name & stuff] alis
[doc-str stuff] (if (string? (first stuff))
[(first stuff) (next stuff)]
[nil stuff])
[attr-map stuff] (if (map? (first stuff))
[(first stuff) (next stuff)]
[nil stuff])]
(pprint-logical-block writer :prefix "(" :suffix ")"
(cl-format true "~w ~1I~@_~w" defn-sym defn-name)
(if doc-str
(cl-format true " ~_~w" doc-str))
(if attr-map
(cl-format true " ~_~w" attr-map))
;; Note: the multi-defn case will work OK for malformed defns too
(cond
(vector? (first stuff)) (single-defn stuff (or doc-str attr-map))
:else (multi-defn stuff (or doc-str attr-map)))))
(pprint-simple-code-list writer alis)))))
)
nil
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/pprint/pprint_base.clj 0000664 0000000 0000000 00000034162 11575623476 0024703 0 ustar 00root root 0000000 0000000 ;;; pprint_base.clj -- part of the pretty printer for Clojure
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; Author: Tom Faulhaber
;; April 3, 2009
;; This module implements the generic pretty print functions and special variables
(in-ns 'clojure.pprint)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Variables that control the pretty printer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; *print-length*, *print-level* and *print-dup* are defined in clojure.core
;;; TODO: use *print-dup* here (or is it supplanted by other variables?)
;;; TODO: make dispatch items like "(let..." get counted in *print-length*
;;; constructs
(def
^{:doc "Bind to true if you want write to use pretty printing", :added "1.2"}
*print-pretty* true)
(defonce ; If folks have added stuff here, don't overwrite
^{:doc "The pretty print dispatch function. Use with-pprint-dispatch or set-pprint-dispatch
to modify.",
:added "1.2"}
*print-pprint-dispatch* nil)
(def
^{:doc "Pretty printing will try to avoid anything going beyond this column.
Set it to nil to have pprint let the line be arbitrarily long. This will ignore all
non-mandatory newlines.",
:added "1.2"}
*print-right-margin* 72)
(def
^{:doc "The column at which to enter miser style. Depending on the dispatch table,
miser style add newlines in more places to try to keep lines short allowing for further
levels of nesting.",
:added "1.2"}
*print-miser-width* 40)
;;; TODO implement output limiting
(def
^{:private true,
:doc "Maximum number of lines to print in a pretty print instance (N.B. This is not yet used)"}
*print-lines* nil)
;;; TODO: implement circle and shared
(def
^{:private true,
:doc "Mark circular structures (N.B. This is not yet used)"}
*print-circle* nil)
;;; TODO: should we just use *print-dup* here?
(def
^{:private true,
:doc "Mark repeated structures rather than repeat them (N.B. This is not yet used)"}
*print-shared* nil)
(def
^{:doc "Don't print namespaces with symbols. This is particularly useful when
pretty printing the results of macro expansions"
:added "1.2"}
*print-suppress-namespaces* nil)
;;; TODO: support print-base and print-radix in cl-format
;;; TODO: support print-base and print-radix in rationals
(def
^{:doc "Print a radix specifier in front of integers and rationals. If *print-base* is 2, 8,
or 16, then the radix specifier used is #b, #o, or #x, respectively. Otherwise the
radix specifier is in the form #XXr where XX is the decimal value of *print-base* "
:added "1.2"}
*print-radix* nil)
(def
^{:doc "The base to use for printing integers and rationals."
:added "1.2"}
*print-base* 10)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Internal variables that keep track of where we are in the
;; structure
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^{ :private true } *current-level* 0)
(def ^{ :private true } *current-length* nil)
;; TODO: add variables for length, lines.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Support for the write function
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare format-simple-number)
(def ^{:private true} orig-pr pr)
(defn- pr-with-base [x]
(if-let [s (format-simple-number x)]
(print s)
(orig-pr x)))
(def ^{:private true} write-option-table
{;:array *print-array*
:base 'clojure.pprint/*print-base*,
;;:case *print-case*,
:circle 'clojure.pprint/*print-circle*,
;;:escape *print-escape*,
;;:gensym *print-gensym*,
:length 'clojure.core/*print-length*,
:level 'clojure.core/*print-level*,
:lines 'clojure.pprint/*print-lines*,
:miser-width 'clojure.pprint/*print-miser-width*,
:dispatch 'clojure.pprint/*print-pprint-dispatch*,
:pretty 'clojure.pprint/*print-pretty*,
:radix 'clojure.pprint/*print-radix*,
:readably 'clojure.core/*print-readably*,
:right-margin 'clojure.pprint/*print-right-margin*,
:suppress-namespaces 'clojure.pprint/*print-suppress-namespaces*})
(defmacro ^{:private true} binding-map [amap & body]
(let []
`(do
(. clojure.lang.Var (pushThreadBindings ~amap))
(try
~@body
(finally
(. clojure.lang.Var (popThreadBindings)))))))
(defn- table-ize [t m]
(apply hash-map (mapcat
#(when-let [v (get t (key %))] [(find-var v) (val %)])
m)))
(defn- pretty-writer?
"Return true iff x is a PrettyWriter"
[x] (and (instance? clojure.lang.IDeref x) (:pretty-writer @@x)))
(defn- make-pretty-writer
"Wrap base-writer in a PrettyWriter with the specified right-margin and miser-width"
[base-writer right-margin miser-width]
(pretty-writer base-writer right-margin miser-width))
(defmacro ^{:private true} with-pretty-writer [base-writer & body]
`(let [base-writer# ~base-writer
new-writer# (not (pretty-writer? base-writer#))]
(binding [*out* (if new-writer#
(make-pretty-writer base-writer# *print-right-margin* *print-miser-width*)
base-writer#)]
~@body
(.flush *out*))))
;;;TODO: if pretty print is not set, don't use pr but rather something that respects *print-base*, etc.
(defn write-out
"Write an object to *out* subject to the current bindings of the printer control
variables. Use the kw-args argument to override individual variables for this call (and
any recursive calls).
*out* must be a PrettyWriter if pretty printing is enabled. This is the responsibility
of the caller.
This method is primarily intended for use by pretty print dispatch functions that
already know that the pretty printer will have set up their environment appropriately.
Normal library clients should use the standard \"write\" interface. "
{:added "1.2"}
[object]
(let [length-reached (and
*current-length*
*print-length*
(>= *current-length* *print-length*))]
(if-not *print-pretty*
(pr object)
(if length-reached
(print "...")
(do
(if *current-length* (set! *current-length* (inc *current-length*)))
(*print-pprint-dispatch* object))))
length-reached))
(defn write
"Write an object subject to the current bindings of the printer control variables.
Use the kw-args argument to override individual variables for this call (and any
recursive calls). Returns the string result if :stream is nil or nil otherwise.
The following keyword arguments can be passed with values:
Keyword Meaning Default value
:stream Writer for output or nil true (indicates *out*)
:base Base to use for writing rationals Current value of *print-base*
:circle* If true, mark circular structures Current value of *print-circle*
:length Maximum elements to show in sublists Current value of *print-length*
:level Maximum depth Current value of *print-level*
:lines* Maximum lines of output Current value of *print-lines*
:miser-width Width to enter miser mode Current value of *print-miser-width*
:dispatch The pretty print dispatch function Current value of *print-pprint-dispatch*
:pretty If true, do pretty printing Current value of *print-pretty*
:radix If true, prepend a radix specifier Current value of *print-radix*
:readably* If true, print readably Current value of *print-readably*
:right-margin The column for the right margin Current value of *print-right-margin*
:suppress-namespaces If true, no namespaces in symbols Current value of *print-suppress-namespaces*
* = not yet supported
"
{:added "1.2"}
[object & kw-args]
(let [options (merge {:stream true} (apply hash-map kw-args))]
(binding-map (table-ize write-option-table options)
(binding-map (if (or (not (= *print-base* 10)) *print-radix*) {#'pr pr-with-base} {})
(let [optval (if (contains? options :stream)
(:stream options)
true)
base-writer (condp = optval
nil (java.io.StringWriter.)
true *out*
optval)]
(if *print-pretty*
(with-pretty-writer base-writer
(write-out object))
(binding [*out* base-writer]
(pr object)))
(if (nil? optval)
(.toString ^java.io.StringWriter base-writer)))))))
(defn pprint
"Pretty print object to the optional output writer. If the writer is not provided,
print the object to the currently bound value of *out*."
{:added "1.2"}
([object] (pprint object *out*))
([object writer]
(with-pretty-writer writer
(binding [*print-pretty* true]
(binding-map (if (or (not (= *print-base* 10)) *print-radix*) {#'pr pr-with-base} {})
(write-out object)))
(if (not (= 0 (get-column *out*)))
(.write *out* (int \newline))))))
(defmacro pp
"A convenience macro that pretty prints the last thing output. This is
exactly equivalent to (pprint *1)."
{:added "1.2"}
[] `(pprint *1))
(defn set-pprint-dispatch
"Set the pretty print dispatch function to a function matching (fn [obj] ...)
where obj is the object to pretty print. That function will be called with *out* set
to a pretty printing writer to which it should do its printing.
For example functions, see simple-dispatch and code-dispatch in
clojure.pprint.dispatch.clj."
{:added "1.2"}
[function]
(let [old-meta (meta #'*print-pprint-dispatch*)]
(alter-var-root #'*print-pprint-dispatch* (constantly function))
(alter-meta! #'*print-pprint-dispatch* (constantly old-meta)))
nil)
(defmacro with-pprint-dispatch
"Execute body with the pretty print dispatch function bound to function."
{:added "1.2"}
[function & body]
`(binding [*print-pprint-dispatch* ~function]
~@body))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Support for the functional interface to the pretty printer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- parse-lb-options [opts body]
(loop [body body
acc []]
(if (opts (first body))
(recur (drop 2 body) (concat acc (take 2 body)))
[(apply hash-map acc) body])))
(defn- check-enumerated-arg [arg choices]
(if-not (choices arg)
(throw
(IllegalArgumentException.
;; TODO clean up choices string
(str "Bad argument: " arg ". It must be one of " choices)))))
(defn- level-exceeded []
(and *print-level* (>= *current-level* *print-level*)))
(defmacro pprint-logical-block
"Execute the body as a pretty printing logical block with output to *out* which
must be a pretty printing writer. When used from pprint or cl-format, this can be
assumed.
This function is intended for use when writing custom dispatch functions.
Before the body, the caller can optionally specify options: :prefix, :per-line-prefix,
and :suffix."
{:added "1.2", :arglists '[[options* body]]}
[& args]
(let [[options body] (parse-lb-options #{:prefix :per-line-prefix :suffix} args)]
`(do (if (#'clojure.pprint/level-exceeded)
(.write ^java.io.Writer *out* "#")
(do
(push-thread-bindings {#'clojure.pprint/*current-level*
(inc (var-get #'clojure.pprint/*current-level*))
#'clojure.pprint/*current-length* 0})
(try
(#'clojure.pprint/start-block *out*
~(:prefix options) ~(:per-line-prefix options) ~(:suffix options))
~@body
(#'clojure.pprint/end-block *out*)
(finally
(pop-thread-bindings)))))
nil)))
(defn pprint-newline
"Print a conditional newline to a pretty printing stream. kind specifies if the
newline is :linear, :miser, :fill, or :mandatory.
This function is intended for use when writing custom dispatch functions.
Output is sent to *out* which must be a pretty printing writer."
{:added "1.2"}
[kind]
(check-enumerated-arg kind #{:linear :miser :fill :mandatory})
(nl *out* kind))
(defn pprint-indent
"Create an indent at this point in the pretty printing stream. This defines how
following lines are indented. relative-to can be either :block or :current depending
whether the indent should be computed relative to the start of the logical block or
the current column position. n is an offset.
This function is intended for use when writing custom dispatch functions.
Output is sent to *out* which must be a pretty printing writer."
{:added "1.2"}
[relative-to n]
(check-enumerated-arg relative-to #{:block :current})
(indent *out* relative-to n))
;; TODO a real implementation for pprint-tab
(defn pprint-tab
"Tab at this point in the pretty printing stream. kind specifies whether the tab
is :line, :section, :line-relative, or :section-relative.
Colnum and colinc specify the target column and the increment to move the target
forward if the output is already past the original target.
This function is intended for use when writing custom dispatch functions.
Output is sent to *out* which must be a pretty printing writer.
THIS FUNCTION IS NOT YET IMPLEMENTED."
{:added "1.2"}
[kind colnum colinc]
(check-enumerated-arg kind #{:line :section :line-relative :section-relative})
(throw (UnsupportedOperationException. "pprint-tab is not yet implemented")))
nil
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/pprint/pretty_writer.clj 0000664 0000000 0000000 00000041100 11575623476 0025306 0 ustar 00root root 0000000 0000000 ;;; pretty_writer.clj -- part of the pretty printer for Clojure
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; Author: Tom Faulhaber
;; April 3, 2009
;; Revised to use proxy instead of gen-class April 2010
;; This module implements a wrapper around a java.io.Writer which implements the
;; core of the XP algorithm.
(in-ns 'clojure.pprint)
(import [clojure.lang IDeref]
[java.io Writer])
;; TODO: Support for tab directives
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Forward declarations
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare get-miser-width)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Macros to simplify dealing with types and classes. These are
;;; really utilities, but I'm experimenting with them here.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmacro ^{:private true}
getf
"Get the value of the field a named by the argument (which should be a keyword)."
[sym]
`(~sym @@~'this))
(defmacro ^{:private true}
setf [sym new-val]
"Set the value of the field SYM to NEW-VAL"
`(alter @~'this assoc ~sym ~new-val))
(defmacro ^{:private true}
deftype [type-name & fields]
(let [name-str (name type-name)]
`(do
(defstruct ~type-name :type-tag ~@fields)
(alter-meta! #'~type-name assoc :private true)
(defn- ~(symbol (str "make-" name-str))
[& vals#] (apply struct ~type-name ~(keyword name-str) vals#))
(defn- ~(symbol (str name-str "?")) [x#] (= (:type-tag x#) ~(keyword name-str))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; The data structures used by pretty-writer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defstruct ^{:private true} logical-block
:parent :section :start-col :indent
:done-nl :intra-block-nl
:prefix :per-line-prefix :suffix
:logical-block-callback)
(defn- ancestor? [parent child]
(loop [child (:parent child)]
(cond
(nil? child) false
(identical? parent child) true
:else (recur (:parent child)))))
(defstruct ^{:private true} section :parent)
(defn- buffer-length [l]
(let [l (seq l)]
(if l
(- (:end-pos (last l)) (:start-pos (first l)))
0)))
; A blob of characters (aka a string)
(deftype buffer-blob :data :trailing-white-space :start-pos :end-pos)
; A newline
(deftype nl-t :type :logical-block :start-pos :end-pos)
(deftype start-block-t :logical-block :start-pos :end-pos)
(deftype end-block-t :logical-block :start-pos :end-pos)
(deftype indent-t :logical-block :relative-to :offset :start-pos :end-pos)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Functions to write tokens in the output buffer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare emit-nl)
(defmulti ^{:private true} write-token #(:type-tag %2))
(defmethod write-token :start-block-t [^Writer this token]
(when-let [cb (getf :logical-block-callback)] (cb :start))
(let [lb (:logical-block token)]
(dosync
(when-let [^String prefix (:prefix lb)]
(.write (getf :base) prefix))
(let [col (get-column (getf :base))]
(ref-set (:start-col lb) col)
(ref-set (:indent lb) col)))))
(defmethod write-token :end-block-t [^Writer this token]
(when-let [cb (getf :logical-block-callback)] (cb :end))
(when-let [^String suffix (:suffix (:logical-block token))]
(.write (getf :base) suffix)))
(defmethod write-token :indent-t [^Writer this token]
(let [lb (:logical-block token)]
(ref-set (:indent lb)
(+ (:offset token)
(condp = (:relative-to token)
:block @(:start-col lb)
:current (get-column (getf :base)))))))
(defmethod write-token :buffer-blob [^Writer this token]
(.write (getf :base) ^String (:data token)))
(defmethod write-token :nl-t [^Writer this token]
; (prlabel wt @(:done-nl (:logical-block token)))
; (prlabel wt (:type token) (= (:type token) :mandatory))
(if (or (= (:type token) :mandatory)
(and (not (= (:type token) :fill))
@(:done-nl (:logical-block token))))
(emit-nl this token)
(if-let [^String tws (getf :trailing-white-space)]
(.write (getf :base) tws)))
(dosync (setf :trailing-white-space nil)))
(defn- write-tokens [^Writer this tokens force-trailing-whitespace]
(doseq [token tokens]
(if-not (= (:type-tag token) :nl-t)
(if-let [^String tws (getf :trailing-white-space)]
(.write (getf :base) tws)))
(write-token this token)
(setf :trailing-white-space (:trailing-white-space token)))
(let [^String tws (getf :trailing-white-space)]
(when (and force-trailing-whitespace tws)
(.write (getf :base) tws)
(setf :trailing-white-space nil))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; emit-nl? method defs for each type of new line. This makes
;;; the decision about whether to print this type of new line.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- tokens-fit? [^Writer this tokens]
;;; (prlabel tf? (get-column (getf :base) (buffer-length tokens))
(let [maxcol (get-max-column (getf :base))]
(or
(nil? maxcol)
(< (+ (get-column (getf :base)) (buffer-length tokens)) maxcol))))
(defn- linear-nl? [this lb section]
; (prlabel lnl? @(:done-nl lb) (tokens-fit? this section))
(or @(:done-nl lb)
(not (tokens-fit? this section))))
(defn- miser-nl? [^Writer this lb section]
(let [miser-width (get-miser-width this)
maxcol (get-max-column (getf :base))]
(and miser-width maxcol
(>= @(:start-col lb) (- maxcol miser-width))
(linear-nl? this lb section))))
(defmulti ^{:private true} emit-nl? (fn [t _ _ _] (:type t)))
(defmethod emit-nl? :linear [newl this section _]
(let [lb (:logical-block newl)]
(linear-nl? this lb section)))
(defmethod emit-nl? :miser [newl this section _]
(let [lb (:logical-block newl)]
(miser-nl? this lb section)))
(defmethod emit-nl? :fill [newl this section subsection]
(let [lb (:logical-block newl)]
(or @(:intra-block-nl lb)
(not (tokens-fit? this subsection))
(miser-nl? this lb section))))
(defmethod emit-nl? :mandatory [_ _ _ _]
true)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Various support functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- get-section [buffer]
(let [nl (first buffer)
lb (:logical-block nl)
section (seq (take-while #(not (and (nl-t? %) (ancestor? (:logical-block %) lb)))
(next buffer)))]
[section (seq (drop (inc (count section)) buffer))]))
(defn- get-sub-section [buffer]
(let [nl (first buffer)
lb (:logical-block nl)
section (seq (take-while #(let [nl-lb (:logical-block %)]
(not (and (nl-t? %) (or (= nl-lb lb) (ancestor? nl-lb lb)))))
(next buffer)))]
section))
(defn- update-nl-state [lb]
(dosync
(ref-set (:intra-block-nl lb) false)
(ref-set (:done-nl lb) true)
(loop [lb (:parent lb)]
(if lb
(do (ref-set (:done-nl lb) true)
(ref-set (:intra-block-nl lb) true)
(recur (:parent lb)))))))
(defn- emit-nl [^Writer this nl]
(.write (getf :base) (int \newline))
(dosync (setf :trailing-white-space nil))
(let [lb (:logical-block nl)
^String prefix (:per-line-prefix lb)]
(if prefix
(.write (getf :base) prefix))
(let [^String istr (apply str (repeat (- @(:indent lb) (count prefix))
\space))]
(.write (getf :base) istr))
(update-nl-state lb)))
(defn- split-at-newline [tokens]
(let [pre (seq (take-while #(not (nl-t? %)) tokens))]
[pre (seq (drop (count pre) tokens))]))
;;; Methods for showing token strings for debugging
(defmulti ^{:private true} tok :type-tag)
(defmethod tok :nl-t [token]
(:type token))
(defmethod tok :buffer-blob [token]
(str \" (:data token) (:trailing-white-space token) \"))
(defmethod tok :default [token]
(:type-tag token))
(defn- toks [toks] (map tok toks))
;;; write-token-string is called when the set of tokens in the buffer
;;; is longer than the available space on the line
(defn- write-token-string [this tokens]
(let [[a b] (split-at-newline tokens)]
;; (prlabel wts (toks a) (toks b))
(if a (write-tokens this a false))
(if b
(let [[section remainder] (get-section b)
newl (first b)]
;; (prlabel wts (toks section)) (prlabel wts (:type newl)) (prlabel wts (toks remainder))
(let [do-nl (emit-nl? newl this section (get-sub-section b))
result (if do-nl
(do
;; (prlabel emit-nl (:type newl))
(emit-nl this newl)
(next b))
b)
long-section (not (tokens-fit? this result))
result (if long-section
(let [rem2 (write-token-string this section)]
;;; (prlabel recurse (toks rem2))
(if (= rem2 section)
(do ; If that didn't produce any output, it has no nls
; so we'll force it
(write-tokens this section false)
remainder)
(into [] (concat rem2 remainder))))
result)
;; ff (prlabel wts (toks result))
]
result)))))
(defn- write-line [^Writer this]
(dosync
(loop [buffer (getf :buffer)]
;; (prlabel wl1 (toks buffer))
(setf :buffer (into [] buffer))
(if (not (tokens-fit? this buffer))
(let [new-buffer (write-token-string this buffer)]
;; (prlabel wl new-buffer)
(if-not (identical? buffer new-buffer)
(recur new-buffer)))))))
;;; Add a buffer token to the buffer and see if it's time to start
;;; writing
(defn- add-to-buffer [^Writer this token]
; (prlabel a2b token)
(dosync
(setf :buffer (conj (getf :buffer) token))
(if (not (tokens-fit? this (getf :buffer)))
(write-line this))))
;;; Write all the tokens that have been buffered
(defn- write-buffered-output [^Writer this]
(write-line this)
(if-let [buf (getf :buffer)]
(do
(write-tokens this buf true)
(setf :buffer []))))
;;; If there are newlines in the string, print the lines up until the last newline,
;;; making the appropriate adjustments. Return the remainder of the string
(defn- write-initial-lines
[^Writer this ^String s]
(let [lines (.split s "\n" -1)]
(if (= (count lines) 1)
s
(dosync
(let [^String prefix (:per-line-prefix (first (getf :logical-blocks)))
^String l (first lines)]
(if (= :buffering (getf :mode))
(let [oldpos (getf :pos)
newpos (+ oldpos (count l))]
(setf :pos newpos)
(add-to-buffer this (make-buffer-blob l nil oldpos newpos))
(write-buffered-output this))
(.write (getf :base) l))
(.write (getf :base) (int \newline))
(doseq [^String l (next (butlast lines))]
(.write (getf :base) l)
(.write (getf :base) (int \newline))
(if prefix
(.write (getf :base) prefix)))
(setf :buffering :writing)
(last lines))))))
(defn- write-white-space [^Writer this]
(if-let [^String tws (getf :trailing-white-space)]
(dosync
(.write (getf :base) tws)
(setf :trailing-white-space nil))))
(defn- p-write-char [^Writer this ^Integer c]
(if (= (getf :mode) :writing)
(do
(write-white-space this)
(.write (getf :base) c))
(if (= c \newline)
(write-initial-lines this "\n")
(let [oldpos (getf :pos)
newpos (inc oldpos)]
(dosync
(setf :pos newpos)
(add-to-buffer this (make-buffer-blob (str (char c)) nil oldpos newpos)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Initialize the pretty-writer instance
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- pretty-writer [writer max-columns miser-width]
(let [lb (struct logical-block nil nil (ref 0) (ref 0) (ref false) (ref false))
fields (ref {:pretty-writer true
:base (column-writer writer max-columns)
:logical-blocks lb
:sections nil
:mode :writing
:buffer []
:buffer-block lb
:buffer-level 1
:miser-width miser-width
:trailing-white-space nil
:pos 0})]
(proxy [Writer IDeref] []
(deref [] fields)
(write
([x]
;; (prlabel write x (getf :mode))
(condp = (class x)
String
(let [^String s0 (write-initial-lines this x)
^String s (.replaceFirst s0 "\\s+$" "")
white-space (.substring s0 (count s))
mode (getf :mode)]
(dosync
(if (= mode :writing)
(do
(write-white-space this)
(.write (getf :base) s)
(setf :trailing-white-space white-space))
(let [oldpos (getf :pos)
newpos (+ oldpos (count s0))]
(setf :pos newpos)
(add-to-buffer this (make-buffer-blob s white-space oldpos newpos))))))
Integer
(p-write-char this x))))
(flush []
(if (= (getf :mode) :buffering)
(dosync
(write-tokens this (getf :buffer) true)
(setf :buffer []))
(write-white-space this)))
(close []
(.flush this)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Methods for pretty-writer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- start-block
[^Writer this
^String prefix ^String per-line-prefix ^String suffix]
(dosync
(let [lb (struct logical-block (getf :logical-blocks) nil (ref 0) (ref 0)
(ref false) (ref false)
prefix per-line-prefix suffix)]
(setf :logical-blocks lb)
(if (= (getf :mode) :writing)
(do
(write-white-space this)
(when-let [cb (getf :logical-block-callback)] (cb :start))
(if prefix
(.write (getf :base) prefix))
(let [col (get-column (getf :base))]
(ref-set (:start-col lb) col)
(ref-set (:indent lb) col)))
(let [oldpos (getf :pos)
newpos (+ oldpos (if prefix (count prefix) 0))]
(setf :pos newpos)
(add-to-buffer this (make-start-block-t lb oldpos newpos)))))))
(defn- end-block [^Writer this]
(dosync
(let [lb (getf :logical-blocks)
^String suffix (:suffix lb)]
(if (= (getf :mode) :writing)
(do
(write-white-space this)
(if suffix
(.write (getf :base) suffix))
(when-let [cb (getf :logical-block-callback)] (cb :end)))
(let [oldpos (getf :pos)
newpos (+ oldpos (if suffix (count suffix) 0))]
(setf :pos newpos)
(add-to-buffer this (make-end-block-t lb oldpos newpos))))
(setf :logical-blocks (:parent lb)))))
(defn- nl [^Writer this type]
(dosync
(setf :mode :buffering)
(let [pos (getf :pos)]
(add-to-buffer this (make-nl-t type (getf :logical-blocks) pos pos)))))
(defn- indent [^Writer this relative-to offset]
(dosync
(let [lb (getf :logical-blocks)]
(if (= (getf :mode) :writing)
(do
(write-white-space this)
(ref-set (:indent lb)
(+ offset (condp = relative-to
:block @(:start-col lb)
:current (get-column (getf :base))))))
(let [pos (getf :pos)]
(add-to-buffer this (make-indent-t lb relative-to offset pos pos)))))))
(defn- get-miser-width [^Writer this]
(getf :miser-width))
(defn- set-miser-width [^Writer this new-miser-width]
(dosync (setf :miser-width new-miser-width)))
(defn- set-logical-block-callback [^Writer this f]
(dosync (setf :logical-block-callback f)))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/pprint/utilities.clj 0000664 0000000 0000000 00000007044 11575623476 0024407 0 ustar 00root root 0000000 0000000 ;;; utilities.clj -- part of the pretty printer for Clojure
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; Author: Tom Faulhaber
;; April 3, 2009
;; This module implements some utility function used in formatting and pretty
;; printing. The functions here could go in a more general purpose library,
;; perhaps.
(in-ns 'clojure.pprint)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Helper functions for digesting formats in the various
;;; phases of their lives.
;;; These functions are actually pretty general.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- map-passing-context [func initial-context lis]
(loop [context initial-context
lis lis
acc []]
(if (empty? lis)
[acc context]
(let [this (first lis)
remainder (next lis)
[result new-context] (apply func [this context])]
(recur new-context remainder (conj acc result))))))
(defn- consume [func initial-context]
(loop [context initial-context
acc []]
(let [[result new-context] (apply func [context])]
(if (not result)
[acc new-context]
(recur new-context (conj acc result))))))
(defn- consume-while [func initial-context]
(loop [context initial-context
acc []]
(let [[result continue new-context] (apply func [context])]
(if (not continue)
[acc context]
(recur new-context (conj acc result))))))
(defn- unzip-map [m]
"Take a map that has pairs in the value slots and produce a pair of maps,
the first having all the first elements of the pairs and the second all
the second elements of the pairs"
[(into {} (for [[k [v1 v2]] m] [k v1]))
(into {} (for [[k [v1 v2]] m] [k v2]))])
(defn- tuple-map [m v1]
"For all the values, v, in the map, replace them with [v v1]"
(into {} (for [[k v] m] [k [v v1]])))
(defn- rtrim [s c]
"Trim all instances of c from the end of sequence s"
(let [len (count s)]
(if (and (pos? len) (= (nth s (dec (count s))) c))
(loop [n (dec len)]
(cond
(neg? n) ""
(not (= (nth s n) c)) (subs s 0 (inc n))
true (recur (dec n))))
s)))
(defn- ltrim [s c]
"Trim all instances of c from the beginning of sequence s"
(let [len (count s)]
(if (and (pos? len) (= (nth s 0) c))
(loop [n 0]
(if (or (= n len) (not (= (nth s n) c)))
(subs s n)
(recur (inc n))))
s)))
(defn- prefix-count [aseq val]
"Return the number of times that val occurs at the start of sequence aseq,
if val is a seq itself, count the number of times any element of val occurs at the
beginning of aseq"
(let [test (if (coll? val) (set val) #{val})]
(loop [pos 0]
(if (or (= pos (count aseq)) (not (test (nth aseq pos))))
pos
(recur (inc pos))))))
(defn- prerr [& args]
"Println to *err*"
(binding [*out* *err*]
(apply println args)))
(defmacro ^{:private true} prlabel [prefix arg & more-args]
"Print args to *err* in name = value format"
`(prerr ~@(cons (list 'quote prefix) (mapcat #(list (list 'quote %) "=" %)
(cons arg (seq more-args))))))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/repl.clj 0000664 0000000 0000000 00000005537 11575623476 0022027 0 ustar 00root root 0000000 0000000 ; Copyright (c) Chris Houser, Dec 2008. All rights reserved.
; The use and distribution terms for this software are covered by the
; Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
; which can be found in the file CPL.TXT at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Utilities meant to be used interactively at the REPL
(ns
#^{:author "Chris Houser, Christophe Grand, Stephen Gilardi, Michel Salim, Christophe Grande"
:doc "Utilities meant to be used interactively at the REPL"}
clojure.repl
(:import (java.io LineNumberReader InputStreamReader PushbackReader)
(clojure.lang RT Reflector)))
;; ----------------------------------------------------------------------
;; Examine Clojure functions (Vars, really)
(defn source-fn
"Returns a string of the source code for the given symbol, if it can
find it. This requires that the symbol resolve to a Var defined in
a namespace for which the .clj is in the classpath. Returns nil if
it can't find the source. For most REPL usage, 'source' is more
convenient.
Example: (source-fn 'filter)"
[x]
(when-let [v (resolve x)]
(when-let [filepath (:file (meta v))]
(when-let [strm (.getResourceAsStream (RT/baseLoader) filepath)]
(with-open [rdr (LineNumberReader. (InputStreamReader. strm))]
(dotimes [_ (dec (:line (meta v)))] (.readLine rdr))
(let [text (StringBuilder.)
pbr (proxy [PushbackReader] [rdr]
(read [] (let [i (proxy-super read)]
(.append text (char i))
i)))]
(read (PushbackReader. pbr))
(str text)))))))
(defmacro source
"Prints the source code for the given symbol, if it can find it.
This requires that the symbol resolve to a Var defined in a
namespace for which the .clj is in the classpath.
Example: (source filter)"
[n]
`(println (or (source-fn '~n) (str "Source not found"))))
(defn apropos
"Given a regular expression or stringable thing, return a seq of
all definitions in all currently-loaded namespaces that match the
str-or-pattern."
[str-or-pattern]
(let [matches? (if (instance? java.util.regex.Pattern str-or-pattern)
#(re-find str-or-pattern (str %))
#(.contains (str %) (str str-or-pattern)))]
(mapcat (fn [ns]
(filter matches? (keys (ns-publics ns))))
(all-ns))))
(defn dir-fn
"Returns a sorted seq of symbols naming public vars in
a namespace"
[ns]
(sort (map first (ns-publics (the-ns ns)))))
(defmacro dir
"Prints a sorted directory of public vars in a namespace"
[nsname]
`(doseq [v# (dir-fn '~nsname)]
(println v#)))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/set.clj 0000664 0000000 0000000 00000012151 11575623476 0021646 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns ^{:doc "Set operations such as union/intersection."
:author "Rich Hickey"}
clojure.set)
(defn- bubble-max-key [k coll]
"Move a maximal element of coll according to fn k (which returns a number)
to the front of coll."
(let [max (apply max-key k coll)]
(cons max (remove #(identical? max %) coll))))
(defn union
"Return a set that is the union of the input sets"
{:added "1.0"}
([] #{})
([s1] s1)
([s1 s2]
(if (< (count s1) (count s2))
(reduce conj s2 s1)
(reduce conj s1 s2)))
([s1 s2 & sets]
(let [bubbled-sets (bubble-max-key count (conj sets s2 s1))]
(reduce into (first bubbled-sets) (rest bubbled-sets)))))
(defn intersection
"Return a set that is the intersection of the input sets"
{:added "1.0"}
([s1] s1)
([s1 s2]
(if (< (count s2) (count s1))
(recur s2 s1)
(reduce (fn [result item]
(if (contains? s2 item)
result
(disj result item)))
s1 s1)))
([s1 s2 & sets]
(let [bubbled-sets (bubble-max-key #(- (count %)) (conj sets s2 s1))]
(reduce intersection (first bubbled-sets) (rest bubbled-sets)))))
(defn difference
"Return a set that is the first set without elements of the remaining sets"
{:added "1.0"}
([s1] s1)
([s1 s2]
(if (< (count s1) (count s2))
(reduce (fn [result item]
(if (contains? s2 item)
(disj result item)
result))
s1 s1)
(reduce disj s1 s2)))
([s1 s2 & sets]
(reduce difference s1 (conj sets s2))))
(defn select
"Returns a set of the elements for which pred is true"
{:added "1.0"}
[pred xset]
(reduce (fn [s k] (if (pred k) s (disj s k)))
xset xset))
(defn project
"Returns a rel of the elements of xrel with only the keys in ks"
{:added "1.0"}
[xrel ks]
(set (map #(select-keys % ks) xrel)))
(defn rename-keys
"Returns the map with the keys in kmap renamed to the vals in kmap"
{:added "1.0"}
[map kmap]
(reduce
(fn [m [old new]]
(if (and (not= old new)
(contains? m old))
(-> m (assoc new (get m old)) (dissoc old))
m))
map kmap))
(defn rename
"Returns a rel of the maps in xrel with the keys in kmap renamed to the vals in kmap"
{:added "1.0"}
[xrel kmap]
(set (map #(rename-keys % kmap) xrel)))
(defn index
"Returns a map of the distinct values of ks in the xrel mapped to a
set of the maps in xrel with the corresponding values of ks."
{:added "1.0"}
[xrel ks]
(reduce
(fn [m x]
(let [ik (select-keys x ks)]
(assoc m ik (conj (get m ik #{}) x))))
{} xrel))
(defn map-invert
"Returns the map with the vals mapped to the keys."
{:added "1.0"}
[m] (reduce (fn [m [k v]] (assoc m v k)) {} m))
(defn join
"When passed 2 rels, returns the rel corresponding to the natural
join. When passed an additional keymap, joins on the corresponding
keys."
{:added "1.0"}
([xrel yrel] ;natural join
(if (and (seq xrel) (seq yrel))
(let [ks (intersection (set (keys (first xrel))) (set (keys (first yrel))))
[r s] (if (<= (count xrel) (count yrel))
[xrel yrel]
[yrel xrel])
idx (index r ks)]
(reduce (fn [ret x]
(let [found (idx (select-keys x ks))]
(if found
(reduce #(conj %1 (merge %2 x)) ret found)
ret)))
#{} s))
#{}))
([xrel yrel km] ;arbitrary key mapping
(let [[r s k] (if (<= (count xrel) (count yrel))
[xrel yrel (map-invert km)]
[yrel xrel km])
idx (index r (vals k))]
(reduce (fn [ret x]
(let [found (idx (rename-keys (select-keys x (keys k)) k))]
(if found
(reduce #(conj %1 (merge %2 x)) ret found)
ret)))
#{} s))))
(defn subset?
"Is set1 a subset of set2?"
{:added "1.2",
:tag Boolean}
[set1 set2]
(and (<= (count set1) (count set2))
(every? set2 set1)))
(defn superset?
"Is set1 a superset of set2?"
{:added "1.2",
:tag Boolean}
[set1 set2]
(and (>= (count set1) (count set2))
(every? set1 set2)))
(comment
(refer 'set)
(def xs #{{:a 11 :b 1 :c 1 :d 4}
{:a 2 :b 12 :c 2 :d 6}
{:a 3 :b 3 :c 3 :d 8 :f 42}})
(def ys #{{:a 11 :b 11 :c 11 :e 5}
{:a 12 :b 11 :c 12 :e 3}
{:a 3 :b 3 :c 3 :e 7 }})
(join xs ys)
(join xs (rename ys {:b :yb :c :yc}) {:a :a})
(union #{:a :b :c} #{:c :d :e })
(difference #{:a :b :c} #{:c :d :e})
(intersection #{:a :b :c} #{:c :d :e})
(index ys [:b])
)
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/stacktrace.clj 0000664 0000000 0000000 00000004602 11575623476 0023201 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;;; stacktrace.clj: print Clojure-centric stack traces
;; by Stuart Sierra
;; January 6, 2009
(ns ^{:doc "Print stack traces oriented towards Clojure, not Java."
:author "Stuart Sierra"}
clojure.stacktrace)
(defn root-cause
"Returns the last 'cause' Throwable in a chain of Throwables."
{:added "1.1"}
[tr]
(if-let [cause (.getCause tr)]
(recur cause)
tr))
(defn print-trace-element
"Prints a Clojure-oriented view of one element in a stack trace."
{:added "1.1"}
[e]
(let [class (.getClassName e)
method (.getMethodName e)]
(let [match (re-matches #"^([A-Za-z0-9_.-]+)\$(\w+)__\d+$" class)]
(if (and match (= "invoke" method))
(apply printf "%s/%s" (rest match))
(printf "%s.%s" class method))))
(printf " (%s:%d)" (or (.getFileName e) "") (.getLineNumber e)))
(defn print-throwable
"Prints the class and message of a Throwable."
{:added "1.1"}
[tr]
(printf "%s: %s" (.getName (class tr)) (.getMessage tr)))
(defn print-stack-trace
"Prints a Clojure-oriented stack trace of tr, a Throwable.
Prints a maximum of n stack frames (default: unlimited).
Does not print chained exceptions (causes)."
{:added "1.1"}
([tr] (print-stack-trace tr nil))
([tr n]
(let [st (.getStackTrace tr)]
(print-throwable tr)
(newline)
(print " at ")
(print-trace-element (first st))
(newline)
(doseq [e (if (nil? n)
(rest st)
(take (dec n) (rest st)))]
(print " ")
(print-trace-element e)
(newline)))))
(defn print-cause-trace
"Like print-stack-trace but prints chained exceptions (causes)."
{:added "1.1"}
([tr] (print-cause-trace tr nil))
([tr n]
(print-stack-trace tr n)
(when-let [cause (.getCause tr)]
(print "Caused by: " )
(recur cause n))))
(defn e
"REPL utility. Prints a brief stack trace for the root cause of the
most recent exception."
{:added "1.1"}
[]
(print-stack-trace (root-cause *e) 8))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/string.clj 0000664 0000000 0000000 00000020006 11575623476 0022357 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns ^{:doc "Clojure String utilities
It is poor form to (:use clojure.string). Instead, use require
with :as to specify a prefix, e.g.
(ns your.namespace.here
(:require '[clojure.string :as str]))
Design notes for clojure.string:
1. Strings are objects (as opposed to sequences). As such, the
string being manipulated is the first argument to a function;
passing nil will result in a NullPointerException unless
documented otherwise. If you want sequence-y behavior instead,
use a sequence.
2. Functions are generally not lazy, and call straight to host
methods where those are available and efficient.
3. Functions take advantage of String implementation details to
write high-performing loop/recurs instead of using higher-order
functions. (This is not idiomatic in general-purpose application
code.)
4. When a function is documented to accept a string argument, it
will take any implementation of the correct *interface* on the
host platform. In Java, this is CharSequence, which is more
general than String. In ordinary usage you will almost always
pass concrete strings. If you are doing something unusual,
e.g. passing a mutable implementation of CharSequence, then
thead-safety is your responsibility."
:author "Stuart Sierra, Stuart Halloway, David Liebke"}
clojure.string
(:refer-clojure :exclude (replace reverse))
(:import (java.util.regex Pattern)
clojure.lang.LazilyPersistentVector))
(defn ^String reverse
"Returns s with its characters reversed."
{:added "1.2"}
[^CharSequence s]
(.toString (.reverse (StringBuilder. s))))
(defn- replace-by
[^CharSequence s re f]
(let [m (re-matcher re s)]
(let [buffer (StringBuffer. (.length s))]
(loop []
(if (.find m)
(do (.appendReplacement m buffer (f (re-groups m)))
(recur))
(do (.appendTail m buffer)
(.toString buffer)))))))
(defn ^String replace
"Replaces all instance of match with replacement in s.
match/replacement can be:
string / string
char / char
pattern / (string or function of match).
See also replace-first."
{:added "1.2"}
[^CharSequence s match replacement]
(let [s (.toString s)]
(cond
(instance? Character match) (.replace s ^Character match ^Character replacement)
(instance? CharSequence match) (.replace s ^CharSequence match ^CharSequence replacement)
(instance? Pattern match) (if (instance? CharSequence replacement)
(.replaceAll (re-matcher ^Pattern match s)
(.toString ^CharSequence replacement))
(replace-by s match replacement))
:else (throw (IllegalArgumentException. (str "Invalid match arg: " match))))))
(defn- replace-first-by
[^CharSequence s ^Pattern re f]
(let [m (re-matcher re s)]
(let [buffer (StringBuffer. (.length s))]
(if (.find m)
(let [rep (f (re-groups m))]
(.appendReplacement m buffer rep)
(.appendTail m buffer)
(str buffer))))))
(defn- replace-first-char
[^CharSequence s ^Character match replace]
(let [s (.toString s)
i (.indexOf s (int match))]
(if (= -1 i)
s
(str (subs s 0 i) replace (subs s (inc i))))))
(defn ^String replace-first
"Replaces the first instance of match with replacement in s.
match/replacement can be:
char / char
string / string
pattern / (string or function of match).
See also replace-all."
{:added "1.2"}
[^CharSequence s match replacement]
(let [s (.toString s)]
(cond
(instance? Character match)
(replace-first-char s match replacement)
(instance? CharSequence match)
(.replaceFirst s (Pattern/quote (.toString ^CharSequence match))
(.toString ^CharSequence replacement))
(instance? Pattern match)
(if (instance? CharSequence replacement)
(.replaceFirst (re-matcher ^Pattern match s)
(.toString ^CharSequence replacement))
(replace-first-by s match replacement))
:else (throw (IllegalArgumentException. (str "Invalid match arg: " match))))))
(defn ^String join
"Returns a string of all elements in coll, separated by
an optional separator. Like Perl's join."
{:added "1.2"}
([coll]
(apply str coll))
([separator [x & more]]
(loop [sb (StringBuilder. (str x))
more more
sep (str separator)]
(if more
(recur (-> sb (.append sep) (.append (str (first more))))
(next more)
sep)
(str sb)))))
(defn ^String capitalize
"Converts first character of the string to upper-case, all other
characters to lower-case."
{:added "1.2"}
[^CharSequence s]
(let [s (.toString s)]
(if (< (count s) 2)
(.toUpperCase s)
(str (.toUpperCase (subs s 0 1))
(.toLowerCase (subs s 1))))))
(defn ^String upper-case
"Converts string to all upper-case."
{:added "1.2"}
[^CharSequence s]
(.. s toString toUpperCase))
(defn ^String lower-case
"Converts string to all lower-case."
{:added "1.2"}
[^CharSequence s]
(.. s toString toLowerCase))
(defn split
"Splits string on a regular expression. Optional argument limit is
the maximum number of splits. Not lazy. Returns vector of the splits."
{:added "1.2"}
([^CharSequence s ^Pattern re]
(LazilyPersistentVector/createOwning (.split re s)))
([ ^CharSequence s ^Pattern re limit]
(LazilyPersistentVector/createOwning (.split re s limit))))
(defn split-lines
"Splits s on \\n or \\r\\n."
{:added "1.2"}
[^CharSequence s]
(split s #"\r?\n"))
(defn ^String trim
"Removes whitespace from both ends of string."
{:added "1.2"}
[^CharSequence s]
(.. s toString trim))
(defn ^String triml
"Removes whitespace from the left side of string."
{:added "1.2"}
[^CharSequence s]
(loop [index (int 0)]
(if (= (.length s) index)
""
(if (Character/isWhitespace (.charAt s index))
(recur (inc index))
(.. s (subSequence index (.length s)) toString)))))
(defn ^String trimr
"Removes whitespace from the right side of string."
{:added "1.2"}
[^CharSequence s]
(loop [index (.length s)]
(if (zero? index)
""
(if (Character/isWhitespace (.charAt s (dec index)))
(recur (dec index))
(.. s (subSequence 0 index) toString)))))
(defn ^String trim-newline
"Removes all trailing newline \\n or return \\r characters from
string. Similar to Perl's chomp."
{:added "1.2"}
[^CharSequence s]
(loop [index (.length s)]
(if (zero? index)
""
(let [ch (.charAt s (dec index))]
(if (or (= ch \newline) (= ch \return))
(recur (dec index))
(.. s (subSequence 0 index) toString))))))
(defn blank?
"True if s is nil, empty, or contains only whitespace."
{:added "1.2"}
[^CharSequence s]
(if s
(loop [index (int 0)]
(if (= (.length s) index)
true
(if (Character/isWhitespace (.charAt s index))
(recur (inc index))
false)))
true))
(defn ^String escape
"Return a new string, using cmap to escape each character ch
from s as follows:
If (cmap ch) is nil, append ch to the new string.
If (cmap ch) is non-nil, append (str (cmap ch)) instead."
{:added "1.2"}
[^CharSequence s cmap]
(loop [index (int 0)
buffer (StringBuilder. (.length s))]
(if (= (.length s) index)
(.toString buffer)
(let [ch (.charAt s index)]
(if-let [replacement (cmap ch)]
(.append buffer replacement)
(.append buffer ch))
(recur (inc index) buffer)))))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/template.clj 0000664 0000000 0000000 00000003647 11575623476 0022700 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;;; template.clj - anonymous functions that pre-evaluate sub-expressions
;; By Stuart Sierra
;; June 23, 2009
;; CHANGE LOG
;;
;; June 23, 2009: complete rewrite, eliminated _1,_2,... argument
;; syntax
;;
;; January 20, 2009: added "template?" and checks for valid template
;; expressions.
;;
;; December 15, 2008: first version
(ns ^{:doc "Macros that expand to repeated copies of a template expression."
:author "Stuart Sierra"}
clojure.template
(:require [clojure.walk :as walk]))
(defn apply-template
"For use in macros. argv is an argument list, as in defn. expr is
a quoted expression using the symbols in argv. values is a sequence
of values to be used for the arguments.
apply-template will recursively replace argument symbols in expr
with their corresponding values, returning a modified expr.
Example: (apply-template '[x] '(+ x x) '[2])
;=> (+ 2 2)"
[argv expr values]
(assert (vector? argv))
(assert (every? symbol? argv))
(walk/prewalk-replace (zipmap argv values) expr))
(defmacro do-template
"Repeatedly copies expr (in a do block) for each group of arguments
in values. values are automatically partitioned by the number of
arguments in argv, an argument vector as in defn.
Example: (macroexpand '(do-template [x y] (+ y x) 2 4 3 5))
;=> (do (+ 4 2) (+ 5 3))"
[argv expr & values]
(let [c (count argv)]
`(do ~@(map (fn [a] (apply-template argv expr a))
(partition c values)))))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/test.clj 0000664 0000000 0000000 00000060231 11575623476 0022034 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;;; test.clj: test framework for Clojure
;; by Stuart Sierra
;; March 28, 2009
;; Thanks to Chas Emerick, Allen Rohner, and Stuart Halloway for
;; contributions and suggestions.
(ns
^{:author "Stuart Sierra, with contributions and suggestions by
Chas Emerick, Allen Rohner, and Stuart Halloway",
:doc "A unit testing framework.
ASSERTIONS
The core of the library is the \"is\" macro, which lets you make
assertions of any arbitrary expression:
(is (= 4 (+ 2 2)))
(is (instance? Integer 256))
(is (.startsWith \"abcde\" \"ab\"))
You can type an \"is\" expression directly at the REPL, which will
print a message if it fails.
user> (is (= 5 (+ 2 2)))
FAIL in (:1)
expected: (= 5 (+ 2 2))
actual: (not (= 5 4))
false
The \"expected:\" line shows you the original expression, and the
\"actual:\" shows you what actually happened. In this case, it
shows that (+ 2 2) returned 4, which is not = to 5. Finally, the
\"false\" on the last line is the value returned from the
expression. The \"is\" macro always returns the result of the
inner expression.
There are two special assertions for testing exceptions. The
\"(is (thrown? c ...))\" form tests if an exception of class c is
thrown:
(is (thrown? ArithmeticException (/ 1 0)))
\"(is (thrown-with-msg? c re ...))\" does the same thing and also
tests that the message on the exception matches the regular
expression re:
(is (thrown-with-msg? ArithmeticException #\"Divide by zero\"
(/ 1 0)))
DOCUMENTING TESTS
\"is\" takes an optional second argument, a string describing the
assertion. This message will be included in the error report.
(is (= 5 (+ 2 2)) \"Crazy arithmetic\")
In addition, you can document groups of assertions with the
\"testing\" macro, which takes a string followed by any number of
assertions. The string will be included in failure reports.
Calls to \"testing\" may be nested, and all of the strings will be
joined together with spaces in the final report, in a style
similar to RSpec
(testing \"Arithmetic\"
(testing \"with positive integers\"
(is (= 4 (+ 2 2)))
(is (= 7 (+ 3 4))))
(testing \"with negative integers\"
(is (= -4 (+ -2 -2)))
(is (= -1 (+ 3 -4)))))
Note that, unlike RSpec, the \"testing\" macro may only be used
INSIDE a \"deftest\" or \"with-test\" form (see below).
DEFINING TESTS
There are two ways to define tests. The \"with-test\" macro takes
a defn or def form as its first argument, followed by any number
of assertions. The tests will be stored as metadata on the
definition.
(with-test
(defn my-function [x y]
(+ x y))
(is (= 4 (my-function 2 2)))
(is (= 7 (my-function 3 4))))
As of Clojure SVN rev. 1221, this does not work with defmacro.
See http://code.google.com/p/clojure/issues/detail?id=51
The other way lets you define tests separately from the rest of
your code, even in a different namespace:
(deftest addition
(is (= 4 (+ 2 2)))
(is (= 7 (+ 3 4))))
(deftest subtraction
(is (= 1 (- 4 3)))
(is (= 3 (- 7 4))))
This creates functions named \"addition\" and \"subtraction\", which
can be called like any other function. Therefore, tests can be
grouped and composed, in a style similar to the test framework in
Peter Seibel's \"Practical Common Lisp\"
(deftest arithmetic
(addition)
(subtraction))
The names of the nested tests will be joined in a list, like
\"(arithmetic addition)\", in failure reports. You can use nested
tests to set up a context shared by several tests.
RUNNING TESTS
Run tests with the function \"(run-tests namespaces...)\":
(run-tests 'your.namespace 'some.other.namespace)
If you don't specify any namespaces, the current namespace is
used. To run all tests in all namespaces, use \"(run-all-tests)\".
By default, these functions will search for all tests defined in
a namespace and run them in an undefined order. However, if you
are composing tests, as in the \"arithmetic\" example above, you
probably do not want the \"addition\" and \"subtraction\" tests run
separately. In that case, you must define a special function
named \"test-ns-hook\" that runs your tests in the correct order:
(defn test-ns-hook []
(arithmetic))
OMITTING TESTS FROM PRODUCTION CODE
You can bind the variable \"*load-tests*\" to false when loading or
compiling code in production. This will prevent any tests from
being created by \"with-test\" or \"deftest\".
FIXTURES (new)
Fixtures allow you to run code before and after tests, to set up
the context in which tests should be run.
A fixture is just a function that calls another function passed as
an argument. It looks like this:
(defn my-fixture [f]
Perform setup, establish bindings, whatever.
(f) Then call the function we were passed.
Tear-down / clean-up code here.
)
Fixtures are attached to namespaces in one of two ways. \"each\"
fixtures are run repeatedly, once for each test function created
with \"deftest\" or \"with-test\". \"each\" fixtures are useful for
establishing a consistent before/after state for each test, like
clearing out database tables.
\"each\" fixtures can be attached to the current namespace like this:
(use-fixtures :each fixture1 fixture2 ...)
The fixture1, fixture2 are just functions like the example above.
They can also be anonymous functions, like this:
(use-fixtures :each (fn [f] setup... (f) cleanup...))
The other kind of fixture, a \"once\" fixture, is only run once,
around ALL the tests in the namespace. \"once\" fixtures are useful
for tasks that only need to be performed once, like establishing
database connections, or for time-consuming tasks.
Attach \"once\" fixtures to the current namespace like this:
(use-fixtures :once fixture1 fixture2 ...)
SAVING TEST OUTPUT TO A FILE
All the test reporting functions write to the var *test-out*. By
default, this is the same as *out*, but you can rebind it to any
PrintWriter. For example, it could be a file opened with
clojure.java.io/writer.
EXTENDING TEST-IS (ADVANCED)
You can extend the behavior of the \"is\" macro by defining new
methods for the \"assert-expr\" multimethod. These methods are
called during expansion of the \"is\" macro, so they should return
quoted forms to be evaluated.
You can plug in your own test-reporting framework by rebinding
the \"report\" function: (report event)
The 'event' argument is a map. It will always have a :type key,
whose value will be a keyword signaling the type of event being
reported. Standard events with :type value of :pass, :fail, and
:error are called when an assertion passes, fails, and throws an
exception, respectively. In that case, the event will also have
the following keys:
:expected The form that was expected to be true
:actual A form representing what actually occurred
:message The string message given as an argument to 'is'
The \"testing\" strings will be a list in \"*testing-contexts*\", and
the vars being tested will be a list in \"*testing-vars*\".
Your \"report\" function should wrap any printing calls in the
\"with-test-out\" macro, which rebinds *out* to the current value
of *test-out*.
For additional event types, see the examples in the code.
"}
clojure.test
(:require [clojure.template :as temp]
[clojure.stacktrace :as stack]))
;; Nothing is marked "private" here, so you can rebind things to plug
;; in your own testing or reporting frameworks.
;;; USER-MODIFIABLE GLOBALS
(defonce
^{:doc "True by default. If set to false, no test functions will
be created by deftest, set-test, or with-test. Use this to omit
tests when compiling or loading production code."
:added "1.1"}
*load-tests* true)
(def
^{:doc "The maximum depth of stack traces to print when an Exception
is thrown during a test. Defaults to nil, which means print the
complete stack trace."
:added "1.1"}
*stack-trace-depth* nil)
;;; GLOBALS USED BY THE REPORTING FUNCTIONS
(def *report-counters* nil) ; bound to a ref of a map in test-ns
(def *initial-report-counters* ; used to initialize *report-counters*
{:test 0, :pass 0, :fail 0, :error 0})
(def *testing-vars* (list)) ; bound to hierarchy of vars being tested
(def *testing-contexts* (list)) ; bound to hierarchy of "testing" strings
(def *test-out* *out*) ; PrintWriter for test reporting output
(defmacro with-test-out
"Runs body with *out* bound to the value of *test-out*."
{:added "1.1"}
[& body]
`(binding [*out* *test-out*]
~@body))
;;; UTILITIES FOR REPORTING FUNCTIONS
(defn file-position
"Returns a vector [filename line-number] for the nth call up the
stack.
Deprecated in 1.2: The information needed for test reporting is
now on :file and :line keys in the result map."
{:added "1.1"
:deprecated "1.2"}
[n]
(let [^StackTraceElement s (nth (.getStackTrace (new java.lang.Throwable)) n)]
[(.getFileName s) (.getLineNumber s)]))
(defn testing-vars-str
"Returns a string representation of the current test. Renders names
in *testing-vars* as a list, then the source file and line of
current assertion."
{:added "1.1"}
[m]
(let [{:keys [file line]} m]
(str
;; Uncomment to include namespace in failure report:
;;(ns-name (:ns (meta (first *testing-vars*)))) "/ "
(reverse (map #(:name (meta %)) *testing-vars*))
" (" file ":" line ")")))
(defn testing-contexts-str
"Returns a string representation of the current test context. Joins
strings in *testing-contexts* with spaces."
{:added "1.1"}
[]
(apply str (interpose " " (reverse *testing-contexts*))))
(defn inc-report-counter
"Increments the named counter in *report-counters*, a ref to a map.
Does nothing if *report-counters* is nil."
{:added "1.1"}
[name]
(when *report-counters*
(dosync (commute *report-counters* assoc name
(inc (or (*report-counters* name) 0))))))
;;; TEST RESULT REPORTING
(defmulti
^{:doc "Generic reporting function, may be overridden to plug in
different report formats (e.g., TAP, JUnit). Assertions such as
'is' call 'report' to indicate results. The argument given to
'report' will be a map with a :type key. See the documentation at
the top of test_is.clj for more information on the types of
arguments for 'report'."
:dynamic true
:added "1.1"}
report :type)
(defn- file-and-line
[exception depth]
(let [^StackTraceElement s (nth (.getStackTrace exception) depth)]
{:file (.getFileName s) :line (.getLineNumber s)}))
(defn do-report
"Add file and line information to a test result and call report.
If you are writing a custom assert-expr method, call this function
to pass test results to report."
{:added "1.2"}
[m]
(report
(case
(:type m)
:fail (merge (file-and-line (new java.lang.Throwable) 1) m)
:error (merge (file-and-line (:actual m) 0) m)
m)))
(defmethod report :default [m]
(with-test-out (prn m)))
(defmethod report :pass [m]
(with-test-out (inc-report-counter :pass)))
(defmethod report :fail [m]
(with-test-out
(inc-report-counter :fail)
(println "\nFAIL in" (testing-vars-str m))
(when (seq *testing-contexts*) (println (testing-contexts-str)))
(when-let [message (:message m)] (println message))
(println "expected:" (pr-str (:expected m)))
(println " actual:" (pr-str (:actual m)))))
(defmethod report :error [m]
(with-test-out
(inc-report-counter :error)
(println "\nERROR in" (testing-vars-str m))
(when (seq *testing-contexts*) (println (testing-contexts-str)))
(when-let [message (:message m)] (println message))
(println "expected:" (pr-str (:expected m)))
(print " actual: ")
(let [actual (:actual m)]
(if (instance? Throwable actual)
(stack/print-cause-trace actual *stack-trace-depth*)
(prn actual)))))
(defmethod report :summary [m]
(with-test-out
(println "\nRan" (:test m) "tests containing"
(+ (:pass m) (:fail m) (:error m)) "assertions.")
(println (:fail m) "failures," (:error m) "errors.")))
(defmethod report :begin-test-ns [m]
(with-test-out
(println "\nTesting" (ns-name (:ns m)))))
;; Ignore these message types:
(defmethod report :end-test-ns [m])
(defmethod report :begin-test-var [m])
(defmethod report :end-test-var [m])
;;; UTILITIES FOR ASSERTIONS
(defn get-possibly-unbound-var
"Like var-get but returns nil if the var is unbound."
{:added "1.1"}
[v]
(try (var-get v)
(catch IllegalStateException e
nil)))
(defn function?
"Returns true if argument is a function or a symbol that resolves to
a function (not a macro)."
{:added "1.1"}
[x]
(if (symbol? x)
(when-let [v (resolve x)]
(when-let [value (get-possibly-unbound-var v)]
(and (fn? value)
(not (:macro (meta v))))))
(fn? x)))
(defn assert-predicate
"Returns generic assertion code for any functional predicate. The
'expected' argument to 'report' will contains the original form, the
'actual' argument will contain the form with all its sub-forms
evaluated. If the predicate returns false, the 'actual' form will
be wrapped in (not...)."
{:added "1.1"}
[msg form]
(let [args (rest form)
pred (first form)]
`(let [values# (list ~@args)
result# (apply ~pred values#)]
(if result#
(do-report {:type :pass, :message ~msg,
:expected '~form, :actual (cons ~pred values#)})
(do-report {:type :fail, :message ~msg,
:expected '~form, :actual (list '~'not (cons '~pred values#))}))
result#)))
(defn assert-any
"Returns generic assertion code for any test, including macros, Java
method calls, or isolated symbols."
{:added "1.1"}
[msg form]
`(let [value# ~form]
(if value#
(do-report {:type :pass, :message ~msg,
:expected '~form, :actual value#})
(do-report {:type :fail, :message ~msg,
:expected '~form, :actual value#}))
value#))
;;; ASSERTION METHODS
;; You don't call these, but you can add methods to extend the 'is'
;; macro. These define different kinds of tests, based on the first
;; symbol in the test expression.
(defmulti assert-expr
(fn [msg form]
(cond
(nil? form) :always-fail
(seq? form) (first form)
:else :default)))
(defmethod assert-expr :always-fail [msg form]
;; nil test: always fail
`(do-report {:type :fail, :message ~msg}))
(defmethod assert-expr :default [msg form]
(if (and (sequential? form) (function? (first form)))
(assert-predicate msg form)
(assert-any msg form)))
(defmethod assert-expr 'instance? [msg form]
;; Test if x is an instance of y.
`(let [klass# ~(nth form 1)
object# ~(nth form 2)]
(let [result# (instance? klass# object#)]
(if result#
(do-report {:type :pass, :message ~msg,
:expected '~form, :actual (class object#)})
(do-report {:type :fail, :message ~msg,
:expected '~form, :actual (class object#)}))
result#)))
(defmethod assert-expr 'thrown? [msg form]
;; (is (thrown? c expr))
;; Asserts that evaluating expr throws an exception of class c.
;; Returns the exception thrown.
(let [klass (second form)
body (nthnext form 2)]
`(try ~@body
(do-report {:type :fail, :message ~msg,
:expected '~form, :actual nil})
(catch ~klass e#
(do-report {:type :pass, :message ~msg,
:expected '~form, :actual e#})
e#))))
(defmethod assert-expr 'thrown-with-msg? [msg form]
;; (is (thrown-with-msg? c re expr))
;; Asserts that evaluating expr throws an exception of class c.
;; Also asserts that the message string of the exception matches
;; (with re-find) the regular expression re.
(let [klass (nth form 1)
re (nth form 2)
body (nthnext form 3)]
`(try ~@body
(do-report {:type :fail, :message ~msg, :expected '~form, :actual nil})
(catch ~klass e#
(let [m# (.getMessage e#)]
(if (re-find ~re m#)
(do-report {:type :pass, :message ~msg,
:expected '~form, :actual e#})
(do-report {:type :fail, :message ~msg,
:expected '~form, :actual e#})))
e#))))
(defmacro try-expr
"Used by the 'is' macro to catch unexpected exceptions.
You don't call this."
{:added "1.1"}
[msg form]
`(try ~(assert-expr msg form)
(catch Throwable t#
(do-report {:type :error, :message ~msg,
:expected '~form, :actual t#}))))
;;; ASSERTION MACROS
;; You use these in your tests.
(defmacro is
"Generic assertion macro. 'form' is any predicate test.
'msg' is an optional message to attach to the assertion.
Example: (is (= 4 (+ 2 2)) \"Two plus two should be 4\")
Special forms:
(is (thrown? c body)) checks that an instance of c is thrown from
body, fails if not; then returns the thing thrown.
(is (thrown-with-msg? c re body)) checks that an instance of c is
thrown AND that the message on the exception matches (with
re-find) the regular expression re."
{:added "1.1"}
([form] `(is ~form nil))
([form msg] `(try-expr ~msg ~form)))
(defmacro are
"Checks multiple assertions with a template expression.
See clojure.template/do-template for an explanation of
templates.
Example: (are [x y] (= x y)
2 (+ 1 1)
4 (* 2 2))
Expands to:
(do (is (= 2 (+ 1 1)))
(is (= 4 (* 2 2))))
Note: This breaks some reporting features, such as line numbers."
{:added "1.1"}
[argv expr & args]
`(temp/do-template ~argv (is ~expr) ~@args))
(defmacro testing
"Adds a new string to the list of testing contexts. May be nested,
but must occur inside a test function (deftest)."
{:added "1.1"}
[string & body]
`(binding [*testing-contexts* (conj *testing-contexts* ~string)]
~@body))
;;; DEFINING TESTS
(defmacro with-test
"Takes any definition form (that returns a Var) as the first argument.
Remaining body goes in the :test metadata function for that Var.
When *load-tests* is false, only evaluates the definition, ignoring
the tests."
{:added "1.1"}
[definition & body]
(if *load-tests*
`(doto ~definition (alter-meta! assoc :test (fn [] ~@body)))
definition))
(defmacro deftest
"Defines a test function with no arguments. Test functions may call
other tests, so tests may be composed. If you compose tests, you
should also define a function named test-ns-hook; run-tests will
call test-ns-hook instead of testing all vars.
Note: Actually, the test body goes in the :test metadata on the var,
and the real function (the value of the var) calls test-var on
itself.
When *load-tests* is false, deftest is ignored."
{:added "1.1"}
[name & body]
(when *load-tests*
`(def ~(vary-meta name assoc :test `(fn [] ~@body))
(fn [] (test-var (var ~name))))))
(defmacro deftest-
"Like deftest but creates a private var."
{:added "1.1"}
[name & body]
(when *load-tests*
`(def ~(vary-meta name assoc :test `(fn [] ~@body) :private true)
(fn [] (test-var (var ~name))))))
(defmacro set-test
"Experimental.
Sets :test metadata of the named var to a fn with the given body.
The var must already exist. Does not modify the value of the var.
When *load-tests* is false, set-test is ignored."
{:added "1.1"}
[name & body]
(when *load-tests*
`(alter-meta! (var ~name) assoc :test (fn [] ~@body))))
;;; DEFINING FIXTURES
(defn- add-ns-meta
"Adds elements in coll to the current namespace metadata as the
value of key."
{:added "1.1"}
[key coll]
(alter-meta! *ns* assoc key coll))
(defmulti use-fixtures
"Wrap test runs in a fixture function to perform setup and
teardown. Using a fixture-type of :each wraps every test
individually, while:once wraps the whole run in a single function."
{:added "1.1"}
(fn [fixture-type & args] fixture-type))
(defmethod use-fixtures :each [fixture-type & args]
(add-ns-meta ::each-fixtures args))
(defmethod use-fixtures :once [fixture-type & args]
(add-ns-meta ::once-fixtures args))
(defn- default-fixture
"The default, empty, fixture function. Just calls its argument."
{:added "1.1"}
[f]
(f))
(defn compose-fixtures
"Composes two fixture functions, creating a new fixture function
that combines their behavior."
{:added "1.1"}
[f1 f2]
(fn [g] (f1 (fn [] (f2 g)))))
(defn join-fixtures
"Composes a collection of fixtures, in order. Always returns a valid
fixture function, even if the collection is empty."
{:added "1.1"}
[fixtures]
(reduce compose-fixtures default-fixture fixtures))
;;; RUNNING TESTS: LOW-LEVEL FUNCTIONS
(defn test-var
"If v has a function in its :test metadata, calls that function,
with *testing-vars* bound to (conj *testing-vars* v)."
{:dynamic true, :added "1.1"}
[v]
(when-let [t (:test (meta v))]
(binding [*testing-vars* (conj *testing-vars* v)]
(do-report {:type :begin-test-var, :var v})
(inc-report-counter :test)
(try (t)
(catch Throwable e
(do-report {:type :error, :message "Uncaught exception, not in assertion."
:expected nil, :actual e})))
(do-report {:type :end-test-var, :var v}))))
(defn test-all-vars
"Calls test-var on every var interned in the namespace, with fixtures."
{:added "1.1"}
[ns]
(let [once-fixture-fn (join-fixtures (::once-fixtures (meta ns)))
each-fixture-fn (join-fixtures (::each-fixtures (meta ns)))]
(once-fixture-fn
(fn []
(doseq [v (vals (ns-interns ns))]
(when (:test (meta v))
(each-fixture-fn (fn [] (test-var v)))))))))
(defn test-ns
"If the namespace defines a function named test-ns-hook, calls that.
Otherwise, calls test-all-vars on the namespace. 'ns' is a
namespace object or a symbol.
Internally binds *report-counters* to a ref initialized to
*inital-report-counters*. Returns the final, dereferenced state of
*report-counters*."
{:added "1.1"}
[ns]
(binding [*report-counters* (ref *initial-report-counters*)]
(let [ns-obj (the-ns ns)]
(do-report {:type :begin-test-ns, :ns ns-obj})
;; If the namespace has a test-ns-hook function, call that:
(if-let [v (find-var (symbol (str (ns-name ns-obj)) "test-ns-hook"))]
((var-get v))
;; Otherwise, just test every var in the namespace.
(test-all-vars ns-obj))
(do-report {:type :end-test-ns, :ns ns-obj}))
@*report-counters*))
;;; RUNNING TESTS: HIGH-LEVEL FUNCTIONS
(defn run-tests
"Runs all tests in the given namespaces; prints results.
Defaults to current namespace if none given. Returns a map
summarizing test results."
{:added "1.1"}
([] (run-tests *ns*))
([& namespaces]
(let [summary (assoc (apply merge-with + (map test-ns namespaces))
:type :summary)]
(do-report summary)
summary)))
(defn run-all-tests
"Runs all tests in all namespaces; prints results.
Optional argument is a regular expression; only namespaces with
names matching the regular expression (with re-matches) will be
tested."
{:added "1.1"}
([] (apply run-tests (all-ns)))
([re] (apply run-tests (filter #(re-matches re (name (ns-name %))) (all-ns)))))
(defn successful?
"Returns true if the given test summary indicates all tests
were successful, false otherwise."
{:added "1.1"}
[summary]
(and (zero? (:fail summary 0))
(zero? (:error summary 0))))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/test/ 0000775 0000000 0000000 00000000000 11575623476 0021340 5 ustar 00root root 0000000 0000000 clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/test/junit.clj 0000664 0000000 0000000 00000012377 11575623476 0023175 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;; test/junit.clj: Extension to clojure.test for JUnit-compatible XML output
;; by Jason Sankey
;; June 2009
;; DOCUMENTATION
;;
(ns ^{:doc "clojure.test extension for JUnit-compatible XML output.
JUnit (http://junit.org/) is the most popular unit-testing library
for Java. As such, tool support for JUnit output formats is
common. By producing compatible output from tests, this tool
support can be exploited.
To use, wrap any calls to clojure.test/run-tests in the
with-junit-output macro, like this:
(use 'clojure.test)
(use 'clojure.test.junit)
(with-junit-output
(run-tests 'my.cool.library))
To write the output to a file, rebind clojure.test/*test-out* to
your own PrintWriter (perhaps opened using
clojure.java.io/writer)."
:author "Jason Sankey"}
clojure.test.junit
(:require [clojure.stacktrace :as stack]
[clojure.test :as t]))
;; copied from clojure.contrib.lazy-xml
(def ^{:private true}
escape-xml-map
(zipmap "'<>\"&" (map #(str \& % \;) '[apos lt gt quot amp])))
(defn- escape-xml [text]
(apply str (map #(escape-xml-map % %) text)))
(def *var-context*)
(def *depth*)
(defn indent
[]
(dotimes [n (* *depth* 4)] (print " ")))
(defn start-element
[tag pretty & [attrs]]
(if pretty (indent))
(print (str "<" tag))
(if (seq attrs)
(doseq [[key value] attrs]
(print (str " " (name key) "=\"" (escape-xml value) "\""))))
(print ">")
(if pretty (println))
(set! *depth* (inc *depth*)))
(defn element-content
[content]
(print (escape-xml content)))
(defn finish-element
[tag pretty]
(set! *depth* (dec *depth*))
(if pretty (indent))
(print (str "" tag ">"))
(if pretty (println)))
(defn test-name
[vars]
(apply str (interpose "."
(reverse (map #(:name (meta %)) vars)))))
(defn package-class
[name]
(let [i (.lastIndexOf name ".")]
(if (< i 0)
[nil name]
[(.substring name 0 i) (.substring name (+ i 1))])))
(defn start-case
[name classname]
(start-element 'testcase true {:name name :classname classname}))
(defn finish-case
[]
(finish-element 'testcase true))
(defn suite-attrs
[package classname]
(let [attrs {:name classname}]
(if package
(assoc attrs :package package)
attrs)))
(defn start-suite
[name]
(let [[package classname] (package-class name)]
(start-element 'testsuite true (suite-attrs package classname))))
(defn finish-suite
[]
(finish-element 'testsuite true))
(defn message-el
[tag message expected-str actual-str]
(indent)
(start-element tag false (if message {:message message} {}))
(element-content
(let [[file line] (t/file-position 5)
detail (apply str (interpose
"\n"
[(str "expected: " expected-str)
(str " actual: " actual-str)
(str " at: " file ":" line)]))]
(if message (str message "\n" detail) detail)))
(finish-element tag false)
(println))
(defn failure-el
[message expected actual]
(message-el 'failure message (pr-str expected) (pr-str actual)))
(defn error-el
[message expected actual]
(message-el 'error
message
(pr-str expected)
(if (instance? Throwable actual)
(with-out-str (stack/print-cause-trace actual t/*stack-trace-depth*))
(prn actual))))
;; This multimethod will override test-is/report
(defmulti junit-report :type)
(defmethod junit-report :begin-test-ns [m]
(t/with-test-out
(start-suite (name (ns-name (:ns m))))))
(defmethod junit-report :end-test-ns [_]
(t/with-test-out
(finish-suite)))
(defmethod junit-report :begin-test-var [m]
(t/with-test-out
(let [var (:var m)]
(binding [*var-context* (conj *var-context* var)]
(start-case (test-name *var-context*) (name (ns-name (:ns (meta var)))))))))
(defmethod junit-report :end-test-var [m]
(t/with-test-out
(finish-case)))
(defmethod junit-report :pass [m]
(t/with-test-out
(t/inc-report-counter :pass)))
(defmethod junit-report :fail [m]
(t/with-test-out
(t/inc-report-counter :fail)
(failure-el (:message m)
(:expected m)
(:actual m))))
(defmethod junit-report :error [m]
(t/with-test-out
(t/inc-report-counter :error)
(error-el (:message m)
(:expected m)
(:actual m))))
(defmethod junit-report :default [_])
(defmacro with-junit-output
"Execute body with modified test-is reporting functions that write
JUnit-compatible XML output."
{:added "1.1"}
[& body]
`(binding [t/report junit-report
*var-context* (list)
*depth* 1]
(println "")
(println "")
(let [result# ~@body]
(println "")
result#)))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/test/tap.clj 0000664 0000000 0000000 00000006701 11575623476 0022622 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;;; test_is/tap.clj: Extension to test for TAP output
;; by Stuart Sierra
;; March 31, 2009
;; Inspired by ClojureCheck by Meikel Brandmeyer:
;; http://kotka.de/projects/clojure/clojurecheck.html
;; DOCUMENTATION
;;
(ns ^{:doc "clojure.test extensions for the Test Anything Protocol (TAP)
TAP is a simple text-based syntax for reporting test results. TAP
was originally develped for Perl, and now has implementations in
several languages. For more information on TAP, see
http://testanything.org/ and
http://search.cpan.org/~petdance/TAP-1.0.0/TAP.pm
To use this library, wrap any calls to
clojure.test/run-tests in the with-tap-output macro,
like this:
(use 'clojure.test)
(use 'clojure.test.tap)
(with-tap-output
(run-tests 'my.cool.library))"
:author "Stuart Sierra"}
clojure.test.tap
(:require [clojure.test :as t]
[clojure.stacktrace :as stack]))
(defn print-tap-plan
"Prints a TAP plan line like '1..n'. n is the number of tests"
{:added "1.1"}
[n]
(println (str "1.." n)))
(defn print-tap-diagnostic
"Prints a TAP diagnostic line. data is a (possibly multi-line)
string."
{:added "1.1"}
[data]
(doseq [line (.split ^String data "\n")]
(println "#" line)))
(defn print-tap-pass
"Prints a TAP 'ok' line. msg is a string, with no line breaks"
{:added "1.1"}
[msg]
(println "ok" msg))
(defn print-tap-fail
"Prints a TAP 'not ok' line. msg is a string, with no line breaks"
{:added "1.1"}
[msg]
(println "not ok" msg))
;; This multimethod will override test/report
(defmulti tap-report (fn [data] (:type data)))
(defmethod tap-report :default [data]
(t/with-test-out
(print-tap-diagnostic (pr-str data))))
(defmethod tap-report :pass [data]
(t/with-test-out
(t/inc-report-counter :pass)
(print-tap-pass (t/testing-vars-str))
(when (seq t/*testing-contexts*)
(print-tap-diagnostic (t/testing-contexts-str)))
(when (:message data)
(print-tap-diagnostic (:message data)))
(print-tap-diagnostic (str "expected:" (pr-str (:expected data))))
(print-tap-diagnostic (str " actual:" (pr-str (:actual data))))))
(defmethod tap-report :error [data]
(t/with-test-out
(t/inc-report-counter :error)
(print-tap-fail (t/testing-vars-str))
(when (seq t/*testing-contexts*)
(print-tap-diagnostic (t/testing-contexts-str)))
(when (:message data)
(print-tap-diagnostic (:message data)))
(print-tap-diagnostic "expected:" (pr-str (:expected data)))
(print-tap-diagnostic " actual: ")
(print-tap-diagnostic
(with-out-str
(if (instance? Throwable (:actual data))
(stack/print-cause-trace (:actual data) t/*stack-trace-depth*)
(prn (:actual data)))))))
(defmethod tap-report :summary [data]
(t/with-test-out
(print-tap-plan (+ (:pass data) (:fail data) (:error data)))))
(defmacro with-tap-output
"Execute body with modified test reporting functions that produce
TAP output"
{:added "1.1"}
[& body]
`(binding [t/report tap-report]
~@body))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/version.properties 0000664 0000000 0000000 00000000207 11575623476 0024163 0 ustar 00root root 0000000 0000000 clojure.version.major=1
clojure.version.minor=2
clojure.version.incremental=1
clojure.version.qualifier=
clojure.version.interim=false
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/walk.clj 0000664 0000000 0000000 00000010463 11575623476 0022015 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;;; walk.clj - generic tree walker with replacement
;; by Stuart Sierra
;; December 15, 2008
;; CHANGE LOG:
;;
;; * December 15, 2008: replaced 'walk' with 'prewalk' & 'postwalk'
;;
;; * December 9, 2008: first version
(ns
^{:author "Stuart Sierra",
:doc "This file defines a generic tree walker for Clojure data
structures. It takes any data structure (list, vector, map, set,
seq), calls a function on every element, and uses the return value
of the function in place of the original. This makes it fairly
easy to write recursive search-and-replace functions, as shown in
the examples.
Note: \"walk\" supports all Clojure data structures EXCEPT maps
created with sorted-map-by. There is no (obvious) way to retrieve
the sorting function."}
clojure.walk)
(defn walk
"Traverses form, an arbitrary data structure. inner and outer are
functions. Applies inner to each element of form, building up a
data structure of the same type, then applies outer to the result.
Recognizes all Clojure data structures except sorted-map-by.
Consumes seqs as with doall."
{:added "1.1"}
[inner outer form]
(cond
(list? form) (outer (apply list (map inner form)))
(seq? form) (outer (doall (map inner form)))
(vector? form) (outer (vec (map inner form)))
(map? form) (outer (into (if (sorted? form) (sorted-map) {})
(map inner form)))
(set? form) (outer (into (if (sorted? form) (sorted-set) #{})
(map inner form)))
:else (outer form)))
(defn postwalk
"Performs a depth-first, post-order traversal of form. Calls f on
each sub-form, uses f's return value in place of the original.
Recognizes all Clojure data structures except sorted-map-by.
Consumes seqs as with doall."
{:added "1.1"}
[f form]
(walk (partial postwalk f) f form))
(defn prewalk
"Like postwalk, but does pre-order traversal."
{:added "1.1"}
[f form]
(walk (partial prewalk f) identity (f form)))
;; Note: I wanted to write:
;;
;; (defn walk
;; [f form]
;; (let [pf (partial walk f)]
;; (if (coll? form)
;; (f (into (empty form) (map pf form)))
;; (f form))))
;;
;; but this throws a ClassCastException when applied to a map.
(defn postwalk-demo
"Demonstrates the behavior of postwalk by printing each form as it is
walked. Returns form."
{:added "1.1"}
[form]
(postwalk (fn [x] (print "Walked: ") (prn x) x) form))
(defn prewalk-demo
"Demonstrates the behavior of prewalk by printing each form as it is
walked. Returns form."
{:added "1.1"}
[form]
(prewalk (fn [x] (print "Walked: ") (prn x) x) form))
(defn keywordize-keys
"Recursively transforms all map keys from strings to keywords."
{:added "1.1"}
[m]
(let [f (fn [[k v]] (if (string? k) [(keyword k) v] [k v]))]
;; only apply to maps
(postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m)))
(defn stringify-keys
"Recursively transforms all map keys from keywords to strings."
{:added "1.1"}
[m]
(let [f (fn [[k v]] (if (keyword? k) [(name k) v] [k v]))]
;; only apply to maps
(postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m)))
(defn prewalk-replace
"Recursively transforms form by replacing keys in smap with their
values. Like clojure/replace but works on any data structure. Does
replacement at the root of the tree first."
{:added "1.1"}
[smap form]
(prewalk (fn [x] (if (contains? smap x) (smap x) x)) form))
(defn postwalk-replace
"Recursively transforms form by replacing keys in smap with their
values. Like clojure/replace but works on any data structure. Does
replacement at the leaves of the tree first."
{:added "1.1"}
[smap form]
(postwalk (fn [x] (if (contains? smap x) (smap x) x)) form))
(defn macroexpand-all
"Recursively performs all possible macroexpansions in form."
{:added "1.1"}
[form]
(prewalk (fn [x] (if (seq? x) (macroexpand x) x)) form))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/xml.clj 0000664 0000000 0000000 00000010552 11575623476 0021656 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
(ns ^{:doc "XML reading/writing."
:author "Rich Hickey"}
clojure.xml
(:import (org.xml.sax ContentHandler Attributes SAXException)
(javax.xml.parsers SAXParser SAXParserFactory)))
(def *stack*)
(def *current*)
(def *state*) ; :element :chars :between
(def *sb*)
(defstruct element :tag :attrs :content)
(def tag (accessor element :tag))
(def attrs (accessor element :attrs))
(def content (accessor element :content))
(def content-handler
(let [push-content (fn [e c]
(assoc e :content (conj (or (:content e) []) c)))
push-chars (fn []
(when (and (= *state* :chars)
(some (complement #(Character/isWhitespace (char %))) (str *sb*)))
(set! *current* (push-content *current* (str *sb*)))))]
(new clojure.lang.XMLHandler
(proxy [ContentHandler] []
(startElement [uri local-name q-name ^Attributes atts]
(let [attrs (fn [ret i]
(if (neg? i)
ret
(recur (assoc ret
(clojure.lang.Keyword/intern (symbol (.getQName atts i)))
(.getValue atts (int i)))
(dec i))))
e (struct element
(. clojure.lang.Keyword (intern (symbol q-name)))
(when (pos? (.getLength atts))
(attrs {} (dec (.getLength atts)))))]
(push-chars)
(set! *stack* (conj *stack* *current*))
(set! *current* e)
(set! *state* :element))
nil)
(endElement [uri local-name q-name]
(push-chars)
(set! *current* (push-content (peek *stack*) *current*))
(set! *stack* (pop *stack*))
(set! *state* :between)
nil)
(characters [^chars ch start length]
(when-not (= *state* :chars)
(set! *sb* (new StringBuilder)))
(let [^StringBuilder sb *sb*]
(.append sb ch (int start) (int length))
(set! *state* :chars))
nil)
(setDocumentLocator [locator])
(startDocument [])
(endDocument [])
(startPrefixMapping [prefix uri])
(endPrefixMapping [prefix])
(ignorableWhitespace [ch start length])
(processingInstruction [target data])
(skippedEntity [name])
))))
(defn startparse-sax [s ch]
(.. SAXParserFactory (newInstance) (newSAXParser) (parse s ch)))
(defn parse
"Parses and loads the source s, which can be a File, InputStream or
String naming a URI. Returns a tree of the xml/element struct-map,
which has the keys :tag, :attrs, and :content. and accessor fns tag,
attrs, and content. Other parsers can be supplied by passing
startparse, a fn taking a source and a ContentHandler and returning
a parser"
{:added "1.0"}
([s] (parse s startparse-sax))
([s startparse]
(binding [*stack* nil
*current* (struct element)
*state* :between
*sb* nil]
(startparse s content-handler)
((:content *current*) 0))))
(defn emit-element [e]
(if (instance? String e)
(println e)
(do
(print (str "<" (name (:tag e))))
(when (:attrs e)
(doseq [attr (:attrs e)]
(print (str " " (name (key attr)) "='" (val attr)"'"))))
(if (:content e)
(do
(println ">")
(doseq [c (:content e)]
(emit-element c))
(println (str "" (name (:tag e)) ">")))
(println "/>")))))
(defn emit [x]
(println "")
(emit-element x))
;(export '(tag attrs content parse element emit emit-element))
;(load-file "/Users/rich/dev/clojure/src/xml.clj")
;(def x (xml/parse "http://arstechnica.com/journals.rssx"))
clojure1.2_1.2.1+dfsg.orig/src/clj/clojure/zip.clj 0000664 0000000 0000000 00000022503 11575623476 0021657 0 ustar 00root root 0000000 0000000 ; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
;functional hierarchical zipper, with navigation, editing and enumeration
;see Huet
(ns ^{:doc "Functional hierarchical zipper, with navigation, editing,
and enumeration. See Huet"
:author "Rich Hickey"}
clojure.zip
(:refer-clojure :exclude (replace remove next)))
(defn zipper
"Creates a new zipper structure.
branch? is a fn that, given a node, returns true if can have
children, even if it currently doesn't.
children is a fn that, given a branch node, returns a seq of its
children.
make-node is a fn that, given an existing node and a seq of
children, returns a new branch node with the supplied children.
root is the root node."
{:added "1.0"}
[branch? children make-node root]
^{:zip/branch? branch? :zip/children children :zip/make-node make-node}
[root nil])
(defn seq-zip
"Returns a zipper for nested sequences, given a root sequence"
{:added "1.0"}
[root]
(zipper seq?
identity
(fn [node children] (with-meta children (meta node)))
root))
(defn vector-zip
"Returns a zipper for nested vectors, given a root vector"
{:added "1.0"}
[root]
(zipper vector?
seq
(fn [node children] (with-meta (vec children) (meta node)))
root))
(defn xml-zip
"Returns a zipper for xml elements (as from xml/parse),
given a root element"
{:added "1.0"}
[root]
(zipper (complement string?)
(comp seq :content)
(fn [node children]
(assoc node :content (and children (apply vector children))))
root))
(defn node
"Returns the node at loc"
{:added "1.0"}
[loc] (loc 0))
(defn branch?
"Returns true if the node at loc is a branch"
{:added "1.0"}
[loc]
((:zip/branch? (meta loc)) (node loc)))
(defn children
"Returns a seq of the children of node at loc, which must be a branch"
{:added "1.0"}
[loc]
(if (branch? loc)
((:zip/children (meta loc)) (node loc))
(throw (Exception. "called children on a leaf node"))))
(defn make-node
"Returns a new branch node, given an existing node and new
children. The loc is only used to supply the constructor."
{:added "1.0"}
[loc node children]
((:zip/make-node (meta loc)) node children))
(defn path
"Returns a seq of nodes leading to this loc"
{:added "1.0"}
[loc]
(:pnodes (loc 1)))
(defn lefts
"Returns a seq of the left siblings of this loc"
{:added "1.0"}
[loc]
(seq (:l (loc 1))))
(defn rights
"Returns a seq of the right siblings of this loc"
{:added "1.0"}
[loc]
(:r (loc 1)))
(defn down
"Returns the loc of the leftmost child of the node at this loc, or
nil if no children"
{:added "1.0"}
[loc]
(when (branch? loc)
(let [[node path] loc
[c & cnext :as cs] (children loc)]
(when cs
(with-meta [c {:l []
:pnodes (if path (conj (:pnodes path) node) [node])
:ppath path
:r cnext}] (meta loc))))))
(defn up
"Returns the loc of the parent of the node at this loc, or nil if at
the top"
{:added "1.0"}
[loc]
(let [[node {l :l, ppath :ppath, pnodes :pnodes r :r, changed? :changed?, :as path}] loc]
(when pnodes
(let [pnode (peek pnodes)]
(with-meta (if changed?
[(make-node loc pnode (concat l (cons node r)))
(and ppath (assoc ppath :changed? true))]
[pnode ppath])
(meta loc))))))
(defn root
"zips all the way up and returns the root node, reflecting any
changes."
{:added "1.0"}
[loc]
(if (= :end (loc 1))
(node loc)
(let [p (up loc)]
(if p
(recur p)
(node loc)))))
(defn right
"Returns the loc of the right sibling of the node at this loc, or nil"
{:added "1.0"}
[loc]
(let [[node {l :l [r & rnext :as rs] :r :as path}] loc]
(when (and path rs)
(with-meta [r (assoc path :l (conj l node) :r rnext)] (meta loc)))))
(defn rightmost
"Returns the loc of the rightmost sibling of the node at this loc, or self"
{:added "1.0"}
[loc]
(let [[node {l :l r :r :as path}] loc]
(if (and path r)
(with-meta [(last r) (assoc path :l (apply conj l node (butlast r)) :r nil)] (meta loc))
loc)))
(defn left
"Returns the loc of the left sibling of the node at this loc, or nil"
{:added "1.0"}
[loc]
(let [[node {l :l r :r :as path}] loc]
(when (and path (seq l))
(with-meta [(peek l) (assoc path :l (pop l) :r (cons node r))] (meta loc)))))
(defn leftmost
"Returns the loc of the leftmost sibling of the node at this loc, or self"
{:added "1.0"}
[loc]
(let [[node {l :l r :r :as path}] loc]
(if (and path (seq l))
(with-meta [(first l) (assoc path :l [] :r (concat (rest l) [node] r))] (meta loc))
loc)))
(defn insert-left
"Inserts the item as the left sibling of the node at this loc,
without moving"
{:added "1.0"}
[loc item]
(let [[node {l :l :as path}] loc]
(if (nil? path)
(throw (new Exception "Insert at top"))
(with-meta [node (assoc path :l (conj l item) :changed? true)] (meta loc)))))
(defn insert-right
"Inserts the item as the right sibling of the node at this loc,
without moving"
{:added "1.0"}
[loc item]
(let [[node {r :r :as path}] loc]
(if (nil? path)
(throw (new Exception "Insert at top"))
(with-meta [node (assoc path :r (cons item r) :changed? true)] (meta loc)))))
(defn replace
"Replaces the node at this loc, without moving"
{:added "1.0"}
[loc node]
(let [[_ path] loc]
(with-meta [node (assoc path :changed? true)] (meta loc))))
(defn edit
"Replaces the node at this loc with the value of (f node args)"
{:added "1.0"}
[loc f & args]
(replace loc (apply f (node loc) args)))
(defn insert-child
"Inserts the item as the leftmost child of the node at this loc,
without moving"
{:added "1.0"}
[loc item]
(replace loc (make-node loc (node loc) (cons item (children loc)))))
(defn append-child
"Inserts the item as the rightmost child of the node at this loc,
without moving"
{:added "1.0"}
[loc item]
(replace loc (make-node loc (node loc) (concat (children loc) [item]))))
(defn next
"Moves to the next loc in the hierarchy, depth-first. When reaching
the end, returns a distinguished loc detectable via end?. If already
at the end, stays there."
{:added "1.0"}
[loc]
(if (= :end (loc 1))
loc
(or
(and (branch? loc) (down loc))
(right loc)
(loop [p loc]
(if (up p)
(or (right (up p)) (recur (up p)))
[(node p) :end])))))
(defn prev
"Moves to the previous loc in the hierarchy, depth-first. If already
at the root, returns nil."
{:added "1.0"}
[loc]
(if-let [lloc (left loc)]
(loop [loc lloc]
(if-let [child (and (branch? loc) (down loc))]
(recur (rightmost child))
loc))
(up loc)))
(defn end?
"Returns true if loc represents the end of a depth-first walk"
{:added "1.0"}
[loc]
(= :end (loc 1)))
(defn remove
"Removes the node at loc, returning the loc that would have preceded
it in a depth-first walk."
{:added "1.0"}
[loc]
(let [[node {l :l, ppath :ppath, pnodes :pnodes, rs :r, :as path}] loc]
(if (nil? path)
(throw (new Exception "Remove at top"))
(if (pos? (count l))
(loop [loc (with-meta [(peek l) (assoc path :l (pop l) :changed? true)] (meta loc))]
(if-let [child (and (branch? loc) (down loc))]
(recur (rightmost child))
loc))
(with-meta [(make-node loc (peek pnodes) rs)
(and ppath (assoc ppath :changed? true))]
(meta loc))))))
(comment
(load-file "/Users/rich/dev/clojure/src/zip.clj")
(refer 'zip)
(def data '[[a * b] + [c * d]])
(def dz (vector-zip data))
(right (down (right (right (down dz)))))
(lefts (right (down (right (right (down dz))))))
(rights (right (down (right (right (down dz))))))
(up (up (right (down (right (right (down dz)))))))
(path (right (down (right (right (down dz))))))
(-> dz down right right down right)
(-> dz down right right down right (replace '/) root)
(-> dz next next (edit str) next next next (replace '/) root)
(-> dz next next next next next next next next next remove root)
(-> dz next next next next next next next next next remove (insert-right 'e) root)
(-> dz next next next next next next next next next remove up (append-child 'e) root)
(end? (-> dz next next next next next next next next next remove next))
(-> dz next remove next remove root)
(loop [loc dz]
(if (end? loc)
(root loc)
(recur (next (if (= '* (node loc))
(replace loc '/)
loc)))))
(loop [loc dz]
(if (end? loc)
(root loc)
(recur (next (if (= '* (node loc))
(remove loc)
loc)))))
)
clojure1.2_1.2.1+dfsg.orig/src/jvm/ 0000775 0000000 0000000 00000000000 11575623476 0016742 5 ustar 00root root 0000000 0000000 clojure1.2_1.2.1+dfsg.orig/src/jvm/clojure/ 0000775 0000000 0000000 00000000000 11575623476 0020405 5 ustar 00root root 0000000 0000000 clojure1.2_1.2.1+dfsg.orig/src/jvm/clojure/lang/ 0000775 0000000 0000000 00000000000 11575623476 0021326 5 ustar 00root root 0000000 0000000 clojure1.2_1.2.1+dfsg.orig/src/jvm/clojure/lang/AFn.java 0000664 0000000 0000000 00000040710 11575623476 0022637 0 ustar 00root root 0000000 0000000 /**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 25, 2006 4:05:37 PM */
package clojure.lang;
public abstract class AFn implements IFn {
public Object call() throws Exception{
return invoke();
}
public void run(){
try
{
invoke();
}
catch(Exception e)
{
throw new RuntimeException(e);
}
}
public Object invoke() throws Exception{
return throwArity(0);
}
public Object invoke(Object arg1) throws Exception{
return throwArity(1);
}
public Object invoke(Object arg1, Object arg2) throws Exception{
return throwArity(2);
}
public Object invoke(Object arg1, Object arg2, Object arg3) throws Exception{
return throwArity(3);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4) throws Exception{
return throwArity(4);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) throws Exception{
return throwArity(5);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) throws Exception{
return throwArity(6);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7)
throws Exception{
return throwArity(7);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8) throws Exception{
return throwArity(8);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9) throws Exception{
return throwArity(9);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10) throws Exception{
return throwArity(10);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11) throws Exception{
return throwArity(11);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12) throws Exception{
return throwArity(12);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13)
throws Exception{
return throwArity(13);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14)
throws Exception{
return throwArity(14);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15) throws Exception{
return throwArity(15);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16) throws Exception{
return throwArity(16);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17) throws Exception{
return throwArity(17);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18) throws Exception{
return throwArity(18);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19) throws Exception{
return throwArity(19);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20)
throws Exception{
return throwArity(20);
}
public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20,
Object... args)
throws Exception{
return throwArity(21);
}
public Object applyTo(ISeq arglist) throws Exception{
return applyToHelper(this, Util.ret1(arglist,arglist = null));
}
static public Object applyToHelper(IFn ifn, ISeq arglist) throws Exception{
switch(RT.boundedLength(arglist, 20))
{
case 0:
arglist = null;
return ifn.invoke();
case 1:
return ifn.invoke(Util.ret1(arglist.first(),arglist = null));
case 2:
return ifn.invoke(arglist.first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 3:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 4:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 5:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 6:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 7:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 8:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 9:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 10:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 11:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 12:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 13:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 14:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 15:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 16:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 17:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 18:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 19:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
case 20:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, Util.ret1((arglist = arglist.next()).first(),arglist = null)
);
default:
return ifn.invoke(arglist.first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, (arglist = arglist.next()).first()
, RT.seqToArray(Util.ret1(arglist.next(),arglist = null)));
}
}
public Object throwArity(int n){
String name = getClass().getSimpleName();
int suffix = name.lastIndexOf("__");
throw new IllegalArgumentException("Wrong number of args (" + n + ") passed to: "
+ (suffix == -1 ? name : name.substring(0, suffix)).replace('_', '-'));
}
}
clojure1.2_1.2.1+dfsg.orig/src/jvm/clojure/lang/AFunction.java 0000664 0000000 0000000 00000002040 11575623476 0024053 0 ustar 00root root 0000000 0000000 /**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Dec 16, 2008 */
package clojure.lang;
import java.io.Serializable;
import java.util.Comparator;
public abstract class AFunction extends AFn implements IObj, Comparator, Fn, Serializable {
public volatile MethodImplCache __methodImplCache;
public int compare(Object o1, Object o2){
try
{
Object o = invoke(o1, o2);
if(o instanceof Boolean)
{
if(RT.booleanCast(o))
return -1;
return RT.booleanCast(invoke(o2,o1))? 1 : 0;
}
Number n = (Number) o;
return n.intValue();
}
catch(Exception e)
{
throw new RuntimeException(e);
}
}
}
clojure1.2_1.2.1+dfsg.orig/src/jvm/clojure/lang/AMapEntry.java 0000664 0000000 0000000 00000005515 11575623476 0024037 0 ustar 00root root 0000000 0000000 /**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 1, 2008 */
package clojure.lang;
import java.io.StringWriter;
public abstract class AMapEntry extends APersistentVector implements IMapEntry{
public Object nth(int i){
if(i == 0)
return key();
else if(i == 1)
return val();
else
throw new IndexOutOfBoundsException();
}
private IPersistentVector asVector(){
return LazilyPersistentVector.createOwning(key(), val());
}
public IPersistentVector assocN(int i, Object val){
return asVector().assocN(i, val);
}
public int count(){
return 2;
}
public ISeq seq(){
return asVector().seq();
}
public IPersistentVector cons(Object o){
return asVector().cons(o);
}
public IPersistentCollection empty(){
return null;
}
public IPersistentStack pop(){
return LazilyPersistentVector.createOwning(key());
}
public Object setValue(Object value){
throw new UnsupportedOperationException();
}
/*
public boolean equals(Object obj){
return APersistentVector.doEquals(this, obj);
}
public int hashCode(){
//must match logic in APersistentVector
return 31 * (31 + Util.hash(key())) + Util.hash(val());
// return Util.hashCombine(Util.hashCombine(0, Util.hash(key())), Util.hash(val()));
}
public String toString(){
StringWriter sw = new StringWriter();
try
{
RT.print(this, sw);
}
catch(Exception e)
{
//checked exceptions stink!
throw new RuntimeException(e);
}
return sw.toString();
}
public int length(){
return 2;
}
public Object nth(int i){
if(i == 0)
return key();
else if(i == 1)
return val();
else
throw new IndexOutOfBoundsException();
}
private IPersistentVector asVector(){
return LazilyPersistentVector.createOwning(key(), val());
}
public IPersistentVector assocN(int i, Object val){
return asVector().assocN(i, val);
}
public int count(){
return 2;
}
public ISeq seq(){
return asVector().seq();
}
public IPersistentVector cons(Object o){
return asVector().cons(o);
}
public boolean containsKey(Object key){
return asVector().containsKey(key);
}
public IMapEntry entryAt(Object key){
return asVector().entryAt(key);
}
public Associative assoc(Object key, Object val){
return asVector().assoc(key, val);
}
public Object valAt(Object key){
return asVector().valAt(key);
}
public Object valAt(Object key, Object notFound){
return asVector().valAt(key, notFound);
}
public Object peek(){
return val();
}
public ISeq rseq() throws Exception{
return asVector().rseq();
}
*/
}
clojure1.2_1.2.1+dfsg.orig/src/jvm/clojure/lang/APersistentMap.java 0000664 0000000 0000000 00000016756 11575623476 0025107 0 ustar 00root root 0000000 0000000 /**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.io.Serializable;
import java.util.*;
public abstract class APersistentMap extends AFn implements IPersistentMap, Map, Iterable, Serializable, MapEquivalence {
int _hash = -1;
public String toString(){
return RT.printString(this);
}
public IPersistentCollection cons(Object o){
if(o instanceof Map.Entry)
{
Map.Entry e = (Map.Entry) o;
return assoc(e.getKey(), e.getValue());
}
else if(o instanceof IPersistentVector)
{
IPersistentVector v = (IPersistentVector) o;
if(v.count() != 2)
throw new IllegalArgumentException("Vector arg to map conj must be a pair");
return assoc(v.nth(0), v.nth(1));
}
IPersistentMap ret = this;
for(ISeq es = RT.seq(o); es != null; es = es.next())
{
Map.Entry e = (Map.Entry) es.first();
ret = ret.assoc(e.getKey(), e.getValue());
}
return ret;
}
public boolean equals(Object obj){
return mapEquals(this, obj);
}
static public boolean mapEquals(IPersistentMap m1, Object obj){
if(m1 == obj) return true;
if(!(obj instanceof Map))
return false;
Map m = (Map) obj;
if(m.size() != m1.count() || m.hashCode() != m1.hashCode())
return false;
for(ISeq s = m1.seq(); s != null; s = s.next())
{
Map.Entry e = (Map.Entry) s.first();
boolean found = m.containsKey(e.getKey());
if(!found || !Util.equals(e.getValue(), m.get(e.getKey())))
return false;
}
return true;
}
public boolean equiv(Object obj){
if(!(obj instanceof Map))
return false;
if(obj instanceof IPersistentMap && !(obj instanceof MapEquivalence))
return false;
Map m = (Map) obj;
if(m.size() != size())
return false;
for(ISeq s = seq(); s != null; s = s.next())
{
Map.Entry e = (Map.Entry) s.first();
boolean found = m.containsKey(e.getKey());
if(!found || !Util.equiv(e.getValue(), m.get(e.getKey())))
return false;
}
return true;
}
public int hashCode(){
if(_hash == -1)
{
this._hash = mapHash(this);
}
return _hash;
}
static public int mapHash(IPersistentMap m){
int hash = 0;
for(ISeq s = m.seq(); s != null; s = s.next())
{
Map.Entry e = (Map.Entry) s.first();
hash += (e.getKey() == null ? 0 : e.getKey().hashCode()) ^
(e.getValue() == null ? 0 : e.getValue().hashCode());
}
return hash;
}
static public class KeySeq extends ASeq{
ISeq seq;
static public KeySeq create(ISeq seq){
if(seq == null)
return null;
return new KeySeq(seq);
}
private KeySeq(ISeq seq){
this.seq = seq;
}
private KeySeq(IPersistentMap meta, ISeq seq){
super(meta);
this.seq = seq;
}
public Object first(){
return ((Map.Entry) seq.first()).getKey();
}
public ISeq next(){
return create(seq.next());
}
public KeySeq withMeta(IPersistentMap meta){
return new KeySeq(meta, seq);
}
}
static public class ValSeq extends ASeq{
ISeq seq;
static public ValSeq create(ISeq seq){
if(seq == null)
return null;
return new ValSeq(seq);
}
private ValSeq(ISeq seq){
this.seq = seq;
}
private ValSeq(IPersistentMap meta, ISeq seq){
super(meta);
this.seq = seq;
}
public Object first(){
return ((Map.Entry) seq.first()).getValue();
}
public ISeq next(){
return create(seq.next());
}
public ValSeq withMeta(IPersistentMap meta){
return new ValSeq(meta, seq);
}
}
public Object invoke(Object arg1) throws Exception{
return valAt(arg1);
}
public Object invoke(Object arg1, Object notFound) throws Exception{
return valAt(arg1, notFound);
}
// java.util.Map implementation
public void clear(){
throw new UnsupportedOperationException();
}
public boolean containsValue(Object value){
return values().contains(value);
}
public Set entrySet(){
return new AbstractSet(){
public Iterator iterator(){
return APersistentMap.this.iterator();
}
public int size(){
return count();
}
public int hashCode(){
return APersistentMap.this.hashCode();
}
public boolean contains(Object o){
if(o instanceof Entry)
{
Entry e = (Entry) o;
Entry found = entryAt(e.getKey());
if(found != null && Util.equals(found.getValue(), e.getValue()))
return true;
}
return false;
}
};
}
public Object get(Object key){
return valAt(key);
}
public boolean isEmpty(){
return count() == 0;
}
public Set keySet(){
return new AbstractSet(){
public Iterator iterator(){
final Iterator mi = APersistentMap.this.iterator();
return new Iterator(){
public boolean hasNext(){
return mi.hasNext();
}
public Object next(){
Entry e = (Entry) mi.next();
return e.getKey();
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
public int size(){
return count();
}
public boolean contains(Object o){
return APersistentMap.this.containsKey(o);
}
};
}
public Object put(Object key, Object value){
throw new UnsupportedOperationException();
}
public void putAll(Map t){
throw new UnsupportedOperationException();
}
public Object remove(Object key){
throw new UnsupportedOperationException();
}
public int size(){
return count();
}
public Collection values(){
return new AbstractCollection(){
public Iterator iterator(){
final Iterator mi = APersistentMap.this.iterator();
return new Iterator(){
public boolean hasNext(){
return mi.hasNext();
}
public Object next(){
Entry e = (Entry) mi.next();
return e.getValue();
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
public int size(){
return count();
}
};
}
/*
// java.util.Collection implementation
public Object[] toArray(){
return RT.seqToArray(seq());
}
public boolean add(Object o){
throw new UnsupportedOperationException();
}
public boolean remove(Object o){
throw new UnsupportedOperationException();
}
public boolean addAll(Collection c){
throw new UnsupportedOperationException();
}
public void clear(){
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection c){
for(Object o : c)
{
if(!contains(o))
return false;
}
return true;
}
public Object[] toArray(Object[] a){
if(a.length >= count())
{
ISeq s = seq();
for(int i = 0; s != null; ++i, s = s.rest())
{
a[i] = s.first();
}
if(a.length > count())
a[count()] = null;
return a;
}
else
return toArray();
}
public int size(){
return count();
}
public boolean isEmpty(){
return count() == 0;
}
public boolean contains(Object o){
if(o instanceof Map.Entry)
{
Map.Entry e = (Map.Entry) o;
Map.Entry v = entryAt(e.getKey());
return (v != null && Util.equal(v.getValue(), e.getValue()));
}
return false;
}
*/
}
clojure1.2_1.2.1+dfsg.orig/src/jvm/clojure/lang/APersistentSet.java 0000664 0000000 0000000 00000005710 11575623476 0025111 0 ustar 00root root 0000000 0000000 /**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 3, 2008 */
package clojure.lang;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
public abstract class APersistentSet extends AFn implements IPersistentSet, Collection, Set, Serializable {
int _hash = -1;
final IPersistentMap impl;
protected APersistentSet(IPersistentMap impl){
this.impl = impl;
}
public String toString(){
return RT.printString(this);
}
public boolean contains(Object key){
return impl.containsKey(key);
}
public Object get(Object key){
return impl.valAt(key);
}
public int count(){
return impl.count();
}
public ISeq seq(){
return RT.keys(impl);
}
public Object invoke(Object arg1) throws Exception{
return get(arg1);
}
public boolean equals(Object obj){
if(this == obj) return true;
if(!(obj instanceof Set))
return false;
Set m = (Set) obj;
if(m.size() != count() || m.hashCode() != hashCode())
return false;
for(Object aM : m)
{
if(!contains(aM))
return false;
}
// for(ISeq s = seq(); s != null; s = s.rest())
// {
// if(!m.contains(s.first()))
// return false;
// }
return true;
}
public boolean equiv(Object o){
return equals(o);
}
public int hashCode(){
if(_hash == -1)
{
//int hash = count();
int hash = 0;
for(ISeq s = seq(); s != null; s = s.next())
{
Object e = s.first();
// hash = Util.hashCombine(hash, Util.hash(e));
hash += Util.hash(e);
}
this._hash = hash;
}
return _hash;
}
public Object[] toArray(){
return RT.seqToArray(seq());
}
public boolean add(Object o){
throw new UnsupportedOperationException();
}
public boolean remove(Object o){
throw new UnsupportedOperationException();
}
public boolean addAll(Collection c){
throw new UnsupportedOperationException();
}
public void clear(){
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection c){
for(Object o : c)
{
if(!contains(o))
return false;
}
return true;
}
public Object[] toArray(Object[] a){
if(a.length >= count())
{
ISeq s = seq();
for(int i = 0; s != null; ++i, s = s.next())
{
a[i] = s.first();
}
if(a.length > count())
a[count()] = null;
return a;
}
else
return toArray();
}
public int size(){
return count();
}
public boolean isEmpty(){
return count() == 0;
}
public Iterator iterator(){
return new SeqIterator(seq());
}
}
clojure1.2_1.2.1+dfsg.orig/src/jvm/clojure/lang/APersistentVector.java 0000664 0000000 0000000 00000025760 11575623476 0025627 0 ustar 00root root 0000000 0000000 /**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Dec 18, 2007 */
package clojure.lang;
import java.io.Serializable;
import java.util.*;
public abstract class APersistentVector extends AFn implements IPersistentVector, Iterable,
List,
RandomAccess, Comparable,
Serializable {
int _hash = -1;
public String toString(){
return RT.printString(this);
}
public ISeq seq(){
if(count() > 0)
return new Seq(this, 0);
return null;
}
public ISeq rseq(){
if(count() > 0)
return new RSeq(this, count() - 1);
return null;
}
static boolean doEquals(IPersistentVector v, Object obj){
if(v == obj) return true;
if(obj instanceof List || obj instanceof IPersistentVector)
{
Collection ma = (Collection) obj;
if(ma.size() != v.count() || ma.hashCode() != v.hashCode())
return false;
for(Iterator i1 = ((List) v).iterator(), i2 = ma.iterator();
i1.hasNext();)
{
if(!Util.equals(i1.next(), i2.next()))
return false;
}
return true;
}
// if(obj instanceof IPersistentVector)
// {
// IPersistentVector ma = (IPersistentVector) obj;
// if(ma.count() != v.count() || ma.hashCode() != v.hashCode())
// return false;
// for(int i = 0; i < v.count(); i++)
// {
// if(!Util.equal(v.nth(i), ma.nth(i)))
// return false;
// }
// }
else
{
if(!(obj instanceof Sequential))
return false;
ISeq ms = RT.seq(obj);
for(int i = 0; i < v.count(); i++, ms = ms.next())
{
if(ms == null || !Util.equals(v.nth(i), ms.first()))
return false;
}
if(ms != null)
return false;
}
return true;
}
static boolean doEquiv(IPersistentVector v, Object obj){
if(obj instanceof List || obj instanceof IPersistentVector)
{
Collection ma = (Collection) obj;
if(ma.size() != v.count())
return false;
for(Iterator i1 = ((List) v).iterator(), i2 = ma.iterator();
i1.hasNext();)
{
if(!Util.equiv(i1.next(), i2.next()))
return false;
}
return true;
}
// if(obj instanceof IPersistentVector)
// {
// IPersistentVector ma = (IPersistentVector) obj;
// if(ma.count() != v.count() || ma.hashCode() != v.hashCode())
// return false;
// for(int i = 0; i < v.count(); i++)
// {
// if(!Util.equal(v.nth(i), ma.nth(i)))
// return false;
// }
// }
else
{
if(!(obj instanceof Sequential))
return false;
ISeq ms = RT.seq(obj);
for(int i = 0; i < v.count(); i++, ms = ms.next())
{
if(ms == null || !Util.equiv(v.nth(i), ms.first()))
return false;
}
if(ms != null)
return false;
}
return true;
}
public boolean equals(Object obj){
return doEquals(this, obj);
}
public boolean equiv(Object obj){
return doEquiv(this, obj);
}
public int hashCode(){
if(_hash == -1)
{
int hash = 1;
Iterator i = iterator();
while(i.hasNext())
{
Object obj = i.next();
hash = 31 * hash + (obj == null ? 0 : obj.hashCode());
}
// int hash = 0;
// for(int i = 0; i < count(); i++)
// {
// hash = Util.hashCombine(hash, Util.hash(nth(i)));
// }
this._hash = hash;
}
return _hash;
}
public Object get(int index){
return nth(index);
}
public Object nth(int i, Object notFound){
if(i >= 0 && i < count())
return nth(i);
return notFound;
}
public Object remove(int i){
throw new UnsupportedOperationException();
}
public int indexOf(Object o){
for(int i = 0; i < count(); i++)
if(Util.equiv(nth(i), o))
return i;
return -1;
}
public int lastIndexOf(Object o){
for(int i = count() - 1; i >= 0; i--)
if(Util.equiv(nth(i), o))
return i;
return -1;
}
public ListIterator listIterator(){
return listIterator(0);
}
public ListIterator listIterator(final int index){
return new ListIterator(){
int nexti = index;
public boolean hasNext(){
return nexti < count();
}
public Object next(){
return nth(nexti++);
}
public boolean hasPrevious(){
return nexti > 0;
}
public Object previous(){
return nth(--nexti);
}
public int nextIndex(){
return nexti;
}
public int previousIndex(){
return nexti - 1;
}
public void remove(){
throw new UnsupportedOperationException();
}
public void set(Object o){
throw new UnsupportedOperationException();
}
public void add(Object o){
throw new UnsupportedOperationException();
}
};
}
public List subList(int fromIndex, int toIndex){
return (List) RT.subvec(this, fromIndex, toIndex);
}
public Object set(int i, Object o){
throw new UnsupportedOperationException();
}
public void add(int i, Object o){
throw new UnsupportedOperationException();
}
public boolean addAll(int i, Collection c){
throw new UnsupportedOperationException();
}
public Object invoke(Object arg1) throws Exception{
if(Util.isInteger(arg1))
return nth(((Number) arg1).intValue());
throw new IllegalArgumentException("Key must be integer");
}
public Iterator iterator(){
//todo - something more efficient
return new Iterator(){
int i = 0;
public boolean hasNext(){
return i < count();
}
public Object next(){
return nth(i++);
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
public Object peek(){
if(count() > 0)
return nth(count() - 1);
return null;
}
public boolean containsKey(Object key){
if(!(Util.isInteger(key)))
return false;
int i = ((Number) key).intValue();
return i >= 0 && i < count();
}
public IMapEntry entryAt(Object key){
if(Util.isInteger(key))
{
int i = ((Number) key).intValue();
if(i >= 0 && i < count())
return new MapEntry(key, nth(i));
}
return null;
}
public IPersistentVector assoc(Object key, Object val){
if(Util.isInteger(key))
{
int i = ((Number) key).intValue();
return assocN(i, val);
}
throw new IllegalArgumentException("Key must be integer");
}
public Object valAt(Object key, Object notFound){
if(Util.isInteger(key))
{
int i = ((Number) key).intValue();
if(i >= 0 && i < count())
return nth(i);
}
return notFound;
}
public Object valAt(Object key){
return valAt(key, null);
}
// java.util.Collection implementation
public Object[] toArray(){
return RT.seqToArray(seq());
}
public boolean add(Object o){
throw new UnsupportedOperationException();
}
public boolean remove(Object o){
throw new UnsupportedOperationException();
}
public boolean addAll(Collection c){
throw new UnsupportedOperationException();
}
public void clear(){
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection c){
for(Object o : c)
{
if(!contains(o))
return false;
}
return true;
}
public Object[] toArray(Object[] a){
if(a.length >= count())
{
ISeq s = seq();
for(int i = 0; s != null; ++i, s = s.next())
{
a[i] = s.first();
}
if(a.length > count())
a[count()] = null;
return a;
}
else
return toArray();
}
public int size(){
return count();
}
public boolean isEmpty(){
return count() == 0;
}
public boolean contains(Object o){
for(ISeq s = seq(); s != null; s = s.next())
{
if(Util.equiv(s.first(), o))
return true;
}
return false;
}
public int length(){
return count();
}
public int compareTo(Object o){
IPersistentVector v = (IPersistentVector) o;
if(count() < v.count())
return -1;
else if(count() > v.count())
return 1;
for(int i = 0; i < count(); i++)
{
int c = Util.compare(nth(i),v.nth(i));
if(c != 0)
return c;
}
return 0;
}
static class Seq extends ASeq implements IndexedSeq, IReduce{
//todo - something more efficient
final IPersistentVector v;
final int i;
public Seq(IPersistentVector v, int i){
this.v = v;
this.i = i;
}
Seq(IPersistentMap meta, IPersistentVector v, int i){
super(meta);
this.v = v;
this.i = i;
}
public Object first(){
return v.nth(i);
}
public ISeq next(){
if(i + 1 < v.count())
return new APersistentVector.Seq(v, i + 1);
return null;
}
public int index(){
return i;
}
public int count(){
return v.count() - i;
}
public APersistentVector.Seq withMeta(IPersistentMap meta){
return new APersistentVector.Seq(meta, v, i);
}
public Object reduce(IFn f) throws Exception{
Object ret = v.nth(i);
for(int x = i + 1; x < v.count(); x++)
ret = f.invoke(ret, v.nth(x));
return ret;
}
public Object reduce(IFn f, Object start) throws Exception{
Object ret = f.invoke(start, v.nth(i));
for(int x = i + 1; x < v.count(); x++)
ret = f.invoke(ret, v.nth(x));
return ret;
}
}
public static class RSeq extends ASeq implements IndexedSeq, Counted{
final IPersistentVector v;
final int i;
public RSeq(IPersistentVector vector, int i){
this.v = vector;
this.i = i;
}
RSeq(IPersistentMap meta, IPersistentVector v, int i){
super(meta);
this.v = v;
this.i = i;
}
public Object first(){
return v.nth(i);
}
public ISeq next(){
if(i > 0)
return new APersistentVector.RSeq(v, i - 1);
return null;
}
public int index(){
return i;
}
public int count(){
return i + 1;
}
public APersistentVector.RSeq withMeta(IPersistentMap meta){
return new APersistentVector.RSeq(meta, v, i);
}
}
static class SubVector extends APersistentVector implements IObj{
final IPersistentVector v;
final int start;
final int end;
final IPersistentMap _meta;
public SubVector(IPersistentMap meta, IPersistentVector v, int start, int end){
this._meta = meta;
if(v instanceof APersistentVector.SubVector)
{
APersistentVector.SubVector sv = (APersistentVector.SubVector) v;
start += sv.start;
end += sv.start;
v = sv.v;
}
this.v = v;
this.start = start;
this.end = end;
}
public Object nth(int i){
if(start + i >= end)
throw new IndexOutOfBoundsException();
return v.nth(start + i);
}
public IPersistentVector assocN(int i, Object val){
if(start + i > end)
throw new IndexOutOfBoundsException();
else if(start + i == end)
return cons(val);
return new SubVector(_meta, v.assocN(start + i, val), start, end);
}
public int count(){
return end - start;
}
public IPersistentVector cons(Object o){
return new SubVector(_meta, v.assocN(end, o), start, end + 1);
}
public IPersistentCollection empty(){
return PersistentVector.EMPTY.withMeta(meta());
}
public IPersistentStack pop(){
if(end - 1 == start)
{
return PersistentVector.EMPTY;
}
return new SubVector(_meta, v, start, end - 1);
}
public SubVector withMeta(IPersistentMap meta){
if(meta == _meta)
return this;
return new SubVector(meta, v, start, end);
}
public IPersistentMap meta(){
return _meta;
}
}
}
clojure1.2_1.2.1+dfsg.orig/src/jvm/clojure/lang/ARef.java 0000664 0000000 0000000 00000004120 11575623476 0023003 0 ustar 00root root 0000000 0000000 /**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jan 1, 2009 */
package clojure.lang;
import java.util.Map;
public abstract class ARef extends AReference implements IRef{
protected volatile IFn validator = null;
private volatile IPersistentMap watches = PersistentHashMap.EMPTY;
public ARef(){
super();
}
public ARef(IPersistentMap meta){
super(meta);
}
void validate(IFn vf, Object val){
try
{
if(vf != null && !RT.booleanCast(vf.invoke(val)))
throw new IllegalStateException("Invalid reference state");
}
catch(RuntimeException re)
{
throw re;
}
catch(Exception e)
{
throw new IllegalStateException("Invalid reference state", e);
}
}
void validate(Object val){
validate(validator, val);
}
public void setValidator(IFn vf){
try
{
validate(vf, deref());
}
catch(Exception e)
{
throw new RuntimeException(e);
}
validator = vf;
}
public IFn getValidator(){
return validator;
}
public IPersistentMap getWatches(){
return watches;
}
synchronized public IRef addWatch(Object key, IFn callback){
watches = watches.assoc(key, callback);
return this;
}
synchronized public IRef removeWatch(Object key){
try
{
watches = watches.without(key);
}
catch(Exception e)
{
throw new RuntimeException(e);
}
return this;
}
public void notifyWatches(Object oldval, Object newval){
IPersistentMap ws = watches;
if(ws.count() > 0)
{
for(ISeq s = ws.seq(); s != null; s = s.next())
{
Map.Entry e = (Map.Entry) s.first();
IFn fn = (IFn) e.getValue();
try
{
if(fn != null)
fn.invoke(e.getKey(), this, oldval, newval);
}
catch(Exception e1)
{
throw new RuntimeException(e1);
}
}
}
}
}
clojure1.2_1.2.1+dfsg.orig/src/jvm/clojure/lang/AReference.java 0000664 0000000 0000000 00000002140 11575623476 0024165 0 ustar 00root root 0000000 0000000 /**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Dec 31, 2008 */
package clojure.lang;
public class AReference implements IReference {
private IPersistentMap _meta;
public AReference() {
this(null);
}
public AReference(IPersistentMap meta) {
_meta = meta;
}
synchronized public IPersistentMap meta() {
return _meta;
}
synchronized public IPersistentMap alterMeta(IFn alter, ISeq args) throws Exception {
_meta = (IPersistentMap) alter.applyTo(new Cons(_meta, args));
return _meta;
}
synchronized public IPersistentMap resetMeta(IPersistentMap m) {
_meta = m;
return m;
}
}
clojure1.2_1.2.1+dfsg.orig/src/jvm/clojure/lang/ASeq.java 0000664 0000000 0000000 00000012215 11575623476 0023023 0 ustar 00root root 0000000 0000000 /**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.io.Serializable;
import java.util.*;
public abstract class ASeq extends Obj implements ISeq, List, Serializable {
transient int _hash = -1;
public String toString(){
return RT.printString(this);
}
public IPersistentCollection empty(){
return PersistentList.EMPTY;
}
protected ASeq(IPersistentMap meta){
super(meta);
}
protected ASeq(){
}
public boolean equiv(Object obj){
if(!(obj instanceof Sequential || obj instanceof List))
return false;
ISeq ms = RT.seq(obj);
for(ISeq s = seq(); s != null; s = s.next(), ms = ms.next())
{
if(ms == null || !Util.equiv(s.first(), ms.first()))
return false;
}
return ms == null;
}
public boolean equals(Object obj){
if(this == obj) return true;
if(!(obj instanceof Sequential || obj instanceof List))
return false;
ISeq ms = RT.seq(obj);
for(ISeq s = seq(); s != null; s = s.next(), ms = ms.next())
{
if(ms == null || !Util.equals(s.first(), ms.first()))
return false;
}
return ms == null;
}
public int hashCode(){
if(_hash == -1)
{
int hash = 1;
for(ISeq s = seq(); s != null; s = s.next())
{
hash = 31 * hash + (s.first() == null ? 0 : s.first().hashCode());
}
this._hash = hash;
}
return _hash;
}
//public Object reduce(IFn f) throws Exception{
// Object ret = first();
// for(ISeq s = rest(); s != null; s = s.rest())
// ret = f.invoke(ret, s.first());
// return ret;
//}
//
//public Object reduce(IFn f, Object start) throws Exception{
// Object ret = f.invoke(start, first());
// for(ISeq s = rest(); s != null; s = s.rest())
// ret = f.invoke(ret, s.first());
// return ret;
//}
//public Object peek(){
// return first();
//}
//
//public IPersistentList pop(){
// return rest();
//}
public int count(){
int i = 1;
for(ISeq s = next(); s != null; s = s.next(), i++)
if(s instanceof Counted)
return i + s.count();
return i;
}
final public ISeq seq(){
return this;
}
public ISeq cons(Object o){
return new Cons(o, this);
}
public ISeq more(){
ISeq s = next();
if(s == null)
return PersistentList.EMPTY;
return s;
}
//final public ISeq rest(){
// Seqable m = more();
// if(m == null)
// return null;
// return m.seq();
//}
// java.util.Collection implementation
public Object[] toArray(){
return RT.seqToArray(seq());
}
public boolean add(Object o){
throw new UnsupportedOperationException();
}
public boolean remove(Object o){
throw new UnsupportedOperationException();
}
public boolean addAll(Collection c){
throw new UnsupportedOperationException();
}
public void clear(){
throw new UnsupportedOperationException();
}
public boolean retainAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean removeAll(Collection c){
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection c){
for(Object o : c)
{
if(!contains(o))
return false;
}
return true;
}
public Object[] toArray(Object[] a){
if(a.length >= count())
{
ISeq s = seq();
for(int i = 0; s != null; ++i, s = s.next())
{
a[i] = s.first();
}
if(a.length > count())
a[count()] = null;
return a;
}
else
return toArray();
}
public int size(){
return count();
}
public boolean isEmpty(){
return seq() == null;
}
public boolean contains(Object o){
for(ISeq s = seq(); s != null; s = s.next())
{
if(Util.equiv(s.first(), o))
return true;
}
return false;
}
public Iterator iterator(){
return new SeqIterator(this);
}
//////////// List stuff /////////////////
private List reify(){
return Collections.unmodifiableList(new ArrayList(this));
}
public List subList(int fromIndex, int toIndex){
return reify().subList(fromIndex, toIndex);
}
public Object set(int index, Object element){
throw new UnsupportedOperationException();
}
public Object remove(int index){
throw new UnsupportedOperationException();
}
public int indexOf(Object o){
ISeq s = seq();
for(int i = 0; s != null; s = s.next(), i++)
{
if(Util.equiv(s.first(), o))
return i;
}
return -1;
}
public int lastIndexOf(Object o){
return reify().lastIndexOf(o);
}
public ListIterator listIterator(){
return reify().listIterator();
}
public ListIterator listIterator(int index){
return reify().listIterator(index);
}
public Object get(int index){
return RT.nth(this, index);
}
public void add(int index, Object element){
throw new UnsupportedOperationException();
}
public boolean addAll(int index, Collection c){
throw new UnsupportedOperationException();
}
}
clojure1.2_1.2.1+dfsg.orig/src/jvm/clojure/lang/ATransientMap.java 0000664 0000000 0000000 00000004345 11575623476 0024705 0 ustar 00root root 0000000 0000000 /**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
package clojure.lang;
import java.util.Map;
import clojure.lang.PersistentHashMap.INode;
abstract class ATransientMap extends AFn implements ITransientMap {
abstract void ensureEditable();
abstract ITransientMap doAssoc(Object key, Object val);
abstract ITransientMap doWithout(Object key);
abstract Object doValAt(Object key, Object notFound);
abstract int doCount();
abstract IPersistentMap doPersistent();
public ITransientMap conj(Object o) {
ensureEditable();
if(o instanceof Map.Entry)
{
Map.Entry e = (Map.Entry) o;
return assoc(e.getKey(), e.getValue());
}
else if(o instanceof IPersistentVector)
{
IPersistentVector v = (IPersistentVector) o;
if(v.count() != 2)
throw new IllegalArgumentException("Vector arg to map conj must be a pair");
return assoc(v.nth(0), v.nth(1));
}
ITransientMap ret = this;
for(ISeq es = RT.seq(o); es != null; es = es.next())
{
Map.Entry e = (Map.Entry) es.first();
ret = ret.assoc(e.getKey(), e.getValue());
}
return ret;
}
public final Object invoke(Object arg1) throws Exception{
return valAt(arg1);
}
public final Object invoke(Object arg1, Object notFound) throws Exception{
return valAt(arg1, notFound);
}
public final Object valAt(Object key) {
return valAt(key, null);
}
public final ITransientMap assoc(Object key, Object val) {
ensureEditable();
return doAssoc(key, val);
}
public final ITransientMap without(Object key) {
ensureEditable();
return doWithout(key);
}
public final IPersistentMap persistent() {
ensureEditable();
return doPersistent();
}
public final Object valAt(Object key, Object notFound) {
ensureEditable();
return doValAt(key, notFound);
}
public final int count() {
ensureEditable();
return doCount();
}
}
clojure1.2_1.2.1+dfsg.orig/src/jvm/clojure/lang/ATransientSet.java 0000664 0000000 0000000 00000002522 11575623476 0024716 0 ustar 00root root 0000000 0000000 /**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Mar 3, 2008 */
package clojure.lang;
public abstract class ATransientSet extends AFn implements ITransientSet{
ITransientMap impl;
ATransientSet(ITransientMap impl) {
this.impl = impl;
}
public int count() {
return impl.count();
}
public ITransientSet conj(Object val) {
ITransientMap m = impl.assoc(val, val);
if (m != impl) this.impl = m;
return this;
}
public boolean contains(Object key) {
return this != impl.valAt(key, this);
}
public ITransientSet disjoin(Object key) throws Exception {
ITransientMap m = impl.without(key);
if (m != impl) this.impl = m;
return this;
}
public Object get(Object key) {
return impl.valAt(key);
}
public Object invoke(Object key, Object notFound) throws Exception {
return impl.valAt(key, notFound);
}
public Object invoke(Object key) throws Exception {
return impl.valAt(key);
}
}
clojure1.2_1.2.1+dfsg.orig/src/jvm/clojure/lang/Agent.java 0000664 0000000 0000000 00000013510 11575623476 0023227 0 ustar 00root root 0000000 0000000 /**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Nov 17, 2007 */
package clojure.lang;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.Map;
public class Agent extends ARef {
static class ActionQueue {
public final IPersistentStack q;
public final Throwable error; // non-null indicates fail state
static final ActionQueue EMPTY = new ActionQueue(PersistentQueue.EMPTY, null);
public ActionQueue( IPersistentStack q, Throwable error )
{
this.q = q;
this.error = error;
}
}
static final Keyword CONTINUE = Keyword.intern(null, "continue");
static final Keyword FAIL = Keyword.intern(null, "fail");
volatile Object state;
AtomicReference aq = new AtomicReference(ActionQueue.EMPTY);
volatile Keyword errorMode = CONTINUE;
volatile IFn errorHandler = null;
final public static ExecutorService pooledExecutor =
Executors.newFixedThreadPool(2 + Runtime.getRuntime().availableProcessors());
final public static ExecutorService soloExecutor = Executors.newCachedThreadPool();
final static ThreadLocal nested = new ThreadLocal();
public static void shutdown(){
soloExecutor.shutdown();
pooledExecutor.shutdown();
}
static class Action implements Runnable{
final Agent agent;
final IFn fn;
final ISeq args;
final boolean solo;
public Action(Agent agent, IFn fn, ISeq args, boolean solo){
this.agent = agent;
this.args = args;
this.fn = fn;
this.solo = solo;
}
void execute(){
try
{
if(solo)
soloExecutor.execute(this);
else
pooledExecutor.execute(this);
}
catch(Throwable error)
{
if(agent.errorHandler != null)
{
try
{
agent.errorHandler.invoke(agent, error);
}
catch(Throwable e) {} // ignore errorHandler errors
}
}
}
static void doRun(Action action){
try
{
Var.pushThreadBindings(RT.map(RT.AGENT, action.agent));
nested.set(PersistentVector.EMPTY);
Throwable error = null;
try
{
Object oldval = action.agent.state;
Object newval = action.fn.applyTo(RT.cons(action.agent.state, action.args));
action.agent.setState(newval);
action.agent.notifyWatches(oldval,newval);
}
catch(Throwable e)
{
error = e;
}
if(error == null)
{
releasePendingSends();
}
else
{
nested.set(PersistentVector.EMPTY);
if(action.agent.errorHandler != null)
{
try
{
action.agent.errorHandler.invoke(action.agent, error);
}
catch(Throwable e) {} // ignore errorHandler errors
}
if(action.agent.errorMode == CONTINUE)
{
error = null;
}
}
boolean popped = false;
ActionQueue next = null;
while(!popped)
{
ActionQueue prior = action.agent.aq.get();
next = new ActionQueue(prior.q.pop(), error);
popped = action.agent.aq.compareAndSet(prior, next);
}
if(error == null && next.q.count() > 0)
((Action) next.q.peek()).execute();
}
finally
{
nested.set(null);
Var.popThreadBindings();
}
}
public void run(){
doRun(this);
}
}
public Agent(Object state) throws Exception{
this(state,null);
}
public Agent(Object state, IPersistentMap meta) throws Exception {
super(meta);
setState(state);
}
boolean setState(Object newState) throws Exception{
validate(newState);
boolean ret = state != newState;
state = newState;
return ret;
}
public Object deref() throws Exception{
return state;
}
public Throwable getError(){
return aq.get().error;
}
public void setErrorMode(Keyword k){
errorMode = k;
}
public Keyword getErrorMode(){
return errorMode;
}
public void setErrorHandler(IFn f){
errorHandler = f;
}
public IFn getErrorHandler(){
return errorHandler;
}
synchronized public Object restart(Object newState, boolean clearActions){
if(getError() == null)
{
throw new RuntimeException("Agent does not need a restart");
}
validate(newState);
state = newState;
if(clearActions)
aq.set(ActionQueue.EMPTY);
else
{
boolean restarted = false;
ActionQueue prior = null;
while(!restarted)
{
prior = aq.get();
restarted = aq.compareAndSet(prior, new ActionQueue(prior.q, null));
}
if(prior.q.count() > 0)
((Action) prior.q.peek()).execute();
}
return newState;
}
public Object dispatch(IFn fn, ISeq args, boolean solo) {
Throwable error = getError();
if(error != null)
{
throw new RuntimeException("Agent is failed, needs restart", error);
}
Action action = new Action(this, fn, args, solo);
dispatchAction(action);
return this;
}
static void dispatchAction(Action action){
LockingTransaction trans = LockingTransaction.getRunning();
if(trans != null)
trans.enqueue(action);
else if(nested.get() != null)
{
nested.set(nested.get().cons(action));
}
else
action.agent.enqueue(action);
}
void enqueue(Action action){
boolean queued = false;
ActionQueue prior = null;
while(!queued)
{
prior = aq.get();
queued = aq.compareAndSet(prior, new ActionQueue((IPersistentStack)prior.q.cons(action), prior.error));
}
if(prior.q.count() == 0 && prior.error == null)
action.execute();
}
public int getQueueCount(){
return aq.get().q.count();
}
static public int releasePendingSends(){
IPersistentVector sends = nested.get();
if(sends == null)
return 0;
for(int i=0;i