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
@@ -9,9 +9,9 @@ Girl ID is written as Auto Counter;
9
9
  /*
10
10
  * Entity Types
11
11
  */
12
- Boy is identified by its ID [independent];
12
+ Boy is independent identified by its ID;
13
13
 
14
- Girl is identified by its ID [independent];
14
+ Girl is independent identified by its ID;
15
15
  Girl is going out with at most one Boy,
16
16
  Boy is going out with at most one Girl;
17
17
 
@@ -29,7 +29,7 @@ Recurring Schedule Id is written as Auto Counter;
29
29
  Satellite Message Id is written as Unsigned Integer(32);
30
30
  Seconds is written as Unsigned Integer(32);
31
31
  Serial Number is written as String(20);
32
- Service Type is written as String(15) [independent];
32
+ Service Type [independent] is written as String(15);
33
33
  Subscription Nr is written as Signed Integer(32);
34
34
  Switch Id is written as Auto Counter;
35
35
  Transaction Nr is written as Unsigned Integer(32);
@@ -84,7 +84,7 @@ Monitor is identified by its Id;
84
84
  Monitor monitors one Data Store;
85
85
  Monitor is disabled;
86
86
 
87
- Monitoring Application is identified by its Name [independent];
87
+ Monitoring Application is independent identified by its Name;
88
88
  Monitor is owned by one Monitoring Application;
89
89
 
90
90
  Network is identified by its Nr;
@@ -105,13 +105,14 @@ Notification Level is identified by its Nr;
105
105
  Notification Level has one Initial- Delay Duration;
106
106
  Notification Level has one Repeat-Duration;
107
107
 
108
- Notification Type is identified by its Name [independent];
108
+ Notification Type is independent identified by its Name;
109
109
 
110
110
  Provider Type is identified by its Id;
111
111
 
112
112
  Recurring Schedule is identified by its Id;
113
113
  Monitor has All- Exclusion Recurring Schedule,
114
114
  All- Exclusion Recurring Schedule applies to at most one Monitor;
115
+ Monitor (as IntegratingMonitor) has Integration- Exclusion Recurring Schedule; // Avoid ambiguity; this is a new fact type
115
116
  Monitor (as IntegratingMonitor) has Integration- Exclusion Recurring Schedule,
116
117
  Integration- Exclusion Recurring Schedule applies to at most one IntegratingMonitor;
117
118
  Recurring Schedule has one Duration;
@@ -146,6 +147,7 @@ Switch is one Revision-Version;
146
147
  Switch has one monitoring-Port;
147
148
  Switch (as Private Interface Switch) is on private-Network,
148
149
  private-Network connects to at most one Private Interface Switch;
150
+ Switch (as Public Interface Switch) is on public-Network; // Avoid ambiguity; this is a new fact type
149
151
  Switch (as Public Interface Switch) is on at least one public-Network,
150
152
  Network connects to at most one Public Interface Switch;
151
153
  Switch is backup messages;
@@ -208,11 +210,11 @@ Data Store Service has one Subscription;
208
210
  */
209
211
  either Company is client or Company is vendor but not both;
210
212
  for each Credential exactly one of these holds:
211
- Data Store(1) requires Credential,
213
+ Data Store(2) requires Credential,
212
214
  Data Store Service requires Credential,
213
215
  Vendor requires Credential,
214
216
  Data Store File Host System has Internal-Credential,
215
- Data Store(2) has Internal-Credential;
217
+ Data Store(1) has Internal-Credential;
216
218
  for each Network exactly one of these holds:
217
219
  Network is used by Host System,
218
220
  Company has Origin-Network,
@@ -225,6 +227,9 @@ either Host System runs Switch or Data Store has Legacy-Switch but not both;
225
227
  for each Network at most one of these holds:
226
228
  Network is ip_single,
227
229
  Network has Ending-IP;
230
+ Data Store Service (where Service is from Data Store) belongs to Client
231
+ if and only if
232
+ Client has default Data Store;
228
233
  Network has Ending IP
229
234
  if and only if
