diff --git a/build/Makefile.in b/build/Makefile.in index d7177c5..6981c9a 100644 --- a/build/Makefile.in +++ b/build/Makefile.in @@ -78,6 +78,13 @@ STAGE0_SOURCES = \ src/stage0/P6Regex-s0.pir \ src/stage0/NQP-s0.pir +SETTINGS_SOURCES = \ + src/settings/Array.pm \ + src/settings/Functions.pm \ + src/settings/Hash.pm \ + src/settings/String.pm \ + src/settings/IO.pm + STAGE0 = src/stage0 STAGE1 = src/stage1 STAGE2 = src/stage2 @@ -103,6 +110,10 @@ NQP_G = gen/nqp-grammar.pir NQP_A = gen/nqp-actions.pir NQP_EXE = nqp$(EXE) +SETTINGS_PBC = nqp-settings.pbc +SETTINGS_GEN = src/gen/settings.pm +SETTINGS_PIR = src/gen/settings.pir + STAGE0_PBCS = $(STAGE0)/$(HLL_PBC) $(STAGE0)/$(P6REGEX_PBC) $(STAGE0)/$(NQP_PBC) $(STAGE0)/$(REGEX_PBC) STAGE1_PBCS = $(STAGE1)/$(HLL_PBC) $(STAGE1)/$(P6REGEX_PBC) $(STAGE1)/$(NQP_PBC) $(REGEX_PBC) STAGE2_PBCS = $(STAGE2)/$(HLL_PBC) $(STAGE2)/$(P6REGEX_PBC) $(STAGE2)/$(NQP_PBC) $(REGEX_PBC) @@ -114,6 +125,7 @@ CLEANUPS = \ $(REGEX_PBC) \ $(HLL_PBC) \ $(P6REGEX_PBC) \ + $(SETTINGS_PBC) \ P6Regex$(EXE) \ $(P6GRAMMAR_PBC) \ P6Grammar$(EXE) \ @@ -127,8 +139,9 @@ CLEANUPS = \ src/stage2/gen/* \ src/stage2/*.pbc \ src/gen/*.pir \ + src/gen/*.pm -all: $(NQP_EXE) +all: $(NQP_EXE) $(SETTINGS_PBC) install: all $(MKPATH) $(DESTDIR)$(NQP_LANG_DIR) @@ -136,6 +149,7 @@ install: all $(CP) $(P6REGEX_PBC) $(DESTDIR)$(PARROT_LIBRARY_DIR)/$(P6REGEX_PBC) $(CP) $(REGEX_PBC) $(DESTDIR)$(PARROT_LIBRARY_DIR)/$(REGEX_PBC) $(CP) $(HLL_PBC) $(DESTDIR)$(PARROT_LIBRARY_DIR)/$(HLL_PBC) + $(CP) $(SETTINGS_PBC) $(DESTDIR)$(PARROT_LIBRARY_DIR)/$(SETTINGS_PBC) $(MKPATH) $(DESTDIR)$(PARROT_BIN_DIR) $(CP) $(NQP_EXE) $(DESTDIR)$(PARROT_BIN_DIR)/nqp$(EXE) $(CHMOD) 755 $(DESTDIR)$(PARROT_BIN_DIR)/nqp$(EXE) @@ -239,6 +253,16 @@ $(ALL_PBCS): $(REGEX_PBC) $(STAGE2_PBCS) $(NQP_EXE): $(NQP_PBC) $(PBC_TO_EXE) $(NQP_PBC) +$(SETTINGS_GEN): $(SETTINGS_SOURCES) + $(PERL) build/gen_settings.pl $(SETTINGS_SOURCES) > $(SETTINGS_GEN) + +$(SETTINGS_PIR): $(SETTINGS_GEN) $(NQP_PBC) + $(PARROT) $(NQP_PBC) --target=pir -o $(SETTINGS_PIR) $(SETTINGS_GEN) + +$(SETTINGS_PBC): $(SETTINGS_PIR) + $(PARROT) -o $(SETTINGS_PBC) $(SETTINGS_PIR) + + bootstrap-files: $(STAGE2_PBCS) $(PERL) build/gen_bootstrap.pl src/Regex.pir >src/stage0/Regex-s0.pir $(PERL) build/gen_bootstrap.pl src/HLL.pir >src/stage0/HLL-s0.pir @@ -250,7 +274,7 @@ bootstrap-files: $(STAGE2_PBCS) test: core-test -test-loud: core-test-loud p6regex-test-loud +test-loud: core-test-loud p6regex-test-loud settings-test-loud core-test: $(NQP_EXE) prove -r --exec ./$(NQP_EXE) t/nqp t/hll @@ -264,6 +288,12 @@ p6regex-test: $(P6REGEX_PBC) p6regex-test-loud: $(P6REGEX_PBC) prove -r -v --exec $(PARROT) t/p6regex +settings-test: $(SETTINGS_PBC) $(NQP_EXE) + prove -r --exec ./$(NQP_EXE) t/settings + +settings-test-loud: $(SETTINGS_PBC) $(NQP_EXE) + prove -r -v --exec ./$(NQP_EXE) t/settings + ## cleaning clean: diff --git a/build/gen_settings.pl b/build/gen_settings.pl new file mode 100644 index 0000000..562613a --- /dev/null +++ b/build/gen_settings.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl +# Copyright (C) 2008, The Perl Foundation. +# $Id$ + +use strict; +use warnings; + +my @files = @ARGV; + +print <<"END_SETTING"; + +# This file automatically generated by $0. + +END_SETTING + +my %classnames; +foreach my $file (@files) { + print "# From $file\n\n"; + open(my $fh, "<", $file) or die $!; + local $/; + my $x = <$fh>; + close $fh; + print $x; +} + +print "\n# vim: set ft=perl6 nomodifiable :\n"; diff --git a/src/Regex/Cursor-protoregex-peek.pir b/src/Regex/Cursor-protoregex-peek.pir index 4349440..846051b 100644 --- a/src/Regex/Cursor-protoregex-peek.pir +++ b/src/Regex/Cursor-protoregex-peek.pir @@ -82,7 +82,7 @@ Perform a match for protoregex C. cand_done: token_next: unless token > '' goto fail - chopn token, 1 + token = chopn token, 1 goto token_loop done: diff --git a/src/Regex/P6Regex/Actions.pm b/src/Regex/P6Regex/Actions.pm index 647ca35..8691c74 100644 --- a/src/Regex/P6Regex/Actions.pm +++ b/src/Regex/P6Regex/Actions.pm @@ -383,7 +383,7 @@ method assertion:sym($/) { $I0 = index $S0, ':sym<' add $I0, 5 $S0 = substr $S0, $I0 - chopn $S0, 1 + $S0 = chopn $S0, 1 %r = box $S0 }; $past := PAST::Regex.new( diff --git a/src/cheats/hll-compiler.pir b/src/cheats/hll-compiler.pir index e47cd32..6a35c0c 100644 --- a/src/cheats/hll-compiler.pir +++ b/src/cheats/hll-compiler.pir @@ -81,7 +81,7 @@ sigil = substr $S0, 0, 1 $I0 = index '$@%&', sigil if $I0 < 0 goto sigil_done - substr $S0, 0, 1, '' + $S0 = replace $S0, 0, 1, '' ns[0] = $S0 $S0 = ns[-1] $S0 = concat sigil, $S0 diff --git a/src/cheats/hll-grammar.pir b/src/cheats/hll-grammar.pir index 0e49c43..c616b03 100644 --- a/src/cheats/hll-grammar.pir +++ b/src/cheats/hll-grammar.pir @@ -337,7 +337,7 @@ position C. .end -.sub 'quotemod_check' :method +.sub 'quotemod_check' :method :nsentry .param string mod $P0 = find_dynamic_lex '%*QUOTEMOD' diff --git a/src/settings/Array.pm b/src/settings/Array.pm new file mode 100644 index 0000000..3ab1c0c --- /dev/null +++ b/src/settings/Array.pm @@ -0,0 +1,60 @@ +=begin + +=head2 Array Methods + +These methods extend the native NQP Array class to support more of the basic +functionality expected for Perl 6 Hashes. + +=end + +module ResizablePMCArray { + + +=begin + +=over 4 + +=item @reversed := @array.reverse + +Return a C<@reversed> copy of the C<@array>. + +=end + + method reverse () { + my @reversed; + for self { @reversed.unshift($_); } + @reversed; + } + +=begin + +=item $string := @array.join($join_string) + +Join C<@array> using C<$join_string> + +=end + + method join ($join_string) { + return Q:PIR{ + $P0 = find_lex '$join_string' + $S0 = $P0 + $S1 = join $S0, self + %r = box $S1 + } + } + +=begin + +=back + +=end + +} + +sub join($join_string, *@list) { @list.join($join_string) } + +sub list(*@list) { @list }; + + + +# vim: ft=perl6 diff --git a/src/settings/Functions.pm b/src/settings/Functions.pm new file mode 100644 index 0000000..1bb6836 --- /dev/null +++ b/src/settings/Functions.pm @@ -0,0 +1,111 @@ +#! nqp + +=begin + +=head2 Basic Functions + +These functions provide basic functionality that would be part of the standard +setting in Perl 6, but are not provided with NQP by default. + +=over 4 + +=item @mapped := map(&code, @originals) + +Pretty much as you would expect, except there is no flattening or other +coersion, due to the current semantics of NQP. This means that every +application of C<&code> to an item in the C<@originals> produces exactly +one entry in the C<@mapped> output. + +=end + +sub map (&code, @originals) { + my @mapped; + + for @originals { + @mapped.push(&code($_)); + } + + return @mapped; +} + + +=begin + +=item @matches := grep(&code, @all) + +Select all members of C<@all> for which C<&code($member)> returns true. +Order is retained, and duplicates are handled independently. + +=end + +sub grep (&code, @all) { + my @matches; + + for @all { + @matches.push($_) if &code($_); + } + + return @matches; +} + + +=begin + +=item $result := reduce(&code, @array, $initial?) + +Loop over the C<@array>, applying the binary function C<&code> to the current +C<$result> and next element of the C<@array>, each time saving the return +value of the C<&code> as the new C<$result>. When all elements of the array +have been processed, the last C<$result> computed is returned. + +If an C<$initial> value is supplied, it is used as the starting value for +C<$result> when iterating over the C<@array>. This automatically works with +any length C<@array>, even an empty one. + +Without an C<$initial> value, C applies the C<&code> to the first two +elements in the C<@array> to determine the inital C<$result> (and skips these +first two elements when looping). If the C<@array> has only one element, it +is returned directly as the final C<$result>. If the C<@array> is empty, the +C<$result> is an undefined value. + +=end + +sub reduce (&code, @array, *@initial) { + my $init_elems := pir::elements(@initial); + if $init_elems > 1 { + pir::die('Only one initial value allowed in reduce()'); + } + elsif $init_elems == 1 { + return _reduce(&code, @array, @initial[0]); + } + else { + my $array_elems := pir::elements(@array); + if $array_elems == 0 { + return my $undef; + } + elsif $array_elems == 1 { + return @array[0]; + } + else { + my $initial := &code(@array[0], @array[1]); + my $iter := pir::iter__PP(@array); + + pir::shift($iter); + pir::shift($iter); + + return _reduce(&code, $iter, $initial); + } + } +} + +sub _reduce(&code, $iter, $initial) { + my $result := $initial; + + for $iter { + $result := &code($result, $_); + } + + return $result; +} + +# vim: ft=perl6 diff --git a/src/settings/Hash.pm b/src/settings/Hash.pm new file mode 100644 index 0000000..d642d97 --- /dev/null +++ b/src/settings/Hash.pm @@ -0,0 +1,113 @@ +=begin + +=head2 Hash Methods + +These methods extend the native NQP Hash class to support more of the basic +functionality expected for Perl 6 Hashes. + +=end + +module Hash { + + +=begin + +=over 4 + +=item $found := %hash.exists($key) + +Return a true value if C<$key> exists in C<%hash>, or a false value otherwise. + +=end + + method exists ($key) { + return Q:PIR{ + $P1 = find_lex '$key' + $I0 = exists self[$P1] + %r = box $I0 + }; + } + +=begin + +=item %hash.delete($key) + +Delete C<$key> from C<%hash>. + +=end + + method delete ($key) { + Q:PIR{ + $P1 = find_lex '$key' + delete self[$P1] + }; + } + + +=begin + +=item @keys := %hash.keys + +Return all the C<@keys> in the C<%hash> as an unordered array. + +=end + + method keys () { + my @keys; + for self { @keys.push($_.key); } + @keys; + } + + +=begin + +=item @values := %hash.values + +Return all the C<@values> in the C<%hash> as an unordered array. + +=end + + method values () { + my @values; + for self { @values.push($_.value); } + @values; + } + + +=begin + +=item @flattened := %hash.kv + +Flatten C<%hash> into an array, alternating key and value. This is useful +when iterating over key and value simultaneously: + + for %hash.kv -> $k, $v { ... } + +=end + + method kv () { + my @kv; + for self { @kv.push($_.key); @kv.push($_.value); } + @kv; + } + + +=begin + +=back + +=end + +} + +=begin + +=item %hash := hash(:key1(value1), :key2(value2), ...) + +Coerce a list of pairs into a hash. + +=end + +sub hash (*%h) { return %h } + +# vim: ft=perl6 diff --git a/src/settings/IO.pm b/src/settings/IO.pm new file mode 100644 index 0000000..d1efee4 --- /dev/null +++ b/src/settings/IO.pm @@ -0,0 +1,73 @@ +=begin + +=head1 NAME + +IO.nqp - IO related functions for NQP. + +=head1 SYNOPSIS + + # I/O + print('things', ' to ', 'print', ...); + say( 'things', ' to ', 'say', ...); + $contents := slurp($filename); + spew( $filename, $contents); + append($filename, $contents); + +=head1 I/O Functions + +Basic stdio and file I/O functions. + +=over 4 + +=item $contents := slurp($filename) + +Read the C<$contents> of a file as a single string. + +=end + +sub slurp ($filename) { + my $fh := pir::open__Pss($filename, 'r'); + my $contents := $fh.readall; + pir::close($fh); + + return $contents; +} + + +=begin + +=item spew($filename, $contents) + +Write the string C<$contents> to a file. + +=end + +sub spew ($filename, $contents) { + my $fh := pir::open__Pss($filename, 'w'); + $fh.print($contents); + pir::close($fh); +} + + +=begin + +=item append($filename, $contents) + +Append the string C<$contents> to a file. + +=end + +sub append ($filename, $contents) { + my $fh := pir::open__Pss($filename, 'a'); + $fh.print($contents); + pir::close($fh); +} + + +=begin + +=back + +=end + +# vim: ft=perl6 diff --git a/src/settings/String.pm b/src/settings/String.pm new file mode 100644 index 0000000..97dd9ec --- /dev/null +++ b/src/settings/String.pm @@ -0,0 +1,72 @@ + + +=begin + +These functions add more power to the basic regex matching capability, +including doing global matches and global substitutions. + +=over 4 + +=item @matches := all_matches($regex, $text) + +=end + +sub all_matches($regex, $text) { + my @matches; + + my $match := $text ~~ $regex; + while $match { + @matches.push($match); + $match := $match.CURSOR.parse($text, :rule($regex), :c($match.to)); + } + + return @matches; +} + +=begin + +=item $edited := subst($original, $regex, $replacement) + +Substitute all matches of the C<$regex> in the C<$original> string with the +C<$replacement>, and return the edited string. The C<$regex> must be a regex +object as returned by C. + +The C<$replacement> may be either a simple string or a sub that will be called +with each match object in turn, and must return the proper replacement string +for that match. + +=end + +sub subst($original, $regex, $replacement) { + my @matches := all_matches($regex, $original); + my $edited := pir::clone($original); + my $is_sub := pir::isa($replacement, 'Sub'); + my $offset := 0; + + for @matches -> $match { + my $replace_string := $is_sub ?? $replacement($match) !! $replacement; + my $replace_len := pir::length($replace_string); + my $match_len := $match.to - $match.from; + my $real_from := $match.from + $offset; + + Q:PIR{ + $P0 = find_lex '$edited' + $S0 = $P0 + $P1 = find_lex '$real_from' + $I0 = $P1 + $P2 = find_lex '$match_len' + $I1 = $P2 + $P3 = find_lex '$replace_string' + $S1 = $P3 + substr $S0, $I0, $I1, $S1 + $P0 = $S0 + }; + + $offset := $offset - $match_len + $replace_len; + } + + return $edited; +} + + +# vim: ft=perl6 diff --git a/src/stage0/HLL-s0.pir b/src/stage0/HLL-s0.pir index 0794fb9..d0400e9 100644 --- a/src/stage0/HLL-s0.pir +++ b/src/stage0/HLL-s0.pir @@ -99,7 +99,7 @@ and HLL::Grammar. sigil = substr $S0, 0, 1 $I0 = index '$@%&', sigil if $I0 < 0 goto sigil_done - substr $S0, 0, 1, '' + $S0 = replace $S0, 0, 1, '' ns[0] = $S0 $S0 = ns[-1] $S0 = concat sigil, $S0 diff --git a/src/stage0/P6Regex-s0.pir b/src/stage0/P6Regex-s0.pir index c381a1d..1c88b20 100644 --- a/src/stage0/P6Regex-s0.pir +++ b/src/stage0/P6Regex-s0.pir @@ -11285,7 +11285,7 @@ Regex::P6Regex - Parser/compiler for Perl 6 regexes $I0 = index $S0, ':sym<' add $I0, 5 $S0 = substr $S0, $I0 - chopn $S0, 1 + $S0 = chopn $S0, 1 $P1353 = box $S0 store_lex "$regexsym", $P1353 diff --git a/src/stage0/Regex-s0.pir b/src/stage0/Regex-s0.pir index a12aa06..fffa6f8 100644 --- a/src/stage0/Regex-s0.pir +++ b/src/stage0/Regex-s0.pir @@ -1036,7 +1036,7 @@ Perform a match for protoregex C. cand_done: token_next: unless token > '' goto fail - chopn token, 1 + token = chopn token, 1 goto token_loop done: @@ -1893,7 +1893,7 @@ An alternate dump output for a Match object and all of its subcaptures. # vim: expandtab shiftwidth=4 ft=pir: ### .include 'src/PAST/Regex.pir' -# $Id: Regex.pir 41578 2009-09-30 14:45:23Z pmichaud $ +# $Id$ =head1 NAME diff --git a/t/p6regex/01-regex.t b/t/p6regex/01-regex.t index 6bb1009..07b5499 100644 --- a/t/p6regex/01-regex.t +++ b/t/p6regex/01-regex.t @@ -237,8 +237,8 @@ Description of the test. # remove /'s $S0 = substr result, 0, 1 if $S0 != "/" goto bad_line - substr result, 0, 1, '' - substr result, -1, 1, '' + replace result, result, 0, 1, '' + replace result, result, -1, 1, '' $I0 = index $S1, result if $I0 == -1 goto is_nok @@ -280,8 +280,8 @@ Description of the test. # remove /'s $S0 = substr result, 0, 1 if $S0 != "/" goto bad_error - substr result, 0, 1, '' - substr result, -1, 1, '' + replace result, result, 0, 1, '' + replace result, result, -1, 1, '' $I0 = index message, result if $I0 == -1 goto bad_error ok = 1 @@ -313,7 +313,7 @@ Description of the test. # NOTE: there can be multiple tabs between entries, so skip until # we have something. # remove the trailing newline from record - chopn test_line, 1 + chopn test_line, test_line, 1 $P1 = split "\t", test_line $I0 = elements $P1 # length of array @@ -442,27 +442,27 @@ bad_digit: target1: $I0 = index target, '\n' if $I0 == -1 goto target2 - substr target, $I0, 2, "\n" + replace target, target, $I0, 2, "\n" goto target1 target2: $I0 = index target, '\r' if $I0 == -1 goto target3 - substr target, $I0, 2, "\r" + replace target, target, $I0, 2, "\r" goto target2 target3: $I0 = index target, '\e' if $I0 == -1 goto target4 - substr target, $I0, 2, "\e" + replace target, target, $I0, 2, "\e" goto target3 target4: $I0 = index target, '\t' if $I0 == -1 goto target5 - substr target, $I0, 2, "\t" + replace target, target, $I0, 2, "\t" goto target4 target5: $I0 = index target, '\f' if $I0 == -1 goto target6 - substr target, $I0, 2, "\f" + replace target, target, $I0, 2, "\f" goto target5 target6: # handle \xHH, hex escape. @@ -475,7 +475,7 @@ bad_digit: ($S0, $I2) = $P0(target, 'x', $I1) $S3 = substr target, $I1, $I2 $I2 += 2 - substr target, $I0, $I2, $S0 + replace target, target, $I0, $I2, $S0 goto target6 target7: .return (target) diff --git a/t/settings/01-array.t b/t/settings/01-array.t new file mode 100644 index 0000000..2103402 --- /dev/null +++ b/t/settings/01-array.t @@ -0,0 +1,19 @@ +#! nqp + +pir::load_bytecode('nqp-settings.pbc'); + +my @array := <0 1 2>; +my @reversed := @array.reverse(); + +plan(4); + +ok( @reversed[0] == 2, 'First element correct'); +ok( @reversed[1] == 1, 'Second element correct'); +ok( @reversed[2] == 0, 'Third element correct'); + +my $join := @array.join('|'); +ok( $join == '0|1|2', 'Join elements'); + +ok( join(':', 'foo', 'bar', 'baz') == 'foo:bar:baz', 'Join as standalone function'); + +# vim: ft=perl6 diff --git a/t/settings/02-hash.t b/t/settings/02-hash.t new file mode 100644 index 0000000..aa356d6 --- /dev/null +++ b/t/settings/02-hash.t @@ -0,0 +1,40 @@ +#! nqp + +pir::load_bytecode('nqp-settings.pbc' ); + +my %hash := hash( foo => 1, bar => 2, baz => 42 ); + +plan(17 ); + +ok( %hash.exists('foo'), 'Key exists'); +ok( !(%hash.exists('bang')), "Key doesn't exists"); + +my @keys := %hash.keys; +ok(+@keys == 3, "Got 3 keys total" ); + +my @sorted := ; +for @keys.sort -> $key { + my $expected := @sorted.shift; + ok( $expected == $key, "Key is correct" ); +} + +my %expected := hash( foo => 1, bar => 2, baz => 42 ); +my %values; + +for %hash.kv -> $k, $v { + ok( %expected.exists($k), "Key exists" ); + ok( %expected{$k} == $v, "Value correct" ); + %expected.delete($k); + %values{$v} := 1 +} + +ok( +%expected.keys == 0, "All keys processed" ); + +for %hash.values -> $v { + ok( %values.exists($v), "Value correct" ); + %values.delete($v); +} + +ok( +%values.keys == 0, "All values processed" ); + +# vim: ft=perl6