Skip to content
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

Implements encoding directive macros and comment macro #973

Merged
merged 1 commit into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,24 @@
package com.amazon.ion.impl.macro

import com.amazon.ion.*
import com.amazon.ion.impl.*
import com.amazon.ion.impl.macro.Expression.*
import java.math.BigInteger
import kotlin.reflect.KFunction1

/**
* Nothing in this file should be made public because it would expose the shaded kotlin std library in our public API.
*/

/** A marker annotation for a [type-safe builder](https://kotlinlang.org/docs/type-safe-builders.html). */
@DslMarker
annotation class ExpressionBuilderDslMarker
internal annotation class ExpressionBuilderDslMarker

/** Base DSL; functions are common for [DataModelExpression], [TemplateBodyExpression], and [EExpressionBodyExpression]. */
interface ValuesDsl {
internal interface ValuesDsl {
fun <T> annotated(annotations: List<SymbolToken>, valueFn: KFunction1<T, Unit>, value: T)
fun <T> annotated(annotation: SystemSymbols_1_1, valueFn: KFunction1<T, Unit>, value: T) =
annotated(listOf(annotation.token), valueFn, value)
fun nullValue(value: IonType = IonType.NULL)
fun bool(value: Boolean)
fun int(value: Long)
Expand All @@ -22,20 +29,22 @@
fun decimal(value: Decimal)
fun timestamp(value: Timestamp)
fun symbol(value: SymbolToken)
fun symbol(value: String) = symbol(_Private_Utils.newSymbolToken(value))
fun symbol(value: SystemSymbols_1_1) = symbol(value.token)
fun string(value: String)
fun clob(value: ByteArray)
fun blob(value: ByteArray)

/** Helper interface for use when building the content of a struct */
interface Fields {
fun fieldName(fieldName: SymbolToken)
fun fieldName(fieldName: String) = fieldName(FakeSymbolToken(fieldName, -1))
fun fieldName(fieldName: String) = fieldName(_Private_Utils.newSymbolToken(fieldName))
}
}

/** DSL for building [DataModelExpression] lists. */
@ExpressionBuilderDslMarker
interface DataModelDsl : ValuesDsl {
internal interface DataModelDsl : ValuesDsl {

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

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/ExpressionBuilderDsl.kt#L47

Added line #L47 was not covered by tests
fun list(content: DataModelDsl.() -> Unit)
fun sexp(content: DataModelDsl.() -> Unit)
fun struct(content: Fields.() -> Unit)
Expand All @@ -46,7 +55,7 @@

/** DSL for building [TemplateBodyExpression] lists. */
@ExpressionBuilderDslMarker
interface TemplateDsl : ValuesDsl {
internal interface TemplateDsl : ValuesDsl {

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

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/ExpressionBuilderDsl.kt#L58

Added line #L58 was not covered by tests
fun macro(macro: Macro, arguments: InvocationBody.() -> Unit)
fun variable(signatureIndex: Int)
fun list(content: TemplateDsl.() -> Unit)
Expand All @@ -64,7 +73,7 @@

/** DSL for building [EExpressionBodyExpression] lists. */
@ExpressionBuilderDslMarker
interface EExpDsl : ValuesDsl {
internal interface EExpDsl : ValuesDsl {

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

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/ExpressionBuilderDsl.kt#L76

Added line #L76 was not covered by tests
fun eexp(macro: Macro, arguments: InvocationBody.() -> Unit)
fun list(content: EExpDsl.() -> Unit)
fun sexp(content: EExpDsl.() -> Unit)
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/amazon/ion/impl/macro/Macro.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.amazon.ion.impl.TaglessEncoding
*/
sealed interface Macro {
val signature: List<Parameter>
val body: List<Expression.TemplateBodyExpression>?
val dependencies: Iterable<Macro>

data class Parameter(val variableName: String, val type: ParameterEncoding, val cardinality: ParameterCardinality) {
Expand Down
28 changes: 14 additions & 14 deletions src/main/java/com/amazon/ion/impl/macro/MacroEvaluator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
import com.amazon.ion.impl._Private_RecyclingStack
import com.amazon.ion.impl._Private_Utils.newSymbolToken
import com.amazon.ion.impl.macro.Expression.*
import com.amazon.ion.util.*
import java.io.ByteArrayOutputStream
import java.lang.IllegalStateException
import java.math.BigDecimal

/**
Expand Down Expand Up @@ -330,7 +328,9 @@
companion object {
@JvmStatic
fun forSystemMacro(macro: SystemMacro): ExpansionKind {
return when (macro) {
return if (macro.body != null) {
TemplateBody

Check warning on line 332 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#L332

Added line #L332 was not covered by tests
} else when (macro) {
SystemMacro.None -> Values // "none" takes no args, so we can treat it as an empty "values" expansion
SystemMacro.Values -> Values
SystemMacro.Annotate -> Annotate
Expand All @@ -344,6 +344,7 @@
SystemMacro.IfMulti -> IfMulti
SystemMacro.Repeat -> Repeat
SystemMacro.MakeField -> MakeField
else -> throw IllegalStateException("Unreachable. All other macros have a template body.")

Check warning on line 347 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#L347

Added line #L347 was not covered by tests
}
}
}
Expand Down Expand Up @@ -585,21 +586,20 @@
encodingExpressions: List<Expression>,
) {
val argIndices = calculateArgumentIndices(macro, encodingExpressions, argsStartInclusive, argsEndExclusive)

when (macro) {
is TemplateMacro -> pushExpansion(
val templateBody = macro.body
if (templateBody == null) {
// If there's no template body, it must be a system macro.
macro as SystemMacro
val kind = ExpansionKind.forSystemMacro(macro)
pushExpansion(kind, argsStartInclusive, argsEndExclusive, environment, encodingExpressions)
} else {
pushExpansion(
ExpansionKind.TemplateBody,
argsStartInclusive = 0,
argsEndExclusive = macro.body.size,
expressions = macro.body,
argsEndExclusive = templateBody.size,
expressions = templateBody,
environment = environment.createChild(encodingExpressions, argIndices)
)
// TODO: Values and MakeString have the same code in their blocks. As we get further along, see
// if this is generally applicable for all system macros.
is SystemMacro -> {
val kind = ExpansionKind.forSystemMacro(macro)
pushExpansion(kind, argsStartInclusive, argsEndExclusive, environment, encodingExpressions,)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@
}

override fun close() { /* Nothing to do (yet) */ }
override fun <T : Any?> asFacet(facetType: Class<T>?): Nothing = TODO("Not supported")
override fun <T : Any?> asFacet(facetType: Class<T>?): Nothing? = null

Check warning on line 96 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#L96

Added line #L96 was not covered by tests
override fun getDepth(): Int = containerStack.size()
override fun getSymbolTable(): SymbolTable = TODO("Not implemented in this abstraction")
override fun getSymbolTable(): SymbolTable? = null
Comment on lines -96 to +98
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗺️ Rather than throwing exceptions, these now return null, which is allowed by the interface. I don't know why I didn't do that to begin with, but now it means we can use e.g. _Private_IonSystem.iterate() on a MacroEvaluatorAsIonReader instance.


override fun getType(): IonType? = currentValueExpression?.type

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/amazon/ion/impl/macro/ParameterFactory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ object ParameterFactory {
@JvmStatic
fun zeroToManyTagged(name: String) = Parameter(name, ParameterEncoding.Tagged, ParameterCardinality.ZeroOrMore)
@JvmStatic
fun zeroOrOneTagged(name: String) = Parameter(name, ParameterEncoding.Tagged, ParameterCardinality.ZeroOrOne)
@JvmStatic
fun oneToManyTagged(name: String) = Parameter(name, ParameterEncoding.Tagged, ParameterCardinality.OneOrMore)
@JvmStatic
fun exactlyOneTagged(name: String) = Parameter(name, ParameterEncoding.Tagged, ParameterCardinality.ExactlyOne)
Expand Down
169 changes: 160 additions & 9 deletions src/main/java/com/amazon/ion/impl/macro/SystemMacro.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,27 @@
// SPDX-License-Identifier: Apache-2.0
package com.amazon.ion.impl.macro

import com.amazon.ion.impl.*
import com.amazon.ion.impl.SystemSymbols_1_1.*
import com.amazon.ion.impl.macro.ExpressionBuilderDsl.Companion.templateBody
import com.amazon.ion.impl.macro.ParameterFactory.exactlyOneFlexInt
import com.amazon.ion.impl.macro.ParameterFactory.exactlyOneTagged
import com.amazon.ion.impl.macro.ParameterFactory.oneToManyTagged
import com.amazon.ion.impl.macro.ParameterFactory.zeroOrOneTagged
import com.amazon.ion.impl.macro.ParameterFactory.zeroToManyTagged

/**
* Macros that are built in, rather than being defined by a template.
*/
enum class SystemMacro(val id: Byte, val macroName: String, override val signature: List<Macro.Parameter>) : Macro {
enum class SystemMacro(val id: Byte, val macroName: String, override val signature: List<Macro.Parameter>, override val body: List<Expression.TemplateBodyExpression>? = null) : Macro {
// Technically not system macros, but special forms. However, it's easier to model them as if they are macros in TDL.
// We give them an ID of -1 to distinguish that they are not addressable outside TDL.
IfNone(-1, "if_none", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
IfSome(-1, "if_some", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
IfSingle(-1, "if_single", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
IfMulti(-1, "if_multi", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
Comment on lines +18 to +23
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗺️ These were moved to the beginning of the enum because use needs to reference if_none, and the constructor args for an enum entry can only use back references to other entries of the same enum.


// The real macros
None(0, "none", emptyList()),
Values(1, "values", listOf(zeroToManyTagged("values"))),
Annotate(2, "annotate", listOf(zeroToManyTagged("ann"), exactlyOneTagged("value"))),
Expand All @@ -19,8 +31,150 @@
MakeBlob(5, "make_blob", listOf(zeroToManyTagged("bytes"))),
MakeDecimal(6, "make_decimal", listOf(exactlyOneFlexInt("coefficient"), exactlyOneFlexInt("exponent"))),

/**
* ```ion
* (macro set_symbols (symbols*)
* $ion_encoding::(
* (symbol_table [(%symbols)])
* (macro_table $ion_encoding)
* ))
* ```
*/
SetSymbols(
11, "set_symbols", listOf(zeroToManyTagged("symbols")),
templateBody {
annotated(ION_ENCODING, ::sexp) {
sexp {
symbol(SYMBOL_TABLE)
list { variable(0) }
}
sexp {
symbol(MACRO_TABLE)
symbol(ION_ENCODING)
}
}
}
),

/**
* ```ion
* (macro add_symbols (symbols*)
* $ion_encoding::(
* (symbol_table $ion_encoding [(%symbols)])
* (macro_table $ion_encoding)
* ))
* ```
*/
AddSymbols(
12, "add_symbols", listOf(zeroToManyTagged("symbols")),
templateBody {
annotated(ION_ENCODING, ::sexp) {
sexp {
symbol(SYMBOL_TABLE)
symbol(ION_ENCODING)
list { variable(0) }
}
sexp {
symbol(MACRO_TABLE)
symbol(ION_ENCODING)
}
}
}
),

/**
* ```ion
* (macro set_macros (macros*)
* $ion_encoding::(
* (symbol_table $ion_encoding)
* (macro_table (%macros))
* ))
* ```
*/
SetMacros(
13, "set_macros", listOf(zeroToManyTagged("macros")),
templateBody {
annotated(ION_ENCODING, ::sexp) {
sexp {
symbol(SYMBOL_TABLE)
symbol(ION_ENCODING)
}
sexp {
symbol(MACRO_TABLE)
variable(0)
}
}
}
),

/**
* ```ion
* (macro add_macros (macros*)
* $ion_encoding::(
* (symbol_table $ion_encoding)
* (macro_table $ion_encoding (%macros))
* ))
* ```
*/
AddMacros(
14, "add_macros", listOf(zeroToManyTagged("macros")),
templateBody {
annotated(ION_ENCODING, ::sexp) {
sexp {
symbol(SYMBOL_TABLE)
symbol(ION_ENCODING)
}
sexp {
symbol(MACRO_TABLE)
symbol(ION_ENCODING)
variable(0)
}
}
}
),

/**
* ```ion
* (macro use (catalog_key version?)
* $ion_encoding::(
* (import the_module (%catalog_key) (.if_none (%version) 1 (%version)))
* (symbol_table $ion_encoding the_module)
* (macro_table $ion_encoding the_module)
* ))
* ```
*/
Use(
15, "use", listOf(exactlyOneTagged("catalog_key"), zeroOrOneTagged("version")),
templateBody {
val theModule = _Private_Utils.newSymbolToken("the_module")
annotated(ION_ENCODING, ::sexp) {
sexp {
symbol(IMPORT)
symbol(theModule)
variable(0)
macro(IfNone) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're adding default, so we could use it here once we implement it.

variable(1)
int(1)
variable(1)
}
}
sexp {
symbol(SYMBOL_TABLE)
symbol(ION_ENCODING)
symbol(theModule)
}
sexp {
symbol(MACRO_TABLE)
symbol(ION_ENCODING)
symbol(theModule)
}
}
}
),

Repeat(17, "repeat", listOf(exactlyOneTagged("n"), oneToManyTagged("value"))),

Comment(21, "comment", listOf(zeroToManyTagged("values")), templateBody { macro(None) {} }),
MakeField(
22, "make_field",
listOf(
Expand All @@ -29,17 +183,14 @@
),

// TODO: Other system macros

// Technically not system macros, but special forms. However, it's easier to model them as if they are macros in TDL.
// We give them an ID of -1 to distinguish that they are not addressable outside TDL.
IfNone(-1, "if_none", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
IfSome(-1, "if_some", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
IfSingle(-1, "if_single", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
IfMulti(-1, "if_multi", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
;

override val dependencies: List<Macro>
get() = emptyList()
get() = body
?.filterIsInstance<Expression.MacroInvocation>()
?.map(Expression.MacroInvocation::macro)
?.distinct()

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

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/amazon/ion/impl/macro/SystemMacro.kt#L191-L192

Added lines #L191 - L192 were not covered by tests
?: emptyList()

companion object {

Expand Down
4 changes: 3 additions & 1 deletion src/main/java/com/amazon/ion/impl/macro/TemplateMacro.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package com.amazon.ion.impl.macro

/**
* Represents a template macro. A template macro is defined by a signature, and a list of template expressions.
* A template macro only gains a name and/or ID when it is added to a macro table.
*/
data class TemplateMacro(override val signature: List<Macro.Parameter>, val body: List<Expression.TemplateBodyExpression>) :
class TemplateMacro(override val signature: List<Macro.Parameter>, override val body: List<Expression.TemplateBodyExpression>) :
Macro {
// TODO: Consider rewriting the body of the macro if we discover that there are any macros invoked using only
// constants as arguments—either at compile time or lazily.
Expand Down
Loading
Loading