05.03., 9:00 - 11:00: Due to updates GitLab will be unavailable for some minutes between 09:00 and 11:00.

Commit cfb4b043 authored by di68kap's avatar di68kap

- Anpassung der Grammatik-Tests and geänderte Reglen für wortarten-Angabe

parent 1161ec14
......@@ -97,11 +97,11 @@ from DHParser import start_logging, suspend_logging, resume_logging, is_filename
remove_anonymous_tokens, keep_children, is_one_of, not_one_of, has_content, apply_if, peek, \\
remove_anonymous_empty, keep_nodes, traverse_locally, strip, lstrip, rstrip, \\
replace_content, replace_content_by, forbid, assert_content, remove_infix_operator, \\
error_on, recompile_grammar, left_associative, lean_left, set_config_value, chain, \\
get_config_value, XML_SERIALIZATION, SXPRESSION_SERIALIZATION, COMPACT_SERIALIZATION, \\
JSON_SERIALIZATION, access_thread_locals, access_presets, finalize_presets, ErrorCode, \\
RX_NEVER_MATCH, set_tracer, resume_notices_on, trace_history, has_descendant, neg, \\
has_parent
add_error, error_on, recompile_grammar, left_associative, lean_left, set_config_value, \\
chain, get_config_value, XML_SERIALIZATION, SXPRESSION_SERIALIZATION, \\
COMPACT_SERIALIZATION, JSON_SERIALIZATION, access_thread_locals, access_presets, \\
finalize_presets, ErrorCode, RX_NEVER_MATCH, set_tracer, resume_notices_on, \\
trace_history, has_descendant, neg, has_parent
'''.format(dhparser_parentdir=DHPARSER_PARENTDIR)
......
......@@ -52,6 +52,7 @@ Example::
"""
import collections
import contextlib
import html
import os
from typing import List, Tuple, Union, Optional
......@@ -68,6 +69,7 @@ __all__ = ('CallItem',
'suspend_logging',
'resume_logging',
'log_dir',
'local_log_dir',
'is_logging',
'create_log',
'append_log',
......@@ -138,12 +140,11 @@ def log_dir(path: str = "") -> str:
Returns:
str - name of the logging directory or '' if logging is turned off.
"""
# the try-except clauses in the following are precautions for multithreading
dirname = path if path else get_config_value('log_dir')
if not dirname:
return ''
# `try ... except` rather `if os.path.exists(...)` to create directory
# to ensure thread-saftey.
dirname = os.path.normpath(dirname)
# `try ... except` rather than `if os.path.exists(...)` for thread-safety
try:
os.mkdir(dirname)
info_file_name = os.path.join(dirname, 'info.txt')
......@@ -161,6 +162,18 @@ def log_dir(path: str = "") -> str:
return dirname
@contextlib.contextmanager
def local_log_dir(path: str = './LOGS'):
"""Context manager for temporarily switching to a different log-directory."""
assert path, "Pathname cannot be empty"
saved_log_dir = get_config_value('log_dir')
log_dir(path)
try:
yield
finally:
set_config_value('log_dir', saved_log_dir)
def is_logging(thread_local_query: bool = True) -> bool:
"""-> True, if logging is turned on."""
if thread_local_query:
......@@ -513,7 +526,7 @@ def log_parsing_history(grammar, log_file_name: str = '', html: bool = True) ->
"""
def write_log(history: List[str], log_name: str) -> None:
htm = '.html' if html else ''
path = os.path.join(log_dir() or '', log_name + "_parser.log" + htm)
path = os.path.join(log_dir(), log_name + "_parser.log" + htm)
if os.path.exists(path):
os.remove(path)
# print('WARNING: Log-file "%s" already existed and was deleted.' % path)
......
......@@ -39,7 +39,7 @@ from typing import Dict, List, Union, cast
from DHParser.configuration import get_config_value
from DHParser.error import Error, is_error, adjust_error_locations
from DHParser.log import is_logging, clear_logs, log_parsing_history
from DHParser.log import is_logging, clear_logs, local_log_dir, log_parsing_history
from DHParser.parse import UnknownParserError, Parser, Lookahead
from DHParser.syntaxtree import Node, RootNode, parse_tree, flatten_sxpr, ZOMBIE_TAG
from DHParser.trace import set_tracer, all_descendants, trace_history
......@@ -63,32 +63,6 @@ __all__ = ('unit_from_config',
UNIT_STAGES = {'match*', 'match', 'fail', 'ast', 'cst'}
RESULT_STAGES = {'__cst__', '__ast__', '__err__'}
# def unit_from_configfile(config_filename):
# """
# Reads a grammar unit test from a config file.
# """
# cfg = configparser.ConfigParser(interpolation=None)
# cfg.read(config_filename, encoding="utf8")
# OD = collections.OrderedDict
# unit = OD()
# for section in cfg.sections():
# symbol, stage = section.split(':')
# if stage not in UNIT_STAGES:
# if symbol in UNIT_STAGES:
# symbol, stage = stage, symbol
# else:
# raise ValueError('Test stage %s not in: ' % (stage, str(UNIT_STAGES)))
# for testkey, testcode in cfg[section].items():
# if testcode[:3] + testcode[-3:] in {"''''''", '""""""'}:
# testcode = testcode[3:-3]
# # testcode = testcode.replace('\\#', '#')
# testcode = re.sub(r'(?<!\\)\\#', '#', testcode).replace('\\\\', '\\')
# elif testcode[:1] + testcode[-1:] in {"''", '""'}:
# testcode = testcode[1:-1]
# unit.setdefault(symbol, OD()).setdefault(stage, OD())[testkey] = testcode
# # print(json.dumps(unit, sort_keys=True, indent=4))
# return unit
RX_SECTION = re.compile(r'\s*\[(?P<stage>\w+):(?P<symbol>\w+)\]')
RE_VALUE = '(?:"""((?:.|\n)*?)""")|' + "(?:'''((?:.|\n)*?)''')|" + \
r'(?:"(.*?)")|' + "(?:'(.*?)')|" + r'(.*(?:\n(?:\s*\n)* .*)*)'
......@@ -431,7 +405,8 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report='REPORT'
cst = RootNode()
cst = cst.new_error(Node(ZOMBIE_TAG, "").with_pos(0), str(upe))
clean_test_name = str(test_name).replace('*', '')
# log_ST(cst, "match_%s_%s.cst" % (parser_name, clean_test_name))
# with local_log_dir('./LOGS'):
# log_ST(cst, "match_%s_%s.cst" % (parser_name, clean_test_name))
tests.setdefault('__cst__', {})[test_name] = cst
errors = [] # type: List[Error]
if is_error(cst.error_flag) and not lookahead_artifact(cst):
......@@ -450,8 +425,10 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report='REPORT'
ast_errors = ast.errors_sorted
adjust_error_locations(ast_errors, test_code)
if errors:
errata.append('\n\t'.join(
str(m).replace('\n', '\n\t\t') for m in ast_errors))
if errata: errata[-1] = errata[-1].rstrip('\n')
ast_errors.append('\n')
errata.append('\t' + '\n\t'.join(
str(msg).replace('\n', '\n\t\t') for msg in ast_errors))
else:
errata.append('Match test "%s" for parser "%s" failed:'
'\n\tExpr.: %s\n\n\t%s\n\n' %
......@@ -492,7 +469,9 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report='REPORT'
tests.setdefault('__err__', {})[test_name] = errata[-1]
# write parsing-history log only in case of failure!
if is_logging() and track_history:
log_parsing_history(parser, "match_%s_%s.log" % (parser_name, clean_test_name))
with local_log_dir('./LOGS'):
log_parsing_history(parser, "match_%s_%s.log" %
(parser_name, clean_test_name))
if verbose and 'fail' in tests:
write(' Fail-Tests for parser "' + parser_name + '"')
......@@ -515,7 +494,8 @@ def grammar_unit(test_unit, parser_factory, transformer_factory, report='REPORT'
tests.setdefault('__err__', {})[test_name] = errata[-1]
# write parsing-history log only in case of test-failure
if is_logging() and track_history:
log_parsing_history(parser, "fail_%s_%s.log" % (parser_name, test_name))
with local_log_dir('./LOGS'):
log_parsing_history(parser, "fail_%s_%s.log" % (parser_name, test_name))
if cst.error_flag:
adjust_error_locations(cst.errors, test_code)
tests.setdefault('__msg__', {})[test_name] = \
......
......@@ -62,6 +62,7 @@ __all__ = ('typing',
'concurrent_ident',
'unrepr',
'abbreviate_middle',
'escape_formatstr',
'lstrip_docstring',
'issubtype',
'isgenerictype',
......@@ -237,6 +238,15 @@ def abbreviate_middle(s: str, max_length: int) -> str:
return s
def escape_formatstr(s: str) -> str:
"""Replaces single curly braces by double curly-braces in string `s`,
so that they are not misinterpreted as place-holder by "".format().
"""
s = re.sub(r'(?<!\{)\{(?!\{)', '{{', s)
s = re.sub(r'(?<!\})\}(?!\})', '}}', s)
return s
#######################################################################
#
# type system support
......
......@@ -37,7 +37,8 @@ from typing import AbstractSet, Any, ByteString, Callable, cast, Container, Dict
from DHParser.error import Error, ErrorCode
from DHParser.syntaxtree import Node, WHITESPACE_PTYPE, TOKEN_PTYPE, LEAF_PTYPES, PLACEHOLDER, \
RootNode
from DHParser.toolkit import issubtype, isgenerictype, expand_table, smart_list, re, cython
from DHParser.toolkit import issubtype, isgenerictype, expand_table, smart_list, re, cython, \
escape_formatstr
__all__ = ('TransformationDict',
......@@ -109,6 +110,7 @@ __all__ = ('TransformationDict',
'forbid',
'require',
'assert_content',
'add_error',
'error_on',
'assert_has_children',
'peek')
......@@ -1233,6 +1235,27 @@ def delimit_children(context: List[Node], delimiter_tag_name: str, delimiter: st
#
########################################################################
@transformation_factory
@transformation_factory(str)
def add_error(context: List[Node], error_msg: str, error_code: ErrorCode = Error.ERROR):
"""
Raises an error unconditionally. This makes sense in case illegal pathes are
encoded in the syntax to provide more accurate error messages.
"""
node = context[-1]
if not error_msg:
error_msg = "Syntax Error"
try:
cast(RootNode, context[0]).new_error(node, error_msg.format(
tag_name=node.tag_name, content=node.content, pos=node.pos), error_code)
except KeyError as key_error:
cast(RootNode, context[0].new_error(
node, 'Schlüssel %s nicht erlaubt in Format-Zeichenkette: "%s"! '
'Erlaubt sind "tag_name", "content", "pos"' % (str(key_error), error_msg),
Error.AST_TRANSFORM_CRASH))
@transformation_factory(collections.abc.Callable)
def error_on(context: List[Node],
condition: Callable,
......@@ -1243,15 +1266,12 @@ def error_on(context: List[Node],
"""
node = context[-1]
if condition(context):
if error_msg:
cast(RootNode, context[0]).new_error(node, error_msg.format(
tag_name=node.tag_name, content=node.content, pos=node.pos), error_code)
else:
if not error_msg:
cond_name = condition.__name__ if hasattr(condition, '__name__') \
else condition.__class__.__name__ if hasattr(condition, '__class__') \
else '<unknown>'
cast(RootNode, context[0]).new_error(node, "transform.error_on: Failed to meet"
"condition " + cond_name, error_code)
error_msg = "transform.error_on: Failed to meet condition " + cond_name
add_error(context, error_msg, error_code)
#
# @transformation_factory(collections.abc.Callable)
......
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