()
| 818 | } |
| 819 | |
| 820 | private parsePipe(): AST { |
| 821 | const start = this.inputIndex; |
| 822 | let result = this.parseExpression(); |
| 823 | if (this.consumeOptionalOperator('|')) { |
| 824 | if (this.parseFlags & ParseFlags.Action) { |
| 825 | this.error(`Cannot have a pipe in an action expression`); |
| 826 | } |
| 827 | |
| 828 | do { |
| 829 | const nameStart = this.inputIndex; |
| 830 | let nameId = this.expectIdentifierOrKeyword(); |
| 831 | let nameSpan: AbsoluteSourceSpan; |
| 832 | let fullSpanEnd: number | undefined = undefined; |
| 833 | if (nameId !== null) { |
| 834 | nameSpan = this.sourceSpan(nameStart); |
| 835 | } else { |
| 836 | // No valid identifier was found, so we'll assume an empty pipe name (''). |
| 837 | nameId = ''; |
| 838 | |
| 839 | // However, there may have been whitespace present between the pipe character and the next |
| 840 | // token in the sequence (or the end of input). We want to track this whitespace so that |
| 841 | // the `BindingPipe` we produce covers not just the pipe character, but any trailing |
| 842 | // whitespace beyond it. Another way of thinking about this is that the zero-length name |
| 843 | // is assumed to be at the end of any whitespace beyond the pipe character. |
| 844 | // |
| 845 | // Therefore, we push the end of the `ParseSpan` for this pipe all the way up to the |
| 846 | // beginning of the next token, or until the end of input if the next token is EOF. |
| 847 | fullSpanEnd = this.next.index !== -1 ? this.next.index : this.input.length + this.offset; |
| 848 | |
| 849 | // The `nameSpan` for an empty pipe name is zero-length at the end of any whitespace |
| 850 | // beyond the pipe character. |
| 851 | nameSpan = new ParseSpan(fullSpanEnd, fullSpanEnd).toAbsolute(this.absoluteOffset); |
| 852 | } |
| 853 | |
| 854 | const args: AST[] = []; |
| 855 | while (this.consumeOptionalCharacter(chars.$COLON)) { |
| 856 | args.push(this.parseExpression()); |
| 857 | |
| 858 | // If there are additional expressions beyond the name, then the artificial end for the |
| 859 | // name is no longer relevant. |
| 860 | } |
| 861 | let type: BindingPipeType; |
| 862 | if (this.supportsDirectPipeReferences) { |
| 863 | const charCode = nameId.charCodeAt(0); |
| 864 | type = |
| 865 | charCode === chars.$_ || (charCode >= chars.$A && charCode <= chars.$Z) |
| 866 | ? BindingPipeType.ReferencedDirectly |
| 867 | : BindingPipeType.ReferencedByName; |
| 868 | } else { |
| 869 | type = BindingPipeType.ReferencedByName; |
| 870 | } |
| 871 | |
| 872 | result = new BindingPipe( |
| 873 | this.span(start), |
| 874 | this.sourceSpan(start, fullSpanEnd), |
| 875 | result, |
| 876 | nameId, |
| 877 | args, |
no test coverage detected