diff --git a/lib/body_tracking/formula.rb b/lib/body_tracking/formula.rb index 2b5f943..3f38e81 100644 --- a/lib/body_tracking/formula.rb +++ b/lib/body_tracking/formula.rb @@ -14,13 +14,6 @@ module BodyTracking end 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 - parser = FormulaBuilder.new(@formula) identifiers, parts = parser.parse errors = parser.errors @@ -191,7 +184,7 @@ module BodyTracking [right[1], :unknown_method] end else - raise NotImplementedError + raise NotImplementedError, right.inspect end case left[0] @@ -211,7 +204,7 @@ module BodyTracking else [:bt_numeric_method_call, "#{left[1]}#{dot.to_s}#{method}"] end - when :bt_numeric_method_call + when :bt_numeric_method_call, :bt_expression if mtype == :quantity_method # TODO: add error reporting raise NotImplementedError @@ -219,7 +212,7 @@ module BodyTracking [:bt_numeric_method_call, "#{left[1]}#{dot.to_s}#{method}"] end else - raise NotImplementedError + raise NotImplementedError, left.inspect end end @@ -279,10 +272,18 @@ module BodyTracking [:bt_quantity, token] end + def on_float(token) + [:bt_expression, token] + end + def on_ident(token) [:bt_ident, token] end + def on_int(token) + [:bt_expression, token] + end + def on_kw(token) @disallowed[:keyword] << token unless token == 'nil' end @@ -295,10 +296,12 @@ module BodyTracking stmts.map do |stmt| ttype, token = stmt case ttype - when :bt_expression + when :bt_expression, :bt_numeric_method_call token + when :bt_quantity + "quantities['#{token}'][_index]" else - raise NotImplementedError + raise NotImplementedError, stmt.inspect end end.join(';') end diff --git a/test/unit/formula_test.rb b/test/unit/formula_test.rb index 76e9571..7446b9b 100644 --- a/test/unit/formula_test.rb +++ b/test/unit/formula_test.rb @@ -7,14 +7,61 @@ class FormulaTest < ActiveSupport::TestCase end def test_builder_parses_valid_formulas_properly + # TODO: add tests + # failing test vectors: + # - fcall disallowed: "abs(Fats)+Energy < 10" + vector = [ + # Non-quantity expressions '4', Set[], [ - {type: :indexed, content: '4*2'} + {type: :indexed, content: "4"} + ], + '3.2', Set[], [ + {type: :indexed, content: "3.2"} + ], + '4 * 2', Set[], [ + {type: :indexed, content: "4*2"} + ], + '7.3 * 2.1', Set[], [ + {type: :indexed, content: "7.3*2.1"} + ], + '7 * 2.1', Set[], [ + {type: :indexed, content: "7*2.1"} ], - #'4*2' - #'Fats' - #'fats' + # Quantity expressions + 'Fats', Set['Fats'], [ + {type: :indexed, content: "quantities['Fats'][_index]"} + ], + 'fats', Set['fats'], [ + {type: :indexed, content: "quantities['fats'][_index]"} + ], + '2 * Fats', Set['Fats'], [ + {type: :indexed, content: "2*quantities['Fats'][_index]"} + ], + '4*Proteins + 9*Fats + 4*Carbohydrates', Set['Proteins', 'Fats', 'Carbohydrates'], [ + {type: :indexed, content: "4*quantities['Proteins'][_index]+" \ + "9*quantities['Fats'][_index]+4*quantities['Carbohydrates'][_index]"} + ], + 'Weight * (Fats + 0.2)', Set['Weight', 'Fats'], [ + {type: :indexed, content: "quantities['Weight'][_index]*" \ + "(quantities['Fats'][_index]+0.2)"} + ], + + # Numeric method calls + 'Fats.nil?', Set['Fats'], [ + {type: :indexed, content: "quantities['Fats'][_index].nil?"} + ], + '((Energy-Calculated)/Energy).abs', Set['Energy', 'Calculated'], [ + {type: :indexed, content: "((quantities['Energy'][_index]-" \ + "quantities['Calculated'][_index])/quantities['Energy'][_index]).abs"} + ], + + # Conditional expressions + 'Fats.nil? || Fats/Proteins > 2', Set['Fats', 'Proteins'], [ + {type: :indexed, content: "quantities['Fats'][_index].nil?||" \ + "quantities['Fats'][_index]/quantities['Proteins'][_index]>2"} + ], ] vector.each_slice(3) do |formula, identifiers, parts|