use crate::*; #[derive(Clone)] pub enum IntermediateToken { Word(Word), PinnedAddress(usize), } #[derive(Clone)] pub struct Word { pub value: usize, /// Width of the word in bits. pub width: u32, } impl std::fmt::Display for Word { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { if self.width == 0 { write!(f, "0") } else { for i in (0..self.width).rev() { let is_first_bit = i+1 == self.width; if !is_first_bit && (i+1) % 4 == 0 { write!(f, "_")?; } match (self.value >> i) & 1 { 0 => write!(f, "0")?, _ => write!(f, "1")?, } } Ok(()) } } } #[derive(Clone)] pub enum IntermediateValue { Integer(IntermediateInteger), List(IntermediateList), Block(IntermediateBlock), } pub type IntermediateInteger = Tracked; pub type IntermediateList = Vec>; pub type IntermediateBlock = Vec>; pub enum RepeatedArgument { Loop(IntermediateValue), List(Vec), } impl RepeatedArgument { pub fn len(&self) -> usize { match self { Self::Loop(_) => 1, Self::List(list) => list.len(), } } } pub enum IntermediateError { ExpectedInteger, ExpectedBlock, ExpectedList, ListExhausted, LabelDefinitionInConditionBody, LabelReferenceInPinnedAddress, InvalidBlockInExpression, ExpressionError(Tracked), /// expected, received IncorrectArgumentCount(usize, usize), /// expected, received IncorrectArgumentType(ArgumentType, ArgumentType), /// pinned, real PinnedAddressBacktrack(usize, usize), /// expected, received ValueTooWide(u32, u32), LabelNeverStabilised(String), MaxRecursionDepthExceeded, } pub fn report_intermediate_errors(errors: &[Tracked], source_code: &str) { for error in errors { report_intermediate_error(error, source_code); } } fn report_intermediate_error(error: &Tracked, source_code: &str) { let context = Context { source_code: &source_code, source: &error.source }; let message = match &error.value { IntermediateError::ExpectedInteger => "An integer value was expected here", IntermediateError::ExpectedBlock => "A block value was expected here", IntermediateError::ExpectedList => "A string value was expected here", IntermediateError::ListExhausted => "This list is shorter than another list passed to the same invocation", IntermediateError::LabelDefinitionInConditionBody => "The body of a conditional block cannot contain a label definition", IntermediateError::LabelReferenceInPinnedAddress => "The value of a pinned address cannot contain a label reference", IntermediateError::InvalidBlockInExpression => "Expression cannot contain an invocation that expands to a block value", IntermediateError::ExpressionError(expression_error) => { report_expression_error(expression_error, source_code); return; }, IntermediateError::IncorrectArgumentCount(expected, received) => &format!("Expected {expected} arguments, but received {received} instead"), IntermediateError::IncorrectArgumentType(expected, received) => &format!("Expected {expected} value but received {received} value instead"), IntermediateError::PinnedAddressBacktrack(pinned, real) => &format!("Cannot pin to address {pinned} when address is already {real}"), IntermediateError::ValueTooWide(expected, received) => &format!("Field is {expected} bits wide, but received a value that is {received} bits wide"), IntermediateError::LabelNeverStabilised(name) => &format!("Label '{name}' never stabilised"), IntermediateError::MaxRecursionDepthExceeded => &format!("Macro invocation exceededs the maximum recursion depth of {MAX_RECURSION_DEPTH}"), }; report_source_issue(LogLevel::Error, &context, message); } pub fn print_intermediate_token(i: usize, token: &IntermediateToken) { match token { IntermediateToken::Word(word) => { indent!(i, "Word({:>0w$b})", word.value, w = word.width as usize); } IntermediateToken::PinnedAddress(address) => { indent!(i, "PinnedAddress({address})"); } } }