diff options
Diffstat (limited to 'lib/Tcl.py')
| -rw-r--r-- | lib/Tcl.py | 712 |
1 files changed, 356 insertions, 356 deletions
@@ -4,27 +4,27 @@ # Design choices: # # - Names used for functions are not exactly those used by C Tcl. -# In Python, names without 'Tcl_' prefix are acceptable because -# names are less global than in C (and often they are prefixed -# with a module name anyway). Parameter conventions also differ. +# In Python, names without 'Tcl_' prefix are acceptable because +# names are less global than in C (and often they are prefixed +# with a module name anyway). Parameter conventions also differ. # # - The Tcl Interpreter type is implemented using a Python class. -# Almost all functions with an Interpreter as first parameter are -# methods of this class. -# Applications can create derived classes to add additional commands -# or to override specific internal functions. +# Almost all functions with an Interpreter as first parameter are +# methods of this class. +# Applications can create derived classes to add additional commands +# or to override specific internal functions. # # - Tcl errors are mapped to Python exceptions. -# (I bet Ousterhout would have done the same in a language with -# a proper exception mechanism). +# (I bet Ousterhout would have done the same in a language with +# a proper exception mechanism). # # - Tcl expressions are evaluated by Python's built-in function eval(). -# This makes Python Tcl scripts incompatible with C Tcl scripts, -# but is the only sensible solution for a quick-and-dirty version. -# It also makes an escape to Python possible. +# This makes Python Tcl scripts incompatible with C Tcl scripts, +# but is the only sensible solution for a quick-and-dirty version. +# It also makes an escape to Python possible. # # - The Backslash function interprets \<newline>, since it -# can return a string instead of a character. +# can return a string instead of a character. from TclUtil import * @@ -38,59 +38,59 @@ TclReturn = 'TclReturn' class CmdBuf(): - # - def Create(buffer): - buffer.string = '' - return buffer - # - def Assemble(buffer, str): - buffer.string = buffer.string + str - if buffer.string[-1:] = '\n': - i, end = 0, len(buffer.string) - try: - while i < end: - list, i = FindNextCommand( \ - buffer.string, i, end, 0) - except TclMatchingError: - return '' - except TclSyntaxError: - pass # Let Eval() return the error - ret = buffer.string - buffer.string = '' - return ret - else: - return '' + # + def Create(buffer): + buffer.string = '' + return buffer + # + def Assemble(buffer, str): + buffer.string = buffer.string + str + if buffer.string[-1:] = '\n': + i, end = 0, len(buffer.string) + try: + while i < end: + list, i = FindNextCommand( \ + buffer.string, i, end, 0) + except TclMatchingError: + return '' + except TclSyntaxError: + pass # Let Eval() return the error + ret = buffer.string + buffer.string = '' + return ret + else: + return '' class _Frame(): - def Create(frame): - frame.locals = {} - return frame + def Create(frame): + frame.locals = {} + return frame class _Proc(): - # - def Create(proc, (interp, args, body)): - proc.interp = interp - proc.args = SplitList(args) # Do this once here - proc.body = body - return proc - # - def Call(proc, argv): - if len(argv) <> len(proc.args)+1: - raise TclRuntimeError, \ - 'wrong # args to proc "' + \ - argv[0] + '"' - # XXX No defaults or variable length 'args' yet - frame = _Frame().Create() - for i in range(len(proc.args)): - frame.locals[proc.args[i]] = argv[i+1] - proc.interp.stack.append(frame) - try: - value = proc.interp.Eval(proc.body) - except TclReturn, value: - pass - del proc.interp.stack[-1:] - return value + # + def Create(proc, (interp, args, body)): + proc.interp = interp + proc.args = SplitList(args) # Do this once here + proc.body = body + return proc + # + def Call(proc, argv): + if len(argv) <> len(proc.args)+1: + raise TclRuntimeError, \ + 'wrong # args to proc "' + \ + argv[0] + '"' + # XXX No defaults or variable length 'args' yet + frame = _Frame().Create() + for i in range(len(proc.args)): + frame.locals[proc.args[i]] = argv[i+1] + proc.interp.stack.append(frame) + try: + value = proc.interp.Eval(proc.body) + except TclReturn, value: + pass + del proc.interp.stack[-1:] + return value import regexp @@ -98,311 +98,311 @@ _expand_prog = regexp.compile('([^[$\\]+|\n)*') del regexp class Interpreter(): - # - def Create(interp): - interp.globals = {} - interp.commands = {} - interp.stack = [] - interp.commands['break'] = interp.BreakCmd - interp.commands['concat'] = interp.ConcatCmd - interp.commands['continue'] = interp.ContinueCmd - interp.commands['echo'] = interp.EchoCmd - interp.commands['eval'] = interp.EvalCmd - interp.commands['expr'] = interp.ExprCmd - interp.commands['for'] = interp.ForCmd - interp.commands['glob'] = interp.GlobCmd - interp.commands['global'] = interp.GlobalCmd - interp.commands['if'] = interp.IfCmd - interp.commands['index'] = interp.IndexCmd - interp.commands['list'] = interp.ListCmd - interp.commands['proc'] = interp.ProcCmd - interp.commands['rename'] = interp.RenameCmd - interp.commands['return'] = interp.ReturnCmd - interp.commands['set'] = interp.SetCmd - return interp - # - def Delete(interp): - # - # Only break circular references here; - # most things will be garbage-collected. - # - for name in interp.commands.keys(): - del interp.commands[name] - # - def CreateCommand(interp, (name, proc)): - interp.commands[name] = proc - # - def DeleteCommand(interp, (name)): - del interp.commands[name] - # - # Local variables are maintained on the stack. - # A local variable with value "None" is a dummy - # meaning that the corresponding global variable - # should be used. - # - def GetVar(interp, varName): - dict = interp.globals - if interp.stack: - d = interp.stack[-1:][0].locals - if d.has_key(varName) and d[varName] = None: - pass - else: - dict = d - if not dict.has_key(varName): - raise TclRuntimeError, \ - 'Variable "' + varName + '" not found' - return dict[varName] - # - def SetVar(interp, (varName, newValue)): - dict = interp.globals - if interp.stack: - d = interp.stack[-1:][0].locals - if d.has_key(varName) and d[varName] = None: - pass - else: - dict = d - dict[varName] = newValue - # - def Expand(interp, (str, i, end)): - if end <= i: return '' - if str[i] = '{' and str[end-1] = '}': - return str[i+1:end-1] - if str[i] = '"' and str[end-1] = '"': - i, end = i+1, end-1 - result = '' - while i < end: - c = str[i] - if c = '\\': - x, i = Backslash(str, i, end) - result = result + x - elif c = '[': - j = BalanceBrackets(str, i, end) - x = interp.EvalBasic(str, i+1, j-1, 1) - result = result + x - i = j - elif c = '$': - i = i+1 - j = FindVarName(str, i, end) - name = str[i:j] - i = j - if not name: - result = result + '$' - else: - if name[:1] = '{' and name[-1:] = '}': - name = name[1:-1] - result = result + interp.GetVar(name) - else: - j = _expand_prog.exec(str, i) - j = min(j, end) - result = result + str[i:j] - i = j - return result - # - def EvalBasic(interp, (str, i, end, bracketed)): - result = '' - while i < end: - indexargv, i = FindNextCommand( \ - str, i, end, bracketed) - if indexargv: - argv = [] - for x, y in indexargv: - arg = interp.Expand(str, x, y) - argv.append(arg) - name = argv[0] - if not interp.commands.has_key(name): - raise TclRuntimeError, \ - 'Command "' + name + \ - '" not found' - result = interp.commands[name](argv) - return result - # - def Eval(interp, str): - return interp.EvalBasic(str, 0, len(str), 0) - # - def ExprBasic(interp, (str, begin, end)): - expr = interp.Expand(str, begin, end) - i = SkipSpaces(expr, 0, len(expr)) - expr = expr[i:] - try: - return eval(expr, {}) - except (NameError, TypeError, RuntimeError, EOFError), msg: - import sys - raise TclRuntimeError, sys.exc_type + ': ' + msg - # - def Expr(interp, str): - return interp.ExprBasic(str, 0, len(str)) - # - # The rest are command implementations - # - def BreakCmd(interp, argv): - if len(argv) <> 1: - raise TclRuntimeError, 'usage: break' - raise TclBreak - # - def ConcatCmd(interp, argv): - if len(argv) < 2: - raise TclRuntimeError, 'usage: concat arg ...' - return Concat(argv[1:]) - # - def ContinueCmd(interp, argv): - if len(argv) <> 1: - raise TclRuntimeError, 'usage: continue' - raise TclContinue - # - def EchoCmd(interp, argv): - for arg in argv[1:]: print arg, - print - return '' - # - def EvalCmd(interp, argv): - if len(argv) < 2: - raise TclRuntimeError, 'usage: eval arg [arg ...]' - str = Concat(argv[1:]) - return interp.Eval(str) - # - def ExprCmd(interp, argv): - if len(argv) <> 2: - raise TclRuntimeError, 'usage: expr expression' - expr = argv[1] - result = interp.Expr(expr) - if type(result) <> type(''): result = `result` - return result - # - def ForCmd(interp, argv): - if len(argv) <> 5: - raise TclRuntimeError, \ - 'usage: for start test next body' - x = interp.Eval(argv[1]) - while interp.Expr(argv[2]): - try: - x = interp.Eval(argv[4]) - except TclBreak: - break - except TclContinue: - pass - x = interp.Eval(argv[3]) - return '' - # - def GlobCmd(interp, argv): - import macglob - if len(argv) < 2: - raise TclRuntimeError, 'usage: glob pattern ...' - list = [] - for pat in argv[1:]: - list = list + macglob.glob(pat) - if not list: - raise TclRuntimeError, 'no match for glob pattern(s)' - return BuildList(list) - # - def GlobalCmd(interp, argv): - if len(argv) < 2: - raise TclRuntimeError, 'usage: global varname ...' - if not interp.stack: - raise TclRuntimeError, 'global used outside proc' - dict = interp.stack[-1:][0].locals - for name in argv[1:]: - dict[name] = None - return '' - # - def IfCmd(interp, argv): - argv = argv[:] - if len(argv) > 2 and argv[2] = 'then': del argv[2] - if len(argv) > 3 and argv[3] = 'else': del argv[3] - if not 3 <= len(argv) <= 4: - raise TclRuntimeError, \ - 'usage: if test [then] trueBody [else] falseBody' - if interp.Expr(argv[1]): - return interp.Eval(argv[2]) - if len(argv) > 3: - return interp.Eval(argv[3]) - return '' - # - def IndexCmd(interp, argv): - if len(argv) <> 3: - raise TclRuntimeError, 'usage: index value index' - import string - try: - index = string.atoi(argv[2]) - if index < 0: raise string.atoi_error - except string.atoi_error: - raise TclRuntimeError, 'bad index: ' + argv[2] - list = SplitList(argv[1]) - if index >= len(list): return '' - return list[index] - # - def ListCmd(interp, argv): - if len(argv) < 2: - raise TclRuntimeError, 'usage: list arg ...' - return BuildList(argv[1:]) - # - def ProcCmd(interp, argv): - if len(argv) <> 4: - raise TclRuntimeError, 'usage: proc name args body' - x = _Proc().Create(interp, argv[2], argv[3]) - interp.CreateCommand(argv[1], x.Call) - return '' - # - def RenameCmd(interp, argv): - if len(argv) <> 3: - raise TclRuntimeError, 'usage: rename oldName newName' - oldName, newName = argv[1], argv[2] - if not interp.commands.has_key(oldName): - raise TclRuntimeError, \ - 'command "' + oldName + '" not found' - if newName: interp.commands[newName] = interp.commands[oldName] - del interp.commands[oldName] - return '' - # - def ReturnCmd(interp, argv): - if not 1 <= len(argv) <= 2: - raise TclRuntimeError, 'usage: return [arg]' - if len(argv) = 1: raise TclReturn, '' - raise TclReturn, argv[1] - # - def SetCmd(interp, argv): - n = len(argv) - if not 2 <= n <= 3: - raise TclRuntimeError, 'usage: set varname [newvalue]' - if n = 2: return interp.GetVar(argv[1]) - interp.SetVar(argv[1], argv[2]) - return '' + # + def Create(interp): + interp.globals = {} + interp.commands = {} + interp.stack = [] + interp.commands['break'] = interp.BreakCmd + interp.commands['concat'] = interp.ConcatCmd + interp.commands['continue'] = interp.ContinueCmd + interp.commands['echo'] = interp.EchoCmd + interp.commands['eval'] = interp.EvalCmd + interp.commands['expr'] = interp.ExprCmd + interp.commands['for'] = interp.ForCmd + interp.commands['glob'] = interp.GlobCmd + interp.commands['global'] = interp.GlobalCmd + interp.commands['if'] = interp.IfCmd + interp.commands['index'] = interp.IndexCmd + interp.commands['list'] = interp.ListCmd + interp.commands['proc'] = interp.ProcCmd + interp.commands['rename'] = interp.RenameCmd + interp.commands['return'] = interp.ReturnCmd + interp.commands['set'] = interp.SetCmd + return interp + # + def Delete(interp): + # + # Only break circular references here; + # most things will be garbage-collected. + # + for name in interp.commands.keys(): + del interp.commands[name] + # + def CreateCommand(interp, (name, proc)): + interp.commands[name] = proc + # + def DeleteCommand(interp, (name)): + del interp.commands[name] + # + # Local variables are maintained on the stack. + # A local variable with value "None" is a dummy + # meaning that the corresponding global variable + # should be used. + # + def GetVar(interp, varName): + dict = interp.globals + if interp.stack: + d = interp.stack[-1:][0].locals + if d.has_key(varName) and d[varName] = None: + pass + else: + dict = d + if not dict.has_key(varName): + raise TclRuntimeError, \ + 'Variable "' + varName + '" not found' + return dict[varName] + # + def SetVar(interp, (varName, newValue)): + dict = interp.globals + if interp.stack: + d = interp.stack[-1:][0].locals + if d.has_key(varName) and d[varName] = None: + pass + else: + dict = d + dict[varName] = newValue + # + def Expand(interp, (str, i, end)): + if end <= i: return '' + if str[i] = '{' and str[end-1] = '}': + return str[i+1:end-1] + if str[i] = '"' and str[end-1] = '"': + i, end = i+1, end-1 + result = '' + while i < end: + c = str[i] + if c = '\\': + x, i = Backslash(str, i, end) + result = result + x + elif c = '[': + j = BalanceBrackets(str, i, end) + x = interp.EvalBasic(str, i+1, j-1, 1) + result = result + x + i = j + elif c = '$': + i = i+1 + j = FindVarName(str, i, end) + name = str[i:j] + i = j + if not name: + result = result + '$' + else: + if name[:1] = '{' and name[-1:] = '}': + name = name[1:-1] + result = result + interp.GetVar(name) + else: + j = _expand_prog.exec(str, i) + j = min(j, end) + result = result + str[i:j] + i = j + return result + # + def EvalBasic(interp, (str, i, end, bracketed)): + result = '' + while i < end: + indexargv, i = FindNextCommand( \ + str, i, end, bracketed) + if indexargv: + argv = [] + for x, y in indexargv: + arg = interp.Expand(str, x, y) + argv.append(arg) + name = argv[0] + if not interp.commands.has_key(name): + raise TclRuntimeError, \ + 'Command "' + name + \ + '" not found' + result = interp.commands[name](argv) + return result + # + def Eval(interp, str): + return interp.EvalBasic(str, 0, len(str), 0) + # + def ExprBasic(interp, (str, begin, end)): + expr = interp.Expand(str, begin, end) + i = SkipSpaces(expr, 0, len(expr)) + expr = expr[i:] + try: + return eval(expr, {}) + except (NameError, TypeError, RuntimeError, EOFError), msg: + import sys + raise TclRuntimeError, sys.exc_type + ': ' + msg + # + def Expr(interp, str): + return interp.ExprBasic(str, 0, len(str)) + # + # The rest are command implementations + # + def BreakCmd(interp, argv): + if len(argv) <> 1: + raise TclRuntimeError, 'usage: break' + raise TclBreak + # + def ConcatCmd(interp, argv): + if len(argv) < 2: + raise TclRuntimeError, 'usage: concat arg ...' + return Concat(argv[1:]) + # + def ContinueCmd(interp, argv): + if len(argv) <> 1: + raise TclRuntimeError, 'usage: continue' + raise TclContinue + # + def EchoCmd(interp, argv): + for arg in argv[1:]: print arg, + print + return '' + # + def EvalCmd(interp, argv): + if len(argv) < 2: + raise TclRuntimeError, 'usage: eval arg [arg ...]' + str = Concat(argv[1:]) + return interp.Eval(str) + # + def ExprCmd(interp, argv): + if len(argv) <> 2: + raise TclRuntimeError, 'usage: expr expression' + expr = argv[1] + result = interp.Expr(expr) + if type(result) <> type(''): result = `result` + return result + # + def ForCmd(interp, argv): + if len(argv) <> 5: + raise TclRuntimeError, \ + 'usage: for start test next body' + x = interp.Eval(argv[1]) + while interp.Expr(argv[2]): + try: + x = interp.Eval(argv[4]) + except TclBreak: + break + except TclContinue: + pass + x = interp.Eval(argv[3]) + return '' + # + def GlobCmd(interp, argv): + import macglob + if len(argv) < 2: + raise TclRuntimeError, 'usage: glob pattern ...' + list = [] + for pat in argv[1:]: + list = list + macglob.glob(pat) + if not list: + raise TclRuntimeError, 'no match for glob pattern(s)' + return BuildList(list) + # + def GlobalCmd(interp, argv): + if len(argv) < 2: + raise TclRuntimeError, 'usage: global varname ...' + if not interp.stack: + raise TclRuntimeError, 'global used outside proc' + dict = interp.stack[-1:][0].locals + for name in argv[1:]: + dict[name] = None + return '' + # + def IfCmd(interp, argv): + argv = argv[:] + if len(argv) > 2 and argv[2] = 'then': del argv[2] + if len(argv) > 3 and argv[3] = 'else': del argv[3] + if not 3 <= len(argv) <= 4: + raise TclRuntimeError, \ + 'usage: if test [then] trueBody [else] falseBody' + if interp.Expr(argv[1]): + return interp.Eval(argv[2]) + if len(argv) > 3: + return interp.Eval(argv[3]) + return '' + # + def IndexCmd(interp, argv): + if len(argv) <> 3: + raise TclRuntimeError, 'usage: index value index' + import string + try: + index = string.atoi(argv[2]) + if index < 0: raise string.atoi_error + except string.atoi_error: + raise TclRuntimeError, 'bad index: ' + argv[2] + list = SplitList(argv[1]) + if index >= len(list): return '' + return list[index] + # + def ListCmd(interp, argv): + if len(argv) < 2: + raise TclRuntimeError, 'usage: list arg ...' + return BuildList(argv[1:]) + # + def ProcCmd(interp, argv): + if len(argv) <> 4: + raise TclRuntimeError, 'usage: proc name args body' + x = _Proc().Create(interp, argv[2], argv[3]) + interp.CreateCommand(argv[1], x.Call) + return '' + # + def RenameCmd(interp, argv): + if len(argv) <> 3: + raise TclRuntimeError, 'usage: rename oldName newName' + oldName, newName = argv[1], argv[2] + if not interp.commands.has_key(oldName): + raise TclRuntimeError, \ + 'command "' + oldName + '" not found' + if newName: interp.commands[newName] = interp.commands[oldName] + del interp.commands[oldName] + return '' + # + def ReturnCmd(interp, argv): + if not 1 <= len(argv) <= 2: + raise TclRuntimeError, 'usage: return [arg]' + if len(argv) = 1: raise TclReturn, '' + raise TclReturn, argv[1] + # + def SetCmd(interp, argv): + n = len(argv) + if not 2 <= n <= 3: + raise TclRuntimeError, 'usage: set varname [newvalue]' + if n = 2: return interp.GetVar(argv[1]) + interp.SetVar(argv[1], argv[2]) + return '' # The rest are just demos: def MainLoop(interp): - buffer = CmdBuf().Create() - if not interp.globals.has_key('ps1'): interp.globals['ps1'] = '% ' - if not interp.globals.has_key('ps2'): interp.globals['ps2'] = '' - psname = 'ps1' - while 1: - try: - line = raw_input(interp.globals[psname]) - except (EOFError, KeyboardInterrupt): - print - break - line = buffer.Assemble(line + '\n') - if not line: - psname = 'ps2' - else: - psname = 'ps1' - try: - x = interp.Eval(line) - if x <> '': print 'Result:', `x` - except (TclRuntimeError, TclSyntaxError, \ - TclMatchingError), msg: - print 'Error:', msg - except (TclBreak, TclContinue): - print 'Error: break or continue outside loop' - except TclReturn, value: - # Return outside proc returns to main loop - if value: print value + buffer = CmdBuf().Create() + if not interp.globals.has_key('ps1'): interp.globals['ps1'] = '% ' + if not interp.globals.has_key('ps2'): interp.globals['ps2'] = '' + psname = 'ps1' + while 1: + try: + line = raw_input(interp.globals[psname]) + except (EOFError, KeyboardInterrupt): + print + break + line = buffer.Assemble(line + '\n') + if not line: + psname = 'ps2' + else: + psname = 'ps1' + try: + x = interp.Eval(line) + if x <> '': print 'Result:', `x` + except (TclRuntimeError, TclSyntaxError, \ + TclMatchingError), msg: + print 'Error:', msg + except (TclBreak, TclContinue): + print 'Error: break or continue outside loop' + except TclReturn, value: + # Return outside proc returns to main loop + if value: print value the_interpreter = Interpreter().Create() def main(): - MainLoop(the_interpreter) + MainLoop(the_interpreter) # XXX To do: |
