textmate-grammar-python 0.1.2__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ __version__ = "0.3.0"
@@ -1,19 +1,147 @@
1
- from typing import TYPE_CHECKING
2
- from pprint import pprint
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC
3
4
  from collections import defaultdict
4
5
  from itertools import groupby
6
+ from pprint import pprint
7
+ from typing import TYPE_CHECKING, Generator
5
8
 
6
- from .handler import POS
9
+ from .utils.handler import POS, ContentHandler, Match, Pattern
10
+ from .utils.logger import LOGGER
7
11
 
8
12
  if TYPE_CHECKING:
9
- from .parser import Capture
13
+ from .parser import GrammarParser
10
14
 
11
15
 
12
16
  TOKEN_DICT = dict[POS, list[str]]
13
17
 
14
18
 
15
- class ContentElement(object):
16
- """The base grammar element object."""
19
+ class Capture:
20
+ """A captured matching group.
21
+
22
+ After mathing, any pattern can have a number of capture groups for which subsequent parsers can be defined.
23
+ The Capture object stores this subsequent parse to be dispatched at a later moment.
24
+ """
25
+
26
+ def __init__(
27
+ self,
28
+ handler: ContentHandler,
29
+ pattern: Pattern,
30
+ matching: Match,
31
+ parsers: dict[int, GrammarParser],
32
+ starting: tuple[int, int],
33
+ boundary: tuple[int, int],
34
+ key: str = "",
35
+ **kwargs,
36
+ ):
37
+ """
38
+ Initialize a new instance of the Element class.
39
+
40
+ :param handler: The content handler for the element.
41
+ :param pattern: The pattern used for matching.
42
+ :param matching: The match object.
43
+ :param parsers: A dictionary of grammar parsers.
44
+ :param starting: The starting position of the element.
45
+ :param boundary: The boundary position of the element.
46
+ :param key: The key for the element. Defaults to "".
47
+ :param **kwargs: Additional keyword arguments.
48
+ :returns: None
49
+ """
50
+ self.handler = handler
51
+ self.pattern = pattern
52
+ self.matching = matching
53
+ self.parsers = parsers
54
+ self.starting = starting
55
+ self.boundary = boundary
56
+ self.key = key
57
+ self.kwargs = kwargs
58
+
59
+ def __eq__(self, other: object) -> bool:
60
+ if isinstance(other, Capture):
61
+ return bool(
62
+ self.key == other.key
63
+ and self.starting == other.starting
64
+ and self.matching.group() == other.matching.group()
65
+ )
66
+ else:
67
+ return False
68
+
69
+ def __repr__(self) -> str:
70
+ return f"@capture<{self.key}>"
71
+
72
+ def dispatch(self) -> list[Capture | ContentElement]:
73
+ """Dispatches the remaining parse of the capture group.
74
+
75
+ This method iterates over the defined parsers for the capture group and dispatches the remaining parse
76
+ based on the captured elements. It returns a list of captured elements or captures.
77
+
78
+ :return: A list of Capture or ContentElement objects representing the parsed elements.
79
+ """
80
+ elements = []
81
+ for group_id, parser in self.parsers.items():
82
+ if group_id > self.pattern.number_of_captures():
83
+ LOGGER.warning(
84
+ f"The capture group {group_id} does not exist in pattern {self.pattern._pattern}"
85
+ )
86
+ continue
87
+
88
+ group_span = self.matching.span(group_id)
89
+
90
+ # Empty group
91
+ if group_span[0] == group_span[1]:
92
+ continue
93
+
94
+ group_starting = (self.starting[0], group_span[0])
95
+ group_boundary = (self.starting[0], group_span[1])
96
+
97
+ if (
98
+ parser == self
99
+ and group_starting == self.starting
100
+ and group_boundary == self.boundary
101
+ ):
102
+ LOGGER.warning("Parser loop detected, continuing...", self, self.starting)
103
+ continue
104
+
105
+ # Dispatch the parse
106
+ self.kwargs.pop("greedy", None)
107
+ parsed, captured_elements, _ = parser._parse(
108
+ self.handler,
109
+ starting=group_starting,
110
+ boundary=group_boundary,
111
+ find_one=self.kwargs.pop("find_one", False),
112
+ parent_capture=self,
113
+ **self.kwargs,
114
+ )
115
+
116
+ if parsed:
117
+ elements.extend(captured_elements)
118
+
119
+ return elements
120
+
121
+
122
+ def _dispatch_list(
123
+ pending_elements: list[Capture | ContentElement], parent: ContentElement | None = None
124
+ ) -> list[ContentElement]:
125
+ """Dispatches all captured parsers in the list."""
126
+ elements = []
127
+ for item in pending_elements:
128
+ if isinstance(item, Capture):
129
+ captured_elements: list[ContentElement] = _dispatch_list(item.dispatch())
130
+ elements.extend(captured_elements)
131
+ elif item != parent:
132
+ elements.append(item)
133
+ return elements
134
+
135
+
136
+ def _str_to_list(input: str | list[str]) -> list[str]:
137
+ if isinstance(input, str):
138
+ return [input] if input else []
139
+ else:
140
+ return input
141
+
142
+
143
+ class ContentElement:
144
+ """The parsed grammar element."""
17
145
 
