use crate::*; static mut ERROR_REPORTED: bool = false; macro_rules! report_source_error { ($context:expr, $message:expr) => { report_source_issue(LogLevel::Error, $context, $message); unsafe { ERROR_REPORTED = true; } }; } macro_rules! exit_if_error_reported { () => { if unsafe { ERROR_REPORTED } { std::process::exit(1); } }; } pub fn report_syntactic_errors(syntactic_tokens: &[SyntacticToken], source_code: &str) { use SyntacticTokenVariant as SynVar; for token in syntactic_tokens { let context = Context { source_code: &source_code, source: &token.source }; match &token.variant { SynVar::Expression(expr) => for t in &expr.tokens { let context = Context { source_code: &source_code, source: &t.source }; if let ExpressionTokenVariant::Error(err) = &t.variant { let ExpressionParseError::InvalidHexadecimalLiteral(hex) = err; let message = format!("Invalid hexadecimal literal {hex:?} in constant expression"); report_source_error!(&context, &message); } } SynVar::PackedBinaryLiteral(pbl) => for e in &pbl.errors { let context = Context { source_code: &source_code, source: &e.source }; match &e.variant { PackedBinaryLiteralParseErrorVariant::DuplicateFieldName(name) => { let message = format!("Duplicate field name {name:?} in packed binary literal"); report_source_error!(&context, &message); } PackedBinaryLiteralParseErrorVariant::InvalidCharacter(c) => { let message = format!("Invalid character {c:?} in packed binary literal"); report_source_error!(&context, &message); } } } SynVar::Error(err) => match err { SyntacticParseError::InvalidHexadecimalLiteral(hex) => { let message = format!("Invalid hexadecimal literal {hex:?}"); report_source_error!(&context, &message); } SyntacticParseError::InvalidDecimalLiteral(dec) => { let message = format!("Invalid decimal literal {dec:?}"); report_source_error!(&context, &message); } SyntacticParseError::InvalidSymbolIdentifier(name) => { let message = format!("Invalid identifier {name:?}"); report_source_error!(&context, &message); } SyntacticParseError::UnterminatedComment => { let message = format!("Unterminated comment"); report_source_error!(&context, &message); } SyntacticParseError::UnterminatedExpression => { let message = format!("Unterminated constant expression"); report_source_error!(&context, &message); } SyntacticParseError::LabelInMacroDefinition => { let message = format!("Only sublabels can be used in macro definitions"); report_source_error!(&context, &message); } } _ => (), } } exit_if_error_reported!(); } pub fn report_semantic_errors(program: &SemanticProgram, source_code: &str) { for (_, definition) in &program.macro_definitions { report_value_errors(&definition.value, source_code); } for token in &program.body { report_semantic_token_errors(token, source_code); } exit_if_error_reported!(); } fn report_value_errors(definition: &Value, source_code: &str) { match definition { Value::Integer(integer) => match integer { Integer::Expression(expr) => for token in &expr.tokens { if let ExpressionTokenVariant::Error(error) = &token.variant { let message = match error { ExpressionParseError::InvalidHexadecimalLiteral(hex) => format!("Invalid hexadecimal literal '{hex}' in constant expression"), }; let context = Context { source: &token.source, source_code}; report_source_error!(&context, &message); } } _ => (), } Value::Block(block) => { for token in block { report_semantic_token_errors(token, source_code); } } Value::Invocation(invocation) => report_invocation_errors(invocation, source_code), } } fn report_semantic_token_errors(token: &SemanticToken, source_code: &str) { match &token { SemanticToken::Word(pbl) => for error in &pbl.errors { let message = match &error.variant { PackedBinaryLiteralParseErrorVariant::DuplicateFieldName(name) => format!("Duplicate field name '{name}' in packed binary literal"), PackedBinaryLiteralParseErrorVariant::InvalidCharacter(c) => format!("Invalid character '{c}' in packed binary literal"), }; let context = Context { source: &error.source, source_code }; report_source_error!(&context, &message); } SemanticToken::Invocation(invocation) => { report_invocation_errors(invocation, source_code) } SemanticToken::Error(error) => { report_semantic_error(error, source_code) } SemanticToken::LabelDefinition(_) => (), SemanticToken::PinnedAddress(_) => (), } } fn report_invocation_errors(invocation: &Invocation, source_code: &str) { for error in &invocation.errors { report_semantic_error(&error, source_code); } for argument in &invocation.arguments { report_value_errors(&argument.value, source_code); } } fn report_semantic_error(error: &SemanticParseError, source_code: &str) { let message = match &error.variant { SemanticParseErrorVariant::UnterminatedMacroDefinition(name) => format!("The macro definition '{name}' is missing a terminating ';' character"), SemanticParseErrorVariant::UnterminatedBlock => format!("Block literal is missing a terminating '}}' character"), SemanticParseErrorVariant::InvalidToken => format!("Invalid token"), }; let context = Context { source: &error.source, source_code}; report_source_error!(&context, &message); } pub fn report_assembler_errors(tokens: &[AssembledToken], source_code: &str) { for token in tokens { match token { AssembledToken::Word(word) => { for error in &word.errors { report_assembler_error(&error, source_code); } } AssembledToken::Error(error) => { report_assembler_error(error, source_code); }, _ => (), } } exit_if_error_reported!(); } fn report_assembler_error(error: &AssemblerError, source_code: &str) { let message = match &error.variant { AssemblerErrorVariant::DefinitionNotFound(name) => format!("Definition not found for name '{name}'"), AssemblerErrorVariant::NotABlock => format!("Value of type block was expected here"), AssemblerErrorVariant::NotAnInteger => format!("Value of type integer was expected here"), AssemblerErrorVariant::IntegerInBlock => format!("Integer in block"), AssemblerErrorVariant::IncorrectArgumentCount(expected, received) => format!("Expected {expected} arguments, but received {received} instead"), AssemblerErrorVariant::IncorrectArgumentType(expected, received) => format!("Expected {expected} argument but received {received} instead"), }; let context = Context { source_code: &source_code, source: &error.source, }; report_source_error!(&context, &message); } pub fn report_bytecode_errors(bytecode: &Bytecode, source_code: &str) { for error in &bytecode.errors { report_bytecode_error(error, source_code); } exit_if_error_reported!(); } pub fn report_bytecode_error(error: &BytecodeError, source_code: &str) { let message = match &error.variant { BytecodeErrorVariant::DefinitionNotFound(name) => format!("Could not find definition for label reference '{name}'"), BytecodeErrorVariant::DuplicateLabelDefinition(name) => format!("Duplicate definition for label '{name}'"), BytecodeErrorVariant::PinnedAddressBacktrack(expected, received) => format!("Cannot pin back to address {expected} when already at address {received}"), BytecodeErrorVariant::ValueTooLarge(expected, received) => format!("Expected {expected}-bit value, but received {received}-bit value instead"), BytecodeErrorVariant::StackUnderflow => format!("Stack underflow when evaluating expression"), BytecodeErrorVariant::NoReturnValue => format!("No value left on stack when evaluating expression"), BytecodeErrorVariant::MultipleReturnValues => format!("More than one value left on stack when evaluating expression"), }; let context = Context { source_code: &source_code, source: &error.source, }; report_source_error!(&context, &message); }