Core Reason

Contributor Setup

With esy

# Make sure you have the latest esy
npm install -g esy@next
git clone
cd reason


Test Suite:

esy test # Run tests

One Off Tests:

Start up the rtop top level with your changes:

esy x rtop

Pipe some text to refmt with your changes:

echo "let a = 1" | esy x refmt

esy tips:

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

With opam

# On OSX, install opam via Homebrew:
brew update
brew install opam
# On Linux, see here (you will need opam >= 1.2.2):

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
cd reason
opam pin add -y reason .
opam pin add -y rtop .

Opam Troubleshooting:

Repo Walkthrough

![reason_-bucklescript_in_ocaml]( (_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 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, a library that generates parsers. You can read more about Menhir here.

Core Files

Miscellaneous Files

Working With Parser

Here’s a recommended workflow:

Lexer helpers doc: Parser helper docs: Menhir manual: Small Menhir example: Random Stack Overflow answer: (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:

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 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

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

File "", 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.

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

esy x refmt --parse 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

Where 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" ([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):

Then do:

esy x refmt --parse --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.