|
|
|
@ -5,10 +5,12 @@ use crate::asm::data::reg::parse_reg; |
|
|
|
|
use crate::asm::error::CrsnError; |
|
|
|
|
use crate::asm::instr::op::OpKind; |
|
|
|
|
use crate::asm::parse::arg_parser::TokenParser; |
|
|
|
|
use crate::asm::parse::parse_data::{parse_constant_name, parse_label, parse_rd, parse_reg_alias, parse_value}; |
|
|
|
|
use crate::asm::parse::parse_data::{parse_constant_name, parse_label, parse_rd, parse_reg_alias, parse_value, parse_u64, parse_label_str}; |
|
|
|
|
use crate::asm::parse::sexp_expect::expect_string_atom; |
|
|
|
|
use crate::builtin::defs::{Barrier, BuiltinOp}; |
|
|
|
|
use crate::module::{CrsnExtension, ParseRes}; |
|
|
|
|
use crate::utils::A; |
|
|
|
|
use std::convert::TryFrom; |
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)] |
|
|
|
|
pub struct BuiltinOps { |
|
|
|
@ -82,7 +84,7 @@ impl CrsnExtension for BuiltinOps { |
|
|
|
|
|
|
|
|
|
"def" => { |
|
|
|
|
let name = parse_constant_name(args.next())?; |
|
|
|
|
let value = parse_value(args.next())?; |
|
|
|
|
let value = parse_value(args.next(), pcx)?; |
|
|
|
|
|
|
|
|
|
let mut pstate = pcx.state.borrow_mut(); |
|
|
|
|
if pstate.constants.contains_key(&name) { |
|
|
|
@ -155,6 +157,20 @@ impl CrsnExtension for BuiltinOps { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"barrier-open" => { |
|
|
|
|
BuiltinOp::Barrier { |
|
|
|
|
kind: Barrier::Open(parse_label(args.next())?), |
|
|
|
|
msg: None, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"barrier-close" => { |
|
|
|
|
BuiltinOp::Barrier { |
|
|
|
|
kind: Barrier::Close(parse_label(args.next())?), |
|
|
|
|
msg: None, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"fault" => { |
|
|
|
|
BuiltinOp::Fault(match args.next() { |
|
|
|
|
None => None, |
|
|
|
@ -200,8 +216,7 @@ impl CrsnExtension for BuiltinOps { |
|
|
|
|
|
|
|
|
|
other => { |
|
|
|
|
if let Some(label) = other.strip_prefix(':') { |
|
|
|
|
let label = Label::Named(label.to_string()); |
|
|
|
|
BuiltinOp::Label(label) |
|
|
|
|
BuiltinOp::Label(parse_label_str(label)?) |
|
|
|
|
} else { |
|
|
|
|
return Ok(ParseRes::Unknown(args)); |
|
|
|
|
} |
|
|
|
@ -222,3 +237,177 @@ pub(crate) fn parse_routine_name(name: String) -> Result<RoutineName, CrsnError> |
|
|
|
|
|
|
|
|
|
Ok(RoutineName { name, arity }) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp { |
|
|
|
|
match op { |
|
|
|
|
BuiltinOp::Nop => sexp::list(&[A("nop")]), |
|
|
|
|
BuiltinOp::Halt => sexp::list(&[A("halt")]), |
|
|
|
|
BuiltinOp::Sleep { micros } => sexp::list(&[A("sleep"), A(micros)]), |
|
|
|
|
BuiltinOp::Label(label) => sexp::list(&[A(label)]), |
|
|
|
|
BuiltinOp::Jump(label) => sexp::list(&[A("j"), A(label)]), |
|
|
|
|
BuiltinOp::FarLabel(label) => sexp::list(&[A("far"), A(label)]), |
|
|
|
|
BuiltinOp::FarJump(label) => sexp::list(&[A("fj"), A(label)]), |
|
|
|
|
BuiltinOp::Call(name, args) => { |
|
|
|
|
if args.is_empty() { |
|
|
|
|
sexp::list(&[A("call"), A(&name.name)]) |
|
|
|
|
} else { |
|
|
|
|
let mut inner = vec![A("call"), A(&name.name)]; |
|
|
|
|
inner.extend(args.iter().map(A)); |
|
|
|
|
sexp::list(&inner) |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
BuiltinOp::Ret(values) => { |
|
|
|
|
if values.is_empty() { |
|
|
|
|
sexp::list(&[A("ret")]) |
|
|
|
|
} else { |
|
|
|
|
let mut inner = vec![A("ret")]; |
|
|
|
|
inner.extend(values.iter().map(A)); |
|
|
|
|
sexp::list(&inner) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
BuiltinOp::Routine(name) => sexp::list(&[A("routine"), A(name)]), |
|
|
|
|
BuiltinOp::Skip(n) => sexp::list(&[A("skip"), A(n)]), |
|
|
|
|
BuiltinOp::Barrier { kind, msg } => { |
|
|
|
|
let mut inner = vec![]; |
|
|
|
|
match kind { |
|
|
|
|
Barrier::Open(label) => { |
|
|
|
|
inner.push(A("barrier-open")); |
|
|
|
|
inner.push(A(label)); |
|
|
|
|
} |
|
|
|
|
Barrier::Close(label) => { |
|
|
|
|
inner.push(A("barrier-close")); |
|
|
|
|
inner.push(A(label)); |
|
|
|
|
} |
|
|
|
|
Barrier::Standalone => { |
|
|
|
|
inner.push(A("barrier")); |
|
|
|
|
|
|
|
|
|
if let Some(msg) = msg { |
|
|
|
|
inner.push(A(msg)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
sexp::list(&inner) |
|
|
|
|
} |
|
|
|
|
BuiltinOp::Fault(msg) => { |
|
|
|
|
if let Some(msg) = msg { |
|
|
|
|
sexp::list(&[A("fault"), A(msg)]) |
|
|
|
|
} else { |
|
|
|
|
sexp::list(&[A("fault")]) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
BuiltinOp::Drop(obj) => sexp::list(&[A("drop"), A(obj)]), |
|
|
|
|
BuiltinOp::Move { dst, src } => sexp::list(&[A("ld"), A(dst), A(src)]), |
|
|
|
|
BuiltinOp::StoreStatus { dst } => sexp::list(&[A("sst"), A(dst)]), |
|
|
|
|
BuiltinOp::LoadStatus { src } => sexp::list(&[A("sld"), A(src)]) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
|
mod test { |
|
|
|
|
use std::any::Any; |
|
|
|
|
use std::cell::RefCell; |
|
|
|
|
use std::sync::atomic::AtomicU32; |
|
|
|
|
|
|
|
|
|
use crate::asm::instr::{Flatten, InstrWithBranches}; |
|
|
|
|
use crate::asm::parse::{parse_instructions, ParserContext}; |
|
|
|
|
use crate::asm::parse::sexp_expect::expect_list; |
|
|
|
|
use crate::builtin::defs::BuiltinOp; |
|
|
|
|
use crate::builtin::parse::BuiltinOps; |
|
|
|
|
use crate::module::OpTrait; |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn roundtrip() { |
|
|
|
|
let samples = vec![ |
|
|
|
|
// sym, unsym, def, undef - pseudo-instructions
|
|
|
|
|
// jump is translated to a skip
|
|
|
|
|
("(nop)", "(nop)"), |
|
|
|
|
("(halt)", "(halt)"), |
|
|
|
|
("(sleep 1000)", "(sleep 1000)"), |
|
|
|
|
("(:x)\ |
|
|
|
|
(j :x)", "(skip 0)"), |
|
|
|
|
("(:#7)\ |
|
|
|
|
(j :#7)", "(skip 0)"), |
|
|
|
|
("(fj :x)", "(fj :x)"), |
|
|
|
|
("(skip 0)", "(skip 0)"), |
|
|
|
|
("(skip r0)", "(skip r0)"), |
|
|
|
|
("(sym banana r0)(unsym banana)(sym banana r1)(skip banana)", "(skip r1)"), |
|
|
|
|
("(def foo 123)(skip foo)", "(skip 123)"), |
|
|
|
|
("(def foo 123)(undef foo)(def foo 444)(skip foo)", "(skip 444)"), |
|
|
|
|
("(def foo -777)(def bar foo)(skip bar)", "(skip -777)"), |
|
|
|
|
("(skip -10)", "(skip -10)"), |
|
|
|
|
("(skip -10000)", "(skip -10000)"), |
|
|
|
|
("(call funcname)", "(call funcname)"), |
|
|
|
|
("(call funcname 13)", "(call funcname 13)"), |
|
|
|
|
("(sym haf r0)\ |
|
|
|
|
(call štěkej haf haf)", "(call štěkej r0 r0)"), |
|
|
|
|
("(sym foo r0)\ |
|
|
|
|
(sym bar r1)\ |
|
|
|
|
(call funcname foo bar)", "(call funcname r0 r1)"), |
|
|
|
|
("(ret)", "(ret)"), |
|
|
|
|
("(ret r0)", "(ret r0)"), |
|
|
|
|
("(ret r0 r1 r2 5)", "(ret r0 r1 r2 5)"), |
|
|
|
|
("(routine ěščžřčřýřážíýáéáýúů)", "(routine ěščžřčřýřážíýáéáýúů/0)"), |
|
|
|
|
("(routine ahoj)", "(routine ahoj/0)"), |
|
|
|
|
("(routine ahoj/2)", "(routine ahoj/2)"), |
|
|
|
|
("(barrier)", "(barrier)"), |
|
|
|
|
("(barrier blablabla)", "(barrier blablabla)"), |
|
|
|
|
("(barrier \"s mezerou\")", "(barrier \"s mezerou\")"), |
|
|
|
|
("(barrier-open :xoxo)", "(barrier-open :xoxo)"), |
|
|
|
|
("(barrier-open :#10)", "(barrier-open :#10)"), |
|
|
|
|
("(barrier-close :xoxo)", "(barrier-close :xoxo)"), |
|
|
|
|
("(barrier-close :#10)", "(barrier-close :#10)"), |
|
|
|
|
("(fault)", "(fault)"), |
|
|
|
|
("(fault kur*a)", "(fault kur*a)"), |
|
|
|
|
("(fault \"do pr*ele\")", "(fault \"do pr*ele\")"), |
|
|
|
|
("(ld r0 r0)", "(ld r0 r0)"), |
|
|
|
|
("(ld r0 156)", "(ld r0 156)"), |
|
|
|
|
("(ld _ -32767)", "(ld _ -32767)"), |
|
|
|
|
("(sst r0)", "(sst r0)"), |
|
|
|
|
("(sld r0)", "(sld r0)"), |
|
|
|
|
("(far :label)", "(far :label)"), |
|
|
|
|
("(drop @r5)", "(drop @r5)"), |
|
|
|
|
("(sym cat r0)(drop @cat)", "(drop @r0)"), |
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
let parser = BuiltinOps::new(); |
|
|
|
|
|
|
|
|
|
let parsers = &[parser]; |
|
|
|
|
|
|
|
|
|
for (sample, expected) in samples { |
|
|
|
|
let mut pcx = ParserContext { |
|
|
|
|
parsers, |
|
|
|
|
state: Default::default(), |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
println!("Parse: {}", sample); |
|
|
|
|
|
|
|
|
|
/* first cycle */ |
|
|
|
|
let s = sexp::parse(&format!("({})", sample)) |
|
|
|
|
.expect("parse sexp"); |
|
|
|
|
let list = expect_list(Some(s), false).unwrap(); |
|
|
|
|
let num = AtomicU32::new(0); |
|
|
|
|
let mut parsed = parse_instructions(list.into_iter(), &pcx) |
|
|
|
|
.expect("parse instr").flatten(&num) |
|
|
|
|
.expect("flatten").remove(0); |
|
|
|
|
|
|
|
|
|
let exported = parsed.to_sexp().to_string(); |
|
|
|
|
println!("Parsed: {}", exported); |
|
|
|
|
|
|
|
|
|
assert_eq!(expected, exported); |
|
|
|
|
|
|
|
|
|
println!(" - 2nd cycle"); |
|
|
|
|
|
|
|
|
|
/* second cycle, nothing should change */ |
|
|
|
|
let s = sexp::parse(&format!("({})", exported)) |
|
|
|
|
.expect("parse sexp (2c)"); |
|
|
|
|
let list = expect_list(Some(s), false).unwrap(); |
|
|
|
|
let num = AtomicU32::new(0); |
|
|
|
|
let mut parsed = parse_instructions(list.into_iter(), &pcx) |
|
|
|
|
.expect("parse instr (2c)").flatten(&num) |
|
|
|
|
.expect("flatten (2c)").remove(0); |
|
|
|
|
|
|
|
|
|
let exported2 = parsed.to_sexp().to_string(); |
|
|
|
|
assert_eq!(expected, exported2); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|