From 812834656a1c5e211db8d18774edee8abf8ceb95 Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Wulfman Date: Thu, 9 Jan 2020 18:23:37 +0100 Subject: [PATCH 1/3] Updates for OcamLIGO and PascaLIGO --- src/passes/1-parser/cameligo/AST.ml | 19 +- src/passes/1-parser/cameligo/AST.mli | 367 -------------------- src/passes/1-parser/cameligo/Parser.mly | 14 + src/passes/1-parser/cameligo/ParserLog.ml | 26 ++ src/passes/1-parser/pascaligo/AST.ml | 13 +- src/passes/1-parser/pascaligo/Parser.mly | 11 + src/passes/1-parser/pascaligo/ParserLog.ml | 14 + src/passes/2-simplify/cameligo.ml | 35 ++ src/passes/2-simplify/pascaligo.ml | 19 + src/passes/3-self_ast_simplified/helpers.ml | 14 + src/passes/4-typer-new/typer.ml | 17 + src/passes/4-typer-old/typer.ml | 25 ++ src/passes/6-transpiler/transpiler.ml | 19 +- src/passes/7-self_mini_c/helpers.ml | 14 + src/passes/7-self_mini_c/self_mini_c.ml | 4 + src/passes/7-self_mini_c/subst.ml | 9 + src/passes/8-compiler/compiler_program.ml | 26 ++ src/stages/ast_simplified/PP.ml | 1 + src/stages/ast_simplified/combinators.ml | 3 + src/stages/ast_simplified/combinators.mli | 1 + src/stages/ast_simplified/misc.ml | 13 + src/stages/ast_simplified/types.ml | 3 +- src/stages/ast_typed/PP.ml | 1 + src/stages/ast_typed/misc.ml | 16 + src/stages/ast_typed/misc_smart.ml | 8 + src/stages/ast_typed/types.ml | 1 + src/stages/mini_c/PP.ml | 2 + src/stages/mini_c/misc.ml | 3 +- src/stages/mini_c/types.ml | 1 + src/stages/typesystem/misc.ml | 4 + src/test/contracts/record.ligo | 2 +- src/test/contracts/record.mligo | 2 +- src/test/integration_tests.ml | 38 ++ 33 files changed, 369 insertions(+), 376 deletions(-) delete mode 100644 src/passes/1-parser/cameligo/AST.mli diff --git a/src/passes/1-parser/cameligo/AST.ml b/src/passes/1-parser/cameligo/AST.ml index 65c07a49d..f8b008a9a 100644 --- a/src/passes/1-parser/cameligo/AST.ml +++ b/src/passes/1-parser/cameligo/AST.ml @@ -232,8 +232,9 @@ and expr = | EString of string_expr | EList of list_expr | EConstr of constr_expr -| ERecord of field_assign reg ne_injection reg +| ERecord of record reg | EProj of projection reg +| EUpdate of update reg | EVar of variable | ECall of (expr * expr nseq) reg | EBytes of (string * Hex.t) reg @@ -316,6 +317,7 @@ and comp_expr = | Equal of equal bin_op reg | Neq of neq bin_op reg +and record = field_assign reg ne_injection and projection = { struct_name : variable; selector : dot; @@ -332,6 +334,15 @@ and field_assign = { field_expr : expr } +and update = { + record : path; + kwd_with : kwd_with; + updates : record reg; +} +and path = + Name of variable +| Path of projection reg + and 'a case = { kwd_match : kwd_match; expr : expr; @@ -443,8 +454,12 @@ let expr_to_region = function | ECond {region;_} | ETuple {region;_} | ECase {region;_} | ECall {region;_} | EVar {region; _} | EProj {region; _} | EUnit {region;_} | EPar {region;_} | EBytes {region; _} -| ESeq {region; _} | ERecord {region; _} -> region +| ESeq {region; _} | ERecord {region; _} | EUpdate {region; _} -> region let selection_to_region = function FieldName f -> f.region | Component c -> c.region + +let path_to_region = function + Name var -> var.region +| Path {region; _} -> region diff --git a/src/passes/1-parser/cameligo/AST.mli b/src/passes/1-parser/cameligo/AST.mli deleted file mode 100644 index c00771ef8..000000000 --- a/src/passes/1-parser/cameligo/AST.mli +++ /dev/null @@ -1,367 +0,0 @@ -(* Abstract Syntax Tree (AST) for Cameligo *) - -[@@@warning "-30"] - -open Utils - -(* Regions - - The AST carries all the regions where tokens have been found by the - lexer, plus additional regions corresponding to whole subtrees - (like entire expressions, patterns etc.). These regions are needed - for error reporting and source-to-source transformations. To make - these pervasive regions more legible, we define singleton types for - the symbols, keywords etc. with suggestive names like "kwd_and" - denoting the _region_ of the occurrence of the keyword "and". -*) - -type 'a reg = 'a Region.reg - -val last : ('a -> Region.t) -> 'a list -> Region.t -val nsepseq_to_region : ('a -> Region.t) -> ('a,'sep) nsepseq -> Region.t - -(* Some keywords of OCaml *) - -type keyword = Region.t -type kwd_and = Region.t -type kwd_begin = Region.t -type kwd_else = Region.t -type kwd_end = Region.t -type kwd_false = Region.t -type kwd_fun = Region.t -type kwd_if = Region.t -type kwd_in = Region.t -type kwd_let = Region.t -type kwd_let_entry = Region.t -type kwd_match = Region.t -type kwd_mod = Region.t -type kwd_not = Region.t -type kwd_of = Region.t -type kwd_or = Region.t -type kwd_then = Region.t -type kwd_true = Region.t -type kwd_type = Region.t -type kwd_with = Region.t - -(* Data constructors *) - -type c_None = Region.t -type c_Some = Region.t - -(* Symbols *) - -type arrow = Region.t (* "->" *) -type cons = Region.t (* "::" *) -type cat = Region.t (* "^" *) -type append = Region.t (* "@" *) -type dot = Region.t (* "." *) - -(* Arithmetic operators *) - -type minus = Region.t (* "-" *) -type plus = Region.t (* "+" *) -type slash = Region.t (* "/" *) -type times = Region.t (* "*" *) - -(* Boolean operators *) - -type bool_or = Region.t (* "||" *) -type bool_and = Region.t (* "&&" *) - -(* Comparisons *) - -type equal = Region.t (* "=" *) -type neq = Region.t (* "<>" *) -type lt = Region.t (* "<" *) -type gt = Region.t (* ">" *) -type leq = Region.t (* "=<" *) -type geq = Region.t (* ">=" *) - -(* Compounds *) - -type lpar = Region.t (* "(" *) -type rpar = Region.t (* ")" *) -type lbracket = Region.t (* "[" *) -type rbracket = Region.t (* "]" *) -type lbrace = Region.t (* "{" *) -type rbrace = Region.t (* "}" *) - -(* Separators *) - -type comma = Region.t (* "," *) -type semi = Region.t (* ";" *) -type vbar = Region.t (* "|" *) -type colon = Region.t - -(* Wildcard *) - -type wild = Region.t (* "_" *) - -(* Literals *) - -type variable = string reg -type fun_name = string reg -type type_name = string reg -type field_name = string reg -type type_constr = string reg -type constr = string reg - -(* Parentheses *) - -type 'a par = { - lpar : lpar; - inside : 'a; - rpar : rpar -} - -type the_unit = lpar * rpar - -(* The Abstract Syntax Tree (finally) *) - -type t = { - decl : declaration nseq; - eof : eof -} - -and ast = t - -and eof = Region.t - -and declaration = - Let of (kwd_let * let_binding) reg (* let x = e *) -| TypeDecl of type_decl reg (* type ... *) - -(* Non-recursive values *) - -and let_binding = { (* p = e p : t = e *) - binders : pattern nseq; - lhs_type : (colon * type_expr) option; - eq : equal; - let_rhs : expr -} - -(* Recursive types *) - -and type_decl = { - kwd_type : kwd_type; - name : type_name; - eq : equal; - type_expr : type_expr -} - -and type_expr = - TProd of cartesian -| TSum of (variant reg, vbar) nsepseq reg -| TRecord of field_decl reg ne_injection reg -| TApp of (type_constr * type_tuple) reg -| TFun of (type_expr * arrow * type_expr) reg -| TPar of type_expr par reg -| TVar of variable - -and cartesian = (type_expr, times) nsepseq reg - -and variant = { - constr : constr; - arg : (kwd_of * type_expr) option -} - -and field_decl = { - field_name : field_name; - colon : colon; - field_type : type_expr -} - -and type_tuple = (type_expr, comma) nsepseq par reg - -and pattern = - PConstr of constr_pattern (* True () None A B(3,"") *) -| PUnit of the_unit reg (* () *) -| PFalse of kwd_false (* false *) -| PTrue of kwd_true (* true *) -| PVar of variable (* x *) -| PInt of (Lexer.lexeme * Z.t) reg (* 7 *) -| PNat of (Lexer.lexeme * Z.t) reg (* 7p 7n *) -| PBytes of (Lexer.lexeme * Hex.t) reg (* 0xAA0F *) -| PString of string reg (* "foo" *) -| PWild of wild (* _ *) -| PList of list_pattern -| PTuple of (pattern, comma) nsepseq reg (* p1, p2, ... *) -| PPar of pattern par reg (* (p) *) -| PRecord of field_pattern reg ne_injection reg (* {a=...; ...} *) -| PTyped of typed_pattern reg (* (x : int) *) - -and constr_pattern = -| PNone of c_None -| PSomeApp of (c_Some * pattern) reg -| PConstrApp of (constr * pattern option) reg - -and list_pattern = - PListComp of pattern injection reg (* [p1; p2; ...] *) -| PCons of (pattern * cons * pattern) reg (* p1 :: p2 *) - -and typed_pattern = { - pattern : pattern; - colon : colon; - type_expr : type_expr -} - -and field_pattern = { - field_name : field_name; - eq : equal; - pattern : pattern -} - -and expr = - ECase of expr case reg (* p1 -> e1 | p2 -> e2 | ... *) -| ECond of cond_expr reg (* if e1 then e2 else e3 *) -| EAnnot of (expr * colon * type_expr) par reg (* (e : t) *) -| ELogic of logic_expr -| EArith of arith_expr -| EString of string_expr -| EList of list_expr (* x::y::l [1;2;3] *) -| EConstr of constr_expr (* A B(1,A) (C A) *) -| ERecord of field_assign reg ne_injection reg (* {f1=e1; ... } *) -| EProj of projection reg (* x.y.z M.x.y *) -| EVar of variable (* x *) -| ECall of (expr * expr nseq) reg (* e e1 ... en *) -| EBytes of (string * Hex.t) reg (* 0xAEFF *) -| EUnit of the_unit reg (* () *) -| ETuple of (expr, comma) nsepseq reg (* e1, e2, ... *) -| EPar of expr par reg (* (e) *) -| ELetIn of let_in reg (* let p1 = e1 and p2 = e2 and ... in e *) -| EFun of fun_expr reg (* fun x -> e *) -| ESeq of expr injection reg (* begin e1; e2; ... ; en end *) - -and 'a injection = { - compound : compound; - elements : ('a, semi) sepseq; - terminator : semi option -} - -and 'a ne_injection = { - compound : compound; - ne_elements : ('a, semi) nsepseq; - terminator : semi option -} - -and compound = - BeginEnd of kwd_begin * kwd_end -| Braces of lbrace * rbrace -| Brackets of lbracket * rbracket - -and list_expr = - ECons of cat bin_op reg (* e1 :: e3 *) -| EListComp of expr injection reg (* [e1; e2; ...] *) -(*| Append of (expr * append * expr) reg *) (* e1 @ e2 *) - -and string_expr = - Cat of cat bin_op reg (* e1 ^ e2 *) -| String of string reg (* "foo" *) - -and constr_expr = - ENone of c_None -| ESomeApp of (c_Some * expr) reg -| EConstrApp of (constr * expr option) reg - -and arith_expr = - Add of plus bin_op reg (* e1 + e2 *) -| Sub of minus bin_op reg (* e1 - e2 *) -| Mult of times bin_op reg (* e1 * e2 *) -| Div of slash bin_op reg (* e1 / e2 *) -| Mod of kwd_mod bin_op reg (* e1 mod e2 *) -| Neg of minus un_op reg (* -e *) -| Int of (string * Z.t) reg (* 12345 *) -| Nat of (string * Z.t) reg (* 3n *) -| Mutez of (string * Z.t) reg (* 1.00tz 3tz 233mutez *) - -and logic_expr = - BoolExpr of bool_expr -| CompExpr of comp_expr - -and bool_expr = - Or of kwd_or bin_op reg -| And of kwd_and bin_op reg -| Not of kwd_not un_op reg -| True of kwd_true -| False of kwd_false - -and 'a bin_op = { - op : 'a; - arg1 : expr; - arg2 : expr -} - -and 'a un_op = { - op : 'a; - arg : expr -} - -and comp_expr = - Lt of lt bin_op reg -| Leq of leq bin_op reg -| Gt of gt bin_op reg -| Geq of geq bin_op reg -| Equal of equal bin_op reg -| Neq of neq bin_op reg - -and projection = { - struct_name : variable; - selector : dot; - field_path : (selection, dot) nsepseq -} - -and selection = - FieldName of variable -| Component of (string * Z.t) reg - -and field_assign = { - field_name : field_name; - assignment : equal; - field_expr : expr -} - -and 'a case = { - kwd_match : kwd_match; - expr : expr; - kwd_with : kwd_with; - lead_vbar : vbar option; - cases : ('a case_clause reg, vbar) nsepseq reg -} - -and 'a case_clause = { - pattern : pattern; - arrow : arrow; - rhs : 'a -} - -and let_in = { - kwd_let : kwd_let; - binding : let_binding; - kwd_in : kwd_in; - body : expr -} - -and fun_expr = { - kwd_fun : kwd_fun; - binders : pattern nseq; - lhs_type : (colon * type_expr) option; - arrow : arrow; - body : expr -} - -and cond_expr = { - kwd_if : kwd_if; - test : expr; - kwd_then : kwd_then; - ifso : expr; - kwd_else : kwd_else; - ifnot : expr -} - -(* Projecting regions from sundry nodes of the AST. See the first - comment at the beginning of this file. *) - -val pattern_to_region : pattern -> Region.t -val expr_to_region : expr -> Region.t -val type_expr_to_region : type_expr -> Region.t -val selection_to_region : selection -> Region.t diff --git a/src/passes/1-parser/cameligo/Parser.mly b/src/passes/1-parser/cameligo/Parser.mly index 11f858752..5cba24a52 100644 --- a/src/passes/1-parser/cameligo/Parser.mly +++ b/src/passes/1-parser/cameligo/Parser.mly @@ -576,6 +576,7 @@ core_expr: | list(expr) { EList (EListComp $1) } | sequence { ESeq $1 } | record_expr { ERecord $1 } +| update_record { EUpdate $1 } | par(expr) { EPar $1 } | par(expr ":" type_expr {$1,$2,$3}) { EAnnot $1 } @@ -614,6 +615,15 @@ record_expr: terminator} in {region; value} } +update_record: + "{" path "with" record_expr "}" { + let region = cover $1 $5 in + let value = { + record = $2; + kwd_with = $3; + updates = $4} + in {region; value} } + field_assignment: field_name "=" expr { let start = $1.region in @@ -635,3 +645,7 @@ sequence: Some ne_elements, terminator in let value = {compound; elements; terminator} in {region; value} } + +path : + "" {Name $1} +| projection { Path $1} diff --git a/src/passes/1-parser/cameligo/ParserLog.ml b/src/passes/1-parser/cameligo/ParserLog.ml index 79c8baf09..07dcd19ca 100644 --- a/src/passes/1-parser/cameligo/ParserLog.ml +++ b/src/passes/1-parser/cameligo/ParserLog.ml @@ -175,6 +175,16 @@ and print_projection state {value; _} = print_token state selector "."; print_nsepseq state "." print_selection field_path +and print_update state {value; _} = + let {record; kwd_with; updates} = value in + print_path state record; + print_token state kwd_with "with"; + print_record_expr state updates + +and print_path state = function + Name var -> print_var state var +| Path path -> print_projection state path + and print_selection state = function FieldName id -> print_var state id | Component c -> print_int state c @@ -329,6 +339,7 @@ and print_expr state = function | ECall e -> print_fun_call state e | EVar v -> print_var state v | EProj p -> print_projection state p +| EUpdate u -> print_update state u | EUnit e -> print_unit state e | EBytes b -> print_bytes state b | EPar e -> print_expr_par state e @@ -765,6 +776,9 @@ and pp_expr state = function | EProj {value; region} -> pp_loc_node state "EProj" region; pp_projection state value +| EUpdate {value; region} -> + pp_loc_node state "EUpdate" region; + pp_update state value | EVar v -> pp_node state "EVar"; pp_ident (state#pad 1 0) v @@ -857,6 +871,18 @@ and pp_projection state proj = pp_ident (state#pad (1+len) 0) proj.struct_name; List.iteri (apply len) selections +and pp_update state update = + pp_path state update.record; + pp_ne_injection pp_field_assign state update.updates.value + +and pp_path state = function + Name name -> + pp_node state "Name"; + pp_ident (state#pad 1 0) name +| Path {value; region} -> + pp_loc_node state "Path" region; + pp_projection state value + and pp_selection state = function FieldName fn -> pp_node state "FieldName"; diff --git a/src/passes/1-parser/pascaligo/AST.ml b/src/passes/1-parser/pascaligo/AST.ml index 453c15674..7f41af532 100644 --- a/src/passes/1-parser/pascaligo/AST.ml +++ b/src/passes/1-parser/pascaligo/AST.ml @@ -324,7 +324,7 @@ and record_patch = { kwd_patch : kwd_patch; path : path; kwd_with : kwd_with; - record_inj : field_assign reg ne_injection reg + record_inj : record reg } and cond_expr = { @@ -443,8 +443,9 @@ and expr = | EList of list_expr | ESet of set_expr | EConstr of constr_expr -| ERecord of field_assign reg ne_injection reg +| ERecord of record reg | EProj of projection reg +| EUpdate of update reg | EMap of map_expr | EVar of Lexer.lexeme reg | ECall of fun_call @@ -556,6 +557,7 @@ and field_assign = { equal : equal; field_expr : expr } +and record = field_assign reg ne_injection and projection = { struct_name : variable; @@ -563,6 +565,12 @@ and projection = { field_path : (selection, dot) nsepseq } +and update = { + record : path; + kwd_with : kwd_with; + updates : record reg; +} + and selection = FieldName of field_name | Component of (Lexer.lexeme * Z.t) reg @@ -641,6 +649,7 @@ let rec expr_to_region = function | ERecord e -> record_expr_to_region e | EMap e -> map_expr_to_region e | ETuple e -> tuple_expr_to_region e +| EUpdate {region; _} | EProj {region; _} | EVar {region; _} | ECall {region; _} diff --git a/src/passes/1-parser/pascaligo/Parser.mly b/src/passes/1-parser/pascaligo/Parser.mly index 131362464..c07844818 100644 --- a/src/passes/1-parser/pascaligo/Parser.mly +++ b/src/passes/1-parser/pascaligo/Parser.mly @@ -829,6 +829,7 @@ core_expr: | map_expr { EMap $1 } | set_expr { ESet $1 } | record_expr { ERecord $1 } +| update_record { EUpdate $1 } | "" arguments { let region = cover $1.region $2.region in EConstr (ConstrApp {region; value = $1, Some $2}) @@ -921,6 +922,16 @@ record_expr: closing = RBracket $4} in {region; value} } +update_record: + path "with" ne_injection("record",field_assignment){ + let region = cover (path_to_region $1) $3.region in + let value = { + record = $1; + kwd_with = $2; + updates = $3} + in {region; value} } + + field_assignment: field_name "=" expr { let region = cover $1.region (expr_to_region $3) diff --git a/src/passes/1-parser/pascaligo/ParserLog.ml b/src/passes/1-parser/pascaligo/ParserLog.ml index 9b793a327..1a6547751 100644 --- a/src/passes/1-parser/pascaligo/ParserLog.ml +++ b/src/passes/1-parser/pascaligo/ParserLog.ml @@ -433,6 +433,7 @@ and print_expr state = function | ESet e -> print_set_expr state e | EConstr e -> print_constr_expr state e | ERecord e -> print_record_expr state e +| EUpdate e -> print_update_expr state e | EProj e -> print_projection state e | EMap e -> print_map_expr state e | EVar v -> print_var state v @@ -597,6 +598,12 @@ and print_field_assign state {value; _} = print_token state equal "="; print_expr state field_expr +and print_update_expr state {value; _} = + let {record; kwd_with; updates} = value in + print_path state record; + print_token state kwd_with "with"; + print_record_expr state updates + and print_projection state {value; _} = let {struct_name; selector; field_path} = value in print_var state struct_name; @@ -1191,6 +1198,10 @@ and pp_projection state proj = pp_ident (state#pad (1+len) 0) proj.struct_name; List.iteri (apply len) selections +and pp_update state update = + pp_path state update.record; + pp_ne_injection pp_field_assign state update.updates.value + and pp_selection state = function FieldName name -> pp_node state "FieldName"; @@ -1366,6 +1377,9 @@ and pp_expr state = function | EProj {value; region} -> pp_loc_node state "EProj" region; pp_projection state value +| EUpdate {value; region} -> + pp_loc_node state "EUpdate" region; + pp_update state value | EMap e_map -> pp_node state "EMap"; pp_map_expr (state#pad 1 0) e_map diff --git a/src/passes/2-simplify/cameligo.ml b/src/passes/2-simplify/cameligo.ml index ba790e390..c2eb0270b 100644 --- a/src/passes/2-simplify/cameligo.ml +++ b/src/passes/2-simplify/cameligo.ml @@ -262,6 +262,40 @@ let rec simpl_expression : List.map aux @@ npseq_to_list path in return @@ e_accessor ~loc var path' in + let simpl_path : Raw.path -> string * Ast_simplified.access_path = fun p -> + match p with + | Raw.Name v -> (v.value , []) + | Raw.Path p -> ( + let p' = p.value in + let var = p'.struct_name.value in + let path = p'.field_path in + let path' = + let aux (s:Raw.selection) = + match s with + | FieldName property -> Access_record property.value + | Component index -> Access_tuple (Z.to_int (snd index.value)) + in + List.map aux @@ npseq_to_list path in + (var , path') + ) + in + let simpl_update = fun (u:Raw.update Region.reg) -> + let (u, loc) = r_split u in + let (name, path) = simpl_path u.record in + let record = match path with + | [] -> e_variable (Var.of_name name) + | _ -> e_accessor (e_variable (Var.of_name name)) path in + let updates = u.updates.value.ne_elements in + let%bind updates' = + let aux (f:Raw.field_assign Raw.reg) = + let (f,_) = r_split f in + let%bind expr = simpl_expression f.field_expr in + ok (f.field_name.value, expr) + in + bind_map_list aux @@ npseq_to_list updates + in + return @@ e_update ~loc record updates' + in trace (simplifying_expr t) @@ match t with @@ -367,6 +401,7 @@ let rec simpl_expression : let map = SMap.of_list fields in return @@ e_record ~loc map | EProj p -> simpl_projection p + | EUpdate u -> simpl_update u | EConstr (ESomeApp a) -> let (_, args), loc = r_split a in let%bind arg = simpl_expression args in diff --git a/src/passes/2-simplify/pascaligo.ml b/src/passes/2-simplify/pascaligo.ml index 913e1bddc..fb7583389 100644 --- a/src/passes/2-simplify/pascaligo.ml +++ b/src/passes/2-simplify/pascaligo.ml @@ -338,6 +338,7 @@ let rec simpl_expression (t:Raw.expr) : expr result = let aux prev (k, v) = SMap.add k v prev in return @@ e_record (List.fold_left aux SMap.empty fields) | EProj p -> simpl_projection p + | EUpdate u -> simpl_update u | EConstr (ConstrApp c) -> ( let ((c, args) , loc) = r_split c in match args with @@ -462,6 +463,24 @@ let rec simpl_expression (t:Raw.expr) : expr result = let%bind (_ty_opt, f') = simpl_fun_expression ~loc f in return @@ f' + +and simpl_update = fun (u:Raw.update Region.reg) -> + let (u, loc) = r_split u in + let (name, path) = simpl_path u.record in + let record = match path with + | [] -> e_variable (Var.of_name name) + | _ -> e_accessor (e_variable (Var.of_name name)) path in + let updates = u.updates.value.ne_elements in + let%bind updates' = + let aux (f:Raw.field_assign Raw.reg) = + let (f,_) = r_split f in + let%bind expr = simpl_expression f.field_expr in + ok (f.field_name.value, expr) + in + bind_map_list aux @@ npseq_to_list updates + in + ok @@ e_update ~loc record updates' + and simpl_logic_expression (t:Raw.logic_expr) : expression result = let return x = ok x in match t with diff --git a/src/passes/3-self_ast_simplified/helpers.ml b/src/passes/3-self_ast_simplified/helpers.ml index 779bdf7ed..f57fb256b 100644 --- a/src/passes/3-self_ast_simplified/helpers.ml +++ b/src/passes/3-self_ast_simplified/helpers.ml @@ -41,6 +41,15 @@ let rec fold_expression : 'a folder -> 'a -> expression -> 'a result = fun f ini let%bind res = bind_fold_lmap aux (ok init') m in ok res ) + | E_update {record;updates} -> ( + let%bind res = self init' record in + let aux res (_, expr) = + let%bind res = fold_expression self res expr in + ok res + in + let%bind res = bind_fold_list aux res updates in + ok res + ) | E_let_in { binder = _ ; rhs ; result } -> ( let%bind res = self init' rhs in let%bind res = self res result in @@ -131,6 +140,11 @@ let rec map_expression : mapper -> expression -> expression result = fun f e -> let%bind m' = bind_map_lmap self m in return @@ E_record m' ) + | E_update {record; updates} -> ( + let%bind record = self record in + let%bind updates = bind_map_list (fun(l,e) -> let%bind e = self e in ok (l,e)) updates in + return @@ E_update {record;updates} + ) | E_constructor (name , e) -> ( let%bind e' = self e in return @@ E_constructor (name , e') diff --git a/src/passes/4-typer-new/typer.ml b/src/passes/4-typer-new/typer.ml index e6848c232..b65df8593 100644 --- a/src/passes/4-typer-new/typer.ml +++ b/src/passes/4-typer-new/typer.ml @@ -529,6 +529,15 @@ and type_expression : environment -> Solver.state -> ?tv_opt:O.type_value -> I.e let%bind (m' , state') = I.bind_fold_lmap aux (ok (I.LMap.empty , state)) m in let wrapped = Wrap.record (I.LMap.map get_type_annotation m') in return_wrapped (E_record m') state' wrapped + | E_update {record; updates} -> + let%bind (record, state') = type_expression e state record in + let aux (acc, state) (k, expr) = + let%bind (expr',state') = type_expression e state expr in + ok ((k,expr')::acc, state') + in + let%bind(updates,state') = bind_fold_list aux ([], state') updates in + let wrapped = Wrap.list (List.map (fun (_,e) -> get_type_annotation e) updates) in + return_wrapped (E_record_update (record, updates)) state' wrapped (* Data-structure *) (* @@ -1089,6 +1098,14 @@ let rec untype_expression (e:O.annotated_expression) : (I.expression) result = | E_record_accessor (r, Label s) -> let%bind r' = untype_expression r in return (e_accessor r' [Access_record s]) + | E_record_update (r, updates) -> + let%bind r' = untype_expression r in + let aux (Label l,e) = + let%bind e = untype_expression e in + ok (l, e) + in + let%bind updates = bind_map_list aux updates in + return (e_update r' updates) | E_map m -> let%bind m' = bind_map_list (bind_map_pair untype_expression) m in return (e_map m') diff --git a/src/passes/4-typer-old/typer.ml b/src/passes/4-typer-old/typer.ml index 20b54514b..6e54cfe63 100644 --- a/src/passes/4-typer-old/typer.ml +++ b/src/passes/4-typer-old/typer.ml @@ -496,6 +496,23 @@ and type_expression' : environment -> ?tv_opt:O.type_value -> I.expression -> O. in let%bind m' = I.bind_fold_lmap aux (ok I.LMap.empty) m in return (E_record m') (t_record (I.LMap.map get_type_annotation m') ()) + | E_update {record; updates} -> + let%bind record = type_expression' e record in + let aux acc (k, expr) = + let%bind expr' = type_expression' e expr in + ok ((k,expr')::acc) + in + let%bind updates = bind_fold_list aux ([]) updates in + let wrapped = get_type_annotation record in + let wrapped = match wrapped.type_value' with + | T_record record -> + let aux acc (k, e) = + I.LMap.add k (get_type_annotation e) acc + in + t_record (List.fold_left aux record updates) () + | _ -> failwith "Update something which is not a record" + in + return (E_record_update (record, updates)) wrapped (* Data-structure *) | E_list lst -> let%bind lst' = bind_map_list (type_expression' e) lst in @@ -876,6 +893,14 @@ let rec untype_expression (e:O.annotated_expression) : (I.expression) result = | E_record_accessor (r, Label s) -> let%bind r' = untype_expression r in return (e_accessor r' [Access_record s]) + | E_record_update (r, updates) -> + let%bind r' = untype_expression r in + let aux (Label l,e) = + let%bind e = untype_expression e in + ok (l, e) + in + let%bind updates = bind_map_list aux updates in + return (e_update r' updates) | E_map m -> let%bind m' = bind_map_list (bind_map_pair untype_expression) m in return (e_map m') diff --git a/src/passes/6-transpiler/transpiler.ml b/src/passes/6-transpiler/transpiler.ml index 916c7c88d..36637aca2 100644 --- a/src/passes/6-transpiler/transpiler.ml +++ b/src/passes/6-transpiler/transpiler.ml @@ -217,7 +217,7 @@ let record_access_to_lr : type_value -> type_value AST.label_map -> string -> (t let%bind (_ , lst) = let aux = fun (ty , acc) cur -> let%bind (a , b) = - trace_strong (corner_case ~loc:__LOC__ "recard access pair") @@ + trace_strong (corner_case ~loc:__LOC__ "record access pair") @@ Mini_c.get_t_pair ty in match cur with | `Left -> ok (a , acc @ [(a , `Left)]) @@ -365,6 +365,23 @@ and transpile_annotated_expression (ae:AST.annotated_expression) : expression re let%bind record' = transpile_annotated_expression record in let expr = List.fold_left aux record' path in ok expr + | E_record_update (record, updates) -> + let%bind ty' = transpile_type (get_type_annotation record) in + let%bind ty_lmap = + trace_strong (corner_case ~loc:__LOC__ "not a record") @@ + get_t_record (get_type_annotation record) in + let%bind ty'_lmap = AST.bind_map_lmap transpile_type ty_lmap in + let aux (Label l, expr) = + let%bind path = + trace_strong (corner_case ~loc:__LOC__ "record access") @@ + record_access_to_lr ty' ty'_lmap l in + let path' = List.map snd path in + let%bind expr' = transpile_annotated_expression expr in + ok (path',expr') + in + let%bind updates = bind_map_list aux updates in + let%bind record = transpile_annotated_expression record in + return @@ E_update (record, updates) | E_constant (name , lst) -> ( let iterator_generator iterator_name = let lambda_to_iterator_body (f : AST.annotated_expression) (l : AST.lambda) = diff --git a/src/passes/7-self_mini_c/helpers.ml b/src/passes/7-self_mini_c/helpers.ml index 17e27803d..4c8af0d33 100644 --- a/src/passes/7-self_mini_c/helpers.ml +++ b/src/passes/7-self_mini_c/helpers.ml @@ -84,6 +84,15 @@ let rec fold_expression : 'a folder -> 'a -> expression -> 'a result = fun f ini let%bind res = self init' exp in ok res ) + | E_update (r, updates) -> ( + let%bind res = self init' r in + let aux prev (_,e) = + let%bind res = self prev e in + ok res + in + let%bind res = bind_fold_list aux res updates in + ok res + ) type mapper = expression -> expression result @@ -149,3 +158,8 @@ let rec map_expression : mapper -> expression -> expression result = fun f e -> let%bind exp' = self exp in return @@ E_assignment (s, lrl, exp') ) + | E_update (r, updates) -> ( + let%bind r = self r in + let%bind updates = bind_map_list (fun (p,e) -> let%bind e = self e in ok(p,e)) updates in + return @@ E_update(r,updates) + ) diff --git a/src/passes/7-self_mini_c/self_mini_c.ml b/src/passes/7-self_mini_c/self_mini_c.ml index e025eed42..f0c370d05 100644 --- a/src/passes/7-self_mini_c/self_mini_c.ml +++ b/src/passes/7-self_mini_c/self_mini_c.ml @@ -66,6 +66,8 @@ let rec is_pure : expression -> bool = fun e -> | E_constant (c, args) -> is_pure_constant c && List.for_all is_pure args + | E_update (e, updates) + -> is_pure e && List.for_all (fun (_,e) -> is_pure e) updates (* I'm not sure about these. Maybe can be tested better? *) | E_application _ @@ -109,6 +111,8 @@ let rec is_assigned : ignore_lambdas:bool -> expression_variable -> expression - match e.content with | E_assignment (x, _, e) -> it x || self e + | E_update (r, updates) -> + List.fold_left (fun prev (_,e) -> prev || self e) (self r) updates | E_closure { binder; body } -> if ignore_lambdas then false diff --git a/src/passes/7-self_mini_c/subst.ml b/src/passes/7-self_mini_c/subst.ml index 753f33969..be4924c01 100644 --- a/src/passes/7-self_mini_c/subst.ml +++ b/src/passes/7-self_mini_c/subst.ml @@ -94,6 +94,10 @@ let rec replace : expression -> var_name -> var_name -> expression = let v = replace_var v in let e = replace e in return @@ E_assignment (v, path, e) + | E_update (r, updates) -> + let r = replace r in + let updates = List.map (fun (p,e)-> (p, replace e)) updates in + return @@ E_update (r,updates) | E_while (cond, body) -> let cond = replace cond in let body = replace body in @@ -205,6 +209,11 @@ let rec subst_expression : body:expression -> x:var_name -> expr:expression -> e if Var.equal s x then raise Bad_argument ; return @@ E_assignment (s, lrl, exp') ) + | E_update (r, updates) -> ( + let r' = self r in + let updates' = List.map (fun (p,e) -> (p, self e)) updates in + return @@ E_update(r',updates') + ) let%expect_test _ = let dummy_type = T_base Base_unit in diff --git a/src/passes/8-compiler/compiler_program.ml b/src/passes/8-compiler/compiler_program.ml index 37c44b7f3..0c5ab353b 100644 --- a/src/passes/8-compiler/compiler_program.ml +++ b/src/passes/8-compiler/compiler_program.ml @@ -402,6 +402,32 @@ and translate_expression (expr:expression) (env:environment) : michelson result i_push_unit ; ] ) + | E_update (record, updates) -> ( + let%bind record = translate_expression record env in + let insts = [ + i_comment "r_update: start, move the record on top # env"; + record;] in + let aux (init :t list) (update,expr) = + let%bind expr' = translate_expression expr env in + let modify_code = + let aux acc step = match step with + | `Left -> seq [dip i_unpair ; acc ; i_pair] + | `Right -> seq [dip i_unpiar ; acc ; i_piar] + in + let init = dip i_drop in + List.fold_right' aux init update + in + ok @@ init @ [ + expr'; + i_comment "r_updates : compute rhs # rhs:env"; + modify_code; + i_comment "r_update: modify code # record+rhs : env"; + ] + in + let%bind insts = bind_fold_list aux insts updates in + return @@ seq insts + + ) | E_while (expr , block) -> ( let%bind expr' = translate_expression expr env in let%bind block' = translate_expression block env in diff --git a/src/stages/ast_simplified/PP.ml b/src/stages/ast_simplified/PP.ml index 71ec1d6ae..2cedff888 100644 --- a/src/stages/ast_simplified/PP.ml +++ b/src/stages/ast_simplified/PP.ml @@ -26,6 +26,7 @@ let rec expression ppf (e:expression) = match e.expression with | E_tuple lst -> fprintf ppf "(%a)" (tuple_sep_d expression) lst | E_accessor (ae, p) -> fprintf ppf "%a.%a" expression ae access_path p | E_record m -> fprintf ppf "{%a}" (lrecord_sep expression (const " , ")) m + | E_update {record; updates} -> fprintf ppf "%a with {%a}" expression record (tuple_sep_d (fun ppf (a,b) -> fprintf ppf "%a = %a" label a expression b)) updates | E_map m -> fprintf ppf "[%a]" (list_sep_d assoc_expression) m | E_big_map m -> fprintf ppf "big_map[%a]" (list_sep_d assoc_expression) m | E_list lst -> fprintf ppf "[%a]" (list_sep_d expression) lst diff --git a/src/stages/ast_simplified/combinators.ml b/src/stages/ast_simplified/combinators.ml index eb00a86a6..d8d6c3ebf 100644 --- a/src/stages/ast_simplified/combinators.ml +++ b/src/stages/ast_simplified/combinators.ml @@ -172,6 +172,9 @@ let e_ez_record ?loc (lst : (string * expr) list) : expression = let e_record ?loc map = let lst = Map.String.to_kv_list map in e_ez_record ?loc lst +let e_update ?loc record updates = + let updates = List.map (fun (x,y) -> (Label x, y)) updates in + location_wrap ?loc @@ E_update {record; updates} let get_e_accessor = fun t -> match t with diff --git a/src/stages/ast_simplified/combinators.mli b/src/stages/ast_simplified/combinators.mli index b3a0751e0..760eb59b5 100644 --- a/src/stages/ast_simplified/combinators.mli +++ b/src/stages/ast_simplified/combinators.mli @@ -108,6 +108,7 @@ val e_typed_set : ?loc:Location.t -> expression list -> type_expression -> expre val e_lambda : ?loc:Location.t -> expression_variable -> type_expression option -> type_expression option -> expression -> expression val e_record : ?loc:Location.t -> expr Map.String.t -> expression +val e_update : ?loc:Location.t -> expression -> (string * expression) list -> expression val e_ez_record : ?loc:Location.t -> ( string * expr ) list -> expression (* diff --git a/src/stages/ast_simplified/misc.ml b/src/stages/ast_simplified/misc.ml index ea9050e55..b4f40c803 100644 --- a/src/stages/ast_simplified/misc.ml +++ b/src/stages/ast_simplified/misc.ml @@ -131,6 +131,19 @@ let rec assert_value_eq (a, b: (expression * expression )) : unit result = ) | E_record _, _ -> simple_fail "comparing record with other stuff" + + | E_update ura, E_update urb -> + let%bind lst = + generic_try (simple_error "updates with different number of fields") + (fun () -> List.combine ura.updates urb.updates) in + let aux ((Label a,expra),(Label b, exprb))= + assert (String.equal a b); + assert_value_eq (expra,exprb) + in + let%bind _all = bind_list @@ List.map aux lst in + ok () + | E_update _, _ -> + simple_fail "comparing record update with orther stuff" | (E_map lsta, E_map lstb | E_big_map lsta, E_big_map lstb) -> ( let%bind lst = generic_try (simple_error "maps of different lengths") diff --git a/src/stages/ast_simplified/types.ml b/src/stages/ast_simplified/types.ml index 9a5d6777a..7e73908f8 100644 --- a/src/stages/ast_simplified/types.ml +++ b/src/stages/ast_simplified/types.ml @@ -43,6 +43,7 @@ and expression' = | E_record of expr label_map (* TODO: Change it to (expr * access) *) | E_accessor of (expr * access_path) + | E_update of update (* Data Structures *) | E_map of (expr * expr) list | E_big_map of (expr * expr) list @@ -63,6 +64,6 @@ and expression = { expression : expression' ; location : Location.t ; } - +and update = {record: expr; updates: (label*expr)list} and matching_expr = (expr,unit) matching diff --git a/src/stages/ast_typed/PP.ml b/src/stages/ast_typed/PP.ml index ef0e96a39..985f05dd1 100644 --- a/src/stages/ast_typed/PP.ml +++ b/src/stages/ast_typed/PP.ml @@ -34,6 +34,7 @@ and expression ppf (e:expression) : unit = | E_lambda l -> fprintf ppf "%a" lambda l | E_tuple_accessor (ae, i) -> fprintf ppf "%a.%d" annotated_expression ae i | E_record_accessor (ae, l) -> fprintf ppf "%a.%a" annotated_expression ae label l + | E_record_update (ae, ups) -> fprintf ppf "%a with record[%a]" annotated_expression ae (lmap_sep annotated_expression (const " , ")) (LMap.of_list ups) | E_tuple lst -> fprintf ppf "tuple[@; @[%a@]@;]" (list_sep annotated_expression (tag ",@;")) lst | E_record m -> fprintf ppf "record[%a]" (lmap_sep annotated_expression (const " , ")) m | E_map m -> fprintf ppf "map[@; @[%a@]@;]" (list_sep assoc_annotated_expression (tag ",@;")) m diff --git a/src/stages/ast_typed/misc.ml b/src/stages/ast_typed/misc.ml index ebfd7ee27..7572762df 100644 --- a/src/stages/ast_typed/misc.ml +++ b/src/stages/ast_typed/misc.ml @@ -178,6 +178,7 @@ module Free_variables = struct | E_constructor (_ , a) -> self a | E_record m -> unions @@ List.map self @@ LMap.to_list m | E_record_accessor (a, _) -> self a + | E_record_update (r,ups) -> union (self r) @@ unions @@ List.map (fun (_,e) -> self e) ups | E_tuple_accessor (a, _) -> self a | E_list lst -> unions @@ List.map self lst | E_set lst -> unions @@ List.map self lst @@ -472,6 +473,21 @@ let rec assert_value_eq (a, b: (value*value)) : unit result = | E_record _, _ -> fail @@ (different_values_because_different_types "record vs. non-record" a b) + | E_record_update (ra,upa), E_record_update (rb,upb) -> ( + let%bind _r = assert_value_eq (ra,rb) in + let%bind lst = + generic_try (simple_error "updates with different number of fields") + (fun () -> List.combine upa upb) in + let aux ((Label a,expra),(Label b, exprb))= + assert (String.equal a b); + assert_value_eq (expra,exprb) + in + let%bind _all = bind_list @@ List.map aux lst in + ok () + ) + | E_record_update _ , _ -> + fail @@ (different_values_because_different_types "record update vs. non record update" a b) + | (E_map lsta, E_map lstb | E_big_map lsta, E_big_map lstb) -> ( let%bind lst = generic_try (different_size_values "maps of different lengths" a b) (fun () -> diff --git a/src/stages/ast_typed/misc_smart.ml b/src/stages/ast_typed/misc_smart.ml index 9cefc64fd..14fbccbb5 100644 --- a/src/stages/ast_typed/misc_smart.ml +++ b/src/stages/ast_typed/misc_smart.ml @@ -72,6 +72,14 @@ module Captured_variables = struct let%bind lst' = bind_map_list self @@ LMap.to_list m in ok @@ unions lst' | E_record_accessor (a, _) -> self a + | E_record_update (r,ups) -> + let%bind r = self r in + let aux (_, e) = + let%bind e = self e in + ok e + in + let%bind lst = bind_map_list aux ups in + ok @@ union r @@ unions lst | E_tuple_accessor (a, _) -> self a | E_list lst -> let%bind lst' = bind_map_list self lst in diff --git a/src/stages/ast_typed/types.ml b/src/stages/ast_typed/types.ml index 4b924d23f..388a09eb7 100644 --- a/src/stages/ast_typed/types.ml +++ b/src/stages/ast_typed/types.ml @@ -82,6 +82,7 @@ and 'a expression' = (* Record *) | E_record of ('a) label_map | E_record_accessor of (('a) * label) + | E_record_update of ('a * (label* 'a) list) (* Data Structures *) | E_map of (('a) * ('a)) list | E_big_map of (('a) * ('a)) list diff --git a/src/stages/mini_c/PP.ml b/src/stages/mini_c/PP.ml index 9b3e7fa3d..66179745e 100644 --- a/src/stages/mini_c/PP.ml +++ b/src/stages/mini_c/PP.ml @@ -99,6 +99,8 @@ and expression' ppf (e:expression') = match e with fprintf ppf "fold %a on %a with %a do ( %a )" expression collection expression initial Stage_common.PP.name name expression body | E_assignment (r , path , e) -> fprintf ppf "%a.%a := %a" Stage_common.PP.name r (list_sep lr (const ".")) path expression e + | E_update (r, updates) -> + fprintf ppf "%a with {%a}" expression r (list_sep_d (fun ppf (path, e) -> fprintf ppf "%a = %a" (list_sep lr (const ".")) path expression e)) updates | E_while (e , b) -> fprintf ppf "while (%a) %a" expression e expression b diff --git a/src/stages/mini_c/misc.ml b/src/stages/mini_c/misc.ml index 2dae579d3..df0387b19 100644 --- a/src/stages/mini_c/misc.ml +++ b/src/stages/mini_c/misc.ml @@ -81,6 +81,7 @@ module Free_variables = struct | E_sequence (x, y) -> union (self x) (self y) (* NB different from ast_typed... *) | E_assignment (v, _, e) -> unions [ var_name b v ; self e ] + | E_update (e, updates) -> union (self e) (unions @@ List.map (fun (_,e) -> self e) updates) | E_while (cond , body) -> union (self cond) (self body) and var_name : bindings -> var_name -> bindings = fun b n -> @@ -165,4 +166,4 @@ let aggregate_entry (lst : program) (form : form_t) : expression result = ) | _ -> simple_fail "a contract must be a closure" ) | ExpressionForm entry_expression -> - ok @@ wrapper entry_expression \ No newline at end of file + ok @@ wrapper entry_expression diff --git a/src/stages/mini_c/types.ml b/src/stages/mini_c/types.ml index 5e14b8349..42e411add 100644 --- a/src/stages/mini_c/types.ml +++ b/src/stages/mini_c/types.ml @@ -71,6 +71,7 @@ and expression' = | E_let_in of ((var_name * type_value) * expression * expression) | E_sequence of (expression * expression) | E_assignment of (expression_variable * [`Left | `Right] list * expression) + | E_update of (expression * ([`Left | `Right] list * expression) list) | E_while of (expression * expression) and expression = { diff --git a/src/stages/typesystem/misc.ml b/src/stages/typesystem/misc.ml index 06879a319..76d2ec7c1 100644 --- a/src/stages/typesystem/misc.ml +++ b/src/stages/typesystem/misc.ml @@ -175,6 +175,10 @@ module Substitution = struct let%bind val_ = s_annotated_expression ~v ~expr val_ in let%bind l = s_label ~v ~expr l in ok @@ T.E_record_accessor (val_, l) + | T.E_record_update (r, ups) -> + let%bind r = s_annotated_expression ~v ~expr r in + let%bind ups = bind_map_list (fun (l,e) -> let%bind e = s_annotated_expression ~v ~expr e in ok (l,e)) ups in + ok @@ T.E_record_update (r,ups) | T.E_map val_val_list -> let%bind val_val_list = bind_map_list (fun (val1 , val2) -> let%bind val1 = s_annotated_expression ~v ~expr val1 in diff --git a/src/test/contracts/record.ligo b/src/test/contracts/record.ligo index cb578abb0..0ce9737fd 100644 --- a/src/test/contracts/record.ligo +++ b/src/test/contracts/record.ligo @@ -38,7 +38,7 @@ function modify (const r : foobar) : foobar is function modify_abc (const r : abc) : abc is block { - r.b := 2048 ; + r := r with record b = 2048; end; } with r type big_record is record diff --git a/src/test/contracts/record.mligo b/src/test/contracts/record.mligo index 943ccf91d..ecd5f99d8 100644 --- a/src/test/contracts/record.mligo +++ b/src/test/contracts/record.mligo @@ -28,7 +28,7 @@ let projection (r : foobar) : int = r.foo + r.bar let modify (r : foobar) : foobar = {foo = 256; bar = r.bar} -let modify_abc (r : abc) : abc = {a = r.a; b = 2048; c = r.c} +let modify_abc (r : abc) : abc = {r with {b = 2048}} type big_record = { a : int ; diff --git a/src/test/integration_tests.ml b/src/test/integration_tests.ml index 6277e1012..555f66119 100644 --- a/src/test/integration_tests.ml +++ b/src/test/integration_tests.ml @@ -692,6 +692,43 @@ let record () : unit result = in ok () +let record_mligo () : unit result = + let%bind program = mtype_file "./contracts/record.mligo" in + let%bind () = + let expected = record_ez_int ["foo" ; "bar"] 0 in + expect_eq_evaluate program "fb" expected + in + let%bind () = + let%bind () = expect_eq_evaluate program "a" (e_int 42) in + let%bind () = expect_eq_evaluate program "b" (e_int 142) in + let%bind () = expect_eq_evaluate program "c" (e_int 242) in + ok () + in + let%bind () = + let make_input = record_ez_int ["foo" ; "bar"] in + let make_expected = fun n -> e_int (2 * n) in + expect_eq_n program "projection" make_input make_expected + in + let%bind () = + let make_input = record_ez_int ["foo" ; "bar"] in + let make_expected = fun n -> ez_e_record [("foo" , e_int 256) ; ("bar" , e_int n) ] in + expect_eq_n program "modify" make_input make_expected + in + let%bind () = + let make_input = record_ez_int ["a" ; "b" ; "c"] in + let make_expected = fun n -> ez_e_record [ + ("a" , e_int n) ; + ("b" , e_int 2048) ; + ("c" , e_int n) + ] in + expect_eq_n program "modify_abc" make_input make_expected + in + let%bind () = + let expected = record_ez_int ["a";"b";"c";"d";"e"] 23 in + expect_eq_evaluate program "br" expected + in + ok () + let tuple () : unit result = let%bind program = type_file "./contracts/tuple.ligo" in let ez n = @@ -1912,6 +1949,7 @@ let main = test_suite "Integration (End to End)" [ test "tuple (mligo)" tuple_mligo ; test "tuple (religo)" tuple_religo ; test "record" record ; + test "record" record_mligo ; test "condition simple" condition_simple ; test "condition (ligo)" condition ; test "condition (mligo)" condition_mligo ; From 60edd0cf5b0be4c8a209de0647e2f5b5e2c2f1a5 Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Wulfman Date: Fri, 10 Jan 2020 16:41:47 +0100 Subject: [PATCH 2/3] after review 1 --- src/passes/1-parser/cameligo/AST.ml | 2 ++ src/passes/1-parser/cameligo/Parser.mly | 12 +++++++--- src/passes/1-parser/cameligo/ParserLog.ml | 6 +++-- src/passes/1-parser/cameligo/Tests/pp.mligo | 3 +++ src/passes/1-parser/pascaligo/Tests/pp.ligo | 2 ++ src/passes/4-typer-new/typer.ml | 26 +++++++++++++++------ src/passes/4-typer-old/typer.ml | 13 +++++++---- src/stages/ast_simplified/misc.ml | 16 ++++++------- src/test/contracts/record.ligo | 11 ++++++++- src/test/contracts/record.mligo | 8 ++++++- src/test/integration_tests.ml | 22 +++++++++++++++-- 11 files changed, 92 insertions(+), 29 deletions(-) diff --git a/src/passes/1-parser/cameligo/AST.ml b/src/passes/1-parser/cameligo/AST.ml index f8b008a9a..9c7f1f982 100644 --- a/src/passes/1-parser/cameligo/AST.ml +++ b/src/passes/1-parser/cameligo/AST.ml @@ -335,9 +335,11 @@ and field_assign = { } and update = { + lbrace : lbrace; record : path; kwd_with : kwd_with; updates : record reg; + rbrace : rbrace; } and path = Name of variable diff --git a/src/passes/1-parser/cameligo/Parser.mly b/src/passes/1-parser/cameligo/Parser.mly index 5cba24a52..f37c463f9 100644 --- a/src/passes/1-parser/cameligo/Parser.mly +++ b/src/passes/1-parser/cameligo/Parser.mly @@ -616,12 +616,18 @@ record_expr: in {region; value} } update_record: - "{" path "with" record_expr "}" { + "{" path "with" sep_or_term_list(field_assignment,";") "}" { let region = cover $1 $5 in + let ne_elements, terminator = $4 in let value = { - record = $2; + lbrace = $1; + record = $2; kwd_with = $3; - updates = $4} + updates = { value = {compound = Braces($1,$5); + ne_elements; + terminator}; + region = cover $3 $5}; + rbrace = $5} in {region; value} } field_assignment: diff --git a/src/passes/1-parser/cameligo/ParserLog.ml b/src/passes/1-parser/cameligo/ParserLog.ml index 07dcd19ca..e10539e3e 100644 --- a/src/passes/1-parser/cameligo/ParserLog.ml +++ b/src/passes/1-parser/cameligo/ParserLog.ml @@ -176,10 +176,12 @@ and print_projection state {value; _} = print_nsepseq state "." print_selection field_path and print_update state {value; _} = - let {record; kwd_with; updates} = value in + let {lbrace; record; kwd_with; updates; rbrace} = value in + print_token state lbrace "{"; print_path state record; print_token state kwd_with "with"; - print_record_expr state updates + print_record_expr state updates; + print_token state rbrace "}" and print_path state = function Name var -> print_var state var diff --git a/src/passes/1-parser/cameligo/Tests/pp.mligo b/src/passes/1-parser/cameligo/Tests/pp.mligo index 99aff4f23..d84c270aa 100644 --- a/src/passes/1-parser/cameligo/Tests/pp.mligo +++ b/src/passes/1-parser/cameligo/Tests/pp.mligo @@ -24,3 +24,6 @@ let e = Some (a, B b) let z = z.1.2 let v = "hello" ^ "world" ^ "!" let w = Map.literal [(1,"1"); (2,"2")] + +let r = { field = 0} +let r = { r with field = 42} diff --git a/src/passes/1-parser/pascaligo/Tests/pp.ligo b/src/passes/1-parser/pascaligo/Tests/pp.ligo index 78c06c34d..a2e873338 100644 --- a/src/passes/1-parser/pascaligo/Tests/pp.ligo +++ b/src/passes/1-parser/pascaligo/Tests/pp.ligo @@ -24,6 +24,8 @@ function back (var store : store) : list (operation) * store is x := map [1 -> "1"; 2 -> "2"]; y := a.b.c[3]; a := "hello " ^ "world" ^ "!"; + r := record a = 0 end; + r := r with record a = 42 end; patch store.backers with set [(1); f(2*3)]; remove (1,2,3) from set foo.bar; remove 3 from map foo.bar; diff --git a/src/passes/4-typer-new/typer.ml b/src/passes/4-typer-new/typer.ml index b65df8593..da0543b74 100644 --- a/src/passes/4-typer-new/typer.ml +++ b/src/passes/4-typer-new/typer.ml @@ -530,14 +530,26 @@ and type_expression : environment -> Solver.state -> ?tv_opt:O.type_value -> I.e let wrapped = Wrap.record (I.LMap.map get_type_annotation m') in return_wrapped (E_record m') state' wrapped | E_update {record; updates} -> - let%bind (record, state') = type_expression e state record in - let aux (acc, state) (k, expr) = - let%bind (expr',state') = type_expression e state expr in - ok ((k,expr')::acc, state') + let%bind (record, state) = type_expression e state record in + let aux (lst,state) (k, expr) = + let%bind (expr', state) = type_expression e state expr in + ok ((k,expr')::lst, state) in - let%bind(updates,state') = bind_fold_list aux ([], state') updates in - let wrapped = Wrap.list (List.map (fun (_,e) -> get_type_annotation e) updates) in - return_wrapped (E_record_update (record, updates)) state' wrapped + let%bind (updates, state) = bind_fold_list aux ([], state) updates in + let wrapped = get_type_annotation record in + let%bind wrapped = match wrapped.type_value' with + | T_record record -> + let aux (k, e) = + let field_op = I.LMap.find_opt k record in + match field_op with + | None -> failwith @@ Format.asprintf "field %a is not part of record" Stage_common.PP.label k + | Some tv -> O.assert_type_value_eq (tv, get_type_annotation e) + in + let%bind () = bind_iter_list aux updates in + ok (record) + | _ -> failwith "Update an expression which is not a record" + in + return_wrapped (E_record_update (record, updates)) state (Wrap.record wrapped) (* Data-structure *) (* diff --git a/src/passes/4-typer-old/typer.ml b/src/passes/4-typer-old/typer.ml index 6e54cfe63..59cbbf8bc 100644 --- a/src/passes/4-typer-old/typer.ml +++ b/src/passes/4-typer-old/typer.ml @@ -504,13 +504,16 @@ and type_expression' : environment -> ?tv_opt:O.type_value -> I.expression -> O. in let%bind updates = bind_fold_list aux ([]) updates in let wrapped = get_type_annotation record in - let wrapped = match wrapped.type_value' with + let%bind () = match wrapped.type_value' with | T_record record -> - let aux acc (k, e) = - I.LMap.add k (get_type_annotation e) acc + let aux (k, e) = + let field_op = I.LMap.find_opt k record in + match field_op with + | None -> failwith @@ Format.asprintf "field %a is not part of record" Stage_common.PP.label k + | Some tv -> O.assert_type_value_eq (tv, get_type_annotation e) in - t_record (List.fold_left aux record updates) () - | _ -> failwith "Update something which is not a record" + bind_iter_list aux updates + | _ -> failwith "Update an expression which is not a record" in return (E_record_update (record, updates)) wrapped (* Data-structure *) diff --git a/src/stages/ast_simplified/misc.ml b/src/stages/ast_simplified/misc.ml index b4f40c803..a37e57cf3 100644 --- a/src/stages/ast_simplified/misc.ml +++ b/src/stages/ast_simplified/misc.ml @@ -99,7 +99,7 @@ let rec assert_value_eq (a, b: (expression * expression )) : unit result = PP.expression a PP.expression b in - fail @@ (fun () -> error (thunk "comparing constant with other stuff") error_content ()) + fail @@ (fun () -> error (thunk "comparing constant with other expression") error_content ()) | E_constructor (ca, a), E_constructor (cb, b) when ca = cb -> ( let%bind _eq = assert_value_eq (a, b) in @@ -108,7 +108,7 @@ let rec assert_value_eq (a, b: (expression * expression )) : unit result = | E_constructor _, E_constructor _ -> simple_fail "different constructors" | E_constructor _, _ -> - simple_fail "comparing constructor with other stuff" + simple_fail "comparing constructor with other expression" | E_tuple lsta, E_tuple lstb -> ( let%bind lst = @@ -118,7 +118,7 @@ let rec assert_value_eq (a, b: (expression * expression )) : unit result = ok () ) | E_tuple _, _ -> - simple_fail "comparing tuple with other stuff" + simple_fail "comparing tuple with other expression" | E_record sma, E_record smb -> ( let aux _ a b = @@ -130,7 +130,7 @@ let rec assert_value_eq (a, b: (expression * expression )) : unit result = ok () ) | E_record _, _ -> - simple_fail "comparing record with other stuff" + simple_fail "comparing record with other expression" | E_update ura, E_update urb -> let%bind lst = @@ -143,7 +143,7 @@ let rec assert_value_eq (a, b: (expression * expression )) : unit result = let%bind _all = bind_list @@ List.map aux lst in ok () | E_update _, _ -> - simple_fail "comparing record update with orther stuff" + simple_fail "comparing record update with other expression" | (E_map lsta, E_map lstb | E_big_map lsta, E_big_map lstb) -> ( let%bind lst = generic_try (simple_error "maps of different lengths") @@ -159,7 +159,7 @@ let rec assert_value_eq (a, b: (expression * expression )) : unit result = ok () ) | (E_map _ | E_big_map _), _ -> - simple_fail "comparing map with other stuff" + simple_fail "comparing map with other expression" | E_list lsta, E_list lstb -> ( let%bind lst = @@ -169,7 +169,7 @@ let rec assert_value_eq (a, b: (expression * expression )) : unit result = ok () ) | E_list _, _ -> - simple_fail "comparing list with other stuff" + simple_fail "comparing list with other expression" | E_set lsta, E_set lstb -> ( let lsta' = List.sort (compare) lsta in @@ -181,7 +181,7 @@ let rec assert_value_eq (a, b: (expression * expression )) : unit result = ok () ) | E_set _, _ -> - simple_fail "comparing set with other stuff" + simple_fail "comparing set with other expression" | (E_ascription (a , _) , _b') -> assert_value_eq (a , b) | (_a' , E_ascription (b , _)) -> assert_value_eq (a , b) diff --git a/src/test/contracts/record.ligo b/src/test/contracts/record.ligo index 0ce9737fd..dca49f72c 100644 --- a/src/test/contracts/record.ligo +++ b/src/test/contracts/record.ligo @@ -38,7 +38,7 @@ function modify (const r : foobar) : foobar is function modify_abc (const r : abc) : abc is block { - r := r with record b = 2048; end; + r := r with record b = 2048; c = 42; end; } with r type big_record is record @@ -56,3 +56,12 @@ const br : big_record = record d = 23 ; e = 23 ; end + +type double_record is record + inner : abc; +end + +function modify_inner (const r : double_record) : double_record is + block { + r := r with record inner = r.inner with record b = 2048; end; end; + } with r diff --git a/src/test/contracts/record.mligo b/src/test/contracts/record.mligo index ecd5f99d8..8b340cd1e 100644 --- a/src/test/contracts/record.mligo +++ b/src/test/contracts/record.mligo @@ -28,7 +28,7 @@ let projection (r : foobar) : int = r.foo + r.bar let modify (r : foobar) : foobar = {foo = 256; bar = r.bar} -let modify_abc (r : abc) : abc = {r with {b = 2048}} +let modify_abc (r : abc) : abc = {r with b = 2048; c = 42} type big_record = { a : int ; @@ -45,3 +45,9 @@ let br : big_record = { d = 23 ; e = 23 ; } + +type double_record = { + inner : abc; +} + +let modify_inner (r : double_record) : double_record = {r with inner = {r.inner with b = 2048 }} diff --git a/src/test/integration_tests.ml b/src/test/integration_tests.ml index 555f66119..26915dc4c 100644 --- a/src/test/integration_tests.ml +++ b/src/test/integration_tests.ml @@ -682,7 +682,7 @@ let record () : unit result = let make_expected = fun n -> ez_e_record [ ("a" , e_int n) ; ("b" , e_int 2048) ; - ("c" , e_int n) + ("c" , e_int 42) ] in expect_eq_n program "modify_abc" make_input make_expected in @@ -690,6 +690,15 @@ let record () : unit result = let expected = record_ez_int ["a";"b";"c";"d";"e"] 23 in expect_eq_evaluate program "br" expected in + let%bind () = + let make_input = fun n -> ez_e_record [("inner", record_ez_int ["a";"b";"c"] n)] in + let make_expected = fun n -> ez_e_record [("inner", ez_e_record[ + ("a" , e_int n) ; + ("b" , e_int 2048) ; + ("c" , e_int n) + ])] in + expect_eq_n program "modify_inner" make_input make_expected + in ok () let record_mligo () : unit result = @@ -719,7 +728,7 @@ let record_mligo () : unit result = let make_expected = fun n -> ez_e_record [ ("a" , e_int n) ; ("b" , e_int 2048) ; - ("c" , e_int n) + ("c" , e_int 42) ] in expect_eq_n program "modify_abc" make_input make_expected in @@ -727,6 +736,15 @@ let record_mligo () : unit result = let expected = record_ez_int ["a";"b";"c";"d";"e"] 23 in expect_eq_evaluate program "br" expected in + let%bind () = + let make_input = fun n -> ez_e_record [("inner", record_ez_int ["a";"b";"c"] n)] in + let make_expected = fun n -> ez_e_record [("inner", ez_e_record[ + ("a" , e_int n) ; + ("b" , e_int 2048) ; + ("c" , e_int n) + ])] in + expect_eq_n program "modify_inner" make_input make_expected + in ok () let tuple () : unit result = From 98d6aea4e1fbac190c7f73b08b7673acd5d5edde Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Wulfman Date: Fri, 10 Jan 2020 17:28:45 +0100 Subject: [PATCH 3/3] mr review 2 --- src/passes/8-compiler/compiler_program.ml | 8 +++++--- src/stages/ast_typed/misc.ml | 16 +--------------- src/test/contracts/record.ligo | 3 ++- src/test/contracts/record.mligo | 2 +- 4 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/passes/8-compiler/compiler_program.ml b/src/passes/8-compiler/compiler_program.ml index 0c5ab353b..339e3aa85 100644 --- a/src/passes/8-compiler/compiler_program.ml +++ b/src/passes/8-compiler/compiler_program.ml @@ -403,12 +403,14 @@ and translate_expression (expr:expression) (env:environment) : michelson result ] ) | E_update (record, updates) -> ( - let%bind record = translate_expression record env in + let%bind record' = translate_expression record env in let insts = [ i_comment "r_update: start, move the record on top # env"; - record;] in + record';] in let aux (init :t list) (update,expr) = - let%bind expr' = translate_expression expr env in + let record_var = Var.fresh () in + let env' = Environment.add (record_var, record.type_value) env in + let%bind expr' = translate_expression expr env' in let modify_code = let aux acc step = match step with | `Left -> seq [dip i_unpair ; acc ; i_pair] diff --git a/src/stages/ast_typed/misc.ml b/src/stages/ast_typed/misc.ml index 7572762df..1d46a6bb6 100644 --- a/src/stages/ast_typed/misc.ml +++ b/src/stages/ast_typed/misc.ml @@ -473,21 +473,6 @@ let rec assert_value_eq (a, b: (value*value)) : unit result = | E_record _, _ -> fail @@ (different_values_because_different_types "record vs. non-record" a b) - | E_record_update (ra,upa), E_record_update (rb,upb) -> ( - let%bind _r = assert_value_eq (ra,rb) in - let%bind lst = - generic_try (simple_error "updates with different number of fields") - (fun () -> List.combine upa upb) in - let aux ((Label a,expra),(Label b, exprb))= - assert (String.equal a b); - assert_value_eq (expra,exprb) - in - let%bind _all = bind_list @@ List.map aux lst in - ok () - ) - | E_record_update _ , _ -> - fail @@ (different_values_because_different_types "record update vs. non record update" a b) - | (E_map lsta, E_map lstb | E_big_map lsta, E_big_map lstb) -> ( let%bind lst = generic_try (different_size_values "maps of different lengths" a b) (fun () -> @@ -524,6 +509,7 @@ let rec assert_value_eq (a, b: (value*value)) : unit result = fail @@ different_values_because_different_types "set vs. non-set" a b | (E_literal _, _) | (E_variable _, _) | (E_application _, _) | (E_lambda _, _) | (E_let_in _, _) | (E_tuple_accessor _, _) + | (E_record_update _,_) | (E_record_accessor _, _) | (E_look_up _, _) | (E_matching _, _) | (E_assign _ , _) diff --git a/src/test/contracts/record.ligo b/src/test/contracts/record.ligo index dca49f72c..0b4921fb3 100644 --- a/src/test/contracts/record.ligo +++ b/src/test/contracts/record.ligo @@ -38,7 +38,8 @@ function modify (const r : foobar) : foobar is function modify_abc (const r : abc) : abc is block { - r := r with record b = 2048; c = 42; end; + const c : int = 42; + r := r with record b = 2048; c = c; end; } with r type big_record is record diff --git a/src/test/contracts/record.mligo b/src/test/contracts/record.mligo index 8b340cd1e..b898c41f1 100644 --- a/src/test/contracts/record.mligo +++ b/src/test/contracts/record.mligo @@ -28,7 +28,7 @@ let projection (r : foobar) : int = r.foo + r.bar let modify (r : foobar) : foobar = {foo = 256; bar = r.bar} -let modify_abc (r : abc) : abc = {r with b = 2048; c = 42} +let modify_abc (r : abc) : abc = let c = 42 in {r with b = 2048; c = c} type big_record = { a : int ;