Compare commits

...

3 Commits

Author SHA1 Message Date
6c1666392e
parseRes tests 2025-01-12 03:11:57 +02:00
3623842827
sanitize Source.loc argument 2025-01-12 03:10:44 +02:00
24d0cb73b6
don't use JSON for parsing metadata 2025-01-11 15:21:14 +02:00
10 changed files with 288 additions and 98 deletions

View File

@ -5,7 +5,7 @@ plugins {
description = "A collection of utils and structures for the rest of the project"; description = "A collection of utils and structures for the rest of the project";
tasks.processResources { tasks.processResources {
filesMatching("metadata.json", { filesMatching("metadata", {
expand( expand(
"version" to properties["project_version"], "version" to properties["project_version"],
"name" to properties["project_name"], "name" to properties["project_name"],

View File

@ -1,18 +1,46 @@
package me.topchetoeu.j2s.common; package me.topchetoeu.j2s.common;
import me.topchetoeu.j2s.common.json.JSON;
import me.topchetoeu.j2s.common.parsing.Filename;
public class Metadata { public class Metadata {
private static final String VERSION; private static String VERSION;
private static final String AUTHOR; private static String AUTHOR;
private static final String NAME; private static String NAME;
static { static {
var data = JSON.parse(new Filename("internal", "metadata.json"), Reading.resourceToString("metadata.json")).map(); var raw = Reading.resourceToString("metadata").split("\n");
VERSION = data.string("version"); var line = 0;
AUTHOR = data.string("author"); var file = "internal://metadata";
NAME = data.string("name");
for (var el : raw) {
line++;
el = el.trim();
if (el.startsWith("#")) continue;
if (el.isEmpty()) continue;
var i = el.indexOf(":");
if (i < 0) throw new RuntimeException(String.format("%s:%s: Expected colon on line", file, line));
var name = el.substring(0, i).trim();
var value = el.substring(i + 1).trim();
switch (name) {
case "version":
VERSION = value;
break;
case "author":
AUTHOR = value;
break;
case "name":
NAME = name;
break;
default:
throw new RuntimeException(String.format("%s:%s: Unexpected metadata key '%s'", file, line, name));
}
}
if (VERSION == null) throw new RuntimeException(String.format("%s:%s: No version specified", file, line));
if (AUTHOR == null) throw new RuntimeException(String.format("%s:%s: No author specified", file, line));
if (NAME == null) throw new RuntimeException(String.format("%s:%s: No name specified", file, line));
} }
public static String version() { public static String version() {

View File

@ -33,13 +33,14 @@ public class ParseRes<T> {
if (!state.isSuccess()) return this; if (!state.isSuccess()) return this;
return new ParseRes<>(state, null, null, result, this.n + n); return new ParseRes<>(state, null, null, result, this.n + n);
} }
@SuppressWarnings("unchecked")
public <T2> ParseRes<T2> chainError() { public <T2> ParseRes<T2> chainError() {
if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed"); if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed");
return new ParseRes<>(state, errorLocation, error, null, 0); return (ParseRes<T2>)this;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T2> ParseRes<T2> chainError(ParseRes<?> other) { public <T2> ParseRes<T2> chainError(ParseRes<?> other) {
if (!this.isError()) return other.chainError(); if (this.isError()) return other.chainError();
return (ParseRes<T2>)this; return (ParseRes<T2>)this;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -63,7 +64,6 @@ public class ParseRes<T> {
return new ParseRes<>(State.SUCCESS, null, null, val, i); return new ParseRes<>(State.SUCCESS, null, null, val, i);
} }
@SafeVarargs
@SuppressWarnings("all") @SuppressWarnings("all")
public static <T> ParseRes<T> first(Source src, int i, Parser ...parsers) { public static <T> ParseRes<T> first(Source src, int i, Parser ...parsers) {
int n = Parsing.skipEmpty(src, i); int n = Parsing.skipEmpty(src, i);

View File

@ -12,6 +12,8 @@ public class Source {
private int[] lineStarts; private int[] lineStarts;
public Location loc(int offset) { public Location loc(int offset) {
if (offset < 0) offset = 0;
if (offset > src.length()) offset = src.length();
return new SourceLocation(filename, lineStarts, offset); return new SourceLocation(filename, lineStarts, offset);
} }
public boolean is(int i, char c) { public boolean is(int i, char c) {

View File

@ -0,0 +1,3 @@
version: ${version}
name: ${name}
author: TopchetoEU

View File

@ -1,5 +0,0 @@
{
"version": "${version}",
"name": "${name}",
"author": "TopchetoEU"
}

View File

@ -1,7 +1,7 @@
package me.topchetoeu.j2s.common; package me.topchetoeu.j2s.common;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -18,55 +18,55 @@ public class TestEnvironment {
} }
@Test public void testShouldNotExist() { @Test public void testShouldNotExist() {
var env = new Environment(); var env = new Environment();
assertEquals(env.get(FOO), null); assertEquals(null, env.get(FOO));
assertEquals(env.has(FOO), false); assertEquals(false, env.has(FOO));
} }
@Test public void testShouldAdd() { @Test public void testShouldAdd() {
var env = new Environment(); var env = new Environment();
env.add(FOO, "test"); env.add(FOO, "test");
assertEquals(env.get(FOO), "test"); assertEquals("test", env.get(FOO));
} }
@Test public void testShouldGetFromParent() { @Test public void testShouldGetFromParent() {
var parent = new Environment(); var parent = new Environment();
parent.add(FOO, "test"); parent.add(FOO, "test");
var child = parent.child(); var child = parent.child();
assertEquals(child.get(FOO), "test"); assertEquals("test", child.get(FOO));
assertEquals(child.has(FOO), true); assertEquals(true, child.has(FOO));
} }
@Test public void testShouldHideParent() { @Test public void testShouldHideParent() {
var parent = new Environment(); var parent = new Environment();
parent.add(FOO, "test"); parent.add(FOO, "test");
var child = parent.child(); var child = parent.child();
child.remove(FOO); child.remove(FOO);
assertEquals(child.get(FOO), null); assertEquals(null, child.get(FOO));
assertEquals(child.has(FOO), false); assertEquals(false, child.has(FOO));
} }
@Test public void testShouldAddMarker() { @Test public void testShouldAddMarker() {
var env = new Environment(); var env = new Environment();
env.add(MARKER); env.add(MARKER);
assertEquals(env.has(MARKER), true); assertEquals(true, env.has(MARKER));
assertEquals(env.hasNotNull(MARKER), false); assertEquals(false, env.hasNotNull(MARKER));
} }
@Test public void testShouldInitOnce() { @Test public void testShouldInitOnce() {
var env = new Environment(); var env = new Environment();
assertEquals(env.init(FOO, "a"), "a"); assertEquals("a", env.init(FOO, "a"));
assertEquals(env.init(FOO, "b"), "a"); assertEquals("a", env.init(FOO, "b"));
assertEquals(env.get(FOO), "a"); assertEquals("a", env.get(FOO));
} }
@Test public void testShouldInitOnceFrom() { @Test public void testShouldInitOnceFrom() {
var env = new Environment(); var env = new Environment();
assertEquals(env.initFrom(FOO, () -> "a"), "a"); assertEquals("a", env.initFrom(FOO, () -> "a"));
assertEquals(env.initFrom(FOO, () -> "b"), "a"); assertEquals("a", env.initFrom(FOO, () -> "b"));
assertEquals(env.get(FOO), "a"); assertEquals("a", env.get(FOO));
} }
@Test public void testShouldWrap() { @Test public void testShouldWrap() {
var env = new Environment(); var env = new Environment();
assertEquals(Environment.wrap(env), env); assertEquals(env, Environment.wrap(env));
assertNotEquals(Environment.wrap(null), null); assertInstanceOf(Environment.class, Environment.wrap(null));
} }
} }

View File

@ -18,145 +18,145 @@ public class TestLocation {
@Test public void testShouldGetNextLineLocation() { @Test public void testShouldGetNextLineLocation() {
var loc = Location.of("test.txt:10:5"); var loc = Location.of("test.txt:10:5");
var next = loc.nextLine(); var next = loc.nextLine();
assertEquals(next.filename(), new Filename("file", "test.txt")); assertEquals(new Filename("file", "test.txt"), next.filename());
assertEquals(next.line(), 10); assertEquals(10, next.line());
assertEquals(next.start(), 0); assertEquals(0, next.start());
assertEquals(loc.filename(), new Filename("file", "test.txt")); assertEquals(new Filename("file", "test.txt"), loc.filename());
assertEquals(loc.line(), 9); assertEquals(9, loc.line());
assertEquals(loc.start(), 4); assertEquals(4, loc.start());
} }
@Test public void testShouldGetNextNthLineLocation() { @Test public void testShouldGetNextNthLineLocation() {
var loc = Location.of(new Filename("file", "test.txt"), 10, 5); var loc = Location.of(new Filename("file", "test.txt"), 10, 5);
var next = loc.nextLine(5); var next = loc.nextLine(5);
assertEquals(next.line(), 15); assertEquals(15, next.line());
assertEquals(next.start(), 0); assertEquals(0, next.start());
assertEquals(loc.line(), 10); assertEquals(10, loc.line());
assertEquals(loc.start(), 5); assertEquals(5, loc.start());
} }
@Test public void testShouldGetNextLocation() { @Test public void testShouldGetNextLocation() {
var loc = Location.of("test:10:5"); var loc = Location.of("test:10:5");
var next = loc.add(10); var next = loc.add(10);
assertEquals(next.filename(), new Filename("file", "test")); assertEquals(new Filename("file", "test"), next.filename());
assertEquals(next.line(), 9); assertEquals(9, next.line());
assertEquals(next.start(), 14); assertEquals(14, next.start());
assertEquals(loc.filename(), new Filename("file", "test")); assertEquals(new Filename("file", "test"), loc.filename());
assertEquals(loc.line(), 9); assertEquals(9, loc.line());
assertEquals(loc.start(), 4); assertEquals(4, loc.start());
} }
@Test public void testShouldParseLocation() { @Test public void testShouldParseLocation() {
var loc = Location.of("test.txt:10:5"); var loc = Location.of("test.txt:10:5");
assertEquals(loc.filename(), new Filename("file", "test.txt")); assertEquals(new Filename("file", "test.txt"), loc.filename());
assertEquals(loc.line(), 9); assertEquals(9, loc.line());
assertEquals(loc.start(), 4); assertEquals(4, loc.start());
} }
@Test public void testShouldParseComplexFilenameLocation() { @Test public void testShouldParseComplexFilenameLocation() {
var loc = Location.of("testificate://test.txt:10:5"); var loc = Location.of("testificate://test.txt:10:5");
assertEquals(loc.filename(), new Filename("testificate", "test.txt")); assertEquals(new Filename("testificate", "test.txt"), loc.filename());
assertEquals(loc.line(), 9); assertEquals(9, loc.line());
assertEquals(loc.start(), 4); assertEquals(4, loc.start());
} }
@Test public void testShouldParseNoFilenameLocation() { @Test public void testShouldParseNoFilenameLocation() {
var loc = Location.of("10:5"); var loc = Location.of("10:5");
assertEquals(loc.filename(), null); assertEquals(null, loc.filename());
assertEquals(loc.line(), 9); assertEquals(9, loc.line());
assertEquals(loc.start(), 4); assertEquals(4, loc.start());
} }
@Test public void testShouldParseNoStartLocationA() { @Test public void testShouldParseNoStartLocationA() {
var loc = Location.of("file://10:5"); var loc = Location.of("file://10:5");
assertEquals(loc.filename(), new Filename("file", "10")); assertEquals(new Filename("file", "10"), loc.filename());
assertEquals(loc.line(), 4); assertEquals(4, loc.line());
assertEquals(loc.start(), -1); assertEquals(-1, loc.start());
} }
@Test public void testShouldParseNoStartLocationB() { @Test public void testShouldParseNoStartLocationB() {
var loc = Location.of("file:5"); var loc = Location.of("file:5");
assertEquals(loc.filename(), new Filename("file", "file")); assertEquals(new Filename("file", "file"), loc.filename());
assertEquals(loc.line(), 4); assertEquals(4, loc.line());
assertEquals(loc.start(), -1); assertEquals(-1, loc.start());
} }
@Test public void testShouldParseOnlyFilenameLocationA() { @Test public void testShouldParseOnlyFilenameLocationA() {
var loc = Location.of("http://example.org/test.txt"); var loc = Location.of("http://example.org/test.txt");
assertEquals(loc.filename(), new Filename("http", "example.org/test.txt")); assertEquals(new Filename("http", "example.org/test.txt"), loc.filename());
assertEquals(loc.line(), -1); assertEquals(-1, loc.line());
assertEquals(loc.start(), -1); assertEquals(-1, loc.start());
} }
@Test public void testShouldParseOnlyFilenameLocationB() { @Test public void testShouldParseOnlyFilenameLocationB() {
var loc = Location.of("test.txt"); var loc = Location.of("test.txt");
assertEquals(loc.filename(), new Filename("file", "test.txt")); assertEquals(new Filename("file", "test.txt"), loc.filename());
assertEquals(loc.line(), -1); assertEquals(-1, loc.line());
assertEquals(loc.start(), -1); assertEquals(-1, loc.start());
} }
@Test public void testShouldParseOnlyFilenameWithColonLocation() { @Test public void testShouldParseOnlyFilenameWithColonLocation() {
var loc = Location.of("my-file:bad-file"); var loc = Location.of("my-file:bad-file");
assertEquals(loc.filename(), new Filename("file", "my-file:bad-file")); assertEquals(new Filename("file", "my-file:bad-file"), loc.filename());
assertEquals(loc.line(), -1); assertEquals(-1, loc.line());
assertEquals(loc.start(), -1); assertEquals(-1, loc.start());
} }
@Test public void testShouldParseOnlyFilenameWithTripleColonLocation() { @Test public void testShouldParseOnlyFilenameWithTripleColonLocation() {
var loc = Location.of("a:my-file:bad-file"); var loc = Location.of("a:my-file:bad-file");
assertEquals(loc.filename(), new Filename("file", "a:my-file:bad-file")); assertEquals(new Filename("file", "a:my-file:bad-file"), loc.filename());
assertEquals(loc.line(), -1); assertEquals(-1, loc.line());
assertEquals(loc.start(), -1); assertEquals(-1, loc.start());
} }
@Test public void testCompareEqualLoc() { @Test public void testCompareEqualLoc() {
var locA = Location.of("test:10:5"); var locA = Location.of("test:10:5");
var locB = Location.of("test:10:5"); var locB = Location.of("test:10:5");
assertEquals(locA.compareTo(locB), 0); assertEquals(0, locA.compareTo(locB));
assertEquals(locB.compareTo(locA), 0); assertEquals(0, locB.compareTo(locA));
} }
@Test public void testCompareNoFileLoc() { @Test public void testCompareNoFileLoc() {
var locA = Location.of("10:5"); var locA = Location.of("10:5");
var locB = Location.of("11:5"); var locB = Location.of("11:5");
assertEquals(locA.compareTo(locB), -1); assertEquals(-1, locA.compareTo(locB));
assertEquals(locB.compareTo(locA), 1); assertEquals(1, locB.compareTo(locA));
} }
@Test public void testCompareOneNoFileLoc() { @Test public void testCompareOneNoFileLoc() {
var locA = Location.of("10:5"); var locA = Location.of("10:5");
var locB = Location.of("test:10:5"); var locB = Location.of("test:10:5");
assertEquals(locA.compareTo(locB), -1); assertEquals(-1, locA.compareTo(locB));
assertEquals(locB.compareTo(locA), 1); assertEquals(1, locB.compareTo(locA));
} }
@Test public void testCompareDiffFileLoc() { @Test public void testCompareDiffFileLoc() {
var locA = Location.of("a:10:5"); var locA = Location.of("a:10:5");
var locB = Location.of("b:10:5"); var locB = Location.of("b:10:5");
assertEquals(locA.compareTo(locB), -1); assertEquals(-1, locA.compareTo(locB));
assertEquals(locB.compareTo(locA), 1); assertEquals(1, locB.compareTo(locA));
} }
@Test public void testCompareDiffLineLoc() { @Test public void testCompareDiffLineLoc() {
var locA = Location.of("test:10:5"); var locA = Location.of("test:10:5");
var locB = Location.of("test:11:5"); var locB = Location.of("test:11:5");
assertEquals(locA.compareTo(locB), -1); assertEquals(-1, locA.compareTo(locB));
assertEquals(locB.compareTo(locA), 1); assertEquals(1, locB.compareTo(locA));
} }
@Test public void testCompareDiffStartLoc() { @Test public void testCompareDiffStartLoc() {
var locA = Location.of("test:10:5"); var locA = Location.of("test:10:5");
var locB = Location.of("test:10:10"); var locB = Location.of("test:10:10");
assertEquals(locA.compareTo(locB), -1); assertEquals(-1, locA.compareTo(locB));
assertEquals(locB.compareTo(locA), 1); assertEquals(1, locB.compareTo(locA));
} }
@Test public void testToStringAll() { @Test public void testToStringAll() {
var locA = Location.of("test:10:5"); var locA = Location.of("test:10:5");
assertEquals(locA.toString(), "file://test:10:5"); assertEquals("file://test:10:5", locA.toString());
} }
@Test public void testToStringNoFilename() { @Test public void testToStringNoFilename() {
var locA = Location.of("10:5"); var locA = Location.of("10:5");
assertEquals(locA.toString(), "10:5"); assertEquals("10:5", locA.toString());
} }
@Test public void testToStringNoStart() { @Test public void testToStringNoStart() {
var locA = Location.of("file:5"); var locA = Location.of("file:5");
assertEquals(locA.toString(), "file://file:5"); assertEquals("file://file:5", locA.toString());
} }
@Test public void testToStringNoLoc() { @Test public void testToStringNoLoc() {
var locA = Location.of("file"); var locA = Location.of("file");
assertEquals(locA.toString(), "file://file"); assertEquals("file://file", locA.toString());
} }
} }

View File

@ -0,0 +1,98 @@
package me.topchetoeu.j2s.common;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
public class TestParseRes {
@Test public void testCreateFailed() {
var res = ParseRes.failed();
assertEquals(false, res.isSuccess());
assertEquals(true, res.isFailed());
assertEquals(false, res.isError());
assertEquals(0, res.n);
assertEquals(null, res.result);
assertEquals(null, res.errorLocation);
assertEquals(null, res.error);
}
@Test public void testCreateError() {
var res = ParseRes.error(Location.of("test:10:5"), "test");
assertEquals(false, res.isSuccess());
assertEquals(false, res.isFailed());
assertEquals(true, res.isError());
assertEquals(0, res.n);
assertEquals(null, res.result);
assertEquals(Location.of("test:10:5"), res.errorLocation);
assertEquals("test", res.error);
}
@Test public void testCreateResult() {
var res = ParseRes.res("test", 10);
assertEquals(true, res.isSuccess());
assertEquals(false, res.isFailed());
assertEquals(false, res.isError());
assertEquals(10, res.n);
assertEquals("test", res.result);
assertEquals(null, res.errorLocation);
assertEquals(null, res.error);
}
@Test public void testChainFailed() {
var a = ParseRes.<Integer>failed();
var b1 = a.<String>chainError();
assertEquals(a, b1);
var b2 = a.<String>chainError(Location.of("test:1:2"), "test");
assertEquals(true, b2.isError());
assertEquals("test", b2.error);
}
@Test public void testChainError() {
var a = ParseRes.<Integer>error(Location.of("test:1:2"), "test");
var b1 = a.<String>chainError();
assertEquals(a, b1);
var b2 = a.<String>chainError(Location.of("test:1:2"), "test");
assertEquals(a, b2);
}
@Test public void testChainResult() {
var a = ParseRes.<Integer>res(10, 6);
assertThrows(RuntimeException.class, () -> a.<String>chainError());
var b1 = a.<String>chainError(Location.of("test:1:2"), "test");
assertEquals(true, b1.isError());
assertEquals("test", b1.error);
}
@Test public void testShouldAdd5() {
var a = ParseRes.res("test", 6);
var b = a.addN(5);
assertEquals(11, b.n);
}
@Test public void testShouldSet5() {
var a = ParseRes.res("test", 6);
var b = a.setN(5);
assertEquals(5, b.n);
}
@Test public void testShouldNotAdd() {
var a = ParseRes.failed();
var b = a.addN(5);
assertEquals(0, b.n);
}
@Test public void testShouldNotSet() {
var a = ParseRes.failed();
var b = a.setN(5);
assertEquals(0, b.n);
}
}

View File

@ -0,0 +1,64 @@
package me.topchetoeu.j2s.common;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
import me.topchetoeu.j2s.common.environment.Environment;
import me.topchetoeu.j2s.common.parsing.Filename;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.Source;
public class TestSource {
private Source mkSource(String src) {
return new Source(new Environment(), Filename.parse("test"), src);
}
@Test public void testShouldCreate() {
new Source(new Environment(), Filename.parse("test"), "my little source :)");
new Source(null, Filename.parse("test"), "my little source :)");
}
@Test public void testShouldGet() {
var src = mkSource("1234567890");
assertEquals('1', src.at(0));
assertEquals('6', src.at(5));
}
@Test public void testShouldThrowOutOfRange() {
var src = mkSource("1234567890");
assertThrows(IndexOutOfBoundsException.class, () -> src.at(-1));
assertThrows(IndexOutOfBoundsException.class, () -> src.at(10));
}
@Test public void testImmutableSrcLoc() {
var src = mkSource("1234567890");
var loc = src.loc(5);
// kinda stupid
for (var i = 0; i < 1000; i++) {
assertEquals(5, loc.start());
assertEquals(0, loc.line());
assertEquals(Filename.parse("test"), loc.filename());
}
}
@Test public void testSingleLineSourceLocation() {
var src = mkSource("1234567890");
assertEquals(Location.of("test:1:1"), src.loc(-5));
assertEquals(Location.of("test:1:1"), src.loc(0));
assertEquals(Location.of("test:1:10"), src.loc(9));
assertEquals(Location.of("test:1:11"), src.loc(14));
}
@Test public void testMultilineSourceLocation() {
var src = mkSource("123\n456\n\n789\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
assertEquals(Location.of("test:1:1"), src.loc(-5));
assertEquals(Location.of("test:1:1"), src.loc(0));
assertEquals(Location.of("test:1:4"), src.loc(3));
assertEquals(Location.of("test:2:1"), src.loc(4));
assertEquals(Location.of("test:2:4"), src.loc(7));
assertEquals(Location.of("test:3:1"), src.loc(8));
assertEquals(Location.of("test:4:1"), src.loc(9));
assertEquals(Location.of("test:4:2"), src.loc(10));
assertEquals(Location.of("test:4:4"), src.loc(12));
assertEquals(Location.of("test:5:1"), src.loc(13));
assertEquals(Location.of("test:19:1"), src.loc(50));
}
}