Skip to content
← Back

src/syntax/parsers/variables/variable.ghul

1
namespace Syntax.Parsers.Variables is
2
class VARIABLE(
3
identifier_parser: Parser[Trees.Identifiers.Identifier],
4
destructure_left_parser: Parser[Trees.Variables.DESTRUCTURING_VARIABLE_LEFT],
5
type_parser: Parser[Trees.TypeExpressions.TypeExpression],
6
expression_parser: Parser[Trees.Expressions.Expression],
7
modifier_list_parser: Parser[Trees.Modifiers.LIST],
8
pragma_parser: Parser[Trees.Pragmas.PRAGMA]
9
): Base[Trees.Variables.VARIABLE] is
10
super()
11
12
parse(context: CONTEXT) -> Trees.Variables.VARIABLE? is
13
let start = context.location
14
15
// An attribute pragma before a formal-argument parameter, e.g.
16
// `@FromQuery() id: int`. Only recognised inside a function or
17
// method parameter list (CONTEXT.in_formal_arguments) — a `let`,
18
// primary-ctor header, or variant field list leaves `@` alone
19
// for the caller's usual "unexpected token" handling.
20
let pragmas: Collections.LIST[Trees.Pragmas.PRAGMA]? mut = _
21
22
if context.in_formal_arguments then
23
while context.current.token == Lexical.TOKEN.AT do
24
let pragma = pragma_parser.parse(context)
25
26
if !pragma? then
27
break
28
fi
29
30
if !pragmas? then
31
pragmas = Collections.LIST[Trees.Pragmas.PRAGMA]()
32
fi
33
34
pragmas.add(pragma)
35
od
36
fi
37
38
// The .. splice marker in a secondary-init formal-arg list
39
// parses as a synthetic VARIABLE flagged is_splice. The
40
// rewrite-primary-constructors pass expands it into the
41
// surrounding class's primary parameters; no downstream
42
// phase ever observes it. Outside an init parameter list
43
// (the function parser opens / closes the window via
44
// `context.in_init_arguments`) it's a syntax error.
45
if
46
context.current.token == Lexical.TOKEN.OPERATOR /\
47
context.current.value_string =~ ".."
48
then
49
let splice_location = context.location
50
context.next_token()
51
52
let splice = Trees.Variables.VARIABLE(
53
splice_location,
54
Trees.Identifiers.Identifier(splice_location, "$splice"),
55
Trees.TypeExpressions.INFER(splice_location),
56
false,
57
false,
58
null
59
)
60
61
splice.mark_splice()
62
splice.mark_synthesized()
63
64
if !context.in_init_arguments then
65
context.error(
66
splice_location,
67
".. is only allowed in an init parameter list"
68
)
69
splice.poison(true)
70
fi
71
72
if pragmas? then
73
context.error(
74
splice_location,
75
"attribute is not allowed on the .. splice marker"
76
)
77
splice.poison(true)
78
fi
79
80
return splice
81
fi
82
83
// The `~` match marker belongs on a leaf inside a
84
// destructure group, where a name would otherwise bind.
85
// A variable in any other position is a binding by
86
// definition, so a marker here is rejected and consumed —
87
// parsing the rest as the binding it looks like keeps one
88
// mistake to one diagnostic.
89
if
90
context.current.token == Lexical.TOKEN.OPERATOR /\
91
context.current.value_string =~ "~"
92
then
93
context.error(
94
context.location,
95
"a match marker is only allowed on a destructure leaf"
96
)
97
98
context.next_token()
99
fi
100
101
let variable_left: Trees.Variables.VariableLeft mut
102
103
if context.current_token == Lexical.TOKEN.PAREN_OPEN then
104
variable_left = destructure_left_parser.parse(context)!
105
elif context.current_token == Lexical.TOKEN.IDENTIFIER then
106
let identifier = identifier_parser.parse(context)
107
108
if identifier? then
109
variable_left = Trees.Variables.SIMPLE_VARIABLE_LEFT(identifier.location, identifier)
110
else
111
return null
112
fi
113
else
114
context.expect_token([Lexical.TOKEN.IDENTIFIER, Lexical.TOKEN.PAREN_OPEN], "in variable")
115
return null
116
fi
117
118
let end mut = variable_left.location
119
let type_expression: Trees.TypeExpressions.TypeExpression mut = Trees.TypeExpressions.INFER(start::context.location)
120
let initializer: Trees.Expressions.Expression? mut = _
121
let is_explicit_type mut = false
122
123
if context.current.token == Lexical.TOKEN.COLON then
124
is_explicit_type = true
125
context.next_token()
126
type_expression = type_parser.parse(context)!
127
128
end = type_expression.location
129
fi
130
131
let is_mutable_marked mut = false
132
if context.current_token == Lexical.TOKEN.MUT then
133
// Read before the token is consumed: afterwards
134
// context.location is the token that follows, which across a
135
// line break is on the next line.
136
end = context.location
137
context.next_token()
138
is_mutable_marked = true
139
fi
140
141
// Trailing modifier list — only consumed in primary-ctor
142
// header position. Elsewhere (`let`, formal args, secondary
143
// inits) a stray modifier token falls through to the caller
144
// for normal "unexpected token" handling.
145
let modifiers: Trees.Modifiers.LIST? mut = _
146
if context.in_primary_ctor_params then
147
modifiers = modifier_list_parser.parse(context)!
148
if !modifiers.is_empty then
149
end = modifiers.location
150
fi
151
fi
152
153
if context.current.token == Lexical.TOKEN.ASSIGN then
154
context.next_token()
155
initializer = parse_assigned_value(context, expression_parser)
156
end = initializer.location
157
fi
158
159
let result =
160
Trees.Variables.VARIABLE(
161
start::end,
162
variable_left,
163
type_expression,
164
false,
165
is_explicit_type,
166
initializer
167
)
168
169
if is_mutable_marked then
170
result.mark_mutable()
171
fi
172
173
if modifiers? /\ !modifiers.is_empty then
174
result.set_modifiers(modifiers)
175
fi
176
177
if pragmas? then
178
result.set_pragmas(pragmas)
179
fi
180
181
result.poison(type_expression.is_poisoned)
182
183
return result
184
si
185
si
186
si