前に Scheme の字句 をクソ楽に区切りたいなーとか思って,一行で済ませてみようとしたのでしたが,結果としてうまくいきませんでした.
my @tokens = map { split /(?<=\()|(?=\))/, $_ } split /\s+/, <<SOURCE;
最初に空白を区切り文字として split しているもんだから," abc sdd" みたいな空白を含むクオート文字列がムダに分割されてしまう.
あーもうムカつくなー,他の人はどうやってんのかなーと,Text::CSV の PurePerl 実装の方を見てみたところ,正規表現使って区切ってるっぽい.
そういうわけですから,こんな感じで区切ったりするなどしました.
- 括弧( '(' or ')' )が来たら,それは一文字で区切る
- クオートが来たらその次のクオートまでで区切る
- その他の場合は,括弧かクオートまでで区切る
とても単純ですね.これはあんましぼくが考えてないからで,クオート文字がエスケープとかされたら死ぬでしょう.
sub parse { my ($self) = @_; my $source = $self->{source}; my @result; while( $source ) { $source =~ s/^\s+//; if ( $source =~ /( ^[()] | ^(["']).*?\2 | ^[^"'()\s]+ )/x ) { push @result, $1; my $len = length $1; $source =~ s/^.{$len}//; } } \@result; }
こんな感じで使う.
use strict; use warnings; use Test::More qw(no_plan); BEGIN { use_ok( 'Language::Scheme::Lexer' ); } my $lexer = Language::Scheme::Lexer->new; isa_ok( $lexer, 'Language::Scheme::Lexer' ); can_ok( $lexer, qw(load parse) ); my $source = do { local $/; <DATA> }; my $a = $lexer->load( $source )->parse(); ok( eq_array( [ '(', 'define', '(', 'list->set-name', 'size', 'lst', ')', '(', 'cond', '(', '(', 'null?', 'lst', ')', '"e"', ')', '(', '(', '=', 'size', '(', 'list-length', 'lst', ')', ')', '"f"', ')', '(', '#t', '(', 'apply', 'join-fields-with', '" "', '(', 'map', '->string', 'lst', ')', ')', ')', ')', ')' ], $a ), 'lexical analyse check' ); __DATA__ (define (list->set-name size lst) (cond ((null? lst) "e") ((= size (list-length lst)) "f") (#t (apply join-fields-with " " (map ->string lst)))))