@Override
  public void meet(MathExpr expr) throws RuntimeException {
    ValueType ot = new OPTypeFinder(expr).coerce();

    if (ot == ValueType.STRING) {
      if (expr.getOperator() == MathExpr.MathOp.PLUS) {
        builder.append(
            functionRegistry
                .get(FN.CONCAT.stringValue())
                .getNative(
                    parent.getDialect(),
                    new ValueExpressionEvaluator(expr.getLeftArg(), parent, ot).build(),
                    new ValueExpressionEvaluator(expr.getRightArg(), parent, ot).build()));
      } else {
        throw new IllegalArgumentException(
            "operation " + expr.getOperator() + " is not supported on strings");
      }
    } else {
      if (ot == ValueType.NODE || ot == ValueType.TERM) {
        ot = ValueType.DOUBLE;
      }

      optypes.push(ot);
      expr.getLeftArg().visit(this);
      builder.append(getSQLOperator(expr.getOperator()));
      expr.getRightArg().visit(this);
      optypes.pop();
    }
  }
  @Override
  public void meet(IRIFunction fun) throws RuntimeException {
    if (fun.getBaseURI() != null) {

      String ex = new ValueExpressionEvaluator(fun.getArg(), parent, ValueType.STRING).build();

      builder
          .append("CASE WHEN position(':' IN ")
          .append(ex)
          .append(") > 0 THEN ")
          .append(ex)
          .append(" ELSE ")
          .append(
              functionRegistry
                  .get(FN.CONCAT.stringValue())
                  .getNative(parent.getDialect(), "'" + fun.getBaseURI() + "'", ex))
          .append(" END ");
    } else {
      // get value of argument and express it as string
      optypes.push(ValueType.STRING);
      fun.getArg().visit(this);
      optypes.pop();
    }
  }