/*
 * $Id$
 *
 * Copyright © 2003 Keith Packard
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Keith Packard not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Keith Packard makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

autoload RR
autoload Ctype

extend namespace RR {
    public namespace Lex {

	public exception syntax ();
	
	int lexc (file f) {
	    try {
		return File::getc (f);
	    } catch File::io_error (string reason, File::error_type error, file f) {
		switch (error) {
		case File::error_type.UTF8:
		    printf ("invalid utf\n");
		    break;
		default:
		    raise File::io_error (reason, error, f);
		}
	    }
	    return lexc (f);
	}

	bool skipwhite (file f, bool allow_newline) {
	    while ((int c = lexc (f)) != -1) {
		if (c == '\n' && !allow_newline) {
		    File::ungetc (c, f);
		    return true;
		}
		if (!Ctype::isspace(c)) {
		    File::ungetc (c, f);
		    return false;
		}
	    }
	    return true;
	}

	public void skipline (file f) {
	    while ((int c = lexc (f)) != -1)
	    {
		if (c == '\n')
		    break;
	    }
	}

	string next_word (file f) {
	    string  s = "";
	    bool quoted = false;
	    bool escaped = false;
	    while ((int c = lexc (f)) != -1) {
		if (!escaped)
		{
		    if (c == '\\')
		    {
			escaped = true;
			continue;
		    }
		    if (c == '"')
		    {
			quoted = !quoted;
			continue;
		    }
		}
		if (!quoted && !escaped)
		{
		    if (Ctype::isspace (c))
		    {
			File::ungetc (c, f);
			break;
		    }
		}
		s = s + String::new(c);
		escaped = false;
	    }
	    return s;
	}

	public bool eol (file f) {
	    skipwhite (f, false);
	    int c = lexc (f);
	    if (c == -1)
		return true;
	    File::ungetc (c, f);
	    if (c == '\n')
		return true;
	    return false;
	}

	public string word (file f) {
	    if (skipwhite (f, false)) {
		skipline(f);
		raise syntax ();
	    }
	    return next_word (f);
	}

	public string firstword (file f) {
    	    skipwhite (f, true);
    	    return next_word (f);
	}

	public string[] recv(file f) {

	    string[...]	words = {};

	    try {
		void add (string w) {
		    words[dim(words)] = w;
		}

		string w = firstword (f);
		add (w);
		while (!eol (f))
		    add (word(f));
		skipline(f);
	    }
	    return words;
	}
    }
}
