Friday, June 3, 2011

The First Step of a Long Journey

Over the past couple of weeks, I have assembled a reader in PHP, such that it understands code of the form (print (== (+ 4 4 6) (- 30 15 1))) and will be able to create PHP source that ultimately prints out "1".  It's kind of brokenly stupid in other ways, but it's the bare-bones skeleton of a working compiler.  Something I have never been able to build prior to this attempt, largely because I wanted to tokenize something superficially like PHP, and I always got bored of defining all the stupid tokens.  Going with s-expressions made for only a handful of token types so that I could get on with the interesting bits instead of grinding out pages of /&&|\|\|/ crud.  Because almost anything can go in an identifier, I can treat everything as identifiers for now.

There are a few obvious things it needs next: string types.  Variables.  defun.  defmacro.  Separate namespaces for functions and variables, defined by context, so you can say (array_map htmlspecialchars row) and it will know that the first argument passed is a callable and the second is a expression, so that they can compile to 'htmlspecialchars' and $row, respectively.  And to serve its original purpose as an "enhanced PHP"-to-PHP compiler, it needs to read that source language rather than s-expressions.  Of course, with a non-sexp-based language, macros might not work out so well, but I do want to be able to run code to rewrite the AST (or the whole tokenizer: aka reader macros) at compile-time.

There's a bunch of features I want to add, too.  Proper named arguments.  Multiple-value return.  Ubiquitous lexical scope, so obviously let and its function equivalent (flet perhaps?). Something else that I'm forgetting at the moment.

In the long run, I also want to do some optimizations; ideally, I could turn $efoo = array_map('htmlspecialchars', $foo); into $efoo=array(); foreach ($foo as $k=>$v) $efoo[$k]=htmlspecialchars($v); as well as doing simple optimizations like i++; to ++i;.  I'd also love to be able to compile some 5.3 code like $foo::bar("baz"), ?:, and "nowdoc" syntax into 5.2-compatible renditions (answer to the first: call_user_func(array($foo, 'bar'), "baz") though my accumulated wisdom now considers such things to be a code smell).

The weird thing about this is that if I succeed, I'll be doing what Rasmus did to create PHP—riffing on an existing system in the domain to come up with something a little better.

Thursday, May 19, 2011

Accidental Lisp

It began with a simple bit of laziness: I wanted a preprocessor so that I could write as if PHP had multiple return values.  I'd write "return $x, $y;" in the callee, and "$a, $b = fn();" in the caller, and the preprocessor would rewrite it to valid PHP (throwing array() and list() around the appropriate expressions).

But I'm even too lazy for that.  To do this right, I'd need to fully parse the PHP, so I could understand more complicated return expressions like method calls.  So instead of that, I slapped together a lexer for s-expressions.  They're a lot less hairy, and this is just some twisted experiment.

I was halfway through putting together a parser this evening for the lexer output, when I realized: a few years ago, I ported the metacircular evaluator from the SICP lectures into Ruby... then discovered I would need to write an s-expression parser, which you get for free with Lisp.  (That project then died.)  But if I finish an s-expression parser... I can port the metacircular evaluator to it and have the world's stupidest Lisp-1 implementation, i.e. it'll be done in PHP.*

Alternatively, I can define a package in SBCL that emits PHP, and have the reader and macros for free.  Then my head exploded.


* Because this tool was intended for PHP shops, the compiler would have to be written in and emit PHP so there's no Scary Foreign Language involved, other than the compiler's input.  And originally, that input language was going to be almost PHP.

Tuesday, May 10, 2011

Quickie: Diffie-Hellman Groups

Relying on others' suggested magic numbers for crypto is probably a Bad Idea, so recently I studied Diffie-Hellman a while to understand what the "DH Group" parameter was in my IPSEC setup, and my PuTTY settings.

DH turns out to be a lot like RSA, so bit lengths are comparable between the two and neither is directly comparable to symmetric ciphers like AES.  A specific Diffie-Hellman exchange happens using some parameters: a generator for the base, and a prime to use as modulus.  (An exponent remains secret.)  DH Groups refer to specific, pre-chosen prime-and-generator pairs so that, for example, SSH can negotiate "group 14" instead of transferring the complete parameters themselves.

