如何在Rebol中评估带有PARSE的DSL?

时间:2014-09-10 14:17:46

标签: dsl rebol

当我学到一些DSL时,我意识到Rebol中的Parse方言可以成为一个伟大的词法分析器和解析器。 the Parse tutorial有一个很好的例子:

    expr:    [term ["+" | "-"] expr | term]
    term:    [factor ["*" | "/"] term | factor]
    factor:  [primary "**" factor | primary]
    primary: [some digit | "(" expr ")"]
    digit:   charset "0123456789"

    probe parse "4/5+3**2-(5*6+1)" expr
    ;will output true

上面的代码验证表达式是否符合上面定义的“语法”。我的问题是:

  1. 如何计算或评估它?
  2. 如何表示“*”和“+”等运算符的优先级?

3 个答案:

答案 0 :(得分:2)

Boyko Bantchev修改版code

arith-eval: funct [
    a-exp [string!]
    ] [
    op-stack: copy []  
    num-stack: copy []
    pop: func [stk /local t] [t: last stk   remove back tail stk t]
    do.op: func [/local op x y] [
        op: to-word pop op-stack  
        y: pop num-stack  
        x: pop num-stack
        append num-stack do reduce [x op y]
    ]
    num: op: none
    expr: [term any [copy op [{+} | {-}] (append op-stack op) term (do.op)]]
    term: [prim any [copy op [{*} | {/}] (append op-stack op) prim (do.op)]]
    prim: [copy num some digit (append num-stack to-decimal num) | {(} expr {)}]
    digit: charset {0123456789}
    ; whitespace is not allowed between tokens

    either parse a-exp expr  [num-stack/1] [{wrong expression}]
]



>> arith-eval "12+34*56-89/2"
== 1871.5
>>

答案 1 :(得分:1)

1,您生成字符串或更好的块,例如with collect,你可以用do评估。
2,Gabriele Santilli on rebol.org中有一个带有运算符优先级的旧示例方言。

答案 2 :(得分:1)

请参阅下文,了解良好的订购解析规则。

REBOL []

math-expression?: func
[   {Returns a block of rebol code for a given mathematical expression string,
     or none if mathematical expression is not correct.

     >> e: math-expression? "1-2**3*6"
     == [subtract 1.0 multiply power 2.0 3.0 6.0]
     >> do e
     == -47.0

     >> x: 0
     == 0
     >> e: math-expression? "sqrt(1+1)/(1-x)**3"
     == [divide square-root add 1.0 1.0 power subtract 1.0 x 3.0]
     >> do e
     == 1.4142135623731

    }

    Amath-expression  {The mathematical expression to convert to REBOL code.}

    /trace 'Rtrace  {Set Rtrace from the syntax error to the mathematical expression end.}

    /local
        exprs fx expr text
        digit number parameter primary factor term expression
]
[   exprs: copy []
    fx: copy []
    append/only exprs expr: copy []

    digit: charset "0123456789"
    number: [some digit]

    parameter: charset "abcdefghijklmnopqrstuvwxyz"

    primary:
    [   opt
        [   trace: "abs"    (fx: copy [abs])
        |   trace: "acos"   (fx: copy [arccosine/radians])
        |   trace: "arccos" (fx: copy [arccosine/radians])
        |   trace: "arcsin" (fx: copy [arcsine/radians])
        |   trace: "arctan" (fx: copy [arctangent/radians])
        |   trace: "asin"   (fx: copy [arcsine/radians])
        |   trace: "atan"   (fx: copy [arctangent/radians])
        |   trace: "cos"    (fx: copy [cosine/radians])
        |   trace: "exp"    (fx: copy [exp])
        |   trace: "ln"     (fx: copy [log-e])
        |   trace: "log2"   (fx: copy [log-2])
        |   trace: "log10"  (fx: copy [log-10])
        |   trace: "sin"    (fx: copy [sine/radians])
        |   trace: "sqrt"   (fx: copy [square-root])
        |   trace: "tan"    (fx: copy [tangent/radians])
        ]
        trace: "("
        (   append/only expr fx
            fx: copy []
            append/only  exprs  expr: copy []
        )
        expression
        trace: ")"
        (   insert  head expr  last  second-to-last exprs
            remove  back tail  second-to-last exprs

            temp: tail  second-to-last exprs

            expr: append  second-to-last exprs  head expr
            remove  back tail exprs

            expr: temp
        )
    |   trace: "+" expression
    |   trace: "-" (append expr 'negate) expression
    |   trace: copy text number (append expr to decimal! trim/all text)  ; (probe head exprs)
    |   trace: copy text parameter (append expr to word! trim/all text)
    ]

    factor:
    [   primary any
        [   [   trace: "**" (insert expr 'power)
            |   trace: "^^" (insert expr 'power)
            |   trace: "//" (insert expr 'remainder)
            ]
            primary
        ]
    ]

    term:
    [   factor any
        [   [   trace: "*" (insert expr 'multiply)
            |   trace: "/" (insert expr 'divide)
            ]
            factor
        ]
    ]

    expression:
    [   term any
        [   [   trace: "+" (expr: tail  insert head expr 'add)
            |   trace: "-" (expr: tail  insert head expr 'subtract)
            ]
            term
        ]
    ]

    either parse Amath-expression expression
    [   first exprs
    ]
    [   if Rtrace [set Rtrace copy back trace]
        none
    ]
]


a: b: i: x: y: 0

foreach e
[   "1 * (2 * (3 - sqrt(4 * sqrt(16)) + 5))"

    "-1"
    "+1"
    "1 + 2 + 3"
    "1 + 2 * 3"
    "1 * 2 + 4 * 5"
    "(1 + 2) * 3"
    "-((1 + 2) * 3)"
    "3 * (1 + 2)"

    "SQRT(1-x**2)/(1-x)**3"
    "3*(3+x)**sin(2-i)+cos((3*y)**3*(a-b))+1"
    "+(1+-x)**-sqrt(-1--x)"

    "3-2+1"
    "4/2*2"
    "2**3*6"
    "1-2**3*6"
    "4/5+3**2-(5*6+1)"
    "sqrt(3*3)"

    "1-x**3"
    "(1-x)**3"
    "(1+1)/(1-x)**3"
    "sqrt(1+1)/(1-x)**3"
    "-(1+1)/(1-x)**3"

    "+(1+2)/-(1-2)"

    "1 - 2"
    "-(1 + 2)"
    "-1/2"
    "(-1)/2"
    "-(1)/2"
]
[   print ""
    print e
    probe e: math-expression? e
    prin "= "probe either error? err: try [e: first reduce e] [disarm err] [e]
  ; break
]

print ""

probe m: "2*(1*1+1))"
probe math-expression? m
probe math-expression?/trace m t: copy ""
probe t

probe m: "1+abs(-1"
probe math-expression? m
probe math-expression?/trace m t: copy ""
probe t

probe m: "1+2+"
probe math-expression? m
probe math-expression?/trace m t: copy ""
probe t