context: /*----------------------------------------------------------------------
  Compiler Generator Coco/R,
  Copyright (c) 1990, 2004 Hanspeter Moessenboeck, University of Linz
  extended by M. Loeberbauer & A. Woess, Univ. of Linz
  with improvements by Pat Terry, Rhodes University
  
  This program is free software; you can redistribute it and/or modify it 
  under the terms of the GNU General Public License as published by the 
  Free Software Foundation; either version 2, or (at your option) any 
  later version.
  
  This program is distributed in the hope that it will be useful, but 
  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
  for more details.
  
  You should have received a copy of the GNU General Public License along 
  with this program; if not, write to the Free Software Foundation, Inc., 
  59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  
  As an exception, it is allowed to write an extension of Coco/R that is
  used as a plugin in non-free software.
  
  If not otherwise stated, any source code generated by Coco/R (other than 
  Coco/R itself) does not fall under the GNU General Public License.
  -----------------------------------------------------------------------*/
  
  using System;
  using System.IO;
  using System.Collections;
  
  namespace at.jku.ssw.Coco {
  
  public class Token {
  	public int kind;    // token kind
  	public int pos;     // token position in the source text (starting at 0)
  	public int col;     // token column (starting at 1)
  	public int line;    // token line (starting at 1)
  	public string val;  // token value
  	public Token next;  // ML 2005-03-11 Tokens are kept in linked list
  }
  
  //-----------------------------------------------------------------------------------
  // Buffer
  //-----------------------------------------------------------------------------------
  public class Buffer {
  	// This Buffer supports the following cases:
  	// 1) seekable stream (file)
  	//    a) whole stream in buffer
  	//    b) part of stream in buffer
  	// 2) non seekable stream (network, console)
  
  	public const int EOF = char.MaxValue + 1;
  	const int MIN_BUFFER_LENGTH = 1024; // 1KB
  	const int MAX_BUFFER_LENGTH = MIN_BUFFER_LENGTH * 64; // 64KB
  	byte[] buf;         // input buffer
  	int bufStart;       // position of first byte in buffer relative to input stream
  	int bufLen;         // length of buffer
  	int fileLen;        // length of input stream (may change if the stream is no file)
  	int bufPos;         // current position in buffer
  	Stream stream;      // input stream (seekable)
  	bool isUserStream;  // was the stream opened by the user?
  	
  	public Buffer (Stream s, bool isUserStream) {
  		stream = s; this.isUserStream = isUserStream;
  		
  		if (stream.CanSeek) {
  			fileLen = (int) stream.Length;
  			bufLen = Math.Min(fileLen, MAX_BUFFER_LENGTH);
  			bufStart = Int32.MaxValue; // nothing in the buffer so far
  		} else {
  			fileLen = bufLen = bufStart = 0;
  		}
  
  		buf = new byte[(bufLen>0) ? bufLen : MIN_BUFFER_LENGTH];
  		if (fileLen > 0) Pos = 0; // setup buffer to position 0 (start)
  		else bufPos = 0; // index 0 is already after the file, thus Pos = 0 is invalid
  		if (bufLen == fileLen && stream.CanSeek) Close();
  	}
  	
  	protected Buffer(Buffer b) { // called in UTF8Buffer constructor
  		buf = b.buf;
  		bufStart = b.bufStart;
  		bufLen = b.bufLen;
  		fileLen = b.fileLen;
  		bufPos = b.bufPos;
  		stream = b.stream;
  		// keep destructor from closing the stream
  		b.stream = null;
  		isUserStream = b.isUserStream;
  	}
  
  	~Buffer() { Close(); }
  	
  	protected void Close() {
  		if (!isUserStream && stream != null) {
  			stream.Close();
  			stream = null;
  		}
  	}
  	
  	public virtual int Read () {
  		if (bufPos < bufLen) {
  			return buf[bufPos++];
  		} else if (Pos < fileLen) {
  			Pos = Pos; // shift buffer start to Pos
  			return buf[bufPos++];
  		} else if (stream != null && !stream.CanSeek && ReadNextStreamChunk() > 0) {
  			return buf[bufPos++];
  		} else {
  			return EOF;
  		}
  	}
  
  	public int Peek () {
  		int curPos = Pos;
  		int ch = Read();
  		Pos = curPos;
  		return ch;
  	}
  	
  	public string GetString (int beg, int end) {
  		int len = end - beg;
  		char[] buf = new char[len];
  		int oldPos = Pos;
  		Pos = beg;
  		for (int i = 0; i < len; i++) buf[i] = (char) Read();
  		Pos = oldPos;
  		return new String(buf);
  	}
  
  	public int Pos {
  		get { return bufPos + bufStart; }
  		set {
  			if (value >= fileLen && stream != null && !stream.CanSeek) {
  				// Wanted position is after buffer and the stream
  				// is not seek-able e.g. network or console,
  				// thus we have to read the stream manually till
  				// the wanted position is in sight.
  				while (value >= fileLen && ReadNextStreamChunk() > 0);
  			}
  
  			if (value < 0 || value > fileLen) {
  				throw new FatalError("buffer out of bounds access, position: " + value);
  			}
  
  			if (value >= bufStart && value < bufStart + bufLen) { // already in buffer
  				bufPos = value - bufStart;
  			} else if (stream != null) { // must be swapped in
  				stream.Seek(value, SeekOrigin.Begin);
  				bufLen = stream.Read(buf, 0, buf.Length);
  				bufStart = value; bufPos = 0;
  			} else {
  				// set the position to the end of the file, Pos will return fileLen.
  				bufPos = fileLen - bufStart;
  			}
  		}
  	}
  	
  	// Read the next chunk of bytes from the stream, increases the buffer
  	// if needed and updates the fields fileLen and bufLen.
  	// Returns the number of bytes read.
  	private int ReadNextStreamChunk() {
  		int free = buf.Length - bufLen;
  		if (free == 0) {
  			// in the case of a growing input stream
  			// we can neither seek in the stream, nor can we
  			// foresee the maximum length, thus we must adapt
  			// the buffer size on demand.
  			byte[] newBuf = new byte[bufLen * 2];
  			Array.Copy(buf, newBuf, bufLen);
  			buf = newBuf;
  			free = bufLen;
  		}
  		int read = stream.Read(buf, bufLen, free);
  		if (read > 0) {
  			fileLen = bufLen = (bufLen + read);
  			return read;
  		}
  		// end of stream reached
  		return 0;
  	}
  }
  
  //-----------------------------------------------------------------------------------
  // UTF8Buffer
  //-----------------------------------------------------------------------------------
  public class UTF8Buffer: Buffer {
  	public UTF8Buffer(Buffer b): base(b) {}
  
  	public override int Read() {
  		int ch;
  		do {
  			ch = base.Read();
  			// until we find a uft8 start (0xxxxxxx or 11xxxxxx)
  		} while ((ch >= 128) && ((ch & 0xC0) != 0xC0) && (ch != EOF));
  		if (ch < 128 || ch == EOF) {
  			// nothing to do, first 127 chars are the same in ascii and utf8
  			// 0xxxxxxx or end of file character
  		} else if ((ch & 0xF0) == 0xF0) {
  			// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  			int c1 = ch & 0x07; ch = base.Read();
  			int c2 = ch & 0x3F; ch = base.Read();
  			int c3 = ch & 0x3F; ch = base.Read();
  			int c4 = ch & 0x3F;
  			ch = (((((c1 << 6) | c2) << 6) | c3) << 6) | c4;
  		} else if ((ch & 0xE0) == 0xE0) {
  			// 1110xxxx 10xxxxxx 10xxxxxx
  			int c1 = ch & 0x0F; ch = base.Read();
  			int c2 = ch & 0x3F; ch = base.Read();
  			int c3 = ch & 0x3F;
  			ch = (((c1 << 6) | c2) << 6) | c3;
  		} else if ((ch & 0xC0) == 0xC0) {
  			// 110xxxxx 10xxxxxx
  			int c1 = ch & 0x1F; ch = base.Read();
  			int c2 = ch & 0x3F;
  			ch = (c1 << 6) | c2;
  		}
  		return ch;
  	}
  }
  
  //-----------------------------------------------------------------------------------
  // Scanner
  //-----------------------------------------------------------------------------------
  public class Scanner {
  	const char EOL = '\n';
  	const int eofSym = 0; /* pdt */
  	const int maxT = 43;
  	const int noSym = 43;
  
  
  	public Buffer buffer; // scanner buffer
  	
  	Token t;          // current token
  	int ch;           // current input character
  	int pos;          // byte position of current character
  	int col;          // column number of current character
  	int line;         // line number of current character
  	int oldEols;      // EOLs that appeared in a comment;
  	static readonly Hashtable start; // maps first token character to start state
  
  	Token tokens;     // list of tokens already peeked (first token is a dummy)
  	Token pt;         // current peek token
  	
  	char[] tval = new char[128]; // text of current token
  	int tlen;         // length of current token
  	
  	static Scanner() {
  		start = new Hashtable(128);
  		for (int i = 65; i <= 90; ++i) start[i] = 1;
  		for (int i = 97; i <= 122; ++i) start[i] = 1;
  		for (int i = 48; i <= 57; ++i) start[i] = 2;
  		start[34] = 11; 
  		start[39] = 5; 
  		start[36] = 10; 
  		start[61] = 13; 
  		start[46] = 29; 
  		start[43] = 14; 
  		start[45] = 15; 
  		start[60] = 30; 
  		start[62] = 17; 
  		start[124] = 20; 
  		start[40] = 31; 
  		start[41] = 21; 
  		start[91] = 22; 
  		start[93] = 23; 
  		start[123] = 24; 
  		start[125] = 25; 
  		start[59] = 28; 
  		start[Buffer.EOF] = -1;
  
  	}
  	
  	public Scanner (string fileName) {
  		try {
  			Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
  			buffer = new Buffer(stream, false);
  			Init();
  		} catch (IOException) {
  			throw new FatalError("Cannot open file " + fileName);
  		}
  	}
  	
  	public Scanner (Stream s) {
  		buffer = new Buffer(s, true);
  		Init();
  	}
  	
  	void Init() {
  		pos = -1; line = 1; col = 0;
  		oldEols = 0;
  		NextCh();
  		if (ch == 0xEF) { // check optional byte order mark for UTF-8
  			NextCh(); int ch1 = ch;
  			NextCh(); int ch2 = ch;
  			if (ch1 != 0xBB || ch2 != 0xBF) {
  				throw new FatalError(String.Format("illegal byte order mark: EF {0,2:X} {1,2:X}", ch1, ch2));
  			}
  			buffer = new UTF8Buffer(buffer); col = 0;
  			NextCh();
  		}
  		pt = tokens = new Token();  // first token is a dummy
  	}
  	
  	void NextCh() {
  		if (oldEols > 0) { ch = EOL; oldEols--; } 
  		else {
  			pos = buffer.Pos;
  			ch = buffer.Read(); col++;
  			// replace isolated '\r' by '\n' in order to make
  			// eol handling uniform across Windows, Unix and Mac
  			if (ch == '\r' && buffer.Peek() != '\n') ch = EOL;
  			if (ch == EOL) { line++; col = 0; }
  		}
  
  	}
  
  	void AddCh() {
  		if (tlen >= tval.Length) {
  			char[] newBuf = new char[2 * tval.Length];
  			Array.Copy(tval, 0, newBuf, 0, tval.Length);
  			tval = newBuf;
  		}
  		tval[tlen++] = (char) ch;
  		NextCh();
  	}
  
  
  
  	bool Comment0() {
  		int level = 1, pos0 = pos, line0 = line, col0 = col;
  		NextCh();
  		if (ch == '/') {
  			NextCh();
  			for(;;) {
  				if (ch == 10) {
  					level--;
  					if (level == 0) { oldEols = line - line0; NextCh(); return true; }
  					NextCh();
  				} else if (ch == Buffer.EOF) return false;
  				else NextCh();
  			}
  		} else {
  			buffer.Pos = pos0; NextCh(); line = line0; col = col0;
  		}
  		return false;
  	}
  
  	bool Comment1() {
  		int level = 1, pos0 = pos, line0 = line, col0 = col;
  		NextCh();
  		if (ch == '*') {
  			NextCh();
  			for(;;) {
  				if (ch == '*') {
  					NextCh();
  					if (ch == '/') {
  						level--;
  						if (level == 0) { oldEols = line - line0; NextCh(); return true; }
  						NextCh();
  					}
  				} else if (ch == '/') {
  					NextCh();
  					if (ch == '*') {
  						level++; NextCh();
  					}
  				} else if (ch == Buffer.EOF) return false;
  				else NextCh();
  			}
  		} else {
  			buffer.Pos = pos0; NextCh(); line = line0; col = col0;
  		}
  		return false;
  	}
  
  
  	void CheckLiteral() {
  		switch (t.val) {
  			case "COMPILER": t.kind = 6; break;
  			case "IGNORECASE": t.kind = 7; break;
  			case "CHARACTERS": t.kind = 8; break;
  			case "TOKENS": t.kind = 9; break;
  			case "PRAGMAS": t.kind = 10; break;
  			case "COMMENTS": t.kind = 11; break;
  			case "FROM": t.kind = 12; break;
  			case "TO": t.kind = 13; break;
  			case "NESTED": t.kind = 14; break;
  			case "IGNORE": t.kind = 15; break;
  			case "PRODUCTIONS": t.kind = 16; break;
  			case "END": t.kind = 19; break;
  			case "ANY": t.kind = 23; break;
  			case "WEAK": t.kind = 29; break;
  			case "SYNC": t.kind = 36; break;
  			case "IF": t.kind = 37; break;
  			case "CONTEXT": t.kind = 38; break;
  			case "using": t.kind = 41; break;
  			default: break;
  		}
  	}
  
  	Token NextToken() {
  
gt: 		while (ch == ' ' ||