activefacts-cql 1.8.3 → 1.9.1

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.
@@ -44,7 +44,7 @@ module ActiveFacts
44
44
  unless @conditions.empty? and @returning.empty?
45
45
  trace :query, "building query for derived fact type (returning #{@returning}) with #{@conditions.size} conditions: (#{@conditions.map{|c|c.inspect}*', '})" do
46
46
  @query = build_variables(@conditions.flatten)
47
- @roles_by_binding = build_all_steps(@conditions)
47
+ @roles_by_binding = build_all_steps(@query, @conditions)
48
48
  @query.validate
49
49
  @query
50
50
  end
@@ -108,7 +108,9 @@ module ActiveFacts
108
108
  # Prepare to objectify the fact type (so readings for link fact types can be created)
109
109
  if @name
110
110
  entity_type = @vocabulary.valid_entity_type_name(@name)
111
- raise "You can't objectify #{@name}, it already exists" if entity_type
111
+
112
+ # REVISIT disable name check -- GSP 5 Jul 2017
113
+ raise "You can't objectify #{@name}, it already exists" if entity_type && false
112
114
  @entity_type = @constellation.EntityType(@vocabulary, @name, :fact_type => @fact_type, :concept => :new)
113
115
  end
114
116
 
@@ -8,8 +8,11 @@ module ActiveFacts
8
8
  query = @constellation.Query(:new)
9
9
  all_bindings_in_clauses(clauses_list).
10
10
  each do |binding|
11
- trace :query, "Creating variable #{query.all_variable.size} for #{binding.inspect}"
12
- binding.variable = @constellation.Variable(query, query.all_variable.size, :object_type => binding.player)
11
+ var_name = (r = binding.refs.select{|r| r.is_a?(Reference)}.first) ? r.var_name : nil
12
+ trace :query, "Creating variable #{query.all_variable.size} for #{binding.inspect} with role_name #{var_name}"
13
+ binding.variable = @constellation.Variable(
14
+ query, query.all_variable.size, :object_type => binding.player, role_name: var_name
15
+ )
13
16
  if literal = binding.refs.detect{|r| r.literal}
14
17
  if literal.kind_of?(ActiveFacts::CQL::Compiler::Reference)
15
18
  # REVISIT: Fix this crappy ad-hoc polymorphism hack
@@ -23,21 +26,21 @@ module ActiveFacts
23
26
  end
24
27
  end
25
28
 
26
- def build_all_steps(clauses_list)
29
+ def build_all_steps(query, clauses_list)
27
30
  roles_by_binding = {}
28
31
  trace :query, "Building steps" do
29
32
  clauses_list.each do |clause|
30
- build_step(clause, roles_by_binding)
33
+ build_step(query, clause, roles_by_binding)
31
34
  end
32
35
  end
33
36
  roles_by_binding
34
37
  end
35
38
 
36
- def build_step clause, roles_by_binding = {}, parent_variable = nil
39
+ def build_step query, clause, roles_by_binding = {}, parent_variable = nil
37
40
  return unless clause.refs.size > 0 # Empty clause... really?
38
41
 
