PageRenderTime 47ms CodeModel.GetById 9ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 1ms

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