Appearance
| 1 | namespace Syntax.Parsers.Expressions is | |
| 2 | use IO.Std; | |
| 3 | ||
| 4 | use Source; | |
| 5 | ||
| 6 | class LIST(expression_parser: Parser[Trees.Expressions.Expression]): Base[Trees.Expressions.LIST] is | |
| 7 | super(); | |
| 8 | ||
| 9 | description: string => "expression list"; | |
| 10 | ||
| 11 | parse(context: CONTEXT) -> Trees.Expressions.LIST is | |
| 12 | let start = context.location; | |
| 13 | let expressions = Collections.LIST[Trees.Expressions.Expression](); | |
| 14 | ||
| 15 | do | |
| 16 | context.allow_tuple_element = true; | |
| 17 | let expression = expression_parser.parse(context); | |
| 18 | context.allow_tuple_element = false; | |
| 19 | ||
| 20 | // The expression parser chain bottoms out at a poisoned | |
| 21 | // placeholder rather than null, but that's an invariant of | |
| 22 | // the current wiring, not a contract: guard here so a | |
| 23 | // future element parser without that override can't put | |
| 24 | // a null in the list. | |
| 25 | if !expression? then | |
| 26 | break; | |
| 27 | fi | |
| 28 | ||
| 29 | expressions.add(expression); | |
| 30 | ||
| 31 | if context.current.token != Lexical.TOKEN.COMMA then | |
| 32 | return Trees.Expressions.LIST(start::expression.location, expressions); | |
| 33 | fi | |
| 34 | ||
| 35 | context.next_token(); | |
| 36 | ||
| 37 | // Trailing comma: stop when a closing bracket follows. | |
| 38 | // The same parser drives list literals (`[a, b,]`), | |
| 39 | // tuple literals (`(a, b,)`) and call argument lists | |
| 40 | // (`f(a, b,)`), so one check covers all three. | |
| 41 | if | |
| 42 | context.current.token == Lexical.TOKEN.SQUARE_CLOSE \/ | |
| 43 | context.current.token == Lexical.TOKEN.PAREN_CLOSE | |
| 44 | then | |
| 45 | return Trees.Expressions.LIST(start::context.location, expressions); | |
| 46 | fi | |
| 47 | od | |
| 48 | ||
| 49 | return Trees.Expressions.LIST(start::context.location, expressions); | |
| 50 | si | |
| 51 | si | |
| 52 | si |