quantalogic 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. quantalogic/__init__.py +20 -0
  2. quantalogic/agent.py +638 -0
  3. quantalogic/agent_config.py +138 -0
  4. quantalogic/coding_agent.py +83 -0
  5. quantalogic/event_emitter.py +223 -0
  6. quantalogic/generative_model.py +226 -0
  7. quantalogic/interactive_text_editor.py +190 -0
  8. quantalogic/main.py +185 -0
  9. quantalogic/memory.py +217 -0
  10. quantalogic/model_names.py +19 -0
  11. quantalogic/print_event.py +66 -0
  12. quantalogic/prompts.py +99 -0
  13. quantalogic/server/__init__.py +3 -0
  14. quantalogic/server/agent_server.py +633 -0
  15. quantalogic/server/models.py +60 -0
  16. quantalogic/server/routes.py +117 -0
  17. quantalogic/server/state.py +199 -0
  18. quantalogic/server/static/js/event_visualizer.js +430 -0
  19. quantalogic/server/static/js/quantalogic.js +571 -0
  20. quantalogic/server/templates/index.html +134 -0
  21. quantalogic/tool_manager.py +68 -0
  22. quantalogic/tools/__init__.py +46 -0
  23. quantalogic/tools/agent_tool.py +88 -0
  24. quantalogic/tools/download_http_file_tool.py +64 -0
  25. quantalogic/tools/edit_whole_content_tool.py +70 -0
  26. quantalogic/tools/elixir_tool.py +240 -0
  27. quantalogic/tools/execute_bash_command_tool.py +116 -0
  28. quantalogic/tools/input_question_tool.py +57 -0
  29. quantalogic/tools/language_handlers/__init__.py +21 -0
  30. quantalogic/tools/language_handlers/c_handler.py +33 -0
  31. quantalogic/tools/language_handlers/cpp_handler.py +33 -0
  32. quantalogic/tools/language_handlers/go_handler.py +33 -0
  33. quantalogic/tools/language_handlers/java_handler.py +37 -0
  34. quantalogic/tools/language_handlers/javascript_handler.py +42 -0
  35. quantalogic/tools/language_handlers/python_handler.py +29 -0
  36. quantalogic/tools/language_handlers/rust_handler.py +33 -0
  37. quantalogic/tools/language_handlers/scala_handler.py +33 -0
  38. quantalogic/tools/language_handlers/typescript_handler.py +42 -0
  39. quantalogic/tools/list_directory_tool.py +123 -0
  40. quantalogic/tools/llm_tool.py +119 -0
  41. quantalogic/tools/markitdown_tool.py +105 -0
  42. quantalogic/tools/nodejs_tool.py +515 -0
  43. quantalogic/tools/python_tool.py +469 -0
  44. quantalogic/tools/read_file_block_tool.py +140 -0
  45. quantalogic/tools/read_file_tool.py +79 -0
  46. quantalogic/tools/replace_in_file_tool.py +300 -0
  47. quantalogic/tools/ripgrep_tool.py +353 -0
  48. quantalogic/tools/search_definition_names.py +419 -0
  49. quantalogic/tools/task_complete_tool.py +35 -0
  50. quantalogic/tools/tool.py +146 -0
  51. quantalogic/tools/unified_diff_tool.py +387 -0
  52. quantalogic/tools/write_file_tool.py +97 -0
  53. quantalogic/utils/__init__.py +17 -0
  54. quantalogic/utils/ask_user_validation.py +12 -0
  55. quantalogic/utils/download_http_file.py +77 -0
  56. quantalogic/utils/get_coding_environment.py +15 -0
  57. quantalogic/utils/get_environment.py +26 -0
  58. quantalogic/utils/get_quantalogic_rules_content.py +19 -0
  59. quantalogic/utils/git_ls.py +121 -0
  60. quantalogic/utils/read_file.py +54 -0
  61. quantalogic/utils/read_http_text_content.py +101 -0
  62. quantalogic/xml_parser.py +242 -0
  63. quantalogic/xml_tool_parser.py +99 -0
  64. quantalogic-0.2.0.dist-info/LICENSE +201 -0
  65. quantalogic-0.2.0.dist-info/METADATA +1034 -0
  66. quantalogic-0.2.0.dist-info/RECORD +68 -0
  67. quantalogic-0.2.0.dist-info/WHEEL +4 -0
  68. quantalogic-0.2.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,240 @@
