1
- use std:: { num:: ParseIntError , str:: FromStr } ;
1
+ use std:: { collections :: HashMap , num:: ParseIntError , str:: FromStr } ;
2
2
3
3
use itertools:: Itertools ;
4
4
use redox_core:: {
@@ -40,6 +40,9 @@ const U32_REGISTERS: [RegisterId; 18] = [
40
40
RegisterId :: EDS ,
41
41
] ;
42
42
43
+ /// A dummy label jump address used when handling a label.
44
+ const DUMMY_LABEL_JUMP_ADDRESS : u128 = u32:: MAX as u128 ;
45
+
43
46
#[ derive( Debug , Clone ) ]
44
47
pub enum Argument {
45
48
/// A f64 argument.
@@ -88,17 +91,29 @@ macro_rules! get_inner_expr_arg {
88
91
}
89
92
90
93
pub struct AsmParser < ' a > {
94
+ /// The instruction hints for the parser.
91
95
hints : InstructionHints < ' a > ,
96
+
97
+ /// A vector of the parsed instructions.
98
+ pub parsed_instructions : Vec < Instruction > ,
99
+
100
+ /// A vector containing any label hints that were encountered.
101
+ pub labeled_instructions : HashMap < usize , String > ,
92
102
}
93
103
94
104
impl < ' a > AsmParser < ' a > {
95
105
pub fn new ( ) -> Self {
96
106
Self {
97
107
hints : InstructionHints :: new ( ) ,
108
+ parsed_instructions : vec ! [ ] ,
109
+ labeled_instructions : HashMap :: new ( ) ,
98
110
}
99
111
}
100
112
101
- pub fn parse_code ( & self , string : & str ) -> Vec < Instruction > {
113
+ pub fn parse_code ( & mut self , string : & str ) {
114
+ // Clear the list of label instruction hints.
115
+ self . labeled_instructions = HashMap :: with_capacity ( string. lines ( ) . count ( ) ) ;
116
+
102
117
let mut instructions: Vec < Instruction > = vec ! [ ] ;
103
118
104
119
// Split the string into lines.
@@ -110,6 +125,14 @@ impl<'a> AsmParser<'a> {
110
125
111
126
let raw_args = AsmParser :: parse_instruction_line ( line. trim_matches ( ' ' ) ) ;
112
127
128
+ // Are we dealing with a label marker. These are found at the start of a line
129
+ // and act as a target for branching instructions.
130
+ if raw_args. len ( ) == 1 && raw_args[ 0 ] . starts_with ( ':' ) {
131
+ let ins = Instruction :: Label ( raw_args[ 0 ] . to_string ( ) ) ;
132
+ instructions. push ( ins) ;
133
+ continue ;
134
+ }
135
+
113
136
// The name should always be the first argument we've extracted, the arguments should follow.
114
137
let name = raw_args[ 0 ] . to_lowercase ( ) ;
115
138
@@ -126,7 +149,7 @@ impl<'a> AsmParser<'a> {
126
149
. collect ( ) ;
127
150
128
151
// Do we have any arguments to process.
129
- for raw_arg in & raw_args[ 1 ..] {
152
+ for ( i , raw_arg) in raw_args[ 1 ..] . iter ( ) . enumerate ( ) {
130
153
let mut has_value_pushed = false ;
131
154
let mut hints = vec ! [ ] ;
132
155
@@ -236,6 +259,21 @@ impl<'a> AsmParser<'a> {
236
259
}
237
260
}
238
261
262
+ // Are we dealing with a label? These are placeholders that will be replaced with an
263
+ // address at compile time. For now we'll use dummy values and keep track of the
264
+ // used labels for reference later.
265
+ if substring. starts_with ( ':' ) {
266
+ self . labeled_instructions . insert ( i, substring. to_string ( ) ) ;
267
+
268
+ // We want to insert a dummy 32-bit address argument for now.
269
+ arguments. push ( Argument :: UnsignedInt ( DUMMY_LABEL_JUMP_ADDRESS ) ) ;
270
+
271
+ // We also want to use an argument hint that will correspond with
272
+ // the value we're substituting. Since we're working with a 32-bit
273
+ // virtual machine the address size will always be a u32 integer.
274
+ hints. push ( ArgTypeHint :: U32Pointer ) ;
275
+ }
276
+
239
277
argument_hints. push ( hints) ;
240
278
}
241
279
@@ -278,7 +316,7 @@ impl<'a> AsmParser<'a> {
278
316
instructions. push ( ins) ;
279
317
}
280
318
281
- instructions
319
+ self . parsed_instructions = instructions;
282
320
}
283
321
284
322
/// Try to parse an expression.
@@ -796,7 +834,6 @@ impl<'a> AsmParser<'a> {
796
834
pub fn parse_instruction_line ( line : & str ) -> Vec < String > {
797
835
let mut segments = Vec :: with_capacity ( 5 ) ;
798
836
799
- let mut skip_to_end = false ;
800
837
let mut segment_end = false ;
801
838
let mut start_pos = 0 ;
802
839
let mut end_pos = 0 ;
@@ -805,13 +842,9 @@ impl<'a> AsmParser<'a> {
805
842
while let Some ( c) = chars. next ( ) {
806
843
// What type of character are we dealing with?
807
844
match c {
808
- ' ' | ',' => {
845
+ ' ' | ',' | ';' => {
809
846
segment_end = true ;
810
847
}
811
- ';' => {
812
- segment_end = true ;
813
- skip_to_end = true ;
814
- }
815
848
_ => { }
816
849
}
817
850
@@ -835,7 +868,7 @@ impl<'a> AsmParser<'a> {
835
868
segment_end = false ;
836
869
}
837
870
838
- if skip_to_end {
871
+ if c == ';' {
839
872
break ;
840
873
}
841
874
@@ -865,6 +898,8 @@ mod tests_asm_parsing {
865
898
866
899
use crate :: asm_parser:: AsmParser ;
867
900
901
+ use super :: DUMMY_LABEL_JUMP_ADDRESS ;
902
+
868
903
#[ derive( Clone ) ]
869
904
struct ParserTest {
870
905
/// The input string to be tested.
@@ -911,10 +946,13 @@ mod tests_asm_parsing {
911
946
///
912
947
/// * `id` - The ID of this test.
913
948
pub fn run_test ( & self , id : usize ) {
914
- let parser = AsmParser :: new ( ) ;
915
-
916
949
// Attempt to execute the code.
917
- let result = panic:: catch_unwind ( || parser. parse_code ( & self . input ) ) ;
950
+ let result = panic:: catch_unwind ( || {
951
+ let mut parser = AsmParser :: new ( ) ;
952
+ parser. parse_code ( & self . input ) ;
953
+
954
+ parser
955
+ } ) ;
918
956
919
957
// Confirm whether the test panicked, and whether that panic was expected or not.
920
958
let did_panic = result. is_err ( ) ;
@@ -927,7 +965,10 @@ mod tests_asm_parsing {
927
965
928
966
// We don't have a viable list to interrogate here.
929
967
if !did_panic {
930
- assert_eq ! ( result. unwrap( ) , self . expected_instructions) ;
968
+ assert_eq ! (
969
+ result. unwrap( ) . parsed_instructions,
970
+ self . expected_instructions
971
+ ) ;
931
972
}
932
973
}
933
974
@@ -964,6 +1005,29 @@ mod tests_asm_parsing {
964
1005
}
965
1006
}
966
1007
1008
+ #[ test]
1009
+ fn code_labels ( ) {
1010
+ let tests = [
1011
+ ParserTest :: new (
1012
+ "nop\r \n :LABEL_1" ,
1013
+ & [
1014
+ Instruction :: Nop ,
1015
+ Instruction :: Label ( String :: from ( ":LABEL_1" ) ) ,
1016
+ ] ,
1017
+ false ,
1018
+ "failed to correctly parse label instruction." ,
1019
+ ) ,
1020
+ ParserTest :: new (
1021
+ "call :LABEL_1" ,
1022
+ & [ Instruction :: CallU32Imm ( DUMMY_LABEL_JUMP_ADDRESS as u32 ) ] ,
1023
+ false ,
1024
+ "failed to correctly parse instruction label." ,
1025
+ ) ,
1026
+ ] ;
1027
+
1028
+ ParserTests :: new ( & tests) . run_all ( ) ;
1029
+ }
1030
+
967
1031
#[ test]
968
1032
fn code_parser_comments ( ) {
969
1033
let tests = [
@@ -1144,8 +1208,6 @@ mod tests_asm_parsing {
1144
1208
reg:: registers:: RegisterId :: * ,
1145
1209
} ;
1146
1210
1147
- let asm_parser = AsmParser :: new ( ) ;
1148
-
1149
1211
let expr = Expression :: try_from ( & [ Immediate ( 0x8 ) , Operator ( Add ) , Immediate ( 0x8 ) ] [ ..] )
1150
1212
. expect ( "" )
1151
1213
. pack ( ) ;
@@ -1250,9 +1312,13 @@ mod tests_asm_parsing {
1250
1312
let mut failed = false ;
1251
1313
for ( i, ins) in instructions. iter ( ) . enumerate ( ) {
1252
1314
let ins_str = ins. to_string ( ) ;
1253
- let parsed = panic:: catch_unwind ( || asm_parser. parse_code ( & ins_str) ) ;
1254
- if let Ok ( p) = parsed {
1255
- let result = p. first ( ) . expect ( "" ) ;
1315
+ let parser = panic:: catch_unwind ( || {
1316
+ let mut asm_parser = AsmParser :: new ( ) ;
1317
+ asm_parser. parse_code ( & ins_str) ;
1318
+ asm_parser
1319
+ } ) ;
1320
+ if let Ok ( p) = parser {
1321
+ let result = p. parsed_instructions . first ( ) . expect ( "" ) ;
1256
1322
if result != ins {
1257
1323
println ! ( "Test {i} failed! Original = {ins:?}, actual = {result:?}." ) ;
1258
1324
failed = true ;
0 commit comments