From e1aeb1851c0e88c6a00acf37343040ee44cd1aaf Mon Sep 17 00:00:00 2001 From: Lukas Heidemann Date: Wed, 8 Jan 2025 17:41:14 +0000 Subject: [PATCH] Import/export metadata in new representation. Moved names for compat.* constructors to model. --- hugr-core/src/export.rs | 37 +++++++------------ hugr-core/src/import.rs | 21 ++++++----- .../snapshots/model__roundtrip_call.snap | 14 ++++--- .../snapshots/model__roundtrip_const.snap | 10 ++--- hugr-model/src/v0/mod.rs | 29 +++++++++++++++ hugr-model/tests/fixtures/model-call.edn | 8 ++-- hugr-model/tests/fixtures/model-const.edn | 8 ++-- hugr-model/tests/fixtures/model-decl-exts.edn | 4 +- .../text__declarative_extensions.snap | 9 +++-- 9 files changed, 83 insertions(+), 57 deletions(-) diff --git a/hugr-core/src/export.rs b/hugr-core/src/export.rs index 289559539..4cefaf124 100644 --- a/hugr-core/src/export.rs +++ b/hugr-core/src/export.rs @@ -18,9 +18,6 @@ use std::fmt::Write; pub(crate) const OP_FUNC_CALL_INDIRECT: &str = "func.call-indirect"; const TERM_PARAM_TUPLE: &str = "param.tuple"; -const TERM_JSON: &str = "prelude.json"; -const META_DESCRIPTION: &str = "docs.description"; -const TERM_JSON_CONST: &str = "prelude.const-json"; /// Export a [`Hugr`] graph to its representation in the model. pub fn export_hugr<'a>(hugr: &'a Hugr, bump: &'a Bump) -> model::Module<'a> { @@ -558,15 +555,12 @@ impl<'a> Context<'a> { let mut meta = BumpVec::with_capacity_in(meta_len, self.bump); if let Some(description) = description { - let name = META_DESCRIPTION; let value = self.make_term(model::Term::Str(self.bump.alloc_str(description))); - meta.push(model::MetaItem { name, value }) + meta.push(self.make_term_apply(model::CORE_META_DESCRIPTION, &[value])); } for (name, value) in opdef.iter_misc() { - let name = self.bump.alloc_str(name); - let value = self.export_json_meta(value); - meta.push(model::MetaItem { name, value }); + meta.push(self.export_json_meta(name, value)); } self.bump.alloc_slice_copy(&meta) @@ -1013,7 +1007,7 @@ impl<'a> Context<'a> { let args = self .bump .alloc_slice_copy(&[runtime_type, json, extensions]); - let symbol = self.resolve_symbol(TERM_JSON_CONST); + let symbol = self.resolve_symbol(model::COMPAT_CONST_JSON); self.make_term(model::Term::ApplyFull { symbol, args }) } @@ -1052,30 +1046,21 @@ impl<'a> Context<'a> { } } - pub fn export_node_metadata( - &mut self, - metadata_map: &NodeMetadataMap, - ) -> &'a [model::MetaItem<'a>] { + pub fn export_node_metadata(&mut self, metadata_map: &NodeMetadataMap) -> &'a [model::TermId] { let mut meta = BumpVec::with_capacity_in(metadata_map.len(), self.bump); for (name, value) in metadata_map { - let name = self.bump.alloc_str(name); - let value = self.export_json_meta(value); - meta.push(model::MetaItem { name, value }); + meta.push(self.export_json_meta(name, value)); } meta.into_bump_slice() } - pub fn export_json_meta(&mut self, value: &serde_json::Value) -> model::TermId { + pub fn export_json_meta(&mut self, name: &str, value: &serde_json::Value) -> model::TermId { let value = serde_json::to_string(value).expect("json values are always serializable"); let value = self.make_term(model::Term::Str(self.bump.alloc_str(&value))); - let value = self.bump.alloc_slice_copy(&[value]); - let symbol = self.resolve_symbol(TERM_JSON); - self.make_term(model::Term::ApplyFull { - symbol, - args: value, - }) + let name = self.make_term(model::Term::Str(self.bump.alloc_str(name))); + self.make_term_apply(model::COMPAT_META_JSON, &[name, value]) } fn resolve_symbol(&mut self, name: &'a str) -> model::NodeId { @@ -1091,6 +1076,12 @@ impl<'a> Context<'a> { }), } } + + fn make_term_apply(&mut self, name: &'a str, args: &[model::TermId]) -> model::TermId { + let symbol = self.resolve_symbol(name); + let args = self.bump.alloc_slice_copy(args); + self.make_term(model::Term::ApplyFull { symbol, args }) + } } #[cfg(test)] diff --git a/hugr-core/src/import.rs b/hugr-core/src/import.rs index fffc92396..cd5968104 100644 --- a/hugr-core/src/import.rs +++ b/hugr-core/src/import.rs @@ -28,9 +28,6 @@ use itertools::Either; use smol_str::{SmolStr, ToSmolStr}; use thiserror::Error; -const TERM_JSON: &str = "prelude.json"; -const TERM_JSON_CONST: &str = "prelude.const-json"; - /// Error during import. #[derive(Debug, Clone, Error)] pub enum ImportError { @@ -174,8 +171,8 @@ impl<'a> Context<'a> { for meta_item in node_data.meta { // TODO: For now we expect all metadata to be JSON since this is how // it is handled in `hugr-core`. - let value = self.import_json_meta(meta_item.value)?; - self.hugr.set_metadata(node, meta_item.name, value); + let (name, value) = self.import_json_meta(*meta_item)?; + self.hugr.set_metadata(node, name, value); } Ok(node) @@ -1268,7 +1265,7 @@ impl<'a> Context<'a> { fn import_json_meta( &mut self, term_id: model::TermId, - ) -> Result { + ) -> Result<(&'a str, serde_json::Value), ImportError> { let (global, args) = match self.get_term(term_id)? { model::Term::Apply { symbol, args } | model::Term::ApplyFull { symbol, args } => { (symbol, args) @@ -1277,11 +1274,15 @@ impl<'a> Context<'a> { }; let global = self.get_symbol_name(*global)?; - if global != TERM_JSON { + if global != model::COMPAT_META_JSON { return Err(model::ModelError::TypeError(term_id).into()); } - let [json_arg] = args else { + let [name_arg, json_arg] = args else { + return Err(model::ModelError::TypeError(term_id).into()); + }; + + let model::Term::Str(name) = self.get_term(*name_arg)? else { return Err(model::ModelError::TypeError(term_id).into()); }; @@ -1292,7 +1293,7 @@ impl<'a> Context<'a> { let json_value = serde_json::from_str(json_str).map_err(|_| model::ModelError::TypeError(term_id))?; - Ok(json_value) + Ok((name, json_value)) } fn import_value( @@ -1312,7 +1313,7 @@ impl<'a> Context<'a> { model::Term::ApplyFull { symbol, args } => { let symbol_name = self.get_symbol_name(*symbol)?; - if symbol_name == TERM_JSON_CONST { + if symbol_name == model::COMPAT_CONST_JSON { let value = args.get(1).ok_or(model::ModelError::TypeError(term_id))?; let model::Term::Str(json) = self.get_term(*value)? else { diff --git a/hugr-core/tests/snapshots/model__roundtrip_call.snap b/hugr-core/tests/snapshots/model__roundtrip_call.snap index 7ade416d1..c9d735ba0 100644 --- a/hugr-core/tests/snapshots/model__roundtrip_call.snap +++ b/hugr-core/tests/snapshots/model__roundtrip_call.snap @@ -4,7 +4,7 @@ expression: "roundtrip(include_str!(\"../../hugr-model/tests/fixtures/model-call --- (hugr 0) -(import prelude.json) +(import compat.meta-json) (import arithmetic.int.types.int) @@ -13,18 +13,20 @@ expression: "roundtrip(include_str!(\"../../hugr-model/tests/fixtures/model-call [(@ arithmetic.int.types.int)] [(@ arithmetic.int.types.int)] (ext ?0 ... arithmetic.int) - (meta doc.description (@ prelude.json "\"This is a function declaration.\"")) - (meta doc.title (@ prelude.json "\"Callee\""))) + (meta + (@ compat.meta-json "description" "\"This is a function declaration.\"")) + (meta (@ compat.meta-json "title" "\"Callee\""))) (define-func example.caller [(@ arithmetic.int.types.int)] [(@ arithmetic.int.types.int)] (ext arithmetic.int) - (meta doc.description + (meta (@ - prelude.json + compat.meta-json + "description" "\"This defines a function that calls the function which we declared earlier.\"")) - (meta doc.title (@ prelude.json "\"Caller\"")) + (meta (@ compat.meta-json "title" "\"Caller\"")) (dfg [%0] [%1] (signature diff --git a/hugr-core/tests/snapshots/model__roundtrip_const.snap b/hugr-core/tests/snapshots/model__roundtrip_const.snap index 6b6bc1464..2b934fb46 100644 --- a/hugr-core/tests/snapshots/model__roundtrip_const.snap +++ b/hugr-core/tests/snapshots/model__roundtrip_const.snap @@ -4,7 +4,7 @@ expression: "roundtrip(include_str!(\"../../hugr-model/tests/fixtures/model-cons --- (hugr 0) -(import prelude.const-json) +(import compat.const-json) (import arithmetic.float.types.float64) @@ -34,12 +34,12 @@ expression: "roundtrip(include_str!(\"../../hugr-model/tests/fixtures/model-cons (tag 0 [(@ - prelude.const-json + compat.const-json (@ arithmetic.float.types.float64) "{\"c\":\"ConstF64\",\"v\":{\"value\":2.0}}" (ext arithmetic.float.types)) (@ - prelude.const-json + compat.const-json (@ arithmetic.float.types.float64) "{\"c\":\"ConstF64\",\"v\":{\"value\":3.0}}" (ext arithmetic.float.types))]) @@ -63,7 +63,7 @@ expression: "roundtrip(include_str!(\"../../hugr-model/tests/fixtures/model-cons (ext))) (const (@ - prelude.const-json + compat.const-json (@ arithmetic.float.types.float64) "{\"c\":\"ConstF64\",\"v\":{\"value\":1.0}}" (ext arithmetic.float.types)) @@ -71,7 +71,7 @@ expression: "roundtrip(include_str!(\"../../hugr-model/tests/fixtures/model-cons (signature (-> [] [(@ arithmetic.float.types.float64)] (ext)))) (const (@ - prelude.const-json + compat.const-json (@ arithmetic.float.types.float64) "{\"c\":\"ConstUnknown\",\"v\":{\"value\":1.0}}" (ext)) diff --git a/hugr-model/src/v0/mod.rs b/hugr-model/src/v0/mod.rs index d7c90cd93..9e924e2c4 100644 --- a/hugr-model/src/v0/mod.rs +++ b/hugr-model/src/v0/mod.rs @@ -90,6 +90,35 @@ use smol_str::SmolStr; use thiserror::Error; +/// Constructor for documentation metadata. +/// +/// - **Parameter:** `?description : str` +/// - **Result:** `meta` +pub const CORE_META_DESCRIPTION: &str = "core.meta.description"; + +/// Constructor for JSON encoded metadata. +/// +/// This is included in the model to allow for compatibility with `hugr-core`. +/// The intention is to deprecate this in the future in favor of metadata +/// expressed with custom constructors. +/// +/// - **Parameter:** `?name : str` +/// - **Parameter:** `?json : str` +/// - **Result:** `meta` +pub const COMPAT_META_JSON: &str = "compat.meta-json"; + +/// Constructor for JSON encoded constants. +/// +/// This is included in the model to allow for compatibility with `hugr-core`. +/// The intention is to deprecate this in the future in favor of constants +/// expressed with custom constructors. +/// +/// - **Parameter:** `?type : type` +/// - **Parameter:** `?json : str` +/// - **Parameter:** `?exts : ext-set` +/// - **Result:** `(const ?type ?exts)` +pub const COMPAT_CONST_JSON: &str = "compat.const-json"; + pub mod binary; pub mod scope; pub mod text; diff --git a/hugr-model/tests/fixtures/model-call.edn b/hugr-model/tests/fixtures/model-call.edn index 839fb278a..c757658fc 100644 --- a/hugr-model/tests/fixtures/model-call.edn +++ b/hugr-model/tests/fixtures/model-call.edn @@ -3,13 +3,13 @@ (declare-func example.callee (forall ?ext ext-set) [(@ arithmetic.int.types.int)] [(@ arithmetic.int.types.int)] (ext arithmetic.int ?ext ...) - (meta doc.title (prelude.json "\"Callee\"")) - (meta doc.description (prelude.json "\"This is a function declaration.\""))) + (meta (compat.meta-json "title" "\"Callee\"")) + (meta (compat.meta-json "description" "\"This is a function declaration.\""))) (define-func example.caller [(@ arithmetic.int.types.int)] [(@ arithmetic.int.types.int)] (ext arithmetic.int) - (meta doc.title (prelude.json "\"Caller\"")) - (meta doc.description (prelude.json "\"This defines a function that calls the function which we declared earlier.\"")) + (meta (compat.meta-json "title" "\"Caller\"")) + (meta (compat.meta-json "description" "\"This defines a function that calls the function which we declared earlier.\"")) (dfg [%3] [%4] (signature (-> [(@ arithmetic.int.types.int)] [(@ arithmetic.int.types.int)] (ext))) (call (@ example.callee (ext)) [%3] [%4] diff --git a/hugr-model/tests/fixtures/model-const.edn b/hugr-model/tests/fixtures/model-const.edn index 025e77043..5e66a3c11 100644 --- a/hugr-model/tests/fixtures/model-const.edn +++ b/hugr-model/tests/fixtures/model-const.edn @@ -24,8 +24,8 @@ (const (tag 0 - [(@ prelude.const-json (@ arithmetic.float.types.float64) "{\"c\":\"ConstF64\",\"v\":{\"value\":2.0}}" (ext)) - (@ prelude.const-json (@ arithmetic.float.types.float64) "{\"c\":\"ConstF64\",\"v\":{\"value\":3.0}}" (ext))]) + [(@ compat.const-json (@ arithmetic.float.types.float64) "{\"c\":\"ConstF64\",\"v\":{\"value\":2.0}}" (ext)) + (@ compat.const-json (@ arithmetic.float.types.float64) "{\"c\":\"ConstF64\",\"v\":{\"value\":3.0}}" (ext))]) [] [%pair] (signature (-> @@ -40,11 +40,11 @@ (dfg [] [%0 %1] (signature (-> [] [(@ arithmetic.float.types.float64) (@ arithmetic.float.types.float64)] (ext))) (const - (@ prelude.const-json (@ arithmetic.float.types.float64) "{\"c\":\"ConstF64\",\"v\":{\"value\":1.0}}" (ext)) + (@ compat.const-json (@ arithmetic.float.types.float64) "{\"c\":\"ConstF64\",\"v\":{\"value\":1.0}}" (ext)) [] [%0] (signature (-> [] [(@ arithmetic.float.types.float64)] (ext)))) ; The following const is to test that import/export can deal with unknown constants. (const - (@ prelude.const-json (@ arithmetic.float.types.float64) "{\"c\":\"ConstUnknown\",\"v\":{\"value\":1.0}}" (ext)) + (@ compat.const-json (@ arithmetic.float.types.float64) "{\"c\":\"ConstUnknown\",\"v\":{\"value\":1.0}}" (ext)) [] [%1] (signature (-> [] [(@ arithmetic.float.types.float64)] (ext)))))) diff --git a/hugr-model/tests/fixtures/model-decl-exts.edn b/hugr-model/tests/fixtures/model-decl-exts.edn index bee0d68fc..253f480db 100644 --- a/hugr-model/tests/fixtures/model-decl-exts.edn +++ b/hugr-model/tests/fixtures/model-decl-exts.edn @@ -4,10 +4,10 @@ (param ?t type) (param ?n nat) type - (meta docs.description "Fixed size array.")) + (meta (core.meta.description "Fixed size array."))) (declare-operation array.Init (param ?t type) (param ?n nat) (-> [?t] [(array.Array ?t ?n)] (ext array)) - (meta docs.description "Initialize an array of size ?n with copies of a default value.")) + (meta (core.meta.description "Initialize an array of size ?n with copies of a default value."))) diff --git a/hugr-model/tests/snapshots/text__declarative_extensions.snap b/hugr-model/tests/snapshots/text__declarative_extensions.snap index 852c5bdac..40f7cbeb1 100644 --- a/hugr-model/tests/snapshots/text__declarative_extensions.snap +++ b/hugr-model/tests/snapshots/text__declarative_extensions.snap @@ -8,11 +8,14 @@ expression: "roundtrip(include_str!(\"fixtures/model-decl-exts.edn\"))" (param ?t type) (param ?n nat) type - (meta docs.description "Fixed size array.")) + (meta (core.meta.description "Fixed size array."))) (declare-operation array.Init (param ?t type) (param ?n nat) (-> [?t] [(array.Array ?t ?n)] (ext array)) - (meta docs.description - "Initialize an array of size ?n with copies of a default value.")) + (meta + (core.meta.description + "Initialize an array of size ?n with copies of a default value."))) + +(import core.meta.description)