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
use nb;
use heapless::{ArrayLength, String};

use tokenizer;
use tokenizer::{Token, Tokenizer};

#[derive(Clone)]
#[derive(PartialEq)]
enum MachineState {
    NewCommand,
    NewCommandCR,
    Key,
    Value,
}

pub enum CallbackCommand<'a> {
    Attribute(&'a str, &'a str, &'a str),
    Command(&'a str),
}

pub struct Lexer<SLEN> where SLEN: ArrayLength<u8> {
    current_cmd: String<SLEN>,
    current_key: String<SLEN>,
    state: MachineState,
}

impl<SLEN> Lexer<SLEN> where SLEN: ArrayLength<u8> {
    pub fn new() -> Self {
        Self {
            current_cmd: String::new(),
            current_key: String::new(),
            state: MachineState::NewCommand,
        }
    }

    pub fn parse_data<CB>(&mut self, tokenizer: &mut Tokenizer<SLEN>, mut callback: CB) -> nb::Result<(), tokenizer::Error> 
        where CB: FnMut(CallbackCommand) -> () {
        tokenizer.get_tokens(|token| {
            let new_state = match token {
                Token::NewLine => {
                    if self.state != MachineState::NewCommandCR {
                        callback(CallbackCommand::Command(self.current_cmd.as_str()));
                        self.current_cmd.clear();
                        self.current_key.clear();
                    }
                    MachineState::NewCommand
                },
                Token::CarriageReturn => {
                    callback(CallbackCommand::Command(self.current_cmd.as_str()));
                    self.current_cmd.clear();
                    self.current_key.clear();
                    MachineState::NewCommandCR
                }, // ignore carriage returns
                Token::Value(s) => {
                    match self.state {
                        MachineState::NewCommandCR => {
                            self.current_cmd = String::from(s);
                            MachineState::Key
                        },
                        MachineState::NewCommand => {
                            self.current_cmd = String::from(s);
                            MachineState::Key
                        },
                        MachineState::Key => {
                            self.current_key = String::from(s);
                            MachineState::Value
                        },
                        MachineState::Value => {
                            callback(CallbackCommand::Attribute(self.current_cmd.as_str(), self.current_key.as_str(), s));
                            MachineState::Key
                        },
                    }
                },
                Token::Space => {
                    match self.state {
                        MachineState::Value => {
                            callback(CallbackCommand::Attribute(self.current_cmd.as_str(), self.current_key.as_str(), ""));
                            MachineState::Key
                        },
                        MachineState::NewCommand => self.state.clone(),
                        MachineState::NewCommandCR => self.state.clone(),
                        MachineState::Key => self.state.clone()
                    }
                },
                Token::Equals => {
                    self.state.clone()
                }
            };

            self.state = new_state;
        })
    }
}