diff --git a/bitcoind-tests/tests/common/test.rs b/bitcoind-tests/tests/common/test.rs index da4143a4..b2ba0856 100644 --- a/bitcoind-tests/tests/common/test.rs +++ b/bitcoind-tests/tests/common/test.rs @@ -15,7 +15,7 @@ type FnWitness = fn([u8; 32]) -> simplicityhl::WitnessValues; pub struct TestCase<'a> { pub name: &'static str, - template: Option, + template: Option, compiled: Option, witness: FnWitness, lock_time: elements::LockTime, @@ -67,7 +67,7 @@ impl<'a> TestCase<'a> { pub fn template_path>(mut self, path: P) -> Self { let text = std::fs::read_to_string(path).expect("path should be readable"); let template = - simplicityhl::TemplateProgram::new(text.as_str(), Box::new(ElementsJetHinter::new())) + simplicityhl::TemplateAst::new(text.as_str(), Box::new(ElementsJetHinter::new())) .expect("program should compile"); self.template = Some(template); self diff --git a/external-jet-lib-example/README.md b/external-jet-lib-example/README.md index b2055d7e..a9b34559 100644 --- a/external-jet-lib-example/README.md +++ b/external-jet-lib-example/README.md @@ -30,12 +30,12 @@ unsafe { ### 4. Pass `ExternalJetHinter` to the compiler -`ExternalJetHinter` implements `JetHinter` and delegates `parse_jet` / `construct_verify` to the loaded library. Pass it when constructing a `TemplateProgram`: +`ExternalJetHinter` implements `JetHinter` and delegates `parse_jet` / `construct_verify` to the loaded library. Pass it when constructing a `TemplateAst`: ```rust -use simplicityhl::{jet::external::ExternalJetHinter, TemplateProgram}; +use simplicityhl::{jet::external::ExternalJetHinter, TemplateAst}; -let program = TemplateProgram::new(simf_code, Box::new(ExternalJetHinter::new())) +let program = TemplateAst::new(simf_code, Box::new(ExternalJetHinter::new())) .expect("compilation failed"); ``` diff --git a/external-jet-lib-example/src/main.rs b/external-jet-lib-example/src/main.rs index c4389271..1ff0eab3 100644 --- a/external-jet-lib-example/src/main.rs +++ b/external-jet-lib-example/src/main.rs @@ -19,7 +19,7 @@ //! loads and runs native code from the given path. Only point it at libraries //! you trust. -use simplicityhl::{jet::external::ExternalJetHinter, TemplateProgram}; +use simplicityhl::{jet::external::ExternalJetHinter, TemplateAst}; /// Loads the external jet library named on the command line and compiles a tiny /// SimplicityHL program against it. @@ -45,7 +45,7 @@ fn main() { // (`parse_jet`, `construct_verify`, `conjure`) to the loaded library; here // `assert!(true)` is lowered via `construct_verify` to the library's // `verify` jet. - let _ = TemplateProgram::new(code, Box::new(ExternalJetHinter::new())) + let _ = TemplateAst::new(code, Box::new(ExternalJetHinter::new())) .expect("failed to compile code with external jets"); println!("External jets were successfully used to compile:\n{}", code); diff --git a/fuzz/fuzz_targets/compile_parse_tree.rs b/fuzz/fuzz_targets/compile_parse_tree.rs index a4e89c8c..f5b6dfb9 100644 --- a/fuzz/fuzz_targets/compile_parse_tree.rs +++ b/fuzz/fuzz_targets/compile_parse_tree.rs @@ -5,7 +5,7 @@ fn do_test(data: &[u8]) { use arbitrary::Arbitrary; use simplicityhl::ast::ElementsJetHinter; - use simplicityhl::{ast, named, parse, ArbitraryOfType, Arguments}; + use simplicityhl::{ast, named, parse, ArbitraryOfType, Arguments, WitnessNameToValueMap as _}; let mut u = arbitrary::Unstructured::new(data); let parse_program = match parse::Program::arbitrary(&mut u) { @@ -22,8 +22,13 @@ fn do_test(data: &[u8]) { Err(..) => return, }; let simplicity_named_construct = ast_program - .compile(arguments, false, Box::new(ElementsJetHinter::new())) - .expect("AST should compile with given arguments"); + .compile( + arguments.shallow_clone(), + false, + Box::new(ElementsJetHinter::new()), + ) + .expect("AST should compile with given arguments") + .instantiate(arguments); let _simplicity_commit = named::forget_names(&simplicity_named_construct); } diff --git a/fuzz/fuzz_targets/compile_text.rs b/fuzz/fuzz_targets/compile_text.rs index adcac6c6..89aed900 100644 --- a/fuzz/fuzz_targets/compile_text.rs +++ b/fuzz/fuzz_targets/compile_text.rs @@ -40,13 +40,11 @@ fn do_test(data: &[u8]) -> libfuzzer_sys::Corpus { if slow_input(&program_text) { return Corpus::Reject; } - let template = match simplicityhl::TemplateProgram::new( - program_text, - Box::new(ElementsJetHinter::new()), - ) { - Ok(x) => x, - Err(..) => return Corpus::Keep, - }; + let template = + match simplicityhl::TemplateAst::new(program_text, Box::new(ElementsJetHinter::new())) { + Ok(x) => x, + Err(..) => return Corpus::Keep, + }; let arguments = match Arguments::arbitrary_of_type(&mut u, template.parameters()) { Ok(arguments) => arguments, Err(..) => return Corpus::Reject, diff --git a/src/ast.rs b/src/ast.rs index e3a084cb..7ba62e9c 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -14,13 +14,14 @@ use crate::jet::{source_type, target_type, JetHL}; use crate::num::{NonZeroPow2Usize, Pow2Usize}; use crate::parse::{MatchPattern, UseDecl, Visibility}; use crate::pattern::Pattern; -use crate::str::{AliasName, FunctionName, Identifier, ModuleName, SymbolName, WitnessName}; +use crate::str::{AliasName, FunctionName, Identifier, ModuleName, SymbolName}; use crate::types::{ AliasedType, EnumInfo, EnumVariantInfo, ResolvedType, StructuralType, TypeConstructible, TypeDeconstructible, TypeInner, UIntType, }; use crate::value::{UIntValue, Value}; use crate::witness::{Parameters, WitnessTypes}; +use crate::TemplateProgramWitness; use crate::{impl_eq_hash, parse}; /// A program consists of the main function. @@ -227,9 +228,9 @@ pub enum SingleExpressionInner { /// Constant value. Constant(Value), /// Witness value. - Witness(WitnessName), + Witness(TemplateProgramWitness), /// Parameter value. - Parameter(WitnessName), + Parameter(TemplateProgramWitness), /// Variable that has been assigned a value. Variable(Identifier), /// Expression in parentheses. @@ -725,8 +726,8 @@ struct Scope { /// Block-level variable scopes. Push on block enter, pop on block exit. variables: Vec>, - parameters: HashMap, - witnesses: HashMap, + parameters: HashMap, + witnesses: HashMap, /// Allow enum constructions to name an enum by its declared name even /// when that name is not an alias in scope. Enabled only for value /// parsing (witness and argument files), which runs without a scope. @@ -878,7 +879,7 @@ impl Scope { /// * May also return errors propagated from item collection and insertion, such as [`Error::PrivateItem`] or [`Error::RedefinedItem`]. pub fn resolve_use(&mut self, use_decl: &UseDecl) -> Result<(), Error> { let path = use_decl.path(); - if path.first().map(|id| id.as_inner()) != Some(CRATE_STR) { + if path.first().map(|id| id.as_str()) != Some(CRATE_STR) { return Err(Error::MissingCrateKeyword); } @@ -898,13 +899,13 @@ impl Scope { .module_path .iter() .zip(&path[1..]) - .take_while(|(curr, nav)| curr.as_inner() == nav.as_inner()) + .take_while(|(curr, nav)| curr.as_str() == nav.as_str()) .count(); let mut target_scope = &self.root; for (ind, segment) in path[1..].iter().enumerate() { - let name = ModuleName::from_str_unchecked(segment.as_inner()); + let name = ModuleName::from_ident(segment); let (inner, visibility) = target_scope .submodules @@ -920,7 +921,7 @@ impl Scope { let mut collected = Vec::with_capacity(use_decl_items.len()); for (name, aliased) in use_decl_items { - if aliased.as_ref().is_some_and(|a| a.as_inner() == MAIN_STR) { + if aliased.as_ref().is_some_and(|a| a == MAIN_STR) { return Err(Error::MainCannotBeAlias); } @@ -1127,7 +1128,7 @@ impl Scope { ) -> Result<(), Error> { self.check_alias_free(&name)?; - let info = EnumInfo::new(Arc::from(name.as_inner()), variants); + let info = EnumInfo::new(Arc::clone(name.as_inner()), variants); let resolved = ResolvedType::enumeration(info); self.current_module_mut() @@ -1142,7 +1143,11 @@ impl Scope { /// ## Errors /// /// * [`Error::ExpressionTypeMismatch`] A parameter of the same name has already been defined as a different type. - pub fn insert_parameter(&mut self, name: WitnessName, ty: ResolvedType) -> Result<(), Error> { + pub fn insert_parameter( + &mut self, + name: TemplateProgramWitness, + ty: ResolvedType, + ) -> Result<(), Error> { match self.parameters.entry(name.clone()) { Entry::Occupied(entry) if entry.get() == &ty => Ok(()), Entry::Occupied(entry) => Err(Error::ExpressionTypeMismatch { @@ -1162,7 +1167,11 @@ impl Scope { /// /// * [`Error::WitnessOutsideMain`] The current scope is not inside the main function. /// * [`Error::WitnessReused`] A witness with the same name has already been defined. - pub fn insert_witness(&mut self, name: WitnessName, ty: ResolvedType) -> Result<(), Error> { + pub fn insert_witness( + &mut self, + name: TemplateProgramWitness, + ty: ResolvedType, + ) -> Result<(), Error> { if !self.is_main { return Err(Error::WitnessOutsideMain); } @@ -1402,7 +1411,7 @@ impl AbstractSyntaxTree for Function { "Variables live only inside the function" ); - if from.name().as_inner() != MAIN_STR { + if from.name() != MAIN_STR { let params = from .params() .iter() @@ -1554,7 +1563,7 @@ fn analyze_enum_construction( let written = construction.enum_path_string(); let names_expected_enum = match construction.enum_path() { [single] => { - let alias = AliasName::from_str_unchecked(single.as_inner()); + let alias = AliasName::from_ident(single); match scope.get_alias(&alias) { Ok(resolved) if &resolved == ty => true, Ok(resolved) => { @@ -1578,7 +1587,7 @@ fn analyze_enum_construction( let (variant_index, variant) = info .variant(construction.variant()) - .ok_or_else(|| enum_variant_error(construction.variant().as_inner(), info)) + .ok_or_else(|| enum_variant_error(construction.variant().as_str(), info)) .with_span(span)?; if construction.args().len() != variant.payload().len() { return Err(Error::Grammar { @@ -1904,7 +1913,7 @@ impl AbstractSyntaxTree for EnumMatch { }) .with_span(span); }; - let alias = AliasName::from_str_unchecked(single.as_inner()); + let alias = AliasName::from_ident(single); let enum_ty = scope.get_alias(&alias).with_span(span)?; let info = match enum_ty.as_enum() { Some(info) => info.clone(), @@ -2977,9 +2986,9 @@ mod module_tests { "main.simf", " pub fn global_func() {} - mod inner { - use crate::global_func; - pub fn call_it() { global_func(); } + mod inner { + use crate::global_func; + pub fn call_it() { global_func(); } } fn main() {} ", @@ -3021,11 +3030,11 @@ mod module_tests { let result = analyze_multifile(vec![( "main.simf", " - mod brother { + mod brother { fn secret_toy() {} // Missing 'pub' } - mod sister { - use crate::brother::secret_toy; + mod sister { + use crate::brother::secret_toy; } fn main() {} ", @@ -3041,8 +3050,8 @@ mod module_tests { let result = analyze_multifile(vec![( "main.simf", " - mod child { - fn hidden() {} + mod child { + fn hidden() {} } use crate::child::hidden; fn main() {} @@ -3080,10 +3089,10 @@ mod module_tests { #[cfg(test)] mod enum_tests { use crate::ast::ElementsJetHinter; - use crate::{TemplateProgram, UnstableFeatures}; + use crate::{TemplateAst, UnstableFeatures}; fn analyze(src: &str) -> Result<(), String> { - TemplateProgram::new_with_unstable( + TemplateAst::new_with_unstable( src, &UnstableFeatures::all(), Box::new(ElementsJetHinter::new()), @@ -3541,7 +3550,7 @@ mod enum_tests { fn alias_named_after_pattern_stays_valid_without_enums() { // Stable programs may alias pattern names; the enums feature must // not retroactively reject them. - let result = TemplateProgram::new_with_unstable( + let result = TemplateAst::new_with_unstable( "type Left = u32;\nfn main() { let _x: Left = 1; }", &UnstableFeatures::none(), Box::new(ElementsJetHinter::new()), @@ -3596,7 +3605,7 @@ mod enum_tests { #[test] fn enum_requires_unstable_feature() { - let result = TemplateProgram::new_with_unstable( + let result = TemplateAst::new_with_unstable( "enum Color { Red, Green }\nfn main() {}", &UnstableFeatures::none(), Box::new(ElementsJetHinter::new()), diff --git a/src/compile/mod.rs b/src/compile/mod.rs index ed16801a..02ac4468 100644 --- a/src/compile/mod.rs +++ b/src/compile/mod.rs @@ -19,11 +19,10 @@ use crate::error::{Diagnostic, Error, Span, WithSpan}; use crate::named::{self, CoreExt, PairBuilder}; use crate::num::{NonZeroPow2Usize, Pow2Usize}; use crate::pattern::{BasePattern, Pattern}; -use crate::str::WitnessName; +use crate::template_program::{TemplateProgram, TemplateProgramWitness}; use crate::types::{StructuralType, TypeDeconstructible}; -use crate::value::StructuralValue; -use crate::witness::Arguments; -use crate::Value; +use crate::value::{StructuralValue, Value}; +use crate::witness::{Arguments, WitnessNameToValueMap as _}; type ProgNode<'brand> = Arc>; @@ -217,7 +216,7 @@ impl<'brand> Scope<'brand> { } } - pub fn get_argument(&self, name: &WitnessName) -> &Value { + pub fn get_argument(&self, name: &TemplateProgramWitness) -> &Value { self.arguments .get(name) .expect("Precondition: Arguments are consistent with parameters") @@ -266,7 +265,7 @@ impl Program { arguments: Arguments, include_debug_symbols: bool, jet_hinter: Box, - ) -> Result, Diagnostic> { + ) -> Result { types::Context::with_context(|ctx| { let mut scope = Scope::new( ctx, @@ -276,11 +275,8 @@ impl Program { jet_hinter, ); - let main = self.main(); - let construct = main.compile(&mut scope).map(PairBuilder::build)?; - // SimplicityHL types should be correct by construction. If not, assign the - // whole main function as the span for them, which is as sensible as anything. - named::finalize_types(&construct).with_span(main) + let construct = self.main().compile(&mut scope).map(PairBuilder::build)?; + Ok(TemplateProgram::from_construct_node(&construct)) }) } } diff --git a/src/driver/resolve_order.rs b/src/driver/resolve_order.rs index 32ec6c1b..31228798 100644 --- a/src/driver/resolve_order.rs +++ b/src/driver/resolve_order.rs @@ -22,7 +22,7 @@ fn enum_declarations(items: &[parse::Item]) -> Vec<&parse::EnumDeclaration> { /// Enums by design are nominative, therefore to reason about same named enums in different modules /// we have to have a stable ABI with the suport of "qualified name". /// Currently, there is no support of "qualified name" concpet, therefore at the time of creating -/// enums, it is forbidden to decler them in dependencies. +/// enums, it is forbidden to decler them in dependencies. /// /// If we used current ABI we would face following problems: /// 1. Adding or removing an unrelated dependency renumbers the files, so the same enum's ABI @@ -102,9 +102,9 @@ impl DependencyGraph { .collect(); if source_id == MAIN_MODULE { - let has_main = local_items.iter().any(|item| { - matches!(item, parse::Item::Function(f) if f.name().as_inner() == MAIN_STR) - }); + let has_main = local_items + .iter() + .any(|item| matches!(item, parse::Item::Function(f) if f.name() == MAIN_STR)); if !has_main { diagnostics.push(Diagnostic::global(Error::CannotParse { @@ -119,9 +119,9 @@ impl DependencyGraph { // entry file included — in a generated module, but enum // declarations are only valid at the top level of a file, so // flattening an enum program produces source that no longer - // re-parses (`TemplateProgram::flatten`). Splice the entry + // re-parses (`TemplateAst::flatten`). Splice the entry // file's items at the root instead of wrapping them. - let name = ModuleName::from_str_unchecked(Self::get_module_name(source_id).as_inner()); + let name = ModuleName::from_ident(&Self::get_module_name(source_id)); items.push(parse::Item::Module(parse::Module::new( source_id, Visibility::Private, @@ -233,7 +233,7 @@ mod flattening_tests { .iter() .find_map(|item| { if let parse::Item::Module(m) = item { - if m.name().as_inner() == expected_mod_name.as_str() { + if *m.name() == *expected_mod_name { return Some(m); } } @@ -246,9 +246,10 @@ mod flattening_tests { "The file wrapper module must be strictly private" ); - let has_dep_func = wrapped_module.items().iter().any( - |item| matches!(item, parse::Item::Function(f) if f.name().as_inner() == "dep_func"), - ); + let has_dep_func = wrapped_module + .items() + .iter() + .any(|item| matches!(item, parse::Item::Function(f) if f.name() == "dep_func")); assert!( has_dep_func, "The file_N module must contain the dependency's items" @@ -295,14 +296,10 @@ mod flattening_tests { path.len() >= 2, "Rewritten path must have at least 2 segments" ); + assert_eq!(path[0], *CRATE_STR, "Path must start with `crate`"); assert_eq!( - path[0].as_inner(), - CRATE_STR, - "Path must start with `crate`" - ); - assert_eq!( - path[1].as_inner(), - expected_file_segment.as_str(), + path[1], + *expected_file_segment.as_str(), "Path must route through the canonical `unit_N`" ); } @@ -367,7 +364,7 @@ mod dependency_map_tests { "main.simf", " mod brother { pub fn toy() {} } - use crate::brother::toy; + use crate::brother::toy; fn main() {} ", )]); diff --git a/src/error.rs b/src/error.rs index 5384ca5e..b5b088ff 100644 --- a/src/error.rs +++ b/src/error.rs @@ -21,9 +21,10 @@ use itertools::Itertools; use crate::driver::{SourceMap, CRATE_STR, MAIN_MODULE}; use crate::lexer::Token; use crate::parse::MatchPattern; -use crate::str::{AliasName, FunctionName, Identifier, JetName, ModuleName, WitnessName}; +use crate::str::{AliasName, FunctionName, Identifier, JetName, ModuleName}; use crate::types::{ResolvedType, UIntType}; use crate::unstable::UnstableFeature; +use crate::TemplateProgramWitness; /// Area that an object spans inside a file. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] @@ -449,7 +450,7 @@ impl DiagnosticManager { // // The only caller that hits this branch is the legacy one-file program // flow, which bypasses the driver. All modern paths (LSP, `simc`, - // Simplex, and the Web build via `TemplateProgram::flatten`) register + // Simplex, and the Web build via `TemplateAst::flatten`) register // sources with the driver and hit the `RenderCache`-based render below. // // Legacy callers get message-only output — no source snippets, no @@ -864,18 +865,18 @@ pub enum Error { identifier: Identifier, }, WitnessReused { - name: WitnessName, + name: TemplateProgramWitness, }, WitnessMissing { - name: WitnessName, + name: TemplateProgramWitness, }, WitnessTypeMismatch { - name: WitnessName, + name: TemplateProgramWitness, declared: ResolvedType, assigned: ResolvedType, }, WitnessReassigned { - name: WitnessName, + name: TemplateProgramWitness, }, WitnessOutsideMain, ModuleRedefined { @@ -888,10 +889,10 @@ pub enum Error { name: ModuleName, }, ArgumentMissing { - name: WitnessName, + name: TemplateProgramWitness, }, ArgumentTypeMismatch { - name: WitnessName, + name: TemplateProgramWitness, declared: ResolvedType, assigned: ResolvedType, }, diff --git a/src/lib.rs b/src/lib.rs index eea90535..cc50f758 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ pub mod parse; pub mod pattern; pub mod resolution; pub mod source; +mod template_program; pub mod unstable; #[cfg(feature = "serde")] @@ -45,18 +46,21 @@ use crate::error::DiagnosticManager; use crate::parse::ParseFromStrWithErrors; use crate::resolution::DependencyMap; use crate::source::CanonSourceFile; +pub use crate::template_program::{TemplateProgram, TemplateProgramWitness}; pub use crate::types::ResolvedType; pub use crate::unstable::{UnstableFeature, UnstableFeatures}; pub use crate::value::Value; +pub use crate::witness::{ + Arguments, Parameters, WitnessNameToValueMap as _, WitnessTypes, WitnessValues, +}; #[cfg(feature = "serde")] -pub use crate::witness::UnresolvedValues; -pub use crate::witness::{Arguments, Parameters, WitnessTypes, WitnessValues}; +pub use crate::witness::{UnresolvedValues, WitnessNameToValueMap}; /// The template of a SimplicityHL program. /// /// A template has parameterized values that need to be supplied with arguments. #[derive(Debug)] -pub struct TemplateProgram { +pub struct TemplateAst { simfony: ast::Program, file: Arc, jet_hinter: Box, @@ -64,7 +68,7 @@ pub struct TemplateProgram { resolved_program: parse::Program, } -impl TemplateProgram { +impl TemplateAst { /// Parses and flattens a multi-file program into a single enriched [`parse::Program`] /// with all imports resolved and each file wrapped in a `unit_N` module. /// @@ -196,7 +200,7 @@ impl TemplateProgram { /// ## Errors /// /// The arguments are not consistent with the parameters of the program. - /// Use [`TemplateProgram::parameters`] to see which parameters the program has. + /// Use [`TemplateAst::parameters`] to see which parameters the program has. pub fn instantiate( &self, arguments: Arguments, @@ -212,14 +216,14 @@ impl TemplateProgram { } let commit = self.simfony.compile( - arguments, + arguments.shallow_clone(), include_debug_symbols, self.jet_hinter.clone_box(), )?; Ok(CompiledProgram { debug_symbols: self.simfony.debug_symbols(self.file.as_ref()), - simplicity: commit, + simplicity: commit.instantiate(arguments), witness_types: self.simfony.witness_types().shallow_clone(), parameter_types: self.simfony.parameters().shallow_clone(), }) @@ -259,8 +263,8 @@ impl CompiledProgram { /// /// ## See /// - /// - [`TemplateProgram::new_with_dep`] - /// - [`TemplateProgram::instantiate`] + /// - [`TemplateAst::new_with_dep`] + /// - [`TemplateAst::instantiate`] pub fn new_with_dep( source: CanonSourceFile, dependency_map: &DependencyMap, @@ -269,7 +273,7 @@ impl CompiledProgram { include_debug_symbols: bool, jet_hinter: Box, ) -> Result { - TemplateProgram::new_with_dep( + TemplateAst::new_with_dep( source, dependency_map, unstable_features, @@ -283,8 +287,8 @@ impl CompiledProgram { /// /// ## See /// - /// - [`TemplateProgram::new`] - /// - [`TemplateProgram::instantiate`] + /// - [`TemplateAst::new`] + /// - [`TemplateAst::instantiate`] pub fn new>>( s: Str, arguments: Arguments, @@ -309,7 +313,7 @@ impl CompiledProgram { include_debug_symbols: bool, jet_hinter: Box, ) -> Result { - TemplateProgram::new_with_unstable(s, unstable_features, jet_hinter.clone_box()) + TemplateAst::new_with_unstable(s, unstable_features, jet_hinter.clone_box()) .map_err(|error| error.to_string()) .and_then(|template| template.instantiate(arguments, include_debug_symbols)) } @@ -325,7 +329,7 @@ impl CompiledProgram { } /// The version of the compiler that produced this program — this crate's version. - /// See [`TemplateProgram::compiler_version`]. + /// See [`TemplateAst::compiler_version`]. pub fn compiler_version(&self) -> &'static str { version::SimcDirective::current_version() } @@ -415,8 +419,8 @@ impl SatisfiedProgram { /// /// ## See /// - /// - [`TemplateProgram::new`] - /// - [`TemplateProgram::instantiate`] + /// - [`TemplateAst::new`] + /// - [`TemplateAst::instantiate`] /// - [`CompiledProgram::satisfy`] pub fn new>>( s: Str, @@ -561,6 +565,7 @@ pub(crate) mod tests { use crate::resolution::tests::{build_map, canon}; use crate::resolution::DependencyMapBuilder; use crate::source::CanonPath; + use crate::str::Identifier; use crate::test_utils::TempWorkspace; use base64::display::Base64Display; use base64::engine::general_purpose::STANDARD; @@ -583,7 +588,7 @@ pub(crate) mod tests { Arc::from(program_text), ); - match TemplateProgram::flatten(source, dependency_map, &UnstableFeatures::all()) { + match TemplateAst::flatten(source, dependency_map, &UnstableFeatures::all()) { Ok(single_file) => single_file, Err(error) => panic!("{}", &error), } @@ -630,7 +635,7 @@ pub(crate) mod tests { include_fee_output: bool, } - impl TestCase { + impl TestCase { pub fn template_file>(program_file_path: P) -> Self { Self::template_file_with_unstable(program_file_path, UnstableFeatures::none()) } @@ -654,7 +659,7 @@ pub(crate) mod tests { Arc::from(program_text), ); - let program = match TemplateProgram::new_with_dep( + let program = match TemplateAst::new_with_dep( source, dependency_map, &unstable_features, @@ -680,7 +685,7 @@ pub(crate) mod tests { program_text: Cow, unstable_features: UnstableFeatures, ) -> Self { - let program = match TemplateProgram::new_with_unstable( + let program = match TemplateAst::new_with_unstable( program_text.as_ref(), &unstable_features, Box::new(ElementsJetHinter::new()), @@ -729,7 +734,7 @@ pub(crate) mod tests { impl TestCase { pub fn program_file>(program_file_path: P) -> Self { - TestCase::::template_file(program_file_path) + TestCase::::template_file(program_file_path) .with_arguments(Arguments::default()) } @@ -737,7 +742,7 @@ pub(crate) mod tests { program_file_path: P, unstable_features: UnstableFeatures, ) -> Self { - TestCase::::template_file_with_unstable( + TestCase::::template_file_with_unstable( program_file_path, unstable_features, ) @@ -745,7 +750,7 @@ pub(crate) mod tests { } pub fn program_text(program_text: Cow) -> Self { - TestCase::::template_text(program_text) + TestCase::::template_text(program_text) .with_arguments(Arguments::default()) } @@ -753,11 +758,8 @@ pub(crate) mod tests { program_text: Cow, unstable_features: UnstableFeatures, ) -> Self { - TestCase::::template_text_with_unstable( - program_text, - unstable_features, - ) - .with_arguments(Arguments::default()) + TestCase::::template_text_with_unstable(program_text, unstable_features) + .with_arguments(Arguments::default()) } pub fn program_file_with_deps_and_unstable( @@ -765,7 +767,7 @@ pub(crate) mod tests { dependency_map: &DependencyMap, unstable_features: UnstableFeatures, ) -> Self { - TestCase::::template_deps_with_unstable( + TestCase::::template_deps_with_unstable( prog_path.as_ref(), dependency_map, unstable_features, @@ -1077,7 +1079,7 @@ pub(crate) mod tests { let dependency_map = build_map(&canon_root, &[]).unwrap(); - TestCase::::template_deps_with_unstable( + TestCase::::template_deps_with_unstable( &main_path, &dependency_map, UnstableFeatures::all(), @@ -1090,10 +1092,10 @@ pub(crate) mod tests { #[test] fn test_anonymous_source_compiles_without_dependencies() { let code = "fn main() { assert!(true); }"; - let program = TemplateProgram::new(code, Box::new(ElementsJetHinter::new())); + let program = TemplateAst::new(code, Box::new(ElementsJetHinter::new())); assert!( program.is_ok(), - "TemplateProgram::new should successfully compile anonymous source files without requiring canonical paths" + "TemplateAst::new should successfully compile anonymous source files without requiring canonical paths" ); } @@ -1368,10 +1370,10 @@ fn main() { ]; for hinter in hinters { - let program = TemplateProgram::new(code, hinter); + let program = TemplateAst::new(code, hinter); assert!( program.is_ok(), - "TemplateProgram::new should successfully compile the same program with different jet hinters: {:?}", + "TemplateAst::new should successfully compile the same program with different jet hinters: {:?}", program.err(), ); } @@ -1387,14 +1389,14 @@ fn main() { assert!(jet::eq_32(idx, idx)); }"#; - let elements_result = TemplateProgram::new(code, Box::new(ElementsJetHinter::new())); + let elements_result = TemplateAst::new(code, Box::new(ElementsJetHinter::new())); assert!( elements_result.is_ok(), "ElementsJetHinter should compile Elements-specific jets: {:?}", elements_result.err(), ); - let core_result = TemplateProgram::new(code, Box::new(CoreJetHinter::new())); + let core_result = TemplateAst::new(code, Box::new(CoreJetHinter::new())); assert!( core_result.is_err(), "CoreJetHinter should fail to compile Elements-specific jets", @@ -1556,7 +1558,7 @@ fn main() { } } - // Smoke tests that the version check is wired into `TemplateProgram::new`: one + // Smoke tests that the version check is wired into `TemplateAst::new`: one // compatible directive compiles, one incompatible directive aborts. The semver // matching and per-kind messages are covered exhaustively in `version`'s unit // tests, so they are not re-asserted through the pipeline here. @@ -1569,15 +1571,14 @@ fn main() { .unwrap(); let compatible = format!("simc \"{version}\";\nfn main() {{}}"); assert!( - TemplateProgram::new(compatible, Box::new(crate::ast::ElementsJetHinter::new())) - .is_ok() + TemplateAst::new(compatible, Box::new(crate::ast::ElementsJetHinter::new())).is_ok() ); } /// The producing compiler's version is readable from the program objects. #[test] fn compiler_version_accessor() { - let template = TemplateProgram::new( + let template = TemplateAst::new( "fn main() {}", Box::new(crate::ast::ElementsJetHinter::new()), ) @@ -1590,7 +1591,7 @@ fn main() { #[test] fn incompatible_directive_aborts() { let too_old = "simc \">= 99.99.99\";\nfn main() {}"; - let err = TemplateProgram::new(too_old, Box::new(crate::ast::ElementsJetHinter::new())) + let err = TemplateAst::new(too_old, Box::new(crate::ast::ElementsJetHinter::new())) .unwrap_err() .to_string(); assert!( @@ -1641,8 +1642,6 @@ fn main() { #[test] #[cfg(feature = "serde")] fn enum_match_witness_file_variant_name() { - use crate::str::WitnessName; - // The witness file names the variant; resolution constructs the // enum value at the declared type. let src = "enum Action { Hot, Cold, } @@ -1665,7 +1664,7 @@ fn main() { serde_json::from_str(r#"{ "ACT": "Action::Cold" }"#).unwrap(); let witness: WitnessValues = unresolved.resolve(compiled.witness_types()).unwrap(); assert!(witness - .get(&WitnessName::from_str_unchecked("ACT")) + .get(&TemplateProgramWitness::witness_from_str("ACT")) .is_some()); TestCase::program_text_with_unstable(Cow::Borrowed(src), UnstableFeatures::all()) .with_witness_values(witness) @@ -1674,7 +1673,6 @@ fn main() { #[test] fn strict_satisfy_rejects_missing_witness() { - use crate::str::{Identifier, WitnessName}; use crate::value::ValueConstructible; use std::collections::HashMap; @@ -1697,22 +1695,22 @@ fn main() { .unwrap(); let selector_ty = compiled .witness_types() - .get(&WitnessName::from_str_unchecked("SELECTOR")) + .get(&TemplateProgramWitness::witness_from_str("SELECTOR")) .unwrap() .clone(); // Only SELECTOR and A are provided; B is omitted. The strict entry // points must reject the omitted witness rather than zero-filling it. - let mut map: HashMap = HashMap::new(); + let mut map: HashMap = HashMap::new(); map.insert( - WitnessName::from_str_unchecked("SELECTOR"), + TemplateProgramWitness::witness_from_str("SELECTOR"), Value::enum_variant(&selector_ty, &Identifier::from_str_unchecked("A"), vec![]) .unwrap(), ); - map.insert(WitnessName::from_str_unchecked("A"), Value::u32(0)); + map.insert(TemplateProgramWitness::witness_from_str("A"), Value::u32(0)); let err = compiled - .satisfy(WitnessValues::from(map.clone())) + .satisfy(WitnessValues::from_map(map)) .expect_err("satisfy must reject a missing witness"); assert!( err.contains('B'), @@ -1722,7 +1720,6 @@ fn main() { #[test] fn enum_match_dispatches_every_variant() { - use crate::str::{Identifier, WitnessName}; use std::collections::HashMap; // Three and five variants cover leaves at unequal depths @@ -1757,7 +1754,7 @@ fn main() { .unwrap(); let action_ty = compiled .witness_types() - .get(&WitnessName::from_str_unchecked("ACT")) + .get(&TemplateProgramWitness::witness_from_str("ACT")) .expect("ACT is declared") .clone(); @@ -1767,9 +1764,9 @@ fn main() { .expect("declared variant"); let expected = u32::try_from((i + 1) * 10).unwrap(); let map = HashMap::from([ - (WitnessName::from_str_unchecked("ACT"), action), + (TemplateProgramWitness::witness_from_str("ACT"), action), ( - WitnessName::from_str_unchecked("EXPECTED"), + TemplateProgramWitness::witness_from_str("EXPECTED"), crate::value::ValueConstructible::u32(expected), ), ]); @@ -1777,7 +1774,7 @@ fn main() { Cow::Owned(src.clone()), UnstableFeatures::all(), ) - .with_witness_values(WitnessValues::from(map)) + .with_witness_values(WitnessValues::from_map(map)) .assert_run_success(); } } @@ -1825,7 +1822,7 @@ mod error_tests { let dependencies = dependency_map(&root_dir, "lib", &lib_dir); - let err = TemplateProgram::new_with_dep( + let err = TemplateAst::new_with_dep( source_file(&main_path), &dependencies, &UnstableFeatures::all(), @@ -1857,7 +1854,7 @@ mod error_tests { ws.create_file("workspace/lib/base.simf", "pub fn one() -> u32 { 1 }\n"); let dependencies = dependency_map(&root_dir, "lib", &lib_dir); - let _err = TemplateProgram::new_with_dep( + let _err = TemplateAst::new_with_dep( source_file(&main_path), &dependencies, &UnstableFeatures::none(), @@ -1877,7 +1874,7 @@ mod error_tests { ); let dependencies = dependency_map(&root_dir, "lib", &lib_dir); - let err = TemplateProgram::new_with_dep( + let err = TemplateAst::new_with_dep( source_file(&main_path), &dependencies, &UnstableFeatures::all(), diff --git a/src/main.rs b/src/main.rs index 73bafd05..c37e704b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use simplicityhl::error::should_color; use simplicityhl::version::SimcDirective; use simplicityhl::{ resolution::DependencyMapBuilder, source::CanonPath, source::CanonSourceFile, AbiMeta, - TemplateProgram, + TemplateAst, }; use simplicityhl::{UnstableFeature, UnstableFeatures}; use std::path::Path; @@ -197,7 +197,7 @@ fn main() -> Result<(), Box> { }; let source = CanonSourceFile::new(main_path.clone(), std::sync::Arc::from(main_text)); - let template = match TemplateProgram::new_with_dep( + let template = match TemplateAst::new_with_dep( source, &dependencies, &unstable_features, diff --git a/src/named.rs b/src/named.rs index a62bdd9e..13ae8d25 100644 --- a/src/named.rs +++ b/src/named.rs @@ -7,16 +7,16 @@ use simplicity::node::{ use simplicity::Cmr; use simplicity::{types, FailEntropy}; -use crate::str::WitnessName; use crate::value::StructuralValue; -use crate::witness::WitnessValues; +use crate::witness::{WitnessNameToValueMap as _, WitnessValues}; +use crate::TemplateProgramWitness; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct WithNames(T); impl node::Marker for WithNames { type CachedData = M::CachedData; - type Witness = WitnessName; + type Witness = TemplateProgramWitness; // It's quite difficult to wrap M::Disconnect because of Rust's lack of HKTs, and // we don't use disconnect in this library right now, so punt on it for now. type Disconnect = NoDisconnect; @@ -84,7 +84,7 @@ where N::Witness: Nullable, F: FnMut( &Node>, - Inner<&N::CachedData, &NoDisconnect, &WitnessName>, + Inner<&N::CachedData, &NoDisconnect, &TemplateProgramWitness>, ) -> Result, { struct Translator(F); @@ -96,7 +96,7 @@ where N::Witness: Nullable, F: FnMut( &Node>, - Inner<&N::CachedData, &NoDisconnect, &WitnessName>, + Inner<&N::CachedData, &NoDisconnect, &TemplateProgramWitness>, ) -> Result, { type Error = E; @@ -104,8 +104,8 @@ where fn convert_witness( &mut self, _: &PostOrderIterItem<&Node>>, - wit: &WitnessName, - ) -> Result { + wit: &TemplateProgramWitness, + ) -> Result { Ok(wit.shallow_clone()) } @@ -121,7 +121,7 @@ where fn convert_data( &mut self, data: &PostOrderIterItem<&Node>>, - inner: Inner<&Arc>>, &NoDisconnect, &WitnessName>, + inner: Inner<&Arc>>, &NoDisconnect, &TemplateProgramWitness>, ) -> Result { let new_inner = inner.map(|node| node.cached_data()); self.0(data.node, new_inner) @@ -151,7 +151,7 @@ where fn convert_witness( &mut self, _: &PostOrderIterItem<&Node>>, - _: &WitnessName, + _: &TemplateProgramWitness, ) -> Result { Ok(M::Witness::none()) } @@ -206,7 +206,7 @@ pub fn populate_witnesses( fn convert_witness( &mut self, _: &PostOrderIterItem<&CommitNode>, - witness: &WitnessName, + witness: &TemplateProgramWitness, ) -> Result { match self.values.get(witness) { Some(val) => Ok(simplicity::Value::from(StructuralValue::from(val))), @@ -251,8 +251,8 @@ pub fn populate_witnesses( // This awkward construction is required by rust-simplicity to implement WitnessConstructible // for Node>. See // https://docs.rs/simplicity-lang/latest/simplicity/node/trait.WitnessConstructible.html#foreign-impls -impl<'brand> WitnessConstructible<'brand, WitnessName> for node::ConstructData<'brand> { - fn witness(inference_context: &types::Context<'brand>, _: WitnessName) -> Self { +impl<'brand> WitnessConstructible<'brand, TemplateProgramWitness> for node::ConstructData<'brand> { + fn witness(inference_context: &types::Context<'brand>, _: TemplateProgramWitness) -> Self { WitnessConstructible::>::witness(inference_context, None) } } @@ -602,7 +602,7 @@ impl<'brand, P: CoreExt<'brand>> PairBuilder

{ } } -impl<'brand, P: WitnessConstructible<'brand, WitnessName>> PairBuilder

{ +impl<'brand, P: WitnessConstructible<'brand, TemplateProgramWitness>> PairBuilder

{ /// Create the witness expression. /// /// ## Invariant @@ -613,7 +613,10 @@ impl<'brand, P: WitnessConstructible<'brand, WitnessName>> PairBuilder

{ /// --------------- /// witness : A → B /// ``` - pub fn witness(inference_context: &types::Context<'brand>, witness: WitnessName) -> Self { + pub fn witness( + inference_context: &types::Context<'brand>, + witness: TemplateProgramWitness, + ) -> Self { Self(P::witness(inference_context, witness)) } } diff --git a/src/parse.rs b/src/parse.rs index 547af341..dc175d02 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -27,11 +27,12 @@ use crate::num::NonZeroPow2Usize; use crate::pattern::Pattern; use crate::str::{ AliasName, Binary, Decimal, FunctionName, Hexadecimal, Identifier, JetName, ModuleName, - SymbolName, WitnessName, + SymbolName, }; use crate::types::{AliasedType, BuiltinAlias, TypeConstructible, UIntType}; use crate::unstable::{impl_require_feature, RequireFeature, UnstableFeature, UnstableFeatures}; use crate::version::SimcDirective; +use crate::TemplateProgramWitness; #[cfg(feature = "fmt")] use crate::lexer::{FmtToken, FmtTokens}; @@ -254,7 +255,7 @@ impl UseDecl { } pub fn str_path(&self) -> String { - let path: PathBuf = self.path().iter().map(|iden| iden.as_inner()).collect(); + let path: PathBuf = self.path().iter().map(Identifier::as_str).collect(); path.display().to_string() } @@ -264,7 +265,7 @@ impl UseDecl { /// /// Returns a `Diagnostic` if the use declaration path is completely empty. pub fn drp_name(&self) -> Result<&str, Diagnostic> { - let parts: Vec<&str> = self.path().iter().map(|iden| iden.as_inner()).collect(); + let parts: Vec<&str> = self.path().iter().map(Identifier::as_str).collect(); parts.first().copied().ok_or_else(|| { Error::CannotParse { msg: "Empty use path".to_string(), @@ -816,9 +817,9 @@ pub enum SingleExpressionInner { /// Hexadecimal string literal. Hexadecimal(Hexadecimal), /// Witness value. - Witness(WitnessName), + Witness(TemplateProgramWitness), /// Parameter value. - Parameter(WitnessName), + Parameter(TemplateProgramWitness), /// Variable identifier expression Variable(Identifier), /// Function call @@ -970,7 +971,7 @@ impl EnumMatchArm { pub fn enum_path_string(&self) -> String { self.enum_path .iter() - .map(Identifier::as_inner) + .map(Identifier::as_str) .collect::>() .join("::") } @@ -1056,7 +1057,7 @@ impl EnumConstruction { pub fn enum_path_string(&self) -> String { self.enum_path .iter() - .map(Identifier::as_inner) + .map(Identifier::as_str) .collect::>() .join("::") } @@ -1658,7 +1659,6 @@ macro_rules! impl_parse_wrapped_string { impl_parse_wrapped_string!(SymbolName, "unresolved symbol name"); impl_parse_wrapped_string!(FunctionName, "function name"); impl_parse_wrapped_string!(Identifier, "identifier"); -impl_parse_wrapped_string!(WitnessName, "witness name"); impl_parse_wrapped_string!(AliasName, "alias name"); impl_parse_wrapped_string!(ModuleName, "module name"); @@ -2477,7 +2477,7 @@ impl ChumskyParse for TypeAlias { let name = AliasName::parser() .validate(|name, e, emit| { - let ident = name.as_inner(); + let ident = name.as_str(); let known_type = if ident == "bool" { Some(AliasedType::boolean()) } else if let Ok(uint_type) = UIntType::from_str(ident) { @@ -2529,7 +2529,7 @@ impl ChumskyParse for EnumDeclaration { .map(Option::unwrap_or_default); let name = AliasName::parser().try_map(|name, span| { - if RESERVED_PATTERN_NAMES.contains(&name.as_inner()) { + if RESERVED_PATTERN_NAMES.contains(&name.as_str()) { return Err(Diagnostic::new( Error::Grammar { msg: format!( @@ -2545,7 +2545,7 @@ impl ChumskyParse for EnumDeclaration { // constructions name the enum while type annotations resolve // to the builtin, and the ABI would report the bare name // ambiguously. - if crate::str::is_reserved_alias_name(name.as_inner()) { + if crate::str::is_reserved_alias_name(name.as_str()) { return Err(Diagnostic::new( Error::RedefinedAliasAsBuiltin { name: name.clone() }, span, @@ -2710,8 +2710,8 @@ impl SingleExpression { Token::DecLiteral(s) => SingleExpressionInner::Decimal(s), Token::HexLiteral(s) => SingleExpressionInner::Hexadecimal(s), Token::BinLiteral(s) => SingleExpressionInner::Binary(s), - Token::Witness(s) => SingleExpressionInner::Witness(WitnessName::from_str_unchecked(s)), - Token::Param(s) => SingleExpressionInner::Parameter(WitnessName::from_str_unchecked(s)), + Token::Witness(s) => SingleExpressionInner::Witness(TemplateProgramWitness::witness_from_str(s)), + Token::Param(s) => SingleExpressionInner::Parameter(TemplateProgramWitness::parameter_from_str(s)), }; // Enum variant construction: `Path::To::Enum::Variant(args..)`. @@ -3420,7 +3420,7 @@ impl crate::ArbitraryRec for SingleExpression { 2 => Decimal::arbitrary(u).map(S::Decimal), 3 => Hexadecimal::arbitrary(u).map(S::Hexadecimal), 4 => Identifier::arbitrary(u).map(S::Variable), - 5 => WitnessName::arbitrary(u).map(S::Witness), + 5 => TemplateProgramWitness::arbitrary(u).map(S::Witness), 6 => Ok(S::Option(None)), _ => unreachable!(), }, @@ -3430,7 +3430,7 @@ impl crate::ArbitraryRec for SingleExpression { 2 => Decimal::arbitrary(u).map(S::Decimal), 3 => Hexadecimal::arbitrary(u).map(S::Hexadecimal), 4 => Identifier::arbitrary(u).map(S::Variable), - 5 => WitnessName::arbitrary(u).map(S::Witness), + 5 => TemplateProgramWitness::arbitrary(u).map(S::Witness), 6 => Ok(S::Option(None)), 7 => Expression::arbitrary_rec(u, new_budget) .map(Arc::new) @@ -3820,10 +3820,10 @@ fn main() { let Item::EnumDeclaration(decl) = item else { panic!("expected EnumDeclaration, got {item:?}"); }; - assert_eq!(decl.name().as_inner(), "Path"); + assert_eq!(decl.name(), "Path"); assert_eq!(decl.variants().len(), 3); - assert_eq!(decl.variants()[0].name().as_inner(), "Inherit"); - assert_eq!(decl.variants()[2].name().as_inner(), "RefreshSpend"); + assert_eq!(decl.variants()[0].name(), "Inherit"); + assert_eq!(decl.variants()[2].name(), "RefreshSpend"); } #[test] @@ -3833,7 +3833,7 @@ fn main() { panic!("expected EnumDeclaration"); }; assert_eq!(decl.visibility(), &Visibility::Public); - assert_eq!(decl.name().as_inner(), "Color"); + assert_eq!(decl.name(), "Color"); } #[test] @@ -4177,10 +4177,10 @@ fn main() { let Item::EnumDeclaration(decl) = item else { panic!("expected EnumDeclaration, got {item:?}"); }; - assert_eq!(decl.name().as_inner(), "Path"); + assert_eq!(decl.name(), "Path"); assert_eq!(decl.variants().len(), 3); - assert_eq!(decl.variants()[0].name().as_inner(), "Inherit"); - assert_eq!(decl.variants()[2].name().as_inner(), "RefreshSpend"); + assert_eq!(decl.variants()[0].name(), "Inherit"); + assert_eq!(decl.variants()[2].name(), "RefreshSpend"); } #[test] @@ -4190,7 +4190,7 @@ fn main() { panic!("expected EnumDeclaration"); }; assert_eq!(decl.visibility(), &Visibility::Public); - assert_eq!(decl.name().as_inner(), "Color"); + assert_eq!(decl.name(), "Color"); } #[test] diff --git a/src/resolution.rs b/src/resolution.rs index 470f2110..9dae947f 100644 --- a/src/resolution.rs +++ b/src/resolution.rs @@ -388,7 +388,7 @@ impl DependencyMap { let mut iter = module_parts.iter(); while let Some(part) = iter.next() { - let joined = path.join(part.as_inner()); + let joined = path.join(part.as_str()); if joined.is_dir() { path = joined; continue; diff --git a/src/serde.rs b/src/serde.rs index 99bdc8f5..5e1d1643 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,25 +1,35 @@ +use core::hash::Hash; use std::collections::HashMap; use std::fmt; +use std::marker::PhantomData; -use crate::parse::ParseFromStr; -use crate::str::WitnessName; +use crate::parse::ParseFromStr as _; +use crate::str::Identifier; use crate::types::ResolvedType; use crate::value::Value; -use crate::witness::{Arguments, UnresolvedValue, UnresolvedValues, WitnessValues}; -use crate::{AbiMeta, Parameters, WitnessTypes}; +use crate::witness::{ + Arguments, UnresolvedValue, UnresolvedValues, WitnessNameToValueMap as _, WitnessValues, +}; +use crate::{AbiMeta, Parameters, TemplateProgramWitness, WitnessTypes}; use serde::{de, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer}; -/// Visitor for a map from witness names to values of type `V`, rejecting duplicate names. -struct NamedMapVisitor(std::marker::PhantomData); +/// Visitor for a map from identifiers to values of type `V`, rejecting duplicate names. +struct NamedMapVisitor { + key_map_fn: fn(&Identifier) -> K, + phantom: PhantomData, +} -impl NamedMapVisitor { - const fn new() -> Self { - Self(std::marker::PhantomData) +impl NamedMapVisitor { + const fn new(key_map_fn: fn(&Identifier) -> K) -> Self { + Self { + key_map_fn, + phantom: PhantomData, + } } } -impl<'de, V: Deserialize<'de>> de::Visitor<'de> for NamedMapVisitor { - type Value = HashMap; +impl<'de, K: Eq + Hash, V: Deserialize<'de>> de::Visitor<'de> for NamedMapVisitor { + type Value = HashMap; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a map with string keys") @@ -30,8 +40,8 @@ impl<'de, V: Deserialize<'de>> de::Visitor<'de> for NamedMapVisitor { M: de::MapAccess<'de>, { let mut map = HashMap::new(); - while let Some((key, value)) = access.next_entry::()? { - if map.insert(key.shallow_clone(), value).is_some() { + while let Some((key, value)) = access.next_entry::()? { + if map.insert((self.key_map_fn)(&key), value).is_some() { return Err(de::Error::custom(format!("Name `{key}` is assigned twice"))); } } @@ -45,8 +55,10 @@ impl<'de> Deserialize<'de> for WitnessValues { D: Deserializer<'de>, { deserializer - .deserialize_map(NamedMapVisitor::::new()) - .map(Self::from) + .deserialize_map(NamedMapVisitor::new( + TemplateProgramWitness::witness_from_ident, + )) + .map(Self::from_map) } } @@ -93,7 +105,7 @@ impl<'de> Deserialize<'de> for UnresolvedValues { D: Deserializer<'de>, { deserializer - .deserialize_map(NamedMapVisitor::::new()) + .deserialize_map(NamedMapVisitor::new(Identifier::shallow_clone)) .map(Self::from_map) } } @@ -109,15 +121,6 @@ impl Serialize for ResolvedType { } } -impl Serialize for WitnessName { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(self.as_ref()) - } -} - impl Serialize for AbiMeta { fn serialize(&self, serializer: S) -> Result where @@ -140,7 +143,7 @@ impl Serialize for Parameters { let map_ref = self.as_ref(); let mut map = serializer.serialize_map(Some(map_ref.len()))?; for (key, value) in map_ref { - map.serialize_entry(key, value)?; + map.serialize_entry(key.as_str(), value)?; } map.end() } @@ -154,7 +157,7 @@ impl Serialize for WitnessTypes { let map_ref = self.as_ref(); let mut map = serializer.serialize_map(Some(map_ref.len()))?; for (key, value) in map_ref { - map.serialize_entry(key, value)?; + map.serialize_entry(key.as_str(), value)?; } map.end() } @@ -166,8 +169,10 @@ impl<'de> Deserialize<'de> for Arguments { D: Deserializer<'de>, { deserializer - .deserialize_map(NamedMapVisitor::::new()) - .map(Self::from) + .deserialize_map(NamedMapVisitor::new( + TemplateProgramWitness::parameter_from_ident, + )) + .map(Self::from_map) } } @@ -239,33 +244,32 @@ impl<'de> Deserialize<'de> for Value { } } -struct ParserVisitor(std::marker::PhantomData); - -impl<'de, A: ParseFromStr> de::Visitor<'de> for ParserVisitor { - type Value = A; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a valid string") - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - A::parse_from_str(value).map_err(E::custom) - } -} - -impl<'de> Deserialize<'de> for WitnessName { +impl<'de> Deserialize<'de> for Identifier { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - deserializer.deserialize_str(ParserVisitor::(std::marker::PhantomData)) + struct ParserVisitor; + impl<'de> de::Visitor<'de> for ParserVisitor { + type Value = Identifier; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a valid string") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + crate::parse::ParseFromStr::parse_from_str(value).map_err(E::custom) + } + } + + deserializer.deserialize_str(ParserVisitor) } } -struct WitnessMapSerializer<'a>(&'a HashMap); +struct WitnessMapSerializer<'a>(&'a HashMap); impl<'a> Serialize for WitnessMapSerializer<'a> { fn serialize(&self, serializer: S) -> Result @@ -282,10 +286,10 @@ impl<'a> Serialize for WitnessMapSerializer<'a> { // TODO: Consider serializing every value as a bare string and retiring the { value, type } form. // That drops "witness file readable without the program" entirely. if value.ty().contains_enum() { - map.serialize_entry(name.as_inner(), &value.to_string())?; + map.serialize_entry(name.as_str(), &value.to_string())?; continue; } - map.serialize_entry(name.as_inner(), &ValueMapSerializer(value))?; + map.serialize_entry(name.as_str(), &ValueMapSerializer(value))?; } map.end() } @@ -326,6 +330,7 @@ impl Serialize for Arguments { #[cfg(test)] mod tests { use super::*; + use crate::str::Identifier; #[test] fn witness_serde_duplicate_assignment() { @@ -341,7 +346,6 @@ mod tests { } fn unit_enum(name: &str, variants: &[&str]) -> ResolvedType { - use crate::str::Identifier; use crate::types::{EnumInfo, EnumVariantInfo}; use std::sync::Arc; @@ -354,22 +358,24 @@ mod tests { #[test] fn abi_enum_type_serializes_as_name() { - use crate::str::WitnessName; use crate::types::TypeConstructible; let action_ty = unit_enum("Action", &["Inherit", "ColdSpend"]); let witness_types = WitnessTypes::from(HashMap::from([ - (WitnessName::from_str_unchecked("ACTION"), action_ty.clone()), ( - WitnessName::from_str_unchecked("MAYBE"), + TemplateProgramWitness::witness_from_str("ACTION"), + action_ty.clone(), + ), + ( + TemplateProgramWitness::witness_from_str("MAYBE"), ResolvedType::option(action_ty.clone()), ), ( - WitnessName::from_str_unchecked("PAIR"), + TemplateProgramWitness::witness_from_str("PAIR"), ResolvedType::tuple([action_ty, unit_enum("Reaction", &["Fast", "Slow"])]), ), ( - WitnessName::from_str_unchecked("PLAIN"), + TemplateProgramWitness::witness_from_str("PLAIN"), crate::parse::ParseFromStr::parse_from_str("u32").unwrap(), ), ])); @@ -383,8 +389,6 @@ mod tests { #[test] fn enum_witness_value_serializes_as_variant_name() { - use crate::str::{Identifier, WitnessName}; - let action_ty = unit_enum("Action", &["Inherit", "ColdSpend"]); let value = Value::enum_variant( &action_ty, @@ -392,8 +396,8 @@ mod tests { vec![], ) .unwrap(); - let witness = WitnessValues::from(HashMap::from([( - WitnessName::from_str_unchecked("ACTION"), + let witness = WitnessValues::from_map(HashMap::from([( + TemplateProgramWitness::witness_from_str("ACTION"), value, )])); @@ -405,7 +409,7 @@ mod tests { let text = serde_json::to_string(&witness).unwrap(); let unresolved: UnresolvedValues = serde_json::from_str(&text).unwrap(); let witness_types = WitnessTypes::from(HashMap::from([( - WitnessName::from_str_unchecked("ACTION"), + TemplateProgramWitness::witness_from_str("ACTION"), action_ty, )])); let round_tripped: WitnessValues = unresolved.resolve(&witness_types).unwrap(); @@ -422,12 +426,11 @@ mod tests { #[test] fn payload_enum_witness_value_round_trips() { - use crate::str::{Identifier, WitnessName}; use crate::types::{EnumInfo, EnumVariantInfo}; use crate::value::ValueConstructible; use std::sync::Arc; - let u32_ty: ResolvedType = ParseFromStr::parse_from_str("u32").unwrap(); + let u32_ty = ResolvedType::parse_from_str("u32").unwrap(); let variants: Arc<[EnumVariantInfo]> = Arc::from([ EnumVariantInfo::new(Identifier::from_str_unchecked("Cold"), Arc::from([])), EnumVariantInfo::new( @@ -442,8 +445,8 @@ mod tests { vec![Value::u32(42)], ) .unwrap(); - let witness = WitnessValues::from(HashMap::from([( - WitnessName::from_str_unchecked("ACTION"), + let witness = WitnessValues::from_map(HashMap::from([( + TemplateProgramWitness::witness_from_str("ACTION"), value, )])); @@ -454,7 +457,7 @@ mod tests { let text = serde_json::to_string(&witness).unwrap(); let unresolved: UnresolvedValues = serde_json::from_str(&text).unwrap(); let witness_types = WitnessTypes::from(HashMap::from([( - WitnessName::from_str_unchecked("ACTION"), + TemplateProgramWitness::witness_from_str("ACTION"), action_ty, )])); let round_tripped: WitnessValues = unresolved.resolve(&witness_types).unwrap(); @@ -463,7 +466,6 @@ mod tests { #[test] fn nested_enum_witness_value_serializes_as_bare_string() { - use crate::str::{Identifier, WitnessName}; use crate::types::TypeConstructible; use crate::value::ValueConstructible; @@ -471,8 +473,8 @@ mod tests { let option_ty = ResolvedType::option(action_ty.clone()); let cold = Value::enum_variant(&action_ty, &Identifier::from_str_unchecked("Cold"), vec![]) .unwrap(); - let witness = WitnessValues::from(HashMap::from([( - WitnessName::from_str_unchecked("MAYBE"), + let witness = WitnessValues::from_map(HashMap::from([( + TemplateProgramWitness::witness_from_str("MAYBE"), Value::some(cold), )])); @@ -484,7 +486,7 @@ mod tests { let text = serde_json::to_string(&witness).unwrap(); let unresolved: UnresolvedValues = serde_json::from_str(&text).unwrap(); let witness_types = WitnessTypes::from(HashMap::from([( - WitnessName::from_str_unchecked("MAYBE"), + TemplateProgramWitness::witness_from_str("MAYBE"), option_ty, )])); let round_tripped: WitnessValues = unresolved.resolve(&witness_types).unwrap(); diff --git a/src/str.rs b/src/str.rs index 1fb95cc6..b4f1ee73 100644 --- a/src/str.rs +++ b/src/str.rs @@ -22,7 +22,12 @@ macro_rules! wrapped_string { } /// Access the inner string. - pub fn as_inner(&self) -> &str { + pub fn as_inner(&self) -> &Arc { + &self.0 + } + + /// Access the inner string. + pub fn as_str(&self) -> &str { self.0.as_ref() } @@ -30,17 +35,36 @@ macro_rules! wrapped_string { pub fn shallow_clone(&self) -> Self { Self(Arc::clone(&self.0)) } + + #[doc = "Creates a "] + #[doc = $name] + #[doc = " from an identifier.\n"] + pub fn from_ident(ident: &Identifier) -> Self { + Self(Arc::clone(ident.as_inner())) + } + } + + impl core::cmp::PartialEq for $wrapper { + fn eq(&self, other: &str) -> bool { + self.0.as_ref() == other + } } - impl std::fmt::Display for $wrapper { + impl core::cmp::PartialEq<$wrapper> for str { + fn eq(&self, other: &$wrapper) -> bool { + self == other.0.as_ref() + } + } + + impl core::fmt::Display for $wrapper { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(&self.0, f) + self.0.fmt(f) } } - impl std::fmt::Debug for $wrapper { + impl core::fmt::Debug for $wrapper { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(&self.0, f) + self.0.fmt(f) } } }; @@ -53,7 +77,7 @@ macro_rules! wrapped_string { /// which cannot be used as identifiers. To ensure valid grammar /// for fuzzing, any generated keywords are padded with the `_`. macro_rules! impl_arbitrary_lowercase_alpha { - ($wrapper:ident) => { + ($wrapper:path) => { #[cfg(feature = "arbitrary")] impl<'a> arbitrary::Arbitrary<'a> for $wrapper { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { @@ -190,19 +214,6 @@ pub struct Identifier(Arc); wrapped_string!(Identifier, "variable identifier"); impl_arbitrary_lowercase_alpha!(Identifier); -/// The name of a witness. -#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub struct WitnessName(Arc); - -wrapped_string!(WitnessName, "witness name"); -impl_arbitrary_lowercase_alpha!(WitnessName); - -impl AsRef for WitnessName { - fn as_ref(&self) -> &str { - self.0.as_ref() - } -} - /// The name of a jet. #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] pub struct JetName(Arc); diff --git a/src/template_program/mod.rs b/src/template_program/mod.rs new file mode 100644 index 00000000..59e0156b --- /dev/null +++ b/src/template_program/mod.rs @@ -0,0 +1,39 @@ +mod witness; + +use std::sync::Arc; + +use crate::named; +use crate::Arguments; + +pub use self::witness::TemplateProgramWitness; + +/// A program which has been compiled to Simplicity, except that its parameters have not been +/// resolved. +pub struct TemplateProgram { + inner: Arc, +} + +impl TemplateProgram { + /// Creates a [`TemplateProgram`] from a [`named::ConstructNode`] output from the compiler. + /// + /// # Panics + /// + /// Panics if given a program that doesn't typecheck -- that is, which has infinitely sized + /// types or whose source and target types are non-unit and non-free. + pub(crate) fn from_construct_node<'brand>(node: &named::ConstructNode<'brand>) -> Self { + Self { + inner: named::finalize_types(node) + .expect("SimplicityHL types are 1->1 and have finite types by construction"), + } + } + + /// Instantiates the templated program with the given arguments. + /// + /// ## Precondition + /// + /// The supplied `arguments` are consistent with the program's parameters. + /// Call [`Arguments::is_consistent`] before calling this method! + pub fn instantiate(self, _: Arguments) -> Arc { + self.inner + } +} diff --git a/src/template_program/witness.rs b/src/template_program/witness.rs new file mode 100644 index 00000000..d8d78e06 --- /dev/null +++ b/src/template_program/witness.rs @@ -0,0 +1,128 @@ +use core::fmt; +use std::sync::Arc; + +/// A witness node in a templated Simplicity program. +/// +/// Such a node may represent: +/// +/// * An actual witness, identified by its name in the source code, which will be preserved +/// as a witness node when instantiating. +/// * A program parameter, identified by name in the source code, which will be replaced by +/// code to compute the parameter's value when instantiating. +#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub struct TemplateProgramWitness { + inner: TemplateProgramWitnessInner, +} + +impl TemplateProgramWitness { + /// Create a witness-name [`TemplateProgramWitness`] from a bare string. + /// + /// The string is **not** checked to determine whether it collides with a + /// language keyword, contains valid characters, or is otherwise valid. + pub fn witness_from_str>>(s: S) -> Self { + Self { + inner: TemplateProgramWitnessInner::Witness(s.into()), + } + } + + /// Create a parameter [`TemplateProgramWitness`] from a bare string. + /// + /// The string is **not** checked to determine whether it collides with a + /// language keyword, contains valid characters, or is otherwise valid. + pub fn parameter_from_str>>(s: S) -> Self { + Self { + inner: TemplateProgramWitnessInner::Parameter(s.into()), + } + } + + /// Creates a witness-name [`TemplateProgramWitness`] from an identifier. + pub fn witness_from_ident(ident: &crate::str::Identifier) -> Self { + Self { + inner: TemplateProgramWitnessInner::Witness(Arc::clone(ident.as_inner())), + } + } + + /// Creates a parameter [`TemplateProgramWitness`] from an identifier. + pub fn parameter_from_ident(ident: &crate::str::Identifier) -> Self { + Self { + inner: TemplateProgramWitnessInner::Parameter(Arc::clone(ident.as_inner())), + } + } + + /// Access the inner string. + pub fn as_inner(&self) -> &Arc { + use TemplateProgramWitnessInner as Inn; + match self.inner { + Inn::Witness(ref arc) => arc, + Inn::Parameter(ref arc) => arc, + } + } + + /// Access the inner string. + pub fn as_str(&self) -> &str { + self.as_inner().as_ref() + } + + /// Make a cheap copy of the name. + pub fn shallow_clone(&self) -> Self { + self.clone() + } +} + +impl core::cmp::PartialEq for TemplateProgramWitness { + fn eq(&self, other: &str) -> bool { + self.as_ref() == other + } +} + +impl core::cmp::PartialEq for str { + fn eq(&self, other: &TemplateProgramWitness) -> bool { + self == other.as_ref() + } +} + +impl fmt::Display for TemplateProgramWitness { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_inner().fmt(f) + } +} + +impl fmt::Debug for TemplateProgramWitness { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_inner().fmt(f) + } +} + +impl AsRef for TemplateProgramWitness { + fn as_ref(&self) -> &str { + self.as_inner().as_ref() + } +} + +#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +enum TemplateProgramWitnessInner { + /// An actual witness value. + Witness(Arc), + /// A program paramater. + Parameter(Arc), +} + +#[cfg(feature = "arbitrary")] +impl<'a> arbitrary::Arbitrary<'a> for TemplateProgramWitness { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + let len = u.int_in_range(1..=10)?; + let mut string = String::with_capacity(len); + for _ in 0..len { + let offset = u.int_in_range(0..=25)?; + string.push((b'a' + offset) as char) + } + if crate::lexer::is_keyword(string.as_str()) { + string.push('_'); + } + if bool::arbitrary(u)? { + Ok(Self::witness_from_str(string.as_str())) + } else { + Ok(Self::parameter_from_str(string.as_str())) + } + } +} diff --git a/src/tracker.rs b/src/tracker.rs index bd463e04..cdb5571b 100644 --- a/src/tracker.rs +++ b/src/tracker.rs @@ -97,7 +97,7 @@ impl<'a> DefaultTracker<'a> { /// /// This constructor is deprecated in favor of more flexible tracker setup. /// The deprecation is necessary to show the direction in which the SimplicityHL is moving - /// (i.e. different targets and support for possible custom Jets) + /// (i.e. different targets and support for possible custom Jets) #[deprecated(since = "0.6.0", note = "Please use `build` instead")] pub fn new(debug_symbols: &'a DebugSymbols) -> Self { Self::build(debug_symbols, Box::new(ElementsJetHinter::new())) @@ -378,7 +378,7 @@ mod tests { use crate::elements::hashes::Hash; use crate::elements::pset::Input; use crate::elements::{AssetId, OutPoint, Script, Txid}; - use crate::{Arguments, TemplateProgram, WitnessValues}; + use crate::{Arguments, TemplateAst, WitnessValues}; use super::*; @@ -451,8 +451,7 @@ mod tests { #[test] fn test_debug_and_jet_tracing() { - let program = - TemplateProgram::new(TEST_PROGRAM, Box::new(ElementsJetHinter::new())).unwrap(); + let program = TemplateAst::new(TEST_PROGRAM, Box::new(ElementsJetHinter::new())).unwrap(); let program = program.instantiate(Arguments::default(), true).unwrap(); let satisfied = program.satisfy(WitnessValues::default()).unwrap(); @@ -523,7 +522,7 @@ mod tests { let env = create_test_env(); let program = - TemplateProgram::new(TEST_ARITHMETIC_JETS, Box::new(ElementsJetHinter::new())).unwrap(); + TemplateAst::new(TEST_ARITHMETIC_JETS, Box::new(ElementsJetHinter::new())).unwrap(); let program = program.instantiate(Arguments::default(), true).unwrap(); let satisfied = program.satisfy(WitnessValues::default()).unwrap(); @@ -578,8 +577,7 @@ mod tests { let env = create_test_env(); let program = - TemplateProgram::new(TEST_FULL_MULTIPLY_JETS, Box::new(ElementsJetHinter::new())) - .unwrap(); + TemplateAst::new(TEST_FULL_MULTIPLY_JETS, Box::new(ElementsJetHinter::new())).unwrap(); let program = program.instantiate(Arguments::default(), true).unwrap(); let satisfied = program.satisfy(WitnessValues::default()).unwrap(); diff --git a/src/types.rs b/src/types.rs index b2d67f67..09451c2f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1343,6 +1343,6 @@ mod tests { .variant(&Identifier::from_str_unchecked("Pair")) .expect("Pair is a declared variant"); assert_eq!(2, index); - assert_eq!("Pair", variant.name().as_inner()); + assert_eq!("Pair", variant.name()); } } diff --git a/src/witness.rs b/src/witness.rs index f2aad16a..994f02c4 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -4,20 +4,21 @@ use std::sync::Arc; use crate::error::{Diagnostic, DiagnosticManager, Error, WithSpan}; use crate::parse::ParseFromStr; -use crate::str::WitnessName; +use crate::str::Identifier; use crate::types::{AliasedType, ResolvedType}; use crate::value::Value; +use crate::TemplateProgramWitness; macro_rules! impl_name_type_map { ($wrapper: ident) => { impl $wrapper { /// Get the type that is assigned to the given name. - pub fn get(&self, name: &WitnessName) -> Option<&ResolvedType> { + pub fn get(&self, name: &TemplateProgramWitness) -> Option<&ResolvedType> { self.0.get(name) } /// Create an iterator over all name-type pairs. - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.0.iter() } @@ -27,42 +28,68 @@ macro_rules! impl_name_type_map { } } - impl From> for $wrapper { - fn from(value: HashMap) -> Self { + impl From> for $wrapper { + fn from(value: HashMap) -> Self { Self(Arc::new(value)) } } }; } -macro_rules! impl_name_value_map { - ($wrapper: ident, $module_name: expr) => { - impl $wrapper { - /// Access the inner map. - #[cfg(feature = "serde")] - pub(crate) fn as_inner(&self) -> &HashMap { - &self.0 - } +/// Trait describing a map from template-program "witness names" to values. +/// +/// In a templated program, witness names may refer to actual witnesses, or to parameters. This +/// trait allows manipulating such maps. +pub trait WitnessNameToValueMap { + /// Create a map from an `Arc`. + fn from_inner(map: Arc>) -> Self; - /// Get the value that is assigned to the given name. - pub fn get(&self, name: &WitnessName) -> Option<&Value> { - self.0.get(name) - } + /// Access the inner map. + fn as_inner(&self) -> &Arc>; - /// Create an iterator over all name-value pairs. - pub fn iter(&self) -> impl Iterator { - self.0.iter() + /// Convert a bare identifier to a key. + fn ident_to_key(ident: &Identifier) -> TemplateProgramWitness; + + /// Create a map from a `HashMap`. + fn from_map(map: HashMap) -> Self + where + Self: Sized, + { + Self::from_inner(Arc::new(map)) + } + + /// Make a cheap copy of the map. + fn shallow_clone(&self) -> Self + where + Self: Sized, + { + Self::from_inner(Arc::clone(self.as_inner())) + } + + /// Get the value that is assigned to the given name. + fn get(&self, name: &TemplateProgramWitness) -> Option<&Value> { + self.as_inner().get(name) + } + + /// Create an iterator over all name-value pairs. + fn iter(&self) -> impl Iterator { + self.as_inner().iter() + } +} + +macro_rules! impl_name_value_map { + ($wrapper: ident, $module_name: expr, $ident_fn:ident) => { + impl WitnessNameToValueMap for $wrapper { + fn from_inner(map: Arc>) -> Self { + Self(map) } - /// Make a cheap copy of the map. - pub fn shallow_clone(&self) -> Self { - Self(Arc::clone(&self.0)) + fn as_inner(&self) -> &Arc> { + &self.0 } - } - impl From> for $wrapper { - fn from(value: HashMap) -> Self { - Self(Arc::new(value)) + fn ident_to_key(ident: &Identifier) -> TemplateProgramWitness { + TemplateProgramWitness::$ident_fn(ident) } } @@ -83,12 +110,12 @@ macro_rules! impl_name_value_map { /// Map of witness types. #[derive(Clone, Debug, Eq, PartialEq, Default)] -pub struct WitnessTypes(Arc>); +pub struct WitnessTypes(Arc>); impl_name_type_map!(WitnessTypes); -impl AsRef> for WitnessTypes { - fn as_ref(&self) -> &HashMap { +impl AsRef> for WitnessTypes { + fn as_ref(&self) -> &HashMap { self.0.as_ref() } } @@ -106,9 +133,9 @@ impl AsRef> for WitnessTypes { /// program's declared witness types. #[derive(Clone, Debug, Eq, PartialEq, Default)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] -pub struct WitnessValues(Arc>); +pub struct WitnessValues(Arc>); -impl_name_value_map!(WitnessValues, "witness"); +impl_name_value_map!(WitnessValues, "witness", witness_from_ident); impl WitnessValues { /// Check if the witness values are consistent with the declared witness types. @@ -166,11 +193,11 @@ pub(crate) enum UnresolvedValue { /// [`WitnessValues`] or [`Arguments`]. #[cfg(feature = "serde")] #[derive(Clone, Debug, Eq, PartialEq, Default)] -pub struct UnresolvedValues(HashMap); +pub struct UnresolvedValues(HashMap); #[cfg(feature = "serde")] impl UnresolvedValues { - pub(crate) fn from_map(map: HashMap) -> Self { + pub(crate) fn from_map(map: HashMap) -> Self { Self(map) } @@ -186,12 +213,13 @@ impl UnresolvedValues { /// A bare value string does not parse at the declared type. pub fn resolve(self, declared_types: &M) -> Result where - T: From>, - M: AsRef>, + T: WitnessNameToValueMap, + M: AsRef>, { let declared_types = declared_types.as_ref(); let mut map = HashMap::with_capacity(self.0.len()); for (name, unresolved) in self.0 { + let name = T::ident_to_key(&name); let value = match unresolved { UnresolvedValue::Typed(value) => value, UnresolvedValue::Untyped(s) => { @@ -205,7 +233,7 @@ impl UnresolvedValues { }; map.insert(name, value); } - Ok(T::from(map)) + Ok(T::from_map(map)) } } @@ -224,12 +252,12 @@ impl ParseFromStr for ResolvedType { /// A parameter is a named variable that resolves to a value of a given type. /// Parameters have a name and a type. #[derive(Clone, Debug, Eq, PartialEq, Default)] -pub struct Parameters(Arc>); +pub struct Parameters(Arc>); impl_name_type_map!(Parameters); -impl AsRef> for Parameters { - fn as_ref(&self) -> &HashMap { +impl AsRef> for Parameters { + fn as_ref(&self) -> &HashMap { self.0.as_ref() } } @@ -248,9 +276,9 @@ impl AsRef> for Parameters { /// declared parameter types. #[derive(Clone, Debug, Eq, PartialEq, Default)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] -pub struct Arguments(Arc>); +pub struct Arguments(Arc>); -impl_name_value_map!(Arguments, "param"); +impl_name_value_map!(Arguments, "param", parameter_from_ident); impl Arguments { /// Check if the arguments are consistent with the given parameters. @@ -297,7 +325,7 @@ impl crate::ArbitraryOfType for Arguments { Value::arbitrary_of_type(u, parameter_ty)?, ); } - Ok(Self::from(map)) + Ok(Self::from_map(map)) } } @@ -334,8 +362,8 @@ mod tests { assert!(jet::is_zero_32(witness::A)); }"#; - let witness = WitnessValues::from(HashMap::from([( - WitnessName::from_str_unchecked("A"), + let witness = WitnessValues::from_map(HashMap::from([( + TemplateProgramWitness::witness_from_str("A"), Value::u16(42), )])); match SatisfiedProgram::new( @@ -383,44 +411,47 @@ fn main() { let u32_ty = ResolvedType::parse_from_str("u32").unwrap(); let sig_ty = ResolvedType::parse_from_str("Signature").unwrap(); let witness_types = WitnessTypes::from(HashMap::from([ - (WitnessName::from_str_unchecked("A"), u32_ty.clone()), - (WitnessName::from_str_unchecked("SIG"), sig_ty), + ( + TemplateProgramWitness::witness_from_str("A"), + u32_ty.clone(), + ), + (TemplateProgramWitness::witness_from_str("SIG"), sig_ty), ])); let unresolved = UnresolvedValues::from_map(HashMap::from([ ( - WitnessName::from_str_unchecked("A"), + Identifier::from_str_unchecked("A"), UnresolvedValue::Untyped("42".to_string()), ), ( - WitnessName::from_str_unchecked("B"), + Identifier::from_str_unchecked("B"), UnresolvedValue::Typed(Value::u16(7)), ), ])); let resolved: WitnessValues = unresolved.resolve(&witness_types).unwrap(); assert_eq!( - resolved.get(&WitnessName::from_str_unchecked("A")), + resolved.get(&TemplateProgramWitness::witness_from_str("A")), Some(&Value::u32(42)) ); assert_eq!( - resolved.get(&WitnessName::from_str_unchecked("B")), + resolved.get(&TemplateProgramWitness::witness_from_str("B")), Some(&Value::u16(7)) ); // Entries the program does not declare are skipped (consistent with `WitnessValues::is_consistent`) let extra = UnresolvedValues::from_map(HashMap::from([( - WitnessName::from_str_unchecked("UNUSED"), + Identifier::from_str_unchecked("UNUSED"), UnresolvedValue::Untyped("1".to_string()), )])); let resolved: WitnessValues = extra.resolve(&witness_types).unwrap(); assert_eq!( - resolved.get(&WitnessName::from_str_unchecked("UNUSED")), + resolved.get(&TemplateProgramWitness::witness_from_str("UNUSED")), None, "undeclared bare entries are ignored" ); let bad = UnresolvedValues::from_map(HashMap::from([( - WitnessName::from_str_unchecked("A"), + Identifier::from_str_unchecked("A"), UnresolvedValue::Untyped("not-a-number".to_string()), )])); let err = bad.resolve::(&witness_types).unwrap_err(); @@ -441,16 +472,16 @@ fn main() { let unresolved: UnresolvedValues = serde_json::from_str(s).unwrap(); let u32_ty = ResolvedType::parse_from_str("u32").unwrap(); let witness_types = WitnessTypes::from(HashMap::from([( - WitnessName::from_str_unchecked("A"), + TemplateProgramWitness::witness_from_str("A"), u32_ty, )])); let resolved: WitnessValues = unresolved.resolve(&witness_types).unwrap(); assert_eq!( - resolved.get(&WitnessName::from_str_unchecked("A")), + resolved.get(&TemplateProgramWitness::witness_from_str("A")), Some(&Value::u32(42)) ); assert_eq!( - resolved.get(&WitnessName::from_str_unchecked("B")), + resolved.get(&TemplateProgramWitness::witness_from_str("B")), Some(&Value::u16(7)) ); @@ -468,18 +499,18 @@ fn main() { .collect(); let action_ty = ResolvedType::enumeration(EnumInfo::new(Arc::from("Action"), variants)); let witness_types = WitnessTypes::from(HashMap::from([( - WitnessName::from_str_unchecked("ACTION"), + TemplateProgramWitness::witness_from_str("ACTION"), action_ty.clone(), )])); let resolve_one = |input: &str| -> Result { let unresolved = UnresolvedValues::from_map(HashMap::from([( - WitnessName::from_str_unchecked("ACTION"), + Identifier::from_str_unchecked("ACTION"), UnresolvedValue::Untyped(input.to_string()), )])); let resolved: WitnessValues = unresolved.resolve(&witness_types)?; Ok(resolved - .get(&WitnessName::from_str_unchecked("ACTION")) + .get(&TemplateProgramWitness::witness_from_str("ACTION")) .unwrap() .clone()) }; @@ -514,17 +545,17 @@ fn main() { ResolvedType::parse_from_str("u32").unwrap(), ]); let witness_types = WitnessTypes::from(HashMap::from([ - (WitnessName::from_str_unchecked("MAYBE"), option_ty), - (WitnessName::from_str_unchecked("PAIR"), tuple_ty), + (TemplateProgramWitness::witness_from_str("MAYBE"), option_ty), + (TemplateProgramWitness::witness_from_str("PAIR"), tuple_ty), ])); let unresolved = UnresolvedValues::from_map(HashMap::from([ ( - WitnessName::from_str_unchecked("MAYBE"), + Identifier::from_str_unchecked("MAYBE"), UnresolvedValue::Untyped("Some(Action::Cold)".to_string()), ), ( - WitnessName::from_str_unchecked("PAIR"), + Identifier::from_str_unchecked("PAIR"), UnresolvedValue::Untyped("(Action::Hot, 42)".to_string()), ), ])); @@ -532,11 +563,11 @@ fn main() { .resolve(&witness_types) .expect("variants resolve inside options and tuples"); let maybe = resolved - .get(&WitnessName::from_str_unchecked("MAYBE")) + .get(&TemplateProgramWitness::witness_from_str("MAYBE")) .unwrap(); assert_eq!("Some(Action::Cold)", &maybe.to_string()); let pair = resolved - .get(&WitnessName::from_str_unchecked("PAIR")) + .get(&TemplateProgramWitness::witness_from_str("PAIR")) .unwrap(); assert_eq!("(Action::Hot, 42)", &pair.to_string()); @@ -550,10 +581,10 @@ fn main() { #[test] fn witness_to_string() { - let witness = WitnessValues::from(HashMap::from([ - (WitnessName::from_str_unchecked("A"), Value::u32(1)), - (WitnessName::from_str_unchecked("B"), Value::u32(2)), - (WitnessName::from_str_unchecked("C"), Value::u32(3)), + let witness = WitnessValues::from_map(HashMap::from([ + (TemplateProgramWitness::witness_from_str("A"), Value::u32(1)), + (TemplateProgramWitness::witness_from_str("B"), Value::u32(2)), + (TemplateProgramWitness::witness_from_str("C"), Value::u32(3)), ])); let expected_string = r#"mod witness { const A: u32 = 1; diff --git a/tests/core_tracker.rs b/tests/core_tracker.rs index 6c9b1608..8c587740 100644 --- a/tests/core_tracker.rs +++ b/tests/core_tracker.rs @@ -4,7 +4,7 @@ use std::rc::Rc; use simplicityhl::ast::CoreJetHinter; use simplicityhl::simplicity::jet::CoreEnv; use simplicityhl::tracker::DefaultTracker; -use simplicityhl::{Arguments, TemplateProgram, WitnessValues}; +use simplicityhl::{Arguments, TemplateAst, WitnessValues}; const CORE_PROGRAM: &str = r#"fn main() { let (_, sum): (bool, u32) = jet::add_32(10, 20); @@ -12,7 +12,7 @@ const CORE_PROGRAM: &str = r#"fn main() { }"#; fn satisfied_core_program() -> simplicityhl::SatisfiedProgram { - TemplateProgram::new(CORE_PROGRAM, Box::new(CoreJetHinter::new())) + TemplateAst::new(CORE_PROGRAM, Box::new(CoreJetHinter::new())) .unwrap() .instantiate(Arguments::default(), true) .unwrap()