From a740527b42ebe131bfb382d876817499edb12e23 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 23 Feb 2016 16:47:42 -0500 Subject: [PATCH] [Finishes #114336031] Adds support for parsing ratios --- lib/unit/lexer.rb | 3 + lib/unit/lexer_definition.rex | 2 + lib/unit/parser.rb | 170 ++++++++++++++++++---------------- lib/unit/parser_definition.y | 7 +- test/parser_test.rb | 10 ++ 5 files changed, 112 insertions(+), 80 deletions(-) diff --git a/lib/unit/lexer.rb b/lib/unit/lexer.rb index ed6e861..e8d705f 100644 --- a/lib/unit/lexer.rb +++ b/lib/unit/lexer.rb @@ -63,6 +63,9 @@ def _next_token when (text = @ss.scan(/[-+]?[0-9]*\.?[0-9]+/i)) action { [:SCALAR, BigDecimal.new(text, 10)] } + when (text = @ss.scan(/[:]/i)) + action { [:COLON, text] } + when (text = @ss.scan(/\b(?:gm|gram)\b/i)) action { [:MASS_UOM, 'g'] } diff --git a/lib/unit/lexer_definition.rex b/lib/unit/lexer_definition.rex index 23215e8..aed8889 100644 --- a/lib/unit/lexer_definition.rex +++ b/lib/unit/lexer_definition.rex @@ -9,9 +9,11 @@ macro UNIT_UOM \b(?:unit|u)\b UNITLESS_UOM \b(?:ea)\b EQUIVALENCE_UOM \b(?:meq|eq)\b + COLON [:] rule {BLANK} {SCALAR} { [:SCALAR, BigDecimal.new(text, 10)] } + {COLON} { [:COLON, text] } #Mass \b(?:gm|gram)\b { [:MASS_UOM, 'g'] } diff --git a/lib/unit/parser.rb b/lib/unit/parser.rb index 9c8be69..2f5cf77 100644 --- a/lib/unit/parser.rb +++ b/lib/unit/parser.rb @@ -11,7 +11,7 @@ module Unit class Parser < Racc::Parser -module_eval(<<'...end parser_definition.y/module_eval...', 'parser_definition.y', 52) +module_eval(<<'...end parser_definition.y/module_eval...', 'parser_definition.y', 55) def parse(input) #Takes the results from the lexer's tokenize method and returns stuff @tokens = input @@ -25,83 +25,85 @@ def next_token ##### State transition tables begin ### racc_action_table = [ - 24, 25, 26, 27, 29, 28, 23, 33, 33, 37, - 39, 33, 33, 35, 32, 17, 21, 30, 22, 20, - 19, 18, 40, 25, 41, 42, 43 ] + 25, 26, 27, 28, 30, 29, 24, 31, 35, 35, + 39, 41, 35, 35, 34, 37, 18, 22, 32, 21, + 23, 20, 19, 42, 43, 26, 44, 45, 46 ] racc_action_check = [ - 17, 17, 17, 17, 17, 17, 17, 21, 22, 21, - 22, 20, 19, 20, 19, 0, 14, 18, 15, 13, - 11, 1, 23, 33, 40, 41, 42 ] + 18, 18, 18, 18, 18, 18, 18, 18, 22, 23, + 22, 23, 20, 21, 20, 21, 0, 14, 19, 13, + 15, 11, 1, 24, 31, 35, 42, 44, 45 ] racc_action_pointer = [ - 13, 21, nil, nil, nil, nil, nil, nil, nil, nil, - nil, 11, nil, 10, 7, 9, nil, -3, 17, 10, - 9, 5, 6, 20, nil, nil, nil, nil, nil, nil, - nil, nil, nil, 19, nil, nil, nil, nil, nil, nil, - 21, 16, 22, nil ] + 14, 22, nil, nil, nil, nil, nil, nil, nil, nil, + nil, 12, nil, 10, 8, 11, nil, nil, -3, 18, + 10, 11, 6, 7, 21, nil, nil, nil, nil, nil, + nil, 22, nil, nil, nil, 21, nil, nil, nil, nil, + nil, nil, 23, nil, 18, 24, nil ] racc_action_default = [ - -31, -31, -1, -2, -3, -4, -5, -6, -7, -8, - -9, -10, -11, -12, -13, -14, -15, -31, -31, -31, - -31, -31, -31, -31, -25, -26, -27, -28, -29, -30, - 44, -16, -17, -31, -19, -20, -23, -24, -21, -22, - -31, -31, -31, -18 ] + -33, -33, -1, -2, -3, -4, -5, -6, -7, -8, + -9, -10, -11, -12, -13, -14, -15, -16, -33, -33, + -33, -33, -33, -33, -33, -26, -27, -28, -29, -30, + -31, -33, 47, -17, -18, -33, -20, -21, -24, -25, + -22, -23, -33, -32, -33, -33, -19 ] racc_goto_table = [ 12, 9, 3, 4, 5, 6, 7, 8, 2, 10, - 11, 1, 13, 14, 15, 16, nil, nil, nil, 31, - 34, 36, 38 ] + 11, 1, 13, 14, 15, 16, 17, nil, nil, nil, + 33, 36, 38, 40 ] racc_goto_check = [ 12, 9, 3, 4, 5, 6, 7, 8, 2, 10, - 11, 1, 13, 14, 15, 16, nil, nil, nil, 12, - 12, 12, 12 ] + 11, 1, 13, 14, 15, 16, 17, nil, nil, nil, + 12, 12, 12, 12 ] racc_goto_pointer = [ nil, 11, 8, 2, 3, 4, 5, 6, 7, 1, - 9, 10, 0, 12, 13, 14, 15 ] + 9, 10, 0, 12, 13, 14, 15, 16 ] racc_goto_default = [ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil ] + nil, nil, nil, nil, nil, nil, nil, nil ] racc_reduce_table = [ 0, 0, :racc_error, - 1, 11, :_reduce_none, - 1, 11, :_reduce_none, - 1, 11, :_reduce_none, - 1, 11, :_reduce_none, - 1, 11, :_reduce_none, - 1, 11, :_reduce_none, - 1, 11, :_reduce_none, - 1, 11, :_reduce_none, - 1, 11, :_reduce_none, - 1, 11, :_reduce_none, - 1, 11, :_reduce_none, - 1, 11, :_reduce_none, - 1, 11, :_reduce_none, - 1, 11, :_reduce_none, - 1, 11, :_reduce_none, - 3, 12, :_reduce_16, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, + 1, 12, :_reduce_none, 3, 13, :_reduce_17, - 6, 20, :_reduce_18, - 3, 14, :_reduce_19, + 3, 14, :_reduce_18, + 6, 21, :_reduce_19, 3, 15, :_reduce_20, - 3, 18, :_reduce_21, + 3, 16, :_reduce_21, 3, 19, :_reduce_22, - 3, 16, :_reduce_23, + 3, 20, :_reduce_23, 3, 17, :_reduce_24, - 2, 21, :_reduce_25, + 3, 18, :_reduce_25, 2, 22, :_reduce_26, 2, 23, :_reduce_27, 2, 24, :_reduce_28, - 2, 26, :_reduce_29, - 2, 25, :_reduce_30 ] + 2, 25, :_reduce_29, + 2, 27, :_reduce_30, + 2, 26, :_reduce_31, + 3, 28, :_reduce_32 ] -racc_reduce_n = 31 +racc_reduce_n = 33 -racc_shift_n = 44 +racc_shift_n = 47 racc_token_table = { false => 0, @@ -113,9 +115,10 @@ def next_token :UNITLESS_UOM => 6, :EQUIVALENCE_UOM => 7, :PERCENT => 8, - :SLASH => 9 } + :SLASH => 9, + :COLON => 10 } -racc_nt_base = 10 +racc_nt_base = 11 racc_use_result_var = true @@ -146,6 +149,7 @@ def next_token "EQUIVALENCE_UOM", "PERCENT", "SLASH", + "COLON", "$start", "valid_unit", "concentration", @@ -162,7 +166,8 @@ def next_token "unit", "unitless", "equivalence", - "percent" ] + "percent", + "solution" ] Racc_debug_parser = false @@ -200,58 +205,53 @@ def next_token # reduce 15 omitted -module_eval(<<'.,.,', 'parser_definition.y', 20) - def _reduce_16(val, _values, result) - return Concentration.new(val[0], val[2]) - result - end -.,., +# reduce 16 omitted module_eval(<<'.,.,', 'parser_definition.y', 21) def _reduce_17(val, _values, result) - return Concentration.new(val[0], Volume.new(1, val[2])) + return Concentration.new(val[0], val[2]) result end .,., module_eval(<<'.,.,', 'parser_definition.y', 22) def _reduce_18(val, _values, result) - return Concentration.new(Mass.new(val[0], val[3]), Volume.new(val[2], val[5])) + return Concentration.new(val[0], Volume.new(1, val[2])) result end .,., -module_eval(<<'.,.,', 'parser_definition.y', 24) +module_eval(<<'.,.,', 'parser_definition.y', 23) def _reduce_19(val, _values, result) - return Concentration.new(val[0], val[2]) + return Concentration.new(Mass.new(val[0], val[3]), Volume.new(val[2], val[5])) result end .,., module_eval(<<'.,.,', 'parser_definition.y', 25) def _reduce_20(val, _values, result) - return Concentration.new(val[0], Volume.new(1, val[2])) + return Concentration.new(val[0], val[2]) result end .,., -module_eval(<<'.,.,', 'parser_definition.y', 27) +module_eval(<<'.,.,', 'parser_definition.y', 26) def _reduce_21(val, _values, result) - return Concentration.new(val[0], val[2]) + return Concentration.new(val[0], Volume.new(1, val[2])) result end .,., module_eval(<<'.,.,', 'parser_definition.y', 28) def _reduce_22(val, _values, result) - return Concentration.new(val[0], Volume.new(1, val[2])) + return Concentration.new(val[0], val[2]) result end .,., -module_eval(<<'.,.,', 'parser_definition.y', 30) +module_eval(<<'.,.,', 'parser_definition.y', 29) def _reduce_23(val, _values, result) - return Concentration.new(val[0], val[2]) + return Concentration.new(val[0], Volume.new(1, val[2])) result end .,., @@ -263,48 +263,62 @@ def _reduce_24(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser_definition.y', 33) +module_eval(<<'.,.,', 'parser_definition.y', 32) def _reduce_25(val, _values, result) - return Mass.new(val[0], val[1]) + return Concentration.new(val[0], val[2]) result end .,., -module_eval(<<'.,.,', 'parser_definition.y', 35) +module_eval(<<'.,.,', 'parser_definition.y', 34) def _reduce_26(val, _values, result) - return Volume.new(val[0], val[1]) + return Mass.new(val[0], val[1]) result end .,., -module_eval(<<'.,.,', 'parser_definition.y', 37) +module_eval(<<'.,.,', 'parser_definition.y', 36) def _reduce_27(val, _values, result) - return Unit.new(val[0], 'unit') + return Volume.new(val[0], val[1]) result end .,., -module_eval(<<'.,.,', 'parser_definition.y', 39) +module_eval(<<'.,.,', 'parser_definition.y', 38) def _reduce_28(val, _values, result) - return Unit.new(val[0], val[1]) + return Unit.new(val[0], 'unit') result end .,., -module_eval(<<'.,.,', 'parser_definition.y', 41) +module_eval(<<'.,.,', 'parser_definition.y', 40) def _reduce_29(val, _values, result) - return Concentration.new(Mass.new(val[0] * 10, 'mg'), Volume.new(1, 'ml')) + return Unit.new(val[0], val[1]) result end .,., -module_eval(<<'.,.,', 'parser_definition.y', 43) +module_eval(<<'.,.,', 'parser_definition.y', 42) def _reduce_30(val, _values, result) + return Concentration.new(Mass.new(val[0] * 10, 'mg'), Volume.new(1, 'ml')) + result + end +.,., + +module_eval(<<'.,.,', 'parser_definition.y', 44) + def _reduce_31(val, _values, result) return Equivalence.new(val[0], val[1]) result end .,., +module_eval(<<'.,.,', 'parser_definition.y', 46) + def _reduce_32(val, _values, result) + return Concentration.new(Mass.new(val[0] * 1000, 'mg'), Volume.new(val[2], 'ml')) + result + end +.,., + def _reduce_none(val, _values, result) val[0] end diff --git a/lib/unit/parser_definition.y b/lib/unit/parser_definition.y index f759998..10f8054 100644 --- a/lib/unit/parser_definition.y +++ b/lib/unit/parser_definition.y @@ -1,5 +1,5 @@ class Unit::Parser -token SCALAR MASS_UOM VOLUME_UOM UNIT_UOM UNITLESS_UOM EQUIVALENCE_UOM PERCENT SLASH +token SCALAR MASS_UOM VOLUME_UOM UNIT_UOM UNITLESS_UOM EQUIVALENCE_UOM PERCENT SLASH COLON rule valid_unit: concentration | @@ -16,7 +16,8 @@ rule unit | unitless | equivalence | - percent + percent | + solution concentration : mass SLASH volume { return Concentration.new(val[0], val[2]) } concentration_no_denom_scalar : mass SLASH VOLUME_UOM { return Concentration.new(val[0], Volume.new(1, val[2])) } @@ -43,6 +44,8 @@ rule equivalence : SCALAR EQUIVALENCE_UOM { return Equivalence.new(val[0], val[1]) } + solution : SCALAR COLON SCALAR { return Concentration.new(Mass.new(val[0] * 1000, 'mg'), Volume.new(val[2], 'ml')) } + end ---- header diff --git a/test/parser_test.rb b/test/parser_test.rb index e9a0742..aa11d48 100644 --- a/test/parser_test.rb +++ b/test/parser_test.rb @@ -97,5 +97,15 @@ class ParserTest < Minitest::Test assert_equal "mg/ml", conc.uom end end + + context "solution" do + should "parse a normal solution" do + conc = Unit.parse("1:1000") + + assert_equal true, conc.concentration? + assert_equal 1, conc.scalar + assert_equal "mg/ml", conc.uom + end + end end end