diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index c930f41..f0ed724 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -169,8 +169,8 @@ dependencies = [ "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "libm 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "logos 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=cd893e6)", "tm4c129x 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -195,36 +195,30 @@ name = "linked_list_allocator" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "logos" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "logos-derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "logos-derive" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "managed" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "memchr" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "nb" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "nom" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-traits" version = "0.2.8" @@ -275,11 +269,6 @@ name = "rand_core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "regex-syntax" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "rustc_version" version = "0.2.3" @@ -357,13 +346,13 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "utf8-ranges" -version = "1.0.4" +name = "vcell" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "vcell" -version = "0.1.2" +name = "version_check" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -428,10 +417,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum libm 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" "checksum linked_list_allocator 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "47314ec1d29aa869ee7cb5a5be57be9b1055c56567d59c3fb6689926743e0bea" -"checksum logos 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f494e22d293fa05db60b3fd95fb30e9409feb5672b56ce6f250f99d9fbae6b93" -"checksum logos-derive 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)" = "13ff1b1068db09ee21d12baf55eccc0900a781a735273e0a606f6f4fbb32a322" "checksum managed 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43e2737ecabe4ae36a68061398bf27d2bfd0763f4c3c837a398478459494c4b7" +"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" +"checksum nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c618b63422da4401283884e6668d39f819a106ef51f5f59b81add00075da35ca" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" @@ -439,7 +428,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" -"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" @@ -450,8 +438,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum tm4c129x 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d430ed4ed06dd9fff3d4517a37343e1b53789218f2f608bf1e0432f67abf624" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" "checksum vcell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" +"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum volatile-register 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a470889aa8f2d3ad893bd43cd90c824e63e8ac0ee5fe64c5d81a932d184fd549" "checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml index 2efb71a..a42b17f 100644 --- a/firmware/Cargo.toml +++ b/firmware/Cargo.toml @@ -19,9 +19,11 @@ cortex-m-semihosting = "0.3" byteorder = { version = "1.3", default-features = false } bit_field = "0.10" bare-metal = "0.2" -logos = { version = "~0.9", default-features = false, features = ["export_derive"] } +# TODO: remove alloc-cortex-m = "0.3" +# TODO: needed with nom? btoi = { version = "~0.4", default-features = false } +nom = { version = "~5", default-features = false } [dependencies.smoltcp] git = "https://github.com/m-labs/smoltcp" diff --git a/firmware/src/command_parser.rs b/firmware/src/command_parser.rs index 8f464d8..7dfa91c 100644 --- a/firmware/src/command_parser.rs +++ b/firmware/src/command_parser.rs @@ -1,125 +1,133 @@ -use logos::Logos; +use nom::{ + IResult, + branch::alt, + bytes::complete::{tag, take_while1}, + character::{is_digit, complete::char}, + combinator::{map, value}, + sequence::{preceded, tuple, Tuple}, + multi::fold_many1, + error::ErrorKind, +}; use btoi::{btoi, ParseIntegerError}; use super::session::ReportMode; -#[derive(Logos, Debug, PartialEq)] -enum Token { - #[end] - End, - #[error] - Error, - - #[token = "Quit"] - Quit, - #[token = "report"] - Report, - #[token = "mode"] - Mode, - #[token = "off"] - Off, - #[token = "once"] - Once, - #[token = "continuous"] - Continuous, - #[token = "pwm"] - Pwm, - - #[regex = "[0-9]+"] - Number, -} #[derive(Debug)] pub enum Error { - Parser, - UnexpectedEnd, - UnexpectedToken(Token), + Parser(ErrorKind), + Incomplete, + UnexpectedInput(u8), ParseInteger(ParseIntegerError) } +impl<'t> From> for Error { + fn from(e: nom::Err<(&'t [u8], ErrorKind)>) -> Self { + match e { + nom::Err::Incomplete(_) => + Error::Incomplete, + nom::Err::Error((_, e)) => + Error::Parser(e), + nom::Err::Failure((_, e)) => + Error::Parser(e), + } + } +} + impl From for Error { fn from(e: ParseIntegerError) -> Self { Error::ParseInteger(e) } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ShowCommand { ReportMode, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Command { Quit, Show(ShowCommand), Report(ReportMode), Pwm { - pwm_match: u32, - pwm_reload: u32, + width: u32, + total: u32, }, } +fn whitespace(input: &[u8]) -> IResult<&[u8], ()> { + fold_many1(char(' '), (), |(), _| ())(input) +} + +fn unsigned(input: &[u8]) -> IResult<&[u8], Result> { + take_while1(is_digit)(input) + .map(|(input, digits)| (input, btoi(digits))) +} + +fn report_mode(input: &[u8]) -> IResult<&[u8], ReportMode> { + alt((value(ReportMode::Off, tag("off")), + value(ReportMode::Once, tag("once")), + value(ReportMode::Continuous, tag("continuous")) + ))(input) +} + +fn report(input: &[u8]) -> IResult<&[u8], Command> { + preceded( + preceded( + tag("report"), + whitespace + ), + alt(( + preceded( + whitespace, + preceded( + tag("mode"), + alt(( + preceded( + whitespace, + map(report_mode, + |mode| Command::Report(mode)) + ), + |input| Ok((input, Command::Show(ShowCommand::ReportMode))) + )) + )), + |input| Ok((input, Command::Report(ReportMode::Once))) + )) + )(input) +} + +fn pwm(input: &[u8]) -> IResult<&[u8], Result> { + let (input, _) = tag("pwm")(input)?; + let (input, _) = whitespace(input)?; + let (input, width) = unsigned(input)?; + let width = match width { + Ok(width) => width, + Err(e) => return Ok((input, Err(e.into()))), + }; + let (input, _) = whitespace(input)?; + let (input, total) = unsigned(input)?; + let total = match total { + Ok(total) => total, + Err(e) => return Ok((input, Err(e.into()))), + }; + Ok((input, Ok(Command::Pwm { width, total }))) +} + +fn command(input: &[u8]) -> IResult<&[u8], Command> { + alt((value(Command::Quit, tag("quit")), + report + ))(input) +} + impl Command { - pub fn parse(input: &str) -> Result { - let mut lexer = Token::lexer(input); - - /// Match against a set of expected tokens - macro_rules! choice { - [$($token: tt => $block: stmt,)*] => { - match lexer.token { - $( - Token::$token => { - lexer.advance(); - $block - } - )* - Token::End => return Err(Error::UnexpectedEnd), - _ => return Err(Error::UnexpectedToken(lexer.token)) - } - }; + pub fn parse(input: &[u8]) -> Result { + match command(input) { + Ok((b"", command)) => + Ok(command), + Ok((input_remain, _)) => + Err(Error::UnexpectedInput(input_remain[0])), + Err(e) => + Err(e.into()), } - /// Expecting no further tokens - macro_rules! end { - ($result: expr) => { - match lexer.token { - Token::End => Ok($result), - _ => return Err(Error::UnexpectedToken(lexer.token)), - } - }; - } - - // Command grammar - choice![ - Quit => Ok(Command::Quit), - Report => choice![ - Mode => choice![ - End => end!(Command::Show(ShowCommand::ReportMode)), - Off => Ok(Command::Report(ReportMode::Off)), - Once => Ok(Command::Report(ReportMode::Once)), - Continuous => Ok(Command::Report(ReportMode::Continuous)), - ], - End => Ok(Command::Report(ReportMode::Once)), - ], - Pwm => { - if lexer.token != Token::Number { - return Err(Error::UnexpectedToken(lexer.token)); - } - let pwm_match = btoi(lexer.slice().as_bytes())?; - lexer.advance(); - - if lexer.token != Token::Number { - return Err(Error::UnexpectedToken(lexer.token)); - } - let pwm_reload = btoi(lexer.slice().as_bytes())?; - lexer.advance(); - - if lexer.token != Token::End { - return Err(Error::UnexpectedToken(lexer.token)); - } - - end!(Command::Pwm { - pwm_match, pwm_reload, - }) - }, - ] } } diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 6f52182..67654b3 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -265,9 +265,9 @@ fn main() -> ! { Command::Show(ShowCommand::ReportMode) => { let _ = writeln!(socket, "Report mode: {:?}", session.report_mode()); } - Command::Pwm { pwm_match, pwm_reload } => { - board::set_timer_pwm(pwm_match, pwm_reload); - let _ = writeln!(socket, "PWM duty cycle: {}/{}", pwm_match, pwm_reload); + Command::Pwm { width, total } => { + board::set_timer_pwm(width, total); + let _ = writeln!(socket, "PWM duty cycle: {}/{}", width, total); } } Ok(SessionOutput::Error(e)) => { diff --git a/firmware/src/session.rs b/firmware/src/session.rs index a1a8e04..958f69c 100644 --- a/firmware/src/session.rs +++ b/firmware/src/session.rs @@ -1,6 +1,6 @@ +use core::ops::Deref; use super::command_parser::{Command, Error as ParserError}; - const MAX_LINE_LEN: usize = 64; struct LineReader { @@ -16,13 +16,16 @@ impl LineReader { } } - pub fn feed(&mut self, c: u8) -> Option<&str> { + pub fn feed(&mut self, c: u8) -> Option { if c == 13 || c == 10 { // Enter if self.pos > 0 { let len = self.pos; self.pos = 0; - core::str::from_utf8(&self.buf[..len]).ok() + Some(LineResult { + buf: self.buf.clone(), + len, + }) } else { None } @@ -38,6 +41,18 @@ impl LineReader { } } +pub struct LineResult { + buf: [u8; MAX_LINE_LEN], + len: usize, +} + +impl Deref for LineResult { + type Target = [u8]; + fn deref(&self) -> &Self::Target { + &self.buf[..self.len] + } +} + #[derive(Debug, Clone, Copy)] pub enum ReportMode { Off, @@ -105,9 +120,10 @@ impl Session { let mut buf_bytes = 0; for (i, b) in buf.iter().enumerate() { buf_bytes = i + 1; - match self.reader.feed(*b) { + let line = self.reader.feed(*b); + match line { Some(line) => { - let command = Command::parse(line); + let command = Command::parse(&line); match command { Ok(Command::Report(mode)) => { self.report_mode = mode;