activefacts 0.6.0
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/History.txt +4 -0
- data/Manifest.txt +83 -0
- data/README.rdoc +81 -0
- data/Rakefile +41 -0
- data/bin/afgen +46 -0
- data/bin/cql +52 -0
- data/examples/CQL/Address.cql +46 -0
- data/examples/CQL/Blog.cql +54 -0
- data/examples/CQL/CompanyDirectorEmployee.cql +51 -0
- data/examples/CQL/Death.cql +16 -0
- data/examples/CQL/Genealogy.cql +95 -0
- data/examples/CQL/Marriage.cql +18 -0
- data/examples/CQL/Metamodel.cql +238 -0
- data/examples/CQL/MultiInheritance.cql +19 -0
- data/examples/CQL/OilSupply.cql +47 -0
- data/examples/CQL/Orienteering.cql +108 -0
- data/examples/CQL/PersonPlaysGame.cql +17 -0
- data/examples/CQL/SchoolActivities.cql +31 -0
- data/examples/CQL/SimplestUnary.cql +12 -0
- data/examples/CQL/SubtypePI.cql +32 -0
- data/examples/CQL/Warehousing.cql +99 -0
- data/examples/CQL/WindowInRoomInBldg.cql +22 -0
- data/lib/activefacts.rb +10 -0
- data/lib/activefacts/api.rb +25 -0
- data/lib/activefacts/api/concept.rb +384 -0
- data/lib/activefacts/api/constellation.rb +106 -0
- data/lib/activefacts/api/entity.rb +239 -0
- data/lib/activefacts/api/instance.rb +54 -0
- data/lib/activefacts/api/numeric.rb +158 -0
- data/lib/activefacts/api/role.rb +94 -0
- data/lib/activefacts/api/standard_types.rb +67 -0
- data/lib/activefacts/api/support.rb +59 -0
- data/lib/activefacts/api/value.rb +122 -0
- data/lib/activefacts/api/vocabulary.rb +120 -0
- data/lib/activefacts/cql.rb +31 -0
- data/lib/activefacts/cql/CQLParser.treetop +104 -0
- data/lib/activefacts/cql/Concepts.treetop +112 -0
- data/lib/activefacts/cql/DataTypes.treetop +66 -0
- data/lib/activefacts/cql/Expressions.treetop +113 -0
- data/lib/activefacts/cql/FactTypes.treetop +185 -0
- data/lib/activefacts/cql/Language/English.treetop +92 -0
- data/lib/activefacts/cql/LexicalRules.treetop +169 -0
- data/lib/activefacts/cql/Rakefile +6 -0
- data/lib/activefacts/cql/parser.rb +88 -0
- data/lib/activefacts/generate/absorption.rb +87 -0
- data/lib/activefacts/generate/cql.rb +441 -0
- data/lib/activefacts/generate/cql/html.rb +397 -0
- data/lib/activefacts/generate/null.rb +19 -0
- data/lib/activefacts/generate/ordered.rb +557 -0
- data/lib/activefacts/generate/ruby.rb +326 -0
- data/lib/activefacts/generate/sql/server.rb +164 -0
- data/lib/activefacts/generate/text.rb +21 -0
- data/lib/activefacts/input/cql.rb +1268 -0
- data/lib/activefacts/input/orm.rb +926 -0
- data/lib/activefacts/persistence.rb +1 -0
- data/lib/activefacts/persistence/composition.rb +653 -0
- data/lib/activefacts/support.rb +51 -0
- data/lib/activefacts/version.rb +3 -0
- data/lib/activefacts/vocabulary.rb +6 -0
- data/lib/activefacts/vocabulary/extensions.rb +343 -0
- data/lib/activefacts/vocabulary/metamodel.rb +303 -0
- data/script/txt2html +71 -0
- data/spec/absorption_spec.rb +95 -0
- data/spec/api/autocounter.rb +82 -0
- data/spec/api/constellation.rb +130 -0
- data/spec/api/entity_type.rb +101 -0
- data/spec/api/instance.rb +428 -0
- data/spec/api/roles.rb +122 -0
- data/spec/api/value_type.rb +112 -0
- data/spec/api_spec.rb +14 -0
- data/spec/cql_cql_spec.rb +58 -0
- data/spec/cql_parse_spec.rb +31 -0
- data/spec/cql_ruby_spec.rb +60 -0
- data/spec/cql_sql_spec.rb +54 -0
- data/spec/cql_symbol_tables_spec.rb +259 -0
- data/spec/cql_unit_spec.rb +336 -0
- data/spec/cqldump_spec.rb +169 -0
- data/spec/norma_cql_spec.rb +48 -0
- data/spec/norma_ruby_spec.rb +50 -0
- data/spec/norma_sql_spec.rb +45 -0
- data/spec/norma_tables_spec.rb +94 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- metadata +173 -0
@@ -0,0 +1,336 @@
|
|
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
|
+
require 'rubygems'
|
6
|
+
require 'treetop'
|
7
|
+
require 'activefacts/support'
|
8
|
+
require 'activefacts/api/support'
|
9
|
+
require 'activefacts/cql/parser'
|
10
|
+
|
11
|
+
describe "Valid Numbers, Strings and Ranges" do
|
12
|
+
ValidNumbersEtc = [
|
13
|
+
"a=b();", # Basic data type declaration, minimal whitespace
|
14
|
+
"a = b ( ) ; ", # Basic data type declaration, maximal whitespace
|
15
|
+
"a is defined as b();", # Verbally named data type declaration
|
16
|
+
"a=b() inch;", # Data type declaration with unit
|
17
|
+
"a=b() inch ; ", # Data type declaration with unit
|
18
|
+
"a=b() inch^2 ; ", # Data type declaration with unit and exponent
|
19
|
+
|
20
|
+
# Comments etc as whitespace
|
21
|
+
"\na\n= \nb\n(\n)\n;\n", # Basic data type declaration, newlines for whitespace
|
22
|
+
"\ra\r=\rb\r(\r)\r;\r", # Basic data type declaration, returns for whitespace
|
23
|
+
"\ta\t=\tb\t(\t)\t;\t", # Basic data type declaration, tabs for whitespace
|
24
|
+
" /* Plugh */ a /* Plugh */ =\n b /* *Plugh* / */ ( /* *Plugh* / */ ) /* *Plugh* / */ ; /* *Plugh* / */ ",
|
25
|
+
"//Plugh\na // Plugh\n = // Plugh\n b // Plugh\n ( // Plugh\n ) // Plugh\n ; // Plugh\n ",
|
26
|
+
|
27
|
+
# Integers
|
28
|
+
"a=b(0);", # Integer zero
|
29
|
+
"a=b( 0 ) ; ", # Integer zero, maximal whitespace
|
30
|
+
"a=b(1);", # Integer one
|
31
|
+
"a=b(-1);", # Integer negative one
|
32
|
+
"a=b(+1);", # Positive integer
|
33
|
+
"a=b(1e4);", # Integer with exponent
|
34
|
+
"a=b(1e-4);", # Integer with negative exponent
|
35
|
+
"a=b(-1e-4);", # Negative integer with negative exponent
|
36
|
+
"a=b(077);", # Octal integer
|
37
|
+
"a=b(0xFace8);", # Hexadecimal integer
|
38
|
+
"a=b(0,1);", # Two parameters
|
39
|
+
"a=b( 0 , 1 );",
|
40
|
+
"a=b(0,1,2) ;", # Three parameters now allowed
|
41
|
+
|
42
|
+
# Reals
|
43
|
+
"a=b(1.0);",
|
44
|
+
"a=b(-1.0);",
|
45
|
+
"a=b(+1.0);",
|
46
|
+
"a=b(0.1);",
|
47
|
+
"a=b(-0.1);",
|
48
|
+
"a=b(+0.1);",
|
49
|
+
"a=b(0.0);",
|
50
|
+
"a=b(-0.0);",
|
51
|
+
"a=b(+0.0);",
|
52
|
+
|
53
|
+
# Integer value restrictions
|
54
|
+
"a=b()restricted to{1};", # Integer, minimal whitespace
|
55
|
+
"a=b() restricted to { 1 } ;", # Integer, maximal whitespace
|
56
|
+
"a=b() restricted to {1..2};", # Integer range, minimal whitespace
|
57
|
+
"a=b() restricted to { 1 .. 2 };", # Integer range, maximal whitespace
|
58
|
+
"a=b() restricted to {..2};", # Integer range with open start, minimal whitespace
|
59
|
+
"a=b() restricted to { .. 2 };", # Integer range with open start, maximal whitespace
|
60
|
+
"a=b() restricted to { ..2,3};", # Range followed by integer, minimal whitespace
|
61
|
+
"a=b() restricted to { 1,..2,3};", # Integer, open-start range, integer, minimal whitespace
|
62
|
+
"a=b() restricted to { .. 2 , 3 };", # Range followed by integer, maximal whitespace
|
63
|
+
"a=b() restricted to { ..2 , 3..4 };", # Range followed by range
|
64
|
+
"a=b() restricted to { ..2, 3..};", # Range followed by range with open end, minimal whitespace
|
65
|
+
"a=b() restricted to { ..2, 3 .. };", # Range followed by range with open end, maximal whitespace
|
66
|
+
"a=b() restricted to { 1e4 } ;", # Integer with exponent
|
67
|
+
"a=b() restricted to { -1e4 } ;", # Negative integer with exponent
|
68
|
+
"a=b() restricted to { 1e-4 } ;", # Integer with negative exponent
|
69
|
+
"a=b() restricted to { -1e-4 } ;", # Negative integer with negative exponent
|
70
|
+
|
71
|
+
# Real value restrictions
|
72
|
+
"a=b() restricted to {1.0};", # Real, minimal whitespace
|
73
|
+
"a=b() restricted to { 1.0 } ;", # Real, maximal whitespace
|
74
|
+
"a=b() restricted to { 1.0e4 } ;", # Real with exponent
|
75
|
+
"a=b() restricted to { 1.0e-4 } ;", # Real with negative exponent
|
76
|
+
"a=b() restricted to { -1.0e-4 } ;", # Negative real with negative exponent
|
77
|
+
"a=b() restricted to { 1.1 .. 2.2 } ;", # Real range, maximal whitespace
|
78
|
+
"a=b() restricted to { -1.1 .. 2.2 } ;", # Real range, maximal whitespace
|
79
|
+
"a=b() restricted to { 1.1..2.2};", # Real range, minimal whitespace
|
80
|
+
"a=b() restricted to { 1.1..2 } ;", # Real-integer range
|
81
|
+
"a=b() restricted to { 1..2.2 } ;", # Integer-real range
|
82
|
+
"a=b() restricted to { ..2.2};", # Real range with open start
|
83
|
+
"a=b() restricted to { 1.1.. };", # Real range with open end
|
84
|
+
"a=b() restricted to { 1.1.., 2 };", # Real range with open end and following integer
|
85
|
+
|
86
|
+
# Strings and string value restrictions
|
87
|
+
"a=b() restricted to {''};", # String, empty, minimal whitespace
|
88
|
+
"a=b() restricted to {'A'};", # String, minimal whitespace
|
89
|
+
"a=b() restricted to { 'A' };", # String, maximal whitespace
|
90
|
+
"a=b() restricted to { '\\b\\t\\f\\n\\r\\e\\\\' };", # String with special escapes
|
91
|
+
"a=b() restricted to { ' ' };", # String with space
|
92
|
+
"a=b() restricted to { '\t' };", # String with literal tab
|
93
|
+
"a=b() restricted to { '\\0' };", # String with nul character
|
94
|
+
"a=b() restricted to { '\\077' };", # String with octal escape
|
95
|
+
"a=b() restricted to { '\\0xA9' };", # String with hexadecimal escape
|
96
|
+
"a=b() restricted to { '\\0uBabe' };", # String with unicode escape
|
97
|
+
"a=b() restricted to {'A'..'F'};", # String range, minimal whitespace
|
98
|
+
"a=b() restricted to { 'A' .. 'F' };", # String range, maximal whitespace
|
99
|
+
"a=b() restricted to { ..'F' };", # String range, open start
|
100
|
+
"a=b() restricted to { 'A'.. };", # String range, open end
|
101
|
+
]
|
102
|
+
|
103
|
+
setup do
|
104
|
+
@parser = ActiveFacts::CQLParser.new
|
105
|
+
end
|
106
|
+
|
107
|
+
ValidNumbersEtc.each do |c|
|
108
|
+
source, ast = *[c].flatten
|
109
|
+
it "should parse #{source.inspect}" do
|
110
|
+
result = @parser.parse_all(source, :definition)
|
111
|
+
|
112
|
+
puts @parser.failure_reason unless result
|
113
|
+
result.should_not be_nil
|
114
|
+
result.map{|d| d.value}.should == ast if ast
|
115
|
+
# puts result.map{|d| d.value}.inspect unless ast
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "Invalid Numbers and Strings" do
|
121
|
+
InvalidValueTypes = [
|
122
|
+
"a=b(08);", # Invalid octalnumber
|
123
|
+
"a=b(0xDice);", # Invalid hexadecimal
|
124
|
+
"a=b(- 1);", # Invalid negative
|
125
|
+
"a=b(+ 1);", # Invalid positive
|
126
|
+
"b(- 1e-4);", # Negative integer with negative exponent
|
127
|
+
"a=b(-077);", # Invalid negative octal
|
128
|
+
"a=b(-0xFace);", # Invalid negative hexadecimal
|
129
|
+
"a=b(.0);", # Invalid real
|
130
|
+
"a=b(0.);", # Invalid real
|
131
|
+
"b() inch ^2 ; ", # Illegal whitespace around unit exponent
|
132
|
+
"b() inch^ 2 ; ", # Illegal whitespace around unit exponent
|
133
|
+
"b() restricted to { '\\7a' };", # String with bad octal escape
|
134
|
+
"b() restricted to { '\001' };", # String with control char
|
135
|
+
"b() restricted to { '\n' };", # String with literal newline
|
136
|
+
"b() restricted to { 0..'A' };", # Cross-typed range
|
137
|
+
"b() restricted to { 'a'..27 };", # Cross-typed range
|
138
|
+
]
|
139
|
+
|
140
|
+
setup do
|
141
|
+
@parser = ActiveFacts::CQLParser.new
|
142
|
+
end
|
143
|
+
|
144
|
+
InvalidValueTypes.each do |c|
|
145
|
+
source, ast = *c
|
146
|
+
it "should not parse #{source.inspect}" do
|
147
|
+
result = @parser.parse_all(source, :definition)
|
148
|
+
|
149
|
+
# puts @parser.failure_reason unless result.success?
|
150
|
+
result.should be_nil
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "Data Types" do
|
156
|
+
DataTypes = [
|
157
|
+
[ "a = b(1, 2) inch restricted to { 3 .. 4 } inch ;",
|
158
|
+
[["a", [:data_type, "b", [ 1, 2 ], "inch", [[3, 4]]]]]
|
159
|
+
],
|
160
|
+
# [ "a c = b(1, 2) inch restricted to { 3 .. 4 } inch ;",
|
161
|
+
# [["a c", [:data_type, "b", [1, 2], "inch", [[3, 4]]]]]
|
162
|
+
# ],
|
163
|
+
]
|
164
|
+
|
165
|
+
setup do
|
166
|
+
@parser = ActiveFacts::CQLParser.new
|
167
|
+
end
|
168
|
+
|
169
|
+
DataTypes.each do |c|
|
170
|
+
source, ast = *c
|
171
|
+
it "should parse #{source.inspect}" do
|
172
|
+
result = @parser.parse_all(source, :definition)
|
173
|
+
|
174
|
+
puts @parser.failure_reason unless result
|
175
|
+
result.should_not be_nil
|
176
|
+
|
177
|
+
result.map{|d| d.value}.should == ast if ast
|
178
|
+
puts result.map{|d| d.value}.inspect unless ast
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe "Entity Types" do
|
184
|
+
EntityTypes_RefMode = [
|
185
|
+
=begin
|
186
|
+
[ "a = entity(.id):c;", # Entity type declaration with reference mode
|
187
|
+
[["a", [:entity_type, [], {:mode=>"id"}, [[:fact_clause, [], [{:word=>"c"}]]]]]]
|
188
|
+
],
|
189
|
+
[ "a = entity ( . id ) : c ;", # Entity type declaration with reference mode, maximal whitespace
|
190
|
+
[["a", [:entity_type, [], {:mode=>"id"}, [[:fact_clause, [], [{:word=>"c"}]]]]]]
|
191
|
+
],
|
192
|
+
[ "a = entity(.id) where c;", # Entity type declaration with reference mode and where
|
193
|
+
[["a", [:entity_type, [], {:mode=>"id"}, [[:fact_clause, [], [{:word=>"c"}]]]]]]
|
194
|
+
],
|
195
|
+
=end
|
196
|
+
]
|
197
|
+
|
198
|
+
EntityTypes_Simple = [
|
199
|
+
[ "a is identified by b: c;", # Entity type declaration
|
200
|
+
[["a", [:entity_type, [], {:roles=>[["b"]]}, [[:fact_clause, [], [{:word=>"c"}]]]]]]
|
201
|
+
],
|
202
|
+
[ "a is identified by b where c;", # Entity type declaration with where
|
203
|
+
[["a", [:entity_type, [], {:roles=>[["b"]]}, [[:fact_clause, [], [{:word=>"c"}]]]]]]
|
204
|
+
],
|
205
|
+
[ "a is identified by b and c: d;", # Entity type declaration with two-part identifier
|
206
|
+
[["a", [:entity_type, [], {:roles=>[["b"], ["c"]]}, [[:fact_clause, [], [{:word=>"d"}]]]]]]
|
207
|
+
],
|
208
|
+
[ "a is identified by b, c: d;", # Entity type declaration with two-part identifier
|
209
|
+
[["a", [:entity_type, [], {:roles=>[["b"], ["c"]]}, [[:fact_clause, [], [{:word=>"d"}]]]]]]
|
210
|
+
],
|
211
|
+
[ "a=b(); c is identified by a:d;",
|
212
|
+
[["a", [:data_type, "b", [], nil, []]],
|
213
|
+
["c", [:entity_type, [], {:roles=>[["a"]]}, [[:fact_clause, [], [{:word=>"d"}]]]]]]
|
214
|
+
],
|
215
|
+
[ " a = b ( ) ; c is identified by a : d ; ",
|
216
|
+
[["a", [:data_type, "b", [ ], nil, []]],
|
217
|
+
["c", [:entity_type, [], {:roles=>[["a"]]}, [[:fact_clause, [], [{:word=>"d"}]]]]]]
|
218
|
+
],
|
219
|
+
[ "a is identified by c:maybe d;",
|
220
|
+
[["a", [:entity_type, [], {:roles=>[["c"]]}, [[:fact_clause, ["maybe"], [{:word=>"d"}]]]]]]
|
221
|
+
],
|
222
|
+
]
|
223
|
+
|
224
|
+
EntityTypes_Objectified = [
|
225
|
+
[ "Director = Person directs Company, Company is directed by Person;",
|
226
|
+
[["Director", [:fact_type, [[:fact_clause, [], [{:word=>"Person"}, {:word=>"directs"}, {:word=>"Company"}]], [:fact_clause, [], [{:word=>"Company"}, {:word=>"is"}, {:word=>"directed"}, {:word=>"by"}, {:word=>"Person"}]]], []]]]
|
227
|
+
],
|
228
|
+
[ "Director: Person directs company;",
|
229
|
+
[[nil, [:fact_type, [[:fact_clause, [], [{:word=>"Director"}]]], [[:fact_clause, [], [{:word=>"Person"}, {:word=>"directs"}, {:word=>"company"}]]]]]]
|
230
|
+
],
|
231
|
+
]
|
232
|
+
|
233
|
+
EntityTypes_Subtypes = [
|
234
|
+
[ "Employee is a kind of Person;",
|
235
|
+
[["Employee", [:entity_type, ["Person"], nil, nil]]]
|
236
|
+
],
|
237
|
+
[ "Employee is a subtype of Person;",
|
238
|
+
[["Employee", [:entity_type, ["Person"], nil, nil]]]
|
239
|
+
],
|
240
|
+
[ "Employee is defined as subtype of Person;",
|
241
|
+
[["Employee", [:entity_type, ["Person"], nil, nil]]]
|
242
|
+
],
|
243
|
+
[ "AustralianEmployee is a subtype of Employee, Australian;",
|
244
|
+
[["AustralianEmployee", [:entity_type, ["Employee", "Australian"], nil, nil]]]
|
245
|
+
],
|
246
|
+
[ "Employee is a kind of Person identified by EmployeeNumber;",
|
247
|
+
[["Employee", [:entity_type, ["Person"], {:roles=>[["EmployeeNumber"]]}, nil]]]
|
248
|
+
],
|
249
|
+
[ "Employee is a subtype of Person identified by EmployeeNumber;",
|
250
|
+
[["Employee", [:entity_type, ["Person"], {:roles=>[["EmployeeNumber"]]}, nil]]]
|
251
|
+
],
|
252
|
+
[ "Employee is defined as subtype of Person identified by EmployeeNumber;",
|
253
|
+
[["Employee", [:entity_type, ["Person"], {:roles=>[["EmployeeNumber"]]}, nil]]]
|
254
|
+
],
|
255
|
+
[ "AustralianEmployee is a subtype of Employee, Australian identified by TaxFileNumber;",
|
256
|
+
[["AustralianEmployee", [:entity_type, ["Employee", "Australian"], {:roles=>[["TaxFileNumber"]]}, nil]]]
|
257
|
+
],
|
258
|
+
]
|
259
|
+
|
260
|
+
EntityTypes =
|
261
|
+
EntityTypes_RefMode +
|
262
|
+
EntityTypes_Simple +
|
263
|
+
EntityTypes_Objectified +
|
264
|
+
EntityTypes_Subtypes
|
265
|
+
|
266
|
+
setup do
|
267
|
+
@parser = ActiveFacts::CQLParser.new
|
268
|
+
end
|
269
|
+
|
270
|
+
EntityTypes.each do |c|
|
271
|
+
source, ast = *c
|
272
|
+
it "should parse #{source.inspect}" do
|
273
|
+
result = @parser.parse_all(source, :definition)
|
274
|
+
|
275
|
+
puts @parser.failure_reason unless result
|
276
|
+
|
277
|
+
result.should_not be_nil
|
278
|
+
if ast
|
279
|
+
result.map{|d| d.value}.should == ast
|
280
|
+
else
|
281
|
+
puts "\n"+result.map{|d| d.value}.inspect
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
describe "Fact Types" do
|
288
|
+
FactTypes = [
|
289
|
+
[ "Director is old: Person directs company, Person is of age, age > 60;",
|
290
|
+
[nil, [:fact_type, [[:fact_clause, [], [{:word=>"Director"}, {:word=>"is"}, {:word=>"old"}]]], [[:fact_clause, [], [{:word=>"Person"}, {:word=>"directs"}, {:word=>"company"}]], [:fact_clause, [], [{:word=>"Person"}, {:word=>"is"}, {:word=>"of"}, {:word=>"age"}]], [">", [:variable, "age"], 60]]]]
|
291
|
+
],
|
292
|
+
[ "a: maybe a has completely- green b -totally [transitive, acyclic], b -c = 2;",
|
293
|
+
[nil, [:fact_type, [[:fact_clause, [], [{:word=>"a"}]]], [[:fact_clause, ["maybe", "transitive", "acyclic"], [{:word=>"a"}, {:word=>"has"}, {:word=>"green", :leading_adjective=>"completely"}, {:word=>"b", :trailing_adjective=>"totally"}]], ["=", [:+, [:variable, "b"], [:-, [:variable, "c"]]], 2]]]]
|
294
|
+
],
|
295
|
+
[ "Person is independent: Person has taxable- Income, taxable Income >= 20000 dollars;",
|
296
|
+
[nil, [:fact_type, [[:fact_clause, [], [{:word=>"Person"}, {:word=>"is"}, {:word=>"independent"}]]], [[:fact_clause, [], [{:word=>"Person"}, {:word=>"has"}, {:leading_adjective=>"taxable", :word=>"Income"}]], [">=", [:variable, "taxable", "Income"], [20000, "dollars"]]]]]
|
297
|
+
],
|
298
|
+
[ "Window requires toughening: Window has width-mm, Window has height-mm, width mm * height mm >= 10 foot^2;",
|
299
|
+
[nil, [:fact_type, [[:fact_clause, [], [{:word=>"Window"}, {:word=>"requires"}, {:word=>"toughening"}]]], [[:fact_clause, [], [{:word=>"Window"}, {:word=>"has"}, {:leading_adjective=>"width", :word=>"mm"}]], [:fact_clause, [], [{:word=>"Window"}, {:word=>"has"}, {:leading_adjective=>"height", :word=>"mm"}]], [">=", [:*, [:variable, "width", "mm"], [:variable, "height", "mm"]], [10, "foot^2"]]]]]
|
300
|
+
],
|
301
|
+
# REVISIT: Test all quantifiers
|
302
|
+
# REVISIT: Test all post-qualifiers
|
303
|
+
# REVISIT: Test functions
|
304
|
+
[ "AnnualIncome is where Person has total- Income in Year: Person has total- Income.sum(), Income was earned in current- time.Year() (as Year);",
|
305
|
+
["AnnualIncome", [:fact_type, [[:fact_clause, [], [{:word=>"Person"}, {:word=>"has"}, {:leading_adjective=>"total", :word=>"Income"}, {:word=>"in"}, {:word=>"Year"}]]], [[:fact_clause, [], [{:word=>"Person"}, {:word=>"has"}, {:function=>[:"(", "sum"], :leading_adjective=>"total", :word=>"Income"}]], [:fact_clause, [], [{:word=>"Income"}, {:word=>"was"}, {:word=>"earned"}, {:word=>"in"}, {:function=>[:"(", "Year"], :word=>"time", :role_name=>"Year", :leading_adjective=>"current"}]]]]]
|
306
|
+
],
|
307
|
+
[ "a is interesting : b- c -d has e- f -g;",
|
308
|
+
[nil, [:fact_type, [[:fact_clause, [], [{:word=>"a"}, {:word=>"is"}, {:word=>"interesting"}]]], [[:fact_clause, [], [{:trailing_adjective=>"d", :word=>"c", :leading_adjective=>"b"}, {:word=>"has"}, {:trailing_adjective=>"g", :word=>"f", :leading_adjective=>"e"}]]]]]
|
309
|
+
]
|
310
|
+
]
|
311
|
+
|
312
|
+
setup do
|
313
|
+
@parser = ActiveFacts::CQLParser.new
|
314
|
+
end
|
315
|
+
|
316
|
+
FactTypes.each do |c|
|
317
|
+
source, ast, definition = *c
|
318
|
+
it "should parse #{source.inspect}" do
|
319
|
+
definitions = @parser.parse_all(source, :definition)
|
320
|
+
|
321
|
+
puts @parser.failure_reason unless definitions
|
322
|
+
|
323
|
+
definitions.should_not be_nil
|
324
|
+
result = definitions[-1]
|
325
|
+
|
326
|
+
if (definition)
|
327
|
+
result.definition.should == definition
|
328
|
+
else
|
329
|
+
#p @parser.definition(result)
|
330
|
+
end
|
331
|
+
|
332
|
+
result.value.should == ast if ast
|
333
|
+
puts result.map{|d| d.value}.inspect unless ast
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
#
|
2
|
+
# ActiveFacts tests: Test the generated CQL for some simple cases.
|
3
|
+
# Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
|
4
|
+
#
|
5
|
+
require 'rubygems'
|
6
|
+
require 'stringio'
|
7
|
+
require 'activefacts/support'
|
8
|
+
require 'activefacts/vocabulary'
|
9
|
+
require 'activefacts/generate/cql'
|
10
|
+
|
11
|
+
include ActiveFacts
|
12
|
+
|
13
|
+
describe "CQL Dumper" do
|
14
|
+
def self.hide(*a,&b)
|
15
|
+
end
|
16
|
+
|
17
|
+
setup do
|
18
|
+
@constellation = ActiveFacts::API::Constellation.new(ActiveFacts::Metamodel)
|
19
|
+
@vocabulary = @constellation.Vocabulary("TestVocab")
|
20
|
+
@string_type = @constellation.ValueType("String", @vocabulary)
|
21
|
+
@integer_type = @constellation.ValueType("Integer", @vocabulary)
|
22
|
+
@dumper = ActiveFacts::Generate::CQL.new(@constellation)
|
23
|
+
end
|
24
|
+
|
25
|
+
def cql
|
26
|
+
output = StringIO.new
|
27
|
+
@dumper.generate(output)
|
28
|
+
output.rewind
|
29
|
+
output.read
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should dump a String ValueType" do
|
33
|
+
vt = @constellation.ValueType("Name", @vocabulary, :supertype => @string_type, :length => 20)
|
34
|
+
vt.supertype = @string_type
|
35
|
+
vt.length = 20
|
36
|
+
#p vt.class.roles.keys.sort_by{|s| s.to_s}
|
37
|
+
#p vt.supertype
|
38
|
+
cql.should == <<END
|
39
|
+
vocabulary TestVocab;
|
40
|
+
|
41
|
+
/*
|
42
|
+
* Value Types
|
43
|
+
*/
|
44
|
+
Name is defined as String(20);
|
45
|
+
|
46
|
+
END
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should dump an Integer ValueType" do
|
50
|
+
vt = @constellation.ValueType("Count", @vocabulary, :supertype => @integer_type, :length => 32)
|
51
|
+
cql.should == <<END
|
52
|
+
vocabulary TestVocab;
|
53
|
+
|
54
|
+
/*
|
55
|
+
* Value Types
|
56
|
+
*/
|
57
|
+
Count is defined as Integer(32);
|
58
|
+
|
59
|
+
END
|
60
|
+
end
|
61
|
+
|
62
|
+
def value_type(name, datatype = "String", length = 0, scale = 0)
|
63
|
+
dt = @constellation.ValueType(datatype, @vocabulary)
|
64
|
+
vt = @constellation.ValueType(name, @vocabulary, :supertype => dt)
|
65
|
+
vt.length = length if length != 0
|
66
|
+
vt.scale = scale if scale != 0
|
67
|
+
vt
|
68
|
+
end
|
69
|
+
|
70
|
+
def one_to_many(one, many, reading)
|
71
|
+
# Join them with a fact type:
|
72
|
+
ft = @constellation.FactType(:new)
|
73
|
+
role0 = @constellation.Role(ft, 0, one)
|
74
|
+
role1 = @constellation.Role(ft, 1, many)
|
75
|
+
|
76
|
+
# Make a role sequence:
|
77
|
+
rs = @constellation.RoleSequence(:new)
|
78
|
+
rr0 = @constellation.RoleRef(rs, 0, :role => role0)
|
79
|
+
rr1 = @constellation.RoleRef(rs, 1, :role => role1)
|
80
|
+
|
81
|
+
# Make a uniqueness constraint:
|
82
|
+
pcrs = @constellation.RoleSequence(:new)
|
83
|
+
@constellation.RoleRef(pcrs, 0, :role => role0)
|
84
|
+
pc = @constellation.PresenceConstraint(:new, :is_mandatory => false, :is_preferred_identifier => false, :max_frequency => 1, :min_frequency => 0, :role_sequence => pcrs)
|
85
|
+
|
86
|
+
# Make a new reading:
|
87
|
+
reading = @constellation.Reading(ft, ft.all_reading.size, :role_sequence => rs, :reading_text => reading)
|
88
|
+
|
89
|
+
ft
|
90
|
+
end
|
91
|
+
|
92
|
+
def one_to_one(first, second, reading)
|
93
|
+
# Join them with a fact type:
|
94
|
+
ft = @constellation.FactType(:new)
|
95
|
+
role0 = @constellation.Role(ft, 0, first)
|
96
|
+
role1 = @constellation.Role(ft, 1, second)
|
97
|
+
|
98
|
+
# Make a role sequence for the reading
|
99
|
+
rs = @constellation.RoleSequence(:new)
|
100
|
+
rr0 = @constellation.RoleRef(rs, 0, :role => role0)
|
101
|
+
rr1 = @constellation.RoleRef(rs, 1, :role => role1)
|
102
|
+
|
103
|
+
# Make a new reading:
|
104
|
+
reading = @constellation.Reading(ft, ft.all_reading.size, :role_sequence => rs, :reading_text => reading)
|
105
|
+
|
106
|
+
# Make a uniqueness constraint for the first role
|
107
|
+
first_rs = @constellation.RoleSequence(:new)
|
108
|
+
@constellation.RoleRef(first_rs, 0, :role => role0)
|
109
|
+
first_pc = @constellation.PresenceConstraint(:new, :is_mandatory => true, :is_preferred_identifier => false, :max_frequency => 1, :min_frequency => 1, :role_sequence => first_rs)
|
110
|
+
|
111
|
+
# Make a uniqueness constraint for the second role
|
112
|
+
second_rs = @constellation.RoleSequence(:new)
|
113
|
+
@constellation.RoleRef(second_rs, 0, :role => role1)
|
114
|
+
second_pc = @constellation.PresenceConstraint(:new, :is_mandatory => true, :is_preferred_identifier => true, :max_frequency => 1, :min_frequency => 1, :role_sequence => second_rs)
|
115
|
+
|
116
|
+
ft
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should dump a VT-VT FactType" do
|
120
|
+
# Make two valuetypes:
|
121
|
+
st = value_type("Name", "String", 20)
|
122
|
+
vt = value_type("Count", "Integer", 32)
|
123
|
+
|
124
|
+
ft = one_to_many(st, vt, "{0} occurs {1} times")
|
125
|
+
|
126
|
+
#puts @constellation.verbalise #; exit
|
127
|
+
|
128
|
+
cql.should == <<END
|
129
|
+
vocabulary TestVocab;
|
130
|
+
|
131
|
+
/*
|
132
|
+
* Value Types
|
133
|
+
*/
|
134
|
+
Count is defined as Integer(32);
|
135
|
+
Name is defined as String(20);
|
136
|
+
|
137
|
+
/*
|
138
|
+
* Fact Types
|
139
|
+
*/
|
140
|
+
Name occurs Count times;
|
141
|
+
|
142
|
+
END
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should dump an named EntityType" do
|
146
|
+
vt = @constellation.ValueType("Name", @vocabulary, :supertype => @string_type, :length => 20)
|
147
|
+
et = @constellation.EntityType("Company", @vocabulary)
|
148
|
+
|
149
|
+
ft = one_to_one(et, vt, "{0} is called {1}")
|
150
|
+
|
151
|
+
cql.should == <<END
|
152
|
+
vocabulary TestVocab;
|
153
|
+
|
154
|
+
/*
|
155
|
+
* Value Types
|
156
|
+
*/
|
157
|
+
Name is defined as String(20);
|
158
|
+
|
159
|
+
/*
|
160
|
+
* Entity Types
|
161
|
+
*/
|
162
|
+
Company is identified by Name where
|
163
|
+
Company is called Name;
|
164
|
+
|
165
|
+
END
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|