From 67470aea034fd46f4bbcfe815c51ad3451043188 Mon Sep 17 00:00:00 2001
From: Ben Bridle <ben@derelict.engineering>
Date: Thu, 27 Feb 2025 14:53:21 +1300
Subject: Finish first working version of Torque

This is a huge and messy commit, worked on piecemeal while traveling
and while the language was still being designed.
---
 src/tokens/assembler.rs             | 140 +++++++++++++++++++++++++++
 src/tokens/bytecode.rs              |  49 ++++++++++
 src/tokens/constant_expression.rs   | 134 --------------------------
 src/tokens/expression.rs            |  74 +++++++++++++++
 src/tokens/mod.rs                   |  22 +++--
 src/tokens/packed_binary_literal.rs |  11 +--
 src/tokens/semantic.rs              | 184 ++++++++++++++++++++----------------
 src/tokens/syntactic.rs             |  32 +++----
 src/tokens/tracked.rs               |  47 +++++++++
 src/tokens/tracked_integer.rs       |  14 +++
 10 files changed, 461 insertions(+), 246 deletions(-)
 create mode 100644 src/tokens/assembler.rs
 create mode 100644 src/tokens/bytecode.rs
 delete mode 100644 src/tokens/constant_expression.rs
 create mode 100644 src/tokens/expression.rs
 create mode 100644 src/tokens/tracked.rs
 create mode 100644 src/tokens/tracked_integer.rs

(limited to 'src/tokens')

