[ros-dev] set /a implementation for review

Royce Mitchell III royce3 at ev1.net
Sat Sep 17 09:58:59 CEST 2005


recursive-descent implementation of set /a.

I'm sure not all error messages are going to match MS's exactly. I 
didn't really try to do that.

since trunk is frozen, I'm submitting this patch for anybody that wants 
to review. I'll commit it Monday evening (if I have time) pending any 
suggestions for improvements or bug-fixes, etc that ppl may have for it.
-------------- next part --------------
Index: set.c
===================================================================
--- set.c	(revision 17890)
+++ set.c	(working copy)
@@ -35,6 +35,8 @@
  */
 
 #include <precomp.h>
+#include <malloc.h>
+#include <stdio.h>
 #include "resource.h"
 
 #ifdef INCLUDE_CMD_SET
@@ -43,7 +45,15 @@
 /* initial size of environment variable buffer */
 #define ENV_BUFFER_SIZE  1024
 
+static BOOL
+seta_eval ( LPCTSTR expr );
 
+static LPCTSTR
+skip_ws ( LPCTSTR p )
+{
+	return p + strspn ( p, " \t" );
+}
+
 INT cmd_set (LPTSTR cmd, LPTSTR param)
 {
 	TCHAR szMsg[RC_STRING_MAX_SIZE];
@@ -83,6 +93,12 @@
 		return 0;
 	}
 
+	if ( !_tcsnicmp (param, _T("/A"), 2) )
+	{
+		// TODO FIXME - what are we supposed to return?
+		return seta_eval ( skip_ws(param+2) );
+	}
+
 	p = _tcschr (param, _T('='));
 	if (p)
 	{
@@ -124,4 +140,386 @@
 	return 0;
 }
 
