PageRenderTime 140ms CodeModel.GetById 31ms app.highlight 85ms RepoModel.GetById 15ms app.codeStats 0ms

/pypy/rlib/parsing/makepackrat.py

https://bitbucket.org/dac_io/pypy
Python | 748 lines | 738 code | 10 blank | 0 comment | 7 complexity | 65fa0ded501b901708468e1990fd5f2e MD5 | raw file
  1from __future__ import with_statement
  2import py
  3import sys
  4from pypy.rlib.parsing.tree import Nonterminal, Symbol, RPythonVisitor
  5from pypy.rlib.parsing.codebuilder import Codebuilder
  6from pypy.rlib.objectmodel import we_are_translated
  7
  8class BacktrackException(Exception):
  9    def __init__(self, error=None):
 10        self.error = error
 11        if not we_are_translated():
 12            Exception.__init__(self, error)
 13
 14
 15
 16class TreeOptimizer(RPythonVisitor):
 17    def visit_or(self, t):
 18        if len(t.children) == 1:
 19            return self.dispatch(t.children[0])
 20        return self.general_nonterminal_visit(t)
 21
 22    visit_commands = visit_or
 23
 24    def visit_negation(self, t):
 25        child = self.dispatch(t.children[0])
 26        if child.symbol == "negation":
 27            child.symbol = "lookahead"
 28            return child
 29        t.children[0] = child
 30        return t
 31
 32    def general_nonterminal_visit(self, t):
 33        for i in range(len(t.children)):
 34            t.children[i] = self.dispatch(t.children[i])
 35        return t
 36
 37    def general_visit(self, t):
 38        return t
 39
 40
 41syntax = r"""
 42NAME:
 43    `[a-zA-Z_][a-zA-Z0-9_]*`;
 44
 45SPACE:
 46    ' ';
 47
 48COMMENT:
 49    `( *#[^\n]*\n)+`;
 50
 51IGNORE:
 52    `(#[^\n]*\n)|\n|\t| `;
 53
 54newline:
 55    COMMENT
 56  | `( *\n *)*`;
 57    
 58
 59REGEX:
 60    r = `\`[^\\\`]*(\\.[^\\\`]*)*\``
 61    return {Symbol('REGEX', r, None)};
 62
 63QUOTE:
 64    r = `'[^\']*'`
 65    return {Symbol('QUOTE', r, None)};
 66
 67PYTHONCODE:
 68    r = `\{[^\n\}]*\}`
 69    return {Symbol('PYTHONCODE', r, None)};
 70
 71EOF:
 72    !__any__;
 73
 74file:
 75    IGNORE*
 76    list
 77    [EOF];
 78
 79list:
 80    content = production+
 81    return {Nonterminal('list', content)};
 82
 83production:
 84    name = NAME
 85    SPACE*
 86    args = productionargs
 87    ':'
 88    IGNORE*
 89    what = or_
 90    IGNORE*
 91    ';'
 92    IGNORE*
 93    return {Nonterminal('production', [name, args, what])};
 94
 95productionargs:
 96    '('
 97    IGNORE*
 98    args = (
 99        NAME
100        [
101            IGNORE*
102            ','
103            IGNORE*
104        ]
105    )*
106    arg = NAME
107    IGNORE*
108    ')'
109    IGNORE*
110    return {Nonterminal('productionargs', args + [arg])}
111  | return {Nonterminal('productionargs', [])};
112        
113    
114or_:
115    l = (commands ['|' IGNORE*])+
116    last = commands
117    return {Nonterminal('or', l + [last])}
118  | commands;
119
120commands:
121    cmd = command
122    newline
123    cmds = (command [newline])+
124    return {Nonterminal('commands', [cmd] + cmds)}
125  | command;
126
127command:
128    simplecommand;
129
130simplecommand:
131    return_
132  | if_
133  | named_command
134  | repetition
135  | choose
136  | negation;
137
138return_:
139    'return'
140    SPACE*
141    code = PYTHONCODE
142    IGNORE*
143    return {Nonterminal('return', [code])};
144
145if_:
146    'do'
147    newline
148    cmd = command
149    SPACE*
150    'if'
151    SPACE*
152    condition = PYTHONCODE
153    IGNORE*
154    return {Nonterminal('if', [cmd, condition])}
155  | 'if'
156    SPACE*
157    condition = PYTHONCODE
158    IGNORE*
159    return {Nonterminal('if', [condition])};
160
161choose:
162    'choose'
163    SPACE*
164    name = NAME
165    SPACE*
166    'in'
167    SPACE*
168    expr = PYTHONCODE
169    IGNORE*
170    cmds = commands
171    return {Nonterminal('choose', [name, expr, cmds])};
172
173commandchain:
174    result = simplecommand+
175    return {Nonterminal('commands', result)};
176
177named_command:
178    name = NAME
179    SPACE*
180    '='
181    SPACE*
182    cmd = command
183    return {Nonterminal('named_command', [name, cmd])};
184
185repetition:
186    what = enclosed
187    SPACE* '?' IGNORE*
188    return {Nonterminal('maybe', [what])}
189  | what = enclosed
190    SPACE*
191    repetition = ('*' | '+')
192    IGNORE*
193    return {Nonterminal('repetition', [repetition, what])};
194
195negation:
196    '!'
197    SPACE*
198    what = negation
199    IGNORE*
200    return {Nonterminal('negation', [what])}
201  | enclosed;
202
203enclosed:
204    '<'
205    IGNORE*
206    what = primary
207    IGNORE*
208    '>'
209    IGNORE*
210    return {Nonterminal('exclusive', [what])}
211  | '['
212    IGNORE*
213    what = or_
214    IGNORE*
215    ']'
216    IGNORE*
217    return {Nonterminal('ignore', [what])}
218  | ['(' IGNORE*] or_ [')' IGNORE*]
219  |  primary;
220
221primary:
222    call | REGEX [IGNORE*] | QUOTE [IGNORE*];
223
224call:
225    x = NAME 
226    args = arguments
227    IGNORE*
228    return {Nonterminal("call", [x, args])};
229
230arguments:
231    '('
232    IGNORE*
233    args = (
234        PYTHONCODE
235        [IGNORE* ',' IGNORE*]
236    )*
237    last = PYTHONCODE
238    ')'
239    IGNORE*
240    return {Nonterminal("args", args + [last])}
241  | return {Nonterminal("args", [])};
242"""
243
244class ErrorInformation(object):
245    def __init__(self, pos, expected=None):
246        if expected is None:
247            expected = []
248        self.expected = expected
249        self.pos = pos
250
251    def __str__(self):
252        return "ErrorInformation(%s, %s)" % (self.pos, self.expected)
253
254    def get_line_column(self, source):
255        pos = self.pos
256        assert pos >= 0
257        uptoerror = source[:pos]
258        lineno = uptoerror.count("\n")
259        columnno = pos - uptoerror.rfind("\n")
260        return lineno, columnno
261
262    def nice_error_message(self, filename='<filename>', source=""):
263        if source:
264            lineno, columnno = self.get_line_column(source)
265            result = ["  File %s, line %s" % (filename, lineno + 1)]
266            result.append(source.split("\n")[lineno])
267            result.append(" " * columnno + "^")
268        else:
269            result.append("<couldn't get source>")
270        if self.expected:
271            failure_reasons = self.expected
272            if len(failure_reasons) > 1:
273                all_but_one = failure_reasons[:-1]
274                last = failure_reasons[-1]
275                expected = "%s or '%s'" % (
276                    ", ".join(["'%s'" % e for e in all_but_one]), last)
277            else:
278                expected = failure_reasons[0]
279            result.append("ParseError: expected %s" % (expected, ))
280        else:
281            result.append("ParseError")
282        return "\n".join(result)
283
284class Status(object):
285    # status codes:
286    NORMAL = 0
287    ERROR = 1
288    INPROGRESS = 2
289    LEFTRECURSION = 3
290    SOMESOLUTIONS = 4
291    
292    _annspecialcase_ = 'specialize:ctr_location' # polymorphic
293    def __repr__(self):
294        return "Status(%s, %s, %s, %s)" % (self.pos, self.result, self.error,
295                                           self.status)
296
297    def __init__(self):
298        self.pos = 0
299        self.error = None
300        self.status = self.INPROGRESS
301        self.result = None
302
303class ParserBuilder(RPythonVisitor, Codebuilder):
304    def __init__(self):
305        Codebuilder.__init__(self)
306        self.initcode = []
307        self.names = {}
308        self.matchers = {}
309
310    def make_parser(self):
311        m = {'Status': Status,
312             'Nonterminal': Nonterminal,
313             'Symbol': Symbol,}
314        exec py.code.Source(self.get_code()).compile() in m
315        return m['Parser']
316
317    def memoize_header(self, name, args):
318        dictname = "_dict_%s" % (name, )
319        self.emit_initcode("self.%s = {}" % (dictname, ))
320        if args:
321            self.emit("_key = (self._pos, %s)" % (", ".join(args)))
322        else:
323            self.emit("_key = self._pos")
324        self.emit("_status = self.%s.get(_key, None)" % (dictname, ))
325        with self.block("if _status is None:"):
326            self.emit("_status = self.%s[_key] = Status()" % (
327                dictname, ))
328        with self.block("else:"):
329            self.emit("_statusstatus = _status.status")
330            with self.block("if _statusstatus == _status.NORMAL:"):
331                self.emit("self._pos = _status.pos")
332                self.emit("return _status")
333            with self.block("elif _statusstatus == _status.ERROR:"):
334                self.emit("raise BacktrackException(_status.error)")
335            if self.have_call:
336                with self.block(
337                    "elif (_statusstatus == _status.INPROGRESS or\n"
338                    "      _statusstatus == _status.LEFTRECURSION):"):
339                    self.emit("_status.status = _status.LEFTRECURSION")
340                    with self.block("if _status.result is not None:"):
341                        self.emit("self._pos = _status.pos")
342                        self.emit("return _status")
343                    with self.block("else:"):
344                        self.emit("raise BacktrackException(None)")
345                with self.block(
346                    "elif _statusstatus == _status.SOMESOLUTIONS:"):
347                    self.emit("_status.status = _status.INPROGRESS")
348        self.emit("_startingpos = self._pos")
349        self.start_block("try:")
350        self.emit("_result = None")
351        self.emit("_error = None")
352
353    def memoize_footer(self, name, args):
354        dictname = "_dict_%s" % (name, )
355        if self.have_call:
356            with self.block(
357                "if _status.status == _status.LEFTRECURSION:"):
358                with self.block("if _status.result is not None:"):
359                    with self.block("if _status.pos >= self._pos:"):
360                        self.emit("_status.status = _status.NORMAL")
361                        self.emit("self._pos = _status.pos")
362                        self.emit("return _status")
363                self.emit("_status.pos = self._pos")
364                self.emit("_status.status = _status.SOMESOLUTIONS")
365                self.emit("_status.result = %s" % (self.resultname, ))
366                self.emit("_status.error = _error")
367                self.emit("self._pos = _startingpos")
368                self.emit("return self._%s(%s)" % (name, ', '.join(args)))
369        else:
370            self.emit("assert _status.status != _status.LEFTRECURSION")
371        self.emit("_status.status = _status.NORMAL")
372        self.emit("_status.pos = self._pos")
373        self.emit("_status.result = %s" % (self.resultname, ))
374        self.emit("_status.error = _error")
375        self.emit("return _status")
376        self.end_block("try")
377        with self.block("except BacktrackException, _exc:"):
378            self.emit("_status.pos = -1")
379            self.emit("_status.result = None")
380            self.combine_error('_exc.error')
381            self.emit("_status.error = _error")
382            self.emit("_status.status = _status.ERROR")
383            self.emit("raise BacktrackException(_error)")
384
385    def choice_point(self, name=None):
386        var = "_choice%s" % (self.namecount, )
387        self.namecount += 1
388        self.emit("%s = self._pos" % (var, ))
389        return var
390
391    def revert(self, var):
392        self.emit("self._pos = %s" % (var, ))
393
394    def visit_list(self, t):
395        self.start_block("class Parser(object):")
396        for elt in t.children:
397            self.dispatch(elt)
398        with self.block("def __init__(self, inputstream):"):
399            for line in self.initcode:
400                self.emit(line)
401            self.emit("self._pos = 0")
402            self.emit("self._inputstream = inputstream")
403        if self.matchers:
404            self.emit_regex_code()
405        self.end_block("class")
406
407    def emit_regex_code(self):
408        for regex, matcher in self.matchers.iteritems():
409            with  self.block(
410                    "def _regex%s(self):" % (abs(hash(regex)), )):
411                c = self.choice_point()
412                self.emit("_runner = self._Runner(self._inputstream, self._pos)")
413                self.emit("_i = _runner.recognize_%s(self._pos)" % (
414                    abs(hash(regex)), ))
415                self.start_block("if _runner.last_matched_state == -1:")
416                self.revert(c)
417                self.emit("raise BacktrackException")
418                self.end_block("if")
419                self.emit("_upto = _runner.last_matched_index + 1")
420                self.emit("_pos = self._pos")
421                self.emit("assert _pos >= 0")
422                self.emit("assert _upto >= 0")
423                self.emit("_result = self._inputstream[_pos: _upto]")
424                self.emit("self._pos = _upto")
425                self.emit("return _result")
426
427        with self.block("class _Runner(object):"):
428            with self.block("def __init__(self, text, pos):"):
429                self.emit("self.text = text")
430                self.emit("self.pos = pos")
431                self.emit("self.last_matched_state = -1")
432                self.emit("self.last_matched_index = -1")
433                self.emit("self.state = -1")
434            for regex, matcher in self.matchers.iteritems():
435                matcher = str(matcher).replace(
436                    "def recognize(runner, i)",
437                    "def recognize_%s(runner, i)" % (abs(hash(regex)), ))
438                self.emit(str(matcher))
439
440    def visit_production(self, t):
441        name = t.children[0]
442        if name in self.names:
443            raise Exception("name %s appears twice" % (name, ))
444        self.names[name] = True
445        otherargs = t.children[1].children
446        argswithself = ", ".join(["self"] + otherargs)
447        argswithoutself = ", ".join(otherargs)
448        with self.block("def %s(%s):" % (name, argswithself)):
449            self.emit("return self._%s(%s).result" % (name, argswithoutself))
450        self.start_block("def _%s(%s):" % (name, argswithself, ))
451        self.namecount = 0
452        self.resultname = "_result"
453        self.have_call = False
454        self.created_error = False
455        allother = self.store_code_away()
456        self.dispatch(t.children[-1])
457        subsequent = self.restore_code(allother)
458        self.memoize_header(name, otherargs)
459        self.add_code(subsequent)
460        self.memoize_footer(name, otherargs)
461        self.end_block("def")
462
463    def visit_or(self, t, first=False):
464        possibilities = t.children
465        if len(possibilities) > 1:
466            self.start_block("while 1:")
467        for i, p in enumerate(possibilities):
468            c = self.choice_point()
469            with self.block("try:"):
470                self.dispatch(p)
471                self.emit("break")
472            with self.block("except BacktrackException, _exc:"):
473                self.combine_error('_exc.error')
474                self.revert(c)
475                if i == len(possibilities) - 1:
476                    self.emit("raise BacktrackException(_error)")
477        self.dispatch(possibilities[-1])
478        if len(possibilities) > 1:
479            self.emit("break")
480            self.end_block("while")
481
482    def visit_commands(self, t):
483        for elt in t.children:
484            self.dispatch(elt)
485
486    def visit_maybe(self, t):
487        c = self.choice_point()
488        with self.block("try:"):
489            self.dispatch(t.children[0])
490        with self.block("except BacktrackException:"):
491            self.revert(c)
492
493    def visit_repetition(self, t):
494        name = "_all%s" % (self.namecount, )
495        self.namecount += 1
496        self.emit("%s = []" % (name, ))
497        if t.children[0] == '+':
498            self.dispatch(t.children[1])
499            self.emit("%s.append(_result)"  % (name, ))
500        with self.block("while 1:"):
501            c = self.choice_point()
502            with self.block("try:"):
503                self.dispatch(t.children[1])
504                self.emit("%s.append(_result)" % (name, ))
505            with self.block("except BacktrackException, _exc:"):
506                self.combine_error('_exc.error')
507                self.revert(c)
508                self.emit("break")
509        self.emit("_result = %s" % (name, ))
510
511    def visit_exclusive(self, t):
512        self.resultname = "_enclosed"
513        self.dispatch(t.children[0])
514        self.emit("_enclosed = _result")
515
516    def visit_ignore(self, t):
517        resultname = "_before_discard%i" % (self.namecount, )
518        self.namecount += 1
519        self.emit("%s = _result" % (resultname, ))
520        self.dispatch(t.children[0])
521        self.emit("_result = %s" % (resultname, ))
522
523    def visit_negation(self, t):
524        c = self.choice_point()
525        resultname = "_stored_result%i" % (self.namecount, )
526        self.namecount += 1
527        child = t.children[0]
528        self.emit("%s = _result" % (resultname, ))
529        with self.block("try:"):
530            self.dispatch(child)
531        with self.block("except BacktrackException:"):
532            self.revert(c)
533            self.emit("_result = %s" % (resultname, ))
534        with self.block("else:"):
535            # heuristic to get nice error messages sometimes
536            if isinstance(child, Symbol) and child.symbol == "QUOTE":
537
538                error = "self._ErrorInformation(%s, ['NOT %s'])" % (
539                        c, child.additional_info[1:-1], )
540            else:
541                error = "None"
542            self.emit("raise BacktrackException(%s)" % (error, ))
543
544    def visit_lookahead(self, t):
545        resultname = "_stored_result%i" % (self.namecount, )
546        self.emit("%s = _result" % (resultname, ))
547        c = self.choice_point()
548        self.dispatch(t.children[0])
549        self.revert(c)
550        self.emit("_result = %s" % (resultname, ))
551
552    def visit_named_command(self, t):
553        name = t.children[0]
554        self.dispatch(t.children[1])
555        self.emit("%s = _result" % (name, ))
556
557    def visit_return(self, t):
558        self.emit("_result = (%s)" % (t.children[0].additional_info[1:-1], ))
559
560    def visit_if(self, t):
561        if len(t.children) == 2:
562            self.dispatch(t.children[0])
563        with self.block("if not (%s):" % (
564            t.children[-1].additional_info[1:-1], )):
565            self.emit("raise BacktrackException(")
566            self.emit("    self._ErrorInformation(")
567            self.emit("         _startingpos, ['condition not met']))")
568
569    def visit_choose(self, t):
570        with self.block("for %s in (%s):" % (
571            t.children[0], t.children[1].additional_info[1:-1], )):
572            with self.block("try:"):
573                self.dispatch(t.children[2])
574                self.emit("break")
575            with self.block("except BacktrackException, _exc:"):
576                self.combine_error('_exc.error')
577        with self.block("else:"):
578            self.emit("raise BacktrackException(_error)")
579
580    def visit_call(self, t):
581        self.have_call = True
582        args = ", ".join(['(%s)' % (arg.additional_info[1:-1], )
583                              for arg in t.children[1].children])
584        if t.children[0].startswith("_"):
585            callname = t.children[0]
586            self.emit("_result = self.%s(%s)" % (callname, args))
587        else:
588            callname = "_" + t.children[0]
589            self.emit("_call_status = self.%s(%s)" % (callname, args))
590            self.emit("_result = _call_status.result")
591            self.combine_error('_call_status.error')
592
593    def visit_REGEX(self, t):
594        r = t.additional_info[1:-1].replace('\\`', '`')
595        matcher = self.get_regex(r)
596        self.emit("_result = self._regex%s()" % (abs(hash(r)), ))
597        
598    def visit_QUOTE(self, t):
599        self.emit("_result = self.__chars__(%r)" % (
600                    str(t.additional_info[1:-1]), ))
601
602    def get_regex(self, r):
603        from pypy.rlib.parsing.regexparse import parse_regex
604        if r in self.matchers:
605            return self.matchers[r]
606        regex = parse_regex(r)
607        if regex is None:
608            raise ValueError(
609                "%s is not a valid regular expression" % regextext)
610        automaton = regex.make_automaton().make_deterministic()
611        automaton.optimize()
612        matcher = automaton.make_lexing_code()
613        self.matchers[r] = py.code.Source(matcher)
614        return matcher
615
616    def combine_error(self, newerror):
617        if self.created_error:
618            self.emit(
619                "_error = self._combine_errors(_error, %s)" % (newerror, ))
620        else:
621            self.emit("_error = %s" % (newerror, ))
622            self.created_error = True
623
624class MetaPackratParser(type):
625    def __new__(cls, name_, bases, dct):
626        if '__doc__' not in dct or dct['__doc__'] is None:
627            return type.__new__(cls, name_, bases, dct)
628        from pypackrat import PyPackratSyntaxParser
629        import sys, new, inspect
630        frame = sys._getframe(1)
631        source = dct['__doc__']
632        p = PyPackratSyntaxParser(source)
633        try:
634            t = p.file()
635        except BacktrackException, exc:
636            print exc.error.nice_error_message("<docstring>", source)
637            lineno, _ = exc.error.get_line_column(source)
638            errorline = source.split("\n")[lineno]
639            try:
640                code = frame.f_code
641                source = inspect.getsource(code)
642                lineno_in_orig = source.split("\n").index(errorline)
643                if lineno_in_orig >= 0:
644                    print "probable error position:"
645                    print "file:", code.co_filename
646                    print "line:", lineno_in_orig + code.co_firstlineno + 1
647            except (IOError, ValueError):
648                pass
649            raise exc
650        t = t.visit(TreeOptimizer())
651        visitor = ParserBuilder()
652        t.visit(visitor)
653        pcls = visitor.make_parser()
654        forbidden = dict.fromkeys(("__weakref__ __doc__ "
655                                   "__dict__ __module__").split())
656        initthere = "__init__" in dct
657
658        #XXX XXX XXX
659        if 'BacktrackException' not in frame.f_globals:
660            raise Exception("must import BacktrackException")
661        if 'Status' not in frame.f_globals:
662            raise Exception("must import Status")
663        result = type.__new__(cls, name_, bases, dct)
664        for key, value in pcls.__dict__.iteritems():
665            if isinstance(value, type):
666                value.__module__ = result.__module__ #XXX help the annotator
667            if isinstance(value, type(lambda: None)):
668                value = new.function(value.func_code, frame.f_globals)
669            if not hasattr(result, key) and key not in forbidden:
670                setattr(result, key, value)
671        if result.__init__ is object.__init__:
672            result.__init__ = pcls.__dict__['__init__']
673        result.init_parser = pcls.__dict__['__init__']
674        result._code = visitor.get_code()
675        return result
676
677class PackratParser(object):
678    __metaclass__ = MetaPackratParser
679
680    _ErrorInformation = ErrorInformation
681    _BacktrackException = BacktrackException
682
683    def __chars__(self, chars):
684        #print '__chars__(%s)' % (chars, ), self._pos
685        try:
686            for i in range(len(chars)):
687                if self._inputstream[self._pos + i] != chars[i]:
688                    raise BacktrackException(
689                        self._ErrorInformation(self._pos, [chars]))
690            self._pos += len(chars)
691            return chars
692        except IndexError:
693            raise BacktrackException(
694                self._ErrorInformation(self._pos, [chars]))
695
696    def  __any__(self):
697        try:
698            result = self._inputstream[self._pos]
699            self._pos += 1
700            return result
701        except IndexError:
702            raise BacktrackException(
703                self._ErrorInformation(self._pos, ['anything']))
704
705    def _combine_errors(self, error1, error2):
706        if error1 is None:
707            return error2
708        if (error2 is None or error1.pos > error2.pos or
709            len(error2.expected) == 0):
710            return error1
711        elif error2.pos > error1.pos or len(error1.expected) == 0:
712            return error2
713        expected = []
714        already_there = {}
715        for ep in [error1.expected, error2.expected]:
716            for reason in ep:
717                if reason not in already_there:
718                    already_there[reason] = True
719                    expected.append(reason)
720        return ErrorInformation(error1.pos, expected)
721
722
723def test_generate():
724    f = py.path.local(__file__).dirpath().join("pypackrat.py")
725    from pypackrat import PyPackratSyntaxParser
726    p = PyPackratSyntaxParser(syntax)
727    t = p.file()
728    t = t.visit(TreeOptimizer())
729    visitor = ParserBuilder()
730    t.visit(visitor)
731    code = visitor.get_code()
732    content = """
733from pypy.rlib.parsing.tree import Nonterminal, Symbol
734from makepackrat import PackratParser, BacktrackException, Status
735%s
736class PyPackratSyntaxParser(PackratParser):
737    def __init__(self, stream):
738        self.init_parser(stream)
739forbidden = dict.fromkeys(("__weakref__ __doc__ "
740                           "__dict__ __module__").split())
741initthere = "__init__" in PyPackratSyntaxParser.__dict__
742for key, value in Parser.__dict__.iteritems():
743    if key not in PyPackratSyntaxParser.__dict__ and key not in forbidden:
744        setattr(PyPackratSyntaxParser, key, value)
745PyPackratSyntaxParser.init_parser = Parser.__init__.im_func
746""" % (code, )
747    print content
748    f.write(content)