redshift 1.3.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. data/.gitignore +8 -0
  2. data/README +5 -0
  3. data/RELEASE-NOTES +455 -0
  4. data/TODO +431 -0
  5. data/bench/alg-state.rb +61 -0
  6. data/bench/bench +26 -0
  7. data/bench/bench.rb +10 -0
  8. data/bench/continuous.rb +76 -0
  9. data/bench/diff-bench +86 -0
  10. data/bench/discrete.rb +101 -0
  11. data/bench/euler.rb +50 -0
  12. data/bench/formula.rb +78 -0
  13. data/bench/half-strict.rb +103 -0
  14. data/bench/inertness.rb +116 -0
  15. data/bench/queue.rb +92 -0
  16. data/bench/run +66 -0
  17. data/bench/simple.rb +74 -0
  18. data/bench/strictness.rb +86 -0
  19. data/examples/ball-tkar.rb +72 -0
  20. data/examples/ball.rb +123 -0
  21. data/examples/collide.rb +70 -0
  22. data/examples/connect-parallel.rb +48 -0
  23. data/examples/connect.rb +109 -0
  24. data/examples/constants.rb +27 -0
  25. data/examples/delay.rb +80 -0
  26. data/examples/derivative.rb +77 -0
  27. data/examples/euler.rb +46 -0
  28. data/examples/external-lib.rb +33 -0
  29. data/examples/guard-debugger.rb +77 -0
  30. data/examples/lotka-volterra.rb +33 -0
  31. data/examples/persist-ball.rb +68 -0
  32. data/examples/pid.rb +87 -0
  33. data/examples/ports.rb +60 -0
  34. data/examples/queue.rb +56 -0
  35. data/examples/queue2.rb +98 -0
  36. data/examples/reset-with-event-val.rb +28 -0
  37. data/examples/scheduler.rb +104 -0
  38. data/examples/set-dest.rb +23 -0
  39. data/examples/simulink/README +1 -0
  40. data/examples/simulink/delay.mdl +827 -0
  41. data/examples/simulink/derivative.mdl +655 -0
  42. data/examples/step-discrete-profiler.rb +103 -0
  43. data/examples/subsystem.rb +109 -0
  44. data/examples/sync-deadlock.rb +32 -0
  45. data/examples/sync-queue.rb +91 -0
  46. data/examples/sync-retry.rb +20 -0
  47. data/examples/sync.rb +51 -0
  48. data/examples/thermostat.rb +53 -0
  49. data/examples/zeno.rb +53 -0
  50. data/lib/accessible-index.rb +47 -0
  51. data/lib/redshift.rb +1 -0
  52. data/lib/redshift/component.rb +412 -0
  53. data/lib/redshift/meta.rb +183 -0
  54. data/lib/redshift/mixins/zeno-debugger.rb +69 -0
  55. data/lib/redshift/port.rb +57 -0
  56. data/lib/redshift/queue.rb +104 -0
  57. data/lib/redshift/redshift.rb +111 -0
  58. data/lib/redshift/state.rb +31 -0
  59. data/lib/redshift/syntax.rb +558 -0
  60. data/lib/redshift/target/c.rb +37 -0
  61. data/lib/redshift/target/c/component-gen.rb +1303 -0
  62. data/lib/redshift/target/c/flow-gen.rb +325 -0
  63. data/lib/redshift/target/c/flow/algebraic.rb +85 -0
  64. data/lib/redshift/target/c/flow/buffer.rb +74 -0
  65. data/lib/redshift/target/c/flow/delay.rb +203 -0
  66. data/lib/redshift/target/c/flow/derivative.rb +101 -0
  67. data/lib/redshift/target/c/flow/euler.rb +67 -0
  68. data/lib/redshift/target/c/flow/expr.rb +113 -0
  69. data/lib/redshift/target/c/flow/rk4.rb +80 -0
  70. data/lib/redshift/target/c/library.rb +85 -0
  71. data/lib/redshift/target/c/world-gen.rb +1370 -0
  72. data/lib/redshift/target/spec.rb +34 -0
  73. data/lib/redshift/world.rb +300 -0
  74. data/rakefile +37 -0
  75. data/test/test.rb +52 -0
  76. data/test/test_buffer.rb +58 -0
  77. data/test/test_connect.rb +242 -0
  78. data/test/test_connect_parallel.rb +47 -0
  79. data/test/test_connect_strict.rb +135 -0
  80. data/test/test_constant.rb +74 -0
  81. data/test/test_delay.rb +145 -0
  82. data/test/test_derivative.rb +48 -0
  83. data/test/test_discrete.rb +592 -0
  84. data/test/test_discrete_isolated.rb +92 -0
  85. data/test/test_exit.rb +59 -0
  86. data/test/test_flow.rb +200 -0
  87. data/test/test_flow_link.rb +288 -0
  88. data/test/test_flow_sub.rb +100 -0
  89. data/test/test_flow_trans.rb +292 -0
  90. data/test/test_inherit.rb +127 -0
  91. data/test/test_inherit_event.rb +74 -0
  92. data/test/test_inherit_flow.rb +139 -0
  93. data/test/test_inherit_link.rb +65 -0
  94. data/test/test_inherit_setup.rb +56 -0
  95. data/test/test_inherit_state.rb +66 -0
  96. data/test/test_inherit_transition.rb +168 -0
  97. data/test/test_numerics.rb +34 -0
  98. data/test/test_queue.rb +90 -0
  99. data/test/test_queue_alone.rb +115 -0
  100. data/test/test_reset.rb +209 -0
  101. data/test/test_setup.rb +119 -0
  102. data/test/test_strict_continuity.rb +410 -0
  103. data/test/test_strict_reset_error.rb +30 -0
  104. data/test/test_strictness_error.rb +32 -0
  105. data/test/test_sync.rb +185 -0
  106. data/test/test_world.rb +328 -0
  107. metadata +204 -0
