函数定义处理

pull/1/head
韩天峰 8 months ago
parent 72b7ebdfae
commit f072f944b2
  1. 1
      bin/compiler.php
  2. 2
      src/Php/ArgInfo.php
  3. 5
      src/Php/FunctionDef.php
  4. 10
      src/Php/Reflection.php
  5. 102
      src/Php/Translator.php
  6. 93
      tests/aot/functions.phpt
  7. 140
      tests/aot/math_functions.phpt
  8. 156
      tests/aot/string_functions.phpt

@ -15,6 +15,7 @@ $code = $translator->convert($file);
$info = pathinfo($file);
$cppFile = $info['dirname'] . '/' . $info['filename'] . '.cc';
$translator->save($code, $cppFile);
$translator->genFunctionDeclaration("./php_func_decl.h");
$translator->compileFile($cppFile);
$objectFile = $info['dirname'] . '/' . $info['filename'] . '.cc.o';

@ -6,5 +6,5 @@ class ArgInfo
{
public string $name;
public string $type;
public string $default;
public string $default = '';
}

@ -5,7 +5,8 @@ namespace PhpAot\Php;
class FunctionDef
{
public string $name;
public array $arguments;
public array $argInfoList = [];
public string $params;
public string $returnType;
public int $argumentCountRequired = 0;
public int $argCountRequired = 0;
}