1
+ """Tool to execute Elixir code in an isolated Docker environment with Mix project support."""
2
+
3
+ import logging
4
+ import os
5
+ import subprocess
6
+ import tempfile
7
+
8
+ from quantalogic.tools.tool import Tool, ToolArgument
9
+
10
+ # Configure logging
11
+ logging.basicConfig(level=logging.INFO)
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class ElixirTool(Tool):
16
+ """Tool to execute Elixir code in an isolated Docker environment with Mix project support."""
17
+
18
+ name: str = "elixir_tool"
19
+ description: str = (
20
+ "Executes Elixir code within a Docker container using Mix for package management.\n\n"
21
+ "FEATURES:\n"
22
+ "- Full Mix project support with dependency management\n"
23
+ "- Isolated Docker environment execution\n"
24
+ "- Configurable Elixir versions\n"
25
+ "- Environment variable support\n"
26
+ "- Host directory mounting\n"
27
+ "- Memory limit configuration\n\n"
28
+ "EXECUTION ENVIRONMENT:\n"
29
+ "- Runs in an isolated Docker container\n"
30
+ "- Uses official Elixir Docker images\n"
31
+ "- Supports Mix package manager\n"
32
+ "- Full access to standard library\n\n"
33
+ "ACCEPTED OUTPUT METHODS:\n"
34
+ "- IO.puts/1, IO.write/1\n"
35
+ "- Logger module\n"
36
+ "- File operations when host_dir mounted\n\n"
37
+ "EXAMPLE:\n"
38
+ 'defmodule Example do\n def hello, do: IO.puts("Hello from Elixir!")\nend\n\nExample.hello()'
39
+ )
40
+
41
+ arguments: list[ToolArgument] = [
42
+ ToolArgument(
43
+ name="mix_commands",
44
+ arg_type="string",
45
+ description="Mix commands to run before executing script",
46
+ required=False,
47
+ example="mix deps.get && mix compile",
48
+ ),
49
+ ToolArgument(
50
+ name="script",
51
+ arg_type="string",
52
+ description="Elixir code to execute",
53
+ required=True,
54
+ example='IO.puts("Hello!")',
55
+ ),
56
+ ToolArgument(
57
+ name="version",
58
+ arg_type="string",
59
+ description="Elixir version to use",
60
+ required=False,
61
+ default="1.15",
62
+ ),
63
+ ToolArgument(
64
+ name="host_dir",
65
+ arg_type="string",
66
+ description="Host directory to mount",
67
+ required=False,
68
+ ),
69
+ ToolArgument(
70
+ name="memory_limit",
71
+ arg_type="string",
72
+ description="Container memory limit",
73
+ required=False,
74
+ example="512m",
75
+ ),
76
+ ToolArgument(
77
+ name="environment_vars",
78
+ arg_type="string",
79
+ description="Environment variables (KEY=VALUE)",
80
+ required=False,
81
+ example="MIX_ENV=prod",
82
+ ),
83
+ ]
84
+
85
+ def check_docker(self) -> None:
86
+ """Verify Docker is installed and accessible."""
87
+ try:
88
+ subprocess.run(
89
+ ["docker", "--version"],
90
+ check=True,
91
+ capture_output=True,
92
+ text=True,
93
+ )
94
+ except (subprocess.CalledProcessError, FileNotFoundError) as e:
95
+ raise RuntimeError("Docker is not installed or not accessible") from e
96
+
97
+ def validate_version(self, version: str) -> None:
98
+ """Validate Elixir version is supported."""
99
+ valid_versions = ["1.14", "1.15", "1.16"]
100
+ if version not in valid_versions:
101
+ raise ValueError(f"Unsupported Elixir version '{version}'. Valid versions: {', '.join(valid_versions)}")
102
+
103
+ def write_script(self, script_path: str, script: str) -> None:
104
+ """Write Elixir script to file."""
105
+ if not script.strip():
106
+ raise ValueError("Script content cannot be empty")
107
+
108
+ # Always wrap in ElixirScript module
109
+ wrapped_script = r"""
110
+ defmodule ElixirScript do
111
+ def main do
112
+ try do
113
+ %s
114
+ rescue
115
+ error ->
116
+ formatted = Exception.format(:error, error, __STACKTRACE__)
117
+ IO.puts("Error: #{formatted}")
118
+ System.halt(1)
119
+ end
120
+ end
121
+ end
122
+
123
+ # Execute the main function
124
+ ElixirScript.main()
125
+ """ % script.strip()
126
+
127
+ with open(script_path, "w", encoding="utf-8") as f:
128
+ f.write(wrapped_script.strip())
129
+
130
+ def execute(
131
+ self,
132
+ script: str,
133
+ mix_commands: str | None = None,
134
+ version: str = "1.15",
135
+ host_dir: str | None = None,
136
+ memory_limit: str | None = None,
137
+ environment_vars: str | None = None,
138
+ ) -> str:
139
+ """Execute Elixir code in Docker container with Mix support."""
140
+ # Validate inputs
141
+ self.check_docker()
142
+ self.validate_version(version)
143
+
144
+ docker_image = f"elixir:{version}-slim"
145
+
146
+ with tempfile.TemporaryDirectory() as temp_dir:
147
+ # Write script directly without Mix project
148
+ script_path = os.path.join(temp_dir, "script.exs")
149
+ self.write_script(script_path, script)
150
+
151
+ # Prepare Docker command
152
+ cmd = ["docker", "run", "--rm"]
153
+
154
+ # Add memory limit
155
+ if memory_limit:
156
+ cmd.extend(["--memory", memory_limit])
157
+
158
+ # Mount directories
159
+ cmd.extend(["-v", f"{temp_dir}:/app"])
160
+ cmd.extend(["-w", "/app"])
161
+
162
+ if host_dir:
163
+ cmd.extend(["-v", f"{host_dir}:/host"])
164
+
165
+ # Add environment variables
166
+ if environment_vars:
167
+ for pair in environment_vars.split():
168
+ if "=" in pair:
169
+ cmd.extend(["-e", pair])
170
+
171
+ # Add image and command to run the script directly
172
+ cmd.extend([docker_image, "elixir", "script.exs"])
173
+
174
+ # Execute
175
+ try:
176
+ result = subprocess.run(cmd, check=True, capture_output=True, text=True)
177
+ output = result.stdout
178
+ if result.stderr:
179
+ output = f"{output}\nErrors:\n{result.stderr}"
180
+ return output.strip()
181
+ except subprocess.CalledProcessError as e:
182
+ error_msg = e.stderr if e.stderr else e.stdout
183
+ raise RuntimeError(f"Execution failed: {error_msg}")
184
+
185
+
186
+ def main():
187
+ """Run example Elixir code executions."""
188
+ tool = ElixirTool()
189
+
190
+ print("\nExample 1: Simple Output")
191
+ simple_script = r"""
192
+ IO.puts("Starting simple output test...")
193
+ IO.puts("Hello from Elixir!")
194
+ IO.puts("Simple output test completed.")
195
+ """
196
+ print(tool.execute(script=simple_script))
197
+
198
+ print("\nExample 2: Basic Module")
199
+ module_script = r"""
200
+ IO.puts("Starting calculator module test...")
201
+
202
+ defmodule Calculator do
203
+ def add(a, b) do
204
+ IO.puts("Calculating #{a} + #{b}...")
205
+ result = a + b
206
+ IO.puts("Result: #{a} + #{b} = #{result}")
207
+ result
208
+ end
209
+ end
210
+
211
+ IO.puts("\nTesting Calculator.add(5, 3):")
212
+ result = Calculator.add(5, 3)
213
+ IO.puts("Calculator test completed. Final result: #{result}")
214
+ """
215
+ print(tool.execute(script=module_script))
216
+
217
+ print("\nExample 3: Binary Encoding")
218
+ json_script = r"""
219
+ IO.puts("Starting binary encoding test...")
220
+
221
+ IO.puts("Ensuring applications are started...")
222
+ {:ok, _} = Application.ensure_all_started(:inets)
223
+ {:ok, _} = Application.ensure_all_started(:ssl)
224
+
225
+ IO.puts("\nPreparing test data...")
226
+ data = %{name: "John", age: 30}
227
+ IO.puts("Data: #{inspect(data)}")
228
+
229
+ IO.puts("\nEncoding data...")
230
+ encoded = :erlang.term_to_binary(data)
231
+ |> Base.encode64()
232
+ IO.puts("Encoded result: #{encoded}")
233
+
234
+ IO.puts("\nBinary encoding test completed.")
235
+ """
236
+ print(tool.execute(script=json_script))
237
+
238
+
239
+ if __name__ == "__main__":
240
+ main()
@@ -0,0 +1,116 @@
1
+ """Tool for executing bash commands and capturing their output."""
2
+
3
+ import os
4
+ import subprocess
5
+ from typing import Dict, Optional, Union
6
+
7
+ from quantalogic.tools.tool import Tool, ToolArgument
8
+
9
+
10
+ class ExecuteBashCommandTool(Tool):
11
+ """Tool for executing bash commands and capturing their output."""
12
+
13
+ name: str = "execute_bash_tool"
14
+ description: str = "Executes a bash command and returns its output."
15
+ need_validation: bool = True
16
+ arguments: list = [
17
+ ToolArgument(
18
+ name="command",
19
+ arg_type="string",
20
+ description="The bash command to execute.",
21
+ required=True,
22
+ example="ls -la",
23
+ ),
24
+ ToolArgument(
25
+ name="working_dir",
26
+ arg_type="string",
27
+ description="The working directory where the command will be executed. Defaults to the current directory.",
28
+ required=False,
29
+ example="/path/to/directory",
30
+ ),
31
+ ToolArgument(
32
+ name="timeout",
33
+ arg_type="int",
34
+ description="Maximum time in seconds to wait for the command to complete. Defaults to 60 seconds.",
35
+ required=False,
36
+ example="60",
37
+ ),
38
+ # Removed the `env` argument from ToolArgument since it doesn't support `dict` type
39
+ ]
40
+
41
+ def execute(
42
+ self,
43
+ command: str,
44
+ working_dir: Optional[str] = None,
45
+ timeout: Union[int, str, None] = 60,
46
+ env: Optional[Dict[str, str]] = None,
47
+ ) -> str:
48
+ """Executes a bash command and returns its output.
49
+
50
+ Args:
51
+ command (str): The bash command to execute.
52
+ working_dir (str, optional): Working directory for command execution. Defaults to the current directory.
53
+ timeout (int or str, optional): Maximum execution time in seconds. Defaults to 60 seconds.
54
+ env (dict, optional): Environment variables to set for the command execution. Defaults to the current environment.
55
+
56
+ Returns:
57
+ str: The command output or error message.
58
+
59
+ Raises:
60
+ subprocess.TimeoutExpired: If the command execution exceeds the timeout.
61
+ subprocess.CalledProcessError: If the command returns a non-zero exit status.
62
+ ValueError: If the timeout cannot be converted to an integer.
63
+ """
64
+ # Convert timeout to integer, defaulting to 60 if None or invalid
65
+ try:
66
+ timeout_seconds = int(timeout) if timeout else 60
67
+ except (ValueError, TypeError):
68
+ timeout_seconds = 60
69
+
70
+ # Use the current working directory if no working directory is specified
71
+ cwd = working_dir if working_dir else os.getcwd()
72
+
73
+ # Use the current environment if no custom environment is specified
74
+ env_vars = os.environ.copy()
75
+ if env:
76
+ env_vars.update(env)
77
+
78
+ try:
79
+ # Execute the command with specified timeout, working directory, and environment
80
+ result = subprocess.run(
81
+ command,
82
+ shell=True,
83
+ cwd=cwd,
84
+ capture_output=True,
85
+ text=True,
86
+ timeout=timeout_seconds,
87
+ env=env_vars,
88
+ )
89
+
90
+ formated_result = (
91
+ "<command_output>"
92
+ f" <stdout>"
93
+ f"{result.stdout.strip()}"
94
+ f" </stdout>"
95
+ f" <stderr>"
96
+ f"{result.stderr.strip()}"
97
+ f" </stderr>"
98
+ f" <returncode>"
99
+ f" {result.returncode}"
100
+ f" </returncode>"
101
+ f"</command_output>"
102
+ )
103
+
104
+ return formated_result
105
+
106
+ except subprocess.TimeoutExpired:
107
+ return f"Command timed out after {timeout_seconds} seconds."
108
+ except subprocess.CalledProcessError as e:
109
+ return f"Command failed with error: {e.stderr.strip()}"
110
+ except Exception as e:
111
+ return f"Unexpected error executing command: {str(e)}"
112
+
113
+
114
+ if __name__ == "__main__":
115
+ tool = ExecuteBashCommandTool()
116
+ print(tool.to_markdown())
@@ -0,0 +1,57 @@
1
+ """Tool for prompting the user with a question and capturing their input."""
2
+
3
+ from loguru import logger
4
+ from rich.prompt import Prompt
5
+
6
+ from quantalogic.tools.tool import Tool, ToolArgument
7
+
8
+
9
+ class InputQuestionTool(Tool):
10
+ """Tool to prompt the user with a question and capture their input."""
11
+
12
+ name: str = "input_question_tool"
13
+ description: str = "Prompts the user with a question and captures their input."
14
+ arguments: list = [
15
+ ToolArgument(
16
+ name="question",
17
+ arg_type="string",
18
+ description="The question to ask the user.",
19
+ required=True,
20
+ example="What is your favorite color?",
21
+ ),
22
+ ToolArgument(
23
+ name="default",
24
+ arg_type="string",
25
+ description="Optional default value if no input is provided.",
26
+ required=False,
27
+ example="blue",
28
+ ),
29
+ ]
30
+
31
+ def execute(self, question: str, default: str | None = None) -> str:
32
+ """Prompts the user with a question and captures their input.
33
+
34
+ Args:
35
+ question (str): The question to ask the user.
36
+ default (str | None, optional): Optional default value. Defaults to None.
37
+
38
+ Returns:
39
+ str: The user's input or the default value.
40
+ """
41
+ try:
42
+ # Use rich.prompt to create an interactive prompt
43
+ user_input = Prompt.ask(question, default=default)
44
+
45
+ # Log the input for debugging purposes
46
+ logger.debug(f"User input for question '{question}': {user_input}")
47
+
48
+ return user_input
49
+ except Exception as e:
50
+ # Log any errors that occur during input
51
+ logger.error(f"Error in input_question tool: {e}")
52
+ raise
53
+
54
+
55
+ if __name__ == "__main__":
56
+ tool = InputQuestionTool()
57
+ print(tool.to_markdown())
@@ -0,0 +1,21 @@
1
+ from .c_handler import CLanguageHandler
2
+ from .cpp_handler import CppLanguageHandler
3
+ from .go_handler import GoLanguageHandler
4
+ from .java_handler import JavaLanguageHandler
5
+ from .javascript_handler import JavaScriptLanguageHandler
6
+ from .python_handler import PythonLanguageHandler
7
+ from .rust_handler import RustLanguageHandler
8
+ from .scala_handler import ScalaLanguageHandler
9
+ from .typescript_handler import TypeScriptLanguageHandler
10
+
11
+ __all__ = [
12
+ "CLanguageHandler",
13
+ "CppLanguageHandler",
14
+ "GoLanguageHandler",
15
+ "JavaLanguageHandler",
16
+ "JavaScriptLanguageHandler",
17
+ "PythonLanguageHandler",
18
+ "RustLanguageHandler",
19
+ "ScalaLanguageHandler",
20
+ "TypeScriptLanguageHandler",
21
+ ]
@@ -0,0 +1,33 @@
1
+ import tree_sitter_c as tsc
2
+ from tree_sitter import Language
3
+
4
+
5
+ class CLanguageHandler:
6
+ """Handler for C-specific language processing."""
7
+
8
+ def get_language(self):
9
+ """Returns the Tree-sitter Language object for C."""
10
+ return Language(tsc.language())
11
+
12
+ def validate_root_node(self, root_node):
13
+ """Validates the root node for C syntax trees."""
14
+ return root_node.type == "translation_unit"
15
+
16
+ def process_node(
17
+ self, node, current_class, definitions, process_method, process_function, process_class, process_class_variable
18
+ ):
19
+ """Processes a node in a C syntax tree."""
20
+ if node.type == "function_definition":
21
+ if current_class:
22
+ process_method(node, definitions["classes"][current_class]["methods"])
23
+ else:
24
+ process_function(node, definitions["functions"])
25
+ return "function"
26
+ elif node.type == "struct_specifier":
27
+ class_name = process_class(node)
28
+ definitions["classes"][class_name] = {
29
+ "line": (node.start_point[0] + 1, node.end_point[0] + 1),
30
+ "methods": [],
31
+ "variables": [],
32
+ }
33
+ return "class"
@@ -0,0 +1,33 @@
1
+ import tree_sitter_cpp as tscpp
2
+ from tree_sitter import Language
3
+
4
+
5
+ class CppLanguageHandler:
6
+ """Handler for C++-specific language processing."""
7
+
8
+ def get_language(self):
9
+ """Returns the Tree-sitter Language object for C++."""
10
+ return Language(tscpp.language())
11
+
12
+ def validate_root_node(self, root_node):
13
+ """Validates the root node for C++ syntax trees."""
14
+ return root_node.type == "translation_unit"
15
+
16
+ def process_node(
17
+ self, node, current_class, definitions, process_method, process_function, process_class, process_class_variable
18
+ ):
19
+ """Processes a node in a C++ syntax tree."""
20
+ if node.type == "function_definition":
21
+ if current_class:
22
+ process_method(node, definitions["classes"][current_class]["methods"])
23
+ else:
24
+ process_function(node, definitions["functions"])
25
+ return "function"
26
+ elif node.type == "class_specifier":
27
+ class_name = process_class(node)
28
+ definitions["classes"][class_name] = {
29
+ "line": (node.start_point[0] + 1, node.end_point[0] + 1),
30
+ "methods": [],
31
+ "variables": [],
32
+ }
33
+ return "class"
@@ -0,0 +1,33 @@
1
+ import tree_sitter_go as tsgo
2
+ from tree_sitter import Language
3
+
4
+
5
+ class GoLanguageHandler:
6
+ """Handler for Go-specific language processing."""
7
+
8
+ def get_language(self):
9
+ """Returns the Tree-sitter Language object for Go."""
10
+ return Language(tsgo.language())
11
+
12
+ def validate_root_node(self, root_node):
13
+ """Validates the root node for Go syntax trees."""
14
+ return root_node.type == "source_file"
15
+
16
+ def process_node(
17
+ self, node, current_class, definitions, process_method, process_function, process_class, process_class_variable
18
+ ):
19
+ """Processes a node in a Go syntax tree."""
20
+ if node.type == "function_declaration":
21
+ if current_class:
22
+ process_method(node, definitions["classes"][current_class]["methods"])
23
+ else:
24
+ process_function(node, definitions["functions"])
25
+ return "function"
26
+ elif node.type == "type_declaration":
27
+ class_name = process_class(node)
28
+ definitions["classes"][class_name] = {
29
+ "line": (node.start_point[0] + 1, node.end_point[0] + 1),
30
+ "methods": [],
31
+ "variables": [],
32
+ }
33
+ return "class"
@@ -0,0 +1,37 @@
1
+ import tree_sitter_java as tsjava
2
+ from tree_sitter import Language
3
+
4
+
5
+ class JavaLanguageHandler:
6
+ """Handler for Java-specific language processing."""
7
+
8
+ def get_language(self):
9
+ """Returns the Tree-sitter Language object for Java."""
10
+ return Language(tsjava.language())
11
+
12
+ def validate_root_node(self, root_node):
13
+ """Validates the root node for Java syntax trees."""
14
+ return root_node.type == "program"
15
+
16
+ def process_node(
17
+ self, node, current_class, definitions, process_method, process_function, process_class, process_class_variable
18
+ ):
19
+ """Processes a node in a Java syntax tree."""
20
+ if node.type == "method_declaration":
21
+ if current_class:
22
+ process_method(node, definitions["classes"][current_class]["methods"])
23
+ else:
24
+ process_function(node, definitions["functions"])
25
+ return "method"
26
+ elif node.type == "class_declaration":
27
+ class_name = process_class(node)
28
+ definitions["classes"][class_name] = {
29
+ "line": (node.start_point[0] + 1, node.end_point[0] + 1),
30
+ "methods": [],
31
+ "variables": [],
32
+ }
33
+ return "class"
34
+ elif node.type == "field_declaration":
35
+ if current_class:
36
+ process_class_variable(node, definitions["classes"][current_class]["variables"])
37
+ return "variable"
@@ -0,0 +1,42 @@
1
+ import tree_sitter_javascript as tsjavascript
2
+ from tree_sitter import Language
3
+
4
+
5
+ class JavaScriptLanguageHandler:
6
+ """Handler for JavaScript-specific language processing."""
7
+
8
+ def get_language(self):
9
+ """Returns the Tree-sitter Language object for JavaScript."""
10
+ return Language(tsjavascript.language())
11
+
12
+ def validate_root_node(self, root_node):
13
+ """Validates the root node for JavaScript syntax trees."""
14
+ return root_node.type == "program"
15
+
16
+ def process_node(
17
+ self, node, current_class, definitions, process_method, process_function, process_class, process_class_variable
18
+ ):
19
+ """Processes a node in a JavaScript syntax tree."""
20
+ if node.type in ("function_declaration", "method_definition"):
21
+ if current_class:
22
+ process_method(node, definitions["classes"][current_class]["methods"])
23
+ else:
24
+ process_function(node, definitions["functions"])
25
+ return "function"
26
+ elif node.type == "class_declaration":
27
+ class_name = process_class(node)
28
+ definitions["classes"][class_name] = {
29
+ "line": (node.start_point[0] + 1, node.end_point[0] + 1),
30
+ "methods": [],
31
+ "variables": [],
32
+ }
33
+ return "class"
34
+ elif node.type == "method_definition":
35
+ if current_class:
36
+ process_method(node, definitions["classes"][current_class]["methods"])
37
+ return "method"
38
+ elif node.type == "field_definition":
39
+ if current_class:
40
+ process_class_variable(node, definitions["classes"][current_class]["variables"])
41
+ return "variable"
42
+ return None
@@ -0,0 +1,29 @@
1
+ import tree_sitter_python as tspython
2
+ from tree_sitter import Language
3
+
4
+
5
+ class PythonLanguageHandler:
6
+ """Handler for Python-specific language processing."""
7
+
8
+ def get_language(self):
9
+ """Returns the Tree-sitter Language object for Python."""
10
+ return Language(tspython.language())
11
+
12
+ def validate_root_node(self, root_node):
13
+ """Validates the root node for Python syntax trees."""
14
+ return root_node.type == "module"
15
+
16
+ def process_node(
17
+ self, node, current_class, definitions, process_method, process_function, process_class, process_class_variable
18
+ ):
19
+ """Processes a node in a Python syntax tree."""
20
+ if node.type in ("function_definition", "async_function_definition"):
21
+ if current_class:
22
+ process_method(node, definitions["classes"][current_class]["methods"])
23
+ else:
24
+ process_function(node, definitions["functions"])
25
+ return "function"
26
+ elif node.type == "class_definition":
27
+ class_name, start_line, end_line = process_class(node)
28
+ definitions["classes"][class_name] = {"line": (start_line, end_line), "methods": [], "variables": []}
29
+ return "class"
@@ -0,0 +1,33 @@
1
+ import tree_sitter_rust as tsrust
2
+ from tree_sitter import Language
3
+
4
+
5
+ class RustLanguageHandler:
6
+ """Handler for Rust-specific language processing."""
7
+
8
+ def get_language(self):
9
+ """Returns the Tree-sitter Language object for Rust."""
10
+ return Language(tsrust.language())
11
+
12
+ def validate_root_node(self, root_node):
13
+ """Validates the root node for Rust syntax trees."""
14
+ return root_node.type == "source_file"
15
+
16
+ def process_node(
17
+ self, node, current_class, definitions, process_method, process_function, process_class, process_class_variable
18
+ ):
19
+ """Processes a node in a Rust syntax tree."""
20
+ if node.type == "function_item":
21
+ if current_class:
22
+ process_method(node, definitions["classes"][current_class]["methods"])
23
+ else:
24
+ process_function(node, definitions["functions"])
25
+ return "function"
26
+ elif node.type == "struct_item":
27
+ class_name = process_class(node)
28
+ definitions["classes"][class_name] = {
29
+ "line": (node.start_point[0] + 1, node.end_point[0] + 1),
30
+ "methods": [],
31
+ "variables": [],
32
+ }
33
+ return "class"