All pastes #1338051 Raw Edit

udld.pl

public text v1 · immutable
#1338051 ·published 2009-02-15 16:09 UTC
rendered paste body
##### Underlambda reference interpreter.
##### Implements tier 1

#### Options
use strict;
$|=1; # output immediately, as is usual for esolangs

#### Subroutines and data.

### Grammar for Underlambda, which is extremely simple:
my ($udldcmd, $udldgroup);
# A command is a group, or any character but a paren.
$udldcmd=qr/[^()]|(??{$udldgroup})/s;
# A group is a list of commands inside parens.
$udldgroup=qr/\((??{$udldcmd})*\)/s;

### Reporting warnings and errors.
## udldwarn: Show a warning.
sub udldwarn ($)
{
    my $msg = shift;
    print STDERR "Warning: $msg\n";
}
## udlderror: Error out.
sub udlderror ($)
{
    my $msg = shift;
    print STDERR "Error: $msg\n";
    exit 1;
}

### Helper functions
## charcount: Number of occurrences of the second arg in the first.
sub charcount ($$)
{
    my $string = shift;
    my $char = shift;
    scalar(split $char,$string);
}
## errifundef: Returns its argument, or errors using the second argument
## as the error message if its argument happens to be undef.
sub errifundef ($$)
{
    my $arg = shift;
    defined($arg) or udlderror shift;
    $arg;
}

### Input and output, Underlambda style
## udldgetc: Input a character.
sub udldgetc ()
{
    my $char=getc(STDIN);
    defined($char) ? ord($char)+1 : 0;
}
## udldputc: Output a character.
sub udldputc ($)
{
    my $char=shift;
    $char == 0 and udlderror "Attempt to output an EOF";
    print chr($char-1);
}
## udldgetserial: Input a serialisation.
sub udldgetserial ()
{
    local $_='';
    local $/=')';
    # Anything's a legitimate serialisation as long as it has balanced
    # parentheses and doesn't contain the characters {}#|
    $_ .= (errifundef <>, "D command couldn't deserialise: EOF on input")
	while (charcount $_, "(") > (charcount $_, ")");
    (charcount $_, "(") == (charcount $_, ")")
	or udlderror "D command couldn't deserialise: unbalanced ()";
    m/[{}#|]/
	and udlderror "D command couldn't deserialise: invalid character";
    $_;
}

#### The main program.

### Read in the program.
my $magic=<>; # the first line
$_="";
if($magic =~ /\n/)
{
    local $/=undef;
    $_=<>; # the rest
}
if($magic !~ m%^#!/usr/bin/underlambda%)
{
    udldwarn 'Magic line "#!/usr/bin/underlambda" not found.';
    $_=$magic.$_;
}
else
{
    # Parse command line options: //tk
}

## Check the input validity.
s/\s*$//s; # remove trailing whitespace
s/\|$// or udldwarn 'No | found at end of program.';
m/\|/ and udlderror '| found before end of program.';
m/#/ and udlderror '# found after start of program.';
m/\{/ and udlderror '{ found within program.';
m/\}/ and udlderror '} found within program.';
/^(??{$udldcmd})*$/ or udlderror '() not matched in program.';

### Storage needed for the program.
my $iocharcode = undef;

### Execute the program. The combined code/data model is used;
### that is, the stack is stored in the program itself, which is
### less efficient but makes serialising easy, and can optimise
### inert code. Note that this method means that the () command
### doesn't need to be executed.
{
    ## Tier 1: Basic TCness
    # Out-of-sequence-safe commands
    s/(??{$udldgroup})!//                                     and redo; # !
    s/\(((??{$udldcmd})*)\)\^/$1/                             and redo; # ^
    s/\(((??{$udldcmd})*)\)\(((??{$udldcmd})*)\)\*/($1$2)/    and redo; # *
    s/((??{$udldgroup}))((??{$udldgroup}))~/$2$1/             and redo; # ~
    s/((??{$udldgroup}))a/($1)/                               and redo; # a
    # Commands that must be run in sequence
    s/^((??{$udldgroup})*)((??{$udldgroup})):/$1$2$2/         and redo; # :
    ## Tier 1a: Basic character I/O
    # All of these must be run in sequence
    s/^((??{$udldgroup})*)o/($iocharcode = 0),$1/e            and redo; # o
    s/^((??{$udldgroup})*)i/($iocharcode = udldgetc),$1/e     and redo; # i
    s/^((??{$udldgroup})*)h/($iocharcode++),$1/e              and redo; # h
    s/^((??{$udldgroup})*)O/(udldputc $iocharcode),$1/e       and redo; # O
    s/^((??{$udldgroup})*)((??{$udldgroup}))I/
                    $iocharcode?"$1$2":"$1()"/e               and redo; # I
    s/^((??{$udldgroup})*)H/($iocharcode-- < 0 and
        udlderror "IO character has gone negative"),$1/e      and redo; # H
    ## Tier 2: Manipulating the program itself
    # All of these must be run in sequence
    m/^((??{$udldgroup})*)Q/ and exit 0;                                # Q
    s/^((??{$udldgroup})*)C(.*)$/$1($1($2))$2/                and redo; # C
    ## Tier 2a: Serialisation
    s/^((??{$udldgroup})*)((??{$udldgroup}))S/(print $2),$1/e and redo; # S
    s/^((??{$udldgroup})*)D/$1.udldgetserial/e                and redo; # D
}