/*****************************************************************\
*       32-bit or 64-bit BBC BASIC Interpreter                    *
*       (C) 2017-2026  R.T.Russell  http://www.rtrussell.co.uk/   *
*                                                                 *
*       The name 'BBC BASIC' is the property of the British       *
*       Broadcasting Corporation and used with their permission,  *
*       it is not transferrable to a forked or derived work.      *
*                                                                 *
*       bbeval.c: Expression evaluation, functions and arithmetic *
*       Version 1.43c, 03-Feb-2026                                *
\*****************************************************************/

#define __USE_MINGW_ANSI_STDIO 1

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <setjmp.h>
#include "BBC.h"

#if defined __arm__ || defined __aarch64__ || defined __EMSCRIPTEN__ || defined __ANDROID__
#define powl pow
#define sqrtl sqrt
#define sinl sin
#define cosl cos
#define tanl tan
#define asinl asin
#define acosl acos
#define atanl atan
#define expl exp
#define logl log
#define fabsl fabs
#define truncl trunc
#define EFORMAT "%.*E"
#define FFORMAT "%.*f"
#define GFORMAT "%.*G"
#else
#define EFORMAT "%.*LE"
#define FFORMAT "%.*Lf"
#define GFORMAT "%.*LG"
#endif

// Routines in bbmain:
void check (void) ;		// Check for running out of memory
int range0 (char) ;		// Test char for valid in a variable name
signed char nxt (void) ;	// Skip spaces, handle line continuation
void error (int, const char*) ;	// Process an error
void outchr (unsigned char) ;	// Output a character
void text (const char *) ;	// Output NUL-terminated string
void crlf (void) ;		// Output a newline
unsigned short report (void) ;	// Put error message in string accumulator
char *moves (STR *, int) ;	// Move string into temporary buffer
void fixs (VAR) ;		// Copy to string accumulator
heapptr *pushs (VAR) ;		// Push string on stack
void comma (void) ;		// Check for comma
void braket (void) ;		// Check for closing parenthesis
char *alloct (int) ;		// Allocate a temporary string buffer
int arrlen (void **) ;		// Count elements in an array
void *getvar (unsigned char *) ;	// Get a variable's pointer and type
void *getput (unsigned char *) ;	// Get, and if necessary create, var
void *putvar (void *ptr, unsigned char*) ;
char *lexan (char *, char *, unsigned char) ;
signed char *gettop (signed char *, unsigned short *) ;
unsigned short setlin (signed char *, char **) ;
signed char *search (signed char *, signed char) ;
char *allocs (STR *, int) ;

// Routines in bbexec:
void modify (VAR, void *, unsigned char, signed char) ;
void modifs (VAR, void *, unsigned char, signed char) ;
void storen (VAR, void *, unsigned char) ;
void procfn (signed char) ;	// User-defined PROC or FN
VAR xeq (void) ;		// Execute

// Routines in bbcmos:
unsigned char osrdch (void) ;	// Get character from console input
int oskey (int) ;		// Wait for character or test key
int getime (void) ;		// Return centisecond count
int getims (void) ;		// Get clock time string to accs
int vtint (int, int) ;		// Get RGB pixel colour or -1
int vpoint (int, int) ;		// Get palette index or -1
void getcsr (int*, int*) ;	// Get text cursor (caret) coords
int vgetc (int, int) ;		// Get character at specified coords
int oscall (int) ;		// Call an emulated OS function
int widths (char *, int) ;	// Get string width in graphics units
int adval (int) ;		// ADVAL function
void *osopen (int, char *) ;	// Open a file
unsigned char osbget (void*, int*) ; // Get a byte from a file
long long getptr (void*) ;	// Get file pointer
long long getext (void*) ;	// Get file length
long long geteof (void*) ;	// Get EOF status
void *sysadr (char *) ;		// Get the address of an API function
int getmodeno (void) ;		// Get the current MODE number

// Global jump buffer:
extern jmp_buf env ;

#if defined __arm__ || defined __aarch64__ || defined __EMSCRIPTEN__ || defined __ANDROID__
static void setfpu(void) {}
static double xpower[9] = {1.0e1, 1.0e2, 1.0e4, 1.0e8, 1.0e16, 1.0e32, 1.0e64,
			   1.0e128, 1.0e256} ;
#else
static void setfpu(void) { unsigned int mode = 0x37F; asm ("fldcw %0" : : "m" (*&mode)); } 
static long double xpower[13] = {1.0e1L, 1.0e2L, 1.0e4L, 1.0e8L, 1.0e16L, 1.0e32L, 1.0e64L,
				1.0e128L, 1.0e256L, 1.0e512L, 1.0e1024L, 1.0e2048L, 1.0e4096L} ;
#endif

// Get possibly-parenthesised variable:
static void *getvbr(unsigned char *ptype)
{
	if (nxt () == '(')
	    {
		void *ptr ;
		esi++ ;
		ptr = getvbr (ptype) ;
		braket () ;
		return ptr ;
	    }
	return getvar (ptype) ;
}

// Convert a numeric value to a NUL-terminated decimal string:
int str (VAR v, char *dst, int format)
{
	int n ;
	char *p ;
	char fmt[5] = "%d" ;
	int width = format & 0xFF ;
        int prec = (format & 0xFF00) >> 8 ;

	switch (format & 0x30000)
	    {
		case 0x10000:
			if (prec) prec-- ;
			if (v.i.t == 0) v.f = v.i.n ;
			n = sprintf(dst, EFORMAT, prec, v.f) ;
			strcpy (fmt, "%-3d") ;
			break ;

		case 0x20000:
			if (v.i.t == 0) v.f = v.i.n ;
			n = sprintf(dst, FFORMAT, prec, v.f) ;
			break ;

		default:
		if (prec == 0) prec = 9 ;
		if (v.i.t == 0)
		    {
			n = sprintf(dst, "%lld", v.i.n) ; // ARM (no 80-bit float)
			if (n <= prec) break ;
			v.f = v.i.n ;
		    }
		n = sprintf(dst, GFORMAT, prec, v.f) ;
	    }

	p = strchr (dst, 'E') ;
	if (p)
	    {
		sprintf (p + 1, fmt, atoi (p + 1)) ;
		n = strlen (dst) ;
	    }

	if (n < width) 
	    {
		memmove (dst + width - n, dst, n + 1) ;
		memset (dst, ' ', width - n) ;
		n = width ;
	    }

	if (format & 0x800000)
	    {
		p = strchr (dst, '.') ;
		if (p) *p = ',' ;
	    }
	return n ;
}

// Convert a numeric value to a NUL-terminated hexadecimal string:
int strhex (VAR v, char *dst, int field)
{
#ifdef _WIN32
	char fmt[7] = "%*I64X" ;
#else
	char fmt[6] = "%*llX" ;
#endif
	long long n ;

	if (v.i.t)
	    {
		n = v.f ;
		if (n != truncl (v.f))
			error (20, NULL) ; // 'Number too big'
	    }
	else
		n = v.i.n ;

	if ((liston & BIT2) == 0)
	    {
		int i = n ;
		if (i != n)
			error (20, NULL) ; // 'Number too big'
		n &= 0xFFFFFFFF ;
	    }

	return sprintf(dst, fmt, field, n) ;
}

