pull/1/head
韩天峰 3 years ago
parent 95c8a8e03d
commit 5fb2fc1ac5
  1. 1
      .gitignore
  2. 37
      bin/compiler.php
  3. 5
      composer.json
  4. 32
      conv.php
  5. 17
      examples/hello.php
  6. 36
      src/Core/Translator.php
  7. 314
      src/Php/Translator.php
  8. 9
      src/Php/Visitor.php
  9. 31
      src/Python/Translator.php
  10. 37
      src/functions.php

1
.gitignore vendored

@ -1,3 +1,4 @@
/.idea
/logs
/vendor
/tmp

@ -0,0 +1,37 @@
<?php
require dirname(__DIR__) . '/vendor/autoload.php';
use PhpAot\Php\Translator;
use PhpParser\Error;
use PhpParser\NodeTraverser;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter;
define('DEBUG', true);
$traverser = new NodeTraverser;
$prettyPrinter = new PrettyPrinter\Standard;
$traverser->addVisitor(new \PhpAot\Php\Visitor());
if (empty($argv[1])) {
die("php compiler.php [file]\n");
}
$code = file_get_contents($argv[1]);
$parser = (new ParserFactory())->createForNewestSupportedVersion();
try {
$ast = $parser->parse($code);
$stmts = $traverser->traverse($ast);
$translator = new Translator($stmts);
$translator->setIndent(' ');
$code = $translator->convert();
$translator->save($code, './tmp/hello.cc');
$translator->compileFile('./tmp/hello.cc');
} catch (Error $error) {
echo "Parse error: {$error->getMessage()}\n";
return;
}

@ -5,6 +5,9 @@
"autoload": {
"psr-4": {
"PhpAot\\": "src"
}
},
"files": [
"src/functions.php"
]
}
}

