Skip to content

Commit

Permalink
Merge pull request #85 from Chia-Network/20240423-compiler-opts-deleg…
Browse files Browse the repository at this point in the history
…ation

Allow overridable method delegation in downstream CompilerOpts implementations
  • Loading branch information
prozacchiwawa authored Apr 25, 2024
2 parents db47eab + 89fc129 commit 78e8362
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 186 deletions.
198 changes: 198 additions & 0 deletions src/compiler/comptypes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,204 @@ pub trait CompilerOpts {
) -> Result<SExp, CompileErr>;
}

/// A trait that simplifies implementing one's own CompilerOpts personality.
/// This specifies to a CompilerOptsDelegator that this object contains a
/// CompilerOpts that it uses for most of what it does, allowing end users
/// to opt into a default implementation of all the methods via
/// CompilerOptsDelegator and override only what's desired.
pub trait HasCompilerOptsDelegation {
/// Get this object's inner CompilerOpts.
fn compiler_opts(&self) -> Rc<dyn CompilerOpts>;
/// Call a function that updates this object's CompilerOpts and use the
/// update our own object with the result. Return the new wrapper.
fn update_compiler_opts<F: FnOnce(Rc<dyn CompilerOpts>) -> Rc<dyn CompilerOpts>>(
&self,
f: F,
) -> Rc<dyn CompilerOpts>;

// Defaults.
fn override_filename(&self) -> String {
self.compiler_opts().filename()
}
fn override_code_generator(&self) -> Option<PrimaryCodegen> {
self.compiler_opts().code_generator()
}
fn override_dialect(&self) -> AcceptedDialect {
self.compiler_opts().dialect()
}
fn override_disassembly_ver(&self) -> Option<usize> {
self.compiler_opts().disassembly_ver()
}
fn override_in_defun(&self) -> bool {
self.compiler_opts().in_defun()
}
fn override_stdenv(&self) -> bool {
self.compiler_opts().stdenv()
}
fn override_optimize(&self) -> bool {
self.compiler_opts().optimize()
}
fn override_frontend_opt(&self) -> bool {
self.compiler_opts().frontend_opt()
}
fn override_frontend_check_live(&self) -> bool {
self.compiler_opts().frontend_check_live()
}
fn override_start_env(&self) -> Option<Rc<SExp>> {
self.compiler_opts().start_env()
}
fn override_prim_map(&self) -> Rc<HashMap<Vec<u8>, Rc<SExp>>> {
self.compiler_opts().prim_map()
}
fn override_get_search_paths(&self) -> Vec<String> {
self.compiler_opts().get_search_paths()
}

fn override_set_dialect(&self, dialect: AcceptedDialect) -> Rc<dyn CompilerOpts> {
self.update_compiler_opts(|o| o.set_dialect(dialect))
}
fn override_set_search_paths(&self, dirs: &[String]) -> Rc<dyn CompilerOpts> {
self.update_compiler_opts(|o| o.set_search_paths(dirs))
}
fn override_set_disassembly_ver(&self, ver: Option<usize>) -> Rc<dyn CompilerOpts> {
self.update_compiler_opts(|o| o.set_disassembly_ver(ver))
}
fn override_set_in_defun(&self, new_in_defun: bool) -> Rc<dyn CompilerOpts> {
self.update_compiler_opts(|o| o.set_in_defun(new_in_defun))
}
fn override_set_stdenv(&self, new_stdenv: bool) -> Rc<dyn CompilerOpts> {
self.update_compiler_opts(|o| o.set_stdenv(new_stdenv))
}
fn override_set_optimize(&self, opt: bool) -> Rc<dyn CompilerOpts> {
self.update_compiler_opts(|o| o.set_optimize(opt))
}
fn override_set_frontend_opt(&self, opt: bool) -> Rc<dyn CompilerOpts> {
self.update_compiler_opts(|o| o.set_frontend_opt(opt))
}
fn override_set_frontend_check_live(&self, check: bool) -> Rc<dyn CompilerOpts> {
self.update_compiler_opts(|o| o.set_frontend_check_live(check))
}
fn override_set_code_generator(&self, new_compiler: PrimaryCodegen) -> Rc<dyn CompilerOpts> {
self.update_compiler_opts(|o| o.set_code_generator(new_compiler))
}
fn override_set_start_env(&self, start_env: Option<Rc<SExp>>) -> Rc<dyn CompilerOpts> {
self.update_compiler_opts(|o| o.set_start_env(start_env))
}
fn override_set_prim_map(
&self,
new_map: Rc<HashMap<Vec<u8>, Rc<SExp>>>,
) -> Rc<dyn CompilerOpts> {
self.update_compiler_opts(|o| o.set_prim_map(new_map))
}
fn override_read_new_file(
&self,
inc_from: String,
filename: String,
) -> Result<(String, Vec<u8>), CompileErr> {
self.compiler_opts().read_new_file(inc_from, filename)
}
fn override_compile_program(
&self,
allocator: &mut Allocator,
runner: Rc<dyn TRunProgram>,
sexp: Rc<SExp>,
symbol_table: &mut HashMap<String, String>,
) -> Result<SExp, CompileErr> {
self.compiler_opts()
.compile_program(allocator, runner, sexp, symbol_table)
}
}

