Blog

lis.plのトーカナイザを改善する

lis.pl を地味に改善していきます。
http://norvig.com/lispy2.html
を参考に、トーカナイザを改良します。

今回の改良で改善されたトーカナイザは DSL をつくるときによくつかわれるパターンそのものなので LISP をつくらない人でも実装方法をおぼえておくと便利でしょう。

正規表現の x オプション、つまり //x をつかうことで、難解な正規表現もよみやすくなっているところがポイントといえるでしょう。

package Lispl::EOF {
    my $__instance = bless [], Lispl::EOF::;
    sub instance { $__instance }
}

package Lispl::Scanner {
    use IO::Handle;
    use IO::File;
    use Carp;

    sub new {
        my ($class, $file) = @_;
        $file // croak "Missing mandatory parameter: file";
        bless {
            file => $file,
            line => '',
        }, $class;
    }

    sub next_token {
        my ($self) = @_;
        while (1) {
            if ($self->{line} eq '') {
                $self->{line} = $self->{file}->getline();
            }
            if (!defined $self->{line}) {
                return Lispl::EOF->instance();
            }
            (my $token, $self->{line}) = ($self->{line} =~ m{
                \s* # skip ws
                (
                        ,@
                    |   [('`,)]                # kigou
                    |   "(?:[\\.]|[^\\"])*"    # string
                    |   ;.*                    # comment
                    |   [^\s('"`,;)]*          # identifier, numeric, etc.
                )
                (.*) # rest part
            }x);
            if (length($token) > 0 && $token !~ /^;/) {
                return $token;
            }
        }
    }
}