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

403 lines
14 KiB

use sexp::{Atom, Sexp, SourcePosition};
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};
use crate::module::ParseRes;
use crate::utils::A;
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
}
"sleep" => {
BuiltinOp::Sleep {
micros: args.next_rd()?,
}
}
"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));
}
if pstate.constants.contains_key(&alias) {
return Err(CrsnError::Parse(format!("Name \"{}\" already used for a constant!", alias).into(), aliaspos));
}
if pstate.reg_aliases.iter().find(|x| x.1 == &register).is_some() {
return Err(CrsnError::Parse(format!("Register \"{}\" already aliased!", register).into(), rpos));
}
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() {
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 = 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));
}
if pstate.reg_aliases.contains_key(&name) {
return Err(CrsnError::Parse(format!("Name \"{}\" already used for a 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)?)
}
"skip" => {
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::Move {
dst: args.next_wr()?,
src: args.next_rd()?,
}
}
"sst" => {
BuiltinOp::StoreStatus {
dst: args.next_wr()?,
}
}
"sld" => {
BuiltinOp::LoadStatus {
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 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 { 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::Delete(obj) => sexp::list(&[A("del"), 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::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)"),
("(sleep 1000)", "(sleep 1000)"),
("(sleep 1_0_0_0)", "(sleep 1000)"),
("(sleep ' ')", "(sleep 32)"),
("(sleep '\\n')", "(sleep 10)"),
("(sleep 0b111)", "(sleep 7)"),
("(sleep 0xab_cd)", "(sleep 43981)"),
("(sleep #ab_cd)", "(sleep 43981)"),
("(:x)", "(:x)"),
("(j :x)", "(j :x)"),
("(:#7)", "(:#7)"),
("(j :#7)", "(j :#7)"),
("(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)"),
("(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: {}", exported);
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);
}
}
}