activefacts 0.8.9 → 0.8.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. data/.gemtest +0 -0
  2. data/Manifest.txt +28 -33
  3. data/Rakefile +11 -12
  4. data/bin/cql +90 -46
  5. data/examples/CQL/Blog.cql +2 -1
  6. data/examples/CQL/CompanyDirectorEmployee.cql +2 -2
  7. data/examples/CQL/Death.cql +1 -1
  8. data/examples/CQL/Diplomacy.cql +9 -9
  9. data/examples/CQL/Genealogy.cql +3 -2
  10. data/examples/CQL/Insurance.cql +10 -7
  11. data/examples/CQL/JoinEquality.cql +2 -2
  12. data/examples/CQL/Marriage.cql +1 -1
  13. data/examples/CQL/Metamodel.cql +73 -53
  14. data/examples/CQL/MetamodelNext.cql +89 -67
  15. data/examples/CQL/OneToOnes.cql +2 -2
  16. data/examples/CQL/ServiceDirector.cql +10 -5
  17. data/examples/CQL/Supervision.cql +3 -3
  18. data/examples/CQL/Tests.Test5.Load.cql +1 -1
  19. data/examples/CQL/Warehousing.cql +4 -2
  20. data/lib/activefacts/cql/CQLParser.treetop +26 -60
  21. data/lib/activefacts/cql/Context.treetop +12 -2
  22. data/lib/activefacts/cql/Expressions.treetop +14 -30
  23. data/lib/activefacts/cql/FactTypes.treetop +165 -110
  24. data/lib/activefacts/cql/Language/English.treetop +167 -54
  25. data/lib/activefacts/cql/LexicalRules.treetop +16 -2
  26. data/lib/activefacts/cql/{Concepts.treetop → ObjectTypes.treetop} +36 -37
  27. data/lib/activefacts/cql/Terms.treetop +57 -27
  28. data/lib/activefacts/cql/ValueTypes.treetop +39 -13
  29. data/lib/activefacts/cql/compiler.rb +5 -3
  30. data/lib/activefacts/cql/compiler/{reading.rb → clause.rb} +407 -285
  31. data/lib/activefacts/cql/compiler/constraint.rb +178 -275
  32. data/lib/activefacts/cql/compiler/entity_type.rb +73 -64
  33. data/lib/activefacts/cql/compiler/expression.rb +418 -0
  34. data/lib/activefacts/cql/compiler/fact.rb +146 -145
  35. data/lib/activefacts/cql/compiler/fact_type.rb +197 -80
  36. data/lib/activefacts/cql/compiler/join.rb +159 -0
  37. data/lib/activefacts/cql/compiler/shared.rb +51 -23
  38. data/lib/activefacts/cql/compiler/value_type.rb +56 -2
  39. data/lib/activefacts/cql/parser.rb +15 -4
  40. data/lib/activefacts/generate/absorption.rb +7 -7
  41. data/lib/activefacts/generate/cql.rb +100 -37
  42. data/lib/activefacts/generate/oo.rb +28 -51
  43. data/lib/activefacts/generate/ordered.rb +60 -36
  44. data/lib/activefacts/generate/ruby.rb +6 -6
  45. data/lib/activefacts/generate/sql/server.rb +4 -4
  46. data/lib/activefacts/input/orm.rb +71 -53
  47. data/lib/activefacts/persistence.rb +1 -1
  48. data/lib/activefacts/persistence/columns.rb +27 -23
  49. data/lib/activefacts/persistence/foreignkey.rb +6 -6
  50. data/lib/activefacts/persistence/index.rb +17 -17
  51. data/lib/activefacts/persistence/{concept.rb → object_type.rb} +9 -9
  52. data/lib/activefacts/persistence/reference.rb +61 -36
  53. data/lib/activefacts/persistence/tables.rb +61 -59
  54. data/lib/activefacts/support.rb +54 -29
  55. data/lib/activefacts/version.rb +1 -1
  56. data/lib/activefacts/vocabulary/extensions.rb +99 -54
  57. data/lib/activefacts/vocabulary/metamodel.rb +43 -37
  58. data/lib/activefacts/vocabulary/verbaliser.rb +134 -109
  59. data/spec/absorption_spec.rb +8 -8
  60. data/spec/cql/comparison_spec.rb +91 -0
  61. data/spec/cql/contractions_spec.rb +251 -0
  62. data/spec/cql/entity_type_spec.rb +319 -0
  63. data/spec/cql/expressions_spec.rb +63 -0
  64. data/spec/cql/fact_type_matching_spec.rb +283 -0
  65. data/spec/cql/french_spec.rb +21 -0
  66. data/spec/cql/parser/bad_literals_spec.rb +86 -0
  67. data/spec/cql/parser/constraints_spec.rb +19 -0
  68. data/spec/cql/parser/entity_types_spec.rb +106 -0
  69. data/spec/cql/parser/expressions_spec.rb +179 -0
  70. data/spec/cql/parser/fact_types_spec.rb +41 -0
  71. data/spec/cql/parser/literals_spec.rb +312 -0
  72. data/spec/cql/parser/pragmas_spec.rb +89 -0
  73. data/spec/cql/parser/value_types_spec.rb +42 -0
  74. data/spec/cql/role_matching_spec.rb +147 -0
  75. data/spec/cql/samples_spec.rb +9 -9
  76. data/spec/cql_cql_spec.rb +1 -1
  77. data/spec/cql_dm_spec.rb +116 -0
  78. data/spec/cql_mysql_spec.rb +1 -1
  79. data/spec/cql_ruby_spec.rb +1 -1
  80. data/spec/cql_sql_spec.rb +3 -3
  81. data/spec/cql_symbol_tables_spec.rb +30 -30
  82. data/spec/cqldump_spec.rb +4 -4
  83. data/spec/helpers/array_matcher.rb +32 -27
  84. data/spec/helpers/diff_matcher.rb +6 -26
  85. data/spec/helpers/file_matcher.rb +41 -32
  86. data/spec/helpers/parse_to_ast_matcher.rb +76 -0
  87. data/spec/helpers/string_matcher.rb +32 -31
  88. data/spec/norma_cql_spec.rb +1 -1
  89. data/spec/norma_ruby_spec.rb +1 -1
  90. data/spec/norma_ruby_sql_spec.rb +1 -1
  91. data/spec/norma_sql_spec.rb +3 -1
  92. data/spec/norma_tables_spec.rb +1 -1
  93. data/spec/ruby_api_spec.rb +23 -0
  94. data/spec/spec_helper.rb +5 -4
  95. metadata +66 -66
  96. data/examples/CQL/OrienteeringER.cql +0 -58
  97. data/lib/activefacts/api.rb +0 -44
  98. data/lib/activefacts/api/concept.rb +0 -410
  99. data/lib/activefacts/api/constellation.rb +0 -128
  100. data/lib/activefacts/api/entity.rb +0 -256
  101. data/lib/activefacts/api/instance.rb +0 -60
  102. data/lib/activefacts/api/instance_index.rb +0 -80
  103. data/lib/activefacts/api/numeric.rb +0 -167
  104. data/lib/activefacts/api/role.rb +0 -80
  105. data/lib/activefacts/api/role_proxy.rb +0 -70
  106. data/lib/activefacts/api/role_values.rb +0 -117
  107. data/lib/activefacts/api/standard_types.rb +0 -87
  108. data/lib/activefacts/api/support.rb +0 -65
  109. data/lib/activefacts/api/value.rb +0 -135
  110. data/lib/activefacts/api/vocabulary.rb +0 -82
  111. data/spec/api/autocounter.rb +0 -82
  112. data/spec/api/constellation.rb +0 -130
  113. data/spec/api/entity_type.rb +0 -103
  114. data/spec/api/instance.rb +0 -461
  115. data/spec/api/roles.rb +0 -124
  116. data/spec/api/value_type.rb +0 -112
  117. data/spec/api_spec.rb +0 -13
  118. data/spec/cql/matching_spec.rb +0 -517
  119. data/spec/cql/unit_spec.rb +0 -394
  120. 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 Binding has one or more RoleRef's.
