summaryrefslogblamecommitdiff
path: root/src/tokens/semantic.rs
blob: 225cd6bd7ce1b7919ff2bdd40a3be84c553cc652 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
             
                       
 
                                                            


                                                             
 
                        
                            
                                           
                                        
 
                               
                           
                                 
 
                                        
            
 
                               
                           
                     
 


                              
 
                            
                          
                                    
 
 




                                     
 



                                           
 
 
                            


                           


                           
 
                               
                           
                                           
 
                                    
                                        
                      
                 
 
 







                                                                               
                                                                              





                                             
                      
                                     



                                                           
              
                                                  
                                                   
                                                         
             





                                                                     
              
                       
 

                                                  
         

                                        


                                                                        
                                                  


                                                             

                                                               
                                                     
                                                           
                                                       
                                                              

         
                                                                   
                                   










                                                                                 




                                                                        
                                               





                                                                
              
     
 
use crate::*;

use indexmap::IndexMap;


/// The entire semantic program, ready to generate bytecode.
pub struct SemanticProgram {
    pub macro_definitions: IndexMap<String, MacroDefinition>,
    pub label_definitions: IndexMap<String, LabelDefinition>,
    pub body: Vec<SemanticToken>,
}

/// A symbol definition.
pub struct MacroDefinition {
    pub source: SourceSpan,
    pub arguments: Vec<ArgumentDefinition>,
    pub value: Value,
    pub errors: Vec<SemanticParseError>,
}

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<SemanticToken>),
    Invocation(Invocation),
}

pub enum Integer {
    Literal(TrackedInteger),
    String(TrackedString),
    Expression(Expression),
    LabelReference(Tracked<String>),
}

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<ArgumentInvocation>,
    pub errors: Vec<SemanticParseError>,
}

#[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),
            };
        }
    }
}