Expressions
push
opcodes
The push
family of instructions require an immediate argument. Unlike most arguments (which come from the stack), immediate arguments are encoded in the bytes immediately following the opcode.
For example, push2 258
would assemble to 0x610102
. 0x61
is the instruction, and 0x0102
is the immediate argument. push2
instructs the EVM to use the next two bytes 0x01
and 0x02
as input for the op. That value is left-padded with zeros to 32 bytes (so 0x0000...000102
) and placed on the stack.
While an assembled push
must have a concrete value, it is often useful when developing a program to have the ability to manipulate the operand at compile time. For this reason, push
opcodes take a single expression as their operand.
#![allow(unused)] fn main() { extern crate etk_asm; let src = r#" push1 1+(2*3)/4 "#; let mut output = Vec::new(); let mut ingest = etk_asm::ingest::Ingest::new(&mut output); ingest.ingest(file!(), src).unwrap(); assert_eq!(output, &[0x60, 0x02]); }
Definition
An expression is a standard infix mathematical expression that is evaluated during assembly. Its computed value must fit within the preceding push
's size allowance (eg. less than 256 for push8
).
Terms
Many different types of values are allowed as a term in an expression:
Integer Literals
Integer literals are described by the following Pest grammar:
number = _{ binary | octal | hex | decimal | negative }
decimal = @{ ASCII_DIGIT+ }
negative = @{ "-" ~ ASCII_DIGIT+ }
binary = @{ "0b" ~ ASCII_BIN_DIGIT+ }
hex = @{ "0x" ~ ASCII_HEX_DIGIT ~ ASCII_HEX_DIGIT+ }
octal = @{ "0o" ~ ASCII_OCT_DIGIT+ }
There is no limit for the length of integer literals. While expressions support
both signed and unsigned integers, the result of the expression must
non-negative and fit within the width of the corresponding push
instruction.
Labels
A label may be used as a term in an expression.
#![allow(unused)] fn main() { extern crate etk_asm; let src = r#" start: push1 start + 1 "#; let mut output = Vec::new(); let mut ingest = etk_asm::ingest::Ingest::new(&mut output); ingest.ingest(file!(), src).unwrap(); assert_eq!(output, &[0x60, 0x01]); }
Macros
Expression macros may be used as a term in an expression.
#![allow(unused)] fn main() { extern crate etk_asm; let src = r#" push4 selector("transfer(uint256,uint256)") "#; let mut output = Vec::new(); let mut ingest = etk_asm::ingest::Ingest::new(&mut output); ingest.ingest(file!(), src).unwrap(); assert_eq!(output, &[0x63, 12, 247, 158, 10]); }
Operators
Binary
Expressions support the following binary operators:
#![allow(unused)] fn main() { extern crate etk_asm; let src = r#" push1 1+2 # addition push1 1*2 # multiplication push1 2-1 # subtraction push1 2/2 # division "#; let mut output = Vec::new(); let mut ingest = etk_asm::ingest::Ingest::new(&mut output); ingest.ingest(file!(), src).unwrap(); assert_eq!(output, &[0x60, 0x03, 0x60, 0x02, 0x60, 0x01, 0x60, 0x01]); }