| | 1 | #ifdef RCSID |
| | 2 | static char RCSid[] = |
| | 3 | "$Header: d:/cvsroot/tads/tads3/VMTYPE.CPP,v 1.3 1999/05/17 02:52:29 MJRoberts Exp $"; |
| | 4 | #endif |
| | 5 | |
| | 6 | /* |
| | 7 | * Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved. |
| | 8 | * |
| | 9 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 10 | * on using and copying this software. |
| | 11 | */ |
| | 12 | /* |
| | 13 | Name |
| | 14 | vmtype.cpp - VM types |
| | 15 | Function |
| | 16 | |
| | 17 | Notes |
| | 18 | |
| | 19 | Modified |
| | 20 | 11/18/98 MJRoberts - Creation |
| | 21 | */ |
| | 22 | |
| | 23 | #include "t3std.h" |
| | 24 | #include "vmtype.h" |
| | 25 | #include "vmobj.h" |
| | 26 | #include "vmstr.h" |
| | 27 | #include "vmlst.h" |
| | 28 | #include "vmpool.h" |
| | 29 | |
| | 30 | /* ------------------------------------------------------------------------ */ |
| | 31 | /* |
| | 32 | * Compare this value to another value to determine if the two values |
| | 33 | * are equal. |
| | 34 | */ |
| | 35 | int vm_val_t::equals(VMG_ const vm_val_t *v, int depth) const |
| | 36 | { |
| | 37 | /* |
| | 38 | * if the second value is an object and the first isn't, use the object |
| | 39 | * comparison of the second |
| | 40 | */ |
| | 41 | if (v->typ == VM_OBJ && typ != VM_OBJ) |
| | 42 | return vm_objp(vmg_ v->val.obj) |
| | 43 | ->equals(vmg_ v->val.obj, this, depth); |
| | 44 | |
| | 45 | /* figure out what to do based on my type */ |
| | 46 | switch(typ) |
| | 47 | { |
| | 48 | case VM_NIL: |
| | 49 | case VM_TRUE: |
| | 50 | /* we match only if the other value is the same boolean value */ |
| | 51 | return (v->typ == typ); |
| | 52 | |
| | 53 | case VM_STACK: |
| | 54 | case VM_CODEPTR: |
| | 55 | /* |
| | 56 | * we match only if the other value has the same type and its |
| | 57 | * pointer value matches |
| | 58 | */ |
| | 59 | return (v->typ == typ && v->val.ptr == this->val.ptr); |
| | 60 | |
| | 61 | case VM_OBJ: |
| | 62 | /* use the object's polymorphic equality test routine */ |
| | 63 | return vm_objp(vmg_ this->val.obj) |
| | 64 | ->equals(vmg_ this->val.obj, v, depth); |
| | 65 | |
| | 66 | case VM_PROP: |
| | 67 | /* we match if the other value is the same property ID */ |
| | 68 | return (v->typ == VM_PROP && v->val.prop == this->val.prop); |
| | 69 | |
| | 70 | case VM_INT: |
| | 71 | /* we match if the other value is the same integer */ |
| | 72 | return (v->typ == VM_INT && v->val.intval == this->val.intval); |
| | 73 | |
| | 74 | case VM_ENUM: |
| | 75 | return (v->typ == VM_ENUM && v->val.enumval == this->val.enumval); |
| | 76 | |
| | 77 | case VM_SSTRING: |
| | 78 | /* use the standard string comparison routine */ |
| | 79 | return CVmObjString:: |
| | 80 | const_equals(vmg_ G_const_pool->get_ptr(this->val.ofs), v); |
| | 81 | |
| | 82 | case VM_LIST: |
| | 83 | /* use the standard list comparison routine */ |
| | 84 | return CVmObjList:: |
| | 85 | const_equals(vmg_ this, |
| | 86 | G_const_pool->get_ptr(this->val.ofs), v, depth); |
| | 87 | |
| | 88 | case VM_CODEOFS: |
| | 89 | case VM_FUNCPTR: |
| | 90 | /* we match if the other value is the same code offset */ |
| | 91 | return (v->typ == typ && v->val.ofs == this->val.ofs); |
| | 92 | |
| | 93 | case VM_EMPTY: |
| | 94 | /* empty never matches anything */ |
| | 95 | return FALSE; |
| | 96 | |
| | 97 | case VM_DSTRING: |
| | 98 | /* dstrings have no value, and are thus never equal to anything */ |
| | 99 | return FALSE; |
| | 100 | |
| | 101 | default: |
| | 102 | /* other types are not recognized */ |
| | 103 | return FALSE; |
| | 104 | } |
| | 105 | } |
| | 106 | |
| | 107 | /* ------------------------------------------------------------------------ */ |
| | 108 | /* |
| | 109 | * Calculate a hash value |
| | 110 | */ |
| | 111 | uint vm_val_t::calc_hash(VMG_ int depth) const |
| | 112 | { |
| | 113 | /* see what we have */ |
| | 114 | switch(typ) |
| | 115 | { |
| | 116 | case VM_NIL: |
| | 117 | /* this is rather arbitrary */ |
| | 118 | return 0; |
| | 119 | |
| | 120 | case VM_TRUE: |
| | 121 | /* this is arbitrary, but at least make it different from nil */ |
| | 122 | return 1; |
| | 123 | |
| | 124 | case VM_EMPTY: |
| | 125 | /* also arbitrary */ |
| | 126 | return 2; |
| | 127 | |
| | 128 | case VM_CODEOFS: |
| | 129 | case VM_FUNCPTR: |
| | 130 | /* use a 16-bit hash of the code address */ |
| | 131 | return (uint)((val.ofs & 0xffff) |
| | 132 | ^ ((val.ofs & 0xffff0000) >> 16)); |
| | 133 | |
| | 134 | case VM_PROP: |
| | 135 | /* use the property ID as the hash */ |
| | 136 | return (uint)val.prop; |
| | 137 | |
| | 138 | case VM_INT: |
| | 139 | /* use a 16-bit hash of the int */ |
| | 140 | return (uint)((val.intval & 0xffff) |
| | 141 | ^ ((val.intval & 0xffff0000) >> 16)); |
| | 142 | |
| | 143 | case VM_ENUM: |
| | 144 | /* use a 16-bit hash of the enum value */ |
| | 145 | return (uint)((val.enumval & 0xffff) |
| | 146 | ^ ((val.enumval & 0xffff0000) >> 16)); |
| | 147 | |
| | 148 | case VM_OBJ: |
| | 149 | /* ask the object to calculate its hash value */ |
| | 150 | return vm_objp(vmg_ val.obj)->calc_hash(vmg_ val.obj, depth); |
| | 151 | break; |
| | 152 | |
| | 153 | case VM_SSTRING: |
| | 154 | /* get the hash of the constant string */ |
| | 155 | return CVmObjString:: |
| | 156 | const_calc_hash(G_const_pool->get_ptr(val.ofs)); |
| | 157 | break; |
| | 158 | |
| | 159 | case VM_LIST: |
| | 160 | /* get the hash of the constant list */ |
| | 161 | return CVmObjList:: |
| | 162 | const_calc_hash(vmg_ this, G_const_pool->get_ptr(val.ofs), depth); |
| | 163 | |
| | 164 | default: |
| | 165 | /* return an arbitrary value for any other type */ |
| | 166 | return 3; |
| | 167 | } |
| | 168 | } |
| | 169 | |
| | 170 | /* ------------------------------------------------------------------------ */ |
| | 171 | /* |
| | 172 | * Compare this value to the given value. Returns a positive value if |
| | 173 | * this value is greater than 'val', a negative value if this value is |
| | 174 | * less than 'val', and 0 if the two values are equal. Throws an error |
| | 175 | * if a magnitude comparison is not meaningful for the involved types. |
| | 176 | */ |
| | 177 | int vm_val_t::gen_compare_to(VMG_ const vm_val_t *v) const |
| | 178 | { |
| | 179 | /* |
| | 180 | * if the other value is of type object and I'm not, let the object |
| | 181 | * perform the comparison, and invert the sense of the result |
| | 182 | */ |
| | 183 | if (typ != VM_OBJ && v->typ == VM_OBJ) |
| | 184 | return -(v->compare_to(vmg_ this)); |
| | 185 | |
| | 186 | /* the comparison depends on my type */ |
| | 187 | switch(typ) |
| | 188 | { |
| | 189 | case VM_OBJ: |
| | 190 | /* let the object perform the comparison */ |
| | 191 | return vm_objp(vmg_ this->val.obj) |
| | 192 | ->compare_to(vmg_ this->val.obj, v); |
| | 193 | |
| | 194 | case VM_SSTRING: |
| | 195 | /* compare the string */ |
| | 196 | return CVmObjString:: |
| | 197 | const_compare(vmg_ G_const_pool->get_ptr(this->val.ofs), v); |
| | 198 | |
| | 199 | case VM_INT: |
| | 200 | { |
| | 201 | int32 a, b; |
| | 202 | |
| | 203 | /* the comparison is legal only for another number */ |
| | 204 | if (!v->is_numeric()) |
| | 205 | err_throw(VMERR_INVALID_COMPARISON); |
| | 206 | |
| | 207 | /* get the integers */ |
| | 208 | a = this->val.intval; |
| | 209 | b = v->val.intval; |
| | 210 | |
| | 211 | /* compare them and return the results */ |
| | 212 | if (a > b) |
| | 213 | return 1; |
| | 214 | else if (a < b) |
| | 215 | return -1; |
| | 216 | else |
| | 217 | return 0; |
| | 218 | } |
| | 219 | |
| | 220 | default: |
| | 221 | /* other types cannot be compared for magnitude */ |
| | 222 | err_throw(VMERR_INVALID_COMPARISON); |
| | 223 | |
| | 224 | /* this code is never reached, but the compiler doesn't know that */ |
| | 225 | AFTER_ERR_THROW(return 0;) |
| | 226 | } |
| | 227 | } |
| | 228 | |
| | 229 | /* ------------------------------------------------------------------------ */ |
| | 230 | /* |
| | 231 | * Get the underlying string constant value. |
| | 232 | */ |
| | 233 | const char *vm_val_t::get_as_string(VMG0_) const |
| | 234 | { |
| | 235 | /* check my type */ |
| | 236 | if (typ == VM_SSTRING) |
| | 237 | { |
| | 238 | /* it's a constant string - return its text from the constant pool */ |
| | 239 | return G_const_pool->get_ptr(val.ofs); |
| | 240 | } |
| | 241 | else if (typ == VM_OBJ) |
| | 242 | { |
| | 243 | /* it's an object - ask for its underlying string, if any */ |
| | 244 | return vm_objp(vmg_ val.obj)->get_as_string(vmg0_); |
| | 245 | } |
| | 246 | else |
| | 247 | { |
| | 248 | /* other types do not have underlying strings */ |
| | 249 | return 0; |
| | 250 | } |
| | 251 | } |
| | 252 | |
| | 253 | /* ------------------------------------------------------------------------ */ |
| | 254 | /* |
| | 255 | * Get the underlying list constant value. |
| | 256 | */ |
| | 257 | const char *vm_val_t::get_as_list(VMG0_) const |
| | 258 | { |
| | 259 | /* check my type */ |
| | 260 | if (typ == VM_LIST) |
| | 261 | { |
| | 262 | /* it's a constant list - return its data from the constant pool */ |
| | 263 | return G_const_pool->get_ptr(val.ofs); |
| | 264 | } |
| | 265 | else if (typ == VM_OBJ) |
| | 266 | { |
| | 267 | /* it's an object - ask for its underlying list, if any */ |
| | 268 | return vm_objp(vmg_ val.obj)->get_as_list(); |
| | 269 | } |
| | 270 | else |
| | 271 | { |
| | 272 | /* other types do not have underlying lists */ |
| | 273 | return 0; |
| | 274 | } |
| | 275 | } |
| | 276 | |
| | 277 | /* ------------------------------------------------------------------------ */ |
| | 278 | /* |
| | 279 | * get the effective number of elements when this value is used as the |
| | 280 | * right-hand side of a '+' or '-' operator whose left-hand side is a |
| | 281 | * collection type |
| | 282 | */ |
| | 283 | size_t vm_val_t::get_coll_addsub_rhs_ele_cnt(VMG0_) const |
| | 284 | { |
| | 285 | /* the handling depends on the type */ |
| | 286 | switch(typ) |
| | 287 | { |
| | 288 | case VM_LIST: |
| | 289 | /* it's a static list - return the number of elements in the list */ |
| | 290 | return vmb_get_len(G_const_pool->get_ptr(val.ofs)); |
| | 291 | |
| | 292 | case VM_OBJ: |
| | 293 | /* ask the object what it thinks */ |
| | 294 | return vm_objp(vmg_ val.obj)->get_coll_addsub_rhs_ele_cnt(vmg0_); |
| | 295 | |
| | 296 | default: |
| | 297 | /* for any other type, there's only one element */ |
| | 298 | return 1; |
| | 299 | } |
| | 300 | } |
| | 301 | |
| | 302 | /* |
| | 303 | * get the nth element of a collection add/sub operation of which we're |
| | 304 | * the right-hand side |
| | 305 | */ |
| | 306 | void vm_val_t::get_coll_addsub_rhs_ele(VMG_ size_t idx, |
| | 307 | vm_val_t *result) const |
| | 308 | { |
| | 309 | /* the handling depends on the type */ |
| | 310 | switch(typ) |
| | 311 | { |
| | 312 | case VM_LIST: |
| | 313 | /* it's a static list - get the nth element from the list */ |
| | 314 | CVmObjList::index_list(vmg_ result, |
| | 315 | G_const_pool->get_ptr(val.ofs), idx); |
| | 316 | break; |
| | 317 | |
| | 318 | case VM_OBJ: |
| | 319 | /* ask the object for its indexed value */ |
| | 320 | vm_objp(vmg_ val.obj)->get_coll_addsub_rhs_ele( |
| | 321 | vmg_ result, val.obj, idx); |
| | 322 | break; |
| | 323 | |
| | 324 | default: |
| | 325 | /* for anything else, we have just our own value */ |
| | 326 | *result = *this; |
| | 327 | break; |
| | 328 | } |
| | 329 | } |
| | 330 | |
| | 331 | /* ------------------------------------------------------------------------ */ |
| | 332 | /* |
| | 333 | * Set to an integer giving the datatype code for the given value |
| | 334 | */ |
| | 335 | void vm_val_t::set_datatype(VMG_ const vm_val_t *v) |
| | 336 | { |
| | 337 | /* check for an object */ |
| | 338 | if (v->typ == VM_OBJ) |
| | 339 | { |
| | 340 | /* check for List and String values */ |
| | 341 | if (v->get_as_string(vmg0_) != 0) |
| | 342 | { |
| | 343 | /* treat this the same as a string constant */ |
| | 344 | set_int((int)VM_SSTRING); |
| | 345 | } |
| | 346 | else if (v->get_as_list(vmg0_) != 0) |
| | 347 | { |
| | 348 | /* treat this the same as a list constant */ |
| | 349 | set_int((int)VM_LIST); |
| | 350 | } |
| | 351 | else |
| | 352 | { |
| | 353 | /* any other type of object is simply an object */ |
| | 354 | set_int((int)VM_OBJ); |
| | 355 | } |
| | 356 | } |
| | 357 | else |
| | 358 | { |
| | 359 | /* for any other type, return our internal type code */ |
| | 360 | set_int((int)v->typ); |
| | 361 | } |
| | 362 | } |