diff options
Diffstat (limited to 'src/report.rs')
-rw-r--r-- | src/report.rs | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/src/report.rs b/src/report.rs new file mode 100644 index 0000000..2acdddc --- /dev/null +++ b/src/report.rs @@ -0,0 +1,229 @@ +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); +} |