This repository has been archived by the owner on Dec 16, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 40
/
sphinx.py
193 lines (172 loc) · 5.36 KB
/
sphinx.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import inspect
from typing import (
List,
)
from functools import (
reduce,
)
from ..custom_assert import Assert
from ..token import (
Token,
TokenType,
KEYWORDS,
)
from .cyk import (
parse as cyk_parse,
)
from ..node import (
CykNode,
)
from .combinator import (
parser_combinator,
)
from .long_description import (
parse as long_description_parse,
)
from .grammars.sphinx_arguments_section import ArgumentsGrammar
from .grammars.sphinx_argument_type_section import ArgumentTypeGrammar
from .grammars.sphinx_variables_section import VariablesSectionGrammar
from .grammars.sphinx_variable_type_section import VariableTypeGrammar
from .grammars.sphinx_raises_section import RaisesGrammar
from .grammars.sphinx_returns_section import ReturnsGrammar
from .grammars.sphinx_return_type_section import ReturnTypeGrammar
from .grammars.sphinx_short_description import ShortDescriptionGrammar
from .grammars.sphinx_yields_section import YieldsGrammar
from .grammars.sphinx_yield_type_section import YieldTypeGrammar
def two_newline_separated_or_keyword(tokens, i):
# type: (List[Token], int) -> int
newline_count = 0
j = i
while j < len(tokens):
if tokens[j].token_type == TokenType.NEWLINE:
newline_count += 1
else:
break
j += 1
if newline_count >= 2:
return j
if (j + 1 < len(tokens)
and tokens[j].token_type == TokenType.COLON
and tokens[j + 1].token_type in KEYWORDS):
return j
return 0
def top_parse(tokens):
# type: (List[Token]) -> List[List[Token]]
all_sections = list()
curr = 0
# Strip leading newlines.
while curr < len(tokens) and tokens[curr].token_type == TokenType.NEWLINE:
curr += 1
prev = curr
while curr < len(tokens):
split_end = two_newline_separated_or_keyword(tokens, curr)
if split_end > curr:
if tokens[prev:curr]:
all_sections.append(
tokens[prev:curr]
)
curr = split_end
prev = curr
else:
curr += 1
last_section = tokens[prev:curr]
if last_section:
all_sections.append(last_section)
return all_sections
def _match(token):
"""Match the given token from the given section to a set of grammars.
Args:
token: The token to match. This should hint at what sections
could possibly be here.
Returns:
A list of grammars to be tried in order.
"""
tt_lookup = {
TokenType.VARIABLES: [
VariablesSectionGrammar,
long_description_parse,
],
TokenType.ARGUMENTS: [
ArgumentsGrammar,
long_description_parse,
],
TokenType.ARGUMENT_TYPE: [
ArgumentTypeGrammar,
long_description_parse,
],
TokenType.VARIABLE_TYPE: [
VariableTypeGrammar,
long_description_parse,
],
TokenType.RAISES: [
RaisesGrammar,
long_description_parse,
],
TokenType.YIELDS: [
YieldsGrammar,
long_description_parse,
],
TokenType.YIELD_TYPE: [
YieldTypeGrammar,
long_description_parse,
],
TokenType.RETURNS: [
ReturnsGrammar,
long_description_parse,
],
TokenType.RETURN_TYPE: [
ReturnTypeGrammar,
long_description_parse,
],
}
return tt_lookup.get(token.token_type, [long_description_parse])
def lookup(section, section_index=-1):
Assert(len(section) > 0, 'Expected non-empty section.')
if (section[0].token_type == TokenType.COLON
and len(section) > 1):
grammars = _match(section[1])
else:
grammars = [long_description_parse]
if section_index == 0:
# Add the short description right before the long
# description. That way, the short description will
# always be captured, if possible, and any other
# possible sections will be captured, if possible.
#
# Assumes that the long description will always come
# after all other possible grammars, if it appears at
# all.
if long_description_parse not in grammars:
grammars.append(ShortDescriptionGrammar)
return grammars
long_index = grammars.index(long_description_parse)
grammars.insert(long_index, ShortDescriptionGrammar)
return grammars
return grammars
def combinator(*args):
def inner(*nodes):
if len(nodes) == 1:
return CykNode(
symbol='docstring',
lchild=nodes[0],
)
elif len(nodes) == 2:
return CykNode(
symbol='docstring',
lchild=nodes[0],
rchild=nodes[1],
)
if args:
return reduce(inner, args)
else:
# The arguments are empty, so we return an
# empty docstring.
return CykNode(symbol='docstring')
def parse(tokens):
def mapped_lookup(section, section_index=-1):
for grammar in lookup(section, section_index):
if inspect.isclass(grammar):
yield lambda x: cyk_parse(grammar, x)
else:
yield grammar
return parser_combinator(top_parse, mapped_lookup, combinator, tokens)