Appearance
| 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 |