/*
 * CECALC : 電卓アプリケーション：メインルーチン
 * Copyright(C) 1997-1999 Eiichiro Ito
 *
 * 1997/07/14:Eiichiro Ito
 *  (1) 作成開始
 * 修正履歴
 *
 */
#include	"conslib.h"
#include	<tchar.h>

TCHAR	AppName[] = TEXT("CECalc") ;	/* 必須項目 */

TCHAR	MessageBuf[ 256 ] ;

#define		Assert(x)
#define		Isdigit(c)		((c>=TEXT('0'))&&(c<=TEXT('9')))

#define		NUM_STATE		18
#define		ERROR			0
#define		ACCPT			1
#define		S(x)			((x)+2)
#define		R(x)			((x)+NUM_STATE+2)
#define		ISSHIFT(y)		((y)>=2&&(y)<NUM_STATE+2)
#define		ISREDUCE(y)		((y)>=NUM_STATE+2)
#define		GET_SHIFT(y)	((y)-2)
#define		GET_REDUCE(y)	((y)-NUM_STATE-2)
#define		NV_E			0
#define		NV_T			1
#define		NV_F			2

typedef enum {
	NUM = 0, PLUS, MINUS, MUL, DIV, LPAR, RPAR, END
} TOKEN ;

BYTE	ActionTbl[][8] = {
/* 0*/{S( 1),    0,S(16),    0,    0,S( 2),    0,    0},
/* 1*/{    0,R( 8),R( 8),R( 8),R( 8),    0,R( 8),R( 8)},
/* 2*/{S( 1),    0,S(16),    0,    0,S( 2),    0,    0},
/* 3*/{    0,S( 7),S( 8),    0,    0,    0,    0,ACCPT},
/* 4*/{    0,R( 3),R( 3),S( 9),S(10),    0,R( 3),R( 3)},
/* 5*/{    0,R( 6),R( 6),R( 6),R( 6),    0,R( 6),R( 6)},
/* 6*/{    0,S( 7),S( 8),    0,    0,    0,S(11),    0},
/* 7*/{S( 1),    0,    0,    0,    0,S( 2),    0,    0},
/* 8*/{S( 1),    0,    0,    0,    0,S( 2),    0,    0},
/* 9*/{S( 1),    0,    0,    0,    0,S( 2),    0,    0},
/*10*/{S( 1),    0,    0,    0,    0,S( 2),    0,    0},
/*11*/{    0,R( 7),R( 7),R( 7),R( 7),    0,R( 7),R( 7)},
/*12*/{    0,R( 1),R( 1),S( 9),S(10),    0,R( 1),R( 1)},
/*13*/{    0,R( 2),R( 2),S( 9),S(10),    0,R( 2),R( 2)},
/*14*/{    0,R( 4),R( 4),R( 4),R( 4),    0,R( 4),R( 4)},
/*15*/{    0,R( 5),R( 5),R( 5),R( 5),    0,R( 5),R( 5)},
/*16*/{S( 1),    0,    0,    0,    0,S( 2),    0,    0},
/*17*/{    0,R( 0),R( 0),R( 0),R( 0),    0,R( 0),R( 0)},
} ;

DWORD	DropTbl[] = { 2, 3, 3, 1, 3, 3, 1, 3, 1 } ;

BYTE	Goto[][3] = {
/* 0*/{ 3, 4, 5},
/* 1*/{ 0, 0, 0},
/* 2*/{ 6, 4, 5},
/* 3*/{ 0, 0, 0},
/* 4*/{ 0, 0, 0},
/* 5*/{ 0, 0, 0},
/* 6*/{ 0, 0, 0},
/* 7*/{ 0,12, 5},
/* 8*/{ 0,13, 5},
/* 9*/{ 0, 0,14},
/*10*/{ 0, 0,15},
/*11*/{ 0, 0, 0},
/*12*/{ 0, 0, 0},
/*13*/{ 0, 0, 0},
/*14*/{ 0, 0, 0},
/*15*/{ 0, 0, 0},
/*16*/{ 0, 0,17},
/*17*/{ 0, 0, 0},
} ;
BYTE	NVarTbl[] = { NV_F, NV_E, NV_E, NV_E, NV_T, NV_T, NV_T, NV_F, NV_F } ;

BYTE	Stack[100] ;
LONG	VStack[100] ;
DWORD	Num_stack = 0 ;
LONG	lval = 0 ;
LPCTSTR	TokenPtr = 0 ;

