Website Summary Files Commits Branches @commit @diff @stats
path: root / assembler / src / lexer / mod.rs
blob: c8c8c831c3b32c1cf07ad9cd8d44c44ea0da05a3 (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
use crate::Word;

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum Token {
    Label(String),
    Operator(String),
    Symbol(String),
    SymbolDef(String),
    Number(Word),
    Address(Word),
    String(String),
}

pub fn split(mut line: String) -> Vec<Token> {
    line.push('\n');

    let mut res = Vec::new();
    let mut buffer = String::new();

    let mut has_label = false;
    let mut string_started = false;
    let mut skip_next = false;
    let mut address = false;

    for c in line.chars() {
        buffer.push(c);
        if skip_next || (string_started && c != '"') {
            skip_next = false;
            continue;
        }

        match c {
            ':' => if ! has_label {
                    buffer.pop();
                    res.push(Token::Label(buffer.clone()));
                    buffer.clear();
                    has_label = true;
                }
                else {
                    panic!("Found double labels!");
                },
            ' ' | '\t' => {
                buffer.pop();
                if buffer.is_empty() { continue; }

                if (has_label && (res.len() == 1)) ||
                   (!has_label && (res.is_empty())) {
                    res.push(Token::Operator(buffer.clone()));
                    buffer.clear();
                }
            },
            '"' => {
                buffer.pop();
                if string_started {
                    res.push(Token::String(buffer.clone()));
                    buffer.clear();
                }
                string_started = !string_started;
            },
            ',' | '\n' | ';' => {
                buffer.pop();
                if buffer.is_empty() {
                    if c == ';' { break; } else { continue; }
                }

                if let Some(num) = buffer.parse::<Word>().ok() {
                    if address {
                        address = false;
                        res.push(Token::Address(num));
                    }
                    else {
                        res.push(Token::Number(num));
                    }
                }
                else if res.len() == has_label as usize {
                    res.push(Token::Operator(buffer.clone()));
                }
                else {
                    res.push(Token::Symbol(buffer.clone()));
                }
                buffer.clear();

                if c == ';' { break; }
            },
            '=' => if buffer.is_empty() {
                    panic!("Cannot begin the line with an =")
                }
                else if let Token::Operator(name) = &res[0] {
                    res[0] = Token::SymbolDef(name.clone());
                    buffer.clear();
                }
                else {
                    panic!("Before an = you may only have a symbol name!");
                },
            '\\' => skip_next = true,
            '%' => { buffer.pop(); address = true; },
            '-' => if address {
                panic!("Addresses cannot be negative!");
            },
            'A'..='Z' | 'a'..='z' | '0'..='9' | '_' | '.' | '$' | '?'
                => {},
            _ => panic!("Found invalid character '{}'", c),
        }
    }
    res
}