mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-08-14 04:40:36 +03:00
137 lines
5.0 KiB
Python
137 lines
5.0 KiB
Python
''' Models: RSForm semantic information. '''
|
|
from typing import cast
|
|
|
|
from .api_RSLanguage import (
|
|
infer_template,
|
|
is_base_set,
|
|
is_functional,
|
|
is_simple_expression,
|
|
split_template
|
|
)
|
|
from .Constituenta import Constituenta, CstType, extract_globals
|
|
from .RSForm import RSForm
|
|
from .RSFormCached import RSFormCached
|
|
|
|
|
|
class SemanticInfo:
|
|
''' Semantic information derived from constituents. '''
|
|
|
|
def __init__(self, schema: RSFormCached):
|
|
schema.cache.ensure_loaded()
|
|
self._items = schema.cache.constituents
|
|
self._cst_by_ID = schema.cache.by_id
|
|
self._cst_by_alias = schema.cache.by_alias
|
|
self.graph = RSForm.graph_formal(schema.cache.constituents, schema.cache.by_alias)
|
|
self.info = {
|
|
cst.pk: {
|
|
'is_simple': False,
|
|
'is_template': False,
|
|
'parent': cst.pk,
|
|
'children': []
|
|
}
|
|
for cst in schema.cache.constituents
|
|
}
|
|
self._calculate_attributes()
|
|
|
|
def __getitem__(self, key: int) -> dict:
|
|
return self.info[key]
|
|
|
|
def is_simple_expression(self, target: int) -> bool:
|
|
''' Access "is_simple" attribute. '''
|
|
return cast(bool, self.info[target]['is_simple'])
|
|
|
|
def is_template(self, target: int) -> bool:
|
|
''' Access "is_template" attribute. '''
|
|
return cast(bool, self.info[target]['is_template'])
|
|
|
|
def parent(self, target: int) -> int:
|
|
''' Access "parent" attribute. '''
|
|
return cast(int, self.info[target]['parent'])
|
|
|
|
def children(self, target: int) -> list[int]:
|
|
''' Access "children" attribute. '''
|
|
return cast(list[int], self.info[target]['children'])
|
|
|
|
def _calculate_attributes(self) -> None:
|
|
for cst_id in self.graph.topological_order():
|
|
cst = self._cst_by_ID[cst_id]
|
|
self.info[cst_id]['is_template'] = infer_template(cst.definition_formal)
|
|
self.info[cst_id]['is_simple'] = self._infer_simple_expression(cst)
|
|
if not self.info[cst_id]['is_simple'] or cst.cst_type == CstType.STRUCTURED:
|
|
continue
|
|
parent = self._infer_parent(cst)
|
|
self.info[cst_id]['parent'] = parent
|
|
if parent != cst_id:
|
|
cast(list[int], self.info[parent]['children']).append(cst_id)
|
|
|
|
def _infer_simple_expression(self, target: Constituenta) -> bool:
|
|
if target.cst_type == CstType.STRUCTURED or is_base_set(target.cst_type):
|
|
return False
|
|
|
|
dependencies = self.graph.inputs[target.pk]
|
|
has_complex_dependency = any(
|
|
self.is_template(cst_id) and
|
|
not self.is_simple_expression(cst_id) for cst_id in dependencies
|
|
)
|
|
if has_complex_dependency:
|
|
return False
|
|
|
|
if is_functional(target.cst_type):
|
|
return is_simple_expression(split_template(target.definition_formal)['body'])
|
|
else:
|
|
return is_simple_expression(target.definition_formal)
|
|
|
|
def _infer_parent(self, target: Constituenta) -> int:
|
|
sources = self._extract_sources(target)
|
|
if len(sources) != 1:
|
|
return target.pk
|
|
|
|
parent_id = next(iter(sources))
|
|
parent = self._cst_by_ID[parent_id]
|
|
if is_base_set(parent.cst_type):
|
|
return target.pk
|
|
return parent_id
|
|
|
|
def _extract_sources(self, target: Constituenta) -> set[int]:
|
|
sources: set[int] = set()
|
|
if not is_functional(target.cst_type):
|
|
for parent_id in self.graph.inputs[target.pk]:
|
|
parent_info = self[parent_id]
|
|
if not parent_info['is_template'] or not parent_info['is_simple']:
|
|
sources.add(parent_info['parent'])
|
|
return sources
|
|
|
|
expression = split_template(target.definition_formal)
|
|
body_dependencies = extract_globals(expression['body'])
|
|
for alias in body_dependencies:
|
|
parent = self._cst_by_alias.get(alias)
|
|
if not parent:
|
|
continue
|
|
|
|
parent_info = self[parent.pk]
|
|
if not parent_info['is_template'] or not parent_info['is_simple']:
|
|
sources.add(parent_info['parent'])
|
|
|
|
if self._need_check_head(sources, expression['head']):
|
|
head_dependencies = extract_globals(expression['head'])
|
|
for alias in head_dependencies:
|
|
parent = self._cst_by_alias.get(alias)
|
|
if not parent:
|
|
continue
|
|
|
|
parent_info = self[parent.pk]
|
|
if not is_base_set(parent.cst_type) and \
|
|
(not parent_info['is_template'] or not parent_info['is_simple']):
|
|
sources.add(parent_info['parent'])
|
|
return sources
|
|
|
|
def _need_check_head(self, sources: set[int], head: str) -> bool:
|
|
if not sources:
|
|
return True
|
|
elif len(sources) != 1:
|
|
return False
|
|
else:
|
|
base = self._cst_by_ID[next(iter(sources))]
|
|
return not is_functional(base.cst_type) or \
|
|
split_template(base.definition_formal)['head'] != head
|