use num_rational::{Rational64};
use std::str::FromStr;
use std::string::String;
use scop::Defs;
use weresocool_shared::helpers::generate_random_hash_string;
use weresocool_ast::{
Op::*,
Term,
ASR,
FunDef,
ListOp,
Direction,
CoefState,
Coefs,
Coef,
Axis,
Generator,
GenOp,
Indices,
Index,
Scale,
};
use crate::parser::{Init, handle_fit_length_recursively};
use crate::indices::{et, random_seed};
use crate::float_to_rational::helpers::*;
use regex::Regex;
grammar<'defs, 'err>(
defs: &'defs mut Defs<Term>,
);
pub SoCool: Init = {
<init: Point>
<l: LetDefs> => init
}
Point: Init = {
"{"
<f_axis: F> <f: Rational> ","
<l_axis: L> <l: Rational> ","
<g_axis: G> <g: Rational> ","
<p_axis: P> <p: Rational>
"}" ";"? => Init { f, l, g, p }
}
LetDefs = Lets<LetDef>;
LetDef: () = {
<s: Name> "(" <vars: Comma<Name>> ")" "=" "{" <o: Operation> "}" => {
defs.insert("global",
s.to_owned(),
Term::FunDef(FunDef {
name: s,
vars: vars,
term: Box::new(o)
})
);
()
},
<s: Name> "=" "{" <o: Operation> "}" => {
defs.insert("global", s.to_owned(), o); ()
},
<name: Name> "=" <list: List> => {
defs.insert("global", name.to_owned(), Term::Lop(list)); ()
},
<name: Name> "=" <generator: Generator> => {
defs.insert("global", name.to_owned(), Term::Gen(generator)); ()
},
<stems: Stems> =>{
defs.stems.extend(stems); ()
}
};
Operation: Term = {
<op1: Operation> ">" "FitGain" <op2: ComposeOperation> => {
unimplemented!()
},
// TODO: Revisit Focus/Lens
<op1: Operation> ">" "@" <name: Name> <op2: ComposeOperation> => {
Term::Op(
Focus {
name: name,
main: Box::new(op1.to_owned()),
op_to_apply: Box::new(op2.to_owned()),
}
)
},
<op: ComposeOperation> => op,
<list: List> => Term::Lop(list),
<generator: Generator> => Term::Gen(generator)
}
Generator: GenOp = {
<gen: GeneratorBase> "Take" <n: Int> <seed: ("Seed" Int)?> => {
GenOp::init_taken(gen, n as usize, seed)
},
<gen: GeneratorBase> => gen
}
GeneratorBase: GenOp = {
r"(Gen|\*)" "(" <coefs: Comma<CoefState>> ")" <seed: ("Seed" Int)?> => {
GenOp::init_const(Generator {coefs}, seed)
},
r"(Gen|\*)" <name: Name> <seed: ("Seed" Int)?> => {
GenOp::init_named(name, seed)
},
}
CoefState: CoefState = {
<axis: Axis> <div: CoefStart> <coefs: Coefs> => {
CoefState::new(
div.0,
div.1,
axis,
coefs
)
}
}
Coefs: Coefs = {
"[" <coefs: Comma<Coef>> "]" => Coefs::init_const(coefs),
"Poly" "[" <coefs: Comma<Rational>> "]" => Coefs::init_polynomial(coefs) ,
"Expr" <expr: Expr> => {
Coefs::init_expr(expr[1..expr.len() -1].to_string())
}
}
Expr: String = <s:r#"`(.*)`"#> => s.to_string();
CoefType: Coef = {
"RR" "(" <start: Int> ".." <end: Int> ")" => Coef::RandRange(start..=end),
"RC" "(" <choices: Comma<ChoiceTimes>> ")" => Coef::RandChoice(choices.into_iter().flatten().collect()),
<v: Int> => Coef::Int(v)
}
Coef: Vec<Coef> = {
<coef: CoefType> <times: CoefTimes> => vec![coef; times],
<coef: CoefType> => vec![coef]
}
ChoiceTimes: Vec<i64> = {
<coef: Int> <times: CoefTimes> => vec![coef; times],
<coef: Int> => vec![coef]
}
CoefTimes: usize = {
<s:r"\*([0-9]+)"> => {
let regex = Regex::new("\\*([0-9]+)").unwrap();
let captures = regex.captures(s).unwrap();
let n = captures[1].parse::<usize>().unwrap();
n as usize
}
}
List: ListOp = {
<listops: ListConcat> => ListOp::Concat{ listops },
<list: ListIndexed> => list,
}
ListConcat = Concat<ListIndexed>;
ListIndexed: ListOp = {
<listop: ListIndexed> "@" "[" <indices: Indices> "]" => {
ListOp::ListOpIndexed { list_op: Box::new(listop), indices, direction: Direction::Sequence}
},
<listop: ListIndexed> "%" "[" <indices: Indices> "]" => {
ListOp::ListOpIndexed { list_op: Box::new(listop), indices, direction: Direction::Overlay}
},
"<" <list: List> ">" => list,
<list: ListBase> => list,
}
ListBase: ListOp = {
r"(List|&)" "[" <terms: Operations> "]" => {
ListOp::Const{ terms }
},
r"(List|&)" <name: Name> => {
ListOp::Named { name }
},
"ET" "(" <n: Int> ")" => {
ListOp::Const{ terms: et(n) }
},
r"(List|&)" <gen: Generator> => ListOp::GenOp { gen }
}
Indices: Indices = {
<indices: Comma<Index>> => Indices(indices),
};
Index: Index = {
<index: Int> => Index::Const { indices: vec![index] },
"(" <indices: Comma<Int>> ")" => Index::Const { indices },
<start: Int?>":"<end: Int?><skip: (":" Int)?> => {
match skip {
Some(skip) => Index::Slice { start, end, skip: skip.1 },
None => Index::Slice { start, end, skip: 1 }
}
},
"Random" "(" <n: Int> <seed: ("," Int)?> ")" => {
match seed {
Some(seed) => {
Index::Random { n, seed: seed.1 }
},
None => {
Index::Random { n, seed: random_seed() }
}
}
},
<index: Index> "|" <term: Composeable> => Index::IndexAndTerm {index: Box::new(index), term},
}
ComposeOperation: Term = {
<terms: Pipe<Composeable>> => {
Term::Op(Compose { operations: handle_fit_length_recursively(terms) })
},
<o: BaseOperation> => o,
}
Composeable: Term = {
<base_operation: BaseOperation> => base_operation,
<list: List> => Term::Lop(list),
<generator: Generator> => Term::Gen(generator),
}
Scale: Scale = {
<axis: Axis> <value: Rational> => Scale { axis, value }
}
BaseOperation: Term = {
"Csv1d" <scale: Parenthesized<Rational>?> <path: Import> => Term::Op(CSV1d { path, scale }),
"Csv2d" <scales: Parenthesized<Comma<Scale>>> <path: Import> => Term::Op(CSV2d { path, scales }),
//
r"\\|Lambda" <input_name: Parenthesized<Name>?> "{" <term: Operation> "}" => Term::Op(Lambda { term: Box::new(term),
input_name, scope: uuid::Uuid::new_v4().to_string()}),
"(" <o: Operation> ")" => o,
<fm: Fm> <m:Rational> => Term::Op(TransposeM {m}),
<fa: Fa> <a:Rational> => Term::Op(TransposeA {a}),
<pm: Pm> <m:Rational> => Term::Op(PanM {m}),
<pa: Pa> <a:Rational> => Term::Op(PanA {a}),
<lm: Lm> <m:Rational> => Term::Op(Length {m}),
<gain: Gm> <m:Rational> => Term::Op(Gain {m}),
r"Lowpass|LowPass" <c:Rational> <q:Rational> => Term::Op(Lowpass {
hash: generate_random_hash_string(),
cutoff_frequency: c,
q_factor: q
}),
r"Highpass|HighPass" <c:Rational> <q:Rational> => Term::Op(Highpass {
hash: generate_random_hash_string(),
cutoff_frequency: c,
q_factor: q
}),
r"Bandpass|BandPass" <c:Rational> <q:Rational> => Term::Op(Bandpass {
hash: generate_random_hash_string(),
cutoff_frequency: c,
q_factor: q
}),
"AsIs" => Term::Op(AsIs {}),
"Sine" <pow: Rational?> => {
Term::Op(Sine { pow })
},
"Sine" "[" <pows: Comma<Rational>> "]" => {
let mut operations = Vec::new();
for pow in pows {
operations.push(Term::Op(Sine { pow: Some(pow) }))
}
Term::Op(Sequence { operations })
},
r"Triangle|Tri" <pow: Rational?> => {
Term::Op(Triangle { pow })
},
r"Saw" => {
Term::Op(Saw)
},
r"Triangle|Tri" "[" <pows: Comma<Rational>> "]" => {
let mut operations = Vec::new();
for pow in pows {
operations.push(Term::Op(Triangle { pow: Some(pow) }))
}
Term::Op(Sequence { operations })
},
r"Square|Pulse" <width: Rational?> => {
Term::Op(Square { width })
},
r"Square|Pulse" "[" <widths: Comma<Rational>> "]" => {
let mut operations = Vec::new();
for width in widths {
operations.push(Term::Op(Square { width: Some(width) }))
}
Term::Op(Sequence { operations })
},
"Noise" => Term::Op(Noise {}),
"Reverse" => Term::Op(Reverse {}),
"Invert" => Term::Op(FInvert),
"Repeat" <i: Int> => {
let mut vec = Vec::new();
for x in 0..i {
vec.push(Term::Op(AsIs))
}
Term::Op(Sequence { operations: vec })
},
"AD" "("
<attack:Rational> ","
<decay: Rational> ","
<decay_type: Int> ")" => {
let asr = if decay_type == 2 {ASR::Long} else {ASR::Short};
Term::Op(AD {attack, decay, asr})
},
"Portamento" <m:Rational> => Term::Op(Portamento {m}),
"Reverb" <m:Rational> => Term::Op(Reverb {m: Some(m)}),
"Silence" <m:Rational> => Term::Op(Silence {m}),
r"(Sequence|Seq)" "[" <operations: Operations> "]" => Term::Op(Sequence { operations: operations }),
"Overlay" "[" <operations: Operations> "]" => Term::Op(Overlay { operations: operations }),
r"(ModulateBy|ModBy)" "[" <operations: Operations> "]" => Term::Op(ModulateBy { operations: operations }),
"FitLength" <op: Composeable> => {
Term::Op(
WithLengthRatioOf { with_length_of: Box::new(op), main: None }
)
},
"O" "[" <o: Overtones> "]" => Term::Op(Overlay { operations: o }),
<id: Name> => {
Term::Op(Id(id))
},
"#"<name: Name> => Term::Op(Tag(name)),
<name: Name> "(" <args: Comma<Operation>> ")" => {
Term::Op(
FunctionCall {
name,
args
}
)
},
<fm: Rational>"^"<length: Rational> => {
Term::Op(Compose { operations: vec! [
Term::Op(TransposeM { m: fm }),
Term::Op(Length { m: length }),
]})
},
BracedOvertone
};
Overtone: Term = {
"("
<fm:Rational> ","
<fa:Rational> ","
<g:Rational> ","
<p:Rational>
")"
=> Term::Op(Compose { operations: vec! [
Term::Op(TransposeM { m: fm }),
Term::Op(TransposeA { a: fa }),
Term::Op(Gain { m: g }),
Term::Op(PanA { a: p }),
]
})
}
BracedOvertone: Term = {
"{"
<fm:Rational> ","
<fa:Rational> ","
<g:Rational> ","
<p:Rational>
"}"
=> Term::Op(Compose { operations: vec! [
Term::Op(TransposeM { m: fm }),
Term::Op(TransposeA { a: fa }),
Term::Op(Gain { m: g }),
Term::Op(PanA { a: p }),
]
})
}
Overtones = Comma<Overtone>;
Comma<T>: Vec<T> = {
<v:(<T> ",")*> <e:T?> => match e {
None=> v,
Some(e) => {
let mut v = v;
v.push(e);
v
}
}
};
Parenthesized<T>: T = {
<t: T> => t,
"(" <t: T> ")" => t,
};
Lets<T>: Vec<T> = {
<v:(<T>)*> => v
};
Pipe<T>: Vec<T> = {
<v:(<T> "|")+> <e:T?> => match e {
None=> v,
Some(e) => {
let mut v = v;
v.push(e);
v
}
}
};
Concat<T>: Vec<T> = {
<v:(<T> "++")+> <e:T?> => match e {
None=> v,
Some(e) => {
let mut v = v;
v.push(e);
v
}
}
};
Compose = Pipe<Operation>;
Operations = Comma<Operation>;
Ints = Comma<Int>;
Rational: Rational64 = {
<n: Float> => f32_to_rational(n),
<n: Exponential> => exponential_to_rational(n),
<n: Int> => Rational64::new(n, 1),
<n: Int> "/" <d:Int> => Rational64::new(n, d),
};
CoefStart: (i64, i64) = {
<n: Int> "|" <d:Int> => (n, d),
}
Axis: Axis = {
F => Axis::F,
G => Axis::G,
L => Axis::L,
P => Axis::P,
};
Stems: Vec<String> = {
"->" "[" <names: Comma<Name>> "]" => names
}
Fm: String = <s: r"Fm|Tm"> => s.to_string();
Fa: String = <s: r"Fa|Ta"> => s.to_string();
Gm: String = <s: r"Gain|Gm"> => s.to_string();
Lm: String = <s: r"Length|Lm"> => s.to_string();
Pm: String = <s: r"PanM|Pm"> => s.to_string();
Pa: String = <s: r"PanA|Pa"> => s.to_string();
F: String = <s: r"f:"> => s.to_string();
G: String = <s: r"g:"> => s.to_string();
L: String = <s: r"l:"> => s.to_string();
P: String = <s: r"p:"> => s.to_string();
Import: String = <s:r"(\.?\./)+[a-zA-Z0-9_/]+\.csv"> => s.to_string();
Name: String = <s:r"[a-z_][a-zA-Z_0-9.]*"> => s.to_string();
Float: String = <s:r"-?(0|([1-9]\d*))\.\d+"> => s.to_string();
Exponential: String = <s:r"-?(0|([1-9]\d*))\.\d+(?:[eE]([-+]?\d+))"> => s.to_string();
Int: i64 = <s:r"-?[0-9]+"> => i64::from_str(s).unwrap();