Run only the compiler:
$ php compiler.php test.php
Run compiler, then interpret it
$ php compiler.php test.php | php heap-interpreter.php
vendor |
<?php | |
require_once 'vendor/autoload.php'; | |
$parser = new PhpParser\Parser(new PhpParser\Lexer); | |
//$file = $argv[1]; | |
$file = "test.php"; | |
$code = file_get_contents($file); | |
$ast = $parser->parse($code); | |
//var_dump($ast); | |
$state = [ | |
"lines" => [], | |
]; | |
compile($ast, $state); | |
echo implode(" ", $state['lines']); | |
function compile($ast, &$state) { | |
foreach ($ast as $node) { | |
compileNode($node, $state); | |
} | |
} | |
function compileNode($node, &$state) { | |
switch($node->getType()) { | |
case 'Expr_BinaryOp_Plus': | |
$left = compileNode($node->left, $state); | |
$right = compileNode($node->right, $state); | |
return "$left $right +"; | |
case 'Scalar_LNumber': | |
return $node->value; | |
case 'Scalar_String': | |
$ss = []; | |
$cs = str_split($node->value); | |
foreach ($cs as $c) { | |
$ss[] = ord($c); | |
} | |
return implode(" ", $ss); | |
case 'Stmt_Echo': | |
foreach ($node->exprs as $expr) { | |
$value = compileNode($expr, $state); | |
$ss = []; | |
$cs = explode(" ", $value); | |
foreach ($cs as $c) { | |
$ss[] = $c . " ."; | |
} | |
$state['lines'][] = implode(" ", $ss); | |
} | |
return; | |
default: | |
echo "Unknown node: " . $node->getType() . "\n"; | |
} | |
} |
{ | |
"require": { | |
"nikic/php-parser": "~1.1" | |
} | |
} |
{ | |
"_readme": [ | |
"This file locks the dependencies of your project to a known state", | |
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", | |
"This file is @generated automatically" | |
], | |
"hash": "f2b49436e25196e9c265ae186fa1571d", | |
"packages": [ | |
{ | |
"name": "nikic/php-parser", | |
"version": "v1.1.0", | |
"source": { | |
"type": "git", | |
"url": "https://github.com/nikic/PHP-Parser.git", | |
"reference": "ac05ef6f95bf8361549604b6031c115f92f39528" | |
}, | |
"dist": { | |
"type": "zip", | |
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ac05ef6f95bf8361549604b6031c115f92f39528", | |
"reference": "ac05ef6f95bf8361549604b6031c115f92f39528", | |
"shasum": "" | |
}, | |
"require": { | |
"ext-tokenizer": "*", | |
"php": ">=5.3" | |
}, | |
"type": "library", | |
"extra": { | |
"branch-alias": { | |
"dev-master": "1.0-dev" | |
} | |
}, | |
"autoload": { | |
"files": [ | |
"lib/bootstrap.php" | |
] | |
}, | |
"notification-url": "https://packagist.org/downloads/", | |
"license": [ | |
"BSD-3-Clause" | |
], | |
"authors": [ | |
{ | |
"name": "Nikita Popov" | |
} | |
], | |
"description": "A PHP parser written in PHP", | |
"keywords": [ | |
"parser", | |
"php" | |
], | |
"time": "2015-01-18 11:29:59" | |
} | |
], | |
"packages-dev": [], | |
"aliases": [], | |
"minimum-stability": "stable", | |
"stability-flags": [], | |
"prefer-stable": false, | |
"prefer-lowest": false, | |
"platform": [], | |
"platform-dev": [] | |
} |
<?php | |
<?php | |
// \n => 10 | |
// space => 32 | |
// 0-9 => 48-57 | |
// A-Z => 65-90 | |
// a-z => 97-122 | |
$code = file_get_contents("php://stdin"); | |
$ops = preg_split('/\s+/', trim($code)); | |
$labels = []; | |
$calls = []; | |
$stack = []; | |
$buf = ''; | |
// Pre-index the labels, if we want | |
foreach ($ops as $ip => $op) { | |
if (strpos($op, ':') !== false) { | |
list($op, $param) = explode(':', $op); | |
if ($op == 'label') { | |
$labels[$param] = $ip; | |
} | |
} | |
} | |
$ip = 0; | |
// ip = instruction pointer | |
while($ip < count($ops)) { | |
$op = $ops[$ip]; | |
echo "$ip:\t$op\t" . json_encode($stack) . "\n"; | |
$ip++; | |
if (is_numeric($op)) { | |
array_push($stack, (int)$op); | |
continue; | |
} | |
if (strpos($op, ':') !== false) { | |
list($op, $param) = explode(':', $op); | |
} | |
switch ($op) { | |
case '*': | |
$b = array_pop($stack); | |
$a = array_pop($stack); | |
array_push($stack, $a * $b); | |
break; | |
case '/': | |
$b = array_pop($stack); | |
$a = array_pop($stack); | |
array_push($stack, $a / $b); | |
break; | |
case '+': | |
$b = array_pop($stack); | |
$a = array_pop($stack); | |
array_push($stack, $a + $b); | |
break; | |
case '-': | |
$b = array_pop($stack); | |
$a = array_pop($stack); | |
array_push($stack, $a - $b); | |
break; | |
case '.': // unary op (takes 1 arg) | |
$a = array_pop($stack); | |
$buf .= chr($a); | |
break; | |
case '.num'; | |
$a = array_pop($stack); | |
$buf .= $a; | |
break; | |
case '.newline': | |
$buf .= "\n"; | |
break; | |
case 'dup': | |
$a = array_pop($stack); | |
array_push($stack, $a); | |
array_push($stack, $a); | |
break; | |
case 'jmp': | |
$a = array_pop($stack); | |
$ip += $a; | |
break; | |
case 'label': | |
break; | |
case 'jump': | |
$ip = $labels[$param]; | |
//$ip = array_search('label:' . $param, $ops, true); | |
break; | |
case 'jumpz': | |
$a = array_pop($stack); | |
if ($a === 0) { | |
$ip = $labels[$param]; | |
} | |
break; | |
case 'jumpnz': | |
$a = array_pop($stack); | |
if ($a !== 0) { | |
$ip = $labels[$param]; | |
} | |
break; | |
case 'call': | |
array_push($calls, $ip); | |
$ip = $labels[$param]; | |
break; | |
case 'ret': | |
$ip = array_pop($calls); | |
break; | |
default: | |
throw new RuntimeException("unknown op $op"); | |
} | |
} | |
echo $buf; | |
//var_dump($stack); |
<?php | |
echo "hello world!\n"; |