diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs index b284d24f..9b7c0a42 100644 --- a/crates/lang/src/ast.rs +++ b/crates/lang/src/ast.rs @@ -53,9 +53,9 @@ impl UntypedModule { pub fn dependencies(&self) -> Vec<(String, Span)> { self.definitions() .flat_map(|def| { - if let Definition::Use { + if let Definition::Use(Use { location, module, .. - } = def + }) = def { Some((module.join("/"), *location)) } else { @@ -69,58 +69,73 @@ impl UntypedModule { pub type TypedDefinition = Definition, TypedExpr, String, String>; pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>; +#[derive(Debug, Clone, PartialEq)] +pub struct Function { + pub arguments: Vec>, + pub body: Expr, + pub doc: Option, + pub location: Span, + pub name: String, + pub public: bool, + pub return_annotation: Option, + pub return_type: T, + pub end_position: usize, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct TypeAlias { + pub alias: String, + pub annotation: Annotation, + pub doc: Option, + pub location: Span, + pub parameters: Vec, + pub public: bool, + pub tipo: T, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct DataType { + pub constructors: Vec>, + pub doc: Option, + pub location: Span, + pub name: String, + pub opaque: bool, + pub parameters: Vec, + pub public: bool, + pub typed_parameters: Vec, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Use { + pub as_name: Option, + pub location: Span, + pub module: Vec, + pub package: PackageName, + pub unqualified: Vec, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ModuleConstant { + pub doc: Option, + pub location: Span, + pub public: bool, + pub name: String, + pub annotation: Option, + pub value: Box>, + pub tipo: T, +} + #[derive(Debug, Clone, PartialEq)] pub enum Definition { - Fn { - arguments: Vec>, - body: Expr, - doc: Option, - location: Span, - name: String, - public: bool, - return_annotation: Option, - return_type: T, - end_position: usize, - }, + Fn(Function), - TypeAlias { - alias: String, - annotation: Annotation, - doc: Option, - location: Span, - parameters: Vec, - public: bool, - tipo: T, - }, + TypeAlias(TypeAlias), - DataType { - constructors: Vec>, - doc: Option, - location: Span, - name: String, - opaque: bool, - parameters: Vec, - public: bool, - typed_parameters: Vec, - }, + DataType(DataType), - Use { - as_name: Option, - location: Span, - module: Vec, - package: PackageName, - unqualified: Vec, - }, + Use(Use), - ModuleConstant { - doc: Option, - location: Span, - public: bool, - name: String, - annotation: Option, - value: Box>, - tipo: T, - }, + ModuleConstant(ModuleConstant), } impl Definition { diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index c456ec6c..7890b83e 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -119,12 +119,14 @@ pub fn import_parser() -> impl Parser impl Parser impl Parser impl Parser impl Parser impl Parser impl Parser Environment<'a> { module_name: &String, ) -> TypedDefinition { match s { - Definition::Fn { + Definition::Fn(Function { doc, location, name, @@ -196,7 +197,7 @@ impl<'a> Environment<'a> { return_annotation, return_type, end_position, - } => { + }) => { // Lookup the inferred function information let function = self .get_variable(&name) @@ -230,7 +231,7 @@ impl<'a> Environment<'a> { }, ); - Definition::Fn { + Definition::Fn(Function { doc, location, name, @@ -240,7 +241,7 @@ impl<'a> Environment<'a> { return_type, body, end_position, - } + }) } definition @ (Definition::TypeAlias { .. } @@ -655,13 +656,13 @@ impl<'a> Environment<'a> { pub fn register_import(&mut self, def: &UntypedDefinition) -> Result<(), Error> { match def { - Definition::Use { + Definition::Use(Use { module, as_name, unqualified, location, .. - } => { + }) => { let name = module.join("/"); // Find imported module @@ -819,14 +820,14 @@ impl<'a> Environment<'a> { names: &mut HashMap<&'a str, &'a Span>, ) -> Result<(), Error> { match def { - Definition::DataType { + Definition::DataType(DataType { name, public, parameters, location, constructors, .. - } => { + }) => { assert_unique_type_name(names, name, location)?; // Build a type from the type Annotation @@ -864,14 +865,14 @@ impl<'a> Environment<'a> { } } - Definition::TypeAlias { + Definition::TypeAlias(TypeAlias { location, public, parameters: args, alias: name, annotation: resolved_type, .. - } => { + }) => { assert_unique_type_name(names, name, location)?; // Register the paramerterised types @@ -915,14 +916,14 @@ impl<'a> Environment<'a> { names: &mut HashMap<&'a str, &'a Span>, ) -> Result<(), Error> { match def { - Definition::Fn { + Definition::Fn(Function { name, arguments: args, location, return_annotation, public, .. - } => { + }) => { assert_unique_value_name(names, name, location)?; self.ungeneralised_functions.insert(name.to_string()); @@ -980,14 +981,14 @@ impl<'a> Environment<'a> { } } - Definition::DataType { + Definition::DataType(DataType { location, public, opaque, name, constructors, .. - } => { + }) => { let mut hydrator = hydrators .remove(name) .expect("Could not find hydrator for register_values custom type"); @@ -1079,7 +1080,7 @@ impl<'a> Environment<'a> { } } - Definition::ModuleConstant { name, location, .. } => { + Definition::ModuleConstant(ModuleConstant { name, location, .. }) => { assert_unique_const_name(names, name, location)?; } diff --git a/crates/lang/src/tipo/infer.rs b/crates/lang/src/tipo/infer.rs index a1cd071d..4956e840 100644 --- a/crates/lang/src/tipo/infer.rs +++ b/crates/lang/src/tipo/infer.rs @@ -2,8 +2,9 @@ use std::collections::HashMap; use crate::{ ast::{ - Definition, Layer, ModuleKind, RecordConstructor, RecordConstructorArg, TypedDefinition, - TypedModule, UntypedDefinition, UntypedModule, + DataType, Definition, Function, Layer, ModuleConstant, ModuleKind, RecordConstructor, + RecordConstructorArg, TypeAlias, TypedDefinition, TypedModule, UntypedDefinition, + UntypedModule, Use, }, builtins::function, parser::token::Token, @@ -142,7 +143,7 @@ fn infer_definition( environment: &mut Environment<'_>, ) -> Result { match def { - Definition::Fn { + Definition::Fn(Function { doc, location, name, @@ -152,7 +153,7 @@ fn infer_definition( return_annotation, end_position, .. - } => { + }) => { let preregistered_fn = environment .get_variable(&name) .expect("Could not find preregistered type for function"); @@ -217,7 +218,7 @@ fn infer_definition( tipo }; - Ok(Definition::Fn { + Ok(Definition::Fn(Function { doc, location, name, @@ -229,10 +230,10 @@ fn infer_definition( .expect("Could not find return type for fn"), body, end_position, - }) + })) } - Definition::TypeAlias { + Definition::TypeAlias(TypeAlias { doc, location, public, @@ -240,14 +241,14 @@ fn infer_definition( parameters, annotation, .. - } => { + }) => { let tipo = environment .get_type_constructor(&None, &alias, location) .expect("Could not find existing type for type alias") .tipo .clone(); - Ok(Definition::TypeAlias { + Ok(Definition::TypeAlias(TypeAlias { doc, location, public, @@ -255,10 +256,10 @@ fn infer_definition( parameters, annotation, tipo, - }) + })) } - Definition::DataType { + Definition::DataType(DataType { doc, location, public, @@ -267,7 +268,7 @@ fn infer_definition( parameters, constructors, .. - } => { + }) => { let constructors = constructors .into_iter() .map( @@ -330,7 +331,7 @@ fn infer_definition( .parameters .clone(); - Ok(Definition::DataType { + Ok(Definition::DataType(DataType { doc, location, public, @@ -339,16 +340,16 @@ fn infer_definition( parameters, constructors, typed_parameters, - }) + })) } - Definition::Use { + Definition::Use(Use { location, module, as_name, mut unqualified, .. - } => { + }) => { let name = module.join("/"); // Find imported module @@ -371,16 +372,16 @@ fn infer_definition( } } - Ok(Definition::Use { + Ok(Definition::Use(Use { location, module, as_name, unqualified, package: module_info.package.clone(), - }) + })) } - Definition::ModuleConstant { + Definition::ModuleConstant(ModuleConstant { doc, location, name, @@ -388,7 +389,7 @@ fn infer_definition( public, value, .. - } => { + }) => { let typed_expr = ExprTyper::new(environment).infer_const(&annotation, *value)?; let tipo = typed_expr.tipo(); @@ -411,7 +412,7 @@ fn infer_definition( environment.init_usage(name.clone(), EntityKind::PrivateConstant, location); } - Ok(Definition::ModuleConstant { + Ok(Definition::ModuleConstant(ModuleConstant { doc, location, name, @@ -419,7 +420,7 @@ fn infer_definition( public, value: Box::new(typed_expr), tipo, - }) + })) } } } diff --git a/crates/project/src/lib.rs b/crates/project/src/lib.rs index fc0e0bbc..c679601e 100644 --- a/crates/project/src/lib.rs +++ b/crates/project/src/lib.rs @@ -2,6 +2,7 @@ use std::{ collections::HashMap, fs, path::{Path, PathBuf}, + rc::Rc, }; pub mod config; @@ -10,16 +11,22 @@ pub mod format; pub mod module; use aiken_lang::{ - ast::{Definition, ModuleKind}, + ast::{Definition, Function, ModuleKind}, builtins, + expr::TypedExpr, tipo::TypeInfo, IdGenerator, }; +use uplc::{ + ast::{Constant, Name, NamedDeBruijn, Program, Term, Unique}, + builtins::DefaultFunction, + parser::interner::Interner, +}; use crate::{ config::Config, error::{Error, Warning}, - module::{CheckedModule, CheckedModules, ParsedModule, ParsedModules}, + module::{self, CheckedModule, CheckedModules, ParsedModule, ParsedModules}, }; #[derive(Debug)] @@ -74,7 +81,7 @@ impl Project { self.compile(false) } - pub fn compile(&mut self, _uplc_gen: bool) -> Result<(), Error> { + pub fn compile(&mut self, uplc_gen: bool) -> Result<(), Error> { self.read_source_files()?; let parsed_modules = self.parse_sources()?; @@ -83,7 +90,11 @@ impl Project { let mut checked_modules = self.type_check(parsed_modules, processing_sequence)?; - let _scripts = self.validate_scripts(&mut checked_modules)?; + let scripts = self.validate_scripts(&mut checked_modules)?; + + if uplc_gen { + self.code_gen(&scripts, &checked_modules)?; + } Ok(()) } @@ -240,13 +251,13 @@ impl Project { scripts.push(module.clone()); for def in module.ast.definitions() { - if let Definition::Fn { + if let Definition::Fn(Function { arguments, location, name, return_type, .. - } = def + }) = def { if VALIDATOR_NAMES.contains(&name.as_str()) { // validators must return a Bool @@ -295,6 +306,286 @@ impl Project { } } + fn code_gen( + &mut self, + scripts: &[CheckedModule], + 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(); + let mut imports = HashMap::new(); + let mut constants = HashMap::new(); + + for module in checked_modules.values() { + for def in module.ast.definitions() { + match def { + Definition::Fn(func) => { + functions.insert((module.name.clone(), func.name.clone()), func); + } + Definition::TypeAlias(ta) => { + type_aliases.insert((module.name.clone(), ta.alias.clone()), ta); + } + Definition::DataType(dt) => { + data_types.insert((module.name.clone(), dt.name.clone()), dt); + } + Definition::Use(import) => { + imports.insert((module.name.clone(), import.module.join("/")), import); + } + Definition::ModuleConstant(mc) => { + constants.insert((module.name.clone(), mc.name.clone()), mc); + } + } + } + } + + for script in scripts { + for def in script.ast.definitions() { + if let Definition::Fn(Function { + arguments, + name, + body, + .. + }) = def + { + if VALIDATOR_NAMES.contains(&name.as_str()) { + let type_info = self.module_types.get(&script.name).unwrap(); + println!("{type_info:#?}"); + + let mut term = self.recurse_code_gen( + body, + scripts, + 0, + &uplc_function_holder, + &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); + + programs.push(program.try_into().unwrap()); + } + } + } + } + + Ok(programs) + } + + fn recurse_code_gen( + &self, + body: &aiken_lang::expr::TypedExpr, + scripts: &[CheckedModule], + scope_level: i32, + uplc_function_holder: &Vec>, + functions: &HashMap< + (String, String), + &Function, aiken_lang::expr::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 terms = match body { + aiken_lang::expr::TypedExpr::Int { value, .. } => { + Term::Constant(Constant::Integer(value.parse::().unwrap())) + } + aiken_lang::expr::TypedExpr::String { value, .. } => { + Term::Constant(Constant::String(value.clone())) + } + aiken_lang::expr::TypedExpr::ByteArray { bytes, .. } => { + Term::Constant(Constant::ByteString(bytes.clone())) + } + aiken_lang::expr::TypedExpr::Sequence { + location, + expressions, + } => todo!(), + aiken_lang::expr::TypedExpr::Pipeline { + location, + expressions, + } => todo!(), + aiken_lang::expr::TypedExpr::Var { + location, + constructor, + name, + } => todo!(), + aiken_lang::expr::TypedExpr::Fn { + location, + tipo, + is_capture, + args, + body, + return_annotation, + } => todo!(), + aiken_lang::expr::TypedExpr::List { + location, + tipo, + elements, + tail, + } => todo!(), + aiken_lang::expr::TypedExpr::Call { + location, + tipo, + fun, + args, + } => todo!(), + aiken_lang::expr::TypedExpr::BinOp { + location, + tipo, + name, + left, + right, + } => todo!(), + aiken_lang::expr::TypedExpr::Assignment { + location, + tipo, + value, + pattern, + kind, + } => todo!(), + aiken_lang::expr::TypedExpr::Try { + location, + tipo, + value, + then, + pattern, + } => todo!(), + aiken_lang::expr::TypedExpr::When { + location, + tipo, + subjects, + clauses, + } => todo!(), + //if statements increase scope due to branching. + aiken_lang::expr::TypedExpr::If { + branches, + final_else, + .. + } => { + let mut final_if_term = self.recurse_code_gen( + final_else, + scripts, + scope_level + 1, + uplc_function_holder, + 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 + let condition_term = self.recurse_code_gen( + &branch.condition, + scripts, + scope_level + 1, // Since this happens before branching. Maybe not increase scope level + uplc_function_holder, + functions, + type_aliases, + data_types, + imports, + constants, + ); + + let branch_term = self.recurse_code_gen( + &branch.body, + scripts, + scope_level + 1, + uplc_function_holder, + functions, + type_aliases, + data_types, + imports, + constants, + ); + + 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(Term::Delay(Rc::new(branch_term))), + }), + //If this is just a var then don't include delay + argument: Rc::new(Term::Delay(Rc::new(final_if_term.clone()))), + }; + } + Term::Force(Rc::new(final_if_term)) + } + aiken_lang::expr::TypedExpr::RecordAccess { + location, + tipo, + label, + index, + record, + } => todo!(), + aiken_lang::expr::TypedExpr::ModuleSelect { + location, + tipo, + label, + module_name, + module_alias, + constructor, + } => todo!(), + aiken_lang::expr::TypedExpr::Todo { + location, + label, + tipo, + } => todo!(), + aiken_lang::expr::TypedExpr::RecordUpdate { + location, + tipo, + spread, + args, + } => todo!(), + aiken_lang::expr::TypedExpr::Negate { location, value } => todo!(), + }; + + terms + } + 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/src/parser.rs b/crates/uplc/src/parser.rs index 13ad9f0c..a1d55326 100644 --- a/crates/uplc/src/parser.rs +++ b/crates/uplc/src/parser.rs @@ -9,7 +9,7 @@ use interner::Interner; use pallas_primitives::{alonzo::PlutusData, Fragment}; use peg::{error::ParseError, str::LineCol}; -mod interner; +pub mod interner; /// Parse a `Program` from a str. pub fn program(src: &str) -> Result, ParseError> { diff --git a/examples/sample/src/sample.ak b/examples/sample/src/sample.ak index 58230d45..99915c0b 100644 --- a/examples/sample/src/sample.ak +++ b/examples/sample/src/sample.ak @@ -1,3 +1,11 @@ pub type ScriptContext { - thing: String, + thing: String +} + +pub type Datum { + something: String, +} + +pub fn thing(a: Datum, b){ + a.something == b } diff --git a/examples/sample/src/scripts/swap.ak b/examples/sample/src/scripts/swap.ak index d0f1c4e0..7e091fe6 100644 --- a/examples/sample/src/scripts/swap.ak +++ b/examples/sample/src/scripts/swap.ak @@ -1,10 +1,7 @@ +use sample use sample/mint use sample/spend -pub type Datum { - something: String, -} - pub type Redeemer { Buy { id: Int } Sell(Int)