Boron 0.1.0

calculator.c

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

00001 /*
00002   Copyright 2009 Karl Robillard
00003 
00004   This program is free software: you can redistribute it and/or modify
00005   it under the terms of the GNU General Public License as published by
00006   the Free Software Foundation, either version 3 of the License, or
00007   (at your option) any later version.
00008 
00009   This program is distributed in the hope that it will be useful,
00010   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012   GNU General Public License for more details.
00013 
00014   You should have received a copy of the GNU General Public License
00015   along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016 */
00017 
00018 
00019 #include <stdio.h>
00020 #include <string.h>
00021 #include "urlan.h"
00022 #include "urlan_atoms.h"
00023 
00024 
00025 /*
00026   Recursively evaluate math expression.
00027 
00028   \param cell   Cell to evaluate.
00029   \param res    Result.
00030 
00031   \return UR_OK or UR_THROW.
00032 */
00033 int calc_eval( UThread* ut, UCell* cell, double* res )
00034 {
00035     switch( ur_type(cell) )
00036     {
00037         case UT_WORD:
00038         {
00039             const UCell* val = ur_wordCell( ut, cell );
00040             if( ! val )
00041                 return UR_THROW;
00042             if( ur_is(val, UT_DECIMAL) )
00043                 *res = ur_decimal(val);
00044             else if( ur_is(val, UT_INT) || ur_is(val, UT_CHAR) )
00045                 *res = (double) ur_int(val);
00046             else
00047             {
00048                 return ur_error( ut, UR_ERR_SCRIPT, "Invalid word '%s",
00049                                  ur_wordCStr( cell ) );
00050             }
00051         }
00052             break;
00053 
00054         case UT_DECIMAL:
00055             *res = ur_decimal(cell);
00056             break;
00057 
00058         case UT_INT:
00059         case UT_CHAR:
00060             *res = (double) ur_int(cell);
00061             break;
00062 
00063         case UT_BLOCK:
00064         case UT_PAREN:
00065         {
00066             UBlockIterM bi;
00067             double num = 0.0;
00068             double right;
00069 
00070 #define RIGHT_VAL \
00071     if( ++bi.it == bi.end ) \
00072         return ur_error( ut, UR_ERR_SCRIPT, "Expected operator r-value" ); \
00073     if( ! calc_eval( ut, bi.it, &right ) ) \
00074         return UR_THROW;
00075 
00076             if( ! ur_blkSliceM( ut, &bi, cell ) )
00077                 return UR_THROW;
00078             ur_foreach( bi )
00079             {
00080                 if( ur_is(bi.it, UT_WORD) )
00081                 {
00082                     switch( ur_atom(bi.it) )
00083                     {
00084                         case UR_ATOM_PLUS:
00085                             RIGHT_VAL
00086                             num += right;
00087                             break;
00088 
00089                         case UR_ATOM_MINUS:
00090                             RIGHT_VAL
00091                             num -= right;
00092                             break;
00093 
00094                         case UR_ATOM_ASTERISK:
00095                             RIGHT_VAL
00096                             num *= right;
00097                             break;
00098 
00099                         case UR_ATOM_SLASH:
00100                             RIGHT_VAL
00101                             num /= right;
00102                             break;
00103 
00104                         default:
00105                             if( ! calc_eval( ut, bi.it, &num ) )
00106                                 return UR_THROW;
00107                     }
00108                 }
00109                 else if( ur_is(bi.it, UT_SETWORD) )
00110                 {
00111                     cell = ur_wordCellM( ut, bi.it );
00112                     if( ! cell )
00113                         return UR_THROW;
00114                     ur_setId( cell, UT_DECIMAL );
00115                     ur_decimal(cell) = num;
00116                 }
00117                 else
00118                 {
00119                     if( ! calc_eval( ut, bi.it, &num ) )
00120                         return UR_THROW;
00121                 }
00122             }
00123             *res = num;
00124         }
00125             break;
00126 
00127         default:
00128             *res = 0.0;
00129             break;
00130     }
00131     return UR_OK;
00132 }
00133 
00134 
00135 /*
00136   Evaluate C string.
00137 
00138   \param cmd  String to evaluate.
00139 
00140   \return UR_OK or UR_THROW.
00141 */
00142 int calc_evalCStr( UThread* ut, const char* cmd, double* result )
00143 {
00144     UCell cell;
00145     UIndex blkN;
00146     UIndex hold;
00147     int ok;
00148     int len = strlen( cmd );
00149     if( len )
00150     {
00151         blkN = ur_tokenize( ut, cmd, cmd + len, &cell );
00152         if( blkN )
00153         {
00154             ur_bind(ut, ur_buffer(blkN), ur_threadContext(ut), UR_BIND_THREAD);
00155 
00156             /* Since the program cell is not part of the dataStore,
00157              * the block must be manually held. */
00158             hold = ur_hold( blkN );
00159             ok = calc_eval( ut, &cell, result );
00160             ur_release( hold );
00161 
00162             return ok;
00163         }
00164         return UR_THROW;
00165     }
00166     return UR_OK;
00167 }
00168 
00169 
00170 /*
00171   Define the words 'pi and 'e.
00172 */
00173 void defineWords( UThread* ut )
00174 {
00175     static double constants[2] = { 3.14159265358979, 2.71828182845904 };
00176     UAtom atoms[2];
00177     UBuffer* ctx;
00178     UCell* cell;
00179     int i;
00180 
00181     ur_internAtoms( ut, "pi e", atoms );
00182 
00183     ctx = ur_threadContext( ut );
00184     for( i = 0; i < 2; ++i )
00185     {
00186         cell = ur_ctxAddWord( ctx, atoms[i] );
00187         ur_setId( cell, UT_DECIMAL );
00188         ur_decimal(cell) = constants[i];
00189     }
00190     ur_ctxSort( ctx );
00191 }
00192 
00193 
00194 int main( int argc, char** argv )
00195 {
00196     UThread* ut;
00197     char cmd[ 2048 ];
00198     double result;
00199     (void) argc;
00200     (void) argv;
00201 
00202 
00203     printf( "Urlan Calculator Example %s (%s)\n", UR_VERSION_STR, __DATE__ );
00204 
00205     ut = ur_makeEnv( 256, 0, 0, 0, 0 );
00206     if( ! ut )
00207     {
00208         printf( "ur_makeEnv failed\n" );
00209         return 255;
00210     }
00211 
00212     defineWords( ut );
00213 
00214     while( 1 )
00215     {
00216         printf( ")> " );
00217         fflush( stdout );   /* Required on Windows. */
00218         fgets( cmd, sizeof(cmd), stdin ); 
00219 
00220         if( cmd[0] < ' ' )
00221         {
00222             printf( "\n" );
00223         }
00224         else if( cmd[0] == 'q' )
00225         {
00226             break;
00227         }
00228         else
00229         {
00230             if( calc_evalCStr( ut, cmd, &result ) )
00231             {
00232                 printf( "= %f\n", result );
00233             }
00234             else
00235             {
00236                 UBuffer* blk = ur_errorBlock(ut);
00237                 if( blk->used )
00238                 {
00239                     UBuffer str;
00240 
00241                     ur_strInit( &str, UR_ENC_UTF8, 0 );
00242                     ur_toStr( ut, blk->ptr.cell, &str, 0 );
00243                     ur_strTermNull( &str );
00244                     printf( "%s\n", str.ptr.c );
00245                     ur_strFree( &str );
00246 
00247                     blk->used = 0;
00248                 }
00249                 else
00250                     break;
00251             }
00252         }
00253     }
00254 
00255     ur_freeEnv( ut );
00256     return 0;
00257 }
00258 
00259 
00260 //EOF

Generated on 27 Jan 2012 by Doxygen 1.5.1