Browse Source

Added debug information to bison generated parser.

Sander Mathijs van Veen 14 năm trước cách đây
mục cha
commit
314ca19b2d
11 tập tin đã thay đổi với 261 bổ sung9 xóa
  1. 1 0
      .gitignore
  2. 15 8
      Makefile
  3. 7 0
      docs/rules.mk
  4. 1 1
      external/pybison
  5. 21 0
      external/rules.mk
  6. 153 0
      src/calc.py
  7. 18 0
      src/logger.py
  8. 3 0
      test.py
  9. 0 0
      tests/__init__.py
  10. 29 0
      tests/rules.mk
  11. 13 0
      tests/test_calc.py

+ 1 - 0
.gitignore

@@ -7,3 +7,4 @@
 *.pdf
 *.toc
 *.synctex.gz
+/build

+ 15 - 8
Makefile

@@ -1,4 +1,3 @@
-
 BUILD=build/
 
 # Fix pdflatex search path
@@ -9,21 +8,29 @@ TEXGREP   := grep -i ".*:[0-9]*:.*\|warning"
 TGT_DIR :=
 TGT_DOC :=
 
+# Default target is 'all'. The 'build' target is defined here so that all
+# sub rules.mk can add prerequisites to the 'build' target.
+all:
+build:
+
 d := docs/
 include base.mk
 include $(d)/rules.mk
 
-.PHONY: docs
+d := external/
+include base.mk
+include $(d)/rules.mk
+
+d := tests/
+include base.mk
+include $(d)/rules.mk
+
+.PHONY: doc
 
-all: docs
+all: doc build
 
 clean:
 	rm -rf $(CLEAN)
 
-docs: $(TGT_DOC)
-
 $(TGT_DIR):
 	mkdir -p $(TGT_DIR)
-
-$(b)%.pdf: $(d)%.tex $(TGT_DIR)
-	pdflatex $(TEXFLAGS) -output-directory `dirname $@` $< | ${TEXGREP} || true

+ 7 - 0
docs/rules.mk

@@ -1 +1,8 @@
+.PHONY: docs
+
 TGT_DOC += $(b)proposal.pdf
+
+doc: $(TGT_DOC)
+
+$(b)%.pdf: $(d)%.tex $(TGT_DIR)
+	pdflatex $(TEXFLAGS) -output-directory `dirname $@` $< | ${TEXGREP} || true

+ 1 - 1
external/pybison

@@ -1 +1 @@
-Subproject commit a8765c6130a70855967527f61e527b13ce5143f5
+Subproject commit 9c01c2cea1d39f5334c9e987eb40dfb25788862e

+ 21 - 0
external/rules.mk

@@ -0,0 +1,21 @@
+TGT_DIR += $(b)pybison
+
+PYBISON_INC := -Iexternal/pybison/src/c -I/usr/include/python2.7
+
+build: $(b)pybison/bison_.so $(b)pybison/bison.py
+
+$(b)pybison/bison_.so: $(b)pybison/bison_.o $(b)pybison/bisondynlib-linux.o
+	$(CC) -g -O0 -pipe -Wall -Wextra -shared -pthread -o $@ $^
+
+$(b)pybison/bison.py: $(d)pybison/src/python/bison.py | $(b)pybison
+	ln -s `realpath $<` $@
+
+$(b)pybison/bisondynlib-linux.o $(b)pybison/bison_.o: | $(b)pybison
+	$(CC) -g -O0 -pipe -Wall -Wextra -o $@ -c $< -pthread -fPIC $(PYBISON_INC)
+
+$(b)pybison/bisondynlib-linux.o: $(d)pybison/src/c/bisondynlib-linux.c
+$(b)pybison/bison_.o: $(b)pybison/bison_.c
+
+$(b)pybison/%.c: $(d)pybison/src/pyrex/%.pyx
+	pyrexc -o $@ $<
+

+ 153 - 0
src/calc.py

@@ -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)

+ 18 - 0
src/logger.py

@@ -0,0 +1,18 @@
+import logging
+import logging.config
+import sys
+
+import config
+import default_config as default
+
+try:
+    logging.basicConfig(level=logging.DEBUG,
+                        format=getattr(config, 'LOG_FORMAT', default.LOG_FORMAT),
+                        filename=getattr(config, 'LOG_FILE', default.LOG_FILE),
+                        filemode='a')
+except IOError as e:  # pragma: no cover
+    print >>sys.stderr, 'warning: IOError raised: "%s"' % str(e)
+
+
+def logger(name):
+    return logging.getLogger(name)

+ 3 - 0
test.py

@@ -0,0 +1,3 @@
+from testrunner import main
+import sys
+main(sys.argv[1:])

+ 0 - 0
tests/__init__.py


+ 29 - 0
tests/rules.mk

@@ -0,0 +1,29 @@
+TESTS=$(wildcard tests/test_*.py)
+COVERAGE_OUTPUT_DIR := coverage
+OMIT := --omit /usr/share/pyshared/*,/usr/lib64/portage/*
+
+ifeq ($(findstring python-coverage,$(wildcard /usr/bin/*)), python-coverage)
+COVERAGE=/usr/bin/python-coverage
+else
+COVERAGE=/usr/bin/coverage
+endif
+
+.PHONY: test coverage $(TESTS)
+
+test: $(TESTS) build
+
+coverage: ${COVERAGE}
+	mkdir ${COVERAGE_OUTPUT_DIR} 2>/dev/null || true
+	${COVERAGE} erase
+	for t in ${TESTS}; do \
+		echo $$t; \
+		${COVERAGE} ${OMIT} -x test.py $$t; \
+		${COVERAGE} ${OMIT} -c; \
+	done
+	${COVERAGE} html ${OMIT} --dir ${COVERAGE_OUTPUT_DIR}
+
+${COVERAGE}:
+	@echo "Install package 'python-coverage' to generate a coverage report."
+	@echo "On Debian/Ubuntu use: sudo apt-get install python-coverage"; false
+
+$(TESTS): ; @python -m testrunner $@

+ 13 - 0
tests/test_calc.py

@@ -0,0 +1,13 @@
+import unittest
+
+
+class TestCalc(unittest.TestCase):
+
+    def setUp(self):
+        pass
+
+    def tearDown(self):
+        pass
+
+    def test_true(self):
+        assert True