activefacts 0.8.6 → 0.8.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. data/Manifest.txt +33 -2
  2. data/README.rdoc +30 -36
  3. data/Rakefile +16 -20
  4. data/bin/afgen +17 -11
  5. data/bin/cql +313 -36
  6. data/download.html +43 -19
  7. data/examples/CQL/Address.cql +15 -15
  8. data/examples/CQL/Blog.cql +8 -8
  9. data/examples/CQL/CompanyDirectorEmployee.cql +6 -5
  10. data/examples/CQL/Death.cql +3 -3
  11. data/examples/CQL/Diplomacy.cql +48 -0
  12. data/examples/CQL/Genealogy.cql +41 -41
  13. data/examples/CQL/Insurance.cql +311 -0
  14. data/examples/CQL/JoinEquality.cql +35 -0
  15. data/examples/CQL/Marriage.cql +1 -1
  16. data/examples/CQL/Metamodel.cql +290 -185
  17. data/examples/CQL/MetamodelNext.cql +420 -0
  18. data/examples/CQL/Monogamy.cql +24 -0
  19. data/examples/CQL/MonthInSeason.cql +27 -0
  20. data/examples/CQL/Moon.cql +23 -0
  21. data/examples/CQL/MultiInheritance.cql +4 -4
  22. data/examples/CQL/NonRoleId.cql +14 -0
  23. data/examples/CQL/OddIdentifier.cql +18 -0
  24. data/examples/CQL/OilSupply.cql +24 -24
  25. data/examples/CQL/OneToOnes.cql +17 -0
  26. data/examples/CQL/Orienteering.cql +55 -55
  27. data/examples/CQL/OrienteeringER.cql +58 -0
  28. data/examples/CQL/PersonPlaysGame.cql +2 -2
  29. data/examples/CQL/RedundantDependency.cql +34 -0
  30. data/examples/CQL/SchoolActivities.cql +5 -5
  31. data/examples/CQL/SeparateSubtype.cql +28 -0
  32. data/examples/CQL/ServiceDirector.cql +283 -0
  33. data/examples/CQL/SimplestUnary.cql +2 -2
  34. data/examples/CQL/SubtypePI.cql +11 -11
  35. data/examples/CQL/Supervision.cql +38 -0
  36. data/examples/CQL/Tests.Test5.Load.cql +38 -0
  37. data/examples/CQL/WaiterTips.cql +33 -0
  38. data/examples/CQL/Warehousing.cql +55 -53
  39. data/examples/CQL/WindowInRoomInBldg.cql +9 -9
  40. data/examples/CQL/unit.cql +433 -544
  41. data/examples/index.html +314 -170
  42. data/examples/intro.html +6 -176
  43. data/examples/local.css +8 -4
  44. data/index.html +40 -25
  45. data/lib/activefacts/api/concept.rb +2 -2
  46. data/lib/activefacts/api/constellation.rb +4 -4
  47. data/lib/activefacts/api/instance.rb +2 -2
  48. data/lib/activefacts/api/instance_index.rb +4 -0
  49. data/lib/activefacts/api/numeric.rb +3 -1
  50. data/lib/activefacts/api/role.rb +1 -1
  51. data/lib/activefacts/api/standard_types.rb +23 -16
  52. data/lib/activefacts/api/support.rb +3 -1
  53. data/lib/activefacts/api/vocabulary.rb +4 -0
  54. data/lib/activefacts/cql/CQLParser.treetop +87 -39
  55. data/lib/activefacts/cql/Concepts.treetop +95 -69
  56. data/lib/activefacts/cql/Context.treetop +11 -2
  57. data/lib/activefacts/cql/Expressions.treetop +23 -59
  58. data/lib/activefacts/cql/FactTypes.treetop +141 -95
  59. data/lib/activefacts/cql/Language/English.treetop +33 -21
  60. data/lib/activefacts/cql/LexicalRules.treetop +6 -1
  61. data/lib/activefacts/cql/Terms.treetop +75 -26
  62. data/lib/activefacts/cql/ValueTypes.treetop +52 -54
  63. data/lib/activefacts/cql/compiler.rb +46 -1691
  64. data/lib/activefacts/cql/compiler/constraint.rb +602 -0
  65. data/lib/activefacts/cql/compiler/entity_type.rb +425 -0
  66. data/lib/activefacts/cql/compiler/fact.rb +300 -0
  67. data/lib/activefacts/cql/compiler/fact_type.rb +230 -0
  68. data/lib/activefacts/cql/compiler/reading.rb +832 -0
  69. data/lib/activefacts/cql/compiler/shared.rb +109 -0
  70. data/lib/activefacts/cql/compiler/value_type.rb +104 -0
  71. data/lib/activefacts/cql/parser.rb +132 -81
  72. data/lib/activefacts/generate/cql.rb +397 -274
  73. data/lib/activefacts/generate/oo.rb +13 -12
  74. data/lib/activefacts/generate/ordered.rb +107 -117
  75. data/lib/activefacts/generate/ruby.rb +34 -38
  76. data/lib/activefacts/generate/sql/mysql.rb +62 -45
  77. data/lib/activefacts/generate/sql/server.rb +59 -42
  78. data/lib/activefacts/input/cql.rb +6 -3
  79. data/lib/activefacts/input/orm.rb +991 -557
  80. data/lib/activefacts/persistence/columns.rb +16 -12
  81. data/lib/activefacts/persistence/foreignkey.rb +7 -4
  82. data/lib/activefacts/persistence/index.rb +3 -4
  83. data/lib/activefacts/persistence/reference.rb +5 -2
  84. data/lib/activefacts/support.rb +20 -14
  85. data/lib/activefacts/version.rb +1 -1
  86. data/lib/activefacts/vocabulary.rb +1 -0
  87. data/lib/activefacts/vocabulary/extensions.rb +328 -44
  88. data/lib/activefacts/vocabulary/metamodel.rb +145 -20
  89. data/lib/activefacts/vocabulary/verbaliser.rb +621 -0
  90. data/spec/absorption_spec.rb +4 -4
  91. data/spec/api/value_type.rb +1 -1
  92. data/spec/cql/context_spec.rb +45 -22
  93. data/spec/cql/deontic_spec.rb +88 -0
  94. data/spec/cql/matching_spec.rb +517 -0
  95. data/spec/cql/samples_spec.rb +88 -31
  96. data/spec/cql/unit_spec.rb +58 -37
  97. data/spec/cql_cql_spec.rb +12 -7
  98. data/spec/cql_mysql_spec.rb +3 -7
  99. data/spec/cql_parse_spec.rb +0 -4
  100. data/spec/cql_ruby_spec.rb +1 -4
  101. data/spec/cql_sql_spec.rb +5 -18
  102. data/spec/cql_symbol_tables_spec.rb +3 -0
  103. data/spec/cqldump_spec.rb +0 -2
  104. data/spec/helpers/array_matcher.rb +35 -0
  105. data/spec/helpers/ctrl_c_support.rb +52 -0
  106. data/spec/helpers/diff_matcher.rb +38 -0
  107. data/spec/helpers/file_matcher.rb +5 -3
  108. data/spec/helpers/string_matcher.rb +39 -0
  109. data/spec/helpers/test_parser.rb +13 -0
  110. data/spec/norma_cql_spec.rb +13 -5
  111. data/spec/norma_ruby_spec.rb +11 -3
  112. data/spec/{absorption_ruby_spec.rb → norma_ruby_sql_spec.rb} +37 -32
  113. data/spec/norma_sql_spec.rb +11 -5
  114. data/spec/norma_tables_spec.rb +33 -29
  115. data/spec/spec_helper.rb +4 -1
  116. data/status.html +92 -23
  117. metadata +102 -36
  118. data/lib/activefacts/generate/cql/html.rb +0 -403
@@ -25,7 +25,9 @@ class String #:nodoc:
25
25
  end
26
26
 
27
27
  def camelwords
28
- gsub(/([a-z])([A-Z])/,'\1_\2').split(/[_\s]+/)
28
+ gsub(/-([a-zA-Z])/){ $1.upcase }. # Break and upcase on hyphenated words
29
+ gsub(/([a-z])([A-Z])/,'\1_\2').
30
+ split(/[_\s]+/)
29
31
  end
30
32
  end
31
33
 
@@ -36,6 +36,10 @@ module ActiveFacts
36
36
  Constellation.new(self)
37
37
  end
38
38
 
39
+ def populate &b
40
+ constellation.populate &b
41
+ end
42
+
39
43
  def verbalise
40
44
  "Vocabulary #{name}:\n\t" +
41
45
  @concept.keys.sort.map{|concept|
@@ -27,33 +27,45 @@ module ActiveFacts
27
27
  }
28
28
  end
29
29
 
30
- # Each definition has a value() method that returns an array like
31
- # either [name, [kind, definition]] or [name, kind]:
30
+ # Each definition has an ast() method that returns an instance of a subclass of Compiler::Definition
32
31
  rule definition
32
+ definition_body s
33
+ {
34
+ def ast
35
+ definition_body.ast
36
+ end
37
+
38
+ def body
39
+ definition_body.text_value
40
+ end
41
+ }
42
+ end
43
+
44
+ rule definition_body
33
45
  vocabulary_definition
