redshift 1.3.15

Sign up to get free protection for your applications and to get access to all the features.
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,37 @@
1
+ # Process the selected libname or the name of the current program into
2
+ # an acceptable library name.
3
+ begin
4
+ clib_name = ($REDSHIFT_CLIB_NAME || $0).dup
5
+ ## should think of something better when $0=="irb"
6
+ clib_name =
7
+ if clib_name == "\000PWD" # irb in ruby 1.6.5 bug
8
+ "irb"
9
+ elsif clib_name == "-" and ENV["RUBY_SOURCE_FILE"]
10
+ File.basename(ENV["RUBY_SOURCE_FILE"])
11
+ # RUBY_SOURCE_FILE can be defined by your editor/ide cmd to call ruby
12
+ else
13
+ File.basename(clib_name)
14
+ end
15
+ clib_name.sub!(/\.rb$/, '')
16
+ clib_name.gsub!(/-/, '_')
17
+ clib_name.sub!(/^(?=\d)/, '_')
18
+ # other symbols will be caught in CGenerator::Library#initialize.
19
+ clib_name << "_clib_#{RUBY_VERSION.delete('.')}"
20
+ ## maybe name should be _ext instead of _clib?
21
+ $REDSHIFT_CLIB_NAME = clib_name
22
+ end
23
+
24
+ $REDSHIFT_WORK_DIR ||= "tmp"
25
+
26
+ if false ### $REDSHIFT_SKIP_BUILD
27
+ # useful for: turnkey; fast start if no changes; manual lib edits
28
+ f = File.join($REDSHIFT_WORK_DIR, $REDSHIFT_CLIB_NAME, $REDSHIFT_CLIB_NAME)
29
+ require f
30
+ else
31
+ require 'redshift/target/c/library'
32
+ require 'redshift/target/c/flow-gen'
33
+ require 'redshift/target/c/component-gen'
34
+ require 'redshift/target/c/world-gen'
35
+
36
+ RedShift.do_library_calls
37
+ end
@@ -0,0 +1,1303 @@
1
+ module RedShift
2
+ class World
3
+ include CShadow
4
+ # just so we can call shadow_struct_name on it
5
+ end
6
+
7
+ HAVE_DEFINE_METHOD = Module.private_instance_methods.include?("define_method")
8
+
9
+ class Component
10
+ include CShadow
11
+ shadow_library RedShift.library
12
+ shadow_library_file "Component"
13
+ library = RedShift.library
14
+
15
+ subclasses.each do |sub|
16
+ file_name = CGenerator.make_c_name(sub.name).to_s
17
+ sub.shadow_library_file file_name
18
+ end
19
+
20
+ def self.queue_sleepable_states
21
+ @queue_sleepable_states ||= begin
22
+ raise Library::CommitError unless committed?
23
+ h = {}
24
+ states.values.each do |st|
25
+ trs = all_transitions(st)
26
+ next if trs.empty? # inert
27
+ sleepable = trs.all? do |t, d|
28
+ t.guard && t.guard.any? do |g|
29
+ g.kind_of? Component::QMatch
30
+ end
31
+ end
32
+ h[st] = true if sleepable
33
+ end
34
+ h
35
+ end
36
+ end
37
+
38
+ after_commit do
39
+ # This is necessary because cont_state_class is lazy. ContState classes
40
+ # not defined at this point don't need to be committed.
41
+ subclasses.each {|cl| cl.cont_state_class}
42
+ end
43
+
44
+ library.declare_extern :typedefs => %{
45
+ typedef struct #{shadow_struct_name} ComponentShadow;
46
+ typedef void (*Flow)(ComponentShadow *); // evaluates one variable
47
+ typedef int (*Guard)(ComponentShadow *); // evaluates a guard expr
48
+ typedef double (*Expr)(ComponentShadow *);// evaluates a numerical expr
49
+ #ifdef WIN32
50
+ #pragma pack(push, 1)
51
+ #endif
52
+ typedef struct {
53
+ unsigned d_tick : 16; // last d_tick at which alg flow computed
54
+ unsigned rk_level : 3; // last rk level at which flow was computed
55
+ unsigned algebraic : 1; // should compute flow when inputs change?
56
+ unsigned strict : 1; // never changes discretely
57
+ unsigned ck_strict : 1; // should check strict at end of phase
58
+ unsigned reset : 1; // var is being reset
59
+ Flow flow; // cached flow function of current state
60
+ double value_0; // value during discrete step
61
+ double value_1; // value at steps of Runge-Kutta
62
+ double value_2;
63
+ double value_3;
64
+ } ContVar;
65
+ #ifdef WIN32
66
+ #pragma pack(pop)
67
+ #endif
68
+ }.tabto(0)
69
+
70
+ INPUT_NONE = 0
71
+ INPUT_CONT_VAR = 1
72
+ INPUT_CONST = 2
73
+ INPUT_INP_VAR = 3
74
+
75
+ Component.shadow_library_include_file.declare :InputVar => %{
76
+ #define INPUT_NONE #{INPUT_NONE}
77
+ #define INPUT_CONT_VAR #{INPUT_CONT_VAR}
78
+ #define INPUT_CONST #{INPUT_CONST}
79
+ #define INPUT_INP_VAR #{INPUT_INP_VAR}
80
+ }.tabto(0)
81
+
82
+ class FlowAttribute < CNativeAttribute
83
+ @pattern = /\A(Flow)\s+(\w+)\z/
84
+ end
85
+
86
+ class GuardAttribute < CNativeAttribute
87
+ @pattern = /\A(Guard)\s+(\w+)\z/
88
+ end
89
+
90
+ class ExprAttribute < CNativeAttribute
91
+ @pattern = /\A(Expr)\s+(\w+)\z/
92
+ end
93
+
94
+ class ContVarAttribute < CNativeAttribute
95
+ @pattern = /\A(ContVar)\s+(\w+)\z/
96
+
97
+ def initialize(*args)
98
+ super
99
+ # value_0 is the relevant state outside of continuous update
100
+ @dump = "rb_ary_push(result, rb_float_new(shadow->#{@cvar}.value_0))"
101
+ @load = "shadow->#{@cvar}.value_0 = NUM2DBL(rb_ary_shift(from_array))"
102
+ end
103
+ end
104
+
105
+ # Useful way to represent an object that has compile time (pre-commit) and
106
+ # run-time (post-commit) bevahior. The compile time behavior is in the
107
+ # class, and the run time behavior is in the unique instance.
108
+ class SingletonShadowClass
109
+ include Singleton
110
+ include CShadow; shadow_library Component
111
+ persistent false
112
+ def inspect; self.class.inspect_str; end
113
+ class << self; attr_reader :inspect_str; end
114
+ end
115
+
116
+ # FunctionWrappers wrap function pointers for access from ruby.
117
+ class FunctionWrapper < SingletonShadowClass
118
+ def initialize
119
+ calc_function_pointer
120
+ end
121
+
122
+ class << self
123
+ attr_accessor :constructor
124
+
125
+ def construct
126
+ class_eval &constructor
127
+ rescue => ex
128
+ ex.message << " While defining #{inspect_str}."
129
+ raise
130
+ end
131
+
132
+ def make_subclass(file_name, &bl)
133
+ cl = Class.new(self)
134
+ cl.constructor = bl
135
+ cl.shadow_library_file file_name
136
+ clname = file_name.sub /^#{@tag}/i, @tag
137
+ Object.const_set clname, cl ## maybe put in other namespace?
138
+ before_commit {cl.construct}
139
+ # this is deferred to commit time to resolve forward refs
140
+ ## this would be more elegant with defer.rb
141
+ ## maybe precommit should be used for this now?
142
+ cl
143
+ end
144
+ end
145
+ end
146
+
147
+ class FlowWrapper < FunctionWrapper
148
+ shadow_attr :flow => "Flow flow"
149
+ shadow_attr :algebraic => "int algebraic"
150
+ @tag = "Flow"
151
+ end
152
+
153
+ class GuardWrapper < FunctionWrapper
154
+ shadow_attr :guard => "Guard guard"
155
+ @tag = "Guard"
156
+
157
+ def self.strict; @strict; end
158
+ def strict; @strict ||= self.class.strict; end
159
+ end
160
+
161
+ class ExprWrapper < FunctionWrapper
162
+ shadow_attr :expr => "Expr expr"
163
+ @tag = "Expr"
164
+ end
165
+
166
+ # one per variable, shared by subclasses which inherit it
167
+ # not a run-time object, except for introspection
168
+ class ContVarDescriptor
169
+ attr_reader :name, :kind
170
+ def initialize name, index_delta, cont_state, kind
171
+ @name = name
172
+ @index_delta = index_delta
173
+ @cont_state = cont_state
174
+ @kind = kind
175
+ end
176
+ def strict?; @kind == :strict; end
177
+ def index
178
+ @cont_state.inherited_var_count + @index_delta
179
+ end
180
+ end
181
+
182
+ # One subclass per component subclass; one instance per component.
183
+ # Must have only ContVars in the shadow struct.
184
+ class ContState
185
+ include CShadow; shadow_library Component
186
+
187
+ ## maybe this should be in cgen as "shadow_aligned N"
188
+ if /mswin/i =~ RUBY_PLATFORM
189
+ shadow_struct.declare :begin_vars =>
190
+ "ContVar begin_vars[1]" ## wasted
191
+
192
+ Component.shadow_library_include_file.declare :first_cont_var => '
193
+ #define FIRST_CONT_VAR(shadow) ((shadow)->cont_state->begin_vars[1])
194
+ '
195
+ else
196
+ shadow_struct.declare :begin_vars =>
197
+ "struct {} begin_vars __attribute__ ((aligned (8)))"
198
+ # could conceivably have to be >8, or simply ((aligned)) on some
199
+ # platforms but this seems to work for x86 and sparc
200
+
201
+ Component.shadow_library_include_file.declare :first_cont_var => '
202
+ #define FIRST_CONT_VAR(shadow) ((shadow)->cont_state->begin_vars)
203
+ '
204
+ end
205
+
206
+ class_superhash :vars
207
+
208
+ def vars
209
+ self.class.vars
210
+ end
211
+
212
+ def var_at_index(idx)
213
+ self.class.var_at_index(idx)
214
+ end
215
+
216
+ def strict_var_indexes
217
+ self.class.strict_var_indexes
218
+ end
219
+
220
+ class << self
221
+ def make_subclass_for component_class
222
+ if component_class == Component
223
+ cl = ContState
224
+ else
225
+ sup = component_class.superclass.cont_state_class
226
+ cl = component_class.const_set("ContState", Class.new(sup))
227
+ end
228
+ cl.instance_eval do
229
+ @component_class = component_class
230
+ file_name =
231
+ component_class.shadow_library_source_file.name[/.*(?=\.c$)/] +
232
+ "_ContState" ## a bit hacky
233
+ shadow_library_file file_name
234
+ component_class.shadow_library_include_file.include(
235
+ shadow_library_include_file)
236
+ end
237
+ cl
238
+ end
239
+
240
+ # yields to block only if var was added
241
+ def add_var var_name, kind
242
+ var = vars[var_name]
243
+ if var
244
+ unless kind == :permissive or var.kind == kind
245
+ raise StrictnessError,
246
+ "\nVariable #{var_name} redefined with different strictness."
247
+ end
248
+ else
249
+ var = vars[var_name] =
250
+ ContVarDescriptor.new(var_name, vars.own.size, self, kind)
251
+ shadow_attr var_name => "ContVar #{var_name}"
252
+ yield if block_given?
253
+ end
254
+ var
255
+ end
256
+
257
+ def inherited_var_count
258
+ unless @inherited_var_count
259
+ raise Library::CommitError unless committed?
260
+ @inherited_var_count = superclass.vars.size
261
+ end
262
+ @inherited_var_count
263
+ end
264
+
265
+ def cumulative_var_count
266
+ unless @cumulative_var_count
267
+ raise Library::CommitError unless committed?
268
+ @cumulative_var_count = vars.size
269
+ if @cumulative_var_count > 32767
270
+ raise "overflow in cumulative_var_count: #{@cumulative_var_count}"
271
+ end
272
+ end
273
+ @cumulative_var_count
274
+ end
275
+
276
+ def var_at_index(idx)
277
+ @var_at_index ||= {}
278
+ @var_at_index[idx] ||= vars.values.find {|var| var.index == idx}
279
+ end
280
+
281
+ def strict_var_indexes
282
+ unless @strict_var_indexes
283
+ raise Library::CommitError unless committed?
284
+ @strict_var_indexes = vars.
285
+ select {|name, var| var.strict?}.
286
+ map {|name, var| var.index}
287
+ end
288
+ @strict_var_indexes
289
+ end
290
+ end
291
+ end
292
+
293
+ _load_data_method.post_code %{
294
+ rb_funcall(shadow->self, #{library.declare_symbol :restore}, 0);
295
+ }
296
+
297
+ shadow_attr_accessor :cont_state => [ContState]
298
+ protected :cont_state, :cont_state=
299
+
300
+ shadow_attr_accessor :state => State
301
+ protected :state=
302
+
303
+ shadow_attr_reader :nonpersistent, :outgoing => Array
304
+ shadow_attr_reader :nonpersistent, :trans => Transition
305
+
306
+ shadow_attr_accessor :nonpersistent, :dest => State
307
+ protected :dest=
308
+
309
+ # The values of each event currently being emitted, indexed by event ID.
310
+ # If not emitted, nil. We consider false to be an emitted value.
311
+ shadow_attr_accessor :nonpersistent, :event_values => Array
312
+ shadow_attr_accessor :nonpersistent, :next_event_values => Array
313
+ protected :event_values=, :next_event_values=
314
+
315
+ def active_transition; trans; end # for introspection
316
+
317
+ shadow_attr_accessor :var_count => "short var_count"
318
+ ## needn't be persistent
319
+ protected :var_count=
320
+
321
+ shadow_struct.declare :bits => %{\
322
+ unsigned strict : 1; //# is cur state strict?
323
+ unsigned checked : 1; //# have the guards been checked?
324
+ unsigned has_diff : 1; //# cur state has diff flows?
325
+ unsigned diff_list : 1; //# on diff_list of comps with diff flows?
326
+ unsigned sleepable : 1; //# can sleep in this state waiting for q?
327
+ unsigned unused : 11;
328
+ union {
329
+ struct {
330
+ unsigned idx : 16; //# index of next transition after sync failure
331
+ } trans;
332
+ } tmp;
333
+ }
334
+
335
+ class << self
336
+
337
+ # The flow hash contains flows contributed (not inherited) by this
338
+ # class. The flow table is the cumulative hash (by state) of arrays
339
+ # (by var) of flows.
340
+
341
+ def flow_hash
342
+ @flow_hash ||= {}
343
+ end
344
+
345
+ def add_flow h # [state, var] => flow_wrapper_subclass, ...
346
+ flow_hash.update h
347
+ end
348
+
349
+ def flow_table
350
+ unless @flow_table
351
+ assert committed?
352
+ ft = {}
353
+ if defined? superclass.flow_table
354
+ for k, v in superclass.flow_table
355
+ ft[k] = v.dup
356
+ end
357
+ end
358
+ for (state, var), flow_class in flow_hash
359
+ (ft[state] ||= [])[var.index] = flow_class.instance
360
+ end
361
+ @flow_table = ft
362
+ end
363
+ @flow_table
364
+ end
365
+
366
+ def var_count
367
+ @var_count ||= cont_state_class.cumulative_var_count
368
+ end
369
+
370
+ def cont_state_class
371
+ @cont_state_class ||= ContState.make_subclass_for(self)
372
+ end
373
+
374
+ # Add a scratch variable of type double to the component's
375
+ # shadow. Returns the var name. The scratch var is nonpersistent and
376
+ # not accessible. The scratch var may be read/written by whatever flow
377
+ # controls +var_name+; the value is preserved during the continuous step.
378
+ def scratch_for var_name
379
+ scratch_name = "#{var_name}_scratch"
380
+ shadow_attr :nonpersistent, scratch_name => "double #{scratch_name}"
381
+ scratch_name
382
+ end
383
+
384
+ def define_continuous(kind, var_names)
385
+ var_names.collect do |var_name|
386
+ var_name = var_name.intern if var_name.is_a? String
387
+
388
+ cont_state_class.add_var var_name, kind do
389
+ ssn = cont_state_class.shadow_struct.name
390
+ exc = shadow_library.declare_class(AlgebraicAssignmentError)
391
+ msg = "Cannot set #{var_name}; it is defined algebraically."
392
+
393
+ class_eval %{
394
+ define_c_method :#{var_name} do
395
+ declare :cont_state => "#{ssn} *cont_state"
396
+ declare :var => "ContVar *var"
397
+ body %{
398
+ cont_state = (#{ssn} *)shadow->cont_state;
399
+ var = &cont_state->#{var_name};
400
+ if (var->algebraic &&
401
+ (var->strict ? !var->d_tick :
402
+ var->d_tick != shadow->world->d_tick)) {
403
+ (*var->flow)((ComponentShadow *)shadow);
404
+ }
405
+ else {
406
+ if (shadow->world)
407
+ var->d_tick = shadow->world->d_tick;
408
+ }
409
+ }
410
+ # The d_tick assign above is because we now know that the
411
+ # current-ness of the value has been relied upon (possibly to
412
+ # go to strict sleep), and so the ck_strict flag must be set
413
+ # later, so strictness will get checked at the end of any
414
+ # transitions (but only if someone has relied on it).
415
+
416
+ returns "rb_float_new(cont_state->#{var_name}.value_0)"
417
+ end
418
+ }
419
+
420
+ ## the .strict ? check can be done statically (but cost is
421
+ ## recompile if strict decl changes).
422
+
423
+ if kind == :strict
424
+ exc2 = shadow_library.declare_class StrictnessError
425
+ msg2 = "Cannot reset strictly continuous #{var_name} in #{self}."
426
+ class_eval %{
427
+ define_c_method :#{var_name}= do
428
+ arguments :value
429
+ declare :cont_state => "#{ssn} *cont_state"
430
+ body %{
431
+ cont_state = (#{ssn} *)shadow->cont_state;
432
+ cont_state->#{var_name}.value_0 = NUM2DBL(value);
433
+ if (shadow->world)
434
+ shadow->world->d_tick++;
435
+ if (cont_state->#{var_name}.algebraic)
436
+ rs_raise(#{exc}, shadow->self, #{msg.inspect});
437
+ if (!NIL_P(shadow->state))
438
+ rs_raise(#{exc2}, shadow->self, #{msg2.inspect});
439
+ }
440
+ returns "value"
441
+ end
442
+ }
443
+
444
+ else
445
+ class_eval %{
446
+ define_c_method :#{var_name}= do
447
+ arguments :value
448
+ declare :cont_state => "#{ssn} *cont_state"
449
+ body %{
450
+ cont_state = (#{ssn} *)shadow->cont_state;
451
+ cont_state->#{var_name}.value_0 = NUM2DBL(value);
452
+ if (shadow->world)
453
+ shadow->world->d_tick++;
454
+ if (cont_state->#{var_name}.algebraic)
455
+ rs_raise(#{exc}, shadow->self, #{msg.inspect});
456
+ }
457
+ returns "value"
458
+ end
459
+ }
460
+ end
461
+ end
462
+ end
463
+ end
464
+
465
+ def define_constant(kind, var_names)
466
+ var_names.collect do |var_name|
467
+ var_name = var_name.intern if var_name.is_a? String
468
+ add_var_to_offset_table(var_name)
469
+
470
+ (r,w), = shadow_attr_accessor var_name => "double #{var_name}"
471
+ w.body "if (shadow->world) shadow->world->d_tick++"
472
+
473
+ if kind == :strict
474
+ exc = shadow_library.declare_class StrictnessError
475
+ msg = "Cannot reset strictly constant #{var_name} in #{self}."
476
+ w.body %{
477
+ if (!NIL_P(shadow->state))
478
+ rs_raise(#{exc}, shadow->self, #{msg.inspect});
479
+ }
480
+ end
481
+ end
482
+ end
483
+
484
+ def src_comp var_name
485
+ "#{var_name}_src_comp"
486
+ end
487
+
488
+ def src_type var_name
489
+ "#{var_name}_src_type"
490
+ end
491
+
492
+ def src_offset var_name
493
+ "#{var_name}_src_offset"
494
+ end
495
+
496
+ def input_target_struct
497
+ unless @input_target_struct
498
+ sf = shadow_library_source_file
499
+ st = @input_target_struct = sf.declare_extern_struct(:target)
500
+ st.declare :psh => "char *psh"
501
+ st.declare :type => "short type"
502
+ st.declare :offset => "short offset"
503
+ ## will this always have the same layout as the struct members
504
+ ## in ComponentShadow?
505
+
506
+ define_rs_eval_input_var
507
+ end
508
+ end
509
+
510
+ def define_input(kind, var_names)
511
+ Component.input_target_struct
512
+
513
+ var_names.collect do |var_name|
514
+ var_name = var_name.intern if var_name.is_a? String
515
+
516
+ src_comp = src_comp(var_name)
517
+ src_type = src_type(var_name)
518
+ src_offset = src_offset(var_name)
519
+
520
+ add_var_to_offset_table(src_comp)
521
+
522
+ ## readers only?
523
+ shadow_attr_accessor src_comp => [Component]
524
+ shadow_attr_accessor src_type => "short #{src_type}"
525
+ shadow_attr_accessor src_offset => "short #{src_offset}"
526
+
527
+ define_c_method(var_name) do
528
+ declare :result => "double result"
529
+ returns "rb_float_new(result)"
530
+
531
+ body %{
532
+ result = rs_eval_input_var(shadow, &shadow->#{src_comp});
533
+ }
534
+ end
535
+ end
536
+ end
537
+
538
+ def precommit
539
+ define_events
540
+ define_links
541
+ define_continuous_variables
542
+ define_constant_variables
543
+ define_input_variables
544
+
545
+ states.values.sort_by{|s|s.to_s}.each do |state|
546
+ define_flows(state)
547
+ define_transitions(state)
548
+ end
549
+
550
+ check_variables
551
+ end
552
+
553
+ def define_events
554
+ exported_events.own.each do |event, index|
555
+ define_method event do
556
+ event_values.at(index) ## is it worth doing this in C?
557
+ end ## or at least use eval instead of closure
558
+ end
559
+ end
560
+
561
+ def _offset_table
562
+ {}
563
+ end
564
+
565
+ def offset_table
566
+ @offset_table ||= _offset_table
567
+ end
568
+
569
+ def inv_offset_table
570
+ @inv_offset_table ||= offset_table.invert
571
+ end
572
+
573
+ # Note: for constant and link vars. Offset is in bytes.
574
+ def offset_of_var var_name
575
+ offset_table[var_name.to_sym] or
576
+ raise "#{var_name.inspect} is not a valid constant or link in #{self}"
577
+ end
578
+
579
+ # Note: for constant and link vars. Offset is in bytes.
580
+ def var_at_offset offset
581
+ inv_offset_table[offset] or
582
+ raise "#{offset} is not a constant or link offset in #{self}"
583
+ end
584
+
585
+ def offset_table_method
586
+ @offset_table_method ||= define_c_class_method :_offset_table do
587
+ declare :table => "VALUE table"
588
+ returns "table"
589
+ body %{
590
+ table = rb_call_super(0, 0);
591
+ }
592
+ end
593
+ end
594
+
595
+ def add_var_to_offset_table var_name
596
+ ssn = shadow_struct.name
597
+ offset_table_method.body %{\
598
+ rb_hash_aset(table, #{shadow_library.literal_symbol(var_name)},
599
+ INT2FIX((char *)&(((struct #{ssn} *)0)->#{var_name}) - (char *)0));
600
+ } ## can we just use offsetof() ?
601
+ end
602
+
603
+ def define_links
604
+ own_links = link_variables.own
605
+ return if own_links.empty?
606
+
607
+ ssn = shadow_struct.name
608
+
609
+ own_links.keys.sort_by{|k|k.to_s}.each do |var_name|
610
+ var_type, strictness = link_variables[var_name]
611
+
612
+ unless var_type.is_a? Class
613
+ var_type = var_type.to_s.split(/::/).inject(self) do |p, n|
614
+ p.const_get(n)
615
+ end
616
+ link_variables[var_name] = [var_type, strictness]
617
+ end
618
+
619
+ unless var_type <= Component
620
+ raise TypeError,
621
+ "Linked type must be a subclass of Component: #{var_name}"
622
+ end
623
+
624
+ (r,w), = shadow_attr_accessor(var_name => [var_type])
625
+ w.body "if (shadow->world) shadow->world->d_tick++"
626
+
627
+ if strictness == :strict
628
+ exc = shadow_library.declare_class StrictnessError
629
+ msg = "Cannot reset strict link #{var_name} in #{self}."
630
+ w.body %{
631
+ if (!NIL_P(shadow->state))
632
+ rs_raise(#{exc}, shadow->self, #{msg.inspect});
633
+ }
634
+ end
635
+
636
+ shadow_library_include_file.include(
637
+ var_type.shadow_library_include_file)
638
+
639
+ add_var_to_offset_table(var_name)
640
+ end
641
+ end
642
+
643
+ def check_variables
644
+ bad = constant_variables.keys & continuous_variables.keys
645
+ unless bad.empty?
646
+ raise ConstnessError,
647
+ "In class #{self}, the following variables are " +
648
+ "each defined as both constant and continuous: #{bad.join(", ")}."
649
+ end
650
+ end
651
+
652
+ def define_continuous_variables
653
+ continuous_variables.own.keys.sort_by{|k|k.to_s}.each do |var_name|
654
+ define_continuous(continuous_variables[var_name], [var_name])
655
+ end
656
+ end
657
+
658
+ def define_constant_variables
659
+ constant_variables.own.keys.sort_by{|k|k.to_s}.each do |var_name|
660
+ define_constant(constant_variables[var_name], [var_name])
661
+ end
662
+ end
663
+
664
+ def define_input_variables
665
+ input_variables.own.keys.sort_by{|k|k.to_s}.each do |var_name|
666
+ define_input(input_variables[var_name], [var_name])
667
+ end
668
+ end
669
+
670
+ def define_flows(state)
671
+ own_flows = flows(state).own
672
+ own_flows.keys.sort_by{|sym|sym.to_s}.each do |var|
673
+ flow = own_flows[var]
674
+
675
+ cont_var = cont_state_class.vars[var]
676
+ unless cont_var
677
+ attach_continuous_variables(:permissive, [var])
678
+ cont_var = define_continuous(:permissive, [var])[0]
679
+ end
680
+
681
+ add_flow([state, cont_var] => flow.flow_wrapper(self, state))
682
+
683
+ after_commit do
684
+ ## a pity to use after_commit, when "just_before_commit" would be ok
685
+ ## use the defer mechanism from teja2hsif
686
+ if not flow.strict and cont_var.strict?
687
+ raise StrictnessError,
688
+ "Strict variable '#{cont_var.name}' in #{self} " +
689
+ "redefined with non-strict flow.", []
690
+ end
691
+ end
692
+ end
693
+ end
694
+
695
+ def define_guard(expr)
696
+ @guard_wrapper_hash ||= {} ## could be a superhash?
697
+ @guard_wrapper_hash[expr] ||=
698
+ CexprGuard.new(expr).guard_wrapper(self)
699
+ end
700
+
701
+ def make_guard_method_name
702
+ @guard_ids ||= 0
703
+ @guard_ids += 1
704
+ "__guard_method_impl__#{@guard_ids}".intern
705
+ end
706
+
707
+ def define_guards(guards)
708
+ guards.map! do |g|
709
+ case g
710
+ when Symbol
711
+ # already saw this guard, as in: transition [S, T] => U
712
+ g
713
+
714
+ when QMatch
715
+ g
716
+
717
+ when Proc
718
+ if HAVE_DEFINE_METHOD
719
+ meth = Component.make_guard_method_name
720
+ class_eval do
721
+ define_method(meth, &g)
722
+ end
723
+ ## Currently, methods defined with define_method are a little
724
+ ## slower to invoke in ruby.
725
+ meth
726
+ else
727
+ g # a proc is slower than a method when called from step_discrete
728
+ end
729
+
730
+ when Class
731
+ if g < GuardWrapper
732
+ g
733
+ else
734
+ raise "What is #{g.inspect}?"
735
+ end
736
+
737
+ when String
738
+ define_guard(g)
739
+
740
+ else
741
+ raise "What is #{g.inspect}?"
742
+ end
743
+ end
744
+ end
745
+
746
+ def define_syncs syncs
747
+ after_commit do
748
+ syncs.each do |sync_phase_item|
749
+ sync_phase_item.link_offset = offset_of_var(sync_phase_item.link_name)
750
+ end
751
+ end
752
+ end
753
+
754
+ def define_connects connects
755
+ connects.each do |input_var, connect_spec|
756
+ unless input_variables.key? input_var
757
+ raise "Not an input variable: #{input_var}; in class #{self}"
758
+ end
759
+
760
+ case connect_spec
761
+ when Proc, NilClass
762
+ # nothing to do in this case
763
+ when Array
764
+ raise "unimplemented" ##
765
+ end
766
+ end
767
+ end
768
+
769
+ def define_reset(expr, type = "double")
770
+ @expr_wrapper_hash ||= {} ## could be a superhash?
771
+ @expr_wrapper_hash[expr] ||=
772
+ ResetExpr.new(expr, type).wrapper(self)
773
+ end
774
+
775
+ def define_resets(phase)
776
+ h = phase.value_map
777
+ h.keys.sort_by{|k|k.to_s}.each do |var|
778
+ expr = h[var]
779
+
780
+ case
781
+ when (cont_var = cont_state_class.vars[var])
782
+ define_reset_continuous(cont_var, expr, (phase[0]||=[]))
783
+ when constant_variables[var]
784
+ define_reset_constant(var, expr, (phase[1]||=[]))
785
+ when link_variables[var]
786
+ define_reset_link(var, expr, (phase[2]||=[]))
787
+ else
788
+ raise "No such variable, #{var}"
789
+ end
790
+ end
791
+ end
792
+
793
+ def define_reset_continuous cont_var, expr, phase
794
+ if cont_var.strict?
795
+ raise StrictnessError,
796
+ "Cannot reset strictly continuous '#{cont_var.name}' in #{self}.",
797
+ []
798
+ end
799
+
800
+ case expr
801
+ when String
802
+ reset = define_reset(expr)
803
+
804
+ after_commit do
805
+ phase[cont_var.index] = reset.instance
806
+ end
807
+
808
+ when Numeric
809
+ after_commit do
810
+ phase[cont_var.index] = expr.to_f
811
+ end
812
+
813
+ else
814
+ after_commit do
815
+ phase[cont_var.index] = expr
816
+ end
817
+ end
818
+ end
819
+
820
+ def define_reset_constant var, expr, phase
821
+ if constant_variables[var] == :strict
822
+ raise StrictnessError,
823
+ "Cannot reset strictly constant #{var} in #{self}.", []
824
+ end
825
+
826
+ case expr
827
+ when String
828
+ reset = define_reset(expr)
829
+
830
+ after_commit do
831
+ phase << [offset_of_var(var), reset.instance, var]
832
+ end
833
+
834
+ when Numeric
835
+ after_commit do
836
+ phase << [offset_of_var(var), expr.to_f, var]
837
+ end
838
+
839
+ else
840
+ after_commit do
841
+ phase << [offset_of_var(var), expr, var]
842
+ end
843
+ end
844
+ end
845
+
846
+ def define_reset_link var, expr, phase
847
+ type, strictness = link_variables[var]
848
+ if strictness == :strict
849
+ raise StrictnessError,
850
+ "Cannot reset strict link #{var} in #{self}.", []
851
+ end
852
+
853
+ case expr
854
+ when String
855
+ reset = define_reset(expr, "ComponentShadow *")
856
+
857
+ after_commit do
858
+ phase << [offset_of_var(var), reset.instance, var, type]
859
+ end
860
+
861
+ when Proc, NilClass
862
+ after_commit do
863
+ phase << [offset_of_var(var), expr, var, type]
864
+ end
865
+ end
866
+ end
867
+
868
+ def define_event_phase phase
869
+ phase.each do |item|
870
+ case item.value
871
+ when ExprEventValue
872
+ wrapper_mod = define_reset(item.value)
873
+ after_commit do
874
+ item.value = wrapper_mod.instance
875
+ end
876
+ end
877
+ end
878
+ end
879
+
880
+ def define_transitions(state)
881
+ # transitions don't usually have names, so the following (insertion
882
+ # order) is better than sorting by name.
883
+ own_transitions(state).each do |trans, dst|
884
+ define_guards(trans.guard) if trans.guard
885
+ define_syncs(trans.sync) if trans.sync
886
+ define_resets(trans.reset) if trans.reset
887
+ define_event_phase(trans.event) if trans.event
888
+ define_connects(trans.connect) if trans.connect
889
+ end
890
+ end
891
+
892
+ end
893
+
894
+ define_c_method :init_flags do
895
+ declare :locals => %{
896
+ ContVar *vars;
897
+ long var_count;
898
+ VALUE idx_ary;
899
+ VALUE *indexes;
900
+ long count;
901
+ long i;
902
+ long idx;
903
+ }
904
+ svi = declare_symbol :strict_var_indexes
905
+ ## really only need to call this once (per class)
906
+ body %{
907
+ shadow->has_diff = 0;
908
+ shadow->diff_list = 0;
909
+
910
+ vars = (ContVar *)&FIRST_CONT_VAR(shadow);
911
+ var_count = shadow->var_count;
912
+
913
+ idx_ary = rb_funcall(shadow->cont_state->self, #{svi}, 0);
914
+ Check_Type(idx_ary, T_ARRAY); //## debug only
915
+
916
+ count = RARRAY(idx_ary)->len;
917
+ indexes = RARRAY(idx_ary)->ptr;
918
+
919
+ for (i = 0; i < count; i++) {
920
+ idx = NUM2INT(indexes[i]);
921
+ if (idx > var_count-1)
922
+ rs_raise(#{declare_class IndexError}, shadow->self,
923
+ "Index into continuous variable list out of range: %d > %d.",
924
+ idx, var_count-1);
925
+ vars[idx].strict = 1;
926
+ }
927
+ }
928
+ end
929
+
930
+ ## optimizations:
931
+ ## class-level caching
932
+ ## define connect in C?
933
+ ## if need_connect...
934
+ def connect input_variable, other_component, other_var, bump_d_tick = true
935
+ src_comp = self.class.src_comp(input_variable)
936
+ src_type = self.class.src_type(input_variable)
937
+ src_offset = self.class.src_offset(input_variable)
938
+
939
+ ivs = self.class.input_variables
940
+ unless ivs.key? input_variable
941
+ raise TypeError, "Cannot connect non-input var: #{input_variable}."
942
+ end
943
+
944
+ strict = (ivs[input_variable] == :strict)
945
+ connected_comp = send(src_comp)
946
+ connected_var = source_variable_for(input_variable)
947
+
948
+ if strict and connected_comp and
949
+ (connected_comp != other_component or
950
+ connected_var.to_sym != other_var.to_sym)
951
+
952
+ if other_component
953
+ connected_val = connected_comp.send(connected_var)
954
+ other_val = other_component.send(other_var)
955
+
956
+ unless connected_val == other_val
957
+ raise StrictnessError,
958
+ "Cannot reconnect strict input: #{input_variable}."
959
+ end
960
+
961
+ else
962
+ raise StrictnessError,
963
+ "Cannot disconnect strict input: #{input_variable}."
964
+
965
+ end
966
+ end
967
+
968
+ if other_var and other_component
969
+ other_class = other_component.class
970
+
971
+ case
972
+ when other_class.continuous_variables.key?(other_var)
973
+ other_strict = other_class.continuous_variables[other_var] == :strict
974
+
975
+ type = INPUT_CONT_VAR
976
+ offset = other_class.cont_state_class.vars.each {|v, desc|
977
+ break desc.index if v.to_sym == other_var} ## table?
978
+
979
+ when other_class.constant_variables.key?(other_var)
980
+ other_strict = other_class.constant_variables[other_var] == :strict
981
+
982
+ type = INPUT_CONST
983
+ offset = other_class.offset_of_var(other_var)
984
+
985
+ when other_class.input_variables.key?(other_var)
986
+ other_strict = other_class.input_variables[other_var] == :strict
987
+
988
+ type = INPUT_INP_VAR
989
+ offset = other_class.offset_of_var(other_class.src_comp(other_var))
990
+
991
+ else
992
+ raise ArgumentError,
993
+ "No such variable, #{other_var}, in #{other_class}."
994
+ end
995
+
996
+ if strict and not other_strict
997
+ raise StrictnessError,
998
+ "Cannot connect strict input, #{input_variable}, to non-strict " +
999
+ "source: #{other_var} in #{other_component.class}."
1000
+ end
1001
+
1002
+ else
1003
+ type = INPUT_NONE
1004
+ offset = 0
1005
+ end
1006
+
1007
+ ## if this was in C, wouldn't need writer methods
1008
+ send("#{src_comp}=", other_component)
1009
+ send("#{src_type}=", type)
1010
+ send("#{src_offset}=", offset)
1011
+
1012
+ world.bump_d_tick if bump_d_tick and not strict
1013
+ end
1014
+
1015
+ def source_component_for(input_variable)
1016
+ send(self.class.src_comp(input_variable))
1017
+ end
1018
+
1019
+ def source_variable_for(input_variable)
1020
+ comp = send(self.class.src_comp(input_variable))
1021
+ type = send(self.class.src_type(input_variable))
1022
+ offset = send(self.class.src_offset(input_variable))
1023
+
1024
+ case type
1025
+ when INPUT_CONT_VAR
1026
+ comp.class.cont_state_class.var_at_index(offset).name
1027
+ when INPUT_CONST
1028
+ comp.class.var_at_offset(offset)
1029
+ when INPUT_INP_VAR
1030
+ src_comp_basename = self.class.src_comp("").to_s
1031
+ varname = comp.class.var_at_offset(offset).to_s
1032
+ varname.gsub(src_comp_basename, "").intern
1033
+ ## hacky?
1034
+ else
1035
+ nil
1036
+ end
1037
+ end
1038
+
1039
+ def get_varname_by_offset offset
1040
+ src_comp_basename = self.class.src_comp("").to_s
1041
+ varname = self.class.var_at_offset(offset).to_s
1042
+ varname.gsub!(src_comp_basename, "")
1043
+ ":#{varname}"
1044
+ ## hacky? like above...
1045
+ end
1046
+ ## end # if need connect
1047
+
1048
+ define_c_method :clear_ck_strict do
1049
+ declare :locals => %{
1050
+ ContVar *vars;
1051
+ long var_count;
1052
+ long i;
1053
+ }
1054
+ body %{
1055
+ vars = (ContVar *)&FIRST_CONT_VAR(shadow);
1056
+ var_count = shadow->var_count;
1057
+
1058
+ for (i = 0; i < var_count; i++) {
1059
+ vars[i].ck_strict = 0;
1060
+ }
1061
+ }
1062
+ end
1063
+
1064
+ # Called by World#step_discrete.
1065
+ # Only for certain kinds of strictness errors.
1066
+ def handle_strictness_error var_idx, new_val, old_val
1067
+ var_name = cont_state.var_at_index(var_idx).name
1068
+ raise StrictnessError,
1069
+ "Transition violated strictness: var #{var_name}:" +
1070
+ " new value #{new_val} != #{old_val} in #{self.inspect}"
1071
+ end
1072
+
1073
+ def self.cached_outgoing_transition_data s
1074
+ raise Library::CommitError if $REDSHIFT_DEBUG and not committed?
1075
+ @cached_outgoing_transition_data ||= {}
1076
+ @cached_outgoing_transition_data[s] ||= begin
1077
+ ary = outgoing_transition_data(s)
1078
+ if queue_sleepable_states[s]
1079
+ ary[-1] |= 0x02 ## yuck!
1080
+ end
1081
+ ary.freeze
1082
+ ary
1083
+ end
1084
+ end
1085
+
1086
+ def outgoing_transition_data
1087
+ self.class.cached_outgoing_transition_data state
1088
+ end
1089
+
1090
+ define_c_method :update_cache do body "rs_update_cache(shadow)" end
1091
+
1092
+ ## can this go in shadow_library_source_file instead of library?
1093
+ library.define(:rs_update_cache).instance_eval do
1094
+ flow_wrapper_type = Component::FlowWrapper.shadow_struct.name
1095
+ scope :extern ## might be better to keep static and put in world.c
1096
+ arguments "struct #{Component.shadow_struct.name} *shadow"
1097
+ declare :locals => %{
1098
+ #{flow_wrapper_type} *flow_wrapper;
1099
+
1100
+ VALUE flow_table; //# Hash
1101
+ VALUE flow_array; //# Array
1102
+ VALUE outgoing;
1103
+ long var_count;
1104
+ ContVar *vars;
1105
+ ContVar *var;
1106
+ long i;
1107
+ long count;
1108
+ VALUE *flows;
1109
+ int has_diff;
1110
+ struct RArray *ary;
1111
+ int flags;
1112
+ }.tabto(0)
1113
+
1114
+ body %{
1115
+ var_count = shadow->var_count;
1116
+ vars = (ContVar *)&FIRST_CONT_VAR(shadow);
1117
+
1118
+ //# Cache outgoing transitions.
1119
+ shadow->outgoing = rb_funcall(shadow->self,
1120
+ #{declare_symbol :outgoing_transition_data}, 0);
1121
+
1122
+ ary = RARRAY(shadow->outgoing);
1123
+ flags = FIX2INT(ary->ptr[ary->len-1]);
1124
+ shadow->strict = flags & 0x01;
1125
+ shadow->sleepable = !!(flags & 0x02);
1126
+
1127
+ //# Cache flows.
1128
+ flow_table = rb_funcall(rb_obj_class(shadow->self),
1129
+ #{declare_symbol :flow_table}, 0);
1130
+ //## could use after_commit to cache this method call
1131
+ flow_array = rb_hash_aref(flow_table, shadow->state);
1132
+ has_diff = 0;
1133
+
1134
+ if (flow_array != Qnil) {
1135
+ #{"Check_Type(flow_array, T_ARRAY);\n" if $REDSHIFT_DEBUG}
1136
+ count = RARRAY(flow_array)->len;
1137
+ flows = RARRAY(flow_array)->ptr;
1138
+
1139
+ if (count > var_count)
1140
+ rs_raise(#{declare_class IndexError}, shadow->self,
1141
+ "Index into continuous variable list out of range: %d > %d.",
1142
+ count, var_count);
1143
+
1144
+ for (i = 0; i < count; i++) {
1145
+ var = &vars[i];
1146
+ if (flows[i] != Qnil) {
1147
+ Data_Get_Struct(flows[i], #{flow_wrapper_type}, flow_wrapper);
1148
+ var->flow = flow_wrapper->flow;
1149
+ var->algebraic = flow_wrapper->algebraic;
1150
+ if (!var->algebraic)
1151
+ has_diff = 1;
1152
+ if (var->flow && var->algebraic && var->strict &&
1153
+ var->d_tick > 0) { //# did anyone rely on the strictness?
1154
+ var->value_1 = var->value_0;
1155
+ var->ck_strict = 1;
1156
+ }
1157
+ else {
1158
+ var->ck_strict = 0;
1159
+ }
1160
+ }
1161
+ else {
1162
+ var->flow = 0;
1163
+ var->algebraic = 0;
1164
+ var->d_tick = 0;
1165
+ }
1166
+ }
1167
+
1168
+ for (; i < var_count; i++) {
1169
+ var = &vars[i];
1170
+ var->flow = 0;
1171
+ var->algebraic = 0;
1172
+ var->d_tick = 0;
1173
+ }
1174
+ }
1175
+ else {
1176
+ for (i = 0; i < var_count; i++) {
1177
+ var = &vars[i];
1178
+ var->flow = 0;
1179
+ var->algebraic = 0;
1180
+ var->d_tick = 0;
1181
+ }
1182
+ }
1183
+
1184
+ if (has_diff && !shadow->has_diff) {
1185
+ if (!shadow->diff_list) {
1186
+ if (shadow->world->diff_list) { //# 0 if loading
1187
+ rb_ary_push(shadow->world->diff_list, shadow->self);
1188
+ }
1189
+ shadow->diff_list = 1;
1190
+ }
1191
+ shadow->has_diff = 1;
1192
+ }
1193
+ else if (!has_diff && shadow->has_diff) {
1194
+ //# defer taking comp off the diff_list
1195
+ shadow->has_diff = 0;
1196
+ }
1197
+ }
1198
+ end
1199
+ ## optimization:
1200
+ ## if (var->flow != old_flow && ...)
1201
+
1202
+ shadow_library_source_file.include "<stdarg.h>"
1203
+ shadow_library_source_file.define(:rs_raise).instance_eval do
1204
+ scope :extern
1205
+ arguments "VALUE exc, VALUE obj, const char *fmt, ..."
1206
+ return_type "void"
1207
+ body %{\
1208
+ va_list args;
1209
+ char buf[BUFSIZ];
1210
+ VALUE ex, ary;
1211
+
1212
+ va_start(args, fmt);
1213
+ vsnprintf(buf, BUFSIZ, fmt, args);
1214
+ va_end(args);
1215
+
1216
+ if (rb_mod_include_p(exc, #{declare_module(AugmentedException)})) {
1217
+ ary = rb_ary_new3(2, rb_str_new2(buf), obj);
1218
+ ex = rb_funcall(exc, #{declare_symbol :new}, 1, ary);
1219
+ rb_exc_raise(ex);
1220
+ }
1221
+ else {
1222
+ rb_exc_raise(rb_exc_new2(exc, buf));
1223
+ }\
1224
+ }
1225
+ ## In the else case, can we put obj somewhere else? Warning?
1226
+ end
1227
+
1228
+ def self.define_rs_eval_input_var
1229
+ shadow_library_source_file.define(:rs_eval_input_var).instance_eval do
1230
+ scope :extern
1231
+ arguments "ComponentShadow *shadow, char *psrc_comp"
1232
+ return_type "double"
1233
+ returns "result"
1234
+
1235
+ exc_uncn = declare_class(UnconnectedInputError)
1236
+ exc_circ = declare_class(CircularDefinitionError)
1237
+
1238
+ msg_uncn = "Input %s is not connected."
1239
+ msg_circ = "Circularity in input variable %s."
1240
+
1241
+ body %{\
1242
+ double result;
1243
+ struct target *tgt = (struct target *)(psrc_comp);
1244
+ ComponentShadow *sh;
1245
+ int depth = 0;
1246
+
1247
+ loop:
1248
+ if (!tgt->psh || tgt->type == INPUT_NONE) {
1249
+ size_t offset = psrc_comp - (char *)shadow;
1250
+ VALUE str = rb_funcall(shadow->self,
1251
+ #{declare_symbol :get_varname_by_offset}, 1, INT2FIX(offset));
1252
+ char *var_name = StringValuePtr(str);
1253
+ rs_raise(#{exc_uncn}, shadow->self, #{msg_uncn.inspect}, var_name);
1254
+ }
1255
+
1256
+ sh = (ComponentShadow *)tgt->psh;
1257
+
1258
+ switch(tgt->type) {
1259
+ case INPUT_CONT_VAR: {
1260
+ ContVar *var = (ContVar *)&FIRST_CONT_VAR(sh);
1261
+ var += tgt->offset;
1262
+
1263
+ if (var->algebraic) {
1264
+ if (var->rk_level < sh->world->rk_level ||
1265
+ (sh->world->rk_level == 0 &&
1266
+ (var->strict ? !var->d_tick :
1267
+ var->d_tick != sh->world->d_tick) //## !world ?
1268
+ ))
1269
+ (*var->flow)(sh);
1270
+ }
1271
+ else {
1272
+ if (sh->world)
1273
+ var->d_tick = sh->world->d_tick;
1274
+ }
1275
+
1276
+ result = (&var->value_0)[sh->world->rk_level];
1277
+ break;
1278
+ }
1279
+
1280
+ case INPUT_CONST:
1281
+ result = *(double *)(tgt->psh + tgt->offset);
1282
+ break;
1283
+
1284
+ case INPUT_INP_VAR:
1285
+ tgt = (struct target *)(tgt->psh + tgt->offset);
1286
+ if (depth++ > 100) {
1287
+ size_t offset = psrc_comp - (char *)shadow;
1288
+ VALUE str = rb_funcall(shadow->self,
1289
+ #{declare_symbol :get_varname_by_offset}, 1, INT2FIX(offset));
1290
+ char *var_name = StringValuePtr(str);
1291
+ rs_raise(#{exc_circ}, shadow->self,
1292
+ #{msg_circ.inspect}, var_name);
1293
+ }
1294
+ goto loop;
1295
+
1296
+ default:
1297
+ assert(0);
1298
+ }
1299
+ }
1300
+ end
1301
+ end
1302
+ end
1303
+ end