18
146
  def __init__(
19
147
  self,
@@ -21,35 +149,219 @@ class ContentElement(object):
21
149
  grammar: dict,
22
150
  content: str,
23
151
  characters: dict[POS, str],
24
- children: "list[ContentElement | Capture]" = [],
152
+ children: list[Capture | ContentElement] | None = None,
25
153
  ) -> None:
154
+ """
155
+ Initialize a new instance of the Element class.
156
+
157
+ :param token: The token associated with the element.
158
+ :param grammar: The grammar associated with the element.
159
+ :param content: The content associated with the element.
160
+ :param characters: The characters associated with the element.
161
+ :param children: The children associated with the element. Defaults to None.
162
+ """
163
+ if children is None:
164
+ children = []
26
165
  self.token = token
27
166
  self.grammar = grammar
28
167
  self.content = content
29
168
  self.characters = characters
30
- self.children = children
169
+ self._children_captures = children
170
+ self._dispatched: bool = False
171
+
172
+ @property
173
+ def _subelements(self) -> list[ContentElement]:
174
+ return self.children
175
+
176
+ @property
177
+ def children(self) -> list[ContentElement]:
178
+ """
179
+ Returns a list of children elements.
180
+
181
+ If the elements have not been dispatched yet, this method will dispatch them before returning.
182
+
183
+ :return: A list of ContentElement objects representing the children elements.
184
+ """
185
+ if not self._dispatched:
186
+ self._dispatch()
187
+ return self._children
188
+
189
+ def _dispatch(self, nested: bool = False):
190
+ """
191
+ Dispatches the content element and its children.
192
+
193
+ :param nested: Indicates whether the dispatch is nested within another dispatch.
194
+ :type nested: bool
195
+ :return: None
196
+ """
197
+ if self._dispatched:
198
+ return
199
+ self._dispatched = True
200
+ self._children: list[ContentElement] = _dispatch_list(self._children_captures, parent=self)
201
+ self._children_captures = []
202
+ if nested:
203
+ for child in self._children:
204
+ child._dispatch(True)
31
205
 
32
206
  def __eq__(self, other):
33
- if self.grammar == other.grammar and self.characters == other.characters:
34
- return True
35
- else:
207
+ if not isinstance(other, ContentElement):
36
208
  return False
209
+ return bool(self.grammar == other.grammar and self.characters == other.characters)
210
+
211
+ def _find(
212
+ self,
213
+ tokens: str | list[str],
214
+ start_tokens: str | list[str] = "",
215
+ hide_tokens: str | list[str] = "",
216
+ stop_tokens: str | list[str] = "",
217
+ depth: int = -1,
218
+ attribute: str = "_subelements",
219
+ stack: list[str] | None = None,
220
+ ) -> Generator[tuple[ContentElement, list[str]], None, None]:
221
+ tokens = _str_to_list(tokens)
222
+ start_tokens = _str_to_list(start_tokens)
223
+ hide_tokens = _str_to_list(hide_tokens)
224
+ stop_tokens = _str_to_list(stop_tokens)
225
+
226
+ if not set(tokens).isdisjoint(set(stop_tokens)):
227
+ raise ValueError("Input tokens and stop_tokens must be disjoint")
228
+
229
+ if stack is None:
230
+ stack = []
231
+ stack += [self.token]
37
232
 
