2015-04-16 17:35:49 +00:00
//: Containers contain a fixed number of elements of different types.
2015-04-17 18:22:59 +00:00
2015-03-17 03:26:59 +00:00
: ( 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-11-06 19:06:58 +00:00
type_ordinal point = put ( Type_ordinal , " point " , Next_type_ordinal + + ) ;
2015-11-06 19:31:37 +00:00
get_or_insert ( Type , point ) . size = 2 ;
2015-11-06 19:06:58 +00:00
get ( Type , point ) . kind = CONTAINER ;
get ( Type , point ) . name = " point " ;
2016-02-06 21:03:10 +00:00
get ( Type , point ) . element_type_names . push_back ( new string_tree ( " number " ) ) ;
2015-11-06 19:06:58 +00:00
get ( Type , point ) . elements . push_back ( new type_tree ( number ) ) ;
get ( Type , point ) . element_names . push_back ( " x " ) ;
2016-02-06 21:03:10 +00:00
get ( Type , point ) . element_type_names . push_back ( new string_tree ( " number " ) ) ;
2015-11-06 19:06:58 +00:00
get ( Type , point ) . elements . push_back ( new type_tree ( number ) ) ;
get ( Type , point ) . element_names . push_back ( " y " ) ;
2015-02-20 07:49:13 +00:00
2015-05-07 22:06:53 +00:00
//: Containers can be copied around with a single instruction just like
2015-05-13 17:03:26 +00:00
//: numbers, no matter how large they are.
2015-05-07 22:06:53 +00:00
2015-05-26 22:48:35 +00:00
//: Tests in this layer often explicitly setup memory before reading it as a
//: container. Don't do this in general. I'm tagging exceptions with /raw to
2015-10-07 05:15:45 +00:00
//: avoid errors.
2015-02-20 07:49:13 +00:00
: ( scenario copy_multiple_locations )
recipe main [
2015-07-28 21:33:22 +00:00
1 : number < - copy 34
2 : number < - copy 35
2016-01-12 06:57:35 +00:00
3 : point < - copy 1 : point / unsafe
2015-02-20 07:49:13 +00:00
]
2015-03-24 06:59:59 +00:00
+ mem : storing 34 in location 3
+ mem : storing 35 in location 4
2015-02-20 08:03:47 +00:00
2015-10-01 20:43:32 +00:00
//: trying to copy to a differently-typed destination will fail
2015-08-07 20:01:49 +00:00
: ( scenario copy_checks_size )
2015-10-07 05:15:45 +00:00
% Hide_errors = true ;
2015-08-07 20:01:49 +00:00
recipe main [
2 : point < - copy 1 : number
]
2015-10-07 05:15:45 +00:00
+ error : main : can ' t copy 1 : number to 2 : point ; types don ' t match
2015-08-07 20:01:49 +00:00
2015-03-27 04:06:14 +00:00
: ( before " End Mu Types Initialization " )
2015-04-16 17:35:49 +00:00
// A more complex container, containing another container as one of its
// elements.
2015-11-06 19:06:58 +00:00
type_ordinal point_number = put ( Type_ordinal , " point-number " , Next_type_ordinal + + ) ;
2015-11-06 19:31:37 +00:00
get_or_insert ( Type , point_number ) . size = 2 ;
2015-11-06 19:06:58 +00:00
get ( Type , point_number ) . kind = CONTAINER ;
get ( Type , point_number ) . name = " point-number " ;
get ( Type , point_number ) . elements . push_back ( new type_tree ( point ) ) ;
2016-02-06 21:03:10 +00:00
get ( Type , point_number ) . element_type_names . push_back ( new string_tree ( " point " ) ) ;
2015-11-06 19:06:58 +00:00
get ( Type , point_number ) . element_names . push_back ( " xy " ) ;
get ( Type , point_number ) . elements . push_back ( new type_tree ( number ) ) ;
2016-02-06 21:03:10 +00:00
get ( Type , point_number ) . element_type_names . push_back ( new string_tree ( " number " ) ) ;
2015-11-06 19:06:58 +00:00
get ( Type , point_number ) . element_names . push_back ( " z " ) ;
2015-03-27 04:06:14 +00:00
2015-04-24 17:19:03 +00:00
: ( scenario copy_handles_nested_container_elements )
2015-03-27 04:06:14 +00:00
recipe main [
2015-07-28 21:33:22 +00:00
12 : number < - copy 34
13 : number < - copy 35
14 : number < - copy 36
2016-01-12 06:57:35 +00:00
15 : point - number < - copy 12 : point - number / unsafe
2015-03-27 04:06:14 +00:00
]
+ mem : storing 36 in location 17
2015-05-07 22:06:53 +00:00
//: Containers can be checked for equality with a single instruction just like
2015-05-13 17:03:26 +00:00
//: numbers, no matter how large they are.
2015-05-07 22:06:53 +00:00
: ( scenario compare_multiple_locations )
recipe main [
2015-07-28 21:33:22 +00:00
1 : number < - copy 34 # first
2 : number < - copy 35
3 : number < - copy 36
4 : number < - copy 34 # second
5 : number < - copy 35
6 : number < - copy 36
2016-01-12 06:57:35 +00:00
7 : boolean < - equal 1 : point - number / raw , 4 : point - number / unsafe
2015-05-07 22:06:53 +00:00
]
+ mem : storing 1 in location 7
2015-08-09 19:26:31 +00:00
: ( scenario compare_multiple_locations_2 )
2015-05-07 22:06:53 +00:00
recipe main [
2015-07-28 21:33:22 +00:00
1 : number < - copy 34 # first
2 : number < - copy 35
3 : number < - copy 36
4 : number < - copy 34 # second
5 : number < - copy 35
6 : number < - copy 37 # different
2016-01-12 06:57:35 +00:00
7 : boolean < - equal 1 : point - number / raw , 4 : point - number / unsafe
2015-05-07 22:06:53 +00:00
]
+ mem : storing 0 in location 7
2015-10-26 04:42:18 +00:00
: ( before " End size_of(type) Cases " )
2015-11-06 23:25:40 +00:00
if ( type - > value = = - 1 ) {
// error value, but we'll raise it elsewhere
return 1 ;
}
2015-10-26 04:42:18 +00:00
if ( type - > value = = 0 ) {
assert ( ! type - > left & & ! type - > right ) ;
return 1 ;
}
2015-11-06 19:06:58 +00:00
type_info t = get ( Type , type - > value ) ;
2015-10-27 03:06:51 +00:00
if ( t . kind = = CONTAINER ) {
2015-04-18 01:16:08 +00:00
// size of a container is the sum of the sizes of its elements
2015-05-17 09:22:41 +00:00
long long int result = 0 ;
for ( long long int i = 0 ; i < SIZE ( t . elements ) ; + + i ) {
2015-05-19 01:40:58 +00:00
// todo: strengthen assertion to disallow mutual type recursion
2015-10-26 04:42:18 +00:00
if ( t . elements . at ( i ) - > value = = type - > value ) {
2015-10-07 05:15:45 +00:00
raise_error < < " container " < < t . name < < " can't include itself as a member \n " < < end ( ) ;
2015-07-24 08:05:59 +00:00
return 0 ;
}
2015-10-06 02:49:43 +00:00
// End size_of(type) Container Cases
2015-05-07 22:06:53 +00:00
result + = size_of ( t . elements . at ( i ) ) ;
2015-03-27 04:06:14 +00:00
}
return result ;
}
2015-08-11 06:15:00 +00:00
: ( scenario stash_container )
recipe main [
1 : number < - copy 34 # first
2 : number < - copy 35
3 : number < - copy 36
2015-08-24 20:40:21 +00:00
stash [ foo : ] , 1 : point - number / raw
2015-08-11 06:15:00 +00:00
]
+ app : foo : 34 35 36
2015-04-18 06:24:52 +00:00
//:: To access elements of a container, use 'get'
2015-04-24 17:19:03 +00:00
: ( scenario get )
2015-03-27 04:06:14 +00:00
recipe main [
2015-07-28 21:33:22 +00:00
12 : number < - copy 34
13 : number < - copy 35
2015-05-26 22:48:35 +00:00
15 : number < - get 12 : point / raw , 1 : offset # unsafe
2015-03-27 04:06:14 +00:00
]
+ mem : storing 35 in location 15
2015-04-17 17:31:17 +00:00
: ( before " End Primitive Recipe Declarations " )
GET ,
2015-02-20 08:03:47 +00:00
: ( before " End Primitive Recipe Numbers " )
2015-11-06 19:06:58 +00:00
put ( Recipe_ordinal , " get " , GET ) ;
2015-10-01 20:43:32 +00:00
: ( before " End Primitive Recipe Checks " )
2015-02-20 08:03:47 +00:00
case GET : {
2015-10-01 20:43:32 +00:00
if ( SIZE ( inst . ingredients ) ! = 2 ) {
2015-11-06 19:06:58 +00:00
raise_error < < maybe ( get ( Recipe , r ) . name ) < < " 'get' expects exactly 2 ingredients in ' " < < inst . to_string ( ) < < " ' \n " < < end ( ) ;
2015-10-01 20:43:32 +00:00
break ;
}
2015-11-05 22:13:26 +00:00
reagent base = inst . ingredients . at ( 0 ) ; // new copy for every invocation
2015-10-01 20:43:32 +00:00
// Update GET base in Check
2015-12-08 04:03:05 +00:00
if ( ! base . type | | ! base . type - > value | | ! contains_key ( Type , base . type - > value ) | | get ( Type , base . type - > value ) . kind ! = CONTAINER ) {
2015-11-06 19:06:58 +00:00
raise_error < < maybe ( get ( Recipe , r ) . name ) < < " first ingredient of 'get' should be a container, but got " < < inst . ingredients . at ( 0 ) . original_string < < ' \n ' < < end ( ) ;
2015-10-01 20:43:32 +00:00
break ;
}
2015-10-26 04:42:18 +00:00
type_ordinal base_type = base . type - > value ;
2015-10-01 20:43:32 +00:00
reagent offset = inst . ingredients . at ( 1 ) ;
if ( ! is_literal ( offset ) | | ! is_mu_scalar ( offset ) ) {
2015-11-06 19:06:58 +00:00
raise_error < < maybe ( get ( Recipe , r ) . name ) < < " second ingredient of 'get' should have type 'offset', but got " < < inst . ingredients . at ( 1 ) . original_string < < ' \n ' < < end ( ) ;
2015-07-24 08:05:59 +00:00
break ;
}
2015-10-02 07:28:08 +00:00
long long int offset_value = 0 ;
2015-11-02 02:24:17 +00:00
if ( is_integer ( offset . name ) ) // later layers permit non-integer offsets
2015-10-02 07:28:08 +00:00
offset_value = to_integer ( offset . name ) ;
2015-11-02 02:24:17 +00:00
else
2015-10-02 07:28:08 +00:00
offset_value = offset . value ;
2015-11-06 19:06:58 +00:00
if ( offset_value < 0 | | offset_value > = SIZE ( get ( Type , base_type ) . elements ) ) {
2015-11-06 22:15:37 +00:00
raise_error < < maybe ( get ( Recipe , r ) . name ) < < " invalid offset " < < offset_value < < " for " < < get ( Type , base_type ) . name < < ' \n ' < < end ( ) ;
2015-11-02 02:24:17 +00:00
break ;
2015-10-02 07:28:08 +00:00
}
2015-11-16 02:36:34 +00:00
if ( inst . products . empty ( ) ) break ;
2015-11-05 22:13:26 +00:00
reagent product = inst . products . at ( 0 ) ;
2015-10-02 07:28:08 +00:00
// Update GET product in Check
2015-11-02 03:38:30 +00:00
const reagent element = element_type ( base , offset_value ) ;
2015-11-27 18:32:34 +00:00
if ( ! types_coercible ( product , element ) ) {
2015-11-20 19:32:21 +00:00
raise_error < < maybe ( get ( Recipe , r ) . name ) < < " 'get " < < base . original_string < < " , " < < offset . original_string < < " ' should write to " < < debug_string ( element . type ) < < " but " < < product . name < < " has type " < < debug_string ( product . type ) < < ' \n ' < < end ( ) ;
2015-10-06 00:02:32 +00:00
break ;
2015-10-02 07:28:08 +00:00
}
2015-10-01 20:43:32 +00:00
break ;
}
: ( before " End Primitive Recipe Implementations " )
case GET : {
2015-05-07 22:06:53 +00:00
reagent base = current_instruction ( ) . ingredients . at ( 0 ) ;
2015-10-01 20:43:32 +00:00
// Update GET base in Run
2015-05-17 09:22:41 +00:00
long long int base_address = base . value ;
2015-08-01 00:06:38 +00:00
if ( base_address = = 0 ) {
2015-10-07 05:15:45 +00:00
raise_error < < maybe ( current_recipe_name ( ) ) < < " tried to access location 0 in ' " < < current_instruction ( ) . to_string ( ) < < " ' \n " < < end ( ) ;
2015-08-01 00:06:38 +00:00
break ;
}
2015-10-26 04:42:18 +00:00
type_ordinal base_type = base . type - > value ;
2015-05-17 09:22:41 +00:00
long long int offset = ingredients . at ( 1 ) . at ( 0 ) ;
2015-11-06 19:06:58 +00:00
if ( offset < 0 | | offset > = SIZE ( get ( Type , base_type ) . elements ) ) break ; // copied from Check above
2015-05-17 09:22:41 +00:00
long long int src = base_address ;
for ( long long int i = 0 ; i < offset ; + + i ) {
2015-10-06 03:11:31 +00:00
// End GET field Cases
2015-11-06 19:06:58 +00:00
src + = size_of ( get ( Type , base_type ) . elements . at ( i ) ) ;
2015-02-22 08:15:14 +00:00
}
2015-10-29 19:09:23 +00:00
trace ( 9998 , " run " ) < < " address to copy is " < < src < < end ( ) ;
2015-11-02 03:38:30 +00:00
reagent tmp = element_type ( base , offset ) ;
2015-05-13 00:00:56 +00:00
tmp . set_value ( src ) ;
2015-11-08 22:46:48 +00:00
trace ( 9998 , " run " ) < < " its type is " < < debug_string ( tmp . type ) < < end ( ) ;
2015-08-03 05:18:19 +00:00
products . push_back ( read_memory ( tmp ) ) ;
2015-02-20 08:03:47 +00:00
break ;
}
2015-11-02 03:38:30 +00:00
: ( code )
const reagent element_type ( const reagent & canonized_base , long long int offset_value ) {
assert ( offset_value > = 0 ) ;
2015-11-06 21:22:16 +00:00
assert ( contains_key ( Type , canonized_base . type - > value ) ) ;
2015-11-06 19:06:58 +00:00
assert ( ! get ( Type , canonized_base . type - > value ) . name . empty ( ) ) ;
const type_info & info = get ( Type , canonized_base . type - > value ) ;
2015-11-02 03:38:30 +00:00
assert ( info . kind = = CONTAINER ) ;
reagent element ;
2016-02-07 00:47:10 +00:00
element . name = info . element_names . at ( offset_value ) ;
2015-11-02 03:38:30 +00:00
element . type = new type_tree ( * info . elements . at ( offset_value ) ) ;
2016-02-07 00:47:10 +00:00
element . properties . resize ( 1 ) ;
element . properties . at ( 0 ) . second = new string_tree ( * info . element_type_names . at ( offset_value ) ) ;
2015-11-02 03:38:30 +00:00
// End element_type Special-cases
return element ;
}
2015-04-24 17:19:03 +00:00
: ( scenario get_handles_nested_container_elements )
2015-02-22 08:15:14 +00:00
recipe main [
2015-07-28 21:33:22 +00:00
12 : number < - copy 34
13 : number < - copy 35
14 : number < - copy 36
2015-05-26 22:48:35 +00:00
15 : number < - get 12 : point - number / raw , 1 : offset # unsafe
2015-02-22 08:15:14 +00:00
]
2015-03-24 06:59:59 +00:00
+ mem : storing 36 in location 15
2015-02-22 08:15:14 +00:00
2015-07-17 21:30:17 +00:00
: ( scenario get_out_of_bounds )
2015-10-07 05:15:45 +00:00
% Hide_errors = true ;
2015-07-17 21:30:17 +00:00
recipe main [
2015-07-28 21:33:22 +00:00
12 : number < - copy 34
13 : number < - copy 35
14 : number < - copy 36
2015-07-17 21:30:17 +00:00
get 12 : point - number / raw , 2 : offset # point - number occupies 3 locations but has only 2 fields ; out of bounds
]
2015-10-07 05:15:45 +00:00
+ error : main : invalid offset 2 for point - number
2015-07-17 21:30:17 +00:00
2015-08-09 19:26:31 +00:00
: ( scenario get_out_of_bounds_2 )
2015-10-07 05:15:45 +00:00
% Hide_errors = true ;
2015-07-17 21:30:17 +00:00
recipe main [
2015-07-28 21:33:22 +00:00
12 : number < - copy 34
13 : number < - copy 35
14 : number < - copy 36
2015-07-17 21:30:17 +00:00
get 12 : point - number / raw , - 1 : offset
]
2015-10-07 05:15:45 +00:00
+ error : main : invalid offset - 1 for point - number
2015-07-17 21:30:17 +00:00
2015-10-02 07:28:08 +00:00
: ( scenario get_product_type_mismatch )
2015-10-07 05:15:45 +00:00
% Hide_errors = true ;
2015-10-02 07:28:08 +00:00
recipe main [
12 : number < - copy 34
13 : number < - copy 35
14 : number < - copy 36
15 : address : number < - get 12 : point - number / raw , 1 : offset
]
2015-11-20 19:32:21 +00:00
+ error : main : ' get 12 : point - number / raw , 1 : offset ' should write to number but 15 has type < address : < number : < > > >
2015-10-02 07:28:08 +00:00
2015-11-16 02:36:34 +00:00
//: we might want to call 'get' without saving the results, say in a sandbox
: ( scenario get_without_product )
recipe main [
12 : number < - copy 34
13 : number < - copy 35
get 12 : point / raw , 1 : offset # unsafe
]
# just don't die
2015-10-26 04:42:18 +00:00
//:: To write to elements of containers, you need their address.
: ( scenario get_address )
recipe main [
12 : number < - copy 34
13 : number < - copy 35
15 : address : number < - get - address 12 : point / raw , 1 : offset # unsafe
]
+ mem : storing 13 in location 15
2015-04-17 17:31:17 +00:00
: ( before " End Primitive Recipe Declarations " )
GET_ADDRESS ,
2015-02-21 04:25:45 +00:00
: ( before " End Primitive Recipe Numbers " )
2015-11-06 19:06:58 +00:00
put ( Recipe_ordinal , " get-address " , GET_ADDRESS ) ;
2015-10-01 20:43:32 +00:00
: ( before " End Primitive Recipe Checks " )
2015-02-21 04:25:45 +00:00
case GET_ADDRESS : {
2015-10-01 20:43:32 +00:00
if ( SIZE ( inst . ingredients ) ! = 2 ) {
2015-11-06 19:06:58 +00:00
raise_error < < maybe ( get ( Recipe , r ) . name ) < < " 'get-address' expects exactly 2 ingredients in ' " < < inst . to_string ( ) < < " ' \n " < < end ( ) ;
2015-08-01 00:06:38 +00:00
break ;
}
2015-10-01 20:43:32 +00:00
reagent base = inst . ingredients . at ( 0 ) ;
// Update GET_ADDRESS base in Check
2015-12-08 04:03:05 +00:00
if ( ! base . type | | ! base . type - > value | | ! contains_key ( Type , base . type - > value ) | | get ( Type , base . type - > value ) . kind ! = CONTAINER ) {
2015-11-06 19:06:58 +00:00
raise_error < < maybe ( get ( Recipe , r ) . name ) < < " first ingredient of 'get-address' should be a container, but got " < < inst . ingredients . at ( 0 ) . original_string < < ' \n ' < < end ( ) ;
2015-07-24 08:09:35 +00:00
break ;
}
2015-10-26 04:42:18 +00:00
type_ordinal base_type = base . type - > value ;
2015-10-01 20:43:32 +00:00
reagent offset = inst . ingredients . at ( 1 ) ;
if ( ! is_literal ( offset ) | | ! is_mu_scalar ( offset ) ) {
2015-11-06 19:06:58 +00:00
raise_error < < maybe ( get ( Recipe , r ) . name ) < < " second ingredient of 'get' should have type 'offset', but got " < < inst . ingredients . at ( 1 ) . original_string < < ' \n ' < < end ( ) ;
2015-07-24 08:09:35 +00:00
break ;
}
2015-10-05 23:43:21 +00:00
long long int offset_value = 0 ;
2015-10-01 20:43:32 +00:00
if ( is_integer ( offset . name ) ) { // later layers permit non-integer offsets
2015-10-05 23:43:21 +00:00
offset_value = to_integer ( offset . name ) ;
2015-11-06 19:06:58 +00:00
if ( offset_value < 0 | | offset_value > = SIZE ( get ( Type , base_type ) . elements ) ) {
2015-11-06 22:15:37 +00:00
raise_error < < maybe ( get ( Recipe , r ) . name ) < < " invalid offset " < < offset_value < < " for " < < get ( Type , base_type ) . name < < ' \n ' < < end ( ) ;
2015-10-01 20:43:32 +00:00
break ;
}
}
2015-10-05 23:43:21 +00:00
else {
offset_value = offset . value ;
}
reagent product = inst . products . at ( 0 ) ;
2015-10-06 00:07:11 +00:00
// Update GET_ADDRESS product in Check
2015-10-26 04:42:18 +00:00
// same type as for GET..
2015-11-02 03:38:30 +00:00
reagent element = element_type ( base , offset_value ) ;
2015-10-26 04:42:18 +00:00
// ..except for an address at the start
2015-11-06 19:06:58 +00:00
element . type = new type_tree ( get ( Type_ordinal , " address " ) , element . type ) ;
2015-11-27 18:32:34 +00:00
if ( ! types_coercible ( product , element ) ) {
2015-11-20 19:32:21 +00:00
raise_error < < maybe ( get ( Recipe , r ) . name ) < < " 'get-address " < < base . original_string < < " , " < < offset . original_string < < " ' should write to " < < debug_string ( element . type ) < < " but " < < product . name < < " has type " < < debug_string ( product . type ) < < ' \n ' < < end ( ) ;
2015-10-06 00:02:32 +00:00
break ;
2015-10-05 23:43:21 +00:00
}
2015-10-01 20:43:32 +00:00
break ;
}
: ( before " End Primitive Recipe Implementations " )
case GET_ADDRESS : {
reagent base = current_instruction ( ) . ingredients . at ( 0 ) ;
// Update GET_ADDRESS base in Run
long long int base_address = base . value ;
if ( base_address = = 0 ) {
2015-10-07 05:15:45 +00:00
raise_error < < maybe ( current_recipe_name ( ) ) < < " tried to access location 0 in ' " < < current_instruction ( ) . to_string ( ) < < " ' \n " < < end ( ) ;
2015-07-17 21:30:17 +00:00
break ;
}
2015-10-26 04:42:18 +00:00
type_ordinal base_type = base . type - > value ;
2015-10-01 20:43:32 +00:00
long long int offset = ingredients . at ( 1 ) . at ( 0 ) ;
2015-11-06 19:06:58 +00:00
if ( offset < 0 | | offset > = SIZE ( get ( Type , base_type ) . elements ) ) break ; // copied from Check above
2015-05-17 09:22:41 +00:00
long long int result = base_address ;
for ( long long int i = 0 ; i < offset ; + + i ) {
2015-10-06 03:11:31 +00:00
// End GET_ADDRESS field Cases
2015-11-06 19:06:58 +00:00
result + = size_of ( get ( Type , base_type ) . elements . at ( i ) ) ;
2015-02-22 08:44:30 +00:00
}
2015-10-29 19:09:23 +00:00
trace ( 9998 , " run " ) < < " address to copy is " < < result < < end ( ) ;
2015-08-03 05:18:19 +00:00
products . resize ( 1 ) ;
2015-05-13 00:00:56 +00:00
products . at ( 0 ) . push_back ( result ) ;
2015-02-21 04:25:45 +00:00
break ;
}
2015-05-14 17:30:01 +00:00
2015-07-17 21:30:17 +00:00
: ( scenario get_address_out_of_bounds )
2015-10-07 05:15:45 +00:00
% Hide_errors = true ;
2015-07-17 21:30:17 +00:00
recipe main [
2015-07-28 21:33:22 +00:00
12 : number < - copy 34
13 : number < - copy 35
14 : number < - copy 36
2015-07-17 21:30:17 +00:00
get - address 12 : point - number / raw , 2 : offset # point - number occupies 3 locations but has only 2 fields ; out of bounds
]
2015-10-07 05:15:45 +00:00
+ error : main : invalid offset 2 for point - number
2015-07-17 21:30:17 +00:00
2015-08-09 19:26:31 +00:00
: ( scenario get_address_out_of_bounds_2 )
2015-10-07 05:15:45 +00:00
% Hide_errors = true ;
2015-07-17 21:30:17 +00:00
recipe main [
2015-07-28 21:33:22 +00:00
12 : number < - copy 34
13 : number < - copy 35
14 : number < - copy 36
2015-07-17 21:30:17 +00:00
get - address 12 : point - number / raw , - 1 : offset
]
2015-10-07 05:15:45 +00:00
+ error : main : invalid offset - 1 for point - number
2015-07-17 21:30:17 +00:00
2015-10-05 23:43:21 +00:00
: ( scenario get_address_product_type_mismatch )
2015-10-07 05:15:45 +00:00
% Hide_errors = true ;
2015-11-27 18:32:34 +00:00
container boolbool [
x : boolean
y : boolean
]
2015-10-05 23:43:21 +00:00
recipe main [
2015-11-27 18:32:34 +00:00
12 : boolean < - copy 1
13 : boolean < - copy 0
15 : boolean < - get - address 12 : boolbool , 1 : offset
2015-10-05 23:43:21 +00:00
]
2015-11-27 18:32:34 +00:00
+ error : main : ' get - address 12 : boolbool , 1 : offset ' should write to < address : < boolean : < > > > but 15 has type boolean
2015-10-05 23:43:21 +00:00
2015-05-14 17:30:01 +00:00
//:: Allow containers to be defined in mu code.
: ( scenarios load )
: ( scenario container )
container foo [
x : number
y : number
]
2015-10-29 18:56:10 +00:00
+ parse : - - - defining container foo
2015-10-30 17:08:43 +00:00
+ parse : element name : x
+ parse : type : 1
+ parse : element name : y
+ parse : type : 1
2015-05-14 17:30:01 +00:00
2015-05-18 23:09:09 +00:00
: ( scenario container_use_before_definition )
container foo [
x : number
y : bar
]
container bar [
x : number
y : number
]
2015-10-29 18:56:10 +00:00
+ parse : - - - defining container foo
2015-05-18 23:09:09 +00:00
+ parse : type number : 1000
+ parse : element name : x
+ parse : type : 1
+ parse : element name : y
+ parse : type : 1001
2015-10-29 18:56:10 +00:00
+ parse : - - - defining container bar
2015-05-18 23:09:09 +00:00
+ parse : type number : 1001
2015-05-14 17:30:01 +00:00
: ( before " End Command Handlers " )
else if ( command = = " container " ) {
2015-10-27 03:06:51 +00:00
insert_container ( command , CONTAINER , in ) ;
2015-05-14 17:30:01 +00:00
}
: ( code )
void insert_container ( const string & command , kind_of_type kind , istream & in ) {
2015-12-03 01:11:58 +00:00
skip_whitespace_but_not_newline ( in ) ;
2015-05-14 17:30:01 +00:00
string name = next_word ( in ) ;
2015-10-30 20:00:16 +00:00
// End container Name Refinements
2015-10-29 18:56:10 +00:00
trace ( 9991 , " parse " ) < < " --- defining " < < command < < ' ' < < name < < end ( ) ;
2015-11-06 21:22:16 +00:00
if ( ! contains_key ( Type_ordinal , name )
2015-11-06 19:06:58 +00:00
| | get ( Type_ordinal , name ) = = 0 ) {
put ( Type_ordinal , name , Next_type_ordinal + + ) ;
2015-05-14 17:30:01 +00:00
}
2015-11-06 19:06:58 +00:00
trace ( 9999 , " parse " ) < < " type number: " < < get ( Type_ordinal , name ) < < end ( ) ;
2015-05-14 17:30:01 +00:00
skip_bracket ( in , " 'container' must begin with '[' " ) ;
2015-11-06 19:31:37 +00:00
type_info & info = get_or_insert ( Type , get ( Type_ordinal , name ) ) ;
2015-12-14 04:01:42 +00:00
Recently_added_types . push_back ( get ( Type_ordinal , name ) ) ;
2015-10-06 02:49:43 +00:00
info . name = name ;
info . kind = kind ;
2015-11-17 09:21:00 +00:00
while ( has_data ( in ) ) {
2015-05-14 17:30:01 +00:00
skip_whitespace_and_comments ( in ) ;
string element = next_word ( in ) ;
if ( element = = " ] " ) break ;
istringstream inner ( element ) ;
2015-10-06 02:49:43 +00:00
info . element_names . push_back ( slurp_until ( inner , ' : ' ) ) ;
2015-10-29 18:56:10 +00:00
trace ( 9993 , " parse " ) < < " element name: " < < info . element_names . back ( ) < < end ( ) ;
2016-02-07 00:47:10 +00:00
info . element_type_names . push_back ( parse_property_list ( inner ) ) ;
info . elements . push_back ( new_type_tree_with_new_types_for_unknown ( info . element_type_names . back ( ) , info ) ) ;
for ( long long int i = 0 ; i < SIZE ( info . elements ) ; + + i )
trace ( 9993 , " parse " ) < < " type: " < < info . elements . at ( i ) - > value < < end ( ) ;
2016-02-15 20:25:13 +00:00
// End Load Container Element Definition
2015-05-14 17:30:01 +00:00
}
2015-10-06 02:49:43 +00:00
assert ( SIZE ( info . elements ) = = SIZE ( info . element_names ) ) ;
info . size = SIZE ( info . elements ) ;
2015-05-14 17:30:01 +00:00
}
2016-02-07 00:47:10 +00:00
type_tree * new_type_tree_with_new_types_for_unknown ( const string_tree * properties , const type_info & info ) {
if ( ! properties ) return NULL ;
type_tree * result = new type_tree ( 0 ) ;
if ( ! properties - > value . empty ( ) ) {
const string & type_name = properties - > value ;
if ( contains_key ( Type_ordinal , type_name ) ) {
result - > value = get ( Type_ordinal , type_name ) ;
}
else if ( is_integer ( type_name ) ) { // sometimes types will contain non-type tags, like numbers for the size of an array
result - > value = 0 ;
}
// End insert_container Special-cases
else if ( properties - > value ! = " -> " ) { // used in recipe types
put ( Type_ordinal , type_name , Next_type_ordinal + + ) ;
result - > value = get ( Type_ordinal , type_name ) ;
}
}
result - > left = new_type_tree_with_new_types_for_unknown ( properties - > left , info ) ;
result - > right = new_type_tree_with_new_types_for_unknown ( properties - > right , info ) ;
return result ;
}
2015-10-06 01:49:21 +00:00
void skip_bracket ( istream & in , string message ) {
skip_whitespace_and_comments ( in ) ;
if ( in . get ( ) ! = ' [ ' )
2015-10-07 05:15:45 +00:00
raise_error < < message < < ' \n ' < < end ( ) ;
2015-10-06 01:49:21 +00:00
}
2015-08-05 02:28:45 +00:00
: ( scenarios run )
: ( scenario container_define_twice )
container foo [
x : number
]
container foo [
y : number
]
recipe main [
1 : number < - copy 34
2 : number < - copy 35
2015-08-07 02:02:32 +00:00
3 : number < - get 1 : foo , 0 : offset
4 : number < - get 1 : foo , 1 : offset
2015-08-05 02:28:45 +00:00
]
+ mem : storing 34 in location 3
+ mem : storing 35 in location 4
2015-05-14 17:30:01 +00:00
//: ensure types created in one scenario don't leak outside it.
: ( before " End Globals " )
2015-12-14 04:01:42 +00:00
vector < type_ordinal > Recently_added_types ;
2015-05-14 17:30:01 +00:00
: ( before " End load_permanently " ) //: for non-tests
2015-12-14 04:01:42 +00:00
Recently_added_types . clear ( ) ;
2015-05-14 17:30:01 +00:00
: ( before " End Setup " ) //: for tests
2015-12-14 04:01:42 +00:00
for ( long long int i = 0 ; i < SIZE ( Recently_added_types ) ; + + i ) {
if ( ! contains_key ( Type , Recently_added_types . at ( i ) ) ) continue ;
Type_ordinal . erase ( get ( Type , Recently_added_types . at ( i ) ) . name ) ;
2015-10-26 04:42:18 +00:00
// todo: why do I explicitly need to provide this?
2015-12-14 04:01:42 +00:00
for ( long long int j = 0 ; j < SIZE ( Type . at ( Recently_added_types . at ( i ) ) . elements ) ; + + j ) {
delete Type . at ( Recently_added_types . at ( i ) ) . elements . at ( j ) ;
2016-02-07 00:47:10 +00:00
delete Type . at ( Recently_added_types . at ( i ) ) . element_type_names . at ( j ) ;
2015-10-26 04:42:18 +00:00
}
2015-12-14 04:01:42 +00:00
Type . erase ( Recently_added_types . at ( i ) ) ;
2015-05-14 17:30:01 +00:00
}
2015-12-14 04:01:42 +00:00
Recently_added_types . clear ( ) ;
2015-05-19 01:40:58 +00:00
// delete recent type references
2015-12-14 04:01:42 +00:00
// can't rely on Recently_added_types to cleanup Type_ordinal, because of deliberately misbehaving tests with references to undefined types
2015-07-04 16:40:50 +00:00
map < string , type_ordinal > : : iterator p = Type_ordinal . begin ( ) ;
while ( p ! = Type_ordinal . end ( ) ) {
2015-05-19 01:40:58 +00:00
// save current item
string name = p - > first ;
2015-07-04 16:40:50 +00:00
type_ordinal t = p - > second ;
2015-05-19 01:40:58 +00:00
// increment iterator
+ + p ;
// now delete current item if necessary
2015-11-09 07:36:37 +00:00
if ( t > = 1000 ) Type_ordinal . erase ( name ) ;
2015-05-19 01:40:58 +00:00
}
2015-05-14 17:30:01 +00:00
//: lastly, ensure scenarios are consistent by always starting them at the
//: same type number.
2015-07-04 16:40:50 +00:00
Next_type_ordinal = 1000 ;
2015-05-14 17:30:01 +00:00
: ( before " End Test Run Initialization " )
2015-07-04 16:40:50 +00:00
assert ( Next_type_ordinal < 1000 ) ;
2015-05-14 17:30:01 +00:00
: ( before " End Setup " )
2015-07-04 16:40:50 +00:00
Next_type_ordinal = 1000 ;
2015-05-14 17:30:01 +00:00
2015-10-07 05:15:45 +00:00
//:: Allow container definitions anywhere in the codebase, but complain if you
//:: can't find a definition at the end.
2015-05-19 01:40:58 +00:00
2015-10-07 05:15:45 +00:00
: ( scenario run_complains_on_unknown_types )
% Hide_errors = true ;
2015-05-19 01:40:58 +00:00
recipe main [
# integer is not a type
2015-07-28 21:33:22 +00:00
1 : integer < - copy 0
2015-05-19 01:40:58 +00:00
]
2015-11-17 19:28:19 +00:00
+ error : main : unknown type integer in ' 1 : integer < - copy 0 '
2015-05-19 01:40:58 +00:00
: ( scenario run_allows_type_definition_after_use )
2015-10-07 05:15:45 +00:00
% Hide_errors = true ;
2015-05-19 01:40:58 +00:00
recipe main [
2015-11-22 19:51:36 +00:00
1 : bar < - copy 0 / unsafe
2015-05-19 01:40:58 +00:00
]
container bar [
x : number
]
2015-10-07 05:15:45 +00:00
$ error : 0
2015-05-19 01:40:58 +00:00
2015-11-29 06:17:47 +00:00
: ( after " Begin Instruction Modifying Transforms " )
2015-11-29 07:11:21 +00:00
// Begin Type Modifying Transforms
2015-11-07 01:29:52 +00:00
Transform . push_back ( check_or_set_invalid_types ) ; // idempotent
2015-11-29 07:11:21 +00:00
// End Type Modifying Transforms
2015-05-19 01:40:58 +00:00
: ( code )
2015-11-07 01:03:02 +00:00
void check_or_set_invalid_types ( const recipe_ordinal r ) {
2015-11-19 18:29:55 +00:00
recipe & caller = get ( Recipe , r ) ;
2016-02-15 07:18:33 +00:00
trace ( 9991 , " transform " ) < < " --- check for invalid types in recipe " < < caller . name < < end ( ) ;
2015-11-19 18:29:55 +00:00
for ( long long int index = 0 ; index < SIZE ( caller . steps ) ; + + index ) {
instruction & inst = caller . steps . at ( index ) ;
2015-05-19 01:40:58 +00:00
for ( long long int i = 0 ; i < SIZE ( inst . ingredients ) ; + + i ) {
2015-11-07 01:03:02 +00:00
check_or_set_invalid_types ( inst . ingredients . at ( i ) . type , inst . ingredients . at ( i ) . properties . at ( 0 ) . second ,
2015-11-19 18:29:55 +00:00
maybe ( caller . name ) , " ' " + inst . to_string ( ) + " ' " ) ;
2015-05-19 01:40:58 +00:00
}
for ( long long int i = 0 ; i < SIZE ( inst . products ) ; + + i ) {
2015-11-07 01:03:02 +00:00
check_or_set_invalid_types ( inst . products . at ( i ) . type , inst . products . at ( i ) . properties . at ( 0 ) . second ,
2015-11-19 18:29:55 +00:00
maybe ( caller . name ) , " ' " + inst . to_string ( ) + " ' " ) ;
2015-05-19 01:40:58 +00:00
}
}
2015-11-19 18:29:55 +00:00
// End check_or_set_invalid_types
2015-05-19 01:40:58 +00:00
}
2015-11-07 01:03:02 +00:00
void check_or_set_invalid_types ( type_tree * type , const string_tree * type_name , const string & block , const string & name ) {
2015-11-09 05:06:48 +00:00
// can't assert that type_name is non-null, even at the top of a recursive call tree
2015-10-26 04:42:18 +00:00
if ( ! type ) return ; // will throw a more precise error elsewhere
2015-10-30 20:00:16 +00:00
// End Container Type Checks
2016-01-19 00:46:07 +00:00
if ( type - > value = = 0 ) return ;
2015-11-07 01:03:02 +00:00
if ( ! contains_key ( Type , type - > value ) ) {
if ( type_name & & contains_key ( Type_ordinal , type_name - > value ) )
type - > value = get ( Type_ordinal , type_name - > value ) ;
2015-11-17 19:28:19 +00:00
else if ( type_name )
raise_error < < block < < " unknown type " < < type_name - > value < < " in " < < name < < ' \n ' < < end ( ) ;
2015-11-07 01:03:02 +00:00
else
2015-11-17 19:28:19 +00:00
raise_error < < block < < " missing type in " < < name < < ' \n ' < < end ( ) ;
2015-11-07 01:03:02 +00:00
}
check_or_set_invalid_types ( type - > left , type_name ? type_name - > left : NULL , block , name ) ;
check_or_set_invalid_types ( type - > right , type_name ? type_name - > right : NULL , block , name ) ;
2015-05-19 01:40:58 +00:00
}
: ( scenario container_unknown_field )
2015-10-07 05:15:45 +00:00
% Hide_errors = true ;
2015-05-19 01:40:58 +00:00
container foo [
x : number
y : bar
]
2015-10-26 04:42:18 +00:00
+ error : foo : unknown type in y
2015-05-19 01:40:58 +00:00
2015-05-28 18:28:15 +00:00
: ( scenario read_container_with_bracket_in_comment )
container foo [
x : number
# ']' in comment
y : number
]
2015-10-29 18:56:10 +00:00
+ parse : - - - defining container foo
2015-10-30 17:08:43 +00:00
+ parse : element name : x
+ parse : type : 1
+ parse : element name : y
+ parse : type : 1
2015-05-28 18:28:15 +00:00
2015-11-05 04:41:36 +00:00
: ( before " End Transform All " )
2015-05-19 01:40:58 +00:00
check_container_field_types ( ) ;
: ( code )
void check_container_field_types ( ) {
2015-07-04 16:40:50 +00:00
for ( map < type_ordinal , type_info > : : iterator p = Type . begin ( ) ; p ! = Type . end ( ) ; + + p ) {
2015-05-19 01:40:58 +00:00
const type_info & info = p - > second ;
2015-11-05 07:44:46 +00:00
// Check Container Field Types(info)
2015-11-09 07:36:37 +00:00
for ( long long int i = 0 ; i < SIZE ( info . elements ) ; + + i )
2015-10-26 04:42:18 +00:00
check_invalid_types ( info . elements . at ( i ) , maybe ( info . name ) , info . element_names . at ( i ) ) ;
2015-05-19 01:40:58 +00:00
}
}
2015-11-07 01:03:02 +00:00
void check_invalid_types ( const recipe_ordinal r ) {
for ( long long int index = 0 ; index < SIZE ( get ( Recipe , r ) . steps ) ; + + index ) {
const instruction & inst = get ( Recipe , r ) . steps . at ( index ) ;
for ( long long int i = 0 ; i < SIZE ( inst . ingredients ) ; + + i ) {
check_invalid_types ( inst . ingredients . at ( i ) . type ,
maybe ( get ( Recipe , r ) . name ) , " ' " + inst . to_string ( ) + " ' " ) ;
}
for ( long long int i = 0 ; i < SIZE ( inst . products ) ; + + i ) {
check_invalid_types ( inst . products . at ( i ) . type ,
maybe ( get ( Recipe , r ) . name ) , " ' " + inst . to_string ( ) + " ' " ) ;
}
}
}
2016-02-17 17:08:14 +00:00
void check_invalid_types ( const type_tree * type , const string & block , const string & name ) {
2015-11-07 01:03:02 +00:00
if ( ! type ) return ; // will throw a more precise error elsewhere
// End Container Type Checks
if ( type - > value = = 0 ) {
assert ( ! type - > left & & ! type - > right ) ;
return ;
}
if ( ! contains_key ( Type , type - > value ) )
raise_error < < block < < " unknown type in " < < name < < ' \n ' < < end ( ) ;
check_invalid_types ( type - > left , block , name ) ;
check_invalid_types ( type - > right , block , name ) ;
}
2016-02-15 07:30:59 +00:00
//:: Construct types out of their constituent fields.
2016-02-06 17:34:22 +00:00
: ( scenario merge )
container foo [
x : number
y : number
]
recipe main [
1 : foo < - merge 3 , 4
]
+ mem : storing 3 in location 1
+ mem : storing 4 in location 2
2015-06-23 19:05:18 +00:00
: ( before " End Primitive Recipe Declarations " )
MERGE ,
: ( before " End Primitive Recipe Numbers " )
2015-11-06 19:06:58 +00:00
put ( Recipe_ordinal , " merge " , MERGE ) ;
2015-10-02 06:44:17 +00:00
: ( before " End Primitive Recipe Checks " )
case MERGE : {
2016-02-15 07:30:59 +00:00
// type-checking in a separate transform below
2015-10-02 06:44:17 +00:00
break ;
}
2015-06-23 19:05:18 +00:00
: ( before " End Primitive Recipe Implementations " )
case MERGE : {
products . resize ( 1 ) ;
for ( long long int i = 0 ; i < SIZE ( ingredients ) ; + + i )
for ( long long int j = 0 ; j < SIZE ( ingredients . at ( i ) ) ; + + j )
products . at ( 0 ) . push_back ( ingredients . at ( i ) . at ( j ) ) ;
break ;
}
2016-02-15 07:30:59 +00:00
//: type-check 'merge' to avoid interpreting numbers as addresses
: ( scenario merge_check )
% Hide_errors = true ;
recipe main [
1 : point < - merge 3 , 4
]
$ error : 0
: ( scenario merge_check_missing_element )
% Hide_errors = true ;
recipe main [
1 : point < - merge 3
]
+ error : main : too few ingredients in ' 1 : point < - merge 3 '
: ( scenario merge_check_extra_element )
% Hide_errors = true ;
recipe main [
1 : point < - merge 3 , 4 , 5
]
+ error : main : too many ingredients in ' 1 : point < - merge 3 , 4 , 5 '
//: We want to avoid causing memory corruption, but other than that we want to
//: be flexible in how we construct containers of containers. It should be
//: equally easy to define a container out of primitives or intermediate
//: container fields.
: ( scenario merge_check_recursive_containers )
% Hide_errors = true ;
recipe main [
1 : point < - merge 3 , 4
1 : point - number < - merge 1 : point , 5
]
$ error : 0
: ( scenario merge_check_recursive_containers_2 )
% Hide_errors = true ;
recipe main [
1 : point < - merge 3 , 4
2 : point - number < - merge 1 : point
]
+ error : main : too few ingredients in ' 2 : point - number < - merge 1 : point '
: ( scenario merge_check_recursive_containers_3 )
% Hide_errors = true ;
recipe main [
1 : point - number < - merge 3 , 4 , 5
]
$ error : 0
: ( scenario merge_check_recursive_containers_4 )
% Hide_errors = true ;
recipe main [
1 : point - number < - merge 3 , 4
]
+ error : main : too few ingredients in ' 1 : point - number < - merge 3 , 4 '
//: Since a container can be merged in several ways, we need to be able to
//: backtrack through different possibilities. Later we'll allow creating
//: exclusive containers which contain just one of rather than all of their
//: elements. That will also require backtracking capabilities. Here's the
//: state we need to maintain for backtracking:
: ( before " End Types " )
struct merge_check_point {
reagent container ;
long long int container_element_index ;
merge_check_point ( const reagent & c , long long int i ) : container ( c ) , container_element_index ( i ) { }
} ;
struct merge_check_state {
stack < merge_check_point > data ;
} ;
: ( before " End Checks " )
Transform . push_back ( check_merge_calls ) ;
: ( code )
void check_merge_calls ( const recipe_ordinal r ) {
const recipe & caller = get ( Recipe , r ) ;
trace ( 9991 , " transform " ) < < " --- type-check merge instructions in recipe " < < caller . name < < end ( ) ;
for ( long long int i = 0 ; i < SIZE ( caller . steps ) ; + + i ) {
const instruction & inst = caller . steps . at ( i ) ;
if ( inst . name ! = " merge " ) continue ;
if ( SIZE ( inst . products ) ! = 1 ) {
raise_error < < maybe ( caller . name ) < < " 'merge' should yield a single product in ' " < < inst . to_string ( ) < < " ' \n " < < end ( ) ;
continue ;
}
reagent product = inst . products . at ( 0 ) ;
// Update product While Type-checking Merge
type_ordinal product_type = product . type - > value ;
if ( product_type = = 0 | | ! contains_key ( Type , product_type ) ) {
raise_error < < maybe ( caller . name ) < < " 'merge' should yield a container in ' " < < inst . to_string ( ) < < " ' \n " < < end ( ) ;
continue ;
}
const type_info & info = get ( Type , product_type ) ;
if ( info . kind ! = CONTAINER & & info . kind ! = EXCLUSIVE_CONTAINER ) {
raise_error < < maybe ( caller . name ) < < " 'merge' should yield a container in ' " < < inst . to_string ( ) < < " ' \n " < < end ( ) ;
continue ;
}
check_merge_call ( inst . ingredients , product , caller , inst ) ;
}
}
void check_merge_call ( const vector < reagent > & ingredients , const reagent & product , const recipe & caller , const instruction & inst ) {
long long int ingredient_index = 0 ;
merge_check_state state ;
state . data . push ( merge_check_point ( product , 0 ) ) ;
while ( true ) {
assert ( ! state . data . empty ( ) ) ;
trace ( 9999 , " transform " ) < < ingredient_index < < " vs " < < SIZE ( ingredients ) < < end ( ) ;
if ( ingredient_index > = SIZE ( ingredients ) ) {
raise_error < < maybe ( caller . name ) < < " too few ingredients in ' " < < inst . to_string ( ) < < " ' \n " < < end ( ) ;
return ;
}
reagent & container = state . data . top ( ) . container ;
type_info & container_info = get ( Type , container . type - > value ) ;
switch ( container_info . kind ) {
case CONTAINER : {
reagent expected_ingredient = element_type ( container , state . data . top ( ) . container_element_index ) ;
trace ( 9999 , " transform " ) < < " checking container " < < debug_string ( container ) < < " || " < < debug_string ( expected_ingredient ) < < " vs ingredient " < < ingredient_index < < end ( ) ;
// if the current element is the ingredient we expect, move on to the next element/ingredient
if ( types_coercible ( expected_ingredient , ingredients . at ( ingredient_index ) ) ) {
+ + ingredient_index ;
+ + state . data . top ( ) . container_element_index ;
while ( state . data . top ( ) . container_element_index > = SIZE ( get ( Type , state . data . top ( ) . container . type - > value ) . elements ) ) {
state . data . pop ( ) ;
if ( state . data . empty ( ) ) {
if ( ingredient_index < SIZE ( ingredients ) )
raise_error < < maybe ( caller . name ) < < " too many ingredients in ' " < < inst . to_string ( ) < < " ' \n " < < end ( ) ;
return ;
}
+ + state . data . top ( ) . container_element_index ;
}
}
// if not, maybe it's a field of the current element
else {
// no change to ingredient_index
state . data . push ( merge_check_point ( expected_ingredient , 0 ) ) ;
}
break ;
}
// End valid_merge Cases
default : {
if ( ! types_coercible ( container , ingredients . at ( ingredient_index ) ) ) {
raise_error < < maybe ( caller . name ) < < " incorrect type of ingredient " < < ingredient_index < < " in ' " < < inst . to_string ( ) < < " ' \n " < < end ( ) ;
return ;
}
+ + ingredient_index ;
do {
state . data . pop ( ) ;
if ( state . data . empty ( ) ) {
if ( ingredient_index < SIZE ( ingredients ) )
raise_error < < maybe ( caller . name ) < < " too many ingredients in ' " < < inst . to_string ( ) < < " ' \n " < < end ( ) ;
return ;
}
+ + state . data . top ( ) . container_element_index ;
} while ( state . data . top ( ) . container_element_index > = SIZE ( get ( Type , state . data . top ( ) . container . type - > value ) . elements ) ) ;
}
}
}
// never gets here
assert ( false ) ;
}
: ( scenario merge_check_product )
% Hide_errors = true ;
recipe main [
1 : number < - merge 3
]
+ error : main : ' merge ' should yield a container in ' 1 : number < - merge 3 '
: ( before " End Includes " )
# include <stack>
using std : : stack ;