first two easy exercises

This commit is contained in:
Vladan Popovic 2024-01-13 12:19:21 +01:00
commit 8c221fed78
20 changed files with 562 additions and 0 deletions

4
.envrc Normal file
View file

@ -0,0 +1,4 @@
# shellcheck shell=bash
# For use with direnv.
# Installing nix-direnv will ensure a smoother experience.
use flake

23
dune-project Normal file
View file

@ -0,0 +1,23 @@
(lang dune 3.2)
(name exercism)
(generate_opam_files false)
(source
(uri https://gitea.vp.mk/vladan/exercism))
(authors "Vladan Popovic")
(maintainers "Vladan Popovic")
(license LICENSE)
(package
(name exercism)
(synopsis "Exercism OCaml exercises")
(description "Exercism OCaml exercises")
(depends ocaml dune)
(tags
(exercism "Exercism OCaml exercises")))
; See the complete stanza docs at https://dune.readthedocs.io/en/stable/dune-files.html#dune-project

57
flake.lock Normal file
View file

@ -0,0 +1,57 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nix-filter": {
"locked": {
"lastModified": 1666547822,
"narHash": "sha256-razwnAybPHyoAyhkKCwXdxihIqJi1G6e1XP4FQOJTEs=",
"owner": "numtide",
"repo": "nix-filter",
"rev": "1a3b735e13e90a8d2fd5629f2f8363bd7ffbbec7",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "nix-filter",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1669867399,
"narHash": "sha256-Z8RXSFYOsIsTG96ROKtV0eZ8Q7u4irFWm6ELqfw7mT8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "38e591dd05ffc8bdf79dc752ba78b05e370416fa",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nix-filter": "nix-filter",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

214
flake.nix Normal file
View file

@ -0,0 +1,214 @@
{
description = "A flake demonstrating how to build OCaml projects with Dune";
# Flake dependency specification
#
# To update all flake inputs:
#
# $ nix flake update --commit-lockfile
#
# To update individual flake inputs:
#
# $ nix flake lock --update-input <input> ... --commit-lockfile
#
inputs = {
# Convenience functions for writing flakes
flake-utils.url = "github:numtide/flake-utils";
# Precisely filter files copied to the nix store
nix-filter.url = "github:numtide/nix-filter";
};
outputs = { self, nixpkgs, flake-utils, nix-filter }:
# Construct an output set that supports a number of default systems
flake-utils.lib.eachDefaultSystem (system:
let
# Legacy packages that have not been converted to flakes
legacyPackages = nixpkgs.legacyPackages.${system};
# OCaml packages available on nixpkgs
ocamlPackages = legacyPackages.ocamlPackages;
# Library functions from nixpkgs
lib = legacyPackages.lib;
# Filtered sources (prevents unecessary rebuilds)
sources = {
ocaml = nix-filter.lib {
root = ./.;
include = [
".ocamlformat"
"dune-project"
(nix-filter.lib.inDirectory "bin")
(nix-filter.lib.inDirectory "lib")
(nix-filter.lib.inDirectory "test")
];
};
nix = nix-filter.lib {
root = ./.;
include = [
(nix-filter.lib.matchExt "nix")
];
};
};
in
{
# Exposed packages that can be built or run with `nix build` or
# `nix run` respectively:
#
# $ nix build .#<name>
# $ nix run .#<name> -- <args?>
#
packages = {
# The package that will be built or run by default. For example:
#
# $ nix build
# $ nix run -- <args?>
#
default = self.packages.${system}.exercism;
exercism = ocamlPackages.buildDunePackage {
pname = "exercism";
version = "0.1.0";
duneVersion = "3";
src = sources.ocaml;
strictDeps = true;
preBuild = ''
dune build exercism.opam
'';
};
};
# Flake checks
#
# $ nix flake check
#
checks = {
# Run tests for the `exercism` package
exercism =
let
# Patches calls to dune commands to produce log-friendly output
# when using `nix ... --print-build-log`. Ideally there would be
# support for one or more of the following:
#
# In Dune:
#
# - have workspace-specific dune configuration files
#
# In NixPkgs:
#
# - allow dune flags to be set in in `ocamlPackages.buildDunePackage`
# - alter `ocamlPackages.buildDunePackage` to use `--display=short`
# - alter `ocamlPackages.buildDunePackage` to allow `--config-file=FILE` to be set
patchDuneCommand =
let
subcmds = [ "build" "test" "runtest" "install" ];
in
lib.replaceStrings
(lib.lists.map (subcmd: "dune ${subcmd}") subcmds)
(lib.lists.map (subcmd: "dune ${subcmd} --display=short") subcmds);
in
self.packages.${system}.exercism.overrideAttrs
(oldAttrs: {
name = "check-${oldAttrs.name}";
doCheck = true;
buildPhase = patchDuneCommand oldAttrs.buildPhase;
checkPhase = patchDuneCommand oldAttrs.checkPhase;
# skip installation (this will be tested in the `exercism-app` check)
installPhase = "touch $out";
});
# Check Dune and OCaml formatting
dune-fmt = legacyPackages.runCommand "check-dune-fmt"
{
nativeBuildInputs = [
ocamlPackages.dune_3
ocamlPackages.ocaml
legacyPackages.ocamlformat
];
}
''
echo "checking dune and ocaml formatting"
dune build \
--display=short \
--no-print-directory \
--root="${sources.ocaml}" \
--build-dir="$(pwd)/_build" \
@fmt
touch $out
'';
# Check documentation generation
dune-doc = legacyPackages.runCommand "check-dune-doc"
{
ODOC_WARN_ERROR = "true";
nativeBuildInputs = [
ocamlPackages.dune_3
ocamlPackages.ocaml
ocamlPackages.odoc
];
}
''
echo "checking ocaml documentation"
dune build \
--display=short \
--no-print-directory \
--root="${sources.ocaml}" \
--build-dir="$(pwd)/_build" \
@doc
touch $out
'';
# Check Nix formatting
nixpkgs-fmt = legacyPackages.runCommand "check-nixpkgs-fmt"
{ nativeBuildInputs = [ legacyPackages.nixpkgs-fmt ]; }
''
echo "checking nix formatting"
nixpkgs-fmt --check ${sources.nix}
touch $out
'';
};
# Development shells
#
# $ nix develop .#<name>
# $ nix develop .#<name> --command dune build @test
#
# [Direnv](https://direnv.net/) is recommended for automatically loading
# development environments in your shell. For example:
#
# $ echo "use flake" > .envrc && direnv allow
# $ dune build @test
#
devShells = {
default = legacyPackages.mkShell {
# Development tools
packages = [
# Source file formatting
legacyPackages.nixpkgs-fmt
legacyPackages.ocamlformat
# For `dune build --watch ...`
legacyPackages.fswatch
# For `dune build @doc`
ocamlPackages.odoc
# OCaml editor support
ocamlPackages.ocaml-lsp
# Nicely formatted types on hover
ocamlPackages.ocamlformat-rpc-lib
# Fancy REPL thing
ocamlPackages.utop
ocamlPackages.angstrom
ocamlPackages.ounit
ocamlPackages.ounit2
ocamlPackages.ppx_sexp_conv
];
# Tools from packages
inputsFrom = [
self.packages.${system}.exercism
];
};
};
});
}

36
hello-world/HELP.md Normal file
View file

@ -0,0 +1,36 @@
# Help
## Running the tests
A `Makefile` is provided with a default target to compile your solution and run the tests. At the command line in your exercise's directory, type:
```bash
make
```
## Submitting your solution
You can submit your solution using the `exercism submit hello_world.ml` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [OCaml track's documentation](https://exercism.org/docs/tracks/ocaml)
- The [OCaml track's programming category on the forum](https://forum.exercism.org/c/programming/ocaml)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
To get help if you're having trouble, you can use one of the following resources:
- [Documentation for the Standard Library](http://caml.inria.fr/pub/docs/manual-ocaml/libref/index.html)
- [/r/ocaml](https://www.reddit.com/r/ocaml) is the OCaml subreddit.
- [StackOverflow](http://stackoverflow.com/questions/tagged/ocaml) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.

9
hello-world/Makefile Normal file
View file

@ -0,0 +1,9 @@
default: clean test
test:
dune runtest
clean:
dune clean
.PHONY: clean

42
hello-world/README.md Normal file
View file

@ -0,0 +1,42 @@
# Hello World
Welcome to Hello World on Exercism's OCaml Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
The classical introductory exercise.
Just say "Hello, World!".
["Hello, World!"][hello-world] is the traditional first program for beginning programming in a new language or environment.
The objectives are simple:
- Modify the provided code so that it produces the string "Hello, World!".
- Run the test suite and make sure that it succeeds.
- Submit your solution and check it at the website.
If everything goes well, you will be ready to fetch your first real exercise.
[hello-world]: https://en.wikipedia.org/wiki/%22Hello,_world!%22_program
## Source
### Created by
- @dvberkel
### Contributed to by
- @daveyarwood
- @iHiD
- @kytrinyx
- @marionebl
- @Peaupote
- @sbl
- @sshine
- @stevejb71
### Based on
This is an exercise to introduce users to using Exercism - https://en.wikipedia.org/wiki/%22Hello,_world!%22_program

16
hello-world/dune Normal file
View file

@ -0,0 +1,16 @@
(executable
(name test)
(libraries base ounit2))
(alias
(name runtest)
(deps (:x test.exe))
(action (run %{x})))
(alias
(name buildtest)
(deps (:x test.exe)))
(env
(dev
(flags (:standard -warn-error -A))))

1
hello-world/dune-project Normal file
View file

@ -0,0 +1 @@
(lang dune 1.1)

View file

@ -0,0 +1 @@
let hello = "Hello, World!"

View file

@ -0,0 +1,4 @@
(*
Returns "Hello, World!"
*)
val hello: string

11
hello-world/test.ml Normal file
View file

@ -0,0 +1,11 @@
open OUnit2
open Hello_world
let ae exp got _test_ctxt = assert_equal ~printer:(fun x -> x) exp got
let tests = [
"Say Hi!" >:: ae "Hello, World!" hello;
]
let () =
run_test_tt_main ("Hello World tests" >::: tests)

36
leap/HELP.md Normal file
View file

@ -0,0 +1,36 @@
# Help
## Running the tests
A `Makefile` is provided with a default target to compile your solution and run the tests. At the command line in your exercise's directory, type:
```bash
make
```
## Submitting your solution
You can submit your solution using the `exercism submit leap.ml` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [OCaml track's documentation](https://exercism.org/docs/tracks/ocaml)
- The [OCaml track's programming category on the forum](https://forum.exercism.org/c/programming/ocaml)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
To get help if you're having trouble, you can use one of the following resources:
- [Documentation for the Standard Library](http://caml.inria.fr/pub/docs/manual-ocaml/libref/index.html)
- [/r/ocaml](https://www.reddit.com/r/ocaml) is the OCaml subreddit.
- [StackOverflow](http://stackoverflow.com/questions/tagged/ocaml) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.

9
leap/Makefile Normal file
View file

@ -0,0 +1,9 @@
default: clean test
test:
dune runtest
clean:
dune clean
.PHONY: clean

51
leap/README.md Normal file
View file

@ -0,0 +1,51 @@
# Leap
Welcome to Leap on Exercism's OCaml Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
Given a year, report if it is a leap year.
The tricky thing here is that a leap year in the Gregorian calendar occurs:
```text
on every year that is evenly divisible by 4
except every year that is evenly divisible by 100
unless the year is also evenly divisible by 400
```
For example, 1997 is not a leap year, but 1996 is.
1900 is not a leap year, but 2000 is.
## Notes
Though our exercise adopts some very simple rules, there is more to learn!
For a delightful, four minute explanation of the whole leap year phenomenon, go watch [this youtube video][video].
[video]: https://www.youtube.com/watch?v=xX96xng7sAE
## Source
### Created by
- @tmcgilchrist
### Contributed to by
- @daveyarwood
- @dvberkel
- @iHiD
- @ismaelga
- @kytrinyx
- @marionebl
- @Peaupote
- @pminten
- @sbl
- @sshine
- @stevejb71
### Based on
CodeRanch Cattle Drive, Assignment 3 - https://coderanch.com/t/718816/Leap

16
leap/dune Normal file
View file

@ -0,0 +1,16 @@
(executable
(name test)
(libraries base ounit2))
(alias
(name runtest)
(deps (:x test.exe))
(action (run %{x})))
(alias
(name buildtest)
(deps (:x test.exe)))
(env
(dev
(flags (:standard -warn-error -A))))

1
leap/dune-project Normal file
View file

@ -0,0 +1 @@
(lang dune 1.1)

2
leap/leap.ml Normal file
View file

@ -0,0 +1,2 @@
let leap_year year =
(year mod 4 = 0 && year mod 100 <> 0) || year mod 400 = 0

1
leap/leap.mli Normal file
View file

@ -0,0 +1 @@
val leap_year: int -> bool

28
leap/test.ml Normal file
View file

@ -0,0 +1,28 @@
open OUnit2
open Leap
let ae exp got _test_ctxt = assert_equal exp got ~printer:string_of_bool
let tests = [
"year not divisible by 4 in common year" >::
ae false (leap_year 2015);
"year divisible by 2, not divisible by 4 in common year" >::
ae false (leap_year 1970);
"year divisible by 4, not divisible by 100 in leap year" >::
ae true (leap_year 1996);
"year divisible by 4 and 5 is still a leap year" >::
ae true (leap_year 1960);
"year divisible by 100, not divisible by 400 in common year" >::
ae false (leap_year 2100);
"year divisible by 100 but not by 3 is still not a leap year" >::
ae false (leap_year 1900);
"year divisible by 400 is leap year" >::
ae true (leap_year 2000);
"year divisible by 400 but not by 125 is still a leap year" >::
ae true (leap_year 2400);
"year divisible by 200, not divisible by 400 in common year" >::
ae false (leap_year 1800);
]
let () =
run_test_tt_main ("leap tests" >::: tests)