From 82eb63f32c3c53875c643dbd4d51b16257eb5b6b Mon Sep 17 00:00:00 2001 From: Tim Docker Date: Wed, 22 Mar 2023 09:26:08 +1100 Subject: [PATCH] starting to flesh out rust codegen --- rust/compiler/src/cli/rust.rs | 115 ------------------------- rust/compiler/src/cli/rust/generate.rs | 112 ++++++++++++++++++++++++ rust/compiler/src/cli/rust/mod.rs | 56 ++++++++++++ rust/compiler/src/cli/rust/rsfile.rs | 28 ++++++ 4 files changed, 196 insertions(+), 115 deletions(-) delete mode 100644 rust/compiler/src/cli/rust.rs create mode 100644 rust/compiler/src/cli/rust/generate.rs create mode 100644 rust/compiler/src/cli/rust/mod.rs create mode 100644 rust/compiler/src/cli/rust/rsfile.rs diff --git a/rust/compiler/src/cli/rust.rs b/rust/compiler/src/cli/rust.rs deleted file mode 100644 index d8ca4995..00000000 --- a/rust/compiler/src/cli/rust.rs +++ /dev/null @@ -1,115 +0,0 @@ -use super::RustOpts; -use std::collections::{HashMap, HashSet}; -use std::fmt::Write; -use std::path::PathBuf; - -use anyhow::anyhow; - -use crate::adlgen::sys::adlast2 as adlast; -use crate::processing::loader::loader_from_search_paths; -use crate::processing::resolver::{Module1, Resolver, TypeExpr1}; -use crate::processing::writer::TreeWriter; - -pub fn rust(opts: &RustOpts) -> anyhow::Result<()> { - let loader = loader_from_search_paths(&opts.search.path); - let mut resolver = Resolver::new(loader); - for m in &opts.modules { - let r = resolver.add_module(m); - match r { - Ok(()) => (), - Err(e) => return Err(anyhow!("Failed to load module {}: {:?}", m, e)), - } - } - let modules: Vec<&Module1> = resolver - .get_module_names() - .into_iter() - .map(|mn| resolver.get_module(&mn).unwrap()) - .collect(); - - let mut writer = TreeWriter::new( - opts.output.outdir.clone(), - opts.output.manifest.clone(), - )?; - - for m in modules { - let path = path_from_module_name(opts, m.name.to_owned()); - let code = gen_module(m).unwrap(); - writer.write(path.as_path(), code)?; - } - - gen_rs_mod_files(opts, &resolver, &mut writer)?; - - Ok(()) -} - -fn path_from_module_name(opts: &RustOpts, mname: adlast::ModuleName) -> PathBuf { - let mut path = PathBuf::new(); - path.push(opts.module.clone()); - for el in mname.split(".") { - path.push(el); - } - path.set_extension("rs"); - return path; -} - -/// Generate the tree of mod.rs files that link together the generated code -fn gen_rs_mod_files(opts: &RustOpts, resolver: &Resolver, writer: &mut TreeWriter) -> anyhow::Result<()> { - - // build a map of parent rust modules and their children - let mut modfiles: HashMap,HashSet> = HashMap::new(); - for m in resolver.get_module_names() { - let msplit: Vec<&str> = m.split(".").collect(); - for i in 0..msplit.len() { - let rsmod = msplit.get(i).unwrap(); - let parent = &msplit[0..i]; - let parent: Vec = parent.iter().map(|m| m.to_string()).collect(); - let e = modfiles.entry(parent).or_default(); - e.insert(rsmod.to_string()); - } - } - - for (rsmod,children) in modfiles { - let mut path = PathBuf::new(); - path.push(opts.module.clone()); - for el in rsmod { - path.push(el); - } - path.push("mod.rs"); - let lines: Vec = children - .iter() - .map( |m| format!("pub mod {};", m)) - .collect(); - writer.write(&path, lines.join("\n"))? - }; - Ok(()) -} - -fn gen_module(m: &Module1) -> anyhow::Result { - let mut out = String::new(); - - for d in m.decls.iter() { - match &d.r#type { - adlast::DeclType::Struct(s) => gen_struct(m, d, &s, &mut out)?, - _ => {} - } - } - Ok(out) -} - -fn gen_struct( - _m: &Module1, - d: &adlast::Decl, - s: &adlast::Struct, - out: &mut String, -) -> anyhow::Result<()> { - write!(out, "struct {} {{\n", d.name)?; - for f in &s.fields { - write!(out, " {}: {};\n", f.name, gen_type_expr(&f.type_expr))?; - } - write!(out, "}}\n\n")?; - Ok(()) -} - -fn gen_type_expr(_te: &TypeExpr1) -> String { - "TYPE".to_owned() -} diff --git a/rust/compiler/src/cli/rust/generate.rs b/rust/compiler/src/cli/rust/generate.rs new file mode 100644 index 00000000..9712454f --- /dev/null +++ b/rust/compiler/src/cli/rust/generate.rs @@ -0,0 +1,112 @@ +use super::RustOpts; +use std::collections::{HashMap, HashSet}; +use std::path::PathBuf; + + +use crate::adlgen::sys::adlast2::{self as adlast, PrimitiveType}; +use crate::processing::resolver::{Module1, Resolver, TypeExpr1}; +use crate::processing::writer::TreeWriter; + +use crate::cli::rust::rsfile::RSFile; +use crate::fmtln; + + +/// Generate the tree of mod.rs files that link together the generated code +pub fn gen_rs_mod_files(opts: &RustOpts, resolver: &Resolver, writer: &mut TreeWriter) -> anyhow::Result<()> { + + // build a map of parent rust modules and their children + let mut modfiles: HashMap,HashSet> = HashMap::new(); + for m in resolver.get_module_names() { + let msplit: Vec<&str> = m.split(".").collect(); + for i in 0..msplit.len() { + let rsmod = msplit.get(i).unwrap(); + let parent = &msplit[0..i]; + let parent: Vec = parent.iter().map(|m| m.to_string()).collect(); + let e = modfiles.entry(parent).or_default(); + e.insert(rsmod.to_string()); + } + } + + for (rsmod,children) in modfiles { + let mut path = PathBuf::new(); + path.push(opts.module.clone()); + for el in rsmod { + path.push(el); + } + path.push("mod.rs"); + let lines: Vec = children + .iter() + .map( |m| format!("pub mod {};", m)) + .collect(); + writer.write(&path, lines.join("\n"))? + }; + Ok(()) +} + +pub fn gen_module(m: &Module1) -> anyhow::Result { + let mut out = RSFile::new(); + + for d in m.decls.iter() { + match &d.r#type { + adlast::DeclType::Struct(s) => gen_struct(m, d, &s, &mut out)?, + _ => {} + } + } + Ok(out.to_string()) +} + + +fn gen_struct( + _m: &Module1, + d: &adlast::Decl, + s: &adlast::Struct, + out: &mut RSFile, +) -> anyhow::Result<()> { + fmtln!(out, "pub struct {} {{", d.name); + for f in &s.fields { + fmtln!(out, " {}: {};", f.name, gen_type_expr(&f.type_expr)); + } + fmtln!(out, "}}"); + fmtln!(out, ""); + fmtln!(out, "impl {} {{", d.name); + fmtln!(out, " pub fn new() -> {} {{", d.name); + fmtln!(out, " {} {{", d.name); + fmtln!(out, " }}"); + fmtln!(out, " }}"); + fmtln!(out, "}}"); + fmtln!(out, ""); + Ok(()) +} + +fn gen_type_expr(te: &TypeExpr1) -> String { + match &te.type_ref { + adlast::TypeRef::LocalName(_ln) => "UNIMP.LOCAL_NAME".to_owned(), + adlast::TypeRef::ScopedName(_sn) => "UNIMP.SCOPED_NAME".to_owned(), + adlast::TypeRef::Primitive(p) => gen_primitive_type_expr(p, &te.parameters), + adlast::TypeRef::TypeParam(_t) => "UNIMP.TYPE_PARAM".to_owned(), + } +} + +fn gen_primitive_type_expr(p: &PrimitiveType, _params: &[TypeExpr1]) -> String { + match p { + PrimitiveType::Void => "()".to_owned(), + PrimitiveType::Bool => "bool".to_owned(), + PrimitiveType::Int8 => "i8".to_owned(), + PrimitiveType::Int16 => "i16".to_owned(), + PrimitiveType::Int32 => "i32".to_owned(), + PrimitiveType::Int64 => "i64".to_owned(), + PrimitiveType::Word8 => "u8".to_owned(), + PrimitiveType::Word16 => "u16".to_owned(), + PrimitiveType::Word32 => "u32".to_owned(), + PrimitiveType::Word64 => "u64".to_owned(), + PrimitiveType::Float => "f32".to_owned(), + PrimitiveType::Double => "f64".to_owned(), + PrimitiveType::Json => "UNIMP_JSON".to_owned(), + PrimitiveType::ByteVector => "Vec".to_owned(), + PrimitiveType::String => "String".to_owned(), + PrimitiveType::Vector => "UNIMP_VECTOR".to_owned(), + PrimitiveType::StringMap => "UNIMP_STRINGMAP".to_owned(), + PrimitiveType::Nullable => "UNIMP_NULLABLE".to_owned(), + PrimitiveType::TypeToken => "UNIMP_TYPETOKEN".to_owned(), + } +} diff --git a/rust/compiler/src/cli/rust/mod.rs b/rust/compiler/src/cli/rust/mod.rs new file mode 100644 index 00000000..e03605fa --- /dev/null +++ b/rust/compiler/src/cli/rust/mod.rs @@ -0,0 +1,56 @@ +use super::RustOpts; +use std::path::PathBuf; + +use anyhow::anyhow; + +use crate::adlgen::sys::adlast2::{self as adlast}; +use crate::processing::loader::loader_from_search_paths; +use crate::processing::resolver::{Module1, Resolver}; +use crate::processing::writer::TreeWriter; + +use generate::{gen_module, gen_rs_mod_files}; + +mod rsfile; +mod generate; + +pub fn rust(opts: &RustOpts) -> anyhow::Result<()> { + let loader = loader_from_search_paths(&opts.search.path); + let mut resolver = Resolver::new(loader); + for m in &opts.modules { + let r = resolver.add_module(m); + match r { + Ok(()) => (), + Err(e) => return Err(anyhow!("Failed to load module {}: {:?}", m, e)), + } + } + let modules: Vec<&Module1> = resolver + .get_module_names() + .into_iter() + .map(|mn| resolver.get_module(&mn).unwrap()) + .collect(); + + let mut writer = TreeWriter::new( + opts.output.outdir.clone(), + opts.output.manifest.clone(), + )?; + + for m in modules { + let path = path_from_module_name(opts, m.name.to_owned()); + let code = gen_module(m).unwrap(); + writer.write(path.as_path(), code)?; + } + + gen_rs_mod_files(opts, &resolver, &mut writer)?; + + Ok(()) +} + +fn path_from_module_name(opts: &RustOpts, mname: adlast::ModuleName) -> PathBuf { + let mut path = PathBuf::new(); + path.push(opts.module.clone()); + for el in mname.split(".") { + path.push(el); + } + path.set_extension("rs"); + return path; +} diff --git a/rust/compiler/src/cli/rust/rsfile.rs b/rust/compiler/src/cli/rust/rsfile.rs new file mode 100644 index 00000000..47a8c5d9 --- /dev/null +++ b/rust/compiler/src/cli/rust/rsfile.rs @@ -0,0 +1,28 @@ + +/// In memory representation of a rust file during codegen +pub struct RSFile { + lines: Vec, +} + +impl RSFile { + pub fn new() -> Self { + RSFile{ + lines: Vec::new(), + } + } + + pub fn pushln(&mut self, text: String ) { + self.lines.push(text); + } + + pub fn to_string(&self) -> String { + self.lines.join("\n") + } +} + +#[macro_export] +macro_rules! fmtln { + ($dst:expr, $($arg:tt)*) => { + $dst.pushln(format!($($arg)*)) + }; +}