activefacts 1.6.0 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
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