6
- # A Binding is for a single Concept, normally related to just one Role,
7
- # and the references (RoleRefs) to it will normally be the concept name
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 Binding will have some RoleRefs with the same adjectives,
12
- # and one or more RoleRefs with no adjectives - this is called "loose binding".
13
- class Binding
14
- attr_reader :player # The Concept (object type)
15
- attr_reader :refs # an array of the RoleRefs
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 binding
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
- attr_reader :bindings # The Bindings in this declaration
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
- @bindings = {}
53
+ @variables = {}
50
54
  @player_by_role_name = {}
55
+ @left_contraction_allowed = false
51
56
  end
52
57
 
53
- # Look up this concept by its name
54
- def concept(name)
58
+ # Look up this object_type by its name
59
+ def object_type(name)
55
60
  constellation = @vocabulary.constellation
56
- player = constellation.Concept[[@vocabulary_identifier, name]]
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, :source
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 Concept < Definition
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 < Concept
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
- raise "REVISIT: ValueType units are recognised but not yet compiled" unless @unit.empty?
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/Concepts'
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, n = "#{term} #{adj}", term) && debug(:context, "new role '#{term} -#{adj}'")
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
- block.call(node) if block
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 Concepts that are not tables as well
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.all_concept.sort_by{|c| c.name}.each do |o|
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 concept #:nodoc:
44
- indices = concept.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 "#{concept.name}: #{
49
- # "[#{concept.indices.size} indices] "
48
+ puts "#{object_type.name}: #{
49
+ # "[#{object_type.indices.size} indices] "
50
50
  # } #{
51
- concept.columns.sort_by do |column|
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
- !(concept = other_roles[0].concept).is_a?(ActiveFacts::Metamodel::EntityType) || # Counterpart is not an ET
88
- (pi = concept.preferred_identifier).role_sequence.all_role_ref.size != 1 || # Entity PI has > 1 roles
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} is written as #{(o.supertype || o).name}#{ parameters }#{
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].concept == entity_type ? 0 : 1)] and
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.concept and
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.concept.name} ?},'') and
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.concept == value_role.concept }
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.concept == entity_type}}.compact
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
- return " identified by its #{value_residual}#{constraint_text}#{mapping_pragma(entity_type)}" +
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.concept == entity_type}}.compact
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
- " identified by #{ irn*" and " }" +
267
- mapping_pragma(entity_type) +
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 #{ o.supertypes.map(&:name)*", " }"
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
- print " a kind of #{ supertypes.map(&:name)*", " }" unless supertypes.empty?
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
- verbaliser.create_subscripts
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
- puts(fact_readings_with_constraints(verbaliser, fact_type)*",\n\t"+";")
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].concept){|subclass, role|
348
- next nil unless subclass and EntityType === role.concept
349
- role.concept.supertypes_transitive.include?(subclass) ? role.concept : nil
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.concept})}
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 concepts.
513
- def common_supertype(concepts)
514
- common = concepts[0].supertypes_transitive
515
- concepts[1..-1].each do |concept|
516
- common &= concept.supertypes_transitive
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.concept
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 ]