marluwe commited on
Commit
520bbd9
·
verified ·
1 Parent(s): e6d77be

Update tools/JavaScriptToPythonConverter.py

Browse files
Files changed (1) hide show
  1. tools/JavaScriptToPythonConverter.py +165 -183
tools/JavaScriptToPythonConverter.py CHANGED
@@ -1,207 +1,189 @@
1
- import esprima # For parsing JavaScript
2
- import ast # For Python AST manipulation
3
- from typing import Union, Dict, List, Any
 
 
 
4
 
5
- class JavaScriptToPythonConverter:
 
 
 
 
 
6
  def __init__(self):
7
- self.variable_map: Dict[str, str] = {}
8
 
9
- def convert_js_to_python(self, js_code: str) -> str:
10
  """
11
- Convert JavaScript code to Python code.
12
 
13
  Args:
14
- js_code (str): JavaScript code to convert
15
-
 
 
16
  Returns:
17
- str: Converted Python code
18
  """
19
  try:
20
- # Parse JavaScript code into AST
21
- js_ast = esprima.parseScript(js_code)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
- # Convert the AST to Python code
24
- python_code = self._convert_node(js_ast)
25
 
26
- return python_code
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  except Exception as e:
29
- raise ValueError(f"Failed to convert JavaScript code: {str(e)}")
30
 
31
- def _convert_node(self, node: Any) -> str:
32
- """Convert a JavaScript AST node to Python code."""
33
- node_type = node.type if hasattr(node, 'type') else type(node).__name__
34
-
35
- # Map of JavaScript node types to conversion methods
36
- converters = {
37
- 'Program': self._convert_program,
38
- 'VariableDeclaration': self._convert_variable_declaration,
39
- 'FunctionDeclaration': self._convert_function_declaration,
40
- 'ExpressionStatement': self._convert_expression_statement,
41
- 'BinaryExpression': self._convert_binary_expression,
42
- 'Identifier': self._convert_identifier,
43
- 'Literal': self._convert_literal,
44
- 'CallExpression': self._convert_call_expression,
45
- 'ReturnStatement': self._convert_return_statement,
46
- 'IfStatement': self._convert_if_statement,
47
- 'ForStatement': self._convert_for_statement,
48
- 'WhileStatement': self._convert_while_statement
49
- }
50
-
51
- converter = converters.get(node_type)
52
- if converter:
53
- return converter(node)
54
- else:
55
- raise ValueError(f"Unsupported node type: {node_type}")
56
-
57
- def _convert_program(self, node: Any) -> str:
58
- """Convert a JavaScript program node to Python code."""
59
- return '\n'.join(self._convert_node(stmt) for stmt in node.body)
60
-
61
- def _convert_variable_declaration(self, node: Any) -> str:
62
- """Convert JavaScript variable declarations to Python assignments."""
63
- declarations = []
64
- for decl in node.declarations:
65
- var_name = decl.id.name
66
- self.variable_map[var_name] = var_name
67
-
68
- if decl.init:
69
- init = self._convert_node(decl.init)
70
- declarations.append(f"{var_name} = {init}")
71
- else:
72
- declarations.append(f"{var_name} = None")
73
 
74
- return '\n'.join(declarations)
 
 
 
 
 
 
75
 
76
- def _convert_function_declaration(self, node: Any) -> str:
77
- """Convert JavaScript function declarations to Python functions."""
78
- func_name = node.id.name
79
- params = [param.name for param in node.params]
80
- body = self._convert_node(node.body)
81
 
82
- return f"def {func_name}({', '.join(params)}):\n{self._indent(body)}"
83
-
84
- def _convert_expression_statement(self, node: Any) -> str:
85
- """Convert JavaScript expression statements to Python."""
86
- return self._convert_node(node.expression)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
- def _convert_binary_expression(self, node: Any) -> str:
89
- """Convert JavaScript binary expressions to Python."""
90
- operator_map = {
91
- '===': '==',
92
- '!==': '!=',
93
- '&&': 'and',
94
- '||': 'or'
95
- }
96
 
97
- left = self._convert_node(node.left)
98
- right = self._convert_node(node.right)
99
- op = operator_map.get(node.operator, node.operator)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
- return f"{left} {op} {right}"
102
-
103
- def _convert_identifier(self, node: Any) -> str:
104
- """Convert JavaScript identifiers to Python."""
105
- name = node.name
106
- # Map JavaScript built-ins to Python equivalents
107
- js_to_python_builtins = {
108
- 'console.log': 'print',
109
- 'undefined': 'None',
110
- 'null': 'None',
111
- 'true': 'True',
112
- 'false': 'False'
113
- }
114
- return js_to_python_builtins.get(name, name)
115
-
116
- def _convert_literal(self, node: Any) -> str:
117
- """Convert JavaScript literals to Python literals."""
118
- if node.value is None:
119
- return 'None'
120
- elif isinstance(node.value, (int, float)):
121
- return str(node.value)
122
- elif isinstance(node.value, str):
123
- return repr(node.value)
124
- elif isinstance(node.value, bool):
125
  return str(node.value)
