# BrainFunct Macro interpreter
# $Id: bfm.py 1.9 2001/01/04 22:47:45 vdp Exp vdp $
#
# LANGUAGE LEVEL 2: HIGHER-ORDER MACROS
# Author: Frédéric van der Plancke
#
# Disclaimer: the author(s) take no responsibility to whatever happens
# as a result of using (or not using) this program.
# License: public domain
_testing_ = 0
import string
import re
#input-output classes:
#input_fn is callable with type "unit -> char or None" where None means EOF
#output_fn is callable with type "char -> unit"
class BfRuntimeError:
def __init__(self, msg):
self.msg = msg
def __repr__(self):
return "BfRuntimeError(%s)" % self.msg
class BfRuntime:
def __init__(self, input_fn = None, output_fn = None):
self.array = 256 * [0]
self.array_start = 0
self.memptr = 0
if input_fn is None:
input_fn = (lambda : None)
assert callable(input_fn) # can't assert functional type more precisely...
self.input_fn = input_fn
if output_fn is None:
output_fn = (lambda c : None)
assert callable(output_fn)
self.output_fn = output_fn
self.ins = { '+': self.plus, '-': self.minus, '<': self.left, '>': self.right,
'.': self.output, ',': self.input }
def execute_ins(self, ins):
method = self.ins.get(ins)
if method is not None:
method()
else:
raise BfRuntimeError("Undefined BF instruction")
def plus(self):
self.array[self.memptr] = (self.array[self.memptr] + 1) % 256
def minus(self):
self.array[self.memptr] = (self.array[self.memptr] + 255) % 256
def left(self):
if self.memptr == 0:
n = min(100, len(self.array) / 3)
self.array = n * [0] + self.array
self.array_start = n + self.array_start
self.memptr = n + self.memptr
self.memptr = self.memptr - 1
def right(self):
if self.memptr <= len(self.array):
n = min(100, len(self.array) / 3)
self.array = self.array + n * [0]
self.memptr = self.memptr + 1
def is_zero(self):
return self.array[self.memptr] == 0
def input(self):
c = self.input_fn()
if c:
assert type(c) is type("") and len(c) is 1
i = ord(c)
else:
i = 0
self.array[self.memptr] = i
def output(self):
c = self.array[self.memptr]
self.output_fn(chr(c))
class BfmRuntime(BfRuntime):
debug_stack_depth_limit = 490
# getting such a big stack is easier than you think !
# limit is set to 490 because:
# * Python 2 stack depth limit is 1000 on my machine, and
# * each BFM macro call consumes two Python calls.
def __init__(self, input_fn = None, output_fn = None):
BfRuntime.__init__(self, input_fn, output_fn)
self.env_frame = Frame(None, None, None) #dummy
self.depth = 0 # depth of recursive calls to execute_sequence
def getCurrentFrame(self):
return self.env_frame
def setCurrentFrame(self, frame):
assert isinstance(frame, Frame)
self.env_frame = frame
def resetExecutionDepth(self):
self.depth = 0
def execute_sequence(self, body):
"""execute code_item sequence in current env, which is updated"""
def combined_sequence_value(old_value, new_value):
if new_value is None:
return old_value
else: #new_value is not None:
if old_value is not None:
ok = isinstance(new_value, GroundValue) and isinstance(old_value, GroundValue)
if not ok:
#print "BFERR - old value (%s) new value (%s)" % (old_value, new_value)
#raise BfRuntimeError("non-ground value in sequence")
raise BfRuntimeError("you tried to concatenate a not-fully-evaluated macro with something")
return new_value
# we must check depth, lest the Python stack be overflown; and that would be fatal.
if self.depth > self.debug_stack_depth_limit:
raise BfRuntimeError("stack depth exceeded debug limit : infinite recursion possible")
# a well-formed BFM program has no recursive macro-calls.
# YES BUT: we don't check this well enough: e.g. "&f(f); f(f)"
value = None
self.depth = self.depth + 1
for code_item in body:
assert isinstance(code_item, CodeItem)
new_value = code_item.execute(self)
value = combined_sequence_value(value, new_value)
self.depth = self.depth - 1
assert self.depth >= 0
if value is None: value = theGroundValue # no value -> empty ground value
return value
def execute_ground_sequence(self, body):
"""same as execute_sequence; ensure the result is ground."""
value = self.execute_sequence(body)
if not isinstance(value, GroundValue):
raise BfRuntimeError("non-ground value in ground sequence")
return value
def execute_sequence_in(self, body, env):
"""execute code_item sequence in given temporary env, which is discarded"""
assert isinstance(env, Frame)
old_env = self.getCurrentFrame()
self.setCurrentFrame(env)
value = self.execute_sequence(body)
self.setCurrentFrame(old_env)
return value
def str_sequence(sequence):
return string.join(map(str, sequence), "")
class GroundValue:
pass
theGroundValue = GroundValue()
class CodeItem:
pass
def execute(self, runtime):
raise "undefined interface fn"
class CodeInstruction(CodeItem):
def __init__(self, ins_char):
self.ins = ins_char
def execute(self, runtime):
runtime.execute_ins(self.ins)
return theGroundValue
def __str__(self):
return self.ins
def __repr__(self):
return "CodeInstruction(%s)" % repr(self.ins)
class CodeLoop(CodeItem):
def __init__(self, body):
self.body = body
def execute(self, runtime):
# make new frame
# execute body in new frame
outside_frame = runtime.getCurrentFrame()
while not runtime.is_zero():
runtime.execute_ground_sequence(self.body)
runtime.setCurrentFrame(outside_frame)
return theGroundValue #should combine values of sequence somehow ?
def __str__(self):
return "[%s]" % str_sequence(self.body)
def __repr__(self):
return "CodeLoop(%s)" % repr(self.body)
class CodeMacroCall(CodeItem):
def __init__(self, name, arg_list):
assert type(name) is type("")
# assert type(arg_list) is list of CodeItem lists...
self.name = name
self.arglist = arg_list
def execute(self, runtime):
caller_env = runtime.getCurrentFrame()
(macro_decl, inside_env) = caller_env.find(self.name) #may raise exception
arguments = self.arglist
# we can't use [self] in the sequel. Let's forbid it:
del self ##ever seen this ???
while 1:
#done?@@@to correct: change [outside_env] to [caller_env] and update it for subcalls
#LOOP:
# next iterations of this loop
# execute [macro_decl] with arguments [arguments]
# it cannot use [self] (except perhaps for error messages)
#if __debug__:
# print "CodeMacroCall, execution step:"
# print " formals = %s; args = %s; decl = %s" % (
# macro_decl.formals, arguments, macro_decl)
num_arguments = len(arguments)
num_formals = len(macro_decl.formals)
#--- bind arguments in new environment:
#inside_env = macro_decl.env
#assert inside_env, "call of non-already-executed macro declaration : internal error ?"
num_binds = min(num_formals, num_arguments)
binds = map(None, arguments[:num_binds], macro_decl.formals[:num_binds])
for (arg, formal) in binds:
pseudo_decl = CodeMacroDecl(formal, [], arg)
pseudo_decl.debug_info = "arg of macro %s at %d" % (str(macro_decl), id(macro_decl))
inside_env = Frame(inside_env, pseudo_decl, caller_env) #@why caller_env ?
#--- macro "execution" in new [inside_env]:
if num_formals > num_arguments:
# return closure value as CodeMacroDecl:
# such a closure never appears in code;
# it can have any name, as its name never appears in any frame.
closure_decl = CodeMacroDecl("", macro_decl.formals[num_binds:],
macro_decl.body)
return (closure_decl, inside_env)
else: #if num_formals <= num_arguments:
assert num_formals <= num_arguments
#-- call macro and return or use its value
runtime.setCurrentFrame(inside_env)
value = runtime.execute_sequence(macro_decl.body)
runtime.setCurrentFrame(caller_env) # restore frame
if num_formals == num_arguments:
return value
else:
if value is None or isinstance(value, GroundValue):
raise BfRuntimeError("call of non-macro closure, or too many arguments")
else:
(macro_decl, inside_env) = value
assert isinstance(macro_decl, CodeMacroDecl)
assert isinstance(inside_env, Frame)
arguments = arguments[num_binds :]
def __repr__(self):
return "CodeMacroCall(%s,%s)" % (repr(self.name), repr(self.arglist))
def __str__(self):
if self.arglist:
arg_strings = []
for arg in self.arglist:
arg_strings.append( str_sequence(arg) )
arg_string = string.join(arg_strings, "|")
return " %s(%s)" % (self.name, arg_string)
else:
return " " + self.name
class CodeMacroDecl(CodeItem):
def __init__(self, name, formals, body):
assert type(name) is type("")
assert type(formals) is type([]) # string list
self.name = name
self.formals = formals
self.body = body
# computed lazily:
self.type = None
def execute(self, runtime):
# (1) warning, a declaration can be executed several times
# AND NOT ALWAYS IN THE (semantically) SAME ENVIRONMENT.
# example: 'local' in "&thing(x) = &local(z)=x;; thing(+) thing(-)"
# (2) True, but now useless:
# a macro cannot possibly be called before its definition is reached & executed
# (since the definition does not belong to any Frame before it is executed)
env = runtime.getCurrentFrame()
new_env = Frame(env, self, env)
runtime.setCurrentFrame(new_env)
return None # no value
def __repr__(self):
return "CodeMacroDecl(%s,%s,%s)" % (repr(self.name), self.formals, repr(self.body))
def __str__(self):
if self.formals:
formals_string = "(" + string.join(self.formals, '|') + ")"
else:
formals_string = ""
return "&%s%s=%s;" % (self.name, formals_string, str_sequence(self.body))
class Code_debug_Dump(CodeItem):
def __init__(self,ident):
self.ident = ident
def execute(self, runtime):
print "?%s" % self.ident,
#return None # no value... this is @@@infraction to overall design
return theGroundValue
def __repr__(self):
return "Code_debug_Dump(%s)" % self.ident
def __str__(self):
return "?%s" % self.ident
#---------------------------------
class Frame:
def __init__(self, parent, macro_decl, decl_env):
if parent is not None:
# real cell:
assert isinstance(macro_decl, CodeMacroDecl)
self.parent = parent
self.macro_name = macro_decl.name
self.macro_decl = macro_decl
assert isinstance(decl_env, Frame)
self.macro_decl_env = decl_env
else:
# dummy
assert macro_decl is None
self.parent = None
self.macro_name = "+" # some invalid macro name
self.macro_decl = None
self.macro_decl_env = None
def find(self, name):
assert type(name) is type("")
if self.macro_name == name:
return (self.macro_decl, self.macro_decl_env)
elif self.parent is not None:
return self.parent.find(name)
else:
raise BfRuntimeError("undefined macro " + name)
#--- debug methods:
def dump(self):
if self.parent is not None:
print " ", self.macro_decl
self.parent.dump()
else:
print " end of frame"
def get_all_names(self):
countmap = {}
current = self
names = []
while current.parent is not None:
name = current.macro_name
count = countmap.get(name, 0)
countmap[name] = count + 1
if count > 0:
# name already met
name = "%s(%d)" % (name, count)
names.append(name)
current = current.parent
return names
'''
def find_n(self, name, n):
assert type(name) is type("")
if self.macro_name == name:
if n = 0:
return (self.macro_decl, self.macro_decl_env)
else:
return self.parent.find_n(name, n-1)
elif self.parent is not None:
return self.parent.find_n(name, n)
else:
raise BfRuntimeError("macro " + name + " not found")
def get_definition(self, name):
count = 0
if '(' in name:
try:
prog = re.compile("(.*)\((.*)\)$")
g = prog.match(name).groups()
print g
#@@@@@
name = 00000000
except:
raise BfRuntimeError("undefined macro " + name)
current = self
'''
def get_definition(self, name):
decl, env = self.find(name)
return str(decl)
#---------------------------------
_digits = "0123456789"
_alpha = "abcdefghijklmnopqrstuvwxyz"
_Alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
_identifier_head_char = _digits + _alpha + _Alpha + "_"
_identifier_body_char = _identifier_head_char
class ParseError:
def __init__(self, msg):
self.msg = msg
def __repr__(self):
return "ParseError(%s)" % self.msg
def parse_item(stream):
eat_space(stream)
c = stream.peek()
if c in "+-,.<>":
stream.junk()
return CodeInstruction(c)
elif c == "[":
stream.junk()
loop = parse_sequence(stream)
stream.eat("]")
return CodeLoop(loop)
elif c in _identifier_head_char:
ident = parse_identifier(stream)
args = parse_arguments(stream, parse_sequence)
return CodeMacroCall(ident, args)
elif c == "&":
stream.junk()
eat_space(stream)
ident = parse_identifier(stream)
formals = parse_arguments(stream, parse_identifier)
eat_space(stream)
stream.eat("=")
body = parse_sequence(stream)
stream.eat(";")
return CodeMacroDecl(ident, formals, body)
elif c == "?": # DEBUG ONLY
stream.junk()
ident = parse_identifier(stream)
return Code_debug_Dump(ident)
else:
return None
def parse_arguments(stream, arg_parser):
args = []
eat_space(stream)
d = stream.peek()
while (d == '('):
d = '|'
while d == '|':
stream.junk()
eat_space(stream)
arg = arg_parser(stream)
args.append(arg)
eat_space(stream)
d = stream.peek()
stream.eat(')')
eat_space(stream)
d = stream.peek()
return args
def parse_sequence(stream):
sequence = []
while 1:
item = parse_item(stream)
if item is None:
break
else:
sequence.append(item)
return sequence
def eat_space(stream):
_spaces = " \t"
_ends = "\n\0"
while 1:
c = stream.peek()
if c in _spaces:
stream.junk()
elif c == '#':
while not stream.peek() in _ends:
stream.junk()
stream.junk() # junk the end
else:
break
def parse_identifier(stream):
c = stream.peek()
if c not in _identifier_head_char:
raise ParseError("expected identifier")
ident = c
stream.junk()
while stream.peek() in _identifier_body_char:
ident = ident + stream.peek()
stream.junk()
return ident
#---------------------------------
class Stream:
def __init__(self, string):
self.string = string
self.index = 0
def eof(self):
return self.index >= len(self.string)
def peek(self):
if self.index < len(self.string):
return self.string[self.index]
else:
return "\0"
def junk(self):
self.index = self.index + 1
def eat(self, c):
assert len(c) == 1
if self.peek() != c:
raise ParseError("expected '%s' got '%s'" % (c, self.peek()))
else:
self.junk()
#------------------------------------------------------------------
# PARSING
#------------------------------------------------------------------
_parse_debug = _testing_
def parse(code):
stream = Stream(code)
c = parse_sequence(stream)
eat_space(stream)
stream.eat("\0")
if _parse_debug:
print "From:", code
print "Repr:", repr(c)
print "Str:", string.join(map(str,c), "")
return c
parse("+-,.<>")
parse("+[-]")
parse("+[-+][+[-]]")
parse("+- zut ou quoi")
parse("&zut=+-;")
parse("&zut=+-;&t(x)=;&t(a|b|c)=zut;")
parse("& zut = +- ; & t(x) = ; & t(a|b|c) = zut;")
parse(" & zut = + - ; & t ( x ) = ; & t ( a | b | c ) = zut ; ")
parse("+[?any-]")
parse("+ #comment\n - #other comment\n +")
try:
parse("&zut=[bad;]")
except ParseError:
print "ok 1"
library = [ # standard things:
# you better not call &meet_God;
"&meet_God= &f(f)=f(f); f(f);"
#standard things...
"&if(then)= [ then [-] ];",
#-- level 2 standard things
"&apply(x|y)=x(y);",
"&compose(f|g) = &f_g(x)=f(g(x)); f_g;", #better: &compose(f|g|x)=f(g(x));
"&id(x)=x;", # identity
# Concatenation numbers from 0 to 10
"&0(t)=; &1(t)=0()t; &2(t)=t t; &3(t)=t t t; &4(t)=2(2(t)); &5(t)=4(t) t; &6(t)=3(2(t));"
"&7(t)=4(t) 3(t); &8(t)=4(2(t)); &9(t)=3(3(t)); &10(t)=5(t t);",
# Concatenation number constructors
"&nn(x|y)= &nn_t(t)=x(10(t)) y(t); nn_t;",
"&NN(x|y|t)=x(10(t)) y(t);" # [nn] and [NN] are semantically identical
"&plus(x|y|t)=x(t)y(t);"
# Church numbers from 0 to 10;
# for such a number c whose value is n, c(t|x) applies n times macro t to parameter x
"&ch_0(t|x)=x; &ch_1(t|x)=t(x); &ch_2(t|x)=t(t(x)); &ch_3(t|x)=t(t(t(x)));"
"&ch_plus(n|m|t|x)=n(t|m(t|x));"
"&ch_times(n|m|t|x)=n(m(t)|x);"
"&ch_power(n|m) = m(ch_times(n) | ch_1);"
"&ch_4=ch_times(ch_2|ch_2); &ch_5=ch_plus(ch_2|ch_3); &ch_6=ch_times(ch_2|ch_3);"
"&ch_7=ch_plus(ch_3|ch_4); &ch_8=ch_power(ch_2|ch_3); &ch_9=ch_power(ch_3|ch_2);"
"&ch_10=ch_times(ch_2|ch_5);"
# Output of concatenation numbers, as numbers and as lowercase/uppercase letters:
# out(n), outa(n), outA(n)
"&out(n)=[-] n(+) .;"
"&outa(n)=out(plus(nn(9|6)|n)); &outA(n)=out(plus(nn(6|4)|n));"
# Output of Church numbers:
"&concat_of_ch(n|text) = n( & concat_text(right) = text right; concat_text | );"
"&ch_out(n)=out(concat_of_ch(n));"
]
test = [
"+++[?any -]",
"¯o=?macro; macro",
"¯o2(arg)= ?in arg ?out; macro2(?hello)",
"&zero=[-]; zero",
"&a=?a_outside; a zero++ [&a= ?a_inside; a -] a", # "?a_outside ?a_inside ?a_inside ?a_outside"
"&test= &b=?b_test; b; &b=?b_top; b test b test", # "?b_top ?b_test ?b_top ?b_test"
"&2x(arg)=arg arg; 2x(?arg)", # "?arg ?arg"
"&4x(arg4)=2x(arg4)2x(arg4); 4x(?4)", # "?4 ?4 ?4 ?4"
#and now for some more nasty things
"&2x(arg)=arg arg; &4x(arg)=2x(arg)2x(arg); 4x(?4)", # "?4 ?4 ?4 ?4" nastier than expected (cf note #1)
"&top(arg)= &zut=arg; zut; top(?arg)", # "?arg"
"&zombi(arg)=?zombi arg; zombi(&zombi=?inside; zombi)", # "?zombi ?inside"
"&thing(x)=&local(z)=x; local(+++); thing(?first) thing(?second)", # "?first ?second" (cf note #2)
"&a=?a; &test(a|b)=a b; test(?a_arg|a)", # "?a_arg ?a" and not "?a_arg ?a_arg": b evaluated in top context
#standard things...
"&if(then)= [ then [-] ];",
# LEVEL 2
"&2(m)=m m; &hello(n)=n(?hello); hello(2) ?and hello(2)",
"&apply(x|y)=x(y); &what=?what; apply(2 | what)",
"apply(2 | &temp=?temp; temp )",
"&compose(f|g) = &f_g(x)=f(g(x)); f_g;",
"&4=compose(2|2); 4(?4__)", # ?4__ ?4__ ?4__ ?4__
"&four_args(a|b|c|d)=a b c d; &three_args=four_args(?first);"
"&one_arg=three_args(?second|?third); one_arg(?last)", # ?first ?second ?third ?last
"&identity(x)=x; &APPLY=identity(apply); APPLY(2|?twice)", # ?twice ?twice
"&identity(x)=x; identity(apply|2|?twice)", # ?twice ?twice
# level 2 - nastier... or would-be nastier:
"&oh=?oh; &test=apply(2|oh); &oh=?oh2; ?ha identity(&oh=?inside; test) ?ah" # ?ha ?oh ?oh ?ah
#"&xxx(x)=x(+); &recursive= xxx(2); xxx(recursive)" -> BfRuntimeError
#"&f(x)=x(f); &g(x)=x(g); g(f)" -> BfRuntimeError: undefined g (in &g(x)=x(g))
#"&f(f|x)=f(f|x); f(f|burps)", -> BfRuntimeError: recursion!!! _definitely_ nastier.
# level 2 standard things
"&0(t)=; &1(t)=0()t; &2(t)=t t; &3(t)=t t t; &4(t)=2(2(t)); &5(t)=4(t) t; &6(t)=3(2(t));"
"&7(t)=4(t) 3(t); &8(t)=4(2(t)); &9(t)=3(3(t)); &10(t)=5(t t);",
"&nn(x|y)= &nn_t(t)=x(10(t)) y(t); nn_t;",
"&NN(x|y|t)=x(10(t)) y(t);" # [nn] and [NN] are semantically identical
"&out(n)=[-] n(+) .; out(nn(7|2)) out(NN(10|1))"
"&plus(x|y|t)=x(t)y(t); &outa(n)=out(plus(nn(9|6)|n)); &outA(n)=out(plus(nn(6|4)|n));"
"outa(plus(10|2)) outa(nn(1|2)) outa(nn(1|5))" # "Hello"
# bug in parser: f(x)(y) was refused, except on top-level where it did not do
# the expected thing. "(y)" part probably just got skipped.
"&f(g|x|y)=g(x)(y);",
"f(f)(3)",
]
### missing feature: define local things at top-level (loop executed once)
### -> no so missing !
### &local_block(block)=block; local_block(&temp=...; temp)
### &1x(x)=x; 1x(&temp=...; temp)
# note #1:
# bug: callee environment (inside_env) was build from caller environment (outside_env);
# it must be built from _declaration_ environment (macro_decl.env) instead.
# This error caused infinite looping in "&4xx(arg)=2x(arg)2x(arg); 4xx(?4)"
# for reasons I don't fully understand yet...
# (_did_ it cause infinite looping ? or was this "infinite looping' just apparent ?)
# note #2:
# corrected bug:
# Who's the silly boy who declared that a given macro declaration
# was always executed in the (semantically) same env ?
# That's simply not true. In
# "&thing(x)=&local(z)=x; local(+++); thing(?first) thing(?second)"
# "local"'s declaration is first executed with "&z=?first;" then with "&z=?second;".
# note #3:
# recursion... how to avoid it (in the language definition, not necessarily
# in this interpreter):
# (1) forbid f to be used inside of f code without being to some extent closed
# (2) static typing could detect and forbid recursive types
# e.g. "&f(g|x)=g(g|x);" would have type 'g -> 'x -> 't with 'g = 'g -> 'x -> 't,
# this last equation being forbidden. ('g cannot be equal to some complicated
# expression where 'g appears. Of course 'g = 'g is admitted.)
class InteractiveInputOutput:
def __init__(self):
import sys
self.cout = sys.stdout
self.to_flush = 0
self.input_string = ""
self.input_ptr = 0
def input(self):
self.flush()
while self.input_ptr >= len(self.input_string):
#print "Enter some input for BFM:"
inp = raw_input("BFM runtime input>")
self.input_string = inp
self.input_ptr = 0
c = self.input_string[self.input_ptr]
self.input_ptr = self.input_ptr + 1
return c
def output(self, c):
if ord(c) >= 32:
self.cout.write(c)
else:
self.cout.write("(%d)" % ord(c))
self.to_flush = 1
def flush(self):
if self.to_flush:
print
self.to_flush = 0
_parse_debug = 0
top_io = InteractiveInputOutput() #i/o for BF runtime
top_runtime = BfmRuntime(top_io.input, top_io.output)
if _testing_:
library = test
else:
library = library
for line in library:
c = parse(line)
if _testing_: print "<<<", str_sequence(c)
top_runtime.execute_sequence(c)
if _testing_: print
print "Final env:"
top_runtime.getCurrentFrame().dump()
welcome = """BrainFunctMacro top-level:
"exit" to quit
"?" for a list of all defined macros.
"?xxx" for the definition of macro &xxx;
"??" prints all macro definitions (might be long!);
"help" displays this message again
"""
def toplevel(initial_runtime = None, io = None):
runtime = initial_runtime
if runtime is None:
runtime = BfmRuntime()
# overwrite runtime input/output:
if io is not None:
#io = InteractiveInputOutput() #i/o for BF runtime
runtime.input_fn = io.input
runtime.output_fn = io.output
print welcome
while 1:
io.flush()
line = raw_input("BFM> ");
if line == "exit":
break
elif line == "?":
print "Defined macros (type ?xxx for definition of macro &xxx;):"
for name in top_runtime.getCurrentFrame().get_all_names():
print name,
print
elif line == "??":
top_runtime.getCurrentFrame().dump()
elif line[:1] == "?":
name = line[1:]
try:
value = top_runtime.getCurrentFrame().get_definition(name)
print value
except BfRuntimeError:
print "macro &%s; is undefined." % name
elif line == "help":
print welcome
else:
try:
rescue_frame = runtime.getCurrentFrame()
c = parse(line)
runtime.execute_sequence(c)
except ParseError, exc:
io.flush()
print "Parse error:", exc.msg
except BfRuntimeError, exc:
io.flush()
runtime.setCurrentFrame(rescue_frame)
runtime.resetExecutionDepth()
print "Runtime error:", exc.msg
#end while
#end def toplevel
toplevel(top_runtime, top_io)