use crate::*; pub enum SemanticToken { MacroDefinition(MacroDefinition), BlockToken(BlockToken), } #[derive(Clone)] pub struct MacroDefinition { pub name: Tracked, pub arguments: Vec>, pub body: MacroDefinitionBody, } #[derive(Clone)] pub struct ArgumentDefinition { pub name: String, pub variant: ArgumentType, } #[derive(Clone, Copy, Debug, PartialEq)] pub enum ArgumentType { Integer, Block, List, } 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"), ArgumentType::List => write!(f, "a list"), } } } #[derive(Clone)] pub enum MacroDefinitionBody { Integer(Tracked), Block(Vec>), List(Tracked), Invocation(Tracked), } #[derive(Clone)] pub struct ConditionalBlock { pub predicate: Tracked, pub body: Tracked, } #[derive(Clone)] pub enum IntegerToken { IntegerLiteral(isize), Expression(Expression), Invocation(Invocation), } #[derive(Clone)] pub struct Expression { pub tokens: Vec>, } #[derive(Clone)] pub enum ExpressionToken { IntegerToken(Box), ListToken(ListToken), Invocation(Invocation), Operator(Operator), } #[derive(Clone)] pub enum BlockToken { LabelDefinition(String), PinnedAddress(Tracked), ConditionalBlock(Box), WordTemplate(WordTemplate), Block(Vec>), Invocation(Invocation), } #[derive(Clone)] pub enum ListToken { StringLiteral(StringLiteral), ListLiteral(Vec>), Invocation(Invocation), } #[derive(Clone)] pub struct Invocation { pub name: String, pub arguments: Vec>, } #[derive(Clone)] pub enum InvocationArgument { IntegerToken(IntegerToken), BlockToken(BlockToken), ListToken(ListToken), Invocation(Invocation), } impl Expression { pub fn is_list(&self) -> bool { self.tokens.iter().all(|t| { match t.value { ExpressionToken::IntegerToken(_) => true, ExpressionToken::Invocation(_) => true, ExpressionToken::ListToken(_) => false, ExpressionToken::Operator(_) => false, } }) } pub fn to_list(self) -> Vec> { let mut list = Vec::new(); for token in self.tokens { let source = token.source; match token.value { ExpressionToken::IntegerToken(token) => { let tracked = Tracked::from(*token, source); list.push(tracked); } ExpressionToken::Invocation(invocation) => { let token = IntegerToken::Invocation(invocation); list.push(Tracked::from(token, source)); } ExpressionToken::ListToken(_) => unreachable!( "Could not convert expression containing a list token to a list"), ExpressionToken::Operator(_) => unreachable!( "Could not convert expression containing an operator to a list"), }; } return list; } } pub enum SemanticError { MisplacedSeparator, MisplacedMacroDefinition, ExpectedInteger(SemanticLocation), ExpectedBlock(SemanticLocation), ExpectedString(SemanticLocation), InvalidArgumentDefinition, InvalidInvocationArgument, InvalidBlockInExpression, GlobalLabelInMacroDefinition, LocalLabelWithoutNamespace, 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::MisplacedSeparator => "Separators can only be used for constructing an argument list", SemanticError::MisplacedMacroDefinition => "Macro definition must be placed at the outermost level of a program", SemanticError::ExpectedInteger(location) => &format!("An integer value was expected {location}"), SemanticError::ExpectedBlock(location) => &format!("A block value was expected {location}"), SemanticError::ExpectedString(location) => &format!("A string value was expected {location}"), SemanticError::InvalidArgumentDefinition => "Argument definition must take one of the following forms: name, {name}, or [name]", SemanticError::InvalidInvocationArgument => "This token cannot be used in an invocation argument", SemanticError::InvalidBlockInExpression => "Expression cannot contain a block token", SemanticError::GlobalLabelInMacroDefinition => &format!("Macro definition cannot contain a global label"), SemanticError::LocalLabelWithoutNamespace => &format!("Local label must be placed inside a macro definition or after a global label"), SemanticError::LocalSymbolWithoutNamespace => &format!("Local symbol must be placed inside a macro definition or after a global 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::List(list) => { print_list_token(i+1, list); } MacroDefinitionBody::Invocation(invocation) => { print_invocation(i+1, invocation); } } } SemanticToken::BlockToken(block) => print_block_token(i, 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) } ArgumentType::List => { indent!(i, "Argument({}, list)", 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::ListToken(list) => { print_list_token(i, list) } 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_list_token(i: usize, string: &ListToken) { match string { ListToken::StringLiteral(string_literal) => { indent!(i, "StringLiteral({string_literal})") } ListToken::ListLiteral(integers) => { indent!(i, "ListLiteral"); for integer in integers { print_integer_token(i+1, integer); } } ListToken::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::ListToken(list) => { print_list_token(i+1, &list) } ExpressionToken::Invocation(invocation) => { print_invocation(i+1, &invocation); } ExpressionToken::Operator(operator) => { indent!(i+1, "Operator({operator})") } } } }