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,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