|
|
|
@ -5,224 +5,205 @@ 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, parse_u64, parse_label_str}; |
|
|
|
|
use crate::asm::parse::parse_data::{parse_constant_name, parse_label, parse_rd, parse_reg_alias, parse_value, 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 { |
|
|
|
|
_internal: () |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl BuiltinOps { |
|
|
|
|
pub fn new() -> Box<dyn CrsnExtension> { |
|
|
|
|
Box::new(Self { |
|
|
|
|
_internal: () |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
pub(crate) fn parse_op<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> { |
|
|
|
|
let pcx = args.pcx; |
|
|
|
|
|
|
|
|
|
impl CrsnExtension for BuiltinOps { |
|
|
|
|
fn name(&self) -> &'static str { |
|
|
|
|
"builtin" |
|
|
|
|
} |
|
|
|
|
Ok(ParseRes::Parsed(OpKind::BuiltIn(match keyword { |
|
|
|
|
"nop" => { |
|
|
|
|
BuiltinOp::Nop |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn parse_op<'a>(&self, keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> { |
|
|
|
|
let pcx = args.pcx; |
|
|
|
|
"halt" => { |
|
|
|
|
BuiltinOp::Halt |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(ParseRes::Parsed(OpKind::BuiltIn(match keyword { |
|
|
|
|
"nop" => { |
|
|
|
|
BuiltinOp::Nop |
|
|
|
|
"sleep" => { |
|
|
|
|
BuiltinOp::Sleep { |
|
|
|
|
micros: args.next_rd()?, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"halt" => { |
|
|
|
|
BuiltinOp::Halt |
|
|
|
|
} |
|
|
|
|
"sym" => { |
|
|
|
|
let alias = parse_reg_alias(args.next())?; |
|
|
|
|
trace!("alias={:?}", alias); |
|
|
|
|
let register = parse_reg(&args.next_string()?)?; |
|
|
|
|
trace!("register={:?}", alias); |
|
|
|
|
|
|
|
|
|
"sleep" => { |
|
|
|
|
BuiltinOp::Sleep { |
|
|
|
|
micros: args.next_rd()?, |
|
|
|
|
} |
|
|
|
|
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())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"sym" => { |
|
|
|
|
let alias = parse_reg_alias(args.next())?; |
|
|
|
|
trace!("alias={:?}", alias); |
|
|
|
|
let register = parse_reg(&args.next_string()?)?; |
|
|
|
|
trace!("register={:?}", alias); |
|
|
|
|
if pstate.constants.contains_key(&alias) { |
|
|
|
|
return Err(CrsnError::Parse(format!("Name \"{}\" already used for a constant!", alias).into())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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())); |
|
|
|
|
} |
|
|
|
|
if pstate.reg_aliases.iter().find(|x| x.1 == ®ister).is_some() { |
|
|
|
|
return Err(CrsnError::Parse(format!("Register \"{}\" already aliased!", register).into())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if pstate.constants.contains_key(&alias) { |
|
|
|
|
return Err(CrsnError::Parse(format!("Name \"{}\" already used for a constant!", alias).into())); |
|
|
|
|
} |
|
|
|
|
pstate.reg_aliases.insert(alias, register); |
|
|
|
|
|
|
|
|
|
if pstate.reg_aliases.iter().find(|x| x.1 == ®ister).is_some() { |
|
|
|
|
return Err(CrsnError::Parse(format!("Register \"{}\" already aliased!", register).into())); |
|
|
|
|
} |
|
|
|
|
return Ok(ParseRes::ParsedNone); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pstate.reg_aliases.insert(alias, register); |
|
|
|
|
"unsym" => { |
|
|
|
|
let alias = parse_reg_alias(args.next())?; |
|
|
|
|
|
|
|
|
|
return Ok(ParseRes::ParsedNone); |
|
|
|
|
let mut pstate = pcx.state.borrow_mut(); |
|
|
|
|
if pstate.reg_aliases.remove(&alias).is_none() { |
|
|
|
|
return Err(CrsnError::Parse(format!("Register alias \"{}\" not defined!", alias).into())); |
|
|
|
|
} |
|
|
|
|
return Ok(ParseRes::ParsedNone); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"unsym" => { |
|
|
|
|
let alias = parse_reg_alias(args.next())?; |
|
|
|
|
"def" => { |
|
|
|
|
let name = parse_constant_name(args.next())?; |
|
|
|
|
let value = parse_value(args.next(), pcx)?; |
|
|
|
|
|
|
|
|
|
let mut pstate = pcx.state.borrow_mut(); |
|
|
|
|
if pstate.reg_aliases.remove(&alias).is_none() { |
|
|
|
|
return Err(CrsnError::Parse(format!("Register alias \"{}\" not defined!", alias).into())); |
|
|
|
|
} |
|
|
|
|
return Ok(ParseRes::ParsedNone); |
|
|
|
|
let mut pstate = pcx.state.borrow_mut(); |
|
|
|
|
if pstate.constants.contains_key(&name) { |
|
|
|
|
return Err(CrsnError::Parse(format!("Constant \"{}\" already defined!", name).into())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"def" => { |
|
|
|
|
let name = parse_constant_name(args.next())?; |
|
|
|
|
let value = parse_value(args.next(), pcx)?; |
|
|
|
|
|
|
|
|
|
let mut pstate = pcx.state.borrow_mut(); |
|
|
|
|
if pstate.constants.contains_key(&name) { |
|
|
|
|
return Err(CrsnError::Parse(format!("Constant \"{}\" already defined!", name).into())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if pstate.reg_aliases.contains_key(&name) { |
|
|
|
|
return Err(CrsnError::Parse(format!("Name \"{}\" already used for a register alias!", name).into())); |
|
|
|
|
} |
|
|
|
|
if pstate.reg_aliases.contains_key(&name) { |
|
|
|
|
return Err(CrsnError::Parse(format!("Name \"{}\" already used for a register alias!", name).into())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pstate.constants.insert(name, value); |
|
|
|
|
pstate.constants.insert(name, value); |
|
|
|
|
|
|
|
|
|
return Ok(ParseRes::ParsedNone); |
|
|
|
|
} |
|
|
|
|
return Ok(ParseRes::ParsedNone); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"undef" => { |
|
|
|
|
let name = parse_constant_name(args.next())?; |
|
|
|
|
"undef" => { |
|
|
|
|
let name = parse_constant_name(args.next())?; |
|
|
|
|
|
|
|
|
|
let mut pstate = pcx.state.borrow_mut(); |
|
|
|
|
if pstate.constants.remove(&name).is_none() { |
|
|
|
|
return Err(CrsnError::Parse(format!("Constant \"{}\" not defined!", name).into())); |
|
|
|
|
} |
|
|
|
|
return Ok(ParseRes::ParsedNone); |
|
|
|
|
let mut pstate = pcx.state.borrow_mut(); |
|
|
|
|
if pstate.constants.remove(&name).is_none() { |
|
|
|
|
return Err(CrsnError::Parse(format!("Constant \"{}\" not defined!", name).into())); |
|
|
|
|
} |
|
|
|
|
return Ok(ParseRes::ParsedNone); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"j" => { |
|
|
|
|
let dest = parse_label(args.next())?; |
|
|
|
|
BuiltinOp::Jump(dest) |
|
|
|
|
} |
|
|
|
|
"j" => { |
|
|
|
|
let dest = parse_label(args.next())?; |
|
|
|
|
BuiltinOp::Jump(dest) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"fj" => { |
|
|
|
|
let dest = parse_label(args.next())?; |
|
|
|
|
BuiltinOp::FarJump(dest) |
|
|
|
|
} |
|
|
|
|
"fj" => { |
|
|
|
|
let dest = parse_label(args.next())?; |
|
|
|
|
BuiltinOp::FarJump(dest) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"call" => { |
|
|
|
|
let dest = RoutineName { name: args.next_string()?, arity: args.len() as u8 }; |
|
|
|
|
"call" => { |
|
|
|
|
let dest = RoutineName { name: args.next_string()?, arity: args.len() as u8 }; |
|
|
|
|
|
|
|
|
|
let mut call_args = vec![]; |
|
|
|
|
for t in args { |
|
|
|
|
call_args.push(parse_rd(Some(t), pcx)?); |
|
|
|
|
} |
|
|
|
|
BuiltinOp::Call(dest, call_args) |
|
|
|
|
let mut call_args = vec![]; |
|
|
|
|
for t in args { |
|
|
|
|
call_args.push(parse_rd(Some(t), pcx)?); |
|
|
|
|
} |
|
|
|
|
BuiltinOp::Call(dest, call_args) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"ret" => { |
|
|
|
|
let mut ret_vals = vec![]; |
|
|
|
|
for t in args { |
|
|
|
|
ret_vals.push(parse_rd(Some(t), pcx)?); |
|
|
|
|
} |
|
|
|
|
BuiltinOp::Ret(ret_vals) |
|
|
|
|
"ret" => { |
|
|
|
|
let mut ret_vals = vec![]; |
|
|
|
|
for t in args { |
|
|
|
|
ret_vals.push(parse_rd(Some(t), pcx)?); |
|
|
|
|
} |
|
|
|
|
BuiltinOp::Ret(ret_vals) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"routine" => { |
|
|
|
|
let name = args.next_string()?; |
|
|
|
|
BuiltinOp::Routine(parse_routine_name(name)?) |
|
|
|
|
} |
|
|
|
|
"routine" => { |
|
|
|
|
let name = args.next_string()?; |
|
|
|
|
BuiltinOp::Routine(parse_routine_name(name)?) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"skip" => { |
|
|
|
|
BuiltinOp::Skip(args.next_rd()?) |
|
|
|
|
} |
|
|
|
|
"skip" => { |
|
|
|
|
BuiltinOp::Skip(args.next_rd()?) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"barrier" => { |
|
|
|
|
BuiltinOp::Barrier { |
|
|
|
|
kind: Barrier::Standalone, |
|
|
|
|
msg: match args.next() { |
|
|
|
|
None => None, |
|
|
|
|
Some(s) => Some(expect_string_atom(Some(s))?.into()), |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
"barrier" => { |
|
|
|
|
BuiltinOp::Barrier { |
|
|
|
|
kind: Barrier::Standalone, |
|
|
|
|
msg: match args.next() { |
|
|
|
|
None => None, |
|
|
|
|
Some(s) => Some(expect_string_atom(Some(s))?.into()), |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"barrier-open" => { |
|
|
|
|
BuiltinOp::Barrier { |
|
|
|
|
kind: Barrier::Open(parse_label(args.next())?), |
|
|
|
|
msg: None, |
|
|
|
|
} |
|
|
|
|
"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, |
|
|
|
|
} |
|
|
|
|
"barrier-close" => { |
|
|
|
|
BuiltinOp::Barrier { |
|
|
|
|
kind: Barrier::Close(parse_label(args.next())?), |
|
|
|
|
msg: None, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"fault" => { |
|
|
|
|
BuiltinOp::Fault(match args.next() { |
|
|
|
|
None => None, |
|
|
|
|
Some(s) => Some(expect_string_atom(Some(s))?.into()), |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
"fault" => { |
|
|
|
|
BuiltinOp::Fault(match args.next() { |
|
|
|
|
None => None, |
|
|
|
|
Some(s) => Some(expect_string_atom(Some(s))?.into()), |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"ld" => { |
|
|
|
|
BuiltinOp::Move { |
|
|
|
|
dst: args.next_wr()?, |
|
|
|
|
src: args.next_rd()?, |
|
|
|
|
} |
|
|
|
|
"ld" => { |
|
|
|
|
BuiltinOp::Move { |
|
|
|
|
dst: args.next_wr()?, |
|
|
|
|
src: args.next_rd()?, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"sst" => { |
|
|
|
|
BuiltinOp::StoreStatus { |
|
|
|
|
dst: args.next_wr()?, |
|
|
|
|
} |
|
|
|
|
"sst" => { |
|
|
|
|
BuiltinOp::StoreStatus { |
|
|
|
|
dst: args.next_wr()?, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"sld" => { |
|
|
|
|
BuiltinOp::LoadStatus { |
|
|
|
|
src: args.next_rd()?, |
|
|
|
|
} |
|
|
|
|
"sld" => { |
|
|
|
|
BuiltinOp::LoadStatus { |
|
|
|
|
src: args.next_rd()?, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"drop" => { |
|
|
|
|
BuiltinOp::Drop(args.next_rdobj()?) |
|
|
|
|
} |
|
|
|
|
"drop" => { |
|
|
|
|
BuiltinOp::Drop(args.next_rdobj()?) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
"far" => { |
|
|
|
|
if let Some(Sexp::Atom(Atom::S(ref label))) = args.peek() { |
|
|
|
|
if let Some(label) = label.strip_prefix(':') { |
|
|
|
|
let label = Label::Named(label.to_string()); |
|
|
|
|
BuiltinOp::FarLabel(label) |
|
|
|
|
} else { |
|
|
|
|
return Ok(ParseRes::Unknown(args)); |
|
|
|
|
} |
|
|
|
|
"far" => { |
|
|
|
|
if let Some(Sexp::Atom(Atom::S(ref label))) = args.peek() { |
|
|
|
|
if let Some(label) = label.strip_prefix(':') { |
|
|
|
|
let label = Label::Named(label.to_string()); |
|
|
|
|
BuiltinOp::FarLabel(label) |
|
|
|
|
} else { |
|
|
|
|
return Ok(ParseRes::Unknown(args)); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
return Ok(ParseRes::Unknown(args)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
other => { |
|
|
|
|
if let Some(label) = other.strip_prefix(':') { |
|
|
|
|
BuiltinOp::Label(parse_label_str(label)?) |
|
|
|
|
} else { |
|
|
|
|
return Ok(ParseRes::Unknown(args)); |
|
|
|
|
} |
|
|
|
|
other => { |
|
|
|
|
if let Some(label) = other.strip_prefix(':') { |
|
|
|
|
BuiltinOp::Label(parse_label_str(label)?) |
|
|
|
|
} else { |
|
|
|
|
return Ok(ParseRes::Unknown(args)); |
|
|
|
|
} |
|
|
|
|
}))) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}))) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub(crate) fn parse_routine_name(name: String) -> Result<RoutineName, CrsnError> { |
|
|
|
@ -304,16 +285,16 @@ pub(crate) fn to_sexp(op: &BuiltinOp) -> Sexp { |
|
|
|
|
|
|
|
|
|
#[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::instr::{Flatten}; |
|
|
|
|
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; |
|
|
|
|
use crate::builtin::BuiltinOps; |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn roundtrip() { |
|
|
|
@ -374,7 +355,7 @@ mod test { |
|
|
|
|
let parsers = &[parser]; |
|
|
|
|
|
|
|
|
|
for (sample, expected) in samples { |
|
|
|
|
let mut pcx = ParserContext { |
|
|
|
|
let pcx = ParserContext { |
|
|
|
|
parsers, |
|
|
|
|
state: Default::default(), |
|
|
|
|
}; |
|
|
|
@ -386,7 +367,7 @@ mod test { |
|
|
|
|
.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) |
|
|
|
|
let parsed = parse_instructions(list.into_iter(), &pcx) |
|
|
|
|
.expect("parse instr").flatten(&num) |
|
|
|
|
.expect("flatten").remove(0); |
|
|
|
|
|
|
|
|
@ -402,7 +383,7 @@ mod test { |
|
|
|
|
.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) |
|
|
|
|
let parsed = parse_instructions(list.into_iter(), &pcx) |
|
|
|
|
.expect("parse instr (2c)").flatten(&num) |
|
|
|
|
.expect("flatten (2c)").remove(0); |
|
|
|
|
|
|
|
|
|