126
- else:
127
- raise ValueError(f"Unsupported literal type: {type(node.value)}")
128
-
129
- def _convert_call_expression(self, node: Any) -> str:
130
- """Convert JavaScript function calls to Python."""
131
- callee = self._convert_node(node.callee)
132
- args = [self._convert_node(arg) for arg in node.arguments]
133
-
134
- # Handle special cases
135
- if callee == 'console.log':
136
- callee = 'print'
137
-
138
- return f"{callee}({', '.join(args)})"
139
-
140
- def _convert_return_statement(self, node: Any) -> str:
141
- """Convert JavaScript return statements to Python."""
142
- if node.argument:
143
- return f"return {self._convert_node(node.argument)}"
144
- return "return"
145
-
146
- def _convert_if_statement(self, node: Any) -> str:
147
- """Convert JavaScript if statements to Python."""
148
- test = self._convert_node(node.test)
149
- consequent = self._convert_node(node.consequent)
150
-
151
- if_stmt = f"if {test}:\n{self._indent(consequent)}"
152
-
153
- if node.alternate:
154
- alternate = self._convert_node(node.alternate)
155
- if_stmt += f"\nelse:\n{self._indent(alternate)}"
156
-
157
- return if_stmt
158
-
159
- def _convert_for_statement(self, node: Any) -> str:
160
- """Convert JavaScript for loops to Python."""
161
- init = self._convert_node(node.init) if node.init else ""
162
- test = self._convert_node(node.test) if node.test else "True"
163
- update = self._convert_node(node.update) if node.update else ""
164
- body = self._convert_node(node.body)
165
 
166
- # Convert C-style for loop to while loop
167
- if init:
168
- return f"{init}\nwhile {test}:\n{self._indent(body)}\n{self._indent(update)}"
169
- else:
170
- return f"while {test}:\n{self._indent(body)}"
171
-
172
- def _convert_while_statement(self, node: Any) -> str:
173
- """Convert JavaScript while loops to Python."""
174
- test = self._convert_node(node.test)
175
- body = self._convert_node(node.body)
176
-
177
- return f"while {test}:\n{self._indent(body)}"
178
-
179
- def _indent(self, code: str, level: int = 1) -> str:
180
- """Indent Python code by specified number of levels."""
181
- lines = code.split('\n')
182
- return '\n'.join(' ' * level + line for line in lines)
183
 
 
 
184
 
185
- # Example usage
186
- if __name__ == "__main__":
187
- converter = JavaScriptToPythonConverter()
188
-
189
- # Example JavaScript code
190
- js_code = """
191
- function fibonacci(n) {
192
- if (n <= 1) {
193
- return n;
194
- }
195
- return fibonacci(n - 1) + fibonacci(n - 2);
196
- }
197
-
198
- var result = fibonacci(10);
199
- console.log("Fibonacci result:", result);
200
- """
201
-
202
- try:
203
- python_code = converter.convert_js_to_python(js_code)
204
- print("Converted Python code:")
205
- print(python_code)
206
- except Exception as e:
207
- print(f"Conversion error: {str(e)}")
 
1
+ import esprima
2
+ from typing import Any, Callable, Dict, Optional
3
+ import types
4
+ import ast
5
+ import inspect
6
+ from smolagents.tools import Tool
7
 
8
+ class JavaScriptMethodDispatcher(Tool):
9
+ name = "javascript_as_python_caller"
10
+ description = "turns java script code blocks into callable python functions"
11
+ inputs = {'jsContent':{'type':'string', 'description':'content of a regular java script code block of a function definition'}}
12
+ output_type = "callable"
13
+
14
  def __init__(self):
15
+ self._method_cache: Dict[str, Callable] = {}
16
 
17
+ def register_js_method(self, js_code: str, method_name: Optional[str] = None) -> Callable:
18
  """
19
+ Convert JavaScript code to a Python callable and register it in the dispatcher.
20
 
21
  Args:
22
+ js_code (str): JavaScript code containing the function
23
+ method_name (str, optional): Name to register the method under.
24
+ If None, tries to extract from the JS code.
25
+
26
  Returns:
27
+ Callable: Python function that can be called directly
28
  """
29
  try:
30
+ # Parse JavaScript code
31
+ parsed = esprima.parseScript(js_code)
32
+
33
+ # Find the function declaration
34
+ func_decl = None
35
+ for node in parsed.body:
36
+ if node.type == 'FunctionDeclaration':
37
+ func_decl = node
38
+ break
39
+
40
+ if not func_decl:
41
+ raise ValueError("No function declaration found in JavaScript code")
42
+
43
+ # Get the method name
44
+ if method_name is None:
45
+ method_name = func_decl.id.name
46
+
47
+ # Convert JavaScript parameters to Python
48
+ params = [param.name for param in func_decl.params]
49
 
50
+ # Convert the function body
51
+ body_lines = self._convert_js_body(func_decl.body)
52
 
