activefacts-cql 1.8.1 → 1.8.2
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile +6 -5
- data/activefacts-cql.gemspec +4 -3
- data/lib/activefacts/cql/compiler.rb +29 -22
- data/lib/activefacts/cql/compiler/clause.rb +86 -84
- data/lib/activefacts/cql/compiler/constraint.rb +53 -53
- data/lib/activefacts/cql/compiler/entity_type.rb +28 -28
- data/lib/activefacts/cql/compiler/expression.rb +27 -27
- data/lib/activefacts/cql/compiler/fact.rb +37 -37
- data/lib/activefacts/cql/compiler/fact_type.rb +91 -85
- data/lib/activefacts/cql/compiler/informal.rb +48 -0
- data/lib/activefacts/cql/compiler/query.rb +52 -52
- data/lib/activefacts/cql/compiler/shared.rb +17 -17
- data/lib/activefacts/cql/compiler/value_type.rb +11 -11
- data/lib/activefacts/cql/parser/CQLParser.treetop +76 -56
- data/lib/activefacts/cql/parser/Context.treetop +15 -17
- data/lib/activefacts/cql/parser/Expressions.treetop +21 -21
- data/lib/activefacts/cql/parser/FactTypes.treetop +216 -216
- data/lib/activefacts/cql/parser/Language/English.treetop +136 -131
- data/lib/activefacts/cql/parser/Language/French.treetop +118 -118
- data/lib/activefacts/cql/parser/Language/Mandarin.treetop +111 -111
- data/lib/activefacts/cql/parser/LexicalRules.treetop +45 -45
- data/lib/activefacts/cql/parser/ObjectTypes.treetop +120 -120
- data/lib/activefacts/cql/parser/Terms.treetop +63 -63
- data/lib/activefacts/cql/parser/ValueTypes.treetop +71 -71
- data/lib/activefacts/cql/require.rb +8 -8
- data/lib/activefacts/cql/verbaliser.rb +88 -88
- data/lib/activefacts/cql/version.rb +1 -1
- data/lib/activefacts/input/cql.rb +7 -7
- metadata +12 -16
@@ -0,0 +1,48 @@
|
|
1
|
+
module ActiveFacts
|
2
|
+
module CQL
|
3
|
+
class Compiler < ActiveFacts::CQL::Parser
|
4
|
+
class InformalDefinition < Definition
|
5
|
+
def initialize kind, subject, phrases, text
|
6
|
+
@kind = kind
|
7
|
+
@subject = subject
|
8
|
+
@phrases = phrases
|
9
|
+
@text = text
|
10
|
+
end
|
11
|
+
|
12
|
+
def compile
|
13
|
+
@context = CompilationContext.new(@vocabulary)
|
14
|
+
case @kind
|
15
|
+
when :each
|
16
|
+
compile_object_description
|
17
|
+
when :when
|
18
|
+
compile_fact_type_description
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def apply_description concept_type
|
23
|
+
concept_type.concept.informal_description =
|
24
|
+
[ concept_type.concept.informal_description,
|
25
|
+
@text
|
26
|
+
].compact*".\n"
|
27
|
+
end
|
28
|
+
|
29
|
+
def compile_object_description
|
30
|
+
object_type = @context.object_type(@subject)
|
31
|
+
raise "Cannot add informal description of undefined object #{@subject.inspect}" unless object_type
|
32
|
+
apply_description object_type
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
def compile_fact_type_description
|
37
|
+
@clause = Compiler::Clause.new(@phrases) # Make the phrases into a clause
|
38
|
+
@context.bind [@clause] # Bind player names in the claise
|
39
|
+
fact_type = @clause.match_existing_fact_type @context
|
40
|
+
apply_description fact_type if fact_type
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -10,11 +10,11 @@ module ActiveFacts
|
|
10
10
|
each do |binding|
|
11
11
|
trace :query, "Creating variable #{query.all_variable.size} for #{binding.inspect}"
|
12
12
|
binding.variable = @constellation.Variable(query, query.all_variable.size, :object_type => binding.player)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
if literal = binding.refs.detect{|r| r.literal}
|
14
|
+
if literal.kind_of?(ActiveFacts::CQL::Compiler::Reference)
|
15
|
+
# REVISIT: Fix this crappy ad-hoc polymorphism hack
|
16
|
+
literal = literal.literal
|
17
|
+
end
|
18
18
|
unit = @constellation.Unit.detect{|k, v| [v.name, v.plural_name].include? literal.unit} if literal.unit
|
19
19
|
binding.variable.value = [literal.literal.to_s, literal.literal.is_a?(String), unit]
|
20
20
|
end
|
@@ -34,60 +34,60 @@ module ActiveFacts
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def build_step clause, roles_by_binding = {}, parent_variable = nil
|
37
|
-
|
37
|
+
return unless clause.refs.size > 0 # Empty clause... really?
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
step = @constellation.Step(
|
40
|
+
:guid => :new,
|
41
|
+
:fact_type => clause.fact_type,
|
42
|
+
:alternative_set => nil,
|
43
|
+
:is_disallowed => clause.certainty == false,
|
44
|
+
:is_optional => clause.certainty == nil
|
45
|
+
)
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
47
|
+
trace :query, "Creating Plays for #{clause.inspect} with #{clause.refs.size} refs" do
|
48
|
+
is_input = true
|
49
|
+
clause.refs.each do |ref|
|
50
|
+
# These refs are the Compiler::References, which have associated Metamodel::RoleRefs,
|
51
|
+
# but we need to create Plays for those roles.
|
52
|
+
# REVISIT: Plays may need to save residual_adjectives
|
53
|
+
binding = ref.binding
|
54
|
+
role = (ref && ref.role) || (ref.role_ref && ref.role_ref.role)
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
56
|
+
objectification_step = nil
|
57
|
+
if ref.nested_clauses
|
58
|
+
ref.nested_clauses.each do |nested_clause|
|
59
|
+
objectification_step = build_step nested_clause, roles_by_binding
|
60
|
+
if ref.binding.player.is_a?(ActiveFacts::Metamodel::EntityType) and
|
61
|
+
ref.binding.player.fact_type == nested_clause.fact_type
|
62
|
+
objectification_step.objectification_variable = binding.variable
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
if clause.is_naked_object_type
|
67
|
+
raise "#{self} lacks a proper objectification" if clause.refs[0].nested_clauses and !objectification_step
|
68
|
+
return objectification_step
|
69
|
+
end
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
71
|
+
if binding.variable.object_type != role.object_type # Type mismatch
|
72
|
+
if binding.variable.object_type.common_supertype(role.object_type)
|
73
|
+
# REVISIT: there's an implicit subtyping step here, create it; then always raise the error here.
|
74
|
+
# 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}"
|
76
|
+
end
|
77
|
+
raise "A #{role.object_type.name} cannot satisfy #{binding.variable.object_type.name} in #{clause.fact_type.default_reading.inspect}"
|
78
|
+
end
|
79
79
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
80
|
+
trace :query, "Creating Play for #{ref.inspect}"
|
81
|
+
play = @constellation.Play(:step => step, :role => role, :variable => binding.variable)
|
82
|
+
play.is_input = is_input
|
83
|
+
is_input = false
|
84
84
|
|
85
|
-
|
86
|
-
|
87
|
-
|
85
|
+
roles_by_binding[binding] = [role, play]
|
86
|
+
end
|
87
|
+
end
|
88
88
|
|
89
|
-
|
90
|
-
|
89
|
+
step
|
90
|
+
end
|
91
91
|
|
92
92
|
# Return the unique array of all bindings in these clauses, including in objectification steps
|
93
93
|
def all_bindings_in_clauses clauses
|
@@ -36,18 +36,18 @@ module ActiveFacts
|
|
36
36
|
key <=> other.key
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
39
|
+
def variable= v
|
40
|
+
@variable = v # A place for a breakpoint :)
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_ref ref
|
44
|
+
@refs << ref
|
45
|
+
ref
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete_ref ref
|
49
|
+
@refs.delete ref
|
50
|
+
end
|
51
51
|
end
|
52
52
|
|
53
53
|
class CompilationContext
|
@@ -77,7 +77,7 @@ module ActiveFacts
|
|
77
77
|
player ||= @player_by_role_name[name]
|
78
78
|
|
79
79
|
if !player && @allowed_forward_terms.include?(name)
|
80
|
-
|
80
|
+
@vocabulary.valid_entity_type_name(name) # No need for the result here, just no exceptional condition
|
81
81
|
player = constellation.EntityType(@vocabulary, name, :concept => :new)
|
82
82
|
end
|
83
83
|
|
@@ -117,10 +117,10 @@ module ActiveFacts
|
|
117
117
|
|
118
118
|
def compile
|
119
119
|
if @constellation.Vocabulary.size > 0
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
120
|
+
@constellation.Topic @name
|
121
|
+
else
|
122
|
+
@constellation.Vocabulary @name
|
123
|
+
end
|
124
124
|
end
|
125
125
|
|
126
126
|
def to_s
|
@@ -94,8 +94,8 @@ module ActiveFacts
|
|
94
94
|
@unit = unit
|
95
95
|
@value_constraint = value_constraint
|
96
96
|
@pragmas = pragmas
|
97
|
-
|
98
|
-
|
97
|
+
@context_note = context_note
|
98
|
+
@auto_assigned_at = auto_assigned_at
|
99
99
|
end
|
100
100
|
|
101
101
|
def compile
|
@@ -104,19 +104,19 @@ module ActiveFacts
|
|
104
104
|
# Create the base type unless it already exists:
|
105
105
|
base_type = nil
|
106
106
|
if (@base_type_name != @name)
|
107
|
-
|
107
|
+
unless base_type = @vocabulary.valid_value_type_name(@base_type_name)
|
108
108
|
base_type = @constellation.ValueType(@vocabulary, @base_type_name, :concept => :new)
|
109
109
|
return base_type if @base_type_name == @name
|
110
110
|
end
|
111
111
|
end
|
112
112
|
|
113
113
|
# Create and initialise the ValueType:
|
114
|
-
|
115
|
-
|
114
|
+
vt = @vocabulary.valid_value_type_name(@name) ||
|
115
|
+
@constellation.ValueType(@vocabulary, @name, :concept => :new)
|
116
116
|
vt.is_independent = true if @pragmas.delete('independent')
|
117
|
-
|
118
|
-
|
119
|
-
|
117
|
+
@pragmas.each do |p|
|
118
|
+
@constellation.ConceptAnnotation(:concept => vt.concept, :mapping_annotation => p)
|
119
|
+
end if @pragmas
|
120
120
|
vt.supertype = base_type if base_type
|
121
121
|
vt.length = length if length
|
122
122
|
vt.scale = scale if scale
|
@@ -148,9 +148,9 @@ module ActiveFacts
|
|
148
148
|
vt.value_constraint = @value_constraint.compile
|
149
149
|
end
|
150
150
|
|
151
|
-
|
152
|
-
|
153
|
-
|
151
|
+
if @context_note
|
152
|
+
@context_note.compile(@constellation, vt)
|
153
|
+
end
|
154
154
|
|
155
155
|
vt
|
156
156
|
end
|
@@ -37,16 +37,16 @@ module ActiveFacts
|
|
37
37
|
|
38
38
|
# Each definition has an ast() method that returns an instance of a subclass of Compiler::Definition
|
39
39
|
rule definition
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
40
|
+
definition_body s
|
41
|
+
{
|
42
|
+
def ast
|
43
|
+
definition_body.ast
|
44
|
+
end
|
45
|
+
|
46
|
+
def body
|
47
|
+
definition_body.text_value
|
48
|
+
end
|
49
|
+
}
|
50
50
|
end
|
51
51
|
|
52
52
|
rule definition_body
|
@@ -56,30 +56,31 @@ module ActiveFacts
|
|
56
56
|
/ constraint
|
57
57
|
/ unit_definition # REVISIT: Move this above the prescan?
|
58
58
|
/ object_type
|
59
|
-
|
60
|
-
|
59
|
+
/ informal_description
|
60
|
+
/ query
|
61
|
+
/ s ';' s { def ast; nil; end }
|
61
62
|
end
|
62
63
|
|
63
64
|
rule vocabulary_definition
|
64
65
|
s vocabulary S vocabulary_name s ';'
|
65
66
|
{
|
66
|
-
|
67
|
-
|
68
|
-
|
67
|
+
def ast
|
68
|
+
Compiler::Vocabulary.new(vocabulary_name.value)
|
69
|
+
end
|
69
70
|
}
|
70
71
|
end
|
71
72
|
|
72
73
|
rule vocabulary_name
|
73
|
-
|
74
|
-
|
74
|
+
id
|
75
|
+
{ def node_type; :vocabulary; end }
|
75
76
|
end
|
76
77
|
|
77
78
|
rule import_definition
|
78
79
|
s import S vocabulary_name alias_list ';'
|
79
80
|
{
|
80
|
-
|
81
|
-
|
82
|
-
|
81
|
+
def ast
|
82
|
+
Compiler::Import.new(import.input.parser, vocabulary_name.value, alias_list.value)
|
83
|
+
end
|
83
84
|
}
|
84
85
|
end
|
85
86
|
|
@@ -94,8 +95,27 @@ module ActiveFacts
|
|
94
95
|
end
|
95
96
|
|
96
97
|
rule alias_term
|
97
|
-
|
98
|
-
|
98
|
+
id
|
99
|
+
{ def node_type; :term; end }
|
100
|
+
end
|
101
|
+
|
102
|
+
rule informal_description
|
103
|
+
informally s ',' s
|
104
|
+
subject:(
|
105
|
+
signifier:each S term # Informal definition of an object type
|
106
|
+
/
|
107
|
+
signifier:when S reading:phrase+ s ',' # or a fact type
|
108
|
+
) s
|
109
|
+
text:(!(!. / '.' [ \t\r]* "\n") (string / .))* # Allow . inside a string
|
110
|
+
(!. / '.' [ \t\r]* "\n") # The description terminates in a fullstop at the end of a line, or EOF
|
111
|
+
{
|
112
|
+
def ast
|
113
|
+
kind = subject.signifier.text_value.to_sym
|
114
|
+
subject_name = (kind == :each ? subject.term.text_value : subject.reading.text_value)
|
115
|
+
phrases = subject.reading.elements.map(&:ast) if kind == :when
|
116
|
+
Compiler::InformalDefinition.new(kind, subject_name, phrases, text.text_value)
|
117
|
+
end
|
118
|
+
}
|
99
119
|
end
|
100
120
|
|
101
121
|
rule constraint
|
@@ -106,60 +126,60 @@ module ActiveFacts
|
|
106
126
|
# REVISIT: / value_constraint
|
107
127
|
end
|
108
128
|
|
129
|
+
# Adding enforcement to a constraint makes it deontic
|
109
130
|
rule enforcement
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
id
|
131
|
+
s '(' s
|
132
|
+
otherwise s
|
133
|
+
action:id s # An enforcement action, like SMS, email, log, alarm, etc.
|
134
|
+
a:agent? s
|
135
|
+
')' s
|
136
|
+
{
|
137
|
+
def ast; Compiler::Enforcement.new(action.text_value, a.empty? ? nil : a.text_value); end
|
138
|
+
}
|
139
|
+
/
|
140
|
+
''
|
141
|
+
{
|
142
|
+
def ast; nil; end
|
143
|
+
}
|
124
144
|
end
|
125
145
|
|
126
146
|
# presence constraint:
|
127
147
|
rule presence_constraint
|
128
|
-
|
148
|
+
(each_occurs_in_clauses / either_or)
|
129
149
|
{
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
150
|
+
def ast
|
151
|
+
Compiler::PresenceConstraint.new c, enforcement.ast, clauses_ast, role_list_ast, quantifier_ast
|
152
|
+
end
|
153
|
+
}
|
134
154
|
end
|
135
155
|
|
136
156
|
# set (exclusion, mandatory exclusion, complex equality) constraint
|
137
157
|
rule set_constraint
|
138
|
-
|
158
|
+
(for_each_how_many / either_or_not_both)
|
139
159
|
{
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
160
|
+
def ast
|
161
|
+
Compiler::SetExclusionConstraint.new c, enforcement.ast, clauses_ast, role_list_ast, quantifier_ast
|
162
|
+
end
|
163
|
+
}
|
144
164
|
end
|
145
165
|
|
146
166
|
rule subset_constraint
|
147
167
|
(a_only_if_b / if_b_then_a)
|
148
168
|
{
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
169
|
+
def ast
|
170
|
+
Compiler::SubsetConstraint.new c, enforcement.ast, [clauses.ast, r2.ast]
|
171
|
+
end
|
172
|
+
}
|
153
173
|
end
|
154
174
|
|
155
175
|
rule equality_constraint
|
156
|
-
|
176
|
+
if_and_only_if
|
157
177
|
{
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
178
|
+
def ast
|
179
|
+
all_clauses = [clauses.ast, *tail.elements.map{|e| e.clauses.ast }]
|
180
|
+
Compiler::SetEqualityConstraint.new c, enforcement.ast, all_clauses
|
181
|
+
end
|
182
|
+
}
|
163
183
|
end
|
164
184
|
|
165
185
|
end
|