Skip to content
← Back

src/syntax/parsers/bodies/body.ghul

1
namespace Syntax.Parsers.Bodies is
2
use IO.Std
3
4
5
class BODY(
6
expression_parser: Parser[Trees.Expressions.Expression],
7
statement_parser: Parser[Trees.Statements.Statement],
8
statement_list_parser: Parser[Trees.Statements.LIST],
9
identifier_qualified_parser: Parser[Trees.Identifiers.Identifier]
10
): Base[Trees.Bodies.Body] is
11
description: string => "function body"
12
13
super()
14
15
init(..) is
16
add_parsers()
17
si
18
19
add_parsers() is
20
add_parser(
21
(context: CONTEXT) is
22
context.next_token(Lexical.TOKEN.ARROW_FAT)
23
24
// `=> throw E` stubs out a body that always diverges.
25
// `throw` is a statement, not an expression, so wrap
26
// it the way `if`/`case` bodies are wrapped; the body
27
// takes its type from the declared return type (or
28
// settles void when that is inferred).
29
if context.current.token == Lexical.TOKEN.THROW then
30
let statement = statement_parser.parse(context)!
31
return Trees.Bodies.EXPRESSION(statement.location, Trees.Expressions.STATEMENT(statement.location, statement))
32
fi
33
34
let expression = expression_parser.parse(context)!
35
return Trees.Bodies.EXPRESSION(expression.location, expression)
36
si,
37
Lexical.TOKEN.ARROW_FAT
38
)
39
40
add_parser(
41
(context: CONTEXT) is
42
let start = context.location
43
context.next_token(Lexical.TOKEN.IS)
44
45
return _block(context, start)
46
si,
47
Lexical.TOKEN.IS
48
)
49
50
add_parser(
51
(context: CONTEXT) is
52
let start = context.location
53
context.next_token(Lexical.TOKEN.INNATE)
54
let identifier = identifier_qualified_parser.parse(context)!
55
return Trees.Bodies.INNATE(start::identifier.location, identifier)
56
si,
57
Lexical.TOKEN.INNATE
58
)
59
si
60
61
// The statements of a block body, once its `is` has been read or found
62
// missing.
63
_block(context: CONTEXT, start: Source.LOCATION) -> Trees.Bodies.Body is
64
let use open = context.open_construct(Lexical.TOKEN.SI)
65
let statement_list = statement_list_parser.parse(context)!
66
let end = context.location
67
context.expect_closer(Lexical.TOKEN.SI)
68
return Trees.Bodies.BLOCK(start::end, statement_list)
69
si
70
71
// No body opener, which a declaration with no body leaves. A header
72
// whose `is` was left out has its body below it instead: a keyword
73
// that can only begin a statement, on a line indented deeper than the
74
// header, cannot begin the next member, so the lines are read as the
75
// body they were written as, with the missing `is` reported, rather
76
// than one at a time as members that are not there. A correct
77
// program never has such a line here, so none reads differently.
78
other_token(context: CONTEXT) -> Trees.Bodies.Body is
79
let current = context.current
80
81
if
82
_starts_only_a_statement(current.token) /\
83
current.first_on_line /\
84
current.location.start_column > context.tokenizer.indent_of_line(context.previous_end.start_line)
85
then
86
context.error(context.location, "syntax error: expected is but found {context.current_token_name}")
87
88
return _block(context, context.location)
89
fi
90
91
return Trees.Bodies.NULL(context.location)
92
si
93
94
_starts_only_a_statement(token: Lexical.TOKEN) -> bool =>
95
token == Lexical.TOKEN.LET \/
96
token == Lexical.TOKEN.IF \/
97
token == Lexical.TOKEN.WHILE \/
98
token == Lexical.TOKEN.FOR \/
99
token == Lexical.TOKEN.TRY \/
100
token == Lexical.TOKEN.RETURN \/
101
token == Lexical.TOKEN.YIELD \/
102
token == Lexical.TOKEN.THROW \/
103
token == Lexical.TOKEN.ASSERT
104
si
105
si