Historical difference between `/` and `÷` in mathematical expressions

R, 68 66 bytes

function(x,y,`=`=`/`)eval(parse(t=`if`(y<1918,x,gsub('=','/',x))))

Try it online!

Expects equality sign = instead of ÷ and * instead of ×.

The code makes use of some nasty operator overloading, making advantage of the fact that = is a right-to-left operator with very low precedence (the exact behavior that we want from pre-1918 ÷), and R retains its original precedence when it is overloaded. The rest is automatically done for us by eval.

As a bonus, here is the same exact approach implemented in terser syntax. This time our special division operator is tilde (~):

Julia 0.7, 51 bytes

~=/;f(x,y)=eval(parse(y<1918?x:replace(x,'~','/')))

Try it online!


JavaScript (ES6),  130 129  120 bytes

Saved 9 bytes thanks to @ScottHamper

Takes input as (year)(expr). Expects % and * instead of ÷ and ×.

y=>g=e=>(e!=(e=e.replace(/\([^()]*\)/,h=e=>eval(e.split`%`.reduceRight((a,c)=>y<1918?`(${c})/(${a})`:c+'/'+a))))?g:h)(e)

Try it online!

How?

Processing leaf expressions

The helper function \$h\$ expects a leaf expression \$e\$ as input, processes all % symbols according to the rules of the year \$y\$ (defined in the parent scope) and evaluates the resulting string.

If \$y<1918\$, we transform X%Y into (X)/(Y), to enforce low precedence and repeat this process for the entire string from right to left to enforce right-to-left associativity.

Examples:

  • 8%2 becomes (8)/(2), whose simplified form is 8/2
  • 2+3%3+2 becomes (2+3)/(3+2)
  • 8%2%2 becomes (8)/((2)/(2)), whose simplified form is 8/(2/2)

If \$y\ge 1918\$, each % is simply turned into a /.

h = e =>                    // e = input string
  eval(                     // evaluate as JS code:
    e.split`%`              //   split e on '%'
    .reduceRight((a, c) =>  //   for each element 'c', starting from the right and
                            //   using 'a' as the accumulator:
      y < 1918 ?            //     if y is less than 1918:
        `(${c})/(${a})`     //       transform 'X%Y' into '(X)/(Y)'
      :                     //     else:
        c + '/' + a         //       just replace '%' with '/'
    )                       //   end of reduceRight()
  )                         // end of eval()

Dealing with nested expressions

As mentioned above, the function \$h\$ is designed to operate on a leaf expression, i.e. an expression without any other sub-expression enclosed in parentheses.

That's why we use the helper function \$g\$ to recursively identify and process such leaf expressions.

g = e => (            // e = input
  e !=                // compare the current expression with
    ( e = e.replace(  // the updated expression where:
        /\([^()]*\)/, //   each leaf expression '(A)'
        h             //   is processed with h
      )               // end of replace()
    ) ?               // if the new expression is different from the original one:
      g               //   do a recursive call to g
    :                 // else:
      h               //   invoke h on the final string
)(e)                  // invoke either g(e) or h(e)

Python 3.8 (pre-release), 324 310 306 bytes

lambda s,y:eval((g(s*(y<1918))or s).replace('%','/'))
def g(s):
 if'%'not in s:return s
 l=r=j=J=i=s.find('%');x=y=0
 while j>-1and(x:=x+~-')('.find(s[j])%3-1)>-1:l=[l,j][x<1];j-=1
 while s[J:]and(y:=y+~-'()'.find(s[J])%3-1)>-1:r=[r,J+1][y<1];J+=1
 return g(s[:l]+'('+g(s[l:i])+')/('+g(s[i+1:r])+')'+s[r:])

Try it online!

Takes % instead of ÷ and * instead of ×