Merge branch 'feature/pascaligo-anon-functions' into 'dev'
Feature/pascaligo anon functions See merge request ligolang/ligo!201
This commit is contained in:
commit
bbcacc253b
@ -35,8 +35,10 @@ Here's how you define a basic function that accepts two `ints` and returns a sin
|
||||
|
||||
|
||||
```pascaligo
|
||||
function add(const a: int; const b: int): int is
|
||||
block { skip } with a + b
|
||||
function add(const a: int; const b: int): int is
|
||||
begin
|
||||
const result: int = a + b;
|
||||
end with result;
|
||||
```
|
||||
|
||||
The function body consists of two parts:
|
||||
@ -44,9 +46,14 @@ The function body consists of two parts:
|
||||
- `block {<code>}` - logic of the function
|
||||
- `with <value>` - the return value of the function
|
||||
|
||||
> 💡 `skip` can be used as a placeholder for empty function blocks, when all the neccessary logic fits into `with` at the end. It is also possible to omit `block { skip } with`
|
||||
in the above example, leaving only `a + b`.
|
||||
#### Blockless functions
|
||||
|
||||
Functions that can contain all of their logic into a single instruction/expression, can be defined without the surrounding `block`.
|
||||
Instead, you can inline the necessary logic directly, like this:
|
||||
|
||||
```pascaligo
|
||||
function add(const a: int; const b: int): int is a + b
|
||||
```
|
||||
|
||||
<!--Cameligo-->
|
||||
|
||||
@ -65,3 +72,25 @@ value.
|
||||
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
const increment : (int -> int) = (function (const i : int) : int is i + 1);
|
||||
// a = 2
|
||||
const a: int = increment(1);
|
||||
```
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||
|
||||
## Anonymous functions
|
||||
|
||||
Functions without a name, also known as anonymous functions are useful in cases when you want to pass the function as an argument or assign it to a key in a record/map.
|
||||
|
||||
Here's how to define an anonymous function assigned to a variable `increment`, with it's appropriate function type signature.
|
||||
<!--DOCUSAURUS_CODE_TABS-->
|
||||
<!--Pascaligo-->
|
||||
```pascaligo
|
||||
const increment : (int -> int) = (function (const i : int) : int is i + 1);
|
||||
// a = 2
|
||||
const a: int = increment(1);
|
||||
```
|
||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
4
gitlab-pages/docs/language-basics/src/functions/add.ligo
Normal file
4
gitlab-pages/docs/language-basics/src/functions/add.ligo
Normal file
@ -0,0 +1,4 @@
|
||||
function add(const a: int; const b: int): int is
|
||||
begin
|
||||
const result: int = a + b;
|
||||
end with result;
|
@ -0,0 +1,3 @@
|
||||
const increment : (int -> int) = (function (const i : int) : int is i + 1);
|
||||
// a = 2
|
||||
const a: int = increment(1);
|
@ -0,0 +1 @@
|
||||
function add(const a: int; const b: int): int is a + b
|
@ -43,6 +43,9 @@ Variables, unlike constants, are mutable. They can't be used in a *global scope*
|
||||
|
||||
> 💡 Don't worry if you don't understand the function syntax yet. We'll get to it in upcoming sections of the docs.
|
||||
|
||||
> ⚠️ Please be wary that mutation only works within the function scope itself, values outside of the function scope will not be affected.
|
||||
|
||||
|
||||
|
||||
```pascaligo
|
||||
// won't work, use const for global values instead
|
||||
|
@ -18,55 +18,25 @@ const GridBlock = CompLibrary.GridBlock;
|
||||
const pre = "```";
|
||||
|
||||
const pascaligoExampleSmall = `${pre}pascaligo
|
||||
// variant defining pseudo multi-entrypoint
|
||||
// actions
|
||||
type action is
|
||||
| Increment of int
|
||||
| Decrement of int
|
||||
|
||||
function add
|
||||
(const a: int; const b: int): int is
|
||||
block { skip } with a + b
|
||||
|
||||
function subtract
|
||||
(const a: int; const b: int): int
|
||||
is block { skip } with a - b
|
||||
|
||||
// real entrypoint that re-routes the flow
|
||||
// based on the action provided
|
||||
function main
|
||||
(const p: action; const s: int):
|
||||
(list(operation) * int) is
|
||||
block { skip }
|
||||
with ((nil : list(operation)),
|
||||
case p of
|
||||
| Increment(n) -> add(s, n)
|
||||
| Decrement(n) -> subtract(s, n)
|
||||
end)
|
||||
${pre}`;
|
||||
|
||||
const pascaligoExample = `${pre}pascaligo
|
||||
// variant defining pseudo multi-entrypoint actions
|
||||
type action is
|
||||
| Increment of int
|
||||
| Decrement of int
|
||||
|
||||
function add (const a : int ; const b : int) : int is
|
||||
block { skip } with a + b
|
||||
function add (const a : int ; const b : int) : int is a + b
|
||||
|
||||
function subtract (const a : int ; const b : int) : int is
|
||||
block { skip } with a - b
|
||||
function subtract (const a : int ; const b : int) : int is a - b
|
||||
|
||||
// real entrypoint that re-routes the flow based
|
||||
// on the action provided
|
||||
function main (const p : action ; const s : int) :
|
||||
(list(operation) * int) is
|
||||
block { skip } with ((nil : list(operation)),
|
||||
case p of
|
||||
| Increment(n) -> add(s, n)
|
||||
| Decrement(n) -> subtract(s, n)
|
||||
end)
|
||||
// real entrypoint that re-routes the flow based on the action provided
|
||||
function main (const p : action ; const s : int) : (list(operation) * int) is
|
||||
((nil : list(operation)),
|
||||
case p of
|
||||
| Increment (n) -> add (s, n)
|
||||
| Decrement (n) -> subtract (s, n)
|
||||
end)
|
||||
${pre}`;
|
||||
|
||||
const pascaligoExample = pascaligoExampleSmall;
|
||||
const cameligoExampleSmall = `${pre}ocaml
|
||||
type storage = int
|
||||
|
||||
|
@ -210,9 +210,9 @@ and type_tuple = (type_expr, comma) nsepseq par reg
|
||||
|
||||
(* Function and procedure declarations *)
|
||||
|
||||
and fun_decl = {
|
||||
and fun_expr = {
|
||||
kwd_function : kwd_function;
|
||||
name : variable;
|
||||
name : variable option;
|
||||
param : parameters;
|
||||
colon : colon;
|
||||
ret_type : type_expr;
|
||||
@ -221,7 +221,11 @@ and fun_decl = {
|
||||
block : block reg option;
|
||||
kwd_with : kwd_with option;
|
||||
return : expr;
|
||||
terminator : semi option
|
||||
}
|
||||
|
||||
and fun_decl = {
|
||||
fun_expr : fun_expr reg ;
|
||||
terminator : semi option ;
|
||||
}
|
||||
|
||||
and parameters = (param_decl, semi) nsepseq par reg
|
||||
@ -266,12 +270,12 @@ and statement =
|
||||
| Data of data_decl
|
||||
|
||||
and local_decl =
|
||||
LocalFun of fun_decl reg
|
||||
| LocalData of data_decl
|
||||
|
||||
and data_decl =
|
||||
LocalConst of const_decl reg
|
||||
| LocalVar of var_decl reg
|
||||
| LocalFun of fun_decl reg
|
||||
|
||||
and var_decl = {
|
||||
kwd_var : kwd_var;
|
||||
@ -464,6 +468,7 @@ and expr =
|
||||
| EUnit of c_Unit
|
||||
| ETuple of tuple_expr
|
||||
| EPar of expr par reg
|
||||
| EFun of fun_expr reg
|
||||
|
||||
and annot_expr = (expr * type_expr)
|
||||
|
||||
@ -644,7 +649,8 @@ let rec expr_to_region = function
|
||||
| EUnit region
|
||||
| ECase {region;_}
|
||||
| ECond {region; _}
|
||||
| EPar {region; _} -> region
|
||||
| EPar {region; _}
|
||||
| EFun {region; _} -> region
|
||||
|
||||
and tuple_expr_to_region {region; _} = region
|
||||
|
||||
@ -752,7 +758,7 @@ let pattern_to_region = function
|
||||
| PTuple {region; _} -> region
|
||||
|
||||
let local_decl_to_region = function
|
||||
LocalFun {region; _}
|
||||
| LocalData LocalFun {region; _}
|
||||
| LocalData LocalConst {region; _}
|
||||
| LocalData LocalVar {region; _} -> region
|
||||
|
||||
|
@ -201,18 +201,23 @@ and type_tuple = (type_expr, comma) nsepseq par reg
|
||||
|
||||
(* Function declarations *)
|
||||
|
||||
and fun_decl ={
|
||||
kwd_function : kwd_function;
|
||||
name : variable;
|
||||
param : parameters;
|
||||
colon : colon;
|
||||
ret_type : type_expr;
|
||||
kwd_is : kwd_is;
|
||||
local_decls : local_decl list;
|
||||
block : block reg option;
|
||||
kwd_with : kwd_with option;
|
||||
return : expr;
|
||||
terminator : semi option }
|
||||
and fun_expr = {
|
||||
kwd_function : kwd_function;
|
||||
name : variable option;
|
||||
param : parameters;
|
||||
colon : colon;
|
||||
ret_type : type_expr;
|
||||
kwd_is : kwd_is;
|
||||
local_decls : local_decl list;
|
||||
block : block reg option;
|
||||
kwd_with : kwd_with option;
|
||||
return : expr;
|
||||
}
|
||||
|
||||
and fun_decl = {
|
||||
fun_expr : fun_expr reg ;
|
||||
terminator : semi option ;
|
||||
}
|
||||
|
||||
and parameters = (param_decl, semi) nsepseq par reg
|
||||
|
||||
@ -256,12 +261,12 @@ and statement =
|
||||
| Data of data_decl
|
||||
|
||||
and local_decl =
|
||||
LocalFun of fun_decl reg
|
||||
| LocalData of data_decl
|
||||
|
||||
and data_decl =
|
||||
LocalConst of const_decl reg
|
||||
| LocalVar of var_decl reg
|
||||
| LocalFun of fun_decl reg
|
||||
|
||||
and var_decl = {
|
||||
kwd_var : kwd_var;
|
||||
@ -454,6 +459,7 @@ and expr =
|
||||
| EUnit of c_Unit
|
||||
| ETuple of tuple_expr
|
||||
| EPar of expr par reg
|
||||
| EFun of fun_expr reg
|
||||
|
||||
and annot_expr = (expr * type_expr)
|
||||
|
||||
|
@ -239,17 +239,11 @@ field_decl:
|
||||
and value = {field_name = $1; colon = $2; field_type = $3}
|
||||
in {region; value} }
|
||||
|
||||
(* Function declarations *)
|
||||
|
||||
fun_decl:
|
||||
Function fun_name parameters COLON type_expr Is
|
||||
seq(local_decl)
|
||||
fun_expr:
|
||||
Function option(fun_name) parameters COLON type_expr Is
|
||||
block
|
||||
With expr option(SEMI) {
|
||||
let stop =
|
||||
match $11 with
|
||||
Some region -> region
|
||||
| None -> expr_to_region $10 in
|
||||
With expr {
|
||||
let stop = expr_to_region $9 in
|
||||
let region = cover $1 stop
|
||||
and value = {
|
||||
kwd_function = $1;
|
||||
@ -258,18 +252,15 @@ fun_decl:
|
||||
colon = $4;
|
||||
ret_type = $5;
|
||||
kwd_is = $6;
|
||||
local_decls = $7;
|
||||
block = Some $8;
|
||||
kwd_with = Some $9;
|
||||
return = $10;
|
||||
terminator = $11}
|
||||
local_decls = [];
|
||||
block = Some $7;
|
||||
kwd_with = Some $8;
|
||||
return = $9;
|
||||
}
|
||||
in {region;value}}
|
||||
| Function fun_name parameters COLON type_expr Is
|
||||
expr option(SEMI) {
|
||||
let stop =
|
||||
match $8 with
|
||||
Some region -> region
|
||||
| None -> expr_to_region $7 in
|
||||
| Function option(fun_name) parameters COLON type_expr Is
|
||||
expr {
|
||||
let stop = expr_to_region $7 in
|
||||
let region = cover $1 stop
|
||||
and value = {
|
||||
kwd_function = $1;
|
||||
@ -282,10 +273,36 @@ fun_decl:
|
||||
block = None;
|
||||
kwd_with = None;
|
||||
return = $7;
|
||||
terminator = $8;
|
||||
}
|
||||
in {region;value}}
|
||||
|
||||
|
||||
|
||||
(* Function declarations *)
|
||||
|
||||
fun_decl:
|
||||
fun_expr option(SEMI) {
|
||||
let stop =
|
||||
match $2 with
|
||||
Some region -> region
|
||||
| None -> $1.region in
|
||||
let region = cover $1.region stop
|
||||
and value = {
|
||||
fun_expr = $1;
|
||||
terminator = $2;
|
||||
}
|
||||
in {region;value}}
|
||||
|
||||
open_fun_decl:
|
||||
fun_expr {
|
||||
let region = $1.region
|
||||
and value = {
|
||||
fun_expr = $1;
|
||||
terminator = None;
|
||||
}
|
||||
in {region;value}}
|
||||
|
||||
|
||||
parameters:
|
||||
par(nsepseq(param_decl,SEMI)) { $1 }
|
||||
|
||||
@ -341,6 +358,7 @@ statement:
|
||||
open_data_decl:
|
||||
open_const_decl { LocalConst $1 }
|
||||
| open_var_decl { LocalVar $1 }
|
||||
| open_fun_decl { LocalFun $1 }
|
||||
|
||||
open_const_decl:
|
||||
Const unqualified_decl(EQ) {
|
||||
@ -370,14 +388,6 @@ open_var_decl:
|
||||
terminator = None}
|
||||
in {region; value}}
|
||||
|
||||
local_decl:
|
||||
fun_decl { LocalFun $1 }
|
||||
| data_decl { LocalData $1 }
|
||||
|
||||
data_decl:
|
||||
const_decl { LocalConst $1 }
|
||||
| var_decl { LocalVar $1 }
|
||||
|
||||
unqualified_decl(OP):
|
||||
var COLON type_expr OP expr {
|
||||
let region = expr_to_region $5
|
||||
@ -390,12 +400,6 @@ const_decl:
|
||||
}
|
||||
| open_const_decl { $1 }
|
||||
|
||||
var_decl:
|
||||
open_var_decl SEMI {
|
||||
let var_decl : AST.var_decl = $1.value in
|
||||
{$1 with value = {var_decl with terminator = Some $2}}
|
||||
}
|
||||
| open_var_decl { $1 }
|
||||
|
||||
instruction:
|
||||
conditional { Cond $1 }
|
||||
@ -683,6 +687,7 @@ expr:
|
||||
case(expr) { ECase ($1 expr_to_region) }
|
||||
| cond_expr { $1 }
|
||||
| disj_expr { $1 }
|
||||
| fun_expr { EFun $1 }
|
||||
|
||||
cond_expr:
|
||||
If expr Then expr option(SEMI) Else expr {
|
||||
|
@ -156,12 +156,13 @@ and print_type_tuple buffer {value; _} =
|
||||
print_nsepseq buffer "," print_type_expr inside;
|
||||
print_token buffer rpar ")"
|
||||
|
||||
and print_fun_decl buffer {value; _} =
|
||||
and print_fun_expr buffer {value; _} =
|
||||
let {kwd_function; name; param; colon;
|
||||
ret_type; kwd_is; local_decls;
|
||||
block; kwd_with; return; terminator} = value in
|
||||
block; kwd_with; return;} = value in
|
||||
let anonymous_name = Region.wrap_ghost "#anonymous" in
|
||||
print_token buffer kwd_function "function";
|
||||
print_var buffer name;
|
||||
print_var buffer @@ Simple_utils.Option.unopt ~default:anonymous_name name;
|
||||
print_parameters buffer param;
|
||||
print_token buffer colon ":";
|
||||
print_type_expr buffer ret_type;
|
||||
@ -173,7 +174,11 @@ and print_fun_decl buffer {value; _} =
|
||||
print_token buffer kwd_with "with";
|
||||
| None -> ();
|
||||
print_expr buffer return;
|
||||
print_terminator buffer terminator
|
||||
|
||||
and print_fun_decl buffer {value; _} =
|
||||
let {fun_expr ; terminator;} = value in
|
||||
print_fun_expr buffer fun_expr;
|
||||
print_terminator buffer terminator;
|
||||
|
||||
and print_parameters buffer {value; _} =
|
||||
let {lpar; inside; rpar} = value in
|
||||
@ -225,12 +230,12 @@ and print_local_decls buffer sequence =
|
||||
List.iter (print_local_decl buffer) sequence
|
||||
|
||||
and print_local_decl buffer = function
|
||||
LocalFun decl -> print_fun_decl buffer decl
|
||||
| LocalData decl -> print_data_decl buffer decl
|
||||
|
||||
and print_data_decl buffer = function
|
||||
LocalConst decl -> print_const_decl buffer decl
|
||||
| LocalVar decl -> print_var_decl buffer decl
|
||||
| LocalFun decl -> print_fun_decl buffer decl
|
||||
|
||||
and print_var_decl buffer {value; _} =
|
||||
let {kwd_var; name; colon; var_type;
|
||||
@ -402,6 +407,7 @@ and print_expr buffer = function
|
||||
| EUnit r -> print_token buffer r "Unit"
|
||||
| ETuple e -> print_tuple_expr buffer e
|
||||
| EPar e -> print_par_expr buffer e
|
||||
| EFun e -> print_fun_expr buffer e
|
||||
|
||||
and print_annot_expr buffer (expr , type_expr) =
|
||||
print_expr buffer expr;
|
||||
@ -795,7 +801,7 @@ and pp_declaration buffer ~pad:(_,pc as pad) = function
|
||||
pp_const_decl buffer ~pad value
|
||||
| FunDecl {value; region} ->
|
||||
pp_loc_node buffer ~pad "FunDecl" region;
|
||||
pp_fun_decl buffer ~pad value
|
||||
pp_fun_expr buffer ~pad value.fun_expr.value
|
||||
|
||||
and pp_const_decl buffer ~pad:(_,pc) decl =
|
||||
pp_ident buffer ~pad:(mk_pad 3 0 pc) decl.name;
|
||||
@ -861,12 +867,13 @@ and pp_type_tuple buffer ~pad:(_,pc) {value; _} =
|
||||
pp_type_expr buffer ~pad:(mk_pad len rank pc)
|
||||
in List.iteri (List.length components |> apply) components
|
||||
|
||||
and pp_fun_decl buffer ~pad:(_,pc) decl =
|
||||
and pp_fun_expr buffer ~pad:(_,pc) decl =
|
||||
let fields =
|
||||
if decl.local_decls = [] then 5 else 6 in
|
||||
let () =
|
||||
let pad = mk_pad fields 0 pc in
|
||||
pp_ident buffer ~pad decl.name in
|
||||
let anonymous_name = Region.wrap_ghost "#anonymous" in
|
||||
pp_ident buffer ~pad @@ Simple_utils.Option.unopt ~default:anonymous_name decl.name in
|
||||
let () =
|
||||
let pad = mk_pad fields 1 pc in
|
||||
pp_node buffer ~pad "<parameters>";
|
||||
@ -1294,9 +1301,6 @@ and pp_local_decls buffer ~pad:(_,pc) decls =
|
||||
in List.iteri (List.length decls |> apply) decls
|
||||
|
||||
and pp_local_decl buffer ~pad:(_,pc as pad) = function
|
||||
LocalFun {value; region} ->
|
||||
pp_loc_node buffer ~pad "LocalFun" region;
|
||||
pp_fun_decl buffer ~pad value
|
||||
| LocalData data ->
|
||||
pp_node buffer ~pad "LocalData";
|
||||
pp_data_decl buffer ~pad:(mk_pad 1 0 pc) data
|
||||
@ -1308,6 +1312,9 @@ and pp_data_decl buffer ~pad = function
|
||||
| LocalVar {value; region} ->
|
||||
pp_loc_node buffer ~pad "LocalVar" region;
|
||||
pp_var_decl buffer ~pad value
|
||||
| LocalFun {value; region} ->
|
||||
pp_loc_node buffer ~pad "LocalFun" region;
|
||||
pp_fun_expr buffer ~pad value.fun_expr.value
|
||||
|
||||
and pp_var_decl buffer ~pad:(_,pc) decl =
|
||||
pp_ident buffer ~pad:(mk_pad 3 0 pc) decl.name;
|
||||
@ -1368,6 +1375,9 @@ and pp_expr buffer ~pad:(_,pc as pad) = function
|
||||
| EPar {value; region} ->
|
||||
pp_loc_node buffer ~pad "EPar" region;
|
||||
pp_expr buffer ~pad:(mk_pad 1 0 pc) value.inside
|
||||
| EFun {value; region} ->
|
||||
pp_loc_node buffer ~pad "EFun" region;
|
||||
pp_fun_expr ~pad buffer value;
|
||||
|
||||
and pp_list_expr buffer ~pad:(_,pc as pad) = function
|
||||
ECons {value; region} ->
|
||||
|
@ -117,6 +117,22 @@ module Errors = struct
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let unexpected_anonymous_function loc =
|
||||
let title () = "unexpected anonymous function" in
|
||||
let message () = "you provided a function declaration without name" in
|
||||
let data = [
|
||||
("loc" , fun () -> Format.asprintf "%a" Location.pp @@ loc)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
let unexpected_named_function loc =
|
||||
let title () = "unexpected named function" in
|
||||
let message () = "you provided a function expression with a name (remove it)" in
|
||||
let data = [
|
||||
("loc" , fun () -> Format.asprintf "%a" Location.pp @@ loc)
|
||||
] in
|
||||
error ~data title message
|
||||
|
||||
(* Logging *)
|
||||
|
||||
let simplifying_instruction t =
|
||||
@ -410,6 +426,13 @@ let rec simpl_expression (t:Raw.expr) : expr result =
|
||||
let%bind index = simpl_expression lu.index.value.inside in
|
||||
return @@ e_look_up ~loc path index
|
||||
)
|
||||
| EFun f -> (
|
||||
let (f , loc) = r_split f in
|
||||
let%bind ((name_opt , _ty_opt) , f') = simpl_fun_expression ~loc f in
|
||||
match name_opt with
|
||||
| None -> return @@ f'
|
||||
| Some _ -> fail @@ unexpected_named_function loc
|
||||
)
|
||||
|
||||
and simpl_logic_expression (t:Raw.logic_expr) : expression result =
|
||||
let return x = ok x in
|
||||
@ -497,10 +520,6 @@ and simpl_local_declaration : Raw.local_decl -> _ result = fun t ->
|
||||
match t with
|
||||
| LocalData d ->
|
||||
simpl_data_declaration d
|
||||
| LocalFun f ->
|
||||
let (f , loc) = r_split f in
|
||||
let%bind (name , e) = simpl_fun_declaration ~loc f in
|
||||
return_let_in ~loc name e
|
||||
|
||||
and simpl_data_declaration : Raw.data_decl -> _ result = fun t ->
|
||||
match t with
|
||||
@ -516,6 +535,11 @@ and simpl_data_declaration : Raw.data_decl -> _ result = fun t ->
|
||||
let%bind t = simpl_type_expression x.const_type in
|
||||
let%bind expression = simpl_expression x.init in
|
||||
return_let_in ~loc (name , Some t) expression
|
||||
| LocalFun f ->
|
||||
let (f , loc) = r_split f in
|
||||
let%bind ((name_opt , ty_opt) , e) = simpl_fun_expression ~loc f.fun_expr.value in
|
||||
let%bind name = trace_option (unexpected_anonymous_function loc) name_opt in
|
||||
return_let_in ~loc (name , ty_opt) e
|
||||
|
||||
and simpl_param : Raw.param_decl -> (type_name * type_expression) result =
|
||||
fun t ->
|
||||
@ -531,11 +555,11 @@ and simpl_param : Raw.param_decl -> (type_name * type_expression) result =
|
||||
let%bind type_expression = simpl_type_expression c.param_type in
|
||||
ok (type_name , type_expression)
|
||||
|
||||
and simpl_fun_declaration :
|
||||
loc:_ -> Raw.fun_decl -> ((name * type_expression option) * expression) result =
|
||||
and simpl_fun_expression :
|
||||
loc:_ -> Raw.fun_expr -> ((name option * type_expression option) * expression) result =
|
||||
fun ~loc x ->
|
||||
let open! Raw in
|
||||
let {name;param;ret_type;local_decls;block;return} : fun_decl = x in
|
||||
let {name;param;ret_type;local_decls;block;return} : fun_expr = x in
|
||||
let statements =
|
||||
match block with
|
||||
| Some block -> npseq_to_list block.value.statements
|
||||
@ -544,7 +568,7 @@ and simpl_fun_declaration :
|
||||
(match param.value.inside with
|
||||
a, [] -> (
|
||||
let%bind input = simpl_param a in
|
||||
let name = name.value in
|
||||
let name = Option.map (fun (x : _ reg) -> x.value) name in
|
||||
let (binder , input_type) = input in
|
||||
let%bind local_declarations =
|
||||
bind_map_list simpl_local_declaration local_decls in
|
||||
@ -591,7 +615,8 @@ and simpl_fun_declaration :
|
||||
let expression =
|
||||
e_lambda ~loc binder (Some input_type) (Some output_type) result in
|
||||
let type_annotation = Some (T_function (input_type, output_type)) in
|
||||
ok ((name.value , type_annotation) , expression)
|
||||
let name = Option.map (fun (x : _ reg) -> x.value) name in
|
||||
ok ((name , type_annotation) , expression)
|
||||
)
|
||||
)
|
||||
and simpl_declaration : Raw.declaration -> declaration Location.wrap result =
|
||||
@ -614,7 +639,8 @@ and simpl_declaration : Raw.declaration -> declaration Location.wrap result =
|
||||
bind_map_location simpl_const_decl (Location.lift_region x)
|
||||
| FunDecl x -> (
|
||||
let (x , loc) = r_split x in
|
||||
let%bind ((name , ty_opt) , expr) = simpl_fun_declaration ~loc x in
|
||||
let%bind ((name_opt , ty_opt) , expr) = simpl_fun_expression ~loc x.fun_expr.value in
|
||||
let%bind name = trace_option (unexpected_anonymous_function loc) name_opt in
|
||||
ok @@ Location.wrap ~loc (Declaration_constant (name , ty_opt , expr))
|
||||
)
|
||||
|
||||
|
@ -2,8 +2,8 @@ type storage_ is big_map(int, int) * unit
|
||||
type foo is big_map(int, int)
|
||||
|
||||
function main(const p : unit; const s : storage_) : list(operation) * storage_ is
|
||||
var toto : option (int) := Some(0);
|
||||
block {
|
||||
var toto : option (int) := Some(0);
|
||||
toto := s.0[23];
|
||||
s.0[2] := 444;
|
||||
}
|
||||
@ -32,5 +32,5 @@ function mutimaps (const m : foo ; const n : foo) : foo is block
|
||||
{
|
||||
var bar : foo := m ;
|
||||
bar[42] := 0 ;
|
||||
n[42] := get_force(42, bar) ;
|
||||
} with n
|
||||
n[42] := get_force(42, bar) ;
|
||||
} with n
|
||||
|
@ -1,4 +1,5 @@
|
||||
function foo (const i : int) : int is
|
||||
function bar (const j : int) : int is
|
||||
block { skip } with i + j ;
|
||||
block { skip } with bar (i)
|
||||
block {
|
||||
function bar (const j : int) : int is
|
||||
i + j ;
|
||||
} with bar (i)
|
||||
|
@ -1,5 +1,6 @@
|
||||
function foobar(const i : int) : int is
|
||||
const j : int = 3 ;
|
||||
function toto(const k : int) : int is
|
||||
block { skip } with i + j + k ;
|
||||
block { skip } with toto(42)
|
||||
block {
|
||||
const j : int = 3 ;
|
||||
function toto(const k : int) : int is
|
||||
i + j + k ;
|
||||
} with toto(42)
|
||||
|
@ -1,10 +1,12 @@
|
||||
// This might seem like it's covered by induction with closure-2.ligo
|
||||
// But it exists to prevent a regression on the bug patched by:
|
||||
// But it exists to prevent a regression on the bug patched by:
|
||||
// https://gitlab.com/ligolang/ligo/commit/faf3bbc06106de98189f1c1673bd57e78351dc7e
|
||||
|
||||
function foobar(const i : int) : int is
|
||||
const j : int = 3 ;
|
||||
const k : int = 4 ;
|
||||
function toto(const l : int) : int is
|
||||
block { skip } with i + j + k + l;
|
||||
block { skip } with toto(42)
|
||||
block {
|
||||
const j : int = 3 ;
|
||||
const k : int = 4 ;
|
||||
function toto(const l : int) : int is
|
||||
i + j + k + l;
|
||||
|
||||
} with toto(42)
|
||||
|
@ -1,6 +1,7 @@
|
||||
function toto (const i : int) : int is
|
||||
function tata (const j : int) : int is
|
||||
block { skip } with i + j ;
|
||||
function titi (const j : int) : int is
|
||||
block { skip } with i + j ;
|
||||
block { skip } with tata(i) + titi(i)
|
||||
block {
|
||||
function tata (const j : int) : int is
|
||||
i + j ;
|
||||
function titi (const j : int) : int is
|
||||
i + j ;
|
||||
} with tata(i) + titi(i)
|
||||
|
@ -1,8 +1,8 @@
|
||||
// Test if conditional in PascaLIGO
|
||||
|
||||
function main (const i : int) : int is
|
||||
var result : int := 23 ;
|
||||
begin
|
||||
var result : int := 23 ;
|
||||
if i = 2 then
|
||||
result := 42
|
||||
else
|
||||
@ -10,7 +10,7 @@ function main (const i : int) : int is
|
||||
end with result
|
||||
|
||||
function foo (const b : bool) : int is
|
||||
var x : int := 41 ;
|
||||
begin
|
||||
var x : int := 41 ;
|
||||
x := 1 + (if b then x else main(x)) ;
|
||||
end with x
|
||||
end with x
|
||||
|
@ -3,20 +3,17 @@ type pii is (int*int)
|
||||
type ppi is record x:pii; y:pii end
|
||||
type ppp is (ppi*ppi)
|
||||
|
||||
function main (const toto : unit) : int is
|
||||
var a : ppp :=
|
||||
(
|
||||
record
|
||||
x = (0,1);
|
||||
y = (10,11);
|
||||
end
|
||||
,
|
||||
record
|
||||
x = (100,101);
|
||||
y = (110,111);
|
||||
end
|
||||
)
|
||||
begin
|
||||
a.0.x.0 := 2;
|
||||
const b:int = a.0.x.0;
|
||||
end with b
|
||||
function main (const toto : unit) : int is block {
|
||||
var a : ppp := (
|
||||
record
|
||||
x = (0,1);
|
||||
y = (10,11);
|
||||
end ,
|
||||
record
|
||||
x = (100,101);
|
||||
y = (110,111);
|
||||
end
|
||||
) ;
|
||||
a.0.x.0 := 2;
|
||||
const b:int = a.0.x.0;
|
||||
} with b
|
||||
|
@ -12,8 +12,8 @@ function main (const p : param; const s : unit) : list(operation) * unit is
|
||||
with ((nil : list(operation)), s)
|
||||
|
||||
function foobar (const i : int) : int is
|
||||
var p : param := Zero (42n) ;
|
||||
block {
|
||||
var p : param := Zero (42n) ;
|
||||
if i > 0 then block {
|
||||
i := i + 1 ;
|
||||
if i > 10 then block {
|
||||
|
1
src/test/contracts/function-anon.ligo
Normal file
1
src/test/contracts/function-anon.ligo
Normal file
@ -0,0 +1 @@
|
||||
const x : int = (function (const i : int) : int is i + 1)(41)
|
@ -1,9 +1,9 @@
|
||||
// Test a PascaLIGO function with more complex logic than function.ligo
|
||||
|
||||
function main (const i : int) : int is
|
||||
var j : int := 0 ;
|
||||
var k : int := 1 ;
|
||||
begin
|
||||
var j : int := 0 ;
|
||||
var k : int := 1 ;
|
||||
j := k + i ;
|
||||
k := i + j ;
|
||||
end with (k + j)
|
||||
|
@ -1,49 +1,50 @@
|
||||
// Test a PascaLIGO function which takes another PascaLIGO function as an argument
|
||||
function foobar (const i : int) : int is
|
||||
function foo (const i : int) : int is
|
||||
block { skip } with i ;
|
||||
function bar (const f : int -> int) : int is
|
||||
block { skip } with f ( i ) ;
|
||||
block { skip } with bar (foo) ;
|
||||
block {
|
||||
function foo (const i : int) : int is
|
||||
i ;
|
||||
function bar (const f : int -> int) : int is
|
||||
f ( i ) ;
|
||||
} with bar (foo) ;
|
||||
|
||||
// higher order function with more than one argument
|
||||
function higher2(const i: int; const f: int -> int): int is
|
||||
block {
|
||||
block {
|
||||
const ii: int = f(i)
|
||||
} with ii
|
||||
|
||||
function foobar2 (const i : int) : int is
|
||||
function foo2 (const i : int) : int is
|
||||
block { skip } with i;
|
||||
block { skip } with higher2(i,foo2)
|
||||
block {
|
||||
function foo2 (const i : int) : int is
|
||||
i;
|
||||
} with higher2(i,foo2)
|
||||
|
||||
const a : int = 0;
|
||||
function foobar3 (const i : int) : int is
|
||||
function foo2 (const i : int) : int is
|
||||
block { skip } with (a+i);
|
||||
block { skip } with higher2(i,foo2)
|
||||
block {
|
||||
function foo2 (const i : int) : int is
|
||||
(a+i);
|
||||
} with higher2(i,foo2)
|
||||
|
||||
function f (const i : int) : int is
|
||||
block { skip }
|
||||
with i
|
||||
i
|
||||
|
||||
function g (const i : int) : int is
|
||||
block { skip }
|
||||
with f(i)
|
||||
f(i)
|
||||
|
||||
function foobar4 (const i : int) : int is
|
||||
block { skip }
|
||||
with g(g(i))
|
||||
g(g(i))
|
||||
|
||||
function higher3(const i: int; const f: int -> int; const g: int -> int): int is
|
||||
block {
|
||||
block {
|
||||
const ii: int = f(g(i));
|
||||
} with ii
|
||||
|
||||
function foobar5 (const i : int) : int is
|
||||
const a : int = 0;
|
||||
function foo (const i : int) : int is
|
||||
block { skip } with (a+i);
|
||||
function goo (const i : int) : int is
|
||||
block { skip } with foo(i);
|
||||
block { skip } with higher3(i,foo,goo)
|
||||
block {
|
||||
const a : int = 0;
|
||||
function foo (const i : int) : int is
|
||||
(a+i);
|
||||
function goo (const i : int) : int is
|
||||
foo(i);
|
||||
} with higher3(i,foo,goo)
|
||||
|
@ -2,5 +2,6 @@ function f (const x : unit) : unit is
|
||||
begin skip end with unit
|
||||
|
||||
function main (const p : unit ; const s : unit) : unit is
|
||||
var y : unit := f(unit) ;
|
||||
begin skip end with y
|
||||
begin
|
||||
var y : unit := f(unit) ;
|
||||
end with y
|
||||
|
@ -25,15 +25,16 @@ const bl : foobar = list
|
||||
end
|
||||
|
||||
function iter_op (const s : list(int)) : int is
|
||||
var r : int := 0 ;
|
||||
function aggregate (const i : int) : unit is
|
||||
begin
|
||||
r := r + i ;
|
||||
end with unit
|
||||
begin
|
||||
var r : int := 0 ;
|
||||
function aggregate (const i : int) : unit is
|
||||
begin
|
||||
r := r + i ;
|
||||
end with unit ;
|
||||
list_iter(s , aggregate) ;
|
||||
end with r
|
||||
|
||||
function map_op (const s : list(int)) : list(int) is
|
||||
function increment (const i : int) : int is block { skip } with i + 1
|
||||
block { skip } with list_map(s , increment)
|
||||
block {
|
||||
function increment (const i : int) : int is block { skip } with i + 1
|
||||
} with list_map(s , increment)
|
||||
|
@ -48,23 +48,25 @@ function get_ (const m : foobar) : option(int) is
|
||||
end with map_get(42 , m)
|
||||
|
||||
function iter_op (const m : foobar) : unit is
|
||||
function aggregate (const i : int ; const j : int) : unit is block
|
||||
{ if (i=j) then skip else failwith("fail") } with unit ;
|
||||
block {skip}
|
||||
block {
|
||||
function aggregate (const i : int ; const j : int) : unit is block
|
||||
{ if (i=j) then skip else failwith("fail") } with unit ;
|
||||
// map_iter(m , aggregate) ;
|
||||
with map_iter(m, aggregate) ;
|
||||
} with map_iter(m, aggregate) ;
|
||||
|
||||
function map_op (const m : foobar) : foobar is
|
||||
function increment (const i : int ; const j : int) : int is block { skip } with j + 1 ;
|
||||
block { skip } with map_map(m , increment) ;
|
||||
block {
|
||||
function increment (const i : int ; const j : int) : int is block { skip } with j + 1 ;
|
||||
} with map_map(m , increment) ;
|
||||
|
||||
function fold_op (const m : foobar) : int is
|
||||
function aggregate (const i : int ; const j : (int * int)) : int is block { skip } with i + j.0 + j.1 ;
|
||||
block { skip } with map_fold(m , 10 , aggregate)
|
||||
block {
|
||||
function aggregate (const i : int ; const j : (int * int)) : int is block { skip } with i + j.0 + j.1 ;
|
||||
} with map_fold(m , 10 , aggregate)
|
||||
|
||||
function deep_op (var m : foobar) : foobar is
|
||||
var coco : (int*foobar) := (0, m);
|
||||
block {
|
||||
remove 42 from map coco.1 ;
|
||||
coco.1[32] := 16 ;
|
||||
} with coco.1
|
||||
block {
|
||||
var coco : (int*foobar) := (0, m);
|
||||
remove 42 from map coco.1 ;
|
||||
coco.1[32] := 16 ;
|
||||
} with coco.1
|
||||
|
@ -1,39 +1,36 @@
|
||||
// Test the pattern matching functionality of PascaLIGO
|
||||
|
||||
function match_bool (const i : int) : int is
|
||||
var result : int := 23 ;
|
||||
begin
|
||||
case i = 2 of
|
||||
| True -> result := 42
|
||||
| False -> result := 0
|
||||
end
|
||||
var result : int := 23 ;
|
||||
case i = 2 of
|
||||
| True -> result := 42
|
||||
| False -> result := 0
|
||||
end
|
||||
end with result
|
||||
|
||||
function match_option (const o : option(int)) : int is
|
||||
var result : int := 23 ;
|
||||
begin
|
||||
case o of
|
||||
| None -> skip
|
||||
| Some (s) -> result := s
|
||||
end
|
||||
var result : int := 23 ;
|
||||
case o of
|
||||
| None -> skip
|
||||
| Some (s) -> result := s
|
||||
end
|
||||
end with result
|
||||
|
||||
function match_expr_bool (const i : int) : int is
|
||||
begin skip end with
|
||||
case i = 2 of
|
||||
| True -> 42
|
||||
| False -> 0
|
||||
end
|
||||
|
||||
function match_expr_option (const o : option(int)) : int is
|
||||
begin skip end with
|
||||
case o of
|
||||
| None -> 42
|
||||
| Some (s) -> s
|
||||
end
|
||||
|
||||
function match_expr_list (const l : list(int)) : int is
|
||||
begin skip end with
|
||||
case l of
|
||||
| nil -> -1
|
||||
| hd # tl -> hd
|
||||
|
@ -6,10 +6,8 @@ const s : foobar = Some(42)
|
||||
const n : foobar = None
|
||||
|
||||
function assign (var m : int) : foobar is
|
||||
var coco : foobar := None;
|
||||
block
|
||||
{
|
||||
coco := Some(m);
|
||||
coco := None;
|
||||
}
|
||||
with coco
|
||||
block {
|
||||
var coco : foobar := None;
|
||||
coco := Some(m);
|
||||
coco := None;
|
||||
} with coco
|
||||
|
@ -1,16 +1,17 @@
|
||||
// Test set iteration in PascaLIGO
|
||||
|
||||
function iter_op (const s : set(int)) : int is
|
||||
var r : int := 0 ;
|
||||
function aggregate (const i : int) : unit is
|
||||
begin
|
||||
r := r + i ;
|
||||
end with unit
|
||||
begin
|
||||
var r : int := 0 ;
|
||||
function aggregate (const i : int) : unit is
|
||||
begin
|
||||
r := r + i ;
|
||||
end with unit ;
|
||||
set_iter(s , aggregate) ;
|
||||
end with r
|
||||
|
||||
function fold_op (const s : set(int)) : int is
|
||||
function aggregate (const i : int ; const j : int) : int is
|
||||
block { skip } with i + j
|
||||
block { skip } with set_fold(s , 15 , aggregate)
|
||||
block {
|
||||
function aggregate (const i : int ; const j : int) : int is
|
||||
i + j
|
||||
} with set_fold(s , 15 , aggregate)
|
||||
|
@ -1,4 +1,5 @@
|
||||
function foo (const i : int) : int is
|
||||
function bar (const i : int) : int is
|
||||
block { skip } with i ;
|
||||
block { skip } with bar (0)
|
||||
block {
|
||||
function bar (const i : int) : int is
|
||||
i ;
|
||||
} with bar (0)
|
||||
|
@ -7,15 +7,15 @@ end
|
||||
type mpi is map(string,int)
|
||||
|
||||
function main (const toto : tpi) : int is
|
||||
var a : tpi := toto;
|
||||
var b : rpi := record x = 0; y=1 ; end;
|
||||
var m : mpi := map "y" -> 1; end;
|
||||
begin
|
||||
a.0 := 2;
|
||||
b.x := a.0;
|
||||
m["x"] := b.x;
|
||||
end with
|
||||
case m["x"] of
|
||||
| Some (s) -> s
|
||||
| None -> 42
|
||||
end
|
||||
begin
|
||||
var a : tpi := toto;
|
||||
var b : rpi := record x = 0; y=1 ; end;
|
||||
var m : mpi := map "y" -> 1; end;
|
||||
a.0 := 2;
|
||||
b.x := a.0;
|
||||
m["x"] := b.x;
|
||||
end with
|
||||
case m["x"] of
|
||||
| Some (s) -> s
|
||||
| None -> 42
|
||||
end
|
||||
|
@ -3,15 +3,13 @@ type action is
|
||||
| Increment of int
|
||||
| Decrement of int
|
||||
|
||||
function add (const a : int ; const b : int) : int is
|
||||
block { skip } with a + b
|
||||
function add (const a : int ; const b : int) : int is a + b
|
||||
|
||||
function subtract (const a : int ; const b : int) : int is
|
||||
block { skip } with a - b
|
||||
function subtract (const a : int ; const b : int) : int is a - b
|
||||
|
||||
// real entrypoint that re-routes the flow based on the action provided
|
||||
function main (const p : action ; const s : int) : (list(operation) * int) is
|
||||
block {skip} with ((nil : list(operation)),
|
||||
function main (const p : action ; const s : int) : (list(operation) * int) is
|
||||
((nil : list(operation)),
|
||||
case p of
|
||||
| Increment (n) -> add (s, n)
|
||||
| Decrement (n) -> subtract (s, n)
|
||||
|
@ -52,6 +52,13 @@ let complex_function () : unit result =
|
||||
let make_expect = fun n -> (3 * n + 2) in
|
||||
expect_eq_n_int program "main" make_expect
|
||||
|
||||
let anon_function () : unit result =
|
||||
let%bind program = type_file "./contracts/function-anon.ligo" in
|
||||
let%bind () =
|
||||
expect_eq_evaluate program "x" (e_int 42)
|
||||
in
|
||||
ok ()
|
||||
|
||||
let application () : unit result =
|
||||
let%bind program = type_file "./contracts/application.ligo" in
|
||||
let%bind () =
|
||||
@ -1196,6 +1203,7 @@ let main = test_suite "Integration (End to End)" [
|
||||
test "assign" assign ;
|
||||
test "declaration local" declaration_local ;
|
||||
test "complex function" complex_function ;
|
||||
test "anon function" anon_function ;
|
||||
test "various applications" application ;
|
||||
test "closure" closure ;
|
||||
test "shared function" shared_function ;
|
||||
|
2
vendors/ligo-utils/simple-utils/region.ml
vendored
2
vendors/ligo-utils/simple-utils/region.ml
vendored
@ -108,6 +108,8 @@ let make ~(start: Pos.t) ~(stop: Pos.t) =
|
||||
|
||||
let ghost = make ~start:Pos.ghost ~stop:Pos.ghost
|
||||
|
||||
let wrap_ghost value = {value ; region = ghost}
|
||||
|
||||
let min = make ~start:Pos.min ~stop:Pos.min
|
||||
|
||||
(* Comparisons *)
|
||||
|
5
vendors/ligo-utils/simple-utils/region.mli
vendored
5
vendors/ligo-utils/simple-utils/region.mli
vendored
@ -96,6 +96,11 @@ val make : start:Pos.t -> stop:Pos.t -> t
|
||||
|
||||
val ghost : t (* Two [Pos.ghost] positions *)
|
||||
|
||||
(* This wraps a value with a ghost region. *)
|
||||
|
||||
val wrap_ghost : 'a -> 'a reg
|
||||
|
||||
|
||||
(* Occasionnally, we may need a minimum region. It is here made of two
|
||||
minimal positions. *)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user