lib/activefacts/cql/compiler/expression.rb in activefacts-0.8.16 vs lib/activefacts/cql/compiler/expression.rb in activefacts-0.8.18
- old
+ new
@@ -2,23 +2,23 @@
module CQL
class Compiler
# An Operation is a binary or ternary fact type involving an operator,
# a result, and one or two operands.
- # Viewed as a result, it behaves like a VarRef with a nested Clause.
+ # Viewed as a result, it behaves like a Reference with a nested Clause.
# Viewed as a fact type, it behaves like a Clause.
#
# The only exception here is an equality comparison, where it may
# turn out that the equality is merely a projection. In this case
# the Operation is dropped from the clauses and is replaced by the
# projected operand.
#
- # Each operand may be a Literal, a VarRef, or another Operation,
- # so we need to recurse down the tree to build the join.
+ # Each operand may be a Literal, a Reference, or another Operation,
+ # so we need to recurse down the tree to build the query.
#
class Operation
- # VarRef (in)compatibility:
+ # Reference (in)compatibility:
[ :term, :leading_adjective, :trailing_adjective, :role_name, :quantifier,
:value_constraint, :embedded_presence_constraint, :literal
].each do |s|
define_method(s) { raise "Unexpected call to Operation\##{s}" }
define_method(:"#{s}=") { raise "Unexpected call to Operation\##{s}=" }
@@ -26,103 +26,115 @@
def role_name; nil; end
def leading_adjective; nil; end
def trailing_adjective; nil; end
def value_constraint; nil; end
def literal; nil; end
- attr_accessor :player # What ObjectType does the Variable denote
- attr_accessor :variable # What Variable for that ObjectType
+ def side_effects; nil; end
+ attr_accessor :player # What ObjectType does the Binding denote
+ attr_accessor :binding # What Binding for that ObjectType
attr_accessor :clause # What clause does the result participate in?
attr_accessor :role # Which Role of this ObjectType
attr_accessor :role_ref # Which RoleRef to that Role
+ attr_accessor :certainty # nil, true, false -> maybe, definitely, not
def nested_clauses; @nested_clauses ||= [self]; end
def clause; self; end
def objectification_of; @fact_type; end
-
# Clause (in)compatibility:
[ :phrases, :qualifiers, :context_note, :reading, :role_sequence, :fact
].each do |s|
define_method(s) { raise "Unexpected call to Operation\##{s}" }
define_method(:"#{s}=") { raise "Unexpected call to Operation\##{s}=" }
end
def conjunction; nil; end
attr_reader :fact_type
- def objectified_as; self; end # The VarRef which objectified this fact type
+ def objectified_as; self; end # The Reference which objectified this fact type
+ def initialize
+ @certainty = true # Assume it's definite
+ end
+
def operands context = nil
raise "REVISIT: Implement operand enumeration in the operator subclass #{self.class.name}"
end
def identify_players_with_role_name context
# Just recurse, there's no way (yet: REVISIT?) to add a role name to the result of an expression
- var_refs.each { |o|
+ refs.each { |o|
o.identify_players_with_role_name(context)
}
# As yet, an operation cannot have a role name:
# identify_player context if role_name
end
def identify_other_players context
# Just recurse, there's no way (yet: REVISIT?) to add a role name to the result of an expression
- var_refs.each { |o|
+ refs.each { |o|
o.identify_other_players(context)
}
identify_player context
end
def bind context
- var_refs.each do |o|
+ refs.each do |o|
o.bind context
end
name = result_type_name(context)
@player = result_value_type(context, name)
- key = "#{name} #{object_id}" # Every Operation result is a unique Variable
- @variable = (context.variables[key] ||= Variable.new(@player))
- @variable.refs << self
- @variable
+ key = "#{name} #{object_id}" # Every Operation result is a unique Binding
+ @binding = (context.bindings[key] ||= Binding.new(@player))
+ @binding.refs << self
+ @binding
end
def result_type_name(context)
raise "REVISIT: Implement result_type_name in the #{self.class.name} subclass"
end
def result_value_type(context, name)
vocabulary = context.vocabulary
constellation = vocabulary.constellation
- constellation.ValueType(vocabulary, name, :guid => :new)
+ vocabulary.valid_value_type_name(name) ||
+ constellation.ValueType(vocabulary, name, :guid => :new)
end
def is_naked_object_type
false # All Operations are non-naked
end
def match_existing_fact_type context
- opnds = var_refs
- result_var_ref = VarRef.new(@variable.player.name)
- result_var_ref.player = @variable.player
- result_var_ref.variable = @variable
- @variable.refs << result_var_ref
+ opnds = refs
+ result_ref = Reference.new(@binding.player.name)
+ result_ref.player = @binding.player
+ result_ref.binding = @binding
+ @binding.refs << result_ref
clause_ast = Clause.new(
- [result_var_ref, '='] +
+ [result_ref, '='] +
(opnds.size > 1 ? [opnds[0]] : []) +
[operator, opnds[-1]]
)
# REVISIT: All operands must be value-types or simply-identified Entity Types.
- # REVISIT: We should auto-create joins from Entity Types to an identifying ValueType
+ # REVISIT: We should auto-create steps from Entity Types to an identifying ValueType
# REVISIT: We should traverse up the supertype of ValueTypes to find a DataType
@fact_type = clause_ast.match_existing_fact_type(context, :exact_type => true)
+ if clause.certainty == false
+ raise "Negated fact types in expressions are not yet supported: #{clause.inspect}"
+ end
return @fact_type if @fact_type
@fact_type = clause_ast.make_fact_type context.vocabulary
reading = clause_ast.make_reading context.vocabulary, @fact_type
rrs = reading.role_sequence.all_role_ref_in_order
opnds[0].role_ref = rrs[0]
opnds[-1].role_ref = rrs[-1]
opnds.each do |opnd|
next unless opnd.is_a?(Operation)
opnd.match_existing_fact_type context
+ if opnd.certainty == false
+ raise "Negated fact types in expressions are not yet supported: #{opnd.inspect}"
+ end
end
@fact_type
end
def is_existential_type
@@ -140,32 +152,32 @@
end
class Comparison < Operation
attr_accessor :operator, :e1, :e2, :qualifiers, :conjunction
- def initialize operator, e1, e2, qualifiers = []
- @operator, @e1, @e2, @qualifiers = operator, e1, e2, qualifiers
+ def initialize operator, e1, e2, certainty = true
+ @operator, @e1, @e2, @certainty, @qualifiers = operator, e1, e2, certainty, []
end
- def var_refs
+ def refs
[@e1, @e2]
end
def bind context
- var_refs.each do |o|
+ refs.each do |o|
o.bind context
end
# REVISIT: Return the projected binding instead:
return @result = nil if @projection
name = 'Boolean'
@player = result_value_type(context, name)
- key = "#{name} #{object_id}" # Every Comparison result is a unique Variable
- @variable = (context.variables[key] ||= Variable.new(@player))
- @variable.refs << self
- @variable
+ key = "#{name} #{object_id}" # Every Comparison result is a unique Binding
+ @binding = (context.bindings[key] ||= Binding.new(@player))
+ @binding.refs << self
+ @binding
end
def result_type_name(context)
"compare#{operator}(#{[@e1,@e2].map{|e| e.player.name}*', '}))"
end
@@ -178,11 +190,14 @@
@player || begin
if @projection
raise "REVISIT: The player is the projected expression"
end
v = context.vocabulary
- @player = v.constellation.ValueType(v, 'Boolean', :guid => :new)
+ @boolean ||=
+ v.constellation.ValueType[[[v.name], 'Boolean']] ||
+ v.constellation.ValueType(v, 'Boolean', :guid => :new)
+ @player = @boolean
end
end
=begin
def project lr
@@ -193,21 +208,35 @@
=end
def inspect; to_s; end
def to_s
- "compare#{operator}(#{e1.to_s} #{e2.to_s}#{@qualifiers.empty? ? '' : ', ['+@qualifiers*', '+']'})"
+ "compare#{
+ operator
+ }(#{
+ case @certainty
+ when nil; 'maybe '
+ when false; 'negated '
+ # else 'definitely '
+ end
+ }#{
+ e1.to_s
+ } #{
+ e2.to_s
+ }#{
+ @qualifiers.empty? ? '' : ', ['+@qualifiers*', '+']'
+ })"
end
end
class Sum < Operation
attr_accessor :terms
def initialize *terms
@terms = terms
end
- def var_refs
+ def refs
@terms
end
def operator
'+'
@@ -248,11 +277,11 @@
attr_accessor :factors
def initialize *factors
@factors = factors
end
- def var_refs
+ def refs
@factors
end
def operator
'*'
@@ -296,11 +325,11 @@
def operator
'1/'
end
- def var_refs
+ def refs
[@divisor]
end
def identify_player context
@player || begin
@@ -394,23 +423,23 @@
when Float; 'Real'
when Numeric; 'Integer'
when TrueClass, FalseClass; 'Boolean'
end
v = context.vocabulary
- @player = v.constellation.ValueType(v, player_name, :guid => :new)
+ @player = v.constellation.ValueType(v, player_name)
end
end
def bind context
- @variable || begin
+ @binding || begin
key = "#{@player.name} #{@literal}"
- @variable = (context.variables[key] ||= Variable.new(@player))
- @variable.refs << self
+ @binding = (context.bindings[key] ||= Binding.new(@player))
+ @binding.refs << self
end
end
- def variable
- @variable
+ def binding
+ @binding
end
end
end
end