############################################################################
#
#	File:     parse.icn
#
#	Subject:  Program to parse simple statements
#
#	Author:   Kenneth Walker
#
#	Date:     February 18, 1996
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#  This program parses simple statements.
#
#  It provides an interesting example of the use of co-expressions.
#
############################################################################

global lex	# co-expression for lexical analyzer
global next_tok	# next token from input

record token(type, string)

procedure main()
   lex := create ((!&input ? get_tok()) | |token("eof", "eof"))
   prog()
end

#
# get_tok is the main body of lexical analyzer
#
procedure get_tok()
   local tok
   repeat {	# skip white space and comments
      tab(many(' 	'))
      if ="#" | pos(0) then fail

      if any(&letters) then	# determine token type
         tok := token("id", tab(many(&letters ++ '_')))
      else if any(&digits) then
         tok := token("integer", tab(many(&digits)))
      else case move(1) of {
         ";"	:	tok := token("semi", ";")
         "("	:	tok := token("lparen", "(")
         ")"	:	tok := token("rparen", ")")
         ":"	:	if ="=" then tok := token("assign", ":=")
            		   else tok := token("colon", ":")
         "+"	:	tok := token("add_op", "+")
         "-"	:	tok := token("add_op", "-")
         "*"	:	tok := token("mult_op", "*")
         "/"	:	tok := token("mult_op", "/")
         default	:	err("invalid character in input")
         }
      suspend tok
      }
end

#
# The procedures that follow make up the parser
#

procedure prog()
   next_tok := @lex
   stmt()
   while next_tok.type == "semi" do {
      next_tok := @lex
      stmt()
      }
   if next_tok.type ~== "eof" then
      err("eof expected")
end

procedure stmt()
   if next_tok.type ~== "id" then
      err("id expected")
   write(next_tok.string)
   if (@lex).type ~== "assign" then
      err(":= expected")
   next_tok := @lex
   expr()
   write(":=")
end

procedure expr()
   local op

   term()
   while next_tok.type == "add_op" do {
      op := next_tok.string
      next_tok := @lex
      term()
      write(op)
      }
end

procedure term()
   local op

   factor()
   while next_tok.type == "mult_op" do {
      op := next_tok.string
      next_tok := @lex
      factor()
      write(op)
      }
end

procedure factor()
   case next_tok.type of {
      "id" | "integer": {
         write(next_tok.string)
         next_tok := @lex
         }
      "lparen": {
         next_tok := @lex
         expr()
         if next_tok.type ~== "rparen" then
            err(") expected")
         else
            next_tok := @lex
         }
      default:
         err("id or integer expected")
      }
end

procedure err(s)
   stop(" ** error **  ", s)
end
