Skip to content
← Back

src/syntax/parsers/definitions/class.ghul

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