import collectionsimport tokenize # from stdlibfrom . import grammar, tokenclass ParserGenerator(object):def __init__(self, grammar_file, token_file, stream=None, verbose=False):close_stream = Noneif stream is None:stream = open(grammar_file)close_stream = stream.closewith open(token_file) as tok_file:token_lines = tok_file.readlines()self.tokens = dict(token.generate_tokens(token_lines))self.opmap = dict(token.generate_opmap(token_lines))# Manually add <> so it does not collide with !=self.opmap['<>'] = "NOTEQUAL"self.verbose = verboseself.filename = grammar_fileself.stream = streamself.generator = tokenize.generate_tokens(stream.readline)self.gettoken() # Initialize lookaheadself.dfas, self.startsymbol = self.parse()if close_stream is not None:close_stream()self.first = {} # map from symbol name to set of tokensself.addfirstsets()def make_grammar(self):c = grammar.Grammar()names = list(self.dfas.keys())names.remove(self.startsymbol)names.insert(0, self.startsymbol)for name in names:i = 256 + len(c.symbol2number)c.symbol2number[name] = ic.number2symbol[i] = namefor name in names:self.make_label(c, name)dfa = self.dfas[name]states = []for state in dfa:arcs = []for label, next in sorted(state.arcs.items()):arcs.append((self.make_label(c, label), dfa.index(next)))if state.isfinal:arcs.append((0, dfa.index(state)))states.append(arcs)c.states.append(states)c.dfas[c.symbol2number[name]] = (states, self.make_first(c, name))c.start = c.symbol2number[self.startsymbol]if self.verbose:print("")print("Grammar summary")print("===============")print("- {n_labels} labels".format(n_labels=len(c.labels)))print("- {n_dfas} dfas".format(n_dfas=len(c.dfas)))print("- {n_tokens} tokens".format(n_tokens=len(c.tokens)))print("- {n_keywords} keywords".format(n_keywords=len(c.keywords)))print("- Start symbol: {start_symbol}".format(start_symbol=c.number2symbol[c.start]))return cdef make_first(self, c, name):rawfirst = self.first[name]first = set()for label in sorted(rawfirst):ilabel = self.make_label(c, label)##assert ilabel not in first # XXX failed on <> ... !=first.add(ilabel)return firstdef make_label(self, c, label):# XXX Maybe this should be a method on a subclass of converter?ilabel = len(c.labels)if label[0].isalpha():# Either a symbol name or a named tokenif label in c.symbol2number:# A symbol name (a non-terminal)if label in c.symbol2label:return c.symbol2label[label]else:c.labels.append((c.symbol2number[label], None))c.symbol2label[label] = ilabelreturn ilabelelse:# A named token (NAME, NUMBER, STRING)itoken = self.tokens.get(label, None)assert isinstance(itoken, int), labelassert itoken in self.tokens.values(), labelif itoken in c.tokens:return c.tokens[itoken]else:c.labels.append((itoken, None))c.tokens[itoken] = ilabelreturn ilabelelse:# Either a keyword or an operatorassert label[0] in ('"', "'"), labelvalue = eval(label)if value[0].isalpha():# A keywordif value in c.keywords:return c.keywords[value]else:c.labels.append((self.tokens["NAME"], value))c.keywords[value] = ilabelreturn ilabelelse:# An operator (any non-numeric token)tok_name = self.opmap[value] # Fails if unknown tokenitoken = self.tokens[tok_name]if itoken in c.tokens:return c.tokens[itoken]else:c.labels.append((itoken, None))c.tokens[itoken] = ilabelreturn ilabeldef addfirstsets(self):names = list(self.dfas.keys())for name in names:if name not in self.first:self.calcfirst(name)if self.verbose:print("First set for {dfa_name}".format(dfa_name=name))for item in self.first[name]:print(" - {terminal}".format(terminal=item))def calcfirst(self, name):dfa = self.dfas[name]self.first[name] = None # dummy to detect left recursionstate = dfa[0]totalset = set()overlapcheck = {}for label, next in state.arcs.items():if label in self.dfas:if label in self.first:fset = self.first[label]if fset is None:raise ValueError("recursion for rule %r" % name)else:self.calcfirst(label)fset = self.first[label]totalset.update(fset)overlapcheck[label] = fsetelse:totalset.add(label)overlapcheck[label] = {label}inverse = {}for label, itsfirst in overlapcheck.items():for symbol in itsfirst:if symbol in inverse:raise ValueError("rule %s is ambiguous; %s is in the"" first sets of %s as well as %s" %(name, symbol, label, inverse[symbol]))inverse[symbol] = labelself.first[name] = totalsetdef parse(self):dfas = collections.OrderedDict()startsymbol = None# MSTART: (NEWLINE | RULE)* ENDMARKERwhile self.type != tokenize.ENDMARKER:while self.type == tokenize.NEWLINE:self.gettoken()# RULE: NAME ':' RHS NEWLINEname = self.expect(tokenize.NAME)if self.verbose:print("Processing rule {dfa_name}".format(dfa_name=name))self.expect(tokenize.OP, ":")a, z = self.parse_rhs()self.expect(tokenize.NEWLINE)if self.verbose:self.dump_nfa(name, a, z)dfa = self.make_dfa(a, z)if self.verbose:self.dump_dfa(name, dfa)self.simplify_dfa(dfa)dfas[name] = dfaif startsymbol is None:startsymbol = namereturn dfas, startsymboldef make_dfa(self, start, finish):# To turn an NFA into a DFA, we define the states of the DFA# to correspond to *sets* of states of the NFA. Then do some# state reduction. Let's represent sets as dicts with 1 for# values.assert isinstance(start, NFAState)assert isinstance(finish, NFAState)def closure(state):base = set()addclosure(state, base)return basedef addclosure(state, base):assert isinstance(state, NFAState)if state in base:returnbase.add(state)for label, next in state.arcs:if label is None:addclosure(next, base)states = [DFAState(closure(start), finish)]for state in states: # NB states grows while we're iteratingarcs = {}for nfastate in state.nfaset:for label, next in nfastate.arcs:if label is not None:addclosure(next, arcs.setdefault(label, set()))for label, nfaset in sorted(arcs.items()):for st in states:if st.nfaset == nfaset:breakelse:st = DFAState(nfaset, finish)states.append(st)state.addarc(st, label)return states # List of DFAState instances; first one is startdef dump_nfa(self, name, start, finish):print("Dump of NFA for", name)todo = [start]for i, state in enumerate(todo):print(" State", i, state is finish and "(final)" or "")for label, next in state.arcs:if next in todo:j = todo.index(next)else:j = len(todo)todo.append(next)if label is None:print(" -> %d" % j)else:print(" %s -> %d" % (label, j))def dump_dfa(self, name, dfa):print("Dump of DFA for", name)for i, state in enumerate(dfa):print(" State", i, state.isfinal and "(final)" or "")for label, next in sorted(state.arcs.items()):print(" %s -> %d" % (label, dfa.index(next)))def simplify_dfa(self, dfa):# This is not theoretically optimal, but works well enough.# Algorithm: repeatedly look for two states that have the same# set of arcs (same labels pointing to the same nodes) and# unify them, until things stop changing.# dfa is a list of DFAState instanceschanges = Truewhile changes:changes = Falsefor i, state_i in enumerate(dfa):for j in range(i+1, len(dfa)):state_j = dfa[j]if state_i == state_j:#print " unify", i, jdel dfa[j]for state in dfa:state.unifystate(state_j, state_i)changes = Truebreakdef parse_rhs(self):# RHS: ALT ('|' ALT)*a, z = self.parse_alt()if self.value != "|":return a, zelse:aa = NFAState()zz = NFAState()aa.addarc(a)z.addarc(zz)while self.value == "|":self.gettoken()a, z = self.parse_alt()aa.addarc(a)z.addarc(zz)return aa, zzdef parse_alt(self):# ALT: ITEM+a, b = self.parse_item()while (self.value in ("(", "[") orself.type in (tokenize.NAME, tokenize.STRING)):c, d = self.parse_item()b.addarc(c)b = dreturn a, bdef parse_item(self):# ITEM: '[' RHS ']' | ATOM ['+' | '*']if self.value == "[":self.gettoken()a, z = self.parse_rhs()self.expect(tokenize.OP, "]")a.addarc(z)return a, zelse:a, z = self.parse_atom()value = self.valueif value not in ("+", "*"):return a, zself.gettoken()z.addarc(a)if value == "+":return a, zelse:return a, adef parse_atom(self):# ATOM: '(' RHS ')' | NAME | STRINGif self.value == "(":self.gettoken()a, z = self.parse_rhs()self.expect(tokenize.OP, ")")return a, zelif self.type in (tokenize.NAME, tokenize.STRING):a = NFAState()z = NFAState()a.addarc(z, self.value)self.gettoken()return a, zelse:self.raise_error("expected (...) or NAME or STRING, got %s/%s",self.type, self.value)def expect(self, type, value=None):if self.type != type or (value is not None and self.value != value):self.raise_error("expected %s/%s, got %s/%s",type, value, self.type, self.value)value = self.valueself.gettoken()return valuedef gettoken(self):tup = next(self.generator)while tup[0] in (tokenize.COMMENT, tokenize.NL):tup = next(self.generator)self.type, self.value, self.begin, self.end, self.line = tup# print(getattr(tokenize, 'tok_name')[self.type], repr(self.value))def raise_error(self, msg, *args):if args:try:msg = msg % argsexcept Exception:msg = " ".join([msg] + list(map(str, args)))raise SyntaxError(msg, (self.filename, self.end[0],self.end[1], self.line))class NFAState(object):def __init__(self):self.arcs = [] # list of (label, NFAState) pairsdef addarc(self, next, label=None):assert label is None or isinstance(label, str)assert isinstance(next, NFAState)self.arcs.append((label, next))class DFAState(object):def __init__(self, nfaset, final):assert isinstance(nfaset, set)assert isinstance(next(iter(nfaset)), NFAState)assert isinstance(final, NFAState)self.nfaset = nfasetself.isfinal = final in nfasetself.arcs = {} # map from label to DFAStatedef addarc(self, next, label):assert isinstance(label, str)assert label not in self.arcsassert isinstance(next, DFAState)self.arcs[label] = nextdef unifystate(self, old, new):for label, next in self.arcs.items():if next is old:self.arcs[label] = newdef __eq__(self, other):# Equality test -- ignore the nfaset instance variableassert isinstance(other, DFAState)if self.isfinal != other.isfinal:return False# Can't just return self.arcs == other.arcs, because that# would invoke this method recursively, with cycles...if len(self.arcs) != len(other.arcs):return Falsefor label, next in self.arcs.items():if next is not other.arcs.get(label):return Falsereturn True__hash__ = None # For Py3 compatibility.
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。