-
Notifications
You must be signed in to change notification settings - Fork 110
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
248 additions
and
1 deletion.
There are no files selected for viewing
15 changes: 15 additions & 0 deletions
15
src/main/java/com/amazon/ion/impl/macro/EncodingContext.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.amazon.ion.impl.macro | ||
|
||
/** | ||
* When we implement modules, this will likely need to be replaced. | ||
* For now, it is a placeholder for what is to come and a container for the macro table. | ||
*/ | ||
class EncodingContext( | ||
val macroTable: Map<MacroRef, Macro> | ||
) { | ||
companion object { | ||
// TODO: Replace this with a DEFAULT encoding context that includes system macros. | ||
@JvmStatic | ||
val EMPTY = EncodingContext(emptyMap()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.amazon.ion.impl.macro | ||
|
||
import com.amazon.ion.* | ||
import com.amazon.ion.impl.macro.Expression.* | ||
|
||
/** | ||
* Evaluates an EExpression from a List of [EncodingExpression] and the [TemplateBodyExpression]s | ||
* given in the macro table of the [EncodingContext]. | ||
* | ||
* General Usage: | ||
* - To start evaluating an e-expression, call [initExpansion] | ||
* - Call [expandNext] to get the next field name or value, or null | ||
* if the end of the container or end of expansion has been reached. | ||
* - Call [stepIn] when positioned on a container to step into that container. | ||
* - Call [stepOut] to step out of the current container. | ||
*/ | ||
class MacroEvaluator( | ||
private val encodingContext: EncodingContext, | ||
// TODO: Add expansion limit | ||
) { | ||
|
||
/** | ||
* Initialize the macro evaluator with an E-Expression. | ||
*/ | ||
fun initExpansion(encodingExpressions: List<EncodingExpression>) { | ||
TODO() | ||
} | ||
|
||
/** | ||
* Evaluate the macro expansion until the next [DataModelExpression] can be returned. | ||
* Returns null if at the end of a container or at the end of the expansion. | ||
*/ | ||
fun expandNext(): DataModelExpression? { | ||
TODO() | ||
} | ||
|
||
/** | ||
* Steps out of the current [DataModelContainer]. | ||
*/ | ||
fun stepOut() { | ||
TODO() | ||
} | ||
|
||
/** | ||
* Steps in to the current [DataModelContainer]. | ||
* Throws [IonException] if not positioned on a container. | ||
*/ | ||
fun stepIn() { | ||
TODO() | ||
} | ||
} |
149 changes: 149 additions & 0 deletions
149
src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package com.amazon.ion.impl.macro | ||
|
||
import com.amazon.ion.Decimal | ||
import com.amazon.ion.IntegerSize | ||
import com.amazon.ion.IonReader | ||
import com.amazon.ion.IonType | ||
import com.amazon.ion.SymbolTable | ||
import com.amazon.ion.SymbolToken | ||
import com.amazon.ion.Timestamp | ||
import com.amazon.ion.impl._Private_RecyclingStack | ||
import java.math.BigDecimal | ||
import java.math.BigInteger | ||
import java.util.* | ||
|
||
/** | ||
* This class is an example of how we might wrap the macro evaluator's [Expression] model, adapting it to an [IonReader]. | ||
* | ||
* TODO: | ||
* - Consider merging this with [MacroEvaluator]. | ||
* - Error handling is inconsistent with other [IonReader] implementations | ||
* - Testing | ||
*/ | ||
class MacroEvaluatorAsIonReader( | ||
private val evaluator: MacroEvaluator, | ||
) : IonReader { | ||
|
||
private class ContainerInfo { | ||
@JvmField var currentFieldName: Expression.FieldName? = null | ||
@JvmField var container: Expression.DataModelContainer? = null | ||
} | ||
private val containerStack = _Private_RecyclingStack(8) { ContainerInfo() } | ||
|
||
private var currentFieldName: Expression.FieldName? = null | ||
private var currentValueExpression: Expression.DataModelValue? = null | ||
|
||
private var queuedFieldName: Expression.FieldName? = null | ||
private var queuedValueExpression: Expression.DataModelValue? = null | ||
|
||
private fun queueNext() { | ||
queuedValueExpression = null | ||
while (queuedValueExpression == null) { | ||
val nextCandidate = evaluator.expandNext() ?: return | ||
when (nextCandidate) { | ||
is Expression.FieldName -> queuedFieldName = nextCandidate | ||
is Expression.DataModelValue -> queuedValueExpression = nextCandidate | ||
} | ||
} | ||
} | ||
|
||
@Deprecated("Deprecated in Java") | ||
override fun hasNext(): Boolean { | ||
if (queuedValueExpression == null) queueNext() | ||
return queuedValueExpression != null | ||
} | ||
|
||
override fun next(): IonType? { | ||
if (!hasNext()) return null | ||
currentValueExpression = queuedValueExpression | ||
currentFieldName = queuedFieldName | ||
queuedValueExpression = null | ||
return getType() | ||
} | ||
|
||
override fun stepIn() { | ||
// This is essentially a no-op for Lists and SExps | ||
containerStack.peek().currentFieldName = this.currentFieldName | ||
|
||
val containerToStepInto = currentValueExpression | ||
evaluator.stepIn() | ||
containerStack.push { | ||
it.container = containerToStepInto as Expression.DataModelContainer | ||
it.currentFieldName = null | ||
} | ||
currentFieldName = null | ||
currentValueExpression = null | ||
queuedFieldName = null | ||
queuedValueExpression = null | ||
} | ||
|
||
override fun stepOut() { | ||
evaluator.stepOut() | ||
containerStack.pop() | ||
// This is essentially a no-op for Lists and SExps | ||
currentFieldName = containerStack.peek().currentFieldName | ||
currentValueExpression = null // Must call `next()` to get the next value | ||
queuedFieldName = null | ||
queuedValueExpression = null | ||
} | ||
|
||
override fun close() { TODO("Not yet implemented") } | ||
override fun <T : Any?> asFacet(facetType: Class<T>?): Nothing = TODO("Not supported") | ||
override fun getDepth(): Int = containerStack.size() | ||
override fun getSymbolTable(): SymbolTable = TODO("Not implemented in this abstraction") | ||
|
||
override fun getType(): IonType? = currentValueExpression?.type | ||
|
||
override fun getTypeAnnotations(): Array<String>? = currentValueExpression?.annotations?.let { Array(it.size) { i -> it[i].assumeText() } } | ||
override fun getTypeAnnotationSymbols(): Array<SymbolToken>? = currentValueExpression?.annotations?.toTypedArray() | ||
// TODO: Make this into an iterator that unwraps the SymbolTokens as it goes instead of allocating a new list | ||
override fun iterateTypeAnnotations(): MutableIterator<String>? = currentValueExpression?.annotations?.mapTo(mutableListOf()) { it.assumeText() }?.iterator() | ||
|
||
override fun isInStruct(): Boolean = TODO("Not yet implemented") | ||
|
||
override fun getFieldId(): Int = currentFieldName?.value?.sid ?: 0 | ||
override fun getFieldName(): String? = currentFieldName?.value?.text | ||
override fun getFieldNameSymbol(): SymbolToken? = currentFieldName?.value | ||
|
||
override fun isNullValue(): Boolean = currentValueExpression is Expression.NullValue | ||
override fun booleanValue(): Boolean = (currentValueExpression as Expression.BoolValue).value | ||
|
||
override fun getIntegerSize(): IntegerSize { | ||
// TODO: Make this more efficient, more precise | ||
return when (val intExpression = currentValueExpression as Expression.IntValue) { | ||
is Expression.LongIntValue -> if (intExpression.value.toInt().toLong() == intExpression.value) { | ||
IntegerSize.INT | ||
} else { | ||
IntegerSize.LONG | ||
} | ||
is Expression.BigIntValue -> IntegerSize.BIG_INTEGER | ||
} | ||
} | ||
|
||
/** TODO: Throw on data loss */ | ||
override fun intValue(): Int = longValue().toInt() | ||
|
||
override fun longValue(): Long = when (val intExpression = currentValueExpression as Expression.IntValue) { | ||
is Expression.LongIntValue -> intExpression.value | ||
is Expression.BigIntValue -> intExpression.value.longValueExact() | ||
} | ||
|
||
override fun bigIntegerValue(): BigInteger = when (val intExpression = currentValueExpression as Expression.IntValue) { | ||
is Expression.LongIntValue -> intExpression.value.toBigInteger() | ||
is Expression.BigIntValue -> intExpression.value | ||
} | ||
|
||
override fun doubleValue(): Double = (currentValueExpression as Expression.FloatValue).value | ||
override fun bigDecimalValue(): BigDecimal = (currentValueExpression as Expression.DecimalValue).value | ||
override fun decimalValue(): Decimal = Decimal.valueOf(bigDecimalValue()) | ||
override fun timestampValue(): Timestamp = (currentValueExpression as Expression.TimestampValue).value | ||
override fun dateValue(): Date = timestampValue().dateValue() | ||
override fun stringValue(): String = (currentValueExpression as Expression.TextValue).stringValue | ||
override fun symbolValue(): SymbolToken = (currentValueExpression as Expression.SymbolValue).value | ||
override fun byteSize(): Int = (currentValueExpression as Expression.LobValue).value.size | ||
override fun newBytes(): ByteArray = (currentValueExpression as Expression.LobValue).value.copyOf() | ||
|
||
override fun getBytes(buffer: ByteArray?, offset: Int, len: Int): Int { | ||
TODO("Not yet implemented") | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
src/main/java/com/amazon/ion/impl/macro/MacroExpressionizer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.amazon.ion.impl.macro | ||
|
||
import com.amazon.ion.* | ||
|
||
/** | ||
* A [MacroExpressionizer] reads an E-Expression from an [IonReader], [IonCursor], or similar and constructs | ||
* a list of [Expression]s representing the E-Expression and its arguments. | ||
* | ||
* There are two sources of expressions. The template macro definitions, and the macro arguments. | ||
* The macro expander merges those. | ||
* | ||
* The [Expression] model does not (yet) support lazily reading values, so for now, all macro arguments must | ||
* be read eagerly. | ||
*/ | ||
class MacroExpressionizer(private val reader: IonReader) { | ||
/** | ||
* Reads an E-Expression into the [Expression] model for evaluation by the [MacroEvaluator]. | ||
* | ||
* Caller is responsible for ensuring that the reader is positioned at the op-code of an E-Expression. | ||
* | ||
* **Implementation Overview:** | ||
* 1. Read macro id | ||
* 2. Get corresponding macro signature from encoding context | ||
* 3. Read macro args | ||
* 1. Recurse/loop to 1 if an E-Expression is found in the arguments | ||
* | ||
*/ | ||
fun readMacroExpression(encodingContext: EncodingContext): List<Expression.EncodingExpression> { | ||
TODO() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters