upload code

This commit is contained in:
nganhkhoa 2024-06-18 22:05:01 +07:00
commit 7e10d070b3
8 changed files with 2369 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

219
Cargo.lock generated Normal file
View File

@ -0,0 +1,219 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cpufeatures"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "libc"
version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "memchr"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "pest"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06"
dependencies = [
"memchr",
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pest_meta"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d"
dependencies = [
"once_cell",
"pest",
"sha2",
]
[[package]]
name = "proc-macro2"
version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "syn"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "ucd-trie"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "zkevm"
version = "0.1.0"
dependencies = [
"either",
"pest",
"pest_derive",
]

11
Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "zkevm"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
either = "1.9.0"
pest = "2.7.6"
pest_derive = "2.7.6"

1112
src/execution.rs Normal file

File diff suppressed because it is too large Load Diff

91
src/main.rs Normal file
View File

@ -0,0 +1,91 @@
use std::fs;
mod execution;
mod zkasm;
use crate::zkasm::{parse_instruction_list, parse_expr, Definition, Program, Rule, Subroutine, ZkasmParser};
use pest::Parser;
fn main() {
let filepath = std::env::args().nth(1).expect("dude, pass a file to decompile");
let input = fs::read_to_string(filepath).expect("cannot read file");
let program = ZkasmParser::parse(Rule::program, &input)
.expect("file format is wrong or the parser is wrong")
.next()
.expect("cannot parse input file as a zkasm program");
let definitions = program
.into_inner()
.find_map(|x| match x.as_rule() {
Rule::definition_list => Some(x),
_ => None,
})
.unwrap();
let mut program_ast = Program {
constants: Vec::new(),
definitions: Vec::new(),
};
for definition in definitions.into_inner() {
match definition.as_rule() {
Rule::variable => {
let mut var = definition.into_inner();
let vartype = var.next().unwrap();
let varname = var.next().unwrap().as_str();
if vartype.as_str() == "GLOBAL" {
program_ast
.definitions
.push(Definition::VariableGlobal(varname.into(), var.next().map(parse_expr)));
} else if vartype.as_str() == "CTX" {
program_ast
.definitions
.push(Definition::VariableCTX(varname.into(), var.next().map(parse_expr)));
} else {
unreachable!();
}
}
Rule::constant => {
let mut var = definition.into_inner();
let constid = var.next().unwrap().as_str();
if let Some(constexpr) = var.next() {
program_ast.constants.push((constid.into(), format!("{}", parse_expr(constexpr))));
} else {
program_ast.constants.push((constid.into(), "0".into()));
}
}
Rule::subroutine => {
let mut subroutine = definition.into_inner();
let name = subroutine.next().unwrap().as_str();
if let Some(instructions) = subroutine.next() {
let subroutine_ast = Subroutine {
name: name.to_string(),
instructions: parse_instruction_list(instructions),
};
program_ast
.definitions
.push(Definition::Subroutine(subroutine_ast));
} else {
// empty instruction list, or just a label that is alias to the next
let subroutine_ast = Subroutine {
name: name.to_string(),
instructions: Vec::new(),
};
program_ast
.definitions
.push(Definition::Subroutine(subroutine_ast));
}
}
Rule::include => {
let file = definition.into_inner().next().unwrap().as_str().to_string().replace("\"", "");
program_ast.definitions.push(Definition::Include(file));
}
_ => {
unreachable!();
}
}
}
program_ast.decompile();
}

269
src/zkasm.pest Normal file
View File

@ -0,0 +1,269 @@
identifier = @{
ASCII_ALPHA ~ ("_" | ASCII_ALPHANUMERIC)*
}
reference = @{
"@" ~ identifier
}
instruction_name = @{
ASCII_ALPHA_UPPER ~ ("_" | ASCII_ALPHANUMERIC)*
}
register = @{
"CTX"
| "A"
| "B"
| "C"
| "D"
| "E"
| "F"
| "RR"
| "SR"
| "SP"
| "PC"
| "RCX"
| "GAS"
| "HASHPOS"
| "HASH_LEFT"
| "HASH_RIGHT"
| "OLD_ROOT"
| "NEW_ROOT"
| "VALUE_LOW"
| "VALUE_HIGH"
| "SIBLING_VALUE_HASH"
| "RKEY_BIT"
| "RKEY"
| "SIBLING_RKEY"
| "LEVEL"
}
memory_scope = {
"MEM"
| "STACK"
| "SYS"
}
memory_access = {
memory_scope ~ ":" ~ expr
}
instruction_right_param_atomic = {
memory_access
| expr
}
instruction_right_param_list = _{
instruction_right_param_atomic ~ "," ~ instruction_right_param_list
| instruction_right_param_atomic
}
instruction_right = {
instruction_name ~ "(" ~ instruction_right_param_list? ~ ")"
| instruction_name
}
assignment = {
expr ~ "=>" ~ register ~ ("," ~ register)*
}
dotaccess = @{
identifier ~ "." ~ identifier
}
invocation_param_atomic = {
dotaccess
| identifier
| register
}
params = {
invocation_param_atomic ~ ("," ~ invocation_param_atomic)*
}
invocation = {
identifier ~ "(" ~ params? ~ ")"
}
freeinput_param = {
invocation
| expr
}
freeinput = {
| "$${" ~ (!("}") ~ ANY)* ~ "}"
| "$0{" ~ (!("}") ~ ANY)* ~ "}"
| "${" ~ (!("}") ~ ANY)* ~ "}"
| "$"
}
binop = _{
power
| add
| sub
| mul
| div
| mod
| shl
| shr
| xor
| and
| or
| lt
| gt
| lte
| gte
| eq
| neq
| land
| lor
}
power = { "**" }
add = { "+" }
sub = { "-" }
mul = { "*" }
div = { "/" }
mod = { "%" }
shl = { "<<" }
shr = { ">>" }
xor = { "^" }
and = { "&" }
or = { "|" }
lt = { "<" }
gt = { ">" }
lte = { "<=" }
gte = { ">=" }
eq = { "==" }
neq = { "!=" }
land = { "&&" }
lor = { "||" }
special_variable = @{
!("VAR") ~ !("INCLUDE") ~ ("_" | ASCII_ALPHANUMERIC)+ ~ !(":")
}
number = @{
"0x" ~ ASCII_HEX_DIGIT+ ~ "n"?
| ASCII_DIGIT+ ~ "n"?
}
atomic_operand = _{
number
| constid
| freeinput
| special_variable
| reference
| register
}
selfmodifying = _{
increment | decrement
}
increment = {"++"}
decrement = {"--"}
exprfragment = _{
atomic_operand ~ binop ~ atomic_operand
| atomic_operand ~ selfmodifying?
| negate_expr
| "(" ~ expr_all ~ ")"
}
expr = {
exprfragment ~ (binop ~ exprfragment)*
}
not = { "!" }
negate = { "-" }
negate_expr = _{
(not | negate) ~ expr
}
tenary_expr = {
expr ~ "?" ~ expr ~ ":" ~ expr
}
expr_all = _{
tenary_expr
| negate_expr
| expr
}
instruction_type1 = {
assignment
| expr
}
instruction_type2 = {
instruction_right ~ ("," ~ instruction_right)*
}
instruction_type3 = {
instruction_type1 ~ ":" ~ instruction_type2
}
instruction = _{
instruction_type3
| instruction_type1
| ":" ~ instruction_type2
}
instruction_list = {
instruction ~ (NEWLINE* ~ instruction)*
}
subroutine_id = @{ ("_" | ASCII_ALPHANUMERIC)* }
subroutine = {
subroutine_id ~ ":" ~ NEWLINE* ~ instruction_list?
}
definition = _{
constant
| variable
| subroutine
| include
}
definition_list = {
definition ~ (NEWLINE* ~ definition)*
}
variable_type = {
"GLOBAL"
| "CTX"
}
variable = {
"VAR" ~ variable_type ~ identifier ~ ("[" ~ expr ~ "]")?
}
constid = @{
"%" ~ identifier
}
constant = {
("CONSTL" | "CONST") ~ constid ~ ("=" ~ expr)?
}
file_path_component = @{
(ASCII_ALPHANUMERIC | "_" | "-" | ".")+
}
file_path = @{
file_path_component ~ ("/" ~ file_path_component)*
}
include_name = @{
"\"" ~ file_path ~ "\""
}
include = {
"INCLUDE" ~ include_name
}
// allow newline between
program = {
SOI ~ NEWLINE* ~ definition_list ~ NEWLINE* ~ EOI
}
WHITESPACE = _{ " " | "\t" }
comment_type1 = _{ ";" ~ (!NEWLINE ~ ANY)* ~ NEWLINE? }
comment_type2 = _{ "/*" ~ (!"*/" ~ ANY)* ~ "*/" }
COMMENT = _{ comment_type1 | comment_type2 }

623
src/zkasm.rs Normal file
View File

@ -0,0 +1,623 @@
#[derive(pest_derive::Parser)]
#[grammar = "zkasm.pest"]
pub struct ZkasmParser;
use pest::iterators::Pair;
use std::fmt;
use crate::execution::Execution;
#[derive(Debug, Clone)]
pub enum UnaryOp {
Negate,
Not,
}
impl fmt::Display for UnaryOp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
UnaryOp::Negate => write!(f, "-"),
UnaryOp::Not => write!(f, "!"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Mod,
Lt,
Gt,
Le,
Ge,
Eq,
Neq,
LogicalXor,
LogicalAnd,
LogicalOr,
ArithAnd,
ArithOr,
ShiftLeft,
ShiftRight,
Power,
}
impl fmt::Display for BinaryOp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
BinaryOp::Add => "+",
BinaryOp::Sub => "-",
BinaryOp::Mul => "*",
BinaryOp::Div => "/",
BinaryOp::Lt => "<",
BinaryOp::Gt => ">",
BinaryOp::Le => "<=",
BinaryOp::Ge => ">=",
BinaryOp::Eq => "==",
BinaryOp::Neq => "!=",
BinaryOp::ArithOr => "|",
BinaryOp::ArithAnd => "&",
BinaryOp::LogicalOr => "||",
BinaryOp::LogicalAnd => "&&",
BinaryOp::ShiftLeft => "<<",
BinaryOp::ShiftRight => ">>",
BinaryOp::Power => "**",
BinaryOp::Mod => "%",
_ => {
unreachable!()
}
};
write!(f, "{}", s)
}
}
impl Into<BinaryOp> for String {
fn into(self) -> BinaryOp {
match self.as_str() {
"+" => BinaryOp::Add,
"-" => BinaryOp::Sub,
"*" => BinaryOp::Mul,
"/" => BinaryOp::Div,
"%" => BinaryOp::Mod,
"<" => BinaryOp::Lt,
">" => BinaryOp::Gt,
"<=" => BinaryOp::Le,
">=" => BinaryOp::Ge,
"==" => BinaryOp::Eq,
"!=" => BinaryOp::Neq,
"|" => BinaryOp::ArithOr,
"&" => BinaryOp::ArithAnd,
"||" => BinaryOp::LogicalOr,
"&&" => BinaryOp::LogicalAnd,
"<<" => BinaryOp::ShiftLeft,
">>" => BinaryOp::ShiftRight,
"**" => BinaryOp::Power,
_ => {
unreachable!()
}
}
}
}
#[derive(Debug, Clone)]
pub enum Expr {
Unary(UnaryOp, Box<Expr>),
Binary(Box<Expr>, BinaryOp, Box<Expr>),
Tenary(Box<Expr>, Box<Expr>, Box<Expr>),
FreeInput(String),
Register(Register),
Value(u64),
ValueComplex(String), // for hex number
NameVariable(String),
Increment(Box<Expr>),
Decrement(Box<Expr>),
}
impl fmt::Display for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expr::Unary(op, expr) => {
write!(f, "{}({})", op, expr)
}
Expr::Binary(lhs, op, rhs) => {
write!(f, "({} {} {})", lhs, op, rhs)
}
Expr::Tenary(expr, ifbranch, elsebranch) => {
write!(f, "({} ? {} : {})", expr, ifbranch, elsebranch)
}
Expr::Register(r) => {
write!(f, "{}", r.name())
}
Expr::Value(v) => {
write!(f, "{}", v)
}
Expr::ValueComplex(v) => {
if v.starts_with("0x") {
match u64::from_str_radix(v.strip_prefix("0x").unwrap(), 16).ok() {
Some(s) => write!(f, "{}/* {} */", s, v),
None => write!(f, "{}", v),
}
} else {
write!(f, "{}", v)
}
}
Expr::NameVariable(v) => {
write!(f, "{}", v)
}
Expr::FreeInput(free) => {
write!(f, "{}", free)
}
Expr::Increment(e) => {
write!(f, "({})++", e)
}
Expr::Decrement(e) => {
write!(f, "({})--", e)
}
}
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum Register {
A,
B,
C,
D,
E,
SP,
RR,
SR,
PC,
CTX,
RCX,
GAS,
HASHPOS,
HASH_LEFT,
HASH_RIGHT,
OLD_ROOT,
NEW_ROOT,
VALUE_LOW,
VALUE_HIGH,
SIBLING_VALUE_HASH,
RKEY,
SIBLING_RKEY,
RKEY_BIT,
LEVEL,
}
impl fmt::Display for Register {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
_ if self.is_special() => write!(f, "{}", self.name()),
Register::A => write!(f, "A"),
Register::B => write!(f, "B"),
Register::C => write!(f, "C"),
Register::D => write!(f, "D"),
Register::E => write!(f, "E"),
Register::SP => write!(f, "SP"),
Register::SR => write!(f, "SR"),
Register::RR => write!(f, "RR"),
Register::PC => write!(f, "PC"),
Register::GAS => write!(f, "GAS"),
Register::CTX => write!(f, "CTX"),
Register::RCX => write!(f, "RCX"),
Register::HASHPOS => write!(f, "HASHPOS"),
Register::HASH_LEFT => write!(f, "HASH_LEFT"),
Register::HASH_RIGHT => write!(f, "HASH_RIGHT"),
Register::OLD_ROOT => write!(f, "OLD_ROOT"),
Register::NEW_ROOT => write!(f, "NEW_ROOT"),
Register::VALUE_LOW => write!(f, "VALUE_LOW"),
Register::VALUE_HIGH => write!(f, "VALUE_HIGH"),
Register::SIBLING_VALUE_HASH => write!(f, "SIBLING_VALUE_HASH"),
Register::RKEY => write!(f, "RKEY"),
Register::SIBLING_RKEY => write!(f, "SIBLING_RKEY"),
Register::RKEY_BIT => write!(f, "RKEY_BIT"),
Register::LEVEL => write!(f, "LEVEL"),
}
}
}
impl Register {
pub fn is_special(self) -> bool {
match self {
Register::A | Register::B | Register::C | Register::D | Register::E => false,
_ => true,
}
}
pub fn name(self) -> String {
match self {
Register::SR => "StateRoot".to_string(),
Register::RR => "ReturnRegister".to_string(),
Register::GAS => "GAS".to_string(),
Register::HASHPOS => "HASHPOS".to_string(),
Register::CTX => "CONTEXT".to_string(),
Register::PC => "EVM(ProgramCounter)".to_string(),
Register::SP => "EVM(StackPointer)".to_string(),
_ => format!("{:?}", self)
}
}
pub fn from_name(name: &str) -> Self {
match name {
"A" => Register::A,
"B" => Register::B,
"C" => Register::C,
"D" => Register::D,
"E" => Register::E,
"SP" => Register::SP,
"RR" => Register::RR,
"SR" => Register::SR,
"PC" => Register::PC,
"CTX" => Register::CTX,
"RCX" => Register::RCX,
"GAS" => Register::GAS,
"HASHPOS" => Register::HASHPOS,
"HASH_LEFT" => Register::HASH_LEFT,
"HASH_RIGHT" => Register::HASH_RIGHT,
"OLD_ROOT" => Register::OLD_ROOT,
"NEW_ROOT" => Register::NEW_ROOT,
"VALUE_LOW" => Register::VALUE_LOW,
"VALUE_HIGH" => Register::VALUE_HIGH,
"SIBLING_VALUE_HASH" => Register::SIBLING_VALUE_HASH,
"RKEY" => Register::RKEY,
"SIBLING_RKEY" => Register::SIBLING_RKEY,
"RKEY_BIT" => Register::RKEY_BIT,
"LEVEL" => Register::LEVEL,
_ => {
unreachable!()
}
}
}
}
#[derive(Debug, Clone)]
pub enum AccessPlace {
Memory,
Stack,
System,
}
impl fmt::Display for AccessPlace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AccessPlace::Memory => write!(f, "MEM"),
AccessPlace::Stack => write!(f, "STACK"),
AccessPlace::System => write!(f, "SYS"),
}
}
}
#[derive(Debug, Clone)]
pub enum InstructionOpcodeParam {
NameVariable(String),
Accessor(AccessPlace, Expr),
Calculated(Expr),
}
impl fmt::Display for InstructionOpcodeParam {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
InstructionOpcodeParam::NameVariable(name) => {
write!(f, "{}", name)
}
InstructionOpcodeParam::Accessor(access, expr) => {
write!(f, "{}:{}", access, expr)
}
InstructionOpcodeParam::Calculated(expr) => {
write!(f, "{}", expr)
}
}
}
}
#[derive(Debug)]
pub struct InstructionOpcode {
pub name: String,
pub params: Vec<InstructionOpcodeParam>,
}
impl InstructionOpcode {
pub fn get_single(&self) -> InstructionOpcodeParam {
self.params[0].clone()
}
pub fn get_at(&self, idx: usize) -> Option<InstructionOpcodeParam> {
self.params.get(idx).map(|x| x.clone())
}
}
#[derive(Debug)]
pub enum Instruction {
Expr(Expr),
Assignment(Expr, Vec<Register>),
Opcode(InstructionOpcode),
Compound(Box<Instruction>, Vec<Instruction>),
}
#[derive(Debug)]
pub struct Subroutine {
pub name: String,
pub instructions: Vec<Instruction>,
}
#[derive(Debug)]
pub enum Definition {
VariableGlobal(String, Option<Expr>),
VariableCTX(String, Option<Expr>),
Subroutine(Subroutine),
Include(String),
}
// temporarily use display as a decompile printer
impl fmt::Display for Definition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Definition::VariableCTX(var, Some(array)) => {
write!(f, "VAR CTX {}[{}]", var, array)
}
Definition::VariableGlobal(var, Some(array)) => {
write!(f, "VAR GLOBAL {}[{}]", var, array)
}
Definition::VariableCTX(var, None) => {
write!(f, "VAR CTX {}", var)
}
Definition::VariableGlobal(var, None) => {
write!(f, "VAR GLOBAL {}", var)
}
Definition::Include(include) => {
write!(f, "INCLUDE {}", include)
}
Definition::Subroutine(subroutine) => {
// subroutine will be decompiled
let mut run = Execution::new();
run.start(subroutine);
Ok(())
}
}
}
}
#[derive(Debug)]
pub struct Program {
pub constants: Vec<(String, String)>,
pub definitions: Vec<Definition>,
}
impl Program {
pub fn decompile(self) {
for c in self.constants {
println!("CONST {} = {}", c.0, c.1);
}
for def in self.definitions {
println!("{}", def);
}
}
}
fn parse_long_expr(expr: Pair<Rule>) -> Expr {
use pest::pratt_parser::PrattParser;
use pest::pratt_parser::{Assoc, Op};
assert!(expr.as_rule() == Rule::expr);
let pratt =
PrattParser::new()
.op(Op::infix(Rule::r#mod, Assoc::Left))
.op(Op::infix(Rule::add, Assoc::Left) | Op::infix(Rule::sub, Assoc::Left))
.op(Op::infix(Rule::mul, Assoc::Left) | Op::infix(Rule::div, Assoc::Left))
.op(Op::infix(Rule::and, Assoc::Left) | Op::infix(Rule::or, Assoc::Left) | Op::infix(Rule::xor, Assoc::Left))
.op(Op::infix(Rule::shl, Assoc::Left) | Op::infix(Rule::shr, Assoc::Left))
.op(Op::infix(Rule::power, Assoc::Left))
.op(Op::prefix(Rule::not) | Op::prefix(Rule::negate))
.op(Op::postfix(Rule::increment) | Op::postfix(Rule::decrement))
;
// println!("{:?}", expr);
pratt
.map_primary(|primary| match primary.as_rule() {
Rule::expr => {
// ( expr )
parse_expr(primary)
}
_ => {
parse_expr_atomic(primary)
}
})
.map_prefix(|op, rhs| {
match op.as_str() {
"-" => {
Expr::Unary(UnaryOp::Negate, Box::new(rhs))
}
"!" => {
Expr::Unary(UnaryOp::Not, Box::new(rhs))
}
_ => unreachable!()
}})
.map_infix(|lhs, op, rhs| {
let binop: BinaryOp = op.as_str().to_string().into();
Expr::Binary(Box::new(lhs.clone()), binop, Box::new(rhs.clone()))
})
.map_postfix(|expr, op| {
match op.as_rule() {
Rule::decrement => Expr::Decrement(Box::new(expr.clone())),
Rule::increment => Expr::Increment(Box::new(expr.clone())),
_ => unreachable!()
}
})
.parse(expr.into_inner())
}
fn parse_expr_atomic(expr: Pair<Rule>) -> Expr {
match expr.as_rule() {
Rule::register => Expr::Register(Register::from_name(expr.as_str())),
Rule::number => {
match expr.as_str().to_string().parse::<u64>().ok() {
Some(v) => Expr::Value(v),
None => Expr::ValueComplex(expr.as_str().into()),
}
}
Rule::freeinput => Expr::FreeInput(expr.as_str().into()),
Rule::special_variable => {
let v = expr.as_str();
match v {
"A" | "B" | "C" | "D" | "E" | "SR" | "RR" | "SP" | "CTX" | "HASHPOS" => {
Expr::Register(Register::from_name(v))
}
_ => Expr::NameVariable(v.into()),
}
}
Rule::constid => {
let v = expr.as_str();
match v {
"A" | "B" | "C" | "D" | "E" | "SR" | "RR" | "SP" | "CTX" | "HASHPOS" => {
Expr::Register(Register::from_name(v))
}
_ => Expr::NameVariable(v.into()),
}
}
Rule::reference => {
let v = expr.as_str();
Expr::NameVariable(v.into())
}
_ => {
println!("parse atomic {:?}", expr);
unreachable!();
}
}
}
pub fn parse_expr(inst: Pair<Rule>) -> Expr {
match inst.as_rule() {
Rule::expr => {
parse_long_expr(inst)
// let mut peak = inst.clone().into_inner();
// if peak.len() > 1 {
// return parse_long_expr(inst);
// }
// let expr = peak.next().unwrap();
// parse_expr_atomic(expr)
}
Rule::negate_expr => {
let mut p = inst.into_inner();
let op = match p.next().unwrap().as_str() {
"-" => UnaryOp::Negate,
"!" => UnaryOp::Not,
_ => unreachable!()
};
let expr = parse_expr(p.next().unwrap());
Expr::Unary(op, Box::new(expr))
}
Rule::tenary_expr => {
let mut p = inst.into_inner();
let condition = parse_expr(p.next().unwrap());
let ifbranch = parse_expr(p.next().unwrap());
let elsebranch = parse_expr(p.next().unwrap());
Expr::Tenary(Box::new(condition), Box::new(ifbranch), Box::new(elsebranch))
}
Rule::register | Rule::number | Rule::freeinput | Rule::special_variable | Rule::constid | Rule::reference => {
parse_expr_atomic(inst)
}
_ => {
unreachable!();
}
}
}
pub fn parse_param(param: Pair<Rule>) -> InstructionOpcodeParam {
match param.as_rule() {
Rule::identifier => InstructionOpcodeParam::NameVariable(param.as_str().into()),
Rule::memory_access => {
let mut p = param.into_inner();
let scope = match p.next().unwrap().as_str() {
"MEM" => AccessPlace::Memory,
"STACK" => AccessPlace::Stack,
"SYS" => AccessPlace::System,
_ => unreachable!(),
};
let expr = parse_expr(p.next().unwrap());
InstructionOpcodeParam::Accessor(scope, expr)
}
Rule::expr => {
let e = parse_expr(param);
if let Expr::NameVariable(name) = e {
InstructionOpcodeParam::NameVariable(name)
} else {
InstructionOpcodeParam::Calculated(e)
}
}
_ => {
unreachable!();
}
}
}
// parse a single instruction, could be type 1, 2 or 3
pub fn parse_instruction(instruction: Pair<Rule>) -> Instruction {
// println!("parsing inst {:?}", instruction);
match instruction.as_rule() {
Rule::instruction_type1 => {
let inst = instruction.into_inner().next().unwrap();
if inst.as_rule() == Rule::expr {
Instruction::Expr(parse_expr(inst))
} else if inst.as_rule() == Rule::negate_expr {
Instruction::Expr(parse_expr(inst))
} else if inst.as_rule() == Rule::assignment {
let mut p = inst.into_inner();
let expr = parse_expr(p.next().unwrap());
let registers: Vec<Register> = p.map(|r| Register::from_name(r.as_str())).collect();
Instruction::Assignment(expr, registers)
} else {
println!("parsing inst {:?}", inst);
unreachable!()
}
// println!("typ1 {:?}", inst)
}
// instruction_type2 is a list of instruction_right and will be resolved using parse_instruction_list
Rule::instruction_right => {
let mut inst = instruction.into_inner();
let name = inst.next().unwrap().as_str();
let mut params: Vec<InstructionOpcodeParam> = Vec::new();
while let Some(param) = inst.next() {
let mut p: Vec<InstructionOpcodeParam> = param.into_inner().map(parse_param).collect();
params.append(&mut p);
}
Instruction::Opcode(InstructionOpcode {
name: name.into(),
params,
})
}
Rule::instruction_type3 => {
let mut insts = instruction.into_inner();
let lhs = parse_instruction(insts.next().unwrap());
let rhs = parse_instruction_list(insts.next().unwrap());
Instruction::Compound(Box::new(lhs), rhs)
}
_ => {
unreachable!()
}
}
}
// parse a list of instruction
pub fn parse_instruction_list(instruction_list: Pair<Rule>) -> Vec<Instruction> {
let mut parsed_instructions = Vec::new();
for instruction in instruction_list.into_inner() {
if instruction.as_rule() == Rule::instruction_type2 {
// could be many or 1
for inst in instruction.into_inner() {
parsed_instructions.push(parse_instruction(inst));
}
} else {
parsed_instructions.push(parse_instruction(instruction));
}
}
return parsed_instructions;
}

43
test.zkasm Normal file
View File

@ -0,0 +1,43 @@
/*
CONST %aa = 2**32**8 ; atomic ** atomic
CONST %aa = 2**32 ; atomic ** atomic
CONST %bb = 2**32-1 ; atomic ** atomic ~ rest
CONST %bb = (1+1)**32-1 ; expr ** atomic ~ rest
CONST %bb = 2**(31+1)-1 ; atomic ** expr ~ rest
CONST %bb = (1+1)**(31+1)-1 ; expr ** expr ~ rest
CONST %bb = (1+1)**(-32)-1 ; expr ** expr ~ rest
CONST %cc = 2**32-1-1 % 10
CONST %dd = 2**(32-1) ; good
CONST %ee = (2**32)-1 ; good
CONSTL %P2_C0_EGX = %ECGX & 0xFFFF
a:
-1 => B
CONST %MAX_CNT_PADDING_PG_LIMIT = (%TOTAL_STEPS_LIMIT / 56)
*/
; CONSTL %FPEC = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2Fn
; CONSTL %FPEC_MINUS_ONE = %FPEC - 1
; CONSTL %FNEC_DIV_TWO = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0n
; CONSTL %FPEC_C2_256 = 0x1000003D1n
; CONSTL %FPEC_NON_SQRT = (1n << 256n) - 1n
;
; CONSTL %FNEC = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141n
; CONSTL %FNEC_MINUS_ONE = %FNEC - 1
;
; CONSTL %ECGX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798n
; CONSTL %ECGY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8n
; CONSTL %P2_160 = 2n ** 160n
; CONSTL %P2_96 = 2n ** 96n
/*
Run:
0 => HASH_LEFT, HASH_RIGHT, OLD_ROOT, NEW_ROOT, VALUE_LOW, VALUE_HIGH, SIBLING_VALUE_HASH, RKEY, SIBLING_RKEY, RKEY_BIT, LEVEL
*/
a:
A :MSTORE(SP--)