Sunday, November 30, 2008

on the road to lisp

So one of my goals with my new syntax was to eliminate the un-necessary. It's funny that as I did that I slowy ended up with a s-expression-like feel. Check it:

#So we start with a function:

function filter($array, $field, $pattern=false, $limit=0, $above=0){
}


#first we make an array of users

$users = Array(Array('name'=>'shaun','age'=>26),Array('name'=>'peter','age'=>60));


#now we can filter it

$newusers = filter($users, 'age', false, false, 25);


#clearly if we didn't have the definition of function right above us it would get confusing. So the first thing that would make it more legible is keyword parameters.

$newusers = filter(array:$users, field:'age', above:25);


#whats nice about that is we can leave out the params we don't need and just pass the ones we do thus allowing functions to start to become more of a DSL. Still this feels akward, what if we dropped the brackets? They're just a security blanket anyway.

$newusers = filter array:$users, field:'age', above:10;


#now we're getting somewhere but why not drop the comas and just infer from the whitespace that there are seperations?

$newusers = filter array:$users field:'age' above:25;


#sweet, but now that we can match functions why not allow "other words" to make it read more like a sentence and just have the parser ignore them? This way we don't clutter our methods, objects and keyword params with meaningless "joining words"

$newusers = filter the array:$users where field:'age' is above:25;


#that would end up getting parsed into:

$newusers = filter($users,'age',false,false,25);


#thus it does not add overhead but creates a much more readable "inline-commented" version which can thus easily modified by someone who did not write the original code or method.

#now what about the array declaration? using our concept of dropping the un-necessary lets use key:value pairs and no commas

$users = ((name:'shaun' age:25) (name:'peter' age:60));


#that definitely feels nicer than php or the json approach in my book. What is the obsession with commas? What purpose do they provide that white space does not?

#the next question is probably: what about inline expressions and or method calls i.e.


function get_users($group,$limit){}

$users = filter array:getusers group: 10 limit: 10 field:'name' pattern:'/[a-z]*/i';


#cleary we have some issues how can the parser group the arguments? well:

$users = filter array:(getusers group:10 limit:10) field:'name' pattern:'/[a-z]*/i';


#holy s-expressions batman!

Wednesday, November 26, 2008

bootstrapping the bootstrap

So to implement lisp in a "non-ugly" manner I am going to first complete my php+ syntax (ruby, python, smalltalk type stuff) and THEN use that to write the lisp transmogrifer. HAHA.

getting emotional over lisp

Man, what a journey. I started this blog quite a while ago with the intention of bringing lisp-esque elegance to php. This led to phparrayplus (which I still think is really cool considering it works in just "normal" php) and fluent expressions being used to implement DSLs. Now I have the transmogrifier which allows me to implement custom syntax in native php with no extra .so or extension. I am now using that same transmogrifier to implement lisp syntax and functionality (yes macros included). It is a thing of great beauty. I keep having moments of "this is genius" paired directly with "this is insane. what am I doing?" hahaha.

Tuesday, November 25, 2008

compilers, transmogrifiers and dragons oh my

So I have been pretty busy implementing the transmogrification library which is allowing me to add new syntax to php which is compiled by php into php. Some of the things I have already implemented include:

let locks, inline lambdas, yield blocks, with blocks, native json, function calls with keyword parameters and "dot [.]" notation for object method/param access... When it's all bug tested we will be releasing it on google code.

the collection space is going to be written using the transmogrifier.

Once we are done with the ruby/python/perl type constructs I am going to be using the lbirary to implement lisphp.

Friday, November 21, 2008

a tasty tidbit to scratch an itch

One of my favorite perl constructs is the ability to put an if statement after a print statement:

print 'test' if($x == 2);

In php that becomes

if($x == 2) echo 'test';

Not strictly longer but the phrasing feels wrong.

Extending the chain class like so I can do this:

class Puts extends Chain{
var $val;
function __construct($val){
parent::__construct();
$this->val = $val;
}

function __if($condition){
if($condition) echo $this->val."\n";
}
}

function puts($string){
$new = new Puts($string);
return $new;
}

$x = 2;
puts('this is an assertion')->if($x == 2);

#this would be nice for debugging
puts('this is a debug message')->if(DEBUG);

Thursday, November 20, 2008

fluent expressions and conditionals

One problem I have encountered when designing "dsl" fluent expressions in php is conditionals.

If you are chaining methods and you have a method a which could fail and thus affect method b it is best to execute them independently and check the validity with an if clause before calling method b.


class Chain{
var $valid;
var $__methods;
var $__class_name;

function __construct(){
$this->valid = true;
$this->__class_name = get_class($this);
$this->__methods = get_class_methods($this->__class_name);
}

function __get($arg){
if(in_array($arg,$this->__methods)){
return $this->$arg();
}
}

function __call($arg,$data){
if(!$this->valid){
return $this;
}
$arg = '__'.$arg;
if(in_array($arg,$this->__methods)){
return call_user_func_array(Array($this,$arg),$data);
}
}

function __if($arg){
$this->valid = $arg;
return $this;
}

function __method1($value){
echo $value."\n";
return $this;
}

function start(){
$this->valid = true;
return $this;
}

function stop(){
$this->valid = false;
return $this;
}
}

function chain(){
$new = new Chain();
return $new;
}
chain()->if(false)->method1('will not display');

$x = 10;
chain()->if($x == 10)
->stop
->method1('wont work')
->if(false)
->start
->method1('will not work - will now')
->if(false)
->method1('will not work either');