Evaluating a maths expression in a string?
[1/14] from: mat:eurogamer at: 29-Dec-2000 17:21
Hello,
I was just thinking of adding another function to my IRC bot. A
mathematical evaluation expression.
The question is, if I get a string which contains a mathematical
expression - how would you evaluate this and get the result?
Of course some form of error? try [] would be involved in case
something went wrong...
--
Mat Bettinson - EuroGamer's Gaming Evangelist with a Goatee
http://www.eurogamer.net | http://www.eurogamer-network.com
[2/14] from: rchristiansen:pop:isdfa:sei-it at: 29-Dec-2000 12:34
Here is something to get you started...
>> expression-string: "2 + 2"
== "2 + 2"
>> do load expression-string
== 4
[3/14] from: larry:ecotope at: 29-Dec-2000 10:48
Hi Mat
> The question is, if I get a string which contains a mathematical
> expression - how would you evaluate this and get the result?
Here is one way:
>> str: "3 + 4"
== "3 + 4"
>> either not error? try[result: do str][result]["code to handle error"]
== 7
>>
HTH
-Larry
[4/14] from: mat:eurogamer at: 29-Dec-2000 18:52
Heya Ryan,
RCC> Here is something to get you started...
>>> expression-string: "2 + 2"
RCC> == "2 + 2"
>>> do load expression-string
RCC> == 4
Mmm quite obvious really.
expression-string: "2 / 0"
either error? try [expresult: do load expression-string] [
Print "Bum expression man, get some skillz!"
][
Print rejoin ["The result was: " expresult]
]
Sweet!
--
Mat Bettinson - EuroGamer's Gaming Evangelist with a Goatee
http://www.eurogamer.net | http://www.eurogamer-network.com
[5/14] from: mat:eurogamer at: 29-Dec-2000 19:09
Heya Mat,
MB> Mmm quite obvious really.
MB> expression-string: "2 / 0"
Next stage, it's natural for folks to enter expressions without white
spaces. As rebol is a somewhat human illogical white-space sensitive
language - this must be worked around.
I was thinking, the easiest hack might be to replace +,-,* and / with
+
, " - ", " * " etc
Sure it's going to result in redundant spaces if they DO put in spaces
but it should work no?
--
Mat Bettinson - EuroGamer's Gaming Evangelist with a Goatee
http://www.eurogamer.net | http://www.eurogamer-network.com
[6/14] from: mat:eurogamer at: 29-Dec-2000 19:17
Heya Larry,
>>> either not error? try[result: do str][result]["code to handle error"]
I've just realised the fatal flaw with this approach. Since I allow
user input to my evaluation string - there's nothing to stop them
typing out rebol code.
Security issue to say the least!
I suppose I could filter to make sure there are only characters that
fit a character set of "0123456789+-*/()" - that might do it?
--
Mat Bettinson - EuroGamer's Gaming Evangelist with a Goatee
http://www.eurogamer.net | http://www.eurogamer-network.com
[7/14] from: al::bri::xtra::co::nz at: 30-Dec-2000 8:24
> I was thinking, the easiest hack might be to replace +,-,* and / with " +
,
- ", " * " etc
> Sure it's going to result in redundant spaces if they DO put in spaces but
it should work no?
You might have a problem with signed numbers:
1--1-+1
would come out as:
1 - - 1 - + 1
which is illegal.
IIRC, Gabriele had a maths parser which could be able to handle this
problem.
Andrew Martin
ICQ: 26227169 http://members.nbci.com/AndrewMartin/
[8/14] from: al:bri:xtra at: 30-Dec-2000 8:42
Mat wrote:
> I've just realised the fatal flaw with this approach. Since I allow user
input to my evaluation string - there's nothing to stop them typing out
rebol code.
> Security issue to say the least!
>
> I suppose I could filter to make sure there are only characters that fit a
character set of "0123456789+-*/()" - that might do it?
>> str: "1 + 2 print mold 12"
== "1 + 2 print mold 12"
>> context: make object! [print: func [x][system/words/print join "test" x
9]]
>> probe context
make object! [
print: func [x][system/words/print join "test" x 9]
]
>> either not error? try[result: do bind load str in context
'self][result][probe disarm Result]
test12
== 9
Note that 'context can include all the Rebol words you wouldn't want J.
Random Cracker to be able to access, for example, 'rebol, 'system, 'set,
'get, and 'probe.
I hope that helps!
Andrew Martin
ICQ: 26227169 http://members.nbci.com/AndrewMartin/
[9/14] from: mike:myers:cybarite at: 29-Dec-2000 15:25
Following is a calculator in Rebol/View that encountered some of the same challenges
discussed in this thread - evaluating mathematical strings,
negating, enforcing the separation of tokens with spaces. It might be of interest ....
or not.
It has been submitted to the [script-master--rebol--com] for consideration (... or not)
[mike--myers--cybarite--com]
-- Binary/unsupported file stripped by Listar --
-- Type: application/octet-stream
-- File: calculator.r
[10/14] from: mat:eurogamer at: 29-Dec-2000 20:41
Heya Mike,
MM> Following is a calculator in Rebol/View that encountered some of the same challenges
discussed in this thread - evaluating mathematical strings,
MM> negating, enforcing the separation of tokens with spaces. It might be of interest
.... or not.
I'm sure it will be! Could you just give us a URL for that, Listar ate
the file attach.
--
Mat Bettinson - EuroGamer's Gaming Evangelist with a Goatee
http://www.eurogamer.net | http://www.eurogamer-network.com
[11/14] from: larry:ecotope at: 29-Dec-2000 13:05
Hi Andrew
Just a quick comment. You wrote:
> >> context: make object! [print: func [x][system/words/print join "test" x
> 9]]
In the recent REBOL builds, this code overwrites the built-in function
CONTEXT which is heavily used in VID.
>> help context
USAGE:
CONTEXT blk
DESCRIPTION:
Defines a unique (underived) object.
CONTEXT is a function value.
ARGUMENTS:
blk -- Object variables and values. (Type: block)
>>
Cheers
-Larry
[12/14] from: mike:myers:cybarite at: 29-Dec-2000 18:34
>I'm sure it will be! Could you just give us a URL for that, Listar ate
>the file attach.
Ouch! Here is the source clipped into mail.
========================================================================================================================
REBOL [
Title: "Calculator"
File: %calculator.r
Date: 11-December-2000
Author: [mike--myers--cybarite--com]
Purpose: {Simple numeric calculator using VID Layout.
Based on January 2000 REBOL/View Face Calculator by Carl Sassenrath. Adds memory ability.}
]
memory: 0
set-memory: func [aValue] [
memory: aValue
either aValue = 0
[memory-recall/edge/color: memory-recall-edge-default]
[memory-recall/edge/color: green]
show memory-recall
]
evaluated?: false ; determines whether the calculation is clearing an old one or appending
to it.
evaluate: func [] [
evaluated?: true
if error? try [
text-box/text: form evaluation: do text-box/text
][ text-box/text: "Error"
evaluation: 0
]
show text-box
return evaluation
]
cell-size: 32x32 ; use one cell size and base all faces on that size via pair arithmetic
face-size: cell-size - 4x4 ; calculator buttons size is less than cell size to give
a border
button-size: face-size * 2x1 + 4x0 ; standard buttons will be twice as wide as the
calculator buttons
text-box-size: cell-size * 5x1 - 4x8 ; the text box will be 5 columns wide
page-size: cell-size * 7x10 ; the page will hold 7 columns and 10 rows
calculator-styles: stylize [
operator-button: button face-size gray [
evaluated?: false
append-entry rejoin [" " face/text " "]]
number-button: button face-size gray [
if evaluated? [text-box/text: copy ""]
evaluated?: false
append-entry face/text]
]
append-entry: func [entry [string!]] [
append text-box/text entry
show text-box
]
negate-text: func [] [
either "-" = pick text-box/text 1
[text-box/text: skip head text-box/text 1]
[text-box/text: rejoin ["-" text-box/text]]
]
calculator-layout: layout [size page-size
styles calculator-styles
across backdrop gray
at cell-size * 1x1 text-box: field "" right text-box-size
at cell-size * 1x3 operator-button "(" #"("
at cell-size * 2x3 operator-button ")" #")"
return
at cell-size * 3x3 arrow face-size gray left keycode [left #"^(back)"] ; backspace one
character
[text-box/text: head clear back tail text-box/text show text-box]
at cell-size * 4x3 number-button "C"
keycode [#"C" #"c"]
[clear text-box/text show text-box]
at cell-size * 5x3 operator-button "MC" [set-memory 0]
at cell-size * 1x4 number-button "7" #"7"
at cell-size * 2x4 number-button "8" #"8"
at cell-size * 3x4 number-button "9" #"9"
at cell-size * 4x4 operator-button "/" #"/"
at cell-size * 5x4 memory-recall: operator-button "MR" [append-entry form memory]
at cell-size * 1x5 number-button "4" #"4"
at cell-size * 2x5 number-button "5" #"5"
at cell-size * 3x5 number-button "6" #"6"
at cell-size * 4x5 operator-button "*" #"*"
at cell-size * 5x5 operator-button "MS" [set-memory evaluate]
at cell-size * 1x6 number-button "1" #"1"
at cell-size * 2x6 number-button "2" #"2"
at cell-size * 3x6 number-button "3" #"3"
at cell-size * 4x6 operator-button "-" #"-"
at cell-size * 5x6 operator-button "M+" [set-memory memory + evaluate]
at cell-size * 1x7 number-button "0" #"0"
at cell-size * 2x7 number-button "+/-"
[negate-text show text-box]
at cell-size * 3x7 number-button "." #"."
at cell-size * 4x7 operator-button "+" #"+"
at cell-size * 5x7 operator-button "=" keycode [#"=" #"^M"] [
evaluate]
at cell-size * 1x9 button button-size "About" [view/new/offset about 10x10]
at cell-size * 4x9 button button-size "Done" [unview/only calculator-layout]
]
about: layout [
backdrop black
size 500x280
across return title "About"
return text rejoin ["Title: " form system/script/header/title]
return text rejoin ["Author: " form system/script/header/author]
return text rejoin ["Purpose: " form system/script/header/purpose]
return text {Enter numbers or operators shown using keyboard or mouse clicks. }
return text red {Warning - Brackets are needed for precedence of arithmetic operations.}
return text red {Otherwise 2 + 3 * 5 = 25. Use 2 + (3 * 5) = 17}
return button "OK" keycode [#"^(ESC)" #" "] [unview]
]
memory-recall-edge-default: memory-recall/edge/color
view calculator-layout
[mike--myers--cybarite--com]
[13/14] from: g:santilli:tiscalinet:it at: 29-Dec-2000 20:47
Hello Andrew!
On 29-Dic-00, you wrote:
AM> IIRC, Gabriele had a maths parser which could be able to
AM> handle this problem.
It parses blocks only, but it can be adapted to parse strings. I
hope not to annoy anyone if I repost it here.
Regards,
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
REBOL [
Title: "Expression evaluator"
Author: "Gabriele Santilli <[giesse--writeme--com]>"
Comment: {
Evaluates expressions taking usual operator precedence
into account.
1. (...)
2. - [negation], ! [factorial]
3. ^ [power]
4. *, /
5. +, - [subtraction]
}
]
; A simple iterative implementation; returns 1 for negative
; numbers. FEEL FREE TO IMPROVE THIS!
factorial: func [n [integer!] /local res] [
if n < 2 [return 1]
res: 1
; should avoid doing the loop for i = 1...
repeat i n [res: res * i]
]
expression-parser: make object! [
slash: to-lit-word first [ / ]
expr-val:
expr-op: none
expression: [
term (expr-val: term-val)
any [
['+ (expr-op: 'add) | '- (expr-op: 'subtract)]
term (expr-val: compose [(expr-op) (expr-val) (term-val)])
]
]
term-val:
term-op: none
term: [
pow (term-val: power-val)
any [
['* (term-op: 'multiply) | slash (term-op: 'divide)]
pow (term-val: compose [(term-op) (term-val) (power-val)])
]
]
power-val: none
pow: [
unary (power-val: unary-val)
opt ['^ unary (power-val: compose [power (power-val) (unary-val)])]
]
unary-val:
pre-uop:
post-uop: none
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: none
; WARNING: uses recursion for parens.
primary: [
set prim-val [number! | word!]
| set prim-val paren! (prim-val: translate to-block :prim-val)
]
translate: func [expr [block!] /local res recursion] [
; to allow recursive calling, we need to preserve our state
recursion: reduce [
:expr-val :expr-op :term-val :term-op :power-val :unary-val
:pre-uop :post-uop :prim-val
]
res: if parse expr expression [expr-val]
set [
expr-val expr-op term-val term-op power-val unary-val
pre-uop post-uop prim-val
] recursion
res
]
set 'eval func [expr [block!] /translate] [
expr: self/translate expr
either translate [expr] [do expr]
]
]
[14/14] from: al:bri:xtra at: 31-Dec-2000 6:45
Larry reminded:
> In the recent REBOL builds, this code overwrites the built-in function
CONTEXT which is heavily used in VID.
Normally, these functions reside inside an object. In this example, my eText
object (which translates normal text into HTML). Naturally, always enclose
your variables in a context of their own, to avoid overwriting Rebol words.
Andrew Martin
ICQ: 26227169 http://members.nbci.com/AndrewMartin/