redshift 1.3.15
Sign up to get free protection for your applications and to get access to all the features.
- 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
|