File: define_parser.rs

package info (click to toggle)
rust-ssh2-config 0.6.2-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 356 kB
  • sloc: makefile: 2
file content (126 lines) | stat: -rw-r--r-- 3,794 bytes parent folder | download
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
use std::collections::HashMap;
use std::io::BufRead;

struct Scope {
    name: String,
    tokens: Vec<String>,
}

pub fn parse_defines(reader: impl BufRead) -> anyhow::Result<HashMap<String, String>> {
    let mut defines = HashMap::new();

    // iterate over each line in the reader
    let mut scope: Option<Scope> = None;

    for line in reader.lines() {
        let line = line?;
        // check if the line is a define
        if line.trim().starts_with("#define") {
            if let Some(prev_scope) = scope.take() {
                // if we have a previous scope, store it
                defines.insert(prev_scope.name, prev_scope.tokens.join(" "));
            }
            // start a new scope
            let mut tokens = line.split_whitespace();
            let name = tokens
                .nth(1)
                .ok_or_else(|| anyhow::anyhow!("Expected a name after #define"))?
                .to_string();

            let mut tokens = tokens.collect::<Vec<_>>();
            let mut single_line = true;

            // if last token is a \; remove it
            if let Some(last) = tokens.last()
                && *last == "\\"
            {
                tokens.pop();
                single_line = false;
            }

            // get tokens after the name
            let mut parsed_tokens: Vec<String> = vec![];
            for token in tokens {
                let parsed = parse_token(&defines, token, false)?;
                parsed_tokens.extend(parsed);
            }

            scope = Some(Scope {
                name,
                tokens: parsed_tokens,
            });

            // if is single line, push to defines and set scope to None
            if single_line && let Some(scope) = scope.take() {
                defines.insert(scope.name, scope.tokens.join(" "));
            }
        } else {
            // if we are in a scope, add the line to the tokens
            let Some(inner_scope) = scope.as_mut() else {
                continue;
            };

            let tokens = line.split_whitespace();
            let mut tokens: Vec<String> = tokens.map(|s| s.to_string()).collect();

            // check if it ends with a \, if so, remove it
            let mut last_line = true;
            if let Some(last) = tokens.last()
                && last == "\\"
            {
                tokens.pop();
                last_line = false;
            }

            // parse tokens
            for token in tokens {
                let parsed = parse_token(&defines, &token, false)?;
                inner_scope.tokens.extend(parsed);
            }

            // if last line, push to defines and set scope to None
            if last_line && let Some(scope) = scope.take() {
                defines.insert(scope.name, scope.tokens.join(" "));
            }
        }
    }

    // put last scope
    if let Some(scope) = scope {
        defines.insert(scope.name, scope.tokens.join(" "));
    }

    Ok(defines)
}

/// Parse token
fn parse_token(
    defines: &HashMap<String, String>,
    token: &str,
    nested: bool,
) -> anyhow::Result<Vec<String>> {
    let token = token.trim().trim_end_matches(',');

    // if token is a define, parse it
    if let Some(value) = defines.get(token) {
        return parse_token(defines, value, true);
    }

    // otherwise, check if it is a string
    if token.starts_with('"') && token.ends_with('"') {
        return Ok(vec![
            token[1..token.len() - 1].trim_end_matches(',').to_string(),
        ]);
    }

    // check if it is a number
    if token.parse::<i64>().is_ok() {
        return Ok(vec![token.to_string()]);
    }

    if nested {
        return Ok(vec![token.to_string()]);
    }

    anyhow::bail!("Unknown token: {token}; defines: {defines:#?}",)
}