Thanks for the quick response!
Do you perhaps know of a Quark that generates a syntax tree? Would be really useful! Particular since all the behaviour I need is already being done by the language somewhere…
I’ve managed to get it to work - its a bit dumb and will break pretty easily…
this.preProcessor = {
|code|
var openBracket = { |c| [ ${, $(, $[ ].includes(c) };
var closeBracket = { |c| [ $}, $), $] ].includes(c) };
var funcSeperator = { |c| [ $,, $;] .includes(c) };
var parseSingleInstance = {
|in, operator|
var operatorEnd = operator + 2; // given size of operator "|>"
var operatorBegin = operator - 1; // white space before
var operandStart = {
// go back until second space from operator, begining of line, opening bracket, or function seperator
var searching = true;
var pos = operatorBegin - 2;
var result;
while {searching} {
if( (in[pos] == $ ) || (pos <= 0) || openBracket.(in[pos]) || funcSeperator.(in[pos]),
{ result = pos; searching = false; },
{ pos = pos - 1; })
};
max(result, 0)
}.();
var preOperand = if(operandStart > 0, {in[0..operandStart]}, {""});
var operand = in[(operandStart+1)..operatorBegin];
var funcSpecEnd = {
var pos = operatorEnd;
var indent = 0;
var searching = true;
// go until, end of expression or indent level goes negative
while { (indent >= 0) && searching && (pos < in.size) } {
indent = case(
{ openBracket.(in[pos]) }, { indent + 1 },
{ closeBracket.(in[pos]) }, { indent - 1 },
{ indent }
);
case(
{(indent == 0) && (in[pos] == $;)}, { searching = false },
{ indent < 0 }, { searching = false; pos = pos - 1 },
{ pos = pos + 1 }
)
};
if(in[pos] == $;, pos - 1, pos);
}.();
var funcSpecWithoutSemi = if(in[funcSpecEnd] == $;, funcSpecEnd - 1, funcSpecEnd);
var funcSpec = in[operatorEnd..funcSpecWithoutSemi];
var posFuncSpec = if(funcSpecEnd != (in.size - 1), {in[(funcSpecEnd+1)..(in.size-1)]}, {""});
var replacement = format("[%].inject(%, {|n,f| f.(n)})", funcSpec.asString, operand);
format("% % %", preOperand, replacement, posFuncSpec).stripWhiteSpace;
};
var str = code.stripWhiteSpace;
var hasOperatorsLeft = str.find("|>");
while {hasOperatorsLeft.isNil.not} {
str = parseSingleInstance.(str, hasOperatorsLeft);
hasOperatorsLeft = str.find("|>");
};
str;
};
But stuff like this now works, which I think is a much more musical way to think about using UGens - also avoids pre-declaring variables and too many variables that won’t be used.
SynthDef(\pipeTest, {
var sig = ( 200 |>
Saw.ar(_),
LPF.ar(_, 350),
HPF.ar(_, 50),
CompanderD.ar(_, thresh: -15.dbamp, slopeAbove: 1/4),
Pan2.ar(_, MouseX.kr(-1, 1))
);
Out.ar(0, sig);
}).add;
You can also write normal functions in there …
(
x = 1;
f = {|v| v / 2 };
j = x |> {|v| "J"; v}, _ + 1, _ * 2, {|a| a*1; a*[9, 2, 4, 2, 14]; {{SinOsc.ar()}}; a + 1}, f.(_);
y = (x |> _+1,
{|v| "Y"; v},
_ * 2,
{|a| a*1; a*[9, 2, 4, 2, 14]; {{SinOsc.ar()}}; a + 1},
f.(_)
) * 2;
h = { |v| v |> _ - 1, _ / 2 };
z = x |> _ * 100, _ + y, h;
[x, j, y, z].asString.warn; // WARNING: [ 1, 2.5, 5.0, 52.0 ]
g = { 200 |> SinOsc.ar(_), _ * -15.dbamp }.play;
)
… but if you mention "|>"
in a string it will break - also putting comments in just brackets or stuff will just break everything, so will having more complex operands (it really needs to be a variable name and nothing else).
Anyway, because of the weird edge cases that I can’t be bothered to fix, I’m gonna stick with a function call, which is a shame.
~pipe = { |operand ...funcs| funcs.inject(operand, {|n,f| f.(n)}) };