mirror of
https://github.com/CatalaLang/catala.git
synced 2024-11-08 07:51:43 +03:00
Support for structure updates (#603)
This commit is contained in:
commit
87b2c4ded2
@ -184,13 +184,16 @@ to generate the documentation, then open the `doc/odoc.html` file in any browser
|
||||
|
||||
## Examples
|
||||
|
||||
To explore the different programs written in Catala, see
|
||||
[the dedicated readme](https://github.com/CatalaLang/catala-examples/README.md).
|
||||
To explore the different programs written in Catala, see [the dedicated
|
||||
readme](https://github.com/CatalaLang/catala-examples/blob/master/README.md).
|
||||
|
||||
## API
|
||||
|
||||
To know how to use the code generated by the Catala compiler in your favorite
|
||||
programming language, head to the [readme of the French law library](https://github.com/catalalang/french_law/README.md). The corresponding pre-built examples are also [available](https://catalalang.github.io/catala/).
|
||||
programming language, head to the [readme of the French law
|
||||
library](https://github.com/CatalaLang/french-law/blob/master/README.md). The
|
||||
corresponding pre-built examples are also
|
||||
[available](https://catalalang.github.io/catala/).
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@ -559,6 +559,18 @@ let rec translate_expr
|
||||
Expr.make_abs (Array.of_list vs) (rec_helper ~local_vars e2) taus pos
|
||||
in
|
||||
Expr.eapp ~f ~args:[rec_helper e1] ~tys:[] emark
|
||||
| StructReplace (e, fields) ->
|
||||
let fields =
|
||||
List.fold_left
|
||||
(fun acc (field_id, field_expr) ->
|
||||
if Ident.Map.mem (Mark.remove field_id) acc then
|
||||
Message.error ~pos:(Mark.get field_expr)
|
||||
"Duplicate redefinition of field@ %a" Ident.format
|
||||
(Mark.remove field_id);
|
||||
Ident.Map.add (Mark.remove field_id) (rec_helper field_expr) acc)
|
||||
Ident.Map.empty fields
|
||||
in
|
||||
Expr.edstructamend ~fields ~e:(rec_helper e) ~name_opt:None emark
|
||||
| StructLit (((path, s_name), _), fields) ->
|
||||
let ctxt = Name_resolution.module_ctx ctxt path in
|
||||
let s_uid =
|
||||
|
@ -83,7 +83,50 @@ let rec translate_expr (ctx : ctx) (e : D.expr) : untyped Ast.expr boxed =
|
||||
})
|
||||
m
|
||||
| ELocation (ToplevelVar v) -> Expr.elocation (ToplevelVar v) m
|
||||
| EDStructAccess _ ->
|
||||
| EDStructAmend { name_opt = Some name; e; fields } ->
|
||||
let str_fields = StructName.Map.find name ctx.decl_ctx.ctx_structs in
|
||||
let fields =
|
||||
Ident.Map.fold
|
||||
(fun id e ->
|
||||
match
|
||||
StructName.Map.find name
|
||||
(Ident.Map.find id ctx.decl_ctx.ctx_struct_fields)
|
||||
with
|
||||
| field -> StructField.Map.add field (translate_expr ctx e)
|
||||
| exception (Ident.Map.Not_found _ | StructName.Map.Not_found _) ->
|
||||
Message.error ~pos:(Expr.pos e)
|
||||
~fmt_pos:
|
||||
[
|
||||
( (fun ppf ->
|
||||
Format.fprintf ppf "Declaration of structure %a"
|
||||
StructName.format name),
|
||||
Mark.get (StructName.get_info name) );
|
||||
]
|
||||
"Field %a@ does@ not@ belong@ to@ structure@ %a" Ident.format id
|
||||
StructName.format name)
|
||||
fields StructField.Map.empty
|
||||
in
|
||||
if StructField.Map.cardinal fields = StructField.Map.cardinal str_fields
|
||||
then
|
||||
Message.warning ~pos:(Expr.mark_pos m)
|
||||
"All fields of@ %a@ are@ rewritten@ in@ this@ replacement."
|
||||
StructName.format name;
|
||||
let orig_var = Var.make "orig" in
|
||||
let orig_e = Expr.evar orig_var (Mark.get e) in
|
||||
let fields =
|
||||
StructField.Map.mapi
|
||||
(fun field _ty ->
|
||||
match StructField.Map.find_opt field fields with
|
||||
| Some e -> e
|
||||
| None -> Expr.estructaccess ~name ~field ~e:orig_e m)
|
||||
str_fields
|
||||
in
|
||||
Expr.make_let_in orig_var
|
||||
(TStruct name, Expr.pos e)
|
||||
(translate_expr ctx e)
|
||||
(Expr.estruct ~name ~fields m)
|
||||
(Expr.mark_pos m)
|
||||
| EDStructAmend { name_opt = None; _ } | EDStructAccess _ ->
|
||||
assert false (* This shouldn't appear in desugared after disambiguation *)
|
||||
| EScopeCall { scope; args } ->
|
||||
Expr.escopecall ~scope
|
||||
|
@ -527,6 +527,12 @@ and ('a, 'b, 'm) base_gexpr =
|
||||
args : ('a, 'm) gexpr ScopeVar.Map.t;
|
||||
}
|
||||
-> ('a, < explicitScopes : yes ; .. >, 'm) base_gexpr
|
||||
| EDStructAmend : {
|
||||
name_opt : StructName.t option;
|
||||
e : ('a, 'm) gexpr;
|
||||
fields : ('a, 'm) gexpr Ident.Map.t;
|
||||
}
|
||||
-> ('a, < syntacticNames : yes ; .. >, 'm) base_gexpr
|
||||
| EDStructAccess : {
|
||||
name_opt : StructName.t option;
|
||||
e : ('a, 'm) gexpr;
|
||||
|
@ -56,6 +56,10 @@ module Box = struct
|
||||
let lift : ('a, 't) boxed_gexpr -> ('a, 't) gexpr B.box =
|
||||
fun em -> B.box_apply (fun e -> Mark.add (Mark.get em) e) (Mark.remove em)
|
||||
|
||||
module LiftIdentMap = Bindlib.Lift (Ident.Map)
|
||||
|
||||
let lift_identmap = LiftIdentMap.lift_box
|
||||
|
||||
module LiftStruct = Bindlib.Lift (StructField.Map)
|
||||
|
||||
let lift_struct = LiftStruct.lift_box
|
||||
@ -156,6 +160,14 @@ let estruct ~name ~(fields : ('a, 't) boxed_gexpr StructField.Map.t) mark =
|
||||
(fun fields -> EStruct { name; fields })
|
||||
(Box.lift_struct (StructField.Map.map Box.lift fields))
|
||||
|
||||
let edstructamend ~name_opt ~e ~(fields : ('a, 't) boxed_gexpr Ident.Map.t) mark
|
||||
=
|
||||
Mark.add mark
|
||||
@@ Bindlib.box_apply2
|
||||
(fun e fields -> EDStructAmend { name_opt; e; fields })
|
||||
(Box.lift e)
|
||||
(Box.lift_identmap (Ident.Map.map Box.lift fields))
|
||||
|
||||
let edstructaccess ~name_opt ~field ~e =
|
||||
Box.app1 e @@ fun e -> EDStructAccess { name_opt; field; e }
|
||||
|
||||
@ -305,6 +317,9 @@ let map
|
||||
| EStruct { name; fields } ->
|
||||
let fields = StructField.Map.map f fields in
|
||||
estruct ~name ~fields m
|
||||
| EDStructAmend { name_opt; e; fields } ->
|
||||
let fields = Ident.Map.map f fields in
|
||||
edstructamend ~name_opt ~e:(f e) ~fields m
|
||||
| EDStructAccess { name_opt; field; e } ->
|
||||
edstructaccess ~name_opt ~field ~e:(f e) m
|
||||
| EStructAccess { name; field; e } -> estructaccess ~name ~field ~e:(f e) m
|
||||
@ -345,6 +360,8 @@ let shallow_fold
|
||||
| EErrorOnEmpty e -> acc |> f e
|
||||
| ECatch { body; handler; _ } -> acc |> f body |> f handler
|
||||
| EStruct { fields; _ } -> acc |> StructField.Map.fold (fun _ -> f) fields
|
||||
| EDStructAmend { e; fields; _ } ->
|
||||
acc |> f e |> Ident.Map.fold (fun _ -> f) fields
|
||||
| EDStructAccess { e; _ } -> acc |> f e
|
||||
| EStructAccess { e; _ } -> acc |> f e
|
||||
| EMatch { e; cases; _ } ->
|
||||
@ -434,6 +451,16 @@ let map_gather
|
||||
(acc, StructField.Map.empty)
|
||||
in
|
||||
acc, estruct ~name ~fields m
|
||||
| EDStructAmend { name_opt; e; fields } ->
|
||||
let acc, e = f e in
|
||||
let acc, fields =
|
||||
Ident.Map.fold
|
||||
(fun cons e (acc, fields) ->
|
||||
let acc1, e = f e in
|
||||
join acc acc1, Ident.Map.add cons e fields)
|
||||
fields (acc, Ident.Map.empty)
|
||||
in
|
||||
acc, edstructamend ~name_opt ~e ~fields m
|
||||
| EDStructAccess { name_opt; field; e } ->
|
||||
let acc, e = f e in
|
||||
acc, edstructaccess ~name_opt ~field ~e m
|
||||
@ -618,6 +645,11 @@ and equal : type a. (a, 't) gexpr -> (a, 't) gexpr -> bool =
|
||||
| ( EStruct { name = s1; fields = fields1 },
|
||||
EStruct { name = s2; fields = fields2 } ) ->
|
||||
StructName.equal s1 s2 && StructField.Map.equal equal fields1 fields2
|
||||
| ( EDStructAmend { name_opt = s1; e = e1; fields = fields1 },
|
||||
EDStructAmend { name_opt = s2; e = e2; fields = fields2 } ) ->
|
||||
Option.equal StructName.equal s1 s2
|
||||
&& equal e1 e2
|
||||
&& Ident.Map.equal equal fields1 fields2
|
||||
| ( EDStructAccess { e = e1; field = f1; name_opt = s1 },
|
||||
EDStructAccess { e = e2; field = f2; name_opt = s2 } ) ->
|
||||
Option.equal StructName.equal s1 s2 && Ident.equal f1 f2 && equal e1 e2
|
||||
@ -641,8 +673,8 @@ and equal : type a. (a, 't) gexpr -> (a, 't) gexpr -> bool =
|
||||
| ( ( EVar _ | EExternal _ | ETuple _ | ETupleAccess _ | EArray _ | ELit _
|
||||
| EAbs _ | EApp _ | EAppOp _ | EAssert _ | EDefault _ | EPureDefault _
|
||||
| EIfThenElse _ | EEmptyError | EErrorOnEmpty _ | ERaise _ | ECatch _
|
||||
| ELocation _ | EStruct _ | EDStructAccess _ | EStructAccess _ | EInj _
|
||||
| EMatch _ | EScopeCall _ | ECustom _ ),
|
||||
| ELocation _ | EStruct _ | EDStructAmend _ | EDStructAccess _
|
||||
| EStructAccess _ | EInj _ | EMatch _ | EScopeCall _ | ECustom _ ),
|
||||
_ ) ->
|
||||
false
|
||||
|
||||
@ -685,6 +717,11 @@ let rec compare : type a. (a, _) gexpr -> (a, _) gexpr -> int =
|
||||
EStruct {name=name2; fields=field_map2 } ->
|
||||
StructName.compare name1 name2 @@< fun () ->
|
||||
StructField.Map.compare compare field_map1 field_map2
|
||||
| EDStructAmend {name_opt=n1; e=e1; fields=field_map1},
|
||||
EDStructAmend {name_opt=n2; e=e2; fields=field_map2} ->
|
||||
compare e1 e2 @@< fun () ->
|
||||
Ident.Map.compare compare field_map1 field_map2 @@< fun () ->
|
||||
Option.compare StructName.compare n1 n2
|
||||
| EDStructAccess {e=e1; field=field_name1; name_opt=struct_name1},
|
||||
EDStructAccess {e=e2; field=field_name2; name_opt=struct_name2} ->
|
||||
compare e1 e2 @@< fun () ->
|
||||
@ -748,6 +785,7 @@ let rec compare : type a. (a, _) gexpr -> (a, _) gexpr -> int =
|
||||
| EIfThenElse _, _ -> -1 | _, EIfThenElse _ -> 1
|
||||
| ELocation _, _ -> -1 | _, ELocation _ -> 1
|
||||
| EStruct _, _ -> -1 | _, EStruct _ -> 1
|
||||
| EDStructAmend _, _ -> -1 | _, EDStructAmend _ -> 1
|
||||
| EDStructAccess _, _ -> -1 | _, EDStructAccess _ -> 1
|
||||
| EStructAccess _, _ -> -1 | _, EStructAccess _ -> 1
|
||||
| EMatch _, _ -> -1 | _, EMatch _ -> 1
|
||||
@ -895,6 +933,8 @@ let rec size : type a. (a, 't) gexpr -> int =
|
||||
| ELocation _ -> 1
|
||||
| EStruct { fields; _ } ->
|
||||
StructField.Map.fold (fun _ e acc -> acc + 1 + size e) fields 0
|
||||
| EDStructAmend { e; fields; _ } ->
|
||||
1 + size e + Ident.Map.fold (fun _ e acc -> acc + 1 + size e) fields 0
|
||||
| EDStructAccess { e; _ } -> 1 + size e
|
||||
| EStructAccess { e; _ } -> 1 + size e
|
||||
| EMatch { e; cases; _ } ->
|
||||
|
@ -132,6 +132,13 @@ val estruct :
|
||||
'm mark ->
|
||||
('a any, 'm) boxed_gexpr
|
||||
|
||||
val edstructamend :
|
||||
name_opt:StructName.t option ->
|
||||
e:('a, 'm) boxed_gexpr ->
|
||||
fields:('a, 'm) boxed_gexpr Ident.Map.t ->
|
||||
'm mark ->
|
||||
((< syntacticNames : yes ; .. > as 'a), 'm) boxed_gexpr
|
||||
|
||||
val edstructaccess :
|
||||
name_opt:StructName.t option ->
|
||||
field:Ident.t ->
|
||||
|
@ -423,6 +423,7 @@ module Precedence = struct
|
||||
| ETupleAccess _ -> Dot
|
||||
| ELocation _ -> Contained
|
||||
| EScopeCall _ -> App
|
||||
| EDStructAmend _ -> App
|
||||
| EDStructAccess _ | EStructAccess _ -> Dot
|
||||
| EAssert _ -> App
|
||||
| EDefault _ -> Contained
|
||||
@ -681,6 +682,14 @@ module ExprGen (C : EXPR_PARAM) = struct
|
||||
| EDStructAccess { e; field; _ } ->
|
||||
Format.fprintf fmt "@[<hv 2>%a%a@,%a%a%a@]" (lhs exprc) e punctuation
|
||||
"." punctuation "\"" Ident.format field punctuation "\""
|
||||
| EDStructAmend { e; fields; _ } ->
|
||||
Format.fprintf fmt "@[<hv 2>@[<hov>%a %a@ with@]@ %a@;<1 -2>%a@]"
|
||||
punctuation "{" (lhs exprc) e
|
||||
(Ident.Map.format_bindings ~pp_sep:Format.pp_print_space
|
||||
(fun fmt pp_field_name field_expr ->
|
||||
Format.fprintf fmt "@[<hv 2>%t %a@ %a%a@]" pp_field_name
|
||||
punctuation "=" (lhs exprc) field_expr punctuation ";"))
|
||||
fields punctuation "}"
|
||||
| EStruct { name; fields } ->
|
||||
if StructField.Map.is_empty fields then (
|
||||
punctuation fmt "{";
|
||||
@ -1127,7 +1136,7 @@ module UserFacing = struct
|
||||
| EApp _ | EAppOp _ | EVar _ | EIfThenElse _ | EMatch _ | ETupleAccess _
|
||||
| EStructAccess _ | EAssert _ | EDefault _ | EPureDefault _
|
||||
| EErrorOnEmpty _ | ERaise _ | ECatch _ | ELocation _ | EScopeCall _
|
||||
| EDStructAccess _ | ECustom _ ->
|
||||
| EDStructAmend _ | EDStructAccess _ | ECustom _ ->
|
||||
fallback ppf e
|
||||
|
||||
let expr :
|
||||
|
@ -548,6 +548,25 @@ and typecheck_expr_top_down :
|
||||
fields
|
||||
in
|
||||
Expr.estruct ~name ~fields mark
|
||||
| A.EDStructAmend { name_opt = _; e; fields } ->
|
||||
let e = typecheck_expr_top_down ctx env tau e in
|
||||
let name =
|
||||
match UnionFind.get (ty e) with
|
||||
| TStruct name, _ -> name
|
||||
| TAny _, _ -> failwith "Disambiguation failure"
|
||||
| _ ->
|
||||
Message.error ~pos:(Expr.pos e)
|
||||
"This expression has type %a, where a structure was expected"
|
||||
(format_typ ctx) (ty e)
|
||||
in
|
||||
let fields = A.Ident.Map.map (typecheck_expr_bottom_up ctx env) fields in
|
||||
(* Note: here we identify the structure name, and type the fields
|
||||
individually, but without enforcing any consistency constraint between
|
||||
the two. This is fine because this construction only appears in
|
||||
Desugared, where it is used for disambiguation. In later passes this is
|
||||
rewritten into a struct literal, so no need to anticipate name resolution
|
||||
and duplicate the checks here. *)
|
||||
Expr.edstructamend ~name_opt:(Some name) ~e ~fields context_mark
|
||||
| A.EDStructAccess { e = e_struct; name_opt; field } ->
|
||||
let t_struct =
|
||||
match name_opt with
|
||||
|
@ -187,6 +187,7 @@ and naked_expression =
|
||||
| EnumInject of (path * uident Mark.pos) Mark.pos * expression option
|
||||
| StructLit of
|
||||
(path * uident Mark.pos) Mark.pos * (lident Mark.pos * expression) list
|
||||
| StructReplace of expression * (lident Mark.pos * expression) list
|
||||
| ArrayLit of expression list
|
||||
| Tuple of expression list
|
||||
| Ident of path * lident Mark.pos * lident Mark.pos option
|
||||
|
@ -206,6 +206,9 @@ module R = Re.Pcre
|
||||
#ifndef MR_LIST_EMPTY
|
||||
#define MR_LIST_EMPTY MS_LIST_EMPTY
|
||||
#endif
|
||||
#ifndef MR_BUT_REPLACE
|
||||
#define MR_BUT_REPLACE MS_BUT_REPLACE
|
||||
#endif
|
||||
#ifndef MR_CARDINAL
|
||||
#define MR_CARDINAL MS_CARDINAL
|
||||
#endif
|
||||
@ -316,6 +319,7 @@ let token_list : (string * token) list =
|
||||
(MS_MINIMUM, MINIMUM);
|
||||
(MS_IS, IS);
|
||||
(MS_LIST_EMPTY, LIST_EMPTY);
|
||||
(MS_BUT_REPLACE, BUT_REPLACE);
|
||||
(MS_CARDINAL, CARDINAL);
|
||||
(MS_YEAR, YEAR);
|
||||
(MS_MONTH, MONTH);
|
||||
@ -570,6 +574,9 @@ let rec lex_code (lexbuf : lexbuf) : token =
|
||||
| MR_LIST_EMPTY ->
|
||||
L.update_acc lexbuf;
|
||||
LIST_EMPTY
|
||||
| MR_BUT_REPLACE ->
|
||||
L.update_acc lexbuf;
|
||||
BUT_REPLACE
|
||||
| MR_CARDINAL ->
|
||||
L.update_acc lexbuf;
|
||||
CARDINAL
|
||||
|
@ -83,6 +83,8 @@
|
||||
#define MS_IS "is"
|
||||
#define MS_LIST_EMPTY "list empty"
|
||||
#define MR_LIST_EMPTY "list", space_plus, "empty"
|
||||
#define MS_BUT_REPLACE "but replace"
|
||||
#define MR_BUT_REPLACE "but", space_plus, "replace"
|
||||
#define MS_CARDINAL "number"
|
||||
#define MS_YEAR "year"
|
||||
#define MS_MONTH "month"
|
||||
|
@ -103,6 +103,8 @@
|
||||
#define MS_IS "est"
|
||||
#define MS_LIST_EMPTY "liste vide"
|
||||
#define MR_LIST_EMPTY "liste", space_plus, "vide"
|
||||
#define MS_BUT_REPLACE "mais en rempla\195\167ant"
|
||||
#define MR_BUT_REPLACE "mais", space_plus, "en", space_plus, "rempla", 0xE7, "ant"
|
||||
#define MS_CARDINAL "nombre"
|
||||
#define MS_YEAR "an"
|
||||
#define MS_MONTH "mois"
|
||||
|
@ -95,6 +95,8 @@
|
||||
#define MS_IS "jest"
|
||||
#define MS_LIST_EMPTY "lista pusta"
|
||||
#define MR_LIST_EMPTY "lista", space_plus, "pusta"
|
||||
#define MS_BUT_REPLACE "ale zastąpić"
|
||||
#define MR_BUT_REPLACE "ale", space_plus, "zast", 0x0105, "pi", 0x0107
|
||||
#define MS_CARDINAL "liczba"
|
||||
#define MS_YEAR "rok"
|
||||
#define MS_MONTH "miesiąc"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -37,7 +37,7 @@ end>
|
||||
%nonassoc GREATER GREATER_EQUAL LESSER LESSER_EQUAL EQUAL NOT_EQUAL
|
||||
%left PLUS MINUS PLUSPLUS
|
||||
%left MULT DIV
|
||||
%right apply OF CONTAINS FOR SUCH WITH
|
||||
%right apply OF CONTAINS FOR SUCH WITH BUT_REPLACE
|
||||
%right COMMA
|
||||
%right unop_expr
|
||||
%right CONTENT
|
||||
@ -218,6 +218,13 @@ let naked_expression ==
|
||||
WITH ; c = constructor_binding ; {
|
||||
TestMatchCase (e, (c, Pos.from_lpos $sloc))
|
||||
}
|
||||
| e = expression ;
|
||||
BUT_REPLACE ;
|
||||
LBRACE ;
|
||||
fields = nonempty_list(preceded (ALT, struct_content_field)) ;
|
||||
RBRACE ; {
|
||||
StructReplace (e, fields)
|
||||
}
|
||||
| e1 = expression ;
|
||||
CONTAINS ;
|
||||
e2 = expression ; {
|
||||
|
@ -55,6 +55,6 @@
|
||||
%token BEGIN_METADATA MONEY DECIMAL
|
||||
%token UNDER_CONDITION CONSEQUENCE LBRACE RBRACE
|
||||
%token LABEL EXCEPTION LBRACKET RBRACKET SEMICOLON
|
||||
%token MAXIMUM MINIMUM IS LIST_EMPTY
|
||||
%token MAXIMUM MINIMUM IS LIST_EMPTY BUT_REPLACE
|
||||
|
||||
%%
|
||||
|
36
tests/struct/bad/struct_update.catala_en
Normal file
36
tests/struct/bad/struct_update.catala_en
Normal file
@ -0,0 +1,36 @@
|
||||
```catala
|
||||
declaration structure Str:
|
||||
data fld1 content integer
|
||||
data fld2 content money
|
||||
data fld3 content date
|
||||
|
||||
declaration scope S:
|
||||
internal s0 content Str
|
||||
output s1 content Str
|
||||
|
||||
scope S:
|
||||
definition s0 equals Str {
|
||||
-- fld1: 0
|
||||
-- fld2: $1
|
||||
-- fld3: |2003-01-01|
|
||||
}
|
||||
definition s1 equals s0 but replace { --fld1: 0 -- flx1: 99 }
|
||||
```
|
||||
|
||||
|
||||
```catala-test-inline
|
||||
$ catala test-scope S
|
||||
[ERROR] Field flx1 does not belong to structure Str
|
||||
|
||||
┌─⯈ tests/struct/bad/struct_update.catala_en:17.60-17.62:
|
||||
└──┐
|
||||
17 │ definition s1 equals s0 but replace { --fld1: 0 -- flx1: 99 }
|
||||
│ ‾‾
|
||||
|
||||
Declaration of structure Str
|
||||
┌─⯈ tests/struct/bad/struct_update.catala_en:2.23-2.26:
|
||||
└─┐
|
||||
2 │ declaration structure Str:
|
||||
│ ‾‾‾
|
||||
#return code 123#
|
||||
```
|
47
tests/struct/good/struct_update.catala_en
Normal file
47
tests/struct/good/struct_update.catala_en
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
```catala
|
||||
declaration structure Str:
|
||||
data fld1 content integer
|
||||
data fld2 content money
|
||||
data fld3 content date
|
||||
|
||||
declaration scope S:
|
||||
internal s0 content Str
|
||||
output s1 content Str
|
||||
output s2 content Str
|
||||
output s3 content Str
|
||||
output s4 content Str
|
||||
output s5 content Str
|
||||
|
||||
scope S:
|
||||
definition s0 equals Str {
|
||||
-- fld1: 0
|
||||
-- fld2: $1
|
||||
-- fld3: |2003-01-01|
|
||||
}
|
||||
definition s1 equals s0 but replace { -- fld1: 99 }
|
||||
definition s2 equals s0 but replace { -- fld2: $99 }
|
||||
definition s3 equals s2 but replace { -- fld1: 99 -- fld3: |2099-01-01| }
|
||||
definition s4 equals s3 but replace { -- fld1: 100 -- fld2: $100 -- fld3: |2100-01-01| }
|
||||
definition s5 equals s0
|
||||
but replace { -- fld1: 99 }
|
||||
but replace { -- fld2: $99 }
|
||||
but replace { -- fld1: 100 -- fld3 : |2100-01-01| }
|
||||
```
|
||||
|
||||
|
||||
```catala-test-inline
|
||||
$ catala test-scope S
|
||||
[WARNING] All fields of Str are rewritten in this replacement.
|
||||
|
||||
┌─⯈ tests/struct/good/struct_update.catala_en:25.24-25.91:
|
||||
└──┐
|
||||
25 │ definition s4 equals s3 but replace { -- fld1: 100 -- fld2: $100 -- fld3: |2100-01-01| }
|
||||
│ ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
|
||||
[RESULT] Computation successful! Results:
|
||||
[RESULT] s1 = Str { -- fld1: 99 -- fld2: $1.00 -- fld3: 2003-01-01 }
|
||||
[RESULT] s2 = Str { -- fld1: 0 -- fld2: $99.00 -- fld3: 2003-01-01 }
|
||||
[RESULT] s3 = Str { -- fld1: 99 -- fld2: $99.00 -- fld3: 2099-01-01 }
|
||||
[RESULT] s4 = Str { -- fld1: 100 -- fld2: $100.00 -- fld3: 2100-01-01 }
|
||||
[RESULT] s5 = Str { -- fld1: 100 -- fld2: $99.00 -- fld3: 2100-01-01 }
|
||||
```
|
Loading…
Reference in New Issue
Block a user