|
|
|
use std::borrow::Cow;
|
|
|
|
use std::convert::TryFrom;
|
|
|
|
|
|
|
|
use sexp::{Atom, Sexp};
|
|
|
|
|
|
|
|
use crate::asm::data::{DataDisp, Rd, RdData, reg, Wr, WrData};
|
|
|
|
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;
|
|
|
|
|
|
|
|
fn is_valid_identifier(name: &str) -> bool {
|
|
|
|
name.starts_with(|c: char| c.is_ascii_alphabetic() || c == '_')
|
|
|
|
&& name.chars().find(|c| !c.is_ascii_alphanumeric() && *c != '_').is_none()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse register alias
|
|
|
|
pub fn parse_reg_alias(name: Option<Sexp>) -> Result<RegisterAlias, CrsnError> {
|
|
|
|
// trace!("parse reg alias: {:?}", name);
|
|
|
|
|
|
|
|
let name = expect_string_atom(name)?;
|
|
|
|
|
|
|
|
if !is_valid_identifier(&name) {
|
|
|
|
return Err(CrsnError::Parse(format!("\"{}\" is not an allowed register alias.", name).into()));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse constant name
|
|
|
|
pub fn parse_constant_name(name: Option<Sexp>) -> Result<ConstantName, CrsnError> {
|
|
|
|
// trace!("parse const name: {:?}", name);
|
|
|
|
|
|
|
|
let name = expect_string_atom(name)?;
|
|
|
|
|
|
|
|
if !is_valid_identifier(&name) {
|
|
|
|
return Err(CrsnError::Parse(format!("\"{}\" is not an allowed constant name.", name).into()));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a label
|
|
|
|
pub fn parse_label(name: Option<Sexp>) -> Result<Label, CrsnError> {
|
|
|
|
// trace!("parse label: {:?}", name);
|
|
|
|
|
|
|
|
let name = expect_string_atom(name)?;
|
|
|
|
Ok(Label::Named(name.trim_start_matches(':').into()))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse data disposition (address/value, without the read/write restriction)
|
|
|
|
pub fn parse_data_disp(tok: Option<Sexp>, pcx: &ParserContext) -> Result<DataDisp, CrsnError> {
|
|
|
|
// trace!("parse data: {:?}", tok);
|
|
|
|
|
|
|
|
let tok = if let Some(tok) = tok {
|
|
|
|
tok
|
|
|
|
} else {
|
|
|
|
return Err(CrsnError::Parse("Expected data disposition token".into()));
|
|
|
|
};
|
|
|
|
|
|
|
|
// TODO implement masks
|
|
|
|
|
|
|
|
match &tok {
|
|
|
|
Sexp::Atom(Atom::I(val)) => {
|
|
|
|
Ok(DataDisp::Immediate(unsafe { std::mem::transmute(*val) }))
|
|
|
|
}
|
|
|
|
Sexp::Atom(Atom::S(s)) => {
|
|
|
|
if s == "_" {
|
|
|
|
return Ok(DataDisp::Discard);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if we have an alias defined
|
|
|
|
{
|
|
|
|
let pstate = pcx.state.borrow();
|
|
|
|
if let Some(val) = pstate.constants.get(s) {
|
|
|
|
return Ok(DataDisp::Immediate(*val));
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(val) = pstate.reg_aliases.get(s) {
|
|
|
|
return Ok(DataDisp::Register(*val));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(reference) = s.strip_prefix('@') {
|
|
|
|
Ok(DataDisp::ObjectPtr(reg::parse_reg(reference)?))
|
|
|
|
} else if s.starts_with(|c: char| c.is_ascii_digit()) {
|
|
|
|
Ok(DataDisp::Immediate(unsafe { std::mem::transmute(parse_i64(s)?) }))
|
|
|
|
} else {
|
|
|
|
Ok(DataDisp::Register(reg::parse_reg(s)?))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
Err(CrsnError::Parse(format!("bad data disp: {:?}", tok).into()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse immediate value
|
|
|
|
pub fn parse_value(tok: Option<Sexp>) -> Result<Value, CrsnError> {
|
|
|
|
let tok = if let Some(tok) = tok {
|
|
|
|
tok
|
|
|
|
} else {
|
|
|
|
return Err(CrsnError::Parse("Expected value token".into()));
|
|
|
|
};
|
|
|
|
|
|
|
|
// trace!("parse value: {:?}", tok);
|
|
|
|
|
|
|
|
match &tok {
|
|
|
|
Sexp::Atom(Atom::I(val)) => {
|
|
|
|
Ok(unsafe { std::mem::transmute(*val) })
|
|
|
|
}
|
|
|
|
Sexp::Atom(Atom::S(s)) => {
|
|
|
|
Ok(unsafe { std::mem::transmute(parse_i64(s)?) })
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
Err(CrsnError::Parse(format!("bad value format: {:?}", tok).into()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn parse_u64(literal: &str) -> anyhow::Result<u64> {
|
|
|
|
// 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)?)
|
|
|
|
} else if let Some(hex) = without_underscores.strip_prefix("0b") {
|
|
|
|
Ok(u64::from_str_radix(hex, 2)?)
|
|
|
|
} else {
|
|
|
|
Ok(u64::from_str_radix(&without_underscores, 10)?)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parse_i64(literal: &str) -> anyhow::Result<i64> {
|
|
|
|
// trace!("parse i64 from {}", literal);
|
|
|
|
if let Some(_value) = literal.strip_prefix("-") {
|
|
|
|
Ok(-1 * i64::try_from(parse_u64(literal)?)?)
|
|
|
|
} else {
|
|
|
|
Ok(i64::try_from(parse_u64(literal)?)?)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parse_rd(tok: Option<Sexp>, pcx: &ParserContext) -> anyhow::Result<Rd> {
|
|
|
|
Ok(Rd::new(RdData::try_from(parse_data_disp(tok, pcx)?)?))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parse_wr(tok: Option<Sexp>, pcx: &ParserContext) -> anyhow::Result<Wr> {
|
|
|
|
Ok(Wr::new(WrData::try_from(parse_data_disp(tok, pcx)?)?))
|
|
|
|
}
|