Skip to content
← Back

src/syntax/parsers/definitions/class.ghul

1
namespace Syntax.Parsers.Definitions is
2
use Source
3
use Logging
4
use Ghul.Disposable
5
6
class CLASS(
7
identifier_parser: Parser[Trees.Identifiers.Identifier],
8
type_parser: Parser[Trees.TypeExpressions.TypeExpression],
9
type_list_parser: Parser[Trees.TypeExpressions.LIST],
10
modifier_list_parser: Parser[Trees.Modifiers.LIST],
11
variable_list_parser: Parser[Trees.Variables.LIST],
12
definition_list_parser: Parser[Trees.Definitions.LIST]
13
): Base[Trees.Definitions.CLASS] is
14
super()
15
16
parse(context: CONTEXT) -> Trees.Definitions.CLASS? is
17
let start = context.location
18
context.in_classy = true
19
context.global_indent = start.start_column
20
21
try
22
context.next_token(Lexical.TOKEN.CLASS)
23
let identifier = identifier_parser.parse(context)
24
25
let is_poisoned mut = false
26
27
if !identifier? then
28
return null
29
fi
30
31
let arguments: Trees.TypeExpressions.LIST? mut = null
32
let ancestors: Trees.TypeExpressions.LIST? mut = null
33
34
if context.current.token == Lexical.TOKEN.SQUARE_OPEN then
35
context.next_token()
36
37
context.in_type_parameters = true
38
context.in_generic_parameter_list = true
39
arguments = type_list_parser.parse(context)!
40
context.in_type_parameters = false
41
context.in_generic_parameter_list = false
42
arguments.check_no_reference_types(context.logger)
43
44
is_poisoned = arguments.is_poisoned
45
46
if
47
!is_poisoned \/
48
context.current.token == Lexical.TOKEN.SQUARE_CLOSE
49
then
50
is_poisoned = !context.next_token(Lexical.TOKEN.SQUARE_CLOSE) \/ is_poisoned
51
fi
52
fi
53
54
let primary_params: Trees.Variables.LIST? mut = null
55
56
if context.current.token == Lexical.TOKEN.PAREN_OPEN then
57
context.next_token()
58
59
if context.current.token != Lexical.TOKEN.PAREN_CLOSE then
60
let previous_in_primary_ctor_params = context.in_primary_ctor_params
61
context.in_primary_ctor_params = true
62
try
63
primary_params = variable_list_parser.parse(context)
64
finally
65
context.in_primary_ctor_params = previous_in_primary_ctor_params
66
yrt
67
68
if primary_params? then
69
for p in primary_params do
70
p.mark_argument()
71
od
72
73
is_poisoned = is_poisoned \/ primary_params.is_poisoned
74
fi
75
else
76
primary_params = Trees.Variables.LIST(
77
context.location,
78
Collections.LIST[Trees.Variables.VARIABLE](0)
79
)
80
fi
81
82
is_poisoned = !context.next_token(Lexical.TOKEN.PAREN_CLOSE) \/ is_poisoned
83
fi
84
85
if context.current.token == Lexical.TOKEN.COLON then
86
context.next_token()
87
ancestors = type_list_parser.parse(context)!
88
ancestors.check_no_reference_types(context.logger)
89
90
is_poisoned = is_poisoned \/ ancestors.is_poisoned
91
fi
92
93
let modifiers = modifier_list_parser.parse(context)!
94
95
let read_a_body mut = false
96
97
let body: Trees.Definitions.LIST mut
98
let expect_body mut = false
99
100
let semicolon_end: LOCATION? mut = null
101
102
if
103
primary_params? /\
104
(
105
context.current.token == Lexical.TOKEN.SEMICOLON \/
106
(context.at_inferred_terminator /\ context.current.token != Lexical.TOKEN.IS)
107
)
108
then
109
semicolon_end = context.location
110
111
if context.current.token == Lexical.TOKEN.SEMICOLON then
112
let semicolon = context.current
113
context.next_token()
114
context.note_written_terminator(semicolon)
115
else
116
context.note_inferred_terminator()
117
fi
118
elif !is_poisoned then
119
if context.next_token(Lexical.TOKEN.IS) then
120
expect_body = true
121
elif lookahead_for_body(context) then
122
is_poisoned = true
123
expect_body = true
124
fi
125
else
126
expect_body = lookahead_for_body(context)
127
fi
128
129
if expect_body then
130
let use open = context.open_construct(Lexical.TOKEN.SI)
131
132
body = definition_list_parser.parse(context)!
133
134
read_a_body = true
135
else
136
body = Trees.Definitions.LIST(LOCATION.internal, Collections.LIST[Trees.Definitions.Definition](0))
137
fi
138
139
let end_location = if semicolon_end? then semicolon_end else context.location fi
140
141
let result = Trees.Definitions.CLASS(
142
start::end_location,
143
identifier!,
144
arguments,
145
ancestors,
146
modifiers,
147
body
148
)
149
150
if primary_params? then
151
result.set_primary_params(primary_params)
152
fi
153
154
result.poison(is_poisoned)
155
156
if read_a_body then
157
if !is_poisoned then
158
context.expect_closer(Lexical.TOKEN.SI)
159
elif context.current_token == Lexical.TOKEN.SI /\ context.current.location.start_column >= start.start_column then
160
context.next_token()
161
fi
162
fi
163
164
return result
165
finally
166
context.in_classy = false
167
yrt
168
si
169
170
lookahead_for_body(context: CONTEXT) -> bool is
171
let want_backtrack = true
172
173
let line = context.current.location.start_line
174
175
let use diagnostics_snapshot = context.diagnostics_speculate_then_backtrack()
176
177
// search on the current line for an 'is'
178
while context.current.location.start_line == line /\ context.current.location.start_column >= context.global_indent do
179
if context.current.token == Lexical.TOKEN.IS then
180
diagnostics_snapshot.commit()
181
182
context.next_token()
183
184
// is on the same line as the start of the class definition means it's likely the block is associated with the
185
// class, and we should parse it as such, even if the first part of the class definition is invalid
186
return true
187
fi
188
189
context.next_token()
190
od
191
192
if context.current_token == Lexical.TOKEN.IS /\ context.current.location.start_column >= context.global_indent then
193
diagnostics_snapshot.commit()
194
context.next_token()
195
196
// again, if the `is` is on the line immediately following the class definition, and it's properly indented,
197
// it's likely the block is associated with the class, and we should parse it as such, even if the first part of
198
// the class definition is invalid
199
return true
200
fi
201
202
return false
203
si
204
si
205
si