activefacts 0.7.3 → 0.8.5

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 (94) hide show
  1. data/LICENSE +19 -0
  2. data/Manifest.txt +24 -2
  3. data/Rakefile +25 -3
  4. data/bin/afgen +1 -1
  5. data/bin/cql +13 -2
  6. data/css/offline.css +3 -0
  7. data/css/orm2.css +24 -0
  8. data/css/print.css +8 -0
  9. data/css/style-print.css +357 -0
  10. data/css/style.css +387 -0
  11. data/download.html +85 -0
  12. data/examples/CQL/Address.cql +3 -3
  13. data/examples/CQL/Blog.cql +13 -14
  14. data/examples/CQL/CompanyDirectorEmployee.cql +4 -4
  15. data/examples/CQL/Death.cql +3 -2
  16. data/examples/CQL/Genealogy.cql +13 -11
  17. data/examples/CQL/Marriage.cql +2 -2
  18. data/examples/CQL/Metamodel.cql +136 -93
  19. data/examples/CQL/MultiInheritance.cql +2 -2
  20. data/examples/CQL/OilSupply.cql +14 -10
  21. data/examples/CQL/Orienteering.cql +22 -19
  22. data/examples/CQL/PersonPlaysGame.cql +3 -2
  23. data/examples/CQL/SchoolActivities.cql +4 -2
  24. data/examples/CQL/SimplestUnary.cql +1 -1
  25. data/examples/CQL/SubtypePI.cql +6 -7
  26. data/examples/CQL/Warehousing.cql +16 -19
  27. data/examples/CQL/unit.cql +584 -0
  28. data/examples/index.html +276 -0
  29. data/examples/intro.html +497 -0
  30. data/examples/local.css +20 -0
  31. data/index.html +96 -0
  32. data/lib/activefacts/api/concept.rb +48 -46
  33. data/lib/activefacts/api/constellation.rb +43 -23
  34. data/lib/activefacts/api/entity.rb +2 -2
  35. data/lib/activefacts/api/instance.rb +6 -2
  36. data/lib/activefacts/api/instance_index.rb +5 -0
  37. data/lib/activefacts/api/value.rb +8 -2
  38. data/lib/activefacts/api/vocabulary.rb +15 -10
  39. data/lib/activefacts/cql/CQLParser.treetop +109 -88
  40. data/lib/activefacts/cql/Concepts.treetop +32 -10
  41. data/lib/activefacts/cql/Context.treetop +34 -0
  42. data/lib/activefacts/cql/Expressions.treetop +9 -9
  43. data/lib/activefacts/cql/FactTypes.treetop +30 -31
  44. data/lib/activefacts/cql/Language/English.treetop +50 -0
  45. data/lib/activefacts/cql/LexicalRules.treetop +2 -1
  46. data/lib/activefacts/cql/Terms.treetop +117 -0
  47. data/lib/activefacts/cql/ValueTypes.treetop +152 -0
  48. data/lib/activefacts/cql/compiler.rb +1718 -0
  49. data/lib/activefacts/cql/parser.rb +124 -57
  50. data/lib/activefacts/generate/absorption.rb +1 -1
  51. data/lib/activefacts/generate/cql.rb +111 -100
  52. data/lib/activefacts/generate/cql/html.rb +5 -5
  53. data/lib/activefacts/generate/oo.rb +3 -3
  54. data/lib/activefacts/generate/ordered.rb +51 -19
  55. data/lib/activefacts/generate/ruby.rb +10 -8
  56. data/lib/activefacts/generate/sql/mysql.rb +14 -10
  57. data/lib/activefacts/generate/sql/server.rb +29 -24
  58. data/lib/activefacts/input/cql.rb +9 -1264
  59. data/lib/activefacts/input/orm.rb +213 -200
  60. data/lib/activefacts/persistence/columns.rb +11 -10
  61. data/lib/activefacts/persistence/index.rb +15 -18
  62. data/lib/activefacts/persistence/reference.rb +17 -17
  63. data/lib/activefacts/persistence/tables.rb +50 -51
  64. data/lib/activefacts/version.rb +1 -1
  65. data/lib/activefacts/vocabulary/extensions.rb +79 -8
  66. data/lib/activefacts/vocabulary/metamodel.rb +183 -114
  67. data/spec/absorption_ruby_spec.rb +99 -0
  68. data/spec/absorption_spec.rb +3 -4
  69. data/spec/api/constellation.rb +1 -1
  70. data/spec/api/entity_type.rb +3 -1
  71. data/spec/api/instance.rb +4 -2
  72. data/spec/api/roles.rb +8 -6
  73. data/spec/api_spec.rb +1 -2
  74. data/spec/cql/context_spec.rb +71 -0
  75. data/spec/cql/samples_spec.rb +154 -0
  76. data/spec/cql/unit_spec.rb +375 -0
  77. data/spec/cql_cql_spec.rb +31 -21
  78. data/spec/cql_mysql_spec.rb +70 -0
  79. data/spec/cql_parse_spec.rb +15 -9
  80. data/spec/cql_ruby_spec.rb +27 -13
  81. data/spec/cql_sql_spec.rb +42 -16
  82. data/spec/cql_symbol_tables_spec.rb +2 -3
  83. data/spec/cqldump_spec.rb +7 -7
  84. data/spec/helpers/file_matcher.rb +39 -0
  85. data/spec/norma_cql_spec.rb +20 -12
  86. data/spec/norma_ruby_spec.rb +6 -3
  87. data/spec/norma_sql_spec.rb +6 -3
  88. data/spec/norma_tables_spec.rb +6 -4
  89. data/spec/spec_helper.rb +27 -8
  90. data/status.html +69 -0
  91. data/why.html +60 -0
  92. metadata +34 -11
  93. data/lib/activefacts/cql/DataTypes.treetop +0 -81
  94. data/spec/cql_unit_spec.rb +0 -330
