PLACE |

spec |

solutions |

Euphoria |

The program below consists of several functions and a following main part. The latter is a (nominally) infinite loop where we read lines `s`

from the standard input defice (`0`

), call `evalrpn`

to evaluate an RPN expression from `s`

and print the result or an error message. As we shall see, `evalrpn`

returns one of several possible kinds of values. If an atom, that must be the result of a successful evaluation – a number, which we print out by the `?`

command. A non-empty, non-numeric value would signify an error, and in such a case we act by printing `"error"`

. If `evalrpn`

returns an empty string, the input line was empty (or only contained blanks), so we do nothing there.

We have to use the `puts`

function for printing a string on the standard output (`1`

) and not `?`

, as the latter prints the string contents as integers instead of the string proper.

The program ends by `exit`

ing the loop upon receiving `-1`

– an atom – instead of a string from `gets`

, the `-1`

signaling end-of-input. Note that `puts`

is called after each line reading to print an empty line. This is confusing but, by definition in Euphoria, it is the only way to ensure proper interleaving of input and output lines. (No actual empty lines are added to the output because of this; the program behaves just as required.)

Evaluating the expression within `evalrpn`

takes place by reading tokens from the string `s`

and maintaining a stack `st`

where the intermediate results go. After reading a token, it gets assigned to `tk`

, while the corresponding initial sub-string is removed from `s`

. If `tk`

is non-empty, we check whether it is an operator. If so, and if the stack holds at least two arguments for that operation, we perform it and store the result back on the stack top. If, instead, `tk`

appears to parse as a number, the value of that number is obtained and pushed onto `st`

. (The function `value`

, which does the actual extracting from `tk`

, returns a two-item sequence where the value we are interested in is the second entry. The library file `get.e`

is `include`

d in the program to give access to the `value`

function.)

When there are no more tokens in `s`

, `gettoken`

returns an empty string. Then, if there is only one number on the stack, that number is returned. We actually return `x`

, making use of the fact that `x`

is always equal to the stack top. On having more than one items on the stack, the stack itself is returned, i.e. a value of type *sequence*. Other errors, namely a token being neither an operator nor a number, as well as an operation missing one or two of its arguments, are dealt with by returning the string `"e"`

.

The function `number`

is used for testing whether a token represents a number. It removes from `tk`

a possible arithmetic sign, and then similarly removes a decimal dot if present. The remaining string must be non-empty, and consisting of digits. If this is not so, the token was not a number.

Note that `number`

does not have to really compute the number from the string, only to check if it parses correctly. For the rest we use `value`

. The latter, on the other hand, is not good at parsing, as it would have been satisfied by any initial substring of the token that reads as a number, and there is no way to know if there would have been left a remainder of the token that is not parsed by `value`

. That is why we need both `number`

and `value`

.

The function `number`

exemplifies a specific feature of the language. As seen, it is designated by the word `type`

instead of `function`

. In Euphoria, a *type* is a one-argument, Boolean-valued function, i.e. one with regard to the result of which we only distinguish between non-zero (true) and zero (false), and it is assumed that if a non-zero is returned for a certain value as an argument, that value has the type named by the function. Therefore, we have a type `number`

defined here.

This is Euphoria's mechanism for user-defined types. A type's name can be used in variable declarations or as a predicate to typecheck a value, and thus user-defined datatypes have equal power to that of the predefined types. We only use `number`

as a predicate in this program.

include get.e object s,r function gettoken() integer i,j i = 1 while s[i]=' ' or s[i]='\t' do i += 1 end while j = i while 0=find(s[j]," \t\n") do j += 1 end while r = s[i..j-1] s = s[j..$] return r end function type number(sequence tk) integer i if tk[1]='+' or tk[1]='-' then tk = tk[2..$] end if i = find('.',tk) if i then tk = tk[1..i-1]&tk[i+1..$] end if i = length(tk) if i=0 then return 0 end if while i and find(tk[i],"0123456789") do i -= 1 end while return i=0 end type function evalrpn() sequence tk,st object x,y,o st = {} while 1 do tk = gettoken() if equal(tk,"") then if length(st)=1 then return x else return st end if end if o = tk[1] if length(tk)=1 and find(o,"+-*/") then if length(st)<2 then return "e" end if x = st[$-1] y = st[$] if o='+' then x += y elsif o='-' then x -= y elsif o='*' then x *= y else x /= y end if st = st[1..$-1] st[$] = x elsif number(tk) then x = value(tk) x = x[2] st = st&x else return "e" end if end while end function while 1 do s = gets(0) puts(1,"\n") if atom(s) then exit end if r = evalrpn() if atom(r) then ?r elsif not equal(r,"") then puts(1,"error\n") end if end while

boykobbatgmaildotcom