@ -3,6 +3,7 @@ if ($argc < 2) {
die("Usage: php conv.php [python-file]\n");
}
require __DIR__ . '/vendor/autoload.php';
define('DEBUG', getenv('PY2PHP_DEBUG'));
define('STEP', getenv('PY2PHP_STEP'));
@ -16,37 +17,6 @@ if ($json->_type != 'Module') {
echo "invalid python module\n";
}
function debug($v)
{
global $translator;
if ($translator->mode == 'cli') {
echo 'Error: Unsupported Python Syntax, Line: ' . $v->lineno . ', Type: ' . $v->_type . PHP_EOL;
if (DEBUG) {
debug_print_backtrace();
var_dump($v);
}
} else {
header('Content-Type: application/json');
echo json_encode($v, JSON_PRETTY_PRINT);
}
die;
}
function if_empty_debug($if_expr, $v)
{
if (empty($if_expr)) {
debug($v);
}
}
function if_not_empty_debug($if_expr, $v)
{
if (!empty($if_expr)) {
debug($v);
}
}
error_reporting(E_ERROR);
// web or cli
if (!empty($argv[2])) {

@ -0,0 +1,17 @@
<?php
function main(int $argc, array $argv): int
{
$a = 1;
$b = 2;
$c = 3.12343;
$d = 'hello';
$e = ['hello' => 1, 'world' => 33.43];
$argc = 999;
$a = $b * 13;
echo "value:=" . ($a + $b);
return $a * $b;
}

@ -0,0 +1,36 @@
<?php
namespace PhpAot\Core;
abstract class Translator
{
protected int $indentLevel = 0;
protected string $indentStr = "\t";
public string $mode = 'cli';
protected string $lang;
function setMode($mode): void
{
$this->mode = $mode;
}
function setIndent(string $indent): void
{
$this->indentStr = $indent;
}
public function getLang(): string
{
return $this->lang;
}
abstract public function getLine($node): int;
abstract public function getType($node): string;
protected function getIndent(): string
{
return str_repeat($this->indentStr, $this->indentLevel);
}
}

@ -0,0 +1,314 @@
<?php
namespace PhpAot\Php;
use PhpParser\Node;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
class Translator extends \PhpAot\Core\Translator
{
protected array $stmts;
protected string $phpxDir = '~/workspace/phpx';
protected string $lang = 'PHP';
protected array $typeMap = [];
protected array $headers = [
'phpx.h',
];
function __construct(array $stmts)
{
$this->stmts = $stmts;
}
function parseHeaders(): string
{
$lines = [];
foreach ($this->headers as $header) {
$lines[] = '#include <' . $header . '>';
}
return implode(PHP_EOL, $lines) . PHP_EOL . PHP_EOL;
}
function setPhpxDir($dir): void
{
$this->phpxDir = $dir;
}
function convert()
{
$code = '';
$code .= $this->parseHeaders();
$code .= $this->parseStmts($this->stmts);
return $code;
}
function save($code, $file)
{
file_put_contents($file, $code);
}
function getLine($node): int
{
return $node->getLine();
}
function getType($node): string
{
return $node->getType();
}
private function parseFunctionDef($v)
{
$name = $this->parseIdentifier($v->name);
$return = $this->parseIdentifier($v->returnType);
$params = $this->parseParams($v->params);
$code = $return . ' ' . $name . '(' . $params . ') {' . PHP_EOL;
$this->indentLevel++;
$stmts = $this->parseStmts($v->stmts);
$this->indentLevel--;
$code .= $stmts;
$code .= "}";
return $code;
}
protected function parseIdentifier($node)
{
$type = $node->getType();
switch ($type) {
case 'Identifier':
case 'Expr_Variable':
return $node->name;
case 'Scalar_Int':
case 'Scalar_Float':
return $node->value;
case 'Scalar_String':
return '"' . $node->value . '"';
case 'Expr_Array':
return $this->parseArray($node);
case 'Expr_BinaryOp_Mul':
return '(' . $this->parseBinaryOpMul($node) . ')';
case 'Expr_BinaryOp_Concat':
return '(' . $this->parseBinaryOpConcat($node) . ')';
case 'Expr_BinaryOp_Plus':
return '(' . $this->parseBinaryOpPlus($node) . ')';
default:
debug($node);
}
}
private function parseParams($params)
{
$list = [];
foreach ($params as $param) {
$type = $this->parseType($param->type);
$name = $this->parseIdentifier($param->var);
$list[] = $type . ' ' . $name;
$this->typeMap[$name] = $type;
}
return implode(', ', $list);
}
private function parseStmts(array $stmts)
{
$lines = [];
foreach ($stmts as $v) {
$class = $v->getType();
switch ($class) {
case 'Stmt_Function':
$lines[] = $this->parseFunctionDef($v);
break;
case 'Stmt_Expression':
$lines[] = $this->parseExpr($v->expr) . ';';
break;
case 'Stmt_Echo':
$lines[] = $this->parseEcho($v) . ';';
break;
case 'Stmt_Return':
$lines[] = $this->parseReturn($v) . ';';
break;
default:
debug($v);
}
}
$code = '';
foreach ($lines as $line) {
$code .= $this->getIndent() . $line . PHP_EOL;
}
return $code;
}
private function parseExpr(mixed $expr)
{
$type = $expr->getType();
switch ($type) {
case 'Expr_Assign':
return $this->parseAssign($expr);
case 'Expr_BinaryOp_Plus':
return $this->parseBinaryOpPlus($expr);
case 'Expr_BinaryOp_Mul':
return $this->parseBinaryOpMul($expr);
case 'Expr_BinaryOp_Concat':
return $this->parseBinaryOpConcat($expr);
default:
debug($expr);
}
}
private function parseAssign(mixed $v)
{
$var = $this->parseIdentifier($v->var);
$expr = $this->parseIdentifier($v->expr);
if (!isset($this->typeMap[$var])) {
$type = $this->detectType($v->var, $v->expr);
$this->typeMap[$var] = $type;
return $type . ' ' . $var . ' = ' . $expr;
} else {
return $var . ' = ' . $expr;
}
}
private function parseEcho(mixed $v)
{
return 'php::echo(' . $this->parseExprs($v->exprs) . ')';
}
private function parseExprs($exprs)
{
$code = '';
foreach ($exprs as $expr) {
$code .= $this->parseExpr($expr);
}
return $code;
}
private function parseBinaryOpPlus(mixed $expr)
{
$left = $this->parseIdentifier($expr->left);
$right = $this->parseIdentifier($expr->right);
return $left . ' + ' . $right;
}
private function parseReturn(mixed $v)
{
return 'return ' . $this->parseExpr($v->expr);
}
private function parseBinaryOpMul(mixed $expr)
{
$left = $this->parseIdentifier($expr->left);
$right = $this->parseIdentifier($expr->right);
return $left . ' * ' . $right;
}
private function detectType($var, $expr)
{
$exprType = $expr->getType();
switch ($exprType) {
case 'Scalar_Int':
return 'zend_long';
case 'Scalar_Float':
return 'double';
case 'Scalar_String':
return 'php::Variant';
case 'Expr_Array':
return 'php::Array';
default:
debug($expr);
}
}
private function parseArray($node)
{
$items = $node->items;
$list = [];
$this->indentLevel++;
foreach ($items as $item) {
if ($item->key) {
$list[] = $this->getIndent() . '{ php::Variant(' . $this->parseIdentifier($item->key) . '), php::Variant(' . $this->parseIdentifier($item->value) . ') }';
} else {
$list[] = $this->getIndent() . 'php::Variant(' . $this->parseIdentifier($item->value) . ')';
}
}
$this->indentLevel--;
return '{' . PHP_EOL .
implode(', ' . PHP_EOL, $list) . PHP_EOL .
$this->getIndent() .
'}';
}
private function parseType($type)
{
$name = $type->name;
switch ($name) {
case 'int':
return 'zend_long';
case 'array':
return 'php::Array';
case 'float':
return 'double';
default:
debug($type);
}
}
private function parseIncludes()
{
$list = [
$this->phpxDir . '/include',
];
$out = '$(php-config --includes) ';
foreach ($list as $li) {
$out .= '-I ' . $li . ' ';
}
return $out;
}
private function parseLdflags()
{
$list = [
'$(php-config --prefix)/lib',
$this->phpxDir . '/lib',
];
$out = '';
foreach ($list as $li) {
$out .= '-L ' . $li . ' ';
}
return $out;
}
private function parseLibs()
{
$list = [
'phpx',
'php',
];
$out = '';
foreach ($list as $li) {
$out .= '-l' . $li . ' ';
}
return $out;
}
public function compileFile($file)
{
$cmd = 'g++ -c ' . $file . ' -o ' . $file . '.o ' . $this->parseIncludes() . $this->parseLdflags() . $this->parseLibs();
echo $cmd . PHP_EOL;
shell_exec($cmd);
}
private function parseBinaryOpConcat(mixed $expr)
{
$left = $this->parseIdentifier($expr->left);
$right = $this->parseIdentifier($expr->right);
return $left . ' + ' . $right;
}
}

@ -0,0 +1,9 @@
<?php
namespace PhpAot\Php;
use PhpParser\NodeVisitorAbstract;
class Visitor extends NodeVisitorAbstract
{
}

@ -2,14 +2,12 @@
namespace PhpAot\Python;
class Translator
class Translator extends \PhpAot\Core\Translator
{
private array $keywords = ['abs', 'aiter', 'all', 'anext', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip'];
private array $keywordsMap = [];
private int $indentLevel = 0;
private string $indentStr = "\t";
public string $mode;
private array $definedFunctions = [];
protected string $lang = 'Python';
private array $builtinTypes = [
'ArithmeticError',
'AssertionError',
@ -50,16 +48,6 @@ class Translator
$this->builtinTypes = array_flip($this->builtinTypes);
}
function setMode($mode)
{
$this->mode = $mode;
}
function setIndent(string $indent)
{
$this->indentStr = $indent;
}
function prepare($root)
{
foreach ($root->body as $body) {
@ -69,6 +57,16 @@ class Translator
}
}
function getLine($node): int
{
return $node->lineno;
}
function getType($node): string
{
return $node->_type;
}
function parseAttribute($attr)
{
switch ($attr->_type) {
@ -482,11 +480,6 @@ class Translator
return $code . $this->getIndent() . PHP_EOL . $this->getIndent() . '}';
}
private function getIndent()
{
return str_repeat($this->indentStr, $this->indentLevel);
}
function parseFunctionDef($node)
{
$name = $node->name;

@ -0,0 +1,37 @@
<?php
use PhpAot\Core\Translator;
function debug($v)
{
/**
* @var $translator Translator
*/
global $translator;
$lang = $translator->getLang();
if ($translator->mode == 'cli') {
echo 'Error: Unsupported ' . $lang . ' Syntax, Line: ' . $translator->getLine($v) . ', Type: ' . $translator->getType($v) . PHP_EOL;
if (DEBUG) {
debug_print_backtrace();
var_dump($v);
}
} else {
header('Content-Type: application/json');
echo json_encode($v, JSON_PRETTY_PRINT);
}
die;
}
function if_empty_debug($if_expr, $v)
{
if (empty($if_expr)) {
debug($v);
}
}
function if_not_empty_debug($if_expr, $v)
{
if (!empty($if_expr)) {
debug($v);
}
}
Loading…
Cancel
Save