-
Notifications
You must be signed in to change notification settings - Fork 110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Includes various small fixes for tagless parameters, expression groups, and system macro invocations. #975
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1449,7 +1449,7 @@ private void readGroupExpression(Macro.Parameter parameter, List<Expression.EExp | |
int startIndex = expressions.size(); | ||
expressions.add(Expression.Placeholder.INSTANCE); | ||
boolean isSingleton = true; | ||
while (nextGroupedValue() != Event.NEEDS_INSTRUCTION) { | ||
while (nextGroupedValue() != Event.NEEDS_INSTRUCTION || isMacroInvocation()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NEEDS_INSTRUCTION is used either when the end of the group has been reached, or when a macro invocation header has been encountered. When it's the latter, we need to read the invocation as an expression to continue progressing through the group. |
||
readValueAsExpression(false, expressions); | ||
isSingleton = false; | ||
} | ||
|
@@ -1560,7 +1560,7 @@ protected PresenceBitmap loadPresenceBitmapIfNecessary(List<Macro.Parameter> sig | |
|
||
@Override | ||
protected boolean isMacroInvocation() { | ||
return valueTid.isMacroInvocation; | ||
return valueTid != null && valueTid.isMacroInvocation; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is now called when the cursor may be at the end of the group, at which point |
||
} | ||
|
||
@Override | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1211,6 +1211,8 @@ protected Macro loadMacro() { | |
if (IonReaderTextSystemX.this.nextRaw() == null) { | ||
throw new IonException("Macro invocation missing address."); | ||
} | ||
List<SymbolToken> annotations = getAnnotations(); | ||
boolean isSystemMacro = !annotations.isEmpty() && SystemSymbols_1_1.ION.getText().equals(annotations.get(0).getText()); | ||
Comment on lines
+1214
to
+1215
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The added |
||
MacroRef address; | ||
if (_value_type == IonType.SYMBOL) { | ||
String name = stringValue(); | ||
|
@@ -1228,7 +1230,7 @@ protected Macro loadMacro() { | |
throw new IonException("E-expressions must begin with an address."); | ||
} | ||
Macro macro; | ||
if (encodingContext == null) { | ||
if (encodingContext == null || isSystemMacro) { | ||
macro = SystemMacro.get(address); | ||
if (macro == null) { | ||
throw new IonException(String.format("Encountered an unknown macro address: %s.", address)); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -859,12 +859,12 @@ class IonRawBinaryWriter_1_1 internal constructor( | |
thisContainerTotalLength++ | ||
} else if (isTagless) { | ||
// End tagless group -- write the number of expressions, end with FlexUInt 0 | ||
thisContainerTotalLength += writeCurrentContainerLength(lengthPrefixPreallocation) | ||
thisContainerTotalLength += writeCurrentContainerLength(maxOf(1, lengthPrefixPreallocation)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is now symmetrical with This change is verified by the added |
||
buffer.writeByte(FlexInt.ZERO) | ||
thisContainerTotalLength++ | ||
} else if (currentContainer.isLengthPrefixed) { | ||
// Length-prefixed, tagged -- write the number of bytes | ||
thisContainerTotalLength += writeCurrentContainerLength(lengthPrefixPreallocation) | ||
thisContainerTotalLength += writeCurrentContainerLength(maxOf(1, lengthPrefixPreallocation)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exercised by the added |
||
} else { | ||
// Delimited, tagged -- start with `01` end with `F0` | ||
buffer.writeByte(OpCodes.DELIMITED_END_MARKER) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -85,10 +85,23 @@ internal class MacroCompiler( | |
|
||
// Read the next parameter name | ||
val annotations = getTypeAnnotationSymbols() | ||
confirm(annotations.isEmptyOr(Macro.ParameterEncoding.Tagged.ionTextName)) { "unsupported parameter encoding $annotations" } | ||
val parameterEncoding = when (annotations.size) { | ||
0 -> Macro.ParameterEncoding.Tagged | ||
1 -> { | ||
val encodingText = annotations[0].text | ||
val encoding = Macro.ParameterEncoding.entries.singleOrNull { it.ionTextName == encodingText } | ||
if (encoding == null) { | ||
// TODO: Check for macro-shaped parameter encodings, and only if it's still null, we throw. | ||
throw IonException("unsupported parameter encoding $annotations") | ||
} | ||
encoding | ||
} | ||
2 -> TODO("Qualified references for macro-shaped parameters") | ||
else -> throw IonException("unsupported parameter encoding $annotations") | ||
} | ||
Comment on lines
+88
to
+101
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This patch is contributed by @popematt, and is exercised by the added |
||
confirm(isIdentifierSymbol(symbolText)) { "invalid parameter name: '$symbolText'" } | ||
confirm(signature.none { it.variableName == symbolText }) { "redeclaration of parameter '$symbolText'" } | ||
pendingParameter = Macro.Parameter(symbolText, Macro.ParameterEncoding.Tagged, Macro.ParameterCardinality.ExactlyOne) | ||
pendingParameter = Macro.Parameter(symbolText, parameterEncoding, Macro.ParameterCardinality.ExactlyOne) | ||
} | ||
// If we have a pending parameter than hasn't been added to the signature, add it here. | ||
if (pendingParameter != null) signature.add(pendingParameter!!) | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -4,8 +4,11 @@ | |||||||||||||||||||
|
||||||||||||||||||||
import com.amazon.ion.FakeSymbolToken; | ||||||||||||||||||||
import com.amazon.ion.IntegerSize; | ||||||||||||||||||||
import com.amazon.ion.IonDatagram; | ||||||||||||||||||||
import com.amazon.ion.IonEncodingVersion; | ||||||||||||||||||||
import com.amazon.ion.IonLoader; | ||||||||||||||||||||
import com.amazon.ion.IonReader; | ||||||||||||||||||||
import com.amazon.ion.IonText; | ||||||||||||||||||||
import com.amazon.ion.IonType; | ||||||||||||||||||||
import com.amazon.ion.SystemSymbols; | ||||||||||||||||||||
import com.amazon.ion.impl.bin.IonRawBinaryWriter_1_1; | ||||||||||||||||||||
|
@@ -14,8 +17,10 @@ | |||||||||||||||||||
import com.amazon.ion.impl.macro.Macro; | ||||||||||||||||||||
import com.amazon.ion.impl.macro.MacroRef; | ||||||||||||||||||||
import com.amazon.ion.impl.macro.ParameterFactory; | ||||||||||||||||||||
import com.amazon.ion.impl.macro.SystemMacro; | ||||||||||||||||||||
import com.amazon.ion.impl.macro.TemplateMacro; | ||||||||||||||||||||
import com.amazon.ion.system.IonReaderBuilder; | ||||||||||||||||||||
import com.amazon.ion.system.IonSystemBuilder; | ||||||||||||||||||||
import org.junit.jupiter.params.ParameterizedTest; | ||||||||||||||||||||
import org.junit.jupiter.params.provider.Arguments; | ||||||||||||||||||||
import org.junit.jupiter.params.provider.MethodSource; | ||||||||||||||||||||
|
@@ -136,6 +141,30 @@ private static void writeMacroSignature(IonRawWriter_1_1 writer, Map<String, Int | |||||||||||||||||||
writer.stepOut(); | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
private static void writeMacroSignatureFromDatagram(IonRawWriter_1_1 writer, Map<String, Integer> symbols, IonDatagram... signature) { | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This allows us to specify tagless encodings. The other variant just takes a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fascinating use of IonDatagram for "unmodeled sequence of values". I did a double-take. |
||||||||||||||||||||
writer.stepInSExp(false); | ||||||||||||||||||||
for (IonDatagram parameter : signature) { | ||||||||||||||||||||
if (parameter.size() > 2) { | ||||||||||||||||||||
throw new IllegalStateException("Parameters can only have two components: a name and a cardinality."); | ||||||||||||||||||||
} | ||||||||||||||||||||
Comment on lines
+147
to
+149
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||
IonText name = (IonText) parameter.get(0); | ||||||||||||||||||||
String[] encoding = name.getTypeAnnotations(); | ||||||||||||||||||||
if (encoding.length == 1) { | ||||||||||||||||||||
// The encoding, e.g. uint8 | ||||||||||||||||||||
writer.writeAnnotations(encoding); | ||||||||||||||||||||
} else if (encoding.length > 1) { | ||||||||||||||||||||
throw new IllegalStateException("Only one encoding annotation is allowed."); | ||||||||||||||||||||
} | ||||||||||||||||||||
// The name | ||||||||||||||||||||
writeSymbol(writer, symbols, name.stringValue()); | ||||||||||||||||||||
if (parameter.size() == 2) { | ||||||||||||||||||||
// The cardinality, e.g. * | ||||||||||||||||||||
writeSymbol(writer, symbols, ((IonText) parameter.get(1)).stringValue()); | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
writer.stepOut(); | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
private static void writeVariableExpansion(IonRawWriter_1_1 writer, Map<String, Integer> symbols, String variableName) { | ||||||||||||||||||||
writer.stepInSExp(false); | ||||||||||||||||||||
writer.writeSymbol("%"); | ||||||||||||||||||||
|
@@ -1031,8 +1060,88 @@ public void blobsAndClobs(InputType inputType, StreamType streamType) throws Exc | |||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
@ParameterizedTest(name = "{0},{1}") | ||||||||||||||||||||
@MethodSource("allCombinations") | ||||||||||||||||||||
public void macroInvocationInTaggedExpressionGroup(InputType inputType, StreamType streamType) throws Exception { | ||||||||||||||||||||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||||||||||||||||||
IonRawWriter_1_1 writer = streamType.newWriter(out); | ||||||||||||||||||||
|
||||||||||||||||||||
writer.writeIVM(); | ||||||||||||||||||||
Map<String, Integer> symbols = initializeSymbolTable(writer, "foo", "value"); | ||||||||||||||||||||
startEncodingDirective(writer); | ||||||||||||||||||||
startMacroTable(writer); | ||||||||||||||||||||
startMacro(writer, symbols, "foo"); | ||||||||||||||||||||
writeMacroSignature(writer, symbols, "value", "*"); | ||||||||||||||||||||
writeVariableExpansion(writer, symbols, "value"); | ||||||||||||||||||||
endMacro(writer); | ||||||||||||||||||||
endMacroTable(writer); | ||||||||||||||||||||
endEncodingDirective(writer); | ||||||||||||||||||||
|
||||||||||||||||||||
Macro expectedMacro = new TemplateMacro( | ||||||||||||||||||||
Collections.singletonList(new Macro.Parameter("value", Macro.ParameterEncoding.Tagged, Macro.ParameterCardinality.ZeroOrMore)), | ||||||||||||||||||||
Collections.singletonList(new Expression.VariableRef(0)) | ||||||||||||||||||||
); | ||||||||||||||||||||
|
||||||||||||||||||||
writer.stepInEExp(0, false, expectedMacro); { | ||||||||||||||||||||
writer.stepInExpressionGroup(true); { | ||||||||||||||||||||
writer.stepInEExp(SystemMacro.Values); { | ||||||||||||||||||||
writer.writeString("bar"); | ||||||||||||||||||||
} writer.stepOut(); | ||||||||||||||||||||
} writer.stepOut(); | ||||||||||||||||||||
} writer.stepOut(); | ||||||||||||||||||||
Comment on lines
+1085
to
+1091
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see you're also using the stepIn/Out idiom from old IonJava tests :D |
||||||||||||||||||||
|
||||||||||||||||||||
byte[] data = getBytes(writer, out); | ||||||||||||||||||||
|
||||||||||||||||||||
try (IonReader reader = inputType.newReader(data)) { | ||||||||||||||||||||
assertEquals(IonType.STRING, reader.next()); | ||||||||||||||||||||
assertEquals("bar", reader.stringValue()); | ||||||||||||||||||||
assertNull(reader.next()); | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
private static final IonLoader LOADER = IonSystemBuilder.standard().build().getLoader(); | ||||||||||||||||||||
|
||||||||||||||||||||
@ParameterizedTest(name = "{0},{1}") | ||||||||||||||||||||
@MethodSource("allCombinations") | ||||||||||||||||||||
public void taglessExpressionGroup(InputType inputType, StreamType streamType) throws Exception { | ||||||||||||||||||||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||||||||||||||||||
IonRawWriter_1_1 writer = streamType.newWriter(out); | ||||||||||||||||||||
|
||||||||||||||||||||
writer.writeIVM(); | ||||||||||||||||||||
Map<String, Integer> symbols = initializeSymbolTable(writer, "foo", "value"); | ||||||||||||||||||||
startEncodingDirective(writer); | ||||||||||||||||||||
startMacroTable(writer); | ||||||||||||||||||||
startMacro(writer, symbols, "foo"); | ||||||||||||||||||||
writeMacroSignatureFromDatagram(writer, symbols, LOADER.load("uint8::value '*'")); | ||||||||||||||||||||
writeVariableExpansion(writer, symbols, "value"); | ||||||||||||||||||||
endMacro(writer); | ||||||||||||||||||||
endMacroTable(writer); | ||||||||||||||||||||
endEncodingDirective(writer); | ||||||||||||||||||||
|
||||||||||||||||||||
Macro expectedMacro = new TemplateMacro( | ||||||||||||||||||||
Collections.singletonList(new Macro.Parameter("value", Macro.ParameterEncoding.Uint8, Macro.ParameterCardinality.ZeroOrMore)), | ||||||||||||||||||||
Collections.singletonList(new Expression.VariableRef(0)) | ||||||||||||||||||||
); | ||||||||||||||||||||
|
||||||||||||||||||||
writer.stepInEExp(0, false, expectedMacro); { | ||||||||||||||||||||
writer.stepInExpressionGroup(true); { | ||||||||||||||||||||
writer.writeInt(1); | ||||||||||||||||||||
writer.writeInt(2); | ||||||||||||||||||||
} writer.stepOut(); | ||||||||||||||||||||
} writer.stepOut(); | ||||||||||||||||||||
|
||||||||||||||||||||
byte[] data = getBytes(writer, out); | ||||||||||||||||||||
|
||||||||||||||||||||
try (IonReader reader = inputType.newReader(data)) { | ||||||||||||||||||||
assertEquals(IonType.INT, reader.next()); | ||||||||||||||||||||
assertEquals(1, reader.intValue()); | ||||||||||||||||||||
assertEquals(IonType.INT, reader.next()); | ||||||||||||||||||||
assertEquals(2, reader.intValue()); | ||||||||||||||||||||
assertNull(reader.next()); | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
// TODO cover every Ion type | ||||||||||||||||||||
// TODO tagless values and tagless argument groups | ||||||||||||||||||||
// TODO annotations in macro definition (using 'annotate' system macro) | ||||||||||||||||||||
// TODO macro invocation that expands to a system value | ||||||||||||||||||||
// TODO test error conditions | ||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This moves the cursor past the macro invocation header, positioning it on the first argument (if any). This method is called when parsing expression groups.