gdscript/php/classParser.php (236 lines of code) (raw):
<?php
$files = [];
$classes = scandir("./godot-master/doc/classes");
foreach ($classes as $filename) {
$files[] = sprintf("./godot-master/doc/classes/%s", $filename);
}
// Search modules
$modules = scandir("./godot-master/modules");
foreach ($modules as $module) {
//$files[] = sprintf("./classes/%s", $filename);
$modulePath = sprintf("./godot-master/modules/%s", $module);
if (substr($module, 0, 1) == ".") continue;
if (is_dir($modulePath)) {
$modulePath = sprintf("%s/doc_classes", $modulePath);
if (is_dir($modulePath)) {
$classes = scandir($modulePath);
foreach ($classes as $filename) {
$files[] = sprintf("%s/%s", $modulePath, $filename);
}
}
}
}
$target = "./classesGd/%s.gd";
$moduleTarget = "./classesGd/modules/%s.gd";
$addDocumentation = function($data, $desc, $brief = null, $tutorials = null, $deprecated = false, $experimental = false) {
if (!$brief && !$desc && !$tutorials && !$deprecated && !$experimental) {
return $data;
}
$addSpace = false;
if ($brief) {
$addSpace = true;
$lines = explode("\n", $brief);
foreach ($lines as $line) {
$line = trim($line);
if ($line) {
$data .= "## $line\n";
}
}
}
if ($desc) {
if ($addSpace) {
$data .= "##\n";
}
$addSpace = true;
$lines = explode("\n", $desc);
foreach ($lines as $line) {
$line = trim($line);
if ($line) {
$data .= "## $line\n";
}
}
}
if ($tutorials) {
if ($addSpace) {
$data .= "##\n";
}
foreach ($tutorials as $tutorial) {
$name = $tutorial[0];
if ($name) {
$name = "($name)";
}
$data .= sprintf("## @tutorial%s: %s\n", $name, $tutorial[1]);
}
}
if ($deprecated == "true") {
$data .= "## @deprecated\n";
}
if ($experimental == "true") {
$data .= "## @experimental\n";
}
return $data;
};
$formatType = function($type) {
if (substr($type, -2) == "[]") {
$type = substr($type, 0, strlen($type) - 2);
return sprintf("Array[%s]", $type);
}
return $type;
};
$parseParams = function($value) {
$params = [];
$parsed = [];
$list = $value['param'] ?? [];
if (!is_array($list)) {
$list = [$list];
}
foreach ($list as $param) {
$param = (array) $param;
$p_att = $param['@attributes'];
$p_name = $p_att['name'];
if ($p_name == "var") $p_name = "variable";
$parsed[$p_att['index']] = [$p_name, $p_att['type'], $p_att['default'] ?? null];
}
foreach ($parsed as $param) {
$paramType = $param[1];
if (str_ends_with($paramType, '[]')) {
$paramType = substr($paramType, 0, strlen($paramType) - 2);
$paramType = "Array[$paramType]";
}
$p = sprintf("%s: %s", $param[0], $paramType);
if (($param[2] ?? null) !== null) {
$p = sprintf("%s = %s", $p, $param[2]);
}
$params[] = $p;
}
return $params;
};
foreach ($files as $filepath) {
$paths = explode("/", $filepath);
$filename = $paths[count($paths) - 1];
if ($filename == "." || $filename == "..") continue;
if (substr($filename, strlen($filename) - 4) != ".xml") continue;
$data = "";
$content = file_get_contents($filepath);
$xml = (array) simplexml_load_string($content);
$att = (array) $xml['@attributes'];
$class_name = str_replace("@", "_", $att['name'] ?? substr($filename, 0, strlen($filename) - 4));
if ($att['inherits'] ?? null) {
$data .= sprintf("extends %s\n", $att['inherits']);
}
$data .= sprintf("class_name %s\n\n", $class_name);
/** Description */
$baseTutorialUrl = 'https://docs.godotengine.org/en/stable';
$desc = (array) ($xml['brief_description'] ?? []);
$brief = $desc['0'] ?? "";
$desc = (array) ($xml['description'] ?? []);
$description = $desc['0'] ?? "";
$links = (array) ($xml['tutorials'] ?? []);
$tutorials = [];
foreach ($xml['tutorials'] as $link) {
$link = (array) $link;
$p_att = $link['@attributes'];
$value = str_replace('$DOCS_URL', $baseTutorialUrl, $link[0]);
$tutorials[] = [$p_att['title'] ?? null, $value];
}
$data = $addDocumentation($data, $description, $brief, $tutorials, $att['is_deprecated'] ?? false, $att['is_experimental'] ?? false);
$data .= "\n\n";
/** Signals */
foreach ($xml['signals'] ?? [] as $value) {
$value = (array) $value;
$att = (array) $value['@attributes'];
$params = $parseParams($value);
$desc = (array) ($value['description'] ?? []);
$data = $addDocumentation($data, $desc['0'] ?? '', null, null, $att['is_deprecated'] ?? false, $att['is_experimental'] ?? false);
$paramsStr = implode(', ', $params);
if ($paramsStr) {
$paramsStr = sprintf("(%s)", $paramsStr);
}
$data .= sprintf("signal %s%s\n", $att['name'], $paramsStr);
}
$enums = [];
/** Constants */
foreach ($xml['constants'] ?? [] as $value) {
$value = (array) $value;
$att = (array) $value['@attributes'];
$data = $addDocumentation($data, $value['0'] ?? null, null, null, $att['is_deprecated'] ?? false, $att['is_experimental'] ?? false);
$enum = $att['enum'] ?? null;
if ($enum) {
$enums[$enum][] = sprintf("%s = %s,\n", $att['name'], $att['value']);
} else {
$data .= sprintf("const %s = %s;\n\n", $att['name'], $att['value']);
}
}
$data .= "\n";
foreach ($enums as $key => $values) {
$data .= "#enum $key\n";
$data .= "enum {\n";
foreach ($values as $value) {
$data .= " $value";
}
$data .= "}\n";
}
// enums can be accesses both ways as named & unnamed
foreach ($enums as $key => $values) {
if (str_contains($key, '.')) {
continue;
}
$data .= "#enum $key\n";
$data .= "enum $key {\n";
foreach ($values as $value) {
$data .= " $value";
}
$data .= "}\n";
}
/** Variables */
$getSetMethods = "";
// TODO should this be incorporated into hints?
if ($filename != 'ProjectSettings.xml') {
foreach ($xml['members'] ?? [] as $value) {
$value = (array) $value;
$att = (array) $value['@attributes'];
$data = $addDocumentation($data, $value['0'] ?? null, null, null, $att['is_deprecated'] ?? false, $att['is_experimental'] ?? false);
$data .= sprintf("var %s: %s", $att['name'], $formatType($att['type']));
$getter = $att['getter'] ?? null ? sprintf("get = %s", $att['getter']) : null;
$setter = $att['setter'] ?? null ? sprintf("set = %s", $att['setter']) : null;
$getSet = [];
if ($getter) {
$getSet[] = $getter;
$getSetMethods .= sprintf("func %s() -> %s:\n\treturn %s\n\n", $att['getter'], $formatType($att['type']), $att['name']);
}
if ($setter) {
$getSet[] = $setter;
$getSetMethods .= sprintf("func %s(value: %s) -> void:\n\t%s = value\n\n", $att['setter'], $formatType($att['type']), $att['name']);
}
if ($getSet) {
$data .= ":\n\t";
$data .= implode(', ', $getSet);
}
$data .= "\n\n";
}
$data .= "\n";
}
/** Constructor methods */
foreach ($xml['constructors'] ?? [] as $value) {
$value = (array) $value;
$att = (array) $value['@attributes'];
$ret = (array) $value['return'];
$params = $parseParams($value);
$desc = (array) ($value['description'] ?? []);
$data = $addDocumentation($data, $desc['0'] ?? '', null, null, $att['is_deprecated'] ?? false, $att['is_experimental'] ?? false);
$data .= sprintf("func %s(%s) -> %s:\n", $att['name'], implode(', ', $params), $formatType($ret['@attributes']['type']));
$data .= sprintf("\tpass;\n\n");
}
$data .= "\n";
/** Methods */
foreach ($xml['methods'] ?? [] as $value) {
$value = (array) $value;
$att = (array) $value['@attributes'];
$ret = (array) ($value['return'] ?? ['@attributes' => [ 'type' => 'void' ]]);
$quali = explode(' ', $att['qualifiers'] ?? '');
// TODO Delete this when const func is implemented .. attention there is also "virtual" .. e.g. _init
$allowed = ["static", "vararg"];
$quali = array_filter($quali, fn($qualiItem) => in_array($qualiItem, $allowed));
$quali = implode(' ', $quali);
if ($quali) {
$quali .= " ";
}
$params = $parseParams($value);
$desc = (array) ($value['description'] ?? []);
$data = $addDocumentation($data, $desc['0'] ?? '', null, null, $att['is_deprecated'] ?? false, $att['is_experimental'] ?? false);
$data .= sprintf("%sfunc %s(%s) -> %s:\n", $quali, $att['name'], implode(', ', $params), $formatType($ret['@attributes']['type']));
$data .= sprintf("\tpass;\n\n");
}
$data .= "\n";
$data .= $getSetMethods;
file_put_contents(sprintf($target, $class_name), $data);
}