Add %rep syntax for repeating a block

Same syntax as NASM.
This commit is contained in:
Daniel Lubarov 2022-07-14 14:55:17 -07:00
parent 8751aaec7a
commit 6d69e14a89
5 changed files with 49 additions and 12 deletions

View File

@ -9,18 +9,21 @@
%endmacro %endmacro
%macro pop2 %macro pop2
pop %rep 2
pop pop
%endrep
%endmacro %endmacro
%macro pop3 %macro pop3
pop %rep 3
%pop2 pop
%endrep
%endmacro %endmacro
%macro pop4 %macro pop4
%pop2 %rep 4
%pop2 pop
%endrep
%endmacro %endmacro
// If pred is zero, yields z; otherwise, yields nz // If pred is zero, yields z; otherwise, yields nz

View File

@ -44,6 +44,7 @@ pub(crate) fn assemble(files: Vec<File>, constants: HashMap<String, U256>) -> Ke
let mut local_labels = Vec::with_capacity(files.len()); let mut local_labels = Vec::with_capacity(files.len());
for file in files { for file in files {
let expanded_file = expand_macros(file.body, &macros); let expanded_file = expand_macros(file.body, &macros);
let expanded_file = expand_repeats(expanded_file);
let expanded_file = inline_constants(expanded_file, &constants); let expanded_file = inline_constants(expanded_file, &constants);
local_labels.push(find_labels(&expanded_file, &mut offset, &mut global_labels)); local_labels.push(find_labels(&expanded_file, &mut offset, &mut global_labels));
expanded_files.push(expanded_file); expanded_files.push(expanded_file);
@ -132,6 +133,21 @@ fn expand_macro_call(
expand_macros(expanded_item, macros) expand_macros(expanded_item, macros)
} }
fn expand_repeats(body: Vec<Item>) -> Vec<Item> {
let mut expanded = vec![];
for item in body {
if let Item::Repeat(count, block) = item {
let reps = count.to_u256().as_usize();
for _ in 0..reps {
expanded.extend(block.clone());
}
} else {
expanded.push(item);
}
}
expanded
}
fn inline_constants(body: Vec<Item>, constants: &HashMap<String, U256>) -> Vec<Item> { fn inline_constants(body: Vec<Item>, constants: &HashMap<String, U256>) -> Vec<Item> {
body.into_iter() body.into_iter()
.map(|item| { .map(|item| {
@ -157,8 +173,8 @@ fn find_labels(
let mut local_labels = HashMap::<String, usize>::new(); let mut local_labels = HashMap::<String, usize>::new();
for item in body { for item in body {
match item { match item {
Item::MacroDef(_, _, _) | Item::MacroCall(_, _) => { Item::MacroDef(_, _, _) | Item::MacroCall(_, _) | Item::Repeat(_, _) => {
panic!("Macros should have been expanded already") panic!("Macros and repeats should have been expanded already")
} }
Item::GlobalLabelDeclaration(label) => { Item::GlobalLabelDeclaration(label) => {
let old = global_labels.insert(label.clone(), *offset); let old = global_labels.insert(label.clone(), *offset);
@ -185,8 +201,8 @@ fn assemble_file(
// Assemble the file. // Assemble the file.
for item in body { for item in body {
match item { match item {
Item::MacroDef(_, _, _) | Item::MacroCall(_, _) => { Item::MacroDef(_, _, _) | Item::MacroCall(_, _) | Item::Repeat(_, _) => {
panic!("Macros should have been expanded already") panic!("Macros and repeats should have been expanded already")
} }
Item::GlobalLabelDeclaration(_) | Item::LocalLabelDeclaration(_) => { Item::GlobalLabelDeclaration(_) | Item::LocalLabelDeclaration(_) => {
// Nothing to do; we processed labels in the prior phase. // Nothing to do; we processed labels in the prior phase.
@ -393,6 +409,13 @@ mod tests {
assert_eq!(kernel.code, vec![push4, 0xDE, 0xAD, 0xBE, 0xEF]); assert_eq!(kernel.code, vec![push4, 0xDE, 0xAD, 0xBE, 0xEF]);
} }
#[test]
fn repeat() {
let kernel = parse_and_assemble(&["%rep 3 ADD %endrep"]);
let add = get_opcode("ADD");
assert_eq!(kernel.code, vec![add, add, add]);
}
fn parse_and_assemble(files: &[&str]) -> Kernel { fn parse_and_assemble(files: &[&str]) -> Kernel {
parse_and_assemble_with_constants(files, HashMap::new()) parse_and_assemble_with_constants(files, HashMap::new())
} }

View File

@ -12,6 +12,8 @@ pub(crate) enum Item {
MacroDef(String, Vec<String>, Vec<Item>), MacroDef(String, Vec<String>, Vec<Item>),
/// Calls a macro: name, args. /// Calls a macro: name, args.
MacroCall(String, Vec<PushTarget>), MacroCall(String, Vec<PushTarget>),
/// Repetition, like `%rep` in NASM.
Repeat(Literal, Vec<Item>),
/// Declares a global label. /// Declares a global label.
GlobalLabelDeclaration(String), GlobalLabelDeclaration(String),
/// Declares a label that is local to the current file. /// Declares a label that is local to the current file.

View File

@ -15,9 +15,10 @@ literal = { literal_hex | literal_decimal }
variable = ${ "$" ~ identifier } variable = ${ "$" ~ identifier }
constant = ${ "@" ~ identifier } constant = ${ "@" ~ identifier }
item = { macro_def | macro_call | global_label | local_label | bytes_item | push_instruction | nullary_instruction } item = { macro_def | macro_call | repeat | global_label | local_label | bytes_item | push_instruction | nullary_instruction }
macro_def = { ^"%macro" ~ identifier ~ macro_paramlist? ~ item* ~ ^"%endmacro" } macro_def = { ^"%macro" ~ identifier ~ macro_paramlist? ~ item* ~ ^"%endmacro" }
macro_call = ${ "%" ~ !(^"macro" | ^"endmacro") ~ identifier ~ macro_arglist? } macro_call = ${ "%" ~ !(^"macro" | ^"endmacro" | ^"rep" | ^"endrep") ~ identifier ~ macro_arglist? }
repeat = { ^"%rep" ~ literal ~ item* ~ ^"%endrep" }
macro_paramlist = { "(" ~ identifier ~ ("," ~ identifier)* ~ ")" } macro_paramlist = { "(" ~ identifier ~ ("," ~ identifier)* ~ ")" }
macro_arglist = !{ "(" ~ push_target ~ ("," ~ push_target)* ~ ")" } macro_arglist = !{ "(" ~ push_target ~ ("," ~ push_target)* ~ ")" }
global_label = { ^"GLOBAL " ~ identifier ~ ":" } global_label = { ^"GLOBAL " ~ identifier ~ ":" }

View File

@ -23,6 +23,7 @@ fn parse_item(item: Pair<Rule>) -> Item {
match item.as_rule() { match item.as_rule() {
Rule::macro_def => parse_macro_def(item), Rule::macro_def => parse_macro_def(item),
Rule::macro_call => parse_macro_call(item), Rule::macro_call => parse_macro_call(item),
Rule::repeat => parse_repeat(item),
Rule::global_label => { Rule::global_label => {
Item::GlobalLabelDeclaration(item.into_inner().next().unwrap().as_str().into()) Item::GlobalLabelDeclaration(item.into_inner().next().unwrap().as_str().into())
} }
@ -70,6 +71,13 @@ fn parse_macro_call(item: Pair<Rule>) -> Item {
Item::MacroCall(name, args) Item::MacroCall(name, args)
} }
fn parse_repeat(item: Pair<Rule>) -> Item {
assert_eq!(item.as_rule(), Rule::repeat);
let mut inner = item.into_inner().peekable();
let count = parse_literal(inner.next().unwrap());
Item::Repeat(count, inner.map(parse_item).collect())
}
fn parse_push_target(target: Pair<Rule>) -> PushTarget { fn parse_push_target(target: Pair<Rule>) -> PushTarget {
assert_eq!(target.as_rule(), Rule::push_target); assert_eq!(target.as_rule(), Rule::push_target);
let inner = target.into_inner().next().unwrap(); let inner = target.into_inner().next().unwrap();