34
46
  / import_definition
35
- / prescan
47
+ / prescan # Always fails, but its side-effects are needed in the following
36
48
  / constraint
37
- / unit_definition
49
+ / unit_definition # REVISIT: Move this above the prescan?
38
50
  / concept
51
+ / s ';' s { def ast; nil; end }
39
52
  end
40
53
 
41
54
  rule vocabulary_definition
42
- s vocabulary S id s ';' s &{|e| input.context.vocabulary(e[3].text_value); true }
55
+ s vocabulary S id s ';'
43
56
  {
44
- def value
45
- [ id.value, [ :vocabulary ] ]
46
- end
57
+ def ast
58
+ Compiler::Vocabulary.new(id.value)
59
+ end
47
60
  }
48
61
  end
49
62
 
50
63
  rule import_definition
51
- s import S id alias_list ';' s
64
+ s import S id alias_list ';'
52
65
  {
53
- def value
54
- puts "import #{id.value}: not implemented"
55
- [ id.value, [ :import ], alias_list.value ]
56
- end
66
+ def ast
67
+ Compiler::Import.new(id.value, alias_list.value)
68
+ end
57
69
  }
58
70
  end
59
71
 
@@ -76,63 +88,99 @@ module ActiveFacts
76
88
  end
77
89
 
78
90
  rule enforcement
79
- s '(' s otherwise s action s agent? s ')' s
80
- { def value; [action.text_value, agent.empty? ? nil : agent.text_value]; end }
91
+ s '(' s otherwise s action s a:agent? s ')' s
92
+ {
93
+ def ast; Compiler::Enforcement.new(action.text_value, a.empty? ? nil : a.text_value); end
94
+ }
81
95
  /
82
96
  ''
83
- { def value; []; end }
97
+ {
98
+ def ast; nil; end
99
+ }
84
100
  end
85
101
 
86
102
  # presence constraint:
87
103
  rule presence_constraint
88
- s 'each' s ('combination' S)? role_list s 'occurs' s quantifier s 'time' s enforcement 'in' s
104
+ s 'each' s ('combination' S)? role_list s 'occurs' s quantifier s 'time' 's'? s enforcement 'in' s
89
105
  readings_list s
90
- c:context? ';' s
91
- { def value; [ nil, [ :constraint, :presence, role_list.roles, quantifier.value, readings_list.value, c.empty? ? nil : c.value, enforcement.value ] ]; end }
106
+ c:context_note? ';'
107
+ {
108
+ def ast
109
+ context_note = c.empty? ? nil : c.ast
110
+ Compiler::PresenceConstraint.new context_note, enforcement.ast, readings_list.ast, role_list.ast, quantifier.ast
111
+ end
112
+ }
92
113
  end
93
114
 
94
115
  # set (exclusion, mandatory exclusion, complex equality) constraint
95
116
  rule set_constraint
96
117
  s 'for' s 'each' s role_list s quantifier s 'of' s 'these' s 'holds' s enforcement ':' s
97
118
  readings_list s
98
- c:context? ';' s
99
- { def value; [ nil, [ :constraint, :set, role_list.roles, quantifier.value, readings_list.value, c.empty? ? nil : c.value, enforcement.value ] ]; end }
119
+ c:context_note? ';'
120
+ {
121
+ def ast
122
+ context_note = c.empty? ? nil : c.ast
123
+ Compiler::SetExclusionConstraint.new context_note, enforcement.ast, readings_list.ast, role_list.ast, quantifier.ast
124
+ end
125
+ }
100
126
  /
