list subcommand support

This commit is contained in:
Ponj 2024-11-17 01:51:36 -05:00
parent efceea0705
commit e40f6f48a1
Signed by: p6nj
GPG key ID: 6FED68D87C479A59
3 changed files with 146 additions and 13 deletions

View file

@ -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<MathDocKind>,
},
/// 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))]

98
src/cli/doc.rs Normal file
View file

@ -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<ba
;this is a comment (or lyrics whatever),
C:CC
(3deff)
(deff)
[ffe]
{{l 1-cos((PI*x)/2),acced}}
abbc!o5cc!v15feed!l4fedd!t60Gdd"#;
}

View file

@ -4,7 +4,7 @@ use anyhow::Error;
use bng::{BngFile, Channel, Expression, Instrument};
/// TODO: remove clap, use only a file or standard in
use clap::Parser;
use cli::{BngCli as Cli, PlayOpts};
use cli::{doc, BngCli as Cli, ListOpts, MathDocKind, PlayOpts};
use fasteval::Compiler;
mod bng;
@ -24,7 +24,27 @@ fn main() -> 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(())
}