From 16e3b01ca6787969ebce27ac1eb29efad82c88c1 Mon Sep 17 00:00:00 2001 From: German Nikolishin Date: Mon, 15 Apr 2024 20:29:01 +0100 Subject: [PATCH] entrypoint codegen + attemp at compilation --- crates/emitter/src/ast.rs | 11 ++ crates/emitter/src/expression.rs | 26 ++-- crates/emitter/src/function.rs | 7 +- crates/emitter/src/lib.rs | 11 +- crates/emitter/src/teal.rs | 206 ++++++++++++++++++++++++++++++- 5 files changed, 238 insertions(+), 23 deletions(-) diff --git a/crates/emitter/src/ast.rs b/crates/emitter/src/ast.rs index c94a006..87d4ef0 100644 --- a/crates/emitter/src/ast.rs +++ b/crates/emitter/src/ast.rs @@ -158,6 +158,8 @@ pub enum Instruction { #[display(fmt = "assert")] Assert, + #[display(fmt = "err")] + Error, #[display(fmt = "itob")] Itob, #[display(fmt = "dup")] @@ -169,6 +171,10 @@ pub enum Instruction { #[display(fmt = "txn")] Txn, + #[display(fmt = "txna")] + Txna, + #[display(fmt = "global")] + Global, #[display(fmt = "box_get")] BoxGet, @@ -181,6 +187,11 @@ pub enum Instruction { BranchNotZero, #[display(fmt = "bz")] BranchZero, + + #[display(fmt = "return")] + Return, + #[display(fmt = "log")] + Log, } pub trait TypeSizeHint { diff --git a/crates/emitter/src/expression.rs b/crates/emitter/src/expression.rs index 9279e1f..1ca60c0 100644 --- a/crates/emitter/src/expression.rs +++ b/crates/emitter/src/expression.rs @@ -58,11 +58,11 @@ pub fn emit_expression( args, ) } - Expression::Boolean(u) => bool(u, chunks, args), - Expression::Char(u) => char(u, chunks, args), - Expression::String(u) => string(u, chunks, args), - Expression::Hex(u) => hex(u, chunks, args), - Expression::Address(u) => address(u, chunks, args), + Expression::Boolean(u) => bool(u, chunks), + Expression::Char(u) => char(u, chunks), + Expression::String(u) => string(u, chunks), + Expression::Hex(u) => hex(u, chunks), + Expression::Address(u) => address(u, chunks), Expression::Enum(u) => enum_(u, chunks, args), Expression::Float(u) => float(u, chunks, args), @@ -92,7 +92,7 @@ pub fn emit_expression( } // todo: write a support teal function to checking inclusion and use it here. -fn in_(b: &BinaryExpression, chunks: &mut Vec, args: &mut EmitArgs) -> EmitResult { +fn in_(b: &BinaryExpression, _chunks: &mut [Chunk], args: &mut EmitArgs) -> EmitResult { args.diagnostics.push(Report::emit_error( b.loc.clone(), "Unsupported currently".to_string(), @@ -925,7 +925,7 @@ fn int(n: &BigInt, loc: &Span, chunks: &mut Vec, args: &mut EmitArgs) -> } /// Handle boolean values as `1` and `0` in Teal. -fn bool(u: &UnaryExpression, chunks: &mut Vec, args: &mut EmitArgs) -> EmitResult { +fn bool(u: &UnaryExpression, chunks: &mut Vec) -> EmitResult { let val: u64 = if u.element { 1 } else { 0 }; let c = Constant::Uint(val); let chunk = Chunk::new_single(Instruction::PushInt, c); @@ -935,7 +935,7 @@ fn bool(u: &UnaryExpression, chunks: &mut Vec, args: &mut EmitArgs) } /// Handle character as u64 value. -fn char(u: &UnaryExpression, chunks: &mut Vec, args: &mut EmitArgs) -> EmitResult { +fn char(u: &UnaryExpression, chunks: &mut Vec) -> EmitResult { let val: u64 = u.element.into(); let c = Constant::Uint(val); let chunk = Chunk::new_single(Instruction::PushInt, c); @@ -945,7 +945,7 @@ fn char(u: &UnaryExpression, chunks: &mut Vec, args: &mut EmitArgs) } /// Handle raw string literals. -fn string(u: &UnaryExpression, chunks: &mut Vec, args: &mut EmitArgs) -> EmitResult { +fn string(u: &UnaryExpression, chunks: &mut Vec) -> EmitResult { let c = Constant::String(u.element.clone()); let chunk = Chunk::new_single(Instruction::PushBytes, c); chunks.push(chunk); @@ -954,7 +954,7 @@ fn string(u: &UnaryExpression, chunks: &mut Vec, args: &mut EmitA } /// Handle hex value bytes. -fn hex(u: &UnaryExpression>, chunks: &mut Vec, args: &mut EmitArgs) -> EmitResult { +fn hex(u: &UnaryExpression>, chunks: &mut Vec) -> EmitResult { let c = Constant::Bytes(u.element.clone()); let chunk = Chunk::new_single(Instruction::PushBytes, c); chunks.push(chunk); @@ -963,11 +963,7 @@ fn hex(u: &UnaryExpression>, chunks: &mut Vec, args: &mut EmitArg } /// Handle Algorand address. -fn address( - u: &UnaryExpression
, - chunks: &mut Vec, - args: &mut EmitArgs, -) -> EmitResult { +fn address(u: &UnaryExpression
, chunks: &mut Vec) -> EmitResult { let c = Constant::StringLit(u.element.to_string()); let chunk = Chunk::new_single(Instruction::PushAddr, c); chunks.push(chunk); diff --git a/crates/emitter/src/function.rs b/crates/emitter/src/function.rs index 52eddf5..531d1c8 100644 --- a/crates/emitter/src/function.rs +++ b/crates/emitter/src/function.rs @@ -3,7 +3,6 @@ use folidity_semantics::{ ast::{ Function, FunctionVisibility, - StateParam, TypeVariant, }, SymbolInfo, @@ -51,7 +50,7 @@ pub fn emit_function(func: &Function, emitter: &mut TealEmitter) -> Result Result4096 bytes let name_chunk = Chunk::new_single(Instruction::PushBytes, Constant::String(box_name)); diff --git a/crates/emitter/src/lib.rs b/crates/emitter/src/lib.rs index 10cdd91..c0b599d 100644 --- a/crates/emitter/src/lib.rs +++ b/crates/emitter/src/lib.rs @@ -27,8 +27,15 @@ impl<'a> Runner for TealEmitter<'a> { where Self: std::marker::Sized, { - let _emitter = TealEmitter::new(source); - todo!() + let mut emitter = TealEmitter::new(source); + emitter.emit_entry_point(); + if !emitter.emit_functions() { + return Err(CompilationError::Emit(emitter.diagnostics)); + } + + let artifacts = emitter.compile(); + + Ok(artifacts) } } diff --git a/crates/emitter/src/teal.rs b/crates/emitter/src/teal.rs index 5edffdf..b7d8a66 100644 --- a/crates/emitter/src/teal.rs +++ b/crates/emitter/src/teal.rs @@ -3,6 +3,7 @@ use folidity_semantics::{ ast::{ Expression, Function, + TypeVariant, }, ContractDefinition, Span, @@ -11,10 +12,14 @@ use folidity_semantics::{ use indexmap::IndexMap; use crate::{ + add_padding, ast::{ Chunk, + Constant, FuncInfo, + Instruction, }, + function::emit_function, scratch_table::ScratchTable, }; @@ -33,9 +38,9 @@ pub struct EmitArgs<'a, 'b> { #[derive(Debug, Clone)] pub struct TealArtifacts { /// Teal approval program bytes. - approval_bytes: Vec, + pub approval_bytes: Vec, /// Teal clear program bytes. - clear_bytes: Vec, + pub clear_bytes: Vec, } #[derive(Debug)] @@ -73,6 +78,170 @@ impl<'a> TealEmitter<'a> { } } + pub fn emit_entry_point(&mut self) { + let mut chunks = vec![]; + let create_end_label = "create_end".to_string(); + + chunks.push(Chunk::new_single( + Instruction::Txn, + Constant::StringLit("ApplicationId".to_string()), + )); + chunks.push(Chunk::new_single(Instruction::PushInt, Constant::Uint(0))); + chunks.push(Chunk::new_empty(Instruction::Eq)); + chunks.push(Chunk::new_single( + Instruction::BranchZero, + Constant::StringLit(create_end_label.clone()), + )); + + let init_func_i = self + .definition + .functions + .iter() + .position(|f| f.is_init) + .expect("should be defined"); + + let init_func_name = format!( + "__block__{}", + self.definition.functions[init_func_i].name.name + ); + chunks.push(Chunk::new_single( + Instruction::Branch, + Constant::StringLit(init_func_name), + )); + chunks.push(Chunk::new_empty(Instruction::Label(create_end_label))); + + chunks.extend_from_slice(&[ + Chunk::new_single( + Instruction::Txn, + Constant::StringLit("OnCompletion".to_string()), + ), + Chunk::new_single(Instruction::PushInt, Constant::Uint(0)), // NoOp + Chunk::new_empty(Instruction::Eq), + Chunk::new_single( + Instruction::BranchNotZero, + Constant::StringLit("on_call".to_string()), + ), + Chunk::new_single( + Instruction::Txn, + Constant::StringLit("OnCompletion".to_string()), + ), + Chunk::new_single(Instruction::PushInt, Constant::Uint(5)), // Delete + Chunk::new_empty(Instruction::Eq), + Chunk::new_single( + Instruction::BranchNotZero, + Constant::StringLit("check_creator".to_string()), + ), + Chunk::new_single( + Instruction::Txn, + Constant::StringLit("OnCompletion".to_string()), + ), + Chunk::new_single(Instruction::PushInt, Constant::Uint(1)), // OptIn + Chunk::new_empty(Instruction::Eq), + Chunk::new_empty(Instruction::Error), // error out for now + Chunk::new_single( + Instruction::Txn, + Constant::StringLit("OnCompletion".to_string()), + ), + Chunk::new_single(Instruction::PushInt, Constant::Uint(2)), // CloseOut + Chunk::new_empty(Instruction::Eq), + Chunk::new_empty(Instruction::Error), // error out for now + Chunk::new_single( + Instruction::Txn, + Constant::StringLit("OnCompletion".to_string()), + ), + Chunk::new_single(Instruction::PushInt, Constant::Uint(4)), // UpdateApplication + Chunk::new_empty(Instruction::Eq), + Chunk::new_single( + Instruction::BranchNotZero, + Constant::StringLit("check_creator".to_string()), + ), + Chunk::new_empty(Instruction::Error), // error if None matches. + // + Chunk::new_empty(Instruction::Empty), + Chunk::new_empty(Instruction::Empty), + // + Chunk::new_empty(Instruction::Label("check_creator".to_string())), + Chunk::new_single(Instruction::Txn, Constant::StringLit("Sender".to_string())), + Chunk::new_single( + Instruction::Global, + Constant::StringLit("CreatorAddress".to_string()), + ), + Chunk::new_empty(Instruction::Eq), + Chunk::new_empty(Instruction::Assert), + Chunk::new_single(Instruction::PushInt, Constant::Uint(1)), + Chunk::new_empty(Instruction::Return), + // + Chunk::new_empty(Instruction::Empty), + Chunk::new_empty(Instruction::Empty), + ]); + + chunks.push(Chunk::new_empty(Instruction::Label("on_call".to_string()))); + + for name in self.definition.functions.iter().map(|f| &f.name.name) { + chunks.extend_from_slice(&[ + Chunk::new_multiple( + Instruction::Txna, + vec![ + Constant::StringLit("ApplicationArg".to_string()), + Constant::Uint(0), + ], + ), + Chunk::new_single(Instruction::PushBytes, Constant::String(name.clone())), + Chunk::new_empty(Instruction::Eq), + Chunk::new_single( + Instruction::BranchNotZero, + Constant::StringLit(format!("__{}", name)), + ), + ]); + } + chunks.push(Chunk::new_empty(Instruction::Error)); // error if none matches. + + self.emit_blocks(); + + add_padding(&mut chunks); + self.chunks.extend(chunks); + } + + pub fn emit_functions(&mut self) -> bool { + let mut error = false; + + for func in &self.definition.functions { + if let Ok(mut chunks) = emit_function(func, self) { + add_padding(&mut chunks); + } else { + error |= true; + } + } + + !error + } + + pub fn compile(&mut self) -> TealArtifacts { + let approval_string = self + .chunks + .iter() + .fold("#pragma version 8".to_string(), |init, c| { + format!("{}\n{}", init, c) + }); + let approval_bytes: Vec = approval_string.bytes().collect(); + + let clear_chunks = [ + Chunk::new_single(Instruction::PushInt, Constant::Uint(0)), + Chunk::new_empty(Instruction::Return), + ]; + let clear_string = clear_chunks + .iter() + .fold("#pragma version 8".to_string(), |init, c| { + format!("{}\n{}", init, c) + }); + let clear_bytes: Vec = clear_string.bytes().collect(); + + TealArtifacts { + approval_bytes, + clear_bytes, + } + } + pub fn scratch_index_incr(&mut self) -> Result { let i = self.scratch_index; self.scratch_index.checked_add(1).ok_or_else(|| { @@ -108,4 +277,37 @@ impl<'a> TealEmitter<'a> { Ok(i) } + + fn emit_blocks(&mut self) -> Vec { + let mut chunks = vec![]; + + for f in &self.definition.functions { + let mut block_chunks = vec![]; + let block_name = format!("__block__{}", f.name.name); + let func_name = format!("__{}", f.name.name); + + block_chunks.extend_from_slice(&[ + Chunk::new_empty(Instruction::Label(block_name)), + Chunk::new_single( + Instruction::CallSub, + crate::ast::Constant::StringLit(func_name), + ), + ]); + + if f.return_ty.ty() != &TypeVariant::Uint { + block_chunks.push(Chunk::new_empty(Instruction::Log)); + } + + block_chunks.extend_from_slice(&[ + Chunk::new_single(Instruction::PushInt, Constant::Uint(1)), + Chunk::new_empty(Instruction::Return), + ]); + + add_padding(&mut block_chunks); + + chunks.extend(block_chunks); + } + add_padding(&mut chunks); + chunks + } }