PLACE |
spec |
solutions |
Pascal |
As with all the other implementations on this site, the one here is done in a strictly standard language. This makes quite a difference in the case of Pascal. For one thing, Standard Pascal has no string manipulation facilities whatsoever, so (scanning and) parsing of the input has to be done from the ground up.
Most other implementations read a line of input entirely before processing it further. This has the advantage of having to read at only one point in the program, which results in clean separation from the rest of the work. However, there is no provision for reading arbitrarily long lines in Pascal, and implementing it is awkward. In fact, the same holds with regard even to single tokens – the language only allows statically sized arrays, and the problem specification forbids explicit size limitations. Therefore, in the program below, we only read a single character at a time, skipping whitespace and recognizing and processing individual tokens as we go. If a token is an arithmetic operator, it is immediately executed, and if it is a number, its value is accumulated from its representation character by character. Incorrect tokens and other parsing errors are detected at appropriate places, which sometimes leads to reading to the end of line just to discard the input. Thus, no buffer is needed to store an input line or even a token, but reading is scattered at various points along the program.
A stack of numbers is maintained for keeping the partial results in the course of computing an expression.
The main program, at the end of the text below, runs a loop controlled by calling the built-in function eof
(end-of-file) to detect end of input. In the loop, when not immediately at end-of-line (eoln
), i.e. for a non-empty line, processtoken
is called repeatedly to read a number or an arithmetic operator and perform the corresponding action with it on the stack. Either an entire RPN expression is computed this way, or the error
flag is set due to processtoken
finding a syntax error. In the latter case the current line is read up to its end and discarded.
If a line is successfully read and processed, a number, z
, is popped off and printed as the result of computing the expression, provided there are no other numbers stored in the stack (nums = nil
) at that time. When there are, however, the expression is incorrect, so the error
flag is set. If an error is signaled one way or another, a corresponding message is printed.
Finally, in preparation for processing the next line of input, readln
and init
are called. readln
moves the point of reading to the beginning of the next line, while init
purges any possible items from the stack, clears the error
flag, and moves to the beginning of the first token. Moving, i.e. skipping whitespace, takes place by calling skipws
. This is also done within processtoken
for advancing between tokens.
The processtoken
procedure starts parsing a token by reading a character to see if it is an operator. An additional character may be read in order to discriminate between a +
or a -
being indeed an operator or the arithmetic sign of a number: a nonblank character indicates a no-operator case. If a token fails to read as an operator, the nested function getnumber
is called, which either succeeds in reading a number or sets the error
flag. If processtoken
eventually finds the current token to be an operator or a number, it proceeds by using that token to advance the computation of the RPN expression accordingly: if an operation, it is performed on the top two stack items, and if a number, it is simply pushed onto the stack.
The stack is implemented as a linked list of a user-defined type num
. Throughout the program the head of the list, which is the top of the stack, is held in the variable nums
: a pointer to num
(of type pnum
). The two procedures push
and pop
add to and pull items off the stack. When the stack is empty, pop
returns 0 – an arbitrary value – but sets error
, so that it is known that the returned value is insignificant.
Pascal
's input/output is peculiar by making use of buffer variables associated with each file, not excluding the standard input and output streams. For an input file, the buffer variable, in our case input^
, takes its value by forward (hidden, implicit) reading. That value is set even before any actual call of read
is made. read
returns the current value of the buffer variable and obtains a new one, which will be returned by a subsequent call of the same procedure. A call of get
only obtains the next value of the buffer variable.
End-of-line is not a character value – another peculiarity of Pascal – so instead of it reading operations see a space. It should be noted that both forward reading and equating the end-of-line to a space are used in the program.
program rpncalc(input,output); type pnum = ^num; num = record x: real; next: pnum end; var nums: pnum; digits: packed array [1..10] of char; tktype: (nm,ad,sb,mp,dv); error: boolean; z: real; procedure push(x: real); var p: pnum; begin new(p); p^.x := x; p^.next := nums; nums := p end; function pop: real; var p: pnum; begin if nums <> nil then begin p := nums; nums := nums^.next; pop := p^.x; dispose(p) end else begin error := true; pop := 0 end end; procedure skipws; begin while (input^ = ' ') and (not eoln) do get(input) end; procedure processtoken; var c,cn: char; f,e,x,y: real; sgn,d,i: integer; procedure getnumber; begin while (c <> ' ') and (not error) do begin d := -1; i := 1; while i < 11 do if digits[i] = c then begin d := i-1; i := 11 end else i := i+1; if c = '.' then if f = -1 then f := 0 else error := true else if d > -1 then begin if z = -1 then z := 0; if f = -1 then z := 10*z+d else begin e := e/10; f := f+d*e end end else error := true; if eoln then c := ' ' else read(c) end; if z = -1 then error := true; if not error then begin if f = -1 then f := 0; z := sgn*(z+f) end end; begin { processtoken } tktype := nm; sgn := 1; z := -1; f := -1; e := 1; read(c); if c in ['+', '-', '*', '/'] then begin if eoln then cn := ' ' else read(cn); if cn = ' ' then case c of '+': tktype := ad; '-': tktype := sb; '*': tktype := mp; '/': tktype := dv end else begin if c = '-' then sgn := -1 else error := c <> '+'; c := cn end end; if (not error) and (tktype = nm) then getnumber; if not error then begin if tktype <> nm then begin y := pop; x := pop; if not error then case tktype of ad: z := x+y; sb: z := x-y; mp: z := x*y; dv: z := x/y end end; push(z); skipws end end; procedure init; var t: real; begin while nums <> nil do t := pop; error := false; if not eof then skipws end; begin { main } digits := '0123456789'; nums := nil; init; while not eof do begin if not eoln then begin repeat processtoken; if error then while not eoln do get(input) until eoln; if not error then begin z := pop; if nums = nil then writeln(z:1:4) else error := true end; if error then writeln('error') end; readln; init end end.
boykobbatgmaildotcom