use std::collections::BTreeMap;
use telltale_types::LocalTypeR;
use crate::instr::{Instr, InvokeAction, PC};
#[must_use]
pub fn compile(local_type: &LocalTypeR) -> Vec<Instr> {
let mut instrs = Vec::new();
let mut loop_targets: BTreeMap<String, PC> = BTreeMap::new();
compile_inner(local_type, &mut instrs, &mut loop_targets);
instrs
}
fn compile_inner(
lt: &LocalTypeR,
instrs: &mut Vec<Instr>,
loop_targets: &mut BTreeMap<String, PC>,
) {
match lt {
LocalTypeR::Send { branches, .. } => {
if branches.len() == 1 {
if let Some((_, _vt, cont)) = branches.first() {
instrs.push(Instr::Send { chan: 0, val: 1 });
instrs.push(Instr::Invoke {
action: InvokeAction::Named("runtime.step".to_string()),
});
compile_inner(cont, instrs, loop_targets);
}
} else if branches.len() > 1 {
compile_send_choice_branches(branches, instrs, loop_targets);
}
}
LocalTypeR::Recv { branches, .. } => {
if branches.len() == 1 {
if let Some((_, _vt, cont)) = branches.first() {
instrs.push(Instr::Receive { chan: 0, dst: 1 });
instrs.push(Instr::Invoke {
action: InvokeAction::Named("runtime.step".to_string()),
});
compile_inner(cont, instrs, loop_targets);
}
} else if branches.len() > 1 {
compile_recv_choice_branches(branches, instrs, loop_targets);
}
}
LocalTypeR::Mu { var, body } => {
let target = instrs.len();
loop_targets.insert(var.clone(), target);
compile_inner(body, instrs, loop_targets);
}
LocalTypeR::Var(name) => {
if let Some(&target) = loop_targets.get(name) {
instrs.push(Instr::Jump { target });
} else {
instrs.push(Instr::Halt);
}
}
LocalTypeR::End => {
instrs.push(Instr::Halt);
}
}
}
fn compile_send_choice_branches(
branches: &[(
telltale_types::Label,
Option<telltale_types::ValType>,
LocalTypeR,
)],
instrs: &mut Vec<Instr>,
loop_targets: &mut BTreeMap<String, PC>,
) {
if let Some((label, _vt, cont)) = branches.first() {
instrs.push(Instr::Offer {
chan: 0,
label: label.name.clone(),
});
compile_inner(cont, instrs, loop_targets);
}
}
fn compile_recv_choice_branches(
branches: &[(
telltale_types::Label,
Option<telltale_types::ValType>,
LocalTypeR,
)],
instrs: &mut Vec<Instr>,
loop_targets: &mut BTreeMap<String, PC>,
) {
let placeholder_idx = instrs.len();
instrs.push(Instr::Choose {
chan: 0,
table: vec![],
});
let mut table = Vec::with_capacity(branches.len());
for (label, _vt, cont) in branches {
let start_pc = instrs.len();
table.push((label.name.clone(), start_pc));
compile_inner(cont, instrs, loop_targets);
}
instrs[placeholder_idx] = Instr::Choose { chan: 0, table };
}
#[cfg(test)]
mod tests {
use super::*;
use telltale_types::Label;
#[test]
fn test_compile_send_end() {
let lt = LocalTypeR::Send {
partner: "B".into(),
branches: vec![(Label::new("msg"), None, LocalTypeR::End)],
};
let code = compile(<);
assert_eq!(
code,
vec![
Instr::Send { chan: 0, val: 1 },
Instr::Invoke {
action: InvokeAction::Named("runtime.step".to_string()),
},
Instr::Halt,
]
);
}
#[test]
fn test_compile_recursive() {
let lt = LocalTypeR::mu(
"step",
LocalTypeR::Send {
partner: "B".into(),
branches: vec![(Label::new("msg"), None, LocalTypeR::var("step"))],
},
);
let code = compile(<);
assert_eq!(
code,
vec![
Instr::Send { chan: 0, val: 1 },
Instr::Invoke {
action: InvokeAction::Named("runtime.step".to_string()),
},
Instr::Jump { target: 0 },
]
);
}
#[test]
fn test_compile_send_recv_loop() {
let lt = LocalTypeR::mu(
"step",
LocalTypeR::Send {
partner: "B".into(),
branches: vec![(
Label::new("pos"),
None,
LocalTypeR::Recv {
partner: "B".into(),
branches: vec![(Label::new("pos"), None, LocalTypeR::var("step"))],
},
)],
},
);
let code = compile(<);
assert_eq!(
code,
vec![
Instr::Send { chan: 0, val: 1 },
Instr::Invoke {
action: InvokeAction::Named("runtime.step".to_string()),
},
Instr::Receive { chan: 0, dst: 1 },
Instr::Invoke {
action: InvokeAction::Named("runtime.step".to_string()),
},
Instr::Jump { target: 0 },
]
);
}
}