]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/stxview/StructuredText/DocumentClass.py
Added some simple sample apps
[wxWidgets.git] / wxPython / samples / stxview / StructuredText / DocumentClass.py
1 ##############################################################################
2 #
3 # Zope Public License (ZPL) Version 1.0
4 # -------------------------------------
5 #
6 # Copyright (c) Digital Creations. All rights reserved.
7 #
8 # This license has been certified as Open Source(tm).
9 #
10 # Redistribution and use in source and binary forms, with or without
11 # modification, are permitted provided that the following conditions are
12 # met:
13 #
14 # 1. Redistributions in source code must retain the above copyright
15 # notice, this list of conditions, and the following disclaimer.
16 #
17 # 2. Redistributions in binary form must reproduce the above copyright
18 # notice, this list of conditions, and the following disclaimer in
19 # the documentation and/or other materials provided with the
20 # distribution.
21 #
22 # 3. Digital Creations requests that attribution be given to Zope
23 # in any manner possible. Zope includes a "Powered by Zope"
24 # button that is installed by default. While it is not a license
25 # violation to remove this button, it is requested that the
26 # attribution remain. A significant investment has been put
27 # into Zope, and this effort will continue if the Zope community
28 # continues to grow. This is one way to assure that growth.
29 #
30 # 4. All advertising materials and documentation mentioning
31 # features derived from or use of this software must display
32 # the following acknowledgement:
33 #
34 # "This product includes software developed by Digital Creations
35 # for use in the Z Object Publishing Environment
36 # (http://www.zope.org/)."
37 #
38 # In the event that the product being advertised includes an
39 # intact Zope distribution (with copyright and license included)
40 # then this clause is waived.
41 #
42 # 5. Names associated with Zope or Digital Creations must not be used to
43 # endorse or promote products derived from this software without
44 # prior written permission from Digital Creations.
45 #
46 # 6. Modified redistributions of any form whatsoever must retain
47 # the following acknowledgment:
48 #
49 # "This product includes software developed by Digital Creations
50 # for use in the Z Object Publishing Environment
51 # (http://www.zope.org/)."
52 #
53 # Intact (re-)distributions of any official Zope release do not
54 # require an external acknowledgement.
55 #
56 # 7. Modifications are encouraged but must be packaged separately as
57 # patches to official Zope releases. Distributions that do not
58 # clearly separate the patches from the original work must be clearly
59 # labeled as unofficial distributions. Modifications which do not
60 # carry the name Zope may be packaged in any form, as long as they
61 # conform to all of the clauses above.
62 #
63 #
64 # Disclaimer
65 #
66 # THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
67 # EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
68 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
69 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
70 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
71 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
72 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
73 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
74 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
75 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
76 # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
77 # SUCH DAMAGE.
78 #
79 #
80 # This software consists of contributions made by Digital Creations and
81 # many individuals on behalf of Digital Creations. Specific
82 # attributions are listed in the accompanying credits file.
83 #
84 ##############################################################################
85
86 import re, ST, STDOM
87 from string import split, join, replace, expandtabs, strip, find, rstrip
88
89 StringType=type('')
90 ListType=type([])
91
92 class StructuredTextExample(ST.StructuredTextParagraph):
93 """Represents a section of document with literal text, as for examples"""
94
95 def __init__(self, subs, **kw):
96 t=[]; a=t.append
97 for s in subs: a(s.getNodeValue())
98 apply(ST.StructuredTextParagraph.__init__,
99 (self, join(t,'\n\n'), ()),
100 kw)
101
102 def getColorizableTexts(self): return ()
103 def setColorizableTexts(self, src): pass # never color examples
104
105 class StructuredTextBullet(ST.StructuredTextParagraph):
106 """Represents a section of a document with a title and a body"""
107
108 class StructuredTextNumbered(ST.StructuredTextParagraph):
109 """Represents a section of a document with a title and a body"""
110
111 class StructuredTextDescriptionTitle(ST.StructuredTextParagraph):
112 """Represents a section of a document with a title and a body"""
113
114 class StructuredTextDescriptionBody(ST.StructuredTextParagraph):
115 """Represents a section of a document with a title and a body"""
116
117 class StructuredTextDescription(ST.StructuredTextParagraph):
118 """Represents a section of a document with a title and a body"""
119
120 def __init__(self, title, src, subs, **kw):
121 apply(ST.StructuredTextParagraph.__init__, (self, src, subs), kw)
122 self._title=title
123
124 def getColorizableTexts(self): return self._title, self._src
125 def setColorizableTexts(self, src): self._title, self._src = src
126
127 def getChildren(self):
128 return (StructuredTextDescriptionTitle(self._title),
129 StructuredTextDescriptionBody(self._src, self._subs))
130
131 class StructuredTextSectionTitle(ST.StructuredTextParagraph):
132 """Represents a section of a document with a title and a body"""
133
134 class StructuredTextSection(ST.StructuredTextParagraph):
135 """Represents a section of a document with a title and a body"""
136 def __init__(self, src, subs=None, **kw):
137 apply(ST.StructuredTextParagraph.__init__,
138 (self, StructuredTextSectionTitle(src), subs),
139 kw)
140
141 # a StructuredTextTable holds StructuredTextRows
142 class StructuredTextTable(ST.StructuredTextDocument):
143 """
144 rows is a list of lists containing tuples, which
145 represent the columns/cells in each rows.
146 EX
147 rows = [[('row 1:column1',1)],[('row2:column1',1)]]
148 """
149
150 def __init__(self, rows, src, subs, **kw):
151 apply(ST.StructuredTextDocument.__init__,(self,subs),kw)
152 self._rows = []
153 for row in rows:
154 if row:
155 self._rows.append(StructuredTextRow(row,kw))
156
157 def getRows(self):
158 return [self._rows]
159
160 def _getRows(self):
161 return self.getRows()
162
163 def getColumns(self):
164 result = []
165 for row in self._rows:
166 result.append(row.getColumns())
167 return result
168
169 def _getColumns(self):
170 return self.getColumns()
171
172 def setColumns(self,columns):
173 for index in range(len(self._rows)):
174 self._rows[index].setColumns(columns[index])
175
176 def _setColumns(self,columns):
177 return self.setColumns(columns)
178
179 def getColorizableTexts(self):
180 """
181 return a tuple where each item is a column/cell's
182 contents. The tuple, result, will be of this format.
183 ("r1 col1", "r1=col2", "r2 col1", "r2 col2")
184 """
185
186 result = []
187 for row in self._rows:
188 for column in row.getColumns()[0]:
189 result.append(column.getColorizableTexts()[0])
190 return result
191
192 def setColorizableTexts(self,texts):
193 """
194 texts is going to a tuple where each item is the
195 result of being mapped to the colortext function.
196 Need to insert the results appropriately into the
197 individual columns/cells
198 """
199 for row_index in range(len(self._rows)):
200 for column_index in range(len(self._rows[row_index]._columns)):
201 self._rows[row_index]._columns[column_index].setColorizableTexts((texts[0],))
202 texts = texts[1:]
203
204 def _getColorizableTexts(self):
205 return self.getColorizableTexts()
206
207 def _setColorizableTexts(self):
208 return self.setColorizableTexts()
209
210 # StructuredTextRow holds StructuredTextColumns
211 class StructuredTextRow(ST.StructuredTextDocument):
212
213 def __init__(self,row,kw):
214 """
215 row is a list of tuples, where each tuple is
216 the raw text for a cell/column and the span
217 of that cell/column".
218 EX
219 [('this is column one',1), ('this is column two',1)]
220 """
221
222 apply(ST.StructuredTextDocument.__init__,(self,[]),kw)
223 self._columns = []
224 for column in row:
225 self._columns.append(StructuredTextColumn(column[0],column[1],kw))
226
227 def getColumns(self):
228 return [self._columns]
229
230 def _getColumns(self):
231 return [self._columns]
232
233 def setColumns(self,columns):
234 self._columns = columns
235
236 def _setColumns(self,columns):
237 return self.setColumns(columns)
238
239 # this holds the text of a table cell
240 class StructuredTextColumn(ST.StructuredTextParagraph):
241 """
242 StructuredTextColumn is a cell/column in a table.
243 A cell can hold multiple paragraphs. The cell
244 is either classified as a StructuredTextTableHeader
245 or StructuredTextTableData.
246 """
247
248 def __init__(self,text,span,kw):
249 # print "StructuredTextColumn", text, span
250 apply(ST.StructuredTextParagraph.__init__,(self,text,[]),kw)
251 self._span = span
252
253 def getSpan(self):
254 return self._span
255
256 def _getSpan(self):
257 return self._span
258
259 class StructuredTextTableHeader(ST.StructuredTextDocument): pass
260
261 class StructuredTextTableData(ST.StructuredTextDocument): pass
262
263 class StructuredTextMarkup(STDOM.Element):
264
265 def __init__(self, v, **kw):
266 self._value=v
267 self._attributes=kw.keys()
268 for k, v in kw.items(): setattr(self, k, v)
269
270 def getChildren(self, type=type, lt=type([])):
271 v=self._value
272 if type(v) is not lt: v=[v]
273 return v
274
275 def getColorizableTexts(self): return self._value,
276 def setColorizableTexts(self, v): self._value=v[0]
277
278 def __repr__(self):
279 return '%s(%s)' % (self.__class__.__name__, `self._value`)
280
281 class StructuredTextLiteral(StructuredTextMarkup):
282 def getColorizableTexts(self): return ()
283 def setColorizableTexts(self, v): pass
284
285 class StructuredTextEmphasis(StructuredTextMarkup): pass
286
287 class StructuredTextStrong(StructuredTextMarkup): pass
288
289 class StructuredTextInnerLink(StructuredTextMarkup): pass
290
291 class StructuredTextNamedLink(StructuredTextMarkup): pass
292
293 class StructuredTextUnderline(StructuredTextMarkup): pass
294
295 class StructuredTextSGML(StructuredTextMarkup): pass
296
297 class StructuredTextLink(StructuredTextMarkup): pass
298
299 class DocumentClass:
300 """
301 Class instance calls [ex.=> x()] require a structured text
302 structure. Doc will then parse each paragraph in the structure
303 and will find the special structures within each paragraph.
304 Each special structure will be stored as an instance. Special
305 structures within another special structure are stored within
306 the 'top' structure
307 EX : '-underline this-' => would be turned into an underline
308 instance. '-underline **this**' would be stored as an underline
309 instance with a strong instance stored in its string
310 """
311
312 #'doc_table',
313 paragraph_types = [
314 'doc_bullet',
315 'doc_numbered',
316 'doc_description',
317 'doc_header',
318 'doc_table',
319 ]
320
321 #'doc_inner_link',
322 #'doc_named_link',
323 #'doc_underline',
324 text_types = [
325 'doc_href',
326 'doc_strong',
327 'doc_emphasize',
328 'doc_literal',
329 'doc_sgml'
330 ]
331
332 def __call__(self, doc):
333 if type(doc) is type(''):
334 doc=ST.StructuredText(doc)
335 doc.setSubparagraphs(self.color_paragraphs(
336 doc.getSubparagraphs()))
337 else:
338 doc=ST.StructuredTextDocument(self.color_paragraphs(
339 doc.getSubparagraphs()))
340 return doc
341
342 def parse(self, raw_string, text_type,
343 type=type, st=type(''), lt=type([])):
344
345 """
346 Parse accepts a raw_string, an expr to test the raw_string,
347 and the raw_string's subparagraphs.
348
349 Parse will continue to search through raw_string until
350 all instances of expr in raw_string are found.
351
352 If no instances of expr are found, raw_string is returned.
353 Otherwise a list of substrings and instances is returned
354 """
355
356 tmp = [] # the list to be returned if raw_string is split
357 append=tmp.append
358
359 if type(text_type) is st: text_type=getattr(self, text_type)
360
361 while 1:
362 t = text_type(raw_string)
363 if not t: break
364 #an instance of expr was found
365 t, start, end = t
366
367 if start: append(raw_string[0:start])
368
369 tt=type(t)
370 if tt is st:
371 # if we get a string back, add it to text to be parsed
372 raw_string = t+raw_string[end:len(raw_string)]
373 else:
374 if tt is lt:
375 # is we get a list, append it's elements
376 tmp[len(tmp):]=t
377 else:
378 # normal case, an object
379 append(t)
380 raw_string = raw_string[end:len(raw_string)]
381
382 if not tmp: return raw_string # nothing found
383
384 if raw_string: append(raw_string)
385 elif len(tmp)==1: return tmp[0]
386
387 return tmp
388
389
390 def color_text(self, str, types=None):
391 """Search the paragraph for each special structure
392 """
393 if types is None: types=self.text_types
394
395 for text_type in types:
396
397 if type(str) is StringType:
398 str = self.parse(str, text_type)
399 elif type(str) is ListType:
400 r=[]; a=r.append
401 for s in str:
402 if type(s) is StringType:
403 s=self.parse(s, text_type)
404 if type(s) is ListType: r[len(r):]=s
405 else: a(s)
406 else:
407 s.setColorizableTexts(
408 map(self.color_text,
409 s.getColorizableTexts()
410 ))
411 a(s)
412 str=r
413 else:
414 r=[]; a=r.append; color=self.color_text
415 for s in str.getColorizableTexts():
416 color(s, (text_type,))
417 a(s)
418
419 str.setColorizableTexts(r)
420
421 return str
422
423 def color_paragraphs(self, raw_paragraphs,
424 type=type, sequence_types=(type([]), type(())),
425 st=type('')):
426 result=[]
427 for paragraph in raw_paragraphs:
428 #print type(paragraph)
429 if paragraph.getNodeName() != 'StructuredTextParagraph':
430 result.append(paragraph)
431 continue
432
433 for pt in self.paragraph_types:
434 if type(pt) is st:
435 # grab the corresponding function
436 pt=getattr(self, pt)
437 # evaluate the paragraph
438 r=pt(paragraph)
439 if r:
440 if type(r) not in sequence_types:
441 r=r,
442 new_paragraphs=r
443 for paragraph in new_paragraphs:
444 paragraph.setSubparagraphs(self.color_paragraphs(paragraph.getSubparagraphs()))
445 break
446 else:
447 new_paragraphs=ST.StructuredTextParagraph(paragraph.getColorizableTexts()[0],
448 self.color_paragraphs(paragraph.getSubparagraphs()),
449 indent=paragraph.indent),
450
451 # color the inline StructuredText types
452 # for each StructuredTextParagraph
453 for paragraph in new_paragraphs:
454
455 if paragraph.getNodeName() is "StructuredTextTable":
456 #print "we have a table"
457 cells = paragraph.getColumns()
458 text = paragraph.getColorizableTexts()
459 text = map(ST.StructuredText,text)
460 text = map(self.__call__,text)
461 #for index in range(len(text)):
462 # text[index].setColorizableTexts(map(self.color_text,text[index].getColorizableTexts()))
463 paragraph.setColorizableTexts(text)
464
465 paragraph.setColorizableTexts(
466 map(self.color_text,
467 paragraph.getColorizableTexts()
468 ))
469 result.append(paragraph)
470
471 return result
472
473 def doc_table(self, paragraph, expr = re.compile('\s*\|[-]+\|').match):
474 text = paragraph.getColorizableTexts()[0]
475 m = expr(text)
476
477 subs = paragraph.getSubparagraphs()
478
479 if not (m):
480 return None
481 rows = []
482
483 rows = split(text,'\n')
484
485 spans = []
486 ROWS = []
487 COLS = []
488
489 TDdivider = re.compile("[\-]+").match
490 THdivider = re.compile("[\=]+").match
491
492 # find where the column markers are located
493 col = re.compile('\|').search
494 text = strip(text)
495 rows = split(text,'\n')
496 for row in range(len(rows)):
497 rows[row] = strip(rows[row])
498
499 for row in rows:
500 tmp = strip(row)
501 tmp = row[1:len(tmp)-1] # remove leading and trailing |
502 offset = 0
503 if col(tmp):
504 while col(tmp):
505 start,end = col(tmp).span()
506 if not start+offset in spans:
507 spans.append(start + offset)
508 COLS.append((tmp[0:start],start+offset))
509 tmp = " " + tmp[end:]
510 offset = offset + (start)
511 if not offset+len(tmp) in spans:
512 spans.append(offset+len(tmp))
513 COLS.append((tmp,offset+len(tmp)))
514 ROWS.append(COLS)
515 COLS = []
516
517 spans.sort()
518
519 ROWS = ROWS[1:len(ROWS)]
520
521 # find each column span
522 cols = []
523 tmp = []
524
525 for row in ROWS:
526 for c in row:
527 tmp.append(c[1])
528 cols.append(tmp)
529 tmp = []
530
531 cur = 1 # the current column span
532 tmp = []
533 C = [] # holds the span of each cell
534 for col in cols:
535 for span in spans:
536 if not span in col:
537 cur = cur + 1
538 else:
539 tmp.append(cur)
540 cur = 1
541 C.append(tmp)
542 tmp = []
543
544 # make rows contain the cell's text and the span
545 # of that cell
546 for index in range(len(C)):
547 for i in range(len(C[index])):
548 ROWS[index][i] = (ROWS[index][i][0],C[index][i])
549 rows = ROWS
550
551 # now munge the table cells together
552 ROWS = []
553 COLS = []
554 for row in rows:
555 for index in range(len(row)):
556 if not COLS:
557 COLS = range(len(row))
558 for i in range(len(COLS)):
559 COLS[i] = ["",1]
560 if TDdivider(row[index][0]) or THdivider(row[index][0]):
561 ROWS.append(COLS)
562 COLS = []
563 else:
564 COLS[index][0] = COLS[index][0] + rstrip(row[index][0]) + "\n"
565 COLS[index][1] = row[index][1]
566 return StructuredTextTable(ROWS,text,subs,indent=paragraph.indent)
567
568 def doc_bullet(self, paragraph, expr = re.compile('\s*[-*o]\s+').match):
569 top=paragraph.getColorizableTexts()[0]
570 m=expr(top)
571
572 if not m:
573 return None
574
575 subs=paragraph.getSubparagraphs()
576 if top[-2:]=='::':
577 subs=[StructuredTextExample(subs)]
578 top=top[:-1]
579 return StructuredTextBullet(top[m.span()[1]:], subs,
580 indent=paragraph.indent,
581 bullet=top[:m.span()[1]]
582 )
583
584 def doc_numbered(
585 self, paragraph,
586 expr = re.compile('(\s*[a-zA-Z]+\.)|(\s*[0-9]+\.)|(\s*[0-9]+\s+)').match):
587
588 # This is the old expression. It had a nasty habit
589 # of grabbing paragraphs that began with a single
590 # letter word even if there was no following period.
591
592 #expr = re.compile('\s*'
593 # '(([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.)*'
594 # '([a-zA-Z]|[0-9]+|[ivxlcdmIVXLCDM]+)\.?'
595 # '\s+').match):
596
597 top=paragraph.getColorizableTexts()[0]
598 m=expr(top)
599 if not m: return None
600 subs=paragraph.getSubparagraphs()
601 if top[-2:]=='::':
602 subs=[StructuredTextExample(subs)]
603 top=top[:-1]
604 return StructuredTextNumbered(top[m.span()[1]:], subs,
605 indent=paragraph.indent,
606 number=top[:m.span()[1]])
607
608 def doc_description(
609 self, paragraph,
610 delim = re.compile('\s+--\s+').search,
611 nb=re.compile(r'[^\0- ]').search,
612 ):
613
614 top=paragraph.getColorizableTexts()[0]
615 d=delim(top)
616 if not d: return None
617 start, end = d.span()
618 title=top[:start]
619 if find(title, '\n') >= 0: return None
620 if not nb(title): return None
621 d=top[start:end]
622 top=top[end:]
623
624 subs=paragraph.getSubparagraphs()
625 if top[-2:]=='::':
626 subs=[StructuredTextExample(subs)]
627 top=top[:-1]
628
629 return StructuredTextDescription(
630 title, top, subs,
631 indent=paragraph.indent,
632 delim=d)
633
634 def doc_header(self, paragraph,
635 expr = re.compile('[ a-zA-Z0-9.:/,-_*<>\?\'\"]+').match
636 ):
637 subs=paragraph.getSubparagraphs()
638 if not subs: return None
639 top=paragraph.getColorizableTexts()[0]
640 if not strip(top): return None
641 if top[-2:]=='::':
642 subs=StructuredTextExample(subs)
643 if strip(top)=='::': return subs
644 return ST.StructuredTextParagraph(
645 top[:-1], [subs], indent=paragraph.indent)
646
647 if find(top,'\n') >= 0: return None
648 return StructuredTextSection(top, subs, indent=paragraph.indent)
649
650 def doc_literal(
651 self, s,
652 expr=re.compile(
653 "(?:\s|^)'" # open
654 "([^ \t\n\r\f\v']|[^ \t\n\r\f\v'][^\n']*[^ \t\n\r\f\v'])" # contents
655 "'(?:\s|[,.;:!?]|$)" # close
656 ).search):
657
658 r=expr(s)
659 if r:
660 start, end = r.span(1)
661 return (StructuredTextLiteral(s[start:end]), start-1, end+1)
662 else:
663 return None
664
665 def doc_emphasize(
666 self, s,
667 expr = re.compile('\s*\*([ \na-zA-Z0-9.:/;,\'\"\?]+)\*(?!\*|-)').search
668 ):
669
670 r=expr(s)
671 if r:
672 start, end = r.span(1)
673 return (StructuredTextEmphasis(s[start:end]), start-1, end+1)
674 else:
675 return None
676
677 def doc_inner_link(self,
678 s,
679 expr1 = re.compile("\.\.\s*").search,
680 expr2 = re.compile("\[[a-zA-Z0-9]+\]").search):
681
682 # make sure we dont grab a named link
683 if expr2(s) and expr1(s):
684 start1,end1 = expr1(s).span()
685 start2,end2 = expr2(s).span()
686 if end1 == start2:
687 # uh-oh, looks like a named link
688 return None
689 else:
690 # the .. is somewhere else, ignore it
691 return (StructuredTextInnerLink(s[start2+1,end2-1],start2,end2))
692 return None
693 elif expr2(s) and not expr1(s):
694 start,end = expr2(s).span()
695 return (StructuredTextInnerLink(s[start+1:end-1]),start,end)
696 return None
697
698 def doc_named_link(self,
699 s,
700 expr=re.compile("(\.\.\s)(\[[a-zA-Z0-9]+\])").search):
701
702 result = expr(s)
703 if result:
704 start,end = result.span(2)
705 a,b = result.span(1)
706 str = strip(s[a:b]) + s[start:end]
707 st,en = result.span()
708 return (StructuredTextNamedLink(str),st,en)
709 #return (StructuredTextNamedLink(s[st:en]),st,en)
710 return None
711
712 def doc_underline(self,
713 s,
714 expr=re.compile("\_([a-zA-Z0-9\s\.,\?]+)\_").search):
715
716 result = expr(s)
717 if result:
718 start,end = result.span(1)
719 st,e = result.span()
720 return (StructuredTextUnderline(s[start:end]),st,e)
721 else:
722 return None
723
724 def doc_strong(self,
725 s,
726 expr = re.compile('\s*\*\*([ \na-zA-Z0-9.:/;\-,!\?\'\"]+)\*\*').search
727 ):
728
729 r=expr(s)
730 if r:
731 start, end = r.span(1)
732 return (StructuredTextStrong(s[start:end]), start-2, end+2)
733 else:
734 return None
735
736 def doc_href(
737
738 self, s,
739 expr1 = re.compile("(\"[ a-zA-Z0-9\n\-\.\,\;\(\)\/\:\/]+\")(:)([a-zA-Z0-9\:\/\.\~\-]+)([,]*\s*)").search,
740 expr2 = re.compile('(\"[ a-zA-Z0-9\n\-\.\:\;\(\)\/]+\")([,]+\s+)([a-zA-Z0-9\@\.\,\?\!\/\:\;\-\#]+)(\s*)').search):
741
742 punctuation = re.compile("[\,\.\?\!\;]+").match
743 r=expr1(s) or expr2(s)
744
745 if r:
746 # need to grab the href part and the
747 # beginning part
748
749 start,e = r.span(1)
750 name = s[start:e]
751 name = replace(name,'"','',2)
752 #start = start + 1
753 st,end = r.span(3)
754 if punctuation(s[end-1:end]):
755 end = end -1
756 link = s[st:end]
757 #end = end - 1
758
759 # name is the href title, link is the target
760 # of the href
761 return (StructuredTextLink(name, href=link),
762 start, end)
763
764 #return (StructuredTextLink(s[start:end], href=s[start:end]),
765 # start, end)
766 else:
767 return None
768
769 def doc_sgml(self,s,expr=re.compile("\<[a-zA-Z0-9\.\=\'\"\:\/\-\#\+\s]+\>").search):
770 """
771 SGML text is ignored and outputed as-is
772 """
773 r = expr(s)
774 if r:
775 start,end = r.span()
776 text = s[start:end]
777 return (StructuredTextSGML(text),start,end)