summaryrefslogblamecommitdiff
path: root/src/parsers/syntactic.rs
blob: f3fcec1799c1c028b69ecd1aedc8c2969884b292 (plain) (tree)
1
2
3
4
5
6
7
8
9
10



                            
                                
                                           
                                          

                      
                                                                              
                                                              
                                                                             
                      
                               
                             
                                      
         
 

                                                   
                                    
 















                                                                        
                 


                                                                                   
                     


                                                                                      
                     






                                                               
                 










                                                                                        













                                                                          





























































                                                                                              
         
                           
     








                                                                
use crate::*;


pub struct SyntacticParser {
    tokeniser: Tokeniser,
    tokens: Vec<SyntacticToken>,
    /// The name of the macro being parsed.
    macro_name: Option<String>,
    /// The name of the most recent label.
    label_name: String,
}

impl SyntacticParser {
    pub fn new<P: Into<PathBuf>>(source_code: &str, path: Option<P>) -> Self {
        let mut tokeniser = Tokeniser::new(source_code, path);
        tokeniser.add_delimiters(&['@','%',';',':','{','}','(','[','#','~']);
        Self {
            tokeniser,
            tokens: Vec::new(),
            macro_name: None,
            label_name: String::new(),
        }
    }

    pub fn parse(mut self) -> Vec<SyntacticToken> {
        use SyntacticTokenVariant as SynVar;
        use SyntacticParseError as SynErr;
        let t = &mut self.tokeniser;

        loop {
            t.eat_whitespace();
            t.mark_start();
            let Some(c) = t.eat_char() else { break };
            let variant = match c {
                ':' => SynVar::Separator,
                '{' => SynVar::BlockOpen,
                '}' => SynVar::BlockClose,
                '@' => match &self.macro_name {
                    Some(_) => {
                        t.eat_token();
                        SynVar::Error(SynErr::LabelInMacroDefinition)
                    }
                    None => {
                        self.label_name = t.eat_token();
                        SynVar::LabelDefinition(self.label_name.clone())
                    }
                }
                '&' => match &self.macro_name {
                    Some(macro_name) => {
                        let label_name = format!("{macro_name}:{}", t.eat_token());
                        SynVar::LabelDefinition(label_name)
                    }
                    None => {
                        let label_name = &self.label_name;
                        let sublabel_name = format!("{label_name}/{}", t.eat_token());
                        SynVar::LabelDefinition(sublabel_name)
                    }
                }
                '%' => {
                    let macro_name = t.eat_token();
                    self.macro_name = Some(macro_name.clone());
                    SynVar::MacroDefinition(macro_name)
                }
                ';' => {
                    self.macro_name = None;
                    SynVar::MacroDefinitionTerminator
                }
                '[' => {
                    t.mark_child();
                    match t.eat_to_delimiter(']') {
                        Some(_) => {
                            let child = t.subtokenise();
                            t.mark_end();
                            let expr = parse_constant_expression(child, t.get_source());
                            SynVar::Expression(expr)
                        }
                        None => SynVar::Error(SynErr::UnterminatedExpression),
                    }
                }
                '"' => {
                    t.mark_child();
                    match t.eat_to_delimiter('"') {
                        Some(string) => {
                            let child = t.subtokenise();
                            t.mark_end();
                            let chars = parse_tracked_chars(child);
                            let tracked_string = TrackedString {
                                source: t.get_source(), string, chars,
                            };
                            SynVar::String(tracked_string)
                        }
                        None => SynVar::Error(SynErr::UnterminatedString),
                    }
                }
                '(' => match t.eat_to_delimiter(')') {
                    Some(string) => {
                        // Check if the comment fills the entire line.
                        if t.start.position.column == 0 && t.end_of_line() {
                            if let Some(path) = string.strip_prefix(": ") {
                                t.embedded_path = Some(PathBuf::from(path.trim()));
                                t.embedded_first_line = t.start.position.line + 1;
                            }
                        }
                        continue;
                    },
                    None => SynVar::Error(SynErr::UnterminatedComment),
                }
                '|' => {
                    let token = t.eat_token();
                    if let Some(hex_string) = token.strip_prefix("0x") {
                        match usize::from_str_radix(hex_string, 16) {
                            Ok(addr) => SynVar::PinnedAddress(addr),
                            Err(_) => SynVar::Error(SynErr::InvalidHexadecimalLiteral(token)),
                        }
                    } else {
                        match usize::from_str_radix(&token, 10) {
                            Ok(addr) => SynVar::PinnedAddress(addr),
                            Err(_) => SynVar::Error(SynErr::InvalidDecimalLiteral(token)),
                        }
                    }
                }
                '#' => {
                    t.mark_child();
                    t.eat_token();
                    let pbl = parse_packed_binary_literal(t.subtokenise(), t.get_source());
                    SynVar::PackedBinaryLiteral(pbl)
                },
                '~' => match &self.macro_name {
                    Some(macro_name) => {
                        let symbol_name = format!("{macro_name}:{}", t.eat_token());
                        SynVar::Symbol(symbol_name)
                    }
                    None => {
                        let label_name = &self.label_name;
                        let symbol_name = format!("{label_name}/{}", t.eat_token());
                        SynVar::Symbol(symbol_name)
                    }
                }
                c => {
                    let token = format!("{c}{}", t.eat_token());
                    if let Some(hex_string) = token.strip_prefix("0x") {
                        match usize::from_str_radix(hex_string, 16) {
                            Ok(value) => SynVar::IntegerLiteral(value as isize),
                            Err(_) => SynVar::Error(SynErr::InvalidHexadecimalLiteral(token)),
                        }
                    } else {
                        match usize::from_str_radix(&token, 10) {
                            Ok(value) => SynVar::IntegerLiteral(value as isize),
                            Err(_) => SynVar::Symbol(token),
                        }
                    }
                }
            };

            t.mark_end();
            let source = t.get_source();
            self.tokens.push(SyntacticToken { source, variant });
        }

        return self.tokens;
    }
}


fn parse_tracked_chars(mut t: Tokeniser) -> Vec<Tracked<char>> {
    let mut output = Vec::new();
    while let Some(c) = t.eat_char() {
        output.push(Tracked::from(c, t.get_source()));
        t.mark_start();
    }
    return output;
}