activefacts 1.6.0 → 1.7.1

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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +14 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +60 -0
  8. data/Rakefile +3 -80
  9. data/activefacts.gemspec +36 -0
  10. data/bin/afgen +4 -2
  11. data/bin/cql +5 -1
  12. data/lib/activefacts.rb +3 -12
  13. data/lib/activefacts/{vocabulary/query_evaluator.rb → query/evaluator.rb} +0 -0
  14. data/lib/activefacts/version.rb +2 -2
  15. metadata +48 -296
  16. data/History.txt +0 -4
  17. data/LICENSE +0 -19
  18. data/Manifest.txt +0 -165
  19. data/README.rdoc +0 -81
  20. data/css/offline.css +0 -3
  21. data/css/orm2.css +0 -124
  22. data/css/print.css +0 -8
  23. data/css/style-print.css +0 -357
  24. data/css/style.css +0 -387
  25. data/download.html +0 -110
  26. data/examples/CQL/Address.cql +0 -44
  27. data/examples/CQL/Blog.cql +0 -54
  28. data/examples/CQL/CompanyDirectorEmployee.cql +0 -56
  29. data/examples/CQL/Death.cql +0 -17
  30. data/examples/CQL/Diplomacy.cql +0 -48
  31. data/examples/CQL/Genealogy.cql +0 -98
  32. data/examples/CQL/Insurance.cql +0 -320
  33. data/examples/CQL/Marriage.cql +0 -18
  34. data/examples/CQL/Metamodel.cql +0 -493
  35. data/examples/CQL/Monogamy.cql +0 -24
  36. data/examples/CQL/MultiInheritance.cql +0 -22
  37. data/examples/CQL/NonRoleId.cql +0 -14
  38. data/examples/CQL/OddIdentifier.cql +0 -18
  39. data/examples/CQL/OilSupply.cql +0 -53
  40. data/examples/CQL/OneToOnes.cql +0 -17
  41. data/examples/CQL/Orienteering.cql +0 -111
  42. data/examples/CQL/PersonPlaysGame.cql +0 -18
  43. data/examples/CQL/RedundantDependency.cql +0 -34
  44. data/examples/CQL/SchoolActivities.cql +0 -33
  45. data/examples/CQL/SeparateSubtype.cql +0 -30
  46. data/examples/CQL/ServiceDirector.cql +0 -276
  47. data/examples/CQL/SimplestUnary.cql +0 -12
  48. data/examples/CQL/Supervision.cql +0 -34
  49. data/examples/CQL/WaiterTips.cql +0 -33
  50. data/examples/CQL/Warehousing.cql +0 -101
  51. data/examples/CQL/WindowInRoomInBldg.cql +0 -28
  52. data/examples/CQL/unit.cql +0 -474
  53. data/examples/index.html +0 -420
  54. data/examples/intro.html +0 -327
  55. data/examples/local.css +0 -24
  56. data/index.html +0 -111
  57. data/lib/activefacts/cql.rb +0 -35
  58. data/lib/activefacts/cql/CQLParser.treetop +0 -158
  59. data/lib/activefacts/cql/Context.treetop +0 -48
  60. data/lib/activefacts/cql/Expressions.treetop +0 -67
  61. data/lib/activefacts/cql/FactTypes.treetop +0 -358
  62. data/lib/activefacts/cql/Language/English.treetop +0 -315
  63. data/lib/activefacts/cql/LexicalRules.treetop +0 -253
  64. data/lib/activefacts/cql/ObjectTypes.treetop +0 -210
  65. data/lib/activefacts/cql/Rakefile +0 -14
  66. data/lib/activefacts/cql/Terms.treetop +0 -183
  67. data/lib/activefacts/cql/ValueTypes.treetop +0 -202
  68. data/lib/activefacts/cql/compiler.rb +0 -156
  69. data/lib/activefacts/cql/compiler/clause.rb +0 -1137
  70. data/lib/activefacts/cql/compiler/constraint.rb +0 -581
  71. data/lib/activefacts/cql/compiler/entity_type.rb +0 -457
  72. data/lib/activefacts/cql/compiler/expression.rb +0 -443
  73. data/lib/activefacts/cql/compiler/fact.rb +0 -390
  74. data/lib/activefacts/cql/compiler/fact_type.rb +0 -421
  75. data/lib/activefacts/cql/compiler/query.rb +0 -106
  76. data/lib/activefacts/cql/compiler/shared.rb +0 -161
  77. data/lib/activefacts/cql/compiler/value_type.rb +0 -174
  78. data/lib/activefacts/cql/nodes.rb +0 -49
  79. data/lib/activefacts/cql/parser.rb +0 -241
  80. data/lib/activefacts/dependency_analyser.rb +0 -182
  81. data/lib/activefacts/generate/absorption.rb +0 -70
  82. data/lib/activefacts/generate/composition.rb +0 -118
  83. data/lib/activefacts/generate/cql.rb +0 -714
  84. data/lib/activefacts/generate/dm.rb +0 -279
  85. data/lib/activefacts/generate/help.rb +0 -64
  86. data/lib/activefacts/generate/helpers/inject.rb +0 -16
  87. data/lib/activefacts/generate/helpers/oo.rb +0 -162
  88. data/lib/activefacts/generate/helpers/ordered.rb +0 -605
  89. data/lib/activefacts/generate/helpers/rails.rb +0 -57
  90. data/lib/activefacts/generate/html/glossary.rb +0 -461
  91. data/lib/activefacts/generate/json.rb +0 -337
  92. data/lib/activefacts/generate/null.rb +0 -32
  93. data/lib/activefacts/generate/rails/models.rb +0 -246
  94. data/lib/activefacts/generate/rails/schema.rb +0 -216
  95. data/lib/activefacts/generate/records.rb +0 -46
  96. data/lib/activefacts/generate/ruby.rb +0 -133
  97. data/lib/activefacts/generate/sql/mysql.rb +0 -280
  98. data/lib/activefacts/generate/sql/server.rb +0 -273
  99. data/lib/activefacts/generate/stats.rb +0 -69
  100. data/lib/activefacts/generate/text.rb +0 -27
  101. data/lib/activefacts/generate/topics.rb +0 -265
  102. data/lib/activefacts/generate/traits/datavault.rb +0 -241
  103. data/lib/activefacts/generate/traits/oo.rb +0 -73
  104. data/lib/activefacts/generate/traits/ordered.rb +0 -33
  105. data/lib/activefacts/generate/traits/ruby.rb +0 -210
  106. data/lib/activefacts/generate/transform/datavault.rb +0 -266
  107. data/lib/activefacts/generate/transform/surrogate.rb +0 -214
  108. data/lib/activefacts/generate/version.rb +0 -26
  109. data/lib/activefacts/input/cql.rb +0 -43
  110. data/lib/activefacts/input/orm.rb +0 -1636
  111. data/lib/activefacts/mapping/rails.rb +0 -132
  112. data/lib/activefacts/persistence.rb +0 -15
  113. data/lib/activefacts/persistence/columns.rb +0 -446
  114. data/lib/activefacts/persistence/foreignkey.rb +0 -187
  115. data/lib/activefacts/persistence/index.rb +0 -240
  116. data/lib/activefacts/persistence/object_type.rb +0 -198
  117. data/lib/activefacts/persistence/reference.rb +0 -434
  118. data/lib/activefacts/persistence/tables.rb +0 -380
  119. data/lib/activefacts/registry.rb +0 -11
  120. data/lib/activefacts/support.rb +0 -132
  121. data/lib/activefacts/vocabulary.rb +0 -9
  122. data/lib/activefacts/vocabulary/extensions.rb +0 -1348
  123. data/lib/activefacts/vocabulary/metamodel.rb +0 -570
  124. data/lib/activefacts/vocabulary/verbaliser.rb +0 -804
  125. data/script/txt2html +0 -71
  126. data/spec/absorption_spec.rb +0 -95
  127. data/spec/cql/comparison_spec.rb +0 -89
  128. data/spec/cql/context_spec.rb +0 -94
  129. data/spec/cql/contractions_spec.rb +0 -224
  130. data/spec/cql/deontic_spec.rb +0 -88
  131. data/spec/cql/entity_type_spec.rb +0 -320
  132. data/spec/cql/expressions_spec.rb +0 -66
  133. data/spec/cql/fact_type_matching_spec.rb +0 -338
  134. data/spec/cql/french_spec.rb +0 -21
  135. data/spec/cql/parser/bad_literals_spec.rb +0 -86
  136. data/spec/cql/parser/constraints_spec.rb +0 -19
  137. data/spec/cql/parser/entity_types_spec.rb +0 -106
  138. data/spec/cql/parser/expressions_spec.rb +0 -199
  139. data/spec/cql/parser/fact_types_spec.rb +0 -44
  140. data/spec/cql/parser/literals_spec.rb +0 -312
  141. data/spec/cql/parser/pragmas_spec.rb +0 -89
  142. data/spec/cql/parser/value_types_spec.rb +0 -42
  143. data/spec/cql/role_matching_spec.rb +0 -148
  144. data/spec/cql/samples_spec.rb +0 -244
  145. data/spec/cql_cql_spec.rb +0 -73
  146. data/spec/cql_dm_spec.rb +0 -136
  147. data/spec/cql_mysql_spec.rb +0 -69
  148. data/spec/cql_parse_spec.rb +0 -34
  149. data/spec/cql_ruby_spec.rb +0 -73
  150. data/spec/cql_sql_spec.rb +0 -72
  151. data/spec/cql_symbol_tables_spec.rb +0 -261
  152. data/spec/cqldump_spec.rb +0 -170
  153. data/spec/helpers/array_matcher.rb +0 -23
  154. data/spec/helpers/ctrl_c_support.rb +0 -52
  155. data/spec/helpers/diff_matcher.rb +0 -39
  156. data/spec/helpers/file_matcher.rb +0 -34
  157. data/spec/helpers/parse_to_ast_matcher.rb +0 -80
  158. data/spec/helpers/string_matcher.rb +0 -30
  159. data/spec/helpers/test_parser.rb +0 -15
  160. data/spec/norma_cql_spec.rb +0 -66
  161. data/spec/norma_ruby_spec.rb +0 -62
  162. data/spec/norma_ruby_sql_spec.rb +0 -107
  163. data/spec/norma_sql_spec.rb +0 -57
  164. data/spec/norma_tables_spec.rb +0 -95
  165. data/spec/ruby_api_spec.rb +0 -23
  166. data/spec/spec_helper.rb +0 -35
  167. data/spec/transform_surrogate_spec.rb +0 -59
  168. data/status.html +0 -138
  169. data/why.html +0 -60
