wproot 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }