dbrownell-parserlib 0.8.1__tar.gz → 0.8.2__tar.gz

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.
Files changed (16) hide show
  1. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/PKG-INFO +1 -1
  2. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/pyproject.toml +1 -1
  3. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/src/dbrownell_ParserLib/antlr/antlr_parser.py +1 -1
  4. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/src/dbrownell_ParserLib/antlr/antlr_visitor_mixins.py +57 -72
  5. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/README.md +0 -0
  6. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/src/dbrownell_ParserLib/__init__.py +0 -0
  7. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/src/dbrownell_ParserLib/antlr/__init__.py +0 -0
  8. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/src/dbrownell_ParserLib/antlr/build_antlr_grammar/__init__.py +0 -0
  9. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/src/dbrownell_ParserLib/antlr/build_antlr_grammar/antlr-4.13.2-complete.jar +0 -0
  10. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/src/dbrownell_ParserLib/errors.py +0 -0
  11. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/src/dbrownell_ParserLib/expression.py +0 -0
  12. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/src/dbrownell_ParserLib/location.py +0 -0
  13. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/src/dbrownell_ParserLib/py.typed +0 -0
  14. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/src/dbrownell_ParserLib/region.py +0 -0
  15. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/src/dbrownell_ParserLib/terminal_expression.py +0 -0
  16. {dbrownell_parserlib-0.8.1 → dbrownell_parserlib-0.8.2}/src/dbrownell_ParserLib/visitors.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dbrownell-parserlib
3
- Version: 0.8.1
3
+ Version: 0.8.2
4
4
  Summary: Functionality useful when creating parsers.
5
5
  Author: Dave Brownell
6
6
  Author-email: Dave Brownell <github@DavidBrownell.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dbrownell-parserlib"
3
- version = "0.8.1"
3
+ version = "0.8.2"
4
4
  # ^^^^^
5
5
  # Wheel names will be generated according to this value. Do not manually modify this value; instead
6
6
  # update it according to committed changes by running this command from the root of the repository:
@@ -485,7 +485,7 @@ class _ErrorListener(antlr4.DiagnosticErrorListener):
485
485
  return
486
486
 
487
487
  location = Location(line, column + 1)
488
- error = Error(msg, Region(self._source, location, location))
488
+ error = Error(f"Syntax error: {msg}", Region(self._source, location, location))
489
489
  ex = AntlrParserException(error)
490
490
 
491
491
  if e is None:
@@ -1,5 +1,7 @@
1
1
  """Various ANTLR visitor mixins that can be used to add functionality to the generated visitor classes."""
2
2
 
3
+ import re
4
+
3
5
  from abc import ABC, abstractmethod
4
6
  from typing import cast, override, TYPE_CHECKING
5
7
 
@@ -35,14 +37,36 @@ class AntlrVisitorMixinBase(ABC):
35
37
  self._current_line: int = 0
36
38
 
37
39
  # ----------------------------------------------------------------------
38
- @abstractmethod
39
40
  def CreateRegion(
40
41
  self,
41
- ctx: antlr4.ParserRuleContext,
42
+ ctx: antlr4.TerminalNode | antlr4.ParserRuleContext,
42
43
  ) -> Region:
43
44
  """Create a `Region` from the given context."""
44
45
 
45
- raise NotImplementedError() # pragma: no cover
46
+ if isinstance(ctx, antlr4.TerminalNode):
47
+ token = cast(antlr4.Token, ctx.symbol) # ty: ignore[unresolved-attribute]
48
+
49
+ assert token.column is not None
50
+
51
+ start_line = token.line
52
+ start_col = token.column + 1
53
+ end_line = token.line
54
+ end_col = token.column + 1 + len(token.text)
55
+
56
+ assert start_line is not None
57
+ assert end_line is not None
58
+
59
+ region = Region(
60
+ self.filename,
61
+ Location(start_line, start_col),
62
+ Location(end_line, end_col),
63
+ )
64
+ else:
65
+ region = self._CreateRegionFromRule(ctx)
66
+
67
+ self._OnProgress(region.end.line)
68
+
69
+ return region
46
70
 
47
71
  # ----------------------------------------------------------------------
48
72
  def GetChildren(self, ctx: antlr4.ParserRuleContext) -> list[object]:
@@ -64,49 +88,28 @@ class AntlrVisitorMixinBase(ABC):
64
88
  self._current_line = end_line
65
89
  self._on_progress_func(end_line)
66
90
 
91
+ # ----------------------------------------------------------------------
92
+ @abstractmethod
93
+ def _CreateRegionFromRule(self, ctx: antlr4.ParserRuleContext) -> Region: ...
94
+
67
95
 
68
96
  # ----------------------------------------------------------------------
69
97
  class InsignificantWhitespaceAntlrVisitorMixin(AntlrVisitorMixinBase):
70
98
  """Mixin that adds functionality used for grammars that ignore insignificant whitespace."""
71
99
 
72
100
  # ----------------------------------------------------------------------
