- Removed entire Python Translator class with all parsing methods - Deleted sys.php example file with Python import statements - Removed all AST parsing functionality for Python to PHP conversion - Eliminated keyword mapping and builtin types handling for Python - Removed codepull/15/head
parent
719b2ce4fc
commit
da0707e4d0
2 changed files with 0 additions and 1005 deletions
@ -1,26 +0,0 @@ |
||||
<?php |
||||
use sys; |
||||
use os; |
||||
use numpy as np; |
||||
|
||||
function np() |
||||
{ |
||||
print(np::$module); |
||||
print(np::version->full_version); |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
var_dump(sys::api_version); |
||||
var_dump(os::getpid()); |
||||
var_dump(os::environ->get("PATH")); |
||||
} |
||||
|
||||
|
||||
|
||||
$array["hello"] = $a; |
||||
$array["world"] = $b; |
||||
|
||||
$c = $array["world"]; |
||||
|
||||
|
||||
@ -1,979 +0,0 @@ |
||||
<?php |
||||
|
||||
namespace PhpAot\Python; |
||||
|
||||
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 array $definedFunctions = []; |
||||
protected string $lang = 'Python'; |
||||
private array $builtinTypes = [ |
||||
'ArithmeticError', |
||||
'AssertionError', |
||||
'AttributeError', |
||||
'BaseException', |
||||
'BaseExceptionGroup', |
||||
'BlockingIOError', |
||||
'BrokenPipeError', |
||||
'BufferError', |
||||
'BytesWarning', |
||||
'ChildProcessError', |
||||
'ConnectionAbortedError', |
||||
'ConnectionError', |
||||
'ConnectionRefusedError', |
||||
'ConnectionResetError', |
||||
'DeprecationWarning', |
||||
'EOFError', |
||||
'Ellipsis', |
||||
'EncodingWarning', |
||||
'EnvironmentError', |
||||
'Exception', 'ExceptionGroup', 'False', |
||||
'FileExistsError', 'FileNotFoundError', 'FloatingPointError', |
||||
'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', |
||||
'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', |
||||
'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', |
||||
'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', |
||||
'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', |
||||
'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', |
||||
'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', |
||||
'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', |
||||
'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', |
||||
'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError' |
||||
]; |
||||
|
||||
public function __construct() |
||||
{ |
||||
$this->keywordsMap = array_flip($this->keywords); |
||||
$this->builtinTypes = array_flip($this->builtinTypes); |
||||
} |
||||
|
||||
public function prepare($root) |
||||
{ |
||||
foreach ($root->body as $body) { |
||||
if ($body->_type == 'FunctionDef') { |
||||
$this->definedFunctions[$body->name] = 1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public function getLine($node): int |
||||
{ |
||||
return $node->lineno; |
||||
} |
||||
|
||||
public function getType($node): string |
||||
{ |
||||
return $node->_type; |
||||
} |
||||
|
||||
public function parseAttribute($attr) |
||||
{ |
||||
switch ($attr->_type) { |
||||
case 'Name': |
||||
return '$' . $attr->id; |
||||
case 'Attribute': |
||||
return $this->parseAttribute($attr->value) . '->' . $attr->attr; |
||||
case 'Constant': |
||||
return 'PyCore::str("' . $attr->s . '")'; |
||||
case 'Call': |
||||
return $this->parseCall($attr); |
||||
case 'Subscript': |
||||
return $this->parseSubscript($attr); |
||||
case 'BinOp': |
||||
return $this->parseBinOp($attr); |
||||
default: |
||||
var_dump(__METHOD__, __LINE__); |
||||
abort($attr); |
||||
} |
||||
} |
||||
|
||||
public function parseFunc($fn) |
||||
{ |
||||
switch ($fn->_type) { |
||||
case 'Name': |
||||
$id = $fn->id; |
||||
if (isset($this->keywordsMap[$id])) { |
||||
return 'PyCore::' . $id; |
||||
} elseif (isset($this->definedFunctions[$id])) { |
||||
return $id; |
||||
} elseif (isset($this->builtinTypes[$id])) { |
||||
return '$builtins->' . $id; |
||||
} else { |
||||
return '$' . $id; |
||||
} |
||||
// no break |
||||
case 'Attribute': |
||||
return $this->parseAttribute($fn->value) . '->' . $fn->attr; |
||||
default: |
||||
abort($fn); |
||||
} |
||||
} |
||||
|
||||
public function parseCall($call): string |
||||
{ |
||||
$fn = $this->parseFunc($call->func); |
||||
if_empty_debug($fn, $call); |
||||
$args = $call->args; |
||||
$kwargs = $call->keywords; |
||||
|
||||
$args_list = []; |
||||
|
||||
foreach ($args as $arg) { |
||||
$args_list[] = $this->parseValue($arg); |
||||
} |
||||
|
||||
foreach ($kwargs as $arg) { |
||||
$name = $arg->arg; |
||||
$args_list[] = $name . ': ' . $this->parseValue($arg->value); |
||||
} |
||||
|
||||
if (empty($args_list)) { |
||||
return "$fn()"; |
||||
} else { |
||||
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", "\\\""], |
||||
$v |
||||
); |
||||
return "\"$v\""; |
||||
} elseif ($v === []) { |
||||
return '[]'; |
||||
} elseif (is_numeric($v)) { |
||||
if ($python) { |
||||
if (is_infinite($v)) { |
||||
return "float('inf')"; |
||||
} elseif (is_nan($v)) { |
||||
return "float('nan')"; |
||||
} |
||||
} |
||||
return strval($v); |
||||
} elseif (is_bool($v)) { |
||||
return $python ? ($v ? 'True' : 'False') : ($v ? 'true' : 'false'); |
||||
} elseif (is_null($v)) { |
||||
return $python ? 'None' : 'null'; |
||||
} elseif (is_array($v)) { |
||||
return var_export($v, true); |
||||
} else { |
||||
return $python ? 'None' : 'null'; |
||||
} |
||||
} |
||||
|
||||
public function parseConstant($value): string |
||||
{ |
||||
return self::valueToRepr($value->value); |
||||
} |
||||
|
||||
public function parseTuple($tuple) |
||||
{ |
||||
$ids = []; |
||||
foreach ($tuple->dims as $dim) { |
||||
$ids[] = $this->parseTarget($dim); |
||||
} |
||||
return 'PyCore::tuple([' . implode(', ', $ids) . '])'; |
||||
} |
||||
|
||||
public function parseListComp($listComp) |
||||
{ |
||||
$code = ''; |
||||
$this->indentLevel++; |
||||
$code .= $this->getIndent() . '$___ = [];' . PHP_EOL; |
||||
$recipient = '$___'; |
||||
$generators = $this->parseGenerators($listComp->generators, $recipient, $captures); |
||||
$code .= $this->getIndent() . $generators; |
||||
$code .= $this->getIndent() . 'return $___;' . PHP_EOL; |
||||
$this->indentLevel--; |
||||
$code .= $this->getIndent() . '})(' . implode(',', $captures) . ')'; |
||||
return '(function(' . implode(',', $captures) . ') {' . PHP_EOL . $code; |
||||
} |
||||
|
||||
public function parseValue($value) |
||||
{ |
||||
switch ($value->_type) { |
||||
case 'Call': |
||||
return $this->parseCall($value); |
||||
case 'Constant': |
||||
return $this->parseConstant($value); |
||||
case 'Attribute': |
||||
return $this->parseAttribute($value); |
||||
case 'Name': |
||||
return $this->parseName($value); |
||||
case 'List': |
||||
return $this->parseList($value); |
||||
case 'Dict': |
||||
return $this->parseDict($value); |
||||
case 'Set': |
||||
return $this->parseSet($value); |
||||
case 'Tuple': |
||||
return $this->parseTuple($value); |
||||
case 'ListComp': |
||||
return $this->parseListComp($value); |
||||
case 'DictComp': |
||||
return $this->parseDictComp($value); |
||||
case 'BinOp': |
||||
return $this->parseBinOp($value); |
||||
case 'JoinedStr': |
||||
return $this->parseJoinedStr($value); |
||||
case 'Subscript': |
||||
return $this->parseSubscript($value); |
||||
case 'FormattedValue': |
||||
return $this->parseFormattedValue($value); |
||||
case 'UnaryOp': |
||||
return $this->parseTarget($value); |
||||
case 'BoolOp': |
||||
return $this->parseBoolOp($value); |
||||
case 'Compare': |
||||
return $this->parseTest($value); |
||||
case 'IfExp': |
||||
return $this->parseIfExp($value); |
||||
case 'Yield': |
||||
return $this->parseYield($value); |
||||
case 'Lambda': |
||||
return $this->parseLambda($value); |
||||
case 'GeneratorExp': |
||||
return $this->parseGeneratorExp($value); |
||||
default: |
||||
abort($value); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
public function parseSlice($slice): string |
||||
{ |
||||
if ($slice->_type == 'Constant') { |
||||
return $this->parseConstant($slice); |
||||
} elseif ($slice->_type === 'Tuple') { |
||||
return $this->parseTuple($slice); |
||||
} elseif ($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); |
||||
} |
||||
} |
||||
|
||||
public function parseImportFrom($node): string |
||||
{ |
||||
$module = $node->module; |
||||
$imports = []; |
||||
foreach ($node->names as $name) { |
||||
$type = $name->name; |
||||
$as = empty($name->asname) ? $type : $name->asname; |
||||
$imports[] = "\$$as = PyCore::import('$module')->$type"; |
||||
} |
||||
|
||||
return implode(';' . PHP_EOL, $imports); |
||||
} |
||||
|
||||
public function parseImport($node): string |
||||
{ |
||||
$out = ''; |
||||
foreach ($node->names as $name) { |
||||
$module = $name->name; |
||||
$as = empty($name->asname) ? $module : $name->asname; |
||||
$out .= $this->getIndent() . "\$$as = PyCore::import('$module');\n"; |
||||
} |
||||
return $out; |
||||
} |
||||
|
||||
public function parseTarget($target) |
||||
{ |
||||
switch ($target->_type) { |
||||
case 'Name': |
||||
return '$' . $target->id; |
||||
case 'UnaryOp': |
||||
$operand = $this->parseTarget($target->operand); |
||||
switch ($target->op->_type) { |
||||
case 'USub': |
||||
return '-' . $operand; |
||||
case 'Not': |
||||
return '!' . $operand; |
||||
default: |
||||
abort($target); |
||||
break; |
||||
} |
||||
break; |
||||
case 'Tuple': |
||||
return $this->parseTuple($target); |
||||
case 'Subscript': |
||||
return $this->parseSubscript($target); |
||||
case 'IfExp': |
||||
return $this->parseIfExp($target); |
||||
case 'Attribute': |
||||
return $this->parseAttribute($target); |
||||
case 'Constant': |
||||
return $this->parseConstant($target); |
||||
case 'List': |
||||
return $this->parseList($target); |
||||
case 'Set': |
||||
return $this->parseSet($target); |
||||
case 'Call': |
||||
return $this->parseCall($target); |
||||
case 'BinOp': |
||||
return $this->parseBinOp($target); |
||||
case 'Compare': |
||||
return $this->parseTest($target); |
||||
case 'Slice': |
||||
return $this->parseSlice($target); |
||||
case 'BoolOp': |
||||
return $this->parseBoolOp($target); |
||||
case 'ListComp': |
||||
return $this->parseListComp($target); |
||||
default: |
||||
abort($target); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
public function parseIfExp($exp) |
||||
{ |
||||
$test = $this->parseTest($exp->test); |
||||
$orelse = $this->parseValue($exp->orelse); |
||||
$body = $this->parseValue($exp->body); |
||||
return $test . ' ? ' . $body . ' : ' . $orelse; |
||||
} |
||||
|
||||
public function parseExpr($node) |
||||
{ |
||||
switch ($node->_type) { |
||||
case 'Expr': |
||||
if (is_string($node->value->value)) { |
||||
return '/** ' . $node->value->value . ' */' . PHP_EOL; |
||||
} else { |
||||
return $this->parseValue($node->value) . ';'; |
||||
} |
||||
// no break |
||||
default: |
||||
return $this->parseValue($node->value) . ';'; |
||||
} |
||||
} |
||||
|
||||
public function parseArguments($args): string |
||||
{ |
||||
$defaults = $args->defaults ?? []; |
||||
$names = []; |
||||
foreach ($args->args as $k => $arg) { |
||||
$tmp = '$' . $arg->arg; |
||||
if ($k >= count($args->args) - count($defaults)) { |
||||
$tmp .= ' = ' . $this->parseValue($defaults[$k - count($args->args) + count($defaults)]); |
||||
} |
||||
$names[] = $tmp; |
||||
} |
||||
if ($args->vararg) { |
||||
$names[] = '...$' . $args->vararg->arg; |
||||
} |
||||
return implode(', ', $names); |
||||
} |
||||
|
||||
public function parseReturn($node) |
||||
{ |
||||
return 'return ' . $this->parseValue($node->value); |
||||
} |
||||
|
||||
public function parseTest($test) |
||||
{ |
||||
$comparator = null; |
||||
$left = $test->left ? $this->parseValue($test->left) : ''; |
||||
if ($test->ops) { |
||||
$ops = $test->ops; |
||||
if (count($ops) != 1) { |
||||
abort($test); |
||||
} |
||||
$op = $ops[0]->_type; |
||||
$comparators = $test->comparators; |
||||
$comparator = $comparators[0]; |
||||
} elseif ($test->op) { |
||||
$op = $test->op->_type; |
||||
} else { |
||||
$op = ''; |
||||
} |
||||
|
||||
switch ($op) { |
||||
case 'Is': |
||||
case 'Eq': |
||||
return $left . ' == ' . $this->parseValue($comparator); |
||||
case 'NotEq': |
||||
return $left . ' != ' . $this->parseValue($comparator); |
||||
case 'In': |
||||
return $this->parseValue($comparator) . '->__contains__(' . $left . ')'; |
||||
case 'NotIn': |
||||
return '!' . $this->parseValue($comparator) . '->__contains__(' . $left . ')'; |
||||
case 'Gt': |
||||
return $left . ' > ' . $this->parseValue($comparator); |
||||
case 'Lt': |
||||
return $left . ' < ' . $this->parseValue($comparator); |
||||
case 'GtE': |
||||
return $left . ' >= ' . $this->parseValue($comparator); |
||||
case 'LtE': |
||||
return $left . ' <= ' . $this->parseValue($comparator); |
||||
case 'Not': |
||||
return '!' . $this->parseValue($test->operand); |
||||
case 'Or': |
||||
return $this->parseOr($test->values); |
||||
case 'And': |
||||
return $this->parseAnd($test->values); |
||||
default: |
||||
return $this->parseTarget($test); |
||||
} |
||||
} |
||||
|
||||
public function parseIf($node) |
||||
{ |
||||
$expr = $this->parseTest($node->test); |
||||
$this->indentLevel++; |
||||
$body = $this->parseBody($node->body); |
||||
$orelse = empty($node->orelse) ? '' : $this->parseBody($node->orelse); |
||||
$this->indentLevel--; |
||||
return 'if (' . $expr . ') {' . |
||||
PHP_EOL . $body . |
||||
PHP_EOL . $this->getIndent() . '}' . |
||||
($orelse ? ' else {' . PHP_EOL . $orelse . PHP_EOL . $this->getIndent() . '}' : '') . |
||||
PHP_EOL; |
||||
} |
||||
|
||||
public function parseIter($iter) |
||||
{ |
||||
switch ($iter->_type) { |
||||
case 'Call': |
||||
return $this->parseCall($iter); |
||||
case 'List': |
||||
return $this->parseList($iter); |
||||
case 'Set': |
||||
return $this->parseSet($iter); |
||||
case 'Dict': |
||||
return $this->parseDict($iter); |
||||
case 'Name': |
||||
return $this->parseName($iter); |
||||
case 'Subscript': |
||||
return $this->parseSubscript($iter); |
||||
case 'Attribute': |
||||
return $this->parseAttribute($iter); |
||||
default: |
||||
abort($iter); |
||||
} |
||||
} |
||||
|
||||
public function parseSubscript($node) |
||||
{ |
||||
if ($node->slice) { |
||||
$op = $node->ctx->_type; |
||||
$value = $this->parseValue($node->value); |
||||
if ($node->ctx->_type == 'Store') { |
||||
return $value. '[' . $this->parseSlice($node->slice) . '] = $__value'; |
||||
} elseif ($op == 'Del') { |
||||
return 'unset(' . $value . '[' . $this->parseSlice($node->slice) . '])'; |
||||
} else { |
||||
return $value . '[' . $this->parseSlice($node->slice) . ']'; |
||||
} |
||||
} else { |
||||
abort($node); |
||||
} |
||||
} |
||||
|
||||
public function parseFor($node) |
||||
{ |
||||
$target = $this->parseTarget($node->target); |
||||
$iter = $this->parseIter($node->iter); |
||||
|
||||
$code = '$__iter = PyCore::iter(' . $iter . ');' . PHP_EOL; |
||||
$code .= $this->getIndent() . 'while($current = PyCore::next($__iter)) {' . PHP_EOL; |
||||
|
||||
$this->indentLevel++; |
||||
$code .= $this->getIndent() . $target . ' = $current;' . PHP_EOL; |
||||
$code .= $this->parseBody($node->body); |
||||
$this->indentLevel--; |
||||
|
||||
return $code . $this->getIndent() . PHP_EOL . $this->getIndent() . '}'; |
||||
} |
||||
|
||||
public function parseFunctionDef($node) |
||||
{ |
||||
$name = $node->name; |
||||
$args = $this->parseArguments($node->args); |
||||
$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; |
||||
} |
||||
|
||||
private function addLine($line, array &$lines) |
||||
{ |
||||
if ($this->mode == 'cli' && STEP) { |
||||
echo $line . PHP_EOL; |
||||
} |
||||
$lines[] = $line; |
||||
} |
||||
|
||||
private function parseLine($node, &$lines) |
||||
{ |
||||
switch ($node->_type) { |
||||
case 'ImportFrom': |
||||
$line = $this->parseImportFrom($node) . ';'; |
||||
break; |
||||
case 'Assign': |
||||
$target = $this->parseTarget($node->targets[0]); |
||||
$value = $this->parseValue($node->value); |
||||
if ($node->targets[0]->_type == 'Subscript') { |
||||
$this->addLine('$__value = ' . $value . ';', $lines); |
||||
$line = "$target;"; |
||||
} else { |
||||
$line = "$target = $value;"; |
||||
} |
||||
break; |
||||
case 'AugAssign': |
||||
$line = $this->parseAugAssign($node); |
||||
break; |
||||
case 'Import': |
||||
$line = $this->parseImport($node); |
||||
break; |
||||
case 'Expr': |
||||
$line = $this->parseExpr($node); |
||||
break; |
||||
case 'FunctionDef': |
||||
$line = $this->parseFunctionDef($node); |
||||
break; |
||||
case 'Return': |
||||
$line = $this->parseReturn($node) . ';'; |
||||
break; |
||||
case 'If': |
||||
$line = $this->parseIf($node); |
||||
break; |
||||
case 'For': |
||||
$line = $this->parseFor($node); |
||||
break; |
||||
case 'While': |
||||
$line = $this->parseWhile($node); |
||||
break; |
||||
case 'Try': |
||||
$line = $this->parseTry($node); |
||||
break; |
||||
case 'Raise': |
||||
$line = $this->parseRaise($node) . ';'; |
||||
break; |
||||
case 'Subscript': |
||||
$line = $this->parseSubscript($node) . ';'; |
||||
break; |
||||
case 'With': |
||||
$line = $this->parseWith($node); |
||||
break; |
||||
case 'Compare': |
||||
$line = $this->parseTest($node); |
||||
break; |
||||
case 'Delete': |
||||
$line = $this->parseDelete($node); |
||||
break; |
||||
case 'Continue': |
||||
$line = $this->parseContinue($node) . ';'; |
||||
break; |
||||
case 'Break': |
||||
$line = $this->parseBreak($node) . ';'; |
||||
break; |
||||
case 'Global': |
||||
$line = $this->parseGlobal($node) . ';'; |
||||
break; |
||||
case 'Pass': |
||||
$line = $this->parsePass($node) . ';'; |
||||
break; |
||||
case 'BinOp': |
||||
$line = $this->parseBinOp($node) . ';'; |
||||
break; |
||||
case 'IfExp': |
||||
$line = $this->parseIfExp($node) . ';'; |
||||
break; |
||||
case 'Tuple': |
||||
$line = $this->parseTuple($node) . ';'; |
||||
break; |
||||
case 'Lambda': |
||||
$line = $this->parseLambda($node) . ';'; |
||||
break; |
||||
case 'Call': |
||||
$line = $this->parseCall($node) . ';'; |
||||
break; |
||||
default: |
||||
abort($node); |
||||
$line = ''; |
||||
break; |
||||
} |
||||
$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 = []; |
||||
$code = ''; |
||||
|
||||
foreach ($generators as $k => $generator) { |
||||
$target = $this->parseTarget($generator->target); |
||||
if ($generator->iter->_type == 'Name') { |
||||
$name = '$' . $generator->iter->id; |
||||
$captures[] = $name; |
||||
} else { |
||||
$name = '$___iter'; |
||||
$code .= $this->getIndent() . '$___iter = ' . $this->parseIter($generator->iter) . ';' . PHP_EOL; |
||||
} |
||||
$code .= $this->getIndent() . 'foreach(' . $name . ' as $___i => ' . $target . ') {' . PHP_EOL; |
||||
$this->indentLevel++; |
||||
if (count($generators) === 1) { |
||||
$code .= $this->getIndent() . $recipient . '[] = ' . $target . ';' . PHP_EOL; |
||||
} else { |
||||
$code .= $this->getIndent() . $recipient . '[$___i][' . $k . '] = ' . $target . ';' . PHP_EOL; |
||||
} |
||||
$this->indentLevel--; |
||||
$code .= $this->getIndent() . '}' . PHP_EOL; |
||||
} |
||||
return $code; |
||||
} |
||||
|
||||
private function parseOp($op) |
||||
{ |
||||
switch ($op) { |
||||
case 'Mod': |
||||
return '%'; |
||||
case 'Add': |
||||
return '+'; |
||||
case 'Sub': |
||||
return '-'; |
||||
case 'Mult': |
||||
return '*'; |
||||
case 'Div': |
||||
return '/'; |
||||
default: |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
private function parseAugAssign($value): string |
||||
{ |
||||
$op = $value->op->_type; |
||||
$left = $this->parseTarget($value->target); |
||||
$right = $this->parseValue($value->value); |
||||
$op_str = $this->parseOp($op); |
||||
if ($op_str) { |
||||
return $left . ' ' . $op_str . '= ' . $right . ';'; |
||||
} else { |
||||
return $left . ' = $operator->' . strtolower($op) . '(' . $left . ' , ' . $right . ');'; |
||||
} |
||||
} |
||||
|
||||
private function parseBinOp($value) |
||||
{ |
||||
$op = $value->op->_type; |
||||
$left = $this->parseTarget($value->left); |
||||
$right = $this->parseValue($value->right); |
||||
$op_str = $this->parseOp($op); |
||||
if ($op_str) { |
||||
return $left . ' ' . $op_str . ' ' . $right; |
||||
} else { |
||||
return '$operator->' . strtolower($op) . '(' . $left . ' , ' . $right . ')'; |
||||
} |
||||
} |
||||
|
||||
private function parseJoinedStr($value) |
||||
{ |
||||
$list = []; |
||||
foreach ($value->values as $v) { |
||||
$list[] = $this->parseValue($v); |
||||
} |
||||
return implode(' . ', $list); |
||||
} |
||||
|
||||
private function parseList($target) |
||||
{ |
||||
$values = []; |
||||
foreach ($target->elts as $e) { |
||||
$values[] = $this->parseValue($e); |
||||
} |
||||
return 'new PyList([' . implode(', ', $values) . '])'; |
||||
} |
||||
|
||||
private function parseYield($value) |
||||
{ |
||||
return 'yield ' . $this->parseValue($value->value) . ';'; |
||||
} |
||||
|
||||
private function parseWith(mixed $node): string |
||||
{ |
||||
$items = $node->items; |
||||
$body = $node->body; |
||||
$code = ''; |
||||
$finally_code = ''; |
||||
|
||||
$parseItem = function ($call, $target) use (&$code, &$finally_code) { |
||||
$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; |
||||
$this->indentLevel--; |
||||
}; |
||||
|
||||
foreach ($items as $k => $item) { |
||||
if ($item->context_expr->_type == 'Tuple') { |
||||
$context_expr = $item->context_expr->dims; |
||||
$optional_vars = $item->optional_vars->dims; |
||||
foreach ($context_expr as $k2 => $dim) { |
||||
$parseItem($dim, $optional_vars[$k2]); |
||||
} |
||||
} else { |
||||
$parseItem($item->context_expr, $item->optional_vars); |
||||
} |
||||
} |
||||
|
||||
$code .= 'try {' . PHP_EOL; |
||||
$this->indentLevel++; |
||||
$code .= $this->parseBody($body); |
||||
$this->indentLevel--; |
||||
$code .= PHP_EOL . '} finally {' . PHP_EOL; |
||||
$code .= $finally_code; |
||||
$code .= '}' . PHP_EOL; |
||||
return $code; |
||||
} |
||||
|
||||
private function parseDict($dict) |
||||
{ |
||||
$code = '[' . PHP_EOL; |
||||
foreach ($dict->values as $k => $v) { |
||||
if (empty($dict->keys[$k])) { |
||||
abort($v); |
||||
} |
||||
$key = $dict->keys[$k]; |
||||
$this->indentLevel++; |
||||
$code .= $this->getIndent() . $this->parseTarget($key) . ' => ' . $this->parseTarget($v) . ',' . PHP_EOL; |
||||
$this->indentLevel--; |
||||
} |
||||
return 'new PyDict(' . $code . '])'; |
||||
} |
||||
|
||||
|
||||
private function parseBoolOp($value) |
||||
{ |
||||
switch ($value->op->_type) { |
||||
case 'And': |
||||
return $this->parseAnd($value->values); |
||||
case 'Or': |
||||
return $this->parseOr($value->values); |
||||
default: |
||||
abort($value); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
private function parseAnd($values) |
||||
{ |
||||
$targets = []; |
||||
foreach ($values as $v) { |
||||
$targets[] = $this->parseTarget($v); |
||||
} |
||||
return implode(' and ', $targets); |
||||
} |
||||
|
||||
private function parseOr($values) |
||||
{ |
||||
$targets = []; |
||||
foreach ($values as $v) { |
||||
$targets[] = $this->parseTarget($v); |
||||
} |
||||
return implode(' or ', $targets); |
||||
} |
||||
|
||||
private function parseDelete(mixed $node) |
||||
{ |
||||
$code = ''; |
||||
foreach ($node->targets as $target) { |
||||
$code .= $this->getIndent() . $this->parseSubscript($target) . ';' . PHP_EOL; |
||||
} |
||||
return $code; |
||||
} |
||||
|
||||
private function parseSet($target) |
||||
{ |
||||
$values = []; |
||||
foreach ($target->elts as $e) { |
||||
$values[] = $this->parseValue($e); |
||||
} |
||||
return 'new PySet([' . implode(', ', $values) . '])'; |
||||
} |
||||
|
||||
private function parseWhile(mixed $node) |
||||
{ |
||||
$test = $this->parseTest($node->test); |
||||
$code = 'while(' . $test . ') {' . PHP_EOL; |
||||
$this->indentLevel++; |
||||
$code .= $this->parseBody($node->body); |
||||
$this->indentLevel--; |
||||
return $code . $this->getIndent() . PHP_EOL . $this->getIndent() . '}'; |
||||
} |
||||
|
||||
private function parseTry(mixed $node) |
||||
{ |
||||
$code = 'try {' . PHP_EOL; |
||||
$this->indentLevel++; |
||||
$code .= $this->parseBody($node->body); |
||||
$this->indentLevel--; |
||||
$code .= PHP_EOL . '}'; |
||||
|
||||
if ($node->handlers) { |
||||
$code .= ' catch(PyError $e) {' . PHP_EOL; |
||||
$this->indentLevel++; |
||||
foreach ($node->handlers as $k => $handler) { |
||||
if ($handler->type->_type == 'Name') { |
||||
$id = $handler->type->id; |
||||
$type = isset($this->builtinTypes[$id]) ? '$builtins->' . $id : '$' . $id; |
||||
} else { |
||||
$types = []; |
||||
foreach ($handler->type->dims as $dim) { |
||||
$id = $dim->id; |
||||
$types[] = isset($this->builtinTypes[$id]) ? '$builtins->' . $id : '$' . $id; |
||||
} |
||||
$type = 'new PyTuple([' . implode(', ', $types) . '])'; |
||||
} |
||||
$code .= ($k == 0 ? $this->getIndent() . 'if' : ' elseif') . ' (PyCore::isinstance($e, ' . $type . ')) {' . PHP_EOL; |
||||
$this->indentLevel++; |
||||
$code .= $this->parseBody($node->body); |
||||
$this->indentLevel--; |
||||
$code .= PHP_EOL . $this->getIndent() . '}'; |
||||
} |
||||
$code .= ' else {'; |
||||
$this->indentLevel++; |
||||
$code .= PHP_EOL . $this->getIndent() . 'throw $e;'; |
||||
$this->indentLevel--; |
||||
$code .= PHP_EOL . $this->getIndent() . '}' . PHP_EOL; |
||||
$this->indentLevel--; |
||||
$code .= $this->getIndent() . '}'; |
||||
} |
||||
|
||||
if ($node->finalbody) { |
||||
$code .= ' finally {' . PHP_EOL; |
||||
$this->indentLevel++; |
||||
$code .= $this->parseBody($node->finalbody); |
||||
$this->indentLevel--; |
||||
$code .= PHP_EOL . $this->getIndent() . '}'; |
||||
} |
||||
|
||||
return $code; |
||||
} |
||||
|
||||
private function parseRaise(mixed $node) |
||||
{ |
||||
return 'throw ' . $this->parseCall($node->exc); |
||||
} |
||||
|
||||
private function parseName($value) |
||||
{ |
||||
return '$' . $value->id; |
||||
} |
||||
|
||||
private function parseContinue(mixed $node) |
||||
{ |
||||
return 'continue'; |
||||
} |
||||
|
||||
private function parseBreak(mixed $node) |
||||
{ |
||||
return 'break'; |
||||
} |
||||
|
||||
private function parseGlobal(mixed $node) |
||||
{ |
||||
$names = []; |
||||
foreach ($node->names as $name) { |
||||
$names[] = '$' . $name; |
||||
} |
||||
return 'global ' . implode(', ', $names); |
||||
} |
||||
|
||||
private function parsePass(mixed $node) |
||||
{ |
||||
return 'return'; |
||||
} |
||||
|
||||
private function parseLambda($value) |
||||
{ |
||||
$lines = []; |
||||
$args = $this->parseArguments($value->args); |
||||
$code = 'function (' . $args . ') {' . PHP_EOL; |
||||
$this->indentLevel++; |
||||
$this->parseLine($value->body, $lines); |
||||
$code .= $this->getIndent() . 'return ' . $lines[0] . PHP_EOL; |
||||
$this->indentLevel--; |
||||
$code .= $this->getIndent() . '}'; |
||||
return $code; |
||||
} |
||||
|
||||
private function parseFormattedValue($value) |
||||
{ |
||||
$expr = $this->parseValue($value->value); |
||||
if ($value->format_spec and $value->format_spec->_type == 'JoinedStr') { |
||||
if (count($value->format_spec->values) != 1) { |
||||
abort($value); |
||||
} |
||||
$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, '%')) { |
||||
return $expr . '->strftime("' . $format . '")'; |
||||
} else { |
||||
throw new \RuntimeException("Unsupported format: $format"); |
||||
} |
||||
} else { |
||||
return $expr; |
||||
} |
||||
} |
||||
|
||||
private function parseDictComp($node) |
||||
{ |
||||
$code = ''; |
||||
$this->indentLevel++; |
||||
$code .= $this->getIndent() . '$___ = [];' . PHP_EOL; |
||||
$recipient = '$___'; |
||||
$generators = $this->parseGenerators($node->generators, $recipient, $captures); |
||||
$code .= $this->getIndent() . $generators; |
||||
$code .= $this->getIndent() . 'return $___;' . PHP_EOL; |
||||
$this->indentLevel--; |
||||
$code .= $this->getIndent() . '})(' . implode(',', $captures) . ')'; |
||||
return '(function(' . implode(',', $captures) . ') {' . PHP_EOL . $code; |
||||
} |
||||
|
||||
private function parseGeneratorExp($value) |
||||
{ |
||||
abort($value); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue