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.
- data/.gitignore +8 -0
- data/README +5 -0
- data/RELEASE-NOTES +455 -0
- data/TODO +431 -0
- data/bench/alg-state.rb +61 -0
- data/bench/bench +26 -0
- data/bench/bench.rb +10 -0
- data/bench/continuous.rb +76 -0
- data/bench/diff-bench +86 -0
- data/bench/discrete.rb +101 -0
- data/bench/euler.rb +50 -0
- data/bench/formula.rb +78 -0
- data/bench/half-strict.rb +103 -0
- data/bench/inertness.rb +116 -0
- data/bench/queue.rb +92 -0
- data/bench/run +66 -0
- data/bench/simple.rb +74 -0
- data/bench/strictness.rb +86 -0
- data/examples/ball-tkar.rb +72 -0
- data/examples/ball.rb +123 -0
- data/examples/collide.rb +70 -0
- data/examples/connect-parallel.rb +48 -0
- data/examples/connect.rb +109 -0
- data/examples/constants.rb +27 -0
- data/examples/delay.rb +80 -0
- data/examples/derivative.rb +77 -0
- data/examples/euler.rb +46 -0
- data/examples/external-lib.rb +33 -0
- data/examples/guard-debugger.rb +77 -0
- data/examples/lotka-volterra.rb +33 -0
- data/examples/persist-ball.rb +68 -0
- data/examples/pid.rb +87 -0
- data/examples/ports.rb +60 -0
- data/examples/queue.rb +56 -0
- data/examples/queue2.rb +98 -0
- data/examples/reset-with-event-val.rb +28 -0
- data/examples/scheduler.rb +104 -0
- data/examples/set-dest.rb +23 -0
- data/examples/simulink/README +1 -0
- data/examples/simulink/delay.mdl +827 -0
- data/examples/simulink/derivative.mdl +655 -0
- data/examples/step-discrete-profiler.rb +103 -0
- data/examples/subsystem.rb +109 -0
- data/examples/sync-deadlock.rb +32 -0
- data/examples/sync-queue.rb +91 -0
- data/examples/sync-retry.rb +20 -0
- data/examples/sync.rb +51 -0
- data/examples/thermostat.rb +53 -0
- data/examples/zeno.rb +53 -0
- data/lib/accessible-index.rb +47 -0
- data/lib/redshift.rb +1 -0
- data/lib/redshift/component.rb +412 -0
- data/lib/redshift/meta.rb +183 -0
- data/lib/redshift/mixins/zeno-debugger.rb +69 -0
- data/lib/redshift/port.rb +57 -0
- data/lib/redshift/queue.rb +104 -0
- data/lib/redshift/redshift.rb +111 -0
- data/lib/redshift/state.rb +31 -0
- data/lib/redshift/syntax.rb +558 -0
- data/lib/redshift/target/c.rb +37 -0
- data/lib/redshift/target/c/component-gen.rb +1303 -0
- data/lib/redshift/target/c/flow-gen.rb +325 -0
- data/lib/redshift/target/c/flow/algebraic.rb +85 -0
- data/lib/redshift/target/c/flow/buffer.rb +74 -0
- data/lib/redshift/target/c/flow/delay.rb +203 -0
- data/lib/redshift/target/c/flow/derivative.rb +101 -0
- data/lib/redshift/target/c/flow/euler.rb +67 -0
- data/lib/redshift/target/c/flow/expr.rb +113 -0
- data/lib/redshift/target/c/flow/rk4.rb +80 -0
- data/lib/redshift/target/c/library.rb +85 -0
- data/lib/redshift/target/c/world-gen.rb +1370 -0
- data/lib/redshift/target/spec.rb +34 -0
- data/lib/redshift/world.rb +300 -0
- data/rakefile +37 -0
- data/test/test.rb +52 -0
- data/test/test_buffer.rb +58 -0
- data/test/test_connect.rb +242 -0
- data/test/test_connect_parallel.rb +47 -0
- data/test/test_connect_strict.rb +135 -0
- data/test/test_constant.rb +74 -0
- data/test/test_delay.rb +145 -0
- data/test/test_derivative.rb +48 -0
- data/test/test_discrete.rb +592 -0
- data/test/test_discrete_isolated.rb +92 -0
- data/test/test_exit.rb +59 -0
- data/test/test_flow.rb +200 -0
- data/test/test_flow_link.rb +288 -0
- data/test/test_flow_sub.rb +100 -0
- data/test/test_flow_trans.rb +292 -0
- data/test/test_inherit.rb +127 -0
- data/test/test_inherit_event.rb +74 -0
- data/test/test_inherit_flow.rb +139 -0
- data/test/test_inherit_link.rb +65 -0
- data/test/test_inherit_setup.rb +56 -0
- data/test/test_inherit_state.rb +66 -0
- data/test/test_inherit_transition.rb +168 -0
- data/test/test_numerics.rb +34 -0
- data/test/test_queue.rb +90 -0
- data/test/test_queue_alone.rb +115 -0
- data/test/test_reset.rb +209 -0
- data/test/test_setup.rb +119 -0
- data/test/test_strict_continuity.rb +410 -0
- data/test/test_strict_reset_error.rb +30 -0
- data/test/test_strictness_error.rb +32 -0
- data/test/test_sync.rb +185 -0
- data/test/test_world.rb +328 -0
- 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
|