summaryrefslogtreecommitdiff
path: root/src/stages/compiler.rs
blob: cdeb601ce00fc1b81df2598a21f6fcaa55f75e24 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
use crate::*;
use assembler::SymbolRole::*;
use assembler::DefinitionType::*;


pub fn new_compiler() -> Compiler {
    Compiler::new(parse_symbols, push_code)
}


/// Parse all symbols from a source code string.
pub fn parse_symbols(source_code: &str, path: Option<&Path>) -> Option<Vec<Symbol>> {
    let syntactic = match parse_syntactic(source_code, path) {
        Ok(syntactic) => syntactic,
        Err(_errors) => return None,
    };
    Some(SymbolParser::new().parse(&syntactic))
}

/// Push source code to a source compilation string.
pub fn push_code(compilation: &mut String, source_file: &SourceFile) {
    // Skip blank files.
    let source_code = &source_file.source_code;
    if source_code.chars().all(|c| c.is_whitespace()) { return; }
    // Ensure that the previous section is followed by two newline characters.
    if !compilation.is_empty() {
        if !compilation.ends_with('\n') { compilation.push('\n'); }
        if !compilation.ends_with("\n\n") { compilation.push('\n'); }
    }
    // Push a path comment and the source code.
    let path_str = source_file.path.as_os_str().to_string_lossy();
    let path_comment = format!("(: {path_str} )\n");
    compilation.push_str(&path_comment);
    compilation.push_str(&source_code);
}


// Extract symbol definitions from a list of syntactic tokens.
pub struct SymbolParser {
    pub symbols: Vec<Symbol>,
}

impl SymbolParser {
    pub fn new() -> Self {
        Self {
            symbols: Vec::new(),
        }
    }

    fn record_symbol(&mut self, name: &str, source: &SourceSpan, role: SymbolRole) {
        let name = name.to_string();
        let namespace = Vec::new();
        let source = source.to_owned();
        self.symbols.push(Symbol { name, namespace, source, role });
    }

    pub fn parse(mut self, syntactic: &[Tracked<SyntacticToken>]) -> Vec<Symbol> {
        for token in syntactic {
            match &token.value {
                SyntacticToken::MacroDefinition(definition) => {
                    self.record_symbol(
                        &definition.name,
                        &definition.name.source,
                        Definition(MustPrecedeReference),
                    );
                    for token in &definition.body {
                        if let SyntacticToken::Invocation(name) = &token.value {
                            self.record_symbol(&name, &token.source, Reference);
                        }
                    }
                }
                SyntacticToken::LabelDefinition(name) => {
                    self.record_symbol(&name, &token.source, Definition(CanFollowReference));
                }
                SyntacticToken::Invocation(name) => {
                    self.record_symbol(&name, &token.source, Reference);
                }
                _ => (),
            }
        }
        return self.symbols;
    }
}