impl<T: HasCompilerOptsDelegation> CompilerOpts for T {
// Defaults.
fn filename(&self) -> String {
self.override_filename()
}
fn code_generator(&self) -> Option<PrimaryCodegen> {
self.override_code_generator()
}
fn dialect(&self) -> AcceptedDialect {
self.override_dialect()
}
fn disassembly_ver(&self) -> Option<usize> {
self.override_disassembly_ver()
}
fn in_defun(&self) -> bool {
self.override_in_defun()
}
fn stdenv(&self) -> bool {
self.override_stdenv()
}
fn optimize(&self) -> bool {
self.override_optimize()
}
fn frontend_opt(&self) -> bool {
self.override_frontend_opt()
}
fn frontend_check_live(&self) -> bool {
self.override_frontend_check_live()
}
fn start_env(&self) -> Option<Rc<SExp>> {
self.override_start_env()
}
fn prim_map(&self) -> Rc<HashMap<Vec<u8>, Rc<SExp>>> {
self.override_prim_map()
}
fn get_search_paths(&self) -> Vec<String> {
self.override_get_search_paths()
}

fn set_dialect(&self, dialect: AcceptedDialect) -> Rc<dyn CompilerOpts> {
self.override_set_dialect(dialect)
}
fn set_search_paths(&self, dirs: &[String]) -> Rc<dyn CompilerOpts> {
self.override_set_search_paths(dirs)
}
fn set_disassembly_ver(&self, ver: Option<usize>) -> Rc<dyn CompilerOpts> {
self.override_set_disassembly_ver(ver)
}
fn set_in_defun(&self, new_in_defun: bool) -> Rc<dyn CompilerOpts> {
self.override_set_in_defun(new_in_defun)
}
fn set_stdenv(&self, new_stdenv: bool) -> Rc<dyn CompilerOpts> {
self.override_set_stdenv(new_stdenv)
}
fn set_optimize(&self, opt: bool) -> Rc<dyn CompilerOpts> {
self.override_set_optimize(opt)
}
fn set_frontend_opt(&self, opt: bool) -> Rc<dyn CompilerOpts> {
self.override_set_frontend_opt(opt)
}
fn set_frontend_check_live(&self, check: bool) -> Rc<dyn CompilerOpts> {
self.override_set_frontend_check_live(check)
}
fn set_code_generator(&self, new_compiler: PrimaryCodegen) -> Rc<dyn CompilerOpts> {
self.override_set_code_generator(new_compiler)
}
fn set_start_env(&self, start_env: Option<Rc<SExp>>) -> Rc<dyn CompilerOpts> {
self.override_set_start_env(start_env)
}
fn set_prim_map(&self, new_map: Rc<HashMap<Vec<u8>, Rc<SExp>>>) -> Rc<dyn CompilerOpts> {
self.override_set_prim_map(new_map)
}
fn read_new_file(
&self,
inc_from: String,
filename: String,
) -> Result<(String, Vec<u8>), CompileErr> {
self.override_read_new_file(inc_from, filename)
}
fn compile_program(
&self,
allocator: &mut Allocator,
runner: Rc<dyn TRunProgram>,
sexp: Rc<SExp>,
symbol_table: &mut HashMap<String, String>,
) -> Result<SExp, CompileErr> {
self.override_compile_program(allocator, runner, sexp, symbol_table)
}
}

