CINXE.COM
Irregular Expression: Implementing IO::Path in Rakudo
<!DOCTYPE html> <html class='v2' dir='ltr' lang='en'> <head> <link href='https://www.blogger.com/static/v1/widgets/3566091532-css_bundle_v2.css' rel='stylesheet' type='text/css'/> <meta content='width=1100' name='viewport'/> <meta content='text/html; charset=UTF-8' http-equiv='Content-Type'/> <meta content='blogger' name='generator'/> <link href='http://blog.brentlaabs.com/favicon.ico' rel='icon' type='image/x-icon'/> <link href='http://blog.brentlaabs.com/2013/05/implementing-iopath-in-rakudo.html' rel='canonical'/> <link rel="alternate" type="application/atom+xml" title="Irregular Expression - Atom" href="http://blog.brentlaabs.com/feeds/posts/default" /> <link rel="alternate" type="application/rss+xml" title="Irregular Expression - RSS" href="http://blog.brentlaabs.com/feeds/posts/default?alt=rss" /> <link rel="service.post" type="application/atom+xml" title="Irregular Expression - Atom" href="https://www.blogger.com/feeds/1163079349106481731/posts/default" /> <link rel="alternate" type="application/atom+xml" title="Irregular Expression - Atom" href="http://blog.brentlaabs.com/feeds/6687406856608519456/comments/default" /> <!--Can't find substitution for tag [blog.ieCssRetrofitLinks]--> <meta content='http://blog.brentlaabs.com/2013/05/implementing-iopath-in-rakudo.html' property='og:url'/> <meta content='Implementing IO::Path in Rakudo' property='og:title'/> <meta content='I started off implementing the File::Spec module for Perl 6, as explained in the last blog post , but what I really wanted to do was to get ...' property='og:description'/> <title>Irregular Expression: Implementing IO::Path in Rakudo</title> <style id='page-skin-1' type='text/css'><!-- /* ----------------------------------------------- Blogger Template Style Name: Watermark Designer: Blogger URL: www.blogger.com ----------------------------------------------- */ /* Use this with templates/1ktemplate-*.html */ /* Content ----------------------------------------------- */ body { font: normal normal 14px Arial, Tahoma, Helvetica, FreeSans, sans-serif; color: #333333; background: #c0a154 url(https://resources.blogblog.com/blogblog/data/1kt/watermark/body_background_birds.png) repeat scroll top left; } html body .content-outer { min-width: 0; max-width: 100%; width: 100%; } .content-outer { font-size: 92%; } a:link { text-decoration:none; color: #cc3300; } a:visited { text-decoration:none; color: #993322; } a:hover { text-decoration:underline; color: #ff3300; } .body-fauxcolumns .cap-top { margin-top: 30px; background: transparent url(https://resources.blogblog.com/blogblog/data/1kt/watermark/body_overlay_birds.png) no-repeat scroll top right; height: 121px; } .content-inner { padding: 0; } /* Header ----------------------------------------------- */ .header-inner .Header .titlewrapper, .header-inner .Header .descriptionwrapper { padding-left: 20px; padding-right: 20px; } .Header h1 { font: normal normal 60px Georgia, Utopia, 'Palatino Linotype', Palatino, serif; color: #ffffff; text-shadow: 2px 2px rgba(0, 0, 0, .1); } .Header h1 a { color: #ffffff; } .Header .description { font-size: 140%; color: #997755; } /* Tabs ----------------------------------------------- */ .tabs-inner .section { margin: 0 20px; } .tabs-inner .PageList, .tabs-inner .LinkList, .tabs-inner .Labels { margin-left: -11px; margin-right: -11px; background-color: transparent; border-top: 0 solid #ffffff; border-bottom: 0 solid #ffffff; -moz-box-shadow: 0 0 0 rgba(0, 0, 0, .3); -webkit-box-shadow: 0 0 0 rgba(0, 0, 0, .3); -goog-ms-box-shadow: 0 0 0 rgba(0, 0, 0, .3); box-shadow: 0 0 0 rgba(0, 0, 0, .3); } .tabs-inner .PageList .widget-content, .tabs-inner .LinkList .widget-content, .tabs-inner .Labels .widget-content { margin: -3px -11px; background: transparent none no-repeat scroll right; } .tabs-inner .widget ul { padding: 2px 25px; max-height: 34px; background: transparent none no-repeat scroll left; } .tabs-inner .widget li { border: none; } .tabs-inner .widget li a { display: inline-block; padding: .25em 1em; font: normal normal 20px Georgia, Utopia, 'Palatino Linotype', Palatino, serif; color: #cc3300; border-right: 1px solid #c0a154; } .tabs-inner .widget li:first-child a { border-left: 1px solid #c0a154; } .tabs-inner .widget li.selected a, .tabs-inner .widget li a:hover { color: #000000; } /* Headings ----------------------------------------------- */ h2 { font: normal normal 20px Georgia, Utopia, 'Palatino Linotype', Palatino, serif; color: #000000; margin: 0 0 .5em; } h2.date-header { font: normal normal 16px Arial, Tahoma, Helvetica, FreeSans, sans-serif; color: #997755; } /* Main ----------------------------------------------- */ .main-inner .column-center-inner, .main-inner .column-left-inner, .main-inner .column-right-inner { padding: 0 5px; } .main-outer { margin-top: 0; background: transparent none no-repeat scroll top left; } .main-inner { padding-top: 30px; } .main-cap-top { position: relative; } .main-cap-top .cap-right { position: absolute; height: 0; width: 100%; bottom: 0; background: transparent none repeat-x scroll bottom center; } .main-cap-top .cap-left { position: absolute; height: 245px; width: 280px; right: 0; bottom: 0; background: transparent none no-repeat scroll bottom left; } /* Posts ----------------------------------------------- */ .post-outer { padding: 15px 20px; margin: 0 0 25px; background: transparent url(https://resources.blogblog.com/blogblog/data/1kt/watermark/post_background_birds.png) repeat scroll top left; _background-image: none; border: dotted 1px #ccbb99; -moz-box-shadow: 0 0 0 rgba(0, 0, 0, .1); -webkit-box-shadow: 0 0 0 rgba(0, 0, 0, .1); -goog-ms-box-shadow: 0 0 0 rgba(0, 0, 0, .1); box-shadow: 0 0 0 rgba(0, 0, 0, .1); } h3.post-title { font: normal normal 30px Georgia, Utopia, 'Palatino Linotype', Palatino, serif; margin: 0; } .comments h4 { font: normal normal 30px Georgia, Utopia, 'Palatino Linotype', Palatino, serif; margin: 1em 0 0; } .post-body { font-size: 105%; line-height: 1.5; position: relative; } .post-header { margin: 0 0 1em; color: #997755; } .post-footer { margin: 10px 0 0; padding: 10px 0 0; color: #997755; border-top: dashed 1px #777777; } #blog-pager { font-size: 140% } #comments .comment-author { padding-top: 1.5em; border-top: dashed 1px #777777; background-position: 0 1.5em; } #comments .comment-author:first-child { padding-top: 0; border-top: none; } .avatar-image-container { margin: .2em 0 0; } /* Comments ----------------------------------------------- */ .comments .comments-content .icon.blog-author { background-repeat: no-repeat; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9sLFwMeCjjhcOMAAAD+SURBVDjLtZSvTgNBEIe/WRRnm3U8RC1neQdsm1zSBIU9VVF1FkUguQQsD9ITmD7ECZIJSE4OZo9stoVjC/zc7ky+zH9hXwVwDpTAWWLrgS3QAe8AZgaAJI5zYAmc8r0G4AHYHQKVwII8PZrZFsBFkeRCABYiMh9BRUhnSkPTNCtVXYXURi1FpBDgArj8QU1eVXUzfnjv7yP7kwu1mYrkWlU33vs1QNu2qU8pwN0UpKoqokjWwCztrMuBhEhmh8bD5UDqur75asbcX0BGUB9/HAMB+r32hznJgXy2v0sGLBcyAJ1EK3LFcbo1s91JeLwAbwGYu7TP/3ZGfnXYPgAVNngtqatUNgAAAABJRU5ErkJggg==); } .comments .comments-content .loadmore a { border-top: 1px solid #777777; border-bottom: 1px solid #777777; } .comments .continue { border-top: 2px solid #777777; } /* Widgets ----------------------------------------------- */ .widget ul, .widget #ArchiveList ul.flat { padding: 0; list-style: none; } .widget ul li, .widget #ArchiveList ul.flat li { padding: .35em 0; text-indent: 0; border-top: dashed 1px #777777; } .widget ul li:first-child, .widget #ArchiveList ul.flat li:first-child { border-top: none; } .widget .post-body ul { list-style: disc; } .widget .post-body ul li { border: none; } .widget .zippy { color: #777777; } .post-body img, .post-body .tr-caption-container, .Profile img, .Image img, .BlogList .item-thumbnail img { padding: 5px; background: #fff; -moz-box-shadow: 1px 1px 5px rgba(0, 0, 0, .5); -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, .5); -goog-ms-box-shadow: 1px 1px 5px rgba(0, 0, 0, .5); box-shadow: 1px 1px 5px rgba(0, 0, 0, .5); } .post-body img, .post-body .tr-caption-container { padding: 8px; } .post-body .tr-caption-container { color: #333333; } .post-body .tr-caption-container img { padding: 0; background: transparent; border: none; -moz-box-shadow: 0 0 0 rgba(0, 0, 0, .1); -webkit-box-shadow: 0 0 0 rgba(0, 0, 0, .1); -goog-ms-box-shadow: 0 0 0 rgba(0, 0, 0, .1); box-shadow: 0 0 0 rgba(0, 0, 0, .1); } /* Footer ----------------------------------------------- */ .footer-outer { color:#ccbb99; background: #330000 url(https://resources.blogblog.com/blogblog/data/1kt/watermark/body_background_navigator.png) repeat scroll top left; } .footer-outer a { color: #ff7755; } .footer-outer a:visited { color: #dd5533; } .footer-outer a:hover { color: #ff9977; } .footer-outer .widget h2 { color: #eeddbb; } /* Mobile ----------------------------------------------- */ body.mobile { background-size: 100% auto; } .mobile .body-fauxcolumn-outer { background: transparent none repeat scroll top left; } html .mobile .mobile-date-outer { border-bottom: none; background: transparent url(https://resources.blogblog.com/blogblog/data/1kt/watermark/post_background_birds.png) repeat scroll top left; _background-image: none; margin-bottom: 10px; } .mobile .main-inner .date-outer { padding: 0; } .mobile .main-inner .date-header { margin: 10px; } .mobile .main-cap-top { z-index: -1; } .mobile .content-outer { font-size: 100%; } .mobile .post-outer { padding: 10px; } .mobile .main-cap-top .cap-left { background: transparent none no-repeat scroll bottom left; } .mobile .body-fauxcolumns .cap-top { margin: 0; } .mobile-link-button { background: transparent url(https://resources.blogblog.com/blogblog/data/1kt/watermark/post_background_birds.png) repeat scroll top left; } .mobile-link-button a:link, .mobile-link-button a:visited { color: #cc3300; } .mobile-index-date .date-header { color: #997755; } .mobile-index-contents { color: #333333; } .mobile .tabs-inner .section { margin: 0; } .mobile .tabs-inner .PageList { margin-left: 0; margin-right: 0; } .mobile .tabs-inner .PageList .widget-content { margin: 0; color: #000000; background: transparent url(https://resources.blogblog.com/blogblog/data/1kt/watermark/post_background_birds.png) repeat scroll top left; } .mobile .tabs-inner .PageList .widget-content .pagelist-arrow { border-left: 1px solid #c0a154; } .striped-table .odd { background-color: #DCCBA1; } --></style> <style id='template-skin-1' type='text/css'><!-- body { min-width: 960px; } .content-outer, .content-fauxcolumn-outer, .region-inner { min-width: 960px; max-width: 960px; _width: 960px; } .main-inner .columns { padding-left: 0px; padding-right: 310px; } .main-inner .fauxcolumn-center-outer { left: 0px; right: 310px; /* IE6 does not respect left and right together */ _width: expression(this.parentNode.offsetWidth - parseInt("0px") - parseInt("310px") + 'px'); } .main-inner .fauxcolumn-left-outer { width: 0px; } .main-inner .fauxcolumn-right-outer { width: 310px; } .main-inner .column-left-outer { width: 0px; right: 100%; margin-left: -0px; } .main-inner .column-right-outer { width: 310px; margin-right: -310px; } #layout { min-width: 0; } #layout .content-outer { min-width: 0; width: 800px; } #layout .region-inner { min-width: 0; width: auto; } body#layout div.add_widget { padding: 8px; } body#layout div.add_widget a { margin-left: 32px; } --></style> <link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=1163079349106481731&zx=c92d9171-713d-414c-963f-50bec28b445b' media='none' onload='if(media!='all')media='all'' rel='stylesheet'/><noscript><link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=1163079349106481731&zx=c92d9171-713d-414c-963f-50bec28b445b' rel='stylesheet'/></noscript> <meta name='google-adsense-platform-account' content='ca-host-pub-1556223355139109'/> <meta name='google-adsense-platform-domain' content='blogspot.com'/> </head> <body class='loading variant-birds'> <div class='navbar section' id='navbar' name='Navbar'><div class='widget Navbar' data-version='1' id='Navbar1'><script type="text/javascript"> function setAttributeOnload(object, attribute, val) { if(window.addEventListener) { window.addEventListener('load', function(){ object[attribute] = val; }, false); } else { window.attachEvent('onload', function(){ object[attribute] = val; }); } } </script> <div id="navbar-iframe-container"></div> <script type="text/javascript" src="https://apis.google.com/js/platform.js"></script> <script type="text/javascript"> gapi.load("gapi.iframes:gapi.iframes.style.bubble", function() { if (gapi.iframes && gapi.iframes.getContext) { gapi.iframes.getContext().openChild({ url: 'https://www.blogger.com/navbar/1163079349106481731?po\x3d6687406856608519456\x26origin\x3dhttp://blog.brentlaabs.com', where: document.getElementById("navbar-iframe-container"), id: "navbar-iframe" }); } }); </script><script type="text/javascript"> (function() { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = '//pagead2.googlesyndication.com/pagead/js/google_top_exp.js'; var head = document.getElementsByTagName('head')[0]; if (head) { head.appendChild(script); }})(); </script> </div></div> <div class='body-fauxcolumns'> <div class='fauxcolumn-outer body-fauxcolumn-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <div class='content'> <div class='content-fauxcolumns'> <div class='fauxcolumn-outer content-fauxcolumn-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <div class='content-outer'> <div class='content-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left content-fauxborder-left'> <div class='fauxborder-right content-fauxborder-right'></div> <div class='content-inner'> <header> <div class='header-outer'> <div class='header-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left header-fauxborder-left'> <div class='fauxborder-right header-fauxborder-right'></div> <div class='region-inner header-inner'> <div class='header section' id='header' name='Header'><div class='widget Header' data-version='1' id='Header1'> <div id='header-inner'> <div class='titlewrapper'> <h1 class='title'> <a href='http://blog.brentlaabs.com/'> Irregular Expression </a> </h1> </div> <div class='descriptionwrapper'> <p class='description'><span>Brent Laabs on programming and culture</span></p> </div> </div> </div></div> </div> </div> <div class='header-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </header> <div class='tabs-outer'> <div class='tabs-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left tabs-fauxborder-left'> <div class='fauxborder-right tabs-fauxborder-right'></div> <div class='region-inner tabs-inner'> <div class='tabs no-items section' id='crosscol' name='Cross-Column'></div> <div class='tabs no-items section' id='crosscol-overflow' name='Cross-Column 2'></div> </div> </div> <div class='tabs-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='main-outer'> <div class='main-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left main-fauxborder-left'> <div class='fauxborder-right main-fauxborder-right'></div> <div class='region-inner main-inner'> <div class='columns fauxcolumns'> <div class='fauxcolumn-outer fauxcolumn-center-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='fauxcolumn-outer fauxcolumn-left-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='fauxcolumn-outer fauxcolumn-right-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <!-- corrects IE6 width calculation --> <div class='columns-inner'> <div class='column-center-outer'> <div class='column-center-inner'> <div class='main section' id='main' name='Main'><div class='widget Blog' data-version='1' id='Blog1'> <div class='blog-posts hfeed'> <div class="date-outer"> <h2 class='date-header'><span>Wednesday, May 8, 2013</span></h2> <div class="date-posts"> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='1163079349106481731' itemprop='blogId'/> <meta content='6687406856608519456' itemprop='postId'/> <a name='6687406856608519456'></a> <h3 class='post-title entry-title' itemprop='name'> Implementing IO::Path in Rakudo </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-6687406856608519456' itemprop='description articleBody'> I started off implementing the File::Spec module for Perl 6, <a href="http://blog.brentlaabs.com/2013/05/porting-module-to-perl-6.html">as explained in the last blog post</a>, but what I really wanted to do was to get some sanity in working with paths through IO::Path objects. And if I was going to do this, I needed to actually edit the core modules of Rakudo.<br /> <br /> Starting with a module which does stringy operations on directory paths, I set out with the goal of making some sort of easy-to-use, path manipulation class in the core. Something like how <a href="http://search.cpan.org/~kwilliams/Path-Class-0.31/lib/Path/Class.pm">Path::Class</a> works in Perl 5. Then I looked at <a href="http://perlcabal.org/syn/S32/IO.html">S32::IO</a>, and realized that IO::Path was exactly what I was seeking. But it was only partially implemented, and only for POSIX.<br /> <br /> So I'm going to walk through the steps I took to integrate multiple-OS path support into Rakudo, in the hopes that it will help other people to avoid my mistakes. Which were fairly numerous. :/<br /> <br /> This was my first foray into hacking a compiler, and I must confess it was fairly intimidating. I'm no script kiddie, but I hadn't worked on any large open-source projects before. However, the setting is made of Perl 6 code, so it was more or less a matter of integrating what FROGGS and I had already written.<br /> <br /> <h3> Baby's first steps</h3> <br /> I had played around with using File::Spec as a backend to an IO::Path in the <a href="https://github.com/labster/p6-IO-Path-More">IO::Path::More module</a>, so it became clear that this was the best way forward for Rakudo. <br /> <br /> I realized right away that IO::Path's interface would have to change to include systems with a concept of volume. I did a small edit to IO.pm to add a <code>$.volume</code> attribute, changed a few lines of code in <code>sub dir</code>, and compiled. Everything worked. I sent a pull request into Rakudo, just to get the interface-changing out of the way first. It tested okay, and was accepted. Wow, I'm good at this!<br /> <br /> Naturally, it all got worse from there.<br /> <br /> <h3> Problem 1: Biting off more than I can chew</h3> <br /> The next step was to add the File::Spec modules into the core. So I just started by copying over the .pm files into the core directory. Unlike in normal Perl code, the modules aren't included with <code>use Module;</code>. Instead, I edited the <code>Makefile.in</code> to add the modules in the correct order.<br /> <br /> Since the File::Spec object needs to inherit from the subclasses, and the other subclasses inherit from File::Spec::Unix, I went with this order: File::Spec::Unix, File::Spec::Win32, File::Spec::Cygwin, File::Spec. Now, some of you might already see a problem with that. To them, I say: "Shh! No spoilers!"<br /> <br /> I realized that I couldn't use the file-scoped class definition (<code>class Foo;</code>) if it was going to end up all in one file, so I switched those out for curly braces. Then I rebuilt the makefile and compiled rakudo.<br /> <br /> That generated a whole mess of errors. And not all nice errors like before -- Exception.pm hadn't even loaded yet! This was a bunch of nqp recursion errors. I tried scaling back a little at a time, even commenting out the entire inside of classes, but I still had issues building Rakudo. Eventually, I had to scale back my approach, and add files to the build one at a time.<br /> <br /> <h3> Problem 2: Inheritance</h3> <br /> It turned out that each file in my additions had it's own unique problems. The first was well, it seemed like File::Spec::Unix just, well, disappeared. Unless I completely removed the File::Spec class, and then it worked.<br /> <br /> When you declare a subclass, you're actually adding to the main class' package. So File::Spec::Unix is really <code>File::Spec.WHO<Unix></code>. So if you initialize File::Spec after File::Spec::Unix, it nukes the previous package and its symbol table. This problem was a lot of no fun to figure out, and I'm glad moritz++ and jnthn++ walked me through it.<br /> <br /> The solution here was simple enough -- stub out File::Spec with <code>class File::Spec { ... }</code> before creating File::Spec::Unix. This is enough to make sure File::Spec will be able to refer to its children.<br /> <br /> Although... the last thing I need is some yahoo doing <code>my class File</code> and then complaining about why they can't load File::Spec. So it was at this time I decided to change File::Spec to IO::Spec. Making a File class I can see -- if you decide to replace class IO, then you deserve what you get.<br /> <br /> <h3> Problem 3: The language is in the process of building</h3> <br /> The setting may feel like normal Perl code, but it's not. It's still in the process of being built. It's like a house in the process of construction. If there's only a wood frame, you can still hang a portrait on the "walls" -- but this will only get in the way when it's time to hang the drywall. Things need to come together in the correct order.<br /> <br /> I encountered these problems in a couple of different ways. The first was in using <code>rx//</code> to precompile some patterns before Regex.pm was loaded. Windows-style paths really need this for readablity, because they use both kinds of slashes as separators and the concept of volume is fairly complicated. I tried a lot of different ways of formatting, each of which made the build fail in new and unique ways. Then I discovered <code>MAKE_REGEX()</code> had loaded a bit before the IO modules. This particular problem seemed to be solved.<br /> <br /> The next couple of problems were caused by <code>$*OS</code> not being in scope at build time, as terms.pm was way, way down at the end. It works just fine in method calls, but if it's needed as a class attribute, it's simply not in scope when you're building the class. I ended up replicating the same op used in terms.pm to get the kernel string, so I could have it available earlier. Early enough to figure out which subclass of IO::Spec to use for the main object.<br /> <br /> So remember, object building happens right away, but subs and methods can carry references to things that happen later.<br /> <br /> <h3> Problem 4: <code>Br</code>eaking <code>Pa</code>nda</h3> <br /> Everything seemed like it was working pretty okay at this point. Until I got to the day of the masakism IRC seminar. It was at this point that tried to install a module for the class, so I couldn't help but notice that Panda seemed to die horribly. I checked out and built the nom branch to use for the duration, but I really had no idea what was going on.<br /> <br /> When I golfed the breakage in Panda, it came down to its "use lib" line -- and lib.pm is shockingly simple. Running <code>use lib 'foo'</code> in the REPL alternated between three different errors from the NQP level. Something was seriously wrong.<br /> <br /> My only choice was to work backwards, and see what was causing the problem. I would say <code>git bisect</code> here, but I hadn't actually been making enough commits to effectively get at the problem. So that was the first learning experience here -- commit any time you think you have functional code.<br /> <br /> Anyway, it took a lot of edits, and I got most of the way through a novel while waiting for Rakudo to recompile, but I eventually traced it back to the precompiled regexes that were giving me a problem earlier. At this point I was about to give up, and make long, ugly regexes. Finally jnthn++ noted that Regex.pm hadn't loaded when this was trying to run, so I should just move all of the IO modules to later in the build.<br /> <br /> So I swapped back in the <code>rx//</code> syntax, and naturally, it all worked. The lesson here is that running some real software can pick up bugs (although the spectests would have shown it too). And that you really do need to make sure that dependencies come earlier. And most of all, if you're stuck, just ask in #perl6.<br /> <br /> <h3> Spectesting</h3> <br /> The methods I developed for IO::Path::More to IO::Path went in painlessly. I ended up writing an additional set of methods for IO::Spec -- <code>.split</code> and <code>.join</code>, to replace <code>.splitpath</code> and <code>.catpath</code> but with <code>basename</code> and <code>dirname</code> syntax. That allows IO::Path.basename to always have the current item in question, and all of the trailing slashes are gone.<br /> <br /> It was at this point where I started thinking about testing. IO::Spec had literally hundreds of tests from File::Spec in Perl 5. But ironically, IO::Spec wasn't actually specced. So the question became, should IO::Spec be just a backend, or a fully specified part of Perl 6?<br /> <br /> Implementations in Perl 6 are supposed to inform the spec, as well as the other way around. And the more I thought about it, *something* has to do the low-level string operations on paths. And there is no reason to hide it, either. Rakudo already provides access to all of its lower layers via nqp or pir ops, so it made sense to include it as a specced part of Perl 6.<br /> <br /> So I went ahead and edited the Specification for S32::IO, adding IO::Spec and several methods for manipulating IO::Paths. Lots of text. And then even more went into writing tests for IO::Path. Naturally, these uncovered some more minor bugs, but that's what tests are for.<br /> <br /> <h3> Patch Approved</h3> <br /> It didn't take all that long for my pull request to get merged, especially after I started writing tests. This whole process took about three weeks. I'll have a few minor cleanup I'm going to have to do in the next couple of days, as I resolve a bug in using IO::Spec::Unix.rel2abs. Parrot just added a readlink op on my request, so IO::Path.resolve should be working soon.<br /> <br /> And what we have to show for all this work is a Perl 6 implementation that does file path modifications on Linux, Cygwin, or Windows/DOS.<br /> <code> On Linux:<br /> "/foo/./bar//"\ .path.cleanup.parent; #yields "/foo"<br /> On Windows: <br /> "C:/foo\\.\\bar\\".path.cleanup.parent; #yields "C:\foo"<br /> On any platform:<br /> IO::Path::Win32.new("C:/baz").volume; #yields "C:"</code><br /> I never finished VMS, or Mac Classic, but at this point, they can just be dropped in, by adding a new IO::Spec subclass.<br /> <br /> So there it is, at long last: sanity in file paths in Perl. I think, if I had known how it was going to go from the beginning, I would have been even <i>more</i> intimidated. Even so, it was just the same kind of debugging I'm used to in modules. Only without the safety rails of the parser and a much longer build time.<br /> <br /> But if you can write a module and a class in Perl 6, you already have most of the skills to contribute to the setting. A compiler with internals that feel like a high-level scripting language: that, like digital watches, is a pretty neat idea. <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> Posted by <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta content='https://www.blogger.com/profile/17518100165641296059' itemprop='url'/> <a class='g-profile' href='https://www.blogger.com/profile/17518100165641296059' rel='author' title='author profile'> <span itemprop='name'>Brent Laabs</span> </a> </span> </span> <span class='post-timestamp'> at <meta content='http://blog.brentlaabs.com/2013/05/implementing-iopath-in-rakudo.html' itemprop='url'/> <a class='timestamp-link' href='http://blog.brentlaabs.com/2013/05/implementing-iopath-in-rakudo.html' rel='bookmark' title='permanent link'><abbr class='published' itemprop='datePublished' title='2013-05-08T01:02:00-07:00'>1:02 AM</abbr></a> </span> <span class='post-comment-link'> </span> <span class='post-icons'> <span class='item-control blog-admin pid-758962430'> <a href='https://www.blogger.com/post-edit.g?blogID=1163079349106481731&postID=6687406856608519456&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <div class='post-share-buttons goog-inline-block'> </div> </div> <div class='post-footer-line post-footer-line-2'> <span class='post-labels'> Labels: <a href='http://blog.brentlaabs.com/search/label/debugging' rel='tag'>debugging</a>, <a href='http://blog.brentlaabs.com/search/label/perl6' rel='tag'>perl6</a>, <a href='http://blog.brentlaabs.com/search/label/programming' rel='tag'>programming</a> </span> </div> <div class='post-footer-line post-footer-line-3'> <span class='post-location'> </span> </div> </div> </div> <div class='comments' id='comments'> <a name='comments'></a> <h4>No comments:</h4> <div id='Blog1_comments-block-wrapper'> <dl class='avatar-comment-indent' id='comments-block'> </dl> </div> <p class='comment-footer'> <div class='comment-form'> <a name='comment-form'></a> <h4 id='comment-post-message'>Post a Comment</h4> <p> </p> <a href='https://www.blogger.com/comment/frame/1163079349106481731?po=6687406856608519456&hl=en' id='comment-editor-src'></a> <iframe allowtransparency='true' class='blogger-iframe-colorize blogger-comment-from-post' frameborder='0' height='410px' id='comment-editor' name='comment-editor' src='' width='100%'></iframe> <script src='https://www.blogger.com/static/v1/jsbin/681870030-comment_from_post_iframe.js' type='text/javascript'></script> <script type='text/javascript'> BLOG_CMT_createIframe('https://www.blogger.com/rpc_relay.html'); </script> </div> </p> </div> </div> </div></div> </div> <div class='blog-pager' id='blog-pager'> <span id='blog-pager-newer-link'> <a class='blog-pager-newer-link' href='http://blog.brentlaabs.com/2013/05/how-to-start-hacking-on-rakudo-perl-6.html' id='Blog1_blog-pager-newer-link' title='Newer Post'>Newer Post</a> </span> <span id='blog-pager-older-link'> <a class='blog-pager-older-link' href='http://blog.brentlaabs.com/2013/05/porting-module-to-perl-6.html' id='Blog1_blog-pager-older-link' title='Older Post'>Older Post</a> </span> <a class='home-link' href='http://blog.brentlaabs.com/'>Home</a> </div> <div class='clear'></div> <div class='post-feeds'> <div class='feed-links'> Subscribe to: <a class='feed-link' href='http://blog.brentlaabs.com/feeds/6687406856608519456/comments/default' target='_blank' type='application/atom+xml'>Post Comments (Atom)</a> </div> </div> </div></div> </div> </div> <div class='column-left-outer'> <div class='column-left-inner'> <aside> </aside> </div> </div> <div class='column-right-outer'> <div class='column-right-inner'> <aside> <div class='sidebar section' id='sidebar-right-1'><div class='widget BlogArchive' data-version='1' id='BlogArchive1'> <h2>Blog Archive</h2> <div class='widget-content'> <div id='ArchiveList'> <div id='BlogArchive1_ArchiveList'> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2018/'> 2018 </a> <span class='post-count' dir='ltr'>(1)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2018/01/'> January </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2017/'> 2017 </a> <span class='post-count' dir='ltr'>(1)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2017/01/'> January </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2015/'> 2015 </a> <span class='post-count' dir='ltr'>(2)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2015/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2015/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2014/'> 2014 </a> <span class='post-count' dir='ltr'>(2)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2014/02/'> February </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2014/01/'> January </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate expanded'> <a class='toggle' href='javascript:void(0)'> <span class='zippy toggle-open'> ▼  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2013/'> 2013 </a> <span class='post-count' dir='ltr'>(10)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2013/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2013/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2013/06/'> June </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate expanded'> <a class='toggle' href='javascript:void(0)'> <span class='zippy toggle-open'> ▼  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2013/05/'> May </a> <span class='post-count' dir='ltr'>(3)</span> <ul class='posts'> <li><a href='http://blog.brentlaabs.com/2013/05/how-to-start-hacking-on-rakudo-perl-6.html'>How to start hacking on Rakudo Perl 6</a></li> <li><a href='http://blog.brentlaabs.com/2013/05/implementing-iopath-in-rakudo.html'>Implementing IO::Path in Rakudo</a></li> <li><a href='http://blog.brentlaabs.com/2013/05/porting-module-to-perl-6.html'>Porting a Module to Perl 6</a></li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://blog.brentlaabs.com/2013/04/'> April </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> </div> </div> <div class='clear'></div> </div> </div><div class='widget Profile' data-version='1' id='Profile1'> <h2>About Me</h2> <div class='widget-content'> <dl class='profile-datablock'> <dt class='profile-data'> <a class='profile-name-link g-profile' href='https://www.blogger.com/profile/17518100165641296059' rel='author' style='background-image: url(//www.blogger.com/img/logo-16.png);'> Brent Laabs </a> </dt> <dd class='profile-data'>Ventura, California, United States</dd> <dd class='profile-textblock'>Programmer, writer, gamer, analyst, anime watcher, and wiki editor.</dd> </dl> <a class='profile-link' href='https://www.blogger.com/profile/17518100165641296059' rel='author'>View my complete profile</a> <div class='clear'></div> </div> </div></div> </aside> </div> </div> </div> <div style='clear: both'></div> <!-- columns --> </div> <!-- main --> </div> </div> <div class='main-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <footer> <div class='footer-outer'> <div class='footer-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left footer-fauxborder-left'> <div class='fauxborder-right footer-fauxborder-right'></div> <div class='region-inner footer-inner'> <div class='foot no-items section' id='footer-1'></div> <table border='0' cellpadding='0' cellspacing='0' class='section-columns columns-2'> <tbody> <tr> <td class='first columns-cell'> <div class='foot no-items section' id='footer-2-1'></div> </td> <td class='columns-cell'> <div class='foot no-items section' id='footer-2-2'></div> </td> </tr> </tbody> </table> <!-- outside of the include in order to lock Attribution widget --> <div class='foot section' id='footer-3' name='Footer'><div class='widget Attribution' data-version='1' id='Attribution1'> <div class='widget-content' style='text-align: center;'> Watermark theme. Powered by <a href='https://www.blogger.com' target='_blank'>Blogger</a>. </div> <div class='clear'></div> </div></div> </div> </div> <div class='footer-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </footer> <!-- content --> </div> </div> <div class='content-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <script type='text/javascript'> window.setTimeout(function() { document.body.className = document.body.className.replace('loading', ''); }, 10); </script> <script type="text/javascript" src="https://www.blogger.com/static/v1/widgets/2074308869-widgets.js"></script> <script type='text/javascript'> window['__wavt'] = 'AOuZoY4ga_5Bh15u70hgYQOWKADnASbpXQ:1743051512062';_WidgetManager._Init('//www.blogger.com/rearrange?blogID\x3d1163079349106481731','//blog.brentlaabs.com/2013/05/implementing-iopath-in-rakudo.html','1163079349106481731'); _WidgetManager._SetDataContext([{'name': 'blog', 'data': {'blogId': '1163079349106481731', 'title': 'Irregular Expression', 'url': 'http://blog.brentlaabs.com/2013/05/implementing-iopath-in-rakudo.html', 'canonicalUrl': 'http://blog.brentlaabs.com/2013/05/implementing-iopath-in-rakudo.html', 'homepageUrl': 'http://blog.brentlaabs.com/', 'searchUrl': 'http://blog.brentlaabs.com/search', 'canonicalHomepageUrl': 'http://blog.brentlaabs.com/', 'blogspotFaviconUrl': 'http://blog.brentlaabs.com/favicon.ico', 'bloggerUrl': 'https://www.blogger.com', 'hasCustomDomain': true, 'httpsEnabled': false, 'enabledCommentProfileImages': true, 'gPlusViewType': 'FILTERED_POSTMOD', 'adultContent': false, 'analyticsAccountNumber': '', 'encoding': 'UTF-8', 'locale': 'en', 'localeUnderscoreDelimited': 'en', 'languageDirection': 'ltr', 'isPrivate': false, 'isMobile': false, 'isMobileRequest': false, 'mobileClass': '', 'isPrivateBlog': false, 'isDynamicViewsAvailable': true, 'feedLinks': '\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Irregular Expression - Atom\x22 href\x3d\x22http://blog.brentlaabs.com/feeds/posts/default\x22 /\x3e\n\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/rss+xml\x22 title\x3d\x22Irregular Expression - RSS\x22 href\x3d\x22http://blog.brentlaabs.com/feeds/posts/default?alt\x3drss\x22 /\x3e\n\x3clink rel\x3d\x22service.post\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Irregular Expression - Atom\x22 href\x3d\x22https://www.blogger.com/feeds/1163079349106481731/posts/default\x22 /\x3e\n\n\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Irregular Expression - Atom\x22 href\x3d\x22http://blog.brentlaabs.com/feeds/6687406856608519456/comments/default\x22 /\x3e\n', 'meTag': '', 'adsenseHostId': 'ca-host-pub-1556223355139109', 'adsenseHasAds': false, 'adsenseAutoAds': false, 'boqCommentIframeForm': true, 'loginRedirectParam': '', 'view': '', 'dynamicViewsCommentsSrc': '//www.blogblog.com/dynamicviews/4224c15c4e7c9321/js/comments.js', 'dynamicViewsScriptSrc': '//www.blogblog.com/dynamicviews/cbe0cd4e6298c445', 'plusOneApiSrc': 'https://apis.google.com/js/platform.js', 'disableGComments': true, 'interstitialAccepted': false, 'sharing': {'platforms': [{'name': 'Get link', 'key': 'link', 'shareMessage': 'Get link', 'target': ''}, {'name': 'Facebook', 'key': 'facebook', 'shareMessage': 'Share to Facebook', 'target': 'facebook'}, {'name': 'BlogThis!', 'key': 'blogThis', 'shareMessage': 'BlogThis!', 'target': 'blog'}, {'name': 'X', 'key': 'twitter', 'shareMessage': 'Share to X', 'target': 'twitter'}, {'name': 'Pinterest', 'key': 'pinterest', 'shareMessage': 'Share to Pinterest', 'target': 'pinterest'}, {'name': 'Email', 'key': 'email', 'shareMessage': 'Email', 'target': 'email'}], 'disableGooglePlus': true, 'googlePlusShareButtonWidth': 0, 'googlePlusBootstrap': '\x3cscript type\x3d\x22text/javascript\x22\x3ewindow.___gcfg \x3d {\x27lang\x27: \x27en\x27};\x3c/script\x3e'}, 'hasCustomJumpLinkMessage': false, 'jumpLinkMessage': 'Read more', 'pageType': 'item', 'postId': '6687406856608519456', 'pageName': 'Implementing IO::Path in Rakudo', 'pageTitle': 'Irregular Expression: Implementing IO::Path in Rakudo'}}, {'name': 'features', 'data': {}}, {'name': 'messages', 'data': {'edit': 'Edit', 'linkCopiedToClipboard': 'Link copied to clipboard!', 'ok': 'Ok', 'postLink': 'Post Link'}}, {'name': 'template', 'data': {'name': 'Watermark', 'localizedName': 'Watermark', 'isResponsive': false, 'isAlternateRendering': false, 'isCustom': false, 'variant': 'birds', 'variantId': 'birds'}}, {'name': 'view', 'data': {'classic': {'name': 'classic', 'url': '?view\x3dclassic'}, 'flipcard': {'name': 'flipcard', 'url': '?view\x3dflipcard'}, 'magazine': {'name': 'magazine', 'url': '?view\x3dmagazine'}, 'mosaic': {'name': 'mosaic', 'url': '?view\x3dmosaic'}, 'sidebar': {'name': 'sidebar', 'url': '?view\x3dsidebar'}, 'snapshot': {'name': 'snapshot', 'url': '?view\x3dsnapshot'}, 'timeslide': {'name': 'timeslide', 'url': '?view\x3dtimeslide'}, 'isMobile': false, 'title': 'Implementing IO::Path in Rakudo', 'description': 'I started off implementing the File::Spec module for Perl 6, as explained in the last blog post , but what I really wanted to do was to get ...', 'url': 'http://blog.brentlaabs.com/2013/05/implementing-iopath-in-rakudo.html', 'type': 'item', 'isSingleItem': true, 'isMultipleItems': false, 'isError': false, 'isPage': false, 'isPost': true, 'isHomepage': false, 'isArchive': false, 'isLabelSearch': false, 'postId': 6687406856608519456}}]); _WidgetManager._RegisterWidget('_NavbarView', new _WidgetInfo('Navbar1', 'navbar', document.getElementById('Navbar1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_HeaderView', new _WidgetInfo('Header1', 'header', document.getElementById('Header1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogView', new _WidgetInfo('Blog1', 'main', document.getElementById('Blog1'), {'cmtInteractionsEnabled': false, 'lightboxEnabled': true, 'lightboxModuleUrl': 'https://www.blogger.com/static/v1/jsbin/2223122975-lbx.js', 'lightboxCssUrl': 'https://www.blogger.com/static/v1/v-css/1964470060-lightbox_bundle.css'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogArchiveView', new _WidgetInfo('BlogArchive1', 'sidebar-right-1', document.getElementById('BlogArchive1'), {'languageDirection': 'ltr', 'loadingMessage': 'Loading\x26hellip;'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_ProfileView', new _WidgetInfo('Profile1', 'sidebar-right-1', document.getElementById('Profile1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_AttributionView', new _WidgetInfo('Attribution1', 'footer-3', document.getElementById('Attribution1'), {}, 'displayModeFull')); </script> </body> </html>