|
|
|
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;
|
|
|
|
use crate::asm::parse::sexp_expect::expect_string_atom;
|
|
|
|
use crate::asm::patches::ErrWithPos;
|
|
|
|
|
|
|
|
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<Label, CrsnError> {
|
|
|
|
let (name, namepos) = expect_string_atom(name)?;
|
|
|
|
Ok(parse_label_str(&name, &namepos)?)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parse_label_str(name: &str, pos: &SourcePosition) -> Result<Label, CrsnError> {
|
|
|
|
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<DataDisp, CrsnError> {
|
|
|
|
// 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) => {
|
|
|
|
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<DataDisp, CrsnError> {
|
|
|
|
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<u64, CrsnError> {
|
|
|
|
// 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<Rd, CrsnError> {
|
|
|
|
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<Wr, CrsnError> {
|
|
|
|
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<RdWr, CrsnError> {
|
|
|
|
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))
|
|
|
|
}
|
|
|
|
}
|