소스 검색

Added debug information to bison generated parser.

Sander Mathijs van Veen 14 년 전
부모
커밋
314ca19b2d
11개의 변경된 파일261개의 추가작업 그리고 9개의 파일을 삭제
  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