/*
This program is distributed under the terms of the 'MIT license'. The text
of this licence follows...

Copyright (c) 2005 J.D.Medhurst (a.k.a. Tixy)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
@file

@brief Forth virtual machine
*/

#ifndef FORTH_H
#define FORTH_H

/**
@defgroup forth Forth - Forth virtual machine

@brief ANS forth virtual machine.

This is a 'token threaded' implementation which contains the ANS Core words
plus a few extra words from other word sets. It doesn't make use of any C library
functions, and so should compile to stand-alone code suitable for embedding
in other programs.

@version 2005-12-27
	- Fixed various bugs in Forth virtual machine which manifested when <code>sizeof(CELL)</code>
	  was greater than <code>sizeof(CELL*)</code>.

@version 2006-05-20
	- Changed code to use standard typedefs, e.g. replaced uint8 with uint8_t, and made use of size_t.
@{
*/

/**
A type representing a forth cell type.
If this typedef is changed then #CHARS_PER_CELL must be updated.
*/
typedef int32_t CELL;

ASSERT_COMPILE(sizeof(CELL)>=sizeof(CELL*)); // CELL must be big enough to store pointers

/**
A type representing a forth unsigned cell type.
This mus have the same size as #CELL.
*/
typedef uint32_t UCELL;

/**
A type representing a forth char type.
If this typedef is changed then #BITS_PER_CHAR and #CHARS_PER_CELL must be updated.
*/
typedef uint8_t CHAR;

/**
Number of bits in a #CHAR
*/
#define BITS_PER_CHAR 8

/**
Number of CHARs in a #CELL
*/
#define CHARS_PER_CELL 4

ASSERT_COMPILE(CHARS_PER_CELL==sizeof(CELL)/sizeof(CHAR)); // Check CHARS_PER_CELL is defined correctly


/**
@brief Abstract interfrace class for forth character i/o.

The forth VM uses this object to request character input and output.
Here is an example implementation of this class using the C standard library...
@code
	#include <stdio.h>
	#include <unistd.h>
	#include <termios.h>

	class StdForthIo : public ForthIo
		{
	public:
		StdForthIo()
			{
			// set new line sequence as CR/LF...
			NewLine[0]=2;
			NewLine[1]=13;
			NewLine[2]=10;

			// if stdin is a terminal then set non-canonical input mode without echo...
			if((IsTerminal = tcgetattr(fileno(stdin),&InitialSettings)==0))
				{
				struct termios settings = InitialSettings;
				settings.c_lflag &= ~(ICANON|ECHO);
				settings.c_cc[VMIN] = 1;
				settings.c_cc[VTIME] = 0;
				tcsetattr(fileno(stdin),TCSANOW,&settings);
				}
			}

		~StdForthIo()
			{
			// restore terminal settings...
			if(IsTerminal)
				tcsetattr(fileno(stdin),TCSANOW,&InitialSettings);
			}


		virtual void ConsoleOut(const CHAR* text,UCELL textLength)
			{
			while(textLength--)
				putchar(*text++);
			}

		virtual CELL ConsoleIn()
			{
			return getchar();
			}

	private:
		bool IsTerminal;
		struct termios InitialSettings;
		};
@endcode
*/
class ForthIo
	{
public:
	/**
	Display a text string on the console.
	This is called by the forth words \c TYPE and \c EMIT.
	@param text Pointer to string.
	@param textLength Number of characters in string.
	*/
	virtual void ConsoleOut(const CHAR* text,UCELL textLength) = 0;

	/**
	Get a single character from the console input.
	This is called by the forth word \c KEY.
	@return The character.
	*/
	virtual CELL ConsoleIn() = 0;
public:
	/**
	A counted string representing the system's line terminating sequence.
	This is used by the forth word \c CR and the first character in this string is also used
	as the input termination character by \c ACECPT.

	<tt>NewLine[0]</tt> is the character count, <tt>NewLine[1]</tt> and subsequent
	are the characters of the line terminating sequence; unused characters may be left
	uninitialised. E.g. the following sequence initialises the string with a
	carriage-return line-feed sequence:
	@code
	NewLine[0]=2; NewLine[1]=13; NewLine[2]=10;
	@endcode
	*/
	CHAR NewLine[4];
	};


/**
@brief Public interface to the forth virtual machine.
*/
class Forth
	{
public:
	/**
	Construct a forth virtual machine in the specified memory region.

	@param memoryStart Start address of memory.
	@param memorySize Size of memory.
	@param ioHandler Pointer to the console i/o object the VM will use.

	@return Pointer to VM or 0 if \a memorySize is too small.
	*/
	static Forth* Construct(void* memoryStart,size_t memorySize,ForthIo* ioHandler);

	/**
	Reset the virtual machine state to that of a newly constructed object.
	*/
	void Reset();

	/**
	Perform the action of the forth word \c QUIT. I.e. continuously accept console
	input and interpret it.

	To exit this function, enter the word <code>BYE</code> at the console.

	@return 0 or a forth execption value.
	*/
	CELL Quit();

	/**
	Execute a forth execution token.

	@param xt A forth execution token.

	@return 0 or a forth execption value.
	*/
	CELL Execute(CELL xt);

	/**
	Evaluate (interpret) a text string.

	@param text Pointer to string.
	@param textLength Number of characters in string.

	@return 0 or a forth execption value.
	*/
	CELL Evaluate(const CHAR* text,unsigned textLength);

	/**
	Push a number of cells onto the forth parameter stack. Values are pushed in reverse order.
	E.g. \a cells[\a numCells -1] will be pushed first and \a cells [0] will be the
	top item on the stack.

	@param cells Pointer to values to be pushed.
	@param numCells Number of values to be pushed.
	*/
	void Push(const CELL* cells, unsigned numCells);

	/**
	Pop a number of cells off the forth parameter stack.

	@param numCells Number of values to be popped. This can be zero, in which case
					no values are removed from the stack, but a pointer to the top-most
					value is still returned; enabling values on the stack to read without
					removing them.

	@return Pointer to top most stack value popped; other values follow this in memory.
			These values become invalid once any other Forth class method is called.
	*/
	const CELL* Pop(unsigned numCells);

	};


/** @} */ // End of group

#endif
