use std::borrow::Cow; use std::convert::TryFrom; use sexp::{Atom, Sexp, SourcePosition}; 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; use std::num::TryFromIntError; use crate::asm::patches::ErrWithPos; 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: 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 { // trace!("parse label: {:?}", name); let (name, namepos) = expect_string_atom(name)?; Ok(parse_label_str(&name, namepos)?) } pub fn parse_label_str(name: &str, pos: SourcePosition) -> Result { let label = name.trim_start_matches(':'); Ok(if label.starts_with('#') { let val = parse_u64(&label[1..], pos.clone())?; Label::Numbered(u32::try_from(val).err_pos(pos)?) } else { 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); // TODO implement masks match tok { Sexp::Atom(Atom::I(val), pos) => { Ok(DataDisp::Immediate(unsafe { std::mem::transmute(val) })) } Sexp::Atom(Atom::S(s), pos) => { 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('@') { let pstate = pcx.state.borrow(); if let Some(val) = pstate.reg_aliases.get(reference) { Ok(DataDisp::ObjectPtr(*val)) } else { let reg = reg::parse_reg(reference, pos.clone())?; 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)) } else { Ok(DataDisp::ObjectPtr(reg)) } } } else if s.starts_with(|c: char| c.is_ascii_digit()) { Ok(DataDisp::Immediate(unsafe { std::mem::transmute(parse_i64(&s, pos)?) })) } else { let reg = reg::parse_reg(&s, pos.clone())?; 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)) } else { Ok(DataDisp::Register(reg)) } } } other => { Err(CrsnError::Parse(format!("bad data disp: {:?}", other).into(), other.pos().clone())) } } } /// Parse immediate value pub fn parse_value(tok: Sexp, pcx: &ParserContext) -> Result { match tok { Sexp::Atom(Atom::I(val), pos) => { Ok(unsafe { std::mem::transmute(val) }) } Sexp::Atom(Atom::S(s), pos) => { let pstate = pcx.state.borrow(); if let Some(val) = pstate.constants.get(&s) { return Ok(*val); } Ok(unsafe { std::mem::transmute(parse_i64(&s, pos)?) }) } other => { Err(CrsnError::Parse(format!("bad value format: {:?}", other).into(), other.pos().clone())) } } } 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_i64(literal: &str, pos: SourcePosition) -> Result { // trace!("parse i64 from {}", literal); if let Some(_value) = literal.strip_prefix("-") { Ok(-1 * i64::try_from(parse_u64(literal, pos.clone())?).err_pos(pos)?) } else { Ok(i64::try_from(parse_u64(literal, pos.clone())?).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)?)) }