Skip to content
← Back

src/syntax/parsers/assigned_value.ghul

1
namespace Syntax.Parsers is
2
use Ghul.Pipes
3
4
// The value of a `let` or an assignment whose `=` has just been consumed.
5
//
6
// An initializer wraps onto the next line often enough that the value has
7
// to be read across the boundary, and how the continuation is laid out
8
// settles nothing: the grammar demands a value here, so the next token is
9
// one wherever it sits. But where the value is simply absent - a
10
// half-written line, which in an editor is the ordinary case rather than
11
// the exceptional one - that same rule takes the subject of the following
12
// statement as the value, and leaves that statement's own `=` behind as
13
// the token everything downstream then fails on.
14
//
15
// So a value beginning on a later line is read speculatively and kept
16
// when it reaches somewhere a value can end and either parsed without
17
// complaint or began with a token that starts a value in its own right
18
// (see _starts_only_a_value), whose mistake is then reported alone. A
19
// program that parses is unaffected whatever its layout, since its
20
// speculation always commits; otherwise the value is reported missing
21
// where it was written, instead of a stray token surfacing further on.
22
parse_assigned_value(
23
context: CONTEXT,
24
expression_parser: Parser[Trees.Expressions.Expression]
25
) -> Trees.Expressions.Expression is
26
if !context.current.first_on_line then
27
return expression_parser.parse(context)!
28
fi
29
30
let missing_at = context.previous_end
31
let starts_only_a_value = _starts_only_a_value(context.current_token)
32
33
let use diagnostics_snapshot = context.diagnostics_speculate_then_backtrack()
34
let use snapshot = context.tokenizer_speculate_then_backtrack_bounded()
35
36
let errors_before = context.logger.error_count
37
38
let value = expression_parser.parse(context)!
39
40
if
41
!isa Trees.Expressions.Literals.NONE(value) /\
42
(starts_only_a_value \/ (!value.is_poisoned /\ context.logger.error_count == errors_before)) /\
43
ends_assigned_value(context)
44
then
45
snapshot.commit()
46
diagnostics_snapshot.commit()
47
48
return value
49
fi
50
51
snapshot.backtrack()
52
diagnostics_snapshot.backtrack()
53
54
context.error(missing_at, "expected a value to assign here")
55
56
return Trees.Expressions.Literals.NONE(missing_at)
57
si
58
59
// A token that begins a value in its own right. A value starting with
60
// one and reaching a place a value can end is kept, even with a mistake
61
// inside it: an error-free one is kept already, so the mistake is the
62
// only difference, and reporting the value missing as well would
63
// describe it twice. An identifier is not one of these, since it is as
64
// likely to be the subject of the statement after a value that really
65
// is missing.
66
_starts_only_a_value(token: Lexical.TOKEN) -> bool =>
67
token == Lexical.TOKEN.CASE \/
68
token == Lexical.TOKEN.IF \/
69
token == Lexical.TOKEN.INT_LITERAL \/
70
token == Lexical.TOKEN.FLOAT_LITERAL \/
71
token == Lexical.TOKEN.DOUBLE_LITERAL \/
72
token == Lexical.TOKEN.CHAR_LITERAL \/
73
token == Lexical.TOKEN.STRING_LITERAL \/
74
token == Lexical.TOKEN.TRUE \/
75
token == Lexical.TOKEN.FALSE \/
76
token == Lexical.TOKEN.NULL \/
77
token == Lexical.TOKEN.PAREN_OPEN \/
78
token == Lexical.TOKEN.SQUARE_OPEN
79
80
// Where a value may end: a statement boundary, or a token that closes or
81
// continues whatever the `let` was written inside. A value that stops
82
// anywhere else has run into something it could not read, which is the
83
// reading the speculation exists to decline.
84
ends_assigned_value(context: CONTEXT) -> bool is
85
if context.at_inferred_terminator then
86
return true
87
fi
88
89
let token = context.current_token
90
91
token == Lexical.TOKEN.SEMICOLON \/
92
token == Lexical.TOKEN.COMMA \/
93
token == Lexical.TOKEN.IN \/
94
token == Lexical.TOKEN.THEN \/
95
token == Lexical.TOKEN.DO \/
96
token == Lexical.TOKEN.ELSE \/
97
token == Lexical.TOKEN.ELIF \/
98
token == Lexical.TOKEN.FI \/
99
token == Lexical.TOKEN.OD \/
100
token == Lexical.TOKEN.CATCH \/
101
token == Lexical.TOKEN.FINALLY \/
102
token == Lexical.TOKEN.YRT \/
103
token == Lexical.TOKEN.WHEN \/
104
token == Lexical.TOKEN.ESAC \/
105
token == Lexical.TOKEN.LAV \/
106
token == Lexical.TOKEN.SI \/
107
token == Lexical.TOKEN.PAREN_CLOSE \/
108
token == Lexical.TOKEN.SQUARE_CLOSE
109
si
110
si