38
- def to_dict(self, verbosity: int = -1, all_content: bool = False, **kwargs) -> dict:
39
- "Converts the object to dictionary."
233
+ start_found = not start_tokens
234
+
235
+ if depth:
236
+ depth -= 1
237
+ children: list[ContentElement] = getattr(self, attribute, self._subelements)
238
+ for child in children:
239
+ if stop_tokens and (
240
+ child.token in stop_tokens
241
+ or (stop_tokens == ["*"] and child.token not in tokens)
242
+ ):
243
+ return None
244
+
245
+ if not start_found and child.token in start_tokens:
246
+ start_found = True
247
+ start_tokens = []
248
+
249
+ if (
250
+ start_found
251
+ and (child.token in tokens or tokens == ["*"])
252
+ and child.token not in hide_tokens
253
+ ):
254
+ yield child, [e for e in stack]
255
+ if depth:
256
+ nested_generator = child._find(
257
+ tokens,
258
+ start_tokens=start_tokens,
259
+ hide_tokens=hide_tokens,
260
+ stop_tokens=stop_tokens,
261
+ depth=depth - 1,
262
+ stack=[e for e in stack],
263
+ )
264
+ yield from nested_generator
265
+ return None
266
+
267
+ def find(
268
+ self,
269
+ tokens: str | list[str],
270
+ start_tokens: str | list[str] = "",
271
+ hide_tokens: str | list[str] = "",
272
+ stop_tokens: str | list[str] = "",
273
+ depth: int = -1,
274
+ attribute: str = "_subelements",
275
+ ) -> Generator[tuple[ContentElement, list[str]], None, None]:
276
+ """
277
+ Find content elements based on the given criteria.
278
+
279
+ The find method will return a generator that globs though the element-tree, searching for the next
280
+ subelement that matches the given token.
281
+
282
+ :param tokens: The tokens to search for. Can be a single token or a list of tokens.
283
+ :param start_tokens: The tokens that mark the start of the search. Can be a single token or a list of tokens.
284
+ :param hide_tokens: The tokens to hide from the search results. Can be a single token or a list of tokens.
285
+ :param stop_tokens: The tokens that mark the end of the search. Can be a single token or a list of tokens.
286
+ :param depth: The maximum depth to search. Defaults to -1 (unlimited depth).
287
+ :param attribute: The attribute name to access the subelements. Defaults to "_subelements".
288
+
289
+ :yield: A tuple containing the found content element and the stack of tokens encountered.
290
+
291
+ :raises ValueError: If the input tokens and stop_tokens are not disjoint.
292
+
293
+ :return: None if no matching content elements are found.
294
+ """
295
+ return self._find(
296
+ tokens,
297
+ start_tokens=start_tokens,
298
+ hide_tokens=hide_tokens,
299
+ stop_tokens=stop_tokens,
300
+ depth=depth,
301
+ attribute=attribute,
302
+ )
303
+
304
+ def findall(
305
+ self,
306
+ tokens: str | list[str],
307
+ start_tokens: str | list[str] = "",
308
+ hide_tokens: str | list[str] = "",
309
+ stop_tokens: str | list[str] = "",
310
+ depth: int = -1,
311
+ attribute: str = "_subelements",
312
+ ) -> list[tuple[ContentElement, list[str]]]:
313
+ """
314
+ Find all occurrences of the specified tokens within the content element.
315
+
316
+ :param tokens: The tokens to search for.
317
+ :param start_tokens: The tokens that must appear before the found tokens. Defaults to "".
318
+ :param hide_tokens: The tokens that should be hidden from the search. Defaults to "".
319
+ :param stop_tokens: The tokens that, if found, should stop the search. Defaults to "".
320
+ :param depth: The maximum depth to search. Defaults to -1 (unlimited depth).
321
+ :param attribute: The attribute to search within. Defaults to "_subelements".
322
+
323
+ :return: A list of tuples containing the content element and the found tokens.
324
+ """
325
+ return list(
326
+ self._find(
327
+ tokens,
328
+ start_tokens=start_tokens,
329
+ hide_tokens=hide_tokens,
330
+ stop_tokens=stop_tokens,
331
+ depth=depth,
332
+ attribute=attribute,
333
+ )
334
+ )
335
+
336
+ def to_dict(self, depth: int = -1, all_content: bool = False, **kwargs) -> dict:
337
+ """
338
+ Converts the object to a dictionary.
339
+
340
+ :param depth: The depth of the conversion. Defaults to -1.
341
+ :param all_content: Whether to include all content or only the top-level content. Defaults to False.
342
+
343
+ :return: The converted dictionary representation of the object.
344
+ """
40
345
  out_dict = {"token": self.token}
