From dc930e44fbc176c554767a01bdf05c1a83cbde10 Mon Sep 17 00:00:00 2001 From: Austin Henriksen Date: Wed, 4 Mar 2026 14:00:27 -0500 Subject: [PATCH 1/8] Checkpoint before we update the types to match the new compiler definitions. --- slice-codec/src/slice2/encoding.rs | 24 +++- slicec/src/main.rs | 219 +++++++++++++++++++++++++++++ 2 files changed, 242 insertions(+), 1 deletion(-) diff --git a/slice-codec/src/slice2/encoding.rs b/slice-codec/src/slice2/encoding.rs index 75421634..e3966d66 100644 --- a/slice-codec/src/slice2/encoding.rs +++ b/slice-codec/src/slice2/encoding.rs @@ -6,9 +6,14 @@ use crate::encode_into::*; use crate::encoder::Encoder; use crate::{Error, InvalidDataErrorKind, Result}; -// We only support `BTreeMap` if the `alloc` crate is available through the `alloc` feature flag. +// We only support 'owned' sequence/dictionary types if the `alloc` crate is available through the `alloc` feature flag. +// Note that we always support encoding views into these types (which don't require allocating memory). #[cfg(feature = "alloc")] use alloc::collections::BTreeMap; +#[cfg(feature = "alloc")] +use alloc::string::String; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; // We only support `HashMap` if the standard library is available through the `std` feature flag. #[cfg(feature = "std")] @@ -151,6 +156,13 @@ impl EncodeInto for &str { } } +#[cfg(feature = "alloc")] +impl EncodeInto for &String { + fn encode_into(self, encoder: &mut Encoder) -> Result<()> { + self.as_str().encode_into(encoder) + } +} + /// TODO impl<'a, T> EncodeInto for &'a [T] where @@ -166,6 +178,16 @@ where } } +#[cfg(feature = "alloc")] +impl<'a, T> EncodeInto for &'a Vec +where + &'a T: EncodeInto, +{ + fn encode_into(self, encoder: &mut Encoder) -> Result<()> { + self.as_slice().encode_into(encoder) + } +} + // ============================================================================= // Dictionary type implementations // ============================================================================= diff --git a/slicec/src/main.rs b/slicec/src/main.rs index a91aa0f6..51562cbf 100644 --- a/slicec/src/main.rs +++ b/slicec/src/main.rs @@ -4,6 +4,225 @@ use clap::Parser; use slicec::compilation_state::CompilationState; use slicec::slice_options::SliceOptions; + + +use slice_codec::slice2::Slice2; + +use slice_codec::buffer::OutputTarget; +use slice_codec::encode_into::*; +use slice_codec::encoder::Encoder; +use slice_codec::Result; + +const TAG_END_MARKER: i32 = -1; + +pub type EntityId = String; + +macro_rules! implement_encode_into_for_struct { + ($type_name:ty$(, $field_name:ident)*) => { + impl EncodeInto for &$type_name { + fn encode_into(self, encoder: &mut Encoder) -> Result<()> { + $(encoder.encode(&self.$field_name);)* + encoder.encode_varint(TAG_END_MARKER); + Ok(()) + } + } + } +} + +#[derive(Clone, Debug)] +pub struct EntityInfo { + identifier: String, + attributes: Option>, // TAG(0) + comment: Option, // TAG(1) +} +implement_encode_into_for_struct!(EntityInfo, identifier, attributes, comment); + +#[derive(Clone, Debug)] +pub struct Module { + identifier: String, + attributes: Option>, // TAG(0) +} +implement_encode_into_for_struct!(Module, identifier); + +#[derive(Clone, Debug)] +pub struct Struct { + entity_info: EntityInfo, + fields: Vec, + is_compact: bool, + is_slice1_only: bool, +} +implement_encode_into_for_struct!(Struct, entity_info, fields, is_compact, is_slice1_only); + +#[derive(Clone, Debug)] +pub struct Class { + entity_info: EntityInfo, + fields: Vec, + compact_id: Option, + base: Option, +} +implement_encode_into_for_struct!(Class, entity_info, fields, compact_id, base); + +#[derive(Clone, Debug)] +pub struct Exception { + entity_info: EntityInfo, + fields: Vec, + base: Option, +} +implement_encode_into_for_struct!(Exception, entity_info, fields, base); + +#[derive(Clone, Debug)] +pub struct Field { + entity_info: EntityInfo, + data_type: TypeRef, + tag: Option, // TODO: varint32 isn't a real type? +} +// TODO how to encode field properly> + +#[derive(Clone, Debug)] +pub struct Interface { + entity_info: EntityInfo, + operations: Vec, + bases: Vec, +} +implement_encode_into_for_struct!(Interface, entity_info, operations, bases); + +#[derive(Clone, Debug)] +pub struct Operation { + entity_info: EntityInfo, + parameters: Vec, + has_streamed_parameter: bool, + return_type: Vec, + has_streamed_return: bool, + exception_specification: Vec, + is_idempotent: bool, +} +implement_encode_into_for_struct!(Operation, entity_info, parameters, has_streamed_parameter, return_type, has_streamed_return, exception_specification, is_idempotent); + +#[derive(Clone, Debug)] +pub struct Enum { + entity_info: EntityInfo, + enumerators: Vec, + underlying: Option, + is_compact: bool, + is_unchecked: bool, +} +implement_encode_into_for_struct!(Enum, entity_info, enumerators, underlying, is_compact, is_unchecked); + +#[derive(Clone, Debug)] +pub struct Enumerator { + entity_info: EntityInfo, + value: Discriminant, + fields: Option>, // TODO we probably shouldn't distinguish +} +implement_encode_into_for_struct!(Enumerator, entity_info, value, fields); + +#[derive(Clone, Debug)] +pub struct Discriminant { + absolute_value: u64, + is_positive: bool, +} +implement_encode_into_for_struct!(Discriminant, absolute_value, is_positive); + +#[derive(Clone, Debug)] +pub struct CustomType { + entity_info: EntityInfo, +} +implement_encode_into_for_struct!(CustomType, entity_info); + +#[derive(Clone, Debug)] +pub struct TypeAlias { + entity_info: EntityInfo, + underlying: TypeRef, +} +implement_encode_into_for_struct!(TypeAlias, entity_info, underlying); + +#[derive(Clone, Debug)] +#[repr(usize)] +pub enum AnonymousType { + SequenceType(SequenceType), + DictionaryType(DictionaryType), + ResultType(ResultType), +} +impl EncodeInto for &AnonymousType { + fn encode_into(self, encoder: &mut Encoder) -> Result<()> { + encoder.encode(*<*const _>::from(self).cast::()); // TODO, we don't treat this as a varuint62 when we should... + match self { + AnonymousType::SequenceType(v) => encoder.encode(v), + AnonymousType::DictionaryType(v) => encoder.encode(v), + AnonymousType::ResultType(v) => encoder.encode(v), + }; + encoder.encode_varint(TAG_END_MARKER); + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct SequenceType { + element_type: TypeRef, +} +implement_encode_into_for_struct!(SequenceType, element_type); + +#[derive(Clone, Debug)] +pub struct DictionaryType { + key_type: TypeRef, + value_type: TypeRef, +} +implement_encode_into_for_struct!(DictionaryType, key_type, value_type); + +#[derive(Clone, Debug)] +pub struct ResultType { + success_type: TypeRef, + failure_type: TypeRef, +} +implement_encode_into_for_struct!(ResultType, success_type, failure_type); + +#[derive(Clone, Copy, Debug)] +#[repr(u8)] +pub enum Primitive { + Bool, Int8, UInt8, Int16, UInt16, Int32, UInt32, VarInt32, VarUInt32, Int64, UInt64, VarInt62, VarUInt62, Float32, Float64, String, AnyClass, +} +impl EncodeInto for &Primitive { + fn encode_into(self, encoder: &mut Encoder) -> Result<()> { + encoder.encode(*self as u8); + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct TypeRef { + value: TypeRefDefinition, + is_optional: bool, + attributes: Option>, // TAG(0) +} +implement_encode_into_for_struct!(TypeRef, value, is_optional, attributes); + +#[derive(Clone, Debug)] +#[repr(usize)] +pub enum TypeRefDefinition { + Definition(EntityId), + Primitive(Primitive), + Anonymous(usize), // TODO, we don't treat this as a varuint62 when we should... +} +impl EncodeInto for &TypeRefDefinition { + fn encode_into(self, encoder: &mut Encoder) -> Result<()> { + encoder.encode(*<*const _>::from(self).cast::()); // TODO, we don't treat this as a varuint62 when we should... + match self { + TypeRefDefinition::Definition(v) => encoder.encode(v), + TypeRefDefinition::Primitive(v) => encoder.encode(v), + TypeRefDefinition::Anonymous(v) => encoder.encode(v), + }; + encoder.encode_varint(TAG_END_MARKER); + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct Attribute { + directive: String, + args: Vec, +} +implement_encode_into_for_struct!(Attribute, directive, args); + fn main() { // Parse the command-line input. let slice_options = SliceOptions::parse(); From 37b45daf5a6aa0eb9a69ac7e24b1e7e5c390015f Mon Sep 17 00:00:00 2001 From: Austin Henriksen Date: Fri, 6 Mar 2026 10:33:40 -0500 Subject: [PATCH 2/8] This doesn't actually work... --- slicec/src/definition_encoder.rs | 266 ++++++++++++++++++++++++ slicec/src/definition_types.rs | 339 +++++++++++++++++++++++++++++++ slicec/src/main.rs | 220 +------------------- 3 files changed, 607 insertions(+), 218 deletions(-) create mode 100644 slicec/src/definition_encoder.rs create mode 100644 slicec/src/definition_types.rs diff --git a/slicec/src/definition_encoder.rs b/slicec/src/definition_encoder.rs new file mode 100644 index 00000000..cab57fa3 --- /dev/null +++ b/slicec/src/definition_encoder.rs @@ -0,0 +1,266 @@ +// Copyright (c) ZeroC, Inc. + +// Pull in all the mapped compiler-definition types. +use crate::definition_types::*; + +// Pull in traits from 'slicec' so we can call their functions. +use slicec::grammar::Attributable; +use slicec::grammar::Commentable; +use slicec::grammar::Contained; +use slicec::grammar::Member; +use slicec::grammar::NamedSymbol; +use slicec::grammar::Type; +use slicec::visitor::Visitor; + +// Pull in the core compiler types, using aliases to disambiguate. +// Any type that starts with 'Compiler' is a compiler type, not a mapped type. +use slicec::grammar::Attribute as CompilerAttribute; +use slicec::grammar::CustomType as CompilerCustomType; +use slicec::grammar::Definition as CompilerDefinition; +use slicec::grammar::Dictionary as CompilerDictionary; +use slicec::grammar::DocComment as CompilerDocComment; +use slicec::grammar::Enum as CompilerEnum; +use slicec::grammar::Enumerator as CompilerEnumerator; +use slicec::grammar::Field as CompilerField; +use slicec::grammar::Interface as CompilerInterface; +use slicec::grammar::Module as CompilerModule; +use slicec::grammar::Operation as CompilerOperation; +use slicec::grammar::Parameter as CompilerParameter; +use slicec::grammar::ResultType as CompilerResultType; +use slicec::grammar::Sequence as CompilerSequence; +use slicec::grammar::Struct as CompilerStruct; +use slicec::grammar::Types as CompilerTypes; +use slicec::grammar::TypeAlias as CompilerTypeAlias; +use slicec::grammar::TypeRef as CompilerTypeRef; +use slicec::slice_file::SliceFile as CompilerSliceFile; + +pub struct AnonymousTypeEncoder { + anonymous_types: Vec, +} + +impl Visitor for AnonymousTypeEncoder { + // TODO check that this visits all the places that we care about correctly! + fn visit_type_ref(&mut self, type_ref: &CompilerTypeRef) { + match type_ref.concrete_type() { + CompilerTypes::Sequence(v) => self.anonymous_types.push(Symbol::SequenceType(v.into())), + CompilerTypes::Dictionary(v) => self.anonymous_types.push(Symbol::DictionaryType(v.into())), + CompilerTypes::ResultType(v) => self.anonymous_types.push(Symbol::ResultType(v.into())), + _ => {} + } + } +} + +fn get_entity_info_for(element: &impl Commentable) -> EntityInfo { + EntityInfo { + identifier: element.identifier().to_owned(), + attributes: get_attributes_from(element.attributes()), + comment: element.comment().map(Into::into), + } +} + +fn get_attributes_from(attributes: Vec<&CompilerAttribute>) -> Vec { + +} + +impl From<&CompilerSliceFile> for SliceFile { + fn from(slice_file: &CompilerSliceFile) -> Self { + // TODO run the symbol visitor from this spot! + + SliceFile { + path: slice_file.relative_path.clone(), + module_declaration: slice_file.module.as_ref().unwrap().borrow().into(), + attributes: get_attributes_from(slice_file.attributes()), + contents: slice_file.contents.iter().map(Into::into).collect(), + } + } +} + +impl From<&CompilerModule> for Module { + fn from(module: &CompilerModule) -> Self { + Module { + identifier: module.identifier().to_owned(), + attributes: get_attributes_from(module.attributes()), + } + } +} + +impl From<&CompilerDefinition> for Symbol { + fn from(definition: &CompilerDefinition) -> Self { + match definition { + CompilerDefinition::Struct(v) => Symbol::Struct(v.borrow().into()), + CompilerDefinition::Interface(v) => Symbol::Interface(v.borrow().into()), + CompilerDefinition::Enum(v) => Symbol::Enum(v.borrow().into()), + CompilerDefinition::CustomType(v) => Symbol::CustomType(v.borrow().into()), + CompilerDefinition::TypeAlias(v) => Symbol::TypeAlias(v.borrow().into()), + _ => panic!("TODO: remove classes and exceptions"), + } + } +} + +impl From<&CompilerStruct> for Struct { + fn from(struct_def: &CompilerStruct) -> Self { + Struct { + entity_info: get_entity_info_for(struct_def), + is_compact: struct_def.is_compact, + fields: struct_def.fields().into_iter().map(Into::into).collect(), + } + } +} + +impl From<&CompilerField> for Field { + fn from(field: &CompilerField) -> Self { + Field { + entity_info: get_entity_info_for(field), + tag: field.tag.as_ref().map(|integer| integer.value as i32), + data_type: field.data_type().into(), + } + } +} + +impl From<&CompilerInterface> for Interface { + fn from(interface: &CompilerInterface) -> Self { + let bases = interface.base_interfaces(); + + Interface { + entity_info: get_entity_info_for(interface), + bases: bases.into_iter().map(|i| i.parser_scoped_identifier()).collect(), + operations: interface.operations().into_iter().map(Into::into).collect(), + } + } +} + +impl From<&CompilerOperation> for Operation { + fn from(operation: &CompilerOperation) -> Self { + Operation { + entity_info: get_entity_info_for(operation), + is_idempotent: operation.is_idempotent, + parameters: operation.parameters().into_iter().map(Into::into).collect(), + has_streamed_parameter: operation.streamed_parameter().is_some(), + return_type: operation.return_members().into_iter().map(Into::into).collect(), + has_streamed_return: operation.streamed_return_member().is_some(), + } + } +} + +impl From<&CompilerParameter> for Field { + fn from(parameter: &CompilerParameter) -> Self { + // Check if this parameter's parent operation has a corresponding doc-comment on it. + let parameter_comment = parameter.parent().comment().and_then(|comment| { + comment.params.iter() + .find(|param_tag| param_tag.identifier.value == parameter.identifier()) + .map(|param_tag| param_tag.message) + }); + + let parameter_info = EntityInfo { + identifier: parameter.identifier().to_owned(), + attributes: get_attributes_from(parameter.attributes()), + comment: parameter_comment, + }; + + Field { + entity_info: parameter_info, + tag: parameter.tag.as_ref().map(|integer| integer.value as i32), + data_type: parameter.data_type().into(), + } + } +} + +impl From<&CompilerEnum> for Enum { + fn from(enum_def: &CompilerEnum) -> Self { + Enum { + entity_info: get_entity_info_for(enum_def), + is_compact: enum_def.is_compact, + is_unchecked: enum_def.is_unchecked, + underlying: enum_def.underlying.as_ref().map(|type_ref| type_ref.type_string()), + enumerators: enum_def.enumerators().into_iter().map(Into::into).collect(), + } + } +} + +impl From<&CompilerEnumerator> for Enumerator { + fn from(enumerator: &CompilerEnumerator) -> Self { + let entity_info = get_entity_info_for(enumerator); + let raw_value = enumerator.value(); + let value = Discriminant { + absolute_value: raw_value.abs() as u64, + is_positive: raw_value.is_positive(), + }; + let fields = enumerator.fields().into_iter().map(Into::into).collect(); + + Enumerator { entity_info, value, fields } + } +} + +impl From<&CompilerCustomType> for CustomType { + fn from(custom_type: &CompilerCustomType) -> Self { + CustomType { entity_info: get_entity_info_for(custom_type) } + } +} + +impl From<&CompilerTypeAlias> for TypeAlias { + fn from(type_alias: &CompilerTypeAlias) -> Self { + TypeAlias { + entity_info: get_entity_info_for(type_alias), + underlying_type: (&type_alias.underlying).into() + } + } +} + +impl From<&CompilerSequence> for SequenceType { + fn from(sequence: &CompilerSequence) -> Self { + SequenceType { + element_type: (&sequence.element_type).into(), + } + } +} + +impl From<&CompilerDictionary> for DictionaryType { + fn from(dictionary: &CompilerDictionary) -> Self { + DictionaryType { + key_type: (&dictionary.key_type).into(), + value_type: (&dictionary.value_type).into(), + } + } +} + +impl From<&CompilerResultType> for ResultType { + fn from(result_type: &CompilerResultType) -> Self { + ResultType { + success_type: (&result_type.success_type).into(), + failure_type: (&result_type.failure_type).into(), + } + } +} + +impl From<&CompilerTypeRef> for TypeRef { + fn from(type_ref: &CompilerTypeRef) -> Self { + let type_string = match type_ref.concrete_type() { + CompilerTypes::Struct(v) => v.module_scoped_identifier(), + CompilerTypes::Enum(v) => v.module_scoped_identifier(), + CompilerTypes::CustomType(v) => v.module_scoped_identifier(), + CompilerTypes::Primitive(v) => v.type_string(), + CompilerTypes::ResultType(v) => todo!(), + CompilerTypes::Sequence(v) => todo!(), + CompilerTypes::Dictionary(v) => todo!(), + + CompilerTypes::Class(_) => panic!("remove classes!"), + }; + + TypeRef { + type_id: type_string, + is_optional: type_ref.is_optional, + type_attributes: get_attributes_from(type_ref.attributes()), + } + } +} + + + + + + +impl From<&CompilerDocComment> for DocComment { + fn from(value: &CompilerDocComment) -> Self { + + } +} diff --git a/slicec/src/definition_types.rs b/slicec/src/definition_types.rs new file mode 100644 index 00000000..5ea9c06e --- /dev/null +++ b/slicec/src/definition_types.rs @@ -0,0 +1,339 @@ +// Copyright (c) ZeroC, Inc. + +//! This module contains the handwritten encoding code for the Slice-compiler definitions. +//! After bootstrapping has been completed, this file will be deleted, and we will use generated definitions instead. + +use slice_codec::slice2::Slice2; + +use slice_codec::buffer::OutputTarget; +use slice_codec::encode_into::*; +use slice_codec::encoder::Encoder; +use slice_codec::Result; + +/// TAG_END_MARKER must be encoded at the end of every non-compact type. +const TAG_END_MARKER: i32 = -1; + +/// These aliases are for consistency with the aliases in the Slice compiler definition. +pub type EntityId = String; +pub type TypeId = String; + +/// This macro implements `EncodeInto` for a Rust struct (that represents a non-compact Slice struct). +/// It encodes all the struct's fields (in definition order), followed by `TAG_END_MARKER`. +/// +/// It uses macro-function-syntax, and should be called like: +/// `implement_encode_into_for_struct!(struct_type_name, field1, field2, ...);` +macro_rules! implement_encode_into_for_struct { + ($type_name:ty$(, $field_name:ident)*) => { + impl EncodeInto for &$type_name { + fn encode_into(self, encoder: &mut Encoder) -> Result<()> { + $(encoder.encode(&self.$field_name)?;)* + encoder.encode_varint(TAG_END_MARKER)?; + Ok(()) + } + } + } +} + +// TODO implement tag encoding, so we can send DocComments along. (Doing this requires thought about the encoder API) +// This should probably be moved into the encoder anyways. +fn encode_tagged(tag: i32, value: &Option, size: u64, encoder: &mut Encoder) -> Result<()> +where + for<'a> &'a T : EncodeInto, +{ + if let Some(v) = value { + encoder.encode_varint(tag)?; + + // TODO: Reserve space to write the size after the fact. + //let size_reservation = encoder.reserve_space(4); + encoder.encode_varuint(size)?; + + // Write the actual value. + encoder.encode(v)?; + } + Ok(()) +} + +// ================= // +// Hand-mapped types // +// ================= // + +#[derive(Clone, Debug)] +pub struct EntityInfo { + pub identifier: String, + pub attributes: Vec, + pub comment: Option, +} +impl EncodeInto for &EntityInfo { + fn encode_into(self, encoder: &mut Encoder) -> Result<()> { + encoder.encode(&self.identifier)?; + encoder.encode(&self.attributes)?; + // encoder.encode_tagged(0, &self.comment, X)?; TODO add doc-comment support + encoder.encode_varint(TAG_END_MARKER)?; + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct Module { + pub identifier: String, + pub attributes: Vec, +} +implement_encode_into_for_struct!(Module, identifier, attributes); + +#[derive(Clone, Debug)] +pub struct Struct { + pub entity_info: EntityInfo, + pub is_compact: bool, + pub fields: Vec, +} +implement_encode_into_for_struct!(Struct, entity_info, is_compact, fields); + +#[derive(Clone, Debug)] +pub struct Field { + pub entity_info: EntityInfo, + pub tag: Option, // TODO: varint32 isn't a real type? + pub data_type: TypeRef, +} +impl EncodeInto for &Field { + fn encode_into(self, encoder: &mut Encoder) -> Result<()> { + // Encode the bit-sequence. With only one optional, this is just a bool. + encoder.encode(self.tag.is_some())?; + + // Encode the actual fields. + encoder.encode(&self.entity_info)?; + if let Some(tag_value) = &self.tag { + encoder.encode(tag_value)?; + } + encoder.encode(&self.data_type)?; + encoder.encode_varint(TAG_END_MARKER)?; + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct Interface { + pub entity_info: EntityInfo, + pub bases: Vec, + pub operations: Vec, +} +implement_encode_into_for_struct!(Interface, entity_info, bases, operations); + +#[derive(Clone, Debug)] +pub struct Operation { + pub entity_info: EntityInfo, + pub is_idempotent: bool, + pub parameters: Vec, + pub has_streamed_parameter: bool, + pub return_type: Vec, + pub has_streamed_return: bool, +} +implement_encode_into_for_struct!(Operation, entity_info, is_idempotent, parameters, has_streamed_parameter, return_type, has_streamed_return); + +#[derive(Clone, Debug)] +pub struct Enum { + pub entity_info: EntityInfo, + pub is_compact: bool, + pub is_unchecked: bool, + pub underlying: Option, + pub enumerators: Vec, +} +impl EncodeInto for &Enum { + fn encode_into(self, encoder: &mut Encoder) -> Result<()> { + // Encode the bit-sequence. With only one optional, this is just a bool. + encoder.encode(self.underlying.is_some())?; + + // Encode the actual fields. + encoder.encode(&self.entity_info)?; + encoder.encode(&self.is_compact)?; + encoder.encode(&self.is_unchecked)?; + if let Some(underlying_value) = &self.underlying { + encoder.encode(underlying_value)?; + } + encoder.encode(&self.enumerators)?; + encoder.encode_varint(TAG_END_MARKER)?; + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct Enumerator { + pub entity_info: EntityInfo, + pub value: Discriminant, + pub fields: Vec, +} +implement_encode_into_for_struct!(Enumerator, entity_info, value, fields); + +#[derive(Clone, Debug)] +pub struct Discriminant { + pub absolute_value: u64, + pub is_positive: bool, +} +implement_encode_into_for_struct!(Discriminant, absolute_value, is_positive); + +#[derive(Clone, Debug)] +pub struct CustomType { + pub entity_info: EntityInfo, +} +implement_encode_into_for_struct!(CustomType, entity_info); + +#[derive(Clone, Debug)] +pub struct TypeAlias { + pub entity_info: EntityInfo, + pub underlying_type: TypeRef, // Can never be optional. +} +implement_encode_into_for_struct!(TypeAlias, entity_info, underlying_type); + +#[derive(Clone, Debug)] +pub struct SequenceType { + pub element_type: TypeRef, +} +implement_encode_into_for_struct!(SequenceType, element_type); + +#[derive(Clone, Debug)] +pub struct DictionaryType { + pub key_type: TypeRef, // Can never be optional. + pub value_type: TypeRef, +} +implement_encode_into_for_struct!(DictionaryType, key_type, value_type); + +#[derive(Clone, Debug)] +pub struct ResultType { + pub success_type: TypeRef, + pub failure_type: TypeRef, +} +implement_encode_into_for_struct!(ResultType, success_type, failure_type); + +#[derive(Clone, Debug)] +pub struct TypeRef { + pub type_id: TypeId, + pub is_optional: bool, + pub type_attributes: Vec, +} +implement_encode_into_for_struct!(TypeRef, type_id, is_optional, type_attributes); + +#[derive(Clone, Debug)] +pub struct Attribute { + pub directive: String, + pub args: Vec, +} +implement_encode_into_for_struct!(Attribute, directive, args); + +#[derive(Clone, Debug)] +pub struct DocComment { + pub overview: Vec, + pub see_tags: Vec, +} +implement_encode_into_for_struct!(DocComment, overview, see_tags); + +#[repr(u8)] +#[derive(Clone, Debug)] +pub enum MessageComponent { + Text(String) = 0, + LinkTag(EntityId) = 1, +} +impl EncodeInto for &MessageComponent { + fn encode_into(self, encoder: &mut Encoder) -> Result<()> { + // Write the discriminant value. + // SAFETY: this cast is guaranteed to be safe because the enum is marked with `repr(u8)`, which means we know + // the first 'field' of this type's data layout must be a u8. This lets us read without offsetting the pointer. + unsafe { + let discriminant = *<*const _>::from(self).cast::(); + encoder.encode_varint(discriminant)?; + } + + // Encode the actual value. + match self { + MessageComponent::Text(v) => encoder.encode(v)?, + MessageComponent::LinkTag(v) => encoder.encode(v)?, + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct SliceFile { + pub path: String, + pub module_declaration: Module, + pub attributes: Vec, + pub contents: Vec, +} +implement_encode_into_for_struct!(SliceFile, path, module_declaration, attributes, contents); + +#[derive(Clone, Debug)] +pub struct GeneratedFile { + pub path: String, + pub contents: String, +} +implement_encode_into_for_struct!(GeneratedFile, path, contents); + +#[repr(u8)] +#[derive(Clone, Debug)] +pub enum Symbol { + Interface(Interface) = 0, + Enum(Enum) = 1, + Struct(Struct) = 2, + CustomType(CustomType) = 3, + SequenceType(SequenceType) = 4, + DictionaryType(DictionaryType) = 5, + ResultType(ResultType) = 6, + TypeAlias(TypeAlias) = 7, +} +impl EncodeInto for &Symbol { + fn encode_into(self, encoder: &mut Encoder) -> Result<()> { + // Write the discriminant value. + // SAFETY: this cast is guaranteed to be safe because the enum is marked with `repr(u8)`, which means we know + // the first 'field' of this type's data layout must be a u8. This lets us read without offsetting the pointer. + unsafe { + let discriminant = *<*const _>::from(self).cast::(); + encoder.encode_varint(discriminant)?; + } + + // Encode the actual value. + match self { + Symbol::Interface(v) => encoder.encode(v)?, + Symbol::Enum(v) => encoder.encode(v)?, + Symbol::Struct(v) => encoder.encode(v)?, + Symbol::CustomType(v) => encoder.encode(v)?, + Symbol::SequenceType(v) => encoder.encode(v)?, + Symbol::DictionaryType(v) => encoder.encode(v)?, + Symbol::ResultType(v) => encoder.encode(v)?, + Symbol::TypeAlias(v) => encoder.encode(v)?, + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub struct Diagnostic { + pub level: DiagnosticLevel, + pub message: String, + pub source: Option, +} +impl EncodeInto for &Diagnostic { + fn encode_into(self, encoder: &mut Encoder) -> Result<()> { + // Encode the bit-sequence. With only one optional, this is just a bool. + encoder.encode(self.source.is_some())?; + + // Encode the actual fields. + encoder.encode(&self.level)?; + encoder.encode(&self.message)?; + if let Some(source_value) = &self.source { + encoder.encode(source_value)?; + } + encoder.encode_varint(TAG_END_MARKER)?; + Ok(()) + } +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug)] +pub enum DiagnosticLevel { + Info, + Warning, + Error, +} +impl EncodeInto for &DiagnosticLevel { + fn encode_into(self, encoder: &mut Encoder) -> Result<()> { + encoder.encode(*self as u8) + } +} \ No newline at end of file diff --git a/slicec/src/main.rs b/slicec/src/main.rs index 51562cbf..14608bb1 100644 --- a/slicec/src/main.rs +++ b/slicec/src/main.rs @@ -4,224 +4,8 @@ use clap::Parser; use slicec::compilation_state::CompilationState; use slicec::slice_options::SliceOptions; - - -use slice_codec::slice2::Slice2; - -use slice_codec::buffer::OutputTarget; -use slice_codec::encode_into::*; -use slice_codec::encoder::Encoder; -use slice_codec::Result; - -const TAG_END_MARKER: i32 = -1; - -pub type EntityId = String; - -macro_rules! implement_encode_into_for_struct { - ($type_name:ty$(, $field_name:ident)*) => { - impl EncodeInto for &$type_name { - fn encode_into(self, encoder: &mut Encoder) -> Result<()> { - $(encoder.encode(&self.$field_name);)* - encoder.encode_varint(TAG_END_MARKER); - Ok(()) - } - } - } -} - -#[derive(Clone, Debug)] -pub struct EntityInfo { - identifier: String, - attributes: Option>, // TAG(0) - comment: Option, // TAG(1) -} -implement_encode_into_for_struct!(EntityInfo, identifier, attributes, comment); - -#[derive(Clone, Debug)] -pub struct Module { - identifier: String, - attributes: Option>, // TAG(0) -} -implement_encode_into_for_struct!(Module, identifier); - -#[derive(Clone, Debug)] -pub struct Struct { - entity_info: EntityInfo, - fields: Vec, - is_compact: bool, - is_slice1_only: bool, -} -implement_encode_into_for_struct!(Struct, entity_info, fields, is_compact, is_slice1_only); - -#[derive(Clone, Debug)] -pub struct Class { - entity_info: EntityInfo, - fields: Vec, - compact_id: Option, - base: Option, -} -implement_encode_into_for_struct!(Class, entity_info, fields, compact_id, base); - -#[derive(Clone, Debug)] -pub struct Exception { - entity_info: EntityInfo, - fields: Vec, - base: Option, -} -implement_encode_into_for_struct!(Exception, entity_info, fields, base); - -#[derive(Clone, Debug)] -pub struct Field { - entity_info: EntityInfo, - data_type: TypeRef, - tag: Option, // TODO: varint32 isn't a real type? -} -// TODO how to encode field properly> - -#[derive(Clone, Debug)] -pub struct Interface { - entity_info: EntityInfo, - operations: Vec, - bases: Vec, -} -implement_encode_into_for_struct!(Interface, entity_info, operations, bases); - -#[derive(Clone, Debug)] -pub struct Operation { - entity_info: EntityInfo, - parameters: Vec, - has_streamed_parameter: bool, - return_type: Vec, - has_streamed_return: bool, - exception_specification: Vec, - is_idempotent: bool, -} -implement_encode_into_for_struct!(Operation, entity_info, parameters, has_streamed_parameter, return_type, has_streamed_return, exception_specification, is_idempotent); - -#[derive(Clone, Debug)] -pub struct Enum { - entity_info: EntityInfo, - enumerators: Vec, - underlying: Option, - is_compact: bool, - is_unchecked: bool, -} -implement_encode_into_for_struct!(Enum, entity_info, enumerators, underlying, is_compact, is_unchecked); - -#[derive(Clone, Debug)] -pub struct Enumerator { - entity_info: EntityInfo, - value: Discriminant, - fields: Option>, // TODO we probably shouldn't distinguish -} -implement_encode_into_for_struct!(Enumerator, entity_info, value, fields); - -#[derive(Clone, Debug)] -pub struct Discriminant { - absolute_value: u64, - is_positive: bool, -} -implement_encode_into_for_struct!(Discriminant, absolute_value, is_positive); - -#[derive(Clone, Debug)] -pub struct CustomType { - entity_info: EntityInfo, -} -implement_encode_into_for_struct!(CustomType, entity_info); - -#[derive(Clone, Debug)] -pub struct TypeAlias { - entity_info: EntityInfo, - underlying: TypeRef, -} -implement_encode_into_for_struct!(TypeAlias, entity_info, underlying); - -#[derive(Clone, Debug)] -#[repr(usize)] -pub enum AnonymousType { - SequenceType(SequenceType), - DictionaryType(DictionaryType), - ResultType(ResultType), -} -impl EncodeInto for &AnonymousType { - fn encode_into(self, encoder: &mut Encoder) -> Result<()> { - encoder.encode(*<*const _>::from(self).cast::()); // TODO, we don't treat this as a varuint62 when we should... - match self { - AnonymousType::SequenceType(v) => encoder.encode(v), - AnonymousType::DictionaryType(v) => encoder.encode(v), - AnonymousType::ResultType(v) => encoder.encode(v), - }; - encoder.encode_varint(TAG_END_MARKER); - Ok(()) - } -} - -#[derive(Clone, Debug)] -pub struct SequenceType { - element_type: TypeRef, -} -implement_encode_into_for_struct!(SequenceType, element_type); - -#[derive(Clone, Debug)] -pub struct DictionaryType { - key_type: TypeRef, - value_type: TypeRef, -} -implement_encode_into_for_struct!(DictionaryType, key_type, value_type); - -#[derive(Clone, Debug)] -pub struct ResultType { - success_type: TypeRef, - failure_type: TypeRef, -} -implement_encode_into_for_struct!(ResultType, success_type, failure_type); - -#[derive(Clone, Copy, Debug)] -#[repr(u8)] -pub enum Primitive { - Bool, Int8, UInt8, Int16, UInt16, Int32, UInt32, VarInt32, VarUInt32, Int64, UInt64, VarInt62, VarUInt62, Float32, Float64, String, AnyClass, -} -impl EncodeInto for &Primitive { - fn encode_into(self, encoder: &mut Encoder) -> Result<()> { - encoder.encode(*self as u8); - Ok(()) - } -} - -#[derive(Clone, Debug)] -pub struct TypeRef { - value: TypeRefDefinition, - is_optional: bool, - attributes: Option>, // TAG(0) -} -implement_encode_into_for_struct!(TypeRef, value, is_optional, attributes); - -#[derive(Clone, Debug)] -#[repr(usize)] -pub enum TypeRefDefinition { - Definition(EntityId), - Primitive(Primitive), - Anonymous(usize), // TODO, we don't treat this as a varuint62 when we should... -} -impl EncodeInto for &TypeRefDefinition { - fn encode_into(self, encoder: &mut Encoder) -> Result<()> { - encoder.encode(*<*const _>::from(self).cast::()); // TODO, we don't treat this as a varuint62 when we should... - match self { - TypeRefDefinition::Definition(v) => encoder.encode(v), - TypeRefDefinition::Primitive(v) => encoder.encode(v), - TypeRefDefinition::Anonymous(v) => encoder.encode(v), - }; - encoder.encode_varint(TAG_END_MARKER); - Ok(()) - } -} - -#[derive(Clone, Debug)] -pub struct Attribute { - directive: String, - args: Vec, -} -implement_encode_into_for_struct!(Attribute, directive, args); +pub mod definition_encoder; +pub mod definition_types; fn main() { // Parse the command-line input. From d54fa7aff6ac232844df716dbaa350fa3ead014f Mon Sep 17 00:00:00 2001 From: Austin Henriksen Date: Fri, 6 Mar 2026 16:07:02 -0500 Subject: [PATCH 3/8] First completed version. --- slicec/src/definition_encoder.rs | 266 --------------------- slicec/src/definition_types.rs | 81 +++---- slicec/src/main.rs | 2 +- slicec/src/slice_file_converter.rs | 360 +++++++++++++++++++++++++++++ 4 files changed, 392 insertions(+), 317 deletions(-) delete mode 100644 slicec/src/definition_encoder.rs create mode 100644 slicec/src/slice_file_converter.rs diff --git a/slicec/src/definition_encoder.rs b/slicec/src/definition_encoder.rs deleted file mode 100644 index cab57fa3..00000000 --- a/slicec/src/definition_encoder.rs +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright (c) ZeroC, Inc. - -// Pull in all the mapped compiler-definition types. -use crate::definition_types::*; - -// Pull in traits from 'slicec' so we can call their functions. -use slicec::grammar::Attributable; -use slicec::grammar::Commentable; -use slicec::grammar::Contained; -use slicec::grammar::Member; -use slicec::grammar::NamedSymbol; -use slicec::grammar::Type; -use slicec::visitor::Visitor; - -// Pull in the core compiler types, using aliases to disambiguate. -// Any type that starts with 'Compiler' is a compiler type, not a mapped type. -use slicec::grammar::Attribute as CompilerAttribute; -use slicec::grammar::CustomType as CompilerCustomType; -use slicec::grammar::Definition as CompilerDefinition; -use slicec::grammar::Dictionary as CompilerDictionary; -use slicec::grammar::DocComment as CompilerDocComment; -use slicec::grammar::Enum as CompilerEnum; -use slicec::grammar::Enumerator as CompilerEnumerator; -use slicec::grammar::Field as CompilerField; -use slicec::grammar::Interface as CompilerInterface; -use slicec::grammar::Module as CompilerModule; -use slicec::grammar::Operation as CompilerOperation; -use slicec::grammar::Parameter as CompilerParameter; -use slicec::grammar::ResultType as CompilerResultType; -use slicec::grammar::Sequence as CompilerSequence; -use slicec::grammar::Struct as CompilerStruct; -use slicec::grammar::Types as CompilerTypes; -use slicec::grammar::TypeAlias as CompilerTypeAlias; -use slicec::grammar::TypeRef as CompilerTypeRef; -use slicec::slice_file::SliceFile as CompilerSliceFile; - -pub struct AnonymousTypeEncoder { - anonymous_types: Vec, -} - -impl Visitor for AnonymousTypeEncoder { - // TODO check that this visits all the places that we care about correctly! - fn visit_type_ref(&mut self, type_ref: &CompilerTypeRef) { - match type_ref.concrete_type() { - CompilerTypes::Sequence(v) => self.anonymous_types.push(Symbol::SequenceType(v.into())), - CompilerTypes::Dictionary(v) => self.anonymous_types.push(Symbol::DictionaryType(v.into())), - CompilerTypes::ResultType(v) => self.anonymous_types.push(Symbol::ResultType(v.into())), - _ => {} - } - } -} - -fn get_entity_info_for(element: &impl Commentable) -> EntityInfo { - EntityInfo { - identifier: element.identifier().to_owned(), - attributes: get_attributes_from(element.attributes()), - comment: element.comment().map(Into::into), - } -} - -fn get_attributes_from(attributes: Vec<&CompilerAttribute>) -> Vec { - -} - -impl From<&CompilerSliceFile> for SliceFile { - fn from(slice_file: &CompilerSliceFile) -> Self { - // TODO run the symbol visitor from this spot! - - SliceFile { - path: slice_file.relative_path.clone(), - module_declaration: slice_file.module.as_ref().unwrap().borrow().into(), - attributes: get_attributes_from(slice_file.attributes()), - contents: slice_file.contents.iter().map(Into::into).collect(), - } - } -} - -impl From<&CompilerModule> for Module { - fn from(module: &CompilerModule) -> Self { - Module { - identifier: module.identifier().to_owned(), - attributes: get_attributes_from(module.attributes()), - } - } -} - -impl From<&CompilerDefinition> for Symbol { - fn from(definition: &CompilerDefinition) -> Self { - match definition { - CompilerDefinition::Struct(v) => Symbol::Struct(v.borrow().into()), - CompilerDefinition::Interface(v) => Symbol::Interface(v.borrow().into()), - CompilerDefinition::Enum(v) => Symbol::Enum(v.borrow().into()), - CompilerDefinition::CustomType(v) => Symbol::CustomType(v.borrow().into()), - CompilerDefinition::TypeAlias(v) => Symbol::TypeAlias(v.borrow().into()), - _ => panic!("TODO: remove classes and exceptions"), - } - } -} - -impl From<&CompilerStruct> for Struct { - fn from(struct_def: &CompilerStruct) -> Self { - Struct { - entity_info: get_entity_info_for(struct_def), - is_compact: struct_def.is_compact, - fields: struct_def.fields().into_iter().map(Into::into).collect(), - } - } -} - -impl From<&CompilerField> for Field { - fn from(field: &CompilerField) -> Self { - Field { - entity_info: get_entity_info_for(field), - tag: field.tag.as_ref().map(|integer| integer.value as i32), - data_type: field.data_type().into(), - } - } -} - -impl From<&CompilerInterface> for Interface { - fn from(interface: &CompilerInterface) -> Self { - let bases = interface.base_interfaces(); - - Interface { - entity_info: get_entity_info_for(interface), - bases: bases.into_iter().map(|i| i.parser_scoped_identifier()).collect(), - operations: interface.operations().into_iter().map(Into::into).collect(), - } - } -} - -impl From<&CompilerOperation> for Operation { - fn from(operation: &CompilerOperation) -> Self { - Operation { - entity_info: get_entity_info_for(operation), - is_idempotent: operation.is_idempotent, - parameters: operation.parameters().into_iter().map(Into::into).collect(), - has_streamed_parameter: operation.streamed_parameter().is_some(), - return_type: operation.return_members().into_iter().map(Into::into).collect(), - has_streamed_return: operation.streamed_return_member().is_some(), - } - } -} - -impl From<&CompilerParameter> for Field { - fn from(parameter: &CompilerParameter) -> Self { - // Check if this parameter's parent operation has a corresponding doc-comment on it. - let parameter_comment = parameter.parent().comment().and_then(|comment| { - comment.params.iter() - .find(|param_tag| param_tag.identifier.value == parameter.identifier()) - .map(|param_tag| param_tag.message) - }); - - let parameter_info = EntityInfo { - identifier: parameter.identifier().to_owned(), - attributes: get_attributes_from(parameter.attributes()), - comment: parameter_comment, - }; - - Field { - entity_info: parameter_info, - tag: parameter.tag.as_ref().map(|integer| integer.value as i32), - data_type: parameter.data_type().into(), - } - } -} - -impl From<&CompilerEnum> for Enum { - fn from(enum_def: &CompilerEnum) -> Self { - Enum { - entity_info: get_entity_info_for(enum_def), - is_compact: enum_def.is_compact, - is_unchecked: enum_def.is_unchecked, - underlying: enum_def.underlying.as_ref().map(|type_ref| type_ref.type_string()), - enumerators: enum_def.enumerators().into_iter().map(Into::into).collect(), - } - } -} - -impl From<&CompilerEnumerator> for Enumerator { - fn from(enumerator: &CompilerEnumerator) -> Self { - let entity_info = get_entity_info_for(enumerator); - let raw_value = enumerator.value(); - let value = Discriminant { - absolute_value: raw_value.abs() as u64, - is_positive: raw_value.is_positive(), - }; - let fields = enumerator.fields().into_iter().map(Into::into).collect(); - - Enumerator { entity_info, value, fields } - } -} - -impl From<&CompilerCustomType> for CustomType { - fn from(custom_type: &CompilerCustomType) -> Self { - CustomType { entity_info: get_entity_info_for(custom_type) } - } -} - -impl From<&CompilerTypeAlias> for TypeAlias { - fn from(type_alias: &CompilerTypeAlias) -> Self { - TypeAlias { - entity_info: get_entity_info_for(type_alias), - underlying_type: (&type_alias.underlying).into() - } - } -} - -impl From<&CompilerSequence> for SequenceType { - fn from(sequence: &CompilerSequence) -> Self { - SequenceType { - element_type: (&sequence.element_type).into(), - } - } -} - -impl From<&CompilerDictionary> for DictionaryType { - fn from(dictionary: &CompilerDictionary) -> Self { - DictionaryType { - key_type: (&dictionary.key_type).into(), - value_type: (&dictionary.value_type).into(), - } - } -} - -impl From<&CompilerResultType> for ResultType { - fn from(result_type: &CompilerResultType) -> Self { - ResultType { - success_type: (&result_type.success_type).into(), - failure_type: (&result_type.failure_type).into(), - } - } -} - -impl From<&CompilerTypeRef> for TypeRef { - fn from(type_ref: &CompilerTypeRef) -> Self { - let type_string = match type_ref.concrete_type() { - CompilerTypes::Struct(v) => v.module_scoped_identifier(), - CompilerTypes::Enum(v) => v.module_scoped_identifier(), - CompilerTypes::CustomType(v) => v.module_scoped_identifier(), - CompilerTypes::Primitive(v) => v.type_string(), - CompilerTypes::ResultType(v) => todo!(), - CompilerTypes::Sequence(v) => todo!(), - CompilerTypes::Dictionary(v) => todo!(), - - CompilerTypes::Class(_) => panic!("remove classes!"), - }; - - TypeRef { - type_id: type_string, - is_optional: type_ref.is_optional, - type_attributes: get_attributes_from(type_ref.attributes()), - } - } -} - - - - - - -impl From<&CompilerDocComment> for DocComment { - fn from(value: &CompilerDocComment) -> Self { - - } -} diff --git a/slicec/src/definition_types.rs b/slicec/src/definition_types.rs index 5ea9c06e..96470946 100644 --- a/slicec/src/definition_types.rs +++ b/slicec/src/definition_types.rs @@ -1,7 +1,7 @@ // Copyright (c) ZeroC, Inc. //! This module contains the handwritten encoding code for the Slice-compiler definitions. -//! After bootstrapping has been completed, this file will be deleted, and we will use generated definitions instead. +//! After rust-codegen has been implemented, this file will be deleted, and we will use generated definitions instead. use slice_codec::slice2::Slice2; @@ -13,11 +13,7 @@ use slice_codec::Result; /// TAG_END_MARKER must be encoded at the end of every non-compact type. const TAG_END_MARKER: i32 = -1; -/// These aliases are for consistency with the aliases in the Slice compiler definition. -pub type EntityId = String; -pub type TypeId = String; - -/// This macro implements `EncodeInto` for a Rust struct (that represents a non-compact Slice struct). +/// This macro implements `EncodeInto` for a Rust struct (which is mapped from a non-compact Slice struct). /// It encodes all the struct's fields (in definition order), followed by `TAG_END_MARKER`. /// /// It uses macro-function-syntax, and should be called like: @@ -34,29 +30,29 @@ macro_rules! implement_encode_into_for_struct { } } -// TODO implement tag encoding, so we can send DocComments along. (Doing this requires thought about the encoder API) -// This should probably be moved into the encoder anyways. -fn encode_tagged(tag: i32, value: &Option, size: u64, encoder: &mut Encoder) -> Result<()> -where - for<'a> &'a T : EncodeInto, -{ - if let Some(v) = value { - encoder.encode_varint(tag)?; - - // TODO: Reserve space to write the size after the fact. - //let size_reservation = encoder.reserve_space(4); - encoder.encode_varuint(size)?; - - // Write the actual value. - encoder.encode(v)?; - } - Ok(()) -} - // ================= // // Hand-mapped types // // ================= // +pub type EntityId = String; +pub type TypeId = String; +pub type Message = Vec; + +#[derive(Clone, Debug)] +pub struct Attribute { + pub directive: String, + pub args: Vec, +} +implement_encode_into_for_struct!(Attribute, directive, args); + +#[derive(Clone, Debug)] +pub struct TypeRef { + pub type_id: TypeId, + pub is_optional: bool, + pub type_attributes: Vec, +} +implement_encode_into_for_struct!(TypeRef, type_id, is_optional, type_attributes); + #[derive(Clone, Debug)] pub struct EntityInfo { pub identifier: String, @@ -67,7 +63,7 @@ impl EncodeInto for &EntityInfo { fn encode_into(self, encoder: &mut Encoder) -> Result<()> { encoder.encode(&self.identifier)?; encoder.encode(&self.attributes)?; - // encoder.encode_tagged(0, &self.comment, X)?; TODO add doc-comment support + // encoder.encode_tagged(0, &self.comment)?; TODO add doc-comments after adding tag encoding support. encoder.encode_varint(TAG_END_MARKER)?; Ok(()) } @@ -101,8 +97,8 @@ impl EncodeInto for &Field { // Encode the actual fields. encoder.encode(&self.entity_info)?; - if let Some(tag_value) = &self.tag { - encoder.encode(tag_value)?; + if let Some(tag_value) = self.tag { + encoder.encode_varint(tag_value)?; } encoder.encode(&self.data_type)?; encoder.encode_varint(TAG_END_MARKER)?; @@ -113,7 +109,7 @@ impl EncodeInto for &Field { #[derive(Clone, Debug)] pub struct Interface { pub entity_info: EntityInfo, - pub bases: Vec, + pub bases: Vec, pub operations: Vec, } implement_encode_into_for_struct!(Interface, entity_info, bases, operations); @@ -203,24 +199,9 @@ pub struct ResultType { } implement_encode_into_for_struct!(ResultType, success_type, failure_type); -#[derive(Clone, Debug)] -pub struct TypeRef { - pub type_id: TypeId, - pub is_optional: bool, - pub type_attributes: Vec, -} -implement_encode_into_for_struct!(TypeRef, type_id, is_optional, type_attributes); - -#[derive(Clone, Debug)] -pub struct Attribute { - pub directive: String, - pub args: Vec, -} -implement_encode_into_for_struct!(Attribute, directive, args); - #[derive(Clone, Debug)] pub struct DocComment { - pub overview: Vec, + pub overview: Message, pub see_tags: Vec, } implement_encode_into_for_struct!(DocComment, overview, see_tags); @@ -229,13 +210,13 @@ implement_encode_into_for_struct!(DocComment, overview, see_tags); #[derive(Clone, Debug)] pub enum MessageComponent { Text(String) = 0, - LinkTag(EntityId) = 1, + Link(EntityId) = 1, } impl EncodeInto for &MessageComponent { fn encode_into(self, encoder: &mut Encoder) -> Result<()> { // Write the discriminant value. - // SAFETY: this cast is guaranteed to be safe because the enum is marked with `repr(u8)`, which means we know - // the first 'field' of this type's data layout must be a u8. This lets us read without offsetting the pointer. + // SAFETY: this cast is guaranteed to be safe because the enum is marked with `repr(u8)`, so it's safe to cast + // it directly to a `u8`. unsafe { let discriminant = *<*const _>::from(self).cast::(); encoder.encode_varint(discriminant)?; @@ -244,7 +225,7 @@ impl EncodeInto for &MessageComponent { // Encode the actual value. match self { MessageComponent::Text(v) => encoder.encode(v)?, - MessageComponent::LinkTag(v) => encoder.encode(v)?, + MessageComponent::Link(v) => encoder.encode(v)?, } Ok(()) } @@ -275,7 +256,7 @@ pub enum Symbol { CustomType(CustomType) = 3, SequenceType(SequenceType) = 4, DictionaryType(DictionaryType) = 5, - ResultType(ResultType) = 6, + ResultType(ResultType) = 6, // TODO make result come before dictionary! TypeAlias(TypeAlias) = 7, } impl EncodeInto for &Symbol { diff --git a/slicec/src/main.rs b/slicec/src/main.rs index 14608bb1..1be79554 100644 --- a/slicec/src/main.rs +++ b/slicec/src/main.rs @@ -4,8 +4,8 @@ use clap::Parser; use slicec::compilation_state::CompilationState; use slicec::slice_options::SliceOptions; -pub mod definition_encoder; pub mod definition_types; +pub mod slice_file_converter; fn main() { // Parse the command-line input. diff --git a/slicec/src/slice_file_converter.rs b/slicec/src/slice_file_converter.rs new file mode 100644 index 00000000..e2f7a61c --- /dev/null +++ b/slicec/src/slice_file_converter.rs @@ -0,0 +1,360 @@ +// Copyright (c) ZeroC, Inc. + +// Pull in all the mapped compiler-definition types. +use crate::definition_types::*; + +// Pull in the core compiler types, using aliases to disambiguate. +// Any type that starts with 'Compiler' is a compiler type, not a mapped type. +use slicec::grammar::Attribute as CompilerAttribute; +use slicec::grammar::CustomType as CompilerCustomType; +use slicec::grammar::Definition as CompilerDefinition; +use slicec::grammar::Dictionary as CompilerDictionary; +use slicec::grammar::DocComment as CompilerDocComment; +use slicec::grammar::Enum as CompilerEnum; +use slicec::grammar::Enumerator as CompilerEnumerator; +use slicec::grammar::Field as CompilerField; +use slicec::grammar::Identifier as CompilerIdentifier; +use slicec::grammar::Interface as CompilerInterface; +use slicec::grammar::MessageComponent as CompilerMessageComponent; +use slicec::grammar::Operation as CompilerOperation; +use slicec::grammar::Parameter as CompilerParameter; +use slicec::grammar::ResultType as CompilerResultType; +use slicec::grammar::Sequence as CompilerSequence; +use slicec::grammar::Struct as CompilerStruct; +use slicec::grammar::Types as CompilerTypes; +use slicec::grammar::TypeAlias as CompilerTypeAlias; +use slicec::grammar::TypeRef as CompilerTypeRef; +use slicec::grammar::attributes::Allow; +use slicec::grammar::attributes::Compress; +use slicec::grammar::attributes::Deprecated; +use slicec::grammar::attributes::Oneway; +use slicec::grammar::attributes::SlicedFormat; +use slicec::grammar::attributes::Unparsed; +use slicec::slice_file::SliceFile as CompilerSliceFile; + +// Pull in traits from 'slicec' so we can call their functions. +use slicec::grammar::Attributable; +use slicec::grammar::Commentable; +use slicec::grammar::Contained; +use slicec::grammar::Entity; +use slicec::grammar::Member; +use slicec::grammar::NamedSymbol; +use slicec::grammar::Type; + + + + + + + +fn get_entity_info_for(element: &impl Commentable) -> EntityInfo { + EntityInfo { + identifier: element.identifier().to_owned(), + attributes: get_attributes_from(element.attributes()), + comment: element.comment().map(Into::into), + } +} + +fn get_doc_comment_for_parameter(parameter: &CompilerParameter) -> Option { + let operation_comment = parameter.parent().comment()?; + + // We get the parameter's doc-comment in 3 steps: + // 1) Try to find a matching '@param' tag on the operation's doc-comment. + // 2) If one was present, extract just it's `Message` field, and convert it to the mapped type. + // 3) Construct a mapped `DocComment` which contains the mapped message. + operation_comment.params.iter() + .find(|param_tag| param_tag.identifier.value == parameter.identifier()) + .map(|param_tag| param_tag.message.value.iter().map(Into::into).collect()) + .map(|message| DocComment { overview: message, see_tags: Vec::new() }) +} + +fn convert_doc_comment_link(link_result: Result<&dyn Entity, &CompilerIdentifier>) -> String { + match link_result { + Ok(entity) => entity.parser_scoped_identifier(), + Err(identifier) => identifier.value.clone(), + } +} + +fn get_attributes_from(attributes: Vec<&CompilerAttribute>) -> Vec { + attributes.into_iter().map(|attribute| Attribute { + directive: attribute.kind.directive().to_owned(), + args: get_attribute_args(attribute), + }) + .collect() +} + +// TODO this is a temporary hack because we know all the possible attributes. +// The `Attribute` API doesn't offer a way to convert parsed-arguments back to a string. +// And this entire API will be rewritten after porting slicec-cs, so no point changing it now. +fn get_attribute_args(attribute: &CompilerAttribute) -> Vec { + if let Some(unparsed) = attribute.downcast::() { + return unparsed.args.clone(); + } + + if let Some(allow) = attribute.downcast::() { + return allow.allowed_lints.clone(); + } + + if let Some(compress) = attribute.downcast::() { + let mut args = Vec::new(); + if compress.compress_args { + args.push("Args".to_owned()); + } + if compress.compress_return { + args.push("Return".to_owned()); + } + return args; + } + + if let Some(deprecated) = attribute.downcast::() { + return deprecated.reason.iter().cloned().collect(); + } + + if let Some(_) = attribute.downcast::() { + return Vec::new(); + } + + if let Some(sliced_format) = attribute.downcast::() { + let mut args = Vec::new(); + if sliced_format.sliced_args { + args.push("Args".to_owned()); + } + if sliced_format.sliced_return { + args.push("Return".to_owned()); + } + return args; + } + + panic!("Impossible attribute encountered") +} + + + + + + + + +impl From<&CompilerSliceFile> for SliceFile { + fn from(slice_file: &CompilerSliceFile) -> Self { + // Convert the slice_file's module declaration. + let module = slice_file.module.as_ref().unwrap().borrow(); + let converted_module = Module { + identifier: module.identifier().to_owned(), + attributes: get_attributes_from(module.attributes()), + }; + + // Return a converted slice file. + SliceFile { + path: slice_file.relative_path.clone(), + module_declaration: converted_module, + attributes: get_attributes_from(slice_file.attributes()), + contents: SliceFileContentsConverter::convert(&slice_file.contents), + } + } +} + +impl From<&CompilerDocComment> for DocComment { + fn from(doc_comment: &CompilerDocComment) -> Self { + let overview = doc_comment.overview.as_ref().map(|message| { + message.value.iter().map(Into::into) + }); + + let see_tags = doc_comment.see.iter().map(|tag| { + convert_doc_comment_link(tag.linked_entity()) + }); + + DocComment { + overview: overview.map_or(Vec::new(), |v| v.collect()), + see_tags: see_tags.collect(), + } + } +} + +impl From<&CompilerMessageComponent> for MessageComponent { + fn from(component: &CompilerMessageComponent) -> Self { + match component { + CompilerMessageComponent::Text(text) => MessageComponent::Text(text.clone()), + CompilerMessageComponent::Link(tag) => { + MessageComponent::Link(convert_doc_comment_link(tag.linked_entity())) + } + } + } +} + + + + + + + + + + + + +#[derive(Debug)] +pub struct SliceFileContentsConverter { + converted_contents: Vec, +} + +impl SliceFileContentsConverter { + pub fn convert(contents: &[CompilerDefinition]) -> Vec { + // Create a new converter. + let mut converter = SliceFileContentsConverter { converted_contents: Vec::new() }; + + // Iterate through the provided file's contents, and convert each of it's top-level definitions. + for definition in contents { + let converted = match definition { + CompilerDefinition::Struct(v) => Symbol::Struct(converter.convert_struct(v.borrow())), + CompilerDefinition::Interface(v) => Symbol::Interface(converter.convert_interface(v.borrow())), + CompilerDefinition::Enum(v) => Symbol::Enum(converter.convert_enum(v.borrow())), + CompilerDefinition::CustomType(v) => Symbol::CustomType(converter.convert_custom_type(v.borrow())), + CompilerDefinition::TypeAlias(v) => Symbol::TypeAlias(converter.convert_type_alias(v.borrow())), + _ => panic!("TODO: remove classes and exceptions"), + }; + converter.converted_contents.push(converted); + } + + // Return all the converted elements, consuming the converter. + converter.converted_contents + } + + fn convert_type_ref(&mut self, type_ref: &CompilerTypeRef) -> TypeRef { + TypeRef { + type_id: self.get_type_id_for(type_ref), + is_optional: type_ref.is_optional, + type_attributes: get_attributes_from(type_ref.attributes()), + } + } + + fn convert_struct(&mut self, struct_def: &CompilerStruct) -> Struct { + Struct { + entity_info: get_entity_info_for(struct_def), + is_compact: struct_def.is_compact, + fields: struct_def.fields().into_iter().map(|e| self.convert_field(e)).collect(), + } + } + + fn convert_field(&mut self, field: &CompilerField) -> Field { + Field { + entity_info: get_entity_info_for(field), + tag: field.tag.as_ref().map(|integer| integer.value as i32), + data_type: self.convert_type_ref(field.data_type()), + } + } + + fn convert_interface(&mut self, interface_def: &CompilerInterface) -> Interface { + let bases = interface_def.base_interfaces(); + + Interface { + entity_info: get_entity_info_for(interface_def), + bases: bases.into_iter().map(|i| i.module_scoped_identifier()).collect(), + operations: interface_def.operations().into_iter().map(|e| self.convert_operation(e)).collect(), + } + } + + fn convert_operation(&mut self, operation: &CompilerOperation) -> Operation { + Operation { + entity_info: get_entity_info_for(operation), + is_idempotent: operation.is_idempotent, + parameters: operation.parameters().into_iter().map(|e| self.convert_parameter(e)).collect(), + has_streamed_parameter: operation.streamed_parameter().is_some(), + return_type: operation.return_members().into_iter().map(|e| self.convert_parameter(e)).collect(), + has_streamed_return: operation.streamed_return_member().is_some(), + } + } + + fn convert_parameter(&mut self, parameter: &CompilerParameter) -> Field { + let parameter_info = EntityInfo { + identifier: parameter.identifier().to_owned(), + attributes: get_attributes_from(parameter.attributes()), + comment: get_doc_comment_for_parameter(parameter), + }; + + Field { + entity_info: parameter_info, + tag: parameter.tag.as_ref().map(|integer| integer.value as i32), + data_type: self.convert_type_ref(parameter.data_type()), + } + } + + fn convert_enum(&mut self, enum_def: &CompilerEnum) -> Enum { + Enum { + entity_info: get_entity_info_for(enum_def), + is_compact: enum_def.is_compact, + is_unchecked: enum_def.is_unchecked, + underlying: enum_def.underlying.as_ref().map(|type_ref| type_ref.type_string()), + enumerators: enum_def.enumerators().into_iter().map(|e| self.convert_enumerator(e)).collect(), + } + } + + fn convert_enumerator(&mut self, enumerator: &CompilerEnumerator) -> Enumerator { + let entity_info = get_entity_info_for(enumerator); + let raw_value = enumerator.value(); + let value = Discriminant { + absolute_value: raw_value.abs() as u64, + is_positive: raw_value.is_positive(), + }; + let fields = enumerator.fields().into_iter().map(|e| self.convert_field(e)).collect(); + + Enumerator { entity_info, value, fields } + } + + fn convert_custom_type(&mut self, custom_type: &CompilerCustomType) -> CustomType { + CustomType { entity_info: get_entity_info_for(custom_type) } + } + + fn convert_type_alias(&mut self, type_alias: &CompilerTypeAlias) -> TypeAlias { + TypeAlias { + entity_info: get_entity_info_for(type_alias), + underlying_type: self.convert_type_ref(&type_alias.underlying), + } + } + + fn convert_sequence(&mut self, sequence: &CompilerSequence) -> SequenceType { + SequenceType { + element_type: self.convert_type_ref(&sequence.element_type), + } + } + + fn convert_dictionary(&mut self, dictionary: &CompilerDictionary) -> DictionaryType { + DictionaryType { + key_type: self.convert_type_ref(&dictionary.key_type), + value_type: self.convert_type_ref(&dictionary.value_type), + } + } + + fn convert_result_type(&mut self, result_type: &CompilerResultType) -> ResultType { + ResultType { + success_type: self.convert_type_ref(&result_type.success_type), + failure_type: self.convert_type_ref(&result_type.failure_type), + } + } + + fn get_type_id_for(&mut self, type_ref: &CompilerTypeRef) -> String { + match type_ref.concrete_type() { + CompilerTypes::Struct(v) => v.module_scoped_identifier(), + CompilerTypes::Enum(v) => v.module_scoped_identifier(), + CompilerTypes::CustomType(v) => v.module_scoped_identifier(), + CompilerTypes::Primitive(v) => v.type_string(), + CompilerTypes::ResultType(v) => { + let converted_symbol = Symbol::ResultType(self.convert_result_type(v)); + self.converted_contents.push(converted_symbol); + (self.converted_contents.len() - 1).to_string() + }, + CompilerTypes::Sequence(v) => { + let converted_symbol = Symbol::SequenceType(self.convert_sequence(v)); + self.converted_contents.push(converted_symbol); + (self.converted_contents.len() - 1).to_string() + }, + CompilerTypes::Dictionary(v) => { + let converted_symbol = Symbol::DictionaryType(self.convert_dictionary(v)); + self.converted_contents.push(converted_symbol); + (self.converted_contents.len() - 1).to_string() + }, + + CompilerTypes::Class(_) => panic!("TODO: remove classes!"), + } + } +} From 2e8a82ae4d6faed79ef9520e420b490675e57df2 Mon Sep 17 00:00:00 2001 From: Austin Henriksen Date: Fri, 6 Mar 2026 16:50:24 -0500 Subject: [PATCH 4/8] The code is good enough, time to use it! --- slicec/src/definition_types.rs | 18 ++++-- slicec/src/slice_file_converter.rs | 100 ++++++++++++++--------------- 2 files changed, 62 insertions(+), 56 deletions(-) diff --git a/slicec/src/definition_types.rs b/slicec/src/definition_types.rs index 96470946..21bad872 100644 --- a/slicec/src/definition_types.rs +++ b/slicec/src/definition_types.rs @@ -19,7 +19,7 @@ const TAG_END_MARKER: i32 = -1; /// It uses macro-function-syntax, and should be called like: /// `implement_encode_into_for_struct!(struct_type_name, field1, field2, ...);` macro_rules! implement_encode_into_for_struct { - ($type_name:ty$(, $field_name:ident)*) => { + ($type_name:ty$(, $field_name:ident)*$(,)?) => { impl EncodeInto for &$type_name { fn encode_into(self, encoder: &mut Encoder) -> Result<()> { $(encoder.encode(&self.$field_name)?;)* @@ -123,7 +123,15 @@ pub struct Operation { pub return_type: Vec, pub has_streamed_return: bool, } -implement_encode_into_for_struct!(Operation, entity_info, is_idempotent, parameters, has_streamed_parameter, return_type, has_streamed_return); +implement_encode_into_for_struct!( + Operation, + entity_info, + is_idempotent, + parameters, + has_streamed_parameter, + return_type, + has_streamed_return, +); #[derive(Clone, Debug)] pub struct Enum { @@ -140,8 +148,8 @@ impl EncodeInto for &Enum { // Encode the actual fields. encoder.encode(&self.entity_info)?; - encoder.encode(&self.is_compact)?; - encoder.encode(&self.is_unchecked)?; + encoder.encode(self.is_compact)?; + encoder.encode(self.is_unchecked)?; if let Some(underlying_value) = &self.underlying { encoder.encode(underlying_value)?; } @@ -317,4 +325,4 @@ impl EncodeInto for &DiagnosticLevel { fn encode_into(self, encoder: &mut Encoder) -> Result<()> { encoder.encode(*self as u8) } -} \ No newline at end of file +} diff --git a/slicec/src/slice_file_converter.rs b/slicec/src/slice_file_converter.rs index e2f7a61c..c598ad42 100644 --- a/slicec/src/slice_file_converter.rs +++ b/slicec/src/slice_file_converter.rs @@ -1,10 +1,8 @@ // Copyright (c) ZeroC, Inc. -// Pull in all the mapped compiler-definition types. -use crate::definition_types::*; - -// Pull in the core compiler types, using aliases to disambiguate. -// Any type that starts with 'Compiler' is a compiler type, not a mapped type. +// Pull in the core compiler types using aliases to disambiguate them from the Slice-compiler definitions. +// Any type that starts with 'Compiler' is a compiler type, not a mapped Slice-compiler definition type. +#![cfg_attr(rustfmt, rustfmt_skip)] // skip the `use ... as ...` to keep them one-per-line. use slicec::grammar::Attribute as CompilerAttribute; use slicec::grammar::CustomType as CompilerCustomType; use slicec::grammar::Definition as CompilerDefinition; @@ -24,29 +22,16 @@ use slicec::grammar::Struct as CompilerStruct; use slicec::grammar::Types as CompilerTypes; use slicec::grammar::TypeAlias as CompilerTypeAlias; use slicec::grammar::TypeRef as CompilerTypeRef; -use slicec::grammar::attributes::Allow; -use slicec::grammar::attributes::Compress; -use slicec::grammar::attributes::Deprecated; -use slicec::grammar::attributes::Oneway; -use slicec::grammar::attributes::SlicedFormat; -use slicec::grammar::attributes::Unparsed; use slicec::slice_file::SliceFile as CompilerSliceFile; +// Pull in all the mapped compiler-definition types. +use crate::definition_types::*; // Pull in traits from 'slicec' so we can call their functions. -use slicec::grammar::Attributable; -use slicec::grammar::Commentable; -use slicec::grammar::Contained; -use slicec::grammar::Entity; -use slicec::grammar::Member; -use slicec::grammar::NamedSymbol; -use slicec::grammar::Type; - - - - - - +use slicec::grammar::{Attributable, Commentable, Contained, Entity, Member, NamedSymbol, Type}; +// Pull in the attribute types without aliases, since they're not ambiguous. +use slicec::grammar::attributes::{Allow, Compress, Deprecated, Oneway, SlicedFormat, Unparsed}; +/// Returns an [EntityInfo] describing the provided element. fn get_entity_info_for(element: &impl Commentable) -> EntityInfo { EntityInfo { identifier: element.identifier().to_owned(), @@ -55,6 +40,10 @@ fn get_entity_info_for(element: &impl Commentable) -> EntityInfo { } } +/// Returns a [`DocComment`] describing the provided parameter if one is present. +/// +/// In Slice, doc-comments are not allowed on parameters. Instead, you would use a '@param' tag applied to an enclosing +/// operation. But this is an implementation detail of the language, not something code-generators should deal with. fn get_doc_comment_for_parameter(parameter: &CompilerParameter) -> Option { let operation_comment = parameter.parent().comment()?; @@ -65,16 +54,21 @@ fn get_doc_comment_for_parameter(parameter: &CompilerParameter) -> Option) -> String { +/// Helper function to convert the result of `tag.linked_entity()` into an [`EntityId`]. +fn convert_doc_comment_link(link_result: Result<&dyn Entity, &CompilerIdentifier>) -> EntityId { match link_result { Ok(entity) => entity.parser_scoped_identifier(), Err(identifier) => identifier.value.clone(), } } +/// Helper function to convert a [`Vec`] of compiler-attributes to mapped-attributes. fn get_attributes_from(attributes: Vec<&CompilerAttribute>) -> Vec { attributes.into_iter().map(|attribute| Attribute { directive: attribute.kind.directive().to_owned(), @@ -110,7 +104,7 @@ fn get_attribute_args(attribute: &CompilerAttribute) -> Vec { return deprecated.reason.iter().cloned().collect(); } - if let Some(_) = attribute.downcast::() { + if attribute.downcast::().is_some() { return Vec::new(); } @@ -128,12 +122,9 @@ fn get_attribute_args(attribute: &CompilerAttribute) -> Vec { panic!("Impossible attribute encountered") } - - - - - - +// =========================== // +// Direct conversion functions // +// =========================== // impl From<&CompilerSliceFile> for SliceFile { fn from(slice_file: &CompilerSliceFile) -> Self { @@ -182,26 +173,26 @@ impl From<&CompilerMessageComponent> for MessageComponent { } } - - - - - - - - - - - +/// This struct exposes a function ([`SliceFileContentsConverter::convert`]) that converts the contents of a Slice file +/// from their AST representation, to a representation that let's them be encoded (with the Slice encoding). #[derive(Debug)] pub struct SliceFileContentsConverter { converted_contents: Vec, } impl SliceFileContentsConverter { + /// Converts the contents of SliceFile from their representation in the AST (as [`CompilerDefinition`]s), to their + /// representation in the `Compiler` Slice module (as [`Symbol`]s). + /// + /// Specifically, this iterates through the top-level definitions of a Slice-file (in definition order) converting + /// and storing them. In addition to top-level definitions, the returned [`Vec`] also contains [`Symbol`]s for each + /// anonymous type encountered while iterating. Anonymous types always appear in the returned contents _before_ + /// the [`Symbol`]s that referenced them. pub fn convert(contents: &[CompilerDefinition]) -> Vec { // Create a new converter. - let mut converter = SliceFileContentsConverter { converted_contents: Vec::new() }; + let mut converter = SliceFileContentsConverter { + converted_contents: Vec::new() + }; // Iterate through the provided file's contents, and convert each of it's top-level definitions. for definition in contents { @@ -293,7 +284,7 @@ impl SliceFileContentsConverter { let entity_info = get_entity_info_for(enumerator); let raw_value = enumerator.value(); let value = Discriminant { - absolute_value: raw_value.abs() as u64, + absolute_value: raw_value.unsigned_abs() as u64, is_positive: raw_value.is_positive(), }; let fields = enumerator.fields().into_iter().map(|e| self.convert_field(e)).collect(); @@ -302,7 +293,9 @@ impl SliceFileContentsConverter { } fn convert_custom_type(&mut self, custom_type: &CompilerCustomType) -> CustomType { - CustomType { entity_info: get_entity_info_for(custom_type) } + CustomType { + entity_info: get_entity_info_for(custom_type) + } } fn convert_type_alias(&mut self, type_alias: &CompilerTypeAlias) -> TypeAlias { @@ -332,7 +325,12 @@ impl SliceFileContentsConverter { } } - fn get_type_id_for(&mut self, type_ref: &CompilerTypeRef) -> String { + /// Returns a [TypeId] for the provided `type_ref`. This is a fully-scoped identifier for user-defined types, + /// the corresponding keyword for primitive types, and for anonymous types, we do the following: + /// 1) Recursively convert the anonymous type (and any nested types) to the mapped definition types. + /// 2) Add these directly to [Self::converted_contents] (so these types appear in the contents before their users) + /// 3) Returns its index in [Self::converted_contents] as a numeric TypeId. + fn get_type_id_for(&mut self, type_ref: &CompilerTypeRef) -> TypeId { match type_ref.concrete_type() { CompilerTypes::Struct(v) => v.module_scoped_identifier(), CompilerTypes::Enum(v) => v.module_scoped_identifier(), @@ -342,17 +340,17 @@ impl SliceFileContentsConverter { let converted_symbol = Symbol::ResultType(self.convert_result_type(v)); self.converted_contents.push(converted_symbol); (self.converted_contents.len() - 1).to_string() - }, + } CompilerTypes::Sequence(v) => { let converted_symbol = Symbol::SequenceType(self.convert_sequence(v)); self.converted_contents.push(converted_symbol); (self.converted_contents.len() - 1).to_string() - }, + } CompilerTypes::Dictionary(v) => { let converted_symbol = Symbol::DictionaryType(self.convert_dictionary(v)); self.converted_contents.push(converted_symbol); (self.converted_contents.len() - 1).to_string() - }, + } CompilerTypes::Class(_) => panic!("TODO: remove classes!"), } From aa7ec57201b58cde3c1492aa7daa0a8f6cb4ef9e Mon Sep 17 00:00:00 2001 From: Austin Henriksen Date: Fri, 6 Mar 2026 17:28:26 -0500 Subject: [PATCH 5/8] Write the encoded definitions to 'stdout' now. --- slicec/src/main.rs | 76 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/slicec/src/main.rs b/slicec/src/main.rs index 1be79554..6609a228 100644 --- a/slicec/src/main.rs +++ b/slicec/src/main.rs @@ -1,12 +1,50 @@ // Copyright (c) ZeroC, Inc. +use std::io::Write; + use clap::Parser; + +use slice_codec::encoder::Encoder; + use slicec::compilation_state::CompilationState; use slicec::slice_options::SliceOptions; pub mod definition_types; pub mod slice_file_converter; +/// Attempts to encode a set of parsed Slice files into a byte-buffer. +/// If the encoding succeeds, this returns `Ok` with the encoded bytes, +/// otherwise this returns `Err` with an error describing the failure. +fn encode_generate_code_request(parsed_files: &[slicec::slice_file::SliceFile]) -> Result, slice_codec::Error> { + // Create a buffer to encode into, and encoder over-top of it. + let mut encoding_buffer: Vec = Vec::new(); + let mut slice_encoder: Encoder<_> = (&mut encoding_buffer).into(); + + // Encode the 'operation name'. + slice_encoder.encode("generateCode")?; + + // Sort the parsed files into two groups: source files and reference files. + // We also convert from the AST representation to the Slice representation at this time. + let mut source_files = Vec::new(); + let mut reference_files = Vec::new(); + for parsed_file in parsed_files { + // Convert the Slice file from AST representation to Slice representation. + let converted_file: crate::definition_types::SliceFile = parsed_file.into(); + // Determine whether this is a source or reference file and place it accordingly. + match parsed_file.is_source { + true => source_files.push(converted_file), + false => reference_files.push(converted_file), + } + } + + // Encode the Slice-files as 2 sequences; one of source files, and one of reference files. + slice_encoder.encode(&source_files)?; + slice_encoder.encode(&reference_files)?; + + // We're done! + Ok(encoding_buffer) +} + fn main() { // Parse the command-line input. let slice_options = SliceOptions::parse(); @@ -19,13 +57,37 @@ fn main() { let updated_diagnostics = diagnostics.into_updated(&ast, &files, &slice_options); let totals = slicec::diagnostics::get_totals(&updated_diagnostics); - // Print output to stdout. - print!("Diagnostics: "); - println!("{totals:?}"); - for diagnostic in updated_diagnostics { - println!("{diagnostic:?}"); + // TODO: replace this by forking a code-gen plugin once they exist. + // For now, if there are any diagnostics, we emit those and NOT the encoded definitions. + // Code-generators can tell if it's okay to decode or not by the presence of the `"generateCode"` string. + if totals.0 + totals.1 > 0 { + // If there were diagnostics, print them to 'stdout' and don't encode the Slice definitions. + print!("Diagnostics: "); + println!("{totals:?}"); + for diagnostic in updated_diagnostics { + println!("{diagnostic:?}"); + } + } else { + // Encode the parsed Slice definitions. + let encoded_bytes = match encode_generate_code_request(&files) { + Ok(bytes) => bytes, + Err(error) => { + eprintln!("{error:?}"); + std::process::exit(11); + } + }; + + // Obtain an exclusive handle to 'stdout'. + let mut stdout = std::io::stdout().lock(); + match stdout.write_all(&encoded_bytes) { + Ok(_) => {} + Err(error) => { + eprintln!("{error:?}"); + std::process::exit(12); + } + } } - println!("{ast:?}"); - std::process::exit(i32::from(totals.1 != 0)); + // Success. + std::process::exit(0); } From 35d6d1f4ece2b116671039430b86863d025911f3 Mon Sep 17 00:00:00 2001 From: Austin Henriksen Date: Mon, 9 Mar 2026 00:20:40 -0400 Subject: [PATCH 6/8] Fix spelling failure in CI. --- .vscode/cspell.json | 1 + slicec/src/definition_types.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/cspell.json b/.vscode/cspell.json index a2df8ebc..2ebc0921 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -23,6 +23,7 @@ "nonoverlapping", "nonterminal", "peekable", + "repr", "rfind", "rsplit", "RUSTDOCFLAGS", diff --git a/slicec/src/definition_types.rs b/slicec/src/definition_types.rs index 21bad872..e0086a4c 100644 --- a/slicec/src/definition_types.rs +++ b/slicec/src/definition_types.rs @@ -1,7 +1,7 @@ // Copyright (c) ZeroC, Inc. //! This module contains the handwritten encoding code for the Slice-compiler definitions. -//! After rust-codegen has been implemented, this file will be deleted, and we will use generated definitions instead. +//! After rust code-gen has been implemented, this file will be deleted, and we will use generated definitions instead. use slice_codec::slice2::Slice2; From d210cc2ffc3f04296f011c738275b0938929533b Mon Sep 17 00:00:00 2001 From: Austin Henriksen Date: Mon, 9 Mar 2026 01:26:45 -0400 Subject: [PATCH 7/8] Self review fixes. --- slicec/src/main.rs | 8 ++++---- slicec/src/slice_file_converter.rs | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/slicec/src/main.rs b/slicec/src/main.rs index 6609a228..45fe89aa 100644 --- a/slicec/src/main.rs +++ b/slicec/src/main.rs @@ -16,9 +16,9 @@ pub mod slice_file_converter; /// If the encoding succeeds, this returns `Ok` with the encoded bytes, /// otherwise this returns `Err` with an error describing the failure. fn encode_generate_code_request(parsed_files: &[slicec::slice_file::SliceFile]) -> Result, slice_codec::Error> { - // Create a buffer to encode into, and encoder over-top of it. + // Create a buffer to encode into, and an encoder over-top of it. let mut encoding_buffer: Vec = Vec::new(); - let mut slice_encoder: Encoder<_> = (&mut encoding_buffer).into(); + let mut slice_encoder = Encoder::from(&mut encoding_buffer); // Encode the 'operation name'. slice_encoder.encode("generateCode")?; @@ -29,7 +29,7 @@ fn encode_generate_code_request(parsed_files: &[slicec::slice_file::SliceFile]) let mut reference_files = Vec::new(); for parsed_file in parsed_files { // Convert the Slice file from AST representation to Slice representation. - let converted_file: crate::definition_types::SliceFile = parsed_file.into(); + let converted_file = crate::definition_types::SliceFile::from(parsed_file); // Determine whether this is a source or reference file and place it accordingly. match parsed_file.is_source { true => source_files.push(converted_file), @@ -77,7 +77,7 @@ fn main() { } }; - // Obtain an exclusive handle to 'stdout'. + // Obtain an exclusive handle to 'stdout', and write the encoded bytes to it. let mut stdout = std::io::stdout().lock(); match stdout.write_all(&encoded_bytes) { Ok(_) => {} diff --git a/slicec/src/slice_file_converter.rs b/slicec/src/slice_file_converter.rs index c598ad42..cfffcaf9 100644 --- a/slicec/src/slice_file_converter.rs +++ b/slicec/src/slice_file_converter.rs @@ -129,6 +129,7 @@ fn get_attribute_args(attribute: &CompilerAttribute) -> Vec { impl From<&CompilerSliceFile> for SliceFile { fn from(slice_file: &CompilerSliceFile) -> Self { // Convert the slice_file's module declaration. + // TODO this crashes on an empty Slice file, we need to filter out empty files at an earlier stage. let module = slice_file.module.as_ref().unwrap().borrow(); let converted_module = Module { identifier: module.identifier().to_owned(), @@ -174,7 +175,7 @@ impl From<&CompilerMessageComponent> for MessageComponent { } /// This struct exposes a function ([`SliceFileContentsConverter::convert`]) that converts the contents of a Slice file -/// from their AST representation, to a representation that let's them be encoded (with the Slice encoding). +/// from their AST representation, to a representation that can be encoded with the Slice encoding. #[derive(Debug)] pub struct SliceFileContentsConverter { converted_contents: Vec, @@ -329,7 +330,7 @@ impl SliceFileContentsConverter { /// the corresponding keyword for primitive types, and for anonymous types, we do the following: /// 1) Recursively convert the anonymous type (and any nested types) to the mapped definition types. /// 2) Add these directly to [Self::converted_contents] (so these types appear in the contents before their users) - /// 3) Returns its index in [Self::converted_contents] as a numeric TypeId. + /// 3) Return its index in [Self::converted_contents] as a numeric TypeId. fn get_type_id_for(&mut self, type_ref: &CompilerTypeRef) -> TypeId { match type_ref.concrete_type() { CompilerTypes::Struct(v) => v.module_scoped_identifier(), From 3b7c4575c6b3135a4d3f811b3c4a80fa50be49b4 Mon Sep 17 00:00:00 2001 From: Austin Henriksen Date: Tue, 10 Mar 2026 09:37:15 -0400 Subject: [PATCH 8/8] Added missing tag-end-marker for enums with fields. --- slicec/src/definition_types.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/slicec/src/definition_types.rs b/slicec/src/definition_types.rs index e0086a4c..89b7f97e 100644 --- a/slicec/src/definition_types.rs +++ b/slicec/src/definition_types.rs @@ -235,6 +235,8 @@ impl EncodeInto for &MessageComponent { MessageComponent::Text(v) => encoder.encode(v)?, MessageComponent::Link(v) => encoder.encode(v)?, } + + encoder.encode_varint(TAG_END_MARKER)?; Ok(()) } } @@ -288,6 +290,8 @@ impl EncodeInto for &Symbol { Symbol::ResultType(v) => encoder.encode(v)?, Symbol::TypeAlias(v) => encoder.encode(v)?, } + + encoder.encode_varint(TAG_END_MARKER)?; Ok(()) } }