walkthrough.html 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036
  1. <!--@+leo-ver=4-->
  2. <!--@+node:@file doc/walkthrough.html-->
  3. <!--@@language html-->
  4. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  5. <html>
  6. <head>
  7. <title>PyBison Walkthrough</title>
  8. </head>
  9. <body>
  10. <!--@ @+others-->
  11. <!--@+node:body-->
  12. <center>Back to <a href="index.html">PyBison Homepage</a></center>
  13. <h1>PyBison Walkthrough</h1>
  14. <!--@+others-->
  15. <!--@+node:intro-->
  16. <h2>0. Introduction</h2>
  17. This document aims to get you up to speed with PyBison in the fastest possible
  18. time, by walking you through the motions of using it, and supporting the explanations
  19. with an example.<br>
  20. <br>
  21. <blockquote><big>
  22. NOTE - recent versions of flex violate the ANSI standards.<br>
  23. <br>
  24. If any of the pyBison examples fail to build, remove the following line from the lex code portion of your scripts:
  25. <b><blockquote>int yylineno = 0;</blockquote></b>
  26. Also, make sure your system is capable of looking in the local directory when trying to load .so files. If you see any errors like <b>failed to load somefilename.so</b>, just add "." to <b>LD_LIBRARY_PATH</b>, or
  27. execute the command:
  28. <b><blockquote>export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.</blockquote></b>
  29. </big></blockquote>
  30. <hr>
  31. <!--@-node:intro-->
  32. <!--@+node:1. procure grammar/scanner scripts-->
  33. <h2>1. Procure Grammar and Scanner Scripts</h2>
  34. <blockquote>
  35. The best place to start in building your PyBison parser is to write a grammar (.y)
  36. script, and a scanner (.l) script.<br>
  37. <br>
  38. (Or, if possible, source these scripts from other (open source) projects).<br>
  39. <br>
  40. Once you're familiar with the layout of PyBison parser python modules, you can
  41. skip this step, and start building each new parser from scratch, or fram a template
  42. (refer to the <b>examples/template</b> directory).
  43. <!--@+others-->
  44. <!--@+node:1.1 introducing pybison-->
  45. <h3>1.1. Introducing the bison2py Utility</h3>
  46. <blockquote>
  47. bison2py munges your new (or legacy) grammar (.y) and scanner (.l) files, and
  48. generates a new Python file containing classes and unit test code for your
  49. PyBison parser.<br>
  50. <br>
  51. To see <b>bison2py</b> in action, go into the <b>examples/java</b> directory,
  52. read the README file, and generate a <b>javaparser.py</b> file from <b>javaparser.y</b>
  53. and <b>javaparser.l</b> scripts.<br>
  54. <br>
  55. Study the generated javaparser.py file - it's
  56. especially useful from a point of view of seeing what's good to put into a pybison
  57. python parser file, especially when writing your own.<br>
  58. <br>
  59. In fact, when starting a new parser project, you might like to start by writing <b>.y</b>
  60. and <b>.l</b> files yourself, and repeatedly:
  61. <ol>
  62. <li>Edit these files</li>
  63. <li>Generate a parser from them with bison2py</li>
  64. <li>Test the parser rigorously against a whole range of inputs</li>
  65. <li>Remove the grammar and scanner errors as you find them</li>
  66. <li>Repeat these steps as often as needed till you have a bug-free parser and scanner</li>
  67. </ol>
  68. We suggest you may have a far easier time if you ensure you have a bug-free parser
  69. script before even <i>beginning</i> to edit your target handler and parse node methods.<br>
  70. <br>
  71. Once you've got a stable parser, you'll have a structure to work from. You'll then be free to
  72. discard or archive your .y and .l files, and tweak the grammar and scanner
  73. by editing the target handler docstrings and scanner script attributes, respectively.
  74. </blockquote>
  75. <hr>
  76. <!--@-node:1.1 introducing pybison-->
  77. <!--@+node:1.2 prepare grammar file-->
  78. <h3>1.2. Preparing Your Grammar File</h3>
  79. <blockquote>
  80. If you're using an existing .y file (perhaps sourced from another project),
  81. you'll need to massage it a bit to get it into a state where you can process
  82. it automatically with bison2py.<br>
  83. <br>
  84. In summary, you'll need to:
  85. <ul>
  86. <li>Eliminate actions and comments from rules section</li>
  87. <li>Replace character literals in rules, with abstract tokens</li>
  88. <li>Enclose all <b>:</b>, <b>|</b> and <b>;</b> rule delimiters in whitespace.</li>
  89. </ul>
  90. <h4>1.2.1. Strip Out Comments and Actions</h4>
  91. <blockquote>
  92. With your grammar (.y) file, you'll need to strip out all action statements
  93. and comments from the rules section.<br>
  94. <br>
  95. For instance, if you're using a legacy grammar file, you'll need to convert rules like:
  96. <b>
  97. <pre>
  98. expr: expr PLUS expr
  99. { $$ = $1 + $3; } /* add the numbers now */
  100. |expr MINUS expr
  101. { $$ = $1 - $3; }
  102. ;</pre>
  103. </b>
  104. to:
  105. <b><pre>
  106. expr : expr PLUS expr
  107. | expr MINUS expr
  108. ;</pre></b>
  109. or:
  110. <b>
  111. <pre>
  112. expr
  113. : expr PLUS expr
  114. | expr MINUS expr
  115. ;</pre>
  116. </b>
  117. depending on what style meets your taste.<br>
  118. <br>
  119. The reason for this is that your pybison script will receive callbacks every time a parse
  120. target is reached, which is done by automatically appending special action code to each
  121. rule clause. If you don't remove all action statements, the conversion will fail.
  122. </blockquote>
  123. <h4>1.2.2. Replace All Character Literals in Rules</h4>
  124. <blockquote>
  125. Within the PyBison-generated parser, all targets and tokens are rendered as Python
  126. objects (for people familiar with the Python/C API, type <b>PyObject *</b>)<br>
  127. <br>
  128. Therefore, you unfortunately lose the convenience of being able to deal in C character
  129. literals in your rules.<br>
  130. <br>
  131. For instance, with a rule like:
  132. <b><pre>
  133. expr : expr '+' expr
  134. | expr '-' expr
  135. ;</pre></b>
  136. you'll have to replace the <b>'+'</b> and <b>'-'</b> char literals to abstract tokens,
  137. and ensure that your scanner script returns Python-wrapped tokens for these operators.
  138. You should end up with a rule like:
  139. <b><pre>
  140. expr : expr PLUS expr
  141. | expr MINUS expr
  142. ;</pre></b>
  143. And you'll need to ensure your scanner script does a <b>returntoken(PLUS);</b>
  144. and <b>returntoken(MINUS);</b> for <b>'+'</b> and <b>'-'</b> respectively.
  145. </blockquote>
  146. <h4>1.2.3. Enclose Rule Delimiters in Whitespace</h4>
  147. <blockquote>
  148. You need to ensure that the delimiters <b>:</b>, <b>|</b> and <b>;</b> delimiters used in
  149. your rules have at least one whitespace character on either side. Sorry about this, but
  150. this version of PyBison has some quirks in the regular expressions used for
  151. extracting/dissecting the rules, and bison2py (or the resultant parser) may fail
  152. if you don't follow this step.
  153. </blockquote>
  154. And also, you'll need to have a:
  155. <blockquote><b>
  156. %{
  157. ...
  158. %}
  159. </b></blockquote>
  160. section in the prologue (before the first <b>%%</b>).
  161. </blockquote>
  162. <hr>
  163. <!--@-node:1.2 prepare grammar file-->
  164. <!--@+node:1.3 prepare lex file-->
  165. <h3>1.3. Preparing Your Tokeniser File</h3>
  166. <blockquote>
  167. In addition to parse targets callbacks, PyBison has an input callback, so your Parser
  168. object will have control over the input that is sent to the lexer.<br>
  169. <br>
  170. You'll have to set up your tokeniser to use this callback mechanism, and also to wrap
  171. tokens as Python objects.<br>
  172. <br>
  173. To set this up, ensure the following lines are in the C declarations sections of your
  174. lex/flex script:
  175. <b><pre>
  176. %{
  177. #include &lt;stdio.h&gt;
  178. #include &lt;string.h&gt;
  179. #include "Python.h"
  180. #define YYSTYPE void *
  181. #include "tokens.h"
  182. extern void *py_parser;
  183. extern void (*py_input)(PyObject *parser, char *buf, int *result, int max_size);
  184. #define returntoken(tok) yylval = PyString_FromString(strdup(yytext)); return (tok);
  185. #define YY_INPUT(buf,result,max_size) {(*py_input)(py_parser, buf, &result, max_size);}
  186. }%</pre></b>
  187. <b>&lt;quick-diversion&gt;</b>
  188. <small><blockquote>
  189. Let's explain each of these lines now:
  190. <b><pre>
  191. #include &lt;stdio.h&gt;
  192. #include &lt;string.h&gt;
  193. #include "Python.h"</pre></b>
  194. Include the standard <b>stdio.h</b> and <b>string.h</b> headers, as well as the
  195. Python-C API file <b>Python.h</b>.
  196. <b><pre>
  197. #define YYSTYPE void *</pre></b>
  198. All parse targets and tokens are actually of type <b>PyObject *</b>, or 'pointer to
  199. Python object', but neither bison nor flex-generated code need to know this. We'll
  200. just give them opaque pointers, and <b>void *</b> will suffice just fine.
  201. <b><pre>
  202. #include "tokens.h"</pre></b>
  203. When PyBison first instantiates any given parser class (and auto-generates, processes,
  204. compiles, links the grammar/scanner files into a dynamic lib), the bison program generates
  205. a header file of token definitions, which gets renamed to <b>tokens.h</b>. Your scanner
  206. script will need this file, so the token macros will be defined and resolved to the correct
  207. token numbers.
  208. <b><pre>
  209. extern void (*py_input)(PyObject *parser, char *buf, int *result, int max_size);
  210. extern void *py_parser;
  211. #define YY_INPUT(buf,result,max_size) {(*py_input)(py_parser, buf, &result, max_size);}</pre></b>
  212. These lines activate the input callback mechanism. Whenever the scanner needs more input,
  213. it will call a global function called <b>py_input()</b>, which forwards the callback to
  214. your Python Parser's <b>.read(nbytes)</b> method.
  215. <blockquote>
  216. Note that if you want your scanner to use a different source of input (eg, a live TCP socket
  217. connection), you can override this method in your parser class, or pass a <b>read=myreadfunction</b>
  218. keyword argument when instantiating your parser (<b>myreadfunction</b> should be a callable
  219. accepting a single argument <b>nbytes</b>, being the maximum number of bytes to retrieve,
  220. and returning a string).
  221. </blockquote>
  222. <b><pre>
  223. #define returntoken(tok) yylval = PyString_FromString(strdup(yytext)); return (tok);</pre></b>
  224. A macro which wraps all tokens values as Python strings, so your parser target handlers can uplift
  225. the original input text which constitutes that token.
  226. </blockquote></small>
  227. <b>&lt;/quick-diversion&gt;</b><br><br>
  228. Defining the <b>YY_INPUT</b> C macro tells flex to invoke a callback every time it needs
  229. input, so your <b>Parser</b> class' <b>.read()</b> method will have control over what the
  230. lexer receives.<br>
  231. <br>
  232. Now, you'll need to change all the <b>return</b> statements in your token targets to use
  233. <b>returntoken()</b> instead. For example, change:
  234. <blockquote>
  235. <b>
  236. "(" { return LPAREN; }<br>
  237. </b>
  238. </blockquote>
  239. to:
  240. <blockquote>
  241. <b>
  242. "(" { returntoken(LPAREN); }<br>
  243. </b>
  244. </blockquote>
  245. Lastly, in the epilogue of your lexer file (ie, after the second '%%' line), you'll need to add a line like:
  246. <b><pre>
  247. yywrap() { return(1); }
  248. </pre></b>
  249. </blockquote>
  250. <hr>
  251. <!--@-node:1.3 prepare lex file-->
  252. <!--@+node:1.4 do the conversion-->
  253. <h3>1.4. Doing The Conversion</h3>
  254. <blockquote>
  255. When you're sure you've got your .y and .l files prepared properly, you can generate
  256. the .py file, which will contain your pyBison <b>Parser</b> class.<br>
  257. <br>
  258. To do this conversion, run the command:
  259. <blockquote>
  260. <b>bison2py mybisonfile.y myflexfile.l mypythonfile.py</b>
  261. </blockquote>
  262. where <b>mybisonfile.y</b> is your grammar file, with bison/yacc declarations,
  263. <b>myflexfile.l</b> is your tokeniser script, with flex/lex declarations, and
  264. <b>mypythonfile.py</b> is the name of the python file you want to generate.<br>
  265. <br>
  266. You should now see a file <b>mypythonfile.py</b> which contains a couple of import
  267. statements, plus a declaration of a class called <b>Parser</b>.
  268. <blockquote>
  269. If your grammar is large and complex, you should consider adding a <b>-c</b>
  270. argument to the bison2py command.<br>
  271. <br>
  272. This will cause the <b>mypythonfile.py</b> file to be generated with a bunch
  273. of parse node subclasses, one per parse target, and with each grammar target
  274. handler method instantiating its respective parse node class, rather
  275. than the default pybison.BisonNode class.<br>
  276. <br>
  277. Also, it'll generate a <b>ParseNode</b> class (derived from <b>pybison.BisonNode</b>,
  278. from which all these target-specific node classes are derived.
  279. <br>
  280. This can be extremely handy, because you can add a bunch of methods to the
  281. ParseNode class, and optionally override these in your per-target node classes. Also,
  282. override the constructor and/or the existing .dump() method in this class or
  283. the per-target classes.<br>
  284. </blockquote>
  285. </blockquote>
  286. <!--@-node:1.4 do the conversion-->
  287. <!--@-others-->
  288. </blockquote>
  289. <hr>
  290. <!--@-node:1. procure grammar/scanner scripts-->
  291. <!--@+node:2. prepare parser class-->
  292. <a name="chap2">
  293. <h2>2. Prepare Your Parser Class</h2>
  294. <blockquote>
  295. Now, we focus on creation of a working parser.<br>
  296. <br>
  297. Note here that we will be creating the parser .py file by hand from
  298. scratch - not the preferred approach, but chosen here as an alternative
  299. to deriving a parser module boilerplate as discussed in the previous chapter.<br>
  300. <br>
  301. To make this easy, we will use
  302. a simple calculator example.<br>
  303. <br>
  304. Create a new python file, perhaps <b>mycalc.py</b>, and follow these steps:<br>
  305. <hr>
  306. <!--@+others-->
  307. <!--@+node:2.1. required imports-->
  308. <h3>2.1. Required Imports</h3>
  309. <blockquote>
  310. You will need at least the following imports:
  311. <blockquote>
  312. <b>from bison import BisonParser, BisonNode</b>
  313. </blockquote>
  314. <b>BisonParser</b> is the base class from which you derive your own
  315. Parser class.<br>
  316. <br>
  317. <b>BisonNode</b> is a convenient wrapper for containing the contents
  318. of parse targets, and can assist you in building your parse tree.<br>
  319. <br>
  320. </blockquote>
  321. <hr>
  322. <!--@-node:2.1. required imports-->
  323. <!--@+node:2.2. devise your grammar-->
  324. <h3>2.2. Devise Your Grammar</h3>
  325. <blockquote>
  326. We'll base our example on the Calculator example from the standard bison/yacc manual.
  327. Note that we won't use exactly the same token names:
  328. <b>
  329. <pre>
  330. %token NUM
  331. %left '-' '+'
  332. %left '*' '/'
  333. %left NEG /* negation--unary minus */
  334. %right '^' /* exponentiation */
  335. /* Grammar follows */
  336. %%
  337. input: /* empty string */
  338. | input line
  339. ;
  340. line: '\n'
  341. | exp '\n' { printf ("\t%.10g\n", $1); }
  342. ;
  343. exp: NUM { $$ = $1; }
  344. | exp '+' exp { $$ = $1 + $3; }
  345. | exp '-' exp { $$ = $1 - $3; }
  346. | exp '*' exp { $$ = $1 * $3; }
  347. | exp '/' exp { $$ = $1 / $3; }
  348. | '-' exp %prec NEG { $$ = -$2; }
  349. | exp '^' exp { $$ = pow ($1, $3); }
  350. | '(' exp ')' { $$ = $2; }
  351. ;
  352. </pre>
  353. </b>
  354. However, in PyBison, you don't dump all this into a script - you declare the
  355. grammar items one by one in methods of your class.
  356. </blockquote>
  357. <hr>
  358. <!--@-node:2.2. devise your grammar-->
  359. <!--@+node:2.3. skeleton class-->
  360. <h3>2.3. Create Skeleton Parser Class</h3>
  361. <blockquote>
  362. In your calc.py file, you've already done the required imports, so
  363. now you can create your skeleton class declaration:
  364. <b><blockquote><pre>
  365. class Parser(BisonParser):
  366. pass
  367. </pre></blockquote></b>
  368. </blockquote>
  369. <hr>
  370. <!--@-node:2.3. skeleton class-->
  371. <!--@+node:2.4. declare tokens-->
  372. <h3>2.4. Declare the Tokens</h3>
  373. <blockquote>
  374. Now, it's time to declare our tokens. To do this, we add to our class an attribute
  375. called <b>tokens</b> which contains a list of our tokens.<br>
  376. <br>
  377. Our class now looks like this:
  378. <b>
  379. <blockquote>
  380. <pre style="color: #808080">
  381. class Parser(BisonParser):</pre>
  382. <pre>
  383. tokens = ['NUMBER',
  384. 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'POW',
  385. 'LPAREN', 'RPAREN',
  386. 'NEWLINE', 'QUIT',
  387. ]
  388. </pre>
  389. </blockquote>
  390. </b>
  391. </blockquote>
  392. <hr>
  393. <!--@-node:2.4. declare tokens-->
  394. <!--@+node:2.5. declare precedences-->
  395. <h3>2.5. Declare the Precedences</h3>
  396. <blockquote>
  397. To resolve ambiguities in our grammar, we need to declare which entities
  398. have precedence, and the associativity (left/right) of these entities.<br>
  399. <br>
  400. We adapt this from the example, and add it as an attribute <b>precedences</b>.<br>
  401. <br>
  402. Our class now looks like this:
  403. <b><pre style="color: #808080">
  404. class Parser(BisonParser):
  405. tokens = ['NUMBER',
  406. 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'POW',
  407. 'LPAREN', 'RPAREN',
  408. 'NEWLINE', 'QUIT',
  409. ]</pre>
  410. <pre>
  411. precedences = (
  412. ('left', ('MINUS', 'PLUS')),
  413. ('left', ('TIMES', 'DIVIDE')),
  414. ('left', ('NEG', )),
  415. ('right', ('POW', )),
  416. )</pre></b>
  417. </blockquote>
  418. <hr>
  419. <!--@-node:2.5. declare precedences-->
  420. <!--@+node:2.6. declare the start symbol-->
  421. <h3>2.6. Declare the Start Symbol</h3>
  422. <blockquote>
  423. As you can see from studying the grammar above, the topmost
  424. entity is <b>line</b>. We need to tell PyBison to use this,
  425. by adding an attribute called <b>start</b>.
  426. <br>
  427. Our class now looks like this:
  428. <b><pre style="color: #808080">
  429. class Parser(BisonParser):
  430. tokens = ['NUMBER',
  431. 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'POW',
  432. 'LPAREN', 'RPAREN',
  433. 'NEWLINE', 'QUIT',
  434. ]
  435. precedences = (
  436. ('left', ('MINUS', 'PLUS')),
  437. ('left', ('TIMES', 'DIVIDE')),
  438. ('left', ('NEG', )),
  439. ('right', ('POW', )),
  440. )</pre>
  441. <pre>
  442. start = 'input'</pre></b>
  443. </blockquote>
  444. <hr>
  445. <!--@-node:2.6. declare the start symbol-->
  446. <!--@+node:2.7. add rules callbacks-->
  447. <h3>2.7. Add Rules Callbacks</h3>
  448. <blockquote>
  449. This is the fun part. We add a method to our class for each of
  450. the parse targets.<br>
  451. <br>
  452. For each parse target <b>sometarget</b>, we need to provide a method
  453. called:
  454. <b><blockquote><pre>on_sometarget(self, target, option, names, items)</pre></blockquote></b>
  455. Each such callback method accepts the arguments:
  456. <ul>
  457. <li><b>target</b> - string - the name of the target - passed in mainly as
  458. a convenience for when you're debugging your grammar.
  459. </li>
  460. <li><b>option</b> - int - a numerical index indicating which 'clause' matched
  461. the target. For example, given a rule:
  462. <b><pre>
  463. exp : NUMBER
  464. | exp PLUS exp
  465. | exp MINUS exp</pre></b>
  466. If we have matched the expression <b>3 + 6</b>, the <b>option</b> argument
  467. will be 1, because the clause <b>exp PLUS exp</b> occurs at position 1
  468. in the list of rule clauses.</li>
  469. <li><b>names</b> - list of strings, being names of the terms in the matching clause.
  470. For example, with the above rule, the expression <b>3 + 6</b> would produce a
  471. names list <b>['exp', 'PLUS', 'exp']</b></li>
  472. <li><b>items</b> - list - a list of objects, being the values of the items in the
  473. matching clause. Each item of this list will (in the case of token
  474. matches), be a literal string of the token, or (in the case of previously
  475. handled parse targets), whatever your parse target handler happened to
  476. return previously. For instance, in the <b>3 + 6</b> example, assuming your <b>on_exp()</b>
  477. handler returns a float value, this list would be <b>[3.0, '+', 6.0]</b></li>
  478. </ul>
  479. We must now note a major difference from traditional yacc/bison. In yacc/bison, we
  480. provide <b>{ action-stmts;... }</b> action blocks after each rule clause. But with
  481. pyBison, the one parse target callback handles all possible clauses for that target.
  482. The <b>option</b> argument indicates which clause actually matched.<br>
  483. <br>
  484. Now, with this explanation out of the way, we can get down to the business of actually
  485. writing our callbacks.<br>
  486. <br>
  487. Our class now looks like:
  488. <b><pre style="color: #808080">
  489. class Parser(BisonParser):
  490. tokens = ['NUMBER',
  491. 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'POW',
  492. 'LPAREN', 'RPAREN',
  493. 'NEWLINE', 'QUIT',
  494. ]
  495. precedences = (
  496. ('left', ('MINUS', 'PLUS')),
  497. ('left', ('TIMES', 'DIVIDE')),
  498. ('left', ('NEG', )),
  499. ('right', ('POW', )),
  500. )
  501. start = 'input'</pre>
  502. <pre>
  503. def on_input(self, target, option, names, items):
  504. """
  505. input :
  506. | input line
  507. """
  508. return
  509. def on_line(self, target, option, names, items):
  510. """
  511. line : NEWLINE
  512. | exp NEWLINE
  513. """
  514. if option == 1:
  515. print "on_line: got exp %s" % items[0]
  516. def on_exp(self, target, option, names, items):
  517. """
  518. exp : NUMBER
  519. | exp PLUS exp
  520. | exp MINUS exp
  521. | exp TIMES exp
  522. | exp DIVIDE exp
  523. | MINUS exp %prec NEG
  524. | exp POW exp
  525. | LPAREN exp RPAREN
  526. """
  527. if option == 0:
  528. return float(items[0])
  529. elif option == 1:
  530. return items[0] + items[2]
  531. elif option == 2:
  532. return items[0] - items[2]
  533. elif option == 3:
  534. return items[0] * items[2]
  535. elif option == 4:
  536. return items[0] / items[2]
  537. elif option == 5:
  538. return - items[1]
  539. elif option == 6:
  540. return items[0] ** items[2]
  541. elif option == 7:
  542. return items[1]
  543. </pre></b>
  544. Note one important thing here - the rules, declared in our docstrings, are <b>not</b> terminated
  545. by a semicolon. This is not needed (as in traditional yacc), because the rules
  546. are separated into separate handler method docstrings, rather than being lumped in together.<br>
  547. <br>
  548. So don't put a semicolon in your grammar rule docstrings, or Bad Things might happen.
  549. </blockquote>
  550. <hr>
  551. <!--@-node:2.7. add rules callbacks-->
  552. <!--@+node:2.8. add flex script-->
  553. <h3>2.8. Add Flex Script</h3>
  554. <blockquote>
  555. Finally, we must tell pyBison how to carve up the input into tokens.<br>
  556. <br>
  557. Instead of having a separate flex or lex script, we embed the script
  558. verbatim as attribute <b>lexscript</b>.<br>
  559. <br>
  560. <b>NOTE</b> - you should provide this script as a Python raw string (<b>r"""</b>)<br>
  561. <br>
  562. We'll use here a simple flex script which simply recognises numbers, the '+',
  563. '-', '*', '/', '**' operators, and parentheses.<br>
  564. <br>
  565. For our lexer to work, it will need a C declarations section with the magic lines:
  566. <b><pre>
  567. %{
  568. int yylineno = 0;
  569. #include &lt;stdio.h&gt;
  570. #include &lt;string.h&gt;
  571. #include "Python.h"
  572. #define YYSTYPE void *
  573. #include "tokens.h"
  574. extern void *py_parser;
  575. extern void (*py_input)(PyObject *parser, char *buf, int *result, int max_size);
  576. #define returntoken(tok) yylval = PyString_FromString(strdup(yytext)); return (tok);
  577. #define YY_INPUT(buf,result,max_size) { (*py_input)(py_parser, buf, &result, max_size); }
  578. %}
  579. </pre></b>
  580. (refer to Section 1.3 above for an explanation of these declarations).<br>
  581. <br>
  582. Our completed <b>Parser</b> class declaration now looks like this:
  583. <b><pre>
  584. class Parser(BisonParser):
  585. tokens = ['NUMBER',
  586. 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'POW',
  587. 'LPAREN', 'RPAREN',
  588. 'NEWLINE', 'QUIT']
  589. precedences = (
  590. ('left', ('MINUS', 'PLUS')),
  591. ('left', ('TIMES', 'DIVIDE')),
  592. ('left', ('NEG', )),
  593. ('right', ('POW', )),
  594. )
  595. def read(self, nbytes):
  596. try:
  597. return raw_input("> ")
  598. except EOFError:
  599. return ''
  600. # Declare the start target here (by name)
  601. start = "input"
  602. def on_input(self, target, option, names, items):
  603. """
  604. input :
  605. | input line
  606. """
  607. return
  608. def on_line(self, target, option, names, items):
  609. """
  610. line : NEWLINE
  611. | exp NEWLINE
  612. """
  613. if option == 1:
  614. print items[0].value
  615. def on_exp(self, target, option, names, items):
  616. """
  617. exp : NUMBER
  618. | exp PLUS exp
  619. | exp MINUS exp
  620. | exp TIMES exp
  621. | exp DIVIDE exp
  622. | MINUS exp %prec NEG
  623. | exp POW exp
  624. | LPAREN exp RPAREN
  625. """
  626. if option == 0:
  627. return float(items[0])
  628. elif option == 1:
  629. return items[0] + items[2]
  630. elif option == 2:
  631. return items[0] - items[2]
  632. elif option == 3:
  633. return items[0] * items[2]
  634. elif option == 4:
  635. return items[0] / items[2]
  636. elif option == 5:
  637. return - items[1]
  638. elif option == 6:
  639. return items[0] ** items[2]
  640. elif option == 7:
  641. return items[1]
  642. lexscript = r"""
  643. %{
  644. int yylineno = 0;
  645. #include &lt;stdio.h&gt;
  646. #include &lt;string.h&gt;
  647. #include "Python.h"
  648. #define YYSTYPE void *
  649. #include "tokens.h"
  650. extern void *py_parser;
  651. extern void (*py_input)(PyObject *parser, char *buf, int *result, int max_size);
  652. #define returntoken(tok) yylval = PyString_FromString(strdup(yytext)); return (tok);
  653. #define YY_INPUT(buf,result,max_size) { (*py_input)(py_parser, buf, &result, max_size); }
  654. %}
  655. %%
  656. [0-9]+ { returntoken(NUMBER); }
  657. "(" { returntoken(LPAREN); }
  658. ")" { returntoken(RPAREN); }
  659. "+" { returntoken(PLUS); }
  660. "-" { returntoken(MINUS); }
  661. "*" { returntoken(TIMES); }
  662. "**" { returntoken(POW); }
  663. "/" { returntoken(DIVIDE); }
  664. "quit" { printf("lex: got QUIT\n"); yyterminate(); returntoken(QUIT); }
  665. [ \t\v\f] {}
  666. [\n] {yylineno++; returntoken(NEWLINE); }
  667. . { printf("unknown char %c ignored\n", yytext[0]); /* ignore bad chars */}
  668. %%
  669. int yywrap() { return(1); }
  670. """
  671. </pre></b>
  672. <blockquote style=""color:#900000"><big><b>NOTE</b> - if you are using recent versions of flex (ie, the ones which violate
  673. the ANSI standards for lex/flex), you'll have to change the lexing code above;
  674. removing the line <b>int yylineno = 0;</b></big></blockquote><br>
  675. <br>
  676. Note that we've sneaked in an additional method, <b>.read(self, nbytes)</b>.
  677. This is another callback that gets invoked by the lexer whenever it needs
  678. more input. <i>(quick tip - in your mycalc.py file, do an 'import readline', so
  679. you get line editing and recall when the parser runs)</i>.<br>
  680. <br>
  681. This gives a lot of flexibility, because our Parser class gets to control
  682. exactly where its input comes from - file, or a string, socket, whatever.<br>
  683. <br>
  684. </blockquote>
  685. <hr>
  686. <!--@-node:2.8. add flex script-->
  687. <!--@+node:2.9. write runner script-->
  688. <h3>2.9. Write Runner Script</h3>
  689. <blockquote>
  690. One quick last thing to do here - we just need a tiny script (say, 'runcalc.py'),
  691. to import our Parser class and run it:
  692. <b><blockquote><pre>
  693. #!/usr/bin/env python
  694. import mycalc
  695. p = mycalc.Parser()
  696. p.run()
  697. </pre></blockquote></b>
  698. There's a specific reason why we do this - if we made our <b>mycalc.py</b>
  699. script executable, then when we first instantiate our <b>Parser</b> class,
  700. PyBison will guess a name for the dynamic library to create. If running
  701. mycalc.py directly, then <b>self.__class__.__module__</b> will be '__main__',
  702. and our dynamic library would be created with the name <b>__main__-parser.so</b>,
  703. which is pretty ugly. You could force a name for the library file by declaring
  704. an additional attribute in the Parser class:
  705. <b><blockquote><pre>
  706. bisonEngineLibName = "mycalc-parser"
  707. </pre></blockquote></b>
  708. Oh, and don't forget to chmod the script to be executable.
  709. </blockquote>
  710. <!--@-node:2.9. write runner script-->
  711. <!--@-others-->
  712. </blockquote>
  713. <hr>
  714. <!--@-node:2. prepare parser class-->
  715. <!--@+node:3. running our example-->
  716. <h2>3. Run The Parser</h2>
  717. <blockquote>
  718. We're now ready to run our completed parser.<br>
  719. <br>
  720. Given that you have created the files <b>mycalc.py</b> and <b>runcalc.py</b>
  721. in the current directory, and that you've already installed PyBison (refer INSTALL
  722. file), you'll be set to go.<br>
  723. <br>
  724. From your shell, just type:
  725. <b><blockquote><pre>
  726. $ ./runcalc.py
  727. </pre></blockquote></b>
  728. The first time you run this parser, it might make a lot of compilation-type noises. For example, my aging Debian-based system produces:
  729. <b><blockquote><pre>
  730. In file included from /usr/include/python2.3/Python.h:8,
  731. from tmp.l:6:
  732. /usr/include/python2.3/pyconfig.h:847:1: warning: "_POSIX_C_SOURCE" redefined
  733. In file included from /usr/include/stdio.h:28,
  734. from tmp.lex.c:11:
  735. /usr/include/features.h:171:1: warning: this is the location of the previous definition
  736. </pre></blockquote></b>
  737. All this relates to a bit of black magic which is happening in the background.<br>
  738. <br>
  739. The first time you instantiate your <b>mycalc.Parser</b> class, the <b>bison.BisonParser</b>
  740. base class tries to load the dynamic library <b>mycalc-parser.so</b> (or, on windows, mycalc-parser.dll).<br>
  741. <br>
  742. If the library file is not present (or if it is out of date, determined from hashing handler docstrings and pertinent attributes in the class), PyBison attempts to build it.<br>
  743. <br>
  744. To build this library, PyBison:
  745. <ul>
  746. <li>Rips the static attributes, and handler method docstrings, from the client
  747. Parser class</li>
  748. <li>Generates temporary grammar (tmp.y) and tokeniser (tmp.l) files</li>
  749. <li>Runs <b>bison</b> (or <b>self.bisonCmd</b>, refer source file bison.pyx) on tmp.y</li>
  750. <li>Runs <b>flex</b> (or <b>self.flexCmd</b>, refer bison.pyx) on tmp.l</li>
  751. <li>Compiles the resulting <b>tmp.bison.c</b> and <b>tmp.flex.c</b> files to
  752. object files</li>
  753. <li>Links these objects into the shared library file <b>mycalc-parser.so</b></li>
  754. Subsequent instantiations of the class will not repeat this compilation, unless you
  755. happen to have changed the embedded lex script, or grammar-related attributes of
  756. your class.<br>
  757. <hr>
  758. Getting back to the point - as long as the <b>mycalc-parser.so</b> library built
  759. and loaded successfully, we should now see a prompt (refer <b>.input(self, nchars)</b>
  760. method in 2.8):
  761. <b><blockquote><pre>
  762. $ ./runcalc.py
  763. &gt;
  764. </pre></blockquote></b>
  765. At this prompt, you can type in numbers, or simple arithmetic expressions, and see the
  766. result get printed out:
  767. <b><blockquote><pre>
  768. &gt; 2 + 3
  769. 5
  770. &gt; 4 + 5 * 6
  771. 34
  772. </pre></blockquote></b>
  773. (note that the higher precedence of '*' has applied).
  774. </ul>
  775. </blockquote>
  776. <hr>
  777. <!--@-node:3. running our example-->
  778. <!--@+node:4. miscellaneous-->
  779. <h2>4. Miscellaneous Remarks</h2>
  780. <blockquote>
  781. Just a few quick notes, to cover some of the possible gotchas.<br>
  782. <!--@+others-->
  783. <!--@+node:4.1. plurality-->
  784. <h3>4.1. Plurality</h3>
  785. <blockquote>
  786. In the present version of PyBison, you may only have one instance of
  787. any given Parser class actually <i>running</i> at any one time.
  788. This is because the present version of PyBison makes use of a couple
  789. of global C variables to store hooks into your Parser instance.<br>
  790. <br>
  791. However, you can have multiple instances existing at the same time.<br>
  792. <br>
  793. Also, you can have several parsers running at the same time, <b><i>as long as
  794. they are each instantiated from different Parser classes</i></b>.
  795. </blockquote>
  796. <hr>
  797. <!--@-node:4.1. plurality-->
  798. <!--@+node:4.2. building a parse tree-->
  799. <h3>4.2. Building A Parse Tree</h3>
  800. <blockquote>
  801. The <b>.run()</b> method of your parser object returns whatever your handler for
  802. the top-level target returned.<br>
  803. <br>
  804. Building a whole parse tree is pretty simple.<br>
  805. <br>
  806. Within each parse target handler callback (your <b>.on_whateverTarget()</b> methods),
  807. you need to create a new <b>BisonNode</b> instance, and store the component items
  808. (the <b>items</b> argument, or whatever you want to extract from <b>items</b>) as
  809. attributes, then return this BisonNode object.<br>
  810. <br>
  811. Then, with the BisonNode object returned from your parser's <b>.run()</b> method,
  812. you'll be able to traverse the tree of the entire parse run.
  813. </blockquote>
  814. <!--@-node:4.2. building a parse tree-->
  815. <!--@-others-->
  816. </blockquote>
  817. <hr>
  818. <!--@-node:4. miscellaneous-->
  819. <!--@+node:5. conclusion-->
  820. <h2>5. Conclusion</h2>
  821. <blockquote>
  822. Through this document, we have started from scratch, and created and used a
  823. complete, working parser.<br>
  824. <br>
  825. We have presented the options of starting with existing .y and .l scripts and
  826. converting them to a boilerplate PyBison .py file, versus writing your own
  827. python parser file from scratch.<br>
  828. <br>
  829. We have covered the requirements for building Parser classes, the attributes and
  830. methods you need to declare.<br>
  831. <br>
  832. We have discussed the callback model, whereby instances of your Parser class
  833. receive callbacks from PyBison whenever input is required, and whenver a
  834. parse target has been unambiguously reached.<br>
  835. <br>
  836. We have briefly discussed how PyBison derives grammar and tokeniser scripts
  837. from the contents of our Parser class, and how PyBison runs bison and flex on
  838. these scripts, compiles the output, and links the result into a shared library,
  839. which can be used in subsequent uses of the Parser to get almost the full speed
  840. of C-based code, from the comfort and convenience of the Python environment.<br>
  841. <br>
  842. And also, we have briefly mentioned how to use PyBison to build up a parse tree
  843. as an easily-traversed Python data structure.<br>
  844. <br>
  845. We hope this document has got you up to speed without undue head-scratching, and
  846. that you're now starting to get a feel for designing and building your own
  847. parsers.
  848. </blockquote>
  849. <!--@-node:5. conclusion-->
  850. <!--@-others-->
  851. <hr>
  852. <address><a href="mailto:david@freenet.co.nz">David McNab</a></address>
  853. <!-- Created: Fri Apr 23 01:27:41 NZST 2004 -->
  854. <!-- hhmts start -->
  855. <!-- hhmts end -->
  856. <!--@-node:body-->
  857. <!--@-others-->
  858. </body>
  859. </html>
  860. <!--@-node:@file doc/walkthrough.html-->
  861. <!--@-leo-->