bison_.pyx 24 KB

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