+static INT
+ident_len ( LPCTSTR p )
+{
+	LPCTSTR p2 = p;
+	if ( __iscsymf(*p) )
+	{
+		++p2;
+		while ( __iscsym(*p2) )
+			++p2;
+	}
+	return p2-p;
+}
+
+#define dup_ident(p) _tcsncpy_s(alloca((ident_len(p)+1)*sizeof(TCHAR)), p, 
+#define PARSE_IDENT(ident,identlen,p) \
+	identlen = ident_len(p); \
+	ident = (LPTSTR)alloca ( ( identlen + 1 ) * sizeof(TCHAR) ); \
+	memmove ( ident, p, identlen * sizeof(TCHAR) ); \
+	ident[identlen] = 0;
+
+static BOOL
+seta_identval ( LPCTSTR ident, INT* result )
+{
+	// get size of buffer for env var
+	LPTSTR buf;
+	DWORD dwBuffer = GetEnvironmentVariable ( ident, NULL, 0 );
+	// if GetEnvironmentVariable() fails, it returns 0
+	if ( !dwBuffer )
+	{
+		// TODO FIXME - is it correct to report a value of 0 for non-existant variables?
+		*result = 0;
+		return FALSE;
+	}
+	buf = (LPTSTR)alloca ( dwBuffer * sizeof(TCHAR) );
+	if ( !buf )
+	{
+		// TODO FIXME - is it correct to report 0 when report resources low... should we error somehow?
+		*result = 0;
+		return FALSE;
+	}
+	GetEnvironmentVariable ( ident, buf, dwBuffer );
+	*result = atoi ( buf );
+	return TRUE;
+}
+
+static BOOL
+calc ( INT* lval, TCHAR op, INT rval )
+{
+	switch ( op )
+	{
+	case '*':
+		*lval *= rval;
+		break;
+	case '/':
+		*lval /= rval;
+		break;
+	case '%':
+		*lval %= rval;
+		break;
+	case '+':
+		*lval += rval;
+		break;
+	case '-':
+		*lval -= rval;
+		break;
+	case '&':
+		*lval &= rval;
+		break;
+	case '^':
+		*lval ^= rval;
+		break;
+	case '|':
+		*lval |= rval;
+		break;
+	default:
+		printf ( "Invalid operand.\n" );
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static BOOL
+seta_assignment ( LPCTSTR* p_, INT* result );
+
+static BOOL
+seta_unaryTerm ( LPCTSTR* p_, INT* result )
+{
+	LPCTSTR p = *p_;
+	if ( *p == '(' )
+	{
+		INT rval;
+		p = skip_ws ( p + 1 );
+		if ( !seta_assignment ( &p, &rval ) )
+			return FALSE;
+		if ( *p != ')' )
+		{
+			_tprintf ( _T("Expected ')'\n") );
+			return FALSE;
+		}
+		p = skip_ws ( p + 1 );
+		*p_ = p;
+		return TRUE;
+	}
+	if ( isdigit(*p) )
+	{
+		*result = atoi ( p );
+		p = skip_ws ( p + strspn ( p, "1234567890" ) );
+	}
+	else if ( __iscsymf(*p) )
+	{
+		LPTSTR ident;
+		INT identlen;
+		PARSE_IDENT(ident,identlen,p);
+		if ( !seta_identval ( ident, result ) )
+			return FALSE;
+	}
+	else
+	{
+		_tprintf ( _T("Expected number or variable name\n") );
+		return FALSE;
+	}
+	*p_ = p;
+	return TRUE;
+}
+
+static BOOL
+seta_mulTerm ( LPCTSTR* p_, INT* result )
+{
+	LPCTSTR p = *p_;
+	TCHAR op = 0;
+	INT rval;
+	if ( _tcschr(_T("!~-"),*p) )
+	{
+		op = *p;
+		p = skip_ws ( p + 1 );
+	}
+	if ( !seta_unaryTerm ( &p, &rval ) )
+		return FALSE;
+	switch ( op )
+	{
+	case '!':
+		rval = !rval;
+		break;
+	case '~':
+		rval = ~rval;
+		break;
+	case '-':
+		rval = -rval;
+		break;
+	}
+
+	*result = rval;
+	*p_ = p;
+	return TRUE;
+}
+
+static BOOL
+seta_addTerm ( LPCTSTR* p_, INT* result )
+{
+	LPCTSTR p = *p_;
+	INT lval;
+	if ( !seta_mulTerm ( &p, &lval ) )
+		return FALSE;
+	while ( *p && _tcschr(_T("*/%"),*p) )
+	{
+		INT rval;
+		TCHAR op = *p;
+
+		p = skip_ws ( p+1 );
+
+		if ( !seta_mulTerm ( &p, &rval ) )
+			return FALSE;
+
+		if ( !calc ( &lval, op, rval ) )
+			return FALSE;
+	}
+
+	*result = lval;
+	*p_ = p;
+	return TRUE;
+}
+
+static BOOL
+seta_logShiftTerm ( LPCTSTR* p_, INT* result )
+{
+	LPCTSTR p = *p_;
+	INT lval;
+	if ( !seta_addTerm ( &p, &lval ) )
+		return FALSE;
+	while ( *p && _tcschr(_T("+-"),*p) )
+	{
+		INT rval;
+		TCHAR op = *p;
+
+		p = skip_ws ( p+1 );
+
+		if ( !seta_addTerm ( &p, &rval ) )
+			return FALSE;
+
+		if ( !calc ( &lval, op, rval ) )
+			return FALSE;
+	}
+
+	*result = lval;
+	*p_ = p;
+	return TRUE;
+}
+
+static BOOL
+seta_bitAndTerm ( LPCTSTR* p_, INT* result )
+{
+	LPCTSTR p = *p_;
+	INT lval;
+	if ( !seta_logShiftTerm ( &p, &lval ) )
+		return FALSE;
+	while ( *p && _tcschr(_T("<>"),*p) && p[0] == p[1] )
+	{
+		INT rval;
+		TCHAR op = *p;
+
+		p = skip_ws ( p+2 );
+
+		if ( !seta_logShiftTerm ( &p, &rval ) )
+			return FALSE;
+
+		if ( !calc ( &lval, op, rval ) )
+			return FALSE;
+	}
+
+	*result = lval;
+	*p_ = p;
+	return TRUE;
+}
+
+static BOOL
+seta_bitExclOrTerm ( LPCTSTR* p_, INT* result )
+{
+	LPCTSTR p = *p_;
+	INT lval;
+	if ( !seta_bitAndTerm ( &p, &lval ) )
+		return FALSE;
+	while ( *p == _T('&') )
+	{
+		INT rval;
+
+		p = skip_ws ( p+1 );
+
+		if ( !seta_bitAndTerm ( &p, &rval ) )
+			return FALSE;
+
+		lval &= rval;
+	}
+
+	*result = lval;
+	*p_ = p;
+	return TRUE;
+}
+
+static BOOL
+seta_bitOrTerm ( LPCTSTR* p_, INT* result )
+{
+	LPCTSTR p = *p_;
+	INT lval;
+	if ( !seta_bitExclOrTerm ( &p, &lval ) )
+		return FALSE;
+	while ( *p == _T('^') )
+	{
+		INT rval;
+
+		p = skip_ws ( p+1 );
+
+		if ( !seta_bitExclOrTerm ( &p, &rval ) )
+			return FALSE;
+
+		lval ^= rval;
+	}
+
+	*result = lval;
+	*p_ = p;
+	return TRUE;
+}
+
+static BOOL
+seta_expr ( LPCTSTR* p_, INT* result )
+{
+	LPCTSTR p = *p_;
+	INT lval;
+	if ( !seta_bitOrTerm ( &p, &lval ) )
+		return FALSE;
+	while ( *p == _T('|') )
+	{
+		INT rval;
+
+		p = skip_ws ( p+1 );
+
+		if ( !seta_bitOrTerm ( &p, &rval ) )
+			return FALSE;
+
+		lval |= rval;
+	}
+
+	*result = lval;
+	*p_ = p;
+	return TRUE;
+}
+
+static BOOL
+seta_assignment ( LPCTSTR* p_, INT* result )
+{
+	LPCTSTR p = *p_;
+	LPTSTR ident;
+	TCHAR op = 0;
+	INT identlen, exprval;
+
+	PARSE_IDENT(ident,identlen,p);
+	if ( identlen )
+	{
+		if ( *p == _T('=') )
+			op = *p, p++;
+		else if ( _tcschr ( _T("*/%+-&^|"), *p ) && p[1] == _T('=') )
+			op = *p, p += 2;
+		else if ( _tcschr ( _T("<>"), *p ) && *p == p[1] && p[2] == _T('=') )
+			op = *p, p += 3;
+		else
+		{
+			_tprintf ( _T("Missing operand.\n") );
+			return FALSE;
+		}
+		p += strspn ( p, " \t" );
+	}
+
+	if ( !seta_expr ( &p, &exprval ) )
+		return FALSE;
+
+	if ( identlen )
+	{
+		INT identval;
+		LPTSTR buf;
+
+		if ( !seta_identval ( ident, &identval ) )
+			return FALSE;
+		if ( op == '=' )
+			identval = *result;
+		else if ( !calc ( &identval, op, exprval ) )
+			return FALSE;
+		buf = (LPTSTR)alloca ( 32 * sizeof(TCHAR) );
+		_sntprintf ( buf, 32, _T("%i"), identval );
+		SetEnvironmentVariable ( ident, buf ); // TODO FIXME - check return value
+		exprval = identval;
+	}
+
+	*result = exprval;
+	*p_ = p;
+	return TRUE;
+}
+
+static BOOL
+seta_eval ( LPCTSTR p )
+{
+	INT rval;
+	if ( !*p )
+	{
+		_tprintf ( _T("The syntax of the command is incorrect.\n") );
+		return FALSE;
+	}
+	for ( ;; )
+	{
+		if ( !seta_assignment ( &p, &rval ) )
+			return FALSE;
+		if ( !*p )
+			break;
+		if ( *p != _T(',') )
+		{
+			_tprintf ( _T("Missing operator.\n") );
+			return FALSE;
+		}
+		++p; // skip comma
+	}
+	_tprintf ( _T("%i\n"), rval );
+	return TRUE;
+}
+
 #endif


More information about the Ros-dev mailing list