@@ -1,14 +0,0 @@
1
- #
2
- # ActiveFacts CQL Parser.
3
- # A Rakefile to run Treetop when the ActiveFacts gem is installed.
4
- # This isn't mandatory but makes it much faster to start the parser.
5
- # Delete the generated files during parser development.
6
- #
7
- # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
8
- #
9
- task :default do
10
- pattern = File.dirname(__FILE__) + '**/*.treetop'
11
- files = Dir[pattern]
12
- # Hopefully this quoting will work where there are spaces in filenames, and even maybe on Windows?
13
- exec "tt '#{files*"' '"}'"
14
- end
@@ -1,183 +0,0 @@
1
- #
2
- # ActiveFacts CQL Parser.
3
- # Parse rules relating to Term names
4
- #
5
- # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
- #
7
- module ActiveFacts
8
- module CQL
9
- grammar Terms
10
- rule term_definition_name
11
- id s t:(!non_term_def id s)*
12
- <Parser::TermDefinitionNameNode>
13
- end
14
-
15
- rule non_term_def
16
- mapping_pragmas entity_prefix
17
- / mapping_pragmas written_as # Value type
18
- / mapping_pragmas is_where # Objectified type
19
- / non_phrase
20
- / identified_by # as in: "a kind of X identified by..."
21
- / in_units
22
- / auto_assignment
23
- / value_constraint
24
- end
25
-
26
- rule entity_prefix
27
- is s (independent s )? identified_by
28
- /
29
- subtype_prefix (independent s )? term_definition_name
30
- &{|e| input.context.object_type(e[2].value, "subtype") }
31
- end
32
-
33
- rule prescan
34
- s each?
35
- s (
36
- term_definition_name mapping_pragmas entity_prefix
37
- &{|e| input.context.object_type(e[0].value, "entity type") }
38
- /
39
- t1:term_definition_name mapping_pragmas written_as any? s t2:term_definition_name
40
- &{|e|
41
- new_term = e[0].value
42
- input.context.object_type(new_term, "value type")
43
- base_term = e[5].value
44
- input.context.object_type(base_term, "value type")
45
- }
46
- /
47
- term_definition_name s mapping_pragmas is_where
48
- &{|e| input.context.object_type(e[0].value, "objectified_fact_type") }
49
- )?
50
- prescan_rest
51
- &{|s|
52
- # Wipe any terminal failures that were added:
53
- @terminal_failures = []
54
- @max_terminal_failure_index = start_index
55
-
56
- # puts "========== prescan is complete on #{(s.map{|e|e.text_value}*" ").inspect} =========="
57
- false
58
- }
59
- end
60
-
61
- # Do a first-pass mainly lexical analysis, looking for role name definitions and adjectives,
62
- # for use in detecting terms later.
63
- rule prescan_rest
64
- &{|s| input.context.reset_role_names }
65
- (
66
- context_note # Context notes have different lexical conventions
67
- / '(' as S term_definition_name s ')' s # Prepare for a Role Name
68
- &{|s| input.context.role_name(s[3].value) }
69
- / new_derived_value # Prepare for a derived term
70
- / new_adjective_term # Prepare for an existing term with new Adjectives
71
- # The remaining rules exist to correctly eat up anything that doesn't match the above:
72
- / global_term # If we see A B - C D, don't recognise B as a new adjective for C D.
73
- / prescan_aggregate
74
- / id
75
- # / literal # REVISIT: Literals might contain "(as Foo)" and mess things up
76
- / range # Covers all numbers and strings
77
- / comparator # handle two-character operators
78
- / S # White space and comments, must precede / and *
79
- / [-+{}\[\].,:^/%*()] # All other punctuation and operators
80
- )* [?;] s
81
- end
82
-
83
- # Not sure this is even needed, but it doesn't seem to hurt:
84
- rule prescan_aggregate
85
- aggregate_type:id s agg_of s global_term agg_in s &'('
86
- end
87
-
88
- rule new_derived_value
89
- !global_term id derived_value_continuation? s '='
90
- &{|s|
91
- name = [s[1].text_value] + (s[2].empty? ? [] : s[2].value)
92
- input.context.object_type(name*' ', "derived value type")
93
- }
94
- /
95
- '=' s !global_term id derived_value_continuation? s (that/who)
96
- &{|s|
97
- name = [s[3].text_value] + (s[4].empty? ? [] : s[4].value)
98
- input.context.object_type(name*' ', "derived value type")
99
- }
100
- end
101
-
102
- # Derived values are new terms introduced by an = sign before an expression
103
- # This rule handles trailing words of a multi-word derived value
104
- rule derived_value_continuation
105
- s '-' tail:(s !global_term !(that/who) id)*
106
- {
107
- def value
108
- tail.elements.map{|e| e.id.text_value}
109
- end
110
- }
111
- end
112
-
113
- # Used during the pre-scan, match a term with new adjective(s)
114
- rule new_adjective_term
115
- !global_term adj:id '-' '-'? lead_intervening s global_term # Definitely a new leading adjective for this term
116
- &{|s| adj = [s[1].text_value, s[4].value].compact*" "; input.context.new_leading_adjective_term(adj, s[6].text_value) }
117
- /
118
- global_term s trail_intervening '-' '-'? !global_term adj:id # Definitely a new trailing adjective for this term
119
- &{|s| adj = [s[2].value, s[6].text_value].compact*" "; input.context.new_trailing_adjective_term(adj, s[0].text_value) }
120
- end
121
-
122
- rule lead_intervening # Words intervening between a new adjective and the term
123
- (S !global_term id)*
124
- {
125
- def value
126
- elements.size == 0 ? nil : elements.map{|e| e.id.text_value}*" "
127
- end
128
- }
129
- end
130
-
131
- rule trail_intervening # Words intervening between a new adjective and the term
132
- (!global_term id S)*
133
- {
134
- def value
135
- elements.size == 0 ? nil : elements.map{|e| e.id.text_value}*" "
136
- end
137
- }
138
- end
139
-
140
- # This is the rule to use after the prescan; it only succeeds on a complete term or role reference
141
- rule term
142
- s head:id x &{|s| w = s[1].text_value; input.context.term_starts?(w, s[2]) }
143
- tail:(
144
- s '-'? dbl:'-'? s w:id &{|s| w = s[4].text_value; input.context.term_continues?(w) }
145
- )* &{|s| input.context.term_complete? }
146
- <Parser::TermNode>
147
- /
148
- s head:id '-' '-'? s term &{|s| s[5].ast.leading_adjective == nil }
149
- <Parser::TermLANode>
150
- end
151
-
152
- rule x
153
- '' <SavedContext>
154
- end
155
-
156
- rule global_term
157
- # This rule shouldn't be used outside the prescan, it will memoize the wrong things.
158
- head:id x &{|s| input.context.term_starts?(s[0].text_value, s[1]) }
159
- tail:(s w:id &{|s| input.context.term_continues?(s[1].text_value) } )*
160
- { def value
161
- tail.elements.inject(head.value) { |t, e| "#{t} #{e.w.value}" }
162
- end
163
- }
164
- end
165
-
166
- rule non_phrase
167
- # These words are illegal in (but maybe ok following) a clause where a phrase is expected:
168
- and
169
- / but
170
- / if
171
- / role_list_constraint_followers
172
- / only_if
173
- / or
174
- / quantifier
175
- / returning
176
- / then
177
- / value_constraint
178
- / where
179
- end
180
-
181
- end
182
- end
183
- end
@@ -1,202 +0,0 @@
1
- #
2
- # ActiveFacts CQL Parser.
3
- # Parse rules relating to ValueType definitions.
4
- #
5
- # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
6
- #
7
- module ActiveFacts
8
- module CQL
9
- grammar ValueTypes
10
- rule value_type
11
- s each?
12
- s term_definition_name
13
- m1:mapping_pragmas
14
- # REVISIT: ORM2 would allow (subtype_prefix term)?
15
- written_as
16
- any? s
17
- base:(term/implicit_value_type_name) s
18
- value_type_parameters
19
- u:in_units?
20
- a:auto_assignment?
21
- c:context_note?
22
- r:(value_constraint enforcement)?
23
- m2:mapping_pragmas
24
- c2:context_note?
25
- s ';' s
26
- {
27
- def ast
28
- name = term_definition_name.value
29
- params = value_type_parameters.values
30
- value_constraint = nil
31
- unless r.empty?
32
- value_constraint = Compiler::ValueConstraint.new(r.value_constraint.ast, r.enforcement.ast)
33
- end
34
- units = u.empty? ? [] : u.units.value
35
- auto_assigned_at = a.empty? ? nil : a.auto_assigned_at
36
- pragmas = m1.value+m2.value
37
- context_note = !c.empty? ? c.ast : (!c2.empty? ? c2.ast : nil)
38
- Compiler::ValueType.new name, base.value, params, units, value_constraint, pragmas, context_note, auto_assigned_at
39
- end
40
- }
41
- end
42
-
43
- rule in_units
44
- in S units
45
- end
46
-
47
- rule implicit_value_type_name
48
- id
49
- {
50
- def node_type; :term; end
51
- }
52
- end
53
-
54
- rule value_type_parameters
55
- '(' s tpl:type_parameter_list? ')' s
56
- { def values; tpl.empty? ? [] : tpl.values; end }
57
- / s
58
- { def values; []; end }
59
- end
60
-
61
- rule type_parameter_list
62
- head:number s tail:( ',' s number s )*
63
- {
64
- def values
65
- [head.value, *tail.elements.map{|i| i.number.value}]
66
- end
67
- }
68
- end
69
-
70
- rule unit_definition
71
- u:(
72
- s coeff:unit_coefficient? base:units? s o:unit_offset?
73
- conversion
74
- singular:unit_name s plural:('/' s p:unit_name s)?
75
- /
76
- s singular:unit_name s plural:('/' s p:unit_name s)?
77
- conversion
78
- coeff:unit_coefficient? base:units? s o:unit_offset?
79
- )
80
- q:(approximately '' / ephemera s url )? s
81
- ';'
82
- {
83
- def ast
84
- singular = u.singular.text_value
85
- plural = u.plural.text_value.empty? ? nil : u.plural.p.text_value
86
- if u.coeff.empty?
87
- raise "Unit definition requires either a coefficient or an ephemera URL" unless q.respond_to?(:ephemera)
88
- numerator,denominator = 1, 1
89
- else
90
- numerator, denominator = *u.coeff.ast
91
- end
92
- offset = u.o.text_value.empty? ? 0 : u.o.value
93
- bases = u.base.empty? ? [] : u.base.value
94
- approximately = q.respond_to?(:approximately) || u.conversion.approximate?
95
- ephemera = q.respond_to?(:ephemera) ? q.url.text_value : nil
96
- Compiler::Unit.new singular, plural, numerator, denominator, offset, bases, approximately, ephemera
97
- end
98
- }
99
- end
100
-
101
- rule unit_name
102
- id
103
- {
104
- def node_type; :unit; end
105
- }
106
- end
107
-
108
-
109
- rule unit_coefficient
110
- numerator:number denominator:(s '/' s number)? s
111
- {
112
- def ast
113
- [ numerator.text_value,
114
- (denominator.text_value.empty? ? "1" : denominator.number.text_value)
115
- ]
116
- end
117
- }
118
- end
119
-
120
- rule unit_offset
121
- sign:[-+] s number s
122
- { def value
123
- sign.text_value == '-' ? "-"+number.text_value : number.text_value
124
- end
125
- }
126
- end
127
-
128
- # In a unit definition, we may use undefined base units; this is the only way to get fundamental units
129
- rule units
130
- !non_unit maybe_unit s tail:(!non_unit maybe_unit s)* div:('/' s maybe_unit s tail:(!non_unit maybe_unit s)*)?
131
- { def value
132
- tail.elements.inject([maybe_unit.value]) { |a, e| a << e.maybe_unit.value } +
133
- (div.text_value.empty? ? [] : div.tail.elements.inject([div.maybe_unit.inverse]) { |a, e| a << e.maybe_unit.inverse })
134
- end
135
- }
136
- end
137
-
138
- rule non_unit
139
- restricted_to / conversion / approximately / ephemera / auto_assignment
140
- end
141
-
142
- rule unit
143
- maybe_unit &{|s| input.context.unit?(s[0].unit_name.text_value) }
144
- end
145
-
146
- rule maybe_unit
147
- unit_name pow:('^' '-'? [0-9])?
148
- { def value
149
- [unit_name.text_value, pow.text_value.empty? ? 1 : Integer(pow.text_value[1..-1])]
150
- end
151
- def inverse
152
- a = value
153
- a[1] = -a[1]
154
- a
155
- end
156
- }
157
- end
158
-
159
- rule value_constraint
160
- restricted_to restricted_values c:context_note?
161
- {
162
- def ast
163
- v = restricted_values.values
164
- c[:context_note] = c.ast unless c.empty?
165
- v
166
- end
167
- }
168
- # REVISIT: "where the possible value/s of that <Term> is/are value (, ...)"
169
- end
170
-
171
- rule restricted_values
172
- range_list s u:units?
173
- {
174
- def values
175
- { :ranges => range_list.ranges,
176
- :units => u.empty? ? nil : u.value
177
- }
178
- end
179
- }
180
- /
181
- regular_expression
182
- {
183
- def values
184
- { :regular_expression => contents }
185
- end
186
- }
187
- end
188
-
189
- rule range_list
190
- '{' s
191
- head:range s tail:( ',' s range )*
192
- '}' s
193
- {
194
- def ranges
195
- [head.value, *tail.elements.map{|e| e.range.value }]
196
- end
197
- }
198
- end
199
-
200
- end
201
- end
202
- end
@@ -1,156 +0,0 @@
1
- # Compile a CQL string into an ActiveFacts vocabulary.
2
- #
3
- # Copyright (c) 2009 Clifford Heath. Read the LICENSE file.
4
- #
5
- require 'activefacts/vocabulary'
6
- require 'activefacts/cql/parser'
7
- require 'activefacts/cql/compiler/shared'
8
- require 'activefacts/cql/compiler/value_type'
9
- require 'activefacts/cql/compiler/entity_type'
10
- require 'activefacts/cql/compiler/clause'
11
- require 'activefacts/cql/compiler/fact_type'
12
- require 'activefacts/cql/compiler/expression'
13
- require 'activefacts/cql/compiler/fact'
14
- require 'activefacts/cql/compiler/constraint'
15
- require 'activefacts/cql/compiler/query'
16
-
17
- module ActiveFacts
18
- module CQL
19
- class Compiler < ActiveFacts::CQL::Parser
20
- LANGUAGES = {
21
- 'en' => 'English',
22
- 'fr' => 'French',
23
- 'cn' => 'Mandarin'
24
- }
25
- attr_reader :vocabulary
26
-
27
- def initialize *a
28
- @filename = a.shift || "stdio"
29
- super *a
30
- @constellation = ActiveFacts::API::Constellation.new(ActiveFacts::Metamodel)
31
- @language = nil
32
- trace :file, "Parsing '#{@filename}'"
33
- end
34
-
35
- def compile_file filename
36
- old_filename = @filename
37
- @filename = filename
38
- File.open(filename) do |f|
39
- compile(f.read)
40
- end
41
- @filename = old_filename
42
- @vocabulary
43
- end
44
-
45
- # Load the appropriate natural language module
46
- def detect_language
47
- @filename =~ /.*\.(..)\.cql$/i
48
- language_code = $1
49
- @language = LANGUAGES[language_code] || 'English'
50
- end
51
-
52
- def include_language
53
- detect_language unless @langage
54
- require 'activefacts/cql/Language/'+@language
55
- language_module = ActiveFacts::CQL.const_get(@language)
56
- extend language_module
57
- end
58
-
59
- # Mark any new Concepts as belonging to this topic
60
- def topic_flood
61
- @constellation.Concept.each do |key, concept|
62
- next if concept.topic
63
- trace :topic, "Colouring #{concept.describe} with #{@topic.topic_name}"
64
- concept.topic = @topic
65
- end
66
- end
67
-
68
- def compile input
69
- include_language
70
-
71
- @string = input
72
-
73
- # The syntax tree created from each parsed CQL statement gets passed to the block.
74
- # parse_all returns an array of the block's non-nil return values.
75
- ok = parse_all(@string, :definition) do |node|
76
- trace :parse, "Parsed '#{node.text_value.gsub(/\s+/,' ').strip}'" do
77
- trace :lex, (proc { node.inspect })
78
- begin
79
- ast = node.ast
80
- next unless ast
81
- trace :ast, ast.inspect
82
- ast.tree = node
83
- ast.constellation = @constellation
84
- ast.vocabulary = @vocabulary
85
- value = compile_definition ast
86
- trace :definition, "Compiled to #{value.is_a?(Array) ? value.map{|v| v.verbalise}*', ' : value.verbalise}" if value
87
- if value.is_a?(ActiveFacts::Metamodel::Topic)
88
- topic_flood if @topic
89
- @topic = value
90
- elsif ast.is_a?(Compiler::Vocabulary)
91
- topic_flood if @topic
92
- @vocabulary = value
93
- @topic = @constellation.Topic(@vocabulary.name)
94
- end
95
- rescue => e
96
- # Augment the exception message, but preserve the backtrace
97
- start_line = @string.line_of(node.interval.first)
98
- end_line = @string.line_of(node.interval.last-1)
99
- lines = start_line != end_line ? "s #{start_line}-#{end_line}" : " #{start_line.to_s}"
100
- ne = StandardError.new("at line#{lines} #{e.message.strip}")
101
- ne.set_backtrace(e.backtrace)
102
- raise ne
103
- end
104
- end
105
- topic_flood if @topic
106
- end
107
- raise failure_reason unless ok
108
- vocabulary
109
- end
110
-
111
- def compile_import file, aliases
112
- saved_index = @index
113
- saved_block = @block
114
- saved_string = @string
115
- saved_input_length = @input_length
116
- saved_topic = @topic
117
- old_filename = @filename
118
- @filename = File.dirname(old_filename)+'/'+file+'.cql'
119
-
120
- # REVISIT: Save and use another @vocabulary for this file?
121
- File.open(@filename) do |f|
122
- topic_flood if @topic
123
- @topic = @constellation.Topic(File.basename(@filename, '.cql'))
124
- trace :import, "Importing #{@filename} as #{@topic.topic_name}" do
125
- ok = parse_all(f.read, nil, &@block)
126
- end
127
- @topic = saved_topic
128
- end
129
-
130
- rescue => e
131
- ne = StandardError.new("In #{@filename} #{e.message.strip}")
132
- ne.set_backtrace(e.backtrace)
133
- raise ne
134
- ensure
135
- @block = saved_block
136
- @index = saved_index
137
- @input_length = saved_input_length
138
- @string = saved_string
139
- @filename = old_filename
140
- nil
141
- end
142
-
143
- def compile_definition ast
144
- ast.compile
145
- end
146
-
147
- def unit? s
148
- name = @constellation.Name[s]
149
- units = (!name ? [] : Array(name.unit) + Array(name.plural_named_unit)).uniq
150
- trace :units, "Looking for unit #{s}, got #{units.map{|u|u.name}.inspect}"
151
- units.size > 0
152
- end
153
-
154
- end
155
- end
156
- end