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 + + ) ;
2016-03-28 17:11:23 +00:00
get_or_insert ( Type , point ) ; // initialize
2015-11-06 19:06:58 +00:00
get ( Type , point ) . kind = CONTAINER ;
get ( Type , point ) . name = " point " ;
2016-02-17 18:09:48 +00:00
get ( Type , point ) . elements . push_back ( reagent ( " x:number " ) ) ;
get ( Type , point ) . elements . push_back ( reagent ( " y:number " ) ) ;
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
2016-05-07 17:33:53 +00:00
//: Tests in this layer often explicitly set up memory before reading it as a
2015-05-26 22:48:35 +00:00
//: 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 )
2016-03-08 09:30:14 +00:00
def 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 ;
2016-03-08 09:30:14 +00:00
def main [
2015-08-07 20:01:49 +00:00
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 + + ) ;
2016-03-28 17:11:23 +00:00
get_or_insert ( Type , point_number ) ; // initialize
2015-11-06 19:06:58 +00:00
get ( Type , point_number ) . kind = CONTAINER ;
get ( Type , point_number ) . name = " point-number " ;
2016-02-17 18:09:48 +00:00
get ( Type , point_number ) . elements . push_back ( reagent ( " xy:point " ) ) ;
get ( Type , point_number ) . elements . push_back ( reagent ( " z:number " ) ) ;
2015-03-27 04:06:14 +00:00
2015-04-24 17:19:03 +00:00
: ( scenario copy_handles_nested_container_elements )
2016-03-08 09:30:14 +00:00
def 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
2016-04-24 07:36:30 +00:00
//: products of recipes can include containers
2016-04-27 22:37:09 +00:00
: ( scenario return_container )
2016-04-24 05:11:48 +00:00
def main [
3 : point < - f 2
]
def f [
12 : number < - next - ingredient
13 : number < - copy 35
return 12 : point / raw
]
+ run : result 0 is [ 2 , 35 ]
+ mem : storing 2 in location 3
+ mem : storing 35 in location 4
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 )
2016-03-08 09:30:14 +00:00
def 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 )
2016-03-08 09:30:14 +00:00
def 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
2016-05-03 06:11:33 +00:00
//: Global data structure for container metadata.
//: Can't put this in type_info because later layers will add support for more
2016-05-17 17:44:12 +00:00
//: complex type trees where metadata depends on *combinations* of types.
2016-05-03 06:11:33 +00:00
2016-05-17 19:35:06 +00:00
: ( before " struct reagent " )
2016-05-03 06:11:33 +00:00
struct container_metadata {
int size ;
vector < int > offset ;
// End container_metadata Fields
container_metadata ( ) : size ( 0 ) {
// End container_metadata Constructor
}
} ;
: ( before " End reagent Fields " )
container_metadata metadata ; // can't be a pointer into Container_metadata because we keep changing the base storage when we save/restore snapshots
: ( before " End reagent Copy Operator " )
2016-05-17 19:13:52 +00:00
metadata = other . metadata ;
2016-05-03 06:11:33 +00:00
: ( before " End reagent Copy Constructor " )
2016-05-17 19:13:52 +00:00
metadata = other . metadata ;
2016-05-03 06:11:33 +00:00
: ( before " End Globals " )
// todo: switch to map after figuring out how to consistently compare type trees
vector < pair < type_tree * , container_metadata > > Container_metadata , Container_metadata_snapshot ;
: ( before " End save_snapshots " )
Container_metadata_snapshot = Container_metadata ;
: ( before " End restore_snapshots " )
2016-05-07 16:45:13 +00:00
restore_container_metadata ( ) ;
: ( before " End One-time Setup " )
atexit ( clear_container_metadata ) ;
: ( code )
// invariant: Container_metadata always contains a superset of Container_metadata_snapshot
void restore_container_metadata ( ) {
for ( int i = 0 ; i < SIZE ( Container_metadata ) ; + + i ) {
assert ( Container_metadata . at ( i ) . first ) ;
if ( i < SIZE ( Container_metadata_snapshot ) ) {
assert ( Container_metadata . at ( i ) . first = = Container_metadata_snapshot . at ( i ) . first ) ;
continue ;
}
delete Container_metadata . at ( i ) . first ;
Container_metadata . at ( i ) . first = NULL ;
}
Container_metadata . resize ( SIZE ( Container_metadata_snapshot ) ) ;
}
void clear_container_metadata ( ) {
Container_metadata_snapshot . clear ( ) ;
for ( int i = 0 ; i < SIZE ( Container_metadata ) ; + + i ) {
delete Container_metadata . at ( i ) . first ;
Container_metadata . at ( i ) . first = NULL ;
}
Container_metadata . clear ( ) ;
}
2016-05-03 06:11:33 +00:00
//: do no work in size_of, simply lookup Container_metadata
2016-05-06 15:33:15 +00:00
: ( before " End size_of(reagent r) Cases " )
2016-05-03 06:11:33 +00:00
if ( r . metadata . size ) return r . metadata . size ;
2015-10-26 04:42:18 +00:00
: ( before " End size_of(type) Cases " )
2016-04-30 16:48:57 +00:00
if ( type - > value = = - 1 ) return 1 ; // error value, but we'll raise it elsewhere
2015-10-26 04:42:18 +00:00
if ( type - > value = = 0 ) {
assert ( ! type - > left & & ! type - > right ) ;
return 1 ;
}
2016-02-19 10:21:37 +00:00
if ( ! contains_key ( Type , type - > value ) ) {
2016-02-26 21:04:55 +00:00
raise < < " no such type " < < type - > value < < ' \n ' < < end ( ) ;
2016-02-19 10:21:37 +00:00
return 0 ;
}
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 ) {
2016-05-03 06:11:33 +00:00
// Compute size_of Container
return get ( Container_metadata , type ) . size ;
}
//: precompute Container_metadata before we need size_of
//: also store a copy in each reagent in each instruction in each recipe
: ( after " Begin Instruction Modifying Transforms " ) // needs to happen before transform_names, therefore after "End Type Modifying Transforms" below
2016-05-12 23:38:59 +00:00
Transform . push_back ( compute_container_sizes ) ;
2016-05-03 06:11:33 +00:00
: ( code )
2016-05-12 23:38:59 +00:00
void compute_container_sizes ( recipe_ordinal r ) {
2016-05-03 06:11:33 +00:00
recipe & caller = get ( Recipe , r ) ;
for ( int i = 0 ; i < SIZE ( caller . steps ) ; + + i ) {
instruction & inst = caller . steps . at ( i ) ;
for ( int i = 0 ; i < SIZE ( inst . ingredients ) ; + + i )
2016-05-12 23:38:59 +00:00
compute_container_sizes ( inst . ingredients . at ( i ) ) ;
2016-05-03 06:11:33 +00:00
for ( int i = 0 ; i < SIZE ( inst . products ) ; + + i )
2016-05-12 23:38:59 +00:00
compute_container_sizes ( inst . products . at ( i ) ) ;
2016-05-03 06:11:33 +00:00
}
}
2016-05-12 23:38:59 +00:00
void compute_container_sizes ( reagent & r ) {
2016-05-03 06:11:33 +00:00
if ( is_literal ( r ) | | is_dummy ( r ) ) return ;
reagent rcopy = r ;
2016-05-12 23:38:59 +00:00
// Compute Container Size(reagent rcopy)
2016-05-03 06:11:33 +00:00
set < type_ordinal > pending_metadata ;
2016-05-12 23:38:59 +00:00
compute_container_sizes ( rcopy . type , pending_metadata ) ;
2016-05-03 06:11:33 +00:00
if ( contains_key ( Container_metadata , rcopy . type ) )
r . metadata = get ( Container_metadata , rcopy . type ) ;
}
2016-05-12 23:38:59 +00:00
void compute_container_sizes ( const type_tree * type , set < type_ordinal > & pending_metadata ) {
2016-05-03 06:11:33 +00:00
if ( ! type ) return ;
if ( contains_key ( pending_metadata , type - > value ) ) return ;
2016-05-12 23:38:59 +00:00
if ( type - > value ) pending_metadata . insert ( type - > value ) ;
2016-05-03 06:11:33 +00:00
if ( contains_key ( Container_metadata , type ) ) return ;
2016-05-12 23:38:59 +00:00
if ( type - > left ) compute_container_sizes ( type - > left , pending_metadata ) ;
if ( type - > right ) compute_container_sizes ( type - > right , pending_metadata ) ;
2016-05-03 06:11:33 +00:00
if ( ! contains_key ( Type , type - > value ) ) return ; // error raised elsewhere
type_info & info = get ( Type , type - > value ) ;
if ( info . kind = = CONTAINER ) {
container_metadata metadata ;
for ( int i = 0 ; i < SIZE ( info . elements ) ; + + i ) {
2016-05-06 07:46:39 +00:00
reagent /*copy*/ element = info . elements . at ( i ) ;
2016-05-12 23:38:59 +00:00
// Compute Container Size(element)
compute_container_sizes ( element . type , pending_metadata ) ;
2016-05-03 06:11:33 +00:00
metadata . offset . push_back ( metadata . size ) ; // save previous size as offset
metadata . size + = size_of ( element . type ) ;
2015-07-24 08:05:59 +00:00
}
2016-05-03 06:11:33 +00:00
Container_metadata . push_back ( pair < type_tree * , container_metadata > ( new type_tree ( * type ) , metadata ) ) ;
}
2016-05-12 23:38:59 +00:00
// End compute_container_sizes Cases
2016-05-03 06:11:33 +00:00
}
2016-05-12 23:38:59 +00:00
container_metadata & get ( vector < pair < type_tree * , container_metadata > > & all , const type_tree * key ) {
2016-05-03 06:11:33 +00:00
for ( int i = 0 ; i < SIZE ( all ) ; + + i ) {
if ( matches ( all . at ( i ) . first , key ) )
return all . at ( i ) . second ;
2015-03-27 04:06:14 +00:00
}
2016-05-03 06:11:33 +00:00
tb_shutdown ( ) ;
raise < < " unknown size for type " < < to_string ( key ) < < ' \n ' < < end ( ) ;
assert ( false ) ;
}
bool contains_key ( const vector < pair < type_tree * , container_metadata > > & all , const type_tree * key ) {
for ( int i = 0 ; i < SIZE ( all ) ; + + i ) {
if ( matches ( all . at ( i ) . first , key ) )
return true ;
}
return false ;
}
bool matches ( const type_tree * a , const type_tree * b ) {
if ( a = = b ) return true ;
if ( ! a | | ! b ) return false ;
if ( a - > value ! = b - > value ) return false ;
return matches ( a - > left , b - > left ) & & matches ( a - > right , b - > right ) ;
2015-03-27 04:06:14 +00:00
}
2015-08-11 06:15:00 +00:00
: ( scenario stash_container )
2016-03-08 09:30:14 +00:00
def main [
2015-08-11 06:15:00 +00:00
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 )
2016-03-08 09:30:14 +00:00
def 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 ) {
2016-03-21 09:25:52 +00:00
raise < < maybe ( get ( Recipe , r ) . name ) < < " 'get' expects exactly 2 ingredients in ' " < < to_original_string ( inst ) < < " ' \n " < < end ( ) ;
2015-10-01 20:43:32 +00:00
break ;
}
2016-05-06 07:46:39 +00:00
reagent /*copy*/ base = inst . ingredients . at ( 0 ) ; // new copy for every invocation
2016-04-24 07:36:30 +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 ) {
2016-02-26 21:04:55 +00:00
raise < < 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 ;
2016-05-06 07:46:39 +00:00
const reagent & offset = inst . ingredients . at ( 1 ) ;
2015-10-01 20:43:32 +00:00
if ( ! is_literal ( offset ) | | ! is_mu_scalar ( offset ) ) {
2016-02-26 21:04:55 +00:00
raise < < 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 ;
}
2016-03-14 03:26:47 +00:00
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 ) ) {
2016-02-26 21:04:55 +00:00
raise < < 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 ;
2016-05-06 07:46:39 +00:00
reagent /*copy*/ product = inst . products . at ( 0 ) ;
2016-04-24 07:36:30 +00:00
// Update GET product in Check
2016-05-15 03:36:09 +00:00
const reagent /*copy*/ element = element_type ( base . type , offset_value ) ;
2015-11-27 18:32:34 +00:00
if ( ! types_coercible ( product , element ) ) {
2016-02-26 21:04:55 +00:00
raise < < maybe ( get ( Recipe , r ) . name ) < < " 'get " < < base . original_string < < " , " < < offset . original_string < < " ' should write to " < < names_to_string_without_quotes ( element . type ) < < " but " < < product . name < < " has type " < < names_to_string_without_quotes ( 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 : {
2016-05-06 07:46:39 +00:00
reagent /*copy*/ base = current_instruction ( ) . ingredients . at ( 0 ) ;
2016-04-24 07:36:30 +00:00
// Update GET base in Run
2016-03-14 03:26:47 +00:00
int base_address = base . value ;
2015-08-01 00:06:38 +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-08-01 00:06:38 +00:00
break ;
}
2015-10-26 04:42:18 +00:00
type_ordinal base_type = base . type - > value ;
2016-03-14 03:26:47 +00:00
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
2016-05-03 06:11:33 +00:00
assert ( base . metadata . size ) ;
int src = base_address + base . metadata . offset . at ( offset ) ;
2015-10-29 19:09:23 +00:00
trace ( 9998 , " run " ) < < " address to copy is " < < src < < end ( ) ;
2016-05-06 07:46:39 +00:00
reagent /*copy*/ element = element_type ( base . type , offset ) ;
2016-04-20 16:58:07 +00:00
element . set_value ( src ) ;
trace ( 9998 , " run " ) < < " its type is " < < names_to_string ( element . type ) < < end ( ) ;
// Read element
products . push_back ( read_memory ( element ) ) ;
2015-02-20 08:03:47 +00:00
break ;
}
2015-11-02 03:38:30 +00:00
: ( code )
2016-04-30 17:09:38 +00:00
const reagent element_type ( const type_tree * type , int offset_value ) {
2015-11-02 03:38:30 +00:00
assert ( offset_value > = 0 ) ;
2016-04-30 17:09:38 +00:00
assert ( contains_key ( Type , type - > value ) ) ;
assert ( ! get ( Type , type - > value ) . name . empty ( ) ) ;
const type_info & info = get ( Type , type - > value ) ;
2015-11-02 03:38:30 +00:00
assert ( info . kind = = CONTAINER ) ;
2016-05-06 07:46:39 +00:00
reagent /*copy*/ element = info . elements . 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 )
2016-03-08 09:30:14 +00:00
def 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 ;
2016-03-08 09:30:14 +00:00
def 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 ;
2016-03-08 09:30:14 +00:00
def 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 ;
2016-03-08 09:30:14 +00:00
def main [
2015-10-02 07:28:08 +00:00
12 : number < - copy 34
13 : number < - copy 35
14 : number < - copy 36
15 : address : number < - get 12 : point - number / raw , 1 : offset
]
2016-02-22 04:30:02 +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 )
2016-03-08 09:30:14 +00:00
def main [
2015-11-16 02:36:34 +00:00
12 : number < - copy 34
13 : number < - copy 35
get 12 : point / raw , 1 : offset # unsafe
]
# just don't die
2016-04-11 02:26:37 +00:00
//:: To write to elements of containers, use 'put'.
: ( scenario put )
def main [
12 : number < - copy 34
13 : number < - copy 35
$ clear - trace
12 : point < - put 12 : point , 1 : offset , 36
]
+ mem : storing 36 in location 13
- mem : storing 34 in location 12
: ( before " End Primitive Recipe Declarations " )
PUT ,
: ( before " End Primitive Recipe Numbers " )
put ( Recipe_ordinal , " put " , PUT ) ;
: ( before " End Primitive Recipe Checks " )
case PUT : {
if ( SIZE ( inst . ingredients ) ! = 3 ) {
raise < < maybe ( get ( Recipe , r ) . name ) < < " 'put' expects exactly 3 ingredients in ' " < < to_original_string ( inst ) < < " ' \n " < < end ( ) ;
break ;
}
2016-05-06 07:46:39 +00:00
reagent /*copy*/ base = inst . ingredients . at ( 0 ) ;
2016-04-24 07:36:30 +00:00
// Update PUT base in Check
2016-04-11 02:26:37 +00:00
if ( ! base . type | | ! base . type - > value | | ! contains_key ( Type , base . type - > value ) | | get ( Type , base . type - > value ) . kind ! = CONTAINER ) {
raise < < maybe ( get ( Recipe , r ) . name ) < < " first ingredient of 'put' should be a container, but got " < < inst . ingredients . at ( 0 ) . original_string < < ' \n ' < < end ( ) ;
break ;
}
type_ordinal base_type = base . type - > value ;
2016-05-06 07:46:39 +00:00
reagent /*copy*/ offset = inst . ingredients . at ( 1 ) ;
2016-04-24 07:36:30 +00:00
// Update PUT offset in Check
2016-04-11 02:26:37 +00:00
if ( ! is_literal ( offset ) | | ! is_mu_scalar ( offset ) ) {
raise < < maybe ( get ( Recipe , r ) . name ) < < " second ingredient of 'put' should have type 'offset', but got " < < inst . ingredients . at ( 1 ) . original_string < < ' \n ' < < end ( ) ;
break ;
}
int offset_value = 0 ;
if ( is_integer ( offset . name ) ) { // later layers permit non-integer offsets
offset_value = to_integer ( offset . name ) ;
if ( offset_value < 0 | | offset_value > = SIZE ( get ( Type , base_type ) . elements ) ) {
raise < < maybe ( get ( Recipe , r ) . name ) < < " invalid offset " < < offset_value < < " for " < < get ( Type , base_type ) . name < < ' \n ' < < end ( ) ;
break ;
}
}
else {
offset_value = offset . value ;
}
2016-05-06 07:46:39 +00:00
const reagent & value = inst . ingredients . at ( 2 ) ;
const reagent & element = element_type ( base . type , offset_value ) ;
2016-04-11 02:26:37 +00:00
if ( ! types_coercible ( element , value ) ) {
raise < < maybe ( get ( Recipe , r ) . name ) < < " 'put " < < base . original_string < < " , " < < offset . original_string < < " ' should store " < < names_to_string_without_quotes ( element . type ) < < " but " < < value . name < < " has type " < < names_to_string_without_quotes ( value . type ) < < ' \n ' < < end ( ) ;
break ;
}
break ;
}
: ( before " End Primitive Recipe Implementations " )
case PUT : {
2016-05-06 07:46:39 +00:00
reagent /*copy*/ base = current_instruction ( ) . ingredients . at ( 0 ) ;
2016-04-24 07:36:30 +00:00
// Update PUT base in Run
2016-04-11 02:26:37 +00:00
int base_address = base . value ;
if ( base_address = = 0 ) {
raise < < maybe ( current_recipe_name ( ) ) < < " tried to access location 0 in ' " < < to_original_string ( current_instruction ( ) ) < < " ' \n " < < end ( ) ;
break ;
}
type_ordinal base_type = base . type - > value ;
int offset = ingredients . at ( 1 ) . at ( 0 ) ;
if ( offset < 0 | | offset > = SIZE ( get ( Type , base_type ) . elements ) ) break ; // copied from Check above
2016-05-04 00:38:33 +00:00
int address = base_address + base . metadata . offset . at ( offset ) ;
2016-04-11 02:26:37 +00:00
trace ( 9998 , " run " ) < < " address to copy to is " < < address < < end ( ) ;
// optimization: directly write the element rather than updating 'product'
// and writing the entire container
2016-05-04 00:38:33 +00:00
// Write Memory in PUT in Run
2016-04-11 02:26:37 +00:00
for ( int i = 0 ; i < SIZE ( ingredients . at ( 2 ) ) ; + + i ) {
trace ( 9999 , " mem " ) < < " storing " < < no_scientific ( ingredients . at ( 2 ) . at ( i ) ) < < " in location " < < address + i < < end ( ) ;
put ( Memory , address + i , ingredients . at ( 2 ) . at ( i ) ) ;
}
goto finish_instruction ;
}
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
2016-03-21 09:25:52 +00:00
+ parse : element : { x : " number " }
+ parse : element : { y : " number " }
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
2016-03-21 09:25:52 +00:00
+ parse : element : { x : " number " }
2016-02-17 18:09:48 +00:00
# todo: brittle
# type bar is unknown at this point, but we assign it a number
2016-03-21 09:25:52 +00:00
+ parse : element : { y : " bar " }
2016-02-19 23:19:36 +00:00
# later type bar geon
2015-10-29 18:56:10 +00:00
+ parse : - - - defining container bar
2015-05-18 23:09:09 +00:00
+ parse : type number : 1001
2016-03-21 09:25:52 +00:00
+ parse : element : { x : " number " }
+ parse : element : { y : " number " }
2015-05-18 23:09:09 +00:00
2016-04-29 00:23:50 +00:00
//: if a container is defined again, the new fields add to the original definition
: ( scenarios run )
: ( scenario container_extend )
container foo [
x : number
]
# add to previous definition
container foo [
y : number
]
def main [
1 : number < - copy 34
2 : number < - copy 35
3 : number < - get 1 : foo , 0 : offset
4 : number < - get 1 : foo , 1 : offset
]
+ mem : storing 34 in location 3
+ mem : storing 35 in location 4
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
}
2016-04-29 00:23:50 +00:00
//: Even though we allow containers to be extended, we don't allow this after
//: a call to transform_all. But we do want to detect this situation and raise
//: an error. This field will help us raise such errors.
: ( before " End type_info Fields " )
int Num_calls_to_transform_all_at_first_definition ;
: ( before " End type_info Constructor " )
Num_calls_to_transform_all_at_first_definition = - 1 ;
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 ) ) ;
2016-04-29 00:23:50 +00:00
if ( info . Num_calls_to_transform_all_at_first_definition = = - 1 ) {
// initial definition of this container
info . Num_calls_to_transform_all_at_first_definition = Num_calls_to_transform_all ;
}
else if ( info . Num_calls_to_transform_all_at_first_definition ! = Num_calls_to_transform_all ) {
// extension after transform_all
raise < < " there was a call to transform_all() between the definition of container " < < name < < " and a subsequent extension. This is not supported, since any recipes that used " < < name < < " values have already been transformed and 'frozen'. \n " < < end ( ) ;
return ;
}
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 ;
2016-02-17 18:09:48 +00:00
info . elements . push_back ( reagent ( element ) ) ;
2016-02-22 04:30:02 +00:00
replace_unknown_types_with_unique_ordinals ( info . elements . back ( ) . type , info ) ;
2016-02-19 23:19:36 +00:00
trace ( 9993 , " parse " ) < < " element: " < < to_string ( info . elements . back ( ) ) < < end ( ) ;
2016-02-15 20:25:13 +00:00
// End Load Container Element Definition
2015-05-14 17:30:01 +00:00
}
}
2016-02-22 04:30:02 +00:00
void replace_unknown_types_with_unique_ordinals ( type_tree * type , const type_info & info ) {
if ( ! type ) return ;
if ( ! type - > name . empty ( ) ) {
if ( contains_key ( Type_ordinal , type - > name ) ) {
type - > value = get ( Type_ordinal , type - > name ) ;
2016-02-07 00:47:10 +00:00
}
2016-02-22 04:30:02 +00:00
else if ( is_integer ( type - > name ) ) { // sometimes types will contain non-type tags, like numbers for the size of an array
type - > value = 0 ;
2016-02-07 00:47:10 +00:00
}
2016-03-19 07:51:19 +00:00
// End insert_container Special-cases
2016-02-22 04:30:02 +00:00
else if ( type - > name ! = " -> " ) { // used in recipe types
put ( Type_ordinal , type - > name , Next_type_ordinal + + ) ;
type - > value = get ( Type_ordinal , type - > name ) ;
2016-02-07 00:47:10 +00:00
}
}
2016-02-22 04:30:02 +00:00
replace_unknown_types_with_unique_ordinals ( type - > left , info ) ;
replace_unknown_types_with_unique_ordinals ( type - > right , info ) ;
2016-02-07 00:47:10 +00:00
}
2015-10-06 01:49:21 +00:00
void skip_bracket ( istream & in , string message ) {
skip_whitespace_and_comments ( in ) ;
if ( in . get ( ) ! = ' [ ' )
2016-02-26 21:04:55 +00:00
raise < < message < < ' \n ' < < end ( ) ;
2015-10-06 01:49:21 +00:00
}
2016-03-20 07:48:04 +00:00
//: ensure scenarios are consistent by always starting them at the same type
//: number.
2015-05-14 17:30:01 +00:00
: ( before " End Setup " ) //: for tests
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
2016-04-29 00:23:50 +00:00
: ( code )
void test_error_on_transform_all_between_container_definition_and_extension ( ) {
// define a container
run ( " container foo [ \n "
" a:number \n "
" ] \n " ) ;
// try to extend the container after transform
transform_all ( ) ;
CHECK_TRACE_DOESNT_CONTAIN_ERROR ( ) ;
Hide_errors = true ;
run ( " container foo [ \n "
" b:number \n "
" ] \n " ) ;
CHECK_TRACE_CONTAINS_ERROR ( ) ;
}
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 ;
2016-03-08 09:30:14 +00:00
def main [
2015-05-19 01:40:58 +00:00
# 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 )
2016-03-08 09:30:14 +00:00
def 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 ( ) ;
2016-03-14 03:26:47 +00:00
for ( int index = 0 ; index < SIZE ( caller . steps ) ; + + index ) {
2015-11-19 18:29:55 +00:00
instruction & inst = caller . steps . at ( index ) ;
2016-03-14 03:26:47 +00:00
for ( int i = 0 ; i < SIZE ( inst . ingredients ) ; + + i )
2016-03-21 09:25:52 +00:00
check_or_set_invalid_types ( inst . ingredients . at ( i ) . type , maybe ( caller . name ) , " ' " + to_original_string ( inst ) + " ' " ) ;
2016-03-14 03:26:47 +00:00
for ( int i = 0 ; i < SIZE ( inst . products ) ; + + i )
2016-03-21 09:25:52 +00:00
check_or_set_invalid_types ( inst . products . at ( i ) . type , maybe ( caller . name ) , " ' " + to_original_string ( inst ) + " ' " ) ;
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
}
2016-02-22 04:30:02 +00:00
void check_or_set_invalid_types ( type_tree * type , const string & block , const string & name ) {
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 ) ) {
2016-02-22 04:30:02 +00:00
assert ( ! type - > name . empty ( ) ) ;
if ( contains_key ( Type_ordinal , type - > name ) )
type - > value = get ( Type_ordinal , type - > name ) ;
2015-11-07 01:03:02 +00:00
else
2016-02-26 21:04:55 +00:00
raise < < block < < " unknown type " < < type - > name < < " in " < < name < < ' \n ' < < end ( ) ;
2015-11-07 01:03:02 +00:00
}
2016-02-22 04:30:02 +00:00
check_or_set_invalid_types ( type - > left , block , name ) ;
check_or_set_invalid_types ( type - > right , 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
2016-03-21 09:25:52 +00:00
+ parse : element : { x : " number " }
+ parse : element : { y : " number " }
2015-05-28 18:28:15 +00:00
2016-03-28 05:03:13 +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)
2016-03-14 03:26:47 +00:00
for ( int i = 0 ; i < SIZE ( info . elements ) ; + + i )
2016-02-17 18:09:48 +00:00
check_invalid_types ( info . elements . at ( i ) . type , maybe ( info . name ) , info . elements . at ( i ) . name ) ;
2015-05-19 01:40:58 +00:00
}
}
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
if ( type - > value = = 0 ) {
assert ( ! type - > left & & ! type - > right ) ;
return ;
}
if ( ! contains_key ( Type , type - > value ) )
2016-02-26 21:04:55 +00:00
raise < < block < < " unknown type in " < < name < < ' \n ' < < end ( ) ;
2015-11-07 01:03:02 +00:00
check_invalid_types ( type - > left , block , name ) ;
check_invalid_types ( type - > right , block , name ) ;
}