Coverage for src / tracekit / dsl / repl.py: 94%

94 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-11 23:04 +0000

1"""TraceKit DSL REPL (Read-Eval-Print Loop). 

2 

3Interactive shell for TraceKit DSL. 

4""" 

5 

6import sys 

7 

8from tracekit.dsl.interpreter import Interpreter, InterpreterError 

9from tracekit.dsl.parser import parse_dsl 

10 

11 

12class REPL: 

13 """Interactive DSL shell.""" 

14 

15 def __init__(self) -> None: 

16 """Initialize REPL with new interpreter.""" 

17 self.interpreter = Interpreter() 

18 self.running = True 

19 

20 def print_banner(self) -> None: 

21 """Print welcome banner.""" 

22 print("TraceKit DSL REPL v0.1.0") 

23 print("Type 'exit' or 'quit' to exit, 'help' for help") 

24 print() 

25 

26 def print_help(self) -> None: 

27 """Print help message.""" 

28 print("TraceKit DSL Commands:") 

29 print(" load <filename> - Load a trace file") 

30 print(" filter <type> <params> - Apply filter (lowpass, highpass, etc.)") 

31 print(" measure <name> - Measure property (rise_time, etc.)") 

32 print(" plot - Plot trace") 

33 print(" export <format> - Export data (json, csv, hdf5)") 

34 print(" glob(<pattern>) - Match files") 

35 print() 

36 print("Variables:") 

37 print(" $name = expression - Assign variable") 

38 print(" $name - Reference variable") 

39 print() 

40 print("Pipelines:") 

41 print(" expr | command | command - Chain operations") 

42 print() 

43 print("Loops:") 

44 print(" for $var in expr: statement - Iterate") 

45 print() 

46 print("Special commands:") 

47 print(" help - Show this help") 

48 print(" vars - List variables") 

49 print(" exit, quit - Exit REPL") 

50 print() 

51 

52 def print_variables(self) -> None: 

53 """Print current variables.""" 

54 if not self.interpreter.variables: 

55 print("No variables defined") 

56 return 

57 

58 print("Variables:") 

59 for name, value in self.interpreter.variables.items(): 

60 # Truncate long values 

61 str_value = str(value) 

62 if len(str_value) > 60: 

63 str_value = str_value[:57] + "..." 

64 print(f" {name} = {str_value}") 

65 

66 def read_input(self) -> str | None: 

67 """Read input line from user. 

68 

69 Returns: 

70 Input line or None on EOF 

71 """ 

72 try: 

73 return input("tk> ") 

74 except EOFError: 

75 return None 

76 

77 def eval_special_command(self, line: str) -> bool: 

78 """Evaluate special REPL commands. 

79 

80 Args: 

81 line: Input line 

82 

83 Returns: 

84 True if special command was handled, False otherwise 

85 """ 

86 line = line.strip() 

87 

88 if line in ("exit", "quit"): 

89 self.running = False 

90 return True 

91 

92 if line == "help": 

93 self.print_help() 

94 return True 

95 

96 if line == "vars": 

97 self.print_variables() 

98 return True 

99 

100 return False 

101 

102 def eval_line(self, line: str) -> None: 

103 """Evaluate a single line of input. 

104 

105 Args: 

106 line: Input line 

107 """ 

108 line = line.strip() 

109 

110 # Skip empty lines 

111 if not line: 

112 return 

113 

114 # Skip comments 

115 if line.startswith("#"): 

116 return 

117 

118 # Check for special commands 

119 if self.eval_special_command(line): 

120 return 

121 

122 # Parse and execute 

123 try: 

124 ast = parse_dsl(line) 

125 self.interpreter.execute(ast) 

126 

127 # Print result if expression statement (not assignment) 

128 if ast and hasattr(ast[-1], "expression"): 

129 # Assignment - don't print 

130 pass 

131 else: 

132 # Expression - could print result 

133 # For now, commands handle their own output 

134 pass 

135 

136 except SyntaxError as e: 

137 print(f"Syntax error: {e}", file=sys.stderr) 

138 

139 except InterpreterError as e: 

140 print(f"Error: {e}", file=sys.stderr) 

141 

142 except Exception as e: 

143 print(f"Unexpected error: {e}", file=sys.stderr) 

144 

145 def run(self) -> None: 

146 """Run REPL loop.""" 

147 self.print_banner() 

148 

149 while self.running: 

150 line = self.read_input() 

151 

152 if line is None: 

153 # EOF 

154 print() 

155 break 

156 

157 self.eval_line(line) 

158 

159 print("Goodbye!") 

160 

161 

162def start_repl() -> None: 

163 """Start interactive REPL. 

164 

165 This is the main entry point for the DSL REPL. 

166 """ 

167 repl = REPL() 

168 repl.run() 

169 

170 

171if __name__ == "__main__": 

172 start_repl()