use crate::*; pub enum SemanticToken { MacroDefinition(MacroDefinition), BlockToken(BlockToken), } pub struct MacroDefinition { pub name: Tracked, pub arguments: Vec>, pub body: MacroDefinitionBody, } pub struct ArgumentDefinition { pub name: String, pub variant: ArgumentType, } #[derive(PartialEq)] pub enum ArgumentType { Integer, Block, } impl std::fmt::Display for ArgumentType { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { match self { ArgumentType::Integer => write!(f, "an integer"), ArgumentType::Block => write!(f, "a block"), } } } pub enum MacroDefinitionBody { Integer(Tracked), Block(Vec>), Invocation(Tracked), } pub struct ConditionalBlock { pub predicate: Tracked, pub body: Tracked, } pub enum IntegerToken { IntegerLiteral(isize), Expression(Expression), Invocation(Invocation), } pub struct Expression { pub tokens: Vec>, } pub enum ExpressionToken { IntegerToken(Box), Invocation(Invocation), Operator(Operator), } pub enum BlockToken { LabelDefinition(String), PinnedAddress(Tracked), ConditionalBlock(Box), WordTemplate(WordTemplate), Block(Vec>), Invocation(Invocation), } pub struct Invocation { pub name: String, pub arguments: Vec>, } pub enum InvocationArgument { String(StringLiteral), IntegerToken(IntegerToken), BlockToken(BlockToken), Invocation(Invocation), } pub enum SemanticError { MisplacedStringLiteral, MisplacedListLiteral, MisplacedSeparator, MisplacedMacroDefinition, ExpectedInteger(SemanticLocation), ExpectedBlock(SemanticLocation), InvalidArgumentDefinition, InvalidInvocationArgument, LabelInMacroDefinition, SublabelWithoutNamespace, LocalSymbolWithoutNamespace, } #[derive(Clone, Copy)] pub enum SemanticLocation { MacroDefinitionBody, Expression, ConditionPredicate, ConditionBody, Program, BlockLiteral, PinAddress, } impl std::fmt::Display for SemanticLocation { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { let string = match self { SemanticLocation::Expression => "inside this expression", SemanticLocation::ConditionPredicate => "as the predicate of this conditional block", SemanticLocation::ConditionBody => "as the body of this conditional block", SemanticLocation::Program => "at the outermost level of the program", SemanticLocation::BlockLiteral => "inside this block literal", SemanticLocation::MacroDefinitionBody => "inside the body of this macro definition", SemanticLocation::PinAddress => "as the address of this pin", }; write!(f, "{string}") } } pub fn report_semantic_errors(errors: &[Tracked], source_code: &str) { for error in errors { report_semantic_error(error, source_code); } } fn report_semantic_error(error: &Tracked, source_code: &str) { let context = Context { source_code: &source_code, source: &error.source }; let message = match &error.value { SemanticError::MisplacedStringLiteral => "A string literal can only be used as an invocation argument", SemanticError::MisplacedListLiteral => "A list literal can only be used as an invocation argument", SemanticError::MisplacedSeparator => "A separator can only be used to construct an argument list", SemanticError::MisplacedMacroDefinition => "A macro definition must be used at the outermost level of the program", SemanticError::ExpectedInteger(location) => &format!("An integer value was expected {location}"), SemanticError::ExpectedBlock(location) => &format!("A block value was expected {location}"), SemanticError::InvalidArgumentDefinition => "Argument definitions must be in the form 'name' or '{{name}}'", SemanticError::InvalidInvocationArgument => "This token cannot be used in an invocation argument", SemanticError::LabelInMacroDefinition => &format!("Only sublabels can be defined inside macro definitions"), SemanticError::SublabelWithoutNamespace => &format!("Sublabel was not defined inside a macro definition or after a label"), SemanticError::LocalSymbolWithoutNamespace => &format!("Local symbol was not defined inside a macro definition or after a label"), }; report_source_issue(LogLevel::Error, &context, message); } pub fn print_semantic_token(i: usize, token: &SemanticToken) { match token { SemanticToken::MacroDefinition(definition) => { indent!(i, "MacroDefinition({})", definition.name); for argument in &definition.arguments { print_argument_definition(i+1, argument); } match &definition.body { MacroDefinitionBody::Integer(integer) => { print_integer_token(i+1, integer) } MacroDefinitionBody::Block(tokens) => { print_block(i+1, tokens); } MacroDefinitionBody::Invocation(invocation) => { print_invocation(i+1, invocation); } } } SemanticToken::BlockToken(block) => print_block_token(0, block), } } fn print_argument_definition(i: usize, argument: &ArgumentDefinition) { match argument.variant { ArgumentType::Integer => { indent!(i, "Argument({}, integer)", argument.name) } ArgumentType::Block => { indent!(i, "Argument({}, block)", argument.name) } } } fn print_block_token(i: usize, block: &BlockToken) { match block { BlockToken::Invocation(invocation) => { print_invocation(i, invocation) } BlockToken::LabelDefinition(name) => { indent!(i, "LabelDefinition({name})") } BlockToken::Block(block) => { print_block(i, block); } BlockToken::PinnedAddress(integer) => { indent!(i, "PinnedAddress"); print_integer_token(i+1, integer); } BlockToken::ConditionalBlock(condition) => { indent!(i, "ConditionalBlock"); indent!(i+1, "Predicate"); print_integer_token(i+2, &condition.predicate); indent!(i+1, "Body"); print_block_token(i+2, &condition.body); } BlockToken::WordTemplate(word_template) => { indent!(i, "WordTemplate({word_template})") } } } fn print_block(i: usize, tokens: &[Tracked]) { indent!(i, "Block"); for token in tokens { print_block_token(i+1, token); } } fn print_invocation(i: usize, invocation: &Invocation) { indent!(i, "Invocation({})", invocation.name); for argument in &invocation.arguments { print_invocation_argument(i+1, argument); } } fn print_invocation_argument(i: usize, argument: &InvocationArgument) { match &argument { InvocationArgument::String(string_literal) => { indent!(i, "String({string_literal})") } InvocationArgument::IntegerToken(integer) => { print_integer_token(i, integer) } InvocationArgument::BlockToken(block) => { print_block_token(i, block) } InvocationArgument::Invocation(invocation) => { print_invocation(i, invocation) } } } fn print_integer_token(i: usize, integer: &IntegerToken) { match integer { IntegerToken::IntegerLiteral(value) => { indent!(i, "IntegerValue({value})") } IntegerToken::Expression(expression) => { print_expression(i, expression) } IntegerToken::Invocation(invocation) => { print_invocation(i, invocation) } } } fn print_expression(i: usize, expression: &Expression) { indent!(i, "Expression"); for token in &expression.tokens { match &token.value { ExpressionToken::IntegerToken(integer) => { print_integer_token(i+1, &integer) } ExpressionToken::Invocation(invocation) => { print_invocation(i+1, &invocation); } ExpressionToken::Operator(operator) => { indent!(i+1, "Operator({operator})") } } } }