Advertisement
Tyler_Elric

grammar.py

Mar 21st, 2013
116
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.49 KB | None | 0 0
  1. def follow_chunk(c):
  2.     while c is not None:
  3.         yield c
  4.         c=c.next
  5.  
  6. def print_chunk_tree(base,tc=''):
  7.     for c in follow_chunk(base):
  8.         def display_chunk(chunk,tc):
  9.             print(tc,end='')
  10.             print(chunk)
  11.             if isinstance(chunk,GroupChunk):
  12.                 for sub_chunk in follow_chunk(chunk.sub_chunk):
  13.                     print_chunk_tree(sub_chunk,tc+'-'*2)
  14.             if chunk.alt is not None:
  15.                 print(tc,end='')
  16.                 print("Alternate:")
  17.                 print_chunk_tree(chunk.alt,tc+'^'*4)
  18.         display_chunk(c,tc)
  19.  
  20. def extract_open_close(s,o,c):
  21.     oc,cc,i,s=1,0,1,s[s.find(o):]
  22.     while oc != cc:
  23.         if s[i]=='\\':i+=1
  24.         elif s[i]==o and o!=c:oc+=1
  25.         elif s[i]==c:cc+=1
  26.         i+=1
  27.     return (s[i:] if len(s)>i else ""),s[1:i-1]
  28.  
  29. class ChunkParseError(Exception):pass
  30.  
  31. class Chunk:
  32.     prefix = "chunk"
  33.     def __init__(self,dat):
  34.         self.dat=dat.strip()
  35.         self.next=None
  36.         self.alt=None
  37.         self.name="???"
  38.     def parse(self,text):
  39.         d,s=self(text)
  40.         return self.name,d,(self.next.parse(s) if self.next else s)
  41.     def __call__(self,s):
  42.         try:
  43.             return self.apply(s)
  44.         except ChunkParseError as e:
  45.             if self.alt is not None:
  46.                 return self.alt(s)
  47.             else:
  48.                 raise e
  49.     def apply(self,text):return None,text
  50.     def __str__(self):return self.prefix+":"+self.dat
  51.  
  52. class ExternalChunk(Chunk):
  53.     external_mappings={}
  54.     prefix="identifier"
  55.     def __init__(self,grammar,v):
  56.         super().__init__(v)
  57.         self.grammar=grammar
  58.     def apply(self,s):
  59.         ct=self.grammar.rules.get(self.dat,None)
  60.         if ct is None:raise ChunkParseError()
  61.         return ct.apply(s)
  62.  
  63. class LiteralChunk(Chunk):
  64.     prefix='literal'
  65.     def apply(self,s):
  66.         s=s.strip()
  67.         if s[:len(self.dat)]==self.dat:
  68.             return s[:len(self.dat)],s[len(self.dat):]
  69.         raise ChunkParseError()
  70.  
  71. class GroupChunk(Chunk):
  72.     prefix='group'
  73.     def __init__(self,v,sub_chunk):
  74.         super().__init__(v)
  75.         self.sub_chunk = sub_chunk
  76.     def apply(self,s):
  77.         if self.sub_chunk is None:
  78.             raise ChunkParseError()
  79.         return self.sub_chunk.parse(s)
  80.  
  81. class OptionalChunk(GroupChunk):
  82.     prefix='optional'
  83.  
  84. class RepeatedChunk(OptionalChunk):
  85.     prefix='repeated'
  86.  
  87. class Grammar:
  88.     r'''
  89.         number: "\d+" | number_calculation;
  90.         @bool: "true" | "false" | bool_calculation | compare_calculation;
  91.         string: "\"([^\"\\\\]*(?:\\.[^\"\\\\]*)*)\"" | string_calculation;
  92.         number_calculation: number, ("\\+"|"\\-"|"\\/"|"\\*"), number;
  93.         bool_calculation: bool, ("\\|"|"&"), bool;
  94.         compare_calculation: number, ("([<>]\\=?)"|"([\\!\\=])="), number;
  95.     '''
  96.     default_rules = {}
  97.     def __init__(self,gramstr=None,**rules):
  98.         self.rules=dict(self.default_rules.items())
  99.         self.rules.update(rules)
  100.         if isinstance(gramstr,Chunk):self.head=gramstr
  101.         else:
  102.             rrules,self.head=self.parse_grammar(gramstr or self.__doc__)
  103.             self.rules.update(rrules)
  104.  
  105.     def parse_grammar(self,s):
  106.         def process_rule(rule):
  107.             name=rule[:rule.find(":")].strip()
  108.             rule=rule[rule.find(":")+1:].strip()
  109.             return rule,name
  110.         rules=[process_rule(r.strip()) for r in s.split(";") if len(r.strip())>0]
  111.         def extract_rhs(s):
  112.             f,l=None,None
  113.             def append_to_chain(chunk,ff,ll):
  114.                 if ff is None:return chunk,chunk
  115.                 else:
  116.                     ll.next=chunk
  117.                     return ff,chunk
  118.             while len(s)>0:
  119.                 if s[0]=='"':
  120.                     end=1
  121.                     while end<len(s) and s[end]!='"':
  122.                         if s[end]=="\\":
  123.                             s=s[:end]+s[end+1:]
  124.                         end+=1
  125.                     c,s=s[1:end],s[end+1:]
  126.                     f,l=append_to_chain(LiteralChunk(c),f,l)
  127.                 elif s[0]=='[':
  128.                     s,c = extract_open_close(s,'[',']')
  129.                     sc,a=extract_rhs(c)
  130.                     f,l=append_to_chain(OptionalChunk(c,sc),f,l)
  131.                 elif s[0]=='(':
  132.                     s,c = extract_open_close(s,'(',')')
  133.                     sc,a=extract_rhs(c)
  134.                     f,l=append_to_chain(GroupChunk(c,sc),f,l)
  135.                 elif s[0]=='{':
  136.                     s,c = extract_open_close(s,'{','}')
  137.                     sc,a=extract_rhs(c)
  138.                     f,l=append_to_chain(RepeatedChunk(c,sc),f,l)
  139.                 elif s[0]==',' and f is not None:
  140.                     c,s=extract_rhs(s[1:])
  141.                     f,l=append_to_chain(c,f,l)
  142.                 elif s[0]=='|' and f is not None:
  143.                     c,s=extract_rhs(s[1:])
  144.                     f.alt=c
  145.                 elif len(s)>len(s.strip()):
  146.                     s=s.strip()
  147.                 else:
  148.                     i=0
  149.                     while i<len(s) and (s[i].isalpha() or s[i]=='_') :i+=1
  150.                     name,s=s[:i+1],s[i+1:]
  151.                     f,l=append_to_chain(ExternalChunk(self,name),f,l)
  152.             return f,s.strip()
  153.         rules = {r[1]:extract_rhs(r[0])[0] for r in rules}
  154.         h,c=Chunk("Default Head"),0
  155.         for name, chunk in list(rules.items()):
  156.             oname=name
  157.             while name[0]=='@': name = name[1:]
  158.             chunk.name=name
  159.             if oname!=name:
  160.                 if h.next is None:h.next=chunk
  161.                 else:
  162.                     c+=1
  163.                     s,b=h,Chunk("Alternate Head "+str(c))
  164.                     b.sub=chunk
  165.                     while s.alt is not None:s=s.alt
  166.                     s.alt=b
  167.                 del rules[oname]
  168.                 rules[name]=chunk
  169.         return rules,h
  170.  
  171. g=Grammar()
  172. print_chunk_tree(g.head,' ')
  173.  
  174. d=g.head.parse("false")
  175. print(d)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement