use crate::{
asm_error, asm_info,
symbols::LITERAL_TYPE_PREFIX,
tokens::{LabelOffset, Token, TokenVariant},
utils::IterVec,
};
pub fn expand_mults(tokens: &Vec<Token>) -> Vec<Token> {
let mut tokens = IterVec::new(tokens);
let mut new_tokens: Vec<Token> = Vec::with_capacity(tokens.len());
while !tokens.finished() {
if tokens.contains_n_more(1)
&& let TokenVariant::Asterisk = tokens.get_relative(1).variant
{
if let TokenVariant::DecLiteral { value: count } = &tokens.get_relative(2).variant {
for _ in 0..*count {
new_tokens.push(tokens.current().clone());
}
tokens.consume_n(3); continue;
}
}
new_tokens.push(tokens.current().clone());
tokens.consume();
}
new_tokens
}
pub fn fix_instructions_and_collapse_label_definitions(tokens: &[Token]) -> Vec<Token> {
let mut new_tokens: Vec<Token> = Vec::with_capacity(tokens.len());
let mut i = 0;
while i < tokens.len() {
if let TokenVariant::Linebreak = tokens[i].variant {
i += 1;
continue;
}
if i + 1 < tokens.len()
&& let TokenVariant::LabelArrow { offset } = &tokens[i + 1].variant
{
let label_offset = match offset {
LabelOffset::Char(x) => match x {
'a' => 0,
'b' => 1,
'c' => 2,
_ => unreachable!(),
},
LabelOffset::Int(x) => *x,
};
let name = match &tokens[i].variant {
TokenVariant::Label { name } => name,
_ => asm_error!(&tokens[i].info, "Only a label may precede a label arrow"),
};
new_tokens.push(Token::with_info(
TokenVariant::LabelDefinition {
name: name.clone(),
offset: label_offset,
},
&tokens[i + 1],
));
i += 2;
continue;
}
if i + 1 < tokens.len()
&& let TokenVariant::Subleq = &tokens[i + 1].variant
{
if i + 3 < tokens.len()
&& let TokenVariant::Linebreak = &tokens[i + 3].variant
{
let mut updated_info = tokens[i + 2].info.clone();
updated_info.start_char += updated_info.length + 1;
updated_info.length = 2;
updated_info.sourceline_suffix = Some("$1".to_string());
new_tokens.push(tokens[i + 2].clone());
new_tokens.push(tokens[i].clone());
new_tokens.push(Token {
info: updated_info,
variant: TokenVariant::Relative { offset: 1 },
origin_info: tokens[i + 3].origin_info.clone(), });
i += 4;
continue;
}
if i + 4 < tokens.len() {
new_tokens.push(tokens[i + 2].clone());
new_tokens.push(tokens[i].clone());
new_tokens.push(tokens[i + 3].clone());
if let TokenVariant::Label { name } = &tokens[i + 3].variant {
let mut split_name = name.split('?');
if !split_name.next_back().unwrap().starts_with('.') {
asm_info!(
&tokens[i + 3].info,
"Labels which are jump targets should be prefixed with a '.'"
);
}
}
i += 5;
continue;
}
}
new_tokens.push(tokens[i].clone());
i += 1;
}
new_tokens
}
pub fn insert_asm_macro(macro_name: String, origin_tok: &Token, args: Vec<&Token>) -> Vec<Token> {
let mut toks: Vec<Token> = Vec::new();
toks.push(Token::with_info(
TokenVariant::MacroCall { name: macro_name },
origin_tok,
));
for arg in args {
toks.push(arg.clone());
}
toks
}
pub fn handle_assignments(tokens: &[Token]) -> Vec<Token> {
let mut new_tokens: Vec<Token> = Vec::with_capacity(tokens.len());
let mut i = 0;
while i < tokens.len() {
if i + 2 < tokens.len()
&& let TokenVariant::Equals = &tokens[i + 1].variant
{
let label_tok = if let TokenVariant::Label { .. } = &tokens[i].variant {
&tokens[i]
} else {
asm_error!(
&tokens[i].info,
"The left hand side of an assignment may only be a label"
);
};
let target_tok = &tokens[i + 2];
match &target_tok.variant {
TokenVariant::DecLiteral { value } if *value == 0 => {
let mut toks = insert_asm_macro(
"ASM::AssignZero".to_string(),
&tokens[i + 1],
vec![label_tok],
);
new_tokens.append(&mut toks);
}
TokenVariant::DecLiteral { .. } => {
let mut toks = insert_asm_macro(
"ASM::AssignLit".to_string(),
&tokens[i + 1],
vec![label_tok, target_tok],
);
new_tokens.append(&mut toks);
}
TokenVariant::Label { name } => {
let mut toks = if name.starts_with(LITERAL_TYPE_PREFIX) {
insert_asm_macro(
"ASM::AssignLit".to_string(),
&tokens[i + 1],
vec![label_tok, target_tok],
)
} else {
insert_asm_macro(
"ASM::AssignLabel".to_string(),
&tokens[i + 1],
vec![label_tok, target_tok],
)
};
new_tokens.append(&mut toks);
}
_ => asm_error!(
&tokens[i + 2].info,
"The right hand side of an assignment may only be a label or a literal"
),
}
i += 3;
continue;
}
new_tokens.push(tokens[i].clone());
i += 1;
}
new_tokens
}
#[cfg(test)]
mod tests {
use crate::tokens::{self, tokens_from_token_variant_vec};
use super::*;
#[test]
fn test_assignment() {
let input: Vec<Token> = tokens_from_token_variant_vec(vec![
(
0,
TokenVariant::Label {
name: "label".to_string(),
},
),
(0, TokenVariant::Equals),
(0, TokenVariant::DecLiteral { value: 0 }),
(0, TokenVariant::Linebreak),
(
0,
TokenVariant::Label {
name: "label2".to_string(),
},
),
(0, TokenVariant::Equals),
(0, TokenVariant::DecLiteral { value: 120 }),
(
0,
TokenVariant::Label {
name: "label_a".to_string(),
},
),
(0, TokenVariant::Equals),
(
0,
TokenVariant::Label {
name: "label_b".to_string(),
},
),
(
0,
TokenVariant::Label {
name: "label_c".to_string(),
},
),
(0, TokenVariant::Equals),
(
0,
TokenVariant::Label {
name: "l_lit".to_string(),
},
),
]);
let expected: Vec<Token> = tokens_from_token_variant_vec(vec![
(
0,
TokenVariant::MacroCall {
name: "ASM::AssignZero".to_string(),
},
),
(
0,
TokenVariant::Label {
name: "label".to_string(),
},
),
(0, TokenVariant::Linebreak),
(
0,
TokenVariant::MacroCall {
name: "ASM::AssignLit".to_string(),
},
),
(
0,
TokenVariant::Label {
name: "label2".to_string(),
},
),
(0, TokenVariant::DecLiteral { value: 120 }),
(
0,
TokenVariant::MacroCall {
name: "ASM::AssignLabel".to_string(),
},
),
(
0,
TokenVariant::Label {
name: "label_a".to_string(),
},
),
(
0,
TokenVariant::Label {
name: "label_b".to_string(),
},
),
(
0,
TokenVariant::MacroCall {
name: "ASM::AssignLit".to_string(),
},
),
(
0,
TokenVariant::Label {
name: "label_c".to_string(),
},
),
(
0,
TokenVariant::Label {
name: "l_lit".to_string(),
},
),
]);
let output = handle_assignments(&input);
assert_eq!(output, expected);
}
#[test]
fn test_mult() {
let mut input: Vec<Token> = tokens_from_token_variant_vec(vec![
(0, TokenVariant::Asterisk),
(
1,
TokenVariant::Label {
name: "label".to_owned(),
},
),
(2, TokenVariant::DecLiteral { value: 10 }),
(3, TokenVariant::Asterisk),
(4, TokenVariant::DecLiteral { value: 5 }),
]);
let expected: Vec<Token> = tokens_from_token_variant_vec(vec![
(0, TokenVariant::Asterisk),
(
1,
TokenVariant::Label {
name: "label".to_owned(),
},
),
(2, TokenVariant::DecLiteral { value: 10 }),
(2, TokenVariant::DecLiteral { value: 10 }),
(2, TokenVariant::DecLiteral { value: 10 }),
(2, TokenVariant::DecLiteral { value: 10 }),
(2, TokenVariant::DecLiteral { value: 10 }),
]);
let output = expand_mults(&mut input);
assert_eq!(output, expected);
}
#[test]
fn test_fix_instructions_and_collapse_label_definitions() {
let mut input: Vec<Token> = tokens_from_token_variant_vec(vec![
(
0,
TokenVariant::Label {
name: "label".to_owned(),
},
),
(
1,
TokenVariant::LabelArrow {
offset: tokens::LabelOffset::Int(0),
},
),
(
2,
TokenVariant::Label {
name: "a".to_owned(),
},
),
(3, TokenVariant::Subleq),
(
4,
TokenVariant::Label {
name: "b".to_owned(),
},
),
(5, TokenVariant::Linebreak),
(
6,
TokenVariant::Label {
name: "b".to_owned(),
},
),
(
7,
TokenVariant::Label {
name: "a".to_owned(),
},
),
(
8,
TokenVariant::Label {
name: "c".to_owned(),
},
),
(9, TokenVariant::Linebreak),
]);
let expected: Vec<Token> = tokens_from_token_variant_vec(vec![
(
1,
TokenVariant::LabelDefinition {
name: "label".to_owned(),
offset: 0,
},
),
(
4,
TokenVariant::Label {
name: "b".to_owned(),
},
),
(
2,
TokenVariant::Label {
name: "a".to_owned(),
},
),
(5, TokenVariant::Relative { offset: 1 }),
(
6,
TokenVariant::Label {
name: "b".to_owned(),
},
),
(
7,
TokenVariant::Label {
name: "a".to_owned(),
},
),
(
8,
TokenVariant::Label {
name: "c".to_owned(),
},
),
]);
let output = fix_instructions_and_collapse_label_definitions(&mut input);
assert_eq!(output, expected);
}
}