summaryrefslogtreecommitdiff
path: root/src/parsers/packed_binary_literal.rs
blob: 18f8da743379b2049f25b08f04b161d193522166 (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
use crate::*;


/// t is a Tokeniser over the characters of the PBL, excluding the leading hash.
pub fn parse_packed_binary_literal(mut t: Tokeniser, source: SourceSpan) -> PackedBinaryLiteral  {
    use PackedBinaryLiteralParseError as ParseError;
    use PackedBinaryLiteralParseErrorVariant as ParseErrorVar;

    let mut value = 0;
    let mut bits = 0;
    let mut field_bits = 0;
    let mut name = '\0';
    let mut fields: Vec<BitField> = Vec::new();
    let mut errors: Vec<ParseError> = Vec::new();

    macro_rules! push_field {
        () => {
            if fields.iter().any(|f| f.name == name) {
                let variant = ParseErrorVar::DuplicateFieldName(name);
                errors.push(ParseError { source: t.get_source(), variant });
            } else {
                fields.push(BitField { name, source: t.get_source(), bits: field_bits, shift: 0 });
            }
        };
    }

    while let Some(c) = t.eat_char() {
        // Ignore underscores.
        if c == '_' {
            t.mark.undo();
            continue;
        }

        // Add a bit to the value;
        value <<= 1;
        bits += 1;
        for field in &mut fields {
            field.shift += 1;
        }

        // Extend the current field.
        if c == name {
            field_bits += 1;
            continue;
        }

        // Commit the current field.
        if field_bits > 0 {
            t.mark_end_prev();
            push_field!();
            field_bits = 0;
            name = '\0';
        }

        // Parse bit literals.
        if c == '0' {
            continue;
        }
        if c == '1' {
            value |= 1;
            continue;
        }

        t.mark_start_prev();
        if c.is_alphabetic() {
            name = c;
            field_bits = 1;
            continue;
        } else {
            let source = t.get_source();
            let variant = ParseErrorVar::InvalidCharacter(c);
            errors.push(ParseError { source, variant });
        }
    }

    // Commit the final field.
    for field in &mut fields {
        field.shift += 1;
    }
    if field_bits > 0 {
        push_field!();
    }

    PackedBinaryLiteral { source, bits, value, fields, errors }
}