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);
}