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 make_struct #876

Merged
merged 2 commits into from
Dec 11, 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
6 changes: 5 additions & 1 deletion src/lazy/expanded/e_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ use crate::lazy::expanded::compiler::{ExpansionAnalysis, ExpansionSingleton};
use crate::lazy::expanded::macro_evaluator::{
AnnotateExpansion, ConditionalExpansion, EExpressionArgGroup, ExprGroupExpansion,
FlattenExpansion, IsExhaustedIterator, MacroExpansion, MacroExpansionKind, MacroExpr,
MacroExprArgsIterator, MakeTextExpansion, RawEExpression, TemplateExpansion, ValueExpr,
MacroExprArgsIterator, MakeStructExpansion, MakeTextExpansion, RawEExpression,
TemplateExpansion, ValueExpr,
};
use crate::lazy::expanded::macro_table::{MacroKind, MacroRef};
use crate::lazy::expanded::template::TemplateMacroRef;
Expand Down Expand Up @@ -123,6 +124,9 @@ impl<'top, D: Decoder> EExpression<'top, D> {
MacroKind::MakeSymbol => {
MacroExpansionKind::MakeSymbol(MakeTextExpansion::symbol_maker(arguments))
}
MacroKind::MakeStruct => {
MacroExpansionKind::MakeStruct(MakeStructExpansion::new(arguments))
}
MacroKind::Annotate => MacroExpansionKind::Annotate(AnnotateExpansion::new(arguments)),
MacroKind::Flatten => MacroExpansionKind::Flatten(FlattenExpansion::new(
self.context,
Expand Down
63 changes: 61 additions & 2 deletions src/lazy/expanded/macro_evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ use crate::lazy::text::raw::v1_1::arg_group::EExpArg;
use crate::lazy::text::raw::v1_1::reader::MacroIdRef;
use crate::result::IonFailure;
use crate::{
ExpandedValueRef, ExpandedValueSource, IonError, IonResult, LazyValue, Span, SymbolRef,
ValueRef,
ExpandedValueRef, ExpandedValueSource, IonError, IonResult, LazyExpandedStruct, LazyStruct,
LazyValue, Span, SymbolRef, ValueRef,
};

pub trait IsExhaustedIterator<'top, D: Decoder>:
Expand Down Expand Up @@ -459,6 +459,7 @@ pub enum MacroExpansionKind<'top, D: Decoder> {
ExprGroup(ExprGroupExpansion<'top, D>),
MakeString(MakeTextExpansion<'top, D>),
MakeSymbol(MakeTextExpansion<'top, D>),
MakeStruct(MakeStructExpansion<'top, D>),
Annotate(AnnotateExpansion<'top, D>),
Flatten(FlattenExpansion<'top, D>),
Template(TemplateExpansion<'top>),
Expand Down Expand Up @@ -540,6 +541,7 @@ impl<'top, D: Decoder> MacroExpansion<'top, D> {
ExprGroup(expr_group_expansion) => expr_group_expansion.next(context, environment),
MakeString(make_string_expansion) => make_string_expansion.make_text_value(context),
MakeSymbol(make_symbol_expansion) => make_symbol_expansion.make_text_value(context),
MakeStruct(make_struct_expansion) => make_struct_expansion.next(context, environment),
Annotate(annotate_expansion) => annotate_expansion.next(context, environment),
Flatten(flatten_expansion) => flatten_expansion.next(),
Conditional(cardiality_test_expansion) => cardiality_test_expansion.next(environment),
Expand All @@ -556,6 +558,7 @@ impl<D: Decoder> Debug for MacroExpansion<'_, D> {
MacroExpansionKind::ExprGroup(_) => "[internal] expr_group",
MacroExpansionKind::MakeString(_) => "make_string",
MacroExpansionKind::MakeSymbol(_) => "make_symbol",
MacroExpansionKind::MakeStruct(_) => "make_struct",
MacroExpansionKind::Annotate(_) => "annotate",
MacroExpansionKind::Flatten(_) => "flatten",
MacroExpansionKind::Conditional(test) => test.name(),
Expand Down Expand Up @@ -1112,6 +1115,40 @@ impl<'top, D: Decoder> ConditionalExpansion<'top, D> {
}
}

// ===== Implementation of the `make_struct` macro =====
#[derive(Copy, Clone, Debug)]
pub struct MakeStructExpansion<'top, D: Decoder> {
arguments: MacroExprArgsIterator<'top, D>,
}

impl<'top, D: Decoder> MakeStructExpansion<'top, D> {
pub fn new(arguments: MacroExprArgsIterator<'top, D>) -> Self {
Self { arguments }
}

fn next(
&mut self,
context: EncodingContextRef<'top>,
environment: Environment<'top, D>,
) -> IonResult<MacroExpansionStep<'top, D>> {
// The `make_struct` macro always produces a single struct value. When `next()` is called
// to begin its evaluation, it immediately returns a lazy value representing the (not yet
// computed) struct. If/when the application tries to iterate over its fields,
// the iterator will evaluate the field expressions incrementally.
let lazy_expanded_struct =
LazyExpandedStruct::from_constructed(context, environment, self.arguments);
let lazy_struct = LazyStruct::new(lazy_expanded_struct);
// Store the `Struct` in the bump so it's guaranteed to be around as long as the reader is
// positioned on this top-level value.
let value_ref = context
.allocator()
.alloc_with(|| ValueRef::Struct(lazy_struct));
let lazy_expanded_value = LazyExpandedValue::from_constructed(context, &[], value_ref);
Ok(MacroExpansionStep::FinalStep(Some(
ValueExpr::ValueLiteral(lazy_expanded_value),
)))
}
}
// ===== Implementation of the `make_string` macro =====

#[derive(Copy, Clone, Debug)]
Expand Down Expand Up @@ -2788,6 +2825,28 @@ mod tests {
)
}

#[test]
fn make_struct_eexp() -> IonResult<()> {
stream_eq(
r#"
(:make_struct)
(:make_struct {a: 1})
(:make_struct {a: 1} {})
(:make_struct {a: 1} {b: 2})
(:make_struct {a: 1} {b: 2} {a: 3, d: 4})
(:make_struct {a: 1} {b: 2} {a: 3, d: 4} (:make_struct {e: 5} {f: 6}))
"#,
r#"
{}
{a: 1}
{a: 1}
{a: 1, b: 2}
{a: 1, b: 2, a: 3, d: 4}
{a: 1, b: 2, a: 3, d: 4, e: 5, f: 6}
"#,
)
}

#[test]
fn make_string_tdl_macro_invocation() -> IonResult<()> {
let invocation = r#"
Expand Down
3 changes: 2 additions & 1 deletion src/lazy/expanded/macro_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ pub enum MacroKind {
ExprGroup,
MakeString,
MakeSymbol,
MakeStruct,
Annotate,
Flatten,
Template(TemplateBody),
Expand Down Expand Up @@ -317,7 +318,7 @@ impl MacroTable {
builtin(
"make_struct",
"(fields*)",
MacroKind::ToDo,
MacroKind::MakeStruct,
ExpansionAnalysis::single_application_value(IonType::Struct),
),
template(
Expand Down
6 changes: 6 additions & 0 deletions src/lazy/expanded/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,12 @@ pub enum ExpandedAnnotationsSource<'top, Encoding: Decoder> {
Constructed(std::slice::Iter<'top, SymbolRef<'top>>),
}

impl<Encoding: Decoder> ExpandedAnnotationsSource<'_, Encoding> {
pub fn empty() -> Self {
Self::Constructed(std::slice::Iter::default())
}
}

pub struct ExpandedAnnotationsIterator<'top, Encoding: Decoder> {
source: ExpandedAnnotationsSource<'top, Encoding>,
}
Expand Down
119 changes: 104 additions & 15 deletions src/lazy/expanded/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::element::iterators::SymbolsIterator;
use crate::lazy::decoder::private::{LazyRawStructPrivate, RawStructUnexpandedFieldsIterator};
use crate::lazy::decoder::{Decoder, LazyRawFieldName, LazyRawStruct};
use crate::lazy::expanded::macro_evaluator::{
MacroEvaluator, MacroExpansion, MacroExpr, ValueExpr,
MacroEvaluator, MacroExpansion, MacroExpr, MacroExprArgsIterator, ValueExpr,
};
use crate::lazy::expanded::sequence::Environment;
use crate::lazy::expanded::template::{
Expand All @@ -13,7 +13,7 @@ use crate::lazy::expanded::{
LazyExpandedValue,
};
use crate::result::IonFailure;
use crate::{try_or_some_err, IonResult, RawSymbolRef, SymbolRef};
use crate::{try_next, try_or_some_err, IonResult, RawSymbolRef, SymbolRef};

/// A unified type embodying all possible field representations coming from both input data
/// (i.e. raw structs of some encoding) and template bodies.
Expand Down Expand Up @@ -48,6 +48,10 @@ impl<'top, D: Decoder> LazyExpandedField<'top, D> {
pub fn name(&self) -> LazyExpandedFieldName<'top, D> {
self.name
}

pub fn unexpanded(&self) -> UnexpandedField<'top, D> {
UnexpandedField::NameValue(self.name(), self.value())
}
}

impl<'top, D: Decoder> LazyExpandedField<'top, D> {
Expand Down Expand Up @@ -107,14 +111,16 @@ pub enum ExpandedStructSource<'top, D: Decoder> {
TemplateElement<'top>,
&'top TemplateStructIndex,
),
// TODO: Constructed
// The struct was produced by the `make_struct` macro.
Constructed(Environment<'top, D>, MacroExprArgsIterator<'top, D>),
}

impl<'top, D: Decoder> ExpandedStructSource<'top, D> {
fn environment(&self) -> Environment<'top, D> {
match self {
ExpandedStructSource::ValueLiteral(_) => Environment::empty(),
ExpandedStructSource::Template(environment, _, _) => *environment,
ExpandedStructSource::Constructed(environment, _) => *environment,
}
}
}
Expand Down Expand Up @@ -154,18 +160,27 @@ impl<'top, D: Decoder> LazyExpandedStruct<'top, D> {
Self { source, context }
}

pub fn from_constructed(
context: EncodingContextRef<'top>,
environment: Environment<'top, D>,
arguments: MacroExprArgsIterator<'top, D>,
) -> LazyExpandedStruct<'top, D> {
let source = ExpandedStructSource::Constructed(environment, arguments);
Self { source, context }
}

pub fn annotations(&self) -> ExpandedAnnotationsIterator<'top, D> {
match &self.source {
ExpandedStructSource::ValueLiteral(value) => ExpandedAnnotationsIterator {
source: ExpandedAnnotationsSource::ValueLiteral(value.annotations()),
},
let iter_source = match &self.source {
ExpandedStructSource::ValueLiteral(value) => {
ExpandedAnnotationsSource::ValueLiteral(value.annotations())
}
ExpandedStructSource::Template(_environment, element, _index) => {
let annotations = element.annotations();
ExpandedAnnotationsIterator {
source: ExpandedAnnotationsSource::Template(SymbolsIterator::new(annotations)),
}
ExpandedAnnotationsSource::Template(SymbolsIterator::new(annotations))
}
}
ExpandedStructSource::Constructed(_, _) => ExpandedAnnotationsSource::empty(),
};
ExpandedAnnotationsIterator::new(iter_source)
}

pub fn iter(&self) -> ExpandedStructIterator<'top, D> {
Expand Down Expand Up @@ -197,6 +212,18 @@ impl<'top, D: Decoder> LazyExpandedStruct<'top, D> {
),
)
}
ExpandedStructSource::Constructed(environment, arguments) => {
let evaluator = self
.context
.allocator()
.alloc_with(|| MacroEvaluator::new_with_environment(*environment));
let current_struct_iter = self.context.allocator().alloc_with(|| None);
ExpandedStructIteratorSource::Constructed(
evaluator,
current_struct_iter,
*arguments,
)
}
};
ExpandedStructIterator {
source,
Expand All @@ -214,9 +241,9 @@ impl<'top, D: Decoder> LazyExpandedStruct<'top, D> {

pub fn find(&self, name: &str) -> IonResult<Option<LazyExpandedValue<'top, D>>> {
match &self.source {
// If we're reading from a struct literal, do a linear scan over its fields until we
// encounter one with the requested name.
ExpandedStructSource::ValueLiteral(_) => {
// If we're reading from a struct literal or `make_struct` call, do a linear scan over
// its fields until we encounter one with the requested name.
ExpandedStructSource::ValueLiteral(_) | ExpandedStructSource::Constructed(_, _) => {
for field_result in self.iter() {
let field = field_result?;
if field.name().read()?.text() == Some(name) {
Expand Down Expand Up @@ -287,7 +314,14 @@ pub enum ExpandedStructIteratorSource<'top, D: Decoder> {
&'top mut MacroEvaluator<'top, D>,
TemplateStructUnexpandedFieldsIterator<'top, D>,
),
// TODO: Constructed
Constructed(
&'top mut MacroEvaluator<'top, D>,
// This is `&mut Option<_>` instead of `Option<&mut _>` so we can re-use the allocated space
// for each iterator we traverse.
&'top mut Option<ExpandedStructIterator<'top, D>>,
// Remaining argument expressions
MacroExprArgsIterator<'top, D>,
),
}

impl<'top, D: Decoder> ExpandedStructIteratorSource<'top, D> {
Expand All @@ -300,13 +334,68 @@ impl<'top, D: Decoder> ExpandedStructIteratorSource<'top, D> {
ExpandedStructIteratorSource::ValueLiteral(_, raw_struct_iter) => {
raw_struct_iter.next()
}
ExpandedStructIteratorSource::Constructed(
evaluator,
maybe_current_struct,
arguments,
) => {
loop {
// If we're already traversing a struct, see if it has any fields remaining.
if let Some(current_struct) = maybe_current_struct {
match current_struct.next() {
// If we get a field, we're done.
Some(Ok(field)) => return Some(Ok(field.unexpanded())),
Some(Err(e)) => return Some(Err(e)),
// If we get `None`, the iterator is exhausted and we should continue on to the next struct.
None => **maybe_current_struct = None,
}
}

// If we reach this point, we don't have a current struct.
// We've either just started evaluation and haven't set one yet or
// we just finished inlining a struct and need to set a new one.

// See if the evaluator has an expansion in progress.
let mut next_struct = try_or_some_err!(evaluator.next());
if next_struct.is_none() {
// If we don't get anything from the evaluator, we'll get our struct from the
// next argument expression. If there isn't a next argument expression,
// then evaluation is complete.
next_struct = match try_next!(arguments.next()) {
// If the expression is a value literal, that's our new sequence.
ValueExpr::ValueLiteral(value) => Some(value),
// If the expression is a macro invocation, we'll start evaluating it
// and return to the top of the loop.
ValueExpr::MacroInvocation(invocation) => {
evaluator.push(try_or_some_err!(invocation.expand()));
continue;
}
}
}

// At this point, `next_struct` is definitely populated, so we can safely unwrap it.
let next_struct = next_struct.unwrap();
// Set it as our new current struct.
let ExpandedValueRef::Struct(next_struct) =
try_or_some_err!(next_struct.read())
else {
return Some(IonResult::decoding_error(format!(
"`make_struct` only accepts structs, received {next_struct:?}"
)));
};
**maybe_current_struct = Some(next_struct.iter());
}
}
}
}

fn evaluator(&mut self) -> &mut MacroEvaluator<'top, D> {
// TODO: Should the evaluator be lifted out of the iterator source?
// It is common to all of the variants.
match self {
ExpandedStructIteratorSource::Template(evaluator, _) => evaluator,
ExpandedStructIteratorSource::ValueLiteral(evaluator, _) => evaluator,
ExpandedStructIteratorSource::Constructed(evaluator, _, _) => evaluator,
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/lazy/expanded/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use compact_str::CompactString;
use crate::lazy::binary::raw::v1_1::immutable_buffer::ArgGroupingBitmap;
use crate::lazy::decoder::Decoder;
use crate::lazy::expanded::compiler::ExpansionAnalysis;
use crate::lazy::expanded::macro_evaluator::{AnnotateExpansion, MacroEvaluator, MacroExpansion, MacroExpansionKind, MacroExpr, MacroExprArgsIterator, TemplateExpansion, ValueExpr, ExprGroupExpansion, MakeTextExpansion, FlattenExpansion, ConditionalExpansion};
use crate::lazy::expanded::macro_evaluator::{AnnotateExpansion, MacroEvaluator, MacroExpansion, MacroExpansionKind, MacroExpr, MacroExprArgsIterator, TemplateExpansion, ValueExpr, ExprGroupExpansion, MakeTextExpansion, FlattenExpansion, ConditionalExpansion, MakeStructExpansion};
use crate::lazy::expanded::macro_table::{Macro, MacroKind};
use crate::lazy::expanded::r#struct::UnexpandedField;
use crate::lazy::expanded::sequence::Environment;
Expand Down Expand Up @@ -1221,6 +1221,9 @@ impl<'top, D: Decoder> TemplateMacroInvocation<'top, D> {
MacroKind::MakeSymbol => {
MacroExpansionKind::MakeSymbol(MakeTextExpansion::symbol_maker(arguments))
}
MacroKind::MakeStruct => {
MacroExpansionKind::MakeStruct(MakeStructExpansion::new(arguments))
}
MacroKind::Annotate => MacroExpansionKind::Annotate(AnnotateExpansion::new(arguments)),
MacroKind::Flatten => MacroExpansionKind::Flatten(FlattenExpansion::new(self.context(), self.environment, arguments)),
MacroKind::Template(template_body) => {
Expand Down
10 changes: 8 additions & 2 deletions src/lazy/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,18 @@ impl<'top, D: Decoder> LazyStruct<'top, D> {
}

pub fn as_value(&self) -> LazyValue<'top, D> {
let context = self.expanded_struct.context;
let expanded_value = match self.expanded_struct.source {
ExpandedStructSource::ValueLiteral(v) => {
LazyExpandedValue::from_literal(self.expanded_struct.context, v.as_value())
LazyExpandedValue::from_literal(context, v.as_value())
}
ExpandedStructSource::Template(env, element, _) => {
LazyExpandedValue::from_template(self.expanded_struct.context, env, element)
LazyExpandedValue::from_template(context, env, element)
}
ExpandedStructSource::Constructed(_env, _args) => {
let value_ref = context.allocator().alloc_with(|| ValueRef::Struct(*self));
let annotations = &[];
LazyExpandedValue::from_constructed(context, annotations, value_ref)
}
};
LazyValue::new(expanded_value)
Expand Down
Loading