wproot 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/Rakefile +2 -0
  4. data/bin/wproot +7 -0
  5. data/lib/wproot.rb +11 -0
  6. data/lib/wproot/cli.rb +35 -0
  7. data/lib/wproot/compass.rb +15 -0
  8. data/lib/wproot/haml.rb +15 -0
  9. data/lib/wproot/version.rb +3 -0
  10. data/spec/spec_helper.rb +7 -0
  11. data/spec/wproot_spec.rb +59 -0
  12. data/vendor/HamlPHP.php +7 -0
  13. data/vendor/HamlPHP/.gitignore +6 -0
  14. data/vendor/HamlPHP/MIT-LICENSE +22 -0
  15. data/vendor/HamlPHP/README.mkd +39 -0
  16. data/vendor/HamlPHP/src/HamlPHP/CommentNode.php +92 -0
  17. data/vendor/HamlPHP/src/HamlPHP/Compiler.php +109 -0
  18. data/vendor/HamlPHP/src/HamlPHP/ContentEvaluator/ContentEvaluator.php +16 -0
  19. data/vendor/HamlPHP/src/HamlPHP/ContentEvaluator/DefaultContentEvaluator.php +22 -0
  20. data/vendor/HamlPHP/src/HamlPHP/DoctypeNode.php +47 -0
  21. data/vendor/HamlPHP/src/HamlPHP/Element.php +618 -0
  22. data/vendor/HamlPHP/src/HamlPHP/ElementNode.php +222 -0
  23. data/vendor/HamlPHP/src/HamlPHP/Filter/CssFilter.php +31 -0
  24. data/vendor/HamlPHP/src/HamlPHP/Filter/Filter.php +18 -0
  25. data/vendor/HamlPHP/src/HamlPHP/Filter/FilterContainer.php +34 -0
  26. data/vendor/HamlPHP/src/HamlPHP/Filter/JavascriptFilter.php +29 -0
  27. data/vendor/HamlPHP/src/HamlPHP/Filter/PhpFilter.php +24 -0
  28. data/vendor/HamlPHP/src/HamlPHP/Filter/PlainFilter.php +46 -0
  29. data/vendor/HamlPHP/src/HamlPHP/FilterNode.php +29 -0
  30. data/vendor/HamlPHP/src/HamlPHP/HamlNode.php +75 -0
  31. data/vendor/HamlPHP/src/HamlPHP/HamlPHP.php +191 -0
  32. data/vendor/HamlPHP/src/HamlPHP/Helpers.php +136 -0
  33. data/vendor/HamlPHP/src/HamlPHP/Interpolation.php +71 -0
  34. data/vendor/HamlPHP/src/HamlPHP/NodeFactory.php +80 -0
  35. data/vendor/HamlPHP/src/HamlPHP/RootNode.php +133 -0
  36. data/vendor/HamlPHP/src/HamlPHP/Storage/DontEvaluateStorage.php +68 -0
  37. data/vendor/HamlPHP/src/HamlPHP/Storage/FileStorage.php +74 -0
  38. data/vendor/HamlPHP/src/HamlPHP/Storage/Storage.php +24 -0
  39. data/vendor/HamlPHP/src/HamlPHP/TagNode.php +125 -0
  40. data/vendor/HamlPHP/src/HamlPHP/Util/BaseException.php +340 -0
  41. data/vendor/HamlPHP/src/HamlPHP/Util/BaseObject.php +94 -0
  42. data/vendor/HamlPHP/src/HamlPHP/Util/StringScanner.php +989 -0
  43. data/wproot.gemspec +21 -0
  44. metadata +126 -0
