summaryrefslogtreecommitdiff
path: root/protocols/Telegram/tdlib/td/td/generate/TlDocumentationGenerator.php
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Telegram/tdlib/td/td/generate/TlDocumentationGenerator.php')
-rw-r--r--protocols/Telegram/tdlib/td/td/generate/TlDocumentationGenerator.php306
1 files changed, 306 insertions, 0 deletions
diff --git a/protocols/Telegram/tdlib/td/td/generate/TlDocumentationGenerator.php b/protocols/Telegram/tdlib/td/td/generate/TlDocumentationGenerator.php
new file mode 100644
index 0000000000..11a281ce38
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/td/generate/TlDocumentationGenerator.php
@@ -0,0 +1,306 @@
+<?php
+
+abstract class TlDocumentationGenerator
+{
+ private $current_line = '';
+ private $documentation = array();
+ private $line_replacement = array();
+
+ final protected function printError($error)
+ {
+ fwrite(STDERR, "$error near line \"".rtrim($this->current_line)."\"\n");
+ }
+
+ final protected function addDocumentation($code, $doc) {
+ if (isset($this->documentation[$code])) {
+ $this->printError("Duplicate documentation for \"$code\"");
+ }
+
+ $this->documentation[$code] = $doc;
+ // $this->printError($code);
+ }
+
+ final protected function addLineReplacement($line, $new_line) {
+ if (isset($this->line_replacement[$line])) {
+ $this->printError("Duplicate line replacement for \"$line\"");
+ }
+
+ $this->line_replacement[$line] = $new_line;
+ }
+
+ final protected function addDot($str) {
+ if (!$str) {
+ return '';
+ }
+
+ $len = strlen($str);
+ if ($str[$len - 1] === '.') {
+ return $str;
+ }
+
+ if ($str[$len - 1] === ')') {
+ // trying to place dot inside the brackets
+ $bracket_count = 1;
+ for ($pos = $len - 2; $pos >= 0; $pos--) {
+ if ($str[$pos] === ')') {
+ $bracket_count++;
+ }
+ if ($str[$pos] === '(') {
+ $bracket_count--;
+ if ($bracket_count === 0) {
+ break;
+ }
+ }
+ }
+ if ($bracket_count === 0) {
+ if (ctype_upper($str[$pos + 1])) {
+ return substr($str, 0, -1).'.)';
+ }
+ } else {
+ $this->printError("Unmatched bracket");
+ }
+ }
+ return $str.'.';
+ }
+
+ abstract protected function escapeDocumentation($doc);
+
+ abstract protected function getFieldName($name, $class_name);
+
+ abstract protected function getClassName($name);
+
+ abstract protected function getTypeName($type);
+
+ abstract protected function getBaseClassName($is_function);
+
+ abstract protected function needRemoveLine($line);
+
+ abstract protected function needSkipLine($line);
+
+ abstract protected function isHeaderLine($line);
+
+ abstract protected function extractClassName($line);
+
+ abstract protected function fixLine($line);
+
+ abstract protected function addGlobalDocumentation();
+
+ abstract protected function addAbstractClassDocumentation($class_name, $value);
+
+ abstract protected function addClassDocumentation($class_name, $base_class_name, $description, $return_type);
+
+ abstract protected function addFieldDocumentation($class_name, $field_name, $type_name, $field_info, $may_be_null);
+
+ abstract protected function addDefaultConstructorDocumentation($class_name);
+
+ abstract protected function addFullConstructorDocumentation($class_name, $known_fields, $info);
+
+ public function generate($tl_scheme_file, $source_file)
+ {
+ $lines = array_filter(array_map('trim', file($tl_scheme_file)));
+ $description = '';
+ $current_class = '';
+ $is_function = false;
+ $need_class_description = false;
+
+ $this->addGlobalDocumentation();
+
+ foreach ($lines as $line) {
+ $this->current_line = $line;
+ if ($line === '---types---') {
+ $is_function = false;
+ } elseif ($line === '---functions---') {
+ $is_function = true;
+ $current_class = '';
+ $need_class_description = false;
+ } elseif ($line[0] === '/') {
+ if ($line[1] !== '/') {
+ $this->printError('Wrong comment');
+ continue;
+ }
+ if ($line[2] === '@' || $line[2] === '-') {
+ $description .= trim(substr($line, 2 + intval($line[2] === '-'))).' ';
+ } else {
+ $this->printError('Unexpected comment');
+ }
+ } elseif (strpos($line, '? =') || strpos($line, ' = Vector t;') || $line === 'boolFalse = Bool;' ||
+ $line === 'boolTrue = Bool;' || $line === 'bytes = Bytes;' || $line === 'int32 = Int32;' ||
+ $line === 'int53 = Int53;'|| $line === 'int64 = Int64;') {
+ // skip built-in types
+ continue;
+ } else {
+ $description = trim($description);
+ if ($description[0] !== '@') {
+ $this->printError('Wrong description begin');
+ }
+
+ $docs = explode('@', $description);
+ array_shift($docs);
+ $info = array();
+
+ foreach ($docs as $doc) {
+ list($key, $value) = explode(' ', $doc, 2);
+ $value = trim($value);
+
+ if ($need_class_description) {
+ if ($key === 'description') {
+ $need_class_description = false;
+
+ $value = $this->addDot($value);
+
+ $this->addAbstractClassDocumentation($current_class, $value);
+ continue;
+ } else {
+ $this->printError('Expected abstract class description');
+ }
+ }
+
+ if ($key === 'class') {
+ $current_class = $this->getClassName($value);
+ $need_class_description = true;
+
+ if ($is_function) {
+ $this->printError('Unexpected class definition');
+ }
+ } else {
+ if (isset($info[$key])) {
+ $this->printError("Duplicate info about `$key`");
+ }
+ $info[$key] = trim($value);
+ }
+ }
+
+ if (substr_count($line, '=') !== 1) {
+ $this->printError("Wrong '=' count");
+ continue;
+ }
+
+ list($fields, $type) = explode('=', $line);
+ $type = $this->getClassName($type);
+ $fields = explode(' ', trim($fields));
+ $class_name = $this->getClassName(array_shift($fields));
+
+ if ($type !== $current_class) {
+ $current_class = '';
+ $need_class_description = false;
+ }
+
+ if (!$is_function) {
+ $type_lower = strtolower($type);
+ $class_name_lower = strtolower($class_name);
+ if (empty($current_class) === ($type_lower !== $class_name_lower)) {
+ $this->printError('Wrong constructor name');
+ }
+ if (strpos($class_name_lower, $type_lower) !== 0) {
+ // $this->printError('Wrong constructor name');
+ }
+ }
+
+ $known_fields = array();
+ foreach ($fields as $field) {
+ list ($field_name, $field_type) = explode(':', $field);
+ if (isset($info['param_'.$field_name])) {
+ $known_fields['param_'.$field_name] = $field_type;
+ continue;
+ }
+ if (isset($info[$field_name])) {
+ $known_fields[$field_name] = $field_type;
+ continue;
+ }
+ $this->printError("Have no info about field `$field_name`");
+ }
+
+ foreach ($info as $name => $value) {
+ if (!$value) {
+ $this->printError("info[$name] for $class_name is empty");
+ } elseif ($value[0] < 'A' || $value[0] > 'Z') {
+ $this->printError("info[$name] for $class_name doesn't begins with capital letter");
+ }
+ }
+
+ foreach (array_diff_key($info, $known_fields) as $field_name => $field_info) {
+ if ($field_name !== 'description') {
+ $this->printError("Have info about unexisted field `$field_name`");
+ }
+ }
+
+ if (!$info['description']) {
+ $this->printError("Have no description for class `$class_name`");
+ }
+
+ foreach ($info as &$v) {
+ $v = $this->escapeDocumentation($this->addDot($v));
+ }
+
+ $base_class_name = $current_class ?: $this->getBaseClassName($is_function);
+ $this->addClassDocumentation($class_name, $base_class_name, $info['description'], $is_function ? $this->getTypeName($type) : '');
+
+ foreach ($known_fields as $name => $type) {
+ $may_be_null = stripos($info[$name], 'may be null') !== false;
+ $this->addFieldDocumentation($class_name, $this->getFieldName($name, $class_name), $this->getTypeName($type), $info[$name], $may_be_null);
+ }
+
+ $this->addDefaultConstructorDocumentation($class_name);
+
+ if ($known_fields) {
+ $this->addFullConstructorDocumentation($class_name, $known_fields, $info);
+ }
+
+ $description = '';
+ }
+ }
+
+ $lines = file($source_file);
+ $result = '';
+ $current_class = '';
+ $current_headers = '';
+ foreach ($lines as $line) {
+ $this->current_line = $line;
+ if ($this->needRemoveLine($line)) {
+ continue;
+ }
+ if ($this->needSkipLine($line)) {
+ $result .= $current_headers.$line;
+ $current_headers = '';
+ continue;
+ }
+ if ($this->isHeaderLine($line)) {
+ $current_headers .= $line;
+ continue;
+ }
+
+ $current_class = $this->extractClassName($line) ?: $current_class;
+
+ $fixed_line = rtrim($this->fixLine($line));
+
+ $doc = '';
+ if (isset($this->documentation[$fixed_line])) {
+ $doc = $this->documentation[$fixed_line];
+ // unset($this->documentation[$fixed_line]);
+ } elseif (isset($this->documentation[$current_class.$fixed_line])) {
+ $doc = $this->documentation[$current_class.$fixed_line];
+ // unset($this->documentation[$current_class.$fixed_line]);
+ } else {
+ $this->printError('Have no docs for "'.$fixed_line.'"');
+ }
+ if ($doc) {
+ $result .= $doc."\n";
+ }
+ if (isset($this->line_replacement[$fixed_line])) {
+ $line = $this->line_replacement[$fixed_line];
+ } elseif (isset($this->line_replacement[$current_class.$fixed_line])) {
+ $line = $this->line_replacement[$current_class.$fixed_line];
+ }
+ $result .= $current_headers.$line;
+ $current_headers = '';
+ }
+
+ if (file_get_contents($source_file) !== $result) {
+ file_put_contents($source_file, $result);
+ }
+
+ if (count($this->documentation)) {
+ // $this->printError('Have unused docs '.print_r(array_keys($this->documentation), true));
+ }
+ }
+}