@ -11,7 +11,12 @@ class Reflection
static function getFunction(string $fn)
{
if (!isset(self::$functions[$fn])) {
self::$functions[$fn] = new ReflectionFunction($fn);
try {
$ref = new ReflectionFunction($fn);;
} catch (\ReflectionException $e) {
return null;
}
self::$functions[$fn] = $ref;
}
return self::$functions[$fn];
}
@ -19,6 +24,9 @@ class Reflection
static function getFunctionReturnType(string $fn): ?string
{
$func = self::getFunction($fn);
if (!$func) {
return null;
}
$returnType = $func->getReturnType();
if (!$returnType) {
return null;

@ -40,9 +40,49 @@ class Translator extends \PhpAot\Core\Translator
private array $headers = [
'phpx.h',
'phpx_func.h'
'phpx_func.h',
'php_func_decl.h',
];
private array $reservedNames = [
'auto',
'break',
'case',
'catch',
'class',
'struct',
'const',
'continue',
'default',
'do',
'else',
'elseif',
'enum',
'extends',
'final',
'finally',
'for',
'function',
'global',
'if',
'int',
'double',
'float',
'false',
'for',
'if',
'int',
'new',
'null',
'or',
'and',
'private',
'protected',
'public',
'return',
'static',
];
private array $internalFunctions = [];
private int $optimizeLevel = 0;
@ -315,15 +355,17 @@ class Translator extends \PhpAot\Core\Translator
} else {
$this->functionDef->returnType = 'void';
}
$this->parseParams($v->params);
$this->internalFunctions[$name] = $this->functionDef;
}
$params = $this->parseParams($v->params);
$this->indentLevel++;
$stmts = $this->parseStmts($v->stmts);
$this->indentLevel--;
$code = $this->getReturnType() . ' ' . self::PREFIX . $name . '(' . $params . ') {' . PHP_EOL;
$functionDeclCode = $this->getReturnType() . ' ' . self::PREFIX . $name . '(' . $this->functionDef->params . ')';
$code = $functionDeclCode . ' {' . PHP_EOL;
$this->indentLevel++;
foreach ($this->localVars as $name => $type) {
if (isset($this->arguments[$name])) {
@ -358,7 +400,7 @@ class Translator extends \PhpAot\Core\Translator
case 'Name':
case 'Identifier':
case 'Expr_Variable':
return $expr->name;
return $this->escapeVarName($expr->name);
case 'Scalar_Int':
return $expr->value . 'L';
case 'Scalar_Float':
@ -372,10 +414,10 @@ class Translator extends \PhpAot\Core\Translator
}
}
private function parseParams($params): string
private function parseParams($params): void
{
$list = [];
$this->functionDef->argumentCountRequired = count($params);
$this->functionDef->argCountRequired = count($params);
foreach ($params as $param) {
$type = $this->parseType($param->type);
$name = $this->parseIdentifier($param->var);
@ -387,12 +429,12 @@ class Translator extends \PhpAot\Core\Translator
$argInfo->name = $name;
$argInfo->type = $type;
if (isset($param->default)) {
$this->functionDef->argumentCountRequired = count($list) - 1;
$this->functionDef->argCountRequired = count($list) - 1;
$argInfo->default = $param->default->getAttribute('rawValue');
}
$this->functionDef->arguments[] = $argInfo;
$this->functionDef->argInfoList[] = $argInfo;
}
return implode(', ', $list);
$this->functionDef->params = implode(', ', $list);
}
private function parseStmts(array $stmts): string
@ -905,6 +947,7 @@ class Translator extends \PhpAot\Core\Translator
{
$list = [
$this->phpxDir . '/include',
'./',
];
$out = '$(php-config --includes) ';
foreach ($list as $li) {
@ -941,6 +984,7 @@ class Translator extends \PhpAot\Core\Translator
private function addCompilationOption(string &$cmd): void
{
$cmd .= $this->parseIncludes();
$cmd .= ' -O' . $this->optimizeLevel;
$cmd .= ' -g';
if ($this->climate->arguments->defined('profile')) {
@ -951,7 +995,7 @@ class Translator extends \PhpAot\Core\Translator
public function compileFile($file): void
{
$cmd = $this->cppCompiler . ' -c ' . $file . ' -o ' . $file . '.o ' . $this->parseIncludes();
$cmd = $this->cppCompiler . ' -c ' . $file . ' -o ' . $file . '.o ';
$this->addCompilationOption($cmd);
$this->climate->comment($cmd);
shell_exec($cmd);
@ -963,7 +1007,7 @@ class Translator extends \PhpAot\Core\Translator
if ($this->climate->arguments->defined('output')) {
$targetFile = $this->climate->arguments->get('output');
}
$cmd = $this->cppCompiler . ' main.cc global_vars.cc ' . $objectFile . ' -o ' . $targetFile . ' ' . $this->parseIncludes() . $this->parseLdflags() . $this->parseLibs();
$cmd = $this->cppCompiler . ' main.cc global_vars.cc ' . $objectFile . ' -o ' . $targetFile . ' ' . $this->parseLdflags() . $this->parseLibs();
$this->addCompilationOption($cmd);
$this->climate->comment($cmd);
shell_exec($cmd);
@ -1518,6 +1562,15 @@ class Translator extends \PhpAot\Core\Translator
return addcslashes($str, "\\\"\n\r\t\v\f\0\x01..\x1f\x7f..\xff");
}
private function escapeVarName(string $name): string
{
if (in_array($name, $this->reservedNames)) {
return '_php__var__' . $name;
} else {
return $name;
}
}
private function parseInterpolatedStringPart(Node $expr): string
{
return '"' . $this->escapeString($expr->value) . '"';
@ -1536,7 +1589,7 @@ class Translator extends \PhpAot\Core\Translator
private function getArgInfo(string $funcName, int $index): ArgInfo
{
$funcDef = $this->internalFunctions[$funcName];
return $funcDef->arguments[$index];
return $funcDef->argInfoList[$index];
}
private function getReturnType(): string
@ -1857,4 +1910,29 @@ class Translator extends \PhpAot\Core\Translator
}
return $expr;
}
public function genFunctionDeclaration(string $file): void
{
$code = '';
/**
* @var FunctionDef $func
*/
foreach ($this->internalFunctions as $name => $func) {
$code .= 'extern ' . $func->returnType . ' ' . self::PREFIX . $name . '(';
$argInfoList = $func->argInfoList;
if ($argInfoList) {
$list = [];
foreach ($argInfoList as $argInfo) {
$arg = $argInfo->type . ' ' . $argInfo->name;
if ($argInfo->default) {
$arg .= ' = ' . $argInfo->default;
}
$list[] = $arg;
}
$code .= implode(', ', $list);
}
$code .= ');' . PHP_EOL;
}
file_put_contents($file, $code);
}
}

@ -0,0 +1,93 @@
--TEST--
User-defined and Built-in Functions
--FILE--
<?php
// Test user-defined function
function add($a, $b) {
return $a + $b;
}
function multiply($x, $y) {
return $x * $y;
}
function greet($name) {
return "Hello, " . $name . "!";
}
function main() {
// Test function calls
$result1 = add(5, 3);
var_dump($result1);
$result2 = multiply(4, 7);
var_dump($result2);
$result3 = greet("World");
var_dump($result3);
// Test built-in functions
$test_string = " Hello World ";
$trimmed = trim($test_string);
var_dump($trimmed);
$upper = strtoupper($test_string);
var_dump($upper);
$len = strlen($trimmed);
var_dump($len);
// Test array functions
$numbers = [1, 2, 3, 4, 5];
$reversed = array_reverse($numbers);
var_dump($reversed);
$count = count($numbers);
var_dump($count);
$fact5 = factorial(5);
var_dump($fact5);
$greeting1 = greet_with_title("Smith");
var_dump($greeting1);
$greeting2 = greet_with_title("Jane", "Ms.");
var_dump($greeting2);
}
// Test recursive function
function factorial($n) {
if ($n <= 1) {
return 1;
}
return $n * factorial($n - 1);
}
// Test function with default parameters
function greet_with_title($name, $title = "Mr.") {
return $title . " " . $name;
}
?>
--EXPECT--
int(8)
int(28)
string(13) "Hello, World!"
string(11) "Hello World"
string(15) " HELLO WORLD "
int(11)
array(5) {
[0]=>
int(5)
[1]=>
int(4)
[2]=>
int(3)
[3]=>
int(2)
[4]=>
int(1)
}
int(5)
int(120)
string(9) "Mr. Smith"
string(8) "Ms. Jane"

@ -0,0 +1,140 @@
--TEST--
Mathematical Functions
--FILE--
<?php
// Test basic math functions
$abs = abs(-5);
var_dump($abs);
$abs_float = abs(-3.7);
var_dump($abs_float);
// Test ceil and floor
$ceil = ceil(4.2);
var_dump($ceil);
$floor = floor(4.8);
var_dump($floor);
$round = round(4.567, 2);
var_dump($round);
// Test min and max
$min = min(3, 7, 1, 9, 4);
var_dump($min);
$max = max(3, 7, 1, 9, 4);
var_dump($max);
// Test with arrays
$numbers = [5, 2, 8, 1, 9];
$min_arr = min($numbers);
var_dump($min_arr);
$max_arr = max($numbers);
var_dump($max_arr);
// Test pow function
$power = pow(2, 8);
var_dump($power);
// Test sqrt
$sqrt = sqrt(64);
var_dump($sqrt);
$sqrt_float = sqrt(2);
var_dump($sqrt_float);
// Test rand and mt_rand (within a range for predictability)
srand(12345); // For predictable results in tests
$rand1 = rand(1, 100);
var_dump($rand1);
mt_srand(12345); // For predictable results in tests
$rand2 = mt_rand(1, 100);
var_dump($rand2);
// Test number format
$number = 1234.5678;
$formatted = number_format($number, 2);
var_dump($formatted);
// Test fmod
$fmod = fmod(10.5, 3.2);
var_dump($fmod);
// Test trigonometric functions
$sin = sin(pi()/2); // Should be 1
var_dump($sin);
$cos = cos(0); // Should be 1
var_dump($cos);
$tan = tan(pi()/4); // Should be close to 1
var_dump($tan);
// Test logarithmic functions
$log = log(M_E); // Natural log of e should be 1
var_dump($log);
$log10 = log10(100); // Base-10 log of 100 should be 2
var_dump($log10);
// Test exponential
$exp = exp(1); // e^1 should be e
var_dump($exp);
// Test is_* functions
$int_val = 42;
$float_val = 3.14;
$str_val = "hello";
var_dump(is_int($int_val));
var_dump(is_float($float_val));
var_dump(is_numeric("123"));
var_dump(is_numeric("abc"));
// Test conversion functions
$hex = dechex(255);
var_dump($hex);
$bin = decbin(8);
var_dump($bin);
$oct = decoct(8);
var_dump($oct);
$back_to_dec = hexdec($hex);
var_dump($back_to_dec);
?>
--EXPECT--
int(5)
float(3.7)
float(5)
float(4)
float(4.57)
int(1)
int(9)
int(1)
int(9)
int(256)
float(8)
float(1.4142135623730951)
int(91)
int(91)
string(8) "1,234.57"
float(0.8999999999999995)
float(1)
float(1)
float(0.9999999999999999)
float(1)
float(2)
float(2.718281828459045)
bool(true)
bool(true)
bool(true)
bool(false)
string(2) "ff"
string(4) "1000"
string(2) "10"
int(255)

@ -0,0 +1,156 @@
--TEST--
String Functions
--FILE--
<?php
// Test basic string functions
$str = "Hello, World!";
$len = strlen($str);
var_dump($len);
$upper = strtoupper($str);
var_dump($upper);
$lower = strtolower($str);
var_dump($lower);
// Test substr
$substr = substr($str, 0, 5);
var_dump($substr);
$substr2 = substr($str, 7);
var_dump($substr2);
// Test strpos
$pos = strpos($str, "World");
var_dump($pos);
// Test str_replace
$replaced = str_replace("World", "PHP", $str);
var_dump($replaced);
// Test trim functions
$spaced = " Hello, World! ";
$trimmed = trim($spaced);
var_dump($trimmed);
$ltrimmed = ltrim($spaced);
var_dump($ltrimmed);
$rtrimmed = rtrim($spaced);
var_dump($rtrimmed);
// Test string concatenation
$part1 = "Hello";
$part2 = "World";
$concatenated = $part1 . " " . $part2 . "!";
var_dump($concatenated);
// Test explode and implode
$csv = "apple,banana,orange";
$fruits = explode(",", $csv);
var_dump($fruits);
$joined = implode("-", $fruits);
var_dump($joined);
// Test comparison functions
$cmp1 = strcmp("abc", "abc");
var_dump($cmp1);
$cmp2 = strcmp("abc", "def");
var_dump($cmp2);
$cmp3 = strcasecmp("ABC", "abc");
var_dump($cmp3);
// Test string formatting
$formatted = sprintf("Value: %d, Text: %s", 42, "hello");
var_dump($formatted);
// Test chunk split
$chunked = str_split("abcdefghij", 3);
var_dump($chunked);
// Test strrev
$reversed = strrev("hello");
var_dump($reversed);
// Test word functions
$word_count = str_word_count("Hello world!");
var_dump($word_count);
// Test ucfirst and ucwords
$mixed_case = "hello world from PHP";
$first_upper = ucfirst($mixed_case);
var_dump($first_upper);
$title_case = ucwords($mixed_case);
var_dump($title_case);
// Test number conversion from string
$num_str = "123";
$num = (int)$num_str;
var_dump($num);
$float_str = "45.67";
$float = (float)$float_str;
var_dump($float);
// Test string padding
$padded = str_pad("Hello", 10, "0", STR_PAD_LEFT);
var_dump($padded);
// Test string repeat
$repeated = str_repeat("xy", 4);
var_dump($repeated);
// Test strip_tags
$html = "<p>Hello <b>World</b>!</p>";
$stripped = strip_tags($html);
var_dump($stripped);
?>
--EXPECT--
int(13)
string(13) "HELLO, WORLD!"
string(13) "hello, world!"
string(5) "Hello"
string(6) "World!"
int(7)
string(11) "Hello, PHP!"
string(13) "Hello, World!"
string(15) "Hello, World! "
string(15) " Hello, World!"
string(12) "Hello World!"
array(3) {
[0]=>
string(5) "apple"
[1]=>
string(6) "banana"
[2]=>
string(6) "orange"
}
string(19) "apple-banana-orange"
int(0)
int(-3)
int(0)
string(22) "Value: 42, Text: hello"
array(4) {
[0]=>
string(3) "abc"
[1]=>
string(3) "def"
[2]=>
string(3) "ghi"
[3]=>
string(1) "j"
}
string(5) "olleh"
int(2)
string(20) "Hello world from PHP"
string(20) "Hello World From PHP"
int(123)
float(45.67)
string(10) "00000Hello"
string(8) "xyxyxyxy"
string(12) "Hello World!"
Loading…
Cancel
Save