Posts for Saturday, July 24, 2010

Paludis 0.50.0 Released

Paludis 0.50.0 has been released:

  • We now require a compiler supporting various C++0x features, such as GCC 4.4 or later.
  • format=”exheres” and format=”ebuild” are now known as format=”e”. The old format names remain valid and are not yet deprecated.
  • Arguments in the form -X0 are now parsed as -X 0 rather than -X -0.
  • ‘cave execute-resolution’ now allows a single fetch job and a single execute job to be executed in parallel.
  • ‘cave update-world’ now displays whether or not world is changed.
  • ‘cave execute-resolution’ headings and summaries now include information on the versions being replaced, where appropriate
  • ‘cave resolve’ will now select weakly masked packages rather than being unable to decide. Such packages must be manually unmasked before the resolution can proceed.

Filed under: paludis releases Tagged: paludis
avatar

Unmerge Kde in gentoo

Update

emerge --ask -C `eix -C kde-base --only-names --installed`

nano /etc/make.conf

and delete kde from the USE flags, remove qt3support and qt4 also if you don’t need them anymore

emerge -uavtqND world"

Now you should have just a few kde packages in the list you got as an output of that command, it’s your choice to leave them or unmerge them manually, with unmerge -pv –depclean <atom> you can see what packages pulls in your atom.

avatar

WIPUP 25.07.10 beta released.

What began as a project motivated by the Open Collaboration Services API has really come a long way since it began as a concept submission to KDE’s openDesktop competition. This project was a unique concept for people to share and record what they were working on. Not about showcasing your latest creation – no, rather it is about showcasing the processes behind it: the different ideas, the development, and things that didn’t quite work out in the end. This project is for people who make stuff. People who constantly have ideas bouncing around, juggle their time between various projects and start more than they finish. This project is called WIPUP. WIPUP is a way to conveniently share, critique and track progress on your projects.

WIPUP attained an important milestone today – its beta release. It’s now available for the public to use. WIPUP is a "web 2.0" technology application, to use the cliche term. However more importantly it’s the infrastructure behind and towards a unique Social Desktop tool. For those unfamiliar with what the Social Desktop embodies, allow me to quote:

[The] core idea of the Social Desktop is to connect to your peers in the community, making sharing and exchanging knowledge easier to integrate into applications and the desktop itself. The concept behind the Social Desktop is to bring the power of online communities and group collaboration to desktop applications and the desktop shell itself.

WIPUP is (in terms of this final goal) still in its infancy – there is no desktop client (yet), my plans for KDE integration are still on the drawing board, and no currently existing API implementation. But more important is what does exist, which is the tool – the platform behind all of these future possible interfaces which provides added convenience and flexibility towards any workflow. As such, I’m immensely happy to share this beta with all of you and invite you all to check it out and start using it. WIPUP is also open source and free software – so any interested developers (or anybody wanting to contribute) are welcome to join as well!

Related posts:

  1. WIPUP 21.02.10 released and out in the wild.
  2. WIPUP 27.06.10a released!
  3. WIPUP 19.03.10a released!

Posts for Friday, July 23, 2010

avatar

Linux Sea sources online, cvechecker still in development

First of all, I’ve put the sources for Linux Sea online at GitHub. Not only does that safeguard any latest changes from not hitting my backup in time before my laptop dies (it’s terminal, but I can’t let him go yet ;-) but it also allows people who want to help with it (or translate it) to pull in the sources.

Note that it is still not finished (no spelling and grammar check done yet, still need to add some exercises, etc); once it is, I will tag the sources appropriately.

On the cvechecker state, it is also still under development, but progress is going nicely. Most of the work now is in updating the versions.dat file with information on how to obtain the current version of a package/tool. It is an easy activity – most of the work is in finding out how CVE entries would label a tool (what vendor and product name would be chosen) and because I am too lazy, I am currently only adding those that already have CVE entries assigned to them (so I can just take a look at the correct values).

It is also my first attempt at using autotools. Quite some overkill for such a small project, but why not. At least it allows me to try to do some new things here ;-)

Sun/Oracle Ultra 27 Workstation Discontinued?

I just noticed that the Sun/Oracle Ultra 27 is no longer listed on the Desktops section of Oracles products page.  This is a shame because I’m quite pleased with mine.

This sends a couple of messages:

  1. Oracle doesn’t think Solaris/OpenSolaris is viable on the workstation
  2. Oracle can’t deliver low margin hardware (the prices on these boxes skyrocketed after the acquisition)

It could be a purge while they bump to a new model featuring 6-core Xeons.  Yet more than likely, another victim of the merger.

Share and Enjoy: Digg del.icio.us Slashdot Facebook Reddit StumbleUpon Google Bookmarks FSDaily Twitter email Print PDF

Related posts:

  1. Sun Ultra 27 Review – The Ultimate Linux Workstation Sun Microsystems has a powerful and favorably priced entry in...
  2. Biting the Hand That Feeds Granted, I’m not a Groklaw junkie. Lawsuits are the epitome...
  3. Java: The Good Parts A while back, a book entitled JavaScript: The Good Parts...

Posts for Thursday, July 22, 2010

Paludis 0.48.6 Released

Paludis 0.48.6 has been released:

  • If the user explicitly specified an option for the special ‘test’ flag, this would override the build_options magic. This is now fixed.
  • Certain pkg_ functions are no longer skipped for 0-based EAPIs, to work around badly behaving Gentoo ebuilds.
  • If ‘cave show’ displays a masked package, it will now pick a version that is easy to unmask over a higher version that cannot be unmasked.
  • New ‘cave verify’ subcommand for checking whether installed packages have been modified.

Filed under: paludis releases Tagged: paludis
avatar

qemu-user: Turning your dual-core 2.6GHz processor into a dual-core 500MHz processor

I just discovered qemu-user, thanks to lu_zero on #gentoo-embedded. It’s brilliant. They took QEMU’s ability to simulate an entire processor, tossed out the other hardware emulation, and packaged it up. qemu-user’s primary use is for running foreign binaries.

So far, I’ve been running all my stage generation for Neuvoo on the BeagleBoard, which has a 500MHz armv7a processor. Everything compiles very slowly, but at least it’s native and stable.

There’s a couple problems with that setup. First, everything is running off an SDHC card. We’re talking slow. Second, the beagle has 128M of RAM, and after that it’s using the swapfile. Which is on the SDHC card. Now we’re really talking slow.

qemu-user deftly solves these problems. Of course, running stage generation on my hefty 2.6GHz Core2 Duo processor is going to slightly speed up compiling, just because of the dual cores. But not only that: now I have a huge, fast 7200rpm hard-drive to run everything off of, and 4G of RAM (2 of which is almost always cache) to run it all within.

Think of it like this: qemu-user enables you to run a chroot (and possibly other things) for almost any processor on a system of your choice, as though it were running natively.

Setting up qemu-user is a piece of cake, so I won’t go into details here. Just follow these instructions. One thing I didn’t get at first was why they use “USE=static”. The reason is this: if you’re going to be running an armv7a chroot, you need something inside the chroot to be translating (qemu-user). The problem is, that something can’t link dynamically against any libraries, because they’ll all be armv7a libraries. Static linking means the binary is self-contained and portable.

Let me know if there are other cool uses for qemu-user. I’m all ears. :)


Posts for Wednesday, July 21, 2010

3rd and 4th meeting of FSFE Fellowship group Slovenia

The 3rd meeting our Fellowship group was on the 4th of March and was mainly about organizing the DFD. You can read the full minutes (in Slovenian) on the wiki.

The next — 4th — meeting of FSFE Fellowship group Slovenia took place on the 6th of July and although there was only five of us present, it was pretty important.

  1. Money Refund — we divided the money we got refunded for the DFD from the FSFE.
  2. Structure of the FSFE & Fellowship — I explained what I learnt about the structure of the Fellowship and the FSFE and how they relate to each other to others. Since only those who donate to the FSFE are formally Fellows, strictly speaking most people on our mailing list and participating in our meetings aren't Fellows. But since our Fellowship mailing list, meetings and actions are open to anyone, that doesn't bother anyone really. The main thing is that stuff gets done.
  3. Plans for the Near Future — The general vibe is that we would need to be more vocal about emerging privacy and IPR problems and for that that we need more effective communication channels with the outside world. There was a debate whether and how much we should concentrate on Windows/Apple tax and/or si2010.
  4. Regular Meetings — We plan to have regular (probably monthly) meetings on a fixed date in the future. We will discuss the exact date after the summer vacations.
  5. Censured SourceForge — Rok Papež explained that sf.net follows the US embargo and in general disables downloading of free software in certain countries. This of course goes against the basic ideas of free software. Afterwards a short debate arose on the mailing list as well [start of thread]
  6. Digital Agenda, Internet Censorship in the EU — After that I explained a bit about what's happening in the EU concerning censorship (e.g. access to all internet search terms) and the Digital Agenda.
  7. ACTA — A short introduction and promise to post short and informative links to the mailing list on what problem we face with ACTA.
  8. Better Communication — We all felt that to achieve anything we need better means of communication with the outside world. One of the problems is that for (mainstream) media a wiki page and planet of blogs is not good enough. Apart from the guerilla approach — blog, microblog, mailing lists, social networks etc., a solution would be to make a website with its own domain name where we could post our press releases. We are also planning to cooperate even better with other similar-minded groups on activities that are of interest to both.

    There was also a debate whether local portals where citizens can submit suggestions to the government and to the EU could be of use. Milan Lazarevič commented that in theory the idea of participation via e-government is good, but from his experience in practice it's not worth the time. We'll still keep an eye on it though.

  9. Misc. — general chitchat while sipping coffee and juice.

