1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
use super::*;
impl Parser {
pub(crate) fn parse_resize(&mut self) -> Result<Statement, Box<CompileError>> {
// "resize buffer to N bytes" or "resize buffer to N"
self.advance(); // consume 'resize'
self.skip_noise();
// Get buffer name
let name = match self.current().clone() {
Token::Identifier(n) => { self.advance(); n }
Token::The => {
self.advance();
self.skip_noise();
match self.current().clone() {
Token::Identifier(n) => { self.advance(); n }
_ => return Err(self.err("Expected buffer name after 'the'")),
}
}
_ => return Err(self.err("Expected buffer name after 'resize'")),
};
self.skip_noise();
self.expect(&Token::To);
self.skip_noise();
// Parse new size
let new_size = self.parse_expression()?;
self.skip_noise();
// Skip optional "bytes"
if *self.current() == Token::Bytes {
self.advance();
}
Ok(Statement::BufferResize { name, new_size })
}
/// Parse one value in append position - any primary the language has,
/// read with `to` reserved, because `to` is the append separator and
/// must not be consumed as part of the value. A quoted name followed
/// by of/with/on is a function call (`append "f" of x to items`); `to`
/// is NOT a call trigger here, unlike the general expression parser,
/// so that `append "s" to items` stays an append of the literal "s".
/// Braces force the general expression parser for the enclosed tokens
/// (`append {i multiply i} to s`), matching how braces put an expression
/// into a value slot elsewhere in Vox.
///
/// BUGS_FOUND #77: every arm below the string literal used to be written
/// out here - a hand-rolled copy of `parse_primary` that had fallen
/// behind it. A leading `-`, `nothing`, `element N of L`, `byte N of b`
/// and every `'s` possessive were value forms the copy had never
/// learned, so `append -5 to xs.` answered "Expected value to append"
/// while `append {-5} to xs.` - the same tokens, routed to
/// `parse_primary` - compiled. They read one primary now, so a value
/// form is added or diagnosed in exactly one place (the lesson of #64).
/// The separator protection is unchanged: it lives in
/// `suppress_to_connector`, which `parse_primary_reserving` sets for the
/// whole subtree, not in the arm list. The arm list stays explicit so a
/// token that cannot start a value still gets the append slot's own
/// diagnostic rather than the general parser's "expected a statement".
pub(crate) fn parse_append_value_primary(&mut self) -> Result<Expr, Box<CompileError>> {
match self.current().clone() {
Token::StringLiteral(s) => {
// Plan 270 §S1.5: a string literal is data, not a name, so it
// cannot be a callee. Capture the literal's location now (before
// advancing) so the §S1.5 underline can still point at it.
let s_loc = self.current_location();
self.advance();
self.skip_noise();
// A format string can't be a function name - resolve it first.
let resolved = self.string_value_expr(s.clone());
if matches!(resolved, Expr::FormatString { .. }) {
Ok(resolved)
} else if matches!(self.current(), Token::Of | Token::With | Token::On) {
// `append "<name>" of 3 to s` — a string used as a callee.
let mut err = CompileError::new("expected a name, found a string literal");
if let Some(loc) = s_loc {
err = err.with_location(loc);
}
let span = s.chars().count() + 2;
err = err
.with_underline_note(span, "strings are data; names are bare or 'single-quoted'")
.with_help_line(&Self::suggest_name_form(&s));
Err(Box::new(err))
} else {
Ok(resolved)
}
}
// One primary, with `to` reserved for the append separator.
// Reserving it is what keeps a bare name from reading the
// separator as its own call connector (`append id of item to
// out`), and it covers the whole subtree, so an index's list
// (`element 1 of ys to xs`) is protected by the same flag.
Token::OpenBrace
| Token::Minus
| Token::IntegerLiteral(_)
| Token::IntegerLiteralOverflow(_)
| Token::FloatLiteral(_)
| Token::True
| Token::False
| Token::Nothing
| Token::Element
| Token::Byte
| Token::Identifier(_)
| Token::The => self.parse_primary_reserving(true, false),
_ => Err(self.err("Expected value to append")),
}
}
/// Continue an append value over any binary operators that follow it.
/// Precedence matches the general expression parser (bitwise tighter
/// than multiplicative, multiplicative tighter than additive); right
/// operands reuse the restricted append primary so the separator can
/// never be eaten. `to` is never an operator here, so the walk always
/// stops at it.
pub(crate) fn parse_append_value_ops(&mut self, mut left: Expr, min_prec: u8) -> Result<Expr, Box<CompileError>> {
loop {
self.skip_noise();
let (op, prec) = match self.current() {
Token::Add => (BinaryOperator::Add, 1),
Token::Subtract => (BinaryOperator::Subtract, 1),
Token::Multiply => (BinaryOperator::Multiply, 2),
// BUGS_FOUND #77: `multiply` and `times` are two spellings of
// one operator (LANGUAGE.md's operator table) but two tokens,
// because `Repeat N times` claims the second. This list had
// only the first, so `append v1 times v2 to l1.` stopped at
// `times` and reported "Expected 'to' after value" with the
// `to` written right there. The list now matches
// `parse_multiplicative`'s exactly.
Token::Times => (BinaryOperator::Multiply, 2),
Token::Divide => (BinaryOperator::Divide, 2),
Token::Modulo => (BinaryOperator::Modulo, 2),
Token::BitAnd => (BinaryOperator::BitAnd, 3),
Token::BitOr => (BinaryOperator::BitOr, 3),
Token::BitXor => (BinaryOperator::BitXor, 3),
Token::BitShiftLeft => (BinaryOperator::ShiftLeft, 3),
Token::BitShiftRight => (BinaryOperator::ShiftRight, 3),
_ => break,
};
if prec < min_prec {
break;
}
self.advance();
self.skip_noise();
let primary = self.parse_append_value_primary()?;
let right = self.parse_append_value_ops(primary, prec + 1)?;
left = Expr::BinaryOp {
left: Box::new(left),
op,
right: Box::new(right),
};
}
Ok(left)
}
pub(crate) fn parse_append(&mut self) -> Result<Statement, Box<CompileError>> {
// "append <expr> to <list>" or "append each <var> from <collection> to <list>"
self.advance(); // consume 'append'
self.skip_noise();
// Check for loop expansion: "append each X from Y to Z"
if let Some((variable, collection, treating)) = self.try_parse_each_from(true)? {
// `append` has one source value slot, so a grid of two or more
// `each` clauses is an arity error, not a multi-source append
// (plan 320 rule 12). The separator `to` must follow the single
// collection; an `and` here starts a second clause.
self.skip_noise();
if *self.current() == Token::And {
return Err(self.one_slot_arity_error("append"));
}
// Get target list name after "to"
if *self.current() != Token::To {
return Err(self.err("Expected 'to' after collection in append"));
}
self.advance();
self.skip_noise();
let list_name = match self.current().clone() {
Token::Identifier(n) => { self.advance(); n }
Token::StringLiteral(n) => return Err(self.err_string_as_name(&n)),
Token::The => {
self.advance();
self.skip_noise();
match self.current().clone() {
Token::Identifier(n) => { self.advance(); n }
Token::StringLiteral(n) => return Err(self.err_string_as_name(&n)),
_ => return Err(self.err("Expected list name after 'the'")),
}
}
_ => return Err(self.err("Expected list name after 'to'")),
};
// A `treating` clause written here, after the destination, is in
// the wrong place: it substitutes for the loop variable, so it
// belongs to the `each ... from ...` clause that binds the
// variable (`loop_expansion`, LANGUAGE.md), which is where
// `print`/`open`/a call all spell it. Left to fall through it
// reached the top level as "Expected a statement, got Treating",
// which named neither the clause nor the way out (BUGS_FOUND #70;
// diagnostic in the #45/#62/#63 family, caret on the offending
// token per #46).
self.skip_noise();
if *self.current() == Token::Treating {
let mut err = *self.err(
"A `treating` clause belongs to the `each` clause, not after the append destination"
);
err = err
.with_underline_note(
"treating".chars().count(),
"this substitutes for the loop variable, so it goes where the variable is bound",
)
.with_help_line(&format!(
"write `append each {} from <collection> treating <match> as <replacement> to {}.`",
variable, list_name
));
return Err(Box::new(err));
}
// Create the append statement for loop body. A `treating` clause
// on the `each` wraps the loop variable exactly as it does in
// `print each ... treating ...` and `open ... at each ...`.
let append_stmt = Statement::ListAppend {
list: list_name,
value: Self::each_arg_expr(&variable, &treating),
};
return self.wrap_in_loop_expansion(variable, collection, append_stmt);
}
// Parse just the value, then any arithmetic that applies to it
// (`append i multiply i to s`). The operator walk never treats `to`
// as an operator, so it always stops at the append separator.
let mut value = self.parse_append_value_primary()?;
value = self.parse_append_value_ops(value, 0)?;
self.skip_noise();
// Stage 1c: a type predicate as the append value, e.g.
// `append item is a number to flags` or `append item is not a text
// to flags`. The article `a`/`an` and the noun are keywords, and
// `to` (the append separator) is not one of them, so this cannot
// accidentally swallow the separator — `append "s" to items`
// (current token `to`, not `is`) skips this branch unchanged.
if matches!(self.current(), Token::Is | Token::Are) {
self.advance();
self.skip_noise();
let negated = *self.current() == Token::Not;
if negated {
self.advance();
self.skip_noise();
}
if !matches!(self.current(), Token::A | Token::An) {
return Err(self.err(
"Expected 'a'/'an' and a type noun after 'is' in append value"
));
}
let type_noun = self.parse_type_noun_after_article()?;
let check = Expr::TypeCheck {
value: Box::new(value),
type_noun,
};
value = if negated {
Expr::UnaryOp { op: UnaryOperator::Not, operand: Box::new(check) }
} else {
check
};
}
// Expect "to"
if *self.current() != Token::To {
return Err(self.err("Expected 'to' after value in append statement"));
}
self.advance();
self.skip_noise();
// Get list name
let list = match self.current().clone() {
Token::Identifier(n) => { self.advance(); n }
Token::StringLiteral(n) => return Err(self.err_string_as_name(&n)),
Token::The => {
self.advance();
self.skip_noise();
match self.current().clone() {
Token::Identifier(n) => { self.advance(); n }
Token::StringLiteral(n) => return Err(self.err_string_as_name(&n)),
_ => return Err(self.err("Expected list name after 'the'")),
}
}
_ => return Err(self.err("Expected list name after 'to'")),
};
Ok(Statement::ListAppend { list, value })
}
pub(crate) fn parse_copy(&mut self) -> Result<Statement, Box<CompileError>> {
// "copy <source> to <buffer>"
self.advance(); // consume 'copy'
self.skip_noise();
// Parse just the source value, preserving `to` as separator.
let source = match self.current().clone() {
Token::IntegerLiteral(n) => {
self.advance();
Expr::IntegerLit(n)
}
Token::IntegerLiteralOverflow(raw) => {
return Err(self.integer_literal_overflow_error(&raw));
}
Token::FloatLiteral(n) => {
self.advance();
Expr::FloatLit(n)
}
Token::StringLiteral(s) => {
self.advance();
self.string_value_expr(s)
}
Token::True => {
self.advance();
Expr::BoolLit(true)
}
Token::False => {
self.advance();
Expr::BoolLit(false)
}
Token::Identifier(name) => {
self.advance();
Expr::Identifier(name)
}
Token::The => {
self.advance();
self.skip_noise();
match self.current().clone() {
Token::Identifier(name) => {
self.advance();
Expr::Identifier(name)
}
Token::StringLiteral(s) => {
self.advance();
Expr::StringLit(s)
}
_ => return Err(self.err("Expected source value after 'the'")),
}
}
_ => return Err(self.err("Expected source value after 'copy'")),
};
self.skip_noise();
if *self.current() != Token::To {
return Err(self.err("Expected 'to' after source in copy statement"));
}
self.advance();
self.skip_noise();
let destination = match self.current().clone() {
Token::Identifier(n) => {
self.advance();
n
}
Token::StringLiteral(n) => return Err(self.err_string_as_name(&n)),
Token::The => {
self.advance();
self.skip_noise();
match self.current().clone() {
Token::Identifier(n) => {
self.advance();
n
}
Token::StringLiteral(n) => return Err(self.err_string_as_name(&n)),
_ => return Err(self.err("Expected destination buffer name after 'the'")),
}
}
_ => return Err(self.err("Expected destination buffer name after 'to'")),
};
Ok(Statement::BufferCopy { source, destination })
}
pub(crate) fn parse_clear(&mut self) -> Result<Statement, Box<CompileError>> {
// "clear <buffer>"
self.advance(); // consume 'clear'
self.skip_noise();
let name = match self.current().clone() {
Token::Identifier(n) => {
self.advance();
n
}
Token::StringLiteral(n) => return Err(self.err_string_as_name(&n)),
Token::The => {
self.advance();
self.skip_noise();
match self.current().clone() {
Token::Identifier(n) => {
self.advance();
n
}
Token::StringLiteral(n) => return Err(self.err_string_as_name(&n)),
_ => return Err(self.err("Expected buffer name after 'the'")),
}
}
_ => return Err(self.err("Expected buffer name after 'clear'")),
};
Ok(Statement::BufferClear { name })
}
}