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.
- data/LICENSE +19 -0
- data/Manifest.txt +24 -2
- data/Rakefile +25 -3
- data/bin/afgen +1 -1
- data/bin/cql +13 -2
- data/css/offline.css +3 -0
- data/css/orm2.css +24 -0
- data/css/print.css +8 -0
- data/css/style-print.css +357 -0
- data/css/style.css +387 -0
- data/download.html +85 -0
- data/examples/CQL/Address.cql +3 -3
- data/examples/CQL/Blog.cql +13 -14
- data/examples/CQL/CompanyDirectorEmployee.cql +4 -4
- data/examples/CQL/Death.cql +3 -2
- data/examples/CQL/Genealogy.cql +13 -11
- data/examples/CQL/Marriage.cql +2 -2
- data/examples/CQL/Metamodel.cql +136 -93
- data/examples/CQL/MultiInheritance.cql +2 -2
- data/examples/CQL/OilSupply.cql +14 -10
- data/examples/CQL/Orienteering.cql +22 -19
- data/examples/CQL/PersonPlaysGame.cql +3 -2
- data/examples/CQL/SchoolActivities.cql +4 -2
- data/examples/CQL/SimplestUnary.cql +1 -1
- data/examples/CQL/SubtypePI.cql +6 -7
- data/examples/CQL/Warehousing.cql +16 -19
- data/examples/CQL/unit.cql +584 -0
- data/examples/index.html +276 -0
- data/examples/intro.html +497 -0
- data/examples/local.css +20 -0
- data/index.html +96 -0
- data/lib/activefacts/api/concept.rb +48 -46
- data/lib/activefacts/api/constellation.rb +43 -23
- data/lib/activefacts/api/entity.rb +2 -2
- data/lib/activefacts/api/instance.rb +6 -2
- data/lib/activefacts/api/instance_index.rb +5 -0
- data/lib/activefacts/api/value.rb +8 -2
- data/lib/activefacts/api/vocabulary.rb +15 -10
- data/lib/activefacts/cql/CQLParser.treetop +109 -88
- data/lib/activefacts/cql/Concepts.treetop +32 -10
- data/lib/activefacts/cql/Context.treetop +34 -0
- data/lib/activefacts/cql/Expressions.treetop +9 -9
- data/lib/activefacts/cql/FactTypes.treetop +30 -31
- data/lib/activefacts/cql/Language/English.treetop +50 -0
- data/lib/activefacts/cql/LexicalRules.treetop +2 -1
- data/lib/activefacts/cql/Terms.treetop +117 -0
- data/lib/activefacts/cql/ValueTypes.treetop +152 -0
- data/lib/activefacts/cql/compiler.rb +1718 -0
- data/lib/activefacts/cql/parser.rb +124 -57
- data/lib/activefacts/generate/absorption.rb +1 -1
- data/lib/activefacts/generate/cql.rb +111 -100
- data/lib/activefacts/generate/cql/html.rb +5 -5
- data/lib/activefacts/generate/oo.rb +3 -3
- data/lib/activefacts/generate/ordered.rb +51 -19
- data/lib/activefacts/generate/ruby.rb +10 -8
- data/lib/activefacts/generate/sql/mysql.rb +14 -10
- data/lib/activefacts/generate/sql/server.rb +29 -24
- data/lib/activefacts/input/cql.rb +9 -1264
- data/lib/activefacts/input/orm.rb +213 -200
- data/lib/activefacts/persistence/columns.rb +11 -10
- data/lib/activefacts/persistence/index.rb +15 -18
- data/lib/activefacts/persistence/reference.rb +17 -17
- data/lib/activefacts/persistence/tables.rb +50 -51
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary/extensions.rb +79 -8
- data/lib/activefacts/vocabulary/metamodel.rb +183 -114
- data/spec/absorption_ruby_spec.rb +99 -0
- data/spec/absorption_spec.rb +3 -4
- data/spec/api/constellation.rb +1 -1
- data/spec/api/entity_type.rb +3 -1
- data/spec/api/instance.rb +4 -2
- data/spec/api/roles.rb +8 -6
- data/spec/api_spec.rb +1 -2
- data/spec/cql/context_spec.rb +71 -0
- data/spec/cql/samples_spec.rb +154 -0
- data/spec/cql/unit_spec.rb +375 -0
- data/spec/cql_cql_spec.rb +31 -21
- data/spec/cql_mysql_spec.rb +70 -0
- data/spec/cql_parse_spec.rb +15 -9
- data/spec/cql_ruby_spec.rb +27 -13
- data/spec/cql_sql_spec.rb +42 -16
- data/spec/cql_symbol_tables_spec.rb +2 -3
- data/spec/cqldump_spec.rb +7 -7
- data/spec/helpers/file_matcher.rb +39 -0
- data/spec/norma_cql_spec.rb +20 -12
- data/spec/norma_ruby_spec.rb +6 -3
- data/spec/norma_sql_spec.rb +6 -3
- data/spec/norma_tables_spec.rb +6 -4
- data/spec/spec_helper.rb +27 -8
- data/status.html +69 -0
- data/why.html +60 -0
- metadata +34 -11
- data/lib/activefacts/cql/DataTypes.treetop +0 -81
- 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
|
-
|
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
|
-
|
23
|
-
Airline
|
24
|
-
CompanyQuery
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
46
|
-
vocabulary =
|
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
|
-
|
53
|
-
|
54
|
-
|
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
|