ukhasnet_parser/
parser.rs

1use pest::prelude::*;
2use std::error;
3use std::fmt;
4use packet::{Location, WindSpeed, DataField, Packet};
5
6impl_rdp! {
7    /*
8     * UKHASnet Packet Grammar
9     *
10     * Reference: https://github.com/UKHASnet/protocol/blob/master/grammar.ebnf
11     * Syntax: http://dragostis.github.io/pest/pest/macro.grammar!.html#syntax
12     *
13     * In brief: Literals are quoted and in square brackets,
14     *           ~ concatenates,
15     *           + means "one or more"
16     *           * means "zero or more"
17     *           ? means "optional"
18     *           _ means "this token is silent and is subsumed into the parent"
19     */
20    grammar! {
21        digit       = _{ ['0'..'9'] }
22        integer     =  { (["+"] | ["-"])? ~ digit+ }
23        decimal     =  { (["+"] | ["-"])? ~ digit+ ~ (["."] ~ digit+)? }
24
25        lowercase_letter = _{ ['a'..'z'] }
26        uppercase_letter = _{ ['A'..'Z'] }
27
28        letter      = _{ lowercase_letter | uppercase_letter }
29        symbol      = _{
30            [" "] | ["!"] | ["\""] | ["#"] | ["$"] | ["%"] | ["&"] | ["'"]  |
31            ["("] | [")"] | ["*"]  | ["+"] | [","] | ["-"] | ["."] | ["/"]  |
32            [":"] | [";"] | ["<"]  | ["="] | [">"] | ["?"] | ["@"] | ["\\"] |
33            ["^"] | ["_"] | ["`"]  | ["{"] | ["|"] | ["}"] | ["~"]
34        }
35
36        repeat      =  { digit }
37        sequence    =  { lowercase_letter }
38
39        decimal_list=  { decimal? ~ ( [","] ~ decimal? )* }
40
41        voltage     =  { ["V"] ~ decimal_list }
42        current     =  { ["I"] ~ decimal_list }
43        temperature =  { ["T"] ~ decimal_list }
44        humidity    =  { ["H"] ~ decimal_list }
45        pressure    =  { ["P"] ~ decimal_list }
46        custom      =  { ["X"] ~ decimal_list }
47        sun         =  { ["S"] ~ decimal_list }
48        rssi        =  { ["R"] ~ decimal_list }
49        count       =  { ["C"] ~ decimal_list }
50        windspeed   =  { ["W"] ~ decimal? ~ ( [","] ~ decimal? )? }
51        location    =  { ["L"] ~ ( (decimal ~ [","] ~ decimal)? | [","] )
52                               ~ ( [","] ~ decimal? )? }
53
54        zombie_mode =  { ["0"] | ["1"] }
55        zombie      =  { ["Z"] ~ zombie_mode }
56
57        data_field  =  { voltage | current | temperature | humidity |
58                         pressure | custom | sun  | rssi | windspeed |
59                         location | count | zombie }
60
61        data        =   { data_field* }
62
63        comment_content     =  { (letter | digit | symbol)* }
64        comment             =  { [":"] ~ comment_content }
65
66        /* The specification says upper case letters only, but enough deployed
67         * nodes use lower case names for it to be an annoying breaking change.
68         * To compromise, we will accept lower case letters in node names, but
69         * convert them to upper case in the parser, so they're stored and
70         * displayed as all upper case thereafter.
71         */
72        node_name_content   =  { (letter | digit)* }
73        node_name           =  { node_name_content }
74
75        path        =  { ["["] ~ node_name ~ ( [","] ~ node_name )* ~ ["]"] }
76
77        packet      =  { repeat ~ sequence ~ data ~ comment? ~ path ~ eoi }
78    }
79
80    /*
81     * UKHASnet packet parsing
82     *
83     * Each rule maps one or more tokens from the stream into some reduced
84     * type, for example a sequence token into a char, or three decimal tokens
85     * into a Location struct.
86     *
87     * Some rules are called recursively, such as _decimal_list, which first
88     * consumes the decimal_list token, then consumes a number of decimals.
89     * Think about it backwards: it will recurse into the stack until it cannot
90     * match any decimals, at which point it returns a new Vec, then steps back
91     * up the stack, inserting into that Vec, until it eventually returns it.
92     * _data and _path behave similarly.
93     *
94     * Note that the extensive unwrap() is fine because the parser itself will
95     * have validated that the field contained the relevant type. Any panics
96     * would be a bug in the underlying parser library.
97     */
98    process! {
99        _repeat(&self) -> u8 {
100            (&repeat: repeat) => repeat.parse::<u8>().unwrap()
101        }
102
103        _sequence(&self) -> char {
104            (&sequence: sequence) =>
105                sequence.chars().nth(0).unwrap()
106        }
107
108        _location(&self) -> Location {
109            (&latitude: decimal, &longitude: decimal, &altitude: decimal) => {
110                let lat = latitude.parse::<f32>().unwrap();
111                let lng = longitude.parse::<f32>().unwrap();
112                let alt = altitude.parse::<f32>().unwrap();
113                Location{ latlng: Some((lat, lng)), alt: Some(alt) }
114            },
115            (&latitude: decimal, &longitude: decimal) => {
116                let lat = latitude.parse::<f32>().unwrap();
117                let lng = longitude.parse::<f32>().unwrap();
118                Location{ latlng: Some((lat, lng)), alt: None }
119            },
120            (&altitude: decimal) => {
121                let alt = altitude.parse::<f32>().unwrap();
122                Location{ latlng: None, alt: Some(alt) }
123            },
124            () => Location{ latlng: None, alt: None },
125        }
126
127        _windspeed(&self) -> WindSpeed {
128            (&speed: decimal, &bearing: decimal) => {
129                let speed = speed.parse::<f32>().unwrap();
130                let bearing = bearing.parse::<f32>().unwrap();
131                WindSpeed{speed: Some(speed), bearing: Some(bearing)}
132            },
133            (&speed: decimal) => {
134                let speed = speed.parse::<f32>().unwrap();
135                WindSpeed{speed: Some(speed), bearing: None}
136            },
137            () => WindSpeed{speed: None, bearing: None},
138        }
139
140        _decimal_list(&self) -> Vec<f32> {
141            (_: decimal_list, list: _decimal_list()) => list,
142            (&head: decimal, mut tail: _decimal_list()) => {
143                tail.insert(0, head.parse::<f32>().unwrap());
144                tail
145            },
146            () => Vec::<f32>::new(),
147        }
148
149        _zombie(&self) -> u8 {
150            (&mode: zombie_mode) => mode.parse::<u8>().unwrap()
151        }
152
153        _datafield(&self) -> DataField {
154            (_: voltage, voltages: _decimal_list()) =>
155                DataField::Voltage(voltages),
156            (_: current, currents: _decimal_list()) =>
157                DataField::Current(currents),
158            (_: temperature, temperatures: _decimal_list()) =>
159                DataField::Temperature(temperatures),
160            (_: humidity, humidities: _decimal_list()) =>
161                DataField::Humidity(humidities),
162            (_: pressure, pressures: _decimal_list()) =>
163                DataField::Pressure(pressures),
164            (_: custom, customs: _decimal_list()) =>
165                DataField::Custom(customs),
166            (_: sun, suns: _decimal_list()) =>
167                DataField::Sun(suns),
168            (_: rssi, rssis: _decimal_list()) =>
169                DataField::Rssi(rssis),
170            (_: count, counts: _decimal_list()) =>
171                DataField::Count(counts),
172            (_: windspeed, windspeed: _windspeed()) =>
173                DataField::WindSpeed(windspeed),
174            (_: location, location: _location()) =>
175                DataField::Location(location),
176            (_: zombie, zombie: _zombie()) =>
177                DataField::Zombie(zombie),
178        }
179
180        _data(&self) -> Vec<DataField> {
181            (_: data, fields: _data()) => fields,
182            (_: data_field, head: _datafield(), mut tail: _data()) => {
183                tail.insert(0, head);
184                tail
185            },
186            () => Vec::<DataField>::new(),
187        }
188
189        _comment(&self) -> Option<String> {
190            (_: comment, &comment: comment_content) =>
191                Some(comment.to_owned()),
192            () => None,
193        }
194
195        _node_name(&self) -> String {
196            (&name: node_name_content) => name.to_owned().to_uppercase()
197        }
198
199        _path(&self) -> Vec<String> {
200            (_: path, names: _path()) => names,
201            (_: node_name, head: _node_name(), mut tail: _path()) => {
202                tail.insert(0, head);
203                tail
204            },
205            () => Vec::<String>::new(),
206        }
207
208        parse(&self) -> Packet {
209            (_: packet, repeat: _repeat(), sequence: _sequence(),
210             data: _data(), comment: _comment(), path: _path()) =>
211                Packet{ repeat: repeat, sequence: sequence, data: data,
212                        comment: comment, path: path }
213        }
214    }
215}
216
217/// Contains the position in the input at which a parsing error occurred,
218/// and a Vec of token names we expected to see instead.
219#[derive(Debug)]
220pub struct ParserError {
221    pub position: usize,
222    pub expected: Vec<String>,
223}
224
225impl fmt::Display for ParserError {
226    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
227        write!(f, "Parser error at position {}", self.position)
228    }
229}
230
231impl error::Error for ParserError {
232    fn description(&self) -> &str { "Parser error" }
233    fn cause(&self) -> Option<&error::Error> { None }
234}
235
236impl ParserError {
237    /// Extract error information from a parser.
238    pub fn from_parser<'a, T: Input<'a>>(parser: &mut Rdp<T>) -> ParserError {
239        let (expected, position) = parser.expected();
240        let exp = expected.iter().map(|r| { format!("{:?}", r) }).collect();
241        ParserError{ position: position, expected: exp }
242    }
243}