Skip to content
← Back

src/syntax/parsers/definitions/use.ghul

1
namespace Syntax.Parsers.Definitions is
2
3
use Source
4
5
class USE(
6
identifier_qualified_parser: Parser[Trees.Identifiers.Identifier],
7
type_parser: Parser[Trees.TypeExpressions.TypeExpression],
8
type_list_parser: Parser[Trees.TypeExpressions.LIST]
9
): Base[Trees.Definitions.USE] is
10
super()
11
12
parse(context: CONTEXT) -> Trees.Definitions.USE is
13
let start = context.location
14
15
context.next_token(Lexical.TOKEN.USE)
16
17
if context.current_token == Lexical.TOKEN.DEFAULT then
18
context.next_token()
19
context.accept_terminator()
20
21
let default_use = Trees.Definitions.USE(start::context.previous_end, null, null)
22
23
default_use.is_default = true
24
25
return default_use
26
fi
27
28
// Decided before the qualified-identifier parser ever runs:
29
// that parser eagerly consumes any DOT it sees and then
30
// requires an identifier, so a `use X.*` clause has to be
31
// recognised up front - by the time it errored trying to
32
// read the wildcard as `.<identifier>`, the mistake would
33
// already be logged.
34
if _ends_in_wildcard(context) then
35
let `use = _parse_wildcard_target(context)
36
37
if !`use? then
38
return Trees.Definitions.USE(start::context.location, null, null)
39
fi
40
41
context.accept_terminator()
42
43
let all_use = Trees.Definitions.USE(start::context.previous_end, null, `use)
44
45
all_use.is_all = true
46
47
return all_use
48
fi
49
50
let `use mut = identifier_qualified_parser.parse(context)
51
52
if !`use? then
53
return Trees.Definitions.USE(start::context.location, null, null)
54
fi
55
56
let arguments = _parse_type_parameters(context)
57
58
if arguments? \/ context.current_token == Lexical.TOKEN.ASSIGN then
59
context.next_token(Lexical.TOKEN.ASSIGN)
60
61
let name = `use
62
63
if let name.qualifier? then
64
context.logger.error(qualifier.location, "cannot qualify namespace or symbol alias")
65
fi
66
67
// `use name = X.*;` has no meaning - a wildcard stands for
68
// several imports, not one value an alias could name - so
69
// it is rejected here rather than left to whatever the
70
// type parser makes of a stray `.*`, which fails the same
71
// way `use name = ;` does: cleanly, but as a cascade of
72
// unrelated-looking syntax errors from everything the
73
// aborted alias parse leaves unconsumed.
74
if _ends_in_wildcard(context) then
75
context.logger.error(name.location, "cannot alias a wildcard use")
76
77
_parse_wildcard_target(context)
78
context.accept_terminator()
79
80
return Trees.Definitions.USE(start::context.previous_end, name, null)
81
fi
82
83
// The right-hand side is parsed as a type expression
84
// whichever kind of alias this is: a bare qualified name
85
// parses as a NAMED type expression, and that is the shape
86
// that keeps the original symbol-alias meaning - importing
87
// a namespace, a static method or a global function under
88
// another name, none of which is a type. Anything else -
89
// a tuple, a function type, an array, an optional, a
90
// constructed generic - is a type alias.
91
let target = type_parser.parse(context)
92
93
if !target? \/ target.is_poisoned then
94
return Trees.Definitions.USE(start::context.location, name, null)
95
fi
96
97
let named = cast Trees.TypeExpressions.NAMED?(target)
98
99
if named? /\ !isa Trees.TypeExpressions.GENERIC(target) /\ !arguments? then
100
context.accept_terminator()
101
102
// previous_end rather than location: the terminator has
103
// been consumed, so what the parser is looking at now is
104
// the next construct.
105
106
return Trees.Definitions.USE(start::context.previous_end, name, named.name)
107
fi
108
109
context.accept_terminator()
110
111
return Trees.Definitions.USE(start::context.previous_end, name, null, arguments, target)
112
else
113
context.accept_terminator()
114
115
return Trees.Definitions.USE(start::context.previous_end, null, `use)
116
fi
117
si
118
119
// Whether the upcoming tokens are a dotted identifier chain ending
120
// in the wildcard marker - `.*` written with no space, which lexes
121
// as one OPERATOR token, or a `.` and a `*` with whitespace or a
122
// line break between them, which lex as two. A bounded speculative
123
// probe that always backtracks, exactly like `_starts_definition`
124
// in global_list.ghul: raw token stepping, no diagnostics, no tree
125
// built, so a clause that turns out not to be a wildcard is
126
// re-parsed from scratch by the ordinary path with nothing to undo.
127
_ends_in_wildcard(context: CONTEXT) -> bool is
128
let use snapshot = context.tokenizer_speculate_then_backtrack_bounded()
129
130
if context.current_token != Lexical.TOKEN.IDENTIFIER then
131
return false
132
fi
133
134
context.next_token()
135
136
while context.current_token == Lexical.TOKEN.DOT do
137
context.next_token()
138
139
if context.current_token == Lexical.TOKEN.OPERATOR /\ context.current_string =~ "*" then
140
return true
141
fi
142
143
if context.current_token != Lexical.TOKEN.IDENTIFIER then
144
return false
145
fi
146
147
context.next_token()
148
od
149
150
return context.current_token == Lexical.TOKEN.OPERATOR /\ context.current_string =~ ".*"
151
si
152
153
// The real, consuming counterpart of `_ends_in_wildcard`: builds
154
// the same identifier chain the shared qualified-identifier parser
155
// would, but - already knowing from the probe above that a `*`
156
// marker follows - stops at the dot that introduces it instead of
157
// trying to read it as another `.identifier` segment, then
158
// consumes the marker itself (one token either spelling lexes as).
159
_parse_wildcard_target(context: CONTEXT) -> Trees.Identifiers.Identifier? is
160
let start = context.location
161
162
if !context.expect_token(Lexical.TOKEN.IDENTIFIER, "expected identifier") then
163
return null
164
fi
165
166
let result mut = Trees.Identifiers.Identifier(context.location, context.current.value_string)
167
168
context.next_token()
169
170
while context.current_token == Lexical.TOKEN.DOT do
171
let completion_target_start = context.location
172
173
context.next_token()
174
175
if context.current_token != Lexical.TOKEN.IDENTIFIER then
176
break
177
fi
178
179
result =
180
Trees.Identifiers.QUALIFIED(
181
start::context.location,
182
result,
183
context.current.value_string,
184
completion_target_start::context.location,
185
context.location
186
)
187
188
context.next_token()
189
od
190
191
context.next_token()
192
193
return result
194
si
195
196
_parse_type_parameters(context: CONTEXT) -> Trees.TypeExpressions.LIST? is
197
if context.current_token != Lexical.TOKEN.SQUARE_OPEN \/ context.at_inferred_terminator then
198
return null
199
fi
200
201
context.next_token()
202
203
context.in_type_parameters = true
204
context.in_generic_parameter_list = true
205
let arguments = type_list_parser.parse(context)!
206
context.in_type_parameters = false
207
context.in_generic_parameter_list = false
208
209
arguments.check_no_reference_types(context.logger)
210
211
context.next_token(Lexical.TOKEN.SQUARE_CLOSE)
212
213
return arguments
214
si
215
si
216
si