See the attached file. Save it anywhere, make it executable, that is all.

I had to make some last minute changes, hopefully this didn't break the code
which I didn't touch almost a year...


NOTE! I do not want to use any module/library, so it doesn't use File::Temp
and simply assumes that the temp dir is "/tmp"; you can change $TMPDIR at the
start if this is not correct on your system. fpp will create/remove a couple
of __FPP__.* files in this directory.



Currently it simply ignores import/library. If you want to put the FPP() code
into some.lib, you will need to use fpp_import/fpp_export instead.

IOW, import("file-with-FPP-code") won't work; fpp_import("file-with-FPP-code")
or fpp_library("file-with-FPP-code") should (mostly) work.

The search path for fpp_import/fpp_library is @INCDIR at the start of fpp,
you should probably change it if you want to use fpp_import/fpp_library.


Currently it blindly passes almost all options to /usr/bin/faust, so it will
randomly fail if faust outputs anything but C/C++ code in scalar mode.

Oleg.
#!/usr/bin/perl -w
use strict;

our $TMPDIR = '/tmp';
our @INCDIR = qw(/home/oleg/FAUST/LIB /home/oleg/FAUST/faust.git/libraries);

#------------------------------------------------------------------------------
package re;
our $bal_cur = '(?:\{(?:(?>[^\{\}]+)|(?-1))*\})';
our $bal_par = $bal_cur =~ tr/{}/()/r;
our $bal_sqr = $bal_cur =~ tr/{}/[]/r;

sub qr          { my $r = join '',@_; qr/$r/; }
sub list        { join '|', map "(?:$_)", @_; }
sub chop2($)    { substr $_[0], +1,-1 }
sub chop2s($)   { &chop2 =~ s/^\s+|\s+$//gr }

sub split_args(_)
{
        my ($a, @a) = &chop2;
        push @a, $& =~ s/^\s+|\s+$//gr
                while $a =~ /(?: ($re::bal_par) | [^,])+/oxg;
        return @a;
}

#------------------------------------------------------------------------------
package perl::core;

sub __z_expr($$) { $_[0] }
sub __z_pure($$) { &pp_cpp::zmap =~ /^\w+\[[^]]+\]$/ ? 1 : 0 }
sub __z_iota($$) { &pp_cpp::zmap =~ /\bIOTA\b/ ? 1 : 0 }

