/interpreter/tags/at2dist220411/src/edu/vub/at/objects/natives/NATTable.java

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