230
235
  Network is ip_range;
@@ -28,12 +28,12 @@ CEO runs Company,
28
28
  /*
29
29
  * Constraints:
30
30
  */
31
- either Employee reports to Manager(1) or Employee is a Manager(2) that is a CEO that runs Company but not both;
31
+ either Employee reports to Manager(2) or Employee is a Manager(1) that is a CEO that runs Company but not both;
32
32
  Employee is a Manager that is a CEO that runs Company
33
33
  if and only if
34
34
  Employee works for Company;
35
35
 
36
36
  // This constraint cannot be expressed in NORMA until it adds explicit join paths:
37
- Employee(1) reports to Manager that is a kind of Employee(2) that works for Company
37
+ Employee(2) reports to Manager that is a kind of Employee(1) that works for Company
38
38
  if and only if
39
- Employee(1) works for Company;
39
+ Employee(2) works for Company;
@@ -17,7 +17,7 @@ Event Date is identified by ymd where
17
17
  Event Date has one ymd,
18
18
  ymd is of at most one Event Date;
19
19
 
20
- Party is identified by its Id [independent];
20
+ Party is independent identified by its Id;
21
21
 
22
22
  Party Moniker is where
23
23
  Party is called one Party Name;
@@ -72,12 +72,14 @@ Purchase Order is to one Supplier,
72
72
  Transfer Request is identified by its ID;
73
73
  Dispatch Item is for at most one Transfer Request;
74
74
  Received Item is for at most one Transfer Request;
75
+ Transfer Request is for one Product;
76
+ Transfer Request is for one Quantity;
75
77
 
76
78
  Warehouse is identified by its ID;
77
79
  Purchase Order is to one Warehouse;
78
80
  Sales Order is from one Warehouse;
79
- Transfer Request is at most one from-Warehouse;
80
- Transfer Request is at most one to-Warehouse;
81
+ Transfer Request is from one Warehouse (as From Warehouse);
82
+ Transfer Request is to one Warehouse (as To Warehouse);
81
83
  Warehouse contains at least one Bin;
82
84
 
83
85
  Customer is a kind of Party;
@@ -11,7 +11,7 @@ module ActiveFacts
11
11
  include Language # One of the language modules provides this module
12
12
  include Expressions
13
13
  include Terms
14
- include Concepts
14
+ include ObjectTypes
15
15
  include ValueTypes
16
16
  include FactTypes
17
17
  include Context
@@ -47,21 +47,27 @@ module ActiveFacts
47
47
  / prescan # Always fails, but its side-effects are needed in the following
48
48
  / constraint
49
49
  / unit_definition # REVISIT: Move this above the prescan?
50
- / concept
50
+ / object_type
51
+ / query
51
52
  / s ';' s { def ast; nil; end }
52
53
  end
53
54
 
54
55
  rule vocabulary_definition
55
- s vocabulary S id s ';'
56
+ s vocabulary S vocabulary_name s ';'
56
57
  {
57
58
  def ast
58
- Compiler::Vocabulary.new(id.value)
59
+ Compiler::Vocabulary.new(vocabulary_name.value)
59
60
  end
60
61
  }
61
62
  end
62
63
 
64
+ rule vocabulary_name
65
+ id
66
+ { def node_type; :vocabulary; end }
67
+ end
68
+
63
69
  rule import_definition
