activefacts 0.8.9 → 0.8.10
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/Manifest.txt +28 -33
- data/Rakefile +11 -12
- data/bin/cql +90 -46
- data/examples/CQL/Blog.cql +2 -1
- data/examples/CQL/CompanyDirectorEmployee.cql +2 -2
- data/examples/CQL/Death.cql +1 -1
- data/examples/CQL/Diplomacy.cql +9 -9
- data/examples/CQL/Genealogy.cql +3 -2
- data/examples/CQL/Insurance.cql +10 -7
- data/examples/CQL/JoinEquality.cql +2 -2
- data/examples/CQL/Marriage.cql +1 -1
- data/examples/CQL/Metamodel.cql +73 -53
- data/examples/CQL/MetamodelNext.cql +89 -67
- data/examples/CQL/OneToOnes.cql +2 -2
- data/examples/CQL/ServiceDirector.cql +10 -5
- data/examples/CQL/Supervision.cql +3 -3
- data/examples/CQL/Tests.Test5.Load.cql +1 -1
- data/examples/CQL/Warehousing.cql +4 -2
- data/lib/activefacts/cql/CQLParser.treetop +26 -60
- data/lib/activefacts/cql/Context.treetop +12 -2
- data/lib/activefacts/cql/Expressions.treetop +14 -30
- data/lib/activefacts/cql/FactTypes.treetop +165 -110
- data/lib/activefacts/cql/Language/English.treetop +167 -54
- data/lib/activefacts/cql/LexicalRules.treetop +16 -2
- data/lib/activefacts/cql/{Concepts.treetop → ObjectTypes.treetop} +36 -37
- data/lib/activefacts/cql/Terms.treetop +57 -27
- data/lib/activefacts/cql/ValueTypes.treetop +39 -13
- data/lib/activefacts/cql/compiler.rb +5 -3
- data/lib/activefacts/cql/compiler/{reading.rb → clause.rb} +407 -285
- data/lib/activefacts/cql/compiler/constraint.rb +178 -275
- data/lib/activefacts/cql/compiler/entity_type.rb +73 -64
- data/lib/activefacts/cql/compiler/expression.rb +418 -0
- data/lib/activefacts/cql/compiler/fact.rb +146 -145
- data/lib/activefacts/cql/compiler/fact_type.rb +197 -80
- data/lib/activefacts/cql/compiler/join.rb +159 -0
- data/lib/activefacts/cql/compiler/shared.rb +51 -23
- data/lib/activefacts/cql/compiler/value_type.rb +56 -2
- data/lib/activefacts/cql/parser.rb +15 -4
- data/lib/activefacts/generate/absorption.rb +7 -7
- data/lib/activefacts/generate/cql.rb +100 -37
- data/lib/activefacts/generate/oo.rb +28 -51
- data/lib/activefacts/generate/ordered.rb +60 -36
- data/lib/activefacts/generate/ruby.rb +6 -6
- data/lib/activefacts/generate/sql/server.rb +4 -4
- data/lib/activefacts/input/orm.rb +71 -53
- data/lib/activefacts/persistence.rb +1 -1
- data/lib/activefacts/persistence/columns.rb +27 -23
- data/lib/activefacts/persistence/foreignkey.rb +6 -6
- data/lib/activefacts/persistence/index.rb +17 -17
- data/lib/activefacts/persistence/{concept.rb → object_type.rb} +9 -9
- data/lib/activefacts/persistence/reference.rb +61 -36
- data/lib/activefacts/persistence/tables.rb +61 -59
- data/lib/activefacts/support.rb +54 -29
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +99 -54
- data/lib/activefacts/vocabulary/metamodel.rb +43 -37
- data/lib/activefacts/vocabulary/verbaliser.rb +134 -109
- data/spec/absorption_spec.rb +8 -8
- data/spec/cql/comparison_spec.rb +91 -0
- data/spec/cql/contractions_spec.rb +251 -0
- data/spec/cql/entity_type_spec.rb +319 -0
- data/spec/cql/expressions_spec.rb +63 -0
- data/spec/cql/fact_type_matching_spec.rb +283 -0
- data/spec/cql/french_spec.rb +21 -0
- data/spec/cql/parser/bad_literals_spec.rb +86 -0
- data/spec/cql/parser/constraints_spec.rb +19 -0
- data/spec/cql/parser/entity_types_spec.rb +106 -0
- data/spec/cql/parser/expressions_spec.rb +179 -0
- data/spec/cql/parser/fact_types_spec.rb +41 -0
- data/spec/cql/parser/literals_spec.rb +312 -0
- data/spec/cql/parser/pragmas_spec.rb +89 -0
- data/spec/cql/parser/value_types_spec.rb +42 -0
- data/spec/cql/role_matching_spec.rb +147 -0
- data/spec/cql/samples_spec.rb +9 -9
- data/spec/cql_cql_spec.rb +1 -1
- data/spec/cql_dm_spec.rb +116 -0
- data/spec/cql_mysql_spec.rb +1 -1
- data/spec/cql_ruby_spec.rb +1 -1
- data/spec/cql_sql_spec.rb +3 -3
- data/spec/cql_symbol_tables_spec.rb +30 -30
- data/spec/cqldump_spec.rb +4 -4
- data/spec/helpers/array_matcher.rb +32 -27
- data/spec/helpers/diff_matcher.rb +6 -26
- data/spec/helpers/file_matcher.rb +41 -32
- data/spec/helpers/parse_to_ast_matcher.rb +76 -0
- data/spec/helpers/string_matcher.rb +32 -31
- data/spec/norma_cql_spec.rb +1 -1
- data/spec/norma_ruby_spec.rb +1 -1
- data/spec/norma_ruby_sql_spec.rb +1 -1
- data/spec/norma_sql_spec.rb +3 -1
- data/spec/norma_tables_spec.rb +1 -1
- data/spec/ruby_api_spec.rb +23 -0
- data/spec/spec_helper.rb +5 -4
- metadata +66 -66
- data/examples/CQL/OrienteeringER.cql +0 -58
- data/lib/activefacts/api.rb +0 -44
- data/lib/activefacts/api/concept.rb +0 -410
- data/lib/activefacts/api/constellation.rb +0 -128
- data/lib/activefacts/api/entity.rb +0 -256
- data/lib/activefacts/api/instance.rb +0 -60
- data/lib/activefacts/api/instance_index.rb +0 -80
- data/lib/activefacts/api/numeric.rb +0 -167
- data/lib/activefacts/api/role.rb +0 -80
- data/lib/activefacts/api/role_proxy.rb +0 -70
- data/lib/activefacts/api/role_values.rb +0 -117
- data/lib/activefacts/api/standard_types.rb +0 -87
- data/lib/activefacts/api/support.rb +0 -65
- data/lib/activefacts/api/value.rb +0 -135
- data/lib/activefacts/api/vocabulary.rb +0 -82
- data/spec/api/autocounter.rb +0 -82
- data/spec/api/constellation.rb +0 -130
- data/spec/api/entity_type.rb +0 -103
- data/spec/api/instance.rb +0 -461
- data/spec/api/roles.rb +0 -124
- data/spec/api/value_type.rb +0 -112
- data/spec/api_spec.rb +0 -13
- data/spec/cql/matching_spec.rb +0 -517
- data/spec/cql/unit_spec.rb +0 -394
- data/spec/spec.opts +0 -1
@@ -2,19 +2,19 @@ module ActiveFacts
|
|
2
2
|
module CQL
|
3
3
|
class Compiler < ActiveFacts::CQL::Parser
|
4
4
|
|
5
|
-
# In a declaration, a
|
6
|
-
# A
|
7
|
-
# and the references (
|
5
|
+
# In a declaration, a Variable has one or more VarRef's.
|
6
|
+
# A Variable is for a single ObjectType, normally related to just one Role,
|
7
|
+
# and the references (VarRefs) to it will normally be the object_type name
|
8
8
|
# with the same adjectives (modulo loose binding),
|
9
9
|
# or a role name or subscript reference.
|
10
10
|
#
|
11
|
-
# In some situations a
|
12
|
-
# and one or more
|
13
|
-
class
|
14
|
-
attr_reader :player # The
|
15
|
-
attr_reader :refs # an array of the
|
11
|
+
# In some situations a Variable will have some VarRefs with the same adjectives,
|
12
|
+
# and one or more VarRefs with no adjectives - this is called "loose binding".
|
13
|
+
class Variable
|
14
|
+
attr_reader :player # The ObjectType (object type)
|
15
|
+
attr_reader :refs # an array of the VarRefs
|
16
16
|
attr_reader :role_name
|
17
|
-
attr_accessor :rebound_to # Loose binding may set this to another
|
17
|
+
attr_accessor :rebound_to # Loose binding may set this to another variable
|
18
18
|
attr_accessor :join_node
|
19
19
|
attr_accessor :instance # When binding fact instances, the instance goes here
|
20
20
|
|
@@ -38,22 +38,27 @@ module ActiveFacts
|
|
38
38
|
end
|
39
39
|
|
40
40
|
class CompilationContext
|
41
|
+
attr_accessor :vocabulary
|
41
42
|
attr_accessor :allowed_forward_terms
|
42
|
-
|
43
|
+
attr_accessor :left_contraction_allowed
|
44
|
+
attr_accessor :left_contractable_clause
|
45
|
+
attr_accessor :left_contraction_conjunction
|
46
|
+
attr_reader :variables # The Variables in this declaration
|
43
47
|
attr_reader :player_by_role_name
|
44
48
|
|
45
49
|
def initialize vocabulary
|
46
50
|
@vocabulary = vocabulary
|
47
51
|
@vocabulary_identifier = @vocabulary.identifying_role_values
|
48
52
|
@allowed_forward_terms = []
|
49
|
-
@
|
53
|
+
@variables = {}
|
50
54
|
@player_by_role_name = {}
|
55
|
+
@left_contraction_allowed = false
|
51
56
|
end
|
52
57
|
|
53
|
-
# Look up this
|
54
|
-
def
|
58
|
+
# Look up this object_type by its name
|
59
|
+
def object_type(name)
|
55
60
|
constellation = @vocabulary.constellation
|
56
|
-
player = constellation.
|
61
|
+
player = constellation.ObjectType[[@vocabulary_identifier, name]]
|
57
62
|
|
58
63
|
# Bind to an existing role which has a role name (that's why we bind those first)
|
59
64
|
player ||= @player_by_role_name[name]
|
@@ -64,13 +69,31 @@ module ActiveFacts
|
|
64
69
|
|
65
70
|
player
|
66
71
|
end
|
72
|
+
|
73
|
+
# Pass in an array of clauses or VarRefs for player identification and binding (creating the Variables)
|
74
|
+
# It's necessary to identify all players that define a role name first,
|
75
|
+
# so those names exist in the context for where they're used.
|
76
|
+
def bind *clauses
|
77
|
+
cl = clauses.flatten
|
78
|
+
cl.each { |clause| clause.identify_players_with_role_name(self) }
|
79
|
+
cl.each { |clause| clause.identify_other_players(self) }
|
80
|
+
cl.each { |clause| clause.bind(self) }
|
81
|
+
end
|
67
82
|
end
|
68
83
|
|
69
84
|
class Definition
|
70
|
-
attr_accessor :constellation, :vocabulary, :
|
85
|
+
attr_accessor :constellation, :vocabulary, :tree
|
71
86
|
def compile
|
72
87
|
raise "#{self.class} should implement the compile method"
|
73
88
|
end
|
89
|
+
|
90
|
+
def to_s
|
91
|
+
@vocabulary ? "#{vocabulary.to_s}::" : ''
|
92
|
+
end
|
93
|
+
|
94
|
+
def source
|
95
|
+
@tree.text_value
|
96
|
+
end
|
74
97
|
end
|
75
98
|
|
76
99
|
class Vocabulary < Definition
|
@@ -81,6 +104,10 @@ module ActiveFacts
|
|
81
104
|
def compile
|
82
105
|
@constellation.Vocabulary @name
|
83
106
|
end
|
107
|
+
|
108
|
+
def to_s
|
109
|
+
@name
|
110
|
+
end
|
84
111
|
end
|
85
112
|
|
86
113
|
class Import < Definition
|
@@ -88,23 +115,24 @@ module ActiveFacts
|
|
88
115
|
@name = name
|
89
116
|
@alias_list = alias_list
|
90
117
|
end
|
118
|
+
|
119
|
+
def to_s
|
120
|
+
"#{@vocabulary.to_s} imports #{alias_list*', '};"
|
121
|
+
end
|
91
122
|
end
|
92
123
|
|
93
|
-
class
|
124
|
+
class ObjectType < Definition
|
94
125
|
attr_reader :name
|
95
126
|
|
96
127
|
def initialize name
|
97
128
|
@name = name
|
98
129
|
end
|
130
|
+
|
131
|
+
def to_s
|
132
|
+
"#{super}#{@name}"
|
133
|
+
end
|
99
134
|
end
|
100
135
|
|
101
136
|
end
|
102
137
|
end
|
103
138
|
end
|
104
|
-
|
105
|
-
require 'activefacts/cql/compiler/value_type'
|
106
|
-
require 'activefacts/cql/compiler/entity_type'
|
107
|
-
require 'activefacts/cql/compiler/reading'
|
108
|
-
require 'activefacts/cql/compiler/fact_type'
|
109
|
-
require 'activefacts/cql/compiler/fact'
|
110
|
-
require 'activefacts/cql/compiler/constraint'
|
@@ -58,9 +58,31 @@ module ActiveFacts
|
|
58
58
|
unit
|
59
59
|
end
|
60
60
|
end
|
61
|
+
|
62
|
+
def inspect
|
63
|
+
to_s
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_s
|
67
|
+
super + "Unit(#{
|
68
|
+
@singular
|
69
|
+
}#{
|
70
|
+
@plural ? '/'+@plural : ''
|
71
|
+
}) is #{
|
72
|
+
@numerator
|
73
|
+
}/#{
|
74
|
+
@denominator
|
75
|
+
}+#{
|
76
|
+
@offset
|
77
|
+
} #{
|
78
|
+
@base_units.map{|b,e|
|
79
|
+
b+'^'+e.to_s
|
80
|
+
}*'*'
|
81
|
+
}"
|
82
|
+
end
|
61
83
|
end
|
62
84
|
|
63
|
-
class ValueType <
|
85
|
+
class ValueType < ObjectType
|
64
86
|
def initialize name, base, parameters, unit, value_constraint, pragmas
|
65
87
|
super name
|
66
88
|
@base_type_name = base
|
@@ -89,7 +111,25 @@ module ActiveFacts
|
|
89
111
|
vt.length = length if length
|
90
112
|
vt.scale = scale if scale
|
91
113
|
|
92
|
-
|
114
|
+
unless @unit.empty?
|
115
|
+
unit_name, exponent = *@unit[0]
|
116
|
+
unit = @constellation.Unit.detect{|k,v| v.name == unit_name }
|
117
|
+
raise "Unit #{unit_name} for value type #{@name} is not defined" unless unit
|
118
|
+
if exponent != 1
|
119
|
+
base_unit = unit
|
120
|
+
unit_name = base_unit.name+"^#{exponent}"
|
121
|
+
unless unit = @constellation.Unit.detect{|k,v| v.name == unit_name }
|
122
|
+
# Define a derived unit (these are skipped on output)
|
123
|
+
unit = @constellation.Unit(:new,
|
124
|
+
:vocabulary => @vocabulary,
|
125
|
+
:name => unit_name,
|
126
|
+
:is_fundamental => false
|
127
|
+
)
|
128
|
+
@constellation.Derivation(unit, base_unit).exponent = exponent
|
129
|
+
end
|
130
|
+
end
|
131
|
+
vt.unit = unit
|
132
|
+
end
|
93
133
|
|
94
134
|
if @value_constraint
|
95
135
|
@value_constraint.constellation = @constellation
|
@@ -98,6 +138,20 @@ module ActiveFacts
|
|
98
138
|
|
99
139
|
vt
|
100
140
|
end
|
141
|
+
|
142
|
+
def to_s
|
143
|
+
"ValueType: #{super} is written as #{
|
144
|
+
@base_type_name
|
145
|
+
}#{
|
146
|
+
@parameters.size > 0 ? "(#{ @parameters.map{|p|p.to_s}*', ' })" : ''
|
147
|
+
}#{
|
148
|
+
@unit && @unit.length > 0 ? " in #{@unit.inspect}" : ''
|
149
|
+
}#{
|
150
|
+
@value_constraint ? " "+@value_constraint.to_s : ''
|
151
|
+
}#{
|
152
|
+
@pragmas.size > 0 ? ", pragmas [#{@pragmas*','}]" : ''
|
153
|
+
};"
|
154
|
+
end
|
101
155
|
end
|
102
156
|
end
|
103
157
|
end
|
@@ -12,12 +12,18 @@ require 'activefacts/cql/LexicalRules'
|
|
12
12
|
require 'activefacts/cql/Language/English'
|
13
13
|
require 'activefacts/cql/Expressions'
|
14
14
|
require 'activefacts/cql/Terms'
|
15
|
-
require 'activefacts/cql/
|
15
|
+
require 'activefacts/cql/ObjectTypes'
|
16
16
|
require 'activefacts/cql/ValueTypes'
|
17
17
|
require 'activefacts/cql/FactTypes'
|
18
18
|
require 'activefacts/cql/Context'
|
19
19
|
require 'activefacts/cql/CQLParser'
|
20
20
|
|
21
|
+
class Treetop::Runtime::SyntaxNode
|
22
|
+
def node_type
|
23
|
+
terminal? ? :keyword : :composite
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
21
27
|
module ActiveFacts
|
22
28
|
module CQL
|
23
29
|
module Terms
|
@@ -62,7 +68,7 @@ module ActiveFacts
|
|
62
68
|
end
|
63
69
|
|
64
70
|
def new_trailing_adjective_term(adj, term)
|
65
|
-
index_name(@role_names,
|
71
|
+
index_name(@role_names, "#{term} #{adj}", term) && debug(:context, "new role '#{term} -#{adj}'")
|
66
72
|
true
|
67
73
|
end
|
68
74
|
|
@@ -201,12 +207,17 @@ module ActiveFacts
|
|
201
207
|
|
202
208
|
@index = 0 # Byte offset to start next parse
|
203
209
|
self.consume_all_input = false
|
210
|
+
nodes = []
|
204
211
|
begin
|
205
212
|
node = parse(InputProxy.new(input, context), :index => @index)
|
206
213
|
return nil unless node
|
207
|
-
|
214
|
+
if block
|
215
|
+
block.call(node)
|
216
|
+
else
|
217
|
+
nodes << node
|
218
|
+
end
|
208
219
|
end until self.index == @input_length
|
209
|
-
true
|
220
|
+
block ? true : nodes
|
210
221
|
end
|
211
222
|
end
|
212
223
|
|
@@ -14,7 +14,7 @@ module ActiveFacts
|
|
14
14
|
# afgen --absorption[=options] <file>.cql"
|
15
15
|
# Options are comma or space separated:
|
16
16
|
# * no_columns Don't emit the columns
|
17
|
-
# * all Show
|
17
|
+
# * all Show ObjectTypes that are not tables as well
|
18
18
|
# * paths Show the references paths through which each column was defined
|
19
19
|
# * no_identifier Don't show the identified_by columns for an EntityType
|
20
20
|
|
@@ -34,21 +34,21 @@ module ActiveFacts
|
|
34
34
|
multi_absorption_vts = 0
|
35
35
|
multi_absorption_ets = 0
|
36
36
|
@vocabulary.tables
|
37
|
-
@vocabulary.
|
37
|
+
@vocabulary.all_object_type.sort_by{|c| c.name}.each do |o|
|
38
38
|
next if !o.is_table
|
39
39
|
show(o)
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
def show
|
44
|
-
indices =
|
43
|
+
def show object_type #:nodoc:
|
44
|
+
indices = object_type.indices
|
45
45
|
pk = indices.select(&:is_primary)[0]
|
46
46
|
indices = indices.clone
|
47
47
|
indices.delete pk
|
48
|
-
puts "#{
|
49
|
-
# "[#{
|
48
|
+
puts "#{object_type.name}: #{
|
49
|
+
# "[#{object_type.indices.size} indices] "
|
50
50
|
# } #{
|
51
|
-
|
51
|
+
object_type.columns.sort_by do |column|
|
52
52
|
column.name(nil)
|
53
53
|
end.map do |column|
|
54
54
|
index_nrs =
|
@@ -57,6 +57,10 @@ module ActiveFacts
|
|
57
57
|
puts ";"
|
58
58
|
end
|
59
59
|
|
60
|
+
def units_end
|
61
|
+
puts "\n"
|
62
|
+
end
|
63
|
+
|
60
64
|
def value_type_banner
|
61
65
|
puts "/*\n * Value Types\n */"
|
62
66
|
end
|
@@ -84,8 +88,8 @@ module ActiveFacts
|
|
84
88
|
!o.all_role.
|
85
89
|
detect do |role|
|
86
90
|
(other_roles = role.fact_type.all_role.to_a-[role]).size != 1 || # Not a role in a binary FT
|
87
|
-
!(
|
88
|
-
(pi =
|
91
|
+
!(object_type = other_roles[0].object_type).is_a?(ActiveFacts::Metamodel::EntityType) || # Counterpart is not an ET
|
92
|
+
(pi = object_type.preferred_identifier).role_sequence.all_role_ref.size != 1 || # Entity PI has > 1 roles
|
89
93
|
pi.role_sequence.all_role_ref.single.role != role # This isn't the identifying role
|
90
94
|
end
|
91
95
|
puts "About to skip #{o.name}"
|
@@ -106,9 +110,17 @@ module ActiveFacts
|
|
106
110
|
].compact
|
107
111
|
parameters = parameters.length > 0 ? "("+parameters.join(",")+")" : ""
|
108
112
|
|
109
|
-
puts "#{o.name
|
113
|
+
puts "#{o.name
|
114
|
+
} #{
|
115
|
+
(o.is_independent ? '[independent] ' : '')
|
116
|
+
}is written as #{
|
117
|
+
(o.supertype || o).name
|
118
|
+
}#{
|
119
|
+
parameters
|
120
|
+
}#{
|
121
|
+
o.unit && " "+o.unit.name
|
122
|
+
}#{
|
110
123
|
o.value_constraint && " "+o.value_constraint.describe
|
111
|
-
}#{o.is_independent ? ' [independent]' : ''
|
112
124
|
};"
|
113
125
|
end
|
114
126
|
|
@@ -116,13 +128,13 @@ module ActiveFacts
|
|
116
128
|
reading << " [#{(ring.ring_type.scan(/[A-Z][a-z]*/)*", ").downcase}]"
|
117
129
|
end
|
118
130
|
|
119
|
-
def mapping_pragma(entity_type)
|
131
|
+
def mapping_pragma(entity_type, ignore_independence = false)
|
120
132
|
ti = entity_type.all_type_inheritance_as_subtype
|
121
133
|
assimilation = ti.map{|t| t.assimilation }.compact[0]
|
122
|
-
return "" unless entity_type.is_independent || assimilation
|
134
|
+
return "" unless (entity_type.is_independent && !ignore_independence) || assimilation
|
123
135
|
" [" +
|
124
136
|
[
|
125
|
-
entity_type.is_independent ? "independent" : nil,
|
137
|
+
entity_type.is_independent && !ignore_independence ? "independent" : nil,
|
126
138
|
assimilation || nil
|
127
139
|
].compact*", " +
|
128
140
|
"]"
|
@@ -134,12 +146,12 @@ module ActiveFacts
|
|
134
146
|
fact_type = external_identifying_facts[0]
|
135
147
|
ftr = fact_type && fact_type.all_role.sort_by{|role| role.ordinal}
|
136
148
|
if external_identifying_facts.size == 1 and
|
137
|
-
entity_role = ftr[n = (ftr[0].
|
149
|
+
entity_role = ftr[n = (ftr[0].object_type == entity_type ? 0 : 1)] and
|
138
150
|
value_role = ftr[1-n] and
|
139
|
-
value_player = value_role.
|
151
|
+
value_player = value_role.object_type and
|
140
152
|
value_player.is_a?(ActiveFacts::Metamodel::ValueType) and
|
141
153
|
value_name = value_player.name and
|
142
|
-
value_residual = value_name.sub(%r{^#{entity_role.
|
154
|
+
value_residual = value_name.sub(%r{^#{entity_role.object_type.name} ?},'') and
|
143
155
|
value_residual != '' and
|
144
156
|
value_residual != value_name
|
145
157
|
[fact_type, entity_role, value_role, value_residual]
|
@@ -189,7 +201,7 @@ module ActiveFacts
|
|
189
201
|
# We can't subscript reference modes.
|
190
202
|
# If an objectified fact type has a role played by its identifying player, go long-hand.
|
191
203
|
return nil if entity_type.fact_type and
|
192
|
-
entity_type.fact_type.all_role.detect{|role| role.
|
204
|
+
entity_type.fact_type.all_role.detect{|role| role.object_type == value_role.object_type }
|
193
205
|
|
194
206
|
@fact_types_dumped[fact_type] = true # We've covered this fact type
|
195
207
|
|
@@ -212,7 +224,7 @@ module ActiveFacts
|
|
212
224
|
|
213
225
|
# The verbaliser needs to have a Player for the roles of entity_type, so it doesn't get subscripted.
|
214
226
|
entity_roles =
|
215
|
-
nonstandard_readings.map{|r| r.role_sequence.all_role_ref.detect{|rr| rr.role.
|
227
|
+
nonstandard_readings.map{|r| r.role_sequence.all_role_ref.detect{|rr| rr.role.object_type == entity_type}}.compact
|
216
228
|
verbaliser.role_refs_have_same_player entity_roles
|
217
229
|
|
218
230
|
verbaliser.alternate_readings nonstandard_readings
|
@@ -220,7 +232,7 @@ module ActiveFacts
|
|
220
232
|
verbaliser.alternate_readings entity_type.fact_type.all_reading
|
221
233
|
end
|
222
234
|
|
223
|
-
verbaliser.create_subscripts # Ok, the Verbaliser is ready to fly
|
235
|
+
verbaliser.create_subscripts(:rolenames) # Ok, the Verbaliser is ready to fly
|
224
236
|
|
225
237
|
fact_readings =
|
226
238
|
nonstandard_readings.map { |reading| expanded_reading(verbaliser, reading, fact_constraints, true) }
|
@@ -231,7 +243,8 @@ module ActiveFacts
|
|
231
243
|
if nonstandard_readings.size == 0 and c = value_role.role_value_constraint
|
232
244
|
constraint_text = " "+c.describe
|
233
245
|
end
|
234
|
-
|
246
|
+
(entity_type.is_independent ? ' independent' : '') +
|
247
|
+
" identified by its #{value_residual}#{constraint_text}#{mapping_pragma(entity_type, true)}" +
|
235
248
|
(fact_readings.size > 0 ? " where\n\t" : "") +
|
236
249
|
fact_readings*",\n\t"
|
237
250
|
end
|
@@ -247,14 +260,14 @@ module ActiveFacts
|
|
247
260
|
# Announce all the identifying fact roles to the verbaliser so it can decide on any necessary subscripting.
|
248
261
|
# The verbaliser needs to have a Player for the roles of entity_type, so it doesn't get subscripted.
|
249
262
|
entity_roles =
|
250
|
-
identifying_facts.map{|ft| ft.preferred_reading.role_sequence.all_role_ref.detect{|rr| rr.role.
|
263
|
+
identifying_facts.map{|ft| ft.preferred_reading.role_sequence.all_role_ref.detect{|rr| rr.role.object_type == entity_type}}.compact
|
251
264
|
verbaliser.role_refs_have_same_player entity_roles
|
252
265
|
identifying_facts.each do |fact_type|
|
253
266
|
# The RoleRefs for corresponding roles across all readings are for the same player.
|
254
267
|
verbaliser.alternate_readings fact_type.all_reading
|
255
|
-
@fact_types_dumped[fact_type] = true
|
268
|
+
@fact_types_dumped[fact_type] = true unless fact_type.entity_type # Must dump objectification still!
|
256
269
|
end
|
257
|
-
verbaliser.create_subscripts
|
270
|
+
verbaliser.create_subscripts(:rolenames)
|
258
271
|
|
259
272
|
irn = verbaliser.identifying_role_names identifying_role_refs
|
260
273
|
|
@@ -263,8 +276,9 @@ module ActiveFacts
|
|
263
276
|
fact_readings_with_constraints(verbaliser, f)
|
264
277
|
}.flatten*",\n\t"
|
265
278
|
|
266
|
-
|
267
|
-
|
279
|
+
(entity_type.is_independent ? ' independent' : '') +
|
280
|
+
" identified by #{ irn*" and " }" +
|
281
|
+
mapping_pragma(entity_type, true) +
|
268
282
|
" where\n\t"+identifying_fact_text
|
269
283
|
end
|
270
284
|
|
@@ -277,20 +291,22 @@ module ActiveFacts
|
|
277
291
|
end
|
278
292
|
|
279
293
|
def subtype_dump(o, supertypes, pi)
|
280
|
-
print "#{o.name} is a kind of #{
|
294
|
+
print "#{o.name} is a kind of #{
|
295
|
+
o.is_independent ? 'independent ' : ''
|
296
|
+
}#{ o.supertypes.map(&:name)*", " }"
|
281
297
|
if pi
|
282
298
|
puts identified_by(o, pi)+';'
|
283
299
|
return
|
284
300
|
end
|
285
301
|
|
286
|
-
print mapping_pragma(o)
|
302
|
+
print mapping_pragma(o, true)
|
287
303
|
|
288
304
|
if o.fact_type
|
289
305
|
verbaliser = ActiveFacts::Metamodel::Verbaliser.new
|
290
306
|
# Announce all the objectified fact roles to the verbaliser so it can decide on any necessary subscripting.
|
291
307
|
# The RoleRefs for corresponding roles across all readings are for the same player.
|
292
308
|
verbaliser.alternate_readings o.fact_type.all_reading
|
293
|
-
verbaliser.create_subscripts
|
309
|
+
verbaliser.create_subscripts(:rolenames)
|
294
310
|
|
295
311
|
print " where\n\t" + fact_readings_with_constraints(verbaliser, o.fact_type)*",\n\t"
|
296
312
|
end
|
@@ -301,12 +317,25 @@ module ActiveFacts
|
|
301
317
|
puts "#{o.name} is" + identified_by(o, pi) + ';'
|
302
318
|
end
|
303
319
|
|
320
|
+
def naiive_expand(reading)
|
321
|
+
role_refs = reading.role_sequence.all_role_ref_in_order
|
322
|
+
reading.text.gsub(/\{(\d+)\}/) do
|
323
|
+
role_refs[$1.to_i].role.object_type.name
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
304
327
|
def fact_type_dump(fact_type, name)
|
305
328
|
|
306
329
|
if (o = fact_type.entity_type)
|
307
330
|
print "#{o.name} is"
|
308
331
|
supertypes = o.supertypes
|
309
|
-
|
332
|
+
if supertypes.empty?
|
333
|
+
print ' independent' if o.is_independent
|
334
|
+
else
|
335
|
+
print " a kind of#{
|
336
|
+
o.is_independent ? ' independent' : ''
|
337
|
+
} #{ supertypes.map(&:name)*', ' }"
|
338
|
+
end
|
310
339
|
|
311
340
|
# Alternate identification of objectified fact type?
|
312
341
|
primary_supertype = supertypes[0]
|
@@ -318,12 +347,36 @@ module ActiveFacts
|
|
318
347
|
print " where\n\t"
|
319
348
|
end
|
320
349
|
|
350
|
+
# Check whether this fact type has readings which could be confused for a previously-dumped one:
|
351
|
+
reading_texts = fact_type.all_reading.map{|r| naiive_expand(r)}
|
352
|
+
if reading_texts.size > 1
|
353
|
+
ambiguity =
|
354
|
+
fact_type.all_role.to_a[0].object_type.all_role.map{|r| r.fact_type}.
|
355
|
+
select{|f| f != fact_type && @fact_types_dumped.include?(f) }.
|
356
|
+
detect do |dft|
|
357
|
+
ambiguous_readings =
|
358
|
+
reading_texts & dft.all_reading.map{|r| naiive_expand(r)}
|
359
|
+
ambiguous_readings.size > 0
|
360
|
+
end
|
361
|
+
if ambiguity
|
362
|
+
puts fact_type.default_reading([], true)+'; // Avoid ambiguity; this is a new fact type'
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
321
366
|
# There can be no roles of the objectified fact type in the readings, so no need to tell the Verbaliser anything special
|
322
367
|
verbaliser = ActiveFacts::Metamodel::Verbaliser.new
|
323
368
|
verbaliser.alternate_readings fact_type.all_reading
|
324
|
-
|
369
|
+
pr = fact_type.preferred_reading
|
370
|
+
if (pr.role_sequence.all_role_ref.to_a[0].join_role)
|
371
|
+
verbaliser.prepare_role_sequence pr.role_sequence
|
372
|
+
end
|
373
|
+
verbaliser.create_subscripts(:rolenames)
|
325
374
|
|
326
|
-
|
375
|
+
print(fact_readings_with_constraints(verbaliser, fact_type)*",\n\t")
|
376
|
+
if (pr.role_sequence.all_role_ref.to_a[0].join_role)
|
377
|
+
print ":\n\t"+verbaliser.verbalise_over_role_sequence(pr.role_sequence)
|
378
|
+
end
|
379
|
+
puts(';')
|
327
380
|
end
|
328
381
|
|
329
382
|
def fact_type_banner
|
@@ -344,9 +397,9 @@ module ActiveFacts
|
|
344
397
|
|
345
398
|
# Of the players of a set of roles, return the one that's a subclass of (or same as) all others, else nil
|
346
399
|
def roleplayer_subclass(roles)
|
347
|
-
roles[1..-1].inject(roles[0].
|
348
|
-
next nil unless subclass and EntityType === role.
|
349
|
-
role.
|
400
|
+
roles[1..-1].inject(roles[0].object_type){|subclass, role|
|
401
|
+
next nil unless subclass and EntityType === role.object_type
|
402
|
+
role.object_type.supertypes_transitive.include?(subclass) ? role.object_type : nil
|
350
403
|
}
|
351
404
|
end
|
352
405
|
|
@@ -366,6 +419,8 @@ module ActiveFacts
|
|
366
419
|
end
|
367
420
|
|
368
421
|
verbaliser.prepare_role_sequence(c.role_sequence, join_over)
|
422
|
+
# REVISIT: Need to discount role_adjuncts in here, since this constraint uses loose binding:
|
423
|
+
verbaliser.create_subscripts :loose
|
369
424
|
|
370
425
|
expanded_readings = verbaliser.verbalise_over_role_sequence(c.role_sequence, nil, role_proximity)
|
371
426
|
if c.min_frequency == 1 && c.max_frequency == nil and c.role_sequence.all_role_ref.size == 2
|
@@ -404,7 +459,7 @@ module ActiveFacts
|
|
404
459
|
end
|
405
460
|
end
|
406
461
|
end
|
407
|
-
verbaliser.create_subscripts
|
462
|
+
verbaliser.create_subscripts :normal
|
408
463
|
|
409
464
|
if role_sequences.detect{|scr| scr.all_role_ref.detect{|rr| rr.join_role}}
|
410
465
|
# This set constraint has an explicit join. Verbalise it.
|
@@ -421,6 +476,14 @@ module ActiveFacts
|
|
421
476
|
puts "either " + readings_list.join(" or ") + " but not both;"
|
422
477
|
return
|
423
478
|
end
|
479
|
+
|
480
|
+
# Internal check: We must have located the players here
|
481
|
+
if i = players.index(nil)
|
482
|
+
rrs = transposed_role_refs[i]
|
483
|
+
raise "Internal error detecting constrained object types in join involving #{rrs.map{|rr| rr.role.fact_type.default_reading}.uniq*', '}"
|
484
|
+
end
|
485
|
+
|
486
|
+
# Loose binding will apply only to the constrained roles, not to all roles. Not handled here.
|
424
487
|
mode = c.is_mandatory ? "exactly one" : "at most one"
|
425
488
|
puts "for each #{players.map{|p| p.name}*", "} #{mode} of these holds:\n\t" +
|
426
489
|
readings_list.join(",\n\t") +
|
@@ -437,7 +500,7 @@ module ActiveFacts
|
|
437
500
|
end
|
438
501
|
|
439
502
|
# A constrained role may involve a subtyping join. We substitute the name of the supertype for all occurrences.
|
440
|
-
players = transposed_role_refs.map{|role_refs| common_supertype(role_refs.map{|rr| rr.role.
|
503
|
+
players = transposed_role_refs.map{|role_refs| common_supertype(role_refs.map{|rr| rr.role.object_type})}
|
441
504
|
raise "Constraint must cover matching roles" if players.compact.size < players.size
|
442
505
|
|
443
506
|
readings_expanded = scrs.
|
@@ -480,7 +543,7 @@ module ActiveFacts
|
|
480
543
|
transposed_role_refs.each { |role_refs| verbaliser.role_refs_are_subtype_joined role_refs }
|
481
544
|
verbaliser.prepare_role_sequence c.subset_role_sequence
|
482
545
|
verbaliser.prepare_role_sequence c.superset_role_sequence
|
483
|
-
verbaliser.create_subscripts
|
546
|
+
verbaliser.create_subscripts :normal
|
484
547
|
|
485
548
|
puts \
|
486
549
|
verbaliser.verbalise_over_role_sequence(c.subset_role_sequence) +
|
@@ -509,11 +572,11 @@ module ActiveFacts
|
|
509
572
|
end
|
510
573
|
end
|
511
574
|
|
512
|
-
# Find the common supertype of these
|
513
|
-
def common_supertype(
|
514
|
-
common =
|
515
|
-
|
516
|
-
common &=
|
575
|
+
# Find the common supertype of these object_types.
|
576
|
+
def common_supertype(object_types)
|
577
|
+
common = object_types[0].supertypes_transitive
|
578
|
+
object_types[1..-1].each do |object_type|
|
579
|
+
common &= object_type.supertypes_transitive
|
517
580
|
end
|
518
581
|
common[0]
|
519
582
|
end
|
@@ -581,7 +644,7 @@ module ActiveFacts
|
|
581
644
|
# Make sure that we refer to the constrained players by their common supertype (as passed in)
|
582
645
|
frequency_constraints = reading.role_sequence.all_role_ref.
|
583
646
|
map do |role_ref|
|
584
|
-
player = role_ref.role.
|
647
|
+
player = role_ref.role.object_type
|
585
648
|
i = constrained_roles.index(role_ref.role)
|
586
649
|
player = players[i] if i
|
587
650
|
[ nil, player.name ]
|