subcode: block
    {
        BLOCK
    }

# -- DATA -------------------------
subcode: update_max(max, a)
    $if $(max)<$(a)
        $(max)=$(a)

subcode: update_min(min, a)
    $if $(min)>$(a)
        $(min)=$(a)

subcode: update_minmax(min, max, a)
    $if !defined $(min)
        $(min) = $(a)
        $(max) = $(a)
    $else
        $call update_min, $(min), $(a)
        $call update_max, $(max), $(a)

subcode: swap(a, b)
    ($(a), $(b)) = ($(b), $(a))


subcode: approx_zero(a)
    $if abs($(a))<1e-8
        $(a) = 0

# ----
subcode: dump(@a)
    $(for:$(a))
        print '$1 = ', "$1 "
    $print

subcode: dump_hash(h)
    $foreach $k in sort keys(%$(h))
        print "    $k: ", $$(h){$k}

# -- IO -------------------------
macros:
    In: In
    Out: Out

subcode: open_r(name)
    $(if:name~")
        $(set:name=$(name:strip))
    $(if:In~\$)
        $(set:_my_in=my $(In))
    $(else)
        $(set:_my_in=$(In))
    open $(_my_in), "$(name)" or die "Can't open $(name): $!\n"
    $while <$(In)>
        BLOCK
    close $(In)

subcode: open_w(name)
    $(if:name~")
        $(set:name=$(name:strip))
    $(if:name!~[>|])
        $(set:_name=>$(name))
    $(else)
        $(set:_name=$(name))
    $(if:Out~\$)
        $(set:_my_out=my $(Out))
    $(else)
        $(set:_my_out=$(Out))
    open $(_my_out), "$(_name)" or die "Can't write $(name): $!\n"
    BLOCK
    close $(Out)

subcode: open_W(name)
    &call open_w, $(name)
        $print "  --> [$(name)]"
        $(set:print_to=$(Out))
        BLOCK

subcode: assert(cond)
    $if !($(cond))
        die "$(cond) - FALSE!\n"

subcode: get_file_in_t(name)
    $(if:name~")
        $(set:name=$(name:strip))
    my $t
    &call block
        open In, "$(name)" or die "Can't open $(name).\n"
        local $/
        $t=<In>
        close In

subcode: get_file_lines(name, @lines)
    $(if:name~")
        $(set:name=$(name:strip))
    $(if:!lines)
        $(set:lines=@lines)
    my $(lines)
    &call block
        open In, "$(name)" or die "Can't open $(name).\n"
        $(lines)=<In>
        close In

#---------------------------------------- 
subcode: bench(n)
    use Time::HiRes
    my $time_start=Time::HiRes::clock()
    $for $i_bench=0:$(n)
        BLOCK
    my $time_finish=Time::HiRes::clock()
    printf "bench $(n) iterations: %f sec/iter\n", ($time_finish-$time_start)/$(n)

#---------------------------------------- 
#-- similar to HERE document -----------
perlcode: print_block
    my $codelist=MyDef::compileutil::get_named_block("last_grab")
    $if $codelist
        $foreach $t in @$codelist
            $t=~s/"/\\\"/g
            push @$out, "print Out \"$t\\n\";"

# -- dbl_list ----------------
subcode: dbl_list(name)
    $global @$(name), %$(name)
    @$(name)=()
    %$(name)=()

subcode: dbl_push(name, v)
    $if !$$(name){$(v)}
        $$(name){$(v)}=1
        push @$(name), $(v)

subcode: dbl_push_key(name, k, v)
    $if !$$(name){$(k)}
        $$(name){$(k)}=1
        push @$(name), $(v)

subcode: dbl_push_list(name, list)
    $(list)=~s/\s*;\s*$//
    my @tlist=split /,\s*/, $(list)
    $foreach $v in @tlist
        $call dbl_push, $(name), $v

#-- Strange but useful... ------
# $call match_count, $n, $s=~/'/g
subcode: match_count(n, @re)
    $(n) = () = $(re)

macros:
    # uses possessive quantifier
    RE_PAREN: (\((?:[^()]++|(?-1))*+\))

#-- Parser -------------------
# for simple parsing; for more complicated cases, ref perl/parse.def
subcode: parse_loop(l)
    $while 1
        $if $(l)=~/\G$/sgc
            last
        BLOCK
        $(if:!skip_else)
            $else
                die "parse_loop: nothing matches! [$(l)]\n"

    subcode: if_lex(@pattern)
        $case $(l)=~/\G$(pattern)/sgc
            BLOCK

    macros:
        quote: "(?:[^"\\]|\\.)*"

#---------------------------------------- 
# all print goes to $debug_console
subcode: init_debug_console
    use Socket
    $global $debug_console
    socket($debug_console, PF_INET, SOCK_STREAM, 0) or die "socket $!"
    my $port = 1300
    connect($debug_console, pack_sockaddr_in($port, inet_aton("localhost"))) or die "connect: $!"
    $print_to $debug_console
    {
        my $ofh = select $debug_console
        $|=1
        select $ofh
    }

# -- PATTERNS (less-used) ---------------
subcode: topdown_loop(name, top)
    my %$(name)_hash
    my @$(name)_list
    $global @topdown_list
    @topdown_list = ($(top))

    $while my $$(name)=pop @topdown_list
        $if !$$(name)_hash{$$(name)}
            $$(name)_hash{$$(name)}=1
            BLOCK

