Browse Source

Refactoring and fixes for merge 2fd0087

tags/gm/2021-09-23T00Z/github.com--lark-parser-lark/0.6.3
Erez Shinan 7 years ago
parent
commit
454c88b58a
2 changed files with 42 additions and 25 deletions
  1. +28
    -25
      lark/load_grammar.py
  2. +14
    -0
      tests/test_parser.py

+ 28
- 25
lark/load_grammar.py View File

@@ -24,6 +24,8 @@ inline_args = v_args(inline=True)
__path__ = os.path.dirname(__file__) __path__ = os.path.dirname(__file__)
IMPORT_PATHS = [os.path.join(__path__, 'grammars')] IMPORT_PATHS = [os.path.join(__path__, 'grammars')]


EXT = '.lark'

_RE_FLAGS = 'imslux' _RE_FLAGS = 'imslux'


def is_terminal(sym): def is_terminal(sym):
@@ -139,16 +141,16 @@ RULES = {
'ignore': ['_IGNORE expansions _NL'], 'ignore': ['_IGNORE expansions _NL'],
'declare': ['_DECLARE _declare_args _NL'], 'declare': ['_DECLARE _declare_args _NL'],
'import': ['_IMPORT _import_path _NL', 'import': ['_IMPORT _import_path _NL',
'_IMPORT _import_path _LPAR list_name _RPAR _NL',
'_IMPORT _import_path _LPAR name_list _RPAR _NL',
'_IMPORT _import_path _TO TERMINAL _NL'], '_IMPORT _import_path _TO TERMINAL _NL'],


'_import_path': ['import_common', 'import_rel'],
'import_common': ['_import_args'],
'_import_path': ['import_lib', 'import_rel'],
'import_lib': ['_import_args'],
'import_rel': ['_DOT _import_args'], 'import_rel': ['_DOT _import_args'],
'_import_args': ['name', '_import_args _DOT name'], '_import_args': ['name', '_import_args _DOT name'],


'list_name': ['_list_name'],
'_list_name': ['name', '_list_name _COMMA name'],
'name_list': ['_name_list'],
'_name_list': ['name', '_name_list _COMMA name'],


'_declare_args': ['name', '_declare_args name'], '_declare_args': ['name', '_declare_args name'],
'literal': ['REGEXP', 'STRING'], 'literal': ['REGEXP', 'STRING'],
@@ -506,25 +508,19 @@ class Grammar:




_imported_grammars = {} _imported_grammars = {}
def import_grammar(grammar_path, base_path=None):
def import_grammar(grammar_path, base_paths=[]):
if grammar_path not in _imported_grammars: if grammar_path not in _imported_grammars:
if base_path is None:
import_paths = IMPORT_PATHS
else:
import_paths = [base_path] + IMPORT_PATHS
found = False
import_paths = base_paths + IMPORT_PATHS
for import_path in import_paths: for import_path in import_paths:
try:
with suppress(IOError):
with open(os.path.join(import_path, grammar_path)) as f: with open(os.path.join(import_path, grammar_path)) as f:
text = f.read() text = f.read()
grammar = load_grammar(text, grammar_path) grammar = load_grammar(text, grammar_path)
_imported_grammars[grammar_path] = grammar _imported_grammars[grammar_path] = grammar
found = True
break break
except FileNotFoundError:
pass
if not found:
raise FileNotFoundError(grammar_path)
else:
open(grammar_path)
assert False


return _imported_grammars[grammar_path] return _imported_grammars[grammar_path]


@@ -640,18 +636,25 @@ class GrammarLoader:
t ,= stmt.children t ,= stmt.children
ignore.append(t) ignore.append(t)
elif stmt.data == 'import': elif stmt.data == 'import':
dotted_path = stmt.children[0].children
if len(stmt.children) > 1:
path_node, arg1 = stmt.children
else:
path_node ,= stmt.children
arg1 = None

dotted_path = path_node.children


if len(stmt.children) > 1 and hasattr(stmt.children[1], 'children'): # Multi import
names = stmt.children[1].children
if isinstance(arg1, Tree): # Multi import
names = arg1.children
aliases = names # Can't have aliased multi import, so all aliases will be the same as names aliases = names # Can't have aliased multi import, so all aliases will be the same as names
grammar_path = os.path.join(*dotted_path) + '.lark'
else: # Single import else: # Single import
names = [dotted_path[-1]] # Get name from dotted path names = [dotted_path[-1]] # Get name from dotted path
aliases = [stmt.children[1] if len(stmt.children) > 1 else dotted_path[-1]] # Aliases if exist
grammar_path = os.path.join(*dotted_path[:-1]) + '.lark' # Exclude name from grammar path
aliases = [arg1] if arg1 else names # Aliases if exist
dotted_path = dotted_path[:-1]

grammar_path = os.path.join(*dotted_path) + EXT


if stmt.children[0].data == 'import_common': # Regular import
if path_node.data == 'import_lib': # Import from library
g = import_grammar(grammar_path) g = import_grammar(grammar_path)
else: # Relative import else: # Relative import
if grammar_name == '<string>': # Import relative to script file path if grammar is coded in script if grammar_name == '<string>': # Import relative to script file path if grammar is coded in script
@@ -659,7 +662,7 @@ class GrammarLoader:
else: else:
base_file = grammar_name # Import relative to grammar file path if external grammar file base_file = grammar_name # Import relative to grammar file path if external grammar file
base_path = os.path.split(base_file)[0] base_path = os.path.split(base_file)[0]
g = import_grammar(grammar_path, base_path=base_path)
g = import_grammar(grammar_path, base_paths=[base_path])


for name, alias in zip(names, aliases): for name, alias in zip(names, aliases):
token_options = dict(g.token_defs)[name] token_options = dict(g.token_defs)[name]


+ 14
- 0
tests/test_parser.py View File

@@ -976,6 +976,20 @@ def _make_parser_test(LEXER, PARSER):
x = l.parse('12 capybaras') x = l.parse('12 capybaras')
self.assertEqual(x.children, ['12', 'capybaras']) self.assertEqual(x.children, ['12', 'capybaras'])


def test_import_errors(self):
grammar = """
start: NUMBER WORD

%import .grammars.bad_test.NUMBER
"""
self.assertRaises(IOError, _Lark, grammar)

grammar = """
start: NUMBER WORD

%import bad_test.NUMBER
"""
self.assertRaises(IOError, _Lark, grammar)


@unittest.skipIf(PARSER != 'earley', "Currently only Earley supports priority in rules") @unittest.skipIf(PARSER != 'earley', "Currently only Earley supports priority in rules")
def test_earley_prioritization(self): def test_earley_prioritization(self):


Loading…
Cancel
Save