From dcb1f79e05c07b51167644c9632203ea496fe67b Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 25 Jun 2025 16:23:33 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=9B=B4=E5=A4=9A=20PHP=20?= =?UTF-8?q?=E8=AF=AD=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/compiler.php | 13 +- examples/expr1.php | 94 +++++++++++++ src/Php/Translator.php | 291 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 384 insertions(+), 14 deletions(-) create mode 100644 examples/expr1.php diff --git a/bin/compiler.php b/bin/compiler.php index b3550567..a618102d 100644 --- a/bin/compiler.php +++ b/bin/compiler.php @@ -3,6 +3,7 @@ require dirname(__DIR__) . '/vendor/autoload.php'; use PhpAot\Php\Translator; +use PhpAot\Php\Visitor; use PhpParser\Error; use PhpParser\NodeTraverser; use PhpParser\ParserFactory; @@ -13,13 +14,14 @@ define('DEBUG', true); $traverser = new NodeTraverser; $prettyPrinter = new PrettyPrinter\Standard; -$traverser->addVisitor(new \PhpAot\Php\Visitor()); +$traverser->addVisitor(new Visitor()); if (empty($argv[1])) { die("php compiler.php [file]\n"); } -$code = file_get_contents($argv[1]); +$file = $argv[1]; +$code = file_get_contents($file); $parser = (new ParserFactory())->createForNewestSupportedVersion(); try { @@ -28,10 +30,11 @@ try { $translator = new Translator($stmts); $translator->setIndent(' '); $code = $translator->convert(); - $translator->save($code, './tmp/hello.cc'); - $translator->compileFile('./tmp/hello.cc'); + $info = pathinfo($file); + $cppFile = './tmp/' . $info['filename'] . '.cc'; + $translator->save($code, $cppFile); + //$translator->compileFile($cppFile); } catch (Error $error) { echo "Parse error: {$error->getMessage()}\n"; return; } - diff --git a/examples/expr1.php b/examples/expr1.php new file mode 100644 index 00000000..9bcf4fc5 --- /dev/null +++ b/examples/expr1.php @@ -0,0 +1,94 @@ + 50 ? 1 : 2; + $c = $a ** $b; + $c--; + --$c; + + $d = $a & $b; + $d = $a | $b; + $d = $a ^ $b; + $d = ~$a; + $d = $a << $b; + $d = $a >> $b; + + if ($a == $b) { + echo "[1] Equal\n"; + } else { + echo "[1] Not equal\n"; + } + + if ($a != $b) { + echo "Not equal\n"; + } + + if ($a and $b) { + echo "And\n"; + } + + if ($a or $b) { + echo "Or\n"; + } + + if ($a xor $b) { + echo "Xor\n"; + } + + if ($a && $b) { + echo "Logical And\n"; + } + + if ($a || $b) { + echo "Logical Or\n"; + } + + if (!$a) { + echo "Not\n"; + } + + if ($a > 1000) { + echo "Greater than 1000\n"; + } elseif ($a > 500) { + echo "Greater than 500\n"; + } else if ($a > 100) { + echo "Greater than 100\n"; + } else { + echo "Less to 100\n"; + } + + $i = 1; + while ($i <= 10) { + echo $i++; + } + + $i = 1; + while ($i <= 10): + print $i; + $i++; + endwhile; + + $i = 10; + do { + echo $i; + $i--; + } while ($i > 0); + + $i = 1; + for (; ; $i++) { + if ($i > 10) { + break; + } + if (0 === ($i % 5)) { + continue; + } + echo $i; + } + + return $a; +} diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 7bb50d8d..fbdf23c8 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -75,7 +75,12 @@ class Translator extends \PhpAot\Core\Translator private function parseFunctionDef($v) { $name = $this->parseIdentifier($v->name); - $returnType = $this->getZendType($this->parseIdentifier($v->returnType)); + if ($v->returnType) { + $returnType = $this->getZendType($this->parseIdentifier($v->returnType)); + } else { + $returnType = 'php::Variant'; + } + $params = $this->parseParams($v->params); $code = $returnType . ' ' . self::PREFIX . $name . '(' . $params . ') {' . PHP_EOL; $this->indentLevel++; @@ -99,7 +104,7 @@ class Translator extends \PhpAot\Core\Translator case 'Scalar_Float': return $expr->value; case 'Scalar_String': - return '"' . $expr->value . '"'; + return '"' . str_replace("\n", '\n', $expr->value) . '"'; default: return $this->parseExpr($expr); } @@ -138,6 +143,21 @@ class Translator extends \PhpAot\Core\Translator case 'Stmt_For': $lines[] = $this->parseFor($v); break; + case 'Stmt_While': + $lines[] = $this->parseWhile($v); + break; + case 'Stmt_Do': + $lines[] = $this->parseDo($v); + break; + case 'Stmt_If': + $lines[] = $this->parseIf($v); + break; + case 'Stmt_Break': + $lines[] = 'break;'; + break; + case 'Stmt_Continue': + $lines[] = 'continue;'; + break; default: debug($v); } @@ -155,12 +175,32 @@ class Translator extends \PhpAot\Core\Translator switch ($type) { case 'Expr_Assign': return $this->parseAssign($expr); + case 'Expr_Print': + return $this->parsePrint($expr); + case 'Expr_BinaryOp_Equal': + return $this->parseBinaryOpEqual($expr); + case 'Expr_BinaryOp_NotEqual': + return $this->parseBinaryOpNotEqual($expr); + case 'Expr_BinaryOp_Identical': + return $this->parseBinaryOpIdentical($expr); + case 'Expr_BooleanNot': + return $this->parseBooleanNot($expr); case 'Expr_BinaryOp_Plus': return $this->parseBinaryOpPlus($expr); case 'Expr_BinaryOp_Smaller': - return $this->parseBinarySmaller($expr); + return $this->parseBinaryOpSmaller($expr); + case 'Expr_BinaryOp_SmallerOrEqual': + return $this->parseBinaryOpSmallerOrEqual($expr); + case 'Expr_BinaryOp_GreaterOrEqual': + return $this->parseBinaryOpGreaterOrEqual($expr); case 'Expr_PreInc': return $this->parsePreInc($expr); + case 'Expr_PostInc': + return $this->parsePostInc($expr); + case 'Expr_PreDec': + return $this->parsePreDec($expr); + case 'Expr_PostDec': + return $this->parsePostDec($expr); case 'Expr_AssignOp_Plus': return $this->parseAssignOpPlus($expr); case 'Expr_AssignOp_Minus': @@ -172,9 +212,19 @@ class Translator extends \PhpAot\Core\Translator case 'Expr_BinaryOp_Mul': return $this->parseBinaryOpMul($expr); case 'Expr_AssignOp_Mod': - return $this->parseBinaryOpMod($expr); + return $this->parseAssignOpMod($expr); case 'Expr_BinaryOp_Concat': return $this->parseBinaryOpConcat($expr); + case 'Expr_BinaryOp_Greater': + return $this->parseBinaryOpGreater($expr); + case 'Expr_BinaryOp_LogicalAnd': + case 'Expr_BinaryOp_BooleanAnd': + return $this->parseBinaryOpLogicalAnd($expr); + case 'Expr_BinaryOp_LogicalOr': + case 'Expr_BinaryOp_BooleanOr': + return $this->parseBinaryOpLogicalOr($expr); + case 'Expr_BinaryOp_LogicalXor': + return $this->parseBinaryOpLogicalXor($expr); case 'Expr_Array': return $this->parseArray($expr); case 'Expr_ArrayDimFetch': @@ -183,13 +233,27 @@ class Translator extends \PhpAot\Core\Translator return $this->parseBinaryOpShiftLeft($expr); case 'Expr_BinaryOp_ShiftRight': return $this->parseBinaryOpShiftRight($expr); + case 'Expr_BinaryOp_BitwiseAnd': + return $this->parseBinaryOpBitwiseAnd($expr); + case 'Expr_BinaryOp_BitwiseOr': + return $this->parseBinaryOpBitwiseOr($expr); + case 'Expr_BinaryOp_BitwiseXor': + return $this->parseBinaryOpBitwiseXor($expr); + case 'Expr_BitwiseNot': + return $this->parseBitwiseNot($expr); + case 'Expr_BinaryOp_Mod': + return $this->parseBinaryOpMod($expr); + case 'Expr_BinaryOp_Pow': + return $this->parseBinaryOpPow($expr); + case 'Expr_Ternary': + return $this->parseTernary($expr); case 'Expr_FuncCall': return $this->parseFuncCall($expr); case 'Scalar_Int': case 'Scalar_Float': - return $expr->value; case 'Scalar_String': - return '"' . $expr->value . '"'; + case 'Expr_Variable': + return $this->parseIdentifier($expr); default: debug($expr); } @@ -387,7 +451,7 @@ class Translator extends \PhpAot\Core\Translator return $code; } - private function parseBinarySmaller(mixed $expr): string + private function parseBinaryOpSmaller(mixed $expr): string { $left = $this->parseIdentifier($expr->left); $right = $this->parseIdentifier($expr->right); @@ -449,8 +513,9 @@ class Translator extends \PhpAot\Core\Translator private function parseBinaryOpMod(mixed $expr): string { - $var = $this->parseIdentifier($expr->var); - return $var . ' %= ' . $this->parseIdentifier($expr->expr); + $left = $this->parseExpr($expr->left); + $right = $this->parseExpr($expr->right); + return $left . ' % ' . $right; } private function parseFuncCall(mixed $expr): string @@ -473,5 +538,213 @@ class Translator extends \PhpAot\Core\Translator return $this->parseIdentifier($arg->value); } + private function parsePostInc($expr): string + { + return $this->parseIdentifier($expr->var) . '++'; + } + + private function parsePostDec($expr): string + { + return $this->parseIdentifier($expr->var) . '--'; + } + + private function parseTernary(mixed $expr): string + { + $cond = $expr->cond; + $if = $expr->if; + $else = $expr->else; + return $this->parseExpr($cond) . ' ? ' . $this->parseExpr($if) . ' : ' . $this->parseExpr($else); + } + + private function parseBinaryOpGreater(mixed $expr): string + { + $left = $this->parseIdentifier($expr->left); + $right = $this->parseIdentifier($expr->right); + + return $left . ' > ' . $right; + } + + private function parseAssignOpMod(mixed $expr): string + { + $var = $this->parseIdentifier($expr->var); + return $var . ' % ' . $this->parseIdentifier($expr->expr); + } + + private function parseBinaryOpPow(mixed $expr): string + { + $left = $this->parseIdentifier($expr->left); + $right = $this->parseIdentifier($expr->right); + + return 'pow(' . $left . ', ' . $right . ')'; + } + + private function parsePreDec(mixed $expr): string + { + return '--' . $this->parseIdentifier($expr->var); + } + + private function parseBinaryOpBitwiseAnd(mixed $expr): string + { + $left = $this->parseIdentifier($expr->left); + $right = $this->parseIdentifier($expr->right); + + return $left . ' & ' . $right; + } + + private function parseBinaryOpBitwiseOr(mixed $expr): string + { + $left = $this->parseIdentifier($expr->left); + $right = $this->parseIdentifier($expr->right); + + return $left . ' | ' . $right; + } + + private function parseBinaryOpBitwiseXor(mixed $expr) + { + $left = $this->parseIdentifier($expr->left); + $right = $this->parseIdentifier($expr->right); + + return $left . ' ^ ' . $right; + } + + private function parseBitwiseNot(mixed $expr) + { + $var = $this->parseIdentifier($expr->expr); + return '~' . $var; + } + + private function parseIf(mixed $v): string + { + $cond = $this->parseExpr($v->cond); + + $code = 'if (' . $cond . ') {' . PHP_EOL; + $this->indentLevel++; + $code .= $this->parseStmts($v->stmts); + $this->indentLevel--; + $code .= $this->getIndent() . '}'; + + if ($v->elseifs) { + foreach ($v->elseifs as $elseif) { + $elseifCond = $this->parseExpr($elseif->cond); + $code .= ' else if (' . $elseifCond . ') {' . PHP_EOL; + $this->indentLevel++; + $code .= $this->parseStmts($elseif->stmts); + $this->indentLevel--; + $code .= $this->getIndent() . '}'; + } + } + + if ($v->else) { + $code .= ' else {' . PHP_EOL; + $this->indentLevel++; + $code .= $this->parseStmts($v->else->stmts); + $this->indentLevel--; + $code .= $this->getIndent() . '}'; + } + + return $code . PHP_EOL; + } + + private function parseBinaryOpEqual(mixed $expr): string + { + $left = $this->parseIdentifier($expr->left); + $right = $this->parseIdentifier($expr->right); + + return $left . ' == ' . $right; + } + private function parseBinaryOpNotEqual(mixed $expr) + { + $left = $this->parseIdentifier($expr->left); + $right = $this->parseIdentifier($expr->right); + + return $left . ' != ' . $right; + } + + private function parseBinaryOpLogicalAnd(mixed $expr): string + { + $left = $this->parseIdentifier($expr->left); + $right = $this->parseIdentifier($expr->right); + + return $left . ' && ' . $right; + } + + private function parseBinaryOpLogicalOr(mixed $expr): string + { + $left = $this->parseIdentifier($expr->left); + $right = $this->parseIdentifier($expr->right); + + return $left . ' || ' . $right; + } + + private function parseBinaryOpLogicalXor(mixed $expr): string + { + $left = $this->parseIdentifier($expr->left); + $right = $this->parseIdentifier($expr->right); + + return $left . ' ^ ' . $right; + } + + private function parseBooleanNot(mixed $expr): string + { + $expr = $this->parseExpr($expr->expr); + return '!' . $expr; + } + + private function parseWhile(mixed $v): string + { + $cond = $this->parseExpr($v->cond); + $stmts = $v->stmts; + + $code = 'while (' . $cond . ') {' . PHP_EOL; + $this->indentLevel++; + $code .= $this->parseStmts($stmts); + $this->indentLevel--; + $code .= $this->getIndent() . '}' . PHP_EOL; + + return $code; + } + + private function parseBinaryOpSmallerOrEqual(mixed $expr): string + { + $left = $this->parseIdentifier($expr->left); + $right = $this->parseIdentifier($expr->right); + + return $left . ' <= ' . $right; + } + + private function parseBinaryOpGreaterOrEqual(mixed $expr): string + { + $left = $this->parseIdentifier($expr->left); + $right = $this->parseIdentifier($expr->right); + + return $left . ' >= ' . $right; + } + + private function parsePrint(mixed $expr): string + { + return 'php::echo(' . $this->parseExpr($expr->expr) . ')'; + } + + private function parseDo(mixed $v): string + { + $stmts = $v->stmts; + $cond = $this->parseExpr($v->cond); + + $code = 'do {' . PHP_EOL; + $this->indentLevel++; + $code .= $this->parseStmts($stmts); + $this->indentLevel--; + $code .= $this->getIndent() . '} while (' . $cond . ');' . PHP_EOL; + + return $code; + } + + private function parseBinaryOpIdentical(mixed $expr): string + { + $left = $this->parseIdentifier($expr->left); + $right = $this->parseIdentifier($expr->right); + + return 'php::same(' . $left . ', ' . $right . ')'; + } }