use std::borrow::Cow; use std::convert::TryFrom; use sexp::{Atom, Sexp, SourcePosition}; use crate::asm::data::{DataDisp, Rd, RdData, reg, Wr, WrData, RdWr}; use crate::asm::data::literal::{ConstantName, Label, RegisterAlias, Value}; use crate::asm::error::CrsnError; use crate::asm::parse::{ParserContext, parse_instructions}; use crate::asm::parse::sexp_expect::expect_string_atom; use crate::asm::patches::ErrWithPos; use std::sync::atomic::AtomicU32; use crate::module::OpTrait; use crate::asm::instr::Cond; fn is_valid_identifier(name: &str) -> bool { // ascii symbols "!\"#$_&'()*+,-./:;<=>?@[\\]^_`{|}~" const BLACKLIST : &str = "!\"#$&'()*+,./;<=>?@[\\]^`{|}~"; name != "_" && !name.starts_with(|c: char| c.is_ascii_digit() || BLACKLIST.contains(c)) && !name.contains(|c : char| c.is_whitespace() || BLACKLIST.contains(c)) } /// Parse register alias pub fn parse_reg_alias(name: Sexp) -> Result<(RegisterAlias, SourcePosition), CrsnError> { // trace!("parse reg alias: {:?}", name); let (name, namepos) = expect_string_atom(name)?; if !is_valid_identifier(&name) { return Err(CrsnError::Parse(format!("\"{}\" is not an allowed register alias.", name).into(), namepos)); } Ok((name, namepos)) } /// Parse constant name pub fn parse_constant_name(name: Sexp) -> Result<(ConstantName, SourcePosition), CrsnError> { // trace!("parse const name: {:?}", name); let (name, namepos) = expect_string_atom(name)?; if !is_valid_identifier(&name) { return Err(CrsnError::Parse(format!("\"{}\" is not an allowed constant name.", name).into(), namepos)); } Ok((name, namepos)) } /// Parse a label pub fn parse_label(name: Sexp) -> Result { let (name, namepos) = expect_string_atom(name)?; Ok(parse_label_str(&name, &namepos)?) } pub fn parse_label_str(name: &str, pos: &SourcePosition) -> Result { if !name.starts_with(':') { return Err(CrsnError::Parse("Label must start with a colon".into(), pos.clone())); } let label = name.trim_start_matches(':'); Ok(if label.starts_with('#') { let val = parse_u64(&label[1..], pos)?; Label::Numbered(u32::try_from(val).err_pos(pos)?) } else { if !is_valid_identifier(&label) { return Err(CrsnError::Parse(format!("\"{}\" is not an allowed label name.", name).into(), pos.clone())); } Label::Named(label.to_string()) }) } /// Parse data disposition (address/value, without the read/write restriction) pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result { // trace!("parse data: {:?}", tok); match tok { Sexp::Atom(Atom::I(val), _pos) => { Ok(DataDisp::Immediate(unsafe { std::mem::transmute(val) })) } Sexp::Atom(Atom::F(val), _pos) => { Ok(DataDisp::Immediate(unsafe { std::mem::transmute(val) })) } Sexp::Atom(Atom::U(val), _pos) => { Ok(DataDisp::Immediate(val)) } Sexp::Atom(Atom::C(val), _pos) => { Ok(DataDisp::Immediate(val as u64)) } Sexp::Atom(Atom::QS(_s), pos) => { Err(CrsnError::Parse("Quoted string not expected here".into(), pos)) } Sexp::List(list, pos) => { if let Some(Sexp::Atom(Atom::S(s), s_pos)) = list.first() { let parsing_expr = pcx.state.borrow().parsing_expr; if !s.starts_with('=') && !parsing_expr { return Err(CrsnError::Parse(format!("Invalid syntax, did you mean \"={}\"?", s).into(), s_pos.clone())); } // start expr parsing mode let orig_parsing_expr = pcx.state.borrow().parsing_expr; pcx.state.borrow_mut().parsing_expr = true; let lmut = AtomicU32::new(0); let mut expr_ops = parse_instructions( // TODO avoid this madness vec![Sexp::List(list, pos.clone())].into_iter(), &pos, pcx)? .flatten(&lmut)?; if expr_ops.len() != 1 { return Err(CrsnError::Parse(format!("Expected one top level operation in an expression, got: {:?}", expr_ops).into(), pos.clone())); } let op = expr_ops.remove(0); let mut state_mut = pcx.state.borrow_mut(); state_mut.const_eval.clear_all(); let ticl = state_mut.const_eval.thread_info.clone(); op.execute(&ticl, &mut state_mut.const_eval) .map_err(|f| CrsnError::ParseOther(Box::new(f), pos.clone()))?; if state_mut.const_eval.test_cond(Cond::Invalid) { return Err(CrsnError::Parse("Expression evaluation failed - invalid flag set!".into(), pos.clone())); } if !orig_parsing_expr { // exit expr parsing mode state_mut.parsing_expr = false; } return Ok(DataDisp::Immediate(state_mut.const_eval.frame.gen[0])); } Err(CrsnError::Parse("List not expected here".into(), pos)) } Sexp::Atom(Atom::S(s), pos) => { parse_data_disp_from_str(&s, &pos, pcx) } } } /// Parse data disp from a string token (a &str is used so the string can be preprocessed) pub fn parse_data_disp_from_str(s: &str, pos: &SourcePosition, pcx: &ParserContext) -> Result { if s == "_" { return Ok(DataDisp::Discard); } if let Some(reference) = s.strip_prefix('@') { /* extension constants (pre-defined handles) */ for p in pcx.parsers { if let Some(val) = p.get_constant_value(reference) { return Ok(DataDisp::ImmObject(val)); } } /* register aliases */ let pstate = pcx.state.borrow(); if let Some(reg) = pstate.reg_aliases.get(reference) { return Ok(DataDisp::RegObject(*reg)) } else if let Some(reg) = pstate.global_reg_aliases.get(reference) { return Ok(DataDisp::RegObject(*reg)) } let reg = reg::parse_reg(reference, pos)?; if pstate.reg_aliases.values().find(|v| **v == reg).is_some() { Err(CrsnError::Parse(format!("Sym exists for register {}, direct access denied. Unsym it if needed.", reg).into(), pos.clone())) } else if pstate.global_reg_aliases.values().find(|v| **v == reg).is_some() { Err(CrsnError::Parse(format!("Global sym exists for register {}, direct access denied. Unsym it if needed.", reg).into(), pos.clone())) } else { Ok(DataDisp::RegObject(reg)) } } else { /* extension constants */ for p in pcx.parsers { if let Some(val) = p.get_constant_value(&s) { return Ok(DataDisp::Immediate(val)); } } /* program constants */ let pstate = pcx.state.borrow(); if let Some(val) = pstate.constants.get(s) { return Ok(DataDisp::Immediate(*val)); } /* register aliases */ if let Some(val) = pstate.reg_aliases.get(s) { return Ok(DataDisp::Register(*val)); } else if let Some(val) = pstate.global_reg_aliases.get(s) { return Ok(DataDisp::Register(*val)); } /* It can also be a plain old number */ if s.starts_with('#') || s.starts_with('-') || s.starts_with('.') || s.starts_with("0x") || s.starts_with("0b") || s.starts_with(|c : char| c.is_ascii_digit()) { match sexp::atom_of_string(s.to_string()) { Atom::C(v) => return Ok(DataDisp::Immediate(v as Value)), Atom::I(v) => return Ok(DataDisp::Immediate(unsafe { std::mem::transmute::<_, u64>(v) } as Value)), Atom::U(v) => return Ok(DataDisp::Immediate(v as Value)), Atom::F(v) => return Ok(DataDisp::Immediate(unsafe { std::mem::transmute::<_, u64>(v) } as Value)), Atom::S(_) | Atom::QS(_) => { // this will probably fail validation down the line, but it is definitely not a number } } } /* register */ let reg = reg::parse_reg(&s, &pos)?; let pstate = pcx.state.borrow(); if pstate.reg_aliases.values().find(|v| **v == reg).is_some() { Err(CrsnError::Parse(format!("Sym exists for register {}, direct access denied. Unsym it if needed.", reg).into(), pos.clone())) } else if pstate.global_reg_aliases.values().find(|v| **v == reg).is_some() { Err(CrsnError::Parse(format!("Global sym exists for register {}, direct access denied. Unsym it if needed.", reg).into(), pos.clone())) } else { Ok(DataDisp::Register(reg)) } } } /// Parse immediate value pub fn parse_value(tok: Sexp, pcx: &ParserContext) -> Result<(Value, SourcePosition), CrsnError> { match tok { Sexp::Atom(Atom::I(val), pos) => { Ok((unsafe { std::mem::transmute(val) }, pos)) } Sexp::Atom(Atom::U(val), pos) => { Ok((val, pos)) } Sexp::Atom(Atom::C(val), pos) => { Ok((val as u64, pos)) } Sexp::Atom(Atom::QS(_), pos) => { Err(CrsnError::Parse("quoted string not expected here".into(), pos)) } Sexp::Atom(Atom::F(val), pos) => { Ok((unsafe { std::mem::transmute(val) }, pos)) } Sexp::Atom(Atom::S(s), pos) => { let pstate = pcx.state.borrow(); if let Some(val) = pstate.constants.get(&s) { return Ok((*val, pos)); } /* extension constants */ for p in pcx.parsers { if let Some(val) = p.get_constant_value(&s) { return Ok((val, pos)); } } Err(CrsnError::Parse(format!("unknown constant: {}", s).into(), pos)) } Sexp::List(_, pos) => { Err(CrsnError::Parse("expected a value".into(), pos)) } } } pub fn parse_u64(literal: &str, pos: &SourcePosition) -> Result { // trace!("parse u64 from {}", literal); let mut without_underscores = Cow::Borrowed(literal); if without_underscores.contains('_') { without_underscores = without_underscores.replace('_', "").into(); } if let Some(hex) = without_underscores.strip_prefix("0x") { Ok(u64::from_str_radix(hex, 16).err_pos(pos)?) } else if let Some(hex) = without_underscores.strip_prefix("0b") { Ok(u64::from_str_radix(hex, 2).err_pos(pos)?) } else { Ok(u64::from_str_radix(&without_underscores, 10).err_pos(pos)?) } } pub fn parse_rd(tok: Sexp, pcx: &ParserContext) -> Result { let pos = tok.pos().clone(); Ok(Rd::new(RdData::try_from(parse_data_disp(tok, pcx)?).err_pos(&pos)?)) } pub fn parse_wr(tok: Sexp, pcx: &ParserContext) -> Result { let pos = tok.pos().clone(); Ok(Wr::new(WrData::try_from(parse_data_disp(tok, pcx)?).err_pos(&pos)?)) } pub fn parse_rdwr(tok: Sexp, pcx: &ParserContext) -> Result { let pos = tok.pos().clone(); let wr = parse_wr(tok, pcx)?; if !wr.is_readable() { return Err(CrsnError::Parse("Argument is not readable!".into(), pos)); } Ok(RdWr::new(wr.0)) } pub fn parse_wr_offset(tok: Sexp, pcx: &ParserContext) -> Result<(Wr, u32), CrsnError> { match tok { Sexp::Atom(Atom::S(s), pos) => { let (s, offset) = parse_disp_offs(&s, &pos)?; Ok((Wr::new(WrData::try_from(parse_data_disp_from_str(s, &pos, pcx)?).err_pos(&pos)?), offset)) } _ => Ok((parse_wr(tok, pcx)?, 0)) } } pub fn parse_rd_offset(tok: Sexp, pcx: &ParserContext) -> Result<(Rd, u32), CrsnError> { match tok { Sexp::Atom(Atom::S(s), pos) => { let (s, offset) = parse_disp_offs(&s, &pos)?; Ok((Rd::new(RdData::try_from(parse_data_disp_from_str(s, &pos, pcx)?).err_pos(&pos)?), offset)) } _ => Ok((parse_rd(tok, pcx)?, 0)) } } pub fn parse_rdwr_offset(tok: Sexp, pcx: &ParserContext) -> Result<(RdWr, u32), CrsnError> { match tok { Sexp::Atom(Atom::S(s), pos) => { let (s, offset) = parse_disp_offs(&s, &pos)?; let wr = WrData::try_from(parse_data_disp_from_str(s, &pos, pcx)?).err_pos(&pos)?; if !wr.is_readable() { return Err(CrsnError::Parse("Argument is not readable!".into(), pos)); } Ok((RdWr::new(wr), offset)) } _ => Ok((parse_rdwr(tok, pcx)?, 0)) } } fn parse_disp_offs<'a>(s : &'a str, pos: &SourcePosition) -> Result<(&'a str, u32), CrsnError> { if s.contains(':') { let mut split = s.split(':'); let disp = split.next().unwrap(); let num : u32 = split.next().unwrap().parse().err_pos(pos)?; Ok((disp, num)) } else { Ok((&s, 0)) } }