These groups have been standardized in RFC 2409, with additional groups defined in RFC 3526.  The latter RFC defines the bit lengths of the groups explicitly, stating that group 5 is 1536 bits, group 14 is 2048, and group 16 is 4096 bits.  As far as I can tell, groups 1 and 2 defined in the earlier RFC are only 768 and 1024 bits, respectively.

Note well: I believe this means DH groups 1 and 2 are dangerously short and should not be used to set up an IPSEC VPN today.  Likewise, PuTTY should really be configured out-of-the-box to warn about the use of anything less than DH group 14.  However, before I take my own advice, I need to do some experiments to determine whether the IPSEC client in iOS actually handles DH groups other than 2.  Edit from THE FUTURE: iOS 4.x does not accept other groups; iOS 5.x no longer accepts group 2, AFAICT.  I haven't gotten a working IPSEC VPN set up again, though, since it's not very important to me.  (Work provides a PPTP VPN.)

Wednesday, May 4, 2011

Quickie: The Necessity of Whimsical Names

Rackspace recently announced that they'd like to discontinue Slicehost at some point, migrate everyone to the EC2-like Rackspace Cloud, and make people worry per GB about the bandwidth they're consuming.  So I'm preparing a move to Linode for more of everything*, and in the planning, I've come across a new argument in favor of whimsical names for servers.

If I give each server a whimsical name, like alice.example.com and bob.example.com, I can always refer to the old and new IP addresses as "alice" and "bob", while the change of IP of "www" propagates through the DNS.  Between the time where the new address is set and the old one is expired (and note that there's no way to force an ISP's resolver to honor the TTL if they choose to assume "no TTLs will be shorter than an hour") the name being transitioned points to a more-or-less random server.

Basically, the whimsical name is like a server ID, and the service-based names are just conveniences.  Though a program is three lines long, someday it must be maintained; though a server hosts one service, someday it will have to be replaced.  When an organization gets big enough that it can't generate whimsy as fast as it needs servers, then it should go with something more regular for the server name, but each server should still have a unique, non-service-based name.


* Except bandwidth, but the 11% difference is smaller than my current monthly consumption, so it turns out not to matter much.  Even if it did matter, that much transfer on The Cloud (insert angelic chord here) would be expensive, so Linode still wins.

Thursday, April 21, 2011

Perception

They say you don't get a second chance to make a first impression, but that depends on who you are.  Apple seems to have managed a couple of major architecture transitions and their own Vista without too much ill will, yet Microsoft was practically crucified for Vista with no architecture transitions.

Fair warning: many links in this post lead to tvtropes.


Friday, March 4, 2011

The Authority of the User

I used to believe that my computer was mine, and no program had any authority to do anything without my consent.  (This can probably be traced back to my days on Slashdot, a decade ago; if I didn't get the opinion from there, they certainly reinforced it.)  I believed I was sufficiently smart to manage my own software, without everyone's updater constantly nagging me to do so.  I especially didn't want the updater to do it on its own; this often lead to problems, especially when Firefox got updated behind the scenes while I was using it.  However, I liked automatic security updates on Linux, so I got rather used to restarting Firefox when links mysteriously failed to be followed, or menus and tabs couldn't be opened—these being the days before the "Firefox has been updated and needs to be restarted" notification.

Then, everything changed.


Wednesday, March 2, 2011

Quick tip: extending the man search path without $MANPATH

If you've ever tried to add a directory to man's search path, you've undoubtedly noticed that the MANPATH environment variable replaces rather than extends man's built-in search path.  Today, I rediscovered a clever little setup on a machine at work.
  1. Copy /etc/man.config to somewhere in your home dir.  Mine seems to be at ~/.config/man/man.config for optimal redundant redundancy.  (I will say that keeping the "man.config" name of the file makes vim highlight it without additional fuss.)
  2. Add your desired MANPATH lines to this file at whatever position you wish.  Don't forget to curse the lack of an include mechanism at this point, which prevents you from automatically getting changes to /etc/man.config.  Cheer up, because there probably won't be any.
  3. Add an alias to your shell.  For bash, you would put something like alias man='man -C ~/.config/man/man.config' (which obviously includes the name of the file chosen in step 1) into ~/.bashrc.  Remember to source ~/.bashrc to make it take effect in the current session.
That's all!  Now when you run man, your personal manpages will be searched as well.

The documentation for man on the system in question claims that it will use $PATH to guess at additional man page locations, but this does not actually work for me.  Having a command in ~/.install/bin does not allow man to find the manpage in ~/.install/share/man.