Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow overridable method delegation in downstream CompilerOpts implementations #85

Merged
merged 4 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading