Last active
July 2, 2023 05:28
-
-
Save benjiqq/a463b1a9269ed7128a1bc8a3524d18c4 to your computer and use it in GitHub Desktop.
jechain rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// basic Jechain interpreter | |
// read from simple.jel | |
// example | |
// "SET a, 0" | |
// "SET b, 1" | |
// "STORE astore, 111" | |
// "ADD b, 1" | |
// "SET a, $b" | |
// -------- | |
// result | |
// {"a": 2, "b": 2} | |
// {"astore": 111} | |
use std::collections::HashMap; | |
use std::fs; | |
use std::fs::File; | |
use std::io; | |
use std::io::{prelude::*, BufReader}; | |
use std::str::FromStr; | |
#[derive(Debug, PartialEq)] | |
enum OpCode { | |
Instruction(String, String, String), | |
} | |
// Command to jump to labels conditionally | |
const OP_JUMP: &'static str = "jump"; | |
// Used to set values for variables | |
const OP_SET: &str = "SET"; | |
// storage[key] = value | |
const OP_STORE: &str = "STORE"; | |
// memory[key1] = storage[key2] | |
//const OP_PULL: | |
// Add value to variable | |
const OP_ADD: &str = "ADD"; | |
// Subtract value from variable | |
const OP_SUB: &str = "SUB"; | |
// Multiply variable by value | |
const OP_MUL: &str = "MUL"; | |
// Divide variable by value | |
const OP_DIV: &str = "DIV"; | |
// Modulo | |
const OP_MOD: &str = "MOD"; | |
const OP_AND: &str = "and"; | |
const OP_OR: &str = "or"; | |
const OP_XOR: &str = "xor"; | |
// Left shift | |
const OP_LS: &str = "ls"; | |
// Right shift | |
const OP_RS: &str = "rs"; | |
const OP_NOT: &str = "not"; | |
// Greater than | |
const OP_GTR: &str = "gtr"; | |
// Less than | |
const OP_LSS: &str = "lss"; | |
// Greater or equal to | |
const OP_GEQ: &str = "geq"; | |
// Less or equal to | |
const OP_LEQ: &str = "leq"; | |
// Equal to | |
const OP_EQU: &str = "equ"; | |
// Not equal to | |
const OP_NEQ: &str = "neq"; | |
//-------------------------------- | |
// case "timestamp": // Block's timestamp | |
// case "blocknumber": // Block's number | |
// case "blockhash": // Block's hash | |
// case "difficulty": // Block's difficulty | |
// case "txvalue": // Amount of tokens sent in transaction | |
// case "txsender": // Sender of transaction | |
// case "txgas": // Transaction gas | |
// case "txexecgas": // Contract execution gas | |
// case "address": // Contract's address | |
// case "selfbalance": // Contract's balance | |
// case "balance": // Get balance from address | |
// case "send": // Send tokens to address | |
impl FromStr for OpCode { | |
type Err = io::Error; | |
fn from_str(s: &str) -> Result<Self, Self::Err> { | |
let parts: Vec<&str> = s.splitn(2, ' ').collect(); | |
if parts.len() != 2 { | |
return Err(io::Error::new( | |
io::ErrorKind::InvalidInput, | |
"invalid instruction", | |
)); | |
} | |
let opcode = parts[0].trim().to_string(); | |
let operands: Vec<&str> = parts[1].splitn(2, ',').collect(); | |
if operands.len() != 2 { | |
return Err(io::Error::new( | |
io::ErrorKind::InvalidInput, | |
"invalid operands", | |
)); | |
} | |
let operand1 = operands[0].trim().to_string(); | |
let operand2 = operands[1].trim().to_string(); | |
Ok(OpCode::Instruction(opcode, operand1, operand2)) | |
} | |
} | |
fn get_value(token: &str, memory: &HashMap<String, i32>) -> i32 { | |
if token.starts_with('$') { | |
let reg = token.replace("$", ""); | |
return *memory.get(®).unwrap(); | |
} | |
//TODO deal with % | |
else { | |
return token.parse().unwrap(); | |
} | |
} | |
fn get_storage_value(token: &str, storage: &HashMap<String, i32>) -> i32 { | |
if token.starts_with('$') { | |
let reg = token.replace("$", ""); | |
return *storage.get(®).unwrap(); | |
} | |
//TODO deal with % | |
else { | |
return token.parse().unwrap(); | |
} | |
} | |
fn execute(instructions: Vec<OpCode>) { | |
let mut memory: HashMap<String, i32> = HashMap::new(); | |
let mut storage: HashMap<String, i32> = HashMap::new(); | |
let mut ptr = 0; | |
while ptr < instructions.len() { | |
match &instructions[ptr] { | |
OpCode::Instruction(opcode, operand1, operand2) => match opcode.as_str() { | |
OP_SET => { | |
let value = get_value(operand2, &memory); | |
memory.insert(operand1.clone(), value); | |
} | |
OP_STORE => { | |
let value = get_value(operand2, &memory); | |
storage.insert(operand1.clone(), value); | |
} | |
OP_PULL => { | |
let value = get_storage_value(operand2, &memory); | |
memory.insert(operand1.clone(), value); | |
} | |
OP_ADD => { | |
let value = get_value(operand2, &memory); | |
if let Some(existing) = memory.get_mut(operand1) { | |
*existing += value; | |
} | |
} | |
OP_SUB => { | |
let value = get_value(operand2, &memory); | |
if let Some(existing) = memory.get_mut(operand1) { | |
*existing -= value; | |
} | |
} | |
OP_MUL => { | |
let value = get_value(operand2, &memory); | |
if let Some(existing) = memory.get_mut(operand1) { | |
*existing *= value; | |
} | |
} | |
OP_DIV => { | |
let value = get_value(operand2, &memory); | |
if let Some(existing) = memory.get_mut(operand1) { | |
if value != 0 { | |
*existing /= value; | |
} else { | |
println!("Error: division by zero"); | |
} | |
} | |
} | |
OP_MOD => { | |
let value = get_value(operand2, &memory); | |
if let Some(existing) = memory.get_mut(operand1) { | |
if value != 0 { | |
*existing %= value; | |
} else { | |
println!("Error: division by zero"); | |
} | |
} | |
} | |
OP_AND => { | |
let value = get_value(operand2, &memory); | |
if let Some(existing) = memory.get_mut(operand1) { | |
*existing &= value; | |
} | |
} | |
OP_OR => { | |
let value = get_value(operand2, &memory); | |
if let Some(existing) = memory.get_mut(operand1) { | |
*existing |= value; | |
} | |
} | |
OP_XOR => { | |
let value = get_value(operand2, &memory); | |
if let Some(existing) = memory.get_mut(operand1) { | |
*existing ^= value; | |
} | |
} | |
OP_LS => { | |
let value = get_value(operand2, &memory) as u32; | |
if let Some(existing) = memory.get_mut(operand1) { | |
*existing <<= value; | |
} | |
} | |
OP_RS => { | |
let value = get_value(operand2, &memory) as u32; | |
if let Some(existing) = memory.get_mut(operand1) { | |
*existing >>= value; | |
} | |
} | |
OP_NOT => { | |
if let Some(existing) = memory.get_mut(operand1) { | |
*existing = !(*existing); | |
} | |
} | |
OP_GTR => { | |
let value = get_value(operand2, &memory); | |
memory.insert( | |
operand1.clone(), | |
if memory[operand1] > value { 1 } else { 0 }, | |
); | |
} | |
OP_LSS => { | |
let value = get_value(operand2, &memory); | |
memory.insert( | |
operand1.clone(), | |
if memory[operand1] < value { 1 } else { 0 }, | |
); | |
} | |
OP_GEQ => { | |
let value = get_value(operand2, &memory); | |
memory.insert( | |
operand1.clone(), | |
if memory[operand1] >= value { 1 } else { 0 }, | |
); | |
} | |
OP_LEQ => { | |
let value = get_value(operand2, &memory); | |
memory.insert( | |
operand1.clone(), | |
if memory[operand1] <= value { 1 } else { 0 }, | |
); | |
} | |
OP_EQU => { | |
let value = get_value(operand2, &memory); | |
memory.insert( | |
operand1.clone(), | |
if memory[operand1] == value { 1 } else { 0 }, | |
); | |
} | |
OP_NEQ => { | |
let value = get_value(operand2, &memory); | |
memory.insert( | |
operand1.clone(), | |
if memory[operand1] != value { 1 } else { 0 }, | |
); | |
} | |
OP_JUMP => { | |
// if get_value(operand1, &memory) == 1 { | |
// if let Some((new_ptr, _)) = instructions.iter().enumerate().find(|(_, line)| { | |
// if let OpCode::Instruction(op, label, _) = line { | |
// op == OP_LABEL && label == &get_value(operand2, &memory) | |
// } else { | |
// false | |
// } | |
// }) { | |
// ptr = new_ptr; | |
// } | |
// } | |
} | |
_ => println!("Unknown opcode"), | |
}, | |
} | |
ptr += 1; | |
} | |
println!("--------\nresult\n"); | |
println!("{:?}", memory); | |
println!("{:?}", storage); | |
} | |
fn main() { | |
let mut instructions = vec![]; | |
let filename = "simple.jel"; | |
let file = File::open(filename).unwrap(); | |
let reader = BufReader::new(file); | |
for line in reader.lines() { | |
match line { | |
Ok(content) => { | |
println!("{:?}", content); | |
let instruction: OpCode = content.parse().expect("Failed to parse instruction"); | |
instructions.push(instruction); | |
} | |
Err(e) => println!("Error reading line: {:?}", e), | |
} | |
} | |
// After the loop, you can pass the instructions vector to your execute function: | |
execute(instructions); | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
#[test] | |
fn test_opcode_parsing() { | |
// Define a string to parse | |
let instruction_str = "SET A, 0\nSET B, 1\nSTORE astore, 111\nADD B, 1\nSET A, $B"; | |
// Split the string into lines and parse each line into an OpCode | |
let instructions: Result<Vec<OpCode>, _> = instruction_str.lines().map(str::parse).collect(); | |
// Check that parsing succeeded | |
let instructions = instructions.expect("Failed to parse instructions"); | |
// Define the expected result | |
let expected_instructions = vec![ | |
OpCode::Instruction(String::from("SET"), String::from("A"), String::from("0")), | |
OpCode::Instruction(String::from("SET"), String::from("B"), String::from("1")), | |
OpCode::Instruction(String::from("STORE"), String::from("astore"), String::from("111")), | |
OpCode::Instruction(String::from("ADD"), String::from("B"), String::from("1")), | |
OpCode::Instruction(String::from("SET"), String::from("A"), String::from("$B")), | |
]; | |
// Check that the parsed instructions match the expected result | |
assert_eq!(instructions, expected_instructions); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment