115 lines
4.5 KiB
Python
115 lines
4.5 KiB
Python
from collections import namedtuple
|
|
|
|
import parso
|
|
from core.builtin_concepts import BuiltinConcepts
|
|
from core.sheerka.ExecutionContext import ExecutionContext
|
|
from core.sheerka.services.SheerkaFunctionsParametersHistory import SheerkaFunctionsParametersHistory
|
|
from evaluators.BaseEvaluator import OneReturnValueEvaluator
|
|
from evaluators.PythonEvaluator import PythonEvaluator
|
|
from parso.python.tree import Name, PythonNode, Operator
|
|
|
|
func_found = namedtuple("func_found", "name params")
|
|
|
|
|
|
class UpdateFunctionsParametersEvaluator(OneReturnValueEvaluator):
|
|
"""
|
|
This evaluator scans all successful PythonEvaluator results
|
|
It then records the parameters of every functions found.
|
|
For example, if the PythonEvaluator successfully evaluated
|
|
foo(1, 'string') + bar(3.14)
|
|
the parameters 1 and 'string' will be recorded for the function 'foo'
|
|
and the parameter 3.14 will be recorded for the function 'bar'
|
|
|
|
These records will later be used as input for auto-completion
|
|
"""
|
|
NAME = "UpdateFunctionsParameters"
|
|
|
|
def __init__(self):
|
|
super().__init__(self.NAME, [BuiltinConcepts.AFTER_EVALUATION], 79)
|
|
self.enabled = False
|
|
|
|
def reset(self):
|
|
self.enabled = False
|
|
|
|
def matches(self, context, return_value):
|
|
"""
|
|
True if the return value is the successful result of PythonEvaluator
|
|
:param context:
|
|
:param return_value:
|
|
:return:
|
|
"""
|
|
return return_value.status and return_value.who == context.sheerka.get_evaluator_name(PythonEvaluator.NAME)
|
|
|
|
def eval(self, context: ExecutionContext, return_value):
|
|
sheerka = context.sheerka
|
|
params_record_service = sheerka.services[SheerkaFunctionsParametersHistory.NAME]
|
|
|
|
if not return_value.parents:
|
|
return self.add_not_found_error(sheerka, return_value)
|
|
|
|
for parent in return_value.parents:
|
|
if parent.who == sheerka.get_parser_name("Python") and \
|
|
parent.status and \
|
|
sheerka.isinstance(parent.body, BuiltinConcepts.PARSER_RESULT):
|
|
break
|
|
else:
|
|
return self.add_not_found_error(sheerka, return_value)
|
|
|
|
source = parent.body.source
|
|
parsed = parso.parse(source)
|
|
self.process_functions(context, params_record_service, parsed)
|
|
return return_value
|
|
|
|
def process_functions(self, context, service, node):
|
|
if not hasattr(node, "children"):
|
|
return
|
|
|
|
if (func := self.get_function(node)) is not None:
|
|
for i, p in enumerate(func.params):
|
|
service.record_function_parameter(context, func.name, i, p)
|
|
|
|
function_params = node.children[1].children[1]
|
|
if hasattr(function_params, "children"):
|
|
for child in function_params.children: # function parameters
|
|
self.process_functions(context, service, child)
|
|
else:
|
|
for child in node.children:
|
|
self.process_functions(context, service, child)
|
|
|
|
def add_not_found_error(self, sheerka, return_value):
|
|
error = sheerka.ret(self.name,
|
|
False,
|
|
sheerka.new(BuiltinConcepts.NOT_FOUND, body="source code"),
|
|
parents=[return_value])
|
|
if return_value.parents is None:
|
|
return_value.parents = [error]
|
|
else:
|
|
return_value.parents.append(error)
|
|
|
|
return return_value
|
|
|
|
@staticmethod
|
|
def get_function(node):
|
|
if len(node.children) == 2 and \
|
|
isinstance(node.children[0], Name) and \
|
|
isinstance(node.children[1], PythonNode) and \
|
|
node.children[1].type == "trailer" and \
|
|
len(node.children[1].children) >= 2 and \
|
|
isinstance(node.children[1].children[0], Operator) and \
|
|
node.children[1].children[0].value == "(" and \
|
|
isinstance(node.children[1].children[-1], Operator) and \
|
|
node.children[1].children[-1].value == ")":
|
|
name = node.children[0].value
|
|
if len(node.children[1].children) == 2:
|
|
params = []
|
|
else:
|
|
params_nodes = node.children[1].children[1]
|
|
if hasattr(params_nodes, "children"):
|
|
params = [p.get_code().strip() for p in params_nodes.children if not isinstance(p, Operator)]
|
|
|
|
else:
|
|
params = [params_nodes.get_code().strip()]
|
|
return func_found(name=name, params=params)
|
|
else:
|
|
return None
|