diff options
| author | Michael Williamson <mike@zwobble.org> | 2026-03-22 10:59:03 +0000 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2026-03-22 10:59:03 +0000 |
| commit | d8e89e31a95b7f435b07a5756368e5b39e063c6c (patch) | |
| tree | 34a78709d64c1d146363acf6035606c98f8abb5a | |
| parent | 0d83766cdb08286adf64c54d82ef2a78755f4c1e (diff) | |
Add serialization of sources to flatbuffers
3 files changed, 173 insertions, 1 deletions
diff --git a/flatbuffers/sources.fbs b/flatbuffers/sources.fbs index 9253bf6f..3b797d71 100644 --- a/flatbuffers/sources.fbs +++ b/flatbuffers/sources.fbs @@ -1,11 +1,15 @@ namespace Sources; -union Source { +union SourceValue { NullSource, RuntimeSource, FileFragmentSource, } +table Source { + value:SourceValue (required); +} + table NullSource { } diff --git a/src/main/java/org/shedlang/compiler/sources/serialization/SourceToFlatbuffers.java b/src/main/java/org/shedlang/compiler/sources/serialization/SourceToFlatbuffers.java new file mode 100644 index 00000000..7c1bb074 --- /dev/null +++ b/src/main/java/org/shedlang/compiler/sources/serialization/SourceToFlatbuffers.java @@ -0,0 +1,141 @@ +package org.shedlang.compiler.sources.serialization; + +import com.google.flatbuffers.FlatBufferBuilder; +import org.shedlang.compiler.sources.*; +import org.shedlang.compiler.sources.flatbuffers.*; +import org.zwobble.sourcetext.SourcePosition; +import org.zwobble.sourcetext.SourceRange; +import org.zwobble.sourcetext.SourceText; + +import java.util.Optional; + +public class SourceToFlatbuffers { + private SourceToFlatbuffers() { + } + + public static int serializeSource( + FlatBufferBuilder builder, + Source source, + Source parentSource + ) { + return serializeSource(builder, source, Optional.of(parentSource)); + } + + public static int serializeSource( + FlatBufferBuilder builder, + Source source, + Optional<Source> parentSource + ) { + switch (source) { + case NullSource nullSource -> { + NullSourceFbs.startNullSourceFbs(builder); + var nullSourceFbs = NullSourceFbs.endNullSourceFbs(builder); + + SourceFbs.startSourceFbs(builder); + SourceFbs.addValueType(builder, SourceValueFbs.NullSourceFbs); + SourceFbs.addValue(builder, nullSourceFbs); + return SourceFbs.endSourceFbs(builder); + } + case RuntimeSource runtimeSource -> { + RuntimeSourceFbs.startRuntimeSourceFbs(builder); + var runtimeSourceFbs = RuntimeSourceFbs.endRuntimeSourceFbs(builder); + + SourceFbs.startSourceFbs(builder); + SourceFbs.addValueType(builder, SourceValueFbs.RuntimeSourceFbs); + SourceFbs.addValue(builder, runtimeSourceFbs); + return SourceFbs.endSourceFbs(builder); + } + case FileFragmentSource fileFragmentSource -> { + var fileFragmentSourceFbs = serializeFileFragmentSource(builder, fileFragmentSource, parentSource); + + SourceFbs.startSourceFbs(builder); + SourceFbs.addValueType(builder, SourceValueFbs.FileFragmentSourceFbs); + SourceFbs.addValue( + builder, + fileFragmentSourceFbs + ); + return SourceFbs.endSourceFbs(builder); + } + default -> { + throw new UnsupportedOperationException("Unhandled source: " + source); + } + } + } + + public static Source deserializeSource( + SourceFbs sourceFbs, + Source parentSource + ) { + return deserializeSource(sourceFbs, Optional.of(parentSource)); + } + + public static Source deserializeSource( + SourceFbs sourceFbs, + Optional<Source> parentSource + ) { + return switch (sourceFbs.valueType()) { + case SourceValueFbs.NullSourceFbs -> + Sources.NULL; + + case SourceValueFbs.RuntimeSourceFbs -> + Sources.RUNTIME; + + case SourceValueFbs.FileFragmentSourceFbs -> + deserializeFileFragmentSource( + (FileFragmentSourceFbs) sourceFbs.value(new FileFragmentSourceFbs()), + parentSource + ); + + default -> { + throw new UnsupportedOperationException("TODO"); + } + }; + } + + private static int serializeFileFragmentSource( + FlatBufferBuilder builder, + FileFragmentSource source, + Optional<Source> parentSource + ) { + var sourceTextName = source.sourceRange().sourceText().name(); + if (!( + parentSource.isPresent() && + parentSource.get() instanceof FileFragmentSource( + SourceRange parentSourceRange) && + parentSourceRange.sourceText().name().equals(sourceTextName) + )) { + var sourceTextNameFbs = builder.createString(sourceTextName); + var sourceTextContentsFbs = builder.createString(source.sourceRange().sourceText().charSequence()); + var sourceTextFbs = SourceTextFbs.createSourceTextFbs(builder, sourceTextNameFbs, sourceTextContentsFbs); + FileFragmentSourceFbs.startFileFragmentSourceFbs(builder); + FileFragmentSourceFbs.addSourceText(builder, sourceTextFbs); + } else { + FileFragmentSourceFbs.startFileFragmentSourceFbs(builder); + } + + FileFragmentSourceFbs.addStartCharacterIndex(builder, source.sourceRange().start().characterIndex()); + FileFragmentSourceFbs.addEndCharacterIndex(builder, source.sourceRange().end().characterIndex()); + + return FileFragmentSourceFbs.endFileFragmentSourceFbs(builder); + } + + private static FileFragmentSource deserializeFileFragmentSource( + FileFragmentSourceFbs sourceFbs, + Optional<Source> parentSource + ) { + SourceText sourceText; + if (sourceFbs.sourceText() != null) { + sourceText = SourceText.fromString( + sourceFbs.sourceText().name(), + sourceFbs.sourceText().contents() + ); + } else { + // TODO: handle missing parent + sourceText = ((FileFragmentSource) parentSource.orElseThrow()).sourceRange().sourceText(); + } + + var start = new SourcePosition(sourceText, (int) sourceFbs.startCharacterIndex()); + var end = new SourcePosition(sourceText, (int) sourceFbs.endCharacterIndex()); + return new FileFragmentSource(new SourceRange(sourceText, start, end)); + } +} diff --git a/src/test/java/org/shedlang/compiler/sources/serialization/SourceToFlatbuffersTests.java b/src/test/java/org/shedlang/compiler/sources/serialization/SourceToFlatbuffersTests.java new file mode 100644 index 00000000..31d20149 --- /dev/null +++ b/src/test/java/org/shedlang/compiler/sources/serialization/SourceToFlatbuffersTests.java @@ -0,0 +1,27 @@ +package org.shedlang.compiler.sources.serialization; + +import com.google.flatbuffers.FlatBufferBuilder; +import com.google.protobuf.InvalidProtocolBufferException; +import org.shedlang.compiler.sources.SourceSerializationTests; +import org.shedlang.compiler.sources.flatbuffers.SourceFbs; +import org.shedlang.compiler.sources.protobuf.SourcesProtobuf; + +import java.nio.ByteBuffer; + +public class SourceToFlatbuffersTests extends SourceSerializationTests { + public SourceToFlatbuffersTests() { + super( + (source, parentSource) -> { + var builder = new FlatBufferBuilder(); + var sourceFbs = SourceToFlatbuffers.serializeSource(builder, source, parentSource); + builder.finish(sourceFbs); + return builder.sizedByteArray(); + }, + + (serializedSource, parentSource) -> { + var sourceFbs = SourceFbs.getRootAsSourceFbs(ByteBuffer.wrap(serializedSource)); + return SourceToFlatbuffers.deserializeSource(sourceFbs, parentSource); + } + ); + } +} |
