Gray Soft / Deadly Regular Expressions / A Regex Can't Match Balanced Parentheses
tag:graysoftinc.com,2014-03-20:/posts/131
2015-01-02T23:06:34Z
James Edward Gray II
The 3rd Comment on "A Regex Can't Match Balanced Parentheses"
tag:graysoftinc.com,2015-01-02:/comments/567
2015-01-02T23:06:34Z
It's _known_ now, but, no, I wasn't aware of this bug. Good find.
<p>It's <em>known</em> now, but, no, I wasn't aware of this bug. Good find.</p>
James Edward Gray II
The 2nd Comment on "A Regex Can't Match Balanced Parentheses"
tag:graysoftinc.com,2015-01-02:/comments/566
2015-01-02T23:06:34Z
```
$ ruby math.rb -v '1 + 2 * (3 + 4)'
1 + 2 * (3 + 4)
(1 + 2) * (3 + 4)
((1 + 2) * (3 + 4))
(3.0 * (3 + 4))
(3.0 * 7.0)
21
```
Is this a known limitation?
<pre><code>$ ruby math.rb -v '1 + 2 * (3 + 4)'
1 + 2 * (3 + 4)
(1 + 2) * (3 + 4)
((1 + 2) * (3 + 4))
(3.0 * (3 + 4))
(3.0 * 7.0)
21
</code></pre>
<p>Is this a known limitation?</p>
Krishna Dole
The 1st Comment on "A Regex Can't Match Balanced Parentheses"
tag:graysoftinc.com,2014-10-01:/comments/562
2014-10-01T17:13:11Z
Nice code! I like the approach of defining regexp parts in `build_preparation_regexp` and also the recursive thing (I wasn't aware of this possibility!)
<p>Nice code! I like the approach of defining regexp parts in <code>build_preparation_regexp</code> and also the recursive thing (I wasn't aware of this possibility!)</p>
Iazel
A Regex Can't Match Balanced Parentheses
tag:graysoftinc.com,2014-09-22:/posts/131
2015-01-02T23:06:34Z
Can regular expressions do math, check math problems, and even obey the order of operations as they do it?
<p>Can we do math with regular expressions?</p>
<div class="highlight highlight-ruby"><pre><span class="c1">#!/usr/bin/env ruby -w</span>
<span class="k">def</span> <span class="nf">build_preparation_regex</span><span class="p">(</span><span class="n">number_regex</span><span class="p">,</span> <span class="n">ops</span><span class="p">)</span>
<span class="sr">%r{</span>
<span class="sr"> (?<number> </span><span class="si">#{</span><span class="n">number_regex</span><span class="si">}</span><span class="sr"> ){0}</span>
<span class="sr"> (?<operator> [</span><span class="si">#{</span><span class="n">ops</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="o">&</span><span class="no">Regexp</span><span class="o">.</span><span class="n">method</span><span class="p">(</span><span class="ss">:escape</span><span class="p">))</span><span class="o">.</span><span class="n">join</span><span class="si">}</span><span class="sr">] ){0}</span>
<span class="sr"> (?<term_operator_term> \g<term> \s* \g<operator> \s* \g<term> ){0}</span>
<span class="sr"> (?<term> \g<number> | \( \s* \g<term_operator_term> \s* \) ){0}</span>
<span class="sr"> \g<term_operator_term>(?=\s*\z|[^)])</span>
<span class="sr"> }x</span>
<span class="k">end</span>
<span class="no">NUMBER_REGEX</span> <span class="o">=</span> <span class="sr">%r{</span>
<span class="sr"> -? # an optional minus</span>
<span class="sr"> \d+ # an integer</span>
<span class="sr"> (?: \. \d+)? # an optional fractional bit</span>
<span class="sr">}x</span>
<span class="no">PREPARE_MULT_AND_DIV_REGEX</span> <span class="o">=</span> <span class="n">build_preparation_regex</span><span class="p">(</span><span class="no">NUMBER_REGEX</span><span class="p">,</span> <span class="sx">%w[* /]</span><span class="p">)</span>
<span class="no">PREPARE_ADD_AND_SUB_REGEX</span> <span class="o">=</span> <span class="n">build_preparation_regex</span><span class="p">(</span><span class="no">NUMBER_REGEX</span><span class="p">,</span> <span class="sx">%w[* / + -]</span><span class="p">)</span>
<span class="no">CHECK_REGEX</span> <span class="o">=</span> <span class="sr">%r{</span>
<span class="sr"> \A # the start of the expression</span>
<span class="sr"> (?<term> # a term, which is:</span>
<span class="sr"> </span><span class="si">#{</span><span class="no">NUMBER_REGEX</span><span class="si">}</span><span class="sr"> # a number</span>
<span class="sr"> | # or</span>
<span class="sr"> \( \s* # a parenthesized group of</span>
<span class="sr"> \g<term> # a term</span>
<span class="sr"> \s* [*/+\-] \s* # an operator</span>
<span class="sr"> \g<term> # and another term</span>
<span class="sr"> \s* \) # the end of the parenthesized group</span>
<span class="sr"> )</span>
<span class="sr"> \z # the end of the expression</span>
<span class="sr">}x</span>
<span class="no">MATH_REGEX</span> <span class="o">=</span> <span class="sr">%r{</span>
<span class="sr"> \( \s*</span>
<span class="sr"> (?<left> </span><span class="si">#{</span><span class="no">NUMBER_REGEX</span><span class="si">}</span><span class="sr"> )</span>
<span class="sr"> \s*</span>
<span class="sr"> (?<operator> [*/+\-] )</span>
<span class="sr"> \s*</span>
<span class="sr"> (?<right> </span><span class="si">#{</span><span class="no">NUMBER_REGEX</span><span class="si">}</span><span class="sr"> )</span>
<span class="sr"> \s* \)</span>
<span class="sr">}x</span>
<span class="n">verbose</span> <span class="o">=</span> <span class="no">ARGV</span><span class="o">.</span><span class="n">delete</span><span class="p">(</span><span class="s2">"-v"</span><span class="p">)</span>
<span class="n">problem</span> <span class="o">=</span> <span class="no">ARGV</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">strip</span> <span class="ow">or</span> <span class="nb">abort</span> <span class="s2">"USAGE: </span><span class="si">#{</span><span class="vg">$PROGRAM_NAME</span><span class="si">}</span><span class="s2"> MATH_EXPRESSION"</span>
<span class="n">steps</span> <span class="o">=</span> <span class="o">[</span> <span class="o">]</span>
<span class="o">[</span><span class="no">PREPARE_MULT_AND_DIV_REGEX</span><span class="p">,</span> <span class="no">PREPARE_ADD_AND_SUB_REGEX</span><span class="o">].</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">preparation</span><span class="o">|</span>
<span class="kp">loop</span> <span class="k">do</span>
<span class="n">steps</span> <span class="o"><<</span> <span class="n">problem</span><span class="o">.</span><span class="n">dup</span> <span class="k">if</span> <span class="n">verbose</span>
<span class="n">problem</span><span class="o">.</span><span class="n">sub!</span><span class="p">(</span><span class="n">preparation</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">term</span><span class="o">|</span> <span class="s2">"(</span><span class="si">#{</span><span class="n">term</span><span class="si">}</span><span class="s2">)"</span> <span class="p">}</span> <span class="ow">or</span> <span class="k">break</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">problem</span> <span class="o">=~</span> <span class="no">CHECK_REGEX</span> <span class="ow">or</span> <span class="nb">abort</span> <span class="s2">"Error: Invalid expression"</span>
<span class="n">solution</span> <span class="o">=</span> <span class="n">problem</span><span class="o">.</span><span class="n">dup</span>
<span class="kp">loop</span> <span class="k">do</span>
<span class="n">steps</span> <span class="o"><<</span> <span class="n">solution</span><span class="o">.</span><span class="n">dup</span> <span class="k">if</span> <span class="n">verbose</span>
<span class="n">solution</span><span class="o">.</span><span class="n">sub!</span><span class="p">(</span><span class="no">MATH_REGEX</span><span class="p">)</span> <span class="p">{</span>
<span class="vg">$~</span><span class="o">[</span><span class="ss">:left</span><span class="o">].</span><span class="n">to_f</span><span class="o">.</span><span class="n">public_send</span><span class="p">(</span><span class="vg">$~</span><span class="o">[</span><span class="ss">:operator</span><span class="o">]</span><span class="p">,</span> <span class="vg">$~</span><span class="o">[</span><span class="ss">:right</span><span class="o">].</span><span class="n">to_f</span><span class="p">)</span>
<span class="p">}</span> <span class="ow">or</span> <span class="k">break</span>
<span class="k">end</span>
<span class="nb">puts</span> <span class="n">steps</span><span class="o">.</span><span class="n">uniq</span><span class="o">[</span><span class="mi">0</span><span class="o">.</span><span class="n">.</span><span class="o">-</span><span class="mi">2</span><span class="o">]</span> <span class="k">if</span> <span class="n">verbose</span>
<span class="nb">puts</span> <span class="n">solution</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="sr">/\.0+\z/</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span>
</pre></div>
<pre><code># $ ruby math.rb '2 + 3 * 4 / 6 - 5 + 1'
# 0
# $ ruby math.rb -v '2 + 3 * 4 / 6 - 5 + 1'
# 2 + 3 * 4 / 6 - 5 + 1
# 2 + (3 * 4) / 6 - 5 + 1
# 2 + ((3 * 4) / 6) - 5 + 1
# (2 + ((3 * 4) / 6)) - 5 + 1
# ((2 + ((3 * 4) / 6)) - 5) + 1
# (((2 + ((3 * 4) / 6)) - 5) + 1)
# (((2 + (12.0 / 6)) - 5) + 1)
# (((2 + 2.0) - 5) + 1)
# ((4.0 - 5) + 1)
# (-1.0 + 1)
# 0
# $ ruby math.rb -v '-2.1 * 5'
# -2.1 * 5
# (-2.1 * 5)
# -10.5
# $ ruby math.rb -v '2 +'
# Error: Invalid expression
</code></pre>
James Edward Gray II