@@ -0,0 +1,80 @@
1
+ module RedShift; class RK4DifferentialFlow
2
+ def flow_wrapper cl, state
3
+ var_name = @var
4
+ flow = self
5
+
6
+ flow_name = "flow_#{CGenerator.make_c_name cl.name}_#{var_name}_#{state}"
7
+
8
+ Component::FlowWrapper.make_subclass flow_name do
9
+ @inspect_str = "#{cl.name}:#{state}: #{var_name} = #{flow.formula}"
10
+
11
+ ssn = cl.shadow_struct.name
12
+ cont_state_ssn = cl.cont_state_class.shadow_struct.name
13
+
14
+ # We need the struct
15
+ shadow_library_source_file.include(cl.shadow_library_include_file)
16
+
17
+ shadow_library_source_file.define(flow_name).instance_eval do
18
+ arguments "ComponentShadow *comp_shdw"
19
+ declare :shadow => %{
20
+ struct #{ssn} *shadow;
21
+ struct #{cont_state_ssn} *cont_state;
22
+ ContVar *var;
23
+ double ddt_#{var_name};
24
+ double value_4;
25
+ double time_step;
26
+ }
27
+ setup :shadow => %{
28
+ shadow = (#{ssn} *)comp_shdw;
29
+ cont_state = (#{cont_state_ssn} *)shadow->cont_state;
30
+ var = &cont_state->#{var_name};
31
+ time_step = shadow->world->time_step;
32
+ }
33
+ setup :rk_level => %{
34
+ shadow->world->rk_level--;
35
+ } # has to happen before referenced alg flows are called in other setups
36
+ body %{
37
+ switch (shadow->world->rk_level) {
38
+ case 0:
39
+ #{flow.translate(self, "ddt_#{var_name}", 0, cl).join("
40
+ ")};
41
+ var->value_1 = var->value_0 + ddt_#{var_name} * time_step/2;
42
+ break;
43
+
44
+ case 1:
45
+ #{flow.translate(self, "ddt_#{var_name}", 1, cl).join("
46
+ ")};
47
+ var->value_2 = var->value_0 + ddt_#{var_name} * time_step/2;
48
+ break;
49
+
50
+ case 2:
51
+ #{flow.translate(self, "ddt_#{var_name}", 2, cl).join("
52
+ ")};
53
+ var->value_3 = var->value_0 + ddt_#{var_name} * time_step;
54
+ break;
55
+
56
+ case 3:
57
+ #{flow.translate(self, "ddt_#{var_name}", 3, cl).join("
58
+ ")};
59
+ value_4 = var->value_0 + ddt_#{var_name} * time_step;
60
+ var->value_0 =
61
+ (-3*var->value_0 + 2*var->value_1 + 4*var->value_2 +
62
+ 2*var->value_3 + value_4) / 6;
63
+ break;
64
+
65
+ default:
66
+ rb_raise(#{declare_class RuntimeError},
67
+ "Bad rk_level, %d!", shadow->world->rk_level);
68
+ }
69
+
70
+ shadow->world->rk_level++;
71
+ var->rk_level = shadow->world->rk_level;
72
+ }
73
+ end
74
+
75
+ define_c_method :calc_function_pointer do
76
+ body "shadow->flow = &#{flow_name}"
77
+ end
78
+ end
79
+ end
80
+ end; end
@@ -0,0 +1,85 @@
1
+ $CGEN_VERBOSE = $REDSHIFT_CGEN_VERBOSE ## ugh.
2
+
3
+ require 'cgen/cshadow'
4
+
5
+ module RedShift
6
+ def RedShift.library
7
+ @clib ||= Library.new($REDSHIFT_CLIB_NAME)
8
+ end
9
+
10
+ class Library < CShadow::Library
11
+ def initialize(*args)
12
+ super
13
+
14
+ self.purge_source_dir = :delete
15
+ self.show_times_flag = $REDSHIFT_BUILD_TIMES
16
+
17
+ if $REDSHIFT_DEBUG
18
+ include_file.include "<assert.h>"
19
+ ## better to use something that raises a ruby exception
20
+ else
21
+ include_file.declare :assert => %{#define assert(cond) 0}
22
+ end
23
+
24
+ include_file.include '<math.h>'
25
+ end
26
+
27
+ # Call this to link with external libraries. See examples/external-lib.rb.
28
+ def link_with *libs
29
+ (@link_libs ||= []) << libs
30
+ end
31
+
32
+ def declare_external_constant *vars
33
+ @external_constants ||= {}
34
+ vars.each do |var|
35
+ @external_constants[var.to_s] = true
36
+ end
37
+ end
38
+
39
+ def external_constant? var
40
+ @external_constants and @external_constants.key?(var.to_s)
41
+ end
42
+
43
+ def extconf
44
+ super do |lines|
45
+ if @link_libs
46
+ libstr = " " + @link_libs.flatten.join(" ")
47
+ lines << %{$LOCAL_LIBS << "#{libstr}"}
48
+ end
49
+ end
50
+ end
51
+
52
+ def commit
53
+ return if committed?
54
+ precommit
55
+
56
+ ## this makes it a little trickier to use gdb
57
+ use_work_dir $REDSHIFT_WORK_DIR do
58
+ # $REDSHIFT_SKIP_BUILD is normally handled in redshift/target/c.rb.
59
+ # useful for: turnkey; fast start if no changes; manual lib edits
60
+ super(!$REDSHIFT_SKIP_BUILD)
61
+ end
62
+ ## freeze metadata in comp classes?
63
+ ## can cgen/cshadow freeze some stuff?
64
+ end
65
+
66
+ # Return list of all subclasses of Component (including Component).
67
+ def component_classes
68
+ return @component_classes if @component_classes
69
+
70
+ cc = Library.sort_class_tree(Component.subclasses)
71
+ @component_classes = cc if committed?
72
+
73
+ return cc
74
+ end
75
+
76
+ # Operate on Component class specifications gathered by meta.rb,
77
+ # and stored in the component classes.
78
+ def precommit
79
+ ## no need to precommit Component? Other abstract classes?
80
+ component_classes.each {|cl| cl.precommit}
81
+ ## optimization?
82
+ ## check if changed
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,1370 @@
1
+ module RedShift
2
+
3
+ class World
4
+ include CShadow
5
+ shadow_library RedShift.library
6
+ shadow_library_file "World"
7
+ shadow_library_source_file.include(Component.shadow_library_include_file)
8
+
9
+ shadow_library_include_file.declare :step_discrete_macros => '
10
+ #define INT2BOOL(i) (i ? Qtrue : Qfalse)
11
+
12
+ #define SWAP_VALUE(v, w) {VALUE ___tmp = v; v = w; w = ___tmp;}
13
+
14
+ #define EACH_COMP_DO(lc) \\
15
+ for (list = RARRAY(lc), list_i = list->len - 1; \\
16
+ list_i >= 0 && ( \\
17
+ comp = list->ptr[list_i], \\
18
+ comp_shdw = get_shadow(comp), \\
19
+ 1); \\
20
+ list_i--)
21
+
22
+ int dummy;
23
+ '.tabto(0)
24
+ # Note: EACH_COMP_DO(lc) block may use move_comp and remove_comp
25
+ # but it should (re)move none or all. Must have declarations for comp
26
+ # comp_shdw, list, and list_i in scope.
27
+
28
+ shadow_library_include_file.declare :cv_cache_entry => %{
29
+ typedef struct {
30
+ double *dbl_ptr;
31
+ double value;
32
+ } CVCacheEntry;
33
+ }
34
+
35
+ shadow_library_include_file.declare :link_cache_entry => %{
36
+ typedef struct {
37
+ ComponentShadow **link_ptr;
38
+ VALUE value;
39
+ } LinkCacheEntry;
40
+ }
41
+
42
+ shadow_library_include_file.declare :port_cache_entry => %{
43
+ typedef struct {
44
+ VALUE input_port;
45
+ VALUE other_port;
46
+ } PortCacheEntry;
47
+ }
48
+
49
+ # Initial size for the constant value cache.
50
+ CV_CACHE_SIZE = 64
51
+
52
+ # Initial size for the link cache.
53
+ LINK_CACHE_SIZE = 64
54
+
55
+ # Initial size for the port cache.
56
+ PORT_CACHE_SIZE = 64
57
+
58
+ World.subclasses.each do |sub|
59
+ file_name = CGenerator.make_c_name(sub.name).to_s
60
+ sub.shadow_library_file file_name
61
+ end
62
+
63
+ shadow_attr_accessor :curr_A => Array
64
+ shadow_attr_accessor :curr_P => Array
65
+ shadow_attr_accessor :curr_CR => Array
66
+ shadow_attr_accessor :curr_S => Array
67
+ shadow_attr_accessor :next_S => Array
68
+ shadow_attr_accessor :curr_T => Array
69
+ shadow_attr_accessor :active_E => Array
70
+ shadow_attr_accessor :prev_active_E => Array
71
+ shadow_attr_accessor :awake => Array
72
+ shadow_attr_accessor :prev_awake => Array
73
+ shadow_attr_accessor :strict_sleep => Array
74
+ shadow_attr_accessor :inert => Array
75
+ shadow_attr_accessor :diff_list => Array
76
+ shadow_attr_accessor :queue_sleep => Hash
77
+ protected \
78
+ :curr_A, :curr_P, :curr_CR, :curr_T,
79
+ :active_E=, :prev_active_E=, :awake=,
80
+ :strict_sleep=, :inert=, :diff_list=,
81
+ :queue_sleep=
82
+
83
+ shadow_attr_reader :time_step => "double time_step"
84
+ shadow_attr_accessor :zeno_limit => "long zeno_limit"
85
+ shadow_attr_accessor :step_count => "long step_count"
86
+ shadow_attr_accessor :clock_start => "double clock_start"
87
+ shadow_attr_accessor :clock_finish => "double clock_finish"
88
+ shadow_attr_accessor :zeno_counter => "long zeno_counter"
89
+ shadow_attr_reader :discrete_step => "long discrete_step"
90
+ shadow_attr_accessor :rk_level => "long rk_level"
91
+ shadow_attr_accessor :d_tick => "long d_tick"
92
+ shadow_attr_accessor :alg_nest => "long alg_nest"
93
+
94
+ shadow_attr_accessor :base_clock => "double base_clock"
95
+ shadow_attr_accessor :base_step_count =>
96
+ "long base_step_count"
97
+ protected :base_clock=, :base_step_count=
98
+
99
+ new_method.attr_code %{
100
+ shadow->rk_level = 0;
101
+ shadow->d_tick = 1;
102
+ shadow->alg_nest = 0;
103
+ } # d_tick=1 means alg flows need to be recalculated
104
+
105
+ shadow_struct.declare :constant_value_cache => %{
106
+ CVCacheEntry *constant_value_cache;
107
+ int cv_cache_size;
108
+ int cv_cache_used;
109
+ }
110
+ new_method.attr_code %{
111
+ shadow->constant_value_cache = 0;
112
+ shadow->cv_cache_size = 0;
113
+ shadow->cv_cache_used = 0;
114
+ }
115
+ free_function.free "free(shadow->constant_value_cache)"
116
+
117
+ shadow_struct.declare :link_cache => %{
118
+ LinkCacheEntry *link_cache;
119
+ int link_cache_size;
120
+ int link_cache_used;
121
+ }
122
+ ##should non-persist things like these get initialized in the alloc func, too?
123
+ new_method.attr_code %{
124
+ shadow->link_cache = 0;
125
+ shadow->link_cache_size = 0;
126
+ shadow->link_cache_used = 0;
127
+ }
128
+ free_function.free "free(shadow->link_cache)"
129
+
130
+ shadow_struct.declare :port_cache => %{
131
+ PortCacheEntry *port_cache;
132
+ int port_cache_size;
133
+ int port_cache_used;
134
+ }
135
+ new_method.attr_code %{
136
+ shadow->port_cache = 0;
137
+ shadow->port_cache_size = 0;
138
+ shadow->port_cache_used = 0;
139
+ }
140
+ free_function.free "free(shadow->port_cache)"
141
+
142
+ class << self
143
+ # Redefines World#new so that a library commit happens first.
144
+ def new(*args, &block)
145
+ commit # redefines World.new
146
+ new(*args, &block) # which is what this line calls
147
+ end
148
+ end
149
+
150
+ define_c_method :time_step= do
151
+ arguments :time_step
152
+ body %{
153
+ double new_time_step = NUM2DBL(time_step);
154
+ shadow->base_clock = shadow->base_clock +
155
+ (shadow->step_count - shadow->base_step_count) * shadow->time_step;
156
+ shadow->base_step_count = shadow->step_count;
157
+ shadow->time_step = new_time_step;
158
+ }
159
+ returns "shadow->time_step" ## needed?
160
+ end
161
+
162
+ define_c_method :clock do
163
+ returns %{
164
+ rb_float_new(
165
+ shadow->base_clock +
166
+ (shadow->step_count - shadow->base_step_count) * shadow->time_step)
167
+ }
168
+ end
169
+
170
+ # This is for when Component#world is nonpersistent
171
+ # _load_data_method.post_code %{
172
+ # rb_funcall(shadow->self, #{shadow_library_include_file.declare_symbol "__restore__world__refs"}, 0);
173
+ # }
174
+
175
+ define_c_method :bump_d_tick do
176
+ body "shadow->d_tick++"
177
+ end
178
+
179
+ slif = shadow_library_include_file
180
+ slif.declare :get_shadow => %{
181
+ inline static ComponentShadow *get_shadow(VALUE comp)
182
+ {
183
+ assert(RTEST(rb_obj_is_kind_of(comp, #{slif.declare_module CShadow})));
184
+ return (ComponentShadow *)DATA_PTR(comp);
185
+ }
186
+ }
187
+
188
+ define_c_method :step_continuous do
189
+ declare :locals => %{
190
+ VALUE comp_rb_ary[2], *comp_ary;
191
+ long len;
192
+ long var_count;
193
+ ContVar *var, *end_var;
194
+ long li, ci;
195
+ ComponentShadow *comp_shdw;
196
+ }.tabto(0)
197
+ body %{
198
+ comp_rb_ary[0] = shadow->awake;
199
+ comp_rb_ary[1] = shadow->inert;
200
+ for (li = 0; li < 2; li++) {
201
+ len = RARRAY(comp_rb_ary[li])->len;
202
+ comp_ary = RARRAY(comp_rb_ary[li])->ptr;
203
+ for (ci = 0; ci < len; ci++) {
204
+ Data_Get_Struct(comp_ary[ci], ComponentShadow, comp_shdw);
205
+ var_count = comp_shdw->var_count;
206
+ var = (ContVar *)(&FIRST_CONT_VAR(comp_shdw));
207
+ end_var = &var[var_count];
208
+
209
+ while (var < end_var) {
210
+ var->rk_level = 0;
211
+ if (!var->flow) {
212
+ var->value_1 = var->value_2 = var->value_3 = var->value_0;
213
+ }
214
+ var++;
215
+ }
216
+ }
217
+ }
218
+
219
+ for (shadow->rk_level = 1; shadow->rk_level <= 3; shadow->rk_level++) {
220
+ len = RARRAY(shadow->diff_list)->len;
221
+ comp_ary = RARRAY(shadow->diff_list)->ptr;
222
+ for (ci = 0; ci < len; ci++) {
223
+ Data_Get_Struct(comp_ary[ci], ComponentShadow, comp_shdw);
224
+
225
+ if (shadow->rk_level == 1 && !comp_shdw->has_diff) {
226
+ if (ci < len-1) {
227
+ comp_ary[ci] = comp_ary[len-1];
228
+ ci--;
229
+ }
230
+ len = RARRAY(shadow->diff_list)->len = len-1;
231
+ comp_shdw->diff_list = 0;
232
+ }
233
+ else {
234
+ var_count = comp_shdw->var_count;
235
+ var = (ContVar *)(&FIRST_CONT_VAR(comp_shdw));
236
+ end_var = &var[var_count];
237
+
238
+ while (var < end_var) {
239
+ if (var->flow &&
240
+ var->rk_level < shadow->rk_level &&
241
+ !var->algebraic)
242
+ (*var->flow)((ComponentShadow *)comp_shdw);
243
+ var++;
244
+ }
245
+ }
246
+ }
247
+ }
248
+
249
+ shadow->rk_level = 4;
250
+ for (li = 0; li < 2; li++) {
251
+ len = RARRAY(comp_rb_ary[li])->len;
252
+ comp_ary = RARRAY(comp_rb_ary[li])->ptr;
253
+ for (ci = 0; ci < len; ci++) {
254
+ Data_Get_Struct(comp_ary[ci], ComponentShadow, comp_shdw);
255
+ var_count = comp_shdw->var_count;
256
+ var = (ContVar *)(&FIRST_CONT_VAR(comp_shdw));
257
+ end_var = &var[var_count];
258
+
259
+ while (var < end_var) {
260
+ if (var->flow &&
261
+ var->rk_level < shadow->rk_level &&
262
+ !var->algebraic)
263
+ (*var->flow)((ComponentShadow *)comp_shdw);
264
+
265
+ if (var->rk_level == 4)
266
+ var->d_tick = 1; //# var will be current in discrete_step
267
+ else
268
+ var->d_tick = 0; //# var (if alg) will need to be evaled
269
+ var++;
270
+ }
271
+ }
272
+ }
273
+
274
+ shadow->d_tick = 1; //# alg flows need to be recalculated
275
+ shadow->rk_level = 0;
276
+ } # assumed that comp_ary[i] was a Component--enforced by World#create
277
+ end
278
+ private :step_continuous
279
+
280
+ ## define_c_function :my_instance_eval do
281
+ # shadow_library_source_file.define(:my_instance_eval).instance_eval do
282
+ # arguments "VALUE comp"
283
+ # return_type "VALUE"
284
+ # scope :static
285
+ # returns "rb_obj_instance_eval(0, 0, comp)"
286
+ # end
287
+ #
288
+ # shadow_library_source_file.define(:call_block).instance_eval do
289
+ # arguments "VALUE arg1", "VALUE block"
290
+ # return_type "VALUE"
291
+ # scope :static
292
+ # returns "rb_funcall(block, #{declare_symbol :call}, 0)"
293
+ # end
294
+
295
+ discrete_step_definer = proc do
296
+ parent.declare :static_locals => %{
297
+ static VALUE ExitState, GuardWrapperClass, ExprWrapperClass;
298
+ static VALUE ActionClass, EventClass, ResetClass, GuardClass;
299
+ static VALUE QMatchClass, ConnectClass;
300
+ static VALUE PostClass, DynamicEventClass, SyncClass;
301
+ }.tabto(0)
302
+
303
+ declare :locals => %{
304
+ VALUE comp;
305
+ ComponentShadow *comp_shdw;
306
+ VALUE *ptr;
307
+ long len;
308
+ long i;
309
+ struct RArray *list;
310
+ int list_i;
311
+ int did_reset;
312
+ }.tabto(0)
313
+
314
+ insteval_proc = declare_symbol :insteval_proc
315
+ capa = RUBY_VERSION.to_f >= 1.7 ? "aux.capa" : "capa"
316
+ epi = Component::EventPhaseItem
317
+ spi = Component::SyncPhaseItem
318
+
319
+ parent.declare :step_discrete_subs => %{
320
+ inline static VALUE cur_syncs(ComponentShadow *comp_shdw)
321
+ {
322
+ VALUE syncs = RARRAY(comp_shdw->trans)->ptr[#{Transition::S_IDX}];
323
+ assert(syncs == Qnil || RBASIC(syncs)->klass == SyncClass);
324
+ return syncs;
325
+ }
326
+ inline static VALUE cur_actions(ComponentShadow *comp_shdw)
327
+ {
328
+ VALUE actions = RARRAY(comp_shdw->trans)->ptr[#{Transition::A_IDX}];
329
+ assert(actions == Qnil || RBASIC(actions)->klass == ActionClass);
330
+ return actions;
331
+ }
332
+ inline static VALUE cur_posts(ComponentShadow *comp_shdw)
333
+ {
334
+ VALUE posts = RARRAY(comp_shdw->trans)->ptr[#{Transition::P_IDX}];
335
+ assert(posts == Qnil || RBASIC(posts)->klass == PostClass);
336
+ return posts;
337
+ }
338
+ inline static VALUE cur_events(ComponentShadow *comp_shdw)
339
+ {
340
+ VALUE events = RARRAY(comp_shdw->trans)->ptr[#{Transition::E_IDX}];
341
+ assert(events == Qnil || RBASIC(events)->klass == EventClass);
342
+ return events;
343
+ }
344
+ inline static VALUE cur_resets(ComponentShadow *comp_shdw)
345
+ {
346
+ VALUE resets = RARRAY(comp_shdw->trans)->ptr[#{Transition::R_IDX}];
347
+ assert(resets == Qnil || RBASIC(resets)->klass == ResetClass);
348
+ return resets;
349
+ }
350
+ inline static VALUE cur_connects(ComponentShadow *comp_shdw)
351
+ {
352
+ VALUE connects = RARRAY(comp_shdw->trans)->ptr[#{Transition::C_IDX}];
353
+ assert(connects == Qnil || RBASIC(connects)->klass == ConnectClass);
354
+ return connects;
355
+ }
356
+ inline static void move_comp(VALUE comp, VALUE list, VALUE next_list)
357
+ {
358
+ struct RArray *nl = RARRAY(next_list);
359
+ assert(RARRAY(list)->ptr[RARRAY(list)->len-1] == comp);
360
+ if (nl->len == nl->#{capa})
361
+ rb_ary_store(next_list, nl->len, comp);
362
+ else
363
+ nl->ptr[nl->len++] = comp;
364
+ --RARRAY(list)->len;
365
+ }
366
+ inline static void move_comp_to_hash(VALUE comp, VALUE list, VALUE hash)
367
+ {
368
+ struct RArray *l = RARRAY(list);
369
+ assert(l->ptr[l->len-1] == comp);
370
+ rb_hash_aset(hash, comp, Qtrue);
371
+ --l->len;
372
+ }
373
+ inline static void move_all_comps(VALUE list, VALUE next_list)
374
+ { //## this could be faster using memcpy
375
+ struct RArray *l = RARRAY(list);
376
+ while (l->len)
377
+ move_comp(l->ptr[l->len-1], list, next_list);
378
+ }
379
+ inline static void remove_comp(VALUE comp, VALUE list,
380
+ #{World.shadow_struct.name} *shadow)
381
+ {
382
+ ComponentShadow *comp_shdw = get_shadow(comp);
383
+ assert(RARRAY(list)->ptr[RARRAY(list)->len-1] == comp);
384
+ assert(comp_shdw->world == shadow);
385
+ comp_shdw->world = 0;
386
+ --RARRAY(list)->len;
387
+ }
388
+ inline static double eval_expr(VALUE comp, VALUE expr)
389
+ {
390
+ double (*fn)(ComponentShadow *), rslt;
391
+ assert(rb_obj_is_kind_of(expr, ExprWrapperClass));
392
+ fn = ((#{RedShift::Component::ExprWrapper.shadow_struct.name} *)
393
+ get_shadow(expr))->expr;
394
+ rslt = (*fn)(get_shadow(comp));
395
+ return rslt;
396
+ }
397
+ inline static ComponentShadow *eval_comp_expr(VALUE comp, VALUE expr)
398
+ {
399
+ ComponentShadow *(*fn)(ComponentShadow *);
400
+ ComponentShadow *rslt;
401
+ assert(rb_obj_is_kind_of(expr, ExprWrapperClass));
402
+ fn = ((#{RedShift::Component::ExprWrapper.shadow_struct.name} *)
403
+ get_shadow(expr))->expr;
404
+ rslt = (*fn)(get_shadow(comp));
405
+ return rslt;
406
+ }
407
+ inline static int test_cexpr_guard(VALUE comp, VALUE guard)
408
+ {
409
+ int (*fn)(ComponentShadow *), rslt;
410
+ assert(rb_obj_is_kind_of(guard, GuardWrapperClass));
411
+ fn = ((#{RedShift::Component::GuardWrapper.shadow_struct.name} *)
412
+ get_shadow(guard))->guard;
413
+ rslt = (*fn)(get_shadow(comp));
414
+ return rslt;
415
+ }
416
+ inline static int guard_enabled(VALUE comp, VALUE guards,
417
+ int discrete_step)
418
+ {
419
+ int i;
420
+ VALUE kl;
421
+ assert(BUILTIN_TYPE(guards) == T_ARRAY);
422
+ for (i = 0; i < RARRAY(guards)->len; i++) {
423
+ VALUE guard = RARRAY(guards)->ptr[i];
424
+
425
+ if (SYMBOL_P(guard)) {
426
+ if (!RTEST(rb_funcall(comp, SYM2ID(guard), 0)))
427
+ return 0;
428
+ }
429
+ else {
430
+ switch (BUILTIN_TYPE(guard)) {
431
+ case T_DATA:
432
+ if (RBASIC(guard)->klass == rb_cProc) {
433
+ if (!RTEST(rb_funcall(comp, #{insteval_proc}, 1, guard)))
434
+ return 0; //## faster way to call instance_eval ???
435
+ }
436
+ else {
437
+ assert(!rb_obj_is_kind_of(guard, rb_cProc));
438
+ if (!test_cexpr_guard(comp, guard))
439
+ return 0;
440
+ }
441
+ break;
442
+
443
+ case T_ARRAY:
444
+ kl = RBASIC(guard)->klass;
445
+ if (kl == QMatchClass) {
446
+ int len = RARRAY(guard)->len;
447
+ VALUE *ptr = RARRAY(guard)->ptr;
448
+ assert(len > 0);
449
+ VALUE queue_name = ptr[0];
450
+ VALUE queue = rb_funcall(comp, SYM2ID(queue_name), 0);
451
+ if (!RTEST(rb_funcall2(queue, #{declare_symbol :head_matches},
452
+ len-1, &ptr[1])))
453
+ return 0;
454
+ }
455
+ else {
456
+ rb_raise(#{declare_class StandardError},
457
+ "Bad array class in guard_enabled().");
458
+ }
459
+ break;
460
+
461
+ case T_CLASS:
462
+ assert(RTEST(rb_funcall(guard, #{declare_symbol "<"},
463
+ 1, GuardWrapperClass)));
464
+ guard = rb_funcall(guard, #{declare_symbol :instance}, 0);
465
+ RARRAY(guards)->ptr[i] = guard;
466
+ if (!test_cexpr_guard(comp, guard))
467
+ return 0;
468
+ break;
469
+
470
+ default:
471
+ assert(0);
472
+ }
473
+ }
474
+ }
475
+ return 1;
476
+ }
477
+
478
+ inline static int comp_can_sync(ComponentShadow *comp_shdw,
479
+ #{World.shadow_struct.name} *shadow)
480
+ {
481
+ int i, j;
482
+ int can_sync = 1;
483
+ VALUE syncs = cur_syncs(comp_shdw);
484
+ assert(RTEST(syncs));
485
+
486
+ for (i = RARRAY(syncs)->len - 1; i >= 0; i--) {
487
+ VALUE sync = RARRAY(syncs)->ptr[i];
488
+ assert(RARRAY(sync)->len == 3);
489
+ int link_offset = FIX2INT(RARRAY(sync)->ptr[#{spi::LINK_OFFSET_IDX}]);
490
+ VALUE event = RARRAY(sync)->ptr[#{spi::EVENT_IDX}];
491
+ ComponentShadow *link_shdw =
492
+ *(ComponentShadow **)(((char *)comp_shdw) + link_offset);
493
+
494
+ if (!link_shdw || !RTEST(link_shdw->trans)) {
495
+ can_sync = 0;
496
+ break;
497
+ }
498
+
499
+ int found = 0;
500
+ VALUE link_events = cur_events(link_shdw);
501
+ if (RTEST(link_events)) {
502
+ VALUE *ptr = RARRAY(link_events)->ptr;
503
+ long len = RARRAY(link_events)->len;
504
+
505
+ for (j = len; j > 0; j--, ptr++) {
506
+ VALUE link_event = RARRAY(*ptr)->ptr[#{epi::E_IDX}];
507
+ if (link_event == event) {
508
+ found = 1;
509
+ break;
510
+ }
511
+ }
512
+ }
513
+
514
+ if (!found) {
515
+ can_sync = 0;
516
+ break;
517
+ }
518
+ }
519
+
520
+ //%% hook_can_sync(comp_shdw->self, INT2BOOL(can_sync));
521
+ return can_sync;
522
+ }
523
+
524
+ inline static int eval_events(ComponentShadow *comp_shdw,
525
+ #{World.shadow_struct.name} *shadow)
526
+ {
527
+ VALUE events = cur_events(comp_shdw);
528
+ int has_events = RTEST(events);
529
+
530
+ if (has_events) {
531
+ VALUE *ptr = RARRAY(events)->ptr;
532
+ long len = RARRAY(events)->len;
533
+ int i;
534
+ VALUE comp = comp_shdw->self;
535
+
536
+ for (i = len; i > 0; i--, ptr++) {
537
+ int event_idx = FIX2INT(RARRAY(*ptr)->ptr[#{epi::I_IDX}]);
538
+ VALUE event_val = RARRAY(*ptr)->ptr[#{epi::V_IDX}];
539
+
540
+ //## maybe this distinction should be made clear in the array
541
+ //## itself, with a numeric switch, say.
542
+ if (TYPE(event_val) == T_DATA &&
543
+ rb_obj_is_kind_of(event_val, DynamicEventClass))
544
+ event_val = rb_funcall(comp, #{insteval_proc}, 1, event_val);
545
+ else if (rb_obj_is_kind_of(event_val, ExprWrapperClass))
546
+ event_val = rb_float_new(eval_expr(comp, event_val));
547
+
548
+ //%% hook_eval_event(comp, RARRAY(*ptr)->ptr[#{epi::E_IDX}],
549
+ //%% event_val);
550
+ RARRAY(comp_shdw->next_event_values)->ptr[event_idx] = event_val;
551
+ }
552
+ }
553
+
554
+ return has_events;
555
+ }
556
+
557
+ inline static void cache_new_constant_value(
558
+ double *dbl_ptr, double value,
559
+ #{World.shadow_struct.name} *shadow)
560
+ {
561
+ CVCacheEntry *entry;
562
+
563
+ if (!shadow->constant_value_cache) {
564
+ int n = #{CV_CACHE_SIZE};
565
+ shadow->constant_value_cache = malloc(n*sizeof(CVCacheEntry));
566
+ shadow->cv_cache_size = n;
567
+ shadow->cv_cache_used = 0;
568
+ }
569
+ if (shadow->cv_cache_used == shadow->cv_cache_size) {
570
+ int n_bytes;
571
+ shadow->cv_cache_size *= 2;
572
+ n_bytes = shadow->cv_cache_size*sizeof(CVCacheEntry);
573
+ shadow->constant_value_cache = realloc(
574
+ shadow->constant_value_cache, n_bytes);
575
+ if (!shadow->constant_value_cache) {
576
+ rb_raise(#{declare_class NoMemoryError},
577
+ "Out of memory trying to allocate %d bytes for CV cache.",
578
+ n_bytes);
579
+ }
580
+ }
581
+ entry = &shadow->constant_value_cache[shadow->cv_cache_used];
582
+ entry->dbl_ptr = dbl_ptr;
583
+ entry->value = value;
584
+ shadow->cv_cache_used += 1;
585
+ }
586
+
587
+ inline static int assign_new_constant_values(
588
+ #{World.shadow_struct.name} *shadow)
589
+ {
590
+ int did_reset = shadow->cv_cache_used;
591
+
592
+ if (shadow->cv_cache_used) {
593
+ int i;
594
+ CVCacheEntry *entry;
595
+
596
+ entry = shadow->constant_value_cache;
597
+ for (i = shadow->cv_cache_used; i > 0; i--, entry++) {
598
+ *entry->dbl_ptr = entry->value;
599
+ }
600
+ shadow->cv_cache_used = 0;
601
+ }
602
+
603
+ return did_reset;
604
+ }
605
+
606
+ inline static void cache_new_link(
607
+ ComponentShadow **link_ptr, VALUE value,
608
+ #{World.shadow_struct.name} *shadow)
609
+ {
610
+ LinkCacheEntry *entry;
611
+
612
+ if (!shadow->link_cache) {
613
+ int n = #{LINK_CACHE_SIZE};
614
+ shadow->link_cache = malloc(n*sizeof(LinkCacheEntry));
615
+ shadow->link_cache_size = n;
616
+ shadow->link_cache_used = 0;
617
+ }
618
+ if (shadow->link_cache_used == shadow->link_cache_size) {
619
+ int n_bytes;
620
+ shadow->link_cache_size *= 2;
621
+ n_bytes = shadow->link_cache_size*sizeof(LinkCacheEntry);
622
+ shadow->link_cache = realloc(shadow->link_cache, n_bytes);
623
+ if (!shadow->link_cache) {
624
+ rb_raise(#{declare_class NoMemoryError},
625
+ "Out of memory trying to allocate %d bytes for link cache.",
626
+ n_bytes);
627
+ }
628
+ }
629
+ entry = &shadow->link_cache[shadow->link_cache_used];
630
+ entry->link_ptr = link_ptr;
631
+ entry->value = value;
632
+ shadow->link_cache_used += 1;
633
+ }
634
+
635
+ inline static int assign_new_links(
636
+ #{World.shadow_struct.name} *shadow)
637
+ {
638
+ int did_reset = shadow->link_cache_used;
639
+
640
+ if (shadow->link_cache_used) {
641
+ int i;
642
+ LinkCacheEntry *entry;
643
+
644
+ entry = shadow->link_cache;
645
+ for (i = shadow->link_cache_used; i > 0; i--, entry++) {
646
+ ComponentShadow *comp_shdw;
647
+ if (NIL_P(entry->value))
648
+ comp_shdw = 0;
649
+ else
650
+ comp_shdw = get_shadow(entry->value);
651
+ *entry->link_ptr = comp_shdw;
652
+ }
653
+ shadow->link_cache_used = 0;
654
+ }
655
+
656
+ return did_reset;
657
+ }
658
+
659
+ inline static void cache_new_port(VALUE input_port, VALUE other_port,
660
+ #{World.shadow_struct.name} *shadow)
661
+ {
662
+ PortCacheEntry *entry;
663
+
664
+ if (!shadow->port_cache) {
665
+ int n = #{PORT_CACHE_SIZE};
666
+ shadow->port_cache = malloc(n*sizeof(PortCacheEntry));
667
+ shadow->port_cache_size = n;
668
+ shadow->port_cache_used = 0;
669
+ }
670
+ if (shadow->port_cache_used == shadow->port_cache_size) {
671
+ int n_bytes;
672
+ shadow->port_cache_size *= 2;
673
+ n_bytes = shadow->port_cache_size*sizeof(PortCacheEntry);
674
+ shadow->port_cache = realloc(shadow->port_cache, n_bytes);
675
+ if (!shadow->port_cache) {
676
+ rb_raise(#{declare_class NoMemoryError},
677
+ "Out of memory trying to allocate %d bytes for port cache.",
678
+ n_bytes);
679
+ }
680
+ }
681
+ entry = &shadow->port_cache[shadow->port_cache_used];
682
+ entry->input_port = input_port;
683
+ entry->other_port = other_port;
684
+ shadow->port_cache_used += 1;
685
+ }
686
+
687
+ inline static int assign_new_ports(
688
+ #{World.shadow_struct.name} *shadow)
689
+ {
690
+ int did_reset = shadow->port_cache_used;
691
+
692
+ if (shadow->port_cache_used) {
693
+ int i;
694
+ PortCacheEntry *entry;
695
+
696
+ entry = shadow->port_cache;
697
+ for (i = shadow->port_cache_used; i > 0; i--, entry++) {
698
+ rb_funcall(entry->input_port, #{declare_symbol :_connect}, 1,
699
+ entry->other_port);
700
+ }
701
+ shadow->port_cache_used = 0;
702
+ }
703
+
704
+ return did_reset;
705
+ }
706
+
707
+ inline static int eval_continuous_resets(ComponentShadow *comp_shdw,
708
+ #{World.shadow_struct.name} *shadow)
709
+ {
710
+ VALUE resets = cur_resets(comp_shdw);
711
+ VALUE cont_resets;
712
+ int has_cont_resets;
713
+
714
+ if (!RTEST(resets))
715
+ return 0;
716
+
717
+ cont_resets = RARRAY(resets)->ptr[0];
718
+ has_cont_resets = RTEST(cont_resets);
719
+
720
+ if (has_cont_resets) {
721
+ VALUE *ptr = RARRAY(cont_resets)->ptr;
722
+ long len = RARRAY(cont_resets)->len;
723
+ int i;
724
+ VALUE comp = comp_shdw->self;
725
+ ContVar *var = (ContVar *)&FIRST_CONT_VAR(comp_shdw);
726
+ assert(len <= comp_shdw->var_count);
727
+
728
+ for (i = 0; i < len; i++, var++, ptr++) {
729
+ VALUE reset = *ptr;
730
+ if (reset == Qnil) {
731
+ var->reset = 0;
732
+ }
733
+ else {
734
+ double new_value;
735
+
736
+ //## if (var->algebraic)
737
+ //## rb_raise(#{declare_class AlgebraicAssignmentError},
738
+ //## "reset of variable with algebraic flow"); //## do statically?
739
+
740
+ switch(TYPE(reset)) {
741
+ case T_FLOAT:
742
+ new_value = RFLOAT(reset)->value;
743
+ break;
744
+ default:
745
+ if (RBASIC(reset)->klass == rb_cProc) {
746
+ VALUE val = rb_funcall(comp, #{insteval_proc}, 1, reset);
747
+ switch(TYPE(val)) {
748
+ case T_FLOAT:
749
+ new_value = RFLOAT(val)->value;
750
+ break;
751
+ case T_FIXNUM:
752
+ new_value = FIX2INT(val);
753
+ break;
754
+ default:
755
+ rs_raise(#{declare_class VarTypeError}, comp,
756
+ "tried to reset cont var with %s.",
757
+ STR2CSTR(rb_funcall(
758
+ rb_funcall(val, #{declare_symbol :class}, 0),
759
+ #{declare_symbol :to_s},
760
+ 0))
761
+ );
762
+ }
763
+ }
764
+ else
765
+ new_value = eval_expr(comp, reset);
766
+ }
767
+
768
+ //%% hook_eval_reset_continuous(comp,
769
+ //%% rb_funcall(comp_shdw->cont_state->self,//
770
+ //%% #{declare_symbol :var_at_index},1,INT2NUM(i)),
771
+ //%% rb_float_new(new_value));
772
+ var->value_1 = new_value;
773
+ var->reset = 1;
774
+ }
775
+ }
776
+ }
777
+
778
+ return has_cont_resets;
779
+ }
780
+
781
+ inline static int eval_constant_resets(ComponentShadow *comp_shdw,
782
+ #{World.shadow_struct.name} *shadow)
783
+ {
784
+ VALUE resets = cur_resets(comp_shdw);
785
+ VALUE const_resets;
786
+ VALUE link_resets;
787
+ int has_const_resets;
788
+ int has_link_resets;
789
+ int i;
790
+ VALUE comp;
791
+
792
+ if (!RTEST(resets))
793
+ return 0;
794
+
795
+ const_resets = RARRAY(resets)->ptr[1];
796
+ link_resets = RARRAY(resets)->ptr[2];
797
+ has_const_resets = RTEST(const_resets);
798
+ has_link_resets = RTEST(link_resets);
799
+ comp = comp_shdw->self;
800
+
801
+ if (has_const_resets) {
802
+ VALUE *ptr = RARRAY(const_resets)->ptr;
803
+ long len = RARRAY(const_resets)->len;
804
+
805
+ for (i = 0; i < len; i++) {
806
+ VALUE pair = ptr[i];
807
+ int offset = NUM2INT(RARRAY(pair)->ptr[0]);
808
+ VALUE reset = RARRAY(pair)->ptr[1];
809
+ double new_value;
810
+
811
+ switch(TYPE(reset)) {
812
+ case T_FLOAT:
813
+ new_value = RFLOAT(reset)->value;
814
+ break;
815
+ default:
816
+ if (RBASIC(reset)->klass == rb_cProc)
817
+ new_value =
818
+ NUM2DBL(rb_funcall(comp, #{insteval_proc}, 1, reset));
819
+ else
820
+ new_value = eval_expr(comp, reset);
821
+ }
822
+
823
+ //%% hook_eval_reset_constant(comp,
824
+ //%% RARRAY(pair)->ptr[2], rb_float_new(new_value));
825
+ cache_new_constant_value(
826
+ (double *)((char *)comp_shdw + offset),
827
+ new_value, shadow);
828
+ }
829
+ }
830
+
831
+ if (has_link_resets) {
832
+ VALUE *ptr = RARRAY(link_resets)->ptr;
833
+ long len = RARRAY(link_resets)->len;
834
+
835
+ for (i = 0; i < len; i++) {
836
+ VALUE pair = ptr[i];
837
+ int offset = NUM2INT(RARRAY(pair)->ptr[0]);
838
+ VALUE reset = RARRAY(pair)->ptr[1];
839
+ VALUE new_value;
840
+
841
+ if (rb_obj_is_kind_of(reset, ExprWrapperClass)) {
842
+ ComponentShadow *new_sh;
843
+ new_sh = (ComponentShadow *) eval_comp_expr(comp, reset);
844
+ new_value = new_sh ? new_sh->self : Qnil;
845
+ }
846
+ else if (reset == Qnil) {
847
+ new_value = reset;
848
+ }
849
+ else if (RBASIC(reset)->klass == rb_cProc) {
850
+ new_value =
851
+ (VALUE)(rb_funcall(comp, #{insteval_proc}, 1, reset));
852
+ }
853
+ else {
854
+ new_value = reset; // will raise exception below
855
+ }
856
+
857
+ if (!NIL_P(new_value) &&
858
+ rb_obj_is_kind_of(new_value, RARRAY(pair)->ptr[3]) != Qtrue) {
859
+ VALUE to_s = #{declare_symbol :to_s};
860
+ rs_raise(#{declare_class LinkTypeError}, comp_shdw->self,
861
+ "tried to reset %s, which is declared %s, with %s.",
862
+ STR2CSTR(rb_funcall(RARRAY(pair)->ptr[2], to_s, 0)),
863
+ STR2CSTR(rb_funcall(RARRAY(pair)->ptr[3], to_s, 0)),
864
+ STR2CSTR(rb_funcall(
865
+ rb_funcall(new_value, #{declare_symbol :class}, 0), to_s, 0))
866
+ );
867
+ }
868
+
869
+ //%% hook_eval_reset_link(comp,
870
+ //%% RARRAY(pair)->ptr[2], (VALUE)new_value);
871
+ cache_new_link(
872
+ (ComponentShadow **)((char *)comp_shdw + offset),
873
+ new_value, shadow);
874
+ }
875
+ }
876
+
877
+ return has_const_resets || has_link_resets;
878
+ }
879
+
880
+ inline static int eval_port_connects(ComponentShadow *comp_shdw,
881
+ #{World.shadow_struct.name} *shadow)
882
+ {
883
+ VALUE connects = cur_connects(comp_shdw);
884
+
885
+ if (!RTEST(connects))
886
+ return 0;
887
+ else {
888
+ int i;
889
+ VALUE comp = comp_shdw->self;
890
+ VALUE *ptr = RARRAY(connects)->ptr;
891
+ long len = RARRAY(connects)->len;
892
+
893
+ for (i = 0; i < len; i++) {
894
+ VALUE pair = ptr[i];
895
+ VALUE input_var = RARRAY(pair)->ptr[0];
896
+ VALUE connect_spec = RARRAY(pair)->ptr[1];
897
+ VALUE input_port;
898
+ VALUE other_port;
899
+
900
+ input_port = rb_funcall(comp, #{declare_symbol :port}, 1, input_var);
901
+ if (connect_spec == Qnil) {
902
+ other_port = Qnil;
903
+ }
904
+ else if (RBASIC(connect_spec)->klass == rb_cProc) {
905
+ other_port = rb_funcall(comp, #{insteval_proc}, 1, connect_spec);
906
+ }
907
+ else {
908
+ // ## unimpl.
909
+ }
910
+
911
+ //%% hook_eval_port_connect(comp,
912
+ //%% input_port, other_port);
913
+ cache_new_port(input_port, other_port, shadow);
914
+ }
915
+ return 1;
916
+ }
917
+ }
918
+
919
+ inline static int assign_new_cont_values(ComponentShadow *comp_shdw)
920
+ {
921
+ VALUE resets = cur_resets(comp_shdw);
922
+ VALUE cont_resets;
923
+ ContVar *var;
924
+ int did_reset;
925
+ long len;
926
+ long i;
927
+
928
+ if (!RTEST(resets))
929
+ return 0;
930
+
931
+ cont_resets = RARRAY(resets)->ptr[0];
932
+ var = (ContVar *)&FIRST_CONT_VAR(comp_shdw);
933
+ did_reset = 0;
934
+
935
+ len = RARRAY(cont_resets)->len;
936
+ for (i = len; i > 0; i--, var++) {
937
+ if (var->reset) {
938
+ var->reset = 0;
939
+ var->value_0 = var->value_1;
940
+ did_reset = 1;
941
+ }
942
+ }
943
+ return did_reset;
944
+ }
945
+
946
+ inline static void do_actions(ComponentShadow *comp_shdw, VALUE actions,
947
+ #{World.shadow_struct.name} *shadow)
948
+ {
949
+ long i;
950
+ VALUE comp = comp_shdw->self;
951
+
952
+ assert(RTEST(actions));
953
+
954
+ for (i = 0; i < RARRAY(actions)->len; i++) {
955
+ VALUE val = RARRAY(actions)->ptr[i];
956
+ //%% hook_call_action(comp, val);
957
+
958
+ if (SYMBOL_P(val))
959
+ rb_funcall(comp, SYM2ID(val), 0);
960
+ else
961
+ rb_funcall(comp, #{insteval_proc}, 1, val);
962
+ //## this tech. could be applied in EVENT and RESET.
963
+ //## also, component-gen can make use of this optimization
964
+ //## for procs, using code similar to that for guards.
965
+ //# rb_obj_instance_eval(1, &RARRAY(actions)->ptr[i], comp);
966
+ //# rb_iterate(my_instance_eval, comp, call_block, RARRAY(actions)->ptr[i]);
967
+ }
968
+ }
969
+
970
+ inline static void update_all_alg_vars(ComponentShadow *comp_shdw,
971
+ #{World.shadow_struct.name} *shadow)
972
+ {
973
+ ContVar *vars = (ContVar *)&FIRST_CONT_VAR(comp_shdw);
974
+ long count = comp_shdw->var_count;
975
+ long i;
976
+ for(i = 0; i < count; i++) {
977
+ ContVar *var = &vars[i];
978
+ if (var->algebraic &&
979
+ (var->strict ? var->d_tick == 0 :
980
+ var->d_tick != shadow->d_tick)) {
981
+ var->flow(comp_shdw);
982
+ }
983
+ }
984
+ }
985
+
986
+ inline static void start_trans(ComponentShadow *comp_shdw,
987
+ #{World.shadow_struct.name} *shadow,
988
+ VALUE trans, VALUE dest)
989
+ {
990
+ comp_shdw->trans = trans;
991
+ comp_shdw->dest = dest;
992
+ //%% hook_start_transition(comp_shdw->self, trans, dest);
993
+ }
994
+
995
+ inline static void finish_trans(ComponentShadow *comp_shdw,
996
+ #{World.shadow_struct.name} *shadow)
997
+ { //%% hook_finish_transition(comp_shdw->self, comp_shdw->trans,
998
+ //%% comp_shdw->dest);
999
+ if (comp_shdw->state != comp_shdw->dest) {
1000
+ update_all_alg_vars(comp_shdw, shadow);
1001
+ comp_shdw->state = comp_shdw->dest;
1002
+ rs_update_cache(comp_shdw);
1003
+ comp_shdw->checked = 0;
1004
+ }
1005
+ comp_shdw->trans = Qnil;
1006
+ comp_shdw->dest = Qnil;
1007
+ }
1008
+
1009
+ inline static void abort_trans(ComponentShadow *comp_shdw)
1010
+ {
1011
+ comp_shdw->trans = Qnil;
1012
+ comp_shdw->dest = Qnil;
1013
+ }
1014
+
1015
+ inline static void check_strict(ComponentShadow *comp_shdw)
1016
+ {
1017
+ ContVar *vars = (ContVar *)&FIRST_CONT_VAR(comp_shdw);
1018
+ long count = comp_shdw->var_count;
1019
+ long i;
1020
+
1021
+ for(i = 0; i < count; i++) {
1022
+ ContVar *var = &vars[i];
1023
+ if (var->ck_strict) {
1024
+ var->ck_strict = 0;
1025
+ (*var->flow)(comp_shdw);
1026
+ if (var->value_0 != var->value_1) {
1027
+ rb_funcall(comp_shdw->self,
1028
+ #{declare_symbol :handle_strictness_error}, 3, INT2NUM(i),
1029
+ rb_float_new(var->value_0), rb_float_new(var->value_1));
1030
+ }
1031
+ }
1032
+ }
1033
+ }
1034
+
1035
+ inline static void check_guards(#{World.shadow_struct.name} *shadow,
1036
+ int sync_retry)
1037
+ {
1038
+ VALUE comp;
1039
+ ComponentShadow *comp_shdw;
1040
+ struct RArray *list;
1041
+ int list_i;
1042
+ VALUE *ptr;
1043
+ long len, cur;
1044
+
1045
+ EACH_COMP_DO(shadow->prev_awake) {
1046
+ int enabled = 0;
1047
+
1048
+ if (shadow->discrete_step == 0)
1049
+ comp_shdw->checked = 0;
1050
+
1051
+ len = RARRAY(comp_shdw->outgoing)->len - 1; //# last is flags
1052
+ cur = len;
1053
+
1054
+ if (len == 0) {
1055
+ move_comp(comp, shadow->prev_awake, shadow->inert);
1056
+ continue;
1057
+ }
1058
+
1059
+ ptr = RARRAY(comp_shdw->outgoing)->ptr;
1060
+ if (sync_retry)
1061
+ cur -= 4 * comp_shdw->tmp.trans.idx;
1062
+ else
1063
+ comp_shdw->tmp.trans.idx = 0;
1064
+
1065
+ while (cur >= 4) {
1066
+ VALUE trans, dest, guard, strict;
1067
+
1068
+ strict = ptr[--cur];
1069
+ guard = ptr[--cur];
1070
+
1071
+ enabled = !RTEST(guard) ||
1072
+ ((comp_shdw->checked && RTEST(strict)) ? 0 :
1073
+ guard_enabled(comp, guard, shadow->discrete_step));
1074
+
1075
+ //%% hook_eval_guard(comp, guard, INT2BOOL(enabled),
1076
+ //%% ptr[cur-2], ptr[cur-1]);
1077
+
1078
+ if (enabled) {
1079
+ dest = ptr[--cur];
1080
+ trans = ptr[--cur];
1081
+ start_trans(comp_shdw, shadow, trans, dest);
1082
+ if (RTEST(cur_syncs(comp_shdw))) {
1083
+ move_comp(comp, shadow->prev_awake, shadow->curr_S);
1084
+ comp_shdw->tmp.trans.idx = (len - cur)/4;
1085
+ }
1086
+ else
1087
+ move_comp(comp, shadow->prev_awake, shadow->curr_T);
1088
+ break;
1089
+ }
1090
+ else
1091
+ cur -= 2;
1092
+ }
1093
+
1094
+ if (!enabled) {
1095
+ VALUE qrc;
1096
+ if (comp_shdw->strict)
1097
+ move_comp(comp, shadow->prev_awake, shadow->strict_sleep);
1098
+ else if (comp_shdw->sleepable &&
1099
+ (qrc = rb_ivar_get(comp, #{declare_symbol :@queue_ready_count}),
1100
+ qrc == Qnil || qrc == INT2FIX(0))) {
1101
+ move_comp_to_hash(comp, shadow->prev_awake, shadow->queue_sleep);
1102
+ }
1103
+ else
1104
+ move_comp(comp, shadow->prev_awake, shadow->awake);
1105
+ comp_shdw->checked = 1;
1106
+ }
1107
+ }
1108
+ assert(RARRAY(shadow->prev_awake)->len == 0);
1109
+ }
1110
+
1111
+ inline static void do_sync_phase(#{World.shadow_struct.name} *shadow)
1112
+ {
1113
+ VALUE comp;
1114
+ ComponentShadow *comp_shdw;
1115
+ struct RArray *list;
1116
+ int list_i;
1117
+ int changed;
1118
+
1119
+ do {
1120
+ changed = 0;
1121
+ EACH_COMP_DO(shadow->curr_S) {
1122
+ if (comp_can_sync(comp_shdw, shadow)) {
1123
+ move_comp(comp, shadow->curr_S, shadow->next_S);
1124
+ }
1125
+ else {
1126
+ changed = 1;
1127
+ abort_trans(comp_shdw);
1128
+ move_comp(comp, shadow->curr_S, shadow->prev_awake);
1129
+ }
1130
+ }
1131
+
1132
+ assert(RARRAY(shadow->curr_S)->len == 0);
1133
+ SWAP_VALUE(shadow->curr_S, shadow->next_S);
1134
+ //%% hook_sync_step(shadow->curr_S, INT2BOOL(changed));
1135
+ } while (changed);
1136
+
1137
+ move_all_comps(shadow->curr_S, shadow->curr_T);
1138
+ }
1139
+
1140
+ }.tabto(0)
1141
+
1142
+ comp_id = declare_class RedShift::Component
1143
+ get_const = proc {|k| "rb_const_get(#{comp_id}, #{declare_symbol k})"}
1144
+ init %{
1145
+ ExitState = #{get_const[:Exit]};
1146
+ ActionClass = #{get_const[:ActionPhase]};
1147
+ PostClass = #{get_const[:PostPhase]};
1148
+ EventClass = #{get_const[:EventPhase]};
1149
+ ResetClass = #{get_const[:ResetPhase]};
1150
+ ConnectClass = #{get_const[:ConnectPhase]};
1151
+ GuardClass = #{get_const[:GuardPhase]};
1152
+ SyncClass = #{get_const[:SyncPhase]};
1153
+ QMatchClass = #{get_const[:QMatch]};
1154
+ GuardWrapperClass = #{get_const[:GuardWrapper]};
1155
+ ExprWrapperClass = #{get_const[:ExprWrapper]};
1156
+ DynamicEventClass = #{get_const[:DynamicEventValue]};
1157
+ }
1158
+
1159
+ body %{
1160
+ //%% hook_begin();
1161
+ shadow->zeno_counter = 0;
1162
+
1163
+ for (shadow->discrete_step = 0 ;; shadow->discrete_step++) {
1164
+ //%% hook_begin_step();
1165
+ int sync_retry = 0;
1166
+
1167
+ SWAP_VALUE(shadow->prev_awake, shadow->awake);
1168
+
1169
+ while (RARRAY(shadow->prev_awake)->len) {
1170
+ //%% hook_enter_guard_phase();
1171
+ check_guards(shadow, sync_retry);
1172
+ //%% hook_leave_guard_phase();
1173
+
1174
+ //%% hook_enter_sync_phase();
1175
+ do_sync_phase(shadow);
1176
+ //%% hook_leave_sync_phase();
1177
+ sync_retry = 1;
1178
+ }
1179
+
1180
+ if (!RARRAY(shadow->curr_T)->len) {
1181
+ //%% hook_end_step();
1182
+ break; //# out of main loop
1183
+ }
1184
+
1185
+ EACH_COMP_DO(shadow->curr_T) {
1186
+ //%% hook_begin_eval_events(comp);
1187
+ if (eval_events(comp_shdw, shadow))
1188
+ rb_ary_push(shadow->active_E, comp);
1189
+ //%% hook_end_eval_events(comp);
1190
+ }
1191
+
1192
+ //# Export new event values.
1193
+ EACH_COMP_DO(shadow->active_E) {
1194
+ SWAP_VALUE(comp_shdw->event_values, comp_shdw->next_event_values);
1195
+ //%% hook_export_events(comp, comp_shdw->event_values);
1196
+ }
1197
+ SWAP_VALUE(shadow->active_E, shadow->prev_active_E);
1198
+ assert(RARRAY(shadow->active_E)->len == 0);
1199
+
1200
+ //%% hook_enter_eval_phase();
1201
+ EACH_COMP_DO(shadow->curr_T) {
1202
+ //%% hook_begin_eval_resets(comp);
1203
+ if (eval_continuous_resets(comp_shdw, shadow))
1204
+ rb_ary_push(shadow->curr_CR, comp);
1205
+ eval_constant_resets(comp_shdw, shadow);
1206
+ eval_port_connects(comp_shdw, shadow);
1207
+ //%% hook_end_eval_resets(comp);
1208
+
1209
+ if (RTEST(cur_actions(comp_shdw)))
1210
+ rb_ary_push(shadow->curr_A, comp);
1211
+
1212
+ if (RTEST(cur_posts(comp_shdw)))
1213
+ rb_ary_push(shadow->curr_P, comp);
1214
+ }
1215
+ //%% hook_leave_eval_phase();
1216
+
1217
+ //%% hook_enter_action_phase();
1218
+ EACH_COMP_DO(shadow->curr_A) {
1219
+ do_actions(comp_shdw, cur_actions(comp_shdw), shadow);
1220
+ }
1221
+ RARRAY(shadow->curr_A)->len = 0;
1222
+ //%% hook_leave_action_phase();
1223
+
1224
+ //%% hook_begin_parallel_assign();
1225
+ did_reset = 0;
1226
+ EACH_COMP_DO(shadow->curr_CR) {
1227
+ did_reset = assign_new_cont_values(comp_shdw) || did_reset;
1228
+ }
1229
+ RARRAY(shadow->curr_CR)->len = 0;
1230
+ did_reset = assign_new_constant_values(shadow) || did_reset;
1231
+ did_reset = assign_new_links(shadow) || did_reset;
1232
+ did_reset = assign_new_ports(shadow) || did_reset;
1233
+ //%% hook_end_parallel_assign();
1234
+
1235
+ //%% hook_enter_post_phase();
1236
+ EACH_COMP_DO(shadow->curr_P) {
1237
+ do_actions(comp_shdw, cur_posts(comp_shdw), shadow);
1238
+ }
1239
+ RARRAY(shadow->curr_P)->len = 0;
1240
+ //%% hook_leave_post_phase();
1241
+
1242
+ EACH_COMP_DO(shadow->curr_T) {
1243
+ finish_trans(comp_shdw, shadow);
1244
+ }
1245
+
1246
+ if (did_reset || 1)
1247
+ shadow->d_tick++;
1248
+ //## replace "1" with "some comp entered new
1249
+ //## state with new alg. eqs"
1250
+
1251
+ EACH_COMP_DO(shadow->curr_T) {
1252
+ check_strict(comp_shdw);
1253
+ //## optimize: only keep comps with var->ck_strict on this list
1254
+ //## option to skip this check
1255
+ }
1256
+
1257
+ //# Check for zeno problem.
1258
+ if (shadow->zeno_limit >= 0) {
1259
+ shadow->zeno_counter++;
1260
+ if (shadow->zeno_counter > shadow->zeno_limit)
1261
+ rb_funcall(shadow->self, #{declare_symbol :step_zeno}, 0);
1262
+ }
1263
+
1264
+ EACH_COMP_DO(shadow->curr_T) {
1265
+ if (comp_shdw->state == ExitState)
1266
+ remove_comp(comp, shadow->curr_T, shadow);
1267
+ else
1268
+ move_comp(comp, shadow->curr_T, shadow->awake);
1269
+ }
1270
+ assert(RARRAY(shadow->curr_T)->len == 0);
1271
+ assert(RARRAY(shadow->prev_awake)->len == 0);
1272
+
1273
+ //# Clear event values.
1274
+ EACH_COMP_DO(shadow->prev_active_E) {
1275
+ rb_mem_clear(RARRAY(comp_shdw->event_values)->ptr,
1276
+ RARRAY(comp_shdw->event_values)->len);
1277
+ }
1278
+ RARRAY(shadow->prev_active_E)->len = 0;
1279
+
1280
+ //%% hook_end_step();
1281
+ }
1282
+
1283
+ move_all_comps(shadow->strict_sleep, shadow->awake);
1284
+
1285
+ //%% hook_end();
1286
+ shadow->discrete_step = 0;
1287
+ shadow->zeno_counter = 0;
1288
+ }
1289
+
1290
+ # only call this when all defs have been added
1291
+ def parent.to_s
1292
+ @cached_output ||= super
1293
+ end
1294
+ end
1295
+
1296
+ hook = /\bhook_\w+/
1297
+ world_classes = World.subclasses + [World]
1298
+ hooks = Hash.new {|h,cl| h[cl] = cl.instance_methods(true).grep(hook).sort}
1299
+ hooks[World.superclass] = nil
1300
+ known_hooks = nil
1301
+
1302
+ world_classes.each do |cl|
1303
+ cl_hooks = hooks[cl]
1304
+ next if hooks[cl.superclass] == hooks[cl]
1305
+
1306
+ cl.class_eval do
1307
+ shadow_library_source_file.include(Component.shadow_library_include_file)
1308
+
1309
+ if (instance_methods(false) + protected_instance_methods(false) +
1310
+ private_instance_methods(false)).include?("step_discrete")
1311
+ warn "Redefining step_discrete in #{self}"
1312
+ end
1313
+
1314
+ meth = define_c_method(:step_discrete, &discrete_step_definer)
1315
+ private :step_discrete
1316
+
1317
+ before_commit do
1318
+ # at this point, we know the file is complete
1319
+ file_str = meth.parent.to_s
1320
+
1321
+ known_hooks ||= file_str.scan(hook)
1322
+ unknown_hooks = cl_hooks - known_hooks
1323
+
1324
+ unless unknown_hooks.empty?
1325
+ warn "Unknown hooks:\n #{unknown_hooks.join("\n ")}"
1326
+ end
1327
+
1328
+ hook_pat =
1329
+ /\/\/%%[ \t]*(#{cl_hooks.join("|")})\(((?:.|\n[ \t]*\/\/%%)*)\)/
1330
+ file_str.gsub!(hook_pat) do
1331
+ hook = $1
1332
+ argstr = $2.gsub(/\/\/%%/, "")
1333
+ args = argstr.split(/,\s+/)
1334
+ args.each {|arg| arg.gsub(/\/\/.*$/, "")}
1335
+ # crude parser--no ", " within args, but may be multiline
1336
+ # and may have "//" comments, which can be used to extend an arg
1337
+ # across lines.
1338
+ args.unshift(args.size)
1339
+ ## enclose the following in if(shadow->hook) {...}
1340
+ %{rb_funcall(shadow->self, #{meth.declare_symbol hook},
1341
+ #{args.join(", ")})}
1342
+ end
1343
+ end
1344
+ end
1345
+ end
1346
+
1347
+ end # class World
1348
+
1349
+ ### move this to component-gen.rb
1350
+ class Component
1351
+ shadow_library_include_file.include(World.shadow_library_include_file)
1352
+ shadow_attr_reader :world => [World]
1353
+ ## :nonpersistent -- see __restore__world__refs
1354
+
1355
+ define_c_method :__set__world do
1356
+ ## possible to do this by shadow_attr_writer, and
1357
+ ## post-commit alias and remove_method and protected?
1358
+ arguments :world
1359
+ world_ssn = World.shadow_struct_name
1360
+ body %{
1361
+ if (world != Qnil) {
1362
+ Data_Get_Struct(world, #{world_ssn}, shadow->world);
1363
+ } else
1364
+ shadow->world = 0;
1365
+ }
1366
+ end
1367
+ protected :__set__world
1368
+ end
1369
+
1370
+ end # module RedShift