diff --git a/Cargo.lock b/Cargo.lock index 82d47a1e..1a328be5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -602,9 +602,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "pallas-addresses" -version = "0.14.0-alpha.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1188a2434037b74129f8d209a37a1d09721b900e6e378255db1a91abc37199bc" +checksum = "c82eb1d0c40048cdfcbb06f68eca9b0bb96d0aea621a29f9aa4d8f000803ee34" dependencies = [ "base58", "bech32", @@ -616,9 +616,9 @@ dependencies = [ [[package]] name = "pallas-codec" -version = "0.14.0-alpha.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65c035a772aa84e858e53b7c98e6036eaa216d8a699bb9c826787722bde13d05" +checksum = "32560b75dcbab63b26bd3f529f00d39720118e79506b29b24d746007df71dc9b" dependencies = [ "hex", "minicbor", @@ -627,9 +627,9 @@ dependencies = [ [[package]] name = "pallas-crypto" -version = "0.14.0-alpha.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2841f9225dcd6a78c6f386d4d5e76bcdbecd7b4489455b2d2485b105bf4c0499" +checksum = "2280ac7df1ef64ba253846e83d4a0e7dfdae7507a7ab3cb31c7292a40f17976c" dependencies = [ "cryptoxide", "hex", @@ -641,9 +641,9 @@ dependencies = [ [[package]] name = "pallas-primitives" -version = "0.14.0-alpha.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e51824547f7a1e1a6574ecec4bf8557f3819f435132873c0bae97acba81cbb1" +checksum = "0f2fc36d67a90cedafd0e120608ebf004464be462506fdd8fab96c4540a03f71" dependencies = [ "base58", "bech32", @@ -657,9 +657,9 @@ dependencies = [ [[package]] name = "pallas-traverse" -version = "0.14.0-alpha.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3291d1ae31cd803b9142fb32e1fcb0a58fc1d65b3bbe1d527d14b322db6eb019" +checksum = "c48e218019e6ff9dd368642db9bbe4c87382846932470b20643198b7df377769" dependencies = [ "hex", "pallas-addresses", diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs index 754caa7f..6cc2da41 100644 --- a/crates/lang/src/lib.rs +++ b/crates/lang/src/lib.rs @@ -10,6 +10,8 @@ pub mod format; pub mod parser; pub mod pretty; pub mod tipo; +pub mod token; +pub mod uplc; #[derive(Debug, Default, Clone)] pub struct IdGenerator { diff --git a/crates/lang/src/uplc.rs b/crates/lang/src/uplc.rs new file mode 100644 index 00000000..09afdad3 --- /dev/null +++ b/crates/lang/src/uplc.rs @@ -0,0 +1,837 @@ +use std::{collections::HashMap, rc::Rc, sync::Arc}; + +use uplc::{ + ast::{Constant, Name, Program, Term, Unique}, + builtins::DefaultFunction, + parser::interner::Interner, +}; + +use crate::{ + ast::{self, DataType, Function, ModuleConstant, Pattern, TypeAlias, TypedArg, Use}, + expr::TypedExpr, + tipo::{self, ModuleValueConstructor, Type, ValueConstructorVariant}, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ScopeLevels { + scope_tracker: Vec, +} + +impl ScopeLevels { + pub fn new() -> Self { + ScopeLevels { + scope_tracker: vec![0], + } + } + + pub fn is_less_than(&self, other: &ScopeLevels) -> bool { + if self.scope_tracker.is_empty() && !other.scope_tracker.is_empty() { + return true; + } else if other.scope_tracker.is_empty() { + return false; + } + + let mut result = self.scope_tracker.len() < other.scope_tracker.len(); + + for (scope_self, scope_other) in self.scope_tracker.iter().zip(other.scope_tracker.iter()) { + match scope_self.cmp(scope_other) { + std::cmp::Ordering::Less => { + result = true; + break; + } + std::cmp::Ordering::Equal => {} + std::cmp::Ordering::Greater => { + result = false; + break; + } + } + } + result + } + + pub fn scope_increment_sequence(&self, inc: i32) -> ScopeLevels { + let mut new_scope = self.clone(); + *new_scope.scope_tracker.last_mut().unwrap() += inc; + new_scope.scope_tracker.push(0); + new_scope + } + + pub fn scope_increment(&self, inc: i32) -> ScopeLevels { + let mut new_scope = self.clone(); + *new_scope.scope_tracker.last_mut().unwrap() += inc; + new_scope + } +} + +impl Default for ScopeLevels { + fn default() -> Self { + Self::new() + } +} + +pub struct CodeGenerator<'a> { + uplc_function_holder: Vec<(String, Term)>, + uplc_function_holder_lookup: HashMap<(String, String), (ScopeLevels, TypedExpr)>, + uplc_data_holder_lookup: HashMap<(String, String, String), (ScopeLevels, TypedExpr)>, + uplc_data_usage_holder_lookup: HashMap<(String, String), ScopeLevels>, + functions: &'a HashMap<(String, String), &'a Function, TypedExpr>>, + type_aliases: &'a HashMap<(String, String), &'a TypeAlias>>, + data_types: &'a HashMap<(String, String), &'a DataType>>, + imports: &'a HashMap<(String, String), &'a Use>, + constants: &'a HashMap<(String, String), &'a ModuleConstant, String>>, +} + +impl<'a> CodeGenerator<'a> { + pub fn new( + functions: &'a HashMap<(String, String), &'a Function, TypedExpr>>, + type_aliases: &'a HashMap<(String, String), &'a TypeAlias>>, + data_types: &'a HashMap<(String, String), &'a DataType>>, + imports: &'a HashMap<(String, String), &'a Use>, + constants: &'a HashMap<(String, String), &'a ModuleConstant, String>>, + ) -> Self { + CodeGenerator { + uplc_function_holder: Vec::new(), + uplc_function_holder_lookup: HashMap::new(), + uplc_data_holder_lookup: HashMap::new(), + uplc_data_usage_holder_lookup: HashMap::new(), + functions, + type_aliases, + data_types, + imports, + constants, + } + } + + pub fn generate(&mut self, body: TypedExpr, arguments: Vec) -> Program { + self.recurse_scope_level(&body, ScopeLevels::new()); + + let mut term = self.recurse_code_gen(&body, ScopeLevels::new()); + + for arg in arguments.iter().rev() { + term = Term::Lambda { + parameter_name: uplc::ast::Name { + text: arg.arg_name.get_variable_name().unwrap_or("_").to_string(), + unique: Unique::new(0), + }, + body: Rc::new(term), + } + } + + let mut program = Program { + version: (1, 0, 0), + term, + }; + + let mut interner = Interner::new(); + + interner.program(&mut program); + + println!("{}", program.to_pretty()); + + program + } + + pub(crate) fn recurse_scope_level(&mut self, body: &TypedExpr, scope_level: ScopeLevels) { + match dbg!(body) { + TypedExpr::Int { value, .. } => {} + TypedExpr::String { value, .. } => {} + TypedExpr::ByteArray { bytes, .. } => {} + TypedExpr::Sequence { + location, + expressions, + } => { + // let mut terms = Vec::new(); + for (i, exp) in expressions.iter().enumerate().rev() { + self.recurse_scope_level( + exp, + scope_level.scope_increment_sequence(i as i32 + 1), + ); + } + } + TypedExpr::Pipeline { + location, + expressions, + } => todo!(), + TypedExpr::Var { + location, + constructor, + name, + } => {} + TypedExpr::Fn { + location, + tipo, + is_capture, + args, + body, + return_annotation, + } => todo!(), + TypedExpr::List { + location, + tipo, + elements, + tail, + } => todo!(), + TypedExpr::Call { + location, + tipo, + fun, + args, + } => { + self.recurse_scope_level(fun, scope_level.scope_increment(args.len() as i32 + 1)); + + for (i, arg) in args.iter().enumerate() { + self.recurse_scope_level(&arg.value, scope_level.scope_increment(i as i32 + 1)); + } + } + TypedExpr::BinOp { + location, + tipo, + name, + left, + right, + } => { + self.recurse_scope_level(left, scope_level.clone()); + self.recurse_scope_level(right, scope_level); + } + TypedExpr::Assignment { + location, + tipo, + value, + pattern, + kind, + } => self.recurse_scope_level_pattern(pattern, value, scope_level), + TypedExpr::Try { + location, + tipo, + value, + then, + pattern, + } => todo!(), + TypedExpr::When { + location, + tipo, + subjects, + clauses, + } => { + for clause in clauses { + for pattern in clause.pattern.iter() { + self.recurse_scope_level_pattern( + pattern, + &clause.then, + scope_level.scope_increment_sequence(1), + ) + } + } + } + //if statements increase scope due to branching. + TypedExpr::If { + branches, + final_else, + .. + } => { + self.recurse_scope_level(final_else, scope_level.scope_increment_sequence(1)); + + for branch in branches { + // Need some scoping count to potentially replace condition with var since we should assume a condition + // may be repeated 3 + times or be large enough series of binops to warrant var replacement + self.recurse_scope_level( + &branch.condition, + scope_level.scope_increment_sequence(1), // Since this happens before branching. Maybe not increase scope level + ); + + self.recurse_scope_level(&branch.body, scope_level.scope_increment_sequence(1)); + } + } + a @ TypedExpr::RecordAccess { + tipo, + label, + index, + record, + .. + } => { + println!("tipo {tipo:#?}"); + println!("label {label:#?}"); + println!("index {index:#?}"); + println!("record {record:#?}"); + + match &**record { + TypedExpr::Var { constructor, .. } => { + match (constructor.variant.clone(), (*constructor.tipo).clone()) { + ( + ValueConstructorVariant::LocalVariable { .. }, + Type::App { module, name, .. }, + ) => { + self.uplc_data_holder_lookup.insert( + (module.clone(), name.clone(), label.to_string()), + (scope_level.clone(), a.clone()), + ); + + if let Some(val) = self + .uplc_data_usage_holder_lookup + .get(&(module.clone(), name.clone())) + { + if scope_level.is_less_than(val) { + self.uplc_data_usage_holder_lookup + .insert((module, name), scope_level); + } + } else { + self.uplc_data_usage_holder_lookup + .insert((module, name), scope_level); + } + } + _ => todo!(), + } + } + _ => todo!(), + } + } + a @ TypedExpr::ModuleSelect { + location, + tipo, + label, + module_name, + module_alias, + constructor, + } => match constructor { + ModuleValueConstructor::Record { + name, + arity, + tipo, + field_map, + location, + } => todo!(), + ModuleValueConstructor::Fn { + location, + module, + name, + } => { + if !self + .uplc_function_holder_lookup + .contains_key(&(module.to_string(), name.to_string())) + { + let func_def = self + .functions + .get(&(module.to_string(), name.to_string())) + .unwrap(); + + self.recurse_scope_level( + &func_def.body, + scope_level.scope_increment(func_def.arguments.len() as i32 + 1), + ); + + self.uplc_function_holder_lookup.insert( + (module.to_string(), name.to_string()), + (scope_level.clone(), a.clone()), + ); + } + + if scope_level.is_less_than( + &self + .uplc_function_holder_lookup + .get(&(module.to_string(), name.to_string())) + .unwrap() + .0, + ) { + self.uplc_function_holder_lookup.insert( + (module.to_string(), name.to_string()), + (scope_level, a.clone()), + ); + } + } + ModuleValueConstructor::Constant { literal, location } => todo!(), + }, + TypedExpr::Todo { + location, + label, + tipo, + } => todo!(), + TypedExpr::RecordUpdate { + location, + tipo, + spread, + args, + } => todo!(), + TypedExpr::Negate { location, value } => todo!(), + } + } + + fn recurse_scope_level_pattern( + &mut self, + pattern: &Pattern, + value: &TypedExpr, + scope_level: ScopeLevels, + ) { + match pattern { + Pattern::Int { .. } | Pattern::String { .. } | Pattern::Var { .. } => { + self.recurse_scope_level(value, scope_level); + } + + Pattern::VarUsage { + location, + name, + tipo, + } => todo!(), + Pattern::Assign { + name, + location, + pattern, + } => todo!(), + Pattern::Discard { name, location } => todo!(), + Pattern::List { + location, + elements, + tail, + } => todo!(), + Pattern::Constructor { + location, + name, + arguments, + module, + constructor, + with_spread, + tipo, + } => { + self.recurse_scope_level(value, scope_level); + + } + } + } + + fn recurse_code_gen(&mut self, body: &TypedExpr, scope_level: ScopeLevels) -> Term { + match dbg!(body) { + TypedExpr::Int { value, .. } => { + Term::Constant(Constant::Integer(value.parse::().unwrap())) + } + TypedExpr::String { value, .. } => Term::Constant(Constant::String(value.clone())), + TypedExpr::ByteArray { bytes, .. } => { + Term::Constant(Constant::ByteString(bytes.clone())) + } + TypedExpr::Sequence { + location, + expressions, + } => { + for (i, exp) in expressions.iter().enumerate().rev() { + let mut term = self + .recurse_code_gen(exp, scope_level.scope_increment_sequence(i as i32 + 1)); + + term = self + .maybe_insert_def(term, scope_level.scope_increment_sequence(i as i32 + 1)); + + self.uplc_function_holder + .push(("".to_string(), term.clone())); + } + + self.uplc_function_holder.pop().unwrap().1 + } + TypedExpr::Pipeline { + location, + expressions, + } => todo!(), + TypedExpr::Var { + location, + constructor, + name, + } => { + if name == "True" || name == "False" { + Term::Constant(Constant::Bool(name == "True")) + } else { + match constructor.variant.clone() { + ValueConstructorVariant::LocalVariable { location } => Term::Var(Name { + text: name.to_string(), + unique: 0.into(), + }), + ValueConstructorVariant::ModuleConstant { + location, + module, + literal, + } => todo!(), + ValueConstructorVariant::ModuleFn { + name, + field_map, + module, + arity, + location, + builtin, + } => todo!(), + ValueConstructorVariant::Record { + name, + arity, + field_map, + location, + module, + constructors_count, + } => todo!(), + } + } + } + TypedExpr::Fn { + location, + tipo, + is_capture, + args, + body, + return_annotation, + } => todo!(), + TypedExpr::List { + location, + tipo, + elements, + tail, + } => todo!(), + TypedExpr::Call { + location, + tipo, + fun, + args, + } => { + let mut term = + self.recurse_code_gen(fun, scope_level.scope_increment(args.len() as i32 + 1)); + + for (i, arg) in args.iter().enumerate() { + term = Term::Apply { + function: term.into(), + argument: self + .recurse_code_gen(&arg.value, scope_level.scope_increment(i as i32 + 1)) + .into(), + }; + } + + term + } + TypedExpr::BinOp { + location, + tipo, + name, + left, + right, + } => { + let left_term = self.recurse_code_gen(left, scope_level.clone()); + + let right_term = self.recurse_code_gen(right, scope_level); + println!("NAME IS {name:#?}"); + match name { + Eq => match &*left.tipo() { + Type::App { + public, + module, + name, + args, + } => match name.as_str() { + "Int" => Term::Apply { + function: Term::Apply { + function: Term::Builtin(DefaultFunction::EqualsInteger).into(), + argument: left_term.into(), + } + .into(), + argument: right_term.into(), + }, + + "String" => Term::Apply { + function: Term::Apply { + function: Term::Builtin(DefaultFunction::EqualsString).into(), + argument: left_term.into(), + } + .into(), + argument: right_term.into(), + }, + + _ => todo!(), + }, + Type::Fn { args, ret } => todo!(), + Type::Var { tipo } => todo!(), + }, + And => todo!(), + Or => todo!(), + NotEq => todo!(), + LtInt => todo!(), + LtEqInt => todo!(), + GtEqInt => todo!(), + GtInt => todo!(), + AddInt => todo!(), + SubInt => todo!(), + MultInt => todo!(), + DivInt => todo!(), + ModInt => todo!(), + } + } + TypedExpr::Assignment { + location, + tipo, + value, + pattern, + kind, + } => match pattern { + Pattern::Int { location, value } => todo!(), + Pattern::String { location, value } => todo!(), + Pattern::Var { location, name } => Term::Apply { + function: Term::Lambda { + parameter_name: Name { + text: name.to_string(), + unique: 0.into(), + }, + body: self.uplc_function_holder.pop().unwrap().1.into(), + } + .into(), + argument: self + .recurse_code_gen(value, scope_level.scope_increment(1)) + .into(), + }, + + Pattern::VarUsage { + location, + name, + tipo, + } => todo!(), + Pattern::Assign { + name, + location, + pattern, + } => todo!(), + Pattern::Discard { name, location } => todo!(), + Pattern::List { + location, + elements, + tail, + } => todo!(), + Pattern::Constructor { + location, + name, + arguments, + module, + constructor, + with_spread, + tipo, + } => todo!(), + }, + TypedExpr::Try { + location, + tipo, + value, + then, + pattern, + } => todo!(), + TypedExpr::When { + location, + tipo, + subjects, + clauses, + } => todo!(), + //if statements increase scope due to branching. + TypedExpr::If { + branches, + final_else, + .. + } => { + let mut final_if_term = + self.recurse_code_gen(final_else, scope_level.scope_increment_sequence(1)); + + if branches.len() == 1 { + let condition_term = self.recurse_code_gen( + &branches[0].condition, + scope_level.scope_increment_sequence(1), + ); + + let branch_term = self.recurse_code_gen( + &branches[0].body, + scope_level.scope_increment_sequence(1), + ); + + match (final_if_term.clone(), branch_term.clone()) { + ( + Term::Var(..) | Term::Constant(..), + Term::Var(..) | Term::Constant(..), + ) => { + final_if_term = Term::Apply { + function: Rc::new(Term::Apply { + function: Rc::new(Term::Apply { + function: Rc::new(Term::Force(Rc::new(Term::Builtin( + DefaultFunction::IfThenElse, + )))), + argument: Rc::new(condition_term), + }), + //If this is just a var then don't include delay + argument: Rc::new(branch_term), + }), + //If this is just a var then don't include delay + argument: Rc::new(final_if_term.clone()), + }; + } + _ => { + final_if_term = Term::Force( + Term::Apply { + function: Rc::new(Term::Apply { + function: Rc::new(Term::Apply { + function: Rc::new(Term::Force(Rc::new(Term::Builtin( + DefaultFunction::IfThenElse, + )))), + argument: Rc::new(condition_term), + }), + argument: Rc::new(Term::Delay(Rc::new(branch_term))), + }), + argument: Rc::new(Term::Delay(Rc::new(final_if_term.clone()))), + } + .into(), + ); + } + } + } else { + //TODO: for multi branch if statements we can insert function definitions between branches + for branch in branches { + let condition_term = self.recurse_code_gen( + &branch.condition, + scope_level.scope_increment_sequence(1), + ); + + let branch_term = self.recurse_code_gen( + &branch.body, + scope_level.scope_increment_sequence(1), + ); + + final_if_term = Term::Force( + Term::Apply { + function: Rc::new(Term::Apply { + function: Rc::new(Term::Apply { + function: Rc::new(Term::Force(Rc::new(Term::Builtin( + DefaultFunction::IfThenElse, + )))), + argument: Rc::new(condition_term), + }), + argument: Rc::new(Term::Delay(Rc::new(branch_term))), + }), + argument: Rc::new(Term::Delay(Rc::new(final_if_term.clone()))), + } + .into(), + ); + } + } + self.maybe_insert_def(final_if_term, scope_level) + } + TypedExpr::RecordAccess { + location, + tipo, + label, + index, + record, + } => match &**record { + TypedExpr::Var { constructor, .. } => { + match (constructor.variant.clone(), (*constructor.tipo).clone()) { + ( + ValueConstructorVariant::LocalVariable { .. }, + Type::App { module, name, .. }, + ) => Term::Var(Name { + text: format!("{module}_{name}.{label}"), + unique: 0.into(), + }), + _ => todo!(), + } + } + _ => todo!(), + }, + TypedExpr::ModuleSelect { + location, + tipo, + label, + module_name, + module_alias, + constructor, + } => match constructor { + ModuleValueConstructor::Record { + name, + arity, + tipo, + field_map, + location, + } => todo!(), + ModuleValueConstructor::Fn { + location, + module, + name, + } => Term::Var(Name { + text: format!("{module}_{name}"), + unique: 0.into(), + }), + ModuleValueConstructor::Constant { literal, location } => todo!(), + }, + TypedExpr::Todo { + location, + label, + tipo, + } => todo!(), + TypedExpr::RecordUpdate { + location, + tipo, + spread, + args, + } => todo!(), + TypedExpr::Negate { location, value } => todo!(), + } + } + + fn maybe_insert_def( + &mut self, + current_term: Term, + scope_level: ScopeLevels, + ) -> Term { + let mut term = current_term; + for func in self.uplc_function_holder_lookup.clone().keys() { + if scope_level.is_less_than( + &self + .uplc_function_holder_lookup + .clone() + .get(func) + .unwrap() + .0, + ) { + let func_def = self + .functions + .get(&(func.0.to_string(), func.1.to_string())) + .unwrap(); + + let mut function_body = self.recurse_code_gen( + &func_def.body, + scope_level.scope_increment_sequence(func_def.arguments.len() as i32), + ); + + for arg in func_def.arguments.iter().rev() { + function_body = Term::Lambda { + parameter_name: Name { + text: arg.arg_name.get_variable_name().unwrap_or("_").to_string(), + unique: Unique::new(0), + }, + body: Rc::new(function_body), + } + } + + term = Term::Apply { + function: Term::Lambda { + parameter_name: Name { + text: format!("{}_{}", func.0, func.1), + unique: 0.into(), + }, + body: term.into(), + } + .into(), + argument: function_body.into(), + }; + self.uplc_function_holder_lookup.remove(func); + } + } + + for record_scope in self.uplc_data_usage_holder_lookup.keys() { + if scope_level.is_less_than( + self.uplc_data_usage_holder_lookup + .get(record_scope) + .unwrap(), + ) { + for record in self.uplc_data_holder_lookup.keys() { + if &(record.0.clone(), record.1.clone()) == record_scope { + let dt = self.data_types.get(record_scope).unwrap(); + println!("DATA TYPE IS {dt:#?}") + } + } + } + } + + term + } +} diff --git a/crates/project/src/lib.rs b/crates/project/src/lib.rs index ef5669ed..a39515c8 100644 --- a/crates/project/src/lib.rs +++ b/crates/project/src/lib.rs @@ -2,7 +2,6 @@ use std::{ collections::HashMap, fs, path::{Path, PathBuf}, - rc::Rc, }; pub mod config; @@ -11,17 +10,13 @@ pub mod format; pub mod module; use aiken_lang::{ - ast::{self, Definition, Function, ModuleKind}, + ast::{Definition, Function, ModuleKind}, builtins, - expr::TypedExpr, - tipo::{self, ModuleValueConstructor, TypeInfo}, + tipo::TypeInfo, + uplc::CodeGenerator, IdGenerator, }; -use uplc::{ - ast::{Constant, Name, NamedDeBruijn, Program, Term, Unique}, - builtins::DefaultFunction, - parser::interner::Interner, -}; +use uplc::ast::{NamedDeBruijn, Program}; use crate::{ config::Config, @@ -43,63 +38,6 @@ pub const MINT: &str = "mint"; pub const WITHDRAWL: &str = "withdrawl"; pub const VALIDATOR_NAMES: [&str; 4] = [SPEND, CERT, MINT, WITHDRAWL]; -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ScopeLevels { - scope_tracker: Vec, -} - -impl ScopeLevels { - pub fn new() -> Self { - ScopeLevels { - scope_tracker: vec![0], - } - } - - pub fn is_less_than(&self, other: &ScopeLevels) -> bool { - if self.scope_tracker.is_empty() && !other.scope_tracker.is_empty() { - return true; - } else if other.scope_tracker.is_empty() { - return false; - } - - let mut result = self.scope_tracker.len() < other.scope_tracker.len(); - - for (scope_self, scope_other) in self.scope_tracker.iter().zip(other.scope_tracker.iter()) { - match scope_self.cmp(scope_other) { - std::cmp::Ordering::Less => { - result = true; - break; - } - std::cmp::Ordering::Equal => {} - std::cmp::Ordering::Greater => { - result = false; - break; - } - } - } - result - } - - pub fn scope_increment_sequence(&self, inc: i32) -> ScopeLevels { - let mut new_scope = self.clone(); - *new_scope.scope_tracker.last_mut().unwrap() += inc; - new_scope.scope_tracker.push(0); - new_scope - } - - pub fn scope_increment(&self, inc: i32) -> ScopeLevels { - let mut new_scope = self.clone(); - *new_scope.scope_tracker.last_mut().unwrap() += inc; - new_scope - } -} - -impl Default for ScopeLevels { - fn default() -> Self { - Self::new() - } -} - pub struct Project { config: Config, defined_modules: HashMap, @@ -150,7 +88,7 @@ impl Project { let scripts = self.validate_scripts(&mut checked_modules)?; if uplc_gen { - self.code_gen(&scripts, &checked_modules)?; + self.code_gen(scripts, &checked_modules)?; } Ok(()) @@ -365,11 +303,10 @@ impl Project { fn code_gen( &mut self, - scripts: &[CheckedModule], + scripts: Vec, checked_modules: &CheckedModules, ) -> Result>, Error> { let mut programs = Vec::new(); - let mut uplc_function_holder = Vec::new(); let mut functions = HashMap::new(); let mut type_aliases = HashMap::new(); let mut data_types = HashMap::new(); @@ -399,7 +336,7 @@ impl Project { } for script in scripts { - for def in script.ast.definitions() { + for def in script.ast.into_definitions() { if let Definition::Fn(Function { arguments, name, @@ -408,15 +345,7 @@ impl Project { }) = def { if VALIDATOR_NAMES.contains(&name.as_str()) { - let type_info = self.module_types.get(&script.name).unwrap(); - - let mut lookup_map = HashMap::new(); - - self.recurse_scope_level( - body, - scripts, - ScopeLevels::new(), - &mut lookup_map, + let mut generator = CodeGenerator::new( &functions, &type_aliases, &data_types, @@ -424,43 +353,7 @@ impl Project { &constants, ); - let mut term = self.recurse_code_gen( - body, - scripts, - ScopeLevels::new(), - &mut uplc_function_holder, - &mut lookup_map, - &functions, - &type_aliases, - &data_types, - &imports, - &constants, - ); - - for arg in arguments.iter().rev() { - term = Term::Lambda { - parameter_name: uplc::ast::Name { - text: arg - .arg_name - .get_variable_name() - .unwrap_or("_") - .to_string(), - unique: Unique::new(0), - }, - body: Rc::new(term), - } - } - - let mut program = Program { - version: (1, 0, 0), - term, - }; - - let mut interner = Interner::new(); - - interner.program(&mut program); - - println!("{}", program.to_pretty()); + let program = generator.generate(body, arguments); programs.push(program.try_into().unwrap()); } @@ -471,908 +364,6 @@ impl Project { Ok(programs) } - pub(crate) fn recurse_scope_level( - &self, - body: &TypedExpr, - scripts: &[CheckedModule], - scope_level: ScopeLevels, - uplc_function_holder_lookup: &mut HashMap<(String, String), (ScopeLevels, TypedExpr)>, - functions: &HashMap<(String, String), &Function, TypedExpr>>, - type_aliases: &HashMap<(String, String), &ast::TypeAlias>>, - data_types: &HashMap<(String, String), &ast::DataType>>, - imports: &HashMap<(String, String), &ast::Use>, - constants: &HashMap< - (String, String), - &ast::ModuleConstant, String>, - >, - ) { - match body { - TypedExpr::Int { value, .. } => {} - TypedExpr::String { value, .. } => {} - TypedExpr::ByteArray { bytes, .. } => {} - TypedExpr::Sequence { - location, - expressions, - } => { - // let mut terms = Vec::new(); - for (i, exp) in expressions.iter().enumerate().rev() { - self.recurse_scope_level( - exp, - scripts, - scope_level.scope_increment_sequence(i as i32 + 1), - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - } - } - TypedExpr::Pipeline { - location, - expressions, - } => todo!(), - TypedExpr::Var { - location, - constructor, - name, - } => {} - TypedExpr::Fn { - location, - tipo, - is_capture, - args, - body, - return_annotation, - } => todo!(), - TypedExpr::List { - location, - tipo, - elements, - tail, - } => todo!(), - TypedExpr::Call { - location, - tipo, - fun, - args, - } => { - self.recurse_scope_level( - fun, - scripts, - scope_level.scope_increment(args.len() as i32 + 1), - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - for (i, arg) in args.iter().enumerate() { - self.recurse_scope_level( - &arg.value, - scripts, - scope_level.scope_increment(i as i32 + 1), - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - } - } - TypedExpr::BinOp { - location, - tipo, - name, - left, - right, - } => match name { - ast::BinOp::And => todo!(), - ast::BinOp::Or => todo!(), - ast::BinOp::Eq => { - self.recurse_scope_level( - left, - scripts, - scope_level.clone(), - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - self.recurse_scope_level( - right, - scripts, - scope_level, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - } - ast::BinOp::NotEq => todo!(), - ast::BinOp::LtInt => todo!(), - ast::BinOp::LtEqInt => todo!(), - ast::BinOp::GtEqInt => todo!(), - ast::BinOp::GtInt => todo!(), - ast::BinOp::AddInt => todo!(), - ast::BinOp::SubInt => todo!(), - ast::BinOp::MultInt => todo!(), - ast::BinOp::DivInt => todo!(), - ast::BinOp::ModInt => todo!(), - }, - TypedExpr::Assignment { - location, - tipo, - value, - pattern, - kind, - } => match pattern { - ast::Pattern::Int { location, value } => todo!(), - ast::Pattern::String { location, value } => todo!(), - ast::Pattern::Var { location, name } => { - self.recurse_scope_level( - value, - scripts, - scope_level.scope_increment(1), - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - } - ast::Pattern::VarUsage { - location, - name, - tipo, - } => todo!(), - ast::Pattern::Assign { - name, - location, - pattern, - } => todo!(), - ast::Pattern::Discard { name, location } => todo!(), - ast::Pattern::List { - location, - elements, - tail, - } => todo!(), - ast::Pattern::Constructor { - location, - name, - arguments, - module, - constructor, - with_spread, - tipo, - } => todo!(), - }, - TypedExpr::Try { - location, - tipo, - value, - then, - pattern, - } => todo!(), - TypedExpr::When { - location, - tipo, - subjects, - clauses, - } => todo!(), - //if statements increase scope due to branching. - TypedExpr::If { - branches, - final_else, - .. - } => { - self.recurse_scope_level( - final_else, - scripts, - scope_level.scope_increment_sequence(1), - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - for branch in branches { - // Need some scoping count to potentially replace condition with var since we should assume a condition - // may be repeated 3 + times or be large enough series of binops to warrant var replacement - self.recurse_scope_level( - &branch.condition, - scripts, - scope_level.scope_increment_sequence(1), // Since this happens before branching. Maybe not increase scope level - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - self.recurse_scope_level( - &branch.body, - scripts, - scope_level.scope_increment_sequence(1), - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - } - } - TypedExpr::RecordAccess { - location, - tipo, - label, - index, - record, - } => todo!(), - a @ TypedExpr::ModuleSelect { - location, - tipo, - label, - module_name, - module_alias, - constructor, - } => match constructor { - ModuleValueConstructor::Record { - name, - arity, - tipo, - field_map, - location, - } => todo!(), - ModuleValueConstructor::Fn { - location, - module, - name, - } => { - if !uplc_function_holder_lookup - .contains_key(&(module.to_string(), name.to_string())) - { - let func_def = functions - .get(&(module.to_string(), name.to_string())) - .unwrap(); - - self.recurse_scope_level( - &func_def.body, - scripts, - scope_level.scope_increment(func_def.arguments.len() as i32 + 1), - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - uplc_function_holder_lookup.insert( - (module.to_string(), name.to_string()), - (scope_level.clone(), a.clone()), - ); - } - - if scope_level.is_less_than( - &uplc_function_holder_lookup - .get(&(module.to_string(), name.to_string())) - .unwrap() - .0, - ) { - uplc_function_holder_lookup.insert( - (module.to_string(), name.to_string()), - (scope_level, a.clone()), - ); - } - } - ModuleValueConstructor::Constant { literal, location } => todo!(), - }, - TypedExpr::Todo { - location, - label, - tipo, - } => todo!(), - TypedExpr::RecordUpdate { - location, - tipo, - spread, - args, - } => todo!(), - TypedExpr::Negate { location, value } => todo!(), - } - } - - fn recurse_code_gen( - &self, - body: &aiken_lang::expr::TypedExpr, - scripts: &[CheckedModule], - scope_level: ScopeLevels, - uplc_function_holder: &mut Vec<(String, Term)>, - uplc_function_holder_lookup: &mut HashMap<(String, String), (ScopeLevels, TypedExpr)>, - functions: &HashMap< - (String, String), - &Function, TypedExpr>, - >, - type_aliases: &HashMap< - (String, String), - &aiken_lang::ast::TypeAlias>, - >, - data_types: &HashMap< - (String, String), - &aiken_lang::ast::DataType>, - >, - imports: &HashMap<(String, String), &aiken_lang::ast::Use>, - constants: &HashMap< - (String, String), - &aiken_lang::ast::ModuleConstant, String>, - >, - ) -> Term { - match dbg!(body) { - TypedExpr::Int { value, .. } => { - Term::Constant(Constant::Integer(value.parse::().unwrap())) - } - TypedExpr::String { value, .. } => Term::Constant(Constant::String(value.clone())), - TypedExpr::ByteArray { bytes, .. } => { - Term::Constant(Constant::ByteString(bytes.clone())) - } - TypedExpr::Sequence { - location, - expressions, - } => { - for (i, exp) in expressions.iter().enumerate().rev() { - let mut term = self.recurse_code_gen( - exp, - scripts, - scope_level.scope_increment_sequence(i as i32 + 1), - uplc_function_holder, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - term = self.maybe_insert_func_def( - term, - scripts, - scope_level.scope_increment_sequence(i as i32 + 1), - uplc_function_holder, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - uplc_function_holder.push(("".to_string(), term.clone())); - } - - uplc_function_holder.pop().unwrap().1 - } - TypedExpr::Pipeline { - location, - expressions, - } => todo!(), - TypedExpr::Var { - location, - constructor, - name, - } => { - if name == "True" || name == "False" { - Term::Constant(Constant::Bool(name == "True")) - } else { - match constructor.variant.clone() { - tipo::ValueConstructorVariant::LocalVariable { location } => { - Term::Var(Name { - text: name.to_string(), - unique: 0.into(), - }) - } - tipo::ValueConstructorVariant::ModuleConstant { - location, - module, - literal, - } => todo!(), - tipo::ValueConstructorVariant::ModuleFn { - name, - field_map, - module, - arity, - location, - builtin, - } => todo!(), - tipo::ValueConstructorVariant::Record { - name, - arity, - field_map, - location, - module, - constructors_count, - } => todo!(), - } - } - } - TypedExpr::Fn { - location, - tipo, - is_capture, - args, - body, - return_annotation, - } => todo!(), - TypedExpr::List { - location, - tipo, - elements, - tail, - } => todo!(), - TypedExpr::Call { - location, - tipo, - fun, - args, - } => { - let mut term = self.recurse_code_gen( - fun, - scripts, - scope_level.scope_increment(args.len() as i32 + 1), - uplc_function_holder, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - for (i, arg) in args.iter().enumerate() { - term = Term::Apply { - function: term.into(), - argument: self - .recurse_code_gen( - &arg.value, - scripts, - scope_level.scope_increment(i as i32 + 1), - uplc_function_holder, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ) - .into(), - }; - } - - term - } - TypedExpr::BinOp { - location, - tipo, - name, - left, - right, - } => { - let left_term = self.recurse_code_gen( - left, - scripts, - scope_level.clone(), - uplc_function_holder, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - let right_term = self.recurse_code_gen( - right, - scripts, - scope_level, - uplc_function_holder, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - match name { - ast::BinOp::And => todo!(), - ast::BinOp::Or => todo!(), - ast::BinOp::Eq => match &*left.tipo() { - tipo::Type::App { - public, - module, - name, - args, - } => match name.as_str() { - "Int" => Term::Apply { - function: Term::Apply { - function: Term::Builtin(DefaultFunction::EqualsInteger).into(), - argument: left_term.into(), - } - .into(), - argument: right_term.into(), - }, - - "String" => Term::Apply { - function: Term::Apply { - function: Term::Builtin(DefaultFunction::EqualsString).into(), - argument: left_term.into(), - } - .into(), - argument: right_term.into(), - }, - - _ => todo!(), - }, - tipo::Type::Fn { args, ret } => todo!(), - tipo::Type::Var { tipo } => todo!(), - }, - ast::BinOp::NotEq => todo!(), - ast::BinOp::LtInt => todo!(), - ast::BinOp::LtEqInt => todo!(), - ast::BinOp::GtEqInt => todo!(), - ast::BinOp::GtInt => todo!(), - ast::BinOp::AddInt => todo!(), - ast::BinOp::SubInt => todo!(), - ast::BinOp::MultInt => todo!(), - ast::BinOp::DivInt => todo!(), - ast::BinOp::ModInt => todo!(), - } - } - TypedExpr::Assignment { - location, - tipo, - value, - pattern, - kind, - } => match pattern { - ast::Pattern::Int { location, value } => todo!(), - ast::Pattern::String { location, value } => todo!(), - ast::Pattern::Var { location, name } => Term::Apply { - function: Term::Lambda { - parameter_name: Name { - text: name.to_string(), - unique: 0.into(), - }, - body: uplc_function_holder.pop().unwrap().1.into(), - } - .into(), - argument: self - .recurse_code_gen( - value, - scripts, - scope_level.scope_increment(1), - uplc_function_holder, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ) - .into(), - }, - - ast::Pattern::VarUsage { - location, - name, - tipo, - } => todo!(), - ast::Pattern::Assign { - name, - location, - pattern, - } => todo!(), - ast::Pattern::Discard { name, location } => todo!(), - ast::Pattern::List { - location, - elements, - tail, - } => todo!(), - ast::Pattern::Constructor { - location, - name, - arguments, - module, - constructor, - with_spread, - tipo, - } => todo!(), - }, - TypedExpr::Try { - location, - tipo, - value, - then, - pattern, - } => todo!(), - TypedExpr::When { - location, - tipo, - subjects, - clauses, - } => todo!(), - //if statements increase scope due to branching. - TypedExpr::If { - branches, - final_else, - .. - } => { - let mut final_if_term = self.recurse_code_gen( - final_else, - scripts, - scope_level.scope_increment_sequence(1), - uplc_function_holder, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - if branches.len() == 1 { - let condition_term = self.recurse_code_gen( - &branches[0].condition, - scripts, - scope_level.scope_increment_sequence(1), - uplc_function_holder, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - let branch_term = self.recurse_code_gen( - &branches[0].body, - scripts, - scope_level.scope_increment_sequence(1), - uplc_function_holder, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - match (final_if_term.clone(), branch_term.clone()) { - (Term::Var(..), Term::Var(..)) => { - final_if_term = Term::Apply { - function: Rc::new(Term::Apply { - function: Rc::new(Term::Apply { - function: Rc::new(Term::Force(Rc::new(Term::Builtin( - DefaultFunction::IfThenElse, - )))), - argument: Rc::new(condition_term), - }), - //If this is just a var then don't include delay - argument: Rc::new(branch_term), - }), - //If this is just a var then don't include delay - argument: Rc::new(final_if_term.clone()), - }; - } - _ => { - final_if_term = Term::Force( - Term::Apply { - function: Rc::new(Term::Apply { - function: Rc::new(Term::Apply { - function: Rc::new(Term::Force(Rc::new(Term::Builtin( - DefaultFunction::IfThenElse, - )))), - argument: Rc::new(condition_term), - }), - argument: Rc::new(Term::Delay(Rc::new(branch_term))), - }), - argument: Rc::new(Term::Delay(Rc::new(final_if_term.clone()))), - } - .into(), - ); - } - } - } else { - //TODO: for multi branch if statements we can insert function definitions between branches - for branch in branches { - let condition_term = self.recurse_code_gen( - &branch.condition, - scripts, - scope_level.scope_increment_sequence(1), - uplc_function_holder, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - let branch_term = self.recurse_code_gen( - &branch.body, - scripts, - scope_level.scope_increment_sequence(1), - uplc_function_holder, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - final_if_term = Term::Force( - Term::Apply { - function: Rc::new(Term::Apply { - function: Rc::new(Term::Apply { - function: Rc::new(Term::Force(Rc::new(Term::Builtin( - DefaultFunction::IfThenElse, - )))), - argument: Rc::new(condition_term), - }), - argument: Rc::new(Term::Delay(Rc::new(branch_term))), - }), - argument: Rc::new(Term::Delay(Rc::new(final_if_term.clone()))), - } - .into(), - ); - } - } - self.maybe_insert_func_def( - final_if_term, - scripts, - scope_level, - uplc_function_holder, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ) - } - TypedExpr::RecordAccess { - location, - tipo, - label, - index, - record, - } => todo!(), - TypedExpr::ModuleSelect { - location, - tipo, - label, - module_name, - module_alias, - constructor, - } => match constructor { - ModuleValueConstructor::Record { - name, - arity, - tipo, - field_map, - location, - } => todo!(), - ModuleValueConstructor::Fn { - location, - module, - name, - } => Term::Var(Name { - text: format!("{module}_{name}"), - unique: 0.into(), - }), - ModuleValueConstructor::Constant { literal, location } => todo!(), - }, - TypedExpr::Todo { - location, - label, - tipo, - } => todo!(), - TypedExpr::RecordUpdate { - location, - tipo, - spread, - args, - } => todo!(), - TypedExpr::Negate { location, value } => todo!(), - } - } - - fn maybe_insert_func_def( - &self, - current_term: Term, - scripts: &[CheckedModule], - scope_level: ScopeLevels, - uplc_function_holder: &mut Vec<(String, Term)>, - uplc_function_holder_lookup: &mut HashMap<(String, String), (ScopeLevels, TypedExpr)>, - functions: &HashMap< - (String, String), - &Function, TypedExpr>, - >, - type_aliases: &HashMap< - (String, String), - &aiken_lang::ast::TypeAlias>, - >, - data_types: &HashMap< - (String, String), - &aiken_lang::ast::DataType>, - >, - imports: &HashMap<(String, String), &aiken_lang::ast::Use>, - constants: &HashMap< - (String, String), - &aiken_lang::ast::ModuleConstant, String>, - >, - ) -> Term { - let mut term = current_term; - for func in uplc_function_holder_lookup.clone().keys() { - if scope_level.is_less_than(&uplc_function_holder_lookup.clone().get(func).unwrap().0) { - let func_def = functions - .get(&(func.0.to_string(), func.1.to_string())) - .unwrap(); - - let mut function_body = self.recurse_code_gen( - &func_def.body, - scripts, - scope_level.scope_increment_sequence(func_def.arguments.len() as i32), - uplc_function_holder, - uplc_function_holder_lookup, - functions, - type_aliases, - data_types, - imports, - constants, - ); - - for arg in func_def.arguments.iter().rev() { - function_body = Term::Lambda { - parameter_name: Name { - text: arg.arg_name.get_variable_name().unwrap_or("_").to_string(), - unique: Unique::new(0), - }, - body: Rc::new(function_body), - } - } - - term = Term::Apply { - function: Term::Lambda { - parameter_name: Name { - text: format!("{}_{}", func.0, func.1), - unique: 0.into(), - }, - body: term.into(), - } - .into(), - argument: function_body.into(), - }; - uplc_function_holder_lookup.remove(func); - } - } - term - } - fn aiken_files(&mut self, dir: &Path, kind: ModuleKind) -> Result<(), Error> { let paths = walkdir::WalkDir::new(dir) .follow_links(true) diff --git a/crates/uplc/Cargo.toml b/crates/uplc/Cargo.toml index 71a72f17..dd98f35a 100644 --- a/crates/uplc/Cargo.toml +++ b/crates/uplc/Cargo.toml @@ -16,11 +16,11 @@ exclude = ["test_data/*"] cryptoxide = "0.4.2" flat-rs = { path = "../flat", version = "0.0.21" } hex = "0.4.3" -pallas-addresses = "0.14.0-alpha.3" -pallas-codec = "0.14.0-alpha.3" -pallas-crypto = "0.14.0-alpha.3" -pallas-primitives = "0.14.0-alpha.3" -pallas-traverse = "0.14.0-alpha.3" +pallas-addresses = "0.14.0" +pallas-codec = "0.14.0" +pallas-crypto = "0.14.0" +pallas-primitives = "0.14.0" +pallas-traverse = "0.14.0" peg = "0.8.0" pretty = "0.11.3" thiserror = "1.0.31" diff --git a/examples/sample/src/sample.ak b/examples/sample/src/sample.ak index 7cfd74b9..29aff2d3 100644 --- a/examples/sample/src/sample.ak +++ b/examples/sample/src/sample.ak @@ -3,7 +3,11 @@ pub type ScriptContext { } pub type Datum { - something: String, + Datum2{ + something: String, + fin: Int + } + None{} } pub fn eqInt(a: Int, b: Int) { diff --git a/examples/sample/src/scripts/swap.ak b/examples/sample/src/scripts/swap.ak index 0e02ac04..e791222d 100644 --- a/examples/sample/src/scripts/swap.ak +++ b/examples/sample/src/scripts/swap.ak @@ -10,17 +10,14 @@ pub type Redeemer { pub fn spend(datum: sample.Datum, rdmr: Redeemer, ctx: spend.ScriptContext) -> Bool { - let x = "x" - let a = "y" - let b = sample.eqString(x, a) - let c = 1 - let y = 1 - let d = sample.eqInt(y, c) - - if b { - c == 1 - } else{ - d + let x = 1 + let a = when datum is { + sample.Datum2(first, rest) -> rest + sample.None -> 0 } + let b = x + b == 1 + + }