activefacts-cql 1.8.3 → 1.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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