41
346
  if all_content or not self.children:
42
347
  out_dict["content"] = self.content
43
348
  if self.children:
44
349
  out_dict["children"] = (
45
- self._list_property_to_dict("children", verbosity=verbosity - 1, all_content=all_content)
46
- if verbosity
350
+ self._list_property_to_dict("children", depth=depth - 1, all_content=all_content)
351
+ if depth
47
352
  else self.children
48
353
  )
49
354
  return out_dict
50
355
 
51
356
  def flatten(self) -> list[tuple[tuple[int, int], str, list[str]]]:
52
- """Converts the object to a flattened array of tokens per index."""
357
+ """
358
+ Converts the object to a flattened array of tokens per index, similarly to vscode-textmate.
359
+
360
+ :return: A list of tuples representing the flattened tokens. Each tuple contains:
361
+ - A tuple representing the starting and ending index of the token.
362
+ - The content of the token.
363
+ - A list of keys associated with the token.
364
+ """
53
365
  token_dict = self._token_by_index(defaultdict(list))
54
366
  tokens = []
55
367
  for (_, key), group in groupby(sorted(token_dict.items()), lambda x: (x[0][0], x[1])):
@@ -59,26 +371,55 @@ class ContentElement(object):
59
371
  for pos, _ in group_tokens:
60
372
  content += self.characters[pos]
61
373
  if content:
62
- tokens.append([starting, content, key])
374
+ tokens.append((starting, content, key))
63
375
  return tokens
64
376
 
65
- def print(self, flatten: bool = False, verbosity: int = -1, all_content: bool = False, **kwargs) -> None:
66
- """Prints the current object recursively by converting to dictionary."""
377
+ def print(
378
+ self,
379
+ flatten: bool = False,
380
+ depth: int = -1,
381
+ all_content: bool = False,
382
+ **kwargs,
383
+ ) -> None:
384
+ """
385
+ Prints the current object recursively by converting it to a dictionary or a flattened array.
386
+
387
+ :param flatten: If True, flattens the object before printing. Defaults to False.
388
+ :param depth: The maximum depth to print. Defaults to -1 (unlimited depth).
389
+ :param all_content: If True, includes all content in the printout. Defaults to False.
390
+ :param **kwargs: Additional keyword arguments to be passed to the pprint function.
391
+
392
+ :return: None
393
+ """
67
394
  if flatten:
68
- output = self.flatten(**kwargs)
395
+ pprint(
396
+ self.flatten(**kwargs),
397
+ sort_dicts=False,
398
+ width=kwargs.pop("width", 150),
399
+ **kwargs,
400
+ )
69
401
  else:
70
- output = self.to_dict(verbosity=verbosity, all_content=all_content, **kwargs)
402
+ pprint(
403
+ self.to_dict(depth=depth, all_content=all_content, **kwargs),
404
+ sort_dicts=False,
405
+ width=kwargs.pop("width", 150),
406
+ **kwargs,
407
+ )
71
408
 
72
- pprint(
73
- output,
74
- sort_dicts=False,
75
- width=kwargs.pop("width", 150),
76
- **kwargs,
77
- )
409
+ def _token_by_index(self, token_dict: TOKEN_DICT | None = None) -> TOKEN_DICT:
410
+ """Recursively tokenize every index between start and close.
78
411
 
79
- def _token_by_index(self, token_dict: TOKEN_DICT = defaultdict(list)) -> TOKEN_DICT:
80
- """Recursively tokenize every index between start and close."""
81
- for pos in self.characters.keys():
412
+ This method recursively tokenizes every index between the start and close positions of the element.
413
+ It populates a dictionary, `token_dict`, with the tokens corresponding to each index.
414
+
415
+ :param token_dict: A dictionary to store the tokens. If None, a new dictionary is created.
416
+ :type token_dict: dict | None
417
+ :return: A dictionary containing the tokens for each index.
418
+ :rtype: dict
419
+ """
420
+ if token_dict is None:
421
+ token_dict = defaultdict(list)
422
+ for pos in self.characters:
82
423
  token_dict[pos].append(self.token)
