Rev 74 | Rev 99 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 74 | Rev 82 | ||
---|---|---|---|
Line 1... | Line 1... | ||
1 | <?php |
1 | <?php |
2 | 2 | ||
3 | // TODO: show full signature of each element? |
3 | // TODO: show full signature of each element? |
4 | 4 | ||
5 | error_reporting(0); |
5 | error_reporting(0); |
6 | 6 | ||
7 | require_once __DIR__ . '/codeexplorer_api.inc.php'; |
7 | require_once __DIR__ . '/codeexplorer_api.inc.php'; |
8 | 8 | ||
9 | define('ICON_TYPE_FUNCTION', 1); |
9 | define('ICON_TYPE_FUNCTION', 1); |
10 | define('ICON_TYPE_CONSTRUCTOR', 2); |
10 | define('ICON_TYPE_CONSTRUCTOR', 2); |
11 | define('ICON_TYPE_DESTRUCTOR', 3); |
11 | define('ICON_TYPE_DESTRUCTOR', 3); |
12 | define('ICON_TYPE_MAGICMETHOD', 4); |
12 | define('ICON_TYPE_MAGICMETHOD', 4); |
13 | define('ICON_TYPE_CLASS', 5); |
13 | define('ICON_TYPE_CLASS', 5); |
14 | define('ICON_TYPE_TRAIT', 6); |
14 | define('ICON_TYPE_TRAIT', 6); |
15 | define('ICON_TYPE_INTERFACE', 7); |
15 | define('ICON_TYPE_INTERFACE', 7); |
16 | define('ICON_TYPE_VAR', 8); |
16 | define('ICON_TYPE_VAR', 8); |
17 | define('ICON_TYPE_CONST', 9); |
17 | define('ICON_TYPE_CONST', 9); |
18 | define('ICON_TYPE_TODO', 10); |
18 | define('ICON_TYPE_TODO', 10); |
19 | define('ICON_TYPE_ERROR', 11); |
19 | define('ICON_TYPE_ERROR', 11); |
20 | 20 | ||
21 | class MyFastPHPIcon extends FastPHPIcon { |
21 | class MyFastPHPIcon extends FastPHPIcon { |
22 | 22 | ||
23 | public function imageIndex() { |
23 | public function imageIndex() { |
24 | if (($this->getType() == ICON_TYPE_CLASS) && (!$this->isAbstract())) return 0; // class |
24 | if (($this->getType() == ICON_TYPE_CLASS) && (!$this->isAbstract())) return 0; // class |
25 | else if (($this->getType() == ICON_TYPE_CLASS) && ( $this->isAbstract())) return 1; // abstract class |
25 | else if (($this->getType() == ICON_TYPE_CLASS) && ( $this->isAbstract())) return 1; // abstract class |
26 | else if (($this->getType() == ICON_TYPE_INTERFACE) ) return 2; // interface |
26 | else if (($this->getType() == ICON_TYPE_INTERFACE) ) return 2; // interface |
27 | else if (($this->getType() == ICON_TYPE_TRAIT) ) return 3; // trait |
27 | else if (($this->getType() == ICON_TYPE_TRAIT) ) return 3; // trait |
28 | else if (($this->getType() == ICON_TYPE_CONST) && ($this->isPrivate()) ) return 4; // private const |
28 | else if (($this->getType() == ICON_TYPE_CONST) && ($this->isPrivate()) ) return 4; // private const |
29 | else if (($this->getType() == ICON_TYPE_VAR) && ($this->isPrivate()) ) return 5; // private var |
29 | else if (($this->getType() == ICON_TYPE_VAR) && ($this->isPrivate()) ) return 5; // private var |
30 | else if (($this->isMethod()) && ($this->isPrivate()) && (!$this->isAbstract())) return 6; // private function |
30 | else if (($this->isMethod()) && ($this->isPrivate()) && (!$this->isAbstract())) return 6; // private function |
31 | else if (($this->isMethod()) && ($this->isPrivate()) && ( $this->isAbstract())) return 7; // private abstract function |
31 | else if (($this->isMethod()) && ($this->isPrivate()) && ( $this->isAbstract())) return 7; // private abstract function |
32 | else if (($this->getType() == ICON_TYPE_CONST) && ($this->isProtected()) ) return 8; // protected const |
32 | else if (($this->getType() == ICON_TYPE_CONST) && ($this->isProtected()) ) return 8; // protected const |
33 | else if (($this->getType() == ICON_TYPE_VAR) && ($this->isProtected()) ) return 9; // protected var |
33 | else if (($this->getType() == ICON_TYPE_VAR) && ($this->isProtected()) ) return 9; // protected var |
34 | else if (($this->isMethod()) && ($this->isProtected()) && (!$this->isAbstract())) return 10; // protected function |
34 | else if (($this->isMethod()) && ($this->isProtected()) && (!$this->isAbstract())) return 10; // protected function |
35 | else if (($this->isMethod()) && ($this->isProtected()) && ( $this->isAbstract())) return 11; // protected abstract function |
35 | else if (($this->isMethod()) && ($this->isProtected()) && ( $this->isAbstract())) return 11; // protected abstract function |
36 | else if (($this->getType() == ICON_TYPE_CONST) && ($this->isPublic()) ) return 12; // public const |
36 | else if (($this->getType() == ICON_TYPE_CONST) && ($this->isPublic()) ) return 12; // public const |
37 | else if (($this->getType() == ICON_TYPE_VAR) && ($this->isPublic()) ) return 13; // public var |
37 | else if (($this->getType() == ICON_TYPE_VAR) && ($this->isPublic()) ) return 13; // public var |
38 | else if (($this->isMethod()) && ($this->isPublic()) && (!$this->isAbstract())) return 14; // public function |
38 | else if (($this->isMethod()) && ($this->isPublic()) && (!$this->isAbstract())) return 14; // public function |
39 | else if (($this->isMethod()) && ($this->isPublic()) && ( $this->isAbstract())) return 15; // public abstract function |
39 | else if (($this->isMethod()) && ($this->isPublic()) && ( $this->isAbstract())) return 15; // public abstract function |
40 | else if (($this->getType() == ICON_TYPE_TODO) ) return 16; // To-Do comment |
40 | else if (($this->getType() == ICON_TYPE_TODO) ) return 16; // To-Do comment |
41 | else return -1; |
41 | else return -1; |
42 | } |
42 | } |
43 | 43 | ||
44 | public function isMethod() { |
44 | public function isMethod() { |
45 | return (($this->getType() == ICON_TYPE_FUNCTION) || |
45 | return (($this->getType() == ICON_TYPE_FUNCTION) || |
46 | ($this->getType() == ICON_TYPE_CONSTRUCTOR) || |
46 | ($this->getType() == ICON_TYPE_CONSTRUCTOR) || |
47 | ($this->getType() == ICON_TYPE_DESTRUCTOR) || |
47 | ($this->getType() == ICON_TYPE_DESTRUCTOR) || |
48 | ($this->getType() == ICON_TYPE_MAGICMETHOD)); |
48 | ($this->getType() == ICON_TYPE_MAGICMETHOD)); |
49 | } |
49 | } |
50 | 50 | ||
51 | } |
51 | } |
52 | 52 | ||
53 | class MyFastPHPCodeExplorer { |
53 | class MyFastPHPCodeExplorer { |
54 | 54 | ||
55 | public function handle($code) { |
55 | public function handle($code) { |
56 | // Quick'n'Dirty fix to correctly parse the line |
56 | // Quick'n'Dirty fix to correctly parse the line |
57 | // test(XYZ::class) |
57 | // test(XYZ::class) |
58 | $code = str_replace('::class', '', $code); |
58 | $code = str_replace('::class', '', $code); |
59 | 59 | ||
60 | // Quick'n'Dirty fix to correctly parse the line |
60 | // Quick'n'Dirty fix to correctly parse the line |
61 | // $verify=file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret={$secret}&response={$response}"); |
61 | // $verify=file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret={$secret}&response={$response}"); |
62 | $code = str_replace('{$', '{', $code); |
62 | $code = str_replace('{$', '{', $code); |
63 | 63 | ||
64 | $token = token_get_all($code); |
64 | $token = token_get_all($code); |
65 | $wait_function = false; |
65 | $wait_function = false; |
66 | $wait_const = false; |
66 | $wait_const = false; |
67 | $wait_class = false; |
67 | $wait_class = false; |
68 | $wait_trait = false; |
68 | $wait_trait = false; |
69 | $icon = new MyFastPHPIcon(); |
69 | $icon = new MyFastPHPIcon(); |
70 | $wait_interface = false; |
70 | $wait_interface = false; |
71 | $wait_abstract_func_list_end = false; |
71 | $wait_abstract_func_list_end = false; |
72 | $levelAry = array(); |
72 | $levelAry = array(); |
73 | $dep = 0; |
73 | $dep = 0; |
74 | $insideFuncAry = array(); |
74 | $insideFuncAry = array(); |
75 | 75 | ||
76 | if (!$token) { |
76 | if (!$token) { |
77 | $icon->setType(ICON_TYPE_ERROR); |
77 | $icon->setType(ICON_TYPE_ERROR); |
78 | FastPHPWriter::outputLeafNode($icon, 0, 'SYNTAX ERROR'); |
78 | FastPHPWriter::outputLeafNode($icon, 0, 'SYNTAX ERROR'); |
79 | } |
79 | } |
80 | 80 | ||
81 | foreach ($token as $data) { |
81 | foreach ($token as $data) { |
82 | if ($data == '{') $dep++; |
82 | if ($data == '{') $dep++; |
83 | if ($data == '}') { |
83 | if ($data == '}') { |
84 | $dep--; |
84 | $dep--; |
85 | if ((count($levelAry) > 0) && (self::array_peek($levelAry) == $dep)) { |
85 | if ((count($levelAry) > 0) && (self::array_peek($levelAry) == $dep)) { |
86 | array_pop($levelAry); |
86 | array_pop($levelAry); |
87 | FastPHPWriter::outputDecreaseLevel(); |
87 | FastPHPWriter::outputDecreaseLevel(); |
88 | } |
88 | } |
89 | if ((count($insideFuncAry) > 0) && (self::array_peek($insideFuncAry) == $dep)) { |
89 | if ((count($insideFuncAry) > 0) && (self::array_peek($insideFuncAry) == $dep)) { |
90 | array_pop($insideFuncAry); |
90 | array_pop($insideFuncAry); |
91 | } |
91 | } |
92 | } |
92 | } |
93 | 93 | ||
94 | $token = (!is_array($data)) ? null : $data[0]; |
94 | $token = (!is_array($data)) ? null : $data[0]; |
95 | $value = (!is_array($data)) ? null : $data[1]; |
95 | $value = (!is_array($data)) ? null : $data[1]; |
96 | $line = (!is_array($data)) ? null : $data[2]; |
96 | $line = (!is_array($data)) ? null : $data[2]; |
97 | 97 | ||
98 | if ($value == '${') $dep++; |
98 | if ($value == '${') $dep++; |
99 | 99 | ||
100 | if ($wait_function && ($data == '{')) { |
100 | if ($wait_function && ($data == '{')) { |
101 | $wait_function = false; // Anonymous functions do not have a name |
101 | $wait_function = false; // Anonymous functions do not have a name |
102 | } |
102 | } |
103 | 103 | ||
104 | if ($wait_function && ($token == T_STRING)) { |
104 | if ($wait_function && ($token == T_STRING)) { |
105 | $wait_function = false; |
105 | $wait_function = false; |
106 | if ($icon->isAbstract()) { |
106 | if ($icon->isAbstract()) { |
107 | $desc = "abstract function $value()"; |
107 | $desc = "abstract function $value()"; |
108 | $wait_abstract_func_list_end = true; |
108 | $wait_abstract_func_list_end = true; |
109 | } else { |
109 | } else { |
110 | $desc = "function $value()"; |
110 | $desc = "function $value()"; |
111 | $insideFuncAry[] = $dep; |
111 | $insideFuncAry[] = $dep; |
112 | } |
112 | } |
113 | 113 | ||
114 | if ($value == '__construct') { // TODO: auch eine methode mit dem namen der klasse soll eine konstruktor sein |
114 | if ($value == '__construct') { // TODO: auch eine methode mit dem namen der klasse soll eine konstruktor sein |
115 | $icon->setType(ICON_TYPE_CONSTRUCTOR); |
115 | $icon->setType(ICON_TYPE_CONSTRUCTOR); |
116 | } else if ($value == '__destruct') { |
116 | } else if ($value == '__destruct') { |
117 | $icon->setType(ICON_TYPE_DESTRUCTOR); |
117 | $icon->setType(ICON_TYPE_DESTRUCTOR); |
118 | } else if (substr($value, 0, 2) == '__') { |
118 | } else if (substr($value, 0, 2) == '__') { |
119 | $icon->setType(ICON_TYPE_MAGICMETHOD); |
119 | $icon->setType(ICON_TYPE_MAGICMETHOD); |
120 | } else { |
120 | } else { |
121 | $icon->setType(ICON_TYPE_FUNCTION); |
121 | $icon->setType(ICON_TYPE_FUNCTION); |
122 | } |
122 | } |
123 | FastPHPWriter::outputLeafNode($icon, $line, $desc); |
123 | FastPHPWriter::outputLeafNode($icon, $line, $desc); |
124 | $icon->reset(); |
124 | $icon->reset(); |
125 | } |
125 | } |
126 | 126 | ||
127 | if ($wait_class && ($token == T_STRING)) { |
127 | if ($wait_class && ($token == T_STRING)) { |
128 | if ($icon->isAbstract()) { |
128 | if ($icon->isAbstract()) { |
129 | $desc = "Abstract Class $value\n"; |
129 | $desc = "Abstract Class $value\n"; |
130 | } else { |
130 | } else { |
131 | $desc = "Class $value\n"; |
131 | $desc = "Class $value\n"; |
132 | } |
132 | } |
133 | $wait_class = false; |
133 | $wait_class = false; |
134 | $levelAry[] = $dep; |
134 | $levelAry[] = $dep; |
135 | 135 | ||
136 | $icon->setType(ICON_TYPE_CLASS); |
136 | $icon->setType(ICON_TYPE_CLASS); |
137 | FastPHPWriter::outputLeafNode($icon, $line, $desc); |
137 | FastPHPWriter::outputLeafNode($icon, $line, $desc); |
138 | $icon->reset(); |
138 | $icon->reset(); |
139 | 139 | ||
140 | FastPHPWriter::outputIncreaseLevel(); |
140 | FastPHPWriter::outputIncreaseLevel(); |
141 | } |
141 | } |
142 | 142 | ||
143 | if ($wait_trait && ($token == T_STRING)) { |
143 | if ($wait_trait && ($token == T_STRING)) { |
144 | $desc = "Trait $value\n"; |
144 | $desc = "Trait $value\n"; |
145 | $wait_trait = false; |
145 | $wait_trait = false; |
146 | $levelAry[] = $dep; |
146 | $levelAry[] = $dep; |
147 | 147 | ||
148 | $icon->setType(ICON_TYPE_TRAIT); |
148 | $icon->setType(ICON_TYPE_TRAIT); |
149 | FastPHPWriter::outputLeafNode($icon, $line, $desc); |
149 | FastPHPWriter::outputLeafNode($icon, $line, $desc); |
150 | $icon->reset(); |
150 | $icon->reset(); |
151 | 151 | ||
152 | FastPHPWriter::outputIncreaseLevel(); |
152 | FastPHPWriter::outputIncreaseLevel(); |
153 | } |
153 | } |
154 | 154 | ||
155 | if ($wait_interface && ($token == T_STRING)) { |
155 | if ($wait_interface && ($token == T_STRING)) { |
156 | $desc = "Interface $value\n"; |
156 | $desc = "Interface $value\n"; |
157 | $wait_interface = false; |
157 | $wait_interface = false; |
158 | $levelAry[] = $dep; |
158 | $levelAry[] = $dep; |
159 | 159 | ||
160 | $icon->setType(ICON_TYPE_INTERFACE); |
160 | $icon->setType(ICON_TYPE_INTERFACE); |
161 | FastPHPWriter::outputLeafNode($icon, $line, $desc); |
161 | FastPHPWriter::outputLeafNode($icon, $line, $desc); |
162 | $icon->reset(); |
162 | $icon->reset(); |
163 | 163 | ||
164 | FastPHPWriter::outputIncreaseLevel(); |
164 | FastPHPWriter::outputIncreaseLevel(); |
165 | } |
165 | } |
166 | 166 | ||
167 | if ($wait_const && ($token == T_STRING)) { |
167 | if ($wait_const && ($token == T_STRING)) { |
168 | $desc = "const $value\n"; |
168 | $desc = "const $value\n"; |
169 | $wait_const = false; |
169 | $wait_const = false; |
170 | 170 | ||
171 | $icon->setType(ICON_TYPE_CONST); |
171 | $icon->setType(ICON_TYPE_CONST); |
172 | FastPHPWriter::outputLeafNode($icon, $line, $desc); |
172 | FastPHPWriter::outputLeafNode($icon, $line, $desc); |
173 | $icon->reset(); |
173 | $icon->reset(); |
174 | } |
174 | } |
175 | 175 | ||
176 | if ((!$wait_abstract_func_list_end) && (count($levelAry) > 0) && (count($insideFuncAry) == 0) && ($token == T_VARIABLE)) { |
176 | if ((!$wait_abstract_func_list_end) && (count($levelAry) > 0) && (count($insideFuncAry) == 0) && ($token == T_VARIABLE)) { |
177 | $desc = "$value\n"; |
177 | $desc = "$value\n"; |
178 | 178 | ||
179 | $icon->setType(ICON_TYPE_VAR); |
179 | $icon->setType(ICON_TYPE_VAR); |
180 | FastPHPWriter::outputLeafNode($icon, $line, $desc); |
180 | FastPHPWriter::outputLeafNode($icon, $line, $desc); |
181 | $icon->reset(); |
181 | $icon->reset(); |
182 | } |
182 | } |
183 | 183 | ||
184 | if ($token == T_PRIVATE) $icon->setPrivate(); |
184 | if ($token == T_PRIVATE) $icon->setPrivate(); |
185 | if ($token == T_PROTECTED) $icon->setProtected(); |
185 | if ($token == T_PROTECTED) $icon->setProtected(); |
186 | if ($token == T_PUBLIC) $icon->setPublic(); |
186 | if ($token == T_PUBLIC) $icon->setPublic(); |
187 | if ($token == T_ABSTRACT) $icon->setAbstract(); |
187 | if ($token == T_ABSTRACT) $icon->setAbstract(); |
188 | if ($token == T_FINAL) $icon->setFinal(); |
188 | if ($token == T_FINAL) $icon->setFinal(); |
189 | if ($token == T_STATIC) $icon->setStatic(); |
189 | if ($token == T_STATIC) $icon->setStatic(); |
190 | 190 | ||
191 | if (($data == ';') || ($data == '{') || ($data == '}')) { |
191 | if (($data == ';') || ($data == '{') || ($data == '}')) { |
192 | $wait_abstract_func_list_end = false; |
192 | $wait_abstract_func_list_end = false; |
193 | $icon->reset(); |
193 | $icon->reset(); |
194 | } |
194 | } |
195 | 195 | ||
196 | if ($token == T_FUNCTION) { |
196 | if ($token == T_FUNCTION) { |
197 | $wait_function = true; |
197 | $wait_function = true; |
198 | } |
198 | } |
199 | if ($token == T_CLASS) { |
199 | if ($token == T_CLASS) { |
200 | $wait_class = true; |
200 | $wait_class = true; |
201 | $dep = 0; |
201 | $dep = 0; |
202 | } |
202 | } |
203 | if ($token == T_INTERFACE) { |
203 | if ($token == T_INTERFACE) { |
204 | $wait_interface = true; |
204 | $wait_interface = true; |
205 | $dep = 0; |
205 | $dep = 0; |
206 | } |
206 | } |
207 | if ($token == T_TRAIT) { |
207 | if ($token == T_TRAIT) { |
208 | $wait_trait = true; |
208 | $wait_trait = true; |
209 | $dep = 0; |
209 | $dep = 0; |
210 | } |
210 | } |
211 | 211 | ||
212 | if ($token == T_CONST) { |
212 | if ($token == T_CONST) { |
213 | $wait_const = true; |
213 | $wait_const = true; |
214 | } |
214 | } |
215 | 215 | ||
216 | if (($token == T_COMMENT) && self::isToDoDescription($value)) { |
216 | if (($token == T_COMMENT) && self::isToDoDescription($value)) { |
217 | $comment_lines = explode("\n", trim($value)); |
217 | $comment_lines = explode("\n", trim($value)); |
218 | foreach ($comment_lines as $line_no => $comment_line) { |
218 | foreach ($comment_lines as $line_no => $comment_line) { |
219 | if (self::isToDoDescription($comment_line)) { |
219 | if (self::isToDoDescription($comment_line)) { |
220 | // Because a To-Do-entry can stand everywhere (e.g. between a "private" and a "function" keyword) |
220 | // Because a To-Do-entry can stand everywhere (e.g. between a "private" and a "function" keyword) |
221 | // we shall not alter the $icon instance |
221 | // we shall not alter the $icon instance |
222 | $todoIcon = clone $icon; |
222 | $todoIcon = clone $icon; |
223 | $todoIcon->setType(ICON_TYPE_TODO); |
223 | $todoIcon->setType(ICON_TYPE_TODO); |
224 | FastPHPWriter::outputLeafNode($todoIcon, $line+$line_no, self::stripComment($comment_line)); |
224 | FastPHPWriter::outputLeafNode($todoIcon, $line+$line_no, self::stripComment($comment_line)); |
225 | unset($todoIcon); |
225 | unset($todoIcon); |
226 | } |
226 | } |
227 | } |
227 | } |
228 | } |
228 | } |
229 | } |
229 | } |
230 | } |
230 | } |
231 | 231 | ||
232 | private static function isToDoDescription($comment) { |
232 | private static function isToDoDescription($comment) { |
233 | return ((stripos($comment, 'TODO') !== false) || |
233 | return ((stripos($comment, 'TODO') !== false) || |
234 | (stripos($comment, 'BUGBUG') !== false) || |
234 | (stripos($comment, 'BUGBUG') !== false) || |
235 | (stripos($comment, 'FIXME') !== false) || |
235 | (stripos($comment, 'FIXME') !== false) || |
236 | (stripos($comment, 'XXX') !== false)); |
236 | (stripos($comment, 'XXX') !== false)); |
237 | } |
237 | } |
238 | 238 | ||
239 | private static function stripComment($x) { |
239 | private static function stripComment($x) { |
240 | $x = trim($x); |
240 | $x = trim($x); |
241 | if (substr($x, 0, 1) == '#') return trim(substr($x, 1)); |
241 | if (substr($x, 0, 1) == '#') return trim(substr($x, 1)); |
242 | if (substr($x, 0, 2) == '//') return trim(substr($x, 2)); |
242 | if (substr($x, 0, 2) == '//') return trim(substr($x, 2)); |
243 | if (substr($x, 0, 2) == '/*') return trim(substr($x, 2, strlen($x)-4)); |
243 | if (substr($x, 0, 2) == '/*') return trim(substr($x, 2, strlen($x)-4)); |
244 | return $x; |
244 | return $x; |
245 | } |
245 | } |
246 | 246 | ||
247 | 247 | ||
248 | private static final function array_peek($array) { |
248 | private static /*final*/ function array_peek($array) { |
249 | if (!isset($array[count($array)-1])) return null; |
249 | if (!isset($array[count($array)-1])) return null; |
250 | return $array[count($array)-1]; |
250 | return $array[count($array)-1]; |
251 | } |
251 | } |
252 | } |
252 | } |
253 | 253 | ||
254 | $parser = new MyFastPHPCodeExplorer($icon); |
254 | $parser = new MyFastPHPCodeExplorer($icon); |
255 | while (true) { |
255 | while (true) { |
256 | try { |
256 | try { |
257 | $code = FastPHPReader::readCodeFromEditor(); |
257 | $code = FastPHPReader::readCodeFromEditor(); |
258 | } catch (FastPHPExitSignalReceivedException $e) { |
258 | } catch (FastPHPExitSignalReceivedException $e) { |
259 | die(); |
259 | die(); |
260 | } |
260 | } |
261 | FastPHPWriter::outputHeader(); |
261 | FastPHPWriter::outputHeader(); |
262 | $parser->handle($code); |
262 | $parser->handle($code); |
263 | FastPHPWriter::outputExit(); |
263 | FastPHPWriter::outputExit(); |
264 | FastPHPWriter::signalOutputEnd(); |
264 | FastPHPWriter::signalOutputEnd(); |
265 | sleep(1); |
265 | sleep(1); |
266 | } |
266 | } |