lib/fhir_models/fluentpath/evaluate.rb in fhir_models-1.6.8 vs lib/fhir_models/fluentpath/evaluate.rb in fhir_models-1.8.0
- old
+ new
@@ -1,12 +1,11 @@
module FluentPath
-
- @@context = Hash.new
+ @@context = {}
@@parent = nil
# This is the entry point to using the FluentPath class
- def self.evaluate(expression, hash, parent=nil)
+ def self.evaluate(expression, hash, parent = nil)
@@context = hash
@@parent = parent
tree = FluentPath.parse(expression)
FHIR.logger.debug "TREE: #{tree}"
compute(tree, hash)
@@ -14,40 +13,39 @@
# Get a value from a hash, with some special handling of
# self references
def self.get(key, hash)
return @@context if ['$context', '$resource'].include?(key)
- return @@parent if key=='$parent'
- return 'http://unitsofmeasure.org' if key=='%ucum'
- return 'http://snomed.info/sct' if key=='%sct'
- return 'http://loinc.org' if key=='%loinc'
+ return @@parent if key == '$parent'
+ return 'http://unitsofmeasure.org' if key == '%ucum'
+ return 'http://snomed.info/sct' if key == '%sct'
+ return 'http://loinc.org' if key == '%loinc'
return key.gsub!(/\A\'|\'\Z/, '') if key.start_with?("'") && key.end_with?("'")
key.gsub!(/\A"|"\Z/, '') # remove quotes around path if they exist
if hash.is_a?(Array)
response = []
hash.each do |e|
- if e.is_a?(Hash)
- item = e[key]
- if item.is_a?(Array)
- item.each{|i| response << i }
- else
- response << item
- end
+ next unless e.is_a?(Hash)
+ item = e[key]
+ if item.is_a?(Array)
+ item.each { |i| response << i }
+ else
+ response << item
end
end
return response
end
- return :null if !hash.is_a?(Hash)
- return hash if hash['resourceType']==key
+ return :null unless hash.is_a?(Hash)
+ return hash if hash['resourceType'] == key
val = hash[key]
if val.nil?
# this block is a dangerous hack to get fields of multiple data types
# e.g. 'value' instead of 'valueQuantity', or 'onset' instead of 'onsetDateTime' or 'onsetPeriod'
- nkey = hash.keys.select{|x|x.start_with?(key)}.first
- if !nkey.nil?
+ nkey = hash.keys.select { |x| x.start_with?(key) }.first
+ unless nkey.nil?
tail = nkey.gsub(key, '')
- val = hash[nkey] if (tail[0]==tail[0].capitalize)
+ val = hash[nkey] if tail[0] == tail[0].capitalize
end
end
val = :null if val.nil?
val = "'#{val}'" if val.is_a?(String) && !(val.start_with?("'") && val.end_with?("'"))
val
@@ -57,42 +55,42 @@
# Everything else is true.
def self.convert_to_boolean(value)
return false if value.nil?
return false if value.is_a?(Array) && value.empty?
return false if value.is_a?(Hash) && value.empty?
- return false if value==:null
- return false if value==false
- return true
+ return false if value == :null
+ return false if value == false
+ true
end
def self.clean_index(tree, index)
- tree[index] = nil if !index.nil?
+ tree[index] = nil unless index.nil?
end
# evaluate a parsed expression given some context data
def self.compute(tree, data)
tree = tree.tree if tree.is_a?(FluentPath::Expression)
# --------------- OPERATOR PRECEDENCE ------------------
- #01 . (path/function invocation)
- #02 [] (indexer)
- #03 unary + and -
- #04: *, /, div, mod
- #05: +, -,
- #06: |
- #07: >, <, >=, <=
- #08: is, as
- #09: =, ~, !=, !~
- #10: in, contains
- #11: and
- #12: xor, or
- #13: implies
+ # 01 . (path/function invocation)
+ # 02 [] (indexer)
+ # 03 unary + and -
+ # 04: *, /, div, mod
+ # 05: +, -,
+ # 06: |
+ # 07: >, <, >=, <=
+ # 08: is, as
+ # 09: =, ~, !=, !~
+ # 10: in, contains
+ # 11: and
+ # 12: xor, or
+ # 13: implies
# evaluate all the data at this level
functions = [:where, :select, :extension, :children, :first, :last, :tail]
size = -1
substitutions = 1
- while(tree.length!=size || substitutions > 0)
+ while tree.length != size || substitutions > 0
substitutions = 0
FHIR.logger.debug "DATA: #{tree}"
previous_node = nil
previous_index = nil
size = tree.length
@@ -100,13 +98,13 @@
if node.is_a?(String) && !(node.start_with?("'") && node.end_with?("'"))
array_index = nil
if node.include?('[') && node.end_with?(']')
array_index = node[node.index('[')..-1].gsub(/\[|\]/, '')
t = get(array_index, data)
- t = array_index.to_i if(t.nil? || t==:null)
+ t = array_index.to_i if t.nil? || t == :null
array_index = t
- node = node[0..node.index('[')-1]
+ node = node[0..node.index('[') - 1]
end
if previous_node.is_a?(Hash) || previous_node.is_a?(Array)
tree[index] = get(node, previous_node)
clean_index(tree, previous_index)
elsif !previous_node.is_a?(FluentPath::Expression)
@@ -120,48 +118,43 @@
previous_node = compute(previous_node, data) if previous_node.is_a?(FluentPath::Expression)
case node
when :where
# the previous node should be data (as Array or Hash)
# the next node should be a block or subexpression (as FluentPath::Expression)
- block = tree[index+1]
+ block = tree[index + 1]
if block.is_a?(FluentPath::Expression)
- tree[index+1] = nil
+ tree[index + 1] = nil
else
raise 'Where function requires a block.'
end
- previous_node = [] if previous_node==:null
+ previous_node = [] if previous_node == :null
if previous_node.is_a?(Array)
previous_node.keep_if do |item|
sub = compute(block.clone, item)
convert_to_boolean(sub)
end
tree[index] = previous_node
clean_index(tree, previous_index)
elsif previous_node.is_a?(Hash)
sub = compute(block, previous_node)
- if convert_to_boolean(sub)
- tree[index] = previous_node
- clean_index(tree, previous_index)
- else
- tree[index] = {}
- clean_index(tree, previous_index)
- end
+ tree[index] = convert_to_boolean(sub) ? previous_node : {}
+ clean_index(tree, previous_index)
else
raise "Where function not applicable to #{previous_node.class}: #{previous_node}"
end
break
when :select
# select is equivalent to ruby Array.map!
# the previous node should be data (as Array or Hash)
# the next node should be a block or subexpression (as FluentPath::Expression)
- block = tree[index+1]
+ block = tree[index + 1]
if block.is_a?(FluentPath::Expression)
- tree[index+1] = nil
+ tree[index + 1] = nil
else
raise 'Select function requires a block.'
end
- previous_node = [] if previous_node==:null
+ previous_node = [] if previous_node == :null
if previous_node.is_a?(Array)
previous_node.map! do |item|
compute(block.clone, item)
end
tree[index] = previous_node
@@ -174,13 +167,13 @@
end
break
when :extension
# the previous node should be a data (as Hash)
# the next node optionally is a block or subexpression (as FluentPath::Expression)
- block = tree[index+1]
+ block = tree[index + 1]
if block.is_a?(FluentPath::Expression)
- tree[index+1] = nil
+ tree[index + 1] = nil
else
raise 'Extension function requires a block.'
end
if previous_node.is_a?(Hash)
FHIR.logger.debug 'Evaling Extension Block....'
@@ -190,11 +183,11 @@
begin
url = block.tree.first.gsub(/\'|\"/, '')
rescue
raise 'Extension function requires a single URL as String.'
end
- ext = exts.select{|x|x['url']==url}.first
+ ext = exts.select { |x| x['url'] == url }.first
tree[index] = ext
clean_index(tree, previous_index)
else
raise "Extension function not applicable to #{exts.class}: #{exts}"
end
@@ -206,14 +199,14 @@
# if there is a previous node, it should be data (as Hash)
# otherwise, use the context as data
if previous_node.is_a?(Hash)
tree[index] = previous_node.values
clean_index(tree, previous_index)
- substitutions+=1
+ substitutions += 1
elsif data.is_a?(Hash)
tree[index] = data.values
- substitutions+=1
+ substitutions += 1
else
raise "Children not applicable to #{previous_node.class}: #{previous_node}"
end
break
when :first
@@ -233,11 +226,11 @@
raise "Last function is not applicable to #{previous_node.class}: #{previous_node}"
end
when :tail
# the previous node should be an Array of length > 1
if previous_node.is_a?(Array)
- tree[index] = previous_node.last(previous_node.length-1)
+ tree[index] = previous_node.last(previous_node.length - 1)
clean_index(tree, previous_index)
else
raise "Tail function is not applicable to #{previous_node.class}: #{previous_node}"
end
end
@@ -255,11 +248,11 @@
FHIR.logger.debug "DATA: #{tree}"
# evaluate all the functions at this level
functions = [:all, :not, :empty, :exists, :startsWith, :substring, :contains, :in, :distinct, :toInteger, :count]
size = -1
- while(tree.length!=size)
+ while tree.length != size
FHIR.logger.debug "FUNC: #{tree}"
previous_node = data
previous_index = nil
size = tree.length
tree.each_with_index do |node, index|
@@ -267,40 +260,39 @@
previous_node = compute(previous_node, data) if previous_node.is_a?(FluentPath::Expression)
case node
when :all
if previous_node.is_a?(Array)
result = true
- previous_node.each{|item| result = (result && convert_to_boolean(item))}
+ previous_node.each { |item| result = (result && convert_to_boolean(item)) }
tree[index] = result
- clean_index(tree, previous_index)
else
tree[index] = convert_to_boolean(previous_node)
- clean_index(tree, previous_index)
end
+ clean_index(tree, previous_index)
when :not
tree[index] = !convert_to_boolean(previous_node)
clean_index(tree, previous_index)
when :count
tree[index] = 0
- tree[index] = 1 if !previous_node.nil?
+ tree[index] = 1 unless previous_node.nil?
tree[index] = previous_node.length if previous_node.is_a?(Array)
clean_index(tree, previous_index)
when :empty
- tree[index] = (previous_node==:null || previous_node.empty? rescue previous_node.nil?)
+ tree[index] = (previous_node == :null || previous_node.empty? rescue previous_node.nil?)
clean_index(tree, previous_index)
when :exists
- tree[index] = !previous_node.nil? && previous_node!=:null
+ tree[index] = !previous_node.nil? && previous_node != :null
clean_index(tree, previous_index)
when :distinct
tree[index] = (previous_node.uniq rescue previous_node)
clean_index(tree, previous_index)
when :startsWith
# the previous node should be a data (as String)
# the next node should be a block or subexpression (as FluentPath::Expression)
- block = tree[index+1]
+ block = tree[index + 1]
if block.is_a?(FluentPath::Expression)
- tree[index+1] = nil
+ tree[index + 1] = nil
else
raise 'StartsWith function requires a block.'
end
if previous_node.is_a?(String)
FHIR.logger.debug 'Evaling StartsWith Block....'
@@ -312,41 +304,41 @@
end
break
when :substring
# the previous node should be a data (as String)
# the next node should be a block or subexpression (as FluentPath::Expression)
- block = tree[index+1]
+ block = tree[index + 1]
if block.is_a?(FluentPath::Expression)
- tree[index+1] = nil
+ tree[index + 1] = nil
else
raise 'Substring function requires a block.'
end
if previous_node.is_a?(String)
args = block.tree.first
start = 0
length = previous_node.length
if args.is_a?(String) && args.include?(',')
args = args.split(',')
start = args.first.to_i
- length = args.last.to_i-1
+ length = args.last.to_i - 1
else
FHIR.logger.debug 'Evaling Substring Block....'
start = compute(block, data)
length = previous_node.length - start
end
- tree[index] = previous_node[start..(start+length)]
+ tree[index] = previous_node[start..(start + length)]
clean_index(tree, previous_index)
else
raise "Substring function not applicable to #{previous_node.class}: #{previous_node}"
end
break
when :contains
# the previous node should be a data (as String)
# the next node should be a block or subexpression (as FluentPath::Expression)
- block = tree[index+1]
+ block = tree[index + 1]
if block.is_a?(FluentPath::Expression)
- tree[index+1] = nil
+ tree[index + 1] = nil
else
raise 'Contains function requires a block.'
end
if previous_node.is_a?(String)
FHIR.logger.debug 'Evaling Contains Block....'
@@ -358,22 +350,22 @@
end
break
when :in
# the previous node should be a data (as String, Number, or Boolean)
# the next node should an Array (possibly as a block or subexpression/FluentPath::Expression)
- block = tree[index+1]
+ block = tree[index + 1]
if block.is_a?(FluentPath::Expression)
FHIR.logger.debug 'Evaling In Block....'
- tree[index+1] = compute(block, data)
+ tree[index + 1] = compute(block, data)
end
- array = tree[index+1]
+ array = tree[index + 1]
if array.is_a?(Array)
- tree[index+1] = nil
+ tree[index + 1] = nil
else
raise 'In function requires an array.'
end
- if previous_node.is_a?(String) || previous_node==true || previous_node==false || previous_node.is_a?(Numeric)
+ if previous_node.is_a?(String) || previous_node == true || previous_node == false || previous_node.is_a?(Numeric)
tree[index] = array.include?(previous_node) rescue false
clean_index(tree, previous_index)
else
raise "In function not applicable to #{previous_node.class}: #{previous_node}"
end
@@ -401,29 +393,29 @@
end
# evaluate all mult/div
functions = [:"/", :"*"]
size = -1
- while(tree.length!=size)
+ while tree.length != size
FHIR.logger.debug "MATH: #{tree}"
previous_node = nil
previous_index = nil
size = tree.length
tree.each_with_index do |node, index|
if node.is_a?(Symbol) && functions.include?(node)
previous_node = compute(previous_node, data) if previous_node.is_a?(FluentPath::Expression)
- tree[index+1] = compute(tree[index+1], data) if tree[index+1].is_a?(FluentPath::Expression)
+ tree[index + 1] = compute(tree[index + 1], data) if tree[index + 1].is_a?(FluentPath::Expression)
left = previous_node
- right = tree[index+1]
+ right = tree[index + 1]
case node
when :"/"
- tree[index] = (left/right)
+ tree[index] = (left / right)
when :"*"
- tree[index] = (left*right)
+ tree[index] = (left * right)
end
tree[previous_index] = nil
- tree[index+1] = nil
+ tree[index + 1] = nil
break
end
previous_index = index
previous_node = node
end
@@ -432,29 +424,29 @@
FHIR.logger.debug "MATH: #{tree}"
# evaluate all add/sub
functions = [:"+", :"-"]
size = -1
- while(tree.length!=size)
+ while tree.length != size
FHIR.logger.debug "MATH: #{tree}"
previous_node = nil
previous_index = nil
size = tree.length
tree.each_with_index do |node, index|
if node.is_a?(Symbol) && functions.include?(node)
previous_node = compute(previous_node, data) if previous_node.is_a?(FluentPath::Expression)
- tree[index+1] = compute(tree[index+1], data) if tree[index+1].is_a?(FluentPath::Expression)
+ tree[index + 1] = compute(tree[index + 1], data) if tree[index + 1].is_a?(FluentPath::Expression)
left = previous_node
- right = tree[index+1]
+ right = tree[index + 1]
case node
when :"+"
- tree[index] = (left+right)
+ tree[index] = (left + right)
when :"-"
- tree[index] = (left-right)
+ tree[index] = (left - right)
end
tree[previous_index] = nil
- tree[index+1] = nil
+ tree[index + 1] = nil
break
end
previous_index = index
previous_node = node
end
@@ -463,39 +455,39 @@
FHIR.logger.debug "MATH: #{tree}"
# evaluate all equality tests
functions = [:"=", :"!=", :"<=", :">=", :"<", :">"]
size = -1
- while(tree.length!=size)
+ while tree.length != size
FHIR.logger.debug "EQ: #{tree}"
previous_node = nil
previous_index = nil
size = tree.length
tree.each_with_index do |node, index|
if node.is_a?(Symbol) && functions.include?(node)
previous_node = compute(previous_node, data) if previous_node.is_a?(FluentPath::Expression)
- tree[index+1] = compute(tree[index+1], data) if tree[index+1].is_a?(FluentPath::Expression)
+ tree[index + 1] = compute(tree[index + 1], data) if tree[index + 1].is_a?(FluentPath::Expression)
left = previous_node
- right = tree[index+1]
+ right = tree[index + 1]
case node
when :"="
- tree[index] = (left==right)
+ tree[index] = (left == right)
when :"!="
- tree[index] = (left!=right)
+ tree[index] = (left != right)
when :"<="
- tree[index] = (left<=right)
+ tree[index] = (left <= right)
when :">="
- tree[index] = (left>=right)
+ tree[index] = (left >= right)
when :"<"
- tree[index] = (left<right)
+ tree[index] = (left < right)
when :">"
- tree[index] = (left>right)
+ tree[index] = (left > right)
else
raise "Equality operator not implemented: #{node}"
end
tree[previous_index] = nil
- tree[index+1] = nil
+ tree[index + 1] = nil
break
end
previous_index = index
previous_node = node
end
@@ -504,33 +496,33 @@
FHIR.logger.debug "EQ: #{tree}"
# evaluate all logical tests
functions = [:and, :or, :xor]
size = -1
- while(tree.length!=size)
+ while tree.length != size
FHIR.logger.debug "LOGIC: #{tree}"
previous_node = nil
previous_index = nil
size = tree.length
tree.each_with_index do |node, index|
if node.is_a?(Symbol) && functions.include?(node)
previous_node = compute(previous_node, data) if previous_node.is_a?(FluentPath::Expression)
- tree[index+1] = compute(tree[index+1], data) if tree[index+1].is_a?(FluentPath::Expression)
+ tree[index + 1] = compute(tree[index + 1], data) if tree[index + 1].is_a?(FluentPath::Expression)
left = convert_to_boolean(previous_node)
- right = convert_to_boolean(tree[index+1])
+ right = convert_to_boolean(tree[index + 1])
case node
when :and
- tree[index] = (left&&right)
+ tree[index] = (left && right)
when :or
- tree[index] = (left||right)
+ tree[index] = (left || right)
when :xor
- tree[index] = (left^right)
+ tree[index] = (left ^ right)
else
raise "Logical operator not implemented: #{node}"
end
tree[previous_index] = nil
- tree[index+1] = nil
+ tree[index + 1] = nil
break
end
previous_index = index
previous_node = node
end
@@ -538,30 +530,30 @@
end
FHIR.logger.debug "LOGIC: #{tree}"
functions = [:implies]
size = -1
- while(tree.length!=size)
+ while tree.length != size
FHIR.logger.debug "IMPLIES: #{tree}"
previous_node = nil
previous_index = nil
size = tree.length
tree.each_with_index do |node, index|
if node.is_a?(Symbol) && functions.include?(node)
previous_node = compute(previous_node, data) if previous_node.is_a?(FluentPath::Expression)
- tree[index+1] = compute(tree[index+1], data) if tree[index+1].is_a?(FluentPath::Expression)
+ tree[index + 1] = compute(tree[index + 1], data) if tree[index + 1].is_a?(FluentPath::Expression)
case node
when :implies
tree[index] = false
- exists = !previous_node.nil? && previous_node!=:null
- implication = convert_to_boolean(tree[index+1])
- tree[index] = true if (exists && (implication || tree[index+1]==false))
+ exists = !previous_node.nil? && previous_node != :null
+ implication = convert_to_boolean(tree[index + 1])
+ tree[index] = true if exists && (implication || tree[index + 1] == false)
else
raise "Logical operator not implemented: #{node}"
end
tree[previous_index] = nil
- tree[index+1] = nil
+ tree[index + 1] = nil
break
end
previous_index = index
previous_node = node
end
@@ -584,7 +576,6 @@
end
FHIR.logger.debug "RETURN: #{tree.first}"
tree.first
end
-
end