summaryrefslogblamecommitdiff
path: root/src/report.rs
blob: a88de4fda17215d72bf9e299f558e6d1f60bf637 (plain) (tree)





























































                                                                                                        


                                                                 
                                                                
                                                                               























































































































                                                                                                  
                                                          









































                                                                                                
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::UnterminatedString => {
                    let message = format!("Unterminated string");
                    report_source_error!(&context, &message);
                }
                SyntacticParseError::UnterminatedExpression => {
                    let message = format!("Unterminated assembler 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::StringInExpression =>
            format!("Expressions cannot contain strings"),
        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);
}