diff --git a/src/tokens/assembler.rs b/src/tokens/assembler.rs
new file mode 100644
index 0000000..04ecd38
--- /dev/null
+++ b/src/tokens/assembler.rs
@@ -0,0 +1,140 @@
+use crate::*;
+
+
+#[derive(Clone)]
+pub enum AssembledToken {
+    Word(AssembledWord),
+    LabelDefinition(LabelDefinition),
+    PinnedAddress(PinnedAddress),
+    Error(AssemblerError),
+}
+
+#[derive(Clone)]
+pub struct AssembledWord {
+    pub source: SourceSpan,
+    pub value: usize,
+    pub bits: usize,
+    pub fields: Vec<AssembledField>,
+    pub errors: Vec<AssemblerError>,
+}
+
+#[derive(Clone)]
+pub struct AssembledField {
+    pub source: SourceSpan,
+    pub value: IntegerArgument,
+    /// Length of field in bits
+    pub bits: usize,
+    /// Distance to left-shift field in value
+    pub shift: usize,
+}
+
+#[derive(Clone)]
+pub struct AssembledExpression {
+    pub source: SourceSpan,
+    pub tokens: Vec<AssembledExpressionToken>,
+}
+
+#[derive(Clone)]
+pub enum AssembledExpressionToken {
+    Integer(TrackedInteger),
+    LabelReference(Tracked<String>),
+    Operator(Operator),
+    Expression(Box<AssembledExpression>),
+}
+
+#[derive(Clone)]
+pub enum Argument {
+    Integer(IntegerArgument),
+    Block(Vec<AssembledToken>),
+}
+
+#[derive(Clone)]
+pub enum IntegerArgument {
+    LabelReference(Tracked<String>),
+    Integer(TrackedInteger),
+    Expression(AssembledExpression),
+}
+
+#[derive(Clone)]
+pub struct AssemblerError {
+    pub source: SourceSpan,
+    pub variant: AssemblerErrorVariant,
+}
+
+#[derive(Clone, Debug)]
+pub enum AssemblerErrorVariant {
+    DefinitionNotFound(String),
+    NotAnInteger,
+    NotABlock,
+    IntegerInBlock,
+    /// expected, received
+    IncorrectArgumentCount(usize, usize),
+    /// expected, received, index
+    IncorrectArgumentType(ArgumentVariant, ArgumentVariant),
+}
+
+// ------------------------------------------------------------------------ //
+
+macro_rules! indent {
+    ($indent:expr => $($tokens:tt)*) => {{
+        for _ in 0..$indent { print!("  "); }
+        println!($($tokens)*);
+    }};
+}
+
+pub fn print_assembled_tokens(tokens: &[AssembledToken]) {
+    println!();
+    println!("--------------------------------------------------------------");
+    println!();
+    for token in tokens {
+        match token {
+            AssembledToken::LabelDefinition(definition) => {
+                println!("LABEL {}", definition.name)
+            }
+            AssembledToken::PinnedAddress(address) => {
+                println!("PINNED {}", address.address)
+            }
+            AssembledToken::Word(word) => {
+                println!("WORD {:b}", word.value);
+                for field in &word.fields {
+                    print!("  FIELD ({} << {}) ", field.bits, field.shift);
+                    match &field.value {
+                        IntegerArgument::LabelReference(name) => {
+                            println!("LABEL '{name}'");
+                        }
+                        IntegerArgument::Integer(integer) => {
+                            println!("INTEGER '{}'", integer.value);
+                        }
+                        IntegerArgument::Expression(expr) => {
+                            println!("EXPRESSION");
+                            print_assembled_expression(2, expr);
+                        }
+                    }
+                }
+            }
+            AssembledToken::Error(error) => {
+                println!("ERROR {:?}", error.variant)
+            }
+        }
+    }
+}
+
+fn print_assembled_expression(indent: usize, expr: &AssembledExpression) {
+    for token in &expr.tokens {
+        match token {
+            AssembledExpressionToken::Integer(integer) => {
+                indent!(indent => "INTEGER {}", integer.value)
+            }
+            AssembledExpressionToken::LabelReference(name) => {
+                indent!(indent => "LABEL '{name}'")
+            }
+            AssembledExpressionToken::Operator(operator) => {
+                indent!(indent => "OPERATOR {operator:?}")
+            }
+            AssembledExpressionToken::Expression(expr) => {
+                indent!(indent => "EXPRESSION");
+                print_assembled_expression(indent+1, expr);
+            }
+        }
+    }
+}
diff --git a/src/tokens/bytecode.rs b/src/tokens/bytecode.rs
new file mode 100644
index 0000000..9ac340e
--- /dev/null
+++ b/src/tokens/bytecode.rs
@@ -0,0 +1,49 @@
+use crate::*;
+
+
+pub struct Bytecode {
+    pub words: Vec<Word>,
+    pub errors: Vec<BytecodeError>,
+}
+
+#[derive(Clone, Copy)]
+pub struct Word {
+    pub bits: usize,
+    pub value: usize,
+}
+
+impl std::fmt::Display for Word {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+        for i in (0..self.bits).rev() {
+            let is_first_bit = i+1 == self.bits;
+            if !is_first_bit && (i+1) % 4 == 0 {
+                write!(f, "_")?;
+            }
+            match (self.value >> i) & 1 {
+                0 => write!(f, "0")?,
+                _ => write!(f, "1")?,
+            }
+        }
+        if self.bits == 0 {
+            write!(f, "0")?;
+        }
+        return Ok(());
+    }
+}
+
+pub struct BytecodeError {
+    pub source: SourceSpan,
+    pub variant: BytecodeErrorVariant,
+}
+
+pub enum BytecodeErrorVariant {
+    DefinitionNotFound(String),
+    DuplicateLabelDefinition(String),
+    /// pin, real
+    PinnedAddressBacktrack(usize, usize),
+    /// expected, received
+    ValueTooLarge(usize, usize),
+    StackUnderflow,
+    MultipleReturnValues,
+    NoReturnValue,
+}
diff --git a/src/tokens/constant_expression.rs b/src/tokens/constant_expression.rs
deleted file mode 100644
index e4aa099..0000000
--- a/src/tokens/constant_expression.rs
+++ /dev/null
@@ -1,134 +0,0 @@
-use crate::*;
-
-
-pub struct ConstantExpression {
-    pub tokens: Vec<ConstantExpressionToken>,
-}
-
-impl ConstantExpression {
-    pub fn from_str(string: &str, tokeniser: &Tokeniser) -> Self {
-        parse_constant_expression(string, tokeniser)
-    }
-}
-
-pub struct ConstantExpressionToken {
-    pub source: SourceSpan,
-    pub variant: ConstantExpressionTokenVariant,
-}
-
-pub enum ConstantExpressionTokenVariant {
-    SymbolReference(String),
-    IntegerLiteral(usize),
-    Operator(Operator),
-    Error(ConstantExpressionParseError),
-}
-
-pub enum Operator {
-    Equal,
-    NotEqual,
-    LessThan,
-    GreaterThan,
-    Add,
-    Subtract,
-    LeftShift,
-    RightShift,
-    And,
-    Or,
-    Xor,
-    Not,
-}
-
-pub enum ConstantExpressionParseError {
-    InvalidHexadecimalLiteral(String),
-}
-
-
-impl ConstantExpression {
-    pub fn evaluate(&self, environment: &Environment) -> Result<usize, ConstantExpressionEvaluationError> {
-        use ConstantExpressionTokenVariant as Token;
-        use ConstantExpressionEvaluationError as EvalErr;
-
-        let mut stack = Vec::new();
-        macro_rules! push {
-            ($value:expr) => { stack.push($value) };
-        }
-        macro_rules! pop {
-            ($name:ident) => { let $name = match stack.pop() {
-                Some(value) => value,
-                None => return Err(EvalErr::StackUnderflow),
-            }; };
-        }
-        macro_rules! truth {
-            ($bool:expr) => { match $bool { true => 1, false => 0 } };
-        }
-
-        for token in &self.tokens {
-            match &token.variant {
-                Token::IntegerLiteral(value) => push!(*value),
-                Token::SymbolReference(name) => match environment.get_integer(name) {
-                    Ok(value) => push!(value),
-                    Err(_) => todo!(),
-                }
-                Token::Operator(operator) => match operator {
-                    Operator::Equal       => { pop!(b); pop!(a); push!(truth!(a==b)) },
-                    Operator::NotEqual    => { pop!(b); pop!(a); push!(truth!(a!=b)) },
-                    Operator::LessThan    => { pop!(b); pop!(a); push!(truth!(a < b)) },
-                    Operator::GreaterThan => { pop!(b); pop!(a); push!(truth!(a > b)) },
-                    Operator::Add         => { pop!(b); pop!(a); push!(a + b) },
-                    Operator::Subtract    => { pop!(b); pop!(a); push!(a - b) },
-                    Operator::LeftShift   => { pop!(b); pop!(a); push!(a << b) },
-                    Operator::RightShift  => { pop!(b); pop!(a); push!(a >> b) },
-                    Operator::And         => { pop!(b); pop!(a); push!(a & b) },
-                    Operator::Or          => { pop!(b); pop!(a); push!(a | b) },
-                    Operator::Xor         => { pop!(b); pop!(a); push!(a ^ b) },
-                    Operator::Not         => {          pop!(a); push!(!a) },
-                }
-                Token::Error(_) => (),
-            }
-        }
-        match stack.len() {
-            0 => Err(EvalErr::NoReturnValue),
-            1 => Ok(stack[0]),
-            _ => Err(EvalErr::MultipleReturnValues),
-        }
-    }
-}
-
-pub enum ConstantExpressionEvaluationError {
-    StackUnderflow,
-    MultipleReturnValues,
-    NoReturnValue,
-}
-
-
-impl std::fmt::Debug for ConstantExpression {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
-        use ConstantExpressionTokenVariant as TokenVar;
-        for (i, token) in self.tokens.iter().enumerate() {
-            let string = match &token.variant {
-                TokenVar::SymbolReference(name) => name,
-                TokenVar::IntegerLiteral(value) => &value.to_string(),
-                TokenVar::Operator(operator) => match operator {
-                    Operator::Equal       => "=",
-                    Operator::NotEqual    => "!",
-                    Operator::LessThan    => "<",
-                    Operator::GreaterThan => ">",
-                    Operator::Add         => "+",
-                    Operator::Subtract    => "-",
-                    Operator::LeftShift   => "<<",
-                    Operator::RightShift  => ">>",
-                    Operator::And         => "&",
-                    Operator::Or          => "|",
-                    Operator::Xor         => "^",
-                    Operator::Not         => "~",
-                }
-                TokenVar::Error(_) => "<error>",
-            };
-            match i {
-                0 => write!(f, "{string}")?,
-                _ => write!(f, " {string}")?,
-            }
-        }
-        return Ok(());
-    }
-}
diff --git a/src/tokens/expression.rs b/src/tokens/expression.rs
new file mode 100644
index 0000000..ff2d82d
--- /dev/null
+++ b/src/tokens/expression.rs
@@ -0,0 +1,74 @@
+use crate::*;
+
+
+#[derive(Clone)]
+pub struct Expression {
+    pub source: SourceSpan,
+    pub tokens: Vec<ExpressionToken>,
+}
+
+#[derive(Clone)]
+pub struct ExpressionToken {
+    pub source: SourceSpan,
+    pub variant: ExpressionTokenVariant,
+}
+
+#[derive(Clone)]
+pub enum ExpressionTokenVariant {
+    Invocation(String),
+    Literal(isize),
+    Operator(Operator),
+    Error(ExpressionParseError),
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum Operator {
+    Equal,
+    NotEqual,
+    LessThan,
+    GreaterThan,
+    Add,
+    Subtract,
+    LeftShift,
+    RightShift,
+    And,
+    Or,
+    Xor,
+    Not,
+}
+
+#[derive(Clone)]
+pub enum ExpressionParseError {
+    InvalidHexadecimalLiteral(String),
+}
+
+impl std::fmt::Debug for Expression {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+        for (i, token) in self.tokens.iter().enumerate() {
+            let string = match &token.variant {
+                ExpressionTokenVariant::Invocation(name) => name,
+                ExpressionTokenVariant::Literal(value) => &value.to_string(),
+                ExpressionTokenVariant::Operator(operator) => match operator {
+                    Operator::Equal       => "=",
+                    Operator::NotEqual    => "!=",
+                    Operator::LessThan    => "<",
+                    Operator::GreaterThan => ">",
+                    Operator::Add         => "+",
+                    Operator::Subtract    => "-",
+                    Operator::LeftShift   => "<<",
+                    Operator::RightShift  => ">>",
+                    Operator::And         => "&",
+                    Operator::Or          => "|",
+                    Operator::Xor         => "^",
+                    Operator::Not         => "~",
+                }
+                ExpressionTokenVariant::Error(_) => "<error>",
+            };
+            match i {
+                0 => write!(f, "{string}")?,
+                _ => write!(f, " {string}")?,
+            }
+        }
+        return Ok(());
+    }
+}
diff --git a/src/tokens/mod.rs b/src/tokens/mod.rs
index edb7c19..53ccc6e 100644
--- a/src/tokens/mod.rs
+++ b/src/tokens/mod.rs
@@ -1,9 +1,19 @@
-pub mod syntactic;
+mod expression;
+mod packed_binary_literal;
+mod tracked_integer;
+mod tracked;
 
