let c: char = 'A'; /* comment with link https://example and - = */

let respond_no_content = reqd => {
  Reqd.respond_with_string(reqd, Response.create(`No_content), "");
};

let to_meth =
  fun
  | `GET => `GET
  | `POST => `POST
  | `HEAD => `HEAD
  | `DELETE => `DELETE
  | `PUT => `PUT
  | `OPTIONS => `OPTIONS
  | `TRACE => `TRACE
  | `CONNECT => `CONNECT
  | `Other(w) => failwithf("%s is not supported", w, ());


let ignore: 'a => unit = _ => ();

/* **** comment */
/*** comment */
/** docstring */;
/* comment */
/** docstring */;
/*** comment */
/**** comment */
/***** comment */

/*** */
/**** */

/***/
/****/

/* (** comment *) */
/* (*** comment *) */
/* *(*** comment *) */

/* comment **/
/* comment ***/
/* comment ****/
/* comment *****/

let testingNotQuiteEndOfLineComments = [
  "Item 1" /* Comment For First Item */,
  "Item 2" /* Comment For Second Item */,
  "Item 3" /* Comment For Third Item */,
  "Item 4" /* Comment For Fourth Item - but no semi */
  /* Comment after last item in list. */
]; /* Comment after list bracket */

let testingEndOfLineComments = [
  "Item 1", /* Comment For First Item */
  "Item 2", /* Comment For Second Item */
  "Item 3", /* Comment For Third Item */
  "Item 4" /* Comment For Fourth Item - but before semi */,
  /* Comment after last item in list. */
]; /* Comment after list bracket */

/* This time no space between bracket and comment */
let testingEndOfLineComments = []; /* Comment after list bracket */

type t = (int, int); /* End of line on t */

type t22 =
  /* End of t22 line on type t22 = */
  (int, int);

type variant =
  /* Comment above X */
  | X(int) /* End of line on X */
  /* Comment above Y */
  | Y(int); /* End of line on Y */
/* Comment on entire type def for variant */

type x = {
  /* not attached *above* x */
  fieldOne: int,
} /* Attached end of line after x */
and y = {
  /* not attached *above* y */
  fieldTwo: int,
}; /* Attached end of line after y */

let result =
  switch (X(3)) {
  | X(x) =>
    /* Where does this comment go? */
    let tmp = x;
    x + tmp;
  | Y(x) =>
    /* How about this one */
    let tmp = x;
    x + tmp;
  };

let result =
  switch (None) {
  | Some({fieldOne: 20}) =>
    /* Where does this comment go? */
    let tmp = 0;
    2 + tmp;
  | Some({fieldOne: n}) =>
    /* How about this one */
    let tmp = n;
    n + tmp;
  | None => 20
  };

type pointWithManyKindsOfComments = {
  /* Line before x */
  x: string, /* x field */
  /* Line before y */
  y: string /* y field */
  /* Final row of record */
};

type typeParamPointWithComments('a) = {
  /* Line before x */
  x: 'a, /* x field */
  /* Line before y */
  y: 'a /* y field */
  /* Final row of record */
};

let name_equal = (x, y) => x == y;

let equal = (i1, i2) =>
  i1.contents === i2.contents && true; /* most unlikely first */

let equal = (i1, i2) =>
  compare(compare(0, 0), compare(1, 1)); /* END OF LINE HERE */

module Temp = {
  let v = true;
  let logIt = (str, ()) => print_string(str);
};

let store_attributes = arg => {
  let attributes_file = "test";
  let proc_name = attributes_file ++ ".proc";
  let should_write =
    /* only overwrite defined procedures */
    Temp.v || !Temp.v;
  if (should_write) {
    Temp.logIt(proc_name, ());
  };
};

let run = () => {
  TestUtils.printSection("Basic Structures");
};

while (something) {
  print_string("You're in a while loop");
  print_newline();
};

for (i in 0 to 5) {
  print_int(i);
  print_newline();
  for (i in 10 downto 0) {
    print_string(
      "Counting in reverse direction",
    );
    print_newline();
  };
};

for (i in
     0 to
     endOfRangeMustBeSimple(expr, soWrap)) {
  print_int(i);
  print_newline();
  for (i in
       theSame(isTrue, ofThe, startOfRange) downto
       0) {
    print_string(
      "Counting in reverse direction",
    );
    print_newline();
  };
};

let x = foo^ ^.bar^;

let x = foo.bar^;

let x = foo#bar^;

let x = foo^.bar^;

let x = (foo^)#bar^;

/* Prefix operators:
 * ! followed by zero or more appropriate_operator_suffix_chars (see the
 * lexer).
 * ? or ~ followed by at least one appropriate_operator_suffix_chars.
 */
let x = !(!(!foo)).bar;

let x = !foo.bar;

let x = !foo#bar;

let x = !(!foo).bar;

let x = !(!foo)#bar;

let x = !(!foo.bar);

let x = ?!(!foo.bar);

let x = ! ?!foo.bar;

let x = ~!(!foo.bar);

let x = ! ~!foo.bar;

let x = ~! ~!foo.bar;

let x = !!foo.bar;

let x = !!foo#bar;

let x = !~foo.bar;

let x = !~foo#bar;

let noParensNeeded = !blah.foo.bar;

let parensNeededAroundFirst = (!blah).foo.bar;

let parensNeededAroundSecond = (!blah.foo).bar;

let noParensNeeded = !blah#foo#bar;

let parensNeededAroundFirst = (!blah)#foo#bar;

let parensNeededAroundSecond = (!blah#foo)#bar;

let parensWithSpaceNeededAroundFirst =
  (!(!blah))#foo#bar;

let parensWithSpaceNeededAroundSecond =
  (!(!blah#foo))#bar;

let parensWithSpaceNeededAroundFirst =
  (?!(+ blah))#foo#bar;

let parensWithSpaceNeededAroundSecond =
  (?!(+ blah#foo))#bar;

let x = !(!foo.bar);

let x = !(!foo#bar);

let x = (-10);

let x = (-5.0);

let x = Some(-10);

let x = Some(-5.0);

let lazy x = 10;
let lazy (x: int) = 10;
let lazy [] = 10;
let lazy true = 10;
let lazy #x = 10;
let lazy `Variant = 10;
let lazy `variant = 10;
let lazy '0'..'9' = 10;
let lazy (lazy true) = 10;
let lazy [%extend] = 10;

/* Test precedence on access sugar */
let x = arr^[0];

let x = arr^[0];

let x = str^.[0];

let x = str^.[0];

let x = arr^[0] = 1;

let x = arr^[0] = 1;

/* Comments */
/*Below is an empty comment*/
/**/;
/**                            IF
 *============================================================================
 */;

let (/++) = (+); /* // indicates the start of a comment, not an infix op */

let something =
  if (self.ext.logSuccess) {
    print_string("Did tap");
    print_newline();
  };

let logTapSuccess = self =>
  if (self.ext.logSuccess) {
    print_string("Did tap");
    print_newline();
  } else {
    ();
  };

let logTapSuccess = self =>
  if (self.ext.logSuccess) {
    print_string("Did tap");
    print_newline();
  };

(!data).field = true;
(!data).field1.field2 = true;
(!data.field1).field2 = true;
(!data).field1.field2 = true;
(!data.field1).field2 = true;

let loop = (appTime, frameTime) => {
  if (hasSetup.contents) {
    setupScene();
    renderIntoTop();
    hasSetup.contents = true;
  };
  process(appTime, frameTime);
};

/* These parens should be kept around the entire last if/then/else */
if (something) {
  if (somethingElse) {()} else {"blah"};
};

/* These parens should be kept around just the last if/then*/
if (something) {
  if (somethingElse) {()} else {"blah"};
};

/* Parens should be generated to wrap the entire final if then else.
 * To test that it's being parsed correclty, should print "one". */
if (true) {
  if (true) {
    print_string("one");
  } else {
    print_string("two");
  };
};

/* Should print two */
if (true) {
  if (false) {
    print_string("one");
  } else {
    print_string("two");
  };
};

/* Should not print */
if (false) {
  if (true) {
    print_string("one");
  } else {
    print_string("two");
  };
};

/* Should wrap (if a > b then a else b).
 * printer(
 */
let printIfFirstArgGreater = true;
let result =
  if (printIfFirstArgGreater) {
    (a, b) =>
      if (a > b) {
        print_string("a > b");
      } else {
        print_string("b >= a");
      };
  } else if ({
               (a, b) =>
                 if (a > b) {
                   print_string("b < a");
                 } else {
                   print_string("a <= b");
                 };
             }) {
    print_string(
      "That could never possibly type check",
    );
    print_newline();
  };

let myRecord = {
  nestedRecord: {
    anotherNestedRecord:
      (instaComp, displayRect) =>
      if (Graphics.cgRectIntersectsWithSlop(
            defaultCompositeTimerRectSlop,
            instaComp.relativeRect,
            displayRect,
          )) {
        IoEligible;
      } else {
        IoInelibleButTryComposition;
      },
  },
};

if (printIfFirstArgGreater) {
  (a, b) =>
    if (a > b) {
      print_string("a > b");
    };
} else {
  (a, b) =>
    if (a > b) {
      print_string("b < a");
    };
};
/* Should Be Parsed As: Cleary a type error, but at least the parsing makes that clear */
if (printIfFirstArgGreater) {
  (a, b) =>
    if (a > b) {
      print_string("a > b");
    } else {
      (a, b) =>
        if (a > b) {
          print_string("b < a");
        };
    };
};

(a, b) =>
  if (a > b) {
    print_string("a > b");
  };

/* What you probably wanted was: */
if (printIfFirstArgGreater) {
  (a, b) =>
    if (a > b) {
      print_string("a > b");
    };
} else {
  (a, b) =>
    if (a > b) {
      print_string("b < a");
    };
};

/* Mutative if statement: Not used to evaluate to something. */
if (10 < 100) {
  let msg = "If there was any doubt, 10 is in fact less than 100.";
  print_string(msg);
} else {
  let msg = "All bets are off.";
  print_string(msg);
};

if (10 < 100) {
  print_string(
    "If there was any doubt, 10 is in fact less than 100.",
  );
} else {
  print_string("All bets are off.");
};

/**                            TYPE CONSTRAINTS
 *============================================================================
 */;
let x: int = 10;
let x: int = 10;
let x: int = 10;
let x: int = (10: int);
/* let (x:int) = (10:string); */
/* let (x:string) = ("hello":int); */

/**                            TUPLES
 *============================================================================
 */;

/* In Reason, types look like the data they model! Tuples are no exception. */
type pairOfInts = (int, int);
let letBindingWithTypeConstraint: int = 10;
let (tupleItem: int, withTypeConstraint: int) = (
  10,
  20,
);

/* To make sure that tuple field annotations are annotating the entire field */
let _dummyFunc = x => 10;
let annotatingFuncApplication = (
  _dummyFunc("a"): int,
  _dummyFunc("a"): int,
);

/* Pretty printer might stick the [int] at the label. */
let annotatingSingleFuncApplication: int =
  _dummyFunc("a");

/* So lets try a place where it won't */
let annotatingSingleFuncApplication = {
  /* Commenting a let binding. */
  let a = 100;
  /* Commenting another let binding. */
  let int = 200;
  /*
   * This demonstrates why named arguments cannot simply have the form (func
   * arg:val) - it is indistinguishable from a type constraint.
   */
  2 + (_dummyFunc(a): int);
};

let (
  tupleItem: int,
  constrainedWithoutGrouping: int,
) = (
  10,
  20,
);
let (tupleItem, withOutsideTypeConstraint): (
  int,
  int,
) = (
  10,
  20,
);

/* Trailing commas */
let trailingCommaAccepted = (1, 2);
let moreTrailing = (1, 2, 3, 4, 5, 7);

/**                        Immutable Lists
 * ============================================================================
 */;

/* Anatomy:        -Head-      --------- Tail---------  nil: You can't see nil */
let x: list(int) = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let hd = "appendedToHead";
let tl = ["listTo", "append", "to"];

/* To push *one* and only *one* item to the front of a list - use [hd, ...tl] */
let result: list(string) = [hd, ...tl];

/* Is the same as writing */
let result: list(string) = [
  "appendedToHead",
  "listTo",
  "append",
  "to",
];

/* To operate on lists, use pattern matching */
let rec size =
  fun
  | [] => 0
  | [hd, ...tl] => 1 + size(tl);

/* Optimize for tail recursion */
let rec size = (soFar, lst) =>
  switch (lst) {
  | [] => 0
  | [hd, ...tl] => size(soFar + 1, tl)
  };

let nestedMatch = lstLst =>
  switch (lstLst) {
  | [hd, ...tl] when false => 10
  | [hd, ...tl] =>
    switch (tl) {
    | [] => 0 + 0
    | [tlHd, ...tlTl] => 0 + 1
    }
  | [] => 0
  };

let nestedMatchWithWhen = lstLst =>
  switch (lstLst) {
  | [hd, ...tl] when false => 10
  | [hd, ...tl] when true =>
    switch (tl) {
    | [] when false => 0 + 0
    | [] when true => 0 + 0
    | [tlHd, ...tlTl] => 0 + 1
    }
  | [] => 0
  };

/**
 * Aliasing with "as" during matches.
 */;
type mine =
  | MyThing(int)
  | YourThing(int);
/*
 * Reason parses "as" aliases differently than OCaml.
 */
let ppp =
  switch (MyThing(20)) {
  | MyThing(x) as ppp
  | YourThing(x) as ppp => ppp
  };

let MyThing(_) as ppp | YourThing(_) as ppp = ppp;

/*
 * in order to achieve the previous example in ocaml, you would have to group
 * as:
 */
let ppp =
  switch (MyThing(20)) {
  | MyThing(x) as ppp
  | YourThing(x) as ppp => ppp
  };

let MyThing(_) as ppp | YourThing(_) as ppp = ppp;
/*
 * But this isn't needed in Reason because OR patterns have much lower
 * precedence - they should be pretty printed in the same way.
 */

/* TODO: */
/* let rec nestedMatch lstLst => match lstLst with { */
/*   hd::tl: match tl with { */
/*     []: 0 + 0, */
/*     tlHd::tlTl: 0 + 1, */
/*   }, */
/*   []: 0 */
/* }; */
/*  */

/**                               ARRAYS
 * ============================================================================
 * Arrays are weird looking. Usually you want lists because they support pattern
 * matching - that's why they have nicer syntax - to entice you. But if you want
 * random access and better control over memory layout, use arrays.
 */;
let emptyArray = [||];
let arrayWithOne = [|10|];
let arrayWithTwo = [|10, 10|];
let secondItem = arrayWithTwo[1];

/* Getting And Setting: Yeah, we should really change this */
/* Get an array item at index 1 */
let secondItem = arrayWithTwo[1];
/* Set an array item at index 1 */
arrayWithTwo[1] = 300;

/**
 *                                STRINGS
 *  ============================================================================
 *  The language supports mutating strings, but that should not be depended upon.
 */;
let myString = "asdf";
myString.[2] = '9'; /* Replacing a character: I could do without this sugar */

/*                           FUNCTIONS
 *=============================================================================
 */

/*                           TYPE ANNOTATIONS
 * =============================================================================
 */

let one = 900;
let two = 10000;
/* Tuple expressions can be annotated without additional paren wrapping */
let myTuple = (one: int, two: int);
type myTupleType = (int, int);
let myTuple: myTupleType = myTuple;

/* Anything *outside* of a tuple, must still be annotated within parens. */
let myTuple: myTupleType = (one: int, two: int);

/* Now functions that accept a single argument being a tuple look familiar */
let addValues = (a: int, b: int) => {
  a + b;
};

let addValues = (a: int, b: int) => {
  a + b;
};

let myFunction = (a: int, b: int): int => a + b;

let functionReturnValueType =
    (i: int, s: string): (int => int) =>
  x => x + 1;

let curriedFormOne = (i: int, s: string) =>
  s ++ string_of_int(i);

let curriedFormTwo =
    (i: int, x: int): (int, int) => (
  i,
  x,
);
/* let nonCurriedFormTwo = fun (i:int, x:int) (:(int, int)) => (i, x); */

let curriedFormThree =
    (i: int, (a: int, b: int): (int, int))
    : (int, int, int) => (
  i,
  a,
  b,
);

/* let nonCurriedFormThree = fun (i:int, (a:int, b:int):(int, int)) (:(int, int, int)) => (i, a, b);  */

/** TODO: But this, however doesn't work.
 *  let (myCurriedFunc: int => int) a => a;
 *  Note: This is likely because only "simple patterns" are accepted as constraints
 *  in let bindings - that may be easy to change.
 */;

type myFuncType = (int, int) => int;

let myFunc: myFuncType = (a, b) => a + b;

let funcWithTypeLocallyAbstractTypes =
    (
      type atype,
      type btype,
      a,
      b,
      c: (atype, btype) => unit,
    ) =>
  c(a, b);

/* Checks that function types aren't unnecessary wrapped */
type a = unit => unit;

type b =
  | Foo(unit => unit)
  | Bar(unit => unit, unit => unit, (a, b) => c)
  | Baz(
      unit => unit,
      unit => unit,
      (a, b) => c,
    );

type c =
  | Foo((a, b) => unit)
  | Bar((a, b) => unit);

type d = [> | `Foo(unit => unit)];

/**
 * Records:
 *=============================================================================
 */;

type withThreeFields = {
  name: string,
  age: int,
  occupation: string,
};

let testRecord = {
  name: "joe",
  age: 20,
  occupation: "engineer",
};
let anotherRecord = {
  ...testRecord,
  name: "joe++",
  age: testRecord.age + 10,
};

let makeRecordBase = () => {
  name: "Joe",
  age: 30,
  occupation: "Engineer",
};
let anotherRecord = {
  /* These parens should be evaporated. */
  ...makeRecordBase(),
  name: "joe++",
  age: testRecord.age + 10,
};

let anotherRecord = {
  /* Comments should be correctly placed before ... expression */
  ...makeRecordBase(),
  /* Comment after record extension */
  name: "joe++",
  age: testRecord.age + 10,
};

let anotherRecord = {
  /* Currently, type annotations must be wrapped in parens - that's easy to improve */
  ...(makeRecordBase(): withThreeFields),
  name: "joe++",
  age: testRecord.age + 10,
};

let anotherRecord = {
  /* This is meaningless, sure */
  ...someArray.[0] = 20,
  name: "joe++",
  age: testRecord.age + 10,
};

let anotherRecord = {
  ...
    SomeReally.longFunctionCall({
      passingRecordField: 0,
      andThisOtherRecordField: 10,
    }),
  name: "joe++",
  age: testRecord.age + 10,
};

let anotherRecord = {
  ...
    SomeReally.longFunctionCall(
      withArguments,
      thatWrap: bool,
    ),
  name: "joe++",
  age: testRecord.age + 10,
};

let anotherRecord = {
  ...
    SomeReally.longFunctionCall(
      withArg,
      [
        "and",
        "final",
        "list",
        "that",
        "should",
        "break",
      ],
    ),
  name: "joe++",
  age: testRecord.age + 10,
};

/* Record type punning */
type props = {title: string};

type state = unit;

type component = {props};

type component2 = {
  props,
  state,
  updater: unit,
};

type component3 = {
  props: M.props,
  state,
};

type mutableComponent = {mutable props};

type mutabeleComponent2 = {
  mutable props,
  mutable state,
  style: int,
};

/* Don't pun parameterized types */
type description('props) = {
  element: string,
  tag: tag('props),
};

/* Don't pun types from other modules */
module Foo = {
  type bar = {foo: Baz.foo};
};

/* Don't pun field names that aren't "simple" */
type foo = {
  bar: Baz.bar,
  qux,
  fooo: Fooo.fooo,
};

let moreFoo = {
  bar: Baz.bar,
  qux,
  fooo: Fooo.fooo,
};

/* record value punning */

let props = {title: "hi"};
/* no punning available for a single field. Can't tell the difference with a scope + expression */
let componentA = {props: props};
/* pun for real */
let componentB = {props, state: ()};
/* pun fields with module prefix too */
let foo = {Foo.foo: foo};
let bar = {Foo.foo, bar: 1};
let bar = {bar: 1, Foo.foo};
let bar = {Foo.foo, Bar.bar};

({M.x, y}) => 1;

switch (foo) {
| {y: 1, M.x} => 2
};

/* Requested in #566 */
let break_after_equal =
  no_break_from_here(some_call(to_here));

/* Pexp_letexception */
let () = {
  exception E;
  raise(E);
};

/* # 1587: don't print fun keyword when printing Pexp_fun in a record expression  */
{contents: () => ((): unit)};

/* #1556: Always break nested record/obj */
let z = {
  a: {
    b: c,
    d: e,
  },
  f: g,
};

let z = {
  a: {
    "b": c,
    "d": e,
  },
  f: g,
};

let z = {
  a: {
    pub b = c;
    pub d = e
  },
  f: g,
};

let z = {
  "a": {
    "b": c,
    "d": e,
  },
  "f": g,
};

let z = {
  "a": {
    b: c,
    d: e,
  },
  "f": g,
};

let z = {
  "a": {
    pub b = c;
    pub d = e
  },
  "f": g,
};

/**
 * Unnecessary parens should be removed.
 */
let unitLambda = () => ();
let identifierLambda = a => ();
let underscoreLambda = _ => ();
it("should remove parens", a => {
  print_string("did it work?");
  print_string("did it work?");
});

foo(preserveBraces => {inCallback});

foo(preserveBraces => {inFirstPos}, secondArg);

foo(
  oneArg,
  preserveBraces => {inFirstPos},
  secondArg,
);
