Merge branch 'docs/loop-doc' of ssh://gitlab.com/ligolang/ligo into docs/loop-doc
This commit is contained in:
commit
2f25c4b683
3
.gitignore
vendored
3
.gitignore
vendored
@ -8,3 +8,6 @@ Version.ml
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
/ligo.install
|
||||
*.coverage
|
||||
/_coverage/
|
||||
/_coverage_*/
|
||||
|
@ -85,6 +85,10 @@ local-dune-job:
|
||||
- scripts/install_vendors_deps.sh
|
||||
- scripts/build_ligo_local.sh
|
||||
- dune runtest
|
||||
- make coverage
|
||||
artifacts:
|
||||
paths:
|
||||
- _coverage_all
|
||||
|
||||
# Run a docker build without publishing to the registry
|
||||
build-current-docker-image:
|
||||
|
24
Makefile
24
Makefile
@ -30,3 +30,27 @@ test: build
|
||||
export PATH="/usr/local/bin$${PATH:+:}$${PATH:-}"
|
||||
eval $$(opam config env)
|
||||
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
|
||||
|
@ -20,7 +20,7 @@ This means that every smart contract needs at least one entrypoint function, her
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```
|
||||
```pascaligo group=a
|
||||
type parameter is unit;
|
||||
type store is unit;
|
||||
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-->
|
||||
<!--Pascaligo-->
|
||||
```
|
||||
```pascaligo group=b
|
||||
function main (const p : unit ; const s : unit) : (list(operation) * unit) is
|
||||
block {
|
||||
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-->
|
||||
<!--Pascaligo-->
|
||||
```
|
||||
```pascaligo group=c
|
||||
const owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
||||
function main (const p : unit ; const s : unit) : (list(operation) * unit) is
|
||||
block {
|
||||
@ -79,7 +79,7 @@ In our case, we have a `counter.ligo` contract that accepts a parameter of type
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```
|
||||
```pascaligo group=dup
|
||||
// counter.types.ligo
|
||||
type action is
|
||||
| Increment of int
|
||||
@ -87,7 +87,7 @@ type action is
|
||||
| Reset of unit
|
||||
```
|
||||
|
||||
```
|
||||
```pascaligo group=d
|
||||
// counter.ligo
|
||||
type action is
|
||||
| Increment of int
|
||||
@ -95,7 +95,7 @@ type action is
|
||||
| Reset of unit
|
||||
```
|
||||
|
||||
```
|
||||
```pascaligo gorup=d
|
||||
// proxy.ligo
|
||||
#include "counter.types.ligo"
|
||||
|
||||
|
71
gitlab-pages/docs/advanced/include.md
Normal file
71
gitlab-pages/docs/advanced/include.md
Normal file
@ -0,0 +1,71 @@
|
||||
---
|
||||
id: include
|
||||
title: Including Other Contracts
|
||||
---
|
||||
|
||||
Lets say we have a contract that's getting a bit too big. If it has a modular
|
||||
structure, you might find it useful to use the `#include` statement to split the
|
||||
contract up over multiple files.
|
||||
|
||||
|
||||
You take the code that you want to include and put it in a separate file, for
|
||||
example `included.ligo`:
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
|
||||
<!--PascaLIGO-->
|
||||
```pascaligo
|
||||
|
||||
// Demonstrate PascaLIGO inclusion statements, see includer.ligo
|
||||
|
||||
const foo : int = 144
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
|
||||
// Demonstrate CameLIGO inclusion statements, see includer.mligo
|
||||
|
||||
let foo : int = 144
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
|
||||
// Demonstrate ReasonLIGO inclusion statements, see includer.religo
|
||||
|
||||
let foo : int = 144;
|
||||
```
|
||||
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
|
||||
|
||||
And then you can include this code using the `#include` statement like so:
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
|
||||
<!--PascaLIGO-->
|
||||
```pascaligo
|
||||
|
||||
#include "included.ligo"
|
||||
|
||||
const bar : int = foo
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
|
||||
#include "included.mligo"
|
||||
|
||||
let bar : int = foo
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
|
||||
#include "included.religo"
|
||||
|
||||
let bar : int = foo;
|
||||
```
|
||||
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
@ -14,7 +14,7 @@ You can obtain the current time using the built-in syntax specific expression, p
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=a
|
||||
const today: timestamp = now;
|
||||
```
|
||||
<!--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
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=b
|
||||
const today: timestamp = now;
|
||||
const one_day: int = 86400;
|
||||
const in_24_hrs: timestamp = today + one_day;
|
||||
@ -36,10 +36,10 @@ const in_24_hrs: timestamp = today + one_day;
|
||||
#### 24 hours ago
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=c
|
||||
const today: timestamp = now;
|
||||
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-->
|
||||
|
||||
@ -49,7 +49,7 @@ You can also compare timestamps using the same comparison operators as for numbe
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=c
|
||||
const not_tommorow: bool = (now = in_24_hrs)
|
||||
```
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
@ -62,7 +62,7 @@ Here's how you can define an address:
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=d
|
||||
const my_account: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
||||
```
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
|
@ -11,18 +11,18 @@ Here's how to define a boolean:
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=a
|
||||
const a: bool = True;
|
||||
const b: bool = False;
|
||||
```
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=a
|
||||
let a: bool = true
|
||||
let b: bool = false
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=a
|
||||
let a: bool = true;
|
||||
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-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=b
|
||||
const a: string = "Alice";
|
||||
const b: string = "Alice";
|
||||
// True
|
||||
const c: bool = (a = b);
|
||||
```
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=b
|
||||
let a: string = "Alice"
|
||||
let b: string = "Alice"
|
||||
// true
|
||||
let c: bool = (a = b)
|
||||
```
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=b
|
||||
let a: string = "Alice";
|
||||
let b: string = "Alice";
|
||||
/* true */
|
||||
(* true *)
|
||||
let c: bool = (a == b);
|
||||
```
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
@ -64,7 +64,7 @@ let c: bool = (a == b);
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=c
|
||||
const a: int = 5;
|
||||
const b: int = 4;
|
||||
const c: bool = (a = b);
|
||||
@ -75,7 +75,7 @@ const g: bool = (a >= b);
|
||||
const h: bool = (a =/= b);
|
||||
```
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=c
|
||||
let a: int = 5
|
||||
let b: int = 4
|
||||
let c: bool = (a = b)
|
||||
@ -83,11 +83,11 @@ let d: bool = (a > b)
|
||||
let e: bool = (a < b)
|
||||
let f: bool = (a <= b)
|
||||
let g: bool = (a >= b)
|
||||
let h: bool = (a =/= b)
|
||||
let h: bool = (a <> b)
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=c
|
||||
let a: int = 5;
|
||||
let b: int = 4;
|
||||
let c: bool = (a == b);
|
||||
@ -106,23 +106,23 @@ let h: bool = (a != b);
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=d
|
||||
const a: tez = 5mutez;
|
||||
const b: tez = 10mutez;
|
||||
const c: bool = (a = b);
|
||||
```
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=d
|
||||
let a: tez = 5mutez
|
||||
let b: tez = 10mutez
|
||||
// false
|
||||
let c: bool = (a = b)
|
||||
```
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=d
|
||||
let a: tez = 5mutez;
|
||||
let b: tez = 10mutez;
|
||||
/* false */
|
||||
(* false *)
|
||||
let c: bool = (a == b);
|
||||
```
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
@ -136,7 +136,7 @@ Conditional logic is an important part of every real world program.
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=e
|
||||
const min_age: nat = 16n;
|
||||
|
||||
(*
|
||||
@ -162,7 +162,7 @@ function is_adult(const age: nat): bool is
|
||||
> ```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=e
|
||||
let min_age: nat = 16n
|
||||
|
||||
(**
|
||||
@ -177,17 +177,17 @@ let is_adult (age: nat) : bool =
|
||||
if (age > min_age) then true else false
|
||||
```
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=e
|
||||
let min_age: nat = 16n;
|
||||
|
||||
/**
|
||||
(**
|
||||
|
||||
This function is really obnoxious, but it showcases
|
||||
how the if statement and it's syntax can be used.
|
||||
|
||||
Normally, you'd use `with (age > min_age)` instead.
|
||||
|
||||
*/
|
||||
*)
|
||||
|
||||
let is_adult = (age: nat): bool =>
|
||||
if (age > min_age) {
|
||||
|
@ -13,7 +13,7 @@ Each `block` needs to include at least one `instruction`, or a *placeholder* ins
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
|
||||
```pascaligo
|
||||
```pascaligo skip
|
||||
// shorthand syntax
|
||||
block { skip }
|
||||
// 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`:
|
||||
|
||||
|
||||
```pascaligo
|
||||
```pascaligo group=a
|
||||
function add(const a: int; const b: int): int is
|
||||
begin
|
||||
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`.
|
||||
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
|
||||
```
|
||||
|
||||
@ -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:
|
||||
|
||||
```cameligo
|
||||
```cameligo group=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:
|
||||
|
||||
```reasonligo
|
||||
```reasonligo group=b
|
||||
let add = (a: int, b: int) : int => a + b;
|
||||
```
|
||||
|
||||
@ -90,7 +90,7 @@ value.
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=b
|
||||
const increment : (int -> int) = (function (const i : int) : int is i + 1);
|
||||
// a = 2
|
||||
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.
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=c
|
||||
const increment : (int -> int) = (function (const i : int) : int is i + 1);
|
||||
// a = 2
|
||||
const a: int = increment(1);
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=c
|
||||
let increment : (int -> int) = fun (i: int) -> i + 1
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=c
|
||||
let increment: (int => int) = (i: int) => i + 1;
|
||||
```
|
||||
|
||||
|
@ -36,14 +36,36 @@ that does the fold returns either boolean true or boolean false to indicate
|
||||
whether the fold should continue or not. The initial value must match the input
|
||||
parameter of the auxillary function, and the auxillary should return type `(bool * input)`.
|
||||
|
||||
`continue` and `stop` are provided as syntactic sugar for the return values.
|
||||
|
||||
```cameligo
|
||||
let aux (i: int) : bool * int =
|
||||
if i < 100 then continue (i + 1) else stop i
|
||||
|
||||
let counter_simple (n: int) : int =
|
||||
Loop.fold_while aux_simple n
|
||||
Loop.fold_while aux n
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
|
||||
`Loop.fold_while` is a fold operation that takes an initial value of a certain type
|
||||
and then iterates on it until a condition is reached. The auxillary function
|
||||
that does the fold returns either boolean true or boolean false to indicate
|
||||
whether the fold should continue or not. The initial value must match the input
|
||||
parameter of the auxillary function, and the auxillary should return type `(bool, input)`.
|
||||
|
||||
`continue` and `stop` are provided as syntactic sugar for the return values.
|
||||
|
||||
```reasonligo
|
||||
let aux = (i: int): (bool, int) =>
|
||||
if (i < 100) {
|
||||
continue(i + 1);
|
||||
} else {
|
||||
stop(i);
|
||||
};
|
||||
|
||||
let counter_simple = (n: int): int => Loop.fold_while(aux, n);```
|
||||
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
|
||||
## For Loop
|
||||
|
@ -135,21 +135,21 @@ otherwise.
|
||||
function iter_op (const m : ledger) : 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) ;
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
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
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
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);
|
||||
};
|
||||
```
|
||||
@ -162,21 +162,21 @@ let iter_op = (m: ledger): unit => {
|
||||
```pascaligo
|
||||
function map_op (const m : ledger) : ledger is
|
||||
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) ;
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
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
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
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);
|
||||
};
|
||||
```
|
||||
@ -196,22 +196,22 @@ It eventually returns the result of combining all the elements.
|
||||
```pascaligo
|
||||
function fold_op (const m : ledger) : tez is
|
||||
block {
|
||||
function aggregate (const i : address ; const j : (tez * tez)) : tez is block { skip } with j.0 + j.1 ;
|
||||
} with map_fold(aggregate, m , 10)
|
||||
function aggregate (const j : tez ; const cur : (address * tez)) : tez is j + cur.1 ;
|
||||
} with map_fold(aggregate, m , 10mutez)
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
let fold_op (m : ledger) : ledger =
|
||||
let aggregate = fun (ignore: address) (j: tez * tez) -> j.0 + j.1
|
||||
in Map.fold aggregate m 10
|
||||
let aggregate = fun (j: tez) (cur: address * tez) -> j + cur.1 in
|
||||
Map.fold aggregate m 10tz
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
let fold_op = (m: ledger): ledger => {
|
||||
let aggregate = (ignore: address, j: (tez, tez)) => j[0] + j[1];
|
||||
Map.fold(aggregate, m, 10);
|
||||
let aggregate = (j: tez, cur: (address, tez)) => j + cur[1];
|
||||
Map.fold(aggregate, m, 10tz);
|
||||
};
|
||||
|
||||
```
|
||||
|
@ -14,7 +14,7 @@ In the following example you can find a series of arithmetic operations, includi
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
|
||||
```pascaligo
|
||||
```pascaligo group=a
|
||||
// int + int produces int
|
||||
const a: int = 5 + 10;
|
||||
// nat + int produces int
|
||||
@ -38,7 +38,7 @@ const g: int = 1_000_000;
|
||||
|
||||
<!--CameLIGO-->
|
||||
|
||||
```cameligo
|
||||
```cameligo group=a
|
||||
// int + int produces int
|
||||
let a: int = 5 + 10
|
||||
// nat + int produces int
|
||||
@ -62,19 +62,19 @@ let g: int = 1_000_000
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
|
||||
```reasonligo
|
||||
/* int + int produces int */
|
||||
```reasonligo group=a
|
||||
(* int + int produces int *)
|
||||
let a: int = 5 + 10;
|
||||
/* nat + int produces int */
|
||||
(* nat + int produces int *)
|
||||
let b: int = 5n + 10;
|
||||
/* tez + tez produces tez */
|
||||
(* tez + tez produces tez *)
|
||||
let c: tez = 5mutez + 10mutez;
|
||||
/* you can't add tez + int or tez + nat, this won't compile */
|
||||
/* let d: tez = 5mutez + 10n; */
|
||||
/* two nats produce a nat */
|
||||
(* you can't add tez + int or tez + nat, this won't compile:
|
||||
let d: tez = 5mutez + 10n; *)
|
||||
(* two nats produce a nat *)
|
||||
let e: nat = 5n + 10n;
|
||||
/* nat + int produces an int, this won't compile */
|
||||
/* let f: nat = 5n + 10; */
|
||||
(* nat + int produces an int, this won't compile:
|
||||
let f: nat = 5n + 10; *)
|
||||
let g: int = 1_000_000;
|
||||
```
|
||||
|
||||
@ -94,33 +94,33 @@ The simpliest substraction looks like this:
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=b
|
||||
const a: int = 5 - 10;
|
||||
// substraction of two nats, yields an int
|
||||
const b: int = 5n - 2n;
|
||||
// won't compile, result is an int, not a nat
|
||||
// const c: nat = 5n - 2n;
|
||||
const d: tez = 5mutez - 1mt;
|
||||
const d: tez = 5mutez - 1mutez;
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=b
|
||||
let a: int = 5 - 10
|
||||
// substraction of two nats, yields an int
|
||||
let b: int = 5n - 2n
|
||||
// won't compile, result is an int, not a nat
|
||||
// const c: nat = 5n - 2n
|
||||
let d: tez = 5mutez - 1mt
|
||||
let d: tez = 5mutez - 1mutez
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=b
|
||||
let a: int = 5 - 10;
|
||||
/* substraction of two nats, yields an int */
|
||||
(* substraction of two nats, yields an int *)
|
||||
let b: int = 5n - 2n;
|
||||
/* won't compile, result is an int, not a nat */
|
||||
/* let c: nat = 5n - 2n; */
|
||||
let d: tez = 5mutez - 1mt;
|
||||
(* won't compile, result is an int, not a nat *)
|
||||
(* let c: nat = 5n - 2n; *)
|
||||
let d: tez = 5mutez - 1mutez;
|
||||
```
|
||||
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
@ -133,7 +133,7 @@ You can multiply values of the same type, such as:
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
|
||||
```pascaligo
|
||||
```pascaligo group=c
|
||||
const a: int = 5 * 5;
|
||||
const b: nat = 5n * 5n;
|
||||
// you can also multiply `nat` and `tez`
|
||||
@ -141,7 +141,7 @@ const c: tez = 5n * 5mutez;
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=c
|
||||
let a: int = 5 * 5
|
||||
let b: nat = 5n * 5n
|
||||
// you can also multiply `nat` and `tez`
|
||||
@ -149,10 +149,10 @@ let c: tez = 5n * 5mutez
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=c
|
||||
let a: int = 5 * 5;
|
||||
let b: nat = 5n * 5n;
|
||||
/* you can also multiply `nat` and `tez` */
|
||||
(* you can also multiply `nat` and `tez` *)
|
||||
let c: tez = 5n * 5mutez;
|
||||
```
|
||||
|
||||
@ -167,21 +167,21 @@ In LIGO you can divide `int`, `nat`, and `tez`. Here's how:
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=d
|
||||
const a: int = 10 / 3;
|
||||
const b: nat = 10n / 3n;
|
||||
const c: nat = 10mutez / 3mutez;
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=d
|
||||
let a: int = 10 / 3
|
||||
let b: nat = 10n / 3n
|
||||
let c: nat = 10mutez / 3mutez
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=d
|
||||
let a: int = 10 / 3;
|
||||
let b: nat = 10n / 3n;
|
||||
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-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=e
|
||||
const a: int = int(1n);
|
||||
const b: nat = abs(1);
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=e
|
||||
let a: int = int(1n);
|
||||
let b: nat = abs(1);
|
||||
```
|
||||
|
@ -15,7 +15,7 @@ Sets are similar to lists. The main difference is that elements of a `set` must
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=a
|
||||
type int_set is set(int);
|
||||
const my_set: int_set = set
|
||||
1;
|
||||
@ -25,14 +25,14 @@ end
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=a
|
||||
type int_set = int set
|
||||
let my_set: int_set =
|
||||
Set.add 3 (Set.add 2 (Set.add 1 (Set.empty: int set)))
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=a
|
||||
type int_set = set(int);
|
||||
let my_set: int_set =
|
||||
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-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=a
|
||||
const my_set: int_set = set end;
|
||||
const my_set_2: int_set = set_empty;
|
||||
```
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=a
|
||||
let my_set: int_set = (Set.empty: int set)
|
||||
```
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=a
|
||||
let my_set: int_set = (Set.empty: set(int));
|
||||
```
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
@ -62,18 +62,18 @@ let my_set: int_set = (Set.empty: set(int));
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=a
|
||||
const contains_three: bool = my_set contains 3;
|
||||
// or alternatively
|
||||
const contains_three_fn: bool = set_mem(3, my_set);
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=a
|
||||
let contains_three: bool = Set.mem 3 my_set
|
||||
```
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=a
|
||||
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
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=a
|
||||
const set_size: nat = size(my_set);
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=a
|
||||
let set_size: nat = Set.size my_set
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=a
|
||||
let set_size: nat = Set.size(my_set);
|
||||
```
|
||||
|
||||
@ -103,21 +103,21 @@ let set_size: nat = Set.size(my_set);
|
||||
### Modifying a set
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=a
|
||||
const larger_set: int_set = set_add(4, my_set);
|
||||
const smaller_set: int_set = set_remove(3, my_set);
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
|
||||
```cameligo
|
||||
```cameligo group=a
|
||||
let larger_set: int_set = Set.add 4 my_set
|
||||
let smaller_set: int_set = Set.remove 3 my_set
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
|
||||
```reasonligo
|
||||
```reasonligo group=a
|
||||
let larger_set: int_set = Set.add(4, 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
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=a
|
||||
function sum(const result: int; const i: int): int is result + i;
|
||||
// Outputs 6
|
||||
const sum_of_a_set: int = set_fold(sum, my_set, 0);
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=a
|
||||
let sum (result: int) (i: int) : int = result + i
|
||||
let sum_of_a_set: int = Set.fold sum my_set 0
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=a
|
||||
let sum = (result: int, i: int): int => result + i;
|
||||
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-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=b
|
||||
type int_list is list(int);
|
||||
const my_list: int_list = list
|
||||
1;
|
||||
@ -167,13 +167,13 @@ end
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=b
|
||||
type int_list = int list
|
||||
let my_list: int_list = [1; 2; 3]
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=b
|
||||
type int_list = list(int);
|
||||
let my_list: int_list = [1, 2, 3];
|
||||
```
|
||||
@ -185,21 +185,21 @@ let my_list: int_list = [1, 2, 3];
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=b
|
||||
const larger_list: int_list = cons(4, my_list);
|
||||
const even_larger_list: int_list = 5 # larger_list;
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=b
|
||||
let larger_list: int_list = 4 :: my_list
|
||||
(* CameLIGO doesn't have a List.cons *)
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=b
|
||||
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-->
|
||||
@ -211,7 +211,7 @@ let larger_list: int_list = [4, ...my_list];
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=b
|
||||
function increment(const i: int): int is block { skip } with i + 1;
|
||||
// Creates a new list with elements incremented by 1
|
||||
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 group=b
|
||||
let increment (i: int) : int = i + 1
|
||||
(* Creates a new list with elements incremented by 1 *)
|
||||
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 group=b
|
||||
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);
|
||||
```
|
||||
|
||||
@ -240,7 +240,7 @@ let incremented_list: int_list = List.map(increment, larger_list);
|
||||
### Folding of a list:
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=b
|
||||
function sum(const result: int; const i: int): int is block { skip } with result + i;
|
||||
// Outputs 6
|
||||
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 group=b
|
||||
let sum (result: int) (i: int) : int = result + i
|
||||
// Outputs 6
|
||||
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 group=b
|
||||
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);
|
||||
```
|
||||
|
||||
@ -287,22 +287,22 @@ sake of illustration.
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=c
|
||||
type full_name is string * string;
|
||||
const full_name: full_name = ("Alice", "Johnson");
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=c
|
||||
type full_name = string * string
|
||||
(* The parenthesis here are optional *)
|
||||
let full_name: full_name = ("Alice", "Johnson")
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=c
|
||||
type full_name = (string, string);
|
||||
/* The parenthesis here are optional */
|
||||
(* The parenthesis here are optional *)
|
||||
let full_name: full_name = ("Alice", "Johnson");
|
||||
```
|
||||
|
||||
@ -320,17 +320,17 @@ Tuple elements are one-indexed and accessed like so:
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
```pascaligo group=c
|
||||
const first_name: string = full_name.1;
|
||||
```
|
||||
|
||||
<!--CameLIGO-->
|
||||
```cameligo
|
||||
```cameligo group=c
|
||||
let first_name: string = full_name.1
|
||||
```
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
```reasonligo group=c
|
||||
let first_name: string = full_name[1];
|
||||
```
|
||||
|
||||
|
@ -59,12 +59,12 @@ let ledger: account_balances = Map.literal
|
||||
|
||||
<!--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);
|
||||
|
||||
let ledger: account_balances =
|
||||
Map.literal([
|
||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, 10mutez),
|
||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, 10mutez)
|
||||
]);
|
||||
```
|
||||
|
||||
@ -124,25 +124,23 @@ let ledger: account_balances = Map.literal
|
||||
|
||||
<!--ReasonLIGO-->
|
||||
```reasonligo
|
||||
/* alias two types */
|
||||
(* alias two types *)
|
||||
type account = address;
|
||||
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 = {
|
||||
balance: tez,
|
||||
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);
|
||||
|
||||
/* pseudo-JSON representation of our map */
|
||||
/* {"tz1...": {balance: 10mutez, number_of_transactions: 5n}} */
|
||||
(* pseudo-JSON representation of our map
|
||||
{"tz1...": {balance: 10mutez, number_of_transactions: 5n}} *)
|
||||
let ledger: account_balances =
|
||||
Map.literal([
|
||||
(
|
||||
"tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address,
|
||||
{balance: 10mutez, number_of_transactions: 5n},
|
||||
),
|
||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address,
|
||||
{balance: 10mutez, number_of_transactions: 5n})
|
||||
]);
|
||||
|
||||
```
|
||||
|
@ -17,7 +17,7 @@ In the [previous tutorial](tutorials/get-started/tezos-taco-shop-smart-contract.
|
||||
## Analyzing the current contract
|
||||
|
||||
### **`taco-shop.ligo`**
|
||||
```pascaligo
|
||||
```pascaligo group=a
|
||||
type taco_supply is record
|
||||
current_stock : nat;
|
||||
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
|
||||
// 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
|
||||
// Decrease the stock by 1n, because we've just sold one
|
||||
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
|
||||
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;
|
||||
```
|
||||
|
||||
@ -67,8 +67,8 @@ This means that after all the *purchase conditions* of our contract are met - e.
|
||||
### 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.
|
||||
|
||||
```pascaligo
|
||||
const ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV";
|
||||
```pascaligo group=ex1
|
||||
const ownerAddress : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address);
|
||||
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.
|
||||
|
||||
|
||||
```pascaligo
|
||||
```pascaligo group=ex1
|
||||
const payoutOperation : operation = transaction(unit, amount, receiver) ;
|
||||
const operations : list(operation) = list
|
||||
payoutOperation
|
||||
@ -90,14 +90,14 @@ end;
|
||||
## Finalizing the contract
|
||||
|
||||
### **`taco-shop.ligo`**
|
||||
```pascaligo
|
||||
```pascaligo group=b
|
||||
type taco_supply is record
|
||||
current_stock : nat;
|
||||
max_price : tez;
|
||||
end
|
||||
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
|
||||
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
|
||||
// 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
|
||||
// Decrease the stock by 1n, because we've just sold one
|
||||
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.
|
||||
|
||||
```pascaligo
|
||||
```pascaligo skip
|
||||
ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 buy_taco 1n "map
|
||||
1n -> record
|
||||
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.
|
||||
|
||||
```pascaligo
|
||||
const ownerAddress: address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV";
|
||||
const donationAddress: address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx";
|
||||
```pascaligo group=bonus
|
||||
const ownerAddress: address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address);
|
||||
const donationAddress: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address);
|
||||
```
|
||||
|
||||
```pascaligo
|
||||
```pascaligo group=bonus
|
||||
const receiver : contract(unit) = get_contract(ownerAddress);
|
||||
const donationReceiver : contract(unit) = get_contract(donationAddress);
|
||||
|
||||
|
@ -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:
|
||||
|
||||
```pascaligo
|
||||
```pascaligo skip
|
||||
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.
|
||||
|
||||
### `taco-shop.ligo`
|
||||
```pascaligo
|
||||
```pascaligo group=a
|
||||
function main (const parameter: int; const contractStorage: int) : (list(operation) * int) is
|
||||
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.
|
||||
|
||||
**Taco shop's storage**
|
||||
```pascaligo
|
||||
```pascaligo group=b
|
||||
type taco_supply is record
|
||||
current_stock : nat;
|
||||
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.
|
||||
|
||||
**`taco-shop.ligo`**
|
||||
```pascaligo
|
||||
```pascaligo group=b+
|
||||
type taco_supply is record
|
||||
current_stock : nat;
|
||||
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
|
||||
|
||||
**`taco-shop.ligo`**
|
||||
```pascaligo
|
||||
```pascaligo group=c
|
||||
type taco_supply is record
|
||||
current_stock : nat;
|
||||
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`**
|
||||
|
||||
```pascaligo
|
||||
```pascaligo group=d
|
||||
type taco_supply is record
|
||||
current_stock : nat;
|
||||
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
|
||||
|
||||
**`taco-shop.ligo`**
|
||||
```pascaligo
|
||||
```pascaligo group=e
|
||||
type taco_supply is record
|
||||
current_stock : nat;
|
||||
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
|
||||
// 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
|
||||
// Decrease the stock by 1n, because we've just sold one
|
||||
taco_kind.current_stock := abs(taco_kind.current_stock - 1n);
|
||||
@ -324,14 +324,14 @@ end"
|
||||
|
||||
## 💰 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**
|
||||
```pascaligo
|
||||
```pascaligo skip
|
||||
if amount =/= current_purchase_price then
|
||||
```
|
||||
|
||||
**With tips**
|
||||
```pascaligo
|
||||
```pascaligo skip
|
||||
if amount >= current_purchase_price then
|
||||
```
|
||||
|
@ -16,6 +16,7 @@
|
||||
"Advanced": [
|
||||
"advanced/timestamps-addresses",
|
||||
"advanced/entrypoints-contracts",
|
||||
"advanced/include",
|
||||
"advanced/first-contract"
|
||||
],
|
||||
"API": [
|
||||
|
@ -11,6 +11,7 @@ depends: [
|
||||
"ocamlfind" { build }
|
||||
"dune" { build & = "1.11.4" }
|
||||
"menhir" { = "20190626" }
|
||||
"bisect_ppx" {dev & >= "2.0.0"}
|
||||
"ppx_let"
|
||||
"ppx_deriving"
|
||||
"ppx_expect"
|
||||
@ -20,7 +21,10 @@ depends: [
|
||||
"alcotest" { with-test }
|
||||
"getopt"
|
||||
# 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: [
|
||||
[ "dune" "build" "-p" name "-j" jobs ]
|
||||
|
@ -7,7 +7,7 @@
|
||||
)
|
||||
(modules cli cli_helpers version)
|
||||
(preprocess
|
||||
(pps ppx_let)
|
||||
(pps ppx_let bisect_ppx --conditional)
|
||||
)
|
||||
(flags (:standard -open Simple_utils))
|
||||
)
|
||||
@ -31,7 +31,7 @@
|
||||
(modules runligo)
|
||||
(package ligo)
|
||||
(preprocess
|
||||
(pps ppx_let)
|
||||
(pps ppx_let bisect_ppx --conditional)
|
||||
)
|
||||
(flags (:standard -open Simple_utils))
|
||||
)
|
||||
|
@ -927,4 +927,12 @@ let%expect_test _ =
|
||||
|
||||
let%expect_test _ =
|
||||
run_ligo_bad [ "compile-contract" ; contract "bad_type_operator.ligo" ; "main" ] ;
|
||||
[%expect {| ligo: bad type operator (TO_Map (unit,unit)): |}] ;
|
||||
[%expect {| ligo: bad type operator (TO_Map (unit,unit)): |}]
|
||||
|
||||
let%expect_test _ =
|
||||
run_ligo_bad [ "run-function" ; contract "failwith.ligo" ; "failer" ; "1" ] ;
|
||||
[%expect {| ligo: Execution failed: {"value":"some_string","type":"string"} |}]
|
||||
|
||||
let%expect_test _ =
|
||||
run_ligo_bad [ "compile-contract" ; contract "bad_address_format.religo" ; "main" ] ;
|
||||
[%expect {| ligo: in file "bad_address_format.religo", line 2, characters 25-47. Badly formatted address "KT1badaddr": {"location":"in file \"bad_address_format.religo\", line 2, characters 25-47"} |}]
|
||||
|
@ -1,6 +1,6 @@
|
||||
(library
|
||||
(name cli_expect_tests)
|
||||
(libraries simple-utils cli)
|
||||
(inline_tests)
|
||||
(inline_tests (deps (source_tree ../../test/contracts)))
|
||||
(preprocess (pps ppx_let ppx_expect))
|
||||
(flags (:standard -open Simple_utils)))
|
||||
|
2
src/dune
2
src/dune
@ -9,6 +9,6 @@
|
||||
main
|
||||
)
|
||||
(preprocess
|
||||
(pps ppx_let)
|
||||
(pps ppx_let bisect_ppx --conditional)
|
||||
)
|
||||
)
|
||||
|
@ -18,7 +18,7 @@
|
||||
self_michelson
|
||||
)
|
||||
(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 ))
|
||||
)
|
||||
|
@ -68,15 +68,6 @@ let parsify_reasonligo = fun source ->
|
||||
Simplify.Cameligo.simpl_program raw in
|
||||
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%bind raw =
|
||||
trace (simple_error "parsing expression") @@
|
||||
@ -105,3 +96,40 @@ let parsify_expression = fun syntax source ->
|
||||
let%bind parsified = parsify source in
|
||||
let%bind applied = Self_ast_simplified.all_expression parsified in
|
||||
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
|
||||
|
@ -6,6 +6,10 @@ let compile (source_filename:string) syntax : Ast_simplified.program result =
|
||||
let%bind simplified = parsify syntax source_filename in
|
||||
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 =
|
||||
fun syntax exp ->
|
||||
parsify_expression syntax exp
|
||||
|
@ -7,7 +7,7 @@
|
||||
uncompile
|
||||
)
|
||||
(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 ))
|
||||
)
|
||||
|
@ -17,7 +17,7 @@
|
||||
compile
|
||||
)
|
||||
(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 ))
|
||||
)
|
||||
|
@ -3,6 +3,21 @@ open Trace
|
||||
open Memory_proto_alpha.Protocol.Script_ir_translator
|
||||
open Memory_proto_alpha.X
|
||||
|
||||
module Errors = struct
|
||||
let unknown_failwith_type () =
|
||||
let title () = "Execution failed with an unknown failwith type" in
|
||||
let message () = "only bytes, string or int are printable" in
|
||||
error title message
|
||||
|
||||
let failwith data_str type_str () =
|
||||
let title () = "Execution failed" in
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("value" , fun () -> Format.asprintf "%s" data_str);
|
||||
("type" , fun () -> Format.asprintf "%s" type_str);
|
||||
] in
|
||||
error ~data title message
|
||||
end
|
||||
type options = Memory_proto_alpha.options
|
||||
|
||||
type run_res =
|
||||
@ -121,7 +136,12 @@ let run ?options (exp:Michelson.t) (exp_type:ex_ty) : ex_typed_value result =
|
||||
let%bind expr = run_expression ?options exp exp_type in
|
||||
match expr with
|
||||
| Success res -> ok res
|
||||
| _ -> simple_fail "Execution terminated with failwith"
|
||||
| Fail res -> ( match Tezos_micheline.Micheline.root @@ Memory_proto_alpha.strings_of_prims res with
|
||||
| Int (_ , i) -> fail @@ Errors.failwith (Z.to_string i) "int" ()
|
||||
| String (_ , s) -> fail @@ Errors.failwith s "string" ()
|
||||
| Bytes (_, s) -> fail @@ Errors.failwith (Bytes.to_string s) "bytes" ()
|
||||
| _ -> fail @@ Errors.unknown_failwith_type () )
|
||||
|
||||
|
||||
let run_failwith ?options (exp:Michelson.t) (exp_type:ex_ty) : run_failwith_res result =
|
||||
let%bind expr = run_expression ?options exp exp_type in
|
||||
@ -129,7 +149,7 @@ let run_failwith ?options (exp:Michelson.t) (exp_type:ex_ty) : run_failwith_res
|
||||
| Fail res -> ( match Tezos_micheline.Micheline.root @@ Memory_proto_alpha.strings_of_prims res with
|
||||
| Int (_ , i) -> ok (Failwith_int (Z.to_int i))
|
||||
| String (_ , s) -> ok (Failwith_string s)
|
||||
| Bytes (_,b) -> ok (Failwith_bytes b)
|
||||
| Bytes (_, b) -> ok (Failwith_bytes b)
|
||||
| _ -> simple_fail "Unknown failwith type" )
|
||||
| _ -> simple_fail "An error of execution was expected"
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
transpiler
|
||||
)
|
||||
(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 ))
|
||||
)
|
||||
|
@ -1,4 +1,5 @@
|
||||
[@@@warning "-42"]
|
||||
[@@@coverage exclude_file]
|
||||
|
||||
open AST
|
||||
open! Region
|
||||
|
@ -15,8 +15,11 @@
|
||||
str
|
||||
simple-utils
|
||||
tezos-utils
|
||||
getopt)
|
||||
(flags (:standard -open Simple_utils -open Parser_shared )))
|
||||
getopt )
|
||||
(preprocess
|
||||
(pps bisect_ppx --conditional) )
|
||||
(flags (:standard -open Simple_utils -open Parser_shared ))
|
||||
)
|
||||
|
||||
(executable
|
||||
(name LexerMain)
|
||||
@ -24,6 +27,8 @@
|
||||
parser_cameligo)
|
||||
(modules
|
||||
LexerMain)
|
||||
(preprocess
|
||||
(pps bisect_ppx --conditional))
|
||||
(flags (:standard -open Parser_shared -open Parser_cameligo)))
|
||||
|
||||
(executable
|
||||
@ -33,4 +38,6 @@
|
||||
(modules
|
||||
ParserAPI
|
||||
ParserMain)
|
||||
(preprocess
|
||||
(pps bisect_ppx --conditional))
|
||||
(flags (:standard -open Simple_utils -open Parser_shared -open Parser_cameligo)))
|
||||
|
@ -10,7 +10,7 @@
|
||||
parser_reasonligo
|
||||
)
|
||||
(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))
|
||||
)
|
||||
|
@ -1,4 +1,5 @@
|
||||
[@@@warning "-42"]
|
||||
[@@@coverage exclude_file]
|
||||
|
||||
open AST
|
||||
open! Region
|
||||
|
@ -10,12 +10,16 @@
|
||||
(public_name ligo.parser.pascaligo)
|
||||
(modules AST pascaligo Parser ParserLog LexToken)
|
||||
(libraries
|
||||
menhirLib
|
||||
parser_shared
|
||||
hex
|
||||
simple-utils
|
||||
tezos-utils)
|
||||
(flags (:standard -open Parser_shared -open Simple_utils)))
|
||||
menhirLib
|
||||
parser_shared
|
||||
hex
|
||||
simple-utils
|
||||
tezos-utils
|
||||
)
|
||||
(preprocess
|
||||
(pps bisect_ppx --conditional))
|
||||
(flags (:standard -open Parser_shared -open Simple_utils))
|
||||
)
|
||||
|
||||
(executable
|
||||
(name LexerMain)
|
||||
@ -25,18 +29,25 @@
|
||||
tezos-utils
|
||||
parser_pascaligo)
|
||||
(modules
|
||||
LexerMain)
|
||||
(flags (:standard -open Parser_shared -open Parser_pascaligo)))
|
||||
LexerMain)
|
||||
(preprocess
|
||||
(pps bisect_ppx --conditional))
|
||||
(flags (:standard -open Parser_shared -open Parser_pascaligo))
|
||||
)
|
||||
|
||||
(executable
|
||||
(name ParserMain)
|
||||
(libraries
|
||||
parser_pascaligo)
|
||||
(modules ParserMain)
|
||||
(preprocess
|
||||
(pps bisect_ppx --conditional))
|
||||
(flags (:standard -open Simple_utils -open Parser_shared -open Parser_pascaligo)))
|
||||
|
||||
(executable
|
||||
(name Unlexer)
|
||||
(preprocess
|
||||
(pps bisect_ppx --conditional))
|
||||
(modules Unlexer))
|
||||
|
||||
;; Les deux directives (rule) qui suivent sont pour le dev local.
|
||||
|
@ -16,16 +16,25 @@
|
||||
str
|
||||
simple-utils
|
||||
tezos-utils
|
||||
getopt)
|
||||
(flags (:standard -open Simple_utils -open Parser_shared -open Parser_cameligo)))
|
||||
getopt
|
||||
)
|
||||
(preprocess
|
||||
(pps bisect_ppx --conditional)
|
||||
)
|
||||
(flags (:standard -open Simple_utils -open Parser_shared -open Parser_cameligo ))
|
||||
)
|
||||
|
||||
(executable
|
||||
(name LexerMain)
|
||||
(libraries
|
||||
parser_reasonligo)
|
||||
(modules
|
||||
(modules
|
||||
LexerMain)
|
||||
(flags (:standard -open Parser_shared -open Parser_reasonligo)))
|
||||
(preprocess
|
||||
(pps bisect_ppx --conditional)
|
||||
)
|
||||
(flags (:standard -open Parser_shared -open Parser_reasonligo))
|
||||
)
|
||||
|
||||
(executable
|
||||
(name ParserMain)
|
||||
@ -35,4 +44,7 @@
|
||||
(modules
|
||||
ParserAPI
|
||||
ParserMain)
|
||||
(preprocess
|
||||
(pps bisect_ppx --conditional)
|
||||
)
|
||||
(flags (:standard -open Simple_utils -open Parser_cameligo -open Parser_shared -open Parser_reasonligo)))
|
||||
|
@ -7,7 +7,11 @@
|
||||
simple-utils
|
||||
uutf
|
||||
getopt
|
||||
zarith)
|
||||
zarith
|
||||
)
|
||||
(preprocess
|
||||
(pps bisect_ppx --conditional)
|
||||
)
|
||||
(modules
|
||||
Lexer
|
||||
LexerLog
|
||||
|
@ -12,6 +12,7 @@
|
||||
(preprocess
|
||||
(pps
|
||||
ppx_let
|
||||
bisect_ppx --conditional
|
||||
)
|
||||
)
|
||||
(flags (:standard -w +1..62-4-9-44-40-42-48-30@39@33 -open Simple_utils ))
|
||||
|
@ -4,9 +4,10 @@
|
||||
(libraries
|
||||
simple-utils
|
||||
ast_simplified
|
||||
proto-alpha-utils
|
||||
)
|
||||
(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 ))
|
||||
)
|
||||
|
@ -1,9 +1,28 @@
|
||||
open Ast_simplified
|
||||
open Trace
|
||||
open Proto_alpha_utils
|
||||
|
||||
module Errors = struct
|
||||
let bad_literal_address s_addr loc () =
|
||||
let title = (thunk ("Badly formatted address \""^s_addr^"\"")) in
|
||||
let message () = "" in
|
||||
let data = [
|
||||
("location" , fun () -> Format.asprintf "%a" Location.pp loc)
|
||||
] in
|
||||
error ~data title message ()
|
||||
end
|
||||
open Errors
|
||||
|
||||
let peephole_expression : expression -> expression result = fun e ->
|
||||
let return expression = ok { e with expression } in
|
||||
match e.expression with
|
||||
| E_literal (Literal_address s) as l -> (
|
||||
let open Memory_proto_alpha in
|
||||
let%bind (_contract:Protocol.Alpha_context.Contract.t) =
|
||||
Trace.trace_alpha_tzresult (bad_literal_address s e.location) @@
|
||||
Protocol.Alpha_context.Contract.of_b58check s in
|
||||
return l
|
||||
)
|
||||
| E_constant (C_BIG_MAP_LITERAL , lst) -> (
|
||||
let%bind elt =
|
||||
trace_option (simple_error "big_map literal expects a single parameter") @@
|
||||
|
@ -11,7 +11,7 @@
|
||||
operators
|
||||
)
|
||||
(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 ))
|
||||
)
|
||||
|
@ -10,7 +10,7 @@
|
||||
operators
|
||||
)
|
||||
(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 ))
|
||||
)
|
||||
|
@ -6,7 +6,7 @@
|
||||
)
|
||||
(inline_tests)
|
||||
(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 ))
|
||||
)
|
||||
|
@ -9,7 +9,7 @@
|
||||
operators
|
||||
)
|
||||
(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 ))
|
||||
)
|
||||
|
@ -6,7 +6,7 @@
|
||||
tezos-utils
|
||||
)
|
||||
(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 ))
|
||||
)
|
||||
|
@ -9,7 +9,7 @@
|
||||
mini_c
|
||||
)
|
||||
(preprocess
|
||||
(pps ppx_let)
|
||||
(pps ppx_let bisect_ppx --conditional)
|
||||
)
|
||||
(flags (:standard -open Simple_utils ))
|
||||
)
|
||||
|
@ -92,6 +92,7 @@ module Simplify = struct
|
||||
| "bytes_concat" -> ok C_CONCAT
|
||||
| "bytes_slice" -> ok C_SLICE
|
||||
| "bytes_pack" -> ok C_BYTES_PACK
|
||||
| "bytes_unpack" -> ok C_BYTES_UNPACK
|
||||
| "set_empty" -> ok C_SET_EMPTY
|
||||
| "set_mem" -> ok C_SET_MEM
|
||||
| "set_add" -> ok C_SET_ADD
|
||||
@ -243,6 +244,7 @@ module Simplify = struct
|
||||
| "AND" -> ok C_AND
|
||||
| "OR" -> ok C_OR
|
||||
| "GT" -> ok C_GT
|
||||
| "GE" -> ok C_GE
|
||||
| "LT" -> ok C_LT
|
||||
| "LE" -> ok C_LE
|
||||
| "CONS" -> ok C_CONS
|
||||
|
@ -1,3 +1,4 @@
|
||||
[@@@coverage exclude_file]
|
||||
open Types
|
||||
open PP_helpers
|
||||
open Format
|
||||
|
@ -7,7 +7,7 @@
|
||||
stage_common
|
||||
)
|
||||
(preprocess
|
||||
(pps ppx_let)
|
||||
(pps ppx_let bisect_ppx --conditional)
|
||||
)
|
||||
(flags (:standard -open Simple_utils ))
|
||||
)
|
||||
|
@ -1,3 +1,4 @@
|
||||
[@@@coverage exclude_file]
|
||||
open Types
|
||||
open Format
|
||||
open PP_helpers
|
||||
|
@ -8,7 +8,7 @@
|
||||
stage_common
|
||||
)
|
||||
(preprocess
|
||||
(pps ppx_let)
|
||||
(pps ppx_let bisect_ppx --conditional)
|
||||
)
|
||||
(flags (:standard -open Simple_utils))
|
||||
)
|
||||
|
@ -7,7 +7,7 @@
|
||||
)
|
||||
(inline_tests)
|
||||
(preprocess
|
||||
(pps ppx_let ppx_expect)
|
||||
(pps ppx_let ppx_expect bisect_ppx --conditional)
|
||||
)
|
||||
(flags (:standard -open Simple_utils))
|
||||
)
|
||||
|
@ -1,3 +1,4 @@
|
||||
[@@@coverage exclude_file]
|
||||
open Simple_utils.PP_helpers
|
||||
open Types
|
||||
open Format
|
||||
|
@ -7,6 +7,6 @@
|
||||
stage_common
|
||||
)
|
||||
(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 ))
|
||||
)
|
||||
|
@ -8,7 +8,7 @@
|
||||
mini_c
|
||||
)
|
||||
(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 ))
|
||||
)
|
||||
|
3
src/test/contracts/bad_address_format.religo
Normal file
3
src/test/contracts/bad_address_format.religo
Normal file
@ -0,0 +1,3 @@
|
||||
let main = (parameter: int, storage: address) => {
|
||||
([]:list(operation), "KT1badaddr" : address);
|
||||
};
|
11
src/test/contracts/bytes_unpack.ligo
Normal file
11
src/test/contracts/bytes_unpack.ligo
Normal file
@ -0,0 +1,11 @@
|
||||
function id_string (const p : string) : option(string) is block {
|
||||
const packed : bytes = bytes_pack(p) ;
|
||||
} with (bytes_unpack(packed): option(string))
|
||||
|
||||
function id_int (const p : int) : option(int) is block {
|
||||
const packed : bytes = bytes_pack(p) ;
|
||||
} with (bytes_unpack(packed): option(int))
|
||||
|
||||
function id_address (const p : address) : option(address) is block {
|
||||
const packed : bytes = bytes_pack(p) ;
|
||||
} with (bytes_unpack(packed): option(address))
|
@ -31,3 +31,7 @@ function foobar (const i : int) : int is
|
||||
| Zero (n) -> i
|
||||
| Pos (n) -> (failwith ("waaaa") : int)
|
||||
end
|
||||
|
||||
function failer(const p : int) : int is block {
|
||||
if p = 1 then failwith("some_string") else skip ;
|
||||
} with p
|
||||
|
3
src/test/contracts/included.mligo
Normal file
3
src/test/contracts/included.mligo
Normal file
@ -0,0 +1,3 @@
|
||||
// Demonstrate CameLIGO inclusion statements, see includer.mligo
|
||||
|
||||
let foo : int = 144
|
3
src/test/contracts/included.religo
Normal file
3
src/test/contracts/included.religo
Normal file
@ -0,0 +1,3 @@
|
||||
// Demonstrate ReasonLIGO inclusion statements, see includer.religo
|
||||
|
||||
let foo : int = 144;
|
5
src/test/contracts/includer.mligo
Normal file
5
src/test/contracts/includer.mligo
Normal file
@ -0,0 +1,5 @@
|
||||
// Demonstrate CameLIGO inclusion statements, see included.mligo
|
||||
|
||||
#include "included.mligo"
|
||||
|
||||
let bar : int = foo
|
5
src/test/contracts/includer.religo
Normal file
5
src/test/contracts/includer.religo
Normal file
@ -0,0 +1,5 @@
|
||||
// Demonstrate ReasonLIGO inclusion statements, see included.religo
|
||||
|
||||
#include "included.religo"
|
||||
|
||||
let bar : int = foo;
|
8
src/test/doc_test.ml
Normal file
8
src/test/doc_test.ml
Normal file
@ -0,0 +1,8 @@
|
||||
open Test_helpers
|
||||
|
||||
let () =
|
||||
Printexc.record_backtrace true ;
|
||||
run_test @@ test_suite "LIGO" [
|
||||
Md_file_tests.main ;
|
||||
] ;
|
||||
()
|
@ -1,5 +1,7 @@
|
||||
(ocamllex md)
|
||||
|
||||
(executables
|
||||
(names test manual_test)
|
||||
(names test manual_test doc_test)
|
||||
(libraries
|
||||
simple-utils
|
||||
ligo
|
||||
@ -12,6 +14,12 @@
|
||||
(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
|
||||
(name ligo-test)
|
||||
(action (run ./test.exe))
|
||||
@ -21,7 +29,7 @@
|
||||
|
||||
(alias
|
||||
(name runtest)
|
||||
(deps (alias ligo-test))
|
||||
(deps (alias ligo-test) (alias doc-test))
|
||||
)
|
||||
|
||||
(alias
|
||||
|
@ -604,6 +604,14 @@ let include_ () : unit result =
|
||||
let%bind program = type_file "./contracts/includer.ligo" in
|
||||
expect_eq_evaluate program "bar" (e_int 144)
|
||||
|
||||
let include_mligo () : unit result =
|
||||
let%bind program = mtype_file "./contracts/includer.mligo" in
|
||||
expect_eq_evaluate program "bar" (e_int 144)
|
||||
|
||||
let include_religo () : unit result =
|
||||
let%bind program = retype_file "./contracts/includer.religo" in
|
||||
expect_eq_evaluate program "bar" (e_int 144)
|
||||
|
||||
let record_ez_int names n =
|
||||
ez_e_record @@ List.map (fun x -> x, e_int n) names
|
||||
|
||||
@ -1812,7 +1820,18 @@ let let_in_multi_bind () : unit result =
|
||||
(e_string "mynameisbob")
|
||||
in ok ()
|
||||
|
||||
let bytes_unpack () : unit result =
|
||||
let%bind program = type_file "./contracts/bytes_unpack.ligo" in
|
||||
let%bind () = expect_eq program "id_string" (e_string "teststring") (e_some (e_string "teststring")) in
|
||||
let%bind () = expect_eq program "id_int" (e_int 42) (e_some (e_int 42)) in
|
||||
let open Proto_alpha_utils.Memory_proto_alpha in
|
||||
let addr = Protocol.Alpha_context.Contract.to_b58check @@
|
||||
(List.nth dummy_environment.identities 0).implicit_contract in
|
||||
let%bind () = expect_eq program "id_address" (e_address addr) (e_some (e_address addr)) in
|
||||
ok ()
|
||||
|
||||
let main = test_suite "Integration (End to End)" [
|
||||
test "bytes unpack" bytes_unpack ;
|
||||
test "key hash" key_hash ;
|
||||
test "chain id" chain_id ;
|
||||
test "type alias" type_alias ;
|
||||
@ -1888,6 +1907,8 @@ let main = test_suite "Integration (End to End)" [
|
||||
test "quote declaration" quote_declaration ;
|
||||
test "quote declarations" quote_declarations ;
|
||||
test "#include directives" include_ ;
|
||||
test "#include directives (mligo)" include_mligo ;
|
||||
test "#include directives (religo)" include_religo ;
|
||||
test "counter contract" counter_contract ;
|
||||
test "super counter contract" super_counter_contract ;
|
||||
test "super counter contract" super_counter_contract_mligo ;
|
||||
|
62
src/test/md.mll
Normal file
62
src/test/md.mll
Normal 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")
|
||||
}
|
121
src/test/md_file_tests.ml
Normal file
121
src/test/md_file_tests.ml
Normal file
@ -0,0 +1,121 @@
|
||||
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/language-basics/loops.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)
|
Loading…
Reference in New Issue
Block a user