Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Java CI

on: [push]

jobs:
build:
timeout-minutes: 5
strategy:
matrix:
jdk: ['8', '11', '17', '19']
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up JDK ${{ matrix.jdk }}
uses: actions/setup-java@v2
with:
java-version: ${{ matrix.jdk }}
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn test
- name: Configure settings.xml
run: |
mkdir -p ~/.m2
echo "<settings><servers><server><id>clojars</id><username>${{ secrets.CLOJARS_USER }}-clojars</username><password>${{ secrets.CLOJARS_PASSWORD }}</password></server></servers></settings>" > ~/.m2/settings.xml
38 changes: 38 additions & 0 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
;; https://clojure.org/dev/developing_patches#_run_an_individual_test
{:paths ["test"
"target/test-classes"]
:deps
{org.clojure/clojure {:local/root "."
:deps/manifest :pom} #_{:mvn/version "RELEASE"}
org.clojure/test.check {:mvn/version "1.1.1"}
org.clojure/test.generative {:mvn/version "1.0.0"}}
:aliases
{:dbg {:classpath-overrides {org.clojure/clojure "target/classes"}
:extra-deps {criterium/criterium {:mvn/version "0.4.4"}}}
:cognitest {:extra-deps {io.github.cognitect-labs/test-runner
{:git/tag "v0.5.0" :git/sha "b3fd0d2"}}
:main-opts ["-m" "cognitect.test-runner"]
:exec-fn cognitect.test-runner.api/test
:exec-args {:dirs ["test"]
:patterns [;; FIXME clojure.test-clojure.ns-libs has a test that is sensitive to loading order
;; FIXME clojure.test-clojure.java-interop doesn't seem to work on JDK 17 (untested on others)
;; regex ref: https://stackoverflow.com/a/2387072
"^((?!(clojure.test-clojure.ns-libs|clojure.test-clojure.java-interop)).)*$"
]}}
:test-example-script {:jvm-opts [;; from build.xml
"-Dclojure.test-clojure.exclude-namespaces=#{clojure.test-clojure.compilation.load-ns clojure.test-clojure.ns-libs-load-later}"
"-Dclojure.compiler.direct-linking=true"]
:main-opts ["-e" "(load-file,\"src/script/run_test.clj\")"]}
:test-generative-script {:jvm-opts [;; from build.xml
"-Dclojure.compiler.direct-linking=true"]
:main-opts ["-e" "(load-file,\"src/script/run_test_generative.clj\")"]}

:kaocha {:extra-deps {lambdaisland/kaocha {:mvn/version "1.60.977"}}
:exec-fn kaocha.runner/exec-fn
:exec-args {;:watch? true
:tests [{:id :unit
:test-paths ["test"]
:ns-patterns [".*"]}]
:reporter kaocha.report/dots
;; :plugins [:kaocha.plugin/profiling :kaocha.plugin/notifier]
}}}}
167 changes: 84 additions & 83 deletions src/jvm/clojure/lang/LispReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public class LispReader{
"([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+)|0[0-9]+)(N)?");
static Pattern ratioPat = Pattern.compile("([-+]?[0-9]+)/([0-9]+)");
static Pattern floatPat = Pattern.compile("([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?");
static Pattern argPat = Pattern.compile("%(?:(&)|([1-9][0-9]*))?");
//static Pattern accessorPat = Pattern.compile("\\.[a-zA-Z_]\\w*");
//static Pattern instanceMemberPat = Pattern.compile("\\.([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)");
//static Pattern staticMemberPat = Pattern.compile("([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)");
Expand Down Expand Up @@ -253,66 +254,76 @@ static private Object read(PushbackReader r, boolean eofIsError, Object eofValue
{
for(; ;)
{
Object ret = readSome(r, eofIsError, eofValue, returnOn, returnOnValue, opts, pendingForms, resolver);
if (ret != r) return ret;
}
}
catch(Exception e)
{
if(isRecursive || !(r instanceof LineNumberingPushbackReader))
throw Util.sneakyThrow(e);
LineNumberingPushbackReader rdr = (LineNumberingPushbackReader) r;
//throw Util.runtimeException(String.format("ReaderError:(%d,1) %s", rdr.getLineNumber(), e.getMessage()), e);
throw new ReaderException(rdr.getLineNumber(), rdr.getColumnNumber(), e);
}
}