83
424
 
84
425
  # Tokenize child elements
@@ -89,7 +430,8 @@ class ContentElement(object):
89
430
  def _list_property_to_dict(self, prop: str, **kwargs):
90
431
  """Makes a dictionary from a property."""
91
432
  return [
92
- item.to_dict(**kwargs) if isinstance(item, ContentElement) else item for item in getattr(self, prop, [])
433
+ item.to_dict(**kwargs) if isinstance(item, ContentElement) else item
434
+ for item in getattr(self, prop, [])
93
435
  ]
94
436
 
95
437
  def __repr__(self) -> str:
@@ -101,29 +443,104 @@ class ContentBlockElement(ContentElement):
101
443
  """A parsed element with a begin and a end"""
102
444
 
103
445
  def __init__(
104
- self, begin: "list[ContentElement | Capture]" = [], end: "list[ContentElement | Capture]" = [], **kwargs
446
+ self,
447
+ *args,
448
+ begin: list[Capture | ContentElement] | None = None,
449
+ end: list[Capture | ContentElement] | None = None,
450
+ **kwargs,
105
451
  ) -> None:
106
- super().__init__(**kwargs)
107
- self.begin = begin
108
- self.end = end
452
+ """
453
+ Initialize a new instance of the Element class.
454
+
455
+ :param begin: A list of Capture or ContentElement objects representing the beginning captures of the element. Defaults to None.
456
+ :param end: A list of Capture or ContentElement objects representing the ending captures of the element. Defaults to None.
457
+ :param **kwargs: Additional keyword arguments to be passed to the parent class constructor.
458
+
459
+ :return: None
460
+ """
461
+ if end is None:
462
+ end = []
463
+ if begin is None:
464
+ begin = []
465
+ super().__init__(*args, **kwargs)
466
+ self._begin_captures = begin
467
+ self._end_captures = end
468
+
469
+ @property
470
+ def _subelements(self) -> list[ContentElement]:
471
+ return self.begin + self.children + self.end
109
472
 
110
- def to_dict(self, verbosity: int = -1, **kwargs) -> dict:
111
- out_dict = super().to_dict(verbosity=verbosity, **kwargs)
473
+ @property
474
+ def begin(self) -> list[ContentElement]:
475
+ """
476
+ Returns the list of begin elements.
477
+
478
+ If the elements have not been dispatched yet, this method will dispatch them before returning.
479
+
480
+ :return: The list of begin elements.
481
+ """
482
+ if not self._dispatched:
483
+ self._dispatch()
484
+ return self._begin
485
+
486
+ @property
487
+ def end(self) -> list[ContentElement]:
488
+ """
489
+ Returns the end elements.
490
+
491
+ If the elements have not been dispatched yet, this method will dispatch them before returning.
492
+
493
+ :return: A list of end elements.
494
+ """
495
+ if not self._dispatched:
496
+ self._dispatch()
497
+ return self._end
498
+
499
+ def _dispatch(self, nested: bool = False):
500
+ if self._dispatched:
501
+ return
502
+ super()._dispatch(nested)
503
+ self._begin: list[ContentElement] = _dispatch_list(self._begin_captures, parent=self)
504
+ self._end: list[ContentElement] = _dispatch_list(self._end_captures, parent=self)
505
+ self._begin_captures, self._end_captures = [], []
506
+ if nested:
507
+ for item in self._begin:
508
+ item._dispatch(True)
509
+ for item in self._end:
510
+ item._dispatch(True)
511
+
512
+ def to_dict(self, depth: int = -1, all_content: bool = False, **kwargs) -> dict:
513
+ """
514
+ Converts the element to a dictionary representation.
515
+
516
+ :param depth: The depth of the conversion. Defaults to -1.
517
+ :param all_content: Whether to include all content. Defaults to False.
518
+ :param **kwargs: Additional keyword arguments.
519
+
520
+ :return: The dictionary representation of the element.
521
+ """
522
+ out_dict = super().to_dict(depth=depth, all_content=all_content, **kwargs)
112
523
  if self.begin:
113
524
  out_dict["begin"] = (
114
- self._list_property_to_dict("begin", verbosity=verbosity - 1, **kwargs) if verbosity else self.begin
525
+ self._list_property_to_dict("begin", depth=depth - 1, **kwargs)
526
+ if depth
527
+ else self.begin
115
528
  )
