summaryrefslogtreecommitdiff
path: root/src/report.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/report.rs')
-rw-r--r--src/report.rs229
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);
+}