use crate::*; use indexmap::IndexMap; /// The entire semantic program, ready to generate bytecode. pub struct SemanticProgram { pub macro_definitions: IndexMap, pub label_definitions: IndexMap, pub body: Vec, } /// A symbol definition. pub struct MacroDefinition { pub source: SourceSpan, pub arguments: Vec, pub value: Value, pub errors: Vec, } pub struct ArgumentDefinition { pub name: String, pub source: SourceSpan, pub variant: ArgumentVariant, } #[derive(PartialEq, Clone, Copy, Debug)] pub enum ArgumentVariant { Integer, Block, } pub struct ArgumentInvocation { pub source: SourceSpan, pub value: Value, } pub enum Value { Integer(Integer), Block(Vec), Invocation(Invocation), } pub enum Integer { Literal(TrackedInteger), String(TrackedString), Expression(Expression), LabelReference(Tracked), } pub enum SemanticToken { Word(PackedBinaryLiteral), Invocation(Invocation), LabelDefinition(LabelDefinition), PinnedAddress(PinnedAddress), Error(SemanticParseError), } pub struct Invocation { pub name: String, pub source: SourceSpan, pub arguments: Vec, pub errors: Vec, } #[derive(Clone)] pub struct LabelDefinition { pub source: SourceSpan, pub name: String, } #[derive(Clone)] pub struct PinnedAddress { pub source: SourceSpan, pub address: usize, } pub struct SemanticParseError { pub source: SourceSpan, pub variant: SemanticParseErrorVariant, } pub enum SemanticParseErrorVariant { UnterminatedMacroDefinition(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 { ($indent:expr => $($tokens:tt)*) => {{ for _ in 0..$indent { print!(" "); } println!($($tokens)*); }}; } impl SemanticProgram { pub fn print_definitions(&self) { for (name, definition) in &self.macro_definitions { let variant = match &definition.value { Value::Integer(_) => "INTEGER", Value::Block(_) => "BLOCK", Value::Invocation(_) => "INVOCATION", }; println!("DEFINE {variant} '{name}'"); for argument in &definition.arguments { self.print_argument_definition(argument); } 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!(); } 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 { ArgumentVariant::Integer => "INTEGER", ArgumentVariant::Block => "BLOCK", }; println!(" ARGUMENT {variant} '{}'", argument.name); } fn print_integer(&self, indent: usize, integer: &Integer) { match &integer { Integer::Literal(value) => indent!(indent => "LITERAL {value}"), Integer::Expression(expr) => indent!(indent => "EXPRESSION [{expr:?}]"), Integer::String(string) => indent!(indent => "STRING '{string}'"), Integer::LabelReference(name) => indent!(indent => "LABEL REFERENCE '{name}'"), } } fn print_block(&self, indent: usize, block: &[SemanticToken]) { indent!(indent => "BLOCK"); 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); for argument in &invocation.arguments { 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), }; } } }