Skip to content

Commit

Permalink
Add stubs for macro evalution
Browse files Browse the repository at this point in the history
  • Loading branch information
popematt committed Aug 14, 2024
1 parent 19f2208 commit 20bf7ac
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 1 deletion.
15 changes: 15 additions & 0 deletions src/main/java/com/amazon/ion/impl/macro/EncodingContext.kt
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>

Check warning on line 8 in src/main/java/com/amazon/ion/impl/macro/EncodingContext.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/EncodingContext.kt#L7-L8

Added lines #L7 - L8 were not covered by tests
) {
companion object {
// TODO: Replace this with a DEFAULT encoding context that includes system macros.
@JvmStatic
val EMPTY = EncodingContext(emptyMap())

Check warning on line 13 in src/main/java/com/amazon/ion/impl/macro/EncodingContext.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/EncodingContext.kt#L13

Added line #L13 was not covered by tests
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/amazon/ion/impl/macro/Expression.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ sealed interface Expression {
val endExclusive: Int
}

/** Marker interface representing E-Expressions. */
/** Marker interface representing expressions that can be present in E-Expressions. */
sealed interface EncodingExpression : Expression

/** Marker interface representing expressions in the body of a template. */
Expand Down
51 changes: 51 additions & 0 deletions src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt
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,

Check warning on line 18 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt#L17-L18

Added lines #L17 - L18 were not covered by tests
// TODO: Add expansion limit
) {

/**
* Initialize the macro evaluator with an E-Expression.
*/
fun initExpansion(encodingExpressions: List<EncodingExpression>) {
TODO()

Check warning on line 26 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt#L26

Added line #L26 was not covered by tests
}

/**
* 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()

Check warning on line 34 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt#L34

Added line #L34 was not covered by tests
}

/**
* Steps out of the current [DataModelContainer].
*/
fun stepOut() {
TODO()

Check warning on line 41 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt#L41

Added line #L41 was not covered by tests
}

/**
* Steps in to the current [DataModelContainer].
* Throws [IonException] if not positioned on a container.
*/
fun stepIn() {
TODO()

Check warning on line 49 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt#L49

Added line #L49 was not covered by tests
}
}
149 changes: 149 additions & 0 deletions src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt
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,

Check warning on line 24 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L23-L24

Added lines #L23 - L24 were not covered by tests
) : IonReader {

private class ContainerInfo {

Check warning on line 27 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L27

Added line #L27 was not covered by tests
@JvmField var currentFieldName: Expression.FieldName? = null
@JvmField var container: Expression.DataModelContainer? = null
}
private val containerStack = _Private_RecyclingStack(8) { ContainerInfo() }

Check warning on line 31 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L31

Added line #L31 was not covered by tests

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

Check warning on line 40 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L40

Added line #L40 was not covered by tests
while (queuedValueExpression == null) {
val nextCandidate = evaluator.expandNext() ?: return
when (nextCandidate) {

Check warning on line 43 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L43

Added line #L43 was not covered by tests
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()

Check warning on line 61 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L58-L61

Added lines #L58 - L61 were not covered by tests
}

override fun stepIn() {
// This is essentially a no-op for Lists and SExps
containerStack.peek().currentFieldName = this.currentFieldName

Check warning on line 66 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L66

Added line #L66 was not covered by tests

val containerToStepInto = currentValueExpression
evaluator.stepIn()
containerStack.push {
it.container = containerToStepInto as Expression.DataModelContainer
it.currentFieldName = null

Check warning on line 72 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L68-L72

Added lines #L68 - L72 were not covered by tests
}
currentFieldName = null
currentValueExpression = null
queuedFieldName = null
queuedValueExpression = null

Check warning on line 77 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L74-L77

Added lines #L74 - L77 were not covered by tests
}

override fun stepOut() {
evaluator.stepOut()
containerStack.pop()

Check warning on line 82 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L81-L82

Added lines #L81 - L82 were not covered by tests
// 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

Check warning on line 87 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L84-L87

Added lines #L84 - L87 were not covered by tests
}

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")

Check warning on line 93 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L90-L93

Added lines #L90 - L93 were not covered by tests

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")

Check warning on line 102 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L102

Added line #L102 was not covered by tests

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

Check warning on line 109 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L108-L109

Added lines #L108 - L109 were not covered by tests

override fun getIntegerSize(): IntegerSize {
// TODO: Make this more efficient, more precise
return when (val intExpression = currentValueExpression as Expression.IntValue) {

Check warning on line 113 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L113

Added line #L113 was not covered by tests
is Expression.LongIntValue -> if (intExpression.value.toInt().toLong() == intExpression.value) {
IntegerSize.INT

Check warning on line 115 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L115

Added line #L115 was not covered by tests
} else {
IntegerSize.LONG

Check warning on line 117 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L117

Added line #L117 was not covered by tests
}
is Expression.BigIntValue -> IntegerSize.BIG_INTEGER

Check warning on line 119 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L119

Added line #L119 was not covered by tests
}
}

/** TODO: Throw on data loss */
override fun intValue(): Int = longValue().toInt()

Check warning on line 124 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L124

Added line #L124 was not covered by tests

override fun longValue(): Long = when (val intExpression = currentValueExpression as Expression.IntValue) {

Check warning on line 126 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L126

Added line #L126 was not covered by tests
is Expression.LongIntValue -> intExpression.value
is Expression.BigIntValue -> intExpression.value.longValueExact()

Check warning on line 128 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L128

Added line #L128 was not covered by tests
}

override fun bigIntegerValue(): BigInteger = when (val intExpression = currentValueExpression as Expression.IntValue) {

Check warning on line 131 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L131

Added line #L131 was not covered by tests
is Expression.LongIntValue -> intExpression.value.toBigInteger()
is Expression.BigIntValue -> intExpression.value

Check warning on line 133 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L133

Added line #L133 was not covered by tests
}

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()

Check warning on line 144 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L136-L144

Added lines #L136 - L144 were not covered by tests

override fun getBytes(buffer: ByteArray?, offset: Int, len: Int): Int {
TODO("Not yet implemented")

Check warning on line 147 in src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroEvaluatorAsIonReader.kt#L147

Added line #L147 was not covered by tests
}
}
31 changes: 31 additions & 0 deletions src/main/java/com/amazon/ion/impl/macro/MacroExpressionizer.kt
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) {

Check warning on line 15 in src/main/java/com/amazon/ion/impl/macro/MacroExpressionizer.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroExpressionizer.kt#L15

Added line #L15 was not covered by tests
/**
* 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()

Check warning on line 29 in src/main/java/com/amazon/ion/impl/macro/MacroExpressionizer.kt

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/MacroExpressionizer.kt#L29

Added line #L29 was not covered by tests
}
}
1 change: 1 addition & 0 deletions src/main/java/com/amazon/ion/impl/macro/MacroRef.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ sealed interface MacroRef {
// TODO: See if these could be inline value classes
@JvmInline value class ByName(val name: String) : MacroRef
@JvmInline value class ById(val id: Long) : MacroRef
// TODO: Since system macros have an independent address space, do we need to have a `SystemById` variant?
}

0 comments on commit 20bf7ac

Please sign in to comment.