use crate::*; /// The entire semantic program, ready to generate bytecode. pub struct Program { pub definitions: Vec<Definition>, pub invocations: Vec<Invocation>, pub errors: Vec<ParseError>, } /// A symbol definition. pub struct Definition { pub name: String, pub source: SourceSpan, pub arguments: Vec<ArgumentDefinition>, pub variant: DefinitionVariant, } pub struct ArgumentDefinition { pub name: String, pub source: SourceSpan, pub variant: ArgumentDefinitionVariant, } pub enum ArgumentDefinitionVariant { Integer, Block, } pub enum DefinitionVariant { Integer(IntegerDefinition), Block(BlockDefinition), Reference(ReferenceDefinition), } pub struct IntegerDefinition { pub source: SourceSpan, pub variant: IntegerDefinitionVariant, } pub enum IntegerDefinitionVariant { Literal(usize), Constant(ConstantExpression), } pub struct BlockDefinition { pub tokens: Vec<BlockToken>, pub errors: Vec<ParseError>, } pub struct BlockToken { pub source: SourceSpan, pub variant: BlockTokenVariant, } pub enum BlockTokenVariant { Invocation(Invocation), Comment(String), Word(PackedBinaryLiteral), } /// References aren't necessarily an integer or a block pub struct ReferenceDefinition { pub source: SourceSpan, pub name: String, } pub struct Invocation { pub name: String, pub arguments: Vec<DefinitionVariant>, pub errors: Vec<ParseError>, } pub struct ParseError { pub source: SourceSpan, pub variant: ParseErrorVariant, } pub enum ParseErrorVariant { UnterminatedMacroDefinition(String), UnterminatedBlockDefinition, /// Name of the macro. InvalidArgumentDefinition(String), InvalidToken, } // ------------------------------------------------------------------------ // 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", }; println!("DEFINE {variant} '{}'", definition.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), }; 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), }; } } }