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