// Multiply by an integer-power of 10:
#if defined __arm__ || defined __aarch64__ || defined __EMSCRIPTEN__ || defined __ANDROID__
static double xpow10 (double n, int p)
{
	int f = 0, i = 0 ;

	if (p >= 512)
		error (20, NULL) ; // 'Number too big'
	if (p <= -512)
		return 0.0L ;
#else
static long double xpow10 (long double n, int p)
{
	int f = 0, i = 0 ;
	setfpu () ;

	if (p >= 8192)
		error (20, NULL) ; // 'Number too big'
	if (p <= -8192)
		return 0.0L ;
#endif

	if (p < 0)
	    {
		p = -p ;
		f = 1 ;
	    }

	while (p)
	    {
		if (p & 1)
		    {
			if (f)
				n /= xpower[i] ;
			else
				n *= xpower[i] ;
		    }
		i++ ;
		p = p >> 1 ;
	    }
	return n ;
}

// Get an unsigned integer from a string:

static unsigned long long number (int *pcount, int *ptrunc)
{
	unsigned long long n = 0 ;
	while (1)
	    {
		char al = *esi ;
		if ((al < '0') || (al > '9'))
			break ;
		esi++ ;
		(*pcount)++ ;
		if ((n > 0x1999999999999999L) || ((n == 0x1999999999999999L) && 
				((al > '5') || *ptrunc)))
			(*ptrunc)++ ;
		else
			n = n * 10 + (al - '0') ;
	    }
	return n ;
}

// Get an unsigned numeric constant:
VAR con (void)
{
	VAR v ;
	unsigned long long i = 0, f = 0 ;
	int e = 0, ni, nf = 0, ne = 0, nt = 0 ;
	setfpu () ;

	i = number (&ni, &nt) ;
	v.i.n = i ;
	v.i.t = 0 ;
	if ((*esi == '.') || (v.i.n < 0) || (nt != 0))
	    {
		v.i.t = 1 ; // ARM
		v.f = i ; // integer overflow
	    }

	if (nt != 0)
	    {
		v.f = xpow10 (v.f, nt) ;
		nt = 0 ;
	    }

	if (*esi == '.')
	    {
		esi++ ;
		f = number (&nf, &nt) ;
		v.f += xpow10 (f, nt - nf) ;
	    }

	if ((*esi == 'E') || ((liston & BIT3) && (*esi == 'e')))
	    {
		int neg = 0 ;

		esi++ ;
		if (*esi == '-')
		    {
			esi++ ;
			neg = 1 ;
		    }
		else if (*esi == '+')
			esi++ ;
		e = number (&ne, &nt) ;
		if (neg)
			e = -e ;

		if (v.i.t == 0)
		    {
			v.i.t = 1 ; // ARM
			v.f = i ;
		    }
		v.f = xpow10 (v.f, e) ;
		if ((v.i.t == 0) || (v.i.t == (short)0x8000))
			v.f = 0.0L ; // underflow
		if (isinf(v.f) || (v.i.t == -1))
			error (20, NULL) ; // 'Number too big'
	    }

	if (*esi == '#') esi++ ;
	return v ;
}

// Get a string constant (quoted string):
VAR cons (void)
{
	VAR v ;
	signed char al ;
	char *p = accs ;
	while (1)
	    {
		al = *esi++ ;
		if (al == 0x0D)
			error (9, NULL) ; // 'Missing "'
		if ((al == '"') && (*esi++ != '"'))
		    {
			esi-- ;
			v.s.p = accs - (char *) zero ;
			v.s.l = p - accs ;
			v.s.t = -1 ;
			break ;
		    }
		*p++ = al ;
	    }
	return v ;
}

// Load a numeric variable:
// type is 1, 4, 5, 8, 10, 32, 36 or 40
VAR loadn (void *ptr, unsigned char type)
{
	VAR v ;
	switch (type)
	    {
		case 1:
			v.i.t = 0 ;
			v.i.n = *(unsigned char*)ptr ;
			break ;

		case 4:
			v.i.t = 0 ;
			v.i.n = ILOAD(ptr) ;
			break ;

		case 5:
			{
			int ecx = *((unsigned char*)ptr + 4) ;
			int edx = ILOAD(ptr) ;
			int sign = (edx < 0) ;
			if (ecx == 0)
			    {
				v.i.t = 0 ;
				v.i.n = edx ;
				break ;
			    }
			ecx += 895 ;
			edx = edx << 1 ;
			ecx = (ecx << 20) | ((unsigned int)edx >> 12) ;
			edx = edx << 20 ;
			if (sign) ecx |= 0x80000000 ;
			v.s.p = edx ;
			v.s.l = ecx ;
			v.i.t = 1 ; // ARM
			v.f = v.d.d ;
			}
			break ;

		case 8:
			if (ILOAD((char *) ptr + 4) == 0)
			    {
				v.i.t = 0 ;
				v.i.n = ILOAD(ptr) ; // 64-bit variant
				break ;
			    }
			{
			// v.s.p = *(heapptr*)ptr ;
			// v.s.l = *(int *)((char *)ptr + 4) ;
			memcpy (&v.s.p, ptr, 8) ; // may be unaligned
			v.i.t = 1 ; // ARM
			v.f = v.d.d ;
			}
			break ;

		case 10:
			// v.s.p = *(heapptr*)ptr ;
			// v.s.l = *(int *)((char *)ptr + 4) ;
			// v.i.t = *(short *)((char *)ptr + 8) ;
			memcpy (&v.s.p, ptr, 10) ; // may be unaligned
			break ;

		case 40:
			v.i.t = 0 ;
			// v.s.p = *(heapptr*)ptr ;
			// v.s.l = *(int *)((char *)ptr + 4) ;
			memcpy (&v.s.p, ptr, 8) ; // may be unaligned
			break ;

		case 32:
			{
			union { int i; float f; } u ;
			u.i = ILOAD(ptr) ;
			v.i.t = 1 ; // ARM
			v.f = u.f ;
			break ;
			}

		case 36:
			v.i.t = 0 ;
			v.i.n = (intptr_t) VLOAD(ptr) ;
			break ;

		default:
			v.i.t = 0 ;
			v.i.n = 0 ;
			error (6, NULL) ; // 'Type mismatch'
	    }
	return v ;
}

// Load a string variable:
// type is 128, 130 or 136
VAR loads (void *ptr, unsigned char type)
{
	VAR v ;
	switch (type)
	    {
		case 128:
			v.s.l = memchr (ptr, 0x0D, 0x1000000) - ptr ;
			if (v.s.l > 0xFFFFFF)
				error (19, NULL) ; // 'String too long'
			if ((ptr < zero) || ((ptr + v.s.l) > (zero + 0xFFFFFFFF)))
			    {
				// Don't use alloct because it will put string in accs
				v.s.p = allocs (&tmps, v.s.l) - (char *) zero ;
				memcpy (v.s.p + zero, ptr, v.s.l) ;
			    }
			else
				v.s.p = ptr - zero ;
			break ;

		case 130:
			v.s.l = strlen (ptr) ;
			if ((ptr < zero) || ((ptr + v.s.l) > (zero + 0xFFFFFFFF)))
			    {
				// Don't use alloct because it will put string in accs
				v.s.p = allocs (&tmps, v.s.l) - (char *) zero ;
				memcpy (v.s.p + zero, ptr, v.s.l) ;
			    }
			else
				v.s.p = ptr - zero ;
			break ;

		case 136:
			v.s.p = ULOAD(ptr) ;
			v.s.l = ILOAD(ptr + 4) ;
			break ;

		default:
			error (6, NULL) ; // 'Type mismatch'
	    }
	v.s.t = -1 ;
	return v ;
}

// Load an integer numeric:
long long loadi (void *ptr, unsigned char type)
{
	VAR v = loadn (ptr, type) ;
	if (v.i.t != 0)
	    {
		long long t = v.f ;
		if (t != truncl (v.f))
			error (20, NULL) ; // 'Number too big'
		v.i.n = t ;
	    }
	return v.i.n ;
}

void xfix (VAR *px)
{
	if (px->i.t)
	    {
		long long t = px->f ;
		if (t != truncl (px->f))
			error (20, NULL) ; // 'Number too big'
		px->i.n = t ;
		px->i.t = 0 ;
	    }
}

static void fix2 (VAR *px, VAR *py)
{
	if (px->i.t)
	    {
		long long t = px->f ;
		if (t != truncl (px->f))
			error (20, NULL) ; // 'Number too big'
		px->i.n = t ;
		px->i.t = 0 ;
	    }
	if (py->i.t)
	    {
		long long t = py->f ;
		if (t != truncl (py->f))
			error (20, NULL) ; // 'Number too big'
		py->i.n = t ; 
		py->i.t = 0 ;
	    }
}

void xfloat (VAR *px)
{
	if (px->i.t == 0)
	    {
		px->i.t = 1 ; // ARM
		px->f = px->i.n ;
	    }
}

static void float2 (VAR *px, VAR *py)
{
	if (px->i.t == 0)
	    {
		px->i.t = 1 ; // ARM
		px->f = px->i.n ;
	    }
	if (py->i.t == 0)
	    {
		py->i.t = 1 ; // ARM
		py->f = py->i.n ;
	    }
}

// Return a pseudo-random integer:
unsigned int rnd (void)
{
	unsigned int ecx = prand.h ;
	unsigned int edx = prand.l ;
	unsigned int eax = (edx >> 1) | (ecx << 31) ;
	prand.h = (ecx & ~1) | (edx & 1) ; // Preserve bits 1-7
	eax = eax ^ (edx << 12) ;
	edx = eax ^ (eax >> 20) ;
	prand.l = edx ;
	return edx ;
}

VAR math (VAR x, signed char op, VAR y)
{
	errno = 0 ;
	setfpu () ;
	switch (op)
	    {
		case '+':
			if ((x.i.t == 0) && (y.i.t == 0))
			    {
#if !(defined(__GNUC__) && (__GNUC__ >= 7) || defined(__clang__) && (__clang_major__ >= 4))
				long long sum = x.i.n + y.i.n ;
				if (((int)(x.s.l ^ y.s.l) < 0) || ((sum ^ x.i.n) >= 0))
#else
				long long sum ;
				if (! __builtin_saddll_overflow (x.i.n, y.i.n, &sum))
#endif
				    {
					x.i.n = sum ;
					return x ;
				    }
			    }
			float2 (&x, &y) ;
			x.f += y.f ;
			break ;

		case '-':
			if ((x.i.t == 0) && (y.i.t == 0))
			    {
#if !(defined(__GNUC__) && (__GNUC__ >= 7) || defined(__clang__) && (__clang_major__ >= 4))
				long long dif = x.i.n - y.i.n ;
				if (((int)(x.s.l ^ y.s.l) >= 0) || ((dif ^ x.i.n) >= 0))
#else
				long long dif ;
				if (! __builtin_ssubll_overflow (x.i.n, y.i.n, &dif))
#endif
				    {
					x.i.n = dif ;
					return x ;
				    }
			    }
			float2 (&x, &y) ;
			x.f -= y.f ;
			break ;

		case '*':
			if (x.i.n == 0)
				return x ;
			if (y.i.n == 0)
				return y ;
			if ((x.i.t == 0) && (y.i.t == 0))
			    {
#if !(defined(__GNUC__) && (__GNUC__ >= 7) || defined(__clang__) && (__clang_major__ >= 4))
				int lz = __builtin_clzll(x.i.n) + __builtin_clzll(~x.i.n) +
			                 __builtin_clzll(y.i.n) + __builtin_clzll(~y.i.n) ;
				if ((lz > 64) || ((lz == 64) && (
				  ((x.i.n >= 0) && (y.i.n >= 0) && (x.i.n <= INT64_MAX / y.i.n)) ||
				  ((x.i.n >= 0) && (y.i.n < 0)  && (x.i.n <= INT64_MIN / y.i.n)) ||
				  ((x.i.n < 0)  && (y.i.n >= 0) && (y.i.n <= INT64_MIN / x.i.n)) ||
				  ((x.i.n < 0)  && (y.i.n < 0)  && (y.i.n >= INT64_MAX / x.i.n)))))
				    {
					x.i.n *= y.i.n ;
					return x ;
				    }
#else
				long long prod ;
				if (! __builtin_smulll_overflow (x.i.n, y.i.n, &prod))
				    {
					x.i.n = prod ;
					return x ;
				    }
#endif
			    }
			float2 (&x, &y) ;
			x.f *= y.f ;
			break ;

		case '/':
			float2 (&x, &y) ;
			x.f /= y.f ;
			break ;

		case '^':
			if (y.i.t == 0)
			    {
				if (y.i.n == 0)
				    {
					y.i.n = 1 ;
					return y ;
				    }
				int n ;
				if (y.i.n == -1)
					n = 64 ;
				else
					n = __builtin_clzll(y.i.n) + __builtin_clzll(~y.i.n) ;
				if (n > 32)
				    {
					VAR v ;
					int yi = y.i.n ;
					v.i.t = 0 ;
					v.i.n = 1 ;

					if (yi < 0)
					    {
						yi = -yi ;
						x = math (v, '/', x) ;
					    }
					yi = yi << (n - 33) ;
					n = 65 - n ;
					while (n--)
					    {
						v = math (v, '*', v) ;
						if (yi < 0)
							v = math (v, '*', x) ;
						yi = yi << 1 ;
					    }
					return v ;
				    }
			    }
			float2 (&x, &y) ;
			x.f = powl(x.f, y.f) ;
			break ;

		case TDIV:
			fix2 (&x, &y) ;
			if (y.i.n == 0) error (18, NULL) ; // 'Division by zero'
			x.i.n /= y.i.n ;
			return x ;

		case TMOD:
			fix2 (&x, &y) ;
			if (y.i.n == 0) error (18, NULL) ; // 'Division by zero'
			x.i.n %= y.i.n ;
			return x ;

		case TAND:
			fix2(&x, &y) ;
			x.i.n &= y.i.n ;
			return x ;

		case TOR:
			fix2(&x, &y) ;
			x.i.n |= y.i.n ;
			return x ;

		case TEOR:
			fix2(&x, &y) ;
			x.i.n ^= y.i.n ;
			return x ;

		case TSUM:
			fix2(&x, &y) ;
			x.i.n += y.i.n ;
			return x ;

		case '<':
			if ((x.i.t == 0) && (y.i.t == 0))
				x.i.n = -(x.i.n < y.i.n) ;
			else
			    {
				float2 (&x, &y) ;
				x.i.n = -(x.f < y.f) ;
				x.i.t = 0 ; // must come after!
			    }
			return x ;

		case '=':
			if ((x.i.t == 0) && (y.i.t == 0))
				x.i.n = -(x.i.n == y.i.n) ;
			else
			    {
				float2 (&x, &y) ;
				x.i.n = -(x.f == y.f) ;
				x.i.t = 0 ; // must come after!
			    }
			return x ;

		case '>':
			if ((x.i.t == 0) && (y.i.t == 0))
				x.i.n = -(x.i.n > y.i.n) ;
			else
			    {
				float2 (&x, &y) ;
				x.i.n = -(x.f > y.f) ;
				x.i.t = 0 ; // must come after!
			    }
			return x ;

		case 'y':
			if ((x.i.t == 0) && (y.i.t == 0))
				x.i.n = -(x.i.n <= y.i.n) ;
			else
			    {
				float2 (&x, &y) ;
				x.i.n = -(x.f <= y.f) ;
				x.i.t = 0 ; // must come after!
			    }
			return x ;

		case 'z':
			if ((x.i.t == 0) && (y.i.t == 0))
				x.i.n = -(x.i.n != y.i.n) ;
			else
			    {
				float2 (&x, &y) ;
				x.i.n = -(x.f != y.f) ;
				x.i.t = 0 ; // must come after!
			    }
			return x ;

		case '{':
			if ((x.i.t == 0) && (y.i.t == 0))
				x.i.n = -(x.i.n >= y.i.n) ;
			else
			    {
				float2 (&x, &y) ;
				x.i.n = -(x.f >= y.f) ;
				x.i.t = 0 ; // must come after!
			    }
			return x ;

		case 4:
			fix2 (&x, &y) ;
			if (y.i.n > 63)
			    {
				x.i.n = 0 ;
				return x ;
			    }
			x.i.n = x.i.n << y.i.n ;
			if ((liston & BIT2) == 0)
				x.i.n = (x.i.n << 32) >> 32 ;
			return x ;

		case 5:
			fix2 (&x, &y) ;
			if (y.i.n > 63)
			    {
				x.i.n = 0 ;
				return x ;
			    }
			x.i.n = x.i.n << y.i.n ;
			return x ;

		case 6:
			fix2 (&x, &y) ;
			if (y.i.n > 63)
				y.i.n = 63 ;
			x.i.n = x.i.n >> y.i.n ;
			return x ;

		case 7:
			fix2 (&x, &y) ;
			if (y.i.n > 63)
			    {
				x.i.n = 0 ;
				return x ;
			    }
			if (((liston & BIT2) == 0) && (y.i.n != 0))
				x.i.n &= 0xFFFFFFFF ;
			x.i.n = (unsigned long long) x.i.n >> y.i.n ;
			return x ;
	    }

	if (isinf(x.f) || isnan(x.f) || (x.i.t == -1) || errno)
	    {
		if (op == '/')
			error (18, NULL) ; // 'Division by zero'
		else if (op == '^')
			error (22, NULL) ; // 'Logarithm range'
		else
			error (20, NULL) ; // 'Number too big'
	    }

	if ((x.i.t == 0) || (x.i.t == (short) 0x8000))
		x.f = 0.0 ; // Underflow

	return x ;
}

// Get a possibly signed numeric constant from string accumulator:
VAR val (void)
{
	VAR v ;
	signed char *tmpesi = esi ;
	esi = (signed char *) accs ;
	while (*esi == ' ') esi++ ;
	if (*esi == '-')
	    {
		VAR z ;
		z.i.n = 0 ;
		z.i.t = 0 ;
		esi++ ;
		v = math (z, '-', con ()) ;
	    }
	else
	    {
		if (*esi == '+') esi++ ;
		v = con () ;
	    }
	esi = tmpesi ;
	return v ;
}

VAR item (void) ;
VAR expr (void) ;

VAR items (void)
{
	VAR v = item () ;
	if (v.s.t != -1)
		error (6, NULL) ; // 'Type mismatch'
	return v ;
}

static VAR itemn (void)
{
	VAR v = item () ;
	if (v.s.t == -1)
		error (6, NULL) ; // 'Type mismatch'
	return v ;
}

static VAR itemf (void)
{
	VAR v = item () ;
	if (v.s.t == -1)
		error (6, NULL) ; // 'Type mismatch'
	if (v.i.t == 0)
	    {
		v.i.t = 1 ; // ARM
		v.f = v.i.n ;
	    }
	return v ;
}

long long itemi (void)
{
	VAR v = item () ;
	if (v.s.t == -1)
		error (6, NULL) ; // 'Type mismatch'
	if (v.i.t != 0)
	    {
		long long t = v.f ;
		if (t != truncl (v.f))
			error (20, NULL) ; // 'Number too big'
		v.i.n = t ;
	    }
	return v.i.n ;
}

VAR exprs (void)
{
	VAR v = expr () ;
	if (v.s.t != -1)
		error (6, NULL) ; // 'Type mismatch'
	return v ;
}

VAR exprn (void)
{
	VAR v = expr () ;
	if (v.s.t == -1)
		error (6, NULL) ; // 'Type mismatch'
	return v ;
}

long long expri (void)
{
	VAR v = expr () ;
	if (v.s.t == -1)
		error (6, NULL) ; // 'Type mismatch'
	if (v.i.t != 0)
	    {
		long long t = v.f ;
		if (t != truncl (v.f))
			error (20, NULL) ; // 'Number too big'
		v.i.n = t ;
	    }
	return v.i.n ;
}

void *channel (void)
{
	if (nxt () != '#')
		error (45, NULL) ; // 'Missing #'
	esi++ ;
	return (void *) (size_t) itemi () ;
}

static int dimfunc (void)
{
	unsigned int d, n ;
	void *ptr ;
	unsigned char type ;
	if (nxt () == '(')
	    {
		esi++ ;
		n = dimfunc () ;
		braket () ;
		return n ;
	    }
	ptr = getvar (&type) ;
	if (ptr == NULL)
		error (16, NULL) ; // 'Syntax error'
	if (type == 0)
		error (26, NULL) ; // 'No such variable'
	if ((type & (BIT4 | BIT6)) == 0)
		error (6, NULL) ; // 'Type mismatch'
	if ((type & BIT6) == 0)
	    {
		ptr = VLOAD(ptr) ; // Structure format ptr
		if (ptr < (void *)2)
			error (56, NULL) ; // 'Bad use of structure'
		return ILOAD(ptr) ;
	    }
	ptr = VLOAD(ptr) ;
	if (ptr < (void *)2)
		error (14, NULL) ; // 'Bad use of array'
	d = *(unsigned char *)ptr ;
	if (*esi != ',')
		return d;
	esi++ ;
	n = expri () - 1 ;
	if (n > (d - 1 + (d == 0)))
		error (15, NULL) ; // 'Bad subscript'
	return ILOAD(ptr + 1 + n * 4) - 1 ;
}

VAR item (void)
{
	VAR v ;
	signed char al = nxt () ;
	esi++ ;
	switch (al)
	    {
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		auto void nestedFN(void);
		auto void nestedUSR(void);
		auto void nestedPTR(void);
		auto void nestedGETS(void);
		auto void nestedLOG(void);
		auto void nestedMOD(void);
		auto void nestedSUM(void);
		auto void nestedMID(void);
		auto void nestedADDR(void);
		auto void nestedEVAL(void);
		auto void nestedLEFT(void);
		auto void nestedTINT(void);
		auto void nestedINSTR(void);
		auto void nestedPOINT(void);
		auto void nestedRIGHT(void);
		auto void nestedSTRING(void);
		auto VAR nesteddefault(void);
#endif

/************************* Parenthesised expression ****************************/

		case '(':
			v = expr () ;
			if (nxt () != ')')
				error (27, NULL) ; // 'Missing )'
			esi++ ;
			return v ;

/************************************* FN **************************************/

		case TFN:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedFN();
		void __attribute__ ((noinline)) nestedFN(void)
#endif
			{
			int errcode = 0 ;
			jmp_buf savenv ;
			heapptr* savesp ;

			procfn (TFN) ;

			memcpy (&savenv, &env, sizeof(env)) ;
			savesp = esp;
			errcode = (setjmp (env)) ; // <0 : QUIT, >0 : error, 256: END
			if (errcode)
			    {
				if ((errcode < 0) || (errcode > 255) || (errtrp == 0) ||
					(onersp == NULL) || (onersp > savesp))
				    {
					memcpy (&env, &savenv, sizeof(env)) ;
					longjmp (env, errcode) ;
				    }
				esi = errtrp + (signed char *) zero ;
				esp = onersp ;
			    }

			v = xeq () ;
			memcpy (&env, &savenv, sizeof(env)) ;
			}
			return v ;

/************************************* USR *************************************/

		case TUSR:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedUSR();
		void __attribute__ ((noinline)) nestedUSR(void)
#endif
			{
			int (*func) (int,int,int,int,int,int) ;
			size_t n = itemi () ;

			v.i.t = 0 ;
			if ((n >= 0x8000) && (n <= 0xFFFF))
				v.i.n = oscall (n) ;
			else
			    {
				func = (void *) n ;
				v.i.n = func (stavar[1], stavar[2], stavar[3],
					      stavar[4], stavar[5], stavar[6]) ;
			    }
			}
			return v ;

/************************************ FALSE ************************************/

		case TFALSE:
			v.i.n = 0 ;
			v.i.t = 0 ;
			return v ;

/************************************ TRUE *************************************/

		case TTRUE:
			v.i.n = -1 ;
			v.i.t = 0 ;
			return v ;

/*********************************** OPENIN ************************************/

		case TOPENIN:
			v = items () ;
			fixs (v) ;
			v.i.t = 0 ;
			v.i.n = (size_t) osopen (0, accs) ;
			return v ;

/*********************************** OPENOUT ***********************************/

		case TOPENOUT:
			v = items () ;
			fixs (v) ;
			v.i.t = 0 ;
			v.i.n = (size_t) osopen (1, accs) ;
			return v ;

/*********************************** OPENUP ************************************/

		case TOPENUP:
			v = items () ;
			fixs (v) ;
			v.i.t = 0 ;
			v.i.n = (size_t) osopen (2, accs) ;
			return v ;

/************************************* PTR *************************************/

		case TPTRR:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedPTR();
		void __attribute__ ((noinline)) nestedPTR(void)
#endif
		    {
			if (*esi == '(')
			    {
				void *ptr ;
				unsigned char type ;
				esi++ ;
				nxt () ;
				ptr = getvar (&type) ;
				if (ptr == NULL)
					error (16, NULL) ; // 'Syntax error'
				if (type == 0)
					error (26, NULL) ; // 'No such variable'
				braket () ;
				v.i.t = 0 ;
				if (type == 136)
					v.i.n = ULOAD(ptr) + (size_t) zero ;
				else if ((type == 36) || (type & 0x40))
					v.i.n = TLOAD(ptr) ;
				else if (type == STYPE)
					v.i.n = TLOAD(ptr + sizeof (void *)) ;
				else
					error (6, NULL) ; // 'Type mismatch'
			    }
			else
			    {
				void *n = channel () ;
				v.i.t = 0 ;
				v.i.n = getptr (n) ;
			    }
		    }
		return v ;

/************************************* EXT *************************************/

		case TEXTR:
			v.i.t = 0 ;
			v.i.n = getext (channel ()) ;
			return v ;

/************************************* EOF *************************************/

		case TEOF:
			v.i.t = 0 ;
			v.i.n = geteof (channel ()) ;
			return v ;

/************************************ BGET *************************************/

		case TBGET:
			v.i.t = 0 ;
			v.i.n = osbget (channel (), NULL) ;
			return v ;

/************************************ PAGE *************************************/

		case TPAGER:
			v.i.n = vpage + (size_t) zero ;
			v.i.t = 0 ;
			return v ;

/************************************* TOP *************************************/

		case TTO:
			if ((*esi == 'P') || (((liston & BIT3) != 0) && (*esi == 'p')))
			    {
				esi++ ;
				v.i.n = (size_t) gettop (vpage + (signed char *) zero, NULL) + 3 ;
				v.i.t = 0 ;
				return v ;
			    }
			else
				error (16, NULL) ; // 'Syntax error'

/************************************ LOMEM ************************************/

		case TLOMEMR:
			v.i.n = lomem + (size_t) zero ;
			v.i.t = 0 ;
			return v ;

/************************************* END *************************************/

		case TEND:
			v.i.n = pfree + (size_t) zero ;
			v.i.t = 0 ;
			return v ;

/************************************ HIMEM ************************************/

		case THIMEMR:
			v.i.n = himem + (size_t) zero ;
			v.i.t = 0 ;
			return v ;

/************************************ MODE *************************************/

		case TMODE:
			v.i.n = getmodeno() ;
			v.i.t = 0 ;
			return v ;

/************************************ TIME *************************************/

		case TTIMER:
			if (*esi == '$')
			    {
				esi++ ;
				v.s.t = -1 ;
				v.s.p = accs - (char *) zero ;
				v.s.l = getims () ;
				return v ;
			    }
			v.i.n = (int) getime () ; // Must not overflow 32-bit int
			v.i.t = 0 ;
			return v ;

/************************************ ADVAL ************************************/

		case TADVAL:
			v.i.n = adval (itemi ()) ;
			v.i.t = 0 ;
			return v ;

/************************************ COUNT ************************************/

		case TCOUNT:
			v.i.n = vcount ;
			v.i.t = 0 ;
			return v ;

/************************************ WIDTH ************************************/

		case TWIDTH:
			if (*esi == '(')
			    {
				esi++ ;
				v = exprs () ;
				braket () ;
				if (v.s.l > ((char *)esp - (char *)zero - pfree - STACK_NEEDED))
					error (0, NULL) ; // 'No room'
				v.i.n = widths (v.s.p + (char *) zero, v.s.l) ;
				v.i.t = 0 ;
				return v ;
			    }
			v.i.n = vwidth ;
			v.i.t = 0 ;
			return v ;

/************************************* DIM *************************************/

		case TDIM:
			v.i.t = 0 ;
			v.i.n = dimfunc () ;
			return v ;

/************************************* GET *************************************/

		case TGET:
			{
			if (*esi == '(')
			    {
				int x, y ;
				esi++ ;
				x = expri () ;
				comma () ;
				y = expri () ;
				braket () ;
				v.i.n = vgetc (x, y) ;
				v.i.t = 0 ;
			    }
			else
			    {
				v.i.n = osrdch () ;
				v.i.t = 0 ;
			    }
			}
			return v ;

/************************************ GET$ *************************************/

		case TGETS:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedGETS();
		void __attribute__ ((noinline)) nestedGETS(void)
#endif
		    {
			if (*esi == '#')
			    {
				char *p ;
				int term = 0x100 ;
				int count = 0x7FFFFFFF ;
				void *chan = channel () ;
				if (nxt () == TBY)
				    {
					esi++ ;
					term = -1 ;
					count = itemi () ;
				    }
				else if (*esi == TTO)
				    {
					esi++ ;
					term = itemi () ;
				    }
				allocs (&tmps, 0) ; // Free tmps (may change pfree)	
				p = pfree + (char *) zero ;
				while (count--)
				    {
					int eof ;
					char al = osbget (chan, &eof) ;
					if (eof) break ;
					if (term >= 0)
					    {
						if (al == (term & 0xFF)) break ;
						if ((al == 0x0D) && (term & 0x8100)) break ;
						if ((al == 0x0A) && (term & 0x100)) break ;
					    }
					*p++ = al ;
					if (p > ((char *) esp - STACK_NEEDED))
						error (0, NULL) ; // 'No room'
				    }
				v.s.t = -1 ;
				v.s.p = pfree ;
				v.s.l = p - (char *) zero - pfree ;
				v.s.p = moves ((STR *) &v, 0) - (char *) zero ;
			    }
			else if (*esi == '(')
			    {
				int c, x, y ;
				esi++ ;
				x = expri () ;
				comma () ;
				y = expri () ;
				braket () ;
				c = vgetc (x, y) ;
				ISTORE(accs, c) ;
				v.s.t = -1 ;
				v.s.p = accs - (char *) zero ;
				if (c < 0)
					v.s.l = 0 ;
				else
					v.s.l = strlen (accs) ;
			    }
			else
			    {
				*accs = osrdch () ;
				v.s.t = -1 ;
				v.s.p = accs - (char *) zero ;
				v.s.l = 1 ;
			    }
		    }
		return v ;

/************************************ INKEY ************************************/

		case TINKEY:
			v.i.t = 0 ;
			v.i.n = oskey (itemi ()) ;
			return v ;

/*********************************** INKEY$ ************************************/

		case TINKEYS:
			{
			int n = oskey (itemi ()) ;
			*accs = n ;
			v.s.t = -1 ;
			v.s.p = accs - (char *) zero ;
			v.s.l = (n != -1) ;
			}
			return v ;

/************************************* POS *************************************/

		case TPOS:
			{
			int x ;
			getcsr (&x, NULL) ;
			v.i.t = 0 ;
			v.i.n = x ;
			}
			return v ;

/************************************ VPOS *************************************/

		case TVPOS:
			{
			int y ;
			getcsr (NULL, &y) ;
			v.i.t = 0 ;
			v.i.n = y ;
			}
			return v ;

/************************************ TINT *************************************/

		case TTINT:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedTINT();
		void __attribute__ ((noinline)) nestedTINT(void)
#endif
			{
			int p, x, y ;
			if (*esi++ != '(')
				error (16, NULL) ; // 'Syntax error'
			x = expri () ;
			comma () ;
			y = expri () ;
			braket () ;
			p = vtint (x, y) ;
			v.i.t = 0 ;
			v.i.n = p ;
			}
			return v ;

/*********************************** TPOINT ************************************/

		case TPOINT:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedPOINT();
		void __attribute__ ((noinline)) nestedPOINT(void)
#endif
			{
			int p, x, y ;
			x = expri () ;
			comma () ;
			y = expri () ;
			braket () ;
			p = vpoint (x, y) ;
			v.i.t = 0 ;
			v.i.n = p ;
			}
			return v ;

/************************************* ASC *************************************/

		case TASC:
			v = items () ;
			if (v.s.l)
				v.i.n = *(v.s.p + (unsigned char *) zero) ;
			else
				v.i.n = -1 ;
			v.i.t = 0 ;
			return v ;

/************************************* ERR *************************************/

		case TERR:
			v.i.n = errnum ;
			v.i.t = 0 ;
			return v ;

/************************************* ERL *************************************/

		case TERL:
			v.i.n = setlin (errlin + (signed char *) zero , NULL) ;
			v.i.t = 0 ;
			return v ;

/************************************* PI **************************************/

		case TPI:
			if (*esi == '#') esi++ ;
#if defined __arm__ || defined __aarch64__ || defined __EMSCRIPTEN__ || defined __ANDROID__
			v.i.n = 0x400921FB54442D18L ;
			v.i.t = 1 ;
#else
			v.i.n = 0xC90FDAA22168C235L ;
			v.i.t = 0x4000 ;
#endif
			return v ;

/************************************* SIN *************************************/

		case TSIN:
			v = itemf () ;
			errno = 0 ;
			v.f = sinl (v.f) ;
			break ;

/************************************* COS *************************************/

		case TCOS:
			v = itemf () ;
			errno = 0 ;
			v.f = cosl (v.f) ;
			break ;

/************************************* TAN *************************************/

		case TTAN:
			v = itemf () ;
			errno = 0 ;
			v.f = tanl (v.f) ;
			break ;

/************************************* ASN *************************************/

		case TASN:
			v = itemf () ;
			errno = 0 ;
			v.f = asinl (v.f) ;
			break ;

/************************************* ACS *************************************/

		case TACS:
			v = itemf () ;
			errno = 0 ;
			v.f = acosl (v.f) ;
			break ;

/************************************* ATN *************************************/

		case TATN:
			v = itemf () ;
			errno = 0 ;
			v.f = atanl (v.f) ;
			break ;

/************************************* DEG *************************************/

		case TDEG:
#if defined __arm__ || defined __aarch64__ || defined __EMSCRIPTEN__ || defined __ANDROID__
			v.i.n = 0x404CA5DC1A63C1F8L ;
			v.i.t = 1 ;
#else
			v.i.n = 0xE52EE0D31E0FBDC3L ;
			v.i.t = 0x4004 ;
#endif
			return math (itemf(), '*', v) ;

/************************************* RAD *************************************/

		case TRAD:
#if defined __arm__ || defined __aarch64__ || defined __EMSCRIPTEN__ || defined __ANDROID__
			v.i.n = 0x3F91DF46A2529D39L ;
			v.i.t = 1 ;
#else
			v.i.n = 0x8EFA351294E9C8AEL ;
			v.i.t = 0x3FF9 ;
#endif
			return math (itemf(), '*', v) ;

/************************************* EXP *************************************/

		case TEXP:
			v = itemf () ;
			errno = 0 ;
			v.f = expl (v.f) ;
			break ;

/************************************* LN **************************************/

		case TLN:
			v = itemf () ;
			errno = 0 ;
			v.f = logl (v.f) ;
			break ;

/************************************* LOG *************************************/

		case TLOG:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedLOG();
		void __attribute__ ((noinline)) nestedLOG(void)
#endif
			{
			VAR loge ;
#if defined __arm__ || defined __aarch64__ || defined __EMSCRIPTEN__ || defined __ANDROID__
			loge.i.n = 0x3FDBCB7B1526E50EL ;
			loge.i.t = 1 ;
#else
			loge.i.n = 0xDE5BD8A937287195L ;
			loge.i.t = 0x3FFD ;
#endif
			v = itemf () ;
			v.f = logl (v.f );
			if (isnan (v.f) || isinf (v.f))
				error (22, NULL) ; // 'Logarithm range'
			v = math (v, '*', loge) ;
			}
			return v ;

/************************************* SQR *************************************/

		case TSQR:
			v = itemf () ;
			errno = 0 ;
			v.f = sqrtl (v.f) ;
			break ;

/************************************* MOD *************************************/

		case TMOD:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedMOD();
		void __attribute__ ((noinline)) nestedMOD(void)
#endif
			{
			int i, count ;
			unsigned char type ;
			void *ptr = getvbr (&type) ;
			if (ptr == NULL)
				error (16, NULL) ; // 'Syntax error'
			if (type == 0)
				error (26, NULL) ; // 'No such variable'
			if ((type >= 128) || (type < 64))
				error (6, NULL) ; // 'Type mismatch'
			ptr = VLOAD(ptr) ;
			count = arrlen (&ptr) ;
			v.i.t = 0 ;
			v.i.n = 0 ;
			type &= ~BIT6 ;
			for (i = 0; i < count; i++)
			    {
				VAR x = loadn (ptr, type) ; // n.b. type can be 40
#if !(defined(__GNUC__) && (__GNUC__ >= 7) || defined(__clang__) && (__clang_major__ >= 4))
				v = math (v, '+', math (x, '*', x)) ;
#else
				long long tmp ;
				if ((x.i.t == 0) && ! __builtin_smulll_overflow (x.i.n, x.i.n, &tmp))
					x.i.n = tmp ;
				else
				    {
					xfloat (&x) ;
					x.f *= x.f ;
					if (x.i.t == 0)
						x.f = 0.0 ; // Underflow
				    }
				if ((v.i.t == 0) && (x.i.t == 0) &&
				    ! __builtin_saddll_overflow (v.i.n, x.i.n, &tmp))
					v.i.n = tmp ;
				else
				    {
					float2 (&v, &x) ;
					v.f += x.f ;
					if (isinf(v.f) || (v.i.t == -1))
						error (20, NULL) ; // 'Number too big'
				    }
#endif
				ptr += type & TMASK ;
			    }
			if (v.i.t == 0)
			    {
				v.i.t = 1 ; // ARM
				v.f = v.i.n ;
			    }
			v.f = sqrtl (v.f) ;
			}
			return v;

/************************************* SUM *************************************/

		case TSUM:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedSUM();
		void __attribute__ ((noinline)) nestedSUM(void)
#endif
			{
			VAR x ;
			int count ;
			unsigned char type ;
			void *ptr ;
			int sumlen = (nxt () == TLEN) ;
			if (sumlen) esi++ ;

			ptr = getvbr (&type) ;
			if (ptr == NULL)
				error (16, NULL) ; // 'Syntax error'
			if ((type & 64) == 0)
				error (6, NULL) ; // 'Type mismatch'
			ptr = VLOAD(ptr) ;
			count = arrlen (&ptr) ;
			if (type < 128)
			    {
				if (sumlen)
					error (6, NULL) ; // 'Type mismatch'
				v.i.t = 0 ;
				v.i.n = 0 ;
				type &= ~BIT6 ;
				while (count--)
				    {
#if !(defined(__GNUC__) && (__GNUC__ >= 7) || defined(__clang__) && (__clang_major__ >= 4))
					v = math (v, '+', loadn (ptr, type)) ; // n.b. type can be 40
#else
					long long sum ;
					x = loadn (ptr, type) ; // n.b. type can be 40
					if ((v.i.t == 0) && (x.i.t == 0) &&
					    (! __builtin_saddll_overflow (v.i.n, x.i.n, &sum)))
						v.i.n = sum ;
					else
					    {
						float2 (&v, &x) ;
						v.f += x.f ;
						if (isinf(v.f) || (v.i.t == -1))
							error (20, NULL) ; // 'Number too big'
						if ((v.i.t == 0) || (v.i.t == (short) 0x8000))
							v.f = 0.0 ; // Underflow
					    }
#endif
					ptr += type & TMASK ;
				    }
			    }
			else if (sumlen)
			    {
				int n = 0 ;
				type &= ~BIT6 ;
				while (count--)
				    {
					x = loads (ptr, type) ;
					ptr += 8 ;
					n += x.s.l ;
				    }
				v.i.t = 0 ;
				v.i.n = n ;
			    }
			else
			    {
				int i, n = 0 ;
				void *tmp = ptr ;
				type &= ~BIT6 ;
				for (i = 0 ; i < count; i++)
				    {
					x = loads (tmp, type) ;
					tmp += 8 ;
					n += x.s.l ;
				    }
				v.s.t = -1 ;
				v.s.l = n ;
				v.s.p = alloct (n) - (char *) zero ;
				n = 0 ;
				while (count--)
				    {
					x = loads (ptr, type) ;
					ptr += 8 ;
					if (x.s.l)
						memcpy (v.s.p + n + zero, x.s.p + zero, x.s.l) ;
					n += x.s.l ;
				    }
			    }
			}
			return v ;

/************************************* ABS *************************************/

		case TABS:
			v = itemn () ;
			if (v.i.t)
				v.f = fabsl (v.f) ; // both double and long double
			else if (v.i.n == 0x8000000000000000)
			    {
				v.i.t = 1 ; // ARM
				v.f = 9223372036854775808.0L ;
			    }
			else
				v.i.n = llabs (v.i.n) ;
			return v ;

/************************************* SGN *************************************/

		case TSGN:
			v = itemn () ;
			if (v.i.t)
				v.i.n = (v.f > 0) - (v.f < 0) ;
			else
				v.i.n = (v.i.n > 0) - (v.i.n < 0) ;
			v.i.t = 0 ;
			return v ;

/************************************* INT *************************************/

		case TINT:
			{
			long long tmp ;
			v = itemf () ;
			v.f = floorl (v.f) ;
			tmp = v.f ;
			if (v.f == tmp)
			    {
				v.i.n = tmp ;
				v.i.t = 0 ;
			    }
			}
			return v ;

/************************************* NOT *************************************/

		case TNOT:
			v.i.t = 0 ;
			v.i.n = ~itemi () ;
			return v ;

/************************************* LEN *************************************/

		case TLEN:
			v = items () ;
			v.i.n = v.s.l ;
			v.i.t = 0 ;
			return v ;

/************************************* RND *************************************/

		case TRND:
			v.i.t = 0 ;
			if (*esi == '(')
			    {
				int n ;
				esi++ ;
				n = expri () ;
				braket () ;

				if (n == 0)
				    {
					unsigned int edx = (prand.l >> 16) | (prand.l << 16) ;
					v.i.t = 1 ; // ARM
					v.f = (double)edx / 4294967296.0L ;
				    }
				else if (n == 1)
				    {
					unsigned int edx = rnd () ;
					edx = (edx >> 16) | (edx << 16) ;
					v.i.t = 1 ; // ARM
					v.f = (double)edx / 4294967296.0L ;
				    }
				else if (n > 1)
					v.i.n = (rnd() % n) + 1 ;
				else 
				    {
					prand.l = (unsigned int) n ;
					prand.h = (n & 0x80000) == 0 ;
					v.i.n = n ;
				    }
				return v ;
			    }
			v.i.n = (signed int) rnd () ;
			return v;

/************************************* VAL *************************************/

		case TVAL:
			v = items () ;
			fixs (v) ;
			return val () ;

/************************************ EVAL *************************************/

		case TEVAL:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedEVAL();
		void __attribute__ ((noinline)) nestedEVAL(void)
#endif
			{
			signed char *oldesi ;
			heapptr *oldesp ;
			v = items () ;
			fixs (v) ;
			lexan (accs, accs, 0) ; // assumes string gets no longer
			v.s.p = accs - (char *) zero ;
			v.s.l += 1 ;
			oldesp = pushs (v) ;
			oldesi = esi ;
			esi = (signed char *) esp ;
			v = expr () ;
			esi = oldesi ;
			esp = oldesp ;
			}
			return v ;

/************************************ STR$ *************************************/

		case TSTR:
			if (nxt () == '~')
			    {
				esi++ ;
				v.s.l = strhex (itemn (), accs, 0) ;
			    }
			else
			    {
				v = itemn () ;
				if (stavar[0] & 0xFF000000)
					v.s.l = str (v, accs, stavar[0] & 0xFFFF00) ;
				else
					v.s.l = str (v, accs, 0x900) ;
			    }
			v.s.p = accs - (char *) zero ;
			v.s.t = -1 ;
			return v ;

/************************************ CHR$ *************************************/

		case TCHR:
			*accs = itemi () ;
			v.s.t = -1 ;
			v.s.p = accs - (char *) zero ;
			v.s.l = 1 ;
			return v ;

/************************************ MID$ *************************************/

		case TMID:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedMID();
		void __attribute__ ((noinline)) nestedMID(void)
#endif
			{
			heapptr *oldesp ;
			char * tmp ;
			int s, n ;

			v = exprs () ;
			n = v.s.l ;
			comma () ;
			oldesp = pushs (v) ;

			s = expri () ;
			if (s < 1)
				s = 1 ;

			if (*esi == ',')
			    {
				esi++ ;
				n = expri () ;
				if (n < 0)
					n = v.s.l ;
			    }
			braket () ;

			if (n > ((int)v.s.l - s + 1))
				n = (int)v.s.l - s + 1 ;
			if (n < 0)
				n = 0 ;
			if (n == 0)
			    {
				esp = oldesp ;
				v.s.p = 0 ;
				v.s.l = 0 ;
			    }
			else
			    {
				v.s.p = (char *) esp - (char *) zero + s - 1 ;
				v.s.l = n ;
				tmp = moves ((STR*) &v, 0) ;
				esp = oldesp ;
				v.s.p = tmp - (char *) zero ;
			    }
			}
			return v ;

/*********************************** LEFT$ *************************************/

		case TLEFT:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedLEFT();
		void __attribute__ ((noinline)) nestedLEFT(void)
#endif
			{
			heapptr *oldesp ;
			char * tmp ;
			int n ;

			v = exprs () ;
			if (*esi != ',')
			    {
				braket () ;
				if (v.s.l)
					v.s.l -= 1 ;
			    }
			else
			    {
				esi++ ;
				oldesp = pushs (v) ;
				n = expri () ;
				braket () ;

				if ((n < 0) || (n > v.s.l))
					n = v.s.l ;

				v.s.p = (char *) esp - (char *) zero ;
				v.s.l = n;
				tmp = moves ((STR*) &v, 0) ;
				esp = oldesp ;
				v.s.p = tmp - (char *) zero ;
			    }
			}
			return v ;

/*********************************** RIGHT$ ************************************/

		case TRIGHT:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedRIGHT();
		void __attribute__ ((noinline)) nestedRIGHT(void)
#endif
			{
			heapptr *oldesp ;
			char * tmp ;
			int n ;

			v = exprs () ;
			if (*esi != ',')
			    {
				braket () ;
				if (v.s.l > 1)
				    {
					v.s.p += (int)v.s.l - 1 ;
					v.s.l = 1 ;
				    }
			    }
			else
			    {
				esi++ ;
				oldesp = pushs (v) ;
				n = expri () ;
				braket () ;

				if ((n < 0) || (n > v.s.l))
					n = v.s.l ;

				v.s.p = (char *) esp - (char *) zero + v.s.l - n ;
				v.s.l = n;
				tmp = moves ((STR*) &v, 0) ;
				esp = oldesp ;
				v.s.p = tmp - (char *) zero ;
			    }
			}
			return v ;

/*********************************** STRING$ ***********************************/

		case TSTRING:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedSTRING();
		void __attribute__ ((noinline)) nestedSTRING(void)
#endif
			{
			char *tmp, *p ;
			long long size, n = expri () ;
			comma () ;
			v = exprs () ;
			braket () ;
			if ((n <= 0) || (v.s.l == 0))
			    {
				v.s.p = 0 ;
				v.s.l = 0 ;
			    }
			else
			    {
				size = n * v.s.l ;
				if (size > 0x7FFFFFFF)
					error (19, NULL) ; // 'String too long'
				tmp = alloct (size) ;
				for (p = tmp ; n-- ; p += v.s.l)
					memcpy (p, v.s.p + zero , v.s.l) ;
				v.s.p = tmp - (char *) zero ;
				v.s.l = size ;
			    }
			}
			return v ;

/*********************************** INSTR *************************************/

		case TINSTR:
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedINSTR();
		void __attribute__ ((noinline)) nestedINSTR(void)
#endif
			{
			heapptr *oldesp ;
			int n = 0 ;
			char *p ;
			VAR x = exprs () ;
			oldesp = pushs (x) ;
			x.s.p = (char *) esp - (char *) zero ;
			comma () ;
			v = exprs () ;
			pushs (v) ;
			v.s.p = (char *) esp - (char *) zero ;
			if (*esi == ',')
			    {
				esi++ ;
				n = expri () - 1 ;
				if (n < 0)
					n = 0 ;
			    }
			braket () ;

			if ((x.s.l == 0) || (v.s.l == 0) || ((n + v.s.l) > x.s.l))
			    {
				v.i.t = 0 ;
				v.i.n = 0 ;
				esp = oldesp ;
			    }
			else
			    {
				p = x.s.p + n + (char *) zero ;
				n = 0 ;
				while (p != NULL)
				    {
					if (0 == memcmp (p, v.s.p + zero, v.s.l))
					    {
						n = p - (char *) zero - x.s.p + 1 ;
						break ;
					    }
					p = memchr (p + 1, *(v.s.p + (char *) zero),
							x.s.p + x.s.l - v.s.l + (char *) zero - p) ;
				    }
				v.i.t = 0 ;
				v.i.n = n ;
				esp = oldesp ;
			    }
			}
			return  v ;

/*********************************** REPORT ************************************/

		case TREPORT:
			if (nxt () != '$')
				error (16, NULL) ; // Syntax error
			esi++ ;
			report () ;
			v.s.t = -1 ;
			v.s.p = accs - (char *) zero ;
			v.s.l = strlen (accs) ;
			return v ;

/******************************* Line number ***********************************/

		case TLINO:
			{
			unsigned char ah = *(unsigned char *)esi++ ;
			v.i.t = 0 ;
			v.i.n = ((*(unsigned char *)esi++) ^ ((ah << 2) & 0xC0)) ;
			v.i.n += ((*(unsigned char *)esi++) ^ ((ah << 4) & 0xC0)) * 256 ; 
			} 
			return v ;


/************************************ SYS **************************************/

		case TSYS:
			v = items () ;
			if (v.s.l > 255)
				error (19, NULL) ; // 'String too long'
			memcpy (accs, v.s.p + zero, v.s.l) ;
			*(accs + v.s.l) = 0 ;
			v.i.t = 0 ;
			v.i.n = (intptr_t) sysadr (accs) ;
			return v ;

/*********************************** PROC **************************************/

		case TPROC:
			error (16, NULL) ; // 'Syntax error'

/*************************** Hexadecimal constant ******************************/

		case '&':
			{
			signed char *p = esi ;
			v.i.n = 0 ;
			while (1)
			    {
				signed char x = *esi ;
				signed char d ;
				if ((x >= '0') && (x <= '9')) d = x - 48 ;
				else if ((x >= 'A') && (x <= 'F')) d = x - 55 ;
				else if ((x >= 'a') && (x <= 'f')) d = x - 87 ;
				else break ;
				v.i.n = (v.i.n << 4) | d ;
				esi++ ;
			    }
			if (p == esi)
				error (28, NULL) ; // 'Bad hex or binary'
			if ((liston & BIT2) == 0)
				v.i.n = (v.i.n << 32) >> 32 ;
			v.i.t = 0 ;
			}
			return v ;

/****************************** Binary constant ********************************/

		case '%':
			{
			signed char al = *esi++ ;
			v.i.n = 0 ;
			if ((al != '0') && (al != '1'))
				error (28, NULL) ; // 'Bad hex or binary'
			while ((al == '0') || (al == '1'))
			    {
				v.i.n = (v.i.n << 1) | (al - '0') ;
				al = *esi++ ;
			    } 
			if ((liston & BIT2) == 0)
				v.i.n = (v.i.n << 32) >> 32 ;
			v.i.t = 0 ;
			esi-- ;
			}
			return v ;

/********************************* Unary plus **********************************/

		case '+':
			return itemn () ; // unary plus

/******************************** Unary minus **********************************/

		case '-':
			v = itemn () ;
			if (v.i.t)
				v.f = -v.f ; // Both double and long-double
			else if (v.i.n == 0x8000000000000000)
			    {
				v.i.t = 1 ; // ARM
				v.f = 9223372036854775808.0L ;
			    }
			else
				v.i.n = -v.i.n ;
			return v ;

/************************** Quoted (constant) string ***************************/

		case '"':
			return cons () ;

/***************************** Address-of operator *****************************/

		case '^':
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		nestedADDR();
		void __attribute__ ((noinline)) nestedADDR(void)
#endif
			{
			unsigned char type ;
			void *ptr = getput (&type) ;
			v.i.t = 0 ;
			v.i.n = (size_t) ptr ;
			}
			return v ;

/************************ Decimal constant or variable *************************/

		default:
			esi-- ;
			if (((al >= '0') && (al <= '9')) || (al == '.'))
				return con() ;
#if defined (REDUCE_STACK_SIZE) && defined(__GNUC__) && !defined(__llvm__)
		return nesteddefault();
		VAR __attribute__ ((noinline)) nesteddefault(void)
#endif
			{
			unsigned char type ;
			signed char *savesi = esi ;
			void *ptr = getvar (&type) ;
			if (ptr == NULL)
				error (16, NULL) ; // 'Syntax error'
			if (type == 0)
			    {
				signed char *edi = vpage + (signed char *) zero ;
				if ((liston & BIT5) == 0)
				    {
					while (range0 (*++esi)) ;
					v.i.t = 0 ;
#if defined(__x86_64__) || defined(__aarch64__)
					v.i.n = (unsigned int) stavar[16] + ((long long) stavar[17] << 32) ;
#else
					v.i.n = stavar[16] ;
#endif
					return v ;
				    }
				while ((*(unsigned char*)edi != 0) &&
					((edi = search (edi, '(')) != NULL))
				    {
					signed char *oldesi = esi ;
					esi = edi + 1 ;
					edi -= 3 ;
					ptr = getvar (&type) ;
					if ((ptr != NULL) && (type == 0))
						ptr = putvar (ptr, &type) ;
					if (type)
					    {
						v.i.t = 0 ;
						v.i.n = edi - (signed char *) zero ;
						storen (v, ptr, type) ;
					    }
					esi = oldesi ;
					edi += (int)*(unsigned char*)edi ;
				    }
				esi = savesi ;
				ptr = getvar (&type) ;
				if (type == 0)
					error (26, NULL) ; // 'No such variable'
			    }
			if (type & BIT6)
				error (14, NULL) ; // 'Bad use of array'
			if (type & BIT4)
			    {
				v.i.t = 0 ;
				v.i.n = (intptr_t) VLOAD(ptr + sizeof(void *)) ; // GCC extension: sizeof(void) = 1
				return v ;
			    }
			if (type < 128)
				return loadn (ptr, type) ;
			return loads (ptr, type) ;
			}
	    }

	if (isinf(v.f) || isnan(v.f) || (v.i.t == -1) || errno)
	    {
		if ((al == TLN) || (al == TLOG))
			error (22, NULL) ; // 'Logarithm range'
		else if (al == TSQR)
			error (21, NULL) ; // 'Negative root'
		else if (al == TEXP)
			error (24, NULL) ; // 'Exponent range'
		else
			error (20, NULL) ; // 'Number too big'
	    }

	if (v.i.t == 0)
		v.i.n = 0 ; // Underflow

	return v ;
}

// Check for relational or shift operator.
//           <   0x3C   =   0x3D   >   0x3E
//           <=  0x79   <>  0x7A   >=  0x7B
//           <<  0x04   <<< 0x05   >>  0x06   >>> 0x07

static signed char relop (void)
{
	unsigned char al, op = *esi ;
	if ((op != '<') && (op != '=') && (op != '>'))
		return 0 ;
	esi++ ;
 	al = nxt () ;
	if ((al != '<') && (al != '=') && (al != '>'))
		return op ; // < = >
	esi++ ;
	if (al != op)
		return op + al ; // <= <> >=
	if (al == '=')
		return op ; // == treated as =
	al = nxt () ;
	if (al == op)
	    {
		esi++ ;
		op += 1 ;
	    }
	return (op & 7) ; // << <<< >> >>>
}

// Evaluate an expression:
// Hierarchy: (1) Variables, functions, constants, parenthesized expressions.
//            (2) ^
//            (3) * / MOD DIV
//            (4) + -
//            (5) = <> <= >= > < << >> >>>
//            (6) AND
//            (7) EOR OR

// Level 5: ^:
static VAR expr5 (void)
{
	VAR x = item () ;
	signed char op = nxt () ;
	if (x.s.t == -1)
		return x ; // string
	while (1) 
	    {
		if (op == '^')
		    {
			esi++ ;
			VAR y = item () ;
			if (y.s.t == -1)
				error (6, NULL) ; // 'Type mismatch'
			x = math (x, op, y) ;
		    }
		else
			break ;
		op = nxt () ;
	    }
	return x;
}

// Level 4: *, /, MOD, DIV:
static VAR expr4 (void)
{
	VAR x = expr5 () ;
	if (x.s.t == -1)
		return x ; // string
	while (1) 
	    {
		signed char op = *esi ;
		if ((op == '*') || (op == '/') || (op == TMOD) || (op == TDIV))
		    {
			esi++ ;
			VAR y = expr5 () ;
			if (y.s.t == -1)
				error (6, NULL) ; // 'Type mismatch'
			x = math (x, op, y) ;
		    }
		else
			break ;
	    }
	return x;
}

// Level 3: +, -, SUM:
static VAR expr3 (void)
{
	VAR x = expr4 () ;
	if (x.s.t == -1)
	    {
		while (1)
		    {
			signed char op = *esi ;
			if (op == '+') // concatenate strings
			    {
				char *tmp ;
				heapptr *oldesp ;
				esi++ ;
				oldesp = pushs (x) ;
				VAR y = expr4 () ;
				if (y.s.t != -1)
					error (6, NULL) ; // 'Type mismatch'
				tmp = moves ((STR *) &y, x.s.l) ;
				memmove (tmp, esp, x.s.l) ;
				esp = oldesp ;
				x.s.p = tmp - (char *) zero ;
				x.s.l += y.s.l ;
			    }
			else
				break ;
		    }
		return x ;
	    }
	while (1) 
	    {
		signed char op = *esi ;
		if ((op == '+') || (op == '-') || (op == TSUM))
		    {
			esi++ ;
			VAR y = expr4 () ;
			if (y.s.t == -1)
				error (6, NULL) ; // 'Type mismatch'
			x = math (x, op, y) ;
		    }
		else
			break ;
	    }
	return x;
}

// Level 2: =, <, >, <>, <=, >=, <<, >>, >>>:
// (these operators do not chain)
static VAR expr2 (void)
{
	VAR x = expr3 () ;
	signed char op = relop () ;
	if (op == 0)
		return x ;
	if (x.s.t == -1)
	    {
		VAR y ;
		int n, r ;
		heapptr *oldesp ;

		oldesp = pushs (x) ;
		y = expr3 () ;
		if (y.s.t != -1)
			error (6, NULL) ; // 'Type mismatch'

		n = x.s.l ;
		if (n > y.s.l)
			n = y.s.l ;
		r = memcmp (esp, y.s.p + zero, n) ;
		if (r == 0)
			r = (x.s.l > y.s.l) - (x.s.l < y.s.l) ;
		esp = oldesp ;

		switch (op)
		    {
			case '<':
				x.i.n = -(r < 0) ;
				break ;
			case '=':
				x.i.n = -(r == 0) ;
				break ;
			case '>':
				x.i.n = -(r > 0) ;
				break ;
			case 'y':
				x.i.n = -(r <= 0) ;
				break ;
			case 'z':
				x.i.n = -(r != 0) ;
				break ;
			case '{':
				x.i.n = -(r >= 0) ;
				break ;
			default:
				error (16, NULL) ; // Syntax error
		    }
		x.i.t = 0 ;
	    }
	else
	    {
		VAR y = expr3 () ;
		if (y.s.t == -1)
			error (6, NULL) ; // 'Type mismatch'
		x = math (x, op, y) ;
	    }
	return x;
}

// Level 1: AND:
static VAR expr1 (void)
{
	VAR x = expr2 () ;
	if (x.s.t == -1)
		return x ; // string
	while (1)
	    {
		signed char op = *esi ;
		if (op == TAND)
		    {
			esi++ ;
			VAR y = expr2 () ;
			if (y.s.t == -1)
				error (6, NULL) ; // 'Type mismatch'
			x = math (x, op, y) ;
		    }
		else
			break ;
	    }
	return x;
}

// Level 0: OR, EOR:
VAR expr (void)
{
	VAR x = expr1 () ;
	if (x.s.t == -1)
		return x ; // string
	while (1) 
	    {
		signed char op = *esi ;
		if ((op == TOR) || (op == TEOR))
		    {
			esi++ ;
			VAR y = expr1 () ;
			if (y.s.t == -1)
				error (6, NULL) ; // 'Type mismatch'
			x = math (x, op, y) ;
		    }
		else
			break ;
	    }
	return x;
}

// Evaluate an array expression (strictly left-to-right):
int expra (void *ebp, int ecx, unsigned char type)
{
	int i ;
	signed char op ;
	unsigned char type2 ;
	void *ptr ;

	if (nxt () == TEVAL)
	    {
		int count ;
		signed char *tmpesi ;
		esi++ ;
		VAR v = items () ;
		fixs (v) ;
		lexan (accs, buff, 0) ;
		tmpesi = esi ;
		esi = (signed char *) buff ;
		count = expra (ebp, ecx, type) ; // recursive call
		esi = tmpesi ;
		if (nxt () != ',')
			return count ;
	    }
	else
	    {
		op = '=' ; // for initial assignment
		while (1)
		    {
			void *savebp = ebp ;
			signed char *savesi = esi ;
			signed char saveop = op ;

			if ((nxt () == '-') && (op == '=')) // unary minus
			    {
				esi++ ;
				nxt () ;
				op = '-' ;
			    }

			ptr = getvar (&type2) ;
			if ((ptr != NULL) && (type2 & BIT6)) // RHS is an array
			    {
				if (ptr < (void *)2)
					error (14, NULL) ; // 'Bad use of array'
				ptr = VLOAD(ptr) ;

				if (nxt () == '.') // dot product
					break ;

				if (((type < 128) != (type2 < 128)) || (ecx != arrlen(&ptr)))
					error (6, NULL) ; // 'Type mismatch'
				if (type < 128) // numeric array
				    {
					for (i = 0; i < ecx; i++)
					    {
						VAR v = loadn (ptr, type2 & ~BIT6) ;
						modify (v, ebp, type & ~BIT6, op) ;
						ebp += type & TMASK ; // GCC extension
						ptr += type2 & TMASK ; // GCC extension
					    }
				    }
				else // string array
				    {
					for (i = 0; i < ecx; i++)
					    {
						modifs (NLOAD(ptr), ebp, type & ~BIT6, op) ;
						ebp += 8 ; // GCC extension (void has size 1)
						ptr += 8 ; // GCC extension (void has size 1)
					    }
				    }
			    }
			else // RHS is not an array
			    {
				VAR v ;
				esi = savesi ;
				op = saveop ;

				if (type < 128) // numeric non-array
				    {
					switch (op)
					    {
						case TOR:
						case TEOR:
							v = expr1 () ;
							break ;

						case TAND:
							v = expr3 () ;
							break ;

						case '+':
						case '-':
							v = expr4 () ;
							break ;

						default:
							v = expr5 () ;
					    }
					if (v.s.t == -1)
						error (6, NULL) ; // 'Type mismatch'

					for (i = 0; i < ecx; i++)
					    {
						modify (v, ebp, type & ~BIT6, op) ;
						ebp += type & TMASK ; // GCC extension
						if (nxt () == ',') break ;
					    }
				    }
				else // string non-array
				    {
					heapptr *savesp ;
					v = items () ;
					savesp = pushs (v) ;
					v.s.p = (char *) esp - (char *) zero ;
					for (i = 0; i < ecx; i++)
					    {
						modifs (v, ebp, type & ~BIT6, op) ;
						ebp += 8 ; // GCC extension
						if (nxt () == ',') break ;
					    }
					esp = savesp ;
				    }
			    }

			ebp = savebp ;
			op = nxt () ;
			if ((op == '+') || (op == '-') || (op == '*') || (op == '/') ||
				(op == '^') || ((op >= TAND) && (op <= TOR)))
			    {
				esi++ ;
				continue ;
			    }
			break ;
		    }
	    }

	if (*esi == '.')
	    {
		int i, j, k, dimsl, dimsr, size ;
		int rowsl, colsl, rowsr, colsr ;
		unsigned char type3 ;
		void *rhs ;

		esi++ ; // skip dot
		nxt () ;
		rhs = getvar (&type3) ;
		if ((rhs == NULL) || (ptr == NULL))
			error (16, NULL) ; // 'Syntax error'
		if (type3 == 0)
			error (26, NULL) ; // 'No such variable'
		if ((type2 != type3) || (type3 & BIT7))
			error (6, NULL) ; // 'Type mismatch'
		if (rhs < (void *)2)
			error (14, NULL) ; // 'Bad use of array'
		rhs = VLOAD(rhs) ;

		dimsl = *(unsigned char *)ptr ;
		dimsr = *(unsigned char *)rhs ;
		if ((dimsl > 2) || (dimsr > 2))
			error (6, NULL) ; // 'Type mismatch'

		if (dimsl == 2)
		    {
			rowsl = ILOAD(ptr + 1) ; // GCC extension: sizeof(void) = 1
			colsl = ILOAD(ptr + 5) ; // GCC extension: sizeof(void) = 1
			ptr += 9 ;
		    }
		else
		    {
			rowsl = 1 ;
			colsl = arrlen (&ptr) ;
		    }

		if (dimsr == 2)
		    {
			rowsr = ILOAD(rhs + 1) ; // GCC extension: sizeof(void) = 1
			colsr = ILOAD(rhs + 5) ; // GCC extension: sizeof(void) = 1
			rhs += 9 ;
		    }
		else
		    {
			colsr = 1 ;
			rowsr = arrlen (&rhs) ;
		    }

		if ((colsl != rowsr) || (ecx != (colsr * rowsl)))
			error (6, NULL) ; // 'Type mismatch'

		type &= ~BIT6 ;
		type2 &= ~BIT6 ;
		size = type2 & TMASK ;
		for (i = 0; i < rowsl; i++)
		    {
			void *oldrhs = rhs ;
			for (j = 0; j < colsr; j++)
			    {
				void *oldptr = ptr ;
				void *oldrhs = rhs ;
				VAR v = {0} ;
				for (k = 0; k < colsl; k++)
				    {
#if !(defined(__GNUC__) && (__GNUC__ >= 7) || defined(__clang__) && (__clang_major__ >= 4))
					v = math( math (loadn (ptr,type2), '*', loadn (rhs,type2)), 
						  '+', v) ;
#else
					long long tmp ;
					VAR x = loadn (ptr, type2) ;
					VAR y = loadn (rhs, type2) ;
					if ((x.i.t == 0) && (y.i.t == 0) &&
					    ! __builtin_smulll_overflow (x.i.n, y.i.n, &tmp))
						x.i.n = tmp ;
					else
					    {
						float2 (&x, &y) ;
						x.f *= y.f ;
						if (x.i.t == 0)
							x.f = 0.0 ; // Underflow
					    }
					if ((v.i.t == 0) && (x.i.t == 0) &&
					    ! __builtin_saddll_overflow (v.i.n, x.i.n, &tmp))
						v.i.n = tmp ;
					else
					    {
						float2 (&v, &x) ;
						v.f += x.f ;
						if (isinf(v.f) || (v.i.t == -1))
							error (20, NULL) ; // 'Number too big'
						if ((v.i.t == 0) || (v.i.t == (short) 0x8000))
							v.f = 0.0 ; // Underflow
					    }
#endif
					ptr += size ;
					rhs += size * colsr ;
				    }
				storen (v, ebp, type) ;
				ebp += (type & 15) ;
				rhs = oldrhs + size ;
				ptr = oldptr ;
			    }
			rhs = oldrhs ;
			ptr += size * colsl ;
		    }
		return ecx ;
	    }

	i = 0 ;
	while ((nxt () == ',') && (++i < ecx)) // list of initialisers supplied
	    {
		VAR v ;
		esi++ ;
		if (type < 128)
		    {
			v = exprn () ;
			ebp += type & TMASK ; // GCC extension
			modify (v, ebp, type & ~BIT6, '=') ;
		    }
		else
		    {
			v = exprs () ;
			ebp += 8 ; // GCC extension
			modifs (v, ebp, type & ~BIT6, '=') ;
		    }
	    }

	if (i) ecx = i + 1 ;
	return ecx ;
}

// Function aliases:
int str00 (VAR v, char *dst, int format)
{
	return str (v, dst, format) ;
}
