Skip to content

Instantly share code, notes, and snippets.

@benjiqq
Last active July 2, 2023 05:28
Show Gist options
  • Save benjiqq/a463b1a9269ed7128a1bc8a3524d18c4 to your computer and use it in GitHub Desktop.
Save benjiqq/a463b1a9269ed7128a1bc8a3524d18c4 to your computer and use it in GitHub Desktop.
jechain rs
// 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(&reg).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(&reg).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