Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: a guest on May 11th, 2012  |  syntax: None  |  size: 7.12 KB  |  hits: 15  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. # encoding: utf-8
  2.  
  3. from __future__ import print_function
  4.  
  5. import string
  6. import pprint
  7.  
  8.  
  9.  
  10. class InlineToken(object):
  11.     def __repr__(self):
  12.         return "Token(%s)" % (self.tag, )
  13.    
  14.     def __init__(self, start, end=None, tag=None):
  15.         super(InlineToken, self).__init__()
  16.        
  17.         self.start = start
  18.         self.end = end if end else start
  19.         self.tag = tag
  20.    
  21.     def validate(self, stream):
  22.         # print("\n\t%r.validate() == %r" % (self, stream[0] == self.start and stream.find(self.end, 1) > 0))
  23.         return stream[0] == self.start and stream.find(self.end, 1) > 0
  24.    
  25.     def enter(self, stream):
  26.         yield stream
  27.         yield ('enter', self.tag)
  28.    
  29.     def exit(self, stream):
  30.         yield stream
  31.         yield ('exit', self.tag)
  32.  
  33.  
  34. class LongInlineToken(InlineToken):
  35.     def validate(self, stream):
  36.         # print("\n\t%r.validate() == %r" % (stream.startswith(self.start) and stream.find(self.end, len(self.start)) > 0, ))
  37.         return stream.startswith(self.start) and stream.find(self.end, len(self.start)) > 0
  38.  
  39.  
  40. class UnformattedToken(InlineToken):
  41.     """No substitutions should appear within this token."""
  42.    
  43.     def enter(self, stream):
  44.         end = 0
  45.         while True:
  46.             end = stream.index(self.end, end + 1)
  47.             if stream[end-1] != '\\':
  48.                 break
  49.            
  50.             stream = stream[:end-1] + stream[end:]
  51.             end -= 1
  52.        
  53.         yield stream[end:]
  54.         yield ('enter', self.tag)
  55.         yield ('text', stream[:end])
  56.    
  57.     def exit(self, stream):
  58.         yield stream
  59.         yield ('exit', self.tag)
  60.  
  61.  
  62. class LinkToken(InlineToken):
  63.     """No substitutions should appear within this token."""
  64.    
  65.     def enter(self, stream):
  66.         yield stream
  67.         yield ('enter', self.tag)
  68.    
  69.     def exit(self, stream):
  70.         linkbreak = string.ascii_letters + string.digits + '-_:/@#'
  71.        
  72.         for i, c in enumerate(stream):
  73.             if c not in linkbreak:
  74.                 yield stream[i:]
  75.                 yield ('attr', ('href', stream[:i]))
  76.                 break
  77.        
  78.         else:
  79.             yield stream
  80.        
  81.         yield ('exit', self.tag)
  82.  
  83.  
  84. class FootnoteToken(InlineToken):
  85.     def enter(self, stream):
  86.         end = 0
  87.         while True:
  88.             end = stream.index(self.end, end + 1)
  89.             if stream[end-1] != '\\':
  90.                 break
  91.            
  92.             stream = stream[:end-1] + stream[end:]
  93.             end -= 1
  94.        
  95.         fn = stream[:end]
  96.         if not fn.isdigit():
  97.             # TODO: Store the footnote in the current parser and get index.
  98.             fn = '0'
  99.        
  100.         yield stream[end:]
  101.         yield ('enter', 'sup')
  102.         yield ('enter', 'a')
  103.         yield ('attr', ('href', '#fn' + fn))
  104.         yield ('text', fn)
  105.    
  106.     def exit(self, stream):
  107.         yield stream
  108.         yield ('exit', 'a')
  109.         yield ('exit', 'sup')
  110.  
  111. class InlineRegistry(object):
  112.     def __init__(self):
  113.         super(InlineRegistry, self).__init__()
  114.         self.tokens = dict()
  115.    
  116.     def register(self, token, symbol=None):
  117.         if symbol:
  118.             start, end = (symbol if isinstance(symbol, tuple) == 2 else (symbol, None))
  119.             token = InlineToken(start, end, token) if len(start) == 1 else LongInlineToken(start, end, token)
  120.        
  121.         if token.start[0] not in self.tokens:
  122.             self.tokens[token.start[0]] = []
  123.        
  124.         self.tokens[token.start[0]].append(token)
  125.  
  126.  
  127. registry = InlineRegistry()
  128.  
  129. registry.register('strong', '*')
  130. registry.register('emphasis', '_')
  131. registry.register('del', '-')
  132. registry.register('ins', '+')
  133. registry.register('span', '%')
  134. registry.register('sup', '^')
  135. registry.register('sub', '~')
  136. registry.register('cite', '??')
  137. registry.register('b', '**')
  138. registry.register('i', '__')
  139. registry.register(UnformattedToken('@', tag='code'))
  140. registry.register(LinkToken('"', '":', tag='a'))
  141. registry.register(FootnoteToken('[', ']'))
  142.  
  143.  
  144. def tokenize(source):
  145.     source = source
  146.    
  147.     stack = []
  148.     tokens = registry.tokens
  149.    
  150.     while source:
  151.         # print("\nSource:", repr(source), "\n\tEncountered: ", end="")
  152.        
  153.         for i, char in enumerate(source):
  154.             # print(char, end="")
  155.            
  156.             # Handle exiting.
  157.            
  158.             token = stack[-1] if stack else None
  159.            
  160.             if token and source.find(token.end) == i:
  161.                 # print()
  162.                
  163.                 if i > 0:
  164.                     if source[i-1] == '\\':
  165.                         yield 'text', source[:i-2] + char
  166.                         source = source[:i]
  167.                         break
  168.                    
  169.                     yield 'text', source[:i]
  170.                     source = source[i:]
  171.                
  172.                 emitter = token.exit(source[len(token.end):])
  173.                 source = emitter.next()
  174.                 # # print("New Source:", source)
  175.                
  176.                 for chunk in emitter:
  177.                     if chunk[0] == 'enter':
  178.                         stack.append(token)
  179.                     if chunk[0] == 'exit':
  180.                         stack.pop()
  181.                    
  182.                     yield chunk
  183.                
  184.                 break
  185.            
  186.             # Handle entering.
  187.            
  188.             if char not in tokens:
  189.                 continue
  190.            
  191.             remainder = source if i == 0 else source[i:]
  192.            
  193.             for token in tokens[char]:
  194.                 if token.validate(remainder):
  195.                     break
  196.             else:
  197.                 continue
  198.            
  199.             if i > 0:
  200.                 if source[i-1] == '\\':
  201.                     # print("\n%r, %r, %r\n" % (source[:i-1], char, source[i:]))
  202.                     yield 'text', source[:i-1] + char
  203.                     source = source[i+1:]
  204.                     break
  205.                
  206.                 yield 'text', source[:i]
  207.                 source = remainder
  208.            
  209.             emitter = token.enter(source[len(token.start):])
  210.             source = emitter.next()
  211.             # print("New Source:", source)
  212.            
  213.             for chunk in emitter:
  214.                 if chunk[0] == 'enter':
  215.                     stack.append(token)
  216.                 elif chunk[0] == 'exit':
  217.                     stack.pop()
  218.                
  219.                 yield chunk
  220.            
  221.             break
  222.        
  223.         if source and i == len(source) - 1:
  224.             yield 'text', source
  225.             break
  226.  
  227.  
  228. def main():
  229.     # pprint.pprint(registry.tokens)
  230.    
  231.     result = ''
  232.     source = 'This @example@ is "neither":/defn/neither complete[1] \[bob!] *nor* trite, *though _simple_*.'
  233.    
  234.     print("Source:", repr(source), end="\n\n")
  235.    
  236.     level = 0
  237.     for token, value in tokenize(source):
  238.         if token == 'exit':
  239.             level -= 1
  240.             result += '</{0}>'.format(value)
  241.        
  242.         # print("{0}{1}: {2}".format("    " * level, token, repr(value)))
  243.         print("{0}{1},".format("    " * level, repr((token, value))))
  244.        
  245.         if token == 'enter':
  246.             level += 1
  247.             result += '<{0}>'.format(value)
  248.        
  249.         if token == 'text':
  250.             result += value
  251.    
  252.     print("\nResult:", repr(result))
  253.  
  254.  
  255. if __name__ == '__main__':
  256.     main()