2015-04-18 01:16:08 +00:00
//: Exclusive containers contain exactly one of a fixed number of 'variants'
//: of different types.
//:
//: They also implicitly contain a tag describing precisely which variant is
//: currently stored in them.
: ( before " End Mu Types Initialization " )
2015-05-13 17:03:26 +00:00
//: We'll use this container as a running example, with two number elements.
2015-04-18 01:16:08 +00:00
{
2015-11-06 19:06:58 +00:00
type_ordinal tmp = put ( Type_ordinal , " number-or-point " , Next_type_ordinal + + ) ;
2016-03-28 17:11:23 +00:00
get_or_insert ( Type , tmp ) ; // initialize
2015-11-06 19:06:58 +00:00
get ( Type , tmp ) . kind = EXCLUSIVE_CONTAINER ;
get ( Type , tmp ) . name = " number-or-point " ;
2016-02-17 18:09:48 +00:00
get ( Type , tmp ) . elements . push_back ( reagent ( " i:number " ) ) ;
get ( Type , tmp ) . elements . push_back ( reagent ( " p:point " ) ) ;
2015-04-18 01:16:08 +00:00
}
2015-05-26 22:48:35 +00:00
//: Tests in this layer often explicitly setup memory before reading it as an
//: array. Don't do this in general. I'm tagging exceptions with /raw to
2015-10-07 05:15:45 +00:00
//: avoid errors.
2015-04-24 17:19:03 +00:00
: ( scenario copy_exclusive_container )
2015-04-18 01:16:08 +00:00
# Copying exclusive containers copies all their contents and an extra location for the tag.
2016-03-08 09:30:14 +00:00
def main [
2015-07-28 21:33:22 +00:00
1 : number < - copy 1 # ' point ' variant
2 : number < - copy 34
3 : number < - copy 35
2016-01-12 06:57:35 +00:00
4 : number - or - point < - copy 1 : number - or - point / unsafe
2015-04-18 01:16:08 +00:00
]
+ mem : storing 1 in location 4
+ mem : storing 34 in location 5
+ mem : storing 35 in location 6
2015-10-26 04:42:18 +00:00
: ( before " End size_of(type) Cases " )
2016-05-03 16:14:19 +00:00
if ( t . kind = = EXCLUSIVE_CONTAINER ) {
// Compute size_of Exclusive Container
return get ( Container_metadata , type ) . size ;
}
2016-05-03 06:11:33 +00:00
: ( before " End compute_container_metadata Cases " )
if ( info . kind = = EXCLUSIVE_CONTAINER ) {
container_metadata metadata ;
2015-04-18 01:16:08 +00:00
// size of an exclusive container is the size of its largest variant
2015-04-18 03:36:25 +00:00
// (So like containers, it can't contain arrays.)
2016-05-03 06:11:33 +00:00
int size = 0 ;
for ( int i = 0 ; i < SIZE ( info . elements ) ; + + i ) {
reagent element = info . elements . at ( i ) ;
// Compute Exclusive Container Metadata(element)
compute_container_metadata ( element ) ;
int variant_size = size_of ( element ) ;
if ( variant_size > size ) size = variant_size ;
2015-04-18 01:16:08 +00:00
}
// ...+1 for its tag.
2016-05-03 06:11:33 +00:00
metadata . size = size + 1 ;
Container_metadata . push_back ( pair < type_tree * , container_metadata > ( new type_tree ( * type ) , metadata ) ) ;
2015-04-18 01:16:08 +00:00
}
2015-04-18 01:31:13 +00:00
2015-04-18 06:24:52 +00:00
//:: To access variants of an exclusive container, use 'maybe-convert'.
2015-04-18 01:32:39 +00:00
//: It always returns an address (so that you can modify it) or null (to
//: signal that the conversion failed (because the container contains a
//: different variant).
//: 'maybe-convert' requires a literal in ingredient 1. We'll use a synonym
//: called 'variant'.
: ( before " End Mu Types Initialization " )
2015-11-06 19:06:58 +00:00
put ( Type_ordinal , " variant " , 0 ) ;
2015-04-18 01:32:39 +00:00
2015-04-24 17:19:03 +00:00
: ( scenario maybe_convert )
2016-03-08 09:30:14 +00:00
def main [
2015-07-28 21:33:22 +00:00
12 : number < - copy 1
13 : number < - copy 35
14 : number < - copy 36
2016-04-24 00:02:16 +00:00
20 : point , 22 : boolean < - maybe - convert 12 : number - or - point / unsafe , 1 : variant
2015-04-18 01:32:39 +00:00
]
2016-04-24 00:02:16 +00:00
# point
+ mem : storing 35 in location 20
+ mem : storing 36 in location 21
# boolean
+ mem : storing 1 in location 22
2015-04-18 01:32:39 +00:00
2015-04-24 17:19:03 +00:00
: ( scenario maybe_convert_fail )
2016-03-08 09:30:14 +00:00
def main [
2015-07-28 21:33:22 +00:00
12 : number < - copy 1
13 : number < - copy 35
14 : number < - copy 36
2016-04-24 00:02:16 +00:00
20 : number , 21 : boolean < - maybe - convert 12 : number - or - point / unsafe , 0 : variant
2015-04-18 01:32:39 +00:00
]
2016-04-24 00:02:16 +00:00
# number: no write
# boolean
+ mem : storing 0 in location 21
2015-04-18 01:32:39 +00:00
2015-04-18 01:31:13 +00:00
: ( before " End Primitive Recipe Declarations " )
MAYBE_CONVERT ,
: ( before " End Primitive Recipe Numbers " )
2015-11-06 19:06:58 +00:00
put ( Recipe_ordinal , " maybe-convert " , MAYBE_CONVERT ) ;
2015-10-01 23:28:47 +00:00
: ( before " End Primitive Recipe Checks " )
2015-04-18 01:32:39 +00:00
case MAYBE_CONVERT : {
2016-01-21 04:32:43 +00:00
const recipe & caller = get ( Recipe , r ) ;
2015-10-01 23:28:47 +00:00
if ( SIZE ( inst . ingredients ) ! = 2 ) {
2016-03-21 09:25:52 +00:00
raise < < maybe ( caller . name ) < < " 'maybe-convert' expects exactly 2 ingredients in ' " < < to_original_string ( inst ) < < " ' \n " < < end ( ) ;
2015-07-25 21:19:28 +00:00
break ;
}
2015-10-01 23:28:47 +00:00
reagent base = inst . ingredients . at ( 0 ) ;
2016-04-24 07:36:30 +00:00
// Update MAYBE_CONVERT base in Check
2015-11-06 19:06:58 +00:00
if ( ! base . type | | ! base . type - > value | | get ( Type , base . type - > value ) . kind ! = EXCLUSIVE_CONTAINER ) {
2016-02-26 21:04:55 +00:00
raise < < maybe ( caller . name ) < < " first ingredient of 'maybe-convert' should be an exclusive-container, but got " < < base . original_string < < ' \n ' < < end ( ) ;
2015-08-01 00:06:38 +00:00
break ;
}
2015-10-01 23:28:47 +00:00
if ( ! is_literal ( inst . ingredients . at ( 1 ) ) ) {
2016-02-26 21:04:55 +00:00
raise < < maybe ( caller . name ) < < " second ingredient of 'maybe-convert' should have type 'variant', but got " < < inst . ingredients . at ( 1 ) . original_string < < ' \n ' < < end ( ) ;
2016-01-21 04:32:43 +00:00
break ;
}
if ( inst . products . empty ( ) ) break ;
2016-04-24 00:02:16 +00:00
if ( SIZE ( inst . products ) ! = 2 ) {
raise < < maybe ( caller . name ) < < " 'maybe-convert' expects exactly 2 products in ' " < < to_original_string ( inst ) < < " ' \n " < < end ( ) ;
break ;
}
2016-01-21 04:32:43 +00:00
reagent product = inst . products . at ( 0 ) ;
2016-04-24 07:36:30 +00:00
// Update MAYBE_CONVERT product in Check
2016-02-15 00:16:11 +00:00
reagent & offset = inst . ingredients . at ( 1 ) ;
populate_value ( offset ) ;
if ( offset . value > = SIZE ( get ( Type , base . type - > value ) . elements ) ) {
2016-03-21 09:25:52 +00:00
raise < < maybe ( caller . name ) < < " invalid tag " < < offset . value < < " in ' " < < to_original_string ( inst ) < < ' \n ' < < end ( ) ;
2016-02-15 00:16:11 +00:00
break ;
2016-01-21 04:32:43 +00:00
}
2016-02-15 00:16:11 +00:00
reagent variant = variant_type ( base , offset . value ) ;
2016-01-21 04:32:43 +00:00
if ( ! types_coercible ( product , variant ) ) {
2016-02-26 21:04:55 +00:00
raise < < maybe ( caller . name ) < < " 'maybe-convert " < < base . original_string < < " , " < < inst . ingredients . at ( 1 ) . original_string < < " ' should write to " < < to_string ( variant . type ) < < " but " < < product . name < < " has type " < < to_string ( product . type ) < < ' \n ' < < end ( ) ;
2015-07-24 08:17:36 +00:00
break ;
}
2016-04-24 07:36:30 +00:00
reagent status = inst . products . at ( 1 ) ;
// Update MAYBE_CONVERT status in Check
if ( ! is_mu_boolean ( status ) ) {
2016-04-24 00:02:16 +00:00
raise < < maybe ( get ( Recipe , r ) . name ) < < " second product yielded by 'maybe-convert' should be a boolean, but tried to write to " < < inst . products . at ( 1 ) . original_string < < ' \n ' < < end ( ) ;
break ;
}
2015-10-01 23:28:47 +00:00
break ;
}
: ( before " End Primitive Recipe Implementations " )
case MAYBE_CONVERT : {
2015-10-26 04:42:18 +00:00
reagent base = current_instruction ( ) . ingredients . at ( 0 ) ;
2016-04-24 07:36:30 +00:00
// Update MAYBE_CONVERT base in Run
2016-03-14 03:26:47 +00:00
int base_address = base . value ;
2015-10-01 23:28:47 +00:00
if ( base_address = = 0 ) {
2016-03-21 09:25:52 +00:00
raise < < maybe ( current_recipe_name ( ) ) < < " tried to access location 0 in ' " < < to_original_string ( current_instruction ( ) ) < < " ' \n " < < end ( ) ;
2015-07-24 08:17:36 +00:00
break ;
}
2016-03-14 03:26:47 +00:00
int tag = current_instruction ( ) . ingredients . at ( 1 ) . value ;
2016-04-24 00:02:16 +00:00
reagent product = current_instruction ( ) . products . at ( 0 ) ;
2016-04-24 07:36:30 +00:00
// Update MAYBE_CONVERT product in Run
reagent status = current_instruction ( ) . products . at ( 1 ) ;
// Update MAYBE_CONVERT status in Run
2016-04-24 00:02:16 +00:00
// optimization: directly write results to only update first product when necessary
2016-03-14 03:26:47 +00:00
if ( tag = = static_cast < int > ( get_or_insert ( Memory , base_address ) ) ) {
2016-04-24 00:02:16 +00:00
const reagent variant = variant_type ( base , tag ) ;
for ( int i = 0 ; i < size_of ( variant ) ; + + i ) {
double val = get_or_insert ( Memory , base_address + 1 + i ) ;
trace ( 9999 , " mem " ) < < " storing " < < no_scientific ( val ) < < " in location " < < product . value + i < < end ( ) ;
put ( Memory , product . value + i , val ) ;
}
2016-04-24 07:36:30 +00:00
trace ( 9999 , " mem " ) < < " storing 1 in location " < < status . value < < end ( ) ;
put ( Memory , status . value , 1 ) ;
2015-04-18 01:32:39 +00:00
}
else {
2016-04-24 07:36:30 +00:00
trace ( 9999 , " mem " ) < < " storing 0 in location " < < status . value < < end ( ) ;
put ( Memory , status . value , 0 ) ;
2015-04-18 01:32:39 +00:00
}
2016-04-24 00:02:16 +00:00
goto finish_instruction ;
2015-04-18 01:32:39 +00:00
}
2015-05-14 17:30:01 +00:00
2016-01-21 04:32:43 +00:00
: ( code )
2016-04-24 07:36:30 +00:00
const reagent variant_type ( const reagent & base , int tag ) {
2016-01-21 04:32:43 +00:00
assert ( tag > = 0 ) ;
2016-04-24 07:36:30 +00:00
assert ( contains_key ( Type , base . type - > value ) ) ;
assert ( ! get ( Type , base . type - > value ) . name . empty ( ) ) ;
const type_info & info = get ( Type , base . type - > value ) ;
2016-01-21 04:32:43 +00:00
assert ( info . kind = = EXCLUSIVE_CONTAINER ) ;
2016-02-17 18:09:48 +00:00
reagent element = info . elements . at ( tag ) ;
2016-01-21 04:32:43 +00:00
// End variant_type Special-cases
return element ;
}
: ( scenario maybe_convert_product_type_mismatch )
% Hide_errors = true ;
2016-03-08 09:30:14 +00:00
def main [
2016-01-21 04:32:43 +00:00
12 : number < - copy 1
13 : number < - copy 35
14 : number < - copy 36
2016-04-24 00:02:16 +00:00
20 : number , 21 : boolean < - maybe - convert 12 : number - or - point / unsafe , 1 : variant
2016-01-21 04:32:43 +00:00
]
2016-04-24 00:02:16 +00:00
+ error : main : ' maybe - convert 12 : number - or - point / unsafe , 1 : variant ' should write to point but 20 has type number
2016-01-21 04:32:43 +00:00
2015-05-14 17:30:01 +00:00
//:: Allow exclusive containers to be defined in mu code.
: ( scenario exclusive_container )
exclusive - container foo [
x : number
y : number
]
2015-10-29 18:56:10 +00:00
+ parse : - - - defining exclusive - container foo
2016-03-21 09:25:52 +00:00
+ parse : element : { x : " number " }
+ parse : element : { y : " number " }
2015-05-14 17:30:01 +00:00
: ( before " End Command Handlers " )
else if ( command = = " exclusive-container " ) {
2015-10-27 03:06:51 +00:00
insert_container ( command , EXCLUSIVE_CONTAINER , in ) ;
2015-05-14 17:30:01 +00:00
}
2015-06-23 19:05:18 +00:00
2016-02-15 20:56:45 +00:00
//: arrays are disallowed inside exclusive containers unless their length is
//: fixed in advance
: ( scenario exclusive_container_contains_array )
exclusive - container foo [
x : array : number : 3
]
$ error : 0
2016-02-25 19:29:42 +00:00
: ( scenario exclusive_container_disallows_dynamic_array_element )
2016-02-15 20:56:45 +00:00
% Hide_errors = true ;
exclusive - container foo [
x : array : number
]
+ error : container ' foo ' cannot determine size of element x
2015-06-23 19:05:18 +00:00
//:: To construct exclusive containers out of variant types, use 'merge'.
: ( scenario lift_to_exclusive_container )
exclusive - container foo [
x : number
y : number
]
2016-03-08 09:30:14 +00:00
def main [
2015-07-28 21:33:22 +00:00
1 : number < - copy 34
2016-02-15 00:16:11 +00:00
2 : foo < - merge 0 / x , 1 : number # tag must be a literal when merging exclusive containers
4 : foo < - merge 1 / y , 1 : number
2015-06-23 19:05:18 +00:00
]
+ mem : storing 0 in location 2
+ mem : storing 34 in location 3
+ mem : storing 1 in location 4
+ mem : storing 34 in location 5
2015-08-31 05:13:22 +00:00
2016-02-15 07:30:59 +00:00
//: type-checking for 'merge' on exclusive containers
: ( scenario merge_handles_exclusive_container )
exclusive - container foo [
x : number
y : bar
]
container bar [
z : number
]
2016-03-08 09:30:14 +00:00
def main [
2016-02-15 07:30:59 +00:00
1 : foo < - merge 0 / x , 34
]
+ mem : storing 0 in location 1
+ mem : storing 34 in location 2
$ error : 0
: ( scenario merge_requires_literal_tag_for_exclusive_container )
% Hide_errors = true ;
exclusive - container foo [
x : number
y : bar
]
container bar [
z : number
]
2016-03-08 09:30:14 +00:00
def main [
2016-02-15 07:30:59 +00:00
local - scope
1 : number < - copy 0
2 : foo < - merge 1 : number , 34
]
+ error : main : ingredient 0 of ' merge ' should be a literal , for the tag of exclusive - container foo
: ( before " End valid_merge Cases " )
case EXCLUSIVE_CONTAINER : {
assert ( state . data . top ( ) . container_element_index = = 0 ) ;
2016-02-19 23:19:36 +00:00
trace ( 9999 , " transform " ) < < " checking exclusive container " < < to_string ( container ) < < " vs ingredient " < < ingredient_index < < end ( ) ;
2016-02-15 07:30:59 +00:00
if ( ! is_literal ( ingredients . at ( ingredient_index ) ) ) {
2016-02-26 21:04:55 +00:00
raise < < maybe ( caller . name ) < < " ingredient " < < ingredient_index < < " of 'merge' should be a literal, for the tag of exclusive-container " < < container_info . name < < ' \n ' < < end ( ) ;
2016-02-15 07:30:59 +00:00
return ;
}
reagent ingredient = ingredients . at ( ingredient_index ) ; // unnecessary copy just to keep this function from modifying caller
populate_value ( ingredient ) ;
if ( ingredient . value > = SIZE ( container_info . elements ) ) {
2016-03-21 09:25:52 +00:00
raise < < maybe ( caller . name ) < < " invalid tag at " < < ingredient_index < < " for " < < container_info . name < < " in ' " < < to_original_string ( inst ) < < ' \n ' < < end ( ) ;
2016-02-15 07:30:59 +00:00
return ;
}
reagent variant = variant_type ( container , ingredient . value ) ;
trace ( 9999 , " transform " ) < < " tag: " < < ingredient . value < < end ( ) ;
// replace union with its variant
state . data . pop ( ) ;
state . data . push ( merge_check_point ( variant , 0 ) ) ;
+ + ingredient_index ;
break ;
}
: ( scenario merge_check_container_containing_exclusive_container )
container foo [
x : number
y : bar
]
exclusive - container bar [
x : number
y : number
]
2016-03-08 09:30:14 +00:00
def main [
2016-02-15 07:30:59 +00:00
1 : foo < - merge 23 , 1 / y , 34
]
+ mem : storing 23 in location 1
+ mem : storing 1 in location 2
+ mem : storing 34 in location 3
$ error : 0
: ( scenario merge_check_container_containing_exclusive_container_2 )
% Hide_errors = true ;
container foo [
x : number
y : bar
]
exclusive - container bar [
x : number
y : number
]
2016-03-08 09:30:14 +00:00
def main [
2016-02-15 07:30:59 +00:00
1 : foo < - merge 23 , 1 / y , 34 , 35
]
+ error : main : too many ingredients in ' 1 : foo < - merge 23 , 1 / y , 34 , 35 '
: ( scenario merge_check_exclusive_container_containing_container )
exclusive - container foo [
x : number
y : bar
]
container bar [
x : number
y : number
]
2016-03-08 09:30:14 +00:00
def main [
2016-02-15 07:30:59 +00:00
1 : foo < - merge 1 / y , 23 , 34
]
+ mem : storing 1 in location 1
+ mem : storing 23 in location 2
+ mem : storing 34 in location 3
$ error : 0
: ( scenario merge_check_exclusive_container_containing_container_2 )
exclusive - container foo [
x : number
y : bar
]
container bar [
x : number
y : number
]
2016-03-08 09:30:14 +00:00
def main [
2016-02-15 07:30:59 +00:00
1 : foo < - merge 0 / x , 23
]
$ error : 0
: ( scenario merge_check_exclusive_container_containing_container_3 )
% Hide_errors = true ;
exclusive - container foo [
x : number
y : bar
]
container bar [
x : number
y : number
]
2016-03-08 09:30:14 +00:00
def main [
2016-02-15 07:30:59 +00:00
1 : foo < - merge 1 / y , 23
]
+ error : main : too few ingredients in ' 1 : foo < - merge 1 / y , 23 '
2016-04-24 00:02:16 +00:00
: ( scenario merge_check_exclusive_container_containing_container_4 )
exclusive - container foo [
x : number
y : bar
]
container bar [
a : number
b : number
]
def main [
1 : bar < - merge 23 , 24
3 : foo < - merge 1 / y , 1 : bar
]
$ error : 0
2015-08-31 05:13:22 +00:00
//: Since the different variants of an exclusive-container might have
//: different sizes, relax the size mismatch check for 'merge' instructions.
: ( before " End size_mismatch(x) Cases " )
2016-03-14 00:33:01 +00:00
if ( current_step_index ( ) < SIZE ( Current_routine - > steps ( ) )
& & current_instruction ( ) . operation = = MERGE
2015-08-31 05:13:22 +00:00
& & ! current_instruction ( ) . products . empty ( )
2015-10-26 04:42:18 +00:00
& & current_instruction ( ) . products . at ( 0 ) . type ) {
reagent x = current_instruction ( ) . products . at ( 0 ) ;
2016-04-24 07:36:30 +00:00
// Update size_mismatch Check for MERGE(x)
2016-03-14 00:33:01 +00:00
if ( get ( Type , x . type - > value ) . kind = = EXCLUSIVE_CONTAINER )
2015-08-31 05:13:22 +00:00
return size_of ( x ) < SIZE ( data ) ;
}
: ( scenario merge_exclusive_container_with_mismatched_sizes )
container foo [
x : number
y : number
]
exclusive - container bar [
x : number
y : foo
]
2016-03-08 09:30:14 +00:00
def main [
2015-08-31 05:13:22 +00:00
1 : number < - copy 34
2 : number < - copy 35
3 : bar < - merge 0 / x , 1 : number
6 : bar < - merge 1 / foo , 1 : number , 2 : number
]
+ mem : storing 0 in location 3
+ mem : storing 34 in location 4
# bar is always 3 large so location 5 is skipped
+ mem : storing 1 in location 6
+ mem : storing 34 in location 7
+ mem : storing 35 in location 8