()
| 781 | } |
| 782 | |
| 783 | parseChain(): AST { |
| 784 | const exprs: AST[] = []; |
| 785 | const start = this.inputIndex; |
| 786 | while (this.index < this.tokens.length) { |
| 787 | const expr = this.parsePipe(); |
| 788 | exprs.push(expr); |
| 789 | |
| 790 | if (this.consumeOptionalCharacter(chars.$SEMICOLON)) { |
| 791 | if (!(this.parseFlags & ParseFlags.Action)) { |
| 792 | this.error('Binding expression cannot contain chained expression'); |
| 793 | } |
| 794 | while (this.consumeOptionalCharacter(chars.$SEMICOLON)) {} // read all semicolons |
| 795 | } else if (this.index < this.tokens.length) { |
| 796 | const errorIndex = this.index; |
| 797 | this.error(`Unexpected token '${this.next}'`); |
| 798 | // The `error` call above will skip ahead to the next recovery point in an attempt to |
| 799 | // recover part of the expression, but that might be the token we started from which will |
| 800 | // lead to an infinite loop. If that's the case, break the loop assuming that we can't |
| 801 | // parse further. |
| 802 | if (this.index === errorIndex) { |
| 803 | break; |
| 804 | } |
| 805 | } |
| 806 | } |
| 807 | if (exprs.length === 0) { |
| 808 | // We have no expressions so create an empty expression that spans the entire input length |
| 809 | const artificialStart = this.offset; |
| 810 | const artificialEnd = this.offset + this.input.length; |
| 811 | return new EmptyExpr( |
| 812 | this.span(artificialStart, artificialEnd), |
| 813 | this.sourceSpan(artificialStart, artificialEnd), |
| 814 | ); |
| 815 | } |
| 816 | if (exprs.length == 1) return exprs[0]; |
| 817 | return new Chain(this.span(start), this.sourceSpan(start), exprs); |
| 818 | } |
| 819 | |
| 820 | private parsePipe(): AST { |
| 821 | const start = this.inputIndex; |
no test coverage detected