|
|
PiHiPi is a simple object-oriented scripting language based on and compatible with PHP http://www.php.net. PiHiPi compiler is able to translate most of php code and also adds some new features to the language. The goal of PiHiPi project is to create simpler and more consistent php parser.
usage
PiHiPi compiler is a standalone command line utility that translates PiHiPi into valid php5 code. The resulting code can be executed or deployed directly, no additional libraries or extensions are required.
phicc -o<output_path> source.ph ... source.ph
translates given PiHiPi (.ph) files into one single php script. If -o option is omitted, phicc prints generated code to standard output, so that you can pipe it with php:
phicc source.ph | php
There's also a shell script, called phi, that does essentially the same:
phi source.ph
compiles and then executes source.ph
installation
windows binaries
Download and unzip windows package. Put phicc.exe somewhere in your path, and phrtl.php in your php include_path. Run tests to make sure everything is ok:
php test.php
After running tests, it's safe to remove installation directory, phicc.exe and phrtl.php are only two files you need to use PiHiPi.
building from source
You will need lemon http://www.hwaci.com/sw/lemon and re2c http://re2c.org
svn checkout http://pihipi.googlecode.com/svn/trunk/ pihipi cd pihipi make && make test
For Windows MinGW is preferred, .sln file for Microsoft toolchain is included, but not 'officially supported'. ;)
language extensions
With some exceptions, PiHiPi understands any valid php syntax. The following describes features of PiHiPi not found in standard php. For more details and examples look at the files in test and examples directories.
layout
Code is enclosed in <? ... ?>. Like ?>, closing comment symbol */ can be omitted (assumed at the end of file).
<?= ... ?> is not the same as echo, it's translated into the call to the function web::_print(), which defaults to htmlspecialchars(), but can be redefined:
<?
namespace web {
function _print()
print arguments->map(str::upper)->join(' ');
}
?>
<?='hi','there'?> (outputs: HI THERE)primitives
String interpolation is performed for arbitrary complex references starting with $ and for arbitrary expressions enclosed in {}:
echo "hello $foo->bar->baz[10+20]";
echo "hello {10 + someFunc() + $blah}";There are also two new string delimiters: """ and ''' (triple-double and triple-single quotes).
echo """ double "quotes" are ok """; echo ''' single 'quotes' are ok ''';
Regular expressions are first class primitives. Regexp literals are enclosed in backticks:
$regexp = `[a-z_][a-z0-9_]+`si;
arrays
Added shortcut syntax for array literals:
$ary = [10, 20, 'foo' => 'bar'];
Arrays are objects:
echo [1, 2, 3]->join('|'); // 1|2|3operators
Added ruby-alike match =~, not match !~ and compare <=> operators.
echo 'ABC' =~ `[A-Z]+`; // true echo 'ABC' !~ `[0-9]+`; // true echo 10 <=> 20; // -1
Index operator [] can accept two arguments. This form is only allowed on the right and returns substring/subarray:
echo '012345'[2, 4]; // '234' echo '012345'[2, -1]; // '2345'
Perl semantics for boolean || and &&:
echo 5 && 25; // 25 echo 0 || 25; // 25
isset and unset are true operators and can be used without parentheses
if(isset $a) echo $a; unset $b; echo isset $b;
Added new is and in operators. in tests if the value is in array or is a substring of the argument:
if(10 in [1, 5, 10, 20]) echo "yes!";
if('bar' in 'foobarbaz') echo "yes!";is acts like a wrapper around php is_a... family of functions:
if($var is number) echo "number";
if($var is string) echo "string";
class Foo {}
if($var is Foo)
echo "var is an object of class Foo";assignments
Added support for multiple assignment:
$a,$b = 10,20; // a=10,b=20 $a,,$c = 10,20,30; // a=10,c=30 $a,$b,$c = 10,20; // a=10,b=20,c=20
expressions
As a general rule, expression is allowed everywhere a variable can be used, e.g.:
echo func()[10];
echo $(get_var_name());
echo $obj->('pro' . 'perty');
echo (new Foo)->bar;
echo ('func' . 'name')($arg);
$a = new (get_class_name())($args);statements
throw can throw any object or string:
if(!open_foo_bar()) throw 'panic!';
catch argument can be arbitrary lvalue, even complex:
try { ... }
catch($myObj->myErr) { ... }Includes are statements and resolved at compile time i.e. include(expression) is not allowed. Resulting script is monolithic.
echo and print are statements, not functions. Also added puts:
puts 'Hello';
functions
Curly brackets can be omitted if function body is a single statement. Added support for named call arguments, the syntax is the same as for array literals:
function velocity($dist, $time) return $dist / $time;
echo velocity(100, 4); // 25
echo velocity('time' => 4, 'dist' => 100); // 25Along with traditional callbacks (string and array), there's also direct reference form (Class->method or Namespace::method):
echo [1, 2, 3]->map('someFunc'); // as in php
echo [1, 2, 3]->map([$someObj, 'someMethod']); // as in php
class SomeClass {
function staticFunc($z) return $z * 10;
}
echo [1, 2, 3]->map(SomeClass->staticFunc); // 10,20,30Function parameters are stored in arguments array:
function baz()
echo arguments->reverse()->join();
baz('a', 'b', 'c'); // cbacall and call_array invoke given function (like call_user_func...):
function bar() return 'hi';
call('bar');apply($func, $obj) and apply_array($func, $obj) call a function in context of object $obj, i.e. $obj temporary becomes $this in the function:
class A {
function info()
puts '***' . $this->whoami();
function whoami()
return "object A";
}
class B {
var $bar = '123';
function whoami()
return "object B, bar={@bar}";
}
$a = new A;
$a->info(); // calls A->whoami
$b = new B;
apply($a->info, $b); // calls B->whoaminamespaces
namespace Foo { body }Namespace body can contain constant, function and class declarations. Qualified name is looked for in that namespace:
echo Foo::bar();
Unqualified name is looked for in current namespace and then globally. global is a namespace and can be referred as such:
echo global::foo;
import <namespace-name> import <namespace-name> from <path>
imports another namespace in the scope of current.
classes
@ is a shortcut for $this->
class A {
function __construct($t) @wow = $t + 1;
}
echo (new A(125))->wow; // 126In general, -> refers to a class or object, and :: is used for namespaces. For php compatibility, it's also possible to use :: with global classes.
class GlobClass {
var $foo = 10;
}
echo GlobClass::foo;
echo GlobClass->foo; // the same
namespace Foo {
class Bar {
static $prop;
...
}
}
echo Foo::Bar->prop;extends accepts qualified class names:
class Foo extends OtherNamespace::Bar
Constants are resolved at run-time, that is, arbitrary expressions are allowed on the right:
const X = foo() + bar();
Getters and setters:
class Circle {
var $r;
function __construct($radius)
@r = $radius;
function get area()
return 3.1459 * @r * @r;
function set area()
throw 'cannot set area directly';
}
$c = new Circle(10);
echo $c->area;
$c->area = 111;__construct can return a value. If a non-null is returned, it will be taken as the value of "new".
class GoodFella {}
class Person {
function __construct($name) {
if($name == 'Joe')
return new GoodFella($name);
}
}
echo new Person('Tom'); // (object Person)
echo new Person('Joe'); // (object GoodFella)php interaction
All php functions (system and user) are automatically available to PiHiPi. PiHiPi identifiers take precedence over php ones, to use php function in any case prepend its name with php::
puts htmlspecialchars($var); // call php function
function mysql_connect() {...}
mysql_connect(); // call my function
php::mysql_connect(); // call php functionYou can also embed 'raw' php code using 'unparsed input' brackets {< ... >}:
{<
# php code
$html = strip_tags($_GET['msg']);
>}standard library
Some of php standard library functions are rewritten in PiHiPi style: each function group is either moved to its own namespace or becomes object-oriented:
// in php: echo strlen...
echo str::len('ABC');
// in php: preg_replace...
$text = str::replace($html, `<.*?>`, '');
// in php: fopen/fread
$f = new File('foo');
$first_10 = $f->read(10);
// in php: strtotime/date
echo (new Date('+10 days'))->string('d M Y');Here's a brief example that should give you a better idea on what PiHiPi programming looks like.
<?
// word count example
// http://www.tagarga.com/pihipi/examples/wc.php
const MAX_LEN = 1024;
if(web::SERVER('REQUEST_METHOD') == 'POST')
{
try
{
$text = web::POST('text');
if($text !~ `\S`)
throw "text is empty";
if(str::len($text) > MAX_LEN)
throw "text is too long";
echo "<b>text statistics {new Date}</b>";
$chars = [];
$words = [];
foreach(str::split($text) as $chr) {
if($chr =~ `[A-Z]`i) {
$chr = str::upper($chr);
$chars[$chr] = isset $chars[$chr] ? $chars[$chr] + 1 : 1;
}
}
if(!$chars->count())
throw "no letters in text";
echo "<br> Letters by frequency: ";
echo $chars->arsort()->keys()->join();
foreach(str::match($text, `[A-Z]+`i) as $w) {
$w = str::upper($w);
if(isset $words[$w])
$words[$w][1]++;
else
$words[$w] = [$w, 1];
}
function sort_words($a, $b)
return $b[1] <=> $a[1] || $a[0] <=> $b[0];
echo "<br> Words by frequency: ";
foreach($words->sort('sort_words') as $w)
echo $w->join ('='), ' ';
} catch($e)
{
puts "<b>Error: $e</b>";
}
}
echo """
<form action="{web::SERVER('SCRIPT_NAME')}" method="POST">
<textarea name="text" rows=10 cols=50>I Love You Love Me Love</textarea>
<input type="submit" value="get text stats">
</form>
""";
?>missing php features
going to be implemented:
- eval and dynamic includes
- binary arithmetic
- php5 OOP extensions (public, abstract, interface and friends)
- references (syntax only)
going to be dropped from the language:
- ${...} syntax
- backticks as shell_exec
- 'alternative syntax'
- 'declare'