As always the full minutes (in Slovenian) are available on our Fellowship group's wiki page.

Side note: for the past few months the number of subscribers to our mailing list has pretty much stabilised itself to a little over 60.

hook out >> eating chocolate pudding and going to bed...
<!--break-->

Clojure syntax highlighting via SyntaxHighlighter

How do you syntax-highlight Clojure code for display on a website? The best way I can find is SyntaxHighlighter.

Daniel Gómez wrote a brush to give SyntaxHighlighter Clojure support. I tweaked it a bit myself and integrated it into cow-blog. I also converted my favorite color scheme to a SyntaxHighlighter theme. So when I write this code:

(defn- ip
  "Given a request, return the IP.  Looks for an x-forwarded-for
  header, falls back to :remote-addr on the request."
  [request]
  (or (get-in request [:headers "x-forwarded-for"])
      (request :remote-addr)))

You should see something like this:

Syntax highlighting example

...unless you're reading this via RSS, or in a browser without Javascript enabled, in which case you'll see plain, depressing black and white. But that's one nice thing about SyntaxHighlighter. It degrades nicely.

One bad thing about SyntaxHighlighter is that it doesn't play nicely with Markdown. Or rather, Markdown isn't powerful enough to let you specify the class of any markdown-generated HTML tags. If you want the <pre class="brush: clojure"> that SyntaxHighlighter requires, you have to write out the HTML by hand. But I hacked Showdown a bit to let me specify classes more easily, so I can avoid having to do that.

The code for all of this is on github with the rest of my blog.

cygwin essentials

This isn’t really appropriate for a blog entry, because it’s bound to be updated over and over, but I need a place to keep these notes.

Essential packages (not including pre-selected):

  • xinit. Effectively what is called Cygwin/X. (Creates a new shortcut in the start menu called XWin Server that you probably should stick in your startup list.) With this you can run gvim, xterm etc.
  • file
  • git, gitk
  • openssh
  • ping
  • python
  • rsync
  • vim, gvim
  • wget
  • zip
  • make/patch

UPDATE: Since cygwin doesn’t ship a decent term, you absolutely need puttycyg.

Paludis 0.48.5 Released

Paludis 0.48.5 has been released:

  • xattr support was previously looking for attr/xattr.h rather than sys/xattr.h, and so was not being detected on glibc systems without attr installed. This is now corrected.
  • On some systems, the Python bindings were giving dlopen related warnings due to the linker trying to be too clever. This is now worked around.
  • New cave subcommands for scripting: print-id-actions, print-id-masks, print-repository-metadata, print-set.

Filed under: paludis releases Tagged: paludis

enabling VT no longer chrases X on my hp elitebook 8530w

motivation

i always wanted to use VirtualBox with 64bit guests. but this did not work for a very long time. i guess this has something to do with that Quadro FX 770M card.
lspci
01:00.0 VGA compatible controller: nVidia Corporation G96M [Quadro FX 770M] (rev a1)
so on which architecture is my host OS?
arch
x86_64

what were the previous problems?

i’ve always experimented with the bios setting, on my hp bios this is called: “Virtualization Technology” and it can either be enabled or disabled. but for various versions of x11-drivers/nvidia it:

  • did not start X with an error message in /var/log/Xorg.0.log
  • crash the computer with a black screen (and more than average but constant fan noise)

however it was always able to use the xorg driver called ‘nv’ to use the system. only the proprietary nvidia module did not work as expected.

here is a list of driver versions i’ve experimented with (which did NOT work):

  • x11-drivers/nvidia-drivers-180.27
  • x11-drivers/nvidia-drivers-180.29
  • x11-drivers/nvidia-drivers-180.44
  • x11-drivers/nvidia-drivers-180.51
  • x11-drivers/nvidia-drivers-180.60
  • x11-drivers/nvidia-drivers-185.18.36
  • x11-drivers/nvidia-drivers-185.18.36-r1
  • x11-drivers/nvidia-drivers-190.42-r3
  • x11-drivers/nvidia-drivers-190.53
  • x11-drivers/nvidia-drivers-190.53-r1
  • x11-drivers/nvidia-drivers-195.36.24

what did fix it?

installing x11-drivers/nvidia-drivers-256.35 finally fixed it!

what does work?

  • 3d game, so 3d acceleration support is there
  • 2d acceleration does seem to work as well
  • pm-suspend (hibernate-ram) seems to work as well
  • using VirtualBox with a 64 bit image (enabling System->Acceleration->Hardware Virtualization [x] Enable VT-X/AMD-V)
  • mode switch to consoles (alt+f1 to alt+f12) does work and i can see it actually (no black screen)

i hope this driver is somehow stable. currently it’s hardmasked in gentoo.

update: fr 23 jul

using x11-drivers/nvidia-drivers-256.35 did break suspend’s resume with several crashes. i’ve disabled VT in the bios and now i’m using the latest stable release.


Posts for Monday, July 19, 2010

Paludis 0.48.4 Released

Paludis 0.48.4 has been released:

  • ‘cave uninstall’ now accepts wildcards.
  • ‘cave purge’ and ‘cave resolve’ will no longer attempt to purge things that cannot be uninstalled.
  • ‘cave show -t’ now has short options for values.
  • We now tolerate ebuilds that try to output things or die in global scope.

Filed under: paludis releases Tagged: paludis

Posts for Sunday, July 18, 2010

avatar

When True Is Not True Anymore

We all know that accessing uninitialized variables in C and C++ usually leads to some kind of undefined behavior one usually wants to avoid. What I didn’t know until recently is that uninitialized bool values might be especially malicious beasts. To see what they can do to you, take a look at the following program, and try to predict its output:

#include <string>
#include <iostream>

using namespace std;

namespace {

    inline string stringify(const bool value)
    {
        return (value ? "true" : "false");
    }

    struct Struct
    {
        long l;
        bool u;
    };
}

int main()
{
    Struct s;

    if(true != s.u)
        cout << stringify(true) << " != " << stringify(s.u) << endl;
}

Now type g++ stringify.cc -o stringify and run the generated executable. Here is what you might see on some platforms:

$ ./stringify
true != true

Yes, you got that right true != true! I get this behaviour with g++-4.3 and g++-4.4 on Gentoo (x86 and x86_64) as well as with g++-4.1 and g++-4.4 on Ubuntu 9.10 (x86_64). Before attempting to explain what happened here, I want to summarize a few additional facts:

  • Several attempts to make this program shorter without ending up with something boring failed.
  • Turning on optimization causes g++ optimize all if statements away (especially the implicit one in stringify) under the assumption that s.u is false, which again leads to a much more sane output.
  • I could not reproduce this with icc-11.1.

So, what happened? Is g++ broken? Actually the answer is no. Accessing uninitialized memory leads to undefined behavior, and undefined means undefined. In fact the C++0x Final Committee Draft contains a footnote that explicitly mentions the oddity we have just seen:

47) Using a bool value in ways described by this International Standard as “undefined,” such as by examining the value of an
uninitialized automatic object, might cause it to behave as if it is neither true nor false.

This is not that surprising if one considers that at assembler level, a bool is not represented by a single bit, but at least by a byte. An uninitialized byte might have 256 different values, and not just two. One could of course consistently map 0 to false and everything else to true, but this is not what g++ does. To see what I mean, take a look at the following assembler snippet, that g++ generated for the if statement in line 24:

movzbl  -40(%rbp), %eax  # move s.u to eax.
xorl    $1, %eax         # xor eax with 1.
testb	%al, %al         # check if the low byte of eax is 0.
je      .L8              # jump to .L8 if so.

If the jump is taken, the body of the if statement in line 24 is skipped, otherwise it is executed. Now the xorl in line 2 switches the lowest bit in eax, leaving all other bits unchanged. Therefore s.u is considered to be equal to true if and only if it has the byte value 0×01.

Now lets take a look at the assembler that represents the ternary operator in stringify:

cmpb $0, -36(%rbp)    # compare the argument with 0.
je   .L2              # jump to .L2 if the argument is 0.
movl $.LC0, %eax      # store "true" in eax.
jmp  .L3              # jump out.
.L2:
     movl $.LC1, %eax # store "false" in eax.
.L3:

Here g++ maps 0 to false and every other value to true. This means that if the actual byte value of s.u is for example 0xFF (which for some reason is what cgdb keeps telling me locally), the if in line 24 will be taken as if s.u was false, but stringify will behave as if s.u was true.


avatar

EyeOS has an Oxygen theme!

For the uninitiated, EyeOS is a free, open-source desktop implementation right in the browser. I was recently playing around with my EyeOS installation that I forgot I had installed a while back (v1.x) and like what most people do when they try out a new system, I decided to see what other themes they have.