64
- s import S id alias_list ';'
70
+ s import S vocabulary_name alias_list ';'
65
71
  {
66
72
  def ast
67
73
  Compiler::Import.new(id.value, alias_list.value)
@@ -71,7 +77,7 @@ module ActiveFacts
71
77
 
72
78
  # REVISIT: Need a way to define equivalent readings for fact types here (and in the metamodel)
73
79
  rule alias_list
74
- ( s ',' s alias S aliased_from:id S as S alias_to:id s )*
80
+ ( s ',' s alias S aliased_from:alias_term S as S alias_to:alias_term s )*
75
81
  {
76
82
  def value
77
83
  elements.inject({}){|h, e| h[e.aliased_from.value] = e.alias_to; h }
@@ -79,6 +85,11 @@ module ActiveFacts
79
85
  }
80
86
  end
81
87
 
88
+ rule alias_term
89
+ id
90
+ { def node_type; :term; end }
91
+ end
92
+
82
93
  rule constraint
83
94
  subset_constraint /
84
95
  equality_constraint /
@@ -101,84 +112,39 @@ module ActiveFacts
101
112
 
102
113
  # presence constraint:
103
114
  rule presence_constraint
104
- s 'each' s ('combination' S)? role_list s 'occurs' s quantifier s 'time' 's'? s enforcement 'in' s
105
- readings_list s
106
- c:context_note? ';'
115
+ (each_occurs_in_clauses / either_or)
107
116
  {
108
117
  def ast
109
- context_note = c.empty? ? nil : c.ast
110
- Compiler::PresenceConstraint.new context_note, enforcement.ast, readings_list.ast, role_list.ast, quantifier.ast
118
+ Compiler::PresenceConstraint.new c, enforcement.ast, clauses_ast, role_list_ast, quantifier_ast
111
119
  end
112
120
  }
113
121
  end
114
122
 
115
123
  # set (exclusion, mandatory exclusion, complex equality) constraint
116
124
  rule set_constraint
117
- s 'for' s 'each' s role_list s quantifier s 'of' s 'these' s 'holds' s enforcement ':' s
118
- readings_list s
119
- c:context_note? ';'
125
+ (for_each_how_many / either_or_not_both)
120
126
  {
121
127
  def ast
122
- context_note = c.empty? ? nil : c.ast
123
- Compiler::SetExclusionConstraint.new context_note, enforcement.ast, readings_list.ast, role_list.ast, quantifier.ast
128
+ Compiler::SetExclusionConstraint.new c, enforcement.ast, clauses_ast, role_list_ast, quantifier_ast
124
129
  end
125
130
  }
126
- /
127
- s either? s r1:readings s or s r2:readings exclusion:(but s not s both s)? c:context_note? enforcement ';'
128
- {
129
- def ast
130
- context_note = c.empty? ? nil : c.ast
131
-
132
- if exclusion.text_value.empty?
133
- Compiler::PresenceConstraint.new context_note, enforcement.ast, [r1.ast, r2.ast], nil, Compiler::Quantifier.new(1, nil)
134
- else
135
- quantifier = Compiler::Quantifier.new(*(exclusion.text_value.empty? ? [1, nil] : [1, 1]))
136
- #quantifier = Compiler::Quantifier.new(1, 1)
137
- Compiler::SetExclusionConstraint.new context_note, enforcement.ast, [r1.ast, r2.ast], nil, quantifier
138
- end
139
- end
140
- }
141
131
  end
142
132
 
143
133
  rule subset_constraint
144
- s readings s only s if s r2:readings s
145
- c:context_note? enforcement ';'
134
+ (a_only_if_b / if_b_then_a)
146
135
  {
147
136
  def ast
148
- context_note = c.empty? ? nil : c.ast
149
- Compiler::SubsetConstraint.new context_note, enforcement.ast, [readings.ast, r2.ast]
137
+ Compiler::SubsetConstraint.new c, enforcement.ast, [clauses.ast, r2.ast]
150
138
  end
151
139
  }
152
140
  end
153
141
 
154
142
  rule equality_constraint
155
- s readings s tail:( if s and s only s if s readings s)+
156
- c:context_note? enforcement ';'
157
- {
158
- def ast
159
- context_note = c.empty? ? nil : c.ast
160
- all_readings = [readings.ast, *tail.elements.map{|e| e.readings.ast }]
161
- Compiler::SetEqualityConstraint.new context_note, enforcement.ast, all_readings
162
- end
163
- }
164
- end
165
-
166
- rule readings_list
167
- readings tail:( ',' s readings )*
168
- {
169
- def ast
170
- [readings.ast, *tail.elements.map{|e| e.readings.ast }]
171
- end
172
- }
173
- end
174
-
175
- rule readings
176
- reading s tail:( and s reading s )*
143
+ if_and_only_if
177
144
  {
178
145
  def ast
179
- readings = reading.ast
180
- tail.elements.map{|e| readings += e.reading.ast }
181
- readings
146
+ all_clauses = [clauses.ast, *tail.elements.map{|e| e.clauses.ast }]
147
+ Compiler::SetEqualityConstraint.new c, enforcement.ast, all_clauses
182
148
  end
183
149
  }
184
150
  end
@@ -29,14 +29,24 @@ module ActiveFacts
29
29
  end
30
30
 
31
31
  rule context_type
32
- because { def value; 'because'; end } /
32
+ because s { def value; 'because'; end } /
33
33
  as_opposed_to { def value; 'as_opposed_to'; end } /
34
34
  so_that { def value; 'so_that'; end } /
35
35
  to_avoid { def value; 'to_avoid'; end }
36
36
  end
37
37
 
38
+ # An enforcement action, like SMS, email, log, alarm, etc.
39
+ rule action
40
+ id
41
+ end
42
+
38
43
  rule discussion
39
- '(' discussion ')' / (!( [()] / ',' as_agreed_by) .)*
44
+ (
45
+ '(' discussion ')' / (!( [()] / ',' as_agreed_by) .)*
46
+ )
47
+ {
48
+ def node_type; :linking; end
49
+ }
40
50
  end
41
51
  end
42
52
  end
@@ -7,22 +7,6 @@
7
7
  module ActiveFacts
8
8
  module CQL
9
9
  grammar Expressions
10
- rule comparison
11
- e1:expression s
12
- comparator s e2:expression
13
- {
14
- def ast
15
- Compiler::Comparison.new comparator.text_value, e1.ast, e2.ast
16
- end
17
- }
18
- end
19
-
20
- rule comparator
21
- '<=' / '<' / '=' / '>=' / '>'
22
- # REVISIT: These words occur in readings, find alternates:
23
- # / matches / includes
24
- end
25
-
26
10
  rule expression
27
11
  sum
28
12
  end
@@ -31,7 +15,11 @@ module ActiveFacts
31
15
  t0:product s tail:( op:add_op s t1:product s )*
32
16
  {
33
17
  def ast
34
- Compiler::Sum.new(t0.ast, *tail.elements.map{|e| e.op.text_value == '-' ? Compiler::Negate.new(e.t1.ast) : e.t1.ast})
18
+ if tail.elements.empty?
19
+ t0.ast
20
+ else
21
+ Compiler::Sum.new(t0.ast, *tail.elements.map{|e| e.op.text_value == '-' ? Compiler::Negate.new(e.t1.ast) : e.t1.ast})
22
+ end
35
23
  end
36
24
  }
37
25
  end
@@ -44,7 +32,11 @@ module ActiveFacts
44
32
  f0:factor s tail:( op:mul_op s f1:factor s )*
45
33
  {
46
34
  def ast
47
- Compiler::Product.new(f0.ast, *tail.elements.map{|e| e.op.text_value != '*' ? Compiler::DivideBy.new(e.op.text_value, e.f1.ast) : e.t1.ast})
35
+ if tail.elements.empty?
36
+ f0.ast
37
+ else
38
+ Compiler::Product.new(f0.ast, *tail.elements.map{|e| e.op.text_value != '*' ? Compiler::Reciprocal.new(e.op.text_value, e.f1.ast) : e.f1.ast})
39
+ end
48
40
  end
49
41
  }
50
42
  end
@@ -61,19 +53,11 @@ module ActiveFacts
61
53
  end
62
54
 
63
55
  rule derived_variable
64
- variable:term s p:function_call*
56
+ variable:term s role_id:(role_name / subscript )?
65
57
  {
66
- def ast
67
- Compiler::FunctionCallChain.new(term.ast, *p.elements.each{|f| f.ast})
68
- end
69
- }
70
- end
71
-
72
- rule function_call
73
- '.' s func:id s param_list:( '(' s params:( p0:expression s tail:( ',' s p1:expression s )* )? ')' s )?
74
- {
75
- def ast
76
- Compiler::FunctionCall.new(func.value, *param_list.elements.map{|param| param.ast })
58
+ def ast quantifier = nil, value_constraint = nil, literal = nil, nested_clauses = nil
59
+ role_name = role_id.empty? ? nil : role_id.value
60
+ variable.ast(quantifier, nil, role_name, value_constraint, literal, nested_clauses)
77
61
  end
78
62
  }
79
63
  end
@@ -7,100 +7,93 @@
7
7
  module ActiveFacts
8
8
  module CQL
9
9
  grammar FactTypes
10
+ rule query
11
+ s joined_clauses r:returning_clause? '?'
12
+ {
13
+ def ast
14
+ Compiler::FactType.new nil, [], joined_clauses.ast, (r.empty? ? nil : r)
15
+ end
16
+ }
17
+ end
18
+
10
19
  rule named_fact_type
11
20
  s term_definition_name
12
- s is s mapping_pragmas where # REVISIT: Need a place to put mapping pragmas like [independent]
21
+ mapping_pragmas is_where
13
22
  anonymous_fact_type
14
23
  {
15
24
  def ast
16
25
  ft = anonymous_fact_type.ast
17
26
  ft.name = term_definition_name.value
27
+ pragmas = mapping_pragmas.value
28
+ pragmas << 'independent' if is_where.independent
29
+ ft.pragmas = pragmas
18
30
  ft
19
31
  end
20
32
  }
21
33
  end
22
34
 
23
35
  rule anonymous_fact_type
24
- fact_clause
25
- ftail:( (',' / and ) s fact_clause s )*
26
- ctail:( (':' / where) s c:conditions s)?
36
+ joined_clauses
37
+ ctail:( (':' / where) s a:joined_clauses s)?
27
38
  returning_clause?
28
39
  s ';'
29
40
  {
30
41
  def ast
31
- readings = fact_clause.ast
32
- ftail.elements.each{|e| readings += e.fact_clause.ast }
33
- conditions = !ctail.empty? ? ctail.c.ast : []
42
+ clauses_ast = joined_clauses.ast
43
+ conditions = !ctail.empty? ? ctail.a.ast : []
34
44
  returning = respond_to?(:returning_clause) ? returning_clause.ast : nil
35
- if (conditions.empty? && readings.detect{|r| r.includes_literals})
45
+ value_derivation = clauses_ast.detect{|r| r.is_equality_comparison}
46
+ if !value_derivation and
47
+ conditions.empty? and
48
+ clauses_ast.detect{|r| r.includes_literals}
36
49
  raise "Fact instances may not contain conditions" unless conditions.empty? && !returning
37
- Compiler::Fact.new readings
38
- elsif (readings.size == 1 &&
39
- readings[0].phrases.size == 1 &&
40
- (popname = readings[0].phrases[0]) &&
41
- !popname.is_a?(Compiler::RoleRef) &&
50
+ Compiler::Fact.new clauses_ast
51
+ elsif (clauses_ast.size == 1 &&
52
+ clauses_ast[0].phrases.size == 1 &&
53
+ (popname = clauses_ast[0].phrases[0]) &&
54
+ !popname.is_a?(Compiler::VarRef) &&
42
55
  conditions.detect{|r| r.includes_literals}
43
56
  )
44
57
  Compiler::Fact.new conditions, popname
45
58
  else
46
- Compiler::FactType.new nil, readings, conditions, returning
59
+ Compiler::FactType.new nil, clauses_ast, conditions, returning
47
60
  end
48
61
  end
49
62
  }
50
63
  end
51
64
 
52
- rule fact_clauses
53
- fact_clause
54
- ftail:( (',' / and ) s fact_clause s )*
65
+ rule joined_clauses
66
+ qualified_clauses
67
+ ftail:( conjunction:(',' / and / or ) s qualified_clauses s )*
55
68
  {
56
69
  def ast
57
- readings = fact_clause.ast
58
- ftail.elements.each{|e| readings += e.fact_clause.ast }
59
- readings
70
+ clauses_ast = qualified_clauses.ast
71
+ ftail.elements.each{|e|
72
+ conjunction = e.conjunction.text_value
73
+ # conjunction = 'and' if conjunction == ',' # ',' means AND, but disallows left-contractions
74
+ clauses_ast += e.qualified_clauses.ast(conjunction)
75
+ }
76
+ clauses_ast
60
77
  end
61
78
  }
62
79
  end
63
80
 
64
81
  rule returning_clause
65
- returning return (',' return)*
82
+ returning s return (s ',' s return)*
66
83
  end
67
84
 
68
85
  rule return
69
- by order 'REVISIT: return'
86
+ ordering_prefix? phrase+
70
87
  end
71
88
 
72
- rule conditions
73
- head:condition s tail:( (',' s / and S) next:condition s )*
89
+ rule qualified_clauses
90
+ s q:qualifier? s contracted_clauses s p:post_qualifiers? s c:context_note?
74
91
  {
75
- def ast
76
- conditions = head.ast
77
- tail.elements.each{|i| conditions += i.next.ast}
78
- conditions
79
- end
80
- }
81
- end
82
-
83
- rule condition
84
- head:clause s # tail:(or S alternate:clause s )*
85
- {
86
- def ast
87
- head.ast
88
- # Compiler::Alternates.new(head.ast, *tail.elements.map{|i| i.alternate.ast})
89
- # REVISIT: alternate conditions are not yet implemented
90
- end
91
- }
92
- end
93
-
94
- rule clause
95
- # REVISIT: No context for comparisons, yet
96
- comparison / fact_clause
97
- end
98
-
99
- rule fact_clause
100
- s q:qualifier? s reading s p:post_qualifiers? s c:context_note?
101
- {
102
- def ast
103
- r = reading.ast # An array of readings
92
+ def ast(conjunction = nil)
93
+ r = contracted_clauses.ast # An array of clause asts
94
+ r[0].conjunction = conjunction
95
+ # pre-qualifiers apply to the first clause, post_qualifiers and context_note to the last
96
+ # REVISIT: This may be incorrect where the last is a nested clause
104
97
  r[0].qualifiers << q.text_value unless q.empty?
105
98
  r[-1].qualifiers += p.list unless p.empty?
106
99
  r[-1].context_note = c.ast unless c.empty?
@@ -127,104 +120,173 @@ module ActiveFacts
127
120
  intransitive / transitive / acyclic / symmetric / asymmetric / antisymmetric / reflexive / irreflexive
128
121
  end
129
122
 
130
- rule reading
123
+ rule clauses_list
124
+ clauses tail:( ',' s clauses )*
125
+ {
126
+ def ast
127
+ [clauses.ast, *tail.elements.map{|e| e.clauses.ast }]
128
+ end
129
+ }
130
+ end
131
+
132
+ rule clauses
133
+ contracted_clauses s tail:( and s contracted_clauses s )*
134
+ {
135
+ def ast
136
+ clauses = contracted_clauses.ast
137
+ tail.elements.map{|e| clauses += e.contracted_clauses.ast }
138
+ clauses
139
+ end
140
+ }
141
+ end
142
+
143
+ rule contracted_clauses
144
+ comparison
145
+ /
131
146
  (
132
- contracted_reading
147
+ contraction # A contraction will terminate this repetition by eating to the end
133
148
  /
134
- role # A role reference containing a term, perhaps with attached paraphernalia
135
- / # A hyphenated non-term. Important: no embedded spaces
136
- id tail:('-' !term id)+ s
137
- {
138
- def ast
139
- [id.value, *tail.elements.map{|e| e.id.value}]*"-"
140
- end
141
- }
142
- / # A normal non-term
143
- !non_role_word id s
144
- {
145
- def ast
146
- id.value
147
- end
148
- }
149
+ phrase
149
150
  )+
150
151
  {
151
152
  def ast
152
153
  asts = elements.map{ |r| r.ast }
153
- contraction = []
154
+ contracted_clauses = []
154
155
  qualifiers = []
155
- if asts[-1].is_a?(Array) # A contracted_reading (Array of [role, qualifiers, *readings])
156
- contraction = asts.pop # Pull off the contraction
157
- contracted_role = contraction.shift
158
- qualifiers = contraction.shift
159
- asts.push(contracted_role) # And replace it by the role removed from the contraction
156
+ if asts[-1].is_a?(Array) # A contraction (Array of [role, qualifiers, *clauses])
157
+ contracted_clauses = asts.pop # Pull off the contracted_clauses
158
+ contracted_role = contracted_clauses.shift
159
+ qualifiers = contracted_clauses.shift
160
+ asts.push(contracted_role) # And replace it by the role removed from the contracted_clauses
160
161
  end
161
- reading = Compiler::Reading.new(asts)
162
- reading.qualifiers += qualifiers
163
- [reading] + contraction
162
+ clause_ast = Compiler::Clause.new(asts, qualifiers)
163
+ [clause_ast] + contracted_clauses
164
164
  end
165
165
  }
166
166
  end
167
167
 
168
- # REVISIT: and later, similarly for contracted conditions (including comparisons)
169
- rule contracted_reading
170
- role p:post_qualifiers? that s q:qualifier? s reading s
168
+ rule contraction
169
+ reading_contraction /
170
+ condition_contraction
171
+ end
172
+
173
+ rule reading_contraction
174
+ role p:post_qualifiers? conjunction:(that/who) s q:qualifier? s contracted_clauses s
171
175
  {
172
176
  def ast
173
- # reading.ast will return an array of Readings, but the first reading is special. We must:
174
- # * prepend a new role
177
+ # contracted_clauses.ast will return an array of Clauses, but the first clause is special. We must:
178
+ # * prepend a new role (we get the Role to build *two* ast nodes)
175
179
  # * attach the qualifiers
176
- readings = reading.ast
177
- readings[0].phrases.unshift(role.ast)
178
- readings[0].qualifiers << q.text_value unless q.empty? # Add maybe/definitely
180
+ clauses_ast = contracted_clauses.ast
181
+ clauses_ast[0].conjunction = conjunction.text_value
182
+ clauses_ast[0].phrases.unshift(role.ast)
183
+ clauses_ast[0].qualifiers << q.text_value unless q.empty? # Add maybe/definitely
179
184
 
180
- # A contracted_reading returns an array containing:
185
+ # A contraction returns an array containing:
181
186
  # * a role AST
182
187
  # * a qualifiers array
183
- # * an array of Readings
184
- [role.ast, p.empty? ? [] : p.list] + readings
188
+ # * an array of Clauses
189
+ [role.ast, p.empty? ? [] : p.list] + clauses_ast
190
+ end
191
+ }
192
+ end
193
+
194
+ rule condition_contraction
195
+ role p:post_qualifiers? q:qualifier? s comparator s e2:expression
196
+ !phrase # The contracted_clauses must not continue here!
197
+ {
198
+ def ast
199
+ c = Compiler::Comparison.new(comparator.text_value, role.ast, e2.ast, q.empty? ? [] : [q.text_value])
200
+ c.conjunction = comparator.text_value
201
+ [ role.ast, p.empty? ? [] : p.list, c ]
202
+ end
203
+ }
204
+ end
205
+
206
+ rule comparison
207
+ e1:expression s q:qualifier? s comparator s contraction p:post_qualifiers?
208
+ {
209
+ def ast
210
+ role, qualifiers, *clauses_ast = *contraction.ast
211
+ clauses_ast[0].qualifiers += p.list unless p.empty? # apply post_qualifiers to the contracted clause
212
+ # clauses_ast[0].conjunction = 'and' # AND is implicit for a contraction
213
+ c = Compiler::Comparison.new(comparator.text_value, e1.ast, role)
214
+ c.qualifiers << q.text_value unless q.empty?
215
+ [c] + clauses_ast
185
216
  end
186
217
  }
218
+ /
219
+ q:qualifier? e1:expression s comparator s e2:expression # comparisons have no post-qualifiers: p:post_qualifiers?
220
+ {
221
+ def ast
222
+ [Compiler::Comparison.new(comparator.text_value, e1.ast, e2.ast, q.empty? ? [] : [q.text_value])]
223
+ end
224
+ }
225
+ end
226
+
227
+ rule comparator
228
+ '<=' / '<>' / '<' / '=' / '>=' / '>' / '!='
229
+ end
230
+
231
+ rule phrase
232
+ role # A role reference containing a term, perhaps with attached paraphernalia
233
+ / # A hyphenated non-term. Important: no embedded spaces
234
+ id tail:('-' !term id)+ s
235
+ {
236
+ def ast
237
+ [id.value, *tail.elements.map{|e| e.id.value}]*"-"
238
+ end
239
+ def node_type; :linking; end
240
+ }
241
+ / # A normal non-term
242
+ !non_phrase id s
243
+ {
244
+ def ast
245
+ id.value
246
+ end
247
+ def node_type; :linking; end
248
+ }
187
249
  end
188
250
 
189
251
  # This is the rule that causes most back-tracking. I think you can see why.
190
- # When we have an expression, we will come down here perhaps multiple times,
191
- # but find no way out as soon as we hit the trailing non_role.
192
252
  rule role
193
253
  q:(quantifier enforcement)?
194
- player:term
195
- func:function_call? s
196
- role_id:(role_name / subscript )?
254
+ player:derived_variable
197
255
  lr:(
198
256
  literal u:unit?
199
257
  /
200
258
  value_constraint enforcement
201
259
  )?
202
260
  oj:objectification_join?
203
- !non_role # If we integrate fact_clauses with comparisons, this can go.
204
261
  {
205
262
  def ast
206
263
  if !q.empty? && q.quantifier.value
207
264
  quantifier = Compiler::Quantifier.new(q.quantifier.value[0], q.quantifier.value[1], q.enforcement.ast)
208
265
  end
209
- role_name = role_id.empty? ? nil : role_id.value
210
- function_call = nil
211
266
  if !lr.empty?
212
267
  if lr.respond_to?(:literal)
213
268
  literal = lr.literal.value
214
269
  raise "Literals with units are not yet processed" unless lr.u.empty?
215
270
  end
216
- value_constraint = Compiler::ValueConstraint.new(lr.value_constraint.ranges, lr.enforcement.ast) if lr.respond_to?(:value_constraint)
217
- raise "It is not permitted to provide both a literal value and a value restriction" if value_constraint and literal
271
+ value_constraint = Compiler::ValueConstraint.new(lr.value_constraint.ranges, lr.value_constraint.units, lr.enforcement.ast) if lr.respond_to?(:value_constraint)
272
+ raise "It is not permitted to provide both a literal value and a value constraint" if value_constraint and literal
218
273
  end
219
274
 
220
- objectification_join = oj.empty? ? nil : oj.ast
221
- player.ast(quantifier, function_call, role_name, value_constraint, literal, objectification_join)
275
+ nested_clauses =
276
+ if oj.empty?
277
+ nil
278
+ else
279
+ ast = oj.ast
280
+ ast[0].conjunction = 'where'
281
+ ast
282
+ end
283
+ player.ast(quantifier, value_constraint, literal, nested_clauses)
222
284
  end
223
285
  }
224
286
  end
225
287
 
226
288
  rule objectification_join
227
- '(' s where s facts:fact_clauses s ')' s
289
+ '(' s where s facts:joined_clauses s ')' s
228
290
  {
229
291
  def ast
230
292
  facts.ast
@@ -242,13 +304,6 @@ module ActiveFacts
242
304
  { def value; i.text_value.to_i; end }
243
305
  end
244
306
 
245
- rule non_role
246
- # Any of these is illegal in or following a reading (they indicate a comparison is coming). Later, this will change:
247
- comparator
248
- / add_op
249
- / mul_op
250
- end
251
-
252
307
  end
253
308
  end
254
309
  end