                        Listing Two


;------------------------------------------------------------------
;
;   Four-function calculator written with OmniMark pattern matching
;   rules.
;   Supports + - * / and () with correct precedence
;   Requires OmniMark 5.1 or later (free from
;       http://www.omnimark.com)
;
;   Run the program with the following comannd line:
;
;       omnimark -s dc.xom
;
;   Exit by typing "quit" or EOF (ctrl-Z in Windows, ctrl-D in Unix)
;
;------------------------------------------------------------------

declare #main-input has unbuffered
declare #main-output has unbuffered

;------------------------------------------------------------------

; integer stack, with stack operators

global counter Stack variable initial-size 0

declare catch StackUnderflow

define function push
(   value counter num   )
as
    set new Stack to num

define counter function pop
()
as
    local counter num
    throw StackUnderflow when number of Stack < 1   ;nothing to pop
    set num to Stack    
    remove Stack        ;discard top of stack
    return num

;------------------------------------------------------------------

; patterns and pattern matching functions

declare catch DivisionByZero

macro integer is
(   digit+  )
macro-end

define switch function expression
elsewhere                           ;forward definition

define switch function factor
as
    do scan #current-input
        match blank* "(" blank* expression blank* ")"
            ;do nothing
        match blank* integer => int
            push( int )
        else
            return false
    done
    return true

define switch function multiply-factor
as
    do scan #current-input
        match blank* "*" blank* factor
            push( pop() * pop() )
            return true
    done
    return false

define switch function divide-factor
as
    do scan #current-input
        match blank* "/" blank* factor
            local counter num
            set num to pop()
            throw DivisionByZero when num = 0
            push( pop() / num )
            return true
    done
    return false

define switch function r-term
as
    do scan #current-input
        match multiply-factor r-term
        match divide-factor r-term
    done
    ; if we match nothing else, we can match the empty string
    return true

define switch function term
as
    return #current-input matches ( factor r-term )

define switch function add-term
as
    do scan #current-input
        match blank* "+" term
            push( pop() + pop() )
            return true
    done
    return false

define switch function subtract-term
as
    do scan #current-input
        match blank* "-" term
            push( 0 - pop() + pop() )
            return true
    done
    return false

define switch function r-expression
as
    do scan #current-input
        match add-term r-expression
        match subtract-term r-expression
    done
    ; if we match nothing else, we can match the empty string
    return true

define switch function expression
as
    return #current-input matches ( term r-expression )

;------------------------------------------------------------------

; control loop

process
    repeat
        repeat scan #main-input
            match expression =>text blank* "%n"
                output "d" % pop() || "%n"
                put #error "Stack error: " || "d" % number of Stack
                            || "%n"
                    when number of Stack != 0
                clear Stack
            match blank* ul "quit"
                halt
            match blank* "%n"
            match   any-text+ =>text "%n"?
                put #error "Syntax error: %x(text)%n"
                clear Stack
        again
        halt
    catch DivisionByZero
        put #error "Divide by zero%n"
    catch StackUnderflow
        put #error "Stack underflow%n"
    always
        do scan #main-input
            match any-text* "%n"
        done
        clear Stack
    again

;------------------------------------------------------------------