39
42
  step = @constellation.Step(
40
- :guid => :new,
43
+ query, query.all_step.size,
41
44
  :fact_type => clause.fact_type,
42
45
  :alternative_set => nil,
43
46
  :is_disallowed => clause.certainty == false,
@@ -56,7 +59,7 @@ module ActiveFacts
56
59
  objectification_step = nil
57
60
  if ref.nested_clauses
58
61
  ref.nested_clauses.each do |nested_clause|
59
- objectification_step = build_step nested_clause, roles_by_binding
62
+ objectification_step = build_step(query, nested_clause, roles_by_binding)
60
63
  if ref.binding.player.is_a?(ActiveFacts::Metamodel::EntityType) and
61
64
  ref.binding.player.fact_type == nested_clause.fact_type
62
65
  objectification_step.objectification_variable = binding.variable
@@ -72,9 +75,10 @@ module ActiveFacts
72
75
  if binding.variable.object_type.common_supertype(role.object_type)
73
76
  # REVISIT: there's an implicit subtyping step here, create it; then always raise the error here.
74
77
  # I don't want to do this for now because the verbaliser will always verbalise all steps.
75
- raise "Disallowing implicit subtyping step from #{role.object_type.name} to #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
78
+ # raise "Disallowing implicit subtyping step from #{role.object_type.name} to #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
79
+ else
80
+ raise "A #{role.object_type.name} cannot satisfy #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
76
81
  end
77
- raise "A #{role.object_type.name} cannot satisfy #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
78
82
  end
79
83
 
80
84
  trace :query, "Creating Play for #{ref.inspect}"
@@ -13,7 +13,7 @@ module ActiveFacts
13
13
  class Binding
14
14
  attr_reader :player # The ObjectType (object type)
15
15
  attr_reader :refs # an array of the References
16
- attr_reader :role_name
16
+ attr_accessor :role_name
17
17
  attr_accessor :rebound_to # Loose binding may set this to another binding
18
18
  attr_reader :variable
19
19
  attr_accessor :instance # When binding fact instances, the instance goes here
@@ -35,7 +35,7 @@ module ActiveFacts
35
35
  def <=>(other)
36
36
  key <=> other.key
37
37
  end
38
-
38
+
39
39
  def variable= v
40
40
  @variable = v # A place for a breakpoint :)
41
41
  end
@@ -111,15 +111,17 @@ module ActiveFacts
111
111
  end
112
112
 
113
113
  class Vocabulary < Definition
114
- def initialize name
114
+ def initialize name, is_transform, version_number
115
115
  @name = name
116
+ @is_transform = is_transform
117
+ @version_number = version_number
116
118
  end
117
119
 
118
120
  def compile
119
121
  if @constellation.Vocabulary.size > 0
120
122
  @constellation.Topic @name
121
123
  else
122
- @constellation.Vocabulary @name
124
+ @constellation.Vocabulary(@name, is_transform: @is_transform, version_number: @version_number)
123
125
  end
124
126
  end
125
127
 
@@ -129,9 +131,11 @@ module ActiveFacts
129
131
  end
130
132
 
131
133
  class Import < Definition
132
- def initialize parser, name, alias_hash
134
+ def initialize parser, name, import_role, version_pattern, alias_hash
133
135
  @parser = parser
134
136
  @name = name
137
+ @import_role = import_role
138
+ @version_pattern = version_pattern
135
139
  @alias_hash = alias_hash
136
140
  end
137
141
 
@@ -140,7 +144,7 @@ module ActiveFacts
140
144
  end
141
145
 
142
146
  def compile
143
- @parser.compile_import(@name, @alias_hash)
147
+ @parser.compile_import(@name, @import_role, @alias_hash)
144
148
  end
145
149
  end
146
150
 
@@ -0,0 +1,136 @@
1
+ #
2
+ # ActiveFacts CQL Parser.
3
+ # Compiler classes relating to Transform Rules.
4
+ #
5
+ # Copyright (c) 2017 Factil Pty Ltd. Read the LICENSE file.
6
+ #
7
+ module ActiveFacts
8
+ module CQL
9
+ class Compiler < ActiveFacts::CQL::Parser
10
+
11
+ class TransformRule < Definition
12
+ attr_accessor :compound_matching
13
+
14
+ def initialize compound_matching
15
+ @compound_matching = compound_matching
16
+ end
17
+
18
+ def compile
19
+ context = CompilationContext.new(@vocabulary)
20
+ transform_matching = @compound_matching.compile(context)
21
+ @constellation.TransformRule(:new, :compound_matching => transform_matching)
22
+ end
23
+ end
24
+
25
+ def self.build_transform_target_refs context, targ_term_list, transform_rule
26
+ vocabulary_identifier = context.vocabulary.identifying_role_values
27
+ constellation = context.vocabulary.constellation
28
+
29
+ targ_term_list.flatten!
30
+ (0 ... targ_term_list.size).each do |idx|
31
+ ref = targ_term_list[idx]
32
+ if (target_ot = constellation.ObjectType[[vocabulary_identifier, ref.term]]).nil?
33
+ raise "Target object '#{ref.term}' of transformation must be a valid object type"
34
+ end
35
+ constellation.TransformTargetRef(
36
+ transform_rule, idx, :object_type => target_ot,
37
+ :leading_adjective => ref.leading_adjective, :trailing_adjective => ref.trailing_adjective
38
+ )
39
+ end
40
+ end
41
+
42
+ class CompoundMatching
43
+ attr_accessor :targ_term_list, :transform_query, :transform_matchings
44
+
45
+ def initialize targ_term_list, transform_query, transform_matchings
46
+ @targ_term_list = targ_term_list
47
+ @transform_query = transform_query
48
+ @transform_matchings = transform_matchings
49
+ end
50
+
51
+ def compile(context)
52
+ compound_rule = nil
53
+ vocabulary_identifier = context.vocabulary.identifying_role_values
54
+ constellation = context.vocabulary.constellation
55
+
56
+ source_ot = nil
57
+ source_query = nil
58
+ if @transform_query.is_a?(ActiveFacts::CQL::Compiler::Reference)
59
+ if (source_ot = constellation.ObjectType[[vocabulary_identifier, @transform_query.term]]).nil?
60
+ raise "Invalid source object '#{@transform_query.term}' for '#{@targ_term.term}' transformation"
61
+ end
62
+ elsif @transform_query.is_a?(Array)
63
+ query = Query.new(nil, @transform_query.flatten)
64
+ query.constellation = constellation
65
+ query.vocabulary = context.vocabulary
66
+ if (source_query = query.compile).nil?
67
+ raise "Invalid source query for '#{@targ_term.term}' transformation"
68
+ end
69
+ end
70
+
71
+ compound_rule = constellation.CompoundMatching(
72
+ :new, :source_object_type => source_ot, :source_query => source_query
73
+ )
74
+ ActiveFacts::CQL::Compiler.build_transform_target_refs(context, @targ_term_list, compound_rule)
75
+
76
+ @transform_matchings.each do |tr|
77
+ trule = tr.compile(context)
78
+ trule.compound_matching = compound_rule
79
+ end
80
+
81
+ compound_rule
82
+ end
83
+ end
84
+
85
+ class SimpleMatching
86
+ attr_accessor :targ_term_list, :transform_expr
87
+
88
+ def initialize targ_term_list, transform_expr
89
+ @targ_term_list = targ_term_list
90
+ @transform_expr = transform_expr
91
+ end
92
+
93
+ def compile(context)
94
+ vocabulary_identifier = context.vocabulary.identifying_role_values
95
+ constellation = context.vocabulary.constellation
96
+
97
+ expr = transform_expr ? transform_expr.compile(context) : nil
98
+ simple_rule = constellation.SimpleMatching(:new, :expression => expr)
99
+ ActiveFacts::CQL::Compiler.build_transform_target_refs(context, @targ_term_list, simple_rule)
100
+
101
+ simple_rule
102
+ end
103
+ end
104
+
105
+ class ExpressionTermList
106
+ attr_accessor :term_list
107
+
108
+ def initialize term_list
109
+ @term_list = term_list
110
+ end
111
+
112
+ def compile(context)
113
+ vocabulary_identifier = context.vocabulary.identifying_role_values
114
+ constellation = context.vocabulary.constellation
115
+
116
+ expression = context.vocabulary.constellation.Expression(:new, :expression_type => 'Role')
117
+
118
+ @term_list.flatten!
119
+ (0 ... @term_list.size).each do |idx|
120
+ ref = term_list[idx]
121
+ if (object_type = constellation.ObjectType[[vocabulary_identifier, ref.term]]).nil?
122
+ raise "Object '#{ref.term}' of transformation must be a valid object type"
123
+ end
124
+ constellation.ExpressionObjectRef(
125
+ expression, idx, :object_type => object_type,
126
+ :leading_adjective => ref.leading_adjective, :trailing_adjective => ref.trailing_adjective
127
+ )
128
+ end
129
+
130
+ expression
131
+ end
132
+ end
133
+
134
+ end
135
+ end
136
+ end
@@ -119,6 +119,8 @@ module ActiveFacts
119
119
  @term = @term_part if t[@term_part]
120
120
  @global_term = (t = t[@term_part]) == true ? @term_part : t
121
121
  trace :context, "saving context #{@term}/#{@global_term}"
122
+ # trace :context, "@terms =\n\t#{@terms.map{|k,v| "#{k} => #{v}"} * "\n\t"}"
123
+ # trace :context, "@role_names =\n\t#{@role_names.map{|k,v| "#{k} => #{v}"} * "\n\t"}"
122
124
  @context_saver.context = {:term => @term, :global_term => @global_term }
123
125
  end
124
126
  end
@@ -128,8 +130,49 @@ module ActiveFacts
128
130
  def term_complete?
129
131
  return true if @allowed_forward_terms.include?(@term)
130
132
  return true if system_term(@term)
131
- (t = @terms[@term] and t[@term]) or
132
- (t = @role_names[@term] and t[@term])
133
+ result = ((t = @terms[@term] and t[@term]) or (t = @role_names[@term] and t[@term]))
134
+ trace :context, "term #{@term} is #{result ? '' : 'in'}complete"
135
+ result
136
+ end
137
+
138
+ def global_term_starts?(s, context_saver)
139
+ @term = @global_term = nil
140
+
141
+ @term_part = s
142
+ @context_saver = context_saver
143
+ t = @terms[s] || system_term(s)
144
+ if t
145
+ # s is a prefix of the keys of t.
146
+ if t[s]
147
+ @global_term = @term = @term_part
148
+ @context_saver.context = {:term => @term, :global_term => @global_term }
149
+ end
150
+ trace :context, "Term #{t[s] ? "is" : "starts"} '#{@term_part}'"
151
+ elsif @allowed_forward_terms.include?(@term_part)
152
+ @term = @term_part
153
+ @context_saver.context = {:term => @term, :global_term => @term }
154
+ trace :context, "Term #{s} is an allowed forward"
155
+ return true
156
+ end
157
+ t
158
+ end
159
+
160
+ def global_term_continues?(s)
161
+ @term_part = "#{@term_part} #{s}"
162
+ t = @terms[@term_part]
163
+ if t
164
+ trace :context, "Multi-word term #{t[@term_part] ? 'ends at' : 'continues to'} #{@term_part.inspect}"
165
+
166
+ # Record the name of the full term and the underlying global term:
167
+ if t[@term_part]
168
+ @term = @term_part if t[@term_part]
169
+ @global_term = (t = t[@term_part]) == true ? @term_part : t
170
+ trace :context, "saving context #{@term}/#{@global_term}"
171
+ # trace :context, "@terms =\n\t#{@terms.map{|k,v| "#{k} => #{v}"} * "\n\t"}"
172
+ @context_saver.context = {:term => @term, :global_term => @global_term }
173
+ end
174
+ end
175
+ t
133
176
  end
134
177
 
135
178
  def system_term(s)
@@ -11,6 +11,7 @@ require 'activefacts/cql/parser/Terms'
11
11
  require 'activefacts/cql/parser/ObjectTypes'
12
12
  require 'activefacts/cql/parser/ValueTypes'
13
13
  require 'activefacts/cql/parser/FactTypes'
14
+ require 'activefacts/cql/parser/TransformRules'
14
15
  require 'activefacts/cql/parser/Context'
15
16
 
16
17
  module ActiveFacts
@@ -22,6 +23,7 @@ module ActiveFacts
22
23
  include ObjectTypes
23
24
  include ValueTypes
24
25
  include FactTypes
26
+ include TransformRules
25
27
  include Context
26
28
 
27
29
  rule cql_file
@@ -58,20 +60,35 @@ module ActiveFacts
58
60
  / object_type
59
61
  / informal_description
60
62
  / query
63
+ / transform_rule
61
64
  / s ';' s { def ast; nil; end }
62
65
  end
63
66
 
64
67
  rule vocabulary_definition
65
- s vocabulary S vocabulary_name s ';'
68
+ schema_definition /
69
+ transform_definition
70
+ end
71
+
72
+ rule schema_definition
73
+ s ( schema / topic / vocabulary ) S vocabulary_name vn:version_number? s ';'
74
+ {
75
+ def ast
76
+ Compiler::Vocabulary.new(vocabulary_name.value, false, vn.empty? ? nil : vn.value)
77
+ end
78
+ }
79
+ end
80
+
81
+ rule transform_definition
82
+ s transform S vocabulary_name vn:version_number? s ';'
66
83
  {
67
84
  def ast
68
- Compiler::Vocabulary.new(vocabulary_name.value)
85
+ Compiler::Vocabulary.new(vocabulary_name.value, true, vn.empty? ? nil : vn.value)
69
86
  end
70
87
  }
71
88
  end
72
89
 
73
90
  rule vocabulary_name
74
- id tail:(S id)*
91
+ id tail:(S !version id)*
75
92
  {
76
93
  def node_type; :vocabulary; end
77
94
  def value
@@ -81,10 +98,47 @@ module ActiveFacts
81
98
  end
82
99
 
83
100
  rule import_definition
84
- s import S vocabulary_name alias_list ';'
101
+ s import i:import_role? S vocabulary_name vp:version_pattern? alias_list ';'
85
102
  {
86
103
  def ast
87
- Compiler::Import.new(import.input.parser, vocabulary_name.value, alias_list.value)
104
+ Compiler::Import.new(
105
+ import.input.parser, vocabulary_name.value, i.empty? ? "topic" : i.value, vp.empty? ? nil : vp.value, alias_list.value
106
+ )
107
+ end
108
+ }
109
+ end
110
+
111
+ rule version_number
112
+ S version S version_number_string
113
+ {
114
+ def value
115
+ version_number_string.text_value
116
+ end
117
+ }
118
+ end
119
+
120
+ rule version_pattern
121
+ S version S version_pattern_string
122
+ {
123
+ def value
124
+ version_pattern_string.text_value
125
+ end
126
+ }
127
+ end
128
+
129
+ rule version_number_string
130
+ [0-9]+ '.' [0-9]+ '.' [0-9]+ ('-' [0-9A-Za-z-]+ ('.' [0-9A-Za-z-]+ )* )?
131
+ end
132
+
133
+ rule version_pattern_string
134
+ [0-9]+ ('.' [0-9]+ ('.' [0-9]+ ('-' [0-9A-Za-z-]+ ('.' [0-9A-Za-z-]+ )* )? )? )?
135
+ end
136
+
137
+ rule import_role
138
+ S id
139
+ {
140
+ def value
141
+ id.text_value
88
142
  end
89
143
  }
90
144
  end
@@ -295,6 +295,7 @@ module ActiveFacts
295
295
  rule radix_point '.' end
296
296
  rule reflexive 'reflexive' !alphanumeric end
297
297
  rule returning 'returning' !alphanumeric end
298
+ rule schema 'schema' !alphanumeric end
298
299
  rule separate 'separate' !alphanumeric end
299
300
  rule so_that 'so' S that end
300
301
  rule static 'static' !alphanumeric end
@@ -304,10 +305,13 @@ module ActiveFacts
304
305
  rule then 'then' !alphanumeric end
305
306
  rule to 'to' !alphanumeric end
306
307
  rule to_avoid to s 'avoid' !alphanumeric end
308
+ rule topic 'topic' !alphanumeric end
309
+ rule transform 'transform' !alphanumeric end
307
310
  rule transient 'transient' !alphanumeric end
308
311
  rule transitive 'transitive' !alphanumeric end
309
312
  rule true 'true' !alphanumeric end
310
- rule vocabulary ('vocabulary' / 'schema') !alphanumeric end
313
+ rule version 'version' !alphanumeric end
314
+ rule vocabulary 'vocabulary' !alphanumeric end
311
315
  rule when 'when' !alphanumeric end
312
316
  rule where 'where' !alphanumeric end
313
317
  rule which 'which' !alphanumeric end