@@ -125,6 +125,21 @@ where
125
125
core:: array:: from_fn ( |i| outer ( table[ i] ) )
126
126
}
127
127
128
+ /// An error indicating that an opcode is invalid.
129
+ #[ derive( Debug , PartialEq , Eq ) ]
130
+ #[ cfg( feature = "parse" ) ]
131
+ pub struct OpCodeError ( ( ) ) ;
132
+
133
+ #[ cfg( feature = "parse" ) ]
134
+ impl fmt:: Display for OpCodeError {
135
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
136
+ f. write_str ( "invalid opcode" )
137
+ }
138
+ }
139
+
140
+ #[ cfg( all( feature = "std" , feature = "parse" ) ) ]
141
+ impl std:: error:: Error for OpCodeError { }
142
+
128
143
/// An EVM opcode.
129
144
///
130
145
/// This is always a valid opcode, as declared in the [`opcode`][self] module or the
@@ -144,6 +159,16 @@ impl fmt::Display for OpCode {
144
159
}
145
160
}
146
161
162
+ #[ cfg( feature = "parse" ) ]
163
+ impl core:: str:: FromStr for OpCode {
164
+ type Err = OpCodeError ;
165
+
166
+ #[ inline]
167
+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
168
+ Self :: parse ( s) . ok_or ( OpCodeError ( ( ) ) )
169
+ }
170
+ }
171
+
147
172
impl OpCode {
148
173
/// Instantiate a new opcode from a u8.
149
174
#[ inline]
@@ -154,6 +179,13 @@ impl OpCode {
154
179
}
155
180
}
156
181
182
+ /// Parses an opcode from a string. This is the inverse of [`as_str`](Self::as_str).
183
+ #[ inline]
184
+ #[ cfg( feature = "parse" ) ]
185
+ pub fn parse ( s : & str ) -> Option < Self > {
186
+ NAME_TO_OPCODE . get ( s) . copied ( )
187
+ }
188
+
157
189
/// Returns true if the opcode is a jump destination.
158
190
#[ inline]
159
191
pub const fn is_jumpdest ( & self ) -> bool {
@@ -213,7 +245,7 @@ impl OpCode {
213
245
Self ( opcode)
214
246
}
215
247
216
- /// Returns the opcode as a string.
248
+ /// Returns the opcode as a string. This is the inverse of [`parse`](Self::parse).
217
249
#[ doc( alias = "name" ) ]
218
250
#[ inline]
219
251
pub const fn as_str ( self ) -> & ' static str {
@@ -416,6 +448,25 @@ pub const fn stack_io(mut op: OpCodeInfo, inputs: u8, outputs: u8) -> OpCodeInfo
416
448
/// Alias for the [`JUMPDEST`] opcode.
417
449
pub const NOP : u8 = JUMPDEST ;
418
450
451
+ /// Callback for creating a [`phf`] map with `stringify_with_cb`.
452
+ #[ cfg( feature = "parse" ) ]
453
+ macro_rules! phf_map_cb {
454
+ ( $( #[ doc = $s: literal] $id: ident) * ) => {
455
+ phf:: phf_map! {
456
+ $( $s => OpCode :: $id) ,*
457
+ }
458
+ } ;
459
+ }
460
+
461
+ /// Stringifies identifiers with `paste` so that they are available as literals.
462
+ /// This doesn't work with `stringify!` because it cannot be expanded inside of another macro.
463
+ #[ cfg( feature = "parse" ) ]
464
+ macro_rules! stringify_with_cb {
465
+ ( $callback: ident; $( $id: ident) * ) => { paste:: paste! {
466
+ $callback! { $( #[ doc = "" $id "" ] $id) * }
467
+ } } ;
468
+ }
469
+
419
470
macro_rules! opcodes {
420
471
( $( $val: literal => $name: ident => $f: expr => $( $modifier: ident $( ( $( $modifier_arg: expr) ,* ) ) ?) ,* ) ;* $( ; ) ?) => {
421
472
// Constants for each opcode. This also takes care of duplicate names.
@@ -428,7 +479,7 @@ macro_rules! opcodes {
428
479
pub const $name: Self = Self ( $val) ;
429
480
) * }
430
481
431
- /// Maps each opcode to its name .
482
+ /// Maps each opcode to its info .
432
483
pub const OPCODE_INFO_JUMPTABLE : [ Option <OpCodeInfo >; 256 ] = {
433
484
let mut map = [ None ; 256 ] ;
434
485
let mut prev: u8 = 0 ;
@@ -446,6 +497,10 @@ macro_rules! opcodes {
446
497
map
447
498
} ;
448
499
500
+ /// Maps each name to its opcode.
501
+ #[ cfg( feature = "parse" ) ]
502
+ static NAME_TO_OPCODE : phf:: Map <& ' static str , OpCode > = stringify_with_cb! { phf_map_cb; $( $name) * } ;
503
+
449
504
/// Returns the instruction function for the given opcode and spec.
450
505
pub const fn instruction<H : Host + ?Sized , SPEC : Spec >( opcode: u8 ) -> Instruction <H > {
451
506
match opcode {
@@ -839,4 +894,14 @@ mod tests {
839
894
) ;
840
895
}
841
896
}
897
+
898
+ #[ test]
899
+ #[ cfg( feature = "parse" ) ]
900
+ fn test_parsing ( ) {
901
+ for i in 0 ..=u8:: MAX {
902
+ if let Some ( op) = OpCode :: new ( i) {
903
+ assert_eq ! ( OpCode :: parse( op. as_str( ) ) , Some ( op) ) ;
904
+ }
905
+ }
906
+ }
842
907
}
0 commit comments