Turns out they have an Oxygen theme! It’s a little dated but I must say I’m impressed. Very impressed.

Now all I have to do is find a practical personal use of EyeOS! Perhaps it might replace a few of my cobbled series of other cloudish hacks.

Related posts:

  1. Plans for E2-Productions.com to turn into a personal cloud?
  2. WIPUP: Created my own KDE Plasma theme called Plix

avatar

6 years! + grrbl news

It’s been exactly 6 years (!) since I started this blog: http://www.void.gr/kargig/blog/2004/07/18/hey-ho-lets-go/

Too bad I don’t have as much free time as I had in the past to post about interesting things. It’s a also a time for a redesign…I think I have the same theme for more than 5 years…

Anyway, GrRBL got redesigned yesterday by Christine and now there’s also a submission form for those who prefer it over forwarding emails. I also merged my other Greek spammers email addresses blacklist with lists by a couple of friends (postmasters) and now the list contains over 300 unique, verified, spammer addresses. This list is not yet public but if you are interested to use it and test it, give me a shout and I’ll give you access.

Posts for Saturday, July 17, 2010

"mini-manifesto"

Over the last few days, I've incorporated configurable compression format support into Metro, and I am now creating Funtoo stages using the .xz compression format (these patches are in git, and not yet in an official Metro release.) On the mirrors, this is resulting in a very nice 40% size decrease over bzip2, with stage3's weighing in at around 95MB.

But it gets better. Our old git-based Portage snapshots compressed with bzip2 used to weigh in at a whopping 413MB. By migrating to mini-Manifests, xz compression and adding some additional Funtoo optimizations, our Portage tree snapshot files are now weighing in at a svelte 25MB :)

That's 6% of their original size, or about 16x smaller - more than an order of magnitude difference. This will make the snapshot download go a lot faster - and in the coming months I hope to be able to reduce the Portage snapshot size even further, both in .xz form and on-disk, uncompressed. Stay tuned.

ANTLR via Clojure

ANTLR is a parser-generator for Java. Can you use it from Clojure? Sure. Would you want to? Maybe.

Here's how to do it, start to finish.

For the impatient among you, all of the code below is on github.

git clone git://github.com/briancarper/clojure-antlr-example.git

Setup

I'm going to use leiningen for this project. Let's make a new project called antlr-example.

$ lein new antlr-example

Now edit project.clj to tell lein to fetch ANTLR (and Swank, since I use Emacs). ANTLR is available in Maven Central, so leiningen can grab it for us.

(defproject antlr-example "1.0.0-SNAPSHOT"
  :description "ANTLR Clojure example"
  :dependencies [[org.clojure/clojure "1.2.0-beta1"]
                 [org.clojure/clojure-contrib "1.2.0-beta1"]
                 [org.antlr/antlr "3.2"]]
  :dev-dependencies [[swank-clojure "1.3.0-SNAPSHOT"]])

Then a simple lein deps will download all of the jars and all of ANTLR's dependencies for you. Handy. You end up with this:

antlr-example
├── classes
├── lib
│   ├── antlr-2.7.7.jar
│   ├── antlr-3.2.jar
│   ├── antlr-runtime-3.2.jar
│   ├── clojure-1.2.0-beta1.jar
│   ├── clojure-contrib-1.2.0-beta1.jar
│   ├── stringtemplate-3.2.jar
│   └── swank-clojure-1.3.0-20100502.112537-1.jar
├── project.clj
├── README
├── src
│   └── antlr_example
│       └── core.clj
└── test
    └── antlr_example
        └── core_test.clj

Interactive development? Kind of...

One weakness of ANTLR via Clojure is that development is no longer REPL-based. The ANTLR workflow is essentially a Java workflow:

  1. Write a grammar
  2. Compile the grammar into Java source code
  3. Compile the Java source into Java .class files

Until and unless someone writes a Clojure backend for ANTLR, so that ANTLR can directly produce Clojure source code (and it seems like it should be possible to do so), you're back to a write/compile/debug cycle. Joy!

This also means restarting your REPL every time you alter and recompile your grammar.

But the good thing is that there's ANTLR Works, a free GUI for writing ANTLR grammars. ANTLR Works has an interactive interpreter, which is nice, and it has a compiler/debugger, which is better. These let you test your grammar as you write it, by trying inputs and seeing the resulting AST in graphical form, which is as good as you could hope for. This is actually even a bit nicer than a plaintext REPL.

Plus, it has a nice editor for ANTLR code. Emacs was freaking out over the indentation on a few grammars I tried.

So a decent workflow might be to write and debug your grammar in ANTLR Works, then fire up Clojure afterwards to consume the parser.

A simple grammar

We're going to use a classic textbook example, simple arithmetic expressions, as a proof of concept.

For Java (hence Clojure) to find this code, it has to be named properly and it has to be put into the proper directory on CLASSPATH. I'm going to create the grammar file src/antlr_example/Expr.g. (My grammar file, Java source, and Clojure source will all be jumbled together in src. You can easily do it differently if this offends your sensibilities.)

