Appearance
| 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 |