Merge branch 'feature/coverage-tool' into 'dev'

Use bisect for code coverage

See merge request ligolang/ligo!245
This commit is contained in:
Rémi Lesenechal 2020-01-02 01:07:39 +00:00
commit f946a14ca0
51 changed files with 510 additions and 206 deletions

3
.gitignore vendored
View File

@ -8,3 +8,6 @@ Version.ml
**/.DS_Store **/.DS_Store
.vscode/ .vscode/
/ligo.install /ligo.install
*.coverage
/_coverage/
/_coverage_*/

View File

@ -85,6 +85,10 @@ local-dune-job:
- scripts/install_vendors_deps.sh - scripts/install_vendors_deps.sh
- scripts/build_ligo_local.sh - scripts/build_ligo_local.sh
- dune runtest - dune runtest
- make coverage
artifacts:
paths:
- _coverage_all
# Run a docker build without publishing to the registry # Run a docker build without publishing to the registry
build-current-docker-image: build-current-docker-image:

View File

@ -30,3 +30,27 @@ test: build
export PATH="/usr/local/bin$${PATH:+:}$${PATH:-}" export PATH="/usr/local/bin$${PATH:+:}$${PATH:-}"
eval $$(opam config env) eval $$(opam config env)
scripts/test_ligo.sh scripts/test_ligo.sh
clean:
dune clean
rm -fr _coverage_all _coverage_cli _coverage_ligo
coverage: clean
BISECT_ENABLE=yes dune runtest --force
bisect-ppx-report html -o ./_coverage_all --title="LIGO overall test coverage"
bisect-ppx-report summary --per-file
coverage-ligo: clean
BISECT_ENABLE=yes dune runtest src/test --force
bisect-ppx-report html -o ./_coverage_ligo --title="LIGO test coverage"
bisect-ppx-report summary --per-file
coverage-doc: clean
BISECT_ENABLE=yes dune build @doc-test --force
bisect-ppx-report html -o ./_coverage_doc --title="LIGO doc coverage"
bisect-ppx-report summary --per-file
coverage-cli: clean
BISECT_ENABLE=yes dune runtest src/bin/expect_tests
bisect-ppx-report html -o ./_coverage_cli --title="CLI test coverage"
bisect-ppx-report summary --per-file

View File

