diff --git a/src/cli/cli.rs b/src/cli/cli.rs index 8745204..5a0daa2 100644 --- a/src/cli/cli.rs +++ b/src/cli/cli.rs @@ -37,6 +37,8 @@ const DEFAULT_LENGTH: &str = "2^(2-log(2, l))*(60/T)"; pub(super) enum Cli { /// Play a song Play(PlayOpts), + /// Check for typos + Check(PlayOpts), /// Export a song to an audio file or stdout Export(ExportOpts), /// Memo menu for examples and general help about syntax and supported audio formats diff --git a/src/cli/main.rs b/src/cli/main.rs index f1c2e57..1ad981c 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -26,6 +26,9 @@ fn main() -> anyhow::Result<()> { debug!("options: {cli:#?}"); use Cli::*; match cli { + Check(opts) => { + parse_and_compile(&opts)?; + } Play(opts) => { let (_stream, stream_handle) = OutputStream::try_default() .context("Failed to find (or use) default audio device")?; diff --git a/src/parser.rs b/src/parser.rs index 82f40b1..811c0e4 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -11,7 +11,7 @@ use nom::{ complete::{char, space1, usize}, streaming::one_of, }, - combinator::{all_consuming, opt, value}, + combinator::{all_consuming, cut, opt, value}, error::{Error, ParseError}, multi::many0, sequence::{delimited, preceded}, @@ -53,7 +53,7 @@ where input: &'a str, ) -> Result>> { debug!("parsing input \"{input}\""); - token_parser(self) + all_consuming(token_parser(self)) .parse_complete(LocatedSpan::new(input)) .finish() .map(|(_, o)| o) @@ -81,7 +81,7 @@ where many0(value((), char('#').and(take_till(|c| c == '\n'))).or(value((), space1))), ) }; - all_consuming(many0(delimited( + many0(delimited( space_or_comment(), alt(( Silence::parser().map(into_box), @@ -93,7 +93,7 @@ where Slope::parser(parser).map(into_box), )), space_or_comment(), - ))) + )) .map(Into::into) } @@ -171,10 +171,12 @@ impl VariableChange { move |i: I| { preceded( char('$'), - one_of(variables.as_ref().iter().collect::().as_str()), + cut( + one_of(variables.as_ref().iter().collect::().as_str()) + .and(expression_parser(variables.as_ref())) + .map(|(name, change)| VariableChange(name, change)), + ), ) - .and(expression_parser(variables.as_ref())) - .map(|(name, change)| VariableChange(name, change)) .parse(i) } } @@ -212,7 +214,7 @@ impl Loop { .map(LoopCount::Variable), ))) .and(token_parser(parser)), - char(')'), + cut(char(')')), ) .map(|(c, v)| Self(c.unwrap_or_default(), v)) .parse(input) @@ -237,7 +239,7 @@ impl Tuplet { { trace!("making the {} parser", type_name::()); |input| { - delimited(char('['), token_parser(parser), char(']')) + delimited(char('['), token_parser(parser), cut(char(']'))) .map(Self) .parse(input) } @@ -277,15 +279,15 @@ impl Slope { let iter: std::vec::IntoIter<(String, VariableChange)> = slopes.into_iter(); delimited( char('{'), - alt(iter + cut(alt(iter .map(|(k, v)| { Box::new(move |input: I| value(v.clone(), tag(k.as_str())).parse(input)) as Box IResult> }) .collect:: IResult>>>() - .as_mut_slice()) + .as_mut_slice())) .and(token_parser(parser)), - char('}'), + cut(char('}')), ) .map(|(i, v)| Self::new(i, v)) .parse(input)