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

Container tooling #885

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
10 changes: 10 additions & 0 deletions src/lazy/any_encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ use crate::lazy::text::value::{
LazyRawTextVersionMarker_1_1, RawTextAnnotationsIterator,
};
use crate::symbol_table::{SystemSymbolTable, SYSTEM_SYMBOLS_1_0, SYSTEM_SYMBOLS_1_1};
use crate::LazyRawValueKind::{Binary_1_0, Binary_1_1, Text_1_0, Text_1_1};
use crate::{try_next, Encoding, IonResult, IonType, RawStreamItem, RawSymbolRef};

/// An implementation of the `LazyDecoder` trait that can read any encoding of Ion.
Expand Down Expand Up @@ -1047,6 +1048,15 @@ impl<'top> LazyRawValue<'top, AnyEncoding> for LazyRawAnyValue<'top> {
}
}

fn is_delimited(&self) -> bool {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🪧 Each encoded value kind can now report whether it is delimited. For text 1.0 and binary 1.0, this method just returns false.

match &self.encoding {
Text_1_0(v) => v.is_delimited(),
Binary_1_0(v) => v.is_delimited(),
Text_1_1(v) => v.is_delimited(),
Binary_1_1(v) => v.is_delimited(),
}
}

fn has_annotations(&self) -> bool {
use LazyRawValueKind::*;
match &self.encoding {
Expand Down
4 changes: 4 additions & 0 deletions src/lazy/binary/raw/v1_1/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ impl<'top> LazyRawValue<'top, BinaryEncoding_1_1> for &'top LazyRawBinaryValue_1
self.encoded_value.header().is_null()
}

fn is_delimited(&self) -> bool {
!self.delimited_contents.is_none()
}

fn has_annotations(&self) -> bool {
self.encoded_value.has_annotations()
}
Expand Down
17 changes: 17 additions & 0 deletions src/lazy/binary/raw/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ impl<'top> LazyRawValue<'top, BinaryEncoding_1_0> for LazyRawBinaryValue_1_0<'to
self.is_null()
}

fn is_delimited(&self) -> bool {
false
}

fn has_annotations(&self) -> bool {
self.has_annotations()
}
Expand Down Expand Up @@ -221,6 +225,19 @@ pub trait EncodedBinaryValue<'top, D: Decoder>: LazyRawValue<'top, D> {
let body_bytes = &value_span.bytes()[value_span.len() - body_length..];
Span::with_offset(value_span.range().end - body_length, body_bytes)
}

fn delimited_end_span(&self) -> Span<'top> {
let bytes = self.span().bytes();
let end = bytes.len();
let range = if !self.is_delimited() {
end..end
} else {
debug_assert!(bytes[end - 1] == 0xF0);
end - 1..end
};
let end_bytes = bytes.get(range).unwrap();
Span::with_offset(self.range().end - end_bytes.len(), end_bytes)
}
Comment on lines +229 to +240
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🪧 For scalars and length-prefixed containers, this returns an empty slice.

}

impl<'top> EncodedBinaryValue<'top, BinaryEncoding_1_0> for LazyRawBinaryValue_1_0<'top> {
Expand Down
57 changes: 36 additions & 21 deletions src/lazy/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ use crate::lazy::streaming_raw_reader::RawReaderState;
use crate::read_config::ReadConfig;
use crate::result::IonFailure;
use crate::{
v1_0, v1_1, Catalog, Encoding, IonResult, IonType, LazyExpandedFieldName, LazyExpandedValue,
LazyRawWriter, MacroExpr, RawSymbolRef, ValueExpr, ValueRef,
v1_0, v1_1, Catalog, Encoding, FieldExpr, IonResult, IonType, LazyExpandedFieldName,
LazyExpandedValue, LazyRawAnyFieldName, LazyRawWriter, MacroExpr, RawSymbolRef, ValueExpr,
ValueRef,
};

pub trait HasSpan<'top>: HasRange {
Expand Down Expand Up @@ -257,6 +258,21 @@ pub enum LazyRawFieldExpr<'top, D: Decoder> {
}

