use super::MMixAssembler;
use super::Rule;
use super::SymbolType;
use super::expressions::ExprValue;
use super::instructions::MMixInstruction;
use super::tree::Children;
use tracing::debug;
impl MMixAssembler {
pub(super) fn data_directive_alignment(
pair: &pest::iterators::Pair<Rule>,
) -> Result<u64, String> {
let directive = pair
.clone()
.into_inner()
.next()
.ok_or("Empty data directive")?;
match directive.as_rule() {
Rule::directive_byte => Ok(1),
Rule::directive_wyde => Ok(2),
Rule::directive_tetra => Ok(4),
Rule::directive_octa => Ok(8),
_ => Err(format!("Unknown data directive: {:?}", directive.as_rule())),
}
}
pub(super) fn data_directive_size(
&self,
pair: pest::iterators::Pair<Rule>,
) -> Result<u64, String> {
let mut parts = pair.clone().into_inner();
let directive = parts.next().ok_or("Empty data directive")?;
let unit_width = Self::data_directive_unit_width(directive.as_rule())?;
let values = parts.next().ok_or("Missing data values")?;
let mut unit_count = 0u64;
for value in values.into_inner() {
unit_count += self.data_value_unit_count(value)?;
}
let total_size = unit_width * unit_count;
debug!(
"Data directive size: {} units x {} bytes = {} bytes",
unit_count, unit_width, total_size
);
Ok(total_size)
}
fn data_value_unit_count(&self, value: pest::iterators::Pair<Rule>) -> Result<u64, String> {
let data_expr = Self::data_expr(value)?;
if Self::is_bare_empty_string(&data_expr) {
return Ok(1);
}
let mut char_total = 0u64;
let mut string_count = 0u64;
for term in data_expr.into_inner().step_by(2) {
for primary in term.into_inner().step_by(2) {
self.count_data_primary_strings(primary, &mut char_total, &mut string_count)?;
}
}
Ok(1 + char_total - string_count)
}
fn count_data_primary_strings(
&self,
primary: pest::iterators::Pair<Rule>,
char_total: &mut u64,
string_count: &mut u64,
) -> Result<(), String> {
let mut children = primary.into_inner();
let first = children
.next()
.ok_or_else(|| "data_primary has a child".to_string())?;
match first.as_rule() {
Rule::unary_op => {
let operand = children
.next()
.ok_or_else(|| "unary operator needs an operand".to_string())?;
self.count_data_primary_strings(operand, char_total, string_count)
}
Rule::string_literal => {
let (_, rest) = self.decode_data_string_literal(&first)?;
*char_total += 1 + rest.len() as u64;
*string_count += 1;
Ok(())
}
_ => Ok(()),
}
}
fn data_directive_unit_width(directive_kind: Rule) -> Result<u64, String> {
match directive_kind {
Rule::directive_byte => Ok(1),
Rule::directive_wyde => Ok(2),
Rule::directive_tetra => Ok(4),
Rule::directive_octa => Ok(8),
_ => Err(format!("Unknown data directive: {:?}", directive_kind)),
}
}
fn data_directive_unit(directive_kind: Rule, value: u64) -> Result<MMixInstruction, String> {
match directive_kind {
Rule::directive_byte => Ok(MMixInstruction::BYTE(value as u8)),
Rule::directive_wyde => Ok(MMixInstruction::WYDE(value as u16)),
Rule::directive_tetra => Ok(MMixInstruction::TETRA(value as u32)),
Rule::directive_octa => Ok(MMixInstruction::OCTA(value)),
_ => Err(format!("Unknown data directive: {:?}", directive_kind)),
}
}
fn data_unit_name(width: u64) -> &'static str {
match width {
1 => "byte",
2 => "wyde",
4 => "tetra",
_ => "octa",
}
}
fn data_unit_mask(width: u64) -> u64 {
if width >= 8 {
u64::MAX
} else {
(1u64 << (width * 8)) - 1
}
}
fn warn(&mut self, line: usize, col: usize, message: String) {
self.warnings.push(format!(
"{}:{}:{}: warning: {}",
self.current_filename, line, col, message
));
}
pub(super) fn parse_data_directive(
&mut self,
pair: pest::iterators::Pair<Rule>,
) -> Result<Vec<MMixInstruction>, String> {
let mut parts = pair.into_inner();
let directive_kind = parts.next().ok_or("Empty data directive")?.as_rule();
let values_pair = parts.next().ok_or("Missing data values")?;
let unit_width = Self::data_directive_unit_width(directive_kind)?;
let unit = Self::data_unit_name(unit_width);
let mask = Self::data_unit_mask(unit_width);
let mut result = Vec::new();
for value in values_pair.into_inner() {
let (line, col) = value.line_col();
let text = value.as_str().trim();
if Self::is_bare_empty_string(&Self::data_expr(value.clone())?) {
self.warn(
line,
col,
format!("an empty string assembles as one zero {unit}"),
);
}
for item in self.eval_data_value_items(value)? {
let val = self.require_pure(item, line, col)?;
if val & mask != val {
self.warn(
line,
col,
format!(
"value {} does not fit in a {unit}; its low {unit} assembles",
Self::quote_out_of_range(val, text)
),
);
}
result.push(Self::data_directive_unit(directive_kind, val)?);
}
}
Ok(result)
}
pub(super) fn parse_loc_directive(
&mut self,
pair: pest::iterators::Pair<Rule>,
) -> Result<(), String> {
let mut parts = Children::of(pair);
let _directive = parts.next(); let addr = self.parse_number(parts.required()?)?;
self.current_addr = addr;
self.past_end = false;
Ok(())
}
pub(super) fn parse_is_directive(
&mut self,
pair: pest::iterators::Pair<Rule>,
checking: bool,
) -> Result<(), String> {
let mut parts = Children::of(pair);
let lhs = parts.required()?;
let lhs_rule = lhs.as_rule();
let (line, col) = lhs.line_col();
let raw_name = lhs.as_str().to_string();
let _is_keyword = parts.next(); let value_pair = parts.required()?;
let (vline, vcol) = value_pair.line_col();
self.scan_uses_for_redefinition(&value_pair);
let symbol_type = match self.eval_expr(value_pair)? {
ExprValue::Register(r) => {
SymbolType::Register(self.require_register_in_range(r, vline, vcol)?)
}
ExprValue::Pure(value) => SymbolType::Constant(value),
};
if lhs_rule == Rule::local_label_def {
self.record_local_label(Self::local_digit(&raw_name), symbol_type, checking);
} else if checking {
self.define_symbol(&raw_name, symbol_type, line, col)?;
} else {
let qualified = self.qualify_name(&raw_name);
self.symbols.insert(qualified, symbol_type);
}
Ok(())
}
pub(super) fn parse_prefix_directive(
&mut self,
pair: pest::iterators::Pair<Rule>,
) -> Result<(), String> {
let mut parts = Children::of(pair);
let _directive = parts.next(); let arg = parts.required()?;
let arg_str = arg.as_str();
self.current_prefix = arg_str.strip_prefix(':').unwrap_or(arg_str).to_string();
Ok(())
}
}