remove(Python): remove Python translator and example file

- 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 code
pull/15/head
韩天峰 2 months ago
parent 719b2ce4fc
commit da0707e4d0
  1. 26
      examples/python/sys.php
  2. 979
      src/Python/Translator.php

@ -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…
Cancel
Save