Add revm_runner as module.
This commit is contained in:
@@ -1 +1,2 @@
|
||||
pub mod ast;
|
||||
pub mod push3_describtor;
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
//! src/compiler/push3_descriptor.rs
|
||||
//!
|
||||
//! A small module of helper functions to construct or interpret the 256-bit descriptors
|
||||
//! used by our on-chain `Push3Interpreter`. Each descriptor encodes a tag, an offset,
|
||||
//! a length, and any leftover bits (e.g., immediate data).
|
||||
|
||||
use ethers::types::U256;
|
||||
|
||||
/// The 256-bit descriptor layout is as follows:
|
||||
/// [ 8 bits: tag | 32 bits: offset | 32 bits: length | 184 bits: leftover ]
|
||||
///
|
||||
/// We'll define small helper functions to build or parse these.
|
||||
/// Our `make_sublist_descriptor` is an example for tag=3 (SUBLIST).
|
||||
|
||||
/// Tag constants, if you want them:
|
||||
pub const TAG_SUBLIST: u8 = 3;
|
||||
pub const TAG_INT_LITERAL: u8 = 2;
|
||||
pub const TAG_INSTRUCTION: u8 = 1;
|
||||
pub const TAG_NONE: u8 = 0;
|
||||
|
||||
/// A small helper to shift the tag into the top 8 bits.
|
||||
#[inline]
|
||||
fn tag_bits(tag: u8) -> U256 {
|
||||
U256::from(tag) << 248
|
||||
}
|
||||
|
||||
/// Build a descriptor for a SUBLIST with the given offset and length (in bytes),
|
||||
/// leftover=0.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// let desc = make_sublist_descriptor(0, code_bytes.len() as u32);
|
||||
/// // pass `desc` into the interpreter's exec stack
|
||||
/// ```
|
||||
pub fn make_sublist_descriptor(offset: u32, length: u32) -> U256 {
|
||||
tag_bits(TAG_SUBLIST as u8)
|
||||
| (U256::from(offset) << 216)
|
||||
| (U256::from(length) << 184)
|
||||
| U256::zero() // leftover
|
||||
}
|
||||
|
||||
/// Possibly you want a more general helper that builds any descriptor:
|
||||
/// If you have other uses for leftover bits, you can pass that in:
|
||||
pub fn make_descriptor(tag: u8, offset: u32, length: u32, leftover: U256) -> U256 {
|
||||
tag_bits(tag)
|
||||
| (U256::from(offset) << 216)
|
||||
| (U256::from(length) << 184)
|
||||
| leftover
|
||||
}
|
||||
|
||||
/// If you want to parse a descriptor, you can define getTag, getOffset, getLength, etc:
|
||||
pub fn get_tag(desc: U256) -> u8 {
|
||||
// top 8 bits => shift right 248
|
||||
(desc >> 248).as_u32() as u8
|
||||
}
|
||||
|
||||
pub fn get_offset(desc: U256) -> u32 {
|
||||
((desc >> 216) & U256::from(u32::MAX)).as_u32()
|
||||
}
|
||||
|
||||
pub fn get_length(desc: U256) -> u32 {
|
||||
((desc >> 184) & U256::from(u32::MAX)).as_u32()
|
||||
}
|
||||
|
||||
pub fn get_low_184(desc: U256) -> U256 {
|
||||
desc & ((U256::from(1u64) << 184) - 1)
|
||||
}
|
||||
+53
-129
@@ -1,8 +1,11 @@
|
||||
// src/main.rs
|
||||
|
||||
mod compiler;
|
||||
mod runner;
|
||||
|
||||
use std::env;
|
||||
|
||||
// We’ll use your existing AST definitions for parsing
|
||||
use compiler::ast::{
|
||||
parse_string_to_sexpr,
|
||||
sexpr_to_untyped,
|
||||
@@ -10,22 +13,28 @@ use compiler::ast::{
|
||||
UntypedAst, // The untyped AST enum
|
||||
};
|
||||
|
||||
use runner::revm_runner::{EvmRunner, Push3InterpreterOutputs}; // <--- We import our EvmRunner
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use hex;
|
||||
use database::CacheDB;
|
||||
use revm::{
|
||||
context::Context,
|
||||
context_interface::result::{ExecutionResult, Output},
|
||||
database_interface::EmptyDB,
|
||||
handler::EthHandler,
|
||||
primitives::{Bytes, TxKind},
|
||||
EvmCommit, MainEvm,
|
||||
};
|
||||
use ethers::abi::{encode, Token};
|
||||
use ethers::abi::{ParamType, decode};
|
||||
use ethers::types::U256;
|
||||
use serde::Deserialize; // <--- NEW: for deserializing from JSON
|
||||
use serde::Deserialize; // For JSON artifact loading
|
||||
|
||||
// If you still need these (not strictly required anymore):
|
||||
// use database::CacheDB;
|
||||
// use revm::{
|
||||
// context::Context,
|
||||
// context_interface::result::{ExecutionResult, Output},
|
||||
// database_interface::EmptyDB,
|
||||
// handler::EthHandler,
|
||||
// primitives::{Bytes, TxKind},
|
||||
// EvmCommit, MainEvm,
|
||||
// };
|
||||
use ethers::abi::{decode, ParamType};
|
||||
use ethers::types::U256;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// JSON artifact struct, as before
|
||||
// ----------------------------------------------------------------------
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct BytecodeObject {
|
||||
object: String,
|
||||
@@ -36,9 +45,12 @@ struct MyContractArtifact {
|
||||
bytecode: BytecodeObject,
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// MAIN
|
||||
// ----------------------------------------------------------------------
|
||||
fn main() -> Result<()> {
|
||||
// 1) Read CLI arguments
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
// 1) Read CLI arguments for the Push3 program
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() < 2 {
|
||||
eprintln!("Usage: cargo run -- '<program>'");
|
||||
eprintln!("Example: cargo run -- '((3 5 +) DUP MUL)'");
|
||||
@@ -54,7 +66,7 @@ fn main() -> Result<()> {
|
||||
let ast: UntypedAst = sexpr_to_untyped(&sexpr)
|
||||
.map_err(|e| anyhow!("Error converting to UntypedAst: {}", e))?;
|
||||
|
||||
// 4) Convert UntypedAst => Push3 bytecode
|
||||
// 4) Convert UntypedAst => Push3 bytecode (for debugging, so we can see it)
|
||||
let bytecode = ast.to_bytecode();
|
||||
|
||||
// 5) Print the resulting AST and bytecode (in hex)
|
||||
@@ -62,137 +74,49 @@ fn main() -> Result<()> {
|
||||
println!("Bytecode (hex): 0x{}", hex::encode(&bytecode));
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// NEW: Read the contract creation code (bytecode) from a JSON artifact
|
||||
// Load the creation code (bytecode) from a JSON artifact
|
||||
// ----------------------------------------------------------------------
|
||||
// Example JSON (Forge artifact) might look like:
|
||||
// {
|
||||
// "bytecode": "60808060...0033" <--- entire creation code
|
||||
// ... other fields ...
|
||||
// }
|
||||
|
||||
// Path to your JSON artifact. Adjust as needed:
|
||||
let creation_hex_filename = "../onchain/out/Push3Interpreter.sol/Push3Interpreter.json";
|
||||
|
||||
// Read and parse the JSON artifact
|
||||
let creation_json = std::fs::read_to_string(creation_hex_filename)
|
||||
.map_err(|e| anyhow!("Failed to read JSON file {}: {}", creation_hex_filename, e))?;
|
||||
let contract_artifact: MyContractArtifact = serde_json::from_str(&creation_json)
|
||||
.map_err(|e| anyhow!("Failed to parse JSON artifact: {}", e))?;
|
||||
|
||||
// Extract the "bytecode" field from the JSON
|
||||
|
||||
let raw_hex = &contract_artifact.bytecode.object;
|
||||
let stripped_hex = raw_hex.trim_start_matches("0x");
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// The rest of the deployment+call logic remains mostly unchanged
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// 2) Convert hex string to raw bytes.
|
||||
let creation_bytes = match hex::decode(stripped_hex) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(e) => bail!("Invalid hex for creation code: {e}"),
|
||||
};
|
||||
|
||||
// 3) Build a new EVM instance with a "Create" transaction:
|
||||
let mut evm = MainEvm::new(
|
||||
// a) Prepare context with a chained transaction
|
||||
Context::builder()
|
||||
.modify_tx_chained(|tx| {
|
||||
tx.transact_to = TxKind::Create;
|
||||
tx.data = Bytes::from(creation_bytes.clone());
|
||||
})
|
||||
// b) Provide an in-memory DB for ephemeral state
|
||||
.with_db(CacheDB::<EmptyDB>::default()),
|
||||
// c) The "EthHandler" for block/tx environment logic
|
||||
EthHandler::default(),
|
||||
println!(
|
||||
"Loaded creation code ({} bytes) from {}",
|
||||
creation_bytes.len(),
|
||||
creation_hex_filename
|
||||
);
|
||||
|
||||
println!("Deploying contract with creation code size: {} bytes", creation_bytes.len());
|
||||
// ----------------------------------------------------------------------
|
||||
// 6) Deploy the interpreter in an ephemeral EVM using `EvmRunner`
|
||||
// ----------------------------------------------------------------------
|
||||
let mut runner = EvmRunner::new(creation_bytes)?;
|
||||
println!("Interpreter deployed successfully in ephemeral EVM at: 0x{:x}", runner.interpreter_addr);
|
||||
|
||||
// 4) Execute the CREATE transaction
|
||||
let creation_result = evm.exec_commit()?;
|
||||
// => an ExecutionResult
|
||||
// ----------------------------------------------------------------------
|
||||
// 7) Evaluate the AST in our EVM
|
||||
// ----------------------------------------------------------------------
|
||||
// We'll call `runner.run_ast(&ast)`, which:
|
||||
// - Converts AST => push3 code,
|
||||
// - Builds a sublist descriptor in the exec stack,
|
||||
// - Calls `runInterpreter`,
|
||||
// - Returns final stacks in a `Push3InterpreterOutputs`.
|
||||
let outputs = runner.run_ast(&ast)?;
|
||||
println!("Ran the AST successfully!");
|
||||
|
||||
// 5) Check success
|
||||
let ExecutionResult::Success {
|
||||
output: Output::Create(_, Some(deployed_addr)),
|
||||
..
|
||||
} = creation_result
|
||||
else {
|
||||
bail!("Deployment failed or didn't return an address: {creation_result:#?}");
|
||||
};
|
||||
// 8) Print the final stacks
|
||||
println!("Final CODE stack: {:?}", outputs.final_code_stack);
|
||||
println!("Final EXEC stack: {:?}", outputs.final_exec_stack);
|
||||
println!("Final INT stack: {:?}", outputs.final_int_stack);
|
||||
|
||||
println!("Deployed contract at: 0x{:x}", deployed_addr);
|
||||
|
||||
// 1) Prepare the function selector for runInterpreter
|
||||
let func_selector = ðers::utils::id("runInterpreter(bytes,uint256[],uint256[],int256[])")[0..4];
|
||||
|
||||
// 2) Convert your compiled “Push3” bytecode into the `bytes` argument
|
||||
let code_arg = Token::Bytes(bytecode.clone());
|
||||
|
||||
// 3) Build a “SUBLIST descriptor” for the entire bytecode.
|
||||
let tag_bits = U256::from(3u64) << 248; // CodeTag.SUBLIST = 3
|
||||
let offset_bits = U256::from(0u64) << 216; // offset=0
|
||||
let length_bits = U256::from(bytecode.len() as u64) << 184; // length = bytecode.len()
|
||||
let low_184 = U256::zero();
|
||||
|
||||
let sublist_descriptor = tag_bits | offset_bits | length_bits | low_184;
|
||||
|
||||
// Now we put that descriptor as the one and only item in `initExecStack`.
|
||||
let init_exec_stack = Token::Array(vec![
|
||||
Token::Uint(sublist_descriptor)
|
||||
]);
|
||||
|
||||
// For the other two arrays (codeStack, intStack), we’ll just leave them empty:
|
||||
let empty_uint256_array = Token::Array(vec![]); // codeStack
|
||||
let empty_int256_array = Token::Array(vec![]); // intStack
|
||||
|
||||
// 4) Encode all arguments in the correct order:
|
||||
let encoded_args = encode(&[
|
||||
code_arg,
|
||||
empty_uint256_array.clone(), // initCodeStack
|
||||
init_exec_stack, // initExecStack
|
||||
empty_uint256_array.clone(), // initIntStack
|
||||
]);
|
||||
|
||||
// 5) Concatenate selector + encoded arguments to form the full call data
|
||||
let mut call_data = Vec::from(func_selector);
|
||||
call_data.extend_from_slice(&encoded_args);
|
||||
|
||||
// 6) Modify the transaction to call `runInterpreter` with the encoded data
|
||||
evm.context.modify_tx(|tx| {
|
||||
tx.transact_to = TxKind::Call(deployed_addr);
|
||||
tx.data = Bytes::from(call_data);
|
||||
tx.nonce += 1; // increment nonce to avoid reuse
|
||||
});
|
||||
|
||||
// 7) Execute the call.
|
||||
let call_result = evm.transact()?;
|
||||
match &call_result.result {
|
||||
ExecutionResult::Success {
|
||||
output: Output::Call(return_data),
|
||||
..
|
||||
} => {
|
||||
println!("Call succeeded, returned data: 0x{}", hex::encode(return_data));
|
||||
|
||||
// Decode (uint256[], uint256[], int256[])
|
||||
let param_types = &[
|
||||
ParamType::Array(Box::new(ParamType::Uint(256))), // finalCodeStack
|
||||
ParamType::Array(Box::new(ParamType::Uint(256))), // finalExecStack
|
||||
ParamType::Array(Box::new(ParamType::Int(256))), // finalIntStack
|
||||
];
|
||||
|
||||
let decoded = decode(param_types, return_data)
|
||||
.map_err(|e| anyhow!("Failed to decode return data: {e}"))?;
|
||||
|
||||
println!("Decoded return: {:?}", decoded);
|
||||
}
|
||||
ExecutionResult::Revert { gas_used, output } => {
|
||||
bail!("Call reverted: gas used = {gas_used:?}, output = {:?}", output);
|
||||
}
|
||||
other => {
|
||||
bail!("Call failed: {other:?}");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
//! src/evm_integration/revm_runner.rs
|
||||
//!
|
||||
//! This module provides a thin wrapper around REVM to deploy a Push3Interpreter
|
||||
//! contract, then call its `runInterpreter(...)` function with flexible inputs/outputs,
|
||||
//! using the same style (Context::builder(), .modify_tx_chained, etc.) you had in your main.
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use ethers::abi::{encode, decode, Token, ParamType};
|
||||
use ethers::types::U256;
|
||||
use ethers::utils;
|
||||
use database::CacheDB;
|
||||
use revm::{
|
||||
context::Context,
|
||||
context::{BlockEnv, TxEnv, CfgEnv},
|
||||
context_interface::result::{ExecutionResult, Output},
|
||||
database_interface::EmptyDB,
|
||||
handler::EthHandler,
|
||||
primitives::{Bytes, TxKind}, // 4 generics
|
||||
EvmCommit,
|
||||
MainEvm,
|
||||
};
|
||||
|
||||
// We import your AST definitions so we can call `ast.to_bytecode()`.
|
||||
use crate::compiler::ast::{UntypedAst, Push3Ast};
|
||||
|
||||
// If you have a descriptor helper (like make_sublist_descriptor), bring it in:
|
||||
use crate::compiler::push3_describtor::make_sublist_descriptor;
|
||||
|
||||
/// The input parameters for `runInterpreter(...)`: four fields (code, codeStack, execStack, intStack).
|
||||
pub struct Push3InterpreterInputs {
|
||||
pub code: Vec<u8>,
|
||||
pub init_code_stack: Vec<U256>,
|
||||
pub init_exec_stack: Vec<U256>,
|
||||
pub init_int_stack: Vec<i128>,
|
||||
}
|
||||
|
||||
/// The outputs from `runInterpreter(...)`: three arrays for code/exec/int stacks.
|
||||
pub struct Push3InterpreterOutputs {
|
||||
pub final_code_stack: Vec<U256>,
|
||||
pub final_exec_stack: Vec<U256>,
|
||||
pub final_int_stack: Vec<i128>,
|
||||
}
|
||||
|
||||
/// A thin wrapper around REVM, parameterized by the 4 generics (DB, BLOCK, TX, CFG).
|
||||
/// - We store the ephemeral EVM instance,
|
||||
/// - We store the deployed address of your `Push3Interpreter`,
|
||||
/// - We provide `run_interpreter` and `run_ast`.
|
||||
pub struct EvmRunner {
|
||||
/// The 4-parameter MainEvm:
|
||||
/// DB = CacheDB<EmptyDB>,
|
||||
/// BLOCK = BlockEnv,
|
||||
/// TX = TxEnv,
|
||||
/// CFG = CfgEnv
|
||||
pub evm: MainEvm<CacheDB<EmptyDB>, BlockEnv, TxEnv, CfgEnv>,
|
||||
|
||||
/// The address where Push3Interpreter was deployed.
|
||||
pub interpreter_addr: revm::primitives::Address,
|
||||
}
|
||||
|
||||
impl EvmRunner {
|
||||
/// Deploy a new ephemeral EVM with a "Create" transaction for the given `creation_code`.
|
||||
///
|
||||
/// This matches your older style of:
|
||||
/// `MainEvm::new(Context::builder()... .with_db(...), EthHandler::default())`
|
||||
pub fn new(creation_code: Vec<u8>) -> Result<Self> {
|
||||
// 1) Create the EVM using your old style: `Context::builder()...`
|
||||
// Then pass to MainEvm::new(...).
|
||||
// The difference is that we explicitly say MainEvm<DB,BLOCK,TX,CFG>.
|
||||
let mut evm = MainEvm::new(
|
||||
// a) Prepare context with a chained transaction to CREATE
|
||||
Context::builder()
|
||||
.modify_tx_chained(|tx| {
|
||||
tx.transact_to = TxKind::Create;
|
||||
tx.data = Bytes::from(creation_code.clone());
|
||||
})
|
||||
// b) Provide ephemeral DB
|
||||
.with_db(CacheDB::<EmptyDB>::default()),
|
||||
// c) The EthHandler for block/tx environment logic
|
||||
EthHandler::default(),
|
||||
);
|
||||
|
||||
// 2) Execute the CREATE transaction
|
||||
let creation_result = evm.exec_commit()?;
|
||||
let ExecutionResult::Success {
|
||||
output: Output::Create(_, Some(deployed_addr)),
|
||||
..
|
||||
} = creation_result
|
||||
else {
|
||||
bail!("Interpreter deployment failed or no address returned: {creation_result:#?}");
|
||||
};
|
||||
|
||||
// 3) Return the EvmRunner
|
||||
Ok(Self {
|
||||
evm,
|
||||
interpreter_addr: deployed_addr,
|
||||
})
|
||||
}
|
||||
|
||||
/// Call `runInterpreter(bytes,uint256[],uint256[],int256[])` on the deployed contract,
|
||||
/// returning the final code/exec/int stacks.
|
||||
pub fn run_interpreter(
|
||||
&mut self,
|
||||
inputs: &Push3InterpreterInputs
|
||||
) -> Result<Push3InterpreterOutputs> {
|
||||
// 1) Build function selector
|
||||
let func_selector = &utils::id("runInterpreter(bytes,uint256[],uint256[],int256[])")[0..4];
|
||||
|
||||
// 2) Convert each field to `ethers::abi::Token`
|
||||
let code_token = Token::Bytes(inputs.code.clone());
|
||||
let init_code_stack = Token::Array(
|
||||
inputs.init_code_stack
|
||||
.iter()
|
||||
.map(|&u| Token::Uint(u))
|
||||
.collect()
|
||||
);
|
||||
let init_exec_stack = Token::Array(
|
||||
inputs.init_exec_stack
|
||||
.iter()
|
||||
.map(|&u| Token::Uint(u))
|
||||
.collect()
|
||||
);
|
||||
let init_int_stack = Token::Array(
|
||||
inputs.init_int_stack
|
||||
.iter()
|
||||
.map(|&val_i128| {
|
||||
// store i128 as a U256. If you need negative values, do a two’s complement approach.
|
||||
let as_u128 = val_i128 as u128;
|
||||
Token::Int(U256::from(as_u128))
|
||||
})
|
||||
.collect()
|
||||
);
|
||||
|
||||
// 3) Encode
|
||||
let encoded_args = encode(&[
|
||||
code_token,
|
||||
init_code_stack,
|
||||
init_exec_stack,
|
||||
init_int_stack,
|
||||
]);
|
||||
|
||||
// 4) Build final call data
|
||||
let mut call_data = Vec::from(func_selector);
|
||||
call_data.extend_from_slice(&encoded_args);
|
||||
|
||||
// 5) Modify the transaction to CALL the deployed interpreter
|
||||
self.evm.context.modify_tx(|tx| {
|
||||
tx.transact_to = TxKind::Call(self.interpreter_addr);
|
||||
tx.data = Bytes::from(call_data);
|
||||
tx.nonce += 1; // increment nonce to avoid reuse
|
||||
});
|
||||
|
||||
// 6) Execute the call
|
||||
let call_result = self.evm.transact()?;
|
||||
match &call_result.result {
|
||||
ExecutionResult::Success {
|
||||
output: Output::Call(return_data),
|
||||
..
|
||||
} => {
|
||||
// 7) Decode (uint256[], uint256[], int256[])
|
||||
let param_types = &[
|
||||
ParamType::Array(Box::new(ParamType::Uint(256))), // finalCodeStack
|
||||
ParamType::Array(Box::new(ParamType::Uint(256))), // finalExecStack
|
||||
ParamType::Array(Box::new(ParamType::Int(256))), // finalIntStack
|
||||
];
|
||||
let decoded = decode(param_types, return_data)
|
||||
.map_err(|e| anyhow!("Failed to decode return data: {e}"))?;
|
||||
|
||||
// parse each array
|
||||
let final_code_stack = match &decoded[0] {
|
||||
Token::Array(arr) => arr.iter().filter_map(|t| {
|
||||
if let Token::Uint(u) = t { Some(*u) } else { None }
|
||||
}).collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
let final_exec_stack = match &decoded[1] {
|
||||
Token::Array(arr) => arr.iter().filter_map(|t| {
|
||||
if let Token::Uint(u) = t { Some(*u) } else { None }
|
||||
}).collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
let final_int_stack = match &decoded[2] {
|
||||
Token::Array(arr) => arr.iter().filter_map(|t| {
|
||||
if let Token::Int(u256_val) = t {
|
||||
// read only the lower 128 bits => ignoring sign extension
|
||||
let lo = u256_val.low_u128();
|
||||
Some(lo as i128)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
|
||||
Ok(Push3InterpreterOutputs {
|
||||
final_code_stack,
|
||||
final_exec_stack,
|
||||
final_int_stack,
|
||||
})
|
||||
}
|
||||
ExecutionResult::Revert { gas_used, output } => {
|
||||
bail!("Call reverted: gas used={gas_used:?}, output={output:?}")
|
||||
}
|
||||
other => {
|
||||
bail!("Call failed: {other:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience method to run an `UntypedAst`:
|
||||
/// - Convert AST => push3 code,
|
||||
/// - Build a sublist descriptor in the exec stack,
|
||||
/// - Call `run_interpreter`.
|
||||
pub fn run_ast(&mut self, ast: &UntypedAst) -> Result<Push3InterpreterOutputs> {
|
||||
// 1) Convert AST => push3 bytecode
|
||||
let code_bytes = ast.to_bytecode();
|
||||
let code_len = code_bytes.len() as u32;
|
||||
|
||||
// 2) Build a sublist descriptor
|
||||
let descriptor = make_sublist_descriptor(0, code_len);
|
||||
|
||||
// 3) Put code in `code` and sublist descriptor in `execStack`
|
||||
let inputs = Push3InterpreterInputs {
|
||||
code: code_bytes,
|
||||
init_code_stack: Vec::new(),
|
||||
init_exec_stack: vec![descriptor],
|
||||
init_int_stack: Vec::new(),
|
||||
};
|
||||
|
||||
// 4) Run interpreter
|
||||
self.run_interpreter(&inputs)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user