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.
Files changed (84) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +83 -0
  3. data/README.rdoc +81 -0
  4. data/Rakefile +41 -0
  5. data/bin/afgen +46 -0
  6. data/bin/cql +52 -0
  7. data/examples/CQL/Address.cql +46 -0
  8. data/examples/CQL/Blog.cql +54 -0
  9. data/examples/CQL/CompanyDirectorEmployee.cql +51 -0
  10. data/examples/CQL/Death.cql +16 -0
  11. data/examples/CQL/Genealogy.cql +95 -0
  12. data/examples/CQL/Marriage.cql +18 -0
  13. data/examples/CQL/Metamodel.cql +238 -0
  14. data/examples/CQL/MultiInheritance.cql +19 -0
  15. data/examples/CQL/OilSupply.cql +47 -0
  16. data/examples/CQL/Orienteering.cql +108 -0
  17. data/examples/CQL/PersonPlaysGame.cql +17 -0
  18. data/examples/CQL/SchoolActivities.cql +31 -0
  19. data/examples/CQL/SimplestUnary.cql +12 -0
  20. data/examples/CQL/SubtypePI.cql +32 -0
  21. data/examples/CQL/Warehousing.cql +99 -0
  22. data/examples/CQL/WindowInRoomInBldg.cql +22 -0
  23. data/lib/activefacts.rb +10 -0
  24. data/lib/activefacts/api.rb +25 -0
  25. data/lib/activefacts/api/concept.rb +384 -0
  26. data/lib/activefacts/api/constellation.rb +106 -0
  27. data/lib/activefacts/api/entity.rb +239 -0
  28. data/lib/activefacts/api/instance.rb +54 -0
  29. data/lib/activefacts/api/numeric.rb +158 -0
  30. data/lib/activefacts/api/role.rb +94 -0
  31. data/lib/activefacts/api/standard_types.rb +67 -0
  32. data/lib/activefacts/api/support.rb +59 -0
  33. data/lib/activefacts/api/value.rb +122 -0
  34. data/lib/activefacts/api/vocabulary.rb +120 -0
  35. data/lib/activefacts/cql.rb +31 -0
  36. data/lib/activefacts/cql/CQLParser.treetop +104 -0
  37. data/lib/activefacts/cql/Concepts.treetop +112 -0
  38. data/lib/activefacts/cql/DataTypes.treetop +66 -0
  39. data/lib/activefacts/cql/Expressions.treetop +113 -0
  40. data/lib/activefacts/cql/FactTypes.treetop +185 -0
  41. data/lib/activefacts/cql/Language/English.treetop +92 -0
  42. data/lib/activefacts/cql/LexicalRules.treetop +169 -0
  43. data/lib/activefacts/cql/Rakefile +6 -0
  44. data/lib/activefacts/cql/parser.rb +88 -0
  45. data/lib/activefacts/generate/absorption.rb +87 -0
  46. data/lib/activefacts/generate/cql.rb +441 -0
  47. data/lib/activefacts/generate/cql/html.rb +397 -0
  48. data/lib/activefacts/generate/null.rb +19 -0
  49. data/lib/activefacts/generate/ordered.rb +557 -0
  50. data/lib/activefacts/generate/ruby.rb +326 -0
  51. data/lib/activefacts/generate/sql/server.rb +164 -0
  52. data/lib/activefacts/generate/text.rb +21 -0
  53. data/lib/activefacts/input/cql.rb +1268 -0
  54. data/lib/activefacts/input/orm.rb +926 -0
  55. data/lib/activefacts/persistence.rb +1 -0
  56. data/lib/activefacts/persistence/composition.rb +653 -0
  57. data/lib/activefacts/support.rb +51 -0
  58. data/lib/activefacts/version.rb +3 -0
  59. data/lib/activefacts/vocabulary.rb +6 -0
  60. data/lib/activefacts/vocabulary/extensions.rb +343 -0
  61. data/lib/activefacts/vocabulary/metamodel.rb +303 -0
  62. data/script/txt2html +71 -0
  63. data/spec/absorption_spec.rb +95 -0
  64. data/spec/api/autocounter.rb +82 -0
  65. data/spec/api/constellation.rb +130 -0
  66. data/spec/api/entity_type.rb +101 -0
  67. data/spec/api/instance.rb +428 -0
  68. data/spec/api/roles.rb +122 -0
  69. data/spec/api/value_type.rb +112 -0
  70. data/spec/api_spec.rb +14 -0
  71. data/spec/cql_cql_spec.rb +58 -0
  72. data/spec/cql_parse_spec.rb +31 -0
  73. data/spec/cql_ruby_spec.rb +60 -0
  74. data/spec/cql_sql_spec.rb +54 -0
  75. data/spec/cql_symbol_tables_spec.rb +259 -0
  76. data/spec/cql_unit_spec.rb +336 -0
  77. data/spec/cqldump_spec.rb +169 -0
  78. data/spec/norma_cql_spec.rb +48 -0
  79. data/spec/norma_ruby_spec.rb +50 -0
  80. data/spec/norma_sql_spec.rb +45 -0
  81. data/spec/norma_tables_spec.rb +94 -0
  82. data/spec/spec.opts +1 -0
  83. data/spec/spec_helper.rb +10 -0
  84. metadata +173 -0