if(pendingForms instanceof List && !((List)pendingForms).isEmpty())
return ((List)pendingForms).remove(0);

int ch = read1(r);
private static Object readSome(PushbackReader r, boolean eofIsError, Object eofValue, Object opts, Object pendingForms) {
return readSome(r, eofIsError, eofValue, null, null, opts, pendingForms, (Resolver) RT.READER_RESOLVER.deref());
}

while(isWhitespace(ch))
ch = read1(r);
private static Object readSome(PushbackReader r, boolean eofIsError, Object eofValue, Character returnOn, Object returnOnValue, Object opts, Object pendingForms, Resolver resolver) {
if(pendingForms instanceof List && !((List)pendingForms).isEmpty())
return ((List)pendingForms).remove(0);

if(ch == -1)
{
if(eofIsError)
throw Util.runtimeException("EOF while reading");
return eofValue;
}
int ch = read1(r);

if(returnOn != null && (returnOn.charValue() == ch)) {
return returnOnValue;
}
if(isWhitespace(ch)) {
do
ch = read1(r);
while(isWhitespace(ch));
unread(r, ch);
return r;
}

if(Character.isDigit(ch))
{
Object n = readNumber(r, (char) ch);
return n;
}
if(ch == -1)
{
if(eofIsError)
throw Util.runtimeException("EOF while reading");
return eofValue;
}

IFn macroFn = getMacro(ch);
if(macroFn != null)
{
Object ret = macroFn.invoke(r, (char) ch, opts, pendingForms);
//no op macros return the reader
if(ret == r)
continue;
return ret;
}
if(returnOn != null && (returnOn.charValue() == ch)) {
return returnOnValue;
}

if(ch == '+' || ch == '-')
{
int ch2 = read1(r);
if(Character.isDigit(ch2))
{
unread(r, ch2);
Object n = readNumber(r, (char) ch);
return n;
}
unread(r, ch2);
}
if(Character.isDigit(ch))
{
Object n = readNumber(r, (char) ch);
return n;
}

String token = readToken(r, (char) ch);
return interpretToken(token, resolver);
}
IFn macroFn = getMacro(ch);
if(macroFn != null)
{
Object ret = macroFn.invoke(r, (char) ch, opts, pendingForms);
return ret;
}
catch(Exception e)

if(ch == '+' || ch == '-')
{
if(isRecursive || !(r instanceof LineNumberingPushbackReader))
throw Util.sneakyThrow(e);
LineNumberingPushbackReader rdr = (LineNumberingPushbackReader) r;
//throw Util.runtimeException(String.format("ReaderError:(%d,1) %s", rdr.getLineNumber(), e.getMessage()), e);
throw new ReaderException(rdr.getLineNumber(), rdr.getColumnNumber(), e);
int ch2 = read1(r);
if(Character.isDigit(ch2))
{
unread(r, ch2);
Object n = readNumber(r, (char) ch);
return n;
}
unread(r, ch2);
}

String token = readToken(r, (char) ch);
return interpretToken(token, resolver);
}

