From 4e8fae09f0f7d6f3a4ddbe285aeb01ef0622b761 Mon Sep 17 00:00:00 2001
From: Ben Bridle <bridle.benjamin@gmail.com>
Date: Sat, 15 Feb 2025 08:20:18 +1300
Subject: Implement semantic error reporting

---
 src/tokens/semantic.rs | 89 +++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 73 insertions(+), 16 deletions(-)

(limited to 'src/tokens')

diff --git a/src/tokens/semantic.rs b/src/tokens/semantic.rs
index 66db7b2..7d5d327 100644
--- a/src/tokens/semantic.rs
+++ b/src/tokens/semantic.rs
@@ -68,6 +68,7 @@ pub struct ReferenceDefinition {
 pub struct Invocation {
     pub name: String,
     pub arguments: Vec<DefinitionVariant>,
+    pub errors: Vec<ParseError>,
 }
 
 pub struct ParseError {
@@ -76,7 +77,7 @@ pub struct ParseError {
 }
 
 pub enum ParseErrorVariant {
-    UnterminatedMacroDefinition,
+    UnterminatedMacroDefinition(String),
     UnterminatedBlockDefinition,
     /// Name of the macro.
     InvalidArgumentDefinition(String),
@@ -86,29 +87,85 @@ pub enum ParseErrorVariant {
 
 // ------------------------------------------------------------------------ //
 
+macro_rules! indent {
+    ($indent:expr => $($tokens:tt)*) => {{
+        for _ in 0..$indent { print!("  "); }
+        println!($($tokens)*);
+    }};
+}
+
 impl Program {
     pub fn print_definitions(&self) {
         for definition in &self.definitions {
-            let variant = match definition.variant {
-                DefinitionVariant::Integer(_) => "integer",
-                DefinitionVariant::Block(_) => "block",
-                DefinitionVariant::Reference(_) => "reference",
+            let variant = match &definition.variant {
+                DefinitionVariant::Integer(_) => "INTEGER",
+                DefinitionVariant::Block(_) => "BLOCK",
+                DefinitionVariant::Reference(_) => "REFERENCE",
             };
-            println!("DEFINE {} ({variant})", definition.name);
+            println!("DEFINE {variant} '{}'", definition.name);
             for argument in &definition.arguments {
-                let variant = match argument.variant {
-                    ArgumentDefinitionVariant::Integer => "integer",
-                    ArgumentDefinitionVariant::Block => "block",
-                };
-                println!("  ARGUMENT {} ({variant})", argument.name);
+                self.print_argument_definition(argument);
             }
-            let variant = match &definition.variant {
-                DefinitionVariant::Integer(integer) => todo!(),
-                DefinitionVariant::Block(block) => todo!(),
-                DefinitionVariant::Reference(reference) => todo!()
+            match &definition.variant {
+                DefinitionVariant::Integer(integer) =>
+                    self.print_integer_definition(1, integer),
+                DefinitionVariant::Block(block) =>
+                    self.print_block_definition(1, block),
+                DefinitionVariant::Reference(reference) =>
+                    indent!(1 => "REFERENCE '{}'", reference.name),
             };
-
             println!();
         }
+
+        for invocation in &self.invocations {
+            self.print_invocation(0, invocation);
+        }
+    }
+
+    fn print_argument_definition(&self, argument: &ArgumentDefinition) {
+        let variant = match argument.variant {
+            ArgumentDefinitionVariant::Integer => "INTEGER",
+            ArgumentDefinitionVariant::Block => "BLOCK",
+        };
+        println!("  ARGUMENT {variant} '{}'", argument.name);
+    }
+
+    fn print_integer_definition(&self, indent: usize, definition: &IntegerDefinition) {
+        match &definition.variant {
+            IntegerDefinitionVariant::Literal(value) =>
+                indent!(indent => "LITERAL {value}"),
+            IntegerDefinitionVariant::Constant(expr) =>
+                indent!(indent => "CONSTANT [{expr:?}]"),
+        }
+    }
+
+    fn print_block_definition(&self, indent: usize, definition: &BlockDefinition) {
+        indent!(indent => "BLOCK");
+        let indent = indent + 1;
+        for token in &definition.tokens {
+            match &token.variant {
+                BlockTokenVariant::Invocation(invocation) =>
+                    self.print_invocation(indent, invocation),
+                BlockTokenVariant::Comment(_) =>
+                    indent!(indent => "COMMENT"),
+                BlockTokenVariant::Word(word) =>
+                    indent!(indent => "WORD #{word}"),
+            }
+        }
+    }
+
+    fn print_invocation(&self, indent: usize, invocation: &Invocation) {
+        indent!(indent => "INVOCATION '{}'", invocation.name);
+        let indent = indent + 1;
+        for argument in &invocation.arguments {
+            match &argument {
+                DefinitionVariant::Integer(integer) =>
+                    self.print_integer_definition(indent, integer),
+                DefinitionVariant::Block(block) =>
+                    self.print_block_definition(indent, block),
+                DefinitionVariant::Reference(reference) =>
+                    indent!(indent => "REFERENCE '{}'", reference.name),
+            };
+        }
     }
 }
-- 
cgit v1.2.3-70-g09d2