Skip to content

Commit

Permalink
Use default module instead of $ion_encoding (#1007)
Browse files Browse the repository at this point in the history
  • Loading branch information
popematt authored Dec 5, 2024
1 parent 0a17f82 commit 37a33d2
Show file tree
Hide file tree
Showing 16 changed files with 284 additions and 180 deletions.
2 changes: 1 addition & 1 deletion ion-tests
14 changes: 2 additions & 12 deletions src/main/java/com/amazon/ion/SystemSymbols.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,17 +121,7 @@ private SystemSymbols() { }
// Ion 1.1 Symbols

/**
* The annotation that denotes an Ion encoding directive in Ion 1.1+.
* The name of the default module in Ion 1.1
*/
public static final String ION_ENCODING = "$ion_encoding";

/**
* The name of the symbol table s-expression within an Ion encoding directive.
*/
public static final String SYMBOL_TABLE = "symbol_table";

/**
* The name of the macro table s-expression within an Ion encoding directive.
*/
public static final String MACRO_TABLE = "macro_table";
public static final String DEFAULT_MODULE = "_";
}
72 changes: 56 additions & 16 deletions src/main/java/com/amazon/ion/impl/EncodingDirectiveReader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package com.amazon.ion.impl

import com.amazon.ion.*
import com.amazon.ion.SystemSymbols.*
import com.amazon.ion.impl.macro.*
import com.amazon.ion.impl.macro.MacroRef.Companion.byId
import com.amazon.ion.impl.macro.MacroRef.Companion.byName
Expand All @@ -28,22 +29,39 @@ internal class EncodingDirectiveReader(private val reader: IonReader, private va
var isMacroTableAlreadyClassified = false

private enum class State {
IN_ION_ENCODING_SEXP,
IN_DIRECTIVE_SEXP,
IN_MODULE_DIRECTIVE_SEXP_AWAITING_MODULE_NAME,
IN_MODULE_DIRECTIVE_SEXP,
IN_SYMBOL_TABLE_SEXP,
IN_SYMBOL_TABLE_LIST,
IN_MACRO_TABLE_SEXP,
COMPILING_MACRO,
READING_VALUE
}

private fun classifySexpWithinEncodingDirective() {
private fun classifyDirective() {
errorIf(reader.type != IonType.SYMBOL) { "Ion encoding directives must start with a directive keyword; found ${reader.type}" }
val name: String = reader.stringValue()
// TODO: Add support for `import` and `encoding` directives
if (SystemSymbols_1_1.MODULE.text == name) {
state = State.IN_MODULE_DIRECTIVE_SEXP_AWAITING_MODULE_NAME
} else if (SystemSymbols_1_1.IMPORT.text == name) {
throw IonException("'import' directive not yet supported")
} else if (SystemSymbols_1_1.ENCODING.text == name) {
throw IonException("'encoding' directive not yet supported")
} else {
throw IonException(String.format("'%s' is not a valid directive keyword", name))
}
}

private fun classifySexpWithinModuleDirective() {
val name: String = reader.stringValue()
state = if (SystemSymbols_1_1.SYMBOL_TABLE.text == name) {
State.IN_SYMBOL_TABLE_SEXP
} else if (SystemSymbols_1_1.MACRO_TABLE.text == name) {
State.IN_MACRO_TABLE_SEXP
} else {
throw IonException(String.format("\$ion_encoding expressions '%s' not supported.", name))
throw IonException("'$name' clause not supported in module definition")
}
}

Expand All @@ -64,7 +82,7 @@ internal class EncodingDirectiveReader(private val reader: IonReader, private va
}
isSymbolTableAlreadyClassified = true
if (IonType.isText(type)) {
if (SystemSymbols.ION_ENCODING == reader.stringValue() && !isSymbolTableAppend) {
if (DEFAULT_MODULE == reader.stringValue() && !isSymbolTableAppend) {
isSymbolTableAppend = true
if (reader.next() == null) {
return true
Expand All @@ -73,10 +91,10 @@ internal class EncodingDirectiveReader(private val reader: IonReader, private va
throw IonException("symbol_table s-expression must begin with a list.")
}
} else {
throw IonException("symbol_table s-expression must begin with either \$ion_encoding or a list.")
throw IonException("symbol_table s-expression must begin with either '_' or a list.")
}
} else if (type != IonType.LIST) {
throw IonException("symbol_table s-expression must begin with either \$ion_encoding or a list.")
throw IonException("symbol_table s-expression must begin with either '_' or a list.")
}
reader.stepIn()
state = State.IN_SYMBOL_TABLE_LIST
Expand All @@ -98,7 +116,7 @@ internal class EncodingDirectiveReader(private val reader: IonReader, private va
}
isMacroTableAlreadyClassified = true
if (IonType.isText(type)) {
if (SystemSymbols.ION_ENCODING == reader.stringValue() && !isMacroTableAppend) {
if (SystemSymbols.DEFAULT_MODULE == reader.stringValue() && !isMacroTableAppend) {
isMacroTableAppend = true
if (reader.next() == null) {
return true
Expand All @@ -107,16 +125,27 @@ internal class EncodingDirectiveReader(private val reader: IonReader, private va
throw IonException("macro_table s-expression must begin with s-expression(s).")
}
} else {
throw IonException("macro_table s-expression must begin with either \$ion_encoding or s-expression(s).")
throw IonException("macro_table s-expression must begin with either '_' or s-expression(s).")
}
} else if (type == IonType.SEXP) {
localMacroMaxOffset = -1
} else {
throw IonException("macro_table s-expression must begin with either \$ion_encoding or s-expression(s).")
throw IonException("macro_table s-expression must begin with either '_' or s-expression(s).")
}
return false
}

/**
* Utility function to make error cases more concise.
* @param condition the condition under which an IonException should be thrown
* @param lazyErrorMessage the message to use in the exception
*/
private inline fun errorIf(condition: Boolean, lazyErrorMessage: () -> String) {
if (condition) {
throw IonException(lazyErrorMessage())
}
}

/**
* Reads an encoding directive. After this method returns, the caller should access this class's properties to
* retrieve the symbols and macros declared within the directive.
Expand All @@ -126,30 +155,41 @@ internal class EncodingDirectiveReader(private val reader: IonReader, private va
val macroCompiler = MacroCompiler({ key -> resolveMacro(encodingContext, key) }, readerAdapter)

reader.stepIn()
state = State.IN_ION_ENCODING_SEXP
state = State.IN_DIRECTIVE_SEXP
while (true) {
when (state) {

State.IN_ION_ENCODING_SEXP -> {
State.IN_DIRECTIVE_SEXP -> {
errorIf(reader.next() == null) { "invalid Ion directive; missing directive keyword" }
classifyDirective()
}
State.IN_MODULE_DIRECTIVE_SEXP_AWAITING_MODULE_NAME -> {
errorIf(reader.next() == null) { "invalid module directive; missing module name" }
errorIf(reader.type != IonType.SYMBOL) { "invalid module directive; module name must be a symbol" }
// TODO: Support other module names
errorIf(DEFAULT_MODULE != reader.stringValue()) { "IonJava currently supports only the default module" }
state = State.IN_MODULE_DIRECTIVE_SEXP
}
State.IN_MODULE_DIRECTIVE_SEXP -> {
if (reader.next() == null) {
reader.stepOut()
state = State.READING_VALUE
return
}
if (reader.type != IonType.SEXP) {
throw IonException("Ion encoding directives must contain only s-expressions.")
throw IonException("module definition must contain only s-expressions.")
}
reader.stepIn()
if (reader.next() == null || !IonType.isText(reader.type)) {
throw IonException("S-expressions within encoding directives must begin with a text token.")
throw IonException("S-expressions within module definitions must begin with a text token.")
}
classifySexpWithinEncodingDirective()
classifySexpWithinModuleDirective()
}

State.IN_SYMBOL_TABLE_SEXP -> {
if (reader.next() == null || classifySymbolTable()) {
reader.stepOut()
state = State.IN_ION_ENCODING_SEXP
state = State.IN_MODULE_DIRECTIVE_SEXP
continue
}
}
Expand All @@ -169,7 +209,7 @@ internal class EncodingDirectiveReader(private val reader: IonReader, private va
State.IN_MACRO_TABLE_SEXP -> {
if (reader.next() == null || classifyMacroTable()) {
reader.stepOut()
state = State.IN_ION_ENCODING_SEXP
state = State.IN_MODULE_DIRECTIVE_SEXP
continue
}
state = State.COMPILING_MACRO
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/amazon/ion/impl/IonCursorBinary.java
Original file line number Diff line number Diff line change
Expand Up @@ -2115,7 +2115,7 @@ private void setCheckpointBeforeUnannotatedTypeId() {
*/
private void setMarker(long endIndex, Marker markerToSet) {
if (parent != null && endIndex > parent.endIndex && parent.endIndex > DELIMITED_MARKER) {
throw new IonException("Value exceeds the length of its parent container.");
throw new IonException(String.format("Value [%d:%d] exceeds the length of its parent container [%d:%d].", peekIndex, endIndex, parent.startIndex, parent.endIndex));
}
markerToSet.startIndex = peekIndex;
markerToSet.endIndex = endIndex;
Expand Down
Loading

0 comments on commit 37a33d2

Please sign in to comment.