@ -20,7 +20,7 @@ This means that every smart contract needs at least one entrypoint function, her
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
``` ```pascaligo group=a
type parameter is unit; type parameter is unit;
type store is unit; type store is unit;
function main(const parameter: parameter; const store: store): (list(operation) * store) is function main(const parameter: parameter; const store: store): (list(operation) * store) is
@ -46,7 +46,7 @@ This example shows how `amount` and `failwith` can be used to decline a transact
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
``` ```pascaligo group=b
function main (const p : unit ; const s : unit) : (list(operation) * unit) is function main (const p : unit ; const s : unit) : (list(operation) * unit) is
block { block {
if amount > 0mutez then failwith("This contract does not accept tez") else skip if amount > 0mutez then failwith("This contract does not accept tez") else skip
@ -60,7 +60,7 @@ This example shows how `sender` or `source` can be used to deny access to an ent
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
``` ```pascaligo group=c
const owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); const owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
function main (const p : unit ; const s : unit) : (list(operation) * unit) is function main (const p : unit ; const s : unit) : (list(operation) * unit) is
block { block {
@ -79,7 +79,7 @@ In our case, we have a `counter.ligo` contract that accepts a parameter of type
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
``` ```pascaligo group=dup
// counter.types.ligo // counter.types.ligo
type action is type action is
| Increment of int | Increment of int
@ -87,7 +87,7 @@ type action is
| Reset of unit | Reset of unit
``` ```
``` ```pascaligo group=d
// counter.ligo // counter.ligo
type action is type action is
| Increment of int | Increment of int
@ -95,7 +95,7 @@ type action is
| Reset of unit | Reset of unit
``` ```
``` ```pascaligo gorup=d
// proxy.ligo // proxy.ligo
#include "counter.types.ligo" #include "counter.types.ligo"

View File

@ -14,7 +14,7 @@ You can obtain the current time using the built-in syntax specific expression, p
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=a
const today: timestamp = now; const today: timestamp = now;
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
@ -26,7 +26,7 @@ In LIGO, timestamps can be added with `int`(s), this enables you to set e.g. tim
#### In 24 hours #### In 24 hours
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=b
const today: timestamp = now; const today: timestamp = now;
const one_day: int = 86400; const one_day: int = 86400;
const in_24_hrs: timestamp = today + one_day; const in_24_hrs: timestamp = today + one_day;
@ -36,10 +36,10 @@ const in_24_hrs: timestamp = today + one_day;
#### 24 hours ago #### 24 hours ago
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=c
const today: timestamp = now; const today: timestamp = now;
const one_day: int = 86400; const one_day: int = 86400;
const 24_hrs_ago: timestamp = today - one_day; const in_24_hrs: timestamp = today - one_day;
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
@ -49,7 +49,7 @@ You can also compare timestamps using the same comparison operators as for numbe
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=c
const not_tommorow: bool = (now = in_24_hrs) const not_tommorow: bool = (now = in_24_hrs)
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
@ -62,7 +62,7 @@ Here's how you can define an address:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=d
const my_account: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); const my_account: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->

View File

@ -11,18 +11,18 @@ Here's how to define a boolean:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=a
const a: bool = True; const a: bool = True;
const b: bool = False; const b: bool = False;
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=a
let a: bool = true let a: bool = true
let b: bool = false let b: bool = false
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=a
let a: bool = true; let a: bool = true;
let b: bool = false; let b: bool = false;
``` ```
@ -37,24 +37,24 @@ In LIGO, only values of the same type can be compared. We call these "comparable
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=b
const a: string = "Alice"; const a: string = "Alice";
const b: string = "Alice"; const b: string = "Alice";
// True // True
const c: bool = (a = b); const c: bool = (a = b);
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=b
let a: string = "Alice" let a: string = "Alice"
let b: string = "Alice" let b: string = "Alice"
// true // true
let c: bool = (a = b) let c: bool = (a = b)
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=b
let a: string = "Alice"; let a: string = "Alice";
let b: string = "Alice"; let b: string = "Alice";
/* true */ (* true *)
let c: bool = (a == b); let c: bool = (a == b);
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
@ -64,7 +64,7 @@ let c: bool = (a == b);
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=c
const a: int = 5; const a: int = 5;
const b: int = 4; const b: int = 4;
const c: bool = (a = b); const c: bool = (a = b);
@ -75,7 +75,7 @@ const g: bool = (a >= b);
const h: bool = (a =/= b); const h: bool = (a =/= b);
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=c
let a: int = 5 let a: int = 5
let b: int = 4 let b: int = 4
let c: bool = (a = b) let c: bool = (a = b)
@ -83,11 +83,11 @@ let d: bool = (a > b)
let e: bool = (a < b) let e: bool = (a < b)
let f: bool = (a <= b) let f: bool = (a <= b)
let g: bool = (a >= b) let g: bool = (a >= b)
let h: bool = (a =/= b) let h: bool = (a <> b)
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=c
let a: int = 5; let a: int = 5;
let b: int = 4; let b: int = 4;
let c: bool = (a == b); let c: bool = (a == b);
@ -106,23 +106,23 @@ let h: bool = (a != b);
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=d
const a: tez = 5mutez; const a: tez = 5mutez;
const b: tez = 10mutez; const b: tez = 10mutez;
const c: bool = (a = b); const c: bool = (a = b);
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=d
let a: tez = 5mutez let a: tez = 5mutez
let b: tez = 10mutez let b: tez = 10mutez
// false // false
let c: bool = (a = b) let c: bool = (a = b)
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=d
let a: tez = 5mutez; let a: tez = 5mutez;
let b: tez = 10mutez; let b: tez = 10mutez;
/* false */ (* false *)
let c: bool = (a == b); let c: bool = (a == b);
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
@ -136,7 +136,7 @@ Conditional logic is an important part of every real world program.
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=e
const min_age: nat = 16n; const min_age: nat = 16n;
(* (*
@ -162,7 +162,7 @@ function is_adult(const age: nat): bool is
> ``` > ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=e
let min_age: nat = 16n let min_age: nat = 16n
(** (**
@ -177,17 +177,17 @@ let is_adult (age: nat) : bool =
if (age > min_age) then true else false if (age > min_age) then true else false
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=e
let min_age: nat = 16n; let min_age: nat = 16n;
/** (**
This function is really obnoxious, but it showcases This function is really obnoxious, but it showcases
how the if statement and it's syntax can be used. how the if statement and it's syntax can be used.
Normally, you'd use `with (age > min_age)` instead. Normally, you'd use `with (age > min_age)` instead.
*/ *)
let is_adult = (age: nat): bool => let is_adult = (age: nat): bool =>
if (age > min_age) { if (age > min_age) {

View File

@ -13,7 +13,7 @@ Each `block` needs to include at least one `instruction`, or a *placeholder* ins
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo skip
// shorthand syntax // shorthand syntax
block { skip } block { skip }
// verbose syntax // verbose syntax
@ -34,7 +34,7 @@ Functions in PascaLIGO are defined using the `function` keyword followed by thei
Here's how you define a basic function that accepts two `ints` and returns a single `int`: Here's how you define a basic function that accepts two `ints` and returns a single `int`:
```pascaligo ```pascaligo group=a
function add(const a: int; const b: int): int is function add(const a: int; const b: int): int is
begin begin
const result: int = a + b; const result: int = a + b;
@ -51,7 +51,7 @@ The function body consists of two parts:
Functions that can contain all of their logic into a single instruction/expression, can be defined without the surrounding `block`. Functions that can contain all of their logic into a single instruction/expression, can be defined without the surrounding `block`.
Instead, you can inline the necessary logic directly, like this: Instead, you can inline the necessary logic directly, like this:
```pascaligo ```pascaligo group=b
function add(const a: int; const b: int): int is a + b function add(const a: int; const b: int): int is a + b
``` ```
@ -63,7 +63,7 @@ along with a return type.
Here's how you define a basic function that accepts two `ints` and returns an `int` as well: Here's how you define a basic function that accepts two `ints` and returns an `int` as well:
```cameligo ```cameligo group=b
let add (a: int) (b: int) : int = a + b let add (a: int) (b: int) : int = a + b
``` ```
@ -79,7 +79,7 @@ along with a return type.
Here's how you define a basic function that accepts two `ints` and returns an `int` as well: Here's how you define a basic function that accepts two `ints` and returns an `int` as well:
```reasonligo ```reasonligo group=b
let add = (a: int, b: int) : int => a + b; let add = (a: int, b: int) : int => a + b;
``` ```
@ -90,7 +90,7 @@ value.
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=b
const increment : (int -> int) = (function (const i : int) : int is i + 1); const increment : (int -> int) = (function (const i : int) : int is i + 1);
// a = 2 // a = 2
const a: int = increment(1); const a: int = increment(1);
@ -104,19 +104,19 @@ Functions without a name, also known as anonymous functions are useful in cases
Here's how to define an anonymous function assigned to a variable `increment`, with it's appropriate function type signature. Here's how to define an anonymous function assigned to a variable `increment`, with it's appropriate function type signature.
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=c
const increment : (int -> int) = (function (const i : int) : int is i + 1); const increment : (int -> int) = (function (const i : int) : int is i + 1);
// a = 2 // a = 2
const a: int = increment(1); const a: int = increment(1);
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=c
let increment : (int -> int) = fun (i: int) -> i + 1 let increment : (int -> int) = fun (i: int) -> i + 1
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=c
let increment: (int => int) = (i: int) => i + 1; let increment: (int => int) = (i: int) => i + 1;
``` ```

View File

@ -135,21 +135,21 @@ otherwise.
function iter_op (const m : ledger) : unit is function iter_op (const m : ledger) : unit is
block { block {
function aggregate (const i : address ; const j : tez) : unit is block function aggregate (const i : address ; const j : tez) : unit is block
{ if (j > 100) then skip else failwith("fail") } with unit ; { if (j > 100mutez) then skip else failwith("fail") } with unit ;
} with map_iter(aggregate, m) ; } with map_iter(aggregate, m) ;
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo
let iter_op (m : ledger) : unit = let iter_op (m : ledger) : unit =
let assert_eq = fun (i: address) (j: tez) -> assert (j > 100) let assert_eq = fun (i: address) (j: tez) -> assert (j > 100tz)
in Map.iter assert_eq m in Map.iter assert_eq m
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo
let iter_op = (m: ledger): unit => { let iter_op = (m: ledger): unit => {
let assert_eq = (i: address, j: tez) => assert(j > 100); let assert_eq = (i: address, j: tez) => assert(j > 100mutez);
Map.iter(assert_eq, m); Map.iter(assert_eq, m);
}; };
``` ```
@ -162,21 +162,21 @@ let iter_op = (m: ledger): unit => {
```pascaligo ```pascaligo
function map_op (const m : ledger) : ledger is function map_op (const m : ledger) : ledger is
block { block {
function increment (const i : address ; const j : tez) : tez is block { skip } with j + 1 ; function increment (const i : address ; const j : tez) : tez is block { skip } with j + 1mutez ;
} with map_map(increment, m) ; } with map_map(increment, m) ;
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo
let map_op (m : ledger) : ledger = let map_op (m : ledger) : ledger =
let increment = fun (_: address) (j: tez) -> j+1 let increment = fun (_: address) (j: tez) -> j + 1tz
in Map.map increment m in Map.map increment m
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo
let map_op = (m: ledger): ledger => { let map_op = (m: ledger): ledger => {
let increment = (ignore: address, j: tez) => j + 1; let increment = (ignore: address, j: tez) => j + 1tz;
Map.map(increment, m); Map.map(increment, m);
}; };
``` ```
@ -196,22 +196,22 @@ It eventually returns the result of combining all the elements.
```pascaligo ```pascaligo
function fold_op (const m : ledger) : tez is function fold_op (const m : ledger) : tez is
block { block {
function aggregate (const i : address ; const j : (tez * tez)) : tez is block { skip } with j.0 + j.1 ; function aggregate (const j : tez ; const cur : (address * tez)) : tez is j + cur.1 ;
} with map_fold(aggregate, m , 10) } with map_fold(aggregate, m , 10mutez)
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo
let fold_op (m : ledger) : ledger = let fold_op (m : ledger) : ledger =
let aggregate = fun (ignore: address) (j: tez * tez) -> j.0 + j.1 let aggregate = fun (j: tez) (cur: address * tez) -> j + cur.1 in
in Map.fold aggregate m 10 Map.fold aggregate m 10tz
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo
let fold_op = (m: ledger): ledger => { let fold_op = (m: ledger): ledger => {
let aggregate = (ignore: address, j: (tez, tez)) => j[0] + j[1]; let aggregate = (j: tez, cur: (address, tez)) => j + cur[1];
Map.fold(aggregate, m, 10); Map.fold(aggregate, m, 10tz);
}; };
``` ```

View File

@ -14,7 +14,7 @@ In the following example you can find a series of arithmetic operations, includi
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=a
// int + int produces int // int + int produces int
const a: int = 5 + 10; const a: int = 5 + 10;
// nat + int produces int // nat + int produces int
@ -38,7 +38,7 @@ const g: int = 1_000_000;
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=a
// int + int produces int // int + int produces int
let a: int = 5 + 10 let a: int = 5 + 10
// nat + int produces int // nat + int produces int
@ -62,19 +62,19 @@ let g: int = 1_000_000
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=a
/* int + int produces int */ (* int + int produces int *)
let a: int = 5 + 10; let a: int = 5 + 10;
/* nat + int produces int */ (* nat + int produces int *)
let b: int = 5n + 10; let b: int = 5n + 10;
/* tez + tez produces tez */ (* tez + tez produces tez *)
let c: tez = 5mutez + 10mutez; let c: tez = 5mutez + 10mutez;
/* you can't add tez + int or tez + nat, this won't compile */ (* you can't add tez + int or tez + nat, this won't compile:
/* let d: tez = 5mutez + 10n; */ let d: tez = 5mutez + 10n; *)
/* two nats produce a nat */ (* two nats produce a nat *)
let e: nat = 5n + 10n; let e: nat = 5n + 10n;
/* nat + int produces an int, this won't compile */ (* nat + int produces an int, this won't compile:
/* let f: nat = 5n + 10; */ let f: nat = 5n + 10; *)
let g: int = 1_000_000; let g: int = 1_000_000;
``` ```
@ -94,33 +94,33 @@ The simpliest substraction looks like this:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=b
const a: int = 5 - 10; const a: int = 5 - 10;
// substraction of two nats, yields an int // substraction of two nats, yields an int
const b: int = 5n - 2n; const b: int = 5n - 2n;
// won't compile, result is an int, not a nat // won't compile, result is an int, not a nat
// const c: nat = 5n - 2n; // const c: nat = 5n - 2n;
const d: tez = 5mutez - 1mt; const d: tez = 5mutez - 1mutez;
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=b
let a: int = 5 - 10 let a: int = 5 - 10
// substraction of two nats, yields an int // substraction of two nats, yields an int
let b: int = 5n - 2n let b: int = 5n - 2n
// won't compile, result is an int, not a nat // won't compile, result is an int, not a nat
// const c: nat = 5n - 2n // const c: nat = 5n - 2n
let d: tez = 5mutez - 1mt let d: tez = 5mutez - 1mutez
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=b
let a: int = 5 - 10; let a: int = 5 - 10;
/* substraction of two nats, yields an int */ (* substraction of two nats, yields an int *)
let b: int = 5n - 2n; let b: int = 5n - 2n;
/* won't compile, result is an int, not a nat */ (* won't compile, result is an int, not a nat *)
/* let c: nat = 5n - 2n; */ (* let c: nat = 5n - 2n; *)
let d: tez = 5mutez - 1mt; let d: tez = 5mutez - 1mutez;
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
@ -133,7 +133,7 @@ You can multiply values of the same type, such as:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=c
const a: int = 5 * 5; const a: int = 5 * 5;
const b: nat = 5n * 5n; const b: nat = 5n * 5n;
// you can also multiply `nat` and `tez` // you can also multiply `nat` and `tez`
@ -141,7 +141,7 @@ const c: tez = 5n * 5mutez;
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=c
let a: int = 5 * 5 let a: int = 5 * 5
let b: nat = 5n * 5n let b: nat = 5n * 5n
// you can also multiply `nat` and `tez` // you can also multiply `nat` and `tez`
@ -149,10 +149,10 @@ let c: tez = 5n * 5mutez
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=c
let a: int = 5 * 5; let a: int = 5 * 5;
let b: nat = 5n * 5n; let b: nat = 5n * 5n;
/* you can also multiply `nat` and `tez` */ (* you can also multiply `nat` and `tez` *)
let c: tez = 5n * 5mutez; let c: tez = 5n * 5mutez;
``` ```
@ -167,21 +167,21 @@ In LIGO you can divide `int`, `nat`, and `tez`. Here's how:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=d
const a: int = 10 / 3; const a: int = 10 / 3;
const b: nat = 10n / 3n; const b: nat = 10n / 3n;
const c: nat = 10mutez / 3mutez; const c: nat = 10mutez / 3mutez;
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=d
let a: int = 10 / 3 let a: int = 10 / 3
let b: nat = 10n / 3n let b: nat = 10n / 3n
let c: nat = 10mutez / 3mutez let c: nat = 10mutez / 3mutez
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=d
let a: int = 10 / 3; let a: int = 10 / 3;
let b: nat = 10n / 3n; let b: nat = 10n / 3n;
let c: nat = 10mutez / 3mutez; let c: nat = 10mutez / 3mutez;
@ -195,13 +195,13 @@ You can *cast* an `int` to a `nat` and vice versa, here's how:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=e
const a: int = int(1n); const a: int = int(1n);
const b: nat = abs(1); const b: nat = abs(1);
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=e
let a: int = int(1n); let a: int = int(1n);
let b: nat = abs(1); let b: nat = abs(1);
``` ```

View File

@ -15,7 +15,7 @@ Sets are similar to lists. The main difference is that elements of a `set` must
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=a
type int_set is set(int); type int_set is set(int);
const my_set: int_set = set const my_set: int_set = set
1; 1;
@ -25,14 +25,14 @@ end
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=a
type int_set = int set type int_set = int set
let my_set: int_set = let my_set: int_set =
Set.add 3 (Set.add 2 (Set.add 1 (Set.empty: int set))) Set.add 3 (Set.add 2 (Set.add 1 (Set.empty: int set)))
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=a
type int_set = set(int); type int_set = set(int);
let my_set: int_set = let my_set: int_set =
Set.add(3, Set.add(2, Set.add(1, Set.empty: set(int)))); Set.add(3, Set.add(2, Set.add(1, Set.empty: set(int))));
@ -44,16 +44,16 @@ let my_set: int_set =
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=a
const my_set: int_set = set end; const my_set: int_set = set end;
const my_set_2: int_set = set_empty; const my_set_2: int_set = set_empty;
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=a
let my_set: int_set = (Set.empty: int set) let my_set: int_set = (Set.empty: int set)
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=a
let my_set: int_set = (Set.empty: set(int)); let my_set: int_set = (Set.empty: set(int));
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
@ -62,18 +62,18 @@ let my_set: int_set = (Set.empty: set(int));
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=a
const contains_three: bool = my_set contains 3; const contains_three: bool = my_set contains 3;
// or alternatively // or alternatively
const contains_three_fn: bool = set_mem(3, my_set); const contains_three_fn: bool = set_mem(3, my_set);
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=a
let contains_three: bool = Set.mem 3 my_set let contains_three: bool = Set.mem 3 my_set
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=a
let contains_three: bool = Set.mem(3, my_set); let contains_three: bool = Set.mem(3, my_set);
``` ```
@ -83,17 +83,17 @@ let contains_three: bool = Set.mem(3, my_set);
### Obtaining the size of a set ### Obtaining the size of a set
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=a
const set_size: nat = size(my_set); const set_size: nat = size(my_set);
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=a
let set_size: nat = Set.size my_set let set_size: nat = Set.size my_set
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=a
let set_size: nat = Set.size(my_set); let set_size: nat = Set.size(my_set);
``` ```
@ -103,21 +103,21 @@ let set_size: nat = Set.size(my_set);
### Modifying a set ### Modifying a set
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=a
const larger_set: int_set = set_add(4, my_set); const larger_set: int_set = set_add(4, my_set);
const smaller_set: int_set = set_remove(3, my_set); const smaller_set: int_set = set_remove(3, my_set);
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=a
let larger_set: int_set = Set.add 4 my_set let larger_set: int_set = Set.add 4 my_set
let smaller_set: int_set = Set.remove 3 my_set let smaller_set: int_set = Set.remove 3 my_set
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=a
let larger_set: int_set = Set.add(4, my_set); let larger_set: int_set = Set.add(4, my_set);
let smaller_set: int_set = Set.remove(3, my_set); let smaller_set: int_set = Set.remove(3, my_set);
``` ```
@ -128,20 +128,20 @@ let smaller_set: int_set = Set.remove(3, my_set);
### Folding a set ### Folding a set
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=a
function sum(const result: int; const i: int): int is result + i; function sum(const result: int; const i: int): int is result + i;
// Outputs 6 // Outputs 6
const sum_of_a_set: int = set_fold(sum, my_set, 0); const sum_of_a_set: int = set_fold(sum, my_set, 0);
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=a
let sum (result: int) (i: int) : int = result + i let sum (result: int) (i: int) : int = result + i
let sum_of_a_set: int = Set.fold sum my_set 0 let sum_of_a_set: int = Set.fold sum my_set 0
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=a
let sum = (result: int, i: int): int => result + i; let sum = (result: int, i: int): int => result + i;
let sum_of_a_set: int = Set.fold(sum, my_set, 0); let sum_of_a_set: int = Set.fold(sum, my_set, 0);
``` ```
@ -157,7 +157,7 @@ Lists are similar to sets, but their elements don't need to be unique and they d
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=b
type int_list is list(int); type int_list is list(int);
const my_list: int_list = list const my_list: int_list = list
1; 1;
@ -167,13 +167,13 @@ end
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=b
type int_list = int list type int_list = int list
let my_list: int_list = [1; 2; 3] let my_list: int_list = [1; 2; 3]
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=b
type int_list = list(int); type int_list = list(int);
let my_list: int_list = [1, 2, 3]; let my_list: int_list = [1, 2, 3];
``` ```
@ -185,21 +185,21 @@ let my_list: int_list = [1, 2, 3];
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=b
const larger_list: int_list = cons(4, my_list); const larger_list: int_list = cons(4, my_list);
const even_larger_list: int_list = 5 # larger_list; const even_larger_list: int_list = 5 # larger_list;
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=b
let larger_list: int_list = 4 :: my_list let larger_list: int_list = 4 :: my_list
(* CameLIGO doesn't have a List.cons *) (* CameLIGO doesn't have a List.cons *)
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=b
let larger_list: int_list = [4, ...my_list]; let larger_list: int_list = [4, ...my_list];
/* ReasonLIGO doesn't have a List.cons */ (* ReasonLIGO doesn't have a List.cons *)
``` ```
<!--END_DOCUSAURUS_CODE_TABS--> <!--END_DOCUSAURUS_CODE_TABS-->
@ -211,7 +211,7 @@ let larger_list: int_list = [4, ...my_list];
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=b
function increment(const i: int): int is block { skip } with i + 1; function increment(const i: int): int is block { skip } with i + 1;
// Creates a new list with elements incremented by 1 // Creates a new list with elements incremented by 1
const incremented_list: int_list = list_map(increment, even_larger_list); const incremented_list: int_list = list_map(increment, even_larger_list);
@ -219,7 +219,7 @@ const incremented_list: int_list = list_map(increment, even_larger_list);
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=b
let increment (i: int) : int = i + 1 let increment (i: int) : int = i + 1
(* Creates a new list with elements incremented by 1 *) (* Creates a new list with elements incremented by 1 *)
let incremented_list: int_list = List.map increment larger_list let incremented_list: int_list = List.map increment larger_list
@ -228,9 +228,9 @@ let incremented_list: int_list = List.map increment larger_list
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=b
let increment = (i: int): int => i + 1; let increment = (i: int): int => i + 1;
/* Creates a new list with elements incremented by 1 */ (* Creates a new list with elements incremented by 1 *)
let incremented_list: int_list = List.map(increment, larger_list); let incremented_list: int_list = List.map(increment, larger_list);
``` ```
@ -240,7 +240,7 @@ let incremented_list: int_list = List.map(increment, larger_list);
### Folding of a list: ### Folding of a list:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=b
function sum(const result: int; const i: int): int is block { skip } with result + i; function sum(const result: int; const i: int): int is block { skip } with result + i;
// Outputs 6 // Outputs 6
const sum_of_a_list: int = list_fold(sum, my_list, 0); const sum_of_a_list: int = list_fold(sum, my_list, 0);
@ -248,7 +248,7 @@ const sum_of_a_list: int = list_fold(sum, my_list, 0);
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=b
let sum (result: int) (i: int) : int = result + i let sum (result: int) (i: int) : int = result + i
// Outputs 6 // Outputs 6
let sum_of_a_list: int = List.fold sum my_list 0 let sum_of_a_list: int = List.fold sum my_list 0
@ -256,9 +256,9 @@ let sum_of_a_list: int = List.fold sum my_list 0
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=b
let sum = (result: int, i: int): int => result + i; let sum = (result: int, i: int): int => result + i;
/* Outputs 6 */ (* Outputs 6 *)
let sum_of_a_list: int = List.fold(sum, my_list, 0); let sum_of_a_list: int = List.fold(sum, my_list, 0);
``` ```
@ -287,22 +287,22 @@ sake of illustration.
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=c
type full_name is string * string; type full_name is string * string;
const full_name: full_name = ("Alice", "Johnson"); const full_name: full_name = ("Alice", "Johnson");
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=c
type full_name = string * string type full_name = string * string
(* The parenthesis here are optional *) (* The parenthesis here are optional *)
let full_name: full_name = ("Alice", "Johnson") let full_name: full_name = ("Alice", "Johnson")
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=c
type full_name = (string, string); type full_name = (string, string);
/* The parenthesis here are optional */ (* The parenthesis here are optional *)
let full_name: full_name = ("Alice", "Johnson"); let full_name: full_name = ("Alice", "Johnson");
``` ```
@ -320,17 +320,17 @@ Tuple elements are one-indexed and accessed like so:
<!--DOCUSAURUS_CODE_TABS--> <!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo--> <!--Pascaligo-->
```pascaligo ```pascaligo group=c
const first_name: string = full_name.1; const first_name: string = full_name.1;
``` ```
<!--CameLIGO--> <!--CameLIGO-->
```cameligo ```cameligo group=c
let first_name: string = full_name.1 let first_name: string = full_name.1
``` ```
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo group=c
let first_name: string = full_name[1]; let first_name: string = full_name[1];
``` ```

View File

@ -59,12 +59,12 @@ let ledger: account_balances = Map.literal
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo
/* account_balances is a simple type, a map of address <-> tez */ (* account_balances is a simple type, a map of address <-> tez *)
type account_balances = map(address, tez); type account_balances = map(address, tez);
let ledger: account_balances = let ledger: account_balances =
Map.literal([ Map.literal([
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, 10mutez), ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, 10mutez)
]); ]);
``` ```
@ -124,25 +124,23 @@ let ledger: account_balances = Map.literal
<!--ReasonLIGO--> <!--ReasonLIGO-->
```reasonligo ```reasonligo
/* alias two types */ (* alias two types *)
type account = address; type account = address;
type number_of_transactions = nat; type number_of_transactions = nat;
/* account_data consists of a record with two fields (balance, number_of_transactions) */ (* account_data consists of a record with two fields (balance, number_of_transactions) *)
type account_data = { type account_data = {
balance: tez, balance: tez,
number_of_transactions, number_of_transactions,
}; };
/* our ledger / account_balances is a map of account <-> account_data */ (* our ledger / account_balances is a map of account <-> account_data *)
type account_balances = map(account, account_data); type account_balances = map(account, account_data);
/* pseudo-JSON representation of our map */ (* pseudo-JSON representation of our map
/* {"tz1...": {balance: 10mutez, number_of_transactions: 5n}} */ {"tz1...": {balance: 10mutez, number_of_transactions: 5n}} *)
let ledger: account_balances = let ledger: account_balances =
Map.literal([ Map.literal([
( ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address,
"tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, {balance: 10mutez, number_of_transactions: 5n})
{balance: 10mutez, number_of_transactions: 5n},
),
]); ]);
``` ```

View File

@ -17,7 +17,7 @@ In the [previous tutorial](tutorials/get-started/tezos-taco-shop-smart-contract.
## Analyzing the current contract ## Analyzing the current contract
### **`taco-shop.ligo`** ### **`taco-shop.ligo`**
```pascaligo ```pascaligo group=a
type taco_supply is record type taco_supply is record
current_stock : nat; current_stock : nat;
max_price : tez; max_price : tez;
@ -34,7 +34,7 @@ function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_sho
if amount =/= current_purchase_price then if amount =/= current_purchase_price then
// we won't sell tacos if the amount isn't correct // we won't sell tacos if the amount isn't correct
fail("Sorry, the taco you're trying to purchase has a different price"); failwith("Sorry, the taco you're trying to purchase has a different price");
else else
// Decrease the stock by 1n, because we've just sold one // Decrease the stock by 1n, because we've just sold one
taco_kind.current_stock := abs(taco_kind.current_stock - 1n); taco_kind.current_stock := abs(taco_kind.current_stock - 1n);
@ -47,7 +47,7 @@ function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_sho
### Purchase price formula ### Purchase price formula
Pedro's Taco Shop contract currently enables customers to buy tacos, at a computed price based on a simple formula. Pedro's Taco Shop contract currently enables customers to buy tacos, at a computed price based on a simple formula.
```pascaligo ```pascaligo skip
const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock; const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock;
``` ```
@ -67,8 +67,8 @@ This means that after all the *purchase conditions* of our contract are met - e.
### Defining the recipient ### Defining the recipient
In order to send tokens, we will need a receiver address - which in our case will be Pedro's personal account. Additionally we'll wrap the given address as a *`contract(unit)`* - which represents either a contract with no parameters, or an implicit account. In order to send tokens, we will need a receiver address - which in our case will be Pedro's personal account. Additionally we'll wrap the given address as a *`contract(unit)`* - which represents either a contract with no parameters, or an implicit account.
```pascaligo ```pascaligo group=ex1
const ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV"; const ownerAddress : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address);
const receiver : contract(unit) = get_contract(ownerAddress); const receiver : contract(unit) = get_contract(ownerAddress);
``` ```
@ -78,7 +78,7 @@ const receiver : contract(unit) = get_contract(ownerAddress);
Now we can transfer the `amount` received by `buy_taco` to Pedro's `ownerAddress`. We will do so by forging a `transaction(unit, amount, receiver)` within a list of operations returned at the end of our contract. Now we can transfer the `amount` received by `buy_taco` to Pedro's `ownerAddress`. We will do so by forging a `transaction(unit, amount, receiver)` within a list of operations returned at the end of our contract.
```pascaligo ```pascaligo group=ex1
const payoutOperation : operation = transaction(unit, amount, receiver) ; const payoutOperation : operation = transaction(unit, amount, receiver) ;
const operations : list(operation) = list const operations : list(operation) = list
payoutOperation payoutOperation
@ -90,14 +90,14 @@ end;
## Finalizing the contract ## Finalizing the contract
### **`taco-shop.ligo`** ### **`taco-shop.ligo`**
```pascaligo ```pascaligo group=b
type taco_supply is record type taco_supply is record
current_stock : nat; current_stock : nat;
max_price : tez; max_price : tez;
end end
type taco_shop_storage is map(nat, taco_supply); type taco_shop_storage is map(nat, taco_supply);
const ownerAddress: address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV"; const ownerAddress: address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address);
function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is
begin begin
@ -108,7 +108,7 @@ function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_sho
if amount =/= current_purchase_price then if amount =/= current_purchase_price then
// we won't sell tacos if the amount isn't correct // we won't sell tacos if the amount isn't correct
fail("Sorry, the taco you're trying to purchase has a different price"); failwith("Sorry, the taco you're trying to purchase has a different price");
else else
// Decrease the stock by 1n, because we've just sold one // Decrease the stock by 1n, because we've just sold one
taco_kind.current_stock := abs(taco_kind.current_stock - 1n); taco_kind.current_stock := abs(taco_kind.current_stock - 1n);
@ -130,7 +130,7 @@ function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_sho
To confirm that our contract is valid, we can dry run it. As a result we see a *new operation* in the list of returned operations to be executed subsequently. To confirm that our contract is valid, we can dry run it. As a result we see a *new operation* in the list of returned operations to be executed subsequently.
```pascaligo ```pascaligo skip
ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 buy_taco 1n "map ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 buy_taco 1n "map
1n -> record 1n -> record
current_stock = 50n; current_stock = 50n;
@ -158,12 +158,12 @@ end"
Because Pedro is a member of the (STA) Specialty Taco Association, he has decided to donate **10%** of the earnings to the STA. We'll just add a `donationAddress` to the contract, and compute a 10% donation sum from each taco purchase. Because Pedro is a member of the (STA) Specialty Taco Association, he has decided to donate **10%** of the earnings to the STA. We'll just add a `donationAddress` to the contract, and compute a 10% donation sum from each taco purchase.
```pascaligo ```pascaligo group=bonus
const ownerAddress: address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV"; const ownerAddress: address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address);
const donationAddress: address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; const donationAddress: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address);
``` ```
```pascaligo ```pascaligo group=bonus
const receiver : contract(unit) = get_contract(ownerAddress); const receiver : contract(unit) = get_contract(ownerAddress);
const donationReceiver : contract(unit) = get_contract(donationAddress); const donationReceiver : contract(unit) = get_contract(donationAddress);

View File

@ -35,7 +35,7 @@ Each taco kind, has its own `max_price` that it sells for, and a finite supply f
Current purchase price is calculated with the following equation: Current purchase price is calculated with the following equation:
```pascaligo ```pascaligo skip
current_purchase_price = max_price / available_stock current_purchase_price = max_price / available_stock
``` ```
@ -71,7 +71,7 @@ The best way to install the dockerized LIGO is as a **global executable** throug
To begin implementing our smart contract, we need an entry point. We'll call it `main` and it'll specify our contract's storage (`int`) and input parameter (`int`). Of course this is not the final storage/parameter of our contract, but it's something to get us started and test our LIGO installation as well. To begin implementing our smart contract, we need an entry point. We'll call it `main` and it'll specify our contract's storage (`int`) and input parameter (`int`). Of course this is not the final storage/parameter of our contract, but it's something to get us started and test our LIGO installation as well.
### `taco-shop.ligo` ### `taco-shop.ligo`
```pascaligo ```pascaligo group=a
function main (const parameter: int; const contractStorage: int) : (list(operation) * int) is function main (const parameter: int; const contractStorage: int) : (list(operation) * int) is
block {skip} with ((nil : list(operation)), contractStorage + parameter) block {skip} with ((nil : list(operation)), contractStorage + parameter)
``` ```
@ -129,7 +129,7 @@ ligo dry-run taco-shop.ligo --syntax pascaligo main 4 3
We know that Pedro's Taco Shop serves two kinds of tacos, so we'll need to manage stock individually, per kind. Let's define a type, that will keep the `stock` & `max_price` per kind - in a record with two fields. Additionally, we'll want to combine our `taco_supply` type into a map, consisting of the entire offer of Pedro's shop. We know that Pedro's Taco Shop serves two kinds of tacos, so we'll need to manage stock individually, per kind. Let's define a type, that will keep the `stock` & `max_price` per kind - in a record with two fields. Additionally, we'll want to combine our `taco_supply` type into a map, consisting of the entire offer of Pedro's shop.
**Taco shop's storage** **Taco shop's storage**
```pascaligo ```pascaligo group=b
type taco_supply is record type taco_supply is record
current_stock : nat; current_stock : nat;
max_price : tez; max_price : tez;
@ -141,7 +141,7 @@ type taco_shop_storage is map(nat, taco_supply);
Next step is to update the `main` entry point to include `taco_shop_storage` as its storage - while doing that let's set the `parameter` to `unit` as well to clear things up. Next step is to update the `main` entry point to include `taco_shop_storage` as its storage - while doing that let's set the `parameter` to `unit` as well to clear things up.
**`taco-shop.ligo`** **`taco-shop.ligo`**
```pascaligo ```pascaligo group=b+
type taco_supply is record type taco_supply is record
current_stock : nat; current_stock : nat;
max_price : tez; max_price : tez;
@ -208,7 +208,7 @@ Let's start by customizing our contract a bit, we will:
- change `taco_shop_storage` to a `var` instead of a `const`, because we'll want to modify it - change `taco_shop_storage` to a `var` instead of a `const`, because we'll want to modify it
**`taco-shop.ligo`** **`taco-shop.ligo`**
```pascaligo ```pascaligo group=c
type taco_supply is record type taco_supply is record
current_stock : nat; current_stock : nat;
max_price : tez; max_price : tez;
@ -231,7 +231,7 @@ In order to decrease the stock in our contract's storage for a specific taco kin
**`taco-shop.ligo`** **`taco-shop.ligo`**
```pascaligo ```pascaligo group=d
type taco_supply is record type taco_supply is record
current_stock : nat; current_stock : nat;
max_price : tez; max_price : tez;
@ -266,7 +266,7 @@ To make sure we get paid, we will:
- if yes, stock for the given `taco_kind` will be decreased and the payment accepted - if yes, stock for the given `taco_kind` will be decreased and the payment accepted
**`taco-shop.ligo`** **`taco-shop.ligo`**
```pascaligo ```pascaligo group=e
type taco_supply is record type taco_supply is record
current_stock : nat; current_stock : nat;
max_price : tez; max_price : tez;
@ -282,7 +282,7 @@ function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_sho
if amount =/= current_purchase_price then if amount =/= current_purchase_price then
// we won't sell tacos if the amount isn't correct // we won't sell tacos if the amount isn't correct
fail("Sorry, the taco you're trying to purchase has a different price"); failwith("Sorry, the taco you're trying to purchase has a different price");
else else
// Decrease the stock by 1n, because we've just sold one // Decrease the stock by 1n, because we've just sold one
taco_kind.current_stock := abs(taco_kind.current_stock - 1n); taco_kind.current_stock := abs(taco_kind.current_stock - 1n);
@ -324,14 +324,14 @@ end"
## 💰 Bonus: *Accepting tips above the taco purchase price* ## 💰 Bonus: *Accepting tips above the taco purchase price*
If you'd like to accept tips in your contract as well, simply change the following line, depending on which behavior do you prefer. If you'd like to accept tips in your contract as well, simply change the following line, depending on your preference.
**Without tips** **Without tips**
```pascaligo ```pascaligo skip
if amount =/= current_purchase_price then if amount =/= current_purchase_price then
``` ```
**With tips** **With tips**
```pascaligo ```pascaligo skip
if amount >= current_purchase_price then if amount >= current_purchase_price then
``` ```

View File

@ -11,6 +11,7 @@ depends: [
"ocamlfind" { build } "ocamlfind" { build }
"dune" { build & = "1.11.4" } "dune" { build & = "1.11.4" }
"menhir" { = "20190626" } "menhir" { = "20190626" }
"bisect_ppx" {dev & >= "2.0.0"}
"ppx_let" "ppx_let"
"ppx_deriving" "ppx_deriving"
"ppx_expect" "ppx_expect"
@ -20,7 +21,10 @@ depends: [
"alcotest" { with-test } "alcotest" { with-test }
"getopt" "getopt"
# work around upstream in-place update # work around upstream in-place update
"ocaml-migrate-parsetree" { = "1.3.1" } "ocaml-migrate-parsetree" { = "1.4.0" }
]
pin-depends: [
["bisect_ppx.git" "git+https://github.com/aantron/bisect_ppx.git#02dfb10188033a26d07d23480c2bc44a3a670357"]
] ]
build: [ build: [
[ "dune" "build" "-p" name "-j" jobs ] [ "dune" "build" "-p" name "-j" jobs ]

View File

@ -7,7 +7,7 @@
) )
(modules cli cli_helpers version) (modules cli cli_helpers version)
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -open Simple_utils)) (flags (:standard -open Simple_utils))
) )
@ -31,7 +31,7 @@
(modules runligo) (modules runligo)
(package ligo) (package ligo)
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -open Simple_utils)) (flags (:standard -open Simple_utils))
) )

View File

@ -1,6 +1,6 @@
(library (library
(name cli_expect_tests) (name cli_expect_tests)
(libraries simple-utils cli) (libraries simple-utils cli)
(inline_tests) (inline_tests (deps (source_tree ../../test/contracts)))
(preprocess (pps ppx_let ppx_expect)) (preprocess (pps ppx_let ppx_expect))
(flags (:standard -open Simple_utils))) (flags (:standard -open Simple_utils)))

View File

@ -9,6 +9,6 @@
main main
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
) )

View File

@ -18,7 +18,7 @@
self_michelson self_michelson
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils -open Tezos_utils )) (flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils -open Tezos_utils ))
) )

View File

@ -68,15 +68,6 @@ let parsify_reasonligo = fun source ->
Simplify.Cameligo.simpl_program raw in Simplify.Cameligo.simpl_program raw in
ok simplified ok simplified
let parsify_string_reasonligo = fun source ->
let%bind raw =
trace (simple_error "parsing") @@
Parser.Reasonligo.parse_string source in
let%bind simplified =
trace (simple_error "simplifying") @@
Simplify.Cameligo.simpl_program raw in
ok simplified
let parsify_expression_reasonligo = fun source -> let parsify_expression_reasonligo = fun source ->
let%bind raw = let%bind raw =
trace (simple_error "parsing expression") @@ trace (simple_error "parsing expression") @@
@ -105,3 +96,40 @@ let parsify_expression = fun syntax source ->
let%bind parsified = parsify source in let%bind parsified = parsify source in
let%bind applied = Self_ast_simplified.all_expression parsified in let%bind applied = Self_ast_simplified.all_expression parsified in
ok applied ok applied
let parsify_string_reasonligo = fun source ->
let%bind raw =
trace (simple_error "parsing") @@
Parser.Reasonligo.parse_string source in
let%bind simplified =
trace (simple_error "simplifying") @@
Simplify.Cameligo.simpl_program raw in
ok simplified
let parsify_string_pascaligo = fun source ->
let%bind raw =
trace (simple_error "parsing") @@
Parser.Pascaligo.parse_string source in
let%bind simplified =
trace (simple_error "simplifying") @@
Simplify.Pascaligo.simpl_program raw in
ok simplified
let parsify_string_cameligo = fun source ->
let%bind raw =
trace (simple_error "parsing") @@
Parser.Cameligo.parse_string source in
let%bind simplified =
trace (simple_error "simplifying") @@
Simplify.Cameligo.simpl_program raw in
ok simplified
let parsify_string = fun (syntax : v_syntax) source_filename ->
let%bind parsify = match syntax with
| Pascaligo -> ok parsify_string_pascaligo
| Cameligo -> ok parsify_string_cameligo
| ReasonLIGO -> ok parsify_string_reasonligo
in
let%bind parsified = parsify source_filename in
let%bind applied = Self_ast_simplified.all_program parsified in
ok applied

View File

@ -6,6 +6,10 @@ let compile (source_filename:string) syntax : Ast_simplified.program result =
let%bind simplified = parsify syntax source_filename in let%bind simplified = parsify syntax source_filename in
ok simplified ok simplified
let compile_string (source:string) syntax : Ast_simplified.program result =
let%bind simplified = parsify_string syntax source in
ok simplified
let compile_expression : v_syntax -> string -> Ast_simplified.expression result = let compile_expression : v_syntax -> string -> Ast_simplified.expression result =
fun syntax exp -> fun syntax exp ->
parsify_expression syntax exp parsify_expression syntax exp

View File

@ -7,7 +7,7 @@
uncompile uncompile
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils -open Tezos_utils )) (flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils -open Tezos_utils ))
) )

View File

@ -17,7 +17,7 @@
compile compile
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils -open Tezos_utils )) (flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils -open Tezos_utils ))
) )

View File

@ -11,7 +11,7 @@
transpiler transpiler
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils -open Tezos_utils )) (flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils -open Tezos_utils ))
) )

View File

@ -1,4 +1,5 @@
[@@@warning "-42"] [@@@warning "-42"]
[@@@coverage exclude_file]
open AST open AST
open! Region open! Region

View File

@ -15,8 +15,11 @@
str str
simple-utils simple-utils
tezos-utils tezos-utils
getopt) getopt )
(flags (:standard -open Simple_utils -open Parser_shared ))) (preprocess
(pps bisect_ppx --conditional) )
(flags (:standard -open Simple_utils -open Parser_shared ))
)
(executable (executable
(name LexerMain) (name LexerMain)
@ -24,6 +27,8 @@
parser_cameligo) parser_cameligo)
(modules (modules
LexerMain) LexerMain)
(preprocess
(pps bisect_ppx --conditional))
(flags (:standard -open Parser_shared -open Parser_cameligo))) (flags (:standard -open Parser_shared -open Parser_cameligo)))
(executable (executable
@ -33,4 +38,6 @@
(modules (modules
ParserAPI ParserAPI
ParserMain) ParserMain)
(preprocess
(pps bisect_ppx --conditional))
(flags (:standard -open Simple_utils -open Parser_shared -open Parser_cameligo))) (flags (:standard -open Simple_utils -open Parser_shared -open Parser_cameligo)))

View File

@ -10,7 +10,7 @@
parser_reasonligo parser_reasonligo
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils -open Parser_shared)) (flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils -open Parser_shared))
) )

View File

@ -1,4 +1,5 @@
[@@@warning "-42"] [@@@warning "-42"]
[@@@coverage exclude_file]
open AST open AST
open! Region open! Region

View File

@ -14,8 +14,12 @@
parser_shared parser_shared
hex hex
simple-utils simple-utils
tezos-utils) tezos-utils
(flags (:standard -open Parser_shared -open Simple_utils))) )
(preprocess
(pps bisect_ppx --conditional))
(flags (:standard -open Parser_shared -open Simple_utils))
)
(executable (executable
(name LexerMain) (name LexerMain)
@ -26,17 +30,24 @@
parser_pascaligo) parser_pascaligo)
(modules (modules
LexerMain) LexerMain)
(flags (:standard -open Parser_shared -open Parser_pascaligo))) (preprocess
(pps bisect_ppx --conditional))
(flags (:standard -open Parser_shared -open Parser_pascaligo))
)
(executable (executable
(name ParserMain) (name ParserMain)
(libraries (libraries
parser_pascaligo) parser_pascaligo)
(modules ParserMain) (modules ParserMain)
(preprocess
(pps bisect_ppx --conditional))
(flags (:standard -open Simple_utils -open Parser_shared -open Parser_pascaligo))) (flags (:standard -open Simple_utils -open Parser_shared -open Parser_pascaligo)))
(executable (executable
(name Unlexer) (name Unlexer)
(preprocess
(pps bisect_ppx --conditional))
(modules Unlexer)) (modules Unlexer))
;; Les deux directives (rule) qui suivent sont pour le dev local. ;; Les deux directives (rule) qui suivent sont pour le dev local.

View File

@ -16,8 +16,13 @@
str str
simple-utils simple-utils
tezos-utils tezos-utils
getopt) getopt
(flags (:standard -open Simple_utils -open Parser_shared -open Parser_cameligo))) )
(preprocess
(pps bisect_ppx --conditional)
)
(flags (:standard -open Simple_utils -open Parser_shared -open Parser_cameligo ))
)
(executable (executable
(name LexerMain) (name LexerMain)
@ -25,7 +30,11 @@
parser_reasonligo) parser_reasonligo)
(modules (modules
LexerMain) LexerMain)
(flags (:standard -open Parser_shared -open Parser_reasonligo))) (preprocess
(pps bisect_ppx --conditional)
)
(flags (:standard -open Parser_shared -open Parser_reasonligo))
)
(executable (executable
(name ParserMain) (name ParserMain)
@ -35,4 +44,7 @@
(modules (modules
ParserAPI ParserAPI
ParserMain) ParserMain)
(preprocess
(pps bisect_ppx --conditional)
)
(flags (:standard -open Simple_utils -open Parser_cameligo -open Parser_shared -open Parser_reasonligo))) (flags (:standard -open Simple_utils -open Parser_cameligo -open Parser_shared -open Parser_reasonligo)))

View File

@ -7,7 +7,11 @@
simple-utils simple-utils
uutf uutf
getopt getopt
zarith) zarith
)
(preprocess
(pps bisect_ppx --conditional)
)
(modules (modules
Lexer Lexer
LexerLog LexerLog

View File

@ -12,6 +12,7 @@
(preprocess (preprocess
(pps (pps
ppx_let ppx_let
bisect_ppx --conditional
) )
) )
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils )) (flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils ))

View File

@ -6,7 +6,7 @@
ast_simplified ast_simplified
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils )) (flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils ))
) )

View File

@ -11,7 +11,7 @@
operators operators
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils )) (flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils ))
) )

View File

@ -10,7 +10,7 @@
operators operators
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils )) (flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils ))
) )

View File

@ -6,7 +6,7 @@
) )
(inline_tests) (inline_tests)
(preprocess (preprocess
(pps ppx_let ppx_expect) (pps ppx_let ppx_expect bisect_ppx --conditional)
) )
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils )) (flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils ))
) )

View File

@ -9,7 +9,7 @@
operators operators
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils -open Tezos_utils )) (flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils -open Tezos_utils ))
) )

View File

@ -6,7 +6,7 @@
tezos-utils tezos-utils
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils )) (flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils ))
) )

View File

@ -9,7 +9,7 @@
mini_c mini_c
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -open Simple_utils )) (flags (:standard -open Simple_utils ))
) )

View File

@ -243,6 +243,7 @@ module Simplify = struct
| "AND" -> ok C_AND | "AND" -> ok C_AND
| "OR" -> ok C_OR | "OR" -> ok C_OR
| "GT" -> ok C_GT | "GT" -> ok C_GT
| "GE" -> ok C_GE
| "LT" -> ok C_LT | "LT" -> ok C_LT
| "LE" -> ok C_LE | "LE" -> ok C_LE
| "CONS" -> ok C_CONS | "CONS" -> ok C_CONS

View File

@ -1,3 +1,4 @@
[@@@coverage exclude_file]
open Types open Types
open PP_helpers open PP_helpers
open Format open Format

View File

@ -7,7 +7,7 @@
stage_common stage_common
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -open Simple_utils )) (flags (:standard -open Simple_utils ))
) )

View File

@ -1,3 +1,4 @@
[@@@coverage exclude_file]
open Types open Types
open Format open Format
open PP_helpers open PP_helpers

View File

@ -8,7 +8,7 @@
stage_common stage_common
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -open Simple_utils)) (flags (:standard -open Simple_utils))
) )

View File

@ -7,7 +7,7 @@
) )
(inline_tests) (inline_tests)
(preprocess (preprocess
(pps ppx_let ppx_expect) (pps ppx_let ppx_expect bisect_ppx --conditional)
) )
(flags (:standard -open Simple_utils)) (flags (:standard -open Simple_utils))
) )

View File

@ -1,3 +1,4 @@
[@@@coverage exclude_file]
open Simple_utils.PP_helpers open Simple_utils.PP_helpers
open Types open Types
open Format open Format

View File

@ -7,6 +7,6 @@
stage_common stage_common
) )
(inline_tests) (inline_tests)
(preprocess (pps ppx_expect ppx_let)) (preprocess (pps ppx_expect ppx_let bisect_ppx --conditional))
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils )) (flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils ))
) )

View File

@ -8,7 +8,7 @@
mini_c mini_c
) )
(preprocess (preprocess
(pps ppx_let) (pps ppx_let bisect_ppx --conditional)
) )
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils )) (flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils ))
) )

8
src/test/doc_test.ml Normal file
View File

@ -0,0 +1,8 @@
open Test_helpers
let () =
Printexc.record_backtrace true ;
run_test @@ test_suite "LIGO" [
Md_file_tests.main ;
] ;
()

View File

@ -1,5 +1,7 @@
(ocamllex md)
(executables (executables
(names test manual_test) (names test manual_test doc_test)
(libraries (libraries
simple-utils simple-utils
ligo ligo
@ -12,6 +14,12 @@
(flags (:standard -w +1..62-4-9-44-40-42-48@39@33 -open Simple_utils )) (flags (:standard -w +1..62-4-9-44-40-42-48@39@33 -open Simple_utils ))
) )
(alias
(name doc-test)
(action (run ./doc_test.exe))
(deps (source_tree ../../gitlab-pages/docs))
)
(alias (alias
(name ligo-test) (name ligo-test)
(action (run ./test.exe)) (action (run ./test.exe))
@ -21,7 +29,7 @@
(alias (alias
(name runtest) (name runtest)
(deps (alias ligo-test)) (deps (alias ligo-test) (alias doc-test))
) )
(alias (alias

62
src/test/md.mll Normal file
View File

@ -0,0 +1,62 @@
{
(* initial version taken from https://github.com/realworldocaml/mdx *)
type arg =
| Field of string
| NameValue of string * string
type block = {
line : int;
file : string;
arguments: arg list;
header : string option;
contents: string list;
}
exception Err of string
let line_ref = ref 1
let newline lexbuf =
Lexing.new_line lexbuf;
incr line_ref
}
let eol = '\n' | eof
let ws = ' ' | '\t'
rule text = parse
| eof { [] }
| "```" ([^' ' '\n']* as h) ws* ([^'\n']* as l) eol
{
let header = if h = "" then None else Some h in
let contents = block lexbuf in
let arguments = String.split_on_char ' ' l in
let arguments = List.map (fun a ->
if (String.contains a '=') then
( let a = String.split_on_char '=' a in
NameValue (List.nth a 0, List.nth a 1))
else
Field a
) arguments in
let file = lexbuf.Lexing.lex_start_p.Lexing.pos_fname in
newline lexbuf;
let line = !line_ref in
List.iter (fun _ -> newline lexbuf) contents;
newline lexbuf;
{ file; line; header; arguments; contents; }
:: text lexbuf }
| [^'\n']* eol
{ newline lexbuf;
text lexbuf }
and block = parse
| eof | "```" ws* eol { [] }
| ([^'\n'] * as str) eol { str :: block lexbuf }
{
let token lexbuf =
try
text lexbuf
with Failure _ ->
raise (Err "incomplete code block")
}

120
src/test/md_file_tests.ml Normal file
View File

@ -0,0 +1,120 @@
open Trace
open Test_helpers
module SnippetsGroup = Map.Make(struct type t = (string * string) let compare a b = compare a b end)
let failed_to_compile_md_file md_file (s,group,prg) =
let title () = "Failed to compile ```"^s^" block (group '"^group^"') in file '"^md_file^"'" in
let content () = "\n"^prg in
error title content
(**
binds the snippets by (syntax, group_name)
e.g. :(pascaligo, a) -> "let .. in let .. in"
(cameligo, a) -> "let .. in let .. in"
syntax and group_name being retrieved from the .md file header & arguments
e.g. : ```syntax group=group_name ...some code ... ```
**)
let get_groups md_file =
let channel = open_in md_file in
let lexbuf = Lexing.from_channel channel in
let code_blocks = Md.token lexbuf in
List.fold_left
(fun (grp_map: _ SnippetsGroup.t) (el:Md.block) ->
match el.header with
| Some s when (String.equal s "pascaligo") || (String.equal s "cameligo") || (String.equal s "reasonligo") -> (
match el.arguments with
| [Md.Field ""] -> SnippetsGroup.update (s,"ungrouped")
(fun arg_content ->
match arg_content with
| Some ct -> Some (String.concat "\n" (ct::el.contents))
| None -> Some (String.concat "\n" el.contents)
)
grp_map
| [Md.Field "skip"] -> grp_map
| _ ->
List.fold_left
(fun grp_map arg -> match arg with
| Md.NameValue ("group", name) ->
SnippetsGroup.update (s,name)
(fun arg_content ->
match arg_content with
| Some ct -> Some (String.concat "\n" (ct::el.contents))
| None -> Some (String.concat "\n" el.contents)
)
grp_map
| _ -> grp_map
)
grp_map el.arguments )
| None | Some _ -> grp_map
)
SnippetsGroup.empty code_blocks
(**
evaluate each expression in each programs from the snippets group map
**)
let compile_groups _filename grp_list =
let%bind (_michelsons : Compiler.compiled_expression list list) = bind_map_list
(fun ((s,grp),contents) ->
trace (failed_to_compile_md_file _filename (s,grp,contents)) @@
let%bind v_syntax = Compile.Helpers.syntax_to_variant (Syntax_name s) None in
let%bind simplified = Compile.Of_source.compile_string contents v_syntax in
let%bind typed,_ = Compile.Of_simplified.compile simplified in
let%bind mini_c = Compile.Of_typed.compile typed in
bind_map_list
(fun ((_,exp),_) -> Compile.Of_mini_c.aggregate_and_compile_expression mini_c exp)
mini_c
)
grp_list in
ok ()
let compile filename () =
let groups = SnippetsGroup.bindings @@ get_groups filename in
let%bind () = compile_groups filename groups in
ok ()
(*
find ./gitlab-pages/ -iname "*.md"
*)
let md_files = [
"/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.md";
"/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.md";
"/gitlab-pages/docs/intro/installation.md";
"/gitlab-pages/docs/intro/editor-support.md";
"/gitlab-pages/docs/intro/what-and-why.md";
"/gitlab-pages/docs/language-basics/math-numbers-tez.md";
"/gitlab-pages/docs/language-basics/functions.md";
"/gitlab-pages/docs/language-basics/boolean-if-else.md";
"/gitlab-pages/docs/language-basics/types.md";
"/gitlab-pages/docs/language-basics/strings.md";
"/gitlab-pages/docs/language-basics/maps-records.md";
"/gitlab-pages/docs/language-basics/variables-and-constants.md";
"/gitlab-pages/docs/language-basics/sets-lists-touples.md";
"/gitlab-pages/docs/language-basics/operators.md";
"/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md";
"/gitlab-pages/docs/contributors/big-picture/back-end.md";
"/gitlab-pages/docs/contributors/big-picture/vendors.md";
"/gitlab-pages/docs/contributors/big-picture/front-end.md";
"/gitlab-pages/docs/contributors/big-picture/overview.md";
"/gitlab-pages/docs/contributors/big-picture/middle-end.md";
"/gitlab-pages/docs/contributors/documentation-and-releases.md";
"/gitlab-pages/docs/contributors/getting-started.md";
"/gitlab-pages/docs/contributors/philosophy.md";
"/gitlab-pages/docs/contributors/ligo_test_guide.md";
"/gitlab-pages/docs/contributors/road-map/short-term.md";
"/gitlab-pages/docs/contributors/road-map/long-term.md";
"/gitlab-pages/docs/contributors/origin.md";
"/gitlab-pages/docs/advanced/first-contract.md";
"/gitlab-pages/docs/advanced/entrypoints-contracts.md";
"/gitlab-pages/docs/advanced/timestamps-addresses.md";
"/gitlab-pages/docs/api/cli-commands.md";
"/gitlab-pages/docs/api/cheat-sheet.md";
]
let md_root = "../../gitlab-pages/docs/language-basics/"
let main = test_suite "Markdown files"
(List.map (fun md_file ->
let test_name = "File : \"."^md_file^"\"" in
let md_path = "../.."^md_file in
test test_name (compile md_path))
md_files)