Mailing List Archive: 49091 messages

# 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"
== 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"
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/