Yaffs site version 1.1
[yaffs-website] / vendor / psy / psysh / bin / build-manual
1 #!/usr/bin/env php
2 <?php
3
4 /*
5  * This file is part of Psy Shell.
6  *
7  * (c) 2012-2017 Justin Hileman
8  *
9  * For the full copyright and license information, please view the LICENSE
10  * file that was distributed with this source code.
11  */
12
13 define('WRAP_WIDTH', 100);
14
15 $count = 0;
16
17 if (count($argv) !== 3 || !is_dir($argv[1])) {
18     echo "usage: build_manual path/to/manual output_filename.db\n";
19     exit(1);
20 }
21
22 function htmlwrap($text, $width = null)
23 {
24     if ($width === null) {
25         $width = WRAP_WIDTH;
26     }
27
28     $len = strlen($text);
29
30     $return = array();
31     $lastSpace = null;
32     $inTag = false;
33     $i = $tagWidth = 0;
34     do {
35         switch (substr($text, $i, 1)) {
36             case "\n":
37                 $return[] = trim(substr($text, 0, $i));
38                 $text     = substr($text, $i);
39                 $len      = strlen($text);
40
41                 $i = $lastSpace = 0;
42                 continue;
43
44             case ' ':
45                 if (!$inTag) {
46                     $lastSpace = $i;
47                 }
48                 break;
49
50             case '<':
51                 $inTag = true;
52                 break;
53
54             case '>':
55                 $inTag = false;
56
57             default:
58         }
59
60         if ($inTag) {
61             $tagWidth++;
62         }
63
64         $i++;
65
66         if (!$inTag && ($i - $tagWidth > $width)) {
67             $lastSpace = $lastSpace ?: $width;
68
69             $return[] = trim(substr($text, 0, $lastSpace));
70             $text     = substr($text, $lastSpace);
71             $len      = strlen($text);
72
73             $i = $tagWidth = 0;
74         }
75     } while ($i < $len);
76
77     $return[] = trim($text);
78
79     return implode("\n", $return);
80 }
81
82 function extract_paragraphs($element)
83 {
84     $paragraphs = array();
85     foreach ($element->getElementsByTagName('para') as $p) {
86         $text = '';
87         foreach ($p->childNodes as $child) {
88             // @todo figure out if there's something we can do with tables.
89             if ($child instanceof DOMElement && $child->tagName === 'table') {
90                 continue;
91             }
92
93             // skip references, because ugh.
94             if (preg_match('{^\s*&[a-z][a-z\.]+;\s*$}', $child->textContent)) {
95                 continue;
96             }
97
98             $text .= $child->ownerDocument->saveXML($child);
99         }
100
101         if ($text = trim(preg_replace('{\n[ \t]+}', ' ', $text))) {
102             $paragraphs[] = $text;
103         }
104     }
105
106     return implode("\n\n", $paragraphs);
107 }
108
109 function format_doc($doc)
110 {
111     $chunks   = array();
112
113     if (!empty($doc['description'])) {
114         $chunks[] = '<comment>Description:</comment>';
115         $chunks[] = indent_text(htmlwrap(thunk_tags($doc['description']), WRAP_WIDTH - 2));
116         $chunks[] = '';
117     }
118
119     if (!empty($doc['params'])) {
120         $chunks[] = '<comment>Param:</comment>';
121
122         $typeMax = max(array_map(function ($param) {
123             return strlen($param['type']);
124         }, $doc['params']));
125
126         $max = max(array_map(function ($param) {
127             return strlen($param['name']);
128         }, $doc['params']));
129
130         $template  = '  <info>%-' . $typeMax . 's</info>  <strong>%-' . $max . 's</strong>  %s';
131         $indent    = str_repeat(' ', $typeMax + $max + 6);
132         $wrapWidth = WRAP_WIDTH - strlen($indent);
133
134         foreach ($doc['params'] as $param) {
135             $desc = indent_text(htmlwrap(thunk_tags($param['description']), $wrapWidth), $indent, false);
136             $chunks[] = sprintf($template, $param['type'], $param['name'], $desc);
137         }
138         $chunks[] = '';
139     }
140
141     if (isset($doc['return']) || isset($doc['return_type'])) {
142         $chunks[] = '<comment>Return:</comment>';
143
144         $type   = isset($doc['return_type']) ? $doc['return_type'] : 'unknown';
145         $desc   = isset($doc['return']) ? $doc['return'] : '';
146
147         $indent    = str_repeat(' ', strlen($type) + 4);
148         $wrapWidth = WRAP_WIDTH - strlen($indent);
149
150         if (!empty($desc)) {
151             $desc = indent_text(htmlwrap(thunk_tags($doc['return']), $wrapWidth), $indent, false);
152         }
153
154         $chunks[] = sprintf('  <info>%s</info>  %s', $type, $desc);
155         $chunks[] = '';
156     }
157
158     array_pop($chunks); // get rid of the trailing newline
159
160     return implode("\n", $chunks);
161 }
162
163 function thunk_tags($text)
164 {
165     $tagMap = array(
166         'parameter>' => 'strong>',
167         'function>'  => 'strong>',
168         'literal>'   => 'return>',
169         'type>'      => 'info>',
170         'constant>'  => 'info>',
171     );
172
173     $andBack = array(
174         '&amp;'       => '&',
175         '&amp;true;'  => '<return>true</return>',
176         '&amp;false;' => '<return>false</return>',
177         '&amp;null;'  => '<return>null</return>',
178     );
179
180     return strtr(strip_tags(strtr($text, $tagMap), '<strong><return><info>'), $andBack);
181 }
182
183 function indent_text($text, $indent = '  ', $leading = true)
184 {
185     return ($leading ? $indent : '') . str_replace("\n", "\n" . $indent, $text);
186 }
187
188 function find_type($xml, $paramName)
189 {
190     foreach ($xml->getElementsByTagName('methodparam') as $param) {
191         if ($type = $param->getElementsByTagName('type')->item(0)) {
192             if ($parameter = $param->getElementsByTagName('parameter')->item(0)) {
193                 if ($paramName === $parameter->textContent) {
194                     return $type->textContent;
195                 }
196             }
197         }
198     }
199 }
200
201 $docs = array();
202 foreach (glob($argv[1] . '/*/*/*.xml') as $function) {
203     $funcname = basename($function);
204     if ($funcname === 'main.xml' || strpos($funcname, 'entities.') === 0) {
205         continue;
206     }
207
208     $xmlstr = str_replace('&', '&amp;', file_get_contents($function));
209
210     $xml = new DOMDocument();
211     $xml->preserveWhiteSpace = false;
212
213     if (!@$xml->loadXml($xmlstr)) {
214         echo "XML Parse Error: $function\n";
215         continue;
216     }
217
218     $doc = array();
219     $refsect1s = $xml->getElementsByTagName('refsect1');
220     foreach ($refsect1s as $refsect1) {
221         $role = $refsect1->getAttribute('role');
222         switch ($role) {
223             case 'description':
224                 $doc['description'] = extract_paragraphs($refsect1);
225
226                 if ($synopsis = $refsect1->getElementsByTagName('methodsynopsis')->item(0)) {
227                     foreach ($synopsis->childNodes as $node) {
228                         if ($node instanceof DOMElement && $node->tagName === 'type') {
229                             $doc['return_type'] = $node->textContent;
230                             break;
231                         }
232                     }
233                 }
234                 break;
235
236             case 'returnvalues':
237                 // do nothing.
238                 $doc['return'] = extract_paragraphs($refsect1);
239                 break;
240
241             case 'parameters':
242                 $params = array();
243                 $vars = $refsect1->getElementsByTagName('varlistentry');
244                 foreach ($vars as $var) {
245                     if ($name = $var->getElementsByTagName('parameter')->item(0)) {
246                         $params[] = array(
247                             'name'        => '$' . $name->textContent,
248                             'type'        => find_type($xml, $name->textContent),
249                             'description' => extract_paragraphs($var),
250                         );
251                     }
252                 }
253
254                 $doc['params'] = $params;
255                 break;
256         }
257     }
258
259     // and the purpose
260     if ($purpose = $xml->getElementsByTagName('refpurpose')->item(0)) {
261         $desc = htmlwrap($purpose->textContent);
262         if (isset($doc['description'])) {
263             $desc .= "\n\n" . $doc['description'];
264         }
265
266         $doc['description'] = trim($desc);
267     }
268
269     $formatted = format_doc($doc);
270     foreach ($xml->getElementsByTagName('refname') as $ref) {
271         $docs[$ref->textContent] = $formatted;
272     }
273 }
274
275 if (is_file($argv[2])) {
276     unlink($argv[2]);
277 }
278
279 $db = new PDO('sqlite:' . $argv[2]);
280
281 $db->query('CREATE TABLE php_manual (id char(256) PRIMARY KEY, doc TEXT)');
282 $cmd = $db->prepare('INSERT INTO php_manual (id, doc) VALUES (?, ?)');
283 foreach ($docs as $id => $doc) {
284     $cmd->execute(array($id, $doc));
285 }