├── .github ├── build.sh ├── setup.sh └── workflows │ ├── build-main.yml │ └── build-pr.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── pom.xml └── src ├── main ├── java │ └── org │ │ └── scijava │ │ └── plugins │ │ └── scripting │ │ └── clojure │ │ ├── ClojureBindings.java │ │ ├── ClojureScriptEngine.java │ │ └── ClojureScriptLanguage.java └── resources │ └── META-INF │ └── services │ └── javax.script.ScriptEngineFactory └── test └── java └── org └── scijava └── plugins └── scripting └── clojure └── ClojureTest.java /.github/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | curl -fsLO https://raw.githubusercontent.com/scijava/scijava-scripts/master/ci-build.sh 3 | sh ci-build.sh 4 | -------------------------------------------------------------------------------- /.github/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | curl -fsLO https://raw.githubusercontent.com/scijava/scijava-scripts/master/ci-setup-github-actions.sh 3 | sh ci-setup-github-actions.sh 4 | -------------------------------------------------------------------------------- /.github/workflows/build-main.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - "*-[0-9]+.*" 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up Java 17 | uses: actions/setup-java@v3 18 | with: 19 | java-version: '8' 20 | distribution: 'zulu' 21 | cache: 'maven' 22 | - name: Set up CI environment 23 | run: .github/setup.sh 24 | - name: Execute the build 25 | run: .github/build.sh 26 | env: 27 | GPG_KEY_NAME: ${{ secrets.GPG_KEY_NAME }} 28 | GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 29 | MAVEN_USER: ${{ secrets.MAVEN_USER }} 30 | MAVEN_PASS: ${{ secrets.MAVEN_PASS }} 31 | OSSRH_PASS: ${{ secrets.OSSRH_PASS }} 32 | SIGNING_ASC: ${{ secrets.SIGNING_ASC }} 33 | -------------------------------------------------------------------------------- /.github/workflows/build-pr.yml: -------------------------------------------------------------------------------- 1 | name: build PR 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Set up Java 15 | uses: actions/setup-java@v3 16 | with: 17 | java-version: '8' 18 | distribution: 'zulu' 19 | cache: 'maven' 20 | - name: Set up CI environment 21 | run: .github/setup.sh 22 | - name: Execute the build 23 | run: .github/build.sh 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.classpath 2 | /.project 3 | /.settings/ 4 | /target 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 - 2025, Board of Regents of the University of 2 | Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck 3 | Institute of Molecular Cell Biology and Genetics. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://github.com/scijava/scripting-clojure/actions/workflows/build-main.yml/badge.svg)](https://github.com/scijava/scripting-clojure/actions/workflows/build-main.yml) 2 | 3 | # Clojure Scripting 4 | 5 | This library provides a 6 | [JSR-223-compliant](https://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform) 7 | scripting plugin for the [Clojure](http://clojure.org/) language. 8 | 9 | It is implemented as a `ScriptLanguage` plugin for the [SciJava 10 | Common](https://github.com/scijava/scijava-common) platform, which means that 11 | in addition to being usable directly as a `javax.script.ScriptEngineFactory`, 12 | it also provides some functionality on top, such as the ability to generate 13 | lines of script code based on SciJava events. 14 | 15 | For a complete list of scripting languages available as part of the SciJava 16 | platform, see the 17 | [Scripting](https://github.com/scijava/scijava-common/wiki/Scripting) page on 18 | the SciJava Common wiki. 19 | 20 | See also: 21 | * [Clojure Scripting](http://wiki.imagej.net/Clojure_Scripting) 22 | on the ImageJ wiki. 23 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | org.scijava 7 | pom-scijava 8 | 40.0.0 9 | 10 | 11 | 12 | scripting-clojure 13 | 1.0.1-SNAPSHOT 14 | 15 | SciJava Scripting: Clojure 16 | JSR-223-compliant Clojure scripting language plugin. 17 | https://github.com/scijava/scripting-clojure 18 | 2013 19 | 20 | SciJava 21 | https://scijava.org/ 22 | 23 | 24 | 25 | Simplified BSD License 26 | repo 27 | 28 | 29 | 30 | 31 | 32 | ctrueden 33 | Curtis Rueden 34 | https://imagej.net/User:Rueden 35 | 36 | lead 37 | developer 38 | debugger 39 | reviewer 40 | support 41 | maintainer 42 | 43 | 44 | 45 | 46 | 47 | Johannes Schindelin 48 | https://imagej.net/User:Schindelin 49 | founder 50 | dscho 51 | 52 | 53 | Albert Cardona 54 | https://imagej.net/User:Albertcardona 55 | acardona 56 | 57 | 58 | Kyle Harrington 59 | https://imagej.net/User:Kharrington 60 | kephale 61 | 62 | 63 | Mark Hiner 64 | https://imagej.net/User:Hinerm 65 | hinerm 66 | 67 | 68 | 69 | 70 | 71 | SciJava 72 | https://groups.google.com/group/scijava 73 | https://groups.google.com/group/scijava 74 | scijava@googlegroups.com 75 | https://groups.google.com/group/scijava 76 | 77 | 78 | 79 | 80 | scm:git:https://github.com/scijava/scripting-clojure 81 | scm:git:git@github.com:scijava/scripting-clojure 82 | HEAD 83 | https://github.com/scijava/scripting-clojure 84 | 85 | 86 | GitHub Issues 87 | https://github.com/scijava/scripting-clojure/issues 88 | 89 | 90 | GitHub Actions 91 | https://github.com/scijava/scripting-clojure/actions 92 | 93 | 94 | 95 | org.scijava.plugins.scripting.clojure 96 | bsd_2 97 | Board of Regents of the University of 98 | Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck 99 | Institute of Molecular Cell Biology and Genetics. 100 | 1.12.0 101 | 102 | 103 | sign,deploy-to-scijava 104 | 105 | 106 | 107 | 108 | 109 | org.scijava 110 | scijava-common 111 | 112 | 113 | 114 | 115 | org.clojure 116 | clojure 117 | ${clojure.version} 118 | 119 | 120 | 121 | 122 | junit 123 | junit 124 | test 125 | 126 | 127 | org.scijava 128 | scijava-common 129 | tests 130 | test 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /src/main/java/org/scijava/plugins/scripting/clojure/ClojureBindings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * #%L 3 | * JSR-223-compliant Clojure scripting language plugin. 4 | * %% 5 | * Copyright (C) 2013 - 2025 Board of Regents of the University of 6 | * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck 7 | * Institute of Molecular Cell Biology and Genetics. 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright notice, 15 | * this list of conditions and the following disclaimer in the documentation 16 | * and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | * #L% 30 | */ 31 | 32 | package org.scijava.plugins.scripting.clojure; 33 | 34 | import java.util.Collection; 35 | import java.util.HashMap; 36 | import java.util.Map; 37 | import java.util.Set; 38 | 39 | import javax.script.Bindings; 40 | 41 | import clojure.lang.MapEntry; 42 | import clojure.lang.Namespace; 43 | import clojure.lang.RT; 44 | import clojure.lang.Symbol; 45 | import clojure.lang.Var; 46 | 47 | /** 48 | * A {@link Bindings} wrapper around Clojure's local variables. 49 | * 50 | * @author Johannes Schindelin 51 | */ 52 | public class ClojureBindings implements Bindings { 53 | 54 | private static final String CORE_NS = "clojure.core"; 55 | private static final String USER_NS = "user"; 56 | 57 | public ClojureBindings() { 58 | final Var nameSpace = RT.var(CORE_NS, "*ns*"); 59 | Var.pushThreadBindings(RT.map(nameSpace, nameSpace.get())); 60 | RT.var(CORE_NS, "in-ns").invoke(Symbol.intern(USER_NS)); 61 | RT.var(CORE_NS, "refer").invoke(Symbol.intern(CORE_NS)); 62 | } 63 | 64 | @Override 65 | public int size() { 66 | return Var.getThreadBindings().count(); 67 | } 68 | 69 | @Override 70 | public boolean isEmpty() { 71 | return size() == 0; 72 | } 73 | 74 | @Override 75 | public boolean containsKey(final Object key) { 76 | return get(key) != null; 77 | } 78 | 79 | @Override 80 | public boolean containsValue(final Object value) { 81 | return map().containsValue(value); 82 | } 83 | 84 | @Override 85 | public Object get(final Object keyObject) { 86 | String key = (String) keyObject; 87 | final int dot = key.lastIndexOf('.'); 88 | final String nameSpace; 89 | if (dot < 0) { 90 | nameSpace = USER_NS; 91 | } 92 | else { 93 | nameSpace = key.substring(0, dot); 94 | key = key.substring(dot + 1); 95 | } 96 | try { 97 | return RT.var(nameSpace, key).get(); 98 | } 99 | catch (final Error e) { 100 | return null; 101 | } 102 | } 103 | 104 | private Object get(final String nameSpace, final String key) { 105 | return RT.var(nameSpace, key); 106 | } 107 | 108 | @Override 109 | public Object put(final String name, final Object value) { 110 | final int dot = name.lastIndexOf('.'); 111 | final String nameSpace, key; 112 | if (dot < 0) { 113 | nameSpace = USER_NS; 114 | key = name; 115 | } 116 | else { 117 | nameSpace = name.substring(0, dot); 118 | key = name.substring(dot + 1); 119 | } 120 | final Object result = get(nameSpace, key); 121 | try { 122 | final Var var = RT.var(nameSpace, key, null); 123 | var.setDynamic(); 124 | Var.pushThreadBindings(RT.map(var, value)); 125 | } 126 | catch (final Error e) { 127 | // ignore 128 | } 129 | return result; 130 | } 131 | 132 | @Override 133 | public Object remove(final Object key) { 134 | throw new UnsupportedOperationException(); 135 | } 136 | 137 | @Override 138 | public void putAll(final Map toMerge) { 139 | for (final Entry entry : toMerge 140 | .entrySet()) 141 | { 142 | put(entry.getKey(), entry.getValue()); 143 | } 144 | } 145 | 146 | @Override 147 | public void clear() { 148 | throw new UnsupportedOperationException(); 149 | } 150 | 151 | @Override 152 | public Set keySet() { 153 | return map().keySet(); 154 | } 155 | 156 | @Override 157 | public Collection values() { 158 | return map().values(); 159 | } 160 | 161 | @Override 162 | public Set> entrySet() { 163 | return map().entrySet(); 164 | } 165 | 166 | // -- Helper methods -- 167 | 168 | private static Map map() { 169 | final Map map = new HashMap(); 170 | 171 | final Namespace ns = Namespace.find(Symbol.intern(null, USER_NS)); 172 | for (final Object el : ns.getMappings()) { 173 | final MapEntry entry = (MapEntry) el; 174 | final Symbol key = (Symbol) entry.key(); 175 | // NB: Unfortunately, we cannot simply write: 176 | // final Object value = Var.intern(ns, key).get(); 177 | // because it issues a warning for already-existing variables. 178 | // So instead, we replicate some of its internals here. 179 | final Object valAt = ns.getMappings().valAt(key); 180 | final Var valVar = valAt instanceof Var ? ((Var) valAt) : null; 181 | if (valVar == null) continue; // skip non-variables 182 | if (valVar.ns != ns) continue; // skip non-user vars 183 | if (!valVar.isBound()) continue; // skip unbound vars 184 | map.put(key.getName(), valVar.get()); 185 | } 186 | 187 | return map; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/main/java/org/scijava/plugins/scripting/clojure/ClojureScriptEngine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * #%L 3 | * JSR-223-compliant Clojure scripting language plugin. 4 | * %% 5 | * Copyright (C) 2013 - 2025 Board of Regents of the University of 6 | * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck 7 | * Institute of Molecular Cell Biology and Genetics. 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright notice, 15 | * this list of conditions and the following disclaimer in the documentation 16 | * and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | * #L% 30 | */ 31 | 32 | package org.scijava.plugins.scripting.clojure; 33 | 34 | import clojure.lang.Compiler; 35 | import clojure.lang.LispReader; 36 | import clojure.lang.Var; 37 | 38 | import java.io.PushbackReader; 39 | import java.io.Reader; 40 | import java.io.StringReader; 41 | import java.io.Writer; 42 | 43 | import javax.script.ScriptContext; 44 | import javax.script.ScriptEngine; 45 | import javax.script.ScriptException; 46 | 47 | import org.scijava.script.AbstractScriptEngine; 48 | 49 | /** 50 | * A Clojure interpreter. 51 | * 52 | * @author Johannes Schindelin 53 | */ 54 | public class ClojureScriptEngine extends AbstractScriptEngine 55 | { 56 | public ClojureScriptEngine() { 57 | engineScopeBindings = new ClojureBindings(); 58 | } 59 | 60 | @Override 61 | public Object eval(final String script) throws ScriptException { 62 | try { 63 | return eval(new StringReader(script)); 64 | } 65 | catch (final Exception e) { 66 | throw new ScriptException(e); 67 | } 68 | } 69 | 70 | @Override 71 | public Object eval(final Reader reader) throws ScriptException { 72 | setup(); 73 | try { 74 | final Object filename = get(ScriptEngine.FILENAME); 75 | if (filename == null || filename instanceof Var.Unbound) { 76 | // make up a fake filename, to make clojure happy 77 | put("clojure.core.*file*", "/clojure-dynamic-script"); 78 | } 79 | else { 80 | // use the real filename 81 | put("clojure.core.*file*", filename); 82 | } 83 | final Thread thread = Thread.currentThread(); 84 | Object finalResult = null; 85 | while (!thread.isInterrupted()) { 86 | final Object form = LispReader.read(new PushbackReader(reader), false, this, false); 87 | if (form == this) break; 88 | finalResult = Compiler.eval(form); 89 | } 90 | return finalResult; 91 | } 92 | catch (final Exception e) { 93 | throw new ScriptException(e); 94 | } 95 | } 96 | 97 | protected void setup() { 98 | final ScriptContext context = getContext(); 99 | final Reader reader = context.getReader(); 100 | if (reader != null) { 101 | engineScopeBindings.put("clojure.core.*in*", reader); 102 | } 103 | final Writer writer = context.getWriter(); 104 | if (writer != null) { 105 | engineScopeBindings.put("clojure.core.*out*", writer); 106 | } 107 | final Writer errorWriter = context.getErrorWriter(); 108 | if (errorWriter != null) { 109 | engineScopeBindings.put("clojure.core.*err*", errorWriter); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/org/scijava/plugins/scripting/clojure/ClojureScriptLanguage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * #%L 3 | * JSR-223-compliant Clojure scripting language plugin. 4 | * %% 5 | * Copyright (C) 2013 - 2025 Board of Regents of the University of 6 | * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck 7 | * Institute of Molecular Cell Biology and Genetics. 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright notice, 15 | * this list of conditions and the following disclaimer in the documentation 16 | * and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | * #L% 30 | */ 31 | 32 | package org.scijava.plugins.scripting.clojure; 33 | 34 | import java.util.Arrays; 35 | import java.util.List; 36 | 37 | import javax.script.ScriptEngine; 38 | 39 | import org.scijava.plugin.Plugin; 40 | import org.scijava.script.AbstractScriptLanguage; 41 | import org.scijava.script.ScriptLanguage; 42 | 43 | import clojure.lang.Var; 44 | 45 | /** 46 | * An adapter of the Clojure interpreter to the SciJava scripting interface. 47 | * 48 | * @author Johannes Schindelin 49 | * @see ScriptEngine 50 | */ 51 | @Plugin(type = ScriptLanguage.class, name = "Clojure") 52 | public class ClojureScriptLanguage extends AbstractScriptLanguage { 53 | 54 | @Override 55 | public String getEngineName() { 56 | return "clojure"; 57 | } 58 | 59 | @Override 60 | public List getExtensions() { 61 | return Arrays.asList("clj"); 62 | } 63 | 64 | @Override 65 | public ScriptEngine getScriptEngine() { 66 | return new ClojureScriptEngine(); 67 | } 68 | 69 | @Override 70 | public Object decode(final Object object) { 71 | if (object instanceof Var.Unbound) return null; 72 | return object; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/javax.script.ScriptEngineFactory: -------------------------------------------------------------------------------- 1 | org.scijava.plugins.scripting.clojure.ClojureScriptLanguage 2 | -------------------------------------------------------------------------------- /src/test/java/org/scijava/plugins/scripting/clojure/ClojureTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * #%L 3 | * JSR-223-compliant Clojure scripting language plugin. 4 | * %% 5 | * Copyright (C) 2013 - 2025 Board of Regents of the University of 6 | * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck 7 | * Institute of Molecular Cell Biology and Genetics. 8 | * %% 9 | * Redistribution and use in source and binary forms, with or without 10 | * modification, are permitted provided that the following conditions are met: 11 | * 12 | * 1. Redistributions of source code must retain the above copyright notice, 13 | * this list of conditions and the following disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright notice, 15 | * this list of conditions and the following disclaimer in the documentation 16 | * and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | * #L% 30 | */ 31 | 32 | package org.scijava.plugins.scripting.clojure; 33 | 34 | import static org.junit.Assert.assertEquals; 35 | import static org.junit.Assert.assertFalse; 36 | import static org.junit.Assert.assertSame; 37 | import static org.junit.Assert.assertTrue; 38 | import static org.junit.Assert.fail; 39 | 40 | import java.io.IOException; 41 | import java.util.concurrent.ExecutionException; 42 | 43 | import javax.script.Bindings; 44 | import javax.script.ScriptContext; 45 | import javax.script.ScriptEngine; 46 | import javax.script.ScriptException; 47 | 48 | import org.junit.Test; 49 | import org.scijava.Context; 50 | import org.scijava.script.AbstractScriptLanguageTest; 51 | import org.scijava.script.ScriptLanguage; 52 | import org.scijava.script.ScriptModule; 53 | import org.scijava.script.ScriptService; 54 | 55 | /** 56 | * Clojure unit tests. 57 | * 58 | * @author Johannes Schindelin 59 | */ 60 | public class ClojureTest extends AbstractScriptLanguageTest { 61 | 62 | @Test 63 | public void testDiscovery() { 64 | assertDiscovered(ClojureScriptLanguage.class); 65 | } 66 | 67 | @Test 68 | public void testBasic() throws InterruptedException, ExecutionException, 69 | IOException, ScriptException 70 | { 71 | final Context context = new Context(ScriptService.class); 72 | final ScriptService scriptService = context.getService(ScriptService.class); 73 | final String script = "(+ 1 2)"; 74 | final ScriptModule m = scriptService.run("add.clj", script, true).get(); 75 | final Long result = (Long) m.getReturnValue(); 76 | assertEquals(3L, result.longValue()); 77 | } 78 | 79 | @Test 80 | public void testLocals() throws ScriptException { 81 | final Context context = new Context(ScriptService.class); 82 | final ScriptService scriptService = context.getService(ScriptService.class); 83 | 84 | final ScriptLanguage language = scriptService.getLanguageByExtension("clj"); 85 | final ScriptEngine engine = language.getScriptEngine(); 86 | assertEquals(ClojureScriptEngine.class, engine.getClass()); 87 | engine.put("$hello", 17); 88 | assertEquals("17", engine.eval("$hello").toString()); 89 | assertEquals("17", engine.get("$hello").toString()); 90 | 91 | try { 92 | final Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); 93 | bindings.clear(); 94 | fail("UnsupportedOperationException expected on clear()"); 95 | } 96 | catch (final UnsupportedOperationException exc) { 97 | // NB: Expected. 98 | } 99 | } 100 | 101 | @Test 102 | public void testParameters() throws InterruptedException, ExecutionException, 103 | IOException, ScriptException 104 | { 105 | final Context context = new Context(ScriptService.class); 106 | final ScriptService scriptService = context.getService(ScriptService.class); 107 | 108 | final String script = "" + // 109 | "; @ScriptService ss\n" + // 110 | "; @OUTPUT String language\n" + // 111 | "(def language (.getLanguageName (.getLanguageByName ss \"Clojure\")))\n"; 112 | final ScriptModule m = scriptService.run("hello.clj", script, true).get(); 113 | 114 | final Object actual = m.getOutput("language"); 115 | final String expected = 116 | scriptService.getLanguageByName("Clojure").getLanguageName(); 117 | assertEquals(expected, actual); 118 | } 119 | 120 | @Test 121 | public void testBindings() { 122 | final Context context = new Context(ScriptService.class); 123 | final ScriptService scriptService = context.getService(ScriptService.class); 124 | 125 | ScriptLanguage clojure = scriptService.getLanguageByName("clojure"); 126 | assertSame(ClojureScriptLanguage.class, clojure.getClass()); 127 | 128 | final ScriptEngine engine = clojure.getScriptEngine(); 129 | final Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); 130 | final int bSize = bindings.size(); 131 | 132 | assertEquals(bSize, bindings.keySet().size()); 133 | assertFalse(bindings.keySet().contains("foo")); 134 | 135 | engine.put("foo", "bar"); 136 | assertEquals("bar", engine.get("foo")); 137 | assertEquals("bar", bindings.get("foo")); 138 | assertEquals(bSize + 1, bindings.size()); 139 | 140 | assertEquals(bSize + 1, bindings.keySet().size()); 141 | assertTrue(bindings.keySet().contains("foo")); 142 | } 143 | } 144 | --------------------------------------------------------------------------------