Boron 2.1.0
calculator.c

This example shows how to use the Urlan library to implement a simple calculator language.

This example shows how to use the Urlan library to implement a simple calculator language.

/*
Copyright 2009,2019 Karl Robillard
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include "urlan.h"
#include "urlan_atoms.h"
/*
Recursively evaluate math expression.
\param cell Cell to evaluate.
\param res Result.
\return UR_OK or UR_THROW.
*/
UStatus calc_eval( UThread* ut, UCell* cell, double* res )
{
switch( ur_type(cell) )
{
case UT_WORD:
{
const UCell* val = ur_wordCell( ut, cell );
if( ! val )
return UR_THROW;
if( ur_is(val, UT_DOUBLE) )
*res = ur_double(val);
else if( ur_is(val, UT_INT) || ur_is(val, UT_CHAR) )
*res = (double) ur_int(val);
else
{
return ur_error( ut, UR_ERR_SCRIPT, "Invalid word '%s",
ur_wordCStr( cell ) );
}
}
break;
case UT_DOUBLE:
*res = ur_double(cell);
break;
case UT_INT:
case UT_CHAR:
*res = (double) ur_int(cell);
break;
case UT_BLOCK:
case UT_PAREN:
{
UBlockIterM bi;
double num = 0.0;
double right;
#define RIGHT_VAL \
if( ++bi.it == bi.end ) \
return ur_error( ut, UR_ERR_SCRIPT, "Expected operator r-value" ); \
if( ! calc_eval( ut, bi.it, &right ) ) \
return UR_THROW;
if( ! ur_blkSliceM( ut, &bi, cell ) )
return UR_THROW;
ur_foreach( bi )
{
if( ur_is(bi.it, UT_WORD) )
{
switch( ur_atom(bi.it) )
{
case UR_ATOM_PLUS:
RIGHT_VAL
num += right;
break;
case UR_ATOM_MINUS:
RIGHT_VAL
num -= right;
break;
case UR_ATOM_ASTERISK:
RIGHT_VAL
num *= right;
break;
case UR_ATOM_SLASH:
RIGHT_VAL
num /= right;
break;
default:
if( ! calc_eval( ut, bi.it, &num ) )
return UR_THROW;
}
}
else if( ur_is(bi.it, UT_SETWORD) )
{
cell = ur_wordCellM( ut, bi.it );
if( ! cell )
return UR_THROW;
ur_setId( cell, UT_DOUBLE );
ur_double(cell) = num;
}
else
{
if( ! calc_eval( ut, bi.it, &num ) )
return UR_THROW;
}
}
*res = num;
}
break;
default:
*res = 0.0;
break;
}
return UR_OK;
}
/*
Evaluate C string.
\param cmd String to evaluate.
\return UR_OK or UR_THROW.
*/
UStatus calc_evalCStr( UThread* ut, const char* cmd, double* result )
{
UCell cell;
UIndex blkN;
UIndex hold;
UStatus ok;
int len = strlen( cmd );
if( len )
{
blkN = ur_tokenize( ut, cmd, cmd + len, &cell );
if( blkN )
{
/* Since the program cell is not part of the dataStore,
* the block must be manually held. */
hold = ur_hold( blkN );
ok = calc_eval( ut, &cell, result );
ur_release( hold );
return ok;
}
return UR_THROW;
}
return UR_OK;
}
/*
Define the words 'pi and 'e.
*/
void defineWords( UThread* ut )
{
static double constants[2] = { 3.14159265358979, 2.71828182845904 };
UAtom atoms[2];
UBuffer* ctx;
UCell* cell;
int i;
ur_internAtoms( ut, "pi e", atoms );
ctx = ur_threadContext( ut );
for( i = 0; i < 2; ++i )
{
cell = ur_ctxAddWord( ctx, atoms[i] );
ur_setId( cell, UT_DOUBLE );
ur_double(cell) = constants[i];
}
ur_ctxSort( ctx );
}
int main( int argc, char** argv )
{
UEnvParameters envParam;
UThread* ut;
char cmd[ 2048 ];
double result;
(void) argc;
(void) argv;
printf( "Urlan Calculator Example %s (%s)\n", UR_VERSION_STR, __DATE__ );
ut = ur_makeEnv( ur_envParam(&envParam) );
if( ! ut )
{
printf( "ur_makeEnv failed\n" );
return 255;
}
// Create stack to hold exception.
ur_blkAppendNew( &ut->stack, UT_UNSET );
defineWords( ut );
while( 1 )
{
printf( ")> " );
fflush( stdout ); /* Required on Windows. */
fgets( cmd, sizeof(cmd), stdin );
if( cmd[0] < ' ' )
{
printf( "\n" );
}
else if( cmd[0] == 'q' )
{
break;
}
else
{
if( calc_evalCStr( ut, cmd, &result ) )
{
printf( "= %f\n", result );
}
else
{
UBuffer str;
ur_strInit( &str, UR_ENC_UTF8, 0 );
ur_toText( ut, ur_exception( ut ), &str );
ur_strTermNull( &str );
printf( "%s\n", str.ptr.c );
ur_strFree( &str );
}
}
}
ur_freeEnv( ut );
return 0;
}
//EOF
UStatus ur_blkSliceM(UThread *, UBlockIterM *, const UCell *cell)
Set UBlockIterM to block slice.
Definition block.c:224
UCell * ur_blkAppendNew(UBuffer *, int type)
Add cell to end of block.
Definition block.c:109
UCell * ur_ctxAddWord(UBuffer *, UAtom atom)
Similar to ur_ctxAddWordI(), but safely returns the cell pointer.
Definition context.c:437
UBuffer * ur_ctxSort(UBuffer *)
Sort the internal context search table so ur_ctxLookup() is faster.
Definition context.c:510
void ur_bind(UThread *, UBuffer *blk, const UBuffer *ctx, int bindType)
Bind block to context.
Definition context.c:690
#define ur_strFree
A string is a simple array.
Definition urlan.h:629
void ur_strTermNull(UBuffer *)
Terminate with null character so buffer can be used as a C string.
Definition string.c:1049
void ur_strInit(UBuffer *, int enc, int size)
Initialize buffer to type UT_STRING.
Definition string.c:430
void ur_freeEnv(UThread *)
Free environment and the initial thread.
Definition env.c:480
#define ur_setId(c, t)
Set type and initialize the other 24 bits of UCellId to zero.
Definition urlan.h:701
UIndex ur_tokenize(UThread *, const char *it, const char *end, UCell *res)
Convert a UTF-8 data string into a block.
Definition tokenize.c:1197
UStatus ur_error(UThread *, int errorType, const char *fmt,...)
Create error! exception.
Definition env.c:964
const UCell * ur_wordCell(UThread *, const UCell *cell)
Get word value for read-only operations.
Definition env.c:1132
void ur_toText(UThread *, const UCell *cell, UBuffer *str)
Append textual representation of cell to a string.
Definition env.c:1118
UBuffer * ur_threadContext(UThread *)
Get thread global context.
Definition env.c:934
UThread * ur_makeEnv(const UEnvParameters *)
Allocate UEnv and initial UThread.
Definition env.c:332
@ UR_THROW
Returned to indicate an evaluation exception occured.
Definition urlan.h:117
@ UR_OK
Returned to indicate successful evaluation/operation.
Definition urlan.h:118
#define ur_foreach(bi)
Loop over all members of an iterator struct.
Definition urlan.h:760
#define ur_hold(n)
Convenience macro for ur_holdBuffer().
Definition urlan.h:748
#define ur_release(h)
Convenience macro for ur_releaseBuffer().
Definition urlan.h:749
UAtom * ur_internAtoms(UThread *, const char *words, UAtom *atoms)
Add atoms to the shared environment.
Definition env.c:645
#define ur_buffer(n)
Macro to get buffer known to be in thread dataStore.
Definition urlan.h:750
#define ur_type(c)
Return UrlanDataType of cell.
Definition urlan.h:695
UCell * ur_wordCellM(UThread *, const UCell *cell)
Get modifiable word value.
Definition env.c:1178
UEnvParameters * ur_envParam(UEnvParameters *par)
Initialize UEnvParameters structure to default values.
Definition env.c:288
The UBuffer struct holds information about a resource, usually a chunk of memory.
Definition urlan.h:266
char * c
chars
Definition urlan.h:276
union UBuffer::@312146223224040072236377336057316010374162171270 ptr
This typically holds a pointer to a chunk of memory.
The UEnvParameters struct allows the user to override default buffer and structure sizes.
Definition urlan.h:497
The UThread struct stores the data specific to a thread of execution.
Definition urlan.h:309
A cell holds a single value of a simple type or a reference (often to a UBuffer) for a complex type.
Definition urlan.h:248
The Urlan programmer interface.
UStatus
Definition urlan.h:116
@ UR_ERR_SCRIPT
General script evaluation error.
Definition urlan.h:126
@ UR_BIND_THREAD
Bound to buffer in thread dataStore.
Definition urlan.h:88
int32_t UIndex
This is an index into an array.
Definition urlan.h:150
This header provides fixed atoms which are always present.