Croissant Runtime
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.
 
 
crsn/crsn/src/builtin/parse.rs

555 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 == &register) {
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 == &register) {
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 let Some((dst, expected, src, mask)) = args.parse_masked_rdwr_rd_rd(other, "cas")? {
return Ok(ParseRes::builtin(BuiltinOp::CompareSwap { dst, expected, src, 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::CompareSwap { dst, expected, src, mask } => {
if mask.is_full() {
sexp::list(&[A("cas"), A(dst), A(expected), A(src)])
} else {
sexp::list(&[A(format!("cas{}", mask.width)), AM(dst, mask.dst_pos), AM(expected, mask.src_pos), AM(src, mask.src2_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)"),
("(cas r0 r1 r2)", "(cas r0 r1 r2)"),
("(cas8 r0:8 r1:16 r2:24)", "(cas8 r0:8 r1:16 r2:24)"),
("(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);
}
}
}