void
push( BYTE state, LONG value )
{
	Stack[ Num_stack ] = state ;
	VStack[ Num_stack ] = value ;
	Num_stack ++ ;
}

BYTE
top( void )
{
	Assert( Num_stack ) ;
	return Stack[ Num_stack - 1 ] ;
}

void
drop( DWORD num )
{
	Assert( Num_stack >= num ) ;
	Num_stack -= num ;
}

LONG
arg( DWORD idx )
{
	return VStack[ Num_stack - idx ] ;
}

TOKEN
get_token( void )
{
	TCHAR	c ;

	if ( !TokenPtr ) {
		return END ;
	}
	while ( 1 ) {
		c = *TokenPtr++ ;
		if ( !c ) {
			TokenPtr = 0 ;
			return END ;
		} else if ( Isdigit( c ) ) {
			lval = c - '0' ;
			while ( 1 ) {
				c = *TokenPtr ;
				if ( !Isdigit( c ) ) {
					break ;
				}
				lval *= 10 ;
				lval += c - '0' ;
				TokenPtr ++ ;
			}
			return NUM ;
		} else if ( c == '+' ) {
			return PLUS ;
		} else if ( c == '-' ) {
			return MINUS ;
		} else if ( c == '*' ) {
			return MUL ;
		} else if ( c == '/' ) {
			return DIV ;
		} else if ( c == '(' ) {
			return LPAR ;
		} else if ( c == ')' ) {
			return RPAR ;
		}
	}
}

void
action( DWORD rnum )
{
    switch ( rnum ) {
    case 1:	/* E :- E + T */
		lval = arg( 3 ) + arg( 1 ) ;
		break ;
    case 2:	/* E :- E - T */
		lval = arg( 3 ) - arg( 1 ) ;
		break ;
    case 3:	/* E :- T */
		lval = arg( 1 ) ;
		break ;
    case 4:	/* T :- T * F */
		lval = arg( 3 ) * arg( 1 ) ;
		break ;
    case 5:	/* T :- T / F */
		lval = arg( 3 ) / arg( 1 ) ;
		break ;
    case 6:	/* T :- F */
		lval = arg( 1 ) ;
		break ;
    case 7:	/* F :- ( E ) */
		lval = arg( 2 ) ;
		break ;
    case 8:	/* F :- num */
		lval = arg( 1 ) ;
		break ;
    case 0:	/* F :- - E */
		lval = - arg( 1 ) ;
		break ;
    }
}

BOOL
calc( LPCTSTR buf, LPLONG result )
{
	TOKEN	a = END ;
	DWORD	rn ;
	BYTE	aa, s, ss ;
	BOOL	f_token = TRUE ;

	*result = 0 ;
	push( 0, 0L ) ;
	TokenPtr = buf ;
	while ( 1 ) {
		s = top() ;
		if ( f_token ) {
			f_token = FALSE ;
			a = get_token() ;
		}
		ss = ActionTbl[ s ][ a ] ;
		if ( ISSHIFT( ss ) ) {
			ss = GET_SHIFT( ss ) ;
			push( ss, lval ) ;
			f_token = TRUE ;
		} else if ( ISREDUCE( ss ) ) {
			rn = GET_REDUCE( ss ) ;
			action( rn ) ;
			drop( DropTbl[ rn ] ) ;
			ss = top() ;
			aa = NVarTbl[ rn ] ;
			ss = Goto[ ss ][ aa ] ;
			push( ss, lval ) ;
		} else if ( ss == ACCPT ) {
			*result = arg( 1 ) ;
			return TRUE ;
		} else {
			return FALSE ;
		}
	}
}

int
Main( DWORD argc, LPTSTR argv[] )
{
	LPTSTR	ptr ;
	LONG	result ;

	if ( Argc > 1 ) {
		calc( CmdLine, &result ) ;
		wsprintf( MessageBuf, TEXT("%d\n"), result ) ;
		Cputs( MessageBuf ) ;
	} else {
		while ( 1 ) {
			Cputs( TEXT("CALC>") ) ;
			ptr = Cgets( MessageBuf ) ;
			if ( ptr == NULL || !_tcsicmp( ptr, TEXT("exit") ) ) {
				break ;
			}
			calc( ptr, &result ) ;
			wsprintf( MessageBuf, TEXT("%d\n"), result ) ;
			Cputs( MessageBuf ) ;
		}
	}
	return result ;
}
