summaryrefslogtreecommitdiff
path: root/src/semantic_token.rs
blob: 265db9189bb0138e444ab57c806dc05674ff302a (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
use crate::*;

pub enum SemanticTokenType {
    LabelReference(usize),
    MacroReference(usize),

    LabelDefinition(LabelDefinition),
    MacroDefinition(MacroDefinition),

    Padding(u16),
    ByteLiteral(u8),
    ShortLiteral(u16),
    Instruction(u8),

    MacroDefinitionTerminator,
    Comment,
    Error(SyntacticTokenType, Error),
}

pub struct SemanticToken {
    pub r#type: SemanticTokenType,
    pub source_location: SourceLocation,
    pub bytecode_location: BytecodeLocation,
    pub parent_label: Option<String>,
}

impl SemanticToken {
    /// Returns true if an error was printed.
    pub fn print_error(&self, source_code: &str) -> bool {
        let mut is_error = false;
        macro_rules! red {()=>{eprint!("\x1b[31m")};}
        macro_rules! dim {()=>{eprint!("\x1b[0;2m")};}
        macro_rules! normal {()=>{eprint!("\x1b[0m")};}

        if let SemanticTokenType::Error(token, error) = &self.r#type {
            is_error = true;

            red!(); eprint!("[ERROR] "); normal!();
            let source = &self.source_location.source;
            match error {
                Error::UnresolvedReference => {
                    eprintln!("Unresolved reference, no label or macro has been defined with the name '{source}'") }
                Error::DuplicateDefinition => {
                    eprintln!("Duplicate definition, a label or macro has already been defined with the name '{source}'") }
                Error::OrphanedMacroDefinitionTerminator => {
                    eprintln!("Unmatched macro definition terminator, no macro definition is in progress") }
                Error::InvalidPaddingValue => {
                    eprintln!("Invalid value for padding, the value must be at least one and at most four hexadecimal characters") }
                Error::CyclicMacroReference => {
                    eprintln!("Cyclic macro reference, this macro reference contains a reference to the macro being defined") }
                Error::InvalidTypeInMacroDefinition => {
                    let name = match token {
                        SyntacticTokenType::Reference(_) => "references",
                        SyntacticTokenType::LabelDefinition(_) => "label definitions",
                        SyntacticTokenType::MacroDefinition(_) => "macro definitions",
                        SyntacticTokenType::MacroDefinitionTerminator => "macro definition terminators",
                        SyntacticTokenType::Padding(_) => "padding",
                        SyntacticTokenType::ByteLiteral(_) => "byte literals",
                        SyntacticTokenType::ShortLiteral(_) => "short literals",
                        SyntacticTokenType::Instruction(_) => "instructions",
                        SyntacticTokenType::Comment => "comments",
                    };
                    eprintln!("Invalid token in macro definition, macro definitions are not allowed to contain {name}") }
            }

            if let Some(label) = &self.parent_label {
                eprint!("  ... "); red!(); eprint!("| "); dim!(); eprintln!("@{label} "); normal!();
            }

            let line = source_code.split('\n').nth(self.source_location.start.line).unwrap();
            eprint!("{:>5} ", self.source_location.start.line+1);
            red!(); eprint!("| "); normal!();
            for (i, c) in line.chars().enumerate() {
                if i == self.source_location.start.column { red!() }
                eprint!("{c}");
                if i == self.source_location.end.column { normal!() }
            }
            eprintln!(); red!(); eprint!("      | ");
            for i in 0..=self.source_location.end.column {
                if i < self.source_location.start.column { eprint!(" ") } else { eprint!("^") };
            }
            normal!(); eprintln!();
        }
        else if let SemanticTokenType::MacroDefinition(definition) = &self.r#type {
            for token in &definition.body_tokens {
                if token.print_error(source_code) { is_error = true }
            }
        }
        is_error
    }
}

pub struct LabelDefinition {
    pub name: String,
    pub address: u16,
    /// A list of pointers to label reference tokens
    pub references: Vec<usize>,
}
impl LabelDefinition {
    pub fn new(name: String) -> Self {
        Self { name, address:0, references:Vec::new() }
    }
}

pub struct MacroDefinition {
    pub name: String,
    pub body_tokens: Vec<SemanticToken>,
    /// A list of pointers to macro reference tokens
    pub references: Vec<usize>,
}
impl MacroDefinition {
    pub fn new(name: String) -> Self {
        Self { name, body_tokens:Vec::new(), references:Vec::new() }
    }
}