aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2025-01-01 01:39:18 +0000
committerMichael Williamson <mike@zwobble.org>2025-01-01 09:45:58 +0000
commitb65c719e2b65a9662b6170b0b53dfed9f1a3edcd (patch)
tree6700b1a717d01a83b644dd06eb508e12a43b492c /src/main/java
parent0df6725da8a0953c5643c5692c5244826e448d09 (diff)
Use JSON5 instead of TOML for config
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/org/shedlang/compiler/config/BinConfig.java8
-rw-r--r--src/main/java/org/shedlang/compiler/config/ExampleConfig.java12
-rw-r--r--src/main/java/org/shedlang/compiler/config/GcConfig.java8
-rw-r--r--src/main/java/org/shedlang/compiler/config/GcMarkAndSweepConfig.java6
-rw-r--r--src/main/java/org/shedlang/compiler/config/ProfileConfig.java13
-rw-r--r--src/main/java/org/shedlang/compiler/config/ProjectConfig.java42
-rw-r--r--src/main/java/org/shedlang/compiler/config/errors/Json5SyntaxConfigError.java19
-rw-r--r--src/main/java/org/shedlang/compiler/config/errors/TomlSyntaxConfigError.java19
-rw-r--r--src/main/java/org/shedlang/compiler/config/errors/UnexpectedTypeConfigError.java38
-rw-r--r--src/main/java/org/shedlang/compiler/config/json/Json5ObjectWithSource.java170
-rw-r--r--src/main/java/org/shedlang/compiler/config/json/Json5Path.java36
-rw-r--r--src/main/java/org/shedlang/compiler/config/json/Json5Source.java18
-rw-r--r--src/main/java/org/shedlang/compiler/config/json/Json5StringWithSource.java28
-rw-r--r--src/main/java/org/shedlang/compiler/config/toml/TomlPath.java35
-rw-r--r--src/main/java/org/shedlang/compiler/config/toml/TomlSource.java20
-rw-r--r--src/main/java/org/shedlang/compiler/config/toml/TomlString.java6
-rw-r--r--src/main/java/org/shedlang/compiler/config/toml/TomlTableWithSource.java214
17 files changed, 343 insertions, 349 deletions
diff --git a/src/main/java/org/shedlang/compiler/config/BinConfig.java b/src/main/java/org/shedlang/compiler/config/BinConfig.java
index e91a6af4..5f2b0a7d 100644
--- a/src/main/java/org/shedlang/compiler/config/BinConfig.java
+++ b/src/main/java/org/shedlang/compiler/config/BinConfig.java
@@ -1,7 +1,7 @@
package org.shedlang.compiler.config;
import org.shedlang.compiler.config.errors.ConfigError;
-import org.shedlang.compiler.config.toml.TomlTableWithSource;
+import org.shedlang.compiler.config.json.Json5ObjectWithSource;
import org.shedlang.compiler.parser.SourceParser;
import org.shedlang.compiler.sources.Sources;
import org.shedlang.compiler.tokeniser.UnexpectedTokenException;
@@ -10,10 +10,10 @@ import org.shedlang.compiler.types.NamespaceName;
import java.text.MessageFormat;
public record BinConfig(String name, NamespaceName mainNamespace) {
- public static BinConfig fromToml(TomlTableWithSource table) {
- var name = table.getString("name").value();
+ public static BinConfig fromJson5(Json5ObjectWithSource object) {
+ var name = object.getString("name").value();
- var mainNamespaceString = table.getString("main-namespace");
+ var mainNamespaceString = object.getString("mainNamespace");
try {
var mainNamespace = SourceParser.parseNamespaceName(Sources.string(mainNamespaceString.value()));
diff --git a/src/main/java/org/shedlang/compiler/config/ExampleConfig.java b/src/main/java/org/shedlang/compiler/config/ExampleConfig.java
index abc311d3..1135066f 100644
--- a/src/main/java/org/shedlang/compiler/config/ExampleConfig.java
+++ b/src/main/java/org/shedlang/compiler/config/ExampleConfig.java
@@ -1,7 +1,7 @@
package org.shedlang.compiler.config;
-import org.shedlang.compiler.config.toml.TomlString;
-import org.shedlang.compiler.config.toml.TomlTableWithSource;
+import org.shedlang.compiler.config.json.Json5ObjectWithSource;
+import org.shedlang.compiler.config.json.Json5StringWithSource;
import java.util.Optional;
@@ -10,11 +10,11 @@ public record ExampleConfig(
Optional<String> expectedOutput,
Optional<String> expectedError
) {
- public static ExampleConfig fromToml(TomlTableWithSource table) {
+ public static ExampleConfig fromJson5(Json5ObjectWithSource object) {
return new ExampleConfig(
- table.getString("bin").value(),
- table.getStringOrNone("expected-output").map(TomlString::value),
- table.getStringOrNone("expected-error").map(TomlString::value)
+ object.getString("bin").value(),
+ object.getStringOrNone("expectedOutput").map(Json5StringWithSource::value),
+ object.getStringOrNone("expectedError").map(Json5StringWithSource::value)
);
}
}
diff --git a/src/main/java/org/shedlang/compiler/config/GcConfig.java b/src/main/java/org/shedlang/compiler/config/GcConfig.java
index fad51f63..312c2f96 100644
--- a/src/main/java/org/shedlang/compiler/config/GcConfig.java
+++ b/src/main/java/org/shedlang/compiler/config/GcConfig.java
@@ -1,6 +1,6 @@
package org.shedlang.compiler.config;
-import org.shedlang.compiler.config.toml.TomlTableWithSource;
+import org.shedlang.compiler.config.json.Json5ObjectWithSource;
import java.util.List;
@@ -8,15 +8,15 @@ public sealed interface GcConfig permits GcBumpConfig, GcMarkAndSweepConfig {
GcConfig BUMP = new GcBumpConfig();
GcConfig DEFAULT = GcMarkAndSweepConfig.DEFAULT;
- static GcConfig fromToml(TomlTableWithSource table) {
- var name = table.getString("name").value();
+ static GcConfig fromJson5(Json5ObjectWithSource object) {
+ var name = object.getString("name").value();
return switch (name) {
case "bump" ->
BUMP;
case "mark-and-sweep" ->
- GcMarkAndSweepConfig.fromToml(table);
+ GcMarkAndSweepConfig.fromJson5(object);
default ->
throw new IllegalArgumentException("Unrecognised GC name: \"%s\"".formatted(name));
diff --git a/src/main/java/org/shedlang/compiler/config/GcMarkAndSweepConfig.java b/src/main/java/org/shedlang/compiler/config/GcMarkAndSweepConfig.java
index 0c2ea9bb..8ac9ce56 100644
--- a/src/main/java/org/shedlang/compiler/config/GcMarkAndSweepConfig.java
+++ b/src/main/java/org/shedlang/compiler/config/GcMarkAndSweepConfig.java
@@ -1,6 +1,6 @@
package org.shedlang.compiler.config;
-import org.shedlang.compiler.config.toml.TomlTableWithSource;
+import org.shedlang.compiler.config.json.Json5ObjectWithSource;
import java.util.ArrayList;
import java.util.List;
@@ -13,8 +13,8 @@ public record GcMarkAndSweepConfig(
OptionalLong.empty()
);
- public static GcMarkAndSweepConfig fromToml(TomlTableWithSource table) {
- var collectInterval = table.getLongOrNone("collect-interval");
+ public static GcMarkAndSweepConfig fromJson5(Json5ObjectWithSource object) {
+ var collectInterval = object.getLongOrNone("collectInterval");
return new GcMarkAndSweepConfig(collectInterval);
}
diff --git a/src/main/java/org/shedlang/compiler/config/ProfileConfig.java b/src/main/java/org/shedlang/compiler/config/ProfileConfig.java
index cf8b4998..d922a4cb 100644
--- a/src/main/java/org/shedlang/compiler/config/ProfileConfig.java
+++ b/src/main/java/org/shedlang/compiler/config/ProfileConfig.java
@@ -1,19 +1,16 @@
package org.shedlang.compiler.config;
-import org.shedlang.compiler.config.toml.TomlTableWithSource;
+import org.shedlang.compiler.config.json.Json5ObjectWithSource;
import java.util.Optional;
public record ProfileConfig(String name, Optional<GcConfig> gc) {
public static final ProfileConfig DEFAULT = new ProfileConfig("dev", Optional.empty());
- public static ProfileConfig fromToml(TomlTableWithSource table) {
- var name = table.getString("name").value();
- var gc = table.getTable("gc").map(GcConfig::fromToml);
+ public static ProfileConfig fromJson5(Json5ObjectWithSource object) {
+ var name = object.getString("name").value();
+ var gc = object.getObject("gc").map(GcConfig::fromJson5);
- return new ProfileConfig(
- name,
- gc
- );
+ return new ProfileConfig(name, gc);
}
}
diff --git a/src/main/java/org/shedlang/compiler/config/ProjectConfig.java b/src/main/java/org/shedlang/compiler/config/ProjectConfig.java
index f6ddb80f..0408a143 100644
--- a/src/main/java/org/shedlang/compiler/config/ProjectConfig.java
+++ b/src/main/java/org/shedlang/compiler/config/ProjectConfig.java
@@ -1,6 +1,6 @@
package org.shedlang.compiler.config;
-import org.shedlang.compiler.config.toml.TomlTableWithSource;
+import org.shedlang.compiler.config.json.Json5ObjectWithSource;
import org.shedlang.compiler.sources.Sources;
import java.io.IOException;
@@ -15,55 +15,49 @@ public record ProjectConfig(
List<ProfileConfig> profiles
) {
public static ProjectConfig read(Path projectPath) throws IOException {
- var configPath = projectPath.resolve("shed.toml");
+ var configPath = projectPath.resolve("shed.json5");
var contents = Files.readString(configPath);
var source = Sources.file(configPath, contents);
- return fromToml(projectPath, TomlTableWithSource.parse(source));
+ return fromJson5(projectPath, Json5ObjectWithSource.parse(source));
}
public static ProjectConfig fromString(Path projectPath, String config) {
- return fromToml(
+ return fromJson5(
projectPath,
- TomlTableWithSource.parse(Sources.string(config))
+ Json5ObjectWithSource.parse(Sources.string(config))
);
}
- public static ProjectConfig fromToml(Path root, TomlTableWithSource table) {
+ public static ProjectConfig fromJson5(Path root, Json5ObjectWithSource object) {
return new ProjectConfig(
root,
- binsFromToml(table),
- examplesFromToml(table),
- profilesFromToml(table)
+ binsFromToml(object),
+ examplesFromToml(object),
+ profilesFromToml(object)
);
}
- private static List<BinConfig> binsFromToml(TomlTableWithSource table) {
- return table
- .getTable("shed")
- .flatMap(shed -> shed.getArrayOfTables("bins"))
+ private static List<BinConfig> binsFromToml(Json5ObjectWithSource object) {
+ return object.getArrayOfObjects("bins")
.orElseGet(() -> List.of())
.stream()
- .map(bin -> BinConfig.fromToml(bin))
+ .map(bin -> BinConfig.fromJson5(bin))
.toList();
}
- public static List<ExampleConfig> examplesFromToml(TomlTableWithSource table) {
- return table
- .getTable("shed")
- .flatMap(shed -> shed.getArrayOfTables("examples"))
+ public static List<ExampleConfig> examplesFromToml(Json5ObjectWithSource object) {
+ return object.getArrayOfObjects("examples")
.orElseGet(() -> List.of())
.stream()
- .map(example -> ExampleConfig.fromToml(example))
+ .map(example -> ExampleConfig.fromJson5(example))
.toList();
}
- public static List<ProfileConfig> profilesFromToml(TomlTableWithSource table) {
- return table
- .getTable("shed")
- .flatMap(shed -> shed.getArrayOfTables("profiles"))
+ public static List<ProfileConfig> profilesFromToml(Json5ObjectWithSource object) {
+ return object.getArrayOfObjects("profiles")
.orElseGet(() -> List.of())
.stream()
- .map(profile -> ProfileConfig.fromToml(profile))
+ .map(profile -> ProfileConfig.fromJson5(profile))
.toList();
}
diff --git a/src/main/java/org/shedlang/compiler/config/errors/Json5SyntaxConfigError.java b/src/main/java/org/shedlang/compiler/config/errors/Json5SyntaxConfigError.java
new file mode 100644
index 00000000..d0db7f6c
--- /dev/null
+++ b/src/main/java/org/shedlang/compiler/config/errors/Json5SyntaxConfigError.java
@@ -0,0 +1,19 @@
+package org.shedlang.compiler.config.errors;
+
+import org.shedlang.compiler.config.json.Json5Source;
+import org.shedlang.compiler.sources.Source;
+import org.zwobble.json5.parser.Json5ParseError;
+
+public class Json5SyntaxConfigError extends ConfigError {
+ public static Json5SyntaxConfigError fromJson5ParseError(
+ Json5ParseError error,
+ Source fullSource
+ ) {
+ var source = Json5Source.fromSourceRange(fullSource, error.sourceRange());
+ return new Json5SyntaxConfigError(error.getMessage(), source);
+ }
+
+ public Json5SyntaxConfigError(String message, Source source) {
+ super(message, source);
+ }
+}
diff --git a/src/main/java/org/shedlang/compiler/config/errors/TomlSyntaxConfigError.java b/src/main/java/org/shedlang/compiler/config/errors/TomlSyntaxConfigError.java
deleted file mode 100644
index aa034738..00000000
--- a/src/main/java/org/shedlang/compiler/config/errors/TomlSyntaxConfigError.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.shedlang.compiler.config.errors;
-
-import org.shedlang.compiler.config.toml.TomlSource;
-import org.shedlang.compiler.sources.Source;
-import org.tomlj.TomlParseError;
-
-public class TomlSyntaxConfigError extends ConfigError {
- public static TomlSyntaxConfigError fromTomljError(
- TomlParseError error,
- Source fullSource
- ) {
- var source = TomlSource.sourceAtTomlPosition(fullSource, error.position());
- return new TomlSyntaxConfigError(error.getMessage(), source);
- }
-
- public TomlSyntaxConfigError(String message, Source source) {
- super(message, source);
- }
-}
diff --git a/src/main/java/org/shedlang/compiler/config/errors/UnexpectedTypeConfigError.java b/src/main/java/org/shedlang/compiler/config/errors/UnexpectedTypeConfigError.java
index 0b0eda67..1a13f3a2 100644
--- a/src/main/java/org/shedlang/compiler/config/errors/UnexpectedTypeConfigError.java
+++ b/src/main/java/org/shedlang/compiler/config/errors/UnexpectedTypeConfigError.java
@@ -1,25 +1,51 @@
package org.shedlang.compiler.config.errors;
-import org.shedlang.compiler.config.toml.TomlPath;
+import org.shedlang.compiler.config.json.Json5Path;
import org.shedlang.compiler.sources.Source;
+import org.zwobble.json5.values.*;
import java.text.MessageFormat;
public class UnexpectedTypeConfigError extends ConfigError {
public UnexpectedTypeConfigError(
- TomlPath path,
- String expectedType,
- Class<?> actualType,
+ Json5Path path,
+ Class<? extends Json5Value> expectedType,
+ Class<? extends Json5Value> actualType,
Source source
) {
super(
MessageFormat.format(
"Expected {0} to be {1}, but was {2}",
path,
- expectedType,
- actualType.getSimpleName()
+ // TODO: nicer names for JSON5 value types
+ describeJson5ValueType(expectedType),
+ describeJson5ValueType(actualType)
),
source
);
}
+
+ private static String describeJson5ValueType(Class<? extends Json5Value> expectedType) {
+ if (expectedType.equals(Json5Array.class)) {
+ return "array";
+ } else if (expectedType.equals(Json5Boolean.class)) {
+ return "boolean";
+ } else if (expectedType.equals(Json5Null.class)) {
+ return "null";
+ } else if (expectedType.equals(Json5NumberFinite.class)) {
+ return "finite number";
+ } else if (expectedType.equals(Json5NumberNan.class)) {
+ return "NaN";
+ } else if (expectedType.equals(Json5NumberPositiveInfinity.class)) {
+ return "Infinity";
+ } else if (expectedType.equals(Json5NumberNegativeInfinity.class)) {
+ return "-Infinity";
+ } else if (expectedType.equals(Json5Object.class)) {
+ return "object";
+ } else if (expectedType.equals(Json5String.class)) {
+ return "string";
+ } else {
+ return expectedType.getSimpleName();
+ }
+ }
}
diff --git a/src/main/java/org/shedlang/compiler/config/json/Json5ObjectWithSource.java b/src/main/java/org/shedlang/compiler/config/json/Json5ObjectWithSource.java
new file mode 100644
index 00000000..f073f267
--- /dev/null
+++ b/src/main/java/org/shedlang/compiler/config/json/Json5ObjectWithSource.java
@@ -0,0 +1,170 @@
+package org.shedlang.compiler.config.json;
+
+import org.shedlang.compiler.config.errors.ConfigError;
+import org.shedlang.compiler.config.errors.Json5SyntaxConfigError;
+import org.shedlang.compiler.config.errors.UnexpectedTypeConfigError;
+import org.shedlang.compiler.sources.Source;
+import org.zwobble.json5.parser.Json5ParseError;
+import org.zwobble.json5.parser.Json5Parser;
+import org.zwobble.json5.values.*;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.OptionalLong;
+
+public class Json5ObjectWithSource {
+ private final Json5Object object;
+ private final Json5Path path;
+ private final Source fullSource;
+
+ private Json5ObjectWithSource(
+ Json5Object object,
+ Json5Path path,
+ Source fullSource
+ ) {
+ this.object = object;
+ this.path = path;
+ this.fullSource = fullSource;
+ }
+
+ public static Json5ObjectWithSource parse(Source source) {
+ try {
+ var document = Json5Parser.parseText(source.contents());
+
+ if (document instanceof Json5Object object) {
+ return new Json5ObjectWithSource(
+ object,
+ Json5Path.TOP,
+ source
+ );
+ } else {
+ // TODO: write a test for this.
+ throw new UnexpectedTypeConfigError(
+ Json5Path.TOP,
+ Json5Object.class,
+ document.getClass(),
+ source
+ );
+ }
+ } catch (Json5ParseError error) {
+ throw Json5SyntaxConfigError.fromJson5ParseError(error, source);
+ }
+ }
+
+ public Optional<Json5ObjectWithSource> getObject(String memberName) {
+ return getValueOfType(Json5Object.class, memberName)
+ .map(object -> new Json5ObjectWithSource(
+ object,
+ path.plusMemberName(memberName),
+ fullSource
+ ));
+ }
+
+ private Optional<Json5Array> getArray(String memberName) {
+ return getValueOfType(Json5Array.class, memberName);
+ }
+
+ public Optional<List<Json5ObjectWithSource>> getArrayOfObjects(String memberName) {
+ return getArray(memberName).map(array -> {
+ var objects = new ArrayList<Json5ObjectWithSource>();
+ var index = 0;
+ var arrayPath = path.plusMemberName(memberName);
+ for (var element : array.elements()) {
+ var objectPath = arrayPath.plusArrayIndex(index);
+ var object = castJson5Value(
+ Json5Object.class,
+ objectPath,
+ element,
+ fullSource
+ );
+ objects.add(new Json5ObjectWithSource(
+ object,
+ objectPath,
+ fullSource
+ ));
+ index++;
+ }
+ return objects;
+ });
+ }
+
+ public Optional<Json5StringWithSource> getStringOrNone(String memberName) {
+ return getValueOfType(Json5String.class, memberName)
+ .map(string -> new Json5StringWithSource(
+ string,
+ path.plusMemberName(memberName),
+ fullSource
+ ));
+ }
+
+ public Json5StringWithSource getString(String memberName) {
+ return getStringOrNone(memberName)
+ .orElseThrow(() -> new ConfigError(
+ // TODO: escape member name
+ MessageFormat.format("{0} is missing {1}", path, memberName),
+ Json5Source.fromSourceRange(fullSource, object.sourceRange())
+ ));
+ }
+
+ public OptionalLong getLongOrNone(String memberName) {
+ var numberFinite = getValueOfType(Json5NumberFinite.class, memberName);
+ if (numberFinite.isEmpty()) {
+ return OptionalLong.empty();
+ }
+
+ try {
+ return OptionalLong.of(numberFinite.get().value().longValueExact());
+ } catch (ArithmeticException exception) {
+ // TODO: more specific error?
+ throw new ConfigError(
+ String.format(
+ "%s must be a 64-bit integer",
+ path.plusMemberName(memberName)
+ ),
+ Json5Source.fromSourceRange(fullSource, numberFinite.get().sourceRange())
+ );
+ }
+ }
+
+ public long getLong(String memberName) {
+ return getLongOrNone(memberName)
+ .orElseThrow(() -> new ConfigError(
+ // TODO: escape member name
+ MessageFormat.format("{0} is missing {1}", path, memberName),
+ Json5Source.fromSourceRange(fullSource, object.sourceRange())
+ ));
+ }
+
+ private <TValue extends Json5Value> Optional<TValue> getValueOfType(
+ Class<TValue> valueType,
+ String memberName
+ ) {
+ return object.getValue(memberName)
+ .map(value -> castJson5Value(
+ valueType,
+ path.plusMemberName(memberName),
+ value,
+ fullSource
+ ));
+ }
+
+ private static <TValue extends Json5Value> TValue castJson5Value(
+ Class<TValue> valueType,
+ Json5Path path,
+ Json5Value value,
+ Source fullSource
+ ) {
+ try {
+ return valueType.cast(value);
+ } catch (ClassCastException exception) {
+ throw new UnexpectedTypeConfigError(
+ path,
+ valueType,
+ value.getClass(),
+ Json5Source.fromSourceRange(fullSource, value.sourceRange())
+ );
+ }
+ }
+}
diff --git a/src/main/java/org/shedlang/compiler/config/json/Json5Path.java b/src/main/java/org/shedlang/compiler/config/json/Json5Path.java
new file mode 100644
index 00000000..f63e7ae4
--- /dev/null
+++ b/src/main/java/org/shedlang/compiler/config/json/Json5Path.java
@@ -0,0 +1,36 @@
+package org.shedlang.compiler.config.json;
+
+import org.pcollections.PVector;
+import org.shedlang.compiler.util.CheckReturnValue;
+import org.shedlang.compiler.util.P;
+
+import java.text.MessageFormat;
+
+public class Json5Path {
+ public static final Json5Path TOP = new Json5Path(P.vector());
+
+ private final PVector<String> components;
+
+ private Json5Path(PVector<String> components) {
+ this.components = components;
+ }
+
+ @CheckReturnValue
+ public Json5Path plusMemberName(String memberName) {
+ // TODO: handle string escaping
+ var component = components.isEmpty() ? memberName : "." + memberName;
+ return new Json5Path(this.components.plus(component));
+ }
+
+ @CheckReturnValue
+ public Json5Path plusArrayIndex(int index) {
+ var component = MessageFormat.format("[{0}]", index);
+ return new Json5Path(this.components.plus(component));
+ }
+
+ @Override
+ public String toString() {
+ // TODO: handle empty path
+ return String.join("", this.components);
+ }
+}
diff --git a/src/main/java/org/shedlang/compiler/config/json/Json5Source.java b/src/main/java/org/shedlang/compiler/config/json/Json5Source.java
new file mode 100644
index 00000000..6b761a6a
--- /dev/null
+++ b/src/main/java/org/shedlang/compiler/config/json/Json5Source.java
@@ -0,0 +1,18 @@
+package org.shedlang.compiler.config.json;
+
+import org.shedlang.compiler.sources.Source;
+import org.zwobble.json5.sources.Json5SourceRange;
+
+public class Json5Source {
+ private Json5Source() {}
+
+ public static Source fromSourceRange(Source fullSource, Json5SourceRange sourceRange) {
+ // TODO: decide whether we're using code points or code units, and make
+ // consistent (currently Shed sources use UTF-16 code units, JSON5
+ // sources use code points)
+ return fullSource.at(
+ sourceRange.startCodePointIndex(),
+ sourceRange.endCodePointIndex()
+ );
+ }
+}
diff --git a/src/main/java/org/shedlang/compiler/config/json/Json5StringWithSource.java b/src/main/java/org/shedlang/compiler/config/json/Json5StringWithSource.java
new file mode 100644
index 00000000..bc9cf4b9
--- /dev/null
+++ b/src/main/java/org/shedlang/compiler/config/json/Json5StringWithSource.java
@@ -0,0 +1,28 @@
+package org.shedlang.compiler.config.json;
+
+import org.shedlang.compiler.sources.Source;
+import org.zwobble.json5.values.Json5String;
+
+public class Json5StringWithSource {
+ private final Json5String string;
+ private final Json5Path path;
+ private final Source fullSource;
+
+ public Json5StringWithSource(
+ Json5String string,
+ Json5Path path,
+ Source fullSource
+ ) {
+ this.string = string;
+ this.path = path;
+ this.fullSource = fullSource;
+ }
+
+ public String value() {
+ return string.value();
+ }
+
+ public Source source() {
+ return Json5Source.fromSourceRange(fullSource, string.sourceRange());
+ }
+}
diff --git a/src/main/java/org/shedlang/compiler/config/toml/TomlPath.java b/src/main/java/org/shedlang/compiler/config/toml/TomlPath.java
deleted file mode 100644
index dc5910a9..00000000
--- a/src/main/java/org/shedlang/compiler/config/toml/TomlPath.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.shedlang.compiler.config.toml;
-
-import org.pcollections.PVector;
-import org.shedlang.compiler.util.CheckReturnValue;
-import org.shedlang.compiler.util.P;
-
-import java.text.MessageFormat;
-
-public class TomlPath {
- public static final TomlPath TOP = new TomlPath(P.vector());
-
- private final PVector<String> components;
-
- private TomlPath(PVector<String> components) {
- this.components = components;
- }
-
- @CheckReturnValue
- public TomlPath plusKey(String key) {
- // TODO: handle string escaping
- var component = components.isEmpty() ? key : "." + key;
- return new TomlPath(this.components.plus(component));
- }
-
- @CheckReturnValue
- public TomlPath plusIndex(int index) {
- var component = MessageFormat.format("[{0}]", index);
- return new TomlPath(this.components.plus(component));
- }
-
- @Override
- public String toString() {
- return String.join("", this.components);
- }
-}
diff --git a/src/main/java/org/shedlang/compiler/config/toml/TomlSource.java b/src/main/java/org/shedlang/compiler/config/toml/TomlSource.java
deleted file mode 100644
index e72fbb45..00000000
--- a/src/main/java/org/shedlang/compiler/config/toml/TomlSource.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.shedlang.compiler.config.toml;
-
-import org.shedlang.compiler.sources.Source;
-import org.tomlj.TomlPosition;
-
-import java.util.Arrays;
-
-public class TomlSource {
- private TomlSource() {}
-
- public static Source sourceAtTomlPosition(Source fullSource, TomlPosition position) {
- var lines = fullSource.contents().split("\n");
- var lineCharacterIndex = Arrays.stream(lines)
- .limit(position.line() - 1)
- .mapToInt(line -> line.length() + 1)
- .sum();
- var characterIndex = lineCharacterIndex + position.column() - 1;
- return fullSource.at(characterIndex, characterIndex);
- }
-}
diff --git a/src/main/java/org/shedlang/compiler/config/toml/TomlString.java b/src/main/java/org/shedlang/compiler/config/toml/TomlString.java
deleted file mode 100644
index b8aac9e5..00000000
--- a/src/main/java/org/shedlang/compiler/config/toml/TomlString.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package org.shedlang.compiler.config.toml;
-
-import org.shedlang.compiler.sources.Source;
-
-public record TomlString(String value, Source source) {
-}
diff --git a/src/main/java/org/shedlang/compiler/config/toml/TomlTableWithSource.java b/src/main/java/org/shedlang/compiler/config/toml/TomlTableWithSource.java
deleted file mode 100644
index f7ef240f..00000000
--- a/src/main/java/org/shedlang/compiler/config/toml/TomlTableWithSource.java
+++ /dev/null
@@ -1,214 +0,0 @@
-package org.shedlang.compiler.config.toml;
-
-import org.shedlang.compiler.config.errors.ConfigError;
-import org.shedlang.compiler.config.errors.TomlSyntaxConfigError;
-import org.shedlang.compiler.config.errors.UnexpectedTypeConfigError;
-import org.shedlang.compiler.sources.Source;
-import org.tomlj.Toml;
-import org.tomlj.TomlArray;
-import org.tomlj.TomlPosition;
-import org.tomlj.TomlTable;
-
-import java.text.MessageFormat;
-import java.util.List;
-import java.util.Optional;
-import java.util.OptionalLong;
-import java.util.stream.IntStream;
-
-public class TomlTableWithSource {
- private final TomlTable table;
- private final TomlPath path;
- private final TomlPosition position;
- private final Source fullSource;
-
- private TomlTableWithSource(TomlTable table, TomlPath path, TomlPosition position, Source fullSource) {
- this.table = table;
- this.path = path;
- this.position = position;
- this.fullSource = fullSource;
- }
-
- public static TomlTableWithSource parse(Source source) {
- var result = Toml.parse(source.contents());
-
- if (result.hasErrors()) {
- var error = result.errors().get(0);
- throw TomlSyntaxConfigError.fromTomljError(error, source);
- }
-
- return new TomlTableWithSource(
- result,
- TomlPath.TOP,
- TomlPosition.positionAt(1, 1),
- source
- );
- }
-
- public Optional<TomlTableWithSource> getTable(String key) {
- var value = table.get(key);
- var position = table.inputPositionOf(key);
-
- return tomlValueToTable(path.plusKey(key), value, position);
- }
-
- private Optional<TomlTableWithSource> tomlValueToTable(
- TomlPath valuePath,
- Object value,
- TomlPosition valuePosition
- ) {
- if (value == null || valuePosition == null) {
- return Optional.empty();
- }
-
- if (value instanceof TomlTable valueTable) {
- return Optional.of(new TomlTableWithSource(
- valueTable,
- valuePath,
- valuePosition,
- fullSource
- ));
- }
-
- throw new UnexpectedTypeConfigError(
- valuePath,
- "a table",
- value.getClass(),
- TomlSource.sourceAtTomlPosition(fullSource, valuePosition)
- );
- }
-
- private Optional<TomlArray> getArray(String key) {
- var value = table.get(key);
- var position = table.inputPositionOf(key);
-
- if (value == null || position == null) {
- return Optional.empty();
- }
-
- if (value instanceof TomlArray valueArray) {
- return Optional.of(valueArray);
- }
-
- throw new UnexpectedTypeConfigError(
- path.plusKey(key),
- "an array",
- value.getClass(),
- TomlSource.sourceAtTomlPosition(fullSource, position)
- );
- }
-
- public Optional<List<TomlTableWithSource>> getArrayOfTables(String key) {
- return getArray(key).map(array ->
- IntStream.range(0, array.size())
- .mapToObj(index -> {
- var element = array.get(index);
- var elementPosition = array.inputPositionOf(index);
- return tomlValueToTable(
- path.plusKey(key).plusIndex(index),
- element,
- elementPosition
- );
- })
- .flatMap(x -> x.stream())
- .toList()
- );
- }
-
- public Optional<TomlString> getStringOrNone(String key) {
- var value = table.get(key);
- var valuePosition = table.inputPositionOf(key);
-
- if (value == null || valuePosition == null) {
- return Optional.empty();
- }
-
- if (value instanceof String valueString) {
- var tomlString = new TomlString(
- valueString,
- TomlSource.sourceAtTomlPosition(fullSource, valuePosition)
- );
- return Optional.of(tomlString);
- }
-
- throw new UnexpectedTypeConfigError(
- path.plusKey(key),
- "a string",
- value.getClass(),
- TomlSource.sourceAtTomlPosition(fullSource, valuePosition)
- );
- }
-
- public TomlString getString(String key) {
- var value = table.get(key);
- var valuePosition = table.inputPositionOf(key);
-
- if (value == null || valuePosition == null) {
- throw new ConfigError(
- MessageFormat.format("{0} is missing {1}", path, key),
- TomlSource.sourceAtTomlPosition(fullSource, position)
- );
- }
-
- if (value instanceof String valueString) {
- return new TomlString(
- valueString,
- TomlSource.sourceAtTomlPosition(fullSource, valuePosition)
- );
- }
-
- throw new UnexpectedTypeConfigError(
- path.plusKey(key),
- "a string",
- value.getClass(),
- TomlSource.sourceAtTomlPosition(fullSource, valuePosition)
- );
- }
-
- public OptionalLong getLongOrNone(String key) {
- var value = table.get(key);
- var valuePosition = table.inputPositionOf(key);
-
- if (value == null) {
- return OptionalLong.empty();
- }
-
- if (value instanceof Long valueLong) {
- return OptionalLong.of(valueLong);
- }
-
- throw new UnexpectedTypeConfigError(
- path.plusKey(key),
- "a long",
- value.getClass(),
- TomlSource.sourceAtTomlPosition(fullSource, valuePosition)
- );
- }
-
- public long getLong(String key) {
- var value = table.get(key);
- var valuePosition = table.inputPositionOf(key);
-
- if (value == null) {
- throw new ConfigError(
- MessageFormat.format("{0} is missing {1}", path, key),
- TomlSource.sourceAtTomlPosition(fullSource, position)
- );
- }
-
- if (value instanceof Long valueLong) {
- return valueLong;
- }
-
- throw new UnexpectedTypeConfigError(
- path.plusKey(key),
- "a long",
- value.getClass(),
- TomlSource.sourceAtTomlPosition(fullSource, valuePosition)
- );
- }
-
- public Optional<Source> getSource(String key) {
- return Optional.ofNullable(table.inputPositionOf(key))
- .map(position -> TomlSource.sourceAtTomlPosition(fullSource, position));
- }
-}