Skip to content

Commit 7ed9b36

Browse files
committed
tuple -> Tuple
1 parent 4cd3a31 commit 7ed9b36

File tree

1 file changed

+75
-50
lines changed

1 file changed

+75
-50
lines changed

lark/load_grammar.py

Lines changed: 75 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,14 @@
101101
'_DECLARE': r'%declare',
102102
'_EXTEND': r'%extend',
103103
'_IMPORT': r'%import',
104+
'_INCLUDE': r'%include',
104105
'NUMBER': r'[+-]?\d+',
105106
}
106107

107108
RULES = {
108109
'start': ['_list'],
109110
'_list': ['_item', '_list _item'],
110-
'_item': ['rule', 'term', 'ignore', 'import', 'declare', 'override', 'extend', '_NL'],
111+
'_item': ['rule', 'term', 'ignore', 'import', 'declare', 'override', 'extend', 'include', '_NL'],
111112

112113
'rule': ['RULE template_params _COLON expansions _NL',
113114
'RULE template_params _DOT NUMBER _COLON expansions _NL'],
@@ -164,6 +165,7 @@
164165
'import': ['_IMPORT _import_path _NL',
165166
'_IMPORT _import_path _LPAR name_list _RPAR _NL',
166167
'_IMPORT _import_path _TO name _NL'],
168+
'include': ['_INCLUDE _import_path _NL'],
167169

168170
'_import_path': ['import_lib', 'import_rel'],
169171
'import_lib': ['_import_args'],
@@ -811,7 +813,7 @@ def __init__(self, pkg_name: str, search_paths: Tuple[str, ...]=("", )) -> None:
811813
def __repr__(self):
812814
return "%s(%r, %r)" % (type(self).__name__, self.pkg_name, self.search_paths)
813815

814-
def __call__(self, base_path: Union[None, str, PackageResource], grammar_path: str, used_files: Dict[str, tuple[str, str]]=None) -> Tuple[PackageResource, str]:
816+
def __call__(self, base_path: Union[None, str, PackageResource], grammar_path: str, used_files: Dict[str, Tuple[str, str]]=None) -> Tuple[PackageResource, str]:
815817
if base_path is None:
816818
to_try = self.search_paths
817819
else:
@@ -1137,7 +1139,10 @@ def _unpack_import(self, stmt, grammar_name):
11371139
path_node, = stmt.children
11381140
arg1 = None
11391141

1140-
if isinstance(arg1, Tree): # Multi import
1142+
if stmt.data == "include": # we only want the dotted_path, no aliases
1143+
dotted_path = tuple(path_node.children)
1144+
aliases = None
1145+
elif isinstance(arg1, Tree): # Multi import
11411146
dotted_path = tuple(path_node.children)
11421147
names = arg1.children
11431148
aliases = dict(zip(names, names)) # Can't have aliased multi import, so all aliases will be the same as names
@@ -1185,50 +1190,66 @@ def _unpack_definition(self, tree, mangle):
11851190
exp = _mangle_exp(exp, mangle)
11861191
return name, exp, params, opts
11871192

1188-
1189-
def load_grammar(self, grammar_text: str, grammar_name: str="<?>", mangle: Optional[Callable[[str], str]]=None) -> None:
1193+
def _get_parsed(self, grammar_text, grammar_name):
11901194
if grammar_text not in self.cached_grammars:
11911195
tree = _parse_grammar(grammar_text, grammar_name)
11921196
self.cached_grammars[grammar_text] = tree
1193-
tree = nr_deepcopy_tree(self.cached_grammars[grammar_text])
1197+
return nr_deepcopy_tree(self.cached_grammars[grammar_text])
1198+
1199+
def load_grammar(self, grammar_text, grammar_name="<?>", mangle=None):
1200+
tree = self._get_parsed(grammar_text, grammar_name)
11941201

11951202
imports = {}
1196-
for stmt in tree.children:
1197-
if stmt.data == 'import':
1198-
dotted_path, base_path, aliases = self._unpack_import(stmt, grammar_name)
1199-
try:
1200-
import_base_path, import_aliases = imports[dotted_path]
1201-
assert base_path == import_base_path, 'Inconsistent base_path for %s.' % '.'.join(dotted_path)
1202-
import_aliases.update(aliases)
1203-
except KeyError:
1204-
imports[dotted_path] = base_path, aliases
1203+
todo = [tree.children]
1204+
after_import_todo = []
1205+
while todo:
1206+
stmts = todo.pop(0)
1207+
after_import_todo.append(stmts)
1208+
for stmt in stmts:
1209+
if stmt.data == 'import':
1210+
dotted_path, base_path, aliases = self._unpack_import(stmt, grammar_name)
1211+
try:
1212+
import_base_path, import_aliases = imports[dotted_path]
1213+
assert base_path == import_base_path, 'Inconsistent base_path for %s.' % '.'.join(dotted_path)
1214+
import_aliases.update(aliases)
1215+
except KeyError:
1216+
imports[dotted_path] = base_path, aliases
1217+
elif stmt.data == 'include':
1218+
dotted_path, base_path, aliases = self._unpack_import(stmt, grammar_name)
1219+
assert aliases is None, "For some reason '_unpack_import' returned aliases for %include"
1220+
text, name = self.resolve_import(dotted_path, base_path)
1221+
new_tree = self._get_parsed(text, name)
1222+
todo.append(new_tree.children)
1223+
12051224

12061225
for dotted_path, (base_path, aliases) in imports.items():
12071226
self.do_import(dotted_path, base_path, aliases, mangle)
12081227

1209-
for stmt in tree.children:
1210-
if stmt.data in ('term', 'rule'):
1211-
self._define(*self._unpack_definition(stmt, mangle))
1212-
elif stmt.data == 'override':
1213-
r ,= stmt.children
1214-
self._define(*self._unpack_definition(r, mangle), override=True)
1215-
elif stmt.data == 'extend':
1216-
r ,= stmt.children
1217-
self._extend(*self._unpack_definition(r, mangle))
1218-
elif stmt.data == 'ignore':
1219-
# if mangle is not None, we shouldn't apply ignore, since we aren't in a toplevel grammar
1220-
if mangle is None:
1221-
self._ignore(*stmt.children)
1222-
elif stmt.data == 'declare':
1223-
names = [t.value for t in stmt.children]
1224-
if mangle is None:
1225-
self._declare(*names)
1228+
while after_import_todo:
1229+
stmts = after_import_todo.pop(0)
1230+
for stmt in stmts:
1231+
if stmt.data in ('term', 'rule'):
1232+
self._define(*self._unpack_definition(stmt, mangle))
1233+
elif stmt.data == 'override':
1234+
r ,= stmt.children
1235+
self._define(*self._unpack_definition(r, mangle), override=True)
1236+
elif stmt.data == 'extend':
1237+
r ,= stmt.children
1238+
self._extend(*self._unpack_definition(r, mangle))
1239+
elif stmt.data == 'ignore':
1240+
# if mangle is not None, we shouldn't apply ignore, since we aren't in a toplevel grammar
1241+
if mangle is None:
1242+
self._ignore(*stmt.children)
1243+
elif stmt.data == 'declare':
1244+
names = [t.value for t in stmt.children]
1245+
if mangle is None:
1246+
self._declare(*names)
1247+
else:
1248+
self._declare(*map(mangle, names))
1249+
elif stmt.data in ('import', 'include'): # These have already been processed
1250+
pass
12261251
else:
1227-
self._declare(*map(mangle, names))
1228-
elif stmt.data == 'import':
1229-
pass
1230-
else:
1231-
assert False, stmt
1252+
assert False, stmt
12321253

12331254

12341255
term_defs = { name: exp
@@ -1252,9 +1273,8 @@ def rule_dependencies(symbol):
12521273
self._definitions = {k: v for k, v in self._definitions.items() if k in _used}
12531274

12541275

1255-
def do_import(self, dotted_path: Tuple[str, ...], base_path: Optional[str], aliases: Dict[str, str], base_mangle: Optional[Callable[[str], str]]=None) -> None:
1276+
def resolve_import(self, dotted_path: Tuple[str, ...], base_path: Optional[str]) -> Tuple[str, Union[str, PackageResource]]:
12561277
assert dotted_path
1257-
mangle = _get_mangle('__'.join(dotted_path), aliases, base_mangle)
12581278
grammar_path = os.path.join(*dotted_path) + EXT
12591279
to_try = self.import_paths + ([base_path] if base_path is not None else []) + [stdlib_loader]
12601280
for source in to_try:
@@ -1274,21 +1294,26 @@ def do_import(self, dotted_path: Tuple[str, ...], base_path: Optional[str], alia
12741294
h = hashlib.md5(text.encode('utf8')).hexdigest()
12751295
if self.used_files.setdefault(joined_path, (h,text))[0] != h:
12761296
raise RuntimeError("Grammar file was changed during importing")
1277-
1278-
gb = GrammarBuilder(self.global_keep_all_tokens, self.import_paths, self.used_files, self.cached_grammars)
1279-
gb.load_grammar(text, joined_path, mangle)
1280-
gb._remove_unused(map(mangle, aliases))
1281-
for name in gb._definitions:
1282-
if name in self._definitions:
1283-
raise GrammarError("Cannot import '%s' from '%s': Symbol already defined." % (name, grammar_path))
1284-
1285-
self._definitions.update(**gb._definitions)
1286-
break
1297+
return text, joined_path
12871298
else:
12881299
# Search failed. Make Python throw a nice error.
12891300
open(grammar_path, encoding='utf8')
1290-
assert False, "Couldn't import grammar %s, but a corresponding file was found at a place where lark doesn't search for it" % (dotted_path,)
1301+
raise GrammarError("Couldn't import grammar %s, but a corresponding file was found at a place where lark"
1302+
" doesn't search for it. This probably means that you wrongly used non-relative imports."
1303+
" Try adding a `.` at the beginning of the path" % (dotted_path,))
1304+
1305+
def do_import(self, dotted_path: Tuple[str, ...], base_path: Optional[str], aliases: Dict[str, str], base_mangle: Optional[Callable[[str], str]]=None):
1306+
mangle = _get_mangle('__'.join(dotted_path), aliases, base_mangle)
1307+
text, joined_path = self.resolve_import(dotted_path, base_path)
1308+
1309+
gb = GrammarBuilder(self.global_keep_all_tokens, self.import_paths, self.used_files)
1310+
gb.load_grammar(text, joined_path, mangle)
1311+
gb._remove_unused(map(mangle, aliases))
1312+
for name in gb._definitions:
1313+
if name in self._definitions:
1314+
raise GrammarError("Cannot import '%s' from '%s': Symbol already defined." % (name, joined_path))
12911315

1316+
self._definitions.update(**gb._definitions)
12921317

12931318
def validate(self) -> None:
12941319
for name, (params, exp, _options) in self._definitions.items():

0 commit comments

Comments
 (0)