activefacts 0.8.6 → 0.8.8
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +33 -2
- data/README.rdoc +30 -36
- data/Rakefile +16 -20
- data/bin/afgen +17 -11
- data/bin/cql +313 -36
- data/download.html +43 -19
- data/examples/CQL/Address.cql +15 -15
- data/examples/CQL/Blog.cql +8 -8
- data/examples/CQL/CompanyDirectorEmployee.cql +6 -5
- data/examples/CQL/Death.cql +3 -3
- data/examples/CQL/Diplomacy.cql +48 -0
- data/examples/CQL/Genealogy.cql +41 -41
- data/examples/CQL/Insurance.cql +311 -0
- data/examples/CQL/JoinEquality.cql +35 -0
- data/examples/CQL/Marriage.cql +1 -1
- data/examples/CQL/Metamodel.cql +290 -185
- data/examples/CQL/MetamodelNext.cql +420 -0
- data/examples/CQL/Monogamy.cql +24 -0
- data/examples/CQL/MonthInSeason.cql +27 -0
- data/examples/CQL/Moon.cql +23 -0
- data/examples/CQL/MultiInheritance.cql +4 -4
- data/examples/CQL/NonRoleId.cql +14 -0
- data/examples/CQL/OddIdentifier.cql +18 -0
- data/examples/CQL/OilSupply.cql +24 -24
- data/examples/CQL/OneToOnes.cql +17 -0
- data/examples/CQL/Orienteering.cql +55 -55
- data/examples/CQL/OrienteeringER.cql +58 -0
- data/examples/CQL/PersonPlaysGame.cql +2 -2
- data/examples/CQL/RedundantDependency.cql +34 -0
- data/examples/CQL/SchoolActivities.cql +5 -5
- data/examples/CQL/SeparateSubtype.cql +28 -0
- data/examples/CQL/ServiceDirector.cql +283 -0
- data/examples/CQL/SimplestUnary.cql +2 -2
- data/examples/CQL/SubtypePI.cql +11 -11
- data/examples/CQL/Supervision.cql +38 -0
- data/examples/CQL/Tests.Test5.Load.cql +38 -0
- data/examples/CQL/WaiterTips.cql +33 -0
- data/examples/CQL/Warehousing.cql +55 -53
- data/examples/CQL/WindowInRoomInBldg.cql +9 -9
- data/examples/CQL/unit.cql +433 -544
- data/examples/index.html +314 -170
- data/examples/intro.html +6 -176
- data/examples/local.css +8 -4
- data/index.html +40 -25
- data/lib/activefacts/api/concept.rb +2 -2
- data/lib/activefacts/api/constellation.rb +4 -4
- data/lib/activefacts/api/instance.rb +2 -2
- data/lib/activefacts/api/instance_index.rb +4 -0
- data/lib/activefacts/api/numeric.rb +3 -1
- data/lib/activefacts/api/role.rb +1 -1
- data/lib/activefacts/api/standard_types.rb +23 -16
- data/lib/activefacts/api/support.rb +3 -1
- data/lib/activefacts/api/vocabulary.rb +4 -0
- data/lib/activefacts/cql/CQLParser.treetop +87 -39
- data/lib/activefacts/cql/Concepts.treetop +95 -69
- data/lib/activefacts/cql/Context.treetop +11 -2
- data/lib/activefacts/cql/Expressions.treetop +23 -59
- data/lib/activefacts/cql/FactTypes.treetop +141 -95
- data/lib/activefacts/cql/Language/English.treetop +33 -21
- data/lib/activefacts/cql/LexicalRules.treetop +6 -1
- data/lib/activefacts/cql/Terms.treetop +75 -26
- data/lib/activefacts/cql/ValueTypes.treetop +52 -54
- data/lib/activefacts/cql/compiler.rb +46 -1691
- data/lib/activefacts/cql/compiler/constraint.rb +602 -0
- data/lib/activefacts/cql/compiler/entity_type.rb +425 -0
- data/lib/activefacts/cql/compiler/fact.rb +300 -0
- data/lib/activefacts/cql/compiler/fact_type.rb +230 -0
- data/lib/activefacts/cql/compiler/reading.rb +832 -0
- data/lib/activefacts/cql/compiler/shared.rb +109 -0
- data/lib/activefacts/cql/compiler/value_type.rb +104 -0
- data/lib/activefacts/cql/parser.rb +132 -81
- data/lib/activefacts/generate/cql.rb +397 -274
- data/lib/activefacts/generate/oo.rb +13 -12
- data/lib/activefacts/generate/ordered.rb +107 -117
- data/lib/activefacts/generate/ruby.rb +34 -38
- data/lib/activefacts/generate/sql/mysql.rb +62 -45
- data/lib/activefacts/generate/sql/server.rb +59 -42
- data/lib/activefacts/input/cql.rb +6 -3
- data/lib/activefacts/input/orm.rb +991 -557
- data/lib/activefacts/persistence/columns.rb +16 -12
- data/lib/activefacts/persistence/foreignkey.rb +7 -4
- data/lib/activefacts/persistence/index.rb +3 -4
- data/lib/activefacts/persistence/reference.rb +5 -2
- data/lib/activefacts/support.rb +20 -14
- data/lib/activefacts/version.rb +1 -1
- data/lib/activefacts/vocabulary.rb +1 -0
- data/lib/activefacts/vocabulary/extensions.rb +328 -44
- data/lib/activefacts/vocabulary/metamodel.rb +145 -20
- data/lib/activefacts/vocabulary/verbaliser.rb +621 -0
- data/spec/absorption_spec.rb +4 -4
- data/spec/api/value_type.rb +1 -1
- data/spec/cql/context_spec.rb +45 -22
- data/spec/cql/deontic_spec.rb +88 -0
- data/spec/cql/matching_spec.rb +517 -0
- data/spec/cql/samples_spec.rb +88 -31
- data/spec/cql/unit_spec.rb +58 -37
- data/spec/cql_cql_spec.rb +12 -7
- data/spec/cql_mysql_spec.rb +3 -7
- data/spec/cql_parse_spec.rb +0 -4
- data/spec/cql_ruby_spec.rb +1 -4
- data/spec/cql_sql_spec.rb +5 -18
- data/spec/cql_symbol_tables_spec.rb +3 -0
- data/spec/cqldump_spec.rb +0 -2
- data/spec/helpers/array_matcher.rb +35 -0
- data/spec/helpers/ctrl_c_support.rb +52 -0
- data/spec/helpers/diff_matcher.rb +38 -0
- data/spec/helpers/file_matcher.rb +5 -3
- data/spec/helpers/string_matcher.rb +39 -0
- data/spec/helpers/test_parser.rb +13 -0
- data/spec/norma_cql_spec.rb +13 -5
- data/spec/norma_ruby_spec.rb +11 -3
- data/spec/{absorption_ruby_spec.rb → norma_ruby_sql_spec.rb} +37 -32
- data/spec/norma_sql_spec.rb +11 -5
- data/spec/norma_tables_spec.rb +33 -29
- data/spec/spec_helper.rb +4 -1
- data/status.html +92 -23
- metadata +102 -36
- data/lib/activefacts/generate/cql/html.rb +0 -403
data/Manifest.txt
CHANGED
@@ -15,16 +15,33 @@ examples/CQL/Address.cql
|
|
15
15
|
examples/CQL/Blog.cql
|
16
16
|
examples/CQL/CompanyDirectorEmployee.cql
|
17
17
|
examples/CQL/Death.cql
|
18
|
+
examples/CQL/Diplomacy.cql
|
18
19
|
examples/CQL/Genealogy.cql
|
20
|
+
examples/CQL/Insurance.cql
|
21
|
+
examples/CQL/JoinEquality.cql
|
19
22
|
examples/CQL/Marriage.cql
|
20
23
|
examples/CQL/Metamodel.cql
|
24
|
+
examples/CQL/MetamodelNext.cql
|
25
|
+
examples/CQL/Monogamy.cql
|
26
|
+
examples/CQL/MonthInSeason.cql
|
27
|
+
examples/CQL/Moon.cql
|
21
28
|
examples/CQL/MultiInheritance.cql
|
29
|
+
examples/CQL/NonRoleId.cql
|
30
|
+
examples/CQL/OddIdentifier.cql
|
22
31
|
examples/CQL/OilSupply.cql
|
32
|
+
examples/CQL/OneToOnes.cql
|
23
33
|
examples/CQL/Orienteering.cql
|
34
|
+
examples/CQL/OrienteeringER.cql
|
24
35
|
examples/CQL/PersonPlaysGame.cql
|
36
|
+
examples/CQL/RedundantDependency.cql
|
25
37
|
examples/CQL/SchoolActivities.cql
|
38
|
+
examples/CQL/SeparateSubtype.cql
|
39
|
+
examples/CQL/ServiceDirector.cql
|
26
40
|
examples/CQL/SimplestUnary.cql
|
27
41
|
examples/CQL/SubtypePI.cql
|
42
|
+
examples/CQL/Supervision.cql
|
43
|
+
examples/CQL/Tests.Test5.Load.cql
|
44
|
+
examples/CQL/WaiterTips.cql
|
28
45
|
examples/CQL/Warehousing.cql
|
29
46
|
examples/CQL/WindowInRoomInBldg.cql
|
30
47
|
examples/CQL/unit.cql
|
@@ -59,10 +76,16 @@ lib/activefacts/cql/Rakefile
|
|
59
76
|
lib/activefacts/cql/Terms.treetop
|
60
77
|
lib/activefacts/cql/ValueTypes.treetop
|
61
78
|
lib/activefacts/cql/compiler.rb
|
79
|
+
lib/activefacts/cql/compiler/constraint.rb
|
80
|
+
lib/activefacts/cql/compiler/entity_type.rb
|
81
|
+
lib/activefacts/cql/compiler/fact.rb
|
82
|
+
lib/activefacts/cql/compiler/fact_type.rb
|
83
|
+
lib/activefacts/cql/compiler/reading.rb
|
84
|
+
lib/activefacts/cql/compiler/shared.rb
|
85
|
+
lib/activefacts/cql/compiler/value_type.rb
|
62
86
|
lib/activefacts/cql/parser.rb
|
63
87
|
lib/activefacts/generate/absorption.rb
|
64
88
|
lib/activefacts/generate/cql.rb
|
65
|
-
lib/activefacts/generate/cql/html.rb
|
66
89
|
lib/activefacts/generate/null.rb
|
67
90
|
lib/activefacts/generate/oo.rb
|
68
91
|
lib/activefacts/generate/ordered.rb
|
@@ -84,8 +107,8 @@ lib/activefacts/version.rb
|
|
84
107
|
lib/activefacts/vocabulary.rb
|
85
108
|
lib/activefacts/vocabulary/extensions.rb
|
86
109
|
lib/activefacts/vocabulary/metamodel.rb
|
110
|
+
lib/activefacts/vocabulary/verbaliser.rb
|
87
111
|
script/txt2html
|
88
|
-
spec/absorption_ruby_spec.rb
|
89
112
|
spec/absorption_spec.rb
|
90
113
|
spec/api/autocounter.rb
|
91
114
|
spec/api/constellation.rb
|
@@ -95,6 +118,8 @@ spec/api/roles.rb
|
|
95
118
|
spec/api/value_type.rb
|
96
119
|
spec/api_spec.rb
|
97
120
|
spec/cql/context_spec.rb
|
121
|
+
spec/cql/deontic_spec.rb
|
122
|
+
spec/cql/matching_spec.rb
|
98
123
|
spec/cql/samples_spec.rb
|
99
124
|
spec/cql/unit_spec.rb
|
100
125
|
spec/cql_cql_spec.rb
|
@@ -104,9 +129,15 @@ spec/cql_ruby_spec.rb
|
|
104
129
|
spec/cql_sql_spec.rb
|
105
130
|
spec/cql_symbol_tables_spec.rb
|
106
131
|
spec/cqldump_spec.rb
|
132
|
+
spec/helpers/array_matcher.rb
|
133
|
+
spec/helpers/ctrl_c_support.rb
|
134
|
+
spec/helpers/diff_matcher.rb
|
107
135
|
spec/helpers/file_matcher.rb
|
136
|
+
spec/helpers/string_matcher.rb
|
137
|
+
spec/helpers/test_parser.rb
|
108
138
|
spec/norma_cql_spec.rb
|
109
139
|
spec/norma_ruby_spec.rb
|
140
|
+
spec/norma_ruby_sql_spec.rb
|
110
141
|
spec/norma_sql_spec.rb
|
111
142
|
spec/norma_tables_spec.rb
|
112
143
|
spec/spec.opts
|
data/README.rdoc
CHANGED
@@ -2,58 +2,52 @@
|
|
2
2
|
|
3
3
|
* http://dataconstellation.com/ActiveFacts/
|
4
4
|
|
5
|
-
== DESCRIPTION
|
5
|
+
== DESCRIPTION
|
6
6
|
|
7
|
-
ActiveFacts
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
ActiveFacts provides a semantic modeling language, the Constellation
|
8
|
+
Query Language (CQL). CQL combines natural language verbalisation and
|
9
|
+
formal logic, producing a formal language that reads like plain
|
10
|
+
English. ActiveFacts converts semantic models from CQL to relational
|
11
|
+
and object models in SQL, Ruby and other languages.
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
both the original semantic model and to the generated SQL.
|
19
|
-
|
20
|
-
The result is a formal language that reads like plain English, and
|
21
|
-
allows creation of relational and object models that are guaranteed
|
22
|
-
equivalent, and much more stable in the face of schema evolution than
|
23
|
-
SQL is.
|
13
|
+
The generated models are guaranteed congruent, which eliminates the
|
14
|
+
object-relational impedance mismatch. Semantic models are much more
|
15
|
+
stable under evolving requirements than either relational or
|
16
|
+
object-oriented models, because they directly express the underlying
|
17
|
+
elementary facts, so are not susceptible to ramifying change in the
|
18
|
+
way those attribute-oriented approaches are.
|
24
19
|
|
25
|
-
|
20
|
+
Semantic modeling is a refinement of fact-based modeling techniques
|
21
|
+
such as ORM2, NIAM and others. ActiveFacts can convert ORM2 files from
|
22
|
+
NORMA to CQL. Fact-based modeling is closely related to relational
|
23
|
+
modeling in the sixth normal form, but doesn't suffer from 6NF
|
24
|
+
inefficiency. The relational models it derives are highly efficient.
|
26
25
|
|
27
|
-
|
26
|
+
== SYNOPSIS:
|
28
27
|
|
29
|
-
|
30
|
-
|
28
|
+
afgen --sql/server myfile.cql
|
29
|
+
afgen --ruby myfile.cql
|
30
|
+
afgen --cql myfile.orm
|
31
31
|
|
32
|
-
|
32
|
+
== INSTALL:
|
33
33
|
|
34
|
-
|
35
|
-
don't yet pass the CQL parser's semantic analysis.
|
34
|
+
* sudo gem install activefacts
|
36
35
|
|
37
|
-
|
36
|
+
== UNIMPLEMENTED FEATURES
|
38
37
|
|
39
|
-
*
|
40
|
-
shows the parse tree.
|
38
|
+
* Queries are parsed, but not yet generated to SQL.
|
41
39
|
|
42
|
-
|
40
|
+
* The Constellation API lacks SQL persistence (but is already useful;
|
41
|
+
the CQL compiler uses the generated Ruby code extensively)
|
43
42
|
|
44
|
-
|
45
|
-
afgen --ruby myfile.cql
|
46
|
-
afgen --cql myfile.orm
|
43
|
+
* Validation of semantic models is incomplete
|
47
44
|
|
48
45
|
== REQUIREMENTS:
|
49
46
|
|
50
47
|
* Treetop parser generator
|
51
|
-
* Optional: NORMA, see <http://www.ormfoundation.org/files/>
|
52
|
-
(needs Visual Studio pro edition), if you want to use ORM
|
53
48
|
|
54
|
-
|
55
|
-
|
56
|
-
* sudo gem install activefacts
|
49
|
+
* NORMA (see <http://www.ormfoundation.org/files/>), if you want to
|
50
|
+
use ORM (needs Visual Studio Pro edition)
|
57
51
|
|
58
52
|
== LICENSE:
|
59
53
|
|
data/Rakefile
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
%w[rubygems hoe rake rake/clean fileutils newgem rubigen spec spec/rake/spectask].each { |f| require f }
|
2
2
|
|
3
|
-
|
3
|
+
# Use mislav-hanna to the API format documentation, if it's installed:
|
4
|
+
begin
|
5
|
+
require 'hanna/rdoctask'
|
6
|
+
HANNA = true
|
7
|
+
rescue
|
8
|
+
HANNA = false
|
9
|
+
end
|
4
10
|
|
5
11
|
require File.dirname(__FILE__) + '/lib/activefacts'
|
6
12
|
|
@@ -10,23 +16,11 @@ $hoe = Hoe.spec('activefacts') do |p|
|
|
10
16
|
p.version = ActiveFacts::VERSION
|
11
17
|
p.summary = "A semantic modeling and query language (CQL) and application runtime (the Constellation API)"
|
12
18
|
p.description = %q{
|
13
|
-
ActiveFacts
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
Semantic modeling is a refinement of fact-based modeling techniques
|
19
|
-
that draw on natural language verbalisation and formal logic. Fact
|
20
|
-
based modeling is essentially the same as relational modeling in the
|
21
|
-
sixth normal form. The tools provided here automatically condense
|
22
|
-
that to third normal form for efficient storage. They also generate
|
23
|
-
object models as a Ruby module which has an effective mapping to
|
24
|
-
both the original semantic model and to the generated SQL.
|
25
|
-
|
26
|
-
The result is a formal language that reads like plain English, and
|
27
|
-
allows creation of relational and object models that are guaranteed
|
28
|
-
equivalent, and much more stable in the face of schema evolution than
|
29
|
-
SQL is.
|
19
|
+
ActiveFacts provides a semantic modeling language, the Constellation
|
20
|
+
Query Language (CQL). CQL combines natural language verbalisation and
|
21
|
+
formal logic, producing a formal language that reads like plain
|
22
|
+
English. ActiveFacts converts semantic models from CQL to relational
|
23
|
+
and object models in SQL, Ruby and other languages.
|
30
24
|
}
|
31
25
|
p.url = "http://dataconstellation.com/ActiveFacts/"
|
32
26
|
p.developer('Clifford Heath', 'cjh@dataconstellation.org')
|
@@ -35,6 +29,7 @@ SQL is.
|
|
35
29
|
p.rubyforge_name = "cjheath@rubyforge.org"
|
36
30
|
p.extra_deps = [
|
37
31
|
['treetop','>= 1.4.1'],
|
32
|
+
['rake','>= 0.8.7'],
|
38
33
|
]
|
39
34
|
p.extra_dev_deps = [
|
40
35
|
['newgem', ">= #{::Newgem::VERSION}"]
|
@@ -42,8 +37,9 @@ SQL is.
|
|
42
37
|
p.spec_extras[:extensions] = 'lib/activefacts/cql/Rakefile'
|
43
38
|
# Magic Hoe hook to prevent the generation of diagrams:
|
44
39
|
ENV['NODOT'] = 'yes'
|
45
|
-
p.spec_extras[:rdoc_options] =
|
46
|
-
|
40
|
+
p.spec_extras[:rdoc_options] = ['-S'] +
|
41
|
+
(HANNA ? %w{ -S -T hanna} : []) +
|
42
|
+
%w{
|
47
43
|
-A has_one -A one_to_one -A maybe
|
48
44
|
-x lib/activefacts/cql/.*.rb
|
49
45
|
-x lib/activefacts/vocabulary/.*.rb
|
data/bin/afgen
CHANGED
@@ -1,34 +1,37 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
2
|
#
|
3
|
-
#
|
3
|
+
# ActiveFacts: Read a Vocabulary (from a NORMA, CQL or other file) and run a generator
|
4
4
|
#
|
5
5
|
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
6
|
#
|
7
7
|
$:.unshift File.dirname(File.expand_path(__FILE__))+"/../lib"
|
8
8
|
|
9
9
|
require 'rubygems'
|
10
|
-
require 'activefacts'
|
11
|
-
require 'activefacts/vocabulary'
|
12
10
|
|
13
|
-
if
|
11
|
+
# Load the ruby debugger before everything else, if requested
|
12
|
+
if d = ENV['DEBUG'] and d.split(/,/).include?('debug')
|
14
13
|
begin
|
15
14
|
require 'ruby-debug'
|
16
|
-
Debugger.start(:post_mortem => true)
|
15
|
+
Debugger.start(:post_mortem => true) # Stop when an exception is thrown, but before it's rescued
|
17
16
|
rescue LoadError
|
18
17
|
# Ok, no debugger, tough luck.
|
19
18
|
end
|
20
19
|
end
|
21
|
-
|
20
|
+
|
21
|
+
require 'activefacts'
|
22
|
+
require 'activefacts/vocabulary'
|
22
23
|
|
23
24
|
arg = ARGV.shift
|
24
25
|
|
25
26
|
# Load the required generator, or the default "text" generator:
|
26
27
|
generator = "text"
|
28
|
+
generator_options = []
|
27
29
|
if arg =~ /^--([^=]*)(?:=(.*))?/
|
28
30
|
generator = $1
|
29
31
|
generator_options = ($2||"").split(/,/)
|
30
32
|
arg = ARGV.shift
|
31
33
|
end
|
34
|
+
|
32
35
|
output_handler = "activefacts/generate/#{generator.downcase}"
|
33
36
|
require output_handler
|
34
37
|
output_class = generator.upcase.gsub(%r{[/\\]+},'::')
|
@@ -36,20 +39,23 @@ output_klass = eval("ActiveFacts::Generate::#{output_class}")
|
|
36
39
|
raise "Expected #{output_handler} to define #{output_class}" unless output_klass
|
37
40
|
|
38
41
|
# Load the file type input method
|
42
|
+
arg, *options = *arg.split(/=/)
|
39
43
|
extension = arg.sub(/\A.*\./,'').downcase
|
40
44
|
input_handler = "activefacts/input/#{extension}"
|
41
45
|
require input_handler
|
46
|
+
|
42
47
|
input_class = extension.upcase
|
43
48
|
input_klass = ActiveFacts::Input.const_get(input_class.to_sym)
|
44
49
|
raise "Expected #{input_handler} to define #{input_class}" unless input_klass
|
45
50
|
|
46
51
|
# Read the input file:
|
47
52
|
begin
|
48
|
-
vocabulary = input_klass.readfile(arg)
|
53
|
+
vocabulary = input_klass.readfile(arg, *options)
|
54
|
+
|
55
|
+
# Generate the output:
|
56
|
+
output_klass.new(vocabulary, *generator_options).generate if vocabulary
|
49
57
|
rescue => e
|
50
58
|
puts "#{e.message}"
|
51
|
-
puts "
|
59
|
+
# puts "\t#{e.backtrace*"\n\t"}"
|
60
|
+
puts "\t#{e.backtrace*"\n\t"}" if debug :exception
|
52
61
|
end
|
53
|
-
|
54
|
-
# Generate the output:
|
55
|
-
output_klass.new(vocabulary, *generator_options).generate if vocabulary
|
data/bin/cql
CHANGED
@@ -1,62 +1,339 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
2
|
#
|
3
|
-
#
|
3
|
+
# ActiveFacts: Interactive CQL command-line. Incomplete; only parses CQL and shows the parse trees
|
4
4
|
#
|
5
5
|
# Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
|
6
6
|
#
|
7
|
-
|
8
|
-
require 'activefacts'
|
9
|
-
require 'activefacts/cql/parser'
|
10
7
|
require 'readline'
|
11
8
|
|
12
|
-
if
|
9
|
+
# Load the ruby debugger before everything else, if requested
|
10
|
+
if d = ENV['DEBUG'] and d.split(/,/).include?('debug')
|
13
11
|
begin
|
14
12
|
require 'ruby-debug'
|
15
|
-
|
13
|
+
Debugger.start(:post_mortem => true) # Stop when an exception is thrown, but before it's rescued
|
16
14
|
rescue LoadError
|
17
15
|
# Ok, no debugger, tough luck.
|
18
16
|
end
|
19
17
|
end
|
20
|
-
true # Ok, got the stack set up, continue to the fault
|
21
18
|
|
22
|
-
|
19
|
+
require 'activefacts'
|
20
|
+
require 'activefacts/cql/compiler'
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
class InteractiveCQL < ActiveFacts::CQL::Compiler
|
23
|
+
def initialize *a
|
24
|
+
@show_tree = false # Show the raw Treetop parse tree
|
25
|
+
super *a
|
26
|
+
self.root = :definition
|
27
|
+
end
|
28
|
+
|
29
|
+
def metacommand(line)
|
30
30
|
# meta-commands start with /
|
31
|
-
|
31
|
+
words = line.split
|
32
|
+
case cmd = words.shift
|
33
|
+
when "/tree"
|
34
|
+
@show_tree = !@show_tree
|
35
|
+
puts "Will #{@show_tree ? "" : "not "}show the parse tree"
|
32
36
|
when "/root"
|
33
|
-
|
34
|
-
puts "
|
37
|
+
self.root = words[0] && words[0].to_sym || :definition
|
38
|
+
puts "Looking for a #{self.root}"
|
39
|
+
when "/list"
|
40
|
+
concepts_by_vocabulary = {}
|
41
|
+
@constellation.Concept.keys.
|
42
|
+
each do |v,t|
|
43
|
+
(concepts_by_vocabulary[v[0]] ||= []) << t
|
44
|
+
end
|
45
|
+
concepts_by_vocabulary.keys.sort.each do |v|
|
46
|
+
puts "#{v}:\n\t" + concepts_by_vocabulary[v].sort*"\n\t"
|
47
|
+
end
|
48
|
+
when "/about"
|
49
|
+
concept = @constellation.Concept[[@vocabulary.identifying_role_values, words[0]]]
|
50
|
+
unless concept
|
51
|
+
puts "Concept #{words[0]} is unknown in #{@vocabulary.name}"
|
52
|
+
return
|
53
|
+
end
|
54
|
+
if concept.is_a?(ActiveFacts::Metamodel::ValueType)
|
55
|
+
puts "\t#{concept.name} is written as #{concept.supertype.name};" if concept.supertype
|
56
|
+
concept.all_value_type_as_supertype.each do |st|
|
57
|
+
puts "#{st.name} is written as #{concept.name};"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
concept.all_role.map{|role| role.fact_type}.uniq.each do |fact_type|
|
61
|
+
puts "\t#{fact_type.default_reading}"
|
62
|
+
end
|
63
|
+
when "/load"
|
64
|
+
begin
|
65
|
+
vocabularies = @constellation.Vocabulary.keys
|
66
|
+
@results = []
|
67
|
+
compile_file words*' '
|
68
|
+
new_vocabularies = @constellation.Vocabulary.keys-vocabularies
|
69
|
+
puts "Loaded new vocabularies: #{new_vocabularies*', '}" unless new_vocabularies.empty?
|
70
|
+
rescue Errno::ENOENT => e
|
71
|
+
$stderr.puts e.message
|
72
|
+
end
|
35
73
|
else
|
36
|
-
|
37
|
-
|
38
|
-
statement = nil
|
39
|
-
elsif parser.root != :definition or
|
40
|
-
line.gsub(/(['"])([^\1\\]|\\.)*\1/,'') =~ /;/
|
41
|
-
# After stripping string literals the line contains a ';', it's the last line of the command:
|
42
|
-
begin
|
43
|
-
result = parser.parse(statement)
|
44
|
-
if result
|
45
|
-
begin
|
46
|
-
p result.value
|
47
|
-
rescue => e
|
48
|
-
puts e.to_s+":"
|
49
|
-
puts e.backtrace*"\n\t" if ENV["DEBUG"] =~ /exception/
|
50
|
-
p result # In case the root is changed and there's no value()
|
51
|
-
end
|
52
|
-
#p parser.definition(result)
|
74
|
+
if cmd == "/help" or cmd == "/"
|
75
|
+
help words
|
53
76
|
else
|
54
|
-
|
77
|
+
puts "Unknown metacommand #{line}?"
|
78
|
+
help
|
55
79
|
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def process(statement)
|
84
|
+
begin
|
85
|
+
@results = []
|
86
|
+
compile(statement)
|
87
|
+
puts(@results.map{|r| "\t"+r.inspect}*"\n")
|
56
88
|
rescue => e
|
57
89
|
puts e
|
58
|
-
puts "\t"+e.backtrace*"\n\t"
|
90
|
+
puts "\t"+e.backtrace*"\n\t" if ENV["DEBUG"] =~ /exception/
|
59
91
|
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def compile_definition ast
|
95
|
+
# Accumulate the results:
|
96
|
+
p ast if @show_tree
|
97
|
+
@results += Array(result = super)
|
98
|
+
result
|
99
|
+
end
|
100
|
+
|
101
|
+
def help words = []
|
102
|
+
if words.empty?
|
103
|
+
puts %Q{
|
104
|
+
Meta-commands are:
|
105
|
+
/about term\t\tDisplay the fact types in which term plays a part
|
106
|
+
/help\t\t\tThis help message
|
107
|
+
/help topic\t\thelp on a specific topic
|
108
|
+
/help topics\t\tList the available help topics
|
109
|
+
/list\t\t\tList all object type names (terms)
|
110
|
+
/load file.cql\tLoad a CQL file
|
111
|
+
/root rule\t\tParse just a fragment of a CQL statement, matching syntax rule only
|
112
|
+
/tree\t\t\tDisplay the abstract syntax tree from each statement parsed
|
113
|
+
}
|
114
|
+
else
|
115
|
+
words.each do |word|
|
116
|
+
case word
|
117
|
+
when /topics/
|
118
|
+
puts %Q{
|
119
|
+
constraint\tHow to enter external constraints
|
120
|
+
entity\t\tHow to define a new entity type
|
121
|
+
fact\t\tHow to define a new fact type
|
122
|
+
instance\tHow to assert instance data (facts)
|
123
|
+
query\tHow to formulate queries (and derived fact types)
|
124
|
+
term\t\tHow to name object types
|
125
|
+
value\t\tHow to define a new value type
|
126
|
+
vocabulary\tHow to specify the target vocabulary
|
127
|
+
}
|
128
|
+
when /constraint/
|
129
|
+
puts %Q{
|
130
|
+
External Constraints are of various types, and use fact readings with
|
131
|
+
role players that may occur in more than one reading. Adjectives, role
|
132
|
+
names and subscripts may be used to disambiguate multiple occurrences
|
133
|
+
of the same term.
|
134
|
+
|
135
|
+
// A subset constraint:
|
136
|
+
Person is an Employee
|
137
|
+
only if Person has Tax File Number;
|
138
|
+
|
139
|
+
// Mandatory constraint:
|
140
|
+
each Person occurs at least one time in
|
141
|
+
Person has mobile Phone Nr,
|
142
|
+
Person has mailing Address;
|
143
|
+
// Alternate syntax:
|
144
|
+
either Person has mobile Phone Nr or Phone has mailing Address;
|
145
|
+
|
146
|
+
// Uniqueness constraint:
|
147
|
+
each combination Series, Number occurs at most one time in
|
148
|
+
Event is in Series,
|
149
|
+
Event has Number;
|
150
|
+
|
151
|
+
// Disjunctive mandatory constraint:
|
152
|
+
each Person occurs one time in
|
153
|
+
Person is employeed by Company,
|
154
|
+
Person receives Unemployment Benefits;
|
155
|
+
Person is unsupported,
|
156
|
+
// Alternate syntax
|
157
|
+
either Employee reports to Manager or Employee runs Company but not both;
|
158
|
+
|
159
|
+
// Equality constraint with joins:
|
160
|
+
Ticket is for Seat that is at Venue
|
161
|
+
if and only if
|
162
|
+
Ticket is for Event that is held at Venue;
|
163
|
+
}
|
164
|
+
when /entity/
|
165
|
+
puts %q{
|
166
|
+
Entity Type with simple identification (a one-to-one with a value type):
|
167
|
+
|
168
|
+
Employee is identified by its Nr; // Asserts the value type 'Employee Nr'
|
169
|
+
|
170
|
+
The Value Type 'Employee Nr' is created if necessary, and the supertype 'Nr'
|
171
|
+
also. You may add alternate readings (to "...has..." and "...is for...")
|
172
|
+
in a where clause.
|
173
|
+
|
174
|
+
Entity Type with a compound identifier:
|
175
|
+
|
176
|
+
Person is identified by given Name and family Name where
|
177
|
+
Person is called given-Name,
|
178
|
+
Person has family-Name;
|
179
|
+
|
180
|
+
An Entity Type may be a subtype of one or more other Entity Types.
|
181
|
+
If no identification is declared for a subtype, it uses the identification
|
182
|
+
of its first supertype:
|
183
|
+
|
184
|
+
Vehicle Policy is a kind of Insurance Policy;
|
185
|
+
}
|
186
|
+
when /fact/
|
187
|
+
puts %Q{
|
188
|
+
A Fact Type consists of one or more readings, where every reading has
|
189
|
+
the same role players interspersed amongst unrestricted (almost!) text.
|
190
|
+
Each role player is a Term (/help term) which denotes a Value Type or
|
191
|
+
Entity Type (including an objectified Fact Type). Terms are always
|
192
|
+
singular (except where a singular group is indicated).
|
193
|
+
|
194
|
+
The last role player in each reading may be preceded by a quantifier
|
195
|
+
(one, at most one, at least one, etc). Note that "one" or "at least one"
|
196
|
+
make the relevant fact instance mandatory:
|
197
|
+
|
198
|
+
Person directs Company,
|
199
|
+
Company is directed by at least 2 Person;
|
200
|
+
|
201
|
+
Any role player may be preceeded or followed by an adjective, using
|
202
|
+
an adjacent hyphen to indicate the adjective. The hyphen is required
|
203
|
+
only for one occurrence of the adjective, and other occurrences will
|
204
|
+
be recognised. Other hyphenated words are allowed in readings, as
|
205
|
+
long as neither word is an existing term.
|
206
|
+
|
207
|
+
// Using the Entity Type 'Person' and Value Type 'Name':
|
208
|
+
Person has one family Name, family-Name is of Person;
|
209
|
+
|
210
|
+
// Using the Entity Type 'Personne' and Value Type 'Nom':
|
211
|
+
Personne à Nom-donné;
|
212
|
+
|
213
|
+
Multiple adjectives may be used, as long as the hyphen has adjacent space:
|
214
|
+
|
215
|
+
Person driving semi-trailer avoided stray- farm Animal;
|
216
|
+
|
217
|
+
Any role player may define a Role Name, which is used to refer to
|
218
|
+
this player in other readings of this fact type:
|
219
|
+
|
220
|
+
Person (as Director) directs Company,
|
221
|
+
Company is directed by Director;
|
222
|
+
|
223
|
+
Any role player may be followed by a role value restriction (/help constraint):
|
224
|
+
|
225
|
+
Person was born on one birth-Date restricted to {'1900/01/01'..};
|
226
|
+
|
227
|
+
A Fact Type which has no embedded uniqueness constraint must be
|
228
|
+
objectified (named) by prefixing it with an "is where" clause.
|
229
|
+
An objectified fact type is an Entity Type, so maye be used as a
|
230
|
+
player in subsequent Fact Types:
|
231
|
+
|
232
|
+
Directorship is where
|
233
|
+
Person directs Company;
|
234
|
+
Directorship began on at most one appointment-Date;
|
235
|
+
|
236
|
+
Finally, an objectified Fact Type may be a subtype, and may also have
|
237
|
+
separate identification like an Entity Type (/help entity):
|
238
|
+
|
239
|
+
Vehicle Claim is a kind of Insurance Claim identified by Incident ID where
|
240
|
+
Incident ID is of one Vehicle Claim,
|
241
|
+
Vehicle Claim has one Incident ID,
|
242
|
+
Insured Person claimed against Policy on Date;
|
243
|
+
}
|
244
|
+
when /instance/
|
245
|
+
puts %Q{
|
246
|
+
An instance (or a collection of instances) is a reading or term (or
|
247
|
+
joined collection of readings) with values following the Value Types.
|
248
|
+
Simply-identified entities may be contracted by providing the value directly.
|
249
|
+
Adjectives, role names and subscripts may be used to differentiate multiple
|
250
|
+
occurrences of the same object types.
|
251
|
+
|
252
|
+
Company Name 'Microsoft'; // An instance of Company Name (a Value Type)
|
253
|
+
Company 'Sun'; // An instance of Company identified by the Company Name 'Sun'
|
254
|
+
Company 'Sun' was founded on Date '1982-02-24'; // A simple binary fact over two objects
|
255
|
+
family Name 'Smith' is of Person, Person has given Name 'Daniel'; // Two facts joined
|
256
|
+
family Name 'Smith' is of Person who has given Name 'Daniel'; // The same, contracted
|
257
|
+
// A collection of facts involving several kinds of join:
|
258
|
+
Directorship (where Person directs Company 'Sun') began in Year '1982',
|
259
|
+
family Name 'Joy' is of Person that has given Name 'Bill',
|
260
|
+
Person was born on Date '1954-11-8;
|
261
|
+
}
|
262
|
+
when /query/
|
263
|
+
puts %Q{
|
264
|
+
Queries are not yet implemented
|
265
|
+
}
|
266
|
+
when /term/
|
267
|
+
puts %Q{
|
268
|
+
A Term is used to identify a Value Type or Entity Type (collectively, Object
|
269
|
+
Types). A Term may be any word or sequence of words, each word being one
|
270
|
+
or more alphanumeric characters (or underscore) but not starting with a
|
271
|
+
digit. Terms are preferably capitalised; this makes it easier to construct
|
272
|
+
unambiguous fact type readings.
|
273
|
+
|
274
|
+
Many keywords are disallowed as Terms, but not all.
|
275
|
+
}
|
276
|
+
when /value/
|
277
|
+
puts %Q{
|
278
|
+
A Value Type is a kind of Object Type which has an accepted written form.
|
279
|
+
For example, a Name or a Date can be written, but a Person cannot. A Year
|
280
|
+
is not a Value Type, but a Year Number is.
|
281
|
+
|
282
|
+
Every written value must uniquely identify one instance of that Value Type.
|
283
|
+
A value may have more than one written form. For example, the number 10000
|
284
|
+
may also be written 10E4. A written value may identify a single instance of
|
285
|
+
more than one value type however; the string '9/11' may identify a fraction,
|
286
|
+
or an event.
|
287
|
+
|
288
|
+
Value Types are asserted by the phrase "is written as":
|
289
|
+
|
290
|
+
Birth Date is written as Date;
|
291
|
+
Employee Nr is written as Integer;
|
292
|
+
|
293
|
+
If the supertype (the value type after "is written as") is unknown, it is
|
294
|
+
also asserted as a Value Type.
|
295
|
+
|
296
|
+
A Value Type may have implicit length and scale properties, and in future,
|
297
|
+
may also have custom properties. Property values are defined in parentheses
|
298
|
+
after the supertype:
|
299
|
+
|
300
|
+
Money Amount is written as Decimal(14, 2);
|
301
|
+
Company Name is written as String(60);
|
302
|
+
|
303
|
+
After any parameter list, a Value Type may be associated with a Unit, and be
|
304
|
+
subject to a Value Restriction (using "restricted to { <values or ranges> }").
|
305
|
+
Either the low end or the high end of a range may be omitted for an open range.
|
306
|
+
|
307
|
+
Drill Size is written as Real in mm restricted to { 0.3, 0.5..15.0};
|
308
|
+
}
|
309
|
+
when /vocabulary/
|
310
|
+
%q{
|
311
|
+
Every CQL definition or instance exists in some vocabulary, so you must
|
312
|
+
specify a vocabulary name before any other definitions:
|
313
|
+
|
314
|
+
vocabulary Employment;
|
315
|
+
... definitions...
|
316
|
+
}
|
317
|
+
else
|
318
|
+
puts "topic #{word} is unknown"
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
compiler = InteractiveCQL.new
|
326
|
+
statement = nil
|
327
|
+
puts "Enter / for help on special commands"
|
328
|
+
while line = Readline::readline(statement ? "CQL+ " : "CQL? ", [])
|
329
|
+
statement = statement ? statement + "\n"+line : line
|
330
|
+
if line =~ %r{\A/}
|
331
|
+
compiler.metacommand(line)
|
332
|
+
statement = nil
|
333
|
+
elsif compiler.root != :definition or
|
334
|
+
line.gsub(/(['"])([^\1\\]|\\.)*\1/,'') =~ /[;?]/
|
335
|
+
# After stripping string literals the line contains a ';' or /?', we've found the last line of the command:
|
336
|
+
compiler.process(statement)
|
60
337
|
statement = nil
|
61
338
|
end
|
62
339
|
end
|