summaryrefslogtreecommitdiff
path: root/src/stages/semantic_tokens.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/stages/semantic_tokens.rs')
-rw-r--r--src/stages/semantic_tokens.rs296
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})")
+ }
+ }
+ }
+}