53
+ # Create the Python function source
54
+ py_source = f"def {method_name}({', '.join(params)}):\n"
55
+ py_source += "\n".join(f" {line}" for line in body_lines)
56
+
57
+ # Create function namespace
58
+ namespace = {
59
+ 'print': print, # Common built-ins
60
+ 'str': str,
61
+ 'int': int,
62
+ 'float': float,
63
+ 'bool': bool,
64
+ 'list': list,
65
+ 'dict': dict,
66
+ 'None': None,
67
+ 'True': True,
68
+ 'False': False
69
+ }
70
+
71
+ # Compile and execute the Python code
72
+ compiled_code = compile(py_source, '<string>', 'exec')
73
+ exec(compiled_code, namespace)
74
+
75
+ # Get the compiled function
76
+ py_func = namespace[method_name]
77
+
78
+ # Store in cache
79
+ self._method_cache[method_name] = py_func
80
+
81
+ return py_func
82
 
83
  except Exception as e:
84
+ raise ValueError(f"Failed to convert JavaScript method: {str(e)}")
85
 
86
+ def get_method(self, method_name: str) -> Optional[Callable]:
87
+ """
88
+ Get a registered method by name.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
+ Args:
91
+ method_name (str): Name of the registered method
92
+
93
+ Returns:
94
+ Optional[Callable]: The registered Python function, or None if not found
95
+ """
96
+ return self._method_cache.get(method_name)
97
 
98
+ def call_method(self, method_name: str, *args, **kwargs) -> Any:
99
+ """
100
+ Call a registered method by name with given arguments.
 
 
101
 
102
+ Args:
103
+ method_name (str): Name of the registered method
104
+ *args: Positional arguments to pass to the method
105
+ **kwargs: Keyword arguments to pass to the method
106
+
107
+ Returns:
108
+ Any: Result of the method call
109
+
110
+ Raises:
111
+ ValueError: If method is not found or call fails
112
+ """
113
+ method = self.get_method(method_name)
114
+ if method is None:
115
+ raise ValueError(f"Method '{method_name}' not found")
116
+
117
+ try:
118
+ return method(*args, **kwargs)
119
+ except Exception as e:
120
+ raise ValueError(f"Failed to call method '{method_name}': {str(e)}")
121
 
122
+ def _convert_js_body(self, body_node: Any) -> list[str]:
123
+ """Convert JavaScript function body to Python code lines."""
124
+ # This is a simplified conversion - you'd want to expand this
125
+ # based on your specific needs
126
+ lines = []
 
 
 
127
 
128
+ # Handle different types of statements
129
+ if body_node.type == 'BlockStatement':
130
+ for statement in body_node.body:
131
+ if statement.type == 'ReturnStatement':
132
+ return_value = self._convert_js_expression(statement.argument)
133
+ lines.append(f"return {return_value}")
134
+ elif statement.type == 'ExpressionStatement':
135
+ expr = self._convert_js_expression(statement.expression)
136
+ lines.append(expr)
137
+ elif statement.type == 'IfStatement':
138
+ condition = self._convert_js_expression(statement.test)
139
+ lines.append(f"if {condition}:")
140
+ then_lines = self._convert_js_body(statement.consequent)
141
+ lines.extend(f" {line}" for line in then_lines)
142
+ if statement.alternate:
143
+ lines.append("else:")
144
+ else_lines = self._convert_js_body(statement.alternate)
145
+ lines.extend(f" {line}" for line in else_lines)
146
 
147
+ return lines
148
+
149
+ def _convert_js_expression(self, node: Any) -> str:
150
+ """Convert JavaScript expression to Python code string."""
151
+ if node.type == 'BinaryExpression':
152
+ left = self._convert_js_expression(node.left)
153
+ right = self._convert_js_expression(node.right)
154
+ op = node.operator
155
+ # Convert JavaScript operators to Python
156
+ op_map = {
157
+ '===': '==',
158
+ '!==': '!=',
159
+ '&&': 'and',
160
+ '||': 'or'
161
+ }
162
+ op = op_map.get(op, op)
163
+ return f"({left} {op} {right})"
164
+ elif node.type == 'Literal':
165
+ if isinstance(node.value, str):
166
+ return f"'{node.value}'"
 
 
 
 
167
  return str(node.value)
168
+ elif node.type == 'Identifier':
169
+ # Handle JavaScript built-ins
170
+ js_to_py = {
171
+ 'undefined': 'None',
172
+ 'null': 'None',
173
+ 'true': 'True',
174
+ 'false': 'False'
175
+ }
176
+ return js_to_py.get(node.name, node.name)
177
+ elif node.type == 'CallExpression':
178
+ func = self._convert_js_expression(node.callee)
179
+ args = [self._convert_js_expression(arg) for arg in node.arguments]
180
+ # Handle special cases
181
+ if func == 'console.log':
182
+ func = 'print'
183
+ return f"{func}({', '.join(args)})"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
+ raise ValueError(f"Unsupported expression type: {node.type}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
 
187
+ def forward(self, jsContent: str) -> callable:
188
+ return self.register_js_method(jsContent)
189