1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12:
13:
14: 15: 16: 17: 18: 19:
20: class TexyParser extends TexyObject
21: {
22:
23: protected $texy;
24:
25:
26: protected $element;
27:
28:
29: public $patterns;
30:
31:
32:
33: 34: 35:
36: public function getTexy()
37: {
38: return $this->texy;
39: }
40:
41: }
42:
43:
44:
45:
46:
47:
48: 49: 50:
51: class TexyBlockParser extends TexyParser
52: {
53:
54: private $text;
55:
56:
57: private $offset;
58:
59:
60: private $indented;
61:
62:
63:
64: 65: 66: 67:
68: public function __construct(Texy $texy, TexyHtml $element, $indented)
69: {
70: $this->texy = $texy;
71: $this->element = $element;
72: $this->indented = (bool) $indented;
73: $this->patterns = $texy->getBlockPatterns();
74: }
75:
76:
77:
78: public function isIndented()
79: {
80: return $this->indented;
81: }
82:
83:
84:
85: 86: 87: public function next($pattern, &$matches)
88: {
89: $matches = NULL;
90: $ok = preg_match(
91: $pattern . 'Am', 92: $this->text,
93: $matches,
94: PREG_OFFSET_CAPTURE,
95: $this->offset
96: );
97:
98: if ($ok) {
99: $this->offset += strlen($matches[0][0]) + 1; 100: foreach ($matches as $key => $value) $matches[$key] = $value[0];
101: }
102: return $ok;
103: }
104:
105:
106:
107: public function moveBackward($linesCount = 1)
108: {
109: while (--$this->offset > 0)
110: if ($this->text{ $this->offset-1 } === "\n") {
111: $linesCount--;
112: if ($linesCount < 1) break;
113: }
114:
115: $this->offset = max($this->offset, 0);
116: }
117:
118:
119:
120: public static function cmp($a, $b)
121: {
122: if ($a[0] === $b[0]) return $a[3] < $b[3] ? -1 : 1;
123: if ($a[0] < $b[0]) return -1;
124: return 1;
125: }
126:
127:
128:
129: 130: 131: 132:
133: public function parse($text)
134: {
135: $tx = $this->texy;
136:
137: $tx->invokeHandlers('beforeBlockParse', array($this, & $text));
138:
139: 140: $this->text = $text;
141: $this->offset = 0;
142:
143: 144: $matches = array();
145: $priority = 0;
146: foreach ($this->patterns as $name => $pattern)
147: {
148: preg_match_all(
149: $pattern['pattern'],
150: $text,
151: $ms,
152: PREG_OFFSET_CAPTURE | PREG_SET_ORDER
153: );
154:
155: foreach ($ms as $m) {
156: $offset = $m[0][1];
157: foreach ($m as $k => $v) $m[$k] = $v[0];
158: $matches[] = array($offset, $name, $m, $priority);
159: }
160: $priority++;
161: }
162: unset($name, $pattern, $ms, $m, $k, $v);
163:
164: usort($matches, array(__CLASS__, 'cmp')); 165: $matches[] = array(strlen($text), NULL, NULL); 166:
167:
168: 169: $el = $this->element;
170: $cursor = 0;
171: do {
172: do {
173: list($mOffset, $mName, $mMatches) = $matches[$cursor];
174: $cursor++;
175: if ($mName === NULL) break;
176: if ($mOffset >= $this->offset) break;
177: } while (1);
178:
179: 180: if ($mOffset > $this->offset) {
181: $s = trim(substr($text, $this->offset, $mOffset - $this->offset));
182: if ($s !== '') {
183: $tx->paragraphModule->process($this, $s, $el);
184: }
185: }
186:
187: if ($mName === NULL) break; 188:
189: $this->offset = $mOffset + strlen($mMatches[0]) + 1; 190:
191: $res = call_user_func_array(
192: $this->patterns[$mName]['handler'],
193: array($this, $mMatches, $mName)
194: );
195:
196: if ($res === FALSE || $this->offset <= $mOffset) { 197: 198: $this->offset = $mOffset; 199: continue;
200:
201: } elseif ($res instanceof TexyHtml) {
202: $el->insert(NULL, $res);
203:
204: } elseif (is_string($res)) {
205: $el->insert(NULL, $res);
206: }
207:
208: } while (1);
209: }
210:
211: }
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
224: 225: 226:
227: class TexyLineParser extends TexyParser
228: {
229:
230: public $again;
231:
232:
233:
234: 235: 236: 237:
238: public function __construct(Texy $texy, TexyHtml $element)
239: {
240: $this->texy = $texy;
241: $this->element = $element;
242: $this->patterns = $texy->getLinePatterns();
243: }
244:
245:
246:
247: 248: 249: 250:
251: public function parse($text)
252: {
253: $tx = $this->texy;
254:
255: 256: $pl = $this->patterns;
257: if (!$pl) {
258: 259: $this->element->insert(NULL, $text);
260: return;
261: }
262:
263: $offset = 0;
264: $names = array_keys($pl);
265: $arrMatches = $arrOffset = array();
266: foreach ($names as $name) $arrOffset[$name] = -1;
267:
268:
269: 270: do {
271: $min = NULL;
272: $minOffset = strlen($text);
273:
274: foreach ($names as $index => $name)
275: {
276: if ($arrOffset[$name] < $offset) {
277: $delta = ($arrOffset[$name] === -2) ? 1 : 0;
278:
279: if (preg_match($pl[$name]['pattern'],
280: $text,
281: $arrMatches[$name],
282: PREG_OFFSET_CAPTURE,
283: $offset + $delta)
284: ) {
285: $m = & $arrMatches[$name];
286: if (!strlen($m[0][0])) continue;
287: $arrOffset[$name] = $m[0][1];
288: foreach ($m as $keyx => $value) $m[$keyx] = $value[0];
289:
290: } else {
291: 292: if (!$pl[$name]['again'] || !preg_match($pl[$name]['again'], $text, $foo, NULL, $offset + $delta)) {
293: unset($names[$index]);
294: }
295: continue;
296: }
297: } 298:
299: if ($arrOffset[$name] < $minOffset) {
300: $minOffset = $arrOffset[$name];
301: $min = $name;
302: }
303: } 304:
305: if ($min === NULL) break;
306:
307: $px = $pl[$min];
308: $offset = $start = $arrOffset[$min];
309:
310: $this->again = FALSE;
311: $res = call_user_func_array(
312: $px['handler'],
313: array($this, $arrMatches[$min], $min)
314: );
315:
316: if ($res instanceof TexyHtml) {
317: $res = $res->toString($tx);
318: } elseif ($res === FALSE) {
319: $arrOffset[$min] = -2;
320: continue;
321: }
322:
323: $len = strlen($arrMatches[$min][0]);
324: $text = substr_replace(
325: $text,
326: (string) $res,
327: $start,
328: $len
329: );
330:
331: $delta = strlen($res) - $len;
332: foreach ($names as $name) {
333: if ($arrOffset[$name] < $start + $len) $arrOffset[$name] = -1;
334: else $arrOffset[$name] += $delta;
335: }
336:
337: if ($this->again) {
338: $arrOffset[$min] = -2;
339: } else {
340: $arrOffset[$min] = -1;
341: $offset += strlen($res);
342: }
343:
344: } while (1);
345:
346: $this->element->insert(NULL, $text);
347: }
348:
349: }
350: