dbrownell-parserlib 0.9.0__tar.gz → 0.10.0__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 (19) hide show
  1. {dbrownell_parserlib-0.9.0 → dbrownell_parserlib-0.10.0}/PKG-INFO +8 -10
  2. {dbrownell_parserlib-0.9.0 → dbrownell_parserlib-0.10.0}/README.md +7 -9
  3. {dbrownell_parserlib-0.9.0 → dbrownell_parserlib-0.10.0}/pyproject.toml +1 -1
  4. dbrownell_parserlib-0.10.0/src/dbrownell_ParserLib/__init__.py +6 -0
  5. dbrownell_parserlib-0.10.0/src/dbrownell_ParserLib/antlr/__init__.py +1 -0
  6. dbrownell_parserlib-0.10.0/src/dbrownell_ParserLib/multiline_strings.py +124 -0
  7. dbrownell_parserlib-0.9.0/src/dbrownell_ParserLib/__init__.py +0 -44
  8. dbrownell_parserlib-0.9.0/src/dbrownell_ParserLib/antlr/__init__.py +0 -19
  9. {dbrownell_parserlib-0.9.0 → dbrownell_parserlib-0.10.0}/src/dbrownell_ParserLib/antlr/antlr_parser.py +0 -0
  10. {dbrownell_parserlib-0.9.0 → dbrownell_parserlib-0.10.0}/src/dbrownell_ParserLib/antlr/antlr_visitor_mixins.py +0 -0
  11. {dbrownell_parserlib-0.9.0 → dbrownell_parserlib-0.10.0}/src/dbrownell_ParserLib/antlr/build_antlr_grammar/__init__.py +0 -0
  12. {dbrownell_parserlib-0.9.0 → dbrownell_parserlib-0.10.0}/src/dbrownell_ParserLib/antlr/build_antlr_grammar/antlr-4.13.2-complete.jar +0 -0
  13. {dbrownell_parserlib-0.9.0 → dbrownell_parserlib-0.10.0}/src/dbrownell_ParserLib/errors.py +0 -0
  14. {dbrownell_parserlib-0.9.0 → dbrownell_parserlib-0.10.0}/src/dbrownell_ParserLib/expression.py +0 -0
  15. {dbrownell_parserlib-0.9.0 → dbrownell_parserlib-0.10.0}/src/dbrownell_ParserLib/location.py +0 -0
  16. {dbrownell_parserlib-0.9.0 → dbrownell_parserlib-0.10.0}/src/dbrownell_ParserLib/py.typed +0 -0
  17. {dbrownell_parserlib-0.9.0 → dbrownell_parserlib-0.10.0}/src/dbrownell_ParserLib/region.py +0 -0
  18. {dbrownell_parserlib-0.9.0 → dbrownell_parserlib-0.10.0}/src/dbrownell_ParserLib/terminal_expression.py +0 -0
  19. {dbrownell_parserlib-0.9.0 → dbrownell_parserlib-0.10.0}/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.9.0
3
+ Version: 0.10.0
4
4
  Summary: Functionality useful when creating parsers.
5
5
  Author: Dave Brownell
6
6
  Author-email: Dave Brownell <github@DavidBrownell.com>
@@ -85,7 +85,7 @@ Use `BuildAntlrGrammar` to generate the lexer and parser:
85
85
  ```python
86
86
  from pathlib import Path
87
87
 
88
- from dbrownell_ParserLib import BuildAntlrGrammar
88
+ from dbrownell_ParserLib.antlr.build_antlr_grammar import BuildAntlrGrammar
89
89
 
90
90
 
91
91
  BuildAntlrGrammar(
@@ -101,11 +101,9 @@ Implement a visitor that converts ANTLR parse trees to Expression objects:
101
101
  ```python
102
102
  from dataclasses import dataclass
103
103
 
104
- from dbrownell_ParserLib import (
105
- Expression,
106
- InsignificantWhitespaceAntlrVisitorMixin,
107
- TerminalExpression,
108
- )
104
+ from dbrownell_ParserLib.antlr.antlr_visitor_mixins import InsignificantWhitespaceAntlrVisitorMixin
105
+ from dbrownell_ParserLib.expression import Expression
106
+ from dbrownell_ParserLib.terminal_expression import TerminalExpression
109
107
 
110
108
  from CalculatorVisitor import CalculatorVisitor as GeneratedVisitor
111
109
 
@@ -147,7 +145,7 @@ class CalculatorVisitor(InsignificantWhitespaceAntlrVisitorMixin, GeneratedVisit
147
145
  Use `CreateAntlrParser` to build a callable parser:
148
146
 
149
147
  ```python
150
- from dbrownell_ParserLib import CreateAntlrParser
148
+ from dbrownell_ParserLib.antlr.antlr_parser import CreateAntlrParser
151
149
 
152
150
 
153
151
  parser = CreateAntlrParser(
@@ -162,7 +160,7 @@ parser = CreateAntlrParser(
162
160
  Parse single files, multiple files, or entire workspaces:
163
161
 
164
162
  ```python
165
- from dbrownell_ParserLib import Error
163
+ from dbrownell_ParserLib.errors import Error
166
164
 
167
165
 
168
166
  # Parse a single file
@@ -187,7 +185,7 @@ Use the visitor pattern to process your AST:
187
185
  ```python
188
186
  from contextlib import contextmanager
189
187
 
190
- from dbrownell_ParserLib import ExpressionVisitorHelper, VisitResult
188
+ from dbrownell_ParserLib.visitors import ExpressionVisitorHelper, VisitResult
191
189
 
192
190
 
193
191
  class EvaluatorVisitor(ExpressionVisitorHelper):
@@ -63,7 +63,7 @@ Use `BuildAntlrGrammar` to generate the lexer and parser:
63
63
  ```python
64
64
  from pathlib import Path
65
65
 
66
- from dbrownell_ParserLib import BuildAntlrGrammar
66
+ from dbrownell_ParserLib.antlr.build_antlr_grammar import BuildAntlrGrammar
67
67
 
68
68
 
69
69
  BuildAntlrGrammar(
@@ -79,11 +79,9 @@ Implement a visitor that converts ANTLR parse trees to Expression objects:
79
79
  ```python
80
80
  from dataclasses import dataclass
81
81
 
82
- from dbrownell_ParserLib import (
83
- Expression,
84
- InsignificantWhitespaceAntlrVisitorMixin,
85
- TerminalExpression,
86
- )
82
+ from dbrownell_ParserLib.antlr.antlr_visitor_mixins import InsignificantWhitespaceAntlrVisitorMixin
83
+ from dbrownell_ParserLib.expression import Expression
84
+ from dbrownell_ParserLib.terminal_expression import TerminalExpression
87
85
 
88
86
  from CalculatorVisitor import CalculatorVisitor as GeneratedVisitor
89
87
 
@@ -125,7 +123,7 @@ class CalculatorVisitor(InsignificantWhitespaceAntlrVisitorMixin, GeneratedVisit
125
123
  Use `CreateAntlrParser` to build a callable parser:
126
124
 
127
125
  ```python
128
- from dbrownell_ParserLib import CreateAntlrParser
126
+ from dbrownell_ParserLib.antlr.antlr_parser import CreateAntlrParser
129
127
 
130
128
 
131
129
  parser = CreateAntlrParser(
@@ -140,7 +138,7 @@ parser = CreateAntlrParser(
140
138
  Parse single files, multiple files, or entire workspaces:
141
139
 
142
140
  ```python
143
- from dbrownell_ParserLib import Error
141
+ from dbrownell_ParserLib.errors import Error
144
142
 
145
143
 
146
144
  # Parse a single file
@@ -165,7 +163,7 @@ Use the visitor pattern to process your AST:
165
163
  ```python
166
164
  from contextlib import contextmanager
167
165
 
168
- from dbrownell_ParserLib import ExpressionVisitorHelper, VisitResult
166
+ from dbrownell_ParserLib.visitors import ExpressionVisitorHelper, VisitResult
169
167
 
170
168
 
171
169
  class EvaluatorVisitor(ExpressionVisitorHelper):
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dbrownell-parserlib"
3
- version = "0.9.0"
3
+ version = "0.10.0"
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:
@@ -0,0 +1,6 @@
1
+ # noqa: D104
2
+
3
+ from importlib.metadata import version
4
+
5
+
6
+ __version__ = version("dbrownell_ParserLib")
@@ -0,0 +1,124 @@
1
+ """Functionality used when parsing multiline strings."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from dbrownell_ParserLib.errors import CreateErrorType
6
+ from dbrownell_ParserLib.region import Location, Region
7
+
8
+
9
+ # ----------------------------------------------------------------------
10
+ MisalignedMultilineStringError = CreateErrorType(
11
+ "All lines in a multiline string must be vertically aligned with the opening token."
12
+ )
13
+
14
+
15
+ # ----------------------------------------------------------------------
16
+ def ExtractMultilineString(
17
+ content: str,
18
+ opening_token_region: Region,
19
+ opening_token: str = '"""', # noqa: S107
20
+ closing_token: str = '"""', # noqa: S107
21
+ tab_size: int = 4,
22
+ ) -> str:
23
+ """Extract a multiline string from the given content, ensuring that all lines are vertically aligned with the opening token."""
24
+
25
+ assert content.startswith(opening_token), content
26
+ content = content[len(opening_token) :]
27
+
28
+ assert content.endswith(closing_token), content
29
+ content = content[: -len(closing_token)]
30
+
31
+ lines = content.splitlines()
32
+
33
+ # Handle the special case where the multiline string begins at column 1. In this case, the content
34
+ # should end with a newline.
35
+ if content.endswith("\n") and opening_token_region.begin.column == 1:
36
+ lines.append("")
37
+
38
+ # Ensure that there is a newline immediately following the opening token
39
+ if lines[0]:
40
+ # This means that there was content immediately following the opening token when a newline was expected.
41
+ location = Location(
42
+ opening_token_region.begin.line,
43
+ opening_token_region.begin.column + len(opening_token),
44
+ )
45
+
46
+ raise MisalignedMultilineStringError.CreateAsException(
47
+ Region(opening_token_region.filename, location, location)
48
+ )
49
+
50
+ lines = lines[1:]
51
+ assert lines
52
+
53
+ # Ensure that the closing token is on its own line
54
+ result = _CalcPrefixColumn(lines[-1], tab_size, None)
55
+
56
+ if result.column != opening_token_region.begin.column:
57
+ location = Location(opening_token_region.begin.line + len(lines) + 1, result.column)
58
+
59
+ raise MisalignedMultilineStringError.CreateAsException(
60
+ Region(opening_token_region.filename, location, location)
61
+ )
62
+
63
+ lines = lines[:-1]
64
+
65
+ # Process the content
66
+ acceptable_prefix_column_values = {1, opening_token_region.begin.column}
67
+ escaped_closing_token = "".join(f"\\{c}" for c in closing_token)
68
+
69
+ # Ensure that all lines are vertically aligned with the opening token
70
+ for line_index, line in enumerate(lines):
71
+ result = _CalcPrefixColumn(line, tab_size, opening_token_region.begin.column)
72
+
73
+ if result.column not in acceptable_prefix_column_values:
74
+ location = Location(opening_token_region.begin.line + line_index + 1, result.column)
75
+
76
+ raise MisalignedMultilineStringError.CreateAsException(
77
+ Region(opening_token_region.filename, location, location)
78
+ )
79
+
80
+ normalized_line = line[result.num_chars :]
81
+
82
+ if normalized_line.isspace():
83
+ normalized_line = ""
84
+ else:
85
+ normalized_line = normalized_line.replace(escaped_closing_token, closing_token)
86
+
87
+ lines[line_index] = normalized_line
88
+
89
+ # Handle the special case where the multiline string consists of all newlines
90
+ if lines and all(not line for line in lines):
91
+ return "".join("\n" * len(lines))
92
+
93
+ return "\n".join(lines)
94
+
95
+
96
+ # ----------------------------------------------------------------------
97
+ # ----------------------------------------------------------------------
98
+ # ----------------------------------------------------------------------
99
+ @dataclass(frozen=True)
100
+ class _CalcPrefixColumnResult:
101
+ column: int
102
+ num_chars: int
103
+
104
+
105
+ def _CalcPrefixColumn(line: str, tab_size: int, max_column: int | None) -> _CalcPrefixColumnResult:
106
+ max_column = max_column or 99999999
107
+
108
+ column = 1
109
+ num_chars = 0
110
+
111
+ for char in line:
112
+ if column >= max_column:
113
+ break
114
+
115
+ if char == " ":
116
+ column += 1
117
+ elif char == "\t":
118
+ column += tab_size
119
+ else:
120
+ break
121
+
122
+ num_chars += 1
123
+
124
+ return _CalcPrefixColumnResult(column, num_chars)
@@ -1,44 +0,0 @@
1
- # noqa: D104
2
-
3
- from importlib.metadata import version
4
-
5
- from dbrownell_ParserLib.antlr import (
6
- AntlrParser,
7
- AntlrVisitorMixinBase,
8
- BuildAntlrGrammar,
9
- CreateAntlrParser,
10
- InsignificantWhitespaceAntlrVisitorMixin,
11
- SignificantWhitespaceAntlrVisitorMixin,
12
- )
13
- from dbrownell_ParserLib.errors import CreateErrorType, Error, ErrorException, PythonError
14
- from dbrownell_ParserLib.expression import Expression
15
- from dbrownell_ParserLib.location import Location
16
- from dbrownell_ParserLib.region import Region
17
- from dbrownell_ParserLib.terminal_expression import TerminalExpression
18
- from dbrownell_ParserLib.visitors import ExpressionVisitor, ExpressionVisitorHelper, VisitResult
19
-
20
-
21
- # ----------------------------------------------------------------------
22
- __all__ = [
23
- "AntlrParser",
24
- "AntlrVisitorMixinBase",
25
- "BuildAntlrGrammar",
26
- "CreateAntlrParser",
27
- "CreateErrorType",
28
- "Error",
29
- "ErrorException",
30
- "Expression",
31
- "ExpressionVisitor",
32
- "ExpressionVisitorHelper",
33
- "InsignificantWhitespaceAntlrVisitorMixin",
34
- "Location",
35
- "PythonError",
36
- "Region",
37
- "SignificantWhitespaceAntlrVisitorMixin",
38
- "TerminalExpression",
39
- "VisitResult",
40
- "__version__",
41
- ]
42
-
43
-
44
- __version__ = version("dbrownell_ParserLib")
@@ -1,19 +0,0 @@
1
- # noqa: D104
2
- from dbrownell_ParserLib.antlr.build_antlr_grammar import BuildAntlrGrammar
3
- from dbrownell_ParserLib.antlr.antlr_parser import AntlrParser, CreateAntlrParser
4
- from dbrownell_ParserLib.antlr.antlr_visitor_mixins import (
5
- AntlrVisitorMixinBase,
6
- InsignificantWhitespaceAntlrVisitorMixin,
7
- SignificantWhitespaceAntlrVisitorMixin,
8
- )
9
-
10
-
11
- # ----------------------------------------------------------------------
12
- __all__ = [
13
- "AntlrParser",
14
- "AntlrVisitorMixinBase",
15
- "BuildAntlrGrammar",
16
- "CreateAntlrParser",
17
- "InsignificantWhitespaceAntlrVisitorMixin",
18
- "SignificantWhitespaceAntlrVisitorMixin",
19
- ]