/interpreter/tags/at2dist220411/src/edu/vub/at/objects/natives/NATTable.java
Java | 533 lines | 390 code | 51 blank | 92 comment | 45 complexity | c6de06a7d3be43d5cbb1f9871dceee00 MD5 | raw file
1/** 2 * AmbientTalk/2 Project 3 * NATTable.java created on 26-jul-2006 at 16:48:34 4 * (c) Programming Technology Lab, 2006 - 2007 5 * Authors: Tom Van Cutsem & Stijn Mostinckx 6 * 7 * Permission is hereby granted, free of charge, to any person 8 * obtaining a copy of this software and associated documentation 9 * files (the "Software"), to deal in the Software without 10 * restriction, including without limitation the rights to use, 11 * copy, modify, merge, publish, distribute, sublicense, and/or 12 * sell copies of the Software, and to permit persons to whom the 13 * Software is furnished to do so, subject to the following 14 * conditions: 15 * 16 * The above copyright notice and this permission notice shall be 17 * included in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 * OTHER DEALINGS IN THE SOFTWARE. 27 */ 28package edu.vub.at.objects.natives; 29 30import edu.vub.at.eval.Evaluator; 31import edu.vub.at.exceptions.InterpreterException; 32import edu.vub.at.exceptions.XIndexOutOfBounds; 33import edu.vub.at.objects.ATBoolean; 34import edu.vub.at.objects.ATClosure; 35import edu.vub.at.objects.ATContext; 36import edu.vub.at.objects.ATMethod; 37import edu.vub.at.objects.ATNil; 38import edu.vub.at.objects.ATNumber; 39import edu.vub.at.objects.ATObject; 40import edu.vub.at.objects.ATTable; 41import edu.vub.at.objects.ATText; 42import edu.vub.at.objects.coercion.NativeTypeTags; 43import edu.vub.at.objects.grammar.ATSymbol; 44import edu.vub.at.objects.mirrors.DirectNativeMethod; 45import edu.vub.at.objects.mirrors.NativeClosure; 46import edu.vub.at.objects.natives.grammar.AGExpression; 47import edu.vub.at.parser.SourceLocation; 48import edu.vub.util.TempFieldGenerator; 49 50import java.util.HashMap; 51import java.util.HashSet; 52import java.util.LinkedList; 53import java.util.Set; 54import java.util.Vector; 55 56/** 57 * The native implementation of an AmbientTalk table. 58 * A table is implemented by a java array. 59 * 60 * An important distinction between AT tables and Java arrays is that 61 * ATTable objects are indexed from [1..size] rather than [0..size[ 62 * 63 * @author tvcutsem 64 */ 65public class NATTable extends AGExpression implements ATTable { 66 67 /** 68 * The empty table. This instance is shared between all actors on this VM, 69 * which is safe since it is an immutable object. 70 */ 71 public final static NATTable EMPTY = new NATTable(new ATObject[] {}) { 72 // since the empty table is shared, its source location is meaningless 73 public SourceLocation impl_getLocation() { return null; } 74 public void impl_setLocation(SourceLocation loc) {} 75 76 static final long serialVersionUID = 4036096689737987809L; 77 }; 78 79 public final ATObject[] elements_; 80 81 /** 82 * Table factory method. Used to enforce that only one empty table 83 * in the system exists. 84 */ 85 public static final NATTable atValue(ATObject[] array) { 86 if (array.length == 0) 87 return NATTable.EMPTY; 88 else 89 return new NATTable(array); 90 } 91 92 /** 93 * @return a table of the given size, filled with nil 94 */ 95 public static final NATTable ofSize(int size) { 96 ATObject[] array = new ATObject[size]; 97 for (int i = 0; i < size; i++) { 98 array[i] = Evaluator.getNil(); 99 } 100 return atValue(array); 101 } 102 103 /* 104 * Auxiliary methods to create tables more easily. 105 */ 106 107 public static final NATTable of(ATObject one) { 108 return new NATTable(new ATObject[] { one }); 109 } 110 111 public static final NATTable of(ATObject one, ATObject two) { 112 return new NATTable(new ATObject[] { one, two }); 113 } 114 115 public static final NATTable of(ATObject one, ATObject two, ATObject three) { 116 return new NATTable(new ATObject[] { one, two, three }); 117 } 118 119 private NATTable(ATObject[] elements) { 120 // assert elements.length > 0 121 elements_ = elements; 122 } 123 124 public ATTable asTable() { return this; } 125 126 public boolean isTable() { return true; } 127 128 public NATTable asNativeTable() { return this; } 129 130 /** 131 * To evaluate a table, evaluate all of its constituent expressions, taking 132 * special care to take into account spliced expressions. 133 * 134 * NATTAB(exps).eval(ctx) = NATTAB(map eval(ctx) over exps) 135 * 136 * @return a table of evaluated arguments 137 */ 138 public ATObject meta_eval(ATContext ctx) throws InterpreterException { 139 if (this == EMPTY) return EMPTY; 140 141 LinkedList result = new LinkedList(); 142 int siz = elements_.length; 143 for (int i = 0; i < elements_.length; i++) { 144 if (elements_[i].isSplice()) { 145 ATObject[] tbl = elements_[i].asSplice().base_expression().meta_eval(ctx).asNativeTable().elements_; 146 for (int j = 0; j < tbl.length; j++) { 147 result.add(tbl[j]); 148 } 149 siz += (tbl.length - 1); // -1 because we replace one element by a table of elements 150 } else { 151 result.add(elements_[i].meta_eval(ctx)); 152 } 153 } 154 return atValue((ATObject[]) result.toArray(new ATObject[siz])); 155 } 156 157 /** 158 * To quote a table, quote all elements of the table. 159 * Special care needs to be taken in order to properly deal with unquote-spliced elements. 160 * When one of the direct elements of the table is an unquote-splice element, the resulting 161 * unquotation must result in a table whose elements are directly added to this table's elements. 162 */ 163 public ATObject meta_quote(ATContext ctx) throws InterpreterException { 164 if (this == EMPTY) return EMPTY; 165 166 LinkedList result = new LinkedList(); 167 int siz = elements_.length; 168 for (int i = 0; i < elements_.length; i++) { 169 if (elements_[i].isUnquoteSplice()) { 170 ATObject[] tbl = elements_[i].asUnquoteSplice().base_expression().meta_eval(ctx).asNativeTable().elements_; 171 for (int j = 0; j < tbl.length; j++) { 172 result.add(tbl[j]); 173 } 174 siz += (tbl.length - 1); // -1 because we replace one element by a table of elements 175 } else { 176 result.add(elements_[i].meta_quote(ctx)); 177 } 178 } 179 return atValue((ATObject[]) result.toArray(new ATObject[siz])); 180 } 181 182 public NATText meta_print() throws InterpreterException { 183 return Evaluator.printElements(this, "[", ", ","]"); 184 } 185 186 public NATText impl_asCode(TempFieldGenerator objectMap) throws InterpreterException { 187 if(objectMap.contains(this)) { 188 return objectMap.getName(this); 189 } 190 191 StringBuffer out = new StringBuffer("["); 192 for(int i = 0 ; i < elements_.length ; i++) { 193 if(i > 0) { out.append(", "); } 194 out.append(elements_[i].impl_asCode(objectMap).javaValue); 195 } 196 out.append("]"); 197 NATText code = NATText.atValue(out.toString()); 198 NATText name = objectMap.put(this, code); 199 return name; 200 } 201 202 public NATText impl_asUnquotedCode(TempFieldGenerator objectMap) throws InterpreterException { 203 StringBuffer out = new StringBuffer("["); 204 for(int i = 0 ; i < elements_.length ; i++) { 205 if(i > 0) { out.append(", "); } 206 out.append(elements_[i].impl_asUnquotedCode(objectMap).javaValue); 207 } 208 out.append("]"); 209 NATText code = NATText.atValue(out.toString()); 210 return code; 211 } 212 213 public ATTable meta_typeTags() throws InterpreterException { 214 return NATTable.of(NativeTypeTags._TABLE_); 215 } 216 217 public ATNumber base_length() { return NATNumber.atValue(elements_.length); } 218 219 public ATObject base_at(ATNumber index) throws InterpreterException { 220 return elements_[extractIndex(index)]; 221 } 222 223 public ATObject base_atPut(ATNumber index, ATObject value) throws InterpreterException { 224 elements_[extractIndex(index)] = value; 225 return value; 226 } 227 228 public ATBoolean base_isEmpty() { 229 return NATBoolean.atValue(elements_.length == 0); 230 } 231 232 public ATNil base_each_(ATClosure clo) throws InterpreterException { 233 for (int i = 0; i < elements_.length; i++) { 234 clo.base_apply(atValue(new ATObject[] { elements_[i] })); 235 } 236 return Evaluator.getNil(); 237 } 238 239 public ATTable base_map_(ATClosure clo) throws InterpreterException { 240 if (this == EMPTY) return EMPTY; 241 242 ATObject[] result = new ATObject[elements_.length]; 243 for (int i = 0; i < elements_.length; i++) { 244 result[i] = clo.base_apply(atValue(new ATObject[] { elements_[i] })); 245 } 246 return atValue(result); 247 } 248 249 public ATObject base_inject_into_(ATObject init, ATClosure clo) throws InterpreterException { 250 ATObject total = init; 251 for (int i = 0; i < elements_.length; i++) { 252 total = clo.base_apply(atValue(new ATObject[] { total, elements_[i] })); 253 } 254 return total; 255 } 256 257 public ATTable base_filter_(ATClosure clo) throws InterpreterException { 258 Vector matchingElements = new Vector(elements_.length); 259 for (int i = 0; i < elements_.length; i++) { 260 if (clo.base_apply(atValue(new ATObject[] { elements_[i] })).asNativeBoolean().javaValue) { 261 matchingElements.add(elements_[i]); 262 } 263 } 264 return atValue((ATObject[]) matchingElements.toArray(new ATObject[matchingElements.size()])); 265 } 266 267 public ATObject base_find_(ATClosure clo) throws InterpreterException { 268 for (int i = 0; i < elements_.length; i++) { 269 if (clo.base_apply(atValue(new ATObject[] { elements_[i] })).asNativeBoolean().javaValue) { 270 return NATNumber.atValue(i+1); 271 } 272 } 273 return Evaluator.getNil(); 274 } 275 276 public ATBoolean base_contains(ATObject obj) throws InterpreterException { 277 for (int i = 0; i < elements_.length; i++) { 278 if (obj.equals(elements_[i])) { 279 return NATBoolean._TRUE_; 280 } 281 } 282 return NATBoolean._FALSE_; 283 } 284 285 public ATText base_implode() throws InterpreterException { 286 StringBuffer buff = new StringBuffer(""); 287 for (int i = 0; i < elements_.length; i++) { 288 buff.append(elements_[i].asNativeText().javaValue); 289 } 290 return NATText.atValue(buff.toString()); 291 } 292 293 public ATText base_join(ATText sep) throws InterpreterException { 294 String separator = sep.asNativeText().javaValue; 295 StringBuffer buff = new StringBuffer(""); 296 for (int i = 0; i < elements_.length-1; i++) { 297 buff.append(elements_[i].asNativeText().javaValue); 298 buff.append(separator); 299 } 300 if (elements_.length > 0) 301 buff.append(elements_[elements_.length-1].asNativeText().javaValue); 302 return NATText.atValue(buff.toString()); 303 } 304 305 /** 306 * tab.select(start, stop) == els = [ ] ; start.to: stop do: { |i| els << tab[i] } ; els 307 */ 308 public ATTable base_select(ATNumber first, ATNumber last) throws InterpreterException { 309 final LinkedList selection = new LinkedList(); 310 first.base_to_do_(last, new NativeClosure(this) { 311 public ATObject base_apply(ATTable args) throws InterpreterException { 312 selection.add(base_at(args.base_at(NATNumber.ONE).asNumber())); 313 return Evaluator.getNil(); 314 } 315 }); 316 return NATTable.atValue((ATObject[]) selection.toArray(new ATObject[selection.size()])); 317 } 318 319 public ATTable base__oppls_(ATTable other) throws InterpreterException { 320 return NATTable.atValue(collate(elements_, other.asNativeTable().elements_)); 321 } 322 323 protected int extractIndex(ATNumber atIndex) throws InterpreterException { 324 int javaIndex = atIndex.asNativeNumber().javaValue - 1; 325 if ((javaIndex < 0) || (javaIndex >= elements_.length)) 326 throw new XIndexOutOfBounds(javaIndex + 1, elements_.length); 327 else 328 return javaIndex; 329 } 330 331 /** 332 * Auxiliary method to collate two Java arrays 333 * @return an array containing first the elements of ary1, then the elements of ary2 334 */ 335 public static final ATObject[] collate(ATObject[] ary1, ATObject[] ary2) { 336 int siz1 = ary1.length; 337 int siz2 = ary2.length; 338 ATObject[] union = new ATObject[siz1 + siz2]; 339 System.arraycopy(ary1, 0, union, 0, siz1); 340 System.arraycopy(ary2, 0, union, siz1, siz2); 341 return union; 342 } 343 344 public ATObject meta_clone() throws InterpreterException { 345 ATObject[] clonedArray = new ATObject[elements_.length]; 346 System.arraycopy(elements_, 0, clonedArray, 0, elements_.length); 347 return NATTable.atValue(clonedArray); 348 } 349 350 public ATObject meta_resolve() throws InterpreterException { 351 if (elements_.length == 0) 352 return NATTable.EMPTY; 353 else 354 return this; 355 } 356 357 /** 358 * FV([exp1, exp2, ...]) = FV(exp1) U FV(exp2) U ... 359 */ 360 public Set impl_freeVariables() throws InterpreterException { 361 HashSet freeVars = new HashSet(); 362 for (int i = 0; i < elements_.length; i++) { 363 freeVars.addAll(elements_[i].asExpression().impl_freeVariables()); 364 } 365 return freeVars; 366 } 367 368 public Set impl_quotedFreeVariables() throws InterpreterException { 369 HashSet freeVars = new HashSet(); 370 for (int i = 0; i < elements_.length; i++) { 371 freeVars.addAll(elements_[i].asExpression().impl_quotedFreeVariables()); 372 } 373 return freeVars; 374 } 375 376 /** 377 * This hashmap stores all native methods of native AmbientTalk tables. 378 * It is populated when this class is loaded, and shared between all 379 * AmbientTalk actors on this VM. This is safe, since {@link DirectNativeMethod} 380 * instances are all immutable. 381 */ 382 private static final HashMap<String, ATMethod> _meths = new HashMap<String, ATMethod>(); 383 384 // initialize NATTable methods 385 static { 386 _meths.put("length", new DirectNativeMethod("length") { 387 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 388 NATTable self = ctx.base_receiver().asNativeTable(); 389 checkArity(args, 0); 390 return self.base_length(); 391 } 392 }); 393 _meths.put("at", new DirectNativeMethod("at") { 394 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 395 NATTable self = ctx.base_receiver().asNativeTable(); 396 checkArity(args, 1); 397 ATNumber index = get(args, 1).asNumber(); 398 return self.base_at(index); 399 } 400 }); 401 _meths.put("atPut", new DirectNativeMethod("atPut") { 402 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 403 NATTable self = ctx.base_receiver().asNativeTable(); 404 checkArity(args, 2); 405 ATNumber index = get(args, 1).asNumber(); 406 ATObject value = get(args, 2); 407 return self.base_atPut(index, value); 408 } 409 }); 410 _meths.put("isEmpty", new DirectNativeMethod("isEmpty") { 411 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 412 NATTable self = ctx.base_receiver().asNativeTable(); 413 checkArity(args, 0); 414 return self.base_isEmpty(); 415 } 416 }); 417 _meths.put("each:", new DirectNativeMethod("each:") { 418 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 419 NATTable self = ctx.base_receiver().asNativeTable(); 420 checkArity(args, 1); 421 ATClosure clo = get(args, 1).asClosure(); 422 return self.base_each_(clo); 423 } 424 }); 425 _meths.put("map:", new DirectNativeMethod("map:") { 426 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 427 NATTable self = ctx.base_receiver().asNativeTable(); 428 checkArity(args, 1); 429 ATClosure clo = get(args, 1).asClosure(); 430 return self.base_map_(clo); 431 } 432 }); 433 _meths.put("inject:into:", new DirectNativeMethod("inject:into:") { 434 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 435 NATTable self = ctx.base_receiver().asNativeTable(); 436 checkArity(args, 2); 437 ATObject init = get(args, 1); 438 ATClosure clo = get(args, 2).asClosure(); 439 return self.base_inject_into_(init, clo); 440 } 441 }); 442 _meths.put("filter:", new DirectNativeMethod("filter:") { 443 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 444 NATTable self = ctx.base_receiver().asNativeTable(); 445 checkArity(args, 1); 446 ATClosure clo = get(args, 1).asClosure(); 447 return self.base_filter_(clo); 448 } 449 }); 450 _meths.put("find:", new DirectNativeMethod("find:") { 451 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 452 NATTable self = ctx.base_receiver().asNativeTable(); 453 checkArity(args, 1); 454 ATClosure clo = get(args, 1).asClosure(); 455 return self.base_find_(clo); 456 } 457 }); 458 _meths.put("contains", new DirectNativeMethod("contains") { 459 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 460 NATTable self = ctx.base_receiver().asNativeTable(); 461 checkArity(args, 1); 462 ATObject obj = get(args, 1); 463 return self.base_contains(obj); 464 } 465 }); 466 _meths.put("implode", new DirectNativeMethod("implode") { 467 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 468 NATTable self = ctx.base_receiver().asNativeTable(); 469 checkArity(args, 0); 470 return self.base_implode(); 471 } 472 }); 473 _meths.put("join", new DirectNativeMethod("join") { 474 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 475 NATTable self = ctx.base_receiver().asNativeTable(); 476 checkArity(args, 1); 477 ATText sep = get(args, 1).asNativeText(); 478 return self.base_join(sep); 479 } 480 }); 481 _meths.put("select", new DirectNativeMethod("select") { 482 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 483 NATTable self = ctx.base_receiver().asNativeTable(); 484 checkArity(args, 2); 485 ATNumber first = get(args, 1).asNumber(); 486 ATNumber last = get(args, 2).asNumber(); 487 return self.base_select(first, last); 488 } 489 }); 490 _meths.put("+", new DirectNativeMethod("+") { 491 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 492 NATTable self = ctx.base_receiver().asNativeTable(); 493 checkArity(args, 1); 494 ATTable other = get(args, 1).asTable(); 495 return self.base__oppls_(other); 496 } 497 }); 498 _meths.put("==", new DirectNativeMethod("==") { 499 public ATObject base_apply(ATTable args, ATContext ctx) throws InterpreterException { 500 NATTable self = ctx.base_receiver().asNativeTable(); 501 checkArity(args, 1); 502 ATObject other = get(args, 1); 503 return self.base__opeql__opeql_(other); 504 } 505 }); 506 } 507 508 /** 509 * Overrides the default AmbientTalk native object behavior of extracting native 510 * methods based on the 'base_' naming convention. Instead, native AT tables use 511 * an explicit hashmap of native methods. This is much faster than the default 512 * behavior, which requires reflection. 513 */ 514 protected boolean hasLocalMethod(ATSymbol atSelector) throws InterpreterException { 515 if (_meths.containsKey(atSelector.base_text().asNativeText().javaValue)) { 516 return true; 517 } else { 518 return super.hasLocalMethod(atSelector); 519 } 520 } 521 522 /** 523 * @see NATTable#hasLocalMethod(ATSymbol) 524 */ 525 protected ATMethod getLocalMethod(ATSymbol selector) throws InterpreterException { 526 ATMethod val = _meths.get(selector.base_text().asNativeText().javaValue); 527 if (val == null) { 528 return super.getLocalMethod(selector); 529 } 530 return val; 531 } 532 533}