impl<'top, D: Decoder> LazyRawFieldExpr<'top, D> {
pub fn resolve(self, context: EncodingContextRef<'top>) -> IonResult<FieldExpr<'top, D>> {
use LazyRawFieldExpr::*;
let field = match self {
NameValue(name, value) => FieldExpr::NameValue(
name.resolve(context),
LazyExpandedValue::from_literal(context, value),
),
NameEExp(name, eexp) => {
FieldExpr::NameMacro(name.resolve(context), eexp.resolve(context)?.into())
}
EExp(eexp) => FieldExpr::EExp(eexp.resolve(context)?),
};
Ok(field)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🪧 A convenience method pattern used widely throughout the crate; resolve turns a raw value (or in this case, field) into its higher-level, context-ful equivalent.


pub fn expect_name_value(self) -> IonResult<(D::FieldName<'top>, D::Value<'top>)> {
let LazyRawFieldExpr::NameValue(name, value) = self else {
return IonResult::decoding_error(format!(
Expand Down Expand Up @@ -381,8 +397,8 @@ impl<D: Decoder> HasRange for LazyRawFieldExpr<'_, 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::expanded::macro_evaluator::{MacroExpr, RawEExpression};
use crate::lazy::expanded::r#struct::UnexpandedField;
use crate::lazy::expanded::macro_evaluator::RawEExpression;
use crate::lazy::expanded::r#struct::FieldExpr;
use crate::lazy::expanded::EncodingContextRef;
use crate::{try_next, try_or_some_err, IonResult, LazyExpandedValue, LazyRawFieldName};

Expand All @@ -395,47 +411,44 @@ pub(crate) mod private {
}

pub trait LazyRawStructPrivate<'top, D: Decoder> {
/// Creates an iterator that converts each raw struct field into an `UnexpandedField`, a
/// Creates an iterator that converts each raw struct field into an `FieldExpr`, a
/// common representation for both raw fields and template fields that is used in the
/// expansion process.
fn unexpanded_fields(
fn field_exprs(
Comment on lines -398 to +417
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🪧 UnexpandedField was a clunky name, hopefully this is better. The new relationship between LazyExpandedField and FieldExpr mirrors the existing relationship between LazyExpandedValue and ValueExpr.

&self,
context: EncodingContextRef<'top>,
) -> RawStructUnexpandedFieldsIterator<'top, D>;
) -> RawStructFieldExprIterator<'top, D>;
}

pub struct RawStructUnexpandedFieldsIterator<'top, D: Decoder> {
pub struct RawStructFieldExprIterator<'top, D: Decoder> {
context: EncodingContextRef<'top>,
raw_fields: <D::Struct<'top> as LazyRawStruct<'top, D>>::Iterator,
}

impl<'top, D: Decoder> RawStructUnexpandedFieldsIterator<'top, D> {
impl<'top, D: Decoder> RawStructFieldExprIterator<'top, D> {
pub fn context(&self) -> EncodingContextRef<'top> {
self.context
}
}

impl<'top, D: Decoder> Iterator for RawStructUnexpandedFieldsIterator<'top, D> {
type Item = IonResult<UnexpandedField<'top, D>>;
impl<'top, D: Decoder> Iterator for RawStructFieldExprIterator<'top, D> {
type Item = IonResult<FieldExpr<'top, D>>;

fn next(&mut self) -> Option<Self::Item> {
let field: LazyRawFieldExpr<'top, D> = try_next!(self.raw_fields.next());
use LazyRawFieldExpr::*;
let unexpanded_field = match field {
NameValue(name, value) => UnexpandedField::NameValue(
NameValue(name, value) => FieldExpr::NameValue(
name.resolve(self.context),
LazyExpandedValue::from_literal(self.context, value),
),
NameEExp(name, raw_eexp) => {
let eexp = try_or_some_err!(raw_eexp.resolve(self.context));
UnexpandedField::NameMacro(
name.resolve(self.context),
MacroExpr::from_eexp(eexp),
)
FieldExpr::NameMacro(name.resolve(self.context), eexp.into())
}
EExp(raw_eexp) => {
let eexp = try_or_some_err!(raw_eexp.resolve(self.context));
UnexpandedField::Macro(MacroExpr::from_eexp(eexp))
FieldExpr::EExp(eexp)
}
};
Some(Ok(unexpanded_field))
Expand All @@ -446,12 +459,12 @@ pub(crate) mod private {
where
S: LazyRawStruct<'top, D>,
{
fn unexpanded_fields(
fn field_exprs(
&self,
context: EncodingContextRef<'top>,
) -> RawStructUnexpandedFieldsIterator<'top, D> {
) -> RawStructFieldExprIterator<'top, D> {
let raw_fields = <Self as LazyRawStruct<'top, D>>::iter(self);
RawStructUnexpandedFieldsIterator {
RawStructFieldExprIterator {
context,
raw_fields,
}
Expand Down Expand Up @@ -600,6 +613,8 @@ pub trait LazyRawValue<'top, D: Decoder>:
{
fn ion_type(&self) -> IonType;
fn is_null(&self) -> bool;

fn is_delimited(&self) -> bool;
fn has_annotations(&self) -> bool;
fn annotations(&self) -> D::AnnotationsIterator<'top>;
fn read(&self) -> IonResult<RawValueRef<'top, D>>;
Expand Down Expand Up @@ -675,7 +690,7 @@ pub trait LazyRawStruct<'top, D: Decoder>:
}

pub trait LazyRawFieldName<'top, D: Decoder<FieldName<'top> = Self>>:
HasSpan<'top> + Copy + Debug + Clone
Into<LazyRawAnyFieldName<'top>> + HasSpan<'top> + Copy + Debug + Clone
{
fn read(&self) -> IonResult<RawSymbolRef<'top>>;

Expand Down
2 changes: 1 addition & 1 deletion src/lazy/encoder/binary/v1_1/flex_sym.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ impl<'top> FlexSym<'top> {
Ordering::Equal => {
// Get the first byte following the leading FlexInt
let flex_int_len = value.size_in_bytes();
if input.len() < flex_int_len {
if input.len() <= flex_int_len {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🪧 This bug surfaced while I was working on an example file from @jobarr-amzn. This fixed it.

return IonResult::incomplete("reading a FlexSym", offset);
}
let byte = input[flex_int_len];
Expand Down
66 changes: 60 additions & 6 deletions src/lazy/expanded/macro_evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ where
#[derive(Copy, Clone)]
pub struct MacroExpr<'top, D: Decoder> {
kind: MacroExprKind<'top, D>,
variable: Option<TemplateVariableReference<'top>>,
pub(crate) variable: Option<TemplateVariableReference<'top>>,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🪧 Macros now remember the variable (if any) that produced them, allowing them in turn to tag the values they produce.

}

impl<D: Decoder> Debug for MacroExpr<'_, D> {
Expand All @@ -137,8 +137,8 @@ impl<'top, D: Decoder> MacroExpr<'top, D> {
}
}

pub fn via_variable(mut self, variable_ref: TemplateVariableReference<'top>) -> Self {
self.variable = Some(variable_ref);
pub fn via_variable(mut self, variable_ref: Option<TemplateVariableReference<'top>>) -> Self {
self.variable = variable_ref;
self
}

Expand All @@ -149,6 +149,30 @@ impl<'top, D: Decoder> MacroExpr<'top, D> {
MacroExprKind::EExp(e) => e.expand(),
MacroExprKind::EExpArgGroup(g) => g.expand(),
}
.map(|expansion| expansion.via_variable(self.variable))
}

pub fn range(&self) -> Option<Range<usize>> {
self.span().as_ref().map(Span::range)
}

/// If this `ValueExpr` represents an entity encoded in the data stream, returns `Some(range)`.
/// If it represents an ephemeral value produced by a macro evaluation, returns `None`.
pub fn span(&self) -> Option<Span<'top>> {
use MacroExprKind::*;
match self.kind {
TemplateMacro(_) | TemplateArgGroup(_) => None,
EExp(eexp) => Some(eexp.span()),
EExpArgGroup(group) => Some(group.span()),
}
}

pub fn is_eexp(&self) -> bool {
matches!(self.kind, MacroExprKind::EExp(_))
}

pub fn is_tdl_macro(&self) -> bool {
matches!(self.kind, MacroExprKind::TemplateMacro(_))
}
}

Expand Down Expand Up @@ -389,6 +413,19 @@ impl<D: Decoder> Debug for ValueExpr<'_, D> {
}

impl<'top, D: Decoder> ValueExpr<'top, D> {
pub fn via_variable(
self,
template_variable_ref: Option<TemplateVariableReference<'top>>,
) -> Self {
use ValueExpr::*;
match self {
ValueLiteral(value) => ValueLiteral(value.via_variable(template_variable_ref)),
MacroInvocation(invocation) => {
MacroInvocation(invocation.via_variable(template_variable_ref))
}
}
}

/// Like [`evaluate_singleton_in`](Self::evaluate_singleton_in), but uses an empty environment.
pub fn evaluate_singleton(&self) -> IonResult<LazyExpandedValue<'top, D>> {
self.evaluate_singleton_in(Environment::empty())
Expand Down Expand Up @@ -475,7 +512,7 @@ impl<'top, D: Decoder> ValueExpr<'top, D> {
ValueExpr::ValueLiteral(value) => {
use ExpandedValueSource::*;
match value.source {
SingletonEExp(_) => todo!(),
SingletonEExp(eexp) => Some(eexp.span()),
ValueLiteral(literal) => Some(literal.span()),
Template(_, _) | Constructed(_, _) => None,
}
Expand Down Expand Up @@ -522,9 +559,15 @@ pub struct MacroExpansion<'top, D: Decoder> {
kind: MacroExpansionKind<'top, D>,
environment: Environment<'top, D>,
is_complete: bool,
variable_ref: Option<TemplateVariableReference<'top>>,
}

impl<'top, D: Decoder> MacroExpansion<'top, D> {
pub fn via_variable(mut self, variable_ref: Option<TemplateVariableReference<'top>>) -> Self {
self.variable_ref = variable_ref;
self
}

pub fn context(&self) -> EncodingContextRef<'top> {
self.context
}
Expand Down Expand Up @@ -565,6 +608,7 @@ impl<'top, D: Decoder> MacroExpansion<'top, D> {
kind,
context,
is_complete: false,
variable_ref: None,
}
}

Expand All @@ -591,6 +635,7 @@ impl<'top, D: Decoder> MacroExpansion<'top, D> {
// `none` is trivial and requires no delegation
None => Ok(MacroExpansionStep::FinalStep(Option::None)),
}
.map(|expansion| expansion.via_variable(self.variable_ref))
}
}

Expand Down Expand Up @@ -635,6 +680,15 @@ impl<'top, D: Decoder> MacroExpansionStep<'top, D> {
pub fn is_final(&self) -> bool {
matches!(self, MacroExpansionStep::FinalStep(_))
}

pub fn via_variable(mut self, variable_ref: Option<TemplateVariableReference<'top>>) -> Self {
use MacroExpansionStep::*;
match &mut self {
Step(expr) => Step(expr.via_variable(variable_ref)),
FinalStep(Some(expr)) => FinalStep(Some(expr.via_variable(variable_ref))),
FinalStep(None) => FinalStep(None),
}
}
}

/// The internal bookkeeping representation used by a [`MacroEvaluator`].
Expand Down Expand Up @@ -768,7 +822,7 @@ impl<'top, D: Decoder> MacroEvaluator<'top, D> {
}

pub fn for_macro_expr(macro_expr: MacroExpr<'top, D>) -> IonResult<Self> {
let expansion = MacroExpansion::initialize(macro_expr)?;
let expansion = macro_expr.expand()?;
Ok(Self::for_expansion(expansion))
}

Expand Down Expand Up @@ -889,7 +943,7 @@ impl<'top, D: Decoder> StackedMacroEvaluator<'top, D> {
/// current encoding context and push the resulting `MacroExpansion` onto the stack.
pub fn push(&mut self, invocation: impl Into<MacroExpr<'top, D>>) -> IonResult<()> {
let macro_expr = invocation.into();
let expansion = match MacroExpansion::initialize(macro_expr) {
let expansion = match macro_expr.expand() {
Ok(expansion) => expansion,
Err(e) => return Err(e),
};
Expand Down
13 changes: 7 additions & 6 deletions src/lazy/expanded/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ use crate::lazy::decoder::{Decoder, LazyRawValue};
use crate::lazy::encoding::RawValueLiteral;
use crate::lazy::expanded::compiler::TemplateCompiler;
use crate::lazy::expanded::e_expression::EExpression;
use crate::lazy::expanded::macro_evaluator::{
MacroEvaluator, MacroExpansion, MacroExpr, RawEExpression,
};
use crate::lazy::expanded::macro_evaluator::{MacroEvaluator, MacroExpr, RawEExpression};
use crate::lazy::expanded::macro_table::{Macro, MacroTable, ION_1_1_SYSTEM_MACROS};
use crate::lazy::expanded::r#struct::LazyExpandedStruct;
use crate::lazy::expanded::sequence::Environment;
Expand Down Expand Up @@ -887,8 +885,11 @@ impl<'top, Encoding: Decoder> LazyExpandedValue<'top, Encoding> {
}
}

pub(crate) fn via_variable(mut self, variable_ref: TemplateVariableReference<'top>) -> Self {
self.variable = Some(variable_ref);
pub(crate) fn via_variable(
mut self,
variable_ref: Option<TemplateVariableReference<'top>>,
) -> Self {
self.variable = variable_ref;
self
}

Expand Down Expand Up @@ -978,7 +979,7 @@ impl<'top, Encoding: Decoder> LazyExpandedValue<'top, Encoding> {
&self,
eexp: &EExpression<'top, Encoding>,
) -> IonResult<ValueRef<'top, Encoding>> {
let new_expansion = MacroExpansion::initialize(MacroExpr::from_eexp(*eexp))?;
let new_expansion = MacroExpr::from_eexp(*eexp).expand()?;
new_expansion.expand_singleton()?.read_resolved()
}

Expand Down
Loading
Loading