Skip to content

Recursive macro expansion in TDL containers #647

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

Merged
merged 5 commits into from
Sep 29, 2023
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
50 changes: 27 additions & 23 deletions src/lazy/any_encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::lazy::binary::raw::value::LazyRawBinaryValue;
use crate::lazy::decoder::private::{LazyContainerPrivate, LazyRawValuePrivate};
use crate::lazy::decoder::{
LazyDecoder, LazyRawFieldExpr, LazyRawReader, LazyRawSequence, LazyRawStruct, LazyRawValue,
LazyRawValueExpr,
LazyRawValueExpr, RawFieldExpr, RawValueExpr,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🗺️ The enum LazyRawValueExpr<'data, D> can be either a value literal (D::Value) or a macro invocation (D::MacroInvocation). Starting with this PR, we needed the ability to talk about value expressions that might have come from templates, which are not a kind of stream encoding and so do not have a corresponding implementation D: LazyDecoder<'data>.

The new enum RawValueExpr<V, M> allows any types to be used as the value (V) and the macro invocation (M). LazyRawValueExpr<'data, D> is now a type alias for the most common kind of value expression:

pub type LazyRawValueExpr<'data, D> =
    RawValueExpr<
        <D as LazyDecoder<'data>>::Value,
        <D as LazyDecoder<'data>>::MacroInvocation
    >;

An analogous change has been made for the LazyRawFieldExpr/RawFieldExpr pair.

};
use crate::lazy::encoding::{BinaryEncoding_1_0, Never, TextEncoding_1_0, TextEncoding_1_1};
use crate::lazy::expanded::macro_evaluator::MacroInvocation;
Expand Down Expand Up @@ -114,11 +114,11 @@ impl<'data> Iterator for LazyRawAnyMacroArgsIterator<'data> {
fn next(&mut self) -> Option<Self::Item> {
match self.encoding {
LazyRawAnyMacroArgsIteratorKind::Text_1_1(mut iter) => match iter.next() {
Some(Ok(LazyRawValueExpr::ValueLiteral(value))) => Some(Ok(
LazyRawValueExpr::ValueLiteral(LazyRawAnyValue::from(value)),
)),
Some(Ok(LazyRawValueExpr::MacroInvocation(invocation))) => Some(Ok(
LazyRawValueExpr::MacroInvocation(LazyRawAnyMacroInvocation {
Some(Ok(RawValueExpr::ValueLiteral(value))) => {
Some(Ok(RawValueExpr::ValueLiteral(LazyRawAnyValue::from(value))))
}
Some(Ok(RawValueExpr::MacroInvocation(invocation))) => Some(Ok(
RawValueExpr::MacroInvocation(LazyRawAnyMacroInvocation {
Comment on lines -117 to +121
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🗺️ Now that LazyRawValueExpr<'data, D> is a type alias, existing concrete uses of it would need to supply type annotations:

// Before:
LazyRawValueExpr::ValueLiteral(v)
// After:
LazyRawValueExpr::<'data, D>::ValueLiteral(v)

However, the compiler is able to infer the correct types for RawValuExpr, to which the LazyRawValueExpr alias points. I've switched instantiations of LazyRawValueExpr to RawValueExpr for concision.

Unfortunately, that change makes up the lion's share of the line count in this diff. x_x

An analogous change has been made for the LazyRawFieldExpr/RawFieldExpr pair.

encoding: LazyRawAnyMacroInvocationKind::Text_1_1(invocation),
}),
)),
Expand Down Expand Up @@ -217,12 +217,12 @@ impl<'data> From<LazyRawValueExpr<'data, TextEncoding_1_0>>
{
fn from(value: LazyRawValueExpr<'data, TextEncoding_1_0>) -> Self {
match value {
LazyRawValueExpr::ValueLiteral(v) => LazyRawValueExpr::ValueLiteral(v.into()),
LazyRawValueExpr::MacroInvocation(m) => {
RawValueExpr::ValueLiteral(v) => RawValueExpr::ValueLiteral(v.into()),
RawValueExpr::MacroInvocation(m) => {
let invocation = LazyRawAnyMacroInvocation {
encoding: LazyRawAnyMacroInvocationKind::Text_1_0(m),
};
LazyRawValueExpr::MacroInvocation(invocation)
RawValueExpr::MacroInvocation(invocation)
}
}
}
Expand All @@ -233,12 +233,12 @@ impl<'data> From<LazyRawValueExpr<'data, BinaryEncoding_1_0>>
{
fn from(value: LazyRawValueExpr<'data, BinaryEncoding_1_0>) -> Self {
match value {
LazyRawValueExpr::ValueLiteral(v) => LazyRawValueExpr::ValueLiteral(v.into()),
LazyRawValueExpr::MacroInvocation(m) => {
RawValueExpr::ValueLiteral(v) => RawValueExpr::ValueLiteral(v.into()),
RawValueExpr::MacroInvocation(m) => {
let invocation = LazyRawAnyMacroInvocation {
encoding: LazyRawAnyMacroInvocationKind::Binary_1_0(m),
};
LazyRawValueExpr::MacroInvocation(invocation)
RawValueExpr::MacroInvocation(invocation)
}
}
}
Expand All @@ -249,12 +249,12 @@ impl<'data> From<LazyRawValueExpr<'data, TextEncoding_1_1>>
{
fn from(value: LazyRawValueExpr<'data, TextEncoding_1_1>) -> Self {
match value {
LazyRawValueExpr::ValueLiteral(v) => LazyRawValueExpr::ValueLiteral(v.into()),
LazyRawValueExpr::MacroInvocation(m) => {
RawValueExpr::ValueLiteral(v) => RawValueExpr::ValueLiteral(v.into()),
RawValueExpr::MacroInvocation(m) => {
let invocation = LazyRawAnyMacroInvocation {
encoding: LazyRawAnyMacroInvocationKind::Text_1_1(m),
};
LazyRawValueExpr::MacroInvocation(invocation)
RawValueExpr::MacroInvocation(invocation)
}
}
}
Expand Down Expand Up @@ -731,11 +731,13 @@ impl<'data> From<LazyRawFieldExpr<'data, TextEncoding_1_0>>
{
fn from(text_field: LazyRawFieldExpr<'data, TextEncoding_1_0>) -> Self {
let (name, value) = match text_field {
LazyRawFieldExpr::NameValuePair(name, value) => (name, value),
LazyRawFieldExpr::MacroInvocation(_) => unreachable!("macro invocation in Ion 1.0"),
RawFieldExpr::NameValuePair(name, value) => (name, value),
RawFieldExpr::MacroInvocation(_) => {
unreachable!("macro invocation in Ion 1.0")
}
};
// Convert the text-encoded value into an any-encoded value
LazyRawFieldExpr::NameValuePair(name, value.into())
RawFieldExpr::NameValuePair(name, value.into())
}
}

Expand All @@ -744,20 +746,22 @@ impl<'data> From<LazyRawFieldExpr<'data, BinaryEncoding_1_0>>
{
fn from(binary_field: LazyRawFieldExpr<'data, BinaryEncoding_1_0>) -> Self {
let (name, value) = match binary_field {
LazyRawFieldExpr::NameValuePair(name, value) => (name, value),
LazyRawFieldExpr::MacroInvocation(_) => unreachable!("macro invocation in Ion 1.0"),
RawFieldExpr::NameValuePair(name, value) => (name, value),
RawFieldExpr::MacroInvocation(_) => {
unreachable!("macro invocation in Ion 1.0")
}
};
// Convert the binary-encoded value into an any-encoded value
LazyRawFieldExpr::NameValuePair(name, value.into())
RawFieldExpr::NameValuePair(name, value.into())
}
}

impl<'data> From<LazyRawFieldExpr<'data, TextEncoding_1_1>>
for LazyRawFieldExpr<'data, AnyEncoding>
{
fn from(text_field: LazyRawFieldExpr<'data, TextEncoding_1_1>) -> Self {
use LazyRawFieldExpr::{MacroInvocation as FieldMacroInvocation, NameValuePair};
use LazyRawValueExpr::{MacroInvocation as ValueMacroInvocation, ValueLiteral};
use RawFieldExpr::{MacroInvocation as FieldMacroInvocation, NameValuePair};
use RawValueExpr::{MacroInvocation as ValueMacroInvocation, ValueLiteral};
match text_field {
NameValuePair(name, ValueLiteral(value)) => {
NameValuePair(name, ValueLiteral(value.into()))
Expand Down
4 changes: 2 additions & 2 deletions src/lazy/binary/raw/sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::lazy::binary::raw::annotations_iterator::RawBinaryAnnotationsIterator
use crate::lazy::binary::raw::reader::DataSource;
use crate::lazy::binary::raw::value::LazyRawBinaryValue;
use crate::lazy::decoder::private::LazyContainerPrivate;
use crate::lazy::decoder::{LazyRawSequence, LazyRawValueExpr};
use crate::lazy::decoder::{LazyRawSequence, LazyRawValueExpr, RawValueExpr};
use crate::lazy::encoding::BinaryEncoding_1_0;
use crate::{IonResult, IonType};
use std::fmt::{Debug, Formatter};
Expand Down Expand Up @@ -145,7 +145,7 @@ impl<'data> Iterator for RawBinarySequenceIterator<'data> {
.source
.try_parse_next(ImmutableBuffer::peek_sequence_value)
{
Ok(Some(value)) => Some(Ok(LazyRawValueExpr::ValueLiteral(value))),
Ok(Some(value)) => Some(Ok(RawValueExpr::ValueLiteral(value))),
Ok(None) => None,
Err(e) => Some(Err(e)),
}
Expand Down
8 changes: 5 additions & 3 deletions src/lazy/binary/raw/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use crate::lazy::binary::raw::value::LazyRawBinaryValue;
use crate::lazy::decoder::private::{
LazyContainerPrivate, LazyRawFieldPrivate, LazyRawValuePrivate,
};
use crate::lazy::decoder::{LazyRawField, LazyRawFieldExpr, LazyRawStruct, LazyRawValueExpr};
use crate::lazy::decoder::{
LazyRawField, LazyRawFieldExpr, LazyRawStruct, RawFieldExpr, RawValueExpr,
};
use crate::lazy::encoding::BinaryEncoding_1_0;
use crate::{IonResult, RawSymbolTokenRef};

Expand Down Expand Up @@ -87,9 +89,9 @@ impl<'data> Iterator for RawBinaryStructIterator<'data> {

fn next(&mut self) -> Option<Self::Item> {
match self.source.try_parse_next(ImmutableBuffer::peek_field) {
Ok(Some(lazy_raw_value)) => Some(Ok(LazyRawFieldExpr::NameValuePair(
Ok(Some(lazy_raw_value)) => Some(Ok(RawFieldExpr::NameValuePair(
lazy_raw_value.field_name().unwrap(),
LazyRawValueExpr::ValueLiteral(lazy_raw_value),
RawValueExpr::ValueLiteral(lazy_raw_value),
))),
Ok(None) => None,
Err(e) => Some(Err(e)),
Expand Down
84 changes: 53 additions & 31 deletions src/lazy/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,35 +35,45 @@ where
type MacroInvocation: MacroInvocation<'data, Self>;
}

/// An item found in value position within an Ion data stream.
/// This item may be either a value literal or a macro invocation.
/// An expression found in value position in either serialized Ion or a template.
/// If it is a value literal, it is considered a stream with exactly one Ion value.
/// If it is a macro invocation, it is a stream with zero or more Ion values.
#[derive(Copy, Clone, Debug)]
pub enum LazyRawValueExpr<'data, D: LazyDecoder<'data>> {
/// A text Ion 1.1 value literal. For example: `5`, `foo`, or `"hello"`
ValueLiteral(D::Value),
/// A text Ion 1.1 macro invocation. For example: `(:employee 12345 "Sarah" "Gonzalez")`
MacroInvocation(D::MacroInvocation),
pub enum RawValueExpr<V, M> {
/// A value literal. For example: `5`, `foo`, or `"hello"` in text.
ValueLiteral(V),
/// An Ion 1.1+ macro invocation. For example: `(:employee 12345 "Sarah" "Gonzalez")` in text.
MacroInvocation(M),
}

impl<'data, D: LazyDecoder<'data>> LazyRawValueExpr<'data, D> {
pub fn expect_value(self) -> IonResult<D::Value> {
// `RawValueExpr` above has no ties to a particular encoding. The `LazyRawValueExpr` type alias
// below uses the `Value` and `MacroInvocation` associated types from the decoder `D`. In most
// places, this is a helpful constraint; we can talk about the value expression in terms of the
// LazyDecoder it's associated with. However, in some places (primarily when expanding template
// values that don't have a LazyDecoder) we need to be able to use it without constraints.
Comment on lines +49 to +53
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be moved to the doc comments of one or both of RawValueExpr and LazyRawValueExpr since it's the best explanation of why you would use one vs the other.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call, I'll add this to #652.


/// An item found in value position within an Ion data stream written in the encoding represented
/// by the LazyDecoder `D`. This item may be either a value literal or a macro invocation.
pub type LazyRawValueExpr<'data, D> =
RawValueExpr<<D as LazyDecoder<'data>>::Value, <D as LazyDecoder<'data>>::MacroInvocation>;

impl<V: Debug, M: Debug> RawValueExpr<V, M> {
pub fn expect_value(self) -> IonResult<V> {
match self {
LazyRawValueExpr::ValueLiteral(v) => Ok(v),
LazyRawValueExpr::MacroInvocation(_m) => {
IonResult::decoding_error("expected a value literal, but found macro invocation")
}
RawValueExpr::ValueLiteral(v) => Ok(v),
RawValueExpr::MacroInvocation(_m) => IonResult::decoding_error(
"expected a value literal, but found a macro invocation ({:?})",
),
}
}

pub fn expect_macro(self) -> IonResult<D::MacroInvocation> {
pub fn expect_macro(self) -> IonResult<M> {
match self {
LazyRawValueExpr::ValueLiteral(v) => IonResult::decoding_error(format!(
"expected a macro invocation but found a value literal {:?}",
RawValueExpr::ValueLiteral(v) => IonResult::decoding_error(format!(
"expected a macro invocation but found a value literal ({:?})",
v
)),
LazyRawValueExpr::MacroInvocation(m) => Ok(m),
RawValueExpr::MacroInvocation(m) => Ok(m),
}
}
}
Expand All @@ -74,15 +84,27 @@ impl<'data, D: LazyDecoder<'data>> LazyRawValueExpr<'data, D> {
/// * a name/e-expression pair
/// * an e-expression
#[derive(Debug)]
pub enum LazyRawFieldExpr<'data, D: LazyDecoder<'data>> {
NameValuePair(RawSymbolTokenRef<'data>, LazyRawValueExpr<'data, D>),
MacroInvocation(D::MacroInvocation),
pub enum RawFieldExpr<'name, V, M> {
NameValuePair(RawSymbolTokenRef<'name>, RawValueExpr<V, M>),
MacroInvocation(M),
}

impl<'data, D: LazyDecoder<'data>> LazyRawFieldExpr<'data, D> {
pub fn expect_name_value(self) -> IonResult<(RawSymbolTokenRef<'data>, D::Value)> {
// As with the `RawValueExpr`/`LazyRawValueExpr` type pair, a `RawFieldExpr` has no constraints
// on the types used for values or macros, while the `LazyRawFieldExpr` type alias below uses the
// value and macro types associated with the decoder `D`.

/// An item found in struct field position an Ion data stream written in the encoding represented
/// by the LazyDecoder `D`.
pub type LazyRawFieldExpr<'data, D> = RawFieldExpr<
'data,
<D as LazyDecoder<'data>>::Value,
<D as LazyDecoder<'data>>::MacroInvocation,
>;

impl<'name, V: Debug, M: Debug> RawFieldExpr<'name, V, M> {
pub fn expect_name_value(self) -> IonResult<(RawSymbolTokenRef<'name>, V)> {
match self {
LazyRawFieldExpr::NameValuePair(name, LazyRawValueExpr::ValueLiteral(value)) => {
RawFieldExpr::NameValuePair(name, RawValueExpr::ValueLiteral(value)) => {
Ok((name, value))
}
_ => IonResult::decoding_error(format!(
Expand All @@ -92,22 +114,21 @@ impl<'data, D: LazyDecoder<'data>> LazyRawFieldExpr<'data, D> {
}
}

pub fn expect_name_macro(self) -> IonResult<(RawSymbolTokenRef<'data>, D::MacroInvocation)> {
pub fn expect_name_macro(self) -> IonResult<(RawSymbolTokenRef<'name>, M)> {
match self {
LazyRawFieldExpr::NameValuePair(
name,
LazyRawValueExpr::MacroInvocation(invocation),
) => Ok((name, invocation)),
RawFieldExpr::NameValuePair(name, RawValueExpr::MacroInvocation(invocation)) => {
Ok((name, invocation))
}
_ => IonResult::decoding_error(format!(
"expected a name/macro pair but found {:?}",
self
)),
}
}

pub fn expect_macro(self) -> IonResult<D::MacroInvocation> {
pub fn expect_macro(self) -> IonResult<M> {
match self {
LazyRawFieldExpr::MacroInvocation(invocation) => Ok(invocation),
RawFieldExpr::MacroInvocation(invocation) => Ok(invocation),
_ => IonResult::decoding_error(format!(
"expected a macro invocation but found {:?}",
self
Expand All @@ -125,6 +146,7 @@ impl<'data, D: LazyDecoder<'data>> LazyRawFieldExpr<'data, D> {
// internal code that is defined in terms of `LazyRawField` to call the private `into_value()`
// function while also preventing users from seeing or depending on it.
pub(crate) mod private {
use crate::lazy::encoding::RawValueLiteral;
use crate::{IonResult, RawSymbolTokenRef};

use super::LazyDecoder;
Expand All @@ -143,7 +165,7 @@ pub(crate) mod private {
fn from_value(value: D::Value) -> Self;
}

pub trait LazyRawValuePrivate<'data> {
pub trait LazyRawValuePrivate<'data>: RawValueLiteral {
/// Returns the field name associated with this value. If the value is not inside a struct,
/// returns `IllegalOperation`.
fn field_name(&self) -> IonResult<RawSymbolTokenRef<'data>>;
Expand Down
19 changes: 19 additions & 0 deletions src/lazy/encoding.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![allow(non_camel_case_types)]

use crate::lazy::any_encoding::LazyRawAnyValue;
use crate::lazy::binary::raw::annotations_iterator::RawBinaryAnnotationsIterator;
use crate::lazy::binary::raw::r#struct::LazyRawBinaryStruct;
use crate::lazy::binary::raw::reader::LazyRawBinaryReader;
Expand Down Expand Up @@ -131,3 +132,21 @@ impl<'data> LazyDecoder<'data> for TextEncoding_1_1 {
type AnnotationsIterator = RawTextAnnotationsIterator<'data>;
type MacroInvocation = RawTextMacroInvocation<'data>;
}

/// Marker trait for types that represent value literals in an Ion stream of some encoding.
// This trait is used to provide generic conversion implementation of types used as a
// `LazyDecoder::Value` to `ExpandedValueSource`. That is:
//
// impl<'top, 'data, V: RawValueLiteral, D: LazyDecoder<'data, Value = V>> From<V>
// for ExpandedValueSource<'top, 'data, D>
//
// If we do not confine the implementation to types with a marker trait, rustc complains that
// someone may someday use `ExpandedValueSource` as a `LazyDecoder::Value`, and then the
// the implementation will conflict with the core `impl<T> From<T> for T` implementation.
pub trait RawValueLiteral {}

impl<'data> RawValueLiteral for MatchedRawTextValue<'data> {}
impl<'data> RawValueLiteral for LazyRawTextValue_1_0<'data> {}
impl<'data> RawValueLiteral for LazyRawTextValue_1_1<'data> {}
impl<'data> RawValueLiteral for LazyRawBinaryValue<'data> {}
impl<'data> RawValueLiteral for LazyRawAnyValue<'data> {}
16 changes: 6 additions & 10 deletions src/lazy/expanded/e_expression.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Types and traits representing an e-expression in an Ion stream.

use crate::lazy::decoder::{LazyDecoder, LazyRawValueExpr};
use crate::lazy::decoder::{LazyDecoder, LazyRawValueExpr, RawValueExpr};
use crate::lazy::expanded::macro_evaluator::{ArgumentKind, ToArgumentKind};
use crate::lazy::expanded::{EncodingContext, ExpandedValueSource, LazyExpandedValue};

Expand All @@ -21,15 +21,11 @@ impl<'data, D: LazyDecoder<'data>> ToArgumentKind<'data, D, D::MacroInvocation>
// Because e-expressions appear in the data stream (and not in a template), there is no
// environment of named variables. We do not attempt to resolve symbols as though they
// were variable names and instead pass them along as value literals.
LazyRawValueExpr::ValueLiteral(value) => {
ArgumentKind::ValueLiteral(LazyExpandedValue {
context,
source: ExpandedValueSource::ValueLiteral(value),
})
}
LazyRawValueExpr::MacroInvocation(invocation) => {
ArgumentKind::MacroInvocation(invocation)
}
RawValueExpr::ValueLiteral(value) => ArgumentKind::ValueLiteral(LazyExpandedValue {
context,
source: ExpandedValueSource::ValueLiteral(value),
}),
RawValueExpr::MacroInvocation(invocation) => ArgumentKind::MacroInvocation(invocation),
}
}
}
Loading