|
@@ -0,0 +1,153 @@
|
|
|
|
|
+#!/usr/bin/env python
|
|
|
|
|
+"""
|
|
|
|
|
+A simple pybison parser program implementing a calculator
|
|
|
|
|
+"""
|
|
|
|
|
+
|
|
|
|
|
+import os.path
|
|
|
|
|
+PYBISON_BUILD = os.path.realpath('build/external/pybison')
|
|
|
|
|
+PYBISON_PYREX = os.path.realpath('external/pybison/src/pyrex')
|
|
|
|
|
+
|
|
|
|
|
+import sys
|
|
|
|
|
+sys.path.insert(0, PYBISON_BUILD)
|
|
|
|
|
+sys.path.insert(1, PYBISON_PYREX)
|
|
|
|
|
+
|
|
|
|
|
+from bison import BisonParser, BisonNode
|
|
|
|
|
+
|
|
|
|
|
+class Parser(BisonParser):
|
|
|
|
|
+ """
|
|
|
|
|
+ Implements the calculator parser. Grammar rules are defined in the method
|
|
|
|
|
+ docstrings. Scanner rules are in the 'lexscript' attribute.
|
|
|
|
|
+ """
|
|
|
|
|
+
|
|
|
|
|
+ # Output directory of generated pybison files, including a trailing slash.
|
|
|
|
|
+ buildDirectory = PYBISON_BUILD + '/'
|
|
|
|
|
+
|
|
|
|
|
+ # ----------------------------------------------------------------
|
|
|
|
|
+ # lexer tokens - these must match those in your lex script (below)
|
|
|
|
|
+ # ----------------------------------------------------------------
|
|
|
|
|
+ tokens = ['NUMBER',
|
|
|
|
|
+ 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'POW',
|
|
|
|
|
+ 'LPAREN', 'RPAREN',
|
|
|
|
|
+ 'NEWLINE', 'QUIT']
|
|
|
|
|
+
|
|
|
|
|
+ # ------------------------------
|
|
|
|
|
+ # precedences
|
|
|
|
|
+ # ------------------------------
|
|
|
|
|
+ precedences = (
|
|
|
|
|
+ ('left', ('MINUS', 'PLUS')),
|
|
|
|
|
+ ('left', ('TIMES', 'DIVIDE')),
|
|
|
|
|
+ ('left', ('NEG', )),
|
|
|
|
|
+ ('right', ('POW', )),
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ # ------------------------------------------------------------------
|
|
|
|
|
+ # override default read method with a version that prompts for input
|
|
|
|
|
+ # ------------------------------------------------------------------
|
|
|
|
|
+ def read(self, nbytes):
|
|
|
|
|
+ try:
|
|
|
|
|
+ return raw_input("> ") + "\n"
|
|
|
|
|
+ except EOFError:
|
|
|
|
|
+ return ''
|
|
|
|
|
+
|
|
|
|
|
+ # ---------------------------------------------------------------
|
|
|
|
|
+ # These methods are the python handlers for the bison targets.
|
|
|
|
|
+ # (which get called by the bison code each time the corresponding
|
|
|
|
|
+ # parse target is unambiguously reached)
|
|
|
|
|
+ #
|
|
|
|
|
+ # WARNING - don't touch the method docstrings unless you know what
|
|
|
|
|
+ # you are doing - they are in bison rule syntax, and are passed
|
|
|
|
|
+ # verbatim to bison to build the parser engine library.
|
|
|
|
|
+ # ---------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ # Declare the start target here (by name)
|
|
|
|
|
+ start = "input"
|
|
|
|
|
+
|
|
|
|
|
+ def on_input(self, target, option, names, values):
|
|
|
|
|
+ """
|
|
|
|
|
+ input :
|
|
|
|
|
+ | input line
|
|
|
|
|
+ """
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ def on_line(self, target, option, names, values):
|
|
|
|
|
+ """
|
|
|
|
|
+ line : NEWLINE
|
|
|
|
|
+ | exp NEWLINE
|
|
|
|
|
+ """
|
|
|
|
|
+ if option == 1:
|
|
|
|
|
+ print 'option:', option
|
|
|
|
|
+ #print 'on_line:', values[0]
|
|
|
|
|
+
|
|
|
|
|
+ def on_exp(self, target, option, names, values):
|
|
|
|
|
+ """
|
|
|
|
|
+ exp : NUMBER
|
|
|
|
|
+ | exp PLUS exp
|
|
|
|
|
+ | exp MINUS exp
|
|
|
|
|
+ | exp TIMES exp
|
|
|
|
|
+ | exp DIVIDE exp
|
|
|
|
|
+ | MINUS exp %prec NEG
|
|
|
|
|
+ | exp POW exp
|
|
|
|
|
+ | LPAREN exp RPAREN
|
|
|
|
|
+ """
|
|
|
|
|
+ print 'on_exp: got %s %s %s %s' % (target, option, names, values)
|
|
|
|
|
+
|
|
|
|
|
+ if option == 0:
|
|
|
|
|
+ return
|
|
|
|
|
+ #return float(values[0])
|
|
|
|
|
+ elif option == 1:
|
|
|
|
|
+ return
|
|
|
|
|
+ #return values[0] + values[2]
|
|
|
|
|
+ elif option == 2:
|
|
|
|
|
+ return values[0] - values[2]
|
|
|
|
|
+ elif option == 3:
|
|
|
|
|
+ return values[0] * values[2]
|
|
|
|
|
+ elif option == 4:
|
|
|
|
|
+ return values[0] / values[2]
|
|
|
|
|
+ elif option == 5:
|
|
|
|
|
+ return - values[1]
|
|
|
|
|
+ elif option == 6:
|
|
|
|
|
+ return values[0] ** values[2]
|
|
|
|
|
+ elif option == 7:
|
|
|
|
|
+ return values[1]
|
|
|
|
|
+
|
|
|
|
|
+ # -----------------------------------------
|
|
|
|
|
+ # raw lex script, verbatim here
|
|
|
|
|
+ # -----------------------------------------
|
|
|
|
|
+ lexscript = r"""
|
|
|
|
|
+ %{
|
|
|
|
|
+ //int yylineno = 0;
|
|
|
|
|
+ #include <stdio.h>
|
|
|
|
|
+ #include <string.h>
|
|
|
|
|
+ #include "Python.h"
|
|
|
|
|
+ #define YYSTYPE void *
|
|
|
|
|
+ #include "tokens.h"
|
|
|
|
|
+ extern void *py_parser;
|
|
|
|
|
+ extern void (*py_input)(PyObject *parser, char *buf, int *result, int max_size);
|
|
|
|
|
+ #define returntoken(tok) yylval = PyString_FromString(strdup(yytext)); return (tok);
|
|
|
|
|
+ #define YY_INPUT(buf,result,max_size) { (*py_input)(py_parser, buf, &result, max_size); }
|
|
|
|
|
+ %}
|
|
|
|
|
+
|
|
|
|
|
+ %%
|
|
|
|
|
+
|
|
|
|
|
+ [0-9]+ { returntoken(NUMBER); }
|
|
|
|
|
+ "(" { returntoken(LPAREN); }
|
|
|
|
|
+ ")" { returntoken(RPAREN); }
|
|
|
|
|
+ "+" { returntoken(PLUS); }
|
|
|
|
|
+ "-" { returntoken(MINUS); }
|
|
|
|
|
+ "*" { returntoken(TIMES); }
|
|
|
|
|
+ "**" { returntoken(POW); }
|
|
|
|
|
+ "/" { returntoken(DIVIDE); }
|
|
|
|
|
+ "quit" { printf("lex: got QUIT\n"); yyterminate(); returntoken(QUIT); }
|
|
|
|
|
+
|
|
|
|
|
+ [ \t\v\f] {}
|
|
|
|
|
+ [\n] {yylineno++; returntoken(NEWLINE); }
|
|
|
|
|
+ . { printf("unknown char %c ignored, yytext=0x%lx\n", yytext[0], yytext); /* ignore bad chars */}
|
|
|
|
|
+
|
|
|
|
|
+ %%
|
|
|
|
|
+
|
|
|
|
|
+ yywrap() { return(1); }
|
|
|
|
|
+ """
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == '__main__':
|
|
|
|
|
+ p = Parser(verbose=0, keepfiles=1)
|
|
|
|
|
+ p.run(debug=1)
|