activefacts 0.7.3 → 0.8.5
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +19 -0
- data/Manifest.txt +24 -2
- data/Rakefile +25 -3
- data/bin/afgen +1 -1
- data/bin/cql +13 -2
- data/css/offline.css +3 -0
- data/css/orm2.css +24 -0
- data/css/print.css +8 -0
- data/css/style-print.css +357 -0
- data/css/style.css +387 -0
- data/download.html +85 -0
- data/examples/CQL/Address.cql +3 -3
- data/examples/CQL/Blog.cql +13 -14
- data/examples/CQL/CompanyDirectorEmployee.cql +4 -4
- data/examples/CQL/Death.cql +3 -2
- data/examples/CQL/Genealogy.cql +13 -11
- data/examples/CQL/Marriage.cql +2 -2
- data/examples/CQL/Metamodel.cql +136 -93
- data/examples/CQL/MultiInheritance.cql +2 -2
- data/examples/CQL/OilSupply.cql +14 -10
- data/examples/CQL/Orienteering.cql +22 -19
- data/examples/CQL/PersonPlaysGame.cql +3 -2
- data/examples/CQL/SchoolActivities.cql +4 -2
- data/examples/CQL/SimplestUnary.cql +1 -1
- data/examples/CQL/SubtypePI.cql +6 -7
- data/examples/CQL/Warehousing.cql +16 -19
- data/examples/CQL/unit.cql +584 -0
- data/examples/index.html +276 -0
- data/examples/intro.html +497 -0
- data/examples/local.css +20 -0
- data/index.html +96 -0
- data/lib/activefacts/api/concept.rb +48 -46
- data/lib/activefacts/api/constellation.rb +43 -23
- data/lib/activefacts/api/entity.rb +2 -2
- data/lib/activefacts/api/instance.rb +6 -2
- data/lib/activefacts/api/instance_index.rb +5 -0
- data/lib/activefacts/api/value.rb +8 -2
- data/lib/activefacts/api/vocabulary.rb +15 -10
- data/lib/activefacts/cql/CQLParser.treetop +109 -88
- data/lib/activefacts/cql/Concepts.treetop +32 -10
- data/lib/activefacts/cql/Context.treetop +34 -0
- data/lib/activefacts/cql/Expressions.treetop +9 -9
- data/lib/activefacts/cql/FactTypes.treetop +30 -31
- data/lib/activefacts/cql/Language/English.treetop +50 -0
- data/lib/activefacts/cql/LexicalRules.treetop +2 -1
- data/lib/activefacts/cql/Terms.treetop +117 -0
- data/lib/activefacts/cql/ValueTypes.treetop +152 -0
- data/lib/activefacts/cql/compiler.rb +1718 -0
- data/lib/activefacts/cql/parser.rb +124 -57
- data/lib/activefacts/generate/absorption.rb +1 -1
- data/lib/activefacts/generate/cql.rb +111 -100
- data/lib/activefacts/generate/cql/html.rb +5 -5
- data/lib/activefacts/generate/oo.rb +3 -3
- data/lib/activefacts/generate/ordered.rb +51 -19
- data/lib/activefacts/generate/ruby.rb +10 -8
- data/lib/activefacts/generate/sql/mysql.rb +14 -10
- data/lib/activefacts/generate/sql/server.rb +29 -24
- data/lib/activefacts/input/cql.rb +9 -1264
- data/lib/activefacts/input/orm.rb +213 -200
- data/lib/activefacts/persistence/columns.rb +11 -10
- data/lib/activefacts/persistence/index.rb +15 -18
- data/lib/activefacts/persistence/reference.rb +17 -17
- data/lib/activefacts/persistence/tables.rb +50 -51
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +79 -8
- data/lib/activefacts/vocabulary/metamodel.rb +183 -114
- data/spec/absorption_ruby_spec.rb +99 -0
- data/spec/absorption_spec.rb +3 -4
- data/spec/api/constellation.rb +1 -1
- data/spec/api/entity_type.rb +3 -1
- data/spec/api/instance.rb +4 -2
- data/spec/api/roles.rb +8 -6
- data/spec/api_spec.rb +1 -2
- data/spec/cql/context_spec.rb +71 -0
- data/spec/cql/samples_spec.rb +154 -0
- data/spec/cql/unit_spec.rb +375 -0
- data/spec/cql_cql_spec.rb +31 -21
- data/spec/cql_mysql_spec.rb +70 -0
- data/spec/cql_parse_spec.rb +15 -9
- data/spec/cql_ruby_spec.rb +27 -13
- data/spec/cql_sql_spec.rb +42 -16
- data/spec/cql_symbol_tables_spec.rb +2 -3
- data/spec/cqldump_spec.rb +7 -7
- data/spec/helpers/file_matcher.rb +39 -0
- data/spec/norma_cql_spec.rb +20 -12
- data/spec/norma_ruby_spec.rb +6 -3
- data/spec/norma_sql_spec.rb +6 -3
- data/spec/norma_tables_spec.rb +6 -4
- data/spec/spec_helper.rb +27 -8
- data/status.html +69 -0
- data/why.html +60 -0
- metadata +34 -11
- data/lib/activefacts/cql/DataTypes.treetop +0 -81
- data/spec/cql_unit_spec.rb +0 -330
@@ -11,80 +11,147 @@ require 'treetop'
|
|
11
11
|
require 'activefacts/cql/LexicalRules'
|
12
12
|
require 'activefacts/cql/Language/English'
|
13
13
|
require 'activefacts/cql/Expressions'
|
14
|
+
require 'activefacts/cql/Terms'
|
14
15
|
require 'activefacts/cql/Concepts'
|
15
|
-
require 'activefacts/cql/
|
16
|
+
require 'activefacts/cql/ValueTypes'
|
16
17
|
require 'activefacts/cql/FactTypes'
|
18
|
+
require 'activefacts/cql/Context'
|
17
19
|
require 'activefacts/cql/CQLParser'
|
18
20
|
|
19
21
|
module ActiveFacts
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
22
|
+
module CQL
|
23
|
+
# Extend the generated parser:
|
24
|
+
class Parser < CQLParser
|
25
|
+
include ActiveFacts
|
26
|
+
|
27
|
+
class BlackHole
|
28
|
+
def method_missing(m, *p, &b)
|
29
|
+
self # Make all calls vanish
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class InputProxy < Object
|
34
|
+
attr_reader :context
|
35
|
+
|
36
|
+
def initialize(input, context)
|
37
|
+
@input = input
|
38
|
+
@context = context
|
39
|
+
end
|
40
|
+
|
41
|
+
def length
|
42
|
+
@input.length
|
43
|
+
end
|
44
|
+
|
45
|
+
def size
|
46
|
+
length
|
47
|
+
end
|
48
|
+
|
49
|
+
def [](*a)
|
50
|
+
@input[*a]
|
51
|
+
end
|
52
|
+
|
53
|
+
def index(*a)
|
54
|
+
@input.index(*a)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def context
|
59
|
+
@context ||= BlackHole.new
|
60
|
+
end
|
40
61
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
62
|
+
def parse(input, options = {})
|
63
|
+
input = InputProxy.new(input, context) unless input.respond_to?(:context)
|
64
|
+
super(input, options)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Repeatedly parse rule_name until all input is consumed,
|
68
|
+
# returning an array of syntax trees for each definition.
|
69
|
+
def parse_all(input, rule_name = nil, &block)
|
70
|
+
self.root = rule_name if rule_name
|
71
|
+
|
72
|
+
@index = 0 # Byte offset to start next parse
|
73
|
+
self.consume_all_input = false
|
74
|
+
results = []
|
75
|
+
begin
|
76
|
+
node = parse(InputProxy.new(input, context), :index => @index)
|
77
|
+
return nil unless node
|
78
|
+
node = block.call(node) if block
|
79
|
+
results << node if node
|
80
|
+
end until self.index == @input_length
|
81
|
+
results
|
82
|
+
end
|
83
|
+
|
84
|
+
def definition(node)
|
85
|
+
name, ast = *node.value
|
86
|
+
kind, *value = *ast
|
87
|
+
|
88
|
+
begin
|
89
|
+
debug "CQL: Processing definition #{[kind, name].compact*" "}" do
|
90
|
+
case kind
|
91
|
+
when :vocabulary
|
92
|
+
[kind, name]
|
93
|
+
when :value_type
|
94
|
+
value_type_ast(name, value)
|
95
|
+
when :entity_type
|
96
|
+
supertypes = value.shift
|
97
|
+
entity_type_ast(name, supertypes, value)
|
98
|
+
when :fact_type
|
99
|
+
f = fact_type_ast(name, value)
|
100
|
+
when :unit
|
101
|
+
ast
|
102
|
+
when :constraint
|
103
|
+
ast
|
104
|
+
else
|
105
|
+
raise "CQL: internal error, unknown definition kind"
|
106
|
+
end
|
59
107
|
end
|
60
108
|
end
|
109
|
+
rescue => e
|
110
|
+
raise "in #{kind.to_s.camelcase(true)} definition, #{e.message}:\n\t#{node.text_value}" +
|
111
|
+
(ENV['DEBUG'] =~ /\bexception\b/ ? "\nfrom\t"+e.backtrace*"\n\t" : "")
|
61
112
|
end
|
62
|
-
rescue => e
|
63
|
-
raise "in #{kind.to_s.camelcase(true)} definition, #{e.message}:\n\t#{node.text_value}"
|
64
|
-
end
|
65
113
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
114
|
+
def value_type_ast(name, value)
|
115
|
+
# REVISIT: Massage/check value type here?
|
116
|
+
[:value_type, name, *value]
|
117
|
+
end
|
70
118
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
119
|
+
def entity_type_ast(name, supertypes, value)
|
120
|
+
#print "entity_type parameters for #{name}: "; p value
|
121
|
+
identification, mapping_pragmas, clauses = *value
|
122
|
+
clauses ||= []
|
75
123
|
|
76
|
-
|
124
|
+
# raise "Entity type clauses must all be fact types" if clauses.detect{|c| c[0] != :fact_clause }
|
77
125
|
|
78
|
-
|
79
|
-
|
126
|
+
[:entity_type, name, supertypes, identification, mapping_pragmas, clauses]
|
127
|
+
end
|
128
|
+
|
129
|
+
def fact_type_ast(name, value)
|
130
|
+
clauses, conditions = value
|
131
|
+
|
132
|
+
if conditions.empty? && includes_literals(clauses)
|
133
|
+
[:fact, nil, clauses]
|
134
|
+
elsif clauses.size == 1 &&
|
135
|
+
(popname = clauses[0][2]).size == 1 &&
|
136
|
+
popname[0].keys == [:word] &&
|
137
|
+
includes_literals(conditions)
|
138
|
+
[:fact, popname[0][:word], conditions]
|
139
|
+
else
|
140
|
+
[:fact_type, name, clauses, conditions]
|
141
|
+
end
|
142
|
+
end
|
80
143
|
|
81
|
-
|
82
|
-
|
144
|
+
def includes_literals(clauses)
|
145
|
+
clauses.detect do |clause|
|
146
|
+
raise "alternate clauses are not yet supported" if clause[0] == :"||"
|
147
|
+
fc, qualifiers, phrases, context_note = *clause
|
148
|
+
phrases.detect{|w| w[:literal]}
|
149
|
+
end
|
150
|
+
end
|
83
151
|
|
84
|
-
[:fact_type, name, defined_readings, clauses]
|
85
152
|
end
|
86
153
|
|
87
154
|
end
|
88
155
|
|
89
|
-
Polyglot.register('cql',
|
156
|
+
Polyglot.register('cql', CQL::Parser)
|
90
157
|
end
|
@@ -31,33 +31,22 @@ module ActiveFacts
|
|
31
31
|
|
32
32
|
def value_type_dump(o)
|
33
33
|
return unless o.supertype # An imported type
|
34
|
+
|
35
|
+
# REVISIT: A ValueType that is only used as a reference mode need not be emitted here. We haven't detected this situation yet however...
|
36
|
+
|
34
37
|
if o.name == o.supertype.name
|
35
|
-
# In ActiveFacts, parameterising a ValueType will create a new
|
36
|
-
# throw Can't handle parameterized value type of same name as its
|
38
|
+
# In ActiveFacts, parameterising a ValueType will create a new ValueType
|
39
|
+
# throw Can't handle parameterized value type of same name as its ValueType" if ...
|
37
40
|
end
|
38
41
|
|
39
42
|
parameters =
|
40
43
|
[ o.length != 0 || o.scale != 0 ? o.length : nil,
|
41
44
|
o.scale != 0 ? o.scale : nil
|
42
45
|
].compact
|
43
|
-
parameters = parameters.length > 0 ? "("+parameters.join(",")+")" : "
|
44
|
-
|
45
|
-
#" restricted to {#{(allowed_values.map{|r| r.inspect}*", ").gsub('"',"'")}}")
|
46
|
+
parameters = parameters.length > 0 ? "("+parameters.join(",")+")" : ""
|
46
47
|
|
47
48
|
puts "#{o.name} is written as #{o.supertype.name}#{ parameters }#{
|
48
|
-
o.value_restriction
|
49
|
-
o.value_restriction.all_allowed_range.sort_by{|ar|
|
50
|
-
((min = ar.value_range.minimum_bound) && min.value) ||
|
51
|
-
((max = ar.value_range.maximum_bound) && max.value)
|
52
|
-
}.map{|ar|
|
53
|
-
# REVISIT: Need to display as string or numeric according to type here...
|
54
|
-
min = ar.value_range.minimum_bound
|
55
|
-
max = ar.value_range.maximum_bound
|
56
|
-
|
57
|
-
(min ? min.value : "") +
|
58
|
-
(min.value != (max&&max.value) ? (".." + (max ? max.value : "")) : "")
|
59
|
-
}*", "
|
60
|
-
}}" : ""
|
49
|
+
o.value_restriction && " "+o.value_restriction.describe
|
61
50
|
};"
|
62
51
|
end
|
63
52
|
|
@@ -65,6 +54,18 @@ module ActiveFacts
|
|
65
54
|
reading << " [#{(ring.ring_type.scan(/[A-Z][a-z]*/)*", ").downcase}]"
|
66
55
|
end
|
67
56
|
|
57
|
+
def mapping_pragma(entity_type)
|
58
|
+
ti = entity_type.all_type_inheritance_as_subtype
|
59
|
+
assimilation = ti.map{|t| t.assimilation }.compact[0]
|
60
|
+
return "" unless entity_type.is_independent || assimilation
|
61
|
+
" [" +
|
62
|
+
[
|
63
|
+
entity_type.is_independent ? "independent" : nil,
|
64
|
+
assimilation || nil
|
65
|
+
].compact*", " +
|
66
|
+
"]"
|
67
|
+
end
|
68
|
+
|
68
69
|
def identified_by_roles_and_facts(entity_type, identifying_roles, identifying_facts, preferred_readings)
|
69
70
|
identifying_role_names = identifying_roles.map{|role|
|
70
71
|
preferred_role_ref = preferred_readings[role.fact_type].role_sequence.all_role_ref.detect{|reading_rr|
|
@@ -94,13 +95,16 @@ module ActiveFacts
|
|
94
95
|
# Just beware that readings having the same players will be considered to be of the same fact type, even if they're not.
|
95
96
|
|
96
97
|
# Detect standard reference-mode scenarios
|
97
|
-
|
98
|
+
external_identifying_facts = identifying_facts - [entity_type.fact_type]
|
99
|
+
ft = external_identifying_facts[0]
|
98
100
|
fact_constraints = nil
|
99
|
-
ftr = ft.all_role.sort_by{|role| role.ordinal}
|
100
|
-
if
|
101
|
+
ftr = ft && ft.all_role.sort_by{|role| role.ordinal}
|
102
|
+
if external_identifying_facts.size == 1 and
|
101
103
|
entity_role = ftr[n = (ftr[0].concept == entity_type ? 0 : 1)] and
|
102
104
|
value_role = ftr[1-n] and
|
103
|
-
|
105
|
+
value_player = value_role.concept and
|
106
|
+
value_player.is_a?(ActiveFacts::Metamodel::ValueType) and
|
107
|
+
value_name = value_player.name and
|
104
108
|
residual = value_name.gsub(%r{#{entity_role.concept.name}},'') and
|
105
109
|
residual != '' and
|
106
110
|
residual != value_name
|
@@ -111,13 +115,13 @@ module ActiveFacts
|
|
111
115
|
# Detect standard reference-mode readings:
|
112
116
|
forward_reading = reverse_reading = nil
|
113
117
|
ft.all_reading.each do |reading|
|
114
|
-
if reading.
|
118
|
+
if reading.text =~ /^\{(\d)\} has \{\d\}$/
|
115
119
|
if reading.role_sequence.all_role_ref.detect{|rr| rr.ordinal == $1.to_i}.role == entity_role
|
116
120
|
forward_reading = reading
|
117
121
|
else
|
118
122
|
reverse_reading = reading
|
119
123
|
end
|
120
|
-
elsif reading.
|
124
|
+
elsif reading.text =~ /^\{(\d)\} is of \{\d\}$/
|
121
125
|
if reading.role_sequence.all_role_ref.detect{|rr| rr.ordinal == $1.to_i}.role == value_role
|
122
126
|
reverse_reading = reading
|
123
127
|
else
|
@@ -141,6 +145,7 @@ module ActiveFacts
|
|
141
145
|
@constraints_used[pc] = true
|
142
146
|
end
|
143
147
|
end
|
148
|
+
fact_constraints += Array(@presence_constraints_by_fact[entity_type.fact_type])
|
144
149
|
|
145
150
|
@fact_types_dumped[ft] = true
|
146
151
|
|
@@ -148,10 +153,19 @@ module ActiveFacts
|
|
148
153
|
other_readings = ft.all_reading - [forward_reading] - [reverse_reading]
|
149
154
|
debug :mode, "--- other_readings.size now = #{other_readings.size}" if other_readings.size > 0
|
150
155
|
|
151
|
-
fact_text =
|
152
|
-
|
153
|
-
|
154
|
-
|
156
|
+
fact_text = (
|
157
|
+
other_readings.map do |reading|
|
158
|
+
expanded_reading(reading, fact_constraints, true)
|
159
|
+
end +
|
160
|
+
(entity_type.fact_type ?
|
161
|
+
fact_readings_with_constraints(entity_type.fact_type, fact_constraints) : []
|
162
|
+
)
|
163
|
+
)*",\n\t"
|
164
|
+
|
165
|
+
restriction = value_role.role_value_restriction || value_player.value_restriction
|
166
|
+
# REVISIT: If both restrictions apply and differ, we can't use a reference mode
|
167
|
+
restriction_text = restriction ? " "+restriction.describe : ""
|
168
|
+
return " identified by its #{residual}#{restriction_text}#{mapping_pragma(entity_type)}" +
|
155
169
|
(fact_text != "" ? " where\n\t" + fact_text : "")
|
156
170
|
end
|
157
171
|
end
|
@@ -163,6 +177,7 @@ module ActiveFacts
|
|
163
177
|
}.flatten*",\n\t"
|
164
178
|
|
165
179
|
" identified by #{ identifying_role_names*" and " }" +
|
180
|
+
mapping_pragma(entity_type) +
|
166
181
|
" where\n\t"+@identifying_fact_text
|
167
182
|
end
|
168
183
|
|
@@ -183,7 +198,10 @@ module ActiveFacts
|
|
183
198
|
print "#{o.name} is a kind of #{ o.supertypes.map(&:name)*", " }"
|
184
199
|
if pi
|
185
200
|
print identified_by(o, pi)
|
201
|
+
else
|
202
|
+
print mapping_pragma(o)
|
186
203
|
end
|
204
|
+
|
187
205
|
# If there's a preferred_identifier for this subtype, identifying readings were emitted
|
188
206
|
print((pi ? "," : " where") + "\n\t" + fact_readings(o.fact_type)) if o.fact_type
|
189
207
|
puts ";\n"
|
@@ -191,7 +209,7 @@ module ActiveFacts
|
|
191
209
|
|
192
210
|
def non_subtype_dump(o, pi)
|
193
211
|
print "#{o.name} is" + identified_by(o, pi)
|
194
|
-
print(" where\n\t"+ fact_readings(o.fact_type)) if o.fact_type
|
212
|
+
# print(" where\n\t"+ fact_readings(o.fact_type)) if o.fact_type
|
195
213
|
puts ";\n"
|
196
214
|
end
|
197
215
|
|
@@ -209,6 +227,7 @@ module ActiveFacts
|
|
209
227
|
pi = fact_type.entity_type.preferred_identifier
|
210
228
|
if pi && primary_supertype && primary_supertype.preferred_identifier != pi
|
211
229
|
print identified_by(o, pi)
|
230
|
+
# REVISIT: This *has* to be wrong. When you fix it, remember mapping_pragmas!
|
212
231
|
print ";\n"
|
213
232
|
end
|
214
233
|
end
|
@@ -244,55 +263,25 @@ module ActiveFacts
|
|
244
263
|
end
|
245
264
|
|
246
265
|
def dump_presence_constraint(c)
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
=
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
else
|
267
|
-
counterparts = roles.map{|r|
|
268
|
-
r.fact_type.all_role[r.fact_type.all_role[0] != r ? 0 : -1]
|
269
|
-
}
|
270
|
-
player = roleplayer_subclass(counterparts)
|
271
|
-
"#{c.frequency} #{player ? player.name : "UNKNOWN" } exists for each "
|
272
|
-
end +
|
273
|
-
"#{
|
274
|
-
c.role_sequence.all_role_ref.map{|rr|
|
275
|
-
"'#{rr.role.fact_type.default_reading([], nil)}'"
|
276
|
-
}*", "
|
277
|
-
}"
|
278
|
-
=end
|
279
|
-
|
280
|
-
=begin
|
281
|
-
puts \
|
282
|
-
"FOR each #{players*", "}" +
|
283
|
-
(c.role_sequence.all_role_ref.size > 1 ? " "+c.frequency+" of these holds" : "") + "\n\t"+
|
284
|
-
"#{c.role_sequence.all_role_ref.map{|rr|
|
285
|
-
role = rr.role
|
286
|
-
fact_type = role.fact_type
|
287
|
-
some_that = Array.new(fact_type.all_role.size, "some")
|
288
|
-
c.role_sequence.all_role_ref.each{|rr2|
|
289
|
-
next if rr2.role.fact_type != fact_type
|
290
|
-
some_that[fact_type.all_role.index(role)] = "that"
|
291
|
-
}
|
292
|
-
rr.role.fact_type.default_reading(some_that, nil)
|
293
|
-
}*",\n\t"}" +
|
294
|
-
";"
|
295
|
-
=end
|
266
|
+
if c.min_frequency == 1 && c.max_frequency == nil and c.role_sequence.all_role_ref.size == 2
|
267
|
+
# REVISIT: Implement the "either... or" syntax for a simple external mandatory constraint
|
268
|
+
puts \
|
269
|
+
"either #{
|
270
|
+
c.role_sequence.all_role_ref.map { |rr|
|
271
|
+
rr.role.fact_type.default_reading([], nil)
|
272
|
+
}*" or "
|
273
|
+
};"
|
274
|
+
else
|
275
|
+
# REVISIT: If only one role is covered and it's mandatory >=1 constraint, use SOME/THAT form:
|
276
|
+
# for each Bug SOME Tester logged THAT Bug;
|
277
|
+
roles = c.role_sequence.all_role_ref.map{|rr| rr.role }
|
278
|
+
players = c.role_sequence.all_role_ref.map{|rr| rr.role.concept.name}.uniq
|
279
|
+
fact_types = c.role_sequence.all_role_ref.map{|rr| rr.role.fact_type}.uniq
|
280
|
+
puts \
|
281
|
+
"each #{players.size > 1 ? "combination " : ""}#{players*", "} occurs #{c.frequency} time in\n\t"+
|
282
|
+
"#{fact_types.map{|ft| ft.default_reading([], nil)}*",\n\t"}" +
|
283
|
+
";"
|
284
|
+
end
|
296
285
|
end
|
297
286
|
|
298
287
|
# Find the common supertype of these concepts.
|
@@ -331,11 +320,13 @@ module ActiveFacts
|
|
331
320
|
# puts "#{c.class.basename} has #{role_seq_count} scr's: #{scrs.map{|scr| "("+scr.role_sequence.all_role_ref.map{|rr| rr.role.concept.name}*", "+")"}*", "}"
|
332
321
|
|
333
322
|
players_differ = [] # Record which players are also played by subclasses
|
334
|
-
players = (0...player_count).map do |
|
335
|
-
# Find the common supertype of the players of the
|
336
|
-
concepts = scrs.map
|
337
|
-
|
338
|
-
|
323
|
+
players = (0...player_count).map do |pindex|
|
324
|
+
# Find the common supertype of the players of the pindex'th role in each sequence
|
325
|
+
concepts = scrs.map do |r|
|
326
|
+
r.role_sequence.all_role_ref.sort_by{|rr| rr.ordinal}[pindex].role.concept
|
327
|
+
end
|
328
|
+
player, players_differ[pindex] = common_supertype(concepts)
|
329
|
+
raise "Role sequences of #{c.class.basename} must have concepts matching #{concepts.map(&:name)*","} in position #{pindex}" unless player
|
339
330
|
player
|
340
331
|
end
|
341
332
|
#puts "#{c.class.basename} has players #{players.map{|p| p.name}*", "}"
|
@@ -349,22 +340,42 @@ module ActiveFacts
|
|
349
340
|
return
|
350
341
|
end
|
351
342
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
343
|
+
if scrs.size == 2 && c.is_mandatory
|
344
|
+
puts "either " +
|
345
|
+
( scrs.map do |scr|
|
346
|
+
constrained_roles = scr.role_sequence.all_role_ref.map{|rr| rr.role }
|
347
|
+
fact_types = constrained_roles.map{|r| r.fact_type }.uniq
|
348
|
+
|
349
|
+
fact_types.map do |fact_type|
|
350
|
+
# Choose a reading that starts with the input role (constrained role if none)
|
351
|
+
reading = fact_type.all_reading.sort_by{|r| r.ordinal}.detect do |r|
|
352
|
+
first_reading_role = r.role_sequence.all_role_ref.detect{|rr| rr.ordinal == 0}.role
|
353
|
+
constrained_roles.include?(first_reading_role)
|
354
|
+
end
|
355
|
+
reading ||= fact_type.preferred_reading
|
356
|
+
expand_constrained(reading, constrained_roles, players, players_differ)
|
357
|
+
end * " and "
|
358
|
+
end*" or "
|
359
|
+
) +
|
360
|
+
" but not both;"
|
361
|
+
else
|
362
|
+
mode = c.is_mandatory ? "exactly one" : "at most one"
|
363
|
+
puts "for each #{players.map{|p| p.name}*", "} #{mode} of these holds:\n\t" +
|
364
|
+
(scrs.map do |scr|
|
365
|
+
constrained_roles = scr.role_sequence.all_role_ref.map{|rr| rr.role }
|
366
|
+
fact_types = constrained_roles.map{|r| r.fact_type }.uniq
|
367
|
+
|
368
|
+
fact_types.map do |fact_type|
|
369
|
+
# REVISIT: future: Use "THAT" and "SOME" only when:
|
370
|
+
# - the role player occurs twice in the reading, or
|
371
|
+
# - is a subclass of the constrained concept, or
|
372
|
+
reading = fact_type.preferred_reading
|
373
|
+
expand_constrained(reading, constrained_roles, players, players_differ)
|
374
|
+
end * " and "
|
375
|
+
|
376
|
+
end*",\n\t"
|
377
|
+
)+';'
|
378
|
+
end
|
368
379
|
end
|
369
380
|
|
370
381
|
# Expand this reading using (in)definite articles where needed
|
@@ -386,7 +397,7 @@ module ActiveFacts
|
|
386
397
|
}
|
387
398
|
frequency_constraints = [] unless frequency_constraints.detect{|fc| fc[0] != "some" }
|
388
399
|
|
389
|
-
#$stderr.puts "fact_type roles (#{fact_type.all_role.map{|r| r.concept.name}*","}) default_reading '#{fact_type.preferred_reading.
|
400
|
+
#$stderr.puts "fact_type roles (#{fact_type.all_role.map{|r| r.concept.name}*","}) default_reading '#{fact_type.preferred_reading.text}' roles (#{fact_type.preferred_reading.role_sequence.all_role_ref.map{|rr| rr.role.concept.name}*","}) #{frequency_constraints.inspect}"
|
390
401
|
|
391
402
|
# REVISIT: Make sure that we refer to the constrained players by their common supertype
|
392
403
|
|