/// Frontend uses this to accumulate frontend forms, used internally.
#[derive(Debug)]
pub struct ModAccum {
Expand Down
113 changes: 24 additions & 89 deletions src/tests/classic/stage_2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@ use crate::classic::clvm_tools::binutils::{assemble, assemble_from_ir, disassemb
use crate::classic::clvm_tools::clvmc::compile_clvm_text;
use crate::classic::clvm_tools::cmds::call_tool;
use crate::classic::clvm_tools::ir::reader::read_ir;
use crate::classic::clvm_tools::stages::stage_0::TRunProgram;
use crate::classic::clvm_tools::stages::stage_2::compile::{
do_com_prog, get_compile_filename, get_last_path_component, try_expand_macro_for_atom,
};
use crate::classic::clvm_tools::stages::stage_2::helpers::{brun, evaluate, quote, run};
use crate::classic::clvm_tools::stages::stage_2::operators::run_program_for_search_paths;
use crate::classic::clvm_tools::stages::stage_2::reader::{process_embed_file, read_file};

use crate::compiler::comptypes::{CompileErr, CompilerOpts, PrimaryCodegen};
use crate::compiler::dialect::AcceptedDialect;
use crate::compiler::sexp::{decode_string, SExp};
use crate::compiler::compiler::DefaultCompilerOpts;
use crate::compiler::comptypes::{CompileErr, CompilerOpts, HasCompilerOptsDelegation};
use crate::compiler::sexp::decode_string;
use crate::compiler::srcloc::Srcloc;

fn test_expand_macro(
Expand Down Expand Up @@ -300,90 +299,38 @@ fn test_process_embed_file_as_hex() {
assert_eq!(name, b"test-embed-from-hex");
}

#[derive(Clone, Debug)]
#[derive(Clone)]
struct TestCompilerOptsPresentsOwnFiles {
filename: String,
files: HashMap<String, String>,
files: Rc<HashMap<String, String>>,
opts: Rc<dyn CompilerOpts>,
}

impl TestCompilerOptsPresentsOwnFiles {
fn new(filename: String, files: HashMap<String, String>) -> Self {
TestCompilerOptsPresentsOwnFiles { filename, files }
TestCompilerOptsPresentsOwnFiles {
files: Rc::new(files),
opts: Rc::new(DefaultCompilerOpts::new(&filename)),
}
}
}

impl CompilerOpts for TestCompilerOptsPresentsOwnFiles {
fn filename(&self) -> String {
self.filename.clone()
impl HasCompilerOptsDelegation for TestCompilerOptsPresentsOwnFiles {
fn compiler_opts(&self) -> Rc<dyn CompilerOpts> {
self.opts.clone()
}

fn code_generator(&self) -> Option<PrimaryCodegen> {
None
}
fn dialect(&self) -> AcceptedDialect {
AcceptedDialect::default()
}
fn in_defun(&self) -> bool {
false
}
fn stdenv(&self) -> bool {
false
}
fn optimize(&self) -> bool {
false
}
fn frontend_opt(&self) -> bool {
false
}
fn frontend_check_live(&self) -> bool {
false
}
fn start_env(&self) -> Option<Rc<SExp>> {
None
}
fn disassembly_ver(&self) -> Option<usize> {
None
}
fn prim_map(&self) -> Rc<HashMap<Vec<u8>, Rc<SExp>>> {
Rc::new(HashMap::new())
}
fn get_search_paths(&self) -> Vec<String> {
vec![".".to_string()]
}
fn set_dialect(&self, _dialect: AcceptedDialect) -> Rc<dyn CompilerOpts> {
Rc::new(self.clone())
}
fn set_search_paths(&self, _dirs: &[String]) -> Rc<dyn CompilerOpts> {
Rc::new(self.clone())
}
fn set_in_defun(&self, _new_in_defun: bool) -> Rc<dyn CompilerOpts> {
Rc::new(self.clone())
}
fn set_stdenv(&self, _new_stdenv: bool) -> Rc<dyn CompilerOpts> {
Rc::new(self.clone())
}
fn set_optimize(&self, _opt: bool) -> Rc<dyn CompilerOpts> {
Rc::new(self.clone())
}
fn set_frontend_opt(&self, _opt: bool) -> Rc<dyn CompilerOpts> {
Rc::new(self.clone())
}
fn set_frontend_check_live(&self, _check: bool) -> Rc<dyn CompilerOpts> {
Rc::new(self.clone())
}
fn set_code_generator(&self, _new_compiler: PrimaryCodegen) -> Rc<dyn CompilerOpts> {
Rc::new(self.clone())
}
fn set_start_env(&self, _start_env: Option<Rc<SExp>>) -> Rc<dyn CompilerOpts> {
Rc::new(self.clone())
}
fn set_prim_map(&self, _prims: Rc<HashMap<Vec<u8>, Rc<SExp>>>) -> Rc<dyn CompilerOpts> {
Rc::new(self.clone())
}
fn set_disassembly_ver(&self, _ver: Option<usize>) -> Rc<dyn CompilerOpts> {
Rc::new(self.clone())
fn update_compiler_opts<F: FnOnce(Rc<dyn CompilerOpts>) -> Rc<dyn CompilerOpts>>(
&self,
f: F,
) -> Rc<dyn CompilerOpts> {
let new_opts = f(self.opts.clone());
Rc::new(TestCompilerOptsPresentsOwnFiles {
opts: new_opts,
..self.clone()
})
}
fn read_new_file(

fn override_read_new_file(
&self,
inc_from: String,
filename: String,
Expand All @@ -397,18 +344,6 @@ impl CompilerOpts for TestCompilerOptsPresentsOwnFiles {
format!("could not read {filename}"),
))
}
fn compile_program(
&self,
_allocator: &mut Allocator,
_runner: Rc<dyn TRunProgram>,
_sexp: Rc<SExp>,
_symbol_table: &mut HashMap<String, String>,
) -> Result<SExp, CompileErr> {
Err(CompileErr(
Srcloc::start(&self.filename),
"test object only".to_string(),
))
}
}

// Shows that we can inject a compiler opts and have it provide file data.
Expand Down
Loading

0 comments on commit 78e8362

Please sign in to comment.