101
- s either? s r1:readings s or s r2:readings exclusion:(but s not s both s)? c:context? enforcement ';' s
102
- { def value;
103
- [ nil, [ :constraint, :set,
104
- nil, # No roles names, rely on the join
105
- exclusion.text_value.empty? ?
106
- [1, nil] : # At least one (meaning 1 or 2/more)
107
- [1,1], # Exactly one (1 and only 1)
108
- [r1.value, r2.value],
109
- c.empty? ? nil : c.value,
110
- enforcement.value
111
- ] ];
112
- end
127
+ s either? s r1:readings s or s r2:readings exclusion:(but s not s both s)? c:context_note? enforcement ';'
128
+ {
129
+ def ast
130
+ context_note = c.empty? ? nil : c.ast
131
+
132
+ if exclusion.text_value.empty?
133
+ Compiler::PresenceConstraint.new context_note, enforcement.ast, [r1.ast, r2.ast], nil, Compiler::Quantifier.new(1, nil)
134
+ else
135
+ quantifier = Compiler::Quantifier.new(*(exclusion.text_value.empty? ? [1, nil] : [1, 1]))
136
+ #quantifier = Compiler::Quantifier.new(1, 1)
137
+ Compiler::SetExclusionConstraint.new context_note, enforcement.ast, [r1.ast, r2.ast], nil, quantifier
138
+ end
139
+ end
113
140
  }
114
141
  end
115
142
 
116
143
  rule subset_constraint
117
144
  s readings s only s if s r2:readings s
118
- c:context? enforcement ';' s
119
- { def value; [ nil, [ :constraint, :subset, [readings.value, r2.value], c.empty? ? nil : c.value, enforcement.value ] ]; end }
145
+ c:context_note? enforcement ';'
146
+ {
147
+ def ast
148
+ context_note = c.empty? ? nil : c.ast
149
+ Compiler::SubsetConstraint.new context_note, enforcement.ast, [readings.ast, r2.ast]
150
+ end
151
+ }
120
152
  end
121
153
 
122
154
  rule equality_constraint
123
155
  s readings s tail:( if s and s only s if s readings s)+
124
- c:context? enforcement ';' s
125
- { def value; [ nil, [ :constraint, :equality, [readings.value] + tail.elements.map{|e| e.readings.value }, c.empty? ? nil : c.value, enforcement.value ]]; end }
156
+ c:context_note? enforcement ';'
157
+ {
158
+ def ast
159
+ context_note = c.empty? ? nil : c.ast
160
+ all_readings = [readings.ast, *tail.elements.map{|e| e.readings.ast }]
161
+ Compiler::SetEqualityConstraint.new context_note, enforcement.ast, all_readings
162
+ end
163
+ }
126
164
  end
127
165
 
128
166
  rule readings_list
129
167
  readings tail:( ',' s readings )*
130
- { def value; [readings.value]+tail.elements.map{|e| e.readings.value }; end }
168
+ {
169
+ def ast
170
+ [readings.ast, *tail.elements.map{|e| e.readings.ast }]
171
+ end
172
+ }
131
173
  end
132
174
 
133
175
  rule readings
134
176
  reading s tail:( and s reading s )*
135
- { def value; [reading.value]+tail.elements.map{|e| e.reading.value }; end }
177
+ {
178
+ def ast
179
+ readings = reading.ast
180
+ tail.elements.map{|e| readings += e.reading.ast }
181
+ readings
182
+ end
183
+ }
136
184
  end
137
185
 
138
186
  end
@@ -17,20 +17,24 @@ module ActiveFacts
17
17
  rule entity_type
18
18
  s term_definition_name
19
19
  sup:(basetype / subtype)
20
- &{|e| input.context.entity_type(e[1].value); true }
20
+ &{|s|
21
+ # There's an implicit type when we use an identification mode, register it:
22
+ mode = s[2].identification_mode
23
+ if mode
24
+ input.context.object_type(s[1].value+mode, "identification mode type")
25
+ input.context.object_type(s[1].value+' '+mode, "identification mode type")
26
+ end
27
+ true
28
+ }
21
29
  mapping_pragmas
22
30
  ec:entity_conditions?
23
- ';' s
31
+ ';'
24
32
  {
25
- def defined_type
26
- [ :entity_type, sup.supers, sup.identifier, mapping_pragmas.value, (ec.empty? ? nil : ec.conditions) ]
27
- end
28
-
29
- def value
30
- [ term_definition_name.value,
31
- defined_type
32
- ]
33
- end
33
+ def ast
34
+ name = term_definition_name.value
35
+ readings = ec.empty? ? [] : ec.ast
36
+ Compiler::EntityType.new name, sup.supers, sup.ast, mapping_pragmas.value, readings
37
+ end
34
38
  }
35
39
  end
36
40
 
@@ -38,8 +42,10 @@ module ActiveFacts
38
42
  is s
39
43
  # independency?
40
44
  identification
41
- { def supers; [] end
42
- def identifier; identification.value; end
45
+ {
46
+ def ast; identification.ast; end
47
+ def supers; []; end
48
+ def identification_mode; identification.mode; end
43
49
  }
44
50
  end
45
51
 
@@ -48,8 +54,9 @@ module ActiveFacts
48
54
  # independency?
49
55
  supertype_list ident:identification?
50
56
  {
57
+ def ast; ident.empty? ? nil : ident.ast; end
51
58
  def supers; supertype_list.value; end
52
- def identifier; ident.empty? ? nil : ident.value; end
59
+ def identification_mode; ident.empty? ? nil : ident.mode; end
53
60
  }
54
61
  end
55
62
 
@@ -59,80 +66,95 @@ module ActiveFacts
59
66
  end
60
67
 
61
68
  rule supertype_list
62
- primary:id s
63
- alternate_supertypes:( ',' s !identified_by name:id s )*
64
- { def value
65
- [primary.value]+alternate_supertypes.elements.map{|sup| sup.name.value}
66
- end
67
- }
69
+ primary:term s alternate_supertypes:( ',' s !identified_by name:term s )*
70
+ {
71
+ def value
72
+ [primary.value, *alternate_supertypes.elements.map { |sup| sup.name.value } ]
73
+ end
74
+ }
68
75
  end
69
76
 
70
77
  rule identification
71
78
  # REVISIT: Consider distinguishing "-Id" from just "Id", and not prepending the entity type name if no "-"
72
- identified_by its s id value_type_parameters
73
- r:(restriction enforcement)? # Reference Mode; restriction may be needed for the ValueType
74
- { def value
75
- { :mode => id.value,
76
- :restriction => (r.text_value.empty? ? nil : r.restriction.ranges),
77
- :enforcement => (r.text_value.empty? ? nil : r.enforcement.value),
78
- :parameters => value_type_parameters.values
79
- }
79
+ identified_by its s i:(term/id) value_type_parameters
80
+ r:(value_constraint enforcement)? # Reference Mode; value_constraint may be needed for the ValueType
81
+ {
82
+ def ast
83
+ if r.empty?
84
+ value_constraint = nil
85
+ else
86
+ value_constraint = Compiler::ValueConstraint.new(r.value_constraint.ranges, r.enforcement.ast)
87
+ end
88
+ Compiler::ReferenceMode.new(i.value, value_constraint, value_type_parameters.values)
89
+ end
90
+
91
+ def mode
92
+ i.value
80
93
  end
81
94
  }
82
95
  /
83
96
  identified_by role_list
84
- { def value; {:roles => role_list.roles }; end }
97
+ &{|s|
98
+ role_list = s[-1]
99
+ forwards = role_list.ast.
100
+ map do |role_ref|
101
+ next nil if role_ref.is_a?(Compiler::Reading) # Can't forward-reference unaries
102
+ next nil if role_ref.leading_adjective or role_ref.trailing_adjective
103
+ role_ref.term
104
+ end.
105
+ compact
106
+ input.context.allowed_forward_terms(forwards)
107
+ true
108
+ }
109
+ {
110
+ def ast
111
+ role_list.ast
112
+ end
113
+
114
+ def mode
115
+ nil
116
+ end
117
+ }
85
118
  end
86
119
 
87
120
  # Identified by roles... also used for constraints, beware
88
121
  rule role_list
89
- head:role_player s tail:( ( and S / ',' s ) role:role_player s )*
122
+ head:term_or_unary s tail:( ( and S / ',' s ) term_or_unary s )*
90
123
  {
91
- def roles
92
- [head.value] + tail.elements.map{|i| i.role.value}
93
- end
124
+ def ast
125
+ [head.ast, *tail.elements.map{|e| e.term_or_unary.ast}]
126
+ end
94
127
  }
95
128
  end
96
129
 
97
- # We can't tell which word is an adjective and which is a concept here.
98
- # The concept might be forward-referenced, but not if adjectives are used.
99
- # REVISIT: This accepts double-adjective expressions (three words) but they don't work elsewhere
100
- rule lead_adj
101
- role_player_id '-'
102
- end
103
-
104
- rule trail_adj
105
- '-' role_player_id
106
- end
107
-
108
- rule role_player
109
- l:lead_adj? tail:(s role_player_id)+ s t:trail_adj?
110
- {
111
- def value
112
- (l.empty? ? [] : [l.role_player_id.value]) +
113
- tail.elements.map{|e| e.role_player_id.value } +
114
- (t.empty? ? [] : [t.role_player_id.value])
115
- end
116
- }
117
- end
118
-
119
- rule role_player_id
120
- !(role_list_sep / quantifier) id
121
- { def value; id.value; end }
122
- end
123
-
124
- rule role_list_sep
125
- (where / and / 'occurs')
126
- end
127
-
128
- rule reference_mode
129
- # REVISIT: Adopt ORM2-style reference mode patterns here
130
- '(' s '.' s mode_name:id s ')' s
131
- end
130
+ rule term_or_unary
131
+ pre_text:(s !non_role_word !term id)* s term post_text:(s !non_role_word !term id)* s ss:subscript?
132
+ {
133
+ def ast
134
+ t = term.ast
135
+ t.role_name = ss.value if !ss.empty?
136
+ if pre_text.elements.size == 0 && post_text.elements.size == 0
137
+ t
138
+ else
139
+ pre_words = pre_text.elements.map{|w| w.id.text_value}
140
+ post_words = post_text.elements.map{|w| w.id.text_value}
141
+ Compiler::Reading.new(pre_words + [t] + post_words, [], nil)
142
+ end
143
+ end
144
+ }
145
+ /
146
+ s !non_role_word id s &non_role_word s ss:subscript
147
+ { # A forward-referenced entity type
148
+ # REVISIT: A change in this rule might allow forward-referencing a multi-word term
149
+ def ast
150
+ Compiler::RoleRef.new(id.text_value, nil, nil, nil, ss.empty? ? nil : ss.value)
151
+ end
152
+ }
153
+ end
132
154
 
133
155
  rule mapping_pragmas
134
156
  '[' s h:mapping_pragma t:(s ',' s mapping_pragma)* s ']' s
135
- { def value; [h.value] + t.elements.map{|e| e.mapping_pragma.value}; end }
157
+ { def value; t.elements.inject([h.value]) { |a, e| a << e.mapping_pragma.value }; end }
136
158
  /
137
159
  ''
138
160
  { def value; []; end }
@@ -146,6 +168,10 @@ module ActiveFacts
146
168
  rule entity_conditions
147
169
  (':' / where) s c:conditions?
148
170
  {
171
+ def ast
172
+ c.empty? ? [] : c.ast
173
+ end
174
+
149
175
  def conditions
150
176
  c.empty? ? [] : c.condition_list
151
177
  end
@@ -7,11 +7,20 @@
7
7
  module ActiveFacts
8
8
  module CQL
9
9
  grammar Context
10
- rule context
10
+ rule context_note
11
11
  '('
12
12
  w:who_says? s context_type discussion agreed:(',' a:as_agreed_by)? s
13
13
  ')'
14
- { def value; [ w.empty? ? nil : w.value, context_type.value, discussion.text_value, agreed.empty? ? [] : agreed.a.value]; end }
14
+ {
15
+ def value
16
+ [ w.empty? ? nil : w.value, context_type.value, discussion.text_value, agreed.empty? ? [] : agreed.a.value]
17
+ end
18
+ def ast
19
+ who = w.empty? ? nil : w.value
20
+ ag = agreed.empty? ? [] : agreed.a.value
21
+ Compiler::ContextNote.new context_type.value, discussion.text_value, who, ag
22
+ end
23
+ }
15
24
  end
16
25
 
17
26
  rule who_says