Skip to content

Commit d4fdc07

Browse files
committed
Add basic support for labels in jump instructions. The compiler hasn't been updated to take them into account yet.
1 parent 8eba8be commit d4fdc07

File tree

2 files changed

+90
-24
lines changed

2 files changed

+90
-24
lines changed

redox-parser/src/asm_parser.rs

+86-20
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{num::ParseIntError, str::FromStr};
1+
use std::{collections::HashMap, num::ParseIntError, str::FromStr};
22

33
use itertools::Itertools;
44
use redox_core::{
@@ -40,6 +40,9 @@ const U32_REGISTERS: [RegisterId; 18] = [
4040
RegisterId::EDS,
4141
];
4242

43+
/// A dummy label jump address used when handling a label.
44+
const DUMMY_LABEL_JUMP_ADDRESS: u128 = u32::MAX as u128;
45+
4346
#[derive(Debug, Clone)]
4447
pub enum Argument {
4548
/// A f64 argument.
@@ -88,17 +91,29 @@ macro_rules! get_inner_expr_arg {
8891
}
8992

9093
pub struct AsmParser<'a> {
94+
/// The instruction hints for the parser.
9195
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>,
92102
}
93103

94104
impl<'a> AsmParser<'a> {
95105
pub fn new() -> Self {
96106
Self {
97107
hints: InstructionHints::new(),
108+
parsed_instructions: vec![],
109+
labeled_instructions: HashMap::new(),
98110
}
99111
}
100112

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+
102117
let mut instructions: Vec<Instruction> = vec![];
103118

104119
// Split the string into lines.
@@ -110,6 +125,14 @@ impl<'a> AsmParser<'a> {
110125

111126
let raw_args = AsmParser::parse_instruction_line(line.trim_matches(' '));
112127

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+
113136
// The name should always be the first argument we've extracted, the arguments should follow.
114137
let name = raw_args[0].to_lowercase();
115138

@@ -126,7 +149,7 @@ impl<'a> AsmParser<'a> {
126149
.collect();
127150

128151
// 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() {
130153
let mut has_value_pushed = false;
131154
let mut hints = vec![];
132155

@@ -236,6 +259,21 @@ impl<'a> AsmParser<'a> {
236259
}
237260
}
238261

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+
239277
argument_hints.push(hints);
240278
}
241279

@@ -278,7 +316,7 @@ impl<'a> AsmParser<'a> {
278316
instructions.push(ins);
279317
}
280318

281-
instructions
319+
self.parsed_instructions = instructions;
282320
}
283321

284322
/// Try to parse an expression.
@@ -796,7 +834,6 @@ impl<'a> AsmParser<'a> {
796834
pub fn parse_instruction_line(line: &str) -> Vec<String> {
797835
let mut segments = Vec::with_capacity(5);
798836

799-
let mut skip_to_end = false;
800837
let mut segment_end = false;
801838
let mut start_pos = 0;
802839
let mut end_pos = 0;
@@ -805,13 +842,9 @@ impl<'a> AsmParser<'a> {
805842
while let Some(c) = chars.next() {
806843
// What type of character are we dealing with?
807844
match c {
808-
' ' | ',' => {
845+
' ' | ',' | ';' => {
809846
segment_end = true;
810847
}
811-
';' => {
812-
segment_end = true;
813-
skip_to_end = true;
814-
}
815848
_ => {}
816849
}
817850

@@ -835,7 +868,7 @@ impl<'a> AsmParser<'a> {
835868
segment_end = false;
836869
}
837870

838-
if skip_to_end {
871+
if c == ';' {
839872
break;
840873
}
841874

@@ -865,6 +898,8 @@ mod tests_asm_parsing {
865898

866899
use crate::asm_parser::AsmParser;
867900

901+
use super::DUMMY_LABEL_JUMP_ADDRESS;
902+
868903
#[derive(Clone)]
869904
struct ParserTest {
870905
/// The input string to be tested.
@@ -911,10 +946,13 @@ mod tests_asm_parsing {
911946
///
912947
/// * `id` - The ID of this test.
913948
pub fn run_test(&self, id: usize) {
914-
let parser = AsmParser::new();
915-
916949
// 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+
});
918956

919957
// Confirm whether the test panicked, and whether that panic was expected or not.
920958
let did_panic = result.is_err();
@@ -927,7 +965,10 @@ mod tests_asm_parsing {
927965

928966
// We don't have a viable list to interrogate here.
929967
if !did_panic {
930-
assert_eq!(result.unwrap(), self.expected_instructions);
968+
assert_eq!(
969+
result.unwrap().parsed_instructions,
970+
self.expected_instructions
971+
);
931972
}
932973
}
933974

@@ -964,6 +1005,29 @@ mod tests_asm_parsing {
9641005
}
9651006
}
9661007

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+
9671031
#[test]
9681032
fn code_parser_comments() {
9691033
let tests = [
@@ -1144,8 +1208,6 @@ mod tests_asm_parsing {
11441208
reg::registers::RegisterId::*,
11451209
};
11461210

1147-
let asm_parser = AsmParser::new();
1148-
11491211
let expr = Expression::try_from(&[Immediate(0x8), Operator(Add), Immediate(0x8)][..])
11501212
.expect("")
11511213
.pack();
@@ -1250,9 +1312,13 @@ mod tests_asm_parsing {
12501312
let mut failed = false;
12511313
for (i, ins) in instructions.iter().enumerate() {
12521314
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("");
12561322
if result != ins {
12571323
println!("Test {i} failed! Original = {ins:?}, actual = {result:?}.");
12581324
failed = true;

redox-terminal/src/main.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,12 @@ fn main() {
6767
panic!("currently unsupported");
6868
}
6969

70-
let code = "nop ; test comment";
70+
let code = "call :LABEL";
7171

72-
let parser = AsmParser::new();
73-
let parsed = parser.parse_code(code);
72+
let mut parser = AsmParser::new();
73+
parser.parse_code(code);
7474

75-
println!("parsed = {parsed:?}");
75+
println!("parsed = {:?}", parser.parsed_instructions);
7676
return;
7777

7878
let instructions = &[

0 commit comments

Comments
 (0)