@@ -0,0 +1,375 @@
1
+ #
2
+ # ActiveFacts tests: Test the CQL parser by looking at its parse trees.
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+
6
+ require 'activefacts/support'
7
+ require 'activefacts/api/support'
8
+ require 'activefacts/cql/parser'
9
+
10
+ describe "Valid Numbers, Strings and Ranges" do
11
+ ValidNumbersEtc = [
12
+ "a is written as b;", # Value type declaration, no params, minimal whitespace
13
+ "a is written as b();", # Value type declaration, minimal whitespace
14
+ "a is written as b ;", # Value type declaration, no params, trailing whitespace
15
+ "a is written as b ( ) ; ", # Value type declaration, maximal whitespace
16
+
17
+ # Comments and newlines, etc as whitespace
18
+ "\na\nis written as \nb\n(\n)\n;\n", # Basic value type declaration, newlines for whitespace
19
+ "\ra\ris written as\rb\r(\r)\r;\r", # Basic value type declaration, returns for whitespace
20
+ "\ta\tis written as\tb\t(\t)\t;\t", # Basic value type declaration, tabs for whitespace
21
+ " /* Plugh */ a /* Plugh */ is written as\n b /* *Plugh* / */ ( /* *Plugh* / */ ) /* *Plugh* / */ ; /* *Plugh* / */ ",
22
+ "//Plugh\na // Plugh\n is written as // Plugh\n b // Plugh\n ( // Plugh\n ) // Plugh\n ; // Plugh\n ",
23
+
24
+ # Integers
25
+ "a is written as b(0);", # Integer zero
26
+ "a is written as b( 0 ) ; ", # Integer zero, maximal whitespace
27
+ "a is written as b(1);", # Integer one
28
+ "a is written as b(-1);", # Integer negative one
29
+ "a is written as b(+1);", # Positive integer
30
+ "a is written as b(1e4);", # Integer with exponent
31
+ "a is written as b(1e-4);", # Integer with negative exponent
32
+ "a is written as b(-1e-4);", # Negative integer with negative exponent
33
+ "a is written as b(077);", # Octal integer
34
+ "a is written as b(0xFace8);", # Hexadecimal integer
35
+ "a is written as b(0,1);", # Two parameters
36
+ "a is written as b( 0 , 1 );",
37
+ "a is written as b(0,1,2) ;", # Three parameters now allowed
38
+
39
+ # Reals
40
+ "a is written as b(1.0);",
41
+ "a is written as b(-1.0);",
42
+ "a is written as b(+1.0);",
43
+ "a is written as b(0.1);",
44
+ "a is written as b(-0.1);",
45
+ "a is written as b(+0.1);",
46
+ "a is written as b(0.0);",
47
+ "a is written as b(-0.0);",
48
+ "a is written as b(+0.0);",
49
+
50
+ # Value types with units
51
+ "a is written as b inch;", # Value type declaration with unit
52
+ "a is written as b() inch ; ", # Value type declaration with unit and whitespace
53
+ "a is written as b() inch;", # Value type declaration with unit
54
+ "a is written as b inch^2;", # Value type declaration with unit and exponent
55
+ "a is written as b() inch^2 ; ", # Value type declaration with unit and exponent with maximum whitespace
56
+ "a is written as b second^-1;", # Value type declaration with unit and negative exponent
57
+ "a is written as b inch inch;", # Value type declaration with repeated unit
58
+ "a is written as b inch^2/minute^-1;", # Value type declaration with unit and divided unit with exponents
59
+ "a is written as b() second^-1/mm^-1 mm^-1;", # Value type declaration with repeated divided unit
60
+
61
+ # Integer value restrictions
62
+ "a is written as b()restricted to{1};", # Integer, minimal whitespace
63
+ "a is written as b() restricted to { 1 } ;", # Integer, maximal whitespace
64
+ "a is written as b() restricted to {1..2};", # Integer range, minimal whitespace
65
+ "a is written as b() restricted to { 1 .. 2 };", # Integer range, maximal whitespace
66
+ "a is written as b() restricted to {..2};", # Integer range with open start, minimal whitespace
67
+ "a is written as b() restricted to { .. 2 };", # Integer range with open start, maximal whitespace
68
+ "a is written as b() restricted to { ..2,3};", # Range followed by integer, minimal whitespace
69
+ "a is written as b() restricted to { 1,..2,3};", # Integer, open-start range, integer, minimal whitespace
70
+ "a is written as b() restricted to { .. 2 , 3 };", # Range followed by integer, maximal whitespace
71
+ "a is written as b() restricted to { ..2 , 3..4 };",# Range followed by range
72
+ "a is written as b() restricted to { ..2, 3..};", # Range followed by range with open end, minimal whitespace
73
+ "a is written as b() restricted to { ..2, 3 .. };", # Range followed by range with open end, maximal whitespace
74
+ "a is written as b() restricted to { 1e4 } ;", # Integer with exponent
75
+ "a is written as b() restricted to { -1e4 } ;", # Negative integer with exponent
76
+ "a is written as b() restricted to { 1e-4 } ;", # Integer with negative exponent
77
+ "a is written as b() restricted to { -1e-4 } ;", # Negative integer with negative exponent
78
+
79
+ # Real value restrictions
80
+ "a is written as b() restricted to {1.0};", # Real, minimal whitespace
81
+ "a is written as b() restricted to { 1.0 } ;", # Real, maximal whitespace
82
+ "a is written as b() restricted to { 1.0e4 } ;", # Real with exponent
83
+ "a is written as b() restricted to { 1.0e-4 } ;", # Real with negative exponent
84
+ "a is written as b() restricted to { -1.0e-4 } ;", # Negative real with negative exponent
85
+ "a is written as b() restricted to { 1.1 .. 2.2 } ;", # Real range, maximal whitespace
86
+ "a is written as b() restricted to { -1.1 .. 2.2 } ;", # Real range, maximal whitespace
87
+ "a is written as b() restricted to { 1.1..2.2};", # Real range, minimal whitespace
88
+ "a is written as b() restricted to { 1.1..2 } ;", # Real-integer range
89
+ "a is written as b() restricted to { 1..2.2 } ;", # Integer-real range
90
+ "a is written as b() restricted to { ..2.2};", # Real range with open start
91
+ "a is written as b() restricted to { 1.1.. };", # Real range with open end
92
+ "a is written as b() restricted to { 1.1.., 2 };", # Real range with open end and following integer
93
+
94
+ # Strings and string value restrictions
95
+ "a is written as b() restricted to {''};", # String, empty, minimal whitespace
96
+ "a is written as b() restricted to {'A'};", # String, minimal whitespace
97
+ "a is written as b() restricted to { 'A' };", # String, maximal whitespace
98
+ "a is written as b() restricted to { '\\b\\t\\f\\n\\r\\e\\\\' };", # String with special escapes
99
+ "a is written as b() restricted to { ' ' };", # String with space
100
+ "a is written as b() restricted to { '\t' };", # String with literal tab
101
+ "a is written as b() restricted to { '\\0' };", # String with nul character
102
+ "a is written as b() restricted to { '\\077' };", # String with octal escape
103
+ "a is written as b() restricted to { '\\0xA9' };", # String with hexadecimal escape
104
+ "a is written as b() restricted to { '\\0uBabe' };",# String with unicode escape
105
+ "a is written as b() restricted to {'A'..'F'};", # String range, minimal whitespace
106
+ "a is written as b() restricted to { 'A' .. 'F' };",# String range, maximal whitespace
107
+ "a is written as b() restricted to { ..'F' };", # String range, open start
108
+ "a is written as b() restricted to { 'A'.. };", # String range, open end
109
+
110
+ # Value restrictions with units
111
+ "a is written as b() restricted to {1} inches^2/second;", # restriction with units and exponent
112
+ "a is written as b() second^-1/mm^-1 mm^-1 restricted to {1} inches^2/second;", # type with unit and restriction with units and exponent
113
+ ]
114
+
115
+ before :each do
116
+ @parser = ActiveFacts::CQL::Parser.new
117
+ end
118
+
119
+ ValidNumbersEtc.each do |c|
120
+ source, ast = *[c].flatten
121
+ it "should parse #{source.inspect}" do
122
+ result = @parser.parse_all(source, :definition)
123
+
124
+ puts @parser.failure_reason unless result
125
+ result.should_not be_nil
126
+ result.map{|d| d.value}.should == ast if ast
127
+ # puts result.map{|d| d.value}.inspect unless ast
128
+ end
129
+ end
130
+ end
131
+
132
+ describe "Invalid Numbers and Strings" do
133
+ InvalidValueTypes = [
134
+ "a is written as b(08);", # Invalid octalnumber
135
+ "a is written as b(0xDice);", # Invalid hexadecimal
136
+ "a is written as b(- 1);", # Invalid negative
137
+ "a is written as b(+ 1);", # Invalid positive
138
+ "b(- 1e-4);", # Negative integer with negative exponent
139
+ "a is written as b(-077);", # Invalid negative octal
140
+ "a is written as b(-0xFace);", # Invalid negative hexadecimal
141
+ "a is written as b(.0);", # Invalid real
142
+ "a is written as b(0.);", # Invalid real
143
+ "b() inch ^2 ; ", # Illegal whitespace around unit exponent
144
+ "b() inch^ 2 ; ", # Illegal whitespace around unit exponent
145
+ "b() restricted to { '\\7a' };", # String with bad octal escape
146
+ "b() restricted to { '\001' };", # String with control char
147
+ "b() restricted to { '\n' };", # String with literal newline
148
+ "b() restricted to { 0..'A' };", # Cross-typed range
149
+ "b() restricted to { 'a'..27 };", # Cross-typed range
150
+ ]
151
+
152
+ before :each do
153
+ @parser = ActiveFacts::CQL::Parser.new
154
+ end
155
+
156
+ InvalidValueTypes.each do |c|
157
+ source, ast = *c
158
+ it "should not parse #{source.inspect}" do
159
+ result = @parser.parse_all(source, :definition)
160
+
161
+ # puts @parser.failure_reason unless result.success?
162
+ result.should be_nil
163
+ end
164
+ end
165
+ end
166
+
167
+ describe "Value Types" do
168
+ ValueTypes = [
169
+ [ "a is written as b(1, 2) inch restricted to { 3 .. 4 } inch ;",
170
+ [["a", [:value_type, "b", [ 1, 2 ], [["inch", 1]], [[3, 4]], []]]]
171
+ ],
172
+ # [ "a c is written as b(1, 2) inch restricted to { 3 .. 4 } inch ;",
173
+ # [["a c", [:value_type, "b", [1, 2], "inch", [[3, 4]]]]]
174
+ # ],
175
+ ]
176
+
177
+ before :each do
178
+ @parser = ActiveFacts::CQL::Parser.new
179
+ end
180
+
181
+ ValueTypes.each do |c|
182
+ source, ast = *c
183
+ it "should parse #{source.inspect}" do
184
+ result = @parser.parse_all(source, :definition)
185
+
186
+ puts @parser.failure_reason unless result
187
+ result.should_not be_nil
188
+
189
+ result.map{|d| d.value}.should == ast if ast
190
+ puts result.map{|d| d.value}.inspect unless ast
191
+ end
192
+ end
193
+ end
194
+
195
+ describe "Entity Types" do
196
+ EntityTypes_RefMode = [
197
+ [ "a is identified by its id;", # Entity type declaration with reference mode
198
+ [["a", [:entity_type, [], {:mode=>"id", :parameters=> [], :restriction=>nil}, [], nil]]]
199
+ ],
200
+ [ "a is identified by its number(12);", # Entity type declaration with reference mode
201
+ [["a", [:entity_type, [], {:mode=>"number", :parameters => [12], :restriction=>nil}, [], nil]]]
202
+ ],
203
+ [ "a is identified by its id:c;", # Entity type declaration with reference mode and fact type(s)
204
+ [["a", [:entity_type, [], {:mode=>"id", :parameters=> [], :restriction=>nil}, [], [[:fact_clause, [], [{:word=>"c"}], nil]]]]]
205
+ ],
206
+ [ "a is identified by its id where c;", # Entity type declaration with reference mode and where
207
+ [["a", [:entity_type, [], {:mode=>"id", :parameters=> [], :restriction=>nil}, [], [[:fact_clause, [], [{:word=>"c"}], nil]]]]]
208
+ ],
209
+ ]
210
+
211
+ EntityTypes_Simple = [
212
+ [ "a is identified by b: c;", # Entity type declaration
213
+ [["a", [:entity_type, [], {:roles=>[["b"]]}, [], [[:fact_clause, [], [{:word=>"c"}], nil]]]]]
214
+ ],
215
+ [ "a is identified by b where c;", # Entity type declaration with where
216
+ [["a", [:entity_type, [], {:roles=>[["b"]]}, [], [[:fact_clause, [], [{:word=>"c"}], nil]]]]]
217
+ ],
218
+ [ "a is identified by b and c: d;", # Entity type declaration with two-part identifier
219
+ [["a", [:entity_type, [], {:roles=>[["b"], ["c"]]}, [], [[:fact_clause, [], [{:word=>"d"}], nil]]]]]
220
+ ],
221
+ [ "a is identified by b, c: d;", # Entity type declaration with two-part identifier
222
+ [["a", [:entity_type, [], {:roles=>[["b"], ["c"]]}, [], [[:fact_clause, [], [{:word=>"d"}], nil]]]]]
223
+ ],
224
+ [ "a is written as b(); c is identified by a:d;",
225
+ [["a", [:value_type, "b", [], [], [], []]],
226
+ ["c", [:entity_type, [], {:roles=>[["a"]]}, [], [[:fact_clause, [], [{:word=>"d"}], nil]]]]]
227
+ ],
228
+ [ " a is written as b ( ) ; c is identified by a : d ; ",
229
+ [["a", [:value_type, "b", [], [], [], []]],
230
+ ["c", [:entity_type, [], {:roles=>[["a"]]}, [], [[:fact_clause, [], [{:word=>"d"}], nil]]]]]
231
+ ],
232
+ [ "a is identified by c:maybe d;",
233
+ [["a", [:entity_type, [], {:roles=>[["c"]]}, [], [[:fact_clause, ["maybe"], [{:word=>"d"}], nil]]]]]
234
+ ],
235
+ ]
236
+
237
+ EntityTypes_Objectified = [
238
+ [ "Director is where Person directs Company, Company is directed by Person;",
239
+ [["Director", [:fact_type, [[:fact_clause, [], [{:word=>"Person"}, {:word=>"directs"}, {:word=>"Company"}], nil], [:fact_clause, [], [{:word=>"Company"}, {:word=>"is"}, {:word=>"directed"}, {:word=>"by"}, {:word=>"Person"}], nil]], []]]]
240
+ ],
241
+ [ "Director: Person directs company;",
242
+ [[nil, [:fact_type, [[:fact_clause, [], [{:word=>"Director"}], nil]], [[:fact_clause, [], [{:word=>"Person"}, {:word=>"directs"}, {:word=>"company"}], nil]]]]]
243
+ ],
244
+ ]
245
+
246
+ EntityTypes_Subtypes = [
247
+ [ "Employee is a kind of Person;",
248
+ [["Employee", [:entity_type, ["Person"], nil, [], nil]]]
249
+ ],
250
+ [ "Employee is a subtype of Person;",
251
+ [["Employee", [:entity_type, ["Person"], nil, [], nil]]]
252
+ ],
253
+ [ "AustralianEmployee is a subtype of Employee, Australian;",
254
+ [["AustralianEmployee", [:entity_type, ["Employee", "Australian"], nil, [], nil]]]
255
+ ],
256
+ [ "Employee is a kind of Person identified by EmployeeNumber;",
257
+ [["Employee", [:entity_type, ["Person"], {:roles=>[["EmployeeNumber"]]}, [], nil]]]
258
+ ],
259
+ [ "Employee is a subtype of Person identified by EmployeeNumber;",
260
+ [["Employee", [:entity_type, ["Person"], {:roles=>[["EmployeeNumber"]]}, [], nil]]]
261
+ ],
262
+ [ "AustralianEmployee is a subtype of Employee, Australian identified by TaxFileNumber;",
263
+ [["AustralianEmployee", [:entity_type, ["Employee", "Australian"], {:roles=>[["TaxFileNumber"]]}, [], nil]]]
264
+ ],
265
+ ]
266
+
267
+ EntityTypes =
268
+ EntityTypes_RefMode +
269
+ EntityTypes_Simple +
270
+ EntityTypes_Objectified +
271
+ EntityTypes_Subtypes
272
+
273
+ before :each do
274
+ @parser = ActiveFacts::CQL::Parser.new
275
+ end
276
+
277
+ EntityTypes.each do |c|
278
+ source, ast = *c
279
+ it "should parse #{source.inspect}" do
280
+ result = @parser.parse_all(source, :definition)
281
+
282
+ puts @parser.failure_reason unless result
283
+
284
+ result.should_not be_nil
285
+ if ast
286
+ result.map{|d| d.value}.should == ast
287
+ else
288
+ puts "\n"+result.map{|d| d.value}.inspect
289
+ end
290
+ end
291
+ end
292
+ end
293
+
294
+ describe "Fact Types" do
295
+ FactTypes = [
296
+ [ "Director is old: Person directs company, Person is of age, age > 60;",
297
+ [nil, [:fact_type, [[:fact_clause, [], [{:word=>"Director"}, {:word=>"is"}, {:word=>"old"}], nil]], [[:fact_clause, [], [{:word=>"Person"}, {:word=>"directs"}, {:word=>"company"}], nil], [:fact_clause, [], [{:word=>"Person"}, {:word=>"is"}, {:word=>"of"}, {:word=>"age"}], nil], [">", [:variable, "age"], 60]]]]
298
+ ],
299
+ [ "a: maybe a has completely- green b -totally [transitive, acyclic], b -c = 2;",
300
+ [nil, [:fact_type, [[:fact_clause, [], [{:word=>"a"}], nil]], [[:fact_clause, ["maybe", "transitive", "acyclic"], [{:word=>"a"}, {:word=>"has"}, {:leading_adjective=>"completely", :word=>"green"}, {:trailing_adjective=>"totally", :word=>"b"}], nil], ["=", [:+, [:variable, "b"], [:-, [:variable, "c"]]], 2]]]]
301
+ ],
302
+ [ "Person is independent: Person has taxable- Income, taxable Income >= 20000 dollars;",
303
+ [nil, [:fact_type, [[:fact_clause, [], [{:word=>"Person"}, {:word=>"is"}, {:word=>"independent"}], nil]], [[:fact_clause, [], [{:word=>"Person"}, {:word=>"has"}, {:leading_adjective=>"taxable", :word=>"Income"}], nil], [">=", [:variable, "taxable", "Income"], [20000, "dollars"]]]]]
304
+ ],
305
+ [ "Window requires toughening: Window has width-mm, Window has height-mm, width mm * height mm >= 10 foot^2;",
306
+ [nil, [:fact_type, [[:fact_clause, [], [{:word=>"Window"}, {:word=>"requires"}, {:word=>"toughening"}], nil]], [[:fact_clause, [], [{:word=>"Window"}, {:word=>"has"}, {:leading_adjective=>"width", :word=>"mm"}], nil], [:fact_clause, [], [{:word=>"Window"}, {:word=>"has"}, {:leading_adjective=>"height", :word=>"mm"}], nil], [">=", [:*, [:variable, "width", "mm"], [:variable, "height", "mm"]], [10, "foot^2"]]]]]
307
+ ],
308
+ # REVISIT: Test all quantifiers
309
+ # REVISIT: Test all post-qualifiers
310
+ # REVISIT: Test functions
311
+ [ "AnnualIncome is where Person has total- Income in Year: Person has total- Income.sum(), Income was earned in current- time.Year() (as Year);",
312
+ ["AnnualIncome", [:fact_type, [[:fact_clause, [], [{:word=>"Person"}, {:word=>"has"}, {:leading_adjective=>"total", :word=>"Income"}, {:word=>"in"}, {:word=>"Year"}], nil]], [[:fact_clause, [], [{:word=>"Person"}, {:word=>"has"}, {:leading_adjective=>"total", :function=>[:"(", "sum"], :word=>"Income"}], nil], [:fact_clause, [], [{:word=>"Income"}, {:word=>"was"}, {:word=>"earned"}, {:word=>"in"}, {:role_name=>"Year", :leading_adjective=>"current", :function=>[:"(", "Year"], :word=>"time"}], nil]]]]
313
+ ],
314
+ [ "a is interesting : b- c -d has e- f -g;",
315
+ [nil, [:fact_type, [[:fact_clause, [], [{:word=>"a"}, {:word=>"is"}, {:word=>"interesting"}], nil]], [[:fact_clause, [], [{:leading_adjective=>"b", :trailing_adjective=>"d", :word=>"c"}, {:word=>"has"}, {:leading_adjective=>"e", :trailing_adjective=>"g", :word=>"f"}], nil]]]]
316
+ ]
317
+ ]
318
+
319
+ before :each do
320
+ @parser = ActiveFacts::CQL::Parser.new
321
+ end
322
+
323
+ FactTypes.each do |c|
324
+ source, ast, definition = *c
325
+ it "should parse #{source.inspect}" do
326
+ definitions = @parser.parse_all(source, :definition)
327
+
328
+ puts @parser.failure_reason unless definitions
329
+
330
+ definitions.should_not be_nil
331
+ result = definitions[-1]
332
+
333
+ if (definition)
334
+ result.definition.should == definition
335
+ else
336
+ #p @parser.definition(result)
337
+ end
338
+
339
+ result.value.should == ast if ast
340
+ puts result.map{|d| d.value}.inspect unless ast
341
+ end
342
+ end
343
+
344
+ Constraints = [
345
+ [ "each combination FamilyName, GivenName occurs at most one time in Competitor has FamilyName, Competitor has GivenName;",
346
+ [nil, [:constraint, :presence, [["FamilyName"], ["GivenName"]], [nil, 1], [[[{:word=>"Competitor"}, {:word=>"has"}, {:word=>"FamilyName"}]], [[{:word=>"Competitor"}, {:word=>"has"}, {:word=>"GivenName"}]]], nil]]
347
+ ],
348
+ ]
349
+
350
+ before :each do
351
+ @parser = ActiveFacts::CQL::Parser.new
352
+ end
353
+
354
+ Constraints.each do |c|
355
+ source, ast, definition = *c
356
+ it "should parse #{source.inspect}" do
357
+ definitions = @parser.parse_all(source, :definition)
358
+
359
+ puts @parser.failure_reason unless definitions
360
+
361
+ definitions.should_not be_nil
362
+ result = definitions[-1]
363
+
364
+ if (definition)
365
+ result.definition.should == definition
366
+ else
367
+ #p @parser.definition(result)
368
+ end
369
+
370
+ result.value.should == ast if ast
371
+
372
+ result.value.inspect unless ast
373
+ end
374
+ end
375
+ end
data/spec/cql_cql_spec.rb CHANGED
@@ -2,7 +2,8 @@
2
2
  # ActiveFacts tests: Parse all CQL files and check the generated CQL.