-pub mod semantic;
+pub use expression::*;
+pub use packed_binary_literal::*;
+pub use tracked_integer::*;
+pub use tracked::*;
 
-mod constant_expression;
-pub use constant_expression::*;
+mod syntactic;
+mod semantic;
+mod assembler;
+mod bytecode;
 
-mod packed_binary_literal;
-pub use packed_binary_literal::*;
+pub use syntactic::*;
+pub use semantic::*;
+pub use assembler::*;
+pub use bytecode::*;
diff --git a/src/tokens/packed_binary_literal.rs b/src/tokens/packed_binary_literal.rs
index 1252398..a2720b7 100644
--- a/src/tokens/packed_binary_literal.rs
+++ b/src/tokens/packed_binary_literal.rs
@@ -2,17 +2,13 @@ use crate::*;
 
 
 pub struct PackedBinaryLiteral {
+    pub source: SourceSpan,
     pub value: usize,
+    pub bits: usize,
     pub fields: Vec<BitField>,
     pub errors: Vec<PackedBinaryLiteralParseError>,
 }
 
-impl PackedBinaryLiteral {
-    pub fn from_str(string: &str, parent: &Tokeniser) -> Self {
-        parse_packed_binary_literal(string, parent)
-    }
-}
-
 pub struct BitField {
     pub name: char,
     pub source: SourceSpan,
@@ -40,7 +36,8 @@ impl std::fmt::Display for PackedBinaryLiteral {
         } else {
             let bitcount = (self.value.ilog2() + 1) as usize;
             'bit: for i in (0..bitcount).rev() {
-                if (i+1) % 4 == 0 {
+                let is_first_bit = i+1 == bitcount;
+                if !is_first_bit && (i+1) % 4 == 0 {
                     write!(f, "_")?;
                 }
                 for field in &self.fields {
diff --git a/src/tokens/semantic.rs b/src/tokens/semantic.rs
index 7d5d327..d61ad8e 100644
--- a/src/tokens/semantic.rs
+++ b/src/tokens/semantic.rs
@@ -1,90 +1,100 @@
 use crate::*;
 
+use indexmap::IndexMap;
+
 
 /// The entire semantic program, ready to generate bytecode.
-pub struct Program {
-    pub definitions: Vec<Definition>,
-    pub invocations: Vec<Invocation>,
-    pub errors: Vec<ParseError>,
+pub struct SemanticProgram {
+    pub macro_definitions: IndexMap<String, MacroDefinition>,
+    pub label_definitions: IndexMap<String, LabelDefinition>,
+    pub body: Vec<SemanticToken>,
 }
 
 /// A symbol definition.
-pub struct Definition {
-    pub name: String,
+pub struct MacroDefinition {
     pub source: SourceSpan,
     pub arguments: Vec<ArgumentDefinition>,
-    pub variant: DefinitionVariant,
+    pub value: Value,
+    pub errors: Vec<SemanticParseError>,
 }
 
 pub struct ArgumentDefinition {
     pub name: String,
     pub source: SourceSpan,
-    pub variant: ArgumentDefinitionVariant,
+    pub variant: ArgumentVariant,
 }
 
-pub enum ArgumentDefinitionVariant {
+#[derive(PartialEq, Clone, Copy, Debug)]
+pub enum ArgumentVariant {
     Integer,
     Block,
 }
 
-pub enum DefinitionVariant {
-    Integer(IntegerDefinition),
-    Block(BlockDefinition),
-    Reference(ReferenceDefinition),
-}
-
-pub struct IntegerDefinition {
+pub struct ArgumentInvocation {
     pub source: SourceSpan,
-    pub variant: IntegerDefinitionVariant,
+    pub value: Value,
 }
 
-pub enum IntegerDefinitionVariant {
-    Literal(usize),
-    Constant(ConstantExpression),
+pub enum Value {
+    Integer(Integer),
+    Block(Vec<SemanticToken>),
+    Invocation(Invocation),
 }
 
-pub struct BlockDefinition {
-    pub tokens: Vec<BlockToken>,
-    pub errors: Vec<ParseError>,
+pub enum Integer {
+    Literal(TrackedInteger),
+    Expression(Expression),
+    LabelReference(Tracked<String>),
 }
 
-pub struct BlockToken {
-    pub source: SourceSpan,
-    pub variant: BlockTokenVariant,
+pub enum SemanticToken {
+    Word(PackedBinaryLiteral),
+    Invocation(Invocation),
+    LabelDefinition(LabelDefinition),
+    PinnedAddress(PinnedAddress),
+    Error(SemanticParseError),
 }
 
-pub enum BlockTokenVariant {
-    Invocation(Invocation),
-    Comment(String),
-    Word(PackedBinaryLiteral),
+pub struct Invocation {
+    pub name: String,
+    pub source: SourceSpan,
+    pub arguments: Vec<ArgumentInvocation>,
+    pub errors: Vec<SemanticParseError>,
 }
 
-/// References aren't necessarily an integer or a block
-pub struct ReferenceDefinition {
+#[derive(Clone)]
+pub struct LabelDefinition {
     pub source: SourceSpan,
     pub name: String,
 }
 
-pub struct Invocation {
-    pub name: String,
-    pub arguments: Vec<DefinitionVariant>,
-    pub errors: Vec<ParseError>,
+#[derive(Clone)]
+pub struct PinnedAddress {
+    pub source: SourceSpan,
+    pub address: usize,
 }
 
-pub struct ParseError {
+pub struct SemanticParseError {
     pub source: SourceSpan,
-    pub variant: ParseErrorVariant,
+    pub variant: SemanticParseErrorVariant,
 }
 
-pub enum ParseErrorVariant {
+pub enum SemanticParseErrorVariant {
     UnterminatedMacroDefinition(String),
-    UnterminatedBlockDefinition,
-    /// Name of the macro.
-    InvalidArgumentDefinition(String),
+    UnterminatedBlock,
     InvalidToken,
 }
 
 
+impl std::fmt::Display for ArgumentVariant {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+        match self {
+            ArgumentVariant::Integer => write!(f, "integer"),
+            ArgumentVariant::Block => write!(f, "block"),
+        }
+    }
+}
+
 // ------------------------------------------------------------------------ //
 
 macro_rules! indent {
@@ -94,77 +104,85 @@ macro_rules! indent {
     }};
 }
 
-impl Program {
+impl SemanticProgram {
     pub fn print_definitions(&self) {
-        for definition in &self.definitions {
-            let variant = match &definition.variant {
-                DefinitionVariant::Integer(_) => "INTEGER",
-                DefinitionVariant::Block(_) => "BLOCK",
-                DefinitionVariant::Reference(_) => "REFERENCE",
+        for (name, definition) in &self.macro_definitions {
+            let variant = match &definition.value {
+                Value::Integer(_) => "INTEGER",
+                Value::Block(_) => "BLOCK",
+                Value::Invocation(_) => "INVOCATION",
             };
-            println!("DEFINE {variant} '{}'", definition.name);
+            println!("DEFINE {variant} '{name}'");
             for argument in &definition.arguments {
                 self.print_argument_definition(argument);
             }
-            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),
+            match &definition.value {
+                Value::Integer(integer) =>
+                    self.print_integer(1, integer),
+                Value::Block(block) =>
+                    self.print_block(1, block),
+                Value::Invocation(invocation) =>
+                    indent!(1 => "INVOCATION '{}'", invocation.name),
             };
             println!();
         }
 
-        for invocation in &self.invocations {
-            self.print_invocation(0, invocation);
+        println!("LABELS");
+        for (name, _) in &self.label_definitions {
+            println!("  @{name}");
         }
+        println!();
+
+        self.print_block(0, &self.body);
     }
 
     fn print_argument_definition(&self, argument: &ArgumentDefinition) {
         let variant = match argument.variant {
-            ArgumentDefinitionVariant::Integer => "INTEGER",
-            ArgumentDefinitionVariant::Block => "BLOCK",
+            ArgumentVariant::Integer => "INTEGER",
+            ArgumentVariant::Block => "BLOCK",
         };
         println!("  ARGUMENT {variant} '{}'", argument.name);
     }
 
-    fn print_integer_definition(&self, indent: usize, definition: &IntegerDefinition) {
-        match &definition.variant {
-            IntegerDefinitionVariant::Literal(value) =>
+    fn print_integer(&self, indent: usize, integer: &Integer) {
+        match &integer {
+            Integer::Literal(value) =>
                 indent!(indent => "LITERAL {value}"),
-            IntegerDefinitionVariant::Constant(expr) =>
-                indent!(indent => "CONSTANT [{expr:?}]"),
+            Integer::Expression(expr) =>
+                indent!(indent => "EXPRESSION [{expr:?}]"),
+            Integer::LabelReference(name) =>
+                indent!(indent => "LABEL REFERENCE '{name}'"),
         }
     }
 
-    fn print_block_definition(&self, indent: usize, definition: &BlockDefinition) {
+    fn print_block(&self, indent: usize, block: &[SemanticToken]) {
         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}"),
+        for semantic_token in block {
+            match &semantic_token {
+                SemanticToken::Word(word) =>
+                    indent!(indent+1 => "WORD #{word}"),
+                SemanticToken::Invocation(invocation) =>
+                    self.print_invocation(indent+1, invocation),
+                SemanticToken::LabelDefinition(definition) =>
+                    indent!(indent+1 => "LABEL DEFINITION @{}", definition.name),
+                SemanticToken::PinnedAddress(addr) =>
+                    indent!(indent+1 => "PINNED ADDRESS {}", addr.address),
+                SemanticToken::Error(_) =>
+                    indent!(indent+1 => "ERROR"),
             }
         }
     }
 
     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),
+            match &argument.value {
+                Value::Integer(integer) =>
+                    self.print_integer(indent+1, integer),
+                Value::Block(block) =>
+                    self.print_block(indent+1, block),
+                Value::Invocation(invocation) =>
+                    self.print_invocation(indent+1, invocation),
             };
         }
     }
diff --git a/src/tokens/syntactic.rs b/src/tokens/syntactic.rs
index 162f1c0..eb33806 100644
--- a/src/tokens/syntactic.rs
+++ b/src/tokens/syntactic.rs
@@ -1,22 +1,21 @@
 use crate::*;
 
 
-pub struct Token {
+pub struct SyntacticToken {
     pub source: SourceSpan,
-    pub variant: TokenVariant,
+    pub variant: SyntacticTokenVariant,
 }
 
-pub enum TokenVariant {
+pub enum SyntacticTokenVariant {
     LabelDefinition(String),
     MacroDefinition(String),
     MacroDefinitionTerminator,
 
-    DecimalLiteral(usize),
-    HexadecimalLiteral(usize),
+    IntegerLiteral(isize),
     PackedBinaryLiteral(PackedBinaryLiteral),
+    PinnedAddress(usize),
 
-    Comment(String),
-    ConstantExpression(ConstantExpression),
+    Expression(Expression),
 
     BlockOpen,
     BlockClose,
@@ -24,33 +23,34 @@ pub enum TokenVariant {
 
     Symbol(String),
 
-    Error(ParseError),
+    Error(SyntacticParseError),
 }
 
 #[derive(Debug)]
-pub enum ParseError {
+pub enum SyntacticParseError {
     InvalidHexadecimalLiteral(String),
+    InvalidDecimalLiteral(String),
     InvalidSymbolIdentifier(String),
     UnterminatedComment,
-    UnterminatedConstantExpression,
+    UnterminatedExpression,
+    LabelInMacroDefinition,
 }
 
 
-impl std::fmt::Debug for Token {
+impl std::fmt::Debug for SyntacticToken {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
-        use TokenVariant::*;
+        use SyntacticTokenVariant::*;
         let start = &self.source.in_merged;
         let name = match &self.variant {
             LabelDefinition(name) => format!("LabelDefinition({name})"),
             MacroDefinition(name) => format!("MacroDefinition({name})"),
             MacroDefinitionTerminator => format!("MacroDefinitionTerminator"),
 
-            DecimalLiteral(value) => format!("DecimalLiteral({value})"),
-            HexadecimalLiteral(value) => format!("HexadecimalLiteral(0x{value:x})"),
+            IntegerLiteral(value) => format!("IntegerLiteral({value})"),
             PackedBinaryLiteral(pbl) => format!("PackedBinaryLiteral({pbl})"),
+            PinnedAddress(value) => format!("PinnedAddress({value})"),
 
-            Comment(_) => format!("Comment"),
-            ConstantExpression(expr) => format!("ConstantExpression({expr:?})"),
+            Expression(expr) => format!("Expression({expr:?})"),
 
             BlockOpen => format!("BlockOpen"),
             BlockClose => format!("BlockClose"),
diff --git a/src/tokens/tracked.rs b/src/tokens/tracked.rs
new file mode 100644
index 0000000..049c8f8
--- /dev/null
+++ b/src/tokens/tracked.rs
@@ -0,0 +1,47 @@
+use crate::*;
+
+
+#[derive(Clone)]
+pub struct Tracked<T> {
+    pub source: SourceSpan,
+    pub value: T,
+}
+
+impl<T> Tracked<T> {
+    pub fn from(value: T, source: &SourceSpan) -> Self {
+        Self { source: source.clone(), value }
+    }
+}
+
+impl<T> std::ops::Deref for Tracked<T> {
+    type Target = T;
+    fn deref(&self) -> &T {
+        &self.value
+    }
+}
+
+impl<T> std::ops::DerefMut for Tracked<T> {
+    fn deref_mut(&mut self) -> &mut T {
+        &mut self.value
+    }
+}
+
+impl<T: std::fmt::Display> std::fmt::Display for Tracked<T> {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+        write!(f, "{}", self.value)
+    }
+}
+
+impl<T: std::fmt::Debug> std::fmt::Debug for Tracked<T> {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+        write!(f, "{:?}", self.value)
+    }
+}
+
+impl<T: PartialEq> PartialEq for Tracked<T> {
+    fn eq(&self, other: &Tracked<T>) -> bool {
+        self.value.eq(&other.value)
+    }
+}
+
+impl<T: Eq> Eq for Tracked<T> {}
diff --git a/src/tokens/tracked_integer.rs b/src/tokens/tracked_integer.rs
new file mode 100644
index 0000000..fa55f09
--- /dev/null
+++ b/src/tokens/tracked_integer.rs
@@ -0,0 +1,14 @@
+use crate::*;
+
+
+#[derive(Clone)]
+pub struct TrackedInteger {
+    pub source: SourceSpan,
+    pub value: isize,
+}
+
+impl std::fmt::Display for TrackedInteger {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+        write!(f, "{}", self.value)
+    }
+}
-- 
cgit v1.2.3-70-g09d2