/* Copyright (c) 2015-present, Facebook, Inc. All rights reserved. */

[@autoFormat let wrap=80; let shift=2;];
Modules.run ();
Polymorphism.run ();
Variants.run();
BasicStructures.run();

TestUtils.printSection("General Syntax");
/* Won't work! */
/* let matchingFunc a = match a with */
/*   `Thingy x => (print_string "matched thingy x"); x */
/*   | `Other x => (print_string "matched other x"); x;; */
/*  */
let matchingFunc(a) = switch (a) {
  | `Thingy x => {
    print_string("matched thingy x");
    let zz = 10;
    zz;
  }
  | `Other x => {
    print_string("matched other x");
    x;
  }
};

type firstTwoShouldBeGroupedInParens =
    ((int) => int, int) => int;
type allParensCanBeRemoved =
    (int) => ((int) => ((int) => int));
type firstTwoShouldBeGroupedAndFirstThree =
    (((int) => int) => int) => int;
/* Same thing now but with type constructors instead of each int */
type firstTwoShouldBeGroupedInParens =
    ((list(int)) => list(int), list(int)) => list(int);
type allParensCanBeRemoved =
    (list(int)) => ((list(int)) => ((list(int)) => list(int)));
type firstTwoShouldBeGroupedAndFirstThree =
    (((list(int)) => list(int)) => list(int)) => list(int);

type myRecordType = {
  firstTwoShouldBeGroupedInParens:
    ((int) => int, int) => int,
  allParensCanBeRemoved:
    (int) => ((int) => ((int) => int)),
  firstTwoShouldBeGroupedAndFirstThree:
    (((int) => int) => int) => int,
};

type firstNamedArgShouldBeGroupedInParens =
    (~first: (int) => int, ~second: int) => int;
type allParensCanBeRemoved =
    (~first: int) => ((~second: int) => ((~third: int) => int));
type firstTwoShouldBeGroupedAndFirstThree =
    (~first: ((int) => int) => int) => int;

/* Same thing now, but with type constructors instead of int */
type firstNamedArgShouldBeGroupedInParens =
    (~first: (list(int)) => list(int)) => (~second: list(int)) => list(int);
type allParensCanBeRemoved =
    (~first: list(int)) => (~second: list(int)) => (~third: list(int)) => list(int);
type firstTwoShouldBeGroupedAndFirstThree =
    (~first: ((list(int)) => list(int)) => list(int)) => list(int);


type firstNamedArgShouldBeGroupedInParens =
    (~first: ((int) => int)=?, ~second: list(int)=?) => int;
/* The arrow necessitates parens around the next two args. The ? isn't what
 * makes the parens necessary. */
type firstNamedArgShouldBeGroupedInParensAndSecondNamedArg =
    (~first: ((int) => int)=?, ~second: ((int) => int)=?) => int;
type allParensCanBeRemoved =
    (~first: int=?, ~second: int=?, ~third: int=?) => int;
type firstTwoShouldBeGroupedAndFirstThree =
    (~first: (((int) => int) => int)) => int;

type noParens = (~one: int, int, int, ~two: int) => int;
type noParensNeeded = (~one: int) => ((int) => ((int) => ((~two: int) => int)));
type firstNamedArgNeedsParens = (~one: (int, int) => int, ~two: int) => int;

/* Now, let's try type aliasing */
/* Unless wrapped in parens, types between arrows may not be aliased, may not
 * themselves be arrows. */