3
3
  # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
4
  #
5
- require 'rubygems'
5
+
6
+ require 'spec/spec_helper'
6
7
  require 'stringio'
7
8
  require 'activefacts/vocabulary'
8
9
  require 'activefacts/support'
@@ -11,23 +12,18 @@ require 'activefacts/generate/cql'
11
12
 
12
13
  include ActiveFacts
13
14
 
14
- class String
15
- def strip_comments()
16
- c_comment = %r{/\*((?!\*/).)*\*/}m
17
- gsub(c_comment, '').gsub(%r{\n\n+},"\n")
18
- end
19
- end
20
-
21
15
  describe "CQL Loader" do
22
- CQL_CQL_FAILURES = %w{
23
- Airline
24
- CompanyQuery
25
- Insurance
26
- OddIdentifier
27
- OrienteeringER
28
- ServiceDirector
16
+ cql_failures = {
17
+ "Airline" => "Contains queries, unsupported",
18
+ "CompanyQuery" => "Contains queries, unsupported",
19
+ "OrienteeringER" => "Doesn't parse due to difficult fact type match",
20
+ "ServiceDirector" => "Doesn't parse some constraints due to mis-matched adjectives"
21
+ }
22
+ cql_cql_failures = {
23
+ "Insurance" => "Misses a subtype join in a constraint verbalisation",
24
+ "MetamodelTerms" => "Fails due to weak adjective/role matching",
25
+ # "OddIdentifier" => "Doesn't support identification of object fact types using mixed external/internal roles",
29
26
  }
30
-
31
27
  # Generate and return the CQL for the given vocabulary
32
28
  def cql(vocabulary)
33
29
  output = StringIO.new
@@ -42,16 +38,30 @@ describe "CQL Loader" do
42
38
  actual_file = cql_file.sub(%r{examples/CQL/}, 'spec/actual/')
43
39
 
44
40
  it "should load CQL and dump valid CQL for #{cql_file}" do
45
- pending if CQL_CQL_FAILURES.include? File.basename(cql_file, ".cql")
46
- vocabulary = ActiveFacts::Input::CQL.readfile(cql_file)
41
+ broken = cql_failures[File.basename(actual_file, ".cql")]
42
+ vocabulary = nil
43
+ if broken
44
+ pending(broken) {
45
+ vocabulary = ActiveFacts::Input::CQL.readfile(cql_file)
46
+ }
47
+ else
48
+ vocabulary = ActiveFacts::Input::CQL.readfile(cql_file)
49
+ end
47
50
 
48
51
  # Build and save the actual file:
49
52
  cql_text = cql(vocabulary)
50
53
  File.open(actual_file, "w") { |f| f.write cql_text }
54
+ expected_text = File.open(cql_file) {|f| f.read }
51
55
 
52
- expected_text = File.open(cql_file) {|f| f.read.strip_comments }.scan(/.*?\n/)
53
- cql_text.strip_comments.scan(/.*?\n/).should == expected_text
54
- File.delete(actual_file) # It succeeded, we don't need the file.
56
+ broken = cql_cql_failures[File.basename(actual_file, ".cql")]
57
+ if broken
58
+ pending(broken) {
59
+ cql_text.should_not differ_from(expected_text)
60
+ }
61
+ else
62
+ cql_text.should_not differ_from(expected_text)
63
+ File.delete(actual_file) # It succeeded, we don't need the file.
64
+ end
55
65
  end
56
66
  end
57
67
  end