Commit ee075c8b authored by Eckhart Arnold's avatar Eckhart Arnold
Browse files

- enbf.py: added directive "@testing"

parent feefd262
......@@ -25,7 +25,7 @@ except ImportError:
import re
from typing import Callable, Dict, List, Set, Tuple
from DHParser.toolkit import load_if_file, escape_re, md5, sane_parser_name, warnings
from DHParser.toolkit import load_if_file, escape_re, md5, sane_parser_name
from DHParser.parsers import Grammar, mixin_comment, nil_scanner, Forward, RE, NegativeLookahead, \
Alternative, Sequence, Optional, Required, OneOrMore, ZeroOrMore, Token, Compiler, \
ScannerFunc
......@@ -329,8 +329,9 @@ class EBNFCompiler(Compiler):
self.directives = {'whitespace': self.WHITESPACE['horizontal'],
'comment': '',
'literalws': ['right'],
'tokens': set(), # alt. 'scanner_tokens'
'filter': dict()} # alt. 'filter'
'tokens': set(), # alt. 'scanner_tokens'
'filter': dict(), # alt. 'filter'
'testing': False }
@property
def result(self) -> str:
......@@ -408,7 +409,7 @@ class EBNFCompiler(Compiler):
'r"""Parser for ' + article + self.grammar_name +
' source file' +
(', with this grammar:' if self.grammar_source else '.')]
definitions.append(('parser_initialization__', '"upon instatiation"'))
definitions.append(('parser_initialization__', '"upon instantiation"'))
if self.grammar_source:
definitions.append(('source_hash__',
'"%s"' % md5(self.grammar_source, __version__)))
......@@ -440,7 +441,7 @@ class EBNFCompiler(Compiler):
# check for unconnected rules
if warnings():
if not self.directives['testing']:
defined_symbols.difference_update(self.RESERVED_SYMBOLS)
def remove_connections(symbol):
......@@ -452,7 +453,8 @@ class EBNFCompiler(Compiler):
remove_connections(self.root)
for leftover in defined_symbols:
self.rules[leftover][0].add_error(('Rule "%s" is not connected to parser '
'root "%s"') % (leftover, self.root))
'root "%s" !') % (leftover, self.root) + ' (Use directive "@testing=True" '
'to supress this error message.)')
# set root parser and assemble python grammar definition
......@@ -528,8 +530,9 @@ class EBNFCompiler(Compiler):
return rx
def on_directive(self, node: Node) -> str:
key = str(node.children[0]).lower() # cast(str, node.children[0].result).lower()
key = str(node.children[0]).lower()
assert key not in self.directives['tokens']
if key in {'comment', 'whitespace'}:
if node.children[1].parser.name == "list_":
if len(node.children[1].result) != 1:
......@@ -554,6 +557,10 @@ class EBNFCompiler(Compiler):
"/%s/ does not." % value)
self.directives[key] = value
elif key == 'testing':
value = str(node.children[1])
self.directives['testing'] = value.lower() not in {"off", "false", "no"}
elif key == 'literalws':
value = {item.lower() for item in self._compile(node.children[1])}
if (len(value - {'left', 'right', 'both', 'none'}) > 0
......
......@@ -30,7 +30,7 @@ from DHParser import Node, error_messages
from DHParser.toolkit import compact_sexpr, is_logging
from DHParser.syntaxtree import MockParser
from DHParser.ebnf import grammar_changed
from DHParser.dsl import compile_on_disk
from DHParser.dsl import CompilationError, compile_on_disk
def mock_syntax_tree(sexpr):
......@@ -87,7 +87,7 @@ def mock_syntax_tree(sexpr):
return Node(MockParser(name, ':' + class_name), result)
def recompile_grammar(ebnf_filename, force=False):
def recompile_grammar(ebnf_filename, force=False) -> bool:
"""Recompiles an ebnf-grammar if necessary, that is if either no
corresponding 'XXXXCompiler.py'-file exists or if that file is
outdated.
......@@ -101,10 +101,11 @@ def recompile_grammar(ebnf_filename, force=False):
recompiled if it has been changed.
"""
if os.path.isdir(ebnf_filename):
success = True
for entry in os.listdir(ebnf_filename):
if entry.lower().endswith('.ebnf') and os.path.isfile(entry):
recompile_grammar(entry, force)
return
success = success and recompile_grammar(entry, force)
return success
base, ext = os.path.splitext(ebnf_filename)
compiler_name = base + 'Compiler.py'
......@@ -119,14 +120,11 @@ def recompile_grammar(ebnf_filename, force=False):
for e in errors:
f.write(e)
f.write('\n')
return False
if not errors:
if os.path.exists(base + '_errors.txt'):
# if query_remove_error_files:
# answer = input('Remove obsolete file ' + base + '_errors.txt (y/n)? ').lower()
# if answer not in {'y', 'yes'}:
# return
os.remove(base + '_errors.txt')
if not errors and os.path.exists(base + '_errors.txt'):
os.remove(base + '_errors.txt')
return True
UNIT_STAGES = {'match', 'fail', 'ast', 'cst', '__ast__', '__cst__'}
......
......@@ -45,8 +45,8 @@ __all__ = ['logging',
'is_logging',
'log_dir',
'logfile_basename',
'supress_warnings',
'warnings',
# 'supress_warnings',
# 'warnings',
'line_col',
'error_messages',
'compact_sexpr',
......@@ -127,24 +127,24 @@ def is_logging() -> bool:
return False
@contextlib.contextmanager
def supress_warnings(supress: bool = True):
global SUPRESS_WARNINGS
try:
save = SUPRESS_WARNINGS
except NameError:
save = False # global default for warning supression is False
SUPRESS_WARNINGS = supress
yield
SUPRESS_WARNINGS = save
def warnings() -> bool:
global SUPRESS_WARNINGS
try:
return not SUPRESS_WARNINGS
except NameError:
return True
# @contextlib.contextmanager
# def supress_warnings(supress: bool = True):
# global SUPRESS_WARNINGS
# try:
# save = SUPRESS_WARNINGS
# except NameError:
# save = False # global default for warning supression is False
# SUPRESS_WARNINGS = supress
# yield
# SUPRESS_WARNINGS = save
#
#
# def warnings() -> bool:
# global SUPRESS_WARNINGS
# try:
# return not SUPRESS_WARNINGS
# except NameError:
# return True
def line_col(text: str, pos: int) -> Tuple[int, int]:
......
# latex Grammar
@ testing = True
@ whitespace = /[ \t]*\n?(?!\s*\n)[ \t]*/ # whitespace, including at most one linefeed
@ comment = /%.*(?:\n|$)/
......
......@@ -25,7 +25,6 @@ import sys
sys.path.extend(['../../', '../', './'])
from DHParser.testing import recompile_grammar
from DHParser.toolkit import supress_warnings
with supress_warnings(True):
recompile_grammar('.', True)
if not recompile_grammar('.', True):
sys.exit(1)
......@@ -26,7 +26,7 @@ from multiprocessing import Pool
sys.path.extend(['../', './'])
from DHParser.toolkit import is_logging, compile_python_object, supress_warnings
from DHParser.toolkit import is_logging, compile_python_object
from DHParser.parsers import compile_source, Retrieve, WHITESPACE_PTYPE, nil_scanner
from DHParser.ebnf import get_ebnf_grammar, get_ebnf_transformer, EBNFTransformer, get_ebnf_compiler
from DHParser.dsl import CompilationError, compileDSL, DHPARSER_IMPORTS, parser_factory
......@@ -349,8 +349,7 @@ class TestBoundaryCases:
unconnected = /.*/
"""
try:
with supress_warnings(False):
grammar = parser_factory(ebnf)()
grammar = parser_factory(ebnf)()
assert False, "EBNF compiler should complain about unconnected rules."
except CompilationError as err:
grammar_src = err.result
......@@ -367,6 +366,12 @@ class TestBoundaryCases:
"a non-existant parser name!"
except KeyError:
pass
ebnf_testing = "@testing = True\n" + ebnf
try:
grammar = parser_factory(ebnf_testing)()
except CompilationError:
assert False, "EBNF compiler should not complain about unconnected " \
"rules when directive @testing is set."
class TestSynonymDetection:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment