| @@ -30,12 +30,13 @@ Use the reference pages for more in-depth explanations. (links in the [main page | |||
| ## LALR usage | |||
| By default Lark silently resolves Shift/Reduce conflicts as Shift. To enable warnings pass `debug=True`. To get the messages printed you have to configure `logging` framework beforehand. For example: | |||
| By default Lark silently resolves Shift/Reduce conflicts as Shift. To enable warnings pass `debug=True`. To get the messages printed you have to configure the `logger` beforehand. For example: | |||
| ```python | |||
| from lark import Lark | |||
| import logging | |||
| logging.basicConfig(level=logging.DEBUG) | |||
| from lark import Lark, logger | |||
| logger.setLevel(logging.DEBUG) | |||
| collision_grammar = ''' | |||
| start: as as | |||
| @@ -1,3 +1,4 @@ | |||
| from .common import logger | |||
| from .tree import Tree | |||
| from .visitors import Transformer, Visitor, v_args, Discard | |||
| from .visitors import InlineTransformer, inline_args # XXX Deprecated | |||
| @@ -1,6 +1,13 @@ | |||
| import logging | |||
| from .utils import Serialize | |||
| from .lexer import TerminalDef | |||
| logger = logging.getLogger("lark") | |||
| logger.addHandler(logging.StreamHandler()) | |||
| # Set to highest level, since we have some warnings amongst the code | |||
| # By default, we should not output any log messages | |||
| logger.setLevel(logging.CRITICAL) | |||
| ###{standalone | |||
| class LexerConf(Serialize): | |||
| @@ -1,13 +1,13 @@ | |||
| from __future__ import absolute_import | |||
| import sys, os, pickle, hashlib, logging | |||
| import sys, os, pickle, hashlib | |||
| from io import open | |||
| from .utils import STRING_TYPE, Serialize, SerializeMemoizer, FS, isascii | |||
| from .load_grammar import load_grammar | |||
| from .tree import Tree | |||
| from .common import LexerConf, ParserConf | |||
| from .common import LexerConf, ParserConf, logger | |||
| from .lexer import Lexer, TraditionalLexer, TerminalDef, UnexpectedToken | |||
| from .parse_tree_builder import ParseTreeBuilder | |||
| @@ -214,7 +214,7 @@ class Lark(Serialize): | |||
| cache_fn = '.lark_cache_%s.tmp' % md5 | |||
| if FS.exists(cache_fn): | |||
| logging.debug('Loading grammar from cache: %s', cache_fn) | |||
| logger.debug('Loading grammar from cache: %s', cache_fn) | |||
| with FS.open(cache_fn, 'rb') as f: | |||
| self._load(f, self.options.transformer, self.options.postlex) | |||
| return | |||
| @@ -291,7 +291,7 @@ class Lark(Serialize): | |||
| self.lexer = self._build_lexer() | |||
| if cache_fn: | |||
| logging.debug('Saving grammar to cache: %s', cache_fn) | |||
| logger.debug('Saving grammar to cache: %s', cache_fn) | |||
| with FS.open(cache_fn, 'wb') as f: | |||
| self.save(f) | |||
| @@ -10,11 +10,11 @@ is better documented here: | |||
| http://www.bramvandersanden.com/post/2014/06/shared-packed-parse-forest/ | |||
| """ | |||
| import logging | |||
| from collections import deque | |||
| from ..visitors import Transformer_InPlace, v_args | |||
| from ..exceptions import UnexpectedEOF, UnexpectedToken | |||
| from ..common import logger | |||
| from .grammar_analysis import GrammarAnalyzer | |||
| from ..grammar import NonTerminal | |||
| from .earley_common import Item, TransitiveItem | |||
| @@ -301,7 +301,7 @@ class Parser: | |||
| try: | |||
| debug_walker = ForestToPyDotVisitor() | |||
| except ImportError: | |||
| logging.warning("Cannot find dependency 'pydot', will not generate sppf debug image") | |||
| logger.warning("Cannot find dependency 'pydot', will not generate sppf debug image") | |||
| else: | |||
| debug_walker.visit(solutions[0], "sppf.png") | |||
| @@ -6,11 +6,11 @@ For now, shift/reduce conflicts are automatically resolved as shifts. | |||
| # Author: Erez Shinan (2017) | |||
| # Email : erezshin@gmail.com | |||
| import logging | |||
| from collections import defaultdict, deque | |||
| from ..utils import classify, classify_bool, bfs, fzset, Serialize, Enumerator | |||
| from ..exceptions import GrammarError | |||
| from ..common import logger | |||
| from .grammar_analysis import GrammarAnalyzer, Terminal, LR0ItemSet | |||
| from ..grammar import Rule | |||
| @@ -256,8 +256,8 @@ class LALR_Analyzer(GrammarAnalyzer): | |||
| raise GrammarError('Reduce/Reduce collision in %s between the following rules: %s' % (la, ''.join([ '\n\t\t- ' + str(r) for r in rules ]))) | |||
| if la in actions: | |||
| if self.debug: | |||
| logging.warning('Shift/Reduce conflict for terminal %s: (resolving as shift)', la.name) | |||
| logging.warning(' * %s', list(rules)[0]) | |||
| logger.warning('Shift/Reduce conflict for terminal %s: (resolving as shift)', la.name) | |||
| logger.warning(' * %s', list(rules)[0]) | |||
| else: | |||
| actions[la] = (Reduce, list(rules)[0]) | |||
| m[state] = { k.name: v for k, v in actions.items() } | |||
| @@ -2,6 +2,7 @@ from __future__ import absolute_import, print_function | |||
| import unittest | |||
| import logging | |||
| from lark import logger | |||
| from .test_trees import TestTrees | |||
| from .test_tools import TestStandalone | |||
| @@ -11,11 +12,13 @@ from .test_reconstructor import TestReconstructor | |||
| try: | |||
| from .test_nearley.test_nearley import TestNearley | |||
| except ImportError: | |||
| logging.warning("Warning: Skipping tests for Nearley grammar imports (js2py required)") | |||
| logger.warning("Warning: Skipping tests for Nearley grammar imports (js2py required)") | |||
| # from .test_selectors import TestSelectors | |||
| # from .test_grammars import TestPythonG, TestConfigG | |||
| from .test_logger import Testlogger | |||
| from .test_parser import ( | |||
| TestLalrStandard, | |||
| TestEarleyStandard, | |||
| @@ -31,7 +34,7 @@ from .test_parser import ( | |||
| TestParsers, | |||
| ) | |||
| logging.basicConfig(level=logging.INFO) | |||
| logger.setLevel(logging.INFO) | |||
| if __name__ == '__main__': | |||
| unittest.main() | |||
| @@ -0,0 +1,65 @@ | |||
| import logging | |||
| from contextlib import contextmanager | |||
| from lark import Lark, logger | |||
| from unittest import TestCase, main | |||
| try: | |||
| from StringIO import StringIO | |||
| except ImportError: | |||
| from io import StringIO | |||
| @contextmanager | |||
| def capture_log(): | |||
| stream = StringIO() | |||
| orig_handler = logger.handlers[0] | |||
| del logger.handlers[:] | |||
| logger.addHandler(logging.StreamHandler(stream)) | |||
| yield stream | |||
| del logger.handlers[:] | |||
| logger.addHandler(orig_handler) | |||
| class Testlogger(TestCase): | |||
| def test_debug(self): | |||
| logger.setLevel(logging.DEBUG) | |||
| collision_grammar = ''' | |||
| start: as as | |||
| as: a* | |||
| a: "a" | |||
| ''' | |||
| with capture_log() as log: | |||
| Lark(collision_grammar, parser='lalr', debug=True) | |||
| log = log.getvalue() | |||
| # since there are conflicts about A | |||
| # symbol A should appear in the log message for hint | |||
| self.assertIn("A", log) | |||
| def test_non_debug(self): | |||
| logger.setLevel(logging.DEBUG) | |||
| collision_grammar = ''' | |||
| start: as as | |||
| as: a* | |||
| a: "a" | |||
| ''' | |||
| with capture_log() as log: | |||
| Lark(collision_grammar, parser='lalr', debug=False) | |||
| log = log.getvalue() | |||
| # no log messge | |||
| self.assertEqual(len(log), 0) | |||
| def test_loglevel_higher(self): | |||
| logger.setLevel(logging.ERROR) | |||
| collision_grammar = ''' | |||
| start: as as | |||
| as: a* | |||
| a: "a" | |||
| ''' | |||
| with capture_log() as log: | |||
| Lark(collision_grammar, parser='lalr', debug=True) | |||
| log = log.getvalue() | |||
| # no log messge | |||
| self.assertEqual(len(log), 0) | |||
| if __name__ == '__main__': | |||
| main() | |||
| @@ -6,16 +6,17 @@ import logging | |||
| import os | |||
| import codecs | |||
| logging.basicConfig(level=logging.INFO) | |||
| from lark import logger | |||
| from lark.tools.nearley import create_code_for_nearley_grammar, main as nearley_tool_main | |||
| logger.setLevel(logging.INFO) | |||
| TEST_PATH = os.path.abspath(os.path.dirname(__file__)) | |||
| NEARLEY_PATH = os.path.join(TEST_PATH, 'nearley') | |||
| BUILTIN_PATH = os.path.join(NEARLEY_PATH, 'builtin') | |||
| if not os.path.exists(NEARLEY_PATH): | |||
| logging.warn("Nearley not installed. Skipping Nearley tests!") | |||
| logger.warn("Nearley not installed. Skipping Nearley tests!") | |||
| raise ImportError("Skipping Nearley tests!") | |||
| import js2py # Ensures that js2py exists, to avoid failing tests | |||
| @@ -23,13 +23,13 @@ from io import ( | |||
| open, | |||
| ) | |||
| logging.basicConfig(level=logging.INFO) | |||
| try: | |||
| import regex | |||
| except ImportError: | |||
| regex = None | |||
| from lark import logger | |||
| from lark.lark import Lark | |||
| from lark.exceptions import GrammarError, ParseError, UnexpectedToken, UnexpectedInput, UnexpectedCharacters | |||
| from lark.tree import Tree | |||
| @@ -37,6 +37,7 @@ from lark.visitors import Transformer, Transformer_InPlace, v_args | |||
| from lark.grammar import Rule | |||
| from lark.lexer import TerminalDef, Lexer, TraditionalLexer | |||
| logger.setLevel(logging.INFO) | |||
| __path__ = os.path.dirname(__file__) | |||