Removed old (Ripper.lex based) formula validation code
Muted multiple 'disallowed' messages for keywords Removed unnecessary .to_d call in formula computation
This commit is contained in:
parent
d768adb86b
commit
613c26728c
@ -29,7 +29,8 @@ en:
|
|||||||
invalid_formula: 'interpretation failed: %{msg}'
|
invalid_formula: 'interpretation failed: %{msg}'
|
||||||
unparsable_formula: 'cannot be parsed'
|
unparsable_formula: 'cannot be parsed'
|
||||||
disallowed_token: 'includes disallowed token "%{token}" (of type: %{ttype})'
|
disallowed_token: 'includes disallowed token "%{token}" (of type: %{ttype})'
|
||||||
disallowed_function_call: 'includes disallowed function call: %{function}'
|
disallowed_keyword: 'includes disallowed keyword: "%{keyword}"'
|
||||||
|
disallowed_function: 'includes disallowed function call: %{function}'
|
||||||
unknown_quantity: 'contains undefined quantity: %{quantity}'
|
unknown_quantity: 'contains undefined quantity: %{quantity}'
|
||||||
body_trackers:
|
body_trackers:
|
||||||
index:
|
index:
|
||||||
|
@ -10,66 +10,16 @@ module BodyTracking
|
|||||||
@formula = formula
|
@formula = formula
|
||||||
end
|
end
|
||||||
|
|
||||||
#def validate
|
|
||||||
# errors = []
|
|
||||||
|
|
||||||
# # 1st: check if formula is valid Ruby code
|
|
||||||
# #tokenized_length = Ripper.tokenize(@formula).join.length
|
|
||||||
# #unless tokenized_length == @formula.length
|
|
||||||
# # errors << [:invalid_formula, {part: @formula[0...tokenized_length]}]
|
|
||||||
# #end
|
|
||||||
# begin
|
|
||||||
# eval("-> { #{@formula} }")
|
|
||||||
# rescue ScriptError => e
|
|
||||||
# errors << [:invalid_formula, {msg: e.message}]
|
|
||||||
# end
|
|
||||||
|
|
||||||
# # 2nd: check if formula contains only allowed token types
|
|
||||||
# # 3rd: check for disallowed function calls (they are not detected by Ripper.lex)
|
|
||||||
# # FIXME: this is unreliable (?) detection of function calls, should be replaced
|
|
||||||
# # with parsing Ripper.sexp if necessary
|
|
||||||
# identifiers = []
|
|
||||||
# disallowed_functions = Set.new
|
|
||||||
# prev_ttype, prev_token = nil, nil
|
|
||||||
# Ripper.lex(@formula).each do |location, ttype, token|
|
|
||||||
# puts ttype, token
|
|
||||||
# case
|
|
||||||
# when QUANTITY_TTYPES.include?(prev_ttype) && ttype == :on_lparen
|
|
||||||
# disallowed_functions << prev_token unless FUNCTIONS.include?(prev_token)
|
|
||||||
# identifiers -= [prev_token]
|
|
||||||
# when prev_ttype == :on_period && QUANTITY_TTYPES.include?(ttype)
|
|
||||||
# disallowed_functions << token unless FUNCTIONS.include?(token)
|
|
||||||
# when is_token_quantity?(ttype, token)
|
|
||||||
# identifiers << token
|
|
||||||
# when [:on_sp, :on_int, :on_rational, :on_float, :on_tstring_beg, :on_tstring_end,
|
|
||||||
# :on_lparen, :on_rparen, :on_period].include?(ttype)
|
|
||||||
# when :on_op == ttype &&
|
|
||||||
# ['+', '-', '*', '/', '%', '**', '==', '!=', '>', '<', '>=', '<=', '<=>', '===',
|
|
||||||
# '..', '...', '?:', 'and', 'or', 'not', '&&', '||', '!'].include?(token)
|
|
||||||
# when :on_kw == ttype && ['and', 'or', 'not'].include?(token)
|
|
||||||
# else
|
|
||||||
# errors << [:disallowed_token, {token: token, ttype: ttype, location: location}]
|
|
||||||
# end
|
|
||||||
# prev_ttype, prev_token = ttype, token unless ttype == :on_sp
|
|
||||||
# end
|
|
||||||
# disallowed_functions.each { |f| errors << [:disallowed_function_call, {function: f}] }
|
|
||||||
|
|
||||||
# # 4th: check if identifiers used in formula correspond to existing quantities
|
|
||||||
# identifiers.uniq!
|
|
||||||
# quantities = @project.quantities.where(name: identifiers).pluck(:name)
|
|
||||||
# (identifiers - quantities).each { |q| errors << [:unknown_quantity, {quantity: q}] }
|
|
||||||
|
|
||||||
# errors
|
|
||||||
#end
|
|
||||||
|
|
||||||
def validate
|
def validate
|
||||||
|
# TODO: add tests
|
||||||
|
# failing test vectors:
|
||||||
|
# - fcall disallowed: "abs(Fats)+Energy < 10"
|
||||||
|
# working test vectors:
|
||||||
|
# ((Energy-Calculated)/Energy).abs > 0.2
|
||||||
|
# Fats.nil? || Fats/Proteins > 2
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
# 1st: check if formula is valid Ruby code
|
# 1st: check if formula is syntactically valid Ruby code
|
||||||
#tokenized_length = Ripper.tokenize(@formula).join.length
|
|
||||||
#unless tokenized_length == @formula.length
|
|
||||||
# errors << [:invalid_formula, {part: @formula[0...tokenized_length]}]
|
|
||||||
#end
|
|
||||||
begin
|
begin
|
||||||
eval("-> { #{@formula} }")
|
eval("-> { #{@formula} }")
|
||||||
rescue ScriptError => e
|
rescue ScriptError => e
|
||||||
@ -77,16 +27,9 @@ module BodyTracking
|
|||||||
end
|
end
|
||||||
|
|
||||||
# 2nd: check if formula contains only allowed token types
|
# 2nd: check if formula contains only allowed token types
|
||||||
# 3rd: check for disallowed function calls (they are not detected by Ripper.lex)
|
# 3rd: check for disallowed function calls
|
||||||
# FIXME: this is unreliable (?) detection of function calls, should be replaced
|
|
||||||
# with parsing Ripper.sexp if necessary
|
|
||||||
# failing test vectors:
|
|
||||||
# - fcall disallowed: "abs(Fats)+Energy < 10"
|
|
||||||
# working test vectors:
|
|
||||||
# Fats.abs+Energy < 10
|
|
||||||
# (Energy-Calculated).abs > 10
|
|
||||||
identifiers = []
|
identifiers = []
|
||||||
disallowed_functions = Set.new
|
disallowed = Hash.new { |h,k| h[k] = Set.new }
|
||||||
|
|
||||||
stree = [Ripper.sexp(@formula)]
|
stree = [Ripper.sexp(@formula)]
|
||||||
errors << [:unparsable_formula, {}] unless stree.first
|
errors << [:unparsable_formula, {}] unless stree.first
|
||||||
@ -105,10 +48,10 @@ module BodyTracking
|
|||||||
stree.unshift(token)
|
stree.unshift(token)
|
||||||
dot, method = rest
|
dot, method = rest
|
||||||
ftype, fname, floc = method
|
ftype, fname, floc = method
|
||||||
disallowed_functions << fname unless FUNCTIONS.include?(fname)
|
disallowed[:function] << fname unless FUNCTIONS.include?(fname)
|
||||||
when :fcall
|
when :fcall
|
||||||
ftype, fname, floc = token
|
ftype, fname, floc = token
|
||||||
disallowed_functions << fname
|
disallowed[:function] << fname
|
||||||
when :vcall
|
when :vcall
|
||||||
ftype, fname, floc = token
|
ftype, fname, floc = token
|
||||||
identifiers << fname
|
identifiers << fname
|
||||||
@ -116,35 +59,20 @@ module BodyTracking
|
|||||||
stree.unshift(token)
|
stree.unshift(token)
|
||||||
when :var_ref
|
when :var_ref
|
||||||
vtype, vname, vloc = token
|
vtype, vname, vloc = token
|
||||||
|
case vtype
|
||||||
|
when vtype == :@conts
|
||||||
identifiers << vname
|
identifiers << vname
|
||||||
|
when vtype == :@kw
|
||||||
|
disallowed[:keyword] << token if vname != 'nil'
|
||||||
|
end
|
||||||
when :@int, :@float
|
when :@int, :@float
|
||||||
else
|
else
|
||||||
errors << [:disallowed_token, {token: token, ttype: ttype}]
|
errors << [:disallowed_token, {token: token, ttype: ttype}]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#Ripper.lex(@formula).each do |location, ttype, token|
|
disallowed[:function].each { |f| errors << [:disallowed_function, {function: f}] }
|
||||||
# puts ttype, token
|
disallowed[:keyword].each { |k| errors << [:disallowed_keyword, {keyword: k}] }
|
||||||
# case
|
|
||||||
# when QUANTITY_TTYPES.include?(prev_ttype) && ttype == :on_lparen
|
|
||||||
# disallowed_functions << prev_token unless FUNCTIONS.include?(prev_token)
|
|
||||||
# identifiers -= [prev_token]
|
|
||||||
# when prev_ttype == :on_period && QUANTITY_TTYPES.include?(ttype)
|
|
||||||
# disallowed_functions << token unless FUNCTIONS.include?(token)
|
|
||||||
# when is_token_quantity?(ttype, token)
|
|
||||||
# identifiers << token
|
|
||||||
# when [:on_sp, :on_int, :on_rational, :on_float, :on_tstring_beg, :on_tstring_end,
|
|
||||||
# :on_lparen, :on_rparen, :on_period].include?(ttype)
|
|
||||||
# when :on_op == ttype &&
|
|
||||||
# ['+', '-', '*', '/', '%', '**', '==', '!=', '>', '<', '>=', '<=', '<=>', '===',
|
|
||||||
# '..', '...', '?:', 'and', 'or', 'not', '&&', '||', '!'].include?(token)
|
|
||||||
# when :on_kw == ttype && ['and', 'or', 'not'].include?(token)
|
|
||||||
# else
|
|
||||||
# errors << [:disallowed_token, {token: token, ttype: ttype, location: location}]
|
|
||||||
# end
|
|
||||||
# prev_ttype, prev_token = ttype, token unless ttype == :on_sp
|
|
||||||
#end
|
|
||||||
disallowed_functions.each { |f| errors << [:disallowed_function_call, {function: f}] }
|
|
||||||
|
|
||||||
# 4th: check if identifiers used in formula correspond to existing quantities
|
# 4th: check if identifiers used in formula correspond to existing quantities
|
||||||
identifiers.uniq!
|
identifiers.uniq!
|
||||||
@ -167,10 +95,11 @@ module BodyTracking
|
|||||||
|
|
||||||
def calculate(inputs)
|
def calculate(inputs)
|
||||||
paramed_formula = Ripper.lex(@formula).map do |*, ttype, token|
|
paramed_formula = Ripper.lex(@formula).map do |*, ttype, token|
|
||||||
is_token_quantity?(ttype, token) ? "params['#{token}'].to_d" : token
|
is_token_quantity?(ttype, token) ? "params['#{token}']" : token
|
||||||
end.join
|
end.join
|
||||||
|
|
||||||
inputs.map do |i, values|
|
inputs.map do |i, values|
|
||||||
|
puts values.inspect
|
||||||
begin
|
begin
|
||||||
[i, [get_binding(values).eval(paramed_formula), nil]]
|
[i, [get_binding(values).eval(paramed_formula), nil]]
|
||||||
rescue Exception => e
|
rescue Exception => e
|
||||||
|
Reference in New Issue
Block a user