|
|
|
|
@ -1,13 +1,19 @@ |
|
|
|
|
<?php |
|
|
|
|
/** |
|
|
|
|
* This file is part of Swoole-Compiler(AOT). |
|
|
|
|
* |
|
|
|
|
* @link https://www.swoole.com/ |
|
|
|
|
* @contact service@swoole.com |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
namespace PhpAot\Python; |
|
|
|
|
|
|
|
|
|
class Translator extends \PhpAot\Core\Translator |
|
|
|
|
{ |
|
|
|
|
protected string $lang = 'Python'; |
|
|
|
|
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 array $definedFunctions = []; |
|
|
|
|
protected string $lang = 'Python'; |
|
|
|
|
private array $builtinTypes = [ |
|
|
|
|
'ArithmeticError', |
|
|
|
|
'AssertionError', |
|
|
|
|
@ -39,7 +45,7 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', |
|
|
|
|
'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', |
|
|
|
|
'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', |
|
|
|
|
'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError' |
|
|
|
|
'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', |
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
public function __construct() |
|
|
|
|
@ -95,14 +101,15 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
$id = $fn->id; |
|
|
|
|
if (isset($this->keywordsMap[$id])) { |
|
|
|
|
return 'PyCore::' . $id; |
|
|
|
|
} elseif (isset($this->definedFunctions[$id])) { |
|
|
|
|
} |
|
|
|
|
if (isset($this->definedFunctions[$id])) { |
|
|
|
|
return $id; |
|
|
|
|
} elseif (isset($this->builtinTypes[$id])) { |
|
|
|
|
} |
|
|
|
|
if (isset($this->builtinTypes[$id])) { |
|
|
|
|
return '$builtins->' . $id; |
|
|
|
|
} else { |
|
|
|
|
return '$' . $id; |
|
|
|
|
} |
|
|
|
|
// no break |
|
|
|
|
return '$' . $id; |
|
|
|
|
|
|
|
|
|
case 'Attribute': |
|
|
|
|
return $this->parseAttribute($fn->value) . '->' . $fn->attr; |
|
|
|
|
default: |
|
|
|
|
@ -129,41 +136,45 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (empty($args_list)) { |
|
|
|
|
return "$fn()"; |
|
|
|
|
} else { |
|
|
|
|
return "$fn(" . implode(', ', $args_list) . ")"; |
|
|
|
|
return "{$fn}()"; |
|
|
|
|
} |
|
|
|
|
return "{$fn}(" . implode(', ', $args_list) . ')'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static function valueToRepr($v, $python = false) |
|
|
|
|
{ |
|
|
|
|
if (is_string($v)) { |
|
|
|
|
$v = str_replace( |
|
|
|
|
["\\", "\n", "\r", "\t", "\v", "\x00", "\""], |
|
|
|
|
["\\\\", "\\n", "\\r", "\\t", "\\v", "\\x00", "\\\""], |
|
|
|
|
['\\', "\n", "\r", "\t", "\v", "\x00", '"'], |
|
|
|
|
['\\\\', '\\n', '\\r', '\\t', '\\v', '\\x00', '\\"'], |
|
|
|
|
$v |
|
|
|
|
); |
|
|
|
|
return "\"$v\""; |
|
|
|
|
} elseif ($v === []) { |
|
|
|
|
return "\"{$v}\""; |
|
|
|
|
} |
|
|
|
|
if ($v === []) { |
|
|
|
|
return '[]'; |
|
|
|
|
} elseif (is_numeric($v)) { |
|
|
|
|
} |
|
|
|
|
if (is_numeric($v)) { |
|
|
|
|
if ($python) { |
|
|
|
|
if (is_infinite($v)) { |
|
|
|
|
return "float('inf')"; |
|
|
|
|
} elseif (is_nan($v)) { |
|
|
|
|
} |
|
|
|
|
if (is_nan($v)) { |
|
|
|
|
return "float('nan')"; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return strval($v); |
|
|
|
|
} elseif (is_bool($v)) { |
|
|
|
|
} |
|
|
|
|
if (is_bool($v)) { |
|
|
|
|
return $python ? ($v ? 'True' : 'False') : ($v ? 'true' : 'false'); |
|
|
|
|
} elseif (is_null($v)) { |
|
|
|
|
} |
|
|
|
|
if (is_null($v)) { |
|
|
|
|
return $python ? 'None' : 'null'; |
|
|
|
|
} elseif (is_array($v)) { |
|
|
|
|
} |
|
|
|
|
if (is_array($v)) { |
|
|
|
|
return var_export($v, true); |
|
|
|
|
} else { |
|
|
|
|
return $python ? 'None' : 'null'; |
|
|
|
|
} |
|
|
|
|
return $python ? 'None' : 'null'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function parseConstant($value): string |
|
|
|
|
@ -249,16 +260,17 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
{ |
|
|
|
|
if ($slice->_type == 'Constant') { |
|
|
|
|
return $this->parseConstant($slice); |
|
|
|
|
} elseif ($slice->_type === 'Tuple') { |
|
|
|
|
} |
|
|
|
|
if ($slice->_type === 'Tuple') { |
|
|
|
|
return $this->parseTuple($slice); |
|
|
|
|
} elseif ($slice->_type === 'Slice') { |
|
|
|
|
} |
|
|
|
|
if ($slice->_type === 'Slice') { |
|
|
|
|
$_args[] = $slice->lower ? $this->parseTarget($slice->lower) : 'null'; |
|
|
|
|
$_args[] = $slice->upper ? $this->parseTarget($slice->upper) : 'null'; |
|
|
|
|
$_args[] = $slice->step ? $this->parseTarget($slice->step) : 'null'; |
|
|
|
|
return 'PyCore::slice(' . implode(', ', $_args) . ')'; |
|
|
|
|
} else { |
|
|
|
|
abort($slice); |
|
|
|
|
} |
|
|
|
|
abort($slice); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function parseImportFrom($node): string |
|
|
|
|
@ -268,7 +280,7 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
foreach ($node->names as $name) { |
|
|
|
|
$type = $name->name; |
|
|
|
|
$as = empty($name->asname) ? $type : $name->asname; |
|
|
|
|
$imports[] = "\$$as = PyCore::import('$module')->$type"; |
|
|
|
|
$imports[] = "\${$as} = PyCore::import('{$module}')->{$type}"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return implode(';' . PHP_EOL, $imports); |
|
|
|
|
@ -280,7 +292,7 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
foreach ($node->names as $name) { |
|
|
|
|
$module = $name->name; |
|
|
|
|
$as = empty($name->asname) ? $module : $name->asname; |
|
|
|
|
$out .= $this->getIndent() . "\$$as = PyCore::import('$module');\n"; |
|
|
|
|
$out .= $this->getIndent() . "\${$as} = PyCore::import('{$module}');\n"; |
|
|
|
|
} |
|
|
|
|
return $out; |
|
|
|
|
} |
|
|
|
|
@ -348,10 +360,9 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
case 'Expr': |
|
|
|
|
if (is_string($node->value->value)) { |
|
|
|
|
return '/** ' . $node->value->value . ' */' . PHP_EOL; |
|
|
|
|
} else { |
|
|
|
|
return $this->parseValue($node->value) . ';'; |
|
|
|
|
} |
|
|
|
|
// no break |
|
|
|
|
return $this->parseValue($node->value) . ';'; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
return $this->parseValue($node->value) . ';'; |
|
|
|
|
} |
|
|
|
|
@ -469,15 +480,14 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
$value = $this->parseValue($node->value); |
|
|
|
|
if ($node->ctx->_type == 'Store') { |
|
|
|
|
return $value . '[' . $this->parseSlice($node->slice) . '] = $__value'; |
|
|
|
|
} elseif ($op == 'Del') { |
|
|
|
|
} |
|
|
|
|
if ($op == 'Del') { |
|
|
|
|
return 'unset(' . $value . '[' . $this->parseSlice($node->slice) . '])'; |
|
|
|
|
} else { |
|
|
|
|
} |
|
|
|
|
return $value . '[' . $this->parseSlice($node->slice) . ']'; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
abort($node); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function parseFor($node) |
|
|
|
|
{ |
|
|
|
|
@ -502,13 +512,36 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
$fn = PHP_EOL . $this->getIndent() . 'function ' . $name . '(' . $args . ') {' . PHP_EOL; |
|
|
|
|
$this->indentLevel++; |
|
|
|
|
$fn .= $this->parseBody($node->body) . PHP_EOL; |
|
|
|
|
; |
|
|
|
|
|
|
|
|
|
$this->indentLevel--; |
|
|
|
|
$fn .= $this->getIndent() . '}' . PHP_EOL . PHP_EOL; |
|
|
|
|
|
|
|
|
|
return $fn; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function parseBody($tree): string |
|
|
|
|
{ |
|
|
|
|
$lines = []; |
|
|
|
|
foreach ($tree as $node) { |
|
|
|
|
$this->parseLine($node, $lines); |
|
|
|
|
} |
|
|
|
|
foreach ($lines as &$line) { |
|
|
|
|
$line = $this->getIndent() . $line; |
|
|
|
|
} |
|
|
|
|
return implode(PHP_EOL, $lines); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function convert($tree): void |
|
|
|
|
{ |
|
|
|
|
$this->prepare($tree); |
|
|
|
|
$output = '<?php' . PHP_EOL; |
|
|
|
|
$output .= '$operator = PyCore::import("operator");' . PHP_EOL; |
|
|
|
|
$output .= '$builtins = PyCore::import("builtins");' . PHP_EOL; |
|
|
|
|
$output .= $this->parseBody($tree->body); |
|
|
|
|
echo $output; |
|
|
|
|
echo PHP_EOL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private function addLine($line, array &$lines) |
|
|
|
|
{ |
|
|
|
|
if ($this->mode == 'cli' && STEP) { |
|
|
|
|
@ -528,9 +561,9 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
$value = $this->parseValue($node->value); |
|
|
|
|
if ($node->targets[0]->_type == 'Subscript') { |
|
|
|
|
$this->addLine('$__value = ' . $value . ';', $lines); |
|
|
|
|
$line = "$target;"; |
|
|
|
|
$line = "{$target};"; |
|
|
|
|
} else { |
|
|
|
|
$line = "$target = $value;"; |
|
|
|
|
$line = "{$target} = {$value};"; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case 'AugAssign': |
|
|
|
|
@ -610,29 +643,6 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
$this->addLine($line, $lines); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function parseBody($tree): string |
|
|
|
|
{ |
|
|
|
|
$lines = []; |
|
|
|
|
foreach ($tree as $node) { |
|
|
|
|
$this->parseLine($node, $lines); |
|
|
|
|
} |
|
|
|
|
foreach ($lines as &$line) { |
|
|
|
|
$line = $this->getIndent() . $line; |
|
|
|
|
} |
|
|
|
|
return implode(PHP_EOL, $lines); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public function convert($tree): void |
|
|
|
|
{ |
|
|
|
|
$this->prepare($tree); |
|
|
|
|
$output = '<?php' . PHP_EOL; |
|
|
|
|
$output .= '$operator = PyCore::import("operator");' . PHP_EOL; |
|
|
|
|
$output .= '$builtins = PyCore::import("builtins");' . PHP_EOL; |
|
|
|
|
$output .= $this->parseBody($tree->body); |
|
|
|
|
echo $output; |
|
|
|
|
echo PHP_EOL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private function parseGenerators($generators, $recipient, &$captures) |
|
|
|
|
{ |
|
|
|
|
$captures = []; |
|
|
|
|
@ -686,9 +696,8 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
$op_str = $this->parseOp($op); |
|
|
|
|
if ($op_str) { |
|
|
|
|
return $left . ' ' . $op_str . '= ' . $right . ';'; |
|
|
|
|
} else { |
|
|
|
|
return $left . ' = $operator->' . strtolower($op) . '(' . $left . ' , ' . $right . ');'; |
|
|
|
|
} |
|
|
|
|
return $left . ' = $operator->' . strtolower($op) . '(' . $left . ' , ' . $right . ');'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private function parseBinOp($value) |
|
|
|
|
@ -699,9 +708,8 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
$op_str = $this->parseOp($op); |
|
|
|
|
if ($op_str) { |
|
|
|
|
return $left . ' ' . $op_str . ' ' . $right; |
|
|
|
|
} else { |
|
|
|
|
return '$operator->' . strtolower($op) . '(' . $left . ' , ' . $right . ')'; |
|
|
|
|
} |
|
|
|
|
return '$operator->' . strtolower($op) . '(' . $left . ' , ' . $right . ')'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private function parseJoinedStr($value) |
|
|
|
|
@ -738,7 +746,7 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
$call = $this->parseCall($call); |
|
|
|
|
$target = empty($target) ? '$__' : $this->parseTarget($target); |
|
|
|
|
$code .= $target . '__object = ' . $call . ';' . PHP_EOL; |
|
|
|
|
; |
|
|
|
|
|
|
|
|
|
$code .= $target . ' = ' . $target . '__object->__enter__();' . PHP_EOL; |
|
|
|
|
$this->indentLevel++; |
|
|
|
|
$finally_code .= $this->getIndent() . $target . '__object->__exit__(null, null, null);' . PHP_EOL; |
|
|
|
|
@ -782,7 +790,6 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
return 'new PyDict(' . $code . '])'; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private function parseBoolOp($value) |
|
|
|
|
{ |
|
|
|
|
switch ($value->op->_type) { |
|
|
|
|
@ -948,11 +955,11 @@ class Translator extends \PhpAot\Core\Translator |
|
|
|
|
$format = $value->format_spec->values[0]->value; |
|
|
|
|
if ($format[0] == '.' and $format[-1] == 'f') { |
|
|
|
|
return 'round(' . $expr . ', ' . substr($format, 1, strlen($format) - 2) . ')'; |
|
|
|
|
} elseif (str_contains($format, '%')) { |
|
|
|
|
} |
|
|
|
|
if (str_contains($format, '%')) { |
|
|
|
|
return $expr . '->strftime("' . $format . '")'; |
|
|
|
|
} else { |
|
|
|
|
throw new \RuntimeException("Unsupported format: $format"); |
|
|
|
|
} |
|
|
|
|
throw new \RuntimeException("Unsupported format: {$format}"); |
|
|
|
|
} else { |
|
|
|
|
return $expr; |
|
|
|
|
} |
|
|
|
|
|