sub __z_item($$$)
{
        my ($vec, $len, $idx) = @_;

        $idx =~ s/^\(([+-]?\w+)\)$/$1/;
        my $expr = &pp_cpp::zmap ||
                return  $vec eq '0' || $idx eq '0' && $len eq '0' ?
                        $vec : die "bad delay line: '$vec' [$idx]\n";

#       my @inp = pp_cpp::inputs($expr);
#       warn "WARN! delay line '$vec' inputs: @inp\n" if @inp;

        $expr =~ /\[(\(\((?:dsp->)?IOTA\s*-\s*)?/g;
        my $full = $1 ? "$idx + $len - " : "$idx - $len + ";
        $expr =~ s/\G(\Q$len\E\b)?/defined $1 ? $idx : $full/er;
}

sub __l_len($) { 0 + &re::split_args }

sub __l_item($$)
{
        my @l = re::split_args (my $l = shift);
        @_ == 1 && $_[0] =~ /^\(?(\d+)\)?$/ && $1 < @l
                or die "bad list item: $l\[@_]\n";
        $l[$1];
}

sub __l_for(&$$$)
{
        my ($l, $i, $v, $e) = @_;
        my @l = re::split_args $l;
        /\W/ and die "bad .for() arguments: ($i, $v, $e)\n"
                for $i, $v;
        my $re = qr/\b(?:($i)|($v))\b/;
        join '; ', map $e =~ s/$re/$1?$_:$l[$_]/egr, 0..$#l;
}

#------------------------------------------------------------------------------
package pp_dsp;
our (%GLOB, %INST);

sub expand_perl; sub expand_perl
{
        $_[0] =~ s/\$\.(\w+)($re::bal_par)/
                my $f = can perl::core:: $1
                        or die "undefined perl func: $1$2\n";
                ord '$' == ord prototype $f
                        ? $f->(re::split_args expand_perl $2)
                        : expand_perl $f->(re::split_args $2)
        /oegr;
}

sub __expand_fpp
{
        my ($this, $code, $args) = @_;
        my ($name, $uniq) = 0+$code;

        sub expand; local *expand = sub
        {
                my ($once, $dict, $uarg) = shift;

                if ($code->{uarg}) {
                        while (my ($n, $a) = each %{$code->{uarg}}) {
                                next if $args->[$n] eq $a;
                                die "can't rebind arg_$n from '$a' to 
'$args->[$n]'\n";
                        }
                }

                if ($once) {
                        $uniq = $once->{'$'} ||= {};
                        $code = $once->{$name}, return $this
                                if $once->{$name};
                }

                $this->{$_} .= expand_perl +($code->{$_}//next) =~ s{\$(\w+)\b}{
                        my $n = $1;
                        $dict->{$n} ||= ($n =~ /\D/)
                                ? ("$n\__" . ++($uniq->{$n}||='00'))
                                : ($uarg->{$n} = $args->[$n] // die)
                }egr for @_;

                if ($once) {
                        $code = $once->{$name} = {%$code};
                        $code->{uarg} = $uarg if $uarg;
                        delete @{$code}{@_};
                        if (my $vars = re::list keys %$dict) {
                                ref or s/\$($vars)\b/$dict->{$1}/g
                                        for values %$code;
                        }
                }

                $this;
        };

        expand \%GLOB, qw(FILE);
        expand \%INST, qw(DECL INIT LOOP POST);
        expand  undef, qw(file decl init loop post exec);
}

sub expand_fpp
{
        my ($func, $args) = @_;
        my ($code, $this) = ($func->{code}, {});

        if ($func->{deps} && !$INST{0+$code}) {
                __expand_fpp $this, $_ for @{$func->{deps}};
        }

        shift @$args if exists $func->{arg0};
        __expand_fpp $this, $code, $args;
}

my (%STRN, %FUNC, %DEPS);

sub fix_strn(_)
{
        defined (wantarray)
                ? $_[0] =~ s/\STRN!(\d+)\b/$STRN{$1}||die/egr
                : $_[0] =~ s/\STRN!(\d+)\b/$STRN{$1}||die/eg;
}


sub mk_eval($$)
{
        mk_ffunc(0,$_[0],'_int_',{eval => undef, code => {exec=>$_[1]}});
}

sub mk_ffunc
{
        my ($pure, $narg, $type, $func) = @_;

        $func = {code => {exec => $func}} unless ref $func;

        if ($type =~ /^_(int|float)_$/) { $type = $1 }
        else {
                $func->{type} = $type;
                $type = $type =~ /^(?:float|(?:(?:long\s+)?double)|quad)$/
                        ? 'float' : 'int';
        }

        my $arg0 = '';
        if (0) {
        } elsif (!$pure && $narg) {
                CORE::state $kSamp;
                $arg0 = $kSamp ||= mk_eval 0, 'BUG';
        } elsif ($pure && !$narg) {
                $arg0 = '0';
        }

        if ($arg0 ne '') {
                $narg++;
                $func->{arg0} = undef;
                $arg0 = "($arg0)";
        }

        CORE::state $uniq = '000';
        my $name = 'fpp_func_' . ++$uniq;

        $FUNC{$name} = $func;
        my $args = join ',', qw(float) x $narg;
        qq{ffunction($type $name($args),"","")$arg0}
}

sub push_fpp_bus
{
        CORE::state $done; return if $done++;
        my $b0 = mk_eval 0, '';
        my $b1 = mk_eval 1, '$0';
        my $b2 = mk_eval 2, '$0,$1';
        print main::FAUST "// ======== <push>\n",
                "_fpp_bus_(0) = $b0; _fpp_bus_(1) = $b1;\n",
                "_fpp_bus_(n) = _, _fpp_bus_(n-1) : $b2;\n";
}

sub expand_args
{
        my ($argn, $argz, $argl) = @_;

        my $re_argn = join '|', keys %$argn;

        while (/\$($re_argn)\b/g) {
                my ($arg, $pos) = ($1, pos() - 1 - length $1);
                my ($out, $type, $func, @args) = '$'.$argn->{$arg};

                if (defined(my $z = $argz->{$arg})) {
                        ($func, $type, @args) = ('__z_', 'dline', $out,$z);
                        if (/\G\s*($re::bal_sqr)/gc) {
                                ($func, $type) = '__z_item';
                                my $i = re::chop2s $1;
                                push @args, "($i)";
                        }
                }
                elsif (exists $argl->{$arg}) {
                        @args = "($out)";
                        if (/\G\s*($re::bal_cur)/gc) {
                                $func = '__l_item';
                                my $i = re::chop2s $1;
                                push @args, "($i)";
                        } elsif (/\G\s*\./) {
                                ($func, $type) = ('__l_', 'flist');
                        }
                }

                if ($type) {
                        my $err = '$'.$arg;
                        /\G\s*\.\s*(\w+)/gc or die "bad usage of $type arg: 
'$err'\n";
                        $func .= $1; $err .= $&;

                        my $args = length prototype (can perl::core:: $func
                                                        or die "bad $type op: 
'$err'\n");
                        if ($args -= @args) {
                                /\G\s*($re::bal_par)/gc or die "bad usage of 
$type op: '$err'\n";
                                push @args, re::chop2 $1; $err .= $&;
                                $args == re::split_args $1 or die "bad $type op 
argc: '$err'\n";
                        }
                }

                $out = "\$.$func(${\join',',@args})" if $func;
                substr $_, $pos, pos() - $pos, $out;
                pos = $pos;
        }
}

sub parse_args
{
        my $lambd;
        my (%argn, %argz, %argl);
        my (@args, @pass, @with);

        s/^\s*($re::bal_par)//o or die "arglist";
        for (re::split_args $1) {
                my ($with, $argn,$argv, $m,$z,$l) = s/^=\s*//;
                s/^(?=\D)(\w+)\s*// or die "bad arg: '$_'";
                exists $argn{$argn = $argv = $1} and die "dup arg: '$1'";

                if (s/\s*(?:($re::bal_sqr)|($re::bal_cur))$//) {
                        $1 ? ($z = re::chop2s $1) : ($l = re::chop2s $2);
                        $m = s/\s*:$//;
                }

                if (!$with and $with = s/^=\s*//) {
                        push @with, "$argn=$_; ";
                } elsif ($m ||= ord $_ == ord ':') {
                        $argv = "($argn$_)";
                } elsif (length) {
                        die "bad arg '$argn' modifier: '$_'";
                }

                if (defined $z) {
                        $argv = "$argv@(int($z))" if $z;
                        $argz{$argn} = $z =~ /\D/
                                ? '$'.(-1 + push @pass, "int($z)")
                                : ($z || 0);
                } elsif (defined $l) {
                        push_fpp_bus;
                        $argl{$argn} = undef;
                        my ($n, $a) = ("outputs($argv)", ", $argv");
                        if (length $l) {
                                $n = "int($l)";
                                $a = '', $with = 1 unless $with || $m;
                        }
                        $argv = "_fpp_bus_($n$a)";
                }

                $argn{$argn} = @pass;
                push @pass, $argv;

                if (!$with) {
                        push @args, $argn;
                        $lambd ||= $m || $z || defined $l;
                }
        }

        if ($lambd || @pass > @args) {
                $lambd = join ',', @args;
                $lambd = "\\($lambd)." if $lambd;
                $lambd .= '(' . join ',', @pass;
                $lambd .= " with { @with}" if @with;
        }

        0+@pass, $lambd, %argn && sub {
                expand_args \%argn, \%argz, \%argl;
        };
}

sub parse_block
{
        my ($zone, $re_z, $earg) = @_;
        $_[1] = $re_z = qr/(.*?)(?:\b($re_z):|$)/s
                unless ref $re_z;

        s/^\s*($re::bal_cur)//o or die "body";
        local $_ = re::chop2 $1;

        /\$\s*[\W\d]\w*/x and die "bad sigil var: '$&'\n";
        &$earg() if $earg;

        for (my $code;;) {
                /$re_z/g;
                my ($c, $z) = ($1, $2);
                for ($c) {
                        /\S/ or last;
                        s/^\s*$//mg;
                        s/^\n+//; s/\s*$/\n/;
                        s/^(\s*)// and s/^$1//mg;
                        $code->{$zone} .= $_;
                }
                $zone = $z || return $code;
        }
}

sub parse_fpp
{
        my ($narg, $lambd, $earg) = parse_args;

        my $func;

        if (s/^\s*\[//) {
                s/(^.*)?\]// or die 'deps list';
                $func->{deps} = [map {
                        /^\s*(?=\D)(\w+)\s*$/ or die "bad dep name: $_";
                        $DEPS{$1} || die "bad dep: $_";
                } split ',', $1];
        }

        my $pure = s/^\s*pure\b//;
        $func->{eval} = undef if s/^\s*eval\b//;

        my @type; @type = map s/^\s+|\s+$//gr, split ',', $1
                if s/^\s*(?=\S)([^\{]+)//;

        CORE::state $re_zone = 
'FILE|DECL|_INIT|INIT|file|decl|_init|init|LOOP|loop|POST|post|exec';
        my $code = parse_block 'exec', $re_zone, $earg;

        s/\s+/ /g, s/\s*;*\s*$/;/ for $code->{exec} // die 'no exec';

        my ($type, $tuple);
        if (@type <= 1) {
                $type = $type[0] // '_float_';
        } else {
                CORE::state $uniq = '000';
                $type = 'struct fpp_tuple_' . ++$uniq;
                $code->{exec} =~ s/([^;]+);$/ ($type){$1 };/ or die;

                my $sdef = join ' ', map "$type[$_] m_$_;", 0..$#type;
                # OK, this is ugly, but lets do this here for now ...
                $sdef =~ s/\b_int_\b/int/g; $sdef =~ 
s/\b_float_\b/$pp_cpp::O_FLOAT/g;
                $code->{FILE} .= "$type { $sdef };\n";

                $tuple = join ',', map {
                        mk_ffunc 0, 1, $type[$_], "((($type)\$0).m_$_)";
                } 0..$#type;
        }

        $func->{code} = $code;
        exists $func->{eval}
                ? ($code->{exec} =~ s/\s*;$//)
                : ($code->{exec} = "({ $code->{exec} })");
        my $ffunc = mk_ffunc $pure, $narg, $type, $func;
        $ffunc =  "$lambd : $ffunc)"  if $lambd;
        $ffunc = "($ffunc <: $tuple)" if $tuple;
        $ffunc;
}

sub parse_dep
{
        s/^\s*\(\s*(\w*)\s*\)// or die 'dep name';
        my $name = $1;

        CORE::state $re_zone = 'FILE|DECL|_INIT|INIT|LOOP|POST';
        my $code = parse_block 'FILE', $re_zone;

        if ($name) {
                die 'redefined' if $DEPS{$name};
                $DEPS{$name} = $code;
        } else {
                $DEPS{''}{$_} .= $code->{$_} for keys %$code;
        }

        s/^\s*;// ? '' : die 'expected ;';
}

sub parse_dsp
{
        our $REGERROR;
        s{
                (" (*:string) (?:\\.|[^"])* (*SKIP) ") |
                (?: \/\* (*:comment) (?:[^\*]+|\*(?!\/))* (*SKIP) \*\/) |
                (?: \/\/ [^\n]*)
        }{
                if (defined $1) {
                        CORE::state $uniq = '000';
                        $STRN{++$uniq} = $1;
                        'STRN!' . $uniq;
                }
        }xegs;

        die "unterminated $REGERROR" if $REGERROR && $REGERROR ne 1;
        s/(?:\h*\n){3,}/\n\n/g; # unneeded

        s{
                \b (?:fpp_)(?:(import)|(library)) \b
                (?: \s*\(\s* STRN!(\d+) \s*\)\s* (?(1);) )?
        }{
                my $n = $3 && $STRN{$3} //
                        do { /\G([^\n]*)/; die "syntax error: '$1'"; };
                main::parse_file(re::chop2 $n, !$1);
        }xeg;

        my $dsp = ''; while (m{
                \b (?:
                        __FPP (?{\&parse_dep}) |
                          FPP (?{\&parse_fpp})
                ) \b
        }xo) {
                substr $_, 0, length($`) + length($&), '';
                $dsp .= fix_strn $`;
                $dsp .= $^R->();
        }
        $dsp . fix_strn;
}

#------------------------------------------------------------------------------
package pp_cpp;
our ($O_FLOAT) = 'float';

my $THIS = 'dsp';
my $TYPE = '(?: int | float | double | quad)';

sub re_call { my $f = &re::list; "\\b($f)\\s*($re::bal_par)"; }
sub re_this { my $t = &re::list; "\\b(?:$THIS->)?($t)\\b"; }
sub re_cast
{
        my $expr = "($_[0])";
        my $cast = re::list "\\b $TYPE \\(  $expr \\)",
                            "\\( $TYPE \\)+ $expr";
        qr/(?| $cast )/x;
}

our (%ZMAP, $FILE, $DECL, $INIT, $EXEC);

sub uncast_one {
        CORE::state $cast = re_cast '.*';
        1 while s/\A\s* $cast \s*\z/$1/oxg;
}
sub uncast_all {
        my $cast = re_cast $_[0];
        for ($INIT, $EXEC) { 1 while s/$cast/$1/g; }
}

sub fix_types
{
        my $funcs = shift;
        my $ftype; while (my ($n, $f) = each %$funcs) {
                $ftype->{$n} = exists $f->{eval} ? undef
                                    : $f->{type} // next
        }

        uncast_all my $calls = re_call keys %{$ftype || return};

        my (%vtype, @voids);
        for ($INIT, $EXEC) {
                $vtype{$1} = $ftype->{$2} // do {
                        push @voids, $1;
                        'void';
                } while /\b (\w+) \s*=\s* $calls/xg;
        }

        %vtype or return;
        uncast_all my $vtype = re_this keys %vtype;
        s/\b $TYPE \s+ $vtype/$vtype{$1} $1/xg
                for $DECL, $EXEC;
        s/\h* $vtype \s* = \s* 0; \h*\n?//xg    # TempPerm, ocpp only
                for $INIT;

        @voids or return;
        my $voids = re_this @voids;
        s/\h* void \s+ $voids \s*; \h*\n?//xg
                for $DECL, $EXEC;
        for ($INIT, $EXEC) {
                s/$voids (?! \s*= )/_($1)/xg;
                s/\h* (?:void\s+)? $voids \s*=\s* ([^;]+) \s*; \h*\n?
                 /_($1,($2))/xg;
        }
}

sub mark_block($$_)
{
        $_[2] =~ s/(?<=\{)(?=(\n\h*)\S)/$1#MARK $_[0]/ or die;
        my $ind = $1;
        $_[2] =~ s/(?=\n\h*\}$)/$ind#MARK $_[1]/ or die;
}
sub add_marks
{
        $DECL =~ s/^(?=(\h*)\S)/$1#MARK CALL\n/m or die;

        mark_block 'CALL', 'EXIT' for $INIT, $EXEC;

        $EXEC =~ s/(\n\h*) (for \s* ($re::bal_par) \s*) ($re::bal_cur)/
                my ($ind, $for, $block) = ($1,$2,$4);
                mark_block 'LOOP', 'POST', $block;
                $ind . "#MARK _CALL" .
                $ind . $for . $block .
                $ind . "#MARK _EXIT";
        /oex or die;

        fill_mark($EXEC, $_, '') for qw(CALL EXIT _CALL _EXIT);
}
sub fill_mark
{
        my (undef, $mark, $code) = @_;
        $code .= "\n" if $code ne '';
        $_[0] =~ s/^(\h*)#MARK $mark\n/
                my $i = $1;
                $code =~ s|^(?=\h*\S)|$i|mgr;
        /me or die;
}

sub expand_all
{
        my ($extra, $funcs) = @_;

        my %voids;
        sub expand_void; local *expand_void = sub
        {
                return $voids{$_[0]} // die if @_==1;
                $voids{$_[0]} = re::chop2 $_[1];  '';
        };

        my @extra = $extra;
        sub expand_func; local *expand_func = sub
        {
                my ($name, $args) = @_;
                s/\.0[fL]$// for @$args;
                my $this = pp_dsp::expand_fpp $funcs->{$name}, $args;
                # s/THIS/ for values %$this;
                push @extra, $this if 1 < keys %$this;
                return delete $this->{exec};
        };

        my $calls = re::qr re_call '_', keys %$funcs;
        sub expand_code; local *expand_code = sub {
                s/$calls/
                        my ($f, @a) = ($1, re::split_args $2);
                        uncast_one, expand_code for @a;
                        $f eq '_' ? expand_void @a
                                  : expand_func $f,\@a;
                /xge;
        };

        expand_code for $INIT, $EXEC;

        sub collect; local *collect = sub {
                join '', grep defined, map @$_{@_}, @extra;
        };

        add_marks;
        $FILE .= collect qw(FILE file);
        $FILE .= "\n" if $FILE ne "\n";
        fill_mark $DECL, 'CALL', collect qw(DECL decl);
        fill_mark $INIT, 'CALL', collect qw(_INIT _init);
        fill_mark $INIT, 'EXIT', collect qw(INIT init);
        fill_mark $EXEC, 'LOOP', collect qw(LOOP loop);
        fill_mark $EXEC, 'POST', collect qw(POST post);
}

sub read_cpp
{
        my ($fd, %items) = @_;

        my ($is_c, $class, @code);

        local $_;
        sub getl; local *getl = sub {
                push @code, $_ if defined;
                defined ($_ = <$fd>);
        };
        sub mark; local *mark = sub {
                push @code, $_ unless defined $_[1];
                push @code, $_[1];
                *{delete $items{$_[0]}} = \$code[-1];
                undef $_;
        };

        while (getl) {
                unless ($class) {
                        ($class) = /^\s*\#define \s* FAUSTCLASS \s+(\w+)\s*$/x;
                } elsif (/^\s*$/) {
                        mark -file, $_;
                } elsif (/^\s*typedef \s+ struct \s* \{ \s*$/x) {
                        $is_c = 1;
                        last;
                } elsif (/^\s*class \s+ $class \s*:\s* public \s+ (?:\w+) 
\s*\{\s*$/x) {
                        $is_c = 0;
                        while (getl) { last if /^\s* private: \s*$/x }
                        last;
                }
        }
        eof and die "can't find the start of dsp declaration";

        mark -decl;
        while (<$fd>) {
                last if $is_c
                        ? m/^\s* \} \s* $class \s*; \s*$/x
                        : m/^\s* public: \s*$/x;
                $code[-1] .= $_;
        }
        eof and die "can't find the end of dsp declaration";

        %items = map {$_.$class, $items{$_}} keys %items
                if $is_c;

        my $calls = re_call keys %items;
        while (getl) {
                # temporary hack
                if (/^(\s*)instanceConstants\(/) {
                        (my $i, $_) = $_;
                        1 while getl && !/^\s*instanceClear/;
                        $_ .= $i; next;
                }

                my ($prot, $func, undef, $body)
                        = /^(.*? \b void \s+ $calls \s*) (\{ \s*\z)/x
                        or next;

                $items{$func} or die "redefenition of $func()";

                while (<$fd>) {
                        $body .= $_;
                        next unless /^\s* \} \s*$/x;
                        last if $body =~ /^($re::bal_cur)\s*\z/o;
                }
                eof and die "can't find the end of $func()";
                mark $func, $prot.$body;
        }

        die "can't find funcs: ", join(',', map "$_()", keys %items)
                if %items;

        \@code;
}

# ----------------------------------------------------------------------------
my $ZVEC = '(?:\b(?:$THIS->)?[if][VR]ec\d+\b)';
my $TEMP = '(?:\b[if]Temp\d+\b)';

sub inputs
{
        my %seen;
        grep defined && !$seen{$_}++, $_[0] =~ /\b(?:
                (?:$TYPE) | (?:$THIS) | (?:IOTA) | (?:[if]Const\d+) |
                (?: (?:input|[if][VR]ec)\d+\b) |
                ((?=\D)\w+ (?! \(\s*[^\)] ))
        )\b/oxg
}

sub __zmap($)
{
        my $v = $_[0]; # 1==(() = ($v =~ /$ZVEC/og))
        return $v if $v =~ /\b$ZVEC/og ? $v !~ //g : do {
                my ($c, $t) = 0;
                $v =~ s{($TEMP)}{defined($t = $ZMAP{$1}) ? (++$c, $t) : $1}oeg;
                $c == 1;
        };
}

sub build_zmap
{
        for (split /(?:\h*;)?\n/, $EXEC) {
                my ($l, $r) = /(.*\S) \s*=\s* (.*)/x
                        or next;
                if (my ($t) = $l =~ /($TEMP)$/o) {
                        $ZMAP{$t} = __zmap $r || next;
                } elsif (my ($v, $i) = $l =~ /($ZVEC)\[([^\]]+)\]$/o) {
                        $i eq 0 || $i =~ s/\bIOTA\b/(IOTA - 0)/ or next;
                        $ZMAP{$r} = $v."[$i]";
                }
        }
}

sub zmap($)
{
        my $expr = $_[0]; $ZMAP{$expr} //= __zmap $expr;
}

# ----------------------------------------------------------------------------
sub opt_const
{
        # TODO: can make $DECL empty, add_marks(DECL) will die.
        # move the callsite somewhere else but after fix_type()
        # which can remove a "void" var we can opt otherwise.
        my $freq = $_[1] ? '' : 'fSamplingFreq';
        my @temp = grep { $EXEC !~ /\b$_\b/ and $_ ne $freq }
                          $DECL =~ /\b$TYPE\s+(\w+);/oxg
                   or return;

        my $decl = '';
        my $temp = re::list @temp;
        $DECL =~ s/\h*($TYPE\s+($temp);\h*\n)/$decl .= $1; ''/xeg;
        $INIT =~ s/\b$THIS->($temp)\b/$1/xg; #if $IS_C
        substr +($_[0]->{_INIT} //= ''),0,0, $decl."\n";
}

sub do_cpp
{
        my ($fd, $extra, $funcs) = @_;

        my $code = read_cpp $fd,
                -file                   => \*FILE,
                -decl                   => \*DECL,
                instanceConstants       => \*INIT,
                compute                 => \*EXEC;

        build_zmap;

        fix_types       $funcs;
        opt_const       $extra, 0;
        expand_all      $extra, $funcs;

        pp_dsp::fix_strn for $FILE, $INIT, $EXEC;
        $code;
}

#------------------------------------------------------------------------------
package main;

sub slurp_file
{
        my ($name, $idir) = @_;

        my $path; if (!$idir || $name =~ /^\.{0,2}\//) {
                $path = $name if -f $name;
        } else {
                for (@$idir) {
                        my $n = "$_/$name";
                        $path = $n, last if -f $n;
                }
        }

        $path // die "can't find file '$name'";
        open my $fd, '<', $path or die "can't open file '$path': $!";
        local $/; <$fd>;
}

sub parse_file
{
        my ($name, $islib) = @_;
        my ($isinc, $env) = defined $islib;

        local $_;
        if (defined $name) {
                CORE::state %cache;
                CORE::state $uniq = '00';
                # import() after library() can't work anyway, but let's
                # check $islib and return ''; this allows import(stdfaust)
                # which does library(all) which in turn imports everything.
                return $islib && $cache{$name} if exists $cache{$name};
                $env = $cache{$name} = $islib && 'FPP_ENV_' . ++$uniq;
                $_ = slurp_file $name, $isinc && \@INCDIR;
        } else {
                warn "reading from stdin ...\n" if -t STDIN;
                $_ = do { local $/; <STDIN> };
                $name = '<stdin>';
        }

        my $dsp = pp_dsp::parse_dsp;

        print FAUST "\n" if tell FAUST;
        print FAUST '// ======== ', $isinc?'INCLUDE: ':'CMDLINE: ', $name, "\n";
        print FAUST $env, " = environment {\n" if $islib;
        print FAUST $dsp;
        print FAUST "};\n" if $islib;

        $env;
}

sub usage
{
        die "fpp: usage\n";
}

my @TMPF; END { unlink @TMPF }

sub main
{
        my ($faust, $fmt, $ktf, @opt, @inp, $out) = 'faust';

        while (defined($_ = shift)) {
                if (ord != ord '-')             { push @inp, $_; next; }
                elsif ($_ eq '-k')              { $ktf = 1; next; }
                elsif ($_ eq '-fmt')            { $fmt = shift // usage; next; }
                elsif ($_ eq '-faust')          { $faust = shift // usage; 
next; }
                elsif ($_ eq '-o')              { $out = shift // usage; next; }
                elsif (/^-(double|quad)$/)      { $O_FLOAT = $1; }

                push @opt, $_;
                push @opt, shift // usage if /^
                        -mdlang | --mathdoc-lang        |
                        -f      | --fold                |
                        -mns    | --max-name-size       |
                        -mcd    | --max-copy-delay      |
                        -a                              |
                        -cn     | --class-name          |
                        -scn    | --super-class-name    |
                        -pn     | --process-name        |
                        -t      | --timeout             |
                        -vs     | --vec-size            |
                        -lv     | --loop-variant        |
                        -lang   | --language            |
                        -A      | --architecture-dir    |
                        -I      | --import-dir          |
                        -L      | --library             |
                        -O      | --output-dir          |
                        -inj    | --inject              |
                        -fm     | --fast-math
                $/x;
        }

        -d -w $TMPDIR or die "ERR!! bad \$TMPDIR '$TMPDIR': $!\n";
        my ($dsp, $cpp) = map "$TMPDIR/__FPP__.$_", qw(dsp cpp);
        push @TMPF, $dsp, $cpp unless $ktf;

        open FAUST, '>', $dsp or die "ERR!! can't open '$dsp': $!\n";
        do { parse_file shift @inp} while @inp;
        close FAUST;

        unlink $cpp;
        system $faust, @opt, $dsp, '-o', $cpp and die "\n";
        open my$fd, '<', $cpp or die "ERR!! can't open '$cpp': $!\n";
        my $code = pp_cpp::do_cpp $fd, $DEPS{''}, \%FUNC;

        open STDOUT,'>', $out or die "ERR!! can't open '$out': $!\n"
                if defined $out;

        if (defined $fmt) {
                $fmt eq 0 or usage;
                print $FILE, $DECL, $INIT, $EXEC;
        } else {
                print @$code;
        }
}

main @ARGV;
_______________________________________________
Faudiostream-users mailing list
Faudiostream-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/faudiostream-users

Reply via email to