# Technical Documentation: reasonml/reason

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/reasonml/reason)

### Overview (`README.md`)

<p align="center"><img src="https://reasonml.github.io/img/reason.svg" alt="logo" width="316" /></p>
<h1 align="center">Reason</h1>
<p align="center">Simple, fast & type safe code that leverages the JavaScript & OCaml ecosystems.</p>
<p align="center">
  <a href="https://dev.azure.com/reasonml/reason/_build/latest?definitionId=2?branchName=master">
    <img src="https://dev.azure.com/reasonml/reason/_apis/build/status/reasonml.reason?branchName=master" alt="Build Status" />
  </a>
  <a href="https://circleci.com/gh/reasonml/reason/tree/master">
    <img src="https://circleci.com/gh/reasonml/reason/tree/master.svg?style=svg" alt="CircleCI" />
  </a>
  <a href="https://discord.gg/reasonml">
    <img src="https://img.shields.io/discord/235176658175262720.svg?logo=discord&colorb=blue" alt="Chat" />
  </a>
</p>


## Latest Releases:

[

## User Documentation

**The Reason user docs live online at [https://reasonml.github.io](https://reasonml.github.io)**.
The repo for those Reason docs lives at [github.com/reasonml/reasonml.github.io](https://github.com/reasonml/reasonml.github.io)

Docs links for new users:

- [Getting Started](https://reasonml.github.io/docs/en/installation)

- [Community](https://reasonml.github.io/docs/en/community.html)

### Contributing:

```sh
npm install esy@next
git clone https://github.com/reasonml/reason.git
cd reason
esy
esy test # Run the tests
```

### Contributor Documentation:

The [`docs/`](./docs/) directory in this repo contains documentation for
contributors to Reason itself (this repo).


## License

See Reason license in [LICENSE.txt](LICENSE.txt).

Works that are forked from other projects are under their original licenses.

## Credit

The general structure of `refmt` repo was copied from [whitequark's m17n project](https://github.com/whitequark/ocaml-m17n), including parts of the `README` that instruct how to use this with the OPAM toolchain. Thank you OCaml!

[reason]: https://www.npmjs.com/package/@reason-native/console
[reason-badge]: https://img.shields.io/npm/v/@esy-ocaml/reason/latest.svg?color=blue&label=@esy-ocaml/reason&style=flat&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzOTcgNDE3IiB3aWR0aD0iMzk3IiBoZWlnaHQ9IjQxNyI+PGcgZmlsbD0iI0ZDRkFGQSI+PHBhdGggZD0iTTI2Ny42NDYgMTQyLjk4MmwzOS42MTYtMjIuOTQ2TDI2Ny41ODMgOTcuMmwtMzkuNjE2IDIyLjk0NiAzOS42NzkgMjIuODM2em0tNjkuMzI4IDQwLjEyOWwzOS42MTYtMjIuOTQ1LTM5LjY3OS0yMi44MzYtMzkuNjE2IDIyLjk0NiAzOS42NzkgMjIuODM1em0tNjkuNDM5LTQwLjEzbDM5LjYxNi0yMi45NDVMMTI4LjgxNiA5Ny4yIDg5LjIgMTIwLjE0NmwzOS42NzkgMjIuODM1em02OS4zMjgtMzkuOThsMzkuNjE2LTIyLjk0NS0zOS42NzktMjIuODM2LTM5LjYxNiAyMi45NDYgMzkuNjc5IDIyLjgzNXoiLz48cGF0aCBkPSJNMTkuODU2IDEzNy41OTFsMTY4LjYzOCA5Ny4wNTEuMjA2IDE0OC43ODlMMjAuMDYzIDI4Ni4zOGwtLjIwNy0xNDguNzg5ek0xOTguMTEyIDIyLjg5bDE2OC42MzcgOTcuMDUyLTE2OC4zNjcgOTcuNTE5TDI5Ljc0NCAxMjAuNDFsMTY4LjM2OC05Ny41MnptMTc4LjU3MyAxMTQuMjA2bC4yMDcgMTQ4Ljc4OS0xNjguMzY4IDk3LjUxOS0uMjA2LTE0OC43ODkgMTY4LjM2Ny05Ny41MTl6TTE5OC4wOCAwTDAgMTE0LjcyOGwuMjU1IDE4My4xMjUgMTk4LjM5NyAxMTQuMTc4IDE5OC4wOC0xMTQuNzI4LS4yNTUtMTgzLjEyNUwxOTguMDggMHoiLz48L2c+PC9zdmc+Cg== "esy package on npm"

---

### Overview (`docs/README.md`)

### Documentation

> This directory is not the Reason _User_ documentation. This directory is for
> Reason contributor documentation. **The Reason user docs live online at
> [https://reasonml.github.io](https://reasonml.github.io)**. The repo for
> those Reason docs lives at
> [github.com/reasonml/reasonml.github.io](https://github.com/reasonml/reasonml.github.io)

**Inside of this directory:**


**Getting Started Contributing:**

[`GETTING_STARTED_CONTRIBUTING.md`](./GETTING_STARTED_CONTRIBUTING.md).

**Releasing:**
[`RELEASING.md`](./RELEASING.md)

**Programmatically Using Reason Parser From JavaScript:**
[`USING_PARSER_PROGRAMMATICALLY.md`](./USING_PARSER_PROGRAMMATICALLY.md)

---

### Overview (`test/README.md`)

# refmt cram test suite

This folder contains dune's cram tests.

More information on how they work and how to create them in [dune's documentation](https://dune.readthedocs.io/en/stable/tests.html#cram-tests).

## Run them locally use

```bash
esy dune runtest
# or in watch mode
esy dune runtest -w
```

## Update snapshot

The usual workflow is to run the test once, let it fail and then update the snapshot with the output you expect with promotion:

```bash
esy dune promote
```

## Run only one test

```bash
esy dune build @cram-test-name
# for example: esy dune build @assert
```

## Update only one snapshot

```bash
esy dune build @cram-test-name --auto-promote
# for example: esy dune build @assert
```

## Testing a specific version of OCaml

Some tests are specifically designed to test a specific version of OCaml. To run them, you have a few options:

### Install an specific OCaml version with esy

Make sure the OCaml's version is between the range of the `ocaml` field in esy.json (`>= 4.2.0 < 4.15.0`).

1. Install the specific version you want with: `esy add ocaml@{{ ocaml_version }}`
2. Run the test with: `esy dune runtest`

### Setup the local environment with opam

The opam workflow is only tested in CI and isn't needed for development, but can become handy since you can install many switches with different versions of OCaml and load them when needed.

Using 4.12 as an example:

1. Create the switch: `opam switch create reason-dev-4.12 -y --deps-only --with-test 4.12.0`
2. Install dependencies (this only needs to be done once) `opam install . --deps-only --with-test`
3. Load env variables for your switch `eval $(opam env --switch=reason-dev-4.12 --set-switch)`
4. Run the test with: `opam exec -- dune runtest`

---

### Overview (`src/refmt/README.md`)

# Reason (For Native Compilers)

Simple, fast & type safe code that leverages the JavaScript & OCaml ecosystems.

This package provides support for using Reason with native OCaml compilers.

[](https://dev.azure.com/reasonml/reason/_build/latest?definitionId=2?branchName=master) [](https://travis-ci.org/facebook/reason) [](https://circleci.com/gh/facebook/reason/tree/master)

## [Native Quick Start With Esy](https://reasonml.github.io/docs/en/quickstart-ocaml)

## [Community](https://reasonml.github.io/docs/en/community.html)

## [Roadmap & Contribution](https://reasonml.github.io/docs/en/roadmap)

### Documentations

Go to https://github.com/reasonml/reasonml.github.io to contribute to the Reason documentation.

### Contributing

See the [docs folder's `GETTING_STARTED_CONTRIBUTING.md`](../docs/GETTING_STARTED_CONTRIBUTING.md).

---

### Overview (`src/vendored-omp/README.md`)

# OCaml-migrate-parsetree
Convert OCaml parsetrees between different major versions

This library converts between parsetrees of different OCaml versions.

Supported versions are OCaml 4.08 through 4.14 and OCaml 5.0 through 5.6.
For each version, there is a snapshot of the parsetree and conversion functions
to the next and/or previous version.

## Asts

```ocaml
module Ast_VERSION : sig

  (* These two modules didn't change between compiler versions.
     Just share the ones from compiler-libs. *)
  module Location = Location
  module Longident = Longident

  (* Version specific copy of AST *)
  module Asttypes
  module Parsetree
  module Outcometree

  (* Other modules that are useful for implementing PPX.

     Docstrings and Ast_mapper only contain general definitions
     In particular, the internal state used by compiler-libs has been
     removed.
     Also equalities are lost for abstract types (Docstring.docstring).  *)
  module Docstrings
  module Ast_helper
  module Ast_mapper

  (* Magic numbers used for marshalling *)
  module Config : sig
    val ast_impl_magic_number : string
    val ast_intf_magic_number : string
  end
end
```

These embed copies of AST definitions for each supported OCaml major version.

The AST matching the version of the OCaml toolchain will contain equalities
relating the copy of types to the definitions from compiler-libs.  For
instance, when installed with OCaml 4.14.x, `Ast_414.Parsetree` looks
like.

## Migration modules

For each pair of versions `$(n)` and `$(n+1)`, the two modules
`Migrate_parsetree_$(n)_$(n+1)` and `Migrate_parsetree_$(n+1)_$(n)` convert the AST forward and backward.

The forward conversion is total while the backward conversion is partial: when
a feature is not available in a previous version of the parsetree, a
`Migrate_parsetree_def.Migration_error` exception is raised detailing the
failure case.

`Migrate_parsetree_versions` abstract versions of the compiler. Each version is
represented as a module with `OCaml_version` signature.  Instances are named
`OCaml_408`, `OCaml_409`, ... `OCaml_current` is an alias to the version of the
current compiler.
The `Convert` functor takes two versions of OCaml and produce conversion
functions.

Finally, the `Migrate_parsetree_ast_io` provides an easy interface for
marshalling/unmarshalling.

## Migrate_parsetree.Driver

The `Migrate_parsetree.Driver` provides an API for ppx rewriters to
register OCaml AST rewriters. Ppx rewriters using this API can be used
as standalone rewriter executable or as part of a _driver_ including
several rewriters.

Using a single driver for several rewritings has the advantage that it
is faster. Especially when using many ppx rewriters, it can speed up
compilation a lot.

If using [Dune](https://github.com/ocaml/dune), you can
consult the dune manual to see how to define and use ppx
rewriters. Dune automatically creates drivers based on
ocaml-migrate-parsetree on demand.

The rest of this section describes how to do things manually or with
[ocamlbuild](https://github.com/ocaml/ocamlbuild).

## Building a custom driver using ocamlfind

To build a custom driver using ocamlfind, simply link all the ppx
rewriter libraries together with the
`ocaml-migrate-parsetree.driver-main` package at the end:

    ocamlfind ocamlopt -predicates ppx_driver -o ppx -linkpkg \
      -package ppx_sexp_conv -package ppx_bin_prot \
      -package ocaml-migrate-parsetree.driver-main

Normally, ocaml-migrate-parsetree based rewriters should be build with
the approriate `-linkall` option on individual libraries. If one is
missing this option, the rewriter might not get linked in. If this is
the case, a workaround is to pass `-linkall` when linking the custom
driver.

The resulting `ppx` program can be used as follow:

- `./ppx file.ml` to print the transformed code
- `ocamlc -pp './ppx --as-pp' ...` to use it as a pre-processor
- `ocamlc -ppx './ppx --as-ppx' ...` to use it as a `-ppx` rewriter

# Development

It started from the work of Alain Frisch in
[ppx\_tools](https://github.com/alainfrisch/ppx_tools).

The library is distributed under LGPL 2.1 and is copyright INRIA.

## Adding a new OCaml version

We use [Cinaps](https://github.com/janestreet/cinaps) to generate boilerplate.
You can install it via opam: `opam install cinaps ocamlformat`.

Add the new version in
[src/cinaps_helpers.ml](src/cinaps_helpers.ml)
`supported_versions`.

Copy the last `src/ast_xxx.ml` file to `src/ast_<new_version>.ml`,
then go over the file and update each sub-module by replacing its
signature and implementation with the code from the compiler. For the
`Config` sub-module, update the two variables with the values in
`utils/config.mlp` in the compiler source tree.

Once this is done, call:

    $ dune exec tools/add_special_comments.exe src/ast_<new_version>.ml

Then diff the `src/ast_xxx.ml` and `src/ast_<new_version>.ml` and go
over the diff to make sure the difference are relevant. The `ast_...`
files require some adjustments which should pop up when you do this
diff. Port the old adjustments to the new file as required.

Add migration functions:
- Manually compile the asts (`ocamlc -c src/ast_{NEW,OLD}.ml -I +compiler-libs -I _build/default/src/vendored-omp/src/.reason_omp.objs/byte -open Reason_omp__`)
- Using `tools/gencopy.exe` (`dune build tools/gencopy.exe`), generate copy code to and from previous version (assuming it is 408):
```
_build/default/tools/gencopy.exe -I . -I src/ -I +compiler-libs -map Ast_409:Ast_408 Ast_409.Parsetree.{expression,expr,pattern,pat,core_type,typ,toplevel_phrase} Ast_409.Outcometree.{out_phrase,out_type_extension} > src/migrate_parsetree_409_408_migrate.ml
_build/default/tools/gencopy.exe -I . -I src/ -I +compiler-libs -map Ast_408:Ast_409 Ast_408.Parsetree.{expression,expr,pattern,pat,core_type,typ,toplevel_phrase} Ast_408.Outcometree.{out_phrase,out_type_extension} > src/migrate_parsetree_408_409_migrate.ml
```
- Fix the generated code by implementing new cases
- The migration functor expects specific names, look at `Migrate_parsetree_versions` interface.

*TODO*: specialize and improve gencopy for these cases

Add mapper lifting functions in the files `migrate_parsetree_NEW_408.ml` and
`migrate_parsetree_408_NEW.ml`:
- include the corresponding `Migrate_parsetree_40x_40y_migrate` module
- define `copy_mapper` function, look at existing `Migrate_parsetree_40x_40y`
  for guidance.

At any time, you can expand boilerplate code by running `make cinaps`.

Update build system:
- make sure `make cinaps` reaches a fixed point :)
- `make` should succeed

---

### GETTING STARTED CONTRIBUTING (`docs/GETTING_STARTED_CONTRIBUTING.md`)

# Core Reason

## Contributor Setup


### With esy

```sh
# Make sure you have the latest esy
npm install esy@next
git clone https://github.com/facebook/reason.git
cd reason
esy
```


#### Testing:

**Test Suite:**

```sh
esy test # Run tests
```

**One Off Tests:**

Start up the `rtop` top level with your changes:

```sh
esy x rtop
```

Pipe some text to `refmt` with your changes:

```sh
echo "let a = 1" | esy x refmt
```


> **`esy` tips:**
> - `esy x your command` will run one command `your command` in an environment
>   where the projects are built/installed. `esy x which refmt` will build the
>   packages and install them for the duration of one command - `which refmt`.
>   This will print the location of the built `refmt` binary.
> - For more, see the [esy documentation](https://github.com/esy-ocaml/esy).


> All the built binaries are in `esy echo '#{self.target_dir}/install/default/bin'`.


### With opam

```sh
# On OSX, install opam via Homebrew:
brew update
brew install opam
# On Linux, see here (you will need opam >= 1.2.2): http://opam.ocaml.org/doc/Install.html

opam init
# Add this to your ~/.bashrc (or ~/.zshrc), then do `source ~/.bashrc`
#   eval $(opam config env)

opam update
opam switch 4.04.2
eval $(opam config env)
git clone https://github.com/facebook/reason.git
cd reason
opam pin add -y reason .
opam pin add -y rtop .
```
> **Opam Troubleshooting:**
> - Is the previous pinning unsuccessful? We might have updated a dependency;
>   try `opam update` then `opam upgrade`.
> - During the last `opam pin` step, make sure your local repo is clean. In
>   particular, remove artifacts and `node_modules`. Otherwise the pinning
>   might go stale or stall due to the big `node_modules`.


## Repo Walkthrough


(_Click to see a larger version_)

Reason is the orange part. The core of the codebase is a parser + a printer, plus other miscellaneous utilities we expose.

Throughout the codebase, you might see mentions of "migrate-parsetree", `Ast_404`, etc. These refer to https://github.com/let-def/ocaml-migrate-parsetree. It's a library that allows you to convert between different versions of the OCaml AST. This way, the Reason repo can be written in OCaml 4.04's AST data structures, while being usable on OCaml 4.02's libraries (BuckleScript's on 4.02 too).

The Reason lexer & parser use [Menhir](http://gallium.inria.fr/~fpottier/menhir/), a library that generates parsers. You can read more about Menhir [here](https://realworldocaml.org/v1/en/html/parsing-with-ocamllex-and-menhir.html).

### Core Files

- `src/reason-parser/reason_lexer.mll`: the lexer that chunks a raw string into tokens. See the file for more comments.

- `src/reason-parser/reason_parser.mly`: the parser that takes the lexer's result and turns it into a proper AST (abstract syntax tree). See the file for more comments.

- `src/reason-parser/reason_pprint_ast.ml`: the pretty-printer! This is the reverse of parsing: it takes in the AST (abstract syntax tree) and prints out the nicely formatted code text.

- `src/reason-parser/reason_parser.messages.checked-in`: this is the huge table of mostly generated, sometimes hand-written, syntax error messages. When the parser ends up at an invalid parsing state (aka ends up with a syntax error), it'd refer to that file's content and see if that case has a specific error message assigned to it. For an example fix, see [this PR](https://github.com/facebook/reason/pull/1018) and the [follow-up](https://github.com/facebook/reason/pull/1033). To add a syntax error message see the "Add a Menhir Error Message" section below.
  - When running `esy`, and a new `reason_parser.messages` file is generated, do a `mv reason_parser.messages reason_parser.messages.checked-in` to persist the updated messages.

- `src/reason-parser/reason_oprint.ml`: the "outcome printer" used by Merlin, rtop and terminal, that prints the errors in Reason syntax. More info in the file itself.

- `src/reason-parser/menhir_error_processor.ml, reason_parser_explain.ml`: two files that allows us to batch assign a better syntax error message for a category of errors, like accidentally using a reserved token. More info in the comments of these files.

### Miscellaneous Files

- `ocamlmerlin_reason.ml`: produces the `ocamlmerlin-reason` binary, used in conjunction with [Merlin-extend](https://github.com/let-def/merlin-extend). This is an extension to [Merlin](https://github.com/ocaml/merlin), which picks up this binary from your environment to analyze Reason files when your editor calls Merlin.

- `*.mllib`: related: see the [OCaml extensions list](https://reasonml.github.io/docs/en/faq.html#i-m-seeing-a-weird-cmi-cmx-cmj-cma-file-referenced-in-a-compiler-error-where-do-these-files-come-from-). These are generated file from `pkg/build.ml`, which describes the package we distribute. No need to worry about them.

- `src/reason-parser/reason_config.ml`: global configuration that says whether the parser should run in "recoverable" mode. Merlin has a neat feature which lets it continue diagnosing e.g. type errors even when the file is syntactically invalid (at the expense of the accuracy of those type error reports' quality). Searching `reason_config` in the codebase will show you how this is used.

- `src/reason-parser/reason_parser.messages`: auto-generated from parser changes. Menhir generates parsing code that assigns each syntax error to a code, and lets us customize these errors. Syntax errors can be very precisely pinpointed and explained this way.

- `src/reason-parser/reason_toolchain.ml`, `src/reason-parser/refmt_impl.ml`: the entry point that calls the parsing logic.

- `src/rtop/reason_utop.ml`, `src/rtop/reason_toploop.ml`, `src/rtop/rtop_init.ml`: Reason's [Utop](https://github.com/diml/utop) integration. Utop's the terminal-based REPL you see when executing `utop` (in Reason's case, the wrapper `rtop`).

- `*.sh`: some of the binaries' entries.

- `src/rtop/reason_util.ml`, `reason_syntax_util.ml`: utils.

- `src/reason-parser/reactjs_jsx_ppx_v2.ml`: the ReactJS interop that translates [Reason JSX](https://reasonml.github.io/docs/en/jsx.html) into something that ReactJS understands. See the comments in the file and the description in [ReasonReact](https://reasonml.github.io/reason-react/#reason-react-jsx).

- `src/reason-parser-tests/testOprint.ml`: unit tests for the outcome printer mentioned above. See the file for more info on how outcome printing is tested.

## Working With Parser

Here's a recommended workflow:

- First put your code in the current master syntax in a file `test.re`
- `esy x refmt --print ast test.re`
- look closely at the ast, spot the thing you need
- Search for your item in `reason_parser.mly`
- Change the logic
- `esy test`

Lexer helpers doc: http://caml.inria.fr/pub/docs/manual-ocaml/libref/Lexing.html
Parser helper docs: http://caml.inria.fr/pub/docs/manual-ocaml/libref/Parsetree.html
Menhir manual: http://gallium.inria.fr/~fpottier/menhir/manual.pdf
Small Menhir example: https://github.com/derdon/menhir-example
Random Stack Overflow answer: https://stackoverflow.com/questions/9897358/ocaml-menhir-compiling-writing
(Ok seriously, we need some more Menhir examples. But hey, nobody said it was easy... for now!)

**Want some example pull requests**? Here are a few:

- [Fix outcome printer object printing](https://github.com/facebook/reason/pull/1357)
- [Add more spacing when printing Ptyp_package](https://github.com/facebook/reason/pull/1430)
- [Implement spread for jsx3](https://github.com/facebook/reason/pull/1429)
- [Make deref be a prefix operator](https://github.com/facebook/reason/pull/1463)
- [Print MyConstructor(()) as MyConstructor()](https://github.com/facebook/reason/pull/1465)
- [Ensure valid parsing of constraint expressions after printing](https://github.com/facebook/reason/pull/1464)
- [Record punning for value & pattern for fields with module prefix](https://github.com/facebook/reason/pull/1456)
- [Rage implement everything](https://github.com/facebook/reason/pull/1448)
- [Print functions as javascript](https://github.com/facebook/reason/pull/1469)
- [Transform ocaml pervasives interfaces to reason correctly](https://github.com/facebook/reason/pull/1474)
- [Special case printing of foo(bar)##value](https://github.com/facebook/reason/pull/1481)
- [Use ~ for named args](https://github.com/facebook/reason/pull/1483/)
- [Bring back parentheses-less `switch foo`](https://github.com/facebook/reason/pull/1476)
- [Remove extra parens printed in `type a = Foo((unit => unit))`](https://github.com/facebook/reason/pull/1491)
- [Don't apply sugar to Js.t({.}) and Js.t({..})](https://github.com/facebook/reason/pull/1502)
- [Pun record destructuring with renaming](https://github.com/facebook/reason/pull/1517)
- [Add support for simple pattern direct argument with array, list & record](https://github.com/facebook/reason/pull/1528)
- [Fix outcome printer record value printing](https://github.com/facebook/reason/pull/1529)
- [Print`foo(()) as `foo() + update parser](https://github.com/facebook/reason/pull/1560)
- [Allow parsing of constraint expressions without parens inside constructor expr](https://github.com/facebook/reason/pull/1576)
- [Don't print fun in record expressions with Pexp_fun values](https://github.com/facebook/reason/pull/1588)
- [Force breaks for nested records](https://github.com/facebook/reason/pull/1593)
- [Always break object def with two or more rows](https://github.com/facebook/reason/pull/1596)
- [Make exponentiation operator print with right associativity](https://github.com/facebook/reason/pull/1678)

### Debugging Grammar Conflicts

Run the main parser through Menhir with the `--explain` flag to have it print
out details about the conflict. `esy menhir --explain src/reason-parser/reason_parser.mly`.
The debug information can be found at `src/reason-parser/reason_parser.conflicts`.

Use the `--dump` flag to have Menhir print the state transitions for debugging the parser rules
that are applied `esy  menhir --dump  src/reason-parser/reason_parser.mly`
It will generate `src/reason-parser/reason_parser.automaton` which you can inspect. It will also
drop a `reason_parser.ml` and `reason_parser.mli` in that directory which you need to remove before
building again.

### Debugging the Lexer/Parser State at Runtime

Add the `--trace` flag to the _end_ of the `menhir` `(flags..)` section in the
`dune` file for the `reason-parser`, and it will print out tokens that are
lexed along with parser state transitions.

### Add a Menhir Error Message

To add a Menhir error message, you first need to know the error code. To find the error code, you can run the following commands from the Reason project root:

```
esy x refmt --parse re foo.re
```

Where `foo.re` contains a syntax error. This will result in an error message like:

```
File "test2.re", line 4, characters 2-6:
Error: 2665: <syntax error>
```

Here, the error code is 2665. We then search for this code in `src/reason-parser/reason_parser.messages`.

- If you find it, you can add a better error message instead of the not so descriptive `<syntax error>`.

To test the new error message you can run the following commands again:

```
esy x refmt --parse re foo.re
```

Then submit a PR!

### Improve Error Message Locations

In some situations, Reason might report errors in a different place than where it occurs. This is caused by the AST not having a correct location for a node. When the error reports a location that's simply at the top of the file, that means we likely didn't attach the location in the parser correctly, altogether.

Before digging into Reason parser, make sure this isn't actually caused by some PPX. Otherwise, run:

```
esy x refmt --parse re --print ast test.re
```

Where `test.re` has the code that produces the error message at the wrong location.

In the printed AST, you can see nodes such as `Ptyp_constr "int" (test.re[1,0+15]..[1,0+18])` where the part between parentheses is the location of the node.

The error message's own location will look like `([0,0+-1]..[0,0+-1])` too.

To fix this, we need to find the AST node in `src/reason-parser/reason_parser.mly`. It's a big file, but if you search for the AST node, you should be able to find the location (if not, bug us on Discord). It will probably involve a `mkexp` or `mkpat` without a proper `~loc` attached to it.

As you can see from other parts in the parser, many do have a `~loc` assigned to it. For example

```
| LIDENT jsx_arguments
      {
        (* a (punning) *)
        let loc_lident = mklocation $startpos($1) $endpos($1) in
        [($1, mkexp (Pexp_ident {txt = Lident $1; loc = loc_lident}) ~loc:loc_lident)] @ $2
      }
```


## Testing Two Different Syntax Versions

If you'd like to convert from an old Reason syntax version to the one in master (whether to debug things, or if you're interested in trying out some syntax changes from master and then explaining to us why your perspective on the Reason syntax is the best, lol):

- Revert the repo to the old commit you want
- Build, through `esy`
- Move the built refmt binary `esy x which refmt` somewhere else
- Revert back to master
- `esy x which refmt` again to get the master binary.

Then do:

```
esy x refmt --parse my_old_syntax_file.re --print binary_reason | ./refmt_impl --parse binary_reason --print re
```

Basically, turn your old syntax into an AST (which is resilient to syntax changes), then turn it back into the new, textual code. If you're reverting to an old enough version, the old binary's flags and/or the old build instructions might be different. In that case, see `esy x refmt --help` and/or the old README.

---

### RELEASING (`docs/RELEASING.md`)

## Releasing Native Packages To Npm:

There's a few native `esy` packages included which are released to npm.

- `@esy-ocaml/reason`
- `@esy-ocaml/rtop`

## Releasing

The Reason repo is a "monorepo" `esy` project. To actually created individual
packages from the monorepo that should be published, there is a script
`./scripts/esy-prepublish.js` which accepts the relative paths to various
`.json` files you wish to publish as individual packages. To release packages
`@esy-ocaml/reason` and `@esy-ocaml/rtop` which have json files `reason.json`
and `rtop.json` respectively in the repo root, you would run that script after
committing/bumping some versions:


```sh
git checkout -b MYRELEASE origin/master
git rebase origin/master
vim -O esy.json reason.json
# Then edit the version number accordingly on BOTH files. With that same VERSION do:
version=3.5.0 make pre_release
git commit -m "Bump version"
git push origin HEAD:PullRequestForVersion # Commit these version bumps
node ./scripts/esy-prepublish.js ./reason.json ./rtop.json

# Then publish. For example:
# cd _release/reason.json/package/
# npm publish --access=public
# cd _release/refmt.json/package/
# npm publish --access=public
```

Then follow the printed instructions for pushing any of the packages to npm.
They will show up under `@esy-ocaml/reason` etc.


## Releasing Native Packages To Opam:

*note: it is recommended to install opam-publish via* `opam-depext -i opam-publish`

*Also, the commands below are examples based on specific Reason and rtop versions, the version numbers and possibly source urls will need to be changed to match the relevant release.

1. `cd` into a directory that you don't mind having stuff downloaded into
2. `opam-publish prepare reason.3.2.0 "https://registry.npmjs.org/@esy-ocaml/reason/-/reason-3.2.0.tgz"`
3. `opam-publish submit reason.3.2.0`
4. `opam-publish prepare rtop.3.2.0 "https://registry.npmjs.org/@esy-ocaml/rtop/-/rtop-3.2.0.tgz"`
5. `opam-publish rtop.3.2.0`

## [Depracated] reason-cli
Those two Reason packages are combined together into a separate npm package
`reason-cli` which prebuilds those as well as merlin. They can also be used
individually from `esy` projects without prebuilding, but they are more or less
just npm hosted versions of the Opam packages. `reason-cli` is now no longer
necessary, as projects can/should declare `@esy-ocaml` and `@opam/merlin` as
`devDependencies` in their project's `package.json`/`esy.json`.  We may revive
a separate project just for prebuilt binaries of `rtop` without merlin.

---

### TYPE PARAMETERS PARSING (`docs/TYPE_PARAMETERS_PARSING.md`)

Contributors: Lexing and Parsing Type Parameters:
===================================================

Lexing and Parsing type parameters with angle brackets such as the following:

```reason
type t<'x> = list<'x>;
```

is difficult because type parameter angle brackets can "stack" at the end of a
nested parameterized type, resulting in something that looks a lot like an
infix operator beginning with greater-than symbol `>`.

```reason
type t<'x> = something<list<'x>>;
```

In the original implementation of the Reason lexer
`reason_declarative_lexer.mll`, it would tokenize what appears to be infix
operators as a single token so in that case will tokenize `>>` at the end of
the type definition. But in order to correctly parse parameterized types, we
nee to be able to "balance" the opening and closing angle brackets.

Another potential lexing conflict with infix operators is in the parsing of
default values in named arguments with type annotations.

```reason
let f = (~name: list<thing>=[myThing]) => {..};
```

In that example, the `>=` could be parsed as an infix operator if we are not
careful.

Any workable solution will need to maintain multiple `GREATER` tokens when
lexing `>>>` so they can be used for balancing type parameters. There are a few
options:

**Solution:**

In `Reason_single_parser.ml`, we use the same token splitting technique, where
upon a failure to parse on a token, we examine the token and determine if we
can split it into several tokens. We already did this for when we lexed the
token `=?` and failed to parse with it (we split it into `=`, `?'`).  When we
fail to parse with a lexed token beginning with `>`, we split all of the
leading `>` characters into a stream of `GREATER` tokens, and split the
remaining as best as possible.

---

### USING PARSER PROGRAMMATICALLY (`docs/USING_PARSER_PROGRAMMATICALLY.md`)

## Using Reason Parser Programmatically

This document describes how to integrate the Reason parser into other custom toolchains that need to get an AST tree of Reason source code.

### JavaScript API

We expose a `refmt.js` for you to use on the web. Again, for local development, please use the native `refmt` that comes with the installation [here](https://reasonml.github.io/docs/en/installation.html). It's an order of magnitude faster than the JS version. Don't use the JS version unless you know what you're doing. Let's keep our ecosystem fast please.

Aaaanyways, to install `refmt.js`: `npm install reason`.

Here's the API, with pseudo type annotations:

- `parseRE(code: string): astAndComments`: parse Reason code
- `parseREI(code: string): astAndComments`: parse Reason interface code
- `printRE(data: astAndComments): string`: print Reason code
- `printREI(data: astAndComments): string`: print Reason interface code
- `parseML(code)`, `parseMLI(code)`, `printML(data)`, `printMLI(data)`: same as above, but for the OCaml syntax

The type `string` is self-descriptive. The type `astAndComments` returned by the `parse*` functions is an opaque data structure; you will only use it as input to the `print*` functions. For example:

```js
const refmt = require('reason');

// convert the ocaml syntax to reason syntax
const ast = refmt.parseML('let f a = 1');
const result = refmt.printRE(ast);
console.log(result); // prints `let f = (a) => 1`
```

The `parse*` functions potentially throw an error of this shape:

```js
{
  message: string,
  // location can be undefined
  location: {
    // all 1-indexed
    startLine: number, // inclusive
    startLineStartChar: number, // inclusive
    endLine: number, // inclusive
    endLineEndChar: number, // **exclusive**
  }
}
```

**NOTE**: `refmt.js` requires the node module `fs`, which of course isn't available on the web. If using webpack, to avoid the missing module error, put `node: { fs: 'empty' }` into `webpack.config.js`. See https://webpack.js.org/configuration/node/#other-node-core-libraries for more information.

`refmt.js` is minified for you through Closure Compiler, with an accompanying `refmt.map`. The size is 2.3MB **but don't get fooled; it gzips down to just 345KB**. This way, you can carry it around in your own blog and use it to create an interactive refmt playground, without worrying about imposing bandwidth overhead to your readers. Again, keep our ecosystem fast and lean.

### Native API

We're spoiled with more APIs on the native side. To use Reason from OPAM as a native library, you have [these functions](https://github.com/facebook/reason/blob/5a253048e8077c4597a8935adbed7aa22bfff647/src/reason_toolchain.ml#L141-L157). So:

- `Reason_toolchain.RE.implementation_with_comments`
- `Reason_toolchain.RE.interface_with_comments`
- `Reason_toolchain.RE.print_interface_with_comments`
- `Reason_toolchain.ML.implementation_with_comments`
- etc.

The `ML` parsing functions might throw [`Syntaxerr.Error error`](https://caml.inria.fr/pub/docs/manual-ocaml/compilerlibref/Syntaxerr.html). The `RE` parsing functions might throw:

- [`Reason_syntax_util.Error`](https://github.com/facebook/reason/blob/6e99ea5aae3791359b1e356060691f7b5b596365/src/reason-parser/reason_syntax_util.ml#L456) (docs on `Location.t` [here](https://caml.inria.fr/pub/docs/manual-ocaml/libref/Location.html))
- [`Syntaxerr.Error`](https://caml.inria.fr/pub/docs/manual-ocaml/compilerlibref/Syntaxerr.html).
- [`Reason_lexer.Error`](https://github.com/facebook/reason/blob/6e99ea5aae3791359b1e356060691f7b5b596365/src/reason-parser/reason_lexer.mll#L84).

Example usage:

```ocaml
let ast_and_comments =
  Lexing.from_string "let f a => 1"
  |> Reason_toolchain.RE.implementation_with_comments

(* Convert Reason back to OCaml syntax. That'll show these Reason users! *)
let ocaml_syntax =
  Reason_toolchain.ML.print_implementation_with_comments
    Format.str_formatter
    ast_and_comments;
  Format.flush_str_formatter ()
```

---

### Site ORIGINS (`docs/site/ORIGINS.md`)

Bookmark:
(A simple tool for editing and rendering Reason documentation)

Here's a list of all of the technologies that are used and included in Bookmark.

Each of these projects should include a LICENSE file from their original
project, but even if they do not, they still retain the license/copyright of
their original project (even though they are copied/"vendored" in 

## Flatdoc:
This project is just a fork of flatdoc, so of course
[https://github.com/rstacruz/flatdoc](flatdoc) should be mentioned.  Flatdoc is
excellent.

# jquery:
jquery is licensed under the MIT license, and is vendored in vendor/jquery.js
See its license [here](https://jquery.org/license/)

#medium-zoom
[Medium-zoom](https://github.com/francoischalifour/medium-zoom) is vendored as
well.

#Fira Mono Font
Is also vendored as the default source code font. See its LICENSE included in the vendor directory.

#Roboto Font
Is also vendored and is the default font for text. See its LICENSE included in the vendor directory.

# Stylus
The vendored stylus in `support/vendor/stylus.min.js` is gotten from
https://github.com/stylus/stylus/tree/client and is under MIT license (see the
repo).

# hljs
HLJS is vendored in support/vendor/ along with all of its out of the box css
styles.  hljs is licensed under BSD 3-Clause License and the license can be
found in its repo [here](https://github.com/highlightjs/highlight.js).


# marked
Flatdoc vendors marked (markdown parsing library) inside of flatdoc.js
https://github.com/markedjs/marked
It is licensed under MIT (see the repo for license).

# Beach Images:
Credit Lance Aspser:
https://unsplash.com/photos/W785zpEXZZo
https://unsplash.com/photos/woDxDNvpmdk

---

--- METRICS ---
- Files Extracted: 10
- Estimated Token Budget: ~9310 tokens
- Canonical Reference: https://codewiki.google/github.com/reasonml/reason
