diff options
Diffstat (limited to 'src/stages/semantic_tokens.rs')
| -rw-r--r-- | src/stages/semantic_tokens.rs | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/src/stages/semantic_tokens.rs b/src/stages/semantic_tokens.rs new file mode 100644 index 0000000..dfbea1a --- /dev/null +++ b/src/stages/semantic_tokens.rs @@ -0,0 +1,296 @@ +use crate::*; + + +pub enum SemanticToken { + MacroDefinition(MacroDefinition), + BlockToken(BlockToken), +} + +pub struct MacroDefinition { + pub name: Tracked<String>, + pub arguments: Vec<Tracked<ArgumentDefinition>>, + 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<IntegerToken>), + Block(Vec<Tracked<BlockToken>>), + Invocation(Tracked<Invocation>), +} + +pub struct ConditionalBlock { + pub predicate: Tracked<IntegerToken>, + pub body: Tracked<BlockToken>, +} + +pub enum IntegerToken { + IntegerLiteral(isize), + Expression(Expression), + Invocation(Invocation), +} + +pub struct Expression { + pub tokens: Vec<Tracked<ExpressionToken>>, +} + +pub enum ExpressionToken { + IntegerToken(Box<IntegerToken>), + Invocation(Invocation), + Operator(Operator), +} + +pub enum BlockToken { + LabelDefinition(String), + PinnedAddress(Tracked<IntegerToken>), + ConditionalBlock(Box<ConditionalBlock>), + WordTemplate(WordTemplate), + Block(Vec<Tracked<BlockToken>>), + Invocation(Invocation), +} + +pub struct Invocation { + pub name: String, + pub arguments: Vec<Tracked<InvocationArgument>>, +} + +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<SemanticError>], source_code: &str) { + for error in errors { + report_semantic_error(error, source_code); + } +} + +fn report_semantic_error(error: &Tracked<SemanticError>, 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<BlockToken>]) { + 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})") + } + } + } +} |
