Author: particle
Date: Fri Nov 4 13:17:33 2005
New Revision: 9784
Added:
trunk/runtime/parrot/library/File/
trunk/runtime/parrot/library/File/Spec/
trunk/runtime/parrot/library/File/Spec.pir
trunk/runtime/parrot/library/File/Spec/Unix.pir
trunk/runtime/parrot/library/File/Spec/Win32.pir
trunk/t/library/File-Spec.t
Modified:
trunk/MANIFEST
Log:
beginnings of a File::Spec implementation in PIR.
currently working on win32 only. more to come.
Modified: trunk/MANIFEST
==============================================================================
--- trunk/MANIFEST (original)
+++ trunk/MANIFEST Fri Nov 4 13:17:33 2005
@@ -1648,6 +1648,9 @@ runtime/parrot/library/Data/Dumper.imc
runtime/parrot/library/Data/Dumper/Base.imc [library]
runtime/parrot/library/Data/Dumper/Default.imc [library]
runtime/parrot/library/Digest/MD5.pir [library]
+runtime/parrot/library/File/Spec.pir [library]
+runtime/parrot/library/File/Spec/Win32.pir [library]
+runtime/parrot/library/File/Spec/Unix.pir [library]
runtime/parrot/library/PGE/Dumper.pir [library]
runtime/parrot/library/PGE/Glob.pir [library]
runtime/parrot/library/PGE/Hs.pir [library]
@@ -1946,6 +1949,7 @@ t/pmc/tqueue.t
t/pmc/undef.t []
t/library/data_escape.t []
t/library/dumper.t []
+t/library/File-Spec.t []
t/library/json.t []
t/library/getopt_long.t []
t/library/md5.t []
Added: trunk/runtime/parrot/library/File/Spec.pir
==============================================================================
--- (empty file)
+++ trunk/runtime/parrot/library/File/Spec.pir Fri Nov 4 13:17:33 2005
@@ -0,0 +1,175 @@
+=head1 TITLE
+
+File::Spec - portably perform operations on file names
+
+=head1 SYNOPSIS
+
+ .local int classtype
+ .local pmc spec
+
+ load_bytecode 'File/Spec.pir'
+
+ find_type classtype, 'File::Spec'
+ new spec, classtype
+
+ .local pmc x
+ .local String a, b, c
+ x= spec.'catdir'( 'a', 'b', 'c' )
+
+which returns 'a/b/c' under Unix.
+
+=head1 DESCRIPTION
+
+This module is designed to support operations commonly performed on file
+specifications (usually called "file names", but not to be confused with the
+contents of a file, or Perl's file handles), such as concatenating several
+directory and file names into a single path, or determining whether a path
+is rooted. It is based on code directly taken from MakeMaker 5.17, code
+written by Andreas KE<ouml>nig, Andy Dougherty, Charles Bailey, Ilya
+Zakharevich, Paul Schinder, and others.
+
+Since these functions are different for most operating systems, each set of
+OS specific routines is available in a separate module, including:
+
+ *File::Spec::Unix
+ *File::Spec::Mac
+ *File::Spec::OS2
+ File::Spec::Win32
+ *File::Spec::VMS
+
+*These modules have not yet been created in this release.
+
+The module appropriate for the current OS is automatically loaded by
+File::Spec. Since some modules (like VMS) make use of facilities available
+only under that OS, it may not be possible to load all modules under all
+operating systems.
+
+Since File::Spec is object oriented, subroutines should not be called directly,
+and since parrot does not yet support class methods, subroutines should be
+called as object methods.
+
+=cut
+
+
+.namespace [ 'File::Spec' ]
+
+
+.sub VERSION method
+ .local string version
+ version= '0.1'
+ .return( version )
+.end
+
+
+.sub '__onload' :load
+ '_init'()
+
+ .local string osname
+ osname= '_get_osname'()
+
+ .local string platform
+ platform= '_get_module'( osname )
+
+ '_load_lib'( platform )
+
+ .local string baseclass
+ concat baseclass, 'File::Spec::', platform
+
+ ## make this class a subclass of the base class
+ .local pmc self
+ subclass self, baseclass, 'File::Spec'
+
+ .return()
+.end
+
+
+.sub '_init'
+ .local pmc modules
+ modules= new .Hash
+
+ ## TODO implement the other platforms
+ set modules['MSWin32'], 'Win32'
+ set modules['NetWare'], 'Win32'
+ ## set modules['MacOS'], 'Mac'
+ ## set modules['os2'], 'OS2'
+ ## set modules['VMS'], 'VMS'
+ ## set modules['epoc'], 'Epoc'
+ ## set modules['dos'], 'OS2'
+ ## set modules['cygwin'], 'Cygwin'
+
+ store_global '_modules', modules
+.end
+
+
+.sub '_get_osname'
+ .local pmc config
+ .local pmc osname
+
+ config= '_config'()
+ osname= config['osname']
+
+ .return( osname )
+.end
+
+
+.sub '_get_module'
+ .param string osname
+
+ .local pmc modules
+ modules= new .Hash
+
+ modules= find_global '_modules'
+
+ .local string module
+ module= modules[ osname ]
+
+ ## FIXME: when undef doesn't return 'None' in scalar context
+ ne 'None', module, found_module
+ module= 'Unix'
+
+found_module:
+ .return( module )
+.end
+
+
+.sub '_load_lib'
+ .param string module
+
+ .local string filename
+ .local string libname
+
+ concat filename, module, ".pir"
+ concat libname, "File/Spec/", filename
+ load_bytecode libname
+
+ .return()
+.end
+
+
+.sub '__isa' method
+ .local pmc sub
+ .local string name
+
+ classname name, self
+
+ .local string osname
+ osname= '_get_osname'()
+
+ .local string module
+ module= '_get_module'( osname )
+
+ .local pmc platform
+ platform= new String
+ platform= module
+
+ .local pmc class
+ class= new String
+ class= name
+
+ concat class, class, '::'
+ concat class, class, platform
+ .return(class)
+.end
+
+
+.include 'library/config.imc'
Added: trunk/runtime/parrot/library/File/Spec/Unix.pir
==============================================================================
--- (empty file)
+++ trunk/runtime/parrot/library/File/Spec/Unix.pir Fri Nov 4 13:17:33 2005
@@ -0,0 +1,108 @@
+.namespace [ 'File::Spec::Unix' ]
+
+.sub '__onload' :load
+ .local pmc self
+ newclass self, 'File::Spec::Unix'
+.end
+
+.sub 'curdir' method
+ .return( '.' )
+.end
+
+
+.sub 'devnull' method
+ .return( '/dev/null' )
+.end
+
+
+.sub 'rootdir' method
+ .return( '/' )
+.end
+
+
+.sub 'tmpdir' method
+ '_E_not_yet_implemented'()
+.end
+
+
+.sub 'updir' method
+ .return( '..' )
+.end
+
+
+.sub 'no_upwards' method
+ '_E_not_yet_implemented'()
+.end
+
+
+.sub 'case_tolerant' method
+ .return( 1 )
+.end
+
+
+.sub 'file_name_is_absolute' method
+ .return( 0 )
+.end
+
+
+.sub 'VERSION' method
+ .local pmc version
+ version= global 'VERSION'
+ .return( version )
+.end
+
+
+.sub 'catfile' method
+ '_E_not_yet_implemented'()
+.end
+
+
+.sub 'catdir' method
+ '_E_not_yet_implemented'()
+.end
+
+
+.sub 'path' method
+ '_E_not_yet_implemented'()
+.end
+
+
+.sub 'canonpath' method
+ '_E_not_yet_implemented'()
+.end
+
+
+.sub 'splitpath' method
+ '_E_not_yet_implemented'()
+.end
+
+
+.sub 'splitdir' method
+ '_E_not_yet_implemented'()
+.end
+
+
+.sub 'catpath' method
+ '_E_not_yet_implemented'()
+.end
+
+
+.sub 'abs2rel' method
+ '_E_not_yet_implemented'()
+.end
+
+
+.sub 'rel2abs' method
+ '_E_not_yet_implemented'()
+.end
+
+
+## TODO more functions
+
+
+## throw an error that the sub isn't yet implemented
+.sub '_E_not_yet_implemented'
+ new $P0, .Exception
+ $P0['_message']= 'not yet implemented'
+ throw $P0
+.end
Added: trunk/runtime/parrot/library/File/Spec/Win32.pir
==============================================================================
--- (empty file)
+++ trunk/runtime/parrot/library/File/Spec/Win32.pir Fri Nov 4 13:17:33 2005
@@ -0,0 +1,624 @@
+.namespace [ 'File::Spec::Win32' ]
+
+=head2 Description
+
+=cut
+
+.sub '__onload' :load
+ load_bytecode 'File/Spec/Unix.pir'
+ load_bytecode 'PGE.pbc'
+
+ .local pmc self
+ subclass self, 'File::Spec::Unix', 'File::Spec::Win32'
+
+ .local pmc tmpdir
+ tmpdir= new .ResizableStringArray
+
+ store_global '_tmpdir', tmpdir
+.end
+
+
+.sub 'devnull' method
+ .return( 'nul' )
+.end
+
+
+.sub 'tmpdir' method
+ .local pmc tmpdir
+ tmpdir= new .ResizableStringArray
+
+ tmpdir= global '_tmpdir'
+
+ .local int size
+ size= tmpdir
+
+ if size, return_cached
+
+ .local pmc env
+ env= new .Env
+
+ .local string env_val
+
+find_tmpdir:
+ env_val= env['TMPDIR']
+ if env_val, found_tmpdir
+ goto find_temp
+
+found_tmpdir:
+ push tmpdir, env_val
+ env_val= ''
+
+find_temp:
+ env_val= env['TEMP']
+ if env_val, found_temp
+ goto find_tmp
+
+found_temp:
+ push tmpdir, env_val
+ env_val= ''
+
+find_tmp:
+ env_val= env['TMP']
+ if env_val, found_tmp
+ goto add_more
+
+found_tmp:
+ push tmpdir, env_val
+ env_val= ''
+
+add_more:
+ push tmpdir, 'SYS:/temp'
+ push tmpdir, 'C:/temp'
+ push tmpdir, '/tmp'
+ push tmpdir, '/'
+
+store:
+ store_global '_tmpdir', tmpdir
+return_cached:
+ .return( tmpdir )
+.end
+
+
+.sub case_tolerant method
+ .return( 1 )
+.end
+
+
+.sub 'file_name_is_absolute' method
+ .param string file
+
+ .local pmc p6rule
+ .local pmc rulesub
+ .local pmc match
+
+ p6rule= find_global 'PGE', 'p6rule'
+
+ ## m{^([a-z]:)?[\\/]}is
+ rulesub= p6rule( ':ignorecase ^ ( <?alpha> \: )? <[\\/]>' )
+ match= rulesub( file )
+
+ .return match.'__get_bool'()
+.end
+
+
+.sub 'catfile' method
+ _E_not_yet_implemented()
+
+=for later development
+
+ .param pmc args :slurpy
+ .local pmc dir
+ .local pmc file
+
+ $I0= args
+ unless $I0, return
+
+ $P1= pop args
+ file= new .String
+ file= $P1
+
+ file= self.'canonpath'( file )
+
+ $I0= args
+ unless $I0, return_file
+
+ dir= self.'catdir'( args :flat )
+
+ .local pmc p6rule
+ .local pmc rulesub
+ .local pmc match
+
+ p6rule= find_global 'PGE', 'p6rule'
+
+ ## dir =~ m{\\$}is
+ rulesub= p6rule( '\\ $' )
+ match= rulesub( dir )
+
+ if match, return_dir_file
+ dir .= "\\"
+
+return_dir_file:
+ dir .= file
+ .return( dir )
+return_file:
+ .return( file )
+return:
+
+=cut
+
+.end
+
+
+.sub 'catdir' method
+ .param pmc dirs :slurpy
+
+ .local int num_args
+ num_args= dirs
+
+ unless num_args, return
+
+ .local pmc p6rule, rulesub, match
+ p6rule= find_global 'PGE', 'p6rule'
+
+ .local pmc dir
+ dir= new .String
+ .local int i
+ i= 0
+
+loop_dirs:
+ if num_args <= i goto join_dirs
+ dir= dirs[i]
+
+ .local string s_dir, slash, bslash
+ s_dir= dir
+ slash= '/'
+ bslash= '\'
+
+loop_rule:
+ $I0= index s_dir, slash
+ if -1 == $I0 goto final_slash
+ $I1= $I0 + 1
+ $S2= substr s_dir, $I1
+ substr s_dir, $I0, $I1, bslash
+ concat s_dir, $S2
+ goto loop_rule
+
+final_slash:
+ ## $_ .= "\\" unless m{\\$};
+ $S0= substr s_dir, -1
+ eq bslash, $S0, next_arg
+ concat s_dir, bslash
+
+ dir= s_dir
+ dirs[i]= dir
+
+next_arg:
+ i += 1
+ goto loop_dirs
+
+join_dirs:
+ ## return $self->canonpath(join('', @args));
+ s_dir= join '', dirs
+ dir= s_dir
+ dir= self.'canonpath'( dir )
+ .return( dir )
+return:
+.end
+
+
+.sub 'path' method
+##TODO
+ '_E_not_yet_implemented'()
+.end
+
+
+.sub 'canonpath' method
+ .param string path
+
+ .local pmc p6rule, rulesub, match
+ p6rule= find_global 'PGE', 'p6rule'
+
+match_1:
+ ## $path =~ s/^([a-z]:)/\u$1/s;
+ rulesub= p6rule( '^ ( <?[a..z]> \: )' )
+do_match_1:
+ match= rulesub( path )
+ unless match, no_match_1
+ $P0= match[0]
+ $I0= $P0.from()
+ $I1= $P0.to()
+ $S0= $P0
+ $S0= upcase $S0
+ substr path, $I0, $I1, $S0
+no_match_1:
+
+print "-1 "
+print path
+print "\n"
+
+match_2:
+ ## $path =~ s|/|\\|g;
+ rulesub= p6rule( "/" )
+do_match_2:
+ match= rulesub( path )
+ unless match, no_match_2
+ $P0= match[0]
+ $I0= $P0.from()
+ $I1= $P0.to()
+ $S0= "\\"
+ substr path, $I0, $I1, $S0
+ goto do_match_2
+no_match_2:
+
+print "-2 "
+print path
+print "\n"
+
+match_3:
+ ## $path =~ s|([^\\])\\+|$1\\|g;
+ ## xx\\\\xx -> xx\xx
+ rulesub= p6rule( '( <-[\\]> ) \+' )
+do_match_3:
+ match= rulesub( path )
+ unless match, no_match_3
+ $P0= match[0]
+ $I0= $P0.from()
+ $I1= $P0.to()
+ $S0= $P0
+ $S0 .= "\\"
+ substr path, $I0, $I1, $S0
+ goto do_match_3
+no_match_3:
+
+print "-3 "
+print path
+print "\n"
+
+match_4:
+ ## $path =~ s|(\\\.)+\\|\\|g;
+ ## xx\.\.\xx -> xx\xx
+ rulesub= p6rule( '( \\ \. )+ \\' )
+do_match_4:
+ match= rulesub( path )
+ unless match, no_match_4
+ $P0= match[0]
+ $I0= $P0.from()
+ $I1= $P0.to()
+ $S0= "\\"
+ substr path, $I0, $I1, $S0
+ goto do_match_4
+no_match_4:
+
+print "-4 "
+print path
+print "\n"
+
+match_5:
+ ## $path =~ s|^(\.\\)+||s unless $path eq ".\\";
+ ## .\xx -> xx
+ .local string relpath
+ relpath= ".\\"
+
+ iseq $I0, relpath, path
+ if $I0, no_match_5
+ rulesub= p6rule( '^ ( \. \\ )+ \\' )
+do_match_5:
+ match= rulesub( path )
+ unless match, no_match_5
+ $P0= match[0]
+ $I0= $P0.from()
+ $I1= $P0.to()
+ $S0= ""
+ substr path, $I0, $I1, $S0
+no_match_5:
+
+print "-5 "
+print path
+print "\n"
+
+match_6:
+ ## $path =~ s|\\\Z(?!\n)||
+ ## unless $path =~ m{^([A-Z]:)?\\\Z(?!\n)}s;
+ ## xx\ -> xx
+ ## xx1/xx2/xx3/../../xx -> xx1/xx
+ rulesub= p6rule( '$$ <!before \n>' )
+ match= rulesub( path )
+ if match, no_match_6
+
+ rulesub= p6rule( '\\ \n? $' )
+ match= rulesub( path )
+ unless match, no_match_3
+ $P0= match[0]
+ $I0= $P0.from()
+ $I1= $P0.to()
+ $S0= ""
+ substr path, $I0, $I1, $S0
+no_match_6:
+
+print "-6 "
+print path
+print "\n"
+
+match_7:
+ ## $path =~ s|\\\.\.\.\\|\\\.\.\\\.\.\\|g;
+ ## \...\ is 2 levels up
+ rulesub= p6rule( '\\ \. \. \. \\' )
+do_match_7:
+ match= rulesub( path )
+ unless match, no_match_7
+ $P0= match[0]
+ $I0= $P0.from()
+ $I1= $P0.to()
+ $S0= "\\\.\.\\\.\.\\"
+ substr path, $I0, $I1, $S0
+ goto do_match_7
+no_match_7:
+
+print "-7 "
+print path
+print "\n"
+
+match_8:
+ ## $path =~ s|^\.\.\.\\|\.\.\\\.\.\\|g;
+ ## ...\ is 2 levels up
+ rulesub= p6rule( '^ \. \. \. \\' )
+ match= rulesub( path )
+do_match_8:
+ unless match, no_match_8
+ $P0= match[0]
+ $I0= $P0.from()
+ $I1= $P0.to()
+ $S0= "\.\.\\\.\.\\"
+ substr path, $I0, $I1, $S0
+ goto do_match_8
+no_match_8:
+
+print "-8 "
+print path
+print "\n"
+
+match_9:
+ ## return $path if $path =~ m|^\.\.|;
+ ## skip relative paths
+ rulesub= p6rule( '^ \. \.' )
+ match= rulesub( path )
+do_match_9:
+ if match, return
+no_match_9:
+
+print "-9 "
+print path
+print "\n"
+
+match_10:
+ ## return $path unless $path =~ /\.\./;
+ ## too few .'s to cleanup
+ rulesub= p6rule( '\. \.' )
+ match= rulesub( path )
+do_match_10:
+ unless match, return
+no_match_10:
+
+print "10 "
+print path
+print "\n"
+
+match_11:
+ ## return $path if $path =~ /\.\.\.\./;
+ ## too many .'s to cleanup
+ rulesub= p6rule( '\. \. \. \.' )
+ match= rulesub( path )
+do_match_11:
+ if match, return
+no_match_11:
+
+print "11 "
+print path
+print "\n"
+
+match_12:
+ ## $path =~ s{^\\\.\.$}{\\};
+ ## \.. -> \
+ rulesub= p6rule( '^ \\ \. \. $' )
+do_match_12:
+ match= rulesub( path )
+ unless match, no_match_12
+ $P0= match[0]
+ $I0= $P0.from()
+ $I1= $P0.to()
+ $S0= "\\"
+ substr path, $I0, $I1, $S0
+no_match_12:
+
+print "12 "
+print path
+print "\n"
+
+match_13:
+ ## 1 while $path =~ s{^\\\.\.}{};
+ ## \..\xx -> \xx
+ rulesub= p6rule( '^ \\ \. \.' )
+do_match_13:
+ match= rulesub( path )
+ unless match, no_match_13
+ $P0= match[0]
+ $I0= $P0.from()
+ $I1= $P0.to()
+ $S0= ""
+ substr path, $I0, $I1, $S0
+ goto match_13
+no_match_13:
+
+print "13 "
+print path
+print "\n"
+
+# my ($vol,$s_dirs,$file) = $self->splitpath($path);
+ .local string vol, s_dirs, file
+ ( vol, s_dirs, file )= self.'splitpath'( path )
+
+# my @dirs = $self->splitdir($dirs);
+ .local pmc a_dirs
+ a_dirs= new .ResizableStringArray
+ ( a_dirs )= self.splitdir( s_dirs )
+
+## TODO unfinished
+# my (@base_dirs, @path_dirs);
+# my $dest = [EMAIL PROTECTED];
+# for my $dir (@dirs){
+# $dest = [EMAIL PROTECTED] if $dir eq $self->updir;
+# push @$dest, $dir;
+# }
+# # for each .. in @path_dirs pop one item from
+# # @base_dirs
+# while (my $dir = shift @path_dirs){
+# unless ($dir eq $self->updir){
+# unshift @path_dirs, $dir;
+# last;
+# }
+# pop @base_dirs;
+# }
+# $path = $self->catpath(
+# $vol,
+# $self->catdir(@base_dirs, @path_dirs),
+# $file
+# );
+
+return:
+ .return( path )
+.end
+
+
+## TODO probably broken
+.sub 'splitpath' method
+ .param string path
+ .param int no_file
+ .local string volume, directory, file
+
+ '_E_not_yet_implemented'()
+ volume= ''
+ directory= ''
+ file= ''
+
+ .local pmc p6rule
+ .local pmc rulesub
+ .local pmc match
+
+ p6rule= find_global 'PGE', 'p6rule'
+
+ unless no_file, with_file
+ ## m{^( (?:[a-zA-Z]:|(?:\\\\|//)[^\\/]+[\\/][^\\/]+)? )
+ ## (.*)
+ ## }xs;
+
+## TODO these are funky and might not be right
+ rulesub= p6rule( ' ^ ( <[a..zA..Z]> \: | [\\ | //] <-[\\/]>+ <[\\/]>
<-[\\/]>+ )? (.*) ' )
+ match= rulesub( path )
+ unless match, return
+ $P0= match[0]
+ $S0= $P0
+ volume= $S0
+
+ $P0= match[1]
+ $S0= $P0
+ directory= $P0
+ goto return
+
+with_file:
+## TODO these are funky and might not be right
+ rulesub= p6rule( ' ^ ( < [a..zA..Z] \: | <\\ \\ | //> [^\\/]+ [\\/]
[^\\/]+ >)? ( < .* [\\/] < \. \.? <\n? $$>? >?) (.*) ' )
+ match= rulesub( path )
+ unless match, return
+
+ $P0= match[0]
+ $S0= $P0
+ volume= $S0
+
+ $P0= match[1]
+ $S0= $P0
+ directory= $P0
+
+ $P0= match[2]
+ $S0= $P0
+ file= $P0
+
+return:
+ .return( volume, directory, file )
+.end
+
+
+## TODO unfinished
+.sub 'splitdir' method
+ .param string directories
+ '_E_not_yet_implemented'()
+
+ .local string dir_copy
+ dir_copy= directories
+
+ .local pmc a_directories
+ a_directories= new .ResizableStringArray
+
+ .local pmc p6rule
+ .local pmc rulesub
+ .local pmc match
+
+ p6rule= find_global 'PGE', 'p6rule'
+
+## TODO this whole rule section
+ rulesub= p6rule( '^ <[\\/]>? ( <-[\\/]>? ) <[\\/]>? ' )
+match_1:
+ match= rulesub( dir_copy )
+ unless match, trailing_nulls
+ $I0= 0
+split:
+
+trailing_nulls:
+
+return:
+ .return( a_directories )
+.end
+
+
+## TODO unfinished
+.sub 'catpath' method
+ .param string volume
+ .param string directory
+ .param string file
+
+ ## TODO
+ '_E_not_yet_implemented'()
+.end
+
+
+## TODO unfinished
+.sub 'abs2rel' method
+ '_E_not_yet_implemented'()
+.end
+
+
+## TODO unfinished
+.sub 'rel2abs' method
+ '_E_not_yet_implemented'()
+.end
+
+
+.sub 'VERSION' method
+ .local pmc version
+ version= global 'VERSION'
+ .return( version )
+.end
+
+
+## TODO more functions
+
+.sub '_fixup_args'
+ '_E_not_yet_implemented'()
+.end
+
+
+## throw an error that the sub isn't yet implemented
+.sub '_E_not_yet_implemented'
+ new $P0, .Exception
+ $P0['_message']= 'not yet implemented'
+ throw $P0
+.end
Added: trunk/t/library/File-Spec.t
==============================================================================
--- (empty file)
+++ trunk/t/library/File-Spec.t Fri Nov 4 13:17:33 2005
@@ -0,0 +1,119 @@
+#!perl
+# Copyright: 2001-2005 The Perl Foundation. All Rights Reserved.
+# $Id$
+
+use strict;
+use warnings;
+use Parrot::Test;
+
+##############################
+# File::Spec
+
+
+my $PRE= <<'PRE';
+.sub 'main' :main
+ load_bytecode 'library/File/Spec.pir'
+
+ .local int classtype
+ .local pmc spec
+
+ find_type classtype, 'File::Spec'
+ new spec, classtype
+
+PRE
+my $POST= <<'POST';
+ goto OK
+NOK:
+ print "not "
+OK:
+ print "ok"
+END:
+ print "\n"
+.end
+POST
+
+
+plan skip_all => 'win32 implementation only'
+ unless $^O =~ m/MSWin32/;
+
+## 1
+pir_output_is(<<'CODE'.$POST, <<'OUT', "load_bytecode");
+.sub 'main' :main
+ load_bytecode 'File/Spec.pir'
+CODE
+ok
+OUT
+
+
+pir_output_is($PRE.<<'CODE'.$POST, <<'OUT', "new");
+CODE
+ok
+OUT
+
+
+my @meths= (qw/
+ __isa VERSION devnull tmpdir case_tolerant file_name_is_absolute catfile
+ catdir path canonpath splitpath splitdir catpath abs2rel rel2abs
+/);
+pir_output_is($PRE.<<"CODE".$POST, <<'OUT', "can ($_)") for @meths;
+ .local pmc meth
+ \$I0 = can spec, "$_"
+ unless \$I0, NOK
+CODE
+ok
+OUT
+
+
+pir_output_like($PRE.<<'CODE'.$POST, <<'OUT', "isa");
+ .local pmc class
+ class= new String
+
+ class= spec.'__isa'()
+ print class
+ print "\n"
+CODE
+/^File::Spec::.+/
+OUT
+
+
+pir_output_is($PRE.<<'CODE'.$POST, <<'OUT', "version");
+ .local pmc version
+ version= spec.'VERSION'()
+ print version
+ goto END
+CODE
+0.1
+OUT
+
+
+## testing private subs
+pir_output_is($PRE.<<'CODE'.$POST, <<"OUT", "_get_osname");
+ .local string osname
+ .local pmc get_osname
+ get_osname = find_global 'File::Spec', '_get_osname'
+ osname= get_osname()
+ print osname
+ goto END
+CODE
+$^O
+OUT
+
+
+pir_output_is($PRE.<<'CODE'.$POST, <<'OUT', "_get_module");
+ .local string module
+ .local pmc get_module
+ get_module = find_global 'File::Spec', '_get_module'
+ module= get_module( 'MSWin32' )
+ print module
+ print "\n"
+ module= get_module( 'foobar' )
+ print module
+ goto END
+CODE
+Win32
+Unix
+OUT
+
+
+# remember to update the number of tests! :-)
+BEGIN { plan tests => 21; }