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.
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 ]