Appearance
| 1 | namespace Syntax.Parsers.Statements is | |
| 2 | use System.Exception; | |
| 3 | ||
| 4 | use IO.Std; | |
| 5 | ||
| 6 | use Source; | |
| 7 | ||
| 8 | use Ghul.Pipes; | |
| 9 | ||
| 10 | class LIST( | |
| 11 | terminators: Collections.LIST[Lexical.TOKEN], | |
| 12 | statement_parser: Parser[Trees.Statements.Statement] | |
| 13 | ): Base[Trees.Statements.LIST] is | |
| 14 | terminators: Collections.List[Lexical.TOKEN]; | |
| 15 | description: string => "statement list"; | |
| 16 | ||
| 17 | super(); | |
| 18 | ||
| 19 | parse(context: CONTEXT) -> Trees.Statements.LIST is | |
| 20 | self.terminators = terminators; | |
| 21 | ||
| 22 | let start = context.location; | |
| 23 | let end mut = context.location; | |
| 24 | let statements = Collections.LIST[Trees.Statements.Statement](); | |
| 25 | ||
| 26 | let want_semicolon mut = false; | |
| 27 | ||
| 28 | while !context.is_end_of_file /\ !at_terminator(context) do | |
| 29 | if context.current.token == Lexical.TOKEN.SEMICOLON then | |
| 30 | context.next_token(); | |
| 31 | ||
| 32 | // TODO potentially unnecessary semi-colon, but reporting | |
| 33 | // them all is too noisy, at least for the compiler itself | |
| 34 | // which has thousands of them. | |
| 35 | ||
| 36 | want_semicolon = false; | |
| 37 | elif want_semicolon then | |
| 38 | context.next_token(Lexical.TOKEN.SEMICOLON); | |
| 39 | want_semicolon = false; | |
| 40 | else | |
| 41 | try | |
| 42 | let statement = statement_parser.parse(context); | |
| 43 | ||
| 44 | if statement? then | |
| 45 | statements.add(statement); | |
| 46 | want_semicolon = statement.expects_semicolon; | |
| 47 | ||
| 48 | end = statement.location; | |
| 49 | else | |
| 50 | // the statement parser reported its errors and gave up: | |
| 51 | // skip ahead so the unparsed tokens don't produce a | |
| 52 | // cascade of follow-on errors | |
| 53 | end = recover_to_terminator(context); | |
| 54 | fi | |
| 55 | catch e: UnwindException | |
| 56 | throw e; | |
| 57 | catch e: Exception | |
| 58 | end = recover_to_terminator(context); | |
| 59 | yrt | |
| 60 | fi | |
| 61 | od | |
| 62 | ||
| 63 | return Trees.Statements.LIST(start::end, statements); | |
| 64 | si | |
| 65 | ||
| 66 | at_terminator(context: CONTEXT) -> bool => terminators |> any(t => t == context.current_token); | |
| 67 | ||
| 68 | recover_to_terminator(context: CONTEXT) -> LOCATION is | |
| 69 | let end mut = context.current.location; | |
| 70 | ||
| 71 | while !at_terminator(context) do | |
| 72 | end = context.current.location; | |
| 73 | ||
| 74 | context.next_token(); | |
| 75 | od | |
| 76 | ||
| 77 | return end; | |
| 78 | si | |
| 79 | si | |
| 80 | si |