You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
542 lines
20 KiB
542 lines
20 KiB
use sexp::{Atom, Sexp, SourcePosition, atom_qs};
|
|
|
|
use crate::asm::data::literal::{RoutineName};
|
|
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_label_str, parse_rd, parse_reg_alias, parse_value};
|
|
use crate::asm::parse::sexp_expect::{expect_any_string_atom};
|
|
use crate::asm::patches::ErrWithPos;
|
|
use crate::builtin::defs::{Barrier, BuiltinOp, SleepUnit, LdsValue};
|
|
use crate::module::ParseRes;
|
|
use crate::utils::{A, AM};
|
|
use crate::asm::data::{Rd, RdData, RdObj, DataDispEquals};
|
|
|
|
pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
|
|
let pcx = args.pcx;
|
|
|
|
Ok(ParseRes::Parsed(OpKind::BuiltIn(match keyword {
|
|
"nop" => {
|
|
BuiltinOp::Nop
|
|
}
|
|
|
|
"halt" => {
|
|
BuiltinOp::Halt
|
|
}
|
|
|
|
"uslp" => {
|
|
BuiltinOp::Sleep {
|
|
count: args.next_rd()?,
|
|
unit_us: SleepUnit::Usec,
|
|
}
|
|
}
|
|
|
|
"mslp" => {
|
|
BuiltinOp::Sleep {
|
|
count: args.next_rd()?,
|
|
unit_us: SleepUnit::Msec,
|
|
}
|
|
}
|
|
|
|
"sslp" => {
|
|
BuiltinOp::Sleep {
|
|
count: args.next_rd()?,
|
|
unit_us: SleepUnit::Sec,
|
|
}
|
|
}
|
|
|
|
"sym" => {
|
|
let (alias, aliaspos) = parse_reg_alias(args.next_or_err()?)?;
|
|
trace!("alias={:?}", alias);
|
|
let (rn, rpos) = args.next_string()?;
|
|
let register = parse_reg(&rn, &rpos)?;
|
|
trace!("register={:?}", alias);
|
|
|
|
let mut pstate = pcx.state.borrow_mut();
|
|
if pstate.reg_aliases.contains_key(&alias) {
|
|
return Err(CrsnError::Parse(format!("Register alias \"{}\" already defined!", alias).into(), rpos));
|
|
} else if pstate.global_reg_aliases.contains_key(&alias) {
|
|
return Err(CrsnError::Parse(format!("Register alias \"{}\" already defined (global)!", alias).into(), rpos));
|
|
} else if pstate.constants.contains_key(&alias) {
|
|
return Err(CrsnError::Parse(format!("Name \"{}\" already used for a constant!", alias).into(), aliaspos));
|
|
} else if let Some((name, _)) = pstate.reg_aliases.iter().find(|x| x.1 == ®ister) {
|
|
return Err(CrsnError::Parse(format!("Register \"{}\" already aliased as \"{}\"!", register, name).into(), rpos));
|
|
} else if let Some((name, _)) = pstate.global_reg_aliases.iter().find(|x| x.1 == ®ister) {
|
|
return Err(CrsnError::Parse(format!("Register \"{}\" already aliased (globally) as \"{}\"!", register, name).into(), rpos));
|
|
}
|
|
|
|
if register.is_global() {
|
|
pstate.global_reg_aliases.insert(alias, register);
|
|
} else {
|
|
pstate.reg_aliases.insert(alias, register);
|
|
}
|
|
|
|
return Ok(ParseRes::ParsedNone);
|
|
}
|
|
|
|
"unsym" => {
|
|
let alias = parse_reg_alias(args.next_or_err()?)?;
|
|
|
|
let mut pstate = pcx.state.borrow_mut();
|
|
if pstate.reg_aliases.remove(&alias.0).is_none() {
|
|
if pstate.global_reg_aliases.remove(&alias.0).is_none() {
|
|
return Err(CrsnError::Parse(format!("Register alias \"{}\" not defined!", alias.0).into(), alias.1));
|
|
}
|
|
}
|
|
return Ok(ParseRes::ParsedNone);
|
|
}
|
|
|
|
"def" => {
|
|
let (name, namepos) = parse_constant_name(args.next_or_err()?)?;
|
|
let (value, _valuepos) = parse_value(args.next_or_err()?, pcx)?;
|
|
|
|
let mut pstate = pcx.state.borrow_mut();
|
|
if pstate.constants.contains_key(&name) {
|
|
return Err(CrsnError::Parse(format!("Constant \"{}\" already defined!", name).into(), namepos));
|
|
} else if pstate.reg_aliases.contains_key(&name) {
|
|
return Err(CrsnError::Parse(format!("Name \"{}\" already used for a register alias!", name).into(), namepos));
|
|
} else if pstate.global_reg_aliases.contains_key(&name) {
|
|
return Err(CrsnError::Parse(format!("Name \"{}\" already used for a global register alias!", name).into(), namepos));
|
|
}
|
|
|
|
pstate.constants.insert(name, value);
|
|
|
|
return Ok(ParseRes::ParsedNone);
|
|
}
|
|
|
|
"undef" => {
|
|
let (name, namepos) = parse_constant_name(args.next_or_err()?)?;
|
|
|
|
let mut pstate = pcx.state.borrow_mut();
|
|
if pstate.constants.remove(&name).is_none() {
|
|
return Err(CrsnError::Parse(format!("Constant \"{}\" not defined!", name).into(), namepos));
|
|
}
|
|
return Ok(ParseRes::ParsedNone);
|
|
}
|
|
|
|
"j" => {
|
|
let dest = parse_label(args.next_or_err()?)?;
|
|
BuiltinOp::Jump(dest)
|
|
}
|
|
|
|
"fj" => {
|
|
let dest = parse_label(args.next_or_err()?)?;
|
|
BuiltinOp::FarJump(dest)
|
|
}
|
|
|
|
"call" => {
|
|
let dest = RoutineName { name: args.next_string()?.0, arity: args.len() as u8 };
|
|
|
|
let mut call_args = vec![];
|
|
for t in args {
|
|
call_args.push(parse_rd(t, pcx)?);
|
|
}
|
|
BuiltinOp::Call(dest, call_args)
|
|
}
|
|
|
|
"ret" => {
|
|
let mut ret_vals = vec![];
|
|
for t in args {
|
|
ret_vals.push(parse_rd(t, pcx)?);
|
|
}
|
|
BuiltinOp::Ret(ret_vals)
|
|
}
|
|
|
|
"routine" => {
|
|
let name = args.next_string()?;
|
|
BuiltinOp::Routine(parse_routine_name(name.0, &name.1)?)
|
|
}
|
|
|
|
"s" => {
|
|
BuiltinOp::Skip(args.next_rd()?)
|
|
}
|
|
|
|
"barrier" => {
|
|
BuiltinOp::Barrier {
|
|
kind: Barrier::Standalone,
|
|
msg: match args.next() {
|
|
None => None,
|
|
Some(s) => Some(expect_any_string_atom(s)?.0.into()),
|
|
},
|
|
}
|
|
}
|
|
|
|
"barrier-open" => {
|
|
BuiltinOp::Barrier {
|
|
kind: Barrier::Open(parse_label(args.next_or_err()?)?),
|
|
msg: None,
|
|
}
|
|
}
|
|
|
|
"barrier-close" => {
|
|
BuiltinOp::Barrier {
|
|
kind: Barrier::Close(parse_label(args.next_or_err()?)?),
|
|
msg: None,
|
|
}
|
|
}
|
|
|
|
"fault" => {
|
|
BuiltinOp::Fault(match args.next() {
|
|
None => None,
|
|
Some(s) => Some(expect_any_string_atom(s)?.0.into()),
|
|
})
|
|
}
|
|
|
|
"ld" => {
|
|
BuiltinOp::Load {
|
|
dst: args.next_wr()?,
|
|
src: args.next_rd()?,
|
|
}
|
|
}
|
|
|
|
"ldn" => {
|
|
BuiltinOp::LoadMultiple {
|
|
dst: args.next_wr()?,
|
|
src: args.next_rd()?,
|
|
count: args.next_rd()?,
|
|
}
|
|
}
|
|
|
|
"lds" => {
|
|
let dst = args.next_wr()?;
|
|
|
|
let value = match args.next_or_err()? {
|
|
Sexp::Atom(Atom::QS(s), _) => {
|
|
LdsValue::Chars(s)
|
|
}
|
|
tok @ Sexp::Atom(Atom::S(_), _) => {
|
|
let pos = tok.pos().clone();
|
|
match parse_rd(tok, args.pcx)? {
|
|
Rd(RdData::ImmObject(v)) => {
|
|
LdsValue::Handle(RdObj::Imm(v))
|
|
}
|
|
Rd(RdData::RegObject(r)) => {
|
|
LdsValue::Handle(RdObj::Reg(r))
|
|
}
|
|
_ => {
|
|
return Err(CrsnError::Parse(
|
|
"Expected a handle, quoted string, or a tuple of values".into(),
|
|
pos));
|
|
}
|
|
}
|
|
}
|
|
Sexp::List(list, _) => {
|
|
let mut vals = vec![];
|
|
for v in list {
|
|
vals.push(parse_rd(v, args.pcx)?);
|
|
}
|
|
LdsValue::Values(vals)
|
|
}
|
|
other => {
|
|
return Err(CrsnError::Parse(
|
|
"Expected a handle, quoted string, or a tuple of values".into(),
|
|
other.pos().clone()));
|
|
}
|
|
};
|
|
|
|
BuiltinOp::LoadSequence { dst, value }
|
|
}
|
|
|
|
// "xch" => {
|
|
// BuiltinOp::Exchange {
|
|
// a: args.next_rdwr()?,
|
|
// b: args.next_rdwr()?,
|
|
// mask: BitMask::full(),
|
|
// }
|
|
// }
|
|
|
|
"stf" => {
|
|
BuiltinOp::StoreFlags {
|
|
dst: args.next_wr()?,
|
|
}
|
|
}
|
|
|
|
"ldf" => {
|
|
BuiltinOp::LoadFlags {
|
|
src: args.next_rd()?,
|
|
}
|
|
}
|
|
|
|
"del" => {
|
|
BuiltinOp::Delete(args.next_rdobj()?)
|
|
}
|
|
|
|
"far" => {
|
|
if let Some(Sexp::Atom(Atom::S(ref label), labelpos)) = args.peek() {
|
|
if label.starts_with(':') {
|
|
let label = parse_label_str(label, labelpos)?;
|
|
BuiltinOp::FarLabel(label)
|
|
} else {
|
|
return Ok(ParseRes::Unknown(args));
|
|
}
|
|
} else {
|
|
return Ok(ParseRes::Unknown(args));
|
|
}
|
|
}
|
|
|
|
other => {
|
|
if let Some((dst, a, b, mask)) = args.parse_masked_wr_rd_rd(other, "ld")? {
|
|
if !dst.disp_equals(a) && mask.dst_pos != 0 {
|
|
return Err(CrsnError::Parse(
|
|
"Invalid offsets - permissible formats are (RW:dst_offs Rd:src_offs) and (Wr Rd:dst_offs, Rd:src_offs) ".into(),
|
|
op_pos.clone()));
|
|
}
|
|
return Ok(ParseRes::builtin(BuiltinOp::LoadBits { dst, a, b, mask }));
|
|
}
|
|
|
|
if let Some((a, b, mask)) = args.parse_masked_rdwr_rdwr(other, "xch")? {
|
|
return Ok(ParseRes::builtin(BuiltinOp::Exchange { a, b, mask }));
|
|
}
|
|
|
|
if other.starts_with(':') {
|
|
BuiltinOp::Label(parse_label_str(other, &op_pos)?)
|
|
} else {
|
|
return Ok(ParseRes::Unknown(args));
|
|
}
|
|
}
|
|
})))
|
|
}
|
|
|
|
pub(crate) fn parse_routine_name(name: String, pos: &SourcePosition) -> Result<RoutineName, CrsnError> {
|
|
let (name, arity) = if let Some(n) = name.find('/') {
|
|
(
|
|
(&name[0..n]).to_string(),
|
|
(&name[(n + 1)..]).parse::<u8>().err_pos(pos)?
|
|
)
|
|
} else {
|
|
(name, 0)
|
|
};
|
|
|
|
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 { count: micros, unit_us: SleepUnit::Sec } => sexp::list(&[A("sslp"), A(micros)]),
|
|
BuiltinOp::Sleep { count: micros, unit_us: SleepUnit::Msec } => sexp::list(&[A("mslp"), A(micros)]),
|
|
BuiltinOp::Sleep { count: micros, unit_us: SleepUnit::Usec } => sexp::list(&[A("uslp"), 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("s"), 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::Delete(obj) => sexp::list(&[A("del"), A(obj)]),
|
|
BuiltinOp::Load { dst, src, } => {
|
|
sexp::list(&[A("ld"), A(dst), A(src)])
|
|
},
|
|
BuiltinOp::LoadBits { dst, a, b, mask, } => {
|
|
if dst.disp_equals(a) {
|
|
sexp::list(&[A(format!("ld{}", mask.width)), AM(dst, mask.dst_pos), AM(b, mask.src2_pos)])
|
|
} else {
|
|
sexp::list(&[A(format!("ld{}", mask.width)), AM(dst, mask.dst_pos), AM(a, mask.src_pos), AM(b, mask.src2_pos)])
|
|
}
|
|
},
|
|
BuiltinOp::LoadMultiple { dst, src, count } => sexp::list(&[A("ldn"), A(dst), A(src), A(count)]),
|
|
BuiltinOp::Exchange { a, b, mask } => {
|
|
if mask.is_full() {
|
|
sexp::list(&[A("xch"), A(a), A(b)])
|
|
} else {
|
|
sexp::list(&[A(format!("xch{}", mask.width)), AM(a, mask.dst_pos), AM(b, mask.src_pos)])
|
|
}
|
|
},
|
|
BuiltinOp::StoreFlags { dst } => sexp::list(&[A("stf"), A(dst)]),
|
|
BuiltinOp::LoadFlags { src } => sexp::list(&[A("ldf"), A(src)]),
|
|
BuiltinOp::LoadSequence { dst, value } => {
|
|
match value {
|
|
LdsValue::Values(vals) => {
|
|
let vals: Vec<_> = vals.iter().map(A).collect();
|
|
sexp::list(&[A("lds"), A(dst), sexp::list(&vals)])
|
|
}
|
|
LdsValue::Chars(str) => {
|
|
sexp::list(&[A("lds"), A(dst), atom_qs(str)])
|
|
}
|
|
LdsValue::Handle(h) => {
|
|
sexp::list(&[A("lds"), A(dst), A(h)])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use std::sync::atomic::AtomicU32;
|
|
|
|
use sexp::SourcePosition;
|
|
|
|
use crate::asm::parse::{parse_instructions, ParserContext};
|
|
use crate::asm::parse::sexp_expect::expect_list;
|
|
use crate::builtin::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)"),
|
|
("(uslp 1000)", "(uslp 1000)"),
|
|
("(uslp 1_0_0_0)", "(uslp 1000)"),
|
|
("(uslp ' ')", "(uslp 32)"),
|
|
("(uslp '\\n')", "(uslp 10)"),
|
|
("(uslp 0b111)", "(uslp 7)"),
|
|
("(uslp 0xab_cd)", "(uslp 43981)"),
|
|
("(uslp #ab_cd)", "(uslp 43981)"),
|
|
("(mslp 1000)", "(mslp 1000)"),
|
|
("(sslp 1000)", "(sslp 1000)"),
|
|
("(:x)", "(:x)"),
|
|
("(j :x)", "(j :x)"),
|
|
("(:#7)", "(:#7)"),
|
|
("(j :#7)", "(j :#7)"),
|
|
("(fj :x)", "(fj :x)"),
|
|
("(s 0)", "(s 0)"),
|
|
("(s r0)", "(s r0)"),
|
|
("(sym banana r0)(unsym banana)(sym banana r1)(s banana)", "(s r1)"),
|
|
("(def foo 123)(s foo)", "(s 123)"),
|
|
("(def foo 123)(undef foo)(def foo 444)(s foo)", "(s 444)"),
|
|
("(def foo -777)(def bar foo)(s bar)", "(s -777)"),
|
|
("(s -10)", "(s -10)"),
|
|
("(s -10000)", "(s -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\")"),
|
|
("(xch r0 r1)", "(xch r0 r1)"),
|
|
("(xch32 r0 r1)", "(xch32 r0 r1)"),
|
|
("(xch32 r0:8 r1:16)", "(xch32 r0:8 r1:16)"),
|
|
("(ld r0 r0)", "(ld r0 r0)"),
|
|
("(ld8 r0 r1)", "(ld8 r0 r1)"),
|
|
("(ld16 r0 r1)", "(ld16 r0 r1)"),
|
|
("(ld32 r0 r1)", "(ld32 r0 r1)"),
|
|
("(ld32 r0:32 r1)", "(ld32 r0:32 r1)"),
|
|
("(ld32 r0:32 r1:5)", "(ld32 r0:32 r1:5)"),
|
|
("(ld32 r0 r1:16)", "(ld32 r0 r1:16)"),
|
|
("(ld32 r7 r0:3 r1:16)", "(ld32 r7 r0:3 r1:16)"),
|
|
("(ld r0 156)", "(ld r0 156)"),
|
|
("(ld _ -32767)", "(ld _ -32767)"),
|
|
("(ldn _ @r0 7)", "(ldn _ @r0 7)"),
|
|
("(lds @r0 \"ahoj jak se mas\")", "(lds @r0 \"ahoj jak se mas\")"),
|
|
("(lds @r0 (1 2 3 4 0x123 r6))", "(lds @r0 (1 2 3 4 291 r6))"),
|
|
("(stf r0)", "(stf r0)"),
|
|
("(ldf r0)", "(ldf r0)"),
|
|
("(far :label)", "(far :label)"),
|
|
("(del @r5)", "(del @r5)"),
|
|
("(sym cat r0)(del @cat)", "(del @r0)"),
|
|
];
|
|
|
|
let parser = BuiltinOps::new();
|
|
|
|
let parsers = &[parser];
|
|
|
|
for (sample, expected) in samples {
|
|
let pcx = ParserContext {
|
|
parsers,
|
|
state: Default::default(),
|
|
};
|
|
|
|
println!("Parse: {}", sample);
|
|
|
|
/* first cycle */
|
|
let s = sexp::parse(&format!("({})", sample))
|
|
.expect("parse sexp");
|
|
let list = expect_list(s, false).unwrap();
|
|
let num = AtomicU32::new(0);
|
|
let parsed = parse_instructions(list.0.into_iter(), &SourcePosition::default(), &pcx)
|
|
.expect("parse instr").flatten(&num)
|
|
.expect("flatten").remove(0);
|
|
|
|
let exported = parsed.to_sexp().to_string();
|
|
println!("Parsed: {}, dbg {:?}", exported, parsed);
|
|
|
|
assert_eq!(expected, exported);
|
|
|
|
println!(" - 2nd cycle");
|
|
let pcx = ParserContext {
|
|
parsers,
|
|
state: Default::default(),
|
|
};
|
|
|
|
/* second cycle, nothing should change */
|
|
let s = sexp::parse(&format!("({})", exported))
|
|
.expect("parse sexp (2c)");
|
|
let list = expect_list(s, false).unwrap();
|
|
let num = AtomicU32::new(0);
|
|
let parsed = parse_instructions(list.0.into_iter(), &SourcePosition::default(), &pcx)
|
|
.expect("parse instr (2c)").flatten(&num)
|
|
.expect("flatten (2c)").remove(0);
|
|
|
|
let exported2 = parsed.to_sexp().to_string();
|
|
assert_eq!(expected, exported2);
|
|
}
|
|
}
|
|
}
|
|
|