Introduction¶
The goal of this first chapter is to get to a place where you have a working Dune project with a skeleton of a calculator.
This is is a tutorial: it is meant to be followed in order, but you can stop at any point. You can also restart from any chapter, using these sections that are present at the end of the previous chapter.
Checkpoint
This will contain the project at the end of each chapter.
Installing Packages¶
First, you’ll need to have a working Opam installation. This is described in How to Install Dune.
Then, create an empty directory somewhere, say ~/dune-calc
. In this tutorial,
we will only create files in this directory.
Let’s first make sure you have a working opam installation. Run this command:
opam --version
It should display something like “2.2.0”. Anything greater than 2.0.0 is fine.
Important
When asked to type a command, you can click
to copy the full command to your clipboard.In this tutorial, all commands will be typed at the root of your project, like
~/dune-calc
.
Let’s create a local switch: cd
to this directory and run the following command.
This can take a few minutes.
opam switch create ./ 5.1.1
This command has created a directory named _opam
in the current directory.
Now, let’s install some packages by running:
opam install dune.3.15.3 menhir.20231231
You can confirm that opam
is correctly setup by typing dune --version
,
which should display 3.15.3. Otherwise, please refer to
How to Install Dune.
Note
The instructions use precise version numbers in opam install
command. This is
to ensure that the error messages will exactly map what you’re seeing, but
it is very likely to work with any version.
The Calculator Skeleton¶
Now that we have an opam switch and some packages installed, let’s create the various files.
For each file, click file.txt
in a text editor and paste the contents there.
dune-project
(lang dune 3.0)
(using menhir 2.1)
(package (name calc))
This file contains metadata about the project:
See also
dune-project
Reference documentation about dune-project
files
dune
(executable
(public_name calc))
(ocamllex lexer)
(menhir
(modules parser))
This file contains a description of what’s in our project:
an executable stanza defining our calculator binary
an ocamllex stanza, setting up rules to compile
lexer.mll
to aLexer
modulea menhir stanza, to similarly use
parser.mly
as aParser
module
parser.mly
%token Eof
%token<int> Int
%token Plus
%start<Ast.exp> main
%left Plus
%{ open Ast %}
%%
main: expr Eof { $1 }
expr:
| Int { Int $1 }
| expr Plus expr { Add ($1, $3) }
%%
This contains definitions of our tokens and grammar rules, using Menhir.
lexer.mll
let space = [' ']+
let digit = ['0'-'9']
rule token = parse
| eof { Parser.Eof }
| space { token lexbuf }
| '\n' { Parser.Eof }
| '+' { Parser.Plus }
| digit+ { Parser.Int (int_of_string (Lexing.lexeme lexbuf)) }
This is our lexer, using ocamllex.
ast.ml
type exp =
| Int of int
| Add of exp * exp
This contains a definition of the arithmetic expressions manipulated by the calculator.
Note
This is in a separate file from calc.ml
to avoid module cycles, since Calc
depends on Parser
, which depends on the expression type.
calc.ml
let rec eval = function Ast.Int n -> n | Add (a, b) -> eval a + eval b
let () =
while true do
Printf.printf ">> %!";
let lb = Lexing.from_channel Stdlib.stdin in
let e = Parser.main Lexer.token lb in
Printf.printf "%d\n" (eval e)
done
This is the “business logic” of our app, in which we:
display a prompt
call the lexer and parser to get an expression
evaluate the expression
display the result
At this stage, we have the skeleton of a calculator.
Run the following command to build and execute the calculator:
dune exec calc
You can enter additions, such as 1+2
followed by Enter.
Exit with Ctrl+C.
Initially, only addition is supported and anything else triggers an exception terminating the execution.
Note that a _build
directory is now present. This is where Dune will store
all compiled artifacts.
You can safely remove this directory - that’s actually what the dune clean
command does. But that’s not usually necessary since Dune will keep track of
dependencies and what is up to date.
The _opam
directory is where your dependencies are located. It is managed by
opam. If it gets removed by accident or something is corrupted in there, it is
safe to remove it and recreate it by running opam switch
and opam install
as described above.