116
529
  if self.end:
117
530
  out_dict["end"] = (
118
- self._list_property_to_dict("end", verbosity=verbosity - 1, **kwargs) if verbosity else self.end
531
+ self._list_property_to_dict("end", depth=depth - 1, **kwargs) if depth else self.end
119
532
  )
120
533
 
121
- ordered_keys = [key for key in ["token", "begin", "end", "content", "children"] if key in out_dict]
534
+ ordered_keys = [
535
+ key for key in ["token", "begin", "end", "content", "children"] if key in out_dict
536
+ ]
122
537
  ordered_dict = {key: out_dict[key] for key in ordered_keys}
123
538
  return ordered_dict
124
539
 
125
- def _token_by_index(self, token_dict: TOKEN_DICT = defaultdict(list)) -> TOKEN_DICT:
540
+ def _token_by_index(self, token_dict: TOKEN_DICT | None = None) -> TOKEN_DICT:
126
541
  """Converts the object to a flattened array of tokens."""
542
+ if token_dict is None:
543
+ token_dict = defaultdict(list)
127
544
  super()._token_by_index(token_dict)
128
545
  for element in self.begin:
129
546
  element._token_by_index(token_dict)
@@ -1,16 +1,19 @@
1
- from pathlib import Path
2
1
  import shutil
3
- import yaml
2
+ from pathlib import Path
4
3
 
4
+ import yaml
5
5
 
6
- tmLanguageFile = Path(__file__).parents[3] / "syntaxes" / "markdown" / "markdown.tmLanguage.base.yaml"
6
+ tmLanguageFile = (
7
+ Path(__file__).parents[3] / "syntaxes" / "markdown" / "markdown.tmLanguage.base.yaml"
8
+ )
7
9
  tmLanguageYAML = Path(__file__).parent / "grammar.yaml"
8
10
 
9
11
 
10
12
  if tmLanguageFile.exists():
11
- shutil.copyfile(tmLanguageFile, tmLanguageYAML)
12
-
13
- with open(tmLanguageYAML, "r") as file:
14
- GRAMMAR = yaml.load(file.read())
15
-
13
+ shutil.copyfile(tmLanguageFile, tmLanguageYAML)
16
14
 
15
+ with open(tmLanguageYAML) as file:
16
+ try:
17
+ GRAMMAR = yaml.load(file.read(), Loader=yaml.CLoader)
18
+ except ImportError:
19
+ GRAMMAR = yaml.load(file.read(), Loader=yaml.Loader)
@@ -1,25 +1,27 @@
1
- from pathlib import Path
2
1
  import plistlib
3
- import yaml
4
-
5
-
6
- try:
7
- from yaml import CLoader as Loader, CDumper as Dumper
8
- except ImportError:
9
- from yaml import Loader, Dumper
2
+ from pathlib import Path
10
3
 
4
+ import yaml
11
5
 
12
- tmLanguageFile = Path(__file__).parents[3] / "syntaxes" / "matlab" / "Matlab.tmbundle" / "Syntaxes" / "MATLAB.tmLanguage"
6
+ tmLanguageFile = (
7
+ Path(__file__).parents[3]
8
+ / "syntaxes"
9
+ / "matlab"
10
+ / "Matlab.tmbundle"
11
+ / "Syntaxes"
12
+ / "MATLAB.tmLanguage"
13
+ )
13
14
  tmLanguageYAML = Path(__file__).parent / "grammar.yaml"
14
15
 
15
16
 
16
17
  if tmLanguageFile.exists():
17
18
  with open(tmLanguageFile, "rb") as file:
18
19
  GRAMMAR = plistlib.load(file, fmt=plistlib.FMT_XML)
19
- with open(tmLanguageYAML, 'w') as f:
20
+ with open(tmLanguageYAML, "w") as f:
20
21
  f.write(yaml.dump(GRAMMAR, indent=2))
21
22
  else:
22
- with open(tmLanguageYAML, "r") as file:
23
- GRAMMAR = yaml.load(file.read(), Loader=Loader)
24
-
25
-
23
+ with open(tmLanguageYAML) as file:
24
+ try:
25
+ GRAMMAR = yaml.load(file.read(), Loader=yaml.CLoader)
26
+ except ImportError:
27
+ GRAMMAR = yaml.load(file.read(), Loader=yaml.Loader)