diff --git a/src/cli.rs b/src/cli.rs index da8fc4e..3368888 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,7 +1,10 @@ -use std::{fmt::Display, fs::read_to_string, io, str::FromStr}; +use std::{convert::Infallible, fmt::Display, fs::read_to_string, io, str::FromStr}; use amplify::{From, Wrapper}; use clap::Parser; +use strum::EnumString; + +pub mod doc; /// Cli entry point #[derive(Clone, Parser)] @@ -9,9 +12,9 @@ use clap::Parser; pub enum BngCli { /// Play the song through default sink Play(PlayOpts), - /// Export the song to a sound FileContents + /// Export the song to a sound file Export(ExportOpts), - /// List supported sound FileContents extensions and instrument / song available expressions + /// List supported sound file extensions and instrument / song available expressions #[command(subcommand)] List(ListOpts), } @@ -28,10 +31,10 @@ pub struct PlayOpts { #[derive(Clone, Parser)] #[cfg_attr(debug_assertions, derive(Debug))] pub struct ExportOpts { - /// Input FileContents (written song FileContents) + /// Input file (written song file) #[arg(value_parser = FileContents::from_str)] input: FileContents, - /// Output FileContents (sound FileContents) + /// Output file (sound file) #[arg(value_parser = AudioFileName::from_str)] output: AudioFileName, } @@ -40,17 +43,29 @@ pub struct ExportOpts { #[derive(Clone, Parser)] #[cfg_attr(debug_assertions, derive(Debug))] pub enum ListOpts { - /// List supported sound FileContents extensions to export songs - #[command(subcommand)] + /// List supported sound file extensions to export songs Extensions, - /// List available math expressions for instrument definition - #[command(subcommand)] - Math, + /// Show the math syntax used for instrument definition and slopes + Math { + /// Kind of syntax you want to list (functions, operators or literals) + #[arg(value_parser = MathDocKind::from_str)] + kind: Option, + }, /// List available score glyphs and their meaning - #[command(subcommand)] Glyphs, } +#[derive(Clone, Copy, EnumString)] +#[cfg_attr(debug_assertions, derive(Debug))] +pub enum MathDocKind { + #[strum(ascii_case_insensitive)] + Functions, + #[strum(ascii_case_insensitive)] + Operators, + #[strum(ascii_case_insensitive)] + Literals, +} + #[derive(Clone, Wrapper, From)] #[wrapper(Deref)] #[cfg_attr(debug_assertions, derive(Debug))] diff --git a/src/cli/doc.rs b/src/cli/doc.rs new file mode 100644 index 0000000..408ee40 --- /dev/null +++ b/src/cli/doc.rs @@ -0,0 +1,98 @@ +pub mod math { + pub const FUNCTIONS: &str = r#"* print(...strings and values...) -- Prints to stderr. Very useful to 'probe' an expression. + Evaluates to the last value. + Example: `print("x is", x, "and y is", y)` + Example: `x + print("y:", y) + z == x+y+z` + +* log(base=10, val) -- Logarithm with optional 'base' as first argument. + If not provided, 'base' defaults to '10'. + Example: `log(100) + log(e(), 100)` + +* e() -- Euler's number (2.718281828459045) +* pi() -- π (3.141592653589793) + +* int(val) +* ceil(val) +* floor(val) +* round(modulus=1, val) -- Round with optional 'modulus' as first argument. + Example: `round(1.23456) == 1 && round(0.001, 1.23456) == 1.235` + +* abs(val) +* sign(val) + +* min(val, ...) -- Example: `min(1, -2, 3, -4) == -4` +* max(val, ...) -- Example: `max(1, -2, 3, -4) == 3` + +* sin(radians) * asin(val) +* cos(radians) * acos(val) +* tan(radians) * atan(val) +* sinh(val) * asinh(val) +* cosh(val) * acosh(val) +* tanh(val) * atanh(val)"#; + pub const OPERATORS: &str = r#"Listed in order of precedence: + +(Highest Precedence) ^ Exponentiation + % Modulo + / Division + * Multiplication + - Subtraction + + Addition + == != < <= >= > Comparisons (all have equal precedence) + && and Logical AND with short-circuit +(Lowest Precedence) || or Logical OR with short-circuit"#; + pub const LITERALS: &str = r#"Several numeric formats are supported: + +Integers: 1, 2, 10, 100, 1001 + +Decimals: 1.0, 1.23456, 0.000001 + +Exponents: 1e3, 1E3, 1e-3, 1E-3, 1.2345e100 + +Suffix: + 1.23p = 0.00000000000123 + 1.23n = 0.00000000123 + 1.23µ, 1.23u = 0.00000123 + 1.23m = 0.00123 + 1.23K, 1.23k = 1230 + 1.23M = 1230000 + 1.23G = 1230000000 + 1.23T = 1230000000000"#; +} + +pub mod glyphs { + pub const ALL: &str = r#"rest: . +pizz.: '° +volume: +- +length: /\ +octave: >< +comment?: ;, +start here: ':' +slope: {{MODIFIER EXPR score}} +note modifier prefix: n +volume modifier prefix: v +octave modifier prefix: o +length modifier prefix: l +tempo modifier prefix: t +loop: () +loop with count: (COUNT score) +tuple: [] +modifier: ! +volume modifier prefix: v +octave modifier prefix: o +length modifier prefix: l +tempo modifier prefix: t + +EXAMPLE +aabc. +'ab°A ++d+d+d--- +/ff/f\\ +ab>c Result<(), Error> { #[cfg(debug_assertions)] println!("{:#?}", bng_file); } - _ => unimplemented!("can't do that yet"), + Cli::List(l) => match l { + ListOpts::Extensions => println!("you can't export songs yet"), + ListOpts::Math { kind } => match kind { + None => println!( + "Functions :\n{}\n\nOperators :\n{}\n\nLiterals :\n{}", + doc::math::FUNCTIONS, + doc::math::OPERATORS, + doc::math::LITERALS + ), + Some(m) => println!( + "{}", + match m { + MathDocKind::Functions => doc::math::FUNCTIONS, + MathDocKind::Operators => doc::math::OPERATORS, + MathDocKind::Literals => doc::math::LITERALS, + } + ), + }, + ListOpts::Glyphs => println!("{}", doc::glyphs::ALL), + }, + Cli::Export(_) => unimplemented!("can't do that yet"), } Ok(()) }