-
Notifications
You must be signed in to change notification settings - Fork 37
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
Adds high-level Writer API for defining macros #938
Conversation
…ames to be used for writing E-exps.
Codecov ReportAttention: Patch coverage is
❌ Your patch status has failed because the patch coverage (61.96%) is below the target coverage (85.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #938 +/- ##
==========================================
- Coverage 78.76% 78.66% -0.11%
==========================================
Files 137 137
Lines 34739 35203 +464
Branches 34739 35203 +464
==========================================
+ Hits 27363 27691 +328
- Misses 5378 5454 +76
- Partials 1998 2058 +60 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗺️ PR Tour 🧭
//! This program demonstrates implementing WriteAsIon using Ion 1.1's e-expressions for a more | ||
//! compact encoding. It uses raw-level writer APIs that end users are unlikely to leverage. | ||
//! Ion 1.1 is not yet finalized; the encoding produced by this example and the APIs it uses | ||
//! are very likely to change. | ||
|
||
//! compact encoding. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🪧 Happily, this example that ended up at the top of the diff showcases the new functionality.
use ion_rs::*; | ||
|
||
fn main() -> IonResult<()> { | ||
#[cfg(not(feature = "experimental"))] | ||
panic!("This example requires the 'experimental' feature to work."); | ||
{ | ||
eprintln!("This example requires the 'experimental' feature to work. Rebuild it with the flag `--features experimental`."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🪧 This was originally a panic!
to make it clear that the example had failed to run. However, the boilerplate output text from the panic!
obscured the message explaining what had gone wrong. Now it just prints a (more detailed) explanation to STDERR
.
// IDs and Ion 1.1 refers to them as part of a macro. In both cases, however, the encoding | ||
// context is not written out in the resulting Ion stream. | ||
// TODO: Include the symbol/macro table definitions in the resulting output stream. | ||
#[allow(dead_code)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🪧 This example no longer uses the raw writer to encode symbol IDs directly. The text literals are now provided to the managed Writer
as would happen in most applications.
@@ -124,6 +124,7 @@ pub(crate) mod v1_1 { | |||
|
|||
pub const ION: SystemSymbol_1_1 = SystemSymbol_1_1::new_unchecked(1); | |||
pub const ENCODING: SystemSymbol_1_1 = SystemSymbol_1_1::new_unchecked(10); | |||
pub const MACRO_TABLE: SystemSymbol_1_1 = SystemSymbol_1_1::new_unchecked(14); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🪧 The Writer
can now emit a directive to append macros to the macro table. This system symbol is used in that directive.
@@ -1311,7 +1311,7 @@ mod tests { | |||
) -> IonResult<()> { | |||
let mut context = EncodingContext::for_ion_version(IonVersion::v1_1); | |||
let template_macro = | |||
TemplateCompiler::compile_from_source(context.get_ref(), macro_source)?; | |||
TemplateCompiler::compile_from_source(context.macro_table(), macro_source)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🪧 Previously, the TemplateCompiler
would pass around an EncodingContextRef
that was used to resolve macros being invoked in the provided TDL source. However, the EncodingContextRef
is specific to the Reader
. (The Writer
has an analogous construct that is optimized for different operations.) This PR modifies the TemplateCompiler
to pass around a &MacroTable
instead of an EncodingContext
. Both the Reader
and Writer
can easily provide a &MacroTable
, and that was the only field in the EncodingContextRef
that was being used anyway.
@@ -92,6 +96,46 @@ impl<E: Encoding, Output: Write> Writer<E, Output> { | |||
Ok(writer) | |||
} | |||
|
|||
pub fn compile_macro(&mut self, source: impl IonInput) -> IonResult<Macro> { | |||
let mut reader = Reader::new(AnyEncoding, source)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🪧 Eventually I'd like to optimize this by either accepting bulk macro definitions, re-using the Reader
, or just offering a programmatic API for constructing a macro.
// TODO: Once expression group serialization is complete, this can be replaced by a call | ||
// to the `add_macros` system macro. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🪧 Binary expr group writing needs to be functional before we can do this.
@@ -216,14 +216,14 @@ impl TemplateCompiler { | |||
/// to the template without interpretation. `(literal ...)` does not appear in the compiled | |||
/// template as there is nothing more for it to do at expansion time. | |||
pub fn compile_from_source( | |||
context: EncodingContextRef<'_>, | |||
active_macros: &MacroTable, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🪧 All changes in this file are either:
- Replacing the previously-passed-around
EncodingContextRef
with a&MacroTable
.
OR - Mechanical renaming of
Macro
(which is now a user-facing handle) toMacroDef
(which continues to not be user-visible).
@@ -203,6 +327,33 @@ impl From<SystemMacroAddress> for MacroIdRef<'_> { | |||
} | |||
} | |||
|
|||
#[derive(Clone, Debug, PartialEq)] | |||
pub enum MacroId { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🪧 An owned variant of MacroIdRef
.
fn resolve(self, _macro_table: &'a MacroTable) -> IonResult<ResolvedId<'a>> { | ||
// ResolvedId is already resolved | ||
Ok(self) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🪧 This is the important bit. If you pass an unresolved identifier to a raw eexp writer, it will try to resolve it. If you pass a resolved identifier to a raw eexp writer (say, from the managed writer that already resolved it), it won't need to resolve it again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've only quickly scanned the PR. I'll take a closer look later. I had a few drive-by thoughts though.
LocalName(CompactString), | ||
LocalAddress(usize), | ||
SystemAddress(SystemMacroAddress), | ||
// TODO: Qualified names and addresses |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about system name? (Or is that part of qualified name?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the current impl doesn't have multi-module support, System
is a special case. Later, it will be supplanted by QualifiedAddress
and (as you mention) QualifiedName
.
let mut ion_writer = Writer::new(v1_1::Binary, BufWriter::new(ion_1_1_file.as_file()))?; | ||
|
||
// ...then we define some macros that we intend to use to encode our log data... | ||
let macros = LogEventMacros { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this example would be clearer without having a custom struct here. I was initially confused by this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can do that, but it means passing two &Macro
references later on instead of one "all-the-macros" reference. I don't feel strongly either way.
src/lazy/encoder/writer.rs
Outdated
Ok(ApplicationEExpWriter::new( | ||
self.encoding, | ||
self.value_writer_config, | ||
self.raw_value_writer.eexp_writer(macro_id)?, | ||
self.raw_value_writer.eexp_writer(macro_id)?, // HERE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is HERE
—leftover comment?
* Adds `lazzy-soucre-location` feature flag * Adds `row` and `prev_newline_offset` for `IonSlice` and `IonStream` * Adds unit tests for `IonSlice` and `IonStream`
PR tour forthcoming, please stand by until this is no longer in draft mode.
Related to #920. cc @barries
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.