bison_.pyx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. """
  2. Pyrex-generated portion of pybison
  3. """
  4. cdef extern from "Python.h":
  5. object PyString_FromStringAndSize(char *, int)
  6. object PyString_FromString(char *)
  7. char *PyString_AsString(object o)
  8. object PyInt_FromLong(long ival)
  9. long PyInt_AsLong(object io)
  10. object PyList_New(int len)
  11. int PyList_SetItem(object list, int index, object item)
  12. void Py_INCREF(object o)
  13. object PyObject_GetAttrString(object o, char *attr_name)
  14. object PyTuple_New(int len)
  15. int PyTuple_SetItem(object p, int pos, object o)
  16. object PyObject_Call(object callable_object, object args, object kw)
  17. object PyObject_CallObject(object callable_object, object args)
  18. int PyObject_SetAttrString(object o, char *attr_name, object v)
  19. # use libdl for now - easy and simple - maybe switch to
  20. # glib or libtool if a keen windows dev sends in a patch
  21. #cdef extern from "dlfcn.h":
  22. # void *dlopen(char *filename, int mode)
  23. # int dlclose(void *handle)
  24. # void *dlsym(void *handle, char *name)
  25. # char *dlerror()
  26. #
  27. # ctypedef enum DL_MODES:
  28. # RTLD_LAZY
  29. # RTLD_NOW
  30. # RTLD_BINDING_MASK
  31. # RTLD_NOLOAD
  32. # RTLD_GLOBAL
  33. cdef extern from "stdio.h":
  34. int printf(char *format,...)
  35. cdef extern from "string.h":
  36. void *memcpy(void *dest, void *src, long n)
  37. cdef extern from "../c/bisondynlib.h":
  38. void *bisondynlib_open(char *filename)
  39. int bisondynlib_close(void *handle)
  40. char *bisondynlib_err()
  41. object (*bisondynlib_lookup_parser(void *handle))(object, char *)
  42. char *bisondynlib_lookup_hash(void *handle)
  43. object bisondynlib_run(void *handle, object parser, void *cb, void *pyin, int debug)
  44. #int bisondynlib_build(char *libName, char *includedir)
  45. cdef extern from "stdarg.h":
  46. ctypedef struct va_list:
  47. pass
  48. ctypedef struct fake_type:
  49. pass
  50. void va_start(va_list, void* arg)
  51. void* va_arg(va_list, fake_type)
  52. void va_end(va_list)
  53. fake_type void_type "void *"
  54. fake_type str_type "char *"
  55. # Callback function which is invoked by target handlers
  56. # within the C yyparse() function.
  57. cdef public object py_callback(object parser, char *target, int option, \
  58. int nargs, ...):
  59. cdef int i
  60. cdef va_list ap
  61. va_start(ap, <void*>nargs)
  62. cdef void *objptr
  63. cdef object obj
  64. cdef object valobj
  65. cdef void *val
  66. cdef char *tokval
  67. cdef char *termname
  68. #if parser.verbose:
  69. # print 'py_callback: called with nargs=%d' % nargs
  70. try:
  71. names = PyList_New(nargs)
  72. values = PyList_New(nargs)
  73. Py_INCREF(names)
  74. Py_INCREF(values)
  75. #for i in range(nargs):
  76. # print 'i=%d' % i , <char*>va_arg(ap, str_type), \
  77. # hex(<int>va_arg(ap, str_type))
  78. for i in range(nargs):
  79. termname = <char*>va_arg(ap, str_type)
  80. Py_INCREF(termname)
  81. PyList_SetItem(names, i, termname)
  82. val = <void *>va_arg(ap, void_type)
  83. valobj = <object>val
  84. Py_INCREF(valobj)
  85. PyList_SetItem(values, i, valobj)
  86. #if parser.verbose:
  87. # print 'py_callback: calling handler:', \
  88. # (target, option, names, values)
  89. res = parser._handle(target, option, names, values)
  90. #if parser.verbose:
  91. # print 'py_callback: handler returned:', res
  92. except:
  93. traceback.print_exc()
  94. res = None
  95. va_end(ap)
  96. return res
  97. # callback routine for reading input
  98. cdef public void py_input(object parser, char *buf, int *result, int max_size):
  99. cdef char *buf1
  100. cdef int buflen
  101. if parser.verbose:
  102. print "\npy_input: want to read up to %s bytes" % max_size
  103. raw = parser.read(max_size)
  104. buflen = PyInt_AsLong(len(raw))
  105. result[0] = buflen
  106. memcpy(buf, PyString_AsString(raw), buflen)
  107. if parser.verbose:
  108. print "\npy_input: got %s bytes" % buflen
  109. import sys, os, sha, re, imp, traceback
  110. import shutil
  111. import distutils.sysconfig
  112. import distutils.ccompiler
  113. reSpaces = re.compile("\\s+")
  114. #unquoted = r"""^|[^'"]%s[^'"]?"""
  115. unquoted = "[^'\"]%s[^'\"]?"
  116. cdef class ParserEngine:
  117. """
  118. Wraps the interface to the binary bison/lex-generated parser engine dynamic
  119. library.
  120. You shouldn't need to deal with this at all.
  121. Takes care of:
  122. - building the library (if the parser rules have changed)
  123. - loading the library and extracting the parser entry point
  124. - calling the entry point
  125. - closing the library
  126. Makes direct calls to the platform-dependent routines in
  127. bisondynlib-[linux|windows].c
  128. """
  129. cdef object parser
  130. cdef object parserHash # hash of current python parser object
  131. cdef object libFilename_py
  132. cdef void *libHandle
  133. # rules hash str embedded in bison parser lib
  134. cdef char *libHash
  135. def __init__(self, parser, **kw):
  136. """
  137. Creates a ParserEngine wrapper, and builds/loads the library.
  138. Arguments:
  139. - parser - an instance of a subclass of Parser
  140. In the course of initialisation, we check the library against the
  141. parser object's rules. If the lib doesn't exist, or can't be loaded, or
  142. doesn't match, we build a new library.
  143. Either way, we end up with a binary parser engine which matches the
  144. current rules in the parser object.
  145. """
  146. self.parser = parser
  147. self.libFilename_py = parser.buildDirectory \
  148. + parser.bisonEngineLibName \
  149. + imp.get_suffixes()[0][0]
  150. self.parserHash = hashParserObject(self.parser)
  151. self.openCurrentLib()
  152. def openCurrentLib(self):
  153. """
  154. Tests if library exists and is current. If not, builds a fresh one.
  155. Opens the library and imports the parser entry point.
  156. """
  157. parser = self.parser
  158. verbose = parser.verbose
  159. if not os.path.isfile(self.libFilename_py):
  160. self.buildLib()
  161. self.openLib()
  162. # hash our parser spec, compare to hash val stored in lib
  163. libHash = PyString_FromString(self.libHash)
  164. if self.parserHash != libHash:
  165. if verbose:
  166. print "Hash discrepancy, need to rebuild bison lib"
  167. print " current parser class: %s" % self.parserHash
  168. print " bison library: %s" % libHash
  169. self.closeLib()
  170. self.buildLib()
  171. self.openLib()
  172. else:
  173. if verbose:
  174. print "Hashes match, no need to rebuild bison engine lib"
  175. def openLib(self):
  176. """
  177. Loads the parser engine's dynamic library, and extracts the following
  178. symbols:
  179. - void *do_parse() (runs parser)
  180. - char *parserHash (contains hash of python parser rules)
  181. Returns lib handle, plus pointer to do_parse() function, as long ints
  182. (which later need to be cast to pointers)
  183. Important note -this is totally linux-specific.
  184. If you want windows support, you'll have to modify these funcs to
  185. use glib instead (or create windows equivalents), in which case I'd
  186. greatly appreciate you sending me a patch.
  187. """
  188. cdef char *libFilename
  189. cdef char *err
  190. cdef void *handle
  191. # convert python filename string to c string
  192. libFilename = PyString_AsString(self.libFilename_py)
  193. parser = self.parser
  194. if parser.verbose:
  195. print "Opening library %s" % self.libFilename_py
  196. handle = bisondynlib_open(libFilename)
  197. self.libHandle = handle
  198. err = bisondynlib_err()
  199. if err:
  200. printf("ParserEngine.openLib: error '%s'\n", err)
  201. return
  202. # extract symbols
  203. self.libHash = bisondynlib_lookup_hash(handle)
  204. if parser.verbose:
  205. print "Successfully loaded library"
  206. #@-node:openLib
  207. #@+node:buildLib
  208. def buildLib(self):
  209. """
  210. Creates the parser engine lib
  211. This consists of:
  212. 1. Ripping the tokens list, precedences, start target, handler docstrings
  213. and lex script from this Parser instance's attribs and methods
  214. 2. Creating bison and lex files
  215. 3. Compiling bison/lex files to C
  216. 4. Compiling the C files, and link into a dynamic lib
  217. """
  218. cdef char *incdir
  219. # -------------------------------------------------
  220. # rip the pertinent grammar specs from parser class
  221. parser = self.parser
  222. # get target handler methods, in the order of appearance in the source
  223. # file.
  224. attribs = dir(parser)
  225. gHandlers = []
  226. for a in attribs:
  227. if a.startswith("on_"):
  228. method = getattr(parser, a)
  229. gHandlers.append(method)
  230. gHandlers.sort(cmpLines)
  231. # get start symbol, tokens, precedences, lex script
  232. gStart = parser.start
  233. gTokens = parser.tokens
  234. gPrecedences = parser.precedences
  235. gLex = parser.lexscript
  236. buildDirectory = parser.buildDirectory
  237. # ------------------------------------------------
  238. # now, can generate the grammar file
  239. if os.path.isfile(buildDirectory + parser.bisonFile):
  240. os.unlink(buildDirectory + parser.bisonFile)
  241. if parser.verbose:
  242. print 'generating bison file:', buildDirectory + parser.bisonFile
  243. f = open(buildDirectory + parser.bisonFile, "w")
  244. write = f.write
  245. writelines = f.writelines
  246. # grammar file prologue
  247. write("\n".join([
  248. "%{",
  249. '',
  250. "#include <stdio.h>",
  251. '#include "Python.h"',
  252. "extern FILE *yyin;",
  253. "extern int yylineno;"
  254. "extern char *yytext;",
  255. "#define YYSTYPE void*",
  256. #'extern void *py_callback(void *, char *, int, void*, ...);',
  257. 'void *(*py_callback)(void *, char *, int, int, ...);',
  258. 'void (*py_input)(void *, char *, int *, int);',
  259. 'void *py_parser;',
  260. 'char *rules_hash = "%s";' % self.parserHash,
  261. '',
  262. "%}",
  263. '',
  264. ]))
  265. # write out tokens and start target dec
  266. write("%%token %s\n\n" % " ".join(gTokens))
  267. write("%%start %s\n\n" % gStart)
  268. # write out precedences
  269. for p in gPrecedences:
  270. write("%%%s %s\n" % (p[0], " ".join(p[1])))
  271. write("\n\n%%\n\n")
  272. # carve up docstrings
  273. rules = []
  274. for h in gHandlers:
  275. doc = h.__doc__.strip()
  276. # added by Eugene Oden
  277. #target, options = doc.split(":")
  278. doc = re.sub(unquoted % ";", "", doc)
  279. #print "---------------------"
  280. s = re.split(unquoted % ":", doc)
  281. #print "s=%s" % s
  282. target, options = s
  283. target = target.strip()
  284. options = options.strip()
  285. tmp = []
  286. #print "options = %s" % repr(options)
  287. opts = options.split("|")
  288. #print "opts = %s" % repr(opts)
  289. r = unquoted % r"\|"
  290. #print "r = <%s>" % r
  291. opts1 = re.split(r, " " + options)
  292. #print "opts1 = %s" % repr(opts1)
  293. for o in opts1:
  294. o = o.strip()
  295. tmp.append(reSpaces.split(o))
  296. options = tmp
  297. rules.append((target, options))
  298. # and render rules to grammar file
  299. for rule in rules:
  300. try:
  301. write("%s\n : " % rule[0])
  302. options = []
  303. idx = 0
  304. for option in rule[1]:
  305. nterms = len(option)
  306. if nterms == 1 and option[0] == '':
  307. nterms = 0
  308. option = []
  309. action = '\n {\n'
  310. if 'error' in option:
  311. action = action + " yyerrok;\n"
  312. action = action + ' $$ = (*py_callback)(\n py_parser, "%s", %s, %%s' % \
  313. (rule[0], idx) # note we're deferring the substitution of 'nterms' (last arg)
  314. args = []
  315. if nterms == 0:
  316. args.append('NULL')
  317. i = -1
  318. else:
  319. for i in range(nterms):
  320. if option[i] == '%prec':
  321. i = i - 1
  322. break # hack for rules using '%prec'
  323. args.append('"%s", $%d' % (option[i], i+1))
  324. # now, we have the correct terms count
  325. action = action % (i + 1)
  326. # assemble the full rule + action, ad to list
  327. action = action + ",\n "
  328. action = action + ",\n ".join(args) + "\n );\n"
  329. if 'error' in option:
  330. action = action + " PyObject_SetAttrString(py_parser, \"lasterror\", Py_None);\n"
  331. action = action + " Py_INCREF(Py_None);\n"
  332. action = action + " yyclearin;\n"
  333. action = action + " if (PyObject_HasAttrString($$, \"_pyBisonError\"))\n"
  334. action = action + " {\n"
  335. action = action + " yyerror(PyString_AsString(PyObject_GetAttrString(py_parser, \"lasterror\")));\n"
  336. action = action + " Py_INCREF(Py_None);\n"
  337. action = action + " YYERROR;\n"
  338. action = action + " }\n"
  339. action = action + " }\n"
  340. options.append(" ".join(option) + action)
  341. idx = idx + 1
  342. write(" | ".join(options) + " ;\n\n")
  343. except:
  344. traceback.print_exc()
  345. write("\n\n%%\n\n")
  346. # now generate C code
  347. epilogue = "\n".join([
  348. 'void do_parse(void *parser1,',
  349. ' void *(*cb)(void *, char *, int, int, void *, ...),',
  350. ' void (*in)(void *, char*, int *, int),',
  351. ' int debug',
  352. ' )',
  353. '{',
  354. ' //printf("Not calling yyparse\\n");',
  355. ' //return;',
  356. ' py_callback = cb;',
  357. ' py_input = in;',
  358. " py_parser = parser1;",
  359. " yydebug = debug;",
  360. " //yyin = stdin;",
  361. ' //printf("calling yyparse(), in=0x%lx\\n", py_input);',
  362. " yyparse();",
  363. ' //printf("Back from parser\\n");',
  364. "}",
  365. "int yyerror(char *mesg)",
  366. "{",
  367. ' //printf("yytext=0x%lx\\n", yytext);',
  368. ' PyObject *args = PyTuple_New(3);',
  369. ' int ret;',
  370. '',
  371. ' PyTuple_SetItem(args, 0, PyInt_FromLong(yylineno+1));',
  372. ' PyTuple_SetItem(args, 1, PyString_FromString(mesg));',
  373. ' PyTuple_SetItem(args, 2, PyString_FromString(yytext));',
  374. '',
  375. ' ret = PyObject_SetAttrString((PyObject *)py_parser, "lasterror", args);',
  376. ' //printf("PyObject_SetAttrString: %d\\n", ret);',
  377. '',
  378. ' //printf("line %d: %s before %s\\n", yylineno+1, mesg, yytext);',
  379. " //exit(0);",
  380. "}",
  381. ]) + "\n"
  382. write(epilogue)
  383. # done with grammar file
  384. f.close()
  385. # -----------------------------------------------
  386. # now generate the lex script
  387. if os.path.isfile(buildDirectory + parser.flexFile):
  388. os.unlink(buildDirectory + parser.flexFile)
  389. lexLines = gLex.split("\n")
  390. tmp = []
  391. for line in lexLines:
  392. tmp.append(line.strip())
  393. f = open(buildDirectory + parser.flexFile, "w")
  394. f.write("\n".join(tmp) + "\n")
  395. f.close()
  396. # create and set up a compiler object
  397. ccompiler = distutils.ccompiler.new_compiler(verbose=parser.verbose)
  398. ccompiler.set_include_dirs([distutils.sysconfig.get_python_inc()])
  399. # -----------------------------------------
  400. # Now run bison on the grammar file
  401. #os.system("bison -d tmp.y")
  402. bisonCmd = parser.bisonCmd + [buildDirectory + parser.bisonFile]
  403. if parser.verbose:
  404. print 'bison cmd:', ' '.join(bisonCmd)
  405. ccompiler.spawn(bisonCmd)
  406. if parser.verbose:
  407. print "renaming bison output files"
  408. print '%s => %s%s' % (parser.bisonCFile, buildDirectory,
  409. parser.bisonCFile1)
  410. print '%s => %s%s' % (parser.bisonHFile, buildDirectory,
  411. parser.bisonHFile1)
  412. if os.path.isfile(buildDirectory + parser.bisonCFile1):
  413. os.unlink(buildDirectory + parser.bisonCFile1)
  414. shutil.copy(parser.bisonCFile, buildDirectory + parser.bisonCFile1)
  415. if os.path.isfile(buildDirectory + parser.bisonHFile1):
  416. os.unlink(buildDirectory + parser.bisonHFile1)
  417. shutil.copy(parser.bisonHFile, buildDirectory + parser.bisonHFile1)
  418. # -----------------------------------------
  419. # Now run lex on the lex file
  420. #os.system("lex tmp.l")
  421. flexCmd = parser.flexCmd + [buildDirectory + parser.flexFile]
  422. if parser.verbose:
  423. print 'flex cmd:', ' '.join(flexCmd)
  424. ccompiler.spawn(flexCmd)
  425. if os.path.isfile(buildDirectory + parser.flexCFile1):
  426. os.unlink(buildDirectory + parser.flexCFile1)
  427. if parser.verbose:
  428. print '%s => %s%s' % (parser.flexCFile, buildDirectory,
  429. parser.flexCFile1)
  430. shutil.copy(parser.flexCFile, buildDirectory + parser.flexCFile1)
  431. # -----------------------------------------
  432. # Now compile the files into a shared lib
  433. # compile bison and lex c sources
  434. #bisonObj = ccompiler.compile([parser.bisonCFile1])
  435. #lexObj = ccompiler.compile([parser.flexCFile1])
  436. #cl /DWIN32 /G4 /Gs /Oit /MT /nologo /W3 /WX bisondynlib-win32.c /Id:\python23\include
  437. #cc.compile(['bisondynlib-win32.c'],
  438. # extra_preargs=['/DWIN32', '/G4', '/Gs', '/Oit', '/MT', '/nologo', '/W3', '/WX', '/Id:\python23\include'])
  439. # link 'em into a shared lib
  440. objs = ccompiler.compile([buildDirectory + parser.bisonCFile1,
  441. buildDirectory + parser.flexCFile1],
  442. extra_preargs=parser.cflags_pre,
  443. extra_postargs=parser.cflags_post,
  444. debug=parser.debugSymbols)
  445. libFileName = buildDirectory + parser.bisonEngineLibName \
  446. + imp.get_suffixes()[0][0]
  447. if os.path.isfile(libFileName+".bak"):
  448. os.unlink(libFileName+".bak")
  449. if os.path.isfile(libFileName):
  450. os.rename(libFileName, libFileName+".bak")
  451. if parser.verbose:
  452. print 'linking: %s => %s' % (', '.join(objs), libFileName)
  453. ccompiler.link_shared_object(objs, libFileName)
  454. #incdir = PyString_AsString(get_python_inc())
  455. #bisondynlib_build(self.libFilename_py, incdir)
  456. # --------------------------------------------
  457. # clean up, if we succeeded
  458. hitlist = objs[:]
  459. hitlist.append(buildDirectory + "tmp.output")
  460. if os.path.isfile(libFileName):
  461. for name in ['bisonFile', 'bisonCFile', 'bisonHFile',
  462. 'bisonCFile1', 'bisonHFile1', 'flexFile',
  463. 'flexCFile', 'flexCFile1',
  464. ] + objs:
  465. if hasattr(parser, name):
  466. fname = buildDirectory + getattr(parser, name)
  467. else:
  468. fname = None
  469. #print "want to delete %s" % fname
  470. if fname and os.path.isfile(fname):
  471. hitlist.append(fname)
  472. if not parser.keepfiles:
  473. for f in hitlist:
  474. try:
  475. os.unlink(f)
  476. except:
  477. print "Warning: failed to delete temporary file %s" % f
  478. #@-node:buildLib
  479. #@+node:closeLib
  480. def closeLib(self):
  481. """
  482. Does the necessary cleanups and closes the parser library
  483. """
  484. bisondynlib_close(self.libHandle)
  485. #@-node:closeLib
  486. #@+node:runEngine
  487. def runEngine(self, debug=0):
  488. """
  489. Runs the binary parser engine, as loaded from the lib
  490. """
  491. cdef void *handle
  492. cdef void *cbvoid
  493. cdef void *invoid
  494. handle = self.libHandle
  495. parser = self.parser
  496. cbvoid = <void *>py_callback
  497. invoid = <void *>py_input
  498. if parser.verbose:
  499. print "runEngine: about to call, py_input=0x%lx..." % (<int>invoid)
  500. return bisondynlib_run(handle, parser, cbvoid, invoid, debug)
  501. if parser.verbose:
  502. print "runEngine: back from parser"
  503. #@-node:runEngine
  504. #@+node:__del__
  505. def __del__(self):
  506. """
  507. Clean up and bail
  508. """
  509. self.closeLib()
  510. #@-node:__del__
  511. #@-others
  512. #@-node:cdef class ParserEngine
  513. #@+node:cmpLines
  514. def cmpLines(meth1, meth2):
  515. """
  516. Used as a sort() argument for sorting parse target handler methods by
  517. the order of their declaration in their source file.
  518. """
  519. try:
  520. line1 = meth1.func_code.co_firstlineno
  521. line2 = meth2.func_code.co_firstlineno
  522. except:
  523. line1 = meth1.__init__.func_code.co_firstlineno
  524. line2 = meth2.__init__.func_code.co_firstlineno
  525. return cmp(line1, line2)
  526. #@-node:cmpLines
  527. #@+node:hashParserObject
  528. def hashParserObject(parser):
  529. """
  530. Calculates an sha1 hex 'hash' of the lex script
  531. and grammar rules in a parser class instance.
  532. This is based on the raw text of the lex script attribute,
  533. and the grammar rule docstrings within the handler methods.
  534. Used to detect if someone has changed any grammar rules or
  535. lex script, and therefore, whether a shared parser lib rebuild
  536. is required.
  537. """
  538. hasher = sha.new()
  539. # add the lex script
  540. hasher.update(parser.lexscript)
  541. # add the tokens
  542. # workaround pyrex weirdness
  543. tokens = list(parser.tokens)
  544. hasher.update(",".join(list(parser.tokens)))
  545. # add the precedences
  546. for direction, tokens in parser.precedences:
  547. hasher.update(direction + "".join(tokens))
  548. # extract the parser target handler names
  549. handlerNames = dir(parser)
  550. #handlerNames = filter(lambda m: m.startswith('on_'), dir(parser))
  551. tmp = []
  552. for name in handlerNames:
  553. if name.startswith('on_'):
  554. tmp.append(name)
  555. handlerNames = tmp
  556. handlerNames.sort()
  557. # extract method objects, filter down to callables
  558. #handlers = [getattr(parser, m) for m in handlerNames]
  559. #handlers = filter(lambda h: callable(h), handlers)
  560. tmp = []
  561. for m in handlerNames:
  562. attr = getattr(parser, m)
  563. if callable(attr):
  564. tmp.append(attr)
  565. handlers = tmp
  566. # now add in the methods' docstrings
  567. for h in handlers:
  568. docString = h.__doc__
  569. hasher.update(docString)
  570. # done
  571. return hasher.hexdigest()
  572. #@-node:hashParserObject
  573. #@-others
  574. #@-node:@file src/pyrex/bison_.pyx
  575. #@-leo