@@ -0,0 +1,31 @@
1
+ #
2
+ # ActiveFacts CQL loader.
3
+ # Copyright (c) 2007 Clifford Heath. Read the LICENSE file.
4
+ #
5
+ require 'rubygems'
6
+ require 'polyglot'
7
+ require 'activefacts/support'
8
+ require 'activefacts/input/cql'
9
+ require 'activefacts/generate/ruby'
10
+
11
+ module ActiveFacts
12
+ # Extend the generated parser:
13
+ class CQLLoader
14
+ # This load method for Polyglot tells it how to _require_ a CQL file.
15
+ # The CQL file is parsed to a vocabulary constellation, which is generated
16
+ # to Ruby code and eval'd, making the generated classes available.
17
+ def self.load(file)
18
+ debug "Loading #{file}" do
19
+ vocabulary = ActiveFacts::Input::CQL.readfile(file)
20
+
21
+ ruby = StringIO.new
22
+ @dumper = ActiveFacts::Generate::RUBY.new(vocabulary.constellation)
23
+ @dumper.generate(ruby)
24
+ ruby.rewind
25
+ eval ruby.read, ::TOPLEVEL_BINDING
26
+ end
27
+ end
28
+ end
29
+
30
+ Polyglot.register('cql', CQLLoader)
31
+ end
@@ -0,0 +1,104 @@
1
+ module ActiveFacts
2
+ grammar CQL
3
+ include LexicalRules
4
+ include Language # One of the language modules provides this module
5
+ include Expressions
6
+ include Concepts
7
+ include DataTypes
8
+ include FactTypes
9
+
10
+ rule cql_file
11
+ s seq:definition*
12
+ {
13
+ def definitions
14
+ seq.elements.map{|e|
15
+ e.value rescue $stderr.puts "Can't call value() on #{e.inspect}"
16
+ }
17
+ end
18
+ }
19
+ end
20
+
21
+ # Each definition has a value() method that returns an array like
22
+ # either [name, [kind, definition]] or [name, kind]:
23
+ rule definition
24
+ vocabulary_definition
25
+ / import_definition
26
+ / constraint
27
+ / concept
28
+ end
29
+
30
+ rule vocabulary_definition
31
+ s vocabulary S id s ';' s
32
+ {
33
+ def value
34
+ [ id.text_value, [ :vocabulary ] ]
35
+ end
36
+ }
37
+ end
38
+
39
+ rule import_definition
40
+ s import S id alias_list ';' s
41
+ {
42
+ def value
43
+ puts "import #{id.text_value}: not implemented"
44
+ [ id.text_value, [ :import ], alias_list.value ]
45
+ end
46
+ }
47
+ end
48
+
49
+ # REVISIT: Need a way to define equivalent readings for fact types here (and in the metamodel)
50
+ rule alias_list
51
+ ( s ',' s alias S aliase_from:id S as S alias_to:id s )*
52
+ {
53
+ def value
54
+ elements.inject({}){|h, e| h[e.aliase_from.text_value] = e.alias_to; h }
55
+ end
56
+ }
57
+ end
58
+
59
+ rule constraint
60
+ subset_constraint /
61
+ equality_constraint /
62
+ set_constraint /
63
+ presence_constraint
64
+ # REVISIT: / value_constraint
65
+ end
66
+
67
+ # presence constraint:
68
+ rule presence_constraint
69
+ s 'each' s ('combination' S)? role_list s 'occurs' s quantifier s 'time' s 'in' s
70
+ readings_list s
71
+ ';' s
72
+ { def value; [ nil, [ :constraint, :presence, role_list.roles, quantifier.value, readings_list.value ] ]; end }
73
+ end
74
+
75
+ rule subset_constraint
76
+ s readings s only s if s r2:readings s ';' s
77
+ { def value; [ nil, [ :constraint, :subset, readings.value, r2.value ] ]; end }
78
+ end
79
+
80
+ # set (exclusion, mandatory exclusion, complex equality) constraint
81
+ rule set_constraint
82
+ s 'for' s 'each' s role_list s quantifier s 'of' s 'these' s 'holds' s ':' s
83
+ readings_list s
84
+ ';' s
85
+ { def value; [ nil, [ :constraint, :set, role_list.roles, quantifier.value, *readings_list.value ] ]; end }
86
+ end
87
+
88
+ rule equality_constraint
89
+ s readings s tail:( if s and s only s if s readings s)+ ';' s
90
+ { def value; [ nil, [ :constraint, :equality, *([readings.value] + tail.elements.map{|e| e.readings.value }) ] ]; end }
91
+ end
92
+
93
+ rule readings_list
94
+ readings tail:( ',' s readings )*
95
+ { def value; [readings.value]+tail.elements.map{|e| e.readings.value }; end }
96
+ end
97
+
98
+ rule readings
99
+ reading s tail:( and s reading s )*
100
+ { def value; [reading.value]+tail.elements.map{|e| e.reading.value }; end }
101
+ end
102
+
103
+ end
104
+ end
@@ -0,0 +1,112 @@
1
+ module ActiveFacts
2
+ module CQL
3
+ grammar Concepts
4
+ rule concept
5
+ entity_type
6
+ / data_type
7
+ / named_fact_type
8
+ / anonymous_fact_type
9
+ end
10
+
11
+ rule entity_type
12
+ s name:id s
13
+ sup:(basetype / subtype)
14
+ ec:entity_conditions?
15
+ ';' s
16
+ {
17
+ def value
18
+ [ name.text_value, [ :entity_type, sup.supers, sup.identifier, ec.empty? ? nil : ec.conditions ] ]
19
+ end
20
+ }
21
+ end
22
+
23
+ rule subtype
24
+ subtype_prefix supertype_list ident:identification?
25
+ {
26
+ def supers; supertype_list.value; end
27
+ def identifier; ident.empty? ? nil : ident.value; end
28
+ }
29
+ end
30
+
31
+ rule supertype_list
32
+ primary:id s
33
+ alternate_supertypes:( ',' s !identified_by name:id s )*
34
+ { def value
35
+ [primary.text_value]+alternate_supertypes.elements.map{|sup| sup.name.text_value}
36
+ end
37
+ }
38
+ end
39
+
40
+ rule basetype
41
+ is s identification
42
+ { def supers; [] end
43
+ def identifier; identification.value; end
44
+ }
45
+ end
46
+
47
+ rule identification
48
+ # REVISIT: Consider distinguishing "-Id" from just "Id", and not prepending the entity type name if no "-"
49
+ identified_by its s id s # Reference Mode
50
+ { def value; {:mode => id.text_value }; end }
51
+ /
52
+ identified_by role_list
53
+ { def value; {:roles => role_list.roles }; end }
54
+ end
55
+
56
+ # Identified by roles... also used for constraints, beware
57
+ rule role_list
58
+ head:role_player s tail:( ( and S / ',' s ) role:role_player s )*
59
+ {
60
+ def roles
61
+ [head.value] + tail.elements.map{|i| i.role.value}
62
+ end
63
+ }
64
+ end
65
+
66
+ # We can't tell which word is an adjective and which is a concept here.
67
+ # The concept might be forward-referenced, but not if adjectives are used.
68
+ # REVISIT: This accepts double-adjective expressions (three words) but they don't work elsewhere
69
+ rule lead_adj
70
+ role_player_id '-'
71
+ end
72
+
73
+ rule trail_adj
74
+ '-' role_player_id
75
+ end
76
+
77
+ rule role_player
78
+ l:lead_adj? tail:(s role_player_id)+ s t:trail_adj?
79
+ {
80
+ def value
81
+ (l.empty? ? [] : [l.role_player_id.text_value]) +
82
+ tail.elements.map{|e| e.role_player_id.text_value } +
83
+ (t.empty? ? [] : [t.role_player_id.text_value])
84
+ end
85
+ }
86
+ end
87
+
88
+ rule role_player_id
89
+ !(role_list_sep / quantifier) id
90
+ end
91
+
92
+ rule role_list_sep
93
+ (where / and / 'occurs')
94
+ end
95
+
96
+ rule reference_mode
97
+ # REVISIT: Adopt ORM2-style reference mode patterns here
98
+ '(' s '.' s mode_name:id s ')' s
99
+ end
100
+
101
+ rule entity_conditions
102
+ (':' / where) s c:conditions?
103
+ {
104
+ def conditions
105
+ c.empty? ? [] : c.condition_list
106
+ end
107
+ }
108
+ end
109
+
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,66 @@
1
+ module ActiveFacts
2
+ module CQL
3
+ grammar DataTypes
4
+ rule data_type
5
+ s name:id
6
+ ( s '=' s / defined_as )
7
+ base:id s
8
+ '(' s tpl:type_parameter_list? ')' s
9
+ u0:(!restricted u1:unit s)?
10
+ r:restriction?
11
+ s ';' s
12
+ {
13
+ def defined_type
14
+ [
15
+ :data_type,
16
+ base.text_value,
17
+ tpl.empty? ? [] : tpl.value,
18
+ !u0.empty? ? u0.u1.text_value : nil,
19
+ !r.empty? ? r.ranges : [],
20
+ ]
21
+ end
22
+
23
+ def value
24
+ [ name.text_value,
25
+ defined_type
26
+ ]
27
+ end
28
+ }
29
+ end
30
+
31
+ rule type_parameter_list
32
+ head:number s tail:( ',' s number s )*
33
+ {
34
+ def value
35
+ [head.value] + tail.elements.map{|i| i.number.value}
36
+ end
37
+ }
38
+ end
39
+
40
+ rule unit
41
+ unit_name:id ('^' '-'? [0-9])?
42
+ end
43
+
44
+ rule restriction
45
+ restricted s to s range_list s unit?
46
+ {
47
+ def ranges
48
+ range_list.ranges
49
+ end
50
+ }
51
+ end
52
+
53
+ rule range_list
54
+ '{' s
55
+ head:range s tail:( ',' s range )*
56
+ '}' s
57
+ {
58
+ def ranges
59
+ [head.value] + tail.elements.map{|e| e.range.value }
60
+ end
61
+ }
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,113 @@
1
+ module ActiveFacts
2
+ module CQL
3
+ grammar Expressions
4
+ rule comparison
5
+ e1:expression s comparator s e2:expression
6
+ {
7
+ def body
8
+ [ comparator.text_value,
9
+ e1.value,
10
+ e2.value
11
+ ]
12
+ end
13
+ }
14
+ end
15
+
16
+ rule comparator
17
+ '<=' / '<' / '=' / '>=' / '>'
18
+ # REVISIT: These words occur in readings, find alternates:
19
+ # / matches / includes
20
+ end
21
+
22
+ rule expression
23
+ sum
24
+ end
25
+
26
+ rule sum
27
+ t0:term s tail:( o:add_op s t1:term s )*
28
+ {
29
+ def value
30
+ return t0.value if tail.empty?
31
+ [ :"+",
32
+ *([t0.value] +
33
+ tail.elements.map{|e|
34
+ e.o.text_value == '-' ? [ :"-", e.t1.value ] : e.t1.value
35
+ }
36
+ )
37
+ ]
38
+ end
39
+ }
40
+ end
41
+
42
+ rule add_op
43
+ '+' / '-'
44
+ end
45
+
46
+ rule term
47
+ f0:factor s tail:( o:mul_op s f1:factor s )*
48
+ {
49
+ def value
50
+ return f0.value if tail.empty?
51
+ [ :"*",
52
+ *([f0.value] +
53
+ tail.elements.map{|e|
54
+ e.o.text_value != '*' ? [ :"-", e.f1.value ] : e.f1.value
55
+ }
56
+ )
57
+ ]
58
+ end
59
+ }
60
+ end
61
+
62
+ rule factor
63
+ literal u:unit? s
64
+ { def value
65
+ u.empty? ? literal.value : [ literal.value, u.text_value ]
66
+ end
67
+ }
68
+ / derived_variable
69
+ / '(' s sum s ')' s { def value; sum.value; end }
70
+ end
71
+
72
+ rule derived_variable
73
+ variable s p:function_call*
74
+ {
75
+ def value
76
+ r = variable.value
77
+ # Apply the function_call operators in order:
78
+ p.elements.each{|p| r = [ p.value, r ] }
79
+ r
80
+ end
81
+ }
82
+ end
83
+
84
+ rule variable
85
+ id0:id o0:( !defined_as s id1:id )?
86
+ {
87
+ def value
88
+ # Variable names may consist of one or two words (optional adjective and a noun):
89
+ r = [ :variable, id0.text_value ]
90
+ r += [ o0.id1.text_value ] unless o0.empty?
91
+ r
92
+ end
93
+ }
94
+ end
95
+
96
+ rule function_call
97
+ '.' s func:id s param_list:( '(' s params:( p0:expression s tail:( ',' s p1:expression s )* )? ')' s )?
98
+ {
99
+ def value
100
+ r = [ :"(", func.text_value ]
101
+ return r if param_list.empty? || param_list.params.empty?
102
+ r += [ param_list.params.p0.value ]
103
+ param_list.params.tail.elements.each{|e|
104
+ r += [ e.p1.value ]
105
+ }
106
+ r
107
+ end
108
+ }
109
+ end
110
+
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,185 @@
1
+ module ActiveFacts
2
+ module CQL
3
+ grammar FactTypes
4
+ rule named_fact_type
5
+ s name:id ( s '=' s / defined_as / s is s where )
6
+ anonymous_fact_type
7
+ {
8
+ def value
9
+ f = anonymous_fact_type.value
10
+ f[0] = name.text_value
11
+ f
12
+ end
13
+ }
14
+ end
15
+
16
+ rule anonymous_fact_type
17
+ f0:fact_clause
18
+ ftail:( (',' / and ) s f1:fact_clause s )*
19
+ ctail:( (':' / where) s c:conditions s)?
20
+ returning_clause?
21
+ s ';' s
22
+ {
23
+ def value
24
+ [
25
+ nil, # Anonymous fact type
26
+ [
27
+ :fact_type,
28
+ [ f0.body, *ftail.elements.map{|e| e.f1.body } ],
29
+ !ctail.empty? ? ctail.c.condition_list : []
30
+ ]
31
+ ]
32
+ end
33
+ }
34
+ end
35
+
36
+ rule returning_clause
37
+ returning return (',' return)*
38
+ end
39
+
40
+ rule return
41
+ by order 'REVISIT: return'
42
+ end
43
+
44
+ rule conditions
45
+ head:condition s tail:( (',' s / and S) next:condition s )*
46
+ {
47
+ def condition_list
48
+ [head.value] + tail.elements.map{|i| i.next.value}
49
+ end
50
+ }
51
+ end
52
+
53
+ rule condition
54
+ head:clause s
55
+ # tail:(or S alternate:clause s )*
56
+ {
57
+ def value
58
+ # if tail.elements.size == 0
59
+ head.clause
60
+ # else
61
+ # [:"||", head.clause] + tail.elements.map{|i| i.alternate.clause}
62
+ # end
63
+ end
64
+ }
65
+ end
66
+
67
+ rule clause
68
+ (comparison / fact_clause)
69
+ {
70
+ def clause
71
+ self.body
72
+ end
73
+ }
74
+ end
75
+
76
+ rule fact_clause
77
+ s q:qualifier? s reading s p:post_qualifiers? s
78
+ {
79
+ def body
80
+ [ :fact_clause,
81
+ (q.empty? ? [] : [ q.text_value ]) +
82
+ (p.empty? ? [] : p.list),
83
+ reading.value
84
+ ]
85
+ end
86
+ }
87
+ end
88
+
89
+ rule qualifier
90
+ maybe / definitely
91
+ end
92
+
93
+ rule post_qualifiers
94
+ '[' s q0:post_qualifier tail:( s ',' s q1:post_qualifier )* s ']' s
95
+ {
96
+ def list
97
+ [q0.text_value] + tail.elements.map{|e| e.q1.text_value}
98
+ end
99
+ }
100
+ end
101
+
102
+ rule post_qualifier
103
+ static / transient / intransitive / transitive / acyclic / symmetric
104
+ end
105
+
106
+ rule reading
107
+ subtype_invocation
108
+ /
109
+ role+
110
+ {
111
+ def value
112
+ elements.map{|r| r.value}
113
+ end
114
+ }
115
+ end
116
+
117
+ # REVISIT: This allows invocation from subtype to supertype. We need the reverse as well (Employee is a Manager).
118
+ # Now that subtyping fact types have readings created during compilation, perhaps these custom rules can be removed?
119
+ rule subtype_invocation
120
+ (('some'/'that') S)? subtype:id s subtype_prefix (('some'/'that') S)? supertype:id
121
+ {
122
+ def value
123
+ [{:subtype => subtype.text_value, :supertype => supertype.text_value }]
124
+ # [subtype.text_value, "is", "a", "subtype", "of", supertype.text_value].map{|w| {:word => w}}
125
+ end
126
+ }
127
+ end
128
+
129
+ # This is the rule that causes most back-tracking. I think you can see why.
130
+ # When we have an expression, we will come down here perhaps multiple times,
131
+ # but find no way out as soon as we hit the trailing non_role.
132
+ rule role
133
+ q:quantifier?
134
+ adj0:(a:role_word '-' s)?
135
+ player:role_word !'-' s?
136
+ adj1:( '-' a:(a:role_word s)? )?
137
+ func:function_call?
138
+ role_name:( '(' s as S r:id s ')' s )?
139
+ lr:( literal / restriction )?
140
+ !non_role
141
+ {
142
+ def value
143
+ r = {}
144
+ quantifier = !q.empty? && q.value # "some" quantifier has nil value
145
+
146
+ r[:quantifier] = quantifier if quantifier
147
+ r[:leading_adjective] = adj0.a.text_value unless adj0.empty?
148
+
149
+ r[:word] = player.text_value
150
+
151
+ r[:trailing_adjective] = adj1.a.a.text_value unless adj1.empty?
152
+ r[:function] = func.value if !func.empty?
153
+ r[:role_name] = role_name.r.text_value unless role_name.empty?
154
+ r[:restriction] = lr.ranges if !lr.empty? && lr.respond_to?(:ranges)
155
+ r[:literal] = lr.value if !lr.empty? && lr.respond_to?(:value)
156
+
157
+ r
158
+ end
159
+ }
160
+ end
161
+
162
+ rule non_role
163
+ # Any of these is illegal in or following a reading:
164
+ comparator
165
+ / add_op
166
+ / mul_op
167
+ end
168
+
169
+ rule role_word
170
+ !non_role_word id
171
+ end
172
+
173
+ rule non_role_word
174
+ # These words are illegal in (but maybe ok following) a reading where a role word is expected:
175
+ and
176
+ / if
177
+ / only
178
+ / or
179
+ / quantifier
180
+ / restriction
181
+ end
182
+
183
+ end
184
+ end
185
+ end