73
- @override
74
- def CreateRegion(
75
- self,
76
- ctx: antlr4.ParserRuleContext | antlr4.TerminalNode,
77
- ) -> Region:
78
- """Create a `Region` from the given context."""
79
-
80
- if isinstance(ctx, antlr4.ParserRuleContext):
81
- start_token = ctx.start
82
- stop_token = ctx.stop
83
-
84
- if start_token is None or stop_token is None:
85
- msg = "Context does not have start or stop token"
86
- raise ValueError(msg)
101
+ def _CreateRegionFromRule(self, ctx: antlr4.ParserRuleContext) -> Region:
102
+ start_token = ctx.start
103
+ stop_token = ctx.stop
87
104
 
88
- start_line = start_token.line
89
- start_col = start_token.column + 1
90
- end_line = stop_token.line
91
- end_col = stop_token.column + 1
92
-
93
- if start_line == end_line:
94
- end_col += len(stop_token.text)
95
- else:
96
- end_col += 1
97
-
98
- elif isinstance(ctx, antlr4.TerminalNode):
99
- token = cast(antlr4.Token, ctx.symbol) # ty: ignore[unresolved-attribute]
100
-
101
- assert token.column is not None
102
-
103
- start_line = token.line
104
- start_col = token.column + 1
105
- end_line = token.line
106
- end_col = token.column + 1 + len(token.text)
105
+ if start_token is None or stop_token is None:
106
+ msg = "Context does not have start or stop token"
107
+ raise ValueError(msg)
107
108
 
108
- else:
109
- assert False, ctx # noqa: B011, PT015 # pragma: no cover
109
+ start_line = start_token.line
110
+ start_col = start_token.column + 1
111
+ end_line = stop_token.line
112
+ end_col = stop_token.column + 1 + len(stop_token.text)
110
113
 
111
114
  assert start_line is not None
112
115
  assert end_line is not None
@@ -133,52 +136,34 @@ class SignificantWhitespaceAntlrVisitorMixin(AntlrVisitorMixinBase):
133
136
  self._newline_token = newline_token
134
137
  self._newline_token_string = newline_token_string
135
138
 
139
+ self._newline_indent_regex = re.compile(r"^\r?\n[ \t]*$")
140
+
136
141
  # ----------------------------------------------------------------------
137
142
  @override
138
- def CreateRegion(
143
+ def _CreateRegionFromRule(
139
144
  self,
140
145
  ctx: antlr4.ParserRuleContext,
141
146
  ) -> Region:
142
147
  assert isinstance(ctx.start, antlr4.Token), ctx.start
143
148
  assert isinstance(ctx.stop, antlr4.Token), ctx.stop
144
149
 
145
- if ctx.stop.type == self._dedent_token:
146
- stop_line = ctx.stop.line
147
- stop_col = ctx.stop.column
148
-
149
- elif ctx.stop.type == self._newline_token and ctx.stop.text == self._newline_token_string:
150
- if ctx.stop.line == ctx.start.line:
151
- # This is the scenario where the statement is followed by a dedent followed by another
152
- # statement. We don't want the range of this item to overlap with the range of the
153
- # next item, so use the values as they are, even though it means that a statement
154
- # that terminates with a newline will not have the newline here.
155
- stop_line = ctx.stop.line
156
- stop_col = ctx.stop.column
157
- else:
158
- stop_line = ctx.stop.line
159
- stop_col = ctx.stop.column if ctx.stop.column == 0 else ctx.start.column
160
-
161
- else:
162
- stop_line = ctx.stop.line
163
-
164
- content = ctx.stop.text
165
- lines = content.split("\n")
166
-
167
- if ctx.stop.type == self._newline_token:
168
- assert content.startswith("\n"), content
169
- assert len(lines) == 2, lines # noqa: PLR2004
170
-
171
- stop_col = len(lines[-1])
172
-
173
- if stop_line == ctx.stop.line:
174
- stop_col += ctx.stop.column
150
+ if ctx.stop.text == self._newline_token_string or self._newline_indent_regex.match(ctx.stop.text):
151
+ assert ctx.stop.line == ctx.start.line or ctx.stop.line - 1 == ctx.start.line, (
152
+ ctx.start.line,
153
+ ctx.stop.line,
154
+ )
175
155
 
176
- stop_line += len(lines) - 1
156
+ stop_line = ctx.start.line
157
+ stop_column = ctx.start.column + 1 + len(ctx.start.text)
177
158
 
178
- self._OnProgress(stop_line)
159
+ return Region(
160
+ self.filename,
161
+ Location(ctx.start.line, ctx.start.column + 1),
162
+ Location(stop_line, stop_column),
163
+ )
179
164
 
180
165
  return Region(
181
166
  self.filename,
182
167
  Location(ctx.start.line, ctx.start.column + 1),
183
- Location(stop_line, stop_col),
168
+ Location(ctx.stop.line, ctx.stop.column + 1),
184
169
  )