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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// SPDX-FileCopyrightText: 2021-2023 Thomas Kramer <code@tkramer.ch>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

//! Functions for parsing ASCII-based formats from iterators over bytes.

use itertools::{Itertools, PeekingNext};
use std::fmt;
use std::iter::Peekable;
use std::num::ParseIntError;
use std::str::FromStr;

use libreda_stream_parser::{Lexer, ParserError};

/// Error while parsing LEF or DEF.
/// TODO: Separate lexer errors from LEF/DEF specific errors.
#[derive(Clone, Debug)]
pub enum LefDefParseError {
    /// Error during parsing.
    ParserError(ParserError<char>),
    /// Encountered invalid character.
    InvalidCharacter,
    /// Reached end of file before end of library arrived.
    UnexpectedEndOfFile,
    /// Expected and actual token.
    UnexpectedToken(String, String),
    /// Unknown token. The token is given as a string.
    UnknownToken(String),
    /// Unknown literal. The literal is given as a string.
    InvalidLiteral(String),
    /// Illegal value for bus bit chars.
    IllegalBusBitChars(char, char),
    /// Something is not yet implemented.
    NotImplemented(&'static str),
    /// Using a property name that has not been defined in PROPERTYDEFINITIONS.
    UndefinedProperty(String),
    /// Failed to parse an integer.
    ParseIntError(ParseIntError),
    /// Some other error defined by a string.
    Other(&'static str),
}

impl From<ParserError<char>> for LefDefParseError {
    fn from(e: ParserError<char>) -> Self {
        Self::ParserError(e)
    }
}

impl fmt::Display for LefDefParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            LefDefParseError::InvalidCharacter => write!(f, "Invalid character."),
            LefDefParseError::UnexpectedEndOfFile => write!(f, "Unexpected end of file."),
            LefDefParseError::UnexpectedToken(actual, exp) => {
                write!(f, "Unexpected token. '{}' instead of '{}'", actual, exp)
            }
            LefDefParseError::UnknownToken(t) => write!(f, "Unknown token: '{}'.", t),
            LefDefParseError::InvalidLiteral(n) => write!(f, "Invalid literal: '{}'.", n),
            LefDefParseError::IllegalBusBitChars(a, b) => {
                write!(f, "Illegal bus bit chars: '{} {}'.", a, b)
            }
            LefDefParseError::NotImplemented(n) => write!(f, "Not implemented: '{}'.", n),
            LefDefParseError::UndefinedProperty(p) => write!(f, "Undefined property: '{}'.", p),
            LefDefParseError::Other(msg) => write!(f, "'{}'.", msg),
            LefDefParseError::ParseIntError(e) => write!(f, "Illegal integer: '{}'", e),
            LefDefParseError::ParserError(e) => write!(f, "{}", e),
        }
    }
}

impl From<ParseIntError> for LefDefParseError {
    fn from(e: ParseIntError) -> Self {
        Self::ParseIntError(e)
    }
}

pub struct LefDefLexer {}

impl Lexer for LefDefLexer {
    type Char = char;

    fn consume_next_token(
        &mut self,
        iter: &mut (impl Iterator<Item = char> + PeekingNext),
        mut output: impl FnMut(char),
    ) -> Result<(), ParserError<char>> {
        loop {
            // Skip whitespace.
            let _n = iter.peeking_take_while(|c| c.is_whitespace()).count();

            // Look ahead.
            if let Some(c) = iter.peeking_next(|_| true) {
                debug_assert!(!c.is_whitespace());

                match c {
                    '#' => {
                        // Skip comments.
                        iter.peeking_take_while(|&c| c != '\n' && c != '\r').count();
                    }
                    '"' | '\'' => {
                        // Quoted string.
                        let quote_char = c;

                        let mut prev = None;
                        while let Some(c) = iter.next() {
                            if prev != Some('\\') && c == quote_char {
                                // Abort on quote char.
                                break;
                            }
                            output(c);
                            prev = Some(c);
                        }
                        return Ok(());
                    }
                    _ => {
                        // Normal token.
                        let mut prev = Some(c);
                        output(c);

                        while let Some(c) = iter.next() {
                            if prev != Some('\\') && c.is_whitespace() {
                                // Abort on unmasked whitespace.
                                break;
                            }

                            output(c);
                            prev = Some(c);
                        }
                        return Ok(());
                    }
                }
            } else {
                return Ok(());
            }
        }
    }
}

/// Read simple tokens and skip comments.
#[test]
fn test_read_token() {
    let data = r#"
        # Comment 1

        # Comment 2

        token1

        # Comment 3

        token2 token3

        "quoted token"

        token4
    "#;

    let mut iter = data.chars().inspect(|c| print!("{}", c)).peekable();

    let mut buffer = String::new();

    let mut tk = libreda_stream_parser::tokenize(iter, LefDefLexer {});

    tk.advance().unwrap();

    tk.expect_str("token1").unwrap();
    tk.expect_str("token2").unwrap();
    tk.expect_str("token3").unwrap();
    tk.expect_str("quoted token").unwrap();
    tk.expect_str("token4").unwrap();
    assert!(tk.current_token_ref().is_none());
}