/interpreter/tags/at2dist110511/src/edu/vub/at/objects/natives/NATClosure.java

http://ambienttalk.googlecode.com/ · Java · 268 lines · 130 code · 24 blank · 114 comment · 19 complexity · 50bdaf4f4ec20dd9ec049a875548f46b MD5 · raw file

  1. /**
  2. * AmbientTalk/2 Project
  3. * NATClosure.java created on Jul 23, 2006 at 3:22:23 PM
  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 java.util.HashMap;
  30. import edu.vub.at.eval.Evaluator;
  31. import edu.vub.at.exceptions.InterpreterException;
  32. import edu.vub.at.exceptions.XIllegalOperation;
  33. import edu.vub.at.exceptions.signals.SignalEscape;
  34. import edu.vub.at.objects.ATBoolean;
  35. import edu.vub.at.objects.ATClosure;
  36. import edu.vub.at.objects.ATContext;
  37. import edu.vub.at.objects.ATMethod;
  38. import edu.vub.at.objects.ATObject;
  39. import edu.vub.at.objects.ATTable;
  40. import edu.vub.at.objects.coercion.NativeTypeTags;
  41. import edu.vub.at.objects.grammar.ATSymbol;
  42. import edu.vub.at.objects.mirrors.NativeClosure;
  43. import edu.vub.at.parser.SourceLocation;
  44. import edu.vub.util.TempFieldGenerator;
  45. /**
  46. * A NATClosure instance represents a first-class AmbientTalk closure.
  47. * A closure is modelled as a pair (method, context), where the method
  48. * contains the pure function (function name, arguments and body).
  49. *
  50. * The single most important operation to be performed on a closure is applying it.
  51. * This will give rise to the application of its underlying method within the context
  52. * wrapped by the closure.
  53. *
  54. * @author smostinc
  55. */
  56. public class NATClosure extends NATByRef implements ATClosure {
  57. // these instance variables are inherited and used by a NativeClosure as well.
  58. protected ATMethod method_;
  59. protected ATContext context_;
  60. /**
  61. * This constructor creates a closure with a bound dynamic receiver, and it is
  62. * called after the succesful lookup of a receiverful message.
  63. * @param method the method being wrapped into a closure.
  64. * @param implementor the object in which the definition is nested.
  65. * @param receiver the object where the lookup was initiated.
  66. */
  67. public NATClosure(ATMethod method, ATObject implementor, ATObject receiver) throws InterpreterException {
  68. this(method, new NATContext(
  69. /* scope = implementor (to be extended with a callframe upon closure application) */
  70. implementor,
  71. /* self = ` start of lookup ` */
  72. receiver));
  73. }
  74. public NATClosure(ATMethod method, ATContext context) {
  75. method_ = method;
  76. context_ = context;
  77. }
  78. /**
  79. * To apply a closure, apply its underlying method with the context of the closure,
  80. * rather than the runtime context of the invoker.
  81. */
  82. public ATObject base_apply(ATTable arguments) throws InterpreterException {
  83. return method_.base_apply(arguments, this.base_context());
  84. }
  85. /**
  86. * To apply a closure in a given scope, apply its underlying method with a new context
  87. * constructed from the scope object.
  88. */
  89. public ATObject base_applyInScope(ATTable args, ATObject scope) throws InterpreterException {
  90. return method_.base_applyInScope(args, new NATContext(scope, scope));
  91. }
  92. /**
  93. * receiver is a zero-argument block closure returning a boolean
  94. * @param body a zero-argument block closure
  95. *
  96. * def whileTrue: body {
  97. * self.apply().ifTrue: {
  98. * body();
  99. * self.whileTrue: body
  100. * }
  101. * }
  102. */
  103. public ATObject base_whileTrue_(final ATClosure body) throws InterpreterException {
  104. /* ATObject result = NATNil._INSTANCE_;
  105. while (this.meta_apply(NATTable.EMPTY).asNativeBoolean().javaValue) {
  106. result = body.meta_apply(NATTable.EMPTY);
  107. }
  108. return result; */
  109. ATBoolean cond;
  110. while (true) {
  111. // cond = self.apply()
  112. cond = this.base_apply(NATTable.EMPTY).asBoolean();
  113. if(cond.isNativeBoolean()) {
  114. // cond is a native boolean, perform the conditional ifTrue: test natively
  115. if (cond.asNativeBoolean().javaValue) {
  116. // execute body and continue while loop
  117. body.base_apply(NATTable.EMPTY);
  118. continue;
  119. } else {
  120. // return nil
  121. return Evaluator.getNil();
  122. }
  123. } else {
  124. // cond is a user-defined boolean, do a recursive send
  125. return cond.base_ifTrue_(new NativeClosure(this) {
  126. public ATObject base_apply(ATTable args) throws InterpreterException {
  127. // if user-defined bool is true, execute body and recurse
  128. body.base_apply(NATTable.EMPTY);
  129. return base_whileTrue_(body);
  130. }
  131. });
  132. }
  133. }
  134. }
  135. /**
  136. * The following is a pseudo-code implementation of escape. The important difference
  137. * between the native implementation and this pseudo-code is that the 'escaping exception'
  138. * can *not* be caught at the AmbientTalk level. The SignalEscape is a truly native exception.
  139. *
  140. * def block.escape() {
  141. * def returned := false;
  142. * def quit(@args) {
  143. * if: (returned) then: {
  144. * raise: XIllegalOperation.new("Cannot quit, escape activation already returned")
  145. * } else: {
  146. * raise: SignalEscape.new(block, if: (args.isEmpty()) then: nil else: args[1])
  147. * }
  148. * };
  149. *
  150. * try: {
  151. * block(quit);
  152. * } catch: SignalEscape using: {|e|
  153. * if: (e.block == block) then: {
  154. * e.val
  155. * } else: {
  156. * raise: e
  157. * }
  158. * } finally: {
  159. * returned := true;
  160. * }
  161. * }
  162. */
  163. public ATObject base_escape() throws InterpreterException {
  164. final QuitClosureFrame f = new QuitClosureFrame();
  165. NativeClosure quit = new NativeClosure(this) {
  166. public ATObject base_apply(ATTable args) throws InterpreterException {
  167. if (f.alreadyReturned) {
  168. throw new XIllegalOperation("Cannot quit, escape activation already returned");
  169. } else {
  170. ATObject val;
  171. if (args.base_isEmpty().asNativeBoolean().javaValue) {
  172. val = Evaluator.getNil();
  173. } else {
  174. val = get(args, 1);
  175. }
  176. throw new SignalEscape(this.scope_.asClosure(), val);
  177. }
  178. }
  179. };
  180. try {
  181. return this.base_apply(NATTable.atValue(new ATObject[] { quit }));
  182. } catch(SignalEscape e) {
  183. if (e.originatingBlock == this) {
  184. return e.returnedValue;
  185. } else {
  186. // propagate the signal, it did not originate from this block
  187. throw e;
  188. }
  189. } finally {
  190. f.alreadyReturned = true;
  191. }
  192. }
  193. // helper class to get around the fact that Java has no true closures and hence
  194. // does not allow access to mutable lexically scoped free variables
  195. static private class QuitClosureFrame {
  196. /** if true, the escape block has already been exited */
  197. public boolean alreadyReturned = false;
  198. }
  199. public ATContext base_context() throws InterpreterException {
  200. return context_;
  201. }
  202. public ATMethod base_method() {
  203. return method_;
  204. }
  205. public ATClosure asClosure() {
  206. return this;
  207. }
  208. public NATText meta_print() throws InterpreterException {
  209. return NATText.atValue("<closure:"+method_.base_name()+">");
  210. }
  211. public NATText impl_asCode(TempFieldGenerator objectMap) throws InterpreterException {
  212. if(objectMap.contains(this)) {
  213. return objectMap.getName(this);
  214. }
  215. NATText name = objectMap.put(this, ((NATMethod) method_).impl_asCode(objectMap, true));
  216. return name;
  217. }
  218. public ATTable meta_typeTags() throws InterpreterException {
  219. return NATTable.of(NativeTypeTags._CLOSURE_);
  220. }
  221. public ATObject meta_clone() throws InterpreterException {
  222. return this;
  223. }
  224. // Debugging API:
  225. private SourceLocation loc_;
  226. public SourceLocation impl_getLocation() { return loc_; }
  227. public void impl_setLocation(SourceLocation loc) {
  228. // overriding the source location of an AmbientTalk object
  229. // is probably the sign of a bug: locations should be single-assignment
  230. // to prevent mutable shared-state. That is, loc_ is effectively 'final'
  231. if (loc_ == null) {
  232. loc_ = loc;
  233. } else {
  234. throw new RuntimeException("Trying to override source location of "+this.toString()+" from "+loc_+" to "+loc);
  235. }
  236. }
  237. public SourceLocation impl_getSourceOf(ATSymbol sel) throws InterpreterException {
  238. if (sel == Evaluator._APPLY_) {
  239. return method_.impl_getLocation();
  240. } else {
  241. return super.impl_getLocation();
  242. }
  243. }
  244. }