Skip to content
← Back

src/syntax/parsers/definitions/property.ghul

1
namespace Syntax.Parsers.Definitions is
2
use IO.Std
3
4
use Source
5
6
use Logging
7
8
class PROPERTY(
9
identifier_parser: Parser[Trees.Identifiers.Identifier],
10
type_parser: Parser[Trees.TypeExpressions.TypeExpression],
11
modifier_list_parser: Parser[Trees.Modifiers.LIST],
12
body_parser: Parser[Trees.Bodies.Body]
13
): Base[Trees.Definitions.PROPERTY] is
14
super()
15
16
parse(context: CONTEXT) -> Trees.Definitions.PROPERTY is
17
let fail mut = false
18
let progress mut = false
19
20
try
21
if context.in_classy then
22
context.in_member = true
23
context.member_indent = context.location.start_column
24
else
25
context.in_global_function = true
26
context.global_indent = context.location.start_column
27
fi
28
29
let start = context.location
30
let name = identifier_parser.parse(context)
31
let type_expression: Trees.TypeExpressions.TypeExpression mut
32
33
if context.current.token == Lexical.TOKEN.COLON then
34
context.next_token()
35
progress = true
36
37
if let parsed = type_parser.parse(context) then
38
type_expression = parsed
39
40
if parsed.is_poisoned then
41
fail = true
42
fi
43
else
44
type_expression = Trees.TypeExpressions.INFER(context.location)
45
fail = true
46
fi
47
else
48
type_expression = Trees.TypeExpressions.INFER(context.location)
49
fi
50
51
let modifiers = modifier_list_parser.parse(context)!
52
let read_body: Trees.Bodies.Body? mut = null
53
let assign_body: Trees.Bodies.Body? mut = null
54
let setter_argument_name: Trees.Identifiers.Identifier? mut = null
55
let expect_semicolon mut = true
56
57
do
58
if context.current.token == Lexical.TOKEN.ASSIGN then
59
if setter_argument_name? then
60
context.error(context.location, "replacing assign")
61
fi
62
63
context.next_token()
64
progress = true
65
66
setter_argument_name = identifier_parser.parse(context)
67
expect_semicolon = context.current.token == Lexical.TOKEN.ARROW_FAT \/ context.current.token == Lexical.TOKEN.INNATE
68
69
try
70
assign_body = body_parser.parse(context)
71
catch ue: UNWIND_TO_MEMBER_EXCEPTION
72
assign_body = Trees.Bodies.NULL(context.location)
73
fail = true
74
yrt
75
76
if context.current.token == Lexical.TOKEN.COMMA then
77
context.next_token()
78
progress = true
79
else
80
if isa Trees.Bodies.NULL(assign_body) then
81
expect_semicolon = true
82
fi
83
84
break
85
fi
86
elif
87
context.current.token == Lexical.TOKEN.IS \/
88
context.current.token == Lexical.TOKEN.ARROW_FAT \/
89
context.current.token == Lexical.TOKEN.INNATE
90
then
91
expect_semicolon = context.current.token == Lexical.TOKEN.ARROW_FAT \/ context.current.token == Lexical.TOKEN.INNATE
92
93
if read_body? then
94
context.error(context.location, "replacing read")
95
fi
96
97
try
98
read_body = body_parser.parse(context)
99
catch ue: UNWIND_TO_MEMBER_EXCEPTION
100
read_body = Trees.Bodies.NULL(context.location)
101
fail = true
102
yrt
103
104
if context.current.token == Lexical.TOKEN.COMMA then
105
context.next_token()
106
progress = true
107
else
108
if isa Trees.Bodies.NULL(read_body) then
109
expect_semicolon = true
110
else
111
progress = true
112
fi
113
114
break
115
fi
116
elif context.current_token == Lexical.TOKEN.SEMICOLON then
117
break
118
elif context.at_inferred_terminator then
119
// a line break ends a bare declaration the same way
120
// a `;` does; the terminator check below reports it
121
break
122
elif context.current_token == Lexical.TOKEN.COMMA then
123
if read_body? then
124
context.error(context.location, "replacing read")
125
elif assign_body? then
126
context.error(context.location, "empty read body must precede write body")
127
else
128
progress = true
129
context.next_token()
130
131
read_body = Syntax.Trees.Bodies.NULL(context.current.location)
132
fi
133
else
134
if !fail then
135
context.error(context.location, "syntax error in property: unexpected token {context.current_token_name}")
136
fi
137
138
fail = true
139
140
// A function header damaged so that it reads as a
141
// property, `f n: int) -> int is`, still has its body
142
// after it. Read to a body opener on this line, so the
143
// body is kept rather than its statements being read
144
// one at a time as members.
145
while
146
!context.is_end_of_file /\
147
!context.current.first_on_line /\
148
context.current.token != Lexical.TOKEN.IS /\
149
context.current.token != Lexical.TOKEN.ARROW_FAT
150
do
151
context.next_token()
152
progress = true
153
od
154
155
if
156
!context.current.first_on_line /\
157
(context.current.token == Lexical.TOKEN.IS \/ context.current.token == Lexical.TOKEN.ARROW_FAT) /\
158
!read_body?
159
then
160
continue
161
fi
162
163
break
164
fi
165
166
expect_semicolon = true
167
od
168
169
if context.in_trait /\ read_body == null then
170
read_body = Trees.Bodies.NULL(context.location)
171
fi
172
173
let result =
174
Trees.Definitions.PROPERTY(
175
start::context.location,
176
type_expression,
177
name,
178
modifiers,
179
read_body,
180
setter_argument_name,
181
assign_body
182
)
183
184
if expect_semicolon then
185
if !fail \/ context.current.token == Lexical.TOKEN.SEMICOLON then
186
context.accept_terminator()
187
fi
188
fi
189
190
if fail /\ !progress then
191
debug_always("no progress parsing property at {context.location}")
192
IoC.CONTAINER.instance.watchdog.request_restart()
193
194
context.error(context.location, "syntax error in property: unexpected token {context.current_token_name}")
195
context.next_token()
196
197
throw UNWIND_BAD_PROPERTY_EXCEPTION(null)
198
fi
199
200
result.poison(fail)
201
202
return result
203
finally
204
context.in_member = false
205
yrt
206
si
207
si
208
si