type parensRequiredAroundFirstArg = (list(int) as 'a) => int as 'a;

type parensRequiredAroundReturnType = (list(int) as 'a) => (int as 'a);

type parensRequiredAroundReturnType = (list(int) as 'a) => (int as 'a) as 'b;

type noParensNeededWhenInTuple = (list(int) as 'a, list(int) as 'b) as 'entireThing;

type myTypeDef('a) = list('a);

type instatiatedTypeDef = (myTypeDef(int)) => int;

/* Test a type attribute for good measure */
/* We should clean up all of the attribute tagging eventually, but for now,
 * let's make it super ugly to get out of the way of all the formatting/parsing
 * implementations (fewer conflicts during parsing, fewer edge cases during
 * printing).
 */
type something = (int, ([@lookAtThisAttribute] int));

type longWrappingTypeDefinitionExample =
  M_RK__G.Types.instance (
      TGRecognizer.tGFields(unit,unit),
      TGRecognizer.tGMethods(unit,unit)
    )
  ;
type semiLongWrappingTypeDefinitionExample =
  M_RK__Gesture.Types.instance (
    TGRecognizerFinal.tGFields,
    TGRecognizerFinal.tGMethods
  );

type semiLongWrappingTypeWithConstraint =
  M_RK__Gesture.Types.instance('a,
    TGRecognizerFinal.tGFields,
    TGRecognizerFinal.tGMethods
  ) constraint 'a = (unit, unit)
  ;

type onelineConstrain = 'a constraint 'a = int;

/* This must be in trunk but not in this branch of OCaml */
/* type withNestedRecords = MyConstructor {myField: int} */

type colors =
  | Red(int)
  | Black(int)
  | Green(int);

/* Another approach is to require declared variants to wrap any record */
/* type myRecord = MyRecord {name: int}; */
/* let myValue = MyRecord {name: int}; */
/* This would force importing of the module */
/* This would also lend itself naturally to pattern matching - and avoid having
to use `.` operator at all since you normally destructure. */
type nameBlahType = {nameBlah: int};
let myRecord = {nameBlah: 20};
let myRecordName = myRecord.nameBlah;

let {nameBlah}:nameBlahType = {nameBlah: 20};
print_int(nameBlah);
let {nameBlah: aliasedToThisVar}:nameBlahType = {nameBlah: 20};
print_int(aliasedToThisVar);


let desiredFormattingForWrappedLambda:
  (int, int, int) => nameBlahType =
/*

 fun is
 pre-   /firstarg\
 fix   /-coupled--\
  |-\ /-to-prefix--\       */
  fun(curriedArg,
      anotherArg,
      lastArg) => {
    nameBlah: 10
  };

type longerInt = int;
let desiredFormattingForWrappedLambdaWrappedArrow:
  (longerInt,
   longerInt,
   longerInt) =>
  nameBlahType =
/*

 fun is
 pre-   /firstarg\
 fix   /-coupled--\
  |-\ /-to-prefix--\       */
  fun(curriedArg,
      anotherArg,
      lastArg) => {
    nameBlah: 10
  };

let desiredFormattingForWrappedLambdaReturnOnNewLine =
/*

 fun is
 pre-   /firstarg\
 fix   /-coupled--\
  |-\ /-to-prefix--\       */
  fun(curriedArg,
      anotherArg,
      lastArg) =>
  {
    nameBlah: 10
  };


/*
let is
pre-
fix    /-function binding name---\
|-\   / is coupled to prefix      \   */
let desiredFormattingForWrappedSugar
   (curriedArg,
    anotherArg,
    lastArg) {
  nameBlah: 10
};

/*
let is
pre-
fix    /-function binding name---\
|-\   / is coupled to prefix      \   */
let desiredFormattingForWrappedSugarReturnOnNewLine
   (curriedArg,
    anotherArg,
    lastArg)
{
  nameBlah: 10
};


/*
  let  : type t1 t2. t1 * t2 list -> t1 = ...
  let rec f : 't1 't2. 't1 * 't2 list -> 't1 =
    fun (type t1) (type t2) -> (... : t1 * t2 list -> t1)
*/

type point = {x: int, y:int};
type point3D = {x: int, y:int, z:int};
let point2D = {
  x: 20,
  y: 30
};

let point3D:point3D = {
  x: 10,
  y: 11,
  z: 80,    /* Optional Comma */
};

let printPoint (p:point) {
  print_int(p.x);
  print_int(p.y);
};

let addPoints (p1:point, p2:point) {
  x: p1.x + p2.x,
  y: p1.y + p2.y
};

let res1 = printPoint(point2D);
let res2 = printPoint({x:point3D.x, y:point3D.y});

/*
   When () were used to indicate sequences, the parser used seq_expr not only
   for grouping sequences, but also to form standard precedences.
                         /------- sequence_expr ------\
   let res3 = printPoint (addPoints (point2D, point3D));

   Interestingly, it knew that tuples aren't sequences.

   To move towards semi delimited, semi-terminated, braces-grouped sequences:
   while allowing any non-sequence expression to be grouped on parens, we make
   an explicit rule that allows one single non-semi ended expression to be
   grouped in parens.

   Actually: We will allow an arbitrary number of semi-delimited expressions to
   be wrapped in parens, but the braces grouped semi delimited (sequence)
   expressions must *also* be terminated with a semicolon.

   This allows the parser to distinguish between

       let x = {a};    /* Record {a:a} */
       let x = {a;};   /* Single item sequence returning identifier {a} */
*/
let res3 = printPoint (addPoints (point2D, {x:point3D.x, y:point3D.y}));

type person = {age: int, name: string};
type hiredPerson = {age: string, name: string, dateHired: int};

let o: (person) = {name: "bob", age: 10};

/* Parens needed? Nope! */
let o: person = {name: "bob", age: 10};

let printPerson (p: person) {
  let q: person = p;
  p.name ++ p.name;
};

/* let dontParseMeBro x y:int = x = y;*/

/* With this unification, anywhere eyou see `= fun` you can just ommit it */
let blah = fun(a) => a;         /* Done */
let blah (a) = a;               /* Done (almost) */

let blah = fun(a,b) => a;       /* Done */
let blah (a, b) = a;             /* Done (almost) */

/* More than one consecutive pattern must have a single case */
type blah = {blahBlah: int};
let blah = fun (a, {blahBlah}) => a;
let blah (a, {blahBlah}) = a;

module TryToExportTwice = {
  let myVal = "hello";
};

/*
  Unifying top level module syntax with local module syntax is probably a bad
  idea at the moment because it makes it more difficult to continue to support
  `let .. in` bindings. We can distinguish local modules for `let..in` that
  just happen to be defined at the top level (but not exported).

    let MyModule = {let myVal = 20;} in
    MyModule.x

  Wait, where would this ever be valid, even if we continued to support
  `let..in`?
*/

let onlyDoingThisTopLevelLetToBypassTopLevelSequence = {
    let x = {
      print_int(1);
      print_int(20);  /* Missing trailing SEMI */
    };

    let x = {
      print_int(1);
      print_int(20);   /* Ensure missing middle SEMI reported well */
      print_int(20);
    };

    let x = {
      print_int(1);
      print_int(20);
      10;
      /* Comment in final position */
    };  /* Missing final SEMI */
    x + x;
};

type hasA = {a:int};
let a = 10;
let returnsASequenceExpressionWithASingleIdentifier () {a};
let thisReturnsA () {a;};
let thisReturnsAAsWell () = a;

let recordVal:int = thisReturnsARecord().a;
Printf.printf("\nproof that thisReturnsARecord: %n\n", recordVal);
Printf.printf("\nproof that thisReturnsA: %n\n", thisReturnsA());

/* Pattern matching */
let blah = fun(arg) => switch (arg) {
  /* Comment before Bar */
  | /* Comment between bar/pattern */
    Red(_) => 1
  /* Comment Before non-first bar */
  | /* Comment betwen bar/pattern */
    Black(_) => 0
  | Green(_) => 0
};

/* Any function that pattern matches a multicase match is interpretted as a
 * single arg that is then matched on. Instead of the above `blah` example:*/

let blah = fun
  | Red(_) => 1
  | Black(_) => 0
  | Green(_) => 1;

/* `fun a => a` is read as "a function that maps a to a". Then the */
/* above example is read: "a function that 'either maps' Red to.. or maps .." */
/* Thc00f564e first bar is read as "either maps" */


/* Curried form is not supported:
   let blah x | Red _ => 1 | Black _ => 0;
   Theres no sugar rule for dropping => fun, only = fun
*/

/* let blahCurriedX x => fun  /* See, nothing says we can drop the => fun */ */
/*   |(Red x | Black x | Green x) => 1     /* With some effort, we can ammend the sugar rule that would */ */
/*   | Black x => 0                       /* Allow us to drop any => fun.. Just need to make pattern matching */ */
/*   | Green x => 0;                      /* Support that */ */
/*  */

let blahCurriedX(x) =
  fun
  | Red(x)
  | Black(x)
  | Green(x) => 1  /* With some effort, we can ammend the sugar rule that would */
  | Black(x) => 0  /* Allow us to drop any => fun.. Just need to make pattern matching */
  | Green(x) => 0; /* Support that */

let sameThingInLocal = {
  let blahCurriedX(x) =
    fun
    | Red(x)
    | Black(x)
    | Green(x) => 1  /* With some effort, we can ammend the sugar rule that would */
    | Black(x) => 0  /* Allow us to drop any => fun.. Just need to make pattern matching */
    | Green(x) => 0; /* Support that */
  blahCurriedX;

};

/* This should be parsed/printed exactly as the previous */
let blahCurriedX(x) = fun
  | Red(x) | Black(x) | Green(x) => 1
  | Black(x) => 0
  | Green(x) => 0;

/* Any time there are multiple match cases we require a leading BAR */

let v = Red(10);
let (Black(x) | Red(x) | Green(x)) = v;    /* So this NON-function still parses */

/* This doesn't parse, however (and it doesn't in OCaml either):
  let | Black(x) | Red(x) | Green(x) = v;
*/

print_int(x);



/* Scoping: Let sequences. Familiar syntax for lexical ML style scope and
sequences. */

let res = {
  let a = "a starts out as";
  {
    print_string(a);
    let a = 20;
    print_int(a);
  };
  print_string(a);
};

let res = {
  let a = "first its a string";
  let a = 20;
  print_int(a);
  print_int(a);
  print_int(a);
};

let res = {
  let a = "a is always a string";
  print_string(a);
  let b = 30;
  print_int(b);
};


/* let result = LyList.map((fun | [] => true | _ => false), []); */



/* OTHERWISE: You cannot tell if a is the first match case falling through or
 * a curried first arg */
/* let blah = fun a | patt => 0 | anotherPatt => 1; */
/* let blah a patt => 0 | anotherPatt => 1; */

                /*simple pattern  EQUALGREATER      expr */
let blah (    a,      {blahBlah})        =             a;

               /*            match_case             */
               /*     pattern EQUALGREATER  expr */
let blah = fun   |Red(_)          =>      1   |Black(_)=> 0 |Green(_)=> 0;


/* Won't work! */
/* let arrowFunc = fun a b => print_string "returning aplusb from arrow"; a + b;;  */
let arrowFunc = fun(a,b)=> {print_string("returning aplusb from arrow"); a + b;};
let add(a,b) {
  let extra = {print_string("adding"); 0;};
  let anotherExtra = 0;
  extra + a + b + anotherExtra;
};

(print_string (string_of_int (add(4,34))));

let dummy(_) = 10;
dummy(res1);
dummy(res2);
dummy(res3);


/* Some edge cases */
let myFun (firstArg, Red(x) | Black(x) | Green(x)) = firstArg + x;
let matchesWithWhen(a) = switch (a) {
  | Red(x) when 1 > 0 => 10
  | Red(_) => 10
  | Black(x) => 10
  | Green(x) => 10
};

let matchesWithWhen = fun
  | Red(x) when 1 > 0 => 10
  | Red(_) => 10
  | Black(x) => 10
  | Green(x) => 10;


let matchesOne (`Red x) = 10;


/*
Typical OCaml would make you *wrap the functions in parens*! This is because it
can't tell if a semicolon is a sequence operator. Even if we had records use
commas to separate fields,
*/
type adders = {
  addTwoNumbers: (int, int) => int,
  addThreeNumbers: (int, int, int) => int,
  addThreeNumbersTupled: ((int, int, int)) => int
};
let myRecordWithFunctions = {
  addTwoNumbers: (a,b) => a + b,
  addThreeNumbers: (a,b,c) => a + b + c,
  addThreeNumbersTupled: ((a, b, c)) => a + b + c
};

let result = myRecordWithFunctions.addThreeNumbers(10, 20, 30);
let result = myRecordWithFunctions.addThreeNumbersTupled((10, 20, 30));

let lookTuplesRequireParens = (1, 2);
/* let thisDoesntParse = 1, 2;  */
let tupleInsideAParenSequence = {
  print_string("look, a tuple inside a sequence");
  let x = 10;
  (x, x);
};

let tupleInsideALetSequence = {
  print_string("look, a tuple inside a sequence");
  let x = 10;
  (x, x);
};

/* We *require* that function return types be wrapped in
  parenthesis. In this example, there's no ambiguity */
let makeIncrementer (delta:int) : (int)=>int = (a) => a + delta;

/* We could even force that consistency with let bindings - it's allowed
   currently but not forced.
*/
let (myAnnotatedValBinding:int) = 10;

/* Class functions (constructors) and methods are unified in the same way */

class classWithNoArg {
  pub x = 0;
  pub y = 0;
};

/* This parses but doesn't type check
  class myClass init => object
    pub x => init
    pub y => init
  end;
*/

let myFunc (a:int, b:int) : (int, int) = (a, b);
let myFunc (a:int, b:int) : list(int) = [1];
let myFunc (a:int, b:int) : point {
  x: a,
  y: b
};
let myFunc (a:int, b:int) : point {
  x: a,
  y: b
};

type myThing = (int, int);
type stillARecord = {name: string, age: int};

/* Rebase latest OCaml to get the following: And fixup
  `generalized_constructor_arguments` according to master. */
/* type ('a, 'b) myOtherThing = Leaf {first:'a, second: 'b} | Null; */
type branch('a,'b) = {first: 'a, second: 'b};
type myOtherThing('a,'b) = Leaf (branch('a,'b)) | Null;

type yourThing = myOtherThing(int,int);

/* Conveniently - this parses exactly how you would intend! No *need* to wrap
in an extra [], but it doesn't hurt */
/* FIXME type lookAtThesePolyVariants = list [`Red] ; */

/* FIXME type bracketsGroupMultipleParamsAndPrecedence = list (list (list [`Red])); */

/* FIXME type youCanWrapExtraIfYouWant = (list [`Red]); */

/* FIXME type hereAreMultiplePolyVariants = list [`Red | `Black]; */
/* FIXME type hereAreMultiplePolyVariantsWithOptionalWrapping = list ([`Red | `Black]); */



/*
  /* Proposal: ES6 style lambdas: */

  /* Currying */
  let lookES6Style = (`Red x) (`Black y) => { };
  let lookES6Style (`Red x) (`Black y) => { };

  /* Matching the single argument */
  let lookES6Style = oneArg => match oneArg with
    | `Red x => x
    | `Black x => x;

  /* The "trick" to currying that we already have is basically the same - we just
   * have to reword it a bit:
   * From:
   * "Any time you see [let x = fun ...] just replace it with [let x ...]"
   * To:
   * "Any time you see [let x = ... => ] just replace it with [let x ... => ]"
   */
  let lookES6Style oneArg => match oneArg with
    | `Red x => x
    | `Black x => x;

*/


/** Current OCaml Named Arguments. Any aliasing is more than just aliasing!
OCaml allows full on pattern matching of named args. */
/*
A: let named              ~a    ~b                = aa + bb in
B: let namedAlias         ~a:aa ~b:bb             = aa + bb in
C: let namedAnnot         ~(a:int) ~(b:int)       = a + b in
D: let namedAliasAnnot    ~a:(aa:int) ~b:(bb:int) = aa + bb in
E: let optional           ?a    ?b                              = 10 in
F: let optionalAlias      ?a:aa ?b:bb                           = 10 in
G: let optionalAnnot      ?(a:int option) ?(b:int option)       = 10 in
H: let optionalAliasAnnot ?a:(aa:int option) ?b:(bb:int option) = 10 in
/*
Look! When a default is provided, annotation causes inferred type of argument
to not be "option" since it's automatically destructured (because we know it
will always be available one way or another.)
*/
I: let defOptional           ?(a=10)    ?(b=10)                 = 10 in
J: let defOptionalAlias      ?a:(aa=10) ?b:(bb=10)              = 10 in
K: let defOptionalAnnot      ?(a:int=10) ?(b:int=10)            = 10 in
                            \       \
                             \label_let_pattern opt_default: no longer needed in SugarML

L: let defOptionalAliasAnnot ?a:(aa:int=10) ?b:(bb:int=10)      = 10 in
                              \        \
                               \let_pattern: still a useful syntactic building block in SugarML
*/

/**
 * In Reason, the syntax for named args uses double semicolon, since
 * the syntax for lists uses ES6 style [], freeing up the ::.
 */

let a = 10;
let b = 20;

/*A*/
let named                (~a as a,   ~b as b)   = a + b;
type named =             (~a: int, ~b: int) => int;
/*B*/
let namedAlias           (~a as aa,  ~b as bb)  = aa + bb;
let namedAlias           (~a as aa,  ~b as bb)  = aa + bb;
type namedAlias =        (~a: int, ~b: int) => int;
/*C*/
let namedAnnot           (~a :int, ~b :int)  = 20;
/*D*/
let namedAliasAnnot      (~a as aa:int,~b as bb:int) = 20;
/*E*/
let myOptional           (~a=?,    ~b=?,      ()) = 10;
type named =             (~a: int=?, ~b: int=?, unit) => int;
/*F*/
let optionalAlias        (~a as aa=?,  ~b as bb=?,  ()) = 10;
/*G*/
let optionalAnnot        (~a as a:int =?, ~b as b:int=?, ()) = 10;
/*H*/
let optionalAliasAnnot   (~a as aa:int =?, ~b as bb:int=?, ()) = 10;
/*I: */
let defOptional          (~a as a=10, ~b as b=10, ()) = 10;
type named =             (~a: int=?, ~b: int=?, unit) => int;
/*J*/
let defOptionalAlias     (~a as aa=10, ~b as bb=10, ()) = 10;
/*K*/
let defOptionalAnnot     (~a as a:int=10, ~b as b:int=10, ()) = 10;
/*L*/
let defOptionalAliasAnnot(~a as aa:int=10, ~b as bb:int=10, ()) = 10;

/*M: Invoking them - Punned */
let resNotAnnotated = named(~a=a,~b=b);
/*N:*/
let resAnnotated    = (named(~a=a,~b=b):int);
/*O: Invoking them */
let resNotAnnotated = named(~a=a,~b=b);
/*P: Invoking them */
let resAnnotated    = (named(~a=a,~b=b):int);

/*Q: Here's why "punning" doesn't work!  */
/* Is b:: punned with a final non-named arg, or is b:: supplied b as one named arg? */
let b = 20;
let resAnnotated    = (named(~a=a,~b=b));

/*R: Proof that there are no ambiguities with return values being annotated */
let resAnnotated    = (named(~a=a,b):ty);


/*S: Explicitly passed optionals are a nice way to say "use the default value"*/
let explictlyPassed =          myOptional(~a=?None,~b=?None);
/*T: Annotating the return value of the entire function call */
let explictlyPassedAnnotated = (myOptional(~a=?None,~b=?None):int);
/*U: Explicitly passing optional with identifier expression */
let a = None;
let explictlyPassed =           myOptional(~a=?a,~b=?None);
let explictlyPassedAnnotated = (myOptional(~a=?a,~b=?None):int);

let nestedLet = {
  let _ = 1
};

let nestedLet = {
  let _ = 1;
};

let nestedLet = {
  let _ = 1;
  ()
};

let nestedLet = {
  let _ = 1;
  2
};

/*
 * Showing many combinations of type annotations and named arguments.
 */

type typeWithNestedNamedArgs =
    (~outerOne: (~innerOne: int, ~innerTwo: int) => int, ~outerTwo: int) => int;

type typeWithNestedOptionalNamedArgs =
    (~outerOne: (~innerOne: int, ~innerTwo: int) => int=?, ~outerTwo: int=?) => int;

type typeWithNestedOptionalNamedArgs =
    (~outerOne: list(string)=?, ~outerTwo: int=?) => int;

let f(~tuple="long string to trigger line break") = ();

let x =
  callSomeFunction
   (~withArg=10,
    ~andOtherArg=wrappedArg);


let res = {
  (constraintedSequenceItem:string);
  (dontKnowWheYoudWantToActuallyDoThis:string);
};

let res = {
  (butTheyWillBePrintedWithAppropriateSpacing : string);
  (soAsToInstillBestDevelopmentPractices : string);
};

let x = [
  eachItemInListCanBeAnnotated:int,
  typeConstraints:float,
  (tupleConstraints:int, andNotFunctionInvocations:int)
];

let x = [
  butWeWillPrint : int,
  themAsSpaceSeparated : float,
  (toInfluenceYour : int, developmentHabbits : int)
];

let newRecord = {
  ...annotatedSpreadRecord:someRec,
  x: y
};

let newRecord = {
  ...annotatedSpreadRecord : someRec,
  blah: 0,
  foo: 1
};

let newRecord = {
  ...youCanEvenCallMethodsHereAndAnnotate(them): someRec,
  blah: 0,
  foo: 1
};

let newRecord = {
  ...youCanEvenCallMethodsHereAndAnnotate(them,~named=10):someRec,
  blah: 0,
  foo: 1
};


let something = (aTypeAnnotation : thing(blah));
let something = (thisIsANamedArg: thing(blah));


let something = (aTypeAnnotation: thing(blah));



let something = (thisIsANamedArg(thing):blah);
let something = (typeAnnotation(thing): blah);

let newRecord = {
  ...(heresAFunctionWithNamedArgs(~argOne=i) :annotatedResult),
  soAsToInstill: 0,
  developmentHabbits: 1
};


[@thisIsAThing];
let x = 10;

/* Ensure that the parenthesis are preserved here because they are
 * important:
 */
let something =
  fun | None => (fun | [] => "emptyList" | [_, ..._] => "nonEmptyList")
      | Some(_)=> (fun | [] => "emptyList" | [_, ..._] => "nonEmptyList");

/*  A | B = X; */
let A | B = X;

/*  A | (B | C) = X; */
let A | (B | C) = X;

/* (A | B) | (C | D) = X; */
let (A | B) | (C | D) = X;

/*  A | B | (C | D) = X; */
let A | B | (C | D) = X;

/* (A | B) | C = X; */
let (A | B) | C = X;

/*  A | B | C = X; */
let A | B | C = X;


/** External function declaration
 *
 */
external f : (int) => int = "foo";

let x = {contents: 0};

let unitVal = x.contents = 210;

let match = "match";

let method = "method";

let foo(x,~x as bar,~z,~foo as bar,~foo as z) {
  bar + 2
};

let zzz = myFunc(1, 2, [||]);

/* 1492 */
let registerEventHandlers =
    (
      ~window: Window.t,
      ~mouseDown:
          option(((~button: Events.buttonStateT, ~state: Events.stateT, ~x: int, ~y: int) => unit))=?,
      ()
    ) => 1;

/* #1320: record destrucuring + renaming */
let x = ({state: state as prevState}) => 1;
let x = ({ReasonReact.state: state as prevState}) => 1;

/* 1567: optional parens around expr constraint in constructor expression */
Some(x : int);
Some((x : int));
Some(x, y: int, b);
Some(x, (y: int), b);

foo(~x=-. bar);

Some((-1), (-1), (-1));
Some(-1, -1, -1);
Some(-1g, -1G, -1z);
Some((-1g), (-1G), (-1z));
Some(-0.1, -0.1, -0.1);
Some((-0.1), (-0.1), (-0.1));
Some(-0.1G, -0.1x, -0.1H);
Some((-0.1G), (-0.1x), (-0.1H));
Some([@foo] (-1), [@foo] (-1), [@foo] (-1));
Some([@foo] (-1z), [@foo] (-1z), [@foo] (-1z));
Some([@foo] (-0.1), [@foo] (-0.1), [@foo] (-0.1));
Some([@foo] (-0.1m), [@foo] (-0.1n), [@foo] (-0.1p));

foo(~x=-1, ~y=-2);
foo(~x=(-1), ~y=(-2));
foo(~x=-.1, ~y=-.2);
foo(~x=(-.1), ~y=(-.2));
foo(~x=-1g, ~y=-1G, ~z=-1z);
foo(~x=(-1g), ~y=(-1G), ~z=(-1z));
foo(~x=-0.1G, ~y=-0.1x, ~z=-0.1H);
foo(~x=(-0.1G), ~y=(-0.1x), ~z=(-0.1H));
foo(~x=[@foo] (-1), ~y=[@foo] (-1), ~z=[@foo] (-1));
foo(~x=[@foo] (-1z), ~y=[@foo] (-1z), ~z=[@foo] (-1z));
foo(~x=[@foo] (-0.1), ~y=[@foo] (-0.1), ~z=[@foo] (-0.1));
foo(~x=[@foo] (-0.1m), ~y=[@foo] (-0.1n), ~z=[@foo] (-0.1p));

/* Smooth formatting of functions with callbacks as arguments */
funWithCb("text", () => doStuff());

funWithCb([@attr] "text", [@myAttr] () => doStuff());

funWithCb(~text="text", ~f=() => doStuff());

funWithCb(~text=[@attr] "text", ~f=[@myAttr] () => doStuff());

funWithCb(~text="text", ~f=?() => doStuff());

funWithCb(~text=[@attr] "text",  ~f=?[@myAttr] () => doStuff());

test("my test", () => {
 let x = a + b;
 let y = z + c;
 x + y
});

test([@attr] "my test", [@attr] () => {
 let x = a + b;
 let y = z + c;
 x + y
});

test(~desc="my test", ~f=() => {
 let x = a + b;
 let y = z + c;
 x + y
});

test(~desc=[@attr] "my test", ~f=[@myAttr] () => {
 let x = a + b;
 let y = z + c;
 x + y
});

test(~desc=?"my test", ~f=?() => {
  let x = a + b;
  let y = z + c;
  x + y
});

test(~desc=?[@attr] "my test", ~f=?[@attr] () => {
  let x = a + b;
  let y = z + c;
  x + y
});

describe("App", () => {
  test("math", () => {
    Expect.expect(1+2) |> toBe(3)
  });
});

describe([@attr] "App", [@attr] () => {
  test([@attr] "math", [@attr] () => {
    Expect.expect(1+2) |> toBe(3)
  });
});

describe(~text="App", ~f=() =>
  test(~text="math", ~f=() =>
    Expect.expect(1 + 2) |> toBe(3)
  )
);

describe(~text=[@attr] "App", ~f=[@attr] () =>
  test(~text=[@attr] "math", ~f=[@attr] () =>
    Expect.expect(1 + 2) |> toBe(3)
  )
);

describe(~text=?"App", ~f=?() =>
  test(~text=?"math", ~f=?() =>
    Expect.expect(1 + 2) |> toBe(3)
  )
);

describe(~text=?[@attr] "App", ~f=?[@attr] () =>
  test(~text=?[@attr] "math", ~f=?[@attr] () =>
    Expect.expect(1 + 2) |> toBe(3)
  )
);

Thing.map(foo, bar, baz, (abc, z) =>
  MyModuleBlah.toList(argument)
);

Thing.map([@attr] foo, bar, baz, [@attr] (abc, z) =>
  MyModuleBlah.toList(argument)
);

Thing.map(~a=[@attr] foo, ~b=bar, ~c=?baz, ~f=(abc, z) =>
  MyModuleBlah.toList(argument)
);

Thing.map(~a=foo, ~b=bar, ~c=?[@attr] baz, ~f=[@attr] (abc, z) =>
  MyModuleBlah.toList(argument)
);

Thing.map(~a=foo, ~b=bar, ~c=?baz, ~f=?(abc, z) =>
  MyModuleBlah.toList(argument)
);

Thing.map(~a=foo, ~b=bar, ~c=?[@attr] baz, ~f=?[@attr] (abc, z) =>
  MyModuleBlah.toList(argument)
);

Thing.map(
  foo,
  bar,
  baz,
  foo2,
  bakjlksjdf,
  okokokok,
  (abc, z) => {
    let x = 1;
    MyModuleBlah.toList(x, argument);
  }
);

Thing.map(
  foo,
  bar,
  baz,
  foo2,
  [@attr] bakjlksjdf,
  okokokok,
  [@attr] (abc, z) => {
    let x = 1;
    MyModuleBlah.toList(x, argument);
  }
);

Thing.map(
  ~a=foo,
  ~b=bar,
  ~c=baz,
  ~d=foo2,
  ~e=bakjlksjdf,
  ~f=okokokok,
  ~cb=(abc, z) => {
    let x = 1;
    MyModuleBlah.toList(x, argument);
  }
);

Thing.map(
  ~a=foo,
  ~b=bar,
  ~c=[@attr] baz,
  ~d=foo2,
  ~e=bakjlksjdf,
  ~f=okokokok,
  ~cb=[@attr] (abc, z) => {
    let x = 1;
    MyModuleBlah.toList(x, argument);
  }
);

Thing.map(
  ~a=?foo,
  ~b=?bar,
  ~c=?baz,
  ~d=?foo2,
  ~e=?bakjlksjdf,
  ~f=?okokokok,
  ~cb=?(abc, z) => {
    let x = 1;
    MyModuleBlah.toList(x, argument);
  }
);

Thing.map(
  ~a=?foo,
  ~b=?bar,
  ~c=?baz,
  ~d=?foo2,
  ~e=?[@attr] bakjlksjdf,
  ~f=?okokokok,
  ~cb=?[@attr] (abc, z) => {
    let x = 1;
    MyModuleBlah.toList(x, argument);
  }
);

Thing.map(
  foo,
  bar,
  baz,
  (abc, z) => MyModuleBlah.toList(argument),
  (abc, z) => MyModuleBlah.toList(argument)
);

Thing.map(
  foo,
  bar,
  baz,
  [@attr] (abc, z) => MyModuleBlah.toList(argument),
  [@attr] (abc, z) => MyModuleBlah.toList(argument)
);

Js.Option.andThen([@bs] w => w#getThing());

Thing.map(
  ~a=?foo,
  ~b=?bar,
  ~c=?baz,
  ~d=?foo2,
  ~e=?[@attr] bakjlksjdf,
  ~f=?okokokok,
  ~cb=?[@attr] [@attr2] [@aslkdfjlsdjf][@sldkfjlksdjflksjdlkjf] [@sldkflskjdf] (abc, z) => {
    let x = 1;
    MyModuleBlah.toList(x, argument);
  }
);

let result =
  F.call(x => {
    let x = 123;
    let y = 2345;
    doStuff();
  });

let result = F.call(x => doStuff(x));

let () = x |> Bigarray.Genarray.get(_, [|1, 2, 3, 4|]);

let () = x |> Bigarray.Array1.get(_, 1);

let () = x |> Bigarray.Array2.get(_, 1, 2);

let () = x |> Bigarray.Array3.get(_, 1, 2, 3);

let x=-.1;

let x=-1;

let x=+1;

let x=+.1;

let x = (~a: int=- 1) => a;

let x = (~a=-1) => a;

let x: float=-.1;

let x: int =-1;

let x: int=+1;

let x: float =+. 1;

foo(~a=?-1);

/*
  https://github.com/facebook/reason/issues/1992
  Pexp_override
*/
let z = {<state: 0, x: y>};
let z = {<>};

/* https://github.com/facebook/reason/issues/2056 */
type foo = ~a:bool=? => int;

/* https://github.com/facebook/reason/issues/2070 */
f(~commit=!build);

/* https://github.com/facebook/reason/issues/2032 */
let predicate =
  predicate === Functions.alwaysTrue1 ?
    defaultPredicate :
    (fun
      | None => false
      | Some(exn) => predicate(exn));

let predicate =
  predicate === Functions.alwaysTrue1 ?
    (fun
      | None => false
      | Some(exn) => predicate(exn)) :
    (fun
      | None => false
      | Some(exn) => predicate(exn));

/* https://github.com/facebook/reason/issues/2125 */
foo(~a);

foo(~a: int);

foo(~(a: int));

foo(~(a :> int));

foo(~a :> int);

foo(~Foo.a?);

foo(~Foo.a);

/* https://github.com/facebook/reason/issues/2155#issuecomment-422077648 */
true ? (Update({...a, b: 1}), None) : x;
true ? ({...a, b: 1}) : a;
true ? (a, {...a, b: 1}) : a;

true ? ([x, ...xs]) => f(x, xs) : a;

/* https://github.com/facebook/reason/issues/2200 */
foo(~x=-1 + 2);

foo(~x=-1 + 2: int);

foo(~not);

let foo = (~not) => ();

let foo = (~not: string) => ();

foo(~not: string);

/* https://github.com/facebook/reason/issues/2141 */
let testCallNamedArgs = (foo: ((~a: int, ~b: int) => int), a, b) =>
  foo(~a, ~b);

let testCallNamedArgs = (foo: ((~a: int, ~b: int=?) => int), a, b) =>
  foo(~a, ~b);

let Foo.{name} = bar;
let Foo.Bar.{name} = bar;

let Foo.{
  destruct1,
  destruct2,
  destruct3,
  destruct4,
  destruct5,
} = fooBar;

let Foo.[ name ] = bar;
let Foo.Bar.[ name ] = bar;

let Foo.Bar.[] = bar;
let Foo.Bar.[||] = bar;
let Foo.() = foo;

/* let Foo.(bar, baz) = foo; */

let Foo.(exception bar) = baz;

try({
  let this = try_exp;
  let has = hugged;
  parens;
}) {
  | _ => ()
};

/* Pcl_open (4.06+) */
class x = {
  open EM;
  as self;
};

class y = {
  open EM;
  open OM;
  as self;
};

// Arbitrary precision literals

let x = 1G;
let x = 1.123g;

let x = switch () { | _ => .};

let%foo Foo.{
  destruct1,
  destruct2,
  destruct3,
  destruct4,
  destruct5,
} = fooBar;

open%foo Bar;
open! %foo Bar;

let () = {
  let open%foo Bar;
  switch (1 + 1) {
  | 2 => ()
  | _ => ()
  };
};

let () = {
  let open%foo Bar;
  3
};

let () = {
  let open%foo Bar;
  let x = 1;
  3
};

module type x = {
  let a: 'a. 'a => unit;
};

