Skip to content

Commit

Permalink
entrypoint codegen + attemp at compilation
Browse files Browse the repository at this point in the history
  • Loading branch information
SkymanOne committed Apr 15, 2024
1 parent 5a75985 commit 16e3b01
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 23 deletions.
11 changes: 11 additions & 0 deletions crates/emitter/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ pub enum Instruction {

#[display(fmt = "assert")]
Assert,
#[display(fmt = "err")]
Error,
#[display(fmt = "itob")]
Itob,
#[display(fmt = "dup")]
Expand All @@ -169,6 +171,10 @@ pub enum Instruction {

#[display(fmt = "txn")]
Txn,
#[display(fmt = "txna")]
Txna,
#[display(fmt = "global")]
Global,

#[display(fmt = "box_get")]
BoxGet,
Expand All @@ -181,6 +187,11 @@ pub enum Instruction {
BranchNotZero,
#[display(fmt = "bz")]
BranchZero,

#[display(fmt = "return")]
Return,
#[display(fmt = "log")]
Log,
}

pub trait TypeSizeHint {
Expand Down
26 changes: 11 additions & 15 deletions crates/emitter/src/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),

Expand Down Expand Up @@ -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<Chunk>, 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(),
Expand Down Expand Up @@ -925,7 +925,7 @@ fn int(n: &BigInt, loc: &Span, chunks: &mut Vec<Chunk>, args: &mut EmitArgs) ->
}

/// Handle boolean values as `1` and `0` in Teal.
fn bool(u: &UnaryExpression<bool>, chunks: &mut Vec<Chunk>, args: &mut EmitArgs) -> EmitResult {
fn bool(u: &UnaryExpression<bool>, chunks: &mut Vec<Chunk>) -> EmitResult {
let val: u64 = if u.element { 1 } else { 0 };
let c = Constant::Uint(val);
let chunk = Chunk::new_single(Instruction::PushInt, c);
Expand All @@ -935,7 +935,7 @@ fn bool(u: &UnaryExpression<bool>, chunks: &mut Vec<Chunk>, args: &mut EmitArgs)
}

/// Handle character as u64 value.
fn char(u: &UnaryExpression<char>, chunks: &mut Vec<Chunk>, args: &mut EmitArgs) -> EmitResult {
fn char(u: &UnaryExpression<char>, chunks: &mut Vec<Chunk>) -> EmitResult {
let val: u64 = u.element.into();
let c = Constant::Uint(val);
let chunk = Chunk::new_single(Instruction::PushInt, c);
Expand All @@ -945,7 +945,7 @@ fn char(u: &UnaryExpression<char>, chunks: &mut Vec<Chunk>, args: &mut EmitArgs)
}

/// Handle raw string literals.
fn string(u: &UnaryExpression<String>, chunks: &mut Vec<Chunk>, args: &mut EmitArgs) -> EmitResult {
fn string(u: &UnaryExpression<String>, chunks: &mut Vec<Chunk>) -> EmitResult {
let c = Constant::String(u.element.clone());
let chunk = Chunk::new_single(Instruction::PushBytes, c);
chunks.push(chunk);
Expand All @@ -954,7 +954,7 @@ fn string(u: &UnaryExpression<String>, chunks: &mut Vec<Chunk>, args: &mut EmitA
}

/// Handle hex value bytes.
fn hex(u: &UnaryExpression<Vec<u8>>, chunks: &mut Vec<Chunk>, args: &mut EmitArgs) -> EmitResult {
fn hex(u: &UnaryExpression<Vec<u8>>, chunks: &mut Vec<Chunk>) -> EmitResult {
let c = Constant::Bytes(u.element.clone());
let chunk = Chunk::new_single(Instruction::PushBytes, c);
chunks.push(chunk);
Expand All @@ -963,11 +963,7 @@ fn hex(u: &UnaryExpression<Vec<u8>>, chunks: &mut Vec<Chunk>, args: &mut EmitArg
}

/// Handle Algorand address.
fn address(
u: &UnaryExpression<Address>,
chunks: &mut Vec<Chunk>,
args: &mut EmitArgs,
) -> EmitResult {
fn address(u: &UnaryExpression<Address>, chunks: &mut Vec<Chunk>) -> EmitResult {
let c = Constant::StringLit(u.element.to_string());
let chunk = Chunk::new_single(Instruction::PushAddr, c);
chunks.push(chunk);
Expand Down
7 changes: 3 additions & 4 deletions crates/emitter/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use folidity_semantics::{
ast::{
Function,
FunctionVisibility,
StateParam,
TypeVariant,
},
SymbolInfo,
Expand Down Expand Up @@ -51,7 +50,7 @@ pub fn emit_function(func: &Function, emitter: &mut TealEmitter) -> Result<Vec<C
// inject arguments as concrete vars.
// if the function is not a constructor, then the first app arg is a function signature.
let mut func_arg_index: u64 = if func.is_init { 0 } else { 1 };
for (name, p) in &func.params {
for (name, _) in &func.params {
let (p_no, _) = func.scope.find_var_index(name).expect("should exist");
let arg_chunk = Chunk::new_multiple(
Instruction::Txn,
Expand Down Expand Up @@ -126,10 +125,10 @@ pub fn emit_function(func: &Function, emitter: &mut TealEmitter) -> Result<Vec<C
Ok(chunks)
}

fn emit_state_var(ident: &String, sym: &SymbolInfo, func: &Function, args: &mut EmitArgs) {
fn emit_state_var(ident: &str, sym: &SymbolInfo, func: &Function, args: &mut EmitArgs) {
let state_decl = &args.emitter.definition.states[sym.i];
let box_name = format!("__{}", state_decl.name.name);
let (v_no, _) = func.scope.find_var_index(&ident).expect("should exist");
let (v_no, _) = func.scope.find_var_index(ident).expect("should exist");

// todo: support sizes of >4096 bytes
let name_chunk = Chunk::new_single(Instruction::PushBytes, Constant::String(box_name));
Expand Down
11 changes: 9 additions & 2 deletions crates/emitter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,15 @@ impl<'a> Runner<ContractDefinition, TealArtifacts> 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)
}
}

Expand Down
206 changes: 204 additions & 2 deletions crates/emitter/src/teal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use folidity_semantics::{
ast::{
Expression,
Function,
TypeVariant,
},
ContractDefinition,
Span,
Expand All @@ -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,
};

Expand All @@ -33,9 +38,9 @@ pub struct EmitArgs<'a, 'b> {
#[derive(Debug, Clone)]
pub struct TealArtifacts {
/// Teal approval program bytes.
approval_bytes: Vec<u8>,
pub approval_bytes: Vec<u8>,
/// Teal clear program bytes.
clear_bytes: Vec<u8>,
pub clear_bytes: Vec<u8>,
}

#[derive(Debug)]
Expand Down Expand Up @@ -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<u8> = 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<u8> = clear_string.bytes().collect();

TealArtifacts {
approval_bytes,
clear_bytes,
}
}

pub fn scratch_index_incr(&mut self) -> Result<u8, ()> {
let i = self.scratch_index;
self.scratch_index.checked_add(1).ok_or_else(|| {
Expand Down Expand Up @@ -108,4 +277,37 @@ impl<'a> TealEmitter<'a> {

Ok(i)
}

fn emit_blocks(&mut self) -> Vec<Chunk> {
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
}
}

0 comments on commit 16e3b01

Please sign in to comment.