Jack Campin <[EMAIL PROTECTED]> writes:

> Here's an example; the chords could hardly be simpler (play the lower
> octave along with the named note), but even here there's less to type
> and read than writing them out explicitly.

I've appended a little Perl program which will, from Jack's input, create
output that is identical to his expanded text. I don't know whether it 
is general enough (it's probably too general in places) but it could 
serve as a starting point.

Anselm
-- 
Anselm Lingnau .......................................... [EMAIL PROTECTED]
Programming graphics in X is like finding sqrt(pi) using Roman numerals.
                                                               -- Henry Spencer
#!/usr/bin/perl
# abcmac -- Barfly-style macro preprocessor for ABC files.
#
# Copyright � 2001 Anselm Lingnau <[EMAIL PROTECTED]>. Use this as you
# like as long as you don't alter or remove this comment or pretend that
# you wrote it yourself.
#
# Lines of the form `m: On/ = [n/n,/]' (where `O' is an otherwise unused
# letter in abc) define a macro which will subsequently be replaced
# by the expansion on the right of the equals sign. In the macro name,
# `n' is a placeholder for a note; instances of `n' in the expansion
# will be replaced by the actual note.
#
# Points to watch out for:
# - Right now pretty much anything can be used as a macro name. This
#   should probably be tightened up to allow just the unused letters.
# - It's probably too simple-minded about where macros can be defined.
#   Do macros have scope?
# - Can macros have more than one argument? What exactly is allowed as
#   the argument?
#
# Implemented according to an example provided by Jack Campin.

use strict;

# This defines what a macro takes as an argument.
# Currently the argument is a note name (no length).
my $arg = q{[\^=_]?[A-Ga-g](,*|\'*)};

my $subst;
my @m;

while (<>) {
    if (/^([A-Za-z]):/) {       # header line
        if ($1 eq 'm') {        # macro definition
            push @m, $_;
        } elsif ($1 eq 'K') {   # last line in header
            my @subst = ();
            # Construct a sequence of expansion commands for the macros.
            # Make sure to expand longer-named macros first, to avoid
            # replacing `On' before `On/'
            foreach my $macro (@m) {
                my ($name, $value) = $macro =~ /m:\s*(\S+)\s*=\s*(.*)$/;
                my $name_len = length $name;
                $name =~ s/n/($arg)/;
                $value =~ s/n/\$1/g;
                push @subst, [$name_len, qq{s\x01$name\x01$value\x01g;\n}];
            }
            foreach my $s (sort { $$b[0] <=> $$a[0] } @subst) {
                $subst .= $$s[1];
            }
        }
        print;                  # This prints �m:� lines as well - should it?
    } elsif (!/^%/) {           # non-comment line -- expand macros
        chomp;
        my $out = '';
        while (length $_) {
            if (s/^(".*?")//) { # leave stuff in quotes alone
                $out .= $1;
            } else {            # look for macro calls to preprocess
                my $v;
                s/^([^\"]*)//;
                for ($v = $1) { eval $subst; $out .= $_; }
            }
        }
        print $out, "\n";
    } else {
        print;
    }
}

Reply via email to