summaryrefslogtreecommitdiff
path: root/src/stages/semantic.rs
blob: f2774a4bb9d56e8b82a97deb819d6bb6eb457469 (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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use crate::*;

use std::collections::{HashMap, HashSet};


pub fn parse_semantic(syntactic: Vec<Tracked<SyntacticToken>>) -> Result<Program, Vec<Tracked<SemanticError>>> {
    // Record all label definitions and macro names up front.
    let mut definitions = HashMap::new();
    let mut macro_names = HashSet::new();
    for token in &syntactic {
        match &token.value {
            SyntacticToken::LabelDefinition(name) => {
                // Use a fake index for now.
                let definition = Definition::new(0, DefinitionVariant::LabelDefinition);
                let tracked = Tracked::from(definition, token.source.clone());
                if let Some(_) = definitions.insert(name.clone(), tracked) {
                    unreachable!("Uncaught duplicate label definition '{name}'");
                }
            }
            SyntacticToken::MacroDefinition(definition) => {
                let name = &definition.name;
                if !macro_names.insert(name.clone()) {
                    unreachable!("Uncaught duplicate macro definition '{name}'")
                }
            }
            _ => (),
        }
    }

    // Convert syntactic tokens to semantic tokens.
    let mut tokens: Vec<Tracked<SemanticToken>> = Vec::new();
    let mut errors = Vec::new();
    let mut stack = Vec::new();

    for syn_token in syntactic {
        let i = tokens.len();
        let sem_token = match syn_token.value {
            SyntacticToken::Literal(value) => SemanticToken::Literal(value),
            SyntacticToken::Pad(value) => SemanticToken::Pad(value),
            SyntacticToken::String(bytes) => SemanticToken::String(bytes),
            SyntacticToken::Comment(string) => SemanticToken::Comment(string),
            SyntacticToken::BlockOpen => {
                stack.push(i);
                // Use a fake index for now.
                SemanticToken::BlockOpen(0)
            }
            SyntacticToken::BlockClose => {
                let Some(k) = stack.pop() else {
                    unreachable!("Uncaught unmatched block terminator");
                };
                // Replace fake index with real index.
                tokens[k].value = SemanticToken::BlockOpen(i);
                SemanticToken::BlockClose(k)
            }
            SyntacticToken::Symbol(symbol) => {
                if let Some(definition) = definitions.get_mut(&symbol) {
                    definition.value.references.push(i);
                } else if let Some(definition) = macro_names.get(&symbol) {
                    let error = SemanticError::InvocationBeforeDefinition;
                    let source = syn_token.source.wrap(definition.source.clone());
                    errors.push(Tracked::from(error, source));
                } else {
                    unreachable!("Uncaught undefined symbol '{symbol}'");
                };
                SemanticToken::Symbol(symbol)
            }
            SyntacticToken::Instruction(instruction) => SemanticToken::Instruction(instruction),
            SyntacticToken::LabelDefinition(name) => {
                let definition = definitions.get_mut(&name).unwrap();
                // Replace fake index with real index.
                definition.value.definition = i;
                SemanticToken::LabelDefinition(name)
            }
            SyntacticToken::MacroDefinition(definition) => {
                let name = definition.name.clone();
                let mut body: Vec<Tracked<SemanticToken>> = Vec::new();
                let mut body_stack = Vec::new();
                for syn_token in definition.body {
                    let j = body.len();
                    let sem_token = match syn_token.value {
                        SyntacticToken::Literal(value) => SemanticToken::Literal(value),
                        SyntacticToken::Pad(value) => SemanticToken::Pad(value),
                        SyntacticToken::String(bytes) => SemanticToken::String(bytes),
                        SyntacticToken::Comment(string) => SemanticToken::Comment(string),
                        SyntacticToken::BlockOpen => {
                            body_stack.push(j);
                            // Use a fake index for now.
                            SemanticToken::BlockOpen(0)
                        }
                        SyntacticToken::BlockClose => {
                            let Some(k) = body_stack.pop() else {
                                unreachable!("Uncaught unmatched block terminator in macro '{name}'");
                            };
                            // Replace fake index with real index.
                            body[k].value = SemanticToken::BlockOpen(j);
                            SemanticToken::BlockClose(k)
                        }
                        SyntacticToken::Symbol(symbol) => {
                            if let Some(definition) = definitions.get_mut(&symbol) {
                                definition.value.deep_references.push((i, j));
                            } else if let Some(definition) = macro_names.get(&symbol) {
                                let error = SemanticError::InvocationBeforeDefinition;
                                let source = syn_token.source.wrap(definition.source.clone());
                                errors.push(Tracked::from(error, source));
                            } else {
                                unreachable!("Uncaught undefined symbol '{symbol}' in macro '{name}'");
                            };
                            SemanticToken::Symbol(symbol)
                        }
                        SyntacticToken::Instruction(instruction) => SemanticToken::Instruction(instruction),
                        SyntacticToken::LabelDefinition(label) =>
                            unreachable!("Uncaught label definition '{label}' in macro '{name}'"),
                        SyntacticToken::MacroDefinition(definition) =>
                            unreachable!("Uncaught macro definition '{}' in macro '{name}'", definition.name),
                    };
                    body.push(Tracked::from(sem_token, syn_token.source));
                }

                let variant = DefinitionVariant::MacroDefinition(body);
                let source = definition.name.source.clone();
                let tracked = Tracked::from(Definition::new(i, variant), source);
                if let Some(_) = definitions.insert(name.value.clone(), tracked) {
                    unreachable!("Uncaught duplicate definition '{name}'")
                };
                if !body_stack.is_empty() {
                    unreachable!("Uncaught unterminated block in macro '{name}'");
                }
                SemanticToken::MacroDefinition(name)
            }
        };
        tokens.push(Tracked::from(sem_token, syn_token.source));
    }

    if !stack.is_empty() {
        unreachable!("Uncaught unterminated block");
    }
    match errors.is_empty() {
        true => Ok(Program { definitions, tokens }),
        false => Err(errors),
    }
}