static private String readToken(PushbackReader r, char initch) {
Expand Down Expand Up @@ -649,23 +660,24 @@ public Object invoke(Object reader, Object colon, Object opts, Object pendingFor
else
unread(r, autoChar);

Object sym = null;
Symbol sym = null;
int nextChar = read1(r);
if(isWhitespace(nextChar)) { // the #:: { } case or an error
if(auto) {
while (isWhitespace(nextChar))
nextChar = read1(r);
if(nextChar != '{') {
unread(r, nextChar);
throw Util.runtimeException("Namespaced map must specify a namespace");
}
} else {
if(isWhitespace(nextChar) && auto) { // the #:: { } case or an error
while (isWhitespace(nextChar))
nextChar = read1(r);
if(nextChar != '{') {
unread(r, nextChar);
throw Util.runtimeException("Namespaced map must specify a namespace");
}
} else if(nextChar != '{') { // #:foo { } or #::foo { }
unread(r, nextChar);
sym = read(r, true, null, false, opts, pendingForms);
Object ret = readSome(r, true, null, opts, pendingForms);
if (!(ret instanceof Symbol))
throw Util.runtimeException("Namespaced map must specify a namespace");
sym = (Symbol) ret;
if (sym.getNamespace() != null)
throw Util.runtimeException("Namespaced map must specify a valid namespace: " + sym);

nextChar = read1(r);
while(isWhitespace(nextChar))
nextChar = read1(r);
Expand All @@ -682,14 +694,12 @@ public Object invoke(Object reader, Object colon, Object opts, Object pendingFor
ns = resolver.currentNS().name;
else
ns = Compiler.currentNS().getName().getName();
} else if (!(sym instanceof Symbol) || ((Symbol)sym).getNamespace() != null) {
throw Util.runtimeException("Namespaced map must specify a valid namespace: " + sym);
} else {
Symbol resolvedNS;
if (resolver != null)
resolvedNS = resolver.resolveAlias((Symbol) sym);
resolvedNS = resolver.resolveAlias(sym);
else{
Namespace rns = Compiler.currentNS().lookupAlias((Symbol)sym);
Namespace rns = Compiler.currentNS().lookupAlias(sym);
resolvedNS = rns != null?rns.getName():null;
}

Expand All @@ -699,10 +709,8 @@ public Object invoke(Object reader, Object colon, Object opts, Object pendingFor
ns = resolvedNS.getName();
}
}
} else if (!(sym instanceof Symbol) || ((Symbol)sym).getNamespace() != null) {
throw Util.runtimeException("Namespaced map must specify a valid namespace: " + sym);
} else {
ns = ((Symbol)sym).getName();
ns = sym.getName();
}

// Read map
Expand Down Expand Up @@ -920,23 +928,16 @@ static Symbol registerArg(int n){
static class ArgReader extends AFn{
public Object invoke(Object reader, Object pct, Object opts, Object pendingForms) {
PushbackReader r = (PushbackReader) reader;
String token = readToken(r, '%');
if(ARG_ENV.deref() == null)
{
return interpretToken(readToken(r, '%'), null);
}
int ch = read1(r);
unread(r, ch);
//% alone is first arg
if(ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch))
{
return registerArg(1);
}
Object n = read(r, true, null, true, opts, ensurePending(pendingForms));
if(n.equals(Compiler._AMP_))
return registerArg(-1);
if(!(n instanceof Number))
return interpretToken(token, null);

Matcher m = argPat.matcher(token);
if (!m.matches())
throw new IllegalStateException("arg literal must be %, %& or %integer");
return registerArg(((Number) n).intValue());
if (m.group(1) != null) // %&
return registerArg(-1);
return registerArg(m.group(2) == null ? 1 : Integer.parseInt(m.group(2)));
}
}

Expand Down Expand Up @@ -1415,7 +1416,7 @@ public static class CtorReader extends AFn{
public Object invoke(Object reader, Object firstChar, Object opts, Object pendingForms){
PushbackReader r = (PushbackReader) reader;
pendingForms = ensurePending(pendingForms);
Object name = read(r, true, null, false, opts, pendingForms);
Object name = readSome(r, true, null, opts, pendingForms);
if (!(name instanceof Symbol))
throw new RuntimeException("Reader tag must be a symbol");
Symbol sym = (Symbol)name;
Expand Down