The first line in the grammar names the grammar (and the classes we'll consume from Clojure):

grammar Expr;

Clojure being a s-exp based language, it might be nice to have ANTLR generate an AST. ANTLR has good support for this:

options {
    output=AST;
    ASTLabelType=CommonTree;
}

Helper tokens for the grammar:

tokens {
    PLUS = '+';
    MINUS = '-';
    MULT = '*';
    DIV = '/';
}

These next lines are important. We want to generate classes antlr_example.ExprLexer and antlr_example.ExprParser, so we have to set our code up in the proper package, antlr_example.

This code includes both a parser and lexer, so you have to set the package for both in ANTLR.

@header        {package antlr_example;}
@lexer::header {package antlr_example;}

And then the grammar itself, which is simple:

expr: term ((PLUS | MINUS)^ term)* ;
term: factor ((MULT | DIV)^ factor)* ;
factor: INT ;

INT :   '0'..'9'+ ;
WS  :   ( ' ' | '\t' | '\r' | '\n')+ {$channel=HIDDEN;} ;

One thing to note is (PLUS | MINUS)^; the ^ here tells ANTLR to treat (PLUS | MINUS) as the parent of the node created for this rule. This means the term on either side will be children of this node. We do the same for MULT and DIV. This will give us a nice tree. Otherwise you end up with a flat list of tokens.

{$channel=HIDDEN;} for the whitespace rule tells ANTLR to ignore whitespace, also useful.

If you use ANTLR Works with this grammar, on the input 1 + 2 * 3 + 4, you can see that it works OK.

ANTLR

That was an awful lot of boilerplate and setup and ugly syntax though. See how much Clojure spoils you? But for a simple grammar, it's not that much code.

For a longer or more complex grammar, you might end up having to embed inline Java code into your ANTLR grammar. But again, that's not the end of the world.

Compile everything

Once you write your grammar (presumably in ANTLR Works), you can generate the Java code and compile it thusly (run from your project's base directory):

$ java -cp 'lib/*' org.antlr.Tool src/antlr_example/Expr.g
$ javac -cp 'lib/*' -d classes src/antlr_example/Expr{Lexer,Parser}.java

The first command should generate src/antlr_example/ExprLexer.java and src/antlr_example/ExprParser.java.

The second command should spew tons of class files into classes/. Leiningen and other tools generally include classes/ on your CLASSPATH, so that's a good place for them. Again putting them here is a fairly arbitrary decision on my part. You can put the source and class files anywhere, as long as the class files end up on your CLASSPATH when you start Clojure.

If you're really so lazy that you can't run these two commands, you could use ANTLR Works' built-in commands for compiling your grammar. The only way I could find to invoke it was by starting a Debug session, which compiled my code as a side-effect. And it spews files into places I don't want them, so I like the command line version better.

You could probably also set up a leiningen task for this if you're re-running these commands a lot, or configure your build tool of choice to do the same. I didn't bother.

Now (re)start your REPL and let's write some Clojure.

Clojure code

It's pretty easy to consume an ANTLR parser from Clojure. I'm putting the code below into src/antlr_example/core.clj

First import the classes. You probably need some ANTLR helper classes to set up the parser; you also need the classes you just generated.

(ns antlr-example.core
  (:import (org.antlr.runtime ANTLRStringStream
                              CommonTokenStream)
           (antlr_example ExprLexer ExprParser)))

Here's a function to parse a string using our new parser class:

(defn parse-expr [s]
  (let [lexer (ExprLexer. (ANTLRStringStream. s))
        tokens (CommonTokenStream. lexer)
        parser (ExprParser. tokens)]
    (.expr parser)))

.expr is the name of the top-level rule we want to invoke from our grammar. The rest is boilerplate; just plug in the proper class names.

So let's see what we've got.

user> (in-ns 'antlr-example.core)
#<Namespace antlr-example.core>
antlr-example.core> (parse-expr "1 + 2 * 3 + 4")
#<expr_return antlr_example.ExprParser$expr_return@3755e508>

Er, OK. A good way to inspect Java objects is via bean, so let's view the guts of this object:

antlr-example.core> (bean *1)
{:tree #<CommonTree +>,
 :template nil,
 :stop #<CommonToken [@12,12:12='4',<8>,1:12]>,
 :start #<CommonToken [@0,0:0='1',<8>,1:0]>,
 :class antlr_example.ExprParser$expr_return}

I see. We set up our grammar above to generate an AST, so :tree on the bean will give us that. This translates to .getTree on the Java object. So we can edit our function to call this for us, since we always want to have the tree.

(defn parse-expr [s]
  (let [lexer (ExprLexer. (ANTLRStringStream. s))
        tokens (CommonTokenStream. lexer)
        parser (ExprParser. tokens)]
    (.getTree (.expr parser))))

Now:

antlr-example.core> (parse-expr "1 + 2 * 3 + 4")
#<CommonTree +>

OK, it appears we have one node of the tree, the root node. Let's peak inside:

antlr-example.core> (bean *1)
{:children #<ArrayList [+, 4]>,
 :childIndex -1,
 :parent nil,
 :text "+",
 :nil false,
 :token #<CommonToken [@10,10:10='+',<4>,1:10]>,
 :class org.antlr.runtime.tree.CommonTree,
 :ancestors nil,
 :tokenStartIndex 0,
 :type 4,
 :childCount 2,
 :charPositionInLine 10,
 :tokenStopIndex 12,
 :line 1}

Looks like our children are in :children on the bean, which translates to .getChildren on the Java object. And we have .getChildCount to count the children, and .getText to get the text of the current node. (We could've learned all of this by reading the javadocs for ANTLR too, but what fun is that?)

Since we're dealing with a tree, we can use tree-seq in Clojure to get a flat list of all the tokens in our text.

tree-seq takes three arguments. First a function that returns true if the node has children, false otherwise. Second a function that returns the children for a node that has children. Third the root node of our tree.

That'll give us a seq of tree nodes. So finally we call .getText on all the resulting nodes in the list, to turn node objects into strings.

Easy enough:

(defn node-seq [x]
  (map #(.getText %)
   (tree-seq #(not (zero? (.getChildCount %)))
             #(.getChildren %)
             x)))

antlr-example.core> (node-seq (parse-expr "1 + 2 * 3 + 4"))
("+" "+" "1" "*" "2" "3" "4")

But that's no good. We lost our tree structure.

We'd rather have something like nested vectors or lists. It's easy enough to roll something by hand. This should do it:

(defn AST [node]
  (if (zero? (.getChildCount node))
    (.getText node)
    (let [children (map AST (.getChildren node))
          txt (.getText node)]
      (if txt
        (cons txt children)
        children))))

antlr-example.core> (AST (parse-expr "1 + 2 * 3 + 4"))
("+" ("+" "1" ("*" "2" "3")) "4")

That's better, but it's a list of strings. These strings all happen to be literal representations of Clojure objects, so a call to read-string in the proper places should give us something we can work with:

(defn AST [node]
  (if (zero? (.getChildCount node))
    (read-string (.getText node))
    (let [children (map AST (.getChildren node))
          txt (read-string (.getText node))]
      (if txt
        (apply list txt children)
        children))))

antlr-example.core> (AST (parse-expr "1 + 2 * 3 + 4"))
(+ (+ 1 (* 2 3)) 4)

Hey look, now we have something we can evaluate.

antlr-example.core> (eval *1)
11

Yeah I kind of planned that ahead. If you had a more complex grammar, you might not get away with something quite this easy.

Conclusion

The advantage of ANTLR is that it's mature, widely used, actively developed, and well-documented. (There's a whole book about ANTLR, The Definitive ANTLR Reference by Terence Parr.) There's also a lot of tooling available for it, not just ANTLR Works, but plugins for other Java IDEs.

The disadvantage is that it's a Java library, and as always, there will be some friction when consuming it from Clojure. But it's not that bad.

For pure-Clojure parser generator alternatives, there are fnparse and clojure-pg, neither of which I've tried much.

Posts for Thursday, July 15, 2010

Generic Lambda Visitors, or Writing Haskell in C++0x (Part 4)

In part three, we built a generic alternative to visitors that used lambdas. Now we’ll do a few largely pointless but potentially interesting bits of trickery.

Previously, we worked out the return type of our when function by looking at the return type of the first lambda. Let’s get a bit more adventurous. We’ll make a class that works out what the return type should be.

template <typename... Funcs_>
struct WhenReturnType;

template <typename Val_, typename... Funcs_>
typename WhenReturnType<Funcs_...>::Type
when(Val_ && val, Funcs_ && ... funcs)
{
    LambdaVisitor<typename WhenReturnType<Funcs_...>::Type, Funcs_...> visitor(funcs...);
    return accept_returning<typename WhenReturnType<Funcs_...>::Type>(val, visitor);
}

For starters, we’ll say that if the first function returns void, our return type is void; otherwise, it’s an error:

template <typename... Funcs_>
struct WhenReturnType;

template <typename FirstFunc_, typename... Funcs_>
struct WhenReturnType<FirstFunc_, Funcs_...>
{
    typedef typename std::conditional<
        std::is_same<typename LambdaParameterTypes<FirstFunc_>::ReturnType, void>::value,
        void,
        UnknownTypeForOneOf
        >::type Type;
};

Let’s get a bit more adventurous. What if one lambda returns an int, and another returns a long? We could return a long. Similarly, if one returns a char, and another returns a double, we could return a double. And that’s what std::common_type does:

template <typename... Funcs_>
struct WhenReturnType;

template <typename FirstFunc_, typename... Funcs_>
struct WhenReturnType<FirstFunc_, Funcs_...>
{
    typedef typename std::conditional<
        std::is_same<typename LambdaParameterTypes<FirstFunc_>::ReturnType, void>::value,
        void,
        typename std::common_type<typename LambdaParameterTypes<Funcs_>::ReturnType ...>::type
        >::type Type;
};

Except, std::common_type isn’t useful for user defined types. What we really want to do is say “return a type that holds a value of any of the return types we could get”. If only we’d just spent the past few pages developing such a type…

First, though, let’s work out how to just return a value of a given type, if all the return types are the same:

template <typename... Funcs_>
struct AllReturnSame;

template <typename Func_>
struct AllReturnSame<Func_>
{
    enum { value = true };
};

template <typename A_, typename B_, typename... Funcs_>
struct AllReturnSame<A_, B_, Funcs_...>
{
    enum { value = std::is_same<typename LambdaParameterTypes<A_>::ReturnType, typename LambdaParameterTypes<B_>::ReturnType>::value &&
        AllReturnSame<B_, Funcs_...>::value };
};

template <typename... Funcs_>
struct WhenReturnType;

template <typename FirstFunc_, typename... Funcs_>
struct WhenReturnType<FirstFunc_, Funcs_...>
{
    typedef typename std::conditional<
        AllReturnSame<FirstFunc_, Funcs_...>::value,
        typename LambdaParameterTypes<FirstFunc_>::ReturnType,
        UnknownTypeForOneOf
        >::type Type;
};

Note that we no longer have to handle void specially here.

Next, we’ll add in handling for the easy case where all the lambdas return different things:

template <typename... Funcs_>
struct WhenReturnType;

template <typename FirstFunc_, typename... Funcs_>
struct WhenReturnType<FirstFunc_, Funcs_...>
{
    typedef typename std::conditional<
        AllReturnSame<FirstFunc_, Funcs_...>::value,
        typename LambdaParameterTypes<FirstFunc_>::ReturnType,
        OneOf<typename LambdaParameterTypes<FirstFunc_>::ReturnType, typename LambdaParameterTypes<Funcs_>::ReturnType...>
        >::type Type;
};

Again, note the complex expression used to the left of a ... operator.

Unfortunately, this barfs spectacularly if, say, two lambdas return an int and one returns a string. We need a way of removing the duplicates from a type list. It’s not possible to pass around two type lists directly, so we’ll first need a helper struct that stores all the types we’ve seen so far:

template <typename...>
struct SeenSoFar
{
};

Next, let’s have a helper that tells us whether a particular type is already in a SeenSoFar list:

template <typename...>
struct AlreadySeen;

template <typename Query_>
struct AlreadySeen<SeenSoFar<>, Query_>
{
    enum { value = false };
};

template <typename Query_, typename A_, typename... Rest_>
struct AlreadySeen<SeenSoFar<A_, Rest_...>, Query_>
{
    enum { value = std::is_same<Query_, A_>::value || AlreadySeen<SeenSoFar<Rest_...>, Query_>::value };
};

We also need to be able to create a new SeenSoFar with an extra type:

template <typename...>
struct ExtendSeenSoFar;

template <typename New_, typename... Current_>
struct ExtendSeenSoFar<New_, SeenSoFar<Current_...> >
{
    typedef SeenSoFar<Current_..., New_> Type;
};

Now we can put all that together:

template <typename...>
struct OneOfDeduplicatorBuilder;

template <typename... Values_>
struct OneOfDeduplicatorBuilder<SeenSoFar<Values_...> >
{
    typedef OneOf<Values_...> Type;
};

template <typename SeenSoFar_, typename Next_, typename... Funcs_>
struct OneOfDeduplicatorBuilder<SeenSoFar_, Next_, Funcs_...>
{
    typedef typename std::conditional<
        AlreadySeen<SeenSoFar_, Next_>::value,
        typename OneOfDeduplicatorBuilder<SeenSoFar_, Funcs_...>::Type,
        typename OneOfDeduplicatorBuilder<typename ExtendSeenSoFar<Next_, SeenSoFar_>::Type, Funcs_...>::Type
            >::type Type;
};

template <typename... Funcs_>
struct OneOfDeduplicator
{
    typedef typename OneOfDeduplicatorBuilder<SeenSoFar<>, Funcs_...>::Type Type;
};

And we can use it:

template <typename... Funcs_>
struct WhenReturnType;

template <typename FirstFunc_, typename... Funcs_>
struct WhenReturnType<FirstFunc_, Funcs_...>
{
    typedef typename std::conditional<
        AllReturnSame<FirstFunc_, Funcs_...>::value,
        typename LambdaParameterTypes<FirstFunc_>::ReturnType,
        typename OneOfDeduplicator<
            typename LambdaParameterTypes<FirstFunc_>::ReturnType,
            typename LambdaParameterTypes<Funcs_>::ReturnType ...>::Type
        >::type Type;
};

Letting us do horrible things like:

struct SomeType
{
};

int main(int, char *[])
{
    OneOf<int, std::string, SomeType> s(123);

    std::cout << when(
            when(s,
                [] (int & x)               -> std::string { return std::string(++x, 'x'); },
                [] (std::string & x)       -> int         { x.append(" spanker"); return x.length(); },
                [] (const SomeType &)      -> int         { return 42; }
                ),
            [] (const int x)           -> int { return x; },
            [] (const std::string & x) -> int { return x.length(); }
            ) << std::endl;

    return EXIT_SUCCESS;
}

You’ll note we’re explicitly specifying the return types. This is because std::string::length() probably doesn’t return an int, and our code does overload resolution on references, which allows conversions for classes but not integral types. Fixing this in a sane way is left as an easy exercise for the reader — one possibility is to see if the return types of all the lambdas have a std::common_type, and using that rather than a OneOf if they do. Implementing this is left to the reader; in the mean time, our code in full looks like:

#include <memory>
#include <type_traits>
#include <utility>

struct UnknownTypeForOneOf;

template <typename Want_, typename... Types_>
struct SelectOneOfType;

template <typename Want_>
struct SelectOneOfType<Want_>
{
    typedef UnknownTypeForOneOf Type;
};

template <typename Want_, typename Try_, typename... Rest_>
struct SelectOneOfType<Want_, Try_, Rest_...>
{
    typedef typename std::conditional<
        std::is_same<Want_, Try_>::value,
        Try_,
        typename SelectOneOfType<Want_, Rest_...>::Type
            >::type Type;
};

template <typename Type_>
struct ParameterTypes;

template <typename C_, typename R_, typename P_>
struct ParameterTypes<R_ (C_::*)(P_)>
{
    typedef P_ FirstParameterType;
    typedef R_ ReturnType;
};

template <typename C_, typename R_, typename P_>
struct ParameterTypes<R_ (C_::*)(P_) const>
{
    typedef P_ FirstParameterType;
    typedef R_ ReturnType;
};

template <typename Lambda_>
struct LambdaParameterTypes
{
    typedef typename ParameterTypes<decltype(&Lambda_::operator())>::FirstParameterType FirstParameterType;
    typedef typename ParameterTypes<decltype(&Lambda_::operator())>::ReturnType ReturnType;
};

template <typename Type_>
struct OneOfVisitorVisit
{
    virtual void visit(Type_ &) = 0;
};

template <typename... Types_>
struct OneOfVisitor :
    OneOfVisitorVisit<Types_>...
{
};

template <typename Visitor_, typename Underlying_, typename Result_, typename... Types_>
struct OneOfVisitorWrapperVisit;

template <typename Visitor_, typename Underlying_, typename Result_>
struct OneOfVisitorWrapperVisit<Visitor_, Underlying_, Result_> :
    Visitor_
{
    Underlying_ & underlying;
    std::function<Result_ ()> execute;

    OneOfVisitorWrapperVisit(Underlying_ & u) :
        underlying(u)
    {
    }
};

template <typename Visitor_, typename Underlying_, typename Result_, typename Type_, typename... Rest_>
struct OneOfVisitorWrapperVisit<Visitor_, Underlying_, Result_, Type_, Rest_...> :
    OneOfVisitorWrapperVisit<Visitor_, Underlying_, Result_, Rest_...>
{
    OneOfVisitorWrapperVisit(Underlying_ & u) :
        OneOfVisitorWrapperVisit<Visitor_, Underlying_, Result_, Rest_...>(u)
    {
    }

    Result_ visit_returning(Type_ & t)
    {
        return this->underlying.visit(t);
    }

    virtual void visit(Type_ & t)
    {
        this->execute = std::bind(&OneOfVisitorWrapperVisit::visit_returning, this, std::ref(t));
    }
};

template <typename Underlying_, typename Result_, typename... Types_>
struct OneOfVisitorWrapper :
    OneOfVisitorWrapperVisit<OneOfVisitor<Types_...>, Underlying_, Result_, Types_...>
{
    OneOfVisitorWrapper(Underlying_ & u) :
        OneOfVisitorWrapperVisit<OneOfVisitor<Types_...>, Underlying_, Result_, Types_...>(u)
    {
    }
};

template <typename... Types_>
struct OneOfValueBase
{
    virtual ~OneOfValueBase() = 0;

    virtual void accept(OneOfVisitor<Types_...> &) = 0;
    virtual void accept(OneOfVisitor<const Types_...> &) const = 0;
};

template <typename... Types_>
OneOfValueBase<Types_...>::~OneOfValueBase() = default;

template <typename Type_, typename... Types_>
struct OneOfValue :
    OneOfValueBase<Types_...>
{
    Type_ value;

    OneOfValue(const Type_ & type) :
        value(type)
    {
    }

    virtual void accept(OneOfVisitor<Types_...> & visitor)
    {
        static_cast<OneOfVisitorVisit<Type_> &>(visitor).visit(value);
    }

    virtual void accept(OneOfVisitor<const Types_...> & visitor) const
    {
        static_cast<OneOfVisitorVisit<const Type_> &>(visitor).visit(value);
    }
};

template <typename... Types_>
class OneOf
{
    private:
        std::unique_ptr<OneOfValueBase<Types_...> > _value;

    public:
        template <typename Type_>
        OneOf(const Type_ & value) :
            _value(new OneOfValue<typename SelectOneOfType<Type_, Types_...>::Type, Types_...>{value})
        {
        }

        OneOf(const OneOf & other) = delete;

        OneOf(OneOf && other) :
            _value(std::move(other._value))
        {
        }

        template <typename Type_>
        OneOf & operator= (const Type_ & value)
        {
            _value.reset(new OneOfValue<typename SelectOneOfType<Type_, Types_...>::Type, Types_...>{value});
            return *this;
        }

        OneOf & operator= (const OneOf & other) = delete;

        OneOf & operator= (OneOf && other)
        {
            _value = std::move(other._value);
            return *this;
        }

        OneOfValueBase<Types_...> & value()
        {
            return *_value;
        }

        const OneOfValueBase<Types_...> & value() const
        {
            return *_value;
        }
};

template <typename Visitor_, typename Result_, typename OneOf_>
struct OneOfVisitorWrapperTypeFinder;

template <typename Visitor_, typename Result_, typename... Types_>
struct OneOfVisitorWrapperTypeFinder<Visitor_, Result_, const OneOf<Types_...> &>
{
    typedef OneOfVisitorWrapper<Visitor_, Result_, const Types_...> Type;
};

template <typename Visitor_, typename Result_, typename... Types_>
struct OneOfVisitorWrapperTypeFinder<Visitor_, Result_, OneOf<Types_...> &>
{
    typedef OneOfVisitorWrapper<Visitor_, Result_, Types_...> Type;
};

template <typename Result_, typename OneOf_, typename Visitor_>
Result_
accept_returning(OneOf_ && one_of, Visitor_ && visitor)
{
    typename OneOfVisitorWrapperTypeFinder<Visitor_, Result_, OneOf_>::Type visitor_wrapper(visitor);
    one_of.value().accept(visitor_wrapper);
    return visitor_wrapper.execute();
}

template <typename OneOf_, typename Visitor_>
void accept(OneOf_ && one_of, Visitor_ && visitor)
{
    accept_returning<void>(one_of, visitor);
}

template <typename Result_, typename... Funcs_>
struct LambdaVisitor;

template <typename Result_>
struct LambdaVisitor<Result_>
{
    void visit(struct NotReallyAType);
};

template <typename Result_, typename Func_, typename... Rest_>
struct LambdaVisitor<Result_, Func_, Rest_...> :
    LambdaVisitor<Result_, Rest_...>
{
    Func_ & func;

    LambdaVisitor(Func_ & f, Rest_ & ... rest) :
        LambdaVisitor<Result_, Rest_...>(rest...),
        func(f)
    {
    }

    Result_ visit(typename LambdaParameterTypes<Func_>::FirstParameterType & v)
    {
        return func(v);
    }

    using LambdaVisitor<Result_, Rest_...>::visit;
};

template <typename... Funcs_>
struct AllReturnSame;

template <typename Func_>
struct AllReturnSame<Func_>
{
    enum { value = true };
};

template <typename A_, typename B_, typename... Funcs_>
struct AllReturnSame<A_, B_, Funcs_...>
{
    enum { value = std::is_same<typename LambdaParameterTypes<A_>::ReturnType, typename LambdaParameterTypes<B_>::ReturnType>::value &&
        AllReturnSame<B_, Funcs_...>::value };
};

template <typename...>
struct SeenSoFar
{
};

template <typename...>
struct ExtendSeenSoFar;

template <typename New_, typename... Current_>
struct ExtendSeenSoFar<New_, SeenSoFar<Current_...> >
{
    typedef SeenSoFar<Current_..., New_> Type;
};

template <typename...>
struct AlreadySeen;

template <typename Query_>
struct AlreadySeen<SeenSoFar<>, Query_>
{
    enum { value = false };
};

template <typename Query_, typename A_, typename... Rest_>
struct AlreadySeen<SeenSoFar<A_, Rest_...>, Query_>
{
    enum { value = std::is_same<Query_, A_>::value || AlreadySeen<SeenSoFar<Rest_...>, Query_>::value };
};

template <typename...>
struct OneOfDeduplicatorBuilder;

template <typename... Values_>
struct OneOfDeduplicatorBuilder<SeenSoFar<Values_...> >
{
    typedef OneOf<Values_...> Type;
};

template <typename SeenSoFar_, typename Next_, typename... Funcs_>
struct OneOfDeduplicatorBuilder<SeenSoFar_, Next_, Funcs_...>
{
    typedef typename std::conditional<
        AlreadySeen<SeenSoFar_, Next_>::value,
        typename OneOfDeduplicatorBuilder<SeenSoFar_, Funcs_...>::Type,
        typename OneOfDeduplicatorBuilder<typename ExtendSeenSoFar<Next_, SeenSoFar_>::Type, Funcs_...>::Type
            >::type Type;
};

template <typename... Funcs_>
struct OneOfDeduplicator
{
    typedef typename OneOfDeduplicatorBuilder<SeenSoFar<>, Funcs_...>::Type Type;
};

template <typename... Funcs_>
struct WhenReturnType;

template <typename FirstFunc_, typename... Funcs_>
struct WhenReturnType<FirstFunc_, Funcs_...>
{
    typedef typename std::conditional<
        AllReturnSame<FirstFunc_, Funcs_...>::value,
        typename LambdaParameterTypes<FirstFunc_>::ReturnType,
        typename OneOfDeduplicator<
            typename LambdaParameterTypes<FirstFunc_>::ReturnType,
            typename LambdaParameterTypes<Funcs_>::ReturnType ...>::Type
        >::type Type;
};

template <typename Val_, typename... Funcs_>
typename WhenReturnType<Funcs_...>::Type
when(Val_ && val, Funcs_ && ... funcs)
{
    LambdaVisitor<typename WhenReturnType<Funcs_...>::Type, Funcs_...> visitor(funcs...);
    return accept_returning<typename WhenReturnType<Funcs_...>::Type>(val, visitor);
}

Filed under: OneOf Tagged: c++, c++0x, lambdas, metaprogramming, templates, variadic templates, visitor pattern
avatar

Reviewing the statistics for WIPUP 27.06.10a.

I decided to delay the statistics-review post for WIPUP 27.06.10a because a recent update to the dashboard now shows view statistics on a daily basis instead of a weekly basis – the results were quite surprising:

As you can see WIPUP is clearly one of our most active projects, with quite a decent kudos:subscriptions:updates ratio compared to the others. However when looking at the activity, we notice something rather interesting – the increase in views is not a sustained increase. It’s a spike whenever there is an update. Looking at my personal WIPSpace we can attribute the initial spike up to almost 200 to the 27.06.10a release itself. The very next day, with no updates, views returned to a pathetic zero.

The even larger spike was quite an oddity. On the 1st of June, one of my less relevant posts (about cooking) was aggregated onto Planet Larry, which sort of explains it (despite a 1 day time lag occuring before the spike) – but further investigation shows that my update about buying the C++ Qt book received a uniqely larger amount of views. I conclude that where the Planet Larry aggregation helped spark some interest, another equally important factor to the spike was that planet readers decided to read what else was on the blog, which was a post which linked to the programming book update – which was obviously a lot more relevant. All the same, very interesting stuff.

An obvious reason behind the non-sustained views is that WIPUP is largly an unknown entity on the web. Hopefully with more updates (which will come!) I can change this behavior into a steady stream.

In relevant news, the upcoming beta of WIPUP is making rapid progress and should be quite a sweet release.

Related posts:

  1. The WIPUP 21.02.10 stats are out.
  2. After the WIPUP release, the stats are in.
  3. WIPUP 19.03.10a – under or overcooked?

Posts for Wednesday, July 14, 2010

Paludis 0.48.3 Released

Paludis 0.48.3 has been released:

  • ‘cave display-resolution’ now indicates when changing the origin repository for an upgrade.
  • ‘cave display-resolution’ now shows dependency reasons correctly.
  • ‘cave resolve !foo’ now applies to all slots of foo, rather than using the –target-slots option.
  • ‘cave uninstall’ will now only update world if every slot has been removed.
  • ‘cave execute-resolution’ will now show pkg_nofetch information where appropriate.
  • ‘cave resume’ on a resume file that includes already-completed uninstalls no longer errors.
  • Uninstalls are now considered for ‘cave execute-resolution –continue-on-failure’ (previously a failed uninstall would not count as a failure).

Filed under: paludis releases Tagged: paludis

Programming with Falcon: Regex

While programming with Falcon your almost always going to want to do something fun. I personally get tired of the stupid posts over and again about how to use strings

// Standard string
ex_str = "this is a string"
// String addition (concat)
ex_str + " ...and still is" = "this is a string ...and still is"
// Literal strings
lit_str = 'Hell I''m a literal string'  // notice the two single quotes to produce one single quote when outputed
// And even funny things like getting "d" from "a"
letter_from_letter = "a" / 3 = "d"

But personally, that kind of stuff bores me. Everybody can do that with just about any language. What about something more interesting? Like regex?

Regex

Let’s say you want to use Regex in your code. Why? I don’t know why you want regex. I don’t really care to be totally honest. And, of course, I’m really bad at coming up with examples. So, let’s use some regular expressions


#!/usr/bin/env falcon
load regex

string = "1234"

if string == Regex("/\d+/")
> "Found it!"
end

The above does basically what you’d expect. If the string contains a series of integers your good to go! But Falcon can do more and better. You can actually create regular expression objects:


#!/usr/bin/env falcon
load regex

string = "1234"
re = Regex("/\d+/")

if re.match( string )
> "Found it!"
// Note here I use printl so I don't have to worry about
// the type returned by re.captured
printl( "I found it, here: ", re.captured(1))
end

Now that is where it starts to become interesting for me. The examples are obviously trivial, but the you can begin the see the power of language forming. Now you can reuse and abuse not to mention your code becomes much cleaner. You might gain a line or two but the power and beauty you get from it are totally worth it.

Enjoy the Penguins!


Posts for Tuesday, July 13, 2010

Clementine: A triumph of Free Software

Ages ago, in the long-forgotten days of 2008, there was Amarok 1.4. And it was good. Then KDE4 came along and Amarok was rewritten, reshaped, becoming something... different. Something unsettling. Something not altogether pleasant.

Fear not. Today we have Clementine.

Clementine

I consider Clementine a triumph of Free Software. A great project fell off the rails, so someone else picked up the pieces, forked it and kept the spirit alive.

Features present

Clementine embodies everything good about Amarok 1.4, in a shiny Qt4 package. The layout is eminently pleasant to use. It uses the classic "spreadsheet" playlist view that saw so much success in Amarok 1.4. If you care about cramming as much information about your music as possible onto the screen, this is as good as it gets. It's boring, and that's a good thing. It gets the job done.

Like Amaork, 1.4, in Clementine you can very quickly drill into your music collection, filter it, view recently added tracks, group songs by artist or album or year or genre or a combination of those things. Clementine also handles all of the edge cases correctly: it lists albums with Various Artists exactly how I'd want (exactly like Amarok 1.4). It correctly handles songs with non-Latin tag text.

Clementine detects additions and changes to my music collection instantly, without the massive scan-lags on startup that plague some other music players. Clementine doesn't bat an eye at my 7,000 song collection. There's no MySQL integration, but I don't need it. Clementine's SQLite backend supposedly handles 300k songs without much problem, which is good enough for me.

Clementine has Last.FM integration. It has three different styles of desktop notification. It has visualizations. It handles USB devices. It understands reply gain. It has cross-fading. It has an equalizer. It has a transcoder. It has a cover manager.

I'm tired of listing features. Let's just say it has every useful feature you'd ever want. And if you don't need a feature, it stays out of your way.

And for a program under such active development, it's rock solid. I have yet to see a crash. And speaking of active development, if you follow the activity in Clementine's SVN repo, you will find that this program is updated almost daily. How the devs find the time, I don't know, but I'm grateful. This program has gone from non-existent to awesome in record time.

Clementine can use gstreamer, so it even works cross-platform. I fired it up on Windows 7 the other day and I was amazed at how good it looked and felt. It supposedly also works on OS X.

Clementine doesn't cook your breakfast for you, but that might be in the works.

How to make a good UI

A perfect example of the polish of Clementine's UI: Tagging. How do you tag a whole album worth of music at once? You can select some songs and right click and go into a dialog, like most music players allow.

Or:

  1. Edit a tag for a single song (inline) by clicking the field. Let's say you edit Artist.
  2. Select multiple songs in your playlist. (Click and drag, CTRL-click, Shift-click, CTRL-A, whatever.)
  3. Right click the Artist tag in the song you edited, select Set Artist to "XXXXX", and now all the songs you selected will be updated.

Clementine

This is the kind of UI innovation that I like. It's simple, it's useful, and it's predictable. You can get things done without going through dialog windows, without a million clicks, without spending a minute scratching your head figuring things out.

(Meanwhile Amarok 2 is busy getting rid of the Stop button and making the volume control circular.)

Features missing

Admittedly, Clementine is missing a couple of features I wouldn't mind having. You can't skin or theme Clementine. You can't rate songs. You can't display song lyrics. You can't "queue" songs. But oh well. I can live without these features because the rest of the program is so darned good. For all I know, these features might pop up next week. I wouldn't be surprised.

The Clementine devs seem to be very friendly and responsive to feature requests and feedback, which is also great.

Clementine is also missing a few features/bloat that I'm glad to see NOT ported from Amarok. Wikipedia integration? Good riddance.

I would pay money for this program.

In November 2009 I had this to say:

(Anyone out there reading this, if you port Amarok 1.4 to Qt4 intact, I will pay you. Seriously. I will pay you money.)

The offer still stands. I will pay money for Clementine. I'm still waiting for a Donate link so I can do so. (Clementine devs, are you reading this?)

Why do I care about this so much? Because I have music playing whenever I'm using this computer, and when you add up work plus free time, I'm at this computer 8-10 hours per day. Music keeps me sane during multi-hour debug sessions. Music is an integral part of my life, and a music app is an integral part of playing music.

It's very important to me that the programs and tools I use all day are comfortable. Otherwise I become cranky. If you were a carpenter, would you want to use a hammer with a wobbly handle all day? I'm a programmer, and I want to use comfortable computer programs.

Clementine is very comfortable.

avatar

Do (Computer) Geeks Really Rule the (Computer) World?

I just read about an interesting development with the latest iPhone. It all started when we found out the iPhone’s antenna is bad. Now we’re finding out AppleCare has informed customers it won’t be fixed (really), Consumer Reports specifically does not recommend the iPhone 4, and Apple forum moderators are deleting threads right and left, which apparently isn’t out of the ordinary, nor will that likely change in the future. (Whew, that was a mouthful!)

If that weren’t enough, Megan McArdle reported about a growing aggravation with Apple’s policies among the geek crowd (surprise, surprise).

Now, what I have to say here is largely speculation. It’s one of those thoughts where you think about it, and it interests you, but you have no real way or need to prove it at the time, but, as time goes on, you start to notice patterns that add leverage to the idea.

It started with Firefox’s growth, which is almost a miracle. I was trying to figure out, during Firefox 2, how in the world it got so popular. Internet Explorer is still the default on every single Windows computer out there, (and that’s quite a few). People have to actually go out there and get Firefox. How in the world did they decide to do that?

In my corner of the universe, there’s only one way Firefox spread around: word of mouth. Word of mouth has to begin somewhere, though. As I read the Wikipedia article on the “History of Mozilla Firefox,” it became clear that the initial audience was the computer geek, until later versions. If they knew about it first, and they told all their friends, and the word kept spreading because the geeks kept supporting it, then that might explain Firefox’s popularity.

And then there’s the growing hatred of Windows. I’m sure many of you who are reading this blog are Linux users and have spouted your hatred of Windows, like I have, at every polite and appropriate moment possible. That, in combination with Apple’s increased focus (at least in the past) on pleasing developers (us geeks again!), can mostly explain Apple’s success.

There’s probably a few other examples I’m missing that I’ve discovered over the years. Nothing here is conclusive, and with something so socially oriented, I’m not sure there’s ever going to be proofs. Nevertheless, the patterns are definitely there. Besides, people tend to look up to the folks who deal with technology, when it comes to tech advice. It only makes sense, and that alone adds to the power of the computer geek

My answer to the “Do Geeks Really Rule the World?” question, then, is “mostly”.

It almost doesn’t surprise me, then, that, if Apple is getting slammed by geeks for their restrictive policies against developers and geeks, their popularity would begin to fall among everyone else.

What do you think? Am I going a bit overboard?


Generic Lambda Visitors, or Writing Haskell in C++0x (Part 3)

In parts one and two, we built up a OneOf holder that could contain one of a number of different types of values, and developed a flexible variation on the visitor pattern to do things with the underlying values. Now we’ll adapt that to use lambdas.

We’ll need some helper classes so we can figure out the parameter types and return type of a lambda. A lambda itself is just fancy syntax for a struct with an operator(), so combined with the new decltype operator we can do this:

template <typename Type_>
struct ParameterTypes;

template <typename C_, typename R_, typename P_>
struct ParameterTypes<R_ (C_::*)(P_)>
{
    typedef P_ FirstParameterType;
    typedef R_ ReturnType;
};

template <typename C_, typename R_, typename P_>
struct ParameterTypes<R_ (C_::*)(P_) const>
{
    typedef P_ FirstParameterType;
    typedef R_ ReturnType;
};

template <typename Lambda_>
struct LambdaParameterTypes
{
    typedef typename ParameterTypes<decltype(&Lambda_::operator())>::FirstParameterType FirstParameterType;
    typedef typename ParameterTypes<decltype(&Lambda_::operator())>::ReturnType ReturnType;
};

Then we can use this to implement an adapter that turns a number of lambda functions into a visitable class. For starters we’ll ignore the return type:

template <typename... Funcs_>
struct LambdaVisitor;

template <>
struct LambdaVisitor<>
{
    void visit(struct NotReallyAType);
};

template <typename Func_, typename... Rest_>
struct LambdaVisitor<Func_, Rest_...> :
    LambdaVisitor<Rest_...>
{
    Func_ & func;

    LambdaVisitor(Func_ & f, Rest_ & ... rest) :
        LambdaVisitor<Rest_...>(rest...),
        func(f)
    {
    }

    void visit(typename LambdaParameterTypes<Func_>::FirstParameterType & v)
    {
        func(v);
    }

    using LambdaVisitor<Rest_...>::visit;
};

Note the using statement, so all the visit methods defined have equal visibility for overload resolution. Also note how we define a dummy visit method in the base class, since it’s illegal to using something that doesn’t exist.

Incidentally, so far as I can see we have to use the “lots of inheritance” technique here. We can’t inherit using the ... operator, since there doesn’t seem to be a way of applying the ... operator to a using statement. Thus we can’t quite do something like:

template <typename Func_>
struct LambdaVisitorVisit
{
    Func_ func;

    LambdaVisitorVisit(Func_ & f) :
        func(f)
    {
    }

    void visit(typename LambdaParameterTypes<Func_>::FirstParameterType & v)
    {
        func(v);
    }
};

template <typename... Funcs_>
struct LambdaVisitor :
    LambdaVisitorVisit<Funcs_>...
{
    LambdaVisitor(Funcs_ & ... funcs) :
        LambdaVisitorVisit<Funcs_>(funcs)...
    {
    }

    /* Can't do this: */
    using LambdaVisitorVisit<Funcs_>::visit...;
};

I’m not sure whether this is an intentional omission from the standard, or an oversight, or whether it’s just not considered useful.

That aside, now we just have to concoct the when function:

template <typename Val_, typename... Funcs_>
void when(Val_ && val, Funcs_ && ... funcs)
{
    accept(val, LambdaVisitor<Funcs_...>(funcs...));
}

And here we go:

int main(int, char *[])
{
    OneOf<int, std::string> s(123);

    when(s,
            [] (int & x)               { std::cout << "int " << ++x << std::endl; },
            [] (const std::string & x) { std::cout << "string " << x << std::endl; }
        );

    const OneOf<int, std::string> t(std::string("monkey"));

    when(t,
            [] (const int & x)         { std::cout << "int " << x << std::endl; },
            [] (const std::string & x) { std::cout << "string " << x << std::endl; }
        );

    return EXIT_SUCCESS;
}

What about return types? We could switch to using when_returning, but there’s another option: we could use the return type of the first lambda as our return type instead.

template <typename Result_, typename... Funcs_>
struct LambdaVisitor;

template <typename Result_>
struct LambdaVisitor<Result_>
{
    void visit(struct NotReallyAType);
};

template <typename Result_, typename Func_, typename... Rest_>
struct LambdaVisitor<Result_, Func_, Rest_...> :
    LambdaVisitor<Result_, Rest_...>
{
    Func_ & func;

    LambdaVisitor(Func_ & f, Rest_ & ... rest) :
        LambdaVisitor<Result_, Rest_...>(rest...),
        func(f)
    {
    }

    Result_ visit(typename LambdaParameterTypes<Func_>::FirstParameterType & v)
    {
        return func(v);
    }

    using LambdaVisitor<Result_, Rest_...>::visit;
};

template <typename Val_, typename FirstFunc_, typename... Rest_>
typename LambdaParameterTypes<FirstFunc_>::ReturnType
when(Val_ && val, FirstFunc_ && first_func, Rest_ && ... rest)
{
    return accept_returning<typename LambdaParameterTypes<FirstFunc_>::ReturnType>(
            val,
            LambdaVisitor<typename LambdaParameterTypes<FirstFunc_>::ReturnType, FirstFunc_, Rest_...>(first_func, rest...));
}

And using it:

int main(int, char *[])
{
    OneOf<int, std::string> s(123);

    std::cout << when(s,
            [] (const int x)           { return x; },
            [] (const std::string & x) { return x.length(); }
        ) << std::endl;

    return EXIT_SUCCESS;
}

There we have it: a useful, highly generic alternative to visitors that makes use of lambdas. The code in full:

#include <string>
#include <memory>
#include <type_traits>
#include <utility>
#include <cstdlib>
#include <iostream>

struct UnknownTypeForOneOf;

template <typename Want_, typename... Types_>
struct SelectOneOfType;

template <typename Want_>
struct SelectOneOfType<Want_>
{
    typedef UnknownTypeForOneOf Type;
};

template <typename Want_, typename Try_, typename... Rest_>
struct SelectOneOfType<Want_, Try_, Rest_...>
{
    typedef typename std::conditional<
        std::is_same<Want_, Try_>::value,
        Try_,
        typename SelectOneOfType<Want_, Rest_...>::Type
            >::type Type;
};

template <typename Type_>
struct ParameterTypes;

template <typename C_, typename R_, typename P_>
struct ParameterTypes<R_ (C_::*)(P_)>
{
    typedef P_ FirstParameterType;
    typedef R_ ReturnType;
};

template <typename C_, typename R_, typename P_>
struct ParameterTypes<R_ (C_::*)(P_) const>
{
    typedef P_ FirstParameterType;
    typedef R_ ReturnType;
};

template <typename Lambda_>
struct LambdaParameterTypes
{
    typedef typename ParameterTypes<decltype(&Lambda_::operator())>::FirstParameterType FirstParameterType;
    typedef typename ParameterTypes<decltype(&Lambda_::operator())>::ReturnType ReturnType;
};

template <typename Type_>
struct OneOfVisitorVisit
{
    virtual void visit(Type_ &) = 0;
};

template <typename... Types_>
struct OneOfVisitor :
    OneOfVisitorVisit<Types_>...
{
};

template <typename Visitor_, typename Underlying_, typename Result_, typename... Types_>
struct OneOfVisitorWrapperVisit;

template <typename Visitor_, typename Underlying_, typename Result_>
struct OneOfVisitorWrapperVisit<Visitor_, Underlying_, Result_> :
    Visitor_
{
    Underlying_ & underlying;
    std::function<Result_ ()> execute;

    OneOfVisitorWrapperVisit(Underlying_ & u) :
        underlying(u)
    {
    }
};

template <typename Visitor_, typename Underlying_, typename Result_, typename Type_, typename... Rest_>
struct OneOfVisitorWrapperVisit<Visitor_, Underlying_, Result_, Type_, Rest_...> :
    OneOfVisitorWrapperVisit<Visitor_, Underlying_, Result_, Rest_...>
{
    OneOfVisitorWrapperVisit(Underlying_ & u) :
        OneOfVisitorWrapperVisit<Visitor_, Underlying_, Result_, Rest_...>(u)
    {
    }

    Result_ visit_returning(Type_ & t)
    {
        return this->underlying.visit(t);
    }

    virtual void visit(Type_ & t)
    {
        this->execute = std::bind(&OneOfVisitorWrapperVisit::visit_returning, this, std::ref(t));
    }
};

template <typename Underlying_, typename Result_, typename... Types_>
struct OneOfVisitorWrapper :
    OneOfVisitorWrapperVisit<OneOfVisitor<Types_...>, Underlying_, Result_, Types_...>
{
    OneOfVisitorWrapper(Underlying_ & u) :
        OneOfVisitorWrapperVisit<OneOfVisitor<Types_...>, Underlying_, Result_, Types_...>(u)
    {
    }
};

template <typename... Types_>
struct OneOfValueBase
{
    virtual ~OneOfValueBase() = 0;

    virtual void accept(OneOfVisitor<Types_...> &) = 0;
    virtual void accept(OneOfVisitor<const Types_...> &) const = 0;
};

template <typename... Types_>
OneOfValueBase<Types_...>::~OneOfValueBase() = default;

template <typename Type_, typename... Types_>
struct OneOfValue :
    OneOfValueBase<Types_...>
{
    Type_ value;

    OneOfValue(const Type_ & type) :
        value(type)
    {
    }

    virtual void accept(OneOfVisitor<Types_...> & visitor)
    {
        static_cast<OneOfVisitorVisit<Type_> &>(visitor).visit(value);
    }

    virtual void accept(OneOfVisitor<const Types_...> & visitor) const
    {
        static_cast<OneOfVisitorVisit<const Type_> &>(visitor).visit(value);
    }
};

template <typename... Types_>
class OneOf
{
    private:
        std::unique_ptr<OneOfValueBase<Types_...> > _value;

    public:
        template <typename Type_>
        OneOf(const Type_ & value) :
            _value(new OneOfValue<typename SelectOneOfType<Type_, Types_...>::Type, Types_...>{value})
        {
        }

        OneOf(const OneOf & other) = delete;

        OneOf(OneOf && other) :
            _value(std::move(other._value))
        {
        }

        template <typename Type_>
        OneOf & operator= (const Type_ & value)
        {
            _value.reset(new OneOfValue<typename SelectOneOfType<Type_, Types_...>::Type, Types_...>{value});
            return *this;
        }

        OneOf & operator= (const OneOf & other) = delete;

        OneOf & operator= (OneOf && other)
        {
            _value = std::move(other._value);
            return *this;
        }

        OneOfValueBase<Types_...> & value()
        {
            return *_value;
        }

        const OneOfValueBase<Types_...> & value() const
        {
            return *_value;
        }
};

template <typename Visitor_, typename Result_, typename OneOf_>
struct OneOfVisitorWrapperTypeFinder;

template <typename Visitor_, typename Result_, typename... Types_>
struct OneOfVisitorWrapperTypeFinder<Visitor_, Result_, const OneOf<Types_...> &>
{
    typedef OneOfVisitorWrapper<Visitor_, Result_, const Types_...> Type;
};

template <typename Visitor_, typename Result_, typename... Types_>
struct OneOfVisitorWrapperTypeFinder<Visitor_, Result_, OneOf<Types_...> &>
{
    typedef OneOfVisitorWrapper<Visitor_, Result_, Types_...> Type;
};

template <typename Result_, typename OneOf_, typename Visitor_>
Result_
accept_returning(OneOf_ && one_of, Visitor_ && visitor)
{
    typename OneOfVisitorWrapperTypeFinder<Visitor_, Result_, OneOf_>::Type visitor_wrapper(visitor);
    one_of.value().accept(visitor_wrapper);
    return visitor_wrapper.execute();
}

template <typename OneOf_, typename Visitor_>
void accept(OneOf_ && one_of, Visitor_ && visitor)
{
    accept_returning<void>(one_of, visitor);
}

template <typename Result_, typename... Funcs_>
struct LambdaVisitor;

template <typename Result_>
struct LambdaVisitor<Result_>
{
    void visit(struct NotReallyAType);
};

template <typename Result_, typename Func_, typename... Rest_>
struct LambdaVisitor<Result_, Func_, Rest_...> :
    LambdaVisitor<Result_, Rest_...>
{
    Func_ & func;

    LambdaVisitor(Func_ & f, Rest_ & ... rest) :
        LambdaVisitor<Result_, Rest_...>(rest...),
        func(f)
    {
    }

    Result_ visit(typename LambdaParameterTypes<Func_>::FirstParameterType & v)
    {
        return func(v);
    }

    using LambdaVisitor<Result_, Rest_...>::visit;
};

template <typename Val_, typename FirstFunc_, typename... Rest_>
typename LambdaParameterTypes<FirstFunc_>::ReturnType
when(Val_ && val, FirstFunc_ && first_func, Rest_ && ... rest)
{
    return accept_returning<typename LambdaParameterTypes<FirstFunc_>::ReturnType>(
            val,
            LambdaVisitor<typename LambdaParameterTypes<FirstFunc_>::ReturnType, FirstFunc_, Rest_...>(first_func, rest...));
}

In part four we’ll start do some clever things with the return type, mostly because we can, rather than because it’s definitely useful.


Filed under: OneOf Tagged: c++, c++0x, lambdas, metaprogramming, templates, variadic templates, visitor pattern

Planet Larry is not officially affiliated with Gentoo Linux. Original artwork and logos copyright Gentoo Foundation. Yadda, yadda, yadda.