activefacts 0.8.9 → 0.8.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.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 ]
|