https://github.com/akkartik/mu/blob/master/026call.cc
  1 //: So far the recipes we define can't run each other. Let's fix that.
  2 
  3 :(scenario calling_recipe)
  4 def main [
  5   f
  6 ]
  7 def f [
  8   3:num <- add 2, 2
  9 ]
 10 +mem: storing 4 in location 3
 11 
 12 :(scenario return_on_fallthrough)
 13 def main [
 14   f
 15   1:num <- copy 0
 16   2:num <- copy 0
 17   3:num <- copy 0
 18 ]
 19 def f [
 20   4:num <- copy 0
 21   5:num <- copy 0
 22 ]
 23 +run: f
 24 # running f
 25 +run: {4: "number"} <- copy {0: "literal"}
 26 +run: {5: "number"} <- copy {0: "literal"}
 27 
#!/usr/bin/python
# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""Run all the tests inside the test/ directory as a test suite."""
if __name__ == '__main__':
	from re import compile
	from test import *
	from time import time
	from types import FunctionType as function
	from sys import argv
	bms = []
	try:
		n = int(argv[1])
	except IndexError:
		n = 10
	if len(argv) > 2:
		args = [compile(re) for re in argv[2:]]
		def allow(name):
			for re in args:
				if re.search(name):
					return True
			else:
				return False
	else:
		allow = lambda name: True
	for key, val in vars().copy().items():
		if key.startswith('bm_'):
			bms.extend(v for k,v in vars(val).items() if type(v) == type)
	for bmclass in bms:
		for attrname in vars(bmclass):
			if not attrname.startswith('bm_'):
				continue
			bmobj = bmclass()
			t1 = time()
			method = getattr(bmobj, attrname)
			methodname = "{0}.{1}".format(bmobj.__class__.__name__, method.__name__)
			if allow(methodname):
				try:
					method(n)
				except:
					print("{0} failed!".format(methodname))
					raise
				else:
					t2 = time()
					print("{0:60}: {1:10}s".format(methodname, t2 - t1))
l.running_recipe).steps.at(call.running_step_index); 134 //? } 135 136 :(code) 137 void dump_callstack() { 138 if (!Current_routine) return; 139 if (Current_routine->calls.size() <= 1) return; 140 for (call_stack::const_iterator p = ++Current_routine->calls.begin(); p != Current_routine->calls.end(); ++p) 141 raise << " called from " << get(Recipe, p->running_recipe).name << ": " << to_original_string(to_instruction(*p)) << '\n' << end(); 142 } 143 144 :(after "Defined Recipe Checks") 145 // not a primitive; check that it's present in the book of recipes 146 if (!contains_key(Recipe, inst.operation)) { 147 raise << maybe(get(Recipe, r).name) << "undefined operation in '" << to_original_string(inst) << "'\n" << end(); 148 break; 149 } 150 :(replace{} "default:" following "End Primitive Recipe Implementations") 151 default: { 152 if (contains_key(Recipe, current_instruction().operation)) { // error already raised in Checks above 153 // not a primitive; look up the book of recipes 154 if (Trace_stream) { 155 ++Trace_stream->callstack_depth; 156 trace("trace") << "incrementing callstack depth to " << Trace_stream->callstack_depth << end(); 157 assert(Trace_stream->callstack_depth < 9000); // 9998-101 plus cushion 158 } 159 const call& caller_frame = current_call(); 160 Current_routine->calls.push_front(call(to_instruction(caller_frame).operation)); 161 finish_call_housekeeping(to_instruction(caller_frame), ingredients); 162 // not done with caller 163 write_products = false; 164 fall_through_to_next_instruction = false; 165 // End Non-primitive Call(caller_frame) 166 } 167 } 168 :(code) 169 void finish_call_housekeeping(const instruction& call_instruction, const vector<vector<double> >& ingredients) { 170 // End Call Housekeeping 171 } 172 173 :(scenario calling_undefined_recipe_fails) 174 % Hide_errors = true; 175 def main [ 176 foo 177 ] 178 +error: main: undefined operation in 'foo' 179 180 :(scenario calling_undefined_recipe_handles_missing_result) 181 % Hide_errors = true; 182 def main [ 183 x:num <- foo 184 ] 185 +error: main: undefined operation in 'x:num <- foo' 186 187 //:: finally, we need to fix the termination conditions for the run loop 188 189 :(replace{} "bool routine::completed() const") 190 bool routine::completed() const { 191 return calls.empty(); 192 } 193 194 :(replace{} "const vector<instruction>& routine::steps() const") 195 const vector<instruction>& routine::steps() const { 196 assert(!calls.empty()); 197 return get(Recipe, calls.front().running_recipe).steps; 198 } 199 200 :(after "Running One Instruction") 201 // when we reach the end of one call, we may reach the end of the one below 202 // it, and the one below that, and so on 203 while (current_step_index() >= SIZE(Current_routine->steps())) { 204 // Falling Through End Of Recipe 205 if (Trace_stream) { 206 trace("trace") << "fall-through: exiting " << current_recipe_name() << "; decrementing callstack depth from " << Trace_stream->callstack_depth << end(); 207 --Trace_stream->callstack_depth; 208 assert(Trace_stream->callstack_depth >= 0); 209 } 210 Current_routine->calls.pop_front(); 211 if (Current_routine->calls.empty()) goto stop_running_current_routine; 212 // Complete Call Fallthrough 213 // todo: fail if no products returned 214 ++current_step_index(); 215 } 216 217 :(before "End Primitive Recipe Declarations") 218 _DUMP_CALL_STACK, 219 :(before "End Primitive Recipe Numbers") 220 put(Recipe_ordinal, "$dump-call-stack", _DUMP_CALL_STACK); 221 :(before "End Primitive Recipe Checks") 222 case _DUMP_CALL_STACK: { 223 break; 224 } 225 :(before "End Primitive Recipe Implementations") 226 case _DUMP_CALL_STACK: { 227 dump(Current_routine->calls); 228 break; 229 } 230 :(code) 231 void dump(const call_stack& calls) { 232 for (call_stack::const_reverse_iterator p = calls.rbegin(); p != calls.rend(); ++p) 233 cerr << get(Recipe, p->running_recipe).name << ":" << p->running_step_index << " -- " << to_string(to_instruction(*p)) << '\n'; 234 }