Skip to content

Commit

Permalink
Move classes in Macro.kt to their own files
Browse files Browse the repository at this point in the history
  • Loading branch information
popematt committed Oct 7, 2024
1 parent 4ac2697 commit 36d63da
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 82 deletions.
71 changes: 1 addition & 70 deletions src/main/java/com/amazon/ion/impl/macro/Macro.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
package com.amazon.ion.impl.macro

import com.amazon.ion.impl.*
import com.amazon.ion.impl.macro.Macro.Parameter.Companion.exactlyOneTagged
import com.amazon.ion.impl.macro.Macro.Parameter.Companion.zeroToManyTagged
import com.amazon.ion.impl.TaglessEncoding

/**
* A [Macro] is either a [SystemMacro] or a [TemplateMacro].
Expand All @@ -15,13 +13,6 @@ sealed interface Macro {

data class Parameter(val variableName: String, val type: ParameterEncoding, val cardinality: ParameterCardinality) {
override fun toString() = "$type::$variableName${cardinality.sigil}"

companion object {
@JvmStatic
fun zeroToManyTagged(name: String) = Parameter(name, Macro.ParameterEncoding.Tagged, Macro.ParameterCardinality.ZeroOrMore)
@JvmStatic
fun exactlyOneTagged(name: String) = Parameter(name, Macro.ParameterEncoding.Tagged, Macro.ParameterCardinality.ExactlyOne)
}
}

// TODO: See if we can DRY up ParameterEncoding and PrimitiveType
Expand Down Expand Up @@ -82,63 +73,3 @@ sealed interface 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>) : 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.
// For example, the body of: (macro foo (x) (values (make_string "foo" "bar") x))
// could be rewritten as: (values "foobar" x)

private val cachedHashCode by lazy { signature.hashCode() * 31 + body.hashCode() }
override fun hashCode(): Int = cachedHashCode

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is TemplateMacro) return false
// Check the hashCode as a quick check before we dive into the actual data.
if (cachedHashCode != other.cachedHashCode) return false
if (signature != other.signature) return false
if (body != other.body) return false
return true
}

override val dependencies: List<Macro> by lazy {
body.filterIsInstance<Expression.MacroInvocation>()
.map { it.macro }
.distinct()
}
}

/**
* Macros that are built in, rather than being defined by a template.
*/
enum class SystemMacro(val macroName: String, override val signature: List<Macro.Parameter>) : Macro {
None("none", emptyList()),
Values("values", listOf(zeroToManyTagged("values"))),
Annotate("annotate", listOf(zeroToManyTagged("ann"), exactlyOneTagged("value"))),
MakeString("make_string", listOf(zeroToManyTagged("text"))),
MakeSymbol("make_symbol", listOf(zeroToManyTagged("text"))),
MakeDecimal(
"make_decimal",
listOf(
Macro.Parameter("coefficient", Macro.ParameterEncoding.CompactInt, Macro.ParameterCardinality.ExactlyOne),
Macro.Parameter("exponent", Macro.ParameterEncoding.CompactInt, Macro.ParameterCardinality.ExactlyOne),
)
),

// 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.
IfNone("IfNone", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
IfSome("IfSome", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
IfSingle("IfSingle", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
IfMulti("IfMulti", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
;

override val dependencies: List<Macro>
get() = emptyList()
}
17 changes: 17 additions & 0 deletions src/main/java/com/amazon/ion/impl/macro/ParameterFactory.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package com.amazon.ion.impl.macro

import com.amazon.ion.impl.macro.Macro.*

/**
* Convenience functions for concisely creating [Macro.Parameter]s.
*/
object ParameterFactory {
@JvmStatic
fun zeroToManyTagged(name: String) = Parameter(name, ParameterEncoding.Tagged, ParameterCardinality.ZeroOrMore)
@JvmStatic
fun exactlyOneTagged(name: String) = Parameter(name, ParameterEncoding.Tagged, ParameterCardinality.ExactlyOne)
@JvmStatic
fun exactlyOneFlexInt(name: String) = Parameter(name, ParameterEncoding.CompactInt, ParameterCardinality.ExactlyOne)
}
57 changes: 57 additions & 0 deletions src/main/java/com/amazon/ion/impl/macro/SystemMacro.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package com.amazon.ion.impl.macro

import com.amazon.ion.impl.macro.ParameterFactory.exactlyOneFlexInt
import com.amazon.ion.impl.macro.ParameterFactory.exactlyOneTagged
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: Int, val macroName: String, override val signature: List<Macro.Parameter>) : Macro {
None(0, "none", emptyList()),
Values(1, "values", listOf(zeroToManyTagged("values"))),
Annotate(2, "annotate", listOf(zeroToManyTagged("ann"), exactlyOneTagged("value"))),
MakeString(3, "make_string", listOf(zeroToManyTagged("text"))),
MakeSymbol(4, "make_symbol", listOf(zeroToManyTagged("text"))),
MakeDecimal(6, "make_decimal", listOf(exactlyOneFlexInt("coefficient"), exactlyOneFlexInt("exponent"))),

// 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, "IfNone", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
IfSome(-1, "IfSome", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
IfSingle(-1, "IfSingle", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
IfMulti(-1, "IfMulti", listOf(zeroToManyTagged("stream"), zeroToManyTagged("true_branch"), zeroToManyTagged("false_branch"))),
;

override val dependencies: List<Macro>
get() = emptyList()

companion object {

private val MACROS_BY_NAME: Map<String, SystemMacro> = SystemMacro.entries.associateBy { it.macroName }

// TODO: Once all of the macros are implemented, replace this with an array as in SystemSymbols_1_1
private val MACROS_BY_ID: Map<Int, SystemMacro> = SystemMacro.entries
.filterNot { it.id < 0 }
.associateBy { it.id }

@JvmStatic
fun size() = MACROS_BY_ID.size

/** Gets a [SystemMacro] by its address in the system table */
@JvmStatic
operator fun get(id: Int): SystemMacro? = MACROS_BY_ID[id]

/** Gets, by name, a [SystemMacro] with an address in the system table (i.e. that can be invoked as E-Expressions) */
@JvmStatic
operator fun get(name: String): SystemMacro? = MACROS_BY_NAME[name]?.takeUnless { it.id < 0 }

/** Gets a [SystemMacro] by name, including those which are not in the system table (i.e. special forms) */
@JvmStatic
fun getMacroOrSpecialForm(name: String): SystemMacro? = MACROS_BY_NAME[name]
}
}
32 changes: 32 additions & 0 deletions src/main/java/com/amazon/ion/impl/macro/TemplateMacro.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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>) :
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.
// For example, the body of: (macro foo (x) (values (make_string "foo" "bar") x))
// could be rewritten as: (values "foobar" x)

private val cachedHashCode by lazy { signature.hashCode() * 31 + body.hashCode() }
override fun hashCode(): Int = cachedHashCode

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is TemplateMacro) return false
// Check the hashCode as a quick check before we dive into the actual data.
if (cachedHashCode != other.cachedHashCode) return false
if (signature != other.signature) return false
if (body != other.body) return false
return true
}

override val dependencies: List<Macro> by lazy {
body.filterIsInstance<Expression.MacroInvocation>()
.map { it.macro }
.distinct()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.amazon.ion.impl.macro.Expression;
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.TemplateMacro;
import com.amazon.ion.system.IonReaderBuilder;
import org.junit.jupiter.params.ParameterizedTest;
Expand Down Expand Up @@ -906,7 +907,7 @@ public void macroInvocationInMacroDefinition(InputType inputType) throws Excepti

Macro simonSaysMacro = new TemplateMacro(
Collections.singletonList(
Macro.Parameter.exactlyOneTagged("anything")
ParameterFactory.exactlyOneTagged("anything")
),
Collections.singletonList(
new Expression.VariableRef(0)
Expand Down
24 changes: 13 additions & 11 deletions src/test/java/com/amazon/ion/impl/bin/IonManagedWriter_1_1_Test.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import com.amazon.ion.impl.*
import com.amazon.ion.impl.macro.*
import com.amazon.ion.impl.macro.ExpressionBuilderDsl.Companion.templateBody
import com.amazon.ion.impl.macro.Macro.*
import com.amazon.ion.impl.macro.ParameterFactory.exactlyOneTagged
import com.amazon.ion.impl.macro.ParameterFactory.zeroToManyTagged
import com.amazon.ion.system.*
import java.io.ByteArrayOutputStream
import java.math.BigInteger
Expand Down Expand Up @@ -199,8 +201,8 @@ internal class IonManagedWriter_1_1_Test {
fun `write an e-expression with a expression group argument`() {
val macro = TemplateMacro(
signature = listOf(
Parameter.zeroToManyTagged("a"),
Parameter.zeroToManyTagged("b"),
zeroToManyTagged("a"),
zeroToManyTagged("b"),
),
body = templateBody { string("foo") }
)
Expand Down Expand Up @@ -399,14 +401,14 @@ internal class IonManagedWriter_1_1_Test {
return listOf(
case(
"single required parameter",
signature = listOf(Parameter.exactlyOneTagged("x")),
signature = listOf(exactlyOneTagged("x")),
expectedSignature = "(x)"
),
case(
"multiple required parameters",
signature = listOf(
Parameter.exactlyOneTagged("x"),
Parameter.exactlyOneTagged("y")
exactlyOneTagged("x"),
exactlyOneTagged("y")
),
expectedSignature = "(x y)"
),
Expand Down Expand Up @@ -599,7 +601,7 @@ internal class IonManagedWriter_1_1_Test {
),
case(
"variable",
signature = listOf(Parameter.exactlyOneTagged("x")),
signature = listOf(exactlyOneTagged("x")),
expectedSignature = "(x)",
body = {
variable(0)
Expand All @@ -608,7 +610,7 @@ internal class IonManagedWriter_1_1_Test {
),
case(
"multiple variables",
signature = listOf("x", "y", "z").map(Parameter::exactlyOneTagged),
signature = listOf("x", "y", "z").map(::exactlyOneTagged),
expectedSignature = "(x y z)",
body = {
list {
Expand Down Expand Up @@ -695,7 +697,7 @@ internal class IonManagedWriter_1_1_Test {
endMacro()
startMacro(
TemplateMacro(
listOf(Parameter.exactlyOneTagged("x")),
listOf(exactlyOneTagged("x")),
templateBody {
macro(fooMacro) {
variable(0)
Expand Down Expand Up @@ -787,7 +789,7 @@ internal class IonManagedWriter_1_1_Test {
// Using the qualified class name would be verbose, but may be safer for general
// use so that there is almost no risk of having a name conflict with another macro.
private val MACRO_NAME = Polygon::class.simpleName!!.replace(".", "_")
private val IDENTITY = TemplateMacro(listOf(Parameter.zeroToManyTagged("x")), templateBody { variable(0) })
private val IDENTITY = TemplateMacro(listOf(zeroToManyTagged("x")), templateBody { variable(0) })
private val MACRO = TemplateMacro(
signature = listOf(
// TODO: Change this to a macro shape when they are supported
Expand Down Expand Up @@ -843,8 +845,8 @@ internal class IonManagedWriter_1_1_Test {
private val MACRO_NAME = Point2D::class.simpleName!!.replace(".", "_")
private val MACRO = TemplateMacro(
signature = listOf(
Parameter.exactlyOneTagged("x"),
Parameter.exactlyOneTagged("y"),
exactlyOneTagged("x"),
exactlyOneTagged("y"),
),
templateBody {
struct {
Expand Down

0 comments on commit 36d63da

Please sign in to comment.