package com.semmle.jcorn;

import java.util.ArrayList;
import java.util.List;

import com.semmle.js.ast.AngularPipeRef;
import com.semmle.js.ast.CallExpression;
import com.semmle.js.ast.Expression;
import com.semmle.js.ast.Identifier;
import com.semmle.js.ast.Position;
import com.semmle.js.ast.SourceLocation;

/**
 * Parser for Angular template expressions, based on the JS parser with
 * modified handling of the pipe operator.
 */
public class AngularExpressionParser extends CustomParser {
  public AngularExpressionParser(Options options, String input, int startPos) {
    super(options, input, startPos);
  }

  @Override
  protected Expression buildBinary(
      int startPos,
      Position startLoc,
      Expression left,
      Expression right,
      String op,
      boolean logical) {
    // Angular pipe expression: `x|f:a` is desugared to `f(x, a)`
    if (op.equals("|")) {
      DestructuringErrors refDestructuringErrors = new DestructuringErrors();
      List<Expression> arguments = new ArrayList<>();
      arguments.add(left);
      while (this.type == TokenType.colon) {
        this.next();
        int argStartPos = this.pos;
        Position argStartLocation = this.curPosition();
        Expression arg = parseMaybeUnary(refDestructuringErrors, false);
        arguments.add(parseExprOp(arg, argStartPos, argStartLocation, TokenType.plusMin.binop, true));
      }
      SourceLocation loc = new SourceLocation(startLoc);
      if (right instanceof Identifier) {
        right = new AngularPipeRef(new SourceLocation(right.getLoc()), (Identifier)right);
      }
      return this.finishNode(new CallExpression(loc, right, new ArrayList<>(), arguments, false, false));
    }
    return super.buildBinary(startPos, startLoc, left, right, op, logical);
  }
}

@@ -303,16 +303,59 @@
 ocation;
+%0Aimport com.semmle.ts.ast.NonNullAssertion;
 %0A%0A/**%0A *
@@ -1816,11 +1816,464 @@
 al);%0A  %7D
+%0A%0A  @Override%0A  protected Expression parseExprAtom(DestructuringErrors refDestructuringErrors) %7B%0A    // Parse postfix %22!%22 operator%0A    Position startLoc = this.startLoc;%0A    Expression expr = super.parseExprAtom(refDestructuringErrors);%0A    if (this.type == TokenType.prefix && %22!%22.equals(this.value)) %7B%0A      this.next(); // consume %22!%22 token%0A      return finishNode(new NonNullAssertion(new SourceLocation(startLoc), expr));%0A    %7D%0A    return expr;%0A  %7D
 %0A%7D%0A

Support postfix "!" operator in templates