diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 528c57b..a7084d3 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -21,7 +21,7 @@ cfg_if::cfg_if! { pub mod error; pub mod prelude; -mod primitive; +pub mod primitive; // Little Endian pub type Number = u32; diff --git a/std/rust/Cargo.toml b/std/rust/Cargo.toml new file mode 100644 index 0000000..d47e980 --- /dev/null +++ b/std/rust/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "molecule-std" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +molecule = { path = "../../bindings/rust" } + +[build-dependencies] +codegen = { package ="molecule-codegen", path = "../../tools/codegen" } diff --git a/std/rust/build.rs b/std/rust/build.rs new file mode 100644 index 0000000..8bfb325 --- /dev/null +++ b/std/rust/build.rs @@ -0,0 +1,20 @@ +use codegen::{Compiler, Language}; + +fn compile_schema(schema: &str) { + let mut compiler = Compiler::new(); + compiler + .input_schema_file(schema) + .generate_code(Language::Rust) + .output_dir_set_default() + .expand_primitive_types() + .run() + .unwrap(); + println!("cargo:rerun-if-changed={}", schema); +} + +fn main() { + println!("cargo:rerun-if-changed=primitive_types.mol"); + compile_schema("primitive_types.mol"); + let out_dir = ::std::env::var("OUT_DIR").unwrap(); + println!("{}", out_dir); +} diff --git a/std/rust/primitive_types.mol b/std/rust/primitive_types.mol new file mode 100644 index 0000000..6d2f413 --- /dev/null +++ b/std/rust/primitive_types.mol @@ -0,0 +1 @@ +array uint64 [byte; 8]; diff --git a/std/rust/src/lib.rs b/std/rust/src/lib.rs new file mode 100644 index 0000000..9c3ba62 --- /dev/null +++ b/std/rust/src/lib.rs @@ -0,0 +1,56 @@ +pub mod prelude; +pub mod primitive_types; + +extern crate alloc; + +pub use alloc::{borrow::ToOwned, vec, vec::Vec}; +use core::{clone::Clone, default::Default, fmt}; +use molecule::prelude::*; +pub use molecule::{ + bytes, + bytes::Bytes, + error, + error::VerificationResult, + hex_string, io, + primitive::{Byte, ByteReader}, + verification_error, +}; + +use primitive_types::*; + +pub trait Unpack { + /// Unpack binary data into rust types. + fn unpack(&self) -> T; +} + +/// A syntactic sugar to convert a rust type into binary data. +pub trait Pack { + /// Packs a rust type into binary data. + fn pack(&self) -> T; +} + +impl Pack for u64 { + fn pack(&self) -> Uint64 { + Uint64::new_unchecked(Bytes::from(self.to_le_bytes().to_vec())) + } +} + +impl<'r> Unpack for Uint64Reader<'r> { + fn unpack(&self) -> u64 { + let mut b = [0u8; 8]; + b.copy_from_slice(self.as_slice()); + u64::from_le_bytes(b) + } +} + +macro_rules! impl_conversion_for_entity_unpack { + ($original:ty, $entity:ident) => { + impl Unpack<$original> for $entity { + fn unpack(&self) -> $original { + self.as_reader().unpack() + } + } + }; +} + +impl_conversion_for_entity_unpack!(u64, Uint64); diff --git a/std/rust/src/prelude.rs b/std/rust/src/prelude.rs new file mode 100644 index 0000000..5b8047a --- /dev/null +++ b/std/rust/src/prelude.rs @@ -0,0 +1,14 @@ +include!(concat!(env!("OUT_DIR"), "/primitive_types.rs")); + +extern crate alloc; +pub use alloc::{borrow::ToOwned, vec, vec::Vec}; +pub use molecule::{ + bytes, + bytes::Bytes, + error, + error::VerificationResult, + hex_string, io, prelude, + prelude::*, + primitive::{Byte, ByteReader}, + verification_error, +}; diff --git a/std/rust/src/primitive_types.rs b/std/rust/src/primitive_types.rs new file mode 100644 index 0000000..c6e5f60 --- /dev/null +++ b/std/rust/src/primitive_types.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/primitive_types.rs")); diff --git a/tools/codegen/src/ast/verified/complete.rs b/tools/codegen/src/ast/verified/complete.rs index 70f6ac5..69bccf9 100644 --- a/tools/codegen/src/ast/verified/complete.rs +++ b/tools/codegen/src/ast/verified/complete.rs @@ -1,3 +1,4 @@ +use crate::ast::verified::{PRIMITIVE_BYTE, PRIMITIVE_EXT_TYPES}; use std::{ collections::{HashMap, HashSet}, rc::Rc, @@ -162,12 +163,17 @@ impl CompleteRawDecl for raw::TableDecl { } impl super::Ast { - pub(crate) fn complete(raw: raw::Ast) -> Self { + pub(crate) fn complete(raw: raw::Ast, expand_primitive_ext_types: bool) -> Self { let mut decls_idx = HashMap::new(); let mut decls_keys = HashSet::new(); for decl in raw.decls() { let name = decl.name(); - if super::TopDecl::new_primitive(name.to_lowercase().as_str()).is_some() { + if expand_primitive_ext_types { + if super::TopDecl::new_primitive_without_ext(name.to_lowercase().as_str()).is_some() + { + panic!("the name `{}` is reserved", name); + } + } else if super::TopDecl::new_primitive(name.to_lowercase().as_str()).is_some() { panic!("the name `{}` is reserved", name); } if decls_idx.insert(name, decl).is_some() || !decls_keys.insert(name) { @@ -175,10 +181,21 @@ impl super::Ast { }; } let mut decls_result = HashMap::new(); + decls_result.insert( - "byte", - Rc::new(super::TopDecl::new_primitive("byte").unwrap()), + PRIMITIVE_BYTE, + Rc::new(super::TopDecl::new_primitive(PRIMITIVE_BYTE).unwrap()), ); + + if !expand_primitive_ext_types { + PRIMITIVE_EXT_TYPES.iter().for_each(|&primitive_type| { + decls_result.insert( + primitive_type, + Rc::new(super::TopDecl::new_primitive(primitive_type).unwrap()), + ); + }); + } + loop { if decls_keys.is_empty() { break; diff --git a/tools/codegen/src/ast/verified/mod.rs b/tools/codegen/src/ast/verified/mod.rs index 3f49e40..6491cfc 100644 --- a/tools/codegen/src/ast/verified/mod.rs +++ b/tools/codegen/src/ast/verified/mod.rs @@ -12,6 +12,11 @@ mod recover; pub use default_content::DefaultContent; pub use has_name::HasName; +const PRIMITIVE_BYTE: &str = "byte"; + +const PRIMITIVE_UINT64: &str = "uint64"; +const PRIMITIVE_EXT_TYPES: &[&str] = &[PRIMITIVE_UINT64]; + type Deps<'a> = HashMap<&'a str, Rc>; #[derive(Debug, Property)] @@ -135,7 +140,22 @@ impl Ast { impl TopDecl { fn new_primitive(name: &str) -> Option { match name { - "byte" => Some(Primitive { + PRIMITIVE_BYTE => Some(Primitive { + name: name.to_owned(), + size: 1, + }), + PRIMITIVE_UINT64 => Some(Primitive { + name: name.to_owned(), + size: 8, + }), + _ => None, + } + .map(Self::Primitive) + } + + fn new_primitive_without_ext(name: &str) -> Option { + match name { + PRIMITIVE_BYTE => Some(Primitive { name: name.to_owned(), size: 1, }), diff --git a/tools/codegen/src/ast/verified/recover.rs b/tools/codegen/src/ast/verified/recover.rs index 84b6096..bc01fb6 100644 --- a/tools/codegen/src/ast/verified/recover.rs +++ b/tools/codegen/src/ast/verified/recover.rs @@ -1,3 +1,4 @@ +use crate::ast::verified::{PRIMITIVE_BYTE, PRIMITIVE_EXT_TYPES}; use std::{ collections::{HashMap, HashSet}, rc::Rc, @@ -169,12 +170,17 @@ impl RecoverFromIr for ir::Table { } impl super::Ast { - pub(crate) fn recover(ir: ir::Ir) -> Self { + pub(crate) fn recover(ir: ir::Ir, expand_primitive_ext_types: bool) -> Self { let mut decls_idx = HashMap::new(); let mut decls_keys = HashSet::new(); for decl in ir.decls() { let name = decl.name(); - if super::TopDecl::new_primitive(name.to_lowercase().as_str()).is_some() { + if expand_primitive_ext_types { + if super::TopDecl::new_primitive_without_ext(name.to_lowercase().as_str()).is_some() + { + panic!("the name `{}` is reserved", name); + } + } else if super::TopDecl::new_primitive(name.to_lowercase().as_str()).is_some() { panic!("the name `{}` is reserved", name); } if decls_idx.insert(name, decl).is_some() || !decls_keys.insert(name) { @@ -182,10 +188,20 @@ impl super::Ast { }; } let mut decls_result = HashMap::new(); + decls_result.insert( - "byte", - Rc::new(super::TopDecl::new_primitive("byte").unwrap()), + PRIMITIVE_BYTE, + Rc::new(super::TopDecl::new_primitive(PRIMITIVE_BYTE).unwrap()), ); + if !expand_primitive_ext_types { + PRIMITIVE_EXT_TYPES.iter().for_each(|&primitive_type| { + decls_result.insert( + primitive_type, + Rc::new(super::TopDecl::new_primitive(primitive_type).unwrap()), + ); + }); + } + loop { if decls_keys.is_empty() { break; diff --git a/tools/codegen/src/compiler.rs b/tools/codegen/src/compiler.rs index 3eb262c..16ceac3 100644 --- a/tools/codegen/src/compiler.rs +++ b/tools/codegen/src/compiler.rs @@ -12,6 +12,7 @@ pub struct Compiler { target: Option, input: Option, output: Option, + expand_primitive_types: bool, } pub(crate) enum Input { @@ -39,6 +40,7 @@ impl Compiler { target: None, input: None, output: Some(Output::Stdout), + expand_primitive_types: false, } } @@ -65,6 +67,11 @@ impl Compiler { self } + pub fn expand_primitive_types(&mut self) -> &mut Self { + self.expand_primitive_types = true; + self + } + pub fn output_dir_set_default(&mut self) -> &mut Self { let out_dir = path::PathBuf::from(&env::var("OUT_DIR").unwrap_or_else(|_| ".".to_string())); self.output_dir(out_dir) @@ -90,6 +97,7 @@ impl Compiler { ref output, #[cfg(feature = "compiler-plugin")] ref mut output, + expand_primitive_types: _, } = self; let target = target.ok_or("target is not set: generate code or intermediate data")?; let input = input @@ -113,10 +121,12 @@ impl Compiler { .file_name() .and_then(ffi::OsStr::to_str) .to_owned(); - parser::Parser::parse(file_path) + parser::Parser::parse(file_path, self.expand_primitive_types) } #[cfg(feature = "compiler-plugin")] - Input::Intermediate(format, ref data) => format.recover(data)?, + Input::Intermediate(format, ref data) => { + format.recover(data, self.expand_primitive_types)? + } }; let generator = generator::Generator::new(ast); diff --git a/tools/codegen/src/ir/format.rs b/tools/codegen/src/ir/format.rs index 9778f56..e89b089 100644 --- a/tools/codegen/src/ir/format.rs +++ b/tools/codegen/src/ir/format.rs @@ -77,7 +77,8 @@ impl Format { writer.write_all(&data) } - pub fn recover(self, bytes: &[u8]) -> Result { - self.deserialize(bytes).map(ast::Ast::recover) + pub fn recover(self, bytes: &[u8], expand_primitive_types: bool) -> Result { + self.deserialize(bytes) + .map(|ir| ast::Ast::recover(v, expand_primitive_types)) } } diff --git a/tools/codegen/src/parser/mod.rs b/tools/codegen/src/parser/mod.rs index 25e0300..e57edd4 100644 --- a/tools/codegen/src/parser/mod.rs +++ b/tools/codegen/src/parser/mod.rs @@ -8,8 +8,8 @@ pub(crate) use inner::{Parser as InnerParser, Rule}; pub struct Parser; impl Parser { - pub fn parse>(path: &P) -> ast::Ast { + pub fn parse>(path: &P, expand_primitive_types: bool) -> ast::Ast { let ast_raw = Self::preprocess(path).unwrap(); - ast::Ast::complete(ast_raw) + ast::Ast::complete(ast_raw, expand_primitive_types) } }