diff --git a/gitlab-pages/docs/advanced/attributes-decorators.md b/gitlab-pages/docs/advanced/attributes-decorators.md new file mode 100644 index 0000000000..ff8c4391ef --- /dev/null +++ b/gitlab-pages/docs/advanced/attributes-decorators.md @@ -0,0 +1,187 @@ +--- +id: decorators +title: Attributes or Decorators +--- + +import Syntax from '@theme/Syntax'; + + + +# Attributes + +Attributes (also known as "decorators" in JsLIGO, annotations or pragmas in other languages) are markers that can affect how the code immediately after is compiled or interpreted. Depending on the attribute, that code can be a definition, variable, statement block and so on. The sections below describe the attributes supported by CameLIGO. + +It is possible to specify multiple attributes as follows: + +```cameligo +type storage = int +type result = operation list * storage + +[@entry] [@no_mutation] +let sub (delta : int) (store : storage) : result = + [], store - delta +``` + +## @entry to mark a function as a Smart Contract entrypoint + +The `@entry` attribute is used to indicate that a function should be available as an entrypoint of the smart contract. It is essentially declaring that function as one of the several "main" functions. For more information, see [Entrypoints](../syntax/contracts/entrypoints). + +## @dyn_entry to mark a function as a dynamic entrypoint + +The `@dyn_entry` attribute is used to indicate that a function should be available as a dynamic entrypoint of the smart contract. Dynamic entrypoints can be removed or updated without deploying a new contract, unlike entrypoints marked with `@entry`, which cannot be modified after the contract is originated. Dynamic entrypoints can be used for example to implement a DAO (Decentralized Autonomous Organization) with a built-in update mechanism that allows participants to vote on upgrades to the contract, somewhat akin to the process which allows to amend the Tezos protocol. For more information, see [Dynamic entrypoints](../syntax/contracts/dynamic-entrypoints.md). + +## @inline to ensure a function is inlined at its call sites + +The `@inline` attribute indicates that the code of the function it annotates must be inlined wherever it is called. This allows some optimizations to be performed, possibly at the expense of a larger compiled code. Benchmarks and profiling can help decide whether a function should be inlined or nor. For more information, see [Inlining](../syntax/functions#inlining). + +## @view to mark a function as a Smart Contract on-chain view + +Views are a form of read-only entrypoints, that can be called synchronously. For more information, see [LIGO views](../syntax/contracts/views). + +## @no_mutation + +See [the documentation on mutation testing](../testing/mutation-testing.md#preventing-mutation). + +## @private to prevent an identifier from being accessed outside of its defining module + +The `[@private]` decorator can be used on a top-level declaration, to prevent that value from being used outside the declaring module. + +```cameligo group=module-with-private +(* This is gitlab-pages/docs/advanced/src/attributes-decorators/module-with-private.mligo *) +[@private] let stuff = 42 +[@private] let g x = x * stuff +let f x = (g x) + 1 +``` + +```cameligo group=import-module-with-private +module ModuleWithPrivate = + Gitlab_pages.Docs.Advanced.Src.Attributes_decorators.Module_with_private + +(* foo = 5167 = (123 * 42) + 1 *) +let foo = ModuleWithPrivate.f 123 + +(* + The following lines cause errors because g and stuff are private: + + let bad_1 = ModuleWithPrivate.g 123 + let bad_2 = ModuleWithPrivate.stuff +*) +``` + +## @annot to ornate the generated code with Michelson annotations + +This attribute can be used to choose the name of the Michelson equivalent of record fields or variant constructors. By default, a variant is compiled to a comb of Michelson `or`, and each leaf in the nested tree of `or` that corresponds to a constructor is annotated with that constructor's name. Similarly, a record is compiled to a comb of Michelson `pair`, and each leaf in the nested tree of `pair` that corresponds to a field is annotated with that field's name. + +Using `@annot`, the Michelson annotation for a given field or constructor can be customized. This is useful for interoperability, where a third-party program or contract expects specific Michelson annotations, even if the LIGO code might not use these names internally. + +For more information, see [Interop: Different Michelson annotations](../syntax/contracts/interop.md#different-michelson-annotations)? + +## @layout to specify the Michelson layout of composite data types (structures and variants) + +Michelson does not natively support records or variants. These have to be encoded using nested `pair`s or nested `or`s. Many tree representations could translate to the same linear sequence of fields or constructors. LIGO makes it possible to choose between a right comb which preserves the order or the fields or constructors as declared in the source code, and a left-balanced, alphabetically ordered binary tree. The attributes `[@layout comb]` and `[@layout tree]` can be placed before the `{ ... }` for records and before the first constructor or leading `|` for variants, in order to explicitly choose the desired layout. + +For more information, see [Interop: Michelson layout of LIGO data structures](../syntax/contracts/interop.md##michelson-layout-of-ligo-data-structures). + +## @tzip16_compatible to enable TZIP-16 storage checks + +The `@tzip16_compatible` attribute is used to indicate a storage variable that is expected to contain metadata compliant with [TZIP-16 specification](https://gitlab.com/tezos/tzip/-/blob/master/proposals/tzip-16/tzip-16.md). Values marked with this attribute are subject to automatic checks verifying that the storage indeed satisfies TZIP-16 rules. + +Note that at the moment there is no support for thus feature from the LIGO compiler; IDEs however can recognize the attribute and provide additional verification. + +The attribute works only on variables that are not functions. In case a function returning storage needs to be verified, one can create one or several sample storages out of it and apply the attribute to them. + +## Internal attributes + +Furthermore, the following attributes are used internally by the compiler, you may encounter them when exporting the AST after a certain compilation pass, but they should not appear in normal source code. + +* `@thunk` +* `@hidden` +* `@deprecated` + + + + + +# Decorators + +Decorators (also known as "attributes" in CameLIGO, annotations or pragmas in other languages) are markers that can affect how the code immediately after is compiled or interpreted. Depending on the decorator, that code can be a definition, variable, statement block and so on. The sections below describe the decorators supported by JsLIGO. + +It is possible to specify multiple decorators as follows: + +```jsligo +type storage = int; +type result = [list, storage]; + +// @entry +// @no_mutation +const sub = (delta : int, store : storage) : result => + [[], store - delta] +``` + +## @entry to mark a function as a Smart Contract entrypoint + +The `@entry` decorator is used to indicate that a function should be available as an entrypoint of the smart contract. It is essentially declaring that function as one of the several "main" functions. For more information, see [Entrypoints](../syntax/contracts/entrypoints). + +## @dyn_entry to mark a function as a dynamic entrypoint + +The `@dyn_entry` decorator is used to indicate that a function should be available as a dynamic entrypoint of the smart contract. Dynamic entrypoints can be removed or updated without deploying a new contract, unlike entrypoints marked with `@entry`, which cannot be modified after the contract is originated. Dynamic entrypoints can be used for example to implement a DAO (Decentralized Autonomous Organization) with a built-in update mechanism that allows participants to vote on upgrades to the contract, somewhat akin to the process which allows to amend the Tezos protocol. For more information, see [Dynamic entrypoints](../syntax/contracts/dynamic-entrypoints.md). + +## @inline to ensure a function is inlined at its call sites + +The `@inline` decorator indicates that the code of the function it annotates must be inlined wherever it is called. This allows some optimizations to be performed, possibly at the expense of a larger compiled code. Benchmarks and profiling can help decide whether a function should be inlined or nor. For more information, see [Inlining](../syntax/functions#inlining). + +## @view to mark a function as a Smart Contract on-chain view + +Views are a form of read-only entrypoints, that can be called synchronously. For more information, see [LIGO views](../syntax/contracts/views). + +## @no_mutation + +See [the documentation on mutation testing](../testing/mutation-testing.md#preventing-mutation). + +## @private to prevent an identifier from being accessed outside of its defining module + +This decorator is meant to be used in CameLIGO, where definitions are public/exported by default. It has no use in JsLIGO. + +## @annot to ornate the generated code with Michelson annotations + +This decorator can be used to choose the name of the Michelson equivalent of record fields or variant constructors. By default, a variant is compiled to a comb of Michelson `or`, and each leaf in the nested tree of `or` that corresponds to a constructor is annotated with that constructor's name. Similarly, a record is compiled to a comb of Michelson `pair`, and each leaf in the nested tree of `pair` that corresponds to a field is annotated with that field's name. + +Using `@annot`, the Michelson annotation for a given field or constructor can be customized. This is useful for interoperability, where a third-party program or contract expects specific Michelson annotations, even if the LIGO code might not use these names internally. + +For more information, see [Interop: Different Michelson annotations](../syntax/contracts/interop.md#different-michelson-annotations)? + +## @layout to specify the Michelson layout of composite data types (structures and variants) + +Michelson does not natively support objects or variants. These have to be encoded using nested `pair`s or nested `or`s. Many tree representations could translate to the same linear sequence of fields or constructors. LIGO makes it possible to choose between a right comb which preserves the order or the fields or constructors as declared in the source code, and a left-balanced, alphabetically ordered binary tree. The decorators `@layout("comb")` and `@layout("tree")` can be placed before the `{ ... }` for objects and before the first constructor or leading `|` for variants, in order to explicitly choose the desired layout. + +For more information, see [Interop: Michelson layout of LIGO data structures](../syntax/contracts/interop.md##michelson-layout-of-ligo-data-structures). + +## @tzip16_compatible to enable TZIP-16 storage checks + +The `@tzip16_compatible` decorator is used to indicate a storage variable that is expected to contain metadata compliant with [TZIP-16 specification](https://gitlab.com/tezos/tzip/-/blob/master/proposals/tzip-16/tzip-16.md). Values marked with this decorator are subject to automatic checks verifying that the storage indeed satisfies TZIP-16 rules. + +Note that at the moment there is no support for thus feature from the LIGO compiler; IDEs however can recognize the decorator and provide additional verification. + +The decorator works only on variables that are not functions. In case a function returning storage needs to be verified, one can create one or several sample constant storages out of it and apply the decorator to them. + +## export to mark an identifier as accessible outside its defining module + +This decorator is slightly different from the others in that it does not need `@` and simply appears as follows: + +```jsligo +namespace MathPi { + export const pi_millionth_numerator : int = 3141593 + export const pi_millionth_denominator : nat = 1000000 as nat +} +``` + +## Internal decorators + +Furthermore, the following attributes are used internally by the compiler, you may encounter them when exporting the AST after a certain compilation pass, but they should not appear in normal source code. + +* `@thunk` +* `@hidden` +* `@public` +* `@deprecated` + + diff --git a/gitlab-pages/docs/advanced/code-injection.md b/gitlab-pages/docs/advanced/code-injection.md index b9df093adb..68071b0651 100644 --- a/gitlab-pages/docs/advanced/code-injection.md +++ b/gitlab-pages/docs/advanced/code-injection.md @@ -119,19 +119,19 @@ let main (action : parameter) (store : storage) : operation list * storage = -```jsligo skip +```jsligo group=never type parameter = ["Increment", int] | ["Extend", never]; type storage = int; -@entry +// @entry function main (action: parameter, store: storage) : [list, storage] { let storage = - match(action, { - Increment: n => store + n, - Extend: k => (Michelson`{ NEVER }` as ((n: never) => int))(k) + $match(action, { + "Increment": n => store + n, + "Extend": k => (Michelson`{ NEVER }` as ((n: never) => int))(k) }); return [[], storage]; }; @@ -155,4 +155,3 @@ ligo compile contract --protocol nairobi --disable-michelson-typechecking gitlab > instruction `NEVER` directly from LIGO, using `Tezos.never`. - diff --git a/gitlab-pages/docs/advanced/global-constants.md b/gitlab-pages/docs/advanced/global-constants.md index 2b80e03ba2..36a5b3c51b 100644 --- a/gitlab-pages/docs/advanced/global-constants.md +++ b/gitlab-pages/docs/advanced/global-constants.md @@ -57,10 +57,10 @@ let main (_p : unit) (s : int) : operation list * int = ```jsligo group=global_call -const c : ((_p : int) => int) = Tezos.constant("expruCKsgmUZjC7k8NRcwbcGbFSuLHv5rUyApNd972MwArLuxEZQm2") +const c : (_p : int) => int = Tezos.constant("expruCKsgmUZjC7k8NRcwbcGbFSuLHv5rUyApNd972MwArLuxEZQm2") -@entry -let main = (_p : unit, s : int) : [list, int] => +// @entry +const main = (_p : unit, s : int) : [list, int] => [[], c(s)] ``` @@ -147,7 +147,7 @@ let main (p : string) (s : int) : operation list * int = const helper = ([s, x]: [string, int]) => String.length(s) + x * 3 + 2; -@entry +// @entry const main = (p: string, s: int) : [list, int] => [[], helper ([p, s])]; ``` @@ -165,7 +165,7 @@ ligo compile constant cameligo "helper" --init-file ./gitlab-pages/docs/advanced # Michelson constant as JSON string: # "{ UNPAIR ;\n PUSH int 2 ;\n PUSH int 3 ;\n DIG 3 ;\n MUL ;\n DIG 2 ;\n SIZE ;\n ADD ;\n ADD }" # This string can be passed in `--constants` argument when compiling a contract. -# +# # Remember to register it in the network, e.g.: # > tezos-client register global constant "{ UNPAIR ; # PUSH int 2 ; @@ -176,7 +176,7 @@ ligo compile constant cameligo "helper" --init-file ./gitlab-pages/docs/advanced # SIZE ; # ADD ; # ADD }" from bootstrap1 -# +# # Constant hash: # exprv547Y7U5wKLbQGmkDU9Coh5tKPzvEJjyUed7px9yGt9nrkELXf ``` @@ -191,7 +191,7 @@ ligo compile constant jsligo "helper" --init-file ./gitlab-pages/docs/advanced/s # Michelson constant as JSON string: # "{ UNPAIR ;\n PUSH int 2 ;\n PUSH int 3 ;\n DIG 3 ;\n MUL ;\n DIG 2 ;\n SIZE ;\n ADD ;\n ADD }" # This string can be passed in `--constants` argument when compiling a contract. -# +# # Remember to register it in the network, e.g.: # > tezos-client register global constant "{ UNPAIR ; # PUSH int 2 ; @@ -202,7 +202,7 @@ ligo compile constant jsligo "helper" --init-file ./gitlab-pages/docs/advanced/s # SIZE ; # ADD ; # ADD }" from bootstrap1 -# +# # Constant hash: # exprv547Y7U5wKLbQGmkDU9Coh5tKPzvEJjyUed7px9yGt9nrkELXf ``` @@ -241,7 +241,7 @@ references to `helper` by ``` -(Tezos.constant("exprv547Y7U5wKLbQGmkDU9Coh5tKPzvEJjyUed7px9yGt9nrkELXf") as ((_ps : [string, int]) => int)) +(Tezos.constant("exprv547Y7U5wKLbQGmkDU9Coh5tKPzvEJjyUed7px9yGt9nrkELXf") as (_ps : [string, int]) => int) ``` @@ -261,7 +261,7 @@ let main (p : string) (s : int) : operation list * int = ```jsligo group=global_call_2 -@entry +// @entry const main = (p: string, s: int) : [list, int] => [ [], Tezos.constant("exprv547Y7U5wKLbQGmkDU9Coh5tKPzvEJjyUed7px9yGt9nrkELXf")([p, s]) ]; ``` @@ -340,7 +340,7 @@ The string returned by `Test.register_constant` can be used via `Tezos.constant`, as in the examples above. A simple usage case is the following, in which we obtain a -`michelson_program` by using `Test.eval`: +`michelson_program` by using `Test.Michelson.eval`: @@ -352,17 +352,17 @@ module C = struct let f (x : int) = x * 3 + 2 - let ct = Test.register_constant (Test.eval f) + let ct = Test.State.register_constant (Test.Michelson.eval f) [@entry] let main (() : parameter) (store : storage) : return = - [], (Tezos.constant ct store) + [], Tezos.constant ct store end let test = - let orig = Test.originate (contract_of C) 1 0tez in - let _ = Test.transfer_exn orig.addr (Main ()) 0tez in - assert (Test.get_storage orig.addr = 5) + let orig = Test.Originate.contract (contract_of C) 1 0tez in + let _ = Test.Typed_address.transfer_exn orig.taddr (Main ()) 0tez in + Assert.assert (Test.Typed_address.get_storage orig.taddr = 5) ``` @@ -370,23 +370,23 @@ let test = ```jsligo test-ligo group=test_global -namespace C { - type storage = int - type parameter = unit +type storage = int; +type parameter = unit; - const f = (x : int) => x * 3 + 2; +class C { + static f = (x : int) => x * 3 + 2; - const ct = Test.register_constant(Test.eval(f)); + static ct = Test.State.register_constant(Test.Michelson.eval(f)); @entry - const main = (p: parameter, s: storage) : [list, storage] => + main = (_p: parameter, s: storage) : [list, storage] => [[], Tezos.constant(ct)(s)]; } const _test = () => { - let orig = Test.originate(contract_of(C), 1, 0tez); - Test.transfer_exn(orig.addr, Main(unit), 0tez); - assert (Test.get_storage(orig.addr) == 5); + let orig = Test.Originate.contract(contract_of(C), 1, 0 as tez); + Test.Typed_address.transfer_exn(orig.taddr, ["Main" as "Main"], 0 as tez); + Assert.assert (Test.Typed_address.get_storage(orig.taddr) == 5); }; const test = _test(); diff --git a/gitlab-pages/docs/advanced/include.md b/gitlab-pages/docs/advanced/include.md new file mode 100644 index 0000000000..d412116863 --- /dev/null +++ b/gitlab-pages/docs/advanced/include.md @@ -0,0 +1,33 @@ +--- +id: include +title: Including Other Contracts +--- + +import Syntax from '@theme/Syntax'; + + + +Let us say that we have a contract that is getting a too large. 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 the contract names `included`: + +```cameligo group=included +(* This is "included.mligo" *) +(* Demonstrate CameLIGO inclusion statements, see includer.mligo *) + +let foo = 144 +``` + +And then you can include this code using the `#include` statement like +so: + +```cameligo +#include "gitlab-pages/docs/advanced/src/include/included.mligo" + +let bar = foo +``` + + diff --git a/gitlab-pages/docs/advanced/package-management.md b/gitlab-pages/docs/advanced/package-management.md index c15dd85f88..4100c3254f 100644 --- a/gitlab-pages/docs/advanced/package-management.md +++ b/gitlab-pages/docs/advanced/package-management.md @@ -82,17 +82,6 @@ Now we can write a smart contract which will use the `@ligo/mathlib` library. - - -```jsligo skip -#import "@ligo/mathlib/rational/rational.mligo" "Rational" - -... - -``` - - -
> Note: When using LIGO packages via `#import`/`#include` @@ -126,25 +115,6 @@ let test =
- - -```jsligo skip -#include "main.jsligo" - -const test = (() => { - let storage = Test.compile_value([1, 2, 3]); - let [addr, _, _] = Test.originate_from_file("./main.jsligo", - "main", ([] as list), storage, 0tez); - let taddr : typed_address = Test.cast_address(addr); - let contr : contract = Test.to_contract(taddr); - Test.transfer_to_contract_exn(contr, Reverse(), 1mutez); - assert (Test.get_storage(taddr) == [3, 2, 1]) -})(); - -``` - - - To compile the contract to Michelson run the command @@ -155,14 +125,6 @@ $ ligo compile contract main.mligo - - -```bash -$ ligo compile contract main.jsligo -``` - - - This will find the dependencies installed on the local machine, and compile the `main.mligo` file. To test the contract using LIGO's [testing framework](../testing/testing.md) run the command @@ -175,14 +137,6 @@ $ ligo run test main.test.mligo
- - -```bash -$ ligo run test main.test.jsligo -``` - - - If you working with an existing LIGO project, to install the dependencies, at the root of the project just run ```bash @@ -243,23 +197,6 @@ In [3]:
- - -``` -$ ~/projects/ligo/_build/install/default/bin/ligo repl jsligo -Welcome to LIGO's interpreter! -Included directives: - #use "file_path";; - #import "file_path" "module_name";; -In [1]: #import "@ligo/bigarray/lib/bigarray.mligo" "BA";; -Out [1]: Done. -In [2]: BA.concat ([1, 2, 3])([4, 5, 6]);; -Out [2]: CONS(1 , CONS(2 , CONS(3 , CONS(4 , CONS(5 , CONS(6 , LIST_EMPTY())))))) -In [3]: -``` - - - ## Packaging Packages are code units that can be shared with other developers. Therefore, @@ -352,24 +289,6 @@ let reverse (type a) (xs : a list) : a list = - - -```jsligo group=pkg -/* LIGO library for working with lists */ - -export const concat = (xs : list, ys : list) : list => { - let f = ([x, ys] : [T, list]) : list => [x, ...ys]; - return List.fold_right(f, xs, ys) -} - -export const reverse = (xs : list) : list => { - let f = ([ys, x] : [list, T]) : list => [x, ...ys]; - return List.fold_left(f, [], xs) -} - -``` - -
and some tests for the library @@ -393,26 +312,6 @@ let test_reverse = - - -```jsligo skip -#include "list.jsligo" - -const test_concat = (() => { - let xs : list = [1, 2, 3]; - let ys : list = [4, 5, 6]; - let zs = concat(xs, ys); - assert (zs == [1, 2, 3, 4, 5, 6]) -})(); - -const test_reverse = (() => { - let xs : list = [1, 2, 3]; - assert (reverse(xs) == [3, 2, 1]) -})(); - -``` - -
To run the tests run the command @@ -423,13 +322,6 @@ To run the tests run the command $ ligo run test list.test.mligo ``` - - - -```bash -$ ligo run test list.test.jsligo -``` - ### Logging in @@ -510,14 +402,6 @@ $ ligo compile contract main.mligo --project-root PATH - - -```bash -$ ligo compile contract main.jsligo --project-root PATH -``` - - - ### --ligorc-path LIGO creates a `.ligorc` file to store auth tokens for the user for a specific registry, This auth token is useful when publishing a package. @@ -587,7 +471,7 @@ Yes, any syntax can be used in packages. Furthermore, one can consume a package If you need to use the entry points defined within a package, the best approach is likely to alias them: ```cameligo skip -#import "package_name/increment.mligo" "Increment" +module Increment = Package_name.Increment [@entry] let add = Increment.add ``` diff --git a/gitlab-pages/docs/advanced/security.md b/gitlab-pages/docs/advanced/security.md new file mode 100644 index 0000000000..4d58463efc --- /dev/null +++ b/gitlab-pages/docs/advanced/security.md @@ -0,0 +1,473 @@ +--- +id: security +title: Smart contract security +--- + +import Syntax from '@theme/Syntax'; + +Web3 developers need to keep some specific vulnerabilities in mind when they write on-chain and off-chain applications. +This page covers the basics of smart contract security on Tezos, some of these potential vulnerabilities, and how to protect your contracts against them. + +:::note + +This guide is aimed at giving the reader an overview of popular attacks on smart contracts and distributed applications. +It is not an exhaustive list of all the possible attack vectors. +Use your own judgement, good programming practice, and thorough testing on your contracts. + +The descriptions in this document are valid for the Tezos protocol since the Edo upgrade, last updated at the Rio upgrade. +Because Tezos is an upgradeable blockchain, some of the blockchain mechanics may change when new protocols are adopted. +For this reason, Tezos developers must stay up to date on the changes in the protocol via sources such as the Octez and protocol documentation at https://octez.tezos.com. + +::: + +See these links for more information about security on Tezos applications: + +- The smart contracts section on https://opentezos.com +- The tutorial [Learn and play with security](https://docs.tezos.com/tutorials/security) on docs.tezos.com + +## Resource constraints + +Tezos limits the size of an operation so that nodes can broadcast operations over the network in a reasonable time. +It also places a limit on the computations that bakers need to perform to validate an operation to keep the network running smoothly. +This limit is called the *gas limit* because it is the maximum amount of computations (measured in *gas units*) that a single operation can require. + +Of course, developers make their contracts efficient to save on gas fees, but they must also keep the gas limit in mind because it can lead to security vulnerabilities. + +For example, look at this seemingly innocent wallet contract that stores an event log: + + + +```cameligo group=walletwithflaw +module WalletWithFlaw = struct + + (* Variant for two types of transactions *) + type transaction = + Deposit of address * tez + | Withdrawal of address * tez + + type storage = { + owner : address; + transactionLog : transaction list + } + + type return_type = operation list * storage + + (* Receive a deposit *) + [@entry] + let deposit (_ : unit) (storage : storage) : return_type = + (* Verify that tez was sent *) + let _ = if Tezos.get_amount () = 0tez then failwith "Send tez to deposit" in + (* Add log entry *) + let newLogEntry : transaction = Deposit (Tezos.get_sender (), Tezos.get_amount ()) in + [], { storage with transactionLog = newLogEntry :: storage.transactionLog } + + (* Return a withdrawal *) + [@entry] + let withdraw (tx_destination, tx_amount : address * tez) (storage : storage) : return_type = + (* Verify that the sender is the admin *) + let _ = if Tezos.get_sender () <> storage.owner then failwith "Not the owner" in + (* Verify that no tez was sent *) + let _ = if Tezos.get_amount () <> 0tez then failwith "Don't send tez to this entrypoint" in + (* Create transaction *) + let callee = Tezos.get_contract_opt tx_destination in + let operation = match callee with + Some contract -> + Tezos.Operation.transaction () tx_amount contract + | None -> failwith "Couldn't send withdrawal to that address" + in + (* Add log entry and return operation and new log *) + let newLogEntry : transaction = Withdrawal (tx_destination, tx_amount) in + [operation], { storage with transactionLog = newLogEntry :: storage.transactionLog } + +end +``` + + + + + +```jsligo group=walletwithflaw +namespace WalletWithFlaw { + + // Variant for two types of transactions + type transaction = + ["Deposit", [address, tez]] + | ["Withdrawal", [address, tez]]; + + type storage = { + owner: address, + transactionLog: list, + }; + + type return_type = [list, storage]; + + // Receive a deposit + // @entry + const deposit = (_: unit, storage: storage): return_type => { + // Verify that tez was sent + if (Tezos.get_amount() == (0 as tez)) { + failwith("Send tez to deposit"); + } + // Add log entry + const newLogEntry: transaction = ["Deposit" as "Deposit", [Tezos.get_sender(), Tezos.get_amount()]]; + return [[], { + owner: storage.owner, + transactionLog: [newLogEntry, ...storage.transactionLog], + }]; + } + + // Return a withdrawal + // @entry + const withdraw = (param : [address, tez], storage : storage): return_type => { + const [tx_destination, tx_amount] = param; + // Verify that the sender is the admin + if (Tezos.get_sender() != storage.owner) { + failwith("Not the owner"); + } + // Verify that no tez was sent + if (Tezos.get_amount() != (0 as tez)) { + failwith("Don't send tez to this entrypoint"); + } + // Create transaction + const callee = Tezos.get_contract_opt(tx_destination); + const operation = $match(callee, { + "Some": contract => (() => Tezos.Operation.transaction(unit, tx_amount, contract))(), + "None": () => failwith("Couldn't send withdrawal to that address"), + }); + // Add log entry and return operation and new log + const newLogEntry: transaction = ["Withdrawal" as "Withdrawal", [tx_destination, tx_amount]]; + return [[operation], { + owner: storage.owner, + transactionLog: [newLogEntry, ...storage.transactionLog], + }]; + } +} +``` + + + +This contract: + +- Can receive funds sent to it via the `Deposit` entrypoint. +- Can send some tez to any account via the `Withdrawal` entrypoint callable by the owner. +- Stores a log of all transactions. + +What can go wrong? +To see the flaw, you need to understand how Tezos processes transactions and what limits it places on them. + +As described above, Tezos puts a limit on the amount of processing that a single transaction can require. +This processing includes loading all non-lazy variables in the contract's storage. +Each variable gets fetched, deserialised, and type-checked each time the contract is called, which requires computation. + +Each time you call this contract, it adds a log entry to the `list` variable in the storage and therefore the storage is larger the next time that you call it. +This design flaw causes two problems: + +- Calling this contract gets more expensive each time you call it +- Eventually the amount of processing required will exceed the maximum for a single transaction and thus it will be impossible to call the contract, making it unusable and locking the tez in it + +Can you think of a way to fix this flaw while retaining the transaction log? +There are several ways, including: + +- Storing the log off the chain or relying on an indexer to get a list of past transactions +- Using a lazy storage type such as a big-map, which is not loaded entirely when the contract is called +- Truncating the log to show only a few recent transactions or otherwise limiting the size of the log + +In this way, you must plan ahead to limit the storage size of contracts as they grow. +Here are some other ways that storage size can cause problems: + +- Unbounded types such as nats, integers, and bytes can become arbitrarily large. +These types are less likely to cause problems than lists and maps but still can. + +- Lambdas in storage can grow or cause data storage issues, so you should never store untrusted lambdas. + +Also, storage size isn't the only way that contracts can exceed the maximum gas and become unusable. +Lambdas or loops in your code can cause vulnerabilities by forcing future transactions to run a large loop or make too many computations, exceeding the gas limit. +You must consider both the storage and the logic of the contract to ensure that it will not exceed the gas limit in the long term. + +## Transaction ordering + +Blockchains use block producers (called *bakers* in Tezos) to put transactions into blocks. +Block producers are free to include or exclude transactions within the blocks they produce and to put transactions in any order. +Transactions run in the order that they are listed in the block, so in certain cases, block producers can manipulate the order of transactions to make a profit or cause a certain effect. + +Also, bakers usually put transactions with higher transaction fees or lower counter values before transactions with lower fees. +Therefore, other actors can sometimes influence transaction ordering for their benefit. + +Manipulating the transaction order like this happens very rarely, but it can cause problems for decentralised finance (DeFi) applications. + +A classic example of a system vulnerable to this kind of attacks is a decentralised exchange with an on-chain orderbook. +This exchange accepts orders to buy and sell assets at a certain price and runs them in the order that it receives them, which depends on the order that the transactions are listed in each block. + +In an attack known as *front-running*, an attacker may see a large transaction coming and use the methods described above to insert their transaction before that large transaction. +For example, they could see a large buy order coming and submit their own buy order with a high transaction fee to get it to run before the large one raises the price of the asset. +In fact, if the front-runner is a baker, the so-called _miner extracted value_ [poses a big risk](https://arxiv.org/pdf/1904.05234.pdf) to security of blockchains in general. + +To defend against this kind of attack, you must prevent block producers and other users from profiting by manipulating the order of transactions. +For example, you could store the order book off-chain or use [timelocks](https://docs.tezos.com/smart-contracts/timelocks) to prevent attackers from seeing incoming transactions. + +## Timestamps + +Aside from transaction ordering, block producers can manipulate other variables that contracts rely on. +For example, block producers set the timestamp of each block that they create based on their own clocks. +In older versions of the Tezos protocol, the value of the `Tezos.get_now` value was this timestamp. +If a contract used the value of `Tezos.get_now` to get the time, block producers could manipulate the timestamp to change the behaviour of the contract. + +In the current Tezos protocol, the value of `Tezos.get_now` is always the timestamp of the previous block plus a fixed value, regardless of the time that the block was actually created, which eliminates straightforward manipulations. +However, contracts that rely on timestamps are still vulnerable to manipulation. +In particular, contracts should never use timestamps or `Tezos.get_now` as a source of randomness. + +## Reentrancy and call injection + +As described in [Operations](../syntax/contracts/operation) and in [Operations](https://docs.tezos.com/smart-contracts/logic/operations) on docs.tezos.com, Tezos orders operations in a way that may appear unusual to developers who are unfamiliar with blockchains. +In particular, when an operation (such as a call to a smart contract) generates other operations, those operations do not run until the original operation completes. +In general, calls to smart contracts run in this order: + +1. The smart contract runs its logic. +1. The smart contract returns any operations and events that it created and the new state of its storage. +1. The protocol updates the contract's storage based on the storage that it returned. +1. The protocol queues the operations and events that the smart contract returned and runs them in order. + +Note that, based on this order, a smart contract cannot run operations or emit events in the middle of its own execution. +It can only queue operations and events to run after it finishes running. + +See [Operations](https://docs.tezos.com/smart-contracts/logic/operations) on docs.tezos.com for examples of operation ordering. + +This process is similar to the checks-effects-interactions pattern popular in Solidity. +In Ethereum, this process is considered a best practice, and Tezos enforces this on the protocol level with operation ordering. +Such restrictions help prevent *reentrancy attacks*, which take advantage of flaws in contracts by calling them at intermediate points in their execution. + +Consider the following snippet in Solidity from a bank smart contract: + +```solidity +function withdraw(uint256 amount) { + uint256 balance = balances[beneficiary]; + require(balance >= amount); + beneficiary.call.value(amount)(); + uint256 new_balance = balance - amount; + balances[beneficiary] = new_balance; +} +``` + +This code follows these general steps: + +1. A caller requests to withdraw funds from their account. +1. The smart contract checks that the requested amount is less than or equal to their bank balance. +1. The smart contract sends the withdrawn funds to the caller. +1. The smart contract updates the caller's balance in storage. + +Note that the _effect_ of updating the storage happens after the _interaction_ (transferring the `amount` to the beneficiary). +As a result, this contract has a reentrancy vulnerability: If the contract execution pauses after the transfer starts but before the contract updates the caller's balance in storage, the caller could start another withdrawal and receive more funds before the first balance update happens. + +The way that Tezos orders operations makes it difficult to run reentrancy attacks. +For example, here is an equivalent contract in LIGO: + + + +```cameligo group=bank +type storage = (address, tez) big_map +type return_type = operation list * storage + +[@entry] +let withdraw (tx_amount : tez) (storage : storage) : return_type = + (* Verify that the caller has enough balance for the withdrawal *) + let old_balance = Big_map.find (Tezos.get_sender ()) storage in + let _ = if tx_amount > old_balance then failwith "Insufficient balance" in + (* Create transaction *) + let receiver_account = match Tezos.get_contract_opt (Tezos.get_sender ()) with + Some account -> account + | None -> failwith "Couldn't find account" in + let operation = Tezos.Operation.transaction unit tx_amount receiver_account in + (* Update balance *) + let new_balance : tez = Option.value_with_error "Unreachable error; we already compared balance to amount" (old_balance - tx_amount) in + let new_storage = Big_map.update (Tezos.get_sender ()) (Some new_balance) storage in + [operation], new_storage +``` + + + + + +```jsligo group=bank +namespace Bank { + + type storage = big_map; + type return_type = [list, storage]; + + // Return a withdrawal + // @entry + const withdraw = (tx_amount: tez, storage: storage): return_type => { + // Verify that the caller has enough balance for the withdrawal + const old_balance = Big_map.find(Tezos.get_sender(), storage); + if (tx_amount > old_balance) { + failwith("Insufficient balance"); + } + // Create transaction + const receiver_account = $match((Tezos.get_contract_opt(Tezos.get_sender())), { + "Some": account => account, + "None": () => failwith("Couldn't find account"), + }); + const operation = Tezos.Operation.transaction(unit, tx_amount, receiver_account); + // Update balance + const new_balance: tez = Option.value_with_error("Unreachable error; we already compared balance to amount", old_balance - tx_amount); + const new_storage = Big_map.update(Tezos.get_sender(), ["Some" as "Some", new_balance], storage); + return [[operation], new_storage]; + } + +} +``` + + + +The general steps of the code are similar, but the critical difference is that the operation to transfer funds (the `op` operation variable) is created but does not run immediately. +The smart contract returns it in the list of operations at the end of its execution and Tezos queues it to run later. +Tezos updates the balances in the contract storage before it runs any subsequent operations, preventing reentrancy attacks. + +However, in some cases reentrancy attacks are still possible on Tezos, especially if contracts wait for a callback in an intermediate state. +For example, if the bank contract stores balances in a separate contract and updates them by sending transactions to that contract, it may be susceptible to reentrancy attacks. +Users may be able to manipulate the order of those transactions to run more than one withdrawal before the transactions update their balance. + +## Transactions to untrusted contracts + +When emitting a transaction to an untrusted contract, you can not assume that it will "play by the rules." +Instead, you should always bear in mind that the callee may fail, causing the entire operation to fail or emit other operations that you do not expect. + +Consider this example, which keeps a list of addresses. +When the owner account calls the `send_rewards` entrypoint, it attempts to send 5 tez to each address: + + + +```cameligo group=rewardswithflaw +module RewardsWithFlaw = struct + + type storage = { + owner : address; + beneficiaries : address list + } + + (* Send rewards to one address *) + let send_one_reward (beneficiary_addr : address) : operation = + let contract_opt = + Tezos.get_contract_opt beneficiary_addr in + let beneficiary = + match contract_opt with + Some contract -> contract + | None -> (failwith "CONTRACT_NOT_FOUND" : unit contract) in + Tezos.Operation.transaction () 5tez beneficiary + + (* Send rewards to all beneficiaries *) + [@entry] + let send_rewards (_ : unit) (storage : storage) : operation list * storage = + if Tezos.get_sender () <> storage.owner + then failwith "Not the owner" + else let operations = List.map send_one_reward storage.beneficiaries in + operations, storage + + [@entry] + let change_owner (new_owner : address) (storage : storage) : operation list * storage = + (* Verify that the sender is the admin *) + let _ = if Tezos.get_sender () <> storage.owner then failwith "Not the owner" in + [], { storage with owner = new_owner } + +end +``` + + + + + +```jsligo group=rewardswithflaw +namespace RewardsWithFlaw { + + export type storage = { + owner: address, + beneficiaries: list
, + }; + + // Send rewards to one address + const send_one_reward = (beneficiary_addr: address): operation => { + const contract_opt = + Tezos.get_contract_opt( beneficiary_addr); + const beneficiary = $match(contract_opt, { + "Some": contract => contract, + "None": () => failwith("CONTRACT_NOT_FOUND"), + }); + return Tezos.Operation.transaction(unit, 5 as tez, beneficiary); + } + + // Send rewards to all beneficiaries + // @entry + const send_rewards = (_: unit, storage: storage): [list, storage] => { + if (Tezos.get_sender() != storage.owner) { + failwith("Not the owner"); + } + const operations = List.map(send_one_reward, storage.beneficiaries); + return [operations, storage]; + } + + // @entry + const change_owner = (new_owner: address, storage: storage): [list, storage] => { + // Verify that the sender is the admin + if (Tezos.get_sender() != storage.owner) { + failwith("Not the owner"); + } + return [[], { + owner: new_owner, + beneficiaries: storage.beneficiaries, + }]; + } + +} +``` + + + +When the owner calls the `send_rewards` entrypoint, the contract attempts to create a list of operations. +If one of these attempts to create an operation fails because the receiving contract fails or does not exist, the entire call to the `send_rewards` entrypoint fails and no transfers happen. +Regardless of whether this failure is because of a mistake or intentional censorship, the contract is stuck. + +In cases like these, allow users to withdraw funds independently instead of running operations in a batch. +This way, if one transfer fails, it does not affect other transfers. + +## Incorrect authorisation checks + +When developing a contract, you may want to restrict who can call a certain entrypoint. +In this case you must ensure that: + +- The request comes from an authorised entity +- The authorised entity cannot be tricked into sending the request + +To determine which account sends a request, you may be tempted to use the `Tezos.get_source` function. +This function returns the address of the account that submitted the operation that started a chain of operations, but not necessarily the account that sent the immediate transaction that the contract is running now. +Relying on `Tezos.get_source` can allow a malicious contract to impersonate an account when it calls another contract. + +For example, assume that account A calls smart contract B, which generates an operation to call smart contract C. +When C runs, in Tezos terms, account A is the *source* of the transaction and smart contract B is the *sender* of the transaction. +Therefore, if it uses `Tezos.get_source` to check which account calls it, contract B could trick it into thinking account A called it. +In this way, a malicious contract can invite accounts to make seemingly innocent transactions and use the operation chain to impersonate those users in other transactions. + +:::warning + +For this reason, contracts should never use `Tezos.get_source` for authorisation purposes. + +::: + +For more information about senders and sources, see [Sender vs Source confusion](https://docs.tezos.com/tutorials/security/part-1#sender-vs-source-confusion) on docs.tezos.com. + +Checking whether `Tezos.get_sender` (the address of the immediate caller) is authorised to perform an operation is better. +Because the request comes directly from an authorised entity, contracts can be more confident that the call is legitimate. +This approach is a good default choice if both conditions hold true: + +1. The sender contract is well secured against emitting arbitrary operations. +For instance, it must not contain a certain kind of ["view" entrypoints](https://gitlab.com/tezos/tzip/-/blob/master/proposals/tzip-4/tzip-4.md#view-entrypoints#view-entrypoints) as defined in [TZIP-4](https://gitlab.com/tzip/tzip/-/blob/master/proposals/tzip-4/tzip-4.md). +Ordinary views with the `@view` attribute/decorator do not have this vulnerability because they cannot create operations. + +2. You only need to authorise an immediate caller and not the contracts somewhere up in the call chain. + +If either of these conditions is not met, you may need to use [tickets](../data-types/tickets) to authenticate requests. +Tickets can be transferred, but they always have the address of the contract that created them as their ticketer. + +In this way, tickets allow you to verify that requests came from a certain contract. +For example, you can set up a contract that authenticates requests by requiring a ticket with the request, using the address of the ticketer to determine the permissions for the action, and using the data in the ticket as the parameters or input for the request. + +In general, sender-based authorisation is appropriate only for simple scenarios, such as when the contract has a single "owner" address controlled by a user account. +In more complex scenarios, ticket-based authorisation is often better. diff --git a/gitlab-pages/docs/advanced/src/attributes-decorators/import-module-with-private.mligo b/gitlab-pages/docs/advanced/src/attributes-decorators/import-module-with-private.mligo new file mode 100644 index 0000000000..7ce8f323db --- /dev/null +++ b/gitlab-pages/docs/advanced/src/attributes-decorators/import-module-with-private.mligo @@ -0,0 +1,12 @@ +module ModuleWithPrivate = + Gitlab_pages.Docs.Advanced.Src.Attributes_decorators.Module_with_private + +(* foo = 5167 = (123 * 42) + 1 *) +let foo = ModuleWithPrivate.f 123 + +(* + The following lines cause errors because g and stuff are private: + + let bad_1 = ModuleWithPrivate.g 123 + let bad_2 = ModuleWithPrivate.stuff +*) \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/attributes-decorators/module-with-private.mligo b/gitlab-pages/docs/advanced/src/attributes-decorators/module-with-private.mligo new file mode 100644 index 0000000000..c3ee9fa75a --- /dev/null +++ b/gitlab-pages/docs/advanced/src/attributes-decorators/module-with-private.mligo @@ -0,0 +1,4 @@ +(* This is gitlab-pages/docs/advanced/src/attributes-decorators/module-with-private.mligo *) +[@private] let stuff = 42 +[@private] let g x = x * stuff +let f x = (g x) + 1 \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/attributes-decorators/ungrouped.jsligo b/gitlab-pages/docs/advanced/src/attributes-decorators/ungrouped.jsligo new file mode 100644 index 0000000000..134b387535 --- /dev/null +++ b/gitlab-pages/docs/advanced/src/attributes-decorators/ungrouped.jsligo @@ -0,0 +1,11 @@ +type storage = int; +type result = [list, storage]; + +// @entry +// @no_mutation +const sub = (delta : int, store : storage) : result => + [[], store - delta] +namespace MathPi { + export const pi_millionth_numerator : int = 3141593 + export const pi_millionth_denominator : nat = 1000000 as nat +} \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/attributes-decorators/ungrouped.mligo b/gitlab-pages/docs/advanced/src/attributes-decorators/ungrouped.mligo new file mode 100644 index 0000000000..5ae5a852b0 --- /dev/null +++ b/gitlab-pages/docs/advanced/src/attributes-decorators/ungrouped.mligo @@ -0,0 +1,6 @@ +type storage = int +type result = operation list * storage + +[@entry] [@no_mutation] +let sub (delta : int) (store : storage) : result = + [], store - delta \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/code-injection/never.jsligo b/gitlab-pages/docs/advanced/src/code-injection/never.jsligo new file mode 100644 index 0000000000..5c4dbb3d0c --- /dev/null +++ b/gitlab-pages/docs/advanced/src/code-injection/never.jsligo @@ -0,0 +1,15 @@ +type parameter = + ["Increment", int] +| ["Extend", never]; + +type storage = int; + +// @entry +function main (action: parameter, store: storage) : [list, storage] { + let storage = + $match(action, { + "Increment": n => store + n, + "Extend": k => (Michelson`{ NEVER }` as ((n: never) => int))(k) + }); + return [[], storage]; +}; \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/global-constants/global_call.jsligo b/gitlab-pages/docs/advanced/src/global-constants/global_call.jsligo index 6c263805d9..ccb40a9e25 100644 --- a/gitlab-pages/docs/advanced/src/global-constants/global_call.jsligo +++ b/gitlab-pages/docs/advanced/src/global-constants/global_call.jsligo @@ -1,5 +1,5 @@ -const c : ((_p : int) => int) = Tezos.constant("expruCKsgmUZjC7k8NRcwbcGbFSuLHv5rUyApNd972MwArLuxEZQm2") +const c : (_p : int) => int = Tezos.constant("expruCKsgmUZjC7k8NRcwbcGbFSuLHv5rUyApNd972MwArLuxEZQm2") -@entry -let main = (_p : unit, s : int) : [list, int] => +// @entry +const main = (_p : unit, s : int) : [list, int] => [[], c(s)] \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/global-constants/global_call_2.jsligo b/gitlab-pages/docs/advanced/src/global-constants/global_call_2.jsligo index 53519280e0..29ee883ced 100644 --- a/gitlab-pages/docs/advanced/src/global-constants/global_call_2.jsligo +++ b/gitlab-pages/docs/advanced/src/global-constants/global_call_2.jsligo @@ -1,3 +1,3 @@ -@entry +// @entry const main = (p: string, s: int) : [list, int] => [ [], Tezos.constant("exprv547Y7U5wKLbQGmkDU9Coh5tKPzvEJjyUed7px9yGt9nrkELXf")([p, s]) ]; \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/global-constants/global_const.jsligo b/gitlab-pages/docs/advanced/src/global-constants/global_const.jsligo index 7a1a65f575..463d799042 100644 --- a/gitlab-pages/docs/advanced/src/global-constants/global_const.jsligo +++ b/gitlab-pages/docs/advanced/src/global-constants/global_const.jsligo @@ -1,6 +1,6 @@ const helper = ([s, x]: [string, int]) => String.length(s) + x * 3 + 2; -@entry +// @entry const main = (p: string, s: int) : [list, int] => [[], helper ([p, s])]; \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/global-constants/test_global.jsligo b/gitlab-pages/docs/advanced/src/global-constants/test_global.jsligo index 1bc55b8656..0e7d19176b 100644 --- a/gitlab-pages/docs/advanced/src/global-constants/test_global.jsligo +++ b/gitlab-pages/docs/advanced/src/global-constants/test_global.jsligo @@ -1,20 +1,20 @@ -namespace C { - type storage = int - type parameter = unit +type storage = int; +type parameter = unit; - const f = (x : int) => x * 3 + 2; +class C { + static f = (x : int) => x * 3 + 2; - const ct = Test.register_constant(Test.eval(f)); + static ct = Test.State.register_constant(Test.Michelson.eval(f)); @entry - const main = (p: parameter, s: storage) : [list, storage] => + main = (_p: parameter, s: storage) : [list, storage] => [[], Tezos.constant(ct)(s)]; } const _test = () => { - let orig = Test.originate(contract_of(C), 1, 0tez); - Test.transfer_exn(orig.addr, Main(unit), 0tez); - assert (Test.get_storage(orig.addr) == 5); + let orig = Test.Originate.contract(contract_of(C), 1, 0 as tez); + Test.Typed_address.transfer_exn(orig.taddr, ["Main" as "Main"], 0 as tez); + Assert.assert (Test.Typed_address.get_storage(orig.taddr) == 5); }; const test = _test(); \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/global-constants/test_global.mligo b/gitlab-pages/docs/advanced/src/global-constants/test_global.mligo index 051c5abaf0..99f5bbd350 100644 --- a/gitlab-pages/docs/advanced/src/global-constants/test_global.mligo +++ b/gitlab-pages/docs/advanced/src/global-constants/test_global.mligo @@ -5,14 +5,14 @@ module C = struct let f (x : int) = x * 3 + 2 - let ct = Test.register_constant (Test.eval f) + let ct = Test.State.register_constant (Test.Michelson.eval f) [@entry] let main (() : parameter) (store : storage) : return = - [], (Tezos.constant ct store) + [], Tezos.constant ct store end let test = - let orig = Test.originate (contract_of C) 1 0tez in - let _ = Test.transfer_exn orig.addr (Main ()) 0tez in - assert (Test.get_storage orig.addr = 5) \ No newline at end of file + let orig = Test.Originate.contract (contract_of C) 1 0tez in + let _ = Test.Typed_address.transfer_exn orig.taddr (Main ()) 0tez in + Assert.assert (Test.Typed_address.get_storage orig.taddr = 5) \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/include/included.mligo b/gitlab-pages/docs/advanced/src/include/included.mligo new file mode 100644 index 0000000000..70001fc421 --- /dev/null +++ b/gitlab-pages/docs/advanced/src/include/included.mligo @@ -0,0 +1,4 @@ +(* This is "included.mligo" *) +(* Demonstrate CameLIGO inclusion statements, see includer.mligo *) + +let foo = 144 \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/include/ungrouped.mligo b/gitlab-pages/docs/advanced/src/include/ungrouped.mligo new file mode 100644 index 0000000000..511fca0499 --- /dev/null +++ b/gitlab-pages/docs/advanced/src/include/ungrouped.mligo @@ -0,0 +1,3 @@ +#include "gitlab-pages/docs/advanced/src/include/included.mligo" + +let bar = foo \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/security/bank.jsligo b/gitlab-pages/docs/advanced/src/security/bank.jsligo new file mode 100644 index 0000000000..eeed144285 --- /dev/null +++ b/gitlab-pages/docs/advanced/src/security/bank.jsligo @@ -0,0 +1,26 @@ +namespace Bank { + + type storage = big_map; + type return_type = [list, storage]; + + // Return a withdrawal + // @entry + const withdraw = (tx_amount: tez, storage: storage): return_type => { + // Verify that the caller has enough balance for the withdrawal + const old_balance = Big_map.find(Tezos.get_sender(), storage); + if (tx_amount > old_balance) { + failwith("Insufficient balance"); + } + // Create transaction + const receiver_account = $match((Tezos.get_contract_opt(Tezos.get_sender())), { + "Some": account => account, + "None": () => failwith("Couldn't find account"), + }); + const operation = Tezos.Operation.transaction(unit, tx_amount, receiver_account); + // Update balance + const new_balance: tez = Option.value_with_error("Unreachable error; we already compared balance to amount", old_balance - tx_amount); + const new_storage = Big_map.update(Tezos.get_sender(), ["Some" as "Some", new_balance], storage); + return [[operation], new_storage]; + } + +} \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/security/bank.mligo b/gitlab-pages/docs/advanced/src/security/bank.mligo new file mode 100644 index 0000000000..8efd2dd04f --- /dev/null +++ b/gitlab-pages/docs/advanced/src/security/bank.mligo @@ -0,0 +1,17 @@ +type storage = (address, tez) big_map +type return_type = operation list * storage + +[@entry] +let withdraw (tx_amount : tez) (storage : storage) : return_type = + (* Verify that the caller has enough balance for the withdrawal *) + let old_balance = Big_map.find (Tezos.get_sender ()) storage in + let _ = if tx_amount > old_balance then failwith "Insufficient balance" in + (* Create transaction *) + let receiver_account = match Tezos.get_contract_opt (Tezos.get_sender ()) with + Some account -> account + | None -> failwith "Couldn't find account" in + let operation = Tezos.Operation.transaction unit tx_amount receiver_account in + (* Update balance *) + let new_balance : tez = Option.value_with_error "Unreachable error; we already compared balance to amount" (old_balance - tx_amount) in + let new_storage = Big_map.update (Tezos.get_sender ()) (Some new_balance) storage in + [operation], new_storage \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/security/rewardswithflaw.jsligo b/gitlab-pages/docs/advanced/src/security/rewardswithflaw.jsligo new file mode 100644 index 0000000000..01ba72fcd9 --- /dev/null +++ b/gitlab-pages/docs/advanced/src/security/rewardswithflaw.jsligo @@ -0,0 +1,41 @@ +namespace RewardsWithFlaw { + + export type storage = { + owner: address, + beneficiaries: list
, + }; + + // Send rewards to one address + const send_one_reward = (beneficiary_addr: address): operation => { + const contract_opt = + Tezos.get_contract_opt( beneficiary_addr); + const beneficiary = $match(contract_opt, { + "Some": contract => contract, + "None": () => failwith("CONTRACT_NOT_FOUND"), + }); + return Tezos.Operation.transaction(unit, 5 as tez, beneficiary); + } + + // Send rewards to all beneficiaries + // @entry + const send_rewards = (_: unit, storage: storage): [list, storage] => { + if (Tezos.get_sender() != storage.owner) { + failwith("Not the owner"); + } + const operations = List.map(send_one_reward, storage.beneficiaries); + return [operations, storage]; + } + + // @entry + const change_owner = (new_owner: address, storage: storage): [list, storage] => { + // Verify that the sender is the admin + if (Tezos.get_sender() != storage.owner) { + failwith("Not the owner"); + } + return [[], { + owner: new_owner, + beneficiaries: storage.beneficiaries, + }]; + } + +} \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/security/rewardswithflaw.mligo b/gitlab-pages/docs/advanced/src/security/rewardswithflaw.mligo new file mode 100644 index 0000000000..79507d3c26 --- /dev/null +++ b/gitlab-pages/docs/advanced/src/security/rewardswithflaw.mligo @@ -0,0 +1,32 @@ +module RewardsWithFlaw = struct + + type storage = { + owner : address; + beneficiaries : address list + } + + (* Send rewards to one address *) + let send_one_reward (beneficiary_addr : address) : operation = + let contract_opt = + Tezos.get_contract_opt beneficiary_addr in + let beneficiary = + match contract_opt with + Some contract -> contract + | None -> (failwith "CONTRACT_NOT_FOUND" : unit contract) in + Tezos.Operation.transaction () 5tez beneficiary + + (* Send rewards to all beneficiaries *) + [@entry] + let send_rewards (_ : unit) (storage : storage) : operation list * storage = + if Tezos.get_sender () <> storage.owner + then failwith "Not the owner" + else let operations = List.map send_one_reward storage.beneficiaries in + operations, storage + + [@entry] + let change_owner (new_owner : address) (storage : storage) : operation list * storage = + (* Verify that the sender is the admin *) + let _ = if Tezos.get_sender () <> storage.owner then failwith "Not the owner" in + [], { storage with owner = new_owner } + +end \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/security/walletwithflaw.jsligo b/gitlab-pages/docs/advanced/src/security/walletwithflaw.jsligo new file mode 100644 index 0000000000..baf41555a7 --- /dev/null +++ b/gitlab-pages/docs/advanced/src/security/walletwithflaw.jsligo @@ -0,0 +1,55 @@ +namespace WalletWithFlaw { + + // Variant for two types of transactions + type transaction = + ["Deposit", [address, tez]] + | ["Withdrawal", [address, tez]]; + + type storage = { + owner: address, + transactionLog: list, + }; + + type return_type = [list, storage]; + + // Receive a deposit + // @entry + const deposit = (_: unit, storage: storage): return_type => { + // Verify that tez was sent + if (Tezos.get_amount() == (0 as tez)) { + failwith("Send tez to deposit"); + } + // Add log entry + const newLogEntry: transaction = ["Deposit" as "Deposit", [Tezos.get_sender(), Tezos.get_amount()]]; + return [[], { + owner: storage.owner, + transactionLog: [newLogEntry, ...storage.transactionLog], + }]; + } + + // Return a withdrawal + // @entry + const withdraw = (param : [address, tez], storage : storage): return_type => { + const [tx_destination, tx_amount] = param; + // Verify that the sender is the admin + if (Tezos.get_sender() != storage.owner) { + failwith("Not the owner"); + } + // Verify that no tez was sent + if (Tezos.get_amount() != (0 as tez)) { + failwith("Don't send tez to this entrypoint"); + } + // Create transaction + const callee = Tezos.get_contract_opt(tx_destination); + const operation = $match(callee, { + "Some": contract => (() => Tezos.Operation.transaction(unit, tx_amount, contract))(), + "None": () => failwith("Couldn't send withdrawal to that address"), + }); + // Add log entry and return operation and new log + const newLogEntry: transaction = ["Withdrawal" as "Withdrawal", [tx_destination, tx_amount]]; + return [[operation], { + owner: storage.owner, + transactionLog: [newLogEntry, ...storage.transactionLog], + }]; + } +} \ No newline at end of file diff --git a/gitlab-pages/docs/advanced/src/security/walletwithflaw.mligo b/gitlab-pages/docs/advanced/src/security/walletwithflaw.mligo new file mode 100644 index 0000000000..78777dea02 --- /dev/null +++ b/gitlab-pages/docs/advanced/src/security/walletwithflaw.mligo @@ -0,0 +1,42 @@ +module WalletWithFlaw = struct + + (* Variant for two types of transactions *) + type transaction = + Deposit of address * tez + | Withdrawal of address * tez + + type storage = { + owner : address; + transactionLog : transaction list + } + + type return_type = operation list * storage + + (* Receive a deposit *) + [@entry] + let deposit (_ : unit) (storage : storage) : return_type = + (* Verify that tez was sent *) + let _ = if Tezos.get_amount () = 0tez then failwith "Send tez to deposit" in + (* Add log entry *) + let newLogEntry : transaction = Deposit (Tezos.get_sender (), Tezos.get_amount ()) in + [], { storage with transactionLog = newLogEntry :: storage.transactionLog } + + (* Return a withdrawal *) + [@entry] + let withdraw (tx_destination, tx_amount : address * tez) (storage : storage) : return_type = + (* Verify that the sender is the admin *) + let _ = if Tezos.get_sender () <> storage.owner then failwith "Not the owner" in + (* Verify that no tez was sent *) + let _ = if Tezos.get_amount () <> 0tez then failwith "Don't send tez to this entrypoint" in + (* Create transaction *) + let callee = Tezos.get_contract_opt tx_destination in + let operation = match callee with + Some contract -> + Tezos.Operation.transaction () tx_amount contract + | None -> failwith "Couldn't send withdrawal to that address" + in + (* Add log entry and return operation and new log *) + let newLogEntry : transaction = Withdrawal (tx_destination, tx_amount) in + [operation], { storage with transactionLog = newLogEntry :: storage.transactionLog } + +end \ No newline at end of file diff --git a/gitlab-pages/docs/api/cheat-sheet.md b/gitlab-pages/docs/api/cheat-sheet.md index ab9a06162c..d296a30d78 100644 --- a/gitlab-pages/docs/api/cheat-sheet.md +++ b/gitlab-pages/docs/api/cheat-sheet.md @@ -36,11 +36,12 @@ module C = struct end let testC = - let initial_storage = 42 in - let originated = Test.originate (contract_of C) initial_storage 0tez in - let p : C parameter_of = Increment 1 in - let _ = Test.transfer_exn originated.addr p 1mutez in - assert (Test.get_storage originated.addr = initial_storage + 1) + let initial_storage = 42 in + let originated = + Test.Originate.contract (contract_of C) initial_storage 0tez in + let p : C parameter_of = Increment 1 in + let _ = Test.Typed_address.transfer_exn originated.taddr p 1mutez in + Assert.assert (Test.Typed_address.get_storage originated.taddr = initial_storage + 1) ``` @@ -389,9 +390,8 @@ Transactions
```cameligo group=tezos_specific - let payment : operation = - Tezos.transaction unit 100mutez contract + Tezos.Operation.transaction unit 100mutez contract ``` @@ -513,32 +513,31 @@ Contract, view and test
```jsligo group=simple_contract_with_view_and_test -namespace C { - export type storage = int; +type storage = int; +class C { @entry - const increment = (action: int, store: storage) : [list , storage] => [[], store + action]; + increment = (action: int, store: storage) : [list , storage] => [[], store + action]; @entry - const decrement = (action: int, store: storage) : [list , storage] => [[], store - action]; + decrement = (action: int, store: storage) : [list , storage] => [[], store - action]; @view - const get_storage = (must_be_positive: bool, storage: int): int => { - if (must_be_positive && storage < 0) { + get_storage = (must_be_positive: bool, storage: int): int => { + if (must_be_positive && storage < 0) return failwith("Negative value in storage"); - } else { + else return storage; - } } }; -const testC = do { - let initial_storage = 42; - let originated = Test.originate(contract_of(C), initial_storage, 0tez); - let p : parameter_of C = Increment(1); - Test.transfer_exn(originated.addr, p, 1mutez); - return assert(Test.get_storage(originated.addr) == initial_storage + 1); -} +const testC = (() => { + const initial_storage = 42; + const originated = Test.Originate.contract(contract_of(C), initial_storage, 0 as tez); + const p : parameter_of = ["Increment" as "Increment", 1]; + Test.Typed_address.transfer_exn(originated.taddr, p, 1 as mutez); + return Assert.assert(Test.Typed_address.get_storage(originated.taddr) == initial_storage + 1); +})() ```
@@ -576,7 +575,7 @@ Natural numbers
```jsligo -const n: nat = 7n; +const n: nat = 7 as nat; ```
@@ -621,8 +620,8 @@ Mutez (micro tez)
```jsligo -const tez_amount: tez = 42tez -const tez_amount2: tez = tez_amount + 7mutez // == 42000007mutez +const tez_amount: tez = 42 as tez +const tez_amount2: tez = tez_amount + (7 as mutez) // == (42000007 as mutez) ```
@@ -664,7 +663,7 @@ Addition ```jsligo const add_int: int = 3 + 4; -const add_nat: nat = 3n + 4n; +const add_nat: nat = (3 as nat) + (4 as nat); ```
@@ -675,10 +674,10 @@ Multiplication & Division ```jsligo const mul_int: int = 3 * 4; -const mul_nat: nat = 3n * 4n; +const mul_nat: nat = (3 as nat) * (4 as nat); const div_int: int = 10 / 5; -const div_nat: nat = 10n / 5n; // can fail (division by zero), check your inputs first. +const div_nat: nat = (10 as nat) / (5 as nat); ``` @@ -722,11 +721,6 @@ type name = string Include (prefer import)
- -```jsligo skip -#include "library.jsligo" -``` -
Import (better) @@ -734,8 +728,6 @@ Import (better)
```jsligo skip -#import "library.jsligo" "MyLibrary" -const foo = MyLibrary.bar; ```
@@ -756,9 +748,9 @@ Functions (long form)
```jsligo group=b -const add = (a: int, b: int): int => { - let c = a; - let d = b; +function add (a: int, b: int): int { + const c = a; + const d = b; return c + d }; ``` @@ -771,7 +763,7 @@ If/else Statement ```jsligo function if_statement (age : nat): string { - if (age >= 16n) return "yes" else return "no" + if (age >= (16 as nat)) return "yes"; else return "no"; } ``` @@ -783,8 +775,8 @@ Options ```jsligo type middle_name = option; -const a_middle_name : middle_name = Some("Foo"); -const no_middle_name : middle_name = None(); +const a_middle_name : middle_name = ["Some" as "Some", "Foo"]; +const no_middle_name : middle_name = ["None" as "None"]; ```
@@ -827,12 +819,13 @@ Matching on variant cases
```jsligo group=variants -let a: action = Increment(5) -const result: int = match(a) { - when(Increment(n)): n + 1; - when(Decrement(n)): n - 1; - when(Reset()): 0; -} +const a: action = ["Increment" as "Increment", 5]; +const result: int = + $match(a, { + "Increment": n => n + 1, + "Decrement": n => n - 1, + "Reset": () => 0 + }); ```
@@ -865,14 +858,15 @@ Maps type prices = map; const prices: prices = Map.literal([ - [10n, 60mutez], - [50n, 30mutez], - [100n, 10mutez] + [10 as nat, 60 as mutez], + [50 as nat, 30 as mutez], + [100 as nat, 10 as mutez] ]); -const price: option = Map.find_opt(50n, prices) +const price: option = Map.find_opt(50 as nat, prices) -const prices2: prices = Map.update(200n, Some (5mutez), prices) +const prices2: prices = + Map.update(200 as nat, ["Some" as "Some", 5 as mutez], prices) ```
@@ -885,10 +879,10 @@ Contracts & Accounts const destinationAddress: address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; const contract : contract = - match(Tezos.get_contract_opt(Tezos.get_sender()) as option>) { - when(Some(contract)): contract; - when(None()): failwith("no contract or wrong contract type") - } + $match(Tezos.get_contract_opt(Tezos.get_sender()) as option>, { + "Some": contract => contract, + "None": () => failwith("no contract or wrong contract type") + }) ``` @@ -899,7 +893,7 @@ Transactions ```jsligo group=tezos_specific const payment: operation = - Tezos.transaction(unit, 100mutez, contract); + Tezos.Operation.transaction(unit, 100 as mutez, contract); ``` @@ -921,7 +915,7 @@ Comb layout (default) ```jsligo type animal = -@layout("comb") +// @layout("comb") | ["Elephant"] | ["Dog"] | ["Cat"]; @@ -935,7 +929,7 @@ Tree layout ```jsligo type animal = -@layout("tree") +// @layout("tree") | ["Elephant"] | ["Dog"] | ["Cat"]; @@ -950,8 +944,10 @@ Namespace (auto-inferred type) ```jsligo namespace FA0_inferred { type storage = int; - @entry const add = (s : int, k : int) : [list, int] => [[], s + k]; - @entry const extra = (s : int, k : int) : [list, int] => [[], s - k]; + // @entry + const add = (s : int, k : int) : [list, int] => [[], s + k]; + // @entry + const extra = (s : int, k : int) : [list, int] => [[], s - k]; } ``` @@ -962,47 +958,43 @@ Interface
```jsligo +type storage = int; + interface FA0_INTF { - type storage; - @entry const add : (s : int, k : storage) => [list, storage]; + //@entry + add : (s : int, k : storage) => [list, storage]; } ```
-Extending Interface +Extending an interface
```jsligo interface FA0_EXT_INTF extends FA0_INTF { - type storage; - @entry const add : (s : int, k : storage) => [list, storage]; + // @entry + add : (s : int, k : storage) => [list, storage]; } ```
-Namespace impmlementing +Implementing an interface
```jsligo -namespace FA0 implements FA0_INTF { - export type storage = int; - @entry const add = (s : int, k : int) : [list, int] => [[], s + k]; - @entry const extra = (s : int, k : int) : [list, int] => [[], s - k]; +class FA0 implements FA0_INTF { + @entry + add = (s : int, k : int) : [list, int] => [[], s + k]; + + @entry + extra = (s : int, k : int) : [list, int] => [[], s - k]; } ``` -
-
-Extending namespace -
-
- -Not available in JsLIGO, use CameLIGO. -
diff --git a/gitlab-pages/docs/api/src/cheat-sheet/b.jsligo b/gitlab-pages/docs/api/src/cheat-sheet/b.jsligo index c51755e5ed..dd13a87175 100644 --- a/gitlab-pages/docs/api/src/cheat-sheet/b.jsligo +++ b/gitlab-pages/docs/api/src/cheat-sheet/b.jsligo @@ -1,7 +1,7 @@ type age = int type name = string -const add = (a: int, b: int): int => { - let c = a; - let d = b; +function add (a: int, b: int): int { + const c = a; + const d = b; return c + d }; \ No newline at end of file diff --git a/gitlab-pages/docs/api/src/cheat-sheet/simple_contract_with_view_and_test.jsligo b/gitlab-pages/docs/api/src/cheat-sheet/simple_contract_with_view_and_test.jsligo index 82831075f6..b1c4494502 100644 --- a/gitlab-pages/docs/api/src/cheat-sheet/simple_contract_with_view_and_test.jsligo +++ b/gitlab-pages/docs/api/src/cheat-sheet/simple_contract_with_view_and_test.jsligo @@ -1,26 +1,25 @@ -namespace C { - export type storage = int; +type storage = int; +class C { @entry - const increment = (action: int, store: storage) : [list , storage] => [[], store + action]; + increment = (action: int, store: storage) : [list , storage] => [[], store + action]; @entry - const decrement = (action: int, store: storage) : [list , storage] => [[], store - action]; + decrement = (action: int, store: storage) : [list , storage] => [[], store - action]; @view - const get_storage = (must_be_positive: bool, storage: int): int => { - if (must_be_positive && storage < 0) { + get_storage = (must_be_positive: bool, storage: int): int => { + if (must_be_positive && storage < 0) return failwith("Negative value in storage"); - } else { + else return storage; - } } }; -const testC = do { - let initial_storage = 42; - let originated = Test.originate(contract_of(C), initial_storage, 0tez); - let p : parameter_of C = Increment(1); - Test.transfer_exn(originated.addr, p, 1mutez); - return assert(Test.get_storage(originated.addr) == initial_storage + 1); -} \ No newline at end of file +const testC = (() => { + const initial_storage = 42; + const originated = Test.Originate.contract(contract_of(C), initial_storage, 0 as tez); + const p : parameter_of = ["Increment" as "Increment", 1]; + Test.Typed_address.transfer_exn(originated.taddr, p, 1 as mutez); + return Assert.assert(Test.Typed_address.get_storage(originated.taddr) == initial_storage + 1); +})() \ No newline at end of file diff --git a/gitlab-pages/docs/api/src/cheat-sheet/simple_contract_with_view_and_test.mligo b/gitlab-pages/docs/api/src/cheat-sheet/simple_contract_with_view_and_test.mligo index ae64ed2c04..d986c2d0ca 100644 --- a/gitlab-pages/docs/api/src/cheat-sheet/simple_contract_with_view_and_test.mligo +++ b/gitlab-pages/docs/api/src/cheat-sheet/simple_contract_with_view_and_test.mligo @@ -18,8 +18,9 @@ module C = struct end let testC = - let initial_storage = 42 in - let originated = Test.originate (contract_of C) initial_storage 0tez in - let p : C parameter_of = Increment 1 in - let _ = Test.transfer_exn originated.addr p 1mutez in - assert (Test.get_storage originated.addr = initial_storage + 1) \ No newline at end of file + let initial_storage = 42 in + let originated = + Test.Originate.contract (contract_of C) initial_storage 0tez in + let p : C parameter_of = Increment 1 in + let _ = Test.Typed_address.transfer_exn originated.taddr p 1mutez in + Assert.assert (Test.Typed_address.get_storage originated.taddr = initial_storage + 1) \ No newline at end of file diff --git a/gitlab-pages/docs/api/src/cheat-sheet/tezos_specific.jsligo b/gitlab-pages/docs/api/src/cheat-sheet/tezos_specific.jsligo index c31beb547e..38142fd2b3 100644 --- a/gitlab-pages/docs/api/src/cheat-sheet/tezos_specific.jsligo +++ b/gitlab-pages/docs/api/src/cheat-sheet/tezos_specific.jsligo @@ -1,9 +1,9 @@ const destinationAddress: address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; const contract : contract = - match(Tezos.get_contract_opt(Tezos.get_sender()) as option>) { - when(Some(contract)): contract; - when(None()): failwith("no contract or wrong contract type") - } + $match(Tezos.get_contract_opt(Tezos.get_sender()) as option>, { + "Some": contract => contract, + "None": () => failwith("no contract or wrong contract type") + }) const payment: operation = - Tezos.transaction(unit, 100mutez, contract); \ No newline at end of file + Tezos.Operation.transaction(unit, 100 as mutez, contract); \ No newline at end of file diff --git a/gitlab-pages/docs/api/src/cheat-sheet/tezos_specific.mligo b/gitlab-pages/docs/api/src/cheat-sheet/tezos_specific.mligo index dbffb338df..d2e5d6c071 100644 --- a/gitlab-pages/docs/api/src/cheat-sheet/tezos_specific.mligo +++ b/gitlab-pages/docs/api/src/cheat-sheet/tezos_specific.mligo @@ -5,6 +5,5 @@ let contract : unit contract = match (Tezos.get_contract_opt (Tezos.get_sender ()) : unit contract option) with Some contract -> contract | None -> (failwith "no contract" : unit contract) - let payment : operation = - Tezos.transaction unit 100mutez contract + Tezos.Operation.transaction unit 100mutez contract diff --git a/gitlab-pages/docs/api/src/cheat-sheet/ungrouped.jsligo b/gitlab-pages/docs/api/src/cheat-sheet/ungrouped.jsligo index 75808a77ad..130721b168 100644 --- a/gitlab-pages/docs/api/src/cheat-sheet/ungrouped.jsligo +++ b/gitlab-pages/docs/api/src/cheat-sheet/ungrouped.jsligo @@ -1,7 +1,7 @@ const name: string = "Tezos"; const t: string = "t"; const i: int = 42; -const n: nat = 7n; +const n: nat = 7 as nat; const u: unit = unit; const has_drivers_license: bool = false const adult: bool = true @@ -10,19 +10,19 @@ const booleanLogic: bool = false == (false && true) == (false || false) -const tez_amount: tez = 42tez -const tez_amount2: tez = tez_amount + 7mutez // == 42000007mutez +const tez_amount: tez = 42 as tez +const tez_amount2: tez = tez_amount + (7 as mutez) // == (42000007 as mutez) const tz1address: address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; const kt1address: address = "KT1JepfBfMSqkQyf9B1ndvURghGsSB8YCLMD"; const my_str : string = "Hello World!"; const verbatim_str : string = `verbatim string`; const add_int: int = 3 + 4; -const add_nat: nat = 3n + 4n; +const add_nat: nat = (3 as nat) + (4 as nat); const mul_int: int = 3 * 4; -const mul_nat: nat = 3n * 4n; +const mul_nat: nat = (3 as nat) * (4 as nat); const div_int: int = 10 / 5; -const div_nat: nat = 10n / 5n; // can fail (division by zero), check your inputs first. +const div_nat: nat = (10 as nat) / (5 as nat); const mod_nat: nat = 10 % 3; // can fail (division by zero), check your inputs first. type name = [string, string]; @@ -33,11 +33,11 @@ const lastName: string = winner[1]; const add = (a: int, b: int): int => a + b; function if_statement (age : nat): string { - if (age >= 16n) return "yes" else return "no" + if (age >= (16 as nat)) return "yes"; else return "no"; } type middle_name = option; -const a_middle_name : middle_name = Some("Foo"); -const no_middle_name : middle_name = None(); +const a_middle_name : middle_name = ["Some" as "Some", "Foo"]; +const no_middle_name : middle_name = ["None" as "None"]; const age: int = 5 const someAddress: address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; type person = { @@ -54,41 +54,48 @@ const name_: string = john.name type prices = map; const prices: prices = Map.literal([ - [10n, 60mutez], - [50n, 30mutez], - [100n, 10mutez] + [10 as nat, 60 as mutez], + [50 as nat, 30 as mutez], + [100 as nat, 10 as mutez] ]); -const price: option = Map.find_opt(50n, prices) +const price: option = Map.find_opt(50 as nat, prices) -const prices2: prices = Map.update(200n, Some (5mutez), prices) +const prices2: prices = + Map.update(200 as nat, ["Some" as "Some", 5 as mutez], prices) const fail = (u: unit) : unit => failwith("a failure message") type animal = -@layout("comb") +// @layout("comb") | ["Elephant"] | ["Dog"] | ["Cat"]; type animal = -@layout("tree") +// @layout("tree") | ["Elephant"] | ["Dog"] | ["Cat"]; namespace FA0_inferred { type storage = int; - @entry const add = (s : int, k : int) : [list, int] => [[], s + k]; - @entry const extra = (s : int, k : int) : [list, int] => [[], s - k]; + // @entry + const add = (s : int, k : int) : [list, int] => [[], s + k]; + // @entry + const extra = (s : int, k : int) : [list, int] => [[], s - k]; } +type storage = int; + interface FA0_INTF { - type storage; - @entry const add : (s : int, k : storage) => [list, storage]; + //@entry + add : (s : int, k : storage) => [list, storage]; } interface FA0_EXT_INTF extends FA0_INTF { - type storage; - @entry const add : (s : int, k : storage) => [list, storage]; + // @entry + add : (s : int, k : storage) => [list, storage]; } -namespace FA0 implements FA0_INTF { - export type storage = int; - @entry const add = (s : int, k : int) : [list, int] => [[], s + k]; - @entry const extra = (s : int, k : int) : [list, int] => [[], s - k]; +class FA0 implements FA0_INTF { + @entry + add = (s : int, k : int) : [list, int] => [[], s + k]; + + @entry + extra = (s : int, k : int) : [list, int] => [[], s - k]; } \ No newline at end of file diff --git a/gitlab-pages/docs/api/src/cheat-sheet/variants.jsligo b/gitlab-pages/docs/api/src/cheat-sheet/variants.jsligo index b8dc09c60b..5e9dd49ddb 100644 --- a/gitlab-pages/docs/api/src/cheat-sheet/variants.jsligo +++ b/gitlab-pages/docs/api/src/cheat-sheet/variants.jsligo @@ -2,9 +2,10 @@ type action = ["Increment", int] | ["Decrement", int] | ["Reset"]; -let a: action = Increment(5) -const result: int = match(a) { - when(Increment(n)): n + 1; - when(Decrement(n)): n - 1; - when(Reset()): 0; -} \ No newline at end of file +const a: action = ["Increment" as "Increment", 5]; +const result: int = + $match(a, { + "Increment": n => n + 1, + "Decrement": n => n - 1, + "Reset": () => 0 + }); \ No newline at end of file diff --git a/gitlab-pages/docs/compiling/compiling.md b/gitlab-pages/docs/compiling/compiling.md index e83e244b8c..ca27dd25e2 100644 --- a/gitlab-pages/docs/compiling/compiling.md +++ b/gitlab-pages/docs/compiling/compiling.md @@ -110,17 +110,18 @@ namespace ComplexStorage { y: int, z: int, }; + type my_labels = { a: string, b: string, c: string, }; + type storage = [my_record, my_labels, address]; type return_type = [list, storage]; - @entry + // @entry const noop = (_u: unit, storage: storage): return_type => [[], storage]; - } ``` @@ -198,15 +199,15 @@ namespace ComplexParam { const sum_fold = ([result, i]: [int, int]): int => result + i; const mult_fold = ([result, i]: [int, int]): int => result * i; - @entry - const complexmath = (param: paramType, _s: storage): returnType => { + // @entry + function complexmath (param: paramType, _s: storage): returnType { const [list1, list2, str_tuple] = param; const sum: int = List.fold(sum_fold, list1, 0); const product: int = List.fold(mult_fold, list2, 1); const [str1, str2] = str_tuple; const string_diff: int = String.length(str2) - String.length(str1); const newVal = (sum + product) * string_diff; - return [list([]), newVal]; + return [[], newVal]; } } @@ -215,7 +216,8 @@ namespace ComplexParam { To compile a parameter to call this contract, create a JsLIGO expression for the parameter and pass it and the entrypoint name to the `ligo compile parameter` command, as in this example: ```bash -ligo compile parameter ComplexParam.jsligo -m ComplexParam 'Complexmath([[1, 2, 3], [2, 2, 1], ["three", "fifteen"]])' +ligo compile parameter ComplexParam.jsligo -m ComplexParam +'["Complexmath" as "Complexmath", ([[1, 2, 3], [2, 2, 1], ["three", "fifteen"]])]' ``` @@ -237,3 +239,128 @@ octez-client transfer 0 from my_wallet to ComplexParam \ ``` For more information about the `ligo compile parameter` command, see [`compile parameter`](../manpages/compile%20parameter). + +## Compiling expressions + +The `ligo compile expression` command is more general than the `ligo compile parameter` command because it does not take any source file into account. +Instead, it compiles a single LIGO expression to Michelson using only the information you provide to the command, which means that you must provide information like type annotations and the syntax to use. + +One use of this command is to compile the parameter for a view, which you can not do with the `ligo compile parameter` command. +For example, this contract has a view that accepts a complex parameter with multiple numbers and does some math on them: + + + +```cameligo group=complexview +type storage_type = int +type return_type = operation list * storage_type + +type view_param = + int list + * int set + * int option + +module Counter = struct + + [@entry] + let add (value : int) (storage : storage_type) : return_type = + [], storage + value + + [@entry] + let sub (value : int) (storage : storage_type) : return_type = + [], storage - value + + [@view] + let math (param : view_param) (storage : storage_type) : int = + let intList, intSet, intOption = param in + + (* Get the sum of the ints in the list *) + let listSum = List.fold_left (fun (a, b) -> a + b) 0 intList in + + (* Multiply by the sum of the ints in the set *) + let setSum = Set.fold (fun (a, b) -> a + b) intSet 0 in + let returnValue = listSum * setSum in + + (* If the option int was provided, subtract it *) + match intOption with + None -> storage + returnValue + | Some value -> storage + returnValue - value +end +``` + + + + + +```jsligo group=complexview +type storage_type = int; +type return_type = [list, storage_type]; + +type view_param = [ + list, + [int, int, int], + option, +]; + +class Counter { + + @entry + add = (value: int, storage: storage_type): return_type => + [[], storage + value]; + + @entry + sub = (value: int, storage: storage_type): return_type => + [[], storage - value]; + + @view + math = (param: view_param, storage: storage_type): int => { + const [intList, intSet, intOption] = param; + + // Get the sum of the ints in the list + let listSum = 0; + for (const i of intList) listSum = listSum + i; + + // Multiply by the sum of the ints in the set + const [a, b, c] = intSet; + const setSum = a + b + c; + let returnValue = listSum * setSum; + + // If the option int was provided, subtract it + return $match(intOption, { + "None": () => storage + returnValue, + "Some": (value) => storage + returnValue - value, + }); + } +} +``` + + + +To compile the parameter to pass to the view, create an expression that matches the parameter for the view, including type annotations to ensure that the command compiles it to the correct types, as in this example: + + + +```bash +ligo2 compile expression cameligo "([1; 2; 3; 4] : int list), (Set.literal [1; 2; 3]), Some 2" +``` + + + + + +```bash +ligo compile expression jsligo "[([1, 2, 3, 4]: list), Set.literal([1, 2, 3]), [\"Some\" as \"Some\", 2]]" +``` + + + +The result is a Michelson expression that is the parameter for the view: + +```michelson +(Pair { 1 ; 2 ; 3 ; 4 } { 1 ; 2 ; 3 } (Some 2)) +``` + +You can use this value as the parameter to call the view, as in this example: + +```bash +octez-client run view math on contract counter with input "(Pair { 1 ; 2 ; 3 ; 4 } { 1 ; 2 ; 3 } (Some 2))" +``` diff --git a/gitlab-pages/docs/compiling/preprocessor.md b/gitlab-pages/docs/compiling/preprocessor.md index f1902f5732..c52de3fd8b 100644 --- a/gitlab-pages/docs/compiling/preprocessor.md +++ b/gitlab-pages/docs/compiling/preprocessor.md @@ -6,14 +6,27 @@ title: Preprocessor import Syntax from '@theme/Syntax'; The preprocessor edits files before they go to the LIGO compiler. -You can include commands called _preprocessor directives_ to instruct the preprocessor to make changes to a file before the compiler receives it, such as including or excluding code and importing code from other files. + + + +The JsLIGO compiler no longer supports preprocessor directives. + +- Instead of using the `#include` or `#import` directives, import namespaces in other files directly with the `import` keyword as described in [Importing and using classes](../syntax/classes#importing-and-using-classes) or [Importing namespaces](../syntax/modules#importing-namespaces). + +- The `#if`, `#else`, `#elif`, `#endif`, `#define`, `#undef`, and `#error` directives are no longer supported. +If you need to continue using them, you can run your JsLIGO code through a C++ preprocessor, which uses the same syntax. +JsLIGO code with these directives does not compile. + + + + + +CameLIGO code can include commands called _preprocessor directives_ to instruct the preprocessor to make changes to a file before the compiler receives it, such as including or excluding code and importing code from other files. Preprocessor directives can allow you to make changes to files before the compiler processes them. For example, the following contract has three entrypoints, but one is between `#if` and `#endif` directives. The line `#if INCLUDE_RESET` instructs the preprocessor to include the text between the directives (in this case, the third entrypoint) only if the `INCLUDE_RESET` Boolean variable is set: - - ```cameligo group=includereset module MyContract = struct type storage = int @@ -29,29 +42,6 @@ module MyContract = struct end ``` - - - - -```jsligo group=includereset -export namespace MyContract { - export type storage = int; - export type result = [list, storage]; - - @entry const increment = (delta : int, storage : storage) : result => [[], storage + delta]; - - @entry const decrement = (delta : int, storage : storage) : result => [[], storage - delta]; - - #if INCLUDE_RESET - @entry const reset = (_u : unit, _storage : storage) : result => [[], 0]; - #endif -} -``` - - - - - You can set these Boolean preprocessor variables with the [`#define`](#define-and-undef) directive or by passing them to the `-D` argument of the `ligo compile contract` command. For example, if the contract in the previous example is in a file named `mycontract.mligo`, this command causes the preprocessor and compiler to output a contract with only two entrypoints: @@ -67,23 +57,6 @@ ligo compile contract -D INCLUDE_RESET mycontract.mligo - - -You can set these Boolean preprocessor variables with the [`#define`](#define-and-undef) directive or by passing them to the `-D` argument of the `ligo compile contract` command. -For example, if the contract in the previous example is in a file named `mycontract.jsligo`, this command causes the preprocessor and compiler to output a contract with only two entrypoints: - -```bash -ligo compile contract mycontract.jsligo -``` - -This command passes the `INCLUDE_RESET` Boolean variable to the preprocessor and causes the compiler to output a contract with three entrypoints: - -```bash -ligo compile contract -D INCLUDE_RESET mycontract.jsligo -``` - - - ## Viewing the preprocessor output It's rarely necessary to view the output of the preprocessor, but if you need to see the output to debug directives, you can view the output with the `ligo print preprocessed` command, as in this example: @@ -104,6 +77,8 @@ ligo print preprocessed myContract.jsligo + + ## Comments The preprocessor ignores directives that are in [comments](../syntax/comments), which prevents problems where comments in your code contain text that looks like a directive. @@ -123,26 +98,12 @@ The preprocessor ignores directives that are in strings, which prevents problems For example, this code includes a string with the text `#endif`, but the preprocessor does not interpret this text as the `#endif` directive: - - ```cameligo skip #if true let textValue = "This string includes the text #endif" #endif ``` - - - - -```jsligo skip -#if true -const textValue = "This string includes the text #endif"; -#endif -``` - - - ## Blank lines When the preprocessor hides text, it includes a blank line consisting only of a newline character in place of the omitted line to keep line numbers in compiler errors consistent with the source file. @@ -216,7 +177,7 @@ The LIGO compiler ignores these linemarkers when it compiles the code. ## Directives -These are the preprocessor directives that the LIGO preprocessor supports: +These are the preprocessor directives that the CameLIGO preprocessor supports: - [`#define` and `#undef`](#define-and-undef) - [`#error`](#error) @@ -304,8 +265,6 @@ type storage = ### `#import` - - The `#import` directive prompts the preprocessor to include another file as a [module](../syntax/modules) in the current file. For example, you can create a file with related type definitions, as in this example file named `euro.mligo`: @@ -322,7 +281,7 @@ let two : t = 2n In another file, you can import this file, assign it the module `Euro`, and use its definitions: ```cameligo group=main_importer -#import "gitlab-pages/docs/compiling/src/preprocessor/euro.mligo" "Euro" +module Euro = Gitlab_pages.Docs.Compiling.Src.Preprocessor.Euro type storage = Euro.t @@ -331,86 +290,14 @@ let tip (s : storage) : storage = Euro.add (s, Euro.one) For more information, see [Modules](../syntax/modules). - - - - -The `#import` directive prompts the preprocessor to include another file as a [namespace](../syntax/modules) in the current file. - -For example, you can create a file with related type definitions, as in this example file named `euro.jsligo`: - -```jsligo group=euro -export type t = nat; - -export const add = (a: t, b: t): t => a + b; - -export const one: t = 1n; -export const two: t = 2n; -``` - -In another file, you can import this file, assign it the namespace `Euro`, and use its definitions: - -```jsligo group=main_importer -#import "gitlab-pages/docs/compiling/src/preprocessor/euro.jsligo" "Euro" - -type storage = Euro.t; - -const tip = (s : storage) : storage => - Euro.add (s, Euro.one); -``` - -When you import a file with the `#import` directive, LIGO packages the file as a namespace. -Therefore, any namespaces in the file are sub-namespaces of that namespace. - -However, the namespace does not export those sub-namespaces automatically. -As a result, if you import a file that contains namespaces, those namespaces are not accessible. - -To work around this limitation, add the `@public` decorator to the namespaces in the file. -For example, this file defines the Euro type as a namespace with the `@public` decorator: - -```jsligo group=euro_namespace_public -// This file is gitlab-pages/docs/preprocessor/src/import/euro_namespace_public.jsligo - -@public -namespace Euro { - export type t = nat; - export const add = (a: t, b: t) : t => a + b; - export const one: t = 1n; - export const two: t = 2n; -}; -``` - -Because the namespace is public, you can access it as a sub-namespace when you import the file into another file: - -```jsligo group=import_euro_public -#import "gitlab-pages/docs/compiling/src/preprocessor/euro_namespace_public.jsligo" "Euro_import" - -type euro_balance = Euro_import.Euro.t; - -const add_tip = (s: euro_balance): euro_balance => - Euro_import.Euro.add(s, Euro_import.Euro.one); -``` - -For more information, see [Namespaces](../syntax/modules). - - - ### `#include` The `#include` directive includes the entire text contents of the specified file, as in this example: ``` -#include "path/to/standard_1.ligo" +#include "path/to/standard_1.mligo" ``` - - Unlike the `#import` directive, the `#include` directive does not package the included file as a module. - - - -Unlike the `#import` directive, the `#include` directive does not package the included file as a namespace. - - diff --git a/gitlab-pages/docs/compiling/src/compiling/compile_param.jsligo b/gitlab-pages/docs/compiling/src/compiling/compile_param.jsligo index f9284d2de6..c5ddb934ea 100644 --- a/gitlab-pages/docs/compiling/src/compiling/compile_param.jsligo +++ b/gitlab-pages/docs/compiling/src/compiling/compile_param.jsligo @@ -6,15 +6,15 @@ namespace ComplexParam { const sum_fold = ([result, i]: [int, int]): int => result + i; const mult_fold = ([result, i]: [int, int]): int => result * i; - @entry - const complexmath = (param: paramType, _s: storage): returnType => { + // @entry + function complexmath (param: paramType, _s: storage): returnType { const [list1, list2, str_tuple] = param; const sum: int = List.fold(sum_fold, list1, 0); const product: int = List.fold(mult_fold, list2, 1); const [str1, str2] = str_tuple; const string_diff: int = String.length(str2) - String.length(str1); const newVal = (sum + product) * string_diff; - return [list([]), newVal]; + return [[], newVal]; } } \ No newline at end of file diff --git a/gitlab-pages/docs/compiling/src/compiling/compile_storage.jsligo b/gitlab-pages/docs/compiling/src/compiling/compile_storage.jsligo index 4f8a6589c7..a914779760 100644 --- a/gitlab-pages/docs/compiling/src/compiling/compile_storage.jsligo +++ b/gitlab-pages/docs/compiling/src/compiling/compile_storage.jsligo @@ -4,15 +4,16 @@ namespace ComplexStorage { y: int, z: int, }; + type my_labels = { a: string, b: string, c: string, }; + type storage = [my_record, my_labels, address]; type return_type = [list, storage]; - @entry + // @entry const noop = (_u: unit, storage: storage): return_type => [[], storage]; - } \ No newline at end of file diff --git a/gitlab-pages/docs/compiling/src/compiling/complexview.jsligo b/gitlab-pages/docs/compiling/src/compiling/complexview.jsligo new file mode 100644 index 0000000000..3002e7503d --- /dev/null +++ b/gitlab-pages/docs/compiling/src/compiling/complexview.jsligo @@ -0,0 +1,39 @@ +type storage_type = int; +type return_type = [list, storage_type]; + +type view_param = [ + list, + [int, int, int], + option, +]; + +class Counter { + + @entry + add = (value: int, storage: storage_type): return_type => + [[], storage + value]; + + @entry + sub = (value: int, storage: storage_type): return_type => + [[], storage - value]; + + @view + math = (param: view_param, storage: storage_type): int => { + const [intList, intSet, intOption] = param; + + // Get the sum of the ints in the list + let listSum = 0; + for (const i of intList) listSum = listSum + i; + + // Multiply by the sum of the ints in the set + const [a, b, c] = intSet; + const setSum = a + b + c; + let returnValue = listSum * setSum; + + // If the option int was provided, subtract it + return $match(intOption, { + "None": () => storage + returnValue, + "Some": (value) => storage + returnValue - value, + }); + } +} \ No newline at end of file diff --git a/gitlab-pages/docs/compiling/src/compiling/complexview.mligo b/gitlab-pages/docs/compiling/src/compiling/complexview.mligo new file mode 100644 index 0000000000..b4fd09df3c --- /dev/null +++ b/gitlab-pages/docs/compiling/src/compiling/complexview.mligo @@ -0,0 +1,34 @@ +type storage_type = int +type return_type = operation list * storage_type + +type view_param = + int list + * int set + * int option + +module Counter = struct + + [@entry] + let add (value : int) (storage : storage_type) : return_type = + [], storage + value + + [@entry] + let sub (value : int) (storage : storage_type) : return_type = + [], storage - value + + [@view] + let math (param : view_param) (storage : storage_type) : int = + let intList, intSet, intOption = param in + + (* Get the sum of the ints in the list *) + let listSum = List.fold_left (fun (a, b) -> a + b) 0 intList in + + (* Multiply by the sum of the ints in the set *) + let setSum = Set.fold (fun (a, b) -> a + b) intSet 0 in + let returnValue = listSum * setSum in + + (* If the option int was provided, subtract it *) + match intOption with + None -> storage + returnValue + | Some value -> storage + returnValue - value +end \ No newline at end of file diff --git a/gitlab-pages/docs/compiling/src/compiling/ungrouped.mligo b/gitlab-pages/docs/compiling/src/compiling/ungrouped.mligo new file mode 100644 index 0000000000..c3112363b3 --- /dev/null +++ b/gitlab-pages/docs/compiling/src/compiling/ungrouped.mligo @@ -0,0 +1 @@ +ligo2 compile expression cameligo "([1; 2; 3; 4] : int list), (Set.literal [1; 2; 3]), Some 2" \ No newline at end of file diff --git a/gitlab-pages/docs/compiling/src/preprocessor/main_importer.mligo b/gitlab-pages/docs/compiling/src/preprocessor/main_importer.mligo index 6e9c14a81f..d9378e2d5c 100644 --- a/gitlab-pages/docs/compiling/src/preprocessor/main_importer.mligo +++ b/gitlab-pages/docs/compiling/src/preprocessor/main_importer.mligo @@ -1,4 +1,4 @@ -#import "gitlab-pages/docs/compiling/src/preprocessor/euro.mligo" "Euro" +module Euro = Gitlab_pages.Docs.Compiling.Src.Preprocessor.Euro type storage = Euro.t diff --git a/gitlab-pages/docs/data-types/big_maps.md b/gitlab-pages/docs/data-types/big_maps.md index 5088a722d6..f85ea0fc97 100644 --- a/gitlab-pages/docs/data-types/big_maps.md +++ b/gitlab-pages/docs/data-types/big_maps.md @@ -5,44 +5,27 @@ id: big_maps import Syntax from '@theme/Syntax'; -Ordinary maps are fine for contracts with a finite lifespan or a -bounded number of users. For many contracts however, the intention is -to have a map holding *many* entries, potentially millions of -them. The cost of loading those entries into the environment each time -a user executes the contract would eventually become too expensive -were it not for *big maps*. Big maps are a data structure offered by -Michelson which handles the scaling concerns for us. In LIGO, the -interface for big maps is analogous to the one used for ordinary -maps. - -For convenience, we duplicate here the documentation for maps in order -to feature big maps, and we point out any relevant differences: - - * The hash of the keys are internally used to access the entries of - a big map, so this is advantageous when keys are large. - - * Since big maps are lazily-accessed maps, it makes no sense iterate - over them. In particular, there is not equivalent to the function - `Map.size`. - - * Big maps are not *packable*, as this would assume that they are - entirely serialised first. - -Big maps are a data structure which associates values of the same type -to values of the same type. The former are called *key* and the latter -*values*. Together they make up a *binding*. An additional requirement -is that the type of the keys must be *comparable*, in the Michelson -sense. - -As a consequence, the predefined type `big_map` has two parameters: -the first is the type of the keys, and the second the type of the -associated values. - -The empty big map is denoted by the predefined value -`Big_map.empty`. A non-empty big map can be built by using the -function `Big_map.literal` which takes a list of pairs of key and -values, and returns a big map containing them as bindings, and only -them. +Big-maps are data structures that are similar to [Maps](./maps) but are optimized in Michelson for large datasets. +Like maps, big-maps associate keys to values, where all keys are the same type and all values are the same type. +Like maps, the keys must be comparable types, which includes most primitives and tuples consisting of otherwise comparable types. + +As described in [Maps](./maps), to read a single element from a map, the contract must load the entire map. +By contrast, elements in big-maps are lazily deserialised, which means that when a contract reads elements from a big-map, it loads only the elements that it needs to access, instead of the entire big-map. +Loading only the necessary elements makes big-maps more cost-effective than maps when the dataset gets large because the contract manipulates less data. +Internally, big-maps also store their keys as hashes instead of raw data, which can save space when the keys are large or complex. + +Despite these internal differences, big-maps behave much like maps in contracts. +However, the way big-maps are stored and deserialised causes these limitations that maps don't have: + +- You can't get the number of elements in a big-map like you can with the `Map.size` function. +- There is no way to get a list of all of the keys in a big-map. +- You can't iterate through the elements in a big-map or use functions that access all of the elements in a big-map like the `Map.fold` and `Map.map` functions. +- Big-maps are not packable. + +## Creating big-maps + +To create a big-map, you can use the predefined value `Big_map.empty` or create a non-empty map by passing a list of pairs of keys and values to the function `Big_map.literal`. +This example creates a big-map type that uses a string for the key and a list of strings for the value: @@ -59,10 +42,10 @@ let dictionary : dictionary = ("two", ["The number 2"])] ``` -The `Big_map.literal` predefined function builds a big map from a list -of key-value pairs, `(, )`. Note also the "`;`" to -separate individual map bindings. Note that `("": -address)` means that we type-cast a string into an address. +The `Big_map.literal` predefined function builds a big-map from a list of key-value pairs, `(, )`. +Note that each binding in the list is separated with a semicolon (`;`). + +For reference, see the predefined [module Big_map](../reference/big-map-reference). @@ -75,34 +58,29 @@ type dictionary = big_map; const empty_dict: dictionary = Big_map.empty; -const dictionary : dictionary = +const dictionary: dictionary = Big_map.literal([ ["one", (["The number 1.", "A member of a group."] as definition)], ["two", (["The number 2."] as definition)]]); ``` -The `Big_map.literal` predefined function builds a big map from a list -of key-value pairs, `[, ]`. Note also the "`,`" to -separate individual big map bindings. Note that `"" as -address` means that we type-cast a string into an address. +The `Big_map.literal` predefined function builds a big-map from a list of key-value pairs, `[, ]`. +Note that each binding in the list is separated with a comma (`,`). - +For reference, see the predefined [namespace Big_map](../reference/big-map-reference). -> Note: Map keys are internally sorted by increasing values, so the -> type of the keys be *comparable*, that is, they obey a total order -> (any two keys can be compared). + -## Searching +## Searching for elements -The predicate `Big_map.mem` tests for membership in a given big map, -given a purported key. +The predefined function `Big_map.mem` returns true if a value exists in the big-map for a given key. ```cameligo group=big_map_searching -let my_map : (int, string) big_map = +let my_big_map : (int, string) big_map = Big_map.literal [(1,"one"); (2,"two")] -let contains_2 : bool = Big_map.mem 2 my_map // = true +let contains_2 : bool = Big_map.mem 2 my_big_map // = true ``` @@ -110,41 +88,24 @@ let contains_2 : bool = Big_map.mem 2 my_map // = true ```jsligo group=big_map_searching -const my_map: big_map = - Big_map.literal([[1,"one"],[2,"two"]]); -const contains_2: bool = Big_map.mem(2, my_map); // == true -``` - - - -In practice, however, we would like to get the value associated to the -key we searched. This is achieved by means of `Big_map.find_opt`. - - - -```cameligo group=big_map_searching -let v : string option = Big_map.find_opt 2 my_map +const my_big_map: big_map = + Big_map.literal([[1, "one"], [2, "two"]]); +const contains_2: bool = Big_map.mem(2, my_big_map); // == true ``` - +To get the value for a key, use the `Big_map.find_opt` function, which returns an [option](./variants#options). +If the key exists in the big-map, the option is `Some()` with the value. +If the key does not exist in the big-map, the option is `None()`. -```jsligo group=big_map_searching -const v : option = Big_map.find_opt(2, my_map); -``` - - - -Notice how the value we read is an optional value: this is to force -the reader to account for a missing key in the big map. This requires -*pattern matching*. +Because the return value of the `Big_map.find_opt` function is an option, you must account for missing keys in the big-map by [matching](./variants#matching) the return value, as in this example: ```cameligo group=big_map_searching -let force_access key map = - match Big_map.find_opt key map with +let value_option : string option = Big_map.find_opt 2 my_big_map +let value = match value_option with Some value -> value | None -> failwith "No value." ``` @@ -154,25 +115,22 @@ let force_access key map = ```jsligo group=big_map_searching -let force_access = (key, map) => { - return match(Big_map.find_opt (key, map)) { - when(Some(value)): value; - when(None): failwith("No value.") - }; -}; +const value_option: option = Big_map.find_opt(2, my_big_map); +const value = $match(value_option, { + "Some": value => value, + "None": () => failwith("No value."), +}); ``` -In fact, the predefined function `Big_map.find` does exactly that, -except that the exception raised by `failwith` carries the default -string `"MAP FIND"`. +As shorthand, you can use the function `Big_map.find`. +This function behaves like the previous example: it returns the value for a key if it exists or fails with the message `MAP FIND` if the value does not exist. -## Adding +## Adding elements -Adding a binding to a big map is done by calling the function -`Big_Map.add`. If the key was already present in the given big map, -the corresponding value is updated. +To add an element to a big-map, pass the key and value to the `Big_map.add` function. +If the key already exists, the corresponding value is updated. @@ -196,12 +154,10 @@ const contains_3 = Big_map.mem(3, new_map); // == true -## Removing +## Removing elements -The function `Big_map.remove` creates a big map containing the -elements of a given big map, without a given element. If the element -is not already present, the new big map is the same as the old one, as -expected. +The function `Big_map.remove` creates a big-map containing the elements of a given big-map, without the element with the given key. +If the element is not already present, the new big-map is the same as the old one. @@ -225,14 +181,15 @@ const contains_3 = Big_map.mem(2, new_map); // == false -## Updating +## Updating elements + +Previous sections show how to add and remove an element from a big-map. +The function `Big_map.update` can do both depending whether some value is given for the new binding or not. -Previous sections show how to add and remove a binding from a given -big map. The function `Big_map.update` can do both depending whether -some value is given for the new binding or not: in the former case, a -new binding is added (and replaces any previous binding with the same -key); in the latter case, any binding with the same key is removed and -a new big map is returned. +To update a big-map in this way, pass the key and an option with the value. +If the option is `Some(value)`, the function adds the element, replacing any element with the given key. +If the option is `None()`, the function removes the element with the given key if it exists. +In either case, the function returns a new big-map, as in these examples: @@ -252,16 +209,16 @@ let contains_2 = Big_map.mem 2 map_without_2 // = false ```jsligo group=big_map_updating const my_map: big_map = Big_map.literal([[1,"one"],[2,"two"]]); -const map_with_3 = Big_map.update (3, Some("three"), my_map); +const map_with_3 = Big_map.update (3, ["Some" as "Some", "three"], my_map); const contains_3 = Big_map.mem(3, map_with_3); // == true -const map_without_2 = Big_map.update(2, None(), my_map); +const map_without_2 = Big_map.update(2, ["None" as "None"], my_map); const contains_2 = Big_map.mem (2, map_without_2); // == false ``` -When we want to update a big map, but also obtain the value of the -updated binding, we can use `Big_map.get_and_update`. +To simultaneously update a map and obtain the value of the updated element, use the function `Big_map.get_and_update`. +This function allows you to extract a value from a big-map for use, as in this example: @@ -275,9 +232,9 @@ let three, map_without_3 = Big_map.get_and_update 3 None map_with_3 ```jsligo group=big_map_updating -// three == Some("three") +// three == ["Some" as "Some", "three"] const [three, map_without_3] = - Big_map.get_and_update(3, None(), map_with_3); + Big_map.get_and_update(3, ["None" as "None"], map_with_3); ``` diff --git a/gitlab-pages/docs/data-types/booleans.md b/gitlab-pages/docs/data-types/booleans.md index d2009325c8..318d15210b 100644 --- a/gitlab-pages/docs/data-types/booleans.md +++ b/gitlab-pages/docs/data-types/booleans.md @@ -191,8 +191,4 @@ const b = 1; const min = (a < b) ? a : b; // min == 0 ``` -Note: Parentheses are often necessary before `?`, but not always: you -can either rely on the compiler error message or always use -parentheses. - diff --git a/gitlab-pages/docs/data-types/bytes.md b/gitlab-pages/docs/data-types/bytes.md index e07f419963..f099be33b1 100644 --- a/gitlab-pages/docs/data-types/bytes.md +++ b/gitlab-pages/docs/data-types/bytes.md @@ -29,22 +29,19 @@ let zero_too = 0x00 ```jsligo group=bytes const a : bytes = 0x70FF; -const zero : bytes = 0x; +const zero = "" as bytes; const zero_too = 0x00; ``` -Clearly, this means that literal bytes are always comprised of an even -number of hexadecimal digits (because one hexadecimal digit requires -up to four bits in binary, and eight are needed to make up a byte). +This means that literal bytes are always comprised of an even number of hexadecimal digits, because one hexadecimal digit requires up to four bits in binary, and eight are needed to make up a byte. ## From numbers to bytes and back -Some other numerals can be converted to bytes by means of calling the -predefined function `bytes`, which is overloaded. The reverse -conversion is done by the predefined functions `int` and `nat`. For -instance, here how to create bytes from natural numbers and integers: +You can convert some other numerals to bytes by calling the predefined function `bytes`. +To convert ints or nats to bytes, use the predefined functions `int` and `nat`. +For example, here is how to create bytes from natural numbers and integers: @@ -62,7 +59,7 @@ let i : int = int 0x7B // i = 123 ```jsligo group=bytes -const b: bytes = bytes(123n); // 7B in hexadecimal +const b: bytes = bytes(123 as nat); // 7B in hexadecimal const c: bytes = bytes(123); const d: bytes = bytes(-123); // Two's complement @@ -72,21 +69,16 @@ const i: int = int(0x7B); // i == 123 -> Note: See -> [Two's complement](https://en.wikipedia.org/wiki/Two's_complement). +Note: See [Two's complement](https://en.wikipedia.org/wiki/Two's_complement). ## From strings -A string literal can be converted to bytes in two ways: +You can convert a string literal to bytes in two ways: - 1. by interpreting the [ASCII](https://en.wikipedia.org/wiki/ASCII) -code of each character (which spans over two hexadecimal digits) as -one byte; - 2. by interpreting directly each character as one hexadecimal digit. +- By interpreting the [ASCII](https://en.wikipedia.org/wiki/ASCII) code of each character (which spans over two hexadecimal digits) as one byte +- By interpreting directly each character as one hexadecimal digit - -In the former case, the syntax is somewhat odd -- as opposed to simply -calling the function `bytes`: +To interpret the ASCII code, use this syntax: @@ -99,12 +91,12 @@ let from_ascii : bytes = [%bytes "foo"] ```jsligo group=bytes -const from_ascii: bytes = bytes`foo`; // Not a call +const from_ascii: bytes = bytes`foo`; // Not a function call ``` -The latter case is implemented as a type cast: +To interpret each character directly, use a type cast: @@ -113,12 +105,13 @@ The latter case is implemented as a type cast: let raw : bytes = ("666f6f" : bytes) ``` -> Note that both the `[%bytes ...]` and `(... : bytes)` syntaxes apply -> only to *string literals*, not general expressions of type -> `string`. In other words, the contents of the strings must be -> available in-place at compile-time. (This actually reveals that -> `("666f6f" : bytes)` is not really a cast, as casts are -> non-operations.) +:::note + +Both cases apply only to string literals, not variables or other expressions of type `string`. +In other words, the contents of the strings must be available in-place at compile time. +(This reveals that `("666f6f" : bytes)` is not really a cast, because casts are non-operations.) + +::: @@ -129,11 +122,13 @@ let raw : bytes = ("666f6f" : bytes) const raw: bytes = ("666f6f" as bytes); ``` -> Note that both syntaxes apply respectively only to *verbatim* string -> literals and general strings, not general expressions of type -> `string`. In other words, the contents of the strings must be -> available at compile-time. (This actually reveals that `("666f6f" as -> bytes)` is not really a cast, as casts are non-operations.) +:::note + +Both cases apply only to string literals, not variables or other expressions of type `string`. +In other words, the contents of the strings must be available in-place at compile time. +(This reveals that `("666f6f" as bytes)` is not really a cast, because casts are non-operations.) + +::: @@ -175,17 +170,17 @@ let len : nat = Bytes.length 0x0AFF // len = 2n ```jsligo group=sizing -const len: nat = Bytes.length(0x0AFF); // len == 2n +const len: nat = Bytes.length(0x0AFF); // len == (2 as nat) ``` ## Slicing -Bytes can be extracted using the predefined function `Bytes.sub`. The -first parameter is the start index and the second is the number of -bytes of the slice we want. Keep in mind that the first byte in a -sequence has index `0n`. +You can extract a subset from bytes with the `Bytes.sub` function. +It accepts a nat for the index of the start of the subset and a nat for the number of bytes in the subset. +Both numbers are inclusive. +The first byte has the index 0. @@ -200,7 +195,7 @@ let slice = Bytes.sub 1n 2n large // sub = 0x3456 ```jsligo group=slicing const large = 0x12345678; -const slice = Bytes.sub(1n, 2n, large); // sub == 0x3456 +const slice = Bytes.sub(1 as nat, 2 as nat, large); // sub == 0x3456 ``` @@ -243,28 +238,26 @@ const or: bytes = 0x0005 | 0x0106; // 0x0107 const xor: bytes = 0x0005 ^ 0x0106; // 0x0103 // Bitwise "shift left" -const shift_left: bytes = 0x06 << 8n; // 0x0600 +const shift_left: bytes = 0x06 << (8 as nat); // 0x0600 // Bitwise "shift right" -const shift_right: bytes = 0x0006 >> 1n; // 0x0003 +const shift_right: bytes = 0x0006 >> (1 as nat); // 0x0003 ``` ## Packing and unpacking -As Michelson provides the instructions `PACK` and `UNPACK` for data -serialisation, so does LIGO with `Bytes.pack` and `Bytes.unpack`. The -former serialises Michelson data structures into a binary format, and -the latter reverses that transformation. Unpacking may fail, so the -return type of `Byte.unpack` is an option that needs to be annotated. +LIGO provides the functions `Bytes.pack` and `Bytes.unpack` to serialize and deserialize data into a binary format. +These functions correspond to the Michelson instructions `PACK` and `UNPACK`. +Unpacking may fail, so the return type of `Byte.unpack` is an option that needs a type annotation. + +:::note -> Note: `PACK` and `UNPACK` are Michelson instructions that are -> intended to be used by people that really know what they are -> doing. There are several risks and failure cases, such as unpacking -> a lambda from an untrusted source or casting the result to the wrong -> type. Be careful. +These functions are intended for use by developers who are familiar with data serialization. +There are several risks and failure cases, such as unpacking a lambda from an untrusted source or casting the result to the wrong type. +::: @@ -279,7 +272,7 @@ let id_string (p : string) : string option = ```jsligo group=packing -const id_string = (p: string) : option => { +function id_string (p: string) : option { let packed = Bytes.pack(p); return Bytes.unpack(packed); }; diff --git a/gitlab-pages/docs/data-types/contracts.md b/gitlab-pages/docs/data-types/contracts.md index a1fbaa646a..957e10cda2 100644 --- a/gitlab-pages/docs/data-types/contracts.md +++ b/gitlab-pages/docs/data-types/contracts.md @@ -33,9 +33,11 @@ Reset [@entry] let callContract (_ : unit) (storage : int) : returnType = let contractAddress : address = ("KT1FpuaoBHwXMXJ6zn3F4ZhpjpPZV28MAinz" : address) in - let myContract: contractParam contract = Tezos.get_contract contractAddress in - let operation = Tezos.transaction (Increment 4) 0tez myContract in - [operation], storage + let myContract : contractParam contract = + Tezos.get_contract contractAddress in + let operation = + Tezos.Operation.transaction (Increment 4) 0tez myContract + in [operation], storage ``` @@ -57,13 +59,14 @@ type contractParam = | ["Decrement", int] | ["Increment", int]; -@entry -const callContract = (_: unit, storage: int): returnType => { +// @entry +function callContract (_: unit, storage: int): returnType { const contractAddress: address = ("KT1FpuaoBHwXMXJ6zn3F4ZhpjpPZV28MAinz" as address); const myContract: contract = Tezos.get_contract(contractAddress); - const contractArg: contractParam = Increment(4); - const operation = Tezos.transaction(contractArg, 0tez, myContract); - return [list([operation]), storage + 1] + const contractArg: contractParam = ["Increment" as "Increment", 4]; + const operation = + Tezos.Operation.transaction(contractArg, 0 as tez, myContract); + return [[operation], storage + 1] } ``` @@ -113,8 +116,6 @@ module C = struct let reset () (_ : storage) : return = [], 0 end -module Test = Test.Next - let test_initial_storage () : unit = let init_storage = 42 in let fee = 0mutez in @@ -127,8 +128,8 @@ let test_initial_storage () : unit = (* Call contract through `main` function *) let increment_param : C parameter_of = Increment 8 in let decrement_param : C parameter_of = Decrement 3 in - let _ = Test.transfer_exn contract.taddr increment_param 0mutez in - let _ = Test.transfer_exn contract.taddr decrement_param 0mutez in + let _ = Test.Typed_address.transfer_exn contract.taddr increment_param 0mutez in + let _ = Test.Typed_address.transfer_exn contract.taddr decrement_param 0mutez in let new_storage = Test.Typed_address.get_storage contract.taddr in Assert.assert (new_storage = init_storage + 15 - 14 + 8 - 3) @@ -147,38 +148,36 @@ In the example below, `contract_of(C)` returns the implicitly-declared `main` fu ```jsligo group=contract_of type storage = int; -type @return = [list, storage]; +type return_ = [list, storage]; -namespace C { +class C { @entry - const decrement = (param: int, storage: storage) : @return => + decrement = (param: int, storage: storage) : return_ => [[], storage - param]; @entry - const increment = (param: int, storage: storage) : @return => + increment = (param: int, storage: storage) : return_ => [[], storage + param]; @entry - const reset = (_unit: unit, _storage: storage) : @return => + reset = (_unit: unit, _storage: storage) : return_ => [[], 0]; } -import Test = Test.Next; - const test_initial_storage = () : unit => { const init_storage = 42; - const fee = 0mutez; + const fee = 0 as mutez; const contract = Test.Originate.contract(contract_of(C), init_storage, fee); // Call contract through entrypoints - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("increment", contract.taddr), 15, 0tez); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("decrement", contract.taddr), 14, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("increment", contract.taddr), 15, 0 as tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("decrement", contract.taddr), 14, 0 as tez); // Call contract through `main` function - const increment_param: parameter_of C = Increment(8); - const decrement_param: parameter_of C = Decrement(3); - Test.transfer_exn(contract.taddr, increment_param, 0mutez); - Test.transfer_exn(contract.taddr, decrement_param, 0mutez); + const increment_param: parameter_of = ["Increment" as "Increment", 8]; + const decrement_param: parameter_of = ["Decrement" as "Decrement", 3]; + Test.Typed_address.transfer_exn(contract.taddr, increment_param, 0 as mutez); + Test.Typed_address.transfer_exn(contract.taddr, decrement_param, 0 as mutez); const new_storage = Test.Typed_address.get_storage(contract.taddr); Assert.assert(new_storage == init_storage + 15 - 14 + 8 - 3); diff --git a/gitlab-pages/docs/data-types/lists.md b/gitlab-pages/docs/data-types/lists.md index af856b7137..313602eb1c 100644 --- a/gitlab-pages/docs/data-types/lists.md +++ b/gitlab-pages/docs/data-types/lists.md @@ -4,20 +4,30 @@ title: Lists import Syntax from '@theme/Syntax'; -Lists are linear collections of elements of the same type. Linear -means that, in order to reach an element in a list, we must visit all -the elements before (sequential access). Elements can be repeated, as -only their order in the collection matters. The first element is -called the *head*, and the sub-list after the head is called the -*tail*. For those familiar with algorithmic data structure, you can -think of a list a *stack*, where the top is written on the left. +Lists are linear collections of elements of the same type. +Elements can appear more than once in a list, because only their order in the collection matters. +Lists can be empty or contain any number of elements. -> 💡 Lists are needed when returning operations from a smart -> contract. +In this context, _linear_ means that you cannot access elements in random order. +To access a specific element, you must visit the elements sequentially. +The first element is called the *head*, and the sub-list after the head is called the *tail*. For those familiar with algorithmic data structure, you can think of a list a *stack*, where the top is written on the left. -The type for lists is polymorphic, that is, parameterised by the type -of the list elements, so we can define a "list of integers", a "list -of natural numbers" etc. +:::note + +One important use of lists is in the return values of contract entrypoints. +Each entrypoint returns a tuple, of which the first component is a list of operations to run. + +::: + +Lists are similar to [sets](./sets) because they are both collections of elements of the same type. +The main differences between lists and sets are: + +- Sets cannot contain duplicate entries, while lists can +- Sets are always automatically sorted, while you can put the elements of a list in any order and LIGO retains that order + +The type for lists is polymorphic, that is, parameterised by the type of the list elements, so to define a list you must specify the type of the elements. +Developers say that they are defining a "list of integers", a "list of natural numbers," and so on for lists of other types. +This example shows how to define lists of integers, but you can create lists of any type as long as all of the elements have the same type: @@ -26,37 +36,91 @@ let empty_list : int list = [] let my_list : int list = [1; 2; 2] (* The head is 1, the tail is [2; 2] *) ``` -See predefined -[module List](../reference/list-reference/?lang=cameligo). +For functions that work with lists, see the predefined [module List](../reference/list-reference/?lang=cameligo). ```jsligo group=lists -const empty_list : list = []; -const my_list : list = [1, 2, 2]; // The head is 1, the tail is [2, 2] +const empty_list: list = []; +const my_list: list = [1, 2, 2]; // The head is 1, the tail is [2, 2] ``` -Note how we need to use the cast `list(...)` on a tuple to make it a -list. In general, tuples are not lists: tuples have a fixed number of -components that appear in their type, and each component can have a -different type, whereas lists have a variable number of elements and -they have all the same type. Nevertheless, LIGO uses the same syntax -for tuples and lists, except that the latter is enclosed in -`list(...)`, except when the context makes it unambiguous that it is a -list (we will see some example with pattern matching). +The syntax for a literal list value is the same as for a tuple. +Therefore, in some cases, you may need to use the `list` function to cast a value to make it a list instead of a tuple. +In the previous example, the `my_list` variable is a list because of the type annotation. +However, if you assign a literal list value to a variable when the context is ambiguous, you must cast it with the `list` function or else LIGO assumes that it is a tuple, as in this example: -See predefined -[namespace List](../reference/list-reference/?lang=jsligo). +```jsligo group=lists +const my_literal_tuple = [1, 2, 3]; // Assumed to be a tuple +const my_literal_list = list([1, 2, 3]); // Casted to a list +``` + +For functions that work with lists, see the predefined [namespace List](../reference/list-reference/?lang=jsligo). -## Adding +:::tip + +Because lists can contain any type of element, it can be convenient to use polymorphism to create functions that can operate on lists of any type. +For more information, see [Polymorphic functions](./parametric_types#polymorphic-functions). + +::: + +## Accessing elements -Lists can be augmented by adding an element before the head (or, in -terms of stack, by *pushing an element on top*). This operation is -usually called *consing* in functional languages. +You cannot access elements directly in lists, such as getting the element with an arbitrary index. +One common way to access list elements is to access the first element (the head) or the rest of the elements (the tail) separately. +LIGO provides the functions `List.head` and `List.tail` to access list elements as options, as in these examples: + + + +```cameligo group=access +let my_list : int list = [1; 2; 3] +let head_option : int option = List.head my_list +let head = match head_option with +| Some value -> value +| None -> failwith "Failed to get the head of the list" +let tail_option : int list option = List.tail my_list +let tail = match tail_option with +| Some value -> value +| None -> failwith "Failed to get the tail of the list" +``` + + + + + + +```jsligo group=access +const my_list: list = [1, 2, 3]; +const head_option: option = List.head(my_list); +const head = $match(head_option, { + "Some": value => value, + "None": () => failwith("Failed to get the head of the list"), +}); +const tail_option: option> = List.tail(my_list); +const tail = $match(tail_option, { + "Some": value => value, + "None": () => failwith("Failed to get the tail of the list"), +}); +``` + +:::note + +In JsLIGO, you cannot access an arbitrary element in a list by its index, as in `my_list[4]`. + +::: + + + +Another way to access elements in lists is to process every element in the list, as described in [Processing lists](#processing-lists). + +## Adding elements + +You can add an element to the beginning of a list to become the new head (or, in terms of a stack, by *pushing an element on top*). +This operation is usually called *consing* in functional languages. @@ -77,9 +141,6 @@ There is also a predefined function `List.cons`: let longer_list = List.cons 6 long_list ``` -See predefined -[module List](../reference/list-reference/?lang=cameligo). - @@ -89,9 +150,9 @@ on the left lies the element to cons, and, on the right, a list on which to cons. ```jsligo group=consing -const short_list : list = [1, 2, 2]; +const short_list: list = [1, 2, 2]; // long_list == [5,1,2,2] -const long_list : list = [5, ...short_list]; +const long_list: list = [5, ...short_list]; ``` There is also a predefined function `List.cons`: @@ -101,205 +162,83 @@ There is also a predefined function `List.cons`: const longer_list = List.cons(6, long_list); ``` -See predefined -[namespace List](../reference/list-reference/?lang=jsligo). - -## Matching - -Polymorphism is especially useful when writing functions over -parametric types, which include built-in types like lists, sets, and -maps. - -As an example, we will see how to implement list reversing -parametrically on any type, rather than just on lists of a specific -type. - -Similarly to the polymorphic identity function, we can introduce a -type variable that can be generalised. We will write a direct version -of the function using an accumulator. - - +## Removing elements -```cameligo group=reverse -let rev (type a) (xs : a list) : a list = - let rec rev (type a) (xs : a list) (acc : a list) : a list = - match xs with - | [] -> acc - | x :: xs -> rev xs (x::acc) - in rev xs [] -``` +There is no direct way to remove a specific element from a list. +You can use loops and the other functions listed below to process a list and filter elements out. +For example, you can use the `List.update_with` function to remove certain elements as described in [Updating elements](#updating-elements). -Note that because the type variable `a` was introduced (bound) by -means of `type`, it does not need a quote, like `'a`. +If you need to add and remove elements frequently, consider using a [set](./sets). +Sets have a dedicated `Set.remove` function that removes elements. - +## Processing lists - +Aside from retrieving the head and tail of a list, you can use loops and functions that iterate over every element in a list. -```jsligo group=reverse -function rev (xs : list) : list { - const rev = ([xs, acc] : [list, list]) : list => - match(xs) { - when([]): acc; - when([y,...ys]): rev([ys, list([y,...acc])]) - }; - - return rev([xs, []]); -}; -``` - -Note how the type checker was able to infer the types of `[]` and -`[y,...ys]` in the `when` clauses (without the need of using -`[]` and `[y,...ys]`), but in `[y,...acc]` the cast -to `list` is necessary, because of the rest property that needs to be -interpreted as a cons. Similarly, the `list` in `[xs, []]` is -needed to force the interpretation of `[]` as the empty list, instead -of the empty array ("unit"). - -See predefined -[module List](../reference/list-reference/?lang=cameligo). - - - -We use an accumulator variable `acc` to keep the elements of the list -processed, consing each element on it. - -As with the identity function, we can then use `rev` directly with -different type instances: - - - -```cameligo group=reverse -let ints : int list = rev [1; 2; 3] -let nats : nat list = rev [1n; 2n; 3n] -``` - - - - - -```jsligo group=reverse -const ints : list = rev([1, 2, 3]); -const nats : list = rev([1n, 2n, 3n]); -``` - -See predefined -[namespace List](../reference/list-reference/?lang=jsligo). - - - -## Updating - -The function `List.update_with` enables the replacement of elements of -a given list according to a boolean function: if the call of that -function on a element is true, then the element is replaced, otherwise -it remains. +### Looping over lists -```cameligo group=list_updating -let nats = [0; 1; 2; 3; 4] -// evens_zeroed = [0; 1; 0; 3; 0] -let evens_zeroed = List.update_with (fun x -> x mod 2 = 0n) 0 nats -``` +There is no loop over lists in CameLIGO. -```jsligo group=list_updating -const nats : list = [0, 1, 2, 3, 4]; -// evens_zeroed == [0, 1, 0, 3, 0] -const evens_zeroed = List.update_with(x => x % 2 == 0n, 0, nats); -``` - - - -The function `List.update` enables the selective replacement of -elements of a given list according to a function that returns an -optional value, instead of a boolean as `List.update_with` above. +You can use a `for` loop to iterate over the elements in the list, from left to right, in the form `for (const of ) `. +This statement means that the block of statements (or a single statement) runs once for each element in the list (``) ranging over the elements of the list from left to right. - - -That function takes an element and returns an optional value: if that -value is `None`, then the element is left unchanged, otherwise, if the -value is `Some v`, then the element is replaced in the resulting list -by `v`. +This example uses a `for` loop to get the sum of the integers in a list: -```cameligo group=list_updating -let f x = if x mod 2 = 0n then None else Some (x*x) -// odds = [0; 1; 2; 9; 4] -let odds_squared = List.update f nats +```jsligo group=list_looping +function sum_list (l: list) { + let sum = 0; + for (const i of l) sum = sum + i; + return sum; +}; ``` -See predefined -[module List](../reference/list-reference/?lang=cameligo). - - - -That function takes an element and returns an optional value: if that -value is `None()`, then the element is left unchanged, otherwise, if -the value is `Some(v)`, then the element is replaced in the resulting -list by `v`. - -```jsligo group=list_updating -const f = x => x % 2 == 0n ? None() : Some(x*x); -// odds == [0, 1, 2, 9, 4] -const odds_squared = List.update(f, nats); -``` - -See predefined -[namespace List](../reference/list-reference/?lang=jsligo). - - +### Folding lists -## Folding +Folding a list runs the same function on each element in a list and returns a single value. +The function takes two arguments: an *accumulator* and the current list element, with which it produces a new accumulator to pass to the next iteration of the function. +Folding lists allows you to compute a partial result that becomes complete when the traversal of the data structure is over. -A *functional iterator* is a function that traverses a data structure -and calls in turn a given function over the elements of that structure -to compute some value. Another approach is sometimes possible: *loops* -(see sections loops, sets and maps). +The fold functions `List.fold_left` and `List.fold_right` take the same three parameters **in different orders**: -There are three kinds of functional iterations over lists: the *fold*, -the *map* (not to be confused with the *map data structure*) and the -*iteration*. +- The function to run on each element, which receives a tuple containing the accumulator and the current element +- The initial value for the accumulator +- The list of elements to fold -Let us consider first here the fold, which is the most general form of -functional iteration. The folded function takes two arguments: an -*accumulator* and the structure *element* at hand, with which it then -produces a new accumulator. This enables having a partial result that -becomes complete when the traversal of the data structure is over. +Both functions take the function to run first. +The `fold_left` function takes the initial value second and the list to fold last, while the `fold_right` function takes the list to fold second and the initial value last. -The module `List` exports the functions `fold_left` and `fold_right`, -so folds have either the form: +These fold functions have these forms: ``` -List.fold_left folded init list +List.fold_left fold_function initial_value list ``` -or - ``` -List.fold_right folded list init +List.fold_right fold_function list initial_value ``` -which means that the folding can be done leftwards or rightwards on -the list. One way to tell them apart is to look where the folded -function, and the fold itself, keep the accumulator in their -signatures. Take for example a function `f`, a list `[1; 2; 3]`, and -an initial accumulator `init`. Then +In this way, you can fold lists from left to right or right to left. + +One way to tell them apart is to look where the folded function, and the fold itself, keep the accumulator in their signatures. Take for example a function `f`, a list `[1; 2; 3]`, and an initial accumulator `init`. +Then the `fold_left` function is equivalent to: ``` List.fold_left f init [1; 2; 3] = f (f (f (init, 1), 2), 3) ``` -and +And the `fold_right` function is equivalent to: ``` List.fold_right f [1; 2; 3] init = f (1, f (2, f (3, init))) @@ -311,43 +250,42 @@ List.fold_right f [1; 2; 3] init = f (1, f (2, f (3, init))) * The type of `List.fold_right` is `('b * 'a -> 'a) -> 'b list -> 'a -> 'a`. -For example, let us compute the sum of integers in a list, assuming -that the empty list yields `0`: +For example, here are two ways to compute the sum of integers in a list, assuming that the empty list yields `0`: ```cameligo group=folding_lists -let sum1 = List.fold_left (fun (a,i) -> a + i) 0 [1; 2; 3] -let sum2 = List.fold_right (fun (i,a) -> i + a) [1; 2; 3] 0 +let sum1 = List.fold_left (fun (a, i) -> a + i) 0 [1; 2; 3] +let sum2 = List.fold_right (fun (i, a) -> i + a) [1; 2; 3] 0 ``` -> For OCaml users: In OCaml, the folded functions are curryfied, so -> their types are `('a -> 'b -> 'a)` `List.fold_left`, and `('b -> 'a -> -> 'a)` with `List.fold_right`. +:::note -See predefined -[module List](../reference/list-reference/?lang=cameligo). +For OCaml users: In OCaml, the folded functions are curryfied, so their types are `('a -> 'b -> 'a)` `List.fold_left`, and `('b -> 'a -> 'a)` with `List.fold_right`. + +::: -The module `List` exports the functions `fold_left` and `fold_right`, -so folds have either the form: +These folds have these forms: ``` List.fold_left (folded, init, list) ``` -or - ``` List.fold_right (folded, list, init) ``` -which mean that the folding can be done leftwards or rightwards on the -list. One way to tell them apart is to look where the folded function, -and the fold itself, keep the accumulator in their signatures. Take -for example a function `f`, a list `[1, 2, 3]`, and an initial -accumulator `init`. Then +:::note + +The `List.fold_left` function is similar to the JavaScript `reduce` function, but note the order of the parameters. + +::: + +In this way, you can fold lists from left to right or right to left. + +One way to tell them apart is to look where the folded function, and the fold itself, keep the accumulator in their signatures. Take for example a function `f`, a list `[1, 2, 3]`, and an initial accumulator `init`. Then ``` List.fold_left (f, init, [1;2;3]) = f (f (f (init, 1), 2), 3) @@ -363,8 +301,7 @@ The type of `List.fold_left` is `(p : [a * b => a, a, b list]) => a`. The type of `List.fold_right` is `(p : [b * a => a, b list, a]) => a`. -For example, let us compute the sum of integers in a list, assuming -that the empty list yields `0`: +For example, here are two ways to compute the sum of integers in a list, assuming that the empty list yields `0`: ```jsligo group=folding_lists const add1 = ([a, i]) => a + i; @@ -373,18 +310,13 @@ const add2 = ([i, a]) => i + a; const sum2 = List.fold_right(add2, [1, 2, 3], 0); ``` -See predefined -[namespace List](../reference/list-reference/?lang=jsligo). - -## Mapping +### Mapping lists -We may want to change all the elements of a given list by applying to -them a function. This is called a *map operation*, not to be confused -with the map data structure. The predefined functional iterator -implementing the mapped operation over lists is called `List.map` and -is used as follows. +Mapping a list runs the same function on each element in a list and returns a new list with the result of each function operation. +This is called a *map operation*, not to be confused with the map data type. +To map a list, use the `List.map` function, as in this example: @@ -392,9 +324,6 @@ is used as follows. let plus_one = List.map (fun i -> i + 1) [6; 2; 3; 3] ``` -See predefined -[module List](../reference/list-reference/?lang=cameligo). - @@ -403,40 +332,87 @@ See predefined const plus_one = List.map(i => i + 1, [6, 2, 3, 3]); ``` -See predefined -[namespace List](../reference/list-reference/?lang=jsligo). + + +### Updating elements + +The `List.update_with` function runs a function on each element in the list and if that function returns true, the element is replaced with a specified value. +If it returns false, the element remains. +This example takes a list of numbers and changes all of the even numbers to zero without changing the odd numbers: + + + +```cameligo group=list_updating +let nats = [0; 1; 2; 3; 4] +let evens_zeroed = List.update_with (fun x -> x mod 2 = 0n) 0 nats +// evens_zeroed = [0; 1; 0; 3; 0] +``` + + + + + +```jsligo group=list_updating +const nats: list = [0, 1, 2, 3, 4]; +const evens_zeroed = List.update_with(x => x % 2 == (0 as nat), 0, nats); +// evens_zeroed == [0, 1, 0, 3, 0] +``` -## Looping +The function `List.update` is similar, but it replaces elements based on an option value. +If the function you pass to it returns `Some`, the value of that option becomes the new value of the element. +If it returns `None`, the value of the element stays the same. +This example takes a list of numbers and changes all of the even numbers to their square without changing the odd numbers: -There is no loop over lists in CameLIGO. -See predefined -[module List](../reference/list-reference/?lang=cameligo). +```cameligo group=list_updating +let f x = if x mod 2 = 0n then None else Some (x*x) +let odds_squared = List.update f nats +// odds_squared = [0; 1; 2; 9; 4] +``` -One can iterate through all the elements of a list, from left to -right, thanks to a loop of the form `for (const of ) `. It means that the `` of statements (or a single -statement) will be computed once for each `` ranging over -the elements of the list `` from left to right. +That function takes an element and returns an optional value: if that +value is `None()`, then the element is left unchanged, otherwise, if +the value is `Some(v)`, then the element is replaced in the resulting +list by `v`. + +```jsligo group=list_updating +const f = x => + x % 2 == (0 as nat) ? ["None" as "None"] : ["Some" as "Some", x*x]; +const odds_squared = List.update(f, nats); +// odds_squared == [0, 1, 2, 9, 4] +``` -Here is an example where the integers in a list are summed up, and the -sum is zero if the list is empty: + -```jsligo group=list_looping -function sum_list (l: list) { - let sum = 0; - for (const i of l) sum = sum + i; - return sum; -}; +### Iterating over lists + +The `List.iter` function is similar to the `List.map` function because it runs the same function on every element in a list. +However, the `List.iter` function returns a value of type unit, so it cannot change the list. +Therefore, this function is useful only to produce side effects, such as checking that each element of a list is within a certain range, and fail with an error otherwise. + +This example iterates over a list to check that all its elements (integers) are greater than 3: + + + +```cameligo group=list_iterating +let assert_all_greater_than_3 (l : int list) : unit = + List.iter (fun i -> Assert.assert (i > 3)) l ``` -See predefined -[namespace List](../reference/list-reference/?lang=jsligo). + + + + +```jsligo group=list_iterating +const assert_all_greater_than_3 = + (l: list): unit => List.iter(i => Assert.assert(i > 3), l); +``` diff --git a/gitlab-pages/docs/data-types/maps.md b/gitlab-pages/docs/data-types/maps.md index 3701943cbc..666b39a778 100644 --- a/gitlab-pages/docs/data-types/maps.md +++ b/gitlab-pages/docs/data-types/maps.md @@ -2,20 +2,32 @@ title: Maps --- -*Maps* are a data structure which associates values of the same type to -values of the same type. The former are called *key* and the latter -*values*. Together they make up a *binding*. An additional requirement -is that the type of the keys must be *comparable*, in the Michelson -sense. +Maps are a data structure that associates keys to values. +Together, a key and its value make up a *binding*, also called an *element*. +The keys must all be the same type and the values must be the same type. +Keys must be unique within a map. -As a consequence, the predefined type `map` has two parameters: the -first is the type of the keys, and the second the type of the -associated values. +The predefined type `map` has two parameters: the first is the type of the keys, and the second is the type of the associated values. -The empty map is denoted by the predefined value `Map.empty`. A -non-empty map can be built by using the function `Map.literal` which -takes a list of pairs of key and values, and returns a map containing -them as bindings, and only them. +Internally, LIGO sorts maps in increasing order by their keys. +Therefore, the type of the keys must be *comparable*, which means that Michelson allows them to be compared. +Most primitive types are comparable, including strings, ints, nats, and addresses. +To create more complex keys, you can use a [tuple](./tuples) of two comparable types. +For more information about Michelson types and which types are comparable, see [Michelson](https://octez.tezos.com/docs/active/michelson.html) in the Octez reference. + +:::note Maps and big-maps + +Maps are appropriate for small data sets and data sets that you want to load all at once, such as if you want to run logic on every element or check their lengths. +For data sets that may become larger, consider using a [Big-map](./big_maps). +Big-maps can be more efficient for larger data sets because only the elements that you access are loaded, which reduces gas fees. +However, this means that contracts can't do things that require them to load the entire big-map. + +::: + +## Creating maps + +To create a map, you can use the predefined value `Map.empty` or create a non-empty map by passing a list of pairs of keys and values to the function `Map.literal`. +This example creates a map type that uses a string for the key and a list of strings for the value: @@ -32,13 +44,10 @@ let dictionary : dictionary = ("two", ["The number 2"])] ``` -The `Map.literal` predefined function builds a map from a list of -key-value pairs, `(, )`. Note also the "`;`" to separate -individual map bindings. Note that `("": address)` means -that we type-cast a string into an address. +The `Map.literal` predefined function builds a map from a list of key-value pairs, `(, )`. +Note that each binding in the list is separated with a semicolon (`;`). -Note: See the predefined -[module Map](../reference/map-reference) +For reference, see the predefined [module Map](../reference/map-reference). @@ -51,30 +60,22 @@ type dictionary = map; const empty_dict: dictionary = Map.empty; -const dictionary : dictionary = +const dictionary: dictionary = Map.literal([ - ["one", (["The number 1.", "A member of a group."] as definition)], - ["two", (["The number 2."] as definition)]]); + ["one", (["The number 1.", "A member of a group."])], + ["two", (["The number 2."])]]); ``` -The `Map.literal` predefined function builds a map from a list of -key-value pairs, `[, ]`. Note also the "`,`" to separate -individual map bindings. Note that `"" as address` means -that we type-cast a string into an address. +The `Map.literal` predefined function builds a map from a list of key-value pairs, `[, ]`. -Note: See the predefined -[namespace Map](../reference/map-reference) +For reference, see the predefined [namespace Map](../reference/map-reference). -> Note: Map keys are internally sorted by increasing values, so the -> type of the keys be *comparable*, that is, they obey a total order -> (any two keys can be compared). - -## Sizing +## Sizing maps The predefined function `Map.size` returns the number of bindings -(entries) in a given map. +(elements) in a given map. @@ -83,8 +84,6 @@ let my_map : (int, string) map = Map.literal [(1,"one"); (2,"two")] let size : nat = Map.size my_map // = 2 ``` -Note: See the predefined -[module Map](../reference/map-reference) @@ -92,19 +91,15 @@ Note: See the predefined ```jsligo group=map_size const my_map: map = - Map.literal([[1,"one"],[2,"two"]]); + Map.literal([[1, "one"], [2, "two"]]); const size: nat = Map.size(my_map); // == 2 ``` -Note: See the predefined -[namespace Map](../reference/map-reference) - -## Searching +## Searching for elements -The predicate `Map.mem` tests for membership in a given map, given a -purported key. +The predefined function `Map.mem` returns true if a value exists in the map for a given key. @@ -120,39 +115,23 @@ let contains_2 : bool = Map.mem 2 my_map // = true ```jsligo group=map_searching const my_map: map = - Map.literal([[1,"one"],[2,"two"]]); + Map.literal([[1, "one"], [2, "two"]]); const contains_2: bool = Map.mem(2, my_map); // == true ``` -In practice, however, we would like to get the value associated to the -key we searched. This is achieved by means of `Map.find_opt`. - - - -```cameligo group=map_searching -let v : string option = Map.find_opt 2 my_map -``` - - - - - -```jsligo group=map_searching -const v : option = Map.find_opt(2, my_map); -``` +To get the value for a key, use the `Map.find_opt` function, which returns an [option](./variants#options). +If the key exists in the map, the option is `Some()` with the value. +If the key does not exist in the map, the option is `None()`. - - -Notice how the value we read is an optional value: this is to force -the reader to account for a missing key in the map. This requires -*pattern matching*. +Because the return value of the `Map.find_opt` function is an option, you must account for missing keys in the map by [matching](./variants#matching) the return value, as in this example: ```cameligo group=map_searching -let force_access key map = +let value_option : string option = Map.find_opt 2 my_map +let value key map = match Map.find_opt key map with Some value -> value | None -> failwith "No value." @@ -163,106 +142,78 @@ let force_access key map = ```jsligo group=map_searching -let force_access = (key, map) => { - return match(Map.find_opt (key, map)) { - when(Some(value)): value; - when(None): failwith("No value.") - }; -}; +const value_option: option = Map.find_opt(2, my_map); +const value = (key, map) => + $match(Map.find_opt(key, map), { + "Some": (value) => value, + "None": () => failwith("No value."), + }); ``` -In fact, the predefined function `Map.find` does exactly that, except -that the exception raised by `failwith` carries the default string -`"MAP FIND"`. - - - -Note: See the predefined -[module Map](../reference/map-reference) - - - - - -Note: See the predefined -[namespace Map](../reference/map-reference) +As shorthand, you can use the function `Map.find`. +This function behaves like the previous example: it returns the value for a key if it exists or fails with the message `MAP FIND` if the value does not exist. - - -## Adding +## Adding elements -Adding a binding to a map is done by calling the function -`Map.add`. If the key was already present in the given map, the -corresponding value is updated. +To add an element to a map, pass the key and value to the `Map.add` function. +If the key already exists, the corresponding value is updated. ```cameligo group=map_adding -let my_map : (int, string) map = Map.literal [(1,"one"); (2,"two")] +let my_map : (int, string) map = Map.literal [(1, "one"); (2, "two")] let new_map = Map.add 3 "three" my_map let contains_3 = Map.mem 3 new_map // = true ``` -Note: See the predefined -[module Map](../reference/map-reference) - ```jsligo group=map_adding -const my_map : map = Map.literal([[1,"one"],[2,"two"]]); +const my_map: map = Map.literal([[1, "one"], [2, "two"]]); const new_map = Map.add(3, "three", my_map); const contains_3 = Map.mem(3, new_map); // == true ``` -Note: See the predefined -[namespace Map](../reference/map-reference) - -## Removing +## Removing elements -The function `Map.remove` creates a map containing the elements of a -given map, without a given element. If the element is not already -present, the new map is the same as the old one, as expected. +The function `Map.remove` creates a map containing the elements of a given map, without the element with the given key. +If the element is not already present, the new map is the same as the old one. ```cameligo group=map_removing -let my_map : (int, string) map = Map.literal [(1,"one"); (2,"two")] +let my_map : (int, string) map = Map.literal [(1, "one"); (2, "two")] let new_map = Map.remove 2 my_map let contains_3 = Map.mem 2 new_map // = false ``` -Note: See the predefined -[module Map](../reference/map-reference) - ```jsligo group=map_removing -const my_map : map = Map.literal([[1,"one"],[2,"two"]]); +const my_map: map = Map.literal([[1, "one"], [2, "two"]]); const new_map = Map.remove(2, my_map); const contains_3 = Map.mem(2, new_map); // == false ``` -Note: See the predefined -[namespace Map](../reference/map-reference) - -## Updating +## Updating elements -Previous sections show how to add and remove a binding from a given -map. The function `Map.update` can do both depending whether some -value is given for the new binding or not: in the former case, a new -binding is added (and replaces any previous binding with the same -key); in the latter case, any binding with the same key is removed and -a new map is returned. +Previous sections show how to add and remove an element from a map. +The function `Map.update` can do both depending whether some value is given for the new binding or not. + +To update a map in this way, pass the key and an option with the value. +If the option is `Some(value)`, the function adds the element, replacing any element with the given key. +If the option is `None()`, the function removes the element with the given key if it exists. +In either case, the function returns a new map, as in these examples: @@ -280,16 +231,16 @@ let contains_2 = Map.mem 2 map_without_2 // = false ```jsligo group=map_updating const my_map: map = Map.literal([[1,"one"],[2,"two"]]); -const map_with_3 = Map.update (3, Some("three"), my_map); +const map_with_3 = Map.update (3, ["Some" as "Some", "three"], my_map); const contains_3 = Map.mem(3, map_with_3); // == true -const map_without_2 = Map.update(2, None(), my_map); -const contains_2 = Map.mem (2, map_without_2); // == false +const map_without_2 = Map.update(2, ["None" as "None"], my_map); +const contains_2 = Map.mem(2, map_without_2); // == false ``` -When we want to update a map, but also obtain the value of the updated -binding, we can use `Map.get_and_update`. +To simultaneously update a map and obtain the value of the updated element, use the function `Map.get_and_update`. +This function allows you to extract a value from a map for use, as in this example: @@ -298,157 +249,144 @@ binding, we can use `Map.get_and_update`. let three, map_without_3 = Map.get_and_update 3 None map_with_3 ``` -Note: See the predefined -[module Map](../reference/map-reference) - ```jsligo group=map_updating -// three == Some("three") -const [three, map_without_3] = Map.get_and_update(3, None(), map_with_3); +// three == ["Some" as "Some", "three"] +const [three, map_without_3] = Map.get_and_update(3, ["None" as "None"], map_with_3); ``` -Note: See the predefined -[namespace Map](../reference/map-reference) - -## Folding +## Working with maps as a whole + +As described earlier, you can run logic on an entire map, but not a big-map. +LIGO runs logic on entire maps by applying a *functional iterator* to each element in the map. +In JsLIGO, you can also loop through the elements in a map, but this is not possible in CameLIGO. + +### Folding maps + +A map *fold*, known in some other languages as a *reduce*, runs the same function on each element in a map and returns a single value that is the result of those functions. +The function that you pass to the `Map.fold` function receives two arguments: + +- The *accumulator*, which is the result of the previous function iteration +- The value of the current element -A *functional iterator* is a function that traverses a data structure -and calls in turn a given function over the elements of that structure -to compute some value. Another approach is sometimes possible: -*loops*. +Each iteration of the function returns a new accumulator, which is passed to the next function. +The result of the last function iteration is the return value of the `Map.fold` function. +The `Map.fold` function iterates over the map in increasing order of its keys. -There are three kinds of functional iterations over maps: the *fold*, -the *map* (not to be confused with the *map data structure*) and the -*iteration*. +The `Map.fold` function accepts these parameters: -Let us consider first here the fold, which is the most general form of -functional iteration. The folded function takes two arguments: an -*accumulator* and the structure *element* at hand, with which it then -produces a new accumulator. This enables having a partial result that -becomes complete when the traversal of the data structure is over. +1. The fold function +1. The map to fold +1. The starting value for the accumulator -The function `Map.fold` performs a fold over the binding of a map, in -increasing order of its keys. +For example, this code calculates the sum of the nats in a map. +At each iteration, the accumulator is the value of the sum of the elements up to that point. ```cameligo group=map_folding -type player = string -type abscissa = int -type ordinate = int -type move = abscissa * ordinate -type game = (player, move) map - -let horizontal_offset (g : game) : int = - let folded = fun (acc, j : int * (player * move)) -> acc + j.1.0 - in Map.fold folded g 0 +let my_map : (string, nat) map = Map.literal [ + ("Alice", 1n); + ("Bob", 4n); + ("Charlie", 5n); +] + +let fold_function = fun (acc, element : nat * (string * nat)) -> + let _key, value = element in + acc + value + +let map_sum = Map.fold fold_function my_map 0 (* 10 *) ``` -Note: See the predefined -[module Map](../reference/map-reference) +For reference, see the predefined [module Map](../reference/map-reference). ```jsligo group=map_folding -type player = string -type abscissa = int -type ordinate = int -type move = [abscissa, ordinate] -type game = map - -const horizontal_offset = (g: game): int => { - let folded = ([acc, j]: [int, [player, move]]) => acc + j[1][0]; - return Map.fold(folded, g, 0); -}; +const my_map: map = Map.literal([ + ["Alice", 1 as nat], + ["Bob", 4 as nat], + ["Charlie", 5 as nat], +]); + +const fold_function = ([acc, element]: [nat, [string, nat]]): nat => { + const [_key, value] = element; + return acc + value; +} + +const map_sum: nat = Map.fold(fold_function, my_map, 0 as nat); // 10 as nat ``` -Note: See the predefined -[namespace Map](../reference/map-reference) - -## Mapping +### Mapping maps -We may want to change all the values of a given map by applying to -them a function. This is called a *map operation*, not to be confused -with the map data structure. The predefined functional iterator -implementing the mapped operation over maps is called `Map.map`. It -takes a binding, that is, a key and its associated value in the map, -and computes a new value for that key. +The *mapping* operation (not to be confused with the map type itself) runs the same function on every value in a map and returns the resulting map. +Unlike folding, mapping operates on each element in the map independently from the others and returns a new map. -In the following example, from a map from integers to integers is made -a map whose values are the sum of the keys and values of each binding. +The function that you pass to the `Map.map` function receives the key and value of the current element and returns the new value for the same key. +You cannot change the key with this function; the new map has the same keys as the old map. + +The following example takes a map of integers and squares each integer, producing a map with the same keys and the squared values: ```cameligo group=map_mapping -let my_map : (int, int) map = Map.literal [(0,0); (1,1); (2,2)] -// plus_one = Map.literal [(0,0); (1,2); (2,4)] -let plus_one = Map.map (fun (k,v) -> k + v) my_map -``` +let my_map : (string, int) map = Map.literal [ + ("Alice", 2); + ("Bob", 5); + ("Charlie", 8); +] -Note: See the predefined -[module Map](../reference/map-reference) +let squared_map : (string, int) map = Map.map (fun (_k, v : string * int) : int -> v * v) my_map +``` ```jsligo group=map_mapping -const my_map : map = Map.literal([[0,0], [1,1], [2,2]]); -// plus_one == Map.literal([[0,0],[1,2],[2,4]]) -const plus_one = Map.map(([k,v]) => k + v, my_map); -``` +const my_map: map = Map.literal([ + ["Alice", 2], + ["Bob", 5], + ["Charlie", 8], +]); -Note: See the predefined -[namespace Map](../reference/map-reference) +const squared_map: map = Map.map(([_k, v]) => v * v, my_map); +``` -## Iterating - -An *iterated operation* is a fold over the map that returns the value -of type `unit`, that is, its only use is to produce side-effects. This -can be useful if, for example, you would like to check that each value -of a map is within a certain range, and fail with an error otherwise. +### Iterating over maps -The predefined functional iterator implementing the iterated operation -over maps is called `Map.iter`. It -takes a binding, that is, a key and its associated value in the map, -performs some side-effect and returns the unit value. +An *iterated operation* is a fold over a map that returns the value of type `unit`, that is, its only use is to produce side-effects. +For example, iterating over maps can be useful if you want to verify that each element in a map meets certain criteria, and fail with an error otherwise. -In the following example, a map is iterated to check that all its -integer values are greater than `3`. +To iterate over a map, pass the function to apply to each element to the `Map.iter` function. +This example iterates over a map of integers and fails if any of them are not greater than 3: ```cameligo group=map_iterating let assert_all_greater_than_3 (m : (int, int) map) : unit = - Map.iter (fun (_,v) -> assert (v > 3)) m // The key is discarded + Map.iter (fun (_, v) -> Assert.assert (v > 3)) m ``` -Note: See the predefined -[module Map](../reference/map-reference) - ```jsligo group=map_iterating const assert_all_greater_than_3 = - (m: map) : unit => Map.iter(([_k,v]) => assert(v > 3), m); + (m: map) : unit => Map.iter(([_k, v]) => Assert.assert(v > 3), m); ``` - -Note: See the predefined -[namespace Map](../reference/map-reference) - ## Looping @@ -456,23 +394,17 @@ Note: See the predefined There is no loop over maps in CameLIGO. -Note: See the predefined -[module Map](../reference/map-reference) - -One can iterate through all the bindings of a map, in increasing order -of the keys, thanks to a loop of the form `for (const of ) `. It means that the `` of statements (or a -single statement) will be computed once for each `` ranging -over the bindings (as pairs of keys and values) of the map `` in -increasing order. +To iterate through all of the elements in a map, in increasing order of the keys, use the `for` loop in the form `for (const of ) `. +In this loop, the `` of statements (or a single statement) runs once for each `` ranging over the elements of the map `` in increasing order. -Here is an example where the values in a map are summed up. +Here is an example that adds the values in a map: ```jsligo group=map_looping -function sum_val (m: map) { +function sum_val (m: map) { let sum = 0; // The key is discarded. for (const [_key, val] of m) sum = sum + val; @@ -480,7 +412,4 @@ function sum_val (m: map) { }; ``` -Note: See the predefined -[namespace Map](../reference/map-reference) - diff --git a/gitlab-pages/docs/data-types/numbers.md b/gitlab-pages/docs/data-types/numbers.md index 59e68f859f..e92a127975 100644 --- a/gitlab-pages/docs/data-types/numbers.md +++ b/gitlab-pages/docs/data-types/numbers.md @@ -11,10 +11,14 @@ numbers. languages, for example, `10`, `-6` and `0`, but there is only one canonical zero: `0` (so, for instance, `-0` and `00` are invalid). + + * Natural numbers are written as digits followed by the suffix `n`, like so: `12n`, `0n`, and the same restriction on zero as integers applies: `0n` is the only way to specify the natural zero. + + Contrary to integral numbers in other programming languages, numbers in LIGO have arbitrary-precision, that is, they do not overflow or underflow. @@ -71,9 +75,9 @@ const zero : int = 0 const million : int = 1_000_000 const baekman : int = 100_0000 -const zero_nat : nat = 0n -const million_nat : nat = 1_000_000n -const baekman_nat : nat = 100_0000n +const zero_nat : nat = 0 as nat +const million_nat : nat = 1_000_000 as nat +const baekman_nat : nat = 100_0000 as nat ``` @@ -105,7 +109,7 @@ let two : nat = abs 2 // Explicit cast from int to nat ```jsligo group=casting -const one : int = int(1n); // Explicit cast from nat to int +const one : int = int(1 as nat); // Explicit cast from nat to int const two : nat = abs(2); // Explicit cast from int to nat ``` @@ -121,7 +125,7 @@ natural number for all inputs. -```cameligo group=additing +```cameligo group=adding let a : int = 5 + 10 // int + int yields int let b : nat = 5n + 10n // nat + nat yields nat let c : int = 5n + 10 // nat + int yields int @@ -133,12 +137,12 @@ let d : int = 10 + 5n // int + nat yields int -```jsligo group=additing +```jsligo group=adding const a : int = 5 + 10; // int + int yields int -const b : nat = 5n + 10n; // nat + nat yields nat -const c : int = 5n + 10; // nat + int yields int -const d : int = 10 + 5n; // int + nat yields int -// const error : nat = 5n + 10; +const b : nat = (5 as nat) + (10 as nat); // nat + nat yields nat +const c : int = (5 as nat) + 10; // nat + int yields int +const d : int = 10 + (5 as nat); // int + nat yields int +// const error : nat = (5 as nat) + 10; ``` @@ -168,10 +172,10 @@ let d : int = 5 - 10n // int - nat yields int ```jsligo group=subtracting const a : int = 5 - 10; // int - int yields int -const b : int = 5n - 2n; // nat - nat yields int -const c : int = 10n - 5; // nat - int yields int -const d : int = 5 - 10n; // int - nat yields int -// const error : nat = 5n - 2n; +const b : int = (5 as nat) - (2 as nat); // nat - nat yields int +const c : int = (10 as nat) - 5; // nat - int yields int +const d : int = 5 - (10 as nat); // int - nat yields int +// const error : nat = (5 as nat) - (2 as nat); ``` @@ -195,8 +199,8 @@ let b : int = -5n // - nat yields int ```jsligo group=negating const a : int = -5; // - int yields int -const b : int = -5n; // - nat yields int -// const error : nat = -5n; +const b : int = -(5 as nat); // - nat yields int +// const error : nat = -(5 as nat); ``` @@ -222,9 +226,9 @@ let d : int = 5 * 10n // int * nat yields int ```jsligo group=multiplying const a : int = 5 * 10; // int * int yields int -const b : nat = 5n * 2n; // nat * nat yields nat -const c : int = 10n * 5; // nat * int yields int -const d : int = 5 * 10n; // int * nat yields int +const b : nat = (5 as nat) * (2 as nat); // nat * nat yields nat +const c : int = (10 as nat) * 5; // nat * int yields int +const d : int = 5 * (10 as nat); // int * nat yields int ``` @@ -254,9 +258,9 @@ let d : int = 10 / 3n // int / nat yields int ```jsligo group=dividing const a : int = 10 / 3; // int / int yields int -const b : nat = 10n / 3n; // nat / nat yields nat -const c : int = 10n / 3; // nat / int yields int -const d : int = 10 / 3n; // int / nat yields int +const b : nat = (10 as nat) / (3 as nat); // nat / nat yields nat +const c : int = (10 as nat) / 3; // nat / int yields int +const d : int = 10 / (3 as nat); // int / nat yields int ``` @@ -284,19 +288,141 @@ let c : nat = 120 mod 9n // int mod nat yields nat The binary operator `%` returns the positive modulo of the Euclidean division, that is, the following holds: -> (n*(a/n)+(a%n) == a) && (0n <= a % n) && (a % n < abs(n)) +> (n*(a/n)+(a%n) == a) && ((0 as nat) <= a % n) && (a % n < abs(n)) It is overloaded as the Euclidean division `/` to allow for all four combinations of natural numbers and integers. ```jsligo group=mod -const a : nat = 120 % 9; // int % int yields nat -const b : nat = 120n % 9; // nat % int yields nat -const c : nat = 120n % 9n; // nat % nat yields nat -const d : nat = 120 % 9n; // int % nat yields nat +const a : nat = 120 % 9; // int % int yields nat +const b : nat = (120 as nat) % 9; // nat % int yields nat +const c : nat = (120 as nat) % (9 as nat); // nat % nat yields nat +const d : nat = 120 % (9 as nat); // int % nat yields nat +``` + + + + + +For cases when you need both the quotient and the remainder, LIGO +provides the `ediv` operation. `ediv x y` returns `Some (quotient, +remainder)`, unless `y` is zero, in which case it returns `None`. The +function `ediv` is overloaded to accept all the combinations (4) of +natural and integer numbers: + +```cameligo group=euclidean +// All below equal Some (7,2) +let ediv1 : (int * nat) option = ediv 37 5 +let ediv2 : (int * nat) option = ediv 37n 5 +let ediv3 : (nat * nat) option = ediv 37n 5n +let ediv4 : (int * nat) option = ediv 37 5n +``` + + + + + +For cases when you need both the quotient and the remainder, LIGO +provides the `ediv` operation. `ediv(x,y)` returns `Some (quotient, +remainder)`, unless `y` is zero, in which case it returns `None`. The +function `ediv` is overloaded to accept all the combinations (4) of +natural and integer numbers: + +```jsligo group=euclidean +// All below equal Some (7,2) +const ediv1: option<[int, nat]> = ediv(37, 5); +const ediv2: option<[int, nat]> = ediv(37 as nat, 5); +const ediv3: option<[nat, nat]> = ediv(37 as nat, 5 as nat); +const ediv4: option<[int, nat]> = ediv(37, 5 as nat); +``` + + + +The `ediv` operation returns an option type which is `Some` if the result is defined and `None` if it is not, as when you try to divide by zero. +To handle option types, see [Matching](./variants#matching). + +## Checking positivity + +You can check if a value is a natural number (`nat`) by using a +predefined cast function which accepts an integer (`int`) and returns +an optional natural number (`nat`): if the result is `None`, then the +given integer was positive, otherwise the corresponding natural number +`n` is given with `Some(n)`. + + + +```cameligo group=positive +let one_is_nat : nat option = is_nat (1) ``` -> It is possible to obtain both the quotient and remainder together, by -> means of the predefined function `ediv`: See [Euclidean division](./variants#euclidean-division). + + +```jsligo group=positive +const one_is_nat: option = is_nat(1); +``` + + + + + +## Bitwise operations + +You can perform bitwise operations with numbers in these cases: + +```cameligo group=bitwise +// Bitwise and (first operand can be int or nat) +let four : nat = 4n land 4n // 4 +// Other bitwise operations require two nats +let four_ : nat = 7 land 4n // 4 +// Bitwise or +let seven : nat = 7n lor 4n // 7 +// Bitwise xor +let three : nat = 7n lxor 4n // 3 +// Bitwise shift left +let fourteen : nat = 7n lsl 1n // 14 +// Bitwise shift right +let seven_ : nat = 14n land 1n // 7 +``` + + + + + +## Increment and decrement operators + +The increment operator (`++`) adds one to a number, and the decrement operator (`--`) subtracts one from a number. + +You can use these operators as independent statements, as in these examples: + +```jsligo test-ligo group=increment_ops +const testIncDecIndependent = (() => { + let value = 0; + value++; + Assert.assert(value == 1); + value--; + Assert.assert(value == 0); +})(); +``` + +You can also use these operators in expressions that do other things. +The order of operations for the expressions depends on whether the operator is before or after the value: + +- In the **prefix** position (`++p`) the operator increments the value and returns the updated value for use in the current expression. + +- In the **postfix** position (`p++`) the operator increments the value but returns the old value before the increment for use in the current expression. + +```jsligo test-ligo group=increment_ops +const testIncEmbedded = (() => { + let value = 0; + // Prefix increment operator adds one immediately + Assert.assert(++value == 1); + Assert.assert(value == 1); + // Postfix increment operator adds one after the expression is evaluated + Assert.assert(value++ == 1); + Assert.assert(value == 2); +})(); +``` + + diff --git a/gitlab-pages/docs/data-types/parametric_types.md b/gitlab-pages/docs/data-types/parametric_types.md index 1dc41c5417..c8e5320ff0 100644 --- a/gitlab-pages/docs/data-types/parametric_types.md +++ b/gitlab-pages/docs/data-types/parametric_types.md @@ -143,12 +143,12 @@ let rev (type a) (xs : a list) : a list = ```jsligo group=poly function rev (xs : list) : list { const rev = ([xs, acc] : [list, list]) : list => - match(xs) { - when([]): acc; - when([y,...ys]): rev([ys, [y,...acc]]) - }; + $match(List.head_and_tail(xs), { + "None": () => acc, + "Some": ([y,ys]) => rev([ys, [y,...acc]]) + }); - return rev([xs, ([] as list)]); + return rev([xs, []]); }; ``` @@ -169,11 +169,11 @@ let lnat : nat list = rev [1n; 2n; 3n] ```jsligo group=poly const lint : list = rev([1, 2, 3]); -const lnat : list = rev([1n, 2n, 3n]); +const lnat : list = rev([(1 as nat), (2 as nat), (3 as nat)]); ``` During compilation, LIGO *monomorphises* polymorphic function into specific instances, resulting in Michelson code that does not contain polymorphic function declarations. - \ No newline at end of file + diff --git a/gitlab-pages/docs/data-types/records.md b/gitlab-pages/docs/data-types/records.md index 4379a2c246..fc07257675 100644 --- a/gitlab-pages/docs/data-types/records.md +++ b/gitlab-pages/docs/data-types/records.md @@ -1,22 +1,15 @@ --- title: Records +jsligoTitle: Objects --- import Syntax from '@theme/Syntax'; -So far, we have seen relatively simple data types. LIGO also offers -more complex built-in constructs, such as *records*. - -Records are one-way data of different types can be packed into a -single type. A record is made of a set of *fields*, which are made of -a *field name* and a *field type*. Given a record, the value bound to -a field is accessed by giving its name to the selection operator -"`.`". - -Let us first consider an example of record type declaration. - +Records are a structured data type that can include one or more fields, each with a name and a type. +A record type can contain any number of different data types, as in this example: + ```cameligo group=records1 type user = { id : nat; @@ -29,7 +22,10 @@ type user = { -```jsligo group=records1 +As in JavaScript, objects (called *records* in previous versions of JsLIGO), are a structured data type that can include one or more fields, each with a name and a type. +A record type can contain any number of different data types, as in this example: + +```jsligo group=objects1 type user = { id : nat, is_admin : bool, @@ -41,7 +37,7 @@ type user = { -And here is how a record value is defined: +To create a variable of a record type, specify the name and value of each field, as in this example: ```cameligo group=records1 let alice : user = { @@ -55,27 +51,25 @@ let alice : user = { -And here is how a record value is defined: +To create a variable of an object type, specify the name and value of each field, as in this example: -```jsligo group=records1 +```jsligo group=objects1 const alice : user = { - id : 1n, + id : 1 as nat, is_admin : true, name : "Alice" }; ``` -> Note: A semicolon `;` can also separate fields instead of a -> comma. +You can also use a semicolon (`;`) to separate the fields in a record instead of a comma. ## Accessing - +To get the value of a field, use a period (`.`) as the selection operator, as in this example: -If we want the contents of a given field, we use the selection operator -"`.`", like so: + ```cameligo group=record_access type user = { @@ -98,10 +92,7 @@ let is_alice_admin : bool = alice.is_admin // = true -If we want to access a field, we use the selection operator "`.`" -followed by the field name, like so: - -```jsligo group=record_access +```jsligo group=object_access type user = { login : string, name : string @@ -113,20 +104,25 @@ type account = { is_admin : bool }; -const user : user = {login: "al", name: "Alice"}; -const alice : account = {user, id: 5, is_admin: true}; +const user: user = {login: "al", name: "Alice"}; +const alice: account = {user, id: 5, is_admin: true}; const is_alice_admin = alice.is_admin; // == true +const alice_name = alice.user.name; // == "Alice" ``` -Instead of the field name, we can provide between square brackets a -string that contains the field name, or an integer that is the index -of the field in the record declaration: +Instead of using the field name and the selection operator, you can put the name of the field in square brackets, as in this example: + +```jsligo group=object_access +const alice_id = alice["id"]; // 5 +``` + +Unlike in JavaScript, you cannot use the name of a variable in square brackets to specify which field to retrieve. -We can also access fields of a record using a destructuring syntax, +You can also access fields of a record using a destructuring syntax, known as _pattern matching_, which enables accessing multiple fields of a record in parallel, like so: @@ -140,12 +136,12 @@ let user_to_triple (a : account) = -We can also access fields of a record using the destructuring +You can also access fields of a record using the destructuring syntax, known as _pattern matching_, which allows accessing multiple fields of a record in parallel, like so: -```jsligo group=record_access -function userToTuple (a : account) { +```jsligo group=object_access +function userToTuple (a: account) { const {user, id, is_admin} = a; return [user, id, is_admin]; } @@ -155,9 +151,7 @@ function userToTuple (a : account) { -If we do not use some of the fields we matched, we assign them the -special variable `_`, to avoid triggering a warning about an unused -variable, like so: +If you do not use one or more of the fields in the record, assign them the special variable name `_` to avoid triggering a warning about an unused variable, like so: ```cameligo group=record_access let get_id (a : account) = @@ -169,11 +163,10 @@ let get_id (a : account) = -We can ignore some fields by calling the predefined function -`ignore` on them, like so: +If you do not use one or more of the fields in the object, use the predefined function `ignore` on them to avoid triggering a warning about an unused variable, like so: -```jsligo group=record_access -function getId (a : account) { +```jsligo group=object_access +function getId (a: account) { let {user, id, is_admin} = a; ignore([user, is_admin]); // To avoid a warning return id; @@ -184,9 +177,11 @@ function getId (a : account) { ## Assigning + + Given a record, it is a common design pattern to update only a small number of its fields. Instead of forcing the programmer to copy the -remaining, unchanged fields, CameLIGO offers a way to only update the +remaining, unchanged fields, CameLIGO offers a way to update only the fields that are modified. One way to understand the update of records is the *functional @@ -196,8 +191,6 @@ updated record. Let us consider defining a function that translates three-dimensional points on a plane. - - The syntax for the functional updates of record in CameLIGO follows that of OCaml: @@ -219,28 +212,48 @@ let xy_translate (p, vec : point * vector) : point = -The syntax for the functional updates is: +You can change the fields of an object that is declared as a variable, as in this example: -```jsligo group=record_update +```jsligo group=object_update +function my_function () { + let my_object = {a: 1, b: 2}; + my_object.a = 5; + my_object["b"] = 3; +} +``` + +:::note + +Unlike in JavaScript, you cannot change the fields of an object that is declared as a constant. + +::: + +Similarly, you cannot add fields to an object after you create it, regardless of whether it is declared as a constant or a variable. +To add fields, you can use a *functional update* to create a new object that has all of the fields of one or more other objects with the updates that you want. +As in JavaScript, this type of update uses the `...` operator, as in this example: + +```jsligo group=object_update type point = {x: int, y: int, z: int} type vector = {dx: int, dy: int} -const origin = {x: 0, y: 0, z: 0}; +const origin: point = {x: 0, y: 0, z: 0}; const xy_translate = (p: point, vec: vector) => ({...p, x: p.x + vec.dx, y: p.y + vec.dy}); ``` -> It is important to understand that `p` has not been changed by the -> functional update: a nameless new version of it has been created and -> returned. +:::note - +In the previous example, the constant `p` has not been changed by the functional update; a nameless new version of it has been created and returned. -### Nested updates +::: + + +### Nested updates + A unique feature of CameLIGO is the ability to perform nested updates on records. For example, given the following record declarations: @@ -269,60 +282,38 @@ let change_login (login : string) (account : account) : account = - - - -JsLIGO does not support functional updates of nested records. For -example, if you have the following record declarations: - -```jsligo group=record_nested_update -type user = { - login : string, - name : string -}; - -type account = { - user : user, - id : int, - is_admin : bool -}; -``` - -You can update the record `user` nested inside `account` the long way: - -```jsligo group=record_nested_update -const change_login = (login: string, account: account) : account => - ({...account, user: {...account.user, login}}); -``` - - - ## Comparing + + Record types are comparable types, which means that their values can -be implicitly compared for equality, and records can be used as keys -in [sets](./sets.md) or [maps](./maps.md). By -default, the implicit, total order over records is **undefined and -implementation-dependent** --- ultimately, the order is determined by +be implicitly compared for equality. As a result, records can be used +as keys in [sets](./sets.md) and [maps](./maps.md). By default, the +implicit, total order over records is **undefined and +implementation-dependent** — ultimately, the order is determined by the translated Michelson type. - - When using the `[@layout "comb"]` (or `[@layout:comb]`) attribute, -fields are translated in Michelsom with their order as written in the +fields are translated in Michelson with their order as written in the source code, and records are then ordered lexicographically (that is, when two fields of the same name have the same values, another field -is compared, much rather like ordering two English words according to -the alphabet). +is compared, like ordering two English words according to the alphabet). +Object types are comparable types, which means that their values can +be implicitly compared for equality. As a result, objects can be used +as keys in [sets](./sets.md) and [maps](./maps.md). By default, the +implicit, total order over objects is **undefined and +implementation-dependent** — ultimately, the order is determined by +the translated Michelson type. + When using the `@layout("comb")` decorator, fields are translated in -Michelsom with their order as written in the source code, and records +Michelson with their order as written in the source code, and objects are then ordered lexicographically (that is, when two fields of the -same name have the same values, another field is compared, much rather +same name have the same values, another field is compared, like ordering two English words according to the alphabet). diff --git a/gitlab-pages/docs/data-types/sets.md b/gitlab-pages/docs/data-types/sets.md index 6feedf1fad..02c0bc7e63 100644 --- a/gitlab-pages/docs/data-types/sets.md +++ b/gitlab-pages/docs/data-types/sets.md @@ -4,19 +4,20 @@ title: Sets import Syntax from '@theme/Syntax'; -Sets are unordered collections of values of the same type, like lists -are ordered collections. Like the mathematical sets and lists, sets -can be empty and, if not, elements of sets in LIGO are *unique*, -whereas they can be repeated in a *list*. +Sets are collections of elements of the same type without any duplicates. +Sets can be empty of contain any number of elements. -Like lists, the type of sets is parameterised over the type of its -elements. Like list elements, set elements must all have the same -type. +Sets are similar to to [lists](./lists) because they are both collections of elements of the same type. +The main differences between sets and lists are: -The empty set is denoted by the predefined value `Set.empty`. A -non-empty set can be built by using the function `Set.literal` which -takes a list of *literal elements* and returns a set containing them, -and only them. +- Sets cannot contain duplicate entries, while lists can +- Sets are always automatically sorted, while you can put the elements of a list in any order + +Like lists, the type of sets is parameterised over the type of its elements. +Like list elements, set elements must all have the same type. + +The empty set is denoted by the predefined value `Set.empty`. +To create a non-empty set, pass a list of literal values to the function `Set.literal`, which returns a set containing them, minus any duplicates. @@ -25,11 +26,21 @@ let empty_set : int set = Set.empty let set1 : int set = Set.literal [3; 2; 2; 1] ``` -> Note: The element `2` is repeated in the list, but not in the set -> made from it. +:::note + +The element `2` is repeated in the list, but not in the set made from it. +LIGO automatically removes duplicate elements in sets. + +::: + +To build a set from a list of arbitrary values (including variables and expressions, not just literal values), you must use `Set.of_list` instead of `Set.literal`, as in this example: + +```cameligo group=sets +let two = 2 +let set2 : int set = Set.of_list [3; two; two; two + two; 1] +``` -Note: See the predefined -[module Set](../reference/set-reference/?lang=cameligo) +For functions that work with sets, see the predefined [module Set](../reference/set-reference/?lang=cameligo). @@ -40,30 +51,29 @@ const empty_set: set = Set.empty; const set1: set = Set.literal([3, 2, 2, 1]); ``` -> Note: The element `2` is repeated in the list, but not in the set -> made from it. +:::note + +The element `2` is repeated in the list, but not in the set made from it. +LIGO automatically removed the duplicate. -Note: See the predefined -[namespace Set](../reference/set-reference/?lang=jsligo) +::: -If you want to build a big set from an arbitrary list of arbitrary -values (not just literal values), then you must use `Set.of_list` -instead of `Set.literal`: +To build a set from a list of arbitrary values (including variables and expressions, not just literal values), you must use `Set.of_list` instead of `Set.literal`, as in this example: ```jsligo group=sets const two = 2; -const set2 : set = Set.of_list([3, two, two, 1]); +const set2 : set = Set.of_list([3, two, two, two + two, 1]); ``` + +For functions that work with sets, see the predefined [namespace Set](../reference/set-reference/?lang=jsligo). + -Set elements are internally sorted by increasing values, so the type -of the elements must be *comparable*, that is, they obey a total order -(any two elements can be compared). +Set elements are automatically sorted by increasing values, so the type of the elements must be *comparable*, that is, they obey a total order (any two elements can be compared). ## Sizing -The predefined functions `Set.size` and `Set.cardinal` return the -number of elements in a given set. +The predefined functions `Set.size` and `Set.cardinal` return the number of elements in a given set. @@ -72,26 +82,20 @@ let my_set : int set = Set.literal [3; 2; 2; 1] let cardinal : nat = Set.size my_set // = 3 ``` -Note: See the predefined -[module Set](../reference/set-reference/?lang=cameligo) - ```jsligo group=cardinal const my_set: set = Set.literal([3, 2, 2, 1]); -const cardinal : nat = Set.size(my_set); // == 3 +const cardinal: nat = Set.size(my_set); // == 3 ``` -Note: See the predefined -[namespace Set](../reference/set-reference/?lang=jsligo) - ## Searching -The predicate `Set.mem` tests for membership in a given set. +The function `Set.mem` tests for membership in a given set. @@ -100,9 +104,6 @@ let my_set : int set = Set.literal [3; 2; 2; 1] let contains_3 : bool = Set.mem 3 my_set // = true ``` -Note: See the predefined -[module Set](../reference/set-reference/?lang=cameligo) - @@ -112,16 +113,12 @@ const my_set: set = Set.literal([3, 2, 2, 1]); const contains_3: bool = Set.mem(3, my_set); // == true ``` -Note: See the predefined -[namespace Set](../reference/set-reference/?lang=jsligo) - -## Adding +## Adding elements -Adding an element to a set is done by calling the function -`Set.add`. If the element was already present in the given set, the -resulting set is the same as the original one. +To add an element to a set, pass the element and the set to the `Set.add` function, which returns a new set. +If the element was already present in the given set, the resulting set is the same as the original one. @@ -130,9 +127,6 @@ let my_set : int set = Set.literal [3; 2; 2; 1] let with_4 : int set = Set.add 4 my_set ``` -Note: See the predefined -[module Set](../reference/set-reference/?lang=cameligo) - @@ -142,16 +136,12 @@ const my_set: set = Set.literal([3, 2, 2, 1]); const with_4: set = Set.add(4, my_set); ``` -Note: See the predefined -[namespace Set](../reference/set-reference/?lang=jsligo) - -## Removing +## Removing elements -The function `Set.remove` creates a set containing the elements of a -given set, without a given element. If the element is not already -present, the new set is the same as the old one, as expected. +To remove an element from a set, pass the element and the set to the `Set.remove` function, which returns a new set. +If the element is not already present, the new set is the same as the old one. @@ -161,9 +151,6 @@ let new_set = Set.remove 3 my_set let contains_3 = Set.mem 3 new_set // = false ``` -Note: See the predefined -[module Set](../reference/set-reference/?lang=cameligo) - @@ -174,95 +161,61 @@ const new_set = Set.remove(3, my_set); const contains_3 = Set.mem(3, new_set); // == false ``` -Note: See the predefined -[namespace Set](../reference/set-reference/?lang=jsligo) - -## Updating +## Accessing elements -Previous sections show how to add and remove an element from a given -set. The function `Set.update` can do both depending on a boolean -value: if true, then the given value will be added to the set, -otherwise it will be removed (if present). +You cannot access elements directly in sets, such as getting the element with an arbitrary index. +The main way to get elements from sets is to use `Set.mem` to see if a specified element is in a set. +You can also use loops and the other functions listed below to process a set and access specific elements. - +## Processing sets -```cameligo group=set_updating -let nats : int set = Set.literal [3; 2; 2; 1] -let set_with_5 = Set.update 5 true nats -let set_without_3 = Set.update 3 false nats -``` +Aside from adding and removing elements from sets, you can use loops and functions that iterate over every element in a set. - +### Looping over sets - + -```jsligo group=set_updating -const nats: set = Set.literal([3, 2, 2, 1]); -const set_with_5 = Set.update(5, true, nats); -const set_without_3 = Set.update(3, false, nats); -``` +There is no loop over sets in CameLIGO. -The function `Set.update` implements a one-value update. Sometime we -would like to provide a function that is applied in turn to *all* the -elements of the set, and specifies whether the element at hand has to -be discarded or replaced by a computed value. This is what -`Set.filter_map` does. + -As an example, let us consider a function that removes all the even -numbers from a set. +You can use a `for` loop to iterate over the elements in the set, in increasing order, in the form `for (const of ) `. +This statement means that the block of statements (or a single statement) runs once for each element in the set (``) ranging over the elements of the set from left to right. - +Here is an example where the integers in a set are summed up. -```cameligo group=set_updating -let f x = if x mod 2 = 0n then None else Some x -// odds = Set.literal [3, 1] -let odds = Set.filter_map f nats +```jsligo group=set_looping +function sum_elt (s: set) { + let sum = 0; + for (const e of s) sum = sum + e; + return sum; +}; ``` -Note: See the predefined -[module Set](../reference/set-reference/?lang=cameligo) - - +### Folding sets -```jsligo group=set_updating -const f = x => x % 2 == 0n ? None() : Some(x); -// odds == Set.literal([3, 1]) -const odds = Set.filter_map(f, nats); -``` - -Note: See the predefined -[namespace Set](../reference/set-reference/?lang=jsligo) +Folding a set runs the same function on each element in a set and returns a single value. +The function takes two arguments: an *accumulator* and the current set element, with which it produces a new accumulator to pass to the next iteration of the function. +Folding lists allows you to compute a partial result that becomes complete when the traversal of the data structure is over. - +Two ways to fold sets are available: -## Folding +- The function `Set.fold` iterates over the set in increasing order of its elements. +- The function `Set.fold_desc` iterates over the set in decreasing order of its elements. -A *functional iterator* is a function that traverses a data structure -and calls in turn a given function over the elements of that structure -to compute some value. Another approach is sometimes possible: -*loops*. +These functions take the same parameters, but the function that they run on the set elements is different. +In the `Set.fold` function, the first parameter is the accumulator and the second parameter is the current element in the set. +In the `Set.fold_desc` function, the first parameter is the current element in the set and the second parameter is the accumulator. -There are three kinds of functional iterations over sets: the *fold*, -the *map* (not to be confused with the *map data structure*) and the -*iteration*. +For a more detailed explanation of fold functions, see [Folding lists](./lists#folding-lists). -Let us consider first here the fold, which is the most general form of -functional iteration. The folded function takes two arguments: an -*accumulator* and the structure *element* at hand, with which it then -produces a new accumulator. This enables having a partial result that -becomes complete when the traversal of the data structure is over. - -The function `Set.fold` performs a fold over a set, in increasing -order of its elements. The function `Set.fold_desc` folds in -decreasing order. The different in their types is the type of the -folded operation: with `Set.fold`, that function takes the accumulator -first, whereas with `Set.fold_desc`, the accumulator comes second. +These examples use fold functions to turn a set into a list that is either sorted in increasing or decreasing order: @@ -274,33 +227,25 @@ let incr : int list = Set.fold (fun (a,i) -> i::a) s [] let decr : int list = Set.fold_desc (fun (i,a) -> i::a) s [] ``` -Note: See the predefined -[module Set](../reference/set-reference/?lang=cameligo) - ```jsligo group=set_folding -const s : set = Set.literal([1, 2, 3]); +const s: set = Set.literal([1, 2, 3]); // incr == [3, 2, 1] -const incr : list = Set.fold (([a,i]) => ([i,...a] as list), s, []); +const incr: list = Set.fold (([a,i]) => ([i,...a] as list), s, []); // decr == [1, 2, 3] -const decr : list = Set.fold_desc (([i,a]) => ([i,...a] as list), s, []); +const decr: list = Set.fold_desc (([i,a]) => ([i,...a] as list), s, []); ``` -Note: See the predefined -[namespace Set](../reference/set-reference/?lang=jsligo) - -## Mapping +### Mapping sets -We may want to change all the elements of a given set by applying to -them a function. This is called a *map operation*, not to be confused -with the map data structure. The predefined functional iterator -implementing the mapped operation over sets is called `Set.map` and is -used as follows. +Mapping a set runs the same function on each element in a set and returns a new set with the result of each function operation. +This is called a *map operation*, not to be confused with the map data type. +To map a set, use the `Set.map` function, as in this example: @@ -310,9 +255,6 @@ let s : int set = Set.literal [5; 1; 2; 2] let plus_one : int set = Set.map (fun i -> i + 1) s ``` -Note: See the predefined -[module Set](../reference/set-reference/?lang=cameligo) - @@ -323,77 +265,82 @@ const s: set = Set.literal([5,1,2,2]); const plus_one: set = Set.map(i => i + 1, s); ``` -Note: See the predefined -[namespace Set](../reference/set-reference/?lang=jsligo) - +### Updating elements -## Iterating - -An *iterated operation* is a fold over a set that returns the value of -type `unit`, that is, its only use is to produce side-effects. This -can be useful if, for example, you would like to check that each -element of a set is within a certain range, and fail with an error -otherwise. - -The predefined functional iterator implementing the iterated operation -over sets is called `Set.iter`. In the following example, a set is -iterated to check that all its elements (integers) are greater than -`3`. +Previous sections show how to add and remove an element from a given set. +The function `Set.update` can do both depending on a boolean value: if true, then the given value is added to the set, otherwise it is removed (if present). -```cameligo group=set_iterating -let assert_all_greater_than_3 (s : int set) : unit = - Set.iter (fun i -> assert (i > 3)) s +```cameligo group=set_updating +let nats : int set = Set.literal [3; 2; 2; 1] +let set_with_5 = Set.update 5 true nats +let set_without_3 = Set.update 3 false nats ``` -Note: See the predefined -[module Set](../reference/set-reference/?lang=cameligo) - -```jsligo group=set_iterating -const assert_all_greater_than_3 = - (s: set) : unit => Set.iter(i => assert(i > 3), s); +```jsligo group=set_updating +const nats: set = Set.literal([3, 2, 2, 1]); +const set_with_5 = Set.update(5, true, nats); +const set_without_3 = Set.update(3, false, nats); ``` -Note: See the predefined -[namespace Set](../reference/set-reference/?lang=jsligo) - -## Looping +Similarly, the function `Set.filter_map` applies the same function to every element in a set. +If the function returns a `None` option, the element stays the same, but if it returns a `Some` option, the element is replaced with the value in the option. + +This example uses `Set.filter_map` to remove all even numbers from a set: -There is no loop over lists in CameLIGO. -Note: See the predefined -[module Set](../reference/set-reference/?lang=cameligo) +```cameligo group=set_updating +let f x = if x mod 2 = 0n then None else Some x +// odds = Set.literal [3, 1] +let odds = Set.filter_map f nats +``` -One can iterate through all the elements of a set, in increasing -order, thanks to a loop of the form `for (const of ) `. It means that the `` of statements (or a single -statement) will be computed once for each `` ranging over the -elements of the set `` in increasing order. +```jsligo group=set_updating +const f = x => x % 2 == (0 as nat) ? + ["None" as "None"] : + ["Some" as "Some", x]; +// odds == Set.literal([3, 1]) +const odds = Set.filter_map(f, nats); +``` -Here is an example where the integers in a set are summed up. + -```jsligo group=set_looping -function sum_elt (s: set) { - let sum = 0; - for (const e of s) sum = sum + e; - return sum; -}; +### Iterating over sets + +The `Set.iter` function is similar to the `Set.map` function because it runs the same function on every element in a set. +However, the `Set.iter` function returns a value of type unit, so it cannot change the set. +Therefore, this function is useful only to produce side effects, such as checking that each element of a set is within a certain range, and fail with an error otherwise. + +This example iterates over a set to check that all its elements (integers) are greater than 3: + + + +```cameligo group=set_iterating +let assert_all_greater_than_3 (s : int set) : unit = + Set.iter (fun i -> Assert.assert (i > 3)) s ``` -Note: See the predefined -[namespace Set](../reference/set-reference/?lang=jsligo) + + + + +```jsligo group=set_iterating +const assert_all_greater_than_3 = + (s: set): unit => Set.iter(i => Assert.assert(i > 3), s); +``` diff --git a/gitlab-pages/docs/data-types/src/big_maps/big_map_searching.jsligo b/gitlab-pages/docs/data-types/src/big_maps/big_map_searching.jsligo index c0e2d70b2e..9713bdd52f 100644 --- a/gitlab-pages/docs/data-types/src/big_maps/big_map_searching.jsligo +++ b/gitlab-pages/docs/data-types/src/big_maps/big_map_searching.jsligo @@ -1,10 +1,8 @@ -const my_map: big_map = - Big_map.literal([[1,"one"],[2,"two"]]); -const contains_2: bool = Big_map.mem(2, my_map); // == true -const v : option = Big_map.find_opt(2, my_map); -let force_access = (key, map) => { - return match(Big_map.find_opt (key, map)) { - when(Some(value)): value; - when(None): failwith("No value.") - }; -}; \ No newline at end of file +const my_big_map: big_map = + Big_map.literal([[1, "one"], [2, "two"]]); +const contains_2: bool = Big_map.mem(2, my_big_map); // == true +const value_option: option = Big_map.find_opt(2, my_big_map); +const value = $match(value_option, { + "Some": value => value, + "None": () => failwith("No value."), +}); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/big_maps/big_map_searching.mligo b/gitlab-pages/docs/data-types/src/big_maps/big_map_searching.mligo index 7583e50a42..7b99be7d76 100644 --- a/gitlab-pages/docs/data-types/src/big_maps/big_map_searching.mligo +++ b/gitlab-pages/docs/data-types/src/big_maps/big_map_searching.mligo @@ -1,8 +1,7 @@ -let my_map : (int, string) big_map = +let my_big_map : (int, string) big_map = Big_map.literal [(1,"one"); (2,"two")] -let contains_2 : bool = Big_map.mem 2 my_map // = true -let v : string option = Big_map.find_opt 2 my_map -let force_access key map = - match Big_map.find_opt key map with +let contains_2 : bool = Big_map.mem 2 my_big_map // = true +let value_option : string option = Big_map.find_opt 2 my_big_map +let value = match value_option with Some value -> value | None -> failwith "No value." \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/big_maps/big_map_updating.jsligo b/gitlab-pages/docs/data-types/src/big_maps/big_map_updating.jsligo index 1e246cee3c..bc2ee6ca48 100644 --- a/gitlab-pages/docs/data-types/src/big_maps/big_map_updating.jsligo +++ b/gitlab-pages/docs/data-types/src/big_maps/big_map_updating.jsligo @@ -1,9 +1,9 @@ const my_map: big_map = Big_map.literal([[1,"one"],[2,"two"]]); -const map_with_3 = Big_map.update (3, Some("three"), my_map); +const map_with_3 = Big_map.update (3, ["Some" as "Some", "three"], my_map); const contains_3 = Big_map.mem(3, map_with_3); // == true -const map_without_2 = Big_map.update(2, None(), my_map); +const map_without_2 = Big_map.update(2, ["None" as "None"], my_map); const contains_2 = Big_map.mem (2, map_without_2); // == false -// three == Some("three") +// three == ["Some" as "Some", "three"] const [three, map_without_3] = - Big_map.get_and_update(3, None(), map_with_3); \ No newline at end of file + Big_map.get_and_update(3, ["None" as "None"], map_with_3); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/big_maps/big_maps.jsligo b/gitlab-pages/docs/data-types/src/big_maps/big_maps.jsligo index ac5a040503..78d8fa12bc 100644 --- a/gitlab-pages/docs/data-types/src/big_maps/big_maps.jsligo +++ b/gitlab-pages/docs/data-types/src/big_maps/big_maps.jsligo @@ -4,7 +4,7 @@ type dictionary = big_map; const empty_dict: dictionary = Big_map.empty; -const dictionary : dictionary = +const dictionary: dictionary = Big_map.literal([ ["one", (["The number 1.", "A member of a group."] as definition)], ["two", (["The number 2."] as definition)]]); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/bytes/bitwise.jsligo b/gitlab-pages/docs/data-types/src/bytes/bitwise.jsligo index ed4afd2e45..2e15fba773 100644 --- a/gitlab-pages/docs/data-types/src/bytes/bitwise.jsligo +++ b/gitlab-pages/docs/data-types/src/bytes/bitwise.jsligo @@ -8,7 +8,7 @@ const or: bytes = 0x0005 | 0x0106; // 0x0107 const xor: bytes = 0x0005 ^ 0x0106; // 0x0103 // Bitwise "shift left" -const shift_left: bytes = 0x06 << 8n; // 0x0600 +const shift_left: bytes = 0x06 << (8 as nat); // 0x0600 // Bitwise "shift right" -const shift_right: bytes = 0x0006 >> 1n; // 0x0003 \ No newline at end of file +const shift_right: bytes = 0x0006 >> (1 as nat); // 0x0003 \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/bytes/bytes.jsligo b/gitlab-pages/docs/data-types/src/bytes/bytes.jsligo index b82125379d..f434999699 100644 --- a/gitlab-pages/docs/data-types/src/bytes/bytes.jsligo +++ b/gitlab-pages/docs/data-types/src/bytes/bytes.jsligo @@ -1,12 +1,12 @@ const a : bytes = 0x70FF; -const zero : bytes = 0x; +const zero = "" as bytes; const zero_too = 0x00; -const b: bytes = bytes(123n); // 7B in hexadecimal +const b: bytes = bytes(123 as nat); // 7B in hexadecimal const c: bytes = bytes(123); const d: bytes = bytes(-123); // Two's complement const n: nat = nat(0x7B); // n == 123n const i: int = int(0x7B); // i == 123 -const from_ascii: bytes = bytes`foo`; // Not a call +const from_ascii: bytes = bytes`foo`; // Not a function call // raw == from_ascii const raw: bytes = ("666f6f" as bytes); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/bytes/packing.jsligo b/gitlab-pages/docs/data-types/src/bytes/packing.jsligo index 0576c1c86e..398c5d237e 100644 --- a/gitlab-pages/docs/data-types/src/bytes/packing.jsligo +++ b/gitlab-pages/docs/data-types/src/bytes/packing.jsligo @@ -1,4 +1,4 @@ -const id_string = (p: string) : option => { +function id_string (p: string) : option { let packed = Bytes.pack(p); return Bytes.unpack(packed); }; \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/bytes/sizing.jsligo b/gitlab-pages/docs/data-types/src/bytes/sizing.jsligo index a0ff4a4026..56af956b95 100644 --- a/gitlab-pages/docs/data-types/src/bytes/sizing.jsligo +++ b/gitlab-pages/docs/data-types/src/bytes/sizing.jsligo @@ -1 +1 @@ -const len: nat = Bytes.length(0x0AFF); // len == 2n \ No newline at end of file +const len: nat = Bytes.length(0x0AFF); // len == (2 as nat) \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/bytes/slicing.jsligo b/gitlab-pages/docs/data-types/src/bytes/slicing.jsligo index 020c2200ad..6807765a89 100644 --- a/gitlab-pages/docs/data-types/src/bytes/slicing.jsligo +++ b/gitlab-pages/docs/data-types/src/bytes/slicing.jsligo @@ -1,2 +1,2 @@ const large = 0x12345678; -const slice = Bytes.sub(1n, 2n, large); // sub == 0x3456 \ No newline at end of file +const slice = Bytes.sub(1 as nat, 2 as nat, large); // sub == 0x3456 \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/contracts/contract_of.jsligo b/gitlab-pages/docs/data-types/src/contracts/contract_of.jsligo index da79212a5c..e08710c698 100644 --- a/gitlab-pages/docs/data-types/src/contracts/contract_of.jsligo +++ b/gitlab-pages/docs/data-types/src/contracts/contract_of.jsligo @@ -1,36 +1,34 @@ type storage = int; -type @return = [list, storage]; +type return_ = [list, storage]; -namespace C { +class C { @entry - const decrement = (param: int, storage: storage) : @return => + decrement = (param: int, storage: storage) : return_ => [[], storage - param]; @entry - const increment = (param: int, storage: storage) : @return => + increment = (param: int, storage: storage) : return_ => [[], storage + param]; @entry - const reset = (_unit: unit, _storage: storage) : @return => + reset = (_unit: unit, _storage: storage) : return_ => [[], 0]; } -import Test = Test.Next; - const test_initial_storage = () : unit => { const init_storage = 42; - const fee = 0mutez; + const fee = 0 as mutez; const contract = Test.Originate.contract(contract_of(C), init_storage, fee); // Call contract through entrypoints - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("increment", contract.taddr), 15, 0tez); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("decrement", contract.taddr), 14, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("increment", contract.taddr), 15, 0 as tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("decrement", contract.taddr), 14, 0 as tez); // Call contract through `main` function - const increment_param: parameter_of C = Increment(8); - const decrement_param: parameter_of C = Decrement(3); - Test.transfer_exn(contract.taddr, increment_param, 0mutez); - Test.transfer_exn(contract.taddr, decrement_param, 0mutez); + const increment_param: parameter_of = ["Increment" as "Increment", 8]; + const decrement_param: parameter_of = ["Decrement" as "Decrement", 3]; + Test.Typed_address.transfer_exn(contract.taddr, increment_param, 0 as mutez); + Test.Typed_address.transfer_exn(contract.taddr, decrement_param, 0 as mutez); const new_storage = Test.Typed_address.get_storage(contract.taddr); Assert.assert(new_storage == init_storage + 15 - 14 + 8 - 3); diff --git a/gitlab-pages/docs/data-types/src/contracts/contract_of.mligo b/gitlab-pages/docs/data-types/src/contracts/contract_of.mligo index ad1ed127bb..438806f3b2 100644 --- a/gitlab-pages/docs/data-types/src/contracts/contract_of.mligo +++ b/gitlab-pages/docs/data-types/src/contracts/contract_of.mligo @@ -14,8 +14,6 @@ module C = struct let reset () (_ : storage) : return = [], 0 end -module Test = Test.Next - let test_initial_storage () : unit = let init_storage = 42 in let fee = 0mutez in @@ -28,8 +26,8 @@ let test_initial_storage () : unit = (* Call contract through `main` function *) let increment_param : C parameter_of = Increment 8 in let decrement_param : C parameter_of = Decrement 3 in - let _ = Test.transfer_exn contract.taddr increment_param 0mutez in - let _ = Test.transfer_exn contract.taddr decrement_param 0mutez in + let _ = Test.Typed_address.transfer_exn contract.taddr increment_param 0mutez in + let _ = Test.Typed_address.transfer_exn contract.taddr decrement_param 0mutez in let new_storage = Test.Typed_address.get_storage contract.taddr in Assert.assert (new_storage = init_storage + 15 - 14 + 8 - 3) \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/contracts/get_contract.jsligo b/gitlab-pages/docs/data-types/src/contracts/get_contract.jsligo index ac125e92eb..06fedfdc4c 100644 --- a/gitlab-pages/docs/data-types/src/contracts/get_contract.jsligo +++ b/gitlab-pages/docs/data-types/src/contracts/get_contract.jsligo @@ -5,11 +5,12 @@ type contractParam = | ["Decrement", int] | ["Increment", int]; -@entry -const callContract = (_: unit, storage: int): returnType => { +// @entry +function callContract (_: unit, storage: int): returnType { const contractAddress: address = ("KT1FpuaoBHwXMXJ6zn3F4ZhpjpPZV28MAinz" as address); const myContract: contract = Tezos.get_contract(contractAddress); - const contractArg: contractParam = Increment(4); - const operation = Tezos.transaction(contractArg, 0tez, myContract); - return [list([operation]), storage + 1] + const contractArg: contractParam = ["Increment" as "Increment", 4]; + const operation = + Tezos.Operation.transaction(contractArg, 0 as tez, myContract); + return [[operation], storage + 1] } \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/contracts/get_contract.mligo b/gitlab-pages/docs/data-types/src/contracts/get_contract.mligo index 19e913e850..a683445ce7 100644 --- a/gitlab-pages/docs/data-types/src/contracts/get_contract.mligo +++ b/gitlab-pages/docs/data-types/src/contracts/get_contract.mligo @@ -8,6 +8,8 @@ Reset [@entry] let callContract (_ : unit) (storage : int) : returnType = let contractAddress : address = ("KT1FpuaoBHwXMXJ6zn3F4ZhpjpPZV28MAinz" : address) in - let myContract: contractParam contract = Tezos.get_contract contractAddress in - let operation = Tezos.transaction (Increment 4) 0tez myContract in - [operation], storage \ No newline at end of file + let myContract : contractParam contract = + Tezos.get_contract contractAddress in + let operation = + Tezos.Operation.transaction (Increment 4) 0tez myContract + in [operation], storage \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/lists/access.jsligo b/gitlab-pages/docs/data-types/src/lists/access.jsligo new file mode 100644 index 0000000000..b79a034f9a --- /dev/null +++ b/gitlab-pages/docs/data-types/src/lists/access.jsligo @@ -0,0 +1,11 @@ +const my_list: list = [1, 2, 3]; +const head_option: option = List.head(my_list); +const head = $match(head_option, { + "Some": value => value, + "None": () => failwith("Failed to get the head of the list"), +}); +const tail_option: option> = List.tail(my_list); +const tail = $match(tail_option, { + "Some": value => value, + "None": () => failwith("Failed to get the tail of the list"), +}); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/lists/access.mligo b/gitlab-pages/docs/data-types/src/lists/access.mligo new file mode 100644 index 0000000000..2fb344b6d2 --- /dev/null +++ b/gitlab-pages/docs/data-types/src/lists/access.mligo @@ -0,0 +1,9 @@ +let my_list : int list = [1; 2; 3] +let head_option : int option = List.head my_list +let head = match head_option with +| Some value -> value +| None -> failwith "Failed to get the head of the list" +let tail_option : int list option = List.tail my_list +let tail = match tail_option with +| Some value -> value +| None -> failwith "Failed to get the tail of the list" \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/lists/consing.jsligo b/gitlab-pages/docs/data-types/src/lists/consing.jsligo index 9dce51201d..74e3caaf7f 100644 --- a/gitlab-pages/docs/data-types/src/lists/consing.jsligo +++ b/gitlab-pages/docs/data-types/src/lists/consing.jsligo @@ -1,5 +1,5 @@ -const short_list : list = [1, 2, 2]; +const short_list: list = [1, 2, 2]; // long_list == [5,1,2,2] -const long_list : list = [5, ...short_list]; +const long_list: list = [5, ...short_list]; // longer_list == [6, 5, 1, 2, 2] const longer_list = List.cons(6, long_list); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/lists/folding_lists.mligo b/gitlab-pages/docs/data-types/src/lists/folding_lists.mligo index bf4b89f35c..80e9a45c21 100644 --- a/gitlab-pages/docs/data-types/src/lists/folding_lists.mligo +++ b/gitlab-pages/docs/data-types/src/lists/folding_lists.mligo @@ -1,2 +1,2 @@ -let sum1 = List.fold_left (fun (a,i) -> a + i) 0 [1; 2; 3] -let sum2 = List.fold_right (fun (i,a) -> i + a) [1; 2; 3] 0 \ No newline at end of file +let sum1 = List.fold_left (fun (a, i) -> a + i) 0 [1; 2; 3] +let sum2 = List.fold_right (fun (i, a) -> i + a) [1; 2; 3] 0 \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/lists/list_iterating.jsligo b/gitlab-pages/docs/data-types/src/lists/list_iterating.jsligo new file mode 100644 index 0000000000..77dba346ec --- /dev/null +++ b/gitlab-pages/docs/data-types/src/lists/list_iterating.jsligo @@ -0,0 +1,2 @@ +const assert_all_greater_than_3 = + (l: list): unit => List.iter(i => Assert.assert(i > 3), l); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/lists/list_iterating.mligo b/gitlab-pages/docs/data-types/src/lists/list_iterating.mligo new file mode 100644 index 0000000000..ede60568bf --- /dev/null +++ b/gitlab-pages/docs/data-types/src/lists/list_iterating.mligo @@ -0,0 +1,2 @@ +let assert_all_greater_than_3 (l : int list) : unit = + List.iter (fun i -> Assert.assert (i > 3)) l \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/lists/list_updating.jsligo b/gitlab-pages/docs/data-types/src/lists/list_updating.jsligo index fb8396499e..db132beb0f 100644 --- a/gitlab-pages/docs/data-types/src/lists/list_updating.jsligo +++ b/gitlab-pages/docs/data-types/src/lists/list_updating.jsligo @@ -1,6 +1,7 @@ -const nats : list = [0, 1, 2, 3, 4]; +const nats: list = [0, 1, 2, 3, 4]; +const evens_zeroed = List.update_with(x => x % 2 == (0 as nat), 0, nats); // evens_zeroed == [0, 1, 0, 3, 0] -const evens_zeroed = List.update_with(x => x % 2 == 0n, 0, nats); -const f = x => x % 2 == 0n ? None() : Some(x*x); -// odds == [0, 1, 2, 9, 4] -const odds_squared = List.update(f, nats); \ No newline at end of file +const f = x => + x % 2 == (0 as nat) ? ["None" as "None"] : ["Some" as "Some", x*x]; +const odds_squared = List.update(f, nats); +// odds_squared == [0, 1, 2, 9, 4] \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/lists/list_updating.mligo b/gitlab-pages/docs/data-types/src/lists/list_updating.mligo index f36f73cabc..83452a1c80 100644 --- a/gitlab-pages/docs/data-types/src/lists/list_updating.mligo +++ b/gitlab-pages/docs/data-types/src/lists/list_updating.mligo @@ -1,6 +1,6 @@ let nats = [0; 1; 2; 3; 4] -// evens_zeroed = [0; 1; 0; 3; 0] let evens_zeroed = List.update_with (fun x -> x mod 2 = 0n) 0 nats +// evens_zeroed = [0; 1; 0; 3; 0] let f x = if x mod 2 = 0n then None else Some (x*x) -// odds = [0; 1; 2; 9; 4] -let odds_squared = List.update f nats \ No newline at end of file +let odds_squared = List.update f nats +// odds_squared = [0; 1; 2; 9; 4] \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/lists/lists.jsligo b/gitlab-pages/docs/data-types/src/lists/lists.jsligo index 3897ffc7a2..2df82b59d5 100644 --- a/gitlab-pages/docs/data-types/src/lists/lists.jsligo +++ b/gitlab-pages/docs/data-types/src/lists/lists.jsligo @@ -1,2 +1,4 @@ -const empty_list : list = []; -const my_list : list = [1, 2, 2]; // The head is 1, the tail is [2, 2] \ No newline at end of file +const empty_list: list = []; +const my_list: list = [1, 2, 2]; // The head is 1, the tail is [2, 2] +const my_literal_tuple = [1, 2, 3]; // Assumed to be a tuple +const my_literal_list = list([1, 2, 3]); // Casted to a list \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/lists/reverse.jsligo b/gitlab-pages/docs/data-types/src/lists/reverse.jsligo index 2ab7913d6b..745b8e4ae7 100644 --- a/gitlab-pages/docs/data-types/src/lists/reverse.jsligo +++ b/gitlab-pages/docs/data-types/src/lists/reverse.jsligo @@ -1,11 +1,10 @@ function rev (xs : list) : list { const rev = ([xs, acc] : [list, list]) : list => - match(xs) { - when([]): acc; - when([y,...ys]): rev([ys, list([y,...acc])]) - }; - + $match(List.head_and_tail(xs), { + "None": () => acc, + "Some": ([y,ys]) => rev([ys, list([y,...acc])]) + }); return rev([xs, []]); }; const ints : list = rev([1, 2, 3]); -const nats : list = rev([1n, 2n, 3n]); \ No newline at end of file +const nats : list = rev([1 as nat, 2 as nat, 3 as nat]); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/map_adding.jsligo b/gitlab-pages/docs/data-types/src/maps/map_adding.jsligo index 632608a936..2c791f8277 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_adding.jsligo +++ b/gitlab-pages/docs/data-types/src/maps/map_adding.jsligo @@ -1,3 +1,3 @@ -const my_map : map = Map.literal([[1,"one"],[2,"two"]]); +const my_map: map = Map.literal([[1, "one"], [2, "two"]]); const new_map = Map.add(3, "three", my_map); const contains_3 = Map.mem(3, new_map); // == true \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/map_adding.mligo b/gitlab-pages/docs/data-types/src/maps/map_adding.mligo index c1c97e0f76..b729cfbe23 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_adding.mligo +++ b/gitlab-pages/docs/data-types/src/maps/map_adding.mligo @@ -1,3 +1,3 @@ -let my_map : (int, string) map = Map.literal [(1,"one"); (2,"two")] +let my_map : (int, string) map = Map.literal [(1, "one"); (2, "two")] let new_map = Map.add 3 "three" my_map let contains_3 = Map.mem 3 new_map // = true \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/map_folding.jsligo b/gitlab-pages/docs/data-types/src/maps/map_folding.jsligo index c4e1be12cf..330a007115 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_folding.jsligo +++ b/gitlab-pages/docs/data-types/src/maps/map_folding.jsligo @@ -1,10 +1,12 @@ -type player = string -type abscissa = int -type ordinate = int -type move = [abscissa, ordinate] -type game = map +const my_map: map = Map.literal([ + ["Alice", 1 as nat], + ["Bob", 4 as nat], + ["Charlie", 5 as nat], +]); -const horizontal_offset = (g: game): int => { - let folded = ([acc, j]: [int, [player, move]]) => acc + j[1][0]; - return Map.fold(folded, g, 0); -}; \ No newline at end of file +const fold_function = ([acc, element]: [nat, [string, nat]]): nat => { + const [_key, value] = element; + return acc + value; +} + +const map_sum: nat = Map.fold(fold_function, my_map, 0 as nat); // 10 as nat \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/map_folding.mligo b/gitlab-pages/docs/data-types/src/maps/map_folding.mligo index 68fa0b09b2..3371febee8 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_folding.mligo +++ b/gitlab-pages/docs/data-types/src/maps/map_folding.mligo @@ -1,9 +1,11 @@ -type player = string -type abscissa = int -type ordinate = int -type move = abscissa * ordinate -type game = (player, move) map +let my_map : (string, nat) map = Map.literal [ + ("Alice", 1n); + ("Bob", 4n); + ("Charlie", 5n); +] -let horizontal_offset (g : game) : int = - let folded = fun (acc, j : int * (player * move)) -> acc + j.1.0 - in Map.fold folded g 0 \ No newline at end of file +let fold_function = fun (acc, element : nat * (string * nat)) -> + let _key, value = element in + acc + value + +let map_sum = Map.fold fold_function my_map 0 (* 10 *) \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/map_iterating.jsligo b/gitlab-pages/docs/data-types/src/maps/map_iterating.jsligo index e3dcc5ef85..1c9cf5fad7 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_iterating.jsligo +++ b/gitlab-pages/docs/data-types/src/maps/map_iterating.jsligo @@ -1,2 +1,2 @@ const assert_all_greater_than_3 = - (m: map) : unit => Map.iter(([_k,v]) => assert(v > 3), m); \ No newline at end of file + (m: map) : unit => Map.iter(([_k, v]) => Assert.assert(v > 3), m); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/map_iterating.mligo b/gitlab-pages/docs/data-types/src/maps/map_iterating.mligo index 33a751cc9f..4521923083 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_iterating.mligo +++ b/gitlab-pages/docs/data-types/src/maps/map_iterating.mligo @@ -1,2 +1,2 @@ let assert_all_greater_than_3 (m : (int, int) map) : unit = - Map.iter (fun (_,v) -> assert (v > 3)) m // The key is discarded \ No newline at end of file + Map.iter (fun (_, v) -> Assert.assert (v > 3)) m \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/map_looping.jsligo b/gitlab-pages/docs/data-types/src/maps/map_looping.jsligo index 7b1979a69f..38bc64630c 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_looping.jsligo +++ b/gitlab-pages/docs/data-types/src/maps/map_looping.jsligo @@ -1,4 +1,4 @@ -function sum_val (m: map) { +function sum_val (m: map) { let sum = 0; // The key is discarded. for (const [_key, val] of m) sum = sum + val; diff --git a/gitlab-pages/docs/data-types/src/maps/map_mapping.jsligo b/gitlab-pages/docs/data-types/src/maps/map_mapping.jsligo index 72f29bbe55..56fb9f94d9 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_mapping.jsligo +++ b/gitlab-pages/docs/data-types/src/maps/map_mapping.jsligo @@ -1,3 +1,7 @@ -const my_map : map = Map.literal([[0,0], [1,1], [2,2]]); -// plus_one == Map.literal([[0,0],[1,2],[2,4]]) -const plus_one = Map.map(([k,v]) => k + v, my_map); \ No newline at end of file +const my_map: map = Map.literal([ + ["Alice", 2], + ["Bob", 5], + ["Charlie", 8], +]); + +const squared_map: map = Map.map(([_k, v]) => v * v, my_map); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/map_mapping.mligo b/gitlab-pages/docs/data-types/src/maps/map_mapping.mligo index 9144dcdbf3..806651a967 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_mapping.mligo +++ b/gitlab-pages/docs/data-types/src/maps/map_mapping.mligo @@ -1,3 +1,7 @@ -let my_map : (int, int) map = Map.literal [(0,0); (1,1); (2,2)] -// plus_one = Map.literal [(0,0); (1,2); (2,4)] -let plus_one = Map.map (fun (k,v) -> k + v) my_map \ No newline at end of file +let my_map : (string, int) map = Map.literal [ + ("Alice", 2); + ("Bob", 5); + ("Charlie", 8); +] + +let squared_map : (string, int) map = Map.map (fun (_k, v : string * int) : int -> v * v) my_map \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/map_removing.jsligo b/gitlab-pages/docs/data-types/src/maps/map_removing.jsligo index 66f78f2296..0b5854fe2b 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_removing.jsligo +++ b/gitlab-pages/docs/data-types/src/maps/map_removing.jsligo @@ -1,3 +1,3 @@ -const my_map : map = Map.literal([[1,"one"],[2,"two"]]); +const my_map: map = Map.literal([[1, "one"], [2, "two"]]); const new_map = Map.remove(2, my_map); const contains_3 = Map.mem(2, new_map); // == false \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/map_removing.mligo b/gitlab-pages/docs/data-types/src/maps/map_removing.mligo index 6af8335022..e1d7f2e79a 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_removing.mligo +++ b/gitlab-pages/docs/data-types/src/maps/map_removing.mligo @@ -1,3 +1,3 @@ -let my_map : (int, string) map = Map.literal [(1,"one"); (2,"two")] +let my_map : (int, string) map = Map.literal [(1, "one"); (2, "two")] let new_map = Map.remove 2 my_map let contains_3 = Map.mem 2 new_map // = false \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/map_searching.jsligo b/gitlab-pages/docs/data-types/src/maps/map_searching.jsligo index 14279bd03d..0cece0c947 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_searching.jsligo +++ b/gitlab-pages/docs/data-types/src/maps/map_searching.jsligo @@ -1,10 +1,9 @@ const my_map: map = - Map.literal([[1,"one"],[2,"two"]]); + Map.literal([[1, "one"], [2, "two"]]); const contains_2: bool = Map.mem(2, my_map); // == true -const v : option = Map.find_opt(2, my_map); -let force_access = (key, map) => { - return match(Map.find_opt (key, map)) { - when(Some(value)): value; - when(None): failwith("No value.") - }; -}; \ No newline at end of file +const value_option: option = Map.find_opt(2, my_map); +const value = (key, map) => + $match(Map.find_opt(key, map), { + "Some": (value) => value, + "None": () => failwith("No value."), + }); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/map_searching.mligo b/gitlab-pages/docs/data-types/src/maps/map_searching.mligo index 746870d15a..4f098a8b9a 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_searching.mligo +++ b/gitlab-pages/docs/data-types/src/maps/map_searching.mligo @@ -1,8 +1,8 @@ let my_map : (int, string) map = Map.literal [(1,"one"); (2,"two")] let contains_2 : bool = Map.mem 2 my_map // = true -let v : string option = Map.find_opt 2 my_map -let force_access key map = +let value_option : string option = Map.find_opt 2 my_map +let value key map = match Map.find_opt key map with Some value -> value | None -> failwith "No value." \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/map_size.jsligo b/gitlab-pages/docs/data-types/src/maps/map_size.jsligo index 73bf691f34..d55951cdb2 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_size.jsligo +++ b/gitlab-pages/docs/data-types/src/maps/map_size.jsligo @@ -1,3 +1,3 @@ const my_map: map = - Map.literal([[1,"one"],[2,"two"]]); + Map.literal([[1, "one"], [2, "two"]]); const size: nat = Map.size(my_map); // == 2 \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/map_updating.jsligo b/gitlab-pages/docs/data-types/src/maps/map_updating.jsligo index c964923f37..0a3ed50a8b 100644 --- a/gitlab-pages/docs/data-types/src/maps/map_updating.jsligo +++ b/gitlab-pages/docs/data-types/src/maps/map_updating.jsligo @@ -1,7 +1,7 @@ const my_map: map = Map.literal([[1,"one"],[2,"two"]]); -const map_with_3 = Map.update (3, Some("three"), my_map); +const map_with_3 = Map.update (3, ["Some" as "Some", "three"], my_map); const contains_3 = Map.mem(3, map_with_3); // == true -const map_without_2 = Map.update(2, None(), my_map); -const contains_2 = Map.mem (2, map_without_2); // == false -// three == Some("three") -const [three, map_without_3] = Map.get_and_update(3, None(), map_with_3); \ No newline at end of file +const map_without_2 = Map.update(2, ["None" as "None"], my_map); +const contains_2 = Map.mem(2, map_without_2); // == false +// three == ["Some" as "Some", "three"] +const [three, map_without_3] = Map.get_and_update(3, ["None" as "None"], map_with_3); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/maps/maps.jsligo b/gitlab-pages/docs/data-types/src/maps/maps.jsligo index 2bb3847e5e..8fc000c15c 100644 --- a/gitlab-pages/docs/data-types/src/maps/maps.jsligo +++ b/gitlab-pages/docs/data-types/src/maps/maps.jsligo @@ -4,7 +4,7 @@ type dictionary = map; const empty_dict: dictionary = Map.empty; -const dictionary : dictionary = +const dictionary: dictionary = Map.literal([ - ["one", (["The number 1.", "A member of a group."] as definition)], - ["two", (["The number 2."] as definition)]]); \ No newline at end of file + ["one", (["The number 1.", "A member of a group."])], + ["two", (["The number 2."])]]); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/numbers/adding.jsligo b/gitlab-pages/docs/data-types/src/numbers/adding.jsligo new file mode 100644 index 0000000000..ccf98ac41b --- /dev/null +++ b/gitlab-pages/docs/data-types/src/numbers/adding.jsligo @@ -0,0 +1,5 @@ +const a : int = 5 + 10; // int + int yields int +const b : nat = (5 as nat) + (10 as nat); // nat + nat yields nat +const c : int = (5 as nat) + 10; // nat + int yields int +const d : int = 10 + (5 as nat); // int + nat yields int +// const error : nat = (5 as nat) + 10; \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/numbers/additing.mligo b/gitlab-pages/docs/data-types/src/numbers/adding.mligo similarity index 100% rename from gitlab-pages/docs/data-types/src/numbers/additing.mligo rename to gitlab-pages/docs/data-types/src/numbers/adding.mligo diff --git a/gitlab-pages/docs/data-types/src/numbers/additing.jsligo b/gitlab-pages/docs/data-types/src/numbers/additing.jsligo deleted file mode 100644 index 527428da87..0000000000 --- a/gitlab-pages/docs/data-types/src/numbers/additing.jsligo +++ /dev/null @@ -1,5 +0,0 @@ -const a : int = 5 + 10; // int + int yields int -const b : nat = 5n + 10n; // nat + nat yields nat -const c : int = 5n + 10; // nat + int yields int -const d : int = 10 + 5n; // int + nat yields int -// const error : nat = 5n + 10; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/ungrouped.mligo b/gitlab-pages/docs/data-types/src/numbers/bitwise.mligo similarity index 87% rename from gitlab-pages/docs/language-basics/src/math-numbers-tez/ungrouped.mligo rename to gitlab-pages/docs/data-types/src/numbers/bitwise.mligo index 6a039f560a..bd046b57cf 100644 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/ungrouped.mligo +++ b/gitlab-pages/docs/data-types/src/numbers/bitwise.mligo @@ -1,5 +1,6 @@ // Bitwise and (first operand can be int or nat) let four : nat = 4n land 4n // 4 +// Other bitwise operations require two nats let four_ : nat = 7 land 4n // 4 // Bitwise or let seven : nat = 7n lor 4n // 7 diff --git a/gitlab-pages/docs/data-types/src/numbers/casting.jsligo b/gitlab-pages/docs/data-types/src/numbers/casting.jsligo index ffa436a826..1e579edf8d 100644 --- a/gitlab-pages/docs/data-types/src/numbers/casting.jsligo +++ b/gitlab-pages/docs/data-types/src/numbers/casting.jsligo @@ -1,2 +1,2 @@ -const one : int = int(1n); // Explicit cast from nat to int +const one : int = int(1 as nat); // Explicit cast from nat to int const two : nat = abs(2); // Explicit cast from int to nat \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/numbers/dividing.jsligo b/gitlab-pages/docs/data-types/src/numbers/dividing.jsligo index 861b733da2..88d5ec7ffd 100644 --- a/gitlab-pages/docs/data-types/src/numbers/dividing.jsligo +++ b/gitlab-pages/docs/data-types/src/numbers/dividing.jsligo @@ -1,4 +1,4 @@ const a : int = 10 / 3; // int / int yields int -const b : nat = 10n / 3n; // nat / nat yields nat -const c : int = 10n / 3; // nat / int yields int -const d : int = 10 / 3n; // int / nat yields int \ No newline at end of file +const b : nat = (10 as nat) / (3 as nat); // nat / nat yields nat +const c : int = (10 as nat) / 3; // nat / int yields int +const d : int = 10 / (3 as nat); // int / nat yields int \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/numbers/euclidean.jsligo b/gitlab-pages/docs/data-types/src/numbers/euclidean.jsligo new file mode 100644 index 0000000000..5c01365909 --- /dev/null +++ b/gitlab-pages/docs/data-types/src/numbers/euclidean.jsligo @@ -0,0 +1,5 @@ +// All below equal Some (7,2) +const ediv1: option<[int, nat]> = ediv(37, 5); +const ediv2: option<[int, nat]> = ediv(37 as nat, 5); +const ediv3: option<[nat, nat]> = ediv(37 as nat, 5 as nat); +const ediv4: option<[int, nat]> = ediv(37, 5 as nat); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/variants/options_euclidean.mligo b/gitlab-pages/docs/data-types/src/numbers/euclidean.mligo similarity index 100% rename from gitlab-pages/docs/data-types/src/variants/options_euclidean.mligo rename to gitlab-pages/docs/data-types/src/numbers/euclidean.mligo diff --git a/gitlab-pages/docs/data-types/src/numbers/increment_ops.jsligo b/gitlab-pages/docs/data-types/src/numbers/increment_ops.jsligo new file mode 100644 index 0000000000..d0a62e7271 --- /dev/null +++ b/gitlab-pages/docs/data-types/src/numbers/increment_ops.jsligo @@ -0,0 +1,16 @@ +const testIncDecIndependent = (() => { + let value = 0; + value++; + Assert.assert(value == 1); + value--; + Assert.assert(value == 0); +})(); +const testIncEmbedded = (() => { + let value = 0; + // Prefix increment operator adds one immediately + Assert.assert(++value == 1); + Assert.assert(value == 1); + // Postfix increment operator adds one after the expression is evaluated + Assert.assert(value++ == 1); + Assert.assert(value == 2); +})(); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/numbers/mod.jsligo b/gitlab-pages/docs/data-types/src/numbers/mod.jsligo index 0b1c2c8761..6bcf511295 100644 --- a/gitlab-pages/docs/data-types/src/numbers/mod.jsligo +++ b/gitlab-pages/docs/data-types/src/numbers/mod.jsligo @@ -1,4 +1,4 @@ -const a : nat = 120 % 9; // int % int yields nat -const b : nat = 120n % 9; // nat % int yields nat -const c : nat = 120n % 9n; // nat % nat yields nat -const d : nat = 120 % 9n; // int % nat yields nat \ No newline at end of file +const a : nat = 120 % 9; // int % int yields nat +const b : nat = (120 as nat) % 9; // nat % int yields nat +const c : nat = (120 as nat) % (9 as nat); // nat % nat yields nat +const d : nat = 120 % (9 as nat); // int % nat yields nat \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/numbers/multiplying.jsligo b/gitlab-pages/docs/data-types/src/numbers/multiplying.jsligo index 397b70e3bd..92b19af575 100644 --- a/gitlab-pages/docs/data-types/src/numbers/multiplying.jsligo +++ b/gitlab-pages/docs/data-types/src/numbers/multiplying.jsligo @@ -1,4 +1,4 @@ const a : int = 5 * 10; // int * int yields int -const b : nat = 5n * 2n; // nat * nat yields nat -const c : int = 10n * 5; // nat * int yields int -const d : int = 5 * 10n; // int * nat yields int \ No newline at end of file +const b : nat = (5 as nat) * (2 as nat); // nat * nat yields nat +const c : int = (10 as nat) * 5; // nat * int yields int +const d : int = 5 * (10 as nat); // int * nat yields int \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/numbers/negating.jsligo b/gitlab-pages/docs/data-types/src/numbers/negating.jsligo index 9c055ca008..6f4b5f0ca8 100644 --- a/gitlab-pages/docs/data-types/src/numbers/negating.jsligo +++ b/gitlab-pages/docs/data-types/src/numbers/negating.jsligo @@ -1,3 +1,3 @@ const a : int = -5; // - int yields int -const b : int = -5n; // - nat yields int -// const error : nat = -5n; \ No newline at end of file +const b : int = -(5 as nat); // - nat yields int +// const error : nat = -(5 as nat); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/numbers/positive.jsligo b/gitlab-pages/docs/data-types/src/numbers/positive.jsligo new file mode 100644 index 0000000000..d33ab46909 --- /dev/null +++ b/gitlab-pages/docs/data-types/src/numbers/positive.jsligo @@ -0,0 +1 @@ +const one_is_nat: option = is_nat(1); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/variants/options_positive.mligo b/gitlab-pages/docs/data-types/src/numbers/positive.mligo similarity index 100% rename from gitlab-pages/docs/data-types/src/variants/options_positive.mligo rename to gitlab-pages/docs/data-types/src/numbers/positive.mligo diff --git a/gitlab-pages/docs/data-types/src/numbers/subtracting.jsligo b/gitlab-pages/docs/data-types/src/numbers/subtracting.jsligo index 101ebd7a52..720bcb096d 100644 --- a/gitlab-pages/docs/data-types/src/numbers/subtracting.jsligo +++ b/gitlab-pages/docs/data-types/src/numbers/subtracting.jsligo @@ -1,5 +1,5 @@ const a : int = 5 - 10; // int - int yields int -const b : int = 5n - 2n; // nat - nat yields int -const c : int = 10n - 5; // nat - int yields int -const d : int = 5 - 10n; // int - nat yields int -// const error : nat = 5n - 2n; \ No newline at end of file +const b : int = (5 as nat) - (2 as nat); // nat - nat yields int +const c : int = (10 as nat) - 5; // nat - int yields int +const d : int = 5 - (10 as nat); // int - nat yields int +// const error : nat = (5 as nat) - (2 as nat); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/numbers/typed_int_and_nat.jsligo b/gitlab-pages/docs/data-types/src/numbers/typed_int_and_nat.jsligo index ae8930d684..b512853baf 100644 --- a/gitlab-pages/docs/data-types/src/numbers/typed_int_and_nat.jsligo +++ b/gitlab-pages/docs/data-types/src/numbers/typed_int_and_nat.jsligo @@ -2,6 +2,6 @@ const zero : int = 0 const million : int = 1_000_000 const baekman : int = 100_0000 -const zero_nat : nat = 0n -const million_nat : nat = 1_000_000n -const baekman_nat : nat = 100_0000n \ No newline at end of file +const zero_nat : nat = 0 as nat +const million_nat : nat = 1_000_000 as nat +const baekman_nat : nat = 100_0000 as nat \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/parametric_types/poly.jsligo b/gitlab-pages/docs/data-types/src/parametric_types/poly.jsligo index dd1ccd7f17..0743806ec3 100644 --- a/gitlab-pages/docs/data-types/src/parametric_types/poly.jsligo +++ b/gitlab-pages/docs/data-types/src/parametric_types/poly.jsligo @@ -1,11 +1,11 @@ function rev (xs : list) : list { const rev = ([xs, acc] : [list, list]) : list => - match(xs) { - when([]): acc; - when([y,...ys]): rev([ys, [y,...acc]]) - }; + $match(List.head_and_tail(xs), { + "None": () => acc, + "Some": ([y,ys]) => rev([ys, [y,...acc]]) + }); - return rev([xs, ([] as list)]); + return rev([xs, []]); }; const lint : list = rev([1, 2, 3]); -const lnat : list = rev([1n, 2n, 3n]); \ No newline at end of file +const lnat : list = rev([(1 as nat), (2 as nat), (3 as nat)]); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/records/object_access.jsligo b/gitlab-pages/docs/data-types/src/records/object_access.jsligo index b1d7c6c72e..4746996414 100644 --- a/gitlab-pages/docs/data-types/src/records/object_access.jsligo +++ b/gitlab-pages/docs/data-types/src/records/object_access.jsligo @@ -9,14 +9,16 @@ type account = { is_admin : bool }; -const user : user = {login: "al", name: "Alice"}; -const alice : account = {user, id: 5, is_admin: true}; +const user: user = {login: "al", name: "Alice"}; +const alice: account = {user, id: 5, is_admin: true}; const is_alice_admin = alice.is_admin; // == true -function userToTuple (a : account) { +const alice_name = alice.user.name; // == "Alice" +const alice_id = alice["id"]; // 5 +function userToTuple (a: account) { const {user, id, is_admin} = a; return [user, id, is_admin]; } -function getId (a : account) { +function getId (a: account) { let {user, id, is_admin} = a; ignore([user, is_admin]); // To avoid a warning return id; diff --git a/gitlab-pages/docs/data-types/src/records/object_update.jsligo b/gitlab-pages/docs/data-types/src/records/object_update.jsligo index 05e0f16271..da01b5d1d0 100644 --- a/gitlab-pages/docs/data-types/src/records/object_update.jsligo +++ b/gitlab-pages/docs/data-types/src/records/object_update.jsligo @@ -1,7 +1,12 @@ +function my_function () { + let my_object = {a: 1, b: 2}; + my_object.a = 5; + my_object["b"] = 3; +} type point = {x: int, y: int, z: int} type vector = {dx: int, dy: int} -const origin = {x: 0, y: 0, z: 0}; +const origin: point = {x: 0, y: 0, z: 0}; const xy_translate = (p: point, vec: vector) => ({...p, x: p.x + vec.dx, y: p.y + vec.dy}); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/records/objects1.jsligo b/gitlab-pages/docs/data-types/src/records/objects1.jsligo index 73e69de18c..0436922f53 100644 --- a/gitlab-pages/docs/data-types/src/records/objects1.jsligo +++ b/gitlab-pages/docs/data-types/src/records/objects1.jsligo @@ -4,7 +4,7 @@ type user = { name : string }; const alice : user = { - id : 1n, + id : 1 as nat, is_admin : true, name : "Alice" }; \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/records/record_access.jsligo b/gitlab-pages/docs/data-types/src/records/record_access.jsligo deleted file mode 100644 index b1d7c6c72e..0000000000 --- a/gitlab-pages/docs/data-types/src/records/record_access.jsligo +++ /dev/null @@ -1,23 +0,0 @@ -type user = { - login : string, - name : string -}; - -type account = { - user : user, - id : int, - is_admin : bool -}; - -const user : user = {login: "al", name: "Alice"}; -const alice : account = {user, id: 5, is_admin: true}; -const is_alice_admin = alice.is_admin; // == true -function userToTuple (a : account) { - const {user, id, is_admin} = a; - return [user, id, is_admin]; -} -function getId (a : account) { - let {user, id, is_admin} = a; - ignore([user, is_admin]); // To avoid a warning - return id; -} \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/records/record_nested_update.jsligo b/gitlab-pages/docs/data-types/src/records/record_nested_update.jsligo deleted file mode 100644 index 4bd37dd64a..0000000000 --- a/gitlab-pages/docs/data-types/src/records/record_nested_update.jsligo +++ /dev/null @@ -1,12 +0,0 @@ -type user = { - login : string, - name : string -}; - -type account = { - user : user, - id : int, - is_admin : bool -}; -const change_login = (login: string, account: account) : account => - ({...account, user: {...account.user, login}}); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/records/record_update.jsligo b/gitlab-pages/docs/data-types/src/records/record_update.jsligo deleted file mode 100644 index 05e0f16271..0000000000 --- a/gitlab-pages/docs/data-types/src/records/record_update.jsligo +++ /dev/null @@ -1,7 +0,0 @@ -type point = {x: int, y: int, z: int} -type vector = {dx: int, dy: int} - -const origin = {x: 0, y: 0, z: 0}; - -const xy_translate = (p: point, vec: vector) => - ({...p, x: p.x + vec.dx, y: p.y + vec.dy}); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/records/records1.jsligo b/gitlab-pages/docs/data-types/src/records/records1.jsligo deleted file mode 100644 index 73e69de18c..0000000000 --- a/gitlab-pages/docs/data-types/src/records/records1.jsligo +++ /dev/null @@ -1,10 +0,0 @@ -type user = { - id : nat, - is_admin : bool, - name : string -}; -const alice : user = { - id : 1n, - is_admin : true, - name : "Alice" -}; \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/sets/cardinal.jsligo b/gitlab-pages/docs/data-types/src/sets/cardinal.jsligo index c498c8f48d..e102c03d81 100644 --- a/gitlab-pages/docs/data-types/src/sets/cardinal.jsligo +++ b/gitlab-pages/docs/data-types/src/sets/cardinal.jsligo @@ -1,2 +1,2 @@ const my_set: set = Set.literal([3, 2, 2, 1]); -const cardinal : nat = Set.size(my_set); // == 3 \ No newline at end of file +const cardinal: nat = Set.size(my_set); // == 3 \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/sets/set_folding.jsligo b/gitlab-pages/docs/data-types/src/sets/set_folding.jsligo index 79f8d033ed..4feb85e894 100644 --- a/gitlab-pages/docs/data-types/src/sets/set_folding.jsligo +++ b/gitlab-pages/docs/data-types/src/sets/set_folding.jsligo @@ -1,5 +1,5 @@ -const s : set = Set.literal([1, 2, 3]); +const s: set = Set.literal([1, 2, 3]); // incr == [3, 2, 1] -const incr : list = Set.fold (([a,i]) => ([i,...a] as list), s, []); +const incr: list = Set.fold (([a,i]) => ([i,...a] as list), s, []); // decr == [1, 2, 3] -const decr : list = Set.fold_desc (([i,a]) => ([i,...a] as list), s, []); \ No newline at end of file +const decr: list = Set.fold_desc (([i,a]) => ([i,...a] as list), s, []); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/sets/set_iterating.jsligo b/gitlab-pages/docs/data-types/src/sets/set_iterating.jsligo index 54db15fe28..4b816c7b76 100644 --- a/gitlab-pages/docs/data-types/src/sets/set_iterating.jsligo +++ b/gitlab-pages/docs/data-types/src/sets/set_iterating.jsligo @@ -1,2 +1,2 @@ const assert_all_greater_than_3 = - (s: set) : unit => Set.iter(i => assert(i > 3), s); \ No newline at end of file + (s: set): unit => Set.iter(i => Assert.assert(i > 3), s); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/sets/set_iterating.mligo b/gitlab-pages/docs/data-types/src/sets/set_iterating.mligo index 0066514d64..7e6809f0d0 100644 --- a/gitlab-pages/docs/data-types/src/sets/set_iterating.mligo +++ b/gitlab-pages/docs/data-types/src/sets/set_iterating.mligo @@ -1,2 +1,2 @@ let assert_all_greater_than_3 (s : int set) : unit = - Set.iter (fun i -> assert (i > 3)) s \ No newline at end of file + Set.iter (fun i -> Assert.assert (i > 3)) s \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/sets/set_updating.jsligo b/gitlab-pages/docs/data-types/src/sets/set_updating.jsligo index 92e79f8b4e..9dbed19315 100644 --- a/gitlab-pages/docs/data-types/src/sets/set_updating.jsligo +++ b/gitlab-pages/docs/data-types/src/sets/set_updating.jsligo @@ -1,6 +1,8 @@ const nats: set = Set.literal([3, 2, 2, 1]); const set_with_5 = Set.update(5, true, nats); const set_without_3 = Set.update(3, false, nats); -const f = x => x % 2 == 0n ? None() : Some(x); +const f = x => x % 2 == (0 as nat) ? + ["None" as "None"] : + ["Some" as "Some", x]; // odds == Set.literal([3, 1]) const odds = Set.filter_map(f, nats); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/sets/sets.jsligo b/gitlab-pages/docs/data-types/src/sets/sets.jsligo index 3a3a3870c4..d4049f81ce 100644 --- a/gitlab-pages/docs/data-types/src/sets/sets.jsligo +++ b/gitlab-pages/docs/data-types/src/sets/sets.jsligo @@ -1,4 +1,4 @@ const empty_set: set = Set.empty; const set1: set = Set.literal([3, 2, 2, 1]); const two = 2; -const set2 : set = Set.of_list([3, two, two, 1]); \ No newline at end of file +const set2 : set = Set.of_list([3, two, two, two + two, 1]); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/sets/sets.mligo b/gitlab-pages/docs/data-types/src/sets/sets.mligo index 128427233b..7ba321ae0b 100644 --- a/gitlab-pages/docs/data-types/src/sets/sets.mligo +++ b/gitlab-pages/docs/data-types/src/sets/sets.mligo @@ -1,2 +1,4 @@ let empty_set : int set = Set.empty -let set1 : int set = Set.literal [3; 2; 2; 1] \ No newline at end of file +let set1 : int set = Set.literal [3; 2; 2; 1] +let two = 2 +let set2 : int set = Set.of_list [3; two; two; two + two; 1] \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/strings/length.jsligo b/gitlab-pages/docs/data-types/src/strings/length.jsligo index 5dac1a55af..02167b447c 100644 --- a/gitlab-pages/docs/data-types/src/strings/length.jsligo +++ b/gitlab-pages/docs/data-types/src/strings/length.jsligo @@ -1 +1 @@ -const length : nat = String.size("Alice"); // length == 5n \ No newline at end of file +const length : nat = String.size("Alice"); // length == (5 as nat) \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/strings/slicing.jsligo b/gitlab-pages/docs/data-types/src/strings/slicing.jsligo index fbef99f207..5f77d6399e 100644 --- a/gitlab-pages/docs/data-types/src/strings/slicing.jsligo +++ b/gitlab-pages/docs/data-types/src/strings/slicing.jsligo @@ -1,2 +1,2 @@ const name = "Alice"; -const slice = String.sub (0n, 1n, name); // slice == "A" \ No newline at end of file +const slice = String.sub (0 as nat, 1 as nat, name); // slice == "A" \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/strings/strings.jsligo b/gitlab-pages/docs/data-types/src/strings/strings.jsligo index 5bd7765b97..050d255f33 100644 --- a/gitlab-pages/docs/data-types/src/strings/strings.jsligo +++ b/gitlab-pages/docs/data-types/src/strings/strings.jsligo @@ -1,3 +1,3 @@ -const a :string = "Hello Alice"; -const one = "" ? 0 : 1; +const a: string = "Hello Alice"; +const one = "" ? 0 : 1; const zero = "foo" ? 0 : 1; \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/strings/strings.mligo b/gitlab-pages/docs/data-types/src/strings/strings.mligo index a2a45e7aff..78146c7620 100644 --- a/gitlab-pages/docs/data-types/src/strings/strings.mligo +++ b/gitlab-pages/docs/data-types/src/strings/strings.mligo @@ -1,3 +1,3 @@ let a : string = "Hello Alice" -let one = if "" then 0 else 1 +let one = if "" then 0 else 1 let zero = if "foo" then 0 else 1 \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/tez/add.jsligo b/gitlab-pages/docs/data-types/src/tez/add.jsligo index 26a3c1efa4..6dcce0adfc 100644 --- a/gitlab-pages/docs/data-types/src/tez/add.jsligo +++ b/gitlab-pages/docs/data-types/src/tez/add.jsligo @@ -1 +1 @@ -const sum: tez = 5mutez + 1tez; \ No newline at end of file +const sum: tez = (5 as mutez) + (1 as tez); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/tez/divide.jsligo b/gitlab-pages/docs/data-types/src/tez/divide.jsligo index e0050135ed..9d7fbadc69 100644 --- a/gitlab-pages/docs/data-types/src/tez/divide.jsligo +++ b/gitlab-pages/docs/data-types/src/tez/divide.jsligo @@ -1 +1 @@ -const div: nat = 10mutez / 3mutez; \ No newline at end of file +const div: nat = (10 as mutez) / (3 as mutez); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/tez/multiply.jsligo b/gitlab-pages/docs/data-types/src/tez/multiply.jsligo index 28be30af96..06217f81dd 100644 --- a/gitlab-pages/docs/data-types/src/tez/multiply.jsligo +++ b/gitlab-pages/docs/data-types/src/tez/multiply.jsligo @@ -1 +1 @@ -const mult: tez = 5n * 5mutez; \ No newline at end of file +const mult: tez = (5 as nat) * (5 as mutez); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/tez/subtract.jsligo b/gitlab-pages/docs/data-types/src/tez/subtract.jsligo index c9231b1605..32de0f30b7 100644 --- a/gitlab-pages/docs/data-types/src/tez/subtract.jsligo +++ b/gitlab-pages/docs/data-types/src/tez/subtract.jsligo @@ -1,2 +1,4 @@ -const amount: option = 5mutez - 1mutez; /* Some (4mutez) */ -const negative: option = 1mutez - 5mutez; /* None */ \ No newline at end of file +// ["Some" as "Some", 4 as mutez] +const amount: option = (5 as mutez) - (1 as mutez); +// ["None" as "None"] +const negative: option = (1 as mutez) - (5 as mutez); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/tez/tez_euclidean.jsligo b/gitlab-pages/docs/data-types/src/tez/tez_euclidean.jsligo index 6fb43db75f..11e1c82051 100644 --- a/gitlab-pages/docs/data-types/src/tez/tez_euclidean.jsligo +++ b/gitlab-pages/docs/data-types/src/tez/tez_euclidean.jsligo @@ -1,5 +1,5 @@ -// Some (7, 2mutez) -const ediv1: option<[nat, tez]> = ediv(37mutez, 5mutez); +// ["Some" as "Some", [7, (2 as mutez)]] +const ediv1: option<[nat, tez]> = ediv(37 as mutez, 5 as mutez); -// Some (7mutez, 2mutez) -const ediv2: option<[tez, tez]> = ediv(37mutez, 5n); \ No newline at end of file +// ["Some" as "Some", [7 as mutez, 2 as mutez]] +const ediv2: option<[tez, tez]> = ediv(37 as mutez, 5 as nat); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/tickets/manip_ticket.jsligo b/gitlab-pages/docs/data-types/src/tickets/manip_ticket.jsligo index df0bd29c5c..648435df6d 100644 --- a/gitlab-pages/docs/data-types/src/tickets/manip_ticket.jsligo +++ b/gitlab-pages/docs/data-types/src/tickets/manip_ticket.jsligo @@ -1,11 +1,15 @@ -const my_ticket1 = Option.unopt(Tezos.create_ticket(1, 10n)); -const my_ticket2 = Option.unopt(Tezos.create_ticket("one", 10n)); -const v2 = do { - let [[_addr, [payload, _amt]], _ticket] = Tezos.read_ticket (my_ticket2); +import Ticket = Tezos.Ticket; + +const my_ticket1 = + Option.value_with_error("ticket1 failed", Ticket.create(1, 10 as nat)); +const my_ticket2 = + Option.value_with_error("ticket2 failed", Ticket.create("one", 10 as nat)); +const v2 = (() => { + let [[_addr, [payload, _amt]], _ticket] = Ticket.read(my_ticket2); return payload; -} +})() const [ta, tb] = - match(Tezos.split_ticket(my_ticket1, [6n, 4n])) { - when(None()): failwith("amt_a + amt_v != amt"); - when(Some(split_tickets)): split_tickets - }; \ No newline at end of file + $match(Ticket.split(my_ticket1, [6 as nat, 4 as nat]), { + "None": () => failwith("amt_a + amt_v != amt"), + "Some": split_tickets => split_tickets + }); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/tickets/manip_ticket.mligo b/gitlab-pages/docs/data-types/src/tickets/manip_ticket.mligo index 25daac35df..074bd01de2 100644 --- a/gitlab-pages/docs/data-types/src/tickets/manip_ticket.mligo +++ b/gitlab-pages/docs/data-types/src/tickets/manip_ticket.mligo @@ -1,13 +1,11 @@ -let my_ticket1 = Option.unopt (Tezos.create_ticket 1 10n) -let my_ticket2 = Option.unopt (Tezos.create_ticket "one" 10n) +module Ticket = Tezos.Ticket + +let my_ticket1 = Option.value_with_error "ticket1 failed" (Ticket.create 1 10n) +let my_ticket2 = Option.value_with_error "ticket2 failed" (Ticket.create "one" 10n) let v = - let (_addr, (payload, _amt)), _ticket = Tezos.read_ticket my_ticket1 + let (_addr, (payload, _amt)), _ticket = Ticket.read my_ticket1 in payload let ta, tb = - match Tezos.split_ticket my_ticket1 (6n, 4n) with + match Ticket.split my_ticket1 (6n, 4n) with None -> failwith "amt_a + amt_v <> amt" - | Some split_tickets -> split_tickets -let tc : int ticket option = - let ta = Option.unopt (Tezos.create_ticket 1 10n) in - let tb = Option.unopt (Tezos.create_ticket 1 5n) in - Tezos.join_tickets (ta, tb) \ No newline at end of file + | Some split_tickets -> split_tickets \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/tickets/manip_ticket2.jsligo b/gitlab-pages/docs/data-types/src/tickets/manip_ticket2.jsligo index 37206ad6ef..5fc9a7171c 100644 --- a/gitlab-pages/docs/data-types/src/tickets/manip_ticket2.jsligo +++ b/gitlab-pages/docs/data-types/src/tickets/manip_ticket2.jsligo @@ -1,3 +1,7 @@ -const ta = Option.unopt(Tezos.create_ticket(1, 10n)); -const tb = Option.unopt(Tezos.create_ticket(1, 5n)); -const tc = Tezos.join_tickets([ta, tb]); \ No newline at end of file +import Ticket = Tezos.Ticket; + +const ta = Option.value_with_error("ta failed", + Ticket.create(1, 10 as nat)); +const tb = Option.value_with_error("tb failed", + Ticket.create(1, 5 as nat)); +const tc = Ticket.join([ta, tb]); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/tickets/manip_ticket2.mligo b/gitlab-pages/docs/data-types/src/tickets/manip_ticket2.mligo new file mode 100644 index 0000000000..5e96bd6751 --- /dev/null +++ b/gitlab-pages/docs/data-types/src/tickets/manip_ticket2.mligo @@ -0,0 +1,6 @@ +module Ticket = Tezos.Ticket + +let tc : int ticket option = + let ta = Option.value_with_error "ta failed" (Ticket.create 1 10n) in + let tb = Option.value_with_error "tb failed" (Ticket.create 1 5n) in + Ticket.join (ta, tb) \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/tuples/destructuring.mligo b/gitlab-pages/docs/data-types/src/tuples/destructuring.mligo index d283cc4921..d90f19fe95 100644 --- a/gitlab-pages/docs/data-types/src/tuples/destructuring.mligo +++ b/gitlab-pages/docs/data-types/src/tuples/destructuring.mligo @@ -1,7 +1,6 @@ let friends = "Alice", "Bob" // Parentheses are optional let alice, bob = friends -let alice, _ = friends -let alice, _bob = friends // This alice shadows the previous one +let alice, _bob = friends (* This alice shadows the previous one *) let deep = (1, (2n, "Hello")) let _, (_, greeting) = deep // greeting = "Hello" let film = deep.1.1 ^ ", Dolly!" // film = "Hello, Dolly!" \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/tuples/nested.jsligo b/gitlab-pages/docs/data-types/src/tuples/nested.jsligo new file mode 100644 index 0000000000..5c2eaec817 --- /dev/null +++ b/gitlab-pages/docs/data-types/src/tuples/nested.jsligo @@ -0,0 +1,2 @@ +type nested_tuple = [int, [string, nat]] +const my_nested_value = [-5, ["Hello", 12]]; \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/tuples/nested.mligo b/gitlab-pages/docs/data-types/src/tuples/nested.mligo new file mode 100644 index 0000000000..b54457f3e0 --- /dev/null +++ b/gitlab-pages/docs/data-types/src/tuples/nested.mligo @@ -0,0 +1,2 @@ +type nested_tuple = int * (string * nat) +let my_nested_value = (-5, ("Hello", 12)) \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/tuples/tuple_alt.jsligo b/gitlab-pages/docs/data-types/src/tuples/tuple_alt.jsligo index 95c8c95306..a19d11981f 100644 --- a/gitlab-pages/docs/data-types/src/tuples/tuple_alt.jsligo +++ b/gitlab-pages/docs/data-types/src/tuples/tuple_alt.jsligo @@ -1,2 +1,2 @@ type couple = [string, string]; -const friends : couple = ["Alice", "Bob"]; \ No newline at end of file +const friends: couple = ["Alice", "Bob"]; \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/type-annotations/annotate.jsligo b/gitlab-pages/docs/data-types/src/type-annotations/annotate.jsligo new file mode 100644 index 0000000000..30a1803c61 --- /dev/null +++ b/gitlab-pages/docs/data-types/src/type-annotations/annotate.jsligo @@ -0,0 +1,23 @@ +const myInteger: int = 5; +const myString: string = "Hello"; +const myOtherInteger = 5 as int; +const myOtherString = "Hello" as string; +const myList: list = [1, 2, 3]; +const myMap: map = + Map.literal([ + ["one", 1], + ["two", 2], + ]); +namespace Counter { + type storage_type = int; + type return_type = [list, storage_type]; + + // @entry + const add = (value: int, storage: storage_type): return_type => + [[], storage + value]; + + // @entry + const sub = (value: int, storage: storage_type): return_type => + [[], storage - value]; + +} \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/type-annotations/annotate.mligo b/gitlab-pages/docs/data-types/src/type-annotations/annotate.mligo new file mode 100644 index 0000000000..da333d877a --- /dev/null +++ b/gitlab-pages/docs/data-types/src/type-annotations/annotate.mligo @@ -0,0 +1,21 @@ +let myInteger : int = 5 +let myString : string = "Hello" +let myList : int list = [1; 2; 3] +let myMap : (string, int) map = + Map.literal [ + ("one", 1); + ("two", 2); + ] +module Counter = struct + type storage_type = int + type return_type = operation list * storage_type + + [@entry] + let add (value : int) (storage: storage_type) : return_type = + [], storage + value + + [@entry] + let sub (value : int) (storage: storage_type) : return_type = + [], storage - value + +end \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/type-annotations/assumptions.jsligo b/gitlab-pages/docs/data-types/src/type-annotations/assumptions.jsligo new file mode 100644 index 0000000000..793067d418 --- /dev/null +++ b/gitlab-pages/docs/data-types/src/type-annotations/assumptions.jsligo @@ -0,0 +1,15 @@ +namespace Counter { + type storage_type = int; + type return_type = [list, storage_type]; + + // @entry + // The type of the value parameter is assumed to be an int + const add = (value, storage: storage_type): return_type => + [[], storage + value]; + + // @entry + // The type of the value parameter is assumed to be an int + const sub = (value, storage: storage_type): return_type => + [[], storage - value]; + +} \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/type-annotations/assumptions.mligo b/gitlab-pages/docs/data-types/src/type-annotations/assumptions.mligo new file mode 100644 index 0000000000..0c6238fc12 --- /dev/null +++ b/gitlab-pages/docs/data-types/src/type-annotations/assumptions.mligo @@ -0,0 +1,15 @@ +module Counter = struct + type storage_type = int + type return_type = operation list * storage_type + + [@entry] + (* The type of the value parameter is assumed to be an int *) + let add (value) (storage: storage_type) : return_type = + [], storage + value + + [@entry] + (* The type of the value parameter is assumed to be an int *) + let sub (value) (storage: storage_type) : return_type = + [], storage - value + +end \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/type-annotations/d.jsligo b/gitlab-pages/docs/data-types/src/type-annotations/d.jsligo new file mode 100644 index 0000000000..79f47cb065 --- /dev/null +++ b/gitlab-pages/docs/data-types/src/type-annotations/d.jsligo @@ -0,0 +1,27 @@ +type parameter = + ["Back"] +| ["Claim"] +| ["Withdraw"]; + +type storage = { + owner : address, + goal : tez, + deadline : timestamp, + backers : map, + funded : bool +}; + +// @entry +function back (_param: unit, store: storage): [list, storage] { + if (Tezos.get_now() > store.deadline) + return failwith ("Deadline passed."); + return $match(Map.find_opt (Tezos.get_sender(), store.backers), { + "None": () => + (() => { const backers = + Map.update(Tezos.get_sender(), + ["Some" as "Some", Tezos.get_amount()], + store.backers); + return [list([]), {...store, backers: backers}]})(), + "Some": _x => [[], store] + }) +}; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/type-annotations/d.mligo b/gitlab-pages/docs/data-types/src/type-annotations/d.mligo similarity index 100% rename from gitlab-pages/docs/language-basics/src/type-annotations/d.mligo rename to gitlab-pages/docs/data-types/src/type-annotations/d.mligo diff --git a/gitlab-pages/docs/data-types/src/type-annotations/inferring.jsligo b/gitlab-pages/docs/data-types/src/type-annotations/inferring.jsligo new file mode 100644 index 0000000000..672a2e6d25 --- /dev/null +++ b/gitlab-pages/docs/data-types/src/type-annotations/inferring.jsligo @@ -0,0 +1,3 @@ +const a: nat = 7; +const b: nat = 5; +const c = a - b; // Inferred to be an int \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/type-annotations/inferring.mligo b/gitlab-pages/docs/data-types/src/type-annotations/inferring.mligo new file mode 100644 index 0000000000..1fca40de4a --- /dev/null +++ b/gitlab-pages/docs/data-types/src/type-annotations/inferring.mligo @@ -0,0 +1,3 @@ +let a : nat = 7 +let b : nat = 5 +let c = a - b (* Inferred to be an int *) \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/types/a.jsligo b/gitlab-pages/docs/data-types/src/types/a.jsligo new file mode 100644 index 0000000000..c9c2cf925e --- /dev/null +++ b/gitlab-pages/docs/data-types/src/types/a.jsligo @@ -0,0 +1,6 @@ +type breed = string; +const dog_breed_1: breed = "Saluki"; +const dog_breed_2: string = "Shiba Inu"; + +const greet_dogs = (dogs: list): string => + List.fold(([a, b]: [breed, breed]) => String.concats([a, ", ", b]), dogs, "Hello"); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/types/a.mligo b/gitlab-pages/docs/data-types/src/types/a.mligo new file mode 100644 index 0000000000..fa3ad0fe4f --- /dev/null +++ b/gitlab-pages/docs/data-types/src/types/a.mligo @@ -0,0 +1,8 @@ +type breed = string +let dog_breed_1 : breed = "Saluki" +let dog_breed_2 : string = "Shiba Inu" + +let greet_dogs (dogs : breed list) : string = + List.fold (fun (a, b : breed * breed) -> String.concats [a; ", "; b]) dogs "Hello" + +let greeting = greet_dogs [dog_breed_1; dog_breed_2] \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/types/b.jsligo b/gitlab-pages/docs/data-types/src/types/b.jsligo similarity index 64% rename from gitlab-pages/docs/language-basics/src/types/b.jsligo rename to gitlab-pages/docs/data-types/src/types/b.jsligo index 2bed63141a..0307eb5d25 100644 --- a/gitlab-pages/docs/language-basics/src/types/b.jsligo +++ b/gitlab-pages/docs/data-types/src/types/b.jsligo @@ -1,6 +1,4 @@ -// The type account_balances denotes maps from addresses to tez - type account_balances = map; const ledger : account_balances = - Map.literal([["tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" as address, 10mutez]]); \ No newline at end of file + Map.literal([["tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" as address, 10 as mutez]]); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/types/b.mligo b/gitlab-pages/docs/data-types/src/types/b.mligo similarity index 70% rename from gitlab-pages/docs/language-basics/src/types/b.mligo rename to gitlab-pages/docs/data-types/src/types/b.mligo index 441c1fb670..c27d4960bd 100644 --- a/gitlab-pages/docs/language-basics/src/types/b.mligo +++ b/gitlab-pages/docs/data-types/src/types/b.mligo @@ -1,5 +1,3 @@ -// The type account_balances denotes maps from addresses to tez - type account_balances = (address, tez) map let ledger : account_balances = diff --git a/gitlab-pages/docs/data-types/src/variants/match_with_block.jsligo b/gitlab-pages/docs/data-types/src/variants/match_with_block.jsligo index bfaff12547..58eef2706a 100644 --- a/gitlab-pages/docs/data-types/src/variants/match_with_block.jsligo +++ b/gitlab-pages/docs/data-types/src/variants/match_with_block.jsligo @@ -1,10 +1,8 @@ -function match_with_block (x : option) : int { - return - match(x) { - when(None): 0; - when(Some(n)): do { - let y = n + 1; - return y - } - }; -}; \ No newline at end of file +const match_with_block = (x : option) : int => + $match(x, { + "None": () => 0, + "Some": n => (() => { + const y = n + 1; + return y; + })(), + }); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/variants/nat_matching.jsligo b/gitlab-pages/docs/data-types/src/variants/nat_matching.jsligo index 0f7663399f..7f045e05e9 100644 --- a/gitlab-pages/docs/data-types/src/variants/nat_matching.jsligo +++ b/gitlab-pages/docs/data-types/src/variants/nat_matching.jsligo @@ -1,5 +1,5 @@ const is_it_a_nat = (i : int) => - match (is_nat(i)) { - when(None): false; - when(Some(n)): do {ignore(n); return true; } - } \ No newline at end of file + $match(is_nat(i), { + "None": () => false, + "Some": n => (() => { ignore(n); return true; })() + }) \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/variants/options.jsligo b/gitlab-pages/docs/data-types/src/variants/options.jsligo index 6fcc69d423..a608716cbb 100644 --- a/gitlab-pages/docs/data-types/src/variants/options.jsligo +++ b/gitlab-pages/docs/data-types/src/variants/options.jsligo @@ -1,3 +1,4 @@ function div (a: nat, b: nat): option { - if (b == 0n) return None() else return Some(a/b) + if (b == (0 as nat)) return ["None" as "None"]; + return ["Some" as "Some", a/b] }; \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/variants/options_euclidean.jsligo b/gitlab-pages/docs/data-types/src/variants/options_euclidean.jsligo deleted file mode 100644 index 2f7afbb1d8..0000000000 --- a/gitlab-pages/docs/data-types/src/variants/options_euclidean.jsligo +++ /dev/null @@ -1,5 +0,0 @@ -// All below equal Some (7,2) -const ediv1: option<[int, nat]> = ediv(37, 5); -const ediv2: option<[int, nat]> = ediv(37n, 5); -const ediv3: option<[nat, nat]> = ediv(37n, 5n); -const ediv4: option<[int, nat]> = ediv(37, 5n); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/variants/options_positive.jsligo b/gitlab-pages/docs/data-types/src/variants/options_positive.jsligo deleted file mode 100644 index 1a4762e131..0000000000 --- a/gitlab-pages/docs/data-types/src/variants/options_positive.jsligo +++ /dev/null @@ -1 +0,0 @@ -const one_is_nat : option = is_nat(1); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/variants/unit.mligo b/gitlab-pages/docs/data-types/src/variants/unit.mligo index 7009843737..baaa0ff7e5 100644 --- a/gitlab-pages/docs/data-types/src/variants/unit.mligo +++ b/gitlab-pages/docs/data-types/src/variants/unit.mligo @@ -1 +1 @@ -let x : unit = () \ No newline at end of file +let x: unit = () \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/variants/variant_matching.jsligo b/gitlab-pages/docs/data-types/src/variants/variant_matching.jsligo index d8b6795f04..e3a101743d 100644 --- a/gitlab-pages/docs/data-types/src/variants/variant_matching.jsligo +++ b/gitlab-pages/docs/data-types/src/variants/variant_matching.jsligo @@ -4,8 +4,8 @@ type colour = | ["Default"]; const int_of_colour = (c : colour) : int => - match(c) { - when(RGB([r,g,b])): 16 + b + g * 6 + r * 36; - when(Gray(i)): 232 + i; - when(Default): 0; - }; \ No newline at end of file + $match(c, { + "RGB": ([r,g,b]) => 16 + b + g * 6 + r * 36, + "Gray": i => 232 + i, + "Default": () => 0 + }); \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/src/variants/variants.jsligo b/gitlab-pages/docs/data-types/src/variants/variants.jsligo index 71095f6d0e..4fcb29869a 100644 --- a/gitlab-pages/docs/data-types/src/variants/variants.jsligo +++ b/gitlab-pages/docs/data-types/src/variants/variants.jsligo @@ -1,6 +1,6 @@ type coin = ["Head"] | ["Tail"]; -let head: coin = Head(); -let tail: coin = Tail(); +let head: coin = ["Head" as "Head"]; +let tail: coin = ["Tail" as "Tail"]; type id = nat; type user = @@ -8,5 +8,5 @@ type user = | ["Manager", id] | ["Guest"]; -const bob : user = Admin(1000n); -const carl : user = Guest(); \ No newline at end of file +const bob : user = ["Admin" as "Admin", 1000 as nat]; +const carl : user = ["Guest" as "Guest"]; \ No newline at end of file diff --git a/gitlab-pages/docs/data-types/strings.md b/gitlab-pages/docs/data-types/strings.md index ab8606f254..6ab78a8189 100644 --- a/gitlab-pages/docs/data-types/strings.md +++ b/gitlab-pages/docs/data-types/strings.md @@ -4,8 +4,8 @@ title: Strings import Syntax from '@theme/Syntax'; -Strings are of the predefined type `string`. Literal strings are set -between double quotes. +Strings are of the predefined type `string`. +Literal strings are set between double quotes. @@ -13,30 +13,29 @@ between double quotes. let a : string = "Hello Alice" ``` -Note: See the predefined -[module String](../reference/string-reference/?lang=cameligo) +For reference, see the predefined [module String](../reference/string-reference/?lang=cameligo). ```jsligo group=strings -const a :string = "Hello Alice"; +const a: string = "Hello Alice"; ``` -Note: See predefined [namespace String](../reference/string-reference/?lang=jsligo) +For reference, see the predefined [namespace String](../reference/string-reference/?lang=jsligo). ### Casting Strings can be used in contexts where a boolean is expected: an empty -string is then interpreted as `false`, and `true` otherwise. +string is interpreted as `false` and a non-empty string is interpreted as `true`. ```cameligo group=strings -let one = if "" then 0 else 1 +let one = if "" then 0 else 1 let zero = if "foo" then 0 else 1 ``` @@ -45,7 +44,7 @@ let zero = if "foo" then 0 else 1 ```jsligo group=strings -const one = "" ? 0 : 1; +const one = "" ? 0 : 1; const zero = "foo" ? 0 : 1; ``` @@ -63,9 +62,6 @@ let greeting = "Hello" let full_greeting = greeting ^ " " ^ name ``` -Note: See the predefined -[module String](../reference/string-reference/?lang=cameligo) - @@ -79,14 +75,11 @@ const greeting = "Hello"; const full_greeting = greeting + " " + name; ``` -Note: See predefined [namespace String](../reference/string-reference/?lang=jsligo) - ## Sizing -The length of a string can be obtain by calling the predefined -functions `String.length` or `String.size`: +To get the length of a string, use the function `String.length` or `String.size`: @@ -94,26 +87,22 @@ functions `String.length` or `String.size`: let length : nat = String.size "Alice" // length = 5n ``` -Note: See the predefined -[module String](../reference/string-reference/?lang=cameligo) - ```jsligo group=length -const length : nat = String.size("Alice"); // length == 5n +const length : nat = String.size("Alice"); // length == (5 as nat) ``` -Note: See predefined [namespace String](../reference/string-reference/?lang=jsligo) - ## Slicing -Substrings can be extracted using the predefined function -`String.sub`. The first character has index 0 and the interval of -indices for the substring has inclusive bounds. +You can extract a substring from a string with the `String.sub` function. +It accepts a nat for the index of the start of the substring and a nat for the number of characters. +Both numbers are inclusive. +The first character of a string has the index 0. @@ -122,9 +111,6 @@ let name = "Alice" let slice = String.sub 0n 1n name // slice = "A" ``` -Note: See the predefined -[module String](../reference/string-reference/?lang=cameligo) - @@ -133,21 +119,16 @@ The offset and length of the slice are natural number: ```jsligo group=slicing const name = "Alice"; -const slice = String.sub (0n, 1n, name); // slice == "A" +const slice = String.sub (0 as nat, 1 as nat, name); // slice == "A" ``` -Note: See predefined [namespace String](../reference/string-reference/?lang=jsligo) - -## Verbatim +## Verbatim strings -Strings can contain control characters, like `\n`. Sometimes we need -that each character in a string is interpreted on its own, for example -`\n` as two characters instead of a newline character. In that case, -either we escape the backslash character, or we use verbatim -strings. Those have the same type `string` as normal (that is, -interpreted) strings. +Strings can contain control characters, like `\n`. +To interpret each character on its own (such as treating `\n` as two characters), you can either escape the backslash character or use _verbatim strings_. +Verbatim strings have the same type as ordinary strings (that is, interpreted strings). @@ -158,9 +139,6 @@ instead of double quotes: let s : string = {|\n|} // String made of two characters ``` -Note: See the predefined -[module String](../reference/string-reference/?lang=cameligo) - @@ -172,7 +150,4 @@ instead of double quotes: const s : string = `\n` // String made of two characters ``` -Note: See predefined [namespace String](../reference/string-reference/?lang=jsligo) - - diff --git a/gitlab-pages/docs/data-types/tez.md b/gitlab-pages/docs/data-types/tez.md index 1c6c3d4471..662506992f 100644 --- a/gitlab-pages/docs/data-types/tez.md +++ b/gitlab-pages/docs/data-types/tez.md @@ -36,7 +36,7 @@ let sum : tez = 5mutez + 0.000_010tez ```jsligo group=add -const sum: tez = 5mutez + 1tez; +const sum: tez = (5 as mutez) + (1 as tez); ``` @@ -59,8 +59,10 @@ let negative : tez option = 1mutez - 5mutez (* None *) ```jsligo group=subtract -const amount: option = 5mutez - 1mutez; /* Some (4mutez) */ -const negative: option = 1mutez - 5mutez; /* None */ +// ["Some" as "Some", 4 as mutez] +const amount: option = (5 as mutez) - (1 as mutez); +// ["None" as "None"] +const negative: option = (1 as mutez) - (5 as mutez); ``` @@ -80,7 +82,7 @@ let mult : tez = 5n * 5mutez ```jsligo group=multiply -const mult: tez = 5n * 5mutez; +const mult: tez = (5 as nat) * (5 as mutez); ``` @@ -102,7 +104,7 @@ let div : nat = 10mutez / 3mutez ```jsligo group=divide -const div: nat = 10mutez / 3mutez; +const div: nat = (10 as mutez) / (3 as mutez); ``` @@ -136,11 +138,11 @@ function `ediv` is overloaded to accept tez, beyond all the combinations of natural and integer numbers: ```jsligo group=tez_euclidean -// Some (7, 2mutez) -const ediv1: option<[nat, tez]> = ediv(37mutez, 5mutez); +// ["Some" as "Some", [7, (2 as mutez)]] +const ediv1: option<[nat, tez]> = ediv(37 as mutez, 5 as mutez); -// Some (7mutez, 2mutez) -const ediv2: option<[tez, tez]> = ediv(37mutez, 5n); +// ["Some" as "Some", [7 as mutez, 2 as mutez]] +const ediv2: option<[tez, tez]> = ediv(37 as mutez, 5 as nat); ``` diff --git a/gitlab-pages/docs/data-types/tickets.md b/gitlab-pages/docs/data-types/tickets.md index 12fb43b4c0..4a06027dd0 100644 --- a/gitlab-pages/docs/data-types/tickets.md +++ b/gitlab-pages/docs/data-types/tickets.md @@ -17,29 +17,37 @@ A ticket of type `ticket` has three elements: A ticket's ticketer and contents cannot be changed. -Tickets themselves cannot be duplicated, but you can split one ticket into multiple tickets by creating duplicate tickets each with a portion of the original ticket's amount. -The new tickets have the same ticketer and contents, and the sum of their amounts is always the amount of the original ticket. -Similarly, you can join tickets with matching ticketers and contents into a single ticket with the sum of the joined tickets' amounts. +Tickets themselves cannot be duplicated, but you can split one ticket +into multiple tickets by creating duplicate tickets each with a +portion of the original ticket's amount. The new tickets have the +same ticketer and contents, and the sum of their amounts is always the +amount of the original ticket. Similarly, you can join tickets with +matching ticketers and contents into a single ticket with the sum of +the joined tickets' amounts. ## Creating tickets -To create a ticket, pass the contents and the amount to `Tezos.create_ticket` function. -The function returns an option that contains the ticket or `None` if the amount of the ticket is zero. -The contract's address automatically becomes the ticketer value. +To create a ticket, pass the contents and the amount to +`Tezos.Ticket.create` function. The function returns an +option that contains the ticket or `None` if the amount of the ticket +is zero. The contract's address automatically becomes the ticketer +value. -val Tezos.create_ticket : 'value -> nat -> ('value ticket) option +val create : 'value -> nat -> ('value ticket) option -let Tezos.create_ticket: 'value => nat => option<ticket<'value>> +create: 'value => nat => option<ticket<'value>> ```cameligo group=manip_ticket -let my_ticket1 = Option.unopt (Tezos.create_ticket 1 10n) -let my_ticket2 = Option.unopt (Tezos.create_ticket "one" 10n) +module Ticket = Tezos.Ticket + +let my_ticket1 = Option.value_with_error "ticket1 failed" (Ticket.create 1 10n) +let my_ticket2 = Option.value_with_error "ticket2 failed" (Ticket.create "one" 10n) ``` @@ -47,26 +55,34 @@ let my_ticket2 = Option.unopt (Tezos.create_ticket "one" 10n) ```jsligo group=manip_ticket -const my_ticket1 = Option.unopt(Tezos.create_ticket(1, 10n)); -const my_ticket2 = Option.unopt(Tezos.create_ticket("one", 10n)); +import Ticket = Tezos.Ticket; + +const my_ticket1 = + Option.value_with_error("ticket1 failed", Ticket.create(1, 10 as nat)); +const my_ticket2 = + Option.value_with_error("ticket2 failed", Ticket.create("one", 10 as nat)); ``` ## Reading tickets -You cannot read the contents of a ticket directly; you must use the `Tezos.read_ticket` function to access it. -This function destroys the ticket and returns the ticketer, contents, amount, and a copy of the original ticket. +You cannot read the contents of a ticket directly; you must use the +`Tezos.Ticket.read` function to access it. This function +destroys the ticket and returns the ticketer, contents, amount, and a +copy of the original ticket. -Note that reading a ticket with the `Tezos.read_ticket` function consumes it, destroying the original ticket. -To preserve the ticket, you must use the copy that the function returns, or else the ticket is destroyed. +Note that reading a ticket with the `Tezos.Ticket.read` +function consumes it, destroying the original ticket. To preserve the +ticket, you must use the copy that the function returns, or else the +ticket is destroyed. -val Tezos.read_ticket : 'value ticket -> (address * ('value * nat)) * 'value ticket +val read : 'value ticket -> (address * ('value * nat)) * 'value ticket -let Tezos.read_ticket: ticket<'value> => <<address, <'value , nat>> , ticket<'value>> +read: ticket<'value> => <<address, <'value , nat>> , ticket<'value>> @@ -76,7 +92,7 @@ destructuring or pattern matching: ```cameligo group=manip_ticket let v = - let (_addr, (payload, _amt)), _ticket = Tezos.read_ticket my_ticket1 + let (_addr, (payload, _amt)), _ticket = Ticket.read my_ticket1 in payload ``` @@ -87,36 +103,39 @@ let v = To read the content of a ticket, you need to use tuple destructuring: ```jsligo group=manip_ticket -const v2 = do { - let [[_addr, [payload, _amt]], _ticket] = Tezos.read_ticket (my_ticket2); +const v2 = (() => { + let [[_addr, [payload, _amt]], _ticket] = Ticket.read(my_ticket2); return payload; -} +})() ``` ## Splitting tickets -Splitting a ticket creates two tickets that have the same ticketer and contents as the original and have amounts that add up to the amount of the original -To split a ticket, pass the ticket and two nats to the `Tezos.split_ticket` function. -It returns an option that is `None` if the sum of the two nats does not equal the amount of the original ticket. -If the sum is equal, it returns `Some` with two tickets with the two nats as their amounts. +Splitting a ticket creates two tickets that have the same ticketer and +contents as the original and have amounts that add up to the amount of +the original To split a ticket, pass the ticket and two nats to the +`Tezos.Ticket.split` function. It returns an option that is +`None` if the sum of the two nats does not equal the amount of the +original ticket. If the sum is equal, it returns `Some` with two +tickets with the two nats as their amounts. You can split tickets to divide a ticket to send to multiple sources or to consume only part of a ticket's amount. -val Tezos.split_ticket : 'value ticket -> nat * nat -> ('value ticket * 'value ticket) option +val split : 'value ticket -> nat * nat -> ('value ticket * 'value ticket) option -let Tezos.split_ticket: ticket<'value> => <nat , nat> => option <<ticket<'value>, ticket<'value>>> +split : ticket<'value> => <nat , nat> => option <<ticket<'value>, ticket<'value>>> ```cameligo group=manip_ticket let ta, tb = - match Tezos.split_ticket my_ticket1 (6n, 4n) with + match Ticket.split my_ticket1 (6n, 4n) with None -> failwith "amt_a + amt_v <> amt" | Some split_tickets -> split_tickets ``` @@ -127,35 +146,39 @@ let ta, tb = ```jsligo group=manip_ticket const [ta, tb] = - match(Tezos.split_ticket(my_ticket1, [6n, 4n])) { - when(None()): failwith("amt_a + amt_v != amt"); - when(Some(split_tickets)): split_tickets - }; + $match(Ticket.split(my_ticket1, [6 as nat, 4 as nat]), { + "None": () => failwith("amt_a + amt_v != amt"), + "Some": split_tickets => split_tickets + }); ``` ## Joining tickets -You can join tickets that have identical ticketers and contents. -The `Tezos.join_tickets` function joins tickets and returns an option with `Some` with a single ticket that has an amount that equals the sum of the amounts of the original tickets. -If the ticketer or contents don't match, it returns `None`. +You can join tickets that have identical ticketers and contents. The +`Tezos.Ticket.join` function joins tickets and returns an +option with `Some` with a single ticket that has an amount that equals +the sum of the amounts of the original tickets. If the ticketer or +contents don't match, it returns `None`. -val Tezos.join_tickets : 'value ticket * 'value ticket -> ('value ticket) option +val join : 'value ticket * 'value ticket -> ('value ticket) option -let Tezos.join_tickets = <ticket<'value>, ticket<'value>> => option <ticket<'value>> +join : <ticket<'value>, ticket<'value>> => option <ticket<'value>> -```cameligo group=manip_ticket +```cameligo group=manip_ticket2 +module Ticket = Tezos.Ticket + let tc : int ticket option = - let ta = Option.unopt (Tezos.create_ticket 1 10n) in - let tb = Option.unopt (Tezos.create_ticket 1 5n) in - Tezos.join_tickets (ta, tb) + let ta = Option.value_with_error "ta failed" (Ticket.create 1 10n) in + let tb = Option.value_with_error "tb failed" (Ticket.create 1 5n) in + Ticket.join (ta, tb) ``` @@ -163,15 +186,21 @@ let tc : int ticket option = ```jsligo group=manip_ticket2 -const ta = Option.unopt(Tezos.create_ticket(1, 10n)); -const tb = Option.unopt(Tezos.create_ticket(1, 5n)); -const tc = Tezos.join_tickets([ta, tb]); +import Ticket = Tezos.Ticket; + +const ta = Option.value_with_error("ta failed", + Ticket.create(1, 10 as nat)); +const tb = Option.value_with_error("tb failed", + Ticket.create(1, 5 as nat)); +const tc = Ticket.join([ta, tb]); ``` ## Transferring tickets -You can send tickets to other contracts by passing them with the `Tezos.transaction` function, just like passing any other value to a contract. +You can send tickets to other contracts by passing them with the +`Tezos.Operation.transaction` function, just like passing any other +value to a contract. - \ No newline at end of file + diff --git a/gitlab-pages/docs/data-types/tuples.md b/gitlab-pages/docs/data-types/tuples.md index 924d2ce03d..6b5480d7b7 100644 --- a/gitlab-pages/docs/data-types/tuples.md +++ b/gitlab-pages/docs/data-types/tuples.md @@ -4,36 +4,49 @@ title: Tuples import Syntax from '@theme/Syntax'; +Tuples are a list of a specific number of values in a specific order. +The elements in a tuple are called its _components_. + +Unlike lists and sets, tuples have a specific size and cannot add or remove components. +As a result, you can access tuple components in random order by their index (or position). +Also unlike lists and sets, tuples can contain components of different types. + +A common type of tuple is the _pair_, a tuple of two components. +For example, if you wanted to store coordinates on a two-dimensional grid, you could use a pair `(x, y)`. +These coordinates are in a specific order, so `(x, y)` is not equivalent to `(y, x)`. +The number of components is fixed, so you cannot add a third component to the tuple after it is defined and make it `(x, y, z)`. +In this way, the number of components is part of the type of the tuple, so a tuple with two components does not have the same type as a tuple with three components and therefore cannot be used in the same places, like function parameters. + +:::note + +One important use of tuples is in the return values of contract entrypoints. +Each entrypoint returns a tuple, of which the first component is a list of operations to run and the second is the new value of the contract storage. + +Tuples also often appear as function parameters, which can be confusing if you assume that they receive individual values. +For example, the function that you use for folding (as in [Folding lists](./lists#folding-lists) and [Folding sets](./sets#folding-sets)) receives a pair as a parameter instead of two separate values. + +::: + -Tuples gather a given number of values in a specific order and those -values, called *components*, can be retrieved by their index -(position). Probably the most common tuple is the *pair*. For -example, if we were storing coordinates on a two dimensional grid we -might use a pair `(x,y)` to store the coordinates `x` and `y`. There -is a *specific order*, so `(y,x)` is not equal to `(x,y)` in -general. The number of components is part of the type of a tuple, so, -for example, we cannot add an extra component to a pair and obtain a -triple of the same type: `(x,y)` has always a different type from -`(x,y,z)`, whereas `(y,x)` might have the same type as `(x,y)`. - -> Parentheses are optional, so `(x,y)` can be written `x,y` if the -> context captures neither `x` nor `y`. - -Tuple components can be of arbitrary types. A pair is a 2-tuple. If it -contains a first component of type `t_1` and a second component of -type `t_2`, its type is written `t_1 * t_2`. If more components: `t1 * -t2 * ... * t_n`. (We can think of tuple types as products of types.) -Tuple types do not have to be defined before they can be used: +:::note + +Parentheses are optional, so the tuple `(x,y)` can be written as `x,y` if the context captures neither `x` nor `y`. + +::: + +Tuple components can have different types. +If a pair contains a first component of type `t_1` and a second component of type `t_2`, its type is written `t_1 * t_2`. +If there are more components, the type can be `t1 * t2 * ... * t_n`. +In this way, you can think of tuple types as products of types. + +Tuple types do not have to be defined before they can be used, as in this example, which implicitly creates a pair of strings: ```cameligo group=tuple let friends = "Alice", "Bob" // Parentheses are optional ``` -but it is sometimes more informative to define a type. Type -definitions are introduced with the keyword `type`, like value -definitions are with `let`. Instead of a value expression as a -right-hand side, we have a type expression: +However, defining the type of the tuple explicitly can make code easier to read, as in this example: ```cameligo group=tuple type couple = string * string @@ -44,85 +57,81 @@ let friends : couple = "Alice", "Bob" // Parentheses are optional -Tuples gather a given number of values in a specific order and those -values, called *components*, can be retrieved by their index -(position). Probably the most common tuple is the *pair*. For -example, if we were storing coordinates on a two dimensional grid we -might use a pair `[x, y]` to store the coordinates `x` and `y`. There -is a *specific order*, so `[y, x]` is not equal to `[x, y]` in -general. The number of components is part of the type of a tuple, so, -for example, we cannot add an extra component to a pair and obtain a -triple of the same type: `[x, y]` has always a different type from -`[x, y, z]`, whereas `[y, x]` might have the same type as `[x, y]`. - -Tuple components can be of arbitrary types. A pair is a 2-tuple. If it -contains a first component of type `t_1` and a second component of -type `t_2`, its type is written `[t_1, t_2]`. If more components: -`[t1, t2, ..., t_n]`. (We can think of tuple types as products of -types.) Tuple types do not have to be defined before they can be used: +Tuple components can have different types. +If a pair contains a first component of type `t_1` and a second component of type `t_2`, its type is written `[t_1, t_2]`. +If there are more components, the type can be `[t1, t2, ..., t_n]`. +In this way, you can think of tuple types as products of types. + +Tuple types do not have to be defined before they can be used, as in this example, which implicitly creates a pair of strings: ```jsligo group=tuple const friends = ["Alice", "Bob"]; ``` -but it is sometimes more informative to define a type. Type -definitions are introduced with the keyword `type`, like value -definitions are with `const`. Instead of a value expression as a -right-hand side, we have a type expression: +However, defining the type of the tuple explicitly can make code easier to read, as in this example: ```jsligo group=tuple_alt type couple = [string, string]; -const friends : couple = ["Alice", "Bob"]; +const friends: couple = ["Alice", "Bob"]; ``` -## Accessing - -If we want to get the first and second names of a pair, we can use -destructuring. Destructuring a tuple allows us to give names to the -elements inside the tuple: +You can nest tuples to create complex custom data types. +This example shows a tuple whose second component is a tuple: -```cameligo group=destructuring -let friends = "Alice", "Bob" // Parentheses are optional -let alice, bob = friends +```cameligo group=nested +type nested_tuple = int * (string * nat) +let my_nested_value = (-5, ("Hello", 12)) ``` -That single definition actually introduces in the current scope *two* -constants, `alice` and `bob`. Sometimes we might want to ignore a -component of the tuple, in which case we use the character `_`: + + + + +```jsligo group=nested +type nested_tuple = [int, [string, nat]] +const my_nested_value = [-5, ["Hello", 12]]; +``` + + + +## Accessing components (destructuring) + +To get the components of a tuple, you can destructure the tuple into separate variables. +This example assigns the values of a pair to two variables: + + ```cameligo group=destructuring -let alice, _ = friends +let friends = "Alice", "Bob" // Parentheses are optional +let alice, bob = friends ``` -Alternatively, if we still want to give a meaningful name to a useless -component, we can use a silent variable for it, by prefixing it with -`_`: +That single definition creates two variables in the current scope: `alice` and `bob`. +If you are not going to use any of these variables, give them the name `_` or a name that starts with an underscore to make them into silent variables. +Using silent variables prevents the LIGO compiler from warning you about unused variables, as in this example: ```cameligo group=destructuring -let alice, _bob = friends // This alice shadows the previous one +let alice, _bob = friends (* This alice shadows the previous one *) ``` -We can destructure nested tuples in the same manner: +You can destructure nested tuples in the same manner: ```cameligo group=destructuring let deep = (1, (2n, "Hello")) let _, (_, greeting) = deep // greeting = "Hello" ``` -This works well if we want to give a name to a component (like -`greeting` above), but we might simply want the value of a component -without naming it. In that case, we use the binary operator `.`: +If you don't want to create variables, you can access the components of tuples by their indexes. +This example gets the second component of the second component of the nested tuple in the previous example: ```cameligo group=destructuring let film = deep.1.1 ^ ", Dolly!" // film = "Hello, Dolly!" ``` -The first component has index `0`, the second `1` etc. - @@ -133,33 +142,32 @@ const friends = ["Alice", "Bob"]; const [alice, bob] = friends; ``` -That single definition actually introduces in the current scope *two* -constants, `alice` and `bob`. Alternatively, if we still want to give -a meaningful name to a useless component, we can use a silent variable -for it, by prefixing it with `_`: +That single definition creates two variables in the current scope: `alice` and `bob`. +If you are not going to use any of these variables, give them the name `_` or a name that starts with an underscore to make them into silent variables. +Using silent variables prevents the LIGO compiler from warning you about unused variables, as in this example: ```jsligo group=destructuring const [alice2, _bob] = friends; ``` -Note how we renamed `alice` as `alice2` in order to avoid a collision -(redefinition) with previous one in the same top-level scope. - -We can destructure nested tuples in the same manner: +You can destructure nested tuples in the same manner: ```jsligo group=destructuring const deep = [1, [2n, "Hello"]]; const [_x, [_y, greeting]] = deep; // greeting == "Hello" ``` -This works well if we want to give a name to a component (like -`greeting` above), but we might simply want the value of a component -without naming it. In that case, we use the binary operator `[]`: +If you don't want to create variables, you can access the components of tuples by their indexes. +This example gets the second component of the second component of the nested tuple in the previous example: ```jsligo group=destructuring const film = deep[1][1] + ", Dolly!" // film == "Hello, Dolly!" ``` -The first component has index `0`, the second `1` etc. - + +:::note + +Tuple indexes start at zero. + +::: diff --git a/gitlab-pages/docs/data-types/type-annotations.md b/gitlab-pages/docs/data-types/type-annotations.md new file mode 100644 index 0000000000..925d94d67b --- /dev/null +++ b/gitlab-pages/docs/data-types/type-annotations.md @@ -0,0 +1,184 @@ +--- +id: type-annotations +title: Type annotations +--- + +import Syntax from '@theme/Syntax'; + +As described in [Types](./types), the LIGO compiler must know the type of each variable to be able to compile to Michelson, which is strongly and statically typed. +LIGO can infer or assume the types of variables in many cases, but in cases where it can't, you can manually annotate the types of variables, as in TypeScript and Ocaml. +Annotating types can also improve code readability. + +## Annotating types + + + +As in Ocaml, you can annotate a variable with its type by adding a colon and the type or type alias: + +```cameligo group=annotate +let myInteger : int = 5 +let myString : string = "Hello" +``` + +More complex types such as lists and maps take sub-types, as in these examples: + +```cameligo group=annotate +let myList : int list = [1; 2; 3] +let myMap : (string, int) map = + Map.literal [ + ("one", 1); + ("two", 2); + ] +``` + +For readability, contracts often annotate function and entrypoint parameters and return types, as in this example: + +```cameligo group=annotate +module Counter = struct + type storage_type = int + type return_type = operation list * storage_type + + [@entry] + let add (value : int) (storage: storage_type) : return_type = + [], storage + value + + [@entry] + let sub (value : int) (storage: storage_type) : return_type = + [], storage - value + +end +``` + + + + + +As in TypeScript, you can annotate a variable with its type by adding a colon and the type or type alias: + +```jsligo group=annotate +const myInteger: int = 5; +const myString: string = "Hello"; +``` + +Similarly, you can annotate the value that you assign to a variable: + +```jsligo group=annotate +const myOtherInteger = 5 as int; +const myOtherString = "Hello" as string; +``` + +More complex types such as lists and maps take sub-types in angle brackets, as in these examples: + +```jsligo group=annotate +const myList: list = [1, 2, 3]; +const myMap: map = + Map.literal([ + ["one", 1], + ["two", 2], + ]); +``` + +For readability, contracts often annotate function and entrypoint parameters and return types, as in this example: + +```jsligo group=annotate +namespace Counter { + type storage_type = int; + type return_type = [list, storage_type]; + + // @entry + const add = (value: int, storage: storage_type): return_type => + [[], storage + value]; + + // @entry + const sub = (value: int, storage: storage_type): return_type => + [[], storage - value]; + +} +``` + + + +## Inferring types + +Sometimes, the LIGO compiler can infer a variable's type from the context. + +The following example subtracts two nats and puts the result in a variable. +This variable does not have an explicit type declaration, but the compiler infers that it is a number and that it is an int because the value could be negative, even though in this case it is positive: + + + +```cameligo group=inferring +let a : nat = 7 +let b : nat = 5 +let c = a - b (* Inferred to be an int *) +``` + + + + + +```jsligo group=inferring +const a: nat = 7; +const b: nat = 5; +const c = a - b; // Inferred to be an int +``` + + + +## Type assumptions + +In some cases, the LIGO compiler assumes a variable's type when it does not have complete information. + +For example, when LIGO knows that a variable is a number but not whether that number is an integer or a nat, it assumes that it is an integer. +Similarly, LIGO assumes that a list of literal values in brackets is a tuple, not a list, unless you cast that list with the `list` function. + +The following contract has two entrypoints that each accept one parameter. +The types of the parameters are not specified, but the compiler can infer that they are numbers by how they are used. +From there, it assumes that they are integers and therefore types them as integers in the compiled contract. + + + +```cameligo group=assumptions +module Counter = struct + type storage_type = int + type return_type = operation list * storage_type + + [@entry] + (* The type of the value parameter is assumed to be an int *) + let add (value) (storage: storage_type) : return_type = + [], storage + value + + [@entry] + (* The type of the value parameter is assumed to be an int *) + let sub (value) (storage: storage_type) : return_type = + [], storage - value + +end +``` + + + + + +```jsligo group=assumptions +namespace Counter { + type storage_type = int; + type return_type = [list, storage_type]; + + // @entry + // The type of the value parameter is assumed to be an int + const add = (value, storage: storage_type): return_type => + [[], storage + value]; + + // @entry + // The type of the value parameter is assumed to be an int + const sub = (value, storage: storage_type): return_type => + [[], storage - value]; + +} +``` + + + + + diff --git a/gitlab-pages/docs/data-types/types.md b/gitlab-pages/docs/data-types/types.md new file mode 100644 index 0000000000..0963166f91 --- /dev/null +++ b/gitlab-pages/docs/data-types/types.md @@ -0,0 +1,94 @@ +--- +id: types +title: Data types +--- + +import Syntax from '@theme/Syntax'; + +**LIGO is strongly and statically typed.** + +This means that the LIGO compiler must know what type each variable is to compile to Michelson, which is also strongly and statically typed. +The compiler also uses type information to check how contracts process data, ensuring that each function's expectations are met. +If it passes the test, your contract will not fail at run-time due to some inconsistent assumptions on your data. +This test is called *type checking*. + +LIGO types are built on top of Michelson's type system, so there are many similarities but not a one-to-one match between LIGO types and Michelson types. +For information on Michelson types, see [Michelson: the language of Smart Contracts in Tezos](https://octez.tezos.com/docs/active/michelson.html) in the Octez documentation. + +Types and type annotations work in a way similar to TypeScript and OCaml; see [Type annotations](./type-annotations). + +## Built-in types + +For reference, you can find all the LIGO built-in types [here](https://gitlab.com/ligolang/ligo/-/blob/dev/src/main/build/ligo_lib/std_lib.mligo#L1-33). + +## Type aliases + +You can create aliases for types to give them more precise names, which can increase readability and maintainability of your code. +The following example creates an alias of the string type named `breed` to indicate variables that describe animal breeds. +Then it creates a function that accepts variables of that type: + + + +```cameligo group=a +type breed = string +let dog_breed_1 : breed = "Saluki" +let dog_breed_2 : string = "Shiba Inu" + +let greet_dogs (dogs : breed list) : string = + List.fold (fun (a, b : breed * breed) -> String.concats [a; ", "; b]) dogs "Hello" + +let greeting = greet_dogs [dog_breed_1; dog_breed_2] +``` + + + + + +```jsligo group=a +type breed = string; +const dog_breed_1: breed = "Saluki"; +const dog_breed_2: string = "Shiba Inu"; + +const greet_dogs = (dogs: list): string => + List.fold(([a, b]: [breed, breed]) => String.concats([a, ", ", b]), dogs, "Hello"); +``` + + + +:::note + +Type aliases are not separate types. +Types and their aliases are interchangeable. +As shown in the previous example, you can pass any string to a function that accepts an alias of a string type. + +::: + +## Custom types + +You can create custom types with the `type` keyword. +Then you can use those types in other places, as in this example, which creates a type to represent a map that uses addresses as keys and amounts of tez as values: + + + +```cameligo group=b +type account_balances = (address, tez) map + +let ledger : account_balances = + Map.literal + [(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), 10mutez)] +``` + + + + + +```jsligo group=b +type account_balances = map; + +const ledger : account_balances = + Map.literal([["tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" as address, 10 as mutez]]); +``` + + + + diff --git a/gitlab-pages/docs/data-types/variants.md b/gitlab-pages/docs/data-types/variants.md index 9c30646162..72d7b9f8e6 100644 --- a/gitlab-pages/docs/data-types/variants.md +++ b/gitlab-pages/docs/data-types/variants.md @@ -4,13 +4,11 @@ title: Variants import Syntax from '@theme/Syntax'; -A variant type is a type that defines a type by the union of -non-overlapping cases, so a value of a variant type is either this, or -that or... The simplest variant type is equivalent to the enumerated -types found in Java, C++, JavaScript etc. +Variant types have one or more non-overlapping cases. +A value of a variant type can be one of these cases or another but never more than one case. -Here is how we define a coin as being either head or tail (and nothing -else): +Simple variant types are similar to enumerated types found in many other languages. +For example, this type defines a coin as being either heads or tails (and nothing else): @@ -26,20 +24,18 @@ let tail : coin = Tail ```jsligo group=variants type coin = ["Head"] | ["Tail"]; -let head: coin = Head(); -let tail: coin = Tail(); +let head: coin = ["Head" as "Head"]; +let tail: coin = ["Tail" as "Tail"]; ``` -The names `Head` and `Tail` in the definition of the type `coin` are -called *data constructors*, or *variants*. In this particular case, -they carry no information beyond their names, so they are called -*constant constructors*. +The names `Head` and `Tail` in the definition of the type `coin` are called *data constructors*. +In this particular case, they carry no information beyond their names, so they are called *constant constructors*. -In general, it is interesting for variants to carry some information, -and thus go beyond enumerated types. In the following, we show how to -define different kinds of users of a system. +Variants can carry more information than just their constructors. +Each constructor in a variant can specify types that the constructor contains. +For example, this variant types defines different kinds of users of a system, with some having an ID number and others not: @@ -71,21 +67,27 @@ type user = | ["Manager", id] | ["Guest"]; -const bob : user = Admin(1000n); -const carl : user = Guest(); +const bob : user = ["Admin" as "Admin", 1000 as nat]; +const carl : user = ["Guest" as "Guest"]; ``` -A constant constructor is equivalent to the same constructor taking an -argument of type `unit`, so, for example, `Guest()` is the same value -as `Guest([])` or `Guest(unit)`. +A constant constructor is equivalent to the same constructor taking a value of type `unit`, so, for example, `["Guest" as "Guest"]` is the same value as `["Guest" as "Guest", []]` and `["Guest" as "Guest", unit]`. + +:::note + +To create a variable of a variant type, you must specify its value as a tuple where the first value is the constructor and the second value is the value that the constructor takes. +To prevent the compiler from seeing the name of the constructor as a string, you must set its type as one of the constructors from the variant type with the `as` syntax, as in the previous examples. + +::: ## Unit The type `unit` is a predefined type that contains only one value that -carries no information. It is used when no relevant information is -required or produced. +carries no information. +It is used when no relevant information is +required or produced, as in constant constructors. @@ -93,7 +95,7 @@ The unique value of type `unit` is written `()`, like an empty tuple, following the OCaml convention. ```cameligo group=unit -let x : unit = () +let x: unit = () ``` Imperative statements, like statements and loops, will have type @@ -113,116 +115,58 @@ const x : unit = []; ## Options - +The option type is a predefined variant type that has two cases: `Some(v)`, where `v` is some value of any type, and `None`. + +Some functions return options when they are not defined for certain inputs. +For example, you can get a value from a big map by passing the key to the `Big_map.find_opt` function. +This function returns an option that is `Some` with the value if the key is defined in the big map or `None` with unit if it is not. + +Similarly, division by zero is not defined, so this function divides two numbers and returns `Some` if the result is defined or `None` if it is not: -The `option` type is a parametric, predefined variant type that is -used to express whether there is a value of some type or none. This is -especially useful when calling a *partial function*, that is, a -function that is not defined for some inputs. In that case, the value -of the `option` type would be `None`, otherwise `Some (v)`, where `v` -is some meaningful value *of any type*. A typical example from -arithmetics is the division: + ```cameligo group=options let div (a, b : nat * nat) : nat option = if b = 0n then None else Some (a/b) ``` -Note: See the predefined -[module Option](../reference/option-reference/?lang=cameligo) - -The `option` type is a parametric, predefined variant type that is -used to express whether there is a value of some type or none. This is -especially useful when calling a *partial function*, that is, a -function that is not defined for some inputs. In that case, the value -of the `option` type would be `None()`, otherwise `Some(v)`, where `v` -is some meaningful value *of any type*. A typical example from -arithmetics is the division: - ```jsligo group=options function div (a: nat, b: nat): option { - if (b == 0n) return None() else return Some(a/b) + if (b == (0 as nat)) return ["None" as "None"]; + return ["Some" as "Some", a/b] }; ``` -Note: See the predefined -[namespace Option](../reference/option-reference/?lang=jsligo) - -### Euclidean Division +As a shortcut to dealing with options, you can use the function `Option.value_with_error` to assume that an option is `Some` and retrieve the value. +If the option is `None`, the function throws an error. - - -For cases when you need both the quotient and the remainder, LIGO -provides the `ediv` operation. `ediv x y` returns `Some (quotient, -remainder)`, unless `y` is zero, in which case it returns `None`. The -function `ediv` is overloaded to accept all the combinations (4) of -natural and integer numbers: - -```cameligo group=options_euclidean -// All below equal Some (7,2) -let ediv1 : (int * nat) option = ediv 37 5 -let ediv2 : (int * nat) option = ediv 37n 5 -let ediv3 : (nat * nat) option = ediv 37n 5n -let ediv4 : (int * nat) option = ediv 37 5n -``` - - - - - -For cases when you need both the quotient and the remainder, LIGO -provides the `ediv` operation. `ediv(x,y)` returns `Some (quotient, -remainder)`, unless `y` is zero, in which case it returns `None`. The -function `ediv` is overloaded to accept all the combinations (4) of -natural and integer numbers: - -```jsligo group=options_euclidean -// All below equal Some (7,2) -const ediv1: option<[int, nat]> = ediv(37, 5); -const ediv2: option<[int, nat]> = ediv(37n, 5); -const ediv3: option<[nat, nat]> = ediv(37n, 5n); -const ediv4: option<[int, nat]> = ediv(37, 5n); -``` - - - -### Checking positivity - -You can check if a value is a natural number (`nat`) by using a -predefined cast function which accepts an integer (`int`) and returns -an optional natural number (`nat`): if the result is `None`, then the -given integer was positive, otherwise the corresponding natural number -`n` is given with `Some(n)`. +However, the best way to deal with option types is by pattern matching, as described in the next section, because it allows you to handle both cases explicitly. -```cameligo group=options_positive -let one_is_nat : nat option = is_nat (1) -``` +For more functions that deal with option types, see the predefined [module Option](../reference/option-reference/?lang=cameligo). -```jsligo group=options_positive -const one_is_nat : option = is_nat(1); -``` +For more functions that deal with option types, see the predefined [namespace Option](../reference/option-reference/?lang=jsligo). ## Matching -Variant types being, in essence, the disjunctive union of cases akin -to types, values of such types need to be examined case by case: this -is what *pattern matching* does. +To work with variants and options, you must handle each case of the type. +LIGO handles different cases by *pattern matching*, which uses the `$match` predefined function to run different code for each case. +The `$match` function must cover all of the cases of the type. -Here is a function that transforms a colour variant type to an integer. +For example, the following code defines a colour variant type and a function that converts values of that type to a single integer by using pattern matching on each variant: @@ -252,36 +196,30 @@ type colour = | ["Default"]; const int_of_colour = (c : colour) : int => - match(c) { - when(RGB([r,g,b])): 16 + b + g * 6 + r * 36; - when(Gray(i)): 232 + i; - when(Default): 0; - }; + $match(c, { + "RGB": ([r,g,b]) => 16 + b + g * 6 + r * 36, + "Gray": i => 232 + i, + "Default": () => 0 + }); ``` -> Note: The `when`-clauses must cover all the variants of the type -> `colour`. When the constructor has no argument, which is equivalent -> to having a `[]` (unit) argument, it can be omitted, hence -> `when(Default)` instead of `when(Default())`. +As its parameters, the `$match` function receives the variant value to match on and an object. +The property names of the object are the constructors of the corresponding variant type. +The property values are either a single expression or expressions that receive the value of the variant as a parameter. +When the variant has no value, as in constant constructors or the `None` variant, the expression receives unit as a parameter. -The right-hand sides of each `when`-clause is an expression. Sometimes -we might need statements to be processed before a value is given to -the clause. In that case, the `do` expression comes handy. It enables -the opening of a block of statements like a function body, that is, a -block ended with a `return` statement whose argument has the value of -the block, like so: +For complex expressions, you can use an immediately invoked function expression (IIFE) as the result of a match. +This allows you to use a block of statements with a `return` statement like a function body, as in this example: ```jsligo group=match_with_block -function match_with_block (x : option) : int { - return - match(x) { - when(None): 0; - when(Some(n)): do { - let y = n + 1; - return y - } - }; -}; +const match_with_block = (x : option) : int => + $match(x, { + "None": () => 0, + "Some": n => (() => { + const y = n + 1; + return y; + })(), + }); ``` @@ -304,10 +242,10 @@ let is_it_a_nat (i : int) = ```jsligo group=nat_matching const is_it_a_nat = (i : int) => - match (is_nat(i)) { - when(None): false; - when(Some(n)): do {ignore(n); return true; } - } + $match(is_nat(i), { + "None": () => false, + "Some": n => (() => { ignore(n); return true; })() + }) ``` diff --git a/gitlab-pages/docs/faq/addr-to-contract.md b/gitlab-pages/docs/faq/addr-to-contract.md index c41e0e22f5..81f5499b9d 100644 --- a/gitlab-pages/docs/faq/addr-to-contract.md +++ b/gitlab-pages/docs/faq/addr-to-contract.md @@ -5,20 +5,20 @@ title: How to convert an address to a contract in LIGO testing framework ? import Syntax from '@theme/Syntax'; -In the context of testing framework, -if you want to convert an address to a contract, -you need to convert `address` to `typed_address` using `Test.cast_address`. - -Then convert `typed_address` to `contract` using -`Test.to_contract`. For example: +In the context of testing framework, if you want to convert an address +to a contract, you need to convert `address` to `typed_address` using +`Test.Typed_address.to_typed_address`. +Then cast `typed_address` to `unit contract` using +`Test.Typed_address.to_contract`. For example: + ```cameligo test-ligo group=addr2contract let test = let addr : address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" in - let taddr : (unit, unit) typed_address = Test.cast_address addr in - let contract : (unit) contract = Test.to_contract taddr in + let taddr : (unit, unit) typed_address = Test.Address.to_typed_address addr in + let contract : unit contract = Test.Typed_address.to_contract taddr in contract ``` @@ -26,17 +26,18 @@ let test = +Then cast `typed_address` to `contract` using +`Test.Typed_address.to_contract`. For example: + ```jsligo test-ligo group=addr2contract -const test = do { +const test = (() => { const addr : address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; - const taddr : typed_address = Test.cast_address(addr); - const contract : contract = Test.to_contract(taddr); + const taddr : typed_address = Test.Address.to_typed_address(addr); + const contract : contract = Test.Typed_address.to_contract(taddr); return contract; -}; +})(); ``` Check out the reference of the `Test` framework for exact signature of the functions [here](../reference/test.md). - - diff --git a/gitlab-pages/docs/faq/cameligo-ocaml-syntax-diff.md b/gitlab-pages/docs/faq/cameligo-ocaml-syntax-diff.md index c38f15104a..1da65b7478 100644 --- a/gitlab-pages/docs/faq/cameligo-ocaml-syntax-diff.md +++ b/gitlab-pages/docs/faq/cameligo-ocaml-syntax-diff.md @@ -1,6 +1,6 @@ --- id: cameligo-ocaml-syntax-diff -title: What are the differences between syntaxes of CameLIGO and OCaml ? +title: What are the differences between syntaxes of CameLIGO and OCaml? --- Most of the CameLIGO syntax follows the OCaml syntax, however, there are a few syntactic shortcuts available in one but not the other. @@ -33,7 +33,7 @@ For example, here is a function returning a list of integers : ```cameligo (* In CameLIGO *) -let res = +let res = type t = int list in let x : t = [42] in 43 :: x @@ -50,12 +50,20 @@ the blockchain. An entry point must have the type must be the same for all entry points in a given contract, but different entry points will typically use different parameter types. -An entry point will take the value of the parameter passed in the transaction, -and the value of the permanent on-chain storage, and will return a list of new transactions initiated from the contract (i.e. transfers of 0 or more tokens to other contracts or to implicit account addresses), and a new value -for the on-chain storage. The next transaction sent to that contract will use -the updated storage, and so on. In order to provide a pure function that may -consult the storage without modifying it, one can use `@view` instead of `@entry`. A _view_ can be called by another contract without generating a -transaction (i.e., the call is performed synchronously, instead of returning a delayed transaction which would run after the end of this contract's execution), and the view can return any value (since it cannot produce new transactions nor an updated storage, it simply returns the desired output value). +An entry point will take the value of the parameter passed in the +transaction, and the value of the permanent on-chain storage, and will +return a list of new transactions initiated from the contract +(i.e. transfers of zero or more tokens to other contracts or to +implicit account addresses), and a new value for the on-chain +storage. The next transaction sent to that contract will use the +updated storage, and so on. In order to provide a pure function that +may consult the storage without modifying it, one can use `@view` +instead of `@entry`. A _view_ can be called by another contract +without generating a transaction (i.e., the call is performed +synchronously, instead of returning a delayed transaction which would +run after the end of this contract's execution), and the view can +return any value (since it cannot produce new transactions nor an +updated storage, it simply returns the desired output value). ```cameligo type storage = string @@ -90,8 +98,8 @@ type storage = int let main (_p : unit) (s : storage) : operation list * storage = let tests = begin - assert (1 = 1); - assert (2 = 2) // no semicolon here + Assert.assert (1 = 1); + Assert.assert (2 = 2) // no semicolon here end in [], s @@ -111,9 +119,9 @@ type point = { let x = 24 let y = 42 - + let p_assign_without_punning : point = {x = x; y = y} let p_assign_with_punning : point = {x; y} (* Unavailable in CameLIGO *) ``` - \ No newline at end of file + diff --git a/gitlab-pages/docs/faq/catch-error-view.md b/gitlab-pages/docs/faq/catch-error-view.md index c8d2823bed..4feb4023f5 100644 --- a/gitlab-pages/docs/faq/catch-error-view.md +++ b/gitlab-pages/docs/faq/catch-error-view.md @@ -30,7 +30,7 @@ type ('success,'failure) result = In JsLIGO: ```jsligo group=failwith_view -type result = +type result = | ["Ok", success] | ["Error", failure] ``` diff --git a/gitlab-pages/docs/faq/polymorphic-comparison.md b/gitlab-pages/docs/faq/polymorphic-comparison.md index cec21b72a4..75a891881d 100644 --- a/gitlab-pages/docs/faq/polymorphic-comparison.md +++ b/gitlab-pages/docs/faq/polymorphic-comparison.md @@ -33,15 +33,10 @@ import Syntax from '@theme/Syntax'; > I'm trying to write some functions in (came)ligo that compare several values as long as they are ints, strings, or nats. compare_equal is one of them. > -> This errors out with Only composed types of not more than two element are allowed to be compared. -> > ```jsligo skip -> const compare_equal = (a : k, b: k) : bool => { -> if (a == b) { -> return true; -> } else { -> return false; -> } +> function compare_equal (a : k, b: k) : bool { +> if (a == b) return true; +> return false; > } > ``` > @@ -49,10 +44,10 @@ import Syntax from '@theme/Syntax'; > > ```jsligo skip > const compare_equal = (a : k, b: k) : bool => -> match (a) { -> when(int(v)): do { if (a = b) { return true; } else { return false; } }; -> when(string(v)): do { if (a = b) { return true; } else { return false; } }; -> } +> $match(a, { +> "int": (v) => (() => { if (a = b) return true; else return false; })(), +> "string": (v) => (() => { if (a = b) return true; else return false; })() +> }); > ``` > diff --git a/gitlab-pages/docs/faq/src/addr-to-contract/addr2contract.jsligo b/gitlab-pages/docs/faq/src/addr-to-contract/addr2contract.jsligo index 8938a0db6a..3eceb8d20b 100644 --- a/gitlab-pages/docs/faq/src/addr-to-contract/addr2contract.jsligo +++ b/gitlab-pages/docs/faq/src/addr-to-contract/addr2contract.jsligo @@ -1,6 +1,6 @@ -const test = do { +const test = (() => { const addr : address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; - const taddr : typed_address = Test.cast_address(addr); - const contract : contract = Test.to_contract(taddr); + const taddr : typed_address = Test.Address.to_typed_address(addr); + const contract : contract = Test.Typed_address.to_contract(taddr); return contract; -}; \ No newline at end of file +})(); \ No newline at end of file diff --git a/gitlab-pages/docs/faq/src/addr-to-contract/addr2contract.mligo b/gitlab-pages/docs/faq/src/addr-to-contract/addr2contract.mligo index f8983e3fcf..a90d9fa22c 100644 --- a/gitlab-pages/docs/faq/src/addr-to-contract/addr2contract.mligo +++ b/gitlab-pages/docs/faq/src/addr-to-contract/addr2contract.mligo @@ -1,5 +1,5 @@ let test = let addr : address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" in - let taddr : (unit, unit) typed_address = Test.cast_address addr in - let contract : (unit) contract = Test.to_contract taddr in + let taddr : (unit, unit) typed_address = Test.Address.to_typed_address addr in + let contract : unit contract = Test.Typed_address.to_contract taddr in contract \ No newline at end of file diff --git a/gitlab-pages/docs/faq/src/cameligo-ocaml-syntax-diff/semicolons.mligo b/gitlab-pages/docs/faq/src/cameligo-ocaml-syntax-diff/semicolons.mligo index 57ef2d220c..ee1ee52d02 100644 --- a/gitlab-pages/docs/faq/src/cameligo-ocaml-syntax-diff/semicolons.mligo +++ b/gitlab-pages/docs/faq/src/cameligo-ocaml-syntax-diff/semicolons.mligo @@ -5,8 +5,8 @@ type storage = int let main (_p : unit) (s : storage) : operation list * storage = let tests = begin - assert (1 = 1); - assert (2 = 2) // no semicolon here + Assert.assert (1 = 1); + Assert.assert (2 = 2) // no semicolon here end in [], s \ No newline at end of file diff --git a/gitlab-pages/docs/faq/src/cameligo-ocaml-syntax-diff/ungrouped.mligo b/gitlab-pages/docs/faq/src/cameligo-ocaml-syntax-diff/ungrouped.mligo index 8911961b01..8f42a89e30 100644 --- a/gitlab-pages/docs/faq/src/cameligo-ocaml-syntax-diff/ungrouped.mligo +++ b/gitlab-pages/docs/faq/src/cameligo-ocaml-syntax-diff/ungrouped.mligo @@ -1,6 +1,6 @@ let y = -(-1) (* In CameLIGO *) (* In CameLIGO *) -let res = +let res = type t = int list in let x : t = [42] in 43 :: x diff --git a/gitlab-pages/docs/faq/src/catch-error-view/failwith_view.jsligo b/gitlab-pages/docs/faq/src/catch-error-view/failwith_view.jsligo index 1b9272f557..71d68c969f 100644 --- a/gitlab-pages/docs/faq/src/catch-error-view/failwith_view.jsligo +++ b/gitlab-pages/docs/faq/src/catch-error-view/failwith_view.jsligo @@ -1,3 +1,3 @@ -type result = +type result = | ["Ok", success] | ["Error", failure] \ No newline at end of file diff --git a/gitlab-pages/docs/faq/src/tezos-now-advance-time/log.jsligo b/gitlab-pages/docs/faq/src/tezos-now-advance-time/log.jsligo index 159afb682b..83c2d06de5 100644 --- a/gitlab-pages/docs/faq/src/tezos-now-advance-time/log.jsligo +++ b/gitlab-pages/docs/faq/src/tezos-now-advance-time/log.jsligo @@ -1,2 +1,2 @@ -let _dummy : (cycles : nat) => unit = Test.bake_until_n_cycle_end -let _dummy_2 : (initial_timestamp : timestamp, no_of_accounts: nat, amount: list) => unit = Test.reset_state_at \ No newline at end of file +let _dummy : (cycles : nat) => unit = Test.State.bake_until +let _dummy_2 : (initial_timestamp : timestamp, no_of_accounts: nat, amount: list) => unit = Test.State.reset_at \ No newline at end of file diff --git a/gitlab-pages/docs/faq/src/tezos-now-advance-time/log.mligo b/gitlab-pages/docs/faq/src/tezos-now-advance-time/log.mligo index 6355b661ac..3170ac7212 100644 --- a/gitlab-pages/docs/faq/src/tezos-now-advance-time/log.mligo +++ b/gitlab-pages/docs/faq/src/tezos-now-advance-time/log.mligo @@ -1,2 +1,2 @@ -let _dummy : nat -> unit = Test.bake_until_n_cycle_end -let _dummy : timestamp -> nat -> tez list -> unit = Test.reset_state_at \ No newline at end of file +let _dummy : nat -> unit = Test.State.bake_until +let _dummy : timestamp -> nat -> tez list -> unit = Test.State.reset_at \ No newline at end of file diff --git a/gitlab-pages/docs/faq/tezos-now-advance-time.md b/gitlab-pages/docs/faq/tezos-now-advance-time.md index efdeb6a095..5bd7d0531f 100644 --- a/gitlab-pages/docs/faq/tezos-now-advance-time.md +++ b/gitlab-pages/docs/faq/tezos-now-advance-time.md @@ -29,14 +29,14 @@ If the function is updated, the typer will fail, triggering a warning, and you'll have to change the expected signature everywhere it's mentioned in the file. ```cameligo test-ligo group=log -let _dummy : nat -> unit = Test.bake_until_n_cycle_end -let _dummy : timestamp -> nat -> tez list -> unit = Test.reset_state_at +let _dummy : nat -> unit = Test.State.bake_until +let _dummy : timestamp -> nat -> tez list -> unit = Test.State.reset_at ``` ```jsligo test-ligo group=log -let _dummy : (cycles : nat) => unit = Test.bake_until_n_cycle_end -let _dummy_2 : (initial_timestamp : timestamp, no_of_accounts: nat, amount: list) => unit = Test.reset_state_at +let _dummy : (cycles : nat) => unit = Test.State.bake_until +let _dummy_2 : (initial_timestamp : timestamp, no_of_accounts: nat, amount: list) => unit = Test.State.reset_at ``` --> diff --git a/gitlab-pages/docs/imperative/asserting.md b/gitlab-pages/docs/imperative/asserting.md index b40d661c6c..193d5c1a03 100644 --- a/gitlab-pages/docs/imperative/asserting.md +++ b/gitlab-pages/docs/imperative/asserting.md @@ -6,21 +6,21 @@ title: Asserting import Syntax from '@theme/Syntax'; Assertions can be used to ensure a certain condition is met when -running a contract. The predefined function `assert` is used to check -whether a given a Boolean condition holds. The function `assert_some` -is used to check if an option value is not `None`. The function -`assert_some_with_error` is like `assert_some` but an error message -can be given. Whenever the assertion fails, the contract will stop and -an error will be left on the execution stack. +running a contract. The predefined function `Assert.assert` is used to +check whether a given a Boolean condition holds. The function +`Assert.some` is used to check if an option value is not `None`. The +function `assert_some_with_error` is like `Assert.some` but an error +message can be given. Whenever the assertion fails, the contract will +stop and an error will be left on the execution stack. ```cameligo group=asserting let incr_if_true (b : bool) (n : int) : int = - let () = assert b in n+1 + let () = Assert.assert b in n+1 let incr_if_some (b : unit option) (n : int) : int = - let () = assert_some b in n+1 + let () = Assert.some b in n+1 ``` @@ -28,27 +28,27 @@ let incr_if_some (b : unit option) (n : int) : int = ```jsligo group=asserting -const incr_if_true = (b: bool, n: int) : int => { - assert(b); +function incr_if_true (b: bool, n: int) : int { + Assert.assert(b); return n+1; }; -const incr_if_some = (b: option, n: int) : int => { - assert_some(b); +function incr_if_some (b: option, n: int) : int { + Assert.some(b); return n+1; }; ``` -You can use `assert_with_error` or `assert_some_with_error` to use a +You can use `Assert.Error.assert` or `Assert.Error.some` to use a custom error message. ```cameligo group=assert_with_error let incr_if_true (b : bool) (n : int) : int = - let () = assert_with_error b "My custom error message." + let () = Assert.Error.assert b "My custom error message." in n+1 ``` @@ -58,7 +58,7 @@ let incr_if_true (b : bool) (n : int) : int = ```jsligo group=assert_with_error const incr_if_true = (b: bool, n: int) : int => { - assert_with_error (b, "My custom error message."); + Assert.Error.assert (b, "My custom error message."); return n+1; }; ``` @@ -66,4 +66,4 @@ const incr_if_true = (b: bool, n: int) : int => { Likewise, we can check for `None` instead of `Some` by using -`assert_none` and `assert_none_with_error`. +`Assert.none` and `Assert.Error.none`. diff --git a/gitlab-pages/docs/imperative/exceptions.md b/gitlab-pages/docs/imperative/exceptions.md index ed507b16d0..6ec12bd3c6 100644 --- a/gitlab-pages/docs/imperative/exceptions.md +++ b/gitlab-pages/docs/imperative/exceptions.md @@ -38,7 +38,7 @@ The call to the `failwith` function sometimes needs to be annotated with a type ```jsligo group=failwith -@entry +// @entry const main = (p: unit, s: unit) : [list, unit] => failwith("This contract always fails"); ``` diff --git a/gitlab-pages/docs/imperative/looping.md b/gitlab-pages/docs/imperative/looping.md index 26f06894e4..39b94bf61e 100644 --- a/gitlab-pages/docs/imperative/looping.md +++ b/gitlab-pages/docs/imperative/looping.md @@ -83,8 +83,8 @@ function gcd (a: nat, b: nat) { const z = x; x = y; y = z; // Swapping x and y } - let r: nat = 0n - while (y != 0n) { + let r: nat = 0 + while (y != (0 as nat)) { r = x % y; x = y; y = r; @@ -119,12 +119,12 @@ two natural numbers by means of Euclid's algorithm using tail recursion (no loops): ```jsligo group=looping -function iter (x: nat,y: nat): nat { - if (y == 0n) return x else return iter (y, x % y) +function iter (x: nat, y: nat): nat { + if (y == (0 as nat)) return x; else return iter(y, x % y) }; function gcd2 (x: nat,y: nat) : nat { - if (x < y) return iter (y, x) else return iter (x, y) + if (x < y) return iter(y, x); else return iter(x, y) }; ``` @@ -135,7 +135,8 @@ Finally, here is how to check if a string is a palindrome using a `for` loop: ```jsligo group=looping -const getChar = (s: string, idx: nat) : string => String.sub(idx, 1n, s); +const getChar = (s: string, idx: nat) : string => String.sub(idx, 1 as +nat, s); function isPalindrome (s: string): bool { let p = ""; @@ -157,7 +158,7 @@ of the form `for (const of ) `. Here is an example where the integers in a list are summed up. ```jsligo group=looping -function sum_list (l : list) { +function sum_list (l: list) { let acc = 0; for (const i of l) acc += i; return acc; // total diff --git a/gitlab-pages/docs/imperative/src/asserting/assert_with_error.jsligo b/gitlab-pages/docs/imperative/src/asserting/assert_with_error.jsligo index 00e31f253d..454cc62b85 100644 --- a/gitlab-pages/docs/imperative/src/asserting/assert_with_error.jsligo +++ b/gitlab-pages/docs/imperative/src/asserting/assert_with_error.jsligo @@ -1,4 +1,4 @@ const incr_if_true = (b: bool, n: int) : int => { - assert_with_error (b, "My custom error message."); + Assert.Error.assert (b, "My custom error message."); return n+1; }; \ No newline at end of file diff --git a/gitlab-pages/docs/imperative/src/asserting/assert_with_error.mligo b/gitlab-pages/docs/imperative/src/asserting/assert_with_error.mligo index 17ca9184a4..e96d158a96 100644 --- a/gitlab-pages/docs/imperative/src/asserting/assert_with_error.mligo +++ b/gitlab-pages/docs/imperative/src/asserting/assert_with_error.mligo @@ -1,3 +1,3 @@ let incr_if_true (b : bool) (n : int) : int = - let () = assert_with_error b "My custom error message." + let () = Assert.Error.assert b "My custom error message." in n+1 \ No newline at end of file diff --git a/gitlab-pages/docs/imperative/src/asserting/asserting.jsligo b/gitlab-pages/docs/imperative/src/asserting/asserting.jsligo index 920fe30c44..020f5b6fa7 100644 --- a/gitlab-pages/docs/imperative/src/asserting/asserting.jsligo +++ b/gitlab-pages/docs/imperative/src/asserting/asserting.jsligo @@ -1,9 +1,9 @@ -const incr_if_true = (b: bool, n: int) : int => { - assert(b); +function incr_if_true (b: bool, n: int) : int { + Assert.assert(b); return n+1; }; -const incr_if_some = (b: option, n: int) : int => { - assert_some(b); +function incr_if_some (b: option, n: int) : int { + Assert.some(b); return n+1; }; \ No newline at end of file diff --git a/gitlab-pages/docs/imperative/src/asserting/asserting.mligo b/gitlab-pages/docs/imperative/src/asserting/asserting.mligo index 4350793dec..56d2ee8867 100644 --- a/gitlab-pages/docs/imperative/src/asserting/asserting.mligo +++ b/gitlab-pages/docs/imperative/src/asserting/asserting.mligo @@ -1,5 +1,5 @@ let incr_if_true (b : bool) (n : int) : int = - let () = assert b in n+1 + let () = Assert.assert b in n+1 let incr_if_some (b : unit option) (n : int) : int = - let () = assert_some b in n+1 \ No newline at end of file + let () = Assert.some b in n+1 \ No newline at end of file diff --git a/gitlab-pages/docs/imperative/src/exceptions/failwith.jsligo b/gitlab-pages/docs/imperative/src/exceptions/failwith.jsligo index 40e52b4cea..66d2c39be2 100644 --- a/gitlab-pages/docs/imperative/src/exceptions/failwith.jsligo +++ b/gitlab-pages/docs/imperative/src/exceptions/failwith.jsligo @@ -1,3 +1,3 @@ -@entry +// @entry const main = (p: unit, s: unit) : [list, unit] => failwith("This contract always fails"); \ No newline at end of file diff --git a/gitlab-pages/docs/imperative/src/looping/looping.jsligo b/gitlab-pages/docs/imperative/src/looping/looping.jsligo index bba933101f..6f6d1caf10 100644 --- a/gitlab-pages/docs/imperative/src/looping/looping.jsligo +++ b/gitlab-pages/docs/imperative/src/looping/looping.jsligo @@ -4,22 +4,23 @@ function gcd (a: nat, b: nat) { const z = x; x = y; y = z; // Swapping x and y } - let r: nat = 0n - while (y != 0n) { + let r: nat = 0 + while (y != (0 as nat)) { r = x % y; x = y; y = r; } return x; }; -function iter (x: nat,y: nat): nat { - if (y == 0n) return x else return iter (y, x % y) +function iter (x: nat, y: nat): nat { + if (y == (0 as nat)) return x; else return iter(y, x % y) }; function gcd2 (x: nat,y: nat) : nat { - if (x < y) return iter (y, x) else return iter (x, y) + if (x < y) return iter(y, x); else return iter(x, y) }; -const getChar = (s: string, idx: nat) : string => String.sub(idx, 1n, s); +const getChar = (s: string, idx: nat) : string => String.sub(idx, 1 as +nat, s); function isPalindrome (s: string): bool { let p = ""; @@ -28,7 +29,7 @@ function isPalindrome (s: string): bool { p += getChar(s, abs(i)); return p == s; }; -function sum_list (l : list) { +function sum_list (l: list) { let acc = 0; for (const i of l) acc += i; return acc; // total diff --git a/gitlab-pages/docs/imperative/src/switches/switch.jsligo b/gitlab-pages/docs/imperative/src/switches/switch.jsligo index 94a607f2f3..7cb9761fe5 100644 --- a/gitlab-pages/docs/imperative/src/switches/switch.jsligo +++ b/gitlab-pages/docs/imperative/src/switches/switch.jsligo @@ -1,13 +1,13 @@ function hello (day: nat) : string { let greeting = "Hello"; switch (day) { - case 1n: greeting += " Monday!"; break; - case 2n: greeting += " Tuesday!"; break; - case 3n: greeting += " Wednesday!"; break; - case 4n: greeting += " Thursday!"; break; - case 5n: greeting += " Friday!"; break; - case 6n: greeting += " Saturday!"; break; - case 7n: greeting += " Sunday!"; break; + case (1 as nat): greeting += " Monday!"; break; + case (2 as nat): greeting += " Tuesday!"; break; + case (3 as nat): greeting += " Wednesday!"; break; + case (4 as nat): greeting += " Thursday!"; break; + case (5 as nat): greeting += " Friday!"; break; + case (6 as nat): greeting += " Saturday!"; break; + case (7 as nat): greeting += " Sunday!"; break; default: greeting += "!"; break; }; return greeting; diff --git a/gitlab-pages/docs/imperative/switches.md b/gitlab-pages/docs/imperative/switches.md index 7dd23649c5..7847fe207d 100644 --- a/gitlab-pages/docs/imperative/switches.md +++ b/gitlab-pages/docs/imperative/switches.md @@ -23,13 +23,13 @@ conditional statements. function hello (day: nat) : string { let greeting = "Hello"; switch (day) { - case 1n: greeting += " Monday!"; break; - case 2n: greeting += " Tuesday!"; break; - case 3n: greeting += " Wednesday!"; break; - case 4n: greeting += " Thursday!"; break; - case 5n: greeting += " Friday!"; break; - case 6n: greeting += " Saturday!"; break; - case 7n: greeting += " Sunday!"; break; + case (1 as nat): greeting += " Monday!"; break; + case (2 as nat): greeting += " Tuesday!"; break; + case (3 as nat): greeting += " Wednesday!"; break; + case (4 as nat): greeting += " Thursday!"; break; + case (5 as nat): greeting += " Friday!"; break; + case (6 as nat): greeting += " Saturday!"; break; + case (7 as nat): greeting += " Sunday!"; break; default: greeting += "!"; break; }; return greeting; diff --git a/gitlab-pages/docs/intro/ligo-intro.md b/gitlab-pages/docs/intro/ligo-intro.md index a1e34d626d..2cad8d25c3 100644 --- a/gitlab-pages/docs/intro/ligo-intro.md +++ b/gitlab-pages/docs/intro/ligo-intro.md @@ -14,19 +14,18 @@ Our hope is to have a simple, strongly typed language with a low footprint. Most useful smart contracts can express their core functionality in under a thousand lines of code. -### LIGO, for newcomers or confirmed developpers +### LIGO, for newcomers or confirmed developers Even if LIGO currently offers **two syntaxes**, you'll need to **choose only one**: - **JsLIGO**, ideal for web developers, is a TypeScript/JavaScript inspired syntax without unnecessary complexity, which is not helpful in smart contract development. A quick way to produce your first dApp! - -```jsligo +```jsligo group=intro type storage = string; - @entry - const store_hello = (delta: int, store: storage): [list, storage] => - [[], "Hello"]; + // @entry + const store_hello = (delta: int, store: storage): [list, storage] => [[], "Hello"]; + ``` - **CameLIGO** is designed for developers with a background in @@ -35,14 +34,15 @@ Even if LIGO currently offers **two syntaxes**, you'll need to **choose only one a functional style. -```cameligo +```cameligo group=intro type storage = string [@entry] let store_hello (delta : int) (store : storage) : operation list * storage = [], "Hello" + ``` -A significant advantage of the multi-syntax feature is to share knowledge, toolings, and [modules](../language-basics/modules) (like [libraries](../advanced/package-management) onto [registry](https://packages.ligolang.org/packages)) in a larger community. +A significant advantage of the multi-syntax feature is to share knowledge, toolings, and [modules](../syntax/modules) (like [libraries](../advanced/package-management) onto [registry](https://packages.ligolang.org/packages)) in a larger community. ### LIGO, designed to be cost-effective @@ -97,16 +97,15 @@ For a quick overview, [get-started](../tutorials/getting-started) is a good choi ### Do you want to learn LIGO? Your choice to learn LIGO is already available: -- Read [basics](../language-basics/types) to have a basic comprehension -- Write your first [smart contract](../tutorials/taco-shop/tezos-taco-shop-smart-contract). +- Write your first [smart contract](../tutorials/taco-shop/selling-tacos). - Others resources are available on [marigold.dev](https://www.marigold.dev/learn) ### Do you want to build a production-ready project? You will need a deeper comprehension: -- Teach yourself how to structure your code with [Combining code](../language-basics/modules) section +- Teach yourself how to structure your code with [Combining code](../syntax/modules) section - Learn how to [write tests](../testing) we strongly encourage to use [breathalyzer library from the LIGO registry.](https://packages.ligolang.org/package/ligo-breathalyzer) -- Understand how to [secure a contract](../tutorials/security) +- Understand how to [secure a contract](../advanced/security) ### Dig deeper diff --git a/gitlab-pages/docs/intro/src/ligo-intro/intro.jsligo b/gitlab-pages/docs/intro/src/ligo-intro/intro.jsligo new file mode 100644 index 0000000000..081ce9c3bd --- /dev/null +++ b/gitlab-pages/docs/intro/src/ligo-intro/intro.jsligo @@ -0,0 +1,4 @@ + type storage = string; + + // @entry + const store_hello = (delta: int, store: storage): [list, storage] => [[], "Hello"]; diff --git a/gitlab-pages/docs/intro/src/ligo-intro/intro.mligo b/gitlab-pages/docs/intro/src/ligo-intro/intro.mligo new file mode 100644 index 0000000000..7841baec2d --- /dev/null +++ b/gitlab-pages/docs/intro/src/ligo-intro/intro.mligo @@ -0,0 +1,4 @@ + type storage = string + + [@entry] + let store_hello (delta : int) (store : storage) : operation list * storage = [], "Hello" diff --git a/gitlab-pages/docs/intro/src/ligo-intro/ungrouped.jsligo b/gitlab-pages/docs/intro/src/ligo-intro/ungrouped.jsligo index de8928afcb..7b092a8a2f 100644 --- a/gitlab-pages/docs/intro/src/ligo-intro/ungrouped.jsligo +++ b/gitlab-pages/docs/intro/src/ligo-intro/ungrouped.jsligo @@ -1,5 +1,7 @@ - type storage = string; +type storage = string; +class C { @entry - const store_hello = (delta: int, store: storage): [list, storage] => - [[], "Hello"]; \ No newline at end of file + store_hello = (delta: int, store: storage): [list, storage] => + [[], "Hello"]; +} \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v1/ungrouped.jsligo b/gitlab-pages/docs/intro/src/upgrade-v1/ungrouped.jsligo index 2f5ce47efc..d37d2b2c0b 100644 --- a/gitlab-pages/docs/intro/src/upgrade-v1/ungrouped.jsligo +++ b/gitlab-pages/docs/intro/src/upgrade-v1/ungrouped.jsligo @@ -1,50 +1,47 @@ -@entry +// @entry const set_storage = (new_storage: int, _old_storage: int): [list, int] => [[], new_storage] -@view +// @view const get_storage = (_: unit, storage: int): int => storage -@entry + +// @entry const my_entry_point = (_: unit, n: int) : [list, int] => [[], n]; + const stuff = { x : "foo", y : { universe : [42, "life", true] } }; const part : bool = stuff.y["universe"][2]; -const zero: nat = 2n & 1n; // Bitwise and +const zero: nat = (2 as nat) & (1 as nat); // Bitwise and const two_bytes: bytes = 0x11 & 0x10 -const five: nat = 4n | 1n; // Bitwise or +const five: nat = (4 as nat) | (1 as nat); // Bitwise or const three_bytes: bytes = 0x11 | 0x10 -const three: nat = 2n ^ 1n; // Bitwise xor +const three: nat = (2 as nat) ^ (1 as nat); // Bitwise xor const one_byte: bytes = 0x11 ^ 0x10 -const four: nat = 2n << 1n // Bitwise left shift -const five_one_two: bytes = 0x0100 << 1n - -const one: nat = 2n >> 1n; // Bitwise right shift -const zero_bytes: bytes = 0x01 >> 1n -const force_positive = (key: string, dict: map) => { - return match(Map.find_opt (key, dict)) { - when(Some(val)): do { - if (val >= 0) { - return val; - } else { - failwith("Negative value"); - } - }; - when(None()): failwith("Not found.") - }; -} +const four: nat = (2 as nat) << (1 as nat) // Bitwise left shift +const five_one_two: bytes = 0x0100 << (1 as nat) + +const one: nat = (2 as nat) >> (1 as nat); // Bitwise right shift +const zero_bytes: bytes = 0x01 >> (1 as nat) +const force_positive = (key: string, dict: map) => + $match(Map.find_opt (key, dict), { + "Some": val => + (() => { if (val >= 0) return val; else failwith("Negative value") })(), + "None": () => failwith("Not found.") + }); type storage = [int, list ]; type parameter = list ; type returnx = [list , storage]; -let main = (p : parameter, s : storage) : returnx => { - let storage = match (p) { - when([]): s; - when([hd, ...tl]): [s[0] + hd, tl] - }; - return [([] as list), storage]; +function main (p : parameter, s : storage) : returnx { + const storage = + $match(List.head_and_tail(p), { + "None": () => s, + "Some": ([hd, tl]) => [s[0] + hd, tl] + }); + return [[], storage]; }; \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/classes.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/classes.jsligo new file mode 100644 index 0000000000..7838fedc4c --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/classes.jsligo @@ -0,0 +1,12 @@ +type storage_type = int; +type return_type = [list, storage_type]; + +class Counter { + @entry + add = (value: int, storage: storage_type): return_type => + [[], storage + value]; + + @entry + sub = (value: int, storage: storage_type): return_type => + [[], storage - value]; +} \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/interfaces.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/interfaces.jsligo new file mode 100644 index 0000000000..a363214baa --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/interfaces.jsligo @@ -0,0 +1,5 @@ +type storage = int; +interface MyInterface { + // @entry + add: (s: int, k: storage) => [list, storage]; +} \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/match_case_tuple.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/match_case_tuple.jsligo new file mode 100644 index 0000000000..f19aec201e --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/match_case_tuple.jsligo @@ -0,0 +1,20 @@ +type colour = + ["RGB", [int, int, int]] | ["Gray", int] | ["Default"]; + +let colourInt: colour = ["RGB" as "RGB", 1, 2, 3]; +let result = + $match(colourInt, { + "Gray": (val) => + (() => + { + const a = (5 as nat); + const b = (6 as nat); + return a + b + abs(val); + })(), + "RGB": ([a, b, c]) => + (() => + { return abs(a + b + c); })(), + "Default": () => + (() => + { return (5 as nat); })(), + }) \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/matching.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/matching.jsligo new file mode 100644 index 0000000000..3391511c2d --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/matching.jsligo @@ -0,0 +1,36 @@ +type user = + ["Admin", nat] +| ["Manager", nat] +| ["Guest"]; + +const greetUser = (user: user): string => { + $match (user, { + "Admin": _id => "Hello, administrator", + "Manager": _id => "Welcome, manager", + "Guest": () => "Hello, guest", + }); +} +const getSquareOfUserId = (user: user): nat => { + $match (user, { + "Admin": id => (() => { + const squareOfId = id * id; + return squareOfId; + })(), + "Manager": id => (() => { + const squareOfId = id * id; + return squareOfId; + })(), + "Guest": () => 0, + }); +} +type wrapper = | ["Wrap", [int, int]]; +const wrapped: wrapper = ["Wrap" as "Wrap", [5, 5]]; + +const compareResult: string = $match(wrapped, { + "Wrap": ([_a, _b]) => (() => { + if (_a == _b) return "Equal"; + if (_a > _b) return "Greater"; + if (_a < _b) return "Less"; + return "Default"; + })(), +}); \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/myFunctions.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/myFunctions.jsligo new file mode 100644 index 0000000000..3491beab43 --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/myFunctions.jsligo @@ -0,0 +1,4 @@ +export namespace MyFunctions { + export const addToImport = (a: int, b: int): int => a + b; + export const subToImport = (a: int, b: int): int => a - b; +} \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/myTypes.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/myTypes.jsligo new file mode 100644 index 0000000000..fb05fc6c27 --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/myTypes.jsligo @@ -0,0 +1,2 @@ +export type storage_type = int; +export type return_type = [list, storage_type]; \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/namespace.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/namespace.jsligo new file mode 100644 index 0000000000..361f279504 --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/namespace.jsligo @@ -0,0 +1,12 @@ +namespace Counter { + type storage_type = int; + type return_type = [list, storage_type]; + + // @entry + const add = (value: int, storage: storage_type): return_type => + [[], storage + value]; + + // @entry + const sub = (value: int, storage: storage_type): return_type => + [[], storage - value]; +} \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/objects.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/objects.jsligo new file mode 100644 index 0000000000..9a153cc66d --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/objects.jsligo @@ -0,0 +1,26 @@ +type object_type = {x: int, y: int, z: int}; +type storage_type = object_type; +type return_type = [list, storage_type]; + +class Counter { + @entry + reset = (_: unit, _s: storage_type): return_type => { + const newObject: object_type = { + x: 0, + y: 0, + z: 0, + }; + return [[], newObject]; + } + + @entry + increment = (_: unit, storage: storage_type): return_type => { + const { x, y, z } = storage; + const newObject: object_type = { + x: x + 1, + y: y + 1, + z: z + 1, + }; + return [[], newObject]; + } +} \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/topLevelDefinitions.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/topLevelDefinitions.jsligo new file mode 100644 index 0000000000..0fa3e6f3f3 --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/topLevelDefinitions.jsligo @@ -0,0 +1,3 @@ +export const addToImport = (a: int, b: int): int => a + b; +export const subToImport = (a: int, b: int): int => a - b; +export const myConstant = 5 as int; \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/toplevel.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/toplevel.jsligo new file mode 100644 index 0000000000..cdc014d932 --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/toplevel.jsligo @@ -0,0 +1,10 @@ +type storage_type = int; +type return_type = [list, storage_type]; + +// @entry +const add = (value: int, store: storage_type): return_type => + [[], store + value]; + +// @entry +const sub = (value: int, store: storage_type): return_type => + [[], store - value]; \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/type_punning.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/type_punning.jsligo new file mode 100644 index 0000000000..d0762e4d4a --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/type_punning.jsligo @@ -0,0 +1,5 @@ +type x = int; +type y = int; +type z = int; +// type object_type = { x, y, z }; // not permitted +type object_type = {x: x, y: y, z: z}; // correct \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/union.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/union.jsligo new file mode 100644 index 0000000000..c2f3b4374e --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/union.jsligo @@ -0,0 +1,9 @@ +type singleton = | ["Single"]; +type user = + ["Admin", nat] +| ["Manager", int] +| ["Guest"]; + +const admin1: user = ["Admin" as "Admin", 1 as nat]; +const manager2: user = ["Manager" as "Manager", 2 as int]; +const guest3: user = ["Guest" as "Guest"]; \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/useMyFunctions.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/useMyFunctions.jsligo new file mode 100644 index 0000000000..7fdb4b650d --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/useMyFunctions.jsligo @@ -0,0 +1,17 @@ +import * as MyFileWithFunctions from './gitlab-pages/docs/intro/src/upgrade-v2/myFunctions.jsligo'; +const addToImport = MyFileWithFunctions.MyFunctions.addToImport; +const subToImport = MyFileWithFunctions.MyFunctions.subToImport; + +namespace Counter { + type storage_type = int; + type return_type = [list, storage_type]; + + // @entry + const add = (value: int, storage: storage_type): return_type => + [[], addToImport(storage, value)]; + + // @entry + const sub = (value: int, storage: storage_type): return_type => + [[], subToImport(storage, value)]; + +} \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/useTopLevelDefinitions.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/useTopLevelDefinitions.jsligo new file mode 100644 index 0000000000..413a3e75a2 --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/useTopLevelDefinitions.jsligo @@ -0,0 +1,20 @@ +import { addToImport, subToImport, myConstant } from "./gitlab-pages/docs/intro/src/upgrade-v2/topLevelDefinitions.jsligo"; + +type storage_type = int; +type return_type = [list, storage_type]; + +class Calculator { + + @entry + add = (value: int, storage: storage_type): return_type => + [[], addToImport(storage, value)]; + + @entry + sub = (value: int, storage: storage_type): return_type => + [[], subToImport(storage, value)]; + + @entry + increment = (_: unit, storage: storage_type): return_type => + [[], addToImport(storage, myConstant)]; + +} \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/use_myTypes.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/use_myTypes.jsligo new file mode 100644 index 0000000000..011e5cfa3c --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/use_myTypes.jsligo @@ -0,0 +1,13 @@ +import * as myTypes from "./gitlab-pages/docs/intro/src/upgrade-v2/myTypes.jsligo"; +type storage_type = myTypes.storage_type; +type return_type = myTypes.return_type; + +class Counter { + @entry + add = (value: int, storage: storage_type): return_type => + [[], storage + value]; + + @entry + sub = (value: int, storage: storage_type): return_type => + [[], storage - value]; +} \ No newline at end of file diff --git a/gitlab-pages/docs/intro/src/upgrade-v2/value_punning.jsligo b/gitlab-pages/docs/intro/src/upgrade-v2/value_punning.jsligo new file mode 100644 index 0000000000..a47d672c94 --- /dev/null +++ b/gitlab-pages/docs/intro/src/upgrade-v2/value_punning.jsligo @@ -0,0 +1,5 @@ +type object_type = { x: int, y: int, z: int }; +const x = 5; +const y = 6; +const z = 7; +const newObject: object_type = { x, y, z }; \ No newline at end of file diff --git a/gitlab-pages/docs/intro/upgrade-v1.md b/gitlab-pages/docs/intro/upgrade-v1.md index 02e5c3d67a..e501f692c5 100644 --- a/gitlab-pages/docs/intro/upgrade-v1.md +++ b/gitlab-pages/docs/intro/upgrade-v1.md @@ -70,7 +70,7 @@ For example, this view uses the old syntax: ```jsligo skip // Prior to LIGO 1.0 -// @entry +@entry const set_storage = ([new_storage, _old_storage] : [int, int]): [list, int] => [[], new_storage] @view @@ -80,11 +80,12 @@ const get_storage = ([_, storage] : [unit, int]): int => storage This equivalent view uses the new syntax: ```jsligo -@entry +// @entry const set_storage = (new_storage: int, _old_storage: int): [list, int] => [[], new_storage] -@view +// @view const get_storage = (_: unit, storage: int): int => storage + ``` @@ -131,20 +132,22 @@ If your project has a stable ABI that other tools rely on, you might need to man Combs and trees can perform differently, so you can try setting types such as variants with many cases as combs or trees and comparing the size and gas consumption of the compiled contracts. -## Decorators are not commented +## Decorators in and out of comments -Prior to version 1.0, JsLIGO decorators were put in comments, as in this example: +Prior to version 1.0, JsLIGO some decorators were put in comments, as in this example: ```jsligo skip // @entry const my_entry_point = (_: unit, n: int) : [list, int] => [[], n]; ``` -Now, decorators are not in comments, as in this example: +Now, some decorators are in comments, others not when the TypeScript +allows it, as in this example: ```jsligo -@entry +// @entry const my_entry_point = (_: unit, n: int) : [list, int] => [[], n]; + ``` For more information, see [Decorators](../syntax/decorators). @@ -207,20 +210,20 @@ The following operators have been added, and can be used with `nat` and `bytes`: Here are examples of these operators in context: ```jsligo -const zero: nat = 2n & 1n; // Bitwise and +const zero: nat = (2 as nat) & (1 as nat); // Bitwise and const two_bytes: bytes = 0x11 & 0x10 -const five: nat = 4n | 1n; // Bitwise or +const five: nat = (4 as nat) | (1 as nat); // Bitwise or const three_bytes: bytes = 0x11 | 0x10 -const three: nat = 2n ^ 1n; // Bitwise xor +const three: nat = (2 as nat) ^ (1 as nat); // Bitwise xor const one_byte: bytes = 0x11 ^ 0x10 -const four: nat = 2n << 1n // Bitwise left shift -const five_one_two: bytes = 0x0100 << 1n +const four: nat = (2 as nat) << (1 as nat) // Bitwise left shift +const five_one_two: bytes = 0x0100 << (1 as nat) -const one: nat = 2n >> 1n; // Bitwise right shift -const zero_bytes: bytes = 0x01 >> 1n +const one: nat = (2 as nat) >> (1 as nat); // Bitwise right shift +const zero_bytes: bytes = 0x01 >> (1 as nat) ``` For more information, see [Bitwise operations](../data-types/bytes#bitwise-operations). @@ -256,18 +259,12 @@ const force_positive = (key: string, dict: map) => { This is an equivalent match in JsLIGO 1.0: ```jsligo -const force_positive = (key: string, dict: map) => { - return match(Map.find_opt (key, dict)) { - when(Some(val)): do { - if (val >= 0) { - return val; - } else { - failwith("Negative value"); - } - }; - when(None()): failwith("Not found.") - }; -} +const force_positive = (key: string, dict: map) => + $match(Map.find_opt (key, dict), { + "Some": val => + (() => { if (val >= 0) return val; else failwith("Negative value") })(), + "None": () => failwith("Not found.") + }); ``` Pattern-matching on lists in version 1.0 uses the syntaxes `when([])` and `when([head, ...tail])`: @@ -277,12 +274,13 @@ type storage = [int, list ]; type parameter = list ; type returnx = [list , storage]; -let main = (p : parameter, s : storage) : returnx => { - let storage = match (p) { - when([]): s; - when([hd, ...tl]): [s[0] + hd, tl] - }; - return [([] as list), storage]; +function main (p : parameter, s : storage) : returnx { + const storage = + $match(List.head_and_tail(p), { + "None": () => s, + "Some": ([hd, tl]) => [s[0] + hd, tl] + }); + return [[], storage]; }; ``` diff --git a/gitlab-pages/docs/intro/upgrade-v2.md b/gitlab-pages/docs/intro/upgrade-v2.md new file mode 100644 index 0000000000..723955ed11 --- /dev/null +++ b/gitlab-pages/docs/intro/upgrade-v2.md @@ -0,0 +1,622 @@ +--- +title: Migrating to LIGO v2.0 +--- + +import Syntax from '@theme/Syntax'; + +Version 2.0 of LIGO includes breaking changes, so you must update your code when you upgrade your installation of LIGO as described in [Installation](./installation). + +## Promotion of `Test.Next` + +The `Test.Next` library is now the `Test` library, replacing the old `Test` library. +This table shows the old functions and their new equivalents: + +| Function in LIGO v1.9.2 | Function in LIGO v2 | +| --- | --- | +| `Test.run`| `Test.Michelson.run`| +| `Test.eval`| `Test.Michelson.eval`| +| `Test.decompile`| `Test.Michelson.decompile`| +| `Test.compile_value`| `Test.Michelson.eval`| +| `Test.get_total_voting_power`| `Test.State.get_total_voting_power`| +| `Test.failwith`| `Test.Assert.failwith`| +| `Test.to_contract`| `Test.Typed_address.to_contract`| +| `Test.set_source`| `Test.State.set_source`| +| `Test.cast_address`| `Test.Address.to_typed_address`| +| `Test.to_address`| `Test.Typed_address.to_address`| +| `Test.get_storage`| `Test.Typed_address.get_storage`| +| `Test.get_storage_of_address`| `Test.Address.get_storage`| +| `Test.get_balance_of_address`| `Test.Address.get_balance`| +| `Test.get_balance`| `Test.Typed_address.get_balance`| +| `Test.print`| `Test.IO.print`| +| `Test.eprint`| `Test.IO.eprint`| +| `Test.get_voting_power`| `Test.State.get_voting_power`| +| `Test.nth_bootstrap_contract`| `Test.Account.Contract.bootstrap`| +| `Test.nth_bootstrap_account`| `Test.Account.address`| +| `Test.get_bootstrap_account`| `Test.Account.info`| +| `Test.nth_bootstrap_typed_address`| `Test.Account.Contract.bootstrap_typed_address`| +| `Test.last_originations`| `Test.State.last_originations`| +| `Test.random`| `Test.random` (unchanged) | +| `Test.new_account`| `Test.Account.new`| +| `Test.bake_until_n_cycle_end`| `Test.State.bake_until`| +| `Test.get_time`| `Tezos.get_now` | +| `Test.register_delegate`| `Test.State.register_delegate`| +| `Test.stake`| `Test.State.stake`| +| `Test.register_constant`| `Test.State.register_constant`| +| `Test.to_typed_address`| `Test.Contract.to_typed_address`| +| `Test.constant_to_michelson_program`| `Test.Michelson.parse`| +| `Test.parse_michelson`| `Test.Michelson.parse`| +| `Test.restore_context`| `Test.State.restore`| +| `Test.save_context`| `Test.State.save`| +| `Test.drop_context`| `Test.State.drop`| +| `Test.to_string`| `Test.String.show`| +| `Test.to_json`| `Test.String.json`| +| `Test.to_debugger_json`| `Test.String.debugger_json`| +| `Test.set_baker_policy`| `Test.State.set_baker_policy`| +| `Test.set_baker`| `Test.State.set_baker`| +| `Test.size`| `Test.Michelson.Contract.size`| +| `Test.compile_contract`| `Test.Michelson.Contract.compile`| +| `Test.read_contract_from_file`| `Test.Michelson.Contract.from_file`| +| `Test.chr`| `Test.String.chr`| +| `Test.nl`| `Test.String.nl`| +| `Test.println`| `Test.IO.println`| +| `Test.set_print_values`| `Test.IO.set_test_print`| +| `Test.unset_print_values`| `Test.IO.unset_test_print`| +| `Test.get_last_events_from`| `Test.State.last_events`| +| `Test.transfer`| `Test.Typed_address.transfer`| +| `Test.transfer_exn`| `Test.Typed_address.transfer_exn`| +| `Test.log`| `Test.IO.log`| +| `Test.reset_state`| `Test.State.reset`| +| `Test.reset_state_at`| `Test.State.reset_at`| +| `Test.bootstrap_contract`| `Test.State.Reset.add_func_contract`| +| `Test.mutate_value`| `Test.Mutation.value`| +| `Test.save_mutation`| `Test.Mutation.save`| +| `Test.sign`| `Test.Crypto.sign`| +| `Test.add_account`| `Test.Account.add`| +| `Test.baker_account`| `Test.State.Reset.add_baker`| +| `Test.set_big_map`| `Test.State.set_big_map`| +| `Test.transfer_to_contract`| `Test.Contract.transfer`| +| `Test.transfer_to_contract_exn`| `Test.Contract.transfer_exn`| +| `Test.michelson_equal`| `Test.Compare.eq`| +| `Test.to_entrypoint`| `Test.Typed_address.get_entrypoint`| +| `Test.storage_with_dynamic_entrypoints`| `Test.Dynamic_entrypoints.storage`| +| `Test.originate_contract`| `Test.Originate.michelson`| +| `Test.compile_contract_with_views`| `Test.Michelson.Contract.compile_with_views`| +| `Test.originate`| `Test.Originate.contract`| +| `Test.compile_contract_from_file`| `Test.Michelson.Contract.from_file`| +| `Test.originate_from_file`| `Test.Originate.from_file`| +| `Test.mutation_test`| `Test.Mutation.func`| +| `Test.mutation_test_all`| `Test.Mutation.All.func`| +| `Test.originate_from_file_and_mutate`| `Test.Mutation.from_file`| +| `Test.originate_from_file_and_mutate_all`| `Test.Mutation.All.from_file`| +| `Test.originate_module_and_mutate`| `Test.Mutation.contract`| +| `Test.originate_and_mutate_all`| `Test.Mutation.All.contract`| +| `Test.assert`| `Test.Assert.assert`| +| `Test.assert_some`| `Test.Assert.some`| +| `Test.assert_none`| `Test.Assert.none`| +| `Test.assert_with_error`| `Test.Assert.Error.assert`| +| `Test.assert_some_with_error`| `Test.Assert.Error.some`| +| `Test.assert_none_with_error`| `Test.Assert.Error.none`| +| `Test.equal`| `Test.Compare.eq`| +| `Test.not_equal`| `Test.Compare.neq`| +| `Test.greater`| `Test.Compare.gt`| +| `Test.less`| `Test.Compare.lt`| +| `Test.greater_or_equal`| `Test.Compare.ge`| +| `Test.less_or_equal`| `Test.Compare.le`| +| `Test.create_chest`| `Test.Timelock.create`| +| `Test.create_chest_key`| `Test.Timelock.create_key`| + + + + + + + +## Contract syntax + +In previous versions of JsLIGO, contracts could be defined in namespaces or at the top level of a file. +Version 2.0 adds the option to define contracts in classes. +Whether you define contracts in namespaces, classes, or in the top level of a file depends on the features that you need and the syntax that you prefer. +For example, if you want a contract to implement an interface, you must put the contract in a class. + +The syntax for contracts in namespaces and in the top levels of files has changed in some ways: + +- As before, they can include type definitions +- As before, they must define entrypoints as functions with the `function`, `const`, or `let` properties +- They must put decorators such as `@entry` and `@view` in a comment immediately before the definition that they modify +- They cannot implement interfaces + +Here is an example of a JsLIGO 2.0 contract in a namespace. +The only change for JsLIGO 2.0 is that the decorators are now in comments: + +```jsligo group=namespace +namespace Counter { + type storage_type = int; + type return_type = [list, storage_type]; + + // @entry + const add = (value: int, storage: storage_type): return_type => + [[], storage + value]; + + // @entry + const sub = (value: int, storage: storage_type): return_type => + [[], storage - value]; +} +``` + +The syntax for a JsLIGO 2.0 contract at the top level of a file is similar. +The only change for JsLIGO 2.0 is that the decorators are now in comments: + +```jsligo group=toplevel +type storage_type = int; +type return_type = [list, storage_type]; + +// @entry +const add = (value: int, store: storage_type): return_type => + [[], store + value]; + +// @entry +const sub = (value: int, store: storage_type): return_type => + [[], store - value]; +``` + +However, the syntax for a JsLIGO 2.0 contract in a class is different, as described in [Classes](#classes). + +## Namespaces + +As described in [Contract syntax](#contract-syntax), the syntax for namespaces (whether they define a contract or merely a group of other related definitions) has changed in some ways: + +- As before, they can include type definitions +- As before, they must define entrypoints as functions with the `function`, `const`, or `let` properties +- They must put decorators such as `@entry` and `@view` in a comment immediately before the definition that they modify +- They cannot implement interfaces + +## Classes + +JsLIGO 2.0 introduces classes, which are similar to classes in JavaScript/Typescript. +You can use JsLIGO classes to define a smart contract or group related functions, which are referred to as properties when they are in a class. +However, JsLIGO classes do not have all of the same features as JavaScript/TypeScript classes. +For example, JsLIGO classes cannot inherit from other classes as they can in JavaScript/TypeScript. + +JsLIGO classes have these characteristics: + +- They cannot include type definitions +- They define functions (including entrypoints) without the `function`, `const`, or `let` keywords +- They do not need to put decorators such as `@entry` and `@view` in comments +- They can implement interfaces + +Here is an example contract that is defined in a class: + +```jsligo group=classes +type storage_type = int; +type return_type = [list, storage_type]; + +class Counter { + @entry + add = (value: int, storage: storage_type): return_type => + [[], storage + value]; + + @entry + sub = (value: int, storage: storage_type): return_type => + [[], storage - value]; +} +``` + +To encapsulate classes with types, such as if you want to define a type that represents the contract storage or complex parameter types, you can group the contract class and the related types in a namespace. + +## Interfaces + +The syntax for interfaces has changed; they now have a syntax that is similar to classes: + +- Like classes, they cannot contain type definitions. +- They contain function signatures that are not defined with the `function`, `const`, or `let` properties. +- Unlike classes, they must put decorators such as `@entry` and `@view` in a comment immediately before the definition that they modify. + +For example, this is an interface in JsLIGO 1.9.2: + +```jsligo skip +// Not valid in JsLIGO 2.0 +interface MyInterface { + type storage = int; + @entry + const add: (s: int, k: storage) => [list, storage]; +} +``` + +This is the equivalent interface in JsLIGO 2.0: + +```jsligo group=interfaces +type storage = int; +interface MyInterface { + // @entry + add: (s: int, k: storage) => [list, storage]; +} +``` + +As a result of these changes, abstract types (type names that are required by an interface without specifying the actual type) are no longer permitted in JsLIGO. + +## Types + +JsLIGO 2.0 includes changes to make its type system more similar to TypeScript. + +### Specifying the types of literals + +Instead of using postfixes to denote certain types, LIGO 2.0 uses the `as` keyword to denote the type of a literal, as in these examples: + +Data type | LIGO 1.0 | LIGO 2.0 +--- | --- | --- +Bytes | `const zero = 0x` | `const zero = "" as bytes` +Natural numbers | `const one = 1n` | `const one = 1 as nat` +tez | `const one_tez = 1tez` | `const one_tez = 1 as tez` +mutez | `const one_mutez = 1mutez` | `const one_mutez = 1 as mutez` + +Similarly, the `parameter_of` keyword now takes its type in a way more similar to TypeScript: + +LIGO 1.0 | LIGO 2.0 +--- | --- +`const parameter = MyEntrypoint(value) as parameter_of MyContract;` | `const parameter = MyEntrypoint(value) as parameter_of;` + +### Records (now called objects) + +To be closer to JavaScript, JslIGO now refers to the record data type as an object. +JsLIGO objects are similar to JavaScript objects, but they have some limitations that JavaScript objects don't have. + +To specify the type of an object, use commas to separate the fields, not semicolons as in the previous version of JsLIGO. +Continue to use commas to separate the values in an object, as in the previous version of JsLIGO and as in JavaScript. +For example, this contract uses an object type in JsLIGO 2.0: + +```jsligo group=objects +type object_type = {x: int, y: int, z: int}; +type storage_type = object_type; +type return_type = [list, storage_type]; + +class Counter { + @entry + reset = (_: unit, _s: storage_type): return_type => { + const newObject: object_type = { + x: 0, + y: 0, + z: 0, + }; + return [[], newObject]; + } + + @entry + increment = (_: unit, storage: storage_type): return_type => { + const { x, y, z } = storage; + const newObject: object_type = { + x: x + 1, + y: y + 1, + z: z + 1, + }; + return [[], newObject]; + } +} +``` + +Field name punning (a shorthand way of setting the value of a field to a variable of the same name) is no longer permitted in object type definitions. +This example shows how to avoid punning by specifying the type of each field explicitly: + +```jsligo group=type_punning +type x = int; +type y = int; +type z = int; +// type object_type = { x, y, z }; // not permitted +type object_type = {x: x, y: y, z: z}; // correct +``` + +Punning is still permitted in variable definitions, as in this example: + +```jsligo group=value_punning +type object_type = { x: int, y: int, z: int }; +const x = 5; +const y = 6; +const z = 7; +const newObject: object_type = { x, y, z }; +``` + +JsLIGO objects have these limitations that JavaScript objects don't have: + +- You cannot change the fields of a JsLIGO object that is declared as a constant. +However, you can change the fields of a JsLIGO object that is declared as a variable. +- You cannot add fields to an object after you create it, regardless of whether it is declared as a constant or a variable. +- You cannot access fields in an object by putting a variable name in brackets; you must access the fields by specifying the field names as literals in brackets or with the selection operator (`.`). + +For more information about objects, see [Objects](../data-types/records). + +### Variants and options + +The syntax for variant and option types has changed. +If the variant type has only one variant, it must start with a vertical bar, as in this example: + +```jsligo group=union +type singleton = | ["Single"]; +``` + +To define the values of a variant type, use the usual tuple syntax, with the type as the first tuple value and the value of the variant as the second tuple value. +To prevent the compiler from seeing the first value as a string, you must set its type as one of the constructors from the variant type, as in this example: + +```jsligo group=union +type user = + ["Admin", nat] +| ["Manager", int] +| ["Guest"]; + +const admin1: user = ["Admin" as "Admin", 1 as nat]; +const manager2: user = ["Manager" as "Manager", 2 as int]; +const guest3: user = ["Guest" as "Guest"]; +``` + +## Pattern matching + +The syntax for pattern matching has changed to use the `$match` predefined function instead of the `match` keyword. +As its parameters, the `$match` function receives the variant value to match on and an object. +The property names of the object are the constructors of the corresponding variant type. +The property values are either a single expression or functions that receive the value of the variant as a parameter. +Here is an example: + +```jsligo group=matching +type user = + ["Admin", nat] +| ["Manager", nat] +| ["Guest"]; + +const greetUser = (user: user): string => { + $match (user, { + "Admin": _id => "Hello, administrator", + "Manager": _id => "Welcome, manager", + "Guest": () => "Hello, guest", + }); +} +``` + +To define the thunk, use an immediately invoked function expression (IIFE) as the result of a match, as in the following example. +JsLIGO 2.0 uses this syntax in place of `do` expressions in the previous version. + +```jsligo group=matching +const getSquareOfUserId = (user: user): nat => { + $match (user, { + "Admin": id => (() => { + const squareOfId = id * id; + return squareOfId; + })(), + "Manager": id => (() => { + const squareOfId = id * id; + return squareOfId; + })(), + "Guest": () => 0, + }); +} +``` + +Matching works only on values of variant types. +If you want to use the `$match` statement on other types or on more than one variable at a time, you can wrap them in a variant or option. +This example wraps two types in a variant so the `$match` statement can use them both: + +```jsligo group=matching +type wrapper = | ["Wrap", [int, int]]; +const wrapped: wrapper = ["Wrap" as "Wrap", [5, 5]]; + +const compareResult: string = $match(wrapped, { + "Wrap": ([_a, _b]) => (() => { + if (_a == _b) return "Equal"; + if (_a > _b) return "Greater"; + if (_a < _b) return "Less"; + return "Default"; + })(), +}); +``` + +Also, match cases can accept only one parameter. +In LIGO v1, the following match statement was allowed; note that the match expression for the `RGB` case accepts three parameters, one for each of the values in the variant: + +```jsligo skip +type colour = +| ["RGB", [int, int, int]] +| ["Gray", int] +| ["Default"]; + +let colourInt: colour = RGB(1, 2, 3); +let result = match(colourInt) { + when(Gray(val)): do { + const a = 5n; + const b = 6n; + return a + b + abs(val); + }; + when(RGB(a, b, c)): do { + return abs(a + b + c); + } + when(Default): do { + return 5n; + }; +} +``` + +The equivalent match expression in LIGO v2 accepts only one parameter, a tuple that contains the values from the variant case: + +```jsligo group=match_case_tuple +type colour = + ["RGB", [int, int, int]] | ["Gray", int] | ["Default"]; + +let colourInt: colour = ["RGB" as "RGB", 1, 2, 3]; +let result = + $match(colourInt, { + "Gray": (val) => + (() => + { + const a = (5 as nat); + const b = (6 as nat); + return a + b + abs(val); + })(), + "RGB": ([a, b, c]) => + (() => + { return abs(a + b + c); })(), + "Default": () => + (() => + { return (5 as nat); })(), + }) +``` + +## Imports + +JsLIGO now uses a syntax closer to JavaScript/TypeScript to import definitions. +It supports three syntaxes for importing definitions from other files and namespaces: + +- `import M = M.O` +- `import * as M from "./targetFile.jsligo"` +- `import {x, y} from "./targetFile.jsligo"` + +In most cases, to import definitions from other files, use the syntax `import * as M from "./targetFile.jsligo"` to import everything in a file and then create local definitions from that imported file. + +For example, assume that this file is `myFunctions.jsligo`: + +```jsligo group=myFunctions +export namespace MyFunctions { + export const addToImport = (a: int, b: int): int => a + b; + export const subToImport = (a: int, b: int): int => a - b; +} +``` + +You can import the file and access the namespace like this: + +```jsligo group=useMyFunctions +import * as MyFileWithFunctions from './gitlab-pages/docs/intro/src/upgrade-v2/myFunctions.jsligo'; +const addToImport = MyFileWithFunctions.MyFunctions.addToImport; +const subToImport = MyFileWithFunctions.MyFunctions.subToImport; + +namespace Counter { + type storage_type = int; + type return_type = [list, storage_type]; + + // @entry + const add = (value: int, storage: storage_type): return_type => + [[], addToImport(storage, value)]; + + // @entry + const sub = (value: int, storage: storage_type): return_type => + [[], subToImport(storage, value)]; + +} +``` + +If variables or functions are defined at the top level of the file, you can import them directly with the syntax `import {x, y} from "./targetFile.jsligo"`. +You cannot import types, classes, or namespaces with this syntax. + +For example, assume that this file is `topLevelDefinitions.jsligo`: + +```jsligo group=topLevelDefinitions +export const addToImport = (a: int, b: int): int => a + b; +export const subToImport = (a: int, b: int): int => a - b; +export const myConstant = 5 as int; +``` + +You can import and use those definitions as in this example: + +```jsligo group=useTopLevelDefinitions +import { addToImport, subToImport, myConstant } from "./gitlab-pages/docs/intro/src/upgrade-v2/topLevelDefinitions.jsligo"; + +type storage_type = int; +type return_type = [list, storage_type]; + +class Calculator { + + @entry + add = (value: int, storage: storage_type): return_type => + [[], addToImport(storage, value)]; + + @entry + sub = (value: int, storage: storage_type): return_type => + [[], subToImport(storage, value)]; + + @entry + increment = (_: unit, storage: storage_type): return_type => + [[], addToImport(storage, myConstant)]; + +} +``` + +You cannot import types as in this TypeScript syntax: + +```jsligo skip +// Not allowed +import { type x } from "./myTypes.jsligo"; +``` + +Instead, import the file and bind a type locally. +For example, assume that this file is `myTypes.jsligo`: + +```jsligo group=myTypes +export type storage_type = int; +export type return_type = [list, storage_type]; +``` + +This file imports those types and uses them: + +```jsligo group=use_myTypes +import * as myTypes from "./gitlab-pages/docs/intro/src/upgrade-v2/myTypes.jsligo"; +type storage_type = myTypes.storage_type; +type return_type = myTypes.return_type; + +class Counter { + @entry + add = (value: int, storage: storage_type): return_type => + [[], storage + value]; + + @entry + sub = (value: int, storage: storage_type): return_type => + [[], storage - value]; +} +``` + +## Preprocessor directives + +Preprocessor directives are no longer used in JsLIGO version 2.0. +Here are some ways to update code that uses preprocessor directives: + +- Instead of using the `#include` or `#import` directives, import namespaces in other files directly with the `import` keyword as described previously. + +- The `#if`, `#else`, `#elif`, `#endif`, `#define`, `#undef`, and `#error` directives are no longer supported. +If you need to continue using them, you can run your JsLIGO code through a C++ preprocessor, which uses the same syntax. +JsLIGO code with these directives does not compile. + +## The `switch` statement + +Blocks that use the `switch` statement must have at least one case. +Also, `switch` blocks can have only one default case. + +## `do` expressions + +Instead of the expression `do { ... }`, use an IIFE in the format `(() => {})()`. +IIFEs are often used to run tests, as in this example: + +```jsligo skip +const test = (() => { + + const contract = Test.Originate.contract(contract_of(Counter), 0, 0 as tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("add", contract.taddr), 5, 0 as tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("sub", contract.taddr), 2, 0 as tez); + Assert.assert(Test.Typed_address.get_storage(contract.taddr) == 3); + +})(); +``` + +## Escaping keywords + +Escaping keywords is no longer supported; keywords cannot be used as variable names or object fields, even if you prefix the names with the `@` symbol. +For example, you can add an underscore as a suffix, creating variables with names such as `return_` or `entry_`. + +## Decorators + +Decorators such as `@entry` and `@view` must now be in a comment immediately before the definition that they modify except when they are in classes, as described above. + +## Commands + +The `ligo run dry-run` command accepts a different format for entrypoints and parameters. +Instead of accepting the name of the entrypoint, it accepts the entrypoint and parameter as a value of a variant type with the name of the entrypoint. +The name of the entrypoint is always capitalized. +For example, this command calls an entrypoint named `add` that accepts an integer as a parameter: + +```bash +ligo run dry-run counter.jsligo -m "Counter" '["Add" as "Add", 1]' 5 +``` + + \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/boolean-if-else.md b/gitlab-pages/docs/language-basics/boolean-if-else.md index f0561fa433..d8f2d4b015 100644 --- a/gitlab-pages/docs/language-basics/boolean-if-else.md +++ b/gitlab-pages/docs/language-basics/boolean-if-else.md @@ -334,14 +334,14 @@ let h : bool = (a <> b) ```jsligo group=c -const a = 5; -const b = 4; -const c = (a == b); -const d = (a > b); -const e = (a < b); -const f = (a <= b); -const g = (a >= b); -const h = (a != b); +const a : int = 5; +const b : int = 4; +const c : bool = (a == b); +const d : bool = (a > b); +const e : bool = (a < b); +const f : bool = (a <= b); +const g : bool = (a >= b); +const h : bool = (a != b); ``` @@ -356,8 +356,8 @@ To check if the following operators have the expected result use Usage: ```cameligo group=d -let a : bytes = 0x1001 -let b : bytes = 0x1000 +let a : bytes = 0x1001 +let b : bytes = 0x1000 let c : bool = (a = b) let d : bool = (a > b) let e : bool = (a < b) @@ -376,14 +376,14 @@ To check if the following operators have the expected result use Usage: ```jsligo group=d -const a = 0x1001; -const b = 0x1000; -const c = (a == b); -const d = (a > b); -const e = (a < b); -const f = (a <= b); -const g = (a >= b); -const h = (a != b); +const a : bytes = 0x1001; +const b : bytes = 0x1000; +const c : bool = (a == b); +const d : bool = (a > b); +const e : bool = (a < b); +const f : bool = (a <= b); +const g : bool = (a >= b); +const h : bool = (a != b); ``` @@ -396,8 +396,8 @@ const h = (a != b); ```cameligo group=e -let a : tez = 5mutez -let b : tez = 10mutez +let a : tez = 5mutez +let b : tez = 10mutez let c : bool = (a = b) // false ``` @@ -406,15 +406,13 @@ let c : bool = (a = b) // false ```jsligo group=e -const a: tez = 5mutez; -const b: tez = 10mutez; -const c = (a == b); // false +const a: tez = 5 as mutez; +const b: tez = 10 as mutez; +const c : bool = (a == b); // false ``` - - ## Conditionals Conditional logic enables forking the control flow depending on the @@ -447,8 +445,9 @@ ligo run evaluate-call gitlab-pages/docs/language-basics/src/boolean-if-else/con ```jsligo group=cond type magnitude = ["Small"] | ["Large"]; // See variant types. -const compare = (n) => { - if (n < 10n) return Small() else return Large() +const compare = (n: nat): magnitude => { + if (n < (10 as nat)) return ["Small" as "Small"]; + return ["Large" as "Large"] }; ``` @@ -522,7 +521,7 @@ ligo run evaluate-call gitlab-pages/docs/language-basics/src/boolean-if-else/sw JsLIGO also supports JavaScript's ternary expression: ```jsligo -const ternary = a => a == 1 ? true : false; +const ternary = a => (a == 1) ? true : false; ``` which can also be nested: @@ -537,4 +536,4 @@ const ternary_nested = a => - \ No newline at end of file + diff --git a/gitlab-pages/docs/language-basics/composite-types.md b/gitlab-pages/docs/language-basics/composite-types.md index 4710d21479..49de364bd7 100644 --- a/gitlab-pages/docs/language-basics/composite-types.md +++ b/gitlab-pages/docs/language-basics/composite-types.md @@ -64,13 +64,13 @@ type ledger = map ; const my_ledger : ledger = Map.literal([ ["tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" as address, - {balance: 10mutez, transactions: 5n}]]); + {balance: 10 as mutez, transactions: 5 as nat}]]); ``` -Complementary to records are the *variant types*, which are described in the -section on [pattern matching](../language-basics/unit-option-pattern-matching#variant-types). -Records are a product of types, while variant types are sums of types. +Complementary to objects are the *variant types*, which are described in the +section on [pattern matching](../data-types/variants#matching). +Objects are a product of types, while variant types are sums of types. diff --git a/gitlab-pages/docs/language-basics/functions.md b/gitlab-pages/docs/language-basics/functions.md deleted file mode 100644 index 999ba71bff..0000000000 --- a/gitlab-pages/docs/language-basics/functions.md +++ /dev/null @@ -1,335 +0,0 @@ ---- -id: functions -title: Functions ---- - -import Syntax from '@theme/Syntax'; - -LIGO functions are the basic building block of contracts. For example, -entrypoints are functions and each smart contract needs a main -function that dispatches control to the entrypoints (it is not already -the default entrypoint). - -The semantics of function calls in LIGO is that of a *copy of the -arguments but also of the environment*. In the case of JsLIGO, this -means that any mutation (assignment) on variables outside the scope of -the function will be lost when the function returns, just as the -mutations inside the functions will be. - -## Declaring Functions - - - -Functions in CameLIGO are defined using the `let` keyword, like other -values. The difference is that a succession of parameters is provided -after the value name, followed by the return type. This follows OCaml -syntax. For example: -```cameligo group=add -let add (a : int) (b : int) : int = a + b -``` - -You can call the function `add` defined above using the LIGO compiler -like this: -```shell -ligo run evaluate-expr \ - gitlab-pages/docs/language-basics/src/functions/add.mligo \ - 'add 1 2' -# Outputs: 3 -``` - -CameLIGO is a little different from other syntaxes when it comes to -function parameters. In OCaml, functions can only take one -parameter. To get functions with multiple arguments like we are used -to in imperative programming languages, a technique called -[currying](https://en.wikipedia.org/wiki/Currying) is used. Currying -essentially translates a function with multiple arguments into a -series of single argument functions, each returning a new function -accepting the next argument until every parameter is filled. This is -useful because it means that CameLIGO supports -[partial application](https://en.wikipedia.org/wiki/Partial_application). - -Currying is however *not* the preferred way to pass function arguments -in CameLIGO. While this approach is faithful to the original OCaml, -it is costlier in Michelson than naive function execution accepting -multiple arguments. Instead, for most functions with more than one -parameter, we should gather the arguments in a -[tuple](sets-lists-tuples.md) and pass the tuple in as -a single parameter. - -Here is how you define a basic function that accepts two integers and -returns an integer as well: - -```cameligo group=curry -let add (a, b : int * int) : int = a + b // Uncurried -let add_curry (a : int) (b : int) : int = add (a, b) // Curried -let increment : int -> int = add_curry 1 // Partial application -``` - -You can run the `increment` function defined above using the LIGO -compiler like this: -```shell -ligo run evaluate-call \ - gitlab-pages/docs/language-basics/src/functions/curry.mligo \ - increment 5 -# Outputs: 6 -``` - -The function body is a single expression, whose value is returned. - -By default, LIGO will warn about unused arguments inside -functions. In case we do not use an argument, we can use the wildcard -`_` to prevent warnings. Either use `_` instead of the argument -identifier: - -```cameligo -let k (x : int) (_ : int) = x -``` - -or use an identifier starting with wildcard: - -```cameligo -let k (x : int) (_y : int) = x -``` - - -Sometimes, one has to chain multiple function applications. -In this case, parentheses are needed. - -```cameligo group=revapp -let f (x : int) = x + 1 -let g (x : int) = x - 2 -let h (x : int) = x + x - 3 - -(* Here we apply function f on value 42, - then apply g on the result, - and then apply h on the result *) -let result = h (g (f 42)) - -(* Parentheses are indeed necessary here. If we remove them, we have : *) -// let result' = h g f 42 -(* Which is different, it is equivalent to : *) -// let result' = ((h g) f) 42 -``` - -Here, one might want to reduce the number of parentheses, for readability. -In this case, the reverse-application operator (`|>`) can be used instead. - -Expression `f x` can be rewritten as `x |> f`, -and `g (f x)` can be rewritten as `x |> f |> g` -(you can think of it as "I take `x`, give it to function `f`, and then the result to function `g`"). - -Above `result` can thus be rewritten as : - -```cameligo group=revapp -let result = 42 |> f |> g |> h -``` - -Function application has precedence over reverse-application operator, -which means `f 42 |> g` is the same as `(f 42) |> g` and not `f (42 |> g)`. -So you can rewrite `result` as : - -```cameligo group=revapp -let result = f 42 |> g |> h -``` - -This can be useful when you have to deal with a long chain of function calls. - -This operator actually comes from [OCaml's pervasives](https://v2.ocaml.org/releases/4.02/htmlman/libref/Pervasives.html#6_Compositionoperators). -Other similar operators will be added when enabling support for custom operator definition. - - - - - -Functions in JsLIGO can be defined in two main ways: using the keyword -`function` or `const` (the keyword `let` is defaulted to `const` in -this instance). The latter manner is preferred when the function body -is an expression. For example, here is how you define a basic function -that sums two integers: - -```jsligo group=add -const add = (a: int, b: int) => a + b; -``` - -You can call the function `add` defined above using the LIGO compiler -like this: -```shell -ligo run evaluate-expr \ - gitlab-pages/docs/language-basics/src/functions/add.jsligo \ - 'add(1,2)' -# Outputs: 3 -``` - -If the body contains statements instead of a single expression, you -would use a block and a `return` statement: - -```jsligo group=b -const myFun = (x: int, y: int) => { - const doubleX = x + x; - const doubleY = y + y; - return doubleX + doubleY; -}; -``` - -although it is arguably more readable to use `function`, like so: - -```jsligo group=b -function myFun2 (x: int, y: int) { - const doubleX = x + x; - const doubleY = y + y; - return doubleX + doubleY; -} -``` - -Note that JsLIGO, like JavaScript, requires the `return` keyword to indicate -what is being returned. If `return` is not used, it will be the same as -`return unit`. - -By default, LIGO will warn about unused arguments inside -functions. In case we do not use an argument, its name should start with -`_` to prevent warnings. - -```jsligo -const k_other = (x: int, _y: int) => x; -``` - - - - -## Anonymous functions (a.k.a. lambdas) - -It is possible to define functions without assigning them a name. They -are useful when you want to pass them as arguments, or assign them to -a key in a record or a map. - -Here is how to define an anonymous function: - - - -```cameligo group=anon -let increment (b : int) : int = (fun (a : int) -> a + 1) b -let a = increment 1 // a = 2 -``` - -You can check the value of `a` defined above using the LIGO compiler -like this: -```shell -ligo run evaluate-expr gitlab-pages/docs/language-basics/src/functions/anon.mligo a -# Outputs: 2 -``` - - - - - -```jsligo group=anon -const increment = (b) => ((a) => a + 1) (b); -const a = increment(1); // a == 2 -``` - -You can check the value of `a` defined above using the LIGO compiler -like this: -```shell -ligo run evaluate-expr gitlab-pages/docs/language-basics/src/functions/anon.jsligo a -# Outputs: 2 -``` - - - - -If the example above seems contrived, here is a more common design -pattern for lambdas: to be used as parameters to functions. Consider -the use case of having a list of integers and mapping the increment -function to all its elements. - - - -```cameligo group=incr_map -let incr_map (l : int list) : int list = - List.map (fun (i : int) -> i + 1) l -``` -You can call the function `incr_map` defined above using the LIGO compiler -like so: -```shell -ligo run evaluate-call \ - gitlab-pages/docs/language-basics/src/functions/incr_map.mligo \ - incr_map "[1;2;3]" -# Outputs: CONS(2 , CONS(3 , CONS(4 , LIST_EMPTY()))), equivalent to [ 2 ; 3 ; 4 ] -``` - - - - - -```jsligo group=incr_map -let incr_map = l => List.map(i => i + 1, l); -``` -You can call the function `incr_map` defined above using the LIGO compiler -like so: -```shell -ligo run evaluate-call \ - gitlab-pages/docs/language-basics/src/functions/incr_map.jsligo \ - incr_map "[1,2,3]" -# Outputs: CONS(2 , CONS(3 , CONS(4 , LIST_EMPTY()))), equivalent to [2, 3, 4] -``` - - - - -## Nested functions (also known as closures) - -It is possible to define a functions inside another function. These -functions have access to variables in the same scope. - - - -```cameligo -let closure_example (i : int) : int = - let closure = fun (j : int) -> i + j in - closure i -``` - - - - - -```jsligo -function closure_example (i) { - let closure = j => i + j; - return closure(i); -}; -``` - - - - -## Recursive functions - - -In CameLIGO, recursive functions are defined using the `rec` keyword - -```cameligo group=d -let rec sum (n, acc : int * int) : int = - if n < 1 then acc else sum (n-1, acc + n) - -let rec fibo (n, n_1, n_0 : int * int * int) : int = - if n < 2 then n_1 else fibo (n-1, n_1 + n_0, n_1) -``` - - - - -In JsLigo, recursive functions are defined and called using the same syntax as non-recursive functions. - -```jsligo group=d -function sum (n: int, acc: int): int { - if (n < 1) return acc else return sum(n-1, acc + n); -}; - -function fibo (n: int, n_1: int, n_0: int): int { - if (n < 2) return n_1 else return fibo (n-1, n_1 + n_0, n_1); -}; -``` - - - diff --git a/gitlab-pages/docs/language-basics/loops.md b/gitlab-pages/docs/language-basics/loops.md deleted file mode 100644 index 3ac9d7e29f..0000000000 --- a/gitlab-pages/docs/language-basics/loops.md +++ /dev/null @@ -1,227 +0,0 @@ ---- -id: loops -title: Iteration ---- - -import Syntax from '@theme/Syntax'; - -## General Iteration - - - -CameLIGO is a functional language where user-defined values are -constant, therefore the preferred way to write iterations is by means -of recursive functions. Here is how to compute the greatest common -divisors of two natural numbers by means of Euclid's algorithm: - -```cameligo group=gcd -let rec iter (x, y : nat * nat) : nat = - if y = 0n then x else iter (y, x mod y) - -let gcd (x, y : nat * nat) : nat = - let x, y = if x < y then y,x else x,y - in iter (x, y) -``` - -You can call the function `gcd` defined above using the LIGO compiler -like so: -```shell -ligo run evaluate-call \ - gitlab-pages/docs/language-basics/src/loops/gcd.mligo \ - gcd '(2n*2n*3n*11n, 2n*2n*2n*3n*3n*5n*7n)' -# Outputs: +12 -``` - -CameLIGO also features loops, which we understand as syntactic -constructs where the state of a stopping condition is mutated. There -are two kinds of loops: for-loops and while-loops. Here is again -Euclid's algorithm, but using mutation and a while-loop: - -```cameligo group=gcd-loop -let gcd (a, b : nat * nat) = - let mut x, y = a, b in // we will modify x and y - let () = - if x < y then - let z = x in - begin - x := y; y := z - end in - let mut r : nat = 0n in - let () = - while y <> 0n do - r := x mod y; - x := y; - y := r - done - in x -``` - -Here is how to check if a string is a palindrome or not using a `for` loop: - -```cameligo group=a -let get_char s idx = String.sub idx 1n s - -let is_palindrome s = - let mut p = "" in - let length = String.length s in - let () = - for i = length - 1 downto 0 do - p := p ^ get_char s (abs i) - done - in p = s -``` - - - - - - -JsLIGO currently supports iteration through while-loops, for-loops, -and through the use of tail recursive functions. - -Here is how to check if a string is a palindrome or not using a `for` loop: - -```jsligo group=a -const getChar = (s: string, idx: nat): string => String.sub(idx, 1n, s); - -function isPalindrome (s: string): bool { - let p = ""; - let length = String.length(s); - for (let i = length - 1 ; i >= 0 ; i--) - p += getChar(s, abs(i)); - return p == s; -}; -``` - -Here is how to compute the greatest common divisors of two natural -numbers by means of Euclid's algorithm with using a while loop: - -```jsligo group=gcd -function gcd (a: nat, b: nat) { - let [x,y] = [a,b]; // we will modify x and y - if (x < y) { - const z = x; - x = y; y = z; - } - let r: nat = 0n - while (y != 0n) { - r = x % y; - x = y; - y = r; - } - return x; -}; -``` - -And here is how to compute the greatest common divisors of two natural -numbers by means of Euclid's algorithm using tail recursion: - -```jsligo group=gcd -function iter (x: nat,y: nat): nat { - if (y == 0n) return x else return iter (y, x % y) -}; - -function gcd2 (x: nat,y: nat) : nat { - if (x < y) return iter (y, x) else return iter (x, y) -}; -``` - -You can call the function `gcd` defined above using the LIGO compiler -like so: -```shell -ligo run evaluate-expr \ - gitlab-pages/docs/language-basics/src/loops/gcd.jsligo \ - 'gcd(2n*2n*3n*11n, 2n*2n*2n*3n*3n*5n*7n)' -# Outputs: +12 -``` - -and can call the function `gcd2` defined above using the LIGO compiler -like so: -```shell -ligo run evaluate-expr \ - gitlab-pages/docs/language-basics/src/loops/gcd.jsligo \ - 'gcd2(2n*2n*3n*11n, 2n*2n*2n*3n*3n*5n*7n)' -# Outputs: +12 -``` - -> Note: Currently JsLIGO does not support the key words `break` & `continue` in the context -> of loops. - - - - - -## for-of Loops - -JsLIGO "for-of" loops can iterate through the contents of a -collection, that is, a list, a set or a map. This is done with a loop -of the form `for (const of ) `. - -Here is an example where the integers in a list are summed up. - -```jsligo group=collection -function sum_list (l : list) { - let total = 0; - for (const i of l) total = total + i; - return total; -}; -``` - -You can call the function `sum_list` defined above using the LIGO compiler -like so: -```shell -ligo run evaluate-expr \ - gitlab-pages/docs/language-basics/src/loops/collection.jsligo \ - 'sum_list([1,2,3])' -# Outputs: 6 -``` - -Here is an example where the integers in a set are summed up. - -```jsligo group=collection -function sum_set (s : set) { - let total : int = 0; - for (const i of s) total = total + i; - return total; -}; -``` - -You can call the function `sum_set` defined above using the LIGO compiler -like so: -```shell -ligo run evaluate-expr \ - gitlab-pages/docs/language-basics/src/loops/collection.jsligo \ - 'sum_set(Set.literal([1,2,2,3]))' -# Outputs: 6 -``` - -Loops over maps are actually loops over the bindings of the map. -Given a map from strings to integers, here is how to sum -all the integers and concatenate all the strings. - - -```jsligo group=collection -function sum_map (m: map) { - let string_total = "" - let int_total = 0 - for (const item of m) { - let [key, value] = item; - string_total = string_total + key; - int_total = int_total + value - } - return [string_total, int_total] -} -``` - -You can call the function `sum_map` defined above using the LIGO compiler -like so: -```shell -ligo run evaluate-expr \ - gitlab-pages/docs/language-basics/src/loops/collection.jsligo \ - 'sum_map(Map.literal([ ["1", 1], ["2", 2], ["3", 3] ]))' -# Outputs: ( "123", 6 ) -``` - - - - diff --git a/gitlab-pages/docs/language-basics/maps-records.md b/gitlab-pages/docs/language-basics/maps-records.md deleted file mode 100644 index dccb381f7d..0000000000 --- a/gitlab-pages/docs/language-basics/maps-records.md +++ /dev/null @@ -1,841 +0,0 @@ ---- -id: maps-records -title: Records/Objects and Maps ---- - -import Syntax from '@theme/Syntax'; - -So far, we have seen pretty basic data types. LIGO also offers more -complex built-in constructs, such as *records* and *maps*. - -## Records/Objects - - - -Records are one-way data of different types can be packed into a -single type. A record is made of a set of *fields*, which are made of -a *field name* and a *field type*. Given a value of a record type, the -value bound to a field can be accessed by giving its field name to a -special operator (`.`). - -Let us first consider an example of record type declaration. - -```cameligo group=records1 -type user = { - id : nat; - is_admin : bool; - name : string -} -``` - - - - - -Objects are one-way data of different types can be packed into a -single type. An object is made of a set of *properties*, which are -made of a *property name* and a *property type*. Given a value of a -record type, the value bound to a field can be accessed by giving its -field name to a special operator (`.`). - -Let us first consider an example of object type declaration. - -```jsligo group=records1 -type user = { - id : nat, - is_admin : bool, - name : string -}; -``` - - - - - -And here is how a record value is defined: - -```cameligo group=records1 -let alice : user = { - id = 1n; - is_admin = true; - name = "Alice" -} -``` - - - - - -And here is how an object value is defined: - -```jsligo group=records1 -const alice : user = { - id : 1n, - is_admin : true, - name : "Alice" -}; -``` - - - -### Accessing Record Fields - -If we want the contents of a given field, we use the (`.`) infix -operator, like so: - - - -```cameligo group=records1 -let alice_admin : bool = alice.is_admin -``` - - - - - -```jsligo group=records1 -const alice_admin = alice.is_admin; -``` - - - -### Destructuring Records - -We can also access fields of a record using the destructuring syntax. -This allows accessing multiple fields of a record in a concise manner, like so: - - - -```cameligo group=records1 -let user_to_tuple (u : user) = - let { id ; is_admin ; name } = u in - (id, is_admin, name) -``` - - - - - -```jsligo group=records1 -function userToTuple (u : user) { - let { id, is_admin, name } = u; - return [id, is_admin, name]; -} -``` - - - -We can ignore some fields of the records we can do so by -using `_` (underscore), like so: - - - -```cameligo group=records1 -let get_id (u : user) = - let { id ; is_admin = _ ; name = _ } = u in - id -``` - - - - - -```jsligo group=records1 -function getId (u : user) { - let { id, is_admin, name } = u; - /* we don't use `is_admin` and `name` - so prevent warning with `ignore` */ - ignore([is_admin, name]); - return id -} -``` - - - - -### Functional Updates - -Given a record value, it is a common design pattern to update only a -small number of its fields. Instead of copying the fields that are -unchanged, LIGO offers a way to only update the fields that are -modified. - -One way to understand the update of record values is the *functional -update*. The idea is to have an *expression* whose value is the -updated record. - -Let us consider defining a function that translates three-dimensional -points on a plane. - - - -The syntax for the functional updates of record in CameLIGO follows -that of OCaml: - -```cameligo group=record_update -type point = {x : int; y : int; z : int} -type vector = {dx : int; dy : int} - -let origin : point = {x = 0; y = 0; z = 0} - -let xy_translate (p, vec : point * vector) : point = - {p with x = p.x + vec.dx; y = p.y + vec.dy} -``` - -You can call the function `xy_translate` defined above by running the -following command of the shell: - -```shell -ligo run evaluate-call \ - gitlab-pages/docs/language-basics/src/maps-records/record_update.mligo \ - xy_translate "({x=2;y=3;z=1}, {dx=3;dy=4})" -# Outputs: {z = 1 , y = 7 , x = 5} -``` - -> You have to understand that `p` has not been changed by the -> functional update: a nameless new version of it has been created and -> returned. - - - - - -The syntax for the functional updates of record in JsLIGO: - -```jsligo group=record_update -type point = {x: int, y: int, z: int} -type vector = {dx: int, dy: int} - -const origin = {x: 0, y: 0, z: 0}; - -const xy_translate = (p: point, vec: vector) => - ({...p, x: p.x + vec.dx, y: p.y + vec.dy}); -``` - -You can call the function `xy_translate` defined above by running the -following command of the shell: - -```shell -ligo run evaluate-expr \ - gitlab-pages/docs/language-basics/src/maps-records/record_update.jsligo \ - "xy_translate({x:2,y:3,z:1}, {dx:3,dy:4})" -# Outputs: record[x -> 5 , y -> 7 , z -> 1] -``` - -> It is important to understand that `p` has not been changed by the -> functional update: a nameless new version of it has been created and -> returned. - - - -#### Nested updates - - - -A unique feature of LIGO is the ability to perform nested updates on -records. For example if you have the following record structure: - -```cameligo group=record_nested_update -type color = Blue | Green - -type preferences = { - color : color; - other : int -} - -type account = { - id : int; - preferences : preferences -} -``` - - - - - -A unique feature of LIGO is the ability to perform nested updates on records. -JsLIGO however does not support the specialised syntax as the other syntaxes. -The following however also does the trick. - -For example if you have the following record structure: - -```jsligo group=record_nested_update -type color = ["Blue"] | ["Green"]; - -type preferences = { - color : color, - other : int -}; - -type account = { - id : int, - preferences : preferences -}; -``` - - - -You can update the nested record with the following code: - - - -```cameligo group=record_nested_update -let change_color_preference (account : account) (color : color) : account = - { account with preferences.color = color } -``` - - - - - -```jsligo group=record_nested_update -const change_color_preference = (account : account, color : color) => - ({ ...account, preferences: {...account.preferences, color: color }}); -``` - - - -Note that all the records in the path will get updated. In this -example, those are `account` and `preferences`. - -You can call the function `change_color_preference` defined above by running the -following command: - -```shell -ligo run evaluate-expr \ - gitlab-pages/docs/language-basics/src/maps-records/record_nested_update.jsligo \ - "change_color_preference({id:1001, preferences:{color:Blue(), other:1}}, Green())" -# Outputs: record[id -> 1001 , preferences -> record[color -> Green(unit) , other -> 1]] -``` - -### Comparison - - - -Record types are comparable, which allows to check for equality and -use records as key in sets or maps. By default, the ordering of -records is **undefined and implementation-dependent**. Ultimately, the -order is determined by the translated Michelson type. When using the -`@layout comb` (or `@layout:comb`) attribute, fields are translated in -their order in the record, and records are then ordered with -lexicographic ordering. - - - - - -Record types are comparable, which allows to check for equality and -use records as key in sets or maps. By default, the ordering of -records is **undefined and implementation-dependent**. Ultimately, the -order is determined by the translated Michelson type. When using the -decorator `@layout("comb")`, fields are translated in their order in -the record, and objects are then ordered with lexicographic ordering. - - - -## Maps - -*Maps* are a data structure which associate values of the same type to -values of the same type. The former are called *key* and the latter -*values*. Together they make up a *binding*. An additional requirement -is that the type of the keys must be *comparable*, in the Michelson -sense. - -### Declaring a Map - -Here is how a custom map from addresses to a pair of integers is -defined. - - - -```cameligo group=maps -type move = int * int -type register = (address, move) map -``` - - - - - -```jsligo group=maps -type move = [int, int]; -type register = map; -``` - - - - -### Creating an Empty Map - -Here is how to create an empty map. - - - -```cameligo group=maps -let empty : register = Map.empty -``` - - - - - -```jsligo group=maps -const empty: register = Map.empty; -``` - - - - -### Creating a Non-empty Map - -And here is how to create a non-empty map value: - - - -```cameligo group=maps -let moves : register = - Map.literal [ - (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), (1,2)); - (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (0,3))] -``` - -The `Map.literal` predefined function builds a map from a list of -key-value pair tuples, `(, )`. Note also the `;` to -separate individual map entries. `("": address)` means -that we type-cast a string into an address. - - - - - -```jsligo group=maps -const moves : register = - Map.literal ([ - ["tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" as address, [1,2]], - ["tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, [0,3]]]); -``` - -The `Map.literal` predefined function builds a map from a list of -key-value pair tuples, `[, ]`. Note also the `,` to -separate individual map entries. `"" as address` means -that we type-cast a string into an address. - - - - -### Accessing Map Bindings - - - -```cameligo group=maps -let my_balance : move option = - Map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves -``` - - - - - -```jsligo group=maps -const my_balance: option = - Map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, moves); -``` - - - - -Notice how the value we read is an optional value: this is to force -the reader to account for a missing key in the map. This requires -*pattern matching*. - - - -```cameligo group=maps -let force_access (key, moves : address * register) : move = - match Map.find_opt key moves with - Some move -> move - | None -> failwith "No move." -``` - - - - - -```jsligo group=maps -let force_access = (key: address, moves: register) => { - return match(Map.find_opt (key, moves)) { - when(Some(move)): move; - when(None()): failwith("No move.") - }; -}; -``` - - - - -### Updating a Map - -Given a map, we may want to add a new binding, remove one, or modify -one by changing the value associated to an already existing key. All -those operations are called *updates*. - - - -We can update a binding in a map in CameLIGO by means of the -`Map.update` built-in function: - -```cameligo group=maps -let assign (m : register) : register = - Map.update - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (Some (4,9)) m -``` - -Notice the optional value `Some (4,9)` instead of `(4,9)`. If we had -use `None` instead, that would have meant that the binding is removed. - -As a particular case, we can only add a key and its associated value. - -```cameligo group=maps -let add (m : register) : register = - Map.add - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (4,9) m -``` - - - - - -We can update a binding in a map in JsLIGO by means of the -`Map.update` built-in function: - -```jsligo group=maps -const assign = (m: register) => - Map.update - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, Some ([4, 9]), m); -``` - -Notice the optional value `Some ([4,9])` instead of `[4, 9]`. If we used -`None` instead that would have meant that the binding is removed. - -As a particular case, we can only add a key and its associated value. - -```jsligo group=maps -const add = (m: register) => - Map.add - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, [4, 9], m); -``` - - - -To remove a binding from a map, we need its key. - - - -In CameLIGO, we use the predefined function `Map.remove` as follows: - -```cameligo group=maps -let delete (key, moves : address * register) : register = - Map.remove key moves -``` - - - - - -In JsLIGO, we use the predefined function `Map.remove` as follows: - -```jsligo group=maps -const delete = (key: address, moves: register) => - Map.remove(key, moves); -``` - - - - - -### Functional Iteration over Maps - -A *functional iterator* is a function that traverses a data structure -and calls in turn a given function over the elements of that structure -to compute some value. Another approach is possible in JsLIGO: -*loops* (see the relevant section). - -There are three kinds of functional iterations over LIGO maps: the -*iterated operation*, the *map operation* (not to be confused with the -*map data structure*) and the *fold operation*. - -#### Iterated Operation over Maps - -The first, the *iterated operation*, is an iteration over the map with -no return value: its only use is to produce side-effects. This can be -useful if, for example you would like to check that each value inside -of a map is within a certain range and fail with an error otherwise. - -The predefined functional iterator implementing the iterated operation -over maps is called `Map.iter`. In the following example, the register -of moves is iterated to check that the start of each move is above -`3`. - - - -```cameligo group=maps -let iter_op (m : register) : unit = - let predicate = fun (i,j : address * move) -> assert (j.0 > 3) - in Map.iter predicate m -``` - - - - - -```jsligo group=maps -const assert_all_greater_than_three = (m: register) => { - let predicate = ([i, j]: [address, move]) => assert(j[0] > 3); - Map.iter(predicate, m); -}; -``` - - - - -#### Map Operations over Maps - -We may want to change all the bindings of a map by applying to them a -function. This is called a *map operation*, not to be confused with -the map data structure. The predefined functional iterator -implementing the map operation over maps is called `Map.map`. In the -following example, we add `1` to the ordinate of the moves in the -register. - - - -```cameligo group=maps -let map_op (m : register) : register = - let increment = fun (_,j : address * move) -> j.0, j.1 + 1 - in Map.map increment m -``` - - - - - -```jsligo group=maps -const map_op = (m: register) => { - let increment = ([_a, j]: [address, move]) => [j[0], j[1] + 1]; - return Map.map(increment, m); -}; -``` - - - - -#### Folded Operations over Maps - -A *folded operation* is the most general of iterations. The folded -function takes two arguments: an *accumulator* and the structure -*element* at hand, with which it then produces a new accumulator. This -enables having a partial result that becomes complete when the -traversal of the data structure is over. - -The predefined functional iterator implementing the folded operation -over maps is called `Map.fold` and is used as follows. - - - -```cameligo group=maps -let fold_op (m : register) : int = - let folded = fun (i,j : int * (address * move)) -> i + j.1.1 - in Map.fold folded m 5 -``` - - - - - -```jsligo group=maps -const fold_op = (m: register): int => { - let folded = ([i, j]: [int, [address, move]]) => i + j[1][1]; - return Map.fold(folded, m, 5); -}; -``` - - - - -## Big Maps - -Ordinary maps are fine for contracts with a finite lifespan or a -bounded number of users. For many contracts however, the intention is -to have a map holding *many* entries, potentially millions of -them. The cost of loading those entries into the environment each time -a user executes the contract would eventually become too expensive -were it not for *big maps*. Big maps are a data structure offered by -Michelson which handles the scaling concerns for us. In LIGO, the -interface for big maps is analogous to the one used for ordinary maps. - -### Declaring a Map - -Here is how we define a big map: - - - -```cameligo group=big_maps -type move = int * int -type register = (address, move) big_map -``` - - - - - -```jsligo group=big_maps -type move = [int, int]; -type register = big_map; -``` - - - - -### Creating an Empty Big Map - -Here is how to create an empty big map. - - - -```cameligo group=big_maps -let empty : register = Big_map.empty -``` - - - - - -```jsligo group=big_maps -const empty: register = Big_map.empty; -``` - - - -### Creating a Non-empty Map - -And here is how to create a non-empty map value: - - - -```cameligo group=big_maps -let moves : register = - Big_map.literal [ - (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), (1,2)); - (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (0,3))] -``` - -The predefined function `Big_map.literal` constructs a big map from a -list of key-value pairs `(, )`. Note also the semicolon -separating individual map entries. The annotated value `(" -value>" : address)` means that we cast a string into an address. - - - - - -```jsligo group=big_maps -const moves : register = - Big_map.literal([ - ["tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" as address, [1, 2]], - ["tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, [0, 3]]]); -``` - -The predefined function `Big_map.literal` constructs a big map from a -list of key-value pairs `[, ]`. Note also the semicolon -separating individual map entries. The annotated value `(" -value>" as address)` means that we cast a string into an address. - - - - -### Accessing Values - -If we want to access a move from our `register` above, we can use the -postfix `[]` operator to read the associated `move` value. However, -the value we read is an optional value (in our case, of type `option -(move)`), to account for a missing key. Here is an example: - - - -```cameligo group=big_maps -let my_balance : move option = - Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves -``` - - - - - -```jsligo group=big_maps -const my_balance: option = - Big_map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, moves); -``` - - - - -### Updating Big Maps - - - - -We can update a big map in CameLIGO using the `Big_map.update` -built-in: - -```cameligo group=big_maps -let updated_map : register = - Big_map.update - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (Some (4,9)) moves -``` - - - - - -We can update a big map in JsLIGO using the `Big_map.update` -built-in: - -```jsligo group=big_maps -const updated_map: register = - Big_map.update - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, Some([4, 9]), moves); -``` - - - - -### Removing Bindings - -Removing a binding in a map is done differently according to the LIGO -syntax. - - - -In CameLIGO, the predefined function which removes a binding in a map -is called `Map.remove` and is used as follows: - -```cameligo group=big_maps -let updated_map : register = - Big_map.remove ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves -``` - - - - - -In JsLIGO, the predefined function which removes a binding in a map -is called `Map.remove` and is used as follows: - -```jsligo group=big_maps -const updated_map_: register = - Big_map.remove("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, moves); -``` - - - - diff --git a/gitlab-pages/docs/language-basics/math-numbers-tez.md b/gitlab-pages/docs/language-basics/math-numbers-tez.md deleted file mode 100644 index c44f557971..0000000000 --- a/gitlab-pages/docs/language-basics/math-numbers-tez.md +++ /dev/null @@ -1,421 +0,0 @@ ---- -id: math-numbers-tez -title: Math, Numbers & Tez ---- - -import Syntax from '@theme/Syntax'; - -LIGO offers three built-in numerical types: `int`, `nat` and -`tez`. Values of type `int` are integers; values of type `nat` are -natural numbers (integral numbers greater than or equal to zero); -values of type `tez` are units of measure of Tezos tokens. - - * Integer literals are the same found in mainstream programming - languages, for example, `10`, `-6` and `0`, but there is only one - canonical zero: `0` (so, for instance, `-0` and `00` are invalid). - - * Natural numbers are written as digits followed by the suffix `n`, - like so: `12n`, `0n`, and the same restriction on zero as integers - applies: `0n` is the only way to specify the natural zero. - * Tezos tokens can be specified using literals of three kinds: - * units of millionth of `tez`, using the suffix `mutez` after a - natural literal, like `10000mutez` or `0mutez`; - * units of `tez`, using the suffix `tz` or `tez`, like `3tz` or - `3tez`; - * decimal amounts of `tz` or `tez`, like `12.3tz` or `12.4tez`. - -Note that large integral values can be expressed using underscores to -separate groups of digits, like `1_000mutez` or `0.000_004tez`. - -## Addition - -Addition in LIGO is accomplished by means of the `+` infix -operator. Some type constraints apply, for example you cannot add a -value of type `tez` to a value of type `nat`. - -In the following example you can find a series of arithmetic -operations, including various numerical types. However, some bits -remain in comments as they would otherwise not compile, for example, -adding a value of type `int` to a value of type `tez` is invalid. Note -that adding an integer to a natural number produces an integer. - - - -```cameligo group=a -// int + int yields int -let a : int = 5 + 10 - -// nat + int yields int -let b : int = 5n + 10 - -// tez + tez yields tez -let c : tez = 5mutez + 0.000_010tez - -// tez + int or tez + nat is invalid -// let d : tez = 5mutez + 10n - -// two nats yield a nat -let e : nat = 5n + 10n - -// nat + int yields an int: invalid -// let f : nat = 5n + 10 - -let g : int = 1_000_000 -``` - -> Tip: you can use underscores for readability when defining large -> numbers: -> ->```cameligo ->let sum : tez = 100_000mutez ->``` - - - - - -```jsligo group=a -// int + int yields int -const a = 5 + 10; - -// nat + int yields int -const b = 5n + 10; - -// tez + tez yields tez -const c: tez = 5mutez + 1tez; - -// tez + int or tez + nat is invalid: -// const d : tez = 5mutez + 10n; - -// two nats yield a nat -const e: nat = 5n + 10n; - -// nat + int yields an int: invalid -// const f : nat = 5n + 10; - -const g = 1_000_000; -``` - -> Tip: you can use underscores for readability when defining large -> numbers: ->```jsligo ->let sum : tez = 100_000mutez; ->``` - - - -## Subtraction - -Subtraction looks as follows. - -> ⚠️ Even when subtracting two `nats`, the result is an `int`. - - - -```cameligo group=b -let a : int = 5 - 10 - -// Subtraction of two nats yields an int -let b : int = 5n - 2n - -// Therefore the following is invalid -// let c : nat = 5n - 2n -``` - - - - - -```jsligo group=b -const a = 5 - 10; - -// Subtraction of two nats yields an int -const b: int = 5n - 2n; - -// Therefore the following is invalid -// const c : nat = 5n - 2n; -``` - - - -From protocol *`Ithaca`* onwards subtracting values of type `tez` -yeilds an optional value (due to the Michelson instruction -`SUB_MUTEZ`) - - - -```cameligo group=b -let d : tez option = 5mutez - 1mutez (* Some (4mutez) *) -let e : tez option = 1mutez - 5mutez (* None *) -``` - - - - - -```jsligo group=b -const d : option = 5mutez - 1mutez; /* Some (4mutez) */ -const e : option = 1mutez - 5mutez; /* None */ -``` - - - -## Multiplication - -You can multiply values of the same type, such as: - - - -```cameligo group=c -let a : int = 5 * 5 -let b : nat = 5n * 5n - -// You can also multiply `nat` and `tez` -let c : tez = 5n * 5mutez -``` - - - - - -```jsligo group=c -const a = 5 * 5; -const b: nat = 5n * 5n; - -// You can also multiply `nat` and `tez` -const c: tez = 5n * 5mutez; -``` - - - - -## Euclidean Division - -In LIGO you can divide `int`, `nat`, and `tez`. Here is how: - -> ⚠️ Division of two `tez` values results into a `nat`. - - - -```cameligo group=d -let a : int = 10 / 3 -let b : nat = 10n / 3n -let c : nat = 10mutez / 3mutez -``` - - - - - -```jsligo group=d -const a: int = 10 / 3; -const b: nat = 10n / 3n; -const c: nat = 10mutez / 3mutez; -``` - - - - -LIGO also allows you to compute the remainder of the Euclidean -division. In LIGO, it is a natural number. - - - -```cameligo group=d -let a : int = 120 -let b : int = 9 -let rem1 : nat = a mod b // 3 -let c : nat = 120n -let rem2 : nat = c mod b // 3 -let d : nat = 9n -let rem3 : nat = c mod d // 3 -let rem4 : nat = a mod d // 3 -``` - - - - - -> The behaviour of the `%` operator in JsLIGO is different from JavaScript. -> In JsLIGO, `%` is a modulus operator and in JavaScript it's a remainder -> operator. In the case of positive numbers everything is the same, but -> not with negative numbers. - -```jsligo group=e -const a = 120; -const b = 9; -const rem1 = a % b; // 3 -const c = 120n; -const rem2 = c % b; // 3 -const d = 9n; -const rem3 = c % d; // 3 -const rem4 = a % d; // 3 -``` - - - - -For cases when you need both the quotient and the remainder, LIGO provides the -`ediv` operation. `ediv x y` returns `Some (quotient, remainder)`, unless `y` -is zero, in which case it returns `None` - - - -```cameligo group=f -let a : int = 37 -let b : int = 5 -let ediv1 : (int * nat) option = ediv a b // Some (7, 2) -let c : nat = 37n -let ediv2 : (int * nat) option = ediv c b // Some (7, 2) -let d : nat = 5n -let ediv3 : (nat * nat) option = ediv c d // Some (7, 2) -let ediv4 : (int * nat) option = ediv a d // Some (7, 2) -``` - - - - - -```jsligo group=f -const a = 37; -const b = 5; -const ediv1 : option<[int , nat]> = ediv(a, b); // Some (7, 2) -const c = 37n; -const ediv2: option<[int , nat]> = ediv(c, b); // Some (7, 2) -const d = 5n; -const ediv3: option<[nat , nat]> = ediv(c, d); // Some (7, 2) -const ediv4: option<[int , nat]> = ediv(a, d); // Some (7, 2) -``` - - - - -## From `int` to `nat` and back - -You can *cast* an `int` to a `nat` and vice versa. Here is how: - - - -```cameligo group=e -let a : int = int (1n) -let b : nat = abs (1) -``` - - - - - -```jsligo group=g -const a = int(1n); -const b = abs(1); -``` - - - - -## Checking a `nat` - -You can check if a value is a `nat` by using a predefined cast -function which accepts an `int` and returns an optional `nat`: if the -result is not `None`, then the provided integer was indeed a natural -number, and not otherwise. - - - -```cameligo group=e -let is_a_nat : nat option = is_nat (1) -``` - - - - - -```jsligo group=h -const is_a_nat = is_nat(1); -``` - - - - - -## Bitwise operations -You can perform bitwise operations as follows: - -> bitwise operations can be performed mostly with `nat`'s -> -> only in case of bitwise and, the first operand can be either `int` or `nat` - -```cameligo -// Bitwise and (first operand can be int or nat) -let four : nat = 4n land 4n // 4 -let four_ : nat = 7 land 4n // 4 -// Bitwise or -let seven : nat = 7n lor 4n // 7 -// Bitwise xor -let three : nat = 7n lxor 4n // 3 -// Bitwise shift left -let fourteen : nat = 7n lsl 1n // 14 -// Bitwise shift right -let seven_ : nat = 14n land 1n // 7 -``` - - - - - -## Increment operator - -Increment opeator increments (adds one to) the value of the binder. - -In the **prefix** position (`++p`) the operator increments the value and returns -the latest incremented value. - -In the **postfix** position (`p++`) the operator increments the value but -returns the old value before the increment. - -```jsligo test-ligo group=increment_ops - -const testInc = (() => { - let inc = 0; - - // Prefix increment operator - assert(++inc == 1); - assert(inc == 1); - - // Postfix increment operator - assert(inc++ == 1); - assert(inc == 2); -})(); - -``` - - -## Decrement operator - -Decrement opeator decrements (subtracts one from) the value of the binder. - -In the **prefix** position (`--p`) the operator decrements the value and returns -the latest decremented value. - -In the **postfix** position (`p--`) the operator decrements the value but -returns the old value before the decrement. - -```jsligo test-ligo group=decrement_ops - -const testDec = (() => { - let v = 10; - - // Prefix decrement operator - assert(--v == 9); - assert(v == 9); - - // Postfix decrement operator - assert(v-- == 9); - assert(v == 8); -})(); - -``` - - - - \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/modules.md b/gitlab-pages/docs/language-basics/modules.md deleted file mode 100644 index bee335b52f..0000000000 --- a/gitlab-pages/docs/language-basics/modules.md +++ /dev/null @@ -1,600 +0,0 @@ ---- -id: modules -title: Modules -jsLigoTitle: Namespaces -hide_title: true ---- - -import Syntax from '@theme/Syntax'; - - - -# Namespaces - - - - -# Modules - - - - - -> Note that in JsLIGO modules are called `namespaces`. - - - -Modules are a programming language construction that allows us to -package related definitions together. A canonical example of a module -is a data type and associated operations over it (e.g. stacks or -queues). The rest of the program can access these definitions in a -regular and abstract way, providing maintainability, reusability and -safety. - -For a concrete example, we could create a module that packages a type -that represents amounts in a particular currency together with -functions that manipulate these amounts: constants, addition, -subtraction, etc. A piece of code that uses this module can be -agnostic concerning how the type is actually represented inside the -module: it is said to be abstract. - -## Declaring Modules - - - -Modules are introduced using the `module` keyword. For example, the -following code defines a module `EURO` that packages together a type, -called `t`, together with an operation `add` that sums two values of -the given currency, as well as constants for zero and one. - -```cameligo group=EURO -module EURO = - struct - type t = nat - let add (a , b : t * t) : t = a + b - let zero : t = 0n - let one : t = 1n - end -``` - -As we can see, in CameLIGO we also use a `struct ... end` block to -group together the definitions made in the module. - - - - - -Modules are introduced using the `namespace` keyword. For example, the -following code defines a module `EURO` that packages together a type, -called `t`, together with an operation `add` that sums two values of -the given currency, as well as constants for zero and one. - -```jsligo group=EURO -namespace EURO { - export type t = nat; - export const add = (a: t, b: t) : t => a + b; - export const zero: t = 0n; - export const one: t = 1n -} -``` - -In this example you will also notice the `export` keyword. A statement -within a module can be accessed from outside the module if it is -exported. - - - -## Using Modules - -We can access a module's components by using the selection operator -`.`. Let us suppose that our storage keeps a value in euros using the -previously defined module `EURO`. Then, we can write a `main` entry -point that increments the storage value each time it is called. - - - -```cameligo group=EURO -type storage = EURO.t - -[@entry] -let main (_action : unit) (store : storage) : operation list * storage = - [], EURO.add (store, EURO.one) -``` - - - - -```jsligo group=EURO -type storage = EURO.t; - -@entry -let main = (_action: unit, store: storage): [list, storage] => - [[], EURO.add (store, EURO.one)]; -``` - - - -In principle, we could change the implementation of `EURO`, without -having to change the `storage` type or the function `main`. For -example, if we decide later that we should support manipulating -negative values, we could change `EURO` as follows: - - - -```cameligo group=EURO2 -module EURO = - struct - type t = int - let add (a, b : t * t) : t = a + b - let zero : t = 0 - let one : t = 1 - end -``` - - - - - -```jsligo group=EURO2 -namespace EURO { - export type t = int; - export const add = (a: t, b: t) : t => a + b; - export const zero: t = 0; - export const one: t = 1; -} -``` - - - -Notice that the code in `main` still works, and no change is -needed. Abstraction accomplished! - -> ⚠️ Please note that code using the module `EURO` might still break -> the abstraction if it directly uses the underlying representation of -> `EURO.t`. Client code should always try to respect the interface -> provided by the module, and not make assumptions on its current -> underlying representation (e.g. `EURO.t` is a transparent alias -> of `nat`; future versons of LIGO might make this an opaque/abstract type). - -## Nested Modules: Sub-Modules - -Modules can be nested, which means that we can define a module inside -another module. Let's see how that works, and define a variant of -`EURO` in which the constants are all grouped inside using a sub-module. - - - -```cameligo group=EURO3 -module EURO = - struct - type t = nat - - let add (a, b : t * t) : t = a + b - - module CONST = - struct - let zero : t = 0n - let one : t = 1n - end - end -``` - - - - - -```jsligo group=EURO3 -namespace EURO { - export type t = nat; - - export let add = (a: t, b: t): t => a + b; - - export namespace CONST { - export let zero: t = 0n; - export let one: t = 1n; - }; -}; -``` - - - -To access nested modules we simply apply the selection operator more -than once: - - - -```cameligo group=EURO3 -type storage = EURO.t - -[@entry] -let main (_action : unit) (store : storage) : operation list * storage = - [], EURO.add (store, EURO.CONST.one) -``` - - - - - -```jsligo group=EURO3 -type storage = EURO.t; - -@entry -let main = (_action: unit, store: storage) : [list, storage] => - [[], EURO.add (store, EURO.CONST.one)] -``` - - - -## Modules and Imports: Build System - -Modules also allow us to separate our code in different files: when we -import a file, we obtain a module encapsulating all the definitions in -it. This will become very handy for organising large contracts, as we -can divide it into different files, and the module system keeps the naming -space clean. - -Generally, we will take a set of definitions that can be naturally -grouped by functionality, and put them together in a separate -file. - - - -For example, in CameLIGO, we can create a file `imported.mligo`: - -```cameligo group=imported -type t = nat - -let add (a , b : t * t) : t = a + b - -let zero : t = 0n -let one : t = 1n -``` - - - - - -For example, in JsLIGO, we can create a file `imported.jsligo`: - -```jsligo group=imported -export type t = nat; - -export const add = (a: t, b: t): t => a + b; - -export const zero: t = 0n; -export const one: t = 1n; -``` - - - - - -Later, in another file, we can import `imported.mligo` as a module, and -use its definitions. For example, we could create a `importer.mligo` -that imports all definitions from `imported.mligo` as the module -`EURO`: - -```cameligo group=importer -#import "gitlab-pages/docs/language-basics/src/modules/imported.mligo" "EURO" - -type storage = EURO.t - -[@entry] -let main (_action : unit) (store : storage) : operation list * storage = - ([], EURO.add(store, EURO.one)) -``` - - - - - -Later, in another file, we can import `imported.jsligo` as a module, and -use its definitions. For example, we could create a `importer.jsligo` -that imports all definitions from `imported.jsligo` as the module -`EURO`: - -```jsligo group=importer -#import "gitlab-pages/docs/language-basics/src/modules/imported.jsligo" "EURO" - -type storage = EURO.t; - -@entry -const main = (_action: unit, store: storage): [list, storage] => - [[], EURO.add(store, EURO.one)]; -``` - - - -We can compile the file that uses the `#import` statement directly, -without having to mention the imported file. - - - -```shell -ligo compile contract --library . gitlab-pages/docs/language-basics/src/modules/importer.mligo -``` - - - - - -```shell -ligo compile contract --library . gitlab-pages/docs/language-basics/src/modules/importer.jsligo -``` - - - - -## Module Aliases - -LIGO supports module aliases, that is, modules that work as synonyms -to other (previously defined) modules. This feature can be useful if -we could implement a module using a previously defined one, but in the -future, we might need to change it. - - - -```cameligo group=EURO -module US_DOLLAR = EURO -``` - - - - - -```jsligo group=EURO -import US_DOLLAR = EURO; -``` - - - - -## Modules as Contracts - - - -When a module contains declarations that are tagged with the attribute -`@entry`, then a contract can be obtained from such module. All -declarations in the module tagged as `@entry` are grouped, and a -dispatcher contract is generated. - -```cameligo group=contract -module C = struct - [@entry] let increment (p : int) (s : int) : operation list * int = [], s + p - [@entry] let decrement (p : int) (s : int) : operation list * int = [], s - p -end -``` - - - - - -When a module contains declarations that are tagged with the `@entry` -decorator, then a contract can be obtained from such module. All -declarations in the module tagged as `@entry` are grouped, and a -dispatcher contract is generated. - -```jsligo group=contract -namespace C { - @entry - const increment = (p : int, s : int) : [list, int] => [[], s + p]; - @entry - const decrement = (p : int, s : int) : [list, int] => [[], s - p]; -}; -``` - - - -A module can be compiled as a contract using `-m`: - - - -```shell -ligo compile contract gitlab-pages/docs/language-basics/src/modules/contract.mligo -m C -``` - - - - - -```shell -ligo compile contract gitlab-pages/docs/language-basics/src/modules/contract.jsligo -m C -``` - - - -To access the contract from the module, the primitive `contract_of` -can be used. The type of the parameter generated for the module can be -obtaining using the primitive `parameter_of`. This is particularly -useful when working with the testing framework, in conjunction with the -function `Test.originate`: - - - -```cameligo group=contract -let test = - let orig = Test.originate (contract_of C) 0 0tez in - let _ = Test.transfer_exn orig.addr (Increment 42) 0tez - in assert (42 = Test.get_storage orig.addr) -``` - - - - - -```jsligo group=contract -const test = do { - let orig = Test.originate(contract_of(C), 0, 0tez); - Test.transfer_exn(orig.addr, (Increment (42)), 1mutez); - return assert(Test.get_storage(orig.addr) == 42); -}; -``` - - - -## Module Inclusion - - -When writing a new version of a given contract derived from a module, -it is often needed to add new features, that is, new types and values, -for example when implementing the next version of a standard. This can -be achieved by defining a new module that includes the types and -values of the old one, and defines new ones. - -The inclusion of a module `M` is specified with a field `include M`, -like so: - -```cameligo group=contract -module FA0 = struct - type t = unit - [@entry] let transfer (_ : unit) (_ : t) : operation list * t = [], () -end - -module FA0Ext = struct - include FA0 - [@entry] let transfer2 (a : unit) (b : t) = transfer a b -end -``` - - - - -This feature is not available in JsLIGO. - - -## Module Types - - - -Until now, we dealt with implicit module types, also know as -signatures. Having explicitly declared module types enable abstraction -and reusability by inclusion of signatures. Module types are defined -like in OCaml: - -```cameligo group=contract2 -module type FA0_SIG = sig - type t - [@entry] val transfer : unit -> t -> operation list * t -end - -module type FA0Ext_SIG = sig - include FA0_SIG - [@entry] val transfer2 : unit -> t -> operation list * t -end -``` - -Notice how `t` in the type of `transfer2` refers to `t` in the -signature `FA0_SIG` and remains abstract. We can now revisit the -examples above by constraining the module definitions with the module -types: - -```cameligo group=contract2 -module FA0 : FA0_SIG = struct - type t = unit - [@entry] let transfer (_ : unit) (_ : t) : operation list * t = [], () -end - -module FA0Ext : FA0Ext_SIG = struct - include FA0 - [@entry] let transfer2 (a : unit) (b : t) = transfer a b -end -``` - -Note how module definitions must instantiate any abstract type (here -`FA0Impl.t`). Also, when a module is constrained by a signature, it -must implement the types and values in the latter, but no more: this -is a filtering semantics. - - - - -Until now, we dealt with implicit types of namespaces, also know as -interfaces. Having explicitly declared interface enable more -expressivity and type safety. Interfaces are introduced by the keyword -`interface` and their bodies lists names of types and values paired -with their type, like so: - -```jsligo group=contract2 -interface FA0_INTF { - type storage; - @entry const add : (s : int, k : storage) => [list, storage]; -} -``` - -An interface can then be used to constrain a namespace definition, -ensuring that said namespace contains *at least* the types and values -listed in the given interface, like so: - -```jsligo group=contract2 -namespace FA0 implements FA0_INTF { - export type storage = int; - @entry const add = (s : int, k : int) : [list, int] => [[], s + k]; - @entry const extra = (s : int, k : int) : [list, int] => [[], s - k]; -} -``` - -Interfaces can be extended by inheritance, like so: - -```jsligo group=contract2 -interface FABase_INTF { - type t; -}; - -interface FA0_INTF extends FABase_INTF { - @entry const transfer : (_u : unit, s : t) => [list, t]; -}; - -interface FA0Ext_INTF extends FA0_INTF { - @entry const transfer1 : (_u : unit, s : t) => [list, t]; -}; - -interface FA1_INTF extends FABase_INTF { - @entry const transfer2 : (_u : unit, s : t) => [list, t]; -}; -``` - -Note how the abstract type `t` in `FABase_INTF` remains abstract. - -It is possible to design diamond inheritance, that is, inheriting -twice the same base interface, like so: - -```jsligo group=contract2 -interface FAAll_INTF extends FA0Ext_INTF, FA1_INTF { - @entry const transfer3 : (_u : unit, s : t) => [list, t]; - @view const v1 : (_u : unit, s : t) => t; - @entry const opt_val? : (i : int, s : t) => [list, t]; -} -``` - -Here, the abstract type `t` was inherited twice from -`FABase_INTF`. Note the *optional value* `opt_val`, distinghished as -such by a question mark: `opt_val?`. This means that a namespace -implementing `FAAll_INTF` can choose not to implement -`opt_val`. The implementation of an interface can be done as follows: - -```jsligo group=contract2 -namespace FAAll_wo_opt_val implements FAAll_INTF { - export type t = int; - - @entry const transfer = (_u : unit, s : t) : [list, t] => [[], s]; - @entry const transfer1 = (_u : unit, s : t) : [list, t] => [[], s]; - @entry const transfer2 = (_u : unit, s : t) : [list, t] => [[], s]; - @entry const transfer3 = (_u : unit, s : t) : [list, t] => [[], s]; - @view const v1 = (_u : unit, s : t) : t => s; - - /* "foo", "transfer4" and "v2" are not in "FAAll_INTF", but can - nevertheless be added here, because "implements" does not filter, - but only have the compiler check that the fields in the interface - are implemented. */ - - export const foo = (s : t) : t => s; - @entry const transfer4 = (_u : unit, s : t) : [list, t] => [[], s]; - @view const v2 = (_u : unit, s : t) : t => s; -} - -``` - - - - - diff --git a/gitlab-pages/docs/language-basics/operators.md b/gitlab-pages/docs/language-basics/operators.md deleted file mode 100644 index cceb2a0e87..0000000000 --- a/gitlab-pages/docs/language-basics/operators.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -id: operators -title: Operators ---- - -## Available Michelson Operators - -> This list is non-exhaustive. More operators will be added in -> upcoming LIGO releases. - -| Michelson | LIGO | Description -|----------|--------------------|------------- -| `SENDER` | `Tezos.get_sender` | Address that initiated the current transaction -| `SOURCE` | `Tezos.get_source` | Address that initiated the transaction, which triggered the current transaction. (useful e.g. when there's a transaction sent by another contract) -| `AMOUNT` | `Tezos.get_amount` | Amount of tez sent by the transaction that invoked the contract -| `NOW` | `Tezos.get_now` | Timestamp of the block whose validation triggered execution of the contract, i.e. current time when the contract is run. - - \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/sets-lists-tuples.md b/gitlab-pages/docs/language-basics/sets-lists-tuples.md deleted file mode 100644 index 6949661f35..0000000000 --- a/gitlab-pages/docs/language-basics/sets-lists-tuples.md +++ /dev/null @@ -1,624 +0,0 @@ ---- -id: sets-lists-tuples -title: Tuples, Lists, Sets ---- - -import Syntax from '@theme/Syntax'; - -Apart from complex data types such as `maps` and `records`, LIGO also -features `tuples`, `lists` and `sets`. - -## Tuples - - - -Tuples gather a given number of values in a specific order and those -values, called *components*, can be retrieved by their index -(position). Probably the most common tuple is the *pair*. For -example, if we were storing coordinates on a two dimensional grid we -might use a pair `(x,y)` to store the coordinates `x` and `y`. There -is a *specific order*, so `(y,x)` is not equal to `(x,y)` in -general. The number of components is part of the type of a tuple, so, -for example, we cannot add an extra component to a pair and obtain a -triple of the same type: `(x,y)` has always a different type from -`(x,y,z)`, whereas `(y,x)` might have the same type as `(x,y)`. - - - - - -Tuples gather a given number of values in a specific order and those -values, called *components*, can be retrieved by their index -(position). Probably the most common tuple is the *pair*. For -example, if we were storing coordinates on a two dimensional grid we -might use a pair `[x, y]` to store the coordinates `x` and `y`. There -is a *specific order*, so `[y, x]` is not equal to `[x, y]` in -general. The number of components is part of the type of a tuple, so, -for example, we cannot add an extra component to a pair and obtain a -triple of the same type: `[x, y]` has always a different type from -`[x, y, z]`, whereas `[y, x]` might have the same type as `[x, y]`. - - -Like records, tuple components can be of arbitrary types. - -### Defining Tuples - -Unlike [a record](maps-records.md), tuple types do not -have to be defined before they can be used. However below we will give -them names by *type aliasing*. - - - -```cameligo group=tuple -type two_people = string * string // Alias - -let friends : two_people = ("Alice", "Johnson") // Optional parentheses -``` - - - - - -```jsligo group=tuple -type two_people = [string, string]; // Alias - -const friends: two_people = ["Alice", "Johnson"]; -``` - - - - - -### Destructuring - -If we want to get the first and second names of the `two_people` type, we can use -destructuring. Destructuring a tuple allows you to give names to the elements -inside the tuple. - -```cameligo group=tuple -let (person_a, person_b) : two_people = friends -``` - -This also works in functions: - -```cameligo group=tuple -let first_person ((person_a, _): two_people) = person_a -let alice = first_person friends -``` - -Notice that we use the underscore to indicate that we ignore the last element -of the tuple. - - - - - -### Destructuring - -If we want to get the first and second names of the `two_people` type, we can use -destructuring. Destructuring a tuple allows you to give names to the elements -inside the tuple. - -```jsligo group=tuple -let [person_a, person_b] = friends; -``` - -This also works in functions: - -```jsligo group=tuple -let first_person_fun = ([person_a, _person_b]: two_people) => person_a; -let alice = first_person_fun(friends); -``` - -> note: the leading underscore to indicate that the argument `_person_b` is unused. - -and within a code block: - -```jsligo group=tuple -let destruct_tuple = (x : [ int , [int , nat] ]) : nat => { - let [a,[b,c]] = x ; - return c -}; -``` - -```jsligo group=tuple -let destruct_record = (x : { a : int , b : string }) : int => { - let { a , b } = x ; - return a -}; -``` - -> note: nested patterns in record destructuring are not yet available - - - -### Accessing Components - -Accessing the components of a tuple in OCaml is achieved by -[pattern matching](unit-option-pattern-matching.md). LIGO -currently supports tuple patterns only in the parameters of functions, -not in pattern matching. However, we can access components by their -position in their tuple, which cannot be done in OCaml. *Tuple -components are zero-indexed*, that is, the first component has index -`0`. - - - -```cameligo group=tuple -let first_name : string = friends.0 -``` - - - - - -```jsligo group=tuple -const first_name_component = friends[0]; -``` - - - - -## Lists - -Lists are linear collections of elements of the same type. Linear -means that, in order to reach an element in a list, we must visit all -the elements before (sequential access). Elements can be repeated, as -only their order in the collection matters. The first element is -called the *head*, and the sub-list after the head is called the -*tail*. For those familiar with algorithmic data structure, you can -think of a list a *stack*, where the top is written on the left. - -> 💡 Lists are needed when returning operations from a smart -> contract's main function. - -### Defining Lists - - - -```cameligo group=lists -let empty_list : int list = [] -let my_list : int list = [1; 2; 2] (* The head is 1, the tail is [2; 2] *) -``` - - - - - -```jsligo group=lists -const empty_list : list = []; -const my_list : list = [1, 2, 2]; // The head is 1, the tail is [2, 2] -``` - - - - -### Adding to Lists - -Lists can be augmented by adding an element before the head (or, in -terms of stack, by *pushing an element on top*). This operation is -usually called *consing* in functional languages. - - - -In CameLIGO, the *cons operator* is infix and noted `::`. It is not -symmetric: on the left lies the element to cons, and, on the right, a -list on which to cons. - -```cameligo group=lists -let larger_list : int list = 5 :: my_list (* [5;1;2;2] *) -``` - - - - - -In JsLIGO, the *cons operator* is infix and noted `, ...`. It is -not symmetric: on the left lies the element to cons, and, on the -right, a list on which to cons. - -```jsligo group=lists -const larger_list : list = [5, ...my_list]; // [5,1,2,2] -``` - - - -### Accessing list elements - -You cannot access element directly in list but you can access the -first element, the head or the rest of the list, the tail. The two -function to access those are `List.head_opt` and `List.tail_opt` - - - -```cameligo group=lists -let head : int option = List.head_opt my_list (* 1 *) -let tail : int list option = List.tail_opt my_list (* [2;2] *) -``` - - - - - -```jsligo group=lists -const head: option = List.head_opt(my_list); // 1 -const tail: option> = List.tail_opt(my_list); // [2,2] -``` - - - -However, the canonical way to destructure lists is using [pattern -matching](unit-option-pattern-matching.md#matching-lists). - -### Functional Iteration over Lists - -A *functional iterator* is a function that traverses a data structure -and calls in turn a given function over the elements of that structure -to compute some value. Another approach is possible in JsLIGO: -*loops* (see the relevant section). - -There are three kinds of functional iterations over LIGO lists: the -*iterated operation*, the *map operation* (not to be confused with the -*map data structure*) and the *fold operation*. - -#### Iterated Operation over Lists - -The first, the *iterated operation*, is an iteration over the list -with a unit return value. It is useful to enforce certain invariants -on the element of a list, or fail. - -For example you might want to check that each value inside of a list -is within a certain range, and fail otherwise. The predefined -functional iterator implementing the iterated operation over lists is -called `List.iter`. - -In the following example, a list is iterated to check that all its -elements (integers) are strictly greater than `3`. - - - -```cameligo group=lists -let assert_all_greater_than_three (l : int list) : unit = - let predicate = fun (i:int) -> assert (i > 3) - in List.iter predicate l -``` - - - - - -```jsligo group=lists -const assert_all_greater_than_three = (l: list): unit => { - let predicate = i => assert(i > 3); - List.iter(predicate, l); -}; -``` - - - - -#### Mapped Operation over Lists - -We may want to change all the elements of a given list by applying to -them a function. This is called a *map operation*, not to be confused -with the map data structure. The predefined functional iterator -implementing the mapped operation over lists is called `List.map` and -is used as follows. - - - -```cameligo group=lists -let increment (i : int) = i + 1 - -// Creates a new list with all elements incremented by 1 -let plus_one : int list = List.map increment larger_list (* [6,2,3,3] *) -``` - - - - - -```jsligo group=lists -const increment = i => i + 1; - -// Creates a new list with all elements incremented by 1 -const plus_one: list = List.map(increment, larger_list); // [6,2,3,3] -``` - - - - - -#### Folded Operation over Lists - -A *folded operation* is the most general of iterations. The folded -function takes two arguments: an *accumulator* and the structure -*element* at hand, with which it then produces a new accumulator. This -enables having a partial result that becomes complete when the -traversal of the data structure is over. Folding can be done in two -ways, labelled with the directions left and right. One way to tell them -apart is to look where the folded function, and the fold itself, keep -the accumulator in their signatures. Take for example a function `f`, -a list `[1; 2; 3; 4; 5]`, and an accumulator that's just an empty -list. A rough approximation of the result of a left fold would look -like `f(f(f(f(f([], 1), 2), 3), 4), 5)`, while a right fold would -instead look like `f(1, f(2, f(3, f(4, f(5, [])))))`. - -The left fold operation has a function signature of -`List.fold_left (a -> x -> a) -> a -> x list -> a`, while the right -fold operation has `List.fold_right (x -> a -> a) -> x list -> a -> a`. -Here is an example of their use. - - - -```cameligo group=lists -let sum (acc, i: int * int) = acc + i -let sum_of_elements : int = List.fold_left sum 0 my_list -``` - - - - - -```jsligo group=lists -const sum = ([result, i]: [int, int]) => result + i; -const sum_of_elements: int = List.fold (sum, my_list, 0); -``` - - - -## Sets - -Sets are unordered collections of values of the same type, like lists -are ordered collections. Like the mathematical sets and lists, sets -can be empty and, if not, elements of sets in LIGO are *unique*, -whereas they can be repeated in a *list*. - -### Empty Sets - - - -In CameLIGO, the empty set is denoted by the predefined value -`Set.empty`. - -```cameligo group=sets -let my_set : int set = Set.empty -``` - - - - - -In JsLIGO, the empty set is denoted by the predefined value -`Set.empty`. - -```jsligo group=sets -const my_empty_set: set = Set.empty; -``` - - - - -### Non-empty Sets - - - -In CameLIGO, you can create a non-empty set using the `Set.literal` function -which takes a list of elements & returns a set. - -```cameligo group=sets -let my_set : int set = Set.literal [3; 2; 2; 1] -``` -You can check that `2` is not repeated in `my_set` by using the LIGO -compiler like this (the output will sort the elements of the set, but -that order is not significant for the compiler): - -```shell -ligo run evaluate-expr gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.mligo my_set -# Outputs: SET_ADD(3 , SET_ADD(2 , SET_ADD(1 , SET_EMPTY()))) -``` - - - - - -In JsLIGO, you can define a non-empty set using the `Set.literal` function -which takes a list of elements & returns a set. - -```jsligo group=sets -let my_set: set = Set.literal([3, 2, 2, 1]); -``` - -You can check that `2` is not repeated in `my_set` by using the LIGO -compiler like this (the output will sort the elements of the set, but -that order is not significant for the compiler): - -```shell -ligo run evaluate-expr gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.jsligo my_set -# Outputs: SET_ADD(3 , SET_ADD(2 , SET_ADD(1 , SET_EMPTY()))) -``` - - - -### Adding an element to a Set - -You can add an element to a set, using `Set.add` function. - - - -```cameligo group=sets -let with_999 : int set = Set.add 999 my_set -``` - - - - - -```jsligo group=sets -const with_999: set = Set.add(999, my_set); -``` - - - -### Set Membership - - - -In CameLIGO, the predefined predicate `Set.mem` tests for membership -in a set as follows: - -```cameligo group=sets -let contains_3 : bool = Set.mem 3 my_set -``` - - - - - -In JsLIGO, the predefined predicate `Set.mem` tests for membership -in a set as follows: - -```jsligo group=sets -const contains_3: bool = Set.mem(3, my_set); -``` - - - - - -### Cardinal of Sets - -The predefined function `Set.size` returns the number of -elements in a given set as follows. - - - - -```cameligo group=sets -let cardinal : nat = Set.size my_set -``` - - - - - -```jsligo group=sets -const cardinal: nat = Set.size(my_set); -``` - - - -### Updating Sets - -There are two ways to update a set, that is to add or remove from -it. - - - -In CameLIGO, we can use the predefined functions `Set.add` and -`Set.remove`. We update a given set by creating another one, with or -without some elements. - -```cameligo group=sets -let larger_set : int set = Set.add 4 my_set -let smaller_set : int set = Set.remove 3 my_set -``` - - - - - -In JsLIGO, we can use the predefined functions `Set.add` and -`Set.remove`. We update a given set by creating another one, with or -without some elements. - -```jsligo group=sets -const larger_set: set = Set.add(4, my_set); -const smaller_set: set = Set.remove(3, my_set); -``` - - - -### Functional Iteration over Sets - -A *functional iterator* is a function that traverses a data structure -and calls in turn a given function over the elements of that structure -to compute some value. Another approach is possible in JsLIGO: -*loops* (see the relevant section). - -There are three kinds of functional iterations over LIGO maps: the -*iterated operation*, the *mapped operation* (not to be confused with -the *map data structure*) and the *folded operation*. - -#### Iterated Operation - -The first, the *iterated operation*, is an iteration over the map with -no return value: its only use is to produce side-effects. This can be -useful if for example you would like to check that each value inside -of a map is within a certain range, and fail with an error otherwise. - -The predefined functional iterator implementing the iterated operation -over sets is called `Set.iter`. In the following example, a set is -iterated to check that all its elements (integers) are greater than -`3`. - - - -```cameligo group=sets -let assert_all_greater_than_three (s : int set) : unit = - let predicate = fun (i : int) -> assert (i > 3) - in Set.iter predicate s -``` - - - - - -```jsligo group=sets -const assert_all_greater_than_three = s => { - let predicate = i => assert(i > 3); - Set.iter(predicate, s); -}; -``` - - - - -#### Folded Operation - -A *folded operation* is the most general of iterations. The folded -function takes two arguments: an *accumulator* and the structure -*element* at hand, with which it then produces a new accumulator. This -enables having a partial result that becomes complete when the -traversal of the data structure is over. - - - -The predefined fold over sets is called `Set.fold`, however an -additional function, `Set.fold_right`, has been added to properly -conform to the function signature of OCaml's `Set.fold` operation, and -it has the signature `val fold_right : ('acc * 'elt -> 'acc) -> 'elt -set -> 'acc -> 'acc`. - -```cameligo group=sets -let sum (acc, i : int * int) : int = acc + i -let sum_of_elements : int = Set.fold sum my_set 0 -``` - - - - - -The predefined fold over sets is called `Set.fold`, however an -additional function, `Set.fold_right`, has been added with the -signature `val fold_right : ('acc * 'elt -> 'acc) * 'elt set * 'acc -> -'acc`. - -```jsligo group=sets -const sum = ([acc, i]: [int, int]) => acc + i; -const sum_of_elements = Set.fold (sum, my_set, 0); -``` - - - - diff --git a/gitlab-pages/docs/language-basics/src/boolean-if-else/c.jsligo b/gitlab-pages/docs/language-basics/src/boolean-if-else/c.jsligo index ee6d4c4c7c..eb51588c3a 100644 --- a/gitlab-pages/docs/language-basics/src/boolean-if-else/c.jsligo +++ b/gitlab-pages/docs/language-basics/src/boolean-if-else/c.jsligo @@ -1,8 +1,8 @@ -const a = 5; -const b = 4; -const c = (a == b); -const d = (a > b); -const e = (a < b); -const f = (a <= b); -const g = (a >= b); -const h = (a != b); \ No newline at end of file +const a : int = 5; +const b : int = 4; +const c : bool = (a == b); +const d : bool = (a > b); +const e : bool = (a < b); +const f : bool = (a <= b); +const g : bool = (a >= b); +const h : bool = (a != b); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.jsligo b/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.jsligo index 45418ae30c..010a9fc13e 100644 --- a/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.jsligo +++ b/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.jsligo @@ -1,5 +1,6 @@ type magnitude = ["Small"] | ["Large"]; // See variant types. -const compare = (n) => { - if (n < 10n) return Small() else return Large() +const compare = (n: nat): magnitude => { + if (n < (10 as nat)) return ["Small" as "Small"]; + return ["Large" as "Large"] }; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/boolean-if-else/d.jsligo b/gitlab-pages/docs/language-basics/src/boolean-if-else/d.jsligo index 8d698fc44b..7f42a35a36 100644 --- a/gitlab-pages/docs/language-basics/src/boolean-if-else/d.jsligo +++ b/gitlab-pages/docs/language-basics/src/boolean-if-else/d.jsligo @@ -1,8 +1,8 @@ -const a = 0x1001; -const b = 0x1000; -const c = (a == b); -const d = (a > b); -const e = (a < b); -const f = (a <= b); -const g = (a >= b); -const h = (a != b); \ No newline at end of file +const a : bytes = 0x1001; +const b : bytes = 0x1000; +const c : bool = (a == b); +const d : bool = (a > b); +const e : bool = (a < b); +const f : bool = (a <= b); +const g : bool = (a >= b); +const h : bool = (a != b); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/boolean-if-else/d.mligo b/gitlab-pages/docs/language-basics/src/boolean-if-else/d.mligo index 51053d7283..d2dc45a8c2 100644 --- a/gitlab-pages/docs/language-basics/src/boolean-if-else/d.mligo +++ b/gitlab-pages/docs/language-basics/src/boolean-if-else/d.mligo @@ -1,5 +1,5 @@ -let a : bytes = 0x1001 -let b : bytes = 0x1000 +let a : bytes = 0x1001 +let b : bytes = 0x1000 let c : bool = (a = b) let d : bool = (a > b) let e : bool = (a < b) diff --git a/gitlab-pages/docs/language-basics/src/boolean-if-else/e.jsligo b/gitlab-pages/docs/language-basics/src/boolean-if-else/e.jsligo index 76481e2280..d08bec27e5 100644 --- a/gitlab-pages/docs/language-basics/src/boolean-if-else/e.jsligo +++ b/gitlab-pages/docs/language-basics/src/boolean-if-else/e.jsligo @@ -1,3 +1,3 @@ -const a: tez = 5mutez; -const b: tez = 10mutez; -const c = (a == b); // false \ No newline at end of file +const a: tez = 5 as mutez; +const b: tez = 10 as mutez; +const c : bool = (a == b); // false \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/boolean-if-else/e.mligo b/gitlab-pages/docs/language-basics/src/boolean-if-else/e.mligo index 7bb3bd13a3..3736af71a3 100644 --- a/gitlab-pages/docs/language-basics/src/boolean-if-else/e.mligo +++ b/gitlab-pages/docs/language-basics/src/boolean-if-else/e.mligo @@ -1,3 +1,3 @@ -let a : tez = 5mutez -let b : tez = 10mutez +let a : tez = 5mutez +let b : tez = 10mutez let c : bool = (a = b) // false \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/boolean-if-else/ungrouped.jsligo b/gitlab-pages/docs/language-basics/src/boolean-if-else/ungrouped.jsligo index bb8c6a3216..bb59e4976b 100644 --- a/gitlab-pages/docs/language-basics/src/boolean-if-else/ungrouped.jsligo +++ b/gitlab-pages/docs/language-basics/src/boolean-if-else/ungrouped.jsligo @@ -7,7 +7,7 @@ const gt = 4 > 3; const lt = 4 < 3; const gte = 4 >= 3; const lte = 4 <= 3; -const ternary = a => a == 1 ? true : false; +const ternary = a => (a == 1) ? true : false; const ternary_nested = a => a == 1 ? "one" : a == 2 ? "two" : diff --git a/gitlab-pages/docs/language-basics/src/composite-types/c.jsligo b/gitlab-pages/docs/language-basics/src/composite-types/c.jsligo index e91bc335e7..99b1953fc0 100644 --- a/gitlab-pages/docs/language-basics/src/composite-types/c.jsligo +++ b/gitlab-pages/docs/language-basics/src/composite-types/c.jsligo @@ -17,4 +17,4 @@ type ledger = map ; const my_ledger : ledger = Map.literal([ ["tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" as address, - {balance: 10mutez, transactions: 5n}]]); \ No newline at end of file + {balance: 10 as mutez, transactions: 5 as nat}]]); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/add.jsligo b/gitlab-pages/docs/language-basics/src/functions/add.jsligo deleted file mode 100644 index a9473a06ca..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/add.jsligo +++ /dev/null @@ -1 +0,0 @@ -const add = (a: int, b: int) => a + b; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/add.mligo b/gitlab-pages/docs/language-basics/src/functions/add.mligo deleted file mode 100644 index 56eeeea57c..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/add.mligo +++ /dev/null @@ -1 +0,0 @@ -let add (a : int) (b : int) : int = a + b \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/anon.jsligo b/gitlab-pages/docs/language-basics/src/functions/anon.jsligo deleted file mode 100644 index ff77275eaf..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/anon.jsligo +++ /dev/null @@ -1,2 +0,0 @@ -const increment = (b) => ((a) => a + 1) (b); -const a = increment(1); // a == 2 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/anon.mligo b/gitlab-pages/docs/language-basics/src/functions/anon.mligo deleted file mode 100644 index 4b6419af61..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/anon.mligo +++ /dev/null @@ -1,2 +0,0 @@ -let increment (b : int) : int = (fun (a : int) -> a + 1) b -let a = increment 1 // a = 2 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/b.jsligo b/gitlab-pages/docs/language-basics/src/functions/b.jsligo deleted file mode 100644 index 56ee738c3a..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/b.jsligo +++ /dev/null @@ -1,10 +0,0 @@ -const myFun = (x: int, y: int) => { - const doubleX = x + x; - const doubleY = y + y; - return doubleX + doubleY; -}; -function myFun2 (x: int, y: int) { - const doubleX = x + x; - const doubleY = y + y; - return doubleX + doubleY; -} \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/b.mligo b/gitlab-pages/docs/language-basics/src/functions/b.mligo deleted file mode 100644 index c0e2babe34..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/b.mligo +++ /dev/null @@ -1,3 +0,0 @@ -let add (a, b : int * int) : int = a + b // Uncurried -let add_curry (a : int) (b : int) : int = add (a, b) // Curried -let increment : int -> int = add_curry 1 // Partial application \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/c.jsligo b/gitlab-pages/docs/language-basics/src/functions/c.jsligo deleted file mode 100644 index ce9911233f..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/c.jsligo +++ /dev/null @@ -1,3 +0,0 @@ -const increment = (b) => ((a) => a + 1) (b); -const a = increment(1); // a == 2 -let incr_map = l => List.map(i => i + 1, l); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/c.mligo b/gitlab-pages/docs/language-basics/src/functions/c.mligo deleted file mode 100644 index 632dcdf7e1..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/c.mligo +++ /dev/null @@ -1,4 +0,0 @@ -let increment (b : int) : int = (fun (a : int) -> a + 1) b -let a = increment 1 // a = 2 -let incr_map (l : int list) : int list = - List.map (fun (i : int) -> i + 1) l \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/curry.mligo b/gitlab-pages/docs/language-basics/src/functions/curry.mligo deleted file mode 100644 index c0e2babe34..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/curry.mligo +++ /dev/null @@ -1,3 +0,0 @@ -let add (a, b : int * int) : int = a + b // Uncurried -let add_curry (a : int) (b : int) : int = add (a, b) // Curried -let increment : int -> int = add_curry 1 // Partial application \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/d.jsligo b/gitlab-pages/docs/language-basics/src/functions/d.jsligo deleted file mode 100644 index e9a9e6f3b4..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/d.jsligo +++ /dev/null @@ -1,7 +0,0 @@ -function sum (n: int, acc: int): int { - if (n < 1) return acc else return sum(n-1, acc + n); -}; - -function fibo (n: int, n_1: int, n_0: int): int { - if (n < 2) return n_1 else return fibo (n-1, n_1 + n_0, n_1); -}; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/d.mligo b/gitlab-pages/docs/language-basics/src/functions/d.mligo deleted file mode 100644 index f1a529e841..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/d.mligo +++ /dev/null @@ -1,5 +0,0 @@ -let rec sum (n, acc : int * int) : int = - if n < 1 then acc else sum (n-1, acc + n) - -let rec fibo (n, n_1, n_0 : int * int * int) : int = - if n < 2 then n_1 else fibo (n-1, n_1 + n_0, n_1) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/incr_map.jsligo b/gitlab-pages/docs/language-basics/src/functions/incr_map.jsligo deleted file mode 100644 index 07566dafbd..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/incr_map.jsligo +++ /dev/null @@ -1 +0,0 @@ -let incr_map = l => List.map(i => i + 1, l); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/incr_map.mligo b/gitlab-pages/docs/language-basics/src/functions/incr_map.mligo deleted file mode 100644 index f4dd70b607..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/incr_map.mligo +++ /dev/null @@ -1,2 +0,0 @@ -let incr_map (l : int list) : int list = - List.map (fun (i : int) -> i + 1) l \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/revapp.mligo b/gitlab-pages/docs/language-basics/src/functions/revapp.mligo deleted file mode 100644 index 51d85d7d2e..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/revapp.mligo +++ /dev/null @@ -1,15 +0,0 @@ -let f (x : int) = x + 1 -let g (x : int) = x - 2 -let h (x : int) = x + x - 3 - -(* Here we apply function f on value 42, - then apply g on the result, - and then apply h on the result *) -let result = h (g (f 42)) - -(* Parentheses are indeed necessary here. If we remove them, we have : *) -// let result' = h g f 42 -(* Which is different, it is equivalent to : *) -// let result' = ((h g) f) 42 -let result = 42 |> f |> g |> h -let result = f 42 |> g |> h \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/ungrouped.jsligo b/gitlab-pages/docs/language-basics/src/functions/ungrouped.jsligo deleted file mode 100644 index 65d9309abe..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/ungrouped.jsligo +++ /dev/null @@ -1,5 +0,0 @@ -const k_other = (x: int, _y: int) => x; -function closure_example (i) { - let closure = j => i + j; - return closure(i); -}; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/functions/ungrouped.mligo b/gitlab-pages/docs/language-basics/src/functions/ungrouped.mligo deleted file mode 100644 index 5d13261c76..0000000000 --- a/gitlab-pages/docs/language-basics/src/functions/ungrouped.mligo +++ /dev/null @@ -1,5 +0,0 @@ -let k (x : int) (_ : int) = x -let k (x : int) (_y : int) = x -let closure_example (i : int) : int = - let closure = fun (j : int) -> i + j in - closure i \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/loops/a.jsligo b/gitlab-pages/docs/language-basics/src/loops/a.jsligo deleted file mode 100644 index 1f8b822310..0000000000 --- a/gitlab-pages/docs/language-basics/src/loops/a.jsligo +++ /dev/null @@ -1,9 +0,0 @@ -const getChar = (s: string, idx: nat): string => String.sub(idx, 1n, s); - -function isPalindrome (s: string): bool { - let p = ""; - let length = String.length(s); - for (let i = length - 1 ; i >= 0 ; i--) - p += getChar(s, abs(i)); - return p == s; -}; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/loops/a.mligo b/gitlab-pages/docs/language-basics/src/loops/a.mligo deleted file mode 100644 index 62755aab4e..0000000000 --- a/gitlab-pages/docs/language-basics/src/loops/a.mligo +++ /dev/null @@ -1,10 +0,0 @@ -let get_char s idx = String.sub idx 1n s - -let is_palindrome s = - let mut p = "" in - let length = String.length s in - let () = - for i = length - 1 downto 0 do - p := p ^ get_char s (abs i) - done - in p = s \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/loops/collection.jsligo b/gitlab-pages/docs/language-basics/src/loops/collection.jsligo deleted file mode 100644 index b86157e052..0000000000 --- a/gitlab-pages/docs/language-basics/src/loops/collection.jsligo +++ /dev/null @@ -1,20 +0,0 @@ -function sum_list (l : list) { - let total = 0; - for (const i of l) total = total + i; - return total; -}; -function sum_set (s : set) { - let total : int = 0; - for (const i of s) total = total + i; - return total; -}; -function sum_map (m: map) { - let string_total = "" - let int_total = 0 - for (const item of m) { - let [key, value] = item; - string_total = string_total + key; - int_total = int_total + value - } - return [string_total, int_total] -} \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/loops/d.jsligo b/gitlab-pages/docs/language-basics/src/loops/d.jsligo deleted file mode 100644 index 814bece01d..0000000000 --- a/gitlab-pages/docs/language-basics/src/loops/d.jsligo +++ /dev/null @@ -1,10 +0,0 @@ -function sum_list (l : list) { - let total = 0; - for (const i of l) total = total + i; - return total; -}; -function sum_set (s : set) { - let total : int = 0; - for (const i of s) total = total + i; - return total; -}; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/loops/gcd-loop.mligo b/gitlab-pages/docs/language-basics/src/loops/gcd-loop.mligo deleted file mode 100644 index 5ebf987968..0000000000 --- a/gitlab-pages/docs/language-basics/src/loops/gcd-loop.mligo +++ /dev/null @@ -1,16 +0,0 @@ -let gcd (a, b : nat * nat) = - let mut x, y = a, b in // we will modify x and y - let () = - if x < y then - let z = x in - begin - x := y; y := z - end in - let mut r : nat = 0n in - let () = - while y <> 0n do - r := x mod y; - x := y; - y := r - done - in x \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/loops/gcd.jsligo b/gitlab-pages/docs/language-basics/src/loops/gcd.jsligo deleted file mode 100644 index 9c3d1b0724..0000000000 --- a/gitlab-pages/docs/language-basics/src/loops/gcd.jsligo +++ /dev/null @@ -1,21 +0,0 @@ -function gcd (a: nat, b: nat) { - let [x,y] = [a,b]; // we will modify x and y - if (x < y) { - const z = x; - x = y; y = z; - } - let r: nat = 0n - while (y != 0n) { - r = x % y; - x = y; - y = r; - } - return x; -}; -function iter (x: nat,y: nat): nat { - if (y == 0n) return x else return iter (y, x % y) -}; - -function gcd2 (x: nat,y: nat) : nat { - if (x < y) return iter (y, x) else return iter (x, y) -}; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/loops/gcd.mligo b/gitlab-pages/docs/language-basics/src/loops/gcd.mligo deleted file mode 100644 index ae92c67802..0000000000 --- a/gitlab-pages/docs/language-basics/src/loops/gcd.mligo +++ /dev/null @@ -1,6 +0,0 @@ -let rec iter (x, y : nat * nat) : nat = - if y = 0n then x else iter (y, x mod y) - -let gcd (x, y : nat * nat) : nat = - let x, y = if x < y then y,x else x,y - in iter (x, y) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/loops/ungrouped.jsligo b/gitlab-pages/docs/language-basics/src/loops/ungrouped.jsligo deleted file mode 100644 index beb3035599..0000000000 --- a/gitlab-pages/docs/language-basics/src/loops/ungrouped.jsligo +++ /dev/null @@ -1,10 +0,0 @@ -function sum_map (m: map) { - let string_total = "" - let int_total = 0 - for (const item of m) { - let [key, value] = item; - string_total = string_total + key; - int_total = int_total + value - } - return [string_total, int_total] -} \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/big_maps.jsligo b/gitlab-pages/docs/language-basics/src/maps-records/big_maps.jsligo deleted file mode 100644 index 310f1f3e8d..0000000000 --- a/gitlab-pages/docs/language-basics/src/maps-records/big_maps.jsligo +++ /dev/null @@ -1,14 +0,0 @@ -type move = [int, int]; -type register = big_map; -const empty: register = Big_map.empty; -const moves : register = - Big_map.literal([ - ["tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" as address, [1, 2]], - ["tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, [0, 3]]]); -const my_balance: option = - Big_map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, moves); -const updated_map: register = - Big_map.update - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, Some([4, 9]), moves); -const updated_map_: register = - Big_map.remove("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, moves); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/big_maps.mligo b/gitlab-pages/docs/language-basics/src/maps-records/big_maps.mligo deleted file mode 100644 index 52d9299ba9..0000000000 --- a/gitlab-pages/docs/language-basics/src/maps-records/big_maps.mligo +++ /dev/null @@ -1,14 +0,0 @@ -type move = int * int -type register = (address, move) big_map -let empty : register = Big_map.empty -let moves : register = - Big_map.literal [ - (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), (1,2)); - (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (0,3))] -let my_balance : move option = - Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves -let updated_map : register = - Big_map.update - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (Some (4,9)) moves -let updated_map : register = - Big_map.remove ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/maps.jsligo b/gitlab-pages/docs/language-basics/src/maps-records/maps.jsligo deleted file mode 100644 index 9c99dbc974..0000000000 --- a/gitlab-pages/docs/language-basics/src/maps-records/maps.jsligo +++ /dev/null @@ -1,35 +0,0 @@ -type move = [int, int]; -type register = map; -const empty: register = Map.empty; -const moves : register = - Map.literal ([ - ["tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" as address, [1,2]], - ["tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, [0,3]]]); -const my_balance: option = - Map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, moves); -let force_access = (key: address, moves: register) => { - return match(Map.find_opt (key, moves)) { - when(Some(move)): move; - when(None()): failwith("No move.") - }; -}; -const assign = (m: register) => - Map.update - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, Some ([4, 9]), m); -const add = (m: register) => - Map.add - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" as address, [4, 9], m); -const delete = (key: address, moves: register) => - Map.remove(key, moves); -const assert_all_greater_than_three = (m: register) => { - let predicate = ([i, j]: [address, move]) => assert(j[0] > 3); - Map.iter(predicate, m); -}; -const map_op = (m: register) => { - let increment = ([_a, j]: [address, move]) => [j[0], j[1] + 1]; - return Map.map(increment, m); -}; -const fold_op = (m: register): int => { - let folded = ([i, j]: [int, [address, move]]) => i + j[1][1]; - return Map.fold(folded, m, 5); -}; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/maps.mligo b/gitlab-pages/docs/language-basics/src/maps-records/maps.mligo deleted file mode 100644 index 5b314067ed..0000000000 --- a/gitlab-pages/docs/language-basics/src/maps-records/maps.mligo +++ /dev/null @@ -1,30 +0,0 @@ -type move = int * int -type register = (address, move) map -let empty : register = Map.empty -let moves : register = - Map.literal [ - (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), (1,2)); - (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (0,3))] -let my_balance : move option = - Map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves -let force_access (key, moves : address * register) : move = - match Map.find_opt key moves with - Some move -> move - | None -> failwith "No move." -let assign (m : register) : register = - Map.update - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (Some (4,9)) m -let add (m : register) : register = - Map.add - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (4,9) m -let delete (key, moves : address * register) : register = - Map.remove key moves -let iter_op (m : register) : unit = - let predicate = fun (i,j : address * move) -> assert (j.0 > 3) - in Map.iter predicate m -let map_op (m : register) : register = - let increment = fun (_,j : address * move) -> j.0, j.1 + 1 - in Map.map increment m -let fold_op (m : register) : int = - let folded = fun (i,j : int * (address * move)) -> i + j.1.1 - in Map.fold folded m 5 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/record_nested_update.jsligo b/gitlab-pages/docs/language-basics/src/maps-records/record_nested_update.jsligo deleted file mode 100644 index 8bc1064b16..0000000000 --- a/gitlab-pages/docs/language-basics/src/maps-records/record_nested_update.jsligo +++ /dev/null @@ -1,13 +0,0 @@ -type color = ["Blue"] | ["Green"]; - -type preferences = { - color : color, - other : int -}; - -type account = { - id : int, - preferences : preferences -}; -const change_color_preference = (account : account, color : color) => - ({ ...account, preferences: {...account.preferences, color: color }}); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/record_nested_update.mligo b/gitlab-pages/docs/language-basics/src/maps-records/record_nested_update.mligo deleted file mode 100644 index 84c252c117..0000000000 --- a/gitlab-pages/docs/language-basics/src/maps-records/record_nested_update.mligo +++ /dev/null @@ -1,13 +0,0 @@ -type color = Blue | Green - -type preferences = { - color : color; - other : int -} - -type account = { - id : int; - preferences : preferences -} -let change_color_preference (account : account) (color : color) : account = - { account with preferences.color = color } \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/record_update.jsligo b/gitlab-pages/docs/language-basics/src/maps-records/record_update.jsligo deleted file mode 100644 index 05e0f16271..0000000000 --- a/gitlab-pages/docs/language-basics/src/maps-records/record_update.jsligo +++ /dev/null @@ -1,7 +0,0 @@ -type point = {x: int, y: int, z: int} -type vector = {dx: int, dy: int} - -const origin = {x: 0, y: 0, z: 0}; - -const xy_translate = (p: point, vec: vector) => - ({...p, x: p.x + vec.dx, y: p.y + vec.dy}); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/record_update.mligo b/gitlab-pages/docs/language-basics/src/maps-records/record_update.mligo deleted file mode 100644 index d31c3f61f8..0000000000 --- a/gitlab-pages/docs/language-basics/src/maps-records/record_update.mligo +++ /dev/null @@ -1,7 +0,0 @@ -type point = {x : int; y : int; z : int} -type vector = {dx : int; dy : int} - -let origin : point = {x = 0; y = 0; z = 0} - -let xy_translate (p, vec : point * vector) : point = - {p with x = p.x + vec.dx; y = p.y + vec.dy} \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/records1.jsligo b/gitlab-pages/docs/language-basics/src/maps-records/records1.jsligo deleted file mode 100644 index ff6550d52b..0000000000 --- a/gitlab-pages/docs/language-basics/src/maps-records/records1.jsligo +++ /dev/null @@ -1,22 +0,0 @@ -type user = { - id : nat, - is_admin : bool, - name : string -}; -const alice : user = { - id : 1n, - is_admin : true, - name : "Alice" -}; -const alice_admin = alice.is_admin; -function userToTuple (u : user) { - let { id, is_admin, name } = u; - return [id, is_admin, name]; -} -function getId (u : user) { - let { id, is_admin, name } = u; - /* we don't use `is_admin` and `name` - so prevent warning with `ignore` */ - ignore([is_admin, name]); - return id -} \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/records1.mligo b/gitlab-pages/docs/language-basics/src/maps-records/records1.mligo deleted file mode 100644 index 408e7bf43d..0000000000 --- a/gitlab-pages/docs/language-basics/src/maps-records/records1.mligo +++ /dev/null @@ -1,17 +0,0 @@ -type user = { - id : nat; - is_admin : bool; - name : string -} -let alice : user = { - id = 1n; - is_admin = true; - name = "Alice" -} -let alice_admin : bool = alice.is_admin -let user_to_tuple (u : user) = - let { id ; is_admin ; name } = u in - (id, is_admin, name) -let get_id (u : user) = - let { id ; is_admin = _ ; name = _ } = u in - id \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/records2.jsligo b/gitlab-pages/docs/language-basics/src/maps-records/records2.jsligo deleted file mode 100644 index 05e0f16271..0000000000 --- a/gitlab-pages/docs/language-basics/src/maps-records/records2.jsligo +++ /dev/null @@ -1,7 +0,0 @@ -type point = {x: int, y: int, z: int} -type vector = {dx: int, dy: int} - -const origin = {x: 0, y: 0, z: 0}; - -const xy_translate = (p: point, vec: vector) => - ({...p, x: p.x + vec.dx, y: p.y + vec.dy}); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/records2.mligo b/gitlab-pages/docs/language-basics/src/maps-records/records2.mligo deleted file mode 100644 index d31c3f61f8..0000000000 --- a/gitlab-pages/docs/language-basics/src/maps-records/records2.mligo +++ /dev/null @@ -1,7 +0,0 @@ -type point = {x : int; y : int; z : int} -type vector = {dx : int; dy : int} - -let origin : point = {x = 0; y = 0; z = 0} - -let xy_translate (p, vec : point * vector) : point = - {p with x = p.x + vec.dx; y = p.y + vec.dy} \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/ungrouped.jsligo b/gitlab-pages/docs/language-basics/src/maps-records/ungrouped.jsligo deleted file mode 100644 index 8bc1064b16..0000000000 --- a/gitlab-pages/docs/language-basics/src/maps-records/ungrouped.jsligo +++ /dev/null @@ -1,13 +0,0 @@ -type color = ["Blue"] | ["Green"]; - -type preferences = { - color : color, - other : int -}; - -type account = { - id : int, - preferences : preferences -}; -const change_color_preference = (account : account, color : color) => - ({ ...account, preferences: {...account.preferences, color: color }}); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/maps-records/ungrouped.mligo b/gitlab-pages/docs/language-basics/src/maps-records/ungrouped.mligo deleted file mode 100644 index 84c252c117..0000000000 --- a/gitlab-pages/docs/language-basics/src/maps-records/ungrouped.mligo +++ /dev/null @@ -1,13 +0,0 @@ -type color = Blue | Green - -type preferences = { - color : color; - other : int -} - -type account = { - id : int; - preferences : preferences -} -let change_color_preference (account : account) (color : color) : account = - { account with preferences.color = color } \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/a.jsligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/a.jsligo deleted file mode 100644 index d2280c6e0d..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/a.jsligo +++ /dev/null @@ -1,19 +0,0 @@ -// int + int yields int -const a = 5 + 10; - -// nat + int yields int -const b = 5n + 10; - -// tez + tez yields tez -const c: tez = 5mutez + 1tez; - -// tez + int or tez + nat is invalid: -// const d : tez = 5mutez + 10n; - -// two nats yield a nat -const e: nat = 5n + 10n; - -// nat + int yields an int: invalid -// const f : nat = 5n + 10; - -const g = 1_000_000; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/a.mligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/a.mligo deleted file mode 100644 index 8c3c5e539c..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/a.mligo +++ /dev/null @@ -1,19 +0,0 @@ -// int + int yields int -let a : int = 5 + 10 - -// nat + int yields int -let b : int = 5n + 10 - -// tez + tez yields tez -let c : tez = 5mutez + 0.000_010tez - -// tez + int or tez + nat is invalid -// let d : tez = 5mutez + 10n - -// two nats yield a nat -let e : nat = 5n + 10n - -// nat + int yields an int: invalid -// let f : nat = 5n + 10 - -let g : int = 1_000_000 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/b.jsligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/b.jsligo deleted file mode 100644 index ccbb8ff5b4..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/b.jsligo +++ /dev/null @@ -1,9 +0,0 @@ -const a = 5 - 10; - -// Subtraction of two nats yields an int -const b: int = 5n - 2n; - -// Therefore the following is invalid -// const c : nat = 5n - 2n; -const d : option = 5mutez - 1mutez; /* Some (4mutez) */ -const e : option = 1mutez - 5mutez; /* None */ \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/b.mligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/b.mligo deleted file mode 100644 index 003a6c84f9..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/b.mligo +++ /dev/null @@ -1,9 +0,0 @@ -let a : int = 5 - 10 - -// Subtraction of two nats yields an int -let b : int = 5n - 2n - -// Therefore the following is invalid -// let c : nat = 5n - 2n -let d : tez option = 5mutez - 1mutez (* Some (4mutez) *) -let e : tez option = 1mutez - 5mutez (* None *) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/c.jsligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/c.jsligo deleted file mode 100644 index 7f0e105306..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/c.jsligo +++ /dev/null @@ -1,5 +0,0 @@ -const a = 5 * 5; -const b: nat = 5n * 5n; - -// You can also multiply `nat` and `tez` -const c: tez = 5n * 5mutez; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/c.mligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/c.mligo deleted file mode 100644 index 7cb95f2541..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/c.mligo +++ /dev/null @@ -1,5 +0,0 @@ -let a : int = 5 * 5 -let b : nat = 5n * 5n - -// You can also multiply `nat` and `tez` -let c : tez = 5n * 5mutez \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/d.jsligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/d.jsligo deleted file mode 100644 index 24a6ee1059..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/d.jsligo +++ /dev/null @@ -1,3 +0,0 @@ -const a: int = 10 / 3; -const b: nat = 10n / 3n; -const c: nat = 10mutez / 3mutez; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/d.mligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/d.mligo deleted file mode 100644 index c2179d94c5..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/d.mligo +++ /dev/null @@ -1,11 +0,0 @@ -let a : int = 10 / 3 -let b : nat = 10n / 3n -let c : nat = 10mutez / 3mutez -let a : int = 120 -let b : int = 9 -let rem1 : nat = a mod b // 3 -let c : nat = 120n -let rem2 : nat = c mod b // 3 -let d : nat = 9n -let rem3 : nat = c mod d // 3 -let rem4 : nat = a mod d // 3 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/decrement_ops.jsligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/decrement_ops.jsligo deleted file mode 100644 index a75e8822d5..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/decrement_ops.jsligo +++ /dev/null @@ -1,12 +0,0 @@ - -const testDec = (() => { - let v = 10; - - // Prefix decrement operator - assert(--v == 9); - assert(v == 9); - - // Postfix decrement operator - assert(v-- == 9); - assert(v == 8); -})(); diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/e.jsligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/e.jsligo deleted file mode 100644 index 285daea415..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/e.jsligo +++ /dev/null @@ -1,8 +0,0 @@ -const a = 120; -const b = 9; -const rem1 = a % b; // 3 -const c = 120n; -const rem2 = c % b; // 3 -const d = 9n; -const rem3 = c % d; // 3 -const rem4 = a % d; // 3 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/e.mligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/e.mligo deleted file mode 100644 index 52baa8560e..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/e.mligo +++ /dev/null @@ -1,3 +0,0 @@ -let a : int = int (1n) -let b : nat = abs (1) -let is_a_nat : nat option = is_nat (1) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/f.jsligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/f.jsligo deleted file mode 100644 index d4b55cc111..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/f.jsligo +++ /dev/null @@ -1,8 +0,0 @@ -const a = 37; -const b = 5; -const ediv1 : option<[int , nat]> = ediv(a, b); // Some (7, 2) -const c = 37n; -const ediv2: option<[int , nat]> = ediv(c, b); // Some (7, 2) -const d = 5n; -const ediv3: option<[nat , nat]> = ediv(c, d); // Some (7, 2) -const ediv4: option<[int , nat]> = ediv(a, d); // Some (7, 2) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/f.mligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/f.mligo deleted file mode 100644 index 90d37a3788..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/f.mligo +++ /dev/null @@ -1,8 +0,0 @@ -let a : int = 37 -let b : int = 5 -let ediv1 : (int * nat) option = ediv a b // Some (7, 2) -let c : nat = 37n -let ediv2 : (int * nat) option = ediv c b // Some (7, 2) -let d : nat = 5n -let ediv3 : (nat * nat) option = ediv c d // Some (7, 2) -let ediv4 : (int * nat) option = ediv a d // Some (7, 2) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/g.jsligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/g.jsligo deleted file mode 100644 index 36cae7f048..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/g.jsligo +++ /dev/null @@ -1,2 +0,0 @@ -const a = int(1n); -const b = abs(1); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/h.jsligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/h.jsligo deleted file mode 100644 index 0822edd5cf..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/h.jsligo +++ /dev/null @@ -1 +0,0 @@ -const is_a_nat = is_nat(1); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/math-numbers-tez/increment_ops.jsligo b/gitlab-pages/docs/language-basics/src/math-numbers-tez/increment_ops.jsligo deleted file mode 100644 index b8d947e4ef..0000000000 --- a/gitlab-pages/docs/language-basics/src/math-numbers-tez/increment_ops.jsligo +++ /dev/null @@ -1,12 +0,0 @@ - -const testInc = (() => { - let inc = 0; - - // Prefix increment operator - assert(++inc == 1); - assert(inc == 1); - - // Postfix increment operator - assert(inc++ == 1); - assert(inc == 2); -})(); diff --git a/gitlab-pages/docs/language-basics/src/modules/EURO.jsligo b/gitlab-pages/docs/language-basics/src/modules/EURO.jsligo deleted file mode 100644 index 4f5f8eb3b1..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/EURO.jsligo +++ /dev/null @@ -1,12 +0,0 @@ -namespace EURO { - export type t = nat; - export const add = (a: t, b: t) : t => a + b; - export const zero: t = 0n; - export const one: t = 1n -} -type storage = EURO.t; - -@entry -let main = (_action: unit, store: storage): [list, storage] => - [[], EURO.add (store, EURO.one)]; -import US_DOLLAR = EURO; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/modules/EURO.mligo b/gitlab-pages/docs/language-basics/src/modules/EURO.mligo deleted file mode 100644 index 5a60b16965..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/EURO.mligo +++ /dev/null @@ -1,13 +0,0 @@ -module EURO = - struct - type t = nat - let add (a , b : t * t) : t = a + b - let zero : t = 0n - let one : t = 1n - end -type storage = EURO.t - -[@entry] -let main (_action : unit) (store : storage) : operation list * storage = - [], EURO.add (store, EURO.one) -module US_DOLLAR = EURO \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/modules/EURO2.jsligo b/gitlab-pages/docs/language-basics/src/modules/EURO2.jsligo deleted file mode 100644 index a09c8e13a1..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/EURO2.jsligo +++ /dev/null @@ -1,6 +0,0 @@ -namespace EURO { - export type t = int; - export const add = (a: t, b: t) : t => a + b; - export const zero: t = 0; - export const one: t = 1; -} \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/modules/EURO2.mligo b/gitlab-pages/docs/language-basics/src/modules/EURO2.mligo deleted file mode 100644 index 1d6d0f0b6e..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/EURO2.mligo +++ /dev/null @@ -1,7 +0,0 @@ -module EURO = - struct - type t = int - let add (a, b : t * t) : t = a + b - let zero : t = 0 - let one : t = 1 - end \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/modules/EURO3.jsligo b/gitlab-pages/docs/language-basics/src/modules/EURO3.jsligo deleted file mode 100644 index fa7681e0af..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/EURO3.jsligo +++ /dev/null @@ -1,15 +0,0 @@ -namespace EURO { - export type t = nat; - - export let add = (a: t, b: t): t => a + b; - - export namespace CONST { - export let zero: t = 0n; - export let one: t = 1n; - }; -}; -type storage = EURO.t; - -@entry -let main = (_action: unit, store: storage) : [list, storage] => - [[], EURO.add (store, EURO.CONST.one)] \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/modules/EURO3.mligo b/gitlab-pages/docs/language-basics/src/modules/EURO3.mligo deleted file mode 100644 index e73c66a997..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/EURO3.mligo +++ /dev/null @@ -1,17 +0,0 @@ -module EURO = - struct - type t = nat - - let add (a, b : t * t) : t = a + b - - module CONST = - struct - let zero : t = 0n - let one : t = 1n - end - end -type storage = EURO.t - -[@entry] -let main (_action : unit) (store : storage) : operation list * storage = - [], EURO.add (store, EURO.CONST.one) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/modules/contract.jsligo b/gitlab-pages/docs/language-basics/src/modules/contract.jsligo deleted file mode 100644 index 971c6ed183..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/contract.jsligo +++ /dev/null @@ -1,11 +0,0 @@ -namespace C { - @entry - const increment = (p : int, s : int) : [list, int] => [[], s + p]; - @entry - const decrement = (p : int, s : int) : [list, int] => [[], s - p]; -}; -const test = do { - let orig = Test.originate(contract_of(C), 0, 0tez); - Test.transfer_exn(orig.addr, (Increment (42)), 1mutez); - return assert(Test.get_storage(orig.addr) == 42); -}; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/modules/contract.mligo b/gitlab-pages/docs/language-basics/src/modules/contract.mligo deleted file mode 100644 index d0ccb8f592..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/contract.mligo +++ /dev/null @@ -1,17 +0,0 @@ -module C = struct - [@entry] let increment (p : int) (s : int) : operation list * int = [], s + p - [@entry] let decrement (p : int) (s : int) : operation list * int = [], s - p -end -let test = - let orig = Test.originate (contract_of C) 0 0tez in - let _ = Test.transfer_exn orig.addr (Increment 42) 0tez - in assert (42 = Test.get_storage orig.addr) -module FA0 = struct - type t = unit - [@entry] let transfer (_ : unit) (_ : t) : operation list * t = [], () -end - -module FA0Ext = struct - include FA0 - [@entry] let transfer2 (a : unit) (b : t) = transfer a b -end \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/modules/contract2.jsligo b/gitlab-pages/docs/language-basics/src/modules/contract2.jsligo deleted file mode 100644 index 1687ee22e8..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/contract2.jsligo +++ /dev/null @@ -1,47 +0,0 @@ -interface FA0_INTF { - type storage; - @entry const add : (s : int, k : storage) => [list, storage]; -} -namespace FA0 implements FA0_INTF { - export type storage = int; - @entry const add = (s : int, k : int) : [list, int] => [[], s + k]; - @entry const extra = (s : int, k : int) : [list, int] => [[], s - k]; -} -interface FABase_INTF { - type t; -}; - -interface FA0_INTF extends FABase_INTF { - @entry const transfer : (_u : unit, s : t) => [list, t]; -}; - -interface FA0Ext_INTF extends FA0_INTF { - @entry const transfer1 : (_u : unit, s : t) => [list, t]; -}; - -interface FA1_INTF extends FABase_INTF { - @entry const transfer2 : (_u : unit, s : t) => [list, t]; -}; -interface FAAll_INTF extends FA0Ext_INTF, FA1_INTF { - @entry const transfer3 : (_u : unit, s : t) => [list, t]; - @view const v1 : (_u : unit, s : t) => t; - @entry const opt_val? : (i : int, s : t) => [list, t]; -} -namespace FAAll_wo_opt_val implements FAAll_INTF { - export type t = int; - - @entry const transfer = (_u : unit, s : t) : [list, t] => [[], s]; - @entry const transfer1 = (_u : unit, s : t) : [list, t] => [[], s]; - @entry const transfer2 = (_u : unit, s : t) : [list, t] => [[], s]; - @entry const transfer3 = (_u : unit, s : t) : [list, t] => [[], s]; - @view const v1 = (_u : unit, s : t) : t => s; - - /* "foo", "transfer4" and "v2" are not in "FAAll_INTF", but can - nevertheless be added here, because "implements" does not filter, - but only have the compiler check that the fields in the interface - are implemented. */ - - export const foo = (s : t) : t => s; - @entry const transfer4 = (_u : unit, s : t) : [list, t] => [[], s]; - @view const v2 = (_u : unit, s : t) : t => s; -} diff --git a/gitlab-pages/docs/language-basics/src/modules/contract2.mligo b/gitlab-pages/docs/language-basics/src/modules/contract2.mligo deleted file mode 100644 index a953573c61..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/contract2.mligo +++ /dev/null @@ -1,18 +0,0 @@ -module type FA0_SIG = sig - type t - [@entry] val transfer : unit -> t -> operation list * t -end - -module type FA0Ext_SIG = sig - include FA0_SIG - [@entry] val transfer2 : unit -> t -> operation list * t -end -module FA0 : FA0_SIG = struct - type t = unit - [@entry] let transfer (_ : unit) (_ : t) : operation list * t = [], () -end - -module FA0Ext : FA0Ext_SIG = struct - include FA0 - [@entry] let transfer2 (a : unit) (b : t) = transfer a b -end \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/modules/imported.jsligo b/gitlab-pages/docs/language-basics/src/modules/imported.jsligo deleted file mode 100644 index cf75178b7e..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/imported.jsligo +++ /dev/null @@ -1,6 +0,0 @@ -export type t = nat; - -export const add = (a: t, b: t): t => a + b; - -export const zero: t = 0n; -export const one: t = 1n; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/modules/imported.mligo b/gitlab-pages/docs/language-basics/src/modules/imported.mligo deleted file mode 100644 index 875455c533..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/imported.mligo +++ /dev/null @@ -1,6 +0,0 @@ -type t = nat - -let add (a , b : t * t) : t = a + b - -let zero : t = 0n -let one : t = 1n \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/modules/importer.jsligo b/gitlab-pages/docs/language-basics/src/modules/importer.jsligo deleted file mode 100644 index b8f64e00c1..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/importer.jsligo +++ /dev/null @@ -1,7 +0,0 @@ -#import "gitlab-pages/docs/language-basics/src/modules/imported.jsligo" "EURO" - -type storage = EURO.t; - -@entry -const main = (_action: unit, store: storage): [list, storage] => - [[], EURO.add(store, EURO.one)]; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/modules/importer.mligo b/gitlab-pages/docs/language-basics/src/modules/importer.mligo deleted file mode 100644 index 4cd07d39b0..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/importer.mligo +++ /dev/null @@ -1,7 +0,0 @@ -#import "gitlab-pages/docs/language-basics/src/modules/imported.mligo" "EURO" - -type storage = EURO.t - -[@entry] -let main (_action : unit) (store : storage) : operation list * storage = - ([], EURO.add(store, EURO.one)) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/modules/ungrouped.jsligo b/gitlab-pages/docs/language-basics/src/modules/ungrouped.jsligo deleted file mode 100644 index 8ac5908da2..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/ungrouped.jsligo +++ /dev/null @@ -1,7 +0,0 @@ -#import "./gitlab-pages/docs/language-basics/src/modules/imported.jsligo" "EURO" - -type storage = EURO.t; - -@entry -const main = (_action: unit, store: storage): [list, storage] => - [[], EURO.add(store, EURO.one)]; diff --git a/gitlab-pages/docs/language-basics/src/modules/ungrouped.mligo b/gitlab-pages/docs/language-basics/src/modules/ungrouped.mligo deleted file mode 100644 index 4a3b77605c..0000000000 --- a/gitlab-pages/docs/language-basics/src/modules/ungrouped.mligo +++ /dev/null @@ -1,7 +0,0 @@ -#import "./gitlab-pages/docs/language-basics/src/modules/imported.mligo" "EURO" - -type storage = EURO.t - -[@entry] -let main (_action : unit) (store : storage) : operation list * storage = - ([], EURO.add(store, EURO.one)) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/lists.jsligo b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/lists.jsligo deleted file mode 100644 index d0f2db09eb..0000000000 --- a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/lists.jsligo +++ /dev/null @@ -1,15 +0,0 @@ -const empty_list : list = []; -const my_list : list = [1, 2, 2]; // The head is 1, the tail is [2, 2] -const larger_list : list = [5, ...my_list]; // [5,1,2,2] -const head: option = List.head_opt(my_list); // 1 -const tail: option> = List.tail_opt(my_list); // [2,2] -const assert_all_greater_than_three = (l: list): unit => { - let predicate = i => assert(i > 3); - List.iter(predicate, l); -}; -const increment = i => i + 1; - -// Creates a new list with all elements incremented by 1 -const plus_one: list = List.map(increment, larger_list); // [6,2,3,3] -const sum = ([result, i]: [int, int]) => result + i; -const sum_of_elements: int = List.fold (sum, my_list, 0); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/lists.mligo b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/lists.mligo deleted file mode 100644 index 30dc9b86f0..0000000000 --- a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/lists.mligo +++ /dev/null @@ -1,14 +0,0 @@ -let empty_list : int list = [] -let my_list : int list = [1; 2; 2] (* The head is 1, the tail is [2; 2] *) -let larger_list : int list = 5 :: my_list (* [5;1;2;2] *) -let head : int option = List.head_opt my_list (* 1 *) -let tail : int list option = List.tail_opt my_list (* [2;2] *) -let assert_all_greater_than_three (l : int list) : unit = - let predicate = fun (i:int) -> assert (i > 3) - in List.iter predicate l -let increment (i : int) = i + 1 - -// Creates a new list with all elements incremented by 1 -let plus_one : int list = List.map increment larger_list (* [6,2,3,3] *) -let sum (acc, i: int * int) = acc + i -let sum_of_elements : int = List.fold_left sum 0 my_list \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.jsligo b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.jsligo deleted file mode 100644 index a861cceb9d..0000000000 --- a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.jsligo +++ /dev/null @@ -1,13 +0,0 @@ -const my_empty_set: set = Set.empty; -let my_set: set = Set.literal([3, 2, 2, 1]); -const with_999: set = Set.add(999, my_set); -const contains_3: bool = Set.mem(3, my_set); -const cardinal: nat = Set.size(my_set); -const larger_set: set = Set.add(4, my_set); -const smaller_set: set = Set.remove(3, my_set); -const assert_all_greater_than_three = s => { - let predicate = i => assert(i > 3); - Set.iter(predicate, s); -}; -const sum = ([acc, i]: [int, int]) => acc + i; -const sum_of_elements = Set.fold (sum, my_set, 0); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.mligo b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.mligo deleted file mode 100644 index 7bb16c4990..0000000000 --- a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.mligo +++ /dev/null @@ -1,12 +0,0 @@ -let my_set : int set = Set.empty -let my_set : int set = Set.literal [3; 2; 2; 1] -let with_999 : int set = Set.add 999 my_set -let contains_3 : bool = Set.mem 3 my_set -let cardinal : nat = Set.size my_set -let larger_set : int set = Set.add 4 my_set -let smaller_set : int set = Set.remove 3 my_set -let assert_all_greater_than_three (s : int set) : unit = - let predicate = fun (i : int) -> assert (i > 3) - in Set.iter predicate s -let sum (acc, i : int * int) : int = acc + i -let sum_of_elements : int = Set.fold sum my_set 0 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/tuple.jsligo b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/tuple.jsligo deleted file mode 100644 index c35537616a..0000000000 --- a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/tuple.jsligo +++ /dev/null @@ -1,15 +0,0 @@ -type two_people = [string, string]; // Alias - -const friends: two_people = ["Alice", "Johnson"]; -let [person_a, person_b] = friends; -let first_person_fun = ([person_a, _person_b]: two_people) => person_a; -let alice = first_person_fun(friends); -let destruct_tuple = (x : [ int , [int , nat] ]) : nat => { - let [a,[b,c]] = x ; - return c -}; -let destruct_record = (x : { a : int , b : string }) : int => { - let { a , b } = x ; - return a -}; -const first_name_component = friends[0]; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/tuple.mligo b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/tuple.mligo deleted file mode 100644 index c81f26e48a..0000000000 --- a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/tuple.mligo +++ /dev/null @@ -1,7 +0,0 @@ -type two_people = string * string // Alias - -let friends : two_people = ("Alice", "Johnson") // Optional parentheses -let (person_a, person_b) : two_people = friends -let first_person ((person_a, _): two_people) = person_a -let alice = first_person friends -let first_name : string = friends.0 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/a.jsligo b/gitlab-pages/docs/language-basics/src/strings-bytes/a.jsligo deleted file mode 100644 index e18f48d075..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/a.jsligo +++ /dev/null @@ -1,3 +0,0 @@ -const name = "Alice"; -const greeting = "Hello"; -const full_greeting = greeting + " " + name; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/a.mligo b/gitlab-pages/docs/language-basics/src/strings-bytes/a.mligo deleted file mode 100644 index f2407cf5c8..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/a.mligo +++ /dev/null @@ -1,3 +0,0 @@ -let name : string = "Alice" -let greeting : string = "Hello" -let full_greeting : string = greeting ^ " " ^ name \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/b.jsligo b/gitlab-pages/docs/language-basics/src/strings-bytes/b.jsligo deleted file mode 100644 index fbef99f207..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/b.jsligo +++ /dev/null @@ -1,2 +0,0 @@ -const name = "Alice"; -const slice = String.sub (0n, 1n, name); // slice == "A" \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/b.mligo b/gitlab-pages/docs/language-basics/src/strings-bytes/b.mligo deleted file mode 100644 index 9f2c87682c..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/b.mligo +++ /dev/null @@ -1,2 +0,0 @@ -let name : string = "Alice" -let slice : string = String.sub 0n 1n name (* slice = "A" *) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/c.jsligo b/gitlab-pages/docs/language-basics/src/strings-bytes/c.jsligo deleted file mode 100644 index 38ab59bc49..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/c.jsligo +++ /dev/null @@ -1,2 +0,0 @@ -const name = "Alice"; -const length = String.length(name); // length == 5 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/c.mligo b/gitlab-pages/docs/language-basics/src/strings-bytes/c.mligo deleted file mode 100644 index 3d7abfdf9d..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/c.mligo +++ /dev/null @@ -1,2 +0,0 @@ -let name : string = "Alice" -let length : nat = String.length name // length = 5 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/d.jsligo b/gitlab-pages/docs/language-basics/src/strings-bytes/d.jsligo deleted file mode 100644 index 7ca49cfb6a..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/d.jsligo +++ /dev/null @@ -1,3 +0,0 @@ -const white = 0xffff; -const black = 0x0000; -const pixels = Bytes.concat(white, black); // 0xffff0000 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/d.mligo b/gitlab-pages/docs/language-basics/src/strings-bytes/d.mligo deleted file mode 100644 index 617ff4bc15..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/d.mligo +++ /dev/null @@ -1,3 +0,0 @@ -let white : bytes = 0xffff -let black : bytes = 0x0000 -let pixels : bytes = Bytes.concat white black (* 0xffff0000 *) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/e.jsligo b/gitlab-pages/docs/language-basics/src/strings-bytes/e.jsligo deleted file mode 100644 index 86f9b8b13d..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/e.jsligo +++ /dev/null @@ -1,2 +0,0 @@ -const b = 0x12345678; -const slice = Bytes.sub (1n, 2n, b); // 0x3456 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/e.mligo b/gitlab-pages/docs/language-basics/src/strings-bytes/e.mligo deleted file mode 100644 index b9a3cf1402..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/e.mligo +++ /dev/null @@ -1,2 +0,0 @@ -let b : bytes = 0x12345678 -let slice : bytes = Bytes.sub 1n 2n b (* 0x3456 *) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/f.jsligo b/gitlab-pages/docs/language-basics/src/strings-bytes/f.jsligo deleted file mode 100644 index 7cf74df3bf..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/f.jsligo +++ /dev/null @@ -1,2 +0,0 @@ -const b = 0x123456; -const length = Bytes.length(b); // length = 3 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/f.mligo b/gitlab-pages/docs/language-basics/src/strings-bytes/f.mligo deleted file mode 100644 index bd991bed45..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/f.mligo +++ /dev/null @@ -1,2 +0,0 @@ -let b : bytes = 0x123456 -let length : nat = Bytes.length b (* length = 3 *) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/g.jsligo b/gitlab-pages/docs/language-basics/src/strings-bytes/g.jsligo deleted file mode 100644 index 06a07a5413..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/g.jsligo +++ /dev/null @@ -1,14 +0,0 @@ -/* Bitwise and */ -const b_and = 0x0005 & 0x0106; // 0x0004 - -/* Bitwise or */ -const b_or = 0x0005 | 0x0106; // 0x0107 - -/* Bitwise xor */ -const b_xor = 0x0005 ^ 0x0106; // 0x0103 - -/* Bitwise shift left */ -const b_shift_left = 0x06 << 8n; // 0x0600 - -/* Bitwise shift right */ -const b_shift_right = 0x0006 >> 1n; // 0x0003 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/g.mligo b/gitlab-pages/docs/language-basics/src/strings-bytes/g.mligo deleted file mode 100644 index ed1b05c0a4..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/g.mligo +++ /dev/null @@ -1,14 +0,0 @@ -(* Bitwise and *) -let b_and = 0x0005 land 0x0106 (* 0x0004 *) - -(* Bitwise or *) -let b_or = 0x0005 lor 0x0106 (* 0x0107 *) - -(* Bitwise xor *) -let b_xor = 0x0005 lxor 0x0106 (* 0x0103 *) - -(* Bitwise shift left *) -let b_shift_left = 0x06 lsl 8n (* 0x0600 *) - -(* Bitwise shift right *) -let b_shift_right = 0x0006 lsr 1n (* 0x0003 *) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/h.jsligo b/gitlab-pages/docs/language-basics/src/strings-bytes/h.jsligo deleted file mode 100644 index 4931c628c6..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/h.jsligo +++ /dev/null @@ -1,10 +0,0 @@ -/* bytes -> nat */ -const test_bytes_nat = nat(0x1234) // 1234n - -/* nat -> bytes */ -const test_nat_bytes = bytes(4660n) // 0x1234 -/* bytes -> int */ -const test_bytes_int = int(0x1234) // 4660 - -/* int -> bytes */ -const test_int_bytes = bytes(4660) // 0x1234 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/h.mligo b/gitlab-pages/docs/language-basics/src/strings-bytes/h.mligo deleted file mode 100644 index 4ad9c1daee..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/h.mligo +++ /dev/null @@ -1,10 +0,0 @@ -(* bytes -> nat *) -let test_bytes_nat = nat 0x1234 (* 1234n *) - -(* nat -> bytes *) -let test_nat_bytes = bytes 4660n (* 0x1234 *) -(* bytes -> int *) -let test_bytes_int = int 0x1234 (* 4660 *) - -(* int -> bytes *) -let test_int_bytes = bytes 4660 (* 0x1234 *) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/ungrouped.jsligo b/gitlab-pages/docs/language-basics/src/strings-bytes/ungrouped.jsligo deleted file mode 100644 index f08dfc7e1d..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/ungrouped.jsligo +++ /dev/null @@ -1,3 +0,0 @@ -const a = "Hello Alice"; -const b = 0x7070; -const bs = (bytes `foo`); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/strings-bytes/ungrouped.mligo b/gitlab-pages/docs/language-basics/src/strings-bytes/ungrouped.mligo deleted file mode 100644 index 743a886e74..0000000000 --- a/gitlab-pages/docs/language-basics/src/strings-bytes/ungrouped.mligo +++ /dev/null @@ -1,2 +0,0 @@ -let b : bytes = 0x7070 -let bs : bytes = [%bytes "foo"] \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/type-annotations/d.jsligo b/gitlab-pages/docs/language-basics/src/type-annotations/d.jsligo deleted file mode 100644 index bb96d2f228..0000000000 --- a/gitlab-pages/docs/language-basics/src/type-annotations/d.jsligo +++ /dev/null @@ -1,28 +0,0 @@ -type parameter = - ["Back"] -| ["Claim"] -| ["Withdraw"]; - -type storage = { - owner : address, - goal : tez, - deadline : timestamp, - backers : map, - funded : bool -}; - -@entry -const back = (param : unit, store : storage) : [list, storage] => { // Annotation - if (Tezos.get_now() > store.deadline) { - return failwith ("Deadline passed."); - } - else { - return match(Map.find_opt (Tezos.get_sender(), store.backers)) { - when(None()): do { - let backers = Map.update(Tezos.get_sender(), Some(Tezos.get_amount()), store.backers); - return [[], {...store, backers:backers}]; - }; - when(Some(x)): [[], store] - } - }; -}; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/types/a.jsligo b/gitlab-pages/docs/language-basics/src/types/a.jsligo deleted file mode 100644 index 586359fde7..0000000000 --- a/gitlab-pages/docs/language-basics/src/types/a.jsligo +++ /dev/null @@ -1,2 +0,0 @@ -type breed = string; -const dog_breed: breed = "Saluki"; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/types/a.mligo b/gitlab-pages/docs/language-basics/src/types/a.mligo deleted file mode 100644 index d0010fe488..0000000000 --- a/gitlab-pages/docs/language-basics/src/types/a.mligo +++ /dev/null @@ -1,2 +0,0 @@ -type breed = string -let dog_breed : breed = "Saluki" \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/a.jsligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/a.jsligo deleted file mode 100644 index d8fe6d6cb2..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/a.jsligo +++ /dev/null @@ -1,3 +0,0 @@ -let u1 : unit = []; -let u2 : unit = unit; -let eq = (u1 == u2); // true \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/a.mligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/a.mligo deleted file mode 100644 index 6585999c66..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/a.mligo +++ /dev/null @@ -1,7 +0,0 @@ -let n : unit = () -let m (x : int) = - begin - assert (x > 0); - assert (x < 10); - x - end \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/b.jsligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/b.jsligo deleted file mode 100644 index a58cb8f02e..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/b.jsligo +++ /dev/null @@ -1,3 +0,0 @@ -type coin = ["Head"] | ["Tail"]; -let head: coin = Head(); -let tail: coin = Tail(); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/b.mligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/b.mligo deleted file mode 100644 index 1d59863dd9..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/b.mligo +++ /dev/null @@ -1,3 +0,0 @@ -type coin = Head | Tail -let head : coin = Head -let tail : coin = Tail \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/c.jsligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/c.jsligo deleted file mode 100644 index 11a8365134..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/c.jsligo +++ /dev/null @@ -1,9 +0,0 @@ -type id = nat; - -type user = - ["Admin", id] -| ["Manager", id] -| ["Guest"]; - -const u : user = Admin(1000n); -const g : user = Guest(); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/c.mligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/c.mligo deleted file mode 100644 index 380a37ebdb..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/c.mligo +++ /dev/null @@ -1,9 +0,0 @@ -type id = nat - -type user = - Admin of id -| Manager of id -| Guest - -let u : user = Admin 1000n -let g : user = Guest \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/d.jsligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/d.jsligo deleted file mode 100644 index 6fcc69d423..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/d.jsligo +++ /dev/null @@ -1,3 +0,0 @@ -function div (a: nat, b: nat): option { - if (b == 0n) return None() else return Some(a/b) -}; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/d.mligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/d.mligo deleted file mode 100644 index 50e0a43e11..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/d.mligo +++ /dev/null @@ -1,2 +0,0 @@ -let div (a, b : nat * nat) : nat option = - if b = 0n then None else Some (a/b) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/multi_sum.jsligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/multi_sum.jsligo deleted file mode 100644 index cac2d373a4..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/multi_sum.jsligo +++ /dev/null @@ -1,19 +0,0 @@ -type t2 = ["A", int] | ["B", int]; - -namespace MyModule { - type t5 = ["A", int] | ["C", bool]; - type t4 = ["A", int] | ["D", int]; - - namespace MySubModule { - type t6 = ["A", int] | ["E", tez]; - } -} - -namespace MySecondModule { - type t3 = ["A", int] | ["F", int]; -} - -type t1 = ["A", int] | ["G", tez]; - -// The compiler will search above for sum types with an 'A' constructor -const x = A(42); \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/multi_sum.mligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/multi_sum.mligo deleted file mode 100644 index 79b8ba61ff..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/multi_sum.mligo +++ /dev/null @@ -1,19 +0,0 @@ -type t2 = A of int | B of int - -module MyModule = struct - type t5 = A of int | C of bool - type t4 = A of int | D of int - - module MySubModule = struct - type t6 = A of int | E of tez - end -end - -module MySecondModule = struct - type t3 = A of int | F of int -end - -type t1 = A of int | G of tez - -// The compiler will search above for sum types with an 'A' constructor -let x = A 42 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_complex.jsligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_complex.jsligo deleted file mode 100644 index 21566e7d08..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_complex.jsligo +++ /dev/null @@ -1,9 +0,0 @@ -type complex_t = { a : option> ; b : list } - -const complex = (x: complex_t, y: complex_t) => - match ([x,y]) { - when ([{a:None; b:_bl}, {a:_ar; b:_br}]): -1 - when ([{a:_a; b:_b}, {a: Some ([]); b: [hd,...tl]}]): hd - when ([{a:_a; b:_b}, {a: Some ([hd,...tl]); b:[]}]): hd - when ([{a: Some (a); b:_b}, _l]) : int (List.length (a)) - } \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_complex.mligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_complex.mligo deleted file mode 100644 index a3404f0796..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_complex.mligo +++ /dev/null @@ -1,8 +0,0 @@ -type complex_t = { a : int list option ; b : int list } - -let complex = fun (x:complex_t) (y:complex_t) -> - match (x,y) with - | {a=None; b=_}, {a = _; b = _} -> -1 - | {a=_; b=_}, {a = Some ([]); b = (hd::tl)} -> hd - | {a=_; b=_}, {a = Some (hd::tl); b = []} -> hd - | {a=Some a; b=_}, _ -> int (List.length a) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_lists.jsligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_lists.jsligo deleted file mode 100644 index f941c96202..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_lists.jsligo +++ /dev/null @@ -1,5 +0,0 @@ -let weird_length = (v : list) : int => - match(v) { - when([]): -1; - when([hd, ...tl]): 1 + int(List.length(tl)) - }; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_lists.mligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_lists.mligo deleted file mode 100644 index f3c8275df4..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_lists.mligo +++ /dev/null @@ -1,5 +0,0 @@ -let weird_length (v : int list) : int = - match v with - | [] -> -1 - | [ a; b ; c] -> -2 - | x -> int (List.length x) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_rec_tuple.jsligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_rec_tuple.jsligo deleted file mode 100644 index d8350a9e23..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_rec_tuple.jsligo +++ /dev/null @@ -1,12 +0,0 @@ -type my_record = { a : int ; b : nat ; c : string } -type my_tuple = [int, nat, string] - -let on_record = (v : my_record) : int => - match (v) { - when ({ a ; b : b_renamed ; c : _c }): a + int(b_renamed) - } - -let on_tuple = (v : my_tuple) : int => - match (v) { - when ([x, y, _s]): x + int(y) - } \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_rec_tuple.mligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_rec_tuple.mligo deleted file mode 100644 index 7d01a342d3..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_rec_tuple.mligo +++ /dev/null @@ -1,9 +0,0 @@ -type my_record = {a : int; b : nat; c : string} -type my_tuple = int * nat * string - -let on_record (v : my_record) : int = - match v with - { a ; b = b_renamed ; c = _ } -> a + int b_renamed - -let on_tuple (v : my_tuple) : int = - match v with (x , y, _) -> x + int y \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_variant.jsligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_variant.jsligo deleted file mode 100644 index 9fbee55ea0..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_variant.jsligo +++ /dev/null @@ -1,21 +0,0 @@ -type color = -| ["RGB", [int, int, int]] -| ["Gray", int] -| ["Default"]; - -const int_of_color = (c : color) : int => - match(c) { - when(RGB(rgb)): 16 + rgb[2] + rgb[1] * 6 + rgb[0] * 36; - when(Gray(i)): 232 + i; - when(Default()): 0 }; -function match_with_block () { - let x = 1; - return - match(Some(1)) { - when(None()): failwith(1); - when(Some(org)): do { - let y = x + 1; - return y - } - }; -}; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_variant.mligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_variant.mligo deleted file mode 100644 index bf58662256..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/pm_variant.mligo +++ /dev/null @@ -1,10 +0,0 @@ -type color = - | RGB of int * int * int - | Gray of int - | Default - -let int_of_color (c : color) : int = - match c with - | RGB (r,g,b) -> 16 + b + g * 6 + r * 36 - | Gray i -> 232 + i - | Default -> 0 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/sum_shadow.mligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/sum_shadow.mligo deleted file mode 100644 index abf624317a..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/sum_shadow.mligo +++ /dev/null @@ -1,9 +0,0 @@ -module M = struct - type t1 = A of int | B of int -end -module M = struct - let y = 10 -end - -(* This will fail because A will not be found *) -(* let x = A 42 *) \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/ungrouped.jsligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/ungrouped.jsligo deleted file mode 100644 index 0ac82d23d0..0000000000 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/ungrouped.jsligo +++ /dev/null @@ -1,20 +0,0 @@ -type foo = - { kind: "increment", amount: int} -| { kind: "decrement", amount: int} -| { kind: "reset"}; -let obj = { kind: "increment", amount: 3}; -let obj2 = { kind: "reset" }; -function foo (item: foo) { - let state = 0; - switch(item.kind) { - case "increment": - state += item.amount; - break - case "decrement": - state -= item.amount; - break - case "reset": - state = 0; - break - } -} \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/variables-and-constants/a.jsligo b/gitlab-pages/docs/language-basics/src/variables-and-constants/a.jsligo deleted file mode 100644 index dc443235e8..0000000000 --- a/gitlab-pages/docs/language-basics/src/variables-and-constants/a.jsligo +++ /dev/null @@ -1 +0,0 @@ -const @Unique_name = true \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/variables-and-constants/a.mligo b/gitlab-pages/docs/language-basics/src/variables-and-constants/a.mligo deleted file mode 100644 index 3b5f421e9e..0000000000 --- a/gitlab-pages/docs/language-basics/src/variables-and-constants/a.mligo +++ /dev/null @@ -1 +0,0 @@ -let @Unique_name = true \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/variables-and-constants/add.jsligo b/gitlab-pages/docs/language-basics/src/variables-and-constants/add.jsligo deleted file mode 100644 index 422036c12b..0000000000 --- a/gitlab-pages/docs/language-basics/src/variables-and-constants/add.jsligo +++ /dev/null @@ -1,5 +0,0 @@ -let add = (a: int, b: int): int => { - let c = a; - c = c + b; - return c; -} \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo b/gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo deleted file mode 100644 index 886ab63017..0000000000 --- a/gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo +++ /dev/null @@ -1,2 +0,0 @@ -let add (a : int) (b : int) = - let c = a + b in c \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/variables-and-constants/b.jsligo b/gitlab-pages/docs/language-basics/src/variables-and-constants/b.jsligo deleted file mode 100644 index 422036c12b..0000000000 --- a/gitlab-pages/docs/language-basics/src/variables-and-constants/b.jsligo +++ /dev/null @@ -1,5 +0,0 @@ -let add = (a: int, b: int): int => { - let c = a; - c = c + b; - return c; -} \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/variables-and-constants/c.mligo b/gitlab-pages/docs/language-basics/src/variables-and-constants/c.mligo deleted file mode 100644 index e94d2ca757..0000000000 --- a/gitlab-pages/docs/language-basics/src/variables-and-constants/c.mligo +++ /dev/null @@ -1,2 +0,0 @@ -let add (a, b : int * int) = - let c = a + b in c \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/variables-and-constants/const.jsligo b/gitlab-pages/docs/language-basics/src/variables-and-constants/const.jsligo deleted file mode 100644 index 3700370968..0000000000 --- a/gitlab-pages/docs/language-basics/src/variables-and-constants/const.jsligo +++ /dev/null @@ -1 +0,0 @@ -const age : int = 25; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/variables-and-constants/const.mligo b/gitlab-pages/docs/language-basics/src/variables-and-constants/const.mligo deleted file mode 100644 index c86da6dbf2..0000000000 --- a/gitlab-pages/docs/language-basics/src/variables-and-constants/const.mligo +++ /dev/null @@ -1 +0,0 @@ -let age : int = 25 \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/variables-and-constants/d.jsligo b/gitlab-pages/docs/language-basics/src/variables-and-constants/d.jsligo deleted file mode 100644 index 6c9f864041..0000000000 --- a/gitlab-pages/docs/language-basics/src/variables-and-constants/d.jsligo +++ /dev/null @@ -1,7 +0,0 @@ -const x = () => { - const _age = 25; - { - const _age = 3; // does not give an error - return _age; - } -}; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/src/variables-and-constants/ungrouped.jsligo b/gitlab-pages/docs/language-basics/src/variables-and-constants/ungrouped.jsligo deleted file mode 100644 index 3700370968..0000000000 --- a/gitlab-pages/docs/language-basics/src/variables-and-constants/ungrouped.jsligo +++ /dev/null @@ -1 +0,0 @@ -const age : int = 25; \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/strings-bytes.md b/gitlab-pages/docs/language-basics/strings-bytes.md deleted file mode 100644 index d54d7cf97c..0000000000 --- a/gitlab-pages/docs/language-basics/strings-bytes.md +++ /dev/null @@ -1,320 +0,0 @@ ---- -id: strings-bytes -title: Strings & Bytes ---- - -import Syntax from '@theme/Syntax'; - -## Strings - -Strings are defined using the built-in `string` type like this: - - - -``` -let a : string = "Hello Alice" -``` - - - - - -```jsligo -const a = "Hello Alice"; -``` - - - -### Concatenating Strings - - - -Strings can be concatenated using the `^` operator. - -```cameligo group=a -let name : string = "Alice" -let greeting : string = "Hello" -let full_greeting : string = greeting ^ " " ^ name -``` - - - - - -Strings can be concatenated using the `+` operator. - -```jsligo group=a -const name = "Alice"; -const greeting = "Hello"; -const full_greeting = greeting + " " + name; -``` - - - -### Extracting Substrings - -Substrings can be extracted using the predefined function -`String.sub`. The first character has index 0 and the interval of -indices for the substring has inclusive bounds. - - - -```cameligo group=b -let name : string = "Alice" -let slice : string = String.sub 0n 1n name (* slice = "A" *) -``` - - - - - -```jsligo group=b -const name = "Alice"; -const slice = String.sub (0n, 1n, name); // slice == "A" -``` - - - -> ⚠️ Notice that the offset and length of the slice are natural -> numbers. - -### Length of Strings - -The length of a string can be found using a built-in function: - - - -```cameligo group=c -let name : string = "Alice" -let length : nat = String.length name // length = 5 -``` - -> Note that `String.size` is *deprecated*. - - - - - -```jsligo group=c -const name = "Alice"; -const length = String.length(name); // length == 5 -``` - - - -## Bytes - -Byte literals are defined using the prefix `0x` followed by hexadecimal digits like this: - - - -```cameligo -let b : bytes = 0x7070 -``` - - - - - -```jsligo -const b = 0x7070; -``` - - - -Moreover, a string literal can be converted to its bytes representation: - - - -```cameligo -let bs : bytes = [%bytes "foo"] -``` - - - - - -```jsligo -const bs = (bytes `foo`); -``` - - - - -### Concatenating Bytes - -Bytes can be concatenated using the `Bytes.concat` function. - - - -```cameligo group=d -let white : bytes = 0xffff -let black : bytes = 0x0000 -let pixels : bytes = Bytes.concat white black (* 0xffff0000 *) -``` - - - - - -```jsligo group=d -const white = 0xffff; -const black = 0x0000; -const pixels = Bytes.concat(white, black); // 0xffff0000 -``` - - - -### Extracting Bytes - -Bytes can be extracted using the predefined function `Bytes.sub`. The -first parameter takes the start index and the second parameter takes -the number of bytes. Pay special attention to how `bytes` are -indexed. - - - -```cameligo group=e -let b : bytes = 0x12345678 -let slice : bytes = Bytes.sub 1n 2n b (* 0x3456 *) -``` - - - - - -```jsligo group=e -const b = 0x12345678; -const slice = Bytes.sub (1n, 2n, b); // 0x3456 -``` - - - -### Length of Bytes - -The length of `bytes` can be found using a built-in function `Bytes.length`: - - - -```cameligo group=f -let b : bytes = 0x123456 -let length : nat = Bytes.length b (* length = 3 *) -``` - - - - - -```jsligo group=f -const b = 0x123456; -const length = Bytes.length(b); // length = 3 -``` - - - -### Bitwise operators - -You can perform bitwise operation on `bytes` as follows: - - - -```cameligo group=g -(* Bitwise and *) -let b_and = 0x0005 land 0x0106 (* 0x0004 *) - -(* Bitwise or *) -let b_or = 0x0005 lor 0x0106 (* 0x0107 *) - -(* Bitwise xor *) -let b_xor = 0x0005 lxor 0x0106 (* 0x0103 *) - -(* Bitwise shift left *) -let b_shift_left = 0x06 lsl 8n (* 0x0600 *) - -(* Bitwise shift right *) -let b_shift_right = 0x0006 lsr 1n (* 0x0003 *) -``` - - - - - -```jsligo group=g -/* Bitwise and */ -const b_and = 0x0005 & 0x0106; // 0x0004 - -/* Bitwise or */ -const b_or = 0x0005 | 0x0106; // 0x0107 - -/* Bitwise xor */ -const b_xor = 0x0005 ^ 0x0106; // 0x0103 - -/* Bitwise shift left */ -const b_shift_left = 0x06 << 8n; // 0x0600 - -/* Bitwise shift right */ -const b_shift_right = 0x0006 >> 1n; // 0x0003 -``` - - - - -### From `bytes` to `nat` and back - -You can case `bytes` to `nat` using the built-in `nat` function and vice-versa -using using the `bytes` built-in function. - - - -```cameligo group=h -(* bytes -> nat *) -let test_bytes_nat = nat 0x1234 (* 1234n *) - -(* nat -> bytes *) -let test_nat_bytes = bytes 4660n (* 0x1234 *) -``` - - - - - -```jsligo group=h -/* bytes -> nat */ -const test_bytes_nat = nat(0x1234) // 1234n - -/* nat -> bytes */ -const test_nat_bytes = bytes(4660n) // 0x1234 -``` - - - -### From `bytes` to `int` and back - -You can cast `bytes` to `int` using the built-in `int` function and -vice-versa using the `bytes` built-in function. - - - -```cameligo group=h -(* bytes -> int *) -let test_bytes_int = int 0x1234 (* 4660 *) - -(* int -> bytes *) -let test_int_bytes = bytes 4660 (* 0x1234 *) -``` - - - - - -```jsligo group=h -/* bytes -> int */ -const test_bytes_int = int(0x1234) // 4660 - -/* int -> bytes */ -const test_int_bytes = bytes(4660) // 0x1234 -``` - - - - \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/type-annotations.md b/gitlab-pages/docs/language-basics/type-annotations.md deleted file mode 100644 index ebacd0b5cc..0000000000 --- a/gitlab-pages/docs/language-basics/type-annotations.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -id: type-annotations -title: Type Annotations ---- - -import Syntax from '@theme/Syntax'; - -## Annotations - -In certain cases, the type of an expression cannot be properly -inferred by the compiler. In order to help the type checker, you can -annotate an expression with its desired type. Here is an example: - - - -```cameligo group=d -type parameter = Back | Claim | Withdraw - -type storage = { - owner : address; - goal : tez; - deadline : timestamp; - backers : (address, tez) map; - funded : bool -} - -[@entry] -let back (param : unit) (store : storage) : operation list * storage = (* Annotation *) - if Tezos.get_now () > store.deadline then failwith "Deadline passed." - else - match Map.find_opt (Tezos.get_sender ()) store.backers with - None -> - let backers = Map.update (Tezos.get_sender ()) (Some (Tezos.get_amount ())) store.backers - in [], {store with backers=backers} - | Some (x) -> [], store -``` - - - - - -```jsligo group=d -type parameter = - ["Back"] -| ["Claim"] -| ["Withdraw"]; - -type storage = { - owner : address, - goal : tez, - deadline : timestamp, - backers : map, - funded : bool -}; - -@entry -const back = (param : unit, store : storage) : [list, storage] => { // Annotation - if (Tezos.get_now() > store.deadline) { - return failwith ("Deadline passed."); - } - else { - return match(Map.find_opt (Tezos.get_sender(), store.backers)) { - when(None()): do { - let backers = Map.update(Tezos.get_sender(), Some(Tezos.get_amount()), store.backers); - return [[], {...store, backers:backers}]; - }; - when(Some(x)): [[], store] - } - }; -}; -``` - - - - diff --git a/gitlab-pages/docs/language-basics/types.md b/gitlab-pages/docs/language-basics/types.md deleted file mode 100644 index f7c931ffde..0000000000 --- a/gitlab-pages/docs/language-basics/types.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -id: types -title: Simple Types ---- - -import Syntax from '@theme/Syntax'; - -*LIGO is strongly and statically typed.* This means that the compiler -checks how your contract processes data, ensuring that each function's -expectations are met. If it passes the test, your contract will not fail at -run-time due to some inconsistent assumptions on your data. This is -called *type checking*. - -LIGO types are built on top of Michelson's type system. - -## Built-in types - -For quick reference, you can find all the built-in types [here](https://gitlab.com/ligolang/ligo/-/blob/dev/src/main/build/ligo_lib/std_lib.mligo#L1-33). - -## Type aliases - -*Type aliasing* consists of renaming a given type when the context -calls for a more precise name. This increases readability and -maintainability of your smart contracts. For example we can choose to -alias a string type as an animal breed - this will allow us to -communicate our intent with added clarity. - - - -```cameligo group=a -type breed = string -let dog_breed : breed = "Saluki" -``` - - - - - -```jsligo group=a -type breed = string; -const dog_breed: breed = "Saluki"; -``` - - - - -> The above type definitions are aliases, which means that `breed` and -> `string` are interchangeable in all contexts. - -## Simple types - - - -```cameligo group=b -// The type account_balances denotes maps from addresses to tez - -type account_balances = (address, tez) map - -let ledger : account_balances = - Map.literal - [(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), 10mutez)] -``` - - - - - -```jsligo group=b -// The type account_balances denotes maps from addresses to tez - -type account_balances = map; - -const ledger : account_balances = - Map.literal([["tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" as address, 10mutez]]); -``` - - - - diff --git a/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md b/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md deleted file mode 100644 index 9876f52505..0000000000 --- a/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md +++ /dev/null @@ -1,489 +0,0 @@ ---- -id: unit-option-pattern-matching -title: Unit, Option, Pattern matching ---- - -import Syntax from '@theme/Syntax'; - -Optional values are a pervasive programming pattern in OCaml. Since -Michelson and LIGO are both inspired by OCaml, *optional types* are -available in LIGO as well. Similarly, OCaml features a *unit* type, -and LIGO features it as well. Both the option type and the unit type -are instances of a more general kind of types: *variant types*. - -## The unit Type - -The `unit` type in Michelson or LIGO is a predefined type that -contains only one value that carries no information. It is used when -no relevant information is required or produced. Here is how it used. - - - -In CameLIGO, the unique value of the `unit` type is `()`, following -the OCaml convention. -```cameligo group=a -let n : unit = () -``` - -Sequences of expressions that return the `unit` type can be written -using `begin` and `end`, separating expressions using semi-colons. The -last expression, which represents the value returned, can have a -different type to `unit`: - -```cameligo group=a -let m (x : int) = - begin - assert (x > 0); - assert (x < 10); - x - end -``` - - - - - -In JsLIGO, the unique value of the `unit` type is `[]`. The global variable `unit` contains `[]` so that name can be used for clarity, but the value is the same. -```jsligo group=a -let u1 : unit = []; -let u2 : unit = unit; -let eq = (u1 == u2); // true -``` - - - - - -## Discriminated union type - -The simplest form of pattern matching in JsLIGO is with the help of a discriminated -union type, which should be familiar for developers coming from TypeScript. - -```jsligo -type foo = - { kind: "increment", amount: int} -| { kind: "decrement", amount: int} -| { kind: "reset"}; -``` - -Here, the `kind` field is unique among the objects. If not, an error will be -generated. Also, if multiple fields are present which can be used as unique -field, only the first unique field will be used. - -Creating an object from a discriminated union type requires all the fields -to be fully written. So for increment that would be: - -```jsligo -let obj = { kind: "increment", amount: 3}; -``` - -or - -```jsligo -let obj2 = { kind: "reset" }; -``` - -Pattern matching over a discriminated union type works like this: - -```jsligo -function foo (item: foo) { - let state = 0; - switch(item.kind) { - case "increment": - state += item.amount; - break - case "decrement": - state -= item.amount; - break - case "reset": - state = 0; - break - } -} -``` - -Note that all cases of the discriminated union must be handled, if not an error -will be generated. - -These "strict" rules on discriminated union types help prevent bugs where cases are not handled correctly. - - - - -## Variant types - -A variant type is a user-defined or a built-in type (in case of -options) that defines a type by cases, so a value of a variant type is -either this, or that or... The simplest variant type is equivalent to -the enumerated types found in Java, C++, JavaScript etc. - -Here is how we define a coin as being either head or tail (and nothing -else): - - - -```cameligo group=b -type coin = Head | Tail -let head : coin = Head -let tail : coin = Tail -``` - - - - - -```jsligo group=b -type coin = ["Head"] | ["Tail"]; -let head: coin = Head(); -let tail: coin = Tail(); -``` - - - - -The names `Head` and `Tail` in the definition of the type `coin` are -called *data constructors*, or *variants*. In this particular, they -carry no information beyond their names, so they are called *constant -constructors*. - -In general, it is interesting for variants to carry some information, -and thus go beyond enumerated types. In the following, we show how to -define different kinds of users of a system. - - - -```cameligo group=c -type id = nat - -type user = - Admin of id -| Manager of id -| Guest - -let u : user = Admin 1000n -let g : user = Guest -``` - -In CameLIGO, a constant constructor is equivalent to the same constructor -taking an argument of type `unit`, so, for example, `Guest` is the -same value as `Guest ()`. - - - - - -```jsligo group=c -type id = nat; - -type user = - ["Admin", id] -| ["Manager", id] -| ["Guest"]; - -const u : user = Admin(1000n); -const g : user = Guest(); -``` - -In JsLIGO, a constant constructor is equivalent to the same constructor -taking an argument of type `unit`, so, for example, `Guest ()` is the -same value as `Guest (unit)`. - - -There are cases where several sum types match a given constructor. - -In the example below, types `t1` to `t6` are all possible types for `x`. - -In this case, the compiler will choose one of these types as the type -of the expression, and throw a warning stating that other types are -possible. - -You can add a type annotation to remove this ambiguity. - -**NOTE** : The compiler will choose in priority the latest matching -sum type in the current scope, if no type is defined in this scope, it -will look in the latest module, if not in the second latest etc. -Below, it will choose `t1`, and if `t1` didn't match it would have -chosen `t2`, otherwise `t3`, etc. - - - -```cameligo group=multi_sum -type t2 = A of int | B of int - -module MyModule = struct - type t5 = A of int | C of bool - type t4 = A of int | D of int - - module MySubModule = struct - type t6 = A of int | E of tez - end -end - -module MySecondModule = struct - type t3 = A of int | F of int -end - -type t1 = A of int | G of tez - -// The compiler will search above for sum types with an 'A' constructor -let x = A 42 -``` - - - - - -```jsligo group=multi_sum -type t2 = ["A", int] | ["B", int]; - -namespace MyModule { - type t5 = ["A", int] | ["C", bool]; - type t4 = ["A", int] | ["D", int]; - - namespace MySubModule { - type t6 = ["A", int] | ["E", tez]; - } -} - -namespace MySecondModule { - type t3 = ["A", int] | ["F", int]; -} - -type t1 = ["A", int] | ["G", tez]; - -// The compiler will search above for sum types with an 'A' constructor -const x = A(42); -``` - - - - - -In CameLigo when looking for a matching sum type, the compiler will -not look in shadowed modules. The below code will throw an error -because type `t1` is in a shadowed module and thus not accessible. - -```cameligo group=sum_shadow -module M = struct - type t1 = A of int | B of int -end -module M = struct - let y = 10 -end - -(* This will fail because A will not be found *) -(* let x = A 42 *) -``` - - - - -## Optional values - -The `option` type is a predefined variant type that is used to express -whether there is a value of some type or none. This is especially -useful when calling a *partial function*, that is, a function that is -not defined for some inputs. In that case, the value of the `option` -type would be `None`, otherwise `Some (v)`, where `v` is some -meaningful value *of any type*. An example in arithmetic is the -division operation: - - - -```cameligo group=d -let div (a, b : nat * nat) : nat option = - if b = 0n then None else Some (a/b) -``` - - - - - -```jsligo group=d -function div (a: nat, b: nat): option { - if (b == 0n) return None() else return Some(a/b) -}; -``` - - - -You can extract the value of a `Some (v)` with the function `Option.unopt (Some (v))`. In case the value is `None`, this will fail with an error. - -The proper way to deal with optional values is by means of pattern matching. - - -## Pattern matching - -*Pattern matching* is similar to the `switch` construct in -JavaScript, and can be used to route the program's control flow based -on the value of a variant, record, tuple, or list. - -A component of a pattern can be discarded by using a wildcard `_` -instead of a variable name. - -LIGO will warn about unused variables bound in patterns in the same -way that function arguments are warned about. Variable names beginning -with `_` can be used as a binder to prevent warnings. - -### Match on variants - -Here is a function that transforms a colour variant type to an int. - - - -```cameligo group=pm_variant -type color = - | RGB of int * int * int - | Gray of int - | Default - -let int_of_color (c : color) : int = - match c with - | RGB (r,g,b) -> 16 + b + g * 6 + r * 36 - | Gray i -> 232 + i - | Default -> 0 -``` - - - - - -```jsligo group=pm_variant -type color = -| ["RGB", [int, int, int]] -| ["Gray", int] -| ["Default"]; - -const int_of_color = (c : color) : int => - match(c) { - when(RGB(rgb)): 16 + rgb[2] + rgb[1] * 6 + rgb[0] * 36; - when(Gray(i)): 232 + i; - when(Default()): 0 }; -``` - -The right-hand sides of each `when`-clause is an expression. Sometimes -we might need statements to be processed before a value is given to -the clause. In that case, the `do` expression comes handy. It enables -the opening of a block of statements like a function body, that is, a -block ended with a `return` statement whose argument has the value of -the block, like so: - -```jsligo group=pm_variant -function match_with_block () { - let x = 1; - return - match(Some(1)) { - when(None()): failwith(1); - when(Some(org)): do { - let y = x + 1; - return y - } - }; -}; -``` - - - -### Matching records or tuples - -Fields of records and components of tuples can be destructured. Record -pattern variables can be renamed. - - - -```cameligo group=pm_rec_tuple -type my_record = {a : int; b : nat; c : string} -type my_tuple = int * nat * string - -let on_record (v : my_record) : int = - match v with - { a ; b = b_renamed ; c = _ } -> a + int b_renamed - -let on_tuple (v : my_tuple) : int = - match v with (x , y, _) -> x + int y -``` - - - - - -```jsligo group=pm_rec_tuple -type my_record = { a : int ; b : nat ; c : string } -type my_tuple = [int, nat, string] - -let on_record = (v : my_record) : int => - match (v) { - when ({ a ; b : b_renamed ; c : _c }): a + int(b_renamed) - } - -let on_tuple = (v : my_tuple) : int => - match (v) { - when ([x, y, _s]): x + int(y) - } -``` - - - -### Matching lists - - - -```cameligo group=pm_lists -let weird_length (v : int list) : int = - match v with - | [] -> -1 - | [ a; b ; c] -> -2 - | x -> int (List.length x) -``` - - - - - -```jsligo group=pm_lists -let weird_length = (v : list) : int => - match(v) { - when([]): -1; - when([hd, ...tl]): 1 + int(List.length(tl)) - }; -``` - - - - -### Deep patterns - -Pattern matching can also be used for nested patterns. - - - -```cameligo group=pm_complex -type complex_t = { a : int list option ; b : int list } - -let complex = fun (x:complex_t) (y:complex_t) -> - match (x,y) with - | {a=None; b=_}, {a = _; b = _} -> -1 - | {a=_; b=_}, {a = Some ([]); b = (hd::tl)} -> hd - | {a=_; b=_}, {a = Some (hd::tl); b = []} -> hd - | {a=Some a; b=_}, _ -> int (List.length a) -``` - - - - - -```jsligo group=pm_complex -type complex_t = { a : option> ; b : list } - -const complex = (x: complex_t, y: complex_t) => - match ([x,y]) { - when ([{a:None; b:_bl}, {a:_ar; b:_br}]): -1 - when ([{a:_a; b:_b}, {a: Some ([]); b: [hd,...tl]}]): hd - when ([{a:_a; b:_b}, {a: Some ([hd,...tl]); b:[]}]): hd - when ([{a: Some (a); b:_b}, _l]) : int (List.length (a)) - } -``` - - - - \ No newline at end of file diff --git a/gitlab-pages/docs/language-basics/variables-and-constants.md b/gitlab-pages/docs/language-basics/variables-and-constants.md deleted file mode 100644 index 60e9dca895..0000000000 --- a/gitlab-pages/docs/language-basics/variables-and-constants.md +++ /dev/null @@ -1,160 +0,0 @@ ---- -id: constants-and-variables -title: Constants & Variables ---- - -import Syntax from '@theme/Syntax'; - -The next building block after types are *constants* and *variables*. - -## Constants - -Constants are immutable by design, which means their values cannot be -reassigned. Put in another way, they can be assigned once, at their -declaration. When defining a constant you need to provide a `name`, -`type` and a `value`: - - - -```cameligo group=const -let age : int = 25 -``` - -You can evaluate the constant definition above using the following CLI -command: -```shell -ligo run evaluate-expr gitlab-pages/docs/language-basics/src/variables-and-constants/const.mligo age -# Outputs: 25 -``` - - - - - -> Constants in JsLIGO are enforced: - -```jsligo skip -const x = do { - const age = 25; - age = 3; // Yields an error -}; -``` - -Unlike the other syntaxes, JsLIGO doesn't allow variable names to be reused in the same block scope: - -```jsligo skip -const x = () => { - const age = 25; - const age = 3; // Yields an error -}; -``` - -However, the following does work: - -```jsligo group=d -const x = () => { - const _age = 25; - { - const _age = 3; // does not give an error - return _age; - } -}; -``` - -You can evaluate a constant definition using the following CLI -command: - -```jsligo group=const -const age : int = 25; -``` - -```shell -ligo run evaluate-expr gitlab-pages/docs/language-basics/src/variables-and-constants/const.jsligo age -# Outputs: 25 -``` - - - - -## Variables - - - -As expected in the pure subset of a functional language, CameLIGO only -features *constant values*: once they are declared, the value cannot -be changed (or "mutated"). - -```cameligo group=add -let add (a : int) (b : int) = - let c = a + b in c -``` - -You can run the `add` function defined above using the LIGO compiler -like this: -```shell -ligo run evaluate-expr gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo 'add 1 1' -# Outputs: 2 -``` - - - - - -Variables, unlike constants, are *mutable*. - -> ⚠️ Please be wary that mutation only works within the function scope -> itself, values outside of the function scope will not be -> affected. In other words, when a function is called, its arguments -> are copied, *as well as the environment*. Any side-effect to that -> environment is therefore lost when the function returns. - - -```jsligo group=add -let add = (a: int, b: int): int => { - let c = a; - c = c + b; - return c; -} -``` - -You can run the `add` function defined above using the LIGO compiler -like this: - -```shell -ligo run evaluate-expr gitlab-pages/docs/language-basics/src/variables-and-constants/add.jsligo 'add(1, 1)' -# Outputs: 2 -``` - - - -## Escaped Identifiers - -Both variables and constants are, at the level of the lexicon, -_identifiers_. Each flavour of LIGO has its own set of -keywords. Sometimes we need an identifier that is the same as a -keyword, or, perhaps, we do not want to shadow a predefined -identifier, like `amount`. In those cases, you could suffix your -identifier with an underscore, like `amount_`. (Beware that if you -prefix with an underscore, like `_amount`, the compiler will not -complain about the value being not used.) But this is not a good -practice because we do not pronounce aloud the underscores, and there -is the issue of one or two underscores. To solve all those problems, -in LIGO, you can prefix you identifier with `@`, like `@amount`. - - - -```cameligo group=a -let @Unique_name = true -``` - - - - - -```jsligo group=a -const @Unique_name = true -``` - - - - \ No newline at end of file diff --git a/gitlab-pages/docs/protocol/hangzhou.md b/gitlab-pages/docs/protocol/hangzhou.md index e90dc55d9d..199d61ec5d 100644 --- a/gitlab-pages/docs/protocol/hangzhou.md +++ b/gitlab-pages/docs/protocol/hangzhou.md @@ -165,12 +165,12 @@ let test = ```jsligo skip -let open_or_fail = ([ck, c, @time] : [chest_key, chest, nat]) : bytes => { - return (match ( Tezos.open_chest(ck,c,@time), { - Ok_opening: (b:bytes) => b, - Fail_decrypt: () => failwith("decrypt"), - Fail_timelock: () => failwith("timelock"), - })) +const open_or_fail = ([ck, c, time_] : [chest_key, chest, nat]) : bytes => + $match(Tezos.open_chest(ck,c,time_), { + "Ok_opening": b => b, + "Fail_decrypt": () => failwith("decrypt"), + "Fail_timelock": () => failwith("timelock") + }) }; ``` @@ -208,7 +208,7 @@ type storage = string let main (((),s): unit * storage) : operation list * storage = [] , s (* view 'view1', simply returns the storage *) -[@view] let view1 ((),s: unit * storage) : storage = s +[@view] let view1 ((), s : unit * storage) : storage = s (* view 'v2', returns true if the storage has a given length *) [@view] let v2 (expected_length,s: nat * storage) : bool = (String.length s = expected_length) @@ -222,19 +222,19 @@ let main (((),s): unit * storage) : operation list * storage = [] , s ```jsligo group=views type storage = string -let main = ([_ , s]: [unit , storage]) : [ list , storage] => [[], s]; +const main = ([_ , s]: [unit , storage]) : [ list , storage] => [[], s]; /* view 'view1', simply returns the storage */ -@view -let view1 = ([_ , s]: [unit , storage]) : storage => s; +// @view +const view1 = ([_ , s]: [unit , storage]) : storage => s; /* view 'v2', returns true if the storage has a given length */ -@view -let v2 = ([expected_length,s] : [nat , storage]) : bool => (String.length (s) == expected_length); +// @view +const v2 = ([expected_length,s] : [nat , storage]) : bool => (String.length (s) == expected_length); /* view 'view3' returns a constant int */ -@view -let view3 = ([_ , _s]: [unit , storage]) : int => 42; +// @view +const view3 = ([_ , _s]: [unit , storage]) : int => 42; ``` @@ -256,14 +256,14 @@ view or the parameter type do not match, `Tezos.call_view` will return `None`. ```cameligo group=views -let view_call ((name,parameter,addr): string * int * address) : int option = Tezos.call_view "sto_plus_n" 1 addr +let view_call ((name,parameter,addr): string * int * address) : int option = Tezos.View.call "sto_plus_n" 1 addr ``` ```jsligo group=views -let view_call = ([name,parameter,addr]: [string , int , address]) : option => Tezos.call_view ("sto_plus_n", 1, addr) +const view_call = ([name,parameter,addr]: [string , int , address]) : option => Tezos.View.call ("sto_plus_n", 1, addr) ``` diff --git a/gitlab-pages/docs/protocol/kathmandu.md b/gitlab-pages/docs/protocol/kathmandu.md index 66562762c5..10ecdfe937 100644 --- a/gitlab-pages/docs/protocol/kathmandu.md +++ b/gitlab-pages/docs/protocol/kathmandu.md @@ -46,14 +46,17 @@ Here is how you emit events and fetch them from your tests: ```cameligo test-ligo group=test_ex module C = struct - [@entry] let main (p: int*int) (_: unit) = - [Tezos.emit "%foo" p ; Tezos.emit "%foo" p.0],() + [@entry] + let main (p : int * int) () = + let op1 = Tezos.Operation.emit "%foo" p in + let op2 = Tezos.Operation.emit "%foo" p.0 in + [op1; op2], () end let test_foo = - let orig = Test.originate (contract_of C) () 0tez in - let _ = Test.transfer_exn orig.addr (Main (1,2)) 0tez in - (Test.get_last_events_from orig.addr "foo" : (int*int) list),(Test.get_last_events_from orig.addr "foo" : int list) + let orig = Test.Originate.contract (contract_of C) () 0tez in + let _: nat = Test.Typed_address.transfer_exn orig.taddr (Main (1,2)) 0tez in + (Test.State.last_events orig.taddr "foo" : (int*int) list),(Test.State.last_events orig.taddr "foo" : int list) ``` @@ -61,19 +64,21 @@ let test_foo = ```jsligo test-ligo group=test_ex namespace C { - @entry - let main = (p: [int, int], _s : unit) => { - let op1 = Tezos.emit("%foo", p); - let op2 = Tezos.emit("%foo", p[0]); - return [list([op1, op2]), unit]; + // @entry + const main = (p: [int, int], _: unit) : [list, unit] => { + const op1 = Tezos.Operation.emit("%foo", p); + const op2 = Tezos.Operation.emit("%foo", p[0]); + return [[op1, op2], unit]; }; } -let test = (() : [list<[int,int]>, list] => { - let orig = Test.originate(contract_of(C), unit, 0 as tez); - Test.transfer_exn(orig.addr, Main([1,2]), 0 as tez); - return [Test.get_last_events_from(orig.addr, "foo") as list<[int, int]>, Test.get_last_events_from(orig.addr, "foo") as list]; -}) (); +const test = () => { + const orig = Test.Originate.contract(contract_of(C), unit, 0 as tez); + Test.Typed_address.transfer_exn(orig.taddr, ["Main" as "Main", [1,2]], 0 as tez); + return [Test.State.last_events(orig.taddr, "foo") as list<[int, int]>, Test.State.last_events(orig.taddr, "foo") as list]; +}; + +const run_test = test(); ``` diff --git a/gitlab-pages/docs/protocol/mumbai.md b/gitlab-pages/docs/protocol/mumbai.md index c001d17889..e6744d0bf8 100644 --- a/gitlab-pages/docs/protocol/mumbai.md +++ b/gitlab-pages/docs/protocol/mumbai.md @@ -30,11 +30,11 @@ let test_bytes_bitwise_ops = let b_shift_left = 0x06 lsl 8n in let b_shift_right = 0x0006 lsr 1n in - assert (b_and = 0x0004 && - b_or = 0x0107 && - b_xor = 0x0103 && - b_shift_left = 0x0600 && - b_shift_right = 0x0003 ) + Assert.assert (b_and = 0x0004 && + b_or = 0x0107 && + b_xor = 0x0103 && + b_shift_left = 0x0600 && + b_shift_right = 0x0003) ``` @@ -48,11 +48,12 @@ const test_bytes_bitwise_module = (() => { const b_shift_left = Bitwise.shift_left (0x06 , 8 as nat); const b_shift_right = Bitwise.shift_right (0x0006, 1 as nat); - assert (b_and == 0x0004 && - b_or == 0x0107 && - b_xor == 0x0103 && - b_shift_left == 0x0600 && - b_shift_right == 0x0003 )})() + Assert.assert (b_and == 0x0004 && + b_or == 0x0107 && + b_xor == 0x0103 && + b_shift_left == 0x0600 && + b_shift_right == 0x0003 ) + })() ``` diff --git a/gitlab-pages/docs/protocol/src/hangzhou/views.jsligo b/gitlab-pages/docs/protocol/src/hangzhou/views.jsligo index 3605fb44aa..ce8f903df8 100644 --- a/gitlab-pages/docs/protocol/src/hangzhou/views.jsligo +++ b/gitlab-pages/docs/protocol/src/hangzhou/views.jsligo @@ -1,15 +1,15 @@ type storage = string -let main = ([_ , s]: [unit , storage]) : [ list , storage] => [[], s]; +const main = ([_ , s]: [unit , storage]) : [ list , storage] => [[], s]; /* view 'view1', simply returns the storage */ -@view -let view1 = ([_ , s]: [unit , storage]) : storage => s; +// @view +const view1 = ([_ , s]: [unit , storage]) : storage => s; /* view 'v2', returns true if the storage has a given length */ -@view -let v2 = ([expected_length,s] : [nat , storage]) : bool => (String.length (s) == expected_length); +// @view +const v2 = ([expected_length,s] : [nat , storage]) : bool => (String.length (s) == expected_length); /* view 'view3' returns a constant int */ -@view -let view3 = ([_ , _s]: [unit , storage]) : int => 42; -let view_call = ([name,parameter,addr]: [string , int , address]) : option => Tezos.call_view ("sto_plus_n", 1, addr) \ No newline at end of file +// @view +const view3 = ([_ , _s]: [unit , storage]) : int => 42; +const view_call = ([name,parameter,addr]: [string , int , address]) : option => Tezos.View.call ("sto_plus_n", 1, addr) \ No newline at end of file diff --git a/gitlab-pages/docs/protocol/src/hangzhou/views.mligo b/gitlab-pages/docs/protocol/src/hangzhou/views.mligo index 891fa65059..caefa1ec3c 100644 --- a/gitlab-pages/docs/protocol/src/hangzhou/views.mligo +++ b/gitlab-pages/docs/protocol/src/hangzhou/views.mligo @@ -2,11 +2,11 @@ type storage = string let main (((),s): unit * storage) : operation list * storage = [] , s (* view 'view1', simply returns the storage *) -[@view] let view1 ((),s: unit * storage) : storage = s +[@view] let view1 ((), s : unit * storage) : storage = s (* view 'v2', returns true if the storage has a given length *) [@view] let v2 (expected_length,s: nat * storage) : bool = (String.length s = expected_length) (* view 'v3' returns a constant int *) [@view] let v3 ((),_ : unit * storage) : int = 42 -let view_call ((name,parameter,addr): string * int * address) : int option = Tezos.call_view "sto_plus_n" 1 addr \ No newline at end of file +let view_call ((name,parameter,addr): string * int * address) : int option = Tezos.View.call "sto_plus_n" 1 addr \ No newline at end of file diff --git a/gitlab-pages/docs/protocol/src/kathmandu/test_ex.jsligo b/gitlab-pages/docs/protocol/src/kathmandu/test_ex.jsligo index 2965cb5e9f..d7c9c04176 100644 --- a/gitlab-pages/docs/protocol/src/kathmandu/test_ex.jsligo +++ b/gitlab-pages/docs/protocol/src/kathmandu/test_ex.jsligo @@ -1,14 +1,16 @@ namespace C { - @entry - let main = (p: [int, int], _s : unit) => { - let op1 = Tezos.emit("%foo", p); - let op2 = Tezos.emit("%foo", p[0]); - return [list([op1, op2]), unit]; + // @entry + const main = (p: [int, int], _: unit) : [list, unit] => { + const op1 = Tezos.Operation.emit("%foo", p); + const op2 = Tezos.Operation.emit("%foo", p[0]); + return [[op1, op2], unit]; }; } -let test = (() : [list<[int,int]>, list] => { - let orig = Test.originate(contract_of(C), unit, 0 as tez); - Test.transfer_exn(orig.addr, Main([1,2]), 0 as tez); - return [Test.get_last_events_from(orig.addr, "foo") as list<[int, int]>, Test.get_last_events_from(orig.addr, "foo") as list]; -}) (); \ No newline at end of file +const test = () => { + const orig = Test.Originate.contract(contract_of(C), unit, 0 as tez); + Test.Typed_address.transfer_exn(orig.taddr, ["Main" as "Main", [1,2]], 0 as tez); + return [Test.State.last_events(orig.taddr, "foo") as list<[int, int]>, Test.State.last_events(orig.taddr, "foo") as list]; +}; + +const run_test = test(); \ No newline at end of file diff --git a/gitlab-pages/docs/protocol/src/kathmandu/test_ex.mligo b/gitlab-pages/docs/protocol/src/kathmandu/test_ex.mligo index 25188826a5..933dfe2c4e 100644 --- a/gitlab-pages/docs/protocol/src/kathmandu/test_ex.mligo +++ b/gitlab-pages/docs/protocol/src/kathmandu/test_ex.mligo @@ -1,9 +1,12 @@ module C = struct - [@entry] let main (p: int*int) (_: unit) = - [Tezos.emit "%foo" p ; Tezos.emit "%foo" p.0],() + [@entry] + let main (p : int * int) () = + let op1 = Tezos.Operation.emit "%foo" p in + let op2 = Tezos.Operation.emit "%foo" p.0 in + [op1; op2], () end let test_foo = - let orig = Test.originate (contract_of C) () 0tez in - let _ = Test.transfer_exn orig.addr (Main (1,2)) 0tez in - (Test.get_last_events_from orig.addr "foo" : (int*int) list),(Test.get_last_events_from orig.addr "foo" : int list) \ No newline at end of file + let orig = Test.Originate.contract (contract_of C) () 0tez in + let _: nat = Test.Typed_address.transfer_exn orig.taddr (Main (1,2)) 0tez in + (Test.State.last_events orig.taddr "foo" : (int*int) list),(Test.State.last_events orig.taddr "foo" : int list) \ No newline at end of file diff --git a/gitlab-pages/docs/protocol/src/mumbai/test_bitwise.jsligo b/gitlab-pages/docs/protocol/src/mumbai/test_bitwise.jsligo index 54b938cad6..48fbca4572 100644 --- a/gitlab-pages/docs/protocol/src/mumbai/test_bitwise.jsligo +++ b/gitlab-pages/docs/protocol/src/mumbai/test_bitwise.jsligo @@ -5,8 +5,9 @@ const test_bytes_bitwise_module = (() => { const b_shift_left = Bitwise.shift_left (0x06 , 8 as nat); const b_shift_right = Bitwise.shift_right (0x0006, 1 as nat); - assert (b_and == 0x0004 && - b_or == 0x0107 && - b_xor == 0x0103 && - b_shift_left == 0x0600 && - b_shift_right == 0x0003 )})() \ No newline at end of file + Assert.assert (b_and == 0x0004 && + b_or == 0x0107 && + b_xor == 0x0103 && + b_shift_left == 0x0600 && + b_shift_right == 0x0003 ) + })() \ No newline at end of file diff --git a/gitlab-pages/docs/protocol/src/mumbai/test_bitwise.mligo b/gitlab-pages/docs/protocol/src/mumbai/test_bitwise.mligo index 2f68978efe..da90eb9ca6 100644 --- a/gitlab-pages/docs/protocol/src/mumbai/test_bitwise.mligo +++ b/gitlab-pages/docs/protocol/src/mumbai/test_bitwise.mligo @@ -5,8 +5,8 @@ let test_bytes_bitwise_ops = let b_shift_left = 0x06 lsl 8n in let b_shift_right = 0x0006 lsr 1n in - assert (b_and = 0x0004 && - b_or = 0x0107 && - b_xor = 0x0103 && - b_shift_left = 0x0600 && - b_shift_right = 0x0003 ) \ No newline at end of file + Assert.assert (b_and = 0x0004 && + b_or = 0x0107 && + b_xor = 0x0103 && + b_shift_left = 0x0600 && + b_shift_right = 0x0003) \ No newline at end of file diff --git a/gitlab-pages/docs/reference/Test.Next.Account.Contract.md b/gitlab-pages/docs/reference/Test.Next.Account.Contract.md deleted file mode 100644 index 05accc2c3e..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.Account.Contract.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -id: test.next.account.contract-reference -title: contract -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val bootstrap : nat -> address - - -let bootstrap: (_: nat) => address - -Returns the address corresponding to the nth bootstrapped - contract. - - - -val bootstrap_typed_address : 'a 'b.nat -> ('a, 'b) typed_address - - -let bootstrap_typed_address: <a, b>(_: nat) => typed_address<a, b> - -Returns the typed address corresponding to the nth bootstrapped - contract currently loaded. The types are inferred from those - contracts loaded with `Test.State.Reset.add_func_contract` - (before reset). diff --git a/gitlab-pages/docs/reference/Test.Next.Account.md b/gitlab-pages/docs/reference/Test.Next.Account.md deleted file mode 100644 index c9acf86850..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.Account.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -id: test.next.account-reference -title: account -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - -[module contract](test.next.account.contract.md) - - - -val address : nat -> address - - -let address: (n: nat) => address - -Returns the address of the nth bootstrapped account. - - - -val alice : unit -> address - - -let alice: (_: unit) => address - -Returns the address of the 0th bootstrapped account. - - - -val bob : unit -> address - - -let bob: (_: unit) => address - -Returns the address of the 1st bootstrapped account. - - - -val carol : unit -> address - - -let carol: (_: unit) => address - -Returns the address of the 2nd bootstrapped account. - - - -val dan : unit -> address - - -let dan: (_: unit) => address - -Returns the address of the 3rd bootstrapped account. - - - -val add : string -> key -> unit - - -let add: (_: string) => (_: key) => unit - -Adds an account specfied by secret key & public key to the test - context. - - - -type info = { - addr : address; - pk : key; - sk : string -} - - -type info = { addr: address; pk: key; sk: string } - - - -val info : nat -> Test.Next.Account.info - - -let info: (_: nat) => Test.Next.Account.info - -Returns the address information of the nth bootstrapped - account. - - - -val new : unit -> Test.Next.Account.info - - -let new: (_: unit) => Test.Next.Account.info - -Creates and returns information of a new account. diff --git a/gitlab-pages/docs/reference/Test.Next.Assert.Error.md b/gitlab-pages/docs/reference/Test.Next.Assert.Error.md deleted file mode 100644 index 0ec0c99e4f..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.Assert.Error.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -id: test.next.assert.error-reference -title: error -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val assert : bool -> string -> unit - - -let assert: (b: bool, s: string) => unit - - - -The call `assert cond error` terminates the execution - with the string `error` (that is, an error message) if, and only - if, the boolean condition `cond` is false. The failure is handled - by LIGO's testing framework and not by Michelson's interpreter. - - - - - -The call `assert(cond, error)` terminates the execution - with the string `error` (that is, an error message) if, and only - if, the boolean condition `cond` is false. The failure is handled - by LIGO's testing framework and not by Michelson's interpreter. - - - - - -val some : 'a.'a option -> string -> unit - - -let some: <a>(_: option<a>) => (_: string) => unit - - - -The call `some opt err` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is `None`. The failure is handled by LIGO's testing - framework and not by Michelson's interpreter. - - - - - -The call `some(opt, err)` terminates the - execution with the string `err` (that is, an error message) if, - and only if, `opt` is `None()`. The failure is handled by LIGO's - testing framework and not by Michelson's interpreter. - - - - - -val none : 'a.'a option -> string -> unit - - -let none: <a>(_: option<a>) => (_: string) => unit - - - -The call `none opt err` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is an optional value different from `None`. The failure is - handled by LIGO's testing framework and not by Michelson's - interpreter. - - - - - -The call `none(opt, err)` terminates the - execution with the string `err` (that is, an error message) if, - and only if, `opt` is an optional value different from - `None()`. The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - diff --git a/gitlab-pages/docs/reference/Test.Next.Assert.md b/gitlab-pages/docs/reference/Test.Next.Assert.md deleted file mode 100644 index 3f31ac18a2..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.Assert.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -id: test.next.assert-reference -title: assert -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - -[module error](test.next.assert.error.md) - - - -val failwith : 'a 'b.'a -> 'b - - -let failwith: <a, b>(_: a) => b - -Cause the testing framework to fail. - - - -val assert : bool -> unit - - -let assert: (_: bool) => unit - - - -The call `assert cond` terminates the execution with the string - `"failed assertion"` if, and only if, the boolean condition `cond` - is false. The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -The call `assert(cond)` terminates the execution with the string - `"failed assertion"` if, and only if, the boolean condition `cond` - is false. The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -val some : 'a.'a option -> unit - - -let some: <a>(_: option<a>) => unit - - - -The call `some opt` terminates the execution with the - string `"failed assert some"` if, and only if, `opt` is `None`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -The call `some(opt)` terminates the execution with the - string `"failed assert some"` if, and only if, `opt` is `None()`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -val none : 'a.'a option -> unit - - -let none: <a>(_: option<a>) => unit - - - -The call `none opt` terminates the execution with the string - `"failed assert none"` if, and only if, `opt` is not `None`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -The call `none(opt)` terminates the execution with the string - `"failed assert none"` if, and only if, `opt` is not `None()`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - diff --git a/gitlab-pages/docs/reference/Test.Next.Compare.md b/gitlab-pages/docs/reference/Test.Next.Compare.md deleted file mode 100644 index 5ff8bf545c..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.Compare.md +++ /dev/null @@ -1,146 +0,0 @@ ---- -id: test.next.compare-reference -title: compare -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val eq : 'a.'a -> 'a -> bool - - -let eq: <a>(_: a) => (_: a) => bool - - - -The call `eq x y` returns `true` if, and only if, `x` and `y` - are considered to be equal w.r.t. the order on the underlying - type. - - - - - -The call `eq(x, y)` returns `true` if, and only if, `x` and `y` - are considered to be equal w.r.t. the order on the underlying - type. - - - - - -val neq : 'a.'a -> 'a -> bool - - -let neq: <a>(_: a) => (_: a) => bool - - - -The call `neq x y` returns `true` if, and only if, `x` and - `y` are not considered to be equal w.r.t. the order on the - underlying type. - - - - - -The call `neq(x, y)` returns `true` if, and only if, `x` and - `y` are not considered to be equal w.r.t. the order on the - underlying type. - - - - - -val gt : 'a.'a -> 'a -> bool - - -let gt: <a>(_: a) => (_: a) => bool - - - -The call `gt x y` returns `true` if, and only if, `x` is - considered to be greater than `y` w.r.t. the order on the - underlying type. - - - - - -The call `gt(x, y)` returns `true` if, and only if, `x` is - considered to be greater than `y` w.r.t. the order on the - underlying type. - - - - - -val lt : 'a.'a -> 'a -> bool - - -let lt: <a>(_: a) => (_: a) => bool - - - -The call `lt` returns `true` if, and only if, `x` is - considered to be less than `y` w.r.t. the order on the underlying - type. - - - - - -The call `lt(x, y)` returns `true` if, and only if, `x` is - considered to be less than `y` w.r.t. the order on the underlying - type. - - - - - -val ge : 'a.'a -> 'a -> bool - - -let ge: <a>(_: a) => (_: a) => bool - - - -The call `ge x y` returns `true` if, and only if, - `x` is considered to be greater or equal than `y` w.r.t. the order - on the underlying type. - - - - - -The call `ge(x, y)` returns `true` if, and only if, - `x` is considered to be greater or equal than `y` w.r.t. the order - on the underlying type. - - - - - -val le : 'a.'a -> 'a -> bool - - -let le: <a>(_: a) => (_: a) => bool - - - -The call `le x y` returns `true` if, and only if, `x` - is considered to be less or equal than `y` w.r.t. the order on the - underlying type. - - - - - -The call `le(x, y)` returns `true` if, and only if, `x` - is considered to be less or equal than `y` w.r.t. the order on the - underlying type. - - diff --git a/gitlab-pages/docs/reference/Test.Next.Contract.md b/gitlab-pages/docs/reference/Test.Next.Contract.md deleted file mode 100644 index d7919dcf11..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.Contract.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -id: test.next.contract-reference -title: contract -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val transfer : 'p.'p contract -> 'p -> tez -> test_exec_result - - -let transfer: <p>(_: contract<p>) => (_: p) => (_: tez) => test_exec_result - - - -val transfer_exn : 'p.'p contract -> 'p -> tez -> nat - - -let transfer_exn: <p>(_: contract<p>) => (_: p) => (_: tez) => nat - - - -val to_typed_address : 'p 's.'p contract -> ('p, 's) typed_address - - -let to_typed_address: <p, s>(_: contract<p>) => typed_address<p, s> - diff --git a/gitlab-pages/docs/reference/Test.Next.Dynamic_entrypoints.md b/gitlab-pages/docs/reference/Test.Next.Dynamic_entrypoints.md deleted file mode 100644 index 19b59628ea..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.Dynamic_entrypoints.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -id: test.next.dynamic-entrypoints-reference -title: dynamic_entrypoints -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val storage : - 'p - 's - 's2.('p, 's) module_contract -> - 's2 -> - { - dynamic_entrypoints : dynamic_entrypoints; - storage : 's2 - } - - -let storage: <p, s, s2>(_: module_contract<p, s>, s: s2) => { dynamic_entrypoints: dynamic_entrypoints; storage: s2 } - diff --git a/gitlab-pages/docs/reference/Test.Next.Michelson.Contract.md b/gitlab-pages/docs/reference/Test.Next.Michelson.Contract.md deleted file mode 100644 index c909393eac..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.Michelson.Contract.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -id: test.next.michelson.contract-reference -title: contract -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val compile : 'p 's.(('p * 's) -> (operation list * 's)) -> ('p, 's) michelson_contract - - -let compile: <p, s>(_: (_: [p, s]) => [list<operation>, s]) => michelson_contract<p, s> - -Compiles a contract from an entrypoint function. - - - -val compile_with_views : 'p 's.(('p * 's) -> (operation list * 's)) -> 's views -> ('p, 's) michelson_contract - - -let compile_with_views: <p, s>(_: (_: [p, s]) => [list<operation>, s]) => (_: views<s>) => michelson_contract<p, s> - - - -val size : 'p 's.('p, 's) michelson_contract -> int - - -let size: <p, s>(_: michelson_contract<p, s>) => int - -Measures the size of a contract. - - - -val from_file : 'p 's.string -> ('p, 's) michelson_contract - - -let from_file: <p, s>(_: string) => michelson_contract<p, s> - -Reads a contract from a `.tz` file. diff --git a/gitlab-pages/docs/reference/Test.Next.Michelson.md b/gitlab-pages/docs/reference/Test.Next.Michelson.md deleted file mode 100644 index 3eaa1d7d79..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.Michelson.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -id: test.next.michelson-reference -title: michelson -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - -[module contract](test.next.michelson.contract.md) - - - -val run : 'a 'b.('a -> 'b) -> 'a -> michelson_program - - -let run: <a, b>(_: (_: a) => b) => (_: a) => michelson_program - -Run a function on an input, all in Michelson. More concretely: - a) compiles the function argument to Michelson `f_mich`; b) - compiles the value argument (which was evaluated already) to - Michelson `v_mich`; c) runs the Michelson interpreter on the code - `f_mich` with starting stack `[v_mich]`. - - - -val eval : 'a.'a -> michelson_program - - -let eval: <a>(_: a) => michelson_program - -Compile a LIGO value to Michelson. Currently it is a - renaming of `compile_value`. - - - -val decompile : 'a.michelson_program -> 'a - - -let decompile: <a>(_: michelson_program) => a - -Decompile a Michelson value to LIGO, following the - (mandatory) type annotation. Note: This operation can fail at - run-time, in case that the `michelson_program` given cannot be - decompiled to something compatible with the annotated type. - - - -val parse : string -> michelson_program - - -let parse: (_: string) => michelson_program - -Parses Michelson (as string) into a `michelson_program`. diff --git a/gitlab-pages/docs/reference/Test.Next.Mutation.All.md b/gitlab-pages/docs/reference/Test.Next.Mutation.All.md deleted file mode 100644 index 40413fce07..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.Mutation.All.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -id: test.next.mutation.all-reference -title: all -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val func : 'a 'b.'a -> ('a -> 'b) -> ('b * mutation) list - - -let func: <a, b>(_: a) => (_: (_: a) => b) => list<[b, mutation]> - -Given a value to mutate (first argument), it will try all the - mutations of it, passing each one to the function (second - argument). In case no failure arises when running the function on - a mutation, the failure and mutation involved will be added to the - list to be returned. - - - -val from_file : - 'b - 'p - 's.string -> 's -> tez -> ((('p, 's) typed_address * ('p, 's) michelson_contract * int) -> 'b) -> ('b * mutation) list - - -let from_file: - <b, p, s>(_: string) => (_: s) => (_: tez) => (_: (_: [typed_address<p, s>, michelson_contract<p, s>, int]) => b) => list< - [b, mutation] - > - -Given a contract from a file (passed by filepath, entrypoint and - views), an initial storage and balance, it will originate mutants - of the contract and pass the result to the function (last - argument). In case no failure arises when running the function on - a mutation, the failure and mutation involved will be added to the - list to be returned. - - - -val contract : - 'p - 's - 'b.('p, 's) module_contract -> - 's -> tez -> (('p, 's) typed_address -> ('p, 's) michelson_contract -> int -> 'b) -> ('b * mutation) list - - -let contract: - <p, s, b>(_: module_contract<p, s>) => (_: s) => (_: tez) => ( - _: (_: typed_address<p, s>) => (_: michelson_contract<p, s>) => (_: int) => b - ) => list<[b, mutation]> - -Given a contract as a module/namespace, an initial storage and - balance, it will originate mutants of the contract and pass the - result to the function (last argument). In case no failure arises - when running the function on a mutation, the failure and mutation - involved will be added to the list to be returned. diff --git a/gitlab-pages/docs/reference/Test.Next.Mutation.md b/gitlab-pages/docs/reference/Test.Next.Mutation.md deleted file mode 100644 index 727b4a4a2f..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.Mutation.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -id: test.next.mutation-reference -title: mutation -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - -[module all](test.next.mutation.all.md) - - - -val func : 'a 'b.'a -> ('a -> 'b) -> ('b * mutation) option - - -let func: <a, b>(_: a) => (_: (_: a) => b) => option<[b, mutation]> - -Given a value to mutate (first argument), it will try all the - mutations available of it, passing each one to the function - (second argument). On the first case of non failure when running - the function on a mutation, the value and mutation involved will - be returned. - - - -val from_file : - 'b - 'p - 's.string -> - 's -> tez -> ((('p, 's) typed_address * ('p, 's) michelson_contract * int) -> 'b) -> ('b * mutation) option - - -let from_file: - <b, p, s>(_: string) => (_: s) => (_: tez) => (_: (_: [typed_address<p, s>, michelson_contract<p, s>, int]) => b) => option< - [b, mutation] - > - -Given a contract from a file (passed by filepath, entrypoint and - views), an initial storage and balance, it will originate mutants - of the contract and pass the result to the function (last - argument). On the first case of non failure when running the - function on a mutation, the value and mutation involved will be - returned. - - - -val contract : - 'p - 's - 'b.('p, 's) module_contract -> - 's -> tez -> (('p, 's) typed_address -> ('p, 's) michelson_contract -> int -> 'b) -> ('b * mutation) option - - -let contract: - <p, s, b>(_: module_contract<p, s>) => (_: s) => (_: tez) => ( - _: (_: typed_address<p, s>) => (_: michelson_contract<p, s>) => (_: int) => b - ) => option<[b, mutation]> - -Given a contract as a module/namespace, an initial storage and - balance, it will originate mutants of the contract and pass the - result to the function (last argument). On the first case of non - failure when running the function on a mutation, the value and - mutation involved will be returned. - - - -val value : 'a.nat -> 'a -> ('a * mutation) option - - -let value: <a>(_: nat) => (_: a) => option<[a, mutation]> - -Mutates a value using a natural number as an index for the - available mutations, returns an option for indicating whether - mutation was successful or not. - - - -val save : string -> mutation -> string option - - -let save: (_: string) => (_: mutation) => option<string> - -This function reconstructs a file from a mutation (second - argument), and saves it to a file in the directory path (first - argument). It returns an optional string indicating the filename - where the mutation was saved, or `None` if there was an error. diff --git a/gitlab-pages/docs/reference/Test.Next.Originate.md b/gitlab-pages/docs/reference/Test.Next.Originate.md deleted file mode 100644 index 379cd29963..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.Originate.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -id: test.next.originate-reference -title: originate -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -type ('p, 's) origination_result = { - code : ('p, 's) michelson_contract; - size : int; - taddr : ('p, 's) typed_address -} - - -type origination_result<p, s> = { code: michelson_contract<p, s>; size: int; taddr: typed_address<p, s> } - - - -val contract : 'p 's.('p, 's) module_contract -> 's -> tez -> ('p, 's) origination_result - - -let contract: <p, s>(_: module_contract<p, s>) => (_: s) => (_: tez) => origination_result<p, s> - -Originate a contract with an entrypoint function in curried - form, initial storage and initial balance. - - - -val from_file : 'p 's.string -> 's -> tez -> ('p, 's) origination_result - - -let from_file: <p, s>(_: string) => (_: s) => (_: tez) => origination_result<p, s> - -Originate a contract with a path to the contract file, an - entrypoint, and a list of views, together with an initial storage - and an initial balance. - - - -val michelson : 'p 's.('p, 's) michelson_contract -> 's -> tez -> ('p, 's) typed_address - - -let michelson: <p, s>(_: michelson_contract<p, s>) => (_: s) => (_: tez) => typed_address<p, s> - -Originate a contract with initial storage and initial - balance. diff --git a/gitlab-pages/docs/reference/Test.Next.State.Reset.md b/gitlab-pages/docs/reference/Test.Next.State.Reset.md deleted file mode 100644 index daae21f17b..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.State.Reset.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -id: test.next.state.reset-reference -title: reset -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val add_baker : (string * key) -> tez option -> unit - - -let add_baker: (_: [string, key]) => (_: option<tez>) => unit - -Adds an account `(sk, pk)` as a baker. The change is only - effective after `Test.reset_state`. - - - -val add_func_contract : 'p 's.(('p * 's) -> (operation list * 's)) -> 's -> tez -> unit - - -let add_func_contract: <p, s>(_: (_: [p, s]) => [list<operation>, s]) => (_: s) => (_: tez) => unit - -Setup a bootstrap contract with an entrypoint function, initial - storage and initial balance. Bootstrap contracts will be loaded in - order, and they will be available only after reset. diff --git a/gitlab-pages/docs/reference/Test.Next.State.md b/gitlab-pages/docs/reference/Test.Next.State.md deleted file mode 100644 index c98ad45527..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.State.md +++ /dev/null @@ -1,203 +0,0 @@ ---- -id: test.next.state-reference -title: state -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - -[module reset](test.next.state.reset.md) - - - -val restore : unit -> unit - - -let restore: (_: unit) => unit - -Pops a testing framework context from the stack of contexts, and - sets it up as the new current context. In case the stack was - empty, the current context is kept. - - - -val save : unit -> unit - - -let save: (_: unit) => unit - -Takes current testing framework context and saves it, pushing it - into a stack of contexts. - - - -val drop : unit -> unit - - -let drop: (_: unit) => unit - -Drops a testing framework context from the stack of contexts. In - case the stack was empty, nothing is done. - - - -val reset : nat -> tez list -> unit - - -let reset: (_: nat) => (_: list<tez>) => unit - -Generates a number of random bootstrapped accounts with a - default amount of `4000000` tez. The passed list can be used to - overwrite the amount. By default, the state only has two - bootstrapped accounts. Notice that since Ithaca, a percentage of - an account's balance is frozen (5% in testing mode) in case the - account can be taken to be a validator, and thus getting - balance can show a different amount to the one being set with - `Test.State.reset`. - - - -val reset_at : timestamp -> nat -> tez list -> unit - - -let reset_at: (_: timestamp) => (_: nat) => (_: list<tez>) => unit - -Generates a number of random bootstrapped accounts with a - default amount of `4000000` tez. The passed list can be used to - overwrite the amount. By default, the state only has two - bootstrapped accounts. Notice that since Ithaca, a percentage of - an account's balance is frozen (5% in testing mode) in case the - account can be taken to be a validator, and thus getting - balance can show a different amount to the one being set with - `Test.State.reset`. It also takes a starting timestamp - for the genesis block. - - - -val register_delegate : key_hash -> unit - - -let register_delegate: (_: key_hash) => unit - -Registers a `key_hash` corresponding to an account as a delegate. - - - -val register_constant : michelson_program -> string - - -let register_constant: (_: michelson_program) => string - -Registers a global constant, returns its hash as a string. See - the documentation for global constants for an example of usage. - - - -val set_source : address -> unit - - -let set_source: (_: address) => unit - -Sets the source for `Test.transfer` and `Test.originate`. - - - -val set_baker_policy : test_baker_policy -> unit - - -let set_baker_policy: (_: test_baker_policy) => unit - -Forces the baking policy for `Test.transfer` and - `Test.originate`. By default, the first bootstrapped account. - - - -val set_baker : address -> unit - - -let set_baker: (_: address) => unit - -Forces the baker for `Test.transfer` and `Test.originate`, - implemented using `Test.set_baker_policy` with `By_account`. By - default, the first bootstrapped account. - - - -val bake_until : nat -> unit - - -let bake_until: (_: nat) => unit - -It bakes until a number of cycles pass, so that an account - registered as delegate can effectively act as a baker. Note: It - can be used in tests to manually advance time. - - - -val set_big_map : 'k 'v.int -> ('k, 'v) big_map -> unit - - -let set_big_map: <k, v>(_: int) => (_: big_map<k, v>) => unit - -The testing framework keeps an internal reference to the values - corresponding to big map identifiers. This function allows to - override the value of a particular big map identifier. It should - not be normally needed, except in particular circumstances such as - using custom bootstrap contracts that initialize big maps. - - - -val get_voting_power : key_hash -> nat - - -let get_voting_power: (_: key_hash) => nat - -Return the voting power of a given contract. This voting power - coincides with the weight of the contract in the voting listings - (i.e., the rolls count) which is calculated at the beginning of - every voting period. - - - -val get_total_voting_power : unit -> nat - - -let get_total_voting_power: (_: unit) => nat - -Returns the total voting power of all contracts. The total - voting power coincides with the sum of the rolls count of every - contract in the voting listings. The voting listings is calculated - at the beginning of every voting period. - - - -val last_originations : unit -> (address, address list) map - - -let last_originations: (_: unit) => map<address, list<address>> - -Returns addresses of orginated accounts in the last transfer. It - is given in the form of a map binding the address of the source of - the origination operation to the addresses of newly originated - accounts. - - - -val last_events : 'a 'p 's.('p, 's) typed_address -> string -> 'a list - - -let last_events: <a, p, s>(_: typed_address<p, s>) => (_: string) => list<a> - -Returns the list of all the event payloads emited with a given - tag by a given address. Any call to this function must be - annotated with the expected payload type. - - - -val stake : key_hash -> tez -> unit - - -let stake: (_: key_hash) => (_: tez) => unit - diff --git a/gitlab-pages/docs/reference/Test.Next.Timelock.md b/gitlab-pages/docs/reference/Test.Next.Timelock.md deleted file mode 100644 index ec54b8edff..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.Timelock.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -id: test.next.timelock-reference -title: timelock -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val create : bytes -> nat -> (chest * chest_key) - - -let create: (_: bytes) => (_: nat) => [chest, chest_key] - - - -val create_key : chest -> nat -> chest_key - - -let create_key: (_: chest) => (_: nat) => chest_key - - - -val verify : chest -> chest_key -> nat -> bool - - -let verify: (_: chest) => (_: chest_key) => (_: nat) => bool - - - -The call `verify chest chest_key n` verifies a matching - between `chest` and `chest_key` (taking into account `n`). - - - - - -The call `verify(chest, chest_key, n)` verifies a matching - between `chest` and `chest_key` (taking into account `n`). - - diff --git a/gitlab-pages/docs/reference/Test.Next.Typed_address.md b/gitlab-pages/docs/reference/Test.Next.Typed_address.md deleted file mode 100644 index 38464f17d8..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.Typed_address.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -id: test.next.typed-address-reference -title: typed_address -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val transfer : 'p 's.('p, 's) typed_address -> 'p -> tez -> test_exec_result - - -let transfer: <p, s>(_: typed_address<p, s>) => (_: p) => (_: tez) => test_exec_result - -Bakes a transaction by sending an amount of tez with a parameter - from the current source to another account. Returns the amount of - gas consumed by the execution of the contract. - - - -val transfer_exn : 'p 's.('p, 's) typed_address -> 'p -> tez -> nat - - -let transfer_exn: <p, s>(_: typed_address<p, s>) => (_: p) => (_: tez) => nat - -Bakes a transaction by sending an amount of tez with a parameter - from the current source to another account. Returns the amount of - gas consumed by the execution of the contract. Similar as - `Test.transfer`, but fails when anything goes wrong. - - - -val get_storage : 'p 's.('p, 's) typed_address -> 's - - -let get_storage: <p, s>(_: typed_address<p, s>) => s - -Gets the storage of a typed account. - - - -val get_balance : 'p 's.('p, 's) typed_address -> tez - - -let get_balance: <p, s>(_: typed_address<p, s>) => tez - -Gets the balance of an account in tez. - - - -val to_address : 'p 's.('p, 's) typed_address -> address - - -let to_address: <p, s>(_: typed_address<p, s>) => address - - - -val to_contract : 'p 's.('p, 's) typed_address -> 'p contract - - -let to_contract: <p, s>(_: typed_address<p, s>) => contract<p> - -Gets the contract corresponding to the default entrypoint of a - typed address: the contract parameter in the result will be the - type of the default entrypoint (generally `'param`, but this might - differ if `'param` includes a "default" entrypoint). - - - -val get_entrypoint : 'p 's 'q.string -> ('p, 's) typed_address -> 'q contract - - -let get_entrypoint: <p, s, q>(_: string) => (_: typed_address<p, s>) => contract<q> - -Gets the contract corresponding to an entrypoint of a typed - address: the contract parameter in the result will be the type of - the entrypoint, it needs to be annotated, entrypoint string should - omit the prefix "%", but if passed a string starting with "%", it - will be removed (and a warning emitted). diff --git a/gitlab-pages/docs/reference/Test.Next.md b/gitlab-pages/docs/reference/Test.Next.md deleted file mode 100644 index 74c94de183..0000000000 --- a/gitlab-pages/docs/reference/Test.Next.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -id: test.next-reference -title: next -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - -[module mutation](test.next.mutation.md) - - -module PBT = PBT - -[module state](test.next.state.md) - - -[module account](test.next.account.md) - - -[module compare](test.next.compare.md) - - -[module michelson](test.next.michelson.md) - - -[module io](test.next.io.md) - - -[module assert](test.next.assert.md) - - -[module string](test.next.string.md) - - -[module ticket](test.next.ticket.md) - - -[module originate](test.next.originate.md) - - -[module contract](test.next.contract.md) - - -[module typed_address](test.next.typed_address.md) - - -[module address](test.next.address.md) - - -[module timelock](test.next.timelock.md) - - -[module crypto](test.next.crypto.md) - - -[module dynamic_entrypoints](test.next.dynamic_entrypoints.md) - - - -val originate : 'p 's.('p, 's) module_contract -> 's -> tez -> ('p, 's) origination_result - - -let originate: <p, s>(_: module_contract<p, s>) => (_: s) => (_: tez) => origination_result<p, s> - - - -val failwith : 'a 'b.'a -> 'b - - -let failwith: <a, b>(_: a) => b - diff --git a/gitlab-pages/docs/reference/Test.PBT.md b/gitlab-pages/docs/reference/Test.PBT.md deleted file mode 100644 index 0c16ba31f0..0000000000 --- a/gitlab-pages/docs/reference/Test.PBT.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -id: test.pbt-reference -title: pbt -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val gen : 'a.'a pbt_gen - - -let gen: <a>pbt_gen<a> - - - -val gen_small : 'a.'a pbt_gen - - -let gen_small: <a>pbt_gen<a> - - - -val make_test : 'a.'a pbt_gen -> ('a -> bool) -> 'a pbt_test - - -let make_test: <a>(_: pbt_gen<a>) => (_: (_: a) => bool) => pbt_test<a> - - - -val run : 'a.'a pbt_test -> nat -> 'a pbt_result - - -let run: <a>(_: pbt_test<a>) => (_: nat) => pbt_result<a> - diff --git a/gitlab-pages/docs/reference/Test.Proxy_ticket.md b/gitlab-pages/docs/reference/Test.Proxy_ticket.md deleted file mode 100644 index af3664421d..0000000000 --- a/gitlab-pages/docs/reference/Test.Proxy_ticket.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -id: test.proxy-ticket-reference -title: proxy_ticket -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -type 'v proxy_address = ('v * nat * address, unit) typed_address - - -type proxy_address<v> = typed_address<[[v, nat], address], unit> - - - -val init_transfer : 'vt 'whole_p.('vt ticket -> 'whole_p) -> 'vt proxy_address - - -let init_transfer: <vt, whole_p>(_: (_: ticket<vt>) => whole_p) => proxy_address<vt> - - - -val transfer : 'vt.'vt proxy_address -> ('vt * nat * address) -> test_exec_result - - -let transfer: <vt>(_: proxy_address<vt>) => (_: [[vt, nat], address]) => test_exec_result - - - -val originate : - 'vt - 'whole_s - 'vp.('vt * nat) -> - ('vt ticket -> 'whole_s) -> ('vp -> 'whole_s -> (operation list * 'whole_s)) -> ('vp, 'whole_s) typed_address - - -let originate: - <vt, whole_s, vp>(_: [vt, nat]) => (_: (_: ticket<vt>) => whole_s) => ( - _: (_: vp) => (_: whole_s) => [list<operation>, whole_s] - ) => typed_address<vp, whole_s> - - - -val get_storage : 'p 's 's2.('p, 's) typed_address -> 's2 - - -let get_storage: <p, s, s2>(_: typed_address<p, s>) => s2 - diff --git a/gitlab-pages/docs/reference/Test.md b/gitlab-pages/docs/reference/Test.md deleted file mode 100644 index 2441b190ac..0000000000 --- a/gitlab-pages/docs/reference/Test.md +++ /dev/null @@ -1,1334 +0,0 @@ ---- -id: test-reference -title: test -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - -The testing framework - - -[module pbt](test.pbt.md) - - -[module proxy_ticket](test.proxy_ticket.md) - - -[module next](test.next.md) - - - -val run : 'a 'b.('a -> 'b) -> 'a -> michelson_program - - -let run: <a, b>(_: (_: a) => b) => (_: a) => michelson_program - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.run` from `Test.Next` is encouraged for a smoother migration. - -Run a function on an input, all in Michelson. More concretely: - a) compiles the function argument to Michelson `f_mich`; b) - compiles the value argument (which was evaluated already) to - Michelson `v_mich`; c) runs the Michelson interpreter on the code - `f_mich` with starting stack `[v_mich]`. - - - -val eval : 'a.'a -> michelson_program - - -let eval: <a>(_: a) => michelson_program - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.eval` from `Test.Next` is encouraged for a smoother migration. - -Compile a LIGO value to Michelson. Currently it is a renaming of - `compile_value`. - - - -val decompile : 'a.michelson_program -> 'a - - -let decompile: <a>(_: michelson_program) => a - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.decompile` from `Test.Next` is encouraged for a smoother migration. - -Decompile a Michelson value to LIGO, following the (mandatory) - type annotation. Note: This operation can fail at run-time, in case - that the `michelson_program` given cannot be decompiled to something - compatible with the annotated type. - - - -val compile_value : 'a.'a -> michelson_program - - -let compile_value: <a>(_: a) => michelson_program - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.eval` from `Test.Next` is encouraged for a smoother migration. - -Compile a LIGO value to Michelson. - - - -val get_total_voting_power : unit -> nat - - -let get_total_voting_power: (_: unit) => nat - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.get_total_voting_power` from `Test.Next` is encouraged for a smoother migration. - -Returns the total voting power of all contracts. The total - voting power coincides with the sum of the rolls count of every - contract in the voting listings. The voting listings is calculated - at the beginning of every voting period. - - - -val failwith : 'a 'b.'a -> 'b - - -let failwith: <a, b>(_: a) => b - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Assert.failwith` from `Test.Next` is encouraged for a smoother migration. - -Cause the testing framework to fail. - - - -val to_contract : 'p 's.('p, 's) typed_address -> 'p contract - - -let to_contract: <p, s>(_: typed_address<p, s>) => contract<p> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Typed_address.to_contract` from `Test.Next` is encouraged for a smoother migration. - -Gets the contract corresponding to the default entrypoint of a - typed address: the contract parameter in the result will be the - type of the default entrypoint (generally `'param`, but this might - differ if `'param` includes a "default" entrypoint). - - - -val set_source : address -> unit - - -let set_source: (_: address) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.set_source` from `Test.Next` is encouraged for a smoother migration. - -Sets the source for `Test.transfer` and `Test.originate`. - - - -val cast_address : 'a 'b.address -> ('a, 'b) typed_address - - -let cast_address: <a, b>(_: address) => typed_address<a, b> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Address.to_typed_address` from `Test.Next` is encouraged for a smoother migration. - -This function casts an address to a typed address. You will need - to annotate the result with the type you expect. - - - -val to_address : 'a 'b.('a, 'b) typed_address -> address - - -let to_address: <a, b>(_: typed_address<a, b>) => address - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Typed_address.to_address` from `Test.Next` is encouraged for a smoother migration. - - - -val get_storage : 'p 's.('p, 's) typed_address -> 's - - -let get_storage: <p, s>(_: typed_address<p, s>) => s - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Typed_address.get_storage` from `Test.Next` is encouraged for a smoother migration. - -Gets the storage of a typed account. - - - -val get_storage_of_address : 'b.address -> 'b - - -let get_storage_of_address: <b>(_: address) => b - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Address.get_storage` from `Test.Next` is encouraged for a smoother migration. - -Gets the storage of an account in `michelson_program`. - - - -val get_balance_of_address : address -> tez - - -let get_balance_of_address: (_: address) => tez - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Address.get_balance` from `Test.Next` is encouraged for a smoother migration. - -Gets the balance of an account (given as an address) in tez. - - - -val get_balance : 'p 's.('p, 's) typed_address -> tez - - -let get_balance: <p, s>(_: typed_address<p, s>) => tez - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Typed_address.get_balance` from `Test.Next` is encouraged for a smoother migration. - -Gets the balance of an account in tez. - - - -val print : string -> unit - - -let print: (_: string) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `IO.print` from `Test.Next` is encouraged for a smoother migration. - -Prints an string to stdout. - - - -val eprint : string -> unit - - -let eprint: (_: string) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `IO.eprint` from `Test.Next` is encouraged for a smoother migration. - -Prints an string to stderr. - - - -val get_voting_power : key_hash -> nat - - -let get_voting_power: (_: key_hash) => nat - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.get_voting_power` from `Test.Next` is encouraged for a smoother migration. - -Return the voting power of a given contract. This voting power - coincides with the weight of the contract in the voting listings - (i.e., the rolls count) which is calculated at the beginning of - every voting period. - - - -val nth_bootstrap_contract : nat -> address - - -let nth_bootstrap_contract: (_: nat) => address - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Account.Contract.bootstrap` from `Test.Next` is encouraged for a smoother migration. - -Returns the address corresponding to the nth bootstrapped - contract. - - - -val nth_bootstrap_account : int -> address - - -let nth_bootstrap_account: (_: int) => address - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Account.address` from `Test.Next` is encouraged for a smoother migration. - -Returns the address of the nth bootstrapped account. - - - -val get_bootstrap_account : nat -> (address * key * string) - - -let get_bootstrap_account: (_: nat) => [address, key, string] - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Account.info` from `Test.Next` is encouraged for a smoother migration. - -Returns the address, key and secret key of the nth bootstrapped - account. - - - -val nth_bootstrap_typed_address : 'a 'b.nat -> ('a, 'b) typed_address - - -let nth_bootstrap_typed_address: <a, b>(_: nat) => typed_address<a, b> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Account.Contract.bootstrap_typed_address` from `Test.Next` is encouraged for a smoother migration. - -Returns the typed address corresponding to the nth bootstrapped - contract currently loaded. The types are inferred from those - contracts loaded with `Test.bootstrap_contract` (before reset). - - - -val last_originations : unit -> (address, address list) map - - -let last_originations: (_: unit) => map<address, list<address>> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.last_originations` from `Test.Next` is encouraged for a smoother migration. - -Returns addresses of orginated accounts in the last transfer. It - is given in the form of a map binding the address of the source of - the origination operation to the addresses of newly originated - accounts. - - - -val random : 'a.unit -> 'a - - -let random: <a>(_: unit) => a - -This function creates a random value for a chosen type. - - - -val new_account : unit -> (string * key) - - -let new_account: (_: unit) => [string, key] - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Account.new` from `Test.Next` is encouraged for a smoother migration. - -Creates and returns secret key & public key of a new account. - - - -val bake_until_n_cycle_end : nat -> unit - - -let bake_until_n_cycle_end: (_: nat) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.bake_until` from `Test.Next` is encouraged for a smoother migration. - -It bakes until a number of cycles pass, so that an account - registered as delegate can effectively act as a baker. Note: It - can be used in tests to manually advance time. - - - -val get_time : unit -> timestamp - - -let get_time: (_: unit) => timestamp - - - -val register_delegate : key_hash -> unit - - -let register_delegate: (_: key_hash) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.register_delegate` from `Test.Next` is encouraged for a smoother migration. - -Registers a `key_hash` corresponding to an account as a delegate. - - - -val stake : key_hash -> tez -> unit - - -let stake: (_: key_hash) => (_: tez) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.stake` from `Test.Next` is encouraged for a smoother migration. - - - -val register_constant : michelson_program -> string - - -let register_constant: (_: michelson_program) => string - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.register_constant` from `Test.Next` is encouraged for a smoother migration. - -Registers a global constant, returns its hash as a string. See - the documentation for global constants for an example of usage. - - - -val to_typed_address : 'a 'b.'a contract -> ('a, 'b) typed_address - - -let to_typed_address: <a, b>(_: contract<a>) => typed_address<a, b> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Contract.to_typed_address` from `Test.Next` is encouraged for a smoother migration. - - - -val constant_to_michelson_program : string -> michelson_program - - -let constant_to_michelson_program: (_: string) => michelson_program - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.parse` from `Test.Next` is encouraged for a smoother migration. - -Turn a constant (as a string) into a `michelson_program`. To be - used together with `Test.register_constant`. - - - -val parse_michelson : string -> michelson_program - - -let parse_michelson: (_: string) => michelson_program - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.parse` from `Test.Next` is encouraged for a smoother migration. - -Parses Michelson (as string) into a `michelson_program`. - - - -val restore_context : unit -> unit - - -let restore_context: (_: unit) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.restore` from `Test.Next` is encouraged for a smoother migration. - -Pops a testing framework context from the stack of contexts, and - sets it up as the new current context. In case the stack was - empty, the current context is kept. - - - -val save_context : unit -> unit - - -let save_context: (_: unit) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.save` from `Test.Next` is encouraged for a smoother migration. - -Takes current testing framework context and saves it, pushing it - into a stack of contexts. - - - -val drop_context : unit -> unit - - -let drop_context: (_: unit) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.drop` from `Test.Next` is encouraged for a smoother migration. - -Drops a testing framework context from the stack of contexts. In - case the stack was empty, nothing is done. - - - -val to_string : 'a.'a -> string - - -let to_string: <a>(_: a) => string - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `String.show` from `Test.Next` is encouraged for a smoother migration. - -Converts a value to a string (same conversion as used by - `Test.log`). - - - -val to_json : 'a.'a -> string - - -let to_json: <a>(_: a) => string - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `String.json` from `Test.Next` is encouraged for a smoother migration. - -Converts a value to its JSON representation (as a string). - - - -val to_debugger_json : 'a.'a -> string - - -let to_debugger_json: <a>(_: a) => string - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `String.debugger_json` from `Test.Next` is encouraged for a smoother migration. - - - -val set_baker_policy : test_baker_policy -> unit - - -let set_baker_policy: (_: test_baker_policy) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.set_baker_policy` from `Test.Next` is encouraged for a smoother migration. - -Forces the baking policy for `Test.transfer` and - `Test.originate`. By default, the first bootstrapped account. - - - -val set_baker : address -> unit - - -let set_baker: (_: address) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.set_baker` from `Test.Next` is encouraged for a smoother migration. - -Forces the baker for `Test.transfer` and `Test.originate`, - implemented using `Test.set_baker_policy` with `By_account`. By - default, the first bootstrapped account. - - - -val size : 'p 's.('p, 's) michelson_contract -> int - - -let size: <p, s>(_: michelson_contract<p, s>) => int - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.Contract.size` from `Test.Next` is encouraged for a smoother migration. - -Measures the size of a contract. - - - -val compile_contract : 'p 's.(('p * 's) -> (operation list * 's)) -> ('p, 's) michelson_contract - - -let compile_contract: <p, s>(_: (_: [p, s]) => [list<operation>, s]) => michelson_contract<p, s> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.Contract.compile` from `Test.Next` is encouraged for a smoother migration. - -Compiles a contract from an entrypoint function. - - - -val read_contract_from_file : 'p 's.string -> ('p, 's) michelson_contract - - -let read_contract_from_file: <p, s>(_: string) => michelson_contract<p, s> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.Contract.from_file` from `Test.Next` is encouraged for a smoother migration. - -Reads a contract from a `.tz` file. - - - -val chr : nat -> string option - - -let chr: (_: nat) => option<string> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `String.chr` from `Test.Next` is encouraged for a smoother migration. - -String consisting of the character represented by a `nat` in the - interval `[0, 255]`. - - - -val nl : string - - -let nl: string - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `String.nl` from `Test.Next` is encouraged for a smoother migration. - -String consisting of only a newline. - - - -val println : string -> unit - - -let println: (_: string) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `IO.println` from `Test.Next` is encouraged for a smoother migration. - -Prints an string to stdout, ended with a newline. - - - -val set_print_values : unit -> unit - - -let set_print_values: (_: unit) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `IO.set_test_print` from `Test.Next` is encouraged for a smoother migration. - -Turns on the printing of `test` prefixed values at the end of - tests. This is the default behaviour. - - - -val unset_print_values : unit -> unit - - -let unset_print_values: (_: unit) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `IO.unset_test_print` from `Test.Next` is encouraged for a smoother migration. - -Turns off the printing of `test` prefixed values at the end of - tests. - - - -val get_last_events_from : 'a 'p 's.('p, 's) typed_address -> string -> 'a list - - -let get_last_events_from: <a, p, s>(_: typed_address<p, s>) => (_: string) => list<a> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.last_events` from `Test.Next` is encouraged for a smoother migration. - -Returns the list of all the event payloads emited with a given - tag by a given address. Any call to this function must be - annotated with the expected payload type. - - - -val transfer : 'p 's.('p, 's) typed_address -> 'p -> tez -> test_exec_result - - -let transfer: <p, s>(_: typed_address<p, s>) => (_: p) => (_: tez) => test_exec_result - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Typed_address.transfer` from `Test.Next` is encouraged for a smoother migration. - -Bakes a transaction by sending an amount of tez with a parameter - from the current source to another account. Returns the amount of - gas consumed by the execution of the contract. - - - -val transfer_exn : 'p 's.('p, 's) typed_address -> 'p -> tez -> nat - - -let transfer_exn: <p, s>(_: typed_address<p, s>) => (_: p) => (_: tez) => nat - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Typed_address.transfer_exn` from `Test.Next` is encouraged for a smoother migration. - -Bakes a transaction by sending an amount of tez with a parameter - from the current source to another account. Returns the amount of - gas consumed by the execution of the contract. Similar as - `Test.transfer`, but fails when anything goes wrong. - - - -val log : 'a.'a -> unit - - -let log: <a>(_: a) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `IO.log` from `Test.Next` is encouraged for a smoother migration. - -Logs a value. - - - -val reset_state : nat -> tez list -> unit - - -let reset_state: (_: nat) => (_: list<tez>) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.reset` from `Test.Next` is encouraged for a smoother migration. - -Generates a number of random bootstrapped accounts with a - default amount of `4000000` tez. The passed list can be used to - overwrite the amount. By default, the state only has two - bootstrapped accounts. Notice that since Ithaca, a percentage of - an account's balance is frozen (5% in testing mode) in case the - account can be taken to be a validator, and thus - `Test.get_balance` can show a different amount to the one being - set with `Test.reset_state`. - - - -val reset_state_at : timestamp -> nat -> tez list -> unit - - -let reset_state_at: (_: timestamp) => (_: nat) => (_: list<tez>) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.reset_at` from `Test.Next` is encouraged for a smoother migration. - -Generates a number of random bootstrapped accounts with a - default amount of `4000000` tez. The passed list can be used to - overwrite the amount. By default, the state only has two - bootstrapped accounts. Notice that since Ithaca, a percentage of - an account's balance is frozen (5% in testing mode) in case the - account can be taken to be a validator, and thus - `Test.get_balance` can show a different amount to the one being - set with `Test.reset_state`. It also takes a starting timestamp - for the genesis block. - - - -val bootstrap_contract : 'p 's.(('p * 's) -> (operation list * 's)) -> 's -> tez -> unit - - -let bootstrap_contract: <p, s>(_: (_: [p, s]) => [list<operation>, s]) => (_: s) => (_: tez) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.Reset.add_func_contract` from `Test.Next` is encouraged for a smoother migration. - -Setup a bootstrap contract with an entrypoint function, initial - storage and initial balance. Bootstrap contracts will be loaded in - order, and they will be available only after reset. - - - -val mutate_value : 'a.nat -> 'a -> ('a * mutation) option - - -let mutate_value: <a>(_: nat) => (_: a) => option<[a, mutation]> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.value` from `Test.Next` is encouraged for a smoother migration. - -Mutates a value using a natural number as an index for the - available mutations, returns an option for indicating whether - mutation was successful or not. - - - -val save_mutation : string -> mutation -> string option - - -let save_mutation: (_: string) => (_: mutation) => option<string> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.save` from `Test.Next` is encouraged for a smoother migration. - -This function reconstructs a file from a mutation (second - argument), and saves it to a file in the directory path (first - argument). It returns an optional string indicating the filename - where the mutation was saved, or `None` if there was an error. - - - -val sign : string -> bytes -> signature - - -let sign: (_: string) => (_: bytes) => signature - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Crypto.sign` from `Test.Next` is encouraged for a smoother migration. - -Creates a signature of bytes from a string representing a secret - key, it can be checked with `Crypto.check`. - - - -val add_account : string -> key -> unit - - -let add_account: (_: string) => (_: key) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Account.add` from `Test.Next` is encouraged for a smoother migration. - -Adds an account specfied by secret key & public key to the test - context. - - - -val baker_account : (string * key) -> tez option -> unit - - -let baker_account: (_: [string, key]) => (_: option<tez>) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.Reset.add_baker` from `Test.Next` is encouraged for a smoother migration. - -Adds an account `(sk, pk)` as a baker. The change is only - effective after `Test.reset_state`. - - - -val set_big_map : 'a 'b.int -> ('a, 'b) big_map -> unit - - -let set_big_map: <a, b>(_: int) => (_: big_map<a, b>) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.set_big_map` from `Test.Next` is encouraged for a smoother migration. - -The testing framework keeps an internal reference to the values - corresponding to big map identifiers. This function allows to - override the value of a particular big map identifier. It should - not be normally needed, except in particular circumstances such as - using custom bootstrap contracts that initialize big maps. - - - -val transfer_to_contract : 'p.'p contract -> 'p -> tez -> test_exec_result - - -let transfer_to_contract: <p>(_: contract<p>) => (_: p) => (_: tez) => test_exec_result - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Contract.transfer` from `Test.Next` is encouraged for a smoother migration. - -Bake a transaction by sending an amount of tez with a parameter - from the current source to a contract. Returns the amount of gas - consumed by the execution of the contract. - - - -val transfer_to_contract_exn : 'p.'p contract -> 'p -> tez -> nat - - -let transfer_to_contract_exn: <p>(_: contract<p>) => (_: p) => (_: tez) => nat - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Contract.transfer_exn` from `Test.Next` is encouraged for a smoother migration. - -Bakes a transaction by sending an amount of tez with a parameter - from the current source to a contract. Returns the amount of gas - consumed by the execution of the contract. Similar as - `Test.transfer_to_contract`, but fails when anything goes - wrong. - - - -val michelson_equal : michelson_program -> michelson_program -> bool - - -let michelson_equal: (_: michelson_program) => (_: michelson_program) => bool - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Compare.eq` from `Test.Next` is encouraged for a smoother migration. - -Compares two Michelson values. - - - -val to_entrypoint : 'a 'b 'c.string -> ('a, 'b) typed_address -> 'c contract - - -let to_entrypoint: <a, b, c>(_: string) => (_: typed_address<a, b>) => contract<c> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Typed_address.get_entrypoint` from `Test.Next` is encouraged for a smoother migration. - -Gets the contract corresponding to an entrypoint of a typed - address: the contract parameter in the result will be the type of - the entrypoint, it needs to be annotated, entrypoint string should - omit the prefix "%", but if passed a string starting with "%", it - will be removed (and a warning emitted). - - - -val storage_with_dynamic_entrypoints : - 'p - 's - 's2.('p, 's) module_contract -> - 's2 -> - { - dynamic_entrypoints : dynamic_entrypoints; - storage : 's2 - } - - -let storage_with_dynamic_entrypoints: - <p, s, s2>(_: module_contract<p, s>, s: s2) => { dynamic_entrypoints: dynamic_entrypoints; storage: s2 } - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Dynamic_entrypoints.storage` from `Test.Next` is encouraged for a smoother migration. - - - -val originate_contract : 'p 's.('p, 's) michelson_contract -> 's -> tez -> ('p, 's) typed_address - - -let originate_contract: <p, s>(_: michelson_contract<p, s>) => (_: s) => (_: tez) => typed_address<p, s> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Originate.michelson` from `Test.Next` is encouraged for a smoother migration. - -Originate a contract with initial storage and initial - balance. - - - -val compile_contract_with_views : 'p 's.(('p * 's) -> (operation list * 's)) -> 's views -> ('p, 's) michelson_contract - - -let compile_contract_with_views: - <p, s>(_: (_: [p, s]) => [list<operation>, s]) => (_: views<s>) => michelson_contract<p, s> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.Contract.compile_with_views` from `Test.Next` is encouraged for a smoother migration. - - - -val originate : 'p 's.('p, 's) module_contract -> 's -> tez -> ('p, 's) origination_result - - -let originate: <p, s>(_: module_contract<p, s>) => (_: s) => (_: tez) => origination_result<p, s> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Originate.contract` from `Test.Next` is encouraged for a smoother migration. - -Originate a contract with an entrypoint function in curried - form, initial storage and initial balance. - - - -val compile_contract_from_file : 'p 's.string -> ('p, 's) michelson_contract - - -let compile_contract_from_file: <p, s>(_: string) => michelson_contract<p, s> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.Contract.from_file` from `Test.Next` is encouraged for a smoother migration. - -Compiles a contract with a path to the contract file, an - entrypoint, and a list of views. - - - -val originate_from_file : 'p 's.string -> 's -> tez -> ('p, 's) origination_result - - -let originate_from_file: <p, s>(_: string) => (_: s) => (_: tez) => origination_result<p, s> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Originate.from_file` from `Test.Next` is encouraged for a smoother migration. - -Originate a contract with a path to the contract file, an - entrypoint, and a list of views, together with an initial storage - and an initial balance. - - - -val mutation_test : 'a 'b.'a -> ('a -> 'b) -> ('b * mutation) option - - -let mutation_test: <a, b>(_: a) => (_: (_: a) => b) => option<[b, mutation]> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.func` from `Test.Next` is encouraged for a smoother migration. - -Given a value to mutate (first argument), it will try all the - mutations available of it, passing each one to the function - (second argument). On the first case of non failure when running - the function on a mutation, the value and mutation involved will - be returned. - - - -val mutation_test_all : 'a 'b.'a -> ('a -> 'b) -> ('b * mutation) list - - -let mutation_test_all: <a, b>(_: a) => (_: (_: a) => b) => list<[b, mutation]> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.All.func` from `Test.Next` is encouraged for a smoother migration. - -Given a value to mutate (first argument), it will try all the - mutations of it, passing each one to the function (second - argument). In case no failure arises when running the function on - a mutation, the failure and mutation involved will be added to the - list to be returned. - - - -val originate_from_file_and_mutate : - 'b - 'p - 's.string -> - 's -> tez -> ((('p, 's) typed_address * ('p, 's) michelson_contract * int) -> 'b) -> ('b * mutation) option - - -let originate_from_file_and_mutate: - <b, p, s>(_: string) => (_: s) => (_: tez) => (_: (_: [typed_address<p, s>, michelson_contract<p, s>, int]) => b) => option< - [b, mutation] - > - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.from_file` from `Test.Next` is encouraged for a smoother migration. - -Given a contract from a file (passed by filepath, entrypoint and - views), an initial storage and balance, it will originate mutants - of the contract and pass the result to the function (last - argument). On the first case of non failure when running the - function on a mutation, the value and mutation involved will be - returned. - - - -val originate_from_file_and_mutate_all : - 'b - 'p - 's.string -> 's -> tez -> ((('p, 's) typed_address * ('p, 's) michelson_contract * int) -> 'b) -> ('b * mutation) list - - -let originate_from_file_and_mutate_all: - <b, p, s>(_: string) => (_: s) => (_: tez) => (_: (_: [typed_address<p, s>, michelson_contract<p, s>, int]) => b) => list< - [b, mutation] - > - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.All.from_file` from `Test.Next` is encouraged for a smoother migration. - -Given a contract from a file (passed by filepath, entrypoint and - views), an initial storage and balance, it will originate mutants - of the contract and pass the result to the function (last - argument). In case no failure arises when running the function on - a mutation, the failure and mutation involved will be added to the - list to be returned. - - - -val originate_module_and_mutate : - 'p - 's - 'b.('p, 's) module_contract -> - 's -> tez -> (('p, 's) typed_address -> ('p, 's) michelson_contract -> int -> 'b) -> ('b * mutation) option - - -let originate_module_and_mutate: - <p, s, b>(_: module_contract<p, s>) => (_: s) => (_: tez) => ( - _: (_: typed_address<p, s>) => (_: michelson_contract<p, s>) => (_: int) => b - ) => option<[b, mutation]> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.contract` from `Test.Next` is encouraged for a smoother migration. - -Given a contract as a module/namespace, an initial storage and - balance, it will originate mutants of the contract and pass the - result to the function (last argument). On the first case of non - failure when running the function on a mutation, the value and - mutation involved will be returned. - - - -val originate_and_mutate_all : - 'p - 's - 'b.('p, 's) module_contract -> - 's -> tez -> (('p, 's) typed_address -> ('p, 's) michelson_contract -> int -> 'b) -> ('b * mutation) list - - -let originate_and_mutate_all: - <p, s, b>(_: module_contract<p, s>) => (_: s) => (_: tez) => ( - _: (_: typed_address<p, s>) => (_: michelson_contract<p, s>) => (_: int) => b - ) => list<[b, mutation]> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.All.contract` from `Test.Next` is encouraged for a smoother migration. - -Given a contract as a module/namespace, an initial storage and - balance, it will originate mutants of the contract and pass the - result to the function (last argument). In case no failure arises - when running the function on a mutation, the failure and mutation - involved will be added to the list to be returned. - - - -val assert : bool -> unit - - -let assert: (_: bool) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Assert.assert` from `Test.Next` is encouraged for a smoother migration. - - - -The call `assert cond` terminates the execution with the string - `"failed assertion"` if, and only if, the boolean condition `cond` - is false. The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -The call `assert(cond)` terminates the execution with the string - `"failed assertion"` if, and only if, the boolean condition `cond` - is false. The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -val assert_some : 'a.'a option -> unit - - -let assert_some: <a>(_: option<a>) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Assert.some` from `Test.Next` is encouraged for a smoother migration. - - - -The call `assert_some opt` terminates the execution with the - string `"failed assert some"` if, and only if, `opt` is `None`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -The call `assert_some(opt)` terminates the execution with the - string `"failed assert some"` if, and only if, `opt` is `None()`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -val assert_none : 'a.'a option -> unit - - -let assert_none: <a>(_: option<a>) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Assert.none` from `Test.Next` is encouraged for a smoother migration. - - - -The call `assert_none opt` terminates the execution with the string - `"failed assert none"` if, and only if, `opt` is not `None`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -The call `assert_none(opt)` terminates the execution with the string - `"failed assert none"` if, and only if, `opt` is not `None()`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -val assert_with_error : bool -> string -> unit - - -let assert_with_error: (b: bool, s: string) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Assert.Error.assert` from `Test.Next` is encouraged for a smoother migration. - - - -The call `assert_with_error cond error` terminates the execution - with the string `error` (that is, an error message) if, and only - if, the boolean condition `cond` is false. The failure is handled - by LIGO's testing framework and not by Michelson's interpreter. - - - - - -The call `assert_with_error(cond, error)` terminates the execution - with the string `error` (that is, an error message) if, and only - if, the boolean condition `cond` is false. The failure is handled - by LIGO's testing framework and not by Michelson's interpreter. - - - - - -val assert_some_with_error : 'a.'a option -> string -> unit - - -let assert_some_with_error: <a>(_: option<a>) => (_: string) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Assert.Error.some` from `Test.Next` is encouraged for a smoother migration. - - - -The call `assert_some_with_error opt err` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is `None`. The failure is handled by LIGO's testing - framework and not by Michelson's interpreter. - - - - - -The call `assert_some_with_error(opt, err)` terminates the - execution with the string `err` (that is, an error message) if, - and only if, `opt` is `None()`. The failure is handled by LIGO's - testing framework and not by Michelson's interpreter. - - - - - -val assert_none_with_error : 'a.'a option -> string -> unit - - -let assert_none_with_error: <a>(_: option<a>) => (_: string) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Assert.Error.none` from `Test.Next` is encouraged for a smoother migration. - - - -The call `assert_none_with_error opt err` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is an optional value different from `None`. The failure is - handled by LIGO's testing framework and not by Michelson's - interpreter. - - - - - -The call `assert_none_with_error(opt, err)` terminates the - execution with the string `err` (that is, an error message) if, - and only if, `opt` is an optional value different from - `None()`. The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -val equal : 'a.'a -> 'a -> bool - - -let equal: <a>(_: a) => (_: a) => bool - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Compare.eq` from `Test.Next` is encouraged for a smoother migration. - - - -The call `equal x y` returns `true` if, and only if, `x` and `y` - are considered to be equal w.r.t. the order on the underlying - type. - - - - - -The call `equal(x, y)` returns `true` if, and only if, `x` and `y` - are considered to be equal w.r.t. the order on the underlying - type. - - - - - -val not_equal : 'a.'a -> 'a -> bool - - -let not_equal: <a>(_: a) => (_: a) => bool - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Compare.neq` from `Test.Next` is encouraged for a smoother migration. - - - -The call `not_equal x y` returns `true` if, and only if, `x` and - `y` are not considered to be equal w.r.t. the order on the - underlying type. - - - - - -The call `not_equal(x, y)` returns `true` if, and only if, `x` and - `y` are not considered to be equal w.r.t. the order on the - underlying type. - - - - - -val greater : 'a.'a -> 'a -> bool - - -let greater: <a>(_: a) => (_: a) => bool - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Compare.gt` from `Test.Next` is encouraged for a smoother migration. - - - -The call `greater x y` returns `true` if, and only if, `x` is - considered to be greater than `y` w.r.t. the order on the - underlying type. - - - - - -The call `greater(x, y)` returns `true` if, and only if, `x` is - considered to be greater than `y` w.r.t. the order on the - underlying type. - - - - - -val less : 'a.'a -> 'a -> bool - - -let less: <a>(_: a) => (_: a) => bool - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Compare.lt` from `Test.Next` is encouraged for a smoother migration. - - - -The call `less x y` returns `true` if, and only if, `x` is - considered to be less than `y` w.r.t. the order on the underlying - type. - - - - - -The call `less(x, y)` returns `true` if, and only if, `x` is - considered to be less than `y` w.r.t. the order on the underlying - type. - - - - - -val greater_or_equal : 'a.'a -> 'a -> bool - - -let greater_or_equal: <a>(_: a) => (_: a) => bool - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Compare.ge` from `Test.Next` is encouraged for a smoother migration. - - - -The call `greater_or_equal x y` returns `true` if, and only if, - `x` is considered to be greater or equal than `y` w.r.t. the order - on the underlying type. - - - - - -The call `greater_or_equal(x, y)` returns `true` if, and only if, - `x` is considered to be greater or equal than `y` w.r.t. the order - on the underlying type. - - - - - -val less_or_equal : 'a.'a -> 'a -> bool - - -let less_or_equal: <a>(_: a) => (_: a) => bool - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Compare.le` from `Test.Next` is encouraged for a smoother migration. - - - -The call `less_or_equal x y` returns `true` if, and only if, `x` - is considered to be less or equal than `y` w.r.t. the order on the - underlying type. - - - - - -The call `less_or_equal(x, y)` returns `true` if, and only if, `x` - is considered to be less or equal than `y` w.r.t. the order on the - underlying type. - - - - - -val create_chest : bytes -> nat -> (chest * chest_key) - - -let create_chest: (_: bytes) => (_: nat) => [chest, chest_key] - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Timelock.create` from `Test.Next` is encouraged for a smoother migration. - - - -val create_chest_key : chest -> nat -> chest_key - - -let create_chest_key: (_: chest) => (_: nat) => chest_key - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Timelock.create_key` from `Test.Next` is encouraged for a smoother migration. diff --git a/gitlab-pages/docs/reference/assert.error.md b/gitlab-pages/docs/reference/assert.error.md index 4e8d4813d0..80d14c5088 100644 --- a/gitlab-pages/docs/reference/assert.error.md +++ b/gitlab-pages/docs/reference/assert.error.md @@ -12,21 +12,21 @@ import SyntaxTitle from '@theme/SyntaxTitle'; val assert : bool -> string -> unit -let assert: (_: bool) => (_: string) => unit +assert: (condition: bool, error: string) => unit -The call `assert cond error` terminates the execution - with the string `error` (that is, an error message) if, and only if, - the boolean condition `cond` is false. +The call `assert cond error` terminates the execution with the string +`error` (that is, an error message) if, and only if, the boolean +condition `cond` is false. The call `assert(cond, error)` terminates the execution - with the string `error` (that is, an error message) if, and only if, - the boolean condition `cond` is false. +with the string `error` (that is, an error message) if, and only if, +the boolean condition `cond` is false. @@ -35,21 +35,22 @@ The call `assert(cond, error)` terminates the execution val some : 'a.'a option -> string -> unit -let some: <a>(_: option<a>) => (_: string) => unit +some: <a>(_: option<a>, error: string) => unit + The call `some opt err` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is `None`. +with the string `err` (that is, an error message) if, and only if, +`opt` is `None`. The call `some(opt, err)` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is `None()`. +with the string `err` (that is, an error message) if, and only if, +`opt` is `["None" as "None"]`. @@ -58,20 +59,21 @@ The call `some(opt, err)` terminates the execution val none : 'a.'a option -> string -> unit -let none: <a>(_: option<a>) => (_: string) => unit +none: <a>(_: option<a>, error: string) => unit + -The call `none opt err` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is an optional value different from `None`. +The call `none opt err` terminates the execution with the string `err` +(that is, an error message) if, and only if, `opt` is an optional +value different from `None`. The call `none(opt, err)` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is an optional value different from `None()`. +with the string `err` (that is, an error message) if, and only if, +`opt` is an optional value different from `["None" as "None"]`. diff --git a/gitlab-pages/docs/reference/assert.md b/gitlab-pages/docs/reference/assert.md index aa10d4b6ee..070b14cf3c 100644 --- a/gitlab-pages/docs/reference/assert.md +++ b/gitlab-pages/docs/reference/assert.md @@ -15,7 +15,7 @@ import SyntaxTitle from '@theme/SyntaxTitle'; val assert : bool -> unit -let assert: (_: bool) => unit +assert: (condition: bool) => unit @@ -38,7 +38,7 @@ The call `assert(cond)` terminates the execution with the string val some : 'a.'a option -> unit -let some: <a>(_: option<a>) => unit +some: <a>(_: option<a>) => unit @@ -59,7 +59,7 @@ The call `some(opt)` terminates the execution with the val none : 'a.'a option -> unit -let none: <a>(_: option<a>) => unit +none: <a>(_: option<a>) => unit diff --git a/gitlab-pages/docs/reference/big_map.md b/gitlab-pages/docs/reference/big_map.md index ce2a7b731a..2a5d24d31b 100644 --- a/gitlab-pages/docs/reference/big_map.md +++ b/gitlab-pages/docs/reference/big_map.md @@ -3,31 +3,29 @@ id: big-map-reference title: big_map hide_table_of_contents: true --- + import Syntax from '@theme/Syntax'; import SyntaxTitle from '@theme/SyntaxTitle'; - Maps from keys to values, lazily accessed and where the bindings key/value are ordered by increasing keys. - type ('key, 'value) t = ('key, 'value) big_map type t<key, value> = big_map<key, value> + -The type `('key,'value) Big_map.t` is an alias for - `('key,'value) big_map`. +The type `('key,'value) Big_map.t` is an alias for `('key,'value) big_map`. -The type `Big_map.t` is an alias for `big_map`. +The type `Big_map.t` is an alias for `big_map`. @@ -36,21 +34,22 @@ The type `Big_map.t` is an alias for `big_map -let empty: <key, value>t<key, value> +empty: <key, value>t<key, value> + The value `Big_map.empty` is the empty big map. In some - contexts, it is useful to annotate it with its type, for example: - `(Big_map.empty : (int, string) big_map)`. +contexts, it is useful to annotate it with its type, for example: +`(Big_map.empty : (int, string) big_map)`. The value `Big_map.empty` is the empty big map. In some - contexts, it is useful to annotate it with its type, for example: - `(Big_map.empty as big_map`. +contexts, it is useful to annotate it with its type, for example: +`(Big_map.empty as big_map`. @@ -59,31 +58,33 @@ The value `Big_map.empty` is the empty big map. In some val get_and_update : 'key 'value.'key -> 'value option -> ('key, 'value) t -> ('value option * ('key, 'value) t) -let get_and_update: <key, value>(_: key) => (_: option<value>) => (_: t<key, value>) => [option<value>, t<key, value>] +get_and_update: <key, value>(key: key, upd: option<value>, +map: t<key, value>) => [option<value>, t<key, value>] + The call `Big_map.get_and_update key None map` returns a copy of - the big map `map` without the entry for the key `key` in `map` - (no change if the key is absent). The call - `Big_map.get_and_update key (Some value) map` returns a copy of - the big map `map` where there is an entry for the key `key` - associated with the value `value`. In both cases, if there was - already a value `v` bound to `key`, it is returned as `Some v`, - otherwise `None`. +the big map `map` without the entry for the key `key` in `map` +(no change if the key is absent). The call +`Big_map.get_and_update key (Some value) map` returns a copy of +the big map `map` where there is an entry for the key `key` +associated with the value `value`. In both cases, if there was +already a value `v` bound to `key`, it is returned as `Some v`, +otherwise `None`. -The call `Big_map.get_and_update(key, None(), map)` returns a - copy of the big map `map` without the entry for the key `key` in - `map` (no change if the key is absent). The call - `Big_map.get_and_update(key, Some(value), map)` returns a copy - of the big map `map` where there is an entry for the key `key` - associated with the value `value`. In both cases, if there was - already a value `v` bound to `key`, it is returned as `Some(v)`, - otherwise `None()`. +The call `Big_map.get_and_update(key, ["None" as "None"], map)` +returns a copy of the big map `map` without the entry for the +key `key` in `map` (no change if the key is absent). The call +`Big_map.get_and_update(key, ["Some" as "Some", value], map)` +returns a copy of the big map `map` where there is an entry for +the key `key` associated with the value `value`. In both cases, +if there was already a value `v` bound to `key`, it is returned +as `["Some" as "Some", v]`, otherwise `["None" as "None"]`. *) @@ -92,29 +93,31 @@ The call `Big_map.get_and_update(key, None(), map)` returns a val update : 'key 'value.'key -> 'value option -> ('key, 'value) t -> ('key, 'value) t -let update: <key, value>(_: key) => (_: option<value>) => (_: t<key, value>) => t<key, value> +update: <key, value>(key: key, upd: option<value>, map: t<key, value>) => t<key, value> + The call `Big_map.update key None map` returns a copy of the big - map `map` without the entry for the key `key` in `map` (no - change if the key is absent). The call `Big_map.update key (Some - value) map` returns the big map `map` where there is an entry - for the key `key` associated with the value `value`. In both - cases, the value originally bound to `key` is lost. See - `Big_map.get_and_update`. +map `map` without the entry for the key `key` in `map` (no +change if the key is absent). The call `Big_map.update key (Some +value) map` returns the big map `map` where there is an entry +for the key `key` associated with the value `value`. In both +cases, the value originally bound to `key` is lost. See +`Big_map.get_and_update`. -The call `Big_map.update(key, None(), map)` returns a copy of - the big map `map` without the entry for the key `key` in `map` - (no change if the key is absent). The call `Big_map.update(key, - Some(value), map)` returns the big map `map` where there is an - entry for the key `key` associated with the value `value`. In - both cases, the value originally bound to `key` is lost. See - `Big_map.get_and_update`. +The call `Big_map.update(key, ["None" as "None"], map)` returns +a copy of the big map `map` without the entry for the key `key` +in `map` (no change if the key is absent). The call +`Big_map.update(key, ["Some" as "Some", value], map)` returns +the big map `map` where there is an entry for the key `key` +associated with the value `value`. In both cases, the value +originally bound to `key` is lost. See +`Big_map.get_and_update`. @@ -123,23 +126,24 @@ The call `Big_map.update(key, None(), map)` returns a copy of val add : 'key 'value.'key -> 'value -> ('key, 'value) t -> ('key, 'value) t -let add: <key, value>(_: key) => (_: value) => (_: t<key, value>) => t<key, value> +add: <key, value>(key: key, value: value, map: t<key, value>) => t<key, value> + The call `Big_map.add key value map` returns a copy of the big - map `map` where there is a binding of key `key` to value - `value`. If there is a binding for `key` in `map`, then it is - lost. +map `map` where there is a binding of key `key` to value +`value`. If there is a binding for `key` in `map`, then it is +lost. The call `Big_map.add(key, value, map)` returns a copy of the - big map `map` where there is a binding of key `key` to value - `value`. If there is a binding for `key` in `map`, then it is - lost. +big map `map` where there is a binding of key `key` to value +`value`. If there is a binding for `key` in `map`, then it is +lost. @@ -148,19 +152,20 @@ The call `Big_map.add(key, value, map)` returns a copy of the val remove : 'key 'value.'key -> ('key, 'value) t -> ('key, 'value) t -let remove: <key, value>(_: key) => (_: t<key, value>) => t<key, value> +remove: <key, value>(key: key, map: t<key, value>) => t<key, value> + The call `Big_map.remove key map` returns a copy of the big map - `map` where the binding for key `key` is absent. +`map` where the binding for key `key` is absent. The call `Big_map.remove(key, map)` returns a copy of the big - map `map` where the binding for key `key` is absent. +map `map` where the binding for key `key` is absent. @@ -169,46 +174,47 @@ The call `Big_map.remove(key, map)` returns a copy of the big val literal : 'key 'value.('key * 'value) list -> ('key, 'value) t -let literal: <key, value>(_: list<[key, value]>) => t<key, value> +literal: <key, value>(bindings: list<[key, value]>) => t<key, value> + The call `Big_map.literal [(k1,v1); ...; (kn,vn)]` returns a big - map from the pairs of key/value in the list. Note: The list must - be a literal, not an expression (compile-time list of values). +map from the pairs of key/value in the list. Note: The list must +be a literal, not an expression (compile-time list of values). The call `Big_map.literal(list([[k1,v1], ..., [kn,vn]]))` returns - a big map from the pairs of key/value in the list. Note: The list - must be a literal, not an expression (compile-time list of - values). +a big map from the pairs of key/value in the list. Note: The list +must be a literal, not an expression (compile-time list of +values). -val of_list : 'key 'value.('key * 'value) list -> ('key, 'value) t +val of_list : 'key 'value.('key * 'value) list -> ('key, 'value) t -let of_list: <key, value>(_: list<[key, value]>) => t<key, value> +of_list: <key, value>(bindings: list<[key, value]>) => t<key, value> + The call `Big_map.of_list bindings` returns a big map from the - pairs of key/value in the list `bindings`. Note: Use - `Big_map.literal` instead if using a literal list. +pairs of key/value in the list `bindings`. Note: Use +`Big_map.literal` instead if using a literal list. The call `Big_map.of_list(bindings)` returns a big map from the - pairs of key/value in the list `bindings`. Note: Use - `Big_map.literal` instead if using a literal list. - +pairs of key/value in the list `bindings`. Note: Use +`Big_map.literal` instead if using a literal list. @@ -216,42 +222,45 @@ The call `Big_map.of_list(bindings)` returns a big map from the val mem : 'key 'value.'key -> ('key, 'value) t -> bool -let mem: <key, value>(_: key) => (_: t<key, value>) => bool +mem: <key, value>(key: key, map: t<key, value>) => bool + The call `Big_map.mem key map` is `true` if, and only if, the - key `key` is in the big map `map`. +key `key` is in the big map `map`. The call `Big_map.mem(key, map)` is `true` if, and only if, the - key `key` is in the big map `map`. +key `key` is in the big map `map`. -val find_opt : 'key 'value.'key -> ('key, 'value) t -> 'value option +val find_opt : 'key 'value.'key -> ('key, 'value) t -> 'value option -let find_opt: <key, value>(_: key) => (_: t<key, value>) => option<value> +find_opt: <key, value>(key: key, map: t<key, value>) => option<value> + The call `Big_map.find_opt key map` returns `None` if the key - `key` is present in the big map `map`; otherwise, it is `Some v`, - where `v` is the value associated to `key` in `map`. +`key` is present in the big map `map`; otherwise, it is `Some v`, +where `v` is the value associated to `key` in `map`. -The call `Big_map.find_opt(key, map)` returns `None()` if the - key `key` is present in the big map `map`; otherwise, it is - `Some(v)`, where `v` is the value associated to `key` in `map`. +The call `Big_map.find_opt(key, map)` returns `["None" as "None"]` +if the key `key` is present in the big map `map`; otherwise, it +is `["Some" as "Some", v]`, where `v` is the value associated to +`key` in `map`. @@ -260,20 +269,21 @@ The call `Big_map.find_opt(key, map)` returns `None()` if the val find : 'key 'value.'key -> ('key, 'value) t -> 'value -let find: <key, value>(_: key) => (_: t<key, value>) => value +find: <key, value>(key: key, map: t<key, value>) => value + The call `Big_map.find key map` returns the value associated to - `key` in the big map `map`. If the key is absent, the execution - fails with the string `"MAP FIND"`. +`key` in the big map `map`. If the key is absent, the execution +fails with the string `"MAP FIND"`. The call `Big_map.find(key, map)` returns the value associated - to `key` in the big map `map`. If the key is absent, the - execution fails with the string `"MAP FIND"`. +to `key` in the big map `map`. If the key is absent, the +execution fails with the string `"MAP FIND"`. diff --git a/gitlab-pages/docs/reference/big_set.md b/gitlab-pages/docs/reference/big_set.md index 77764ae43a..995f6bef33 100644 --- a/gitlab-pages/docs/reference/big_set.md +++ b/gitlab-pages/docs/reference/big_set.md @@ -56,7 +56,7 @@ The value `Big_set.empty` denotes the empty big set. In some contexts, val update : 'elt.'elt -> bool -> 'elt t -> 'elt t -let update: <elt>(_: elt) => (_: bool) => (_: t<elt>) => t<elt> +let update: <elt>(elt: elt, add: bool, set: t<elt>) => t<elt> @@ -80,7 +80,7 @@ The call `Big_set.update(elt, true, set)` is a copy of the big set `set` val add : 'elt.'elt -> 'elt t -> 'elt t -let add: <elt>(_: elt) => (_: t<elt>) => t<elt> +let add: <elt>(elt: elt, set: t<elt>) => t<elt> @@ -101,7 +101,7 @@ The call `Big_set.add(elt, set)` is a big set containing all the elements val remove : 'elt.'elt -> 'elt t -> 'elt t -let remove: <elt>(_: elt) => (_: t<elt>) => t<elt> +let remove: <elt>(elt: elt, set: t<elt>) => t<elt> @@ -168,7 +168,7 @@ The call `Big_set.of_list(elements)` is a big set containing exactly the val mem : 'elt.'elt -> 'elt t -> bool -let mem: <elt>(_: elt) => (_: t<elt>) => bool +let mem: <elt>(elt: elt, set: t<elt>) => bool diff --git a/gitlab-pages/docs/reference/bitwise.md b/gitlab-pages/docs/reference/bitwise.md index be980df6df..7c96fe2ad9 100644 --- a/gitlab-pages/docs/reference/bitwise.md +++ b/gitlab-pages/docs/reference/bitwise.md @@ -14,100 +14,122 @@ Bitwise operations val and : 'a 'b.'a -> 'b -> ('a, 'b) external_and -let and: <a, b>(_: a) => (_: b) => external_and<a, b> +and: <a, b>(left: a, right: b) => external_and<a, b> -The call `Bitiwise.@and a b` is the conjunction defined on boolean, - natural number and bytes operands. In the boolean case, the result - is the logical "and" of the operands. In the natural number and - bytes cases, the result is the bitwise "and" of the operands. - The function `Bitwise.@and` is also defined when the left operand is of - type `int`. Negative numbers are considered in two's complement - representation, starting with a virtual infinite number of 1s. +The call `Bitiwise.and a b` is the conjunction defined on boolean, +natural number and bytes operands. In the boolean case, the result is +the logical "and" of the operands. In the natural number and bytes +cases, the result is the bitwise "and" of the operands. - When `Bitwise.@and` is used for bytes operands, the bytes result - has the same length as the shorter operand. The prefix of the - longer operand is cut to match with the length of the shorter one - before taking the bitwise "and". +The function `Bitwise.and` is also defined when the left operand is +of type `int`. Negative numbers are considered in two's complement +representation, starting with a virtual infinite number of 1s. + +When `Bitwise.and` is used for bytes operands, the bytes result has +the same length as the shorter operand. The prefix of the longer +operand is cut to match with the length of the shorter one before +taking the bitwise "and". -val or : 'a 'b.'a -> 'b -> ('a, 'b) external_or +val @or : 'a 'b.'a -> 'b -> ('a, 'b) external_or -let or: <a, b>(_: a) => (_: b) => external_or<a, b> +or: <a, b>(left: a, right: b) => external_or<a, b> + + + The call `Bitwise.@or a b` is the disjunction defined on boolean, - natural number and bytes operands. In the boolean case, the result - is the logical "or" of the operands. In the natural number and - bytes cases, the result is the bitwise "or" of the operands. +natural number and bytes operands. In the boolean case, the result is +the logical "or" of the operands. In the natural number and bytes +cases, the result is the bitwise "or" of the operands. + +When the function `Bitwise.@or` is used for bytes operands, the result +bytes has the same length as the longer operand. The shorter operand +is zero-padded on the left to match with the length of the longer one +before taking the bitwise "or". + + + + - When the function `Bitwise.@or` is used for bytes operands, the result - bytes has the same length as the longer operand. The shorter - operand is zero-padded on the left to match with the length of the - longer one before taking the bitwise "or". +The call `Bitwise.or a b` is the disjunction defined on boolean, +natural number and bytes operands. In the boolean case, the result is +the logical "or" of the operands. In the natural number and bytes +cases, the result is the bitwise "or" of the operands. + +When the function `Bitwise.or` is used for bytes operands, the result +bytes has the same length as the longer operand. The shorter operand +is zero-padded on the left to match with the length of the longer one +before taking the bitwise "or". + + val xor : 'a 'b.'a -> 'b -> ('a, 'b) external_xor -let xor: <a, b>(_: a) => (_: b) => external_xor<a, b> +xor: <a, b>(left: a, right: b) => external_xor<a, b> + The call `Bitwise.xor a b` is the exclusive disjunction defined on - boolean, natural number and bytes operands. In the boolean case, - the result is the logical "exclusive or" of the operands. In the - natural number and bytes cases, the result is the bitwise "xor" of - the operands. +boolean, natural number and bytes operands. In the boolean case, the +result is the logical "exclusive or" of the operands. In the natural +number and bytes cases, the result is the bitwise "xor" of the +operands. - When `Bitwise.xor` is used for bytes operands, the result bytes - has the same length as the longer operand. The shorter operand is - zero-padded on the left to match with the length of the longer one - before taking the bitwise "xor". +When `Bitwise.xor` is used for bytes operands, the result bytes has +the same length as the longer operand. The shorter operand is +zero-padded on the left to match with the length of the longer one +before taking the bitwise "xor". -val shift_left : 'a 'b.'a -> 'b -> ('a, 'b) external_lsl +val shift_left : 'a 'b.'a -> 'b -> ('a, 'b) external_lsl -let shift_left: <a, b>(_: a) => (_: b) => external_lsl<a, b> +shift_left: <a, b>(left: a, right: b) => external_lsl<a, b> + The function `Bitwise.shift_left` on natural numbers consumes two - natural numbers and produces the first number logically - left-shifted by the second number. This instruction is only - defined if the second number is less than or equal to 256. - - For bytes, the function `Biwise.shift_left` consumes one byte sequence - and one natural number, and produces the bytes logically - left-shifted by the natural number. The vacated bits on the right - are filled with zeros. The shifted bits are minimally zero-padded - on the left in order to keep all the original bits, regardless if - they are 0 or 1: for example, `Bitwise.shift_left 0x1234 1` is `0x002468`, - instead of `0x2468` (even though in this case no significant bit - would be lost) or `0x00002468` (where padding is not minimal). The - length of the bytes returned by `Bitwise.shift_left` is `l + (s + 7) / 8` - bytes where `l` is the length of the original bytes and `s` is the - natural number. This instruction is only defined if the second - number is less than or equal to 64000. +natural numbers and produces the first number logically left-shifted +by the second number. This instruction is only defined if the second +number is less than or equal to 256. + +For bytes, the function `Biwise.shift_left` consumes one byte sequence +and one natural number, and produces the bytes logically left-shifted +by the natural number. The vacated bits on the right are filled with +zeros. The shifted bits are minimally zero-padded on the left in order +to keep all the original bits, regardless if they are 0 or 1: for +example, `Bitwise.shift_left 0x1234 1` is `0x002468`, instead of +`0x2468` (even though in this case no significant bit would be lost) +or `0x00002468` (where padding is not minimal). The length of the +bytes returned by `Bitwise.shift_left` is `l + (s + 7) / 8` bytes +where `l` is the length of the original bytes and `s` is the natural +number. This instruction is only defined if the second number is less +than or equal to 64000. -val shift_right : 'a 'b.'a -> 'b -> ('a, 'b) external_lsr +val shift_right : 'a 'b.'a -> 'b -> ('a, 'b) external_lsr -let shift_right: <a, b>(_: a) => (_: b) => external_lsr<a, b> +shift_right: <a, b>(left: a, right: b) => external_lsr<a, b> + The function `Bitwise.shift_right` on natural numbers consumes two - natural numbers and produces the first number logically - right-shifted by second number. This function is only defined if - the second number is less than or equal to 256. - - For bytes, the function `Bitwise.shift_right` consumes one chunk of bytes - and one natural number and produces the bytes logically - right-shifted by the natural number. The shifted bits are - minimally zero-padded on the left. For example, `Bitwise.shift_right - 0x012349 9` is `0x0091`, instead of `0x91` (where the 7 left-most - bits are lost) or `0x000091` (not minimal padding). The length of - the returned bytes by `Bitwise.shift_right` is `max 0 (l - s / 8)` bytes, - where `l` is the length of the original bytes, and `s` is the - natural number. +natural numbers and produces the first number logically right-shifted +by second number. This function is only defined if the second number +is less than or equal to 256. + +For bytes, the function `Bitwise.shift_right` consumes one chunk of +bytes and one natural number and produces the bytes logically +right-shifted by the natural number. The shifted bits are minimally +zero-padded on the left. For example, `Bitwise.shift_right 0x012349 9` +is `0x0091`, instead of `0x91` (where the 7 left-most bits are lost) +or `0x000091` (not minimal padding). The length of the returned bytes +by `Bitwise.shift_right` is `max 0 (l - s / 8)` bytes, where `l` is +the length of the original bytes, and `s` is the natural number. diff --git a/gitlab-pages/docs/reference/bytes.md b/gitlab-pages/docs/reference/bytes.md index 2f5bcbb807..716bcfb5ff 100644 --- a/gitlab-pages/docs/reference/bytes.md +++ b/gitlab-pages/docs/reference/bytes.md @@ -9,30 +9,29 @@ import SyntaxTitle from '@theme/SyntaxTitle'; Sequences of bytes - Bytes are used for serializing data, in order to check signatures - and compute hashes on them. They can also be used to read untyped - data from outside of the contract. +Bytes are used for serializing data, in order to check signatures +and compute hashes on them. They can also be used to read untyped +data from outside of the contract. val length : bytes -> nat -let length: (_: bytes) => nat +length: (_: bytes) => nat + The call `Bytes.length b` is the number of bytes in the sequence of - bytes `b`. Note: `Bytes.length` is another name for - `Bytes.size`. +bytes `b`. Note: `Bytes.length` is another name for `Bytes.size`. The call `Bytes.length(b)` is the number of bytes in the sequence of - bytes `b`. Note: `Bytes.length` is another name for - `Bytes.size`. +bytes `b`. Note: `Bytes.length` is another name for `Bytes.size`. @@ -41,19 +40,20 @@ The call `Bytes.length(b)` is the number of bytes in the sequence of val size : bytes -> nat -let size: (_: bytes) => nat +size: (_: bytes) => nat + The call `Bytes.size b` is the number of bytes in the sequence of - bytes `b`. +bytes `b`. The call `Bytes.size(b)` is the number of bytes in the sequence of - bytes `b`. +bytes `b`. @@ -62,21 +62,20 @@ The call `Bytes.size(b)` is the number of bytes in the sequence of val concat : bytes -> bytes -> bytes -let concat: (_: bytes) => (_: bytes) => bytes +concat: (_: bytes) => (_: bytes) => bytes + The call `Bytes.concat left right` is the sequence of bytes obtained - by concatenating the sequence `left` before the sequence - `right`. +by concatenating the sequence `left` before the sequence `right`. The call `Bytes.concat(left, right)` is the sequence of bytes obtained - by concatenating the sequence `left` before the sequence - `right`. +by concatenating the sequence `left` before the sequence `right`. @@ -85,19 +84,20 @@ The call `Bytes.concat(left, right)` is the sequence of bytes obtained val concats : bytes list -> bytes -let concats: (_: list<bytes>) => bytes +concats: (_: list<bytes>) => bytes + The call `Bytes.concats list` is the concatenation of the byte - sequences in the list `list`, from left to right. +sequences in the list `list`, from left to right. The call `Bytes.concats(list)` is the concatenation of the byte - sequences in the list `list`, from left to right. +sequences in the list `list`, from left to right. @@ -106,23 +106,24 @@ The call `Bytes.concats(list)` is the concatenation of the byte val sub : nat -> nat -> bytes -> bytes -let sub: (_: nat) => (_: nat) => (_: bytes) => bytes +sub: (index: nat, length: nat, bytes: bytes) => bytes + The call `Bytes.sub index len bytes` is the subsequence of bytes - `bytes` starting at index `index` (0 denoting the first byte) and - of length `len`. If the index or length are invalid, an exception - interrupts the execution. +`bytes` starting at index `index` (0 denoting the first byte) and +of length `len`. If the index or length are invalid, an exception +interrupts the execution. The call `Bytes.sub(index, len, bytes)` is the subsequence of bytes - `bytes` starting at index `index` (0 denoting the first byte) and - of length `len`. If the index or length are invalid, an exception - interrupts the execution. +`bytes` starting at index `index` (0 denoting the first byte) and +of length `len`. If the index or length are invalid, an exception +interrupts the execution. @@ -131,23 +132,24 @@ The call `Bytes.sub(index, len, bytes)` is the subsequence of bytes val slice : nat -> nat -> bytes -> bytes -let slice: (_: nat) => (_: nat) => (_: bytes) => bytes +slice: (index: nat, length: nat, bytes: bytes) => bytes + The call `Bytes.slice index len bytes` is the subsequence of bytes - `bytes` starting at index `index` (0 denoting the first byte) and - of length `len`. If the index or length are invalid, an exception - interrupts the execution. +`bytes` starting at index `index` (0 denoting the first byte) and +of length `len`. If the index or length are invalid, an exception +interrupts the execution. The call `Bytes.slice(index, len, bytes)` is the subsequence of bytes - `bytes` starting at index `index` (0 denoting the first byte) and - of length `len`. If the index or length are invalid, an exception - interrupts the execution. +`bytes` starting at index `index` (0 denoting the first byte) and +of length `len`. If the index or length are invalid, an exception +interrupts the execution. @@ -156,19 +158,20 @@ The call `Bytes.slice(index, len, bytes)` is the subsequence of bytes val pack : 'a.'a -> bytes -let pack: <a>(_: a) => bytes +pack: <a>(_: a) => bytes + The call `Bytes.pack v` transforms the value `v` into a sequence of - bytes. +bytes. The call `Bytes.pack(v)` transforms the value `v` into a sequence of - bytes. +bytes. @@ -177,19 +180,20 @@ The call `Bytes.pack(v)` transforms the value `v` into a sequence of val unpack : 'a.bytes -> 'a option -let unpack: <a>(_: bytes) => option<a> +unpack: <a>(_: bytes) => option<a> + The call `Bytes.unpack bytes` is `Some v` if the sequence of bytes - `bytes` decodes into a valid LIGO value `v`; otherwise `None`. +`bytes` decodes into a valid LIGO value `v`; otherwise `None`. -The call `Bytes.unpack(bytes)` is `Some(v)` if the sequence of bytes - `bytes` decodes into a valid LIGO value `v`; otherwise - `None()`. +The call `Bytes.unpack(bytes)` is `["Some" as "Some", v]` if the +sequence of bytes `bytes` decodes into a valid LIGO value `v`; +otherwise `["None" as "None"]`. diff --git a/gitlab-pages/docs/reference/crypto.md b/gitlab-pages/docs/reference/crypto.md index a15368b7f3..a3d71fa1b4 100644 --- a/gitlab-pages/docs/reference/crypto.md +++ b/gitlab-pages/docs/reference/crypto.md @@ -14,7 +14,7 @@ Cryptographic primitives val blake2b : bytes -> bytes -let blake2b: (_: bytes) => bytes +blake2b: (_: bytes) => bytes Compute the cryptographic hash of the top of the stack using the Blake2b-256 cryptographic hash function. @@ -24,59 +24,60 @@ Compute the cryptographic hash of the top of the stack using the val sha256 : bytes -> bytes -let sha256: (_: bytes) => bytes +sha256: (_: bytes) => bytes + Compute the cryptographic hash of the top of the stack using the - SHA-256 cryptographic hash function. +SHA-256 cryptographic hash function. val sha512 : bytes -> bytes -let sha512: (_: bytes) => bytes +sha512: (_: bytes) => bytes Compute the cryptographic hash of the top of the stack using the - SHA-512 cryptographic hash function. +SHA-512 cryptographic hash function. val sha3 : bytes -> bytes -let sha3: (_: bytes) => bytes +sha3: (_: bytes) => bytes + Compute the cryptographic hash of the top of the stack using the - SHA3-256 cryptographic hash function. +SHA3-256 cryptographic hash function. val keccak : bytes -> bytes -let keccak: (_: bytes) => bytes +keccak: (_: bytes) => bytes + Compute the cryptographic hash of the top of the stack using the - Keccak-256 cryptographic hash function. +Keccak-256 cryptographic hash function. val hash_key : key -> key_hash -let hash_key: (_: key) => key_hash +hash_key: (_: key) => key_hash -The call `hash_key k` computes the Base58Check of the public key - `k`. +The call `hash_key k` computes the Base58Check of the public key `k`. -The call `hash_key(k)` computes the Base58Check of the public key - `k`. +The call `hash_key(k)` computes the Base58Check of the public key `k`. @@ -85,22 +86,23 @@ The call `hash_key(k)` computes the Base58Check of the public key val check : key -> signature -> bytes -> bool -let check: (_: key) => (_: signature) => (_: bytes) => bool +check: (key: key, sig: signature, bytes: bytes) => bool + The call `Crypto.check k s b` verifies that the byte sequence `b` has - been signed with the key `k`: it is `true` if, and only if, the - signature `s` is a valid signature of the byte sequence created - with `k`. +been signed with the key `k`: it is `true` if, and only if, the +signature `s` is a valid signature of the byte sequence created with +`k`. -The call `Crypto.check(k, s, b)` verifies that the byte sequence `b` has - been signed with the key `k`: it is `true` if, and only if, the - signature `s` is a valid signature of the byte sequence created - with `k`. +The call `Crypto.check(k, s, b)` verifies that the byte sequence `b` +has been signed with the key `k`: it is `true` if, and only if, the +signature `s` is a valid signature of the byte sequence created with +`k`. diff --git a/gitlab-pages/docs/reference/current.md b/gitlab-pages/docs/reference/current.md index c4eb4828ca..aaeb64ad2a 100644 --- a/gitlab-pages/docs/reference/current.md +++ b/gitlab-pages/docs/reference/current.md @@ -13,7 +13,7 @@ val get_balance : unit -> tez -let get_balance: (_u: unit) => tez +get_balance: (_u: unit) => tez Get the balance for the contract. @@ -29,7 +29,7 @@ let check (p,s : unit * tez) = [], Tezos.get_balance() ```jsligo -let check = (p: unit, s: tez):[list, tez] => +const check = (p: unit, s: tez):[list, tez] => [[], Tezos.get_balance()]; ``` @@ -40,7 +40,7 @@ val get_now : unit -> timestamp -let get_now: (_u : unit) => timestamp +get_now: (_u : unit) => timestamp Returns the current time as a [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time). @@ -68,11 +68,11 @@ let one_day_later = some_date + one_day ```jsligo group=b -let today = Tezos.get_now(); -let one_day = 86_400; -let in_24_hrs = today + one_day; -let some_date = ("2000-01-01t10:10:10Z" as timestamp); -let one_day_later = some_date + one_day; +const today = Tezos.get_now(); +const one_day = 86_400; +const in_24_hrs = today + one_day; +const some_date = ("2000-01-01t10:10:10Z" as timestamp); +const one_day_later = some_date + one_day; ``` @@ -93,9 +93,9 @@ let in_24_hrs = today - one_day ```jsligo group=c -let today = Tezos.get_now(); -let one_day = 86_400; -let in_24_hrs = today - one_day; +const today = Tezos.get_now(); +const one_day = 86_400; +const in_24_hrs = today - one_day; ``` @@ -117,7 +117,7 @@ let not_tomorrow = (Tezos.get_now () = in_24_hrs) ```jsligo group=c -let not_tomorrow = (Tezos.get_now() == in_24_hrs); +const not_tomorrow = (Tezos.get_now() == in_24_hrs); ``` @@ -127,7 +127,7 @@ val get_amount : unit -> tez -let get_amount: (_u : unit) => tez +get_amount: (_u : unit) => tez Get the amount of tez provided by the sender to complete this @@ -145,7 +145,7 @@ let threshold (p : unit) = if Tezos.get_amount () = 100tz then 42 else 0 ```jsligo function threshold (p : unit) { - if (Tezos.get_amount() == 100tez) return 42 else return 0; + if (Tezos.get_amount() == (100 as tez)) return 42; else return 0; }; ``` @@ -156,14 +156,14 @@ val get_sender : unit -> address -let get_sender: (_u : unit) => address +get_sender: (_u : unit) => address Get the address that initiated the current transaction. -```cameligo +```cameligo group=e let check (p : unit) = Tezos.get_sender () ``` @@ -172,7 +172,7 @@ let check (p : unit) = Tezos.get_sender () ```jsligo group=e -let check = (p : unit) => Tezos.get_sender (); +const check = (p : unit) => Tezos.get_sender(); ``` @@ -182,14 +182,14 @@ val address : 'a contract -> address -let address: (contract: contract<'a>) => address +address: (contract: contract<'a>) => address Get the address associated with a value of type `contract`. -```cameligo +```cameligo group=f let check (p : key_hash) = let c = Tezos.implicit_account p in Tezos.address c @@ -200,8 +200,8 @@ let check (p : key_hash) = ```jsligo group=f -let check = (p : key_hash) => { - let c = Tezos.implicit_account(p); +const check = (p : key_hash) => { + const c = Tezos.implicit_account(p); return Tezos.address(c); }; ``` @@ -213,14 +213,14 @@ val get_self_address : unit -> address -let get_self_address: (_u : unit) => address +get_self_address: (_u : unit) => address Get the address of the currently running contract. -```cameligo +```cameligo group=g let check (p : unit) = Tezos.get_self_address () ``` @@ -229,7 +229,7 @@ let check (p : unit) = Tezos.get_self_address () ```jsligo group=g -let check = (p : unit) => Tezos.get_self_address(); +const check = (p : unit) => Tezos.get_self_address(); ``` @@ -239,7 +239,7 @@ val self : string -> 'a contract -let self: (entrypoint: string) => contract<'a> +self: (entrypoint: string) => contract<'a> Typecast the currently running contract with an entrypoint annotation. @@ -248,7 +248,7 @@ you are not using entrypoints: use "%default" -```cameligo +```cameligo group=h let check (p : unit) = Tezos.self("%default") ``` @@ -257,7 +257,7 @@ let check (p : unit) = Tezos.self("%default") ```jsligo group=h -let check = (p: unit) => Tezos.self("%default"); +const check = (p: unit) => Tezos.self("%default"); ``` @@ -267,7 +267,7 @@ val implicit_account : key_hash -> 'a contract -let implicit_account : (_: key_hash) => contract<unit> +implicit_account : (_: key_hash) => contract<unit> Get the default contract associated with an on-chain key-pair. This @@ -278,7 +278,7 @@ See also: https://octez.tezos.com/docs/active/glossary.html#user-account -```cameligo +```cameligo group=i let check (kh : key_hash) = Tezos.implicit_account kh ``` @@ -287,7 +287,7 @@ let check (kh : key_hash) = Tezos.implicit_account kh ```jsligo group=i -let check = (kh: key_hash) => Tezos.implicit_account(kh); +const check = (kh: key_hash) => Tezos.implicit_account(kh); ``` @@ -297,7 +297,7 @@ val get_source : unit -> address -let get_source: (_u : unit) => address +get_source: (_u : unit) => address Get the _originator_ (address) of the current transaction. That is, if @@ -328,7 +328,7 @@ current transaction. -```cameligo +```cameligo group=j let check (p : unit) = Tezos.get_source () ``` @@ -337,7 +337,7 @@ let check (p : unit) = Tezos.get_source () ```jsligo group=j -let check = (p : unit) => Tezos.get_source(); +const check = (p : unit) => Tezos.get_source(); ``` @@ -347,7 +347,7 @@ val failwith : 'a -> unit -let failwith: (message: 'a) => unit +failwith: (message: 'a) => unit [See `failwith`](toplevel.md#failwith) @@ -357,7 +357,7 @@ val get_chain_id : unit -> chain_id -let get_chain_id: (_u : unit) => chain_id +get_chain_id: (_u : unit) => chain_id Get the identifier of the chain to distinguish between main and test chains. @@ -367,16 +367,16 @@ only be used together with `Bytes.pack` and `Bytes.unpack`. -```cameligo +```cameligo group=k type storage = bytes [@entry] -let main (_ignore : unit) (store : storage) = +let main (_ignore : unit) (store : storage) : operation list * storage += let packed = Bytes.pack (Tezos.get_chain_id ()) in - if (store <> packed) then - (failwith "wrong chain" : (operation list * storage)) - else - ([], (packed: storage)) + if store <> packed then + (failwith "wrong chain" : operation list * storage) + else ([], packed) ``` @@ -386,14 +386,12 @@ let main (_ignore : unit) (store : storage) = ```jsligo group=k type storage = bytes; -@entry -let main = (_ignore: unit, storage: storage) : [list, storage] => { - let packed = Bytes.pack(Tezos.get_chain_id()); - if (storage != packed) { +// @entry +const main = (_ignore: unit, storage: storage) : [list, storage] => { + const packed = Bytes.pack(Tezos.get_chain_id()); + if (storage != packed) return failwith("wrong chain") as [list, storage]; - } else { - return [[], packed]; - }; + else return [[], packed]; }; ``` @@ -404,7 +402,7 @@ val transaction : 'param -> mutez -> 'param contract -> operation -let transaction: (action: 'param, amount: mutez, contract: contract<'param>) => operation +transaction: (action: 'param, amount: mutez, contract: contract<'param>) => operation Transfer `tez` to an account, or run code of another smart contract. @@ -416,7 +414,7 @@ val create_contract : ('param -> 'storage -> operation list * 'storage) -> key_h -let create_contract = (contract: ('param, 'storage) => (list <operation>, 'storage), delegate: option<key_hash>, balance: tez, init: 'storage) => [operation, address] +create_contract = (contract: ('param, 'storage) => (list <operation>, 'storage), delegate: option<key_hash>, balance: tez, init: 'storage) => [operation, address] Construct an operation that originates a contract from a function. The @@ -428,7 +426,7 @@ val set_delegate : key_hash option -> operation -let set_delegate: (delegate: option<key_hash>) => operation +set_delegate: (delegate: option<key_hash>) => operation @@ -446,7 +444,7 @@ val get_contract_opt : address -> 'param contract option -let get_contract_opt : (a: address) => option<contract<'param>> +get_contract_opt : (a: address) => option<contract<'param>> Get a contract from an address. @@ -459,7 +457,7 @@ val get_contract_with_error : address -> string -> 'param contract -let get_contract_with_error : (a: address,s: string) => contract<'param> +get_contract_with_error : (a: address,s: string) => contract<'param> Get a contract from an address. @@ -471,7 +469,7 @@ val get_entrypoint_opt : string -> address -> 'param contract option -let get_entrypoint_opt: (entrypoint: string, a: address) => option<contract<'param>> +get_entrypoint_opt: (entrypoint: string, a: address) => option<contract<'param>> Get a contract from an address and entrypoint. @@ -486,7 +484,7 @@ val get_level : unit -> nat -let get_level : (_u : unit) => nat +get_level : (_u : unit) => nat Get the current block level. @@ -496,7 +494,7 @@ val min_block_time : unit -> nat -let min_block_time: unit => nat; +min_block_time: unit => nat; Returns the current minimal time between blocks, the value is obtained from the protocol’s minimal_block_delay constant. @@ -506,7 +504,7 @@ val pairing_check : (bls12_381_g1 * bls12_381_g2) list -> bool -let pairing_check: list<[bls12_381_g1, bls12_381_g2]>) => bool +pairing_check: list<[bls12_381_g1, bls12_381_g2]>) => bool Verify that the product of pairings of the given list of points is equal to 1 in Fq12. Returns true if the list is empty. @@ -518,7 +516,7 @@ val never : never -> 'a -let never: (never: never) => 'a +never: (never: never) => 'a Eliminate a value of the type `never` using the instruction `NEVER` @@ -529,7 +527,7 @@ val get_total_voting_power : unit -> nat -let get_total_voting_power: (_u : unit) => nat +get_total_voting_power: (_u : unit) => nat Return the total voting power of all contracts. The total voting power coincides with the sum of the rolls count of every contract in the voting listings. The voting listings is calculated at the beginning of every voting period. @@ -539,7 +537,7 @@ val voting_power : key_hash -> nat -let voting_power: (key_hash:key_hash) => nat +voting_power: (key_hash:key_hash) => nat Return the voting power of a given contract. The voting power value is the full staking power of the delegate, currently expressed in mutez. Though, developers should not rely on `Tezos.voting_power` to query the staking power of a contract in mutez: the value returned by `Tezos.voting_power` is still of type` nat and it should only be considered relative to `Tezos.total_voting_power`. @@ -567,17 +565,17 @@ type tr = sapling_transaction<8>; -val sapling_empty_state : 'n sapling_state +val empty_state : 'n sapling_state -let sapling_empty_state: sapling_state<n> +empty_state: sapling_state<n> ```cameligo group=sap_t -let x = Tezos.sapling_empty_state +let x = Tezos.Sapling.empty_state ``` @@ -585,7 +583,7 @@ let x = Tezos.sapling_empty_state ```jsligo group=sap_t -let x = Tezos.sapling_empty_state ; +const x = Tezos.Sapling.empty_state; ``` @@ -593,11 +591,11 @@ let x = Tezos.sapling_empty_state ; Sapling empty state -val sapling_verify_update : 'a sapling_transaction -> 'a sapling_state -> (bytes * (int * 'a sapling_state)) option +val verify_update : 'a sapling_transaction -> 'a sapling_state -> (bytes * (int * 'a sapling_state)) option -let sapling_verify_update: sapling_transaction<'a> => sapling_state<'a> => option<[bytes, [int, sapling_state<'a>]]> +verify_update: sapling_transaction<'a> => sapling_state<'a> => option<[bytes, [int, sapling_state<'a>]]> @@ -607,7 +605,7 @@ Verify sapling update ```cameligo group=sap_t let f (tr : tr) = - match Tezos.sapling_verify_update tr x with + match Tezos.Sapling.verify_update tr x with Some (_, x) -> x | None -> (failwith "failed" : int * st) ``` @@ -617,11 +615,11 @@ let f (tr : tr) = ```jsligo group=sap_t -let f = (tr : tr) => - match (Tezos.sapling_verify_update(tr, x)) { - when(Some(p)): p[1]; - when(None()): failwith ("failed") - }; +const f = (tr : tr) => + $match(Tezos.Sapling.verify_update(tr, x), { + "Some": p => p[1], + "None": () => failwith ("failed") + }); ``` @@ -640,7 +638,8 @@ type result = operation list * storage [@entry] let main (i : parameter) (store : storage) : result = - let my_ticket1 = Option.unopt (Tezos.create_ticket i 10n) in + let my_ticket1 = + Option.value_with_error "no ticket" (Tezos.Ticket.create i 10n) in let _, x = Big_map.get_and_update "hello" (Some my_ticket1) store in [], x ``` @@ -650,16 +649,16 @@ let main (i : parameter) (store : storage) : result = ```jsligo group=contract_ticket -type storage = big_map> ; - -type parameter = int ; - +type storage = big_map>; +type parameter = int; type result = [list, storage]; -@entry +// @entry function main (i: parameter, store : storage): result { - let my_ticket1 = Option.unopt (Tezos.create_ticket (i, 10n)); - let [_x, ret] = Big_map.get_and_update ("hello", Some(my_ticket1), store); + const my_ticket1 = + Option.value_with_error ("no ticket", Tezos.Ticket.create(i, 10 as nat)); + const [_x, ret] = + Big_map.get_and_update ("hello", ["Some" as "Some", my_ticket1], store); return [[], ret] }; ``` @@ -674,7 +673,7 @@ val open_chest : chest_key -> chest -> nat -> bytes option -let open_chest : (key: chest_key, chest: chest, time: nat) => option<bytes> +open_chest : (key: chest_key, chest: chest, time: nat) => option<bytes> Open a timelocked chest given its key and the time. diff --git a/gitlab-pages/docs/reference/decorators/annot.md b/gitlab-pages/docs/reference/decorators/annot.md index 53e6399168..d91a8c8fb3 100644 --- a/gitlab-pages/docs/reference/decorators/annot.md +++ b/gitlab-pages/docs/reference/decorators/annot.md @@ -57,9 +57,11 @@ For example, this code assigns annotations to the fields in a record: ```jsligo group=annot type transfer = - @layout("comb") - { @annot("from") address_from: address; - @annot("to") address_to: address; + // @layout("comb") + { // @annot("from") + address_from: address; + // @annot("to") + address_to: address; value: nat } ``` diff --git a/gitlab-pages/docs/reference/decorators/deprecated.md b/gitlab-pages/docs/reference/decorators/deprecated.md index c68bb45bae..c60cb85e00 100644 --- a/gitlab-pages/docs/reference/decorators/deprecated.md +++ b/gitlab-pages/docs/reference/decorators/deprecated.md @@ -29,7 +29,8 @@ For example, the `List` namespace of the standard library has a deprecated funct Its `@deprecated` attribute shows what function to use instead: ```jsligo group=deprecated -@inline @deprecated("Use `List.tail` instead.") +// @inline +// @deprecated("Use `List.tail` instead.") const tail_opt = (list: List.t) : option> => List.tail(list); ``` diff --git a/gitlab-pages/docs/reference/decorators/dyn_entry.md b/gitlab-pages/docs/reference/decorators/dyn_entry.md index d107c6bd81..f045519949 100644 --- a/gitlab-pages/docs/reference/decorators/dyn_entry.md +++ b/gitlab-pages/docs/reference/decorators/dyn_entry.md @@ -54,14 +54,14 @@ akin to the amending process of the Tezos protocol itself. ```jsligo group=dyn_entry type storage = { storage: int; - dynamic_entrypoints; + dynamic_entrypoints: dynamic_entrypoints; }; -@dyn_entry +// @dyn_entry const one = (_u: unit, _i: int): [list, int] => [[], 1]; -@dyn_entry +// @dyn_entry const tick = (_: ticket, x: [int, int]) : [list, [int, int]] => [[], x]; diff --git a/gitlab-pages/docs/reference/decorators/entry.md b/gitlab-pages/docs/reference/decorators/entry.md index 31316648fd..bb54f0f238 100644 --- a/gitlab-pages/docs/reference/decorators/entry.md +++ b/gitlab-pages/docs/reference/decorators/entry.md @@ -40,22 +40,22 @@ Here is an example of entrypoints defined in a module: ```jsligo group=entry type storage = int; -type @return = [list, storage]; +type return_ = [list, storage]; namespace Foo { - @entry - const decrement = (param: int, storage: storage) : @return => + // @entry + const decrement = (param: int, storage: storage) : return_ => [[], storage - param]; - @entry - const increment = (param: int, storage: storage) : @return => + // @entry + const increment = (param: int, storage: storage) : return_ => [[], storage + param]; - @entry - const reset = (_u: unit, _s: storage) : @return => + // @entry + const reset = (_u: unit, _s: storage) : return_ => [[], 0]; - @view + // @view const get_storage = (_: unit, storage: storage) : storage => storage; }; ``` diff --git a/gitlab-pages/docs/reference/decorators/inline.md b/gitlab-pages/docs/reference/decorators/inline.md index c4db407541..a655378b49 100644 --- a/gitlab-pages/docs/reference/decorators/inline.md +++ b/gitlab-pages/docs/reference/decorators/inline.md @@ -39,7 +39,7 @@ Inlining also makes it cheap to create aliases of functions. For example: ```jsligo group=inline -@inline +// @inline const size = (list: List.t) : nat => List.length(list); ``` diff --git a/gitlab-pages/docs/reference/decorators/layout.md b/gitlab-pages/docs/reference/decorators/layout.md index 329952b811..0f7623a926 100644 --- a/gitlab-pages/docs/reference/decorators/layout.md +++ b/gitlab-pages/docs/reference/decorators/layout.md @@ -44,9 +44,11 @@ For example, ```jsligo group=layout type transfer = - @layout("comb") - { @annot("from") address_from: address; - @annot("to") address_to: address; + // @layout("comb") + { // @annot("from") + address_from: address; + // @annot("to") + address_to: address; value: nat } ``` diff --git a/gitlab-pages/docs/reference/decorators/private.md b/gitlab-pages/docs/reference/decorators/private.md index d5c0bfc96c..a40e3f605b 100644 --- a/gitlab-pages/docs/reference/decorators/private.md +++ b/gitlab-pages/docs/reference/decorators/private.md @@ -21,8 +21,9 @@ let f x = g x + 1 // exported by default Then the following piece of code, in another file: + ```cameligo group=import-module-with-private -#import "gitlab-pages/docs/reference/decorators/src/private/module-with-private.mligo" "ModuleWithPrivate" +module ModuleWithPrivate = Gitlab_pages.Docs.Reference.Decorators.Src.Private.Module_with_private let foo = ModuleWithPrivate.f 123 // = 5167 @@ -45,15 +46,17 @@ unit. Consider the following contents of the file `module-with-private.jsligo`: ```jsligo group=module-with-private -@private const stuff = 42; -@private const g = x => x * stuff; +// @private +const stuff = 42; + +// @private +const g = x => x * stuff; + const f = x => g(x) + 1; // exported by default ``` -Then the following piece of code, in another file: - ```jsligo group=import-module-with-private -#import "gitlab-pages/docs/reference/decorators/src/private/module-with-private.mligo" "ModuleWithPrivate" +import * as ModuleWithPrivate from "gitlab-pages/docs/reference/decorators/src/private/module-with-private.mligo"; const foo = ModuleWithPrivate.f(123); // = 5167 diff --git a/gitlab-pages/docs/reference/decorators/src/annot/annot.jsligo b/gitlab-pages/docs/reference/decorators/src/annot/annot.jsligo index e6d203820c..240be6061a 100644 --- a/gitlab-pages/docs/reference/decorators/src/annot/annot.jsligo +++ b/gitlab-pages/docs/reference/decorators/src/annot/annot.jsligo @@ -1,5 +1,7 @@ type transfer = - @layout("comb") - { @annot("from") address_from: address; - @annot("to") address_to: address; + // @layout("comb") + { // @annot("from") + address_from: address; + // @annot("to") + address_to: address; value: nat } \ No newline at end of file diff --git a/gitlab-pages/docs/reference/decorators/src/deprecated/deprecated.jsligo b/gitlab-pages/docs/reference/decorators/src/deprecated/deprecated.jsligo index 221e698088..3a9c73be17 100644 --- a/gitlab-pages/docs/reference/decorators/src/deprecated/deprecated.jsligo +++ b/gitlab-pages/docs/reference/decorators/src/deprecated/deprecated.jsligo @@ -1,3 +1,4 @@ -@inline @deprecated("Use `List.tail` instead.") +// @inline +// @deprecated("Use `List.tail` instead.") const tail_opt = (list: List.t) : option> => List.tail(list); \ No newline at end of file diff --git a/gitlab-pages/docs/reference/decorators/src/dyn_entry/dyn_entry.jsligo b/gitlab-pages/docs/reference/decorators/src/dyn_entry/dyn_entry.jsligo index 9531db9881..d33b3d4a0b 100644 --- a/gitlab-pages/docs/reference/decorators/src/dyn_entry/dyn_entry.jsligo +++ b/gitlab-pages/docs/reference/decorators/src/dyn_entry/dyn_entry.jsligo @@ -1,13 +1,13 @@ type storage = { storage: int; - dynamic_entrypoints; + dynamic_entrypoints: dynamic_entrypoints; }; -@dyn_entry +// @dyn_entry const one = (_u: unit, _i: int): [list, int] => [[], 1]; -@dyn_entry +// @dyn_entry const tick = (_: ticket, x: [int, int]) : [list, [int, int]] => [[], x]; \ No newline at end of file diff --git a/gitlab-pages/docs/reference/decorators/src/entry/entry.jsligo b/gitlab-pages/docs/reference/decorators/src/entry/entry.jsligo index bc2d3a523d..9062ebdc41 100644 --- a/gitlab-pages/docs/reference/decorators/src/entry/entry.jsligo +++ b/gitlab-pages/docs/reference/decorators/src/entry/entry.jsligo @@ -1,19 +1,19 @@ type storage = int; -type @return = [list, storage]; +type return_ = [list, storage]; namespace Foo { - @entry - const decrement = (param: int, storage: storage) : @return => + // @entry + const decrement = (param: int, storage: storage) : return_ => [[], storage - param]; - @entry - const increment = (param: int, storage: storage) : @return => + // @entry + const increment = (param: int, storage: storage) : return_ => [[], storage + param]; - @entry - const reset = (_u: unit, _s: storage) : @return => + // @entry + const reset = (_u: unit, _s: storage) : return_ => [[], 0]; - @view + // @view const get_storage = (_: unit, storage: storage) : storage => storage; }; \ No newline at end of file diff --git a/gitlab-pages/docs/reference/decorators/src/inline/inline.jsligo b/gitlab-pages/docs/reference/decorators/src/inline/inline.jsligo index 079381c811..5ebbf3e874 100644 --- a/gitlab-pages/docs/reference/decorators/src/inline/inline.jsligo +++ b/gitlab-pages/docs/reference/decorators/src/inline/inline.jsligo @@ -1,2 +1,2 @@ -@inline +// @inline const size = (list: List.t) : nat => List.length(list); \ No newline at end of file diff --git a/gitlab-pages/docs/reference/decorators/src/layout/layout.jsligo b/gitlab-pages/docs/reference/decorators/src/layout/layout.jsligo index e6d203820c..240be6061a 100644 --- a/gitlab-pages/docs/reference/decorators/src/layout/layout.jsligo +++ b/gitlab-pages/docs/reference/decorators/src/layout/layout.jsligo @@ -1,5 +1,7 @@ type transfer = - @layout("comb") - { @annot("from") address_from: address; - @annot("to") address_to: address; + // @layout("comb") + { // @annot("from") + address_from: address; + // @annot("to") + address_to: address; value: nat } \ No newline at end of file diff --git a/gitlab-pages/docs/reference/decorators/src/private/import-module-with-private.jsligo b/gitlab-pages/docs/reference/decorators/src/private/import-module-with-private.jsligo index 24c6b323fa..a8e5be1761 100644 --- a/gitlab-pages/docs/reference/decorators/src/private/import-module-with-private.jsligo +++ b/gitlab-pages/docs/reference/decorators/src/private/import-module-with-private.jsligo @@ -1,4 +1,4 @@ -#import "gitlab-pages/docs/reference/decorators/src/private/module-with-private.mligo" "ModuleWithPrivate" +import * as ModuleWithPrivate from "gitlab-pages/docs/reference/decorators/src/private/module-with-private.mligo"; const foo = ModuleWithPrivate.f(123); // = 5167 diff --git a/gitlab-pages/docs/reference/decorators/src/private/import-module-with-private.mligo b/gitlab-pages/docs/reference/decorators/src/private/import-module-with-private.mligo index ffc7dc2c91..a21e12672a 100644 --- a/gitlab-pages/docs/reference/decorators/src/private/import-module-with-private.mligo +++ b/gitlab-pages/docs/reference/decorators/src/private/import-module-with-private.mligo @@ -1,4 +1,4 @@ -#import "gitlab-pages/docs/reference/decorators/src/private/module-with-private.mligo" "ModuleWithPrivate" +module ModuleWithPrivate = Gitlab_pages.Docs.Reference.Decorators.Src.Private.Module_with_private let foo = ModuleWithPrivate.f 123 // = 5167 diff --git a/gitlab-pages/docs/reference/decorators/src/private/module-with-private.jsligo b/gitlab-pages/docs/reference/decorators/src/private/module-with-private.jsligo index cef574025e..048c49b22f 100644 --- a/gitlab-pages/docs/reference/decorators/src/private/module-with-private.jsligo +++ b/gitlab-pages/docs/reference/decorators/src/private/module-with-private.jsligo @@ -1,3 +1,7 @@ -@private const stuff = 42; -@private const g = x => x * stuff; +// @private +const stuff = 42; + +// @private +const g = x => x * stuff; + const f = x => g(x) + 1; // exported by default \ No newline at end of file diff --git a/gitlab-pages/docs/reference/decorators/src/view/view.jsligo b/gitlab-pages/docs/reference/decorators/src/view/view.jsligo index 820a48dfc5..34458117b2 100644 --- a/gitlab-pages/docs/reference/decorators/src/view/view.jsligo +++ b/gitlab-pages/docs/reference/decorators/src/view/view.jsligo @@ -1,11 +1,11 @@ type return_type = [list, int]; -@view +// @view const add = (param: int, storage: int): int => param + storage -@view -const get_storage = (_ : unit, storage: int): int => storage +// @view +const get_storage = (_: unit, storage: int): int => storage -@entry -const main = (_ : unit, storage: int): return_type => +// @entry +const main = (_: unit, storage: int): return_type => [[], storage] \ No newline at end of file diff --git a/gitlab-pages/docs/reference/decorators/view.md b/gitlab-pages/docs/reference/decorators/view.md index d4cda4d6fe..3131573813 100644 --- a/gitlab-pages/docs/reference/decorators/view.md +++ b/gitlab-pages/docs/reference/decorators/view.md @@ -37,14 +37,14 @@ The decorator is `@view` and placed just before a function, like the ```jsligo group=view type return_type = [list, int]; -@view +// @view const add = (param: int, storage: int): int => param + storage -@view -const get_storage = (_ : unit, storage: int): int => storage +// @view +const get_storage = (_: unit, storage: int): int => storage -@entry -const main = (_ : unit, storage: int): return_type => +// @entry +const main = (_: unit, storage: int): return_type => [[], storage] ``` diff --git a/gitlab-pages/docs/reference/dynamic_entrypoints.md b/gitlab-pages/docs/reference/dynamic_entrypoints.md index 674eb9cf32..62e4c941f2 100644 --- a/gitlab-pages/docs/reference/dynamic_entrypoints.md +++ b/gitlab-pages/docs/reference/dynamic_entrypoints.md @@ -14,8 +14,9 @@ type t = (nat, bytes) big_map type t = big_map<nat, bytes> + Type `t` is an alias of the predefined type - `dynamic_entrypoints`. +`dynamic_entrypoints`. @@ -25,64 +26,69 @@ val set : ('param, 'storage) entrypoint option -> dynamic_entrypoints -> dynamic_entrypoints -let set: - <param, storage>(_: dynamic_entrypoint<param, storage>) => (_: option<entrypoint<param, storage>>) => ( - _: dynamic_entrypoints +set: + <param, storage>(dyn: dynamic_entrypoint<param, + storage>, entry_opt: option<entrypoint<param, + storage>>, dyn_map: dynamic_entrypoints ) => dynamic_entrypoints + -The call `Dynamic_entrypoints.set dyn None dyn_map` returns a copy - of the map of dynamic entrypoints `dyn_map` where the dynamic entrypoint - `dyn` is not associated to a static entrypoint. The call - `Dynamic_entrypoints set dyn (Some entrypoint) dyn_map` is a copy of - `dyn_map` where the dynamic entrypoint `dyn` is associated to the static - entrypoint `entrypoint`. +The call `Dynamic_entrypoints.set dyn None dyn_map` returns a copy of +the map of dynamic entrypoints `dyn_map` where the dynamic entrypoint +`dyn` is not associated to a static entrypoint. The call +`Dynamic_entrypoints set dyn (Some entrypoint) dyn_map` is a copy of +`dyn_map` where the dynamic entrypoint `dyn` is associated to the +static entrypoint `entrypoint`. -The call `Dynamic_entrypoints.set(dyn, None(), dyn_map)` returns a copy - of the map of dynamic entrypoints `dyn_map` where the dynamic entrypoint - `dyn` is not associated to a static entrypoint. The call - `Dynamic_entrypoints.set(dyn, Some(entrypoint), dyn_map)` is a copy of - `dyn_map` where the dynamic entrypoint `dyn` is associated to the static - entrypoint `entrypoint`. +The call `Dynamic_entrypoints.set(dyn, ["None" as "None"], dyn_map)` +returns a copy of the map of dynamic entrypoints `dyn_map` where the +dynamic entrypoint `dyn` is not associated to a static entrypoint. The +call `Dynamic_entrypoints.set(dyn, ["Some" as "Some", entrypoint], +dyn_map)` is a copy of `dyn_map` where the dynamic entrypoint `dyn` is +associated to the static entrypoint `entrypoint`. -val set_bytes : +val set_bytes : 'param 'storage.('param, 'storage) dynamic_entrypoint -> bytes option -> dynamic_entrypoints -> dynamic_entrypoints -let set_bytes: - <param, storage>(_: dynamic_entrypoint<param, storage>) => (_: option<bytes>) => (_: dynamic_entrypoints) => dynamic_entrypoints +set_bytes: + <param, storage>(dyn: dynamic_entrypoint<param, + storage>, bytes_opt: option<bytes>, dyn_map: dynamic_entrypoints) => dynamic_entrypoints + -The call `Dynamic_entrypoints.set_bytes dyn None dyn_map` returns a copy of - the map of dynamic entrypoints `dyn_map` where the dynamic entrypoint - `dyn` is not associated to a static entrypoint. The call - `Dynamic_entrypoints.set_bytes dyn (Some bytes) dyn_map` is a copy of - `dyn_map` where the dynamic entrypoint `dyn` is associated to the static - entrypoint encoded by the sequence of bytes `bytes`. If that sequence is - invalid, any call to the dynamic entrypoint will fail. +The call `Dynamic_entrypoints.set_bytes dyn None dyn_map` returns a +copy of the map of dynamic entrypoints `dyn_map` where the dynamic +entrypoint `dyn` is not associated to a static entrypoint. The call +`Dynamic_entrypoints.set_bytes dyn (Some bytes) dyn_map` is a copy of +`dyn_map` where the dynamic entrypoint `dyn` is associated to the +static entrypoint encoded by the sequence of bytes `bytes`. If that +sequence is invalid, any call to the dynamic entrypoint will fail. -The call `Dynamic_entrypoints.set_bytes(dyn, None(), dyn_map)` returns a - copy of the map of dynamic entrypoints `dyn_map` where the dynamic - entrypoint `dyn` is not associated to a static entrypoint. The - call `Dynamic_entrypoints.set_bytes(dyn, Some(bytes), dyn_map)` is a copy - of `dyn_map` where the dynamic entrypoint `dyn` is associated to the static - entrypoint encoded by the sequence of bytes `bytes`. If that sequence is - invalid, any call to the dynamic entrypoint will fail. +The call `Dynamic_entrypoints.set_bytes(dyn, ["None" as "None"], +dyn_map)` returns a copy of the map of dynamic entrypoints `dyn_map` +where the dynamic entrypoint `dyn` is not associated to a static +entrypoint. The call `Dynamic_entrypoints.set_bytes(dyn, +["Some" as "Some", bytes], dyn_map)` is a copy of `dyn_map` where the +dynamic entrypoint `dyn` is associated to the static entrypoint +encoded by the sequence of bytes `bytes`. If that sequence is invalid, +any call to the dynamic entrypoint will fail. @@ -93,27 +99,26 @@ val get : 'storage.('param, 'storage) dynamic_entrypoint -> dynamic_entrypoints -> ('param, 'storage) entrypoint option -let get: - <param, storage>(_: dynamic_entrypoint<param, storage>) => (_: dynamic_entrypoints) => option< - entrypoint<param, storage> - > +get: + <param, storage>(dyn: dynamic_entrypoint<param, + storage>, dyn_map: dynamic_entrypoints) => option<entrypoint<param, storage>> + -The call `Dynamic_entrypoints.get dyn dyn_map` is `None` if the dynamic - entrypoint `dyn` is absent from the dynamic entrypoints map - `dyn_map`. Otherwise, it is `Some entry`, where `entry` is a - static entrypoint that is callable (like a function). See type - `entrypoint`. +The call `Dynamic_entrypoints.get dyn dyn_map` is `None` if the +dynamic entrypoint `dyn` is absent from the dynamic entrypoints map +`dyn_map`. Otherwise, it is `Some entry`, where `entry` is a static +entrypoint that is callable (like a function). See type `entrypoint`. -The call `Dynamic_entrypoints.get(dyn, dyn_map)` is `None()` if the dynamic - entrypoint `dyn` is absent from the dynamic entrypoints map - `dyn_map`. Otherwise, it is `Some(entry)`, where `entry` is a - static entrypoint that is callable (like a function). See type - `entrypoint`. +The call `Dynamic_entrypoints.get(dyn, dyn_map)` is +`["None" as "None"]` if the dynamic entrypoint `dyn` is absent from +the dynamic entrypoints map `dyn_map`. Otherwise, it is +`["Some" as "Some", entry]`, where `entry` is a static entrypoint that +is callable (like a function). See type `entrypoint`. diff --git a/gitlab-pages/docs/reference/list.md b/gitlab-pages/docs/reference/list.md index 69b444ea0c..e855e50f4a 100644 --- a/gitlab-pages/docs/reference/list.md +++ b/gitlab-pages/docs/reference/list.md @@ -23,7 +23,7 @@ The type `t` is an alias for the predefined type `list`. val empty : 'elt.'elt t -let empty: <elt>t<elt> +empty: <elt>t<elt> @@ -46,19 +46,19 @@ The value `List.empty` is the empty list. It is a synonym for val length : 'elt.'elt t -> nat -let length: <elt>(_: t<elt>) => nat +length: <elt>(list: t<elt>) => nat The call `List.length l` is the number of elements in the list - `l`. Note: `List.length` is another name for `List.size`. +`l`. Note: `List.length` is another name for `List.size`. The call `List.length(l)` is the number of elements in the list - `l`. Note: `List.length` is another name for `List.size`. +`l`. Note: `List.length` is another name for `List.size`. @@ -67,8 +67,9 @@ The call `List.length(l)` is the number of elements in the list val size : 'elt.'elt t -> nat -let size: <elt>(_: t<elt>) => nat +size: <elt>(list: t<elt>) => nat + The call `List.size l` is the number of elements in the list `l`. @@ -86,42 +87,21 @@ The call `List.size(l)` is the number of elements in the list `l`. val head : 'elt.'elt t -> 'elt option -let head: <elt>(_: t<elt>) => option<elt> - - - -The call `List.head l`, where `l` is a list, is `None` if `l` is - empty; otherwise, `Some hd`, where `hd` is the head of the list. - - - - - -The call `List.head(l)`, where `l` is a list, is `None()` if `l` is - empty; otherwise, `Some(hd)`, where `hd` is the head of the list. - - - - - -val head_opt : 'elt.'elt t -> 'elt option - - -let head_opt: <elt>(_: t<elt>) => option<elt> +head: <elt>(list: t<elt>) => option<elt> -**Deprecated:** Use `List.head` instead. -The call `List.head_opt l`, where `l` is a list, is `None` if `l` is - empty; otherwise, `Some hd`, where `hd` is the head of the list. +The call `List.head l`, where `l` is a list, is `None` if `l` is +empty; otherwise, `Some hd`, where `hd` is the head of the list. -The call `List.head_opt(l)`, where `l` is a list, is `None()` if `l` is - empty; otherwise, `Some(hd)`, where `hd` is the head of the list. +The call `List.head(l)`, where `l` is a list, is `["None" as "None"]` +if `l` is empty; otherwise, `["Some" as "Some", hd]`, where `hd` is +the head of the list. @@ -130,65 +110,70 @@ The call `List.head_opt(l)`, where `l` is a list, is `None()` if `l` is val tail : 'elt.'elt t -> 'elt t option -let tail: <elt>(_: t<elt>) => option<t<elt>> +tail: <elt>(list: t<elt>) => option<t<elt>> + The call `List.tail l`, where `l` is a list, is `None` if `l` is - empty; otherwise, `Some tl`, where `tl` is the tail of the list. +empty; otherwise, `Some tl`, where `tl` is the tail of the list. -The call `List.tail(l)`, where `l` is a list, is `None()` if `l` is - empty; otherwise, `Some(tl)`, where `tl` is the tail of the list. +The call `List.tail(l)`, where `l` is a list, is `["None" as "None"]` +if `l` is empty; otherwise, `["Some" as "Some", tl]`, where `tl` +is the tail of the list. -val tail_opt : 'elt.'elt t -> 'elt t option +val head_and_tail : 'elt.'elt t -> ('elt * 'elt t) option -let tail_opt: <elt>(_: t<elt>) => option<t<elt>> +head_and_tail: <elt>(list: t<elt>) => option<elt,t< elt>> -**Deprecated:** Use `List.tail` instead. -The call `List.tail_opt l`, where `l` is a list, is `None` if `l` is - empty; otherwise, `Some tl`, where `tl` is the tail of the list. +The call `List.head_and_tail l`, where `l` is a list, is `None` if `l` +is empty; otherwise, `Some (hd, tl)`, where `hd` and `tl` are the head +and tail of the list, respectively. -The call `List.tail_opt(l)`, where `l` is a list, is `None()` if `l` is - empty; otherwise, `Some(tl)`, where `tl` is the tail of the list. +The call `List.head_and_tail(l)`, where `l` is a list, is +`["None" as "None"]` if `l` is empty; otherwise, +`["Some" as "Some", [hd,tl]]`, where `hd` and `tl` are the head and +tail of the list, respectively. -val map : 'src 'dst.('src -> 'dst) -> 'src list -> 'dst list +val map : 'src 'dst.('src -> 'dst) -> 'src t -> 'dst t -let map: <src, dst>(_: (_: src) => dst) => (_: list<src>) => list<dst> +map: <src, dst>(f: (_: src) => dst, list: t<src>) => t<dst> + The call `List.map f [a1; ...; an]` applies the function `f` to `a1`, - ..., `an` (from left to right), and builds the list - `[f a1; ...; f an]` with the results returned by `f`. +..., `an` (from left to right), and builds the list +`[f a1; ...; f an]` with the results returned by `f`. The call `List.map(f, list([a1; ...; an]))` applies the function `f` to - `a1`, ..., `an` (from left to right), and builds the list - `list([f(a1); ...; f(an)])` with the results returned by `f`. +`a1`, ..., `an` (from left to right), and builds the list +`list([f(a1); ...; f(an)])` with the results returned by `f`. @@ -197,63 +182,66 @@ The call `List.map(f, list([a1; ...; an]))` applies the function `f` to val iter : 'elt.('elt -> unit) -> 'elt t -> unit -let iter: <elt>(_: (_: elt) => unit) => (_: t<elt>) => unit +iter: <elt>(f: (_: elt) => unit, list: t<elt>) => unit The call `List.iter f [a1; ...; an]` applies the function `f` in turn - to `[a1; ...; an]`. It is equivalent to - `let () = f a1 in let () = f a2 in ... in f an`. +to `[a1; ...; an]`. It is equivalent to +`let () = f a1 in let () = f a2 in ... in f an`. The call `List.iter(f, list([a1; ...; an]))` applies the function `f` - in turn to `list([a1; ...; an])`. It is equivalent to `{f(a1); - f(a2); ...; f(an)}`. +in turn to `list([a1; ...; an])`. It is equivalent to `{f(a1); +f(a2); ...; f(an)}`. -val fold_left : 'elt 'acc.(('acc * 'elt) -> 'acc) -> 'acc -> 'elt t -> 'acc +val fold_left : 'elt 'acc.(('acc * 'elt) -> 'acc) -> 'acc -> 'elt t -> 'acc -let fold_left: <elt, acc>(_: (_: [acc, elt]) => acc) => (_: acc) => (_: t<elt>) => acc +fold_left: <elt, acc>(f: (_: [acc, elt]) => acc, init: +acc, list: t<elt>) => acc + The call `List.fold_left f init [a1; ...; an]` is - `f (... (f (f init a1) a2) ...) an`. +`f (... (f (f init a1) a2) ...) an`. The call `List.fold_left(f, init, list([a1; ...; an]))` is - `f (... (f (f(init, a1)), a2), ...), an)`. +`f (... (f (f(init, a1)), a2), ...), an)`. -val fold_right : 'elt 'acc.(('elt * 'acc) -> 'acc) -> 'elt t -> 'acc -> 'acc +val fold_right : 'elt 'acc.(('elt * 'acc) -> 'acc) -> 'elt t -> 'acc -> 'acc -let fold_right: <elt, acc>(_: (_: [elt, acc]) => acc) => (_: t<elt>) => (_: acc) => acc +fold_right: <elt, acc>(f: (_: [elt, acc]) => acc, list: +t<elt>, init: acc) => acc The call `List.fold_right f [a1; ...; an] init` is - `f a1 (f a2 (... (f an init) ...))`. +`f a1 (f a2 (... (f an init) ...))`. The call `List.fold_right(f, list([a1; ...; an]), init)` is - `f (a1, f (a2, (..., f (an, init))...))`. +`f (a1, f (a2, (..., f (an, init))...))`. @@ -262,21 +250,23 @@ The call `List.fold_right(f, list([a1; ...; an]), init)` is val fold : 'elt 'acc.(('acc * 'elt) -> 'acc) -> 'elt t -> 'acc -> 'acc -let fold: <elt, acc>(_: (_: [acc, elt]) => acc) => (_: t<elt>) => (_: acc) => acc +fold: <elt, acc>(f: (_: [acc, elt]) => acc, list: +t<elt>, init: acc) => acc + The call `List.fold f [a1; ...; an] init` is - `f (... (f (f init a1) a2) ...) an`. Note: - `List.fold_left f init list` is the same as `List.fold f list init`. +`f (... (f (f init a1) a2) ...) an`. Note: +`List.fold_left f init list` is the same as `List.fold f list init`. The call `List.fold(f, list([a1; ...; an]), init)` is - `f (... (f (f (init, a1), a2) ...), an)`. Note: - `List.fold_left(f, init, list)` is the same as `List.fold(f, list, init)`. +`f (... (f (f (init, a1), a2) ...), an)`. Note: +`List.fold_left(f, init, list)` is the same as `List.fold(f, list, init)`. @@ -285,8 +275,9 @@ The call `List.fold(f, list([a1; ...; an]), init)` is val cons : 'elt.'elt -> 'elt t -> 'elt t -let cons: <elt>(_: elt) => (_: t<elt>) => t<elt> +cons: <elt>(elt: elt, list: t<elt>) => t<elt> + The call `List.cons e l` is `e :: l`. @@ -301,51 +292,55 @@ The call `List.cons(e, l)` is `list([e, ...l])`. -val find_opt : 'elt.('elt -> bool) -> 'elt t -> 'elt option +val find_opt : 'elt.('elt -> bool) -> 'elt t -> 'elt option -let find_opt: <elt>(_: (_: elt) => bool) => (_: t<elt>) => option<elt> +find_opt: <elt>(f: (_: elt) => bool, list: t<elt>) => option<elt> + -The call `List.find_opt pred list` is `None` if no element of the - list `list` satisfies the predicate `pred`; otherwise, it is - `Some e`, where `e` is the leftmost element in `list` that satisfies - `pred`. The order of the calls of `pred` is not specified. +The call `List.find_opt pred list` is `None` if no element of the list +`list` satisfies the predicate `pred`; otherwise, it is `Some e`, +where `e` is the leftmost element in `list` that satisfies `pred`. The +order of the calls of `pred` is not specified. -The call `List.find_opt(pred, list)` is `None()` if no element of the - list `list` satisfies the predicate `pred`; otherwise, it is - `Some(e)`, where `e` is the leftmost element in `list` that satisfies - `pred`. The order of the calls of `pred` is not specified. +The call `List.find_opt(pred, list)` is `["None" as "None"]` if no +element of the list `list` satisfies the predicate `pred`; otherwise, +it is `["Some" as "Some", e]`, where `e` is the leftmost element in +`list` that satisfies `pred`. The order of the calls of `pred` is not +specified. -val filter_map : 'src 'dst.('src -> 'dst option) -> 'src list -> 'dst list +val filter_map : 'src 'dst.('src -> 'dst option) -> 'src list -> 'dst list -let filter_map: <src, dst>(_: (_: src) => option<dst>) => (_: list<src>) => list<dst> +filter_map: <src, dst>(filter: (_: src) => +option<dst>, list: list<src>) => list<dst> + The call `List.filter_map f l` is the maximal sub-list of `l` such - that the call of function `f` on its elements is not `None`. Note: - `f` is called on all elements of `l`. The order of the calls of - `f` is not specified. +that the call of function `f` on its elements is not `None`. Note: `f` +is called on all elements of `l`. The order of the calls of `f` is not +specified. The call `List.filter_map(f, l)` is the maximal sub-list of `l` such - that the call of function `f` on its elements is not `None()`. Note: - `f` is called on all elements of `l`. The order of the calls of - `f` is not specified. +that the call of function `f` on its elements is not +`["None" as "None"]`. Note: `f` is called on all elements of `l`. The +order of the calls of `f` is not specified. @@ -354,39 +349,40 @@ The call `List.filter_map(f, l)` is the maximal sub-list of `l` such val update : 'elt.('elt -> 'elt option) -> 'elt t -> 'elt t -let update: <elt>(_: (_: elt) => option<elt>) => (_: t<elt>) => t<elt> +update: <elt>(filter: (_: elt) => option<elt>, list: t<elt>) => t<elt> + The call `List.update f l` is the list `l` where the elements `e` - such that `f e` is `Some v` have been replaced by `v`. +such that `f e` is `Some v` have been replaced by `v`. The call `List.update(f, l)` is the list `l` where the elements `e` - such that `f(e)` is `Some(v)` have been replaced by `v`. +such that `f(e)` is `["Some" as "Some", v]` have been replaced by `v`. -val update_with : 'elt.('elt -> bool) -> 'elt -> 'elt t -> 'elt t +val update_with : 'elt.('elt -> bool) -> 'elt -> 'elt t -> 'elt t -let update_with: <elt>(_: (_: elt) => bool) => (_: elt) => (_: t<elt>) => t<elt> +update_with: <elt>(pred: (_: elt) => bool, default: elt, list: t<elt>) => t<elt> The call `List.update_with p d l` is the list `l` where the elements - `e` such that satisfy the predicate `p` are replaced by `d`. +`e` such that satisfy the predicate `p` are replaced by `d`. The call `List.update_with(p,d,l)` is the list `l` where the elements - `e` such that satisfy the predicate `p` are replaced by `d`. +`e` such that satisfy the predicate `p` are replaced by `d`. diff --git a/gitlab-pages/docs/reference/map.md b/gitlab-pages/docs/reference/map.md index adba43b8bb..b84fcf48f2 100644 --- a/gitlab-pages/docs/reference/map.md +++ b/gitlab-pages/docs/reference/map.md @@ -7,8 +7,8 @@ import Syntax from '@theme/Syntax'; import SyntaxTitle from '@theme/SyntaxTitle'; -Maps from keys to values, where the bindings key/value are ordered - by increasing keys. +Maps from keys to values, where the bindings key/value are ordered by + increasing keys. @@ -17,6 +17,7 @@ type ('key, 'value) t = ('key, 'value) map type t<key, value> = map<key, value> + The type `('key,'value) Map.t` is an alias for `('key,'value) map`. @@ -34,52 +35,54 @@ The type `Map.t` is an alias for `map`. val empty : 'key 'value.('key, 'value) t -let empty: <key, value>t<key, value> +empty: <key, value>t<key, value> + -The value `Map.empty` is the empty map. In some contexts, it is - useful to annotate it with its type, for example: - `(Map.empty : (int, string) map)`. +The value `Map.empty` is the empty map. In some contexts, it is useful +to annotate it with its type, for example: +`(Map.empty : (int, string) map)`. -The value `Map.empty` is the empty map. In some contexts, it is - useful to annotate it with its type, for example: - `(Map.empty as map)`. +The value `Map.empty` is the empty map. In some contexts, it is useful +to annotate it with its type, for example: +`(Map.empty as map)`. - -val get_and_update : 'key 'value.'key -> 'value option -> ('key, 'value) t -> ('value option * ('key, 'value) t) +val get_and_update : 'key 'value.'key -> 'value option -> ('key, 'value) t -> ('value option * ('key, 'value) t) -let get_and_update: <key, value>(_: key) => (_: option<value>) => (_: t<key, value>) => [option<value>, t<key, value>] +get_and_update: <key, value>(key: key, upd: +option<value>, map: t<key, value>) => [option<value>, t<key, value>] The call `Map.get_and_update key None map` returns a copy of the map - `map` without the entry for the key `key` in `map` (no change if - the key is absent). The call `Map.get_and_update key (Some value) map` - returns a copy of the map `map` where there is an entry for the - key `key` associated with the value `value`. In both cases, if - there was already a value `v` bound to `key`, it is returned as - `Some v`, otherwise `None`. +`map` without the entry for the key `key` in `map` (no change if +the key is absent). The call `Map.get_and_update key (Some value) map` +returns a copy of the map `map` where there is an entry for the +key `key` associated with the value `value`. In both cases, if +there was already a value `v` bound to `key`, it is returned as +`Some v`, otherwise `None`. -The call `Map.get_and_update(key, None(), map)` returns a copy of the - map `map` without the entry for the key `key` in `map` (no change - if the key is absent). The call `Map.get_and_update(key, Some(value), - map)` returns a copy of the map `map` where there is an entry for - the key `key` associated with the value `value`. In both cases, if - there was already a value `v` bound to `key`, it is returned as - `Some(v)`, otherwise `None()`. +The call `Map.get_and_update(key, ["None" as "None"], map)` +returns a copy of the map `map` without the entry for the key +`key` in `map` (no change if the key is absent). The call +`Map.get_and_update(key, ["Some" as "Some", value], map)` returns +a copy of the map `map` where there is an entry for the key `key` +associated with the value `value`. In both cases, if there was +already a value `v` bound to `key`, it is returned as +`["Some" as "Some", v]`, otherwise `["None" as "None"]`. @@ -88,27 +91,28 @@ The call `Map.get_and_update(key, None(), map)` returns a copy of the val update : 'key 'value.'key -> 'value option -> ('key, 'value) t -> ('key, 'value) t -let update: <key, value>(_: key) => (_: option<value>) => (_: t<key, value>) => t<key, value> +update: <key, value>(key: key, upd: option<value>, map: t<key, value>) => t<key, value> The call `Map.update key None map` returns a copy of the map `map` - without the entry for the key `key` in `map` (no change if the key - is absent). The call `Map.update key (Some value) map` returns the map - `map` where there is an entry for the key `key` associated with - the value `value`. In both cases, the value originally bound to - `key` is lost. See `Map.get_and_update`. +without the entry for the key `key` in `map` (no change if the key +is absent). The call `Map.update key (Some value) map` returns the map +`map` where there is an entry for the key `key` associated with +the value `value`. In both cases, the value originally bound to +`key` is lost. See `Map.get_and_update`. -The call `Map.update(key, None(), map)` returns a copy of the map `map` - without the entry for the key `key` in `map` (no change if the key - is absent). The call `Map.update(key, Some(value), map)` returns the map - `map` where there is an entry for the key `key` associated with - the value `value`. In both cases, the value originally bound to - `key` is lost. See `Map.get_and_update`. +The call `Map.update(key, ["None" as "None"], map)` returns a copy +of the map `map` without the entry for the key `key` in `map` (no +change if the key is absent). The call +`Map.update(key, ["Some" as "Some", value], map)` returns the map +`map` where there is an entry for the key `key` associated with +the value `value`. In both cases, the value originally bound to +`key` is lost. See `Map.get_and_update`. @@ -117,21 +121,21 @@ The call `Map.update(key, None(), map)` returns a copy of the map `map` val add : 'key 'value.'key -> 'value -> ('key, 'value) t -> ('key, 'value) t -let add: <key, value>(_: key) => (_: value) => (_: t<key, value>) => t<key, value> +add: <key, value>(key: key, value: value, map: t<key, value>) => t<key, value> The call `Map.add key value map` returns a copy of the `map` where - there is a binding of key `key` to value `value`. If there is a - binding for `key` in `map`, then it is lost. +there is a binding of key `key` to value `value`. If there is a +binding for `key` in `map`, then it is lost. The call `Map.add(key, value, map)` returns a copy of the `map` where - there is a binding of key `key` to value `value`. If there is a - binding for `key` in `map`, then it is lost. +there is a binding of key `key` to value `value`. If there is a +binding for `key` in `map`, then it is lost. @@ -140,19 +144,20 @@ The call `Map.add(key, value, map)` returns a copy of the `map` where val remove : 'key 'value.'key -> ('key, 'value) t -> ('key, 'value) t -let remove: <key, value>(_: key) => (_: t<key, value>) => t<key, value> +remove: <key, value>(key: key, map: t<key, value>) => t<key, value> + The call `Map.remove key map` returns a copy of the map `map` where - the binding for key `key` is absent. +the binding for key `key` is absent. The call `Map.remove(key, map)` returns a copy of the map `map` where - the binding for key `key` is absent. +the binding for key `key` is absent. @@ -161,21 +166,22 @@ The call `Map.remove(key, map)` returns a copy of the map `map` where val literal : 'key 'value.('key * 'value) list -> ('key, 'value) t -let literal: <key, value>(_: list<[key, value]>) => t<key, value> +literal: <key, value>(bindings: list<[key, value]>) => t<key, value> + The call `Map.literal [(k1,v1); ...; (kn,vn)]` returns a map from - the pairs of key/value in the list. Note: The list must be a - literal, not an expression (compile-time list of values). +the pairs of key/value in the list. Note: The list must be a +literal, not an expression (compile-time list of values). The call `Map.literal(list([[k1,v1], ..., [kn,vn]]))` returns a map from - the pairs of key/value in the list. Note: The list must be a - literal, not an expression (compile-time list of values). +the pairs of key/value in the list. Note: The list must be a +literal, not an expression (compile-time list of values). @@ -184,21 +190,22 @@ The call `Map.literal(list([[k1,v1], ..., [kn,vn]]))` returns a map from val of_list : 'key 'value.('key * 'value) list -> ('key, 'value) t -let of_list: <key, value>(_: list<[key, value]>) => t<key, value> +of_list: <key, value>(bindings: list<[key, value]>) => t<key, value> + The call `Map.of_list bindings` returns a map from the pairs of - key/value in the list `bindings`. Note: Use `Map.literal` instead if - using a literal list. +key/value in the list `bindings`. Note: Use `Map.literal` instead +if using a literal list. The call `Map.of_list(bindings)` returns a map from the pairs of - key/value in the list `bindings`. Note: Use `Map.literal` instead if - using a literal list. +key/value in the list `bindings`. Note: Use `Map.literal` instead if +using a literal list. @@ -207,19 +214,20 @@ The call `Map.of_list(bindings)` returns a map from the pairs of val size : 'key 'value.('key, 'value) t -> nat -let size: <key, value>(_: t<key, value>) => nat +size: <key, value>(map: t<key, value>) => nat + The call `Map.size map` evaluates in the number of entries in the - map `map`. +map `map`. The call `Map.size(map)` evaluates in the number of entries in the - map `map`. +map `map`. @@ -228,19 +236,20 @@ The call `Map.size(map)` evaluates in the number of entries in the val mem : 'key 'value.'key -> ('key, 'value) t -> bool -let mem: <key, value>(_: key) => (_: t<key, value>) => bool +mem: <key, value>(key: key, map: t<key, value>) => bool + The call `Map.mem key map` is `true` if, and only if, the key `key` - is in the map `map`. +is in the map `map`. The call `Map.mem(key, map)` is `true` if, and only if, the key `key` - is in the map `map`. +is in the map `map`. @@ -249,21 +258,23 @@ The call `Map.mem(key, map)` is `true` if, and only if, the key `key` val find_opt : 'key 'value.'key -> ('key, 'value) t -> 'value option -let find_opt: <key, value>(_: key) => (_: t<key, value>) => option<value> +find_opt: <key, value>(key: key, map: t<key, value>) => option<value> + The call `Map.find_opt key map` returns `None` if the key `key` is - present in the map `map`; otherwise, it is `Some v`, where `v` is - the value associated to `key` in `map`. +present in the map `map`; otherwise, it is `Some v`, where `v` is +the value associated to `key` in `map`. -The call `Map.find_opt(key, map)` returns `None()` if the key `key` is - present in the map `map`; otherwise, it is `Some(v)`, where `v` is - the value associated to `key` in `map`. +The call `Map.find_opt(key, map)` returns `["None" as "None"]` if +the key `key` is present in the map `map`; otherwise, it is +`["Some" as "Some", v]`, where `v` is the value associated to +`key` in `map`. @@ -272,21 +283,22 @@ The call `Map.find_opt(key, map)` returns `None()` if the key `key` is val find : 'key 'value.'key -> ('key, 'value) t -> 'value -let find: <key, value>(_: key) => (_: t<key, value>) => value +find: <key, value>(key: key, map: t<key, value>) => value + The call `Map.find key map` returns the value associated to `key` in - `map`. If the key is absent, the execution fails with the string - `"MAP FIND"`. +`map`. If the key is absent, the execution fails with the string +`"MAP FIND"`. The call `Map.find(key, map)` returns the value associated to `key` in - `map`. If the key is absent, the execution fails with the string - `"MAP FIND"`. +`map`. If the key is absent, the execution fails with the string +`"MAP FIND"`. @@ -295,23 +307,25 @@ The call `Map.find(key, map)` returns the value associated to `key` in val fold : 'key 'value 'acc.(('acc * 'key * 'value) -> 'acc) -> ('key, 'value) t -> 'acc -> 'acc -let fold: <key, value, acc>(_: (_: [acc, [key, value]]) => acc) => (_: t<key, value>) => (_: acc) => acc +fold: <key, value, acc>(f: (_: [acc, [key, value]]) => +acc, map: t<key, value>, acc: acc) => acc + The call `Map.fold f map init` is - `f ( ... f (f (init, (k1,v1)), (k2,v2)), ..., (kn,vn))` - where `(k1,v1)`, `(k2,v2)`, ..., `(kn,vn)` are the bindings in the - map `map`, in increasing order of the keys `k1`, `k2`, ..., and `kn`. +`f ( ... f (f (init, (k1,v1)), (k2,v2)), ..., (kn,vn))` +where `(k1,v1)`, `(k2,v2)`, ..., `(kn,vn)` are the bindings in the +map `map`, in increasing order of the keys `k1`, `k2`, ..., and `kn`. The call `Map.fold(f, map, init)` is - `f (... f (f (init, [k1,v1]), [k2,v2]), ..., [kn,vn])` - where `[k1,v1]`, `[k2,v2]`, ..., `[kn,vn]` are the bindings in the - map `map`, in increasing order of the keys `k1`, `k2`, ..., and `kn`. +`f (... f (f (init, [k1,v1]), [k2,v2]), ..., [kn,vn])` +where `[k1,v1]`, `[k2,v2]`, ..., `[kn,vn]` are the bindings in the +map `map`, in increasing order of the keys `k1`, `k2`, ..., and `kn`. @@ -320,12 +334,13 @@ The call `Map.fold(f, map, init)` is val iter : 'key 'value.(('key * 'value) -> unit) -> ('key, 'value) t -> unit -let iter: <key, value>(_: (_: [key, value]) => unit) => (_: t<key, value>) => unit +iter: <key, value>(f: (_: [key, value]) => unit, map: t<key, value>) => unit + The call `Map.iter f map` is - `let () = f (k1,v1) in let () = f (k2,v2) in ... in f (kn,vn)`. +`let () = f (k1,v1) in let () = f (k2,v2) in ... in f (kn,vn)`. @@ -340,22 +355,24 @@ The call `Map.iter(f, map)` is `{f (k1,v1); (k2,v2); ...; f (kn,vn);}`. val map : 'key 'value 'new_value.(('key * 'value) -> 'new_value) -> ('key, 'value) t -> ('key, 'new_value) t -let map: <key, value, new_value>(_: (_: [key, value]) => new_value) => (_: t<key, value>) => t<key, new_value> +map: <key, value, new_value>(f: (_: [key, value]) => +new_value, map: t<key, value>) => t<key, new_value> + The call `Map.map f m`, where the map `m` contains the bindings - `(k1,v1)`, `(k2,v2)`, ..., and `(kn,vn)` in increasing order of - the keys, is the map containing the bindings `(k1, f (k1,v1))`, - `(k2, f (k2,v2))`, ..., `(kn, f (kn,vn))`. +`(k1,v1)`, `(k2,v2)`, ..., and `(kn,vn)` in increasing order of +the keys, is the map containing the bindings `(k1, f (k1,v1))`, +`(k2, f (k2,v2))`, ..., `(kn, f (kn,vn))`. The call `Map.map(f, m)`, where the map `m` contains the bindings - `[k1,v1]`, `[k2,v2]`, ..., and `[kn,vn]` in increasing order of - the keys, is the map containing the bindings `[k1, f (k1,v1)]`, - `[k2, f (k2,v2)]`, ..., `[kn, f (kn,vn)]`. +`[k1,v1]`, `[k2,v2]`, ..., and `[kn,vn]` in increasing order of +the keys, is the map containing the bindings `[k1, f (k1,v1)]`, +`[k2, f (k2,v2)]`, ..., `[kn, f (kn,vn)]`. diff --git a/gitlab-pages/docs/reference/option.md b/gitlab-pages/docs/reference/option.md index ce9b006fc4..993efa39e3 100644 --- a/gitlab-pages/docs/reference/option.md +++ b/gitlab-pages/docs/reference/option.md @@ -9,24 +9,24 @@ import SyntaxTitle from '@theme/SyntaxTitle'; The module of optional values - val value : 'a.'a -> 'a option -> 'a -let value: <a>(_: a) => (_: option<a>) => a +value: <a>(default: a, opt: option<a>) => a + The call `Option.value d opt` is `v` if `opt` is `Some v`, and `d` - otherwise. +otherwise. -The call `Option.value(d, opt)` is `v` if `opt` is `Some(v)`, and `d` - otherwise. +The call `Option.value(d, opt)` is `v` if `opt` is +`["Some" as "Some", v]`, and `d` otherwise. @@ -35,96 +35,22 @@ The call `Option.value(d, opt)` is `v` if `opt` is `Some(v)`, and `d` val value_with_error : 'err 'a.'err -> 'a option -> 'a -let value_with_error: <err, a>(_: err) => (_: option<a>) => a - - - -The call `Option.value_with_error err opt` terminates with the error - `err` if, and only if, `opt` is `None`; otherwise it is `Some v` - and `v` is returned. - - - - - -The call `Option.value_with_error(err, opt)` terminates with the error - `err` if, and only if, `opt` is `None()`; otherwise it is `Some(v)` - and `v` is returned. - - - - - -val value_exn : 'err 'a.'err -> 'a option -> 'a - - -let value_exn: <err, a>(_: err) => (_: option<a>) => a +value_with_error: <err, a>(error: err, opt: option<a>) => a -**Deprecated:** Use `Option.value_with_error` instead. -The call `Option.value_exn err opt` terminates with the error `err` if, - and only if, `opt` is `None`; otherwise it is `Some v` and `v` is - returned. - - - - - -The call `Option.value_exn(err, opt)` terminates with the error `err` if, - and only if, `opt` is `None()`; otherwise it is `Some(v)` and `v` is - returned. - - - - - -val unopt_with_error : 'a.'a option -> string -> 'a - - -let unopt_with_error: <a>(_: option<a>) => (_: string) => a - -**Deprecated:** Use `Option.value_with_error` instead. - - - -The call `Option.unopt_with_error opt err` terminates with the error - `err` if, and only if, `opt` is `None`; otherwise it is `Some v` - and `v` is returned. - - - - - -The call `Option.unopt_with_error(opt, err)` terminates with the error - `err` if, and only if, `opt` is `None()`; otherwise it is - `Some(v)` and `v` is returned. - - - - - -val unopt : 'a.'a option -> 'a - - -let unopt: <a>(_: option<a>) => a - -**Deprecated:** Use `Option.value_with_error` instead. - - - -The call `Option.unopt opt ` terminates with the string - `"option is None"` if, and only if, `opt` is `None`; otherwise it is - `Some v` and `v` is returned. +The call `Option.value_with_error err opt` terminates with the error +`err` if, and only if, `opt` is `None`; otherwise it is `Some v` +and `v` is returned. -The call `Option.unopt(opt)` terminates with the string - `"option is None"` if, and only if, `opt` is `None()`; otherwise it is - `Some(v)` and `v` is returned. +The call `Option.value_with_error(err, opt)` terminates with the +error `err` if, and only if, `opt` is `["None" as "None"]`; +otherwise it is `["Some" as "Some", v]` and `v` is returned. @@ -133,19 +59,21 @@ The call `Option.unopt(opt)` terminates with the string val map : 'a 'b.('a -> 'b) -> 'a option -> 'b option -let map: <a, b>(_: (_: a) => b) => (_: option<a>) => option<b> +map: <a, b>(f: (_: a) => b, opt: option<a>) => option<b> + The call `Option.map f opt` is `None` if `opt` is `None`, and - `Some (f v)` if `opt` is `Some v`. +`Some (f v)` if `opt` is `Some v`. -The call `Option.map(f, opt)` is `None()` if `opt` is `None()`, and - `Some(f(v))` if `opt` is `Some(v)`. +The call `Option.map(f, opt)` is `["None" as "None"]` if `opt` is +`["None" as "None"]`, and `["Some" as "Some", f(v)]` if `opt` is +`["Some" as "Some", v]`. @@ -154,19 +82,18 @@ The call `Option.map(f, opt)` is `None()` if `opt` is `None()`, and val is_none : 'a.'a option -> bool -let is_none: <a>(_: option<a>) => bool +is_none: <a>(_: option<a>) => bool - -The call `Option.is_none opt` is `true` if, and only if, `opt` is - `None`. + +The call `Option.is_none opt` is `true` if, and only if, `opt` is `None`. The call `Option.is_none(opt)` is `true` if, and only if, `opt` is - `None()`. +`["None" as "None"]`. @@ -175,18 +102,18 @@ The call `Option.is_none(opt)` is `true` if, and only if, `opt` is val is_some : 'a.'a option -> bool -let is_some: <a>(_: option<a>) => bool +is_some: <a>(_: option<a>) => bool + -The call `Option.is_some opt` is `false` if, and only if, `opt` is - `None`. +The call `Option.is_some opt` is `false` if, and only if, `opt` is `None`. The call `Option.is_some(opt)` is `false` if, and only if, `opt` is - `None()`. +`["None" as "None"]`. diff --git a/gitlab-pages/docs/reference/tuple2.md b/gitlab-pages/docs/reference/pair.md similarity index 60% rename from gitlab-pages/docs/reference/tuple2.md rename to gitlab-pages/docs/reference/pair.md index f3c108825b..4bf32c2fc9 100644 --- a/gitlab-pages/docs/reference/tuple2.md +++ b/gitlab-pages/docs/reference/pair.md @@ -1,21 +1,19 @@ --- -id: tuple2-reference -title: tuple2 +id: pair-reference +title: pair hide_table_of_contents: true --- import Syntax from '@theme/Syntax'; import SyntaxTitle from '@theme/SyntaxTitle'; -Binary tuples - - val curry : 'a 'b 'c.(('a * 'b) -> 'c) -> 'a -> 'b -> 'c -let curry: <a, b, c>(_: (_: [a, b]) => c) => (_: a) => (_: b) => c +curry: <a, b, c>(f: (pair: [a, b]) => c, x: a, y: b) => c + The call `curry f x y` has the same value as `f (x,y)`. @@ -28,13 +26,13 @@ The call `curry(f,x,y)` has the same value as `f(x,y)`. - val uncurry : 'a 'b 'c.('a -> 'b -> 'c) -> ('a * 'b) -> 'c -let uncurry: <a, b, c>(_: (_: a) => (_: b) => c) => (_: [a, b]) => c +uncurry: <a, b, c>(f: (_: a) => (_: b) => c) => (_: [a, b]) => c + The call `uncurry f (x,y)` has the same value as `f x y`. @@ -49,27 +47,19 @@ The call `uncurry(f,[x,y])` has the same value as `f(x)(y)`. -val get1 : 'a 'b.('a * 'b) -> 'a +val fst : 'a 'b.('a * 'b) -> 'a -let get1: <a, b>(_: [a, b]) => a +fst: <a, b>(_: [a, b]) => a -Projecting the first component of a pair +Projecting the first component of a pair. -val get2 : 'a 'b.('a * 'b) -> 'b +val snd : 'a 'b.('a * 'b) -> 'b -let get2: <a, b>(_: [a, b]) => b +snd: <a, b>(_: [a, b]) => b -Projecting the second component of a pair. - - -val swap : 'a 'b.('a * 'b) -> ('b * 'a) - - -let swap: <a, b>(_: [a, b]) => [b, a] - -Swap the components of a pair. +Projecting the second component of a pair. diff --git a/gitlab-pages/docs/reference/set.md b/gitlab-pages/docs/reference/set.md index 24a3478cf0..307780bfa7 100644 --- a/gitlab-pages/docs/reference/set.md +++ b/gitlab-pages/docs/reference/set.md @@ -16,6 +16,7 @@ type 'elt t = 'elt set type t<elt> = set<elt> + The type `'elt Set.t` is an alias for `'elt set`. @@ -33,21 +34,22 @@ The type `Set.t` is an alias for `set`. val empty : 'elt.'elt t -let empty: <elt>t<elt> +empty: <elt>t<elt> + The value `Set.empty` denotes the empty set. In some contexts, it is - useful to annotate it with its type, for example: - `(Set.empty : int set)`. +useful to annotate it with its type, for example: `(Set.empty : int +set)`. The value `Set.empty` denotes the empty set. In some contexts, it is - useful to annotate it with its type, for example: - `(Set.empty as set)`. +useful to annotate it with its type, for example: +`(Set.empty as set)`. @@ -56,21 +58,22 @@ The value `Set.empty` denotes the empty set. In some contexts, it is val update : 'elt.'elt -> bool -> 'elt t -> 'elt t -let update: <elt>(_: elt) => (_: bool) => (_: t<elt>) => t<elt> +update: <elt>(elt: elt, add: bool, set: t<elt>) => t<elt> + The call `Set.update elt true set` is a copy of the set `set` - containing the element `elt`. The call `Set.update elt false set` is a - copy of the set `set` where the element `elt` is absent. +containing the element `elt`. The call `Set.update elt false set` is a +copy of the set `set` where the element `elt` is absent. The call `Set.update(elt, true, set)` is a copy of the set `set` - containing the element `elt`. The call `Set.update(elt, false, set)` is a - copy of the set `set` where the element `elt` is absent. +containing the element `elt`. The call `Set.update(elt, false, set)` is a +copy of the set `set` where the element `elt` is absent. @@ -79,19 +82,20 @@ The call `Set.update(elt, true, set)` is a copy of the set `set` val add : 'elt.'elt -> 'elt t -> 'elt t -let add: <elt>(_: elt) => (_: t<elt>) => t<elt> +add: <elt>(elt: elt, set: t<elt>) => t<elt> + The call `Set.add elt set` is a set containing all the elements of - the set `set`, plus the element `elt`. +the set `set`, plus the element `elt`. The call `Set.add(elt, set)` is a set containing all the elements of - the set `set`, plus the element `elt`. +the set `set`, plus the element `elt`. @@ -100,19 +104,20 @@ The call `Set.add(elt, set)` is a set containing all the elements of val remove : 'elt.'elt -> 'elt t -> 'elt t -let remove: <elt>(_: elt) => (_: t<elt>) => t<elt> +remove: <elt>(elt: elt, set: t<elt>) => t<elt> + The call `Set.remove elt set` is a copy of the set `set` without the - element `elt`. +element `elt`. The call `Set.remove(elt, set)` is a copy of the set `set` without the - element `elt`. +element `elt`. @@ -121,21 +126,21 @@ The call `Set.remove(elt, set)` is a copy of the set `set` without the val literal : 'elt.'elt list -> 'elt t -let literal: <elt>(_: list<elt>) => t<elt> +literal: <elt>(_: list<elt>) => t<elt> The call `Set.literal [e1; ...; en]` is a set containing exactly the - elements in the list. Note: The list must be literal, not an - expression (compile-time list of values). +elements in the list. Note: The list must be literal, not an +expression (compile-time list of values). The call `Set.literal(list([e1, ..., en]))` is a set containing - exactly the elements in the list. Note: The list must be literal, - not an expression (compile-time list of values). +exactly the elements in the list. Note: The list must be literal, not +an expression (compile-time list of values). @@ -144,23 +149,24 @@ The call `Set.literal(list([e1, ..., en]))` is a set containing val of_list : 'elt.'elt list -> 'elt t -let of_list: <elt>(_: list<elt>) => t<elt> +of_list: <elt>(_: list<elt>) => t<elt> + The call `Set.of_list elements` is a set containing exactly the - elements in the list `elements`. Note: Use `Set.literal` instead if - using a literal list. Note: Use `literal` instead if using a - literal list. +elements in the list `elements`. Note: Use `Set.literal` instead if +using a literal list. Note: Use `literal` instead if using a literal +list. The call `Set.of_list(elements)` is a set containing exactly the - elements in the list `elements`. Note: Use `Set.literal` instead if - using a literal list. Note: Use `literal` instead if using a - literal list. +elements in the list `elements`. Note: Use `Set.literal` instead if +using a literal list. Note: Use `literal` instead if using a literal +list. @@ -169,7 +175,7 @@ The call `Set.of_list(elements)` is a set containing exactly the val size : 'elt.'elt t -> nat -let size: <elt>(_: t<elt>) => nat +size: <elt>(_: t<elt>) => nat @@ -188,8 +194,9 @@ The call `Set.size(set)` is the number of elements of the set `set`. val cardinal : 'elt.'elt t -> nat -let cardinal: <elt>(_: t<elt>) => nat +cardinal: <elt>(_: t<elt>) => nat + The call `Set.cardinal set` is the number of elements of the set `set`. @@ -207,19 +214,20 @@ The call `Set.cardinal(set)` is the number of elements of the set `set`. val mem : 'elt.'elt -> 'elt t -> bool -let mem: <elt>(_: elt) => (_: t<elt>) => bool +mem: <elt>(elt: elt, set: t<elt>) => bool + The call `Set.mem elt set` is `true` if, and only if, the element - `elt` belongs to the set `set`. +`elt` belongs to the set `set`. The call `Set.mem(elt, set)` is `true` if, and only if, the element - `elt` belongs to the set `set`. +`elt` belongs to the set `set`. @@ -228,71 +236,72 @@ The call `Set.mem(elt, set)` is `true` if, and only if, the element val fold : 'elt 'acc.(('acc * 'elt) -> 'acc) -> 'elt t -> 'acc -> 'acc -let fold: <elt, acc>(_: (_: [acc, elt]) => acc) => (_: t<elt>) => (_: acc) => acc +fold: <elt, acc>(f: (_: [acc, elt]) => acc, set: +t<elt>, init: acc) => acc + -The call `Set.fold f set init` is - `f(... (f (f (init, e1), e2), ...), en)`, - where `e1`, `e2`, ..., `en` are the elements of the set `set` in - increasing order. +The call `Set.fold f set init` is `f(... (f (f (init, e1), e2), ...), +en)`, where `e1`, `e2`, ..., `en` are the elements of the set `set` in +increasing order. -The call `Set.fold(f, set, init)` is - `f(... (f (f (init, e1), e2), ...), en)`, - where `e1`, `e2`, ..., `en` are the elements of the set `set` in - increasing order. +The call `Set.fold(f, set, init)` is `f(... (f (f (init, e1), e2), ...), en)`, where `e1`, `e2`, ..., `en` are the elements of the +set `set` in increasing order. -val fold_desc : 'elt 'acc.(('elt * 'acc) -> 'acc) -> 'elt t -> 'acc -> 'acc +val fold_desc : 'elt 'acc.(('elt * 'acc) -> 'acc) -> 'elt t -> 'acc -> 'acc -let fold_desc: <elt, acc>(_: (_: [elt, acc]) => acc) => (_: t<elt>) => (_: acc) => acc +fold_desc: <elt, acc>(f: (_: [elt, acc]) => acc, set: +t<elt>, init: acc) => acc + The call `Set.fold f set init` is `f(... (f (init, en), ...), e1)`, - where `e1`, `e2`, ..., `en` are the elements of the set `set` in - increasing order. +where `e1`, `e2`, ..., `en` are the elements of the set `set` in +increasing order. The call `Set.fold(f, set, init)` is `f(... (f (init, en), ...), e1)`, - where `e1`, `e2`, ..., `en` are the elements of the set `set` in - increasing order. +where `e1`, `e2`, ..., `en` are the elements of the set `set` in +increasing order. - -val filter_map : 'old 'new.('old -> 'new option) -> 'old t -> 'new t +val filter_map : 'old 'new.('old -> 'new option) -> 'old t -> 'new t -let filter_map: <old, new>(_: (_: old) => option<new>) => (_: t<old>) => t<new> +filter_map: <old, new>(filter: (elt: old) => option<new>, set: t<old>) => t<new> + The call `Set.filter_map f set` is a set made by calling `f` (the - filter) on each element of the set `set`: if `f` returns `None`, - the element is skipped in the result, otherwise, if it is - `Some e`, then `e` is kept. +filter) on each element of the set `set`: if `f` returns `None`, the +element is skipped in the result, otherwise, if it is `Some e`, then +`e` is kept. The call `Set.filter_map(f, set)` is a set made by calling `f` (the - filter) on each element of the set `set`: if `f` returns `None()`, - the element is skipped in the result, otherwise, if it is - `Some(e)`, then `e` is kept. +filter) on each element of the set `set`: if `f` returns +`["None" as "None"]`, the element is skipped in the result, otherwise, +if it is `["Some" as "Some", e]`, then `e` is kept. @@ -301,19 +310,20 @@ The call `Set.filter_map(f, set)` is a set made by calling `f` (the val iter : 'elt.('elt -> unit) -> 'elt t -> unit -let iter: <elt>(_: (_: elt) => unit) => (_: t<elt>) => unit +iter: <elt>(f: (_: elt) => unit, set: t<elt>) => unit + The call `Set.iter f set` applies `f` to all the elements of the set - `set` in increasing order. +`set` in increasing order. The call `Set.iter(f, set)` applies `f` to all the elements of the set - `set` in increasing order. +`set` in increasing order. @@ -322,18 +332,19 @@ The call `Set.iter(f, set)` applies `f` to all the elements of the set val map : 'old 'new.('old -> 'new) -> 'old t -> 'new t -let map: <old, new>(_: (_: old) => new) => (_: t<old>) => t<new> +map: <old, new>(f: (elt: old) => new, set: t<old>) => t<new> + The call `Set.map f set` evaluates in a set whose elements have been - obtained by applying `f` to the elements of the set `set`. +obtained by applying `f` to the elements of the set `set`. The call `Set.map(f, set)` evaluates in a set whose elements have been - obtained by applying `f` to the elements of the set `set`. +obtained by applying `f` to the elements of the set `set`. diff --git a/gitlab-pages/docs/reference/src/current/b.jsligo b/gitlab-pages/docs/reference/src/current/b.jsligo index a3d870efa5..b6f0f98224 100644 --- a/gitlab-pages/docs/reference/src/current/b.jsligo +++ b/gitlab-pages/docs/reference/src/current/b.jsligo @@ -1,5 +1,5 @@ -let today = Tezos.get_now(); -let one_day = 86_400; -let in_24_hrs = today + one_day; -let some_date = ("2000-01-01t10:10:10Z" as timestamp); -let one_day_later = some_date + one_day; \ No newline at end of file +const today = Tezos.get_now(); +const one_day = 86_400; +const in_24_hrs = today + one_day; +const some_date = ("2000-01-01t10:10:10Z" as timestamp); +const one_day_later = some_date + one_day; \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/c.jsligo b/gitlab-pages/docs/reference/src/current/c.jsligo index a88233808a..526d4a84f5 100644 --- a/gitlab-pages/docs/reference/src/current/c.jsligo +++ b/gitlab-pages/docs/reference/src/current/c.jsligo @@ -1,4 +1,4 @@ -let today = Tezos.get_now(); -let one_day = 86_400; -let in_24_hrs = today - one_day; -let not_tomorrow = (Tezos.get_now() == in_24_hrs); \ No newline at end of file +const today = Tezos.get_now(); +const one_day = 86_400; +const in_24_hrs = today - one_day; +const not_tomorrow = (Tezos.get_now() == in_24_hrs); \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/contract_ticket.jsligo b/gitlab-pages/docs/reference/src/current/contract_ticket.jsligo index cb2bb88a9a..0d078ae7c6 100644 --- a/gitlab-pages/docs/reference/src/current/contract_ticket.jsligo +++ b/gitlab-pages/docs/reference/src/current/contract_ticket.jsligo @@ -1,12 +1,12 @@ -type storage = big_map> ; - -type parameter = int ; - +type storage = big_map>; +type parameter = int; type result = [list, storage]; -@entry +// @entry function main (i: parameter, store : storage): result { - let my_ticket1 = Option.unopt (Tezos.create_ticket (i, 10n)); - let [_x, ret] = Big_map.get_and_update ("hello", Some(my_ticket1), store); + const my_ticket1 = + Option.value_with_error ("no ticket", Tezos.Ticket.create(i, 10 as nat)); + const [_x, ret] = + Big_map.get_and_update ("hello", ["Some" as "Some", my_ticket1], store); return [[], ret] }; \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/contract_ticket.mligo b/gitlab-pages/docs/reference/src/current/contract_ticket.mligo index 3a1c6280ad..7f25ad4233 100644 --- a/gitlab-pages/docs/reference/src/current/contract_ticket.mligo +++ b/gitlab-pages/docs/reference/src/current/contract_ticket.mligo @@ -4,6 +4,7 @@ type result = operation list * storage [@entry] let main (i : parameter) (store : storage) : result = - let my_ticket1 = Option.unopt (Tezos.create_ticket i 10n) in + let my_ticket1 = + Option.value_with_error "no ticket" (Tezos.Ticket.create i 10n) in let _, x = Big_map.get_and_update "hello" (Some my_ticket1) store in [], x \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/e.jsligo b/gitlab-pages/docs/reference/src/current/e.jsligo index 47863f4b01..bcf5f7e13d 100644 --- a/gitlab-pages/docs/reference/src/current/e.jsligo +++ b/gitlab-pages/docs/reference/src/current/e.jsligo @@ -1 +1 @@ -let check = (p : unit) => Tezos.get_sender (); \ No newline at end of file +const check = (p : unit) => Tezos.get_sender(); \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/e.mligo b/gitlab-pages/docs/reference/src/current/e.mligo new file mode 100644 index 0000000000..35a9f3ab97 --- /dev/null +++ b/gitlab-pages/docs/reference/src/current/e.mligo @@ -0,0 +1 @@ +let check (p : unit) = Tezos.get_sender () \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/f.jsligo b/gitlab-pages/docs/reference/src/current/f.jsligo index 41493f852f..057b0a1fb6 100644 --- a/gitlab-pages/docs/reference/src/current/f.jsligo +++ b/gitlab-pages/docs/reference/src/current/f.jsligo @@ -1,4 +1,4 @@ -let check = (p : key_hash) => { - let c = Tezos.implicit_account(p); +const check = (p : key_hash) => { + const c = Tezos.implicit_account(p); return Tezos.address(c); }; \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/f.mligo b/gitlab-pages/docs/reference/src/current/f.mligo new file mode 100644 index 0000000000..52f842e9e0 --- /dev/null +++ b/gitlab-pages/docs/reference/src/current/f.mligo @@ -0,0 +1,3 @@ +let check (p : key_hash) = + let c = Tezos.implicit_account p + in Tezos.address c \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/g.jsligo b/gitlab-pages/docs/reference/src/current/g.jsligo index ffd82fa79f..546388ea22 100644 --- a/gitlab-pages/docs/reference/src/current/g.jsligo +++ b/gitlab-pages/docs/reference/src/current/g.jsligo @@ -1 +1 @@ -let check = (p : unit) => Tezos.get_self_address(); \ No newline at end of file +const check = (p : unit) => Tezos.get_self_address(); \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/g.mligo b/gitlab-pages/docs/reference/src/current/g.mligo new file mode 100644 index 0000000000..1e9447e6ce --- /dev/null +++ b/gitlab-pages/docs/reference/src/current/g.mligo @@ -0,0 +1 @@ +let check (p : unit) = Tezos.get_self_address () \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/h.jsligo b/gitlab-pages/docs/reference/src/current/h.jsligo index 3d4b5a8419..a54c61465a 100644 --- a/gitlab-pages/docs/reference/src/current/h.jsligo +++ b/gitlab-pages/docs/reference/src/current/h.jsligo @@ -1 +1 @@ -let check = (p: unit) => Tezos.self("%default"); \ No newline at end of file +const check = (p: unit) => Tezos.self("%default"); \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/h.mligo b/gitlab-pages/docs/reference/src/current/h.mligo new file mode 100644 index 0000000000..cd5b84b08d --- /dev/null +++ b/gitlab-pages/docs/reference/src/current/h.mligo @@ -0,0 +1 @@ +let check (p : unit) = Tezos.self("%default") \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/i.jsligo b/gitlab-pages/docs/reference/src/current/i.jsligo index 5e07248690..e57a632a7e 100644 --- a/gitlab-pages/docs/reference/src/current/i.jsligo +++ b/gitlab-pages/docs/reference/src/current/i.jsligo @@ -1 +1 @@ -let check = (kh: key_hash) => Tezos.implicit_account(kh); \ No newline at end of file +const check = (kh: key_hash) => Tezos.implicit_account(kh); \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/i.mligo b/gitlab-pages/docs/reference/src/current/i.mligo new file mode 100644 index 0000000000..1ba72464d0 --- /dev/null +++ b/gitlab-pages/docs/reference/src/current/i.mligo @@ -0,0 +1 @@ +let check (kh : key_hash) = Tezos.implicit_account kh \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/j.jsligo b/gitlab-pages/docs/reference/src/current/j.jsligo index 58fefa57bb..8e15b476fd 100644 --- a/gitlab-pages/docs/reference/src/current/j.jsligo +++ b/gitlab-pages/docs/reference/src/current/j.jsligo @@ -1 +1 @@ -let check = (p : unit) => Tezos.get_source(); \ No newline at end of file +const check = (p : unit) => Tezos.get_source(); \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/j.mligo b/gitlab-pages/docs/reference/src/current/j.mligo new file mode 100644 index 0000000000..7afa224a9b --- /dev/null +++ b/gitlab-pages/docs/reference/src/current/j.mligo @@ -0,0 +1 @@ +let check (p : unit) = Tezos.get_source () \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/k.jsligo b/gitlab-pages/docs/reference/src/current/k.jsligo index 63f59a9dd5..3515dec4b5 100644 --- a/gitlab-pages/docs/reference/src/current/k.jsligo +++ b/gitlab-pages/docs/reference/src/current/k.jsligo @@ -1,11 +1,9 @@ type storage = bytes; -@entry -let main = (_ignore: unit, storage: storage) : [list, storage] => { - let packed = Bytes.pack(Tezos.get_chain_id()); - if (storage != packed) { +// @entry +const main = (_ignore: unit, storage: storage) : [list, storage] => { + const packed = Bytes.pack(Tezos.get_chain_id()); + if (storage != packed) return failwith("wrong chain") as [list, storage]; - } else { - return [[], packed]; - }; + else return [[], packed]; }; \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/k.mligo b/gitlab-pages/docs/reference/src/current/k.mligo new file mode 100644 index 0000000000..bb30225ad4 --- /dev/null +++ b/gitlab-pages/docs/reference/src/current/k.mligo @@ -0,0 +1,9 @@ +type storage = bytes + +[@entry] +let main (_ignore : unit) (store : storage) : operation list * storage += + let packed = Bytes.pack (Tezos.get_chain_id ()) in + if store <> packed then + (failwith "wrong chain" : operation list * storage) + else ([], packed) \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/sap_t.jsligo b/gitlab-pages/docs/reference/src/current/sap_t.jsligo index e45f838088..1a878f18bc 100644 --- a/gitlab-pages/docs/reference/src/current/sap_t.jsligo +++ b/gitlab-pages/docs/reference/src/current/sap_t.jsligo @@ -1,8 +1,8 @@ type st = sapling_state<8>; type tr = sapling_transaction<8>; -let x = Tezos.sapling_empty_state ; -let f = (tr : tr) => - match (Tezos.sapling_verify_update(tr, x)) { - when(Some(p)): p[1]; - when(None()): failwith ("failed") - }; \ No newline at end of file +const x = Tezos.Sapling.empty_state; +const f = (tr : tr) => + $match(Tezos.Sapling.verify_update(tr, x), { + "Some": p => p[1], + "None": () => failwith ("failed") + }); \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/sap_t.mligo b/gitlab-pages/docs/reference/src/current/sap_t.mligo index 9e7290fde5..2352206cc0 100644 --- a/gitlab-pages/docs/reference/src/current/sap_t.mligo +++ b/gitlab-pages/docs/reference/src/current/sap_t.mligo @@ -1,7 +1,7 @@ type st = 8 sapling_state type tr = 8 sapling_transaction -let x = Tezos.sapling_empty_state +let x = Tezos.Sapling.empty_state let f (tr : tr) = - match Tezos.sapling_verify_update tr x with + match Tezos.Sapling.verify_update tr x with Some (_, x) -> x | None -> (failwith "failed" : int * st) \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/ungrouped.jsligo b/gitlab-pages/docs/reference/src/current/ungrouped.jsligo index 008a4cf7c4..2adde36244 100644 --- a/gitlab-pages/docs/reference/src/current/ungrouped.jsligo +++ b/gitlab-pages/docs/reference/src/current/ungrouped.jsligo @@ -1,5 +1,5 @@ -let check = (p: unit, s: tez):[list, tez] => +const check = (p: unit, s: tez):[list, tez] => [[], Tezos.get_balance()]; function threshold (p : unit) { - if (Tezos.get_amount() == 100tez) return 42 else return 0; + if (Tezos.get_amount() == (100 as tez)) return 42; else return 0; }; \ No newline at end of file diff --git a/gitlab-pages/docs/reference/src/current/ungrouped.mligo b/gitlab-pages/docs/reference/src/current/ungrouped.mligo index b991b27a11..ee2f29c413 100644 --- a/gitlab-pages/docs/reference/src/current/ungrouped.mligo +++ b/gitlab-pages/docs/reference/src/current/ungrouped.mligo @@ -1,19 +1,2 @@ let check (p,s : unit * tez) = [], Tezos.get_balance() -let threshold (p : unit) = if Tezos.get_amount () = 100tz then 42 else 0 -let check (p : unit) = Tezos.get_sender () -let check (p : key_hash) = - let c = Tezos.implicit_account p - in Tezos.address c -let check (p : unit) = Tezos.get_self_address () -let check (p : unit) = Tezos.self("%default") -let check (kh : key_hash) = Tezos.implicit_account kh -let check (p : unit) = Tezos.get_source () -type storage = bytes - -[@entry] -let main (_ignore : unit) (store : storage) = - let packed = Bytes.pack (Tezos.get_chain_id ()) in - if (store <> packed) then - (failwith "wrong chain" : (operation list * storage)) - else - ([], (packed: storage)) \ No newline at end of file +let threshold (p : unit) = if Tezos.get_amount () = 100tz then 42 else 0 \ No newline at end of file diff --git a/gitlab-pages/docs/reference/string.md b/gitlab-pages/docs/reference/string.md index 2fe2bc0927..c954b95d7c 100644 --- a/gitlab-pages/docs/reference/string.md +++ b/gitlab-pages/docs/reference/string.md @@ -3,6 +3,7 @@ id: string-reference title: string hide_table_of_contents: true --- + import Syntax from '@theme/Syntax'; import SyntaxTitle from '@theme/SyntaxTitle'; @@ -14,19 +15,20 @@ Strings of characters val length : string -> nat -let length: (_: string) => nat +length: (_: string) => nat + The call `String.length s` is the number of characters in the string - `s`. Note: `String.length` is another name for `String.size`. +`s`. Note: `String.length` is another name for `String.size`. The call `String.length(s)` is the number of characters in the string - `s`. Note: `String.length` is another name for `String.size`. +`s`. Note: `String.length` is another name for `String.size`. @@ -35,8 +37,9 @@ The call `String.length(s)` is the number of characters in the string val size : string -> nat -let size: (_: string) => nat +size: (_: string) => nat + The call `String.size s` is the number of characters in the string `s`. @@ -54,19 +57,20 @@ The call `String.size(s)` is the number of characters in the string `s`. val concat : string -> string -> string -let concat: (_: string) => (_: string) => string +concat: (left: string, right: string) => string + The call `String.concat left right` is the concatenation of the string - `left` and the string `right`, in that order. +`left` and the string `right`, in that order. The call `String.concat(left, right)` is the concatenation of the string - `left` and the string `right`, in that order. +`left` and the string `right`, in that order. @@ -75,19 +79,20 @@ The call `String.concat(left, right)` is the concatenation of the string val concats : string list -> string -let concats: (_: list<string>) => string +concats: (list: list<string>) => string + The call `String.concats list` is the concatenation of the strings in - the list `list`, from left to right. +the list `list`, from left to right. The call `String.concats(list)` is the concatenation of the strings in - the list `list`, from left to right. +the list `list`, from left to right. @@ -96,23 +101,24 @@ The call `String.concats(list)` is the concatenation of the strings in val sub : nat -> nat -> string -> string -let sub: (_: nat) => (_: nat) => (_: string) => string +sub: (index: nat, length: nat, string: string) => string + The call `String.sub index len str` is the substring of string `str` - starting at index `index` (0 denoting the first character) and of - length `len`. If the index or length are invalid, an exception - interrupts the execution. +starting at index `index` (0 denoting the first character) and of +length `len`. If the index or length are invalid, an exception +interrupts the execution. The call `String.sub(index, len, str)` is the substring of string `str` - starting at index `index` (0 denoting the first character) and of - length `len`. If the index or length are invalid, an exception - interrupts the execution. +starting at index `index` (0 denoting the first character) and of +length `len`. If the index or length are invalid, an exception +interrupts the execution. @@ -121,22 +127,23 @@ The call `String.sub(index, len, str)` is the substring of string `str` val slice : nat -> nat -> string -> string -let slice: (_: nat) => (_: nat) => (_: string) => string +slice: (index: nat, length: nat, string: string) => string + The call `String.slice index len str` is the substring of string `str` - starting at index `index` (0 denoting the first character) and of - length `len`. If the index or length are invalid, an exception - interrupts the execution. +starting at index `index` (0 denoting the first character) and of +length `len`. If the index or length are invalid, an exception +interrupts the execution. The call `String.slice(index, len, str)` is the substring of string `str` - starting at index `index` (0 denoting the first character) and of - length `len`. If the index or length are invalid, an exception - interrupts the execution. +starting at index `index` (0 denoting the first character) and of +length `len`. If the index or length are invalid, an exception +interrupts the execution. diff --git a/gitlab-pages/docs/reference/test.next.account.contract.md b/gitlab-pages/docs/reference/test.account.contract.md similarity index 55% rename from gitlab-pages/docs/reference/test.next.account.contract.md rename to gitlab-pages/docs/reference/test.account.contract.md index 05accc2c3e..a1aa144ae0 100644 --- a/gitlab-pages/docs/reference/test.next.account.contract.md +++ b/gitlab-pages/docs/reference/test.account.contract.md @@ -1,5 +1,5 @@ --- -id: test.next.account.contract-reference +id: test.account.contract-reference title: contract hide_table_of_contents: true --- @@ -7,24 +7,23 @@ import Syntax from '@theme/Syntax'; import SyntaxTitle from '@theme/SyntaxTitle'; - val bootstrap : nat -> address -let bootstrap: (_: nat) => address +bootstrap: (_: nat) => address -Returns the address corresponding to the nth bootstrapped - contract. + +Returns the address corresponding to the nth bootstrapped contract. val bootstrap_typed_address : 'a 'b.nat -> ('a, 'b) typed_address -let bootstrap_typed_address: <a, b>(_: nat) => typed_address<a, b> +bootstrap_typed_address: <a, b>(_: nat) => typed_address<a, b> + Returns the typed address corresponding to the nth bootstrapped - contract currently loaded. The types are inferred from those - contracts loaded with `Test.State.Reset.add_func_contract` - (before reset). +contract currently loaded. The types are inferred from those contracts +loaded with `Test.State.Reset.add_func_contract` (before reset). diff --git a/gitlab-pages/docs/reference/test.next.account.md b/gitlab-pages/docs/reference/test.account.md similarity index 73% rename from gitlab-pages/docs/reference/test.next.account.md rename to gitlab-pages/docs/reference/test.account.md index c9acf86850..1096cba552 100644 --- a/gitlab-pages/docs/reference/test.next.account.md +++ b/gitlab-pages/docs/reference/test.account.md @@ -1,5 +1,5 @@ --- -id: test.next.account-reference +id: test.account-reference title: account hide_table_of_contents: true --- @@ -7,16 +7,16 @@ import Syntax from '@theme/Syntax'; import SyntaxTitle from '@theme/SyntaxTitle'; - -[module contract](test.next.account.contract.md) +[module contract](test.account.contract.md) val address : nat -> address -let address: (n: nat) => address +address: (n: nat) => address + Returns the address of the nth bootstrapped account. @@ -24,8 +24,9 @@ Returns the address of the nth bootstrapped account. val alice : unit -> address -let alice: (_: unit) => address +alice: (_: unit) => address + Returns the address of the 0th bootstrapped account. @@ -33,8 +34,9 @@ Returns the address of the 0th bootstrapped account. val bob : unit -> address -let bob: (_: unit) => address +bob: (_: unit) => address + Returns the address of the 1st bootstrapped account. @@ -42,8 +44,9 @@ Returns the address of the 1st bootstrapped account. val carol : unit -> address -let carol: (_: unit) => address +carol: (_: unit) => address + Returns the address of the 2nd bootstrapped account. @@ -51,8 +54,9 @@ Returns the address of the 2nd bootstrapped account. val dan : unit -> address -let dan: (_: unit) => address +dan: (_: unit) => address + Returns the address of the 3rd bootstrapped account. @@ -60,10 +64,11 @@ Returns the address of the 3rd bootstrapped account. val add : string -> key -> unit -let add: (_: string) => (_: key) => unit +add: (public: string, secret: key) => unit + Adds an account specfied by secret key & public key to the test - context. +context. @@ -78,19 +83,20 @@ type info = { addr: address; pk: key; sk: string } -val info : nat -> Test.Next.Account.info +val info : nat -> Test.Account.info -let info: (_: nat) => Test.Next.Account.info +info: (_: nat) => Test.Account.info -Returns the address information of the nth bootstrapped - account. + +Returns the address information of the nth bootstrapped account. -val new : unit -> Test.Next.Account.info +val new : unit -> Test.Account.info -let new: (_: unit) => Test.Next.Account.info +new: (_: unit) => Test.Account.info + Creates and returns information of a new account. diff --git a/gitlab-pages/docs/reference/Test.Next.Address.md b/gitlab-pages/docs/reference/test.address.md similarity index 60% rename from gitlab-pages/docs/reference/Test.Next.Address.md rename to gitlab-pages/docs/reference/test.address.md index 009b1eccc0..4124ee9517 100644 --- a/gitlab-pages/docs/reference/Test.Next.Address.md +++ b/gitlab-pages/docs/reference/test.address.md @@ -1,5 +1,5 @@ --- -id: test.next.address-reference +id: test.address-reference title: address hide_table_of_contents: true --- @@ -7,24 +7,29 @@ import Syntax from '@theme/Syntax'; import SyntaxTitle from '@theme/SyntaxTitle'; - val get_balance : address -> tez -let get_balance: (_: address) => tez +get_balance: (_: address) => tez +Gets the balance of an account (given as an address) in tez. + val to_typed_address : 'a 'b.address -> ('a, 'b) typed_address -let to_typed_address: <a, b>(_: address) => typed_address<a, b> +to_typed_address: <a, b>(_: address) => typed_address<a, b> +This function casts an address to a typed address. You will need to +annotate the result with the type you expect. + + val get_storage : 'b.address -> 'b -let get_storage: <b>(_: address) => b +get_storage: <b>(_: address) => b diff --git a/gitlab-pages/docs/reference/test.assert.error.md b/gitlab-pages/docs/reference/test.assert.error.md new file mode 100644 index 0000000000..9f0aff3b76 --- /dev/null +++ b/gitlab-pages/docs/reference/test.assert.error.md @@ -0,0 +1,85 @@ +--- +id: test.assert.error-reference +title: error +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + + +val assert : bool -> string -> unit + + +assert: (condition: bool, error: string) => unit + + + + +The call `assert cond error` terminates the execution with the string +`error` (that is, an error message) if, and only if, the boolean +condition `cond` is false. The failure is handled by LIGO's testing +framework and not by Michelson's interpreter. + + + + + +The call `assert(cond, error)` terminates the execution with the +string `error` (that is, an error message) if, and only if, the +boolean condition `cond` is false. The failure is handled by LIGO's +testing framework and not by Michelson's interpreter. + + + + + +val some : 'a.'a option -> string -> unit + + +some: <a>(_: option<a>, error: string) => unit + + + +The call `some opt err` terminates the execution with the string `err` +(that is, an error message) if, and only if, `opt` is `None`. The +failure is handled by LIGO's testing framework and not by Michelson's +interpreter. + + + + + +The call `some(opt, err)` terminates the execution with the string +`err` (that is, an error message) if, and only if, `opt` is +`["None" as "None"]`. The failure is handled by LIGO's testing +framework and not by Michelson's interpreter. + + + + + +val none : 'a.'a option -> string -> unit + + +none: <a>(_: option<a>, error: string) => unit + + + + +The call `none opt err` terminates the execution with the string `err` +(that is, an error message) if, and only if, `opt` is an optional +value different from `None`. The failure is handled by LIGO's testing +framework and not by Michelson's interpreter. + + + + + +The call `none(opt, err)` terminates the execution with the string +`err` (that is, an error message) if, and only if, `opt` is an +optional value different from `["None" as "None"]`. The failure is +handled by LIGO's testing framework and not by Michelson's +interpreter. + + diff --git a/gitlab-pages/docs/reference/test.assert.md b/gitlab-pages/docs/reference/test.assert.md new file mode 100644 index 0000000000..378238e870 --- /dev/null +++ b/gitlab-pages/docs/reference/test.assert.md @@ -0,0 +1,97 @@ +--- +id: test.assert-reference +title: assert +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + +[module error](test.assert.error.md) + + + +val failwith : 'a 'b.'a -> 'b + + +failwith: <a, b>(_: a) => b + + +Cause the testing framework to fail. + + + +val assert : bool -> unit + + +assert: (_: bool) => unit + + + + +The call `assert cond` terminates the execution with the string +`"failed assertion"` if, and only if, the boolean condition `cond` is +false. The failure is handled by LIGO's testing framework and not by +Michelson's interpreter. + + + + + +The call `assert(cond)` terminates the execution with the string +`"failed assertion"` if, and only if, the boolean condition `cond` is +false. The failure is handled by LIGO's testing framework and not by +Michelson's interpreter. + + + + + +val some : 'a.'a option -> unit + + +some: <a>(_: option<a>) => unit + + + + +The call `some opt` terminates the execution with the string `"failed +assert some"` if, and only if, `opt` is `None`. The failure is +handled by LIGO's testing framework and not by Michelson's +interpreter. + + + + + +The call `some(opt)` terminates the execution with the string `"failed +assert some"` if, and only if, `opt` is `["None" as "None"]`. The +failure is handled by LIGO's testing framework and not by Michelson's +interpreter. + + + + + +val none : 'a.'a option -> unit + + +none: <a>(_: option<a>) => unit + + + +The call `none opt` terminates the execution with the string `"failed +assert none"` if, and only if, `opt` is not `None`. The failure is +handled by LIGO's testing framework and not by Michelson's +interpreter. + + + + + +The call `none(opt)` terminates the execution with the string `"failed +assert none"` if, and only if, `opt` is not `["None" as "None"]`. The +failure is handled by LIGO's testing framework and not by Michelson's +interpreter. + + diff --git a/gitlab-pages/docs/reference/test.compare.md b/gitlab-pages/docs/reference/test.compare.md new file mode 100644 index 0000000000..91e0719060 --- /dev/null +++ b/gitlab-pages/docs/reference/test.compare.md @@ -0,0 +1,139 @@ +--- +id: test.compare-reference +title: compare +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + + +val eq : 'a.'a -> 'a -> bool + + +eq: <a>(lhs: a, rhs: a) => bool + + + + +The call `eq x y` returns `true` if, and only if, `x` and `y` are +considered to be equal w.r.t. the order on the underlying type. + + + + + +The call `eq(x, y)` returns `true` if, and only if, `x` and `y` are +considered to be equal w.r.t. the order on the underlying type. + + + + + +val neq : 'a.'a -> 'a -> bool + + +neq: <a>(lhs: a, rhs: a) => bool + + + + +The call `neq x y` returns `true` if, and only if, `x` and `y` are not +considered to be equal w.r.t. the order on the underlying type. + + + + + +The call `neq(x, y)` returns `true` if, and only if, `x` and `y` are +not considered to be equal w.r.t. the order on the underlying type. + + + + + +val gt : 'a.'a -> 'a -> bool + + +gt: <a>(lhs: a, rhs: a) => bool + + + + +The call `gt x y` returns `true` if, and only if, `x` is considered to +be greater than `y` w.r.t. the order on the underlying type. + + + + + +The call `gt(x, y)` returns `true` if, and only if, `x` is considered +to be greater than `y` w.r.t. the order on the underlying type. + + + + + +val lt : 'a.'a -> 'a -> bool + + +lt: <a>(lhs: a, rhs: a) => bool + + + + +The call `lt x y` returns `true` if, and only if, `x` is considered to +be less than `y` w.r.t. the order on the underlying type. + + + + + +The call `lt(x, y)` returns `true` if, and only if, `x` is considered +to be less than `y` w.r.t. the order on the underlying type. + + + + + +val ge : 'a.'a -> 'a -> bool + + +ge: <a>(lhs: a, rhs: a) => bool + + + +The call `ge x y` returns `true` if, and only if, `x` is considered to +be greater or equal than `y` w.r.t. the order on the underlying type. + + + + + +The call `ge(x, y)` returns `true` if, and only if, `x` is considered +to be greater or equal than `y` w.r.t. the order on the underlying +type. + + + + + +val le : 'a.'a -> 'a -> bool + + +le: <a>(lhs: a, rhs: a) => bool + + + + +The call `le x y` returns `true` if, and only if, `x` is considered to +be less or equal than `y` w.r.t. the order on the underlying type. + + + + + +The call `le(x, y)` returns `true` if, and only if, `x` is considered +to be less or equal than `y` w.r.t. the order on the underlying type. + + diff --git a/gitlab-pages/docs/reference/test.contract.md b/gitlab-pages/docs/reference/test.contract.md new file mode 100644 index 0000000000..5671dd1299 --- /dev/null +++ b/gitlab-pages/docs/reference/test.contract.md @@ -0,0 +1,38 @@ +--- +id: test.contract-reference +title: contract +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + + +val transfer : 'p.'p contract -> 'p -> tez -> test_exec_result + + +transfer: <p>(_: contract<p>, param: p, amount: tez) => test_exec_result + + +Bake a transaction by sending an amount of tez with a parameter from +the current source to a contract. Returns the amount of gas consumed +by the execution of the contract. + + +val transfer_exn : 'p.'p contract -> 'p -> tez -> nat + + +transfer_exn: <p>(_: contract<p>, param: p, amount: tez) => nat + + +Bakes a transaction by sending an amount of tez with a parameter from +the current source to a contract. Returns the amount of gas consumed +by the execution of the contract. Similar to `transfer`, but fails +when anything goes wrong. + + +val to_typed_address : 'p 's.'p contract -> ('p, 's) typed_address + + +to_typed_address: <p, s>(_: contract<p>) => typed_address<p, s> + diff --git a/gitlab-pages/docs/reference/Test.Next.Crypto.md b/gitlab-pages/docs/reference/test.crypto.md similarity index 74% rename from gitlab-pages/docs/reference/Test.Next.Crypto.md rename to gitlab-pages/docs/reference/test.crypto.md index 9d5ea1ec53..d0affb3447 100644 --- a/gitlab-pages/docs/reference/Test.Next.Crypto.md +++ b/gitlab-pages/docs/reference/test.crypto.md @@ -1,5 +1,5 @@ --- -id: test.next.crypto-reference +id: test.crypto-reference title: crypto hide_table_of_contents: true --- @@ -12,5 +12,5 @@ import SyntaxTitle from '@theme/SyntaxTitle'; val sign : string -> bytes -> signature -let sign: (_: string) => (_: bytes) => signature +sign: (_: string, _: bytes) => signature diff --git a/gitlab-pages/docs/reference/test.next.dynamic_entrypoints.md b/gitlab-pages/docs/reference/test.dynamic_entrypoints.md similarity index 51% rename from gitlab-pages/docs/reference/test.next.dynamic_entrypoints.md rename to gitlab-pages/docs/reference/test.dynamic_entrypoints.md index 19b59628ea..ae0dde6fb8 100644 --- a/gitlab-pages/docs/reference/test.next.dynamic_entrypoints.md +++ b/gitlab-pages/docs/reference/test.dynamic_entrypoints.md @@ -1,5 +1,5 @@ --- -id: test.next.dynamic-entrypoints-reference +id: test.dynamic-entrypoints-reference title: dynamic_entrypoints hide_table_of_contents: true --- @@ -7,18 +7,18 @@ import Syntax from '@theme/Syntax'; import SyntaxTitle from '@theme/SyntaxTitle'; - val storage : 'p 's - 's2.('p, 's) module_contract -> + 's2.('p, 's) module_contract -> 's2 -> { - dynamic_entrypoints : dynamic_entrypoints; + dynamic_entrypoints : dynamic_entrypoints; storage : 's2 } + -let storage: <p, s, s2>(_: module_contract<p, s>, s: s2) => { dynamic_entrypoints: dynamic_entrypoints; storage: s2 } +storage: <p, s, s2>(_: module_contract<p, s>, storage: s2) => { dynamic_entrypoints: dynamic_entrypoints; storage: s2 } diff --git a/gitlab-pages/docs/reference/Test.Next.IO.md b/gitlab-pages/docs/reference/test.io.md similarity index 66% rename from gitlab-pages/docs/reference/Test.Next.IO.md rename to gitlab-pages/docs/reference/test.io.md index c2d897f5e3..5854749a1a 100644 --- a/gitlab-pages/docs/reference/Test.Next.IO.md +++ b/gitlab-pages/docs/reference/test.io.md @@ -1,55 +1,55 @@ --- -id: test.next.io-reference +id: test.io-reference title: io hide_table_of_contents: true --- import Syntax from '@theme/Syntax'; import SyntaxTitle from '@theme/SyntaxTitle'; - - val print : string -> unit -let print: (_: string) => unit +print: (_: string) => unit -Prints an string to stdout. +Prints an string to stdout. val println : string -> unit -let println: (_: string) => unit +println: (_: string) => unit -Prints an string to stdout, ended with a newline. +Prints a string to stdout, ended with a newline. val eprint : string -> unit -let eprint: (_: string) => unit +eprint: (_: string) => unit -Prints an string to stderr. +Prints a string to stderr. val eprintln : string -> unit -let eprintln: (_: string) => unit +eprintln: (_: string) => unit -Prints an string to stderr, ended with a newline. + +Prints a string followed by a newline character to stderr. val log : 'a.'a -> unit -let log: <a>(_: a) => unit +log: <a>(_: a) => unit + Logs a value. @@ -57,17 +57,18 @@ Logs a value. val set_test_print : unit -> unit -let set_test_print: (_: unit) => unit +set_test_print: (_: unit) => unit + Turns on the printing of `test` prefixed values at the end of - tests. This is the default behaviour. +tests. This is the default behaviour. val unset_test_print : unit -> unit -let unset_test_print: (_: unit) => unit +unset_test_print: (_: unit) => unit -Turns off the printing of `test` prefixed values at the end of - tests. + +Turns off the printing of `test` prefixed values at the end of tests. diff --git a/gitlab-pages/docs/reference/test.md b/gitlab-pages/docs/reference/test.md index 2441b190ac..c7d14ae30e 100644 --- a/gitlab-pages/docs/reference/test.md +++ b/gitlab-pages/docs/reference/test.md @@ -10,1325 +10,54 @@ import SyntaxTitle from '@theme/SyntaxTitle'; The testing framework -[module pbt](test.pbt.md) - - -[module proxy_ticket](test.proxy_ticket.md) - - -[module next](test.next.md) - - - -val run : 'a 'b.('a -> 'b) -> 'a -> michelson_program - - -let run: <a, b>(_: (_: a) => b) => (_: a) => michelson_program - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.run` from `Test.Next` is encouraged for a smoother migration. - -Run a function on an input, all in Michelson. More concretely: - a) compiles the function argument to Michelson `f_mich`; b) - compiles the value argument (which was evaluated already) to - Michelson `v_mich`; c) runs the Michelson interpreter on the code - `f_mich` with starting stack `[v_mich]`. - - - -val eval : 'a.'a -> michelson_program - - -let eval: <a>(_: a) => michelson_program - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.eval` from `Test.Next` is encouraged for a smoother migration. - -Compile a LIGO value to Michelson. Currently it is a renaming of - `compile_value`. - - - -val decompile : 'a.michelson_program -> 'a - - -let decompile: <a>(_: michelson_program) => a - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.decompile` from `Test.Next` is encouraged for a smoother migration. - -Decompile a Michelson value to LIGO, following the (mandatory) - type annotation. Note: This operation can fail at run-time, in case - that the `michelson_program` given cannot be decompiled to something - compatible with the annotated type. - - - -val compile_value : 'a.'a -> michelson_program - - -let compile_value: <a>(_: a) => michelson_program - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.eval` from `Test.Next` is encouraged for a smoother migration. - -Compile a LIGO value to Michelson. - - - -val get_total_voting_power : unit -> nat - - -let get_total_voting_power: (_: unit) => nat - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.get_total_voting_power` from `Test.Next` is encouraged for a smoother migration. - -Returns the total voting power of all contracts. The total - voting power coincides with the sum of the rolls count of every - contract in the voting listings. The voting listings is calculated - at the beginning of every voting period. - - - -val failwith : 'a 'b.'a -> 'b - - -let failwith: <a, b>(_: a) => b - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Assert.failwith` from `Test.Next` is encouraged for a smoother migration. - -Cause the testing framework to fail. - - - -val to_contract : 'p 's.('p, 's) typed_address -> 'p contract - - -let to_contract: <p, s>(_: typed_address<p, s>) => contract<p> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Typed_address.to_contract` from `Test.Next` is encouraged for a smoother migration. - -Gets the contract corresponding to the default entrypoint of a - typed address: the contract parameter in the result will be the - type of the default entrypoint (generally `'param`, but this might - differ if `'param` includes a "default" entrypoint). - - - -val set_source : address -> unit - - -let set_source: (_: address) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.set_source` from `Test.Next` is encouraged for a smoother migration. - -Sets the source for `Test.transfer` and `Test.originate`. - - - -val cast_address : 'a 'b.address -> ('a, 'b) typed_address - - -let cast_address: <a, b>(_: address) => typed_address<a, b> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Address.to_typed_address` from `Test.Next` is encouraged for a smoother migration. - -This function casts an address to a typed address. You will need - to annotate the result with the type you expect. - - - -val to_address : 'a 'b.('a, 'b) typed_address -> address - - -let to_address: <a, b>(_: typed_address<a, b>) => address - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Typed_address.to_address` from `Test.Next` is encouraged for a smoother migration. - - - -val get_storage : 'p 's.('p, 's) typed_address -> 's - - -let get_storage: <p, s>(_: typed_address<p, s>) => s - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Typed_address.get_storage` from `Test.Next` is encouraged for a smoother migration. - -Gets the storage of a typed account. - - - -val get_storage_of_address : 'b.address -> 'b - - -let get_storage_of_address: <b>(_: address) => b - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Address.get_storage` from `Test.Next` is encouraged for a smoother migration. - -Gets the storage of an account in `michelson_program`. - - - -val get_balance_of_address : address -> tez - - -let get_balance_of_address: (_: address) => tez - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Address.get_balance` from `Test.Next` is encouraged for a smoother migration. - -Gets the balance of an account (given as an address) in tez. - +[module michelson](test.michelson.md) - -val get_balance : 'p 's.('p, 's) typed_address -> tez - - -let get_balance: <p, s>(_: typed_address<p, s>) => tez - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Typed_address.get_balance` from `Test.Next` is encouraged for a smoother migration. - -Gets the balance of an account in tez. - - - -val print : string -> unit - - -let print: (_: string) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `IO.print` from `Test.Next` is encouraged for a smoother migration. - -Prints an string to stdout. - - - -val eprint : string -> unit - - -let eprint: (_: string) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `IO.eprint` from `Test.Next` is encouraged for a smoother migration. - -Prints an string to stderr. - - - -val get_voting_power : key_hash -> nat - - -let get_voting_power: (_: key_hash) => nat - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.get_voting_power` from `Test.Next` is encouraged for a smoother migration. - -Return the voting power of a given contract. This voting power - coincides with the weight of the contract in the voting listings - (i.e., the rolls count) which is calculated at the beginning of - every voting period. +[module originate](test.originate.md) +[module mutation](test.mutation.md) - -val nth_bootstrap_contract : nat -> address - - -let nth_bootstrap_contract: (_: nat) => address - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Account.Contract.bootstrap` from `Test.Next` is encouraged for a smoother migration. - -Returns the address corresponding to the nth bootstrapped - contract. +[module pbt](test.pbt.md) +[module String](test.string.md) - -val nth_bootstrap_account : int -> address - - -let nth_bootstrap_account: (_: int) => address - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Account.address` from `Test.Next` is encouraged for a smoother migration. +[module IO](test.io.md) -Returns the address of the nth bootstrapped account. +[module Typed_address](test.typed_address.md) +[module State](test.state.md) - -val get_bootstrap_account : nat -> (address * key * string) - - -let get_bootstrap_account: (_: nat) => [address, key, string] - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Account.info` from `Test.Next` is encouraged for a smoother migration. +[module Account](test.account.md) -Returns the address, key and secret key of the nth bootstrapped - account. +[module Compare](test.compare.md) +[module Assert](test.assert.md) - -val nth_bootstrap_typed_address : 'a 'b.nat -> ('a, 'b) typed_address - - -let nth_bootstrap_typed_address: <a, b>(_: nat) => typed_address<a, b> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Account.Contract.bootstrap_typed_address` from `Test.Next` is encouraged for a smoother migration. +[module Contract](test.contract.md) -Returns the typed address corresponding to the nth bootstrapped - contract currently loaded. The types are inferred from those - contracts loaded with `Test.bootstrap_contract` (before reset). +[module Address](test.address.md) +[module Ticket](test.ticket.md) - -val last_originations : unit -> (address, address list) map - - -let last_originations: (_: unit) => map<address, list<address>> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.last_originations` from `Test.Next` is encouraged for a smoother migration. +[module Timelock](test.timelock.md) -Returns addresses of orginated accounts in the last transfer. It - is given in the form of a map binding the address of the source of - the origination operation to the addresses of newly originated - accounts. +[module Crypto](test.crypto.md) +[module Dynamic_entrypoints](test.dynamic_entrypoints.md) val random : 'a.unit -> 'a -let random: <a>(_: unit) => a - -This function creates a random value for a chosen type. - - - -val new_account : unit -> (string * key) - - -let new_account: (_: unit) => [string, key] - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Account.new` from `Test.Next` is encouraged for a smoother migration. - -Creates and returns secret key & public key of a new account. - - - -val bake_until_n_cycle_end : nat -> unit - - -let bake_until_n_cycle_end: (_: nat) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.bake_until` from `Test.Next` is encouraged for a smoother migration. - -It bakes until a number of cycles pass, so that an account - registered as delegate can effectively act as a baker. Note: It - can be used in tests to manually advance time. - - - -val get_time : unit -> timestamp - - -let get_time: (_: unit) => timestamp - - - -val register_delegate : key_hash -> unit - - -let register_delegate: (_: key_hash) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.register_delegate` from `Test.Next` is encouraged for a smoother migration. - -Registers a `key_hash` corresponding to an account as a delegate. - - - -val stake : key_hash -> tez -> unit - - -let stake: (_: key_hash) => (_: tez) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.stake` from `Test.Next` is encouraged for a smoother migration. - - - -val register_constant : michelson_program -> string - - -let register_constant: (_: michelson_program) => string - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.register_constant` from `Test.Next` is encouraged for a smoother migration. - -Registers a global constant, returns its hash as a string. See - the documentation for global constants for an example of usage. - - - -val to_typed_address : 'a 'b.'a contract -> ('a, 'b) typed_address - - -let to_typed_address: <a, b>(_: contract<a>) => typed_address<a, b> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Contract.to_typed_address` from `Test.Next` is encouraged for a smoother migration. - - - -val constant_to_michelson_program : string -> michelson_program - - -let constant_to_michelson_program: (_: string) => michelson_program - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.parse` from `Test.Next` is encouraged for a smoother migration. - -Turn a constant (as a string) into a `michelson_program`. To be - used together with `Test.register_constant`. - - - -val parse_michelson : string -> michelson_program - - -let parse_michelson: (_: string) => michelson_program - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.parse` from `Test.Next` is encouraged for a smoother migration. - -Parses Michelson (as string) into a `michelson_program`. - - - -val restore_context : unit -> unit - - -let restore_context: (_: unit) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.restore` from `Test.Next` is encouraged for a smoother migration. - -Pops a testing framework context from the stack of contexts, and - sets it up as the new current context. In case the stack was - empty, the current context is kept. - - - -val save_context : unit -> unit - - -let save_context: (_: unit) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.save` from `Test.Next` is encouraged for a smoother migration. - -Takes current testing framework context and saves it, pushing it - into a stack of contexts. - - - -val drop_context : unit -> unit - - -let drop_context: (_: unit) => unit +random: <a>(_: unit) => a -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.drop` from `Test.Next` is encouraged for a smoother migration. - -Drops a testing framework context from the stack of contexts. In - case the stack was empty, nothing is done. - - - -val to_string : 'a.'a -> string - - -let to_string: <a>(_: a) => string - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `String.show` from `Test.Next` is encouraged for a smoother migration. - -Converts a value to a string (same conversion as used by - `Test.log`). - - - -val to_json : 'a.'a -> string - - -let to_json: <a>(_: a) => string - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `String.json` from `Test.Next` is encouraged for a smoother migration. - -Converts a value to its JSON representation (as a string). - - - -val to_debugger_json : 'a.'a -> string - - -let to_debugger_json: <a>(_: a) => string - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `String.debugger_json` from `Test.Next` is encouraged for a smoother migration. - - - -val set_baker_policy : test_baker_policy -> unit - - -let set_baker_policy: (_: test_baker_policy) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.set_baker_policy` from `Test.Next` is encouraged for a smoother migration. - -Forces the baking policy for `Test.transfer` and - `Test.originate`. By default, the first bootstrapped account. +This function creates a random value for a chosen type. -val set_baker : address -> unit +val get_time : 'a.unit -> 'a -let set_baker: (_: address) => unit +get_time: <a>(_: unit) => a -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.set_baker` from `Test.Next` is encouraged for a smoother migration. - -Forces the baker for `Test.transfer` and `Test.originate`, - implemented using `Test.set_baker_policy` with `By_account`. By - default, the first bootstrapped account. - - -val size : 'p 's.('p, 's) michelson_contract -> int - - -let size: <p, s>(_: michelson_contract<p, s>) => int - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.Contract.size` from `Test.Next` is encouraged for a smoother migration. - -Measures the size of a contract. - - - -val compile_contract : 'p 's.(('p * 's) -> (operation list * 's)) -> ('p, 's) michelson_contract - - -let compile_contract: <p, s>(_: (_: [p, s]) => [list<operation>, s]) => michelson_contract<p, s> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.Contract.compile` from `Test.Next` is encouraged for a smoother migration. - -Compiles a contract from an entrypoint function. - - - -val read_contract_from_file : 'p 's.string -> ('p, 's) michelson_contract - - -let read_contract_from_file: <p, s>(_: string) => michelson_contract<p, s> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.Contract.from_file` from `Test.Next` is encouraged for a smoother migration. - -Reads a contract from a `.tz` file. - - - -val chr : nat -> string option - - -let chr: (_: nat) => option<string> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `String.chr` from `Test.Next` is encouraged for a smoother migration. - -String consisting of the character represented by a `nat` in the - interval `[0, 255]`. - - - -val nl : string - - -let nl: string - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `String.nl` from `Test.Next` is encouraged for a smoother migration. - -String consisting of only a newline. - - - -val println : string -> unit - - -let println: (_: string) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `IO.println` from `Test.Next` is encouraged for a smoother migration. - -Prints an string to stdout, ended with a newline. - - - -val set_print_values : unit -> unit - - -let set_print_values: (_: unit) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `IO.set_test_print` from `Test.Next` is encouraged for a smoother migration. - -Turns on the printing of `test` prefixed values at the end of - tests. This is the default behaviour. - - - -val unset_print_values : unit -> unit - - -let unset_print_values: (_: unit) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `IO.unset_test_print` from `Test.Next` is encouraged for a smoother migration. - -Turns off the printing of `test` prefixed values at the end of - tests. - - - -val get_last_events_from : 'a 'p 's.('p, 's) typed_address -> string -> 'a list - - -let get_last_events_from: <a, p, s>(_: typed_address<p, s>) => (_: string) => list<a> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.last_events` from `Test.Next` is encouraged for a smoother migration. - -Returns the list of all the event payloads emited with a given - tag by a given address. Any call to this function must be - annotated with the expected payload type. - - - -val transfer : 'p 's.('p, 's) typed_address -> 'p -> tez -> test_exec_result - - -let transfer: <p, s>(_: typed_address<p, s>) => (_: p) => (_: tez) => test_exec_result - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Typed_address.transfer` from `Test.Next` is encouraged for a smoother migration. - -Bakes a transaction by sending an amount of tez with a parameter - from the current source to another account. Returns the amount of - gas consumed by the execution of the contract. - - - -val transfer_exn : 'p 's.('p, 's) typed_address -> 'p -> tez -> nat - - -let transfer_exn: <p, s>(_: typed_address<p, s>) => (_: p) => (_: tez) => nat - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Typed_address.transfer_exn` from `Test.Next` is encouraged for a smoother migration. - -Bakes a transaction by sending an amount of tez with a parameter - from the current source to another account. Returns the amount of - gas consumed by the execution of the contract. Similar as - `Test.transfer`, but fails when anything goes wrong. - - - -val log : 'a.'a -> unit - - -let log: <a>(_: a) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `IO.log` from `Test.Next` is encouraged for a smoother migration. - -Logs a value. - - - -val reset_state : nat -> tez list -> unit - - -let reset_state: (_: nat) => (_: list<tez>) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.reset` from `Test.Next` is encouraged for a smoother migration. - -Generates a number of random bootstrapped accounts with a - default amount of `4000000` tez. The passed list can be used to - overwrite the amount. By default, the state only has two - bootstrapped accounts. Notice that since Ithaca, a percentage of - an account's balance is frozen (5% in testing mode) in case the - account can be taken to be a validator, and thus - `Test.get_balance` can show a different amount to the one being - set with `Test.reset_state`. - - - -val reset_state_at : timestamp -> nat -> tez list -> unit - - -let reset_state_at: (_: timestamp) => (_: nat) => (_: list<tez>) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.reset_at` from `Test.Next` is encouraged for a smoother migration. - -Generates a number of random bootstrapped accounts with a - default amount of `4000000` tez. The passed list can be used to - overwrite the amount. By default, the state only has two - bootstrapped accounts. Notice that since Ithaca, a percentage of - an account's balance is frozen (5% in testing mode) in case the - account can be taken to be a validator, and thus - `Test.get_balance` can show a different amount to the one being - set with `Test.reset_state`. It also takes a starting timestamp - for the genesis block. - - - -val bootstrap_contract : 'p 's.(('p * 's) -> (operation list * 's)) -> 's -> tez -> unit - - -let bootstrap_contract: <p, s>(_: (_: [p, s]) => [list<operation>, s]) => (_: s) => (_: tez) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.Reset.add_func_contract` from `Test.Next` is encouraged for a smoother migration. - -Setup a bootstrap contract with an entrypoint function, initial - storage and initial balance. Bootstrap contracts will be loaded in - order, and they will be available only after reset. - - - -val mutate_value : 'a.nat -> 'a -> ('a * mutation) option - - -let mutate_value: <a>(_: nat) => (_: a) => option<[a, mutation]> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.value` from `Test.Next` is encouraged for a smoother migration. - -Mutates a value using a natural number as an index for the - available mutations, returns an option for indicating whether - mutation was successful or not. - - - -val save_mutation : string -> mutation -> string option - - -let save_mutation: (_: string) => (_: mutation) => option<string> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.save` from `Test.Next` is encouraged for a smoother migration. - -This function reconstructs a file from a mutation (second - argument), and saves it to a file in the directory path (first - argument). It returns an optional string indicating the filename - where the mutation was saved, or `None` if there was an error. - - - -val sign : string -> bytes -> signature - - -let sign: (_: string) => (_: bytes) => signature - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Crypto.sign` from `Test.Next` is encouraged for a smoother migration. - -Creates a signature of bytes from a string representing a secret - key, it can be checked with `Crypto.check`. - - - -val add_account : string -> key -> unit - - -let add_account: (_: string) => (_: key) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Account.add` from `Test.Next` is encouraged for a smoother migration. - -Adds an account specfied by secret key & public key to the test - context. - - - -val baker_account : (string * key) -> tez option -> unit - - -let baker_account: (_: [string, key]) => (_: option<tez>) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.Reset.add_baker` from `Test.Next` is encouraged for a smoother migration. - -Adds an account `(sk, pk)` as a baker. The change is only - effective after `Test.reset_state`. - - - -val set_big_map : 'a 'b.int -> ('a, 'b) big_map -> unit - - -let set_big_map: <a, b>(_: int) => (_: big_map<a, b>) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `State.set_big_map` from `Test.Next` is encouraged for a smoother migration. - -The testing framework keeps an internal reference to the values - corresponding to big map identifiers. This function allows to - override the value of a particular big map identifier. It should - not be normally needed, except in particular circumstances such as - using custom bootstrap contracts that initialize big maps. - - - -val transfer_to_contract : 'p.'p contract -> 'p -> tez -> test_exec_result - - -let transfer_to_contract: <p>(_: contract<p>) => (_: p) => (_: tez) => test_exec_result - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Contract.transfer` from `Test.Next` is encouraged for a smoother migration. - -Bake a transaction by sending an amount of tez with a parameter - from the current source to a contract. Returns the amount of gas - consumed by the execution of the contract. - - - -val transfer_to_contract_exn : 'p.'p contract -> 'p -> tez -> nat - - -let transfer_to_contract_exn: <p>(_: contract<p>) => (_: p) => (_: tez) => nat - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Contract.transfer_exn` from `Test.Next` is encouraged for a smoother migration. - -Bakes a transaction by sending an amount of tez with a parameter - from the current source to a contract. Returns the amount of gas - consumed by the execution of the contract. Similar as - `Test.transfer_to_contract`, but fails when anything goes - wrong. - - - -val michelson_equal : michelson_program -> michelson_program -> bool - - -let michelson_equal: (_: michelson_program) => (_: michelson_program) => bool - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Compare.eq` from `Test.Next` is encouraged for a smoother migration. - -Compares two Michelson values. - - - -val to_entrypoint : 'a 'b 'c.string -> ('a, 'b) typed_address -> 'c contract - - -let to_entrypoint: <a, b, c>(_: string) => (_: typed_address<a, b>) => contract<c> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Typed_address.get_entrypoint` from `Test.Next` is encouraged for a smoother migration. - -Gets the contract corresponding to an entrypoint of a typed - address: the contract parameter in the result will be the type of - the entrypoint, it needs to be annotated, entrypoint string should - omit the prefix "%", but if passed a string starting with "%", it - will be removed (and a warning emitted). - - - -val storage_with_dynamic_entrypoints : - 'p - 's - 's2.('p, 's) module_contract -> - 's2 -> - { - dynamic_entrypoints : dynamic_entrypoints; - storage : 's2 - } - - -let storage_with_dynamic_entrypoints: - <p, s, s2>(_: module_contract<p, s>, s: s2) => { dynamic_entrypoints: dynamic_entrypoints; storage: s2 } - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Dynamic_entrypoints.storage` from `Test.Next` is encouraged for a smoother migration. - - - -val originate_contract : 'p 's.('p, 's) michelson_contract -> 's -> tez -> ('p, 's) typed_address - - -let originate_contract: <p, s>(_: michelson_contract<p, s>) => (_: s) => (_: tez) => typed_address<p, s> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Originate.michelson` from `Test.Next` is encouraged for a smoother migration. - -Originate a contract with initial storage and initial - balance. - - - -val compile_contract_with_views : 'p 's.(('p * 's) -> (operation list * 's)) -> 's views -> ('p, 's) michelson_contract - - -let compile_contract_with_views: - <p, s>(_: (_: [p, s]) => [list<operation>, s]) => (_: views<s>) => michelson_contract<p, s> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.Contract.compile_with_views` from `Test.Next` is encouraged for a smoother migration. - - - -val originate : 'p 's.('p, 's) module_contract -> 's -> tez -> ('p, 's) origination_result - - -let originate: <p, s>(_: module_contract<p, s>) => (_: s) => (_: tez) => origination_result<p, s> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Originate.contract` from `Test.Next` is encouraged for a smoother migration. - -Originate a contract with an entrypoint function in curried - form, initial storage and initial balance. - - - -val compile_contract_from_file : 'p 's.string -> ('p, 's) michelson_contract - - -let compile_contract_from_file: <p, s>(_: string) => michelson_contract<p, s> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Michelson.Contract.from_file` from `Test.Next` is encouraged for a smoother migration. - -Compiles a contract with a path to the contract file, an - entrypoint, and a list of views. - - - -val originate_from_file : 'p 's.string -> 's -> tez -> ('p, 's) origination_result - - -let originate_from_file: <p, s>(_: string) => (_: s) => (_: tez) => origination_result<p, s> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Originate.from_file` from `Test.Next` is encouraged for a smoother migration. - -Originate a contract with a path to the contract file, an - entrypoint, and a list of views, together with an initial storage - and an initial balance. - - - -val mutation_test : 'a 'b.'a -> ('a -> 'b) -> ('b * mutation) option - - -let mutation_test: <a, b>(_: a) => (_: (_: a) => b) => option<[b, mutation]> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.func` from `Test.Next` is encouraged for a smoother migration. - -Given a value to mutate (first argument), it will try all the - mutations available of it, passing each one to the function - (second argument). On the first case of non failure when running - the function on a mutation, the value and mutation involved will - be returned. - - - -val mutation_test_all : 'a 'b.'a -> ('a -> 'b) -> ('b * mutation) list - - -let mutation_test_all: <a, b>(_: a) => (_: (_: a) => b) => list<[b, mutation]> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.All.func` from `Test.Next` is encouraged for a smoother migration. - -Given a value to mutate (first argument), it will try all the - mutations of it, passing each one to the function (second - argument). In case no failure arises when running the function on - a mutation, the failure and mutation involved will be added to the - list to be returned. - - - -val originate_from_file_and_mutate : - 'b - 'p - 's.string -> - 's -> tez -> ((('p, 's) typed_address * ('p, 's) michelson_contract * int) -> 'b) -> ('b * mutation) option - - -let originate_from_file_and_mutate: - <b, p, s>(_: string) => (_: s) => (_: tez) => (_: (_: [typed_address<p, s>, michelson_contract<p, s>, int]) => b) => option< - [b, mutation] - > - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.from_file` from `Test.Next` is encouraged for a smoother migration. - -Given a contract from a file (passed by filepath, entrypoint and - views), an initial storage and balance, it will originate mutants - of the contract and pass the result to the function (last - argument). On the first case of non failure when running the - function on a mutation, the value and mutation involved will be - returned. - - - -val originate_from_file_and_mutate_all : - 'b - 'p - 's.string -> 's -> tez -> ((('p, 's) typed_address * ('p, 's) michelson_contract * int) -> 'b) -> ('b * mutation) list - - -let originate_from_file_and_mutate_all: - <b, p, s>(_: string) => (_: s) => (_: tez) => (_: (_: [typed_address<p, s>, michelson_contract<p, s>, int]) => b) => list< - [b, mutation] - > - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.All.from_file` from `Test.Next` is encouraged for a smoother migration. - -Given a contract from a file (passed by filepath, entrypoint and - views), an initial storage and balance, it will originate mutants - of the contract and pass the result to the function (last - argument). In case no failure arises when running the function on - a mutation, the failure and mutation involved will be added to the - list to be returned. - - - -val originate_module_and_mutate : - 'p - 's - 'b.('p, 's) module_contract -> - 's -> tez -> (('p, 's) typed_address -> ('p, 's) michelson_contract -> int -> 'b) -> ('b * mutation) option - - -let originate_module_and_mutate: - <p, s, b>(_: module_contract<p, s>) => (_: s) => (_: tez) => ( - _: (_: typed_address<p, s>) => (_: michelson_contract<p, s>) => (_: int) => b - ) => option<[b, mutation]> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.contract` from `Test.Next` is encouraged for a smoother migration. - -Given a contract as a module/namespace, an initial storage and - balance, it will originate mutants of the contract and pass the - result to the function (last argument). On the first case of non - failure when running the function on a mutation, the value and - mutation involved will be returned. - - - -val originate_and_mutate_all : - 'p - 's - 'b.('p, 's) module_contract -> - 's -> tez -> (('p, 's) typed_address -> ('p, 's) michelson_contract -> int -> 'b) -> ('b * mutation) list - - -let originate_and_mutate_all: - <p, s, b>(_: module_contract<p, s>) => (_: s) => (_: tez) => ( - _: (_: typed_address<p, s>) => (_: michelson_contract<p, s>) => (_: int) => b - ) => list<[b, mutation]> - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Mutation.All.contract` from `Test.Next` is encouraged for a smoother migration. - -Given a contract as a module/namespace, an initial storage and - balance, it will originate mutants of the contract and pass the - result to the function (last argument). In case no failure arises - when running the function on a mutation, the failure and mutation - involved will be added to the list to be returned. - - - -val assert : bool -> unit - - -let assert: (_: bool) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Assert.assert` from `Test.Next` is encouraged for a smoother migration. - - - -The call `assert cond` terminates the execution with the string - `"failed assertion"` if, and only if, the boolean condition `cond` - is false. The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -The call `assert(cond)` terminates the execution with the string - `"failed assertion"` if, and only if, the boolean condition `cond` - is false. The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -val assert_some : 'a.'a option -> unit - - -let assert_some: <a>(_: option<a>) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Assert.some` from `Test.Next` is encouraged for a smoother migration. - - - -The call `assert_some opt` terminates the execution with the - string `"failed assert some"` if, and only if, `opt` is `None`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -The call `assert_some(opt)` terminates the execution with the - string `"failed assert some"` if, and only if, `opt` is `None()`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -val assert_none : 'a.'a option -> unit - - -let assert_none: <a>(_: option<a>) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Assert.none` from `Test.Next` is encouraged for a smoother migration. - - - -The call `assert_none opt` terminates the execution with the string - `"failed assert none"` if, and only if, `opt` is not `None`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -The call `assert_none(opt)` terminates the execution with the string - `"failed assert none"` if, and only if, `opt` is not `None()`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -val assert_with_error : bool -> string -> unit - - -let assert_with_error: (b: bool, s: string) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Assert.Error.assert` from `Test.Next` is encouraged for a smoother migration. - - - -The call `assert_with_error cond error` terminates the execution - with the string `error` (that is, an error message) if, and only - if, the boolean condition `cond` is false. The failure is handled - by LIGO's testing framework and not by Michelson's interpreter. - - - - - -The call `assert_with_error(cond, error)` terminates the execution - with the string `error` (that is, an error message) if, and only - if, the boolean condition `cond` is false. The failure is handled - by LIGO's testing framework and not by Michelson's interpreter. - - - - - -val assert_some_with_error : 'a.'a option -> string -> unit - - -let assert_some_with_error: <a>(_: option<a>) => (_: string) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Assert.Error.some` from `Test.Next` is encouraged for a smoother migration. - - - -The call `assert_some_with_error opt err` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is `None`. The failure is handled by LIGO's testing - framework and not by Michelson's interpreter. - - - - - -The call `assert_some_with_error(opt, err)` terminates the - execution with the string `err` (that is, an error message) if, - and only if, `opt` is `None()`. The failure is handled by LIGO's - testing framework and not by Michelson's interpreter. - - - - - -val assert_none_with_error : 'a.'a option -> string -> unit - - -let assert_none_with_error: <a>(_: option<a>) => (_: string) => unit - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Assert.Error.none` from `Test.Next` is encouraged for a smoother migration. - - - -The call `assert_none_with_error opt err` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is an optional value different from `None`. The failure is - handled by LIGO's testing framework and not by Michelson's - interpreter. - - - - - -The call `assert_none_with_error(opt, err)` terminates the - execution with the string `err` (that is, an error message) if, - and only if, `opt` is an optional value different from - `None()`. The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -val equal : 'a.'a -> 'a -> bool - - -let equal: <a>(_: a) => (_: a) => bool - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Compare.eq` from `Test.Next` is encouraged for a smoother migration. - - - -The call `equal x y` returns `true` if, and only if, `x` and `y` - are considered to be equal w.r.t. the order on the underlying - type. - - - - - -The call `equal(x, y)` returns `true` if, and only if, `x` and `y` - are considered to be equal w.r.t. the order on the underlying - type. - - - - - -val not_equal : 'a.'a -> 'a -> bool - - -let not_equal: <a>(_: a) => (_: a) => bool - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Compare.neq` from `Test.Next` is encouraged for a smoother migration. - - - -The call `not_equal x y` returns `true` if, and only if, `x` and - `y` are not considered to be equal w.r.t. the order on the - underlying type. - - - - - -The call `not_equal(x, y)` returns `true` if, and only if, `x` and - `y` are not considered to be equal w.r.t. the order on the - underlying type. - - - - - -val greater : 'a.'a -> 'a -> bool - - -let greater: <a>(_: a) => (_: a) => bool - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Compare.gt` from `Test.Next` is encouraged for a smoother migration. - - - -The call `greater x y` returns `true` if, and only if, `x` is - considered to be greater than `y` w.r.t. the order on the - underlying type. - - - - - -The call `greater(x, y)` returns `true` if, and only if, `x` is - considered to be greater than `y` w.r.t. the order on the - underlying type. - - - - - -val less : 'a.'a -> 'a -> bool - - -let less: <a>(_: a) => (_: a) => bool - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Compare.lt` from `Test.Next` is encouraged for a smoother migration. - - - -The call `less x y` returns `true` if, and only if, `x` is - considered to be less than `y` w.r.t. the order on the underlying - type. - - - - - -The call `less(x, y)` returns `true` if, and only if, `x` is - considered to be less than `y` w.r.t. the order on the underlying - type. - - - - - -val greater_or_equal : 'a.'a -> 'a -> bool - - -let greater_or_equal: <a>(_: a) => (_: a) => bool - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Compare.ge` from `Test.Next` is encouraged for a smoother migration. - - - -The call `greater_or_equal x y` returns `true` if, and only if, - `x` is considered to be greater or equal than `y` w.r.t. the order - on the underlying type. - - - - - -The call `greater_or_equal(x, y)` returns `true` if, and only if, - `x` is considered to be greater or equal than `y` w.r.t. the order - on the underlying type. - - - - - -val less_or_equal : 'a.'a -> 'a -> bool - - -let less_or_equal: <a>(_: a) => (_: a) => bool - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Compare.le` from `Test.Next` is encouraged for a smoother migration. - - - -The call `less_or_equal x y` returns `true` if, and only if, `x` - is considered to be less or equal than `y` w.r.t. the order on the - underlying type. - - - - - -The call `less_or_equal(x, y)` returns `true` if, and only if, `x` - is considered to be less or equal than `y` w.r.t. the order on the - underlying type. - - - - - -val create_chest : bytes -> nat -> (chest * chest_key) - - -let create_chest: (_: bytes) => (_: nat) => [chest, chest_key] - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Timelock.create` from `Test.Next` is encouraged for a smoother migration. - - - -val create_chest_key : chest -> nat -> chest_key - - -let create_chest_key: (_: chest) => (_: nat) => chest_key - -**Deprecated:** In a future version, `Test` will be replaced by `Test.Next`, and using `Timelock.create_key` from `Test.Next` is encouraged for a smoother migration. +Alias of `Tezos.get_now`. diff --git a/gitlab-pages/docs/reference/test.michelson.contract.md b/gitlab-pages/docs/reference/test.michelson.contract.md new file mode 100644 index 0000000000..b58799d53d --- /dev/null +++ b/gitlab-pages/docs/reference/test.michelson.contract.md @@ -0,0 +1,55 @@ +--- +id: test.michelson.contract-reference +title: contract +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + +val compile : 'p 's.(('p * 's) -> (operation list * 's)) -> ('p, 's) michelson_contract + + +compile : <p, s>(_: (_: [p, s]) => [list<operation>, s]) => michelson_contract<p, s> + + +Compiles a contract from an entrypoint function. + + +val compile_with_views : 'p 's.(('p * 's) -> (operation list * 's)) -> 's views -> ('p, 's) michelson_contract + + +compile_with_views: + <p, s>(f: (_: [p, s]) => [list<operation>, s], v: views<s>) => michelson_contract<p, s> + + +Compiles a contract with given views. + + +val size : 'p 's.('p, 's) michelson_contract -> int + + +size: <p, s>(_: michelson_contract<p, s>) => int + + +Measures the size of a contract. + + + +val from_file : 'p 's.string -> ('p, 's) michelson_contract + + +from_file: <p, s>(michelson_file: string) => michelson_contract<p, s> + + +Reads a contract from a `.tz` file. + + +val compile_from_file : 'p 's.string -> ('p, 's) michelson_contract + + +compile_from_file: <p, s>(contract_file: string) => michelson_contract<p, s> + + +Compiles a contract with a path to the contract file, an entrypoint, +and a list of views. diff --git a/gitlab-pages/docs/reference/test.michelson.md b/gitlab-pages/docs/reference/test.michelson.md new file mode 100644 index 0000000000..dea2a68352 --- /dev/null +++ b/gitlab-pages/docs/reference/test.michelson.md @@ -0,0 +1,55 @@ +--- +id: test.michelson-reference +title: michelson +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + +[module contract](test.michelson.contract.md) + + +val run : 'a 'b.('a -> 'b) -> 'a -> michelson_program + + +run: <a, b>(_: (_: a) => b, _: a) => michelson_program + + +Run a function on an input, all in Michelson. More concretely: a) +compiles the function argument to Michelson `f_mich`; b) compiles the +value argument (which was evaluated already) to Michelson `v_mich`; c) +runs the Michelson interpreter on the code `f_mich` with starting +stack `[v_mich]`. + + + +val eval : 'a.'a -> michelson_program + + +eval: <a>(_: a) => michelson_program + + +Compile a LIGO value to Michelson. + + + +val decompile : 'a.michelson_program -> 'a + + +decompile: <a>(_: michelson_program) => a + + +Decompile a Michelson value to LIGO, following the (mandatory) type +annotation. Note: This operation can fail at run-time, in case that +the `michelson_program` given cannot be decompiled to something +compatible with the annotated type. + + + +val parse : string -> michelson_program + + +parse: (code: string) => michelson_program + + +Parses Michelson (as string) into a `michelson_program`. diff --git a/gitlab-pages/docs/reference/test.mutation.all.md b/gitlab-pages/docs/reference/test.mutation.all.md new file mode 100644 index 0000000000..566a9e9770 --- /dev/null +++ b/gitlab-pages/docs/reference/test.mutation.all.md @@ -0,0 +1,102 @@ +--- +id: test.mutation.all-reference +title: all +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + + +val func : 'a 'b.'a -> ('a -> 'b) -> ('b * mutation) list + + +func: <a, b>(_: a) => (_: (_: a) => b) => list<[b, mutation]> + + +Given a value to mutate (first argument), it will try all the +mutations of it, passing each one to the function (second +argument). In case no failure arises when running the function on a +mutation, the failure and mutation involved will be added to the list +to be returned. + + + +val from_file : + 'b + 'p + 's.string -> 's -> tez -> ((('p, 's) typed_address * ('p, 's) michelson_contract * int) -> 'b) -> ('b * mutation) list + + +from_file: + <b, p, s>(_: string) => (_: s) => (_: tez) => (_: (_: [typed_address<p, s>, michelson_contract<p, s>, int]) => b) => list< + [b, mutation] + > + + +Given a contract from a file (passed by filepath, entrypoint and +views), an initial storage and balance, it will originate mutants of +the contract and pass the result to the function (last argument). In +case no failure arises when running the function on a mutation, the +failure and mutation involved will be added to the list to be +returned. + + + +val contract : + 'p + 's + 'b.('p, 's) module_contract -> + 's -> tez -> (('p, 's) typed_address -> ('p, 's) michelson_contract -> int -> 'b) -> ('b * mutation) list + + +contract: + <p, s, b>(_: module_contract<p, s>, storage: s, amount: + tez, _: (_: typed_address<p, s>, _: michelson_contract<p, + s>, _: int) => b) => list<[b, mutation]> + + + + +Given a contract as a module, an initial storage and balance, it will +originate mutants of the contract and pass the result to the function +(last argument). In case no failure arises when running the function +on a mutation, the failure and mutation involved will be added to the +list to be returned. + + + + + +Given a contract as a namespace, an initial storage and balance, it +will originate mutants of the contract and pass the result to the +function (last argument). In case no failure arises when running the +function on a mutation, the failure and mutation involved will be +added to the list to be returned. + + + + +val from_file : + 'p + 'b + 's.string + -> 's + -> tez + -> (('p,'s) typed_address * ('p,'s) michelson_contract * int -> 'b) + -> ('b * mutation) list + + + +from_file : + <p,b,s>(fn: string, s: s, t: tez, + tester: (_ : [typed_address<p,s>, michelson_contract<p,s>, int]) => b) + => list<[b, mutation]> + + +Given a contract from a file (passed by filepath, entrypoint and +views), an initial storage and balance, it will originate mutants of +the contract and pass the result to the function (last argument). In +case no failure arises when running the function on a mutation, the +failure and mutation involved will be added to the list to be +returned. diff --git a/gitlab-pages/docs/reference/test.mutation.md b/gitlab-pages/docs/reference/test.mutation.md new file mode 100644 index 0000000000..fee2961825 --- /dev/null +++ b/gitlab-pages/docs/reference/test.mutation.md @@ -0,0 +1,113 @@ +--- +id: test.mutation-reference +title: mutation +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + +[module all](test.mutation.all.md) + + + +val value : 'a.nat -> 'a -> ('a * mutation) option + + +value: <a>(n: nat, v: a) => option<[a, mutation]> + + +Mutates a value using a natural number as an index for the available +mutations, returns an option for indicating whether mutation was +successful or not. + + +val save : string -> mutation -> string option + + +save: (s: string, m: mutation) => option<string> + + + + +This function reconstructs a file from a mutation (second argument), +and saves it to a file in the directory path (first argument). It +returns an optional string indicating the filename where the mutation +was saved, or `None` if there was an error. + + + + + +This function reconstructs a file from a mutation (second argument), +and saves it to a file in the directory path (first argument). It +returns an optional string indicating the filename where the mutation +was saved, or `["None" as "None"]` if there was an error. + + + + +val func : 'a 'b.'a -> ('a -> 'b) -> ('b * mutation) option + + +func: <a, b>(v: a, tester: (_: a) => b) => option<[b, mutation]> + + +Given a value to mutate (first argument), it will try all the +mutations available of it, passing each one to the function (second +argument). On the first case of non failure when running the function +on a mutation, the value and mutation involved will be returned. + + +val from_file : + 'b + 'p + 's.string -> + 's -> tez -> ((('p, 's) typed_address * ('p, 's) michelson_contract * int) -> 'b) -> ('b * mutation) option + + +from_file: + <b, p, s>(fn: string, s: s, t: tez, tester: (_: [typed_address<p, s>, michelson_contract<p, s>, int]) => b) => option< + [b, mutation] + > + + +Given a contract from a file (passed by filepath, entrypoint and +views), an initial storage and balance, it will originate mutants of +the contract and pass the result to the function (last argument). On +the first case of non failure when running the function on a mutation, +the value and mutation involved will be returned. + + +val contract : + 'p + 's + 'b.('p, 's) module_contract -> + 's -> tez -> (('p, 's) typed_address -> ('p, 's) michelson_contract -> int -> 'b) -> ('b * mutation) option + + +contract: + <p, s, b>(_: module_contract<p, s>, s: s, t: + tez, tester: (_: typed_address<p, s>, _: + michelson_contract<p, s>, _: int) => b + ) => option<[b, mutation]> + + + + +Given a contract as a module, an initial storage and balance, it will +originate mutants of the contract and pass the result to the function +(last argument). On the first case of non failure when running the +function on a mutation, the value and mutation involved will be +returned. + + + + + +Given a contract as a namespace, an initial storage and balance, it +will originate mutants of the contract and pass the result to the +function (last argument). On the first case of non failure when +running the function on a mutation, the value and mutation involved +will be returned. + + diff --git a/gitlab-pages/docs/reference/test.next.address.md b/gitlab-pages/docs/reference/test.next.address.md deleted file mode 100644 index 009b1eccc0..0000000000 --- a/gitlab-pages/docs/reference/test.next.address.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -id: test.next.address-reference -title: address -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val get_balance : address -> tez - - -let get_balance: (_: address) => tez - - - -val to_typed_address : 'a 'b.address -> ('a, 'b) typed_address - - -let to_typed_address: <a, b>(_: address) => typed_address<a, b> - - - -val get_storage : 'b.address -> 'b - - -let get_storage: <b>(_: address) => b - diff --git a/gitlab-pages/docs/reference/test.next.assert.error.md b/gitlab-pages/docs/reference/test.next.assert.error.md deleted file mode 100644 index 0ec0c99e4f..0000000000 --- a/gitlab-pages/docs/reference/test.next.assert.error.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -id: test.next.assert.error-reference -title: error -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val assert : bool -> string -> unit - - -let assert: (b: bool, s: string) => unit - - - -The call `assert cond error` terminates the execution - with the string `error` (that is, an error message) if, and only - if, the boolean condition `cond` is false. The failure is handled - by LIGO's testing framework and not by Michelson's interpreter. - - - - - -The call `assert(cond, error)` terminates the execution - with the string `error` (that is, an error message) if, and only - if, the boolean condition `cond` is false. The failure is handled - by LIGO's testing framework and not by Michelson's interpreter. - - - - - -val some : 'a.'a option -> string -> unit - - -let some: <a>(_: option<a>) => (_: string) => unit - - - -The call `some opt err` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is `None`. The failure is handled by LIGO's testing - framework and not by Michelson's interpreter. - - - - - -The call `some(opt, err)` terminates the - execution with the string `err` (that is, an error message) if, - and only if, `opt` is `None()`. The failure is handled by LIGO's - testing framework and not by Michelson's interpreter. - - - - - -val none : 'a.'a option -> string -> unit - - -let none: <a>(_: option<a>) => (_: string) => unit - - - -The call `none opt err` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is an optional value different from `None`. The failure is - handled by LIGO's testing framework and not by Michelson's - interpreter. - - - - - -The call `none(opt, err)` terminates the - execution with the string `err` (that is, an error message) if, - and only if, `opt` is an optional value different from - `None()`. The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - diff --git a/gitlab-pages/docs/reference/test.next.assert.md b/gitlab-pages/docs/reference/test.next.assert.md deleted file mode 100644 index 3f31ac18a2..0000000000 --- a/gitlab-pages/docs/reference/test.next.assert.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -id: test.next.assert-reference -title: assert -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - -[module error](test.next.assert.error.md) - - - -val failwith : 'a 'b.'a -> 'b - - -let failwith: <a, b>(_: a) => b - -Cause the testing framework to fail. - - - -val assert : bool -> unit - - -let assert: (_: bool) => unit - - - -The call `assert cond` terminates the execution with the string - `"failed assertion"` if, and only if, the boolean condition `cond` - is false. The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -The call `assert(cond)` terminates the execution with the string - `"failed assertion"` if, and only if, the boolean condition `cond` - is false. The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -val some : 'a.'a option -> unit - - -let some: <a>(_: option<a>) => unit - - - -The call `some opt` terminates the execution with the - string `"failed assert some"` if, and only if, `opt` is `None`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -The call `some(opt)` terminates the execution with the - string `"failed assert some"` if, and only if, `opt` is `None()`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -val none : 'a.'a option -> unit - - -let none: <a>(_: option<a>) => unit - - - -The call `none opt` terminates the execution with the string - `"failed assert none"` if, and only if, `opt` is not `None`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - - - - -The call `none(opt)` terminates the execution with the string - `"failed assert none"` if, and only if, `opt` is not `None()`. - The failure is handled by LIGO's testing framework and - not by Michelson's interpreter. - - diff --git a/gitlab-pages/docs/reference/test.next.compare.md b/gitlab-pages/docs/reference/test.next.compare.md deleted file mode 100644 index 5ff8bf545c..0000000000 --- a/gitlab-pages/docs/reference/test.next.compare.md +++ /dev/null @@ -1,146 +0,0 @@ ---- -id: test.next.compare-reference -title: compare -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val eq : 'a.'a -> 'a -> bool - - -let eq: <a>(_: a) => (_: a) => bool - - - -The call `eq x y` returns `true` if, and only if, `x` and `y` - are considered to be equal w.r.t. the order on the underlying - type. - - - - - -The call `eq(x, y)` returns `true` if, and only if, `x` and `y` - are considered to be equal w.r.t. the order on the underlying - type. - - - - - -val neq : 'a.'a -> 'a -> bool - - -let neq: <a>(_: a) => (_: a) => bool - - - -The call `neq x y` returns `true` if, and only if, `x` and - `y` are not considered to be equal w.r.t. the order on the - underlying type. - - - - - -The call `neq(x, y)` returns `true` if, and only if, `x` and - `y` are not considered to be equal w.r.t. the order on the - underlying type. - - - - - -val gt : 'a.'a -> 'a -> bool - - -let gt: <a>(_: a) => (_: a) => bool - - - -The call `gt x y` returns `true` if, and only if, `x` is - considered to be greater than `y` w.r.t. the order on the - underlying type. - - - - - -The call `gt(x, y)` returns `true` if, and only if, `x` is - considered to be greater than `y` w.r.t. the order on the - underlying type. - - - - - -val lt : 'a.'a -> 'a -> bool - - -let lt: <a>(_: a) => (_: a) => bool - - - -The call `lt` returns `true` if, and only if, `x` is - considered to be less than `y` w.r.t. the order on the underlying - type. - - - - - -The call `lt(x, y)` returns `true` if, and only if, `x` is - considered to be less than `y` w.r.t. the order on the underlying - type. - - - - - -val ge : 'a.'a -> 'a -> bool - - -let ge: <a>(_: a) => (_: a) => bool - - - -The call `ge x y` returns `true` if, and only if, - `x` is considered to be greater or equal than `y` w.r.t. the order - on the underlying type. - - - - - -The call `ge(x, y)` returns `true` if, and only if, - `x` is considered to be greater or equal than `y` w.r.t. the order - on the underlying type. - - - - - -val le : 'a.'a -> 'a -> bool - - -let le: <a>(_: a) => (_: a) => bool - - - -The call `le x y` returns `true` if, and only if, `x` - is considered to be less or equal than `y` w.r.t. the order on the - underlying type. - - - - - -The call `le(x, y)` returns `true` if, and only if, `x` - is considered to be less or equal than `y` w.r.t. the order on the - underlying type. - - diff --git a/gitlab-pages/docs/reference/test.next.contract.md b/gitlab-pages/docs/reference/test.next.contract.md deleted file mode 100644 index d7919dcf11..0000000000 --- a/gitlab-pages/docs/reference/test.next.contract.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -id: test.next.contract-reference -title: contract -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val transfer : 'p.'p contract -> 'p -> tez -> test_exec_result - - -let transfer: <p>(_: contract<p>) => (_: p) => (_: tez) => test_exec_result - - - -val transfer_exn : 'p.'p contract -> 'p -> tez -> nat - - -let transfer_exn: <p>(_: contract<p>) => (_: p) => (_: tez) => nat - - - -val to_typed_address : 'p 's.'p contract -> ('p, 's) typed_address - - -let to_typed_address: <p, s>(_: contract<p>) => typed_address<p, s> - diff --git a/gitlab-pages/docs/reference/test.next.crypto.md b/gitlab-pages/docs/reference/test.next.crypto.md deleted file mode 100644 index 9d5ea1ec53..0000000000 --- a/gitlab-pages/docs/reference/test.next.crypto.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -id: test.next.crypto-reference -title: crypto -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val sign : string -> bytes -> signature - - -let sign: (_: string) => (_: bytes) => signature - diff --git a/gitlab-pages/docs/reference/test.next.io.md b/gitlab-pages/docs/reference/test.next.io.md deleted file mode 100644 index c2d897f5e3..0000000000 --- a/gitlab-pages/docs/reference/test.next.io.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -id: test.next.io-reference -title: io -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val print : string -> unit - - -let print: (_: string) => unit - -Prints an string to stdout. - - - -val println : string -> unit - - -let println: (_: string) => unit - -Prints an string to stdout, ended with a newline. - - - -val eprint : string -> unit - - -let eprint: (_: string) => unit - -Prints an string to stderr. - - - -val eprintln : string -> unit - - -let eprintln: (_: string) => unit - -Prints an string to stderr, ended with a newline. - - - -val log : 'a.'a -> unit - - -let log: <a>(_: a) => unit - -Logs a value. - - - -val set_test_print : unit -> unit - - -let set_test_print: (_: unit) => unit - -Turns on the printing of `test` prefixed values at the end of - tests. This is the default behaviour. - - - -val unset_test_print : unit -> unit - - -let unset_test_print: (_: unit) => unit - -Turns off the printing of `test` prefixed values at the end of - tests. diff --git a/gitlab-pages/docs/reference/test.next.md b/gitlab-pages/docs/reference/test.next.md deleted file mode 100644 index 74c94de183..0000000000 --- a/gitlab-pages/docs/reference/test.next.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -id: test.next-reference -title: next -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - -[module mutation](test.next.mutation.md) - - -module PBT = PBT - -[module state](test.next.state.md) - - -[module account](test.next.account.md) - - -[module compare](test.next.compare.md) - - -[module michelson](test.next.michelson.md) - - -[module io](test.next.io.md) - - -[module assert](test.next.assert.md) - - -[module string](test.next.string.md) - - -[module ticket](test.next.ticket.md) - - -[module originate](test.next.originate.md) - - -[module contract](test.next.contract.md) - - -[module typed_address](test.next.typed_address.md) - - -[module address](test.next.address.md) - - -[module timelock](test.next.timelock.md) - - -[module crypto](test.next.crypto.md) - - -[module dynamic_entrypoints](test.next.dynamic_entrypoints.md) - - - -val originate : 'p 's.('p, 's) module_contract -> 's -> tez -> ('p, 's) origination_result - - -let originate: <p, s>(_: module_contract<p, s>) => (_: s) => (_: tez) => origination_result<p, s> - - - -val failwith : 'a 'b.'a -> 'b - - -let failwith: <a, b>(_: a) => b - diff --git a/gitlab-pages/docs/reference/test.next.michelson.contract.md b/gitlab-pages/docs/reference/test.next.michelson.contract.md deleted file mode 100644 index c909393eac..0000000000 --- a/gitlab-pages/docs/reference/test.next.michelson.contract.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -id: test.next.michelson.contract-reference -title: contract -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val compile : 'p 's.(('p * 's) -> (operation list * 's)) -> ('p, 's) michelson_contract - - -let compile: <p, s>(_: (_: [p, s]) => [list<operation>, s]) => michelson_contract<p, s> - -Compiles a contract from an entrypoint function. - - - -val compile_with_views : 'p 's.(('p * 's) -> (operation list * 's)) -> 's views -> ('p, 's) michelson_contract - - -let compile_with_views: <p, s>(_: (_: [p, s]) => [list<operation>, s]) => (_: views<s>) => michelson_contract<p, s> - - - -val size : 'p 's.('p, 's) michelson_contract -> int - - -let size: <p, s>(_: michelson_contract<p, s>) => int - -Measures the size of a contract. - - - -val from_file : 'p 's.string -> ('p, 's) michelson_contract - - -let from_file: <p, s>(_: string) => michelson_contract<p, s> - -Reads a contract from a `.tz` file. diff --git a/gitlab-pages/docs/reference/test.next.michelson.md b/gitlab-pages/docs/reference/test.next.michelson.md deleted file mode 100644 index 3eaa1d7d79..0000000000 --- a/gitlab-pages/docs/reference/test.next.michelson.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -id: test.next.michelson-reference -title: michelson -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - -[module contract](test.next.michelson.contract.md) - - - -val run : 'a 'b.('a -> 'b) -> 'a -> michelson_program - - -let run: <a, b>(_: (_: a) => b) => (_: a) => michelson_program - -Run a function on an input, all in Michelson. More concretely: - a) compiles the function argument to Michelson `f_mich`; b) - compiles the value argument (which was evaluated already) to - Michelson `v_mich`; c) runs the Michelson interpreter on the code - `f_mich` with starting stack `[v_mich]`. - - - -val eval : 'a.'a -> michelson_program - - -let eval: <a>(_: a) => michelson_program - -Compile a LIGO value to Michelson. Currently it is a - renaming of `compile_value`. - - - -val decompile : 'a.michelson_program -> 'a - - -let decompile: <a>(_: michelson_program) => a - -Decompile a Michelson value to LIGO, following the - (mandatory) type annotation. Note: This operation can fail at - run-time, in case that the `michelson_program` given cannot be - decompiled to something compatible with the annotated type. - - - -val parse : string -> michelson_program - - -let parse: (_: string) => michelson_program - -Parses Michelson (as string) into a `michelson_program`. diff --git a/gitlab-pages/docs/reference/test.next.mutation.all.md b/gitlab-pages/docs/reference/test.next.mutation.all.md deleted file mode 100644 index 40413fce07..0000000000 --- a/gitlab-pages/docs/reference/test.next.mutation.all.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -id: test.next.mutation.all-reference -title: all -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val func : 'a 'b.'a -> ('a -> 'b) -> ('b * mutation) list - - -let func: <a, b>(_: a) => (_: (_: a) => b) => list<[b, mutation]> - -Given a value to mutate (first argument), it will try all the - mutations of it, passing each one to the function (second - argument). In case no failure arises when running the function on - a mutation, the failure and mutation involved will be added to the - list to be returned. - - - -val from_file : - 'b - 'p - 's.string -> 's -> tez -> ((('p, 's) typed_address * ('p, 's) michelson_contract * int) -> 'b) -> ('b * mutation) list - - -let from_file: - <b, p, s>(_: string) => (_: s) => (_: tez) => (_: (_: [typed_address<p, s>, michelson_contract<p, s>, int]) => b) => list< - [b, mutation] - > - -Given a contract from a file (passed by filepath, entrypoint and - views), an initial storage and balance, it will originate mutants - of the contract and pass the result to the function (last - argument). In case no failure arises when running the function on - a mutation, the failure and mutation involved will be added to the - list to be returned. - - - -val contract : - 'p - 's - 'b.('p, 's) module_contract -> - 's -> tez -> (('p, 's) typed_address -> ('p, 's) michelson_contract -> int -> 'b) -> ('b * mutation) list - - -let contract: - <p, s, b>(_: module_contract<p, s>) => (_: s) => (_: tez) => ( - _: (_: typed_address<p, s>) => (_: michelson_contract<p, s>) => (_: int) => b - ) => list<[b, mutation]> - -Given a contract as a module/namespace, an initial storage and - balance, it will originate mutants of the contract and pass the - result to the function (last argument). In case no failure arises - when running the function on a mutation, the failure and mutation - involved will be added to the list to be returned. diff --git a/gitlab-pages/docs/reference/test.next.mutation.md b/gitlab-pages/docs/reference/test.next.mutation.md deleted file mode 100644 index 727b4a4a2f..0000000000 --- a/gitlab-pages/docs/reference/test.next.mutation.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -id: test.next.mutation-reference -title: mutation -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - -[module all](test.next.mutation.all.md) - - - -val func : 'a 'b.'a -> ('a -> 'b) -> ('b * mutation) option - - -let func: <a, b>(_: a) => (_: (_: a) => b) => option<[b, mutation]> - -Given a value to mutate (first argument), it will try all the - mutations available of it, passing each one to the function - (second argument). On the first case of non failure when running - the function on a mutation, the value and mutation involved will - be returned. - - - -val from_file : - 'b - 'p - 's.string -> - 's -> tez -> ((('p, 's) typed_address * ('p, 's) michelson_contract * int) -> 'b) -> ('b * mutation) option - - -let from_file: - <b, p, s>(_: string) => (_: s) => (_: tez) => (_: (_: [typed_address<p, s>, michelson_contract<p, s>, int]) => b) => option< - [b, mutation] - > - -Given a contract from a file (passed by filepath, entrypoint and - views), an initial storage and balance, it will originate mutants - of the contract and pass the result to the function (last - argument). On the first case of non failure when running the - function on a mutation, the value and mutation involved will be - returned. - - - -val contract : - 'p - 's - 'b.('p, 's) module_contract -> - 's -> tez -> (('p, 's) typed_address -> ('p, 's) michelson_contract -> int -> 'b) -> ('b * mutation) option - - -let contract: - <p, s, b>(_: module_contract<p, s>) => (_: s) => (_: tez) => ( - _: (_: typed_address<p, s>) => (_: michelson_contract<p, s>) => (_: int) => b - ) => option<[b, mutation]> - -Given a contract as a module/namespace, an initial storage and - balance, it will originate mutants of the contract and pass the - result to the function (last argument). On the first case of non - failure when running the function on a mutation, the value and - mutation involved will be returned. - - - -val value : 'a.nat -> 'a -> ('a * mutation) option - - -let value: <a>(_: nat) => (_: a) => option<[a, mutation]> - -Mutates a value using a natural number as an index for the - available mutations, returns an option for indicating whether - mutation was successful or not. - - - -val save : string -> mutation -> string option - - -let save: (_: string) => (_: mutation) => option<string> - -This function reconstructs a file from a mutation (second - argument), and saves it to a file in the directory path (first - argument). It returns an optional string indicating the filename - where the mutation was saved, or `None` if there was an error. diff --git a/gitlab-pages/docs/reference/test.next.originate.md b/gitlab-pages/docs/reference/test.next.originate.md deleted file mode 100644 index 379cd29963..0000000000 --- a/gitlab-pages/docs/reference/test.next.originate.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -id: test.next.originate-reference -title: originate -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -type ('p, 's) origination_result = { - code : ('p, 's) michelson_contract; - size : int; - taddr : ('p, 's) typed_address -} - - -type origination_result<p, s> = { code: michelson_contract<p, s>; size: int; taddr: typed_address<p, s> } - - - -val contract : 'p 's.('p, 's) module_contract -> 's -> tez -> ('p, 's) origination_result - - -let contract: <p, s>(_: module_contract<p, s>) => (_: s) => (_: tez) => origination_result<p, s> - -Originate a contract with an entrypoint function in curried - form, initial storage and initial balance. - - - -val from_file : 'p 's.string -> 's -> tez -> ('p, 's) origination_result - - -let from_file: <p, s>(_: string) => (_: s) => (_: tez) => origination_result<p, s> - -Originate a contract with a path to the contract file, an - entrypoint, and a list of views, together with an initial storage - and an initial balance. - - - -val michelson : 'p 's.('p, 's) michelson_contract -> 's -> tez -> ('p, 's) typed_address - - -let michelson: <p, s>(_: michelson_contract<p, s>) => (_: s) => (_: tez) => typed_address<p, s> - -Originate a contract with initial storage and initial - balance. diff --git a/gitlab-pages/docs/reference/test.next.state.md b/gitlab-pages/docs/reference/test.next.state.md deleted file mode 100644 index c98ad45527..0000000000 --- a/gitlab-pages/docs/reference/test.next.state.md +++ /dev/null @@ -1,203 +0,0 @@ ---- -id: test.next.state-reference -title: state -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - -[module reset](test.next.state.reset.md) - - - -val restore : unit -> unit - - -let restore: (_: unit) => unit - -Pops a testing framework context from the stack of contexts, and - sets it up as the new current context. In case the stack was - empty, the current context is kept. - - - -val save : unit -> unit - - -let save: (_: unit) => unit - -Takes current testing framework context and saves it, pushing it - into a stack of contexts. - - - -val drop : unit -> unit - - -let drop: (_: unit) => unit - -Drops a testing framework context from the stack of contexts. In - case the stack was empty, nothing is done. - - - -val reset : nat -> tez list -> unit - - -let reset: (_: nat) => (_: list<tez>) => unit - -Generates a number of random bootstrapped accounts with a - default amount of `4000000` tez. The passed list can be used to - overwrite the amount. By default, the state only has two - bootstrapped accounts. Notice that since Ithaca, a percentage of - an account's balance is frozen (5% in testing mode) in case the - account can be taken to be a validator, and thus getting - balance can show a different amount to the one being set with - `Test.State.reset`. - - - -val reset_at : timestamp -> nat -> tez list -> unit - - -let reset_at: (_: timestamp) => (_: nat) => (_: list<tez>) => unit - -Generates a number of random bootstrapped accounts with a - default amount of `4000000` tez. The passed list can be used to - overwrite the amount. By default, the state only has two - bootstrapped accounts. Notice that since Ithaca, a percentage of - an account's balance is frozen (5% in testing mode) in case the - account can be taken to be a validator, and thus getting - balance can show a different amount to the one being set with - `Test.State.reset`. It also takes a starting timestamp - for the genesis block. - - - -val register_delegate : key_hash -> unit - - -let register_delegate: (_: key_hash) => unit - -Registers a `key_hash` corresponding to an account as a delegate. - - - -val register_constant : michelson_program -> string - - -let register_constant: (_: michelson_program) => string - -Registers a global constant, returns its hash as a string. See - the documentation for global constants for an example of usage. - - - -val set_source : address -> unit - - -let set_source: (_: address) => unit - -Sets the source for `Test.transfer` and `Test.originate`. - - - -val set_baker_policy : test_baker_policy -> unit - - -let set_baker_policy: (_: test_baker_policy) => unit - -Forces the baking policy for `Test.transfer` and - `Test.originate`. By default, the first bootstrapped account. - - - -val set_baker : address -> unit - - -let set_baker: (_: address) => unit - -Forces the baker for `Test.transfer` and `Test.originate`, - implemented using `Test.set_baker_policy` with `By_account`. By - default, the first bootstrapped account. - - - -val bake_until : nat -> unit - - -let bake_until: (_: nat) => unit - -It bakes until a number of cycles pass, so that an account - registered as delegate can effectively act as a baker. Note: It - can be used in tests to manually advance time. - - - -val set_big_map : 'k 'v.int -> ('k, 'v) big_map -> unit - - -let set_big_map: <k, v>(_: int) => (_: big_map<k, v>) => unit - -The testing framework keeps an internal reference to the values - corresponding to big map identifiers. This function allows to - override the value of a particular big map identifier. It should - not be normally needed, except in particular circumstances such as - using custom bootstrap contracts that initialize big maps. - - - -val get_voting_power : key_hash -> nat - - -let get_voting_power: (_: key_hash) => nat - -Return the voting power of a given contract. This voting power - coincides with the weight of the contract in the voting listings - (i.e., the rolls count) which is calculated at the beginning of - every voting period. - - - -val get_total_voting_power : unit -> nat - - -let get_total_voting_power: (_: unit) => nat - -Returns the total voting power of all contracts. The total - voting power coincides with the sum of the rolls count of every - contract in the voting listings. The voting listings is calculated - at the beginning of every voting period. - - - -val last_originations : unit -> (address, address list) map - - -let last_originations: (_: unit) => map<address, list<address>> - -Returns addresses of orginated accounts in the last transfer. It - is given in the form of a map binding the address of the source of - the origination operation to the addresses of newly originated - accounts. - - - -val last_events : 'a 'p 's.('p, 's) typed_address -> string -> 'a list - - -let last_events: <a, p, s>(_: typed_address<p, s>) => (_: string) => list<a> - -Returns the list of all the event payloads emited with a given - tag by a given address. Any call to this function must be - annotated with the expected payload type. - - - -val stake : key_hash -> tez -> unit - - -let stake: (_: key_hash) => (_: tez) => unit - diff --git a/gitlab-pages/docs/reference/test.next.state.reset.md b/gitlab-pages/docs/reference/test.next.state.reset.md deleted file mode 100644 index daae21f17b..0000000000 --- a/gitlab-pages/docs/reference/test.next.state.reset.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -id: test.next.state.reset-reference -title: reset -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val add_baker : (string * key) -> tez option -> unit - - -let add_baker: (_: [string, key]) => (_: option<tez>) => unit - -Adds an account `(sk, pk)` as a baker. The change is only - effective after `Test.reset_state`. - - - -val add_func_contract : 'p 's.(('p * 's) -> (operation list * 's)) -> 's -> tez -> unit - - -let add_func_contract: <p, s>(_: (_: [p, s]) => [list<operation>, s]) => (_: s) => (_: tez) => unit - -Setup a bootstrap contract with an entrypoint function, initial - storage and initial balance. Bootstrap contracts will be loaded in - order, and they will be available only after reset. diff --git a/gitlab-pages/docs/reference/test.next.string.md b/gitlab-pages/docs/reference/test.next.string.md deleted file mode 100644 index ec6527ebc4..0000000000 --- a/gitlab-pages/docs/reference/test.next.string.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -id: test.next.string-reference -title: string -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val chr : nat -> string option - - -let chr: (_: nat) => option<string> - -String consisting of the character represented by a `nat` in the - interval `[0, 255]`. - - - -val nl : string - - -let nl: string - -String consisting of only a newline. - - - -val show : 'a.'a -> string - - -let show: <a>(_: a) => string - -Converts a value to a string (same conversion as used by - `Test.log`). - - - -val json : 'a.'a -> string - - -let json: <a>(_: a) => string - -Converts a value to its JSON representation (as a string). - - - -val debugger_json : 'a.'a -> string - - -let debugger_json: <a>(_: a) => string - diff --git a/gitlab-pages/docs/reference/test.next.ticket.md b/gitlab-pages/docs/reference/test.next.ticket.md deleted file mode 100644 index 36e81519be..0000000000 --- a/gitlab-pages/docs/reference/test.next.ticket.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -id: test.next.ticket-reference -title: ticket -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - -module Proxy = Proxy_ticket diff --git a/gitlab-pages/docs/reference/test.next.timelock.md b/gitlab-pages/docs/reference/test.next.timelock.md deleted file mode 100644 index ec54b8edff..0000000000 --- a/gitlab-pages/docs/reference/test.next.timelock.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -id: test.next.timelock-reference -title: timelock -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val create : bytes -> nat -> (chest * chest_key) - - -let create: (_: bytes) => (_: nat) => [chest, chest_key] - - - -val create_key : chest -> nat -> chest_key - - -let create_key: (_: chest) => (_: nat) => chest_key - - - -val verify : chest -> chest_key -> nat -> bool - - -let verify: (_: chest) => (_: chest_key) => (_: nat) => bool - - - -The call `verify chest chest_key n` verifies a matching - between `chest` and `chest_key` (taking into account `n`). - - - - - -The call `verify(chest, chest_key, n)` verifies a matching - between `chest` and `chest_key` (taking into account `n`). - - diff --git a/gitlab-pages/docs/reference/test.next.typed_address.md b/gitlab-pages/docs/reference/test.next.typed_address.md deleted file mode 100644 index 38464f17d8..0000000000 --- a/gitlab-pages/docs/reference/test.next.typed_address.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -id: test.next.typed-address-reference -title: typed_address -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val transfer : 'p 's.('p, 's) typed_address -> 'p -> tez -> test_exec_result - - -let transfer: <p, s>(_: typed_address<p, s>) => (_: p) => (_: tez) => test_exec_result - -Bakes a transaction by sending an amount of tez with a parameter - from the current source to another account. Returns the amount of - gas consumed by the execution of the contract. - - - -val transfer_exn : 'p 's.('p, 's) typed_address -> 'p -> tez -> nat - - -let transfer_exn: <p, s>(_: typed_address<p, s>) => (_: p) => (_: tez) => nat - -Bakes a transaction by sending an amount of tez with a parameter - from the current source to another account. Returns the amount of - gas consumed by the execution of the contract. Similar as - `Test.transfer`, but fails when anything goes wrong. - - - -val get_storage : 'p 's.('p, 's) typed_address -> 's - - -let get_storage: <p, s>(_: typed_address<p, s>) => s - -Gets the storage of a typed account. - - - -val get_balance : 'p 's.('p, 's) typed_address -> tez - - -let get_balance: <p, s>(_: typed_address<p, s>) => tez - -Gets the balance of an account in tez. - - - -val to_address : 'p 's.('p, 's) typed_address -> address - - -let to_address: <p, s>(_: typed_address<p, s>) => address - - - -val to_contract : 'p 's.('p, 's) typed_address -> 'p contract - - -let to_contract: <p, s>(_: typed_address<p, s>) => contract<p> - -Gets the contract corresponding to the default entrypoint of a - typed address: the contract parameter in the result will be the - type of the default entrypoint (generally `'param`, but this might - differ if `'param` includes a "default" entrypoint). - - - -val get_entrypoint : 'p 's 'q.string -> ('p, 's) typed_address -> 'q contract - - -let get_entrypoint: <p, s, q>(_: string) => (_: typed_address<p, s>) => contract<q> - -Gets the contract corresponding to an entrypoint of a typed - address: the contract parameter in the result will be the type of - the entrypoint, it needs to be annotated, entrypoint string should - omit the prefix "%", but if passed a string starting with "%", it - will be removed (and a warning emitted). diff --git a/gitlab-pages/docs/reference/test.originate.md b/gitlab-pages/docs/reference/test.originate.md new file mode 100644 index 0000000000..e632404887 --- /dev/null +++ b/gitlab-pages/docs/reference/test.originate.md @@ -0,0 +1,51 @@ +--- +id: test.originate-reference +title: originate +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + + +val michelson : 'p 's.('p, 's) michelson_contract -> 's -> tez -> ('p, 's) typed_address + + +michelson: <p, s>(_: michelson_contract<p, s>, +storage: s, balance: tez) => typed_address<p, s> + + +Originate a contract with initial storage and initial balance. + + +val contract : 'p 's.('p, 's) module_contract -> 's -> tez -> ('p, 's) origination_result + + +contract: <p, s>(_: module_contract<p, s>, storage: s, balance: tez) => origination_result<p, s> + + + + +Originate a contract with an entrypoint function in curried form, +initial storage and initial balance. + + + + + +Originate a contract with an entrypoint, initial storage and +initial balance. + + + + + +val from_file : 'p 's.string -> 's -> tez -> ('p, 's) origination_result + + +from_file: <p, s>(path: string, storage: s, balance: tez) => origination_result<p, s> + + +Originate a contract with a path to the contract file, an entrypoint, +and a list of views, together with an initial storage and an initial +balance. diff --git a/gitlab-pages/docs/reference/test.pbt.md b/gitlab-pages/docs/reference/test.pbt.md index 0c16ba31f0..efbb4872df 100644 --- a/gitlab-pages/docs/reference/test.pbt.md +++ b/gitlab-pages/docs/reference/test.pbt.md @@ -3,35 +3,34 @@ id: test.pbt-reference title: pbt hide_table_of_contents: true --- + import Syntax from '@theme/Syntax'; import SyntaxTitle from '@theme/SyntaxTitle'; - - -val gen : 'a.'a pbt_gen +val gen : 'a.'a pbt_gen -let gen: <a>pbt_gen<a> +gen: <a>pbt_gen<a> -val gen_small : 'a.'a pbt_gen +val gen_small : 'a.'a pbt_gen -let gen_small: <a>pbt_gen<a> +gen_small: <a>pbt_gen<a> -val make_test : 'a.'a pbt_gen -> ('a -> bool) -> 'a pbt_test +val make_test : 'a.'a pbt_gen -> ('a -> bool) -> 'a pbt_test -let make_test: <a>(_: pbt_gen<a>) => (_: (_: a) => bool) => pbt_test<a> +make_test: <a>(_: pbt_gen<a>, predicate: (_: a) => bool) => pbt_test<a> -val run : 'a.'a pbt_test -> nat -> 'a pbt_result +val run : 'a.'a pbt_test -> nat -> 'a pbt_result -let run: <a>(_: pbt_test<a>) => (_: nat) => pbt_result<a> +run: <a>(_: pbt_test<a>, _: nat) => pbt_result<a> diff --git a/gitlab-pages/docs/reference/test.proxy_ticket.md b/gitlab-pages/docs/reference/test.proxy_ticket.md deleted file mode 100644 index af3664421d..0000000000 --- a/gitlab-pages/docs/reference/test.proxy_ticket.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -id: test.proxy-ticket-reference -title: proxy_ticket -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -type 'v proxy_address = ('v * nat * address, unit) typed_address - - -type proxy_address<v> = typed_address<[[v, nat], address], unit> - - - -val init_transfer : 'vt 'whole_p.('vt ticket -> 'whole_p) -> 'vt proxy_address - - -let init_transfer: <vt, whole_p>(_: (_: ticket<vt>) => whole_p) => proxy_address<vt> - - - -val transfer : 'vt.'vt proxy_address -> ('vt * nat * address) -> test_exec_result - - -let transfer: <vt>(_: proxy_address<vt>) => (_: [[vt, nat], address]) => test_exec_result - - - -val originate : - 'vt - 'whole_s - 'vp.('vt * nat) -> - ('vt ticket -> 'whole_s) -> ('vp -> 'whole_s -> (operation list * 'whole_s)) -> ('vp, 'whole_s) typed_address - - -let originate: - <vt, whole_s, vp>(_: [vt, nat]) => (_: (_: ticket<vt>) => whole_s) => ( - _: (_: vp) => (_: whole_s) => [list<operation>, whole_s] - ) => typed_address<vp, whole_s> - - - -val get_storage : 'p 's 's2.('p, 's) typed_address -> 's2 - - -let get_storage: <p, s, s2>(_: typed_address<p, s>) => s2 - diff --git a/gitlab-pages/docs/reference/test.state.md b/gitlab-pages/docs/reference/test.state.md new file mode 100644 index 0000000000..48718ef29d --- /dev/null +++ b/gitlab-pages/docs/reference/test.state.md @@ -0,0 +1,213 @@ +--- +id: test.state-reference +title: state +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + +[module reset](test.state.reset.md) + + + +val restore : unit -> unit + + +restore: (_: unit) => unit + + +Pops a testing framework context from the stack of contexts, and sets +it up as the new current context. In case the stack was empty, the +current context is kept. + + + +val save : unit -> unit + + +save: (_: unit) => unit + +Takes current testing framework context and saves it, pushing it + into a stack of contexts. + + + +val drop : unit -> unit + + +drop: (_: unit) => unit + + +Drops a testing framework context from the stack of contexts. In case +the stack was empty, nothing is done. + + + +val reset : nat -> tez list -> unit + + +reset: (_: nat, amounts: list<tez>) => unit + + +Generates a number of random bootstrapped accounts with a default +amount of `4000000` tez. The passed list can be used to overwrite the +amount. By default, the state only has two bootstrapped +accounts. Notice that since Ithaca, a percentage of an account's +balance is frozen (5% in testing mode) in case the account can be +taken to be a validator, and thus getting balance can show a different +amount to the one being set with `Test.State.reset`. + + + +val reset_at : timestamp -> nat -> tez list -> unit + + +reset_at: (_: timestamp, _: nat, amounts: list<tez>) => unit + + +Generates a number of random bootstrapped accounts with a default +amount of `4000000` tez. The passed list can be used to overwrite the +amount. By default, the state only has two bootstrapped +accounts. Notice that since Ithaca, a percentage of an account's +balance is frozen (5% in testing mode) in case the account can be +taken to be a validator, and thus getting balance can show a different +amount to the one being set with `Test.State.reset`. It also takes a +starting timestamp for the genesis block. + + + +val register_delegate : key_hash -> unit + + +register_delegate: (_: key_hash) => unit + + +Registers a `key_hash` corresponding to an account as a delegate. + + + +val register_constant : michelson_program -> string + + +register_constant: (_: michelson_program) => string + + +Registers a global constant, returns its hash as a string. See the +documentation for global constants for an example of usage. + + + +val set_source : address -> unit + + +set_source: (_: address) => unit + + +Sets the source for `Test.transfer` and `Test.originate`. + + + +val set_baker_policy : test_baker_policy -> unit + + +set_baker_policy: (_: test_baker_policy) => unit + + +Forces the baking policy for `Test.transfer` and `Test.originate`. By +default, the first bootstrapped account. + + + +val set_baker : address -> unit + + +set_baker: (_: address) => unit + + +Forces the baker for `Test.transfer` and `Test.originate`, implemented +using `Test.set_baker_policy` with `By_account`. By default, the first +bootstrapped account. + + + +val bake_until : nat -> unit + + +bake_until: (_: nat) => unit + + +It bakes until a number of cycles pass, so that an account registered +as delegate can effectively act as a baker. Note: It can be used in +tests to manually advance time. + + + +val set_big_map : 'k 'v.int -> ('k, 'v) big_map -> unit + + +set_big_map: <k, v>(_: int, _: big_map<k, v>) => unit + + +The testing framework keeps an internal reference to the values +corresponding to big map identifiers. This function allows to override +the value of a particular big map identifier. It should not be +normally needed, except in particular circumstances such as using +custom bootstrap contracts that initialize big maps. + + + +val get_voting_power : key_hash -> nat + + +get_voting_power: (_: key_hash) => nat + + +Return the voting power of a given contract. This voting power +coincides with the weight of the contract in the voting listings +(i.e., the rolls count) which is calculated at the beginning of every +voting period. + + + +val get_total_voting_power : unit -> nat + + +get_total_voting_power: (_: unit) => nat + + +Returns the total voting power of all contracts. The total voting +power coincides with the sum of the rolls count of every contract in +the voting listings. The voting listings is calculated at the +beginning of every voting period. + + + +val last_originations : unit -> (address, address list) map + + +last_originations: (_: unit) => map<address, list<address>> + + +Returns addresses of orginated accounts in the last transfer. It is +given in the form of a map binding the address of the source of the +origination operation to the addresses of newly originated accounts. + + + +val last_events : 'a 'p 's.('p, 's) typed_address -> string -> 'a list + + +last_events: <a, p, s>(_: typed_address<p, s>, tag: string) => list<a> + + +Returns the list of all the event payloads emited with a given tag by +a given address. Any call to this function must be annotated with the +expected payload type. + + + +val stake : key_hash -> tez -> unit + + +stake: (_: key_hash, amount: tez) => unit + diff --git a/gitlab-pages/docs/reference/test.state.reset.md b/gitlab-pages/docs/reference/test.state.reset.md new file mode 100644 index 0000000000..b796034277 --- /dev/null +++ b/gitlab-pages/docs/reference/test.state.reset.md @@ -0,0 +1,43 @@ +--- +id: test.state.reset-reference +title: reset +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + + + +val add_baker : (string * key) -> tez option -> unit + + +add_baker: (keys: [string, key], amount: option<tez>) => unit + + + + +Adds an account `(sk, pk)` as a baker. The change is only effective +after `Test.reset_state`. + + + + + +Adds an account `[sk, pk]` as a baker. The change is only effective +after `Test.reset_state`. + + + + + +val add_func_contract : 'p 's.(('p * 's) -> (operation list * 's)) -> 's -> tez -> unit + + +add_func_contract: <p, s>(_: (_: [p, s]) => +[list<operation>, s], storage: s, amount: tez) => unit + + +Setup a bootstrap contract with an entrypoint function, initial +storage and initial balance. Bootstrap contracts will be loaded in +order, and they will be available only after reset. diff --git a/gitlab-pages/docs/reference/Test.Next.String.md b/gitlab-pages/docs/reference/test.string.md similarity index 73% rename from gitlab-pages/docs/reference/Test.Next.String.md rename to gitlab-pages/docs/reference/test.string.md index ec6527ebc4..9efcec0355 100644 --- a/gitlab-pages/docs/reference/Test.Next.String.md +++ b/gitlab-pages/docs/reference/test.string.md @@ -1,5 +1,5 @@ --- -id: test.next.string-reference +id: test.string-reference title: string hide_table_of_contents: true --- @@ -7,48 +7,47 @@ import Syntax from '@theme/Syntax'; import SyntaxTitle from '@theme/SyntaxTitle'; - -val chr : nat -> string option +val nl : string -let chr: (_: nat) => option<string> +nl: string -String consisting of the character represented by a `nat` in the - interval `[0, 255]`. +String consisting of only a newline. -val nl : string +val show : 'a.'a -> string -let nl: string +show: <a>(_: a) => string -String consisting of only a newline. +Converts a value to a string (same conversion as used by `Test.log`). -val show : 'a.'a -> string +val json : 'a.'a -> string -let show: <a>(_: a) => string +json: <a>(_: a) => string -Converts a value to a string (same conversion as used by - `Test.log`). +Converts a value to its JSON representation (as a string). -val json : 'a.'a -> string +val debugger_json : 'a.'a -> string -let json: <a>(_: a) => string +debugger_json: <a>(_: a) => string -Converts a value to its JSON representation (as a string). -val debugger_json : 'a.'a -> string +val chr : nat -> string option -let debugger_json: <a>(_: a) => string +chr: (_: nat) => option<string> + +String consisting of the character represented by a `nat` in the +interval `[0, 255]`. diff --git a/gitlab-pages/docs/reference/Test.Next.Ticket.md b/gitlab-pages/docs/reference/test.ticket.md similarity index 68% rename from gitlab-pages/docs/reference/Test.Next.Ticket.md rename to gitlab-pages/docs/reference/test.ticket.md index 36e81519be..b9639bad59 100644 --- a/gitlab-pages/docs/reference/Test.Next.Ticket.md +++ b/gitlab-pages/docs/reference/test.ticket.md @@ -1,5 +1,5 @@ --- -id: test.next.ticket-reference +id: test.ticket-reference title: ticket hide_table_of_contents: true --- @@ -7,5 +7,4 @@ import Syntax from '@theme/Syntax'; import SyntaxTitle from '@theme/SyntaxTitle'; - -module Proxy = Proxy_ticket +[module proxy](test.ticket.proxy.md) diff --git a/gitlab-pages/docs/reference/test.ticket.proxy.md b/gitlab-pages/docs/reference/test.ticket.proxy.md new file mode 100644 index 0000000000..e627fbae09 --- /dev/null +++ b/gitlab-pages/docs/reference/test.ticket.proxy.md @@ -0,0 +1,38 @@ +--- +id: test.ticket.proxy-reference +title: proxy +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + +val transfer : 'vt.'vt proxy_address -> ('vt * nat) * +address -> test_exec_result + + +transfer: <vt>(_: proxy_address<vt;>, info: +[[vt, nat], address]) => test_exec_result + + + +val originate: 'vt 'whole_s 'vp.'vt * nat -> ('vt ticket -> 'whole_s) +-> ('vp -> 'whole_s -> operation list * 'whole_s) -> +('vp, ' whole_s) typed_address + + + +originate: <vt, whole_s, vp>(ticket_info: [vt, nat], mk_storage: +ticket<vt> => whole_s), contract: ([vp, whole_s] => +[list<operation>, whole_s])) => typed_address<vp, whole_s> +[[vt, nat], address]) => test_exec_result + + + + +val get_storage : 'p 's 's2.('p, 's) typed_address -> 's2 + + + +get_storage: <p,s,s2>(_: typed_address<p,s>) => s2 + diff --git a/gitlab-pages/docs/reference/test.timelock.md b/gitlab-pages/docs/reference/test.timelock.md new file mode 100644 index 0000000000..df2b99726b --- /dev/null +++ b/gitlab-pages/docs/reference/test.timelock.md @@ -0,0 +1,44 @@ +--- +id: test.timelock-reference +title: timelock +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + + + +val create : bytes -> nat -> (chest * chest_key) + + +create: (_: bytes, _: nat) => [chest, chest_key] + + + +val create_key : chest -> nat -> chest_key + + +create_key: (_: chest, _: nat) => chest_key + + + +val verify : chest -> chest_key -> nat -> bool + + +verify: (_: chest, _: chest_key, _: nat) => bool + + + + +The call `verify chest chest_key n` verifies a matching between +`chest` and `chest_key` (taking into account `n`). + + + + + +The call `verify(chest, chest_key, n)` verifies a matching between +`chest` and `chest_key` (taking into account `n`). + + diff --git a/gitlab-pages/docs/reference/test.typed_address.md b/gitlab-pages/docs/reference/test.typed_address.md new file mode 100644 index 0000000000..79070d4a78 --- /dev/null +++ b/gitlab-pages/docs/reference/test.typed_address.md @@ -0,0 +1,87 @@ +--- +id: test.typed-address-reference +title: typed_address +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + +val to_contract : 'p 's.('p, 's) typed_address -> 'p contract + + +to_contract: <p, s>(_: typed_address<p, s>) => contract<p> + + +Gets the contract corresponding to the default entrypoint of a typed +address: the contract parameter in the result will be the type of the +default entrypoint (generally `'param`, but this might differ if +`'param` includes a "default" entrypoint). + + + +val transfer : 'p 's.('p, 's) typed_address -> 'p -> tez -> test_exec_result + + +transfer: <p, s>(_: typed_address<p, s>, param: p, amount: tez) => test_exec_result + + +Bakes a transaction by sending an amount of tez with a parameter from +the current source to another account. Returns the amount of gas +consumed by the execution of the contract. + + + +val transfer_exn : 'p 's.('p, 's) typed_address -> 'p -> tez -> nat + + +transfer_exn: <p, s>(_: typed_address<p, s>, param: p, amount: tez) => nat + + +Bakes a transaction by sending an amount of tez with a parameter from +the current source to another account. Returns the amount of gas +consumed by the execution of the contract. Similar as `Test.transfer`, +but fails when anything goes wrong. + + + +val get_storage : 'p 's.('p, 's) typed_address -> 's + + +get_storage: <p, s>(_: typed_address<p, s>) => s + + +Gets the storage of a typed account. + + + +val to_address : 'p 's.('p, 's) typed_address -> address + + +to_address: <p, s>(_: typed_address<p, s>) => address + + +Casting a typed address to a regular address. + + +val get_balance : 'p 's.('p, 's) typed_address -> tez + + +get_balance: <p, s>(_: typed_address<p, s>) => tez + + +Gets the balance of an account in tez. + + + +val get_entrypoint : 'p 's 'q.string -> ('p, 's) typed_address -> 'q contract + + +get_entrypoint: <p, s, q>(entrypoint: string, _: typed_address<p, s>) => contract<q> + + +Gets the contract corresponding to an entrypoint of a typed address: +the contract parameter in the result will be the type of the +entrypoint, it needs to be annotated, entrypoint string should omit +the prefix "%", but if passed a string starting with "%", it will be +removed (and a warning emitted). diff --git a/gitlab-pages/docs/reference/tezos.md b/gitlab-pages/docs/reference/tezos.md index 24551d23bf..6593faf125 100644 --- a/gitlab-pages/docs/reference/tezos.md +++ b/gitlab-pages/docs/reference/tezos.md @@ -9,54 +9,60 @@ import SyntaxTitle from '@theme/SyntaxTitle'; Tezos-specific functions +[module Operation](tezos.operation.md) -[module next](tezos.next.md) +[module View](tezos.view.md) +[module Ticket](tezos.ticket.md) + +[module Sapling](tezos.sapling.md) -val get_sender : unit -> address +val get_sender : unit -> address -let get_sender: (_: unit) => address +get_sender: (_: unit) => address + The call `Tezos.get_sender ()` is the address of the contract (that - is, a smart contract or an implicit account) that initiated the - current internal transaction. Note that, if transactions have been - chained, that address could be different from `Tezos.get_source ()`. +is, a smart contract or an implicit account) that initiated the +current internal transaction. Note that, if transactions have been +chained, that address could be different from `Tezos.get_source ()`. -The call `Tezos.get_sender()` is the address of the contract (that - is, a smart contract or an implicit account) that initiated the - current internal transaction. Note that, if transactions have been - chained, that address could be different from `Tezos.get_source()`. +The call `Tezos.get_sender()` is the address of the contract (that is, +a smart contract or an implicit account) that initiated the current +internal transaction. Note that, if transactions have been chained, +that address could be different from `Tezos.get_source()`. -val get_source : unit -> address +val get_source : unit -> address -let get_source: (_: unit) => address +get_source: (_: unit) => address + The call `Tezos.get_source ()` is the address of the implicit account - that initiated the current transaction. If transactions have been - chained, that address is different from `Tezos.get_sender ()`. +that initiated the current transaction. If transactions have been +chained, that address is different from `Tezos.get_sender ()`. The call `Tezos.get_source()` is the address of the implicit account - that initiated the current transaction. If transactions have been - chained, that address is different from `Tezos.get_sender()`. +that initiated the current transaction. If transactions have been +chained, that address is different from `Tezos.get_sender()`. @@ -65,70 +71,70 @@ The call `Tezos.get_source()` is the address of the implicit account val self : 'a.string -> 'a contract -let self: <a>(_: string) => contract<a> +self: <a>(entrypoint: string) => contract<a> + The call `Tezos.self entrypoint` is the address of the current smart - contract, that is, the smart contract containing the call. For the - address of the smart contract actually *executing* the call, - because it is embedded in a lambda sent to another smart contract, - use `Tezos.get_self_address` instead. The string `entrypoint` is the - name of a valid entrypoint such that `entrypoint` is not - `"%default"`, or the empty string denoting the `"%default"` - entrypoint (which is the root of the smart contract parameter if - no `"%default"` entrypoint is explicitly defined). If the contract - does not have the specified entrypoint, the call results in an - type checking error. +contract, that is, the smart contract containing the call. For the +address of the smart contract actually *executing* the call, because +it is embedded in a lambda sent to another smart contract, use +`Tezos.get_self_address` instead. The string `entrypoint` is the name +of a valid entrypoint such that `entrypoint` is not `"%default"`, or +the empty string denoting the `"%default"` entrypoint (which is the +root of the smart contract parameter if no `"%default"` entrypoint is +explicitly defined). If the contract does not have the specified +entrypoint, the call results in an type checking error. The call `Tezos.self(entrypoint)` is the address of the current smart - contract, that is, the smart contract containing the call. For the - address of the smart contract actually *executing* the call, - because it is embedded in a lambda sent to another smart contract, - use `Tezos.get_self_address` instead. The string `entrypoint` is the - name of a valid entrypoint such that `entrypoint` is not - `"%default"`, or the empty string denoting the `"%default"` - entrypoint (which is the root of the smart contract parameter if - no `"%default"` entrypoint is explicitly defined). If the contract - does not have the specified entrypoint, the call results in an - type checking error. +contract, that is, the smart contract containing the call. For the +address of the smart contract actually *executing* the call, because +it is embedded in a lambda sent to another smart contract, use +`Tezos.get_self_address` instead. The string `entrypoint` is the name +of a valid entrypoint such that `entrypoint` is not `"%default"`, or +the empty string denoting the `"%default"` entrypoint (which is the +root of the smart contract parameter if no `"%default"` entrypoint is +explicitly defined). If the contract does not have the specified +entrypoint, the call results in an type checking error. -val get_self_address : unit -> address +val get_self_address : unit -> address -let get_self_address: (_: unit) => address +get_self_address: (_: unit) => address + The call `Tezos.get_self_address ()` is the address of the smart - contract actually executing the call, as a value of type - `address`. That contract can be different from the one containing - the call if the call is in a lambda transmitted to another smart - contract. Therefore, it is assumed that, in general, the type of - the executing contract is statically unknown, so the return type - of `Tezos.get_self_address` is not `'a contract`, but `address`. (See - `Tezos.self`.) +contract actually executing the call, as a value of type +`address`. That contract can be different from the one containing the +call if the call is in a lambda transmitted to another smart +contract. Therefore, it is assumed that, in general, the type of the +executing contract is statically unknown, so the return type of +`Tezos.get_self_address` is not `'a contract`, but `address`. (See +`Tezos.self`.) The call `Tezos.get_self_address()` is the address of the smart - contract actually executing the call, as a value of type - `address`. That contract can be different from the one containing - the call if the call is in a lambda transmitted to another smart - contract. Therefore, it is assumed that, in general, the type of - the executing contract is statically unknown, so the return type - of `Tezos.get_self_address` is not `'a contract`, but `address`. (See - `Tezos.self`.) +contract actually executing the call, as a value of type +`address`. That contract can be different from the one containing the +call if the call is in a lambda transmitted to another smart +contract. Therefore, it is assumed that, in general, the type of the +executing contract is statically unknown, so the return type of +`Tezos.get_self_address` is not `'a contract`, but `address`. (See +`Tezos.self`.) @@ -137,624 +143,309 @@ The call `Tezos.get_self_address()` is the address of the smart val address : 'a.'a contract -> address -let address: <a>(_: contract<a>) => address +address: <a>(contract_addr: contract<a>) => address + The call `Tezos.address contract` casts the address of the smart - contract `contract` into the more general value of type - `address`. +contract `contract` into the more general value of type `address`. The call `Tezos.address(contract)` casts the address of the smart - contract `contract` into the more general value of type - `address`. +contract `contract` into the more general value of type `address`. -val implicit_account : key_hash -> unit contract +val implicit_account : key_hash -> unit contract -let implicit_account: (_: key_hash) => contract<unit> +implicit_account: (_: key_hash) => contract<unit> + The call `Tezos.implicit_account kh` casts the public key hash `kh` - into the address of its implicit account. Note that addresses of - implicit accounts always have the type `unit contract`. +into the address of its implicit account. Note that addresses of +implicit accounts always have the type `unit contract`. The call `Tezos.implicit_account(kh)` casts the public key hash `kh` - into the address of its implicit account. Note that addresses of - implicit accounts always have the type `contract`. +into the address of its implicit account. Note that addresses of +implicit accounts always have the type `contract`. -val get_contract_opt : 'param.address -> 'param contract option +val get_contract_opt : 'param.address -> 'param contract option -let get_contract_opt: <param>(_: address) => option<contract<param>> +get_contract_opt: <param>(_: address) => option<contract<param>> + The call `Tezos.get_contract_opt addr` casts the address `addr` into - that of a contract address, if such contract exists. The value of - the call is `None` if no such contract exists, otherwise `Some - contract`, where `contract` is the contract's address. Note: The - address of an implicit account has type `unit contract`. +that of a contract address, if such contract exists. The value of the +call is `None` if no such contract exists, otherwise `Some contract`, +where `contract` is the contract's address. Note: The address of an +implicit account has type `unit contract`. -The call `Tezos.get_contract_opt(addr)` casts the address `addr` into - that of a contract address, if such contract exists. The value of - the call is `None()` if no such contract exists, otherwise `Some - contract`, where `contract` is the contract's address. Note: The - address of an implicit account has type `unit contract`. +The call `get_contract_opt(addr)` casts the address `addr` into +that of a contract address, if such contract exists. The value +of the call is `["None" as "None"]` if no such contract exists, +otherwise `["Some" as "Some", contract]`, where `contract` is +the contract's address. Note: The address of an implicit account +has type `contract`. -val get_contract_with_error : 'param.address -> string -> 'param contract +val get_contract_with_error : 'param.address -> string -> 'param contract -let get_contract_with_error: <param>(_: address) => (_: string) => contract<param> +get_contract_with_error: <param>(_: address) => (_: string) => contract<param> + The call `Tezos.get_contract_with_error addr error` casts the address - `addr` into that of a contract address, if such contract - exists. If not, the execution fails with the error message - `error`. +`addr` into that of a contract address, if such contract exists. If +not, the execution fails with the error message `error`. -The call `Tezos.get_contract_with_error(addr, error)` casts the address - `addr` into that of a contract address, if such contract - exists. If not, the execution fails with the error message - `error`. +The call `Tezos.get_contract_with_error(addr, error)` casts the +address `addr` into that of a contract address, if such contract +exists. If not, the execution fails with the error message `error`. -val get_contract : 'param.address -> 'param contract +val get_contract : 'param.address -> 'param contract -let get_contract: <param>(_: address) => contract<param> +get_contract: <param>(_: address) => contract<param> + The call `Tezos.get_contract addr` casts the address `addr` into that - of a smart contract address, if such contract exists. The call - fails with the message `"bad address for get_contract"` if no - such smart contract exists. Note: The address of an implicit - account has type `unit contract`. +of a smart contract address, if such contract exists. The call fails +with the message `"bad address for get_contract"` if no such smart +contract exists. Note: The address of an implicit account has type +`unit contract`. The call `Tezos.get_contract(addr)` casts the address `addr` into that - of a smart contract address, if such contract exists. The call - fails with the message `"bad address for get_contract"` if no - such smart contract exists. Note: The address of an implicit - account has type `contract`. - - - - - -val get_entrypoint_opt : 'param.string -> address -> 'param contract option - - -let get_entrypoint_opt: <param>(_: string) => (_: address) => option<contract<param>> - - - -The call `Tezos.get_entrypoint_opt entrypoint addr` has the same - behaviour as `Tezos.get_contract_opt addr`, with the additional - constraint that the contract must have an entrypoint named - `entrypoint`. In other words, `Tezos.get_entrypoint_opt entrypoint addr` - casts the address `addr` into that of a smart contract - address, if such contract exists and has an entrypoint named - `entrypoint`. The value of the call is `None` if no such smart - contract exists, otherwise `Some contract`, where `contract` is - the smart contract's address. Note: The address of an implicit - account has type `unit contract`. - - - - - -The call `Tezos.get_entrypoint_opt(entrypoint, addr)` has the same - behaviour as `Tezos.get_contract_opt(addr)`, with the additional - constraint that the contract must have an entrypoint named - `entrypoint`. In other words, `Tezos.get_entrypoint_opt(entrypoint, addr)` - casts the address `addr` into that of a smart contract - address, if such contract exists and has an entrypoint named - `entrypoint`. The value of the call is `None()` if no such smart - contract exists, otherwise `Some(contract)`, where `contract` is - the smart contract's address. Note: The address of an implicit - account has type `contract`. - - - - - -val get_entrypoint : 'param.string -> address -> 'param contract - - -let get_entrypoint: <param>(_: string) => (_: address) => contract<param> - - - -The call `Tezos.get_entrypoint entrypoint addr` casts the address - `addr` into that of a smart contract address, if such contract - exists and has an entrypoint named `entrypoint`. If no such smart - contract exists, the execution fails with the error message - `"bad address for get_entrypoint"`. Note: The address of an implicit - account has type `unit contract`. - - - - - -The call `Tezos.get_entrypoint(entrypoint, addr)` casts the address - `addr` into that of a smart contract address, if such contract - exists and has an entrypoint named `entrypoint`. If no such smart - contract exists, the execution fails with the error message - `"bad address for get_entrypoint"`. Note: The address of an implicit - account has type `contract`. - - - - - -val create_contract : - 'param - 'storage.('param, 'storage) entrypoint -> key_hash option -> tez -> 'storage -> (operation * address) - - -let create_contract: - <param, storage>(_: entrypoint<param, storage>) => (_: option<key_hash>) => (_: tez) => (_: storage) => [ - operation, - address - ] - -**Deprecated:** In a future version, `Tezos` will be replaced by `Tezos.Next`, and using `Operation.create_contract` from `Tezos.Next` is encouraged for a smoother migration. - - - -The call `Tezos.create_contract e d a s` returns a contract creation - operation (origination) for the entrypoint `e` (as a function) - with optional delegate `d`, initial amount `a` and initial - storage `s`, together with the address of the created - contract. Note that the created contract cannot be called - immediately afterwards (that is, `Tezos.get_contract_opt` on that - address would return `None`), as the origination must be - performed successfully first, for example by calling a proxy - contract or itself. - - - - - -The call `Tezos.create_contract(e,d,a,s)` returns a contract creation - operation (origination) for the entrypoint `e` (as a function) - with optional delegate `d`, initial amount `a` and initial - storage `s`, together with the address of the created - contract. Note that the created contract cannot be called - immediately afterwards (that is, `Tezos.get_contract_opt` on that - address would return `None()`), as the origination must be - performed successfully first, for example by calling a proxy - contract or itself. - - - - - -val set_delegate : key_hash option -> operation - - -let set_delegate: (_: option<key_hash>) => operation - -**Deprecated:** In a future version, `Tezos` will be replaced by `Tezos.Next`, and using `Operation.set_delegate` from `Tezos.Next` is encouraged for a smoother migration. - - - -The call `Tezos.set_delegate d` evaluates in an operation that sets - the delegate of the current smart contract to be `d`, where `d` is - an optional key hash. If `None`, the delegation is withdrawn. If - the contract has no delegation, then no change occurs. If `d` is - `Some kh`, where `kh` is the key hash of a registered delegate - that is not the current delegate of the contract, then this - operation sets the delegate of the contract to this registered - delegate. A failure occurs if `kh` is the current delegate of the - contract or if `kh` is not a registered delegate. However, the - instruction in itself does not fail; it produces an operation that - will fail when applied. - - - - - -The call `Tezos.set_delegate(d)` evaluates in an operation that sets - the delegate of the current smart contract to be `d`, where `d` is - an optional key hash. If `None()`, the delegation is withdrawn. If - the contract has no delegation, then no change occurs. If `d` is - `Some(kh)`, where `kh` is the key hash of a registered delegate - that is not the current delegate of the contract, then this - operation sets the delegate of the contract to this registered - delegate. A failure occurs if `kh` is the current delegate of the - contract or if `kh` is not a registered delegate. However, the - instruction in itself does not fail; it produces an operation that - will fail when applied. - - - - - -val transaction : 'param.'param -> tez -> 'param contract -> operation - - -let transaction: <param>(_: param) => (_: tez) => (_: contract<param>) => operation - -**Deprecated:** In a future version, `Tezos` will be replaced by `Tezos.Next`, and using `Operation.transaction` from `Tezos.Next` is encouraged for a smoother migration. - - - -The call `Tezos.transaction param amount contract_addr` evaluates in - an operation that will send the amount `amount` in mutez to the - contract at the valid address `contract_addr`, with parameter - `param`. If the contract is an implicit account, the parameter - must be `unit`. - - - - - -The call `Tezos.transaction(param, amount, contract_addr)` evaluates in - an operation that will send the amount `amount` in mutez to the - contract at the valid address `contract_addr`, with parameter - `param`. If the contract is an implicit account, the parameter - must be `unit`. - - - - - -val call_view : 'param 'return.string -> 'param -> address -> 'return option - - -let call_view: <param, return>(_: string) => (_: param) => (_: address) => option<return> - - - -The call `Tezos.call_view v p a` calls the view `v` with parameter - `param` at the contract whose address is `a`. The value returned - is `None` if the view does not exist, or has a different type of - parameter, or if the contract does not exist at that - address. Otherwise, it is `Some v`, where `v` is the return value - of the view. Note: the storage of the view is the same as when the - execution of the contract calling the view started. - - - - - -The call `Tezos.call_view(v, p, a)` calls the view `v` with parameter - `param` at the contract whose address is `a`. The value returned - is `None()` if the view does not exist, or has a different type of - parameter, or if the contract does not exist at that - address. Otherwise, it is `Some(v)`, where `v` is the return value - of the view. Note: the storage of the view is the same as when the - execution of the contract calling the view started. - - - - - -val create_ticket : 'a.'a -> nat -> 'a ticket option - - -let create_ticket: <a>(_: a) => (_: nat) => option<ticket<a>> - -**Deprecated:** In a future version, `Tezos` will be replaced by `Tezos.Next`, and using `Ticket.create` from `Tezos.Next` is encouraged for a smoother migration. - - - -The call `Tezos.create_ticket v a` creates a ticket with value `v` and - amount `a`. If the creation is a success, the value `Some t` is - returned, where `t` is the ticket; otherwise, `None` is the - result. Note: Tickets cannot be duplicated. - - - - - -The call `Tezos.create_ticket(v, a)` creates a ticket with value `v` and - amount `a`. If the creation is a success, the value `Some(t)` is - returned, where `t` is the ticket; otherwise, `None()` is the - result. Note: Tickets cannot be duplicated. - - - - - -val split_ticket : 'a.'a ticket -> (nat * nat) -> ('a ticket * 'a ticket) option - - -let split_ticket: <a>(_: ticket<a>) => (_: [nat, nat]) => option<[ticket<a>, ticket<a>]> - -**Deprecated:** In a future version, `Tezos` will be replaced by `Tezos.Next`, and using `Ticket.split` from `Tezos.Next` is encouraged for a smoother migration. - - - -The call `Tezos.split_ticket t (a1, a2)` results in a pair of tickets - `t1` and `t2` such that the former owns the amount `a1` and the - later `a2`. More precisely, the value of the call is - `Some (t1, t2)` because signifying to the callee the failure of - the splitting is achieved by returning the value `None`. - - - - - -The call `Tezos.split_ticket(t, [a1, a2])` results in a pair of tickets - `t1` and `t2` such that the former owns the amount `a1` and the - later `a2`. More precisely, the value of the call is - `Some([t1, t2])` because signifying to the callee the failure of - the splitting is achieved by returning the value `None()`. - - - - - -val join_tickets : 'a.('a ticket * 'a ticket) -> 'a ticket option - - -let join_tickets: <a>(_: [ticket<a>, ticket<a>]) => option<ticket<a>> - -**Deprecated:** In a future version, `Tezos` will be replaced by `Tezos.Next`, and using `Ticket.join` from `Tezos.Next` is encouraged for a smoother migration. - - - -The call `Tezos.join_tickets (t1, t2)` joins the tickets `t1` and - `t2`, which must have the same type of value. - - - - - -The call `Tezos.join_tickets(t1, t2)` joins the tickets `t1` and - `t2`, which must have the same type of value. +of a smart contract address, if such contract exists. The call fails +with the message `"bad address for get_contract"` if no such smart +contract exists. Note: The address of an implicit account has type +`contract`. -val read_ticket : 'a.'a ticket -> (address * 'a * nat * 'a ticket) +val get_entrypoint_opt : 'param.string -> address -> 'param contract option -let read_ticket: <a>(_: ticket<a>) => [[address, [a, nat]], ticket<a>] +get_entrypoint_opt: <param>(entrypoint: string, addr: address) => option<contract<param>> -**Deprecated:** In a future version, `Tezos` will be replaced by `Tezos.Next`, and using `Ticket.read` from `Tezos.Next` is encouraged for a smoother migration. -The call `Tezos.read_ticket t` returns `t` itself and the contents of - `t` which is a pair `(address, (value, amount))`, where `address` is - the address of the smart contract that created it. +The call `Tezos.get_entrypoint_opt entrypoint addr` has the same +behaviour as `Tezos.get_contract_opt addr`, with the additional +constraint that the contract must have an entrypoint named +`entrypoint`. In other words, `Tezos.get_entrypoint_opt entrypoint +addr` casts the address `addr` into that of a smart contract address, +if such contract exists and has an entrypoint named `entrypoint`. The +value of the call is `None` if no such smart contract exists, +otherwise `Some contract`, where `contract` is the smart contract's +address. Note: The address of an implicit account has type `unit +contract`. -The call `Tezos.read_ticket(t)` returns `t` itself and the contents of - `t` which is a pair `[address, [value, amount]]`, where `address` is - the address of the smart contract that created it. +The call `get_entrypoint_opt(entrypoint, addr)` has the same +behaviour as `get_contract_opt(addr)`, with the additional +constraint that the contract must have an entrypoint named +`entrypoint`. In other words, `get_entrypoint_opt(entrypoint, +addr)` casts the address `addr` into that of a smart contract +address, if such contract exists and has an entrypoint named +`entrypoint`. The value of the call is `["None" as "None"]` if +no such smart contract exists, otherwise `["Some" as "Some", +contract]`, where `contract` is the smart contract's +address. Note: The address of an implicit account has type +`contract`. -val sapling_empty_state : 'sap_t.'sap_t sapling_state +val get_entrypoint : 'param.string -> address -> 'param contract -let sapling_empty_state: <sap_t>sapling_state<sap_t> +get_entrypoint: <param>(entrypoint: string, addr: address) => contract<param> -**Deprecated:** In a future version, `Tezos` will be replaced by `Tezos.Next`, and using `Sapling.empty_state` from `Tezos.Next` is encouraged for a smoother migration. - -The evaluation of the constant `Tezos.sapling_empty_state` is an empty - sapling state, that is, no one can spend tokens from it. - - - -val sapling_verify_update : - 'sap_a.'sap_a sapling_transaction -> 'sap_a sapling_state -> (bytes * int * 'sap_a sapling_state) option - - -let sapling_verify_update: - <sap_a>(_: sapling_transaction<sap_a>) => (_: sapling_state<sap_a>) => option<[bytes, [int, sapling_state<sap_a>]]> - -**Deprecated:** In a future version, `Tezos` will be replaced by `Tezos.Next`, and using `Sapling.verify_update` from `Tezos.Next` is encouraged for a smoother migration. -The call `Tezos.sapling_verify_update trans state`, where the - transaction `trans` can be applied to the state `state`, returns - `Some (data, (delta, new_state))`, where `data` is the bound data - (as bytes), `delta` is the difference between the outputs and the - inputs of the transaction, and `new_state` is the updated - state. +The call `get_entrypoint entrypoint addr` casts the address `addr` +into that of a smart contract address, if such contract exists and has +an entrypoint named `entrypoint`. If no such smart contract exists, +the execution fails with the error message `"bad address for +get_entrypoint"`. Note: The address of an implicit account has type +`unit contract`. -The call `Tezos.sapling_verify_update(trans, state)`, where the - transaction `trans` can be applied to the state `state`, returns - `Some ([data, [delta, new_state]])`, where `data` is the bound data - (as bytes), `delta` is the difference between the outputs and the - inputs of the transaction, and `new_state` is the updated - state. +The call `get_entrypoint(entrypoint, addr)` casts the address `addr` +into that of a smart contract address, if such contract exists and has +an entrypoint named `entrypoint`. If no such smart contract exists, +the execution fails with the error message `"bad address for +get_entrypoint"`. Note: The address of an implicit account has type +`contract`. -val emit : 'event_type.string -> 'event_type -> operation +val open_chest : chest_key -> chest -> nat -> bytes option -let emit: <event_type>(_: string) => (_: event_type) => operation +open_chest: (key: chest_key, _: chest, time: nat) => option<bytes> -**Deprecated:** In a future version, `Tezos` will be replaced by `Tezos.Next`, and using `Operation.emit` from `Tezos.Next` is encouraged for a smoother migration. - - - -The call `Tezos.emit event_tag event_type` evaluates in an operation - that will write an event into the transaction receipt after the - successful execution of this contract. The event is annotated by - the string `event_tag` if it is not empty. The argument - `event_type` is used only to specify the type of data attachment. - - - - -The call `Tezos.emit(event_tag, event_type)` evaluates in an operation - that will write an event into the transaction receipt after the - successful execution of this contract. The event is annotated by - the string `event_tag` if it is not empty. The argument - `event_type` is used only to specify the type of data attachment. - - +The function `open_chest` opens a timelocked chest given its key and +the time. The result is a byte option depending if the opening is +correct or not. -val open_chest : chest_key -> chest -> nat -> bytes option +val get_balance : unit -> tez -let open_chest: (_: chest_key) => (_: chest) => (_: nat) => option<bytes> +get_balance: (_: unit) => tez -The function [open_chest] opens a timelocked chest given its key - and the time. The result is a byte option depending if the opening - is correct or not. - - -val get_balance : unit -> tez - - -let get_balance: (_: unit) => tez - The call `Tezos.get_balance ()` returns the balance in mutez of the - account associated to the currently executed smart contract, - including any mutez added by the calling transaction. +account associated to the currently executed smart contract, including +any mutez added by the calling transaction. The call `Tezos.get_balance()` returns the balance in mutez of the - account associated to the currently executed smart contract, - including any mutez added by the calling transaction. +account associated to the currently executed smart contract, including +any mutez added by the calling transaction. - -val get_amount : unit -> tez +val get_amount : unit -> tez -let get_amount: (_: unit) => tez +get_amount: (_: unit) => tez + The call `Tezos.get_amount ()` returns the amount in mutez of the - current transaction. +current transaction. The call `Tezos.get_amount()` returns the amount in mutez of the - current transaction. +current transaction. -val get_now : unit -> timestamp +val get_now : unit -> timestamp -let get_now: (_: unit) => timestamp +get_now: (_: unit) => timestamp + The call `Tezos.get_now ()` returns the minimal injection time for the - current block, namely the block whose application triggered this - execution. The minimal injection time constitutes an estimate of - the moment when the current block is injected, hence the name - "now". +current block, namely the block whose application triggered this +execution. The minimal injection time constitutes an estimate of the +moment when the current block is injected, hence the name "now". The call `Tezos.get_now()` returns the minimal injection time for the - current block, namely the block whose application triggered this - execution. The minimal injection time constitutes an estimate of - the moment when the current block is injected, hence the name - "now". +current block, namely the block whose application triggered this +execution. The minimal injection time constitutes an estimate of the +moment when the current block is injected, hence the name "now". -val get_min_block_time : unit -> nat +val get_min_block_time : unit -> nat -let get_min_block_time: (_: unit) => nat +get_min_block_time: (_: unit) => nat The call `Tezos.get_min_block_time ()` returns the minimal delay - between two consecutive blocks in the chain. +between two consecutive blocks in the chain. The call `Tezos.get_min_block_time()` returns the minimal delay - between two consecutive blocks in the chain. +between two consecutive blocks in the chain. -val get_level : unit -> nat +val get_level : unit -> nat -let get_level: (_: unit) => nat +get_level: (_: unit) => nat + The call `Tezos.get_level ()` returns the current block level. @@ -769,76 +460,76 @@ The call `Tezos.get_level()` returns the current block level. -val get_chain_id : unit -> chain_id +val get_chain_id : unit -> chain_id -let get_chain_id: (_: unit) => chain_id +get_chain_id: (_: unit) => chain_id + The call `Tezos.get_chain_id ()` returns the identifier of the chain - on which the smart contract is executed. +on which the smart contract is executed. The call `Tezos.get_chain_id ()` returns the identifier of the chain - on which the smart contract is executed. +on which the smart contract is executed. -val get_total_voting_power : unit -> nat +val get_total_voting_power : unit -> nat -let get_total_voting_power: (_: unit) => nat +get_total_voting_power: (_: unit) => nat + The call `Tezos.get_total_voting_power ()` returns the total voting - power of all contracts. The total voting power coincides with the - sum of the stake of every contract in the voting listings. The - voting listings is calculated at the beginning of every voting - period. +power of all contracts. The total voting power coincides with the sum +of the stake of every contract in the voting listings. The voting +listings is calculated at the beginning of every voting period. The call `Tezos.get_total_voting_power()` returns the total voting - power of all contracts. The total voting power coincides with the - sum of the stake of every contract in the voting listings. The - voting listings is calculated at the beginning of every voting - period. +power of all contracts. The total voting power coincides with the sum +of the stake of every contract in the voting listings. The voting +listings is calculated at the beginning of every voting period. -val voting_power : key_hash -> nat +val voting_power : key_hash -> nat -let voting_power: (_: key_hash) => nat +voting_power: (_: key_hash) => nat The call `Tezos.voting_power contract_kh` returns the voting power of - a given contract specified by the key hash `contract_kh`. This - voting power coincides with the weight of the contract in the - voting listings (that is, the stake) which is calculated at the - beginning of every voting period. +a given contract specified by the key hash `contract_kh`. This voting +power coincides with the weight of the contract in the voting listings +(that is, the stake) which is calculated at the beginning of every +voting period. The call `Tezos.voting_power(contract_kh)` returns the voting power of - a given contract specified by the key hash `contract_kh`. This - voting power coincides with the weight of the contract in the - voting listings (that is, the stake) which is calculated at the - beginning of every voting period. +a given contract specified by the key hash `contract_kh`. This voting +power coincides with the weight of the contract in the voting listings +(that is, the stake) which is calculated at the beginning of every +voting period. @@ -847,50 +538,49 @@ The call `Tezos.voting_power(contract_kh)` returns the voting power of val never : 'a.never -> 'a -let never: <a>(_: never) => a +never: <a>(_: never) => a The call `Tezos.never n` is never meant to be executed, as the type - `never` is inhabited, but to instruct the typechecker that a - branch in the control flow, for example, in a pattern matching, is - dead. +`never` is inhabited, but to instruct the typechecker that a branch in +the control flow, for example, in a pattern matching, is dead. The call `Tezos.never(n)` is never meant to be executed, as the type - `never` is inhabited, but to instruct the typechecker that a - branch in the control flow, for example, in a pattern matching, is - dead. +`never` is inhabited, but to instruct the typechecker that a branch in +the control flow, for example, in a pattern matching, is dead. -val pairing_check : (bls12_381_g1 * bls12_381_g2) list -> bool +val pairing_check : (bls12_381_g1 * bls12_381_g2) list -> bool -let pairing_check: (_: list<[bls12_381_g1, bls12_381_g2]>) => bool +pairing_check: (_: list<[bls12_381_g1, bls12_381_g2]>) => bool + The call `Tezos.pairing_check pairings` verifies that the product of - pairings of the given list of points `pairings` is equal to 1 in - the field Fq12. It evaluates in `true` if the list is empty. This - function can be used to verify if two pairings P1 and P2 are equal - by verifying P1 * P2^(-1) = 1. +pairings of the given list of points `pairings` is equal to 1 in the +field Fq12. It evaluates in `true` if the list is empty. This function +can be used to verify if two pairings P1 and P2 are equal by verifying +P1 * P2^(-1) = 1. The call `Tezos.pairing_check(pairings)` verifies that the product of - pairings of the given list of points `pairings` is equal to 1 in - the field Fq12. It evaluates in `true` if the list is empty. This - function can be used to verify if two pairings P1 and P2 are equal - by verifying P1 * P2^(-1) = 1. +pairings of the given list of points `pairings` is equal to 1 in the +field Fq12. It evaluates in `true` if the list is empty. This function +can be used to verify if two pairings P1 and P2 are equal by verifying +P1 * P2^(-1) = 1. @@ -899,24 +589,23 @@ The call `Tezos.pairing_check(pairings)` verifies that the product of val constant : 'a.string -> 'a -let constant: <a>(_: string) => a +constant: <a>(hash: string) => a + The call to `Tezos.constant hash` returns the value stored on-chain - whose hash value is `hash` (global constants). This call can fail - when the contract is originated if the hash is invalid or the - expansion of the global constant is ill-typed, or too large (gas - consumption). +whose hash value is `hash` (global constants). This call can fail when +the contract is originated if the hash is invalid or the expansion of +the global constant is ill-typed, or too large (gas consumption). - + The call to `Tezos.constant(hash)` returns the value stored on-chain - whose hash value is `hash` (global constants). This call can fail - when the contract is originated if the hash is invalid or the - expansion of the global constant is ill-typed, or too large (gas - consumption). +whose hash value is `hash` (global constants). This call can fail when +the contract is originated if the hash is invalid or the expansion of +the global constant is ill-typed, or too large (gas consumption). diff --git a/gitlab-pages/docs/reference/tezos.next.md b/gitlab-pages/docs/reference/tezos.next.md deleted file mode 100644 index b7c51f38a0..0000000000 --- a/gitlab-pages/docs/reference/tezos.next.md +++ /dev/null @@ -1,607 +0,0 @@ ---- -id: tezos.next-reference -title: next -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - -[module operation](tezos.next.operation.md) - - -[module view](tezos.next.view.md) - - -[module ticket](tezos.next.ticket.md) - - -[module sapling](tezos.next.sapling.md) - - - -val get_sender : unit -> address - - -let get_sender: (_: unit) => address - - - -The call `get_sender ()` is the address of the contract (that - is, a smart contract or an implicit account) that initiated the - current internal transaction. Note that, if transactions have been - chained, that address could be different from `get_source ()`. - - - - - -The call `get_sender()` is the address of the contract (that - is, a smart contract or an implicit account) that initiated the - current internal transaction. Note that, if transactions have been - chained, that address could be different from `get_source()`. - - - - - -val get_source : unit -> address - - -let get_source: (_: unit) => address - - - -The call `get_source ()` is the address of the implicit account - that initiated the current transaction. If transactions have been - chained, that address is different from `get_sender ()`. - - - - - -The call `get_source()` is the address of the implicit account - that initiated the current transaction. If transactions have been - chained, that address is different from `get_sender()`. - - - - - -val self : 'a.string -> 'a contract - - -let self: <a>(_: string) => contract<a> - - - -The call `self entrypoint` is the address of the current smart - contract, that is, the smart contract containing the call. For the - address of the smart contract actually *executing* the call, - because it is embedded in a lambda sent to another smart contract, - use `get_self_address` instead. The string `entrypoint` is the - name of a valid entrypoint such that `entrypoint` is not - `"default"`, or the empty string denoting the `"default"` - entrypoint (which is the root of the smart contract parameter if - no `"default"` entrypoint is explicitly defined). If the contract - does not have the specified entrypoint, the call results in an - type checking error. - - - - - -The call `self(entrypoint)` is the address of the current smart - contract, that is, the smart contract containing the call. For the - address of the smart contract actually *executing* the call, - because it is embedded in a lambda sent to another smart contract, - use `get_self_address` instead. The string `entrypoint` is the - name of a valid entrypoint such that `entrypoint` is not - `"default"`, or the empty string denoting the `"default"` - entrypoint (which is the root of the smart contract parameter if - no `"default"` entrypoint is explicitly defined). If the contract - does not have the specified entrypoint, the call results in an - type checking error. - - - - - -val get_self_address : unit -> address - - -let get_self_address: (_: unit) => address - - - -The call `get_self_address ()` is the address of the smart - contract actually executing the call, as a value of type - `address`. That contract can be different from the one containing - the call if the call is in a lambda transmitted to another smart - contract. Therefore, it is assumed that, in general, the type of - the executing contract is statically unknown, so the return type - of `get_self_address` is not `'a contract`, but `address`. (See - `self`.) - - - - - -The call `get_self_address()` is the address of the smart - contract actually executing the call, as a value of type - `address`. That contract can be different from the one containing - the call if the call is in a lambda transmitted to another smart - contract. Therefore, it is assumed that, in general, the type of - the executing contract is statically unknown, so the return type - of `get_self_address` is not `'a contract`, but `address`. (See - `self`.) - - - - - -val address : 'a.'a contract -> address - - -let address: <a>(_: contract<a>) => address - - - -The call `address contract` casts the address of the smart - contract `contract` into the more general value of type - `address`. - - - - - -The call `address(contract)` casts the address of the smart - contract `contract` into the more general value of type - `address`. - - - - - -val implicit_account : key_hash -> unit contract - - -let implicit_account: (_: key_hash) => contract<unit> - - - -The call `implicit_account kh` casts the public key hash `kh` - into the address of its implicit account. Note that addresses of - implicit accounts always have the type `unit contract`. - - - - - -The call `implicit_account(kh)` casts the public key hash `kh` - into the address of its implicit account. Note that addresses of - implicit accounts always have the type `contract`. - - - - - -val get_contract_opt : 'param.address -> 'param contract option - - -let get_contract_opt: <param>(_: address) => option<contract<param>> - - - -The call `get_contract_opt addr` casts the address `addr` into - that of a contract address, if such contract exists. The value of - the call is `None` if no such contract exists, otherwise `Some - contract`, where `contract` is the contract's address. Note: The - address of an implicit account has type `unit contract`. - - - - - -The call `get_contract_opt(addr)` casts the address `addr` into - that of a contract address, if such contract exists. The value of - the call is `None()` if no such contract exists, otherwise `Some - contract`, where `contract` is the contract's address. Note: The - address of an implicit account has type `unit contract`. - - - - - -val get_contract_with_error : 'param.address -> string -> 'param contract - - -let get_contract_with_error: <param>(_: address) => (_: string) => contract<param> - - - -The call `get_contract_with_error addr error` casts the address - `addr` into that of a contract address, if such contract - exists. If not, the execution fails with the error message - `error`. - - - - - -The call `get_contract_with_error(addr, error)` casts the address - `addr` into that of a contract address, if such contract - exists. If not, the execution fails with the error message - `error`. - - - - - -val get_contract : 'param.address -> 'param contract - - -let get_contract: <param>(_: address) => contract<param> - - - -The call `get_contract addr` casts the address `addr` into that - of a smart contract address, if such contract exists. The call - fails with the message `"bad address for get_contract"` if no - such smart contract exists. Note: The address of an implicit - account has type `unit contract`. - - - - - -The call `get_contract(addr)` casts the address `addr` into that - of a smart contract address, if such contract exists. The call - fails with the message `"bad address for get_contract"` if no - such smart contract exists. Note: The address of an implicit - account has type `contract`. - - - - - -val get_entrypoint_opt : 'param.string -> address -> 'param contract option - - -let get_entrypoint_opt: <param>(_: string) => (_: address) => option<contract<param>> - - - -The call `get_entrypoint_opt entrypoint addr` has the same - behaviour as `get_contract_opt addr`, with the additional - constraint that the contract must have an entrypoint named - `entrypoint`. In other words, `get_entrypoint_opt entrypoint addr` - casts the address `addr` into that of a smart contract - address, if such contract exists and has an entrypoint named - `entrypoint`. The value of the call is `None` if no such smart - contract exists, otherwise `Some contract`, where `contract` is - the smart contract's address. Note: The address of an implicit - account has type `unit contract`. - - - - - -The call `get_entrypoint_opt(entrypoint, addr)` has the same - behaviour as `get_contract_opt(addr)`, with the additional - constraint that the contract must have an entrypoint named - `entrypoint`. In other words, `get_entrypoint_opt(entrypoint, addr)` - casts the address `addr` into that of a smart contract - address, if such contract exists and has an entrypoint named - `entrypoint`. The value of the call is `None()` if no such smart - contract exists, otherwise `Some(contract)`, where `contract` is - the smart contract's address. Note: The address of an implicit - account has type `contract`. - - - - - -val get_entrypoint : 'param.string -> address -> 'param contract - - -let get_entrypoint: <param>(_: string) => (_: address) => contract<param> - - - -The call `get_entrypoint entrypoint addr` casts the address - `addr` into that of a smart contract address, if such contract - exists and has an entrypoint named `entrypoint`. If no such smart - contract exists, the execution fails with the error message - `"bad address for get_entrypoint"`. Note: The address of an implicit - account has type `unit contract`. - - - - - -The call `get_entrypoint(entrypoint, addr)` casts the address - `addr` into that of a smart contract address, if such contract - exists and has an entrypoint named `entrypoint`. If no such smart - contract exists, the execution fails with the error message - `"bad address for get_entrypoint"`. Note: The address of an implicit - account has type `contract`. - - - - - -val open_chest : chest_key -> chest -> nat -> bytes option - - -let open_chest: (_: chest_key) => (_: chest) => (_: nat) => option<bytes> - -The function [open_chest] opens a timelocked chest given its key - and the time. The result is a byte option depending if the opening - is correct or not. - - - -val get_balance : unit -> tez - - -let get_balance: (_: unit) => tez - - - -The call `get_balance ()` returns the balance in mutez of the - account associated to the currently executed smart contract, - including any mutez added by the calling transaction. - - - - - -The call `get_balance()` returns the balance in mutez of the - account associated to the currently executed smart contract, - including any mutez added by the calling transaction. - - - - - -val get_amount : unit -> tez - - -let get_amount: (_: unit) => tez - - - -The call `get_amount ()` returns the amount in mutez of the - current transaction. - - - - - -The call `get_amount()` returns the amount in mutez of the - current transaction. - - - - - -val get_now : unit -> timestamp - - -let get_now: (_: unit) => timestamp - - - -The call `get_now ()` returns the minimal injection time for the - current block, namely the block whose application triggered this - execution. The minimal injection time constitutes an estimate of - the moment when the current block is injected, hence the name - "now". - - - - - -The call `get_now()` returns the minimal injection time for the - current block, namely the block whose application triggered this - execution. The minimal injection time constitutes an estimate of - the moment when the current block is injected, hence the name - "now". - - - - - -val get_min_block_time : unit -> nat - - -let get_min_block_time: (_: unit) => nat - - - -The call `get_min_block_time ()` returns the minimal delay - between two consecutive blocks in the chain. - - - - - -The call `get_min_block_time()` returns the minimal delay - between two consecutive blocks in the chain. - - - - - -val get_level : unit -> nat - - -let get_level: (_: unit) => nat - - - -The call `get_level ()` returns the current block level. - - - - - -The call `get_level()` returns the current block level. - - - - - -val get_chain_id : unit -> chain_id - - -let get_chain_id: (_: unit) => chain_id - - - -The call `get_chain_id ()` returns the identifier of the chain - on which the smart contract is executed. - - - - - -The call `get_chain_id ()` returns the identifier of the chain - on which the smart contract is executed. - - - - - -val get_total_voting_power : unit -> nat - - -let get_total_voting_power: (_: unit) => nat - - - -The call `get_total_voting_power ()` returns the total voting - power of all contracts. The total voting power coincides with the - sum of the stake of every contract in the voting listings. The - voting listings is calculated at the beginning of every voting - period. - - - - - -The call `get_total_voting_power()` returns the total voting - power of all contracts. The total voting power coincides with the - sum of the stake of every contract in the voting listings. The - voting listings is calculated at the beginning of every voting - period. - - - - - -val voting_power : key_hash -> nat - - -let voting_power: (_: key_hash) => nat - - - -The call `voting_power contract_kh` returns the voting power of - a given contract specified by the key hash `contract_kh`. This - voting power coincides with the weight of the contract in the - voting listings (that is, the stake) which is calculated at the - beginning of every voting period. - - - - - -The call `voting_power(contract_kh)` returns the voting power of - a given contract specified by the key hash `contract_kh`. This - voting power coincides with the weight of the contract in the - voting listings (that is, the stake) which is calculated at the - beginning of every voting period. - - - - - -val never : 'a.never -> 'a - - -let never: <a>(_: never) => a - - - -The call `never n` is never meant to be executed, as the type - `never` is inhabited, but to instruct the typechecker that a - branch in the control flow, for example, in a pattern matching, is - dead. - - - - - -The call `never(n)` is never meant to be executed, as the type - `never` is inhabited, but to instruct the typechecker that a - branch in the control flow, for example, in a pattern matching, is - dead. - - - - - -val pairing_check : (bls12_381_g1 * bls12_381_g2) list -> bool - - -let pairing_check: (_: list<[bls12_381_g1, bls12_381_g2]>) => bool - - - -The call `pairing_check pairings` verifies that the product of - pairings of the given list of points `pairings` is equal to 1 in - the field Fq12. It evaluates in `true` if the list is empty. This - function can be used to verify if two pairings P1 and P2 are equal - by verifying P1 * P2^(-1) = 1. - - - - - -The call `pairing_check(pairings)` verifies that the product of - pairings of the given list of points `pairings` is equal to 1 in - the field Fq12. It evaluates in `true` if the list is empty. This - function can be used to verify if two pairings P1 and P2 are equal - by verifying P1 * P2^(-1) = 1. - - - - - -val constant : 'a.string -> 'a - - -let constant: <a>(_: string) => a - - - -The call to `constant hash` returns the value stored on-chain - whose hash value is `hash` (global constants). This call can fail - when the contract is originated if the hash is invalid or the - expansion of the global constant is ill-typed, or too large (gas - consumption). - - - - - -The call to `constant(hash)` returns the value stored on-chain - whose hash value is `hash` (global constants). This call can fail - when the contract is originated if the hash is invalid or the - expansion of the global constant is ill-typed, or too large (gas - consumption). - - diff --git a/gitlab-pages/docs/reference/tezos.next.operation.md b/gitlab-pages/docs/reference/tezos.next.operation.md deleted file mode 100644 index 3d6aa5b623..0000000000 --- a/gitlab-pages/docs/reference/tezos.next.operation.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -id: tezos.next.operation-reference -title: operation -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val create_contract : - 'param - 'storage.('param, 'storage) entrypoint -> key_hash option -> tez -> 'storage -> (operation * address) - - -let create_contract: - <param, storage>(_: entrypoint<param, storage>) => (_: option<key_hash>) => (_: tez) => (_: storage) => [ - operation, - address - ] - - - -The call `create_contract e d a s` returns a contract creation - operation (origination) for the entrypoint `e` (as a function) - with optional delegate `d`, initial amount `a` and initial - storage `s`, together with the address of the created - contract. Note that the created contract cannot be called - immediately afterwards (that is, `get_contract_opt` on that - address would return `None`), as the origination must be - performed successfully first, for example by calling a proxy - contract or itself. - - - - - -The call `create_contract(e,d,a,s)` returns a contract creation - operation (origination) for the entrypoint `e` (as a function) - with optional delegate `d`, initial amount `a` and initial - storage `s`, together with the address of the created - contract. Note that the created contract cannot be called - immediately afterwards (that is, `get_contract_opt` on that - address would return `None()`), as the origination must be - performed successfully first, for example by calling a proxy - contract or itself. - - - - - -val set_delegate : key_hash option -> operation - - -let set_delegate: (_: option<key_hash>) => operation - - - -The call `set_delegate d` evaluates in an operation that sets - the delegate of the current smart contract to be `d`, where `d` is - an optional key hash. If `None`, the delegation is withdrawn. If - the contract has no delegation, then no change occurs. If `d` is - `Some kh`, where `kh` is the key hash of a registered delegate - that is not the current delegate of the contract, then this - operation sets the delegate of the contract to this registered - delegate. A failure occurs if `kh` is the current delegate of the - contract or if `kh` is not a registered delegate. However, the - instruction in itself does not fail; it produces an operation that - will fail when applied. - - - - - -The call `set_delegate(d)` evaluates in an operation that sets - the delegate of the current smart contract to be `d`, where `d` is - an optional key hash. If `None()`, the delegation is withdrawn. If - the contract has no delegation, then no change occurs. If `d` is - `Some(kh)`, where `kh` is the key hash of a registered delegate - that is not the current delegate of the contract, then this - operation sets the delegate of the contract to this registered - delegate. A failure occurs if `kh` is the current delegate of the - contract or if `kh` is not a registered delegate. However, the - instruction in itself does not fail; it produces an operation that - will fail when applied. - - - - - -val transaction : 'param.'param -> tez -> 'param contract -> operation - - -let transaction: <param>(_: param) => (_: tez) => (_: contract<param>) => operation - - - -The call `transaction param amount contract_addr` evaluates in - an operation that will send the amount `amount` in mutez to the - contract at the valid address `contract_addr`, with parameter - `param`. If the contract is an implicit account, the parameter - must be `unit`. - - - - - -The call `transaction(param, amount, contract_addr)` evaluates in - an operation that will send the amount `amount` in mutez to the - contract at the valid address `contract_addr`, with parameter - `param`. If the contract is an implicit account, the parameter - must be `unit`. - - - - - -val emit : 'event_type.string -> 'event_type -> operation - - -let emit: <event_type>(_: string) => (_: event_type) => operation - - - -The call `emit event_tag event_type` evaluates in an operation - that will write an event into the transaction receipt after the - successful execution of this contract. The event is annotated by - the string `event_tag` if it is not empty. The argument - `event_type` is used only to specify the type of data attachment. - - - - - -The call `emit event_tag(event_type)` evaluates in an operation - that will write an event into the transaction receipt after the - successful execution of this contract. The event is annotated by - the string `event_tag` if it is not empty. The argument - `event_type` is used only to specify the type of data attachment. - - diff --git a/gitlab-pages/docs/reference/tezos.next.sapling.md b/gitlab-pages/docs/reference/tezos.next.sapling.md deleted file mode 100644 index a55a7c6753..0000000000 --- a/gitlab-pages/docs/reference/tezos.next.sapling.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -id: tezos.next.sapling-reference -title: sapling -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val empty_state : 'sap_t.'sap_t sapling_state - - -let empty_state: <sap_t>sapling_state<sap_t> - -The evaluation of the constant `empty_state` is an empty - sapling state, that is, no one can spend tokens from it. - - - -val verify_update : - 'sap_a.'sap_a sapling_transaction -> 'sap_a sapling_state -> (bytes * int * 'sap_a sapling_state) option - - -let verify_update: - <sap_a>(_: sapling_transaction<sap_a>) => (_: sapling_state<sap_a>) => option<[bytes, [int, sapling_state<sap_a>]]> - - - -The call `verify_update trans state`, where the - transaction `trans` can be applied to the state `state`, returns - `Some (data, (delta, new_state))`, where `data` is the bound data - (as bytes), `delta` is the difference between the outputs and the - inputs of the transaction, and `new_state` is the updated - state. - - - - - -The call `verify_update(trans, state)`, where the - transaction `trans` can be applied to the state `state`, returns - `Some ([data, [delta, new_state]])`, where `data` is the bound data - (as bytes), `delta` is the difference between the outputs and the - inputs of the transaction, and `new_state` is the updated - state. - - diff --git a/gitlab-pages/docs/reference/tezos.next.ticket.md b/gitlab-pages/docs/reference/tezos.next.ticket.md deleted file mode 100644 index a9e687b7a6..0000000000 --- a/gitlab-pages/docs/reference/tezos.next.ticket.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -id: tezos.next.ticket-reference -title: ticket -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val create : 'a.'a -> nat -> 'a ticket option - - -let create: <a>(_: a) => (_: nat) => option<ticket<a>> - - - -The call `create v a` creates a ticket with value `v` and - amount `a`. If the creation is a success, the value `Some t` is - returned, where `t` is the ticket; otherwise, `None` is the - result. Note: Tickets cannot be duplicated. - - - - - -The call `create(v, a)` creates a ticket with value `v` and - amount `a`. If the creation is a success, the value `Some(t)` is - returned, where `t` is the ticket; otherwise, `None()` is the - result. Note: Tickets cannot be duplicated. - - - - - -val split : 'a.'a ticket -> (nat * nat) -> ('a ticket * 'a ticket) option - - -let split: <a>(_: ticket<a>) => (_: [nat, nat]) => option<[ticket<a>, ticket<a>]> - - - -The call `split t (a1, a2)` results in a pair of tickets - `t1` and `t2` such that the former owns the amount `a1` and the - later `a2`. More precisely, the value of the call is - `Some (t1, t2)` because signifying to the callee the failure of - the splitting is achieved by returning the value `None`. - - - - - -The call `split(t, [a1, a2])` results in a pair of tickets - `t1` and `t2` such that the former owns the amount `a1` and the - later `a2`. More precisely, the value of the call is - `Some([t1, t2])` because signifying to the callee the failure of - the splitting is achieved by returning the value `None()`. - - - - - -val join : 'a.('a ticket * 'a ticket) -> 'a ticket option - - -let join: <a>(_: [ticket<a>, ticket<a>]) => option<ticket<a>> - - - -The call `join (t1, t2)` joins the tickets `t1` and - `t2`, which must have the same type of value. - - - - - -The call `join(t1, t2)` joins the tickets `t1` and - `t2`, which must have the same type of value. - - - - - -val read : 'a.'a ticket -> (address * 'a * nat * 'a ticket) - - -let read: <a>(_: ticket<a>) => [[address, [a, nat]], ticket<a>] - - - -The call `read t` returns `t` itself and the contents of - `t` which is a pair `(address, (value, amount))`, where `address` is - the address of the smart contract that created it. - - - - - -The call `read(t)` returns `t` itself and the contents of - `t` which is a pair `[address, [value, amount]]`, where `address` is - the address of the smart contract that created it. - - diff --git a/gitlab-pages/docs/reference/tezos.next.view.md b/gitlab-pages/docs/reference/tezos.next.view.md deleted file mode 100644 index f508e77ec4..0000000000 --- a/gitlab-pages/docs/reference/tezos.next.view.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -id: tezos.next.view-reference -title: view -hide_table_of_contents: true ---- -import Syntax from '@theme/Syntax'; -import SyntaxTitle from '@theme/SyntaxTitle'; - - - - -val call : 'param 'return.string -> 'param -> address -> 'return option - - -let call: <param, return>(_: string) => (_: param) => (_: address) => option<return> - - - -The call `call v p a` calls the view `v` with parameter - `param` at the contract whose address is `a`. The value returned - is `None` if the view does not exist, or has a different type of - parameter, or if the contract does not exist at that - address. Otherwise, it is `Some v`, where `v` is the return value - of the view. Note: the storage of the view is the same as when the - execution of the contract calling the view started. - - - - - -The call `call(v, p, a)` calls the view `v` with parameter - `param` at the contract whose address is `a`. The value returned - is `None()` if the view does not exist, or has a different type of - parameter, or if the contract does not exist at that - address. Otherwise, it is `Some(v)`, where `v` is the return value - of the view. Note: the storage of the view is the same as when the - execution of the contract calling the view started. - - diff --git a/gitlab-pages/docs/reference/tezos.operation.md b/gitlab-pages/docs/reference/tezos.operation.md new file mode 100644 index 0000000000..bf9d7218ee --- /dev/null +++ b/gitlab-pages/docs/reference/tezos.operation.md @@ -0,0 +1,144 @@ +--- +id: tezos.operation-reference +title: operation +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + + + +val create_contract : + 'param + 'storage.('param, 'storage) entrypoint -> key_hash option -> tez -> 'storage -> (operation * address) + + +create_contract: + <param, storage>(entrypoint: entrypoint<param, storage>, + delegate: option<key_hash>, amount: tez, _: storage) => [ + operation, + address + ] + + + + +The call `create_contract e d a s` returns a contract creation +operation (origination) for the entrypoint `e` (as a function) with +optional delegate `d`, initial amount `a` and initial storage `s`, +together with the address of the created contract. Note that the +created contract cannot be called immediately afterwards (that is, +`get_contract_opt` on that address would return `None`), as the +origination must be performed successfully first, for example by +calling a proxy contract or itself. + + + + + +The call `create_contract(e,d,a,s)` returns a contract +creation operation (origination) for the entrypoint `e` (as a +function) with optional delegate `d`, initial amount `a` and +initial storage `s`, together with the address of the created +contract. Note that the created contract cannot be called +immediately afterwards (that is, `get_contract_opt` on that +address would return `["None" as "None"]`), as the origination +must be performed successfully first, for example by calling a +proxy contract or itself. + + + + + +val set_delegate : key_hash option -> operation + + +set_delegate: (delegate: option<key_hash>) => operation + + + + +The call `set_delegate d` evaluates in an operation that sets +the delegate of the current smart contract to be `d`, where `d` is +an optional key hash. If `None`, the delegation is withdrawn. If +the contract has no delegation, then no change occurs. If `d` is +`Some kh`, where `kh` is the key hash of a registered delegate +that is not the current delegate of the contract, then this +operation sets the delegate of the contract to this registered +delegate. A failure occurs if `kh` is the current delegate of the +contract or if `kh` is not a registered delegate. However, the +instruction in itself does not fail; it produces an operation that +will fail when applied. + + + + + +The call `set_delegate(d)` evaluates in an operation that sets the +delegate of the current smart contract to be `d`, where `d` is an +optional key hash. If `["None" as "None"]`, the delegation is +withdrawn. If the contract has no delegation, then no change +occurs. If `d` is `["Some" as "Some", kh]`, where `kh` is the key hash +of a registered delegate that is not the current delegate of the +contract, then this operation sets the delegate of the contract to +this registered delegate. A failure occurs if `kh` is the current +delegate of the contract or if `kh` is not a registered +delegate. However, the instruction in itself does not fail; it +produces an operation that will fail when applied. + + + + + +val transaction : 'param.'param -> tez -> 'param contract -> operation + + +transaction: <param>(_: param, amount: tez, contract_addr: contract<param>) => operation + + + + +The call `transaction param amount contract_addr` evaluates in an +operation that will send the amount `amount` in mutez to the contract +at the valid address `contract_addr`, with parameter `param`. If the +contract is an implicit account, the parameter must be `unit`. + + + + + +The call `transaction(param, amount, contract_addr)` evaluates in an +operation that will send the amount `amount` in mutez to the contract +at the valid address `contract_addr`, with parameter `param`. If the +contract is an implicit account, the parameter must be `unit`. + + + + + +val emit : 'event_type.string -> 'event_type -> operation + + +emit: <event_type>(even_tag: string, _: event_type) => operation + + + + +The call `emit event_tag event_type` evaluates in an operation that +will write an event into the transaction receipt after the successful +execution of this contract. The event is annotated by the string +`event_tag` if it is not empty. The argument `event_type` is used only +to specify the type of data attachment. + + + + + +The call `emit event_tag(event_type)` evaluates in an operation that +will write an event into the transaction receipt after the successful +execution of this contract. The event is annotated by the string +`event_tag` if it is not empty. The argument `event_type` is used only +to specify the type of data attachment. + + diff --git a/gitlab-pages/docs/reference/tezos.sapling.md b/gitlab-pages/docs/reference/tezos.sapling.md new file mode 100644 index 0000000000..e8ef603ece --- /dev/null +++ b/gitlab-pages/docs/reference/tezos.sapling.md @@ -0,0 +1,50 @@ +--- +id: tezos.sapling-reference +title: sapling +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + + + +val empty_state : 'sap_t.'sap_t sapling_state + + +empty_state: <sap_t>sapling_state<sap_t> + + +The evaluation of the constant `empty_state` is an empty +sapling state, that is, no one can spend tokens from it. + + + +val verify_update : + 'sap_a.'sap_a sapling_transaction -> 'sap_a sapling_state -> (bytes * int * 'sap_a sapling_state) option + + +verify_update: + <sap_a>(trans: sapling_transaction<sap_a>, state: sapling_state<sap_a>) => option<[bytes, [int, sapling_state<sap_a>]]> + + + + +The call `verify_update trans state`, where the +transaction `trans` can be applied to the state `state`, returns +`Some (data, (delta, new_state))`, where `data` is the bound data +(as bytes), `delta` is the difference between the outputs and the +inputs of the transaction, and `new_state` is the updated state. + + + + + +The call `verify_update(trans, state)`, where the transaction +`trans` can be applied to the state `state`, returns `["Some" +as "Some", [data, [delta, new_state]]]`, where `data` is the +bound data (as bytes), `delta` is the difference between the +outputs and the inputs of the transaction, and `new_state` is +the updated state. + + diff --git a/gitlab-pages/docs/reference/tezos.ticket.md b/gitlab-pages/docs/reference/tezos.ticket.md new file mode 100644 index 0000000000..2e5fa58860 --- /dev/null +++ b/gitlab-pages/docs/reference/tezos.ticket.md @@ -0,0 +1,110 @@ +--- +id: tezos.ticket-reference +title: ticket +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + + + +val create : 'a.'a -> nat -> 'a ticket option + + +create: <a>(value: a, amount: nat) => option<ticket<a>> + + + + +The call `create v a` creates a ticket with value `v` and amount +`a`. If the creation is a success, the value `Some t` is returned, +where `t` is the ticket; otherwise, `None` is the result. Note: +Tickets cannot be duplicated. + + + + + +The call `create(v, a)` creates a ticket with value `v` and +amount `a`. If the creation is a success, the value +`["Some" as "Some", t]` is returned, where `t` is the ticket; +otherwise, `["None" as "None"]` is the result. Note: Tickets +cannot be duplicated. + + + + + +val split : 'a.'a ticket -> (nat * nat) -> ('a ticket * 'a ticket) option + + +split: <a>(_: ticket<a>, amounts: [nat, nat]) => option<[ticket<a>, ticket<a>]> + + + + +The call `split t (a1, a2)` results in a pair of tickets +`t1` and `t2` such that the former owns the amount `a1` and the +latter `a2`. More precisely, the value of the call is +`Some (t1, t2)` because signifying to the callee the failure of +the splitting is achieved by returning the value `None`. + + + + + +The call `split(t, [a1, a2])` results in a pair of tickets +`t1` and `t2` such that the former owns the amount `a1` and +the latter `a2`. More precisely, the value of the call is +`["Some" as "Some", [t1, t2]]` because signifying to the +callee the failure of the splitting is achieved by returning +the value `["None" as "None"]`. + + + + + +val join : 'a.('a ticket * 'a ticket) -> 'a ticket option + + +join: <a>(_: [ticket<a>, ticket<a>]) => option<ticket<a>> + + + + +The call `join (t1, t2)` joins the tickets `t1` and +`t2`, which must have the same type of value. + + + + + +The call `join(t1, t2)` joins the tickets `t1` and +`t2`, which must have the same type of value. + + + + + +val read : 'a.'a ticket -> (address * 'a * nat * 'a ticket) + + +read: <a>(_: ticket<a>) => [[address, [a, nat]], ticket<a>] + + + + +The call `read t` returns `t` itself and the contents of +`t` which is a pair `(address, (value, amount))`, where `address` is +the address of the smart contract that created it. + + + + + +The call `read(t)` returns `t` itself and the contents of +`t` which is a pair `[address, [value, amount]]`, where `address` is +the address of the smart contract that created it. + + diff --git a/gitlab-pages/docs/reference/tezos.view.md b/gitlab-pages/docs/reference/tezos.view.md new file mode 100644 index 0000000000..4f55610893 --- /dev/null +++ b/gitlab-pages/docs/reference/tezos.view.md @@ -0,0 +1,40 @@ +--- +id: tezos.view-reference +title: view +hide_table_of_contents: true +--- +import Syntax from '@theme/Syntax'; +import SyntaxTitle from '@theme/SyntaxTitle'; + + + +val call : 'param 'return.string -> 'param -> address -> 'return option + + +call: <param, return>(view: string, param: param, addr: address) => option<return> + + + + +The call `Tezos.View.call v p a` calls the view `v` with parameter +`param` at the contract whose address is `a`. The value returned is +`None` if the view does not exist, or has a different type of +parameter, or if the contract does not exist at that +address. Otherwise, it is `Some v`, where `v` is the return value of +the view. Note: the storage of the view is the same as when the +execution of the contract calling the view started. + + + + + +The call `Tezos.View.call(v, p, a)` calls the view `v` with +parameter `param` at the contract whose address is `a`. The +value returned is `["None" as "None"]` if the view does not +exist, or has a different type of parameter, or if the contract +does not exist at that address. Otherwise, it is `["Some" as "Some", v]`, +where `v` is the return value of the view. Note: the storage of +the view is the same as when the execution of the contract +calling the view started. + + diff --git a/gitlab-pages/docs/reference/toplevel.md b/gitlab-pages/docs/reference/toplevel.md index 818016830e..68ce9bfdbe 100644 --- a/gitlab-pages/docs/reference/toplevel.md +++ b/gitlab-pages/docs/reference/toplevel.md @@ -6,8 +6,6 @@ hide_table_of_contents: true import Syntax from '@theme/Syntax'; import SyntaxTitle from '@theme/SyntaxTitle'; - - type string @@ -341,7 +339,7 @@ Type of optional values. They are useful, for example, when a val unit : unit -let unit: unit +unit: unit Unit type. It is useful for typing side-effects, for example failures, some iterators and implicit accounts. @@ -351,108 +349,44 @@ Unit type. It is useful for typing side-effects, for example val ignore : 'a.'a -> unit -let ignore: <a>(_: a) => unit +ignore: <a>(_: a) => unit The call `ignore v` evaluates `v` and ignores its value, returning - instead the unit value. This is useful when the argument of `ignore` - performs side-effects. +instead the unit value. This is useful when the argument of `ignore` +performs side-effects. The call `ignore(v)` evaluates `v` and ignores its value, returning - instead the unit value. This is useful when the argument of `ignore` - performs side-effects. - - - - - -val curry : 'a 'b 'c.(('a * 'b) -> 'c) -> 'a -> 'b -> 'c - - -let curry: <a, b, c>(_: (_: [a, b]) => c) => (_: a) => (_: b) => c - -**Deprecated:** In a future version, this function will be deprecated, and using `Tuple2.curry` is encouraged for a smoother migration. - - - -The call `curry f x y` has the same value as `f (x,y)`. - - - - - -The call `curry(f,x,y)` has the same value as `f(x,y)`. - - - - - -val uncurry : 'a 'b 'c.('a -> 'b -> 'c) -> ('a * 'b) -> 'c - - -let uncurry: <a, b, c>(_: (_: a) => (_: b) => c) => (_: [a, b]) => c - -**Deprecated:** In a future version, this function will be deprecated, and using `Tuple2.uncurry` is encouraged for a smoother migration. - - - -The call `uncurry f (x,y)` has the same value as `f x y`. +instead the unit value. This is useful when the argument of `ignore` +performs side-effects. - - -The call `uncurry(f,[x,y])` has the same value as `f(x)(y)`. - - - - - -val fst : 'a 'b.('a * 'b) -> 'a - - -let fst: <a, b>(_: [a, b]) => a - -**Deprecated:** In a future version, this function will be deprecated, and using `Tuple2.get1` is encouraged for a smoother migration. - -Projecting the first component of a pair - - - -val snd : 'a 'b.('a * 'b) -> 'b - - -let snd: <a, b>(_: [a, b]) => b - -**Deprecated:** In a future version, this function will be deprecated, and using `Tuple2.get2` is encouraged for a smoother migration. - -Projecting the second component of a pair. - - val failwith : 'err 'a.'err -> 'a -let failwith: <err, a>(_: err) => a +failwith: <err, a>(_: err) => a + The call `failwith e` terminates the execution with the value `e`, - standing for an error. Note: Using a string for an error message can - be costly in terms of size. +standing for an error. Note: Using a string for an error message can +be costly in terms of size. The call `failwith(e)` terminates the execution with the value `e`, - standing for an error. Note: Using a string for an error message can - be costly in terms of size. +standing for an error. Note: Using a string for an error message can +be costly in terms of size. @@ -461,165 +395,21 @@ The call `failwith(e)` terminates the execution with the value `e`, val bytes : 'a.'a -> 'a external_bytes -let bytes: <a>(_: a) => external_bytes<a> +bytes: <a>(_: a) => external_bytes<a> -The function `bytes` encodes an integer or a natural number to - bytes using the big-endian encoding. For integers, negative numbers - are considered in two's complement representation. - - - -val assert_with_error : bool -> string -> unit - - -let assert_with_error: (_: bool) => (_: string) => unit - -**Deprecated:** In a future version, this function will be deprecated, and using `Assert.Error.assert` is encouraged for a smoother migration. - - - -The call `assert_with_error cond error` terminates the execution - with the string `error` (that is, an error message) if, and only if, - the boolean condition `cond` is false. - - - - - -The call `assert_with_error(cond, error)` terminates the execution - with the string `error` (that is, an error message) if, and only if, - the boolean condition `cond` is false. - - - - -val assert : bool -> unit - - -let assert: (_: bool) => unit - -**Deprecated:** In a future version, this function will be deprecated, and using `Assert.assert` is encouraged for a smoother migration. - - - -The call `assert cond` terminates the execution with the string - `"failed assertion"` if, and only if, the boolean condition `cond` - is false. - - - - - -The call `assert(cond)` terminates the execution with the string - `"failed assertion"` if, and only if, the boolean condition `cond` - is false. - - - - - -val assert_some_with_error : 'a.'a option -> string -> unit - - -let assert_some_with_error: <a>(_: option<a>) => (_: string) => unit - -**Deprecated:** In a future version, this function will be deprecated, and using `Assert.Error.some` is encouraged for a smoother migration. - - - -The call `assert_some_with_error opt err` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is `None`. - - - - - -The call `assert_some_with_error(opt, err)` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is `None()`. - - - - - -val assert_some : 'a.'a option -> unit - - -let assert_some: <a>(_: option<a>) => unit - -**Deprecated:** In a future version, this function will be deprecated, and using `Assert.some` is encouraged for a smoother migration. - - - -The call `assert_some opt` terminates the execution with the - string `"failed assert some"` if, and only if, `opt` is `None`. - - - - - -The call `assert_some(opt)` terminates the execution with the - string `"failed assert some"` if, and only if, `opt` is `None()`. - - - - - -val assert_none_with_error : 'a.'a option -> string -> unit - - -let assert_none_with_error: <a>(_: option<a>) => (_: string) => unit - -**Deprecated:** In a future version, this function will be deprecated, and using `Assert.Error.none` is encouraged for a smoother migration. - - - -The call `assert_none_with_error opt err` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is an optional value different from `None`. - - - - - -The call `assert_none_with_error(opt, err)` terminates the execution - with the string `err` (that is, an error message) if, and only if, - `opt` is an optional value different from `None()`. - - - - - -val assert_none : 'a.'a option -> unit - - -let assert_none: <a>(_: option<a>) => unit - -**Deprecated:** In a future version, this function will be deprecated, and using `Assert.none` is encouraged for a smoother migration. - - - -The call `assert_none opt` terminates the execution with the string - `"failed assert none"` if, and only if, `opt` is not `None`. - - - - - -The call `assert_none(opt)` terminates the execution with the string - `"failed assert none"` if, and only if, `opt` is not `None()`. - - +The function `bytes` encodes an integer or a natural number to bytes +using the big-endian encoding. For integers, negative numbers are +considered in two's complement representation. val abs : int -> nat -let abs: (_: int) => nat +abs: (_: int) => nat + The call `abs i` is the absolute value of `i`. @@ -637,19 +427,20 @@ The call `abs(i)` is the absolute value of `i`. val is_nat : int -> nat option -let is_nat: (_: int) => option<nat> +is_nat: (_: int) => option<nat> + -The call `is_nat i` is `Some n`, where `n` is the absolute - value of `i`, if, and only if, `i` is positive or zero. +The call `is_nat i` is `Some n`, where `n` is the absolute value of +`i`, if, and only if, `i` is positive or zero. -The call `is_nat(i)` is `Some(n)`, where `n` is the absolute - value of `i`, if, and only if, `i` is positive or zero. +The call `is_nat(i)` is `["Some" as "Some", n]`, where `n` is the +absolute value of `i`, if, and only if, `i` is positive or zero. @@ -658,18 +449,19 @@ The call `is_nat(i)` is `Some(n)`, where `n` is the absolute val int : 'a.'a -> int -let int: <a>(_: a) => int +int: <a>(_: a) => int + The call `int v` casts the value `v` to an integer. - For natural numbers, the function `int` is the identity cast from - `nat` to `int`. For BLS12-381 field elements, the returned value is - always between 0 (inclusive) and the order of the BLS12-381 field - (exclusive). For bytes, the function `int` decodes the bytes using - the big-endian encoding, where negative numbers are considered in - two's complement representation. +For natural numbers, the function `int` is the identity cast from +`nat` to `int`. For BLS12-381 field elements, the returned value is +always between 0 (inclusive) and the order of the BLS12-381 field +(exclusive). For bytes, the function `int` decodes the bytes using the +big-endian encoding, where negative numbers are considered in two's +complement representation. @@ -677,12 +469,12 @@ The call `int v` casts the value `v` to an integer. The call `int(v)` casts the value `v` to an integer. - For natural numbers, the function `int` is the identity cast from - `nat` to `int`. For BLS12-381 field elements, the returned value is - always between 0 (inclusive) and the order of the BLS12-381 field - (exclusive). For bytes, the function `int` decodes the bytes using - the big-endian encoding, where negative numbers are considered in - two's complement representation. +For natural numbers, the function `int` is the identity cast from +`nat` to `int`. For BLS12-381 field elements, the returned value is +always between 0 (inclusive) and the order of the BLS12-381 field +(exclusive). For bytes, the function `int` decodes the bytes using the +big-endian encoding, where negative numbers are considered in two's +complement representation. @@ -691,8 +483,9 @@ The call `int(v)` casts the value `v` to an integer. val nat : bytes -> nat -let nat: (_: bytes) => nat +nat: (_: bytes) => nat + The call `nat b` casts the bytes `b` into a natural number. @@ -710,33 +503,34 @@ The call `nat(b)` casts the bytes `b` into a natural number. val ediv : 'a 'b.'a -> 'b -> ('a, 'b) external_ediv -let ediv: <a, b>(_: a) => (_: b) => external_ediv<a, b> +ediv: <a, b>(left: a, right: b) => external_ediv<a, b> + -The call `ediv z1 z2`, where `z1` and `z2` are either of type - `int` or `nat`, returns `None` if `z2` is zero; otherwise, it - returns the pair `(q,r)`, where `q` is the quotient and `r` the - positive remainder, as is the convention of the mathematical - Euclidian division. The function `ediv` is also overloaded to work - on values of type `tez`. When `z1` and `z2` are of type `tez` and - `z2` is nonzero, we get a `nat` quotient and a `tez` - remainder. When `z1` is a `tez` and `z2` is a nonzero `nat`, the - calls yields a quotient and a remainder both of type `tez`. +The call `ediv z1 z2`, where `z1` and `z2` are either of type `int` or +`nat`, returns `None` if `z2` is zero; otherwise, it returns the pair +`(q,r)`, where `q` is the quotient and `r` the positive remainder, as +is the convention of the mathematical Euclidian division. The function +`ediv` is also overloaded to work on values of type `tez`. When `z1` +and `z2` are of type `tez` and `z2` is nonzero, we get a `nat` +quotient and a `tez` remainder. When `z1` is a `tez` and `z2` is a +nonzero `nat`, the calls yields a quotient and a remainder both of +type `tez`. -The call `ediv(z1, z2)`, where `z1` and `z2` are either of type - `int` or `nat`, returns `None()` if `z2` is zero; otherwise, it - returns the pair `[q,r]`, where `q` is the quotient and `r` the - positive remainder, as is the convention of the mathematical - Euclidian division. The function `ediv` is also overloaded to work - on values of type `tez`. When `z1` and `z2` are of type `tez` and - `z2` is nonzero, we get a `nat` quotient and a `tez` - remainder. When `z1` is a `tez` and `z2` is a nonzero `nat`, the - calls yields a quotient and a remainder both of type `tez`. +The call `ediv(z1, z2)`, where `z1` and `z2` are either of type `int` +or `nat`, returns `None()` if `z2` is zero; otherwise, it returns the +pair `[q,r]`, where `q` is the quotient and `r` the positive +remainder, as is the convention of the mathematical Euclidian +division. The function `ediv` is also overloaded to work on values of +type `tez`. When `z1` and `z2` are of type `tez` and `z2` is nonzero, +we get a `nat` quotient and a `tez` remainder. When `z1` is a `tez` +and `z2` is a nonzero `nat`, the calls yields a quotient and a +remainder both of type `tez`. @@ -747,6 +541,7 @@ type 'elt big_set = 'elt Big_set.t type big_set<elt> = Big_set.t<elt> + The type of the big sets is based on `big_map`. diff --git a/gitlab-pages/docs/syntax/classes.md b/gitlab-pages/docs/syntax/classes.md new file mode 100644 index 0000000000..fb487d2624 --- /dev/null +++ b/gitlab-pages/docs/syntax/classes.md @@ -0,0 +1,110 @@ +--- +title: Classes +--- + +import Syntax from '@theme/Syntax'; + + + +Classes are not supported in CameLIGO. + + + + + +Classes (introduced in JsLIGO 2.0) are similar to classes in JavaScript/Typescript. +You can use them to define a smart contract or group related definitions. + +JsLIGO classes do not have all of the same features as JavaScript/TypeScript classes. +For example, JsLIGO classes cannot inherit from other classes as they can in JavaScript/TypeScript. +However, classes can implement [interfaces](./signatures). + +## Creating classes + +JsLIGO classes can contain only function definitions and value definitions, which are referred to as _properties_ when they are in a class. +Because each property is assumed to be constant, you do not prefix definitions with the `function`, `const`, or `let` keywords. +Within a class, you can also apply decorators such as `@entry` to functions without putting them in comments. + +Classes are one way to define contracts, as in this example: + +```jsligo group=classes +type storage_type = int; +type return_type = [list, storage_type]; + +class Counter { + @entry + add = (value: int, storage: storage_type): return_type => + [[], storage + value]; + + @entry + sub = (value: int, storage: storage_type): return_type => + [[], storage - value]; +} +``` + +:::tip + +Classes cannot contain type definitions. +To encapsulate classes with types, such as if you want to define a type that represents the contract storage or types that represent complex parameter types, you can group the contract class and the related types in a namespace. + +::: + +## Importing and using classes + +You can import and use definitions from classes in the same file or in different files. +For example, this version of the previous example uses functions that are in another class and types that are in another namespace: + +```jsligo group=import_classes_local +namespace MyTypes { + export type storage_type = int; + export type return_type = [list, storage_type]; +} + +class MyFunctions { + static add = (a: int, b: int): int => a + b; + static sub = (a: int, b: int): int => a - b; +} + +class Counter { + @entry + add = (value: int, storage: MyTypes.storage_type): MyTypes.return_type => + [[], MyFunctions.add(storage, value)]; + + @entry + sub = (value: int, storage: MyTypes.storage_type): MyTypes.return_type => + [[], MyFunctions.add(storage, value)]; +} +``` + +To make properties accessible outside of a class, you must mark them with the `static` keyword or the `@public` decorator. +The `static` keyword is more appropriate because external code accesses the definitions of the class in a static manner, without creating an instance of the class. + +For example, this file exports a class with static properties: + +```jsligo group=exported_class +export class MyFunctions { + static add = (a: int, b: int): int => a + b; + static sub = (a: int, b: int): int => a - b; +} +``` + +This file imports and uses the class: + +```jsligo group=imports_classes +type storage_type = int; +type return_type = [list, storage_type]; + +import * as MyImportedFunctions from "gitlab-pages/docs/syntax/src/classes/exported_class.jsligo"; + +class Calculator { + @entry + add = (value: int, storage: storage_type): return_type => + [[], MyImportedFunctions.MyFunctions.add(storage, value)]; + + @entry + sub = (value: int, storage: storage_type): return_type => + [[], MyImportedFunctions.MyFunctions.sub(storage, value)]; +} +``` + + diff --git a/gitlab-pages/docs/syntax/comments.md b/gitlab-pages/docs/syntax/comments.md index 39e6711b70..2b3c46e702 100644 --- a/gitlab-pages/docs/syntax/comments.md +++ b/gitlab-pages/docs/syntax/comments.md @@ -36,15 +36,6 @@ JsLIGO multi-line comments work in a way similar to JavaScript comments: */ ``` -Unlike in JavaScript, JsLIGO comments can be nested as long as each comment is properly closed. -Nesting comments like this allows you to comment out a large piece of code that may include other comments. - -```jsligo group=comments -/* This is a multi-line comment. - /* This is a "nested" comment. */ -*/ -``` - Both LIGO syntaxes also use single-line comments like JavaScript. diff --git a/gitlab-pages/docs/syntax/contracts/contracts.md b/gitlab-pages/docs/syntax/contracts/contracts.md index bfa7299047..3c2bc91921 100644 --- a/gitlab-pages/docs/syntax/contracts/contracts.md +++ b/gitlab-pages/docs/syntax/contracts/contracts.md @@ -10,6 +10,12 @@ For an overview of how smart contracts work on Tezos, see [An introduction to sm For the data type that represents a contract, see [Contracts](../../data-types/contracts-type). + + +You can define contracts in [classes](../classes), in [namespaces](../modules), or at the top level of a file. + + + ## Example contract This example contract stores an integer and provides two entrypoints that allow callers to add to that integer or subtract from that integer. @@ -18,24 +24,20 @@ The code includes automated tests for the contract that are not part of the cont ```cameligo group=starter_counter -module Test = Test.Next - module Counter = struct type storage_type = int type return_type = operation list * storage_type [@entry] - let add (value : int) ( store: storage_type) : return_type = + let add (value : int) (store : storage_type) : return_type = [], store + value [@entry] - let sub (value : int) ( store: storage_type) : return_type = + let sub (value : int) (store : storage_type) : return_type = [], store - value - end let test = - let contract = Test.Originate.contract (contract_of Counter) 0 0tez in let _ = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "add" contract.taddr) 5 0tez in let _ = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "sub" contract.taddr) 2 0tez in @@ -47,29 +49,27 @@ let test = ```jsligo group=starter_counter -import Test = Test.Next; - namespace Counter { type storage_type = int; type return_type = [list, storage_type]; - @entry + // @entry const add = (value: int, store: storage_type): return_type => [[], store + value]; - @entry + // @entry const sub = (value: int, store: storage_type): return_type => [[], store - value]; - } const test = (() => { - - const contract = Test.Originate.contract(contract_of(Counter), 0, 0tez); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("add", contract.taddr), 5, 0tez); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("sub", contract.taddr), 2, 0tez); + const contract = + Test.Originate.contract(contract_of(Counter), 0, 0 as tez); + Test.Contract.transfer_exn( + Test.Typed_address.get_entrypoint("add", contract.taddr), 5, 0 as tez); + Test.Contract.transfer_exn( + Test.Typed_address.get_entrypoint("sub", contract.taddr), 2, 0 as tez); Assert.assert(Test.Typed_address.get_storage(contract.taddr) == 3); - })(); ``` diff --git a/gitlab-pages/docs/syntax/contracts/dynamic-entrypoints.md b/gitlab-pages/docs/syntax/contracts/dynamic-entrypoints.md index 5e67fe261d..cfe979c63b 100644 --- a/gitlab-pages/docs/syntax/contracts/dynamic-entrypoints.md +++ b/gitlab-pages/docs/syntax/contracts/dynamic-entrypoints.md @@ -177,45 +177,51 @@ namespace DynamicContract { export const currentAction = (_u: unit, s: internal_storage): dyn_return_type => [[], s]; // Run the currentAction entrypoint - @entry + // @entry const runAction = (_u: unit, full_storage: storage_type): return_type => { - const {storage; dynamic_entrypoints} = full_storage; + const {storage, dynamic_entrypoints} = full_storage; - return match (Dynamic_entrypoints.get(currentAction, dynamic_entrypoints)) { - when (Some(f)): do { + return $match (Dynamic_entrypoints.get(currentAction, dynamic_entrypoints), { + "Some": f => (() => { const [operations, newStorage] = f(unit, storage); return [operations, ({ storage: newStorage, dynamic_entrypoints: dynamic_entrypoints })]; - }; - when (None): failwith(-1); - } + })(), + "None": () => failwith(-1) + }) } // Change the currentAction entrypoint to double or square - @entry + // @entry const changeAction = (new_action_str: string, full_storage: storage_type): return_type => { - const {storage; dynamic_entrypoints} = full_storage; + const {storage, dynamic_entrypoints} = full_storage; let new_dynamic_entrypoints = dynamic_entrypoints; if (new_action_str == "double") { - - const new_action = match (Dynamic_entrypoints.get(double, dynamic_entrypoints)) { - when (Some(f)): f; - when (None): failwith(-1); - }; - new_dynamic_entrypoints = Dynamic_entrypoints.set(currentAction, Some(new_action), dynamic_entrypoints); + const new_action = + $match(Dynamic_entrypoints.get(double, dynamic_entrypoints), { + "Some": f => f, + "None": () => failwith(-1) + }); + new_dynamic_entrypoints = + Dynamic_entrypoints.set(currentAction, + ["Some" as "Some", new_action], + dynamic_entrypoints); } if (new_action_str == "square") { - - const new_action = match (Dynamic_entrypoints.get(square, dynamic_entrypoints)) { - when (Some(f)): f; - when (None): failwith(-1); - }; - new_dynamic_entrypoints = Dynamic_entrypoints.set(currentAction, Some(new_action), dynamic_entrypoints); + const new_action = + $match(Dynamic_entrypoints.get(square, dynamic_entrypoints), { + "Some": f => f, + "None": () => failwith(-1) + }); + new_dynamic_entrypoints = + Dynamic_entrypoints.set(currentAction, + ["Some" as "Some", new_action], + dynamic_entrypoints); } return [[], { @@ -253,16 +259,16 @@ match (Dynamic_entrypoints.get currentAction dynamic_entrypoints) with ```jsligo skip -return match (Dynamic_entrypoints.get(currentAction, dynamic_entrypoints)) { - when (Some(f)): do { +return $match (Dynamic_entrypoints.get(currentAction, dynamic_entrypoints), { + "Some": f => (() => { const [operations, newStorage] = f(unit, storage); return [operations, ({ storage: newStorage, dynamic_entrypoints: dynamic_entrypoints })]; - }; - when (None): failwith("Error"); -} + })(), + "None": () => failwith("Error") +}) ``` @@ -311,12 +317,15 @@ then let new_action = match (Dynamic_entrypoints.get square dynamic_entrypoints) ```jsligo skip if (new_action_str == "square") { - - const new_action = match (Dynamic_entrypoints.get(square, dynamic_entrypoints)) { - when (Some(f)): f; - when (None): failwith("Error"); - }; - new_dynamic_entrypoints = Dynamic_entrypoints.set(currentAction, Some(new_action), dynamic_entrypoints); + const new_action = + $match (Dynamic_entrypoints.get(square, dynamic_entrypoints), { + "Some": f => f, + "None": () => failwith("Error") + }); + new_dynamic_entrypoints = + Dynamic_entrypoints.set(currentAction, + ["Some" as "Some", new_action], + dynamic_entrypoints); } return [[], { @@ -353,11 +362,14 @@ let set_double_bytes (() : unit) (full_storage : storage_type) : return_type = ```jsligo skip -@entry +// @entry const set_double_bytes = (_: unit, full_storage: storage_type): return_type => { - const {storage; dynamic_entrypoints} = full_storage; + const {storage, dynamic_entrypoints} = full_storage; const double_bytes = 0x0502000000250320093100000019035b0765055f036d035b020000000a03210312053d036d034200000000 as bytes; - const new_dynamic_entrypoints = Dynamic_entrypoints.set_bytes(currentAction, Some(double_bytes), dynamic_entrypoints); + const new_dynamic_entrypoints = + Dynamic_entrypoints.set_bytes(currentAction, + ["Some" as "Some", double_bytes], + dynamic_entrypoints); return [[], { storage: storage, @@ -389,7 +401,7 @@ let opt_out (_i : int) (_s : internal_storage) : dyn_return_type = ```jsligo skip -@dyn_entry +// @dyn_entry const opt_out = (_i: int, _s : internal_storage) : dyn_return_type => (External `OPT_OUT_ENTRY`) ``` @@ -435,8 +447,11 @@ You can use this value as the initial storage when you originate the contract. ## Testing dynamic entrypoints -To simplify testing contracts with dynamic entrypoints, you can use the function `Test.Next.storage_with_dynamic_entrypoints` to generate the initial storage. -Like the `ligo compile storage` command, this function takes only the value of the `storage` field in the contract storage, not the full storage value. +To simplify testing contracts with dynamic entrypoints, you can use +the function `Test.Dynamic_entrypoints.storage` to generate the +initial storage. Like the `ligo compile storage` command, this +function takes only the value of the `storage` field in the contract +storage, not the full storage value. It returns the full storage value with both the `storage` and `dynamic_entrypoints` field. Then you can use this return value to originate the contract in the test. @@ -449,10 +464,7 @@ For example, this is a test for the contract in [Defining dynamic entrypoints](# ```cameligo group=simple_dynamic -module Test = Test.Next - let test_dyn = - // Generate storage with dynamic entrypoints let initial_storage = Test.Dynamic_entrypoints.storage (contract_of DynamicContract) 3 in let contract = Test.Originate.contract (contract_of DynamicContract) initial_storage 0mutez in @@ -494,45 +506,52 @@ let test_dyn = ```jsligo group=simple_dynamic -import Test = Test.Next; - -const test_dyn = do { - +const test_dyn = (() => { // Generate storage with dynamic entrypoints - const initial_storage = Test.Dynamic_entrypoints.storage(contract_of(DynamicContract), 3); - const contract = Test.Originate.contract(contract_of(DynamicContract), initial_storage, 0mutez); + const initial_storage = + Test.Dynamic_entrypoints.storage(contract_of(DynamicContract), 3); + const contract = + Test.Originate.contract(contract_of(DynamicContract), + initial_storage, + 0 as mutez); const storage_before = Test.Typed_address.get_storage(contract.taddr); Assert.assert(Test.Compare.eq(storage_before.storage, 3)); // At the start, the runAction dynamic entrypoint does nothing - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", contract.taddr), unit, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", + contract.taddr), unit, 0 as tez); let storage = Test.Typed_address.get_storage(contract.taddr); // Verify that storage did not change Assert.assert(Test.Compare.eq(storage, storage_before)); // Set current action to double - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("changeAction", contract.taddr), "double", 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("changeAction", + contract.taddr), "double", 0 as tez); // Double storage to 6 - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", contract.taddr), unit, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", + contract.taddr), unit, 0 as tez); storage = Test.Typed_address.get_storage(contract.taddr); Assert.assert(Test.Compare.eq(storage.storage, 6)); // Double storage to 12 - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", contract.taddr), unit, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", + contract.taddr), unit, 0 as tez); storage = Test.Typed_address.get_storage(contract.taddr); Assert.assert(Test.Compare.eq(storage.storage, 12)); // Switch to square - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("changeAction", contract.taddr), "square", 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("changeAction", + contract.taddr), "square", 0 as tez); storage = Test.Typed_address.get_storage(contract.taddr); Assert.assert(Test.Compare.eq(storage.storage, 12)); // Square storage to 144 - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", contract.taddr), unit, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", + contract.taddr), unit, 0 as tez); storage = Test.Typed_address.get_storage(contract.taddr); Assert.assert(Test.Compare.eq(storage.storage, 144)); -} +})() ``` diff --git a/gitlab-pages/docs/syntax/contracts/entrypoints.md b/gitlab-pages/docs/syntax/contracts/entrypoints.md index cbc338df56..db99769334 100644 --- a/gitlab-pages/docs/syntax/contracts/entrypoints.md +++ b/gitlab-pages/docs/syntax/contracts/entrypoints.md @@ -42,19 +42,19 @@ export namespace IncDec { // Four entrypoints - @entry + // @entry const increment = (delta: int, storage: storage): result => [[], storage + delta]; - @entry - const @default = (_u: unit, storage: storage): result => + // @entry + const default = (_u: unit, storage: storage): result => increment(1, storage) - @entry + // @entry const decrement = (delta: int, storage: storage): result => [[], storage - delta]; - @entry + // @entry const reset = (_p: unit, _s: storage): result => [[], 0]; }; @@ -62,23 +62,41 @@ export namespace IncDec { + + To call an entrypoint, pass the name of the entrypoint with an initial capital and the parameter. For example, this `run dry-run` command calls the `increment` entrypoint in the previous contract: - - ```bash ligo run dry-run -m IncDec gitlab-pages/docs/syntax/contracts/src/entrypoints/incdec.mligo 'Increment(5)' '4' ``` +:::note + +The entrypoint name in the `dry-run` command always starts with a capital letter, even if the entrypoint name in the source code starts with a lower case letter. + +::: + +To call an entrypoint, you can use the `run dry-run` command, as in this example, which calls the `increment` entrypoint in the previous contract: + ```bash -ligo run dry-run -m IncDec gitlab-pages/docs/syntax/contracts/src/entrypoints/incdec.jsligo 'Increment(5)' '4' +ligo run dry-run -m IncDec gitlab-pages/docs/syntax/contracts/src/entrypoints/incdec.jsligo '["Increment" as "Increment", 5]' '4' ``` +:::note + +The parameter is a [variant](../../data-types/variants) type where the constructor is the name of the entrypoint with the first letter capitalized and the value is the parameter to pass to the entrypoint. +The `ligo run dry-run` command uses this format because Tezos smart contracts don't actually have multiple entrypoints; they are compiled to run code based on the parameter that callers pass. +The LIGO compiler compiles the contract to include a parameter that runs the code that corresponds to the entrypoint. + +For more information about this internal behaviour, see [Implementation details: the default entrypoint](https://docs.tezos.com/smart-contracts/entrypoints#implementation-details-the-default-entrypoint) on docs.tezos.com. + +::: + The response shows an empty list of transactions to run next and the new state of the storage: @@ -87,8 +105,6 @@ The response shows an empty list of transactions to run next and the new state o ( LIST_EMPTY() , 9 ) ``` -Note that even though the entrypoint name starts with a lower-case letter, the `run dry-run` command uses an initial upper-case letter to call it. - ## Parameters LIGO entrypoints always receive two parameters: @@ -128,21 +144,15 @@ type return_type = operation list * storage ```jsligo group=complex_param -type complexParam = [ - int, - string, - bool, -]; +type complexParam = [ int, string, bool ]; type storage = [int, string]; type return_type = [list, storage]; -@entry +// @entry const dosomething = (param: complexParam, storage: storage): return_type => { const [intParam, stringParam, boolParam] = param; - if (boolParam) { - return [[], [intParam, stringParam]]; - } + if (boolParam) return [[], [intParam, stringParam]]; return [[], storage]; } ``` @@ -209,19 +219,19 @@ type parameter = unit; type storage = unit; type result = [list, storage]; -@entry +// @entry const no_tokens = (action: parameter, storage: storage): result => { - if (Tezos.get_amount() > 0tez) { + if (Tezos.get_amount() > (0 as tez)) return failwith("This contract does not accept tokens."); - } else { - return [[], storage]; - }; + return [[], storage]; }; ``` -To send tez, create a transaction with `Tezos.transaction` and return it in the list of operations at the end of the entrypoint, as in this example: +To send tez, create a transaction with `Tezos.Operarion.transaction` +and return it in the list of operations at the end of the entrypoint, +as in this example: @@ -229,12 +239,13 @@ To send tez, create a transaction with `Tezos.transaction` and return it in the type storage = unit type return_value = operation list * storage -[@entry] let give5tez (_ : unit) (storage : storage) : return_value = +[@entry] +let give5tez (_ : unit) (storage : storage) : return_value = if Tezos.get_balance () >= 5tez then let receiver_contract = match Tezos.get_contract_opt (Tezos.get_sender ()) with Some contract -> contract | None -> failwith "Couldn't find account" in - let operation = Tezos.Next.Operation.transaction unit 5tez receiver_contract in + let operation = Tezos.Operation.transaction unit 5tez receiver_contract in [operation], storage else [], storage @@ -248,15 +259,17 @@ type return_value = operation list * storage type storage = unit; type return_value = [list, storage]; -@entry +// @entry const give5tez = (_: unit, storage: storage): return_value => { let operations: list = []; - if (Tezos.get_balance() >= 5tez) { - const receiver_contract = match(Tezos.get_contract_opt(Tezos.get_sender())) { - when(Some(contract)): contract; - when(None): failwith("Couldn't find account"); - }; - operations = [Tezos.Next.Operation.transaction(unit, 5tez, receiver_contract)]; + if (Tezos.get_balance() >= (5 as tez)) { + const receiver_contract = + $match(Tezos.get_contract_opt(Tezos.get_sender()), { + "Some": contract => contract, + "None": () => failwith("Couldn't find account") + }); + operations = + [Tezos.Operation.transaction(unit, 5 as tez, receiver_contract)] } return [operations, storage]; } @@ -287,10 +300,10 @@ let owner_only (action : parameter) (storage: storage) : result = ```jsligo group=c const owner: address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; -@entry +// @entry const owner_only = (action: parameter, storage: storage): result => { - if (Tezos.get_sender() != owner) { return failwith("Access denied."); } - else { return [[], storage]; }; + if (Tezos.get_sender() != owner) return failwith("Access denied."); + return [[], storage]; }; ``` @@ -299,7 +312,7 @@ const owner_only = (action: parameter, storage: storage): result => { :::note The entrypoint in the previous example uses `Tezos.get_sender` instead of `Tezos.get_source` to prevent a security flaw. -For more information, see the [Security tutorial](../../tutorials/security/security.md#incorrect-authorisation-checks). +For more information, see [Security](../../advanced/security). ::: @@ -321,7 +334,7 @@ type return_value = operation list * storage let receiver_contract = match Tezos.get_contract_opt(addr) with Some contract -> contract | None -> failwith "Couldn't find contract" in - let operations = [Tezos.Next.Operation.transaction parameter 0tez receiver_contract] in + let operations = [Tezos.Operation.transaction parameter 0tez receiver_contract] in operations, storage ``` @@ -333,14 +346,16 @@ type return_value = operation list * storage type storage = unit; type return_value = [list, storage]; -@entry +// @entry const callContract = (param: [address, string], storage: storage): return_value => { const [addr, parameter] = param; - const receiver_contract = match(Tezos.get_contract_opt(addr)) { - when(Some(contract)): contract; - when(None): failwith("Couldn't find contract"); - } - const operations = [Tezos.Next.Operation.transaction(parameter, 0tez, receiver_contract)]; + const receiver_contract = + $match(Tezos.get_contract_opt(addr), { + "Some": contract => contract, + "None": () => failwith("Couldn't find contract") + }); + const operations = + [Tezos.Operation.transaction(parameter, 0 as tez, receiver_contract)] return [operations, storage]; } ``` @@ -399,13 +414,13 @@ namespace ContractA { type storage_type = int; type return_type = [list, storage_type]; - @entry + // @entry const increment = (delta: int, storage: storage_type): return_type => [[], storage + delta]; - @entry + // @entry const decrement = (delta: int, storage: storage_type): return_type => [[], storage - delta]; - @entry + // @entry const reset = (_: unit, _s: storage_type): return_type => [[], 0]; } @@ -413,9 +428,10 @@ namespace ContractB { type storage_type = int; export type return_type = [list, storage_type]; - @entry + // @entry const add = ContractA.increment; - @entry + + // @entry const sub = ContractA.decrement; } ``` @@ -426,7 +442,6 @@ namespace ContractB { The name `default` has a special meaning for a Tezos entrypoint. It denotes the default entrypoint that is called unless another is specified. -Because `default` is a reserved keyword in JsLIGO, if you want to create an entrypoint named `default`, you must escape its name as `@default`. For more information about the default entrypoint and its internal behavior, see [Implementation details: the default entrypoint](https://docs.tezos.com/smart-contracts/entrypoints#implementation-details-the-default-entrypoint) on docs.tezos.com. @@ -447,8 +462,6 @@ module OneEntrypoint = struct end -module Test = Test.Next - let test_one_entrypoint = let initial_storage = 42 in let contract = Test.Originate.contract (contract_of OneEntrypoint) initial_storage 0tez in @@ -465,19 +478,20 @@ namespace OneEntrypoint { type storage = int; type return_type = [list, storage]; - @entry + // @entry const increment = (_: unit, storage: storage): return_type => [[], storage + 1]; }; -import Test = Test.Next; - const test_one_entrypoint = (() => { let initial_storage = 42; - let contract = Test.Originate.contract(contract_of(OneEntrypoint), initial_storage, 0tez); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("default", contract.taddr), unit, 0tez); + let contract = + Test.Originate.contract(contract_of(OneEntrypoint), + initial_storage, + 0 as tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("default", contract.taddr), unit, 0 as tez); return Assert.assert(Test.Typed_address.get_storage(contract.taddr) == initial_storage + 1); -}) (); +})(); ``` @@ -543,12 +557,12 @@ const entry_A = (n: nat, store: storage): result => const entry_B = (s: string, store: storage): result => [[], {...store, name: s}]; -@entry +// @entry const main = (action: parameter, store: storage): result => - match(action) { - when(Action_A(n)): entry_A(n, store); - when(Action_B(s)): entry_B(s, store) - }; + $match(action, { + "Action_A": n => entry_A(n, store), + "Action_B": s => entry_B(s, store) + }); ``` @@ -567,7 +581,7 @@ ligo run dry-run gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_mai ```bash -ligo run dry-run gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main.jsligo 'Action_A(5n)' '[5n, "hello"]' +ligo run dry-run gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main.jsligo '["Action_A" as "Action_A", 5 as nat]' '[5n, "hello"]' ``` @@ -586,21 +600,19 @@ proxy file which declares a single entry point and calls the existing ```cameligo group=contract_main_proxy -#import "gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main.mligo" "C" +module C = Gitlab_pages.Docs.Syntax.Contracts.Src.Entrypoints.Contract_main module Proxy = struct - [@entry] let proxy (p : C.parameter) (s : C.storage) : operation list * C.storage = C.main p s - end ``` The contract can then be compiled using the following command: ```shell -ligo compile contract --library . -m Proxy gitlab-pages/docs/advanced/src/entrypoints-contracts/contract_main_proxy.mligo +ligo compile contract --library . -m Proxy gitlab-pages/docs/syntax/contracts/src/entrypoints-contracts/contract_main_proxy.mligo ``` @@ -608,12 +620,11 @@ ligo compile contract --library . -m Proxy gitlab-pages/docs/advanced/src/entryp ```jsligo group=contract_main_proxy -#import "gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main.jsligo" "C" +import * as C from "gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main.jsligo"; namespace Proxy { - @entry - const proxy = - (p: C.parameter, s: C.storage): [list, C.storage] => + // @entry + const proxy = (p: C.parameter, s: C.storage): [list, C.storage] => C.main(p, s) } ``` @@ -621,30 +632,24 @@ namespace Proxy { The contract can then be compiled using the following command: ```shell -ligo compile contract --library . \ - -m Proxy \ - gitlab-pages/docs/advanced/src/entrypoints-contracts/contract_main_proxy.jsligo +ligo compile contract --library . -m Proxy \ gitlab-pages/docs/syntax/contracts/src/entrypoints-contracts/contract_main_proxy.jsligo ``` Notice that to compile a parameter for this contract, now we need to -pass the either `-e proxy` or construct a value using the `Proxy` +pass either `-e proxy` or construct a value using the `Proxy` constructor: ```shell -ligo compile parameter --library . \ - -m Proxy -e proxy \ - gitlab-pages/docs/advanced/src/entrypoints-contracts/contract_main_proxy.mligo \ +ligo compile parameter --library . -m Proxy -e proxy \ gitlab-pages/docs/advanced/src/entrypoints-contracts/contract_main_proxy.mligo \ "Action_A(42n)" ``` ```shell -ligo compile parameter --library . \ - -m Proxy \ - gitlab-pages/docs/advanced/src/entrypoints-contracts/contract_main_proxy.mligo \ +ligo compile parameter --library . -m Proxy \ gitlab-pages/docs/advanced/src/entrypoints-contracts/contract_main_proxy.mligo \ "Proxy(Action_A(42n))" ``` @@ -653,19 +658,13 @@ ligo compile parameter --library . \ ```shell -ligo compile parameter --library . \ - -m Proxy -e proxy \ - gitlab-pages/docs/advanced/src/entrypoints-contracts/contract_main_proxy.jsligo \ - "Action_A(42n)" +ligo compile parameter --library . -m Proxy -e proxy \ gitlab-pages/docs/syntax/contracts/src/entrypoints-contracts/contract_main_proxy.jsligo \ + "["Action_A" as "Action_A", 42 as nat]" ``` ```shell -ligo compile parameter --library . \ - -m Proxy \ - gitlab-pages/docs/advanced/src/entrypoints-contracts/contract_main_proxy.jsligo \ - "Proxy(Action_A(42n))" +ligo compile parameter --library . -m Proxy \ gitlab-pages/docs/syntax/contracts/src/entrypoints-contracts/contract_main_proxy.jsligo \ + "["Proxy" as "Proxy", ["Action_A" as "Action_A", 42 as nat]]" ``` - - diff --git a/gitlab-pages/docs/syntax/contracts/events.md b/gitlab-pages/docs/syntax/contracts/events.md index 5ee81e7fbd..8a8d156f51 100644 --- a/gitlab-pages/docs/syntax/contracts/events.md +++ b/gitlab-pages/docs/syntax/contracts/events.md @@ -9,9 +9,10 @@ Events are a type of internal operation on Tezos. Smart contracts emit events and off-chain applications can listen for events to know when things happen. Smart contracts cannot respond to events. -To create an event, call `Tezos.Next.Operation.Emit` and pass a tag for the event and the information payload for the event. -This function returns the event operation. -Then, emit the event by including it in the list of operations in the return value of the entrypoint. +To create an event, call `Tezos.Operation.emit` and pass a tag for the +event and the information payload for the event. This function +returns the event operation. Then, emit the event by including it in +the list of operations in the return value of the entrypoint. For example, this entrypoint creates two events and emits them: @@ -20,8 +21,8 @@ For example, this entrypoint creates two events and emits them: ```cameligo group=events [@entry] let emitEvents (_ : unit) (storage : int) : operation list * int = - let event1 : operation = Tezos.Next.Operation.emit "%emitEvents" "hi" in - let event2 : operation = Tezos.Next.Operation.emit "%emitEvents" 6 in + let event1 : operation = Tezos.Operation.emit "%emitEvents" "hi" in + let event2 : operation = Tezos.Operation.emit "%emitEvents" 6 in [event1; event2], storage ``` @@ -30,10 +31,10 @@ let emitEvents (_ : unit) (storage : int) : operation list * int = ```jsligo group=events -@entry +// @entry const emitEvents = (_: unit, storage: int): [list, int] => { - const event1: operation = Tezos.Next.Operation.emit("%emitEvents", "hi"); - const event2: operation = Tezos.Next.Operation.emit("%emitEvents", 6); + const event1: operation = Tezos.Operation.emit("%emitEvents", "hi"); + const event2: operation = Tezos.Operation.emit("%emitEvents", 6); return [[event1, event2], storage]; } ``` diff --git a/gitlab-pages/docs/syntax/contracts/interop.md b/gitlab-pages/docs/syntax/contracts/interop.md index 7068f57cd1..1cc877c64b 100644 --- a/gitlab-pages/docs/syntax/contracts/interop.md +++ b/gitlab-pages/docs/syntax/contracts/interop.md @@ -97,7 +97,7 @@ You can use the decorator `@layout("comb")` to make this choice explicit, as in ```jsligo type animal = -@layout("comb") +// @layout("comb") | ["Elephant"] | ["Dog"] | ["Cat"]; @@ -135,7 +135,7 @@ The decorator `@layout("comb")` can also be used on record types: ```jsligo type artist = -@layout("comb") +// @layout("comb") { genre : string, since : timestamp, @@ -176,7 +176,7 @@ You can get the equivalent in version 1.0 and later with the decorator `@layout( ```jsligo group=orig type animal = -@layout("tree") +// @layout("tree") | ["Elephant"] | ["Dog"] | ["Cat"]; @@ -219,9 +219,12 @@ For example, this variant type has custom annotations on each case: ```jsligo group=annot type animal = -| @annot("memory") ["Elephant"] -| @annot("face") ["Dog"] -| @annot("fish") ["Cat"] +| // @annot("memory") + ["Elephant"] +| // @annot("face") + ["Dog"] +| // @annot("fish") + ["Cat"] ``` @@ -260,9 +263,12 @@ annotations: ```jsligo group=annot type artist = { - @annot("style") genre: string, - @annot("from") since: timestamp, - @annot("performer") name: string + // @annot("style") + genre: string, + // @annot("from") + since: timestamp, + // @annot("performer") + name: string } ``` @@ -348,14 +354,14 @@ let x : z_or = M_right y_1 ```jsligo -let z : z_or = M_left(unit); +const z : z_or = ["M_left" as "M_left"]; -let y_1 : y_or = M_left(unit); -let y : z_or = M_right(y_1); +const y_1 : y_or = ["M_left" as "M_left"]; +const y : z_or = ["M_right" as "M_right", y_1]; -let x_pair = ["foo", [2, 3n]]; -let x_1 : y_or = M_right (x_pair); -let x : z_or = M_right (y_1); +const x_pair = ["foo", [2, 3 as nat]]; +const x_1 : y_or = ["M_right" as "M_right", x_pair]; +const x : z_or = ["M_right" as "M_right", y_1]; ``` @@ -442,32 +448,39 @@ type test = { }; const make_concrete_sum = (r: z_to_v): z_or => - match(r) { - when(Z()): M_left(unit); - when(Y()): M_right(M_left(unit)); - when(X()): M_right (M_right (M_left(unit))); - when(W()): M_right (M_right (M_right(M_left(unit)))); - when(V()): M_right (M_right (M_right(M_right(unit)))) - }; - + $match(r, { + "Z": () => ["M_left" as "M_left"], + "Y": () => ["M_right" as "M_right", ["M_left" as "M_left"]], + "X": () => ["M_right" as "M_right", ["M_right" as "M_right", + ["M_left" as "M_left"]]], + "W": () => ["M_right" as "M_right", + ["M_right" as "M_right", + ["M_right" as "M_right", ["M_left" as "M_left"]]]], + "V": () => ["M_right" as "M_right", ["M_right" as "M_right", + ["M_right" as "M_right", + ["M_right" as "M_right"]]]] + }); const make_concrete_record = (r: test) => [r.z, r.y, r.x, r.w, r.v]; const make_abstract_sum = (z_or: z_or): z_to_v => - match(z_or) { - when(M_left(n)): Z(); - when(M_right(y_or)): match(y_or) { - when(M_left(n)): Y(); - when(M_right(x_or)): match(x_or) { - when(M_left(n)): X(); - when(M_right(w_or)): match(w_or) { - when(M_left(n)): W(); - when(M_right(n)): V() - } - } - } - }; + $match(z_or, { + "M_left": _ => ["Z" as "Z"], + "M_right": y_or => + $match(y_or, { + "M_left": _ => ["Y" as "Y"], + "M_right": x_or => + $match(x_or, { + "M_left": _ => ["X" as "X"], + "M_right": w_or => + $match(w_or, { + "M_left": _ => ["W" as "W"], + "M_right": _ => ["V" as "V"] + }) + }) + }) + }); const make_abstract_record = (z: string, y: int, x: string, w: bool, v: int) => ({z,y,x,w,v}); @@ -499,11 +512,11 @@ let right (i : int) (x : storage) : operation list * storage = [], x + i ```jsligo group=entrypoints_and_annotations type storage = int -@entry +// @entry const left = (i: int, x: storage) : [list, storage] => [[], x - i] -@entry +// @entry const right = (i: int, x: storage) : [list, storage] => [[], x + i] ``` @@ -541,13 +554,13 @@ type parameter = ["Left", int] | ["Right", int]; -let main = (p: parameter, x: storage): [list, storage] => - [list ([]), match(p) { - when(Left(i)): x - i; - when(Right(i)): x + i - } +const main = (p: parameter, x: storage): [list, storage] => + [[], + $match(p, { + "Left": i => x - i, + "Right": i => x + i + }) ]; - ``` @@ -558,7 +571,6 @@ This contract can be called by another contract, like this one: ```cameligo group=get_entrypoint_opt type storage = int - type parameter = int type x = Left of int @@ -570,7 +582,7 @@ let main (p : parameter) (s : storage): operation list * storage = | Some c -> c | None -> failwith "contract does not match" in - [Tezos.transaction (Left 2) 2mutez contract], s + [Tezos.Operation.transaction (Left 2) 2mutez contract], s ``` @@ -579,21 +591,19 @@ let main (p : parameter) (s : storage): operation list * storage = ```jsligo group=get_entrypoint_opt type storage = int; - type parameter = int; type x = | ["Left", int]; -@entry +// @entry const main = (p: parameter, s: storage): [list, storage] => { - let contract = - match (Tezos.get_entrypoint_opt("%left", "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx")) { - when(Some(c)): c; - when(None()): failwith ("contract does not match") - }; - return [ - [Tezos.transaction(Left(2), 2mutez, contract)], - s]; + const contract = + $match(Tezos.get_entrypoint_opt("%left", "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"), { + "Some": c => c, + "None": () => failwith ("contract does not match") + }); + return [[Tezos.Operation.transaction(["Left" as "Left", 2], + 2 as mutez, contract)], s]; }; ``` diff --git a/gitlab-pages/docs/syntax/contracts/michelson_inj.md b/gitlab-pages/docs/syntax/contracts/michelson_inj.md index 6830bb7bc8..be26792e7b 100644 --- a/gitlab-pages/docs/syntax/contracts/michelson_inj.md +++ b/gitlab-pages/docs/syntax/contracts/michelson_inj.md @@ -40,7 +40,7 @@ of the Michelson code. ```jsligo group=michelson_inj const michelson_add = n => - (Michelson `{ UNPAIR ; ADD }` as ((n: [nat, nat]) => nat))(n); + (Michelson`{ UNPAIR ; ADD }` as ((n: [nat, nat]) => nat))(n); ``` @@ -99,7 +99,7 @@ outputs: The following command-line: ```shell -ligo compile expression jsligo "(Michelson `{ PUSH nat 42; DROP; PUSH nat 1; ADD }` : nat -> nat)" +ligo compile expression jsligo "(Michelson`{ PUSH nat 42; DROP; PUSH nat 1; ADD }` : nat -> nat)" ``` outputs: @@ -134,7 +134,7 @@ outputs: The following command-line: ```shell -ligo compile expression jsligo "fun n -> (Michelson `{ PUSH nat 42; DROP ; PUSH nat 1; ADD }` : nat -> nat) n" +ligo compile expression jsligo "fun n -> (Michelson`{ PUSH nat 42; DROP ; PUSH nat 1; ADD }` : nat -> nat) n" ``` outputs: @@ -152,18 +152,37 @@ the Michelson code in order to inject it. This is achieved by the special hook `[%of_file ...]`, where the ellipsis is a string containing a file path to a Michelson file with -extension `.tz`. +extension `.tz`. It has to be wrapped in a `[%Michelson ...]` hook, +like so: + +```cameligo skip +let michelson_add (v1 : int) (v2 : int) : int = + [%Michelson ([%of_file "my_michelson.tz"] : int * int -> int)] (v1, v2) +``` -This is achieved by the special hook `(of_file ...)`, where the +This is achieved by the special hook `(michelson_of_file ...)`, where the ellipsis is a *verbatim* string containing a file path to a Michelson -file with extension `.tz`. +file with extension `.tz`, like so: + +```jsligo skip +const michelson_add = (n : [int, int]) : int => + (Michelson_of_file`my_michelson.tz` as ((n: [int, int]) => int))(n) +``` +where `my_michelson.tz` contains + +```michelson +{ parameter nat ; + storage nat ; + code { UNPAIR; ADD } } +``` + ### Injection of Michelson contracts @@ -190,11 +209,11 @@ where the ellipsis is a *verbatim* string containg the file path to a Michelson file with extension `.tz`. ```jsligo group=michelson_inj -@entry +// @entry const main = (param: unit, _storage: unit) : [list, unit] => { const [op, _addr] = (create_contract_of_file `gitlab-pages/docs/syntax/contracts/src/compiled.tz`) - (None(), 1tez, param) + (["None" as "None"], 1 as tez, param) return [[op], []]; } ``` diff --git a/gitlab-pages/docs/syntax/contracts/operation.md b/gitlab-pages/docs/syntax/contracts/operation.md index b4c7a48c6d..3e2f20e3c6 100644 --- a/gitlab-pages/docs/syntax/contracts/operation.md +++ b/gitlab-pages/docs/syntax/contracts/operation.md @@ -18,16 +18,23 @@ The list can include any number of operations of any type. As described in [Operations](https://docs.tezos.com/smart-contracts/logic/operations) on docs.tezos.com, operations do not run immediately when the operation object is created. Instead, operations are added to a stack of operations to run after the code of the entrypoint is complete. -For example, if a contract checks its balance with the `Tezos.Next.get_balance` function, creates an operation to transfer tez to another account, and then checks its balance again in the same entrypoint execution, the balance is the same because the transfer operation has not run yet. +For example, if a contract checks its balance with the `Tezos.Typed_address.get_balance` function, creates an operation to transfer tez to another account, and then checks its balance again in the same entrypoint execution, the balance is the same because the transfer operation has not run yet. For more detailed examples, see [Operations](https://docs.tezos.com/smart-contracts/logic/operations) on docs.tezos.com. -There are no literal values of type operation. -Instead, such values are created using the following functions from the standard library: `Tezos.Next.Operation.transaction` (transfer), `Tezos.Next.Operation.create_contract` (origination), `Tezos.Next.Operation.set_delegate` (delegation), and `Tezos.Next.Operation.Emit` (emission of event). -For the operation to run, these operation values must be included in the list of operations returned at the end of the entrypoint code. +There are no literal values of type operation. Instead, such values +are created using the following functions from the standard library: +`Tezos.Operation.transaction` (transfer), +`Tezos.Operation.create_contract` (origination), +`Tezos.Operation.set_delegate` (delegation), and +`Tezos.peration.emit` (emission of event). For the operation to run, +these operation values must be included in the list of operations +returned at the end of the entrypoint code. ## Creating transactions -The `Tezos.Next.Operation.transaction` function creates a transaction operation, which can be a call to a smart contract (including the same contract) or a transfer of tez to a user account (implicit account). +The `Tezos.Operation.transaction` function creates a transaction +operation, which can be a call to a smart contract (including the same +contract) or a transfer of tez to a user account (implicit account). Its parameters are: - The parameter to pass @@ -45,11 +52,11 @@ type storage = unit type return_value = operation list * storage [@entry] let give5tez (_ : unit) (storage : storage) : return_value = - if Tezos.Next.get_balance () >= 5tez then - let receiver_contract = match Tezos.Next.get_contract_opt (Tezos.Next.get_sender ()) with + if Tezos.get_balance () >= 5tez then + let receiver_contract = match Tezos.get_contract_opt (Tezos.get_sender ()) with Some contract -> contract | None -> failwith "Couldn't find account" in - let operation = Tezos.Next.Operation.transaction unit 5tez receiver_contract in + let operation = Tezos.Operation.transaction unit 5tez receiver_contract in [operation], storage else [], storage @@ -63,15 +70,16 @@ type return_value = operation list * storage type storage = unit; type return_value = [list, storage]; -@entry +// @entry const give5tez = (_: unit, storage: storage): return_value => { let operations: list = []; - if (Tezos.Next.get_balance() >= 5tez) { - const receiver_contract = match(Tezos.Next.get_contract_opt(Tezos.Next.get_sender())) { - when(Some(contract)): contract; - when(None): failwith("Couldn't find account"); - }; - operations = [Tezos.Next.Operation.transaction(unit, 5tez, receiver_contract)]; + if (Tezos.get_balance() >= (5 as tez)) { + const receiver_contract = + $match(Tezos.get_contract_opt(Tezos.get_sender()), { + "Some": contract => contract, + "None": () => failwith("Couldn't find account") + }); + operations = [Tezos.Operation.transaction(unit, 5 as tez, receiver_contract)]; } return [operations, storage]; } @@ -89,8 +97,6 @@ To get the correct parameter for the transaction, contract B uses the `parameter ```cameligo group=operation_transaction -module Test = Test.Next - type 'storage return = operation list * 'storage module A = struct @@ -110,16 +116,16 @@ module B = struct [@entry] let increment (value : int) (stored_address : storage) : storage return = - let contract = Tezos.Next.get_contract stored_address in + let contract = Tezos.get_contract stored_address in let parameter : A parameter_of = Add value in - let operation = Tezos.Next.Operation.transaction parameter 0tez contract in + let operation = Tezos.Operation.transaction parameter 0tez contract in [operation], stored_address [@entry] let decrement (value : int) (stored_address : storage) : storage return = - let contract = Tezos.Next.get_contract stored_address in + let contract = Tezos.get_contract stored_address in let parameter : A parameter_of = Sub value in - let operation = Tezos.Next.Operation.transaction parameter 0tez contract in + let operation = Tezos.Operation.transaction parameter 0tez contract in [operation], stored_address end @@ -144,53 +150,56 @@ let test = ```jsligo group=operation_transaction -import Test = Test.Next; - -type @return = [list, storage]; +type return_ = [list, storage]; namespace A { type storage = int; - @entry - const add = (delta: int, storage: storage): @return => + // @entry + const add = (delta: int, storage: storage): return_ => [[], storage + delta]; - @entry - const sub = (delta: int, storage: storage): @return => + // @entry + const sub = (delta: int, storage: storage): return_ => [[], storage - delta]; } namespace B { type storage = address; - @entry - const increment = (value: int, stored_address: storage): @return => { - const contract = Tezos.Next.get_contract(stored_address); - const parameter = Add(value) as parameter_of A; - const operation = Tezos.Next.Operation.transaction(parameter, 0tez, contract); + // @entry + const increment = (value: int, stored_address: storage): return_ => { + const contract = Tezos.get_contract(stored_address); + const parameter = ["Add" as "Add", value] as parameter_of; + const operation = Tezos.Operation.transaction(parameter, 0 as + tez, contract); return [[operation], stored_address]; } - @entry - const decrement = (value: int, stored_address: storage): @return => { - const contract = Tezos.Next.get_contract(stored_address); - const parameter = Sub(value) as parameter_of A; - const operation = Tezos.Next.Operation.transaction(parameter, 0tez, contract); + // @entry + const decrement = (value: int, stored_address: storage): return_ => { + const contract = Tezos.get_contract(stored_address); + const parameter = ["Sub" as "Sub", value] as parameter_of; + const operation = Tezos.Operation.transaction(parameter, 0 as + tez, contract); return [[operation], stored_address]; } } const test = () => { // Originate contract A - const contract_A = Test.Originate.contract(contract_of(A), 0, 0tez); + const contract_A = Test.Originate.contract(contract_of(A), 0, 0 as tez); const contract_A_address = Test.Typed_address.to_address(contract_A.taddr); // Originate contract B with the address of contract A in its storage - const contract_B = Test.Originate.contract(contract_of(B), contract_A_address, 0tez); + const contract_B = Test.Originate.contract(contract_of(B), + contract_A_address, 0 as tez); // Call contract B - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("increment", contract_B.taddr), 10 as int, 0tez); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("decrement", contract_B.taddr), 2 as int, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("increment", + contract_B.taddr), 10, 0 as tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("decrement", + contract_B.taddr), 2, 0 as tez); const newNumber = Test.Typed_address.get_storage(contract_A.taddr); Assert.assert(newNumber == 8); @@ -215,8 +224,8 @@ In most cases, the parameter is annotated with the names of the entrypoints from In this case, the annotations `%sub` and `%add` indicate the parameters to pass to run the code from the `sub` and `add` entrypoints from the source code. LIGO can use these annotations to parse the parameter and format the call to the contract. -For example, this contract uses the `Tezos.Next.get_entrypoint` function to create a contract address that includes the parameter that indicates the entrypoint. -Then it passes the parameter value for the entrypoint without the entrypoint name as the first parameter of the `Tezos.Next.Operation.transaction` function: +For example, this contract uses the `Tezos.get_entrypoint` function to create a contract address that includes the parameter that indicates the entrypoint. +Then it passes the parameter value for the entrypoint without the entrypoint name as the first parameter of the `Tezos.Operation.transaction` function: @@ -226,14 +235,14 @@ module C = struct [@entry] let increment (value : int) (stored_address : storage) : storage return = - let contract = Tezos.Next.get_entrypoint "%add" stored_address in - let operation = Tezos.Next.Operation.transaction value 0tez contract in + let contract = Tezos.get_entrypoint "%add" stored_address in + let operation = Tezos.Operation.transaction value 0tez contract in [operation], stored_address [@entry] let decrement (value : int) (stored_address : storage) : storage return = - let contract = Tezos.Next.get_entrypoint "%sub" stored_address in - let operation = Tezos.Next.Operation.transaction value 0tez contract in + let contract = Tezos.get_entrypoint "%sub" stored_address in + let operation = Tezos.Operation.transaction value 0tez contract in [operation], stored_address end ``` @@ -246,17 +255,17 @@ module C = struct namespace C { type storage = address; - @entry - const increment = (value: int, stored_address: storage): @return => { - const contract = Tezos.Next.get_entrypoint("%add", stored_address); - const operation = Tezos.Next.Operation.transaction(value, 0tez, contract); + // @entry + const increment = (value: int, stored_address: storage): return_ => { + const contract = Tezos.get_entrypoint("%add", stored_address); + const operation = Tezos.Operation.transaction(value, 0 as tez, contract); return [[operation], stored_address]; } - @entry - const decrement = (value: int, stored_address: storage): @return => { - const contract = Tezos.Next.get_entrypoint("%sub", stored_address); - const operation = Tezos.Next.Operation.transaction(value, 0tez, contract); + // @entry + const decrement = (value: int, stored_address: storage): return_ => { + const contract = Tezos.get_entrypoint("%sub", stored_address); + const operation = Tezos.Operation.transaction(value, 0 as tez, contract); return [[operation], stored_address]; } } @@ -292,7 +301,7 @@ let pass_5_to_sub : contract_a_param = M_left 5 ```jsligo skip type contract_a_param = michelson_or<[int, "sub", int, "add"]>; -const pass_5_to_sub: contract_a_param = M_left(5); +const pass_5_to_sub: contract_a_param = ["M_left" as "M_left", 5]; ``` @@ -309,15 +318,15 @@ module D = struct [@entry] let increment (value : int) (stored_address : storage) : storage return = let pass_to_add : contract_a_param = M_right value in - let contract = Tezos.Next.get_contract stored_address in - let operation = Tezos.Next.Operation.transaction pass_to_add 0tez contract in + let contract = Tezos.get_contract stored_address in + let operation = Tezos.Operation.transaction pass_to_add 0tez contract in [operation], stored_address [@entry] let decrement (value : int) (stored_address : storage) : storage return = let pass_to_sub : contract_a_param = M_left value in - let contract = Tezos.Next.get_contract stored_address in - let operation = Tezos.Next.Operation.transaction pass_to_sub 0tez contract in + let contract = Tezos.get_contract stored_address in + let operation = Tezos.Operation.transaction pass_to_sub 0tez contract in [operation], stored_address end ``` @@ -331,19 +340,21 @@ namespace D { type storage = address; type contract_a_param = michelson_or<[int, "sub", int, "add"]>; - @entry - const increment = (value: int, stored_address: storage): @return => { - const pass_to_add: contract_a_param = M_right(value); - const contract = Tezos.Next.get_contract(stored_address); - const operation = Tezos.Next.Operation.transaction(pass_to_add, 0tez, contract); + // @entry + const increment = (value: int, stored_address: storage): return_ => { + const pass_to_add: contract_a_param = ["M_right" as "M_right", value]; + const contract = Tezos.get_contract(stored_address); + const operation = Tezos.Operation.transaction(pass_to_add, + 0 as tez, contract); return [[operation], stored_address]; } - @entry - const decrement = (value: int, stored_address: storage): @return => { - const pass_to_sub: contract_a_param = M_left(value); - const contract = Tezos.Next.get_contract(stored_address); - const operation = Tezos.Next.Operation.transaction(pass_to_sub, 0tez, contract); + // @entry + const decrement = (value: int, stored_address: storage): return_ => { + const pass_to_sub: contract_a_param = ["M_left" as "M_left", value]; + const contract = Tezos.get_contract(stored_address); + const operation = Tezos.Operation.transaction(pass_to_sub, + 0 as tez, contract); return [[operation], stored_address]; } } @@ -355,7 +366,7 @@ For information about constructing more complicated parameters, see [Interoperab ## Originating contracts -The `Tezos.Next.Operation.create_contract` function creates an operation to originate a contract. +The `Tezos.Operation.create_contract` function creates an operation to originate a contract. Its parameters are: - The code of the new contract as a function @@ -363,9 +374,9 @@ Its parameters are: - The amount of tez for the contract's initial balance - The initial storage value for the contract -The `Tezos.Next.Operation.create_contract` function returns the operation and the address of the new contract. +The `Tezos.Operation.create_contract` function returns the operation and the address of the new contract. However, a contract cannot originate a contract and call it in the same entrypoint execution because the origination operation must run first, and as described previously, operations do not run until the entrypoint execution is complete. -Calling the `Tezos.Next.get_contract_opt` function on that address returns `None` until the new contract is actually originated. +Calling the `Tezos.get_contract_opt` function on that address returns `None` until the new contract is actually originated. This example originates a simple contract: @@ -379,7 +390,7 @@ let main (_ : string) (storage : string) : return = let entrypoint (_ : nat) (storage : string) = (([] : operation list), storage) in let op, _addr : operation * address = - Tezos.Next.Operation.create_contract + Tezos.Operation.create_contract entrypoint (None : key_hash option) 300000000mutez @@ -392,16 +403,15 @@ let main (_ : string) (storage : string) : return = ```jsligo group=origination -type @return = [list, string]; +type return_ = [list, string]; -@entry -const main = (_: string, storage: string) : @return => { - const entrypoint = (_param: nat, storage: string) => - [list([]), storage]; +// @entry +const main = (_: string, storage: string) : return_ => { + const entrypoint = (_param: nat, storage: string) : return_ => [[], storage]; const [op, _addr]: [operation, address] = - Tezos.Next.Operation.create_contract(entrypoint, - (None() as option), - 300000000mutez, + Tezos.Operation.create_contract(entrypoint, + (["None" as "None"] as option), + 300000000 as mutez, "one"); return [[op], storage]; } @@ -411,7 +421,7 @@ const main = (_: string, storage: string) : @return => { ## Changing delegation -The `Tezos.Next.Operation.set_delegate` function creates an operation that changes the delegate for the current contract. +The `Tezos.Operation.set_delegate` function creates an operation that changes the delegate for the current contract. Its parameter is an option with the public key hash of the new delegate or `None` to withdraw delegation. The operation (not the function itself) fails if the new key hash is the same as the current delegate or is not registered as a delegate. @@ -420,7 +430,7 @@ The operation (not the function itself) fails if the new key hash is the same as ```cameligo group=set_delegate [@entry] let changeDelegate (new_delegate : key_hash) (storage : unit) : operation list * unit = - [Tezos.Next.Operation.set_delegate (Some new_delegate)], storage + [Tezos.Operation.set_delegate (Some new_delegate)], storage ``` @@ -429,9 +439,9 @@ let changeDelegate (new_delegate : key_hash) (storage : unit) : operation list * ```jsligo group=set_delegate -@entry +// @entry const changeDelegate = (new_delegate: key_hash, storage: unit): [list, unit] => - [[Tezos.Next.Operation.set_delegate (Some(new_delegate))], storage]; + [[Tezos.Operation.set_delegate (["Some" as "Some", new_delegate])], storage]; ``` @@ -439,17 +449,18 @@ const changeDelegate = (new_delegate: key_hash, storage: unit): [list ## Emitting events -The `Tezos.Next.Operation.Emit` function creates an event emission operation. -Its parameters are the tag for the event and the payload for the event. -For more information about events, see [Events](../../syntax/contracts/events). +The `Tezos.Operation.emit` function creates an event emission +operation. Its parameters are the tag for the event and the payload +for the event. For more information about events, see +[Events](../../syntax/contracts/events). ```cameligo group=event_emit [@entry] let emitEvents (_ : unit) (storage : int) : operation list * int = - let event1 : operation = Tezos.Next.Operation.emit "%emitEvents" "hi" in - let event2 : operation = Tezos.Next.Operation.emit "%emitEvents" 6 in + let event1 : operation = Tezos.Operation.emit "%emitEvents" "hi" in + let event2 : operation = Tezos.Operation.emit "%emitEvents" 6 in [event1; event2], storage ``` @@ -458,10 +469,10 @@ let emitEvents (_ : unit) (storage : int) : operation list * int = ```jsligo group=event_emit -@entry +// @entry const emitEvents = (_: unit, storage: int): [list, int] => { - const event1: operation = Tezos.Next.Operation.emit("%emitEvents", "hi"); - const event2: operation = Tezos.Next.Operation.emit("%emitEvents", 6); + const event1: operation = Tezos.Operation.emit("%emitEvents", "hi"); + const event2: operation = Tezos.Operation.emit("%emitEvents", 6); return [[event1, event2], storage]; } ``` diff --git a/gitlab-pages/docs/syntax/contracts/src/contracts/starter_counter.jsligo b/gitlab-pages/docs/syntax/contracts/src/contracts/starter_counter.jsligo index a91577152b..2c1b585f5c 100644 --- a/gitlab-pages/docs/syntax/contracts/src/contracts/starter_counter.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/contracts/starter_counter.jsligo @@ -1,24 +1,22 @@ -import Test = Test.Next; - namespace Counter { type storage_type = int; type return_type = [list, storage_type]; - @entry + // @entry const add = (value: int, store: storage_type): return_type => [[], store + value]; - @entry + // @entry const sub = (value: int, store: storage_type): return_type => [[], store - value]; - } const test = (() => { - - const contract = Test.Originate.contract(contract_of(Counter), 0, 0tez); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("add", contract.taddr), 5, 0tez); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("sub", contract.taddr), 2, 0tez); + const contract = + Test.Originate.contract(contract_of(Counter), 0, 0 as tez); + Test.Contract.transfer_exn( + Test.Typed_address.get_entrypoint("add", contract.taddr), 5, 0 as tez); + Test.Contract.transfer_exn( + Test.Typed_address.get_entrypoint("sub", contract.taddr), 2, 0 as tez); Assert.assert(Test.Typed_address.get_storage(contract.taddr) == 3); - })(); \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/contracts/starter_counter.mligo b/gitlab-pages/docs/syntax/contracts/src/contracts/starter_counter.mligo index ee32c3cbe2..df996aa228 100644 --- a/gitlab-pages/docs/syntax/contracts/src/contracts/starter_counter.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/contracts/starter_counter.mligo @@ -1,21 +1,17 @@ -module Test = Test.Next - module Counter = struct type storage_type = int type return_type = operation list * storage_type [@entry] - let add (value : int) ( store: storage_type) : return_type = + let add (value : int) (store : storage_type) : return_type = [], store + value [@entry] - let sub (value : int) ( store: storage_type) : return_type = + let sub (value : int) (store : storage_type) : return_type = [], store - value - end let test = - let contract = Test.Originate.contract (contract_of Counter) 0 0tez in let _ = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "add" contract.taddr) 5 0tez in let _ = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "sub" contract.taddr) 2 0tez in diff --git a/gitlab-pages/docs/syntax/contracts/src/dynamic-entrypoints/simple_dynamic.jsligo b/gitlab-pages/docs/syntax/contracts/src/dynamic-entrypoints/simple_dynamic.jsligo index 5e3cb52ce2..3f3c7a22b2 100644 --- a/gitlab-pages/docs/syntax/contracts/src/dynamic-entrypoints/simple_dynamic.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/dynamic-entrypoints/simple_dynamic.jsligo @@ -21,45 +21,51 @@ namespace DynamicContract { export const currentAction = (_u: unit, s: internal_storage): dyn_return_type => [[], s]; // Run the currentAction entrypoint - @entry + // @entry const runAction = (_u: unit, full_storage: storage_type): return_type => { - const {storage; dynamic_entrypoints} = full_storage; + const {storage, dynamic_entrypoints} = full_storage; - return match (Dynamic_entrypoints.get(currentAction, dynamic_entrypoints)) { - when (Some(f)): do { + return $match (Dynamic_entrypoints.get(currentAction, dynamic_entrypoints), { + "Some": f => (() => { const [operations, newStorage] = f(unit, storage); return [operations, ({ storage: newStorage, dynamic_entrypoints: dynamic_entrypoints })]; - }; - when (None): failwith(-1); - } + })(), + "None": () => failwith(-1) + }) } // Change the currentAction entrypoint to double or square - @entry + // @entry const changeAction = (new_action_str: string, full_storage: storage_type): return_type => { - const {storage; dynamic_entrypoints} = full_storage; + const {storage, dynamic_entrypoints} = full_storage; let new_dynamic_entrypoints = dynamic_entrypoints; if (new_action_str == "double") { - - const new_action = match (Dynamic_entrypoints.get(double, dynamic_entrypoints)) { - when (Some(f)): f; - when (None): failwith(-1); - }; - new_dynamic_entrypoints = Dynamic_entrypoints.set(currentAction, Some(new_action), dynamic_entrypoints); + const new_action = + $match(Dynamic_entrypoints.get(double, dynamic_entrypoints), { + "Some": f => f, + "None": () => failwith(-1) + }); + new_dynamic_entrypoints = + Dynamic_entrypoints.set(currentAction, + ["Some" as "Some", new_action], + dynamic_entrypoints); } if (new_action_str == "square") { - - const new_action = match (Dynamic_entrypoints.get(square, dynamic_entrypoints)) { - when (Some(f)): f; - when (None): failwith(-1); - }; - new_dynamic_entrypoints = Dynamic_entrypoints.set(currentAction, Some(new_action), dynamic_entrypoints); + const new_action = + $match(Dynamic_entrypoints.get(square, dynamic_entrypoints), { + "Some": f => f, + "None": () => failwith(-1) + }); + new_dynamic_entrypoints = + Dynamic_entrypoints.set(currentAction, + ["Some" as "Some", new_action], + dynamic_entrypoints); } return [[], { @@ -68,42 +74,49 @@ namespace DynamicContract { }]; } } -import Test = Test.Next; - -const test_dyn = do { - +const test_dyn = (() => { // Generate storage with dynamic entrypoints - const initial_storage = Test.Dynamic_entrypoints.storage(contract_of(DynamicContract), 3); - const contract = Test.Originate.contract(contract_of(DynamicContract), initial_storage, 0mutez); + const initial_storage = + Test.Dynamic_entrypoints.storage(contract_of(DynamicContract), 3); + const contract = + Test.Originate.contract(contract_of(DynamicContract), + initial_storage, + 0 as mutez); const storage_before = Test.Typed_address.get_storage(contract.taddr); Assert.assert(Test.Compare.eq(storage_before.storage, 3)); // At the start, the runAction dynamic entrypoint does nothing - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", contract.taddr), unit, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", + contract.taddr), unit, 0 as tez); let storage = Test.Typed_address.get_storage(contract.taddr); // Verify that storage did not change Assert.assert(Test.Compare.eq(storage, storage_before)); // Set current action to double - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("changeAction", contract.taddr), "double", 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("changeAction", + contract.taddr), "double", 0 as tez); // Double storage to 6 - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", contract.taddr), unit, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", + contract.taddr), unit, 0 as tez); storage = Test.Typed_address.get_storage(contract.taddr); Assert.assert(Test.Compare.eq(storage.storage, 6)); // Double storage to 12 - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", contract.taddr), unit, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", + contract.taddr), unit, 0 as tez); storage = Test.Typed_address.get_storage(contract.taddr); Assert.assert(Test.Compare.eq(storage.storage, 12)); // Switch to square - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("changeAction", contract.taddr), "square", 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("changeAction", + contract.taddr), "square", 0 as tez); storage = Test.Typed_address.get_storage(contract.taddr); Assert.assert(Test.Compare.eq(storage.storage, 12)); // Square storage to 144 - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", contract.taddr), unit, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("runAction", + contract.taddr), unit, 0 as tez); storage = Test.Typed_address.get_storage(contract.taddr); Assert.assert(Test.Compare.eq(storage.storage, 144)); -} \ No newline at end of file +})() \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/dynamic-entrypoints/simple_dynamic.mligo b/gitlab-pages/docs/syntax/contracts/src/dynamic-entrypoints/simple_dynamic.mligo index 3a30f1b187..1cb5f9a2e4 100644 --- a/gitlab-pages/docs/syntax/contracts/src/dynamic-entrypoints/simple_dynamic.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/dynamic-entrypoints/simple_dynamic.mligo @@ -56,10 +56,7 @@ module DynamicContract = struct } end -module Test = Test.Next - let test_dyn = - // Generate storage with dynamic entrypoints let initial_storage = Test.Dynamic_entrypoints.storage (contract_of DynamicContract) 3 in let contract = Test.Originate.contract (contract_of DynamicContract) initial_storage 0mutez in diff --git a/gitlab-pages/docs/syntax/contracts/src/entrypoints/c.jsligo b/gitlab-pages/docs/syntax/contracts/src/entrypoints/c.jsligo index 441df386e0..41d366d4b4 100644 --- a/gitlab-pages/docs/syntax/contracts/src/entrypoints/c.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/entrypoints/c.jsligo @@ -2,18 +2,16 @@ type parameter = unit; type storage = unit; type result = [list, storage]; -@entry +// @entry const no_tokens = (action: parameter, storage: storage): result => { - if (Tezos.get_amount() > 0tez) { + if (Tezos.get_amount() > (0 as tez)) return failwith("This contract does not accept tokens."); - } else { - return [[], storage]; - }; + return [[], storage]; }; const owner: address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; -@entry +// @entry const owner_only = (action: parameter, storage: storage): result => { - if (Tezos.get_sender() != owner) { return failwith("Access denied."); } - else { return [[], storage]; }; + if (Tezos.get_sender() != owner) return failwith("Access denied."); + return [[], storage]; }; \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/entrypoints/call_entrypoint.jsligo b/gitlab-pages/docs/syntax/contracts/src/entrypoints/call_entrypoint.jsligo index 5444fd2dac..4934621a5b 100644 --- a/gitlab-pages/docs/syntax/contracts/src/entrypoints/call_entrypoint.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/entrypoints/call_entrypoint.jsligo @@ -1,13 +1,15 @@ type storage = unit; type return_value = [list, storage]; -@entry +// @entry const callContract = (param: [address, string], storage: storage): return_value => { const [addr, parameter] = param; - const receiver_contract = match(Tezos.get_contract_opt(addr)) { - when(Some(contract)): contract; - when(None): failwith("Couldn't find contract"); - } - const operations = [Tezos.Next.Operation.transaction(parameter, 0tez, receiver_contract)]; + const receiver_contract = + $match(Tezos.get_contract_opt(addr), { + "Some": contract => contract, + "None": () => failwith("Couldn't find contract") + }); + const operations = + [Tezos.Operation.transaction(parameter, 0 as tez, receiver_contract)] return [operations, storage]; } \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/entrypoints/call_entrypoint.mligo b/gitlab-pages/docs/syntax/contracts/src/entrypoints/call_entrypoint.mligo index e4dff278f0..0298058fc1 100644 --- a/gitlab-pages/docs/syntax/contracts/src/entrypoints/call_entrypoint.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/entrypoints/call_entrypoint.mligo @@ -6,5 +6,5 @@ type return_value = operation list * storage let receiver_contract = match Tezos.get_contract_opt(addr) with Some contract -> contract | None -> failwith "Couldn't find contract" in - let operations = [Tezos.Next.Operation.transaction parameter 0tez receiver_contract] in + let operations = [Tezos.Operation.transaction parameter 0tez receiver_contract] in operations, storage \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/entrypoints/complex_param.jsligo b/gitlab-pages/docs/syntax/contracts/src/entrypoints/complex_param.jsligo index 22db2c8ea0..99f71c9525 100644 --- a/gitlab-pages/docs/syntax/contracts/src/entrypoints/complex_param.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/entrypoints/complex_param.jsligo @@ -1,17 +1,11 @@ -type complexParam = [ - int, - string, - bool, -]; +type complexParam = [ int, string, bool ]; type storage = [int, string]; type return_type = [list, storage]; -@entry +// @entry const dosomething = (param: complexParam, storage: storage): return_type => { const [intParam, stringParam, boolParam] = param; - if (boolParam) { - return [[], [intParam, stringParam]]; - } + if (boolParam) return [[], [intParam, stringParam]]; return [[], storage]; } \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main.jsligo b/gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main.jsligo index 72eafa5302..6cb450806f 100644 --- a/gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main.jsligo @@ -15,9 +15,9 @@ const entry_A = (n: nat, store: storage): result => const entry_B = (s: string, store: storage): result => [[], {...store, name: s}]; -@entry +// @entry const main = (action: parameter, store: storage): result => - match(action) { - when(Action_A(n)): entry_A(n, store); - when(Action_B(s)): entry_B(s, store) - }; \ No newline at end of file + $match(action, { + "Action_A": n => entry_A(n, store), + "Action_B": s => entry_B(s, store) + }); \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main_proxy.jsligo b/gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main_proxy.jsligo index d79e4438aa..061c1b3630 100644 --- a/gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main_proxy.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main_proxy.jsligo @@ -1,8 +1,7 @@ -#import "gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main.jsligo" "C" +import * as C from "gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main.jsligo"; namespace Proxy { - @entry - const proxy = - (p: C.parameter, s: C.storage): [list, C.storage] => + // @entry + const proxy = (p: C.parameter, s: C.storage): [list, C.storage] => C.main(p, s) } \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main_proxy.mligo b/gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main_proxy.mligo index 41032ad70e..3ea84a604f 100644 --- a/gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main_proxy.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main_proxy.mligo @@ -1,9 +1,7 @@ -#import "gitlab-pages/docs/syntax/contracts/src/entrypoints/contract_main.mligo" "C" +module C = Gitlab_pages.Docs.Syntax.Contracts.Src.Entrypoints.Contract_main module Proxy = struct - [@entry] let proxy (p : C.parameter) (s : C.storage) : operation list * C.storage = C.main p s - end \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/entrypoints/exported_entrypoints.jsligo b/gitlab-pages/docs/syntax/contracts/src/entrypoints/exported_entrypoints.jsligo index 4c742f68ec..c44dd82299 100644 --- a/gitlab-pages/docs/syntax/contracts/src/entrypoints/exported_entrypoints.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/entrypoints/exported_entrypoints.jsligo @@ -2,13 +2,13 @@ namespace ContractA { type storage_type = int; type return_type = [list, storage_type]; - @entry + // @entry const increment = (delta: int, storage: storage_type): return_type => [[], storage + delta]; - @entry + // @entry const decrement = (delta: int, storage: storage_type): return_type => [[], storage - delta]; - @entry + // @entry const reset = (_: unit, _s: storage_type): return_type => [[], 0]; } @@ -16,8 +16,9 @@ namespace ContractB { type storage_type = int; export type return_type = [list, storage_type]; - @entry + // @entry const add = ContractA.increment; - @entry + + // @entry const sub = ContractA.decrement; } \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/entrypoints/incdec.jsligo b/gitlab-pages/docs/syntax/contracts/src/entrypoints/incdec.jsligo index 877938717b..56008ca4e4 100644 --- a/gitlab-pages/docs/syntax/contracts/src/entrypoints/incdec.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/entrypoints/incdec.jsligo @@ -4,19 +4,19 @@ export namespace IncDec { // Four entrypoints - @entry + // @entry const increment = (delta: int, storage: storage): result => [[], storage + delta]; - @entry - const @default = (_u: unit, storage: storage): result => + // @entry + const default = (_u: unit, storage: storage): result => increment(1, storage) - @entry + // @entry const decrement = (delta: int, storage: storage): result => [[], storage - delta]; - @entry + // @entry const reset = (_p: unit, _s: storage): result => [[], 0]; }; \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/entrypoints/lost_entrypoint_name.jsligo b/gitlab-pages/docs/syntax/contracts/src/entrypoints/lost_entrypoint_name.jsligo index b9ced6095e..8f8d784b35 100644 --- a/gitlab-pages/docs/syntax/contracts/src/entrypoints/lost_entrypoint_name.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/entrypoints/lost_entrypoint_name.jsligo @@ -2,16 +2,17 @@ namespace OneEntrypoint { type storage = int; type return_type = [list, storage]; - @entry + // @entry const increment = (_: unit, storage: storage): return_type => [[], storage + 1]; }; -import Test = Test.Next; - const test_one_entrypoint = (() => { let initial_storage = 42; - let contract = Test.Originate.contract(contract_of(OneEntrypoint), initial_storage, 0tez); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("default", contract.taddr), unit, 0tez); + let contract = + Test.Originate.contract(contract_of(OneEntrypoint), + initial_storage, + 0 as tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("default", contract.taddr), unit, 0 as tez); return Assert.assert(Test.Typed_address.get_storage(contract.taddr) == initial_storage + 1); -}) (); \ No newline at end of file +})(); \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/entrypoints/lost_entrypoint_name.mligo b/gitlab-pages/docs/syntax/contracts/src/entrypoints/lost_entrypoint_name.mligo index a0f79cda15..77bf586050 100644 --- a/gitlab-pages/docs/syntax/contracts/src/entrypoints/lost_entrypoint_name.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/entrypoints/lost_entrypoint_name.mligo @@ -8,8 +8,6 @@ module OneEntrypoint = struct end -module Test = Test.Next - let test_one_entrypoint = let initial_storage = 42 in let contract = Test.Originate.contract (contract_of OneEntrypoint) initial_storage 0tez in diff --git a/gitlab-pages/docs/syntax/contracts/src/entrypoints/send_tez.jsligo b/gitlab-pages/docs/syntax/contracts/src/entrypoints/send_tez.jsligo index 98e74492a5..afcde5c60c 100644 --- a/gitlab-pages/docs/syntax/contracts/src/entrypoints/send_tez.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/entrypoints/send_tez.jsligo @@ -1,15 +1,17 @@ type storage = unit; type return_value = [list, storage]; -@entry +// @entry const give5tez = (_: unit, storage: storage): return_value => { let operations: list = []; - if (Tezos.get_balance() >= 5tez) { - const receiver_contract = match(Tezos.get_contract_opt(Tezos.get_sender())) { - when(Some(contract)): contract; - when(None): failwith("Couldn't find account"); - }; - operations = [Tezos.Next.Operation.transaction(unit, 5tez, receiver_contract)]; + if (Tezos.get_balance() >= (5 as tez)) { + const receiver_contract = + $match(Tezos.get_contract_opt(Tezos.get_sender()), { + "Some": contract => contract, + "None": () => failwith("Couldn't find account") + }); + operations = + [Tezos.Operation.transaction(unit, 5 as tez, receiver_contract)] } return [operations, storage]; } \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/entrypoints/send_tez.mligo b/gitlab-pages/docs/syntax/contracts/src/entrypoints/send_tez.mligo index 2d5f21ac2a..57322bd2e2 100644 --- a/gitlab-pages/docs/syntax/contracts/src/entrypoints/send_tez.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/entrypoints/send_tez.mligo @@ -1,12 +1,13 @@ type storage = unit type return_value = operation list * storage -[@entry] let give5tez (_ : unit) (storage : storage) : return_value = +[@entry] +let give5tez (_ : unit) (storage : storage) : return_value = if Tezos.get_balance () >= 5tez then let receiver_contract = match Tezos.get_contract_opt (Tezos.get_sender ()) with Some contract -> contract | None -> failwith "Couldn't find account" in - let operation = Tezos.Next.Operation.transaction unit 5tez receiver_contract in + let operation = Tezos.Operation.transaction unit 5tez receiver_contract in [operation], storage else [], storage \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/events/events.jsligo b/gitlab-pages/docs/syntax/contracts/src/events/events.jsligo index 4d61188862..52228f8300 100644 --- a/gitlab-pages/docs/syntax/contracts/src/events/events.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/events/events.jsligo @@ -1,6 +1,6 @@ -@entry +// @entry const emitEvents = (_: unit, storage: int): [list, int] => { - const event1: operation = Tezos.Next.Operation.emit("%emitEvents", "hi"); - const event2: operation = Tezos.Next.Operation.emit("%emitEvents", 6); + const event1: operation = Tezos.Operation.emit("%emitEvents", "hi"); + const event2: operation = Tezos.Operation.emit("%emitEvents", 6); return [[event1, event2], storage]; } \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/events/events.mligo b/gitlab-pages/docs/syntax/contracts/src/events/events.mligo index ebf600ea9e..91acc9bc76 100644 --- a/gitlab-pages/docs/syntax/contracts/src/events/events.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/events/events.mligo @@ -1,5 +1,5 @@ [@entry] let emitEvents (_ : unit) (storage : int) : operation list * int = - let event1 : operation = Tezos.Next.Operation.emit "%emitEvents" "hi" in - let event2 : operation = Tezos.Next.Operation.emit "%emitEvents" 6 in + let event1 : operation = Tezos.Operation.emit "%emitEvents" "hi" in + let event2 : operation = Tezos.Operation.emit "%emitEvents" 6 in [event1; event2], storage \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/interop/annot.jsligo b/gitlab-pages/docs/syntax/contracts/src/interop/annot.jsligo index 814ee9add9..e323e091de 100644 --- a/gitlab-pages/docs/syntax/contracts/src/interop/annot.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/interop/annot.jsligo @@ -1,9 +1,15 @@ type animal = -| @annot("memory") ["Elephant"] -| @annot("face") ["Dog"] -| @annot("fish") ["Cat"] +| // @annot("memory") + ["Elephant"] +| // @annot("face") + ["Dog"] +| // @annot("fish") + ["Cat"] type artist = { - @annot("style") genre: string, - @annot("from") since: timestamp, - @annot("performer") name: string + // @annot("style") + genre: string, + // @annot("from") + since: timestamp, + // @annot("performer") + name: string } \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/interop/entrypoints_and_annotations.jsligo b/gitlab-pages/docs/syntax/contracts/src/interop/entrypoints_and_annotations.jsligo index d3d8154521..c2ec1585d1 100644 --- a/gitlab-pages/docs/syntax/contracts/src/interop/entrypoints_and_annotations.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/interop/entrypoints_and_annotations.jsligo @@ -1,9 +1,9 @@ type storage = int -@entry +// @entry const left = (i: int, x: storage) : [list, storage] => [[], x - i] -@entry +// @entry const right = (i: int, x: storage) : [list, storage] => [[], x + i] \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/interop/get_entrypoint_opt.jsligo b/gitlab-pages/docs/syntax/contracts/src/interop/get_entrypoint_opt.jsligo index 0e6e0a1f7b..bddf1ea25d 100644 --- a/gitlab-pages/docs/syntax/contracts/src/interop/get_entrypoint_opt.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/interop/get_entrypoint_opt.jsligo @@ -1,17 +1,15 @@ type storage = int; - type parameter = int; type x = | ["Left", int]; -@entry +// @entry const main = (p: parameter, s: storage): [list, storage] => { - let contract = - match (Tezos.get_entrypoint_opt("%left", "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx")) { - when(Some(c)): c; - when(None()): failwith ("contract does not match") - }; - return [ - [Tezos.transaction(Left(2), 2mutez, contract)], - s]; + const contract = + $match(Tezos.get_entrypoint_opt("%left", "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"), { + "Some": c => c, + "None": () => failwith ("contract does not match") + }); + return [[Tezos.Operation.transaction(["Left" as "Left", 2], + 2 as mutez, contract)], s]; }; \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/interop/get_entrypoint_opt.mligo b/gitlab-pages/docs/syntax/contracts/src/interop/get_entrypoint_opt.mligo index 336027b7f3..76f86626e7 100644 --- a/gitlab-pages/docs/syntax/contracts/src/interop/get_entrypoint_opt.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/interop/get_entrypoint_opt.mligo @@ -1,5 +1,4 @@ type storage = int - type parameter = int type x = Left of int @@ -11,4 +10,4 @@ let main (p : parameter) (s : storage): operation list * storage = | Some c -> c | None -> failwith "contract does not match" in - [Tezos.transaction (Left 2) 2mutez contract], s \ No newline at end of file + [Tezos.Operation.transaction (Left 2) 2mutez contract], s \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/interop/helper_functions.jsligo b/gitlab-pages/docs/syntax/contracts/src/interop/helper_functions.jsligo index 8ad9e58799..be71a30012 100644 --- a/gitlab-pages/docs/syntax/contracts/src/interop/helper_functions.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/interop/helper_functions.jsligo @@ -19,32 +19,39 @@ type test = { }; const make_concrete_sum = (r: z_to_v): z_or => - match(r) { - when(Z()): M_left(unit); - when(Y()): M_right(M_left(unit)); - when(X()): M_right (M_right (M_left(unit))); - when(W()): M_right (M_right (M_right(M_left(unit)))); - when(V()): M_right (M_right (M_right(M_right(unit)))) - }; - + $match(r, { + "Z": () => ["M_left" as "M_left"], + "Y": () => ["M_right" as "M_right", ["M_left" as "M_left"]], + "X": () => ["M_right" as "M_right", ["M_right" as "M_right", + ["M_left" as "M_left"]]], + "W": () => ["M_right" as "M_right", + ["M_right" as "M_right", + ["M_right" as "M_right", ["M_left" as "M_left"]]]], + "V": () => ["M_right" as "M_right", ["M_right" as "M_right", + ["M_right" as "M_right", + ["M_right" as "M_right"]]]] + }); const make_concrete_record = (r: test) => [r.z, r.y, r.x, r.w, r.v]; const make_abstract_sum = (z_or: z_or): z_to_v => - match(z_or) { - when(M_left(n)): Z(); - when(M_right(y_or)): match(y_or) { - when(M_left(n)): Y(); - when(M_right(x_or)): match(x_or) { - when(M_left(n)): X(); - when(M_right(w_or)): match(w_or) { - when(M_left(n)): W(); - when(M_right(n)): V() - } - } - } - }; + $match(z_or, { + "M_left": _ => ["Z" as "Z"], + "M_right": y_or => + $match(y_or, { + "M_left": _ => ["Y" as "Y"], + "M_right": x_or => + $match(x_or, { + "M_left": _ => ["X" as "X"], + "M_right": w_or => + $match(w_or, { + "M_left": _ => ["W" as "W"], + "M_right": _ => ["V" as "V"] + }) + }) + }) + }); const make_abstract_record = (z: string, y: int, x: string, w: bool, v: int) => ({z,y,x,w,v}); \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/interop/orig.jsligo b/gitlab-pages/docs/syntax/contracts/src/interop/orig.jsligo index 6a3fc69d50..3e49078b23 100644 --- a/gitlab-pages/docs/syntax/contracts/src/interop/orig.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/interop/orig.jsligo @@ -1,5 +1,5 @@ type animal = -@layout("tree") +// @layout("tree") | ["Elephant"] | ["Dog"] | ["Cat"]; \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/interop/ungrouped.jsligo b/gitlab-pages/docs/syntax/contracts/src/interop/ungrouped.jsligo index b5fbcb1538..a74e0de396 100644 --- a/gitlab-pages/docs/syntax/contracts/src/interop/ungrouped.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/interop/ungrouped.jsligo @@ -1,10 +1,10 @@ type animal = -@layout("comb") +// @layout("comb") | ["Elephant"] | ["Dog"] | ["Cat"]; type artist = -@layout("comb") +// @layout("comb") { genre : string, since : timestamp, @@ -14,23 +14,24 @@ type w_and_v = michelson_pair<[int, "w", nat, "v"]>; type x_and = michelson_pair<[string, "x", w_and_v, "other"]>; type y_or = michelson_or<[unit, "y", x_and, "other"]>; type z_or = michelson_or<[unit, "z", y_or, "other"]>; -let z : z_or = M_left(unit); +const z : z_or = ["M_left" as "M_left"]; -let y_1 : y_or = M_left(unit); -let y : z_or = M_right(y_1); +const y_1 : y_or = ["M_left" as "M_left"]; +const y : z_or = ["M_right" as "M_right", y_1]; -let x_pair = ["foo", [2, 3n]]; -let x_1 : y_or = M_right (x_pair); -let x : z_or = M_right (y_1); +const x_pair = ["foo", [2, 3 as nat]]; +const x_1 : y_or = ["M_right" as "M_right", x_pair]; +const x : z_or = ["M_right" as "M_right", y_1]; type storage = int; type parameter = ["Left", int] | ["Right", int]; -let main = (p: parameter, x: storage): [list, storage] => - [list ([]), match(p) { - when(Left(i)): x - i; - when(Right(i)): x + i - } - ]; +const main = (p: parameter, x: storage): [list, storage] => + [[], + $match(p, { + "Left": i => x - i, + "Right": i => x + i + }) + ]; \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/michelson_inj/michelson_inj.jsligo b/gitlab-pages/docs/syntax/contracts/src/michelson_inj/michelson_inj.jsligo index 6a789a09de..2ed634fc13 100644 --- a/gitlab-pages/docs/syntax/contracts/src/michelson_inj/michelson_inj.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/michelson_inj/michelson_inj.jsligo @@ -1,9 +1,9 @@ const michelson_add = n => - (Michelson `{ UNPAIR ; ADD }` as ((n: [nat, nat]) => nat))(n); -@entry + (Michelson`{ UNPAIR ; ADD }` as ((n: [nat, nat]) => nat))(n); +// @entry const main = (param: unit, _storage: unit) : [list, unit] => { const [op, _addr] = (create_contract_of_file `gitlab-pages/docs/syntax/contracts/src/compiled.tz`) - (None(), 1tez, param) + (["None" as "None"], 1 as tez, param) return [[op], []]; } \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/michelson_inj/ungrouped.jsligo b/gitlab-pages/docs/syntax/contracts/src/michelson_inj/ungrouped.jsligo new file mode 100644 index 0000000000..65c6f2e24b --- /dev/null +++ b/gitlab-pages/docs/syntax/contracts/src/michelson_inj/ungrouped.jsligo @@ -0,0 +1,2 @@ +const michelson_add = (n : [int, int]) : int => + (michelson_of_file`my_michelson.tz` as ((n: [int, int]) => int))(n) \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/michelson_inj/ungrouped.mligo b/gitlab-pages/docs/syntax/contracts/src/michelson_inj/ungrouped.mligo new file mode 100644 index 0000000000..fc6bd97d5e --- /dev/null +++ b/gitlab-pages/docs/syntax/contracts/src/michelson_inj/ungrouped.mligo @@ -0,0 +1,2 @@ +let michelson_add (v1 : int) (v2 : int) : int = + [%Michelson ([%of_file "my_michelson.tz"] : int * int -> int)] (v1, v2) \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/operation/event_emit.jsligo b/gitlab-pages/docs/syntax/contracts/src/operation/event_emit.jsligo index 4d61188862..52228f8300 100644 --- a/gitlab-pages/docs/syntax/contracts/src/operation/event_emit.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/operation/event_emit.jsligo @@ -1,6 +1,6 @@ -@entry +// @entry const emitEvents = (_: unit, storage: int): [list, int] => { - const event1: operation = Tezos.Next.Operation.emit("%emitEvents", "hi"); - const event2: operation = Tezos.Next.Operation.emit("%emitEvents", 6); + const event1: operation = Tezos.Operation.emit("%emitEvents", "hi"); + const event2: operation = Tezos.Operation.emit("%emitEvents", 6); return [[event1, event2], storage]; } \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/operation/event_emit.mligo b/gitlab-pages/docs/syntax/contracts/src/operation/event_emit.mligo index ebf600ea9e..91acc9bc76 100644 --- a/gitlab-pages/docs/syntax/contracts/src/operation/event_emit.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/operation/event_emit.mligo @@ -1,5 +1,5 @@ [@entry] let emitEvents (_ : unit) (storage : int) : operation list * int = - let event1 : operation = Tezos.Next.Operation.emit "%emitEvents" "hi" in - let event2 : operation = Tezos.Next.Operation.emit "%emitEvents" 6 in + let event1 : operation = Tezos.Operation.emit "%emitEvents" "hi" in + let event2 : operation = Tezos.Operation.emit "%emitEvents" 6 in [event1; event2], storage \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/operation/operation_transaction.jsligo b/gitlab-pages/docs/syntax/contracts/src/operation/operation_transaction.jsligo index 6db87e802c..7490eafa63 100644 --- a/gitlab-pages/docs/syntax/contracts/src/operation/operation_transaction.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/operation/operation_transaction.jsligo @@ -1,50 +1,53 @@ -import Test = Test.Next; - -type @return = [list, storage]; +type return_ = [list, storage]; namespace A { type storage = int; - @entry - const add = (delta: int, storage: storage): @return => + // @entry + const add = (delta: int, storage: storage): return_ => [[], storage + delta]; - @entry - const sub = (delta: int, storage: storage): @return => + // @entry + const sub = (delta: int, storage: storage): return_ => [[], storage - delta]; } namespace B { type storage = address; - @entry - const increment = (value: int, stored_address: storage): @return => { - const contract = Tezos.Next.get_contract(stored_address); - const parameter = Add(value) as parameter_of A; - const operation = Tezos.Next.Operation.transaction(parameter, 0tez, contract); + // @entry + const increment = (value: int, stored_address: storage): return_ => { + const contract = Tezos.get_contract(stored_address); + const parameter = ["Add" as "Add", value] as parameter_of; + const operation = Tezos.Operation.transaction(parameter, 0 as + tez, contract); return [[operation], stored_address]; } - @entry - const decrement = (value: int, stored_address: storage): @return => { - const contract = Tezos.Next.get_contract(stored_address); - const parameter = Sub(value) as parameter_of A; - const operation = Tezos.Next.Operation.transaction(parameter, 0tez, contract); + // @entry + const decrement = (value: int, stored_address: storage): return_ => { + const contract = Tezos.get_contract(stored_address); + const parameter = ["Sub" as "Sub", value] as parameter_of; + const operation = Tezos.Operation.transaction(parameter, 0 as + tez, contract); return [[operation], stored_address]; } } const test = () => { // Originate contract A - const contract_A = Test.Originate.contract(contract_of(A), 0, 0tez); + const contract_A = Test.Originate.contract(contract_of(A), 0, 0 as tez); const contract_A_address = Test.Typed_address.to_address(contract_A.taddr); // Originate contract B with the address of contract A in its storage - const contract_B = Test.Originate.contract(contract_of(B), contract_A_address, 0tez); + const contract_B = Test.Originate.contract(contract_of(B), + contract_A_address, 0 as tez); // Call contract B - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("increment", contract_B.taddr), 10 as int, 0tez); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("decrement", contract_B.taddr), 2 as int, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("increment", + contract_B.taddr), 10, 0 as tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("decrement", + contract_B.taddr), 2, 0 as tez); const newNumber = Test.Typed_address.get_storage(contract_A.taddr); Assert.assert(newNumber == 8); @@ -54,17 +57,17 @@ const result = test(); namespace C { type storage = address; - @entry - const increment = (value: int, stored_address: storage): @return => { - const contract = Tezos.Next.get_entrypoint("%add", stored_address); - const operation = Tezos.Next.Operation.transaction(value, 0tez, contract); + // @entry + const increment = (value: int, stored_address: storage): return_ => { + const contract = Tezos.get_entrypoint("%add", stored_address); + const operation = Tezos.Operation.transaction(value, 0 as tez, contract); return [[operation], stored_address]; } - @entry - const decrement = (value: int, stored_address: storage): @return => { - const contract = Tezos.Next.get_entrypoint("%sub", stored_address); - const operation = Tezos.Next.Operation.transaction(value, 0tez, contract); + // @entry + const decrement = (value: int, stored_address: storage): return_ => { + const contract = Tezos.get_entrypoint("%sub", stored_address); + const operation = Tezos.Operation.transaction(value, 0 as tez, contract); return [[operation], stored_address]; } } @@ -72,19 +75,21 @@ namespace D { type storage = address; type contract_a_param = michelson_or<[int, "sub", int, "add"]>; - @entry - const increment = (value: int, stored_address: storage): @return => { - const pass_to_add: contract_a_param = M_right(value); - const contract = Tezos.Next.get_contract(stored_address); - const operation = Tezos.Next.Operation.transaction(pass_to_add, 0tez, contract); + // @entry + const increment = (value: int, stored_address: storage): return_ => { + const pass_to_add: contract_a_param = ["M_right" as "M_right", value]; + const contract = Tezos.get_contract(stored_address); + const operation = Tezos.Operation.transaction(pass_to_add, + 0 as tez, contract); return [[operation], stored_address]; } - @entry - const decrement = (value: int, stored_address: storage): @return => { - const pass_to_sub: contract_a_param = M_left(value); - const contract = Tezos.Next.get_contract(stored_address); - const operation = Tezos.Next.Operation.transaction(pass_to_sub, 0tez, contract); + // @entry + const decrement = (value: int, stored_address: storage): return_ => { + const pass_to_sub: contract_a_param = ["M_left" as "M_left", value]; + const contract = Tezos.get_contract(stored_address); + const operation = Tezos.Operation.transaction(pass_to_sub, + 0 as tez, contract); return [[operation], stored_address]; } } \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/operation/operation_transaction.mligo b/gitlab-pages/docs/syntax/contracts/src/operation/operation_transaction.mligo index bab17a6e7b..0cfeac5e64 100644 --- a/gitlab-pages/docs/syntax/contracts/src/operation/operation_transaction.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/operation/operation_transaction.mligo @@ -1,5 +1,3 @@ -module Test = Test.Next - type 'storage return = operation list * 'storage module A = struct @@ -19,16 +17,16 @@ module B = struct [@entry] let increment (value : int) (stored_address : storage) : storage return = - let contract = Tezos.Next.get_contract stored_address in + let contract = Tezos.get_contract stored_address in let parameter : A parameter_of = Add value in - let operation = Tezos.Next.Operation.transaction parameter 0tez contract in + let operation = Tezos.Operation.transaction parameter 0tez contract in [operation], stored_address [@entry] let decrement (value : int) (stored_address : storage) : storage return = - let contract = Tezos.Next.get_contract stored_address in + let contract = Tezos.get_contract stored_address in let parameter : A parameter_of = Sub value in - let operation = Tezos.Next.Operation.transaction parameter 0tez contract in + let operation = Tezos.Operation.transaction parameter 0tez contract in [operation], stored_address end @@ -51,14 +49,14 @@ module C = struct [@entry] let increment (value : int) (stored_address : storage) : storage return = - let contract = Tezos.Next.get_entrypoint "%add" stored_address in - let operation = Tezos.Next.Operation.transaction value 0tez contract in + let contract = Tezos.get_entrypoint "%add" stored_address in + let operation = Tezos.Operation.transaction value 0tez contract in [operation], stored_address [@entry] let decrement (value : int) (stored_address : storage) : storage return = - let contract = Tezos.Next.get_entrypoint "%sub" stored_address in - let operation = Tezos.Next.Operation.transaction value 0tez contract in + let contract = Tezos.get_entrypoint "%sub" stored_address in + let operation = Tezos.Operation.transaction value 0tez contract in [operation], stored_address end module D = struct @@ -68,14 +66,14 @@ module D = struct [@entry] let increment (value : int) (stored_address : storage) : storage return = let pass_to_add : contract_a_param = M_right value in - let contract = Tezos.Next.get_contract stored_address in - let operation = Tezos.Next.Operation.transaction pass_to_add 0tez contract in + let contract = Tezos.get_contract stored_address in + let operation = Tezos.Operation.transaction pass_to_add 0tez contract in [operation], stored_address [@entry] let decrement (value : int) (stored_address : storage) : storage return = let pass_to_sub : contract_a_param = M_left value in - let contract = Tezos.Next.get_contract stored_address in - let operation = Tezos.Next.Operation.transaction pass_to_sub 0tez contract in + let contract = Tezos.get_contract stored_address in + let operation = Tezos.Operation.transaction pass_to_sub 0tez contract in [operation], stored_address end \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/operation/origination.jsligo b/gitlab-pages/docs/syntax/contracts/src/operation/origination.jsligo index 333f433e4a..8de36225ff 100644 --- a/gitlab-pages/docs/syntax/contracts/src/operation/origination.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/operation/origination.jsligo @@ -1,13 +1,12 @@ -type @return = [list, string]; +type return_ = [list, string]; -@entry -const main = (_: string, storage: string) : @return => { - const entrypoint = (_param: nat, storage: string) => - [list([]), storage]; +// @entry +const main = (_: string, storage: string) : return_ => { + const entrypoint = (_param: nat, storage: string) : return_ => [[], storage]; const [op, _addr]: [operation, address] = - Tezos.Next.Operation.create_contract(entrypoint, - (None() as option), - 300000000mutez, + Tezos.Operation.create_contract(entrypoint, + (["None" as "None"] as option), + 300000000 as mutez, "one"); return [[op], storage]; } \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/operation/origination.mligo b/gitlab-pages/docs/syntax/contracts/src/operation/origination.mligo index 5156f3efcb..ed3d82b164 100644 --- a/gitlab-pages/docs/syntax/contracts/src/operation/origination.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/operation/origination.mligo @@ -5,7 +5,7 @@ let main (_ : string) (storage : string) : return = let entrypoint (_ : nat) (storage : string) = (([] : operation list), storage) in let op, _addr : operation * address = - Tezos.Next.Operation.create_contract + Tezos.Operation.create_contract entrypoint (None : key_hash option) 300000000mutez diff --git a/gitlab-pages/docs/syntax/contracts/src/operation/send_tez.jsligo b/gitlab-pages/docs/syntax/contracts/src/operation/send_tez.jsligo index 7ca67af722..a10b22f5ff 100644 --- a/gitlab-pages/docs/syntax/contracts/src/operation/send_tez.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/operation/send_tez.jsligo @@ -1,15 +1,16 @@ type storage = unit; type return_value = [list, storage]; -@entry +// @entry const give5tez = (_: unit, storage: storage): return_value => { let operations: list = []; - if (Tezos.Next.get_balance() >= 5tez) { - const receiver_contract = match(Tezos.Next.get_contract_opt(Tezos.Next.get_sender())) { - when(Some(contract)): contract; - when(None): failwith("Couldn't find account"); - }; - operations = [Tezos.Next.Operation.transaction(unit, 5tez, receiver_contract)]; + if (Tezos.get_balance() >= (5 as tez)) { + const receiver_contract = + $match(Tezos.get_contract_opt(Tezos.get_sender()), { + "Some": contract => contract, + "None": () => failwith("Couldn't find account") + }); + operations = [Tezos.Operation.transaction(unit, 5 as tez, receiver_contract)]; } return [operations, storage]; } \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/operation/send_tez.mligo b/gitlab-pages/docs/syntax/contracts/src/operation/send_tez.mligo index 47539fd829..aedc12aaa6 100644 --- a/gitlab-pages/docs/syntax/contracts/src/operation/send_tez.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/operation/send_tez.mligo @@ -2,11 +2,11 @@ type storage = unit type return_value = operation list * storage [@entry] let give5tez (_ : unit) (storage : storage) : return_value = - if Tezos.Next.get_balance () >= 5tez then - let receiver_contract = match Tezos.Next.get_contract_opt (Tezos.Next.get_sender ()) with + if Tezos.get_balance () >= 5tez then + let receiver_contract = match Tezos.get_contract_opt (Tezos.get_sender ()) with Some contract -> contract | None -> failwith "Couldn't find account" in - let operation = Tezos.Next.Operation.transaction unit 5tez receiver_contract in + let operation = Tezos.Operation.transaction unit 5tez receiver_contract in [operation], storage else [], storage \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/operation/set_delegate.jsligo b/gitlab-pages/docs/syntax/contracts/src/operation/set_delegate.jsligo index 3fbe1aaaa9..562071285e 100644 --- a/gitlab-pages/docs/syntax/contracts/src/operation/set_delegate.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/operation/set_delegate.jsligo @@ -1,3 +1,3 @@ -@entry +// @entry const changeDelegate = (new_delegate: key_hash, storage: unit): [list, unit] => - [[Tezos.Next.Operation.set_delegate (Some(new_delegate))], storage]; \ No newline at end of file + [[Tezos.Operation.set_delegate (["Some" as "Some", new_delegate])], storage]; \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/operation/set_delegate.mligo b/gitlab-pages/docs/syntax/contracts/src/operation/set_delegate.mligo index 2859d6438a..e5a3f6c440 100644 --- a/gitlab-pages/docs/syntax/contracts/src/operation/set_delegate.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/operation/set_delegate.mligo @@ -1,3 +1,3 @@ [@entry] let changeDelegate (new_delegate : key_hash) (storage : unit) : operation list * unit = - [Tezos.Next.Operation.set_delegate (Some new_delegate)], storage \ No newline at end of file + [Tezos.Operation.set_delegate (Some new_delegate)], storage \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/views/callonchainview.jsligo b/gitlab-pages/docs/syntax/contracts/src/views/callonchainview.jsligo index 9e9e530cd0..b84f51cd05 100644 --- a/gitlab-pages/docs/syntax/contracts/src/views/callonchainview.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/views/callonchainview.jsligo @@ -2,11 +2,11 @@ namespace ContractWithView { type storage = int; type return_type = [list, storage]; - @entry + // @entry const main = (param: int, _storage: storage): return_type => [[], param]; - @view + // @view const multiply = (param: int, storage: storage): int => param * storage; } @@ -14,34 +14,36 @@ namespace CallView { type storage = [address, int]; type return_type = [list, storage]; - @entry + // @entry const callView = (param: int, storage: storage): return_type => { const [targetAddress, _s] = storage; - const resultOpt: option = Tezos.call_view( + const resultOpt: option = Tezos.View.call( "multiply", param, targetAddress ); - return match(resultOpt) { - when (None): - failwith("Something went wrong"); - when (Some(newValue)): - [[], [targetAddress, newValue]]; - } + return $match(resultOpt, { + "None": () => failwith("Something went wrong"), + "Some": newValue => [[], [targetAddress, newValue]] + }) } } const test = (() => { - // Originate ContractWithView - const contract1 = Test.Next.Originate.contract(contract_of(ContractWithView), 5, 0tez); - const addr1 = Test.Next.Typed_address.to_address(contract1.taddr); + const contract1 = Test.Originate.contract(contract_of(ContractWithView), 5, 0 as tez); + const addr1 = Test.Typed_address.to_address(contract1.taddr); // Originate CallView with address of ContractWithView in storage const initial_storage = [addr1, 0 as int]; - const contract2 = Test.Next.Originate.contract(contract_of(CallView), initial_storage, 0tez); + const contract2 = + Test.Originate.contract(contract_of(CallView), initial_storage, + 0 as tez); // Call callView - Test.Next.Contract.transfer_exn(Test.Next.Typed_address.get_entrypoint("default", contract2.taddr), 12, 0tez); - const [_address, integer] = Test.Next.Typed_address.get_storage(contract2.taddr); + Test.Contract.transfer_exn( + Test.Typed_address.get_entrypoint("default", contract2.taddr), + 12, + 0 as tez); + const [_address, integer] = Test.Typed_address.get_storage(contract2.taddr); Assert.assert(integer == 60); }) () \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/views/callonchainview.mligo b/gitlab-pages/docs/syntax/contracts/src/views/callonchainview.mligo index b713378ec1..b64b76614a 100644 --- a/gitlab-pages/docs/syntax/contracts/src/views/callonchainview.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/views/callonchainview.mligo @@ -7,7 +7,6 @@ module ContractWithView = struct [@view] let multiply (param : int) (storage : storage) : int = param * storage - end module CallView = struct type storage = address * int @@ -15,22 +14,21 @@ module CallView = struct [@entry] let callView (param : int) (storage : storage) : return_type = let (targetAddress, _s) = storage in - let resultOpt : int option = Tezos.call_view "multiply" param targetAddress in + let resultOpt : int option = Tezos.View.call "multiply" param targetAddress in match resultOpt with Some newValue -> [], (targetAddress, newValue) | None -> failwith("Something went wrong") end let test = - // Originate ContractWithView - let contract1 = Test.Next.Originate.contract (contract_of ContractWithView) 5 0tez in - let addr1 = Test.Next.Typed_address.to_address contract1.taddr in + let contract1 = Test.Originate.contract (contract_of ContractWithView) 5 0tez in + let addr1 = Test.Typed_address.to_address contract1.taddr in // Originate CallView with address of ContractWithView in storage let initial_storage = (addr1, 0) in - let contract2 = Test.Next.Originate.contract (contract_of CallView) initial_storage 0tez in + let contract2 = Test.Originate.contract (contract_of CallView) initial_storage 0tez in // Call callView - let _ : nat = Test.Next.Contract.transfer_exn (Test.Next.Typed_address.get_entrypoint "default" contract2.taddr) 12 0tez in - let (_address, integer) = Test.Next.Typed_address.get_storage contract2.taddr in + let _ : nat = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "default" contract2.taddr) 12 0tez in + let _address, integer = Test.Typed_address.get_storage contract2.taddr in Assert.assert(integer = 60) \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/views/onchainviews.jsligo b/gitlab-pages/docs/syntax/contracts/src/views/onchainviews.jsligo index 4980f4b194..435043aae3 100644 --- a/gitlab-pages/docs/syntax/contracts/src/views/onchainviews.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/views/onchainviews.jsligo @@ -1,21 +1,19 @@ type storage = string type ret = [list, storage]; -@entry +// @entry const main = (word : string, storage : storage) : ret => [[] , storage + " " + word] // This view returns the storage -@view -const view1 = (_arg : unit, storage : storage) : storage - => storage; +// @view +const view1 = (_arg : unit, storage : storage) : storage => storage; // This view returns true if the storage has a given length -@view +// @view const view2 = (expected_length : nat , storage : storage) : bool => (String.length (storage) == expected_length); // This view does not use the parameters or storage and returns a constant int -@view -const view3 = (_arg : unit , _s : storage) : int - => 42; \ No newline at end of file +// @view +const view3 = (_arg : unit , _s : storage) : int => 42; \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/views/onchainviews.mligo b/gitlab-pages/docs/syntax/contracts/src/views/onchainviews.mligo index 37b1a50c02..9dcdf813cc 100644 --- a/gitlab-pages/docs/syntax/contracts/src/views/onchainviews.mligo +++ b/gitlab-pages/docs/syntax/contracts/src/views/onchainviews.mligo @@ -2,17 +2,18 @@ type storage = string type ret = operation list * storage [@entry] -let main (word : string) (storage : storage) : ret - = [] , storage ^ " " ^ word +let main (word : string) (storage : storage) : ret = + [] , storage ^ " " ^ word (* This view returns the storage *) -[@view] let view1 (() : unit) (storage : storage) : storage - = storage +[@view] +let view1 (() : unit) (storage : storage) : storage = storage (* This view returns true if the storage has a given length *) -[@view] let view2 (expected_length : nat) (storage : storage) : bool - = (String.length storage = expected_length) +[@view] +let view2 (expected_length : nat) (storage : storage) : bool = + String.length storage = expected_length (* This view does not use the parameters or storage and returns a constant int *) -[@view] let view3 (() : unit) (_ : storage) : int - = 42 \ No newline at end of file +[@view] +let view3 (() : unit) (_ : storage) : int = 42 \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/contracts/src/views/view_file.jsligo b/gitlab-pages/docs/syntax/contracts/src/views/view_file.jsligo index f7592a89c2..86b8f0db62 100644 --- a/gitlab-pages/docs/syntax/contracts/src/views/view_file.jsligo +++ b/gitlab-pages/docs/syntax/contracts/src/views/view_file.jsligo @@ -1,10 +1,10 @@ namespace C { type storage = string - @entry + // @entry const append = (a: string, s: storage) : [list , storage] => [[], s + a]; - @entry + // @entry const clear = (_p: unit, _s: storage) : [list, storage] => [[], ""]; export const v = (expected_length: nat, s: storage) : bool => (String.length (s) == expected_length); diff --git a/gitlab-pages/docs/syntax/contracts/views.md b/gitlab-pages/docs/syntax/contracts/views.md index 6f1d9a364c..dd75f5e1a9 100644 --- a/gitlab-pages/docs/syntax/contracts/views.md +++ b/gitlab-pages/docs/syntax/contracts/views.md @@ -34,20 +34,21 @@ type storage = string type ret = operation list * storage [@entry] -let main (word : string) (storage : storage) : ret - = [] , storage ^ " " ^ word +let main (word : string) (storage : storage) : ret = + [] , storage ^ " " ^ word (* This view returns the storage *) -[@view] let view1 (() : unit) (storage : storage) : storage - = storage +[@view] +let view1 (() : unit) (storage : storage) : storage = storage (* This view returns true if the storage has a given length *) -[@view] let view2 (expected_length : nat) (storage : storage) : bool - = (String.length storage = expected_length) +[@view] +let view2 (expected_length : nat) (storage : storage) : bool = + String.length storage = expected_length (* This view does not use the parameters or storage and returns a constant int *) -[@view] let view3 (() : unit) (_ : storage) : int - = 42 +[@view] +let view3 (() : unit) (_ : storage) : int = 42 ``` @@ -60,24 +61,22 @@ To define an on-chain view, use the `@view` decorator. type storage = string type ret = [list, storage]; -@entry +// @entry const main = (word : string, storage : storage) : ret => [[] , storage + " " + word] // This view returns the storage -@view -const view1 = (_arg : unit, storage : storage) : storage - => storage; +// @view +const view1 = (_arg : unit, storage : storage) : storage => storage; // This view returns true if the storage has a given length -@view +// @view const view2 = (expected_length : nat , storage : storage) : bool => (String.length (storage) == expected_length); // This view does not use the parameters or storage and returns a constant int -@view -const view3 = (_arg : unit , _s : storage) : int - => 42; +// @view +const view3 = (_arg : unit , _s : storage) : int => 42; ``` @@ -112,10 +111,10 @@ end namespace C { type storage = string - @entry + // @entry const append = (a: string, s: storage) : [list , storage] => [[], s + a]; - @entry + // @entry const clear = (_p: unit, _s: storage) : [list, storage] => [[], ""]; export const v = (expected_length: nat, s: storage) : bool => (String.length (s) == expected_length); @@ -153,14 +152,15 @@ Note that the function is not annotated as an entrypoint or on-chain view; it is ## Calling views -Contracts can call on-chain and off-chain views with the `Tezos.call_view` function and use the result immediately. +Contracts can call on-chain and off-chain views with the +`Tezos.View.call` function and use the result immediately. -val call_view : string -> 'arg -> address -> 'ret option +val call : string -> 'arg -> address -> 'ret option -const call_view : string => 'arg => address => option <'ret> +call: string => 'arg => address => option <'ret> The function accepts these parameters: @@ -183,7 +183,6 @@ module ContractWithView = struct [@view] let multiply (param : int) (storage : storage) : int = param * storage - end ``` @@ -196,7 +195,7 @@ module CallView = struct [@entry] let callView (param : int) (storage : storage) : return_type = let (targetAddress, _s) = storage in - let resultOpt : int option = Tezos.call_view "multiply" param targetAddress in + let resultOpt : int option = Tezos.View.call "multiply" param targetAddress in match resultOpt with Some newValue -> [], (targetAddress, newValue) | None -> failwith("Something went wrong") @@ -207,18 +206,17 @@ This test deploys both contracts, calls the contract that calls the view, and ve ```cameligo group=callonchainview let test = - // Originate ContractWithView - let contract1 = Test.Next.Originate.contract (contract_of ContractWithView) 5 0tez in - let addr1 = Test.Next.Typed_address.to_address contract1.taddr in + let contract1 = Test.Originate.contract (contract_of ContractWithView) 5 0tez in + let addr1 = Test.Typed_address.to_address contract1.taddr in // Originate CallView with address of ContractWithView in storage let initial_storage = (addr1, 0) in - let contract2 = Test.Next.Originate.contract (contract_of CallView) initial_storage 0tez in + let contract2 = Test.Originate.contract (contract_of CallView) initial_storage 0tez in // Call callView - let _ : nat = Test.Next.Contract.transfer_exn (Test.Next.Typed_address.get_entrypoint "default" contract2.taddr) 12 0tez in - let (_address, integer) = Test.Next.Typed_address.get_storage contract2.taddr in + let _ : nat = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "default" contract2.taddr) 12 0tez in + let _address, integer = Test.Typed_address.get_storage contract2.taddr in Assert.assert(integer = 60) ``` @@ -233,11 +231,11 @@ namespace ContractWithView { type storage = int; type return_type = [list, storage]; - @entry + // @entry const main = (param: int, _storage: storage): return_type => [[], param]; - @view + // @view const multiply = (param: int, storage: storage): int => param * storage; } @@ -250,20 +248,18 @@ namespace CallView { type storage = [address, int]; type return_type = [list, storage]; - @entry + // @entry const callView = (param: int, storage: storage): return_type => { const [targetAddress, _s] = storage; - const resultOpt: option = Tezos.call_view( + const resultOpt: option = Tezos.View.call( "multiply", param, targetAddress ); - return match(resultOpt) { - when (None): - failwith("Something went wrong"); - when (Some(newValue)): - [[], [targetAddress, newValue]]; - } + return $match(resultOpt, { + "None": () => failwith("Something went wrong"), + "Some": newValue => [[], [targetAddress, newValue]] + }) } } ``` @@ -272,18 +268,22 @@ This test deploys both contracts, calls the contract that calls the view, and ve ```jsligo group=callonchainview const test = (() => { - // Originate ContractWithView - const contract1 = Test.Next.Originate.contract(contract_of(ContractWithView), 5, 0tez); - const addr1 = Test.Next.Typed_address.to_address(contract1.taddr); + const contract1 = Test.Originate.contract(contract_of(ContractWithView), 5, 0 as tez); + const addr1 = Test.Typed_address.to_address(contract1.taddr); // Originate CallView with address of ContractWithView in storage const initial_storage = [addr1, 0 as int]; - const contract2 = Test.Next.Originate.contract(contract_of(CallView), initial_storage, 0tez); + const contract2 = + Test.Originate.contract(contract_of(CallView), initial_storage, + 0 as tez); // Call callView - Test.Next.Contract.transfer_exn(Test.Next.Typed_address.get_entrypoint("default", contract2.taddr), 12, 0tez); - const [_address, integer] = Test.Next.Typed_address.get_storage(contract2.taddr); + Test.Contract.transfer_exn( + Test.Typed_address.get_entrypoint("default", contract2.taddr), + 12, + 0 as tez); + const [_address, integer] = Test.Typed_address.get_storage(contract2.taddr); Assert.assert(integer == 60); }) () ``` @@ -292,4 +292,6 @@ const test = (() => { To call views directly in tests, see [Testing views](../../testing#testing-views). +To compile parameters to pass to views, see [Compiling expressions](../../compiling#compiling-expressions). + diff --git a/gitlab-pages/docs/syntax/decorators.md b/gitlab-pages/docs/syntax/decorators.md index 8ed32b313c..53866b7d80 100644 --- a/gitlab-pages/docs/syntax/decorators.md +++ b/gitlab-pages/docs/syntax/decorators.md @@ -42,6 +42,8 @@ You may encounter them when exporting the Abstract Syntax Tree (AST) after a cer ## List of attributes +LIGO supports these attributes: + @@ -55,26 +57,45 @@ Decorators come in two forms: The most common use of a decorator is to denote an [entrypoint](../syntax/contracts/entrypoints). Decorators are placed immediately before the code they apply to. -You can also apply multiple decorators on one line, as in this example: +You can also apply multiple decorators to the same piece of code, as in this example: ```jsligo group=decorators type storage = int; type result = [list, storage]; -@entry @no_mutation +// @entry +// @no_mutation const sub = (delta: int, storage: storage) : result => [[], storage - delta]; ``` -Note that the lexical convention for decorators clashes with that of [escaped variables](keywords#escaping-keywords). -Therefore, you cannot escape the name of a decorator and use it as a variable name. +Decorators must be in comments unless they are within a class. +For example, this class has decorators that are not in comments: -## List of decorators +```jsligo group=decorators +class Counter { - + @entry + @inline + add = (value: int, storage: storage): result => + [[], storage + value]; + + @entry + @deprecated + sub = (value: int, storage: storage): result => + [[], storage - value]; + + @view + get = (_: unit, storage: storage): storage => storage; +} +``` + +## List of decorators LIGO supports these decorators: + + - [`annot`](../reference/decorators/annot) - [`deprecated`](../reference/decorators/deprecated) - [`dyn_entry`](../reference/decorators/dyn_entry) diff --git a/gitlab-pages/docs/syntax/functions.md b/gitlab-pages/docs/syntax/functions.md index 10637e8e8e..081d495b8e 100644 --- a/gitlab-pages/docs/syntax/functions.md +++ b/gitlab-pages/docs/syntax/functions.md @@ -16,6 +16,7 @@ See [Entrypoints](./contracts/entrypoints). To declare a function, use the `let` keyword, just like declaring variables. +Unlike OCaml, cameLIGO functions can accept multiple parameters. The function declaration includes its parameters, after the function name, following OCaml syntax, as in this example: ```cameligo group=fun_decl @@ -81,6 +82,61 @@ const drop = (x, _y) => x; // _y silently ignored + + +## Chaining functions + +To chain functions, use parentheses to group each function name with its parameters. +For example, assume that you have defined these three functions: + +```cameligo group=paren_grouping +let f (x : int) = x + 1 +let g (x : int) = x - 2 +let h (x : int) = x + x - 3 +``` + +To chain calls to these three functions, you must group them properly with parentheses. +For example, this code passes 42 to function `f`, passes the result to function `g`, and passes that result to function `h`: + +```cameligo group=paren_grouping +let result = h (g (f 42)) +``` + +The parentheses are required in this case. +If you omit the parentheses, CameLIGO runs the functions in order from left to right, as in this example: + +```cameligo skip +(* Previous example without parentheses *) +let result_2 = h g f 42 +(* The resulting value is equivalent to this code: *) +let result_2_equivalent = ((h g) f) 42 +(* These values cause errors because `g` is not a valid parameter of `h`) +``` + +To reduce the amount of parentheses for readability, you can use the reverse-application operator (`|>`). +For example, the expression `f x` can be written as `x |> f`, and the expression `g (f x)` can be written as `x |> f |> g`. +You can think of this expression as "I take `x`, give it to function `f`, and then give the result to function `g`." + +In this way, the expression `let result = h (g (f 42))` can also be written like this: + +```cameligo group=paren_grouping +let result = 42 |> f |> g |> h +``` + +Function application has precedence over the reverse-application operator. +For this reason, `f 42 |> g` is the same as `(f 42) |> g` and not `f (42 |> g)`. +Therefore, this example shows another way to write the expression `let result = h (g (f 42))`: + +```cameligo group=paren_grouping +let result = f 42 |> g |> h +``` + +The reverse-application operator can be useful when you have to deal with a long chain of function calls. + +This operator comes from [OCaml's pervasives](https://v2.ocaml.org/releases/4.02/htmlman/libref/Pervasives.html#6_Compositionoperators). + + + ## Polymorphic functions Polymorphic functions can take advantage of parametric types to accept a wide variety of inputs instead of only a single input type. @@ -252,11 +308,11 @@ Recursive functions are defined and called using the same syntax as non-recursiv ```jsligo group=recursion function sum (n: int, acc: int) : int { - if (n < 1) return acc else return sum (n-1, acc + n); + if (n < 1) return acc; else return sum (n-1, acc + n); }; function fibonacci (n: int, n_1: int, n_0: int): int { - if (n < 2) return n_1 else return fibonacci (n-1, n_1 + n_0, n_1); + if (n < 2) return n_1; else return fibonacci (n-1, n_1 + n_0, n_1); }; ``` @@ -340,12 +396,12 @@ This table shows the results of the command `ligo info measure-contract inline.m To force inlining, use the `@inline` decorator. ```jsligo group=inlining -@inline +// @inline const fst = (p: [nat, nat]) => p[0]; -@entry +// @entry const main = (p: [nat, nat], s: [nat, nat]) : [list, [nat, nat]] => - [[], [fst([p[0], p[1]]), fst([s[1], s[0]])]]; + [[], [fst([p[0], p[1]]), fst([s[1], s[0]])]]; ``` You can measure the difference between inlining and not inlining with the `info measure-contract` command. diff --git a/gitlab-pages/docs/syntax/keywords.md b/gitlab-pages/docs/syntax/keywords.md index 01195e95ae..c5ec95c20a 100644 --- a/gitlab-pages/docs/syntax/keywords.md +++ b/gitlab-pages/docs/syntax/keywords.md @@ -8,30 +8,19 @@ import Syntax from '@theme/Syntax'; _Keywords_ are reserved words that cannot be used as names in declarations. In some cases you can escape keywords to use them as variables or record fields. + + ## Escaping keywords Keywords cannot be used as variables or record fields. If you need to use a keyword as a variable, you can prefix it with `@`, like so: - - ```cameligo group=keywords let @from = ("tz1fakefakefakefakefakefakefakcphLA5" : address) ``` - - -```jsligo group=keywords -const @from = ("tz1fakefakefakefakefakefakefakcphLA5" as address) -``` - -However, you cannot escape decorators in this way because the convention of escaping a keyword with the `@` symbol conflicts with that of *decorators*, as found in JavaScript. -For example, `@entry` is invalid as a variable. - - - ## List of keywords @@ -73,50 +62,3 @@ CameLIGO's keywords are the following:
  • with
  • - - -JsLIGO's keywords are the following: -
      -
    • as
    • -
    • break
    • -
    • case
    • -
    • const
    • -
    • continue
    • -
    • contract_of
    • -
    • default
    • -
    • do
    • -
    • else
    • -
    • export
    • -
    • false
    • -
    • for
    • -
    • from
    • -
    • function
    • -
    • if
    • -
    • implements
    • -
    • import
    • -
    • interface
    • -
    • let
    • -
    • match
    • -
    • namespace
    • -
    • of
    • -
    • parameter_of
    • -
    • return
    • -
    • switch
    • -
    • true
    • -
    • type
    • -
    • when
    • -
    • while
    • -
    • @entry
    • -
    • @deprecated
    • -
    • @dyn_entry
    • -
    • @inline
    • -
    • @view
    • -
    • @no_mutation
    • -
    • @private
    • -
    • @public
    • -
    • @hidden
    • -
    • @thunk
    • -
    • @annot
    • -
    • @layout
    • -
    -
    diff --git a/gitlab-pages/docs/syntax/modules.md b/gitlab-pages/docs/syntax/modules.md index fe9d6d56dd..28572dc8ab 100644 --- a/gitlab-pages/docs/syntax/modules.md +++ b/gitlab-pages/docs/syntax/modules.md @@ -99,15 +99,15 @@ Developers often put a single smart contract in a namespace, but LIGO does not r ::: -Namespaces have some similarities with records because they can both contain multiple definitions. -However, there are significant differences between records and namespaces: +Namespaces have some similarities with objects because they can both contain multiple definitions. +However, there are significant differences between objects and namespaces: -- Records are expressions and therefore can be used as values, and namespaces are not expressions and can't be used as values. +- Objects are expressions and therefore can be used as values, and namespaces are not expressions and can't be used as values. For example, you can pass a record as an argument to a function, but you cannot pass a namespace in this way except in specific circumstances, such using the `contract_of` function to create a contract from a namespace to use in [Testing](../testing). -- Records cannot package type and value definitions together like namespaces can. +- Objects cannot package type and value definitions together like namespaces can. -Which construct you use depends on your design and strategy: namespaces behave like libraries and records behave like individual units of computation. +Which construct you use depends on your design and strategy: namespaces behave like libraries and objects behave like individual units of computation. ## Creating namespaces @@ -123,12 +123,12 @@ It packages together a type (internally called `t`), an operation `add` that sum namespace Euro { export type t = nat; export const add = (a: t, b: t) : t => a + b; - export const one: t = 1n; - export const two: t = 2n; + export const one: t = 1 as nat; + export const two: t = 2 as nat; }; ``` -To access the contents of a namespace, use the name of the namespace and the selection operator "`.`", as with records. +To access the contents of a namespace, use the name of the namespace and the selection operator "`.`", as with objects. For example, this piece of code in the same file defines a value of the `Euro` type and uses the functions and constants in the namespace to manipulate it: ```jsligo group=euro @@ -163,6 +163,83 @@ Client code should always try to respect the interface provided by the namespace +## Importing modules + +You can import modules from other files with the `#import` directive. +See [`#import`](../compiling/preprocessor#import). + + + + + +## Importing namespaces + +You can import namespaces from the same file or other files with the `import` keyword in these ways: + +- `import M = M.O` +- `import * as M from "./targetFile.jsligo"` + +For example, assume that this file is `myFunctions.jsligo`: + +```jsligo group=myFunctions +export namespace MyFunctions { + export const addToImport = (a: int, b: int): int => a + b; + export const subToImport = (a: int, b: int): int => a - b; +} +``` + +You can import the file and access the namespace like this: + +```jsligo group=useMyFunctions +import * as MyFileWithFunctions from './gitlab-pages/docs/syntax/src/modules/myFunctions.jsligo'; +const addToImport = MyFileWithFunctions.MyFunctions.addToImport; +const subToImport = MyFileWithFunctions.MyFunctions.subToImport; + +namespace Counter { + type storage_type = int; + type return_type = [list, storage_type]; + + // @entry + const add = (value: int, storage: storage_type): return_type => + [[], addToImport(storage, value)]; + + // @entry + const sub = (value: int, storage: storage_type): return_type => + [[], subToImport(storage, value)]; + +} +``` + + + + + +## Including modules + +You can include the content of one module inside another with the `include` keyword. +Including another module in this way can be another way to nest and organize code. +It can also allow you to upgrade or extend a module, such as by adding new types, functions, and values. + +This example extends the `Euro` module by including it in a new `NewEuro` module that has a constant for a 10 Euro note: + +```cameligo group=including +module Euro = + struct + type t = nat + let add (a, b : t * t) : t = a + b + let one : t = 1n + let two : t = 2n + end + +module NewEuro = + struct + include Euro + let ten : t = 10n + end +``` + +Including modules is not possible in JsLIGO. + ## Nesting modules You can define a module inside another module. @@ -208,8 +285,8 @@ namespace Euro { export let add = (a: t, b: t): t => a + b; export namespace Coin { - export let one: t = 1n; - export let two: t = 2n; + export let one: t = 1 as nat; + export let two: t = 2 as nat; }; }; ``` @@ -263,8 +340,8 @@ For example, until 2025, the Bulgarian Lev is pegged to the euro currency, so th namespace Euro { export type t = nat; export const add = (a: t, b: t) : t => a + b; - export const one: t = 1n; - export const two: t = 2n; + export const one: t = 1 as nat; + export const two: t = 2 as nat; }; import Bulgarian_Lev = Euro; @@ -279,49 +356,3 @@ You must use the `import` keyword to alias the namespace, even if it is in the s ::: - - - -## Including modules - -You can include the content of one module inside another with the `include` keyword. -Including another module in this way can be another way to nest and organize code. -It can also allow you to upgrade or extend a module, such as by adding new types, functions, and values. - -This example extends the `Euro` module by including it in a new `NewEuro` module that has a constant for a 10 Euro note: - -```cameligo group=including -module Euro = - struct - type t = nat - let add (a, b : t * t) : t = a + b - let one : t = 1n - let two : t = 2n - end - -module NewEuro = - struct - include Euro - let ten : t = 10n - end -``` - -Including modules is not possible in JsLIGO. - - - - - -## Importing modules - -You can import modules from other files with the `#import` directive. -See [`#import`](../compiling/preprocessor#import). - - - - - -You can import namespaces from other files with the `#import` directive, but only if the namespaces have the `@public` decorator. -See [`#import`](../compiling/preprocessor#import). - - diff --git a/gitlab-pages/docs/syntax/signatures.md b/gitlab-pages/docs/syntax/signatures.md index 0b92fcd99b..76467717a0 100644 --- a/gitlab-pages/docs/syntax/signatures.md +++ b/gitlab-pages/docs/syntax/signatures.md @@ -48,42 +48,43 @@ The module must instantiate any abstract type in the signature, as this module d -An interface is a list of types and values that you can apply to a namespace. -When you apply an interface to a namespace, that namespace must have all of the types and values in the interface. -The namespace can also have additional definitions that are not in the interface. -The LIGO compiler warns you of any mismatches between the interface and the namespace. +An interface is a list properties that you can apply to a class. When +you apply an interface to a class, that class must have all of the +properties declared in the interface. The class can also have +additional definitions that are not in the interface. The LIGO +compiler warns you of any mismatches between the interface and the +class. -For example, the following interface specifies that a namespace must have these contents: +For example, the following interface specifies that a class must have these contents: -- A type named `t`, although the data type of that type is not specified, which makes it an *abstract type* -- A function named `add`, which accepts two values of type `t` and returns a value of type `t` -- Values `one` and `two`, which are of the type `t` +- A property named `add`, which accepts two values of type `nat` and returns a value of type `nat` +- Values `one` and `two`, which are of the type `nat` ```jsligo group=intf_declaring interface Euro_INTF { - type t; - const add: (a: t, b: t) => t; - const one: t; - const two: t; + add: (a: nat, b: nat) => nat; + one: nat; + two: nat; }; ``` -To apply an interface to a namespace, put the name of the interface after the keyword `implements` and the namespace name, as in the following example. -It's said that the namespace *implements* the interface. -This namespace defines the type `t` as a nat and defines the `add` function and the `one` and `two` values. -It also adds a function named `multiply` that is not specified in the interface: +To apply an interface to a class, put the name of the interface after +the keyword `implements` and the class name, as in the following +example. We say that the class *implements* the interface. This +class defines the properties `add`, `one` and `two`. It also adds a +property named `multiply` that is not specified in the interface: ```jsligo group=intf_declaring -namespace Euro implements Euro_INTF { - export type t = nat; // No more abstract - export const add = (a: t, b: t): t => a + b; - export const one: t = 1n; - export const two: t = 2n; - export const multiply = (a: t, b: t): t=> a * b; +class Euro implements Euro_INTF { + static add = (a: nat, b: nat): nat => a + b; + static one = 1 as nat; + static two : nat = 2 as nat; + multiply = (a: nat, b: nat): nat => a * b; }; ``` -The namespace must instantiate any abstract type in the interface, as this namespace defines the abstract type `t` as a nat. +Note how properties from the interface must be defined as `static` in +the class. @@ -122,54 +123,53 @@ Interfaces can be extended by inheritance with the `extends` keyword, as in this ```jsligo group=intf_extending interface Euro_INTF { - type t; - const add: (a: t, b: t) => t; - const one: t; - const two: t; + add: (a: nat, b: nat) => nat; + one: nat; + two: nat; }; interface WithTenEuro_INTF extends Euro_INTF { - const ten: t; + ten: nat; }; interface WithFiftyEuro_INTF extends Euro_INTF { - const fifty: t; + fifty: nat; }; ``` -Note that the type `t` remains abstract in all of the interfaces. -Namespaces that use any of these interfaces must instantiate the type. - -Interfaces can extend more than one interface, which can lead to an interface that extends a base interface more than once, known as *diamond inheritance*. -For example, the following interface extends two interfaces from the previous example. -Because both of these interfaces extend the same base interface, it is as if the interface extends the base interface twice. -Diamond inheritance doesn't cause any problems for the interface. +Interfaces can extend more than one interface, which can lead to an +interface that extends a base interface more than once, known as +*diamond inheritance*. For example, the following interface extends +two interfaces from the previous example. Because both of these +interfaces extend the same base interface, it is as if the interface +extends the base interface twice. Diamond inheritance doesn't cause +any problems for the interface. ```jsligo group=intf_extending interface NewEuro_INTF extends WithTenEuro_INTF, WithFiftyEuro_INTF { - const hundred: t; - const five_hundred?: t; + hundred: nat; + five_hundred?: nat; }; ``` -Interfaces can have optional types and values indicated with a question mark `?`. -In the previous example, the interface `NewEuro_INTF` has an optional value `five_hundred`. -This namespace defines this optional value and adds a value named `twenty` that is not defined in the `NewEuro_INTF` interface: +Interfaces can have optional types and values indicated with a +question mark `?`. In the previous example, the interface +`NewEuro_INTF` has an optional property `five_hundred`. This class +defines this optional value and adds a property named `twenty` that is +not defined in the `NewEuro_INTF` interface: ```jsligo group=intf_extending -namespace NewEuro implements NewEuro_INTF { - export type t = int; - - export const add = (a: t, b: t) => a + b; - - export const one: t = 1; - export const two: t = 2; - export const ten: t = 10; - export const fifty: t = 50; - export const hundred: t = 100; - export const five_hundred: t = 500; // Could be omitted - const twenty: t = 20; // Extra new constant +class NewEuro implements NewEuro_INTF { + static add = (a: nat, b: nat) => a + b; + + static one: nat = 1; + static two: nat = 2; + static ten: nat = 10; + static fifty: nat = 50; + static hundred: nat = 100; + static five_hundred: nat = 500; // Could be omitted + static twenty: nat = 20; // Extra new constant } ``` diff --git a/gitlab-pages/docs/syntax/src/classes/classes.jsligo b/gitlab-pages/docs/syntax/src/classes/classes.jsligo new file mode 100644 index 0000000000..7838fedc4c --- /dev/null +++ b/gitlab-pages/docs/syntax/src/classes/classes.jsligo @@ -0,0 +1,12 @@ +type storage_type = int; +type return_type = [list, storage_type]; + +class Counter { + @entry + add = (value: int, storage: storage_type): return_type => + [[], storage + value]; + + @entry + sub = (value: int, storage: storage_type): return_type => + [[], storage - value]; +} \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/classes/exported_class.jsligo b/gitlab-pages/docs/syntax/src/classes/exported_class.jsligo new file mode 100644 index 0000000000..a5cc009798 --- /dev/null +++ b/gitlab-pages/docs/syntax/src/classes/exported_class.jsligo @@ -0,0 +1,4 @@ +export class MyFunctions { + static add = (a: int, b: int): int => a + b; + static sub = (a: int, b: int): int => a - b; +} \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/classes/import_classes_local.jsligo b/gitlab-pages/docs/syntax/src/classes/import_classes_local.jsligo new file mode 100644 index 0000000000..9806010b46 --- /dev/null +++ b/gitlab-pages/docs/syntax/src/classes/import_classes_local.jsligo @@ -0,0 +1,19 @@ +namespace MyTypes { + export type storage_type = int; + export type return_type = [list, storage_type]; +} + +class MyFunctions { + static add = (a: int, b: int): int => a + b; + static sub = (a: int, b: int): int => a - b; +} + +class Counter { + @entry + add = (value: int, storage: MyTypes.storage_type): MyTypes.return_type => + [[], MyFunctions.add(storage, value)]; + + @entry + sub = (value: int, storage: MyTypes.storage_type): MyTypes.return_type => + [[], MyFunctions.add(storage, value)]; +} \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/classes/imports_classes.jsligo b/gitlab-pages/docs/syntax/src/classes/imports_classes.jsligo new file mode 100644 index 0000000000..4c0d499b06 --- /dev/null +++ b/gitlab-pages/docs/syntax/src/classes/imports_classes.jsligo @@ -0,0 +1,14 @@ +type storage_type = int; +type return_type = [list, storage_type]; + +import * as MyImportedFunctions from "gitlab-pages/docs/syntax/src/classes/exported_class.jsligo"; + +class Calculator { + @entry + add = (value: int, storage: storage_type): return_type => + [[], MyImportedFunctions.MyFunctions.add(storage, value)]; + + @entry + sub = (value: int, storage: storage_type): return_type => + [[], MyImportedFunctions.MyFunctions.sub(storage, value)]; +} \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/comments/comments.jsligo b/gitlab-pages/docs/syntax/src/comments/comments.jsligo index ed25e71e63..7bfea6be20 100644 --- a/gitlab-pages/docs/syntax/src/comments/comments.jsligo +++ b/gitlab-pages/docs/syntax/src/comments/comments.jsligo @@ -1,7 +1,4 @@ /* This is a multi-line comment */ -/* This is a multi-line comment. - /* This is a "nested" comment. */ -*/ const x = 10; // This is a single line comment \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/decorators/decorators.jsligo b/gitlab-pages/docs/syntax/src/decorators/decorators.jsligo index 3ac56ac237..8de008dc63 100644 --- a/gitlab-pages/docs/syntax/src/decorators/decorators.jsligo +++ b/gitlab-pages/docs/syntax/src/decorators/decorators.jsligo @@ -1,6 +1,22 @@ type storage = int; type result = [list, storage]; -@entry @no_mutation +// @entry +// @no_mutation const sub = (delta: int, storage: storage) : result => - [[], storage - delta]; \ No newline at end of file + [[], storage - delta]; +class Counter { + + @entry + @inline + add = (value: int, storage: storage): result => + [[], storage + value]; + + @entry + @deprecated + sub = (value: int, storage: storage): result => + [[], storage - value]; + + @view + get = (_: unit, storage: storage): storage => storage; +} \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/functions/inlining.jsligo b/gitlab-pages/docs/syntax/src/functions/inlining.jsligo index 7aa2c16d87..808bc36bd0 100644 --- a/gitlab-pages/docs/syntax/src/functions/inlining.jsligo +++ b/gitlab-pages/docs/syntax/src/functions/inlining.jsligo @@ -1,6 +1,6 @@ -@inline +// @inline const fst = (p: [nat, nat]) => p[0]; -@entry +// @entry const main = (p: [nat, nat], s: [nat, nat]) : [list, [nat, nat]] => - [[], [fst([p[0], p[1]]), fst([s[1], s[0]])]]; \ No newline at end of file + [[], [fst([p[0], p[1]]), fst([s[1], s[0]])]]; \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/functions/paren_grouping.mligo b/gitlab-pages/docs/syntax/src/functions/paren_grouping.mligo new file mode 100644 index 0000000000..bc0b910561 --- /dev/null +++ b/gitlab-pages/docs/syntax/src/functions/paren_grouping.mligo @@ -0,0 +1,6 @@ +let f (x : int) = x + 1 +let g (x : int) = x - 2 +let h (x : int) = x + x - 3 +let result = h (g (f 42)) +let result = 42 |> f |> g |> h +let result = f 42 |> g |> h \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/functions/recursion.jsligo b/gitlab-pages/docs/syntax/src/functions/recursion.jsligo index 7e598950fb..922f19b7f2 100644 --- a/gitlab-pages/docs/syntax/src/functions/recursion.jsligo +++ b/gitlab-pages/docs/syntax/src/functions/recursion.jsligo @@ -1,7 +1,7 @@ function sum (n: int, acc: int) : int { - if (n < 1) return acc else return sum (n-1, acc + n); + if (n < 1) return acc; else return sum (n-1, acc + n); }; function fibonacci (n: int, n_1: int, n_0: int): int { - if (n < 2) return n_1 else return fibonacci (n-1, n_1 + n_0, n_1); + if (n < 2) return n_1; else return fibonacci (n-1, n_1 + n_0, n_1); }; \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/keywords/keywords.jsligo b/gitlab-pages/docs/syntax/src/keywords/keywords.jsligo deleted file mode 100644 index 2840edfc1a..0000000000 --- a/gitlab-pages/docs/syntax/src/keywords/keywords.jsligo +++ /dev/null @@ -1 +0,0 @@ -const @from = ("tz1fakefakefakefakefakefakefakcphLA5" as address) \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/modules/Lev.jsligo b/gitlab-pages/docs/syntax/src/modules/Lev.jsligo index e91954178a..a0cd6724e1 100644 --- a/gitlab-pages/docs/syntax/src/modules/Lev.jsligo +++ b/gitlab-pages/docs/syntax/src/modules/Lev.jsligo @@ -1,8 +1,8 @@ namespace Euro { export type t = nat; export const add = (a: t, b: t) : t => a + b; - export const one: t = 1n; - export const two: t = 2n; + export const one: t = 1 as nat; + export const two: t = 2 as nat; }; import Bulgarian_Lev = Euro; \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/modules/euro.jsligo b/gitlab-pages/docs/syntax/src/modules/euro.jsligo index 4bf4778958..e742e10603 100644 --- a/gitlab-pages/docs/syntax/src/modules/euro.jsligo +++ b/gitlab-pages/docs/syntax/src/modules/euro.jsligo @@ -3,8 +3,8 @@ namespace Euro { export type t = nat; export const add = (a: t, b: t) : t => a + b; - export const one: t = 1n; - export const two: t = 2n; + export const one: t = 1 as nat; + export const two: t = 2 as nat; }; type euro_balance = Euro.t; diff --git a/gitlab-pages/docs/syntax/src/modules/intf_declaring.jsligo b/gitlab-pages/docs/syntax/src/modules/intf_declaring.jsligo deleted file mode 100644 index 7822e342c3..0000000000 --- a/gitlab-pages/docs/syntax/src/modules/intf_declaring.jsligo +++ /dev/null @@ -1,13 +0,0 @@ -interface Euro_INTF { - type t; - const add: (a: t, b: t) => t; - const one: t; - const two: t; -}; -namespace Euro implements Euro_INTF { - export type t = nat; // No more abstract - export const add = (a: t, b: t): t => a + b; - export const one: t = 1n; - export const two: t = 2n; - export const multiply = (a: t, b: t): t=> a * b; -}; \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/modules/intf_extending.jsligo b/gitlab-pages/docs/syntax/src/modules/intf_extending.jsligo deleted file mode 100644 index 46131c593f..0000000000 --- a/gitlab-pages/docs/syntax/src/modules/intf_extending.jsligo +++ /dev/null @@ -1,32 +0,0 @@ -interface Euro_INTF { - type t; - const add: (a: t, b: t) => t; - const one: t; - const two: t; -}; - -interface WithTenEuro_INTF extends Euro_INTF { - const ten: t; -}; - -interface WithFiftyEuro_INTF extends Euro_INTF { - const fifty: t; -}; -interface NewEuro_INTF - extends WithTenEuro_INTF, WithFiftyEuro_INTF { - const hundred: t; - const five_hundred?: t; -}; -namespace NewEuro implements NewEuro_INTF { - export type t = int; - - export const add = (a: t, b: t) => a + b; - - export const one: t = 1; - export const two: t = 2; - export const ten: t = 10; - export const fifty: t = 50; - export const hundred: t = 100; - export const five_hundred: t = 500; // Could be omitted - const twenty: t = 20; // Extra new constant -} \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/modules/myFunctions.jsligo b/gitlab-pages/docs/syntax/src/modules/myFunctions.jsligo new file mode 100644 index 0000000000..3491beab43 --- /dev/null +++ b/gitlab-pages/docs/syntax/src/modules/myFunctions.jsligo @@ -0,0 +1,4 @@ +export namespace MyFunctions { + export const addToImport = (a: int, b: int): int => a + b; + export const subToImport = (a: int, b: int): int => a - b; +} \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/modules/namespace_nesting.jsligo b/gitlab-pages/docs/syntax/src/modules/namespace_nesting.jsligo index 5db179fd2e..79d5fbb596 100644 --- a/gitlab-pages/docs/syntax/src/modules/namespace_nesting.jsligo +++ b/gitlab-pages/docs/syntax/src/modules/namespace_nesting.jsligo @@ -4,8 +4,8 @@ namespace Euro { export let add = (a: t, b: t): t => a + b; export namespace Coin { - export let one: t = 1n; - export let two: t = 2n; + export let one: t = 1 as nat; + export let two: t = 2 as nat; }; }; type euro_balance = Euro.t; diff --git a/gitlab-pages/docs/syntax/src/modules/sig_declaring.mligo b/gitlab-pages/docs/syntax/src/modules/sig_declaring.mligo deleted file mode 100644 index 4b01b05b80..0000000000 --- a/gitlab-pages/docs/syntax/src/modules/sig_declaring.mligo +++ /dev/null @@ -1,14 +0,0 @@ -module type Euro_SIG = - sig - type t - val add : t * t -> t - val one : t - val two : t - end -module Euro : Euro_SIG = - struct - type t = nat - let add (a, b : t * t) = a + b - let one : t = 1n - let two : t = 2n - end \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/modules/sig_including.mligo b/gitlab-pages/docs/syntax/src/modules/sig_including.mligo deleted file mode 100644 index cb544159a6..0000000000 --- a/gitlab-pages/docs/syntax/src/modules/sig_including.mligo +++ /dev/null @@ -1,13 +0,0 @@ -module type Euro_SIG = - sig - type t - val add : t * t -> t - val one : t - val two : t - end - -module type NewEuro_SIG = - sig - include Euro_SIG - val ten : t - end \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/modules/useMyFunctions.jsligo b/gitlab-pages/docs/syntax/src/modules/useMyFunctions.jsligo new file mode 100644 index 0000000000..ea518520b6 --- /dev/null +++ b/gitlab-pages/docs/syntax/src/modules/useMyFunctions.jsligo @@ -0,0 +1,17 @@ +import * as MyFileWithFunctions from './gitlab-pages/docs/syntax/src/modules/myFunctions.jsligo'; +const addToImport = MyFileWithFunctions.MyFunctions.addToImport; +const subToImport = MyFileWithFunctions.MyFunctions.subToImport; + +namespace Counter { + type storage_type = int; + type return_type = [list, storage_type]; + + // @entry + const add = (value: int, storage: storage_type): return_type => + [[], addToImport(storage, value)]; + + // @entry + const sub = (value: int, storage: storage_type): return_type => + [[], subToImport(storage, value)]; + +} \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/signatures/intf_declaring.jsligo b/gitlab-pages/docs/syntax/src/signatures/intf_declaring.jsligo index 7822e342c3..bba07c37a6 100644 --- a/gitlab-pages/docs/syntax/src/signatures/intf_declaring.jsligo +++ b/gitlab-pages/docs/syntax/src/signatures/intf_declaring.jsligo @@ -1,13 +1,11 @@ interface Euro_INTF { - type t; - const add: (a: t, b: t) => t; - const one: t; - const two: t; + add: (a: nat, b: nat) => nat; + one: nat; + two: nat; }; -namespace Euro implements Euro_INTF { - export type t = nat; // No more abstract - export const add = (a: t, b: t): t => a + b; - export const one: t = 1n; - export const two: t = 2n; - export const multiply = (a: t, b: t): t=> a * b; +class Euro implements Euro_INTF { + static add = (a: nat, b: nat): nat => a + b; + static one = 1 as nat; + static two : nat = 2 as nat; + multiply = (a: nat, b: nat): nat => a * b; }; \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/signatures/intf_extending.jsligo b/gitlab-pages/docs/syntax/src/signatures/intf_extending.jsligo index 46131c593f..6c67212434 100644 --- a/gitlab-pages/docs/syntax/src/signatures/intf_extending.jsligo +++ b/gitlab-pages/docs/syntax/src/signatures/intf_extending.jsligo @@ -1,32 +1,29 @@ interface Euro_INTF { - type t; - const add: (a: t, b: t) => t; - const one: t; - const two: t; + add: (a: nat, b: nat) => nat; + one: nat; + two: nat; }; interface WithTenEuro_INTF extends Euro_INTF { - const ten: t; + ten: nat; }; interface WithFiftyEuro_INTF extends Euro_INTF { - const fifty: t; + fifty: nat; }; interface NewEuro_INTF extends WithTenEuro_INTF, WithFiftyEuro_INTF { - const hundred: t; - const five_hundred?: t; + hundred: nat; + five_hundred?: nat; }; -namespace NewEuro implements NewEuro_INTF { - export type t = int; +class NewEuro implements NewEuro_INTF { + static add = (a: nat, b: nat) => a + b; - export const add = (a: t, b: t) => a + b; - - export const one: t = 1; - export const two: t = 2; - export const ten: t = 10; - export const fifty: t = 50; - export const hundred: t = 100; - export const five_hundred: t = 500; // Could be omitted - const twenty: t = 20; // Extra new constant + static one: nat = 1; + static two: nat = 2; + static ten: nat = 10; + static fifty: nat = 50; + static hundred: nat = 100; + static five_hundred: nat = 500; // Could be omitted + static twenty: nat = 20; // Extra new constant } \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/variables/constants.jsligo b/gitlab-pages/docs/syntax/src/variables/constants.jsligo index 92a73a5bb9..f8ae0a2016 100644 --- a/gitlab-pages/docs/syntax/src/variables/constants.jsligo +++ b/gitlab-pages/docs/syntax/src/variables/constants.jsligo @@ -1,9 +1,9 @@ const a = 1; const b : int = a; // Type ascription (a.k.a. annotation) -const d = do { +const d = (() => { const x = 1; { const x = 2; // No error: a sub-block return x; } -}; \ No newline at end of file +})(); \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/variables/silent_variables.jsligo b/gitlab-pages/docs/syntax/src/variables/silent_variables.jsligo index afe33f978e..4ff97e24d2 100644 --- a/gitlab-pages/docs/syntax/src/variables/silent_variables.jsligo +++ b/gitlab-pages/docs/syntax/src/variables/silent_variables.jsligo @@ -1,2 +1,13 @@ -@entry -const reset = (_param : unit, _storage : int) : [list, int] => [[], 0]; \ No newline at end of file +// @entry +const reset = (_param : unit, _storage : int) : [list, int] => [[], 0]; +type user = { + id : int, + is_admin : bool, + name : string +}; + +const getUserID = (user: user): int => { + const { id, is_admin, name } = user; + ignore([is_admin, name]); + return id; +} \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/src/variables/silent_variables.mligo b/gitlab-pages/docs/syntax/src/variables/silent_variables.mligo index 62b954f22b..19e57ad599 100644 --- a/gitlab-pages/docs/syntax/src/variables/silent_variables.mligo +++ b/gitlab-pages/docs/syntax/src/variables/silent_variables.mligo @@ -1 +1,12 @@ -[@entry] let reset (_param: unit) (_storage : int) : operation list * int = [], 0 \ No newline at end of file +[@entry] +let reset (_param: unit) (_storage : int) : operation list * int = [], 0 +type user = { + id : int; + is_admin : bool; + name : string +} + +let getUserID (user : user) : int = + let { id; is_admin; name } = user in + let () = ignore ([is_admin, name]) in + id \ No newline at end of file diff --git a/gitlab-pages/docs/syntax/variables.md b/gitlab-pages/docs/syntax/variables.md index 4661a7582e..8a476dc975 100644 --- a/gitlab-pages/docs/syntax/variables.md +++ b/gitlab-pages/docs/syntax/variables.md @@ -50,17 +50,54 @@ If the entrypoint code doesn't access one or both of these arguments, prefix the ```cameligo group=silent_variables -[@entry] let reset (_param: unit) (_storage : int) : operation list * int = [], 0 +[@entry] +let reset (_param: unit) (_storage : int) : operation list * int = [], 0 ``` - ```jsligo group=silent_variables -@entry +// @entry const reset = (_param : unit, _storage : int) : [list, int] => [[], 0]; ``` + + +You can also use the predefined function `ignore` on variables that +you do not use, as in this example: + + + +```cameligo group=silent_variables +type user = { + id : int; + is_admin : bool; + name : string +} + +let getUserID (user : user) : int = + let { id; is_admin; name } = user in + let () = ignore ([is_admin, name]) in + id +``` + + + + + +```jsligo group=silent_variables +type user = { + id : int, + is_admin : bool, + name : string +}; + +const getUserID = (user: user): int => { + const { id, is_admin, name } = user; + ignore([is_admin, name]); + return id; +} +``` @@ -106,13 +143,13 @@ const x = 2; // Yields an error However, the following example works because the constants are in different scopes: ```jsligo group=constants -const d = do { +const d = (() => { const x = 1; { const x = 2; // No error: a sub-block return x; } -}; +})(); ```
    diff --git a/gitlab-pages/docs/testing/michelson_testing.md b/gitlab-pages/docs/testing/michelson_testing.md index 9fad8bcf65..fd84f8e497 100644 --- a/gitlab-pages/docs/testing/michelson_testing.md +++ b/gitlab-pages/docs/testing/michelson_testing.md @@ -57,11 +57,11 @@ type storage = string; type result = [list, storage]; -@entry +// @entry const append = (s: string, storage: storage): result => [[], storage + s] -@entry +// @entry const reset = (_: unit, _storage: storage): result => [[], ""] ``` diff --git a/gitlab-pages/docs/testing/mutation-testing.md b/gitlab-pages/docs/testing/mutation-testing.md index 6ac2ac6ed8..b8edca4f05 100644 --- a/gitlab-pages/docs/testing/mutation-testing.md +++ b/gitlab-pages/docs/testing/mutation-testing.md @@ -36,7 +36,6 @@ To test this function, you might provide some input and output values as test ca ```cameligo test-ligo group=twice -module Test = Test.Next let simple_tests (f : int -> int) = (* Test 1 *) let () = Assert.assert (Test.Compare.eq (Test.Michelson.run f 0) (Test.Michelson.eval 0)) in @@ -52,7 +51,6 @@ let test = simple_tests twice ```jsligo test-ligo group=twice -import Test = Test.Next; const simple_tests = (f : ((input: int) => int)) : unit => { /* Test 1 */ Assert.assert(Test.Compare.eq(Test.Michelson.run(f, 0), Test.Michelson.eval(0))); @@ -119,9 +117,13 @@ That result suggests that the tests are not good enough to distinguish a good im To help you add more test cases and ensure that the tests are complete, you can use mutation testing to identify different versions of the function (known as _mutations_) that pass all of the tests. -The `Test.Next.Mutation.func` function takes a value to mutate (usually a function) and a test case function to apply to mutated versions of that value. -If the test case function terminates correctly, the `Test.Next.Mutation.func` function stops trying mutations and returns a `Some` option with the mutation that passed all of the tests. -If no mutation passes the test case function, the `Test.Next.Mutation.func` function returns `None`. +The `Test.Mutation.func` function takes a value to mutate (usually a +function) and a test case function to apply to mutated versions of +that value. If the test case function terminates correctly, the +`Test.Mutation.func` function stops trying mutations and returns +a `Some` option with the mutation that passed all of the tests. If no +mutation passes the test case function, the `Test.Mutation.func` +function returns `None`. @@ -134,7 +136,7 @@ val Test.mutation_test : 'a -> ('a -> 'b) -> ('b * mutation) option ```jsligo skip -val Test.mutation_test : (value: 'a, tester: ('a -> 'b)) => option <['b, mutation]> +Test.mutation_test : (value: 'a, tester: ('a -> 'b)) => option <['b, mutation]> ``` @@ -158,13 +160,13 @@ let test_mutation = ```jsligo test-ligo group=twice const test_mutation = - match(Test.Mutation.func(twice, simple_tests)) { - when(None()): unit; - when(Some(pmutation)): do { + $match(Test.Mutation.func(twice, simple_tests), { + "None": () => unit, + "Some": pmutation => (() => { Test.IO.log(pmutation[1]); Test.IO.println("Some mutation also passes the tests! ^^") - } - }; + })() + }); ``` @@ -178,7 +180,6 @@ ligo run test gitlab-pages/docs/testing/src/mutation-testing/twice.mligo # Outputs: # Mutation at: File "gitlab-pages/docs/testing/src/mutation-testing/twice.mligo", line 1, characters 22-27: # 1 | let twice (x : int) = x + x -# 2 | module Test = Test.Next # # Replacing by: x * x. # @@ -194,7 +195,6 @@ ligo run test gitlab-pages/docs/testing/src/mutation-testing/twice.jsligo # Outputs: # Mutation at: File "/Users/timothymcmackin/tezos/ligo/gitlab-pages/docs/testing/src/mutation-testing/twice.jsligo", line 1, characters 26-31: # 1 | const twice = (x: int) => x + x; -# 2 | import Test = Test.Next; # # Replacing by: x * x. # @@ -288,9 +288,10 @@ end export namespace AddSub { export type storage = int; - @entry + // @entry const add = (delta: int, storage: storage): [list, storage] => [[], storage + delta]; - @entry + + // @entry const sub = (delta: int, storage: storage): [list, storage] => [[], storage - delta]; } ``` @@ -307,10 +308,11 @@ Note that the test uses a function named `tester` to deploy the contract and run ```cameligo test-ligo group=mutation-contract-test (* This is mutation-contract-test.mligo *) -#import "gitlab-pages/docs/testing/src/mutation-testing/mutation-contract.mligo" "MutationContract" -module Test = Test.Next +module MutationContract = Gitlab_pages.Docs.Testing.Src.Mutation_testing.Mutation_contract + type storage = MutationContract.AddSub.storage type param = MutationContract.AddSub parameter_of + let initial_storage = 7 let tester (taddr : (param, storage) typed_address) (_ : (param ,storage) michelson_contract) (_:int) : unit = @@ -327,21 +329,25 @@ let test_original = ```jsligo test-ligo group=mutation-contract-test -// This is mutation-contract-test.jsligo +import * as MutationContract from "gitlab-pages/docs/testing/src/mutation-testing/mutation-contract.jsligo"; -#import "gitlab-pages/docs/testing/src/mutation-testing/mutation-contract.jsligo" "MutationContract" -import Test = Test.Next; type storage = int; -type param = parameter_of MutationContract.AddSub; +type param = parameter_of; + const initial_storage = 7; -const tester = (taddr: typed_address, _c: michelson_contract , _: int): unit => { - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("add", taddr), 7, 0tez); +const tester = (taddr: typed_address, + _c: michelson_contract, + _: int): unit => { + Test.Contract.transfer_exn( + Test.Typed_address.get_entrypoint("add", taddr), 7, 0 as tez); Assert.assert(Test.Typed_address.get_storage(taddr) == initial_storage + 7); } const test_original = (() => { - let orig = Test.Originate.contract(contract_of(MutationContract.AddSub), initial_storage, 0tez); + let orig = + Test.Originate.contract + (contract_of(MutationContract.AddSub), initial_storage, 0 as tez); return tester(orig.taddr); })(); ``` @@ -370,16 +376,20 @@ let test_mutation = ```jsligo test-ligo group=mutation-contract-test const test_mutation = - match(Test.Mutation.contract(contract_of(MutationContract.AddSub), initial_storage, 0tez, tester)) { - when(None()): unit; - when(Some(pmutation)): do { + $match(Test.Mutation.contract( + contract_of(MutationContract.AddSub), + initial_storage, + 0 as tez, + tester), { + "None": () => unit, + "Some": pmutation => (() => { Test.IO.log(pmutation[1]); // In a real program, one would write `failwith "A mutation passes"` // Because we want to demonstrate the issue without an actual error // a milder println is used in this document. Test.IO.println("A mutation of the contract still passes the tests!"); - } - }; + })() + }); ``` @@ -429,9 +439,9 @@ The following updated test adds a call to the `Sub` entrypoint to test it: ```cameligo test-ligo group=mutation-contract-test let tester_add_and_sub (taddr : (param, storage) typed_address) (_ : (param, storage) michelson_contract) (_ : int) : unit = let _ = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "add" taddr) 7 0tez in - let () = Assert.assert (Test.get_storage taddr = initial_storage + 7) in + let () = Assert.assert (Test.Typed_address.get_storage taddr = initial_storage + 7) in let _ = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "sub" taddr) 3 0tez in - Assert.assert (Test.get_storage taddr = initial_storage + 4) + Assert.assert (Test.Typed_address.get_storage taddr = initial_storage + 4) let test_mutation_sub = match Test.Mutation.contract (contract_of MutationContract.AddSub) initial_storage 0tez tester_add_and_sub with @@ -446,22 +456,30 @@ let test_mutation_sub = ```jsligo test-ligo group=mutation-contract-test -const tester_add_and_sub = (taddr: typed_address, _c: michelson_contract, _i: int): unit => { - - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("add", taddr), 7, 0tez); +const tester_add_and_sub = + (taddr: typed_address, + _c: michelson_contract, + _i: int): unit => { + Test.Contract.transfer_exn + (Test.Typed_address.get_entrypoint("add", taddr), 7, 0 as tez); Assert.assert(Test.Typed_address.get_storage(taddr) == initial_storage + 7); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("sub", taddr), 3, 0tez); + Test.Contract.transfer_exn + (Test.Typed_address.get_entrypoint("sub", taddr), 3, 0 as tez); Assert.assert(Test.Typed_address.get_storage(taddr) == initial_storage + 4); } const test_mutation_sub = - match(Test.Mutation.contract(contract_of(MutationContract.AddSub), initial_storage, 0tez, tester_add_and_sub)) { - when(None()): unit; - when(Some(pmutation)): do { + $match(Test.Mutation.contract( + contract_of(MutationContract.AddSub), + initial_storage, + 0 as tez, + tester_add_and_sub), { + "None": () => unit, + "Some": pmutation => (() => { Test.IO.log(pmutation[1]); Test.IO.println("A mutation of the contract still passes the tests!"); - } - }; + })() + }); ``` @@ -470,9 +488,13 @@ When this test runs, it finds that no mutation of the `Sub` entrypoint passes al ## Returning multiple mutations -In the previous examples, the functions `Test.Next.Mutation.func` and `Test.Next.Mutation.contract` return an option that contains either None or Some with a single mutation that passes the tests. -To speed up the process of eliminating mutations, you can use the `Test.Next.Mutation.All.func` and `Test.Next.Mutation.All.contract` functions to get every mutation that passes the tests. -These functions return a list of mutations instead of an option. +In the previous examples, the functions `Test.Mutation.func` and +`Test.Mutation.contract` return an option that contains either None or +Some with a single mutation that passes the tests. To speed up the +process of eliminating mutations, you can use the +`Test.Mutation.All.func` and `Test.Mutation.All.contract` functions to +get every mutation that passes the tests. These functions return a +list of mutations instead of an option. This example gets every mutation that passes the tests for the `twice` function: @@ -493,25 +515,27 @@ let get_all_mutations = ```jsligo test-ligo group=twice const get_all_mutations = - match(Test.Mutation.All.func(twice, simple_tests)) { - when([]): unit; - when([hd,...tl]): do { + $match(List.head_and_tail(Test.Mutation.All.func(twice, simple_tests)), { + "None": () => unit, + "Some": ([hd,tl]) => (() => { let mutations = list([hd,...tl]); Test.IO.println("Some mutations also pass the tests!"); for (const m of mutations) { let [_, mutation] = m; Test.IO.log(mutation); }; - } - }; + })() + }); ``` In this case, the output is the same because only one mutation passed all of the tests. -Similarly, the `Test.Next.Mutation.All.contract` function returns a list of all contract mutations that pass the tests. -For example, this test adapts the contract test in the previous section to return every passing mutation: +Similarly, the `Test.Mutation.All.contract` function returns a list of +all contract mutations that pass the tests. For example, this test +adapts the contract test in the previous section to return every +passing mutation: @@ -557,17 +581,22 @@ Replacing by: storage * delta. ```jsligo test-ligo group=mutation-contract-test const test_mutation_all = - match(Test.Mutation.All.contract(contract_of(MutationContract.AddSub), initial_storage, 0tez, tester)) { - when([]): unit; - when([hd,...tl]): do { + $match(List.head_and_tail( + Test.Mutation.All.contract( + contract_of(MutationContract.AddSub), + initial_storage, + 0 as tez, + tester)), { + "None": () => unit, + "Some": ([hd,tl]) => (() => { let mutations = list([hd,...tl]); let _p = Test.IO.println("Some mutations also pass the tests!"); for (const m of mutations) { let [_, mutation] = m; Test.IO.log(mutation); }; - } - }; + })() + }); ``` In this case, the output shows that multiple mutations pass the tests: @@ -618,7 +647,7 @@ type result = operation list * storage (* Two entrypoints *) [@entry] let add (delta : int) (store : storage) : result = - [@no_mutation] let _ = assert (0 = 0) in + [@no_mutation] let _ = Assert.assert (0 = 0) in [], store + delta [@entry] [@no_mutation] @@ -643,17 +672,17 @@ type storage = int; type result = [list, storage]; -// Two entrypoints -@entry +// @entry const add = (delta : int, store : storage) : result => { - @no_mutation let _a = assert (0 == 0); + // @no_mutation + let _a = Assert.assert (0 == 0); return [[], store + delta]; }; -@entry @no_mutation -const sub = (delta : int, store : storage) : result => { - return [[], store - delta]; -}; +// @entry +// @no_mutation +const sub = (delta : int, store : storage) : result => + [[], store - delta]; ``` diff --git a/gitlab-pages/docs/testing/src/michelson_testing/mockup_testme.jsligo b/gitlab-pages/docs/testing/src/michelson_testing/mockup_testme.jsligo index e80047255b..25944d210e 100644 --- a/gitlab-pages/docs/testing/src/michelson_testing/mockup_testme.jsligo +++ b/gitlab-pages/docs/testing/src/michelson_testing/mockup_testme.jsligo @@ -3,10 +3,10 @@ type storage = string; type result = [list, storage]; -@entry +// @entry const append = (s: string, storage: storage): result => [[], storage + s] -@entry +// @entry const reset = (_: unit, _storage: storage): result => [[], ""] \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/mutation-testing/mutation-contract-test.jsligo b/gitlab-pages/docs/testing/src/mutation-testing/mutation-contract-test.jsligo index bc69fd84ce..26d1bf3cb3 100644 --- a/gitlab-pages/docs/testing/src/mutation-testing/mutation-contract-test.jsligo +++ b/gitlab-pages/docs/testing/src/mutation-testing/mutation-contract-test.jsligo @@ -1,56 +1,77 @@ -// This is mutation-contract-test.jsligo +import * as MutationContract from "gitlab-pages/docs/testing/src/mutation-testing/mutation-contract.jsligo"; -#import "gitlab-pages/docs/testing/src/mutation-testing/mutation-contract.jsligo" "MutationContract" -import Test = Test.Next; type storage = int; -type param = parameter_of MutationContract.AddSub; +type param = parameter_of; + const initial_storage = 7; -const tester = (taddr: typed_address, _c: michelson_contract , _: int): unit => { - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("add", taddr), 7, 0tez); +const tester = (taddr: typed_address, + _c: michelson_contract, + _: int): unit => { + Test.Contract.transfer_exn( + Test.Typed_address.get_entrypoint("add", taddr), 7, 0 as tez); Assert.assert(Test.Typed_address.get_storage(taddr) == initial_storage + 7); } const test_original = (() => { - let orig = Test.Originate.contract(contract_of(MutationContract.AddSub), initial_storage, 0tez); + let orig = + Test.Originate.contract + (contract_of(MutationContract.AddSub), initial_storage, 0 as tez); return tester(orig.taddr); })(); const test_mutation = - match(Test.Mutation.contract(contract_of(MutationContract.AddSub), initial_storage, 0tez, tester)) { - when(None()): unit; - when(Some(pmutation)): do { + $match(Test.Mutation.contract( + contract_of(MutationContract.AddSub), + initial_storage, + 0 as tez, + tester), { + "None": () => unit, + "Some": pmutation => (() => { Test.IO.log(pmutation[1]); // In a real program, one would write `failwith "A mutation passes"` // Because we want to demonstrate the issue without an actual error // a milder println is used in this document. Test.IO.println("A mutation of the contract still passes the tests!"); - } - }; -const tester_add_and_sub = (taddr: typed_address, _c: michelson_contract, _i: int): unit => { - - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("add", taddr), 7, 0tez); + })() + }); +const tester_add_and_sub = + (taddr: typed_address, + _c: michelson_contract, + _i: int): unit => { + Test.Contract.transfer_exn + (Test.Typed_address.get_entrypoint("add", taddr), 7, 0 as tez); Assert.assert(Test.Typed_address.get_storage(taddr) == initial_storage + 7); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("sub", taddr), 3, 0tez); + Test.Contract.transfer_exn + (Test.Typed_address.get_entrypoint("sub", taddr), 3, 0 as tez); Assert.assert(Test.Typed_address.get_storage(taddr) == initial_storage + 4); } const test_mutation_sub = - match(Test.Mutation.contract(contract_of(MutationContract.AddSub), initial_storage, 0tez, tester_add_and_sub)) { - when(None()): unit; - when(Some(pmutation)): do { + $match(Test.Mutation.contract( + contract_of(MutationContract.AddSub), + initial_storage, + 0 as tez, + tester_add_and_sub), { + "None": () => unit, + "Some": pmutation => (() => { Test.IO.log(pmutation[1]); Test.IO.println("A mutation of the contract still passes the tests!"); - } - }; + })() + }); const test_mutation_all = - match(Test.Mutation.All.contract(contract_of(MutationContract.AddSub), initial_storage, 0tez, tester)) { - when([]): unit; - when([hd,...tl]): do { + $match(List.head_and_tail( + Test.Mutation.All.contract( + contract_of(MutationContract.AddSub), + initial_storage, + 0 as tez, + tester)), { + "None": () => unit, + "Some": ([hd,tl]) => (() => { let mutations = list([hd,...tl]); let _p = Test.IO.println("Some mutations also pass the tests!"); for (const m of mutations) { let [_, mutation] = m; Test.IO.log(mutation); }; - } - }; \ No newline at end of file + })() + }); \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/mutation-testing/mutation-contract-test.mligo b/gitlab-pages/docs/testing/src/mutation-testing/mutation-contract-test.mligo index 093e684e2e..30c0315f3e 100644 --- a/gitlab-pages/docs/testing/src/mutation-testing/mutation-contract-test.mligo +++ b/gitlab-pages/docs/testing/src/mutation-testing/mutation-contract-test.mligo @@ -1,9 +1,10 @@ (* This is mutation-contract-test.mligo *) -#import "gitlab-pages/docs/testing/src/mutation-testing/mutation-contract.mligo" "MutationContract" -module Test = Test.Next +module MutationContract = Gitlab_pages.Docs.Testing.Src.Mutation_testing.Mutation_contract + type storage = MutationContract.AddSub.storage type param = MutationContract.AddSub parameter_of + let initial_storage = 7 let tester (taddr : (param, storage) typed_address) (_ : (param ,storage) michelson_contract) (_:int) : unit = @@ -24,9 +25,9 @@ let test_mutation = Test.IO.println "A mutation of the contract still passes the tests!" let tester_add_and_sub (taddr : (param, storage) typed_address) (_ : (param, storage) michelson_contract) (_ : int) : unit = let _ = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "add" taddr) 7 0tez in - let () = Assert.assert (Test.get_storage taddr = initial_storage + 7) in + let () = Assert.assert (Test.Typed_address.get_storage taddr = initial_storage + 7) in let _ = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "sub" taddr) 3 0tez in - Assert.assert (Test.get_storage taddr = initial_storage + 4) + Assert.assert (Test.Typed_address.get_storage taddr = initial_storage + 4) let test_mutation_sub = match Test.Mutation.contract (contract_of MutationContract.AddSub) initial_storage 0tez tester_add_and_sub with diff --git a/gitlab-pages/docs/testing/src/mutation-testing/mutation-contract.jsligo b/gitlab-pages/docs/testing/src/mutation-testing/mutation-contract.jsligo index c399b01cf9..e8f00381e2 100644 --- a/gitlab-pages/docs/testing/src/mutation-testing/mutation-contract.jsligo +++ b/gitlab-pages/docs/testing/src/mutation-testing/mutation-contract.jsligo @@ -2,8 +2,9 @@ export namespace AddSub { export type storage = int; - @entry + // @entry const add = (delta: int, storage: storage): [list, storage] => [[], storage + delta]; - @entry + + // @entry const sub = (delta: int, storage: storage): [list, storage] => [[], storage - delta]; } \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/mutation-testing/no_mutation.jsligo b/gitlab-pages/docs/testing/src/mutation-testing/no_mutation.jsligo index 295f9d96c5..4130fc4d6c 100644 --- a/gitlab-pages/docs/testing/src/mutation-testing/no_mutation.jsligo +++ b/gitlab-pages/docs/testing/src/mutation-testing/no_mutation.jsligo @@ -3,14 +3,14 @@ type storage = int; type result = [list, storage]; -// Two entrypoints -@entry +// @entry const add = (delta : int, store : storage) : result => { - @no_mutation let _a = assert (0 == 0); + // @no_mutation + let _a = Assert.assert (0 == 0); return [[], store + delta]; }; -@entry @no_mutation -const sub = (delta : int, store : storage) : result => { - return [[], store - delta]; -}; \ No newline at end of file +// @entry +// @no_mutation +const sub = (delta : int, store : storage) : result => + [[], store - delta]; \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/mutation-testing/no_mutation.mligo b/gitlab-pages/docs/testing/src/mutation-testing/no_mutation.mligo index e54d9eb450..ec9619b2c9 100644 --- a/gitlab-pages/docs/testing/src/mutation-testing/no_mutation.mligo +++ b/gitlab-pages/docs/testing/src/mutation-testing/no_mutation.mligo @@ -6,7 +6,7 @@ type result = operation list * storage (* Two entrypoints *) [@entry] let add (delta : int) (store : storage) : result = - [@no_mutation] let _ = assert (0 = 0) in + [@no_mutation] let _ = Assert.assert (0 = 0) in [], store + delta [@entry] [@no_mutation] diff --git a/gitlab-pages/docs/testing/src/mutation-testing/twice.jsligo b/gitlab-pages/docs/testing/src/mutation-testing/twice.jsligo index cb3eb5e140..bc3249f14b 100644 --- a/gitlab-pages/docs/testing/src/mutation-testing/twice.jsligo +++ b/gitlab-pages/docs/testing/src/mutation-testing/twice.jsligo @@ -1,5 +1,4 @@ const twice = (x: int) => x + x; -import Test = Test.Next; const simple_tests = (f : ((input: int) => int)) : unit => { /* Test 1 */ Assert.assert(Test.Compare.eq(Test.Michelson.run(f, 0), Test.Michelson.eval(0))); @@ -9,22 +8,22 @@ const simple_tests = (f : ((input: int) => int)) : unit => { const test = simple_tests(twice); const test_mutation = - match(Test.Mutation.func(twice, simple_tests)) { - when(None()): unit; - when(Some(pmutation)): do { + $match(Test.Mutation.func(twice, simple_tests), { + "None": () => unit, + "Some": pmutation => (() => { Test.IO.log(pmutation[1]); Test.IO.println("Some mutation also passes the tests! ^^") - } - }; + })() + }); const get_all_mutations = - match(Test.Mutation.All.func(twice, simple_tests)) { - when([]): unit; - when([hd,...tl]): do { + $match(List.head_and_tail(Test.Mutation.All.func(twice, simple_tests)), { + "None": () => unit, + "Some": ([hd,tl]) => (() => { let mutations = list([hd,...tl]); Test.IO.println("Some mutations also pass the tests!"); for (const m of mutations) { let [_, mutation] = m; Test.IO.log(mutation); }; - } - }; \ No newline at end of file + })() + }); \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/mutation-testing/twice.mligo b/gitlab-pages/docs/testing/src/mutation-testing/twice.mligo index 1fd35c5d37..c6df47e6e5 100644 --- a/gitlab-pages/docs/testing/src/mutation-testing/twice.mligo +++ b/gitlab-pages/docs/testing/src/mutation-testing/twice.mligo @@ -1,5 +1,4 @@ let twice (x : int) = x + x -module Test = Test.Next let simple_tests (f : int -> int) = (* Test 1 *) let () = Assert.assert (Test.Compare.eq (Test.Michelson.run f 0) (Test.Michelson.eval 0)) in diff --git a/gitlab-pages/docs/testing/src/testing-tickets/usage_entrypoint.jsligo b/gitlab-pages/docs/testing/src/testing-tickets/usage_entrypoint.jsligo index 05ccd7e78a..5e67842a2e 100644 --- a/gitlab-pages/docs/testing/src/testing-tickets/usage_entrypoint.jsligo +++ b/gitlab-pages/docs/testing/src/testing-tickets/usage_entrypoint.jsligo @@ -2,30 +2,31 @@ namespace MyContract { type storage = int; export type param = [int, ticket]; - @entry + // @entry function main (param: param, storage: storage): [list, storage] { const [multiplier, ticket] = param; // Read the ticket, destroy it, and add its amount times the multiplier to storage - const [[_address, [payload, amount]], _ticket] = Tezos.Next.Ticket.read(ticket); + const [[_address, [payload, amount]], _ticket] = Tezos.Ticket.read(ticket); return ([[], storage + (multiplier * payload * int(amount))]); }; } -const test_transfer_to_contract = do { +const test_transfer_to_contract = (() => { // Originate the contract as usual - let orig = Test.Next.Originate.contract(contract_of(MyContract), 0, 0tez); - let main_addr = Test.Next.Typed_address.to_address(orig.taddr); + let orig = Test.Originate.contract(contract_of(MyContract), 0, + 0 as tez); + let main_addr = Test.Typed_address.to_address(orig.taddr); // Create a function that the proxy runs to return the parameter const create_param = (t: ticket): MyContract.param => [5, t]; // Create the proxy contract - const proxy_taddr = Test.Proxy_ticket.init_transfer(create_param); - Test.Next.IO.log(["proxy addr:", proxy_taddr]); + const proxy_taddr = Test.Ticket.Proxy.init_transfer(create_param); + Test.IO.log(["proxy addr:", proxy_taddr]); // Use the proxy to call the entrypoint - const ticket_info = [3, 10n]; - Test.Proxy_ticket.transfer(proxy_taddr, [ticket_info, main_addr]); + const ticket_info = [3, 10 as nat]; + Test.Ticket.Proxy.transfer(proxy_taddr, [ticket_info, main_addr]); // Verify that the value in storage changes - Assert.assert(Test.Next.Typed_address.get_storage(orig.taddr) == 150); -}; \ No newline at end of file + Assert.assert(Test.Typed_address.get_storage(orig.taddr) == 150); +})(); \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing-tickets/usage_entrypoint.mligo b/gitlab-pages/docs/testing/src/testing-tickets/usage_entrypoint.mligo index 67bd4c3c15..04b7dbc645 100644 --- a/gitlab-pages/docs/testing/src/testing-tickets/usage_entrypoint.mligo +++ b/gitlab-pages/docs/testing/src/testing-tickets/usage_entrypoint.mligo @@ -2,25 +2,26 @@ module MyContract = struct type storage = int type param = int * int ticket - [@entry] let main (param : param) (storage : storage) : operation list * storage = - let (multiplier, ticket) = param in + [@entry] + let main (param : param) (storage : storage) : operation list * storage = + let multiplier, ticket = param in // Read the ticket, destroy it, and add its amount times the multiplier to storage - let ((_address, (payload, amount)), _ticket) = Tezos.Next.Ticket.read ticket in + let (_address, (payload, amount)), _ticket = Tezos.Ticket.read ticket in [], (storage + (multiplier * payload * (int amount))) end let test_transfer_to_contract = // Originate the contract as usual - let orig = Test.Next.Originate.contract (contract_of MyContract) 0 0tez in - let main_addr = Test.Next.Typed_address.to_address orig.taddr in + let orig = Test.Originate.contract (contract_of MyContract) 0 0tez in + let main_addr = Test.Typed_address.to_address orig.taddr in // Create a function that the proxy runs to return the parameter let create_param : int ticket -> MyContract.param = fun (t : int ticket) -> 5, t in // Create the proxy contract - let proxy_taddr = Test.Proxy_ticket.init_transfer create_param in - let () = Test.Next.IO.log ("proxy addr:", proxy_taddr) in + let proxy_taddr = Test.Ticket.Proxy.init_transfer create_param in + let () = Test.IO.log ("proxy addr:", proxy_taddr) in // Use the proxy to call the entrypoint let ticket_info = 3, 10n in - let _ : test_exec_result = Test.Proxy_ticket.transfer proxy_taddr (ticket_info, main_addr) in - Assert.assert ((Test.Next.Typed_address.get_storage orig.taddr) = 150) \ No newline at end of file + let _ : test_exec_result = Test.Ticket.Proxy.transfer proxy_taddr (ticket_info, main_addr) in + Assert.assert (Test.Typed_address.get_storage orig.taddr = 150) \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing-tickets/usage_orig.jsligo b/gitlab-pages/docs/testing/src/testing-tickets/usage_orig.jsligo index 44c05eac2a..0722d3fbd1 100644 --- a/gitlab-pages/docs/testing/src/testing-tickets/usage_orig.jsligo +++ b/gitlab-pages/docs/testing/src/testing-tickets/usage_orig.jsligo @@ -2,49 +2,49 @@ namespace MyContract { export type storage = [int, option>]; export type unforged_storage = [int, option>]; - @entry + // @entry const main = (_: unit, storage: storage): [list, storage] => { const [stored_value, ticket_opt] = storage; const new_storage: storage = - match(ticket_opt) { + $match(ticket_opt, { // If there is a ticket, add its amount to the int in storage - when(Some(ticket)): ((ticket: ticket) => { - const [[_address, [_payload, amount]], new_ticket] = Tezos.Next.Ticket.read(ticket); - return [stored_value + int(amount), Some(new_ticket)]; - })(ticket); + "Some": ticket => ((ticket: ticket) => { + const [[_address, [_payload, amount]], new_ticket] = Tezos.Ticket.read(ticket); + return [stored_value + int(amount), ["Some" as "Some", new_ticket]]; + })(ticket), // If there is no ticket in storage, do nothing - when(None()): [stored_value, None()]; - }; - return [list([]), new_storage]; + "None": () => [stored_value, ["None" as "None"]] + }); + return [[], new_storage]; }; - } -const test_originate_contract = do { - +const test_originate_contract = (() => { // Create a function that the proxy runs to return the contract storage - const create_storage = (t: ticket): MyContract.storage => [0, Some (t)]; - const ticket_info = [0x0202, 15n]; + const create_storage = (t: ticket): MyContract.storage => + [0, ["Some" as "Some", t]]; + const ticket_info = [0x0202, 15 as nat]; // Create the proxy contract and use it to originate the contract - const addr = Test.Proxy_ticket.originate (ticket_info, create_storage, MyContract.main); + const addr = Test.Ticket.Proxy.originate (ticket_info, create_storage, MyContract.main); // ... // The ticket 'unforged_ticket_opt' can be manipulated freely without being destroyed - const [_stored_value, unforged_ticket_opt] = (Test.Proxy_ticket.get_storage(addr) as MyContract.unforged_storage); + const [_stored_value, unforged_ticket_opt] = (Test.Ticket.Proxy.get_storage(addr) as MyContract.unforged_storage); // Verify that the ticket is in storage - match (unforged_ticket_opt) { - when(Some(x)): do { - Test.Next.IO.log(["unforged_ticket", x]); + $match(unforged_ticket_opt, { + "Some": x => (() => { + Test.IO.log(["unforged_ticket", x]); const { ticketer: _, value, amount } = x; Assert.assert(value == ticket_info[0]); Assert.assert(amount == ticket_info[1]); return unit - }; - when(None()): failwith ("impossible") - }; + })(), + "None": () => failwith ("impossible") + }); // Call the entrypoint and verify that the value in storage changes - Test.Next.Contract.transfer_exn(Test.Next.Typed_address.get_entrypoint("default", addr), unit, 0tez); - const [new_stored_value, _unforged_ticket_opt] = Test.Proxy_ticket.get_storage(addr) as MyContract.unforged_storage; + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("default", + addr), unit, 0 as tez); + const [new_stored_value, _unforged_ticket_opt] = Test.Ticket.Proxy.get_storage(addr) as MyContract.unforged_storage; Assert.assert(new_stored_value == int(ticket_info[1])); -}; \ No newline at end of file +})(); \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing-tickets/usage_orig.mligo b/gitlab-pages/docs/testing/src/testing-tickets/usage_orig.mligo index ad75d40303..82b7716eca 100644 --- a/gitlab-pages/docs/testing/src/testing-tickets/usage_orig.mligo +++ b/gitlab-pages/docs/testing/src/testing-tickets/usage_orig.mligo @@ -1,24 +1,21 @@ module MyContract = struct + type storage = int * bytes ticket option + type unforged_storage = int * bytes unforged_ticket option - type storage = int * (bytes ticket) option - type unforged_storage = int * (bytes unforged_ticket) option - - [@entry] let main (_ : unit) (storage : storage) :operation list * storage = + [@entry] + let main (_ : unit) (storage : storage) : operation list * storage = let (stored_value, ticket_opt) = storage in let new_storage : storage = match ticket_opt with // If there is a ticket, add its amount to the int in storage Some ticket -> - let ((_address, (_payload, amount)), new_ticket) = Tezos.Next.Ticket.read ticket in - (stored_value + (int amount), Some new_ticket) + let (_address, (_payload, amount)), new_ticket = Tezos.Ticket.read ticket in + stored_value + int amount, Some new_ticket // If there is no ticket in storage, do nothing - | None -> stored_value, None () - in - [], new_storage - + | None -> stored_value, None + in [], new_storage end let test_originate_contract = - // Create a function that the proxy runs to return the contract storage let create_storage = fun (t : bytes ticket) : MyContract.storage -> (0, Some t) in let ticket_bytes : bytes = 0x0202 in @@ -26,16 +23,16 @@ let test_originate_contract = let ticket_info = ticket_bytes, ticket_amount in // Create the proxy contract and use it to originate the contract - let addr = Test.Proxy_ticket.originate ticket_info create_storage MyContract.main in + let addr = Test.Ticket.Proxy.originate ticket_info create_storage MyContract.main in // ... // The ticket 'unforged_ticket_opt' can be manipulated freely without being destroyed - let unforged_storage : MyContract.unforged_storage = Test.Proxy_ticket.get_storage addr in + let unforged_storage : MyContract.unforged_storage = Test.Ticket.Proxy.get_storage addr in let (_stored_value, unforged_ticket_opt) = unforged_storage in // Verify that the ticket is in storage let () = match unforged_ticket_opt with Some unforged_ticket -> - let () = Test.Next.IO.log ("unforged_ticket",unforged_ticket) in + let () = Test.IO.log ("unforged_ticket",unforged_ticket) in let { ticketer ; value ; amount } = unforged_ticket in let () = Assert.assert (value = ticket_bytes) in Assert.assert (amount = ticket_amount) @@ -43,7 +40,7 @@ let test_originate_contract = in // Call the entrypoint and verify that the value in storage changes - let _ : nat = Test.Next.Contract.transfer_exn (Test.Next.Typed_address.get_entrypoint"default" addr) unit 0tez in - let new_storage : MyContract.unforged_storage = Test.Proxy_ticket.get_storage addr in - let (new_stored_value, _unforged_ticket_opt) = new_storage in + let _ : nat = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint"default" addr) unit 0tez in + let new_storage : MyContract.unforged_storage = Test.Ticket.Proxy.get_storage addr in + let new_stored_value, _unforged_ticket_opt = new_storage in Assert.assert (new_stored_value = (int ticket_amount)) \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing/dry-run-complex.jsligo b/gitlab-pages/docs/testing/src/testing/dry-run-complex.jsligo index 4a43279d87..ac48563908 100644 --- a/gitlab-pages/docs/testing/src/testing/dry-run-complex.jsligo +++ b/gitlab-pages/docs/testing/src/testing/dry-run-complex.jsligo @@ -2,7 +2,7 @@ namespace MyContract { type storage_type = map; type return_type = [list, storage_type]; - @entry + // @entry const update = (param: [nat, string], storage: storage_type): return_type => { const [index, value] = param; const updated_map = Map.add(index, value, storage); diff --git a/gitlab-pages/docs/testing/src/testing/dry-run-simple.jsligo b/gitlab-pages/docs/testing/src/testing/dry-run-simple.jsligo index 574bea2f33..6c40159f7b 100644 --- a/gitlab-pages/docs/testing/src/testing/dry-run-simple.jsligo +++ b/gitlab-pages/docs/testing/src/testing/dry-run-simple.jsligo @@ -2,7 +2,7 @@ namespace Counter { type storage_type = int; type return_type = [list, storage_type]; - @entry + // @entry const main = (_action: unit, storage: storage_type): return_type => [[], storage + 1] } \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing/interpret.jsligo b/gitlab-pages/docs/testing/src/testing/interpret.jsligo index 7b26a1442e..a7d0a37895 100644 --- a/gitlab-pages/docs/testing/src/testing/interpret.jsligo +++ b/gitlab-pages/docs/testing/src/testing/interpret.jsligo @@ -1,6 +1,5 @@ // This is interpret.jsligo type myDataType = map; -const encodeEntry = (a: int, b: string): myDataType => { - return Map.literal([[a, b]]); -} \ No newline at end of file +const encodeEntry = (a: int, b: string): myDataType => + Map.literal([[a, b]]); \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing/mycontract-failures.jsligo b/gitlab-pages/docs/testing/src/testing/mycontract-failures.jsligo index 419e669a88..2c6b5fcf4b 100644 --- a/gitlab-pages/docs/testing/src/testing/mycontract-failures.jsligo +++ b/gitlab-pages/docs/testing/src/testing/mycontract-failures.jsligo @@ -2,23 +2,29 @@ namespace MyContract { export type storage = int; export type result = [list, storage]; - @entry const increment = (delta : int, storage : storage) : result => - abs(delta) <= 5n ? [[], storage + delta] : failwith("Pass 5 or less"); - @entry const decrement = (delta : int, storage : storage) : result => - abs(delta) <= 5n ? [[], storage - delta] : failwith("Pass 5 or less"); - @entry const reset = (_u : unit, _storage : storage) : result => [[], 0]; -} -import Test = Test.Next; + // @entry + const increment = (delta : int, storage : storage) : result => + abs(delta) <= (5 as nat) ? [[], storage + delta] : failwith("Pass 5 or less"); + + // @entry + const decrement = (delta : int, storage : storage) : result => + abs(delta) <= (5 as nat) ? [[], storage - delta] : failwith("Pass 5 or less"); + // @entry + const reset = (_u : unit, _storage : storage) : result => [[], 0]; +} const test_failure = () => { const initial_storage = 10 as int; - const orig = Test.Originate.contract(contract_of(MyContract), initial_storage, 0tez); - const result = Test.Contract.transfer(Test.Typed_address.get_entrypoint("increment", orig.taddr), 50 as int, 0tez); + const orig = Test.Originate.contract(contract_of(MyContract), + initial_storage, 0 as tez); + const result = + Test.Contract.transfer(Test.Typed_address.get_entrypoint("increment", + orig.taddr), 50 as int, 0 as tez); - match(result) { - when(Fail(_x)): Test.IO.log("Failed as expected"); - when(Success(_s)): failwith("This should not succeed") - }; + $match(result, { + "Fail": _x => Test.IO.log("Failed as expected"), + "Success": _s => failwith("This should not succeed") + }); } const test1 = test_failure(); \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing/mycontract-failures.mligo b/gitlab-pages/docs/testing/src/testing/mycontract-failures.mligo index 533e4565a0..93f8cea496 100644 --- a/gitlab-pages/docs/testing/src/testing/mycontract-failures.mligo +++ b/gitlab-pages/docs/testing/src/testing/mycontract-failures.mligo @@ -8,8 +8,6 @@ module MyContract = struct if abs delta <= 5n then [], storage - delta else failwith "Pass 5 or less" [@entry] let reset () (_storage : storage) : result = [], 0 end -module Test = Test.Next - let test_failure = let initial_storage = 10 in let orig = Test.Originate.contract (contract_of MyContract) initial_storage 0tez in diff --git a/gitlab-pages/docs/testing/src/testing/mycontract-test.jsligo b/gitlab-pages/docs/testing/src/testing/mycontract-test.jsligo index 13f98c2d6e..a3e93f4722 100644 --- a/gitlab-pages/docs/testing/src/testing/mycontract-test.jsligo +++ b/gitlab-pages/docs/testing/src/testing/mycontract-test.jsligo @@ -1,14 +1,16 @@ -// This is mycontract-test.jligo - -import Test = Test.Next; - -#import "gitlab-pages/docs/testing/src/testing/mycontract.jsligo" "MyModule" +import * as MyContract from "gitlab-pages/docs/testing/src/testing/mycontract.jsligo"; const run_test1 = () => { - let initial_storage = 10; - let orig = Test.Originate.contract(contract_of(MyModule.MyContract), initial_storage, 0tez); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("increment", orig.taddr), 5, 0tez); - return Assert.assert(Test.Typed_address.get_storage(orig.taddr) == initial_storage + 5); + let initial_storage = 10; + let orig = Test.Originate.contract( + contract_of(MyContract.MyContract), + initial_storage, + 0 as tez); + Test.Contract.transfer_exn( + Test.Typed_address.get_entrypoint("increment", orig.taddr), + 5, + 0 as tez); + return Assert.assert(Test.Typed_address.get_storage(orig.taddr) == initial_storage + 5); }; const test1 = run_test1(); \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing/mycontract-test.mligo b/gitlab-pages/docs/testing/src/testing/mycontract-test.mligo index f2fb5ec94c..b3c5a8c652 100644 --- a/gitlab-pages/docs/testing/src/testing/mycontract-test.mligo +++ b/gitlab-pages/docs/testing/src/testing/mycontract-test.mligo @@ -1,8 +1,4 @@ -(* This is mycontract-test.mligo *) - -module Test = Test.Next - -#import "gitlab-pages/docs/testing/src/testing/mycontract.mligo" "MyContract" +module MyContract = Gitlab_pages.Docs.Testing.Src.Testing.Mycontract let run_test1 = let initial_storage = 10 in diff --git a/gitlab-pages/docs/testing/src/testing/mycontract.jsligo b/gitlab-pages/docs/testing/src/testing/mycontract.jsligo index c9a0637ef6..f0cec4fc29 100644 --- a/gitlab-pages/docs/testing/src/testing/mycontract.jsligo +++ b/gitlab-pages/docs/testing/src/testing/mycontract.jsligo @@ -3,7 +3,12 @@ export namespace MyContract { export type storage = int; export type result = [list, storage]; - @entry const increment = (delta : int, storage : storage) : result => [[], storage + delta]; - @entry const decrement = (delta : int, storage : storage) : result => [[], storage - delta]; - @entry const reset = (_u : unit, _storage : storage) : result => [[], 0]; + // @entry + const increment = (delta : int, storage : storage) : result => [[], storage + delta]; + + // @entry + const decrement = (delta : int, storage : storage) : result => [[], storage - delta]; + + // @entry + const reset = (_u : unit, _storage : storage) : result => [[], 0]; } \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing/remove-balance.jsligo b/gitlab-pages/docs/testing/src/testing/remove-balance.jsligo index 40d828bc1c..059f886203 100644 --- a/gitlab-pages/docs/testing/src/testing/remove-balance.jsligo +++ b/gitlab-pages/docs/testing/src/testing/remove-balance.jsligo @@ -1,11 +1,11 @@ // This is remove-balance.jsligo -type balances = map ; +export type balances = map; -const remove_balances_under = (b: balances, threshold: tez): balances => { +export function remove_balances_under (b: balances, threshold: tez): balances { let f = ([acc, kv]: [balances, [address, tez]] ): balances => { - let [k, v] = kv; - if (v < threshold) { return Map.remove (k, acc) } else {return acc} + const [k, v] = kv; + if (v < threshold) return Map.remove (k, acc); else return acc }; return Map.fold (f, b, b); } \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing/reset.jsligo b/gitlab-pages/docs/testing/src/testing/reset.jsligo index 50f52a3f0f..128ffdf191 100644 --- a/gitlab-pages/docs/testing/src/testing/reset.jsligo +++ b/gitlab-pages/docs/testing/src/testing/reset.jsligo @@ -1,15 +1,13 @@ -import Test = Test.Next; - const test_accounts = () => { - Test.State.reset(3n, [] as list ); - const admin_account = Test.Account.address(0n); - const user_account1 = Test.Account.address(1n); - const user_account2 = Test.Account.address(2n); + Test.State.reset(3 as nat, [] as list); + const admin_account = Test.Account.address(0 as nat); + const user_account1 = Test.Account.address(1 as nat); + const user_account2 = Test.Account.address(2 as nat); Test.IO.log(Test.Address.get_balance(admin_account)); - // 3800000000000mutez + // 3800000000000 as mutez Test.IO.log(Test.Address.get_balance(user_account1)); - // 3800000000000mutez + // 3800000000000 as mutez Test.IO.log(Test.Address.get_balance(user_account2)); - // 3800000000000mutez + // 3800000000000 as mutez } \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing/reset.mligo b/gitlab-pages/docs/testing/src/testing/reset.mligo index 09ce8f82b9..8554ccba9f 100644 --- a/gitlab-pages/docs/testing/src/testing/reset.mligo +++ b/gitlab-pages/docs/testing/src/testing/reset.mligo @@ -1,5 +1,3 @@ -module Test = Test.Next - let test_accounts = let initial_balances : tez list = [] in let () = Test.State.reset 3n initial_balances in diff --git a/gitlab-pages/docs/testing/src/testing/test-accounts.jsligo b/gitlab-pages/docs/testing/src/testing/test-accounts.jsligo index 8dcbbeea53..8ad12401cf 100644 --- a/gitlab-pages/docs/testing/src/testing/test-accounts.jsligo +++ b/gitlab-pages/docs/testing/src/testing/test-accounts.jsligo @@ -2,51 +2,53 @@ namespace Counter { type storage = [int, address]; type return_type = [list, storage]; - @entry + // @entry const increment = (n: int, storage: storage): return_type => { const [number, admin_account] = storage; return [[], [number + n, admin_account]]; } - @entry + // @entry const decrement = (n: int, storage: storage): return_type => { const [number, admin_account] = storage; return [[], [number - n, admin_account]]; } - @entry + // @entry const reset = (_: unit, storage: storage): return_type => { const [_number, admin_account] = storage; - if (Tezos.get_sender() != admin_account) { + if (Tezos.get_sender() != admin_account) return failwith("Only the owner can call this entrypoint"); - } return [[], [0, admin_account]]; } }; -import Test = Test.Next; - const test_admin = (() => { - const admin_account = Test.Account.address(0n); - const user_account = Test.Account.address(1n); + const admin_account = Test.Account.address(0 as nat); + const user_account = Test.Account.address(1 as nat); // Originate the contract with the admin account in storage const initial_storage = [10 as int, admin_account]; - const orig = Test.Originate.contract(contract_of(Counter), initial_storage, 0tez); + const orig = + Test.Originate.contract(contract_of(Counter), initial_storage, 0 as tez); // Try to call the reset entrypoint as the user and expect it to fail Test.State.set_source(user_account); - const result = Test.Contract.transfer(Test.Typed_address.get_entrypoint("reset", orig.taddr), unit, 0tez); - match(result) { - when(Fail(_err)): Test.IO.log("Test succeeded"); - when (Success(_s)): failwith("User should not be able to call reset"); - }; + const result = + Test.Contract.transfer(Test.Typed_address.get_entrypoint("reset", + orig.taddr), unit, 0 as tez); + $match(result, { + "Fail": _err => Test.IO.log("Test succeeded"), + "Success": _s => failwith("User should not be able to call reset") + }); // Call the reset entrypoint as the admin and expect it to succeed Test.State.set_source(admin_account); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("reset", orig.taddr), unit, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("reset", + orig.taddr), unit, 0 as tez); - const [newNumber, _admin_account] = Test.Typed_address.get_storage(orig.taddr); + const [newNumber, _admin_account] = + Test.Typed_address.get_storage(orig.taddr); Assert.assert(newNumber == 0); -}) () \ No newline at end of file +})() \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing/test-accounts.mligo b/gitlab-pages/docs/testing/src/testing/test-accounts.mligo index 789929d271..484d01a4b9 100644 --- a/gitlab-pages/docs/testing/src/testing/test-accounts.mligo +++ b/gitlab-pages/docs/testing/src/testing/test-accounts.mligo @@ -17,8 +17,6 @@ module Counter = struct else failwith "Only the owner can call this entrypoint" end -module Test = Test.Next - let test_admin = let (admin_account, user_account) = (Test.Account.address(0n), Test.Account.address(1n)) in diff --git a/gitlab-pages/docs/testing/src/testing/test_ex.jsligo b/gitlab-pages/docs/testing/src/testing/test_ex.jsligo index 33ef25c31e..d7c9c04176 100644 --- a/gitlab-pages/docs/testing/src/testing/test_ex.jsligo +++ b/gitlab-pages/docs/testing/src/testing/test_ex.jsligo @@ -1,17 +1,15 @@ namespace C { - @entry - const main = (p: [int, int], _: unit) => { - const op1 = Tezos.emit("%foo", p); - const op2 = Tezos.emit("%foo", p[0]); - return [([op1, op2] as list), unit]; + // @entry + const main = (p: [int, int], _: unit) : [list, unit] => { + const op1 = Tezos.Operation.emit("%foo", p); + const op2 = Tezos.Operation.emit("%foo", p[0]); + return [[op1, op2], unit]; }; } -import Test = Test.Next; - const test = () => { - const orig = Test.Originate.contract(contract_of(C), unit, 0tez); - Test.Typed_address.transfer_exn(orig.taddr, Main ([1,2]), 0tez); + const orig = Test.Originate.contract(contract_of(C), unit, 0 as tez); + Test.Typed_address.transfer_exn(orig.taddr, ["Main" as "Main", [1,2]], 0 as tez); return [Test.State.last_events(orig.taddr, "foo") as list<[int, int]>, Test.State.last_events(orig.taddr, "foo") as list]; }; diff --git a/gitlab-pages/docs/testing/src/testing/test_ex.mligo b/gitlab-pages/docs/testing/src/testing/test_ex.mligo index 80684c11a7..933dfe2c4e 100644 --- a/gitlab-pages/docs/testing/src/testing/test_ex.mligo +++ b/gitlab-pages/docs/testing/src/testing/test_ex.mligo @@ -1,10 +1,11 @@ module C = struct - [@entry] let main (p : int*int) () = - [Tezos.emit "%foo" p ; Tezos.emit "%foo" p.0],() + [@entry] + let main (p : int * int) () = + let op1 = Tezos.Operation.emit "%foo" p in + let op2 = Tezos.Operation.emit "%foo" p.0 in + [op1; op2], () end -module Test = Test.Next - let test_foo = let orig = Test.Originate.contract (contract_of C) () 0tez in let _: nat = Test.Typed_address.transfer_exn orig.taddr (Main (1,2)) 0tez in diff --git a/gitlab-pages/docs/testing/src/testing/test_views.jsligo b/gitlab-pages/docs/testing/src/testing/test_views.jsligo index 6771a1daa8..4ac18d475e 100644 --- a/gitlab-pages/docs/testing/src/testing/test_views.jsligo +++ b/gitlab-pages/docs/testing/src/testing/test_views.jsligo @@ -1,33 +1,30 @@ namespace Testviews { - type storage = string; - type return_type = [list, storage]; - @entry + // @entry const set = (inputStr: storage, _storage: storage): return_type => - [list([]), inputStr]; + [[], inputStr]; - @entry + // @entry const reset = (_u: unit, _storage: storage): return_type => - [list([]), ""]; + [[], ""]; - @view + // @view const getString = (_u: unit, storage: storage): string => storage; } -import Test = Test.Next; -import Tezos = Tezos.Next; - const test_view = () => { - const contract = Test.Originate.contract(contract_of(Testviews), "", 0tez); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("set", contract.taddr), "hello", 0tez); + const contract = Test.Originate.contract(contract_of(Testviews), "", + 0 as tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("set", + contract.taddr), "hello", 0 as tez); const address = Test.Typed_address.to_address(contract.taddr); const viewResultOption: option = Tezos.View.call("getString", unit, address); - const viewResult = match(viewResultOption) { - when(Some(str)): str; - when(None()): ""; - }; + const viewResult = $match(viewResultOption, { + "Some": str => str, + "None": () => "" + }); Assert.assert(Test.Compare.eq(viewResult, "hello")); }; const test1 = test_view(); \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing/test_views.mligo b/gitlab-pages/docs/testing/src/testing/test_views.mligo index e495fd8b37..1f0ada61a2 100644 --- a/gitlab-pages/docs/testing/src/testing/test_views.mligo +++ b/gitlab-pages/docs/testing/src/testing/test_views.mligo @@ -16,9 +16,6 @@ module Testviews = struct let getString (_u : unit) (storage : storage) : string = storage end -module Test = Test.Next -module Tezos = Tezos.Next - let test_view = let contract = Test.Originate.contract (contract_of Testviews) "" 0tez in let _ : nat = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "set" contract.taddr) "hello" 0tez in diff --git a/gitlab-pages/docs/testing/src/testing/tezos_specific.jsligo b/gitlab-pages/docs/testing/src/testing/tezos_specific.jsligo index 3642b785b0..4c3c070519 100644 --- a/gitlab-pages/docs/testing/src/testing/tezos_specific.jsligo +++ b/gitlab-pages/docs/testing/src/testing/tezos_specific.jsligo @@ -7,7 +7,6 @@ namespace Counter { @entry const decrement = (action: int, store: storage) : [list , storage] => [[], store - action]; }; -import Test = Test.Next; const testC = () => { const initial_storage = 42; @@ -15,4 +14,4 @@ const testC = () => { const p: (parameter_of Counter) = Increment(1); Test.Contract.transfer_exn(orig.taddr, p, 0tez); return assert(Test.Contract.get_storage(orig.taddr) == initial_storage + 1); -}; \ No newline at end of file +}; diff --git a/gitlab-pages/docs/testing/src/testing/unit-remove-balance-complete.jsligo b/gitlab-pages/docs/testing/src/testing/unit-remove-balance-complete.jsligo index 1ac082fdb8..68e0e54e04 100644 --- a/gitlab-pages/docs/testing/src/testing/unit-remove-balance-complete.jsligo +++ b/gitlab-pages/docs/testing/src/testing/unit-remove-balance-complete.jsligo @@ -1,13 +1,15 @@ -#include "./gitlab-pages/docs/testing/src/testing/remove-balance.jsligo" +import * as RemoveBalance from +"./gitlab-pages/docs/testing/src/testing/remove-balance.jsligo"; -import Test = Test.Next; +type balances = RemoveBalance.balances +const remove_balances_under = RemoveBalance.remove_balances_under const test_remove_balance = (() => { - Test.State.reset(5n, [] as list ); + Test.State.reset(5 as nat, [] as list ); const balances: balances = - Map.literal([[Test.Account.address(1n), 10tez], - [Test.Account.address(2n), 100tez], - [Test.Account.address(3n), 1000tez]]); + Map.literal([[Test.Account.address(1 as nat), 10 as tez], + [Test.Account.address(2 as nat), 100 as tez], + [Test.Account.address(3 as nat), 1000 as tez]]); return List.iter(([threshold, expected_size]: [tez, nat]): unit => { const tester = ([balances, threshold]: [balances, tez]): nat => Map.size (remove_balances_under (balances, threshold)); @@ -17,5 +19,6 @@ const test_remove_balance = (() => { Test.IO.log(["actual", size]); return (Assert.assert (Test.Compare.eq(size, expected_size_))) }, - list ([ [15tez, 2n], [130tez, 1n], [1200tez, 0n]]) ); + list ([ [15 as tez, 2 as nat], [130 as tez, 1 as nat], + [1200 as tez, 0 as nat]]) ); }) () \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing/unit-remove-balance-complete.mligo b/gitlab-pages/docs/testing/src/testing/unit-remove-balance-complete.mligo index 3d8ac9d498..72e637860d 100644 --- a/gitlab-pages/docs/testing/src/testing/unit-remove-balance-complete.mligo +++ b/gitlab-pages/docs/testing/src/testing/unit-remove-balance-complete.mligo @@ -1,7 +1,5 @@ #include "./gitlab-pages/docs/testing/src/testing/remove-balance.mligo" -module Test = Test.Next - let test_remove_balance = let () = Test.State.reset 5n ([]: tez list) in let balances: balances = diff --git a/gitlab-pages/docs/testing/src/testing/unit-remove-balance-mixed.jsligo b/gitlab-pages/docs/testing/src/testing/unit-remove-balance-mixed.jsligo index c2876d5f21..1b9f405f3e 100644 --- a/gitlab-pages/docs/testing/src/testing/unit-remove-balance-mixed.jsligo +++ b/gitlab-pages/docs/testing/src/testing/unit-remove-balance-mixed.jsligo @@ -1,13 +1,15 @@ -#include "./gitlab-pages/docs/testing/src/testing/remove-balance.jsligo" +import * as RemoveBalance from +"./gitlab-pages/docs/testing/src/testing/remove-balance.jsligo"; -import Test = Test.Next; +type balances = RemoveBalance.balances +const remove_balances_under = RemoveBalance.remove_balances_under const test_remove_balance = (() => { - Test.State.reset(5n, [] as list ); + Test.State.reset(5 as nat, [] as list); const balances: balances = - Map.literal([[Test.Account.address(1n), 10tez], - [Test.Account.address(2n), 100tez], - [Test.Account.address(3n), 1000tez]]); + Map.literal([[Test.Account.address(1 as nat), 10 as tez], + [Test.Account.address(2 as nat), 100 as tez], + [Test.Account.address(3 as nat), 1000 as tez]]); return List.iter(([threshold, expected_size]: [tez, nat]): unit => { const tester = ([balances, threshold]: [balances, tez]): nat => Map.size (remove_balances_under (balances, threshold)); @@ -17,5 +19,7 @@ return List.iter(([threshold, expected_size]: [tez, nat]): unit => { Test.IO.log(["actual", size]); return (Assert.assert (Test.Compare.eq(size, expected_size_))) }, - list ([ [15tez, 2n], [130tez, 1n], [1200tez, 0n]]) ); -}) () \ No newline at end of file + list ([ [15 as tez, 2 as nat], + [130 as tez, 1 as nat], + [1200 as tez, 0 as nat]])); +})() \ No newline at end of file diff --git a/gitlab-pages/docs/testing/src/testing/unit-remove-balance-mixed.mligo b/gitlab-pages/docs/testing/src/testing/unit-remove-balance-mixed.mligo index d13213b8a6..fa9932ed58 100644 --- a/gitlab-pages/docs/testing/src/testing/unit-remove-balance-mixed.mligo +++ b/gitlab-pages/docs/testing/src/testing/unit-remove-balance-mixed.mligo @@ -1,7 +1,5 @@ #include "./gitlab-pages/docs/testing/src/testing/remove-balance.mligo" -module Test = Test.Next - let test_remove_balance = let () = Test.State.reset 5n ([]: tez list) in let balances: balances = diff --git a/gitlab-pages/docs/testing/testing-tickets.md b/gitlab-pages/docs/testing/testing-tickets.md index 57dac02eb4..a258ecbaf4 100644 --- a/gitlab-pages/docs/testing/testing-tickets.md +++ b/gitlab-pages/docs/testing/testing-tickets.md @@ -4,7 +4,7 @@ title: Testing tickets --- Testing code that uses tickets requires extra steps because of how tickets are used in Tezos operations. -LIGO provides the `Proxy_ticket` module to help you test with tickets. +LIGO provides the `Ticket.Proxy` module to help you test with tickets. ## The problem with testing tickets @@ -12,7 +12,7 @@ Tickets have specific limitations on the Tezos platform that affect how they can For example, tickets always have the address of the contract that created it as the ticketer field. Also, their data payload cannot be changed after the ticket is created. -As a result, you can't create a ticket in a LIGO test with `Tezos.Next.Ticket.create` and use it to test smart contract origination or entrypoints. +As a result, you can't create a ticket in a LIGO test with `Tezos.Ticket.create` and use it to test smart contract origination or entrypoints. If LIGO allowed you to create and use tickets in this way, you could edit the ticket or assign a ticketer that was not the contract that created it. If you try to use such a ticket in smart contract operations, the operations fail. @@ -31,7 +31,7 @@ ligo compile expression cameligo 'Tezos.create_ticket 0x0202 10n' ```shell -ligo compile expression jsligo 'Tezos.create_ticket(0x0202, 10n)' +ligo compile expression jsligo 'Tezos.create_ticket(0x0202, 10 as nat)' ``` @@ -49,10 +49,10 @@ LIGO testing tools to provide ways to create tickets via a proxy contract so you ## Proxy ticket contracts -The LIGO test library provides a `Proxy_ticket` module which helps in working with tickets in the test framework. +The LIGO test library provides a `Ticket.Proxy` module which helps in working with tickets in the test framework. Instead of creating tickets yourself, you use a proxy contract to create tickets and send them with operations. -The `Proxy_ticket` module provides these functions: +The `Ticket.Proxy` module provides these functions: - `init_transfer`: Creates a proxy contract that you can use as the source of tickets in test operations - `transfer`: Uses a proxy contract to create a ticket and send it as the parameter of a smart contract call @@ -73,23 +73,21 @@ It provides an entrypoint that reads the ticket and adds its amount to the integ ```cameligo group=usage_orig module MyContract = struct + type storage = int * bytes ticket option + type unforged_storage = int * bytes unforged_ticket option - type storage = int * (bytes ticket) option - type unforged_storage = int * (bytes unforged_ticket) option - - [@entry] let main (_ : unit) (storage : storage) :operation list * storage = + [@entry] + let main (_ : unit) (storage : storage) : operation list * storage = let (stored_value, ticket_opt) = storage in let new_storage : storage = match ticket_opt with // If there is a ticket, add its amount to the int in storage Some ticket -> - let ((_address, (_payload, amount)), new_ticket) = Tezos.Next.Ticket.read ticket in - (stored_value + (int amount), Some new_ticket) + let (_address, (_payload, amount)), new_ticket = Tezos.Ticket.read ticket in + stored_value + int amount, Some new_ticket // If there is no ticket in storage, do nothing - | None -> stored_value, None () - in - [], new_storage - + | None -> stored_value, None + in [], new_storage end ``` @@ -102,22 +100,21 @@ namespace MyContract { export type storage = [int, option>]; export type unforged_storage = [int, option>]; - @entry + // @entry const main = (_: unit, storage: storage): [list, storage] => { const [stored_value, ticket_opt] = storage; const new_storage: storage = - match(ticket_opt) { + $match(ticket_opt, { // If there is a ticket, add its amount to the int in storage - when(Some(ticket)): ((ticket: ticket) => { - const [[_address, [_payload, amount]], new_ticket] = Tezos.Next.Ticket.read(ticket); - return [stored_value + int(amount), Some(new_ticket)]; - })(ticket); + "Some": ticket => ((ticket: ticket) => { + const [[_address, [_payload, amount]], new_ticket] = Tezos.Ticket.read(ticket); + return [stored_value + int(amount), ["Some" as "Some", new_ticket]]; + })(ticket), // If there is no ticket in storage, do nothing - when(None()): [stored_value, None()]; - }; - return [list([]), new_storage]; + "None": () => [stored_value, ["None" as "None"]] + }); + return [[], new_storage]; }; - } ``` @@ -134,7 +131,6 @@ Then, the test uses the proxy contract to originate the contract to test: ```cameligo group=usage_orig let test_originate_contract = - // Create a function that the proxy runs to return the contract storage let create_storage = fun (t : bytes ticket) : MyContract.storage -> (0, Some t) in let ticket_bytes : bytes = 0x0202 in @@ -142,7 +138,7 @@ let test_originate_contract = let ticket_info = ticket_bytes, ticket_amount in // Create the proxy contract and use it to originate the contract - let addr = Test.Proxy_ticket.originate ticket_info create_storage MyContract.main in + let addr = Test.Ticket.Proxy.originate ticket_info create_storage MyContract.main in // ... ``` @@ -151,34 +147,34 @@ let test_originate_contract = ```jsligo group=usage_orig -const test_originate_contract = do { - +const test_originate_contract = (() => { // Create a function that the proxy runs to return the contract storage - const create_storage = (t: ticket): MyContract.storage => [0, Some (t)]; - const ticket_info = [0x0202, 15n]; + const create_storage = (t: ticket): MyContract.storage => + [0, ["Some" as "Some", t]]; + const ticket_info = [0x0202, 15 as nat]; // Create the proxy contract and use it to originate the contract - const addr = Test.Proxy_ticket.originate (ticket_info, create_storage, MyContract.main); + const addr = Test.Ticket.Proxy.originate (ticket_info, create_storage, MyContract.main); // ... ``` -To verify that the ticket is in the contract storage, you must use the `Test.Proxy_ticket.get_storage` function to retrieve the ticket from the contract storage. -This function provides tickets as _unforged tickets_, which are tickets that you can read freely without destroying them and recreating them with the `Tezos.Next.Ticket.read` function. +To verify that the ticket is in the contract storage, you must use the `Test.Ticket.Proxy.get_storage` function to retrieve the ticket from the contract storage. +This function provides tickets as _unforged tickets_, which are tickets that you can read freely without destroying them and recreating them with the `Tezos.Ticket.read` function. In this code, the test retrieves the ticket from the contract and verifies its contents: ```cameligo group=usage_orig // The ticket 'unforged_ticket_opt' can be manipulated freely without being destroyed - let unforged_storage : MyContract.unforged_storage = Test.Proxy_ticket.get_storage addr in + let unforged_storage : MyContract.unforged_storage = Test.Ticket.Proxy.get_storage addr in let (_stored_value, unforged_ticket_opt) = unforged_storage in // Verify that the ticket is in storage let () = match unforged_ticket_opt with Some unforged_ticket -> - let () = Test.Next.IO.log ("unforged_ticket",unforged_ticket) in + let () = Test.IO.log ("unforged_ticket",unforged_ticket) in let { ticketer ; value ; amount } = unforged_ticket in let () = Assert.assert (value = ticket_bytes) in Assert.assert (amount = ticket_amount) @@ -186,9 +182,9 @@ In this code, the test retrieves the ticket from the contract and verifies its c in // Call the entrypoint and verify that the value in storage changes - let _ : nat = Test.Next.Contract.transfer_exn (Test.Next.Typed_address.get_entrypoint"default" addr) unit 0tez in - let new_storage : MyContract.unforged_storage = Test.Proxy_ticket.get_storage addr in - let (new_stored_value, _unforged_ticket_opt) = new_storage in + let _ : nat = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint"default" addr) unit 0tez in + let new_storage : MyContract.unforged_storage = Test.Ticket.Proxy.get_storage addr in + let new_stored_value, _unforged_ticket_opt = new_storage in Assert.assert (new_stored_value = (int ticket_amount)) ``` @@ -198,25 +194,26 @@ In this code, the test retrieves the ticket from the contract and verifies its c ```jsligo group=usage_orig // The ticket 'unforged_ticket_opt' can be manipulated freely without being destroyed - const [_stored_value, unforged_ticket_opt] = (Test.Proxy_ticket.get_storage(addr) as MyContract.unforged_storage); + const [_stored_value, unforged_ticket_opt] = (Test.Ticket.Proxy.get_storage(addr) as MyContract.unforged_storage); // Verify that the ticket is in storage - match (unforged_ticket_opt) { - when(Some(x)): do { - Test.Next.IO.log(["unforged_ticket", x]); + $match(unforged_ticket_opt, { + "Some": x => (() => { + Test.IO.log(["unforged_ticket", x]); const { ticketer: _, value, amount } = x; Assert.assert(value == ticket_info[0]); Assert.assert(amount == ticket_info[1]); return unit - }; - when(None()): failwith ("impossible") - }; + })(), + "None": () => failwith ("impossible") + }); // Call the entrypoint and verify that the value in storage changes - Test.Next.Contract.transfer_exn(Test.Next.Typed_address.get_entrypoint("default", addr), unit, 0tez); - const [new_stored_value, _unforged_ticket_opt] = Test.Proxy_ticket.get_storage(addr) as MyContract.unforged_storage; + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("default", + addr), unit, 0 as tez); + const [new_stored_value, _unforged_ticket_opt] = Test.Ticket.Proxy.get_storage(addr) as MyContract.unforged_storage; Assert.assert(new_stored_value == int(ticket_info[1])); -}; +})(); ``` Note that because this is a single-entrypoint contract, the LIGO compiler renames the entrypoint to `default`. @@ -238,10 +235,11 @@ module MyContract = struct type storage = int type param = int * int ticket - [@entry] let main (param : param) (storage : storage) : operation list * storage = - let (multiplier, ticket) = param in + [@entry] + let main (param : param) (storage : storage) : operation list * storage = + let multiplier, ticket = param in // Read the ticket, destroy it, and add its amount times the multiplier to storage - let ((_address, (payload, amount)), _ticket) = Tezos.Next.Ticket.read ticket in + let (_address, (payload, amount)), _ticket = Tezos.Ticket.read ticket in [], (storage + (multiplier * payload * (int amount))) end ``` @@ -255,11 +253,11 @@ namespace MyContract { type storage = int; export type param = [int, ticket]; - @entry + // @entry function main (param: param, storage: storage): [list, storage] { const [multiplier, ticket] = param; // Read the ticket, destroy it, and add its amount times the multiplier to storage - const [[_address, [payload, amount]], _ticket] = Tezos.Next.Ticket.read(ticket); + const [[_address, [payload, amount]], _ticket] = Tezos.Ticket.read(ticket); return ([[], storage + (multiplier * payload * int(amount))]); }; } @@ -275,20 +273,20 @@ Then create a function that returns the parameter for the entrypoint, create a p ```cameligo group=usage_entrypoint let test_transfer_to_contract = // Originate the contract as usual - let orig = Test.Next.Originate.contract (contract_of MyContract) 0 0tez in - let main_addr = Test.Next.Typed_address.to_address orig.taddr in + let orig = Test.Originate.contract (contract_of MyContract) 0 0tez in + let main_addr = Test.Typed_address.to_address orig.taddr in // Create a function that the proxy runs to return the parameter let create_param : int ticket -> MyContract.param = fun (t : int ticket) -> 5, t in // Create the proxy contract - let proxy_taddr = Test.Proxy_ticket.init_transfer create_param in - let () = Test.Next.IO.log ("proxy addr:", proxy_taddr) in + let proxy_taddr = Test.Ticket.Proxy.init_transfer create_param in + let () = Test.IO.log ("proxy addr:", proxy_taddr) in // Use the proxy to call the entrypoint let ticket_info = 3, 10n in - let _ : test_exec_result = Test.Proxy_ticket.transfer proxy_taddr (ticket_info, main_addr) in - Assert.assert ((Test.Next.Typed_address.get_storage orig.taddr) = 150) + let _ : test_exec_result = Test.Ticket.Proxy.transfer proxy_taddr (ticket_info, main_addr) in + Assert.assert (Test.Typed_address.get_storage orig.taddr = 150) ``` @@ -296,25 +294,26 @@ let test_transfer_to_contract = ```jsligo group=usage_entrypoint -const test_transfer_to_contract = do { +const test_transfer_to_contract = (() => { // Originate the contract as usual - let orig = Test.Next.Originate.contract(contract_of(MyContract), 0, 0tez); - let main_addr = Test.Next.Typed_address.to_address(orig.taddr); + let orig = Test.Originate.contract(contract_of(MyContract), 0, + 0 as tez); + let main_addr = Test.Typed_address.to_address(orig.taddr); // Create a function that the proxy runs to return the parameter const create_param = (t: ticket): MyContract.param => [5, t]; // Create the proxy contract - const proxy_taddr = Test.Proxy_ticket.init_transfer(create_param); - Test.Next.IO.log(["proxy addr:", proxy_taddr]); + const proxy_taddr = Test.Ticket.Proxy.init_transfer(create_param); + Test.IO.log(["proxy addr:", proxy_taddr]); // Use the proxy to call the entrypoint - const ticket_info = [3, 10n]; - Test.Proxy_ticket.transfer(proxy_taddr, [ticket_info, main_addr]); + const ticket_info = [3, 10 as nat]; + Test.Ticket.Proxy.transfer(proxy_taddr, [ticket_info, main_addr]); // Verify that the value in storage changes - Assert.assert(Test.Next.Typed_address.get_storage(orig.taddr) == 150); -}; + Assert.assert(Test.Typed_address.get_storage(orig.taddr) == 150); +})(); ``` diff --git a/gitlab-pages/docs/testing/testing.md b/gitlab-pages/docs/testing/testing.md index fc405cfe17..4d04a2e397 100644 --- a/gitlab-pages/docs/testing/testing.md +++ b/gitlab-pages/docs/testing/testing.md @@ -30,7 +30,8 @@ This module provides ways of originating contracts and executing transactions in The LIGO interpreter uses the [same library that Tezos internally uses for testing](https://gitlab.com/tezos/tezos/-/tree/master/src/proto_alpha/lib_protocol/test/helpers). ::: -To originate a contract in the test simulation, use the `Test.Next.originate` function, which accepts these parameters: +To originate a contract in the test simulation, use the +`Test.Originate.contract` function, which accepts these parameters: - The contract itself - The initial storage value @@ -42,7 +43,7 @@ The function returns an object that has these values: - `size`: The size of the deployed contract in bytes, as an integer - `code`: The Michelson code of the contract -You can get the storage of a deployed contract by passing the address of the contract to the `Test.Next.Typed_address.get_storage` function. +You can get the storage of a deployed contract by passing the address of the contract to the `Test.Typed_address.get_storage` function. For example, this LIGO file includes a simple counter contract: @@ -70,9 +71,14 @@ export namespace MyContract { export type storage = int; export type result = [list, storage]; - @entry const increment = (delta : int, storage : storage) : result => [[], storage + delta]; - @entry const decrement = (delta : int, storage : storage) : result => [[], storage - delta]; - @entry const reset = (_u : unit, _storage : storage) : result => [[], 0]; + // @entry + const increment = (delta : int, storage : storage) : result => [[], storage + delta]; + + // @entry + const decrement = (delta : int, storage : storage) : result => [[], storage - delta]; + + // @entry + const reset = (_u : unit, _storage : storage) : result => [[], 0]; } ``` @@ -89,17 +95,13 @@ It follows these basic steps: 1. In the function, it creates a value for the initial storage of the contract. 1. It originates the contract to the test simulation with the initial storage. 1. It verifies that the deployed contract has the storage value. -1. It calls the `increment` entrypoint with the `Test.Next.Contract.transfer_exn` function, passing the entrypoint, the parameter, and 0 tez. +1. It calls the `increment` entrypoint with the `Test.Contract.transfer_exn` function, passing the entrypoint, the parameter, and 0 tez. 1. It verifies the updated storage value. ```cameligo test-ligo group=mycontract-test -(* This is mycontract-test.mligo *) - -module Test = Test.Next - -#import "gitlab-pages/docs/testing/src/testing/mycontract.mligo" "MyContract" +module MyContract = Gitlab_pages.Docs.Testing.Src.Testing.Mycontract let run_test1 = let initial_storage = 10 in @@ -114,17 +116,19 @@ let run_test1 = ```jsligo test-ligo group=mycontract-test -// This is mycontract-test.jligo - -import Test = Test.Next; - -#import "gitlab-pages/docs/testing/src/testing/mycontract.jsligo" "MyModule" +import * as MyContract from "gitlab-pages/docs/testing/src/testing/mycontract.jsligo"; const run_test1 = () => { - let initial_storage = 10; - let orig = Test.Originate.contract(contract_of(MyModule.MyContract), initial_storage, 0tez); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("increment", orig.taddr), 5, 0tez); - return Assert.assert(Test.Typed_address.get_storage(orig.taddr) == initial_storage + 5); + let initial_storage = 10; + let orig = Test.Originate.contract( + contract_of(MyContract.MyContract), + initial_storage, + 0 as tez); + Test.Contract.transfer_exn( + Test.Typed_address.get_entrypoint("increment", orig.taddr), + 5, + 0 as tez); + return Assert.assert(Test.Typed_address.get_storage(orig.taddr) == initial_storage + 5); }; const test1 = run_test1(); @@ -132,11 +136,11 @@ const test1 = run_test1(); -The `run test` command evaluates all top-level definitions and prints any -entries that begin with the prefix `test` as well as the value that these -definitions evaluate to. If any of the definitions fail, it prints a message -with the line number where the problem occurred. -You can also log messages to the console with the `Test.Next.IO.log` function. +The `run test` command evaluates all top-level definitions and prints +any entries that begin with the prefix `test` as well as the value +that these definitions evaluate to. If any of the definitions fail, it +prints a message with the line number where the problem occurred. You +can also log messages to the console with the `Test.IO.log` function. To run the tests, pass the file with the tests to the `run test` command. If the file imports other files, pass the folders that contain these files in the `--library` argument, as in this example: @@ -166,8 +170,9 @@ Everything at the top-level was executed. ### Creating transactions -The function `Test.Next.Contract.transfer_exn` creates a transaction in the test simulation, as in the example in the previous section. -It takes these parameters: +The function `Test.Contract.transfer_exn` creates a transaction in the +test simulation, as in the example in the previous section. It takes +these parameters: - The target entrypoint or account to call - The parameter to pass @@ -176,13 +181,18 @@ It takes these parameters: If the transaction succeeds, it returns the gas consumption. If it fails, it fails the test. -For greater control, such as to test error conditions and error messages, you can use the function `Test.Next.Contract.transfer`. -The function takes the same parameters but returns an option of the type `test_exec_result`, which is `Fail` if the transaction failed and `Success` if it succeeded. -In case of success the value is the gas consumed and in case of failure the value is an object of the type `test_exec_error` that describes the error. +For greater control, such as to test error conditions and error +messages, you can use the function `Test.Contract.transfer`. The +function takes the same parameters but returns an option of the type +`test_exec_result`, which is `Fail` if the transaction failed and +`Success` if it succeeded. In case of success the value is the gas +consumed and in case of failure the value is an object of the type +`test_exec_error` that describes the error. :::warning -If you create a transaction with `Test.Next.Contract.transfer` and the transaction fails, the test does not automatically fail. -You must check the result of the transaction to see if it succeeded or failed. +If you create a transaction with `Test.Contract.transfer` and the +transaction fails, the test does not automatically fail. You must +check the result of the transaction to see if it succeeded or failed. ::: For example, this contract is similar to the contract in an earlier example, but it only allows the number in storage to change by 5 or less with each transaction: @@ -205,8 +215,6 @@ end This test verifies that the error works by passing a number larger than 5 and handling the error: ```cameligo group=mycontract-failures -module Test = Test.Next - let test_failure = let initial_storage = 10 in let orig = Test.Originate.contract (contract_of MyContract) initial_storage 0tez in @@ -225,28 +233,34 @@ namespace MyContract { export type storage = int; export type result = [list, storage]; - @entry const increment = (delta : int, storage : storage) : result => - abs(delta) <= 5n ? [[], storage + delta] : failwith("Pass 5 or less"); - @entry const decrement = (delta : int, storage : storage) : result => - abs(delta) <= 5n ? [[], storage - delta] : failwith("Pass 5 or less"); - @entry const reset = (_u : unit, _storage : storage) : result => [[], 0]; + // @entry + const increment = (delta : int, storage : storage) : result => + abs(delta) <= (5 as nat) ? [[], storage + delta] : failwith("Pass 5 or less"); + + // @entry + const decrement = (delta : int, storage : storage) : result => + abs(delta) <= (5 as nat) ? [[], storage - delta] : failwith("Pass 5 or less"); + + // @entry + const reset = (_u : unit, _storage : storage) : result => [[], 0]; } ``` This test verifies that the error works by passing a number larger than 5 and handling the error: ```jsligo group=mycontract-failures -import Test = Test.Next; - const test_failure = () => { const initial_storage = 10 as int; - const orig = Test.Originate.contract(contract_of(MyContract), initial_storage, 0tez); - const result = Test.Contract.transfer(Test.Typed_address.get_entrypoint("increment", orig.taddr), 50 as int, 0tez); - - match(result) { - when(Fail(_x)): Test.IO.log("Failed as expected"); - when(Success(_s)): failwith("This should not succeed") - }; + const orig = Test.Originate.contract(contract_of(MyContract), + initial_storage, 0 as tez); + const result = + Test.Contract.transfer(Test.Typed_address.get_entrypoint("increment", + orig.taddr), 50 as int, 0 as tez); + + $match(result, { + "Fail": _x => Test.IO.log("Failed as expected"), + "Success": _s => failwith("This should not succeed") + }); } const test1 = test_failure(); @@ -294,25 +308,24 @@ namespace Counter { type storage = [int, address]; type return_type = [list, storage]; - @entry + // @entry const increment = (n: int, storage: storage): return_type => { const [number, admin_account] = storage; return [[], [number + n, admin_account]]; } - @entry + // @entry const decrement = (n: int, storage: storage): return_type => { const [number, admin_account] = storage; return [[], [number - n, admin_account]]; } - @entry + // @entry const reset = (_: unit, storage: storage): return_type => { const [_number, admin_account] = storage; - if (Tezos.get_sender() != admin_account) { + if (Tezos.get_sender() != admin_account) return failwith("Only the owner can call this entrypoint"); - } return [[], [0, admin_account]]; } @@ -321,8 +334,10 @@ namespace Counter { -To generate test accounts, pass a nat to the `Test.Next.Account.address` function, which returns an address. -Then use the `Test.Next.State.set_source` function to set the source account for transactions. +To generate test accounts, pass a nat to the `Test.Account.address` +function, which returns an address. Then use the +`Test.State.set_source` function to set the source account for +transactions. This example creates an admin account and user account. It attempts to call the `reset` entrypoint as the user account and expects it to fail. @@ -331,8 +346,6 @@ Then it calls the `reset` entrypoint as the admin account and verifies that the ```cameligo group=test-accounts -module Test = Test.Next - let test_admin = let (admin_account, user_account) = (Test.Account.address(0n), Test.Account.address(1n)) in @@ -360,44 +373,47 @@ let test_admin = ```jsligo group=test-accounts -import Test = Test.Next; - const test_admin = (() => { - const admin_account = Test.Account.address(0n); - const user_account = Test.Account.address(1n); + const admin_account = Test.Account.address(0 as nat); + const user_account = Test.Account.address(1 as nat); // Originate the contract with the admin account in storage const initial_storage = [10 as int, admin_account]; - const orig = Test.Originate.contract(contract_of(Counter), initial_storage, 0tez); + const orig = + Test.Originate.contract(contract_of(Counter), initial_storage, 0 as tez); // Try to call the reset entrypoint as the user and expect it to fail Test.State.set_source(user_account); - const result = Test.Contract.transfer(Test.Typed_address.get_entrypoint("reset", orig.taddr), unit, 0tez); - match(result) { - when(Fail(_err)): Test.IO.log("Test succeeded"); - when (Success(_s)): failwith("User should not be able to call reset"); - }; + const result = + Test.Contract.transfer(Test.Typed_address.get_entrypoint("reset", + orig.taddr), unit, 0 as tez); + $match(result, { + "Fail": _err => Test.IO.log("Test succeeded"), + "Success": _s => failwith("User should not be able to call reset") + }); // Call the reset entrypoint as the admin and expect it to succeed Test.State.set_source(admin_account); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("reset", orig.taddr), unit, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("reset", + orig.taddr), unit, 0 as tez); - const [newNumber, _admin_account] = Test.Typed_address.get_storage(orig.taddr); + const [newNumber, _admin_account] = + Test.Typed_address.get_storage(orig.taddr); Assert.assert(newNumber == 0); -}) () +})() ``` -By default, the test simulation has two test accounts. -To create more, pass the number of accounts and a list of their balances or an empty list to use the default balance to the `Test.Next.State.Reset` function, as in the following example. -The default balance is 4000000 tez minus %5 that is frozen so the account can act as a validator. +By default, the test simulation has two test accounts. To create +more, pass the number of accounts and a list of their balances or an +empty list to use the default balance to the `Test.State.reset` +function, as in the following example. The default balance is 4000000 +tez minus %5 that is frozen so the account can act as a validator. ```cameligo test-ligo group=reset -module Test = Test.Next - let test_accounts = let initial_balances : tez list = [] in let () = Test.State.reset 3n initial_balances in @@ -418,20 +434,18 @@ let test_accounts = ```jsligo test-ligo group=reset -import Test = Test.Next; - const test_accounts = () => { - Test.State.reset(3n, [] as list ); - const admin_account = Test.Account.address(0n); - const user_account1 = Test.Account.address(1n); - const user_account2 = Test.Account.address(2n); + Test.State.reset(3 as nat, [] as list); + const admin_account = Test.Account.address(0 as nat); + const user_account1 = Test.Account.address(1 as nat); + const user_account2 = Test.Account.address(2 as nat); Test.IO.log(Test.Address.get_balance(admin_account)); - // 3800000000000mutez + // 3800000000000 as mutez Test.IO.log(Test.Address.get_balance(user_account1)); - // 3800000000000mutez + // 3800000000000 as mutez Test.IO.log(Test.Address.get_balance(user_account2)); - // 3800000000000mutez + // 3800000000000 as mutez } ``` @@ -468,13 +482,13 @@ module Testviews = struct end ``` -This test casts the contract's typed address to an ordinary address type and uses that address to call the view with the function `Tezos.Next.View.call` and pass the parameter `unit`. -This function returns an option, so the test matches the option to verify the response from the view: +This test casts the contract's typed address to an ordinary address +type and uses that address to call the view with the function +`Tezos.View.call` and pass the parameter `unit`. This function +returns an option, so the test matches the option to verify the +response from the view: ```cameligo group=test_views -module Test = Test.Next -module Tezos = Tezos.Next - let test_view = let contract = Test.Originate.contract (contract_of Testviews) "" 0tez in let _ : nat = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "set" contract.taddr) "hello" 0tez in @@ -493,41 +507,38 @@ let test_view = ```jsligo group=test_views namespace Testviews { - type storage = string; - type return_type = [list, storage]; - @entry + // @entry const set = (inputStr: storage, _storage: storage): return_type => - [list([]), inputStr]; + [[], inputStr]; - @entry + // @entry const reset = (_u: unit, _storage: storage): return_type => - [list([]), ""]; + [[], ""]; - @view + // @view const getString = (_u: unit, storage: storage): string => storage; } ``` -This test casts the contract's typed address to an ordinary address type and uses that address to call the view with the function `Tezos.Next.View.call`. +This test casts the contract's typed address to an ordinary address type and uses that address to call the view with the function `Tezos.View.call`. This function returns an option, so the test matches the option to verify the response from the view: ```jsligo group=test_views -import Test = Test.Next; -import Tezos = Tezos.Next; - const test_view = () => { - const contract = Test.Originate.contract(contract_of(Testviews), "", 0tez); - Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("set", contract.taddr), "hello", 0tez); + const contract = Test.Originate.contract(contract_of(Testviews), "", + 0 as tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("set", + contract.taddr), "hello", 0 as tez); const address = Test.Typed_address.to_address(contract.taddr); const viewResultOption: option = Tezos.View.call("getString", unit, address); - const viewResult = match(viewResultOption) { - when(Some(str)): str; - when(None()): ""; - }; + const viewResult = $match(viewResultOption, { + "Some": str => str, + "None": () => "" + }); Assert.assert(Test.Compare.eq(viewResult, "hello")); }; const test1 = test_view(); @@ -537,18 +548,21 @@ const test1 = test_view(); ### Testing events -To test events, emit them as usual with the `Tezos.emit` function and use the `Test.Next.State.last_events` function to capture the most recent events, as in this example: +To test events, emit them as usual with the `Tezos.Operation.emit` +function and use the `Test.State.last_events` function to capture the +most recent events, as in this example: ```cameligo test-ligo group=test_ex module C = struct - [@entry] let main (p : int*int) () = - [Tezos.emit "%foo" p ; Tezos.emit "%foo" p.0],() + [@entry] + let main (p : int * int) () = + let op1 = Tezos.Operation.emit "%foo" p in + let op2 = Tezos.Operation.emit "%foo" p.0 in + [op1; op2], () end -module Test = Test.Next - let test_foo = let orig = Test.Originate.contract (contract_of C) () 0tez in let _: nat = Test.Typed_address.transfer_exn orig.taddr (Main (1,2)) 0tez in @@ -561,19 +575,17 @@ let test_foo = ```jsligo test-ligo group=test_ex namespace C { - @entry - const main = (p: [int, int], _: unit) => { - const op1 = Tezos.emit("%foo", p); - const op2 = Tezos.emit("%foo", p[0]); - return [([op1, op2] as list), unit]; + // @entry + const main = (p: [int, int], _: unit) : [list, unit] => { + const op1 = Tezos.Operation.emit("%foo", p); + const op2 = Tezos.Operation.emit("%foo", p[0]); + return [[op1, op2], unit]; }; } -import Test = Test.Next; - const test = () => { - const orig = Test.Originate.contract(contract_of(C), unit, 0tez); - Test.Typed_address.transfer_exn(orig.taddr, Main ([1,2]), 0tez); + const orig = Test.Originate.contract(contract_of(C), unit, 0 as tez); + Test.Typed_address.transfer_exn(orig.taddr, ["Main" as "Main", [1,2]], 0 as tez); return [Test.State.last_events(orig.taddr, "foo") as list<[int, int]>, Test.State.last_events(orig.taddr, "foo") as list]; }; @@ -610,12 +622,12 @@ let remove_balances_under (b : balances) (threshold : tez) : balances = ```jsligo group=remove-balance // This is remove-balance.jsligo -type balances = map ; +export type balances = map; -const remove_balances_under = (b: balances, threshold: tez): balances => { +export function remove_balances_under (b: balances, threshold: tez): balances { let f = ([acc, kv]: [balances, [address, tez]] ): balances => { - let [k, v] = kv; - if (v < threshold) { return Map.remove (k, acc) } else {return acc} + const [k, v] = kv; + if (v < threshold) return Map.remove (k, acc); else return acc }; return Map.fold (f, b, b); } @@ -634,8 +646,6 @@ First, include the file under test and reset the state with 5 bootstrap accounts ```cameligo test-ligo group=unit-remove-balance-mixed #include "./gitlab-pages/docs/testing/src/testing/remove-balance.mligo" -module Test = Test.Next - let test_remove_balance = let () = Test.State.reset 5n ([]: tez list) in ``` @@ -645,12 +655,14 @@ let test_remove_balance = ```jsligo test-ligo group=unit-remove-balance-mixed -#include "./gitlab-pages/docs/testing/src/testing/remove-balance.jsligo" +import * as RemoveBalance from +"./gitlab-pages/docs/testing/src/testing/remove-balance.jsligo"; -import Test = Test.Next; +type balances = RemoveBalance.balances +const remove_balances_under = RemoveBalance.remove_balances_under const test_remove_balance = (() => { - Test.State.reset(5n, [] as list ); + Test.State.reset(5 as nat, [] as list); ``` @@ -666,25 +678,28 @@ let balances: balances = ``` + ```jsligo test-ligo group=unit-remove-balance-mixed const balances: balances = - Map.literal([[Test.Account.address(1n), 10tez], - [Test.Account.address(2n), 100tez], - [Test.Account.address(3n), 1000tez]]); + Map.literal([[Test.Account.address(1 as nat), 10 as tez], + [Test.Account.address(2 as nat), 100 as tez], + [Test.Account.address(3 as nat), 1000 as tez]]); ``` -The test loop will call the function with the compiled map -defined above, get the size of the resulting map, and compare it to an -expected value with `Test.Next.Compare.eq`. +The test loop will call the function with the compiled map defined +above, get the size of the resulting map, and compare it to an +expected value with `Test.Compare.eq`. -The call to `remove_balances_under` and the computation of the size of the resulting map is achieved through the primitive `Test.Next.Michelson.run`. -This primitive runs a function on an input, translating both (function and input) -to Michelson before running on the Michelson interpreter. -More concretely `Test.Next.Michelson.run f v` performs the following: +The call to `remove_balances_under` and the computation of the size of +the resulting map is achieved through the primitive +`Test.Michelson.run`. This primitive runs a function on an input, +translating both (function and input) to Michelson before running on +the Michelson interpreter. More concretely `Test.Michelson.run f v` +performs the following: 1. Compiles the function argument `f` to Michelson `f_mich` 2. Compiles the value argument `v` (which was already evaluated) to Michelson `v_mich` @@ -710,6 +725,7 @@ List.iter ``` + ```jsligo test-ligo group=unit-remove-balance-mixed @@ -722,8 +738,10 @@ return List.iter(([threshold, expected_size]: [tez, nat]): unit => { Test.IO.log(["actual", size]); return (Assert.assert (Test.Compare.eq(size, expected_size_))) }, - list ([ [15tez, 2n], [130tez, 1n], [1200tez, 0n]]) ); -}) () + list ([ [15 as tez, 2 as nat], + [130 as tez, 1 as nat], + [1200 as tez, 0 as nat]])); +})() ``` @@ -735,8 +753,6 @@ Here is the complete test file: ```cameligo test-ligo group=unit-remove-balance-complete #include "./gitlab-pages/docs/testing/src/testing/remove-balance.mligo" -module Test = Test.Next - let test_remove_balance = let () = Test.State.reset 5n ([]: tez list) in let balances: balances = @@ -759,16 +775,18 @@ let balances: balances = ```jsligo test-ligo group=unit-remove-balance-complete -#include "./gitlab-pages/docs/testing/src/testing/remove-balance.jsligo" +import * as RemoveBalance from +"./gitlab-pages/docs/testing/src/testing/remove-balance.jsligo"; -import Test = Test.Next; +type balances = RemoveBalance.balances +const remove_balances_under = RemoveBalance.remove_balances_under const test_remove_balance = (() => { - Test.State.reset(5n, [] as list ); + Test.State.reset(5 as nat, [] as list ); const balances: balances = - Map.literal([[Test.Account.address(1n), 10tez], - [Test.Account.address(2n), 100tez], - [Test.Account.address(3n), 1000tez]]); + Map.literal([[Test.Account.address(1 as nat), 10 as tez], + [Test.Account.address(2 as nat), 100 as tez], + [Test.Account.address(3 as nat), 1000 as tez]]); return List.iter(([threshold, expected_size]: [tez, nat]): unit => { const tester = ([balances, threshold]: [balances, tez]): nat => Map.size (remove_balances_under (balances, threshold)); @@ -778,7 +796,8 @@ const test_remove_balance = (() => { Test.IO.log(["actual", size]); return (Assert.assert (Test.Compare.eq(size, expected_size_))) }, - list ([ [15tez, 2n], [130tez, 1n], [1200tez, 0n]]) ); + list ([ [15 as tez, 2 as nat], [130 as tez, 1 as nat], + [1200 as tez, 0 as nat]]) ); }) () ``` @@ -843,9 +862,8 @@ let encodeEntry (a : int) (b : string): myDataType = // This is interpret.jsligo type myDataType = map; -const encodeEntry = (a: int, b: string): myDataType => { - return Map.literal([[a, b]]); -} +const encodeEntry = (a: int, b: string): myDataType => + Map.literal([[a, b]]); ``` @@ -931,7 +949,7 @@ namespace Counter { type storage_type = int; type return_type = [list, storage_type]; - @entry + // @entry const main = (_action: unit, storage: storage_type): return_type => [[], storage + 1] } @@ -988,7 +1006,7 @@ namespace MyContract { type storage_type = map; type return_type = [list, storage_type]; - @entry + // @entry const update = (param: [nat, string], storage: storage_type): return_type => { const [index, value] = param; const updated_map = Map.add(index, value, storage); @@ -1003,7 +1021,7 @@ type as the contract storage as the initial value of the storage: ```bash ligo run dry-run -m MyContract gitlab-pages/docs/testing/src/testing/dry-run-complex.jsligo \ - 'Update(1n, "new value")' \ + '["Update" as "Update", [1 as nat, "new value"]]' \ 'Map.empty as map' ``` diff --git a/gitlab-pages/docs/tezos/big_maps/searching.md b/gitlab-pages/docs/tezos/big_maps/searching.md index 9a54882529..2eb6e03e8a 100644 --- a/gitlab-pages/docs/tezos/big_maps/searching.md +++ b/gitlab-pages/docs/tezos/big_maps/searching.md @@ -65,12 +65,11 @@ let force_access key map = ```jsligo group=big_map_searching -let force_access = (key, map) => { - return match(Big_map.find_opt (key, map)) { - when(Some(value)): value; - when(None): failwith("No value.") - }; -}; +const force_access = (key, map) => + $match(Big_map.find_opt (key, map), { + "Some": value => value, + "None": () => failwith("No value.") + }); ``` diff --git a/gitlab-pages/docs/tezos/big_maps/src/searching/big_map_searching.jsligo b/gitlab-pages/docs/tezos/big_maps/src/searching/big_map_searching.jsligo index c0e2d70b2e..43d8748f58 100644 --- a/gitlab-pages/docs/tezos/big_maps/src/searching/big_map_searching.jsligo +++ b/gitlab-pages/docs/tezos/big_maps/src/searching/big_map_searching.jsligo @@ -2,9 +2,8 @@ const my_map: big_map = Big_map.literal([[1,"one"],[2,"two"]]); const contains_2: bool = Big_map.mem(2, my_map); // == true const v : option = Big_map.find_opt(2, my_map); -let force_access = (key, map) => { - return match(Big_map.find_opt (key, map)) { - when(Some(value)): value; - when(None): failwith("No value.") - }; -}; \ No newline at end of file +const force_access = (key, map) => + $match(Big_map.find_opt (key, map), { + "Some": value => value, + "None": () => failwith("No value.") + }); \ No newline at end of file diff --git a/gitlab-pages/docs/tezos/big_maps/src/updating/big_map_updating.jsligo b/gitlab-pages/docs/tezos/big_maps/src/updating/big_map_updating.jsligo index 1e246cee3c..bc2ee6ca48 100644 --- a/gitlab-pages/docs/tezos/big_maps/src/updating/big_map_updating.jsligo +++ b/gitlab-pages/docs/tezos/big_maps/src/updating/big_map_updating.jsligo @@ -1,9 +1,9 @@ const my_map: big_map = Big_map.literal([[1,"one"],[2,"two"]]); -const map_with_3 = Big_map.update (3, Some("three"), my_map); +const map_with_3 = Big_map.update (3, ["Some" as "Some", "three"], my_map); const contains_3 = Big_map.mem(3, map_with_3); // == true -const map_without_2 = Big_map.update(2, None(), my_map); +const map_without_2 = Big_map.update(2, ["None" as "None"], my_map); const contains_2 = Big_map.mem (2, map_without_2); // == false -// three == Some("three") +// three == ["Some" as "Some", "three"] const [three, map_without_3] = - Big_map.get_and_update(3, None(), map_with_3); \ No newline at end of file + Big_map.get_and_update(3, ["None" as "None"], map_with_3); \ No newline at end of file diff --git a/gitlab-pages/docs/tezos/big_maps/updating.md b/gitlab-pages/docs/tezos/big_maps/updating.md index 9e78e0fab7..622f8d205b 100644 --- a/gitlab-pages/docs/tezos/big_maps/updating.md +++ b/gitlab-pages/docs/tezos/big_maps/updating.md @@ -30,9 +30,9 @@ let contains_2 = Big_map.mem 2 map_without_2 // = false ```jsligo group=big_map_updating const my_map: big_map = Big_map.literal([[1,"one"],[2,"two"]]); -const map_with_3 = Big_map.update (3, Some("three"), my_map); +const map_with_3 = Big_map.update (3, ["Some" as "Some", "three"], my_map); const contains_3 = Big_map.mem(3, map_with_3); // == true -const map_without_2 = Big_map.update(2, None(), my_map); +const map_without_2 = Big_map.update(2, ["None" as "None"], my_map); const contains_2 = Big_map.mem (2, map_without_2); // == false ``` @@ -53,9 +53,9 @@ let three, map_without_3 = Big_map.get_and_update 3 None map_with_3 ```jsligo group=big_map_updating -// three == Some("three") +// three == ["Some" as "Some", "three"] const [three, map_without_3] = - Big_map.get_and_update(3, None(), map_with_3); + Big_map.get_and_update(3, ["None" as "None"], map_with_3); ``` diff --git a/gitlab-pages/docs/tutorials/getting-started/getting-started.md b/gitlab-pages/docs/tutorials/getting-started/getting-started.md index b8d800715f..118f8b674b 100644 --- a/gitlab-pages/docs/tutorials/getting-started/getting-started.md +++ b/gitlab-pages/docs/tutorials/getting-started/getting-started.md @@ -118,7 +118,7 @@ type return_type = operation list * storage 1. Add an entrypoint named `add` that accepts an integer as a parameter and adds it to the storage value: ```jsligo - @entry + // @entry const add = (n : int, storage : storage) : return_type => [[], storage + n]; ``` @@ -132,7 +132,7 @@ type return_type = operation list * storage 1. Similarly, add an entrypoint named `sub` that accepts an integer and subtracts it from the storage value: ```jsligo - @entry + // @entry const sub = (n : int, storage : storage) : return_type => [[], storage - n]; ``` @@ -142,10 +142,10 @@ The complete contract looks like this: type storage = int; type return_type = [list, storage]; -@entry +// @entry const add = (n : int, storage : storage) : return_type => [[], storage + n]; -@entry +// @entry const sub = (n : int, storage : storage) : return_type => [[], storage - n]; ``` @@ -169,7 +169,7 @@ ligo run dry-run counter.mligo 'Add(3)' '5' ```bash -ligo run dry-run counter.jsligo 'Add(3)' '5' +ligo run dry-run counter.jsligo '["Add" as "Add", 3]' '5' ``` @@ -200,8 +200,11 @@ Follow these steps to add an automated test to the contract: type storage = int type return_type = operation list * storage - [@entry] let add (n : int) (storage : storage) : return_type = [], storage + n - [@entry] let sub (n : int) (storage : storage) : return_type = [], storage - n + [@entry] + let add (n : int) (storage : storage) : return_type = [], storage + n + + [@entry] + let sub (n : int) (storage : storage) : return_type = [], storage - n end ``` @@ -218,7 +221,7 @@ Follow these steps to add an automated test to the contract: ```cameligo let initial_storage = 10 in - let orig = Test.Next.Originate.contract (contract_of Counter) initial_storage 0tez in + let orig = Test.Originate.contract (contract_of Counter) initial_storage 0tez in ``` This command simulates deploying the contract, setting its initial storage to 10, and setting its initial balance to 0 tez. @@ -226,13 +229,13 @@ Follow these steps to add an automated test to the contract: 1. Add code to call the `add` entrypoint and pass the value 32: ```cameligo - let _ = Test.Next.Contract.transfer_exn (Test.Next.Typed_address.get_entrypoint "add" orig.taddr) 32 0tez in + let _ = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "add" orig.taddr) 32 0tez in ``` 1. Add code to verify that the new value of the storage is correct: ```cameligo - Assert.assert (Test.Next.Typed_address.get_storage(orig.taddr) = initial_storage + 32) + Assert.assert (Test.Typed_address.get_storage(orig.taddr) = initial_storage + 32) ``` The complete code looks like this: @@ -248,9 +251,9 @@ Follow these steps to add an automated test to the contract: let test_add = let initial_storage = 10 in - let orig = Test.Next.Originate.contract (contract_of Counter) initial_storage 0tez in - let _ = Test.Next.Contract.transfer_exn (Test.Next.Typed_address.get_entrypoint "add" orig.taddr) 32 0tez in - Assert.assert (Test.Next.Typed_address.get_storage(orig.taddr) = initial_storage + 32) + let orig = Test.Originate.contract (contract_of Counter) initial_storage 0tez in + let _ = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "add" orig.taddr) 32 0tez in + Assert.assert (Test.Typed_address.get_storage(orig.taddr) = initial_storage + 32) ``` 1. Run this command to run the test: @@ -270,10 +273,10 @@ Follow these steps to add an automated test to the contract: type storage = int; type return_type = [list, storage]; - @entry + // @entry const add = (n : int, storage : storage) : return_type => [[], storage + n]; - @entry + // @entry const sub = (n : int, storage : storage) : return_type => [[], storage - n]; }; ``` @@ -293,7 +296,8 @@ Follow these steps to add an automated test to the contract: ```jsligo const initial_storage = 10 as int; - const orig = Test.Next.Originate.contract(contract_of(Counter), initial_storage, 0tez); + const orig = Test.Originate.contract(contract_of(Counter), + initial_storage, 0 as tez); ``` This command simulates deploying the contract, setting its initial storage to 10, and setting its initial balance to 0 tez. @@ -301,13 +305,14 @@ Follow these steps to add an automated test to the contract: 1. Add code to call the `add` entrypoint and pass the value 32: ```jsligo - Test.Next.Contract.transfer_exn(Test.Next.Typed_address.get_entrypoint("add", orig.taddr), 32 as int, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("add", + orig.taddr), 32 as int, 0 as tez); ``` 1. Add code to verify that the new value of the storage is correct: ```jsligo - return Assert.assert(Test.Next.Typed_address.get_storage(orig.taddr) == initial_storage + 32); + return Assert.assert(Test.Typed_address.get_storage(orig.taddr) == initial_storage + 32); ``` The complete code looks like this: @@ -317,19 +322,21 @@ Follow these steps to add an automated test to the contract: type storage = int; type return_type = [list, storage]; - @entry + // @entry const add = (n : int, storage : storage) : return_type => [[], storage + n]; - @entry + // @entry const sub = (n : int, storage : storage) : return_type => [[], storage - n]; }; const test_add = (() => { const initial_storage = 10 as int; - const orig = Test.Next.Originate.contract(contract_of(Counter), initial_storage, 0tez); - Test.Next.Contract.transfer_exn(Test.Next.Typed_address.get_entrypoint("add", orig.taddr), 32 as int, 0tez); - return Assert.assert(Test.Next.Typed_address.get_storage(orig.taddr) == initial_storage + 32); - }) () + const orig = Test.Originate.contract(contract_of(Counter), + initial_storage, 0 as tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("add", + orig.taddr), 32 as int, 0 as tez); + return Assert.assert(Test.Typed_address.get_storage(orig.taddr) == initial_storage + 32); + })() ``` 1. Run this command to run the test: @@ -507,7 +514,7 @@ To deploy (or originate) the contract you need: The result is the compiled value of the integer in Michelson, which is the same as it is in LIGO. In this case the LIGO storage value maps 1:1 to its Michelson representation. - More complex data types like records and maps look different in Michelson than in LIGO. + More complex data types like records, objects, and maps look different in Michelson than in LIGO. 1. Deploy the contract by running this command, putting the initial storage value in the `--init` argument: @@ -575,4 +582,4 @@ octez-client get contract storage for counter Now you have a simple LIGO smart contract and can test it, deploy it, and call it. You can use it as a starting point to write your own contracts and experiment with LIGO. -You can also continue with the [Taco shop tutorial](../taco-shop/tezos-taco-shop-smart-contract) to learn more about programming with LIGO. +You can also continue with the [Taco shop tutorial](../taco-shop/selling-tacos) to learn more about programming with LIGO. diff --git a/gitlab-pages/docs/tutorials/getting-started/src/getting-started/a.jsligo b/gitlab-pages/docs/tutorials/getting-started/src/getting-started/a.jsligo index 171841608a..23b4f10af8 100644 --- a/gitlab-pages/docs/tutorials/getting-started/src/getting-started/a.jsligo +++ b/gitlab-pages/docs/tutorials/getting-started/src/getting-started/a.jsligo @@ -1,8 +1,8 @@ type storage = int; type return_type = [list, storage]; -@entry +// @entry const add = (n : int, storage : storage) : return_type => [[], storage + n]; -@entry +// @entry const sub = (n : int, storage : storage) : return_type => [[], storage - n]; \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/getting-started/src/getting-started/b.jsligo b/gitlab-pages/docs/tutorials/getting-started/src/getting-started/b.jsligo index 4d912cd144..6080aae36c 100644 --- a/gitlab-pages/docs/tutorials/getting-started/src/getting-started/b.jsligo +++ b/gitlab-pages/docs/tutorials/getting-started/src/getting-started/b.jsligo @@ -11,7 +11,7 @@ namespace Counter { const test_add = (() => { const initial_storage = 10 as int; - const orig = Test.Next.Originate.contract(contract_of(Counter), initial_storage, 0tez); - Test.Next.Contract.transfer_exn(Test.Next.Typed_address.get_entrypoint("add", orig.taddr), 32 as int, 0tez); - return Assert.assert(Test.Next.Typed_address.get_storage(orig.taddr) == initial_storage + 32); -}) () \ No newline at end of file + const orig = Test.Originate.contract(contract_of(Counter), initial_storage, 0tez); + Test.Contract.transfer_exn(Test.Typed_address.get_entrypoint("add", orig.taddr), 32 as int, 0tez); + return Assert.assert(Test.Typed_address.get_storage(orig.taddr) == initial_storage + 32); +}) () diff --git a/gitlab-pages/docs/tutorials/getting-started/src/getting-started/b.mligo b/gitlab-pages/docs/tutorials/getting-started/src/getting-started/b.mligo index 7bd68df440..ce56acb190 100644 --- a/gitlab-pages/docs/tutorials/getting-started/src/getting-started/b.mligo +++ b/gitlab-pages/docs/tutorials/getting-started/src/getting-started/b.mligo @@ -2,12 +2,15 @@ module Counter = struct type storage = int type return_type = operation list * storage - [@entry] let add (n : int) (storage : storage) : return_type = [], storage + n - [@entry] let sub (n : int) (storage : storage) : return_type = [], storage - n + [@entry] + let add (n : int) (storage : storage) : return_type = [], storage + n + + [@entry] + let sub (n : int) (storage : storage) : return_type = [], storage - n end let test_add = let initial_storage = 10 in - let orig = Test.Next.Originate.contract (contract_of Counter) initial_storage 0tez in - let _ = Test.Next.Contract.transfer_exn (Test.Next.Typed_address.get_entrypoint "add" orig.taddr) 32 0tez in - Assert.assert (Test.Next.Typed_address.get_storage(orig.taddr) = initial_storage + 32) \ No newline at end of file + let orig = Test.Originate.contract (contract_of Counter) initial_storage 0tez in + let _ = Test.Contract.transfer_exn (Test.Typed_address.get_entrypoint "add" orig.taddr) 32 0tez in + Assert.assert (Test.Typed_address.get_storage(orig.taddr) = initial_storage + 32) diff --git a/gitlab-pages/docs/tutorials/inter-contract-calls/inter-contract-calls.md b/gitlab-pages/docs/tutorials/inter-contract-calls/inter-contract-calls.md index 640e4fcd0d..de0127c1cd 100644 --- a/gitlab-pages/docs/tutorials/inter-contract-calls/inter-contract-calls.md +++ b/gitlab-pages/docs/tutorials/inter-contract-calls/inter-contract-calls.md @@ -36,7 +36,6 @@ The simplest example of an internal transaction is sending Tez to a contract. No ```cameligo type parameter = address - type storage = unit [@entry] @@ -46,7 +45,8 @@ let main (destination_addr : parameter) (_ : storage) = match maybe_contract with Some contract -> contract | None -> failwith "Contract does not exist" in - let op = Tezos.transaction () (Tezos.get_amount ()) destination_contract in + let op = Tezos.Operation.transaction + () (Tezos.get_amount ()) destination_contract in [op], () ``` @@ -65,7 +65,6 @@ Let us also examine a contract that stores the address of another contract and p (* examples/contracts/mligo/Proxy.mligo *) type parameter = int - type storage = address let get_contract (addr : address) = @@ -76,7 +75,7 @@ let get_contract (addr : address) = [@entry] let main (param : parameter) (callee_addr : storage) = let callee = get_contract (callee_addr) in - let op = Tezos.transaction param 0mutez callee in + let op = Tezos.Operation.transaction param 0mutez callee in [op], callee_addr ``` @@ -141,7 +140,6 @@ To specify an entrypoint, we can use `Tezos.get_entrypoint_opt` instead of `Tezo (* contracts/examples/mligo/EntrypointProxy.mligo *) type parameter = int - type storage = address let get_add_entrypoint (addr : address) = @@ -152,7 +150,7 @@ let get_add_entrypoint (addr : address) = [@entry] let main (param : parameter) (callee_addr : storage) = let add : int contract = get_add_entrypoint (callee_addr) in - let op = Tezos.transaction param 0mutez add in + let op = Tezos.Operation.transaction param 0mutez add in [op], callee_addr ``` @@ -271,7 +269,7 @@ let call (op : unit -> operation) (s : storage) : operation list * storage = let iswhitelisted (arg : address * (bool contract)) (s : storage) : operation list * storage = let addr, callback_contract = arg in let whitelisted = Set.mem addr s.senders_whitelist in - let op = Tezos.transaction whitelisted 0mutez callback_contract in + let op = Tezos.Operation.transaction whitelisted 0mutez callback_contract in [op], s ``` @@ -326,7 +324,7 @@ For example, we can create a new counter contract with ```cameligo group=solo_create_contract -let op = Tezos.create_contract +let op = Tezos.Operation.create_contract (fun (p : int) (s : int) -> [], p + s) None 0mutez @@ -351,13 +349,13 @@ let op = Tezos.create_contract let create_and_call (storage : address list) = let create_op, addr = - Tezos.create_contract + Tezos.Operation.create_contract (fun (p : int) (s : int) -> [], p + s) None 0tez 1 in let call_op = - Tezos.transaction (addr, 41) 0tez (Tezos.self "%callback") in + Tezos.Operation.transaction (addr, 41) 0tez (Tezos.self "%callback") in [create_op; call_op], addr :: storage ``` diff --git a/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/accesscontroller.mligo b/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/accesscontroller.mligo index db18d494ce..52a7c504b6 100644 --- a/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/accesscontroller.mligo +++ b/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/accesscontroller.mligo @@ -12,5 +12,5 @@ let call (op : unit -> operation) (s : storage) : operation list * storage = let iswhitelisted (arg : address * (bool contract)) (s : storage) : operation list * storage = let addr, callback_contract = arg in let whitelisted = Set.mem addr s.senders_whitelist in - let op = Tezos.transaction whitelisted 0mutez callback_contract in + let op = Tezos.Operation.transaction whitelisted 0mutez callback_contract in [op], s \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/entrypointproxy.mligo b/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/entrypointproxy.mligo index 75232d7574..7212ec0810 100644 --- a/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/entrypointproxy.mligo +++ b/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/entrypointproxy.mligo @@ -1,7 +1,6 @@ (* contracts/examples/mligo/EntrypointProxy.mligo *) type parameter = int - type storage = address let get_add_entrypoint (addr : address) = @@ -12,5 +11,5 @@ let get_add_entrypoint (addr : address) = [@entry] let main (param : parameter) (callee_addr : storage) = let add : int contract = get_add_entrypoint (callee_addr) in - let op = Tezos.transaction param 0mutez add in + let op = Tezos.Operation.transaction param 0mutez add in [op], callee_addr \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/proxy.mligo b/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/proxy.mligo index 3dd8059794..e5e4632e33 100644 --- a/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/proxy.mligo +++ b/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/proxy.mligo @@ -1,7 +1,6 @@ (* examples/contracts/mligo/Proxy.mligo *) type parameter = int - type storage = address let get_contract (addr : address) = @@ -12,5 +11,5 @@ let get_contract (addr : address) = [@entry] let main (param : parameter) (callee_addr : storage) = let callee = get_contract (callee_addr) in - let op = Tezos.transaction param 0mutez callee in + let op = Tezos.Operation.transaction param 0mutez callee in [op], callee_addr \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/solo_create_contract.mligo b/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/solo_create_contract.mligo index e93e688680..98eb896c07 100644 --- a/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/solo_create_contract.mligo +++ b/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/solo_create_contract.mligo @@ -1,4 +1,4 @@ -let op = Tezos.create_contract +let op = Tezos.Operation.create_contract (fun (p : int) (s : int) -> [], p + s) None 0mutez diff --git a/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/ungrouped.mligo b/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/ungrouped.mligo index 0d035b1f9e..0f431bfdad 100644 --- a/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/ungrouped.mligo +++ b/gitlab-pages/docs/tutorials/inter-contract-calls/src/inter-contract-calls/ungrouped.mligo @@ -1,5 +1,4 @@ type parameter = address - type storage = unit [@entry] @@ -9,7 +8,8 @@ let main (destination_addr : parameter) (_ : storage) = match maybe_contract with Some contract -> contract | None -> failwith "Contract does not exist" in - let op = Tezos.transaction () (Tezos.get_amount ()) destination_contract in + let op = Tezos.Operation.transaction + () (Tezos.get_amount ()) destination_contract in [op], () type t = {hello : int; l : nat; i : bytes; g : string; o : address} (* examples/contracts/mligo/CreateAndCall.mligo *) @@ -20,11 +20,11 @@ type t = {hello : int; l : nat; i : bytes; g : string; o : address} let create_and_call (storage : address list) = let create_op, addr = - Tezos.create_contract + Tezos.Operation.create_contract (fun (p : int) (s : int) -> [], p + s) None 0tez 1 in let call_op = - Tezos.transaction (addr, 41) 0tez (Tezos.self "%callback") in + Tezos.Operation.transaction (addr, 41) 0tez (Tezos.self "%callback") in [create_op; call_op], addr :: storage \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/security/security.md b/gitlab-pages/docs/tutorials/security/security.md deleted file mode 100644 index 87225a8968..0000000000 --- a/gitlab-pages/docs/tutorials/security/security.md +++ /dev/null @@ -1,251 +0,0 @@ ---- -id: security -title: Smart contract security ---- - -import Syntax from '@theme/Syntax'; - -In this article, we will cover the basics of Tezos smart contract security. We will describe several potential vulnerabilities that stem from developers' misconceptions about the distributed nature of blockchains. We will also suggest ways to protect your contracts against these kinds of attacks. - -**Disclaimer:** -1. This guide is aimed at giving the reader an overview of popular attacks on smart contracts and distributed applications. It is not an exhaustive list of all the possible attack vectors. Please, use your own judgement. -2. The descriptions in this document are valid for the protocol 008_PtEdo2Zk (Edo). Since Tezos is an upgradeable blockchain, some of the blockchain mechanics may change in case a new proposal is adopted. - -## Resource constraints - -Tezos limits the resources available to the contracts. It bounds operations size so that nodes can broadcast the operations over the network in a reasonable time. It also places a limit on the computations the bakers need to perform to validate an operation – the **gas limit.** When you develop your contract, you need to bear these limits in mind. - -Let us look at a seemingly innocent wallet contract that stores an event log: - - - -```cameligo -type parameter = Fund | Send of address * tez - -type transaction = Incoming of address * tez | Outgoing of address * tez - -type storage = {owner : address; transactionLog : transaction list} - -type result = operation list * storage - -let do_send (dst, @amount : address * tez) = - let callee = Tezos.get_contract_opt dst in - match callee with - Some contract -> - let op = Tezos.transaction () @amount contract in - Outgoing (dst, @amount), [op] - | None -> (failwith "Could not send tokens" : transaction * operation list) - -let do_fund (from, @amount : address * tez) = - Incoming (from, @amount), ([] : operation list) - -[@entry] -let fund (_ : unit) (s : storage) : result = - let tx, ops = do_fund (Tezos.get_sender (), Tezos.get_amount ()) in - ops, { s with transactionLog = tx :: s.transactionLog } - -[@entry] -let send (args : address * tez) (s : storage) = - let u = assert ((Tezos.get_sender ()) = s.owner && (Tezos.get_amount ()) = 0mutez) in - let tx, ops = do_send args in - ops, { s with transactionLog = tx :: s.transactionLog } -``` - - - - -This contract: -1. Can receive funds sent to it via the `Fund` entrypoint. -2. Can send some tez via the `Send` entrypoint callable by the owner. -3. Stores a log of all the operations. - -What can go wrong? To answer this question, we will need to dive a bit into how Tezos processes transactions and what limits it places on them. - -To guarantee that the nodes spend reasonable time processing transactions, Tezos requires that the execution consumes no more than a certain amount of _gas_ (in the current protocol, it is 1 040 000 gas units). - -But in Tezos, the amount of gas consumed depends on the size of the storage! All non-lazy (i.e. non-BigMap) storage entries get fetched, deserialised, and type-checked upon each contract invocation. It means that: -1. Our contract will be more and more expensive to call with every transaction made. -2. Eventually, when the gas consumption is too high, every transaction will hit the upper bound, which will render the contract unusable. - -In this particular case the best solution would be to use an off-chain indexer that would monitor and record the transactions to the contract. If you are sure you need an event log in the contract storage, you should at least store the logs in a big map, e.g., indexed incrementally. - -Generally, you need to think about whether the side effect of gas consumption can halt the execution prematurely. Here are the tips that can help you reduce the risk of potential gas exhaustion. -1. Limit the size of non-lazy storage: - - Do not store data extendable by the users (e.g., event logs, a set of token holders) in non-lazy containers. - - If using non-lazy containers is absolutely required, place an upper bound on the size of non-lazy containers. - - Limit the maximum size of strings and byte strings. - - Do not put untrusted lambdas in storage. - - Be careful with all unbounded types, including `nat`, `int`, etc. Although exploiting gas exhaustion attacks with non-container types may be harder, it is still possible. -2. Ensure that your contract logic does not allow attackers to increase the interpretation cost, e.g., by forcing future transactions to run a huge loop. - -## Transaction ordering -It is crucial to understand that all blockchains, including Tezos, are distributed systems where block producers – bakers in Tezos – are free to include, censor, and reorder transactions within a block. For most of the practical applications, this does not pose a threat. However, in some cases, especially in Decentralised Finance (DeFi) applications, bakers can use their power to gain economic benefit from reordering or censoring out user transactions. - -Aside from bakers, other actors can indirectly influence the transaction ordering as well. Attackers can set higher fees or use accounts with lower counter values to make bakers put the attackers' transactions in front of others. - -A classic example of a system vulnerable to this kind of attacks is a decentralised exchange with an on-chain orderbook, like this one (let us assume just one asset pair for clarity): - - - -```cameligo skip -type order = {price : nat; volume : nat} - -type storage = {bids : order list; asks : order list} - -type parameter = Buy of order | Sell of order - -let buy (order, s : order * storage) = ... -let sell (order, s : order * storage) = ... -let main (p, s : parameter * storage) = ... -``` - - - - -An attacker may notice some transaction, for example, a request to buy some big volume of asset. They may then _front-run_ this transaction and, anticipating the price going up, insert a _buy_ order at the current price before the trader's transaction. Thus, they can benefit from the price change by selling the asset at a higher price. - -In fact, if the front-runner is a baker, the so-called _miner extracted value_ [poses a big risk](https://arxiv.org/pdf/1904.05234.pdf) to security of blockchains in general. You should avoid letting miners get rewards from transaction ordering. In this particular case, moving the order book off-chain would be a good option. - -## Timestamps - -Aside from transaction ordering, bakers can manipulate other variables you might want to rely on. A classic example of such a value is `Tezos.get_now`. Previously, it used to be equal to the current block timestamp. This behaviour has been changed to eliminate straightforward manipulations. Since Tezos is a distributed system, there is no way to make sure the block was produced _exactly_ at the specified time. Thus, bakers could slightly adjust the timestamp to make a transaction produce a different result. - -In the current protocol, `Tezos.get_now` is equal to the _previous_ block timestamp plus a fixed value. Although `Tezos.get_now` becomes less manipulable with this new behaviour, the only assumption you can make is that the operation goes through _roughly about_ the specified timestamp. And, of course, you should never use `Tezos.get_now` as a source of randomness. - -## Reentrancy and call injection - -Tezos features a rather unconventional model of execution: -1. The contract state is updated _after_ the computations are completed. -2. The contracts cannot emit operations in the middle of execution. -3. Internal operations are _queued._ - -The first two points resemble the Checks-Effects-Interactions pattern popular in Solidity. In Ethereum, it is considered a best practice, and Tezos enforces this on the protocol level. Such restrictions help prevent reentrancy attacks: if the state of your contract is updated _before_ someone makes a reentrant call, this call would be treated as a regular one and should do no harm. - -Consider the following snippet in Solidity: -``` -function withdraw(uint256 amount) { - uint256 balance = balances[beneficiary]; - require(balance >= amount); - uint256 new_balance = balance - amount; - beneficiary.call.value(amount)(); - balances[beneficiary] = new_balance; -} -``` - -You may notice that the _effect_ of updating the storage happens after _interaction_ – transferring the `amount` to the beneficiary. This contract has a reentrancy vulnerability: the contract execution would get paused during the transfer, and the beneficiary can call `withdraw` again _before_ their balance is updated. - -It is quite hard to repeat this attack on Tezos, where the contract storage is always updated _before_ any interactions: - - - -```cameligo -type storage = {beneficiary : address; balances : (address, tez) map} - -type parameter = tez * (unit contract) - -let withdraw (param, s : parameter * storage) = - let @amount, beneficiary = param in - let beneficiary_addr = Tezos.address beneficiary in - let @balance = - match (Map.find_opt beneficiary_addr s.balances) with - Some v -> v - | None -> 0mutez in - let new_balance = match @balance - @amount with - | Some x -> x - | None -> (failwith "Insufficient balance" : tez) - in - let op = Tezos.transaction () @amount beneficiary in - let new_balances = - Map.update beneficiary_addr (Some new_balance) s.balances in - [op], {s with balances = new_balances} -``` - - - - -Notice that the code flow is similar: we first check whether the beneficiary has enough balance, then forge an operation that sends the money, and finally we update the balances mapping. The difference is that in Tezos the operations are not executed immediately: we store the operation and later return it as a result of the entrypoint. Hence, the balances are updated by the time the operation is executed, so the reentrancy attack is mitigated. - -However, in some cases reentrancy attacks are still possible, especially if contracts are supposed to "wait" for a callback in an indeterminate state. If you, for example, choose to store balances in a separate contract, your execution flow will need a lot more interactions than sending one internal operation: - -| Current call | Treasury state after | Queued operations | -|--------------|----------------------|-------------------| -| `Treasury %withdraw` | Waiting for balances | [`Balances %getBalance`] | -| `Balances %getBalance` | Waiting for balances | [`Treasury %withdrawContinuation`] | -| `Treasury %withdrawContinuation` | Sent | [Send tez to `Beneficiary`, `Balances %setNewBalance`] | -| Send tez to `Beneficiary` | Sent | [`Balances %setNewBalance`] | -| `Balances %setNewBalance` | Sent | | - -In this example, the Treasury contract uses a callback mechanism to get the sender balance. In an intermediate state between `%withdraw` and `%withdrawContinuation`, the balances request has already been sent but the funds have not been withdrawn yet, and the balances have not been updated. This opens up a possibility for a call injection attack. - -For example, here is what happens if an attacker tries to call `%withdraw` twice within a single transaction: - -| Step | Current call | Queued operations | -|------|--------------|-------------------| -| 1 | `Evil %attack` | [`Treasury %withdraw`, `Treasury %withdraw`] | -| 2 | `Treasury %withdraw` | [`Balances %getBalance`] | -| 3 | `Treasury %withdraw` | [`Balances %getBalance`, `Balances %getBalance`] | -| 4 | `Balances %getBalance`| [`Balances %getBalance`, `Treasury %withdrawContinuation`] | -| 5 | `Balances %getBalance`| [`Treasury %withdrawContinuation`, `Treasury %withdrawContinuation`] | -| 6 | `Treasury %withdrawContinuation` | [`Treasury %withdrawContinuation`, Send tez to `Beneficiary`, `Balances %setNewBalance`] | -| 7 | `Treasury %withdrawContinuation` | [Send tez to `Beneficiary`, `Balances %setNewBalance`, Send tez to `Beneficiary`, `Balances %setNewBalance`] | -| 8 | Send tez to `Beneficiary` | [`Balances %setNewBalance`, Send tez to `Beneficiary`, `Balances %setNewBalance`] | -| 9 | `Balances %setNewBalance` | [Send tez to `Beneficiary`, `Balances %setNewBalance`] | -| 10 | Send tez to `Beneficiary` | [`Balances %setNewBalance`] | -| 11 | `Balances %setNewBalance` | | - -The attacker successfully withdraws money twice using the fact that by the time the second `%withdraw` is called, the balance has not been updated yet. - -## Transactions to untrusted contracts - -When emitting a transaction to an untrusted contract, you can not assume that it will "play by the rules". Rather, you should always bear in mind that the callee may fail, causing the entire operation to fail, or emit other operations you do not expect. - -Let us consider the following example: - - - -```cameligo -type storage = {owner : address; beneficiaries : address list} - -let send_rewards (beneficiary_addr : address) = - let maybe_contract = - Tezos.get_contract_opt beneficiary_addr in - let beneficiary = - match maybe_contract with - Some contract -> contract - | None -> (failwith "CONTRACT_NOT_FOUND" : unit contract) in - Tezos.transaction () 5000000mutez beneficiary - -let main (p, s : unit * storage) = - if (Tezos.get_sender ()) <> s.owner - then (failwith "ACCESS_DENIED" : operation list * storage) - else - let ops = List.map send_rewards s.beneficiaries in - ops, s -``` - - - - -The contract emits a bunch of operations that transfer 5 tez to each of the beneficiaries listed in storage. The flaw here is that one of the receiver contracts may fail, preventing others from receiving the reward. This may be intentional censorship or a bug in the receiver contract – in either case, the contract gets stuck. - -Instead of making a batch transfer, it is better to let beneficiaries withdraw their funds individually. This way, if the receiver contract fails, it would not affect other withdrawals. - -## Incorrect authorisation checks - -When developing a contract, you may often want to restrict access to certain entrypoint. You need to somehow ensure that: -1. The request comes from an authorised entity -2. This entity cannot be tricked into sending this request. - -You may be tempted to use `Tezos.get_source` instruction – it returns the address of an implicit account who injected the operation – but this violates our second requirement. It is easy to ask the owner of this implicit account to make a seemingly innocent transfer to a malicious contract that, in turn, emits an operation to a restricted entrypoint. The attacker contract may disguise itself as some blockchain game or a DAO, but neither the caller would be aware of its side-effects nor the callee would notice the presence of the intermediary. You should **never** use `Tezos.get_source` for authorisation purposes. - -Checking whether `Tezos.get_sender` – the address of the immediate caller – is authorised to perform an operation is better: since the request comes directly from the authorised entity, we can be more certain this call is intended. Such an approach is a decent default choice if both conditions hold true: -1. The sender contract is well secured against emitting arbitrary operations. For instance, it must not contain ["view" entrypoints](https://gitlab.com/tzip/tzip/-/blob/master/proposals/tzip-4/tzip-4.md#view-entrypoints) as defined in [TZIP-4](https://gitlab.com/tzip/tzip/-/blob/master/proposals/tzip-4/tzip-4.md). -2. You only need to authorise an immediate caller and not the contracts somewhere up in the call chain. - -If any of these conditions is not met, you need to use a more advanced technique called "tickets". Tickets are much like "contract signatures": a contract may issue a ticket that authorises a certain action. A ticket holds the data of any type, and a number – ticket _amount_. A ticket can not be copied but it can be split. If you split a ticket of amount `N`, you would get two tickets with amounts `M` and `K` such that `N = M + K`. You can also join two tickets if they have the same data and are issued by the same contract. In this case, you would get a new ticket with the sum of the amounts. - -To check whether an action is authorised, you need to see if the ticket meets the following conditions: -1. The ticket issuer has enough permissions to perform this action. -2. The ticket amount and data are correct (the definition of "correct" is application-specific, e.g., the amount may mean the number of tokens to spend or the number of _times_ the action can be executed). - -We recommend using the sender-based authorisation only in simple scenarios, e.g., when the contract has a single "owner" contract controlled by an implicit account. Otherwise, it is better to use ticket-based authorisation. diff --git a/gitlab-pages/docs/tutorials/security/src/security/ungrouped.mligo b/gitlab-pages/docs/tutorials/security/src/security/ungrouped.mligo deleted file mode 100644 index be025a6ffe..0000000000 --- a/gitlab-pages/docs/tutorials/security/src/security/ungrouped.mligo +++ /dev/null @@ -1,65 +0,0 @@ -type parameter = Fund | Send of address * tez - -type transaction = Incoming of address * tez | Outgoing of address * tez - -type storage = {owner : address; transactionLog : transaction list} - -type result = operation list * storage - -let do_send (dst, @amount : address * tez) = - let callee = Tezos.get_contract_opt dst in - match callee with - Some contract -> - let op = Tezos.transaction () @amount contract in - Outgoing (dst, @amount), [op] - | None -> (failwith "Could not send tokens" : transaction * operation list) - -let do_fund (from, @amount : address * tez) = - Incoming (from, @amount), ([] : operation list) - -[@entry] -let fund (_ : unit) (s : storage) : result = - let tx, ops = do_fund (Tezos.get_sender (), Tezos.get_amount ()) in - ops, { s with transactionLog = tx :: s.transactionLog } - -[@entry] -let send (args : address * tez) (s : storage) = - let u = assert ((Tezos.get_sender ()) = s.owner && (Tezos.get_amount ()) = 0mutez) in - let tx, ops = do_send args in - ops, { s with transactionLog = tx :: s.transactionLog } -type storage = {beneficiary : address; balances : (address, tez) map} - -type parameter = tez * (unit contract) - -let withdraw (param, s : parameter * storage) = - let @amount, beneficiary = param in - let beneficiary_addr = Tezos.address beneficiary in - let @balance = - match (Map.find_opt beneficiary_addr s.balances) with - Some v -> v - | None -> 0mutez in - let new_balance = match @balance - @amount with - | Some x -> x - | None -> (failwith "Insufficient balance" : tez) - in - let op = Tezos.transaction () @amount beneficiary in - let new_balances = - Map.update beneficiary_addr (Some new_balance) s.balances in - [op], {s with balances = new_balances} -type storage = {owner : address; beneficiaries : address list} - -let send_rewards (beneficiary_addr : address) = - let maybe_contract = - Tezos.get_contract_opt beneficiary_addr in - let beneficiary = - match maybe_contract with - Some contract -> contract - | None -> (failwith "CONTRACT_NOT_FOUND" : unit contract) in - Tezos.transaction () 5000000mutez beneficiary - -let main (p, s : unit * storage) = - if (Tezos.get_sender ()) <> s.owner - then (failwith "ACCESS_DENIED" : operation list * storage) - else - let ops = List.map send_rewards s.beneficiaries in - ops, s \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/getting-payouts.md b/gitlab-pages/docs/tutorials/taco-shop/getting-payouts.md new file mode 100644 index 0000000000..a5220ac223 --- /dev/null +++ b/gitlab-pages/docs/tutorials/taco-shop/getting-payouts.md @@ -0,0 +1,804 @@ +--- +title: "Part 3: Getting the payouts" +pagination_next: null +--- + +Now that the customer-facing entrypoint of the contract is ready, you can set up the administrator-related entrypoint. +In this case, Pedro needs a way to reset the stock of tacos and send the tez from the contract to his account. +You could do this in two entrypoints, but for simplicity this tutorial shows how to do both of these things in one entrypoint named `payout`. + +## Adding administrator information + +Also for the sake of simplicity, the contract provides no way to change Pedro's account address after the contract is deployed. +In production applications, the address of the administrator should be in the contract storage and an entrypoint should allow the current administrator to change the administrator address. +As it is, this contract cannot change the administrator address after it is deployed, so use caution. + + + +1. In the `payout` entrypoint, add this code to verify that the administrator is calling the entrypoint: + + ```jsligo skip + // Ensure that only the admin can call this entrypoint + if (Tezos.get_sender() != storage.admin_address) { + failwith("Only the admin can call this entrypoint"); + } + ``` + + The function `Tezos.get_sender` returns the address of the account that called the smart contract. + +1. Add this code to generate the operation that sends tez to the administrator account: + + ```jsligo skip + // Create contract object that represents the target account + const receiver_contract = $match(Tezos.get_contract_opt(storage.admin_address), { + "Some": (contract) => contract, + "None": () => failwith("Couldn't find account"), + }); + + // Create operation to send tez + const payout_operation = Tezos.Operation.transaction(unit, Tezos.get_balance(), receiver_contract); + ``` + + Sending tez to a user account means treating the user account as though it is a smart contract account. + This way, sending tez to a user account works in the same way as sending tez to a smart contract. + + The `Tezos.Operation.transaction` function creates a Tezos transaction. + There are many kinds of internal transactions in Tezos, but most smart contracts deal with these transactions: + + - Transferring tez to another account + - Calling an entrypoint on a smart contract + + Calling an entrypoint on a smart contract (either the current contract or another contract) is beyond the scope of this tutorial. + For information, see [Calling a contract](../../syntax/contracts/operation#calling-a-contract). + + The `Tezos.Operation.transaction` function takes these parameters: + + 1. The parameter to pass, in this case `unit`, which means no value + 1. The amount of tez to include with the transaction, in this case all of the tez the contract has, denoted by the `Tezos.get_balance` function + 1. The address of the target contract + +1. Add this code to calculate the new value of the storage, using the existing admin address and the default taco data: + + ```jsligo skip + // Restore stock of tacos + const new_storage: storage = { + admin_address: storage.admin_address, + taco_data: default_taco_data, + }; + ``` + +1. Replace the `payout` entrypoint's `return` statement with this code: + + ```jsligo skip + return [[payout_operation], new_storage]; + ``` + + Creating the transaction is not enough to run it; you must return it in the list of operations at the end of the entrypoint. + +The complete entrypoint looks like this: + +```jsligo skip +// @entry +const payout = (_u: unit, storage: storage): [ + list, + storage + ] => { + + // Ensure that only the admin can call this entrypoint + if (Tezos.get_sender() != storage.admin_address) { + failwith("Only the admin can call this entrypoint"); + } + + // Create contract object that represents the target account + const receiver_contract = $match(Tezos.get_contract_opt(storage.admin_address), { + "Some": (contract) => contract, + "None": () => failwith("Couldn't find account"), + }); + + // Create operation to send tez + const payout_operation = Tezos.Operation.transaction(unit, Tezos.get_balance(), receiver_contract); + + // Restore stock of tacos + const new_storage: storage = { + admin_address: storage.admin_address, + taco_data: default_taco_data, + }; + + return [[payout_operation], new_storage]; +} +``` + + + + + +1. In the `payout` entrypoint, add this code to verify that the administrator is calling the entrypoint: + + ```cameligo skip + (* Ensure that only the admin can call this entrypoint *) + let _ = if (Tezos.get_sender () <> storage.admin_address) then + failwith "Only the admin can call this entrypoint" in + ``` + + The function `Tezos.get_sender` returns the address of the account that called the smart contract. + +1. Add this code to generate the operation that sends tez to the administrator account: + + ```cameligo skip + (* Create contract object that represents the target account *) + let receiver_contract = match Tezos.get_contract_opt storage.admin_address with + | Some contract -> contract + | None -> failwith "Couldn't find account" in + + (* Create operation to send tez *) + let payout_operation = Tezos.Operation.transaction unit (Tezos.get_balance ()) receiver_contract in + ``` + + Sending tez to a user account means treating the user account as though it is a smart contract account. + This way, sending tez to a user account works in the same way as sending tez to a smart contract. + + The `Tezos.Operation.transaction` function creates a Tezos transaction. + There are many kinds of internal transactions in Tezos, but most smart contracts deal with these transactions: + + - Transferring tez to another account + - Calling an entrypoint on a smart contract + + Calling an entrypoint on a smart contract (either the current contract or another contract) is beyond the scope of this tutorial. + For information, see [Calling a contract](../../syntax/contracts/operation#calling-a-contract). + + The `Tezos.Operation.transaction` function takes these parameters: + + 1. The parameter to pass, in this case `unit`, which means no value + 1. The amount of tez to include with the transaction, in this case all of the tez the contract has, denoted by the `Tezos.get_balance` function + 1. The address of the target contract + +1. Add this code to calculate the new value of the storage, using the existing admin address and the default taco data: + + ```cameligo skip + (* Restore stock of tacos *) + let new_storage : storage = { + admin_address = storage.admin_address; + taco_data = default_taco_data + } in + ``` + +1. Replace the last line of the `payout` entrypoint with this code: + + ```cameligo skip + [payout_operation], new_storage + ``` + + Creating the transaction is not enough to run it; you must return it in the list of operations at the end of the entrypoint. + +The complete entrypoint looks like this: + +```cameligo skip +[@entry] +let payout (_u : unit) (storage : storage) : operation list * storage = + + (* Ensure that only the admin can call this entrypoint *) + let _ = if (Tezos.get_sender () <> storage.admin_address) then + failwith "Only the admin can call this entrypoint" in + + (* Create contract object that represents the target account *) + let receiver_contract = match Tezos.get_contract_opt storage.admin_address with + | Some contract -> contract + | None -> failwith "Couldn't find account" in + + (* Create operation to send tez *) + let payout_operation = Tezos.Operation.transaction unit (Tezos.get_balance ()) receiver_contract in + + (* Restore stock of tacos *) + let new_storage : storage = { + admin_address = storage.admin_address; + taco_data = default_taco_data + } in + + [payout_operation], new_storage +``` + + + +That's all you need to do to reset the storage and send the contract's tez to the administrator. +If you want to extend this logic, try separating the `payout` entrypoint into separate entrypoints for paying out the tez and resetting the stock of tacos. + +## Testing the new entrypoint + +Of course, after you implement the `payout` entrypoint, you should add tests for it. + + + +1. At the end of the test function, add this code to get the current balance of Pedro's account before calling the entrypoint: + + ```jsligo skip + // Test the payout entrypoint as the administrator + const admin_balance_before = Test.Address.get_balance(admin_address); + ``` + +1. Add this code to set the account that smart contract calls come from in the test scenario: + + ```jsligo skip + Test.State.set_source(admin_address); + ``` + + Now when you call the `Test.Contract.transfer` function, the transaction comes from Pedro's account. + +1. Add this code to call the `payout` entrypoint and verify that the storage was updated, as in previous tests: + + ```jsligo skip + const payout_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("payout", contract.taddr), + unit, + 0 as tez + ); + $match(payout_result, { + "Success": (_s) => (() => { + const storage = Test.Typed_address.get_storage(contract.taddr); + // Check that the stock has been reset + Assert.assert( + eq_in_map( + Map.find(1 as nat, TacoShop.default_taco_data), + storage.taco_data, + 1 as nat + )); + Assert.assert( + eq_in_map( + Map.find(2 as nat, TacoShop.default_taco_data), + storage.taco_data, + 2 as nat + )); + Test.IO.log("Successfully reset taco storage"); + })(), + "Fail": (_err) => failwith("Failed to reset taco storage"), + }); + ``` + +1. Add this code to verify that Pedro's account received the tez from the contract: + + ```jsligo skip + // Check that the admin account got a payout + const admin_balance_after = Test.Address.get_balance(admin_address); + Assert.assert(Test.Compare.lt(admin_balance_before, admin_balance_after)); + ``` + + The exact amounts differ because calling the `payout` entrypoint costs a small fee, but this code verifies that Pedro's account has more tez in it after calling the `payout` entrypoint. + +1. Add this code to generate a test account and verify that it can't call the `payout` entrypoint because it is not the administrator: + + ```jsligo skip + // Verify that the entrypoint fails if called by someone else + const other_user_account = Test.Account.address(1 as nat); + Test.State.set_source(other_user_account); + const failed_payout_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("payout", contract.taddr), + unit, + 0 as tez + ); + $match(failed_payout_result, { + "Success": (_s) => failwith("A non-admin user was able to call the payout entrypoint"), + "Fail": (_err) => Test.IO.log("Successfully prevented a non-admin user from calling the payout entrypoint"), + }); + ``` + +1. Run the test with `ligo run test taco_shop.jsligo` and verify that the test runs successfully. + +The complete contract and tests looks like this: + +```jsligo group=getting_payouts +namespace TacoShop { + export type taco_supply = { current_stock: nat, max_price: tez }; + export type taco_data = map; + export type admin_address = address; + export type storage = { + admin_address: admin_address, + taco_data: taco_data, + }; + + export const default_taco_data: taco_data = Map.literal([ + [1 as nat, { current_stock: 50 as nat, max_price: 50 as tez }], + [2 as nat, { current_stock: 20 as nat, max_price: 75 as tez }] + ]); + + // Internal function to get the price of a taco + const get_taco_price_internal = (taco_kind_index: nat, taco_data: taco_data): tez => { + const taco_kind: taco_supply = + $match (Map.find_opt(taco_kind_index, taco_data), { + "Some": (kind) => kind, + "None": () => failwith("Unknown kind of taco"), + }); + return taco_kind.max_price / taco_kind.current_stock; + } + + // @view + const get_taco_price = (taco_kind_index: nat, storage: storage): tez => + get_taco_price_internal(taco_kind_index, storage.taco_data); + + // Buy a taco + // @entry + const buy_taco = (taco_kind_index: nat, storage: storage): [ + list, + storage + ] => { + + const { admin_address, taco_data } = storage; + + // Retrieve the kind of taco from the contracts storage or fail + const taco_kind: taco_supply = + $match (Map.find_opt(taco_kind_index, taco_data), { + "Some": (kind) => kind, + "None": () => failwith("Unknown kind of taco"), + }); + + // Get the current price of this type of taco + const current_purchase_price = get_taco_price_internal(taco_kind_index, taco_data); + + // Verify that the caller sent the correct amount of tez + if ((Tezos.get_amount()) != current_purchase_price) { + return failwith("Sorry, the taco you are trying to purchase has a different price"); + } + + // Verify that there is at least one of this type of taco + if (taco_kind.current_stock == (0 as nat)) { + return failwith("Sorry, we are out of this type of taco"); + } + + // Update the storage with the new quantity of tacos + const updated_taco_data: taco_data = Map.update( + taco_kind_index, + ["Some" as "Some", {...taco_kind, current_stock: abs(taco_kind.current_stock - 1) }], + taco_data); + + const updated_storage: storage = { + admin_address: admin_address, + taco_data: updated_taco_data, + }; + + return [[], updated_storage]; + } + + // @entry + const payout = (_u: unit, storage: storage): [ + list, + storage + ] => { + + // Ensure that only the admin can call this entrypoint + if (Tezos.get_sender() != storage.admin_address) { + failwith("Only the admin can call this entrypoint"); + } + + // Create contract object that represents the target account + const receiver_contract = $match(Tezos.get_contract_opt(storage.admin_address), { + "Some": (contract) => contract, + "None": () => failwith("Couldn't find account"), + }); + + // Create operation to send tez + const payout_operation = Tezos.Operation.transaction(unit, Tezos.get_balance(), receiver_contract); + + // Restore stock of tacos + const new_storage: storage = { + admin_address: storage.admin_address, + taco_data: default_taco_data, + }; + + return [[payout_operation], new_storage]; + } +}; + +// Convenience function to get current taco price +const get_taco_price = (untyped_address: address, taco_kind_index: nat): tez => { + const view_result_option: option = Tezos.View.call("get_taco_price", taco_kind_index, untyped_address); + return $match(view_result_option, { + "Some": (cost_mutez) => cost_mutez, + "None": () => Test.Assert.failwith("Couldn't get the price of the taco."), + }); +} + +// Convenience function for testing equality in maps +const eq_in_map = (r: TacoShop.taco_supply, m: TacoShop.taco_data, k: nat) => + $match(Map.find_opt(k, m), { + "None": () => false, + "Some": (v) => v.current_stock == r.current_stock && v.max_price == r.max_price + }); + +const test = (() => { + + // Set the initial storage and deploy the contract + const admin_address: address = Test.Account.address(0 as nat); + const initial_storage: TacoShop.storage = { + admin_address: admin_address, + taco_data: TacoShop.default_taco_data, + } + const contract = Test.Originate.contract(contract_of(TacoShop), initial_storage, 0 as tez); + + // Get the current price of a taco + const untyped_address = Test.Typed_address.to_address(contract.taddr); + const current_price = get_taco_price(untyped_address, 1 as nat); + + // Purchase a taco + const success_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("buy_taco", contract.taddr), + 1 as nat, + current_price + ); + + // Verify that the stock was updated + $match(success_result, { + "Success": (_s) => (() => { + const storage = Test.Typed_address.get_storage(contract.taddr); + // Check that the stock has been updated correctly + Assert.assert( + eq_in_map( + { current_stock: 49 as nat, max_price: 50000000 as mutez }, + storage.taco_data, + 1 as nat + )); + // Check that the amount of the other taco type has not changed + Assert.assert(eq_in_map( + { current_stock: 20 as nat, max_price: 75000000 as mutez }, + storage.taco_data, + 2 as nat + ) + ); + Test.IO.log("Successfully bought a taco"); + })(), + "Fail": (err) => failwith(err), + }); + + // Fail to purchase a taco without sending enough tez + const fail_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("buy_taco", contract.taddr), + 1 as nat, + 1 as mutez + ); + $match(fail_result, { + "Success": (_s) => failwith("Test was able to buy a taco for the wrong price"), + "Fail": (_err) => Test.IO.log("Contract successfully blocked purchase with incorrect price"), + }); + + // Test the payout entrypoint as the administrator + const admin_balance_before = Test.Address.get_balance(admin_address); + Test.State.set_source(admin_address); + const payout_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("payout", contract.taddr), + unit, + 0 as tez + ); + $match(payout_result, { + "Success": (_s) => (() => { + const storage = Test.Typed_address.get_storage(contract.taddr); + // Check that the stock has been reset + Assert.assert( + eq_in_map( + Map.find(1 as nat, TacoShop.default_taco_data), + storage.taco_data, + 1 as nat + )); + Assert.assert( + eq_in_map( + Map.find(2 as nat, TacoShop.default_taco_data), + storage.taco_data, + 2 as nat + )); + Test.IO.log("Successfully reset taco storage"); + })(), + "Fail": (_err) => failwith("Failed to reset taco storage"), + }); + + // Check that the admin account got a payout + const admin_balance_after = Test.Address.get_balance(admin_address); + Assert.assert(Test.Compare.lt(admin_balance_before, admin_balance_after)); + + // Verify that the entrypoint fails if called by someone else + const other_user_account = Test.Account.address(1 as nat); + Test.State.set_source(other_user_account); + const failed_payout_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("payout", contract.taddr), + unit, + 0 as tez + ); + $match(failed_payout_result, { + "Success": (_s) => failwith("A non-admin user was able to call the payout entrypoint"), + "Fail": (_err) => Test.IO.log("Successfully prevented a non-admin user from calling the payout entrypoint"), + }); + +}) (); +``` + + + + + +1. At the end of the test function, replace the last block with this code so the function can continue: + + ```cameligo skip + let () = match fail_result with + | Success _s -> failwith "Test was able to buy a taco for the wrong price" + | Fail _err -> Test.IO.log "Contract successfully blocked purchase with incorrect price" in + ``` + +1. Add this code to get the current balance of Pedro's account before calling the entrypoint: + + ```cameligo skip + (* Test the payout entrypoint as the administrator *) + let admin_balance_before = Test.Address.get_balance admin_address in + ``` + +1. Add this code to set the account that smart contract calls come from in the test scenario: + + ```cameligo skip + let () = Test.State.set_source admin_address in + ``` + + Now when you call the `Test.Contract.transfer` function, the transaction comes from Pedro's account. + +1. Add this code to call the `payout` entrypoint and verify that the storage was updated, as in previous tests: + + ```cameligo skip + let payout_result = Test.Contract.transfer + (Test.Typed_address.get_entrypoint "payout" contract.taddr) + unit + 0tez + in + let () = match payout_result with + | Success _s -> let storage = Test.Typed_address.get_storage contract.taddr in + let () = Assert.assert + (eq_in_map (Map.find 1n TacoShop.default_taco_data) + storage.taco_data + 1n) in + let () = Assert.assert + (eq_in_map (Map.find 2n TacoShop.default_taco_data) + storage.taco_data + 2n) in + Test.IO.log "Successfully reset taco storage" + | Fail _err -> failwith "Failed to reset taco storage" in + ``` + +1. Add this code to verify that Pedro's account received the tez from the contract: + + ```cameligo skip + (* Check that the admin account got a payout *) + let admin_balance_after = Test.Address.get_balance admin_address in + let () = Assert.assert (Test.Compare.lt admin_balance_before admin_balance_after) in + ``` + + The exact amounts differ because calling the `payout` entrypoint costs a small fee, but this code verifies that Pedro's account has more tez in it after calling the `payout` entrypoint. + +1. Add this code to generate a test account and verify that it can't call the `payout` entrypoint because it is not the administrator: + + ```cameligo skip + (* Verify that the entrypoint fails if called by someone else *) + let other_user_account = Test.Account.address 1n in + let _ = Test.State.set_source other_user_account in + let failed_payout_result = Test.Contract.transfer + (Test.Typed_address.get_entrypoint "payout" contract.taddr) + unit + 0tez + in + match failed_payout_result with + | Success _s -> failwith "A non-admin user was able to call the payout entrypoint" + | Fail _err -> Test.IO.log "Successfully prevented a non-admin user from calling the payout entrypoint" + ``` + +1. Run the test with `ligo run test taco_shop.mligo` and verify that the test runs successfully. + +The completed contract file with the convenience functions and test functions looks like this: + +```cameligo group=getting_payouts +module TacoShop = struct + type taco_supply = { current_stock: nat; max_price: tez } + type taco_data = (nat, taco_supply) map + type admin_address = address + type storage = { + admin_address: admin_address; + taco_data: taco_data; + } + + let default_taco_data: taco_data = Map.literal [ + (1n, { current_stock = 50n; max_price = 50tez }); + (2n, { current_stock = 20n; max_price = 75tez }); + ] + + (* Internal function to get the price of a taco *) + let get_taco_price_internal (taco_kind_index : nat) (taco_data : taco_data) : tez = + let taco_kind : taco_supply = + match Map.find_opt taco_kind_index taco_data with + | Some kind -> kind + | None -> failwith "Unknown kind of taco" + in + taco_kind.max_price / taco_kind.current_stock + + [@view] + let get_taco_price (taco_kind_index : nat) (storage : storage) : tez = + get_taco_price_internal taco_kind_index storage.taco_data + + (* Buy a taco *) + [@entry] + let buy_taco (taco_kind_index : nat) (storage : storage) : operation list * storage = + + let { admin_address; taco_data } = storage in + + (* Retrieve the kind of taco from the contracts storage or fail *) + let taco_kind : taco_supply = + match Map.find_opt taco_kind_index taco_data with + | Some kind -> kind + | None -> failwith "Unknown kind of taco" in + + (* Get the current price of this type of taco *) + let current_purchase_price = get_taco_price_internal taco_kind_index taco_data in + + (* Verify that the caller sent the correct amount of tez *) + let _ = if (Tezos.get_amount () <> current_purchase_price) then + failwith "Sorry, the taco you are trying to purchase has a different price" in + + (* Verify that there is at least one of this type of taco *) + let _ = if (taco_kind.current_stock = 0n) then + failwith "Sorry, we are out of this type of taco" in + + (* Update the storage with the new quantity of tacos *) + let updated_taco_data : taco_data = Map.update + taco_kind_index + (Some { taco_kind with current_stock = abs (taco_kind.current_stock - 1n) }) + taco_data in + + let updated_storage : storage = { + admin_address = admin_address; + taco_data = updated_taco_data; + } in + + [], updated_storage + + [@entry] + let payout (_u : unit) (storage : storage) : operation list * storage = + + (* Ensure that only the admin can call this entrypoint *) + let _ = if (Tezos.get_sender () <> storage.admin_address) then + failwith "Only the admin can call this entrypoint" in + + (* Create contract object that represents the target account *) + let receiver_contract = match Tezos.get_contract_opt storage.admin_address with + | Some contract -> contract + | None -> failwith "Couldn't find account" in + + (* Create operation to send tez *) + let payout_operation = Tezos.Operation.transaction unit (Tezos.get_balance ()) receiver_contract in + + (* Restore stock of tacos *) + let new_storage : storage = { + admin_address = storage.admin_address; + taco_data = default_taco_data + } in + + [payout_operation], new_storage + +end + +(* Convenience function to get current taco price *) +let get_taco_price (untyped_address : address) (taco_kind_index : nat) : tez = + let view_result_option : tez option = Tezos.View.call + "get_taco_price" + taco_kind_index + untyped_address in + match view_result_option with + | Some cost_mutez -> cost_mutez + | None -> Test.Assert.failwith "Couldn't get the price of a taco" + +(* Convenience function for testing equality in maps *) +let eq_in_map (r : TacoShop.taco_supply) (m : TacoShop.taco_data) (k : nat) = + match Map.find_opt k m with + | None -> false + | Some v -> v.current_stock = r.current_stock && v.max_price = r.max_price + +let test = + + (* Set the initial storage and deploy the contract *) + let admin_address : address = Test.Account.address 0n in + let initial_storage : TacoShop.storage = { + admin_address = admin_address; + taco_data = TacoShop.default_taco_data + } in + let contract = Test.Originate.contract (contract_of TacoShop) initial_storage 0tez in + + (* Get the current price of a taco *) + let untyped_address = Test.Typed_address.to_address contract.taddr in + let current_price = get_taco_price untyped_address 1n in + + (* Purchase a taco *) + let success_result = + Test.Contract.transfer + (Test.Typed_address.get_entrypoint "buy_taco" contract.taddr) + 1n + current_price + in + + (* Verify that the stock was updated *) + let () = match success_result with + | Success _s -> + let storage = Test.Typed_address.get_storage contract.taddr in + let () = Assert.assert (eq_in_map + { current_stock = 49n; max_price = 50000000mutez } + storage.taco_data + 1n + ) in + let () = Assert.assert (eq_in_map + { current_stock = 20n; max_price = 75000000mutez } + storage.taco_data + 2n + ) in + Test.IO.log "Successfully bought a taco" + | Fail err -> failwith err + in + + (* Fail to purchase a taco without sending enough tez *) + let fail_result = Test.Contract.transfer + (Test.Typed_address.get_entrypoint "buy_taco" contract.taddr) + 1n + 1mutez in + let () = match fail_result with + | Success _s -> failwith "Test was able to buy a taco for the wrong price" + | Fail _err -> Test.IO.log "Contract successfully blocked purchase with incorrect price" in + + (* Test the payout entrypoint as the administrator *) + let admin_balance_before = Test.Address.get_balance admin_address in + + let () = Test.State.set_source admin_address in + + let payout_result = Test.Contract.transfer + (Test.Typed_address.get_entrypoint "payout" contract.taddr) + unit + 0tez + in + let () = match payout_result with + | Success _s -> let storage = Test.Typed_address.get_storage contract.taddr in + let () = Assert.assert + (eq_in_map (Map.find 1n TacoShop.default_taco_data) + storage.taco_data + 1n) in + let () = Assert.assert + (eq_in_map (Map.find 2n TacoShop.default_taco_data) + storage.taco_data + 2n) in + Test.IO.log "Successfully reset taco storage" + | Fail _err -> failwith "Failed to reset taco storage" in + + (* Check that the admin account got a payout *) + let admin_balance_after = Test.Address.get_balance admin_address in + let () = Assert.assert (Test.Compare.lt admin_balance_before admin_balance_after) in + + (* Verify that the entrypoint fails if called by someone else *) + let other_user_account = Test.Account.address 1n in + let _ = Test.State.set_source other_user_account in + let failed_payout_result = Test.Contract.transfer + (Test.Typed_address.get_entrypoint "payout" contract.taddr) + unit + 0tez + in + match failed_payout_result with + | Success _s -> failwith "A non-admin user was able to call the payout entrypoint" + | Fail _err -> Test.IO.log "Successfully prevented a non-admin user from calling the payout entrypoint" +``` + + + +Now you can allow different users to do different things in the contract. + +## Conclusion + +Now you have a contract that Pedro can use to sell tacos and manage the profits and the taco stock. +From here you can expand the contract in many ways, such as: + +- Adding more types of tacos +- Changing how the price of tacos is calculated +- Expanding the administrator functionality +- Accepting more than the price of the taco as a tip +- Adding more tests + +You can also try deploying the contract to a test network and trying it in a real Tezos environment. +For a tutorial that covers deploying a contract, see [Deploy a smart contract](https://docs.tezos.com/tutorials/smart-contract) on docs.tezos.com. diff --git a/gitlab-pages/docs/tutorials/taco-shop/selling-tacos.md b/gitlab-pages/docs/tutorials/taco-shop/selling-tacos.md new file mode 100644 index 0000000000..5687b8b8d7 --- /dev/null +++ b/gitlab-pages/docs/tutorials/taco-shop/selling-tacos.md @@ -0,0 +1,770 @@ +--- +title: "Part 1: Creating a contract" +pagination_prev: null +--- + +import Syntax from '@theme/Syntax'; + +
    + +## Learning objectives + +In this tutorial, you will learn how to: + +- Set up a smart contract in JsLIGO or CameLIGO +- Define the storage for the contract +- Define what requests the contract can accept and how it behaves +- Implement the code that handles these requests +- Write tests that ensure that the contract behaves correctly + +## Prerequisites + +Before you begin, install LIGO as described in [Installation](../../intro/installation). + +Optionally, you can also set up your editor to work with LIGO as described in [Editor Support](../../intro/editor-support). + +## Syntaxes + +LIGO has two syntaxes: + +- JsLIGO is inspired by TypeScript/JavaScript, intended for web developers + +- CameLIGO is inspired by OCaml, intended for functional programmers + +The syntaxes do the same thing and have nearly all the same features, so which one you choose depends on your preference or programming background. +You can use either syntax for this tutorial, but you must use the same syntax for the entire contract. +Use the **Syntax Preference** slider at the top left of this page to select the syntax to use. + +## Pricing + +Pedro sells two kinds of tacos: **el Clásico** and the **Especial del Chef**. +His tacos are a rare delicacy and he has a finite amount of each kind, so the price goes up as the stock for the day depletes. +Taco prices are in tez, the currency of the Tezos blockchain. + +The cost for one taco is the maximum price for the taco divided by the total number of tacos, as in this formula: + +``` +current_purchase_price = max_price / available_stock +``` + +For example, the maximum price for an el Clásico taco is 50 tez. +This table shows the price when there are certain amounts of tacos left: + +| Number of tacos available | Maximum price | Purchase price | +|---|---|---| +| 50 | 50 tez | 1 tez | +| 20 | 50 tez | 2.5 tez | +| 5 | 50 tez | 10 tez | +| 1 | 50 tez | 50 tez | + +The maximum price for an Especial del Chef taco is 75 tez, so the prices are different, as in this table: + +| Number of tacos available | Maximum price | Purchase price | +|---|---|---| +| 20 | 75 tez | 3.75 tez | +| 10 | 75 tez | 7.5 tez| +| 5 | 75 tez | 15 tez | +| 1 | 75 tez | 75 tez | + +## Setting up the data storage + +Smart contracts can store persistent data. +Only the contract itself can write to its data, but the data is visible to outside users. +This data can be in many data types, including simple data types like numbers, Boolean values, and strings, and complex data types like arrays and maps. + +Because the cost of a taco is determined by a formula, the contract needs to store only two pieces of data for each type of taco: the maximum price and the number of tacos currently in stock. +LIGO contracts store this type of data in a data type called a *map*, which is a key-value store where each key is the same data type and each value is the same data type. +Maps are flexible, so you can add and remove elements. + +The key for this map is a natural number (also known as a *nat*, an integer zero or greater) and the value is a [Record](../../data-types/records) data type that has two fields: a natural number for the current stock of tacos and a tez amount for the maximum price. +In table format, the map data looks ike this: + +Key | Value +--- | --- +1 | `{ current_stock: 50, maximum_price: 50tez }` +2 | `{ current_stock: 20, maximum_price: 75tez }` + +Follow these steps to set up the data storage for your contract: + + + +1. Anywhere on your computer, create a folder to store your work for this tutorial with a name such as `TacoShopTutorial`. + +1. In the folder, create a file named `taco_shop.jsligo` to store the code of the smart contract. +You can create and edit this file in any text editor. + +1. In the file, create a type named `taco_supply` that represents the value of the map, consisting of a nat for the number of tacos and a tez value for the maximum price: + + ```jsligo skip + export type taco_supply = { current_stock: nat, max_price: tez }; + ``` + +1. Create a map type named `taco_data`, with the key a nat and the value the `taco_supply` type: + + ```jsligo skip + export type taco_data = map; + ``` + + This map can contain the supply and max price for any number of tacos, indexed by a natural number key. + +1. Create an address type to store Pedro's account address, which allows him to lock some features of the contract behind an administrator account: + + ```jsligo skip + export type admin_address = address; + ``` + +1. Create a type to represent the storage for the contract. +In this case, the contract needs to store the taco data map and the administrator address, so the overall contract storage contains those two values: + + ```jsligo skip + export type storage = { + admin_address: admin_address, + taco_data: taco_data, + }; + ``` + +1. Create a constant to represent the starting values for the taco data map: + + ```jsligo skip + export const default_taco_data: taco_data = Map.literal([ + [1 as nat, { current_stock: 50 as nat, max_price: 50 as tez }], + [2 as nat, { current_stock: 20 as nat, max_price: 75 as tez }] + ]); + ``` + + Note that the natural numbers are indicated with an `as nat` after the number; otherwise, LIGO assumes that numbers are integers. + Similarly, the maximum prices of the tacos have `as tez` to indicate that they are amounts of tez. + +1. To keep the code for the contract organized, put the types and values in a namespace named `TacoShop`. +The contract looks like this so far: + + ```jsligo skip + namespace TacoShop { + export type taco_supply = { current_stock: nat, max_price: tez }; + export type taco_data = map; + export type admin_address = address; + export type storage = { + admin_address: admin_address, + taco_data: taco_data, + }; + + export const default_taco_data: taco_data = Map.literal([ + [1 as nat, { current_stock: 50 as nat, max_price: 50 as tez }], + [2 as nat, { current_stock: 20 as nat, max_price: 75 as tez }] + ]); + + }; + ``` + + + + + +1. Anywhere on your computer, create a folder to store your work for this tutorial with a name such as `TacoShopTutorial`. + +1. In the folder, create a file named `taco_shop.mligo` to store the code of the smart contract. +You can create and edit this file in any text editor. + +1. In the file, create a type named `taco_supply` that represents the value of the map, consisting of a nat for the number of tacos and a tez value for the maximum price: + + ```cameligo skip + type taco_supply = { current_stock: nat; max_price: tez } + ``` + +1. Create a map type named `taco_data`, with the key a nat and the value the `taco_supply` type: + + ```cameligo skip + type taco_data = (nat, taco_supply) map + ``` + + This map can contain the supply and max price for any number of tacos, indexed by a natural number key. + +1. Create an address type to store Pedro's account address, which allows him to lock some features of the contract behind an administrator account: + + ```cameligo skip + type admin_address = address + ``` + +1. Create a type to represent the storage for the contract. +In this case, the contract needs to store the taco data map and the administrator address, so the overall contract storage contains those two values: + + ```cameligo skip + type storage = { + admin_address: admin_address; + taco_data: taco_data; + } + ``` + +1. Create a variable to represent the starting values for the taco data map: + + ```cameligo skip + let default_taco_data: taco_data = Map.literal [ + (1n, { current_stock = 50n; max_price = 50tez }); + (2n, { current_stock = 20n; max_price = 75tez }); + ] + ``` + + Note that the natural numbers are indicated with an `n` after the number; otherwise, LIGO assumes that numbers are integers. + Similarly, the maximum prices of the tacos are suffixed with `tez` to indicate that they are amounts of tez. + +1. To keep the code for the contract organized, put the types and values in a module named `TacoShop`. +The contract looks like this so far: + + ```cameligo skip + module TacoShop = struct + + type taco_supply = { current_stock: nat; max_price: tez } + type taco_data = (nat, taco_supply) map + type admin_address = address + type storage = { + admin_address: admin_address; + taco_data: taco_data; + } + + let default_taco_data: taco_data = Map.literal [ + (1n, { current_stock = 50n; max_price = 50tez }); + (2n, { current_stock = 20n; max_price = 75tez }); + ] + + end + ``` + + + +## Getting the price of tacos + +Because the price of tacos changes, it'll be helpful to have a function to get the current price of a certain kind of taco. + + + +Add this function inside the namespace, immediately after the `default_taco_data` constant: + +```jsligo skip +// Internal function to get the price of a taco +const get_taco_price_internal = (taco_kind_index: nat, taco_data: taco_data): tez => { + const taco_kind: taco_supply = + $match (Map.find_opt(taco_kind_index, taco_data), { + "Some": (kind) => kind, + "None": () => failwith("Unknown kind of taco"), + }); + return taco_kind.max_price / taco_kind.current_stock; +} +``` + + + + + +Add this function inside the module, immediately after the `default_taco_data` variable: + +```cameligo skip +(* Internal function to get the price of a taco *) +let get_taco_price_internal (taco_kind_index : nat) (taco_data : taco_data) : tez = + let taco_kind : taco_supply = + match Map.find_opt taco_kind_index taco_data with + | Some kind -> kind + | None -> failwith "Unknown kind of taco" + in + taco_kind.max_price / taco_kind.current_stock +``` + + + +This code uses the `Map.find_opt` function to get an entry from a map based on a key. +It returns an [option](../../data-types/variants#options) value, which is a data type that LIGO uses to handle cases where a value may not exist. +In this case, the option has the value for that key if the key exists or a `None` value if the key does not exist. +If the `taco_kind_index` parameter is not a valid taco ID, the transaction fails. + +This is an internal function, so external callers can't call it directly. +Later, you will add a way for external callers to get the current price of a taco. + +## Selling tacos + +Contracts have one or more _entrypoints_, which are a kind of function that clients can call, like endpoints in an API or functions or methods in many other programming languages. +A contract can have any number of internal functions, but only the functions designated as entrypoints can be called by outside consumers and other contracts. + +The contract you create in this tutorial has two entrypoints: + +- An entrypoint named `buy_taco` which accepts the type of taco to buy and the price of the taco and deducts that type of taco from the current stock in storage +- An entrypoint named `payout` that sends the tez in the contract to Pedro and restocks the supply of tacos + +As described in [Entrypoints](../../syntax/contracts/entrypoints), entrypoints must follow a specific signature to be compiled as entrypoints: + + + +- Entrypoints are functions marked with the `@entry` decorator, which (when used in a namespace) must be in a comment immediately before the function +- Entrypoints receive a parameter from the caller and the current state of the contract storage +- Entrypoints return a tuple consisting of a list of operations to run (such as calls to other smart contracts or transfers of tez) and the new state of the contract storage + +1. In the smart contract file, within the `TacoShop` namespace, add this stub of an entrypoint: + + ```jsligo skip + // Buy a taco + // @entry + const buy_taco = (taco_kind_index: nat, storage: storage): [ + list, + storage + ] => { + + // Entrypoint logic goes here + + return [[], updated_storage]; + } + ``` + + Your IDE may show an error that the `updated_storage` value is not defined, but you can ignore this error for now because you will define it in the next few steps. + + To call this entrypoint, the caller passes a nat to indicate the type of taco. + The function automatically receives the current state of the storage as the last parameter. + The line `return [[], updated_storage];` returns an empty list of operations to run and the new state of the storage. + In the next few steps, you add logic to verify that the caller sent the correct price and to deduct the taco from the current stock. + +1. Within the entrypoint, add code to get the admin address and the taco data by destructuring the storage parameter: + + ```jsligo skip + const { admin_address, taco_data } = storage; + ``` + +1. After this code, add code to get the type of taco that the caller requested based on the `taco_kind_index` parameter: + + ```jsligo skip + // Retrieve the kind of taco from the contracts storage or fail + const taco_kind: taco_supply = + $match (Map.find_opt(taco_kind_index, taco_data), { + "Some": (kind) => kind, + "None": () => failwith("Unknown kind of taco"), + }); + ``` + +1. After the code you just added, add this code to get the current price of a taco: + + ```jsligo skip + // Get the current price of this type of taco + const current_purchase_price = get_taco_price_internal(taco_kind_index, taco_data); + ``` + +1. Add this code to verify that the caller sent the correct amount of tez with the transaction. +It uses the `Tezos.get_amount()` function, which returns the amount of tez that the caller sent: + + ```jsligo skip + // Verify that the caller sent the correct amount of tez + if ((Tezos.get_amount()) != current_purchase_price) { + return failwith("Sorry, the taco you are trying to purchase has a different price"); + } + ``` + +1. Add this code to verify that there is at least one taco in stock: + + ```jsligo skip + // Verify that there is at least one of this type of taco + if (taco_kind.current_stock == 0 as nat) { + return failwith("Sorry, we are out of this type of taco"); + } + ``` + +1. Add this code to calculate the updated taco data map and put it in the `updated_taco_data` constant: + + ```jsligo skip + // Update the storage with the new quantity of tacos + const updated_taco_data: taco_data = Map.update( + taco_kind_index, + ["Some" as "Some", {...taco_kind, current_stock: abs(taco_kind.current_stock - 1) }], + taco_data); + ``` + + This code uses the `Map.update` function to create a new version of the map with an updated record. + In this case, the new map updates the stock of the specified type of taco to be one less. + It uses the `abs` function to ensure that the new stock of tacos is a nat, because subtraction yields an integer. + +1. Create the new value of the contract storage, including the admin address and the updated taco data: + + ```jsligo skip + const updated_storage: storage = { + admin_address: admin_address, + taco_data: updated_taco_data, + }; + ``` + + The next line is the line `return [[], updated_taco_data];`, which you added when you stubbed in the entrypoint code earlier. + + Now the `buy_taco` entrypoint updates the stock in storage to indicate that it has one less of that type of taco. + The contract automatically accepts the tez that is included with the transaction. + +1. After the code for the `buy_taco` entrypoint, stub in the code for the entrypoint that allows Pedro to retrieve the tez in the contract, which you will add in a later section: + + ```jsligo skip + // @entry + const payout = (_u: unit, storage: storage): [ + list, + storage + ] => { + + // Entrypoint logic goes here + + return [[], storage]; + } + ``` + + Currently this entrypoint does nothing, but you will add code for it later. + + + + + +- Entrypoints are functions marked with the `@entry` attribute +- Entrypoints receive a parameter from the caller and the current state of the contract storage +- Entrypoints return a tuple consisting of a list of operations to run (such as calls to other smart contracts or transfers of tez) and the new state of the contract storage + +1. In the smart contract file, within the `TacoShop` module, add this stub of an entrypoint: + + ```cameligo skip + (* Buy a taco *) + [@entry] + let buy_taco (taco_kind_index : nat) (storage : storage) : operation list * storage = + + (* Entrypoint logic goes here *) + + [], updated_storage + ``` + + Your IDE may show an error that the `updated_storage` value is not defined, but you can ignore this error for now because you will define it in the next few steps. + + To call this entrypoint, the caller passes a nat to indicate the type of taco. + The function automatically receives the current state of the storage as the last parameter. + The line `[], updated_storage` returns an empty list of operations to run and the new state of the storage. + In the next few steps, you add logic to verify that the caller sent the correct price and to deduct the taco from the current stock. + +1. Within the entrypoint, add code to get the admin address and the taco data by destructuring the storage parameter: + + ```cameligo skip + let { admin_address; taco_data } = storage in + ``` + +1. After this code, add code to get the type of taco that the caller requested based on the `taco_kind_index` parameter: + + ```cameligo skip + (* Retrieve the kind of taco from the contracts storage or fail *) + let taco_kind : taco_supply = + match Map.find_opt taco_kind_index taco_data with + | Some kind -> kind + | None -> failwith "Unknown kind of taco" in + ``` + +1. After the code you just added, add this code to get the current price of a taco: + + ```cameligo skip + (* Get the current price of this type of taco *) + let current_purchase_price = get_taco_price_internal taco_kind_index taco_data in + ``` + +1. Add this code to verify that the caller sent the correct amount of tez with the transaction. +It uses the `Tezos.get_amount()` function, which returns the amount of tez that the caller sent: + + ```cameligo skip + (* Verify that the caller sent the correct amount of tez *) + let _ = if (Tezos.get_amount () <> current_purchase_price) then + failwith "Sorry, the taco you are trying to purchase has a different price" in + ``` + +1. Add this code to verify that there is at least one taco in stock: + + ```cameligo skip + (* Verify that there is at least one of this type of taco *) + let _ = if (taco_kind.current_stock = 0n) then + failwith "Sorry, we are out of this type of taco" in + ``` + +1. Add this code to calculate the updated taco data map and put it in the `updated_taco_data` variable: + + ```cameligo skip + (* Update the storage with the new quantity of tacos *) + let updated_taco_data : taco_data = Map.update + taco_kind_index + (Some { taco_kind with current_stock = abs (taco_kind.current_stock - 1n) }) + taco_data in + ``` + + This code uses the `Map.update` function to create a new version of the map with an updated record. + In this case, the new map updates the stock of the specified type of taco to be one less. + It uses the `abs` function to ensure that the new stock of tacos is a nat, because subtraction yields an integer. + +1. Create the new value of the contract storage, including the admin address and the updated taco data: + + ```cameligo skip + let updated_storage : storage = { + admin_address = admin_address; + taco_data = updated_taco_data; + } in + ``` + + The next line is the line `[], updated_taco_data`, which you added when you stubbed in the entrypoint code earlier. + + Now the `buy_taco` entrypoint updates the stock in storage to indicate that it has one less of that type of taco. + The contract automatically accepts the tez that is included with the transaction. + +1. After the code for the `buy_taco` entrypoint, stub in the code for the entrypoint that allows Pedro to retrieve the tez in the contract, which you will add in a later section: + + ```cameligo skip + [@entry] + let payout (_u : unit) (storage : storage) : operation list * storage = + + (* Entrypoint logic goes here *) + + [], storage + ``` + + Currently this entrypoint does nothing, but you will add code for it later. + + + +## Providing information to clients + +Earlier, you added an internal function that calculated the price of a taco. +External clients can't call this function because it is private to the contract. + +The contract should give Pedro's customers a way to get the current price of a taco. +However, because entrypoints don't return a value directly to the caller, an entrypoint isn't the best way to provide information to clients. + +If you need to provide information to clients, one way is to use a _view_, which is a static function that returns a value to clients but does not change the storage or generate any operations. +Like entrypoints, views are functions that receive one or more parameters from the caller and the current value of the storage. +Unlike entrypoints, they return a single value to the caller instead of a list of operations and the new value of the storage. + + + +Add this view to the contract, after the `get_taco_price_internal` function and somewhere within the namespace: + +```jsligo skip +// @view +const get_taco_price = (taco_kind_index: nat, storage: storage): tez => + get_taco_price_internal(taco_kind_index, storage.taco_data); +``` + +This view is merely a wrapper around the `get_taco_price_internal` function, but the `@view` decorator makes external clients able to call it. + +For more information about views, see [Views](../../syntax/contracts/views). + +The complete contract file looks like this: + +```jsligo group=selling_tacos +namespace TacoShop { + export type taco_supply = { current_stock: nat, max_price: tez }; + export type taco_data = map; + export type admin_address = address; + export type storage = { + admin_address: admin_address, + taco_data: taco_data, + }; + + export const default_taco_data: taco_data = Map.literal([ + [1 as nat, { current_stock: 50 as nat, max_price: 50 as tez }], + [2 as nat, { current_stock: 20 as nat, max_price: 75 as tez }] + ]); + + // Internal function to get the price of a taco + const get_taco_price_internal = (taco_kind_index: nat, taco_data: taco_data): tez => { + const taco_kind: taco_supply = + $match (Map.find_opt(taco_kind_index, taco_data), { + "Some": (kind) => kind, + "None": () => failwith("Unknown kind of taco"), + }); + return taco_kind.max_price / taco_kind.current_stock; + } + + // @view + const get_taco_price = (taco_kind_index: nat, storage: storage): tez => + get_taco_price_internal(taco_kind_index, storage.taco_data); + + // Buy a taco + // @entry + const buy_taco = (taco_kind_index: nat, storage: storage): [ + list, + storage + ] => { + + const { admin_address, taco_data } = storage; + + // Retrieve the kind of taco from the contracts storage or fail + const taco_kind: taco_supply = + $match (Map.find_opt(taco_kind_index, taco_data), { + "Some": (kind) => kind, + "None": () => failwith("Unknown kind of taco"), + }); + + // Get the current price of this type of taco + const current_purchase_price = get_taco_price_internal(taco_kind_index, taco_data); + + // Verify that the caller sent the correct amount of tez + if ((Tezos.get_amount()) != current_purchase_price) { + return failwith("Sorry, the taco you are trying to purchase has a different price"); + } + + // Verify that there is at least one of this type of taco + if (taco_kind.current_stock == (0 as nat)) { + return failwith("Sorry, we are out of this type of taco"); + } + + // Update the storage with the new quantity of tacos + const updated_taco_data: taco_data = Map.update( + taco_kind_index, + ["Some" as "Some", {...taco_kind, current_stock: abs(taco_kind.current_stock - 1) }], + taco_data); + + const updated_storage: storage = { + admin_address: admin_address, + taco_data: updated_taco_data, + }; + + return [[], updated_storage]; + } + + // @entry + const payout = (_u: unit, storage: storage): [ + list, + storage + ] => { + + // Entrypoint logic goes here + + return [[], storage]; + } + +}; +``` + + + + + +Add this view to the contract, after the `get_taco_price_internal` function and somewhere within the module: + +```cameligo skip +[@view] +let get_taco_price (taco_kind_index : nat) (storage : storage) : tez = + get_taco_price_internal taco_kind_index storage.taco_data +``` + +This view is merely a wrapper around the `get_taco_price_internal` function, but the `@view` attribute makes external clients able to call it. + +For more information about views, see [Views](../../syntax/contracts/views). + +The complete contract file looks like this: + +```cameligo group=selling_tacos +module TacoShop = struct + type taco_supply = { current_stock: nat; max_price: tez } + type taco_data = (nat, taco_supply) map + type admin_address = address + type storage = { + admin_address: admin_address; + taco_data: taco_data; + } + + let default_taco_data: taco_data = Map.literal [ + (1n, { current_stock = 50n; max_price = 50tez }); + (2n, { current_stock = 20n; max_price = 75tez }); + ] + + (* Internal function to get the price of a taco *) + let get_taco_price_internal (taco_kind_index : nat) (taco_data : taco_data) : tez = + let taco_kind : taco_supply = + match Map.find_opt taco_kind_index taco_data with + | Some kind -> kind + | None -> failwith "Unknown kind of taco" + in + taco_kind.max_price / taco_kind.current_stock + + [@view] + let get_taco_price (taco_kind_index : nat) (storage : storage) : tez = + get_taco_price_internal taco_kind_index storage.taco_data + + (* Buy a taco *) + [@entry] + let buy_taco (taco_kind_index : nat) (storage : storage) : operation list * storage = + + let { admin_address; taco_data } = storage in + + (* Retrieve the kind of taco from the contracts storage or fail *) + let taco_kind : taco_supply = + match Map.find_opt taco_kind_index taco_data with + | Some kind -> kind + | None -> failwith "Unknown kind of taco" in + + (* Get the current price of this type of taco *) + let current_purchase_price = get_taco_price_internal taco_kind_index taco_data in + + (* Verify that the caller sent the correct amount of tez *) + let _ = if (Tezos.get_amount () <> current_purchase_price) then + failwith "Sorry, the taco you are trying to purchase has a different price" in + + (* Verify that there is at least one of this type of taco *) + let _ = if (taco_kind.current_stock = 0n) then + failwith "Sorry, we are out of this type of taco" in + + + (* Update the storage with the new quantity of tacos *) + let updated_taco_data : taco_data = Map.update + taco_kind_index + (Some { taco_kind with current_stock = abs (taco_kind.current_stock - 1n) }) + taco_data in + + + let updated_storage : storage = { + admin_address = admin_address; + taco_data = updated_taco_data; + } in + + [], updated_storage + + [@entry] + let payout (_u : unit) (storage : storage) : operation list * storage = + + (* Entrypoint logic goes here *) + + [], storage + + end +``` + + + +## Compiling the contract + +Before you can deploy the contract to Tezos, you must compile it to Michelson,the low-level language of contracts on Tezos. + +Run this command to compile the contract: + + + +```bash +ligo compile contract -m TacoShop -o taco_shop.tz taco_shop.jsligo +``` + + + + + +```bash +ligo compile contract -m TacoShop -o taco_shop.tz taco_shop.mligo +``` + + + +If compilation is successful, LIGO prints nothing to the console and writes the compiled contract to the file `taco_shop.tz`. +You don't need to interact with this file directly. + +If you see errors, make sure your code matches the code in the previous section. + +You now have a basic contract that can accept requests to sell tacos. +However, before you deploy it, you should test the contract to make sure it works. +Continue to [Part 2: Testing the contract](./testing-contract). diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/getting-payouts/getting_payouts.jsligo b/gitlab-pages/docs/tutorials/taco-shop/src/getting-payouts/getting_payouts.jsligo new file mode 100644 index 0000000000..6a69662bff --- /dev/null +++ b/gitlab-pages/docs/tutorials/taco-shop/src/getting-payouts/getting_payouts.jsligo @@ -0,0 +1,223 @@ +namespace TacoShop { + export type taco_supply = { current_stock: nat, max_price: tez }; + export type taco_data = map; + export type admin_address = address; + export type storage = { + admin_address: admin_address, + taco_data: taco_data, + }; + + export const default_taco_data: taco_data = Map.literal([ + [1 as nat, { current_stock: 50 as nat, max_price: 50 as tez }], + [2 as nat, { current_stock: 20 as nat, max_price: 75 as tez }] + ]); + + // Internal function to get the price of a taco + const get_taco_price_internal = (taco_kind_index: nat, taco_data: taco_data): tez => { + const taco_kind: taco_supply = + $match (Map.find_opt(taco_kind_index, taco_data), { + "Some": (kind) => kind, + "None": () => failwith("Unknown kind of taco"), + }); + return taco_kind.max_price / taco_kind.current_stock; + } + + // @view + const get_taco_price = (taco_kind_index: nat, storage: storage): tez => + get_taco_price_internal(taco_kind_index, storage.taco_data); + + // Buy a taco + // @entry + const buy_taco = (taco_kind_index: nat, storage: storage): [ + list, + storage + ] => { + + const { admin_address, taco_data } = storage; + + // Retrieve the kind of taco from the contracts storage or fail + const taco_kind: taco_supply = + $match (Map.find_opt(taco_kind_index, taco_data), { + "Some": (kind) => kind, + "None": () => failwith("Unknown kind of taco"), + }); + + // Get the current price of this type of taco + const current_purchase_price = get_taco_price_internal(taco_kind_index, taco_data); + + // Verify that the caller sent the correct amount of tez + if ((Tezos.get_amount()) != current_purchase_price) { + return failwith("Sorry, the taco you are trying to purchase has a different price"); + } + + // Verify that there is at least one of this type of taco + if (taco_kind.current_stock == (0 as nat)) { + return failwith("Sorry, we are out of this type of taco"); + } + + // Update the storage with the new quantity of tacos + const updated_taco_data: taco_data = Map.update( + taco_kind_index, + ["Some" as "Some", {...taco_kind, current_stock: abs(taco_kind.current_stock - 1) }], + taco_data); + + const updated_storage: storage = { + admin_address: admin_address, + taco_data: updated_taco_data, + }; + + return [[], updated_storage]; + } + + // @entry + const payout = (_u: unit, storage: storage): [ + list, + storage + ] => { + + // Ensure that only the admin can call this entrypoint + if (Tezos.get_sender() != storage.admin_address) { + failwith("Only the admin can call this entrypoint"); + } + + // Create contract object that represents the target account + const receiver_contract = $match(Tezos.get_contract_opt(storage.admin_address), { + "Some": (contract) => contract, + "None": () => failwith("Couldn't find account"), + }); + + // Create operation to send tez + const payout_operation = Tezos.Operation.transaction(unit, Tezos.get_balance(), receiver_contract); + + // Restore stock of tacos + const new_storage: storage = { + admin_address: storage.admin_address, + taco_data: default_taco_data, + }; + + return [[payout_operation], new_storage]; + } +}; + +// Convenience function to get current taco price +const get_taco_price = (untyped_address: address, taco_kind_index: nat): tez => { + const view_result_option: option = Tezos.View.call("get_taco_price", taco_kind_index, untyped_address); + return $match(view_result_option, { + "Some": (cost_mutez) => cost_mutez, + "None": () => Test.Assert.failwith("Couldn't get the price of the taco."), + }); +} + +// Convenience function for testing equality in maps +const eq_in_map = (r: TacoShop.taco_supply, m: TacoShop.taco_data, k: nat) => + $match(Map.find_opt(k, m), { + "None": () => false, + "Some": (v) => v.current_stock == r.current_stock && v.max_price == r.max_price + }); + +const test = (() => { + + // Set the initial storage and deploy the contract + const admin_address: address = Test.Account.address(0 as nat); + const initial_storage: TacoShop.storage = { + admin_address: admin_address, + taco_data: TacoShop.default_taco_data, + } + const contract = Test.Originate.contract(contract_of(TacoShop), initial_storage, 0 as tez); + + // Get the current price of a taco + const untyped_address = Test.Typed_address.to_address(contract.taddr); + const current_price = get_taco_price(untyped_address, 1 as nat); + + // Purchase a taco + const success_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("buy_taco", contract.taddr), + 1 as nat, + current_price + ); + + // Verify that the stock was updated + $match(success_result, { + "Success": (_s) => (() => { + const storage = Test.Typed_address.get_storage(contract.taddr); + // Check that the stock has been updated correctly + Assert.assert( + eq_in_map( + { current_stock: 49 as nat, max_price: 50000000 as mutez }, + storage.taco_data, + 1 as nat + )); + // Check that the amount of the other taco type has not changed + Assert.assert(eq_in_map( + { current_stock: 20 as nat, max_price: 75000000 as mutez }, + storage.taco_data, + 2 as nat + ) + ); + Test.IO.log("Successfully bought a taco"); + })(), + "Fail": (err) => failwith(err), + }); + + // Fail to purchase a taco without sending enough tez + const fail_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("buy_taco", contract.taddr), + 1 as nat, + 1 as mutez + ); + $match(fail_result, { + "Success": (_s) => failwith("Test was able to buy a taco for the wrong price"), + "Fail": (_err) => Test.IO.log("Contract successfully blocked purchase with incorrect price"), + }); + + // Test the payout entrypoint as the administrator + const admin_balance_before = Test.Address.get_balance(admin_address); + Test.State.set_source(admin_address); + const payout_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("payout", contract.taddr), + unit, + 0 as tez + ); + $match(payout_result, { + "Success": (_s) => (() => { + const storage = Test.Typed_address.get_storage(contract.taddr); + // Check that the stock has been reset + Assert.assert( + eq_in_map( + Map.find(1 as nat, TacoShop.default_taco_data), + storage.taco_data, + 1 as nat + )); + Assert.assert( + eq_in_map( + Map.find(2 as nat, TacoShop.default_taco_data), + storage.taco_data, + 2 as nat + )); + Test.IO.log("Successfully reset taco storage"); + })(), + "Fail": (_err) => failwith("Failed to reset taco storage"), + }); + + // Check that the admin account got a payout + const admin_balance_after = Test.Address.get_balance(admin_address); + Assert.assert(Test.Compare.lt(admin_balance_before, admin_balance_after)); + + // Verify that the entrypoint fails if called by someone else + const other_user_account = Test.Account.address(1 as nat); + Test.State.set_source(other_user_account); + const failed_payout_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("payout", contract.taddr), + unit, + 0 as tez + ); + $match(failed_payout_result, { + "Success": (_s) => failwith("A non-admin user was able to call the payout entrypoint"), + "Fail": (_err) => Test.IO.log("Successfully prevented a non-admin user from calling the payout entrypoint"), + }); + +}) (); \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/getting-payouts/getting_payouts.mligo b/gitlab-pages/docs/tutorials/taco-shop/src/getting-payouts/getting_payouts.mligo new file mode 100644 index 0000000000..f2f7e8c2b3 --- /dev/null +++ b/gitlab-pages/docs/tutorials/taco-shop/src/getting-payouts/getting_payouts.mligo @@ -0,0 +1,191 @@ +module TacoShop = struct + type taco_supply = { current_stock: nat; max_price: tez } + type taco_data = (nat, taco_supply) map + type admin_address = address + type storage = { + admin_address: admin_address; + taco_data: taco_data; + } + + let default_taco_data: taco_data = Map.literal [ + (1n, { current_stock = 50n; max_price = 50tez }); + (2n, { current_stock = 20n; max_price = 75tez }); + ] + + (* Internal function to get the price of a taco *) + let get_taco_price_internal (taco_kind_index : nat) (taco_data : taco_data) : tez = + let taco_kind : taco_supply = + match Map.find_opt taco_kind_index taco_data with + | Some kind -> kind + | None -> failwith "Unknown kind of taco" + in + taco_kind.max_price / taco_kind.current_stock + + [@view] + let get_taco_price (taco_kind_index : nat) (storage : storage) : tez = + get_taco_price_internal taco_kind_index storage.taco_data + + (* Buy a taco *) + [@entry] + let buy_taco (taco_kind_index : nat) (storage : storage) : operation list * storage = + + let { admin_address; taco_data } = storage in + + (* Retrieve the kind of taco from the contracts storage or fail *) + let taco_kind : taco_supply = + match Map.find_opt taco_kind_index taco_data with + | Some kind -> kind + | None -> failwith "Unknown kind of taco" in + + (* Get the current price of this type of taco *) + let current_purchase_price = get_taco_price_internal taco_kind_index taco_data in + + (* Verify that the caller sent the correct amount of tez *) + let _ = if (Tezos.get_amount () <> current_purchase_price) then + failwith "Sorry, the taco you are trying to purchase has a different price" in + + (* Verify that there is at least one of this type of taco *) + let _ = if (taco_kind.current_stock = 0n) then + failwith "Sorry, we are out of this type of taco" in + + (* Update the storage with the new quantity of tacos *) + let updated_taco_data : taco_data = Map.update + taco_kind_index + (Some { taco_kind with current_stock = abs (taco_kind.current_stock - 1n) }) + taco_data in + + let updated_storage : storage = { + admin_address = admin_address; + taco_data = updated_taco_data; + } in + + [], updated_storage + + [@entry] + let payout (_u : unit) (storage : storage) : operation list * storage = + + (* Ensure that only the admin can call this entrypoint *) + let _ = if (Tezos.get_sender () <> storage.admin_address) then + failwith "Only the admin can call this entrypoint" in + + (* Create contract object that represents the target account *) + let receiver_contract = match Tezos.get_contract_opt storage.admin_address with + | Some contract -> contract + | None -> failwith "Couldn't find account" in + + (* Create operation to send tez *) + let payout_operation = Tezos.Operation.transaction unit (Tezos.get_balance ()) receiver_contract in + + (* Restore stock of tacos *) + let new_storage : storage = { + admin_address = storage.admin_address; + taco_data = default_taco_data + } in + + [payout_operation], new_storage + +end + +(* Convenience function to get current taco price *) +let get_taco_price (untyped_address : address) (taco_kind_index : nat) : tez = + let view_result_option : tez option = Tezos.View.call + "get_taco_price" + taco_kind_index + untyped_address in + match view_result_option with + | Some cost_mutez -> cost_mutez + | None -> Test.Assert.failwith "Couldn't get the price of a taco" + +(* Convenience function for testing equality in maps *) +let eq_in_map (r : TacoShop.taco_supply) (m : TacoShop.taco_data) (k : nat) = + match Map.find_opt k m with + | None -> false + | Some v -> v.current_stock = r.current_stock && v.max_price = r.max_price + +let test = + + (* Set the initial storage and deploy the contract *) + let admin_address : address = Test.Account.address 0n in + let initial_storage : TacoShop.storage = { + admin_address = admin_address; + taco_data = TacoShop.default_taco_data + } in + let contract = Test.Originate.contract (contract_of TacoShop) initial_storage 0tez in + + (* Get the current price of a taco *) + let untyped_address = Test.Typed_address.to_address contract.taddr in + let current_price = get_taco_price untyped_address 1n in + + (* Purchase a taco *) + let success_result = + Test.Contract.transfer + (Test.Typed_address.get_entrypoint "buy_taco" contract.taddr) + 1n + current_price + in + + (* Verify that the stock was updated *) + let () = match success_result with + | Success _s -> + let storage = Test.Typed_address.get_storage contract.taddr in + let () = Assert.assert (eq_in_map + { current_stock = 49n; max_price = 50000000mutez } + storage.taco_data + 1n + ) in + let () = Assert.assert (eq_in_map + { current_stock = 20n; max_price = 75000000mutez } + storage.taco_data + 2n + ) in + Test.IO.log "Successfully bought a taco" + | Fail err -> failwith err + in + + (* Fail to purchase a taco without sending enough tez *) + let fail_result = Test.Contract.transfer + (Test.Typed_address.get_entrypoint "buy_taco" contract.taddr) + 1n + 1mutez in + let () = match fail_result with + | Success _s -> failwith "Test was able to buy a taco for the wrong price" + | Fail _err -> Test.IO.log "Contract successfully blocked purchase with incorrect price" in + + (* Test the payout entrypoint as the administrator *) + let admin_balance_before = Test.Address.get_balance admin_address in + + let () = Test.State.set_source admin_address in + + let payout_result = Test.Contract.transfer + (Test.Typed_address.get_entrypoint "payout" contract.taddr) + unit + 0tez + in + let () = match payout_result with + | Success _s -> let storage = Test.Typed_address.get_storage contract.taddr in + let () = Assert.assert + (eq_in_map (Map.find 1n TacoShop.default_taco_data) + storage.taco_data + 1n) in + let () = Assert.assert + (eq_in_map (Map.find 2n TacoShop.default_taco_data) + storage.taco_data + 2n) in + Test.IO.log "Successfully reset taco storage" + | Fail _err -> failwith "Failed to reset taco storage" in + + (* Check that the admin account got a payout *) + let admin_balance_after = Test.Address.get_balance admin_address in + let () = Assert.assert (Test.Compare.lt admin_balance_before admin_balance_after) in + + (* Verify that the entrypoint fails if called by someone else *) + let other_user_account = Test.Account.address 1n in + let _ = Test.State.set_source other_user_account in + let failed_payout_result = Test.Contract.transfer + (Test.Typed_address.get_entrypoint "payout" contract.taddr) + unit + 0tez + in + match failed_payout_result with + | Success _s -> failwith "A non-admin user was able to call the payout entrypoint" + | Fail _err -> Test.IO.log "Successfully prevented a non-admin user from calling the payout entrypoint" \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/selling-tacos/selling_tacos.jsligo b/gitlab-pages/docs/tutorials/taco-shop/src/selling-tacos/selling_tacos.jsligo new file mode 100644 index 0000000000..bf4f7dfe35 --- /dev/null +++ b/gitlab-pages/docs/tutorials/taco-shop/src/selling-tacos/selling_tacos.jsligo @@ -0,0 +1,83 @@ +namespace TacoShop { + export type taco_supply = { current_stock: nat, max_price: tez }; + export type taco_data = map; + export type admin_address = address; + export type storage = { + admin_address: admin_address, + taco_data: taco_data, + }; + + export const default_taco_data: taco_data = Map.literal([ + [1 as nat, { current_stock: 50 as nat, max_price: 50 as tez }], + [2 as nat, { current_stock: 20 as nat, max_price: 75 as tez }] + ]); + + // Internal function to get the price of a taco + const get_taco_price_internal = (taco_kind_index: nat, taco_data: taco_data): tez => { + const taco_kind: taco_supply = + $match (Map.find_opt(taco_kind_index, taco_data), { + "Some": (kind) => kind, + "None": () => failwith("Unknown kind of taco"), + }); + return taco_kind.max_price / taco_kind.current_stock; + } + + // @view + const get_taco_price = (taco_kind_index: nat, storage: storage): tez => + get_taco_price_internal(taco_kind_index, storage.taco_data); + + // Buy a taco + // @entry + const buy_taco = (taco_kind_index: nat, storage: storage): [ + list, + storage + ] => { + + const { admin_address, taco_data } = storage; + + // Retrieve the kind of taco from the contracts storage or fail + const taco_kind: taco_supply = + $match (Map.find_opt(taco_kind_index, taco_data), { + "Some": (kind) => kind, + "None": () => failwith("Unknown kind of taco"), + }); + + // Get the current price of this type of taco + const current_purchase_price = get_taco_price_internal(taco_kind_index, taco_data); + + // Verify that the caller sent the correct amount of tez + if ((Tezos.get_amount()) != current_purchase_price) { + return failwith("Sorry, the taco you are trying to purchase has a different price"); + } + + // Verify that there is at least one of this type of taco + if (taco_kind.current_stock == (0 as nat)) { + return failwith("Sorry, we are out of this type of taco"); + } + + // Update the storage with the new quantity of tacos + const updated_taco_data: taco_data = Map.update( + taco_kind_index, + ["Some" as "Some", {...taco_kind, current_stock: abs(taco_kind.current_stock - 1) }], + taco_data); + + const updated_storage: storage = { + admin_address: admin_address, + taco_data: updated_taco_data, + }; + + return [[], updated_storage]; + } + + // @entry + const payout = (_u: unit, storage: storage): [ + list, + storage + ] => { + + // Entrypoint logic goes here + + return [[], storage]; + } + +}; \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/selling-tacos/selling_tacos.mligo b/gitlab-pages/docs/tutorials/taco-shop/src/selling-tacos/selling_tacos.mligo new file mode 100644 index 0000000000..5be1cf00b1 --- /dev/null +++ b/gitlab-pages/docs/tutorials/taco-shop/src/selling-tacos/selling_tacos.mligo @@ -0,0 +1,73 @@ +module TacoShop = struct + type taco_supply = { current_stock: nat; max_price: tez } + type taco_data = (nat, taco_supply) map + type admin_address = address + type storage = { + admin_address: admin_address; + taco_data: taco_data; + } + + let default_taco_data: taco_data = Map.literal [ + (1n, { current_stock = 50n; max_price = 50tez }); + (2n, { current_stock = 20n; max_price = 75tez }); + ] + + (* Internal function to get the price of a taco *) + let get_taco_price_internal (taco_kind_index : nat) (taco_data : taco_data) : tez = + let taco_kind : taco_supply = + match Map.find_opt taco_kind_index taco_data with + | Some kind -> kind + | None -> failwith "Unknown kind of taco" + in + taco_kind.max_price / taco_kind.current_stock + + [@view] + let get_taco_price (taco_kind_index : nat) (storage : storage) : tez = + get_taco_price_internal taco_kind_index storage.taco_data + + (* Buy a taco *) + [@entry] + let buy_taco (taco_kind_index : nat) (storage : storage) : operation list * storage = + + let { admin_address; taco_data } = storage in + + (* Retrieve the kind of taco from the contracts storage or fail *) + let taco_kind : taco_supply = + match Map.find_opt taco_kind_index taco_data with + | Some kind -> kind + | None -> failwith "Unknown kind of taco" in + + (* Get the current price of this type of taco *) + let current_purchase_price = get_taco_price_internal taco_kind_index taco_data in + + (* Verify that the caller sent the correct amount of tez *) + let _ = if (Tezos.get_amount () <> current_purchase_price) then + failwith "Sorry, the taco you are trying to purchase has a different price" in + + (* Verify that there is at least one of this type of taco *) + let _ = if (taco_kind.current_stock = 0n) then + failwith "Sorry, we are out of this type of taco" in + + + (* Update the storage with the new quantity of tacos *) + let updated_taco_data : taco_data = Map.update + taco_kind_index + (Some { taco_kind with current_stock = abs (taco_kind.current_stock - 1n) }) + taco_data in + + + let updated_storage : storage = { + admin_address = admin_address; + taco_data = updated_taco_data; + } in + + [], updated_storage + + [@entry] + let payout (_u : unit) (storage : storage) : operation list * storage = + + (* Entrypoint logic goes here *) + + [], storage + + end \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/testing-contract/testing_contract.jsligo b/gitlab-pages/docs/tutorials/taco-shop/src/testing-contract/testing_contract.jsligo new file mode 100644 index 0000000000..e624ef0fc7 --- /dev/null +++ b/gitlab-pages/docs/tutorials/taco-shop/src/testing-contract/testing_contract.jsligo @@ -0,0 +1,157 @@ +namespace TacoShop { + export type taco_supply = { current_stock: nat, max_price: tez }; + export type taco_data = map; + export type admin_address = address; + export type storage = { + admin_address: admin_address, + taco_data: taco_data, + }; + + export const default_taco_data: taco_data = Map.literal([ + [1 as nat, { current_stock: 50 as nat, max_price: 50 as tez }], + [2 as nat, { current_stock: 20 as nat, max_price: 75 as tez }] + ]); + + // Internal function to get the price of a taco + const get_taco_price_internal = (taco_kind_index: nat, taco_data: taco_data): tez => { + const taco_kind: taco_supply = + $match (Map.find_opt(taco_kind_index, taco_data), { + "Some": (kind) => kind, + "None": () => failwith("Unknown kind of taco"), + }); + return taco_kind.max_price / taco_kind.current_stock; + } + + // @view + const get_taco_price = (taco_kind_index: nat, storage: storage): tez => + get_taco_price_internal(taco_kind_index, storage.taco_data); + + // Buy a taco + // @entry + const buy_taco = (taco_kind_index: nat, storage: storage): [ + list, + storage + ] => { + + const { admin_address, taco_data } = storage; + + // Retrieve the kind of taco from the contracts storage or fail + const taco_kind: taco_supply = + $match (Map.find_opt(taco_kind_index, taco_data), { + "Some": (kind) => kind, + "None": () => failwith("Unknown kind of taco"), + }); + + // Get the current price of this type of taco + const current_purchase_price = get_taco_price_internal(taco_kind_index, taco_data); + + // Verify that the caller sent the correct amount of tez + if ((Tezos.get_amount()) != current_purchase_price) { + return failwith("Sorry, the taco you are trying to purchase has a different price"); + } + + // Verify that there is at least one of this type of taco + if (taco_kind.current_stock == (0 as nat)) { + return failwith("Sorry, we are out of this type of taco"); + } + + // Update the storage with the new quantity of tacos + const updated_taco_data: taco_data = Map.update( + taco_kind_index, + ["Some" as "Some", {...taco_kind, current_stock: abs(taco_kind.current_stock - 1) }], + taco_data); + + const updated_storage: storage = { + admin_address: admin_address, + taco_data: updated_taco_data, + }; + + return [[], updated_storage]; + } + + // @entry + const payout = (_u: unit, storage: storage): [ + list, + storage + ] => { + + // Entrypoint logic goes here + + return [[], storage]; + } + +}; + +// Convenience function to get current taco price +const get_taco_price = (untyped_address: address, taco_kind_index: nat): tez => { + const view_result_option: option = Tezos.View.call("get_taco_price", taco_kind_index, untyped_address); + return $match(view_result_option, { + "Some": cost_mutez => cost_mutez, + "None": () => Test.Assert.failwith("Couldn't get the price of the taco."), + }); +} + +// Convenience function for testing equality in maps +const eq_in_map = (r: TacoShop.taco_supply, m: TacoShop.taco_data, k: nat) => + $match(Map.find_opt(k, m), { + "None": () => false, + "Some": (v) => v.current_stock == r.current_stock && v.max_price == r.max_price + }); + +const test = (() => { + + // Set the initial storage and deploy the contract + const admin_address: address = Test.Account.address(0 as nat); + const initial_storage: TacoShop.storage = { + admin_address: admin_address, + taco_data: TacoShop.default_taco_data, + } + const contract = Test.Originate.contract(contract_of(TacoShop), initial_storage, 0 as tez); + + // Get the current price of a taco + const untyped_address = Test.Typed_address.to_address(contract.taddr); + const current_price = get_taco_price(untyped_address, 1 as nat); + + // Purchase a taco + const success_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("buy_taco", contract.taddr), + 1 as nat, + current_price + ); + + // Verify that the stock was updated + $match(success_result, { + "Success": (_s) => (() => { + const storage = Test.Typed_address.get_storage(contract.taddr); + // Check that the stock has been updated correctly + Assert.assert( + eq_in_map( + { current_stock: 49 as nat, max_price: 50000000 as mutez }, + storage.taco_data, + 1 as nat + )); + // Check that the amount of the other taco type has not changed + Assert.assert(eq_in_map( + { current_stock: 20 as nat, max_price: 75000000 as mutez }, + storage.taco_data, + 2 as nat + ) + ); + Test.IO.log("Successfully bought a taco"); + })(), + "Fail": err => failwith(err), + }); + + // Fail to purchase a taco without sending enough tez + const fail_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("buy_taco", contract.taddr), + 1 as nat, + 1 as mutez + ); + $match(fail_result, { + "Success": _s => failwith("Test was able to buy a taco for the wrong price"), + "Fail": _err => Test.IO.log("Contract successfully blocked purchase with incorrect price"), + }); +}) (); \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/testing-contract/testing_contract.mligo b/gitlab-pages/docs/tutorials/taco-shop/src/testing-contract/testing_contract.mligo new file mode 100644 index 0000000000..920195c9a4 --- /dev/null +++ b/gitlab-pages/docs/tutorials/taco-shop/src/testing-contract/testing_contract.mligo @@ -0,0 +1,138 @@ +module TacoShop = struct + type taco_supply = { current_stock: nat; max_price: tez } + type taco_data = (nat, taco_supply) map + type admin_address = address + type storage = { + admin_address: admin_address; + taco_data: taco_data; + } + + let default_taco_data: taco_data = Map.literal [ + (1n, { current_stock = 50n; max_price = 50tez }); + (2n, { current_stock = 20n; max_price = 75tez }); + ] + + (* Internal function to get the price of a taco *) + let get_taco_price_internal (taco_kind_index : nat) (taco_data : taco_data) : tez = + let taco_kind : taco_supply = + match Map.find_opt taco_kind_index taco_data with + | Some kind -> kind + | None -> failwith "Unknown kind of taco" + in + taco_kind.max_price / taco_kind.current_stock + + [@view] + let get_taco_price (taco_kind_index : nat) (storage : storage) : tez = + get_taco_price_internal taco_kind_index storage.taco_data + + (* Buy a taco *) + [@entry] + let buy_taco (taco_kind_index : nat) (storage : storage) : operation list * storage = + + let { admin_address; taco_data } = storage in + + (* Retrieve the kind of taco from the contracts storage or fail *) + let taco_kind : taco_supply = + match Map.find_opt taco_kind_index taco_data with + | Some kind -> kind + | None -> failwith "Unknown kind of taco" in + + (* Get the current price of this type of taco *) + let current_purchase_price = get_taco_price_internal taco_kind_index taco_data in + + (* Verify that the caller sent the correct amount of tez *) + let _ = if (Tezos.get_amount () <> current_purchase_price) then + failwith "Sorry, the taco you are trying to purchase has a different price" in + + (* Verify that there is at least one of this type of taco *) + let _ = if (taco_kind.current_stock = 0n) then + failwith "Sorry, we are out of this type of taco" in + + + (* Update the storage with the new quantity of tacos *) + let updated_taco_data : taco_data = Map.update + taco_kind_index + (Some { taco_kind with current_stock = abs (taco_kind.current_stock - 1n) }) + taco_data in + + + let updated_storage : storage = { + admin_address = admin_address; + taco_data = updated_taco_data; + } in + + [], updated_storage + + [@entry] + let payout (_u : unit) (storage : storage) : operation list * storage = + + (* Entrypoint logic goes here *) + + [], storage + + end + +(* Convenience function to get current taco price *) +let get_taco_price (untyped_address : address) (taco_kind_index : nat) : tez = + let view_result_option : tez option = Tezos.View.call + "get_taco_price" + taco_kind_index + untyped_address in + match view_result_option with + | Some cost_mutez -> cost_mutez + | None -> Test.Assert.failwith "Couldn't get the price of a taco" + +(* Convenience function for testing equality in maps *) +let eq_in_map (r : TacoShop.taco_supply) (m : TacoShop.taco_data) (k : nat) = + match Map.find_opt k m with + | None -> false + | Some v -> v.current_stock = r.current_stock && v.max_price = r.max_price + +let test = + + (* Set the initial storage and deploy the contract *) + let admin_address : address = Test.Account.address 0n in + let initial_storage : TacoShop.storage = { + admin_address = admin_address; + taco_data = TacoShop.default_taco_data + } in + let contract = Test.Originate.contract (contract_of TacoShop) initial_storage 0tez in + + (* Get the current price of a taco *) + let untyped_address = Test.Typed_address.to_address contract.taddr in + let current_price = get_taco_price untyped_address 1n in + + (* Purchase a taco *) + let success_result = + Test.Contract.transfer + (Test.Typed_address.get_entrypoint "buy_taco" contract.taddr) + 1n + current_price + in + + (* Verify that the stock was updated *) + let () = match success_result with + | Success _s -> + let storage = Test.Typed_address.get_storage contract.taddr in + let () = Assert.assert (eq_in_map + { current_stock = 49n; max_price = 50000000mutez } + storage.taco_data + 1n + ) in + let () = Assert.assert (eq_in_map + { current_stock = 20n; max_price = 75000000mutez } + storage.taco_data + 2n + ) in + Test.IO.log "Successfully bought a taco" + | Fail err -> failwith err + in + + (* Fail to purchase a taco without sending enough tez *) + let fail_result = Test.Contract.transfer + (Test.Typed_address.get_entrypoint "buy_taco" contract.taddr) + 1n + 1mutez in + match fail_result with + | Success _s -> failwith "Test was able to buy a taco for the wrong price" + | Fail _err -> Test.IO.log "Contract successfully blocked purchase with incorrect price" \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/a.jsligo b/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/a.jsligo deleted file mode 100644 index d48ef1e699..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/a.jsligo +++ /dev/null @@ -1,39 +0,0 @@ -namespace TacoShop { - export type taco_supply = { current_stock: nat, max_price: tez }; - export type taco_shop_storage = map; - - @entry - function buy_taco(taco_kind_index: nat, taco_shop_storage: taco_shop_storage): [ - list, - taco_shop_storage - ] { - /* Retrieve the taco_kind from the contracts storage or fail */ - const taco_kind : taco_supply = - match (Map.find_opt (taco_kind_index, taco_shop_storage)) { - when(Some(kind)): kind; - when(None()): failwith ("Unknown kind of taco") - }; - const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock ; - /* We won't sell tacos if the amount is not correct */ - if ((Tezos.get_amount ()) != current_purchase_price) { - return failwith ("Sorry, the taco you are trying to purchase has a different price") - } else { - /* Update the storage decreasing the stock by 1n */ - const taco_shop_storage = Map.update ( - taco_kind_index, - (Some ({...taco_kind, current_stock : abs (taco_kind.current_stock - 1n) })), - taco_shop_storage ); - return [[], taco_shop_storage] - } - }; -}; - -const default_storage: TacoShop.taco_shop_storage = - Map.literal( - list( - [ - [1n, { current_stock: 50n, max_price: 50000000mutez }], - [2n, { current_stock: 20n, max_price: 75000000mutez }] - ] - ) - ); diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/a.mligo b/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/a.mligo deleted file mode 100644 index 43bf74f414..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/a.mligo +++ /dev/null @@ -1,53 +0,0 @@ -module TacoShop = struct - type taco_supply = - { - current_stock : nat; - max_price : tez - } - - type taco_shop_storage = (nat, taco_supply) map - - [@entry] - let buy_taco (taco_kind_index : nat) (taco_shop_storage : taco_shop_storage) - : operation list * taco_shop_storage = - (* Retrieve the taco_kind from the contract's storage or fail *) - - let taco_kind = - match Map.find_opt (taco_kind_index) taco_shop_storage with - Some k -> k - | None -> failwith "Unknown kind of taco" in - let current_purchase_price : tez = - taco_kind.max_price / taco_kind.current_stock in - (* We won't sell tacos if the amount is not correct *) - - let () = - if (Tezos.get_amount ()) <> current_purchase_price - then - failwith - "Sorry, the taco you are trying to purchase has a different price" in - (* Update the storage decreasing the stock by 1n *) - - let taco_shop_storage = - Map.update - taco_kind_index - (Some - {taco_kind with current_stock = abs (taco_kind.current_stock - 1n)}) - taco_shop_storage in - [], taco_shop_storage - -end - -let default_storage : TacoShop.taco_shop_storage = - Map.literal - [ - (1n, - { - current_stock = 50n; - max_price = 50000000mutez - }); - (2n, - { - current_stock = 20n; - max_price = 75000000mutez - }) - ] \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/b.jsligo b/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/b.jsligo deleted file mode 100644 index cb544b4219..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/b.jsligo +++ /dev/null @@ -1,79 +0,0 @@ -namespace TacoShop { - export type taco_supply = { current_stock: nat, max_price: tez }; - export type taco_shop_storage = map; - - const ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV"; - - const donationAddress : address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; - - @entry - function buy_taco(taco_kind_index: nat, taco_shop_storage: taco_shop_storage): [ - list, - taco_shop_storage - ] { - /* Retrieve the taco_kind from the contracts storage or fail */ - - const taco_kind: taco_supply = - match(Map.find_opt(taco_kind_index, taco_shop_storage)) { - when (Some(kind)): - kind - when (None): - failwith("Unknown kind of taco") - }; - const current_purchase_price: tez = - taco_kind.max_price / taco_kind.current_stock; - /* We won't sell tacos if the amount is not correct */ - - if ((Tezos.get_amount()) != current_purchase_price) { - return failwith( - "Sorry, the taco you are trying to purchase has a different price" - ) - } else { - /* Update the storage decreasing the stock by 1n */ - - const taco_shop_storage = - Map.update( - taco_kind_index, - ( - Some({ - ...taco_kind, current_stock: abs(taco_kind.current_stock - 1n) - }) - ), - taco_shop_storage - ); - - const receiver : contract = - match ((Tezos.get_contract_opt (ownerAddress))) { - when(Some(contract)): contract; - when(None()): ((failwith ("Not a contract"))) - }; - - const donationReceiver : contract = - match ((Tezos.get_contract_opt (donationAddress))) { - when(Some(contract)): contract; - when(None()): ((failwith ("Not a contract"))) - }; - - const donationAmount = ((Tezos.get_amount ()) / 10n) as tez; - - // Pedro will get 90% of the amount - const op1 = match ((Tezos.get_amount ()) - donationAmount) { - when(Some(x)): Tezos.transaction (unit, x, receiver); - when(None()): failwith ("Insufficient balance") - }; - const op2 = Tezos.transaction (unit, donationAmount, donationReceiver); - const operations : list = [ op1 , op2 ]; - - return [operations, taco_shop_storage] - } - }; -} -const default_storage: TacoShop.taco_shop_storage = - Map.literal( - list( - [ - [1n, { current_stock: 50n, max_price: 50000000mutez }], - [2n, { current_stock: 20n, max_price: 75000000mutez }] - ] - ) - ); diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/b.mligo b/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/b.mligo deleted file mode 100644 index a0784bff46..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/b.mligo +++ /dev/null @@ -1,63 +0,0 @@ -module TacoShop = struct - type taco_supply = - { - current_stock : nat; - max_price : tez - } - - type taco_shop_storage = (nat, taco_supply) map - - let ownerAddress : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address) - - [@entry] - let buy_taco (taco_kind_index : nat) (taco_shop_storage : taco_shop_storage) - : operation list * taco_shop_storage = - (* Retrieve the taco_kind from the contract's storage or fail *) - - let taco_kind = - match Map.find_opt (taco_kind_index) taco_shop_storage with - Some k -> k - | None -> failwith "Unknown kind of taco" in - let current_purchase_price : tez = - taco_kind.max_price / taco_kind.current_stock in - (* We won't sell tacos if the amount is not correct *) - - let () = - if (Tezos.get_amount ()) <> current_purchase_price - then - failwith - "Sorry, the taco you are trying to purchase has a different price" in - (* Update the storage decreasing the stock by 1n *) - - let taco_shop_storage = - Map.update - taco_kind_index - (Some - {taco_kind with current_stock = abs (taco_kind.current_stock - 1n)}) - taco_shop_storage in - - let receiver : unit contract = - match (Tezos.get_contract_opt ownerAddress : unit contract option) with - Some (contract) -> contract - | None -> (failwith "Not a contract" : unit contract) in - - let payoutOperation : operation = Tezos.transaction () (Tezos.get_amount ()) receiver in - let operations : operation list = [payoutOperation] in - - operations, taco_shop_storage -end - -let default_storage : TacoShop.taco_shop_storage = - Map.literal - [ - (1n, - { - current_stock = 50n; - max_price = 50000000mutez - }); - (2n, - { - current_stock = 20n; - max_price = 75000000mutez - }) - ] \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/bonus.jsligo b/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/bonus.jsligo deleted file mode 100644 index cdd974c147..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/bonus.jsligo +++ /dev/null @@ -1,24 +0,0 @@ -const ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV"; -const donationAddress : address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; - -const receiver : contract = - match ((Tezos.get_contract_opt (ownerAddress)) as option>) { - when(Some(contract)): contract; - when(None()): ((failwith ("Not a contract")) as contract) - }; - -const donationReceiver : contract = - match ((Tezos.get_contract_opt (donationAddress)) as option>) { - when(Some(contract)): contract; - when(None()): ((failwith ("Not a contract")) as contract) - }; - -const donationAmount = ((Tezos.get_amount ()) / 10n) as tez; - -// Pedro will get 90% of the amount -const op1 = match ((Tezos.get_amount ()) - donationAmount) { - when(Some(x)): Tezos.transaction (unit, x, receiver); - when(None()): failwith ("Insufficient balance") -}; -const op2 = Tezos.transaction (unit, donationAmount, donationReceiver); -const operations : list = [ op1 , op2 ]; \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/bonus.mligo b/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/bonus.mligo deleted file mode 100644 index 5352171b6a..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/bonus.mligo +++ /dev/null @@ -1,22 +0,0 @@ -let ownerAddress : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address) -let donationAddress : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) - -let receiver : unit contract = - match ((Tezos.get_contract_opt ownerAddress) : unit contract option) with - Some contract -> contract - | None -> ((failwith "Not a contract") : unit contract) - -let donationReceiver : unit contract = - match ((Tezos.get_contract_opt donationAddress) : unit contract option) with - Some contract -> contract - | None -> ((failwith "Not a contract") : unit contract) - -let donationAmount : tez = (Tezos.get_amount ()) / 10n - -let operations : operation list = - // Pedro will get 90% of the amount - let op = match ((Tezos.get_amount ()) - donationAmount) with - | Some x -> Tezos.transaction () x receiver - | None -> (failwith "Insufficient balance") - in - [ op ; Tezos.transaction () donationAmount donationReceiver ] \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/ex1.jsligo b/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/ex1.jsligo deleted file mode 100644 index f6974e8b08..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/ex1.jsligo +++ /dev/null @@ -1,8 +0,0 @@ -const ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" -const receiver : contract = - match (Tezos.get_contract_opt(ownerAddress) as option>) { - when(Some(contract)): contract; - when(None()): (failwith ("Not a contract") as contract) - } -const payoutOperation : operation = Tezos.transaction (unit, Tezos.get_amount (), receiver) ; -const operations : list = [payoutOperation]; \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/ex1.mligo b/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/ex1.mligo deleted file mode 100644 index 265475f849..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-payout/ex1.mligo +++ /dev/null @@ -1,7 +0,0 @@ -let ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" -let receiver : unit contract = - match (Tezos.get_contract_opt ownerAddress : unit contract option) with - Some (contract) -> contract - | None -> (failwith "Not a contract" : unit contract) -let payoutOperation : operation = Tezos.transaction () (Tezos.get_amount ()) receiver -let operations : operation list = [payoutOperation] \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/TacoShop.jsligo b/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/TacoShop.jsligo deleted file mode 100644 index d6f9c9088a..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/TacoShop.jsligo +++ /dev/null @@ -1,31 +0,0 @@ -export type taco_supply = { current_stock : nat , max_price : tez }; - -export type taco_shop_storage = map ; -const default_storage: taco_shop_storage = Map.literal ([ - [1n, { current_stock : 50n, max_price : 50tez }], - [2n, { current_stock : 20n, max_price : 75tez }] -]); -@entry -const buy_taco = (taco_kind_index: nat, taco_shop_storage: taco_shop_storage) : [ - list, - taco_shop_storage - ] => { - /* Retrieve the taco_kind from the contracts storage or fail */ - const taco_kind : taco_supply = - match (Map.find_opt (taco_kind_index, taco_shop_storage)) { - when(Some(kind)): kind; - when(None()): failwith ("Unknown kind of taco") - }; - const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock ; - /* We won't sell tacos if the amount is not correct */ - if ((Tezos.get_amount ()) != current_purchase_price) { - return failwith ("Sorry, the taco you are trying to purchase has a different price") - } else { - /* Update the storage decreasing the stock by 1n */ - let taco_shop_storage = Map.update ( - taco_kind_index, - (Some (({...taco_kind, current_stock : abs (taco_kind.current_stock - 1n) }))), - taco_shop_storage ); - return [[], taco_shop_storage] - } -}; \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/TacoShop.mligo b/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/TacoShop.mligo deleted file mode 100644 index 410e62876b..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/TacoShop.mligo +++ /dev/null @@ -1,34 +0,0 @@ -type taco_supply = { current_stock : nat ; max_price : tez } - -type taco_shop_storage = (nat, taco_supply) map -let default_storage: taco_shop_storage = Map.literal [ - (1n, { current_stock = 50n ; max_price = 50tez }) ; - (2n, { current_stock = 20n ; max_price = 75tez }) ; -] -[@entry] -let buy_taco (taco_kind_index : nat) (taco_shop_storage : taco_shop_storage) - : operation list * taco_shop_storage = - (* Retrieve the taco_kind from the contract's storage or fail *) - - let taco_kind = - match Map.find_opt (taco_kind_index) taco_shop_storage with - Some k -> k - | None -> failwith "Unknown kind of taco" in - let current_purchase_price : tez = - taco_kind.max_price / taco_kind.current_stock in - (* We won't sell tacos if the amount is not correct *) - - let () = - if (Tezos.get_amount ()) <> current_purchase_price - then - failwith - "Sorry, the taco you are trying to purchase has a different price" in - (* Update the storage decreasing the stock by 1n *) - - let taco_shop_storage = - Map.update - taco_kind_index - (Some - {taco_kind with current_stock = abs (taco_kind.current_stock - 1n)}) - taco_shop_storage in - [], taco_shop_storage \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/b12.jsligo b/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/b12.jsligo deleted file mode 100644 index b256e366f6..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/b12.jsligo +++ /dev/null @@ -1,13 +0,0 @@ - -namespace TacoShop { - type taco_supply = { current_stock: nat, max_price: tez }; - export type taco_shop_storage = map; - - @entry - function buy_taco(taco_kind_index: nat, taco_shop_storage: taco_shop_storage): [ - list, - taco_shop_storage - ] { - return [[], taco_shop_storage] - }; -}; diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/b12.mligo b/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/b12.mligo deleted file mode 100644 index db03566180..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/b12.mligo +++ /dev/null @@ -1,14 +0,0 @@ -module TacoShop = struct - type taco_supply = - { - current_stock : nat; - max_price : tez - } - - type taco_shop_storage = (nat, taco_supply) map - - [@entry] - let buy_taco (taco_kind_index : nat) (taco_shop_storage : taco_shop_storage) : operation list * taco_shop_storage = - [], taco_shop_storage - -end \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/test.jsligo b/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/test.jsligo deleted file mode 100644 index 09a18f7902..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/test.jsligo +++ /dev/null @@ -1,102 +0,0 @@ -#import "gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/TacoShop.jsligo" "TacoShop" - -function assert_string_failure (res: test_exec_result, expected: string) { - const expected_bis = Test.eval(expected); - match(res) { - when (Fail(x)): - match(x) { - when (Rejected(y)): - assert(Test.michelson_equal(y[0], expected_bis)) - when (Balance_too_low(_)): - failwith("contract failed for an unknown reason") - when (Other(_o)): - failwith("contract failed for an unknown reason") - } - when (Success(_s)): - failwith("bad price check") - }; -} - -const test = ( - (_u: unit): unit => { - /* Originate the contract with a initial storage */ - - let init_storage = - Map.literal( - list( - [ - [1n, { current_stock: 50n, max_price: 50000000mutez }], - [2n, { current_stock: 20n, max_price: 75000000mutez }] - ] - ) - ); - const { addr , code , size } = - Test.originate(contract_of(TacoShop), init_storage, 0mutez); - - /* Test inputs */ - - const clasico_kind : parameter_of TacoShop = Buy_taco (1n); - const unknown_kind : parameter_of TacoShop = Buy_taco (3n); - /* Auxiliary function for testing equality in maps */ - - const eq_in_map = (r: TacoShop.taco_supply, m: TacoShop.taco_shop_storage, k: nat) => - match(Map.find_opt(k, m)) { - when (None): - false - when (Some(v)): - v.current_stock == r.current_stock && v.max_price == r.max_price - }; - /* Purchasing a Taco with 1tez and checking that the stock has been updated */ - - const ok_case: test_exec_result = - Test.transfer( - addr, - clasico_kind, - 1000000mutez - ); - - match(ok_case) { - when (Success(_s)): - do { - let storage = Test.get_storage(addr); - assert( - eq_in_map( - { current_stock: 49n, max_price: 50000000mutez }, - storage, - 1n - ) - && eq_in_map( - { current_stock: 20n, max_price: 75000000mutez }, - storage, - 2n - ) - ); - } - when (Fail(_e)): - failwith("ok test case failed") - }; - /* Purchasing an unregistred Taco */ - - const nok_unknown_kind = - Test.transfer( - addr, - unknown_kind, - 1000000mutez - ); - assert_string_failure(nok_unknown_kind, "Unknown kind of taco"); - /* Attempting to Purchase a Taco with 2tez */ - - const nok_wrong_price = - Test.transfer( - addr, - clasico_kind, - 2000000mutez - ); - - assert_string_failure( - nok_wrong_price, - "Sorry, the taco you are trying to purchase has a different price" - ); - return unit - } - ) (); \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/test.mligo b/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/test.mligo deleted file mode 100644 index 527641f1d8..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/test.mligo +++ /dev/null @@ -1,45 +0,0 @@ -#import "gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/TacoShop.mligo" "TacoShop" - -let assert_string_failure (res : test_exec_result) (expected : string) = - let expected = Test.eval expected in - match res with - | Fail (Rejected (actual,_)) -> assert (Test.michelson_equal actual expected) - | Fail _ -> failwith "contract failed for an unknown reason" - | Success _ -> failwith "bad price check" - -let test = - (* originate the contract with a initial storage *) - let init_storage = Map.literal [ - (1n, { current_stock = 50n ; max_price = 50tez }) ; - (2n, { current_stock = 20n ; max_price = 75tez }) ; ] - in - let { addr ; code = _; size = _ } = Test.originate (contract_of TacoShop) init_storage 0tez in - - (* Test inputs *) - let clasico_kind : TacoShop parameter_of = Buy_taco 1n in - let unknown_kind : TacoShop parameter_of = Buy_taco 3n in - - (* Auxiliary function for testing equality in maps *) - let eq_in_map (r : TacoShop.taco_supply) (m : TacoShop.taco_shop_storage) (k : nat) = - match Map.find_opt k m with - | None -> false - | Some v -> v.current_stock = r.current_stock && v.max_price = r.max_price in - - (* Purchasing a Taco with 1tez and checking that the stock has been updated *) - let ok_case : test_exec_result = Test.transfer addr clasico_kind 1tez in - let () = match ok_case with - | Success _ -> - let storage = Test.get_storage addr in - assert ((eq_in_map { current_stock = 49n ; max_price = 50tez } storage 1n) && - (eq_in_map { current_stock = 20n ; max_price = 75tez } storage 2n)) - | Fail _ -> failwith ("ok test case failed") - in - - (* Purchasing an unregistred Taco *) - let nok_unknown_kind = Test.transfer addr unknown_kind 1tez in - let () = assert_string_failure nok_unknown_kind "Unknown kind of taco" in - - (* Attempting to Purchase a Taco with 2tez *) - let nok_wrong_price = Test.transfer addr clasico_kind 2tez in - let () = assert_string_failure nok_wrong_price "Sorry, the taco you are trying to purchase has a different price" in - () \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/taco-shop/testing-contract.md b/gitlab-pages/docs/tutorials/taco-shop/testing-contract.md new file mode 100644 index 0000000000..f8fc475716 --- /dev/null +++ b/gitlab-pages/docs/tutorials/taco-shop/testing-contract.md @@ -0,0 +1,645 @@ +--- +title: "Part 2: Testing the contract" +--- + +It's critical to test contracts before you deploy them because they cannot be changed after you deploy them. +LIGO includes automated testing tools that let you test contracts and verify that they work the way you intend before you deploy them. +In this section, you add tests for the `buy_taco` entrypoint. + +## Creating tests + +You can put tests in the same file as the contract or in a different file. +For convenience, in this tutorial, you put the tests in the same file. + + +1. At the end of the contract file, outside of the namespace, add this convenience function to call the view and get the current price of a taco: + + ```jsligo skip + // Convenience function to get current taco price + const get_taco_price = (untyped_address: address, taco_kind_index: nat): tez => { + const view_result_option: option = Tezos.View.call("get_taco_price", taco_kind_index, untyped_address); + return $match(view_result_option, { + "Some": (cost_mutez) => cost_mutez, + "None": () => Test.Assert.failwith("Couldn't get the price of the taco."), + }); + } + ``` + +1. Add this convenience function to verify the current stock and maximum price of a taco: + + ```jsligo skip + // Convenience function for testing equality in maps + const eq_in_map = (r: TacoShop.taco_supply, m: TacoShop.taco_data, k: nat) => + $match(Map.find_opt(k, m), { + "None": () => false, + "Some": (v) => v.current_stock == r.current_stock && v.max_price == r.max_price + }); + ``` + + This function accepts information about a taco type and verifies that the values in the stored map match. + +1. At the end of the contract file, outside of the namespace, add this stub of a function to hold the test logic: + + ```jsligo skip + const test = (() => { + + // Test logic goes here + + }) (); + ``` + +1. Inside the test function, add code to deploy the contract in the test scenario: + + ```jsligo skip + // Set the initial storage and deploy the contract + const admin_address: address = Test.Account.address(0 as nat); + const initial_storage: TacoShop.storage = { + admin_address: admin_address, + taco_data: TacoShop.default_taco_data, + } + const contract = Test.Originate.contract(contract_of(TacoShop), initial_storage, 0 as tez); + ``` + + This code creates the `contract` object to represent the deployed (originated) contract. + This object has a few fields, but the main one the test uses is the `taddr` field, which is the address of the deployed contract. + Now you can call the deployed contract in the test scenario. + +1. Get the current price of one kind of taco by calling the `get_taco_price` function: + + ```jsligo skip + // Get the current price of a taco + const untyped_address = Test.Typed_address.to_address(contract.taddr); + const current_price = get_taco_price(untyped_address, 1 as nat); + ``` + +1. Call the `buy_taco` entrypoint with this code: + + ```jsligo skip + // Purchase a taco + const success_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("buy_taco", contract.taddr), + 1 as nat, + current_price + ); + ``` + + The `Test.Contract.transfer` function calls an entrypoint in a test scenario. + It takes these parameters: + + 1. The contract to call, here represented by the `buy_taco` entrypoint of the contract. + 1. The parameter to pass to the entrypoint, in this case `1 as nat` to represent the first type of taco. + 1. The amount of tez to send with the transaction, in this case the current price of that type of taco from the previous lines of code. + +1. Verify that the transaction completed successfully and that the number of tacos of that type decreased by 1: + + ```jsligo skip + // Verify that the stock was updated + $match(success_result, { + "Success": (_s) => (() => { + const storage = Test.Typed_address.get_storage(contract.taddr); + // Check that the stock has been updated correctly + Assert.assert( + eq_in_map( + { current_stock: 49 as nat, max_price: 50000000 as mutez }, + storage.taco_data, + 1 as nat + )); + // Check that the amount of the other taco type has not changed + Assert.assert(eq_in_map( + { current_stock: 20 as nat, max_price: 75000000 as mutez }, + storage.taco_data, + 2 as nat + ) + ); + Test.IO.log("Successfully bought a taco"); + })(), + "Fail": err => failwith(err), + }); + ``` + +1. Verify that the entrypoint fails when a client passes the wrong price: + + ```jsligo skip + // Fail to purchase a taco without sending enough tez + const fail_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("buy_taco", contract.taddr), + 1 as nat, + 1 as mutez + ); + $match(fail_result, { + "Success": _s => failwith("Test was able to buy a taco for the wrong price"), + "Fail": _err => Test.IO.log("Contract successfully blocked purchase with incorrect price"), + }); + ``` + + It's important to test failure cases as well as success cases to make sure the contract works properly in all cases. + +The completed contract file with convenience functions and test functions looks like this: + +```jsligo group=testing_contract +namespace TacoShop { + export type taco_supply = { current_stock: nat, max_price: tez }; + export type taco_data = map; + export type admin_address = address; + export type storage = { + admin_address: admin_address, + taco_data: taco_data, + }; + + export const default_taco_data: taco_data = Map.literal([ + [1 as nat, { current_stock: 50 as nat, max_price: 50 as tez }], + [2 as nat, { current_stock: 20 as nat, max_price: 75 as tez }] + ]); + + // Internal function to get the price of a taco + const get_taco_price_internal = (taco_kind_index: nat, taco_data: taco_data): tez => { + const taco_kind: taco_supply = + $match (Map.find_opt(taco_kind_index, taco_data), { + "Some": (kind) => kind, + "None": () => failwith("Unknown kind of taco"), + }); + return taco_kind.max_price / taco_kind.current_stock; + } + + // @view + const get_taco_price = (taco_kind_index: nat, storage: storage): tez => + get_taco_price_internal(taco_kind_index, storage.taco_data); + + // Buy a taco + // @entry + const buy_taco = (taco_kind_index: nat, storage: storage): [ + list, + storage + ] => { + + const { admin_address, taco_data } = storage; + + // Retrieve the kind of taco from the contracts storage or fail + const taco_kind: taco_supply = + $match (Map.find_opt(taco_kind_index, taco_data), { + "Some": (kind) => kind, + "None": () => failwith("Unknown kind of taco"), + }); + + // Get the current price of this type of taco + const current_purchase_price = get_taco_price_internal(taco_kind_index, taco_data); + + // Verify that the caller sent the correct amount of tez + if ((Tezos.get_amount()) != current_purchase_price) { + return failwith("Sorry, the taco you are trying to purchase has a different price"); + } + + // Verify that there is at least one of this type of taco + if (taco_kind.current_stock == (0 as nat)) { + return failwith("Sorry, we are out of this type of taco"); + } + + // Update the storage with the new quantity of tacos + const updated_taco_data: taco_data = Map.update( + taco_kind_index, + ["Some" as "Some", {...taco_kind, current_stock: abs(taco_kind.current_stock - 1) }], + taco_data); + + const updated_storage: storage = { + admin_address: admin_address, + taco_data: updated_taco_data, + }; + + return [[], updated_storage]; + } + + // @entry + const payout = (_u: unit, storage: storage): [ + list, + storage + ] => { + + // Entrypoint logic goes here + + return [[], storage]; + } + +}; + +// Convenience function to get current taco price +const get_taco_price = (untyped_address: address, taco_kind_index: nat): tez => { + const view_result_option: option = Tezos.View.call("get_taco_price", taco_kind_index, untyped_address); + return $match(view_result_option, { + "Some": cost_mutez => cost_mutez, + "None": () => Test.Assert.failwith("Couldn't get the price of the taco."), + }); +} + +// Convenience function for testing equality in maps +const eq_in_map = (r: TacoShop.taco_supply, m: TacoShop.taco_data, k: nat) => + $match(Map.find_opt(k, m), { + "None": () => false, + "Some": (v) => v.current_stock == r.current_stock && v.max_price == r.max_price + }); + +const test = (() => { + + // Set the initial storage and deploy the contract + const admin_address: address = Test.Account.address(0 as nat); + const initial_storage: TacoShop.storage = { + admin_address: admin_address, + taco_data: TacoShop.default_taco_data, + } + const contract = Test.Originate.contract(contract_of(TacoShop), initial_storage, 0 as tez); + + // Get the current price of a taco + const untyped_address = Test.Typed_address.to_address(contract.taddr); + const current_price = get_taco_price(untyped_address, 1 as nat); + + // Purchase a taco + const success_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("buy_taco", contract.taddr), + 1 as nat, + current_price + ); + + // Verify that the stock was updated + $match(success_result, { + "Success": (_s) => (() => { + const storage = Test.Typed_address.get_storage(contract.taddr); + // Check that the stock has been updated correctly + Assert.assert( + eq_in_map( + { current_stock: 49 as nat, max_price: 50000000 as mutez }, + storage.taco_data, + 1 as nat + )); + // Check that the amount of the other taco type has not changed + Assert.assert(eq_in_map( + { current_stock: 20 as nat, max_price: 75000000 as mutez }, + storage.taco_data, + 2 as nat + ) + ); + Test.IO.log("Successfully bought a taco"); + })(), + "Fail": err => failwith(err), + }); + + // Fail to purchase a taco without sending enough tez + const fail_result = + Test.Contract.transfer( + Test.Typed_address.get_entrypoint("buy_taco", contract.taddr), + 1 as nat, + 1 as mutez + ); + $match(fail_result, { + "Success": _s => failwith("Test was able to buy a taco for the wrong price"), + "Fail": _err => Test.IO.log("Contract successfully blocked purchase with incorrect price"), + }); +}) (); +``` + + + + +1. At the end of the contract file, outside of the module, add this convenience function to call the view and get the current price of a taco: + + ```cameligo skip + (* Convenience function to get current taco price *) + let get_taco_price (untyped_address : address) (taco_kind_index : nat) : tez = + let view_result_option : tez option = Tezos.View.call + "get_taco_price" + taco_kind_index + untyped_address in + match view_result_option with + | Some cost_mutez -> cost_mutez + | None -> Test.Assert.failwith "Couldn't get the price of a taco" + ``` + +1. Add this convenience function to verify the current stock and maximum price of a taco: + + ```cameligo skip + (* Convenience function for testing equality in maps *) + let eq_in_map (r : TacoShop.taco_supply) (m : TacoShop.taco_data) (k : nat) = + match Map.find_opt k m with + | None -> false + | Some v -> v.current_stock = r.current_stock && v.max_price = r.max_price + ``` + + This function accepts information about a taco type and verifies that the values in the stored map match. + +1. At the end of the contract file, outside of the namespace, create a function to hold the test logic: + + ```cameligo skip + let test = + ``` + +1. Inside the test function, add code to deploy the contract in the test scenario: + + ```cameligo skip + (* Set the initial storage and deploy the contract *) + let admin_address : address = Test.Account.address 0n in + let initial_storage : TacoShop.storage = { + admin_address = admin_address; + taco_data = TacoShop.default_taco_data + } in + let contract = Test.Originate.contract (contract_of TacoShop) initial_storage 0tez in + ``` + + This code creates the `contract` object to represent the deployed (originated) contract. + This object has a few fields, but the main one the test uses is the `taddr` field, which is the address of the deployed contract. + Now you can call the deployed contract in the test scenario. + +1. Get the current price of one kind of taco by calling the `get_taco_price` function: + + ```cameligo skip + (* Get the current price of a taco *) + let untyped_address = Test.Typed_address.to_address contract.taddr in + let current_price = get_taco_price untyped_address 1n in + ``` + +1. Call the `buy_taco` entrypoint with this code: + + ```cameligo skip + (* Purchase a taco *) + let success_result = + Test.Contract.transfer + (Test.Typed_address.get_entrypoint "buy_taco" contract.taddr) + 1n + current_price + in + ``` + + The `Test.Contract.transfer` function calls an entrypoint in a test scenario. + It takes these parameters: + + 1. The contract to call, here represented by the `buy_taco` entrypoint of the contract. + 1. The parameter to pass to the entrypoint, in this case `1n` to represent the first type of taco. + 1. The amount of tez to send with the transaction, in this case the current price of that type of taco from the previous lines of code. + +1. Verify that the transaction completed successfully and that the number of tacos of that type decreased by 1: + + ```cameligo skip + (* Verify that the stock was updated *) + let () = match success_result with + | Success _s -> + let storage = Test.Typed_address.get_storage contract.taddr in + let () = Assert.assert (eq_in_map + { current_stock = 49n; max_price = 50000000mutez } + storage.taco_data + 1n + ) in + let () = Assert.assert (eq_in_map + { current_stock = 20n; max_price = 75000000mutez } + storage.taco_data + 2n + ) in + Test.IO.log "Successfully bought a taco" + | Fail err -> failwith err + in + ``` + +1. Verify that the entrypoint fails when a client passes the wrong price: + + ```cameligo skip + (* Fail to purchase a taco without sending enough tez *) + let fail_result = Test.Contract.transfer + (Test.Typed_address.get_entrypoint "buy_taco" contract.taddr) + 1n + 1mutez in + match fail_result with + | Success _s -> failwith "Test was able to buy a taco for the wrong price" + | Fail _err -> Test.IO.log "Contract successfully blocked purchase with incorrect price" + ``` + + It's important to test failure cases as well as success cases to make sure the contract works properly in all cases. + +The completed contract file with the convenience functions and test functions looks like this: + +```cameligo group=testing_contract +module TacoShop = struct + type taco_supply = { current_stock: nat; max_price: tez } + type taco_data = (nat, taco_supply) map + type admin_address = address + type storage = { + admin_address: admin_address; + taco_data: taco_data; + } + + let default_taco_data: taco_data = Map.literal [ + (1n, { current_stock = 50n; max_price = 50tez }); + (2n, { current_stock = 20n; max_price = 75tez }); + ] + + (* Internal function to get the price of a taco *) + let get_taco_price_internal (taco_kind_index : nat) (taco_data : taco_data) : tez = + let taco_kind : taco_supply = + match Map.find_opt taco_kind_index taco_data with + | Some kind -> kind + | None -> failwith "Unknown kind of taco" + in + taco_kind.max_price / taco_kind.current_stock + + [@view] + let get_taco_price (taco_kind_index : nat) (storage : storage) : tez = + get_taco_price_internal taco_kind_index storage.taco_data + + (* Buy a taco *) + [@entry] + let buy_taco (taco_kind_index : nat) (storage : storage) : operation list * storage = + + let { admin_address; taco_data } = storage in + + (* Retrieve the kind of taco from the contracts storage or fail *) + let taco_kind : taco_supply = + match Map.find_opt taco_kind_index taco_data with + | Some kind -> kind + | None -> failwith "Unknown kind of taco" in + + (* Get the current price of this type of taco *) + let current_purchase_price = get_taco_price_internal taco_kind_index taco_data in + + (* Verify that the caller sent the correct amount of tez *) + let _ = if (Tezos.get_amount () <> current_purchase_price) then + failwith "Sorry, the taco you are trying to purchase has a different price" in + + (* Verify that there is at least one of this type of taco *) + let _ = if (taco_kind.current_stock = 0n) then + failwith "Sorry, we are out of this type of taco" in + + + (* Update the storage with the new quantity of tacos *) + let updated_taco_data : taco_data = Map.update + taco_kind_index + (Some { taco_kind with current_stock = abs (taco_kind.current_stock - 1n) }) + taco_data in + + + let updated_storage : storage = { + admin_address = admin_address; + taco_data = updated_taco_data; + } in + + [], updated_storage + + [@entry] + let payout (_u : unit) (storage : storage) : operation list * storage = + + (* Entrypoint logic goes here *) + + [], storage + + end + +(* Convenience function to get current taco price *) +let get_taco_price (untyped_address : address) (taco_kind_index : nat) : tez = + let view_result_option : tez option = Tezos.View.call + "get_taco_price" + taco_kind_index + untyped_address in + match view_result_option with + | Some cost_mutez -> cost_mutez + | None -> Test.Assert.failwith "Couldn't get the price of a taco" + +(* Convenience function for testing equality in maps *) +let eq_in_map (r : TacoShop.taco_supply) (m : TacoShop.taco_data) (k : nat) = + match Map.find_opt k m with + | None -> false + | Some v -> v.current_stock = r.current_stock && v.max_price = r.max_price + +let test = + + (* Set the initial storage and deploy the contract *) + let admin_address : address = Test.Account.address 0n in + let initial_storage : TacoShop.storage = { + admin_address = admin_address; + taco_data = TacoShop.default_taco_data + } in + let contract = Test.Originate.contract (contract_of TacoShop) initial_storage 0tez in + + (* Get the current price of a taco *) + let untyped_address = Test.Typed_address.to_address contract.taddr in + let current_price = get_taco_price untyped_address 1n in + + (* Purchase a taco *) + let success_result = + Test.Contract.transfer + (Test.Typed_address.get_entrypoint "buy_taco" contract.taddr) + 1n + current_price + in + + (* Verify that the stock was updated *) + let () = match success_result with + | Success _s -> + let storage = Test.Typed_address.get_storage contract.taddr in + let () = Assert.assert (eq_in_map + { current_stock = 49n; max_price = 50000000mutez } + storage.taco_data + 1n + ) in + let () = Assert.assert (eq_in_map + { current_stock = 20n; max_price = 75000000mutez } + storage.taco_data + 2n + ) in + Test.IO.log "Successfully bought a taco" + | Fail err -> failwith err + in + + (* Fail to purchase a taco without sending enough tez *) + let fail_result = Test.Contract.transfer + (Test.Typed_address.get_entrypoint "buy_taco" contract.taddr) + 1n + 1mutez in + match fail_result with + | Success _s -> failwith "Test was able to buy a taco for the wrong price" + | Fail _err -> Test.IO.log "Contract successfully blocked purchase with incorrect price" +``` + + + +## Running tests + +LIGO tests do not run automatically when you run the `ligo compile contract` command; you must run them with the `ligo run test` command. + +Run the tests in the contract file by running this command: + + + +```bash +ligo run test taco_shop.jsligo +``` + + + + + +```bash +ligo run test taco_shop.mligo +``` + + + +The console response prints the messages from the calls to `Test.IO.log` and a message that the test function completed: + +``` +"Successfully bought a taco" +"Contract successfully blocked purchase with incorrect price" +Everything at the top-level was executed. +- test exited with value (). +``` + +If you want to expand the tests for your contract, you can add more test functions or more test code to the existing function. +For example, you can try buying the other kind of taco or buying more of the first kind of taco and verifying that the stock and price changes as expected. + +## Testing with dry-run + +Another way to test contracts is with the `ligo run dry-run` command. +This command runs the contract in a simulated environment with parameters that you provide on the command line. +You pass these arguments to the command: + +- The contract file to run +- The amount of tez to pass with the transaction +- The parameter to pass to the contract, as a LIGO expression +- The value of the contract storage, as a LIGO expression + +For example, you can test the `buy_taco` entrypoint with this command: + + + +```bash +ligo run dry-run taco_shop.jsligo -m TacoShop --amount 1 '["Buy_taco" as "Buy_taco", 1 as nat]' \ + '{admin_address: "tz1QCVQinE8iVj1H2fckqx6oiM85CNJSK9Sx" as address, taco_data: TacoShop.default_taco_data}' +``` + +The entrypoint and parameter in this command are formatted as a variant type. +When the contract is compiled to Michelson, its parameter is a variant that has cases for each entrypoint, so you must pass the variant that corresponds to the entrypoint. +For the purposes of the `ligo run dry-run` command, the variant type is the name of the entrypoint with the first letter in upper case. +Note also that you can use variables from the contract (as in `TacoShop.default_taco_data`) in the command because the contract parameter and storage value are LIGO expressions. + + + + + +```bash +ligo run dry-run taco_shop.mligo -m TacoShop --amount 1 "Buy_taco 1n" \ + '{admin_address = "tz1QCVQinE8iVj1H2fckqx6oiM85CNJSK9Sx" ; taco_data = TacoShop.default_taco_data}' +``` + +Note that the entrypoint name starts with a capital letter when you use it in a dry run. +Note also that you can use variables from the contract (as in `TacoShop.default_taco_data`) in the command because the contract parameter and storage value are LIGO expressions. + + + +The address in the dry run command isn't stored beyond this run of the command; you just need to provide any address for the amin address in storage. +However, you must use the `as address` declaration to specify that the string is a LIGO `address` type; without the type declaration, LIGO would assume that it was a string. + +The output of the command is the return value of the entrypoint that you called. +In this case, it is an empty list of operations (`LIST_EMPTY()`) and the code for the new state of the storage. + +In this way, you can test different storage states and entrypoints from the command line. +Sometimes testing with a dry run can be more convenient than writing tests; it's up to you how to test the contract. + +Now you know that the customer interface of the contract works. +In the next section, you implement the `payout` entrypoint to retrieve the profits. +Continue to [Part 3: Getting the payouts](./getting-payouts). diff --git a/gitlab-pages/docs/tutorials/taco-shop/tezos-taco-shop-payout.md b/gitlab-pages/docs/tutorials/taco-shop/tezos-taco-shop-payout.md deleted file mode 100644 index 221633c2fb..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/tezos-taco-shop-payout.md +++ /dev/null @@ -1,510 +0,0 @@ ---- -id: tezos-taco-shop-payout -title: Paying out profits from the Taco Shop ---- - -import Syntax from '@theme/Syntax'; - -In the -[previous tutorial](tezos-taco-shop-smart-contract.md) -we have learnt how to setup & interact with the LIGO CLI. Followed an -implementation of a simple Taco Shop smart contract for our -entrepreneur Pedro. - -In this tutorial we will make sure Pedro has access to tokens that -people have spent at his shop when buying tacos. - -
    - - -
    -
    Icons made by Smashicons from www.flaticon.com is licensed by CC 3.0 BY
    -
    - - -## Analysing the Current Contract - - - -### **`taco-shop.mligo`** - -```cameligo group=a -module TacoShop = struct - type taco_supply = - { - current_stock : nat; - max_price : tez - } - - type taco_shop_storage = (nat, taco_supply) map - - [@entry] - let buy_taco (taco_kind_index : nat) (taco_shop_storage : taco_shop_storage) - : operation list * taco_shop_storage = - (* Retrieve the taco_kind from the contract's storage or fail *) - - let taco_kind = - match Map.find_opt (taco_kind_index) taco_shop_storage with - Some k -> k - | None -> failwith "Unknown kind of taco" in - let current_purchase_price : tez = - taco_kind.max_price / taco_kind.current_stock in - (* We won't sell tacos if the amount is not correct *) - - let () = - if (Tezos.get_amount ()) <> current_purchase_price - then - failwith - "Sorry, the taco you are trying to purchase has a different price" in - (* Update the storage decreasing the stock by 1n *) - - let taco_shop_storage = - Map.update - taco_kind_index - (Some - {taco_kind with current_stock = abs (taco_kind.current_stock - 1n)}) - taco_shop_storage in - [], taco_shop_storage - -end - -let default_storage : TacoShop.taco_shop_storage = - Map.literal - [ - (1n, - { - current_stock = 50n; - max_price = 50000000mutez - }); - (2n, - { - current_stock = 20n; - max_price = 75000000mutez - }) - ] -``` - - - - - -### **`taco-shop.jsligo`** - -```jsligo group=a -namespace TacoShop { - export type taco_supply = { current_stock: nat, max_price: tez }; - export type taco_shop_storage = map; - - @entry - function buy_taco(taco_kind_index: nat, taco_shop_storage: taco_shop_storage): [ - list, - taco_shop_storage - ] { - /* Retrieve the taco_kind from the contracts storage or fail */ - const taco_kind : taco_supply = - match (Map.find_opt (taco_kind_index, taco_shop_storage)) { - when(Some(kind)): kind; - when(None()): failwith ("Unknown kind of taco") - }; - const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock ; - /* We won't sell tacos if the amount is not correct */ - if ((Tezos.get_amount ()) != current_purchase_price) { - return failwith ("Sorry, the taco you are trying to purchase has a different price") - } else { - /* Update the storage decreasing the stock by 1n */ - const taco_shop_storage = Map.update ( - taco_kind_index, - (Some ({...taco_kind, current_stock : abs (taco_kind.current_stock - 1n) })), - taco_shop_storage ); - return [[], taco_shop_storage] - } - }; -}; - -const default_storage: TacoShop.taco_shop_storage = - Map.literal( - list( - [ - [1n, { current_stock: 50n, max_price: 50000000mutez }], - [2n, { current_stock: 20n, max_price: 75000000mutez }] - ] - ) - ); - -``` - - - -### Purchase Price Formula - -Pedro's Taco Shop contract currently enables customers to buy tacos, -at a price based on a simple formula. - - - -```cameligo skip -let current_purchase_price : tez = - taco_kind.max_price / taco_kind.current_stock -``` - - - - - -```jsligo skip -const current_purchase_price : tez = - taco_kind.max_price / taco_kind.current_stock -``` - - - ---- - -## Designing a Payout Scheme - -Pedro is a standalone business owner, and in our case, he does not -have to split profits and earnings of the taco shop with anyone. So -for the sake of simplicity, we will payout all the earned XTZ directly -to Pedro right after a successful purchase. - -This means that after all the *purchase conditions* of our contract -are met, e.g., the correct amount is sent to the contract, we will not -only decrease the supply of the individual purchased *taco kind*, but -we will also transfer this amount in a *subsequent transaction* to -Pedro's personal address. - -## Forging a Payout Transaction - -### 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 will wrap -the given address as a *`contract (unit)`*, which represents either a -contract with no parameters, or an implicit account. - - - -```cameligo group=ex1 -let ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" -let receiver : unit contract = - match (Tezos.get_contract_opt ownerAddress : unit contract option) with - Some (contract) -> contract - | None -> (failwith "Not a contract" : unit contract) -``` - - - - - -```jsligo group=ex1 -const ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" -const receiver : contract = - match (Tezos.get_contract_opt(ownerAddress) as option>) { - when(Some(contract)): contract; - when(None()): (failwith ("Not a contract") as contract) - } -``` - - - -> Would you like to learn more about addresses, contracts and -> operations in LIGO? Check out the -> [LIGO cheat sheet](../../api/cheat-sheet.md) - -### Adding the Transaction to the List of Output Operations - -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. - - - -```cameligo group=ex1 -let payoutOperation : operation = Tezos.transaction () (Tezos.get_amount ()) receiver -let operations : operation list = [payoutOperation] -``` - - - - - -```jsligo group=ex1 -const payoutOperation : operation = Tezos.transaction (unit, Tezos.get_amount (), receiver) ; -const operations : list = [payoutOperation]; -``` - - - ---- - -## Finalising the Contract - - - -### **`taco-shop.mligo`** - -```cameligo group=b -module TacoShop = struct - type taco_supply = - { - current_stock : nat; - max_price : tez - } - - type taco_shop_storage = (nat, taco_supply) map - - let ownerAddress : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address) - - [@entry] - let buy_taco (taco_kind_index : nat) (taco_shop_storage : taco_shop_storage) - : operation list * taco_shop_storage = - (* Retrieve the taco_kind from the contract's storage or fail *) - - let taco_kind = - match Map.find_opt (taco_kind_index) taco_shop_storage with - Some k -> k - | None -> failwith "Unknown kind of taco" in - let current_purchase_price : tez = - taco_kind.max_price / taco_kind.current_stock in - (* We won't sell tacos if the amount is not correct *) - - let () = - if (Tezos.get_amount ()) <> current_purchase_price - then - failwith - "Sorry, the taco you are trying to purchase has a different price" in - (* Update the storage decreasing the stock by 1n *) - - let taco_shop_storage = - Map.update - taco_kind_index - (Some - {taco_kind with current_stock = abs (taco_kind.current_stock - 1n)}) - taco_shop_storage in - - let receiver : unit contract = - match (Tezos.get_contract_opt ownerAddress : unit contract option) with - Some (contract) -> contract - | None -> (failwith "Not a contract" : unit contract) in - - let payoutOperation : operation = Tezos.transaction () (Tezos.get_amount ()) receiver in - let operations : operation list = [payoutOperation] in - - operations, taco_shop_storage -end - -let default_storage : TacoShop.taco_shop_storage = - Map.literal - [ - (1n, - { - current_stock = 50n; - max_price = 50000000mutez - }); - (2n, - { - current_stock = 20n; - max_price = 75000000mutez - }) - ] -``` - - - - - -### **`taco-shop.jsligo`** - -```jsligo group=b -namespace TacoShop { - export type taco_supply = { current_stock: nat, max_price: tez }; - export type taco_shop_storage = map; - - const ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV"; - - const donationAddress : address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; - - @entry - function buy_taco(taco_kind_index: nat, taco_shop_storage: taco_shop_storage): [ - list, - taco_shop_storage - ] { - /* Retrieve the taco_kind from the contracts storage or fail */ - - const taco_kind: taco_supply = - match(Map.find_opt(taco_kind_index, taco_shop_storage)) { - when (Some(kind)): - kind - when (None): - failwith("Unknown kind of taco") - }; - const current_purchase_price: tez = - taco_kind.max_price / taco_kind.current_stock; - /* We won't sell tacos if the amount is not correct */ - - if ((Tezos.get_amount()) != current_purchase_price) { - return failwith( - "Sorry, the taco you are trying to purchase has a different price" - ) - } else { - /* Update the storage decreasing the stock by 1n */ - - const taco_shop_storage = - Map.update( - taco_kind_index, - ( - Some({ - ...taco_kind, current_stock: abs(taco_kind.current_stock - 1n) - }) - ), - taco_shop_storage - ); - - const receiver : contract = - match ((Tezos.get_contract_opt (ownerAddress))) { - when(Some(contract)): contract; - when(None()): ((failwith ("Not a contract"))) - }; - - const donationReceiver : contract = - match ((Tezos.get_contract_opt (donationAddress))) { - when(Some(contract)): contract; - when(None()): ((failwith ("Not a contract"))) - }; - - const donationAmount = ((Tezos.get_amount ()) / 10n) as tez; - - // Pedro will get 90% of the amount - const op1 = match ((Tezos.get_amount ()) - donationAmount) { - when(Some(x)): Tezos.transaction (unit, x, receiver); - when(None()): failwith ("Insufficient balance") - }; - const op2 = Tezos.transaction (unit, donationAmount, donationReceiver); - const operations : list = [ op1 , op2 ]; - - return [operations, taco_shop_storage] - } - }; -} -const default_storage: TacoShop.taco_shop_storage = - Map.literal( - list( - [ - [1n, { current_stock: 50n, max_price: 50000000mutez }], - [2n, { current_stock: 20n, max_price: 75000000mutez }] - ] - ) - ); - -``` - - - -### Dry-run the Contract - -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. - - - -```cameligo skip -ligo run dry-run taco-shop.mligo --syntax cameligo --amount 1 --entry-point buy_taco 1n "Map.literal [ - (1n, { current_stock = 50n; max_price = 50tez }) ; - (2n, { current_stock = 20n; max_price = 75tez }) ; -]" -``` - - - - - -```jsligo skip -ligo run dry-run taco-shop.jsligo --syntax jsligo -m TacoShop --amount 1 --entry-point buy_taco '1n' "default_storage" -``` - - - - -
    -Operation(...bytes) included in the output -
    - -
    - -**Done! Our tokens are no longer locked in the contract, and instead - they are sent to Pedro's personal account/wallet.** - ---- - -## 👼 Bonus: Donating Part of the Profits - -Because Pedro is a member of the Speciality Taco Association (STA), he -has decided to donate **10%** of the earnings to the STA. We will just -add a `donationAddress` to the contract, and compute a 10% donation -sum from each taco purchase. - - - -```cameligo group=bonus -let ownerAddress : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address) -let donationAddress : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) - -let receiver : unit contract = - match ((Tezos.get_contract_opt ownerAddress) : unit contract option) with - Some contract -> contract - | None -> ((failwith "Not a contract") : unit contract) - -let donationReceiver : unit contract = - match ((Tezos.get_contract_opt donationAddress) : unit contract option) with - Some contract -> contract - | None -> ((failwith "Not a contract") : unit contract) - -let donationAmount : tez = (Tezos.get_amount ()) / 10n - -let operations : operation list = - // Pedro will get 90% of the amount - let op = match ((Tezos.get_amount ()) - donationAmount) with - | Some x -> Tezos.transaction () x receiver - | None -> (failwith "Insufficient balance") - in - [ op ; Tezos.transaction () donationAmount donationReceiver ] -``` - - - - - -```jsligo group=bonus -const ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV"; -const donationAddress : address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; - -const receiver : contract = - match ((Tezos.get_contract_opt (ownerAddress)) as option>) { - when(Some(contract)): contract; - when(None()): ((failwith ("Not a contract")) as contract) - }; - -const donationReceiver : contract = - match ((Tezos.get_contract_opt (donationAddress)) as option>) { - when(Some(contract)): contract; - when(None()): ((failwith ("Not a contract")) as contract) - }; - -const donationAmount = ((Tezos.get_amount ()) / 10n) as tez; - -// Pedro will get 90% of the amount -const op1 = match ((Tezos.get_amount ()) - donationAmount) { - when(Some(x)): Tezos.transaction (unit, x, receiver); - when(None()): failwith ("Insufficient balance") -}; -const op2 = Tezos.transaction (unit, donationAmount, donationReceiver); -const operations : list = [ op1 , op2 ]; -``` - - - -This will result into two operations being subsequently executed on the blockchain: -- Donation transfer (10%) -- Pedro's profits (90%) diff --git a/gitlab-pages/docs/tutorials/taco-shop/tezos-taco-shop-smart-contract.md b/gitlab-pages/docs/tutorials/taco-shop/tezos-taco-shop-smart-contract.md deleted file mode 100644 index 7c5bf80eeb..0000000000 --- a/gitlab-pages/docs/tutorials/taco-shop/tezos-taco-shop-smart-contract.md +++ /dev/null @@ -1,659 +0,0 @@ ---- -id: tezos-taco-shop-smart-contract -title: The Taco Shop Smart Contract ---- - -import Syntax from '@theme/Syntax'; -import Link from '@docusaurus/Link'; - -
    - -Meet **Pedro**, our *artisan taco chef*, who has decided to open a -Taco shop on the Tezos blockchain, using a smart contract. He sells -two different kinds of tacos: **el Clásico** and the **Especial -del Chef**. - -To help Pedro open his dream taco shop, we will implement a smart -contract that will manage supply, pricing & sales of his tacos to the -consumers. - -
    - -
    Made by Smashicons from www.flaticon.com is licensed by CC 3.0 BY
    -
    - ---- - -## Pricing - -Pedro's tacos are a rare delicacy, so their **price goes up** as the -**stock for the day begins to deplete**. - -Each taco kind, has its own `max_price` that it sells for, and a -finite supply for the current sales life-cycle. - -> For the sake of simplicity, we will not implement the replenishing -> of the supply after it has run out. - -### Daily Offer - -|**kind** |id |**available_stock**| **max_price**| -|---|---|---|---| -|Clásico | `1n` | `50n` | `50tez` | -|Especial del Chef | `2n` | `20n` | `75tez` | - -### Calculating the Current Purchase Price - -The current purchase price is calculated with the following formula: - -```cameligo skip -current_purchase_price = max_price / available_stock -``` - -#### El Clásico -|**available_stock**|**max_price**|**current_purchase_price**| -|---|---|---| -| `50n` | `50tez` | `1tez`| -| `20n` | `50tez` | `2.5tez` | -| `5n` | `50tez` | `10tez` | - -#### Especial del chef -|**available_stock**|**max_price**|**current_purchase_price**| -|---|---|---| -| `20n` | `75tez` | `3.75tez` | -| `10n` | `75tez` | `7.5tez`| -| `5n` | `75tez` | `15tez` | - ---- -## Draft a first contract -### Designing the Taco Shop's Contract Storage - -First think to do when you create a smart contract is -think about what gonna be stored onto it. -We know that Pedro's Taco Shop serves two kinds of tacos, so we will -need to manage stock individually, per kind. Let us define a type, -that will keep the `stock` & `max_price` per kind in a record with two -fields. Additionally, we will want to combine our `taco_supply` type -into a map, consisting of the entire offer of Pedro's shop. - -**Taco shop's storage** - - - -```cameligo group=TacoShop -type taco_supply = { current_stock : nat ; max_price : tez } - -type taco_shop_storage = (nat, taco_supply) map -``` - - - - - -```jsligo group=TacoShop -export type taco_supply = { current_stock : nat , max_price : tez }; - -export type taco_shop_storage = map ; -``` - - - -Now that the storage is defined, let's interact with it. - -### Selling the Tacos for Free - -Create your first entrypoint `buy_taco` which is doing nothing for now : - - - -```cameligo skip -[@entry] -let buy_taco (taco_kind_index : nat) (taco_shop_storage : taco_shop_storage) : operation list * taco_shop_storage = [], taco_shop_storage - -``` - - - - - -```jsligo skip -@entry -function buy_taco(taco_kind_index: nat, taco_shop_storage: taco_shop_storage): [ - list, - taco_shop_storage -] { - return [[], taco_shop_storage] - }; -``` - - - -It's already possible to compile your contract by running : - - - -``` -ligo compile contract taco_shop.jsligo -``` - - - - - -``` -ligo compile contract taco_shop.mligo -``` - - - -> To avoid warning at compilation, change `taco_kind_index` into `_taco_kind_index`, it'll tell to the compiler that this variable is authorized to not be used. - - -A good practice is to scope your contract into a [module](../../language-basics/modules). - - - -```cameligo group=b12 -module TacoShop = struct - type taco_supply = - { - current_stock : nat; - max_price : tez - } - - type taco_shop_storage = (nat, taco_supply) map - - [@entry] - let buy_taco (taco_kind_index : nat) (taco_shop_storage : taco_shop_storage) : operation list * taco_shop_storage = - [], taco_shop_storage - -end -``` - - - - - -```jsligo group=b12 - -namespace TacoShop { - type taco_supply = { current_stock: nat, max_price: tez }; - export type taco_shop_storage = map; - - @entry - function buy_taco(taco_kind_index: nat, taco_shop_storage: taco_shop_storage): [ - list, - taco_shop_storage - ] { - return [[], taco_shop_storage] - }; -}; - -``` - -> We export `taco_shop_storage` to be accessible outside the module/namespace on the next section. - - - -There is an impact onto the compilation, now you have to tell to the compiler which [module](../../language-basics/modules) it need to compile : - -``` -ligo compile contract taco_shop.mligo -m TacoShop -``` - -### Populating our Storage - -When deploying contract, it is crucial to provide a correct -initial storage value. In our case the storage is type-checked as -`taco_shop_storage`, because the default storage is not directly used in the code, -we encourage to declare the type, if your storage mutate, your default_storage will be in error. -Reflecting [Pedro's daily offer](tezos-taco-shop-smart-contract.md#daily-offer), -our storage's value will be defined as follows: - - - -```cameligo group=TacoShop -let default_storage: taco_shop_storage = Map.literal [ - (1n, { current_stock = 50n ; max_price = 50tez }) ; - (2n, { current_stock = 20n ; max_price = 75tez }) ; -] -``` - - - - - -```jsligo group=TacoShop -const default_storage: taco_shop_storage = Map.literal ([ - [1n, { current_stock : 50n, max_price : 50tez }], - [2n, { current_stock : 20n, max_price : 75tez }] -]); -``` - - - -> The storage value is a map with two bindings (entries) distinguished -> by their keys `1n` and `2n`. - -Out of curiosity, let's try to use LIGO `compile storage` command compile this value down to Michelson. - - - -```zsh -ligo compile storage TacoShop.jsligo default_storage -m TacoShop -# Output: -# -# { Elt 1 (Pair 50 50000000) ; Elt 2 (Pair 20 75000000) } -``` - - - - - -```zsh -ligo compile storage TacoShop.jsligo default_storage -m TacoShop -# Output: -# -# { Elt 1 (Pair 50 50000000) ; Elt 2 (Pair 20 75000000) } -``` - - - -Our initial storage record is compiled to a Michelson map `{ Elt 1 (Pair 50 50000000) ; Elt 2 (Pair 20 75000000) }` -holding the `current_stock` and `max_prize` in as a pair. - ---- -## Implement some logic - -### Decreasing `current_stock` when a Taco is Sold - -In order to decrease the stock in our contract's storage for a -specific taco kind, a few things needs to happen: - -- retrieve the `taco_kind` from our storage, based on the - `taco_kind_index` provided; -- subtract the `taco_kind.current_stock` by `1n`; -- we can find the absolute value of the subtraction above by - calling `abs` (otherwise we would be left with an `int`); -- update the storage, and return it. - - - -```cameligo skip -[@entry] -let buy_taco (taco_kind_index : nat) (taco_shop_storage : taco_shop_storage) : operation list * taco_shop_storage = - (* Retrieve the taco_kind from the contract's storage or fail *) - let taco_kind = - match Map.find_opt (taco_kind_index) taco_shop_storage with - | Some k -> k - | None -> failwith "Unknown kind of taco" - in - (* Update the storage decreasing the stock by 1n *) - let taco_shop_storage = Map.update - taco_kind_index - (Some { taco_kind with current_stock = abs (taco_kind.current_stock - 1n) }) - taco_shop_storage - in - [], taco_shop_storage -``` - - - - - -```jsligo skip -@entry -function buy_taco(taco_kind_index: nat, taco_shop_storage: taco_shop_storage): [ - list, - taco_shop_storage -] { - - /* Retrieve the taco_kind from the contracts storage or fail */ - const taco_kind: taco_supply = - match (Map.find_opt (taco_kind_index, taco_shop_storage)) { - when(Some(kind)): kind; - when(None()): (failwith ("Unknown kind of taco")) - }; - - // Update the storage decreasing the stock by 1n - const taco_shop_storage_updated = Map.update ( - taco_kind_index, - (Some (({...taco_kind, current_stock : abs (taco_kind.current_stock - (1n)) }))), - taco_shop_storage ); - return [[], taco_shop_storage_updated] -}; -``` - - - -### Making Sure We Get Paid for Our Tacos - -In order to make Pedro's taco shop profitable, he needs to stop giving -away tacos for free. When a contract is invoked via a transaction, an -amount of tezzies to be sent can be specified as well. This amount is -accessible within LIGO as `Tezos.get_amount`. - -To make sure we get paid, we will: - -- calculate a `current_purchase_price` based on the - [equation specified earlier](tezos-taco-shop-smart-contract.md#calculating-the-current-purchase-price) -- check if the sent amount matches the `current_purchase_price`: - - if not, then our contract will fail (`failwith`) - - otherwise, stock for the given `taco_kind` will be decreased and - the payment accepted - - - -```cameligo group=TacoShop -[@entry] -let buy_taco (taco_kind_index : nat) (taco_shop_storage : taco_shop_storage) - : operation list * taco_shop_storage = - (* Retrieve the taco_kind from the contract's storage or fail *) - - let taco_kind = - match Map.find_opt (taco_kind_index) taco_shop_storage with - Some k -> k - | None -> failwith "Unknown kind of taco" in - let current_purchase_price : tez = - taco_kind.max_price / taco_kind.current_stock in - (* We won't sell tacos if the amount is not correct *) - - let () = - if (Tezos.get_amount ()) <> current_purchase_price - then - failwith - "Sorry, the taco you are trying to purchase has a different price" in - (* Update the storage decreasing the stock by 1n *) - - let taco_shop_storage = - Map.update - taco_kind_index - (Some - {taco_kind with current_stock = abs (taco_kind.current_stock - 1n)}) - taco_shop_storage in - [], taco_shop_storage -``` - - - - - -```jsligo group=TacoShop -@entry -const buy_taco = (taco_kind_index: nat, taco_shop_storage: taco_shop_storage) : [ - list, - taco_shop_storage - ] => { - /* Retrieve the taco_kind from the contracts storage or fail */ - const taco_kind : taco_supply = - match (Map.find_opt (taco_kind_index, taco_shop_storage)) { - when(Some(kind)): kind; - when(None()): failwith ("Unknown kind of taco") - }; - const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock ; - /* We won't sell tacos if the amount is not correct */ - if ((Tezos.get_amount ()) != current_purchase_price) { - return failwith ("Sorry, the taco you are trying to purchase has a different price") - } else { - /* Update the storage decreasing the stock by 1n */ - let taco_shop_storage = Map.update ( - taco_kind_index, - (Some (({...taco_kind, current_stock : abs (taco_kind.current_stock - 1n) }))), - taco_shop_storage ); - return [[], taco_shop_storage] - } -}; -``` - - - -Now let's test our function against a few inputs using the LIGO test framework. -For that, we will have another file in which will describe our test: - - - -```cameligo test-ligo group=test -#import "gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/TacoShop.mligo" "TacoShop" - -let assert_string_failure (res : test_exec_result) (expected : string) = - let expected = Test.eval expected in - match res with - | Fail (Rejected (actual,_)) -> assert (Test.michelson_equal actual expected) - | Fail _ -> failwith "contract failed for an unknown reason" - | Success _ -> failwith "bad price check" - -let test = - (* originate the contract with a initial storage *) - let init_storage = Map.literal [ - (1n, { current_stock = 50n ; max_price = 50tez }) ; - (2n, { current_stock = 20n ; max_price = 75tez }) ; ] - in - let { addr ; code = _; size = _ } = Test.originate (contract_of TacoShop) init_storage 0tez in - - (* Test inputs *) - let clasico_kind : TacoShop parameter_of = Buy_taco 1n in - let unknown_kind : TacoShop parameter_of = Buy_taco 3n in - - (* Auxiliary function for testing equality in maps *) - let eq_in_map (r : TacoShop.taco_supply) (m : TacoShop.taco_shop_storage) (k : nat) = - match Map.find_opt k m with - | None -> false - | Some v -> v.current_stock = r.current_stock && v.max_price = r.max_price in - - (* Purchasing a Taco with 1tez and checking that the stock has been updated *) - let ok_case : test_exec_result = Test.transfer addr clasico_kind 1tez in - let () = match ok_case with - | Success _ -> - let storage = Test.get_storage addr in - assert ((eq_in_map { current_stock = 49n ; max_price = 50tez } storage 1n) && - (eq_in_map { current_stock = 20n ; max_price = 75tez } storage 2n)) - | Fail _ -> failwith ("ok test case failed") - in - - (* Purchasing an unregistred Taco *) - let nok_unknown_kind = Test.transfer addr unknown_kind 1tez in - let () = assert_string_failure nok_unknown_kind "Unknown kind of taco" in - - (* Attempting to Purchase a Taco with 2tez *) - let nok_wrong_price = Test.transfer addr clasico_kind 2tez in - let () = assert_string_failure nok_wrong_price "Sorry, the taco you are trying to purchase has a different price" in - () -``` - - - - - -```jsligo test-ligo group=test -#import "gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/TacoShop.jsligo" "TacoShop" - -function assert_string_failure (res: test_exec_result, expected: string) { - const expected_bis = Test.eval(expected); - match(res) { - when (Fail(x)): - match(x) { - when (Rejected(y)): - assert(Test.michelson_equal(y[0], expected_bis)) - when (Balance_too_low(_)): - failwith("contract failed for an unknown reason") - when (Other(_o)): - failwith("contract failed for an unknown reason") - } - when (Success(_s)): - failwith("bad price check") - }; -} - -const test = ( - (_u: unit): unit => { - /* Originate the contract with a initial storage */ - - let init_storage = - Map.literal( - list( - [ - [1n, { current_stock: 50n, max_price: 50000000mutez }], - [2n, { current_stock: 20n, max_price: 75000000mutez }] - ] - ) - ); - const { addr , code , size } = - Test.originate(contract_of(TacoShop), init_storage, 0mutez); - - /* Test inputs */ - - const clasico_kind : parameter_of TacoShop = Buy_taco (1n); - const unknown_kind : parameter_of TacoShop = Buy_taco (3n); - /* Auxiliary function for testing equality in maps */ - - const eq_in_map = (r: TacoShop.taco_supply, m: TacoShop.taco_shop_storage, k: nat) => - match(Map.find_opt(k, m)) { - when (None): - false - when (Some(v)): - v.current_stock == r.current_stock && v.max_price == r.max_price - }; - /* Purchasing a Taco with 1tez and checking that the stock has been updated */ - - const ok_case: test_exec_result = - Test.transfer( - addr, - clasico_kind, - 1000000mutez - ); - - match(ok_case) { - when (Success(_s)): - do { - let storage = Test.get_storage(addr); - assert( - eq_in_map( - { current_stock: 49n, max_price: 50000000mutez }, - storage, - 1n - ) - && eq_in_map( - { current_stock: 20n, max_price: 75000000mutez }, - storage, - 2n - ) - ); - } - when (Fail(_e)): - failwith("ok test case failed") - }; - /* Purchasing an unregistred Taco */ - - const nok_unknown_kind = - Test.transfer( - addr, - unknown_kind, - 1000000mutez - ); - assert_string_failure(nok_unknown_kind, "Unknown kind of taco"); - /* Attempting to Purchase a Taco with 2tez */ - - const nok_wrong_price = - Test.transfer( - addr, - clasico_kind, - 2000000mutez - ); - - assert_string_failure( - nok_wrong_price, - "Sorry, the taco you are trying to purchase has a different price" - ); - return unit - } - ) (); -``` - - - -Let's break it down a little bit: -- we include the file corresponding to the smart contract we want to - test; -- we define `assert_string_failure`, a function reading a transfer - result and testing against a failure. It also compares the failing - data - here, a string - to what we expect it to be; -- `test` is actually performing the tests: Originates the taco-shop - contract; purchasing a Taco with 1tez and checking that the stock - has been updated ; attempting to purchase a Taco with 2tez and - trying to purchase an unregistered Taco. An auxiliary function to - check equality of values on maps is defined. - -> checkout the [reference page](../../reference/test.md) for a more detailed description of the Test API - -Now it is time to use the LIGO command `test`. It will evaluate our -smart contract and print the result value of those entries that start -with `"test"`: - - - -```zsh -ligo run test gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/test.mligo -# Output: -# -# Everything at the top-level was executed. -# - test exited with value (). -``` - - - - - -```zsh -ligo run test gitlab-pages/docs/tutorials/taco-shop/src/tezos-taco-shop-smart-contract/test.jsligo -# Output: -# -# Everything at the top-level was executed. -# - test exited with value (). -``` - - - - -**The test passed ! That's it - Pedro can now sell tacos on-chain, thanks to Tezos & LIGO.** - ---- - -## 💰 Bonus: *Accepting Tips above the Taco Purchase Price* - -If you would like to accept tips in your contract, simply change the -following line, depending on your preference. - -**Without tips** - - - -```cameligo skip -if (Tezos.get_amount ()) <> current_purchase_price then -``` - - - - -```jsligo skip -if ((Tezos.get_amount ()) != current_purchase_price) -``` - - - -**With tips** - - - -```cameligo skip -if (Tezos.get_amount ()) >= current_purchase_price then -``` - - - - - -```jsligo skip -if ((Tezos.get_amount ()) >= current_purchase_price) -``` - - diff --git a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a1.jsligo b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a1.jsligo index ff1a14bcee..cfd8b0fdad 100644 --- a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a1.jsligo +++ b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a1.jsligo @@ -1,17 +1,16 @@ type storage = { fn : option<((x : int) => int)>, value : int }; type result = [list, storage]; -const call = (fn: option<((x : int) => int)>, value: int) : int => { - return match(fn) { - when(Some(f)): f(value); - when(None()): failwith("Lambda is not set") - } -}; +const call = (fn: option<((x : int) => int)>, value: int) : int => + $match(fn, { + "Some": f => f(value), + "None": () => failwith("Lambda is not set") + }); -@entry +// @entry const setFunction = (fn : ((v : int) => int), s : storage) : result => - [[], {...s, fn: Some(fn)}]; + [[], {...s, fn: ["Some" as "Some", fn]}]; -@entry +// @entry const callFunction = (_u : unit, s : storage) : result => [[], {...s, value: call(s.fn, s.value)}]; \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a2.jsligo b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a2.jsligo index b6fa5e6e53..d09ad7d4db 100644 --- a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a2.jsligo +++ b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a2.jsligo @@ -1,11 +1,9 @@ -let main = (parameter: bytes, storage: int): [list, int] => { - if (parameter == 0xbc1ecb8e) { - return [[], storage + 1] - } else { - if (parameter == 0x36e44653) { - return [[], storage - 1] - } else { - return (failwith("Unknown entrypoint")) - } - } +const main = (parameter: bytes, storage: int): [list, int] => { + if (parameter == 0xbc1ecb8e) + return [[], storage + 1]; + else + if (parameter == 0x36e44653) + return [[], storage - 1]; + else + failwith("Unknown entrypoint"); }; \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a3.jsligo b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a3.jsligo index 81469c81ae..e5ea599207 100644 --- a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a3.jsligo +++ b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a3.jsligo @@ -1,8 +1,8 @@ type storage = int; type result = [list, int] -@entry +// @entry const increment = (_u : unit, s : storage) : result => [[], s + 1] -@entry +// @entry const decrement = (_u : unit, s : storage) : result => [[], s - 1] \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a4.jsligo b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a4.jsligo index afadcea0bd..2bb10d1d2f 100644 --- a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a4.jsligo +++ b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a4.jsligo @@ -1,8 +1,8 @@ type storage = int; type result = [list, int] -@entry +// @entry const add = (i : int, s : storage) : result => [[], s + i] -@entry +// @entry const subtract = (i : int, s : storage) : result => [[], s - i] \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a5.jsligo b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a5.jsligo index 054671a121..e465af4158 100644 --- a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a5.jsligo +++ b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a5.jsligo @@ -1,9 +1,12 @@ type storage = int type result = [list, storage] -let doMultiplyBy2 = (store : storage) : int => store * 2; +const doMultiplyBy2 = (store : storage) : int => store * 2; -let doMultiplyBy4 = (store : storage) : int => doMultiplyBy2(doMultiplyBy2(store)); +const doMultiplyBy4 = (store : storage) : int => doMultiplyBy2(doMultiplyBy2(store)); -@entry const multiplyBy4 = (_u : unit, s : storage) : result => [[], doMultiplyBy4(s)] -@entry const multiplyBy16 = (_u : unit, s : storage) : result => [[], doMultiplyBy4(doMultiplyBy4(s))] \ No newline at end of file +// @entry +const multiplyBy4 = (_u : unit, s : storage) : result => [[], doMultiplyBy4(s)] + +// @entry +const multiplyBy16 = (_u : unit, s : storage) : result => [[], doMultiplyBy4(doMultiplyBy4(s))] \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a6.jsligo b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a6.jsligo index 513ed6d7e6..207d99fae6 100644 --- a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a6.jsligo +++ b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/a6.jsligo @@ -1,5 +1,5 @@ type storage = int; -@entry +// @entry const compute = (func: ((v : int) => int), s: storage) : [list, int] => [[], func(s)] \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b1.jsligo b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b1.jsligo index 0fa04b9506..5f4f3af0cf 100644 --- a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b1.jsligo +++ b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b1.jsligo @@ -1,19 +1,20 @@ type storage = {rewardsLeft: tez, beneficiaryAddress: address }; -let treasury = (p : unit, s : storage) => { +const treasury = (p : unit, s : storage) => { // We do our computations first - let newStorage = {...s, rewardsLeft: 0mutez}; + const newStorage = {...s, rewardsLeft: 0 as mutez}; // Then we find our beneficiary's `handleRewards` entrypoint: - let beneficiaryOpt = Tezos.get_entrypoint_opt("%handleTransfer", s.beneficiaryAddress); - let beneficiary = - match(beneficiaryOpt) { - when(Some(contract)): contract; - when(None()): failwith("Beneficiary does not exist") - }; + const beneficiaryOpt = Tezos.get_entrypoint_opt("%handleTransfer", s.beneficiaryAddress); + const beneficiary = + $match(beneficiaryOpt, { + "Some": contract => contract, + "None": () => failwith("Beneficiary does not exist") + }); // Then we prepare the internal operation we want to perform - let operation = Tezos.transaction(unit, s.rewardsLeft, beneficiary); + const operation = + Tezos.Operation.transaction(unit, s.rewardsLeft, beneficiary); // ...and return both the operations and the updated storage return [list([operation]), newStorage]; diff --git a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b1.mligo b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b1.mligo index 2836fa767a..c65168c9d3 100644 --- a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b1.mligo +++ b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b1.mligo @@ -12,7 +12,8 @@ let treasury (p, s : unit * storage) = | None -> failwith "Beneficiary does not exist" in // Then we prepare the internal operation we want to perform - let operation = Tezos.transaction () s.rewardsLeft beneficiary in + let operation = + Tezos.Operation.transaction () s.rewardsLeft beneficiary in // ...and return both the operations and the updated storage ([operation], newStorage) \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b4.jsligo b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b4.jsligo index 65d7ce3ffb..33f63248b5 100644 --- a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b4.jsligo +++ b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b4.jsligo @@ -1,5 +1,5 @@ type int_option = ["Number", int] | ["Null"]; -let x = Number(5); +const x = ["Number" as "Number", 5]; -let y = Null (); \ No newline at end of file +const y = ["Null" as "Null"]; \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b5.jsligo b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b5.jsligo index 81667b405b..97730dcad2 100644 --- a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b5.jsligo +++ b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b5.jsligo @@ -1,3 +1,3 @@ -let x = Some(5); +const x = ["Some" as "Some", 5]; -let y: option = None(); \ No newline at end of file +const y: option = ["None" as "None"]; \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b6.jsligo b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b6.jsligo index e16947ec79..05c72ee8ba 100644 --- a/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b6.jsligo +++ b/gitlab-pages/docs/tutorials/tz-vs-eth/src/tz-vs-eth/b6.jsligo @@ -1,7 +1,7 @@ -let x = Some (5); +const x = ["Some" as "Some", 5]; -let x_or_zero = - match(x) { - when(Some(value)): value; - when(None()): 0 - }; \ No newline at end of file +const x_or_zero = + $match(x, { + "Some": value => value, + "None": () => 0 + }); \ No newline at end of file diff --git a/gitlab-pages/docs/tutorials/tz-vs-eth/tz-vs-eth.md b/gitlab-pages/docs/tutorials/tz-vs-eth/tz-vs-eth.md index 8a5be117ed..c9219157f0 100644 --- a/gitlab-pages/docs/tutorials/tz-vs-eth/tz-vs-eth.md +++ b/gitlab-pages/docs/tutorials/tz-vs-eth/tz-vs-eth.md @@ -134,9 +134,9 @@ Valid values of this type are regular numbers wrapped in `Number` (e.g., `Number ```jsligo group=b4 type int_option = ["Number", int] | ["Null"]; -let x = Number(5); +const x = ["Number" as "Number", 5]; -let y = Null (); +const y = ["Null" as "Null"]; ``` Valid values of this type are regular numbers wrapped in `Number` (e.g., `Number(5)`, `Number(10)`, etc.) or `Null`. Notice how `Null()` does not hold any value. @@ -170,21 +170,21 @@ let x_or_zero = ```jsligo group=b5 -let x = Some(5); +const x = ["Some" as "Some", 5]; -let y: option = None(); +const y: option = ["None" as "None"]; ``` This is how we express _nullability_ in LIGO: instead of using a special ad-hoc value like "zero address", we just say it is an `option
    `. We can then use `match` to see if there is something inside: ```jsligo group=b6 -let x = Some (5); +const x = ["Some" as "Some", 5]; -let x_or_zero = - match(x) { - when(Some(value)): value; - when(None()): 0 - }; +const x_or_zero = + $match(x, { + "Some": value => value, + "None": () => 0 + }); ``` @@ -269,16 +269,14 @@ let decrement (_ : unit) (s : storage) : result = [], s - 1 ```jsligo group=a2 -let main = (parameter: bytes, storage: int): [list, int] => { - if (parameter == 0xbc1ecb8e) { - return [[], storage + 1] - } else { - if (parameter == 0x36e44653) { - return [[], storage - 1] - } else { - return (failwith("Unknown entrypoint")) - } - } +const main = (parameter: bytes, storage: int): [list, int] => { + if (parameter == 0xbc1ecb8e) + return [[], storage + 1]; + else + if (parameter == 0x36e44653) + return [[], storage - 1]; + else + failwith("Unknown entrypoint"); }; ``` @@ -288,10 +286,10 @@ However, we can do better. As we discussed, LIGO has a much richer type system t type storage = int; type result = [list, int] -@entry +// @entry const increment = (_u : unit, s : storage) : result => [[], s + 1] -@entry +// @entry const decrement = (_u : unit, s : storage) : result => [[], s - 1] ``` @@ -322,10 +320,10 @@ let subtract (i : int) (s : storage) : result = [], s - i type storage = int; type result = [list, int] -@entry +// @entry const add = (i : int, s : storage) : result => [[], s + i] -@entry +// @entry const subtract = (i : int, s : storage) : result => [[], s - i] ``` @@ -375,12 +373,15 @@ let doMultiplyBy4 (store : storage) : int = doMultiplyBy2 (doMultiplyBy2 store) type storage = int type result = [list, storage] -let doMultiplyBy2 = (store : storage) : int => store * 2; +const doMultiplyBy2 = (store : storage) : int => store * 2; + +const doMultiplyBy4 = (store : storage) : int => doMultiplyBy2(doMultiplyBy2(store)); -let doMultiplyBy4 = (store : storage) : int => doMultiplyBy2(doMultiplyBy2(store)); +// @entry +const multiplyBy4 = (_u : unit, s : storage) : result => [[], doMultiplyBy4(s)] -@entry const multiplyBy4 = (_u : unit, s : storage) : result => [[], doMultiplyBy4(s)] -@entry const multiplyBy16 = (_u : unit, s : storage) : result => [[], doMultiplyBy4(doMultiplyBy4(s))] +// @entry +const multiplyBy16 = (_u : unit, s : storage) : result => [[], doMultiplyBy4(doMultiplyBy4(s))] ``` @@ -419,7 +420,7 @@ ligo run interpret 'main (Compute (fun (x : int) -> x * x + 2 * x + 1), 3)' --in ```jsligo group=a6 type storage = int; -@entry +// @entry const compute = (func: ((v : int) => int), s: storage) : [list, int] => [[], func(s)] ``` @@ -465,18 +466,17 @@ Now we can _upgrade_ a part of the implementation by calling our contract with ` type storage = { fn : option<((x : int) => int)>, value : int }; type result = [list, storage]; -const call = (fn: option<((x : int) => int)>, value: int) : int => { - return match(fn) { - when(Some(f)): f(value); - when(None()): failwith("Lambda is not set") - } -}; +const call = (fn: option<((x : int) => int)>, value: int) : int => + $match(fn, { + "Some": f => f(value), + "None": () => failwith("Lambda is not set") + }); -@entry +// @entry const setFunction = (fn : ((v : int) => int), s : storage) : result => - [[], {...s, fn: Some(fn)}]; + [[], {...s, fn: ["Some" as "Some", fn]}]; -@entry +// @entry const callFunction = (_u : unit, s : storage) : result => [[], {...s, value: call(s.fn, s.value)}]; ``` @@ -535,7 +535,8 @@ let treasury (p, s : unit * storage) = | None -> failwith "Beneficiary does not exist" in // Then we prepare the internal operation we want to perform - let operation = Tezos.transaction () s.rewardsLeft beneficiary in + let operation = + Tezos.Operation.transaction () s.rewardsLeft beneficiary in // ...and return both the operations and the updated storage ([operation], newStorage) @@ -548,20 +549,21 @@ let treasury (p, s : unit * storage) = ```jsligo group=b1 type storage = {rewardsLeft: tez, beneficiaryAddress: address }; -let treasury = (p : unit, s : storage) => { +const treasury = (p : unit, s : storage) => { // We do our computations first - let newStorage = {...s, rewardsLeft: 0mutez}; + const newStorage = {...s, rewardsLeft: 0 as mutez}; // Then we find our beneficiary's `handleRewards` entrypoint: - let beneficiaryOpt = Tezos.get_entrypoint_opt("%handleTransfer", s.beneficiaryAddress); - let beneficiary = - match(beneficiaryOpt) { - when(Some(contract)): contract; - when(None()): failwith("Beneficiary does not exist") - }; + const beneficiaryOpt = Tezos.get_entrypoint_opt("%handleTransfer", s.beneficiaryAddress); + const beneficiary = + $match(beneficiaryOpt, { + "Some": contract => contract, + "None": () => failwith("Beneficiary does not exist") + }); // Then we prepare the internal operation we want to perform - let operation = Tezos.transaction(unit, s.rewardsLeft, beneficiary); + const operation = + Tezos.Operation.transaction(unit, s.rewardsLeft, beneficiary); // ...and return both the operations and the updated storage return [list([operation]), newStorage]; @@ -581,7 +583,7 @@ type parameter = DoSomething | DoSomethingCont of int let doSomething (p, s : unit * int) = (* The callee should call `%doSomethingCont` with the value we want *) - let op = Tezos.transaction ... in + let op = Tezos.Operation.transaction ... in ([op], s) let doSomethingCont (p, s : int * int) = ([] : operation list), p + s @@ -594,9 +596,9 @@ let doSomethingCont (p, s : int * int) = ([] : operation list), p + s ```jsligo skip type parameter = ["DoSomething"] | ["DoSomethingCont", int]; -let doSomething = ([p, s]: [unit, int]) => { +const doSomething = ([p, s]: [unit, int]) => { /* The callee should call `%doSomethingCont` with the value we want */ - let op = Tezos.transaction ...; + const op = Tezos.Operation.transaction ...; return [[], s] } diff --git a/gitlab-pages/website/sidebars.js b/gitlab-pages/website/sidebars.js index c4bef81065..596c7136b1 100644 --- a/gitlab-pages/website/sidebars.js +++ b/gitlab-pages/website/sidebars.js @@ -11,16 +11,12 @@ const sidebars = { "tutorials/getting-started/getting-started", "intro/template", "intro/upgrade-v1", + "intro/upgrade-v2", ], - "Writing a Contract": [ - { - "type": "category", - "label": "First contract", - "items": [ - "tutorials/taco-shop/tezos-taco-shop-smart-contract", - "tutorials/taco-shop/tezos-taco-shop-payout" - ] - }, + "Tutorial": [ + "tutorials/taco-shop/selling-tacos", + "tutorials/taco-shop/testing-contract", + "tutorials/taco-shop/getting-payouts", ], "Syntax": [ "syntax/comments", @@ -44,6 +40,7 @@ const sidebars = { }, "id": "syntax/modules" }, + 'syntax/classes', { "type": "category", "label": "Contracts", @@ -77,6 +74,8 @@ const sidebars = { "syntax/keywords" ], "Data types": [ + "data-types/types", + "data-types/type-annotations", { "type": "category", "label": "Primitive types", @@ -95,7 +94,14 @@ const sidebars = { "data-types/tuples", "data-types/variants", "data-types/lists", - "data-types/records", + { + "type": "doc", + "label": "Records", + "customProps": { + "jsLigoName": "Objects" + }, + "id": "data-types/records", + }, "data-types/sets", "data-types/maps" ] @@ -131,13 +137,13 @@ const sidebars = { "Advanced Topics": [ "advanced/package-management", "tutorials/optimisation/optimisation", - "tutorials/security/security" + "advanced/security" ] }, "API": { "Language": [ - "reference/map-reference", "reference/bytes-reference", + "reference/map-reference", "reference/string-reference", "reference/option-reference", "reference/big-map-reference", @@ -148,88 +154,80 @@ const sidebars = { "reference/crypto-reference", "reference/big-set-reference", "reference/dynamic-entrypoints-reference", - "reference/tuple2-reference", + "reference/pair-reference", { "type": "category", "label": "tezos", "items": [ "reference/tezos-reference", - { - "type": "category", - "label": "next", - "items": [ - "reference/tezos.next.sapling-reference", - "reference/tezos.next.view-reference", - "reference/tezos.next.operation-reference", - "reference/tezos.next.ticket-reference", - "reference/tezos.next-reference" - ] - } + "reference/tezos.operation-reference", + "reference/tezos.view-reference", + "reference/tezos.ticket-reference", + "reference/tezos.sapling-reference" ] }, { "type": "category", "label": "test", "items": [ - "reference/test.pbt-reference", - "reference/test.proxy-ticket-reference", "reference/test-reference", + "reference/test.pbt-reference", + "reference/test.originate-reference", + "reference/test.typed-address-reference", + "reference/test.timelock-reference", + "reference/test.compare-reference", + "reference/test.address-reference", + "reference/test.dynamic-entrypoints-reference", + "reference/test.io-reference", + "reference/test.contract-reference", + "reference/test.string-reference", + "reference/test.crypto-reference", + { + "type": "category", + "label": "ticket", + "items": [ + "reference/test.ticket-reference", + "reference/test.ticket.proxy-reference" + ] + }, + { + "type": "category", + "label": "state", + "items": [ + "reference/test.state-reference", + "reference/test.state.reset-reference" + ] + }, + { + "type": "category", + "label": "michelson", + "items": [ + "reference/test.michelson-reference", + "reference/test.michelson.contract-reference" + ] + }, + { + "type": "category", + "label": "mutation", + "items": [ + "reference/test.mutation-reference", + "reference/test.mutation.all-reference" + ] + }, + { + "type": "category", + "label": "account", + "items": [ + "reference/test.account-reference", + "reference/test.account.contract-reference" + ] + }, { "type": "category", - "label": "next", + "label": "assert", "items": [ - "reference/test.next.originate-reference", - "reference/test.next.typed-address-reference", - "reference/test.next.timelock-reference", - "reference/test.next.compare-reference", - "reference/test.next.address-reference", - "reference/test.next.dynamic-entrypoints-reference", - "reference/test.next.io-reference", - "reference/test.next.ticket-reference", - "reference/test.next-reference", - "reference/test.next.contract-reference", - "reference/test.next.string-reference", - "reference/test.next.crypto-reference", - { - "type": "category", - "label": "state", - "items": [ - "reference/test.next.state-reference", - "reference/test.next.state.reset-reference" - ] - }, - { - "type": "category", - "label": "michelson", - "items": [ - "reference/test.next.michelson.contract-reference", - "reference/test.next.michelson-reference" - ] - }, - { - "type": "category", - "label": "mutation", - "items": [ - "reference/test.next.mutation.all-reference", - "reference/test.next.mutation-reference" - ] - }, - { - "type": "category", - "label": "account", - "items": [ - "reference/test.next.account.contract-reference", - "reference/test.next.account-reference" - ] - }, - { - "type": "category", - "label": "assert", - "items": [ - "reference/test.next.assert.error-reference", - "reference/test.next.assert-reference" - ] - } + "reference/test.assert-reference", + "reference/test.assert.error-reference" ] } ] diff --git a/gitlab-pages/website/src/components/HomepageCodeExamples/jsligo.jsligo b/gitlab-pages/website/src/components/HomepageCodeExamples/jsligo.jsligo index 92e6ef0c8c..dbe7431140 100644 --- a/gitlab-pages/website/src/components/HomepageCodeExamples/jsligo.jsligo +++ b/gitlab-pages/website/src/components/HomepageCodeExamples/jsligo.jsligo @@ -1,13 +1,15 @@ type storage = int; type ret = [list, storage]; + // Three entrypoints -@entry +// @entry const increment = (delta: int, store: storage): ret => [[], store + delta]; -@entry + +// @entry const decrement = (delta: int, store: storage): ret => [[], store - delta]; -@entry -const reset = (_p: unit, _s: storage): ret => [[], 0] +// @entry +const reset = (_p: unit, _s: storage): ret => [[], 0] diff --git a/gitlab-pages/website/src/components/HomepageFeatures/index.js b/gitlab-pages/website/src/components/HomepageFeatures/index.js index e1c789902c..cef2fb1929 100644 --- a/gitlab-pages/website/src/components/HomepageFeatures/index.js +++ b/gitlab-pages/website/src/components/HomepageFeatures/index.js @@ -15,7 +15,7 @@ const FEATURES = [ title: "Multi-syntax", content: "Ligo brings syntaxes that fit your preferences: TypeScript-inspired or OCaml-inspired.", - link: "docs/intro/introduction#ligo-for-newcomers-or-confirmed-developpers", + link: "docs/intro/introduction#ligo-for-newcomers-or-confirmed-developers", }, { image: "img/ligo_features/ligo-feature-optimized.svg", diff --git a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-payout/dry-run-1.png b/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-payout/dry-run-1.png deleted file mode 100644 index a8930f63ff..0000000000 Binary files a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-payout/dry-run-1.png and /dev/null differ diff --git a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-payout/get-money.svg b/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-payout/get-money.svg deleted file mode 100644 index 117937d258..0000000000 --- a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-payout/get-money.svg +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-1.png b/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-1.png deleted file mode 100644 index e685074fce..0000000000 Binary files a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-1.png and /dev/null differ diff --git a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-2.png b/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-2.png deleted file mode 100644 index 6f5c902e0b..0000000000 Binary files a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-2.png and /dev/null differ diff --git a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-3.png b/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-3.png deleted file mode 100644 index 5eb7178854..0000000000 Binary files a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-3.png and /dev/null differ diff --git a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-4.png b/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-4.png deleted file mode 100644 index 6ee09b1181..0000000000 Binary files a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-4.png and /dev/null differ diff --git a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-5.png b/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-5.png deleted file mode 100644 index ac390ce9cd..0000000000 Binary files a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/dry-run-5.png and /dev/null differ diff --git a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/install-ligo.png b/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/install-ligo.png deleted file mode 100644 index ca33648207..0000000000 Binary files a/gitlab-pages/website/static/img/tutorials/get-started/tezos-taco-shop-smart-contract/install-ligo.png and /dev/null differ diff --git a/gitlab-pages/website/versioned_docs/version-1.5.0/intro/ligo-intro.md b/gitlab-pages/website/versioned_docs/version-1.5.0/intro/ligo-intro.md index 251bc0b000..2c19803ecc 100644 --- a/gitlab-pages/website/versioned_docs/version-1.5.0/intro/ligo-intro.md +++ b/gitlab-pages/website/versioned_docs/version-1.5.0/intro/ligo-intro.md @@ -14,7 +14,7 @@ Our hope is to have a simple, strongly typed language with a low footprint. Most useful smart contracts can express their core functionality in under a thousand lines of code. -### LIGO, for newcomers or confirmed developpers +### LIGO, for newcomers or confirmed developers Even if LIGO currently offers **two syntaxes**, you'll need to **choose only one**: diff --git a/gitlab-pages/website/versioned_docs/version-1.6.0/intro/ligo-intro.md b/gitlab-pages/website/versioned_docs/version-1.6.0/intro/ligo-intro.md index 304b179d89..a22d20cf56 100644 --- a/gitlab-pages/website/versioned_docs/version-1.6.0/intro/ligo-intro.md +++ b/gitlab-pages/website/versioned_docs/version-1.6.0/intro/ligo-intro.md @@ -14,7 +14,7 @@ Our hope is to have a simple, strongly typed language with a low footprint. Most useful smart contracts can express their core functionality in under a thousand lines of code. -### LIGO, for newcomers or confirmed developpers +### LIGO, for newcomers or confirmed developers Even if LIGO currently offers **two syntaxes**, you'll need to **choose only one**: diff --git a/gitlab-pages/website/versioned_docs/version-1.9.2/advanced/attributes-decorators.md b/gitlab-pages/website/versioned_docs/version-1.9.2/advanced/attributes-decorators.md index 1ef57b37fc..68c2bfa63d 100644 --- a/gitlab-pages/website/versioned_docs/version-1.9.2/advanced/attributes-decorators.md +++ b/gitlab-pages/website/versioned_docs/version-1.9.2/advanced/attributes-decorators.md @@ -55,7 +55,7 @@ let f x = (g x) + 1 ```cameligo group=import-module-with-private (* This is gitlab-pages/docs/advanced/src/attributes-decorators/import-module-with-private.mligo *) -#import "gitlab-pages/docs/advanced/src/attributes-decorators/module-with-private.mligo" "ModuleWithPrivate" +module ModuleWithPrivate = Gitlab_pages.Docs.Advanced.Src.Attributes_decorators.Module_with_private (* foo = 5167 = (123 * 42) + 1 *) let foo = ModuleWithPrivate.f 123 diff --git a/gitlab-pages/website/versioned_docs/version-1.9.2/advanced/src/attributes-decorators/import-module-with-private.mligo b/gitlab-pages/website/versioned_docs/version-1.9.2/advanced/src/attributes-decorators/import-module-with-private.mligo index 48f43a9476..d2692d7448 100644 --- a/gitlab-pages/website/versioned_docs/version-1.9.2/advanced/src/attributes-decorators/import-module-with-private.mligo +++ b/gitlab-pages/website/versioned_docs/version-1.9.2/advanced/src/attributes-decorators/import-module-with-private.mligo @@ -1,5 +1,5 @@ (* This is gitlab-pages/docs/advanced/src/attributes-decorators/import-module-with-private.mligo *) -#import "gitlab-pages/docs/advanced/src/attributes-decorators/module-with-private.mligo" "ModuleWithPrivate" +module ModuleWithPrivate = Gitlab_pages.Docs.Advanced.Src.Attributes_decorators.Module_with_private (* foo = 5167 = (123 * 42) + 1 *) let foo = ModuleWithPrivate.f 123 diff --git a/gitlab-pages/website/versioned_docs/version-1.9.2/intro/ligo-intro.md b/gitlab-pages/website/versioned_docs/version-1.9.2/intro/ligo-intro.md index 6f5429b428..004d89a252 100644 --- a/gitlab-pages/website/versioned_docs/version-1.9.2/intro/ligo-intro.md +++ b/gitlab-pages/website/versioned_docs/version-1.9.2/intro/ligo-intro.md @@ -14,7 +14,7 @@ Our hope is to have a simple, strongly typed language with a low footprint. Most useful smart contracts can express their core functionality in under a thousand lines of code. -### LIGO, for newcomers or confirmed developpers +### LIGO, for newcomers or confirmed developers Even if LIGO currently offers **two syntaxes**, you'll need to **choose only one**: diff --git a/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/import.md b/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/import.md index ec0ae6c97c..3083b317fc 100644 --- a/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/import.md +++ b/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/import.md @@ -37,7 +37,7 @@ definitions. For example, we can create a `main.mligo` that imports all definitions from `euro.mligo` as the module `Euro`: ```cameligo group=main_importer -#import "gitlab-pages/docs/preprocessor/src/import/euro.mligo" "Euro" +module Euro = Gitlab_pages.Docs.Preprocessor.Src.Import.Euro type storage = Euro.t @@ -76,7 +76,7 @@ its definitions. For example, we can create a `main.jsligo` that imports all definitions from `euro.jsligo` as the namespace `Euro`: ```jsligo group=main_importer -#import "gitlab-pages/docs/preprocessor/src/import/euro.jsligo" "Euro" +import * as Euro from "gitlab-pages/docs/preprocessor/src/import/euro.jsligo"; type storage = Euro.t; @@ -110,7 +110,7 @@ namespace Euro { Because the namespace is public, you can access it as a sub-namespace when you import the file into another file: ```jsligo group=import_euro_public -#import "gitlab-pages/docs/preprocessor/src/import/euro_namespace_public.jsligo" "Euro_import" +import * as Euro_import from "gitlab-pages/docs/preprocessor/src/import/euro_namespace_public.jsligo"; type euro_balance = Euro_import.Euro.t; diff --git a/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/src/import/import_euro_public.jsligo b/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/src/import/import_euro_public.jsligo index b888e4bc80..8b9795d89c 100644 --- a/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/src/import/import_euro_public.jsligo +++ b/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/src/import/import_euro_public.jsligo @@ -1,4 +1,4 @@ -#import "gitlab-pages/docs/preprocessor/src/import/euro_namespace_public.jsligo" "Euro_import" +import * as Euro_import from "gitlab-pages/docs/preprocessor/src/import/euro_namespace_public.jsligo"; type euro_balance = Euro_import.Euro.t; diff --git a/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/src/import/main_importer.jsligo b/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/src/import/main_importer.jsligo index 40828f1969..22172c2857 100644 --- a/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/src/import/main_importer.jsligo +++ b/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/src/import/main_importer.jsligo @@ -1,4 +1,4 @@ -#import "gitlab-pages/docs/preprocessor/src/import/euro.jsligo" "Euro" +import * as Euro from "gitlab-pages/docs/preprocessor/src/import/euro.jsligo"; type storage = Euro.t; diff --git a/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/src/import/main_importer.mligo b/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/src/import/main_importer.mligo index 3c806f455a..affe40cf3c 100644 --- a/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/src/import/main_importer.mligo +++ b/gitlab-pages/website/versioned_docs/version-1.9.2/preprocessor/src/import/main_importer.mligo @@ -1,4 +1,4 @@ -#import "gitlab-pages/docs/preprocessor/src/import/euro.mligo" "Euro" +module Euro = Gitlab_pages.Docs.Preprocessor.Src.Import.Euro type storage = Euro.t diff --git a/lib/ligo_build_system/BuildSystem.ml b/lib/ligo_build_system/BuildSystem.ml index 9c635f46e7..ad411a2238 100644 --- a/lib/ligo_build_system/BuildSystem.ml +++ b/lib/ligo_build_system/BuildSystem.ml @@ -2,8 +2,10 @@ module PP = PP module Errors = Errors module To_yojson = To_yojson module Formatter = Formatter -include Types +module Location = Simple_utils.Location +module Ne_list = Simple_utils.Ne_list open Core +include Types module Source_input = struct type file_name = string @@ -19,11 +21,20 @@ module Source_input = struct } type code_input = + (* FIXME remove all non-needed code_input *) | From_file of file_name | HTTP of Uri.t | Raw of raw_input | Raw_input_lsp of raw_input_lsp + let map_code_input : code_input -> f:(file_name -> file_name) -> code_input = + fun code_input ~f -> + match code_input with + | From_file file_name -> From_file (f file_name) + | HTTP uri -> HTTP uri + | Raw { id; code } -> Raw { id = f id; code } + | Raw_input_lsp { file; code } -> Raw_input_lsp { file = f file; code } + let id_of_code_input : code_input -> file_name = function | From_file file_name -> file_name | HTTP uri -> Filename.basename @@ Uri.to_string uri @@ -31,196 +42,244 @@ module Source_input = struct | Raw_input_lsp { file; code = _ } -> file end -module type M = sig +module T = struct type file_name = Source_input.file_name type raw_input = Source_input.raw_input type code_input = Source_input.code_input type module_name = string - type imports = file_name list - type compilation_unit + + (** `import` composes metadata of imported module + `location` is used for error reporting *) + type import = + { code_input : code_input + ; module_name : module_name + ; location : Location.t + } + + type imports = import list +end + +include T + +module type M = sig + (** Metadata of contract being built *) type meta_data - val preprocess - : code_input - -> compilation_unit * meta_data * (file_name * module_name) list + (** Module representing compilation unit *) + module C_unit : sig + type t + + (* Composes all data needed for compilation *) + type meta = + { code_input : code_input + ; location : Location.t + ; module_name : module_name + ; meta : meta_data + ; imports : imports + } + end + (** Converts import into ready to compile C_unit.t, gathers meta_data and imports *) + val preprocess : import -> C_unit.t * meta_data * imports + + (** Module reprenting target AST which contract compiles to *) module AST : sig + (** Target AST type *) type t - (* An interface describes the signature of a module *) + (** An interface describes the signature of a module *) type interface - (* Environment should be a local notion of the BuildSystem *) - type environment - + (** Links two asts into one *) val link : t -> t -> t - val link_interface : interface -> interface -> interface - val init_env : environment - val add_module_to_environment - : file_name - -> module_name - -> imports - -> interface - -> environment - -> environment + (* This should probably be taken in charge be the compiler, which should be able to handle "libraries" *) - val add_interface_to_environment : interface -> environment -> environment + (** Adds inline module to the ast *) + val make_module_in_ast : t -> module_name * interface * t -> t + end - (* This should probably be taken in charge be the compiler, which should be able to handle "libraries" *) - val make_module_in_ast : module_name -> t -> t -> t - val make_module_in_interface : module_name -> interface -> interface -> interface + (** Module representing compilation environment *) + module Environment : sig + type t + + val init_env : t + val add_interface : t -> AST.interface -> t + val find_interface : t -> module_name -> AST.interface + val add_module : t -> C_unit.meta -> AST.interface -> t end - val link_imports : AST.t -> intfs:AST.environment -> AST.t + (* Actually performs compilation *) + val compile : C_unit.t -> C_unit.meta -> Environment.t -> AST.t * AST.interface - val compile - : AST.environment - -> file_name - -> meta_data - -> compilation_unit - -> AST.t * AST.interface + (* Returns compiled standard library *) + val std_lib : unit -> AST.t * AST.interface - val lib_ast : unit -> AST.t - val lib_interface : unit -> AST.interface + (** Applies left transformations required for resulting AST *) + val postprocess : AST.t -> intfs:Environment.t -> AST.t end -module Make (M : M) = struct - type file_name = M.file_name - type code_input = M.code_input - - type vertice = - M.file_name * M.meta_data * M.compilation_unit * (M.file_name * M.module_name) list - - type graph = G.t * vertice SMap.t - type error = Errors.t - type ast = M.AST.t - type obj_env = ast SMap.t - type intf_env = M.AST.environment - type interface = M.AST.interface - type 'a build_error = ('a, error) result - - let dependency_graph : code_input -> graph = - fun code_input -> - let rec dfs (acc : M.file_name) (dep_g, vertices) (code_input, mangled_name) = - let id = Source_input.id_of_code_input code_input in - if not @@ SMap.mem id vertices - then ( - let c_unit, meta_data, deps = M.preprocess code_input in - let vertices = SMap.add id (mangled_name, meta_data, c_unit, deps) vertices in - let dep_g = G.add_vertex dep_g id in - let dep_g = - (* Don't add a loop on the first element *) - if Node.equal acc id then dep_g else G.add_edge dep_g acc id - in - let dep_g, vertices = - let f (x, y) = - let dependency_code_input = Source_input.From_file x in - dependency_code_input, y +module type S = functor (M : M) -> sig + (** Vertex of dependency graph *) + type vertex = M.C_unit.t * M.C_unit.meta + + val module_name_of_vertex : vertex -> module_name + + (** Dependency graph *) + type graph = G.t * vertex SMap.t + + type 'a build_result = ('a, Errors.t) Result.t + + (** Builds dependency graph from code_input *) + val dependency_graph : code_input -> graph + + (** Checks if graph is a DAG and returns topsorted list of files to compile *) + val solve_graph : graph -> module_name -> (module_name * vertex) Ne_list.t build_result + + (** Builds input without linking all the code_input dependencies into one AST. + Useful for inspection, debugging and testing. *) + val build_unqualified : code_input -> M.AST.t build_result + + (** Builds input and links all its dependencies into its ast *) + val build_qualified : code_input -> (M.AST.t * M.Environment.t) build_result +end + +module Make : S = +functor + (M : M) + -> + struct + include M + + type vertex = C_unit.t * C_unit.meta + + let module_name_of_vertex : vertex -> module_name = fun (_, meta) -> meta.module_name + + type graph = G.t * vertex SMap.t + type 'a build_result = ('a, Errors.t) Result.t + type obj_map = AST.t SMap.t + + let dependency_graph : code_input -> graph = + fun code_input -> + let rec dfs + (acc : module_name) + (dep_g, vertices) + ({ code_input; module_name; location } as import) + = + let id = Source_input.id_of_code_input code_input in + if not @@ Map.mem vertices id + then ( + (* Historically, preprocess is used for extracting dependencies also *) + let c_unit, meta, imports = preprocess import in + let vertices = + Map.set + vertices + ~key:id + ~data:(c_unit, C_unit.{ code_input; location; module_name; meta; imports }) in - let deps = List.map ~f deps in - List.fold ~f:(dfs id) ~init:(dep_g, vertices) deps - in - dep_g, vertices) - else ( - let dep_g = G.add_edge dep_g acc id in - dep_g, vertices) - in - let vertices = SMap.empty in - let dep_g = G.empty in - let file_name = Source_input.id_of_code_input code_input in - dfs file_name (dep_g, vertices) @@ (code_input, file_name) - - let solve_graph : graph -> file_name -> ((file_name * vertice) list, error) result = - fun (dep_g, vertices) file_name -> - if Dfs.has_cycle dep_g - then ( - let graph = Format.asprintf "%a" PP.graph (dep_g, file_name) in - Error (Errors.build_dependency_cycle graph)) - else ( - let aux v order = - let elem = SMap.find v vertices in - (v, elem) :: order - in - let order = TopSort.fold aux dep_g [] in - Ok order) - - let link ~(objs : obj_env) ~(intfs : intf_env) linking_order = - (* Separate the program and the dependency (those are process differently) *) - let (file_name, (_, _, _, _deps_lst)), linking_order = - match List.rev linking_order with - | [] -> failwith "compiling nothing" - | hd :: tl -> hd, tl - in - let contract = - match SMap.find_opt file_name objs with - | Some ast -> ast - | None -> failwith "failed to find module" - in - (* Add all dependency at the beginning of the file *) - let add_modules (file_name, (mangled_name, _, _, _deps_lst)) = - let module_binder = mangled_name in - (* Get the ast_type of the module *) - let ast_typed = - match SMap.find_opt file_name objs with - | Some ast -> ast - | None -> failwith "failed to find module" - in - module_binder, ast_typed - in - let header_list = List.map ~f:add_modules @@ linking_order in - let contract = - List.fold_left - ~f:(fun c (module_binder, ast) -> M.AST.make_module_in_ast module_binder ast c) - ~init:contract - header_list - in - (* Link the stdlib *) - let contract = M.AST.link (M.lib_ast ()) contract in - (* Finally link all the imports *) - let contract = M.link_imports contract ~intfs in - contract - - let compile_file_with_deps - ((objs, intfs) : obj_env * intf_env) - (file_name, (mangled_name, meta, c_unit, _deps)) - = - let imports = List.map ~f:(fun (x, _) -> x) _deps in - let ast, ast_intf = M.compile intfs file_name meta c_unit in - let intfs = - M.AST.add_module_to_environment file_name mangled_name imports ast_intf intfs - in - let objs = SMap.add file_name ast objs in - objs, intfs - - let compile_unqualified : code_input -> ast build_error = - fun main_code_input -> - let deps = dependency_graph main_code_input in - let main_file_name = Source_input.id_of_code_input main_code_input in - match solve_graph deps main_file_name with - | Ok ordered_deps -> - let init_env = - M.AST.add_interface_to_environment (M.lib_interface ()) M.AST.init_env + let dep_g = G.add_vertex dep_g id in + let dep_g = + (* Don't add a loop on the first element *) + if Node.equal acc id then dep_g else G.add_edge dep_g acc id + in + let dep_g, vertices = List.fold ~f:(dfs id) ~init:(dep_g, vertices) imports in + dep_g, vertices) + else ( + let dep_g = G.add_edge dep_g acc id in + dep_g, vertices) in - let objs, _ = - List.fold ~f:compile_file_with_deps ~init:(SMap.empty, init_env) ordered_deps + let vertices = SMap.empty in + let dep_g = G.empty in + let file_name = Source_input.id_of_code_input code_input in + let module_name = file_name in + dfs file_name (dep_g, vertices) + @@ { code_input; module_name; location = Location.dummy } + + let solve_graph : graph -> file_name -> (file_name * vertex) Ne_list.t build_result = + fun (dep_g, vertices) file_name -> + if Dfs.has_cycle dep_g + then ( + let graph = Format.asprintf "%a" PP.graph (dep_g, file_name) in + Error (Errors.build_dependency_cycle graph)) + else ( + let aux v order = + let elem = Map.find_exn vertices v in + (v, elem) :: order + in + let order = TopSort.fold aux dep_g [] in + match order with + | hd :: tl -> Ok (hd :: tl) + | [] -> Error (Errors.build_compiling_nothing)) + + let link ~(objs : obj_map) ~(intfs : Environment.t) linking_order = + (* Separate the program and the dependency (those are process differently) *) + let (file_name, (_, C_unit.{ imports = _deps_lst; _ })), linking_order = + match Ne_list.rev linking_order with + | hd :: tl -> hd, tl in - Ok (SMap.find main_file_name objs) - | Error e -> Error e - - let compile_qualified : code_input -> (ast * intf_env) build_error = - fun code_input -> - let deps = dependency_graph code_input in - let file_name = Source_input.id_of_code_input code_input in - match solve_graph deps file_name with - | Ok linking_order -> - let init_env = - M.AST.add_interface_to_environment (M.lib_interface ()) M.AST.init_env + (* NOTE The contract build system was invoked for must present in the build environment at this point *) + let contract = Map.find_exn objs file_name in + (* Add all dependency at the beginning of the file *) + let add_modules (file_name, (_, C_unit.{ module_name; imports = _deps_lst; _ })) = + let module_binder = module_name in + (* Get the ast_type of the module *) + (* NOTE Same for its deps: they were already compiled since we are at linking stage *) + let ast_typed = Map.find_exn objs file_name in + module_binder, ast_typed in - let objs, intfs = - List.fold ~f:compile_file_with_deps ~init:(SMap.empty, init_env) linking_order + let header_list = List.map ~f:add_modules @@ linking_order in + let contract = + List.fold_left + ~f:(fun c (module_binder, ast) -> + AST.make_module_in_ast + c + (module_binder, Environment.find_interface intfs module_binder, ast)) + ~init:contract + header_list in - let contract = link ~objs ~intfs linking_order in - Ok (contract, intfs) - | Error e -> Error e -end + (* Link the stdlib *) + let contract = AST.link (Tuple2.get1 @@ std_lib ()) contract in + (* Finally link all the imports *) + let contract = postprocess contract ~intfs in + contract + + let compile_file_with_deps + ((objs, intfs) : obj_map * Environment.t) + (file_name, (c_unit, c_unit_meta)) + = + let ast, ast_intf = compile c_unit c_unit_meta intfs in + let intfs = Environment.add_module intfs c_unit_meta ast_intf in + let objs = Map.set objs ~key:file_name ~data:ast in + objs, intfs + + let build + : code_input + -> (file_name * (file_name * vertex) Ne_list.t * obj_map * Environment.t) build_result + = + fun code_input -> + let deps = dependency_graph code_input in + let file_name = Source_input.id_of_code_input code_input in + match solve_graph deps file_name with + | Ok linking_order -> + let init_env = Environment.(add_interface init_env) (Tuple2.get2 @@ std_lib ()) in + let objs, intfs = + List.fold ~f:compile_file_with_deps ~init:(SMap.empty, init_env) @@ Ne_list.to_list linking_order + in + Ok (file_name, linking_order, objs, intfs) + | Error e -> Error e + + let build_unqualified : code_input -> AST.t build_result = + fun code_input -> + let open Result.Monad_infix in + build code_input + >>= fun (module_name, _, objs, _) -> + (* NOTE The contract build system was invoked for must present in the build environment at this point *) + Ok (Map.find_exn objs module_name) + + let build_qualified : code_input -> (AST.t * Environment.t) build_result = + fun code_input -> + let open Result.Monad_infix in + build code_input + >>= fun (_, linking_order, objs, intfs) -> + Ok (link ~objs ~intfs linking_order, intfs) + end diff --git a/lib/ligo_build_system/PP.ml b/lib/ligo_build_system/PP.ml index 5f11d5ca70..b7fc34358a 100644 --- a/lib/ligo_build_system/PP.ml +++ b/lib/ligo_build_system/PP.ml @@ -1,3 +1,4 @@ +open Core open Types type state = @@ -23,8 +24,8 @@ let graph' f (dep_g, node) = let rec pp_node state set arity node rank = let state = pad arity rank @@ state in f state.pad_path @@ node; - if SSet.mem node set then raise (Dependency_cycle node); - let set = SSet.add node set in + if Set.mem set node then raise (Dependency_cycle node); + let set = Set.add set node in let len = len node in let _ = G.fold_succ (pp_node state set len) dep_g node 0 in rank + 1 @@ -39,13 +40,13 @@ let graph ppf (dep_g, node) = let module TopSort = Graph.Topological.Make (G) in let order, final = TopSort.fold - (fun node (m, order) -> SMap.add node order m, order + 1) + (fun node (m, order) -> Map.set m ~key:node ~data:order, order + 1) dep_g (SMap.empty, 1) in graph' (fun pad_path node -> - match SMap.find_opt node order with + match Map.find order node with | Some n -> Format.fprintf ppf "%s%d -- %s\n%!" pad_path (final - n) node | None -> ()) (dep_g, node) diff --git a/lib/ligo_build_system/errors.ml b/lib/ligo_build_system/errors.ml index f4cd145c60..5170573d05 100644 --- a/lib/ligo_build_system/errors.ml +++ b/lib/ligo_build_system/errors.ml @@ -4,9 +4,11 @@ module Ligo_Error = Simple_utils.Error type t = [ `Build_dependency_cycle of string | `Build_corner_case of string * string (* TO REMOVE *) + | `Build_compiling_nothing ] let build_dependency_cycle (s : string) = `Build_dependency_cycle s +let build_compiling_nothing = `Build_compiling_nothing let build_corner_case (loc : string) (msg : string) = `Build_corner_case (loc, msg) let error_ppformat @@ -19,6 +21,8 @@ let error_ppformat (match a with | `Build_dependency_cycle trace -> Format.fprintf f "@[Dependency cycle detected :@, %s@]" trace + | `Build_compiling_nothing -> + Format.fprintf f "@[Compiling nothing]" | `Build_corner_case (loc, msg) -> Format.fprintf f "@[Building corner case at %s : %s@]" loc msg) @@ -30,6 +34,10 @@ let error_json : t -> Ligo_Error.t = let message = Format.asprintf "@[Dependency cycle detected :@, %s@]" trace in let content = Ligo_Error.make_content ~message () in Ligo_Error.make ~stage ~content + | `Build_compiling_nothing -> + let message = Format.asprintf "@[Compiling nothing]" in + let content = Ligo_Error.make_content ~message () in + Ligo_Error.make ~stage ~content | `Build_corner_case (loc, msg) -> let message = Format.asprintf "@[Building corner case at %s : %s@]" loc msg in let content = Ligo_Error.make_content ~message () in diff --git a/lib/ligo_build_system/to_yojson.ml b/lib/ligo_build_system/to_yojson.ml index c88d8b6f68..558e900d38 100644 --- a/lib/ligo_build_system/to_yojson.ml +++ b/lib/ligo_build_system/to_yojson.ml @@ -1,13 +1,14 @@ +open Core open Types let graph (dep_g, filename) = let set = SSet.empty in let rec pp_node set name parent = let node = "file", `String name in - if SSet.mem name set + if Set.mem set name then ("child", `Assoc [ node ]) :: parent else ( - let set = SSet.add name set in + let set = Set.add set name in let node = G.fold_succ (pp_node set) dep_g name [ node ] in let node = List.rev node in ("child", `Assoc node) :: parent) diff --git a/lib/ligo_build_system/types.ml b/lib/ligo_build_system/types.ml index 87a35fbb2a..fe7fcef175 100644 --- a/lib/ligo_build_system/types.ml +++ b/lib/ligo_build_system/types.ml @@ -1,3 +1,4 @@ +open Core module Node = struct type t = String.t [@@deriving eq, compare] diff --git a/lib/ligo_lltz_codgen/ligo_lltz_codegen.ml b/lib/ligo_lltz_codgen/ligo_lltz_codegen.ml index e3ba33b1e0..ed93ea4191 100644 --- a/lib/ligo_lltz_codgen/ligo_lltz_codegen.ml +++ b/lib/ligo_lltz_codgen/ligo_lltz_codegen.ml @@ -624,7 +624,7 @@ let rec compile_expression (expr : I.expression) : O.Expr.t = (*TODO: let args = List.map args ~f:compile_expression in return @@ Global_constant { hash; args } - (* { ... + (* { ... PUSH args onto stack ; (constant ) diff --git a/lib/ligo_lsp/lsp_helpers/get_scope.ml b/lib/ligo_lsp/lsp_helpers/get_scope.ml index f2c9f4caa3..2442fef846 100644 --- a/lib/ligo_lsp/lsp_helpers/get_scope.ml +++ b/lib/ligo_lsp/lsp_helpers/get_scope.ml @@ -49,18 +49,15 @@ let with_code_input let options = Compiler_options.set_no_stdlib options true in (* Let's build a dependency graph for the given code input. It will collect a [(file_name * module_name) list] which we can use - to create a module name to file name mapping.*) + to create a module name to file name mapping. *) + (* NOTE Trace.to_option is used to suppress warnings and errors + because due to calling Build.qualified_core* functions they get + duplicated. This is left for now. Once code_input will be deleted, + module_deps and compiled core should be obtained during one build system + invocation. *) let module_deps = - let module Deps_map = Stdlib__Map.Make (Stdlib__String) in - Build.dependency_graph ~raise ~options code_input - |> snd - |> Deps_map.to_seq - |> Seq.fold_left (fun acc (_, (_, _, _, lst)) -> lst :: acc) [] - |> List.concat - |> List.fold_left ~init:String.Map.empty ~f:(fun acc (file_name, mangled_name) -> - match Map.add ~key:mangled_name ~data:file_name acc with - | `Duplicate -> acc - | `Ok added -> added) + Option.value ~default:String.Map.empty + @@ Trace.to_option ~fast_fail:false (Build.module_deps ~options code_input) in ( (match code_input with | From_file _ | HTTP _ -> Build.qualified_core ~raise ~options code_input diff --git a/lib/ligo_lsp/requests/range_formatting.ml b/lib/ligo_lsp/requests/range_formatting.ml index 8f46bce029..74cacae7dd 100644 --- a/lib/ligo_lsp/requests/range_formatting.ml +++ b/lib/ligo_lsp/requests/range_formatting.ml @@ -40,7 +40,7 @@ let print_decl : Pretty.pp_mode -> declaration -> string = Pretty.with_pp_mode pp_mode { cameligo = uncurry CameLIGO_pretty.print_declaration - ; jsligo = uncurry JsLIGO_pretty.print_statement + ; jsligo = uncurry @@ JsLIGO_pretty.print_statement ~let_to_const:false } (* [print_decl] produce a newline at the end of doc, which leads to a trailing newline diff --git a/lib/ligo_parser_lib/CLI.ml b/lib/ligo_parser_lib/CLI.ml index 4d142c5dc7..ae839df854 100644 --- a/lib/ligo_parser_lib/CLI.ml +++ b/lib/ligo_parser_lib/CLI.ml @@ -28,9 +28,11 @@ module Make (LexerParams: LexerLib.CLI.PARAMETERS) : PARAMETERS = let make_help buffer : Buffer.t = let options = [ " --mono Use Menhir monolithic API"; - "Pretty-printing:"; - " --pretty Pretty-print the input"; - " --width= Width for --pretty"; + "Upgrade to JsLIGO v2:"; + " --upgrade Upgrade the input to JsLIGO v2"; + " --width= Display width for --upgrade"; + " --namespaces Namespaces as classes with --upgrade"; + " --stdlib Standard library, except Test and Tezos"; "CST printing:"; " --cst Print the CST"; " --no-layout With --cst, do not print the tree layout"; @@ -57,8 +59,10 @@ module Make (LexerParams: LexerLib.CLI.PARAMETERS) : PARAMETERS = (* Pretty-printing options *) - and pretty = ref false - and width = ref (None : int option) + and pretty = ref false + and width = ref (None : int option) + and classes = ref false + and stdlib = ref false (* CST printing options *) @@ -111,7 +115,9 @@ module Make (LexerParams: LexerLib.CLI.PARAMETERS) : PARAMETERS = let specs = Getopt.[ noshort, "mono", set mono true, None; - noshort, "pretty", set pretty true, None; + noshort, "upgrade", set pretty true, None; + noshort, "namespaces", set classes true, None; + noshort, "stdlib", set stdlib true, None; noshort, "width", None, Some set_width; noshort, "cst", set cst true, None; noshort, "no-layout", set layout false, None; @@ -154,7 +160,9 @@ module Make (LexerParams: LexerLib.CLI.PARAMETERS) : PARAMETERS = let opt_wo_arg = String.Set.empty |> add "--mono" - |> add "--pretty" + |> add "--upgrade" + |> add "--namespaces" + |> add "--stdlib" |> add "--cst" |> add "--recovery" |> add "--trace-recovery" @@ -200,6 +208,8 @@ module Make (LexerParams: LexerLib.CLI.PARAMETERS) : PARAMETERS = let mono = !mono and pretty = !pretty and width = !width + and classes = !classes + and stdlib = !stdlib and cst = !cst and layout = !layout and regions = !regions @@ -216,8 +226,10 @@ module Make (LexerParams: LexerLib.CLI.PARAMETERS) : PARAMETERS = (* Options "help", "version" and "cli" are not given. *) let options = [ sprintf "mono = %b" mono; - sprintf "pretty = %b" pretty; + sprintf "upgrade = %b" pretty; sprintf "width = %s" (print_width width); + sprintf "namespaces = %b" classes; + sprintf "stdlib = %b" stdlib; sprintf "cst = %b" cst; sprintf "layout = %b" layout; sprintf "regions = %b" regions; @@ -238,7 +250,7 @@ module Make (LexerParams: LexerLib.CLI.PARAMETERS) : PARAMETERS = let status = match mono, pretty, cst, recovery, trace_recovery with - | _, true, true, _, _ -> `Conflict ("--pretty", "--cst") + | _, true, true, _, _ -> `Conflict ("--upgrade", "--cst") | true, _, _, true, _ -> `Conflict ("--mono", "--recovery") | _, _, _, false, Some _ -> `DependsOn ("--trace-recovery", "--recovery") | _, _, _, _, _ -> status @@ -260,6 +272,8 @@ module Make (LexerParams: LexerLib.CLI.PARAMETERS) : PARAMETERS = let mono = mono let pretty = pretty let width = width + let classes = classes + let stdlib = stdlib let cst = cst let recovery = recovery let trace_recovery = trace_recovery diff --git a/lib/ligo_parser_lib/Options.ml b/lib/ligo_parser_lib/Options.ml index fce2253839..442c0a63fc 100644 --- a/lib/ligo_parser_lib/Options.ml +++ b/lib/ligo_parser_lib/Options.ml @@ -7,6 +7,8 @@ module type S = val mono : bool val pretty : bool val width : int option + val classes : bool + val stdlib : bool val cst : bool val recovery : bool val used_tokens : bool @@ -23,11 +25,13 @@ module MakeDefault (Options : LexerLib.Options.S) = let mono = false let pretty = false + let width = None + let classes = false + let stdlib = false let cst = false let recovery = true let trace_recovery = None let used_tokens = false - let width = None let layout = true let regions = true end diff --git a/lib/ligo_parser_lib/Options.mli b/lib/ligo_parser_lib/Options.mli index dae273d59e..a41ae81679 100644 --- a/lib/ligo_parser_lib/Options.mli +++ b/lib/ligo_parser_lib/Options.mli @@ -7,8 +7,10 @@ module type S = include LexerLib.Options.S val mono : bool (* --mono *) - val pretty : bool (* --pretty *) + val pretty : bool (* --upgrade *) val width : int option (* --width= *) + val classes : bool (* --namespaces *) + val stdlib : bool (* --stdlib *) val cst : bool (* --cst *) val recovery : bool (* --recovery *) val used_tokens : bool (* --used-tokens *) diff --git a/lib/ligo_preprocessor/LowAPI.mli b/lib/ligo_preprocessor/LowAPI.mli index 49f6cec4fb..19eb2790d3 100644 --- a/lib/ligo_preprocessor/LowAPI.mli +++ b/lib/ligo_preprocessor/LowAPI.mli @@ -43,6 +43,8 @@ type nonrec result = (success, error) result type 'src preprocessor = 'src -> result +val mangle : string -> string + module type S = sig (* Preprocessing from various sources *) diff --git a/lib/ligo_preprocessor/LowAPI.mll b/lib/ligo_preprocessor/LowAPI.mll index 4139806ae1..2b1dfb3668 100644 --- a/lib/ligo_preprocessor/LowAPI.mll +++ b/lib/ligo_preprocessor/LowAPI.mll @@ -33,6 +33,21 @@ type result = (success, error) Core.result type 'src preprocessor = 'src -> result +let mangle str = + let name = + let open Str in + str + |> global_replace (regexp_string "_") "_u_" + |> global_replace (regexp_string ".") "_p_" + |> global_replace (regexp_string ":") "_c_" + |> global_replace (regexp_string "\\") "_b_" + |> global_replace (regexp_string "/") "_s_" + |> global_replace (regexp_string "@") "_a_" + |> global_replace (regexp_string "-") "_d_" + |> global_replace (regexp_string "(") "_l_" + |> global_replace (regexp_string ")") "_r_" + in "Mangled_module_" ^ name + module type S = sig (* Preprocessing from various sources *) @@ -314,23 +329,8 @@ module Make (Config : Config.S) (Options : Options.S) = else callback state lexbuf (* Scanning #import directives *) - +(* let import_action ~callback hash_pos state lexbuf = - let mangle str = - let name = - let open Str in - str - |> global_replace (regexp_string "_") "_u_" - |> global_replace (regexp_string ".") "_p_" - |> global_replace (regexp_string ":") "_c_" - |> global_replace (regexp_string "\\") "_b_" - |> global_replace (regexp_string "/") "_s_" - |> global_replace (regexp_string "@") "_a_" - |> global_replace (regexp_string "-") "_d_" - |> global_replace (regexp_string "(") "_l_" - |> global_replace (regexp_string ")") "_r_" - in "Mangled_module_" ^ name - in match Directive.scan_import hash_pos state lexbuf with Error (region, error) -> fail state region error | Ok (state, import, _, _) -> @@ -362,6 +362,7 @@ let import_action ~callback hash_pos state lexbuf = identical to the original #import. *) let () = state#copy_nl lexbuf in callback state lexbuf +*) (* Scanning #if directives *) @@ -601,8 +602,9 @@ rule scan state = parse match id with "include" -> include_action ~callback:scan region#start state lexbuf - | "import" -> - import_action ~callback:scan region#start state lexbuf +(* NOTE: import directive is deprecated *) +(* | "import" -> + import_action ~callback:scan region#start state lexbuf *) | "define" -> define_action ~callback:scan region#start state lexbuf | "undef" -> diff --git a/lib/tree_sitter/api_funcs_desc.ml b/lib/tree_sitter/api_funcs_desc.ml index f6999a6177..ff41feb14e 100644 --- a/lib/tree_sitter/api_funcs_desc.ml +++ b/lib/tree_sitter/api_funcs_desc.ml @@ -1,95 +1,187 @@ +(* This module defines with Ctypes some foreign function bindings from + the tree-sitter API + https://github.com/tree-sitter/tree-sitter/blob/master/lib/include/tree_sitter/api.h +*) + open Ctypes -open Api_types +open Api_types (* See [Api_types_desc.ml] *) -(* functions api defined in tree-sitter *) -(* check: https://github.com/tree-sitter/tree-sitter/blob/master/lib/include/tree_sitter/api.h *) module Functions (S : FOREIGN) = struct open S - (************************) - (*** Section - Parser ***) - (************************) + (* PARSER *) + + (* Create a new parser. + + TSParser *ts_parser_new(void); + *) + let ts_parser_new = + foreign "ts_parser_new" (void @-> returning @@ ptr ts_parser) + + (* Delete the parser, freeing all of the memory that it used. - (* Create a new parser. *) - let ts_parser_new = foreign "ts_parser_new" (void @-> returning @@ ptr ts_parser) + void ts_parser_delete(TSParser *self); + *) + let ts_parser_delete = + foreign "ts_parser_delete" (ptr ts_parser @-> returning void) - (* Delete the parser, freeing all of the memory that it used. *) - let ts_parser_delete = foreign "ts_parser_delete" (ptr ts_parser @-> returning void) + (* Return a boolean indicating whether or not the language was + successfully assigned. - (* - * Returns a boolean indicating whether or not the language was successfully - * assigned. True means assignment succeeded. False means there was a version - * mismatch: the language was generated with an incompatible version of the - * Tree-sitter CLI. Check the language's version using [`ts_language_version`] - * and compare it to this library's [`TREE_SITTER_LANGUAGE_VERSION`] and - * [`TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION`] constants. - *) + bool ts_parser_set_language(TSParser *self, const TSLanguage *language); + *) let ts_parser_set_language = - foreign "ts_parser_set_language" (ptr ts_parser @-> ptr ts_language @-> returning bool) - - (* - * Use the parser to parse some source code stored in one contiguous buffer. - * The first two parameters are the same as in the [`ts_parser_parse`] function - * above. The second two parameters indicate the location of the buffer and its - * length in bytes. - *) + foreign "ts_parser_set_language" + (ptr ts_parser @-> ptr ts_language @-> returning bool) + + (* Use the parser to parse some source code stored in one contiguous + buffer. The first two parameters are the same as in the + [ts_parser_parse] function above. The second two parameters + indicate the location of the buffer and its length in bytes. + + TSTree *ts_parser_parse_string( + TSParser *self, + const TSTree *old_tree, + const char *string, + uint32_t length + ); + *) let ts_parser_parse_string = foreign "ts_parser_parse_string" (ptr ts_parser @-> ptr ts_tree @-> string @-> uint32_t @-> returning @@ ptr ts_tree) - (************************) - (**** Section - Tree ****) - (************************) + (* TREE *) + + (* Create a shallow copy of the syntax tree + + TSTree *ts_tree_copy(const TSTree *self); + *) + let ts_tree_copy = + foreign "ts_tree_copy" (ptr ts_tree @-> returning @@ ptr ts_tree) + + (* Delete the syntax tree, freeing all of the memory that it used + + void ts_tree_delete(TSTree *self); + *) + let ts_tree_delete = + foreign "ts_tree_delete" (ptr ts_tree @-> returning void) - (* - * Create a shallow copy of the syntax tree. This is very fast. - * - * You need to copy a syntax tree in order to use it on more than one thread at - * a time, as syntax trees are not thread safe. - *) - let ts_tree_copy = foreign "ts_tree_copy" (ptr ts_tree @-> returning @@ ptr ts_tree) + (* Get the root node of the syntax tree - (* Delete the syntax tree, freeing all of the memory that it used. *) - let ts_tree_delete = foreign "ts_tree_delete" (ptr ts_tree @-> returning void) + TSNode ts_tree_root_node(const TSTree *self); + *) + let ts_tree_root_node = + foreign "ts_tree_root_node" (ptr ts_tree @-> returning ts_node) - (* Get the root node of the syntax tree. *) - let ts_tree_root_node = foreign "ts_tree_root_node" (ptr ts_tree @-> returning ts_node) + (* NODE *) - (************************) - (**** Section - Node ****) - (************************) + (* Get the node's type as a null-terminated string - (* Get the node's type as a null-terminated string. *) - let ts_node_type = foreign "ts_node_type" (ts_node @-> returning @@ ptr char) + const char *ts_node_type(TSNode self); + *) + let ts_node_type = + foreign "ts_node_type" (ts_node @-> returning @@ ptr char) - (* - * Get an S-expression representing the node as a string. + (* Get an S-expression representing the node as a string - * This string is allocated with `malloc` and the caller is responsible for - * freeing it using `free`. - *) - let ts_node_string = foreign "ts_node_string" (ts_node @-> returning @@ ptr char) + char *ts_node_string(TSNode self); + *) + let ts_node_string = + foreign "ts_node_string" (ts_node @-> returning @@ ptr char) - (* Get the node's number of children. *) - let ts_node_child_count = foreign "ts_node_child_count" (ts_node @-> returning uint32_t) + (* Get the node's number of children - (* Get the node's *named* child at the given index. *) + uint32_t ts_node_child_count(TSNode self); + *) + let ts_node_child_count = + foreign "ts_node_child_count" (ts_node @-> returning uint32_t) + + (* Get the node's child at the given index (0 is the first) + + TSNode ts_node_child(TSNode self, uint32_t child_index); + *) + let ts_node_child = + foreign "ts_node_child" (ts_node @-> uint32_t @-> returning ts_node) + + (* Get the node's *named* child at the given index (0 is the first) + + TSNode ts_node_named_child(TSNode self, uint32_t child_index); + *) let ts_node_named_child = foreign "ts_node_named_child" (ts_node @-> uint32_t @-> returning ts_node) - (* Get the node's number of *named* children. *) + (* Get the node's number of *named* children + + uint32_t ts_node_named_child_count(TSNode self); + *) let ts_node_named_child_count = foreign "ts_node_named_child_count" (ts_node @-> returning uint32_t) - (* Get the node's next / previous sibling. *) - let ts_node_next_sibling = foreign "ts_node_next_sibling" (ts_node @-> returning ts_node) - let ts_node_prev_sibling = foreign "ts_node_prev_sibling" (ts_node @-> returning ts_node) + (* Get the node's next sibling + + TSNode ts_node_next_sibling(TSNode self); + *) + let ts_node_next_sibling = + foreign "ts_node_next_sibling" (ts_node @-> returning ts_node) + + (* Get the node's previous sibling - (* Get the node's next / previous *named* sibling. *) + TSNode ts_node_prev_sibling(TSNode self); + *) + let ts_node_prev_sibling = + foreign "ts_node_prev_sibling" (ts_node @-> returning ts_node) + + (* Get the node's next *named* sibling. + + TSNode ts_node_next_named_sibling(TSNode self); + *) let ts_node_next_named_sibling = foreign "ts_node_next_named_sibling" (ts_node @-> returning ts_node) + (* Get the node's previous *named* sibling + + TSNode ts_node_prev_named_sibling(TSNode self); + *) let ts_node_prev_named_sibling = foreign "ts_node_prev_named_sibling" (ts_node @-> returning ts_node) + + (* Get the node's child by *field* + + TSNode ts_node_child_by_field_name( + TSNode self, + const char *field_name, + uint32_t field_name_length); + *) + let ts_node_child_by_field_name = + foreign + "ts_node_child_by_field_name" + (ts_node @-> string @-> uint32_t @-> returning ts_node) + + (* Checking is a node is null + + bool ts_node_is_null(TSNode); + *) + + let ts_node_is_null = foreign "ts_node_is_null" (ts_node @-> returning bool) + + (* Syntax nodes store their position in the source code both in terms + of raw bytes and row/column coordinates: + + uint32_t ts_node_start_byte(TSNode); + uint32_t ts_node_end_byte(TSNode); + + typedef struct { + uint32_t row; + uint32_t column; + } TSPoint; + + TSPoint ts_node_start_point(TSNode); + TSPoint ts_node_end_point(TSNode); + *) + + let ts_node_start_byte = foreign "ts_node_start_byte" (ts_node @-> returning uint32_t) + let ts_node_end_byte = foreign "ts_node_end_byte" (ts_node @-> returning uint32_t) + let ts_node_start_point = foreign "ts_node_start_point" (ts_node @-> returning ts_point) + let ts_node_end_point = foreign "ts_node_end_point" (ts_node @-> returning ts_point) end diff --git a/lib/tree_sitter/api_types_desc.ml b/lib/tree_sitter/api_types_desc.ml index 64f1ac4005..6fabedd24a 100644 --- a/lib/tree_sitter/api_types_desc.ml +++ b/lib/tree_sitter/api_types_desc.ml @@ -1,61 +1,84 @@ +(* This module defines with Ctypes some types from the tree-sitter API + https://github.com/tree-sitter/tree-sitter/blob/master/lib/include/tree_sitter/api.h +*) + open Ctypes -(* structs defined in tree-sitter *) -(* check: https://github.com/tree-sitter/tree-sitter/blob/master/lib/include/tree_sitter/api.h *) module Types (S : Ctypes.TYPE) = struct - open S + (*open S*) + + (* Some basic types *) - (* ////////// Types ////////// *) + let ts_symbol : Unsigned.uint16 typ = uint16_t + let ts_state_id : Unsigned.uint16 typ = uint16_t + let ts_field_id : Unsigned.uint16 typ = uint16_t - let ts_symbol = uint16_t - let ts_state_id = uint16_t - let ts_field_id = uint16_t + (* Single source location: - (* ////////// struct TSPoint ////////// *) + typedef struct TSPoint { + uint32_t row; + uint32_t column; + } TSPoint; + *) type ts_point let ts_point : ts_point structure typ = structure "TSPoint" - let row = field ts_point "row" uint - let column = field ts_point "column" uint + let row : _ field = field ts_point "row" uint + let column : _ field = field ts_point "column" uint let () = seal ts_point - (* ////////// struct TSRange ////////// *) + (* Range in the source + + typedef struct TSRange { + TSPoint start_point; + TSPoint end_point; + uint32_t start_byte; + uint32_t end_byte; + } TSRange; + *) type ts_range let ts_range : ts_range structure typ = structure "TSRange" - let start_point = field ts_range "start_point" ts_point - let end_point = field ts_range "end_point" ts_point - let start_byte = field ts_range "start_byte" uint32_t - let end_byte = field ts_range "end_byte" uint32_t + let start_point : _ field = field ts_range "start_point" ts_point + let end_point : _ field = field ts_range "end_point" ts_point + let start_byte : _ field = field ts_range "start_byte" uint32_t + let end_byte : _ field = field ts_range "end_byte" uint32_t let () = seal ts_range - (* ////////// struct TSLanguage ////////// *) + (* The source language (TSLanguage) *) type ts_language let ts_language : ts_language structure typ = structure "TSLanguage" - (* ////////// struct TSTree ////////// *) + (* The syntax tree (TSTree) *) type ts_tree let ts_tree : ts_tree structure typ = structure "TSTree" - (* ////////// struct TSParser ////////// *) + (* The parser (TSParser) *) type ts_parser let ts_parser : ts_parser structure typ = structure "TSParser" - (* ////////// struct TSNode ////////// *) + (* Node in the syntax tree + + typedef struct TSNode { + uint32_t context[4]; + const void *id; + const TSTree *tree; + } TSNode; + *) type ts_node let ts_node : ts_tree structure typ = structure "TSNode" - let context = field ts_node "context" (array 4 uint32_t) - let id = field ts_node "id" (ptr void) - let tree = field ts_node "tree" (ptr ts_tree) + let context : _ field = field ts_node "context" (array 4 uint32_t) + let id : _ field = field ts_node "id" (ptr void) + let tree : _ field = field ts_node "tree" (ptr ts_tree) let () = seal ts_node end diff --git a/lib/tree_sitter/dune b/lib/tree_sitter/dune index 4d925a1516..62d1b7ea81 100644 --- a/lib/tree_sitter/dune +++ b/lib/tree_sitter/dune @@ -1,5 +1,6 @@ (library (name tree_sitter) + (public_name ligo.tree_sitter) (libraries ctypes ctypes.foreign) (ctypes (external_library_name tree-sitter) @@ -21,7 +22,5 @@ (functor Api_funcs_desc)) (generated_types Api_types) (generated_entry_point Api)) - (flags - (:standard -w -27))) - -;; unused variable + (flags + (:standard -w -27))) ;; unused variable diff --git a/lib/tree_sitter_example/example.ml b/lib/tree_sitter_example/example.ml index e26a8eea52..49bf073ae2 100644 --- a/lib/tree_sitter_example/example.ml +++ b/lib/tree_sitter_example/example.ml @@ -1,14 +1,34 @@ -open Tree_sitter.Api -open Types -open Functions -open Tree_sitter_typescript.Api -open Functions +(* This module is a minimal example of parsing some TypeScript program *) + +(* Tree-sitter ctypes-APIs for types and related functions *) + +module TS_types = Tree_sitter.Api.Types +module TS_fun = Tree_sitter.Api.Functions + +(* Integers needed by the tree-sitter APIs above *) + +module UInt32 = Unsigned.UInt32 + +(* Tree-sitter API for TypeScript *) + +let tree_sitter_typescript = Tree_sitter_typescript.Api.Functions.tree_sitter_typescript + +(* ocaml-ctypes types and bindings (only global module opening) *) + open Ctypes -open Unsigned -(* converts from c string of type `char *` to ocaml string of type `string` *) -let string_of_ptr_char ptr = - let rec get_length p = if !@p = '\000' then 0 else 1 + get_length (p +@ 1) in +(* Type aliases for trees *) + +type ts_tree = TS_types.ts_tree structure +type ts_tree_ptr = TS_types.ts_tree structure Ctypes_static.ptr + +(* Converting C strings of type 'char*' to OCaml strings of type + [string]. *) + +let string_of_char_ptr (ptr : char ptr) : string = + let rec get_length (p : char ptr) : int = + if !@p = '\000' then 0 else 1 + get_length (p +@ 1) + in let length = get_length ptr in let buffer = Bytes.create length in for i = 0 to length - 1 do @@ -16,45 +36,62 @@ let string_of_ptr_char ptr = done; Bytes.to_string buffer -let print_node node = - let ptr_char = ts_node_string node in - Printf.printf "\n\n TypeScript CST : %s\n\n" @@ string_of_ptr_char ptr_char +(* Printing the tree *) + +let print_node (node : ts_tree) : unit = + let ptr_char = TS_fun.ts_node_string node in + Printf.printf "%s\n%!" @@ string_of_char_ptr ptr_char + +(* Converting a node to an OCaml string *) -let ts_node_type_string node = string_of_ptr_char @@ ts_node_type node +let string_of_ts_node_type (node : ts_tree) : string = + string_of_char_ptr @@ TS_fun.ts_node_type node -let parse_typescript_string source_code = - let parser = ts_parser_new () in - let language = tree_sitter_typescript () in - let _ = ts_parser_set_language parser language in - let null_tree = from_voidp ts_tree null in - let tree = - ts_parser_parse_string +(* Parsing a string expected to contain a valid TypeScript program *) + +let parse_typescript_string (source_code : string) : ts_tree_ptr = + let parser = TS_fun.ts_parser_new () + and language = tree_sitter_typescript () in + let (_ : bool) = TS_fun.ts_parser_set_language parser language in + (*[true]*) + let null_tree = from_voidp TS_types.ts_tree null in + let parse_tree = + TS_fun.ts_parser_parse_string parser null_tree source_code (UInt32.of_int @@ String.length source_code) in - ts_parser_delete parser; - tree + TS_fun.ts_parser_delete parser; + parse_tree + +(* The example per se *) let () = let code = "[1, null]" in - let tree = parse_typescript_string code in - (* extract the node *) - let root_node = ts_tree_root_node tree in - let expr_stmt_node = ts_node_named_child root_node (UInt32.of_int 0) in - let array_node = ts_node_named_child expr_stmt_node (UInt32.of_int 0) in - let number_node = ts_node_named_child array_node (UInt32.of_int 0) in - let null_node = ts_node_named_child array_node (UInt32.of_int 1) in - (* get the node type *) - assert (ts_node_type_string root_node = "program"); - assert (ts_node_type_string expr_stmt_node = "expression_statement"); - assert (ts_node_type_string array_node = "array"); - assert (ts_node_type_string number_node = "number"); - assert (ts_node_type_string null_node = "null"); - (* get the child count *) - assert (ts_node_child_count root_node = UInt32.of_int 1); - assert (ts_node_child_count array_node = UInt32.of_int 5); - assert (ts_node_child_count number_node = UInt32.of_int 0); - print_node root_node; - ts_tree_delete tree + (* Parsing the code *) + let tree : ts_tree_ptr = parse_typescript_string code in + (* Extracting all the nodes *) + let program_node : ts_tree = TS_fun.ts_tree_root_node tree in + let expr_stmt_node : ts_tree = + TS_fun.ts_node_named_child program_node (UInt32.of_int 0) + in + let array_node : ts_tree = + TS_fun.ts_node_named_child expr_stmt_node (UInt32.of_int 0) + in + let number_node : ts_tree = TS_fun.ts_node_named_child array_node (UInt32.of_int 0) in + let null_node : ts_tree = TS_fun.ts_node_named_child array_node (UInt32.of_int 1) in + (* Checking the node types *) + assert (string_of_ts_node_type program_node = "program"); + assert (string_of_ts_node_type expr_stmt_node = "expression_statement"); + assert (string_of_ts_node_type array_node = "array"); + assert (string_of_ts_node_type number_node = "number"); + assert (string_of_ts_node_type null_node = "null"); + (* Checking the child counts *) + assert (TS_fun.ts_node_child_count program_node = UInt32.of_int 1); + assert (TS_fun.ts_node_child_count expr_stmt_node = UInt32.of_int 1); + assert (TS_fun.ts_node_child_count array_node = UInt32.of_int 5); + assert (TS_fun.ts_node_child_count number_node = UInt32.of_int 0); + (* Printing the tree and freeing the memory *) + print_node program_node; + TS_fun.ts_tree_delete tree diff --git a/lib/tree_sitter_typescript/api_funcs_desc.ml b/lib/tree_sitter_typescript/api_funcs_desc.ml index 662f44d8e4..4060d57233 100644 --- a/lib/tree_sitter_typescript/api_funcs_desc.ml +++ b/lib/tree_sitter_typescript/api_funcs_desc.ml @@ -1,8 +1,9 @@ open Ctypes -open Tree_sitter.Api_types -module Functions (S : FOREIGN) = struct - open S +let ts_language = Tree_sitter.Api_types.ts_language + +module Functions (F: FOREIGN) = struct + open F let tree_sitter_typescript = foreign "tree_sitter_typescript" (void @-> returning (ptr ts_language)) diff --git a/lib/tree_sitter_typescript/api_types_desc.ml b/lib/tree_sitter_typescript/api_types_desc.ml index 3958c7717a..017089d1bd 100644 --- a/lib/tree_sitter_typescript/api_types_desc.ml +++ b/lib/tree_sitter_typescript/api_types_desc.ml @@ -1 +1 @@ -module Types (S : Ctypes.TYPE) = struct end +module Types (S: Ctypes.TYPE) = struct end diff --git a/lib/tree_sitter_typescript/dune b/lib/tree_sitter_typescript/dune index f6ca518050..6f779ea35b 100644 --- a/lib/tree_sitter_typescript/dune +++ b/lib/tree_sitter_typescript/dune @@ -1,5 +1,6 @@ (library (name tree_sitter_typescript) + (public_name ligo.tree_sitter_typescript) (libraries ctypes ctypes.foreign tree_sitter) (ctypes (external_library_name tree-sitter-typescript) @@ -23,7 +24,5 @@ (functor Api_funcs_desc)) (generated_types Api_types) (generated_entry_point Api)) - (flags - (:standard -w -27))) - -;; unused variable + (flags + (:standard -w -27))) ;; unused variable diff --git a/lib/typescript_ast/Tests/test.ts b/lib/typescript_ast/Tests/test.ts new file mode 100644 index 0000000000..e84de34f2f --- /dev/null +++ b/lib/typescript_ast/Tests/test.ts @@ -0,0 +1,202 @@ +let y! : number; +type t = m.n.o.p; +type t = {+readonly [T in number as U] ?: V} +type t = {readonly [x: number] -?: T} +type t = {public static override readonly async get foo? (x: T) : T} +type t = {abstract new (x: T) : T} +type t = {(x: T = v) : X is Y} +type t = {(x: T) : asserts F} +type t = {public static override readonly x?: number} +type t = typeof f["goo"][string]; +type t = typeof f.#foo; +type t = typeof f(6); +type t = typeof x?.[number]; +type t = typeof import; +type t = * +type t = +10; +type t = T[number]; +type t = A extends B ? {} : number; +type t = number & string; +type t = & string; +type t = (x: T) => T is number; +type t = (x: T) => asserts U is V; +type t = abstract new (@foo @bar("baz") public override readonly this:T=v, y?:T) => T; +type t = infer u extends {}; +type t = infer u; +type a = [...[{y=4}, x=5] : c]; +type a = [...{...f, x, k:v, y=3} : c]; + +{ +type t = [x: T, y?: number, string?, ...string, any[]]; +{} +} +type t = readonly number; +f <| (number)>("", x); +f ("", x); +[...x, 6]; +const x = (1+y, w)!, z=4, t; +const x = new.target + +const x = class { + @foo @bar f() {}; + g() : T; + static {}; + abstract h(); + declare public f = 6; +} + +const x = async x => {} +const y = (x: T) : T => x +const f = function (x: T) : T { return x } +const x = { a: 5, ...f(), g() {}, z } +const a = (a.b.c)?.z +const c = (1) +const d = (1, x, f()) +1, x, f(); +const b = (a.b.c)?.[1+x, 3] +const b = x[3] +import m = a.b.m +import m = n +interface I extends m.J.t, K {} +const enum x {a, b=4, c, d=3} +namespace N +namespace N.M {} +namespace "Foo" {} +const x = y +const x = y +const x = 5 satisfies number +const x = 5 as const +const x = y as number + +function* generator(i) { + yield *g; + yield i + 10; + yield; +} + +const c = new C; +const c = new C +const c = new C(4) +const x = ++y +const x = --y +const x = y++ +const x = y-- +const t = true ? x : 0 +const x = (x ^ y) >= z +const {[x]:[]} = {} + +async function foo(name) { + console.log(name, "start"); + await console.log(name, "middle"); + console.log(name, "end"); +} + +x += 4 +x = 5 +module N +module N.M {} +module "Foo" {} +@foo class T extends V, Y implements A, B {} +@foo abstract class T extends V, Y implements A, B {} + +async function f () +var x = (1+y, w)!, z +label: throw C() +; +return (1+y, w)!, z +return +continue label +break label +with (1+x) {using y=x; z=t} + +try {} +try {} catch {} +try {} catch ([x,y]) {} +try {} catch (x: T) {} finally {} +do {} while (true) +while (true) {} + +switch (x) { + case 0: ; + default: yield; break; + case x,y: break; + default: ; +} + +for (;;); +for (;; i++); +for (; i<6; i++); +for (i=0; i<6; i++); +for (i=0; i<6; i++) {} +if (x) {} +if (x) {} else { f() } +declare const x = 4 +declare global {} +declare module.m : T +declare module M {} +interface C { } +declare class C { } + +declare module M { + interface C1 { } + class C1 { } + interface C1 { } + interface C1 { } + export class C2 { } +} + +declare module M { + export interface C2 { } +} + +async function* f () {} +async function* () {} +const x = async function* f () {} + +switch (`abc${0}abc`) { + case `123`: + case `abc${0}abc`: + `def${1}def`, a; 1; +} + +const x = @foo class T extends V, Y implements A, B {} +for (const [i,j] of a, b) {} +for await (var v in c = d) {} +for ((x) of e) {} +for ({} in s) {} + +export * from "/path/foo.ts"; +export * as foo from "/path/foo.ts"; +export * as "foo" from "/path/foo.ts"; +export {}; +export {foo as bar, "foo" as "bar"}; +export {} from "/path/foo.ts"; +export {foo as bar, "foo" as "bar"} from "/path/foo.ts"; + +export type t = number; +export class C {}; // An empty statement is parsed at the comma: why? +export default type t = number; +export default x+1; +export type {t, "u"}; +export type {t} from "/path/foo.ts"; +export = x+1; +export as namespace x; + +debugger; + +import * as x from "./hello.ts"; +import {type t, x as y} from "./hello.ts"; +import type t from "./hello.ts"; +import x, * as y from "./hello.ts"; +import x, {z as y} from "./hello.ts"; +import typeof t = require("fs"); +import type "./hello.ts" with {}; +import x from "./hello.ts" with {}; +import helloWorld from "./hello.js"; +import { pi, phi, absolute } from "./maths.js"; +import { pi as π } from "./maths.js"; +import RandomNumberGenerator, { pi as π } from "./maths.js"; +import { Cat, Dog } from "./animal.js"; +import * as math from "./maths.js"; +import type { Cat, Dog } from "./animal.js"; +import { createCatName, type Cat, type Dog } from "./animal.js"; diff --git a/lib/typescript_ast/ast.ml b/lib/typescript_ast/ast.ml new file mode 100644 index 0000000000..356d753401 --- /dev/null +++ b/lib/typescript_ast/ast.ml @@ -0,0 +1,3901 @@ +(** Abstract Syntax Tree (AST) for TypeScript + + We used the JavaScript tree-sitter grammar and the TypeScript + tree-sitter grammar as reference. The excerpts from those grammars + are copied in a comment before the relevant AST nodes. + + For the sake of the LSP, we retain keywords in the tree. + *) + +[@@@warning "-30"] (* Duplicate record field names *) + +open Core + +let ( let* ) v f = Result.bind v ~f + +(* DEPENDENCIES *) + +type 'a ne_list = 'a Nonempty_list.t + +module Wrap = Lexing_shared.Wrap +module Region = Simple_utils.Region + +type 'a wrap = 'a Wrap.t + +(* Literals *) + +type keyword = string wrap +type symbol = string wrap +type identifier = string wrap +type string_literal = string wrap +type hash_name = string wrap + +(* Numbers + + Note: Only [dec_literal] used [Q.t]: the values for the other types + are meant to be translated into Michelson bytes. *) + +type hex_literal = (string * Hex.t) wrap +type bin_literal = (string * Hex.t) wrap +type oct_literal = (string * Hex.t) wrap +type dec_literal = (string * Q.t) wrap +type big = bool + +type number = + | Hex of hex_literal * big + | Bin of bin_literal * big + | Oct of oct_literal * big + | Dec of dec_literal * big + +(* Keywords + + TODO: Use unique data constructors for each keyword. +*) + +type kwd_infer = keyword +type kwd_keyof = keyword +type kwd_meta = keyword +type kwd_target = keyword +type kwd_false = keyword +type kwd_true = keyword +type kwd_super = keyword +type kwd_null = keyword +type kwd_satisfies = keyword +type kwd_yield = keyword +type kwd_new = keyword +type kwd_instanceof = keyword +type kwd_implements = keyword +type kwd_assert = keyword +type kwd_as = keyword +type kwd_async = keyword +type kwd_function = keyword +type kwd_override = keyword +type kwd_readonly = keyword +type kwd_public = keyword +type kwd_private = keyword +type kwd_protected = keyword +type kwd_set = keyword +type kwd_get = keyword +type kwd_all = keyword +type kwd_static = keyword +type kwd_this = keyword +type kwd_is = keyword +type kwd_class = keyword +type kwd_const = keyword +type kwd_constraint = keyword +type kwd_let = keyword +type kwd_undefined = keyword +type kwd_abstract = keyword +type kwd_declare = keyword +type kwd_accessor = keyword +type kwd_global = keyword +type kwd_module = keyword +type kwd_enum = keyword +type kwd_import = keyword +type kwd_interface = keyword +type kwd_extends = keyword +type kwd_namespace = keyword +type kwd_type = keyword +type kwd_using = keyword +type kwd_return = keyword +type kwd_switch = keyword +type kwd_case = keyword +type kwd_default = keyword +type kwd_throw = keyword +type kwd_while = keyword +type kwd_with = keyword +type kwd_any = keyword +type kwd_number = keyword +type kwd_boolean = keyword +type kwd_string = keyword +type kwd_symbol = keyword +type kwd_unique_symbol = keyword +type kwd_void = keyword +type kwd_unknown = keyword +type kwd_never = keyword +type kwd_object = keyword +type kwd_asserts = keyword +type kwd_debugger = keyword +type kwd_break = keyword +type kwd_continue = keyword +type kwd_do = keyword +type kwd_export = keyword +type kwd_for = keyword +type kwd_from = keyword +type kwd_await = keyword +type kwd_var = keyword +type kwd_in = keyword +type kwd_of = keyword +type kwd_if = keyword +type kwd_else = keyword +type kwd_typeof = keyword +type kwd_try = keyword +type kwd_catch = keyword +type kwd_require = keyword +type kwd_delete = keyword +type kwd_finally = keyword + +(* Symbols + + TODO: Use unique data constructors for each symbol. +*) + +type sym_arrow = symbol (* "=>" *) +type sym_lbracket = symbol (* "[" *) +type sym_rbracket = symbol (* "]" *) +type sym_ampersand = symbol (* "&" *) +type sym_vbar = symbol (* "|" *) +type sym_qmark = symbol (* "?" *) +type sym_equal = symbol (* "=" *) +type sym_colon = symbol (* ":" *) +type sym_asterisk = symbol (* "*" *) +type sym_ellipsis = symbol (* "..." *) +type sym_plus = symbol (* "+" *) +type sym_minus = symbol (* "-" *) +type sym_emark = symbol (* "!" *) +type sym_dot = symbol (* "." *) +type sym_optional_chain = symbol (* "?." *) +type sym_lparen = symbol (* "(" *) +type sym_rparen = symbol (* ")" *) +type sym_tilde = symbol (* "~" *) +type sym_incr = symbol (* "++" *) +type sym_decr = symbol (* "--" *) +type sym_bquote = symbol (* "`" *) + +(* Template string *) + +type template_string_fragment = + | String_fragment of string_literal + | Escape_sequence of string_literal + | Template_substitution of string wrap + +type template_string = sym_bquote * template_string_fragment list * sym_bquote + +(* Compound constructs *) + +type 'a enclosed = + { opening : symbol + ; contents : 'a + ; closing : symbol + } + +type 'a braces = Braces of 'a enclosed wrap +type 'a chevrons = Chevrons of 'a enclosed wrap +type 'a brackets = Brackets of 'a enclosed wrap +type 'a parens = Parens of 'a enclosed wrap + +(** The Abstract Syntax Tree + + The related grammar rule is given by: + + JavaScript + {@js[ + program: $ => seq( + optional($.hash_bang_line), + repeat($.statement)) + ]} +*) +type program = statements + +and t = program +and statements = statement ne_list wrap option + +(** DECLARATIONS + + Declarations, when they are valid, extend the current scope with + new types and values (including functions). + + The related grammar rules are given by: + + JavaScript + {@js[ + declaration: $ => choice( + $.function_declaration, + $.generator_function_declaration, + $.class_declaration, + $.lexical_declaration, + $.variable_declaration) + ]} + + TypeScript + {@js[ + declaration: ($, previous) => choice( + previous, + $.function_signature, + $.abstract_class_declaration, + $.module, + prec('declaration', $.internal_module), + $.type_alias_declaration, + $.enum_declaration, + $.interface_declaration, + $.import_alias, + $.ambient_declaration), + + module: $ => seq('module', $._module) + ]} +*) +and declaration = + | D_function_declaration of function_declaration wrap + | D_generator_function_declaration of generator_function_declaration wrap + | D_class_declaration of class_declaration wrap + | D_lexical_declaration of lexical_declaration wrap + | D_variable_declaration of variable_declaration wrap + | D_function_signature of function_signature wrap + | D_abstract_class_declaration of abstract_class_declaration wrap + | D_module of module_declaration wrap + | D_internal_module of internal_module wrap + | D_type_alias_declaration of type_alias_declaration wrap + | D_enum_declaration of enum_declaration wrap + | D_interface_declaration of interface_declaration wrap + | D_import_alias of import_alias wrap + | D_ambient_declaration of ambient_declaration wrap + +(** Function Declaration + + Function declarations introduce functions in the current scope. + + Example: {@js[function f (x: T) : T { return x; };]} + + The related grammar rules are given by: + + JavaScript + {@js[ + function_declaration: $ => prec.right('declaration', seq( + optional('async'), 'function', + field('name', $.identifier), + $._call_signature, + field('body', $.statement_block), + optional($._automatic_semicolon))), + + statement_block: $ => prec.right(seq( + '{', repeat($.statement), '}', optional($._automatic_semicolon))) + ]} + + See [function_signature] below. +*) +and function_declaration = + { fun_sig : function_signature + ; body : statement_block + } + +(** Function Signature + + A function signature introduces a functional type in the current + type (including type and value parameters, if any). + + Example: {@js[function f (x: T) : T;]} + + The related grammar rules are given by: + + TypeScript + {@js[ + function_signature: $ => seq( + optional('async'), 'function', + field('name', $.identifier), + $._call_signature, + choice($._semicolon, $._function_signature_automatic_semicolon)), + + _semicolon: $ => choice($._automatic_semicolon, ';'), + + _call_signature: $ => seq( + field('type_parameters', optional($.type_parameters)), + field('parameters', $.formal_parameters), + field('return_type', optional( + choice($.type_annotation, + $.asserts_annotation, + $.type_predicate_annotation)))), + + formal_parameters: $ => seq( + '(', optional(seq(commaSep1($._formal_parameter), optional(','))), ')'), + + _formal_parameter: $ => choice( + $.required_parameter, + $.optional_parameter), + + required_parameter: $ => seq( + $._parameter_name, + field('type', optional($.type_annotation)), + optional($._initializer)), + + _initializer: $ => seq('=', field('value', $.expression)), + + _parameter_name: $ => seq( + repeat(field('decorator', $.decorator)), + optional($.accessibility_modifier), + optional($.override_modifier), + optional('readonly'), + field('pattern', choice($.pattern, $.this))), + + accessibility_modifier: _ => choice('public', 'private', 'protected'), + + override_modifier: _ => 'override', + + optional_parameter: $ => seq( + $._parameter_name, + '?', + field('type', optional($.type_annotation)), + optional($._initializer)), + + type_predicate_annotation: $ => seq(seq(':', $.type_predicate)), // Ouch + + type_predicate: $ => seq( + field('name', choice( + $.identifier, + $.this, + alias($.predefined_type, $.identifier))), + 'is', + field('type', $.type)) + ]} + *) +and function_signature = + { kwd_async : kwd_async option + ; kwd_function : kwd_function + ; name : identifier + ; call_sig : call_signature wrap + } + +and call_signature = + { type_parameters : type_parameters option + ; parameters : formal_parameters + ; return_type : call_return_type option + } + +and type_parameters = type_parameter wrap list chevrons +and formal_parameters = formal_parameter wrap list parens + +and formal_parameter = + { parameter_name : parameter_name wrap + ; optional : sym_qmark option + ; type_opt : type_annotation option + ; default : (sym_equal * expression) option + } + +and parameter_name = + { decorators : decorators + ; access : accessibility_modifier option + ; kwd_override : kwd_override option + ; kwd_readonly : kwd_readonly option + ; pattern : parameter_pattern + } + +and decorators = decorator list + +and parameter_pattern = + | Parameter_pattern of pattern + | Parameter_this of kwd_this + +and call_return_type = + | Type_annotation of type_annotation + | Asserts_annotation of asserts_annotation + | Type_predicate_annotation of type_predicate wrap + +and type_annotation = sym_colon * type_expr + +and type_predicate = + { name : type_predicate_name + ; kwd_is : kwd_is + ; type_expr : type_expr + } + +and type_predicate_name = + | Type_predicate_identifier of identifier + | Type_predicate_this of kwd_this + | Type_predicate_type of predefined_type + +(** Generator Function Declaration + + A generator function is a function whose execution can be internally + suspended with a 'yield' instruction, and later resumed by the + caller (at the suspension point). + + Example: + {@js[ + function* generator(i) { + yield i; + yield i + 10; + } + ]} + + The related grammar rule is given by: + + JavaScript + {@js[ + generator_function_declaration: $ => prec.right('declaration', seq( + optional('async'), 'function', '*', + field('name', $.identifier), + $._call_signature, + field('body', $.statement_block), + optional($._automatic_semicolon))) + ]} + *) +and generator_function_declaration = sym_asterisk * function_declaration + +(** Class Declaration + + A class declaration instroduces a class type in the current scope. + + Example: + {@js[ + class Pair { + a : T; + b : U; + constructor (a: T, b: U) { + this.a = a; + this.b = b + } + } + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + class_declaration: $ => prec.left('declaration', seq( + repeat(field('decorator', $.decorator)), + 'class', + field('name', $._type_identifier), + field('type_parameters', optional($.type_parameters)), + optional($.class_heritage), + field('body', $.class_body), + optional($._automatic_semicolon))), + + type_parameters: $ => + seq('<', commaSep1($.type_parameter), optional(','), '>'), + + type_parameter: $ => seq( + optional('const'), + field('name', $._type_identifier), + field('constraint', optional($.constraint)), + field('value', optional($.default_type))), + + constraint: $ => seq(choice('extends', ':'), $.type), // What is ':'? + + default_type: $ => seq('=', $.type), + + class_heritage: $ => choice( + seq($.extends_clause, optional($.implements_clause)), + $.implements_clause), + + extends_clause: $ => seq('extends', commaSep1($._extends_clause_single)), + + _extends_clause_single: $ => prec('extends', seq( + field('value', $.expression), + field('type_arguments', optional($.type_arguments)))), + + implements_clause: $ => seq('implements', commaSep1($.type)), + + type_arguments: $ => seq('<', commaSep1($.type), optional(','), '>'), + + class_body: $ => seq( + '{', + repeat(choice( + seq(repeat(field('decorator', $.decorator)), + $.method_definition, + optional($._semicolon)), + seq($.method_signature, + choice($._function_signature_automatic_semicolon, ',')), + $.class_static_block, + seq(choice( + $.abstract_method_signature, + $.index_signature, + $.method_signature, + $.public_field_definition), + choice($._semicolon, ',')), + ';')), + '}') + ]} + + JavaScript + {@js[ + class_static_block: $ => seq( + 'static', + optional($._automatic_semicolon), + field('body', $.statement_block)) + ]} + *) +and class_declaration = + { decorators : decorators + ; kwd_class : kwd_class + ; name : type_identifier + ; type_parameters : type_parameters option + ; class_heritage : class_heritage option + ; body : class_body + } + +and class_heritage = + | Extends_clause of extends_clause * implements_clause option + | Implements_clause of implements_clause + +and extends_clause = kwd_extends * extends_clause_single wrap ne_list + +and extends_clause_single = + { value : expression + ; type_arguments : type_arguments option + } + +and type_arguments = type_expr ne_list chevrons +and implements_clause = kwd_implements * type_expr ne_list + +and type_parameter = + { kwd_const : kwd_const option + ; name : type_identifier + ; constraint_expr : (kwd_constraint * type_expr) option + ; default_type : (sym_equal * type_expr) option + } + +and class_body = class_member list braces + +and class_member = + | Method_definition of decorators * method_definition wrap + | Method_signature of method_signature wrap + | Call_static_block of (kwd_static * statement_block) + | Abstract_method_signature of abstract_method_signature wrap + | Index_signature of index_signature wrap + | Public_field_definition of public_field_definition wrap + +(** Method Signature + + A method signature is the type of a method in a class. + + Example: {@js[public m? (x: number) : number]} + + The related grammar rules are given by: + + TypeScript + {@js[ + method_signature: $ => seq( + optional($.accessibility_modifier), + optional('static'), + optional($.override_modifier), + optional('readonly'), + optional('async'), + optional(choice('get', 'set', '*')), + field('name', $._property_name), + optional('?'), + $._call_signature) + ]} + + JavaScript + {@js[ + _property_name: $ => choice( + alias(choice($.identifier, $._reserved_identifier), + $.property_identifier), + $.private_property_identifier, + $.string, + $.number, + $.computed_property_name), + + computed_property_name: $ => seq('[', $.expression, ']') + ]} +*) +and method_signature = + { access : accessibility_modifier option + ; scope : method_scope + ; kwd_async : kwd_async option + ; set_get_all : set_get_all option + ; name : property_name + ; optional : sym_qmark option + ; call_sig : call_signature wrap + } + +and accessibility_modifier = + | Public of kwd_public + | Private of kwd_private + | Protected of kwd_protected + +and set_get_all = + | Set of kwd_set + | Get of kwd_get + | All of sym_asterisk + +and method_scope = + { kwd_static : kwd_static option + ; kwd_override : kwd_override option + ; kwd_readonly : kwd_readonly option + } + +and property_name = + | Property_identifier of identifier (* Also reserved identifiers *) + | Private_property_identifier of private_property_identifier + | String of string_literal + | Number of number + | Computed_property_name of expression brackets + +and private_property_identifier = hash_name + +(** Lexical Declaration + + Lexical declarations are declarations of let- or + const-variables. When achieved by means of object & tuple patterns, + the variables they contain are introduced in the current scope. + + Example: {@js[const {x, y} = z;]} + + The related grammar rules are given by: + + JavaScript + {@js[ + lexical_declaration: $ => seq( + field('kind', choice('let', 'const')), + commaSep1($.variable_declarator), + $._semicolon), + + _destructuring_pattern: $ => choice($.object_pattern, $.array_pattern), + + object_pattern: $ => prec('object', seq( + '{', + commaSep(optional(choice( + $.pair_pattern, + $.rest_pattern, + $.object_assignment_pattern, + alias(choice($.identifier, $._reserved_identifier), + $.shorthand_property_identifier_pattern)))), + '}')), + + pair_pattern: $ => seq( + field('key', $._property_name), + ':', + field('value', choice($.pattern, $.assignment_pattern))), + + rest_pattern: $ => prec.right(seq('...', $._lhs_expression)), + + _lhs_expression: $ => choice( + $.member_expression, + $.subscript_expression, + $._identifier, // identifier + undefined + alias($._reserved_identifier, $.identifier), + $._destructuring_pattern), + + object_assignment_pattern: $ => seq( + field('left', choice( + alias(choice($._reserved_identifier, $.identifier), + $.shorthand_property_identifier_pattern), + $._destructuring_pattern)), + '=', + field('right', $.expression)), + + array_pattern: $ => seq( + '[', commaSep(optional(choice($.pattern, $.assignment_pattern))), ']') + }] + + TypeScript + {@js[ + variable_declarator: $ => choice( + seq( + field('name', choice($.identifier, $._destructuring_pattern)), + field('type', optional($.type_annotation)), + optional($._initializer), + ), + prec('declaration', seq( + field('name', $.identifier), + '!', + field('type', $.type_annotation)))), + + _lhs_expression: ($, previous) => choice(previous, $.non_null_expression), + + non_null_expression: $ => + prec.left('unary', seq($.expression, '!')) + ]} + *) +and lexical_declaration = + { kind : let_or_const + ; decls : variable_declarator ne_list + } + +and let_or_const = + | Let of kwd_let + | Const of kwd_const + +and variable_declarator = + | Var_decl of var_decl_lhs wrap + | Var_decl_assertion of var_decl_assertion + +and var_decl_assertion = identifier * sym_qmark * type_annotation + +and var_decl_lhs = + { var_names : lhs_pattern + ; var_type : type_annotation option + ; default : (sym_equal * expression) option + } + +and lhs_pattern = + | Decl_ident of identifier + | Decl_pattern of destructuring_pattern + +and destructuring_pattern = + | Pattern_object of object_pattern + | Pattern_array of array_pattern + +and object_pattern = member_pattern list braces + +and member_pattern = + | Member_pair_pattern of pair_pattern wrap + | Member_rest_pattern of rest_pattern wrap + | Member_object_assignment of object_assignment_pattern wrap + | Member_shorthand_property of identifier (* Including reserved identifiers *) + +and pair_pattern = (property_name, pair_value_pattern) key_value + +and pair_value_pattern = + | Pair_value of pattern + | Pair_value_assignment of assignment_pattern wrap + +and rest_pattern = + { sym_ellipsis : sym_ellipsis + ; expression : lhs_expression + } + +and lhs_expression = + | Member_expression of member_expression wrap + | Subscript_expression of subscript_expression wrap + | Identifier of identifier (* Including reserved identifiers *) + | Undefined of kwd_undefined + | Pattern of destructuring_pattern + | Non_null_expression of expression + +and object_assignment_pattern = + { left : object_lhs_pattern + ; sym_equal : sym_equal + ; right : expression + } + +and object_lhs_pattern = lhs_pattern +and array_pattern = array_cell_pattern list brackets + +and array_cell_pattern = + (* Isomorphic to [pair_value_pattern]. *) + | Cell_pattern of pattern + | Cell_assignment of assignment_pattern wrap + +(** Variable Declaration + + Variable declarations introduce in the current scope mutable + variable. + + Example: {@js[var x : number = 5;]} + + The related grammar rule is given by: + + JavaScript + {@js[ + variable_declaration: $ => + seq('var', commaSep1($.variable_declarator), $._semicolon), + ]} + *) +and variable_declaration = kwd_var * variable_declarator ne_list + +(** Abstract Class Declaration + + An abstract class declaration is the declaration of a class that + cannot be instantiated (no public constructors), and are instead + used as a base to derive other classes, enforcing this way some + method implementations and the presence of certain members with + certain types. + + NOTE: We could have had the type [abstract_class_declaration] reuse + the type [class_declaration], but we did not because of the + different handling of comments (to which keyword they should be + hooked, e.g., "class" or "abstract"). + + Example: + {@js[ + abstract class Base { + abstract getName(): string; + + printName() { + console.log("Hello, " + this.getName()); + } + } + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + abstract_class_declaration: $ => prec('declaration', seq( + repeat(field('decorator', $.decorator)), + 'abstract', + 'class', + field('name', $._type_identifier), + field('type_parameters', optional($.type_parameters)), + optional($.class_heritage), + field('body', $.class_body))) + }] + *) +and abstract_class_declaration = + { decorators : decorators + ; kwd_abstract : kwd_abstract + ; kwd_class : kwd_class + ; name : type_identifier + ; type_parameters : type_parameters option + ; class_heritage : class_heritage option + ; body : class_body + } + +and type_identifier = identifier + +(** Method Definition + + A method is defined in a class, where it is introduced to its scope + (access can be modified). + + Example: [@js{public static id? (x: T) : T { return x; }}] + + The related grammar rules are given by: + + TypeScript + {@js[ + method_definition: $ => prec.left(seq( + optional($.accessibility_modifier), + optional('static'), + optional($.override_modifier), + optional('readonly'), + optional('async'), + optional(choice('get', 'set', '*')), + field('name', $._property_name), + optional('?'), + $._call_signature, + field('body', $.statement_block))) + }] + *) +and method_definition = + { signature : method_signature wrap + ; body : statement_block + } + +and statement_block = statements braces + +(** Abstract Method Signature + + Abstract methods are methods without a body, and thus belonging to + an abstract class. + + Example: + {@js[ + abstract class Base { + abstract getName(): string; + + printName() { + console.log("Hello, " + this.getName()); + } + } + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + abstract_method_signature: $ => seq( + optional($.accessibility_modifier), + 'abstract', + optional($.override_modifier), + optional(choice('get', 'set', '*')), + field('name', $._property_name), + optional('?'), + $._call_signature) + ]} + *) +and abstract_method_signature = + { access : accessibility_modifier option + ; kwd_abstract : kwd_abstract + ; kwd_override : kwd_override option + ; set_get_all : set_get_all option + ; name : property_name + ; optional : sym_qmark option + ; call_sig : call_signature wrap + } + +(** Index Signature + + Classes and interfaces can declare an index signature, that is, the + functional type of the index operator '[]' when applied to an + instance of the class (as if an array). + + Example: + {@js[ + interface StringArray { + [index: number]: string; + } + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + index_signature: $ => seq( + optional( + seq(field('sign', optional(choice('-', '+'))), 'readonly')), + '[', + choice( + seq( + field('name', choice( + $.identifier, + alias($._reserved_identifier, $.identifier))), + ':', + field('index_type', $.type)), + $.mapped_type_clause), + ']', + field('type', choice( + $.type_annotation, + $.omitting_type_annotation, + $.adding_type_annotation, + $.opting_type_annotation))), + + mapped_type_clause: $ => seq( + field('name', $._type_identifier), + 'in', + field('type', $.type), + optional(seq('as', field('alias', $.type)))), + + type_annotation: $ => seq(':', $.type), + omitting_type_annotation: $ => seq('-?:', $.type), + adding_type_annotation: $ => seq('+?:', $.type), + opting_type_annotation: $ => seq('?:', $.type) + ]} + *) +and index_signature = + { sign : (sign option * kwd_readonly) option + ; range : index_range brackets + ; annotation : index_annotation + } + +and sign = + | Plus of sym_plus + | Minus of sym_minus + +and index_range = + | Typed_index_clause of typed_index_clause + | Mapped_type_clause of mapped_type_clause + +and typed_index_clause = + { name : identifier (* Including reserved identifiers *) + ; sym_colon : sym_colon + ; index_type : type_expr + } + +and mapped_type_clause = + { name : type_identifier + ; kwd_in : kwd_in + ; type_expr : type_expr + ; alias : (kwd_as * type_expr) option + } + +and index_annotation = + | Type_annotation of type_annotation + | Omitting_type_annotation of (symbol * type_expr) (* "-?" *) + | Adding_type_annotation of (symbol * type_expr) (* "+?:" *) + | Opting_type_annotation of (symbol * type_expr) +(* "?: " *) + +(** Public Field Definition + + A public field definition is a property definition (variables and + methods). Contrary to what the name indicates, the access to the + property can be private or protected. + + Example: + {@js[ + class ClassWithStaticMethod { + static staticProperty = 'someValue'; + } + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + public_field_definition: $ => seq( + repeat(field('decorator', $.decorator)), + optional(choice( + seq('declare', optional($.accessibility_modifier)), + seq($.accessibility_modifier, optional('declare')))), // private?? + choice( + seq(optional('static'), optional($.override_modifier), + optional('readonly')), + seq(optional('abstract'), optional('readonly')), + seq(optional('readonly'), optional('abstract')), + optional('accessor')), + field('name', $._property_name), + optional(choice('?', '!')), + field('type', optional($.type_annotation)), + optional($._initializer)) + ]} + *) +and public_field_definition = + { decorators : decorators + ; access : accessibility_modifier option + ; kwd_declare : kwd_declare option + ; scope : field_scope + ; name : property_name + ; mode : field_mode option + ; type_ : type_annotation option + ; default : (sym_equal * expression) option + } + +and field_scope = + { kwd_static : kwd_static option + ; kwd_override : kwd_override option + ; kwd_readonly : kwd_readonly option + ; kwd_abstract : kwd_abstract option + ; kwd_accessor : kwd_accessor option + } + +and field_mode = + | Optional of sym_qmark + | Definite_assert of sym_emark + +(** Ambient Declaration + + An ambient declaration is a declaration of a value or function that + is defined externally to the project (a library, for example). + + Example: + {@js[ + declare const myVar: string; + ]} + + The related grammar rule is given by: + + TypeScript + {@js[ + ambient_declaration: $ => seq( + 'declare', + choice( + $.declaration, + seq('global', $.statement_block), + seq('module', '.', alias($.identifier, $.property_identifier), + ':', $.type, $._semicolon))) + ]} + *) +and ambient_declaration = + { kwd_declare : kwd_declare + ; ambient_kind : ambient_kind + } + +and ambient_kind = + | Declaration of declaration + | Global_declaration of kwd_global * statement_block + | Module_declaration of + kwd_module * identifier * type_expr (* "module", property identifier *) + +(** Enumerated Declaration + + An enumerated declaration introduces in the current type environment + a type defined by the finite union of some values, which are + implicitly or explicitly mapped to unique integers. + + Example: + {@js[ + const enum Direction {Up = 1, Down, Left, Right,} + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + enum_declaration: $ => seq( + optional('const'), + 'enum', + field('name', $.identifier), + field('body', $.enum_body)), + + enum_body: $ => seq( + '{', + optional(seq( + sepBy1(',', choice( + field('name', $._property_name), + $.enum_assignment)), + optional(','))), + '}'), + + enum_assignment: $ => + seq(field('name', $._property_name), $._initializer) + }] + *) +and enum_declaration = + { kwd_const : kwd_const option + ; kwd_enum : kwd_enum + ; name : identifier + ; body : enum_body list braces + } + +and enum_body = + | Enum_name of property_name + | Enum_assignment of enum_assignment wrap + +and enum_assignment = + { name : property_name + ; default : sym_equal * expression + } + +(** Import Alias + + Import aliases introduce a new name for a (possibly qualified) + namespace or property. + + Example: + {@[ + import n = p.q.r.s; + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + import_alias: $ => seq( + 'import', + $.identifier, + '=', + choice($.identifier, $.nested_identifier), + $._semicolon), + ]} + + JavaScript + {@js[ + nested_identifier: $ => prec('member', seq( + field('object', + choice($.identifier, + alias($.nested_identifier, $.member_expression))), + '.', + field('property', alias($.identifier, $.property_identifier)))) + ]} + *) +and import_alias = + { kwd_import : kwd_import + ; alias : identifier + ; sym_equal : sym_equal + ; aliased : aliased + } + +and aliased = + | Ident of identifier + | Nested of nested_identifier wrap + +(* The order is reversed in the path, e.g., A.B.c becomes [c; B; A] *) +and 'a nested = identifier ne_list * 'a +and nested_identifier = identifier nested (* property identifier *) + +(** Interface Declaration + + An interface is similar to an abstract class, except it cannot + contain any implementation: it's more akin to a type. Interfaces can + be generic and extend other interfaces (multiple inheritance). + + Example: + {@js[ + interface SquareConfig { + color?: string; + width?: number; + } + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + interface_declaration: $ => seq( + 'interface', + field('name', $._type_identifier), + field('type_parameters', optional($.type_parameters)), + optional($.extends_type_clause), + field('body', alias($.object_type, $.interface_body))), + + extends_type_clause: $ => seq( + 'extends', + commaSep1(field('type', choice( + $._type_identifier, + $.nested_type_identifier, + $.generic_type)))), + + nested_type_identifier: $ => prec('member', seq( + field('module', choice($.identifier, $.nested_identifier)), + '.', + field('name', $._type_identifier))), + }] + *) +and interface_declaration = + { kwd_interface : kwd_interface + ; name : type_identifier + ; type_parameters : type_parameters option + ; extends : extends_type_clause option + ; body : object_type (* See TYPES *) + } + +and extends_type_clause = + { kwd_extends : kwd_extends + ; extensions : type_extension ne_list + } + +and type_extension = + | Extends_type of type_identifier + | Extends_nested of nested_type_identifier wrap + | Extends_generic of generic_type wrap + +and nested_type_identifier = type_identifier nested + +(** Internal Module + + Internal modules, or namespaces, create a scope made of type and + value declarations. + + Example: + {@js[ + namespace Validation { + export interface StringValidator { + isAcceptable(s: string): boolean; + } + } + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + internal_module: $ => seq('namespace', $._module), + + _module: $ => prec.right(seq( + field('name', choice($.string, $.identifier, $.nested_identifier)), + field('body', optional($.statement_block)))) + ]} + *) +and internal_module = + { kwd_namespace : kwd_namespace + ; module_name : module_name + ; module_body : statement_block option + } + +and module_declaration = + { kwd_module : kwd_module + ; module_name : module_name + ; module_body : statement_block option + } + +and module_name = + | Module_string of string_literal + | Module_ident of identifier + | Module_nested of nested_identifier wrap + +(** Type Alias Declaration + + Type aliases introduce in the current scope a new type name that + denotes a type expression. + + Example: + {@js[ + type t = number; + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + type_alias_declaration: $ => seq( + 'type', + field('name', $._type_identifier), + field('type_parameters', optional($.type_parameters)), + '=', + field('value', $.type), + $._semicolon) + ]} + *) +and type_alias_declaration = + { kwd_type : kwd_type + ; name : type_identifier + ; type_parameters : type_parameters option + ; sym_equal : sym_equal + ; type_expr : type_expr + } + +(** EXPRESSIONS + + The related grammar rules are given by: + + JavaScript + {@js[ + expression: $ => choice( // Simplified here + $.primary_expression, + $.glimmer_template, + $.assignment_expression, + $.augmented_assignment_expression, + $.await_expression, + $.unary_expression, + $.binary_expression, + $.ternary_expression, + $.update_expression, + $.new_expression, + $.yield_expression) + ]} + + TypeScript + {@js[ + expression: ($, previous) => { // Simplified here + $.as_expression, + $.satisfies_expression, + $.instantiation_expression, + $.internal_module, + $.type_assertion} + ]} +*) +and expression = + | E_as_expression of as_expression wrap + | E_assignment_expression of assignment_expression wrap + | E_augmented_assignment_expression of augmented_assignment_expression wrap + | E_await_expression of await_expression wrap + | E_binary_expression of binary_expression wrap + (*| E_glimmer_template of glimmer_template*) + | E_instantiation_expression of instantiation_expression wrap + | E_internal_module of internal_module wrap + | E_new_expression of new_expression wrap + | E_primary_expression of primary_expression + | E_satisfies_expression of satisfies_expression wrap + | E_ternary_expression of ternary_expression wrap + | E_type_assertion of type_assertion wrap + | E_unary_expression of unary_expression wrap + | E_update_expression of update_expression + | E_yield_expression of yield_expression + +(** As-expressions + + As-expressions are expressions whose type is explicitly constrained + by an annotation (that is, a so-called type assertion). + + Example: + {@js[ + let a = '123'; + let n = a as number; + const m = a as const; + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + as_expression: $ => prec.left('binary', seq( + $.expression, 'as', choice('const', $.type))) + ]} + *) +and as_expression = expression * kwd_as * as_what + +and as_what = + | As_type of type_expr + | As_const of kwd_const + +(** Assignment Expression + + Assignments to mutable variables. + + Example: + {@js[ + x = 5; + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + assignment_expression: $ => prec.right('assign', seq( + optional('using'), + field('left', choice($.parenthesized_expression, $._lhs_expression)), + '=', + field('right', $.expression))) + ]} +*) +and assignment_expression = + { kwd_using : kwd_using option + ; left : assignment_lhs + ; sym_equal : sym_equal + ; right : expression + } + +and assignment_lhs = + | Assign_lhs_parens of parenthesized_expression + | Assign_lhs of lhs_expression + +(** Member Expression + + A member expression is the selection of a property in an object. + + Example: + {@js[ + x.m + ]} + + The related grammar rules are given by: + + JavaScript + {@js[ + member_expression: $ => prec('member', seq( + // Why primary_expression since it is included in expression? + field('object', choice($.expression, $.primary_expression, $.import)), + choice('.', field('optional_chain', $.optional_chain)), + field('property', choice( + $.private_property_identifier, + alias($.identifier, $.property_identifier))))), + + optional_chain: _ => '?.' + ]} +*) +and member_expression = + { object_expr : object_member + ; selector : selector + ; property : property_ident + } + +and object_member = + | Object_member_expression of expression + | Object_member_import of kwd_import + +and selector = + | Dot of sym_dot + | Optional_chain of sym_optional_chain + +and property_ident = + | Private_property_identifier of hash_name + | Property_identifier of identifier + +(** Subscript Expression + + Projecting arrays. + + Example: + {@js[ + f(x).[n] + ]} + + The related grammar rules are given by: + + JavaScript + {@js[ + subscript_expression: $ => prec.right('member', seq( + // Why primary_expression since it is included in expression? + field('object', choice($.expression, $.primary_expression)), + optional(field('optional_chain', $.optional_chain)), + '[', field('index', $._expressions), ']')), + + optional_chain: _ => '?.', + + _expressions: $ => choice($.expression, $.sequence_expression), + + sequence_expression: $ => prec.right(commaSep1($.expression)) + ]} +*) +and subscript_expression = + { object_expr : expression + ; optional_chain : optional_chain option + ; index : expressions brackets + } + +and optional_chain = Optional_chain of sym_optional_chain +and expressions = sequence_expression +and sequence_expression = expression ne_list wrap + +(** Augmented Assignment Expression + + Assignments can be composed with an arithmetic, logical or bitwise + operator. + + Example: + {@js[ + x += 4; + ]} + + The related grammar rules are given by: + + JavaScript + {@js[ + augmented_assignment_expression: $ => prec.right('assign', seq( + field('left', $._augmented_assignment_lhs), + field('operator', choice('+=', '-=', '*=', '/=', '%=', '^=', '&=', + '|=', '>>=', '>>>=', '<<=', '**=', '&&=', + '||=', '??=')), + field('right', $.expression))), + + _augmented_assignment_lhs: $ => choice( + $.member_expression, + $.subscript_expression, + alias($._reserved_identifier, $.identifier), + $.identifier, + $.parenthesized_expression) + ]} + + TypeScript + {@js[ + _augmented_assignment_lhs: ($, previous) => choice(previous, + $.non_null_expression) + ]} +*) +and augmented_assignment_expression = + { left : augmented_assignment_lhs + ; operator : assignment_operator + ; right : expression + } + +and augmented_assignment_lhs = + | Member_expression of member_expression wrap + | Subscript_expression of subscript_expression wrap + | Identifier of identifier + | Parenthesized_expression of parenthesized_expression + +and assignment_operator = + | Add_eq of symbol (* += *) + | Sub_eq of symbol (* -= *) + | Mult_eq of symbol (* *= *) + | Div_eq of symbol (* /= *) + | Rem_eq of symbol (* %= *) + | Bitwise_xor_eq of symbol (* ^= *) + | Bitwise_and_eq of symbol (* &= *) + | Bitwise_or_eq of symbol (* |= *) + | Bitwise_sr_eq of symbol (* >>= *) + | Bitwise_usr_eq of symbol (* >>>= *) + | Bitwise_sl_eq of symbol (* <<= *) + | Exp_eq of symbol (* **= *) + | Logical_and_eq of symbol (* &&= *) + | Logical_or_eq of symbol (* ||= *) + | Non_null_eq of symbol (* ??= *) + +(** Await-expression + + Expressions introduced by the "await" keyword are promises used + only inside asynchronous functions (introduced by the "async" + keyword). Their evaluation pauses the embedding async function, but + do not block the main thread, so the caller can resume, until the + promise is either fulfilled or rejected, and handled by the async + function. + + Example: + {@js[ + async function foo(name) { + console.log(name, "start"); + await console.log(name, "middle"); + console.log(name, "end"); + } + + foo("First"); + foo("Second"); + + // First start + // First middle + // Second start + // Second middle + // First end + // Second end + ]} + + The related grammar rules are given by:: + + JavaScript + {@js[ + await_expression: $ => prec('unary_void', seq('await', $.expression)) + ]} + *) +and await_expression = + { kwd_await : kwd_await + ; expression : expression + } + +(** Binary Expression + + The related grammar rules are given by: + + JavaScript + {@js[ + binary_expression: $ => choice( + ...[['&&', 'logical_and'], + ['||', 'logical_or'], + ['>>', 'binary_shift'], + ['>>>', 'binary_shift'], + ['<<', 'binary_shift'], + ['&', 'bitwise_and'], + ['^', 'bitwise_xor'], + ['|', 'bitwise_or'], + ['+', 'binary_plus'], + ['-', 'binary_plus'], + ['*', 'binary_times'], + ['/', 'binary_times'], + ['%', 'binary_times'], + ['**', 'binary_exp', 'right'], + ['<', 'binary_relation'], + ['<=', 'binary_relation'], + ['==', 'binary_equality'], + ['===', 'binary_equality'], + ['!=', 'binary_equality'], + ['!==', 'binary_equality'], + ['>=', 'binary_relation'], + ['>', 'binary_relation'], + ['??', 'ternary'], + ['instanceof', 'binary_relation'], + ['in', 'binary_relation'] + ].map(([operator, precedence, associativity]) => + (associativity === 'right' ? prec.right : prec.left)(precedence, seq( + field('left', operator === 'in' ? choice($.expression, $.private_property_identifier) : $.expression), + field('operator', operator), + field('right', $.expression))))) + ]} +*) +and binary_expression = + { lhs_expr : lhs_bin_expression + ; operator : binary_operator + ; rhs_expr : expression + } + +and lhs_bin_expression = + | Lhs_bin_expression of expression + | Lhs_bin_hash of private_property_identifier + +and binary_operator = + | Logical_and of symbol (** && *) + | Logical_or of symbol (** || *) + | Bitwise_sr of symbol (** >> *) + | Bitwise_usr of symbol (** >>> *) + | Bitwise_sl of symbol (** << *) + | Bitwise_and of symbol (** & *) + | Bitwise_xor of symbol (** ^ *) + | Bitwise_or of symbol (** | *) + | Add of symbol (** + *) + | Sub of symbol (** - *) + | Mult of symbol (** * *) + | Div of symbol (** / *) + | Rem of symbol (** % *) + | Exp of symbol (** ** *) + | Lt of symbol (** < *) + | Leq of symbol (** <= *) + | Equal of symbol (** == *) + | Strict_eq of symbol (** === *) + | Neq of symbol (** != *) + | Strict_neq of symbol (** !== *) + | Geq of symbol (** >= *) + | Gt of symbol (** > *) + | Non_null of symbol (** ?? *) + | Instance_of of kwd_instanceof (** instanceof *) + | In of kwd_in (** in *) + +(** Instantiation Expression + + Instantiation expressions are a way to instantiate the type + parameters of a generic function. + + Example: + {@js[ + const generic = (c: C, a: A) => { + // do something + return { c, a } + }; + const specialised = generic; + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + instantiation_expression: $ => prec('instantiation', seq( + $.expression, + field('type_arguments', $.type_arguments))) + ]} + *) +and instantiation_expression = expression * type_arguments + +(** New-expression + + New-expressions are simply the instantiation of an object by calling + a constructor. + + Example: + {@js[ + const c = new C(4); + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + new_expression: $ => prec.right('new', seq( + 'new', + field('constructor', $.primary_expression), + field('type_arguments', optional($.type_arguments)), + field('arguments', optional($.arguments)))) + ]} + + JavaScript + {@js[ + arguments: $ => seq( + '(', commaSep(optional(choice($.expression, $.spread_element))), ')'), + + spread_element: $ => seq('...', $.expression) + ]} + *) +and new_expression = + { kwd_new : kwd_new + ; constructor : primary_expression + ; type_arguments : type_arguments option + ; arguments : arguments option + } + +and arguments = argument list parens + +and argument = + | Expression of expression + | Spread_element of spread_element wrap + +and spread_element = sym_ellipsis * expression + +(** Satisfies-expression + + The 'satisfies' binary operator is like an `as` operator: it brings + together an expression and a type. The difference is that the latter + does not change the type of the expression, only checks its + compatibility. + + Example: + {@js[ + type Colors = "red" | "green" | "blue"; + type RGB = [red: number, green: number, blue: number]; + const palette = { + red: [255, 0, 0], + green: "#00ff00", + blue: [0, 0, 255] + } satisfies Record; + const greenNormalized = palette.green.toUpperCase(); + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + satisfies_expression: $ => prec.left('binary', seq( + $.expression, 'satisfies', $.type)) + ]} + *) +and satisfies_expression = expression * kwd_satisfies * type_expr + +(** Ternary Expression + + The ternary conditional expression (equivalent to if-else as an + expression). + + Example: + {@js[ + const positive_logic = true ? 1 : 0; + ]} + + The related grammar rules are given by: + + JavaScript + {@js[ + ternary_expression: $ => prec.right('ternary', seq( + field('condition', $.expression), + alias($._ternary_qmark, '?'), + field('consequence', $.expression), + ':', + field('alternative', $.expression))) + ]} + *) +and ternary_expression = + { condition : expression + ; sym_qmark : sym_qmark + ; consequence : expression + ; sym_colon : sym_colon + ; alternative : expression + } + +(** Type Assertion + + The related grammar rules are given by: + + TypeScript + {@js[ + type_assertion: $ => prec.left('unary', seq( + $.type_arguments, $.expression)) + ]} +*) +and type_assertion = type_arguments * expression + +(** Unary Expression + + The related grammar rules are given by: + + JavaScript + {@js[ + unary_expression: $ => prec.left('unary_void', seq( + field('operator', choice('!', '~', '-', '+', 'typeof', 'void', 'delete')), + field('argument', $.expression))) + ]} +*) +and unary_expression = + { operator : unary_operator + ; argument : expression + } + +and unary_operator = + | Logical_neg of sym_emark (* !x *) + | Bitwise_not of sym_tilde (* ~x *) + | Neg of sym_minus (* -x *) + | Plus_zero of sym_plus (* +x *) + | Typeof of kwd_typeof (* typeof x *) + | Void of kwd_void (* void x *) + | Delete of kwd_delete (* delete x *) + +(** Update Expression + + Incrementing or decrementing a variable. + + The related grammar rules are given by: + + JavaScript + {@js[ + update_expression: $ => prec.left(choice( + seq(field('argument', $.expression), + field('operator', choice('++', '--'))), + seq(field('operator', choice('++', '--')), + field('argument', $.expression)))) + ]} +*) +and update_expression = + | Update_postfix of update wrap + | Update_prefix of update wrap + +and update = + { argument : expression + ; operator : incr_decr_operator + } + +and incr_decr_operator = + | Increment of sym_incr + | Decrement of sym_decr + +(** Yield-expression + + A generator function is a function whose execution can be internally + suspended with a 'yield' instruction, and later resumed by the + caller (at the suspension point). + + Example: + {@js[ + function* generator(i) { + yield i; + yield i + 10; + } + ]} + + The related grammar rules are given by: + + JavaScript + {@js[ + yield_expression: $ => prec.right(seq( + 'yield', + choice(seq('*', $.expression), + optional($.expression)))) + ]} +*) +and yield_expression = + | Yield of (kwd_yield * expression option) wrap + | Yield_iterable of (kwd_yield * sym_asterisk * expression) wrap + +(** Primary Expression + + The related grammar rules are given by: + + JavaScript + {@js[ + primary_expression: $ => choice( + $.subscript_expression, + $.member_expression, + $.parenthesized_expression, + $._identifier, // identifier + undefined + alias($._reserved_identifier, $.identifier), + $.this, + $.super, + $.number, + $.string, + $.template_string, + $.regex, + $.true, + $.false, + $.null, + $.object, + $.array, + $.function_expression, + $.arrow_function, + $.generator_function, + $.class, + $.meta_property, + $.call_expression), + + template_string: $ => seq( + '`', + repeat(choice( + alias($._template_chars, $.string_fragment), + $.escape_sequence, + $.template_substitution)), + '`'), + + template_substitution: $ => seq('${', $._expressions, '}') + }] + + TypeScript + {@js[ + primary_expression: ($, previous) => choice( + previous, + $.non_null_expression) + ]} +*) +and primary_expression = + | E_array of array + | E_arrow_function of arrow_function wrap + | E_call_expression of call_expression + | E_class of class_expression wrap + | E_false of kwd_false + | E_function_expression of function_expression wrap + | E_generator_function of generator_function wrap + | E_identifier of identifier + | E_member_expression of member_expression wrap + | E_meta_property of meta_property + | E_non_null_expression of expression + | E_null of kwd_null + | E_number of number + | E_object of object_expr + | E_parenthesized_expression of parenthesized_expression + | E_regex of string_literal + | E_string of string_literal + | E_subscript_expression of subscript_expression wrap + | E_super of kwd_super + | E_template_string of template_string wrap + | E_this of kwd_this + | E_true of kwd_true + | E_undefined of kwd_undefined + +(** Array Expression + + Example: + {@js[ + x[1,n] + ]} + + The related grammar rules are given by: + + JavaScript + {@js[ + array: $ => seq( + '[', commaSep(optional(choice($.expression, $.spread_element))), ']') + ]} +*) +and array = argument list brackets + +(** Arrow Function + + Functional expressions. + + Example: + {@js[ + x => x + 1 + ]} + + The related grammar rules are given by: + + JavaScript: + {@js[ + arrow_function: $ => seq( + optional('async'), + choice( + field('parameter', choice( + alias($._reserved_identifier, $.identifier), + $.identifier,)), + $._call_signature), + '=>', + field('body', choice($.expression, $.statement_block))) + ]} +*) +and arrow_function = + { kwd_async : kwd_async option + ; parameters : parameters + ; sym_arrow : sym_arrow + ; body : function_body + } + +and parameters = + | Parameter of identifier + | Call_signature of call_signature wrap + +and function_body = + | Expression of expression + | Statement_block of statement_block + +(** Call Expression + + The related grammar rules are given by: + + TypeScript + {@js[ + call_expression: $ => choice( + prec('call', seq( + field('function', choice($.expression, $.import)), + field('type_arguments', optional($.type_arguments)), + field('arguments', choice($.arguments, $.template_string)))), + prec('member', seq( + field('function', $.primary_expression), + '?.', + field('type_arguments', optional($.type_arguments)), + field('arguments', $.arguments)))) + ]} +*) +and call_expression = + | Call of (fun_call, arguments_to_call) call wrap + | Member of (primary_expression, arguments) call wrap + +and fun_call = + | Fun_call of expression + | Import of kwd_import + +and arguments_to_call = + | Arguments of arguments + | Template_string of template_string wrap + +and ('lambda, 'arguments) call = + { lambda : 'lambda + ; type_arguments : type_arguments option + ; arguments : 'arguments + } + +(** Function Expression + + The related grammar rules are given by: + + JavaScript + {@js[ + function_expression: $ => prec('literal', seq( + optional('async'), 'function', + field('name', optional($.identifier)), + $._call_signature, + field('body', $.statement_block))) + ]} +*) +and function_expression = + { kwd_async : kwd_async option + ; kwd_function : kwd_function + ; name : identifier option + ; call_sig : call_signature wrap + ; body : statement_block + } + +(** Generator Function + + A generator function is a function whose execution can be internally + suspended with a 'yield' instruction, and later resumed by the + caller (at the suspension point). The difference with a declaration + is that the generator can be anonymous, as it is an expression. + + The related grammar rules are given by: + + TypeScript + {@js[ + generator_function: $ => prec('literal', seq( + optional('async'), 'function', '*', + field('name', optional($.identifier)), + $._call_signature, + field('body', $.statement_block))) + ]} + *) +and generator_function = sym_asterisk * function_expression wrap + +(** Metaproperty + + The related grammar rules are given by: + + JavaScript + {@js[ + meta_property: _ => choice( + seq('new', '.', 'target'), + seq('import', '.', 'meta')) + ]} +*) +and meta_property = + | Meta_new_target of (kwd_new * kwd_target) wrap + | Meta_import_meta of (kwd_import * kwd_meta) wrap + +(** Object (expression) + + Example: + {@js[ + {x: 5} + ]} + + The related grammar rules are given by: + + JavaScript + {@js[ + object: $ => prec('object', seq( + '{', + commaSep(optional(choice( + $.pair, + $.spread_element, + $.method_definition, + alias(choice($.identifier, $._reserved_identifier), + $.shorthand_property_identifier)))), + '}')), + + pair: $ => seq( + field('key', $._property_name), + ':', + field('value', $.expression)) + ]} +*) +and object_expr = object_entry list braces + +and object_entry = + | Object_entry_pair of pair wrap + | Object_entry_spread of spread_element wrap + | Object_entry_method of method_definition wrap + | Object_entry_shorthand of identifier + +and pair = (property_name, expression) key_value + +and ('key, 'value) key_value = + { key : 'key + ; sym_colon : sym_colon + ; value : 'value + } + +(** Labeled Statement + + The related grammar rule is given by: + + JavaScript + {@js[ + labeled_statement: $ => prec.dynamic(-1, seq( + field('label', alias(choice($.identifier, + $._reserved_identifier), + $.statement_identifier)), + ':', + field('body', $.statement))) + ]} +*) +and labeled_statement = + { label : identifier (* Including reserved identifiers *) + ; sym_colon : sym_colon + ; body : statement + } + +(** Return Statement + + The related grammar rule is given by: + + JavaScript + {@js[ + return_statement: $ => + seq('return', optional($._expressions), $._semicolon) + ]} +*) +and return_statement = + { kwd_return : kwd_return + ; expressions : expressions option + } + +(** Switch Statement + + The related grammar rules are given by: + + JavaScript + {@js[ + switch_statement: $ => seq( + 'switch', + field('value', $.parenthesized_expression), + field('body', $.switch_body)), + + switch_body: $ => + seq('{', repeat(choice($.switch_case, $.switch_default)), '}'), + + switch_case: $ => seq( + 'case', + field('value', $._expressions), + ':', + field('body', repeat($.statement))), + + switch_default: $ => + seq('default', ':', field('body', repeat($.statement))) + ]} +*) +and switch_statement = + { kwd_switch : kwd_switch + ; value : parenthesized_expression + ; body : switch_body + } + +and switch_body = switch_entry list braces + +and switch_entry = + | Switch_case of switch_case wrap + | Switch_default of switch_default wrap + +and switch_case = + { kwd_case : kwd_case + ; value : expressions + ; body : statements + } + +and switch_default = + { kwd_default : kwd_default + ; statements : statements + } + +(** Throw Statement + + The related grammar rule is given by: + + JavaScript + {@js[ + throw_statement: $ => seq('throw', $._expressions, $._semicolon) + ]} +*) +and throw_statement = + { kwd_throw : kwd_throw + ; expressions : expressions + } + +(** While Statement + + The related grammar rule is given by: + + JavaScript + {@js[ + while_statement: $ => seq( + 'while', + field('condition', $.parenthesized_expression), + field('body', $.statement)) + ]} +*) +and while_statement = + { kwd_while : kwd_while + ; condition : parenthesized_expression + ; body : statement + } + +(** With-statement + + The related grammar rule is given by: + + JavaScript + {@js[ + with_statement: $ => seq( + 'with', + field('object', $.parenthesized_expression), + field('body', $.statement)) + ]} +*) +and with_statement = + { kwd_with : kwd_with + ; object_expr : parenthesized_expression + ; body : statement + } + +(** PATTERNS + + The related grammar rule is given by: + + JavaScript + {@js[ + pattern: $ => prec.dynamic(-1, choice($._lhs_expression, $.rest_pattern)) + ]} +*) +and pattern = + | P_member_expression of member_expression wrap + | P_subscript_expression of subscript_expression wrap + | P_identifier of identifier (* Including reserved identifiers *) + | P_undefined of kwd_undefined + | P_destructuring_pattern of destructuring_pattern + | P_non_null_expression of expression + | P_rest_pattern of rest_pattern wrap + +(** TYPES + + The related grammar rule is given by: + + TypeScript + {@js[ + type: $ => choice( + $.primary_type, + $.function_type, + $.readonly_type, + $.constructor_type, + $.infer_type, + prec(-1, alias($._type_query_member_expression_in_type_annotation, + $.member_expression)), + prec(-1, alias($._type_query_call_expression_in_type_annotation, + $.call_expression))) + ]} +*) +and type_expr = + | T_primary_type of primary_type + | T_function_type of function_type wrap + | T_readonly_type of readonly_type wrap + | T_constructor_type of constructor_type wrap + | T_infer_type of infer_type wrap + | T_member_expression of type_query_member_expression_in_type_annotation wrap + | T_call_expression of type_query_call_expression_in_type_annotation wrap + +(** Type queries in type annotations (expressions) + + The related grammar rules ar given by: + + TypeScript + {@js[ + _type_query_member_expression_in_type_annotation: $ => seq( + field('object', choice( + $.import, + alias($._type_query_member_expression_in_type_annotation, $.member_expression), + alias($._type_query_call_expression_in_type_annotation, $.call_expression))), + '.', + field('property', choice( + $.private_property_identifier, + alias($.identifier, $.property_identifier)))), + + _type_query_call_expression_in_type_annotation: $ => seq( + field('function', choice( + $.import, + alias($._type_query_member_expression_in_type_annotation, $.member_expression))), + field('arguments', $.arguments)) + }] + + NOTE: The type expression "M.t" is parsed as a primary_type -> + nested_type_identifier instead of a + _type_query_member_expression_in_type_annotation. Looks like an + ambiguity resolved elsewhere (either a "conflict" clause or + perhaps order of definition). Likewise, "f(x)" does not parse as a + type_query_call_expression_in_type_annotation, for some reason ("import(x)" does, + though). + *) +and type_query_member_expression_in_type_annotation = + { object_expr : type_query_member_expression_object + ; selector : sym_dot + ; property : type_query_property + } + +and type_query_member_expression_object = + | Type_query_object_import of kwd_import + | Type_query_object_member of type_query_member_expression_in_type_annotation wrap + | Type_query_object_call of type_query_call_expression_in_type_annotation wrap + +and type_query_call_expression_in_type_annotation = + { lambda : type_query_call_lambda + ; arguments : arguments + } + +and type_query_call_lambda = + | Type_query_call_import of kwd_import + | Type_query_call_member of type_query_member_expression_in_type_annotation + +(** Primary Type + + The related grammar rule is given by: + + TypeScript + {@js[ + primary_type: $ => choice( + $.parenthesized_type, + $.predefined_type, + $._type_identifier, + $.nested_type_identifier, + $.generic_type, + $.object_type, + $.array_type, + $.tuple_type, + $.flow_maybe_type, + $.type_query, + $.index_type_query, + alias($.this, $.this_type), + $.existential_type, + $.literal_type, + $.lookup_type, + $.conditional_type, + $.template_literal_type, + $.intersection_type, + $.union_type, + 'const'), + + flow_maybe_type: $ => prec.right(seq('?', $.primary_type)), + + parenthesized_type: $ => seq('(', $.type, ')'), + + _type_identifier: $ => alias($.identifier, $.type_identifier), + + existential_type: _ => '*', + + index_type_query: $ => seq('keyof', $.primary_type) + ]} +*) +and primary_type = + | T_parenthesized_type of type_expr parens + | T_predefined_type of predefined_type + | T_type_identifier of type_identifier + | T_nested_type_identifier of nested_type_identifier wrap + | T_generic_type of generic_type wrap + | T_object_type of object_type + | T_array_type of array_type wrap + | T_tuple_type of tuple_type + | T_flow_maybe_type of (sym_qmark * primary_type) wrap + | T_type_query of (kwd_keyof * type_query) wrap + | T_index_type_query of (kwd_keyof * primary_type) wrap + | T_this of kwd_this + | T_existential_type of sym_asterisk + | T_literal_type of literal_type + | T_lookup_type of lookup_type wrap + | T_conditional_type of conditional_type wrap + | T_template_literal_type of template_literal_type wrap + | T_intersection_type of intersection_type wrap + | T_union_type of union_type wrap + +(** Generic type + + The related grammar rules are given by: + + TypeScript + {@js[ + generic_type: $ => prec('call', seq( + field('name', choice( + $._type_identifier, + $.nested_type_identifier)), + field('type_arguments', $.type_arguments))) + }] + + *) +and generic_type = generic_name * type_arguments + +and generic_name = + | Generic_type of type_identifier + | Generic_nested of nested_type_identifier wrap + +(** Array Type + + The related grammar rule is given by: + + TypeScript + {@js[ + array_type: $ => seq($.primary_type, '[', ']') + ]} +*) +and array_type = primary_type * sym_lbracket * sym_rbracket + +(** Conditional Type + + Conditional types are akin to the ternary conditional statements on + values. They are a form of test on a type, based on inheritance. + + Example: + {@js[ + interface Animal { + live(): void; + } + interface Dog extends Animal { + woof(): void; + } + type Example1 = Dog extends Animal ? number : string; + // type Example1 = number + ]} + + The related grammar rule is given by: + + TypeScript + {@js[ + conditional_type: $ => prec.right(seq( + field('left', $.type), + 'extends', + field('right', $.type), + '?', + field('consequence', $.type), + ':', + field('alternative', $.type))) + ]} +*) +and conditional_type = + { left : type_expr + ; kwd_extends : kwd_extends + ; right : type_expr + ; sym_qmark : sym_qmark + ; consequence : type_expr + ; sym_colon : sym_colon + ; alternative : type_expr + } + +(** Intersection Type + + The related grammar rule is given by: + + TypeScript + {@js[ + intersection_type: $ => prec.left(seq(optional($.type), '&', $.type)) + ]} + NOTE: [type_expr list] would have been better. *) +and intersection_type = type_expr option * sym_ampersand * type_expr + +(** Literal Type + + The related grammar rules are given by: + + TypeScript + {@js[ + literal_type: $ => choice( + alias($._number, $.unary_expression), + $.number, + $.string, + $.true, + $.false, + $.null, + $.undefined), + + _number: $ => prec.left(1, seq( + field('operator', choice('-', '+')), + field('argument', $.number))) + ]} + + JavaScript + {@js[ + number: _ => { + ... + const bigintLiteral = + seq(choice(hexLiteral, + binaryLiteral, + octalLiteral, + decimalDigits), + 'n'), + + return token(choice( + hexLiteral, // 0x12 0X12 + decimalLiteral, // 12.5 10E2 .5 13 + binaryLiteral, // 0b01 0B01 + octalLiteral, // 0o12 0O12 + bigintLiteral)) // 12n 0x12n + } + ]} +*) +and literal_type = + | T_unary_type of unary_expression wrap + | T_number of number + | T_string of string_literal + | T_true of kwd_true + | T_false of kwd_false + | T_null of kwd_null + | T_undefined of kwd_undefined + +and unary_type = + { operator : sign + ; argument : number + } + +(** Lookup Type + + The related grammar rule is given by: + + TypeScript + {@js[ + lookup_type: $ => seq($.primary_type, '[', $.type, ']') + ]} +*) +and lookup_type = primary_type * type_expr brackets + +(** Object type + + The related grammar rules are given by: + + TypeScript + {@js[ + object_type: $ => seq( + choice('{', '{|'), // {| from Flow! + optional(seq( + optional(choice(',', ';')), + sepBy1( + choice(',', $._semicolon), + choice($.export_statement, + $.property_signature, + $.call_signature, + $.construct_signature, + $.index_signature, + $.method_signature)), + optional(choice(','3, $._semicolon)))), + choice('}', '|}')), // |} from Flow! + + property_signature: $ => seq( + optional($.accessibility_modifier), + optional('static'), + optional($.override_modifier), + optional('readonly'), + field('name', $._property_name), + optional('?'), + field('type', optional($.type_annotation))), + + call_signature: $ => $._call_signature, + + construct_signature: $ => seq( + optional('abstract'), + 'new', + field('type_parameters', optional($.type_parameters)), + field('parameters', $.formal_parameters), + field('type', optional($.type_annotation))) + ]} +*) +and object_type = member_type list braces + +and member_type = + | Export_statement of export_statement wrap (* See STATEMENTS *) + | Property_signature of property_signature wrap + | Call_signature of call_signature wrap + | Construct_signature of construct_signature wrap + | Index_signature of index_signature wrap + | Method_signature of method_signature wrap + +and property_signature = + { access : accessibility_modifier option + ; scope : method_scope + ; name : property_name + ; sym_qmark : sym_qmark option + ; type_ : type_annotation option + } + +and construct_signature = + { kwd_abstract : kwd_abstract option + ; kwd_new : kwd_new + ; type_parameters : type_parameters option + ; parameters : formal_parameters + ; type_ : type_annotation option + } + +(** Predefined Type + + The related grammar rule is given by: + + TypeScript + {@js[ + predefined_type: _ => choice( + 'any', + 'number', + 'boolean', + 'string', + 'symbol', + alias(seq('unique', 'symbol'), 'unique symbol'), + 'void', + 'unknown', + 'string', // Repeated! + 'never', + 'object') + ]} +*) +and predefined_type = + | T_any of kwd_any + | T_number of kwd_number + | T_boolean of kwd_boolean + | T_string of kwd_string + | T_symbol of kwd_symbol + | T_unique_symbol of kwd_unique_symbol + | T_void of kwd_void + | T_unknown of kwd_unknown + | T_never of kwd_never + | T_object of kwd_object + +(** Template Literal Type + + The related grammar rules are given by: + + TypeScript + {@js[ + template_literal_type: $ => seq( + '`', + repeat(choice( + alias($._template_chars, $.string_fragment), + $.template_type)), + '`'), + + // _template_chars??? + + template_type: $ => seq('${', choice($.primary_type, $.infer_type), '}') + ]} +*) +and template_literal_type = sym_bquote * template_type_fragment list * sym_bquote + +and template_type_fragment = + | Template_type_string of string_literal + | Template_type of template_type + +and template_type = + | Template_type_primary of primary_type + | Template_type_infer of infer_type wrap + +(** Tuple Type + + The related grammar rules are given by: + + TypeScript + {@js[ + tuple_type: $ => seq( + '[', commaSep($._tuple_type_member), optional(','), ']'), + + _tuple_type_member: $ => choice( + alias($.tuple_parameter, $.required_parameter), + alias($.optional_tuple_parameter, $.optional_parameter), + $.optional_type, + $.rest_type, + $.type), + + tuple_parameter: $ => seq( + field('name', choice($.identifier, $.rest_pattern)), + field('type', $.type_annotation)), + + optional_tuple_parameter: $ => seq( + field('name', $.identifier), + '?', + field('type', $.type_annotation)), + + optional_type: $ => seq($.type, '?'), + + rest_type: $ => seq('...', $.type) + ]} +*) +and tuple_type = tuple_type_member list brackets + +and tuple_type_member = + | Tuple_parameter of tuple_parameter wrap + | Tuple_optional_parameter of optional_tuple_parameter wrap + | Tuple_optional_type of (type_expr * sym_qmark) wrap + | Tuple_rest_type of (sym_ellipsis * type_expr) wrap + | Tuple_type of type_expr + +and tuple_parameter = tuple_parameter_name * type_annotation + +and tuple_parameter_name = + | Tuple_parameter_ident of identifier + | Tuple_parameter_rest of rest_pattern wrap + +and optional_tuple_parameter = identifier * sym_qmark * type_annotation + +(** Type Query + + + TypeScript: + {@js[ + type_query: $ => prec.right(seq( + 'typeof', + choice( + alias($._type_query_subscript_expression, $.subscript_expression), + alias($._type_query_member_expression, $.member_expression), + alias($._type_query_call_expression, $.call_expression), + alias($._type_query_instantiation_expression, + $.instantiation_expression), + $.identifier, + $.this))), + + _type_query_subscript_expression: $ => seq( + field('object', choice( + $.identifier, + $.this, + alias($._type_query_subscript_expression, $.subscript_expression), + alias($._type_query_member_expression, $.member_expression), + alias($._type_query_call_expression, $.call_expression))), + optional('?.'), + '[', field('index', choice($.predefined_type, $.string, $.number)), ']'), + + _type_query_member_expression: $ => seq( + field('object', choice( + $.identifier, + $.this, + alias($._type_query_subscript_expression, $.subscript_expression), + alias($._type_query_member_expression, $.member_expression), + alias($._type_query_call_expression, $.call_expression))), + choice('.', '?.'), + field('property', choice( + $.private_property_identifier, + alias($.identifier, $.property_identifier)))), + + _type_query_call_expression: $ => seq( + field('function', choice( + $.import, + $.identifier, + alias($._type_query_member_expression, $.member_expression), + alias($._type_query_subscript_expression, $.subscript_expression))), + field('arguments', $.arguments)), + + _type_query_instantiation_expression: $ => seq( + field('function', choice( + $.import, + $.identifier, + alias($._type_query_member_expression, $.member_expression), + alias($._type_query_subscript_expression, $.subscript_expression))), + field('type_arguments', $.type_arguments)) + ]} +*) +and type_query = + | Typeof_subscript_expression of type_query_subscript_expression + | Typeof_member_expression of type_query_member_expression + | Typeof_call_expression of type_query_call_expression + | Typeof_instantiation_expression of type_query_instantiation_expression + | Typeof_identifier of identifier + | Typeof_this of kwd_this + +and type_query_subscript_expression = + { object_expr : type_query_object + ; optional : sym_optional_chain option (* "?." *) + ; index : type_query_index brackets + } + +and type_query_object = + | Type_query_object_identifier of identifier + | Type_query_object_this of kwd_this + | Type_query_object_subscript_expression of type_query_subscript_expression + | Type_query_object_member_expression of type_query_member_expression + | Type_query_object_call_expression of type_query_call_expression + +and type_query_index = + | Type_query_index_predefined_type of predefined_type + | Type_query_index_string of string_literal + | Type_query_index_number of number + +and type_query_member_expression = + { object_expr : type_query_object + ; selector : query_selector + ; property : type_query_property + } + +and query_selector = + | Query_selector_dot of sym_dot + | Query_selector_opt_chain of sym_optional_chain + +and type_query_property = + | Type_query_property_private of private_property_identifier + | Type_query_property_identifier of identifier + +and type_query_call_expression = + { lambda : type_query_call_function + ; arguments : type_query_call_arguments + } + +and type_query_call_function = + | Type_query_call_import of kwd_import + | Type_query_call_identifier of identifier + | Type_query_call_member_expression of type_query_member_expression + | Type_query_call_subscript_expression of type_query_subscript_expression + +and type_query_call_arguments = arguments + +and type_query_instantiation_expression = + { lambda : type_query_call_function + ; type_arguments : type_arguments + } + +(** Union Type + + Example: + {@js[ + type t = A | B; + ]} + + The related grammar rule is given by: + + TypeScript + {@js[ + union_type: $ => prec.left(seq(optional($.type), '|', $.type)) + ]} + + NOTE: [type_expr list] would have been better *) +and union_type = type_expr option * sym_vbar * type_expr + +(** Function Type + + Example: + {@js[ + type t = (x: T) => T; + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + function_type: $ => prec.left(seq( + field('type_parameters', optional($.type_parameters)), + field('parameters', $.formal_parameters), + '=>', + field('return_type', choice($.type, $.asserts, $.type_predicate)))), + + asserts: $ => seq( + 'asserts', choice($.type_predicate, $.identifier, $.this)) + ]} +*) +and function_type = + { type_parameters : type_parameters option + ; parameters : formal_parameters + ; sym_arrow : sym_arrow + ; return_type : return_type + } + +and return_type = + | Return_type of type_expr + | Return_asserts of asserts + | Return_type_predicate of type_predicate wrap + +and asserts = + | Assert_predicate of kwd_asserts * type_predicate wrap + | Assert_type of kwd_asserts * identifier + | Assert_this of kwd_asserts * kwd_this + +(** Readonly Type + + The related grammar rules are given by: + + TypeScript + {@js[ + readonly_type: $ => seq('readonly', $.type) + ]} +*) +and readonly_type = kwd_readonly * type_expr + +(** Constructor Type + + The type of an object constructor. + + Example: + {@js[ + type ctor = new (x: T) => T; + ]} + + The related grammar rules are given by: + + TypeScript + {@js[ + constructor_type: $ => prec.left(seq( + optional('abstract'), + 'new', + field('type_parameters', optional($.type_parameters)), + field('parameters', $.formal_parameters), + '=>', + field('type', $.type))) + ]} +*) +and constructor_type = + { kwd_abstract : kwd_abstract option + ; kwd_new : kwd_new + ; type_parameters : type_parameters option + ; parameters : formal_parameters + ; sym_arrow : sym_arrow + ; type_expr : type_expr + } + +(** Infer-type + + Example: + {@js[ + type MyConditionalType = T extends SomeType ? TrueType : FalseType; + type MyInferredType = T extends SomeType ? U : FalseType; + ]} + + The related grammar rule is given by: + + TypeScript + {@js[ + infer_type: $ => prec.right(seq( + 'infer', + $._type_identifier, + optional(seq('extends', $.type)))) + ]} +*) +and infer_type = + { kwd_infer : kwd_infer + ; type_id : type_identifier + ; extends : (kwd_extends * type_expr) option + } + +(** STATEMENTS + + The related grammar rule is given by: + + JavaScript + {@js[ + statement: $ => choice( + $.export_statement, + $.import_statement, + $.debugger_statement, + $.expression_statement, + $.declaration, + $.statement_block, + + $.if_statement, + $.switch_statement, + $.for_statement, + $.for_in_statement, + $.while_statement, + $.do_statement, + $.try_statement, + $.with_statement, + + $.break_statement, + $.continue_statement, + $.return_statement, + $.throw_statement, + $.empty_statement, + $.labeled_statement), + + debugger_statement: $ => seq('debugger', $._semicolon), + + empty_statement: _ => ';' + ]} +*) +and statement = + | S_export_statement of export_statement wrap + | S_import_statement of import_statement wrap + | S_debugger_statement of kwd_debugger + | S_expression_statement of expression_statement + | S_declaration_statement of declaration + | S_statement_block of statement_block + | S_if_statement of if_statement wrap + | S_switch_statement of switch_statement wrap + | S_for_statement of for_statement wrap + | S_for_in_statement of for_in_statement wrap + | S_while_statement of while_statement wrap + | S_do_statement of do_statement wrap + | S_try_statement of try_statement wrap + | S_with_statement of with_statement wrap + | S_break_statement of break_statement wrap + | S_continue_statement of continue_statement wrap + | S_return_statement of return_statement wrap + | S_throw_statement of throw_statement wrap + | S_empty_statement of Region.t + | S_labeled_statement of labeled_statement wrap + +(** Break Statement + + The related grammar rule is given by: + + JavaScript + {@js[ + break_statement: $ => seq( + 'break', + field('label', optional(alias($.identifier, $.statement_identifier))), + $._semicolon) + ]} +*) +and break_statement = + { kwd_break : kwd_break + ; stmt_id : identifier option + } + +(** Continue Statement + + The related grammar rule is given by: + + JavaScript + {@js[ + continue_statement: $ => seq( + 'continue', + field('label', optional(alias($.identifier, $.statement_identifier))), + $._semicolon) + ]} +*) +and continue_statement = + { kwd_continue : kwd_continue + ; stmt_id : identifier option + } + +(** Do-statement + + The related grammar rule is given by: + + JavaScript + {@js[ + do_statement: $ => prec.right(seq( + 'do', + field('body', $.statement), + 'while', + field('condition', $.parenthesized_expression), + optional($._semicolon))) + ]} +*) +and do_statement = + { kwd_do : kwd_do + ; body : statement + ; kwd_while : kwd_while + ; condition : parenthesized_expression + } + +(** Export Statement + + Examples: + {@js[ + export {}; + export var pi = 3.14; + export class C {}; + export function absolute(num: number) { + if (num < 0) return num * -1; + return num; + } + ]} + + The related grammar rules are given by: + + JavaScript + {@js[ + export_statement: $ => choice( + seq('export', + choice( + seq('*', $._from_clause), + seq($.namespace_export, $._from_clause), + seq($.export_clause, $._from_clause), // or seq($.export_clause, optional($._from_clause)) + $.export_clause), // See above. + $._semicolon), + seq(repeat(field('decorator', $.decorator)), + 'export', + choice( + field('declaration', $.declaration), + seq('default', + choice( + field('declaration', $.declaration), + seq(field('value', $.expression), $._semicolon)))))), + + namespace_export: $ => seq('*', 'as', $._module_export_name), + + export_clause: $ => + seq('{', commaSep($.export_specifier), optional(','), '}'), + + export_specifier: $ => seq( + field('name', $._module_export_name), + optional(seq('as', field('alias', $._module_export_name)))), + + _module_export_name: $ => choice($.identifier, $.string), + + _from_clause: $ => seq('from', field('source', $.string)) + ]} + + TypeScript + {@js[ + export_statement: ($, previous) => choice( + previous, + seq('export', 'type', $.export_clause, + optional($._from_clause), $._semicolon), + seq('export', '=', $.expression, $._semicolon), + seq('export', 'as', 'namespace', $.identifier, $._semicolon)) + ]} +*) +and export_statement = + { kwd_export : kwd_export + ; export_kind : export_kind + } + +and export_kind = + | Export_from of from_clause + | Export_as of namespace_export * from_clause + | Export_clause of export_clause * from_clause option + | Export_declaration of declaration decorated + | Export_default_declaration of (kwd_default * declaration) decorated + | Export_default_expression of (kwd_default * expression) decorated + | Export_type of export_type (* "type" *) + | Export_equal of sym_equal * expression + | Export_as_namespace of kwd_as * identifier + +and 'a decorated = + { decorators : decorators + ; decorated : 'a + } + +and from_clause = kwd_from * string_literal + +and namespace_export = + { sym_asterisk : sym_asterisk + ; kwd_as : kwd_as + ; namespace_name : module_export_name + } + +and export_clause = export_specifier list braces + +and export_specifier = + { name : module_export_name + ; alias : (kwd_as * module_export_name) option + } + +and module_export_name = + | Export_ident of identifier + | Export_string of string_literal + +and export_type = + { kwd_type : kwd_type + ; export_clause : export_clause + ; from_clause : from_clause option + } + +(** Expression Statement + + Expressions can be used as statements, for example, a function call + returning nothing (unit). + + The related grammar rule is given by: + + TypeScript + {@js[ + expression_statement: $ => seq($._expressions, $._semicolon) + ]} +*) +and expression_statement = expressions + +(** For-in/of Statement + + The related grammar rules are given by: + + JavaScript + {@js[ + for_in_statement: $ => seq( + 'for', + optional('await'), + $._for_header, + field('body', $.statement)), + + _for_header: $ => seq( + '(', + choice( + field('left', choice( + $._lhs_expression, + $.parenthesized_expression)), + seq( + field('kind', 'var'), + field('left', choice( + $.identifier, + $._destructuring_pattern)), + optional($._initializer)), + seq( + field('kind', choice('let', 'const')), + field('left', choice( + $.identifier, + $._destructuring_pattern)))), + field('operator', choice('in', 'of')), + field('right', $._expressions), + ')') + ]} +*) +and for_in_statement = + { kwd_for : kwd_for + ; kwd_await : kwd_await option + ; sym_lparen : sym_lparen + ; for_header : for_header + ; sym_rparen : sym_rparen + ; body : statement + } + +and for_header = + { range : for_range + ; operator : for_operator + ; collection : expressions + } + +and for_range = + | For_in_expression of lhs_expression + | For_in_parenthesized of parenthesized_expression + | For_in_var of for_in_var + | For_in_let of kwd_let * for_in_variable + | For_in_const of kwd_const * for_in_variable + +and for_in_var = + { kwd_var : kwd_var + ; variable : for_in_variable + ; default : expression option + } + +and for_in_variable = + | For_in_ident of identifier + | For_in_pattern of destructuring_pattern + +and for_operator = + | In of kwd_in + | Of of kwd_of + +(** For-statement + + The related grammar rules are given by: + + JavaScript + {@js[ + for_statement: $ => seq( + 'for', + '(', + field('initializer', choice( + $.lexical_declaration, + $.variable_declaration, + $.expression_statement, + $.empty_statement)), + field('condition', choice( + $.expression_statement, + $.empty_statement)), + field('increment', optional($._expressions)), + ')', + field('body', $.statement)) + ]} +*) +and for_statement = + { kwd_for : kwd_for + ; sym_lparen : sym_lparen + ; initializer_ : for_initializer + ; condition : for_condition + ; increment : expressions option + ; sym_rparen : sym_rparen + ; body : statement + } + +and for_initializer = + | For_lexical_declaration of lexical_declaration wrap + | For_variable_declaration of variable_declaration wrap + | For_expression_statement of expression_statement + | For_empty_statement of Region.t + +and for_condition = + | For_condition_expression of expression_statement + | For_condition_empty of Region.t + +(** If-statement + + The related grammar rules are given by: + + JavaScript + {@js[ + if_statement: $ => prec.right(seq( + 'if', + field('condition', $.parenthesized_expression), + field('consequence', $.statement), + optional(field('alternative', $.else_clause)))), + + else_clause: $ => seq('else', $.statement) + ]} +*) +and if_statement = + { kwd_if : kwd_if + ; condition : parenthesized_expression + ; consequence : statement + ; alternative : (kwd_else * statement) option + } + +(** Parenthesized Expression + + The related grammar rule is given by: + + TypeScript + {@js[ + parenthesized_expression: $ => seq( + '(', + choice( + seq($.expression, field('type', optional($.type_annotation))), + $.sequence_expression), + ')') + ]} + + NOTE: This rule overwrites the one in the JavaScript grammar. +*) +and parenthesized_expression = in_expressions parens + +and in_expressions = + | Sequence_expression of sequence_expression + | Typed_expression of expression * type_annotation + +(** Import Statement + + Examples: + {@js[ + import helloWorld from "./hello.js"; + import { pi, phi, absolute } from "./maths.js"; + import { pi as π } from "./maths.js"; + import RandomNumberGenerator, { pi as π } from "./maths.js"; + import { Cat, Dog } from "./animal.js"; + import * as math from "./maths.js"; + import type { Cat, Dog } from "./animal.js"; + import type { createCatName } from "./animal.js"; + import { createCatName, type Cat, type Dog } from "./animal.js"; + import fs = require("fs"); + ]} + + The related grammar rules are given by: + + TypeScript: + {@js[ + import_statement: $ => seq( + 'import', + optional(choice('type', 'typeof')), + choice( + seq($.import_clause, $._from_clause), + $.import_require_clause, + field('source', $.string)), + optional($.import_attribute), + $._semicolon), + + import_clause: $ => choice( + $.namespace_import, + $.named_imports, + seq($._import_identifier, + optional(seq( + ',', + choice( + $.namespace_import, + $.named_imports))))), + + namespace_import: $ => seq('*', 'as', $.identifier), + + named_imports: $ => + seq('{', commaSep($.import_specifier), optional(','), '}'), + + import_specifier: $ => seq( + optional(choice('type', 'typeof')), + choice( + field('name', $._import_identifier), + seq(field('name', choice($._module_export_name, + alias('type', $.identifier))), + 'as', + field('alias', $._import_identifier)))), + + _import_identifier: $ => + choice($.identifier, alias('type', $.identifier)), + + _module_export_name: $ => choice($.identifier, $.string), // See exports + + import_require_clause: $ => seq( + $.identifier, '=', 'require', '(', field('source', $.string), ')'), + + import_attribute: $ => seq(choice('with', 'assert'), $.object) + ]} +*) +and import_statement = + { kwd_import : kwd_import + ; import_kind : import_kind option + ; import : import + ; import_attribute : import_attribute option + } + +and import_kind = + | Import_type of kwd_type + | Import_typeof of kwd_typeof + +(* + import * as M from "/my/path.ts" + ~> + Import_clause (Import_namespace {sym_asterisk; kwd_as; ident}, (kwd_from, string)) + + import {x} from "/my/path.ts" + ~> + Import_clause (Import_named (Braces [Import_spec_name "x"]), (kwd_from, string)) + *) +and import = + | Import_clause of (import_clause * from_clause) + | Import_require_clause of import_require_clause wrap + | Import_source of string_literal + +and import_clause = + | Import_namespace of namespace_import wrap + | Import_named of named_imports + | Import_ident of import_identifier * namespace_or_named_imports option + +and namespace_or_named_imports = + | Import_namespace of namespace_import wrap + | Import_named of named_imports + +and namespace_import = + { sym_asterisk : sym_asterisk + ; kwd_as : kwd_as + ; identifier : identifier + } + +and named_imports = import_specifier list braces +and import_specifier = import_kind option * import_specifier' + +and import_specifier' = + | Import_spec_name of import_identifier + | Import_spec_alias of import_spec_alias + +and import_identifier = identifier (* Including "type" *) + +and import_spec_alias = + { name : module_export_name + ; kwd_as : kwd_as + ; alias : import_identifier + } + +and import_require_clause = + { ident : identifier + ; sym_equal : sym_equal + ; kwd_require : kwd_require + ; sym_lparen : sym_lparen + ; source : string_literal + ; sym_rparen : sym_rparen + } + +and import_attribute = + | Import_with of kwd_with * object_expr + | Import_assert of kwd_assert * object_expr + +(** Asserts Annotation + + The related grammar rule is given by: + + TypeScript + {@js[ + asserts_annotation: $ => seq(seq(':', $.asserts)), // Really? + ]} +*) +and asserts_annotation = asserts + +(** Assignment Pattern + + The related grammar rule is given by: + + JavaScript + {@js[ + assignment_pattern: $ => seq( + field('left', $.pattern), '=', field('right', $.expression)) + ]} +*) +and assignment_pattern = + { left : pattern + ; sym_equal : sym_equal + ; right : expression + } + +(** Try statement + + Try-with statements enable to guard a piece of code with exception + handlers. + + The related grammar rules are given by: + + JavaScript + {@js[ + try_statement: $ => seq( + 'try', + field('body', $.statement_block), + optional(field('handler', $.catch_clause)), + optional(field('finalizer', $.finally_clause))), + + finally_clause: $ => seq('finally', field('body', $.statement_block)), + ]} + + TypeScript + {@js[ + catch_clause: $ => seq( + 'catch', + optional( + seq('(', + field('parameter', + choice($.identifier, $._destructuring_pattern)), + optional(field('type', $.type_annotation)), + ')')), + field('body', $.statement_block)) + ]} +*) +and try_statement = + { kwd_try : kwd_try + ; body : statement_block + ; handler : catch_clause option + ; finalizer : finally_clause option + } + +and catch_clause = + { kwd_catch : kwd_catch + ; parameter : catch_parameter option + ; body : statement_block + } + +and catch_parameter = + { sym_lparen : sym_lparen + ; catch_parameter : catch_parameter_kind + ; type_opt : type_annotation option + ; sym_rparen : sym_rparen + } + +and catch_parameter_kind = + | Catch_identifier of identifier + | Catch_object_pattern of object_pattern + | Catch_array_pattern of array_pattern + +and finally_clause = kwd_finally * statement_block + +(** Class + + The related grammar rule is given by: + + TypeScript + {@js[ + class: $ => prec('literal', seq( + repeat(field('decorator', $.decorator)), + 'class', + field('name', optional($._type_identifier)), + field('type_parameters', optional($.type_parameters)), + optional($.class_heritage), + field('body', $.class_body))) + ]} +*) +and class_expression = + { decorators : decorators + ; kwd_class : kwd_class + ; name : type_identifier option + ; type_parameters : type_parameters option + ; class_heritage : class_heritage option + ; body : class_body + } + +(** DECORATOR + + The related grammar rules are given by: + + TypeScript + {@js[ + decorator: $ => seq( + '@', + choice( + $.identifier, + alias($.decorator_member_expression, $.member_expression), + alias($.decorator_call_expression, $.call_expression), + alias($.decorator_parenthesized_expression, + $.parenthesized_expression))), + + decorator_call_expression: $ => prec('call', seq( + field('function', choice( + $.identifier, + alias($.decorator_member_expression, $.member_expression))), + optional(field('type_arguments', $.type_arguments)), + field('arguments', $.arguments))), + + decorator_parenthesized_expression: $ => seq( + '(', + choice( + $.identifier, + alias($.decorator_member_expression, $.member_expression), + alias($.decorator_call_expression, $.call_expression)), + ')') + ]} + + JavaScript + {@js[ + decorator_member_expression: $ => prec('member', seq( + field('object', choice( + $.identifier, + alias($.decorator_member_expression, $.member_expression))), + '.', + field('property', alias($.identifier, $.property_identifier)))) + ]} +*) +and decorator = + | Decorator_identifier of identifier + | Decorator_member_expression of decorator_member_expression wrap + | Decorator_call_expression of decorator_call_expression wrap + | Decorator_parenthesized_expression of decorator_parenthesized_expression parens + +and decorator_member_expression = + { object_expr : object_member_expression + ; sym_dot : sym_dot + ; property : identifier + } + +and object_member_expression = + | Object_name of identifier + | Qualified_member_expression of decorator_member_expression wrap + +and decorator_call_expression = + { function_ : function_or_property + ; type_arguments : type_arguments option + ; arguments : arguments + } + +and function_or_property = + | Function_name of identifier + | Qualified_member_expression of decorator_member_expression wrap + +and decorator_parenthesized_expression = + | Parenthesized_ident of identifier + | Parenthesized_member of decorator_member_expression wrap + | Parenthesized_call of decorator_call_expression wrap + +(* PROJECTIONS *) + +(* Regions (a.k.a. source locations) *) + +let region_of_braces (Braces w) = w#region +let region_of_chevrons (Chevrons w) = w#region +let region_of_brackets (Brackets w) = w#region +let region_of_parens (Parens w) = w#region + +let region_of_call_expression = function + | Call e -> e#region + | Member e -> e#region + +let region_of_meta_property = function + | Meta_new_target e -> e#region + | Meta_import_meta e -> e#region + +let region_of_update_expression = function + | Update_postfix e -> e#region + | Update_prefix e -> e#region + +let region_of_yield_expression = function + | Yield e -> e#region + | Yield_iterable e -> e#region + +let region_of_number = function + | Hex (e, _) -> e#region + | Bin (e, _) -> e#region + | Oct (e, _) -> e#region + | Dec (e, _) -> e#region + +let rec region_of_primary_expression = function + | E_array e -> region_of_brackets e + | E_arrow_function e -> e#region + | E_call_expression e -> region_of_call_expression e + | E_class e -> e#region + | E_false e -> e#region + | E_function_expression e -> e#region + | E_generator_function e -> e#region + | E_identifier e -> e#region + | E_member_expression e -> e#region + | E_meta_property e -> region_of_meta_property e + | E_non_null_expression e -> region_of_expression e + | E_null e -> e#region + | E_number e -> region_of_number e + | E_object e -> region_of_braces e + | E_parenthesized_expression e -> region_of_parens e + | E_regex e -> e#region + | E_string e -> e#region + | E_subscript_expression e -> e#region + | E_super e -> e#region + | E_template_string e -> e#region + | E_this e -> e#region + | E_true e -> e#region + | E_undefined e -> e#region + +and region_of_expression = function + | E_as_expression e -> e#region + | E_assignment_expression e -> e#region + | E_augmented_assignment_expression e -> e#region + | E_await_expression e -> e#region + | E_binary_expression e -> e#region + | E_instantiation_expression e -> e#region + | E_internal_module e -> e#region + | E_new_expression e -> e#region + | E_primary_expression e -> region_of_primary_expression e + | E_satisfies_expression e -> e#region + | E_ternary_expression e -> e#region + | E_type_assertion e -> e#region + | E_unary_expression e -> e#region + | E_update_expression e -> region_of_update_expression e + | E_yield_expression e -> region_of_yield_expression e + +let region_of_destructuring_pattern = function + | Pattern_object (Braces b) -> b#region + | Pattern_array (Brackets b) -> b#region + +let region_of_pattern = function + | P_member_expression p -> p#region + | P_subscript_expression e -> e#region + | P_identifier p -> p#region + | P_undefined p -> p#region + | P_destructuring_pattern p -> region_of_destructuring_pattern p + | P_non_null_expression e -> region_of_expression e + | P_rest_pattern p -> p#region + +let region_of_declaration = function + | D_function_declaration d -> d#region + | D_generator_function_declaration d -> d#region + | D_class_declaration d -> d#region + | D_lexical_declaration d -> d#region + | D_variable_declaration d -> d#region + | D_function_signature d -> d#region + | D_abstract_class_declaration d -> d#region + | D_module d -> d#region + | D_internal_module d -> d#region + | D_type_alias_declaration d -> d#region + | D_enum_declaration d -> d#region + | D_interface_declaration d -> d#region + | D_import_alias d -> d#region + | D_ambient_declaration d -> d#region + +let region_of_statement = function + | S_export_statement s -> s#region + | S_import_statement s -> s#region + | S_debugger_statement s -> s#region + | S_expression_statement s -> s#region + | S_declaration_statement d -> region_of_declaration d + | S_statement_block (Braces s) -> s#region + | S_if_statement s -> s#region + | S_switch_statement s -> s#region + | S_for_statement s -> s#region + | S_for_in_statement s -> s#region + | S_while_statement s -> s#region + | S_do_statement s -> s#region + | S_try_statement s -> s#region + | S_with_statement s -> s#region + | S_break_statement s -> s#region + | S_continue_statement s -> s#region + | S_return_statement s -> s#region + | S_throw_statement s -> s#region + | S_empty_statement r -> r + | S_labeled_statement s -> s#region + +let region_of_decorator = function + | Decorator_identifier d -> d#region + | Decorator_member_expression d -> d#region + | Decorator_call_expression d -> d#region + | Decorator_parenthesized_expression (Parens d) -> d#region + +let region_of_function_or_property = function + | Function_name ident -> ident#region + | Qualified_member_expression member -> member#region + +let region_of_predefined_type = function + | T_any t -> t#region + | T_number t -> t#region + | T_boolean t -> t#region + | T_string t -> t#region + | T_symbol t -> t#region + | T_unique_symbol t -> t#region + | T_void t -> t#region + | T_unknown t -> t#region + | T_never t -> t#region + | T_object t -> t#region + +let region_of_literal_type = function + | T_unary_type t -> t#region + | T_number n -> region_of_number n + | T_string t -> t#region + | T_true t -> t#region + | T_false t -> t#region + | T_null t -> t#region + | T_undefined t -> t#region + +let region_of_primary_type = function + | T_parenthesized_type (Parens t) -> t#region + | T_predefined_type t -> region_of_predefined_type t + | T_type_identifier t -> t#region + | T_nested_type_identifier t -> t#region + | T_generic_type t -> t#region + | T_object_type (Braces t) -> t#region + | T_array_type t -> t#region + | T_tuple_type (Brackets t) -> t#region + | T_flow_maybe_type t -> t#region + | T_type_query t -> t#region + | T_index_type_query t -> t#region + | T_this t -> t#region + | T_existential_type t -> t#region + | T_literal_type t -> region_of_literal_type t + | T_lookup_type t -> t#region + | T_conditional_type t -> t#region + | T_template_literal_type t -> t#region + | T_intersection_type t -> t#region + | T_union_type t -> t#region + +let region_of_type_expr = function + | T_primary_type t -> region_of_primary_type t + | T_function_type t -> t#region + | T_readonly_type t -> t#region + | T_constructor_type t -> t#region + | T_infer_type t -> t#region + | T_member_expression t -> t#region + | T_call_expression t -> t#region + +let region_of_template_type = function + | Template_type_primary t -> region_of_primary_type t + | Template_type_infer t -> t#region + +let region_of_template_type_fragment = function + | Template_type_string s -> s#region + | Template_type t -> region_of_template_type t + +let region_of_tuple_type_member = function + | Tuple_parameter w -> w#region + | Tuple_optional_parameter w -> w#region + | Tuple_optional_type w -> w#region + | Tuple_rest_type w -> w#region + | Tuple_type type_expr -> region_of_type_expr type_expr + +let region_of_asserts = function + | Assert_predicate (kwd_asserts, type_predicate) -> + Region.cover kwd_asserts#region type_predicate#region + | Assert_type (kwd_asserts, identifier) -> + Region.cover kwd_asserts#region identifier#region + | Assert_this (kwd_asserts, kwd_this) -> Region.cover kwd_asserts#region kwd_this#region + +let region_of_return_type = function + | Return_type t -> region_of_type_expr t + | Return_asserts a -> region_of_asserts a + | Return_type_predicate w -> w#region + +let region_of_accessibility_modifier = function + | Public kwd_public -> kwd_public#region + | Private kwd_private -> kwd_private#region + | Protected kwd_protected -> kwd_protected#region + +let region_of_lhs_expression = function + | Member_expression w -> w#region + | Subscript_expression w -> w#region + | Identifier var -> var#region + | Undefined kwd_undefined -> kwd_undefined#region + | Pattern p -> region_of_destructuring_pattern p + | Non_null_expression e -> region_of_expression e + +let region_of_for_in_variable = function + | For_in_ident ident -> ident#region + | For_in_pattern p -> region_of_destructuring_pattern p + +let region_of_module_name = function + | Module_string literal -> literal#region + | Module_ident ident -> ident#region + | Module_nested nested -> nested#region + +let region_of_lhs_pattern = function + | Decl_ident id -> id#region + | Decl_pattern p -> region_of_destructuring_pattern p + +let region_of_array_cell_pattern = function + | Cell_pattern pattern -> region_of_pattern pattern + | Cell_assignment pattern -> pattern#region + +let region_of_argument = function + | Expression expr -> region_of_expression expr + | Spread_element w -> w#region + +let region_of_augmented_assignment_lhs (node : augmented_assignment_lhs) = + match node with + | Member_expression w -> w#region + | Subscript_expression w -> w#region + | Identifier ident -> ident#region + | Parenthesized_expression expr -> region_of_parens expr + +let region_of_assignment_lhs (node : assignment_lhs) = + match node with + | Assign_lhs_parens expr -> region_of_parens expr + | Assign_lhs expr -> region_of_lhs_expression expr + +let region_of_member_pattern (node : member_pattern) = + match node with + | Member_pair_pattern w -> w#region + | Member_rest_pattern w -> w#region + | Member_object_assignment w -> w#region + | Member_shorthand_property ident -> ident#region + +let region_of_object_entry = function + | Object_entry_pair pair -> pair#region + | Object_entry_spread spread -> spread#region + | Object_entry_method def -> def#region + | Object_entry_shorthand ident -> ident#region + +let region_of_type_extension = function + | Extends_type ident -> ident#region + | Extends_nested nested -> nested#region + | Extends_generic gen -> gen#region + +let region_of_in_expressions = function + | Sequence_expression expressions -> expressions#region + | Typed_expression (expression, (_, type_expr)) -> + let start = region_of_expression expression in + let stop = region_of_type_expr type_expr in + Region.cover start stop + +(* Extracting comments *) + +let comments_of_property_name = function + | Property_identifier ident -> ident#comments + | Private_property_identifier ident -> ident#comments + | String string -> string#comments + | Number _ -> [] + | Computed_property_name brackets -> + let (Brackets brackets) = brackets in + brackets#comments + +(* TEMPORARY *) + +let print_nested (ne_list, ident) = + let list = Nonempty_list.to_list ne_list in + let app v acc = if String.(acc = "") then v#payload else v#payload ^ "." ^ acc in + let string = Core.List.fold_right ~f:app ~init:"" list in + Printf.eprintf "%s__%s\n%!" ident#payload string diff --git a/lib/typescript_ast/ast_stripped.ml b/lib/typescript_ast/ast_stripped.ml new file mode 100644 index 0000000000..1c5e6629d4 --- /dev/null +++ b/lib/typescript_ast/ast_stripped.ml @@ -0,0 +1,597 @@ +(* Abstract Syntax Tree (AST) for JsLIGO *) + +(* Disabling warnings *) + +[@@@warning "-30"] (* multiply-defined record labels *) + +(* Vendor dependencies *) + +module Utils = Simple_utils.Utils +module Region = Simple_utils.Region +module Ne_list = Nonempty_list + +(* Local dependencies *) + +module Wrap = Lexing_shared.Wrap + +(* Utilities *) + +type 'a reg = 'a Region.reg +type 'a wrap = 'a Wrap.wrap +type comment = string wrap +type dec_name = string +type dec_param = string +type decorator = (dec_name * dec_param option) wrap + +(* Literals *) + +type variable = string wrap +type file_path = string wrap +type bytes_literal = (string * Hex.t) wrap +type int_literal = (string * Z.t) wrap +type string_literal = string wrap + +(* Paths (NOT IN REVERSE ORDER. Compare with ast.ml) + + M.N.x.y -> [M; N; x], y + *) + +type simple_path = + { path : variable list + ; selected : variable + } + +let print_simple_path (path : simple_path) : unit = + let { path; selected } = path in + let app v acc = if acc = "" then v#payload else v#payload ^ "." ^ acc in + let path = Core.List.fold_right ~f:app ~init:"" path in + let path = if path = "" then path else path ^ "." in + Printf.eprintf "%s%s\n%!" path selected#payload + +(* The Abstract Syntax Tree *) + +type t = statements + +(* STATEMENTS + + The constructor [S_decorated] is not initial, that is, it is never + produced by [Strip]: it is used by the translation to the unified + AST. *) +and statements = statement Ne_list.t reg + +and statement = + | S_decorated of decorator * statement (* Not initial *) + | S_block of statements + | S_break of Region.t + | S_decl of declaration + | S_export of declaration + | S_expr of expr + | S_for of for_stmt reg + | S_for_of of for_of_stmt reg + | S_if of if_stmt reg + | S_return of expr option reg + | S_switch of switch_stmt reg + | S_while of while_stmt reg + +(* Conditional statement *) +and if_stmt = + { test : expr + ; if_so : statement + ; if_not : statement option + } + +(* For-loops *) +and for_stmt = + { initialiser : statement option + ; condition : expr option + ; afterthought : expr list + ; for_body : statement option + } + +(* For-of loops *) +and for_of_stmt = + { index_kind : var_kind option + ; index : (key * value option) reg + ; expr : expr + ; for_of_body : statement + } + +and key = variable +and value = variable + +and var_kind = + [ `Let of Region.t + | `Const of Region.t + ] + +(* Switch statement *) +and switch_stmt = expr * cases +and cases = switch_case Nonempty_list.t * switch_default option +and switch_case = expr * statements option +and switch_default = statements option + +(* While-loop *) +and while_stmt = expr * statement + +(* DECLARATIONS *) +and declaration = + | D_class of class_decl reg + | D_decorated of decorator * declaration + | D_function of fun_decl reg + | D_import of import_decl + | D_interface of interface_decl reg + | D_namespace of namespace_decl reg + | D_type of type_decl reg + | D_value of value_decl reg + +(* Class declaration *) +and class_decl = + { comments : comment list (* From the keyword "class" *) + ; class_name : variable + ; implements : simple_path reg list + ; class_body : class_member Nonempty_list.t reg + } + +and class_member = + | Method_definition of method_definition reg + | Public_field_definition of public_field_definition reg + +and method_definition = + { method_sig : method_signature reg + ; method_body : statements + } + +and method_signature = + { decorators : decorator list + ; comments : comment list + ; static : Region.t option + ; method_name : property_name + ; generics : variable list + ; parameters : (variable * type_expr) reg list + ; rhs_type : type_expr + } + +and public_field_definition = + { decorators : decorator list + ; static : Region.t option + ; name : property_name + ; field_type : type_expr option + ; field_value : expr + } + +(* Function declaration *) +and fun_decl = + { comments : comment list (* From the keyword "function" *) + ; fun_name : variable + ; generics : variable list + ; parameters : parameter reg list + ; rhs_type : type_expr option + ; fun_body : statements + } + +and parameter = pattern * type_expr option + +(* All import declarations *) +and import_decl = + | Import_alias of import_alias reg + | Import_all_as of import_all_as reg + | Import_from of import_from reg + +(* import M = N.O *) +and import_alias = variable * simple_path reg + +(* import * as M from "/my/path.ts" *) +and import_all_as = variable * file_path + +(* import {x, y} from "/my/path.ts" *) +and import_from = variable list * file_path + +(* Interfaces + + Note: No value for the type [intf_expr] is decoded: only for further + translation to unified AST. *) +and interface_decl = + { intf_name : variable + ; intf_extends : simple_path reg list + ; intf_body : intf_entry reg list reg + } + +and intf_entry = + { decorators : decorator list + ; comments : comment list + ; entry_name : property_name + ; entry_optional : Region.t option + ; entry_type : type_expr + } + +(* NOTE: Type [intf_expr] is not initial: it is used by the + translation to the unified AST. *) +and intf_expr = + | I_body of intf_entry reg list reg + | I_path of simple_path reg + +(* Namespace declaration *) +and namespace_decl = + { namespace_name : variable + ; namespace_type : intf_expr list (* Not initial. See [intf_expr]. *) + ; namespace_body : statements + } + +(* Type declarations *) +and type_decl = + { name : variable + ; generics : variable list + ; type_expr : type_expr + } + +(* Value declaration *) +and value_decl = + { comments : comment list (* From the keyword "let" or "const" *) + ; kind : var_kind + ; bindings : val_binding reg Ne_list.t + } + +and val_binding = + { pattern : pattern + ; rhs_type : type_expr option + ; rhs_expr : expr + } + +(* TYPE EXPRESSIONS *) +and type_expr = + | T_apply of (type_expr * type_expr list) reg (* t *) + | T_decorated of decorator * type_expr + | T_for_all of (variable list * type_expr) reg (* (x: T) => U *) + | T_fun of fun_type reg (* (x : T) => U *) + | T_int of int_literal (* 42 *) + | T_object of member_type reg list reg (* {x; @a y : t} *) + | T_parameter_of of simple_path reg reg (* parameter_of *) + | T_path of simple_path reg (* t M.t *) + | T_string of string_literal (* "x" *) + | T_sum of sum_type (* ["some", T] | ["none"] *) + | T_tuple of type_expr Ne_list.t reg (* [t, [u, v]] *) + | T_union of union_type (* number | string *) + +(* Sum type + + A sum type is a special case of a union type, where all the summands + are tuple types whose first component is a string. That string + contains an identifier called a _data constructor_, and the rest of + the components (if any) are the _type parameters_ to that + constructor. The tuple type as a whole is called a _variant_. For + instance: + + type option = ["Some", T] | ["None"]; + + The constructors are "Some" and "None". The former takes a parameter + T, whereas the latter takes none. + + The values of a sum type are created with a tuple whose first member + (component) is the constructor coerced to a singleton type, and the + other members are the arguments. For instance: + + const some_one : option = ["Some" as "Some", 1]; + + Those values are projected by means of _pattern matchings_. Those + are a special case of a call to a predefined function "$match", + whose first argument is the value to be matched (the projected + subject), and the second is an object pattern whose property names + are the constructors above, and the properties themselves are + functions taking the parameters to the constructor. The compiler + checks that no constructor has been forgotten. For instance: + + function to_list (x : option) : list { + return $match(x, { "Some": (y) => [y], + "None": () => []}); + + const singleton : list = to_list(some_one); + *) +and sum_type = variant reg Ne_list.t reg + +and variant = + { decorators : decorator list + ; constructor : string_literal + ; arguments : type_expr list + } + +(* Object type and class bodies *) +and member_type = + { decorators : decorator list + ; comments : comment list + ; property_name : property_name + ; rhs_type : type_expr + } + +and property_name = + | Property_string of string_literal + | Property_ident of variable + +(* Functional type *) +and fun_type = (variable * type_expr) reg list * type_expr + +(* Object *) +and 'a _object = 'a property reg list reg + +and 'a property = + { decorators : decorator list (* From the property identifier *) + ; comments : comment list (* From the property identifier *) + ; property_name : property_name + ; static : Region.t option + ; property_rhs : 'a option + } + +(* Union type *) +and union_type = type_expr Ne_list.t reg + +(* PATTERNS *) +and pattern = + | P_array of pattern array (* [x, ...y, z] [] *) + | P_bytes of bytes_literal (* 0xFFFA *) + | P_false of Region.t (* false *) + | P_int of int_literal (* 42 *) + | P_object of pattern _object (* {x, y: z} *) + | P_string of string_literal (* "string" *) + | P_true of Region.t (* true *) + | P_var of simple_path reg (* x M.N.x *) + (* The following constructors are not initial, that is, they are + never produced by [Strip]). They are needed for the compilation + to the unified AST. *) + | P_typed of (pattern * type_expr) reg + | P_ctor_app of (property_name * pattern list) reg + +(* Array pattern (shadowing the predefined type [array]) *) +and 'a array = 'a element list reg + +and 'a element = + | Spread of 'a + | Element of 'a + +(* EXPRESSIONS *) +and expr = + | E_add of (expr * expr) reg (* x + y *) + | E_add_eq of (expr * expr) reg (* x += y *) + | E_and of (expr * expr) reg (* x && y *) + | E_app of (expr * expr list) reg (* f(x,y) foo() *) + | E_array of expr array (* [x, ...y, z] [] *) + | E_arrow_fun of arrow_fun_expr reg (* (x : int) => e *) + | E_assign of (expr * expr) reg (* x = y *) + | E_bit_and of (expr * expr) reg (* x & y *) + | E_bit_and_eq of (expr * expr) reg (* x &= y *) + | E_bit_neg of expr reg (* ~x *) + | E_bit_or of (expr * expr) reg (* x | y *) + | E_bit_or_eq of (expr * expr) reg (* x |= y *) + | E_bit_sl of (expr * expr) reg (* x << y *) + | E_bit_sl_eq of (expr * expr) reg (* x <<= y *) + | E_bit_sr of (expr * expr) reg (* x >> y *) + | E_bit_sr_eq of (expr * expr) reg (* x >>= y *) + | E_bit_xor of (expr * expr) reg (* x ^ y *) + | E_bit_xor_eq of (expr * expr) reg (* x ^= y *) + | E_bytes of bytes_literal (* 0xFFFA *) + | E_contract_of of simple_path reg reg (* contract_of (M.N) *) + | E_ctor_app of (string_literal * expr list) reg (* ["K" as "K", x] *) + | E_div of (expr * expr) reg (* x / y *) + | E_div_eq of (expr * expr) reg (* x /= y *) + | E_equal of (expr * expr) reg (* x == y *) + | E_false of Region.t (* false *) + | E_function of function_expr reg (* function (x) {...} *) + | E_geq of (expr * expr) reg (* x >= y *) + | E_gt of (expr * expr) reg (* x > y *) + | E_int of int_literal (* 42 *) + | E_leq of (expr * expr) reg (* x <= y *) + | E_lt of (expr * expr) reg (* x < y *) + | E_match of (expr * match_clause Ne_list.t) reg (* $match(x, {"K": (x) => e}) *) + | E_member of (expr * variable) reg (* e.x *) + | E_michelson of michelson_expr reg (* michelson `{ADD}` *) + | E_mult of (expr * expr) reg (* x * y *) + | E_mult_eq of (expr * expr) reg (* x *= y *) + | E_neg of expr reg (* -x *) + | E_neq of (expr * expr) reg (* x != y *) + | E_not of expr reg (* !x *) + | E_object of expr _object (* {x : e, y} *) + | E_or of (expr * expr) reg (* x || y *) + | E_post_decr of variable reg (* x-- *) + | E_post_incr of variable reg (* x++ *) + | E_pre_decr of variable reg (* --x *) + | E_pre_incr of variable reg (* ++x *) + | E_rem of (expr * expr) reg (* x % n*) + | E_rem_eq of (expr * expr) reg (* x %= y*) + | E_string of string_literal (* "abcdef" *) + | E_sub of (expr * expr) reg (* x - y *) + | E_subscript of (expr * index) reg (* e[1], e["x"] *) + | E_sub_eq of (expr * expr) reg (* x -= y *) + | E_template of string_literal (* `abcdef` *) + | E_ternary of ternary reg (* x ? y : z *) + | E_true of Region.t (* true *) + | E_typed of typed_expr reg (* e as t *) + | E_update of update_expr reg (* {...x, y : z} *) + | E_var of variable (* x *) + | E_xor of (expr * expr) reg (* x ^^ y *) + +(* Subscript expressions *) +and index = + | Component of int_literal + | PropertyName of string_literal + +(* Pattern matching *) +and match_clause = + { constructor : property_name + ; filter : parameter reg option + ; clause_expr : expr + } + +(* Michelson injection: "Michelson `{ADD}`" or "create_contract_of_file `foo.tz`" *) +and michelson_expr = variable * string_literal + +(* Functional expressions *) +and arrow_fun_expr = + { generics : variable list + ; parameters : parameter reg list + ; rhs_type : type_expr option + ; fun_body : fun_body + } + +and function_expr = arrow_fun_expr + +and fun_body = + | Stmt_body of statements + | Expr_body of expr + +(* Functional update of object expressions *) +and update_expr = + { obj_expr : expr + ; updates : expr property reg list + } + +(* Ternary conditional *) +and ternary = + { condition : expr + ; truthy : expr + ; falsy : expr + } + +(* Typed expression *) +and typed_expr = expr (* "as" *) * type_expr + +(* PROJECTIONS *) + +(* Projecting regions from some nodes of the AST *) + +let region_of_import_decl = function + | Import_alias { region; _ } | Import_all_as { region; _ } | Import_from { region; _ } + -> region + +let rec region_of_declaration = function + | D_class { region; _ } -> region + | D_decorated (_, decl) -> region_of_declaration decl + | D_function { region; _ } -> region + | D_import d -> region_of_import_decl d + | D_interface { region; _ } | D_namespace { region; _ } | D_type { region; _ } -> region + | D_value { region; _ } -> region + +let rec region_of_type_expr = function + | T_apply { region; _ } | T_for_all { region; _ } | T_fun { region; _ } -> region + | T_decorated (_, t) -> region_of_type_expr t + | T_int w -> w#region + | T_object { region; _ } | T_parameter_of { region; _ } -> region + | T_string w -> w#region + | T_sum { region; _ } -> region + | T_tuple { region; _ } -> region + | T_union { region; _ } -> region + | T_path { region; _ } -> region + +let region_of_pattern = function + | P_array { region; _ } -> region + | P_bytes w -> w#region + | P_false r -> r + | P_int w -> w#region + | P_object { region; _ } -> region + | P_string w -> w#region + | P_true r -> r + | P_var { region; _ } -> region + | P_typed { region; _ } -> region + | P_ctor_app { region; _ } -> region + +let region_of_expr = function + | E_add { region; _ } + | E_add_eq { region; _ } + | E_and { region; _ } + | E_app { region; _ } + | E_array { region; _ } + | E_arrow_fun { region; _ } + | E_assign { region; _ } + | E_bit_and { region; _ } + | E_bit_and_eq { region; _ } + | E_bit_neg { region; _ } + | E_bit_or { region; _ } + | E_bit_or_eq { region; _ } + | E_bit_sl { region; _ } + | E_bit_sl_eq { region; _ } + | E_bit_sr { region; _ } + | E_bit_sr_eq { region; _ } + | E_bit_xor { region; _ } + | E_bit_xor_eq { region; _ } -> region + | E_bytes w -> w#region + | E_contract_of { region; _ } + | E_ctor_app { region; _ } + | E_div { region; _ } + | E_div_eq { region; _ } + | E_equal { region; _ } -> region + | E_false r -> r + | E_function { region; _ } | E_geq { region; _ } | E_gt { region; _ } -> region + | E_int w -> w#region + | E_leq { region; _ } + | E_lt { region; _ } + | E_match { region; _ } + | E_member { region; _ } + | E_michelson { region; _ } + | E_mult { region; _ } + | E_mult_eq { region; _ } + | E_neg { region; _ } + | E_neq { region; _ } + | E_not { region; _ } + | E_object { region; _ } + | E_or { region; _ } + | E_post_decr { region; _ } + | E_post_incr { region; _ } + | E_pre_decr { region; _ } + | E_pre_incr { region; _ } + | E_rem { region; _ } + | E_rem_eq { region; _ } -> region + | E_string w -> w#region + | E_sub { region; _ } | E_subscript { region; _ } | E_sub_eq { region; _ } -> region + | E_template w -> w#region + | E_ternary { region; _ } -> region + | E_true r -> r + | E_typed { region; _ } | E_update { region; _ } -> region + | E_var w -> w#region + | E_xor { region; _ } -> region + +let rec region_of_statement = function + | S_decorated (_, stmt) -> region_of_statement stmt + | S_block { region; _ } -> region + | S_break r -> r + | S_decl d | S_export d -> region_of_declaration d + | S_expr e -> region_of_expr e + | S_for { region; _ } | S_for_of { region; _ } | S_if { region; _ } -> region + | S_return { region; _ } | S_switch { region; _ } | S_while { region; _ } -> region + +let region_of_var_kind = function + | `Let w | `Const w -> w#region + +let region_of_fun_body_to_region = function + | Stmt_body { region; _ } -> region + | Expr_body e -> region_of_expr e + +let region_of_class_member = function + | Method_definition { region; _ } | Public_field_definition { region; _ } -> region + +let region_of_property_name = function + | Property_string literal -> literal#region + | Property_ident variable -> variable#region + +let comments_of_property_name = function + | Property_string literal -> literal#comments + | Property_ident variable -> variable#comments + +let contents_of_property_name = function + | Property_string literal -> literal + | Property_ident variable -> variable + +(* Lifting decorators from declarations to statements *) + +let rec lift_decorators_from_decl (decl : declaration) = + match decl with + | D_decorated (decorator, decl) -> + let decorate, decl = lift_decorators_from_decl decl in + (fun stmt -> S_decorated (decorator, decorate stmt)), decl + | _ -> (fun stmt -> stmt), decl + +let lift_decorators (stmt : statement) : statement = + match stmt with + | S_decl decl -> + let decorate, decl = lift_decorators_from_decl decl in + decorate (S_decl decl) + | S_export decl -> + let decorate, decl = lift_decorators_from_decl decl in + decorate (S_export decl) + | _ -> stmt diff --git a/lib/typescript_ast/check.ml b/lib/typescript_ast/check.ml new file mode 100644 index 0000000000..ca35b157c2 --- /dev/null +++ b/lib/typescript_ast/check.ml @@ -0,0 +1,1527 @@ +(* Checking and collecting all error nodes in the C CST + (tree-sitter-generated) *) + +(* Dependencies *) + +open Core +open Typescript_ast.Ts_wrap +module Region = Simple_utils.Region +module Snippet = Simple_utils.Snippet +module Ts_wrap = Typescript_ast.Ts_wrap +module Loc_map = Typescript_ast.Loc_map +module Syntax_err = Typescript_ast.Syntax_err +module Wrap = Lexing_shared.Wrap +open Syntax_err + +(* Utilities *) + +let sprintf = Printf.sprintf + +(* Monadic let-binder for result values *) + +let ( let* ) v f = Result.bind v ~f + +(* Utilities *) + +let swap f x y = f y x + +(* Source map for converting vertical and horizontal offset ranges + into regions *) + +let get_region : (ts_tree -> Region.t) ref = + ref (fun _ -> failwith "Internal error: Check.get_region") + +(* The input source (default: a hundred lines) *) + +let input : Buffer.t ref = ref (Buffer.create (80 * 100)) + +(* Formatting error messages (snippets) *) + +let no_colour = true + +let format_msg error node = + let region = !get_region node in + sprintf + "%s%s" + (Format.asprintf "%a" (Snippet.pp_lift ~no_colour) region) + (Syntax_err.to_string error) + +(* Some literals *) + +let check_leaf node errors ~err = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg err node :: errors + | _ -> errors + +let check_kwd = check_leaf +let check_identifier = check_leaf ~err:Identifier +let check_type_identifier = check_leaf ~err:Type_name +let check_string = check_leaf ~err:String_literal +let check_regex = check_leaf ~err:Regexp +let check_number = check_leaf ~err:Number_literal + +(* Checking forests *) + +let check_named_children node check errors = + let children = collect_named_children node in + List.fold_left ~f:(swap check) ~init:errors children + +let check_children children check errors = + List.fold_left ~f:(swap check) ~init:errors children + +(* Wrapping the fetching of nodes *) + +let update_errors errors = function + | Ok _ -> errors + | Error error -> error :: errors + +let first_child_named name node ~err errors = + let msg = format_msg err node in + update_errors errors @@ Ts_wrap.first_child_named name node ~msg + +let child_ranked index node ~err errors = + let msg = format_msg err node in + update_errors errors @@ Ts_wrap.child_ranked index node ~msg + +let named_child_ranked index node ~err errors = + let msg = format_msg err node in + update_errors errors @@ Ts_wrap.named_child_ranked index node ~msg + +let last_child node ~err errors = + let msg = format_msg err node in + update_errors errors @@ Ts_wrap.last_child node ~msg + +let next_sibling node ~err errors = + let msg = format_msg err node in + update_errors errors @@ Ts_wrap.next_sibling node ~msg + +(* Symbols *) + +let make_sym node ~err errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg err node :: errors + | _ -> errors + +(* Check enclosed constructs *) + +let check_enclosed node check opening closing ~open_err ~close_err errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg open_err node :: errors + | _ -> + first_child_named opening node ~err:open_err errors + |> first_child_named closing node ~err:close_err + |> check_named_children node check + +let check_braces node check err errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg err node :: errors + | _ -> + check_enclosed node check "{" "}" ~open_err:Left_brace ~close_err:Right_brace errors + +let check_chevrons node check err errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg err node :: errors + | _ -> + check_enclosed + node + check + "<" + ">" + ~open_err:Left_chevron + ~close_err:Right_chevron + errors + +let check_brackets node check err errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg err node :: errors + | _ -> + check_enclosed + node + check + "[" + "]" + ~open_err:Left_bracket + ~close_err:Right_bracket + errors + +let check_parens node check err errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg err node :: errors + | _ -> + check_enclosed + node + check + "(" + ")" + ~open_err:Left_parenthesis + ~close_err:Right_parenthesis + errors + +(* Traversing the CST *) + +let rec check_program ~filename ~file (map : Loc_map.t) node = + (* Setting up the extraction of source regions *) + let () = get_region := Ts_wrap.get_region filename map in + (* Setting the input as a top-level string buffer *) + let () = Buffer.add_string !input file in + (* Collating errors from the stripped AST *) + check_statements node [] + +(* STATEMENTS + + The JavaScript tree-sitter grammar has the non-terminals + "statement" be a supertype, that is, a hidden rule. *) + +and check_statements node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Statement node :: errors + | _ -> check_named_children node check_statement errors + +and check_statement node errors = + match get_name node with + | "export_statement" -> check_export_statement node errors + | "import_statement" -> check_import_statement node errors + | "debugger_statement" -> check_debugger_statement node errors + | "expression_statement" -> check_expression_statement node errors + | "statement_block" -> check_statement_block node errors + | "if_statement" -> check_if_statement node errors + | "switch_statement" -> check_switch_statement node errors + | "for_statement" -> check_for_statement node errors + | "for_in_statement" -> check_for_in_statement node errors + | "while_statement" -> check_while_statement node errors + | "do_statement" -> check_do_statement node errors + | "try_statement" -> check_try_statement node errors + | "with_statement" -> check_with_statement node errors + | "break_statement" -> check_break_statement node errors + | "continue_statement" -> check_continue_statement node errors + | "return_statement" -> check_return_statement node errors + | "throw_statement" -> check_throw_statement node errors + | "empty_statement" -> check_empty_statement node errors + | "labeled_statement" -> check_labeled_statement node errors + (* Inlining declarations cases (hidden rule) *) + | "function_declaration" -> check_function_declaration node errors + | "generator_function_declaration" -> check_generator_function_declaration node errors + | "class_declaration" -> check_class_declaration node errors + | "lexical_declaration" -> check_lexical_declaration node errors + | "variable_declaration" -> check_variable_declaration node errors + | "function_signature" -> check_function_signature node errors + | "abstract_class_declaration" -> check_abstract_class_declaration node errors + | "module" -> check_module node errors + | "internal_module" -> check_internal_module node errors + | "type_alias_declaration" -> check_type_alias_declaration node errors + | "enum_declaration" -> check_enum_declaration node errors + | "interface_declaration" -> check_interface_declaration node errors + | "import_alias" -> check_import_alias node errors + | "ambient_declaration" -> check_ambient_declaration node errors + | _ -> format_msg Statement node :: errors + +(* Export statement *) + +and check_export_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Export node :: errors + | _ -> errors (* TODO *) + +and check_namespace_export node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Namespace_export node :: errors + | _ -> errors (* TODO *) + +and check_export_clause node errors = + check_braces node check_export_specifier Export_clause errors + +and check_module_export_name node errors = + match get_name node with + | "identifier" -> check_identifier node errors + | "string" -> check_string node errors + | _ -> format_msg Identifier_or_string node :: errors + +and check_export_specifier node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Identifier_or_string node :: errors + | _ -> errors (* TODO *) + +(* Import statement *) + +and check_import_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Import node :: errors + | _ -> errors (* TODO *) + +and check_import_clause node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Import_clause node :: errors + | _ -> errors (* TODO *) + +and check_namespace_import node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Namespace_import node :: errors + | _ -> errors (* TODO *) + +and check_named_imports node errors = + check_braces node check_import_specifier Named_imports errors + +and check_import_specifier node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Import_specifier node :: errors + | _ -> errors (* TODO *) + +and check_import_require_clause node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Import_require_clause node :: errors + | _ -> errors (* TODO *) + +and check_import_attribute node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Import_attribute node :: errors + | _ -> errors (* TODO *) + +(* Debugger statement *) + +and check_debugger_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Debugger node :: errors + | _ -> errors (* TODO *) + +(* Expression statements + + {@js[ + expression_statement: $ => seq($._expressions, $._semicolon), + _expressions: $ => choice($.expression, $.sequence_expression), + sequence_expression: $ => prec.right(commaSep1($.expression)) + ]} + + See [check_expression]. *) + +and check_expression_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Expression node :: errors + | _ -> errors (* TODO *) + +(* Statement blocks *) + +and check_statement_block node errors = check_braces node check_statement Block errors + +(* If statement *) + +and check_if_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg If node :: errors + | _ -> errors (* TODO *) + +and check_else_clause node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Else node :: errors + | _ -> errors (* TODO *) + +(* Switch statement *) + +and check_switch_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Switch node :: errors + | _ -> errors (* TODO *) + +and check_switch_body node errors = + check_braces node check_in_switch_body Switch_body errors + +and check_in_switch_body node errors = + match get_name node with + | "switch_case" -> check_switch_case node errors + | "switch_default" -> check_switch_default node errors + | _ -> format_msg Switch_body node :: errors + +and check_switch_case node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Case node :: errors + | _ -> errors (* TODO *) + +and check_switch_default node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Default node :: errors + | _ -> errors (* TODO *) + +(* For statement *) + +and check_for_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg For node :: errors + | _ -> errors (* TODO *) + +(* For-in statement *) + +and check_for_in_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg For_or_await node :: errors + | _ -> errors (* TODO *) + +(* While statement *) + +and check_while_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg While node :: errors + | _ -> errors (* TODO *) + +(* Do statement *) + +and check_do_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Do node :: errors + | _ -> errors (* TODO *) + +(* Try statement *) + +and check_try_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Try node :: errors + | _ -> errors (* TODO *) + +and check_catch_clause node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Catch node :: errors + | _ -> errors (* TODO *) + +and check_finally_clause node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Finally node :: errors + | _ -> errors (* TODO *) + +(* With statement *) + +and check_with_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg With node :: errors + | _ -> errors (* TODO *) + +(* Break statement *) + +and check_break_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Break node :: errors + | _ -> errors (* TODO *) + +(* Continue statement *) + +and check_continue_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Continue node :: errors + | _ -> errors (* TODO *) + +(* Return statement + + NOTE: The Javascript grammar states: + + {@js[ + return_statement: $ => + seq('return', optional($._expressions), $._semicolon), + + _semicolon: $ => choice($._automatic_semicolon, ';') + ]} + + but the child of rank 1 is sometimes missing, as if + "_automatic_semicolon" can be the empty word. Other rules use + `optional(_automatic_semicolon)`, which adds to the mystery. *) + +and check_return_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Return node :: errors + | _ -> errors (* TODO *) + +(* Throw statement *) + +and check_throw_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Throw node :: errors + | _ -> errors (* TODO *) + +(* Empty statement *) + +and check_empty_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Empty_statement node :: errors + | _ -> errors (* TODO *) + +(* Labeled statement *) + +and check_labeled_statement node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Label node :: errors + | _ -> errors (* TODO *) + +(* DECLARATION + + The JavaScript tree-sitter grammar has the non-terminal + "declaration" be a supertype, that is, a hidden rule. *) + +and check_declaration node errors = + match get_name node with + | "function_declaration" -> check_function_declaration node errors + | "generator_function_declaration" -> check_generator_function_declaration node errors + | "class_declaration" -> check_class_declaration node errors + | "lexical_declaration" -> check_lexical_declaration node errors + | "variable_declaration" -> check_variable_declaration node errors + | "function_signature" -> check_function_signature node errors + | "abstract_class_declaration" -> check_abstract_class_declaration node errors + | "module" -> check_module node errors + | "internal_module" -> check_internal_module node errors + | "type_alias_declaration" -> check_type_alias_declaration node errors + | "enum_declaration" -> check_enum_declaration node errors + | "interface_declaration" -> check_interface_declaration node errors + | "import_alias" -> check_import_alias node errors + | "ambient_declaration" -> check_ambient_declaration node errors + | _ -> format_msg Declaration node :: errors + +(* Function declaration (see [check_function_signature]) *) + +and check_function_declaration node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Function_declaration node :: errors + | _ -> errors (* TODO *) + +and check_return_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Type_expression node :: errors + | "type_annotation" -> check_type_annotation node errors + | "asserts_annotation" -> check_asserts_annotation node errors + | _ -> check_type_predicate_annotation node errors + +(* Generator function declaration (see function declaration) *) + +and check_generator_function_declaration node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> + format_msg Generator_function_declaration node :: errors + | _ -> errors (* TODO *) + +(* Class declaration (see [check_class]) *) + +and check_class_declaration node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Class_declaration node :: errors + | _ -> errors (* TODO *) + +(* Lexical declaration (see [check_variable_declaration]) *) + +and check_lexical_declaration node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Let_or_const node :: errors + | _ -> errors (* TODO *) + +and check_variable_declarator node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Variable node :: errors + | _ -> errors (* TODO *) + +and check_lhs_pattern node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Pattern node :: errors + | "identifier" -> check_identifier node errors + | _ -> check_destructuring_pattern node errors + +(* Variable declaration (see [check_lexical_declaration]) *) + +and check_variable_declaration node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Var node :: errors + | _ -> errors (* TODO *) + +(* Function signature (See [check_function_declaration]) *) + +and check_function_signature node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Async_or_function node :: errors + | _ -> errors (* TODO *) + +(* Abstract class declaration ( see [check_class_declaration]) *) + +and check_abstract_class_declaration node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Abstract node :: errors + | _ -> errors (* TODO *) + +(* Module *) + +and check_module node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Module_name node :: errors + | _ -> errors (* TODO *) + +(* Internal module (a.k.a. namespaces) *) + +and check_internal_module node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Namespace_name node :: errors + | _ -> errors (* TODO *) + +(* Type alias declaration *) + +and check_type_alias_declaration node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Type_name node :: errors + | _ -> errors (* TODO *) + +and check_type_parameters node errors = + check_chevrons node check_type_parameter Type_parameters errors + +and check_type_parameter node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Const_or_type_name node :: errors + | _ -> errors (* TODO *) + +and check_constraint node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Extends node :: errors + | _ -> errors (* TODO *) + +and check_default_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Equal node :: errors + | _ -> errors (* TODO *) + +(* Enum declaration *) + +and check_enum_declaration node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Const_or_enum node :: errors + | _ -> errors (* TODO *) + +and check_enum_body node errors = check_braces node check_in_enum_body Left_brace errors + +and check_in_enum_body node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Enumeration_name node :: errors + | "enum_assignment" -> check_enum_assignment node errors + | _ -> check_property_name node errors + +and check_enum_assignment node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Enumeration_name node :: errors + | _ -> errors (* TODO *) + +(* Interface declaration *) + +and check_interface_declaration node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Interface node :: errors + | _ -> errors (* TODO *) + +and check_interface_body node errors = check_object_type node errors + +and check_extends_type_clause node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Extends node :: errors + | _ -> errors (* TODO *) + +(* Import alias *) + +and check_import_alias node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Import node :: errors + | _ -> errors (* TODO *) + +(* Ambient declaration *) + +and check_ambient_declaration node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Declare node :: errors + | _ -> errors (* TODO *) + +(* EXPRESSION + + The JavaScript tree-sitter grammar has the non-terminals + "expression" and "primary_expression" be supertypes, that is, + hidden rules. Therefore we have to match all the RHS of those + non-terminals in [check_expression]. *) + +and check_expression node errors = + match get_name node with + (* Rest of "expression": *) + | "glimmer_template" -> check_glimmer_template node errors + | "assignment_expression" -> check_assignment_expression node errors + | "augmented_assignment_expression" -> check_augmented_assignment_expression node errors + | "await_expression" -> check_await_expression node errors + | "unary_expression" -> check_unary_expression node errors + | "binary_expression" -> check_binary_expression node errors + | "ternary_expression" -> check_ternary_expression node errors + | "update_expression" -> check_update_expression node errors + | "new_expression" -> check_new_expression node errors + | "yield_expression" -> check_yield_expression node errors + | "as_expression" -> check_as_expression node errors + | "satisfies_expression" -> check_satisfies_expression node errors + | "instantiation_expression" -> check_instantiation_expression node errors + | "internal_module" -> check_internal_module node errors + | "type_assertion" -> check_type_assertion node errors + | _ -> check_primary_expression node errors + +(* Primary expression *) + +and check_primary_expression node errors = + match get_name node with + | "subscript_expression" -> check_subscript_expression node errors + | "member_expression" -> check_member_expression node errors + | "parenthesized_expression" -> check_parenthesized_expression node errors + | "identifier" -> check_identifier node errors + | "undefined" -> check_kwd node errors ~err:Undefined + | "this" -> check_kwd node errors ~err:This + | "super" -> check_kwd node errors ~err:Super + | "number" -> check_number node errors + | "string" -> check_string node errors + | "template_string" -> check_template_string node errors + | "regex" -> check_regex node errors + | "true" -> check_kwd node errors ~err:True + | "false" -> check_kwd node errors ~err:False + | "null" -> check_kwd node errors ~err:Null + | "object" -> check_object node errors + | "array" -> check_array node errors + | "function_expression" -> check_function_expression node errors + | "arrow_function" -> check_arrow_function node errors + | "generator_function" -> check_generator_function node errors + | "class" -> check_class node errors + | "meta_property" -> check_meta_property node errors + | "call_expression" -> check_call_expression node errors + | "non_null_expression" -> check_non_null_expression node errors + | _ -> format_msg Expression node :: errors + +(* Glimmer template (not supported) *) + +and check_glimmer_template node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Glimmer_template node :: errors + | _ -> errors + +(* Assignment expression *) + +and check_assignment_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Using_or_expression node :: errors + | _ -> errors (* TODO *) + +(* Augmented assignment expression *) + +and check_augmented_assignment_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg LHS_of_augmented_assgmnt node :: errors + | _ -> errors (* TODO *) + +(* Await expression *) + +and check_await_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Await node :: errors + | _ -> errors (* TODO *) + +(* Unary expression *) + +and check_unary_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Unary_operator node :: errors + | _ -> errors (* TODO *) + +(* Binary expression *) + +and check_binary_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Expression node :: errors + | _ -> errors (* TODO *) + +(* Ternary expression *) + +and check_ternary_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Expression node :: errors + | _ -> errors (* TODO *) + +(* Update expression *) + +and check_update_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Expression node :: errors + | _ -> errors (* TODO *) + +(* New expression + + Note that the constructor field is a primary expression, but + "primary_expression" is a supertype, that is, a hidden rule. We + assume it is an "expression", since primary expressions are a subset + of them. *) + +and check_new_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg New node :: errors + | _ -> errors (* TODO *) + +(* Yield expression *) + +and check_yield_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Yield node :: errors + | _ -> errors (* TODO *) + +(* As-expression *) + +and check_as_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg As node :: errors + | _ -> errors (* TODO *) + +(* Statisfies-expression *) + +and check_satisfies_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Satisfies node :: errors + | _ -> errors (* TODO *) + +(* Instantiation expression *) + +and check_instantiation_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Expression node :: errors + | _ -> errors (* TODO *) + +(* Type assertion *) + +and check_type_assertion node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Type_arguments node :: errors + | _ -> errors (* TODO *) + +(* Subscript expression (see [check_member_expression]) *) + +and check_subscript_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Expression node :: errors + | _ -> errors (* TODO *) + +(* Member expression *) + +and check_member_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Member_expression node :: errors + | _ -> errors (* TODO *) + +(* Parenthesised expression *) + +and check_parenthesized_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Parenthesized_expression node :: errors + | _ -> errors (* TODO *) + +(* Template strings *) + +and check_template_string node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Template_string node :: errors + | _ -> errors (* TODO *) + +(* Object *) + +and check_object node errors = + check_braces node check_object_field Object_expression errors + +and check_object_field node errors = + ignore node; + errors (* TODO *) + +(* Pairs *) + +and check_pair node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Key_value_pair node :: errors + | _ -> errors (* TODO *) + +(* Array (expression) *) + +and check_array node errors = check_brackets node check_array_cell Array errors + +and check_array_cell node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Array_cell node :: errors + | _ -> errors (* TODO *) + +and check_spread_element node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Spread node :: errors + | _ -> errors (* TODO *) + +(* Function (expression) *) + +and check_function_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Function_expression node :: errors + | _ -> errors (* TODO *) + +(* Arrow function *) + +and check_arrow_function node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Arrow_function node :: errors + | _ -> errors (* TODO *) + +and check_arrow_function_body node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Block_or_expression node :: errors + | "statement_block" -> check_statement_block node errors + | _ -> check_expression node errors (* Hidden *) + +(* Generator function *) + +and check_generator_function node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Generator_function node :: errors + | _ -> errors (* TODO *) + +(* Class *) + +and check_class node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Class_expression node :: errors + | _ -> errors (* TODO *) + +and check_class_heritage node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Extends_or_implements node :: errors + | _ -> errors (* TODO *) + +and check_implements_clause node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Implements_clause node :: errors + | _ -> errors (* TODO *) + +and check_extends_clause node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Extends_clause node :: errors + | _ -> errors (* TODO *) + +and check_class_body node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Class_body node :: errors + | _ -> errors (* TODO *) + +and check_class_member (decorators, node) errors = + match get_name node with + | "method_definition" -> check_method_definition_with_decorators decorators node errors + | "method_signature" -> check_method_signature node errors + | "class_static_block" -> check_class_static_block node errors + | "abstract_method_signature" -> check_abstract_method_signature node errors + | "index_signature" -> check_index_signature node errors + | "public_field_definition" -> check_public_field_definition node errors + | _ -> format_msg Class_member node :: errors + +and check_method_definition_with_decorators decorators node errors = + let errors = List.fold_left ~f:(swap check_decorator) ~init:errors decorators in + check_method_definition node errors + +and check_method_definition node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Method_definition node :: errors + | _ -> errors (* TODO *) + +and check_class_static_block node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Static_block node :: errors + | _ -> errors (* TODO *) + +and check_abstract_method_signature node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Abstract_method_signature node :: errors + | _ -> errors (* TODO *) + +and check_public_field_definition node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Public_field_definition node :: errors + | _ -> errors (* TODO *) + +(* Meta-property *) + +and check_meta_property node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Meta_property node :: errors + | _ -> errors (* TODO *) + +(* Call expression *) + +and check_call_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Call_expression node :: errors + | _ -> errors (* TODO *) + +and check_type_arguments node errors = + check_chevrons node check_type Type_arguments errors + +and check_arguments node errors = check_parens node check_argument Arguments errors + +and check_argument node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Argument node :: errors + | "spread_element" -> check_spread_element node errors + | _ -> check_expression node errors (* Hidden *) + +(* Non-null expression *) + +and check_non_null_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Non_null_expression node :: errors + | _ -> errors (* TODO *) + +(* Sequence expression *) + +and check_sequence_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Expression node :: errors + | _ -> check_named_children node check_expression errors + +(* TYPE + + The non-terminals "type" and "primary_type" are supertypes in the + TypeScript grammar, which means that they are hidden rules. *) + +and check_type node errors = + match get_name node with + | "function_type" -> check_function_type node errors + | "readonly_type" -> check_readonly_type node errors + | "constructor_type" -> check_constructor_type node errors + | "infer_type" -> check_infer_type node errors + (* A couple of aliases *) + | "member_expression" -> + check_type_query_member_expression_in_type_annotation node errors + | "call_expression" -> check_type_query_call_expression_in_type_annotation node errors + (* "primary_type" is hidden *) + | _ -> check_primary_type node errors + +(* Primary type *) + +and check_primary_type node errors = + match get_name node with + | "parenthesized_type" -> check_parenthesized_type node errors + | "predefined_type" -> check_predefined_type node errors + | "type_identifier" -> check_type_identifier node errors (* Including "const" *) + | "nested_type_identifier" -> check_nested_type_identifier node errors + | "generic_type" -> check_generic_type node errors + | "object_type" -> check_object_type node errors + | "array_type" -> check_array_type node errors + | "tuple_type" -> check_tuple_type node errors + | "flow_maybe_type" -> check_flow_maybe_type node errors + | "type_query" -> check_type_query node errors + | "index_type_query" -> check_index_type_query node errors + | "this_type" -> check_kwd node errors ~err:This + | "existential_type" -> check_existential_type node errors + | "literal_type" -> check_literal_type node errors + | "lookup_type" -> check_lookup_type node errors + | "conditional_type" -> check_conditional_type node errors + | "template_literal_type" -> check_template_literal_type node errors + | "intersection_type" -> check_intersection_type node errors + | "union_type" -> check_union_type node errors + | _ -> format_msg Type_expression node :: errors + +(* Type queries in type annotations (expressions) *) + +and check_type_query_member_expression_in_type_annotation node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Member_or_call node :: errors + | _ -> errors (* TODO *) + +and check_type_query_property node errors = + match get_name node with + | "property_identifier" -> check_identifier node errors + | "private_property_identifier" -> check_identifier node errors + | _ -> format_msg Property_identifier node :: errors + +and check_type_query_call_expression_in_type_annotation node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Member_expression node :: errors + | _ -> errors (* TODO *) + +(* Flow maybe type + + flow_maybe_type: $ => prec.right(seq('?', $.primary_type)) + *) + +and check_flow_maybe_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Type node :: errors + | _ -> errors (* TODO *) + +(* Parenthesized type *) + +and check_parenthesized_type node errors = + check_parens node check_type Parenthesized_type errors + +(* Predefined type *) + +and check_predefined_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Predefined_type node :: errors + | _ -> errors (* TODO *) + +(* Nested type identifier *) + +and check_nested_type_identifier node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Nested_type_identifier node :: errors + | _ -> errors (* TODO *) + +(* Nested identifier *) + +and check_nested_identifier node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Identifier_or_member node :: errors + | _ -> errors (* TODO *) + +(* Generic type *) + +and check_generic_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Generic_type node :: errors + | _ -> errors (* TODO *) + +(* Object type *) + +and check_object_type node errors = + check_braces node check_object_type_field Object_type errors + +and check_object_type_field node errors = + match get_name node with + | "export_statement" -> check_export_statement node errors + | "property_signature" -> check_property_signature node errors + | "call_signature" -> check_call_signature node errors + | "construct_signature" -> check_construct_signature node errors + | "index_signature" -> check_index_signature node errors + | "method_signature" -> check_method_signature node errors + | _ -> format_msg Object_type_field node :: errors + +(* Property signature *) + +and check_property_signature node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Property_signature node :: errors + | _ -> errors (* TODO *) + +(* Call signature *) + +and check_call_signature node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Call_signature node :: errors + | _ -> errors (* TODO *) + +(* Asserts annotation *) + +and check_asserts_annotation node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Asserts_annotation node :: errors + | _ -> errors (* TODO *) + +and check_asserts node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Asserts node :: errors + | _ -> errors (* TODO *) + +(* Type predicate annotation *) + +and check_type_predicate_annotation node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Type_predicate node :: errors + | _ -> errors (* TODO *) + +(* Construct signature *) + +and check_construct_signature node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Construct_signature node :: errors + | _ -> errors (* TODO *) + +(* Index signature *) + +and check_index_signature node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Index_signature node :: errors + | _ -> errors (* TODO *) + +and check_plus_minus node errors = + match get_name node with + | "+" -> errors + | "-" -> errors + | _ -> format_msg Plus_or_minus node :: errors + +and check_mapped_type_clause node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Mapped_type_signature node :: errors + | _ -> errors (* TODO *) + +and check_omitting_type_annotation node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Omitting_type_annotation node :: errors + | _ -> errors (* TODO *) + +and check_adding_type_annotation node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Adding_type_annotation node :: errors + | _ -> errors (* TODO *) + +and check_opting_type_annotation node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Opting_type_annotation node :: errors + | _ -> errors (* TODO *) + +(* Method signature *) + +and check_method_signature node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Method_signature node :: errors + | _ -> errors (* TODO *) + +(* Array type *) + +and check_array_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Array_type node :: errors + | _ -> errors (* TODO *) + +(* Tuple type *) + +and check_tuple_type node errors = + check_brackets node check_tuple_type_member Tuple_type errors + +and check_tuple_type_member node errors = + match get_name node with + | "required_parameter" -> check_tuple_parameter node errors (* Alias *) + | "optional_parameter" -> check_optional_tuple_parameter node errors (* Alias *) + | "optional_type" -> check_optional_type node errors + | "rest_type" -> check_rest_type node errors + | _ -> check_type node errors (* "type" is a hidden rule *) + +and check_tuple_parameter node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Tuple_parameter node :: errors + | _ -> errors (* TODO *) + +and check_optional_tuple_parameter node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Optional_tuple_parameter node :: errors + | _ -> errors (* TODO *) + +(* Type annotation *) + +and check_type_annotation node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Type_annotation node :: errors + | _ -> errors (* TODO *) + +(* Rest pattern *) + +and check_rest_pattern node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Rest_pattern node :: errors + | _ -> errors (* TODO *) + +(* Optional type *) + +and check_optional_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Optional_type node :: errors + | _ -> errors (* TODO *) + +(* Rest type *) + +and check_rest_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Rest_type node :: errors + | _ -> errors (* TODO *) + +(* LHS expression *) + +and check_lhs_expression node errors = + match get_name node with + | "member_expression" -> check_member_expression node errors + | "subscript_expression" -> check_subscript_expression node errors + | "identifier" -> check_identifier node errors + | "undefined" -> check_kwd node errors ~err:Undefined + | "object_pattern" -> check_object_pattern node errors + | "array_pattern" -> check_array_pattern node errors + | "non_null_expression" -> check_non_null_expression node errors + | _ -> format_msg Expression node :: errors + +(* Type query *) + +and check_type_query node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Type_query node :: errors + | _ -> errors (* TODO *) + +and check_type_query_subscript_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Type_query_subscript node :: errors + | _ -> errors (* TODO *) + +and check_type_query_member_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Type_query_member node :: errors + | _ -> errors (* TODO *) + +and check_object_denotation node errors = + match get_name node with + | "identifier" -> check_identifier node errors + | "this" -> check_kwd node errors ~err:This + | "subscript_expression" -> check_type_query_subscript_expression node errors + | "member_expression" -> check_type_query_member_expression node errors + | "call_expression" -> check_type_query_call_expression node errors + | _ -> format_msg Object_denotation node :: errors + +and check_property_field node errors = check_type_query_property node errors + +and check_type_query_instantiation_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Type_query_instantiation node :: errors + | _ -> errors (* TODO *) + +and check_function_field node errors = + match get_name node with + | "import" -> check_kwd node errors ~err:Import + | "identifier" -> check_identifier node errors + | "member_expression" -> check_type_query_member_expression node errors + | "subscript_expression" -> check_type_query_subscript_expression node errors + | _ -> format_msg Function_denotation node :: errors + +and check_type_query_call_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Type_query_call node :: errors + | _ -> errors (* TODO *) + +(* Index type query *) + +and check_index_type_query node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Index_type_query node :: errors + | _ -> errors (* TODO *) + +(* Existential type *) + +and check_existential_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Existential_type node :: errors + | _ -> errors (* TODO *) + +(* Literal type *) + +and check_literal_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Literal_type node :: errors + | _ -> errors (* TODO *) + +(* Look up type + + The non-terminals "type" and "primary_type" are supertypes in the + TypeScript grammar, which means that they are hidden rules. *) + +and check_lookup_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Lookup_type node :: errors + | _ -> errors (* TODO *) + +(* Conditional type *) + +and check_conditional_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Conditional_type node :: errors + | _ -> errors (* TODO *) + +(* Template literal type *) + +and check_template_literal_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Template_literal_type node :: errors + | _ -> errors (* TODO *) + +(* Intersection type *) + +and check_intersection_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Intersection_type node :: errors + | _ -> errors (* TODO *) + +(* Union type *) + +and check_union_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Union_type node :: errors + | _ -> errors (* TODO *) + +(* Function type *) + +and check_function_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Function_type node :: errors + | _ -> errors (* TODO *) + +(* Type predicate *) + +and check_type_predicate node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Type_predicate node :: errors + | _ -> errors (* TODO *) + +(* Readonly type *) + +and check_readonly_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Readonly_type node :: errors + | _ -> errors (* TODO *) + +(* Constructor type *) + +and check_constructor_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Constructor_type node :: errors + | _ -> errors (* TODO *) + +and check_formal_parameters node errors = + check_parens node check_formal_parameter Parameters errors + +and check_formal_parameter node errors = + match get_name node with + | "required_parameter" -> check_required_parameter node errors + | "optional_parameter" -> check_optional_parameter node errors + | _ -> format_msg Parameter node :: errors + +and check_optional_parameter node errors = check_required_parameter node errors + +and check_required_parameter node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Required_parameter node :: errors + | _ -> errors (* TODO *) + +(* Infer type *) + +and check_infer_type node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Infer node :: errors + | _ -> errors (* TODO *) + +(* Decorator *) + +and check_decorator node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Decorator node :: errors + | _ -> errors (* TODO *) + +and check_decorator_member_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Decorator_member node :: errors + | _ -> errors (* TODO *) + +and check_decorator_call_expression node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Decorator_call node :: errors + | _ -> errors (* TODO *) + +and check_decorator_parenthesized_expression node errors = + check_parens node check_decorator_in_parens Parenthesized_decorator errors + +and check_decorator_in_parens node errors = + match get_name node with + | "identifier" -> check_identifier node errors + | "member_expression" -> check_decorator_member_expression node errors + | _ -> check_call_expression node errors + +(* Accessibility modifier *) + +and check_accessibility_modifier node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Public_private_protected node :: errors + | _ -> errors (* TODO *) + +(* Override modifier *) + +and check_override_modifier node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Override node :: errors + | _ -> errors (* TODO *) + +(* PATTERN + + The JavasScript tree-sitter grammar have the non-terminal + "pattern" be a supertype, that is, a hidden rule. *) + +(* Object pattern *) + +and check_object_pattern node errors = + check_braces node check_object_pattern_field Object_pattern errors + +and check_object_pattern_field node errors = + match get_name node with + | "pair_pattern" -> check_pair_pattern node errors + | "rest_pattern" -> check_rest_pattern node errors + | "object_assignment_pattern" -> check_object_assignment_pattern node errors + | "shorthand_property_identifier_pattern" -> + check_shorthand_property_identifier_pattern node errors + | _ -> format_msg Object_pattern_field node :: errors + +(* Pair pattern *) + +and check_pair_pattern node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Pair_pattern node :: errors + | _ -> errors (* TODO *) + +(* Assignment pattern *) + +and check_assignment_pattern node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Assignment_pattern node :: errors + | _ -> errors (* TODO *) + +(* Property names *) + +and check_property_name node errors = + match get_name node with + | "property_identifier" -> check_identifier node errors + | "private_property_identifier" -> check_identifier node errors + | "string" -> check_string node errors + | "number" -> check_number node errors + | "computed_property_name" -> check_computed_property_name node errors + | _ -> format_msg Property_name node :: errors + +and check_computed_property_name node errors = + check_brackets node check_expression Computed_property_name errors + +and check_shorthand_property_identifier_pattern node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Identifier node :: errors + | _ -> errors (* TODO *) + +(* Object assignment pattern *) + +and check_object_assignment_pattern node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Object_assignment_pattern node :: errors + | _ -> errors (* TODO *) + +and check_object_lhs_pattern node errors = + match get_name node with + | "shorthand_property_identifier_pattern" -> + check_shorthand_property_identifier_pattern node errors + | _ -> check_destructuring_pattern node errors + +(* Rule "_destructuring_pattern" is inlined. *) + +and check_destructuring_pattern node errors = + match get_name node with + | "object_pattern" -> check_object_pattern node errors + | "array_pattern" -> check_array_pattern node errors + | _ -> format_msg Object_or_array_pattern node :: errors + +(* Array pattern *) + +and check_array_pattern node errors = + check_brackets node check_array_pattern_cell Array_pattern errors + +and check_array_pattern_cell node errors = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> format_msg Array_cell_pattern node :: errors + | "assignment_pattern" -> check_assignment_pattern node errors + | _ -> check_pattern node errors (* hidden rule *) + +(* General patterns (hidden rule) *) + +and check_pattern node errors = + match get_name node with + | "rest_pattern" -> check_rest_pattern node errors + | _ -> check_lhs_expression node errors diff --git a/lib/typescript_ast/check_main.ml b/lib/typescript_ast/check_main.ml new file mode 100644 index 0000000000..ca92ce8876 --- /dev/null +++ b/lib/typescript_ast/check_main.ml @@ -0,0 +1,49 @@ +(* Collecting all errors in the tree-sitter CST for TypeScript *) + +open Core + +(* Vendored *) + +module Region = Simple_utils.Region + +(* Tree-sitter ctypes-APIs for types and related functions *) + +module TS_types = Tree_sitter.Api.Types +module TS_fun = Tree_sitter.Api.Functions + +(* Local *) + +module Ts_wrap = Typescript_ast.Ts_wrap +module Loc_map = Typescript_ast.Loc_map + +(* Parsing and collecting all errors *) + +let parse filename : string list = + (* Loading the code as text *) + let file : string = Core.In_channel.read_all filename in + (* Building the map from line+columns to positions *) + let line_map = Loc_map.scan_string file in + (* Parsing the code into a tree *) + let tree : Ts_wrap.ts_tree_ptr = Ts_wrap.parse_typescript_string file in + (* Getting ahold of the root of the tree *) + let program_node : Ts_wrap.ts_tree = TS_fun.ts_tree_root_node tree in + (* Traversing the tree *) + let errors = Check.check_program ~filename ~file line_map program_node in + (* Releasing the memory allocated to the tree *) + let () = TS_fun.ts_tree_delete tree in + errors + +(* Reading the input TypeScript, parsing and printing the errors *) + +let cli_args : string array = Sys.get_argv () + +let print_errors = + let print msg = Printf.eprintf "Error: %s\n%!" msg in + Core.List.iter ~f:print + +let () = + match Array.length cli_args with + | 2 -> + let file = cli_args.(1) in + print_errors (parse file) + | _ -> prerr_endline ("Usage: " ^ cli_args.(0) ^ " [file]") diff --git a/lib/typescript_ast/decode.ml b/lib/typescript_ast/decode.ml new file mode 100644 index 0000000000..cb241f1c92 --- /dev/null +++ b/lib/typescript_ast/decode.ml @@ -0,0 +1,5002 @@ +(* Decoding the tree-sitter CST for TypeScript *) + +open Core + +(* Dependencies and scopes *) + +module Region = Simple_utils.Region +module Wrap = Lexing_shared.Wrap +module Ts_wrap = Typescript_ast.Ts_wrap +module Loc_map = Typescript_ast.Loc_map +module Syntax_err = Typescript_ast.Syntax_err +module Ast = Typescript_ast.Ast +module Lexeme = Typescript_ast.Lexeme +module Number = Typescript_ast.Number +open Syntax_err +open Ts_wrap +open Ast + +(* Utilities *) + +let sprintf = Printf.sprintf + +(* Monadic let-binder for result values *) + +let ( let* ) v f = Result.bind v ~f + +(* Skipping strings in a list until a colon is found *) + +let rec skip_until_colon = function + | [] -> [] + | node :: nodes -> + (match get_name node with + | ":" -> nodes + | _ -> skip_until_colon nodes) + +(* Source map for converting vertical and horizontal offset ranges + into regions *) + +let get_region : (Ts_wrap.ts_tree -> Region.t) ref = + ref (fun _ -> failwith "Internal error: Decode.get_region") + +(* The input source (default: a hundred lines) *) + +let input : Buffer.t ref = ref (Buffer.create (80 * 100)) + +(* Utilities *) + +let wrap decode ?comments node : ('a Wrap.t, _) result = + let* decoded_node = decode ?comments node in + Ok (Wrap.make decoded_node (!get_region node)) + +(* Tayloring the fetching of a field, with an error message in case of + failure. If [!debug], a missing field yields internal information. *) + +let debug = ref false + +let child_with_field field node ~err : (_, string Region.reg) result = + match Ts_wrap.child_with_field ~get_region field node with + | Ok _ as ok -> ok + | Error () -> + let region = !get_region node in + let region' = + if Region.is_empty region then "empty region" else region#compact `Byte + in + let value = + if !debug + then ( + let name = get_name node in + if String.equal name "NULL" + then sprintf "ERROR: NULL parent of field %S." field + else sprintf "ERROR: Node %S (%s) is missing the field %S." name region' field) + else Syntax_err.to_string err + in + Error Region.{ region; value } + +(* Making errors *) + +let pack_err err node = + let value = Syntax_err.to_string err + and region = !get_region node in + Region.{ value; region } + +let mk_err err node = Error (pack_err err node) + +(* Wrapping the fetching of nodes *) + +let first_child_named name node ~err = + Ts_wrap.first_child_named name node ~msg:(pack_err err node) + +let child_ranked index node ~err = + Ts_wrap.child_ranked index node ~msg:(pack_err err node) + +let named_child_ranked index node ~err = + Ts_wrap.named_child_ranked index node ~msg:(pack_err err node) + +let last_child node ~err = Ts_wrap.last_child node ~msg:(pack_err err node) +let next_sibling node ~err = Ts_wrap.next_sibling node ~msg:(pack_err err node) +let prev_sibling node ~err = Ts_wrap.prev_sibling node ~msg:(pack_err err node) + +let check_first_error_child node = + match first_child_named_opt "ERROR" node with + | None -> Ok () + | Some error_child -> Error (pack_err Syntax_err.Syntax_error error_child) + +(* Decoding literals *) + +let dec_comments ?(comments = []) node : Wrap.comment list = + let f node = + let region = !get_region node in + let value = Lexeme.read !input region in + Wrap.Block Region.{ value; region } + in + List.map ~f (comments @ prev_comments node) + +let make_node ?comments node : string wrap = + let region = !get_region node in + let root = Lexeme.read !input region + and comments = dec_comments ?comments node in + Wrap.make ~comments root region + +let dec_identifier ?comments node : identifier = make_node ?comments node +let dec_string ?comments node : string_literal = make_node ?comments node +let dec_regex ?comments node : string_literal = make_node ?comments node + +let dec_number ?(comments = []) node : (number, _) result = + let region = !get_region node in + let lexeme = Lexeme.read !input region in + let lexbuf = Lexing.from_string lexeme + and comments = dec_comments ~comments node in + Number.scan comments region lexbuf + +(* Keywords *) + +let dec_kwd ?comments node ~err = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err err node + | _ -> Ok (make_node ?comments node) + +let dec_kwd_infer = dec_kwd ~err:Syntax_err.Infer +let dec_kwd_keyof = dec_kwd ~err:Syntax_err.Keyof +let dec_kwd_meta = dec_kwd ~err:Syntax_err.Meta +let dec_kwd_target = dec_kwd ~err:Syntax_err.Target +let dec_kwd_false = dec_kwd ~err:Syntax_err.False +let dec_kwd_true = dec_kwd ~err:Syntax_err.True +let dec_kwd_super = dec_kwd ~err:Syntax_err.Super +let dec_kwd_null = dec_kwd ~err:Syntax_err.Null +let dec_kwd_satisfies = dec_kwd ~err:Syntax_err.Satisfies +let dec_kwd_yield = dec_kwd ~err:Syntax_err.Yield +let dec_kwd_new = dec_kwd ~err:Syntax_err.New +let dec_kwd_instanceof = dec_kwd ~err:Syntax_err.Instanceof +let dec_kwd_implements = dec_kwd ~err:Syntax_err.Implements +let dec_kwd_assert = dec_kwd ~err:Syntax_err.Assert +let dec_kwd_as = dec_kwd ~err:Syntax_err.As +let dec_kwd_async = dec_kwd ~err:Syntax_err.Async +let dec_kwd_function = dec_kwd ~err:Syntax_err.Function +let dec_kwd_override = dec_kwd ~err:Syntax_err.Override +let dec_kwd_readonly = dec_kwd ~err:Syntax_err.Readonly +let dec_kwd_public = dec_kwd ~err:Syntax_err.Public +let dec_kwd_private = dec_kwd ~err:Syntax_err.Private +let dec_kwd_protected = dec_kwd ~err:Syntax_err.Protected +let dec_kwd_set = dec_kwd ~err:Syntax_err.Set +let dec_kwd_get = dec_kwd ~err:Syntax_err.Get +let dec_kwd_static = dec_kwd ~err:Syntax_err.Static +let dec_kwd_this = dec_kwd ~err:Syntax_err.This +let dec_kwd_is = dec_kwd ~err:Syntax_err.Is +let dec_kwd_class = dec_kwd ~err:Syntax_err.Class +let dec_kwd_const = dec_kwd ~err:Syntax_err.Const +let dec_kwd_let = dec_kwd ~err:Syntax_err.Let +let dec_kwd_undefined = dec_kwd ~err:Syntax_err.Undefined +let dec_kwd_abstract = dec_kwd ~err:Syntax_err.Abstract +let dec_kwd_declare = dec_kwd ~err:Syntax_err.Declare +let dec_kwd_accessor = dec_kwd ~err:Syntax_err.Accessor +let dec_kwd_global = dec_kwd ~err:Syntax_err.Global +let dec_kwd_module = dec_kwd ~err:Syntax_err.Module +let dec_kwd_enum = dec_kwd ~err:Syntax_err.Enum +let dec_kwd_import = dec_kwd ~err:Syntax_err.Import +let dec_kwd_interface = dec_kwd ~err:Syntax_err.Interface +let dec_kwd_extends = dec_kwd ~err:Syntax_err.Extends +let dec_kwd_namespace = dec_kwd ~err:Syntax_err.Namespace +let dec_kwd_type = dec_kwd ~err:Syntax_err.Type +let dec_kwd_using = dec_kwd ~err:Syntax_err.Using +let dec_kwd_return = dec_kwd ~err:Syntax_err.Return +let dec_kwd_switch = dec_kwd ~err:Syntax_err.Switch +let dec_kwd_case = dec_kwd ~err:Syntax_err.Case +let dec_kwd_default = dec_kwd ~err:Syntax_err.Default +let dec_kwd_throw = dec_kwd ~err:Syntax_err.Throw +let dec_kwd_while = dec_kwd ~err:Syntax_err.While +let dec_kwd_with = dec_kwd ~err:Syntax_err.With +let dec_kwd_any = dec_kwd ~err:Syntax_err.Any +let dec_kwd_number = dec_kwd ~err:Syntax_err.Number +let dec_kwd_boolean = dec_kwd ~err:Syntax_err.Boolean +let dec_kwd_string = dec_kwd ~err:Syntax_err.String +let dec_kwd_symbol = dec_kwd ~err:Syntax_err.Symbol +let dec_kwd_unique_symbol = dec_kwd ~err:Syntax_err.Unique_symbol +let dec_kwd_void = dec_kwd ~err:Syntax_err.Void +let dec_kwd_unknown = dec_kwd ~err:Syntax_err.Unknown +let dec_kwd_never = dec_kwd ~err:Syntax_err.Never +let dec_kwd_object = dec_kwd ~err:Syntax_err.Object +let dec_kwd_asserts = dec_kwd ~err:Syntax_err.Asserts +let dec_kwd_debugger = dec_kwd ~err:Syntax_err.Debugger +let dec_kwd_break = dec_kwd ~err:Syntax_err.Break +let dec_kwd_continue = dec_kwd ~err:Syntax_err.Continue +let dec_kwd_do = dec_kwd ~err:Syntax_err.Do +let dec_kwd_export = dec_kwd ~err:Syntax_err.Export +let dec_kwd_for = dec_kwd ~err:Syntax_err.For +let dec_kwd_from = dec_kwd ~err:Syntax_err.From +let dec_kwd_await = dec_kwd ~err:Syntax_err.Await +let dec_kwd_var = dec_kwd ~err:Syntax_err.Var +let dec_kwd_in = dec_kwd ~err:Syntax_err.In +let dec_kwd_of = dec_kwd ~err:Syntax_err.Of +let dec_kwd_if = dec_kwd ~err:Syntax_err.If +let dec_kwd_else = dec_kwd ~err:Syntax_err.Else +let dec_kwd_typeof = dec_kwd ~err:Syntax_err.Typeof +let dec_kwd_try = dec_kwd ~err:Syntax_err.Try +let dec_kwd_catch = dec_kwd ~err:Syntax_err.Catch +let dec_kwd_require = dec_kwd ~err:Syntax_err.Require +let dec_kwd_delete = dec_kwd ~err:Syntax_err.Delete +let dec_kwd_finally = dec_kwd ~err:Syntax_err.Finally + +(* Symbols *) + +let dec_sym ?comments node ~err = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err err node + | _ -> Ok (make_node ?comments node) + +let dec_sym_asterisk = dec_sym ~err:Asterisk +let dec_sym_equal = dec_sym ~err:Equal +let dec_sym_strict_equal = dec_sym ~err:Strict_equal +let dec_sym_lparen = dec_sym ~err:Left_parenthesis +let dec_sym_rparen = dec_sym ~err:Right_parenthesis +let dec_sym_qmark = dec_sym ~err:Question_mark +let dec_sym_plus_equal = dec_sym ~err:Plus_equal +let dec_sym_minus_equal = dec_sym ~err:Minus_equal +let dec_sym_mult_equal = dec_sym ~err:Mult_equal +let dec_sym_div_equal = dec_sym ~err:Div_equal +let dec_sym_rem_equal = dec_sym ~err:Rem_equal +let dec_sym_xor_equal = dec_sym ~err:Xor_equal +let dec_sym_and_equal = dec_sym ~err:And_equal +let dec_sym_or_equal = dec_sym ~err:Or_equal +let dec_sym_shift_right_equal = dec_sym ~err:Right_shift_equal +let dec_sym_increment = dec_sym ~err:Increment +let dec_sym_decrement = dec_sym ~err:Decrement +let dec_sym_lbrace = dec_sym ~err:Left_brace +let dec_sym_rbrace = dec_sym ~err:Right_brace +let dec_sym_lbracket = dec_sym ~err:Left_bracket +let dec_sym_rbracket = dec_sym ~err:Right_bracket +let dec_sym_optional_chain = dec_sym ~err:Optional_chain +let dec_sym_backquote = dec_sym ~err:Backquote +let dec_sym_colon = dec_sym ~err:Colon +let dec_sym_ellipsis = dec_sym ~err:Ellipsis +let dec_sym_arrow = dec_sym ~err:Arrow +let dec_sym_emark = dec_sym ~err:Exclamation_mark +let dec_sym_dot = dec_sym ~err:Dot +let dec_sym_omitting = dec_sym ~err:Omitting_type_annotation +let dec_sym_adding = dec_sym ~err:Adding_type_annotation +let dec_sym_opting = dec_sym ~err:Opting_type_annotation +let dec_sym_and = dec_sym ~err:And +let dec_sym_vbar = dec_sym ~err:Vertical_bar +let dec_sym_unsigned_shift_right_equal = dec_sym ~err:Unsigned_shift_right_equal +let dec_sym_shift_left_equal = dec_sym ~err:Left_shift_equal +let dec_sym_exponent_equal = dec_sym ~err:Exponent_equal +let dec_sym_conjunction_equal = dec_sym ~err:Conjunction_equal +let dec_sym_disjunction_equal = dec_sym ~err:Disjunction_equal +let dec_sym_non_null_equal = dec_sym ~err:Non_null_equal +let dec_sym_tilde = dec_sym ~err:Tilde +let dec_sym_minus = dec_sym ~err:Minus +let dec_sym_plus = dec_sym ~err:Plus +let dec_sym_conjunction = dec_sym ~err:Conjunction +let dec_sym_disjunction = dec_sym ~err:Disjunction +let dec_sym_shift_right = dec_sym ~err:Right_shift +let dec_sym_unsigned_shift_right = dec_sym ~err:Unsigned_shift_right +let dec_sym_shift_left = dec_sym ~err:Left_shift +let dec_sym_xor = dec_sym ~err:Xor +let dec_sym_or = dec_sym ~err:Or +let dec_sym_div = dec_sym ~err:Div +let dec_sym_rem = dec_sym ~err:Rem +let dec_sym_exponent = dec_sym ~err:Exponent +let dec_sym_less_than = dec_sym ~err:Less_than +let dec_sym_less_than_or_equal = dec_sym ~err:Less_than_or_equal +let dec_sym_no_conv_equal = dec_sym ~err:No_conv_equal +let dec_sym_different = dec_sym ~err:Different +let dec_sym_no_conv_different = dec_sym ~err:No_conv_different +let dec_sym_greater_than_or_equal = dec_sym ~err:Greater_than_or_equal +let dec_sym_greater_than = dec_sym ~err:Greater_than +let dec_sym_non_null = dec_sym ~err:Non_null + +(* Optional nodes *) + +let make_opt decoder node = Option.map ~f:decoder node + +let make_opt_res decode = function + | None -> Ok None + | Some value -> + let* decoded = decode value in + Ok (Some decoded) + +(* Handling some modifiers *) + +let mk_set_get_all node : (set_get_all option, _) result = + let kwd_set = first_child_named_opt "set" node + and kwd_get = first_child_named_opt "get" node + and sym_asterisk = first_child_named_opt "*" node in + match kwd_set, kwd_get, sym_asterisk with + | None, None, None -> Ok None + | Some kwd_set, _, _ -> + let* kwd_set = dec_kwd_set kwd_set in + Ok (Some (Set kwd_set)) + | _, Some kwd_get, _ -> + let* kwd_get = dec_kwd_get kwd_get in + Ok (Some (Get kwd_get)) + | _, _, Some sym_asterisk -> + let* sym_asterisk = dec_sym_asterisk sym_asterisk in + Ok (Some (All sym_asterisk)) + +(* Decoding children of the same type *) + +let list_of_children ?(comments = []) decode children : ('a list, _) result = + let f raw_child = List.cons (decode ?comments:None raw_child) in + match children with + | [] -> Ok [] + | fst_raw_child :: siblings -> + let fst_child = decode ?comments:(Some comments) fst_raw_child in + let children = fst_child :: List.fold_right ~f ~init:[] siblings in + Result.all children + +let ne_list_opt_of_children ?(comments = []) decode children + : ('a ne_list option, _) result + = + let f raw_child = List.cons (decode ?comments:None raw_child) in + match children with + | [] -> Ok None + | fst_raw_child :: siblings -> + let* fst_child = decode ?comments:(Some comments) fst_raw_child in + let* tail = List.fold_right ~f ~init:[] siblings |> Result.all in + Ok (Some Nonempty_list.(fst_child :: tail)) + +let ne_list_of_children ?(comments = []) decode error children : ('a ne_list, _) result = + let* list = ne_list_opt_of_children ~comments decode children in + match list with + | None -> error + | Some ne_list -> Ok ne_list + +let wrap_ne_list_opt_of_children ?(comments = []) decode children + : ('a ne_list wrap option, _) result + = + let f raw_child = List.cons (decode ?comments:None raw_child) in + match children with + | [] -> Ok None + | fst_raw_child :: siblings -> + let* fst_child = decode ?comments:(Some comments) fst_raw_child in + let fst_region = !get_region fst_raw_child in + let* tail = List.fold_right ~f ~init:[] siblings |> Result.all in + let region = + match List.last siblings with + | None -> fst_region + | Some last_child -> Region.cover fst_region (!get_region last_child) + in + let ne_list = Nonempty_list.(fst_child :: tail) in + Ok (Some (Wrap.make ne_list region)) + +(* Decoding enclosed unique child *) + +let dec_enclosed ?(comments = []) node decode opening closing ~open_err ~close_err ~err + : ('a enclosed wrap, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err err node + | _ -> + let comments = comments @ prev_comments node in + let* opening = first_child_named opening node ~err:open_err in + let* opening = dec_sym ~comments opening ~err:open_err in + let* closing = first_child_named closing node ~err:close_err in + let* closing = dec_sym closing ~err:close_err in + let* child = (* We assume one child *) child_ranked 1 node ~err in + let* contents = decode child in + let region = !get_region node in + let* () = check_first_error_child node in + Ok (Wrap.make { opening; contents; closing } region) + +let dec_brackets ?comments node decode ~err : ('a brackets, _) result = + let* brackets = + dec_enclosed + ?comments + node + decode + "[" + "]" + ~open_err:Left_bracket + ~close_err:Right_bracket + ~err + in + Ok (Brackets brackets) + +let dec_parens ?comments node decode ~err : ('a parens, _) result = + let* parens = + dec_enclosed + ?comments + node + decode + "(" + ")" + ~open_err:Left_parenthesis + ~close_err:Right_parenthesis + ~err + in + Ok (Parens parens) + +(* Decoding enclosed lists of children *) + +let dec_enclosed_list ?comments node decode opening closing ~open_err ~close_err ~err + : ('a list enclosed wrap, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err err node + | _ -> + let comments = dec_comments ?comments node in + let* opening = first_child_named opening node ~err:open_err in + let* opening = dec_sym opening ~err:open_err in + let* closing = first_child_named closing node ~err:close_err in + let* closing = dec_sym closing ~err:close_err in + let clauses = collect_named_children node in + let* contents = list_of_children decode clauses in + let region = !get_region node in + let* () = check_first_error_child node in + Ok (Wrap.make ~comments { opening; contents; closing } region) + +let dec_list_in_braces ?comments node decode ~err : ('a list braces, _) result = + let* list = + dec_enclosed_list + ?comments + node + decode + "{" + "}" + ~open_err:Left_brace + ~close_err:Right_brace + ~err + in + Ok (Braces list) + +let dec_list_in_chevrons ?comments node decode ~err : ('a list chevrons, _) result = + let* list = + dec_enclosed_list + ?comments + node + decode + "<" + ">" + ~open_err:Left_chevron + ~close_err:Right_chevron + ~err + in + Ok (Chevrons list) + +let dec_list_in_brackets ?comments node decode ~err : ('a list brackets, _) result = + let* list = + dec_enclosed_list + ?comments + node + decode + "[" + "]" + ~open_err:Left_bracket + ~close_err:Right_bracket + ~err + in + Ok (Brackets list) + +let dec_list_in_parens ?comments node decode ~err : ('a list parens, _) result = + let* list = + dec_enclosed_list + ?comments + node + decode + "(" + ")" + ~open_err:Left_parenthesis + ~close_err:Right_parenthesis + ~err + in + Ok (Parens list) + +(* Decoding enclosed non-empty lists *) + +let dec_enclosed_ne_list + ?(comments = []) + node + decode + opening + closing + ~open_err + ~close_err + ~err + : ('a ne_list enclosed wrap, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err err node + | _ -> + let comments = comments @ prev_comments node in + let* opening = first_child_named opening node ~err:open_err in + let* opening = dec_sym ~comments opening ~err:open_err in + let* closing = first_child_named closing node ~err:close_err in + let* closing = dec_sym closing ~err:close_err in + let clauses = collect_named_children node in + let error = mk_err err node in + let* contents = ne_list_of_children decode error clauses in + let region = !get_region node in + let* () = check_first_error_child node in + Ok (Wrap.make { opening; contents; closing } region) + +let dec_ne_list_in_chevrons ?comments node decode ~err : ('a ne_list chevrons, _) result = + let* chevrons = + dec_enclosed_ne_list + ?comments + node + decode + "<" + ">" + ~open_err:Left_chevron + ~close_err:Right_chevron + ~err + in + Ok (Chevrons chevrons) + +(* +let dec_ne_list_in_brackets ?comments node decode error ~err + : ('a ne_list brackets, _) result + = + let* brackets = + dec_enclosed_ne_list + ?comments + node + decode + error + "[" + "]" + ~open_err:Left_bracket + ~close_err:Right_bracket + ~err + in + Ok (Brackets brackets) + *) + +(* STATEMENTS + + The JavaScript tree-sitter grammar has the non-terminals + "statement" be a supertype, that is, a hidden rule. *) + +let rec dec_statements ?(comments = []) node : (statements, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Statement node + | _ -> + let* () = check_first_error_child node in + let children = collect_named_children node in + wrap_ne_list_opt_of_children ~comments dec_statement children + +and dec_statement ?(comments = []) node : (statement, _) result = + match get_name node with + | "export_statement" -> + let* statement = wrap dec_export_statement ~comments node in + Ok (S_export_statement statement) + | "import_statement" -> + let* statement = wrap dec_import_statement ~comments node in + Ok (S_import_statement statement) + | "debugger_statement" -> + let* kwd_debugger = dec_kwd_debugger ~comments node in + Ok (S_debugger_statement kwd_debugger) + | "expression_statement" -> + let* expression = dec_expression_statement ~comments node in + Ok (S_expression_statement expression) + | "statement_block" -> + let* statement = dec_statement_block ~comments node in + Ok (S_statement_block statement) + | "if_statement" -> + let* statement = wrap dec_if_statement ~comments node in + Ok (S_if_statement statement) + | "switch_statement" -> + let* statement = wrap dec_switch_statement node in + Ok (S_switch_statement statement) + | "for_statement" -> + let* statement = wrap dec_for_statement node in + Ok (S_for_statement statement) + | "for_in_statement" -> + let* statement = wrap dec_for_in_statement node in + Ok (S_for_in_statement statement) + | "while_statement" -> + let* statement = wrap dec_while_statement node in + Ok (S_while_statement statement) + | "do_statement" -> + let* statement = wrap dec_do_statement ~comments node in + Ok (S_do_statement statement) + | "try_statement" -> + let* statement = wrap dec_try_statement node in + Ok (S_try_statement statement) + | "with_statement" -> + let* statement = wrap dec_with_statement node in + Ok (S_with_statement statement) + | "break_statement" -> + let* statement = wrap dec_break_statement node in + Ok (S_break_statement statement) + | "continue_statement" -> + let* statement = wrap dec_continue_statement node in + Ok (S_continue_statement statement) + | "return_statement" -> + let* statement = wrap dec_return_statement node in + Ok (S_return_statement statement) + | "throw_statement" -> + let* statement = wrap dec_throw_statement node in + Ok (S_throw_statement statement) + | "empty_statement" -> Ok (S_empty_statement (!get_region node)) + | "labeled_statement" -> + let* statement = wrap dec_labeled_statement node in + Ok (S_labeled_statement statement) + (* "declaration" is a hidden rule *) + | _ -> + let* declaration = dec_declaration ~comments node in + Ok (S_declaration_statement declaration) + +(* Export statement *) + +and dec_export_statement ?(comments = []) node : (export_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Export node + | _ -> + let comments = comments @ prev_comments node in + (* Decorators? *) + let* kwd_export = first_child_named "export" node ~err:Export in + let* after_export = next_sibling kwd_export ~err:Export_clause_or_all in + let* kwd_export = dec_kwd_export ~comments kwd_export in + let* export_kind = + match get_name after_export with + | "*" -> + let* kwd_from = first_child_named "from" node ~err:From in + let* from_clause = dec_from_clause node kwd_from in + Ok (Export_from from_clause) + | "namespace_export" -> + let* kwd_from = first_child_named "from" node ~err:From in + let* namespace_export = dec_namespace_export after_export in + let* from_clause = dec_from_clause node kwd_from in + Ok (Export_as (namespace_export, from_clause)) + | "export_clause" -> + let kwd_from = first_child_named_opt "from" node in + let* export_clause = dec_export_clause after_export in + let* from_clause = make_opt_res (dec_from_clause node) kwd_from in + Ok (Export_clause (export_clause, from_clause)) + | "default" -> dec_export_default after_export node + | "type" -> + let* export_type = dec_export_type after_export node in + Ok (Export_type export_type) + | "=" -> + let* expression = next_sibling after_export ~err:Expression in + let* expression = dec_expression expression in + let* sym_equal = dec_sym_equal after_export in + Ok (Export_equal (sym_equal, expression)) + | "as" -> + let* kwd_namespace = first_child_named "namespace" node ~err:Namespace in + let* kwd_namespace = dec_kwd_namespace kwd_namespace in + let* identifier = first_child_named "identifier" node ~err:Identifier in + Ok (Export_as_namespace (kwd_namespace, dec_identifier identifier)) + | _ -> + let* export_declaration = dec_export_declaration after_export node in + Ok (Export_declaration export_declaration) + in + let* () = check_first_error_child node in + Ok { kwd_export; export_kind } + +and dec_export_type after_export node : (export_type, _) result = + let* export_clause = next_sibling after_export ~err:Export_clause in + let* kwd_type = dec_kwd_type after_export in + let* export_clause = dec_export_clause export_clause in + let kwd_from = first_child_named_opt "from" node in + let* from_clause = make_opt_res (dec_from_clause node) kwd_from in + let* () = check_first_error_child node in + Ok { kwd_type; export_clause; from_clause } + +and dec_export_declaration after_export node : (declaration decorated, _) result = + let decorators = children_named "decorator" node in + let* declaration = dec_declaration after_export in + let* () = check_first_error_child node in + dec_decorated decorators declaration + +and dec_decorated : 'a. ts_forest -> 'a -> ('a decorated, _) result = + fun decorators decorated -> + let* decorators = list_of_children dec_decorator decorators in + Ok { decorators; decorated } + +and dec_export_clause node : (export_clause, _) result = + dec_list_in_braces node dec_export_specifier ~err:Export_clause + +and dec_export_specifier ?(comments = []) node : (export_specifier, _) result = + let comments = comments @ prev_comments node in + let* name_field = child_with_field "name" node ~err:Identifier_or_string in + let* name = dec_module_export_name ~comments name_field in + let alias_field = child_with_field_opt "alias" node in + let* alias = make_opt_res dec_module_export_name alias_field in + let* alias = + match alias with + | None -> Ok None + | Some alias -> + let* kwd_as = first_child_named "as" node ~err:As in + let* kwd_as = dec_kwd_as kwd_as in + Ok (Some (kwd_as, alias)) + in + let* () = check_first_error_child node in + Ok ({ name; alias } : export_specifier) + +and dec_module_export_name ?(comments = []) node : (module_export_name, _) result = + match get_name node with + | "identifier" -> + let* () = check_first_error_child node in + Ok (Export_ident (dec_identifier ~comments node)) + | "string" -> + let* () = check_first_error_child node in + Ok (Export_string (dec_string ~comments node)) + | _ -> mk_err Identifier_or_string node + +and dec_from_clause node kwd_from : (from_clause, _) result = + let* source_field = child_with_field "source" node ~err:File_path in + let* kwd_from = dec_kwd_from kwd_from in + let* () = check_first_error_child node in + Ok (kwd_from, dec_string source_field) + +and dec_namespace_export ?(comments = []) node : (namespace_export, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Namespace_export node + | _ -> + let comments = comments @ prev_comments node in + let* sym_asterisk = first_child_named "*" node ~err:Asterisk in + let* sym_asterisk = dec_sym_asterisk ~comments sym_asterisk in + let* kwd_as = first_child_named "as" node ~err:As in + let* module_export_name = next_sibling kwd_as ~err:Identifier_or_string in + let* kwd_as = dec_kwd_as kwd_as in + let* namespace_name = dec_module_export_name module_export_name in + let* () = check_first_error_child node in + Ok { sym_asterisk; kwd_as; namespace_name } + +and dec_export_default after_export node : (export_kind, _) result = + let decorators = children_named "decorator" node in + let* kwd_default = dec_kwd_default after_export in + match child_with_field_opt "declaration" node with + | None -> + let* value_field = child_with_field "value" node ~err:Expression in + let* expression = dec_expression value_field in + let contents = kwd_default, expression in + let* decorated = dec_decorated decorators contents in + let* () = check_first_error_child node in + Ok (Export_default_expression decorated) + | Some declaration -> + let* declaration = dec_declaration declaration in + let contents = kwd_default, declaration in + let* decorated = dec_decorated decorators contents in + let* () = check_first_error_child node in + Ok (Export_default_declaration decorated) + +(* Import statement *) + +and dec_import_statement ?(comments = []) node : (import_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Import node + | _ -> + let comments = comments @ prev_comments node in + let* kwd_import = first_child_named "import" node ~err:Import in + let* kwd_import = dec_kwd_import ~comments kwd_import in + let* import_kind = + match first_child_named_opt "type" node with + | Some kwd_type -> + let* kwd_type = dec_kwd_type kwd_type in + Ok (Some (Import_type kwd_type)) + | None -> + (match first_child_named_opt "typeof" node with + | None -> Ok None + | Some kwd_typeof -> + let* kwd_typeof = dec_kwd_typeof kwd_typeof in + Ok (Some (Import_typeof kwd_typeof))) + in + let import_attribute = first_child_named_opt "import_attribute" node in + let* import_attribute = make_opt_res dec_import_attribute import_attribute in + let* (import : import) = + match first_child_named_opt "import_clause" node with + | Some import_clause -> + let* kwd_from = first_child_named "from" node ~err:From in + let* import_clause = dec_import_clause import_clause in + let* from_clause = dec_from_clause node kwd_from in + Ok (Import_clause (import_clause, from_clause)) + | None -> + (match first_child_named_opt "import_require_clause" node with + | Some clause -> + let* require_clause = wrap dec_import_require_clause clause in + Ok (Import_require_clause require_clause) + | None -> + let* source_field = child_with_field "source" node ~err:String in + Ok (Import_source (dec_string source_field))) + in + let* () = check_first_error_child node in + Ok { kwd_import; import_kind; import; import_attribute } + +and dec_import_clause ?(comments = []) node : (import_clause, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Import_clause node + | _ -> + let comments = comments @ prev_comments node in + let* fst_child = child_ranked 0 node ~err:Named_imports_or_all_or_id in + (match get_name fst_child with + | "namespace_import" -> + let* namespace_import = wrap dec_namespace_import ~comments fst_child in + let* () = check_first_error_child node in + Ok (Import_namespace namespace_import : import_clause) + | "named_imports" -> + let* named_imports = dec_named_imports ~comments fst_child in + let* () = check_first_error_child node in + Ok (Import_named named_imports : import_clause) + | "identifier" -> + let ident = dec_identifier ~comments fst_child in + let* from = + match next_sibling_opt fst_child with + | None -> Ok None + | Some comma -> + let* next = next_sibling comma ~err:Named_imports_or_all in + let* next = dec_namespace_or_named_imports next in + Ok (Some next) + in + let* () = check_first_error_child node in + Ok (Import_ident (ident, from)) + | _ -> mk_err Namespace_or_named_imports_or_ident fst_child) + +and dec_namespace_or_named_imports node : (namespace_or_named_imports, _) result = + match get_name node with + | "namespace_import" -> + let* namespace_import = wrap dec_namespace_import node in + let* () = check_first_error_child node in + Ok (Import_namespace namespace_import) + | "named_imports" -> + let* named_imports = dec_named_imports node in + let* () = check_first_error_child node in + Ok (Import_named named_imports) + | _ -> mk_err Namespace_or_named_imports node + +and dec_namespace_import ?(comments = []) node : (namespace_import, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Namespace_import node + | _ -> + let comments = comments @ prev_comments node in + let* sym_asterisk = first_child_named "*" node ~err:Asterisk in + let* sym_asterisk = dec_sym_asterisk ~comments sym_asterisk in + let* kwd_as = first_child_named "as" node ~err:As in + let* identifier = next_sibling kwd_as ~err:Identifier in + let* kwd_as = dec_kwd_as kwd_as in + let identifier = dec_identifier identifier in + let* () = check_first_error_child node in + Ok { sym_asterisk; kwd_as; identifier } + +and dec_named_imports ?(comments = []) node : (named_imports, _) result = + dec_list_in_braces ~comments node dec_import_specifier ~err:Named_imports + +and dec_import_specifier ?(comments = []) node : (import_specifier, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Import_specifier node + | _ -> + let comments = comments @ prev_comments node in + let* (import_kind : import_kind option) = + match first_child_named_opt "type" node with + | Some kwd_type -> + let* kwd_type = dec_kwd_type ~comments kwd_type in + Ok (Some (Import_type kwd_type)) + | None -> + (match first_child_named_opt "typeof" node with + | None -> Ok None + | Some kwd_typeof -> + let* kwd_typeof = dec_kwd_typeof ~comments kwd_typeof in + Ok (Some (Import_typeof kwd_typeof))) + in + let snd_child_comments = + match import_kind with + | None -> comments + | Some _ -> [] + in + let* name_field = child_with_field "name" node ~err:Identifier in + let* (import_specifier' : import_specifier') = + match child_with_field_opt "alias" node with + | None -> + Ok (Import_spec_name (dec_identifier ~comments:snd_child_comments name_field)) + | Some alias_field -> + let* kwd_as = first_child_named "as" node ~err:As in + let* name = dec_module_export_name ~comments:snd_child_comments name_field in + let* kwd_as = dec_kwd_as kwd_as in + let alias = dec_identifier alias_field in + Ok (Import_spec_alias { name; kwd_as; alias }) + in + let* () = check_first_error_child node in + Ok (import_kind, import_specifier') + +and dec_import_require_clause ?(comments = []) node : (import_require_clause, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Import_require_clause node + | _ -> + let comments = comments @ prev_comments node in + let* identifier = child_ranked 0 node ~err:Identifier in + let ident = dec_identifier ~comments identifier in + let* sym_equal = first_child_named "=" node ~err:Equal in + let* sym_equal = dec_sym_equal sym_equal in + let* kwd_require = first_child_named "require" node ~err:Require in + let* kwd_require = dec_kwd_require kwd_require in + let* sym_lparen = first_child_named "(" node ~err:Left_parenthesis in + let* sym_lparen = dec_sym_lparen sym_lparen in + let* source_field = child_with_field "source" node ~err:String in + let source = dec_string source_field in + let* sym_rparen = first_child_named ")" node ~err:Right_parenthesis in + let* sym_rparen = dec_sym_rparen sym_rparen in + let* () = check_first_error_child node in + Ok { ident; sym_equal; kwd_require; sym_lparen; source; sym_rparen } + +and dec_import_attribute node : (import_attribute, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Import_attribute node + | _ -> + let* kind_node = child_ranked 0 node ~err:Import_attribute in + let* object_node = child_ranked 1 node ~err:Object_expression in + let* expression = dec_object_expr object_node in + (match get_name kind_node with + | "with" -> + let* kwd_with = dec_kwd_with kind_node in + let* () = check_first_error_child node in + Ok (Import_with (kwd_with, expression)) + | "assert" -> + let* kwd_assert = dec_kwd_assert kind_node in + let* () = check_first_error_child node in + Ok (Import_assert (kwd_assert, expression)) + | _ -> mk_err Import_attribute node) + +(* Expression statements + + {@js[ + expression_statement: $ => seq($._expressions, $._semicolon), + _expressions: $ => choice($.expression, $.sequence_expression), + sequence_expression: $ => prec.right(commaSep1($.expression)) + ]} + + See [dec_expression]. *) + +and dec_expression_statement ?(comments = []) node : (expression_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Expression node + | _ -> + let comments = comments @ prev_comments node in + let* child = named_child_ranked 0 node ~err:Expression in + let* () = check_first_error_child node in + dec_expressions ~comments child + +and dec_expressions ?(comments = []) (node : ts_tree) : (expressions, _) result = + match get_name node with + | "sequence_expression" -> dec_sequence_expression ~comments node + | _ -> + let* expression = dec_expression ~comments node in + let region = !get_region node in + let* () = check_first_error_child node in + Ok (Wrap.make Nonempty_list.[ expression ] region) + +(* Statement blocks *) + +and dec_statement_block ?(comments = []) node : (statement_block, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Block node + | _ -> + let comments = comments @ prev_comments node in + let* opening = first_child_named "{" node ~err:Left_brace in + let* opening = dec_sym_lbrace ~comments opening in + let* closing = first_child_named "}" node ~err:Right_brace in + let* closing = dec_sym_rbrace closing in + let clauses = collect_named_children node in + let* contents = wrap_ne_list_opt_of_children dec_statement clauses in + let region = !get_region node in + let braces = Wrap.make { opening; contents; closing } region in + let* () = check_first_error_child node in + Ok (Braces braces) + +(* If statement *) + +and dec_if_statement ?(comments = []) node : (if_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err If node + | _ -> + let* kwd_if = first_child_named "if" node ~err:If in + let* kwd_if = dec_kwd_if ~comments kwd_if in + let* condition_field = + child_with_field "condition" node ~err:Parenthesized_expression + in + let* condition = dec_parenthesized_expression condition_field in + let* consequence_field = child_with_field "consequence" node ~err:Statement in + let* consequence = dec_statement consequence_field in + let alternative_field = child_with_field_opt "alternative" node in + let* alternative = make_opt_res dec_else_clause alternative_field in + let* () = check_first_error_child node in + Ok { kwd_if; condition; consequence; alternative } + +and dec_else_clause ?(comments = []) node : (kwd_else * statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Else node + | _ -> + let comments = comments @ prev_comments node in + let* kwd_else = first_child_named "else" node ~err:Else in + let* statement = next_sibling kwd_else ~err:Statement in + let* statement = dec_statement statement in + let* kwd_else = dec_kwd_else ~comments kwd_else in + let* () = check_first_error_child node in + Ok (kwd_else, statement) + +(* Switch statement *) + +and dec_switch_statement ?(comments = []) node : (switch_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Switch node + | _ -> + let* kwd_switch = first_child_named "switch" node ~err:Switch in + let* kwd_switch = dec_kwd_switch ~comments kwd_switch in + let* value_field = child_with_field "value" node ~err:Parenthesized_expression in + let* value = dec_parenthesized_expression value_field in + let* body_field = child_with_field "body" node ~err:Switch_body in + let* body = dec_switch_body body_field in + let* () = check_first_error_child node in + Ok { kwd_switch; value; body } + +and dec_switch_body node : (switch_body, _) result = + dec_list_in_braces node dec_switch_entry ~err:Switch_body + +and dec_switch_entry ?(comments = []) node : (switch_entry, _) result = + match get_name node with + | "switch_case" -> + let* switch_case = wrap dec_switch_case ~comments node in + let* () = check_first_error_child node in + Ok (Switch_case switch_case) + | "switch_default" -> + let* default = wrap dec_switch_default ~comments node in + let* () = check_first_error_child node in + Ok (Switch_default default) + | _ -> mk_err Switch_body node + +and dec_switch_case ?(comments = []) node : (switch_case, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Case node + | _ -> + let comments = comments @ prev_comments node in + let* kwd_case = first_child_named "case" node ~err:Case in + let* kwd_case = dec_kwd_case ~comments kwd_case in + let* value_field = child_with_field "value" node ~err:Expression in + let* value = dec_expressions value_field in + let children = collect_children node in + let stmt_children = skip_until_colon children in + let* body = wrap_ne_list_opt_of_children dec_statement stmt_children in + let* () = check_first_error_child node in + Ok { kwd_case; value; body } + +and dec_switch_default ?(comments = []) node : (switch_default, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Default node + | _ -> + let comments = comments @ prev_comments node in + let* kwd_default = first_child_named "default" node ~err:Default in + let* kwd_default = dec_kwd_default ~comments kwd_default in + let statements = collect_named_children node in + let* statements = wrap_ne_list_opt_of_children dec_statement statements in + let* () = check_first_error_child node in + Ok { kwd_default; statements } + +(* For statement *) + +and dec_for_statement ?(comments = []) node : (for_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err For node + | _ -> + let* kwd_for = first_child_named "for" node ~err:For in + let* kwd_for = dec_kwd_for ~comments kwd_for in + let* sym_lparen = first_child_named "(" node ~err:Left_parenthesis in + let* sym_lparen = dec_sym_lparen sym_lparen in + let* initializer_field = + child_with_field "initializer" node ~err:Initial_assignment + in + let* initializer_ = dec_for_initializer initializer_field in + let* condition_field = + child_with_field "condition" node ~err:Expression_or_semicolon + in + let* condition = dec_for_condition condition_field in + let increment_field = child_with_field_opt "increment" node in + let* increment = make_opt_res dec_expressions increment_field in + let* sym_rparen = first_child_named ")" node ~err:Right_parenthesis in + let* sym_rparen = dec_sym_rparen sym_rparen in + let* body_field = child_with_field "body" node ~err:Statement in + let* body = dec_statement body_field in + let* () = check_first_error_child node in + Ok { kwd_for; sym_lparen; initializer_; condition; increment; sym_rparen; body } + +and dec_for_initializer node : (for_initializer, _) result = + match get_name node with + | "lexical_declaration" -> + let* declaration = wrap dec_lexical_declaration node in + let* () = check_first_error_child node in + Ok (For_lexical_declaration declaration) + | "variable_declaration" -> + let* declaration = wrap dec_variable_declaration node in + let* () = check_first_error_child node in + Ok (For_variable_declaration declaration) + | "expression_statement" -> + let* expression = dec_expression_statement node in + let* () = check_first_error_child node in + Ok (For_expression_statement expression) + | "empty_statement" -> + let* () = check_first_error_child node in + Ok (For_empty_statement (!get_region node)) + | _ -> mk_err Initial_assignment node + +and dec_for_condition node : (for_condition, _) result = + match get_name node with + | "expression_statement" -> + let* expression = dec_expression_statement node in + let* () = check_first_error_child node in + Ok (For_condition_expression expression) + | "empty_statement" -> + let* () = check_first_error_child node in + Ok (For_condition_empty (!get_region node)) + | _ -> mk_err Expression_or_semicolon node + +(* For-in statement *) + +and dec_for_in_statement ?(comments = []) node : (for_in_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err For_or_await node + | _ -> + let* kwd_for = first_child_named "for" node ~err:For in + let* kwd_for = dec_kwd_for ~comments kwd_for in + let kwd_await = first_child_named_opt "await" node in + let* kwd_await = make_opt_res dec_kwd_await kwd_await in + let* sym_lparen = first_child_named "(" node ~err:Left_parenthesis in + let* sym_lparen = dec_sym_lparen sym_lparen in + let kind_field = child_with_field_opt "kind" node in + let* left_field = child_with_field "left" node ~err:Expression in + let* sym_rparen = first_child_named ")" node ~err:Right_parenthesis in + let* sym_rparen = dec_sym_rparen sym_rparen in + let* body_field = child_with_field "body" node ~err:Statement in + let* body = dec_statement body_field in + let* operator_field = child_with_field "operator" node ~err:In_or_of in + let* operator = dec_for_operator operator_field in + let* right_field = child_with_field "right" node ~err:Expression in + let* collection = dec_expressions right_field in + let* (range : for_range) = + match kind_field with + | None -> + (match get_name left_field with + | "parenthesized_expression" -> + let* expression = dec_parenthesized_expression left_field in + Ok (For_in_parenthesized expression) + | _ -> + let* expression = dec_lhs_expression left_field in + Ok (For_in_expression expression)) + | Some kind_field -> + let* variable = + match get_name left_field with + | "ERROR" | "MISSING" | "NULL" -> mk_err Pattern left_field + | "identifier" -> Ok (For_in_ident (dec_identifier left_field)) + | _ -> + let* pattern = dec_destructuring_pattern left_field in + Ok (For_in_pattern pattern) + in + (match get_name kind_field with + | "var" -> + let* kwd_var = dec_kwd_var kind_field in + let value_field = child_with_field_opt "value" node in + let* default = make_opt_res dec_expression value_field in + Ok (For_in_var { kwd_var; variable; default }) + | "let" -> + let* kwd_let = dec_kwd_let kind_field in + Ok (For_in_let (kwd_let, variable)) + | "const" -> + let* kwd_const = dec_kwd_const kind_field in + Ok (For_in_const (kwd_const, variable)) + | _ -> mk_err Let_or_const_or_var kind_field) + in + let for_header : for_header = { range; operator; collection } in + let* () = check_first_error_child node in + Ok { kwd_for; kwd_await; sym_lparen; for_header; sym_rparen; body } + +and dec_for_operator node : (for_operator, _) result = + match get_name node with + | "in" -> + let* kwd_in = dec_kwd_in node in + let* () = check_first_error_child node in + Ok (In kwd_in) + | "of" -> + let* kwd_of = dec_kwd_of node in + let* () = check_first_error_child node in + Ok (Of kwd_of) + | _ -> mk_err In_or_of node + +(* While statement *) + +and dec_while_statement ?(comments = []) node : (while_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err While node + | _ -> + let* kwd_while = first_child_named "while" node ~err:While in + let* kwd_while = dec_kwd_while ~comments kwd_while in + let* condition_field = + child_with_field "condition" node ~err:Parenthesized_expression + in + let* condition = dec_parenthesized_expression condition_field in + let* body_field = child_with_field "body" node ~err:Statement in + let* body = dec_statement body_field in + let* () = check_first_error_child node in + Ok { kwd_while; condition; body } + +(* Do statement *) + +and dec_do_statement ?(comments = []) node : (do_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Do node + | _ -> + let* kwd_do = first_child_named "do" node ~err:Do in + let* kwd_do = dec_kwd_do ~comments kwd_do in + let* body_field = child_with_field "body" node ~err:Statement in + let* body = dec_statement body_field in + let* kwd_while = first_child_named "while" node ~err:While in + let* kwd_while = dec_kwd_while kwd_while in + let* condition_field = + child_with_field "condition" node ~err:Parenthesized_expression + in + let* condition = dec_parenthesized_expression condition_field in + let* () = check_first_error_child node in + Ok { kwd_do; body; kwd_while; condition } + +(* Try statement *) + +and dec_try_statement ?(comments = []) node : (try_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Try node + | _ -> + let* kwd_try = first_child_named "try" node ~err:Try in + let* kwd_try = dec_kwd_try ~comments kwd_try in + let* body_field = child_with_field "body" node ~err:Block in + let* body = dec_statement_block body_field in + let handler_field = child_with_field_opt "handler" node in + let* handler = make_opt_res dec_catch_clause handler_field in + let finalizer_field = child_with_field_opt "finalizer" node in + let* finalizer = make_opt_res dec_finally_clause finalizer_field in + let* () = check_first_error_child node in + Ok { kwd_try; body; handler; finalizer } + +and dec_catch_clause node : (catch_clause, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Catch node + | _ -> + let* kwd_catch = first_child_named "catch" node ~err:Catch in + let* kwd_catch = dec_kwd_catch kwd_catch in + let parameter_field = child_with_field_opt "parameter" node in + let* parameter = make_opt_res (dec_catch_parameter node) parameter_field in + let* body_field = child_with_field "body" node ~err:Block in + let* body = dec_statement_block body_field in + let* () = check_first_error_child node in + Ok { kwd_catch; parameter; body } + +and dec_catch_parameter node param : (catch_parameter, _) result = + let* catch_parameter = dec_catch_parameter_kind param in + let* sym_lparen = first_child_named "(" node ~err:Left_parenthesis in + let* sym_lparen = dec_sym_lparen sym_lparen in + let type_field = child_with_field_opt "type" node in + let* type_opt = make_opt_res dec_type_annotation type_field in + let* sym_rparen = first_child_named ")" node ~err:Right_parenthesis in + let* sym_rparen = dec_sym_rparen sym_rparen in + let* () = check_first_error_child node in + Ok { sym_lparen; catch_parameter; type_opt; sym_rparen } + +and dec_catch_parameter_kind node : (catch_parameter_kind, _) result = + match get_name node with + | "identifier" -> Ok (Catch_identifier (dec_identifier node)) + | "object_pattern" -> + let* pattern = dec_object_pattern node in + let* () = check_first_error_child node in + Ok (Catch_object_pattern pattern) + | "array_pattern" -> + let* pattern = dec_array_pattern node in + let* () = check_first_error_child node in + Ok (Catch_array_pattern pattern) + | _ -> mk_err Pattern node + +and dec_finally_clause node : (finally_clause, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Finally node + | _ -> + let* kwd_finally = first_child_named "finally" node ~err:Finally in + let* kwd_finally = dec_kwd_finally kwd_finally in + let* body_field = child_with_field "body" node ~err:Block in + let* finalizer_block = dec_statement_block body_field in + let* () = check_first_error_child node in + Ok (kwd_finally, finalizer_block) + +(* Type annotation *) + +and dec_type_annotation node : (type_annotation, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Type_annotation node + | _ -> + let* sym_colon = first_child_named ":" node ~err:Colon in + let* sym_colon = dec_sym_colon sym_colon in + let* type_child = named_child_ranked 0 node ~err:Type_expression in + let* type_expr = dec_type type_child in + let* () = check_first_error_child node in + Ok (sym_colon, type_expr) + +(* With statement *) + +and dec_with_statement ?(comments = []) node : (with_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err With node + | _ -> + let* kwd_with = first_child_named "with" node ~err:With in + let* kwd_with = dec_kwd_with ~comments kwd_with in + let* object_field = child_with_field "object" node ~err:Parenthesized_expression in + let* object_expr = dec_parenthesized_expression object_field in + let* body_field = child_with_field "body" node ~err:Statement in + let* body = dec_statement body_field in + let* () = check_first_error_child node in + Ok { kwd_with; object_expr; body } + +(* Break statement *) + +and dec_break_statement ?(comments = []) node : (break_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Break node + | _ -> + let* kwd_break = first_child_named "break" node ~err:Break in + let* kwd_break = dec_kwd_break ~comments kwd_break in + let label_field = child_with_field_opt "label" node in + let stmt_id = make_opt dec_identifier label_field in + let* () = check_first_error_child node in + Ok { kwd_break; stmt_id } + +(* Continue statement *) + +and dec_continue_statement ?(comments = []) node : (continue_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Continue node + | _ -> + let* kwd_continue = first_child_named "continue" node ~err:Continue in + let* kwd_continue = dec_kwd_continue ~comments kwd_continue in + let label_field = child_with_field_opt "label" node in + let stmt_id = make_opt dec_identifier label_field in + let* () = check_first_error_child node in + Ok { kwd_continue; stmt_id } + +(* Return statement + + NOTE: The Javascript grammar states: + + {@js[ + return_statement: $ => + seq('return', optional($._expressions), $._semicolon), + + _semicolon: $ => choice($._automatic_semicolon, ';') + ]} + + but the child of rank 1 is sometimes missing, as if + "_automatic_semicolon" could derive the empty word. Other rules use + "optional(_automatic_semicolon)", which adds to the mystery. *) + +and dec_return_statement ?(comments = []) node : (return_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Return node + | _ -> + let* kwd_return = first_child_named "return" node ~err:Return in + let* kwd_return = dec_kwd_return ~comments kwd_return in + (match child_ranked_opt 1 node with + | None -> + let* () = check_first_error_child node in + Ok { kwd_return; expressions = None } + | Some snd_child -> + (match get_name snd_child with + | ";" -> + let* () = check_first_error_child node in + Ok { kwd_return; expressions = None } + | _ -> + let* expressions = dec_expressions snd_child in + let* () = check_first_error_child node in + Ok { kwd_return; expressions = Some expressions })) + +(* Throw statement *) + +and dec_throw_statement ?(comments = []) node : (throw_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Throw node + | _ -> + let* kwd_throw = first_child_named "throw" node ~err:Throw in + let* kwd_throw = dec_kwd_throw ~comments kwd_throw in + let* expr = child_ranked 1 node ~err:Expression in + let* expressions = dec_expressions expr in + let* () = check_first_error_child node in + Ok { kwd_throw; expressions } + +(* Labeled statement *) + +and dec_labeled_statement ?(comments = []) node : (labeled_statement, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Label node + | _ -> + let* label_field = child_with_field "label" node ~err:Label in + let label = dec_identifier ~comments label_field in + let* sym_colon = first_child_named ":" node ~err:Colon in + let* sym_colon = dec_sym_colon sym_colon in + let* body_field = child_with_field "body" node ~err:Statement in + let* body = dec_statement body_field in + let* () = check_first_error_child node in + Ok { label; sym_colon; body } + +(* DECLARATION + + The JavaScript tree-sitter grammar has the non-terminal + "declaration" be a supertype, that is, a hidden rule. *) + +and dec_declaration ?(comments = []) node : (declaration, _) result = + let comments = comments @ prev_comments node in + match get_name node with + | "function_declaration" -> + let* fun_decl = wrap dec_function_declaration ~comments node in + Ok (D_function_declaration fun_decl) + | "generator_function_declaration" -> + let* generator = wrap dec_generator_function_declaration ~comments node in + Ok (D_generator_function_declaration generator) + | "class_declaration" -> + let* declaration = wrap dec_class_declaration node in + Ok (D_class_declaration declaration) + | "lexical_declaration" -> + let* declaration = wrap dec_lexical_declaration ~comments node in + Ok (D_lexical_declaration declaration) + | "variable_declaration" -> + let* declaration = wrap dec_variable_declaration node in + Ok (D_variable_declaration declaration) + | "function_signature" -> + let* declaration = wrap dec_function_signature node in + Ok (D_function_signature declaration) + | "abstract_class_declaration" -> + let* declaration = wrap dec_abstract_class_declaration node in + Ok (D_abstract_class_declaration declaration) + | "module" -> + let* declaration = wrap dec_module_declaration node in + Ok (D_module declaration) + | "internal_module" -> + let* declaration = wrap dec_internal_module ~comments node in + Ok (D_internal_module declaration) + | "type_alias_declaration" -> + let* declaration = wrap dec_type_alias_declaration ~comments node in + Ok (D_type_alias_declaration declaration) + | "enum_declaration" -> + let* declaration = wrap dec_enum_declaration node in + Ok (D_enum_declaration declaration) + | "interface_declaration" -> + let* declaration = wrap dec_interface_declaration ~comments node in + Ok (D_interface_declaration declaration) + | "import_alias" -> + let* declaration = wrap dec_import_alias node in + Ok (D_import_alias declaration) + | "ambient_declaration" -> + let* declaration = wrap dec_ambient_declaration node in + Ok (D_ambient_declaration declaration) + | _ -> mk_err Declaration node + +(* Function declaration (see [dec_function_signature]) *) + +and dec_function_declaration ?(comments = []) node : (function_declaration, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Function_declaration node + | _ -> + let* fun_sig = dec_function_signature ~comments node in + let* body_field = child_with_field "body" node ~err:Block in + let* body = dec_statement_block body_field in + let* () = check_first_error_child node in + Ok { fun_sig; body } + +(* Accessibility modifier *) + +and dec_accessibility_modifier node : (accessibility_modifier, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Public_private_protected node + | _ -> + let* child = child_ranked 0 node ~err:Public_private_protected in + (match get_name child with + | "public" -> + let* kwd_public = dec_kwd_public child in + let* () = check_first_error_child node in + Ok (Public kwd_public) + | "private" -> + let* kwd_private = dec_kwd_private child in + let* () = check_first_error_child node in + Ok (Private kwd_private) + | "protected" -> + let* kwd_protected = dec_kwd_protected child in + let* () = check_first_error_child node in + Ok (Protected kwd_protected) + | _ -> mk_err Public_private_protected node) + +(* Override modifier *) + +and dec_override_modifier node : (kwd_override, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Override node + | _ -> + let* child = first_child_named "override" node ~err:Override in + let* kwd_override = dec_kwd_override child in + (* TODO: Test. See [Print_cst] *) + let* () = check_first_error_child node in + Ok kwd_override + +(* Return type annotation *) + +and dec_call_return_type node : (call_return_type, _) result = + match get_name node with + | "type_annotation" -> + let* annotation = dec_type_annotation node in + let* () = check_first_error_child node in + Ok (Type_annotation annotation : call_return_type) + | "asserts_annotation" -> + let* annotation = dec_asserts_annotation node in + let* () = check_first_error_child node in + Ok (Asserts_annotation annotation) + | "type_predicate_annotation" -> + let* annotation = wrap dec_type_predicate_annotation node in + let* () = check_first_error_child node in + Ok (Type_predicate_annotation annotation) + | _ -> mk_err Type_expression node + +(* Asserts annotation *) + +and dec_asserts_annotation node : (asserts_annotation, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Asserts_annotation node + | _ -> + let* asserts = first_child_named "asserts" node ~err:Asserts in + let* () = check_first_error_child node in + dec_asserts asserts + +and dec_asserts node : (asserts_annotation, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Asserts node + | _ -> + let* kwd_asserts = first_child_named "asserts" node ~err:Asserted in + let* kwd_asserts = dec_kwd_asserts kwd_asserts in + let* child = child_ranked 1 node ~err:Asserted in + (match get_name child with + | "type_predicate" -> + let* predicate = wrap dec_type_predicate child in + let* () = check_first_error_child node in + Ok (Assert_predicate (kwd_asserts, predicate)) + | "identifier" -> + let* () = check_first_error_child node in + Ok (Assert_type (kwd_asserts, dec_identifier child)) + | "this" -> + let* kwd_this = dec_kwd_this child in + let* () = check_first_error_child node in + Ok (Assert_this (kwd_asserts, kwd_this)) + | _ -> mk_err Asserted child) + +(* Type predicate annotation *) + +and dec_type_predicate_annotation ?comments node : (type_predicate, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Type_predicate node + | _ -> + let* predicate = child_ranked 1 node ~err:Type_predicate in + let* () = check_first_error_child node in + dec_type_predicate ?comments predicate + +(* Type predicate *) + +and dec_type_predicate ?(comments = []) node : (type_predicate, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Type_predicate node + | _ -> + let* name_field = child_with_field "name" node ~err:Identifier_or_type in + let* name = dec_type_predicate_name ~comments name_field in + let* kwd_is = first_child_named "is" node ~err:Is in + let* kwd_is = dec_kwd_is kwd_is in + let* type_field = child_with_field "type" node ~err:Type_expression in + let* type_expr = dec_type type_field in + let* () = check_first_error_child node in + Ok { name; kwd_is; type_expr } + +and dec_type_predicate_name ?(comments = []) node : (type_predicate_name, _) result = + match get_name node with + | "identifier" -> + let ident = dec_identifier ~comments node in + let* () = check_first_error_child node in + Ok (Type_predicate_identifier ident) + | "this" -> + let* kwd_this = dec_kwd_this ~comments node in + let* () = check_first_error_child node in + Ok (Type_predicate_this kwd_this) + | _ -> + let* type_expr = dec_predefined_type ~comments node in + let* () = check_first_error_child node in + Ok (Type_predicate_type type_expr) + +(* Predefined type *) + +and dec_predefined_type ?(comments = []) node : (predefined_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Predefined_type node + | _ -> + let comments = comments @ prev_comments node in + (match collect_children node with + | [] -> mk_err Predefined_type node + | child :: _ -> + (* The tree-sitter parser for TypeScript has a bug: a child node + "unique symbol" occurs repeated, for some mysterious + reason. This case of the pattern matching is a hack to work + around the issue. For reference, here is the production: + + predefined_type: _ => choice( + ... + alias(seq('unique', 'symbol'), 'unique symbol') + ...) + *) + (match get_name child with + | "any" -> + let* kwd_any = dec_kwd_any ~comments child in + let* () = check_first_error_child node in + Ok (T_any kwd_any) + | "number" -> + let* kwd_number = dec_kwd_number ~comments child in + let* () = check_first_error_child node in + Ok (T_number kwd_number) + | "boolean" -> + let* kwd_boolean = dec_kwd_boolean ~comments child in + let* () = check_first_error_child node in + Ok (T_boolean kwd_boolean) + | "string" -> + let* kwd_string = dec_kwd_string ~comments child in + let* () = check_first_error_child node in + Ok (T_string kwd_string) + | "symbol" -> + let* kwd_symbol = dec_kwd_symbol ~comments child in + let* () = check_first_error_child node in + Ok (T_symbol kwd_symbol) + | "unique symbol" -> + let* kwd_unique_symbol = dec_kwd_unique_symbol ~comments child in + let* () = check_first_error_child node in + Ok (T_unique_symbol kwd_unique_symbol) + | "void" -> + let* kwd_void = dec_kwd_void ~comments child in + let* () = check_first_error_child node in + Ok (T_void kwd_void) + | "unknown" -> + let* kwd_unknown = dec_kwd_unknown ~comments child in + let* () = check_first_error_child node in + Ok (T_unknown kwd_unknown) + | "never" -> + let* kwd_never = dec_kwd_never ~comments child in + let* () = check_first_error_child node in + Ok (T_never kwd_never) + | "object" -> + let* kwd_object = dec_kwd_object ~comments child in + let* () = check_first_error_child node in + Ok (T_object kwd_object) + | _ -> mk_err Predefined_type child)) + +(* Decorator *) + +and dec_decorator ?(comments = []) node : (decorator, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Decorator node + | _ -> + let* child = named_child_ranked 0 node ~err:Decorator in + (match get_name child with + | "identifier" -> Ok (Decorator_identifier (dec_identifier ~comments child)) + | "member_expression" -> + let* member_expression = wrap dec_decorator_member_expression ~comments child in + let* () = check_first_error_child node in + Ok (Decorator_member_expression member_expression) + | "call_expression" -> + let* call_expression = wrap dec_decorator_call_expression ~comments child in + let* () = check_first_error_child node in + Ok (Decorator_call_expression call_expression) + | "parenthesized_expression" -> + let* expression = dec_decorator_parenthesized_expression ~comments child in + let* () = check_first_error_child node in + Ok (Decorator_parenthesized_expression expression) + | _ -> mk_err Decorator child) + +and dec_decorator_member_expression ?(comments = []) node + : (decorator_member_expression, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Decorator_member node + | _ -> + let* object_field = child_with_field "object" node ~err:Identifier_or_member in + let* object_expr = dec_object_member_expression ~comments object_field in + let* sym_dot = first_child_named "." node ~err:Dot in + let* sym_dot = dec_sym_dot sym_dot in + let* property_field = child_with_field "property" node ~err:Property_identifier in + let property = dec_identifier property_field in + let* () = check_first_error_child node in + Ok { object_expr; sym_dot; property } + +and dec_object_member_expression ?(comments = []) node + : (object_member_expression, _) result + = + match get_name node with + | "identifier" -> + let* () = check_first_error_child node in + Ok (Object_name (dec_identifier ~comments node)) + | _ -> + let* member_expression = wrap dec_decorator_member_expression ~comments node in + let* () = check_first_error_child node in + Ok (Qualified_member_expression member_expression : object_member_expression) + +and dec_decorator_call_expression ?(comments = []) node + : (decorator_call_expression, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Decorator_call node + | _ -> + let* function_field = child_with_field "function" node ~err:Identifier_or_member in + let* function_ = dec_function_or_property ~comments function_field in + let type_arguments_field = child_with_field_opt "type_arguments" node in + let* type_arguments = make_opt_res dec_type_arguments type_arguments_field in + let* arguments_field = child_with_field "arguments" node ~err:Arguments in + let* arguments = dec_arguments arguments_field in + let* () = check_first_error_child node in + Ok { function_; type_arguments; arguments } + +and dec_function_or_property ?(comments = []) node : (function_or_property, _) result = + match get_name node with + | "identifier" -> + let* () = check_first_error_child node in + Ok (Function_name (dec_identifier ~comments node)) + | "member_expression" -> + let* member_expression = wrap dec_decorator_member_expression ~comments node in + let* () = check_first_error_child node in + Ok (Qualified_member_expression member_expression) + | _ -> mk_err Decorator_call node + +and dec_decorator_parenthesized_expression ?comments node + : (decorator_parenthesized_expression parens, _) result + = + dec_parens ?comments node decode_decorator_in_parens ~err:Parenthesized_decorator + +and decode_decorator_in_parens node = + match get_name node with + | "identifier" -> Ok (Parenthesized_ident (dec_identifier node)) + | "member_expression" -> + let* member_expression = wrap dec_decorator_member_expression node in + let* () = check_first_error_child node in + Ok (Parenthesized_member member_expression) + | _ -> + let* call_expression = wrap dec_decorator_call_expression node in + let* () = check_first_error_child node in + Ok (Parenthesized_call call_expression) + +(* Type arguments *) + +and dec_type_arguments ?comments node : (type_arguments, _) result = + dec_ne_list_in_chevrons ?comments node dec_type ~err:Type_arguments + +(* Function arguments *) + +and dec_arguments ?comments node : (arguments, _) result = + dec_list_in_parens ?comments node dec_argument ~err:Arguments + +and dec_argument ?comments node : (argument, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Argument node + | "spread_element" -> + let* spread = wrap dec_spread_element node in + let* () = check_first_error_child node in + Ok (Spread_element spread) + | _ -> + let* expression = dec_expression ?comments node in + let* () = check_first_error_child node in + Ok (Expression expression : argument) + +and dec_spread_element ?(comments = []) node : (spread_element, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Spread node + | _ -> + let* sym_ellipsis = first_child_named "..." node ~err:Ellipsis in + let* sym_ellipsis = dec_sym_ellipsis ~comments sym_ellipsis in + let* expression = named_child_ranked 0 node ~err:Expression in + let* expression = dec_expression expression in + let* () = check_first_error_child node in + Ok (sym_ellipsis, expression) + +(* Generator function declaration (see function declaration) *) + +and dec_generator_function_declaration ?(comments = []) node + : (generator_function_declaration, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Generator_function node + | _ -> + let* fun_decl = dec_function_declaration ~comments node in + let* sym_asterisk = first_child_named "*" node ~err:Asterisk in + let* sym_asterisk = dec_sym_asterisk sym_asterisk in + let* () = check_first_error_child node in + Ok (sym_asterisk, fun_decl) + +(* Class declaration (see [dec_class]) *) + +and dec_class_declaration ?(comments = []) node : (class_declaration, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Class_declaration node + | _ -> + let comments = comments @ prev_comments node in + let decorators = children_named "decorator" node in + let* decorators = list_of_children dec_decorator decorators in + let* kwd_class = first_child_named "class" node ~err:Class in + let* kwd_class = dec_kwd_class ~comments kwd_class in + let* name_field = child_with_field "name" node ~err:Class_name in + let name = dec_identifier name_field in + let type_parameters_field = child_with_field_opt "type_parameters" node in + let* type_parameters = make_opt_res dec_type_parameters type_parameters_field in + let heritage_child = first_child_named_opt "class_heritage" node in + let* class_heritage = make_opt_res dec_class_heritage heritage_child in + let* body_field = child_with_field "body" node ~err:Class_body in + let* body = dec_class_body body_field in + let* () = check_first_error_child node in + Ok + ({ decorators; kwd_class; name; type_parameters; class_heritage; body } + : class_declaration) + +and dec_class_heritage node : (class_heritage, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Extends_or_implements node + | _ -> + let* first_child = child_ranked 0 node ~err:Extends_or_implements in + (match get_name first_child with + | "extends_clause" -> + let* extends_clause = dec_extends_clause first_child in + let implements_clause = first_child_named_opt "implements_clause" node in + let* implements_clause = make_opt_res dec_implements_clause implements_clause in + let* () = check_first_error_child node in + Ok (Extends_clause (extends_clause, implements_clause)) + | "implements_clause" -> + let* implements_clause = dec_implements_clause first_child in + let* () = check_first_error_child node in + Ok (Implements_clause implements_clause) + | _ -> mk_err Extends_or_implements first_child) + +and dec_extends_clause node : (extends_clause, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Extends_clause node + | _ -> + let* kwd_extends = first_child_named "extends" node ~err:Extends in + let* kwd_extends = dec_kwd_extends kwd_extends in + let raw_children : ts_forest = + match collect_children node with + | [] | [ _ ] -> [] + | _extends :: clauses -> clauses + in + let not_comma child = String.(get_name child <> ",") in + let raw_clauses : ts_forest = List.filter raw_children ~f:not_comma in + let rec pair_up acc = function + | value :: snd :: nodes -> + if String.equal (get_name snd) "type_arguments" + then pair_up ((value, Some snd) :: acc) nodes + else pair_up ((value, None) :: acc) (snd :: nodes) + | [ value ] -> List.rev ((value, None) :: acc) + | [] -> List.rev acc + in + let pairs : (ts_tree * ts_tree option) list = pair_up [] raw_clauses in + let mk_clause (value, type_arguments_opt) : (extends_clause_single wrap, _) result = + let region = + match type_arguments_opt with + | None -> !get_region value + | Some type_args -> Region.cover (!get_region value) (!get_region type_args) + in + let* value = dec_expression value in + let* type_arguments = + match type_arguments_opt with + | None -> Ok None + | Some type_arguments -> + let* args = dec_type_arguments type_arguments in + Ok (Some args) + in + Ok (Wrap.make { value; type_arguments } region) + in + let* extends_clauses = Result.all @@ List.map ~f:mk_clause pairs in + let* extends_clauses = + match extends_clauses with + | [] -> mk_err Extends_clause node + | clause :: clauses -> Ok Nonempty_list.(clause :: clauses) + in + let* () = check_first_error_child node in + Ok (kwd_extends, extends_clauses) + +and dec_implements_clause node : (implements_clause, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Implements_clause node + | _ -> + let* kwd_implements = first_child_named "implements" node ~err:Implements in + let* kwd_implements = dec_kwd_implements kwd_implements in + let raw_clauses = collect_named_children node in + let error = mk_err Implements_clause node in + let* type_exprs = ne_list_of_children dec_type error raw_clauses in + let* () = check_first_error_child node in + Ok (kwd_implements, type_exprs) + +and dec_class_body ?(comments = []) node : (class_body, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Class_body node + | _ -> + let comments = comments @ prev_comments node in + let* opening = first_child_named "{" node ~err:Left_brace in + let* opening = dec_sym_lbrace ~comments opening in + let* closing = first_child_named "}" node ~err:Right_brace in + let* closing = dec_sym_rbrace closing in + let named_children = collect_named_children node in + let pair (decorators, acc) child = + match get_name child with + | "decorator" -> child :: decorators, acc + | _ -> [], (List.rev decorators, child) :: acc + in + let _, pairs = List.fold_left ~f:pair ~init:([], []) named_children in + let contents = List.map ~f:dec_class_member @@ List.rev pairs in + let* contents = Result.all contents in + let region = !get_region node in + let* () = check_first_error_child node in + Ok (Braces (Wrap.make { opening; contents; closing } region)) + +and dec_class_member ?(comments = []) (decorators, node) : (class_member, _) result = + match get_name node with + | "method_definition" -> + let* decorators = list_of_children dec_decorator decorators in + let* definition = wrap dec_method_definition ~comments node in + let* () = check_first_error_child node in + (* Not ideal *) + Ok (Method_definition (decorators, definition)) + | "method_signature" -> + let* signature = wrap dec_method_signature node in + let* () = check_first_error_child node in + Ok (Method_signature signature : class_member) + | "class_static_block" -> + let* static_block = dec_class_static_block node in + let* () = check_first_error_child node in + Ok (Call_static_block static_block) + | "abstract_method_signature" -> + let* signature = wrap dec_abstract_method_signature node in + let* () = check_first_error_child node in + Ok (Abstract_method_signature signature) + | "index_signature" -> + let* signature = wrap dec_index_signature node in + let* () = check_first_error_child node in + Ok (Index_signature signature : class_member) + | "public_field_definition" -> + let* definition = wrap dec_public_field_definition node in + let* () = check_first_error_child node in + Ok (Public_field_definition definition) + | _ -> mk_err Class_member node + +(* Method definition *) + +and dec_method_definition ?(comments = []) node : (method_definition, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Method_definition node + | _ -> + let* signature = wrap dec_method_signature ~comments node in + let* body_field = child_with_field "body" node ~err:Block in + let* body = dec_statement_block body_field in + let* () = check_first_error_child node in + Ok { signature; body } + +(* Method signature *) + +and dec_method_signature ?(comments = []) node : (method_signature, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Method_signature node + | _ -> + let accessibility_modifier = first_child_named_opt "accessibility_modifier" node in + let* access = make_opt_res dec_accessibility_modifier accessibility_modifier in + let* scope = dec_method_scope node in + let kwd_async = first_child_named_opt "async" node in + let* kwd_async = make_opt_res dec_kwd_async kwd_async in + let* set_get_all = mk_set_get_all node in + let* name_field = child_with_field "name" node ~err:Property_name in + let* name = dec_property_name ~comments name_field in + let sym_qmark = first_child_named_opt "?" node in + let* optional = make_opt_res dec_sym_qmark sym_qmark in + let* call_sig = dec_call_signature node in + let* () = check_first_error_child node in + Ok { access; scope; kwd_async; set_get_all; name; optional; call_sig } + +(* Method scope *) + +and dec_method_scope node : (method_scope, _) result = + let kwd_static = first_child_named_opt "static" node in + let* kwd_static = make_opt_res dec_kwd_static kwd_static in + let override_modifier = first_child_named_opt "override_modifier" node in + let* kwd_override = make_opt_res dec_override_modifier override_modifier in + let kwd_readonly = first_child_named_opt "readonly" node in + let* kwd_readonly = make_opt_res dec_kwd_readonly kwd_readonly in + let* () = check_first_error_child node in + Ok { kwd_static; kwd_override; kwd_readonly } + +(* Class static block *) + +and dec_class_static_block ?(comments = []) node + : (kwd_static * statement_block, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Static_block node + | _ -> + let* kwd_static = first_child_named "static" node ~err:Static in + let* kwd_static = dec_kwd_static ~comments kwd_static in + let* body_field = child_with_field "body" node ~err:Block in + let* block = dec_statement_block body_field in + let* () = check_first_error_child node in + Ok (kwd_static, block) + +(* Abstract method signature *) + +and dec_abstract_method_signature ?(comments = []) node + : (abstract_method_signature, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Abstract_method_signature node + | _ -> + let accessibility_modifier = first_child_named_opt "accessibility_modifier" node in + let* access = make_opt_res dec_accessibility_modifier accessibility_modifier in + let* kwd_abstract = first_child_named "abstract" node ~err:Abstract in + let* kwd_abstract = dec_kwd_abstract kwd_abstract in + let override_modifier = first_child_named_opt "override_modifier" node in + let* kwd_override = make_opt_res dec_override_modifier override_modifier in + let* set_get_all = mk_set_get_all node in + let* name_field = child_with_field "name" node ~err:Property_name in + (* Not ideal *) + let* name = dec_property_name ~comments name_field in + let sym_qmark = first_child_named_opt "?" node in + let* optional = make_opt_res dec_sym_qmark sym_qmark in + let* call_sig = dec_call_signature node in + let* () = check_first_error_child node in + Ok { access; kwd_abstract; kwd_override; set_get_all; name; optional; call_sig } + +(* Call signature *) + +and dec_call_signature ?(comments = []) node : (call_signature wrap, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Call_signature node + | _ -> + let region = !get_region node in + let type_parameters_field = child_with_field_opt "type_parameters" node in + let* parameters_field = child_with_field "parameters" node ~err:Parameters in + let type_params_comments, params_comments = + match type_parameters_field with + | None -> [], comments + | _ -> comments, [] + in + let* type_parameters = + make_opt_res + (dec_type_parameters ~comments:type_params_comments) + type_parameters_field + in + let* parameters = dec_formal_parameters ~comments:params_comments parameters_field in + let return_type_field = child_with_field_opt "return_type" node in + let* return_type = make_opt_res dec_call_return_type return_type_field in + let call_sig : call_signature = { type_parameters; parameters; return_type } in + let* () = check_first_error_child node in + Ok (Wrap.make call_sig region) + +(* Index signature *) + +and dec_index_signature ?(comments = []) node : (index_signature, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Index_signature node + | _ -> + let kwd_readonly = first_child_named_opt "readonly" node in + let* kwd_readonly = make_opt_res dec_kwd_readonly kwd_readonly in + let sign_field = child_with_field_opt "sign" node in + let* sign = make_opt_res dec_sign sign_field in + let sign = + match kwd_readonly with + | None -> None + | Some kwd -> Some (sign, kwd) + in + let name_field = child_with_field_opt "name" node in + let* type_field = child_with_field "type" node ~err:Type_annotation in + let* annotation = dec_index_annotation type_field in + let* sym_lbracket = first_child_named "[" node ~err:Left_bracket in + (* Not ideal *) + let* opening = dec_sym_lbracket ~comments sym_lbracket in + let* sym_rbracket = first_child_named "]" node ~err:Right_bracket in + let* closing = dec_sym_rbracket sym_rbracket in + let* (range : index_range) = + match name_field with + | Some name_field -> + let name = dec_type_identifier name_field in + let* sym_colon = first_child_named ":" node ~err:Colon in + let* sym_colon = dec_sym_colon sym_colon in + let* index_type_field = child_with_field "index_type" node ~err:Type in + let* index_type = dec_type index_type_field in + Ok (Typed_index_clause { name; sym_colon; index_type }) + | None -> + let* mapped_type_clause = named_child_ranked 0 node ~err:Mapped_type_signature in + let* mapped_type_clause = dec_mapped_type_clause mapped_type_clause in + Ok (Mapped_type_clause mapped_type_clause) + in + let region = !get_region node in + let brackets = { opening; contents = range; closing } in + let range = Brackets (Wrap.make brackets region) in + let* () = check_first_error_child node in + Ok { sign; range; annotation } + +and dec_mapped_type_clause node : (mapped_type_clause, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Mapped_type_signature node + | _ -> + let* name_field = child_with_field "name" node ~err:Type_name in + let name = dec_type_identifier name_field in + let* kwd_in = first_child_named "in" node ~err:In in + let* kwd_in = dec_kwd_in kwd_in in + let* type_field = child_with_field "type" node ~err:Type in + let* type_expr = dec_type type_field in + let alias_field = child_with_field_opt "alias" node in + let* alias = + match alias_field with + | None -> Ok None + | Some alias -> + let* kwd_as = first_child_named "as" node ~err:As in + let* type_expr = dec_type alias in + let* kwd_as = dec_kwd_as kwd_as in + Ok (Some (kwd_as, type_expr)) + in + let* () = check_first_error_child node in + Ok { name; kwd_in; type_expr; alias } + +and dec_omitting_type_annotation node : (symbol * type_expr, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Omitting_type_annotation node + | _ -> + let* sym_kind = first_child_named "-?:" node ~err:Omitting_type_annotation in + let* sym_kind = dec_sym_omitting sym_kind in + let* type_child = named_child_ranked 0 node ~err:Type_expression in + let* type_expr = dec_type type_child in + let* () = check_first_error_child node in + Ok (sym_kind, type_expr) + +and dec_adding_type_annotation node : (symbol * type_expr, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Adding_type_annotation node + | _ -> + let* sym_kind = first_child_named "+?:" node ~err:Adding_type_annotation in + let* sym_kind = dec_sym_adding sym_kind in + let* type_child = named_child_ranked 0 node ~err:Type_expression in + let* type_expr = dec_type type_child in + let* () = check_first_error_child node in + Ok (sym_kind, type_expr) + +and dec_opting_type_annotation node : (symbol * type_expr, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Opting_type_annotation node + | _ -> + let* sym_kind = first_child_named "?:" node ~err:Opting_type_annotation in + let* sym_kind = dec_sym_opting sym_kind in + let* type_child = named_child_ranked 0 node ~err:Type_expression in + let* type_expr = dec_type type_child in + let* () = check_first_error_child node in + Ok (sym_kind, type_expr) + +and dec_index_annotation node : (index_annotation, _) result = + match get_name node with + | "type_annotation" -> + let* annotation = dec_type_annotation node in + let* () = check_first_error_child node in + Ok (Type_annotation annotation) + | "omitting_type_annotation" -> + let* annotation = dec_omitting_type_annotation node in + let* () = check_first_error_child node in + Ok (Omitting_type_annotation annotation) + | "adding_type_annotation" -> + let* annotation = dec_adding_type_annotation node in + let* () = check_first_error_child node in + Ok (Adding_type_annotation annotation) + | "opting_type_annotation" -> + let* annotation = dec_opting_type_annotation node in + let* () = check_first_error_child node in + Ok (Opting_type_annotation annotation) + | _ -> mk_err Type_of_index_signature node + +and dec_sign node : (sign, _) result = + match get_name node with + | "+" -> + let* sym_plus = dec_sym_plus node in + let* () = check_first_error_child node in + Ok (Plus sym_plus) + | "-" -> + let* sym_minus = dec_sym_minus node in + let* () = check_first_error_child node in + Ok (Minus sym_minus) + | _ -> mk_err Plus_or_minus node + +(* Public field definition *) + +and dec_public_field_definition ?(comments = []) node + : (public_field_definition, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Public_field_definition node + | _ -> + let decorators = children_named "decorator" node in + let* decorators = list_of_children dec_decorator decorators in + let accessibility_modifier = first_child_named_opt "accessibility_modifier" node in + let* access = make_opt_res dec_accessibility_modifier accessibility_modifier in + let kwd_declare = first_child_named_opt "declare" node in + let* kwd_declare = make_opt_res dec_kwd_declare kwd_declare in + let* scope = dec_field_scope node in + let* name_field = child_with_field "name" node ~err:Property_name in + let* name = dec_property_name ~comments name_field in + let* mode = dec_field_mode_opt node in + let type_field = child_with_field_opt "type" node in + let* type_ = make_opt_res dec_type_annotation type_field in + let* default = mk_child_initializer_opt node in + let* () = check_first_error_child node in + Ok { decorators; access; kwd_declare; scope; name; mode; type_; default } + +and dec_field_mode_opt node : (field_mode option, _) result = + let sym_qmark = first_child_named_opt "?" node in + match sym_qmark with + | Some sym_qmark -> + let* sym_qmark = dec_sym_qmark sym_qmark in + let* () = check_first_error_child node in + Ok (Some (Optional sym_qmark)) + | None -> + (match first_child_named_opt "!" node with + | None -> + let* () = check_first_error_child node in + Ok None + | Some sym_emark -> + let* sym_emark = dec_sym_emark sym_emark in + let* () = check_first_error_child node in + Ok (Some (Definite_assert sym_emark))) + +and dec_field_scope node : (field_scope, _) result = + let override_modifier = first_child_named_opt "override_modifier" node in + let* kwd_override = make_opt_res dec_override_modifier override_modifier in + let kwd_abstract = first_child_named_opt "abstract" node in + let* kwd_abstract = make_opt_res dec_kwd_abstract kwd_abstract in + let kwd_static = first_child_named_opt "static" node in + let* kwd_static = make_opt_res dec_kwd_static kwd_static in + let kwd_readonly = first_child_named_opt "readonly" node in + let* kwd_readonly = make_opt_res dec_kwd_readonly kwd_readonly in + let kwd_accessor = first_child_named_opt "accessor" node in + let* kwd_accessor = make_opt_res dec_kwd_accessor kwd_accessor in + let* () = check_first_error_child node in + Ok { kwd_static; kwd_override; kwd_readonly; kwd_abstract; kwd_accessor } + +(* Lexical declaration (see [dec_variable_declaration]) *) + +and dec_lexical_declaration ?(comments = []) node : (lexical_declaration, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Let_or_const node + | _ -> + let* kind_field = child_with_field "kind" node ~err:Let_or_const in + let decls = children_named "variable_declarator" node in + let error = mk_err Let_or_const node in + let* decls = ne_list_of_children dec_variable_declarator error decls in + let* kind = + match get_name kind_field with + | "let" -> + let* kwd_let = dec_kwd_let ~comments kind_field in + Ok (Let kwd_let) + | "const" -> + let* kwd_const = dec_kwd_const ~comments kind_field in + Ok (Const kwd_const) + | _ -> mk_err Let_or_const kind_field + in + let* () = check_first_error_child node in + Ok { kind; decls } + +(* Variable declaration (see [dec_lexical_declaration]) *) + +and dec_variable_declaration ?(comments = []) node : (variable_declaration, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Variable_declaration node + | _ -> + let comments = comments @ prev_comments node in + let* kwd_var = first_child_named "var" node ~err:Var in + let* kwd_var = dec_kwd_var ~comments kwd_var in + let var_decls = children_named "variable_declarator" node in + let error = mk_err Variable_declaration node in + let* var_decls = ne_list_of_children dec_variable_declarator error var_decls in + let* () = check_first_error_child node in + Ok (kwd_var, var_decls) + +and dec_variable_declarator ?(comments = []) node : (variable_declarator, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Variable node + | _ -> + let comments = comments @ prev_comments node in + let* name_field = child_with_field "name" node ~err:Variable in + let sym_qmark = first_child_named_opt "!" node in + (match sym_qmark with + | None -> + let* var_names = dec_lhs_pattern ~comments name_field in + let type_field = child_with_field_opt "type" node in + let* var_type = make_opt_res dec_type_annotation type_field in + let* default = mk_child_initializer_opt node in + let decl = { var_names; var_type; default } in + let* () = check_first_error_child node in + Ok (Var_decl (Wrap.make decl (!get_region node))) + | Some sym_qmark -> + let identifier = dec_identifier ~comments name_field in + let* sym_qmark = dec_sym_qmark sym_qmark in + let* type_field = child_with_field "type" node ~err:Type_annotation in + let* var_type = dec_type_annotation type_field in + let* () = check_first_error_child node in + Ok (Var_decl_assertion (identifier, sym_qmark, var_type))) + +and dec_lhs_pattern ?comments node : (lhs_pattern, _) result = + match get_name node with + | "identifier" -> + let* () = check_first_error_child node in + Ok (Decl_ident (dec_identifier ?comments node)) + | _ -> + let* pattern = dec_destructuring_pattern ?comments node in + let* () = check_first_error_child node in + Ok (Decl_pattern pattern) + +(* Function signature (See [dec_function_declaration]) *) + +and dec_function_signature ?(comments = []) node : (function_signature, _) result = + let comments = comments @ prev_comments node in + let kwd_async = first_child_named_opt "async" node in + let async_comments, function_comments = + match kwd_async with + | None -> [], comments + | Some _ -> comments, [] + in + let* kwd_async = make_opt_res (dec_kwd_async ~comments:async_comments) kwd_async in + let* kwd_function = first_child_named "function" node ~err:Function in + let* kwd_function = dec_kwd_function ~comments:function_comments kwd_function in + let* name_field = child_with_field "name" node ~err:Function_name in + let name = dec_identifier name_field in + let* call_sig = dec_call_signature node in + let* () = check_first_error_child node in + Ok { kwd_async; kwd_function; name; call_sig } + +(* Formal parameters *) + +(* TODO: Test with Tests/formal.ts *) + +and dec_formal_parameters ?comments node : (formal_parameters, _) result = + dec_list_in_parens ?comments node (wrap dec_formal_parameter) ~err:Parameters + +and dec_formal_parameter ?(comments = []) node : (formal_parameter, _) result = + match get_name node with + | "required_parameter" -> dec_required_parameter ~comments node + | "optional_parameter" -> dec_optional_parameter ~comments node + | _ -> mk_err Parameter node + +and dec_required_parameter ?(comments = []) node : (formal_parameter, _) result = + let* parameter_name = wrap dec_parameter_name ~comments node in + let optional = None in + let type_field = child_with_field_opt "type" node in + let* type_opt = make_opt_res dec_type_annotation type_field in + let* default = mk_child_initializer_opt node in + let* () = check_first_error_child node in + Ok { parameter_name; optional; type_opt; default } + +and dec_optional_parameter ?(comments = []) node : (formal_parameter, _) result = + let* parameter_name = wrap dec_parameter_name ~comments node in + let* sym_qmark = first_child_named "?" node ~err:Question_mark in + let* sym_qmark = dec_sym_qmark sym_qmark in + let optional = Some sym_qmark in + let type_field = child_with_field_opt "type" node in + let* type_opt = make_opt_res dec_type_annotation type_field in + let* default = mk_child_initializer_opt node in + let* () = check_first_error_child node in + Ok { parameter_name; optional; type_opt; default } + +and dec_parameter_name ?(comments = []) node : (parameter_name, _) result = + let comments = comments @ prev_comments node in + let decorators = children_named "decorator" node in + let* decorators = list_of_children dec_decorator decorators in + let accessibility_modifier = first_child_named_opt "accessibility_modifier" node in + let* access = make_opt_res dec_accessibility_modifier accessibility_modifier in + let override_modifier = first_child_named_opt "override_modifier" node in + let* kwd_override = make_opt_res dec_override_modifier override_modifier in + let kwd_readonly = first_child_named_opt "readonly" node in + let* kwd_readonly = make_opt_res dec_kwd_readonly kwd_readonly in + let* pattern_field = child_with_field "pattern" node ~err:Pattern in + let* pattern = dec_parameter_pattern ~comments pattern_field (* Not ideal *) in + let* () = check_first_error_child node in + Ok { decorators; access; kwd_override; kwd_readonly; pattern } + +and dec_parameter_pattern ?(comments = []) node : (parameter_pattern, _) result = + match get_name node with + | "this" -> + let* kwd_this = dec_kwd_this ~comments node in + let* () = check_first_error_child node in + Ok (Parameter_this kwd_this) + | _ -> + let* pattern = dec_pattern ~comments node in + let* () = check_first_error_child node in + Ok (Parameter_pattern pattern) + +and mk_child_initializer_opt node : ((sym_equal * expression) option, _) result = + match first_child_named_opt "=" node with + | None -> + let* () = check_first_error_child node in + Ok None + | Some sym_equal -> + let* init = mk_child_initializer sym_equal node in + let* () = check_first_error_child node in + Ok (Some init) + +and mk_child_initializer sym_equal node : (sym_equal * expression, _) result = + let* sym_equal = dec_sym_equal sym_equal in + let* value_field = child_with_field "value" node ~err:Expression in + let* expression = dec_expression value_field in + let* () = check_first_error_child node in + Ok (sym_equal, expression) + +(* Abstract class declaration + + The difference with [dec_class_declaration] is the decoding of the + keyword "abstract". The AST of abstract class definitions do not + reuse that for class definitions because of the handling of the + comments, which should be hooked either on the keyword "class" or + "abstract". *) + +and dec_abstract_class_declaration ?(comments = []) node + : (abstract_class_declaration, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Abstract node + | _ -> + let comments = comments @ prev_comments node in + let decorators = children_named "decorator" node in + let* decorators = list_of_children dec_decorator decorators in + let* kwd_abstract = first_child_named "abstract" node ~err:Abstract in + let* kwd_abstract = dec_kwd_abstract ~comments kwd_abstract in + let* kwd_class = first_child_named "class" node ~err:Class in + let* kwd_class = dec_kwd_class kwd_class in + let* name_field = child_with_field "name" node ~err:Class_name in + let name = dec_identifier name_field in + let type_parameters_field = child_with_field_opt "type_parameters" node in + let* type_parameters = make_opt_res dec_type_parameters type_parameters_field in + let heritage_child = first_child_named_opt "class_heritage" node in + let* class_heritage = make_opt_res dec_class_heritage heritage_child in + let* body_field = child_with_field "body" node ~err:Class_body in + let* body = dec_class_body body_field in + let* () = check_first_error_child node in + Ok + { decorators; kwd_abstract; kwd_class; name; type_parameters; class_heritage; body } + +(* Module *) + +and dec_module_declaration ?(comments = []) node : (module_declaration, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Module_declaration node + | _ -> + let comments = comments @ prev_comments node in + let* kwd_module = first_child_named "module" node ~err:Module in + let* kwd_module = dec_kwd_module ~comments kwd_module in + let* name_field = child_with_field "name" node ~err:Module_name in + let* module_name = dec_module_name name_field in + let body_field = child_with_field_opt "body" node in + let* module_body = make_opt_res dec_statement_block body_field in + let* () = check_first_error_child node in + Ok { kwd_module; module_name; module_body } + +and dec_module_name node : (module_name, _) result = + match get_name node with + | "string" -> + let* () = check_first_error_child node in + Ok (Module_string (dec_string node)) + | "identifier" -> + let* () = check_first_error_child node in + Ok (Module_ident (dec_identifier node)) + | "nested_identifier" -> + let* nested = dec_nested_identifier node in + let nested = Wrap.make nested (!get_region node) in + let* () = check_first_error_child node in + Ok (Module_nested nested) + | _ -> mk_err Module_name node + +(* Internal module (a.k.a. namespaces) *) + +and dec_internal_module ?(comments = []) node : (internal_module, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Namespace_declaration node + | _ -> + let comments = comments @ prev_comments node in + let* kwd_namespace = first_child_named "namespace" node ~err:Namespace in + let* kwd_namespace = dec_kwd_namespace ~comments kwd_namespace in + let* name_field = child_with_field "name" node ~err:Namespace_name in + let* module_name = dec_module_name name_field in + let body_field = child_with_field_opt "body" node in + let* module_body = make_opt_res dec_statement_block body_field in + let* () = check_first_error_child node in + Ok { kwd_namespace; module_name; module_body } + +(* Type alias declaration *) + +and dec_type_alias_declaration ?(comments = []) node : (type_alias_declaration, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Type_alias_declaration node + | _ -> + let comments = comments @ prev_comments node in + let* kwd_type = first_child_named "type" node ~err:Type in + let* kwd_type = dec_kwd_type ~comments kwd_type in + let* name_field = child_with_field "name" node ~err:Type_name in + let name = dec_type_identifier name_field in + let* sym_equal = first_child_named "=" node ~err:Equal in + let* sym_equal = dec_sym_equal sym_equal in + let type_parameters_field = child_with_field_opt "type_parameters" node in + let* type_parameters = make_opt_res dec_type_parameters type_parameters_field in + let* value_field = child_with_field "value" node ~err:Type_expression in + let* type_expr = dec_type value_field in + let* () = check_first_error_child node in + Ok { kwd_type; name; type_parameters; sym_equal; type_expr } + +(* Type parameters *) + +and dec_type_parameters ?comments node : (type_parameters, _) result = + dec_list_in_chevrons ?comments node dec_type_parameter ~err:Type_parameters + +and dec_type_parameter ?(comments = []) node : (type_parameter wrap, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Const_or_type_name node + | _ -> + let comments = comments @ prev_comments node in + let kwd_const = first_child_named_opt "const" node in + let* kwd_const = make_opt_res dec_kwd_const kwd_const in + let* name_field = child_with_field "name" node ~err:Type_parameter in + let name = dec_type_identifier ~comments name_field (* Not ideal *) in + let constraint_field = child_with_field_opt "constraint" node in + let* constraint_expr = make_opt_res dec_constraint constraint_field in + let value_field = child_with_field_opt "value" node in + let* default_type = make_opt_res dec_default_type value_field in + let type_parameter = { kwd_const; name; constraint_expr; default_type } in + let* () = check_first_error_child node in + Ok (Wrap.make type_parameter (!get_region node)) + +and dec_type_identifier ?comments node : type_identifier = dec_identifier ?comments node + +and dec_constraint node : (kwd_extends * type_expr, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Extends node + | _ -> + let* kwd_extends = first_child_named "extends" node ~err:Extends in + let* kwd_extends = dec_kwd_extends kwd_extends in + let* type_child = child_ranked 1 node ~err:Type_expression in + let* type_expr = dec_type type_child in + let* () = check_first_error_child node in + Ok (kwd_extends, type_expr) + +and dec_default_type node : (sym_equal * type_expr, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Equal node + | _ -> + let* sym_equal = first_child_named "=" node ~err:Equal in + let* sym_equal = dec_sym_equal sym_equal in + let* type_node = child_ranked 1 node ~err:Type_expression in + let* type_expr = dec_type type_node in + let* () = check_first_error_child node in + Ok (sym_equal, type_expr) + +(* Enum declaration *) + +and dec_enum_declaration ?(comments = []) node : (enum_declaration, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Const_or_enum node + | _ -> + let kwd_const = first_child_named_opt "const" node in + let* kwd_const = make_opt_res dec_kwd_const kwd_const in + let* kwd_enum = first_child_named "enum" node ~err:Enum in + let* kwd_enum = dec_kwd_enum ~comments kwd_enum in + let* name_field = child_with_field "name" node ~err:Enumeration_name in + let name = dec_identifier name_field in + let* body_field = child_with_field "body" node ~err:Enumeration in + let* body = dec_enum_entries body_field in + let* () = check_first_error_child node in + Ok { kwd_const; kwd_enum; name; body } + +and dec_enum_entries node : (enum_body list braces, _) result = + dec_list_in_braces node dec_enum_body ~err:Enumeration + +and dec_enum_body ?(comments = []) node : (enum_body, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Enumeration_name node + | "enum_assignment" -> + let* assignment = wrap dec_enum_assignment ~comments node in + let* () = check_first_error_child node in + Ok (Enum_assignment assignment) + | _ -> + let* property = dec_property_name ~comments node in + let* () = check_first_error_child node in + Ok (Enum_name property) + +and dec_enum_assignment ?comments node : (enum_assignment, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Enumeration_name node + | _ -> + let* name_field = child_with_field "name" node ~err:Enumeration_name in + let* name = dec_property_name ?comments name_field in + let* sym_equal = first_child_named "=" node ~err:Equal in + let* default = mk_child_initializer sym_equal node in + let* () = check_first_error_child node in + Ok { name; default } + +(* Property names *) + +and dec_property_name ?(comments = []) node : (property_name, _) result = + match get_name node with + | "property_identifier" -> + let* () = check_first_error_child node in + let ident = dec_identifier ~comments node in + Ok (Property_identifier ident : property_name) + | "private_property_identifier" -> + let* () = check_first_error_child node in + let ident = dec_private_property_identifier ~comments node in + Ok (Private_property_identifier ident : property_name) + | "string" -> + let* () = check_first_error_child node in + Ok (String (dec_string ~comments node)) + | "number" -> + let* number = dec_number ~comments node in + let* () = check_first_error_child node in + Ok (Number number) + | "computed_property_name" -> + let* expression = dec_computed_property_name ~comments node in + let* () = check_first_error_child node in + Ok (Computed_property_name expression) + | _ -> mk_err Property_name node + +and dec_private_property_identifier ?(comments = []) node : private_property_identifier = + dec_identifier ~comments node + +and dec_computed_property_name ?comments node : (expression brackets, _) result = + dec_brackets ?comments node dec_expression ~err:Computed_property_name + +(* Interface declaration *) + +and dec_interface_declaration ?(comments = []) node : (interface_declaration, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Interface node + | _ -> + let* kwd_interface = first_child_named "interface" node ~err:Interface in + let* kwd_interface = dec_kwd_interface ~comments kwd_interface in + let* name_field = child_with_field "name" node ~err:Interface_name in + let name = dec_type_identifier name_field in + let type_parameters_field = child_with_field_opt "type_parameters" node in + let* type_parameters = make_opt_res dec_type_parameters type_parameters_field in + let extends_type_clause = first_child_named_opt "extends_type_clause" node in + let* extends = make_opt_res dec_extends_type_clause extends_type_clause in + let* body_field = child_with_field "body" node ~err:Interface_body in + let* body = dec_object_type body_field in + let* () = check_first_error_child node in + Ok { kwd_interface; name; type_parameters; extends; body } + +and dec_extends_type_clause node : (extends_type_clause, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Extends node + | _ -> + let* kwd_extends = first_child_named "extends" node ~err:Extends in + let* kwd_extends = dec_kwd_extends kwd_extends in + let named_children = collect_named_children node in + let error = mk_err Extends node in + let* extensions = ne_list_of_children dec_type_extension error named_children in + let* () = check_first_error_child node in + Ok { kwd_extends; extensions } + +and dec_type_extension ?(comments = []) node : (type_extension, _) result = + match get_name node with + | "type_identifier" -> + let* () = check_first_error_child node in + Ok (Extends_type (dec_type_identifier ~comments node)) + | "nested_type_identifier" -> + let* nested = wrap dec_nested_type_identifier ~comments node in + let* () = check_first_error_child node in + Ok (Extends_nested nested) + | "generic_type" -> + let* type_expr = wrap dec_generic_type ~comments node in + let* () = check_first_error_child node in + Ok (Extends_generic type_expr) + | _ -> mk_err Type_expression node + +(* Nested type identifier *) + +and dec_nested_type_identifier ?(comments = []) node : (nested_type_identifier, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Nested_type_identifier node + | _ -> + let* module_field = child_with_field "module" node ~err:Identifier_or_path in + let* name_field = child_with_field "name" node ~err:Type_name in + let* path = dec_module_path ~comments module_field in + let* () = check_first_error_child node in + Ok (path, dec_type_identifier name_field) + +and dec_module_path ?(comments = []) node : (identifier ne_list, _) result = + match get_name node with + | "identifier" -> + let* () = check_first_error_child node in + Ok Nonempty_list.[ dec_type_identifier ~comments node ] + | "nested_identifier" -> + let* path, id = dec_nested_identifier ~comments node in + let* () = check_first_error_child node in + Ok (Nonempty_list.cons id path) + | _ -> mk_err Identifier_or_path node + +(* Import alias *) + +and dec_import_alias ?(comments = []) node : (import_alias, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Import node + | _ -> + let* kwd_import = first_child_named "import" node ~err:Import in + let* kwd_import = dec_kwd_import ~comments kwd_import in + let* lhs = child_ranked 1 node ~err:Identifier in + let alias = dec_identifier lhs in + let* sym_equal = first_child_named "=" node ~err:Equal in + let* sym_equal = dec_sym_equal sym_equal in + let* rhs = child_ranked 3 node ~err:Identifier_or_path in + let* aliased = dec_aliased rhs in + let* () = check_first_error_child node in + Ok { kwd_import; alias; sym_equal; aliased } + +and dec_aliased node : (aliased, _) result = + match get_name node with + | "identifier" -> Ok (Ident (dec_identifier node)) + | "nested_identifier" -> + let* nested = wrap dec_nested_identifier node in + let* () = check_first_error_child node in + Ok (Nested nested) + | _ -> mk_err Identifier_or_path node + +(* Nested identifier *) + +and dec_nested_identifier ?(comments = []) node : (nested_identifier, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Identifier_or_member node + | _ -> + let* object_field = child_with_field "object" node ~err:Identifier_or_member in + let* property_field = child_with_field "property" node ~err:Property_identifier in + let* path = dec_object_path ~comments object_field in + let* property = dec_property property_field in + let* () = check_first_error_child node in + Ok (path, property) + +and dec_object_path ?(comments = []) node : (identifier ne_list, _) result = + match get_name node with + | "identifier" -> + let* () = check_first_error_child node in + Ok Nonempty_list.[ dec_identifier ~comments node ] + | "member_expression" -> + let* path, id = dec_nested_identifier ~comments node in + let* () = check_first_error_child node in + Ok (Nonempty_list.cons id path) + | _ -> mk_err Identifier_or_member node + +and dec_property node : (identifier, _) result = + match get_name node with + | "property_identifier" -> + let* () = check_first_error_child node in + Ok (dec_identifier node) + | _ -> mk_err Property_identifier node + +(* Ambient declaration *) + +and dec_ambient_declaration ?comments node : (ambient_declaration, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Declare node + | _ -> + let* kwd_declare = first_child_named "declare" node ~err:Declare in + let* kwd_declare = dec_kwd_declare ?comments kwd_declare in + let* fst_child = named_child_ranked 0 node ~err:Block_or_ident_or_decl in + let* ambient_kind = + match get_name fst_child with + | "statement_block" -> + let* kwd_global = first_child_named "global" node ~err:Global in + let* kwd_global = dec_kwd_global kwd_global in + let* block = dec_statement_block fst_child in + Ok (Global_declaration (kwd_global, block)) + | "property_identifier" -> + let* kwd_module = first_child_named "module" node ~err:Module in + let* kwd_module = dec_kwd_module kwd_module in + let* type_child = child_ranked 5 node ~err:Type_expression in + let identifier = dec_identifier fst_child in + let* type_expr = dec_type type_child in + Ok (Module_declaration (kwd_module, identifier, type_expr)) + | _ -> + let* declaration = dec_declaration fst_child in + Ok (Declaration declaration) + in + let* () = check_first_error_child node in + Ok { kwd_declare; ambient_kind } + +(* EXPRESSION + + The JavasScript tree-sitter grammar have the non-terminals + "expression" and "primary_expression" be supertypes, that is, + hidden rules. Therefore we have to match all the RHS of those + non-terminals in [dec_expression]. *) + +and dec_expression ?(comments = []) node : (expression, _) result = + match get_name node with + (*| "glimmer_template" -> Ok (E_glimmer_template (dec_glimmer_template node))*) + | "assignment_expression" -> + let* expression = wrap dec_assignment_expression node in + let* () = check_first_error_child node in + Ok (E_assignment_expression expression) + | "augmented_assignment_expression" -> + let* expression = wrap dec_augmented_assignment_expression node in + let* () = check_first_error_child node in + Ok (E_augmented_assignment_expression expression) + | "await_expression" -> + let* expression = wrap dec_await_expression node in + let* () = check_first_error_child node in + Ok (E_await_expression expression) + | "unary_expression" -> + let* expression = wrap dec_unary_expression ~comments node in + let* () = check_first_error_child node in + Ok (E_unary_expression expression) + | "binary_expression" -> + let* expression = wrap dec_binary_expression ~comments node in + let* () = check_first_error_child node in + Ok (E_binary_expression expression) + | "ternary_expression" -> + let* expression = wrap dec_ternary_expression ~comments node in + let* () = check_first_error_child node in + Ok (E_ternary_expression expression) + | "update_expression" -> + let* expression = dec_update_expression ~comments node in + let* () = check_first_error_child node in + Ok (E_update_expression expression) + | "new_expression" -> + let* expression = wrap dec_new_expression ~comments node in + let* () = check_first_error_child node in + Ok (E_new_expression expression) + | "yield_expression" -> + let* expression = dec_yield_expression node in + let* () = check_first_error_child node in + Ok (E_yield_expression expression) + | "as_expression" -> + let* expression = wrap dec_as_expression ~comments node in + let* () = check_first_error_child node in + Ok (E_as_expression expression) + | "satisfies_expression" -> + let* expression = wrap dec_satisfies_expression ~comments node in + let* () = check_first_error_child node in + Ok (E_satisfies_expression expression) + | "instantiation_expression" -> + let* expression = wrap dec_instantiation_expression ~comments node in + let* () = check_first_error_child node in + Ok (E_instantiation_expression expression) + | "internal_module" -> + let* declaration = wrap dec_internal_module ~comments node in + let* () = check_first_error_child node in + Ok (E_internal_module declaration) + | "type_assertion" -> + let* assertion = wrap dec_type_assertion ~comments node in + let* () = check_first_error_child node in + Ok (E_type_assertion assertion) + | _ -> + let* expression = dec_primary_expression ~comments node in + let* () = check_first_error_child node in + Ok (E_primary_expression expression) + +(* Assignment expression *) + +and dec_assignment_expression ?(comments = []) node : (assignment_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Using_or_expression node + | _ -> + let kwd_using = first_child_named_opt "using" node in + let* kwd_using = make_opt_res dec_kwd_using kwd_using in + let* left_field = child_with_field "left" node ~err:Expression in + let* left = dec_assignment_lhs ~comments left_field in + let* sym_equal = first_child_named "=" node ~err:Equal in + let* sym_equal = dec_sym_equal sym_equal in + let* right_field = child_with_field "right" node ~err:Expression in + let* right = dec_expression right_field in + let* () = check_first_error_child node in + Ok { kwd_using; left; sym_equal; right } + +and dec_assignment_lhs ?(comments = []) node : (assignment_lhs, _) result = + match get_name node with + | "parenthesized_expression" -> + let* expression = dec_parenthesized_expression ~comments node in + let* () = check_first_error_child node in + Ok (Assign_lhs_parens expression) + | _ -> + let* expression = dec_lhs_expression ~comments node in + let* () = check_first_error_child node in + Ok (Assign_lhs expression) + +(* Augmented assignment expression *) + +and dec_augmented_assignment_expression ?(comments = []) node + : (augmented_assignment_expression, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err LHS_of_augmented_assgmnt node + | _ -> + let* left_field = child_with_field "left" node ~err:Expression in + let* left = dec_augmented_assignment_lhs ~comments left_field in + let* operator = child_with_field "operator" node ~err:Augmented_assignment in + let* operator = dec_assignment_operator operator in + let* right_field = child_with_field "right" node ~err:Expression in + let* right = dec_expression right_field in + let* () = check_first_error_child node in + Ok { left; operator; right } + +and dec_assignment_operator node : (assignment_operator, _) result = + match get_name node with + | "+=" -> + let* sym_plus_equal = dec_sym_plus_equal node in + let* () = check_first_error_child node in + Ok (Add_eq sym_plus_equal) + | "-=" -> + let* sym_minus_equal = dec_sym_minus_equal node in + let* () = check_first_error_child node in + Ok (Sub_eq sym_minus_equal) + | "*=" -> + let* sym_mult_equal = dec_sym_mult_equal node in + let* () = check_first_error_child node in + Ok (Mult_eq sym_mult_equal) + | "/=" -> + let* sym_div_equal = dec_sym_div_equal node in + let* () = check_first_error_child node in + Ok (Div_eq sym_div_equal) + | "%=" -> + let* sym_rem_equal = dec_sym_rem_equal node in + let* () = check_first_error_child node in + Ok (Rem_eq sym_rem_equal) + | "^=" -> + let* sym_xor_equal = dec_sym_xor_equal node in + let* () = check_first_error_child node in + Ok (Bitwise_xor_eq sym_xor_equal) + | "&=" -> + let* sym_and_equal = dec_sym_and_equal node in + let* () = check_first_error_child node in + Ok (Bitwise_and_eq sym_and_equal) + | "|=" -> + let* sym_or_equal = dec_sym_or_equal node in + let* () = check_first_error_child node in + Ok (Bitwise_or_eq sym_or_equal) + | ">>=" -> + let* sym_shift_right_equal = dec_sym_shift_right_equal node in + let* () = check_first_error_child node in + Ok (Bitwise_sr_eq sym_shift_right_equal) + | ">>>=" -> + let* sym_unsigned_shift_right_equal = dec_sym_unsigned_shift_right_equal node in + let* () = check_first_error_child node in + Ok (Bitwise_usr_eq sym_unsigned_shift_right_equal) + | "<<=" -> + let* sym_shift_left_equal = dec_sym_shift_left_equal node in + let* () = check_first_error_child node in + Ok (Bitwise_sl_eq sym_shift_left_equal) + | "**=" -> + let* sym_exponent_equal = dec_sym_exponent_equal node in + let* () = check_first_error_child node in + Ok (Exp_eq sym_exponent_equal) + | "&&=" -> + let* sym_conjunction_equal = dec_sym_conjunction_equal node in + let* () = check_first_error_child node in + Ok (Logical_and_eq sym_conjunction_equal) + | "||=" -> + let* sym_disjunction_equal = dec_sym_disjunction_equal node in + let* () = check_first_error_child node in + Ok (Logical_or_eq sym_disjunction_equal) + | "??=" -> + let* sym_non_null_equal = dec_sym_non_null_equal node in + let* () = check_first_error_child node in + Ok (Non_null_eq sym_non_null_equal) + | _ -> mk_err Augmented_assignment node + +and dec_augmented_assignment_lhs ?(comments = []) node + : (augmented_assignment_lhs, _) result + = + match get_name node with + | "member_expression" -> + let* expression = wrap dec_member_expression ~comments node in + let* () = check_first_error_child node in + Ok (Member_expression expression) + | "subscript_expression" -> + let* expression = wrap dec_subscript_expression ~comments node in + let* () = check_first_error_child node in + Ok (Subscript_expression expression) + | "identifier" -> + let* () = check_first_error_child node in + Ok (Identifier (dec_identifier ~comments node)) + | "parenthesized_expression" -> + let* expression = dec_parenthesized_expression ~comments node in + let* () = check_first_error_child node in + Ok (Parenthesized_expression expression) + | _ -> mk_err Expression node + +(* Await expression *) + +and dec_await_expression ?(comments = []) node : (await_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Await node + | _ -> + let* kwd_await = first_child_named "await" node ~err:Await in + let* kwd_await = dec_kwd_await ~comments kwd_await in + let* expression = child_ranked 1 node ~err:Expression in + let* expression = dec_expression expression in + let* () = check_first_error_child node in + Ok { kwd_await; expression } + +(* Unary expression *) + +and dec_unary_expression ?(comments = []) node : (unary_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Unary_operator node + | _ -> + let* operator_field = child_with_field "operator" node ~err:Unary_operator in + let* operator = dec_unary_operator ~comments operator_field in + let* argument_field = child_with_field "argument" node ~err:Expression in + let* argument = dec_expression argument_field in + let* () = check_first_error_child node in + Ok ({ operator; argument } : unary_expression) + +and dec_unary_operator ?(comments = []) node : (unary_operator, _) result = + match get_name node with + | "!" -> + let* sym_qmark = dec_sym_qmark ~comments node in + let* () = check_first_error_child node in + Ok (Logical_neg sym_qmark) + | "~" -> + let* sym_tilde = dec_sym_tilde ~comments node in + let* () = check_first_error_child node in + Ok (Bitwise_not sym_tilde) + | "-" -> + let* sym_minus = dec_sym_minus ~comments node in + let* () = check_first_error_child node in + Ok (Neg sym_minus) + | "+" -> + let* sym_plus = dec_sym_plus ~comments node in + let* () = check_first_error_child node in + Ok (Plus_zero sym_plus) + | "typeof" -> + let* kwd_typeof = dec_kwd_typeof ~comments node in + let* () = check_first_error_child node in + Ok (Typeof kwd_typeof) + | "void" -> + let* kwd_void = dec_kwd_void ~comments node in + let* () = check_first_error_child node in + Ok (Void kwd_void) + | "delete" -> + let* kwd_delete = dec_kwd_delete ~comments node in + let* () = check_first_error_child node in + Ok (Delete kwd_delete) + | _ -> mk_err Unary_operator node + +(* Binary expression *) + +and dec_binary_expression ?(comments = []) node : (binary_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Expression node + | _ -> + let comments = comments @ prev_comments node in + let* left_field = child_with_field "left" node ~err:Expression in + let* lhs_expr = dec_lhs_bin_expression ~comments left_field in + let* operator = child_with_field "operator" node ~err:Binary_operator in + let* operator = dec_binary_operator operator in + let* right_field = child_with_field "right" node ~err:Expression in + let* rhs_expr = dec_expression right_field in + let* () = check_first_error_child node in + Ok { lhs_expr; operator; rhs_expr } + +and dec_lhs_bin_expression ~comments node : (lhs_bin_expression, _) result = + match get_name node with + | "private_property_identifier" -> + let* () = check_first_error_child node in + Ok (Lhs_bin_hash (dec_private_property_identifier ~comments node)) + | _ -> + let* hash = dec_expression ~comments node in + let* () = check_first_error_child node in + Ok (Lhs_bin_expression hash) + +and dec_binary_operator node : (binary_operator, _) result = + match get_name node with + | "&&" -> + let* sym_conjunction = dec_sym_conjunction node in + let* () = check_first_error_child node in + Ok (Logical_and sym_conjunction) + | "||" -> + let* sym_disjunction = dec_sym_disjunction node in + let* () = check_first_error_child node in + Ok (Logical_or sym_disjunction) + | ">>" -> + let* sym_shift_right = dec_sym_shift_right node in + let* () = check_first_error_child node in + Ok (Bitwise_sr sym_shift_right) + | ">>>" -> + let* sym_unsigned_shift_right = dec_sym_unsigned_shift_right node in + let* () = check_first_error_child node in + Ok (Bitwise_usr sym_unsigned_shift_right) + | "<<" -> + let* sym_shift_left = dec_sym_shift_left node in + let* () = check_first_error_child node in + Ok (Bitwise_sl sym_shift_left) + | "&" -> + let* sym_and = dec_sym_and node in + let* () = check_first_error_child node in + Ok (Bitwise_and sym_and) + | "^" -> + let* sym_xor = dec_sym_xor node in + let* () = check_first_error_child node in + Ok (Bitwise_xor sym_xor) + | "|" -> + let* sym_or = dec_sym_or node in + let* () = check_first_error_child node in + Ok (Bitwise_or sym_or) + | "+" -> + let* sym_plus = dec_sym_plus node in + let* () = check_first_error_child node in + Ok (Add sym_plus) + | "-" -> + let* sym_minus = dec_sym_minus node in + let* () = check_first_error_child node in + Ok (Sub sym_minus) + | "*" -> + let* sym_asterisk = dec_sym_asterisk node in + let* () = check_first_error_child node in + Ok (Mult sym_asterisk) + | "/" -> + let* sym_div = dec_sym_div node in + let* () = check_first_error_child node in + Ok (Div sym_div) + | "%" -> + let* sym_rem = dec_sym_rem node in + let* () = check_first_error_child node in + Ok (Rem sym_rem) + | "**" -> + let* sym_exponent = dec_sym_exponent node in + let* () = check_first_error_child node in + Ok (Exp sym_exponent) + | "<" -> + let* sym_less_than = dec_sym_less_than node in + let* () = check_first_error_child node in + Ok (Lt sym_less_than) + | "<=" -> + let* sym_less_than_or_equal = dec_sym_less_than_or_equal node in + let* () = check_first_error_child node in + Ok (Leq sym_less_than_or_equal) + | "==" -> + let* sym_strict_equal = dec_sym_strict_equal node in + let* () = check_first_error_child node in + Ok (Equal sym_strict_equal) + | "===" -> + let* sym_no_conv_equal = dec_sym_no_conv_equal node in + let* () = check_first_error_child node in + Ok (Strict_eq sym_no_conv_equal) + | "!=" -> + let* sym_different = dec_sym_different node in + let* () = check_first_error_child node in + Ok (Neq sym_different) + | "!==" -> + let* sym_no_conv_different = dec_sym_no_conv_different node in + let* () = check_first_error_child node in + Ok (Strict_neq sym_no_conv_different) + | ">=" -> + let* sym_greater_than_or_equal = dec_sym_greater_than_or_equal node in + let* () = check_first_error_child node in + Ok (Geq sym_greater_than_or_equal) + | ">" -> + let* sym_greater_than = dec_sym_greater_than node in + let* () = check_first_error_child node in + Ok (Gt sym_greater_than) + | "??" -> + let* sym_non_null = dec_sym_non_null node in + let* () = check_first_error_child node in + Ok (Non_null sym_non_null) + | "instanceof" -> + let* kwd_instanceof = dec_kwd_instanceof node in + let* () = check_first_error_child node in + Ok (Instance_of kwd_instanceof) + | "in" -> + let* kwd_in = dec_kwd_in node in + let* () = check_first_error_child node in + Ok (In kwd_in : binary_operator) + | _ -> mk_err Binary_operator node + +(* Ternary expression *) + +and dec_ternary_expression ?(comments = []) node : (ternary_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Expression node + | _ -> + let* condition_field = child_with_field "condition" node ~err:Expression in + let* condition = dec_expression ~comments condition_field in + let* sym_qmark = first_child_named "?" node ~err:Question_mark in + let* sym_qmark = dec_sym_qmark sym_qmark in + let* consequence_field = child_with_field "consequence" node ~err:Expression in + let* consequence = dec_expression consequence_field in + let* sym_colon = first_child_named ":" node ~err:Colon in + let* sym_colon = dec_sym_colon sym_colon in + let* alternative_field = child_with_field "alternative" node ~err:Expression in + let* alternative = dec_expression alternative_field in + let* () = check_first_error_child node in + Ok { condition; sym_qmark; consequence; sym_colon; alternative } + +(* Update expression *) + +and dec_update_expression ?(comments = []) node : (update_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Expression node + | _ -> + let region = !get_region node in + let* first_child = child_ranked 0 node ~err:Incr_or_decr_or_expr in + (match get_name first_child with + | "++" | "--" -> + let* operator = dec_incr_decr_operator ~comments first_child in + let* argument_field = child_with_field "argument" node ~err:Expression in + let* argument = dec_expression argument_field in + let update : update = { argument; operator } in + let update = Wrap.make update region in + let* () = check_first_error_child node in + Ok (Update_prefix update) + | _ -> + let* argument = dec_expression ~comments first_child in + let* operator_field = + child_with_field "operator" node ~err:Increment_or_decrement + in + let* operator = dec_incr_decr_operator operator_field in + let update : update = { argument; operator } in + let update = Wrap.make update region in + let* () = check_first_error_child node in + Ok (Update_postfix update)) + +and dec_incr_decr_operator ?(comments = []) node : (incr_decr_operator, _) result = + match get_name node with + | "++" -> + let* sym_increment = dec_sym_increment ~comments node in + let* () = check_first_error_child node in + Ok (Increment sym_increment) + | "--" -> + let* sym_decrement = dec_sym_decrement ~comments node in + let* () = check_first_error_child node in + Ok (Decrement sym_decrement) + | _ -> mk_err Increment_or_decrement node + +(* New expression + + Note that the constructor field is a primary expression, but + "primary_expression" is a supertype, that is, a hidden rule. We + assume it is an "expression", since primary expressions are a subset + of them. *) + +and dec_new_expression ?(comments = []) node : (new_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err New node + | _ -> + let* kwd_new = first_child_named "new" node ~err:New in + let* kwd_new = dec_kwd_new ~comments kwd_new in + let* constructor_field = child_with_field "constructor" node ~err:Expression in + let* constructor = dec_primary_expression constructor_field in + let type_arguments_field = child_with_field_opt "type_arguments" node in + let* type_arguments = make_opt_res dec_type_arguments type_arguments_field in + let arguments_field = child_with_field_opt "arguments" node in + let* arguments = make_opt_res dec_arguments arguments_field in + let* () = check_first_error_child node in + Ok { kwd_new; constructor; type_arguments; arguments } + +(* Yield expression *) + +and dec_yield_expression node : (yield_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Yield node + | _ -> + let region = !get_region node in + let* kwd_yield = first_child_named "yield" node ~err:Yield in + let* kwd_yield = dec_kwd_yield kwd_yield in + (match child_ranked_opt 1 node with + | None -> + let* () = check_first_error_child node in + Ok (Yield (Wrap.make (kwd_yield, None) region)) + | Some snd_child -> + (match get_name snd_child with + | "*" -> + let* sym_asterisk = dec_sym_asterisk snd_child in + let* expression = child_ranked 2 node ~err:Expression in + let* expression = dec_expression expression in + let iterable = kwd_yield, sym_asterisk, expression in + let* () = check_first_error_child node in + Ok (Yield_iterable (Wrap.make iterable region)) + | _ -> + let* expression = dec_expression snd_child in + let yield = kwd_yield, Some expression in + let* () = check_first_error_child node in + Ok (Yield (Wrap.make yield region)))) + +(* As-expression *) + +and dec_as_expression ?(comments = []) node : (as_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Expression node + | _ -> + let* expression = child_ranked 0 node ~err:Expression in + let* expression = dec_expression ~comments expression in + let* kwd_as = first_child_named "as" node ~err:As in + let* kwd_as = dec_kwd_as kwd_as in + let* as_what = child_ranked 2 node ~err:Const_or_type in + let* as_what = dec_as_what as_what in + let* () = check_first_error_child node in + Ok (expression, kwd_as, as_what) + +and dec_as_what node : (as_what, _) result = + match get_name node with + | "const" -> + let* kwd_const = dec_kwd_const node in + let* () = check_first_error_child node in + Ok (As_const kwd_const) + | _ -> + let* type_expr = dec_type node in + let* () = check_first_error_child node in + Ok (As_type type_expr) + +(* Statisfies-expression *) + +and dec_satisfies_expression ?(comments = []) node : (satisfies_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Expression node + | _ -> + let* expression = child_ranked 0 node ~err:Expression in + let* expression = dec_expression ~comments expression in + let* kwd_satisfies = first_child_named "satisfies" node ~err:Satisfies in + let* kwd_satisfies = dec_kwd_satisfies kwd_satisfies in + let* type_child = child_ranked 2 node ~err:Type_expression in + let* type_expr = dec_type type_child in + let* () = check_first_error_child node in + Ok (expression, kwd_satisfies, type_expr) + +(* Instantiation expression *) + +and dec_instantiation_expression ?(comments = []) node + : (instantiation_expression, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Expression node + | _ -> + let* expression = named_child_ranked 0 node ~err:Expression in + let* expression = dec_expression ~comments expression in + let* type_arguments_field = + child_with_field "type_arguments" node ~err:Type_arguments + in + let* type_arguments = dec_type_arguments type_arguments_field in + let* () = check_first_error_child node in + Ok (expression, type_arguments) + +(* Type assertion *) + +and dec_type_assertion ?(comments = []) node : (type_assertion, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Type_arguments node + | _ -> + let* type_arguments = named_child_ranked 0 node ~err:Type_arguments in + let* type_arguments = dec_type_arguments ~comments type_arguments in + let* expression = named_child_ranked 1 node ~err:Expression in + let* expression = dec_expression expression in + let* () = check_first_error_child node in + Ok (type_arguments, expression) + +(* Subscript expression (see [dec_member_expression]) *) + +and dec_subscript_expression ?(comments = []) node : (subscript_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Expression node + | _ -> + let* object_field = child_with_field "object" node ~err:Expression in + let* object_expr = dec_expression ~comments object_field in + let optional_chain_field = child_with_field_opt "optional_chain" node in + let* optional_chain = make_opt_res dec_optional_chain optional_chain_field in + let* index_field = child_with_field "index" node ~err:Expression in + let* contents = dec_index index_field in + let* sym_lbracket = first_child_named "[" node ~err:Left_bracket in + let* opening = dec_sym_lbracket sym_lbracket in + let* sym_rbracket = first_child_named "]" node ~err:Right_bracket in + let* closing = dec_sym_rbracket sym_rbracket in + let region = !get_region node in + let brackets = { opening; contents; closing } in + let index = Brackets (Wrap.make brackets region) in + let* () = check_first_error_child node in + Ok { object_expr; optional_chain; index } + +and dec_optional_chain node : (optional_chain, _) result = + match get_name node with + | "optional_chain" -> + let* sym_optional_chain = dec_sym_optional_chain node in + let* () = check_first_error_child node in + Ok (Optional_chain sym_optional_chain) + | _ -> mk_err Optional_chain node + +and dec_index ?(comments = []) node : (sequence_expression, _) result = + (* See [dec_expressions] *) + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Index_expression node + | "sequence_expression" -> dec_sequence_expression ~comments node + | _ -> + let* expression = dec_expression ~comments node in + let region = !get_region node in + let* () = check_first_error_child node in + Ok (Wrap.make Nonempty_list.[ expression ] region) + +(* Member expression *) + +and dec_member_expression ?(comments = []) node : (member_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Member_expression node + | _ -> + let* object_field = child_with_field "object" node ~err:Expression in + let* object_expr = dec_object_member ~comments object_field in + let optional_chain_field = child_with_field_opt "optional_chain" node in + let* property_field = child_with_field "property" node ~err:Property_identifier in + let* property = dec_property_ident property_field in + let* selector = + match optional_chain_field with + | None -> + let* sym_dot = first_child_named "." node ~err:Dot in + let* sym_dot = dec_sym_dot sym_dot in + Ok (Dot sym_dot) + | Some node -> + let* sym_optional_chain = dec_sym_optional_chain node in + Ok (Optional_chain sym_optional_chain : selector) + in + let* () = check_first_error_child node in + Ok ({ object_expr; selector; property } : member_expression) + +and dec_object_member ?comments node : (object_member, _) result = + match get_name node with + | "import" -> + let* kwd_import = dec_kwd_import ?comments node in + let* () = check_first_error_child node in + Ok (Object_member_import kwd_import) + | _ -> + let* expression = dec_expression ?comments node in + let* () = check_first_error_child node in + Ok (Object_member_expression expression) + +and dec_property_ident ?comments node : (property_ident, _) result = + let identifier = dec_identifier ?comments node in + match get_name node with + | "private_property_identifier" -> + let* () = check_first_error_child node in + Ok (Private_property_identifier identifier) + | "property_identifier" -> + let* () = check_first_error_child node in + Ok (Property_identifier identifier) + | _ -> mk_err Property_identifier node + +(* Parenthesised expression *) + +and dec_parenthesized_expression ?(comments = []) node + : (parenthesized_expression, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Parenthesized_expression node + | _ -> + let comments = comments @ prev_comments node in + let* sym_lparen = first_child_named "(" node ~err:Left_parenthesis in + let* opening = dec_sym_lparen ~comments sym_lparen in + let* sym_rparen = first_child_named ")" node ~err:Right_parenthesis in + let* closing = dec_sym_rparen sym_rparen in + let* first_named_child = child_ranked 1 node ~err:Expression in + let type_field = child_with_field_opt "type" node in + let* (contents : in_expressions) = + match type_field with + | Some type_field -> + let* expression = dec_expression first_named_child in + let* type_annotation = dec_type_annotation type_field in + Ok (Typed_expression (expression, type_annotation)) + | None -> + let* seq_expr = + match get_name first_named_child with + | "sequence_expression" -> dec_sequence_expression first_named_child + | _ -> + let* expression = dec_expression first_named_child in + let expressions = Nonempty_list.singleton expression in + let region = !get_region first_named_child in + Ok (Wrap.make expressions region) + in + Ok (Sequence_expression seq_expr) + in + let region = !get_region node in + let* () = check_first_error_child node in + Ok (Parens (Wrap.make { opening; contents; closing } region)) + +(* Sequence expression *) + +and dec_sequence_expression ?(comments = []) node : (sequence_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Expression node + | _ -> + let raw_children = collect_named_children node in + let* list = wrap_ne_list_opt_of_children ~comments dec_expression raw_children in + (match list with + | Some ne_list -> + let* () = check_first_error_child node in + Ok ne_list + | None -> mk_err Expression node) + +(* Object expression *) + +and dec_object_expr ?(comments = []) node : (object_expr, _) result = + dec_list_in_braces ~comments node dec_object_entry ~err:Object_expression + +and dec_object_entry ?(comments = []) node : (object_entry, _) result = + match get_name node with + | "pair" -> + let* pair = wrap dec_pair ~comments node in + let* () = check_first_error_child node in + Ok (Object_entry_pair pair) + | "spread_element" -> + let* spread = wrap dec_spread_element ~comments node in + let* () = check_first_error_child node in + Ok (Object_entry_spread spread) + | "method_definition" -> + let* definition = wrap dec_method_definition ~comments node in + let* () = check_first_error_child node in + Ok (Object_entry_method definition) + | "shorthand_property_identifier" -> + let* pattern = dec_shorthand_property_identifier_pattern ~comments node in + let* () = check_first_error_child node in + Ok (Object_entry_shorthand pattern) + | _ -> mk_err Object_field node + +(* Pairs *) + +and dec_pair ?(comments = []) node : (pair, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Key_value_pair node + | _ -> + let* key_field = child_with_field "key" node ~err:Property_name in + let* key = dec_property_name ~comments key_field in + let* sym_colon = first_child_named ":" node ~err:Colon in + let* sym_colon = dec_sym_colon sym_colon in + let* value_field = child_with_field "value" node ~err:Expression in + let* value = dec_expression value_field in + let* () = check_first_error_child node in + Ok { key; sym_colon; value } + +(* LHS expression *) + +and dec_lhs_expression ?comments node : (lhs_expression, _) result = + match get_name node with + | "member_expression" -> + let* expression = wrap dec_member_expression ?comments node in + let* () = check_first_error_child node in + Ok (Member_expression expression : lhs_expression) + | "subscript_expression" -> + let* expression = wrap dec_subscript_expression ?comments node in + let* () = check_first_error_child node in + Ok (Subscript_expression expression : lhs_expression) + | "identifier" -> Ok (Identifier (dec_identifier ?comments node)) + | "undefined" -> + let* kwd_undefined = dec_kwd_undefined ?comments node in + let* () = check_first_error_child node in + Ok (Undefined kwd_undefined) + | "object_pattern" -> + let* pattern = dec_object_pattern ?comments node in + let* () = check_first_error_child node in + Ok (Pattern (Pattern_object pattern)) + | "array_pattern" -> + let* pattern = dec_array_pattern ?comments node in + let* () = check_first_error_child node in + Ok (Pattern (Pattern_array pattern)) + | "non_null_expression" -> + let* expression = dec_non_null_expression ?comments node in + let* () = check_first_error_child node in + Ok (Non_null_expression expression) + | _ -> mk_err Pattern node + +(* Non-null expression *) + +and dec_non_null_expression ?comments node : (expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Non_null_expression node + | _ -> + let* child = named_child_ranked 0 node ~err:Non_null_expression in + let* () = check_first_error_child node in + dec_expression ?comments child + +(* PRIMARY EXPRESSION *) + +and dec_primary_expression ?(comments = []) node : (primary_expression, _) result = + match get_name node with + | "subscript_expression" -> + let* expression = wrap dec_subscript_expression node in + let* () = check_first_error_child node in + Ok (E_subscript_expression expression) + | "member_expression" -> + let* expression = wrap dec_member_expression node in + let* () = check_first_error_child node in + Ok (E_member_expression expression) + | "parenthesized_expression" -> + let* expression = dec_parenthesized_expression node in + let* () = check_first_error_child node in + Ok (E_parenthesized_expression expression) + | "identifier" -> Ok (E_identifier (dec_identifier ~comments node)) + | "undefined" -> + let* kwd_undefined = dec_kwd_undefined node in + let* () = check_first_error_child node in + Ok (E_undefined kwd_undefined) + | "this" -> + let* kwd_this = dec_kwd_this node in + let* () = check_first_error_child node in + Ok (E_this kwd_this) + | "super" -> + let* kwd_super = dec_kwd_super node in + let* () = check_first_error_child node in + Ok (E_super kwd_super) + | "number" -> + let* number = dec_number ~comments node in + let* () = check_first_error_child node in + Ok (E_number number) + | "string" -> + let* () = check_first_error_child node in + Ok (E_string (dec_string node)) + | "template_string" -> + let* expression = wrap dec_template_string ~comments node in + let* () = check_first_error_child node in + Ok (E_template_string expression) + | "regex" -> + let* () = check_first_error_child node in + Ok (E_regex (dec_regex node)) + | "true" -> + let* kwd_true = dec_kwd_true node in + let* () = check_first_error_child node in + Ok (E_true kwd_true) + | "false" -> + let* kwd_false = dec_kwd_false node in + let* () = check_first_error_child node in + Ok (E_false kwd_false) + | "null" -> + let* kwd_null = dec_kwd_null node in + let* () = check_first_error_child node in + Ok (E_null kwd_null) + | "object" -> + let* expression = dec_object_expr node in + let* () = check_first_error_child node in + Ok (E_object expression) + | "array" -> + let* expression = dec_array node in + let* () = check_first_error_child node in + Ok (E_array expression) + | "function_expression" -> + let* expression = wrap dec_function_expression node in + let* () = check_first_error_child node in + Ok (E_function_expression expression) + | "arrow_function" -> + let* expression = wrap dec_arrow_function node in + let* () = check_first_error_child node in + Ok (E_arrow_function expression) + | "generator_function" -> + let* expression = wrap dec_generator_function node in + let* () = check_first_error_child node in + Ok (E_generator_function expression) + | "class" -> + let* expression = wrap dec_class node in + let* () = check_first_error_child node in + Ok (E_class expression) + | "meta_property" -> + let* expression = dec_meta_property node in + let* () = check_first_error_child node in + Ok (E_meta_property expression) + | "call_expression" -> + let* expression = dec_call_expression node in + let* () = check_first_error_child node in + Ok (E_call_expression expression) + | "non_null_expression" -> + let* expression = dec_non_null_expression node in + let* () = check_first_error_child node in + Ok (E_non_null_expression expression) + | _ -> mk_err Expression node + +(* Call expression *) + +and dec_call_expression ?(comments = []) node : (call_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Call_expression node + | _ -> + let* function_field = child_with_field "function" node ~err:Expression in + let member_selection = first_child_named_opt "?." node in + let type_arguments_field = child_with_field_opt "type_arguments" node in + let* type_arguments = make_opt_res dec_type_arguments type_arguments_field in + let* arguments_field = child_with_field "arguments" node ~err:Arguments in + (match member_selection with + | None -> + let* lambda = dec_fun_call ~comments function_field in + let* arguments = dec_arguments_to_call arguments_field in + let call = { lambda; type_arguments; arguments } in + let region = !get_region node in + let* () = check_first_error_child node in + Ok (Call (Wrap.make call region)) + | Some _ -> + let* lambda = dec_primary_expression ~comments function_field in + let* arguments = dec_arguments arguments_field in + let call = { lambda; type_arguments; arguments } in + let region = !get_region node in + let* () = check_first_error_child node in + Ok (Member (Wrap.make call region))) + +and dec_fun_call ?(comments = []) node : (fun_call, _) result = + match get_name node with + | "import" -> + let* kwd_import = dec_kwd_import ~comments node in + let* () = check_first_error_child node in + Ok (Import kwd_import) + | _ -> + let* expression = dec_expression ~comments node in + let* () = check_first_error_child node in + Ok (Fun_call expression) + +and dec_arguments_to_call node : (arguments_to_call, _) result = + match get_name node with + | "template_string" -> + let* expression = wrap dec_template_string node in + let* () = check_first_error_child node in + Ok (Template_string expression) + | _ -> + let* arguments = dec_arguments node in + let* () = check_first_error_child node in + Ok (Arguments arguments) + +(* Meta-property *) + +and dec_meta_property ?(comments = []) node : (meta_property, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Meta_property node + | _ -> + let region = !get_region node in + let* fst_child = child_ranked 0 node ~err:New_or_import in + let* snd_child = child_ranked 2 node ~err:Target_or_meta in + (match get_name fst_child with + | "new" -> + let* kwd_new = dec_kwd_new ~comments fst_child in + let* kwd_target = dec_kwd_target snd_child in + let meta = kwd_new, kwd_target in + let* () = check_first_error_child node in + Ok (Meta_new_target (Wrap.make meta region)) + | "import" -> + let* kwd_import = dec_kwd_import ~comments fst_child in + let* kwd_meta = dec_kwd_meta snd_child in + let meta = kwd_import, kwd_meta in + let* () = check_first_error_child node in + Ok (Meta_import_meta (Wrap.make meta region)) + | _ -> mk_err Meta_property fst_child) + +(* Class *) + +and dec_class ?(comments = []) node : (class_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Class_expression node + | _ -> + let comments = comments @ prev_comments node in + let decorators = children_named "decorator" node in + let* decorators = list_of_children dec_decorator decorators in + let* kwd_class = first_child_named "class" node ~err:Class in + let* kwd_class = dec_kwd_class ~comments kwd_class in + let name_field = child_with_field_opt "name" node in + let name = make_opt dec_identifier name_field in + let type_parameters_field = child_with_field_opt "type_parameters" node in + let* type_parameters = make_opt_res dec_type_parameters type_parameters_field in + let heritage_child = first_child_named_opt "class_heritage" node in + let* class_heritage = make_opt_res dec_class_heritage heritage_child in + let* body_field = child_with_field "body" node ~err:Class_body in + let* body = dec_class_body body_field in + let* () = check_first_error_child node in + Ok { decorators; kwd_class; name; type_parameters; class_heritage; body } + +(* Generator function *) + +and dec_generator_function ?(comments = []) node : (generator_function, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Generator_function node + | _ -> + let* fun_decl = wrap dec_function_expression ~comments node in + let* sym_asterisk = first_child_named "*" node ~err:Asterisk in + let* sym_asterisk = dec_sym_asterisk sym_asterisk in + let* () = check_first_error_child node in + Ok (sym_asterisk, fun_decl) + +(* Arrow function *) + +and dec_arrow_function ?(comments = []) node : (arrow_function, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Arrow_function node + | _ -> + let kwd_async = first_child_named_opt "async" node in + let* kwd_async = make_opt_res dec_kwd_async kwd_async in + let* sym_arrow = first_child_named "=>" node ~err:Arrow in + let* sym_arrow = dec_sym_arrow sym_arrow in + let* body_field = child_with_field "body" node ~err:Block_or_expression in + let* body = dec_function_body body_field in + let parameter_field = child_with_field_opt "parameter" node in + (match parameter_field with + | Some parameter_field -> + let parameters = Parameter (dec_identifier ~comments parameter_field) in + let* () = check_first_error_child node in + Ok { kwd_async; parameters; sym_arrow; body } + | None -> + let* signature = dec_call_signature ~comments node in + let parameters : parameters = Call_signature signature in + let* () = check_first_error_child node in + Ok { kwd_async; parameters; sym_arrow; body }) + +and dec_function_body ?(comments = []) node : (function_body, _) result = + match get_name node with + | "statement_block" -> + let* statement = dec_statement_block ~comments node in + let* () = check_first_error_child node in + Ok (Statement_block statement) + | _ -> + let* expression = dec_expression ~comments node in + let* () = check_first_error_child node in + Ok (Expression expression) + +(* Function (expression) *) + +and dec_function_expression ?(comments = []) node : (function_expression, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Function_expression node + | _ -> + let comments = comments @ prev_comments node in + let kwd_async = first_child_named_opt "async" node in + let async_comments, function_comments = + match kwd_async with + | None -> [], comments + | Some _ -> comments, [] + in + let* kwd_async = make_opt_res (dec_kwd_async ~comments:async_comments) kwd_async in + let* kwd_function = first_child_named "function" node ~err:Function in + let* kwd_function = dec_kwd_function ~comments:function_comments kwd_function in + let name_field = child_with_field_opt "name" node in + let name = make_opt dec_identifier name_field in + let* call_sig = dec_call_signature node in + let* body_field = child_with_field "body" node ~err:Block in + let* body = dec_statement_block body_field in + let* () = check_first_error_child node in + Ok { kwd_async; kwd_function; name; call_sig; body } + +(* Array (expression) *) + +and dec_array ?comments node : (array, _) result = + dec_list_in_brackets ?comments node dec_array_cell ~err:Array + +and dec_array_cell ?comments node = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Array_cell node + | "spread_element" -> + let* spread = wrap dec_spread_element node in + let* () = check_first_error_child node in + Ok (Spread_element spread) + | _ -> + (* Hidden rule: *) + let* expression = dec_expression ?comments node in + let* () = check_first_error_child node in + Ok (Expression expression : argument) + +(* Template strings *) + +and dec_template_string ?(comments = []) node : (template_string, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Template_string node + | _ -> + let* opening_bquote = child_ranked 0 node ~err:Backquote in + let* opening_bquote = dec_sym_backquote ~comments opening_bquote in + let named_children = collect_named_children node in + let fragments = List.map ~f:dec_template_string_fragment named_children in + let* fragments = Result.all fragments in + let* closing_bquote = last_child node ~err:Backquote in + let* closing_bquote = dec_sym_backquote closing_bquote in + let* () = check_first_error_child node in + Ok (opening_bquote, fragments, closing_bquote) + +and dec_template_string_fragment ?(comments = []) node + : (template_string_fragment, _) result + = + match get_name node with + | "string_fragment" -> + let* () = check_first_error_child node in + Ok (String_fragment (make_node ~comments node)) + | "escape_sequence" -> + let* () = check_first_error_child node in + Ok (Escape_sequence (make_node ~comments node)) + | "template_substitution" -> + let* () = check_first_error_child node in + Ok (Template_substitution (make_node ~comments node)) + | _ -> mk_err Template_string node + +(* PATTERN + + The JavaScript tree-sitter grammar has the non-terminal + "pattern" be a supertype, that is, a hidden rule. *) + +and dec_pattern ?(comments = []) node : (pattern, _) result = + match get_name node with + | "rest_pattern" -> + let* pattern = wrap dec_rest_pattern ~comments node in + let* () = check_first_error_child node in + Ok (P_rest_pattern pattern) + | _ -> + let* expression = dec_lhs_expression ~comments node in + let pattern = + match expression with + | Member_expression expression -> P_member_expression expression + | Subscript_expression expression -> P_subscript_expression expression + | Identifier identifier -> P_identifier identifier + | Undefined kwd_undefined -> P_undefined kwd_undefined + | Pattern pattern -> P_destructuring_pattern pattern + | Non_null_expression expression -> P_non_null_expression expression + in + let* () = check_first_error_child node in + Ok pattern + +(* Object pattern *) + +and dec_object_pattern ?comments node : (object_pattern, _) result = + dec_list_in_braces ?comments node dec_member_pattern ~err:Object_pattern + +and dec_member_pattern ?(comments = []) node : (member_pattern, _) result = + match get_name node with + | "pair_pattern" -> + let* pattern = dec_pair_pattern ~comments node in + let* () = check_first_error_child node in + Ok (Member_pair_pattern pattern) + | "rest_pattern" -> + let* pattern = wrap dec_rest_pattern ~comments node in + let* () = check_first_error_child node in + Ok (Member_rest_pattern pattern) + | "object_assignment_pattern" -> + let* pattern = wrap dec_object_assignment_pattern node in + let* () = check_first_error_child node in + Ok (Member_object_assignment pattern) + | "shorthand_property_identifier_pattern" -> + let* shorthand = dec_shorthand_property_identifier_pattern node in + let* () = check_first_error_child node in + Ok (Member_shorthand_property shorthand) + | _ -> mk_err Object_pattern_field node + +(* Pair pattern *) + +and dec_pair_pattern ?(comments = []) node : (pair_pattern wrap, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Pair_pattern node + | _ -> + let* key_field = child_with_field "key" node ~err:Property_name in + let* key = dec_property_name ~comments key_field in + let* sym_colon = first_child_named ":" node ~err:Colon in + let* sym_colon = dec_sym_colon sym_colon in + let* value_field = child_with_field "value" node ~err:Pattern in + let* value = dec_pair_value_pattern value_field in + let region = !get_region node in + let* () = check_first_error_child node in + Ok (Wrap.make { key; sym_colon; value } region) + +and dec_pair_value_pattern node : (pair_value_pattern, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Value_of_pair_pattern node + | "assignment_pattern" -> + let* pattern = wrap dec_assignment_pattern node in + let* () = check_first_error_child node in + Ok (Pair_value_assignment pattern) + | _ -> + (* Hidden rule *) + let* pattern = dec_pattern node in + let* () = check_first_error_child node in + Ok (Pair_value pattern) + +(* Rest pattern *) + +and dec_rest_pattern ?(comments = []) node : (rest_pattern, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Rest_pattern node + | _ -> + let* sym_ellipsis = first_child_named "..." node ~err:Ellipsis in + let* sym_ellipsis = dec_sym_ellipsis ~comments sym_ellipsis in + let* expr_child = named_child_ranked 0 node ~err:Expression in + let* expression = dec_lhs_expression expr_child in + let* () = check_first_error_child node in + Ok { sym_ellipsis; expression } + +(* Object assignment pattern *) + +and dec_object_assignment_pattern ?(comments = []) node + : (object_assignment_pattern, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Object_assignment_pattern node + | _ -> + let* left_field = child_with_field "left" node ~err:Pattern in + let* left = dec_object_lhs_pattern ~comments left_field in + let* sym_equal = first_child_named "=" node ~err:Equal in + let* sym_equal = dec_sym_equal sym_equal in + let* right_field = child_with_field "right" node ~err:Expression in + let* right = dec_expression right_field in + let* () = check_first_error_child node in + Ok ({ left; sym_equal; right } : object_assignment_pattern) + +and dec_object_lhs_pattern ?comments node : (object_lhs_pattern, _) result = + match get_name node with + | "shorthand_property_identifier_pattern" -> + let* shorthand = dec_shorthand_property_identifier_pattern ?comments node in + let* () = check_first_error_child node in + Ok (Decl_ident shorthand) + | _ -> + (* Hidden rule *) + let* pattern = dec_destructuring_pattern ?comments node in + let* () = check_first_error_child node in + Ok (Decl_pattern pattern) + +(* Shorthand property identifier pattern *) + +and dec_shorthand_property_identifier_pattern ?comments node : (identifier, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Identifier node + | _ -> + let* () = check_first_error_child node in + Ok (dec_identifier ?comments node) + +(* Array pattern *) + +and dec_array_pattern ?comments node : (array_pattern, _) result = + dec_list_in_brackets ?comments node dec_array_cell_pattern ~err:Array_pattern + +and dec_array_cell_pattern ?comments node : (array_cell_pattern, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Array_cell_pattern node + | "assignment_pattern" -> + let* pattern = dec_assignment_pattern ?comments node in + let region = !get_region node in + let* () = check_first_error_child node in + Ok (Cell_assignment (Wrap.make pattern region)) + | _ -> + (* hidden rule *) + let* pattern = dec_pattern ?comments node in + let* () = check_first_error_child node in + Ok (Cell_pattern pattern) + +(* Assignment pattern *) + +and dec_assignment_pattern ?(comments = []) node : (assignment_pattern, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Assignment_pattern node + | _ -> + let* left_field = child_with_field "left" node ~err:Pattern in + let* left = dec_pattern ~comments left_field in + let* sym_equal = first_child_named "=" node ~err:Equal in + let* sym_equal = dec_sym_equal sym_equal in + let* right_field = child_with_field "right" node ~err:Expression in + let* right = dec_expression right_field in + let* () = check_first_error_child node in + Ok { left; sym_equal; right } + +(* Rule "_destructuring_pattern" is inlined. *) + +and dec_destructuring_pattern ?comments node : (destructuring_pattern, _) result = + match get_name node with + | "object_pattern" -> + let* pattern = dec_object_pattern ?comments node in + let* () = check_first_error_child node in + Ok (Pattern_object pattern) + | "array_pattern" -> + let* pattern = dec_array_pattern ?comments node in + let* () = check_first_error_child node in + Ok (Pattern_array pattern) + | _ -> mk_err Object_or_array_pattern node + +(* TYPES + + The non-terminals "type" and "primary_type" are supertypes in the + TypeScript grammar, which means that they are hidden rules. *) + +and dec_type ?(comments = []) node : (type_expr, _) result = + match get_name node with + | "function_type" -> + let* type_expr = wrap dec_function_type ~comments node in + let* () = check_first_error_child node in + Ok (T_function_type type_expr) + | "readonly_type" -> + let* type_expr = wrap dec_readonly_type ~comments node in + let* () = check_first_error_child node in + Ok (T_readonly_type type_expr) + | "constructor_type" -> + let* type_expr = wrap dec_constructor_type ~comments node in + let* () = check_first_error_child node in + Ok (T_constructor_type type_expr) + | "infer_type" -> + let* type_expr = wrap dec_infer_type ~comments node in + let* () = check_first_error_child node in + Ok (T_infer_type type_expr) + (* A couple of aliases *) + | "member_expression" -> + let* expression = + wrap dec_type_query_member_expression_in_type_annotation ~comments node + in + let* () = check_first_error_child node in + Ok (T_member_expression expression) + | "call_expression" -> + let* expression = + wrap dec_type_query_call_expression_in_type_annotation ~comments node + in + let* () = check_first_error_child node in + Ok (T_call_expression expression) + (* "primary_type" is hidden *) + | _ -> + let* type_expr = dec_primary_type ~comments node in + let* () = check_first_error_child node in + Ok (T_primary_type type_expr) + +(* Type queries in type annotations (expressions) + NOTE: Rather mysterious. See ast.ml. *) + +and dec_type_query_member_expression_in_type_annotation ?(comments = []) node + : (type_query_member_expression_in_type_annotation, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Member_or_call node + | _ -> + let* object_field = child_with_field "object" node ~err:Member_or_call in + let* sym_dot = first_child_named "." node ~err:Dot in + let* property_field = child_with_field "property" node ~err:Property_identifier in + let dec_object_field node = + match get_name node with + | "import" -> + let* kwd_import = dec_kwd_import ~comments node in + Ok (Type_query_object_import kwd_import) + | "member_expression" -> + let* member = + wrap dec_type_query_member_expression_in_type_annotation ~comments node + in + Ok (Type_query_object_member member) + | "call_expression" -> + let* expression = + wrap dec_type_query_call_expression_in_type_annotation ~comments node + in + Ok (Type_query_object_call expression) + | _ -> mk_err Object_field node + in + let* object_expr = dec_object_field object_field in + let* selector = dec_sym_dot sym_dot in + let* property = dec_type_query_property property_field in + let* () = check_first_error_child node in + Ok + ({ object_expr; selector; property } + : type_query_member_expression_in_type_annotation) + +and dec_type_query_call_expression_in_type_annotation ?(comments = []) node + : (type_query_call_expression_in_type_annotation, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Member_expression node + | _ -> + let* function_field = child_with_field "function" node ~err:Member_expression in + let* arguments_field = child_with_field "arguments" node ~err:Arguments in + let* lambda = dec_type_query_call_lambda ~comments function_field in + let* arguments = dec_arguments arguments_field in + let* () = check_first_error_child node in + Ok ({ lambda; arguments } : type_query_call_expression_in_type_annotation) + +and dec_type_query_call_lambda ?(comments = []) node : (type_query_call_lambda, _) result = + match get_name node with + | "import" -> + let* kwd_import = dec_kwd_import ~comments node in + let* () = check_first_error_child node in + Ok (Type_query_call_import kwd_import : type_query_call_lambda) + | "member_expression" -> + let* expression = + dec_type_query_member_expression_in_type_annotation ~comments node + in + let* () = check_first_error_child node in + Ok (Type_query_call_member expression) + | _ -> mk_err Member_expression node + +(* Primary type *) + +and dec_primary_type ?(comments = []) node : (primary_type, _) result = + match get_name node with + | "parenthesized_type" -> + let* type_expr = dec_parenthesized_type ~comments node in + let* () = check_first_error_child node in + Ok (T_parenthesized_type type_expr) + | "predefined_type" -> + let* type_expr = dec_predefined_type ~comments node in + let* () = check_first_error_child node in + Ok (T_predefined_type type_expr) + | "type_identifier" -> + let identifier = dec_type_identifier ~comments node in + let* () = check_first_error_child node in + Ok (T_type_identifier identifier) + | "nested_type_identifier" -> + let* nested_id = wrap dec_nested_type_identifier ~comments node in + let* () = check_first_error_child node in + Ok (T_nested_type_identifier nested_id) + | "generic_type" -> + let* type_expr = wrap dec_generic_type ~comments node in + let* () = check_first_error_child node in + Ok (T_generic_type type_expr) + | "object_type" -> + let* type_expr = dec_object_type ~comments node in + let* () = check_first_error_child node in + Ok (T_object_type type_expr) + | "array_type" -> + let* type_expr = wrap dec_array_type ~comments node in + let* () = check_first_error_child node in + Ok (T_array_type type_expr) + | "tuple_type" -> + let* type_expr = dec_tuple_type ~comments node in + let* () = check_first_error_child node in + Ok (T_tuple_type type_expr) + | "flow_maybe_type" -> + let* type_expr = wrap dec_flow_maybe_type ~comments node in + let* () = check_first_error_child node in + Ok (T_flow_maybe_type type_expr) + | "type_query" -> + let* type_query = wrap dec_type_query ~comments node in + let* () = check_first_error_child node in + Ok (T_type_query type_query) + | "index_type_query" -> + let* type_expr = wrap dec_index_type_query ~comments node in + let* () = check_first_error_child node in + Ok (T_index_type_query type_expr) + | "this_type" -> + let* kwd_this = dec_kwd_this ~comments node in + let* () = check_first_error_child node in + Ok (T_this kwd_this) + | "existential_type" -> + let* sym_asterisk = dec_existential_type ~comments node in + let* () = check_first_error_child node in + Ok (T_existential_type sym_asterisk) + | "literal_type" -> + let* type_expr = dec_literal_type ~comments node in + let* () = check_first_error_child node in + Ok (T_literal_type type_expr) + | "lookup_type" -> + let* type_expr = wrap dec_lookup_type ~comments node in + let* () = check_first_error_child node in + Ok (T_lookup_type type_expr) + | "conditional_type" -> + let* type_expr = wrap dec_conditional_type ~comments node in + let* () = check_first_error_child node in + Ok (T_conditional_type type_expr) + | "template_literal_type" -> + let* type_expr = wrap dec_template_literal_type ~comments node in + let* () = check_first_error_child node in + Ok (T_template_literal_type type_expr) + | "intersection_type" -> + let* type_expr = wrap dec_intersection_type ~comments node in + let* () = check_first_error_child node in + Ok (T_intersection_type type_expr) + | "union_type" -> + let* type_expr = wrap dec_union_type ~comments node in + let* () = check_first_error_child node in + Ok (T_union_type type_expr) + | _ -> mk_err Type_expression node + +(* Existential type *) + +and dec_existential_type ?(comments = []) node : (sym_asterisk, _) result = + let* sym_asterisk = first_child_named "*" node ~err:Asterisk in + let* () = check_first_error_child node in + dec_sym_asterisk ~comments sym_asterisk + +(* Union type *) + +and dec_union_type ?(comments = []) node : (union_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Union_type node + | _ -> + let comments = comments @ prev_comments node in + let* first_child = child_ranked 0 node ~err:Type_or_disjunction in + let* sym_vbar = first_child_named "|" node ~err:Vertical_bar in + (match get_name first_child with + | "|" -> + let* sym_vbar = dec_sym_vbar ~comments sym_vbar in + let* single_type_node = child_ranked 1 node ~err:Type_expression in + let* type_expr = dec_type single_type_node in + let* () = check_first_error_child node in + Ok (None, sym_vbar, type_expr) + | _ -> + (* "type" is a supertype, therefore a hidden rule *) + let* left_type = dec_type ~comments first_child in + let* sym_vbar = dec_sym_vbar sym_vbar in + let* right_type = child_ranked 2 node ~err:Type_expression in + let* right_type = dec_type right_type in + let* () = check_first_error_child node in + Ok (Some left_type, sym_vbar, right_type)) + +(* Intersection type *) + +and dec_intersection_type ?(comments = []) node : (intersection_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Intersection_type node + | _ -> + let* first_child = child_ranked 0 node ~err:Type_or_conjunction in + let* sym_and = first_child_named "&" node ~err:And in + (match get_name first_child with + | "&" -> + let* sym_and = dec_sym_and ~comments sym_and in + let* single_type_node = child_ranked 1 node ~err:Type_expression in + let* type_expr = dec_type single_type_node in + let* () = check_first_error_child node in + Ok (None, sym_and, type_expr) + | _ -> + (* "type" is a supertype, therefore a hidden rule *) + let* left_type = dec_type ~comments first_child in + let* sym_and = dec_sym_and sym_and in + let* right_type = child_ranked 2 node ~err:Type_expression in + let* right_type = dec_type right_type in + let* () = check_first_error_child node in + Ok (Some left_type, sym_and, right_type)) + +(* Template literal type *) + +and dec_template_literal_type ?(comments = []) node : (template_literal_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Template_literal_type node + | _ -> + let* opening_bquote = child_ranked 0 node ~err:Backquote in + let* opening_bquote = dec_sym_backquote ~comments opening_bquote in + let named_children = collect_named_children node in + let fragments = List.map ~f:dec_template_type_fragment named_children in + let* fragments = Result.all fragments in + let* closing_bquote = last_child node ~err:Backquote in + let* closing_bquote = dec_sym_backquote closing_bquote in + let* () = check_first_error_child node in + Ok (opening_bquote, fragments, closing_bquote) + +and dec_template_type_fragment ?(comments = []) node : (template_type_fragment, _) result = + match get_name node with + | "string_fragment" -> + let* () = check_first_error_child node in + Ok (Template_type_string (dec_string ~comments node)) + | "template_type" -> + let* type_expr = dec_template_type ~comments node in + let* () = check_first_error_child node in + Ok (Template_type type_expr) + | _ -> mk_err String_or_type node + +and dec_template_type ?(comments = []) node : (template_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Template_literal_type node + | _ -> + let* type_node = child_ranked 1 node ~err:Type_expression in + (match get_name type_node with + | "infer_type" -> + let* type_expr = wrap dec_infer_type ~comments type_node in + let* () = check_first_error_child node in + Ok (Template_type_infer type_expr) + (* "primary_type" is hidden *) + | _ -> + let* type_expr = dec_primary_type ~comments type_node in + let* () = check_first_error_child node in + Ok (Template_type_primary type_expr)) + +(* Conditional type *) + +and dec_conditional_type ?(comments = []) node : (conditional_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Conditional_type node + | _ -> + let* left_field = child_with_field "left" node ~err:Type in + let* left = dec_type ~comments left_field in + let* kwd_extends = first_child_named "extends" node ~err:Extends in + let* kwd_extends = dec_kwd_extends kwd_extends in + let* right_field = child_with_field "right" node ~err:Type_expression in + let* right = dec_type right_field in + let* sym_qmark = first_child_named "?" node ~err:Question_mark in + let* sym_qmark = dec_sym_qmark sym_qmark in + let* consequence_field = child_with_field "consequence" node ~err:Type_expression in + let* consequence = dec_type consequence_field in + let* sym_colon = first_child_named ":" node ~err:Colon in + let* sym_colon = dec_sym_colon sym_colon in + let* alternative_field = child_with_field "alternative" node ~err:Type_expression in + let* alternative = dec_type alternative_field in + let* () = check_first_error_child node in + Ok { left; kwd_extends; right; sym_qmark; consequence; sym_colon; alternative } + +(* Look up type + + The non-terminals "type" and "primary_type" are supertypes in the + TypeScript grammar, which means that they are hidden rules. *) + +and dec_lookup_type ?(comments = []) node : (lookup_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Lookup_type node + | _ -> + let* primary_type_child = named_child_ranked 0 node ~err:Type_expression in + let* primary_type = dec_primary_type ~comments primary_type_child in + let* sym_lbracket = first_child_named "[" node ~err:Left_bracket in + let* opening = dec_sym_lbracket sym_lbracket in + let* type_child = next_sibling sym_lbracket ~err:Type_expression in + let* contents = dec_type type_child in + let* sym_rbracket = first_child_named "]" node ~err:Right_bracket in + let* closing = dec_sym_rbracket sym_rbracket in + let region = !get_region node in + let brackets = { opening; contents; closing } in + let index_type = Brackets (Wrap.make brackets region) in + let* () = check_first_error_child node in + Ok (primary_type, index_type) + +(* Literal type *) + +and dec_literal_type ?(comments = []) node : (literal_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Literal_type node + | _ -> + let* child = named_child_ranked 0 node ~err:Literal_type in + (match get_name child with + | "unary_expression" -> + let* expression = wrap dec_unary_expression ~comments child in + let* () = check_first_error_child node in + Ok (T_unary_type expression) + | "number" -> + let* number = dec_number ~comments child in + let* () = check_first_error_child node in + Ok (T_number number : literal_type) + | "string" -> + let* () = check_first_error_child node in + Ok (T_string (dec_string ~comments child) : literal_type) + | "true" -> + let* kwd_true = dec_kwd_true ~comments child in + let* () = check_first_error_child node in + Ok (T_true kwd_true) + | "false" -> + let* kwd_false = dec_kwd_false ~comments child in + let* () = check_first_error_child node in + Ok (T_false kwd_false) + | "null" -> + let* kwd_null = dec_kwd_null ~comments child in + let* () = check_first_error_child node in + Ok (T_null kwd_null) + | "undefined" -> + let* kwd_undefined = dec_kwd_undefined ~comments child in + let* () = check_first_error_child node in + Ok (T_undefined kwd_undefined) + | _ -> mk_err Literal_type node) + +(* Index type query *) + +and dec_index_type_query ?(comments = []) node : (kwd_keyof * primary_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Index_type_query node + | _ -> + let* kwd_keyof = first_child_named "keyof" node ~err:Keyof in + let* kwd_keyof = dec_kwd_keyof ~comments kwd_keyof in + let* type_node = child_ranked 1 node ~err:Type_expression in + let* primary_type = dec_primary_type type_node in + let* () = check_first_error_child node in + Ok (kwd_keyof, primary_type) + +(* Type query *) + +and dec_type_query ?(comments = []) node : (kwd_keyof * type_query, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Type_query node + | _ -> + let* kwd_typeof = first_child_named "typeof" node ~err:Keyof in + let* kwd_typeof = dec_kwd_typeof ~comments kwd_typeof in + let* child = child_ranked 1 node ~err:Type_query in + let* type_query = + match get_name child with + | "subscript_expression" -> + let* expression = dec_type_query_subscript_expression child in + Ok (Typeof_subscript_expression expression) + | "member_expression" -> + let* expression = dec_type_query_member_expression child in + Ok (Typeof_member_expression expression) + | "call_expression" -> + let* expression = dec_type_query_call_expression child in + Ok (Typeof_call_expression expression) + | "instantiation_expression" -> + let* expression = dec_type_query_instantiation_expression child in + Ok (Typeof_instantiation_expression expression) + | "identifier" -> Ok (Typeof_identifier (dec_identifier child)) + | "this" -> + let* kwd_this = dec_kwd_this child in + Ok (Typeof_this kwd_this) + | _ -> mk_err Type_query node + in + let* () = check_first_error_child node in + Ok (kwd_typeof, type_query) + +and dec_type_query_subscript_expression ?(comments = []) node + : (type_query_subscript_expression, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Type_query_subscript node + | _ -> + let* object_field = child_with_field "object" node ~err:Object_denotation in + let* object_expr = dec_type_query_object ~comments object_field in + let optional_chain = first_child_named_opt "?." node in + let* optional = make_opt_res dec_sym_optional_chain optional_chain in + let* sym_lbracket = first_child_named "[" node ~err:Left_bracket in + let* opening = dec_sym_lbracket sym_lbracket in + let* index_field = child_with_field "index" node ~err:Type_or_string_or_number in + let* contents = dec_type_query_index index_field in + let* sym_rbracket = first_child_named "]" node ~err:Right_bracket in + let* closing = dec_sym_rbracket sym_rbracket in + let region = !get_region node in + let brackets = { opening; contents; closing } in + let index = Brackets (Wrap.make brackets region) in + let* () = check_first_error_child node in + Ok { object_expr; optional; index } + +and dec_type_query_object ?(comments = []) node : (type_query_object, _) result = + match get_name node with + | "identifier" -> + let* () = check_first_error_child node in + Ok (Type_query_object_identifier (dec_identifier ~comments node)) + | "this" -> + let* kwd_this = dec_kwd_this ~comments node in + let* () = check_first_error_child node in + Ok (Type_query_object_this kwd_this) + | "subscript_expression" -> + let* expression = dec_type_query_subscript_expression ~comments node in + let* () = check_first_error_child node in + Ok (Type_query_object_subscript_expression expression) + | "member_expression" -> + let* expression = dec_type_query_member_expression ~comments node in + let* () = check_first_error_child node in + Ok (Type_query_object_member_expression expression) + | "call_expression" -> + let* expression = dec_type_query_call_expression ~comments node in + let* () = check_first_error_child node in + Ok (Type_query_object_call_expression expression) + | _ -> mk_err Object_denotation node + +and dec_type_query_index node : (type_query_index, _) result = + match get_name node with + | "predefined_type" -> + let* type_expr = dec_predefined_type node in + let* () = check_first_error_child node in + Ok (Type_query_index_predefined_type type_expr) + | "string" -> + let* () = check_first_error_child node in + Ok (Type_query_index_string (dec_string node)) + | "number" -> + let* number = dec_number node in + let* () = check_first_error_child node in + Ok (Type_query_index_number number) + | _ -> mk_err Predefined_type node + +and dec_type_query_member_expression ?(comments = []) node + : (type_query_member_expression, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Type_query_member node + | _ -> + let* object_field = child_with_field "object" node ~err:Object_denotation in + let* object_expr = dec_type_query_object ~comments object_field in + let* property_field = child_with_field "property" node ~err:Property_identifier in + let* property = dec_type_query_property property_field in + let* selector = prev_sibling property_field ~err:Selector_or_optional_chain in + let* selector = dec_query_selector selector in + let* () = check_first_error_child node in + Ok { object_expr; selector; property } + +and dec_query_selector node : (query_selector, _) result = + match get_name node with + | "." -> + let* sym_dot = dec_sym_dot node in + let* () = check_first_error_child node in + Ok (Query_selector_dot sym_dot) + | "?." -> + let* sym_optional_chain = dec_sym_optional_chain node in + let* () = check_first_error_child node in + Ok (Query_selector_opt_chain sym_optional_chain) + | _ -> mk_err Selector_or_optional_chain node + +and dec_type_query_property node : (type_query_property, _) result = + match get_name node with + | "private_property_identifier" -> + let* () = check_first_error_child node in + Ok (Type_query_property_private (dec_private_property_identifier node)) + | "property_identifier" -> + let* () = check_first_error_child node in + Ok (Type_query_property_identifier (dec_identifier node)) + | _ -> mk_err Property_identifier node + +and dec_type_query_call_expression ?(comments = []) node + : (type_query_call_expression, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Type_query_call node + | _ -> + let* function_field = child_with_field "function" node ~err:Function_denotation in + let* lambda = dec_type_query_call_function ~comments function_field in + let* arguments_field = child_with_field "arguments" node ~err:Arguments in + let* arguments = dec_type_query_call_arguments arguments_field in + let* () = check_first_error_child node in + Ok ({ lambda; arguments } : type_query_call_expression) + +and dec_type_query_call_function ?(comments = []) node + : (type_query_call_function, _) result + = + match get_name node with + | "import" -> + let* kwd_import = dec_kwd_export ~comments node in + let* () = check_first_error_child node in + Ok (Type_query_call_import kwd_import) + | "identifier" -> Ok (Type_query_call_identifier (dec_identifier ~comments node)) + | "member_expression" -> + let* expression = dec_type_query_member_expression ~comments node in + let* () = check_first_error_child node in + Ok (Type_query_call_member_expression expression) + | "subscript_expression" -> + let* expression = dec_type_query_subscript_expression ~comments node in + let* () = check_first_error_child node in + Ok (Type_query_call_subscript_expression expression) + | _ -> mk_err Function_denotation node + +and dec_type_query_call_arguments node : (type_query_call_arguments, _) result = + dec_arguments node + +and dec_type_query_instantiation_expression ?(comments = []) node + : (type_query_instantiation_expression, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Type_query_instantiation node + | _ -> + let* function_field = child_with_field "function" node ~err:Function_denotation in + let* lambda = dec_type_query_call_function ~comments function_field in + let* type_arguments_field = + child_with_field "type_arguments" node ~err:Type_arguments + in + let* type_arguments = dec_type_arguments type_arguments_field in + let* () = check_first_error_child node in + Ok { lambda; type_arguments } + +(* Flow maybe type + + flow_maybe_type: $ => prec.right(seq('?', $.primary_type)) + *) + +and dec_flow_maybe_type ?(comments = []) node : (sym_qmark * primary_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Type node + | _ -> + let* sym_qmark = child_ranked 0 node ~err:Question_mark in + let* sym_qmark = dec_sym_qmark ~comments sym_qmark in + let* type_node = child_ranked 1 node ~err:Type_expression in + let* primary_type = dec_primary_type type_node in + let* () = check_first_error_child node in + Ok (sym_qmark, primary_type) + +(* Tuple type *) + +and dec_tuple_type ?comments node : (tuple_type, _) result = + dec_list_in_brackets ?comments node dec_tuple_type_member ~err:Tuple_type + +and dec_tuple_type_member ?(comments = []) node : (tuple_type_member, _) result = + match get_name node with + | "required_parameter" -> + (* Alias *) + let* parameter = wrap dec_tuple_parameter ~comments node in + let* () = check_first_error_child node in + Ok (Tuple_parameter parameter) + | "optional_parameter" -> + (* Alias *) + let* parameter = wrap dec_optional_tuple_parameter ~comments node in + let* () = check_first_error_child node in + Ok (Tuple_optional_parameter parameter) + | "optional_type" -> + let* opt_type = wrap dec_optional_type ~comments node in + let* () = check_first_error_child node in + Ok (Tuple_optional_type opt_type) + | "rest_type" -> + let* type_expr = wrap dec_rest_type ~comments node in + let* () = check_first_error_child node in + Ok (Tuple_rest_type type_expr) + | _ -> + (* "type" is a hidden rule *) + let* type_expr = dec_type ~comments node in + let* () = check_first_error_child node in + Ok (Tuple_type type_expr) + +and dec_tuple_parameter ?(comments = []) node : (tuple_parameter, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Tuple_parameter node + | _ -> + let* name_field = child_with_field "name" node ~err:Identifier_or_rest in + let* name = dec_tuple_parameter_name ~comments name_field in + let* type_field = child_with_field "type" node ~err:Type_annotation in + let* annotation = dec_type_annotation type_field in + let* () = check_first_error_child node in + Ok (name, annotation) + +and dec_tuple_parameter_name ?(comments = []) node : (tuple_parameter_name, _) result = + match get_name node with + | "identifier" -> + let* () = check_first_error_child node in + Ok (Tuple_parameter_ident (dec_identifier ~comments node)) + | "rest_pattern" -> + let* pattern = wrap dec_rest_pattern ~comments node in + let* () = check_first_error_child node in + Ok (Tuple_parameter_rest pattern) + | _ -> mk_err Identifier_or_rest node + +and dec_optional_tuple_parameter ?(comments = []) node + : (optional_tuple_parameter, _) result + = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Optional_tuple_parameter node + | _ -> + let* name_field = child_with_field "name" node ~err:Identifier in + let name = dec_identifier ~comments name_field in + let* sym_qmark = first_child_named "?" node ~err:Question_mark in + let* sym_qmark = dec_sym_qmark sym_qmark in + let* type_field = child_with_field "type" node ~err:Type_annotation in + let* annotation = dec_type_annotation type_field in + let* () = check_first_error_child node in + Ok (name, sym_qmark, annotation) + +(* Optional type *) + +and dec_optional_type ?(comments = []) node : (type_expr * sym_qmark, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Optional_type node + | _ -> + let* type_node = child_ranked 0 node ~err:Optional_type in + let* type_expr = dec_type ~comments type_node in + let* sym_qmark = child_ranked 1 node ~err:Question_mark in + let* sym_qmark = dec_sym_qmark sym_qmark in + let* () = check_first_error_child node in + Ok (type_expr, sym_qmark) + +(* Rest type *) + +and dec_rest_type ?(comments = []) node : (sym_ellipsis * type_expr, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Rest_type node + | _ -> + let* sym_ellipsis = first_child_named "..." node ~err:Ellipsis in + let* sym_ellipsis = dec_sym_ellipsis ~comments sym_ellipsis in + let* type_child = named_child_ranked 0 node ~err:Type_expression in + let* type_expr = dec_type type_child in + let* () = check_first_error_child node in + Ok (sym_ellipsis, type_expr) + +(* Array type *) + +and dec_array_type ?(comments = []) node : (array_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Array_type node + | _ -> + let* type_child = child_ranked 0 node ~err:Type_expression in + let* type_expr = dec_primary_type ~comments type_child in + let* sym_lbracket = first_child_named "[" node ~err:Left_bracket in + let* sym_lbracket = dec_sym_lbracket sym_lbracket in + let* sym_rbracket = first_child_named "]" node ~err:Right_bracket in + let* sym_rbracket = dec_sym_rbracket sym_rbracket in + let* () = check_first_error_child node in + Ok (type_expr, sym_lbracket, sym_rbracket) + +(* Object type *) + +and dec_object_type ?(comments = []) node : (object_type, _) result = + dec_list_in_braces ~comments node dec_member_type ~err:Object_type + +and dec_member_type ?(comments = []) node : (member_type, _) result = + match get_name node with + | "export_statement" -> + let* statement = wrap dec_export_statement ~comments node in + let* () = check_first_error_child node in + Ok (Export_statement statement) + | "property_signature" -> + let* signature = wrap dec_property_signature ~comments node in + let* () = check_first_error_child node in + Ok (Property_signature signature) + | "call_signature" -> + let* signature = dec_call_signature ~comments node in + let* () = check_first_error_child node in + Ok (Call_signature signature) + | "construct_signature" -> + let* signature = wrap dec_construct_signature ~comments node in + let* () = check_first_error_child node in + Ok (Construct_signature signature) + | "index_signature" -> + let* signature = wrap dec_index_signature ~comments node in + let* () = check_first_error_child node in + Ok (Index_signature signature) + | "method_signature" -> + let* signature = wrap dec_method_signature ~comments node in + let* () = check_first_error_child node in + Ok (Method_signature signature) + | _ -> mk_err Object_type_field node + +(* Property signature *) + +and dec_property_signature ?(comments = []) node : (property_signature, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Property_signature node + | _ -> + let comments = comments @ prev_comments node in + let accessibility_modifier = first_child_named_opt "accessibility_modifier" node in + let* access = make_opt_res dec_accessibility_modifier accessibility_modifier in + let* scope = dec_method_scope node in + let* name_field = child_with_field "name" node ~err:Identifier in + let* name = dec_property_name ~comments name_field in + let sym_qmark = first_child_named_opt "?" node in + let* sym_qmark = make_opt_res dec_sym_qmark sym_qmark in + let type_field = child_with_field_opt "type" node in + let* type_ = make_opt_res dec_type_annotation type_field in + let* () = check_first_error_child node in + Ok { access; scope; name; sym_qmark; type_ } + +(* Construct signature *) + +and dec_construct_signature ?(comments = []) node : (construct_signature, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Construct_signature node + | _ -> + let kwd_abstract = first_child_named_opt "abstract" node in + let* kwd_abstract = make_opt_res dec_kwd_abstract kwd_abstract in + let* kwd_new = first_child_named "new" node ~err:New in + let* kwd_new = dec_kwd_new ~comments kwd_new in + let type_parameters_field = child_with_field_opt "type_parameters" node in + let* type_parameters = make_opt_res dec_type_parameters type_parameters_field in + let* parameters_field = child_with_field "parameters" node ~err:Parameters in + let* parameters = dec_formal_parameters parameters_field in + let type_field = child_with_field_opt "type" node in + let* type_ = make_opt_res dec_type_annotation type_field in + let* () = check_first_error_child node in + Ok { kwd_abstract; kwd_new; type_parameters; parameters; type_ } + +(* Parenthesized type *) + +and dec_parenthesized_type ?comments node : (type_expr parens, _) result = + dec_parens ?comments node dec_type ~err:Parenthesized_type + +(* Infer type *) + +and dec_infer_type ?(comments = []) node : (infer_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Infer node + | _ -> + let* kwd_infer = first_child_named "infer" node ~err:Infer in + let* kwd_infer = dec_kwd_infer ~comments kwd_infer in + let* type_identifier_child = + child_ranked 1 node ~err:Identifier (* name "type_identifier"? *) + in + let type_id = dec_type_identifier type_identifier_child in + let* extends = + match first_child_named_opt "extends" node with + | None -> Ok None + | Some kwd_extends -> + let* kwd_extends = dec_kwd_extends kwd_extends in + let* type_child = child_ranked 3 node ~err:Type_expression in + let* type_expr = dec_type type_child in + Ok (Some (kwd_extends, type_expr)) + in + let* () = check_first_error_child node in + Ok { kwd_infer; type_id; extends } + +(* Constructor type *) + +and dec_constructor_type ?(comments = []) node : (constructor_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Constructor_type node + | _ -> + let kwd_abstract = first_child_named_opt "abstract" node in + let* kwd_abstract = make_opt_res dec_kwd_abstract kwd_abstract in + let* kwd_new = first_child_named "new" node ~err:New in + let* kwd_new = dec_kwd_new kwd_new in + let type_parameters_field = child_with_field_opt "type_parameters" node in + let* type_parameters = make_opt_res dec_type_parameters type_parameters_field in + let* parameters_field = child_with_field "parameters" node ~err:Parameters in + let* parameters = dec_formal_parameters ~comments parameters_field in + let* sym_arrow = first_child_named "=>" node ~err:Arrow in + let* sym_arrow = dec_sym_arrow sym_arrow in + let* type_field = child_with_field "type" node ~err:Type_expression in + let* type_expr = dec_type type_field in + let* () = check_first_error_child node in + Ok { kwd_abstract; kwd_new; type_parameters; parameters; sym_arrow; type_expr } + +(* Function type *) + +and dec_function_type ?(comments = []) node : (function_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Function_type node + | _ -> + let type_parameters_field = child_with_field_opt "type_parameters" node in + let* type_parameters = make_opt_res dec_type_parameters type_parameters_field in + let* parameters_field = child_with_field "parameters" node ~err:Parameters in + let* parameters = dec_formal_parameters ~comments parameters_field in + let* sym_arrow = first_child_named "=>" node ~err:Arrow in + let* sym_arrow = dec_sym_arrow sym_arrow in + let* return_type_field = child_with_field "return_type" node ~err:Type_expression in + let* return_type = dec_return_type return_type_field in + let* () = check_first_error_child node in + Ok { type_parameters; parameters; sym_arrow; return_type } + +and dec_return_type node : (return_type, _) result = + match get_name node with + | "asserts" -> + let* annotation = dec_asserts node in + let* () = check_first_error_child node in + Ok (Return_asserts annotation) + | "type_predicate" -> + let* predicate = wrap dec_type_predicate node in + let* () = check_first_error_child node in + Ok (Return_type_predicate predicate) + | _ -> + let* type_expr = dec_type node in + let* () = check_first_error_child node in + Ok (Return_type type_expr) + +(* Readonly type *) + +and dec_readonly_type ?(comments = []) node : (readonly_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Readonly_type node + | _ -> + let* kwd_readonly = first_child_named "readonly" node ~err:Readonly in + let* kwd_readonly = dec_kwd_readonly ~comments kwd_readonly in + let* type_child = child_ranked 1 node ~err:Type_expression in + let* type_expr = dec_type type_child in + let* () = check_first_error_child node in + Ok (kwd_readonly, type_expr) + +(* Generic type *) + +and dec_generic_type ?(comments = []) node : (generic_type, _) result = + match get_name node with + | "ERROR" | "MISSING" | "NULL" -> mk_err Generic_type node + | _ -> + let comments = comments @ prev_comments node in + let* name_field = child_with_field "name" node ~err:Type_identifier_or_path in + let* generic_name = dec_generic_name ~comments name_field in + let* type_arguments_field = + child_with_field "type_arguments" node ~err:Type_arguments + in + let* type_arguments = dec_type_arguments type_arguments_field in + let* () = check_first_error_child node in + Ok (generic_name, type_arguments) + +and dec_generic_name ?(comments = []) node : (generic_name, _) result = + match get_name node with + | "type_identifier" -> + let identifier = dec_type_identifier ~comments node in + let* () = check_first_error_child node in + Ok (Generic_type identifier) + | "nested_type_identifier" -> + let* nested = wrap dec_nested_type_identifier ~comments node in + let* () = check_first_error_child node in + Ok (Generic_nested nested) + | _ -> mk_err Type_identifier_or_path node + +(* Decoding the CST *) + +let dec_program ?(debug_arg = false) ~filename ~file (map : Loc_map.t) node + : (Ast.t, _) result + = + (* Setting up the extraction of source regions *) + let () = get_region := Ts_wrap.get_region filename map in + (* Setting the input as a top-level string buffer *) + let () = Buffer.reset !input in + let () = Buffer.add_string !input file in + (* Setting debug mode *) + let () = debug := debug_arg in + (* Decoding the CST into an AST *) + dec_statements node + +let dec_standalone_expression ~file map node : (Ast.expression, _) result = + (* Setting up the extraction of source regions *) + let () = get_region := Ts_wrap.get_region "" map in + (* Setting the input as a top-level string buffer *) + let () = Buffer.reset !input in + let () = Buffer.add_string !input file in + (* Decoding the CST into an AST *) + let* ast = dec_statements node in + match ast with + | None -> Decode_err.(make (Region.min ~file:"") No_single_expression) + | Some stmts -> + (match stmts#payload with + | _ :: stmt2 :: _ -> + Decode_err.(make (region_of_statement stmt2) No_single_expression) + | Nonempty_list.[ stmt ] -> + (match stmt with + | S_expression_statement expr_stmt -> + (match expr_stmt#payload with + | _ :: expr2 :: _ -> + Decode_err.(make (region_of_expression expr2) No_single_expression) + | Nonempty_list.[ expr ] -> Ok expr) + | _ -> Decode_err.(make (region_of_statement stmt) No_single_expression))) + +let dec_standalone_type_expr map node : (Ast.type_expr, _) result = + (* Setting up the extraction of source regions *) + let () = get_region := Ts_wrap.get_region "" map in + (* Decoding the CST into an AST *) + let* ast = dec_statements node in + match ast with + | None -> Decode_err.(make (Region.min ~file:"") No_single_type_expr) + | Some stmts -> + (match stmts#payload with + | _ :: stmt2 :: _ -> Decode_err.(make (region_of_statement stmt2) No_single_type_expr) + | Nonempty_list.[ stmt ] -> + (match stmt with + | S_declaration_statement (D_type_alias_declaration decl) -> + let Ast.{ kwd_type = _; name = _; type_parameters = _; sym_equal = _; type_expr } = + decl#payload + in + Ok type_expr + | _ -> Decode_err.(make (region_of_statement stmt) No_single_type_expr))) diff --git a/lib/typescript_ast/decode.mli b/lib/typescript_ast/decode.mli new file mode 100644 index 0000000000..e07cb68373 --- /dev/null +++ b/lib/typescript_ast/decode.mli @@ -0,0 +1,37 @@ +module Region = Simple_utils.Region +module Loc_map = Typescript_ast.Loc_map +module Ts_wrap = Typescript_ast.Ts_wrap +module Ast = Typescript_ast.Ast + +(* Decoding an input TypeScript program into an AST *) + +val dec_program + : ?debug_arg:bool + -> filename:string + -> file:string + -> Loc_map.t + -> Ts_wrap.ts_tree + -> (Ast.t, string Region.reg) result + +(* The parameter [node] is the root of a Typescript CST, *not of an + expression*. That's why we have to find the expression below the + root. This is because tree-sitter does not provide the generated + parsers with multiple entry-points. *) + +val dec_standalone_expression + : file:string + -> Loc_map.t + -> Ts_wrap.ts_tree + -> (Ast.expression, string Region.reg) result + +(* The parameter [node] is the root of a Typescript CST, *not of a + type expression*. tree-sitter does not provide the generated + parsers with multiple entry-points, so, in order to parse a type + expression, we assume that the input string starts with "type t = ", + so we fetch the type in the produced CST (last child of the root, + which is an type_alias_declaration). *) + +val dec_standalone_type_expr + : Loc_map.t + -> Ts_wrap.ts_tree + -> (Ast.type_expr, string Region.reg) result diff --git a/lib/typescript_ast/decode_err.ml b/lib/typescript_ast/decode_err.ml new file mode 100644 index 0000000000..9e6cc91803 --- /dev/null +++ b/lib/typescript_ast/decode_err.ml @@ -0,0 +1,31 @@ +(* Errors for decoding standalone expressions and type expressions + + NOTE: This is the consequence of the lack of multiple entrypoints + in Tree-sitter grammars. + *) + +(* Vendor dependencies *) + +module Region = Simple_utils.Region + +(* LIGO dependencies *) + +module Wrap = Lexing_shared.Wrap + +(* Errors *) + +type t = + | No_single_expression (* When parsing an expression *) + | No_single_type_expr (* When parsing a type expression *) + +type error = t + +let to_string = function + | No_single_expression -> "No single expression found." + | No_single_type_expr -> "No single type expression found." + +(* Creating errors *) + +let make (region : Region.t) (error : t) = + let value = Printf.sprintf "%s:\n%s" (region#to_string `Byte) (to_string error) in + Error Region.{ value; region } diff --git a/lib/typescript_ast/decode_main.ml b/lib/typescript_ast/decode_main.ml new file mode 100644 index 0000000000..7251179c1f --- /dev/null +++ b/lib/typescript_ast/decode_main.ml @@ -0,0 +1,66 @@ +(* Decoding the tree-sitter CST for TypeScript *) + +open Core + +(* Vendored *) + +module Region = Simple_utils.Region +module Snippet = Simple_utils.Snippet + +(* Tree-sitter ctypes-APIs for types and related functions *) + +module TS_types = Tree_sitter.Api.Types +module TS_fun = Tree_sitter.Api.Functions + +(* Local *) + +module Ts_wrap = Typescript_ast.Ts_wrap +module Loc_map = Typescript_ast.Loc_map +module Ast = Typescript_ast.Ast +module Decode = Typescript_decoder.Decode + +(* Parsing *) + +let parse debug_arg filename : (Ast.t, string Region.reg) result = + (* Loading the code as text *) + let file : string = Core.In_channel.read_all filename in + (* Building the map from line+columns to positions *) + let line_map = Loc_map.scan_string file in + (* Parsing the code into a tree *) + let tree : Ts_wrap.ts_tree_ptr = Ts_wrap.parse_typescript_string file in + (* Getting ahold of the root of the tree *) + let program_node : Ts_wrap.ts_tree = TS_fun.ts_tree_root_node tree in + (* Decoding the CST *) + let ast = Decode.dec_program ~debug_arg ~filename ~file line_map program_node in + (* Releasing the memory allocated to the tree *) + let () = TS_fun.ts_tree_delete tree in + ast + +(* Reading the input TypeScript, parsing and decoding the AST *) + +let usage_msg = "Usage: decode_main [-no-colour] .ts" +let no_colour = ref false +let debug = ref false +let input_file = ref "" +let anon_fun filename = input_file := filename + +let speclist = + [ "-no-colour", Arg.Set no_colour, "Colourless code snippets in errors." + ; "-debug", Arg.Set debug, "A missing field yields internal information." + ] + +(* Formatting error messages (snippets) *) + +let format_msg Region.{ value; region } = + sprintf + "%sError: %s" + (Format.asprintf "%a" (Snippet.pp_lift ~no_colour:!no_colour) region) + value + +(* Main *) + +let () = + Arg.parse speclist anon_fun usage_msg; + match parse !debug !input_file with + | Ok _ast -> Printf.printf "Done.\n%!" + | Error msg -> Printf.printf "%s\n%!" (format_msg msg) diff --git a/lib/typescript_ast/decorator.mli b/lib/typescript_ast/decorator.mli new file mode 100644 index 0000000000..28d76bf472 --- /dev/null +++ b/lib/typescript_ast/decorator.mli @@ -0,0 +1,6 @@ +(* Extracting a decorator from a line comment *) + +type name = string +type argument = string + +val scan : Lexing.lexbuf -> (name * argument option) option diff --git a/lib/typescript_ast/decorator.mll b/lib/typescript_ast/decorator.mll new file mode 100644 index 0000000000..38fab58f22 --- /dev/null +++ b/lib/typescript_ast/decorator.mll @@ -0,0 +1,45 @@ +(* Lexing numbers *) + +{ +(* START HEADER *) + +type name = string +type argument = string + +(* END HEADER *) +} + +(* START LEXER DEFINITION *) + +(* NAMED REGULAR EXPRESSIONS *) + +let blank = ' ' | '\t' +let small = ['a'-'z'] +let capital = ['A'-'Z'] +let letter = small | capital +let digit = ['0'-'9'] +let alphanum = letter | digit +let ident = (small | '_'+ alphanum) (alphanum | '_')* +let string = '"' ([^ '"' '\\' '\n']* as value) '"' + +(* RULES (SCANNERS) *) + +rule scan = parse + "//" blank* { scan_decorator lexbuf } +| eof | _ { None } + +and scan_decorator = parse + '@' (ident as name) blank* { scan_argument name lexbuf } +| eof | _ { None } + +and scan_argument name = parse + "(" blank* string blank* ")" { Some (name, Some value) } +| eof | _ { Some (name, None) } + +(* END LEXER DEFINITION *) + +{ +(* START TRAILER *) + +(* END TRAILER *) +} diff --git a/lib/typescript_ast/dune b/lib/typescript_ast/dune new file mode 100644 index 0000000000..71473e8bce --- /dev/null +++ b/lib/typescript_ast/dune @@ -0,0 +1,86 @@ +;; LIBRARIES + +;; Lexers + +(ocamllex number decorator loc_map) + +;; Library exporting the TypeScript CST and our AST + +(library + (name typescript_ast) + (public_name ligo.typescript_ast) + (modules syntax_err ast ts_wrap loc_map lexeme number) + (libraries + tree_sitter + tree_sitter_typescript + simple-utils ;; For source regions ([Region]) + cst_shared ;; For printing the TS CST ([Print_cst]) + lexing_shared ;; For metadata ([Wrap]) + hex + zarith + core + core_kernel.nonempty_list)) + +;; Library for decoding the TypeScript CST + +(library + (name typescript_decoder) + (public_name ligo.typescript_decoder) + (modules decode decode_err) + (libraries typescript_ast)) + +;; Library for stripping the TypeScript AST + +(library + (name typescript_stripper) + (public_name ligo.typescript_stripper) + (modules ast_stripped decorator strip strip_err) + (libraries typescript_ast typescript_decoder)) + +;; STANDALONES + +;; Printing the CST (debug) + +(executable + (name print_cst_main) + (libraries typescript_ast) + (modules print_cst print_cst_main)) + +;; Collecting all errors from the CST (debug) + +(executable + (name check_main) + (libraries typescript_ast) + (modules check check_main)) + +;; Decoding the CST into the AST + +(executable + (name decode_main) + (libraries typescript_decoder) + (modules decode_main)) + +;; Decoding the cST and stripping the resulting AST + +(executable + (name strip_main) + (libraries typescript_stripper) + (modules strip_main)) + +;; Local build of a standalone parser for node-types.json + +(executable + (name static_nodes_reader) + (libraries + cst_shared ;; For printing the TS CST + core + core_kernel.nonempty_list + yojson) + (modules static_nodes_reader)) + +;; Testing the map from line numbers to beginnings-of-line offsets + +(executable + (name loc_map_main) + (libraries typescript_ast) + (modules loc_map_main)) diff --git a/lib/typescript_ast/lexeme.ml b/lib/typescript_ast/lexeme.ml new file mode 100644 index 0000000000..37fb079ef8 --- /dev/null +++ b/lib/typescript_ast/lexeme.ml @@ -0,0 +1,13 @@ +(* Reading in the source the text at a given region (expecting a + lexeme) *) + +open Core +module Region = Simple_utils.Region + +let read (buffer : Buffer.t) (region : Region.t) : string = + let start_pos, stop_pos = region#byte_pos in + let start_cnum = start_pos.Lexing.pos_cnum + and stop_cnum = stop_pos.Lexing.pos_cnum in + let len = stop_cnum - start_cnum in + let bytes = Buffer.sub buffer ~pos:start_cnum ~len in + Bytes.to_string bytes diff --git a/lib/typescript_ast/lexeme.mli b/lib/typescript_ast/lexeme.mli new file mode 100644 index 0000000000..3cd9f86527 --- /dev/null +++ b/lib/typescript_ast/lexeme.mli @@ -0,0 +1,6 @@ +(* Reading in the source the text at a given region (expecting a + lexeme) *) + +module Region = Simple_utils.Region + +val read : Buffer.t -> Region.t -> string diff --git a/lib/typescript_ast/loc_map.mll b/lib/typescript_ast/loc_map.mll new file mode 100644 index 0000000000..f2a00b397d --- /dev/null +++ b/lib/typescript_ast/loc_map.mll @@ -0,0 +1,49 @@ +(* Mapping source locations as vertical and horizontal offsets to + [Pos.t]. See OCaml module [Lexing]. *) + +{ +(* START HEADER *) + +open Core +module Region = Simple_utils.Region + +type pos_bol = int +type t = pos_bol Int.Map.t (* From line numbers to beginning-of-line offset *) + +(* END HEADER *) +} + +(* START LEXER DEFINITION *) + +(* NAMED REGULAR EXPRESSIONS *) + +(* RULES (SCANNERS) *) + +rule scan map lnum bol = parse + '\n' { let key, data = lnum + 1, bol + 1 in + let map = Map.set ~key ~data map in + scan map key data lexbuf } +| eof { map } +| _ { scan map lnum (bol + 1) lexbuf } + +(* END LEXER DEFINITION *) + +{ +(* START TRAILER *) + +let scan_string (string : string) : t = + let lexbuf = Lexing.from_string string in + let init_map : t = Map.set Int.Map.empty ~key:1 ~data:0 in + scan init_map 1 0 lexbuf + +let scan_buffer buffer : t = scan_string (Buffer.contents buffer) + +let convert file map v_offset h_offset : Lexing.position option = + let line = v_offset + 1 in + match Map.find map line with + | None -> None + | Some pos_bol -> + Some { pos_fname = file; pos_lnum = line; pos_bol; pos_cnum = pos_bol + h_offset } + +(* END TRAILER *) +} diff --git a/lib/typescript_ast/loc_map_main.ml b/lib/typescript_ast/loc_map_main.ml new file mode 100644 index 0000000000..4ec357a2a8 --- /dev/null +++ b/lib/typescript_ast/loc_map_main.ml @@ -0,0 +1,51 @@ +open Core +module Region = Simple_utils.Region +module Loc_map = Typescript_ast.Loc_map + +let cli_args : string array = Sys.get_argv () + +let new_scan (file : string) : (Loc_map.t, string Region.reg) Result.t = + try Ok (Loc_map.scan_string (In_channel.read_all file)) with + | Sys_error msg -> + let region = Region.min ~file in + Error Region.{ region; value = msg } + +let old_scan (file : string) : (Loc_map.t, string Region.reg) Result.t = + try + let in_chan = In_channel.create file + and init_map : Loc_map.t = Map.set Int.Map.empty ~key:1 ~data:0 + and f (lnum, bol, map) line = + let bol = bol + String.length line + 1 in + lnum + 1, bol, Map.set map ~key:lnum ~data:bol + in + let _, _, map = In_channel.fold_lines in_chan ~init:(2, 0, init_map) ~f in + let () = In_channel.close in_chan in + Ok map + with + | Sys_error msg -> + let region = Region.min ~file in + Error Region.{ region; value = msg } + +(* Debug *) + +let print map = + let f ~key ~data = Printf.printf "%i -> %i\n%!" key data in + Map.iteri ~f map + +let () = + match Array.length cli_args with + | 2 -> + let file = cli_args.(1) in + let () = + match new_scan file with + | Ok line_map -> + Printf.printf "New mapping:\n%!"; + print line_map + | Error { region = _; value } -> Printf.eprintf "Error: %s\n%!" value + in + (match old_scan file with + | Ok line_map -> + Printf.printf "Old mapping (reference):\n%!"; + print line_map + | Error { region = _; value } -> Printf.eprintf "Error: %s\n%!" value) + | _ -> prerr_endline ("Usage: " ^ cli_args.(0) ^ " [file]") diff --git a/lib/typescript_ast/node-types.json b/lib/typescript_ast/node-types.json new file mode 100644 index 0000000000..5233d2723c --- /dev/null +++ b/lib/typescript_ast/node-types.json @@ -0,0 +1,6336 @@ +[ + { + "type": "declaration", + "named": true, + "subtypes": [ + { + "type": "abstract_class_declaration", + "named": true + }, + { + "type": "ambient_declaration", + "named": true + }, + { + "type": "class_declaration", + "named": true + }, + { + "type": "enum_declaration", + "named": true + }, + { + "type": "function_declaration", + "named": true + }, + { + "type": "function_signature", + "named": true + }, + { + "type": "generator_function_declaration", + "named": true + }, + { + "type": "import_alias", + "named": true + }, + { + "type": "interface_declaration", + "named": true + }, + { + "type": "internal_module", + "named": true + }, + { + "type": "lexical_declaration", + "named": true + }, + { + "type": "module", + "named": true + }, + { + "type": "type_alias_declaration", + "named": true + }, + { + "type": "variable_declaration", + "named": true + } + ] + }, + { + "type": "expression", + "named": true, + "subtypes": [ + { + "type": "as_expression", + "named": true + }, + { + "type": "assignment_expression", + "named": true + }, + { + "type": "augmented_assignment_expression", + "named": true + }, + { + "type": "await_expression", + "named": true + }, + { + "type": "binary_expression", + "named": true + }, + { + "type": "glimmer_template", + "named": true + }, + { + "type": "instantiation_expression", + "named": true + }, + { + "type": "internal_module", + "named": true + }, + { + "type": "new_expression", + "named": true + }, + { + "type": "primary_expression", + "named": true + }, + { + "type": "satisfies_expression", + "named": true + }, + { + "type": "ternary_expression", + "named": true + }, + { + "type": "type_assertion", + "named": true + }, + { + "type": "unary_expression", + "named": true + }, + { + "type": "update_expression", + "named": true + }, + { + "type": "yield_expression", + "named": true + } + ] + }, + { + "type": "pattern", + "named": true, + "subtypes": [ + { + "type": "array_pattern", + "named": true + }, + { + "type": "identifier", + "named": true + }, + { + "type": "member_expression", + "named": true + }, + { + "type": "non_null_expression", + "named": true + }, + { + "type": "object_pattern", + "named": true + }, + { + "type": "rest_pattern", + "named": true + }, + { + "type": "subscript_expression", + "named": true + }, + { + "type": "undefined", + "named": true + } + ] + }, + { + "type": "primary_expression", + "named": true, + "subtypes": [ + { + "type": "array", + "named": true + }, + { + "type": "arrow_function", + "named": true + }, + { + "type": "call_expression", + "named": true + }, + { + "type": "class", + "named": true + }, + { + "type": "false", + "named": true + }, + { + "type": "function_expression", + "named": true + }, + { + "type": "generator_function", + "named": true + }, + { + "type": "identifier", + "named": true + }, + { + "type": "member_expression", + "named": true + }, + { + "type": "meta_property", + "named": true + }, + { + "type": "non_null_expression", + "named": true + }, + { + "type": "null", + "named": true + }, + { + "type": "number", + "named": true + }, + { + "type": "object", + "named": true + }, + { + "type": "parenthesized_expression", + "named": true + }, + { + "type": "regex", + "named": true + }, + { + "type": "string", + "named": true + }, + { + "type": "subscript_expression", + "named": true + }, + { + "type": "super", + "named": true + }, + { + "type": "template_string", + "named": true + }, + { + "type": "this", + "named": true + }, + { + "type": "true", + "named": true + }, + { + "type": "undefined", + "named": true + } + ] + }, + { + "type": "primary_type", + "named": true, + "subtypes": [ + { + "type": "array_type", + "named": true + }, + { + "type": "conditional_type", + "named": true + }, + { + "type": "const", + "named": false + }, + { + "type": "existential_type", + "named": true + }, + { + "type": "flow_maybe_type", + "named": true + }, + { + "type": "generic_type", + "named": true + }, + { + "type": "index_type_query", + "named": true + }, + { + "type": "intersection_type", + "named": true + }, + { + "type": "literal_type", + "named": true + }, + { + "type": "lookup_type", + "named": true + }, + { + "type": "nested_type_identifier", + "named": true + }, + { + "type": "object_type", + "named": true + }, + { + "type": "parenthesized_type", + "named": true + }, + { + "type": "predefined_type", + "named": true + }, + { + "type": "template_literal_type", + "named": true + }, + { + "type": "this_type", + "named": true + }, + { + "type": "tuple_type", + "named": true + }, + { + "type": "type_identifier", + "named": true + }, + { + "type": "type_query", + "named": true + }, + { + "type": "union_type", + "named": true + } + ] + }, + { + "type": "statement", + "named": true, + "subtypes": [ + { + "type": "break_statement", + "named": true + }, + { + "type": "continue_statement", + "named": true + }, + { + "type": "debugger_statement", + "named": true + }, + { + "type": "declaration", + "named": true + }, + { + "type": "do_statement", + "named": true + }, + { + "type": "empty_statement", + "named": true + }, + { + "type": "export_statement", + "named": true + }, + { + "type": "expression_statement", + "named": true + }, + { + "type": "for_in_statement", + "named": true + }, + { + "type": "for_statement", + "named": true + }, + { + "type": "if_statement", + "named": true + }, + { + "type": "import_statement", + "named": true + }, + { + "type": "labeled_statement", + "named": true + }, + { + "type": "return_statement", + "named": true + }, + { + "type": "statement_block", + "named": true + }, + { + "type": "switch_statement", + "named": true + }, + { + "type": "throw_statement", + "named": true + }, + { + "type": "try_statement", + "named": true + }, + { + "type": "while_statement", + "named": true + }, + { + "type": "with_statement", + "named": true + } + ] + }, + { + "type": "type", + "named": true, + "subtypes": [ + { + "type": "call_expression", + "named": true + }, + { + "type": "constructor_type", + "named": true + }, + { + "type": "function_type", + "named": true + }, + { + "type": "infer_type", + "named": true + }, + { + "type": "member_expression", + "named": true + }, + { + "type": "primary_type", + "named": true + }, + { + "type": "readonly_type", + "named": true + } + ] + }, + { + "type": "abstract_class_declaration", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "class_body", + "named": true + } + ] + }, + "decorator": { + "multiple": true, + "required": false, + "types": [ + { + "type": "decorator", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type_identifier", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + }, + "children": { + "multiple": false, + "required": false, + "types": [ + { + "type": "class_heritage", + "named": true + } + ] + } + }, + { + "type": "abstract_method_signature", + "named": true, + "fields": { + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "computed_property_name", + "named": true + }, + { + "type": "number", + "named": true + }, + { + "type": "private_property_identifier", + "named": true + }, + { + "type": "property_identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + }, + "parameters": { + "multiple": false, + "required": true, + "types": [ + { + "type": "formal_parameters", + "named": true + } + ] + }, + "return_type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "asserts_annotation", + "named": true + }, + { + "type": "type_annotation", + "named": true + }, + { + "type": "type_predicate_annotation", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + }, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "accessibility_modifier", + "named": true + }, + { + "type": "override_modifier", + "named": true + } + ] + } + }, + { + "type": "accessibility_modifier", + "named": true, + "fields": {} + }, + { + "type": "adding_type_annotation", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "ambient_declaration", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "declaration", + "named": true + }, + { + "type": "property_identifier", + "named": true + }, + { + "type": "statement_block", + "named": true + }, + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "arguments", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "spread_element", + "named": true + } + ] + } + }, + { + "type": "array", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "spread_element", + "named": true + } + ] + } + }, + { + "type": "array_pattern", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "assignment_pattern", + "named": true + }, + { + "type": "pattern", + "named": true + } + ] + } + }, + { + "type": "array_type", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "primary_type", + "named": true + } + ] + } + }, + { + "type": "arrow_function", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "statement_block", + "named": true + } + ] + }, + "parameter": { + "multiple": false, + "required": false, + "types": [ + { + "type": "identifier", + "named": true + } + ] + }, + "parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "formal_parameters", + "named": true + } + ] + }, + "return_type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "asserts_annotation", + "named": true + }, + { + "type": "type_annotation", + "named": true + }, + { + "type": "type_predicate_annotation", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + } + }, + { + "type": "as_expression", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "asserts", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "this", + "named": true + }, + { + "type": "type_predicate", + "named": true + } + ] + } + }, + { + "type": "asserts_annotation", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "asserts", + "named": true + } + ] + } + }, + { + "type": "assignment_expression", + "named": true, + "fields": { + "left": { + "multiple": false, + "required": true, + "types": [ + { + "type": "array_pattern", + "named": true + }, + { + "type": "identifier", + "named": true + }, + { + "type": "member_expression", + "named": true + }, + { + "type": "non_null_expression", + "named": true + }, + { + "type": "object_pattern", + "named": true + }, + { + "type": "parenthesized_expression", + "named": true + }, + { + "type": "subscript_expression", + "named": true + }, + { + "type": "undefined", + "named": true + } + ] + }, + "right": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "assignment_pattern", + "named": true, + "fields": { + "left": { + "multiple": false, + "required": true, + "types": [ + { + "type": "pattern", + "named": true + } + ] + }, + "right": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "augmented_assignment_expression", + "named": true, + "fields": { + "left": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "member_expression", + "named": true + }, + { + "type": "non_null_expression", + "named": true + }, + { + "type": "parenthesized_expression", + "named": true + }, + { + "type": "subscript_expression", + "named": true + } + ] + }, + "operator": { + "multiple": false, + "required": true, + "types": [ + { + "type": "%=", + "named": false + }, + { + "type": "&&=", + "named": false + }, + { + "type": "&=", + "named": false + }, + { + "type": "**=", + "named": false + }, + { + "type": "*=", + "named": false + }, + { + "type": "+=", + "named": false + }, + { + "type": "-=", + "named": false + }, + { + "type": "/=", + "named": false + }, + { + "type": "<<=", + "named": false + }, + { + "type": ">>=", + "named": false + }, + { + "type": ">>>=", + "named": false + }, + { + "type": "??=", + "named": false + }, + { + "type": "^=", + "named": false + }, + { + "type": "|=", + "named": false + }, + { + "type": "||=", + "named": false + } + ] + }, + "right": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "await_expression", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + { + "type": "binary_expression", + "named": true, + "fields": { + "left": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "private_property_identifier", + "named": true + } + ] + }, + "operator": { + "multiple": false, + "required": true, + "types": [ + { + "type": "!=", + "named": false + }, + { + "type": "!==", + "named": false + }, + { + "type": "%", + "named": false + }, + { + "type": "&", + "named": false + }, + { + "type": "&&", + "named": false + }, + { + "type": "*", + "named": false + }, + { + "type": "**", + "named": false + }, + { + "type": "+", + "named": false + }, + { + "type": "-", + "named": false + }, + { + "type": "/", + "named": false + }, + { + "type": "<", + "named": false + }, + { + "type": "<<", + "named": false + }, + { + "type": "<=", + "named": false + }, + { + "type": "==", + "named": false + }, + { + "type": "===", + "named": false + }, + { + "type": ">", + "named": false + }, + { + "type": ">=", + "named": false + }, + { + "type": ">>", + "named": false + }, + { + "type": ">>>", + "named": false + }, + { + "type": "??", + "named": false + }, + { + "type": "^", + "named": false + }, + { + "type": "in", + "named": false + }, + { + "type": "instanceof", + "named": false + }, + { + "type": "|", + "named": false + }, + { + "type": "||", + "named": false + } + ] + }, + "right": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "break_statement", + "named": true, + "fields": { + "label": { + "multiple": false, + "required": false, + "types": [ + { + "type": "statement_identifier", + "named": true + } + ] + } + } + }, + { + "type": "call_expression", + "named": true, + "fields": { + "arguments": { + "multiple": false, + "required": true, + "types": [ + { + "type": "arguments", + "named": true + }, + { + "type": "template_string", + "named": true + } + ] + }, + "function": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "import", + "named": true + } + ] + }, + "type_arguments": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_arguments", + "named": true + } + ] + } + } + }, + { + "type": "call_signature", + "named": true, + "fields": { + "parameters": { + "multiple": false, + "required": true, + "types": [ + { + "type": "formal_parameters", + "named": true + } + ] + }, + "return_type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "asserts_annotation", + "named": true + }, + { + "type": "type_annotation", + "named": true + }, + { + "type": "type_predicate_annotation", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + } + }, + { + "type": "catch_clause", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement_block", + "named": true + } + ] + }, + "parameter": { + "multiple": false, + "required": false, + "types": [ + { + "type": "array_pattern", + "named": true + }, + { + "type": "identifier", + "named": true + }, + { + "type": "object_pattern", + "named": true + } + ] + }, + "type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_annotation", + "named": true + } + ] + } + } + }, + { + "type": "class", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "class_body", + "named": true + } + ] + }, + "decorator": { + "multiple": true, + "required": false, + "types": [ + { + "type": "decorator", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_identifier", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + }, + "children": { + "multiple": false, + "required": false, + "types": [ + { + "type": "class_heritage", + "named": true + } + ] + } + }, + { + "type": "class_body", + "named": true, + "fields": { + "decorator": { + "multiple": true, + "required": false, + "types": [ + { + "type": "decorator", + "named": true + } + ] + } + }, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "abstract_method_signature", + "named": true + }, + { + "type": "class_static_block", + "named": true + }, + { + "type": "index_signature", + "named": true + }, + { + "type": "method_definition", + "named": true + }, + { + "type": "method_signature", + "named": true + }, + { + "type": "public_field_definition", + "named": true + } + ] + } + }, + { + "type": "class_declaration", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "class_body", + "named": true + } + ] + }, + "decorator": { + "multiple": true, + "required": false, + "types": [ + { + "type": "decorator", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type_identifier", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + }, + "children": { + "multiple": false, + "required": false, + "types": [ + { + "type": "class_heritage", + "named": true + } + ] + } + }, + { + "type": "class_heritage", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "extends_clause", + "named": true + }, + { + "type": "implements_clause", + "named": true + } + ] + } + }, + { + "type": "class_static_block", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement_block", + "named": true + } + ] + } + } + }, + { + "type": "comment", + "named": true, + "fields": {} + }, + { + "type": "computed_property_name", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + { + "type": "conditional_type", + "named": true, + "fields": { + "alternative": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + }, + "consequence": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + }, + "left": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + }, + "right": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + } + }, + { + "type": "constraint", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "construct_signature", + "named": true, + "fields": { + "parameters": { + "multiple": false, + "required": true, + "types": [ + { + "type": "formal_parameters", + "named": true + } + ] + }, + "type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_annotation", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + } + }, + { + "type": "constructor_type", + "named": true, + "fields": { + "parameters": { + "multiple": false, + "required": true, + "types": [ + { + "type": "formal_parameters", + "named": true + } + ] + }, + "type": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + } + }, + { + "type": "continue_statement", + "named": true, + "fields": { + "label": { + "multiple": false, + "required": false, + "types": [ + { + "type": "statement_identifier", + "named": true + } + ] + } + } + }, + { + "type": "debugger_statement", + "named": true, + "fields": {} + }, + { + "type": "decorator", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "call_expression", + "named": true + }, + { + "type": "identifier", + "named": true + }, + { + "type": "member_expression", + "named": true + }, + { + "type": "parenthesized_expression", + "named": true + } + ] + } + }, + { + "type": "default_type", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "do_statement", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement", + "named": true + } + ] + }, + "condition": { + "multiple": false, + "required": true, + "types": [ + { + "type": "parenthesized_expression", + "named": true + } + ] + } + } + }, + { + "type": "else_clause", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement", + "named": true + } + ] + } + }, + { + "type": "empty_statement", + "named": true, + "fields": {} + }, + { + "type": "enum_assignment", + "named": true, + "fields": { + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "computed_property_name", + "named": true + }, + { + "type": "number", + "named": true + }, + { + "type": "private_property_identifier", + "named": true + }, + { + "type": "property_identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + }, + "value": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "enum_body", + "named": true, + "fields": { + "name": { + "multiple": true, + "required": false, + "types": [ + { + "type": "computed_property_name", + "named": true + }, + { + "type": "number", + "named": true + }, + { + "type": "private_property_identifier", + "named": true + }, + { + "type": "property_identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + } + }, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "enum_assignment", + "named": true + } + ] + } + }, + { + "type": "enum_declaration", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "enum_body", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + } + ] + } + } + }, + { + "type": "existential_type", + "named": true, + "fields": {} + }, + { + "type": "export_clause", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "export_specifier", + "named": true + } + ] + } + }, + { + "type": "export_specifier", + "named": true, + "fields": { + "alias": { + "multiple": false, + "required": false, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + } + } + }, + { + "type": "export_statement", + "named": true, + "fields": { + "declaration": { + "multiple": false, + "required": false, + "types": [ + { + "type": "declaration", + "named": true + } + ] + }, + "decorator": { + "multiple": true, + "required": false, + "types": [ + { + "type": "decorator", + "named": true + } + ] + }, + "source": { + "multiple": false, + "required": false, + "types": [ + { + "type": "string", + "named": true + } + ] + }, + "value": { + "multiple": false, + "required": false, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + "children": { + "multiple": false, + "required": false, + "types": [ + { + "type": "export_clause", + "named": true + }, + { + "type": "expression", + "named": true + }, + { + "type": "identifier", + "named": true + }, + { + "type": "namespace_export", + "named": true + } + ] + } + }, + { + "type": "expression_statement", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "sequence_expression", + "named": true + } + ] + } + }, + { + "type": "extends_clause", + "named": true, + "fields": { + "type_arguments": { + "multiple": true, + "required": false, + "types": [ + { + "type": "type_arguments", + "named": true + } + ] + }, + "value": { + "multiple": true, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "extends_type_clause", + "named": true, + "fields": { + "type": { + "multiple": true, + "required": true, + "types": [ + { + "type": "generic_type", + "named": true + }, + { + "type": "nested_type_identifier", + "named": true + }, + { + "type": "type_identifier", + "named": true + } + ] + } + } + }, + { + "type": "finally_clause", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement_block", + "named": true + } + ] + } + } + }, + { + "type": "flow_maybe_type", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "primary_type", + "named": true + } + ] + } + }, + { + "type": "for_in_statement", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement", + "named": true + } + ] + }, + "kind": { + "multiple": false, + "required": false, + "types": [ + { + "type": "const", + "named": false + }, + { + "type": "let", + "named": false + }, + { + "type": "var", + "named": false + } + ] + }, + "left": { + "multiple": false, + "required": true, + "types": [ + { + "type": "array_pattern", + "named": true + }, + { + "type": "identifier", + "named": true + }, + { + "type": "member_expression", + "named": true + }, + { + "type": "non_null_expression", + "named": true + }, + { + "type": "object_pattern", + "named": true + }, + { + "type": "parenthesized_expression", + "named": true + }, + { + "type": "subscript_expression", + "named": true + }, + { + "type": "undefined", + "named": true + } + ] + }, + "operator": { + "multiple": false, + "required": true, + "types": [ + { + "type": "in", + "named": false + }, + { + "type": "of", + "named": false + } + ] + }, + "right": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "sequence_expression", + "named": true + } + ] + }, + "value": { + "multiple": false, + "required": false, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "for_statement", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement", + "named": true + } + ] + }, + "condition": { + "multiple": false, + "required": true, + "types": [ + { + "type": "empty_statement", + "named": true + }, + { + "type": "expression_statement", + "named": true + } + ] + }, + "increment": { + "multiple": false, + "required": false, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "sequence_expression", + "named": true + } + ] + }, + "initializer": { + "multiple": false, + "required": true, + "types": [ + { + "type": "empty_statement", + "named": true + }, + { + "type": "expression_statement", + "named": true + }, + { + "type": "lexical_declaration", + "named": true + }, + { + "type": "variable_declaration", + "named": true + } + ] + } + } + }, + { + "type": "formal_parameters", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "optional_parameter", + "named": true + }, + { + "type": "required_parameter", + "named": true + } + ] + } + }, + { + "type": "function_declaration", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement_block", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + } + ] + }, + "parameters": { + "multiple": false, + "required": true, + "types": [ + { + "type": "formal_parameters", + "named": true + } + ] + }, + "return_type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "asserts_annotation", + "named": true + }, + { + "type": "type_annotation", + "named": true + }, + { + "type": "type_predicate_annotation", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + } + }, + { + "type": "function_expression", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement_block", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": false, + "types": [ + { + "type": "identifier", + "named": true + } + ] + }, + "parameters": { + "multiple": false, + "required": true, + "types": [ + { + "type": "formal_parameters", + "named": true + } + ] + }, + "return_type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "asserts_annotation", + "named": true + }, + { + "type": "type_annotation", + "named": true + }, + { + "type": "type_predicate_annotation", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + } + }, + { + "type": "function_signature", + "named": true, + "fields": { + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + } + ] + }, + "parameters": { + "multiple": false, + "required": true, + "types": [ + { + "type": "formal_parameters", + "named": true + } + ] + }, + "return_type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "asserts_annotation", + "named": true + }, + { + "type": "type_annotation", + "named": true + }, + { + "type": "type_predicate_annotation", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + } + }, + { + "type": "function_type", + "named": true, + "fields": { + "parameters": { + "multiple": false, + "required": true, + "types": [ + { + "type": "formal_parameters", + "named": true + } + ] + }, + "return_type": { + "multiple": false, + "required": true, + "types": [ + { + "type": "asserts", + "named": true + }, + { + "type": "type", + "named": true + }, + { + "type": "type_predicate", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + } + }, + { + "type": "generator_function", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement_block", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": false, + "types": [ + { + "type": "identifier", + "named": true + } + ] + }, + "parameters": { + "multiple": false, + "required": true, + "types": [ + { + "type": "formal_parameters", + "named": true + } + ] + }, + "return_type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "asserts_annotation", + "named": true + }, + { + "type": "type_annotation", + "named": true + }, + { + "type": "type_predicate_annotation", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + } + }, + { + "type": "generator_function_declaration", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement_block", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + } + ] + }, + "parameters": { + "multiple": false, + "required": true, + "types": [ + { + "type": "formal_parameters", + "named": true + } + ] + }, + "return_type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "asserts_annotation", + "named": true + }, + { + "type": "type_annotation", + "named": true + }, + { + "type": "type_predicate_annotation", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + } + }, + { + "type": "generic_type", + "named": true, + "fields": { + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "nested_type_identifier", + "named": true + }, + { + "type": "type_identifier", + "named": true + } + ] + }, + "type_arguments": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type_arguments", + "named": true + } + ] + } + } + }, + { + "type": "glimmer_closing_tag", + "named": true, + "fields": {} + }, + { + "type": "glimmer_opening_tag", + "named": true, + "fields": {} + }, + { + "type": "glimmer_template", + "named": true, + "fields": { + "close_tag": { + "multiple": false, + "required": true, + "types": [ + { + "type": "glimmer_closing_tag", + "named": true + } + ] + }, + "open_tag": { + "multiple": false, + "required": true, + "types": [ + { + "type": "glimmer_opening_tag", + "named": true + } + ] + } + } + }, + { + "type": "identifier", + "named": true, + "fields": {} + }, + { + "type": "if_statement", + "named": true, + "fields": { + "alternative": { + "multiple": false, + "required": false, + "types": [ + { + "type": "else_clause", + "named": true + } + ] + }, + "condition": { + "multiple": false, + "required": true, + "types": [ + { + "type": "parenthesized_expression", + "named": true + } + ] + }, + "consequence": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement", + "named": true + } + ] + } + } + }, + { + "type": "implements_clause", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "import", + "named": true, + "fields": {} + }, + { + "type": "import_alias", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "nested_identifier", + "named": true + } + ] + } + }, + { + "type": "import_attribute", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "object", + "named": true + } + ] + } + }, + { + "type": "import_clause", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "named_imports", + "named": true + }, + { + "type": "namespace_import", + "named": true + } + ] + } + }, + { + "type": "import_require_clause", + "named": true, + "fields": { + "source": { + "multiple": false, + "required": true, + "types": [ + { + "type": "string", + "named": true + } + ] + } + }, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + } + ] + } + }, + { + "type": "import_specifier", + "named": true, + "fields": { + "alias": { + "multiple": false, + "required": false, + "types": [ + { + "type": "identifier", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + } + } + }, + { + "type": "import_statement", + "named": true, + "fields": { + "source": { + "multiple": false, + "required": false, + "types": [ + { + "type": "string", + "named": true + } + ] + } + }, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "import_attribute", + "named": true + }, + { + "type": "import_clause", + "named": true + }, + { + "type": "import_require_clause", + "named": true + } + ] + } + }, + { + "type": "index_signature", + "named": true, + "fields": { + "index_type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": false, + "types": [ + { + "type": "identifier", + "named": true + } + ] + }, + "sign": { + "multiple": false, + "required": false, + "types": [ + { + "type": "+", + "named": false + }, + { + "type": "-", + "named": false + } + ] + }, + "type": { + "multiple": false, + "required": true, + "types": [ + { + "type": "adding_type_annotation", + "named": true + }, + { + "type": "omitting_type_annotation", + "named": true + }, + { + "type": "opting_type_annotation", + "named": true + }, + { + "type": "type_annotation", + "named": true + } + ] + } + }, + "children": { + "multiple": false, + "required": false, + "types": [ + { + "type": "mapped_type_clause", + "named": true + } + ] + } + }, + { + "type": "index_type_query", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "primary_type", + "named": true + } + ] + } + }, + { + "type": "infer_type", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "type", + "named": true + }, + { + "type": "type_identifier", + "named": true + } + ] + } + }, + { + "type": "instantiation_expression", + "named": true, + "fields": { + "function": { + "multiple": false, + "required": false, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "import", + "named": true + }, + { + "type": "member_expression", + "named": true + }, + { + "type": "subscript_expression", + "named": true + } + ] + }, + "type_arguments": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type_arguments", + "named": true + } + ] + } + }, + "children": { + "multiple": false, + "required": false, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + { + "type": "interface_body", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "call_signature", + "named": true + }, + { + "type": "construct_signature", + "named": true + }, + { + "type": "export_statement", + "named": true + }, + { + "type": "index_signature", + "named": true + }, + { + "type": "method_signature", + "named": true + }, + { + "type": "property_signature", + "named": true + } + ] + } + }, + { + "type": "interface_declaration", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "interface_body", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type_identifier", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + }, + "children": { + "multiple": false, + "required": false, + "types": [ + { + "type": "extends_type_clause", + "named": true + } + ] + } + }, + { + "type": "internal_module", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": false, + "types": [ + { + "type": "statement_block", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "nested_identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + } + } + }, + { + "type": "intersection_type", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "jsx_attribute", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "jsx_element", + "named": true + }, + { + "type": "jsx_expression", + "named": true + }, + { + "type": "jsx_namespace_name", + "named": true + }, + { + "type": "jsx_self_closing_element", + "named": true + }, + { + "type": "property_identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + } + }, + { + "type": "jsx_closing_element", + "named": true, + "fields": { + "name": { + "multiple": false, + "required": false, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "jsx_namespace_name", + "named": true + }, + { + "type": "member_expression", + "named": true + } + ] + } + } + }, + { + "type": "jsx_element", + "named": true, + "fields": { + "close_tag": { + "multiple": false, + "required": true, + "types": [ + { + "type": "jsx_closing_element", + "named": true + } + ] + }, + "open_tag": { + "multiple": false, + "required": true, + "types": [ + { + "type": "jsx_opening_element", + "named": true + } + ] + } + }, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "html_character_reference", + "named": true + }, + { + "type": "jsx_element", + "named": true + }, + { + "type": "jsx_expression", + "named": true + }, + { + "type": "jsx_self_closing_element", + "named": true + }, + { + "type": "jsx_text", + "named": true + } + ] + } + }, + { + "type": "jsx_expression", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": false, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "sequence_expression", + "named": true + }, + { + "type": "spread_element", + "named": true + } + ] + } + }, + { + "type": "jsx_namespace_name", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + } + ] + } + }, + { + "type": "jsx_opening_element", + "named": true, + "fields": { + "attribute": { + "multiple": true, + "required": false, + "types": [ + { + "type": "jsx_attribute", + "named": true + }, + { + "type": "jsx_expression", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": false, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "jsx_namespace_name", + "named": true + }, + { + "type": "member_expression", + "named": true + } + ] + }, + "type_arguments": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_arguments", + "named": true + } + ] + } + } + }, + { + "type": "jsx_self_closing_element", + "named": true, + "fields": { + "attribute": { + "multiple": true, + "required": false, + "types": [ + { + "type": "jsx_attribute", + "named": true + }, + { + "type": "jsx_expression", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": false, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "jsx_namespace_name", + "named": true + }, + { + "type": "member_expression", + "named": true + } + ] + }, + "type_arguments": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_arguments", + "named": true + } + ] + } + } + }, + { + "type": "jsx_text", + "named": true, + "fields": {} + }, + { + "type": "labeled_statement", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement", + "named": true + } + ] + }, + "label": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement_identifier", + "named": true + } + ] + } + } + }, + { + "type": "lexical_declaration", + "named": true, + "fields": { + "kind": { + "multiple": false, + "required": true, + "types": [ + { + "type": "const", + "named": false + }, + { + "type": "let", + "named": false + } + ] + } + }, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "variable_declarator", + "named": true + } + ] + } + }, + { + "type": "literal_type", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "false", + "named": true + }, + { + "type": "null", + "named": true + }, + { + "type": "number", + "named": true + }, + { + "type": "string", + "named": true + }, + { + "type": "true", + "named": true + }, + { + "type": "unary_expression", + "named": true + }, + { + "type": "undefined", + "named": true + } + ] + } + }, + { + "type": "lookup_type", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "mapped_type_clause", + "named": true, + "fields": { + "alias": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type_identifier", + "named": true + } + ] + }, + "type": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + } + }, + { + "type": "member_expression", + "named": true, + "fields": { + "object": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "import", + "named": true + } + ] + }, + "optional_chain": { + "multiple": false, + "required": false, + "types": [ + { + "type": "optional_chain", + "named": true + } + ] + }, + "property": { + "multiple": false, + "required": true, + "types": [ + { + "type": "private_property_identifier", + "named": true + }, + { + "type": "property_identifier", + "named": true + } + ] + } + } + }, + { + "type": "meta_property", + "named": true, + "fields": {} + }, + { + "type": "method_definition", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement_block", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "computed_property_name", + "named": true + }, + { + "type": "number", + "named": true + }, + { + "type": "private_property_identifier", + "named": true + }, + { + "type": "property_identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + }, + "parameters": { + "multiple": false, + "required": true, + "types": [ + { + "type": "formal_parameters", + "named": true + } + ] + }, + "return_type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "asserts_annotation", + "named": true + }, + { + "type": "type_annotation", + "named": true + }, + { + "type": "type_predicate_annotation", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + }, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "accessibility_modifier", + "named": true + }, + { + "type": "override_modifier", + "named": true + } + ] + } + }, + { + "type": "method_signature", + "named": true, + "fields": { + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "computed_property_name", + "named": true + }, + { + "type": "number", + "named": true + }, + { + "type": "private_property_identifier", + "named": true + }, + { + "type": "property_identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + }, + "parameters": { + "multiple": false, + "required": true, + "types": [ + { + "type": "formal_parameters", + "named": true + } + ] + }, + "return_type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "asserts_annotation", + "named": true + }, + { + "type": "type_annotation", + "named": true + }, + { + "type": "type_predicate_annotation", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + } + }, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "accessibility_modifier", + "named": true + }, + { + "type": "override_modifier", + "named": true + } + ] + } + }, + { + "type": "module", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": false, + "types": [ + { + "type": "statement_block", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "nested_identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + } + } + }, + { + "type": "named_imports", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "import_specifier", + "named": true + } + ] + } + }, + { + "type": "namespace_export", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + } + }, + { + "type": "namespace_import", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + } + ] + } + }, + { + "type": "nested_identifier", + "named": true, + "fields": { + "object": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "member_expression", + "named": true + } + ] + }, + "property": { + "multiple": false, + "required": true, + "types": [ + { + "type": "property_identifier", + "named": true + } + ] + } + } + }, + { + "type": "nested_type_identifier", + "named": true, + "fields": { + "module": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "nested_identifier", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type_identifier", + "named": true + } + ] + } + } + }, + { + "type": "new_expression", + "named": true, + "fields": { + "arguments": { + "multiple": false, + "required": false, + "types": [ + { + "type": "arguments", + "named": true + } + ] + }, + "constructor": { + "multiple": false, + "required": true, + "types": [ + { + "type": "primary_expression", + "named": true + } + ] + }, + "type_arguments": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_arguments", + "named": true + } + ] + } + } + }, + { + "type": "non_null_expression", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + { + "type": "object", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "method_definition", + "named": true + }, + { + "type": "pair", + "named": true + }, + { + "type": "shorthand_property_identifier", + "named": true + }, + { + "type": "spread_element", + "named": true + } + ] + } + }, + { + "type": "object_assignment_pattern", + "named": true, + "fields": { + "left": { + "multiple": false, + "required": true, + "types": [ + { + "type": "array_pattern", + "named": true + }, + { + "type": "object_pattern", + "named": true + }, + { + "type": "shorthand_property_identifier_pattern", + "named": true + } + ] + }, + "right": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "object_pattern", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "object_assignment_pattern", + "named": true + }, + { + "type": "pair_pattern", + "named": true + }, + { + "type": "rest_pattern", + "named": true + }, + { + "type": "shorthand_property_identifier_pattern", + "named": true + } + ] + } + }, + { + "type": "object_type", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "call_signature", + "named": true + }, + { + "type": "construct_signature", + "named": true + }, + { + "type": "export_statement", + "named": true + }, + { + "type": "index_signature", + "named": true + }, + { + "type": "method_signature", + "named": true + }, + { + "type": "property_signature", + "named": true + } + ] + } + }, + { + "type": "omitting_type_annotation", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "opting_type_annotation", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "optional_chain", + "named": true, + "fields": {} + }, + { + "type": "optional_parameter", + "named": true, + "fields": { + "decorator": { + "multiple": true, + "required": false, + "types": [ + { + "type": "decorator", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": false, + "types": [ + { + "type": "identifier", + "named": true + } + ] + }, + "pattern": { + "multiple": false, + "required": false, + "types": [ + { + "type": "pattern", + "named": true + }, + { + "type": "this", + "named": true + } + ] + }, + "type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_annotation", + "named": true + } + ] + }, + "value": { + "multiple": false, + "required": false, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "accessibility_modifier", + "named": true + }, + { + "type": "override_modifier", + "named": true + } + ] + } + }, + { + "type": "optional_type", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "override_modifier", + "named": true, + "fields": {} + }, + { + "type": "pair", + "named": true, + "fields": { + "key": { + "multiple": false, + "required": true, + "types": [ + { + "type": "computed_property_name", + "named": true + }, + { + "type": "number", + "named": true + }, + { + "type": "private_property_identifier", + "named": true + }, + { + "type": "property_identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + }, + "value": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "pair_pattern", + "named": true, + "fields": { + "key": { + "multiple": false, + "required": true, + "types": [ + { + "type": "computed_property_name", + "named": true + }, + { + "type": "number", + "named": true + }, + { + "type": "private_property_identifier", + "named": true + }, + { + "type": "property_identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + }, + "value": { + "multiple": false, + "required": true, + "types": [ + { + "type": "assignment_pattern", + "named": true + }, + { + "type": "pattern", + "named": true + } + ] + } + } + }, + { + "type": "parenthesized_expression", + "named": true, + "fields": { + "type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_annotation", + "named": true + } + ] + } + }, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "call_expression", + "named": true + }, + { + "type": "expression", + "named": true + }, + { + "type": "identifier", + "named": true + }, + { + "type": "member_expression", + "named": true + }, + { + "type": "sequence_expression", + "named": true + } + ] + } + }, + { + "type": "parenthesized_type", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "predefined_type", + "named": true, + "fields": {} + }, + { + "type": "program", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "hash_bang_line", + "named": true + }, + { + "type": "statement", + "named": true + } + ] + } + }, + { + "type": "property_signature", + "named": true, + "fields": { + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "computed_property_name", + "named": true + }, + { + "type": "number", + "named": true + }, + { + "type": "private_property_identifier", + "named": true + }, + { + "type": "property_identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + }, + "type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_annotation", + "named": true + } + ] + } + }, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "accessibility_modifier", + "named": true + }, + { + "type": "override_modifier", + "named": true + } + ] + } + }, + { + "type": "public_field_definition", + "named": true, + "fields": { + "decorator": { + "multiple": true, + "required": false, + "types": [ + { + "type": "decorator", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "computed_property_name", + "named": true + }, + { + "type": "number", + "named": true + }, + { + "type": "private_property_identifier", + "named": true + }, + { + "type": "property_identifier", + "named": true + }, + { + "type": "string", + "named": true + } + ] + }, + "type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_annotation", + "named": true + } + ] + }, + "value": { + "multiple": false, + "required": false, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "accessibility_modifier", + "named": true + }, + { + "type": "override_modifier", + "named": true + } + ] + } + }, + { + "type": "readonly_type", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "regex", + "named": true, + "fields": { + "flags": { + "multiple": false, + "required": false, + "types": [ + { + "type": "regex_flags", + "named": true + } + ] + }, + "pattern": { + "multiple": false, + "required": true, + "types": [ + { + "type": "regex_pattern", + "named": true + } + ] + } + } + }, + { + "type": "required_parameter", + "named": true, + "fields": { + "decorator": { + "multiple": true, + "required": false, + "types": [ + { + "type": "decorator", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": false, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "rest_pattern", + "named": true + } + ] + }, + "pattern": { + "multiple": false, + "required": false, + "types": [ + { + "type": "pattern", + "named": true + }, + { + "type": "this", + "named": true + } + ] + }, + "type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_annotation", + "named": true + } + ] + }, + "value": { + "multiple": false, + "required": false, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "accessibility_modifier", + "named": true + }, + { + "type": "override_modifier", + "named": true + } + ] + } + }, + { + "type": "rest_pattern", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "array_pattern", + "named": true + }, + { + "type": "identifier", + "named": true + }, + { + "type": "member_expression", + "named": true + }, + { + "type": "non_null_expression", + "named": true + }, + { + "type": "object_pattern", + "named": true + }, + { + "type": "subscript_expression", + "named": true + }, + { + "type": "undefined", + "named": true + } + ] + } + }, + { + "type": "rest_type", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "return_statement", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": false, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "sequence_expression", + "named": true + } + ] + } + }, + { + "type": "satisfies_expression", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "sequence_expression", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + { + "type": "spread_element", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + { + "type": "statement_block", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "statement", + "named": true + } + ] + } + }, + { + "type": "string", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "escape_sequence", + "named": true + }, + { + "type": "html_character_reference", + "named": true + }, + { + "type": "string_fragment", + "named": true + } + ] + } + }, + { + "type": "subscript_expression", + "named": true, + "fields": { + "index": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "number", + "named": true + }, + { + "type": "predefined_type", + "named": true + }, + { + "type": "sequence_expression", + "named": true + }, + { + "type": "string", + "named": true + } + ] + }, + "object": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "optional_chain": { + "multiple": false, + "required": false, + "types": [ + { + "type": "optional_chain", + "named": true + } + ] + } + } + }, + { + "type": "switch_body", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "switch_case", + "named": true + }, + { + "type": "switch_default", + "named": true + } + ] + } + }, + { + "type": "switch_case", + "named": true, + "fields": { + "body": { + "multiple": true, + "required": false, + "types": [ + { + "type": "statement", + "named": true + } + ] + }, + "value": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "sequence_expression", + "named": true + } + ] + } + } + }, + { + "type": "switch_default", + "named": true, + "fields": { + "body": { + "multiple": true, + "required": false, + "types": [ + { + "type": "statement", + "named": true + } + ] + } + } + }, + { + "type": "switch_statement", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "switch_body", + "named": true + } + ] + }, + "value": { + "multiple": false, + "required": true, + "types": [ + { + "type": "parenthesized_expression", + "named": true + } + ] + } + } + }, + { + "type": "template_literal_type", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "string_fragment", + "named": true + }, + { + "type": "template_type", + "named": true + } + ] + } + }, + { + "type": "template_string", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "escape_sequence", + "named": true + }, + { + "type": "string_fragment", + "named": true + }, + { + "type": "template_substitution", + "named": true + } + ] + } + }, + { + "type": "template_substitution", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "sequence_expression", + "named": true + } + ] + } + }, + { + "type": "template_type", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "infer_type", + "named": true + }, + { + "type": "primary_type", + "named": true + } + ] + } + }, + { + "type": "ternary_expression", + "named": true, + "fields": { + "alternative": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "condition": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "consequence": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "throw_statement", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "sequence_expression", + "named": true + } + ] + } + }, + { + "type": "try_statement", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement_block", + "named": true + } + ] + }, + "finalizer": { + "multiple": false, + "required": false, + "types": [ + { + "type": "finally_clause", + "named": true + } + ] + }, + "handler": { + "multiple": false, + "required": false, + "types": [ + { + "type": "catch_clause", + "named": true + } + ] + } + } + }, + { + "type": "tuple_type", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "optional_parameter", + "named": true + }, + { + "type": "optional_type", + "named": true + }, + { + "type": "required_parameter", + "named": true + }, + { + "type": "rest_type", + "named": true + }, + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "type_alias_declaration", + "named": true, + "fields": { + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type_identifier", + "named": true + } + ] + }, + "type_parameters": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_parameters", + "named": true + } + ] + }, + "value": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + } + }, + { + "type": "type_annotation", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "type_arguments", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "type_assertion", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "type_arguments", + "named": true + } + ] + } + }, + { + "type": "type_parameter", + "named": true, + "fields": { + "constraint": { + "multiple": false, + "required": false, + "types": [ + { + "type": "constraint", + "named": true + } + ] + }, + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type_identifier", + "named": true + } + ] + }, + "value": { + "multiple": false, + "required": false, + "types": [ + { + "type": "default_type", + "named": true + } + ] + } + } + }, + { + "type": "type_parameters", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "type_parameter", + "named": true + } + ] + } + }, + { + "type": "type_predicate", + "named": true, + "fields": { + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "identifier", + "named": true + }, + { + "type": "this", + "named": true + } + ] + }, + "type": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + } + }, + { + "type": "type_predicate_annotation", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "type_predicate", + "named": true + } + ] + } + }, + { + "type": "type_query", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": true, + "types": [ + { + "type": "call_expression", + "named": true + }, + { + "type": "identifier", + "named": true + }, + { + "type": "instantiation_expression", + "named": true + }, + { + "type": "member_expression", + "named": true + }, + { + "type": "subscript_expression", + "named": true + }, + { + "type": "this", + "named": true + } + ] + } + }, + { + "type": "unary_expression", + "named": true, + "fields": { + "argument": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + }, + { + "type": "number", + "named": true + } + ] + }, + "operator": { + "multiple": false, + "required": true, + "types": [ + { + "type": "!", + "named": false + }, + { + "type": "+", + "named": false + }, + { + "type": "-", + "named": false + }, + { + "type": "delete", + "named": false + }, + { + "type": "typeof", + "named": false + }, + { + "type": "void", + "named": false + }, + { + "type": "~", + "named": false + } + ] + } + } + }, + { + "type": "union_type", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "type", + "named": true + } + ] + } + }, + { + "type": "update_expression", + "named": true, + "fields": { + "argument": { + "multiple": false, + "required": true, + "types": [ + { + "type": "expression", + "named": true + } + ] + }, + "operator": { + "multiple": false, + "required": true, + "types": [ + { + "type": "++", + "named": false + }, + { + "type": "--", + "named": false + } + ] + } + } + }, + { + "type": "variable_declaration", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "variable_declarator", + "named": true + } + ] + } + }, + { + "type": "variable_declarator", + "named": true, + "fields": { + "name": { + "multiple": false, + "required": true, + "types": [ + { + "type": "array_pattern", + "named": true + }, + { + "type": "identifier", + "named": true + }, + { + "type": "object_pattern", + "named": true + } + ] + }, + "type": { + "multiple": false, + "required": false, + "types": [ + { + "type": "type_annotation", + "named": true + } + ] + }, + "value": { + "multiple": false, + "required": false, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + } + }, + { + "type": "while_statement", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement", + "named": true + } + ] + }, + "condition": { + "multiple": false, + "required": true, + "types": [ + { + "type": "parenthesized_expression", + "named": true + } + ] + } + } + }, + { + "type": "with_statement", + "named": true, + "fields": { + "body": { + "multiple": false, + "required": true, + "types": [ + { + "type": "statement", + "named": true + } + ] + }, + "object": { + "multiple": false, + "required": true, + "types": [ + { + "type": "parenthesized_expression", + "named": true + } + ] + } + } + }, + { + "type": "yield_expression", + "named": true, + "fields": {}, + "children": { + "multiple": false, + "required": false, + "types": [ + { + "type": "expression", + "named": true + } + ] + } + }, + { + "type": "!", + "named": false + }, + { + "type": "!=", + "named": false + }, + { + "type": "!==", + "named": false + }, + { + "type": "\"", + "named": false + }, + { + "type": "${", + "named": false + }, + { + "type": "%", + "named": false + }, + { + "type": "%=", + "named": false + }, + { + "type": "&", + "named": false + }, + { + "type": "&&", + "named": false + }, + { + "type": "&&=", + "named": false + }, + { + "type": "&=", + "named": false + }, + { + "type": "'", + "named": false + }, + { + "type": "(", + "named": false + }, + { + "type": ")", + "named": false + }, + { + "type": "*", + "named": false + }, + { + "type": "**", + "named": false + }, + { + "type": "**=", + "named": false + }, + { + "type": "*=", + "named": false + }, + { + "type": "+", + "named": false + }, + { + "type": "++", + "named": false + }, + { + "type": "+=", + "named": false + }, + { + "type": "+?:", + "named": false + }, + { + "type": ",", + "named": false + }, + { + "type": "-", + "named": false + }, + { + "type": "--", + "named": false + }, + { + "type": "-=", + "named": false + }, + { + "type": "-?:", + "named": false + }, + { + "type": ".", + "named": false + }, + { + "type": "...", + "named": false + }, + { + "type": "/", + "named": false + }, + { + "type": "/=", + "named": false + }, + { + "type": "/>", + "named": false + }, + { + "type": ":", + "named": false + }, + { + "type": ";", + "named": false + }, + { + "type": "<", + "named": false + }, + { + "type": "", + "named": false + }, + { + "type": "<<", + "named": false + }, + { + "type": "<<=", + "named": false + }, + { + "type": "<=", + "named": false + }, + { + "type": "