@@ -0,0 +1,94 @@
1
+ <?php
2
+
3
+ /**
4
+ * @author Saulo Vallory <email@saulovallory.com>
5
+ */
6
+ require_once dirname(__FILE__) . '/BaseException.php';
7
+
8
+ class UndefinedPropertyException extends BaseException {
9
+ /**
10
+ * @param string $class
11
+ * @param string $property
12
+ */
13
+ public function __construct($class, $property) {
14
+ parent::__construct("Trying to access an undefined property ($class::$property) on {{guiltyFile}} at line {{guiltyLine}}");
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Thrown when trying to set a read-only property.
20
+ */
21
+ class ReadOnlyPropertyException extends BaseException {
22
+ /**
23
+ * @param string $class
24
+ * @param string $property
25
+ */
26
+ public function __construct($class, $property) {
27
+ parent::__construct("Trying to set a read-only property ($class::$property) on {{guiltyFile}} at line {{guiltyLine}}");
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Provides default __get and __set magic methods
33
+ * You can define a protected array $magic_get_methods in a subclass to
34
+ * specify a "magic property" -> "get method" relation.
35
+ * Ex:
36
+ * <code>
37
+ * protected $magic_get_methods = array(
38
+ * 'eos' => 'endOfStream'
39
+ * );
40
+ * </code>
41
+ */
42
+ abstract class BaseObject
43
+ {
44
+ public function __get($name)
45
+ {
46
+ if(isset($this->magic_get_methods[$name])) {
47
+ $methods = array($this->magic_get_methods[$name]);
48
+ }
49
+ else {
50
+ $name = ucfirst($name);
51
+ $methods = array("get$name", "is$name", "has$name");
52
+ }
53
+
54
+ foreach($methods as $m)
55
+ {
56
+ if(method_exists($this, $m)) {
57
+ return $this->$m();
58
+ }
59
+ }
60
+
61
+ $ex = new UndefinedPropertyException(get_class($this), $name);
62
+ $btrace = debug_backtrace();
63
+ $ex->setGuiltyFile($btrace[0]['file']);
64
+ $ex->setGuiltyLine($btrace[0]['line']);
65
+ throw $ex;
66
+ }
67
+
68
+ public function __set($name,$value)
69
+ {
70
+ $setter='set'.$name;
71
+ $boolSetter = "is$name";
72
+
73
+ if(method_exists($this,$setter))
74
+ {
75
+ $this->$setter($value);
76
+ }
77
+ else if(method_exists($this,'get'.$name))
78
+ {
79
+ $ex = new ReadOnlyPropertyException(get_class($this), $name);
80
+ $btrace = debug_backtrace();
81
+ $ex->setGuiltyFile($btrace[0]['file']);
82
+ $ex->setGuiltyLine($btrace[0]['line']);
83
+ throw $ex;
84
+ }
85
+ else
86
+ {
87
+ $ex = new UndefinedPropertyException(get_class($this), $name);
88
+ $btrace = debug_backtrace();
89
+ $ex->setGuiltyFile($btrace[0]['file']);
90
+ $ex->setGuiltyLine($btrace[0]['line']);
91
+ throw $ex;
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,989 @@
1
+ <?php
2
+
3
+ require_once 'BaseObject.php';
4
+
5
+ /** @class StringScanner
6
+ * @nosubgrouping
7
+ * StringScanner provides for lexical scanning operations on a String.
8
+ * This class is a port of Ruby's StringScan class. So the documentation was "stolen" from there.
9
+ * Here is an example of its usage:
10
+ *
11
+ * @code
12
+ * $s = new StringScanner('This is an example string');
13
+ * $s->eos; # -> false
14
+ *
15
+ * echo $s->scan('/\w+/'); # -> "This"
16
+ * echo $s->scan('/\w+/'); # -> null
17
+ * echo $s->scan('/\s+/'); # -> " "
18
+ * echo $s->scan('/\s+/'); # -> null
19
+ * echo $s->scan('/\w+/'); # -> "is"
20
+ * $s->eos; # -> false
21
+ *
22
+ * echo $s->scan('/\s+/'); # -> " "
23
+ * echo $s->scan('/\w+/'); # -> "an"
24
+ * echo $s->scan('/\s+/'); # -> " "
25
+ * echo $s->scan('/\w+/'); # -> "example"
26
+ * echo $s->scan('/\s+/'); # -> " "
27
+ * echo $s->scan('/\w+/'); # -> "string"
28
+ * $s->eos; # -> true
29
+ *
30
+ * echo $s->scan('/\s+/'); # -> null
31
+ * echo $s->scan('/\w+/'); # -> null
32
+ * @endcode
33
+ *
34
+ * <p>
35
+ * Scanning a @mlink{$string} means remembering the position of a scan @mlink{$pointer}, which is just an index.
36
+ * The point of scanning is to move forward a bit at a time, so matches are
37
+ * sought after the scan @mlink{$pointer}; usually immediately after it.
38
+ * </p>
39
+ *
40
+ * <p>
41
+ * Given the string "test string", here are the pertinent scan @mlink{$pointer} positions:
42
+ * </p>
43
+ *
44
+ * <pre>
45
+ * t e s t s t r i n g
46
+ * 0 1 2 ... 1
47
+ * 0
48
+ * </pre>
49
+ *
50
+ * <p>
51
+ * When you {@link scan()} for a pattern (a regular expression), the match must occur at the character after the scan @mlink{$pointer}.
52
+ * If you use {@link scanUntil()}, then the match can occur anywhere after the scan pointer. In both cases, the scan @mlink{$pointer} moves <em>just beyond</em>
53
+ * the last character of the match, ready to <a href="StringScanner.html#M003869">scan</a> again from the next character
54
+ * onwards. This is demonstrated by the example above.
55
+ * </p>
56
+ *
57
+ * <h2>Method_Categories Method Categories</h2>
58
+ *
59
+ * There are other methods besides the plain scanners. You can look ahead in
60
+ * the <a href="StringScanner.html#M003861">string</a> without actually
61
+ * scanning. You can access the most recent match. You can modify the @mlink{$string} being scanned,
62
+ * {@link reset()} or {@link terminate()} the scanner, find out or
63
+ * change the position of the scan @mlink{$pointer}, {@link skip()} ahead, and so on.
64
+ *
65
+ * @par Advancing the Scan Pointer
66
+ *
67
+ * - {@link getch()}
68
+ * - {@link scan()}
69
+ * - {@link scanUntil()}
70
+ * - {@link skip()}
71
+ * - {@link skipUntil()}
72
+ *
73
+ * @par Looking Ahead
74
+ *
75
+ * - {@link check()}
76
+ * - {@link checkUntil()}
77
+ * - {@link exist()}
78
+ * - {@link match()}
79
+ * - {@link peek()}
80
+ *
81
+ * @par Finding Where we Are
82
+ *
83
+ * - @mlink{$bol} or {@link isBeginningOfLine()}
84
+ * - {@link eos}
85
+ * - @mlink{$rest}
86
+ * - {@link restSize()}
87
+ * - @mlink{$pos} or @mlink{$pointer}
88
+ *
89
+ * @par Setting Where we Are
90
+ *
91
+ * - {@link reset()}
92
+ * - {@link terminate()}
93
+ * - @mlink{$pos}
94
+ *
95
+ * @par Match Data
96
+ *
97
+ * - {@link matched()}
98
+ * - @mlink{$matchedSize} or {@link getMatchedSize()}
99
+ * - [] (You can use array like syntax to <em>get</em> the last match and it's subgroups
100
+ * - {@link preMatch()}
101
+ * - {@link postMatch()}
102
+ *
103
+ * @par Miscellaneous
104
+ *
105
+ * - {@link concat()}
106
+ * - @mlink{$string}
107
+ * - {@link unscan()}
108
+ *
109
+ * There are aliases to several of the methods.
110
+ *
111
+ * @todo Implement byte reading
112
+ *
113
+ * @author Saulo Vallory <email@saulovallory.com>
114
+ *
115
+ * @property-read bool $beginningOfLine Returns true if the scan @mlink{$pointer} is at the beginning of the line
116
+ * @property-read bool $bol Returns true if the scan @mlink{$pointer} is at the beginning of a new line.
117
+ * @property-read bool $eos Returns true if the scan @mlink{$pointer} is at the end of the string.
118
+ * @property-read int $matchedSize Returns the size of the most recent match (see @mlink{$matched} or {@link getMatched()}), or null if there was no recent match.
119
+ * @property-read int $pointer Returns true if the scan @mlink{$pointer} is at the beginning of a new line.
120
+ * @property-read int $pos Returns true if the scan @mlink{$pointer} is at the beginning of a new line.
121
+ * @property-read string $rest Returns the "rest" of the string (i.e. everything after the scan @mlink{$pointer}). If there is no more data (@mlink{$eos} = true), it returns "".
122
+ * @property-read string $restSize Returns the size of the "rest" of the string.
123
+ * @property string $string Returns the string being scanned.
124
+ * @property-read string $matched Returns true if the last match was successful.
125
+ */
126
+ class StringScanner extends BaseObject implements ArrayAccess
127
+ {
128
+ /** \name Magic Properties
129
+ * @{
130
+ */
131
+ /** @property read_only bool $beginningOfLine
132
+ * @brief Returns true if the scan @mlink{$pointer} is at the beginning of the line
133
+ * @memberof StringScanner
134
+ * @readonly
135
+ */
136
+
137
+ /** @property read_only bool $bol
138
+ * @brief Returns true if the scan @mlink{$pointer} is at the beginning of a new line.
139
+ * @memberof StringScanner
140
+ * @readonly
141
+ */
142
+
143
+ /** @property read_only bool $eos
144
+ * @brief Returns true if the scan @mlink{$pointer} is at the end of the string.
145
+ * @memberof StringScanner
146
+ * @readonly
147
+ */
148
+
149
+ /** @property read_only int $matchedSize
150
+ * @brief Returns the size of the most recent match (see @mlink{$matched} or {@link getMatched()}), or null if there was no recent match.
151
+ * @memberof StringScanner
152
+ * @readonly
153
+ */
154
+
155
+ /**
156
+ * @property read_only int $pointer
157
+ * @brief Returns the current pointer position.
158
+ * @memberof StringScanner
159
+ * @readonly
160
+ */
161
+
162
+ /** @property int $pos
163
+ * @brief Returns true if the scan @mlink{$pointer} is at the beginning of a new line.
164
+ * @memberof StringScanner
165
+ */
166
+
167
+ /** @property read_only string $rest
168
+ * @brief Returns the "rest" of the string (i.e. everything after the scan @mlink{$pointer}). If there is no more data (@mlink{$eos} = true), it returns "".
169
+ * @memberof StringScanner
170
+ * @readonly
171
+ */
172
+
173
+ /** @property read_only string $restSize
174
+ * @brief Returns the size of the "rest" of the string.
175
+ * @memberof StringScanner
176
+ * @readonly
177
+ */
178
+
179
+ /** @property string $string
180
+ * @brief Returns the string being scanned.
181
+ * @memberof StringScanner
182
+ */
183
+
184
+ /** @property read_only string $matched
185
+ * @brief Returns true if the last match was successful.
186
+ * @memberof StringScanner
187
+ * @readonly
188
+ */
189
+ /** @} */
190
+
191
+ private $_string;
192
+
193
+ private $_curr;
194
+
195
+ private $_prev;
196
+
197
+ /**
198
+ * Last match and it's subgroups
199
+ * @var array
200
+ */
201
+ private $_matches;
202
+
203
+ /**
204
+ * The beginning pos of the last match
205
+ * @var int
206
+ */
207
+ private $_lastMatchBeg;
208
+
209
+ /**
210
+ * The ending pos of the last match
211
+ * @var int
212
+ */
213
+ private $_lastMatchEnd;
214
+
215
+ private $_rest;
216
+
217
+ /**
218
+ * The length of the {@link string} being parsed
219
+ * @var int
220
+ */
221
+ private $_size;
222
+
223
+ private $_restSize;
224
+
225
+ private $_encoding = null;
226
+
227
+ protected $magic_get_methods = array(
228
+ 'bol' => 'isBeginningOfLine',
229
+ 'beginningOfLine' => 'isBeginningOfLine',
230
+ 'pointer' => 'getPos',
231
+ 'eos' => 'isEos',
232
+ );
233
+
234
+ /**
235
+ * Creates a new StringScanner object to scan over the given $string.
236
+ * @param string $str The string to be scanned
237
+ * @param string $encoding The encoding of the string
238
+ */
239
+ public function __construct($str, $encoding = null)
240
+ {
241
+ if($encoding)
242
+ $this->_encoding = $encoding;
243
+ else
244
+ $this->_encoding = mb_internal_encoding();
245
+
246
+ $this->_string = $str;
247
+ $this->reset();
248
+ }
249
+
250
+ /**
251
+ * @internal
252
+ * @param string $regex The regular expression
253
+ * @param bool $update_ptr Wheter to update the pointer or not
254
+ * @param bool $get_str Wheter to return the matched string or the position
255
+ * @param bool $head_only Match only at the beginning of the string
256
+ * @return mixed Either the matched string (if $return_string is true) or the end position of the match
257
+ */
258
+ private function doScan($regex, $update_ptr, $get_str, $head_only)
259
+ {
260
+ if($this->eos)
261
+ return null;
262
+
263
+ if ($head_only) {
264
+ $regex = $regex[0] . '^' . mb_substr($regex, 1, mb_strlen($regex), $this->_encoding);
265
+ }
266
+ else
267
+ {
268
+ $delim = $regex[0];
269
+ $end_pos = strrpos($regex, $delim);
270
+
271
+ $regex = "$delim.*?(".substr($regex, 1, $end_pos-1).")$delim".substr($regex, $end_pos+1);
272
+ }
273
+
274
+ $ret = preg_match($regex, $this->_rest, $this->_matches);
275
+
276
+ if ($ret === false)
277
+ throw new Exception(preg_last_error());
278
+
279
+ if ($ret == 0) {
280
+ // not matched
281
+ $this->_clear_matched();
282
+ return null;
283
+ }
284
+
285
+ $this->_matched($update_ptr);
286
+
287
+ if ($get_str) {
288
+ return mb_substr($this->_string, $this->_prev, $this->_lastMatchEnd - $this->_prev, $this->_encoding);
289
+ }
290
+ else {
291
+ return $this->_lastMatchEnd - $this->_prev;
292
+ }
293
+ }
294
+
295
+ /**
296
+ * @internal
297
+ * NEEDS to be called whenever the scanner does a new match
298
+ * Update optimization vars based on the last match
299
+ *
300
+ * @param bool $update_curr Wheter to update the pointer or not
301
+ */
302
+ private function _matched($update_curr)
303
+ {
304
+ $this->_prev = $this->_curr;
305
+ $this->_lastMatchBeg = $this->_curr + (empty($this->_matches[0]) ? 0 : mb_strpos($this->_rest, $this->_matches[0], 0, $this->_encoding));
306
+ $this->_lastMatchEnd = $this->_lastMatchBeg + mb_strlen($this->_matches[0], $this->_encoding);
307
+
308
+ if($update_curr)
309
+ {
310
+ $this->_curr = $this->_lastMatchEnd;
311
+ $this->_rest = mb_substr($this->_string, $this->_curr, $this->_size, $this->_encoding);
312
+ $this->_restSize = mb_strlen($this->_rest, $this->_encoding);
313
+ }
314
+ }
315
+
316
+ private function _clear_matched()
317
+ {
318
+ $this->_matches = null;
319
+ $this->_lastMatchBeg = null;
320
+ $this->_lastMatchEnd = null;
321
+ }
322
+
323
+ private function _string_updated($keep_pointer)
324
+ {
325
+ if($keep_pointer)
326
+ {
327
+ $this->_size = mb_strlen($this->_string, $this->_encoding);
328
+ $this->_rest = mb_substr($this->_string, $this->_curr, $this->_size, $this->_encoding);
329
+ $this->_restSize = mb_strlen($this->_rest, $this->_encoding);
330
+
331
+ if($this->_curr > $this->_size)
332
+ throw new Exception('The operation resulted in an invalid pointer position!');
333
+ }
334
+ else
335
+ {
336
+ $this->_prev = null;
337
+ $this->_curr = 0;
338
+ $this->_rest = $this->_string;
339
+ $this->_restSize = $this->_size = mb_strlen($this->_string, $this->_encoding);
340
+ $this->_matches = $this->_lastMatchBeg = $this->_lastMatchEnd = null;
341
+ }
342
+ }
343
+
344
+ /* ArrayAccess methods */
345
+
346
+ /**
347
+ * Wether the n-th group was caught or not in the last match.
348
+ * 0 stands for the whole match.
349
+ * This method is executed when using isset() or empty().
350
+ *
351
+ * @param int $offset
352
+ */
353
+ public function offsetExists($offset)
354
+ {
355
+ return isset($this->_matches[$offset]);
356
+ }
357
+
358
+ /**
359
+ * Implements ArrayAccess
360
+ * Return the n-th subgroup in the most recent match.
361
+ * @code
362
+ * $s = new StringScanner("Fri Dec 12 1975 14:39");
363
+ * $s->scan('/(\w+) (\w+) (\d+) /'); # -> "Fri Dec 12 "
364
+ * echo $s[0]; # -> "Fri Dec 12 "
365
+ * echo $s[1]; # -> "Fri"
366
+ * echo $s[2]; # -> "Dec"
367
+ * echo $s[3]; # -> "12"
368
+ * echo $s->postMatch(); # -> "1975 14:39"
369
+ * echo $s->preMatch(); # -> ""
370
+ * @endcode
371
+ * @param int $offset
372
+ */
373
+ public function offsetGet($offset)
374
+ {
375
+ return isset($this->_matches[$offset]) ? $this->_matches[$offset] : null;
376
+ }
377
+
378
+ /**
379
+ * Calling this method will throw an exception.
380
+ * You can't set the value of the last match.
381
+ */
382
+ public function offsetSet($offset, $value)
383
+ {
384
+ throw new Exception("You can't set the value of the last match.");
385
+ }
386
+
387
+ /**
388
+ * Calling this method will throw an exception.
389
+ * You can't unset the value of the last match.
390
+ */
391
+ public function offsetUnset($offset)
392
+ {
393
+ throw new Exception("You can't unset the value of the last match.");
394
+ }
395
+
396
+ /* / ArrayAccess methods */
397
+
398
+ /**
399
+ * Returns true if the scan @mlink{$pointer} is at the beginning of the line.
400
+ * @magicalias{$bol}
401
+ *
402
+ * @code
403
+ * $s = new StringScanner("test\ntest\n");
404
+ * $s->bol(); # => true
405
+ * $s->scan('/te/');
406
+ * $s->bol(); # => false
407
+ * $s->scan('/st\n/');
408
+ * $s->bol(); # => true
409
+ * $s->terminate();
410
+ * $s->bol(); # => true
411
+ * @endcode
412
+ *
413
+ * @return bool
414
+ */
415
+ public function isBeginningOfLine() {
416
+ return ($this->_curr == 0 || $this->_string[$this->_curr-1] == "\n");
417
+ }
418
+
419
+ /**
420
+ * This returns the value that scan would return, without advancing the scan pointer. The match register is affected, though.
421
+ * @code
422
+ * $s = new StringScanner("Fri Dec 12 1975 14:39");
423
+ * $s->check('/Fri/'); # -> "Fri"
424
+ * $s->pos; # -> 0
425
+ * $s->matched; # -> "Fri"
426
+ * $s->check('/12/'); # -> null
427
+ * $s->matched; # -> null
428
+ * @endcode
429
+ * Mnemonic: it "checks" to see whether a scan will return a value.
430
+ *
431
+ * @param string $pattern
432
+ */
433
+ public function check($pattern) {
434
+ return $this->doScan($pattern, false, true, true);
435
+ }
436
+
437
+ /**
438
+ * This returns the value that {@link scanUntil()} would return, without advancing the scan @mlink{$pointer}.
439
+ * The match register is affected, though.
440
+ * @code
441
+ * $s = new StringScanner("Fri Dec 12 1975 14:39");
442
+ * $s->checkUntil('/12/'); # -> "Fri Dec 12"
443
+ * $s->pos; # -> 0
444
+ * $s->matched; # -> 12
445
+ * @endcode
446
+ * Mnemonic: it "checks" to see whether a scanUntil will return a value.
447
+ *
448
+ * @param string $pattern
449
+ * @return string
450
+ */
451
+ public function checkUntil($pattern) {
452
+ return $this->doScan($pattern, false, true, false);
453
+ }
454
+
455
+ /**
456
+ * Appends $str to the @mlink{$string} being scanned.
457
+ * This method does not affect scan @mlink{$pointer}.
458
+ * @code
459
+ * $s = new StringScanner("Fri Dec 12 1975 14:39");
460
+ * $s->scan('/Fri /');
461
+ * $s->concat(" +1000 GMT");
462
+ * $s->string; // -> "Fri Dec 12 1975 14:39 +1000 GMT"
463
+ * $s->scan('/Dec/'); // -> "Dec"
464
+ * @endcode
465
+ * @param $str
466
+ */
467
+ public function concat($str)
468
+ {
469
+ $this->_string .= $str;
470
+ $this->_string_updated(true);
471
+ }
472
+
473
+ /**
474
+ * Returns true if the scan @mlink{$pointer} is at the end of the string.
475
+ * @code
476
+ * $s = new StringScanner('test string');
477
+ * echo $s->eos; # => false
478
+ * $s->scan('/test/');
479
+ * echo $s->eos; # => false
480
+ * $s->terminate();
481
+ * echo $s->eos; # => true
482
+ * @endcode
483
+ *
484
+ * @return bool
485
+ */
486
+ protected function isEos() {
487
+ return $this->_curr == $this->_size;
488
+ }
489
+
490
+ /**
491
+ * Looks ahead to see if the $pattern exists anywhere in the @mlink{$string}, without advancing the scan @mlink{$pointer}.
492
+ * This predicts whether a {@link scanUntil()} will return a value.
493
+ * @code
494
+ * $s = new StringScanner('test string');
495
+ * $s->exist('/s/'); # -> 3
496
+ * $s->scan('/test/'); # -> "test"
497
+ * $s->exist('/s/'); # -> 2
498
+ * $s->exist('/e/'); # -> null
499
+ * @endcode
500
+ *
501
+ * @param string $pattern
502
+ */
503
+ public function exist($pattern) {
504
+ return $this->doScan($pattern, false, false, false);
505
+ }
506
+
507
+ /* Scath of getByte method.
508
+ * Important!!!
509
+ * The implementation of this method affects getch() because using it can leave part of a multi-byte character in the $_rest of the string.
510
+ *
511
+ * Scans one byte and returns it. This method is not multibyte character sensitive and will not work if mbstring.func_overload is set to ON in php.ini.
512
+ * @code
513
+ * $s = new StringScanner('ab')
514
+ * $s->get_byte; # => "a"
515
+ * $s->get_byte; # => "b"
516
+ * $s->get_byte; # => null
517
+ *
518
+ * $KCODE = 'EUC'
519
+ * $s = new StringScanner("\244\242")
520
+ * $s->get_byte; # => "\244"
521
+ * $s->get_byte; # => "\242"
522
+ * $s->get_byte; # => null
523
+ * @endcode
524
+ *
525
+ * @see getch()
526
+ * @return string
527
+ *
528
+ public function getByte() {
529
+
530
+ $this->_matches = null;
531
+
532
+ if ($this->_eos)
533
+ return null;
534
+
535
+ $this->prev = $this->curr;
536
+ $this->curr++;
537
+
538
+ $this->_matches = array($this->_rest[$this->_prev]);
539
+
540
+ return $this->_rest[$this->_prev];
541
+ }
542
+ */
543
+
544
+ /**
545
+ * Scans one character and returns it. This method is multibyte character sensitive.
546
+ * @code
547
+ * $s = new StringScanner("ab");
548
+ * $s->getch(); # => "a"
549
+ * $s->getch(); # => "b"
550
+ * $s->getch(); # => null
551
+ *
552
+ * $s = new StringScanner("\244\242");
553
+ * $s->getch(); # => "\244\242" # Japanese hira-kana "A" in EUC-JP
554
+ * $s->getch(); # => null
555
+ * @endcode
556
+ */
557
+ public function getch() {
558
+ if($this->eos)
559
+ return null;
560
+
561
+ $ch = mb_substr($this->_rest, 0, 1, $this->_encoding);
562
+ $this->_matches[0] = $ch;
563
+ $this->_matched(true);
564
+
565
+ return $ch;
566
+ }
567
+
568
+ /**
569
+ * Returns a string that represents the StringScanner object, showing:
570
+ * - the current position
571
+ * - the size of the string
572
+ * - the characters surrounding the scan pointer
573
+ * @code
574
+ * $s = new StringScanner("Fri Dec 12 1975 14:39");
575
+ * $s->inspect(); # -> '#<StringScanner 0/21 @ "Fri D...">'
576
+ * $s->scanUntil('/12/'); # -> "Fri Dec 12"
577
+ * $s->inspect(); # -> '#<StringScanner 10/21 "...ec 12" @ " 1975...">'
578
+ * @endcode
579
+ */
580
+ public function inspect() {
581
+ $s = "#<StringScanner {$this->_curr}/{$this->_size} ";
582
+
583
+ if($this->_curr > 0)
584
+ {
585
+ $s .= '"';
586
+ if($this->_curr > 5)
587
+ $s .= '...';
588
+ $s .= mb_substr($this->_string, $this->_curr-5, 5, $this->_encoding).'" ';
589
+ }
590
+
591
+ $s .= '@ ';
592
+
593
+ if($this->_curr < $this->_size)
594
+ {
595
+ $s .= '"'.mb_substr($this->_rest, 0, 5, $this->_encoding);
596
+ if($this->_restSize > 5)
597
+ $s .= '...';
598
+ $s .= '">';
599
+ }
600
+
601
+ return $s;
602
+ }
603
+
604
+ /**
605
+ * Tests whether the given $pattern is matched from the current scan @mlink{$pointer}.
606
+ * Returns the length of the match, or <pre>null</pre>. The scan @mlink{$pointer} is not advanced.
607
+ * @code
608
+ * $s = new StringScanner('test string');
609
+ * echo $s->match('/\w+/'); # -> 4
610
+ * echo $s->match('/\w+/'); # -> 4
611
+ * echo $s->match('/\s+/'); # -> null
612
+ * @endcode
613
+ *
614
+ * @param string $pattern
615
+ * @return int
616
+ */
617
+ public function match($pattern) {
618
+ return $this->doScan($pattern, false, false, true);
619
+ }
620
+
621
+ /**
622
+ * Returns true if the last match was successful.
623
+ * @code
624
+ * $s = new StringScanner('test string');
625
+ * $s->match('/\w+/'); # => 4
626
+ * $s->matched(); # => true
627
+ * $s->matched; # => "test"
628
+ * $s->match('/\d+/'); # => null
629
+ * $s->matched(); # => false
630
+ * @endcode
631
+ * @return bool
632
+ */
633
+ public function matched() {
634
+ return isset($this->_matches[0]);
635
+ }
636
+
637
+ /**
638
+ * Returns the last matched string.
639
+ * @code
640
+ * $s = new StringScanner('test string');
641
+ * $s->match('/\w+/'); # -> 4
642
+ * $s->matched; # -> "test"
643
+ * @endcode
644
+ * @return string
645
+ */
646
+ public function getMatched() {
647
+ if(isset($this->_matches[0]))
648
+ return $this->_matches[0];
649
+ return null;
650
+ }
651
+
652
+ /**
653
+ * Returns the size of the most recent match (see matched), or null if there was no recent match.
654
+ * @code
655
+ * $s = new StringScanner('test string');
656
+ * $s->check('/\w+/'); # -> "test"
657
+ * $s->matchedSize; # -> 4
658
+ * $s->check('/\d+/'); # -> null
659
+ * $s->matchedSize; # -> null
660
+ * @endcode
661
+ * @return int
662
+ */
663
+ public function getMatchedSize() {
664
+ if(isset($this->_matches[0]))
665
+ return mb_strlen($this->_matches[0], $this->_encoding);
666
+
667
+ return null;
668
+ }
669
+
670
+ /**
671
+ * Extracts a string corresponding to <tt>mb_substr(@mlink{$rest}, 0, $len)</tt>, without
672
+ * advancing the scan pointer.
673
+ * @code
674
+ * $s = new StringScanner('test string');
675
+ * $s->peek(7) # => "test st"
676
+ * $s->peek(7) # => "test st"
677
+ * @endcode
678
+ *
679
+ * @param $len
680
+ * @return string
681
+ */
682
+ public function peek($len)
683
+ {
684
+ if($this->eos)
685
+ return null;
686
+
687
+ return mb_substr($this->_rest, $this->_curr, $len, $this->_encoding);
688
+ }
689
+
690
+ /**
691
+ * Returns the character position of the scan @mlink{$pointer}. In the '{@link reset() reset}' position, this
692
+ * value is zero. In the '{@link terminate() terminated}' position (i.e. the string is exhausted),
693
+ * this value is the size of the @mlink{$string}.
694
+ * @magicalias{$pos, $pointer}
695
+ *
696
+ * In short, it's a 0-based index into the @mlink{$string}.
697
+ * @code
698
+ * $s = new StringScanner('test string');
699
+ * $s->pos; # -> 0
700
+ * $s->scanUntil /str/ # -> "test str"
701
+ * $s->pos; # -> 8
702
+ * $s->terminate(); # -> #<StringScanner fin>
703
+ * $s->pos; # -> 11
704
+ * @endcode
705
+ *
706
+ * @return int
707
+ */
708
+ public function getPos() {
709
+ return $this->_curr;
710
+ }
711
+
712
+ /**
713
+ * Set the byte position of the scan pointer.
714
+ * @code
715
+ * $s = new StringScanner('test string');
716
+ * $s->pos = 7 # -> 7
717
+ * $s->rest; # -> "ring"
718
+ * @endcode
719
+ *
720
+ * @param int $i
721
+ * @return int The new position
722
+ */
723
+ public function setPointer($i)
724
+ {
725
+ if ($i < 0)
726
+ $i += $this->_restSize;
727
+
728
+ if ($i < 0 || $i > $this->_restSize)
729
+ throw new Exception("Index out of range.");
730
+
731
+ $this->_curr = $i;
732
+
733
+ return $i;
734
+ }
735
+
736
+ /**
737
+ * Same as {@link setPointer()}
738
+ * Set the byte position of the scan pointer.
739
+ * @code
740
+ * $s = new StringScanner('test string');
741
+ * $s->pos = 7 # -> 7
742
+ * $s->rest; # -> "ring"
743
+ * @endcode
744
+ *
745
+ * @param int $i
746
+ * @return int The new position
747
+ */
748
+ public function setPos($i) {
749
+ return $this->setPointer($i);
750
+ }
751
+
752
+ /**
753
+ * Return the <i><b>post</b>-match</i> (in the regular expression sense) of the last scan.
754
+ * @code
755
+ * $s = new StringScanner('test string');
756
+ * $s->scan('/\w+/'); # -> "test"
757
+ * $s->scan('/\s+/'); # -> " "
758
+ * $s->preMatch; # -> "test"
759
+ * $s->postMatch; # -> "string"
760
+ * @endcode
761
+ *
762
+ * @return string
763
+ */
764
+ public function getPostMatch()
765
+ {
766
+ if(!isset($this->_matches[0]))
767
+ return null;
768
+
769
+ return mb_substr($this->_string, $this->_lastMatchEnd, $this->_size, $this->_encoding);
770
+ }
771
+
772
+ /**
773
+ * Return the <i><b>pre</b>-match</i> (in the regular expression sense) of the last scan.
774
+ * @code
775
+ * $s = new StringScanner('test string');
776
+ * $s->scan('/\w+/'); # -> "test"
777
+ * $s->scan('/\s+/'); # -> " "
778
+ * $s->preMatch; # -> "test"
779
+ * $s->postMatch; # -> "string"
780
+ * @endcode
781
+ *
782
+ * @return string
783
+ */
784
+ public function getPreMatch()
785
+ {
786
+ if(!isset($this->_matches[0]))
787
+ return null;
788
+
789
+ return mb_substr($this->_string, 0, $this->_lastMatchBeg, $this->_encoding);
790
+ }
791
+
792
+ /**
793
+ * Reset the scan pointer (index 0) and clear matching data.
794
+ */
795
+ public function reset()
796
+ {
797
+ $this->_string_updated(false);
798
+ }
799
+
800
+ /**
801
+ * Returns the "rest" of the string (i.e. everything after the scan pointer).
802
+ * If there is no more data (eos? = true), it returns <tt>""</tt>.
803
+ *
804
+ * @return string
805
+ */
806
+ public function getRest()
807
+ {
808
+ return $this->_rest;
809
+ }
810
+
811
+ /**
812
+ * $s->restSize() is equivalent to mb_strlen($s->rest)
813
+ */
814
+ public function restSize() {
815
+ return $this->_restSize;
816
+ }
817
+
818
+ /**
819
+ * Tries to match with $pattern at the current position. If there's a match,
820
+ * the scanner advances the scan @mlink{$pointer} and returns the matched string.
821
+ * Otherwise, the scanner returns <pre>null</pre>.
822
+ * @code
823
+ * $s = new StringScanner('test string');
824
+ * echo $s->scan('/\w+/'); # -> "test"
825
+ * echo $s->scan('/\w+/'); # -> null
826
+ * echo $s->scan('/\s+/'); # -> " "
827
+ * echo $s->scan('/\w+/'); # -> "string"
828
+ * echo $s->scan('/./'); # -> null
829
+ * @endcode
830
+ *
831
+ * @param string $pattern
832
+ */
833
+ public function scan($pattern)
834
+ {
835
+ return $this->doScan($pattern, true, true, true);
836
+ }
837
+
838
+ /**
839
+ * Tests whether the given +pattern+ is matched from the current scan pointer.
840
+ * Advances the scan pointer if +advance_pointer_p+ is true.
841
+ * Returns the matched string if +return_string_p+ is true.
842
+ * The match register is affected.
843
+ *
844
+ * "full" means "#scan with full parameters".
845
+ *
846
+ * @todo Write test
847
+ * @param string $pattern The regular expression
848
+ * @param bool $advance_pointer Wheter to update the pointer or not
849
+ * @param bool $return_string Wheter to return the matched string or the position
850
+ * @return mixed Either the matched string (if $return_string is true) or the end position of the match
851
+ */
852
+ public function scanFull($pattern, $advance_pointer, $return_string)
853
+ {
854
+ return $this->doScan($pattern, $advance_pointer, $return_string, true);
855
+ }
856
+
857
+ /**
858
+ * Scans the string _until_ the +pattern+ is matched. Returns the substring up
859
+ * to and including the end of the match, advancing the scan pointer to that
860
+ * location. If there is no match, +null+ is returned.
861
+ * @code
862
+ * $s = new StringScanner("Fri Dec 12 1975 14:39");
863
+ * $s->scanUntil('/1/'); # -> "Fri Dec 1"
864
+ * $s->preMatch(); # -> "Fri Dec "
865
+ * $s->scanUntil('/XYZ/'); # -> null
866
+ * @endcode
867
+ *
868
+ * @param string $pattern
869
+ * @return string The matched string
870
+ */
871
+ public function scanUntil($pattern)
872
+ {
873
+ return $this->doScan($pattern, true, true, false);
874
+ }
875
+
876
+ /**
877
+ * Scans the string _until_ the $pattern is matched.
878
+ * Advances the scan pointer if $advance_pointer, otherwise not.
879
+ * Returns the matched string if $return_string is true, otherwise
880
+ * returns the number of bytes advanced.
881
+ * This method does affect the match register.
882
+ *
883
+ * @todo Write test
884
+ * @param string $pattern The regular expression
885
+ * @param bool $advance_pointer Wheter to update the pointer or not
886
+ * @param bool $return_string Wheter to return the matched string or the position
887
+ * @return mixed Either the matched string (if $return_string is true) or the end position of the match
888
+ */
889
+ public function searchFull($pattern, $advance_pointer, $return_string)
890
+ {
891
+ return $this->doScan($pattern, $advance_pointer, $return_string, false);
892
+ }
893
+
894
+ /**
895
+ * Attempts to skip over the given +pattern+ beginning with the scan pointer.
896
+ * If it matches, the scan pointer is advanced to the end of the match, and the
897
+ * length of the match is returned. Otherwise, +null+ is returned.
898
+ *
899
+ * It's similar to #scan, but without returning the matched string.
900
+ * @code
901
+ * $s = new StringScanner('test string');
902
+ * echo $s->skip('/\w+/'); # -> 4
903
+ * echo $s->skip('/\w+/'); # -> null
904
+ * echo $s->skip('/\s+/'); # -> 1
905
+ * echo $s->skip('/\w+/'); # -> 6
906
+ * echo $s->skip('/./'); # -> null
907
+ * @endcode
908
+ *
909
+ * @param string $pattern
910
+ * @return int The new position
911
+ */
912
+ public function skip($pattern)
913
+ {
914
+ return $this->doScan($pattern, true, false, true);
915
+ }
916
+
917
+ /**
918
+ * Advances the scan pointer until +pattern+ is matched and consumed. Returns
919
+ * the number of bytes advanced, or +null+ if no match was found.
920
+ *
921
+ * Look ahead to match +pattern+, and advance the scan pointer to the _end_
922
+ * of the match. Return the number of characters advanced, or +null+ if the
923
+ * match was unsuccessful.
924
+ *
925
+ * It's similar to #scanUntil, but without returning the intervening string.
926
+ *@code
927
+ * $s = new StringScanner("Fri Dec 12 1975 14:39");
928
+ * $s->skipUntil('/12/'); # -> 10
929
+ * @endcode
930
+ *
931
+ * @param string $pattern
932
+ * @return string The new pointer position
933
+ */
934
+ public function skipUntil($pattern)
935
+ {
936
+ return $this->doScan($pattern, true, false, false);
937
+ }
938
+
939
+ /**
940
+ * Returns the string being scanned.
941
+ *
942
+ * @return string
943
+ */
944
+ public function getString() {
945
+ return $this->_string;
946
+ }
947
+
948
+ /**
949
+ * Changes the string being scanned to +str+ and resets the scanner.
950
+ *
951
+ * @param string $str
952
+ * @return string $str
953
+ */
954
+ public function setString($str) {
955
+ $this->_string = $str;
956
+ $this->reset();
957
+ return $str;
958
+ }
959
+
960
+ /**
961
+ * Set the scan pointer to the end of the string and clear matching data.
962
+ */
963
+ public function terminate() {
964
+ $this->_curr = $this->_size;
965
+ $this->_clear_matched();
966
+ }
967
+
968
+ /**
969
+ * Set the scan pointer to the previous position. Only one previous position is
970
+ * remembered, and it changes with each scanning operation.
971
+ * @code
972
+ * $s = new StringScanner('test string');
973
+ * $s->scan('/\w+/'); # => "test"
974
+ * $s->unscan();
975
+ * $s->scan('/../'); # => "te"
976
+ * $s->scan('/\d/'); # => null
977
+ * $s->unscan(); # ScanError: unscan failed: previous match record not exist
978
+ * @endcode
979
+ */
980
+ public function unscan()
981
+ {
982
+ if(!isset($this->_matches[0]))
983
+ throw new Exception("Unscan failed: previous match record not exist.");
984
+
985
+ $this->_curr = $this->_prev;
986
+ $this->_rest = mb_substr($this->_string, $this->_curr, $this->_size, $this->_encoding);
987
+ $this->_clear_matched();
988
+ }
989
+ }