Mailing List Archive: 49091 messages
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

[REBOL] Expression evaluator (an example of parsing blocks)

From: g:santilli:tiscalinet:it at: 14-Oct-2000 13:12

Hi all! Have you been sometimes annoyed by tha fact REBOL does not support operator precedence? Here's the answer:
>> a: 2 b: 4 c: 3
== 3
>> eval [b ^ 2 - 4 * a * c]
== -8 The magic trick is:
>> eval/translate [b ^ 2 - 4 * a * c]
== [subtract power b 2 multiply multiply 4 a c] Other examples:
>> eval/translate [1 / k !]
== [divide 1 factorial k]
>> eval/translate [a * - b]
== [multiply a negate b]
>> eval/translate [e ^ (i / h * (p * x - E * t))]
== [power e multiply divide i h subtract multiply p x multiply E t] NOTE: it still needs improvements, such as error handling. They are "left to the reader as an exercise". :-) Enjoy!, 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] ] ]