It is basically inevitable that people coming to Rebol will ask about its mathematical evaluation being left-to-right, instead of obeying the precedence order that they are used to. Red just had a new user try to file it as a bug... 3 days ago:
https://github.com/red/red/issues/5276
What seemed to make sense to people like me and BrianH was that the core ship with a dialect called MATH. The concept was that math [1 + 2 * 3] would give 7 and not 9. Having something in the box seemed better than having the first line of argument being "you don't want the precedence you think you do".
But it turned out to be kind of hard to make pleasing. One key difficulty which @Brett and I fretted over at some point was that since Rebol isn't "psychic" regarding arity, does not know a-priori how much code an expression will consume:
math [1 + 2 * foo baz bar + 3]
; should it be...
[1 + (2 * (foo baz bar)) + 3]
; or perhaps...
[(1 + (2 * foo)) ((baz bar) + 3)]
; maybe...
[(1 + (2 * foo)) baz (bar + 3)]
It started to appear that the user would have to put anything that wasn't a number or a math operator in groups. There may be some heuristics which tolerate words that look up to numbers vs. functions, but it feels very slippery.
As I've said I hate to be dropping things, but MATH is something that Rebol programmers don't really want in the first place--and I don't think non-Rebol programmers would be satisfied by it. If it's included in the core that suggests support for it, and there are just too many things in play.
Here is an implementation that was previously included, by Gabriele:
; This MATH implementation is from Gabrielle Santilli circa 2001, found
; via http://www.rebol.org/ml-display-thread.r?m=rmlXJHS. It implements the
; much-requested (by new users) idea of * and / running before + and - in
; math expressions. Expanded to include functions.
;
math: func [
{Process expression taking "usual" operator precedence into account.}
expr [block!]
{Block to evaluate}
/only
{Translate operators to their prefix calls, but don't execute}
; !!! This creation of static rules helps avoid creating those rules
; every time, but has the problem that the references to what should
; be locals are bound to statics as well (e.g. everything below which
; is assigned with BLANK! really should be relatively bound to the
; function, so that it will refer to the specific call.) It's not
; technically obvious how to do that, not the least of the problem is
; that statics are currently a usermode feature...and injecting relative
; binding information into something that's not the function body itself
; isn't implemented.
<static>
slash (the /)
expr-val (_)
expr-op (_)
expression ([
term (expr-val: term-val)
opt some [
['+ (expr-op: 'add) | '- (expr-op: 'subtract)]
term (expr-val: compose [(expr-op) (expr-val) (term-val)])
]
<end>
])
term-val (_)
term-op (_)
term ([
pow (term-val: power-val)
opt some [
['* (term-op: 'multiply) | slash (term-op: 'divide)]
pow (term-val: compose [(term-op) (term-val) (power-val)])
]
])
power-val (_)
pow ([
unary (power-val: unary-val)
opt ['** unary (power-val: compose [power (power-val) (unary-val)])]
])
unary-val (_)
pre-uop (_)
post-uop (_)
unary ([
(post-uop: pre-uop: [])
opt ['- (pre-uop: 'negate)]
primary
opt ['! (post-uop: 'factorial)]
(unary-val: compose [(post-uop) (pre-uop) (prim-val)])
])
prim-val (_)
primary ([
set prim-val any-number!
| set prim-val [word! | path!] (prim-val: reduce [prim-val])
; might be a funtion call, looking for arguments
opt some [
nested-expression (append prim-val take nested-expr-val)
]
| ahead group! into nested-expression (prim-val: take nested-expr-val)
])
p-recursion (_)
nested-expr-val ([])
save-vars (func [][
p-recursion: reduce [
:p-recursion :expr-val :expr-op :term-val :term-op :power-val :unary-val
:pre-uop :post-uop :prim-val
]
])
restore-vars (func [][
set [
p-recursion expr-val expr-op term-val term-op power-val unary-val
pre-uop post-uop prim-val
] p-recursion
])
nested-expression ([
;all of the static variables have to be saved
(save-vars)
expression
(
; This rule can be recursively called as well,
; so result has to be passed via a stack
insert nested-expr-val expr-val
restore-vars
)
; vars could be changed even it failed, so restore them and fail
| (restore-vars) fail
])
][
clear nested-expr-val
let res: either parse3 expr expression [expr-val] [blank]
either only [
return res
][
ret: reduce res
all [
1 = length of ret
any-number? ret.1
] else [
fail [
unspaced ["Cannot be REDUCED to a number (" mold ret ")"]
":" mold res
]
]
return ret.1
]
]