First implementation of Debugger for SyaNodeParser

This commit is contained in:
2020-12-03 21:50:48 +01:00
parent 4f899280c4
commit 8b86998225
48 changed files with 1781 additions and 1795 deletions
+5 -24
View File
@@ -8,39 +8,20 @@ set_isa(c:explain last:, __AUTO_EVAL)
def concept explain x as get_results(id=x, depth=3) def concept explain x as get_results(id=x, depth=3)
set_isa(c:explain x:, __AUTO_EVAL) set_isa(c:explain x:, __AUTO_EVAL)
def concept precedence a > precedence b as set_is_greater_than(__PRECEDENCE, a, b) def concept precedence a > precedence b as set_is_greater_than(__PRECEDENCE, a, b, 'Sya')
set_isa(c:precedence a > precedence b:, __AUTO_EVAL) set_isa(c:precedence a > precedence b:, __AUTO_EVAL)
def concept x is a command as set_auto_eval(x, __AUTO_EVAL) def concept x is a command as set_auto_eval(x, __AUTO_EVAL)
set_auto_eval(c:x is a command:) set_auto_eval(c:x is a command:)
def concept q from q ? as question(q) pre is_question()
set_is_lesser(__PRECEDENCE, q)
set_auto_eval(c:q:)
def concept "x is a concept" as isinstance(x, Concept) pre is_question()
def concept x is a y as set_isa(x, y)
set_auto_eval(c:x is a y:)
def concept x is an y as set_isa(x, y)
set_auto_eval(c:x is an y:)
def concept x is a y as isa(x,y) pre is_question()
# no need to auto eval as it's a question
def concept x is an y as isa(x,y) pre is_question()
# no need to auto eval as it's a question
def concept x has a y as set_hasa(x, y)
set_auto_eval(c:x has a y:)
def concept x has an y as set_hasa(x, y)
set_auto_eval(c:x has an y:)
def concept x has a y as hasa(x,y) pre is_question()
# no need to auto eval as it's a question
def concept x has an y as hasa(x,y) pre is_question()
# no need to auto eval as it's a question
def concept activate debug as set_debug(True) def concept activate debug as set_debug(True)
set_auto_eval(c:activate debug:) set_auto_eval(c:activate debug:)
def concept deactivate debug as set_debug(False) def concept deactivate debug as set_debug(False)
set_auto_eval(c:deactivate debug:) set_auto_eval(c:deactivate debug:)
def concept debug on as set_debug(True)
set_auto_eval(c:debug on:)
def concept debug off as set_debug(False)
set_auto_eval(c:debug off:)
def concept activate debug on x as debug_var(x) def concept activate debug on x as debug_var(x)
set_auto_eval(c:activate debug on x:) set_auto_eval(c:activate debug on x:)
+27
View File
@@ -0,0 +1,27 @@
# question
def concept q from q ? as question(q) pre is_question()
set_is_lesser(__PRECEDENCE, q, 'Sya')
set_auto_eval(c:q:)
def concept "x is a concept" as isinstance(x, Concept) pre is_question()
# is a
def concept x is a y as set_isa(x, y)
set_auto_eval(c:x is a y:)
def concept x is an y as set_isa(x, y)
set_auto_eval(c:x is an y:)
def concept x is a y as isa(x,y) pre is_question()
# no need to auto eval as it's a question
def concept x is an y as isa(x,y) pre is_question()
# no need to auto eval as it's a question
# has a
def concept x has a y as set_hasa(x, y)
set_auto_eval(c:x has a y:)
def concept x has an y as set_hasa(x, y)
set_auto_eval(c:x has an y:)
def concept x has a y as hasa(x,y) pre is_question()
# no need to auto eval as it's a question
def concept x has an y as hasa(x,y) pre is_question()
# no need to auto eval as it's a question
+4 -95
View File
@@ -1,98 +1,7 @@
#import admin #import admin
#import default
# define numbers #import numbers
def concept one as 1
def concept two as 2
def concept three as 3
def concept four as 4
def concept five as 5
def concept six as 6
def concept seven as 7
def concept eight as 8
def concept nine as 9
def concept ten as 10
def concept eleven as 11
def concept twelve as 12
def concept thirteen as 13
def concept fourteen as 14
def concept fifteen as 15
def concept sixteen as 16
def concept seventeen as 17
def concept eighteen as 18
def concept nineteen as 19
def concept twenty as 20
def concept number
set_isa(one, number)
set_isa(two, number)
set_isa(three, number)
set_isa(four, number)
set_isa(five, number)
set_isa(six, number)
set_isa(seven, number)
set_isa(eight, number)
set_isa(nine, number)
set_isa(ten, number)
set_isa(eleven, number)
set_isa(twelve, number)
set_isa(thirteen, number)
set_isa(fourteen, number)
set_isa(fifteen, number)
set_isa(sixteen, number)
set_isa(seventeen, number)
set_isa(eighteen, number)
set_isa(nineteen, number)
set_isa(twenty, number)
def concept twenties from bnf twenty number where number < 10 as twenty + number
set_isa(twenties, number)
def concept thirty as 30
set_isa(thirty, number)
def concept thirties from bnf thirty number where number < 10 as thirty + number
set_isa(thirties, number)
def concept forty as 40
set_isa(forty, number)
def concept forties from bnf forty number where number < 10 as forty + number
set_isa(forties, number)
def concept fifty as 50
set_isa(fifty, number)
def concept fifties from bnf fifty number where number < 10 as fifty + number
set_isa(fifties, number)
def concept sixty as 60
set_isa(sixty, number)
def concept sixties from bnf sixty number where number < 10 as sixty + number
set_isa(sixties, number)
def concept seventy as 70
set_isa(seventy, number)
def concept seventies from bnf seventy number where number < 10 as seventy + number
set_isa(seventies, number)
def concept eighty as 80
set_isa(eighty, number)
def concept eighties from bnf eighty number where number < 10 as eighty + number
set_isa(eighties, number)
def concept ninety as 90
set_isa(ninety, number)
def concept nineties from bnf ninety number where number < 10 as ninety + number
set_isa(nineties, number)
def concept one hundred as 100
set_isa(one hundred, number)
def concept hundreds from bnf number=n1 'hundred' 'and' number=n2 where n1 < 10 and n2 < 100 as n1 * 100 + n2
set_isa(hundreds, number)
def concept hundreds from bnf number 'hundred' where number < 10 as number * 100
set_isa(last_created_concept(), number)
def concept thousands from bnf number 'thousand' where number < 1000 as number * 1000
set_isa(thousands, number)
def concept thousands from bnf number=n1 'thousand' 'and' number=n2 as n1 * 1000 + n2 where n1 < 1000 and n2 < 1000
set_isa(last_created_concept(), number)
# define basic operations on numbers
def concept plus from a plus b as a + b
def concept minus from a minus b as a - b
def concept multiplied from a multiplied by b as a * b
def concept divided from a divided by b as a * b
set_is_greater_than(__PRECEDENCE, multiplied, plus)
set_is_greater_than(__PRECEDENCE, divided, plus)
set_is_greater_than(__PRECEDENCE, multiplied, minus)
set_is_greater_than(__PRECEDENCE, divided, minus)
activate return values processing
+3 -1
View File
@@ -1,4 +1,6 @@
#import admin #import admin
#import default
def concept one as 1 def concept one as 1
def concept two as 2 def concept two as 2
def concept number def concept number
@@ -6,5 +8,5 @@ def concept apple
def concept table def concept table
def concept location def concept location
def concept x is on y as set_attr(x, location, y) def concept x is on y as set_attr(x, location, y)
activate return values processing
+92
View File
@@ -0,0 +1,92 @@
# define numbers
def concept one as 1
def concept two as 2
def concept three as 3
def concept four as 4
def concept five as 5
def concept six as 6
def concept seven as 7
def concept eight as 8
def concept nine as 9
def concept ten as 10
def concept eleven as 11
def concept twelve as 12
def concept thirteen as 13
def concept fourteen as 14
def concept fifteen as 15
def concept sixteen as 16
def concept seventeen as 17
def concept eighteen as 18
def concept nineteen as 19
def concept twenty as 20
def concept number
set_isa(one, number)
set_isa(two, number)
set_isa(three, number)
set_isa(four, number)
set_isa(five, number)
set_isa(six, number)
set_isa(seven, number)
set_isa(eight, number)
set_isa(nine, number)
set_isa(ten, number)
set_isa(eleven, number)
set_isa(twelve, number)
set_isa(thirteen, number)
set_isa(fourteen, number)
set_isa(fifteen, number)
set_isa(sixteen, number)
set_isa(seventeen, number)
set_isa(eighteen, number)
set_isa(nineteen, number)
set_isa(twenty, number)
def concept twenties from bnf twenty number where number < 10 as twenty + number
set_isa(twenties, number)
def concept thirty as 30
set_isa(thirty, number)
def concept thirties from bnf thirty number where number < 10 as thirty + number
set_isa(thirties, number)
def concept forty as 40
set_isa(forty, number)
def concept forties from bnf forty number where number < 10 as forty + number
set_isa(forties, number)
def concept fifty as 50
set_isa(fifty, number)
def concept fifties from bnf fifty number where number < 10 as fifty + number
set_isa(fifties, number)
def concept sixty as 60
set_isa(sixty, number)
def concept sixties from bnf sixty number where number < 10 as sixty + number
set_isa(sixties, number)
def concept seventy as 70
set_isa(seventy, number)
def concept seventies from bnf seventy number where number < 10 as seventy + number
set_isa(seventies, number)
def concept eighty as 80
set_isa(eighty, number)
def concept eighties from bnf eighty number where number < 10 as eighty + number
set_isa(eighties, number)
def concept ninety as 90
set_isa(ninety, number)
def concept nineties from bnf ninety number where number < 10 as ninety + number
set_isa(nineties, number)
def concept one hundred as 100
set_isa(one hundred, number)
def concept hundreds from bnf number=n1 'hundred' 'and' number=n2 where n1 < 10 and n2 < 100 as n1 * 100 + n2
set_isa(hundreds, number)
def concept hundreds from bnf number 'hundred' where number < 10 as number * 100
set_isa(last_created_concept(), number)
def concept thousands from bnf number 'thousand' where number < 1000 as number * 1000
set_isa(thousands, number)
def concept thousands from bnf number=n1 'thousand' 'and' number=n2 as n1 * 1000 + n2 where n1 < 1000 and n2 < 1000
set_isa(last_created_concept(), number)
# define basic operations on numbers
def concept plus from a plus b as a + b
def concept minus from a minus b as a - b
def concept multiplied from a multiplied by b as a * b
def concept divided from a divided by b as a * b
set_is_greater_than(__PRECEDENCE, multiplied, plus)
set_is_greater_than(__PRECEDENCE, divided, plus)
set_is_greater_than(__PRECEDENCE, multiplied, minus)
set_is_greater_than(__PRECEDENCE, divided, minus)
+9
View File
@@ -0,0 +1,9 @@
#import full
def concept size
def concept little
def concept girl
def concept little x as set_attr(x, size, little) ret x
def concept the x ret memory(x)
def concept how is x as get_attr(x, size)
+21
View File
@@ -565,3 +565,24 @@ class ToListConcept(Concept):
self.set_value("recurse_on", recurse_on) # which sub items should we display self.set_value("recurse_on", recurse_on) # which sub items should we display
self.set_value("tab", tab) # customise tab (content and length) self.set_value("tab", tab) # customise tab (content and length)
self._metadata.is_evaluated = True self._metadata.is_evaluated = True
class NewConceptConcept(Concept):
ALL_ATTRIBUTES = ["concept"]
def __init__(self, concept=None):
Concept.__init__(self,
BuiltinConcepts.NEW_CONCEPT,
True,
False,
BuiltinConcepts.NEW_CONCEPT,
bound_body="concept")
self.set_value("concept", concept)
self._metadata.is_evaluated = True
def __repr__(self):
if self.concept:
return f"NewConcept(concept={self.concept}, key='{self.concept.key}')"
else:
return super().__repr__()
+22 -1
View File
@@ -393,7 +393,7 @@ class Concept:
for name, value in other.get_metadata().variables: for name, value in other.get_metadata().variables:
self.def_var(name, value) self.def_var(name, value)
elif prop == "props": elif prop == "props":
self._metadata.props = deepcopy(other.get_metadata().props) self._metadata.props = core.utils.sheerka_deepcopy(other.get_metadata().props)
else: else:
setattr(self._metadata, prop, getattr(other.get_metadata(), prop)) setattr(self._metadata, prop, getattr(other.get_metadata(), prop))
@@ -534,6 +534,27 @@ class Concept:
bag[prop] = getattr(self, prop) bag[prop] = getattr(self, prop)
return bag return bag
def as_debug_bag(self, new_obj, recurse):
bag = {"id": self.id, "name": self.name, "key": self.key}
# add variable metadata
for k, v in [(k, v) for k, v in self._metadata.variables if v]:
bag[f"meta.{k}"] = f"'{v}'"
# add compiled info
for k, v in [(k, v) for k, v in self._compiled.items() if isinstance(v, Concept)]:
bag[f"compiled.{k}"] = new_obj(v) if recurse else v
# add values
for prop in [p for p in self.__dict__ if not p.startswith("_") and not p.startswith("##")]:
v = self.get_value(prop)
if v == NotInit:
continue
bag[f"value.{prop}"] = new_obj(v) if (isinstance(v, Concept) and recurse) else core.utils.escape_str(v)
return bag
def get_format_instructions(self): def get_format_instructions(self):
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
return self.get_prop(BuiltinConcepts.FORMAT_INSTRUCTIONS) return self.get_prop(BuiltinConcepts.FORMAT_INSTRUCTIONS)
+5 -3
View File
@@ -1,7 +1,9 @@
# events # events
CONCEPT_PRECEDENCE_MODIFIED = "cpm" EVENT_CONCEPT_PRECEDENCE_MODIFIED = "evt_cpm"
RULE_PRECEDENCE_MODIFIED = "rpm" EVENT_RULE_PRECEDENCE_MODIFIED = "evt_rpm"
CONTEXT_DISPOSED = "cd" EVENT_CONTEXT_DISPOSED = "evt_cd"
EVENT_USER_INPUT_EVALUATED = "evt_uie"
EVENT_CONCEPT_CREATED = "evt_cc"
# comparison context # comparison context
RULE_COMPARISON_CONTEXT = "Rule" RULE_COMPARISON_CONTEXT = "Rule"
+5 -10
View File
@@ -5,18 +5,13 @@ import time
from core.builtin_concepts import BuiltinConcepts, ParserResultConcept from core.builtin_concepts import BuiltinConcepts, ParserResultConcept
from core.concept import Concept, get_concept_attrs from core.concept import Concept, get_concept_attrs
from core.global_symbols import CONTEXT_DISPOSED from core.global_symbols import EVENT_CONTEXT_DISPOSED
from core.sheerka.services.SheerkaExecute import NO_MATCH from core.sheerka.services.SheerkaExecute import NO_MATCH
from core.sheerka.services.SheerkaMemory import SheerkaMemory from core.sheerka.services.SheerkaMemory import SheerkaMemory
from core.utils import CONSOLE_COLORS_MAP as CCM from core.utils import CONSOLE_COLORS_MAP as CCM, CONSOLE_COLUMNS
from sdp.sheerkaDataProvider import Event from sdp.sheerkaDataProvider import Event
try: pp = pprint.PrettyPrinter(indent=2, width=CONSOLE_COLUMNS)
rows, columns = os.popen('stty size', 'r').read().split()
except ValueError:
rows, columns = 50, 80
pp = pprint.PrettyPrinter(indent=2, width=columns)
DEBUG_TAB_SIZE = 4 DEBUG_TAB_SIZE = 4
@@ -134,7 +129,7 @@ class ExecutionContext:
return return
if self.stm: if self.stm:
self.sheerka.publish(self, CONTEXT_DISPOSED) self.sheerka.publish(self, EVENT_CONTEXT_DISPOSED)
self._stop = time.time_ns() self._stop = time.time_ns()
@@ -207,7 +202,7 @@ class ExecutionContext:
def activate_push(self): def activate_push(self):
if self._push: if self._push:
if self._push.stm: if self._push.stm:
self.sheerka.publish(self._push, CONTEXT_DISPOSED) self.sheerka.publish(self._push, EVENT_CONTEXT_DISPOSED)
self._push._stop = time.time_ns() self._push._stop = time.time_ns()
self._push = None self._push = None
+11 -53
View File
@@ -14,9 +14,11 @@ from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConc
UnknownConcept, AllBuiltinConcepts UnknownConcept, AllBuiltinConcepts
from core.concept import Concept, ConceptParts, NotInit, get_concept_attrs from core.concept import Concept, ConceptParts, NotInit, get_concept_attrs
from core.error import ErrorObj from core.error import ErrorObj
from core.global_symbols import EVENT_USER_INPUT_EVALUATED
from core.profiling import profile from core.profiling import profile
from core.sheerka.ExecutionContext import ExecutionContext from core.sheerka.ExecutionContext import ExecutionContext
from core.sheerka_logger import console_handler from core.sheerka_logger import console_handler
from core.simple_debug import my_debug
from core.tokenizer import Token, TokenKind from core.tokenizer import Token, TokenKind
from printer.SheerkaPrinter import SheerkaPrinter from printer.SheerkaPrinter import SheerkaPrinter
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event from sdp.sheerkaDataProvider import SheerkaDataProvider, Event
@@ -81,15 +83,6 @@ class Sheerka(Concept):
self.return_value_concept_id = None self.return_value_concept_id = None
self.error_concept_id = None self.error_concept_id = None
# a concept can be instantiated
# ex: File is a concept, but File('foo.txt') is an instance
# TODO: manage contexts
self.instances = []
# List of the known rules by the system
# ex: hello => say('hello')
self.rules = []
self.sdp: SheerkaDataProvider = None self.sdp: SheerkaDataProvider = None
self.cache_manager = CacheManager(cache_only) self.cache_manager = CacheManager(cache_only)
@@ -105,10 +98,11 @@ class Sheerka(Concept):
self.printer_handler = SheerkaPrinter(self) self.printer_handler = SheerkaPrinter(self)
self.during_restore = False self.during_restore = False
self.during_initialisation = False
self._builtins_classes_cache = None self._builtins_classes_cache = None
self.save_execution_context = True self.save_execution_context = True
self.enable_process_return_values = False self.enable_process_return_values = True
self.methods_with_context = {"test_using_context"} # only the names, the method is defined in sheerka_methods self.methods_with_context = {"test_using_context"} # only the names, the method is defined in sheerka_methods
self.sheerka_methods = { self.sheerka_methods = {
@@ -120,10 +114,6 @@ class Sheerka(Concept):
self.locals = {} self.locals = {}
self.last_executions = []
self.last_return_values = []
self.execution_count = 0
@property @property
def resolved_concepts_by_first_keyword(self): def resolved_concepts_by_first_keyword(self):
""" """
@@ -196,6 +186,7 @@ class Sheerka(Concept):
self.enable_process_return_values = enable_process_return_values self.enable_process_return_values = enable_process_return_values
try: try:
self.during_initialisation = True
from sheerkapickle.sheerka_handlers import initialize_pickle_handlers from sheerkapickle.sheerka_handlers import initialize_pickle_handlers
initialize_pickle_handlers() initialize_pickle_handlers()
@@ -235,6 +226,9 @@ class Sheerka(Concept):
except IOError as e: except IOError as e:
res = ReturnValueConcept(self, False, self.get(BuiltinConcepts.ERROR), e) res = ReturnValueConcept(self, False, self.get(BuiltinConcepts.ERROR), e)
finally:
self.during_initialisation = False
return res return res
def initialize_caching(self): def initialize_caching(self):
@@ -445,6 +439,7 @@ class Sheerka(Concept):
:return: :return:
""" """
# self.log.debug(f"Processing user input '{text}', {user_name=}.") # self.log.debug(f"Processing user input '{text}', {user_name=}.")
my_debug(f"****************** Processing user input '{text}', {user_name=}.***********************************")
event = Event(text, user_name) event = Event(text, user_name)
self.sdp.save_event(event) self.sdp.save_event(event)
@@ -456,6 +451,7 @@ class Sheerka(Concept):
desc=f"Evaluating '{text}'") as execution_context: desc=f"Evaluating '{text}'") as execution_context:
user_input = self.ret(self.name, True, self.new(BuiltinConcepts.USER_INPUT, body=text, user_name=user_name)) user_input = self.ret(self.name, True, self.new(BuiltinConcepts.USER_INPUT, body=text, user_name=user_name))
execution_context.add_inputs(user_input=user_input)
# TODO. Must be a context hint, not a return value # TODO. Must be a context hint, not a return value
reduce_requested = self.ret(self.name, True, self.new(BuiltinConcepts.REDUCE_REQUESTED)) reduce_requested = self.ret(self.name, True, self.new(BuiltinConcepts.REDUCE_REQUESTED))
@@ -466,32 +462,12 @@ class Sheerka(Concept):
if self.cache_manager.is_dirty: if self.cache_manager.is_dirty:
self.cache_manager.commit(execution_context) self.cache_manager.commit(execution_context)
# exec_count = ExecutionContext.ids[execution_context.event.get_digest()] self.publish(execution_context, EVENT_USER_INPUT_EVALUATED)
# print("Execution Context Count:", exec_count)
if self.save_execution_context:
try:
# if exec_count > 3400:
# print("Saving result. digest=", execution_context.event.get_digest())
self.sdp.save_result(execution_context)
except Exception as ex:
print(f"Failed to save execution context. Reason: {ex}")
pass
# self.log.error(f"Failed to save execution context. Reason: {ex}")
# Do not save execution contexts from process_return_values # Do not save execution contexts from process_return_values
if self.enable_process_return_values: if self.enable_process_return_values:
self.process_return_values(execution_context, ret) self.process_return_values(execution_context, ret)
self.execution_count += 1
self._last_execution = execution_context
if len(self.last_executions) == self.MAX_EXECUTION_HISTORY:
del self.last_executions[0]
self.last_executions.append(execution_context)
if len(self.last_return_values) == self.MAX_RETURN_VALUES_HISTORY:
del self.last_return_values[0]
self.last_return_values.append(ret)
return ret return ret
def print(self, result, instructions=None): def print(self, result, instructions=None):
@@ -887,24 +863,6 @@ class Sheerka(Concept):
return self.parsers_prefix + name return self.parsers_prefix + name
# def concepts(self):
# """
# List of all known concepts (look up in sdp)
# :return:
# """
# res = []
# lst = self.sdp.list(self.CONCEPTS_BY_ID_ENTRY)
# for item in lst:
# if isinstance(item, list):
# res.extend(item)
# else:
# res.append(item)
#
# return sorted(res, key=lambda i: int(i.id))
def get_last_execution(self):
return self._last_execution
def test(self): def test(self):
return f"I have access to Sheerka !" return f"I have access to Sheerka !"
-41
View File
@@ -24,9 +24,6 @@ class SheerkaAdmin(BaseService):
self.sheerka.bind_service_method(self.restore, True) self.sheerka.bind_service_method(self.restore, True)
self.sheerka.bind_service_method(self.concepts, False) self.sheerka.bind_service_method(self.concepts, False)
self.sheerka.bind_service_method(self.desc, False) self.sheerka.bind_service_method(self.desc, False)
self.sheerka.bind_service_method(self.last_created_concept, False)
self.sheerka.bind_service_method(self.last_ret, False)
self.sheerka.bind_service_method(self.last_error_ret, False)
self.sheerka.bind_service_method(self.extended_isinstance, False) self.sheerka.bind_service_method(self.extended_isinstance, False)
self.sheerka.bind_service_method(self.is_container, False) self.sheerka.bind_service_method(self.is_container, False)
self.sheerka.bind_service_method(self.format_rules, False) self.sheerka.bind_service_method(self.format_rules, False)
@@ -149,44 +146,6 @@ class SheerkaAdmin(BaseService):
def format_rules(self): def format_rules(self):
return self.sheerka.new(BuiltinConcepts.TO_LIST, items=self.sheerka.get_format_rules()) return self.sheerka.new(BuiltinConcepts.TO_LIST, items=self.sheerka.get_format_rules())
def last_created_concept(self, use_history=False):
for exec_result in reversed(self.sheerka.last_executions):
return_values = exec_result.values["return_values"]
for ret in return_values:
if ret.status and self.sheerka.isinstance(ret.value, BuiltinConcepts.NEW_CONCEPT):
return ret.value.body
if use_history:
return self.sheerka.new(BuiltinConcepts.ERROR, body="Not yet implement")
return self.sheerka.new(BuiltinConcepts.NOT_FOUND)
def last_ret(self, context, index=-1):
try:
last = self.sheerka.last_return_values[index]
return last[0] if isinstance(last, list) and len(last) == 1 else last
except IndexError:
return None
def last_error_ret(self, context, index=-1):
while index >= -len(self.sheerka.last_return_values):
last = self.sheerka.last_return_values[index]
last = [last] if not hasattr(last, "__iter__") else last
last = [ret_val for ret_val in last if not ret_val.status]
if len(last) == 0:
index -= 1
continue
if len(last) > 1:
return context.sheerka.ret(SheerkaAdmin.NAME,
False,
context.sheerka.new(BuiltinConcepts.TOO_MANY_ERRORS, body=last))
return last[0]
return context.sheerka.ret(SheerkaAdmin.NAME,
False,
context.sheerka.new(BuiltinConcepts.NOT_FOUND))
def extended_isinstance(self, a, b): def extended_isinstance(self, a, b):
""" """
@@ -3,7 +3,7 @@ from dataclasses import dataclass
from cache.Cache import Cache from cache.Cache import Cache
from cache.ListCache import ListCache from cache.ListCache import ListCache
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.global_symbols import CONCEPT_PRECEDENCE_MODIFIED, RULE_PRECEDENCE_MODIFIED, RULE_COMPARISON_CONTEXT, \ from core.global_symbols import EVENT_CONCEPT_PRECEDENCE_MODIFIED, EVENT_RULE_PRECEDENCE_MODIFIED, RULE_COMPARISON_CONTEXT, \
CONCEPT_COMPARISON_CONTEXT CONCEPT_COMPARISON_CONTEXT
from core.builtin_helpers import ensure_concept_or_rule from core.builtin_helpers import ensure_concept_or_rule
from core.concept import Concept from core.concept import Concept
@@ -183,9 +183,9 @@ class SheerkaComparisonManager(BaseService):
if comparison_obj.property == BuiltinConcepts.PRECEDENCE: if comparison_obj.property == BuiltinConcepts.PRECEDENCE:
if comparison_obj.context == CONCEPT_COMPARISON_CONTEXT: if comparison_obj.context == CONCEPT_COMPARISON_CONTEXT:
self.sheerka.publish(context, CONCEPT_PRECEDENCE_MODIFIED) self.sheerka.publish(context, EVENT_CONCEPT_PRECEDENCE_MODIFIED)
elif comparison_obj.context == RULE_COMPARISON_CONTEXT: elif comparison_obj.context == RULE_COMPARISON_CONTEXT:
self.sheerka.publish(context, RULE_PRECEDENCE_MODIFIED) self.sheerka.publish(context, EVENT_RULE_PRECEDENCE_MODIFIED)
return self.sheerka.ret(self.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS)) return self.sheerka.ret(self.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
@@ -2,6 +2,7 @@ import core.utils
from core.builtin_concepts import BuiltinConcepts, ErrorConcept from core.builtin_concepts import BuiltinConcepts, ErrorConcept
from core.builtin_helpers import ensure_concept from core.builtin_helpers import ensure_concept
from core.concept import Concept, DEFINITION_TYPE_DEF, DEFINITION_TYPE_BNF, freeze_concept_attrs from core.concept import Concept, DEFINITION_TYPE_DEF, DEFINITION_TYPE_BNF, freeze_concept_attrs
from core.global_symbols import EVENT_CONCEPT_CREATED
from core.sheerka.services.sheerka_service import BaseService from core.sheerka.services.sheerka_service import BaseService
from sdp.sheerkaDataProvider import SheerkaDataProviderDuplicateKeyError from sdp.sheerkaDataProvider import SheerkaDataProviderDuplicateKeyError
@@ -85,6 +86,9 @@ class SheerkaCreateNewConcept(BaseService):
if concept.get_bnf() and init_bnf_ret_value is not None and init_bnf_ret_value.status: if concept.get_bnf() and init_bnf_ret_value is not None and init_bnf_ret_value.status:
sheerka.cache_manager.clear(sheerka.CONCEPTS_GRAMMARS_ENTRY) sheerka.cache_manager.clear(sheerka.CONCEPTS_GRAMMARS_ENTRY)
# publish the new concept
sheerka.publish(context, EVENT_CONCEPT_CREATED, concept)
# process the return if needed # process the return if needed
ret = sheerka.ret(self.NAME, True, sheerka.new(BuiltinConcepts.NEW_CONCEPT, body=concept)) ret = sheerka.ret(self.NAME, True, sheerka.new(BuiltinConcepts.NEW_CONCEPT, body=concept))
return ret return ret
+250 -163
View File
@@ -1,20 +1,57 @@
import os
import pprint import pprint
import re import re
from dataclasses import dataclass from dataclasses import dataclass
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept, NotInit
from core.sheerka.ExecutionContext import ExecutionContext from core.sheerka.ExecutionContext import ExecutionContext
from core.sheerka.services.sheerka_service import BaseService from core.sheerka.services.sheerka_service import BaseService
from core.utils import CONSOLE_COLORS_MAP as CCM from core.utils import CONSOLE_COLORS_MAP as CCM, CONSOLE_COLUMNS, PRIMITIVES_TYPES
from core.utils import evaluate_expression, as_bag from core.utils import evaluate_expression, as_bag
try: pp = pprint.PrettyPrinter(indent=2, width=CONSOLE_COLUMNS)
rows, columns = os.popen('stty size', 'r').read().split()
except ValueError:
rows, columns = 50, 80
pp = pprint.PrettyPrinter(indent=2, width=columns) NotFound = "** Not Found **"
class ConceptDebugObj:
def __init__(self, concept):
self.concept = concept
self.attrs = concept.as_debug_bag(lambda x: ConceptDebugObj(x), True)
def __repr__(self):
return f"({self.concept_repr()})"
def __eq__(self, other):
if not isinstance(other, ConceptDebugObj):
return False
return self.attrs == other.attrs
def __hash__(self):
return hash(self.concept)
def concept_repr(self):
res = f":{self.concept.name}|{self.concept.id}:"
# print metadata
first = True
for k, v in [(k, v) for k, v in self.attrs.items() if k not in ("id", "name", "key")]:
if not first:
res += ", "
res += f"{k}={v}"
first = False
return res
class ConceptNodeDebugObj:
def __init__(self, concept_node):
self.concept_node = concept_node
self.concept_debug = ConceptDebugObj(concept_node.concept)
def __repr__(self):
return f"ConceptNode({self.concept_debug.concept_repr()})"
class BaseDebugLogger: class BaseDebugLogger:
@@ -49,6 +86,9 @@ class BaseDebugLogger:
def is_enabled(self): def is_enabled(self):
pass pass
def get_enabled_vars(self):
pass
class NullDebugLogger(BaseDebugLogger): class NullDebugLogger(BaseDebugLogger):
def __init__(self): def __init__(self):
@@ -57,6 +97,9 @@ class NullDebugLogger(BaseDebugLogger):
def is_enabled(self): def is_enabled(self):
return False return False
def get_enabled_vars(self):
pass
class ConsoleDebugLogger(BaseDebugLogger): class ConsoleDebugLogger(BaseDebugLogger):
@@ -70,9 +113,29 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.is_highlighted = "" self.is_highlighted = ""
def is_enabled(self): def is_enabled(self):
"""
True if the debug is activated for the current service, method and context
:return:
"""
return True return True
def get_enabled_vars(self):
"""
Returns the list of all enabled variables for this console debugger
:return:
"""
return self.debug_manager.get_enabled_items("vars",
self.context,
self.service_name,
self.method_name,
self.debug_id)
def debug_entering(self, **kwargs): def debug_entering(self, **kwargs):
"""
Log that we start debugging a method (for a specified service and context)
:param kwargs:
:return:
"""
super().debug_entering(**kwargs) super().debug_entering(**kwargs)
str_text = f"{CCM['blue']}Entering {self.service_name}.{self.method_name} with {CCM['reset']}" str_text = f"{CCM['blue']}Entering {self.service_name}.{self.method_name} with {CCM['reset']}"
@@ -84,10 +147,24 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.debug_manager.debug(self.prefix() + str_vars) self.debug_manager.debug(self.prefix() + str_vars)
def debug_log(self, text, is_error=False): def debug_log(self, text, is_error=False):
"""
Prints a debug information (not related to a specific variable, concept or rule)
:param text:
:param is_error:
:return:
"""
color = 'red' if is_error else 'blue' color = 'red' if is_error else 'blue'
self.debug_manager.debug(self.prefix() + f"{CCM[color]}..{text}{CCM['reset']}") self.debug_manager.debug(self.prefix() + f"{CCM[color]}..{text}{CCM['reset']}")
def debug_var(self, name, value, is_error=False, hint=None): def debug_var(self, name, value, is_error=False, hint=None):
"""
Prints the value of a variable
:param name:
:param value:
:param is_error:
:param hint:
:return:
"""
enabled = is_error or self.debug_manager.compute_debug_var(self.context, enabled = is_error or self.debug_manager.compute_debug_var(self.context,
self.service_name, self.service_name,
self.method_name, self.method_name,
@@ -103,6 +180,12 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.debug(str_text, str_vars) self.debug(str_text, str_vars)
def debug_rule(self, rule, results): def debug_rule(self, rule, results):
"""
Prints debug information related to a specific rule id
:param rule:
:param results:
:return:
"""
if not self.debug_manager.compute_debug_rule(self.context, if not self.debug_manager.compute_debug_rule(self.context,
self.service_name, self.service_name,
self.method_name, self.method_name,
@@ -115,6 +198,13 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.debug(str_text, str_vars) self.debug(str_text, str_vars)
def debug_concept(self, concept, text=None, **kwargs): def debug_concept(self, concept, text=None, **kwargs):
"""
Prints debug information related to a specific concept
:param concept:
:param text:
:param kwargs:
:return:
"""
raw = kwargs.pop('raw', None) raw = kwargs.pop('raw', None)
if not self.debug_manager.compute_debug_concept(self.context, if not self.debug_manager.compute_debug_concept(self.context,
self.service_name, self.service_name,
@@ -283,27 +373,6 @@ class SheerkaDebugManager(BaseService):
return debug_for_self, debug_for_children return debug_for_self, debug_for_children
def inspect(self, context, context_id, *props):
"""
Print
:param context:
:param context_id:
:return:
"""
to_inspect = self.sheerka.get_execution_item(context, context_id)
if not isinstance(to_inspect, ExecutionContext):
return to_inspect
if not props:
props = ["inputs", "values.return_values"]
bag = as_bag(to_inspect)
res = {}
for prop in props:
res[prop] = evaluate_expression(prop, bag)
return self.sheerka.new(BuiltinConcepts.TO_DICT, body=res)
def debug(self, *args, **kwargs): def debug(self, *args, **kwargs):
print(*args, **kwargs) print(*args, **kwargs)
@@ -428,6 +497,24 @@ class SheerkaDebugManager(BaseService):
return res return res
def get_enabled_items(self, item_type, context, service_name, method_name, debug_id):
if not self.activated:
return False
selected = set()
for setting in getattr(self, self.container_name(item_type)):
if not setting.enabled or setting.item is None:
continue
if (setting.service_name is None or setting.service_name == service_name) and \
(setting.method_name is None or setting.method_name == method_name) and \
(setting.context_id is None or setting.context_id == context.id or (
setting.context_children and context.has_parent(setting.context_id))) and \
(setting.debug_id is None or setting.debug_id == debug_id):
selected.add(setting.item)
return selected
def reset_debug(self, context): def reset_debug(self, context):
for item_type in ["vars", "rules", "concepts"]: for item_type in ["vars", "rules", "concepts"]:
setting_name = self.container_name(item_type) setting_name = self.container_name(item_type)
@@ -487,6 +574,123 @@ class SheerkaDebugManager(BaseService):
def compute_debug_rule(self, context, service_name, method_name, item, debug_id): def compute_debug_rule(self, context, service_name, method_name, item, debug_id):
return self.compute_debug_item("rules", context, service_name, method_name, item, debug_id) return self.compute_debug_item("rules", context, service_name, method_name, item, debug_id)
def inspect(self, context, *args, **kwargs):
"""
Print
:param context:
:param args: 1st parameter is what to display, the other are the properties to display
:param kwargs: how to display the result
:return:
"""
if len(args) == 0:
return
forced_prop, props = None, None
if isinstance(args[0], int):
to_inspect = self.sheerka.get_execution_item(context, int(args[0]))
if isinstance(to_inspect, ExecutionContext):
if len(args) == 1:
props = ["inputs", "values.return_values"]
else:
props = args[1:]
else:
props = None
else:
to_inspect = args[0]
if isinstance(to_inspect, Concept):
if len(args) > 1:
forced_prop = args[1:]
props = forced_prop
else:
props = args[1:]
bag = self.as_debug_bag(to_inspect, False, forced_prop)
props = props or list(bag.keys())
res = self.inspect_object(bag, props, False, **kwargs)
# Attributes that are not found are probably directly requested by the user
# Let's try for the full names of these attributes
not_found = {k: v for k, v in [(k, v) for k, v in res.items() if v == NotFound]}
if len(not_found) > 0:
to_add = {}
to_remove = []
for k, v in not_found.items():
alternate_props = ["meta." + k, "compiled." + k, "value." + k]
res2 = self.inspect_object(bag, alternate_props, True, **kwargs)
if len(res2) > 0:
to_add.update(res2)
to_remove.append(k)
res.update(to_add)
for k in to_remove:
del res[k]
return self.sheerka.new(BuiltinConcepts.TO_DICT, body=res)
def inspect_object(self, bag, props, discard_not_found, **kwargs):
values_required = kwargs.get("values", False)
as_bag_required = kwargs.get("as_bag", False)
res = {}
for prop in props:
if prop in res:
# discard duplicates
continue
try:
value = bag[prop] if prop.startswith("#") else evaluate_expression(prop, bag)
except NameError:
if discard_not_found:
continue
else:
value = NotFound
if callable(value):
# discard methods
continue
if values_required:
value = self.get_inner_values(value, **kwargs)
elif as_bag_required:
value = self.get_debug_repr(value, **kwargs)
res[prop] = value
return res
def get_inner_values(self, obj, **kwargs):
if obj is None:
return None
if isinstance(obj, list):
return [self.get_inner_values(item, **kwargs) for item in obj]
if hasattr(obj, "get_obj_value"):
return obj.get_obj_value()
if not isinstance(obj, Concept):
return self.get_debug_repr(obj, **kwargs)
if obj.body is NotInit:
return self.get_debug_repr(obj, **kwargs)
return self.get_inner_values(obj.body, **kwargs)
def get_debug_repr(self, obj, **kwargs):
if kwargs.get("as_bag", False):
forced_props = self.get_concept_forced_props(obj) if isinstance(obj, Concept) else None
return self.as_debug_bag(obj, True, forced_props)
from parsers.BaseNodeParser import ConceptNode
if isinstance(obj, Concept):
return ConceptDebugObj(obj)
elif isinstance(obj, ConceptNode):
return ConceptNodeDebugObj(obj)
else:
return obj
@staticmethod @staticmethod
def container_name(item_type): def container_name(item_type):
return f"debug_{item_type}_settings" return f"debug_{item_type}_settings"
@@ -505,7 +709,7 @@ class SheerkaDebugManager(BaseService):
if len(parts) > 1: if len(parts) > 1:
method_name = None if parts[1] == "*" else parts[1] method_name = None if parts[1] == "*" else parts[1]
if len(parts) > 2: if len(parts) > 2:
item = parts[2] item = ".".join(parts[2:])
if len(args) > 1: if len(args) > 1:
context_part = args[1] context_part = args[1]
@@ -535,139 +739,22 @@ class SheerkaDebugManager(BaseService):
return item, service, method_name, context_id, context_children, debug_id, enabled return item, service, method_name, context_id, context_children, debug_id, enabled
# def debug_rule(self, context, rule=None, context_id=None, debug_id=None, enabled=True): @staticmethod
# """ def get_concept_forced_props(concept):
# Add a debug rule request return ["id", "name", "key"] + [p for p in concept.__dict__ if not p.startswith("_")]
# :param context:
# :param rule:
# :param context_id:
# :param debug_id:
# :param enabled:
# :return:
# """
# rule = str(rule) if rule is not None else None
# for setting in self.debug_rules_settings:
# if setting.rule_id == rule and \
# setting.context_id == context_id and \
# setting.debug_id == debug_id:
# setting.enabled = enabled
# break
# else:
# self.debug_rules_settings.append(DebugRuleSetting(rule,
# context_id,
# debug_id,
# enabled))
#
# self.sheerka.record_var(context, self.NAME, "debug_rules_settings", self.debug_rules_settings)
# return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
#
# def debug_concept(self, context, concept=None, context_id=None, debug_id=None, enabled=True):
# """
# Add a debug rule request
# :param context:
# :param concept:
# :param context_id:
# :param debug_id:
# :param enabled:
# :return:
# """
# concept_id = concept.id if isinstance(concept, Concept) else str(concept) if concept is not None else None
# for setting in self.debug_concepts_settings:
# if setting.concept_id == concept_id and \
# setting.context_id == context_id and \
# setting.debug_id == debug_id:
# setting.enabled = enabled
# break
# else:
# self.debug_concepts_settings.append(DebugConceptSetting(concept_id,
# context_id,
# debug_id,
# enabled))
#
# self.sheerka.record_var(context, self.NAME, "debug_concepts_settings", self.debug_concepts_settings)
# return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
#
# @staticmethod
def as_debug_bag(obj, recurse=True, forced_props=None):
if type(obj) in PRIMITIVES_TYPES:
return obj
# def compute_debug_var(self, service_name, method_name, context_id, variable_name, debug_id): res = {'#type#': type(obj).__name__}
# if not self.activated: if isinstance(obj, Concept) and not obj.get_metadata().is_builtin:
# return False res.update(obj.as_debug_bag(SheerkaDebugManager.as_debug_bag, recurse))
#
# selected = [] else:
# for setting in self.debug_vars_settings: forced_props_to_use = [p for p in forced_props if p != "#type#"] if forced_props else None
# if setting.variable_name is None and setting.debug_id is None: res.update(as_bag(obj, forced_props_to_use))
# continue del res["self"]
#
# if (setting.service_name is None or setting.service_name == service_name) and \ return res
# (setting.method_name is None or setting.method_name == method_name) and \
# (setting.context_id is None or setting.context_id == context_id) and \
# (setting.variable_name is None or
# setting.variable_name == "*" or
# setting.variable_name == variable_name) and \
# (setting.debug_id is None or setting.debug_id == debug_id):
# selected.append(setting.enabled)
#
# if len(selected) == 0:
# return False
#
# res = selected[0]
# for enabled in selected[1:]:
# if res == False or enabled == False:
# return False
#
# if isinstance(res, str):
# continue
#
# res = enabled
#
# return res
#
# def compute_debug_rule(self, rule_id, context_id, debug_id):
# if not self.activated:
# return False
#
# selected = []
# for setting in self.debug_rules_settings:
# if (setting.rule_id is None or setting.rule_id == rule_id) and \
# (setting.context_id is None or setting.context_id == context_id) and \
# (setting.debug_id is None or setting.debug_id == debug_id):
# selected.append(setting.enabled)
#
# if len(selected) == 0:
# return False
#
# res = selected[0]
# for enabled in selected[1:]:
# res &= enabled
#
# return res
#
# def compute_debug_concept(self, concept_id, context_id, debug_id):
# if not self.activated:
# return False
#
# selected = []
# for setting in self.debug_concepts_settings:
# if (setting.concept_id is None or setting.concept_id == concept_id) and \
# (setting.context_id is None or setting.context_id == context_id) and \
# (setting.debug_id is None or setting.debug_id == debug_id):
# selected.append(setting.enabled)
#
# if len(selected) == 0:
# return False
#
# res = selected[0]
# for enabled in selected[1:]:
# res &= enabled
#
# return res
#
# def reset_debug_rules(self, context):
# self.debug_rules_settings.clear()
# self.sheerka.record_var(context, self.NAME, "debug_rules_settings", self.debug_rules_settings)
# return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
#
# def get_debug_settings(self):
# lst = self.debug_vars_settings + self.debug_concepts_settings + self.debug_rules_settings
# return self.sheerka.new(BuiltinConcepts.TO_LIST, body=lst)
+2 -3
View File
@@ -1,16 +1,15 @@
import os
import pprint import pprint
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept from core.concept import Concept
from core.sheerka.ExecutionContext import ExecutionContext from core.sheerka.ExecutionContext import ExecutionContext
from core.sheerka.services.sheerka_service import BaseService from core.sheerka.services.sheerka_service import BaseService
from core.utils import CONSOLE_COLUMNS
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event from sdp.sheerkaDataProvider import SheerkaDataProvider, Event
def get_pp(): def get_pp():
rows, columns = os.popen('stty size', 'r').read().split() pp = pprint.PrettyPrinter(width=CONSOLE_COLUMNS, compact=True)
pp = pprint.PrettyPrinter(width=columns, compact=True)
return pp return pp
+17 -11
View File
@@ -4,7 +4,7 @@ from cache.FastCache import FastCache
from cache.ListIfNeededCache import ListIfNeededCache from cache.ListIfNeededCache import ListIfNeededCache
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept from core.concept import Concept
from core.global_symbols import CONTEXT_DISPOSED from core.global_symbols import EVENT_CONTEXT_DISPOSED
from core.sheerka.services.sheerka_service import BaseService, ServiceObj from core.sheerka.services.sheerka_service import BaseService, ServiceObj
@@ -23,7 +23,7 @@ class SheerkaMemory(BaseService):
def __init__(self, sheerka): def __init__(self, sheerka):
super().__init__(sheerka) super().__init__(sheerka)
self.short_term_objects = FastCache() self.short_term_objects = FastCache()
self.objects = ListIfNeededCache(default=lambda k: self.sheerka.sdp.get(self.OBJECTS_ENTRY, k)) self.memory_objects = ListIfNeededCache(default=lambda k: self.sheerka.sdp.get(self.OBJECTS_ENTRY, k))
self.registration = {} self.registration = {}
def initialize(self): def initialize(self):
@@ -37,14 +37,16 @@ class SheerkaMemory(BaseService):
self.sheerka.bind_service_method(self.unregister_object, True, visible=False) self.sheerka.bind_service_method(self.unregister_object, True, visible=False)
self.sheerka.bind_service_method(self.add_registered_objects, True, visible=False) self.sheerka.bind_service_method(self.add_registered_objects, True, visible=False)
self.sheerka.bind_service_method(self.memory, False) self.sheerka.bind_service_method(self.memory, False)
self.sheerka.bind_service_method(self.mem, False)
self.sheerka.cache_manager.register_cache(self.OBJECTS_ENTRY, self.objects, persist=True, use_ref=True) self.sheerka.cache_manager.register_cache(self.OBJECTS_ENTRY, self.memory_objects, persist=True, use_ref=True)
def reset(self): def reset(self):
self.short_term_objects.clear() self.short_term_objects.clear()
self.memory_objects.clear()
def initialize_deferred(self, context, is_first_time): def initialize_deferred(self, context, is_first_time):
self.sheerka.subscribe(CONTEXT_DISPOSED, self.remove_context) self.sheerka.subscribe(EVENT_CONTEXT_DISPOSED, self.remove_context)
def get_from_short_term_memory(self, context, key): def get_from_short_term_memory(self, context, key):
while True: while True:
@@ -90,16 +92,16 @@ class SheerkaMemory(BaseService):
:param concept: :param concept:
:return: :return:
""" """
self.objects.put(key, MemoryObject(context.event.get_digest(), concept)) self.memory_objects.put(key, MemoryObject(context.event.get_digest(), concept))
def get_from_memory(self, context, key): def get_from_memory(self, context, key):
"""" """"
""" """
return self.objects.get(key) return self.memory_objects.get(key)
def register_object(self, context, key, concept): def register_object(self, context, key, concept):
""" """
Before adding objects to memory, they first need to be registered Before adding memory_objects to memory, they first need to be registered
More: More:
We don't want to add all evaluated concept into memory We don't want to add all evaluated concept into memory
(because some of them may be ref to concept already in memory) (because some of them may be ref to concept already in memory)
@@ -126,7 +128,7 @@ class SheerkaMemory(BaseService):
def add_registered_objects(self, context): def add_registered_objects(self, context):
""" """
Adds all registered objects Adds all registered memory_objects
:param context: :param context:
:return: :return:
""" """
@@ -136,7 +138,7 @@ class SheerkaMemory(BaseService):
def memory(self, context, name=None): def memory(self, context, name=None):
""" """
Get the list of all objects in memory Get the list of all memory_objects in memory
:param context: :param context:
:param name: :param name:
:return: :return:
@@ -154,10 +156,14 @@ class SheerkaMemory(BaseService):
return obj.obj return obj.obj
res = {} res = {}
for k in self.objects: for k in self.memory_objects:
obj = self.objects.get(k) obj = self.memory_objects.get(k)
if isinstance(obj, list): if isinstance(obj, list):
obj = obj[-1] obj = obj[-1]
res[k] = obj.obj res[k] = obj.obj
return res return res
def mem(self):
keys = sorted([k for k in self.memory_objects])
return {"keys": keys, "len": len(keys)}
+143 -28
View File
@@ -1,9 +1,14 @@
import ast import ast
from cache.Cache import Cache
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.global_symbols import EVENT_USER_INPUT_EVALUATED, EVENT_CONCEPT_CREATED
from core.sheerka.services.sheerka_service import BaseService from core.sheerka.services.sheerka_service import BaseService
from core.utils import CONSOLE_COLORS_MAP as CCM
from core.utils import as_bag from core.utils import as_bag
MAX_EXECUTION_HISTORY = 100
class SheerkaResultConcept(BaseService): class SheerkaResultConcept(BaseService):
NAME = "Result" NAME = "Result"
@@ -11,6 +16,10 @@ class SheerkaResultConcept(BaseService):
def __init__(self, sheerka, page_size=30): def __init__(self, sheerka, page_size=30):
super().__init__(sheerka) super().__init__(sheerka)
self.page_size = page_size self.page_size = page_size
self.executions_contexts_cache = Cache(MAX_EXECUTION_HISTORY)
self.last_execution = None
self.last_created_concept = None
self.last_created_concept_id = None
def initialize(self): def initialize(self):
self.sheerka.bind_service_method(self.get_results_by_digest, True) # digest is recorded self.sheerka.bind_service_method(self.get_results_by_digest, True) # digest is recorded
@@ -18,6 +27,19 @@ class SheerkaResultConcept(BaseService):
self.sheerka.bind_service_method(self.get_last_results, True) # digest is recorded self.sheerka.bind_service_method(self.get_last_results, True) # digest is recorded
self.sheerka.bind_service_method(self.get_results, False) self.sheerka.bind_service_method(self.get_results, False)
self.sheerka.bind_service_method(self.get_execution_item, False) self.sheerka.bind_service_method(self.get_execution_item, False)
self.sheerka.bind_service_method(self.get_last_ret, False, as_name="last_ret")
self.sheerka.bind_service_method(self.get_last_created_concept, False, as_name="last_created_concept")
def initialize_deferred(self, context, is_first_time):
self.restore_values("last_created_concept_id")
self.sheerka.subscribe(EVENT_USER_INPUT_EVALUATED, self.user_input_evaluated)
self.sheerka.subscribe(EVENT_CONCEPT_CREATED, self.new_concept_created)
def reset(self):
self.executions_contexts_cache.clear()
self.last_execution = None
self.last_created_concept = None
self.last_created_concept_id = None
@staticmethod @staticmethod
def get_predicate(**kwargs): def get_predicate(**kwargs):
@@ -38,12 +60,24 @@ class SheerkaResultConcept(BaseService):
predicate = " and ".join(res) predicate = " and ".join(res)
return compile(ast.parse(predicate, mode="eval"), "<SheerkaResultManager.get_predicate>", mode="eval") return compile(ast.parse(predicate, mode="eval"), "<SheerkaResultManager.get_predicate>", mode="eval")
@staticmethod
def as_list(execution_context, predicate):
def _yield_result(lst):
for e in lst:
if predicate is None or eval(predicate, as_bag(e)):
yield e
if e._children:
yield from _yield_result(e._children)
return _yield_result([execution_context])
def get_results_by_digest(self, context, digest, filter=None, record_digest=True, **kwargs): def get_results_by_digest(self, context, digest, filter=None, record_digest=True, **kwargs):
""" """
Gets the entire execution tree for the given event digest Gets the entire execution tree for the given event digest
:param filter:
:param context: :param context:
:param digest: :param digest:
:param filter:
:param record_digest: :param record_digest:
:return: :return:
""" """
@@ -54,8 +88,12 @@ class SheerkaResultConcept(BaseService):
kwargs["filter"] = filter kwargs["filter"] = filter
try: try:
if digest in self.executions_contexts_cache:
result = self.executions_contexts_cache.get(digest)
event = result.event
else:
result = self.sheerka.sdp.load_result(digest) result = self.sheerka.sdp.load_result(digest)
event = self.sheerka.sdp.load_event(digest) event = self.sheerka.sdp.load_event(digest) # there is no real need for a cache of the events
if record_digest: if record_digest:
context.log(f"Recording digest '{digest}'") context.log(f"Recording digest '{digest}'")
@@ -89,7 +127,18 @@ class SheerkaResultConcept(BaseService):
if command is None: if command is None:
return None return None
start = 0 # first, search in cache
for event_id in self.executions_contexts_cache:
execution_context = self.executions_contexts_cache.get(event_id)
if execution_context.event.message.startswith(command):
return self.get_results_by_digest(context,
execution_context.event.get_digest(),
filter,
record_digest,
**kwargs)
# not found, search in db
start = len(self.executions_contexts_cache)
consumed = 0 consumed = 0
while True: while True:
for event in self.sheerka.sdp.load_events(self.page_size, start): for event in self.sheerka.sdp.load_events(self.page_size, start):
@@ -103,6 +152,7 @@ class SheerkaResultConcept(BaseService):
start += self.page_size start += self.page_size
consumed = 0 consumed = 0
# not found, return error
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"command": command}) return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"command": command})
def get_last_results(self, context, filter=None, record_digest=True, **kwargs): def get_last_results(self, context, filter=None, record_digest=True, **kwargs):
@@ -114,23 +164,16 @@ class SheerkaResultConcept(BaseService):
:return: :return:
""" """
start = 0 if self.last_execution:
page_size = 2 return self.get_results_by_digest(context,
consumed = 0 self.last_execution.event.get_digest(),
while True: filter,
for event in self.sheerka.sdp.load_events(page_size, start): record_digest,
consumed += 1 **kwargs)
if self.sheerka.sdp.has_result(event.get_digest()):
return self.get_results_by_digest(context, event.get_digest(), filter, record_digest, **kwargs)
if consumed < page_size: event_id = self._get_last_execution_result_event_id_from_db()
break if event_id is not None:
return self.get_results_by_digest(context, event_id, filter, record_digest, **kwargs)
if page_size < 100:
page_size *= 2
start += page_size
consumed = 0
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"query": "last"}) return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"query": "last"})
@@ -150,11 +193,20 @@ class SheerkaResultConcept(BaseService):
return self.get_results_by_digest(context, digest, filter, False, **kwargs) return self.get_results_by_digest(context, digest, filter, False, **kwargs)
def get_execution_item(self, context, item_id): def get_execution_item(self, context, item_id):
"""
Return the item_id'th element of the execution result under investigation
:param context:
:param item_id:
:return:
"""
digest = self.sheerka.load_var(self.NAME, "digest") digest = self.sheerka.load_var(self.NAME, "digest")
if digest is None: if digest is None:
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body="no digest") return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body="no digest")
try: try:
if digest in self.executions_contexts_cache:
result = self.executions_contexts_cache.get(digest)
else:
result = self.sheerka.sdp.load_result(digest) result = self.sheerka.sdp.load_result(digest)
items = list(self.as_list(result, self.get_predicate(id=item_id))) items = list(self.as_list(result, self.get_predicate(id=item_id)))
@@ -167,14 +219,77 @@ class SheerkaResultConcept(BaseService):
context.log_error(f"Digest {digest} is not found.", self.NAME, ex) context.log_error(f"Digest {digest} is not found.", self.NAME, ex)
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"digest": digest}) return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"digest": digest})
@staticmethod def user_input_evaluated(self, execution_context):
def as_list(execution_context, predicate): """
def _yield_result(lst): Callback that updates the cache of execution contexts
for e in lst: :param execution_context:
if predicate is None or eval(predicate, as_bag(e)): :return:
yield e """
if self.sheerka.save_execution_context:
try:
self.sheerka.sdp.save_result(execution_context)
except Exception as ex:
print(f"{CCM['red']}Failed to save execution context. Reason: {ex}{CCM['reset']}")
pass
# self.log.error(f"Failed to save execution context. Reason: {ex}")
if e._children: self.executions_contexts_cache.put(execution_context.event.get_digest(), execution_context)
yield from _yield_result(e._children) self.last_execution = execution_context
return _yield_result([execution_context]) def get_last_ret(self, context):
"""
Return the last return value(s)
:return:
"""
if self.last_execution:
return self.last_execution.values["return_values"]
event_id = self._get_last_execution_result_event_id_from_db()
if event_id is not None:
try:
execution_result = self.sheerka.sdp.load_result(event_id)
return execution_result.values["return_values"]
except FileNotFoundError as ex:
context.log_error(f"Digest {event_id} is not found.", self.NAME, ex)
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"digest": event_id})
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"query": "last"})
def new_concept_created(self, context, concept):
self.last_created_concept = concept
self.last_created_concept_id = concept.id
self.sheerka.record_var(context, self.NAME, "last_created_concept_id", concept.id)
def get_last_created_concept(self, context):
if self.last_created_concept:
return self.last_created_concept
if self.last_created_concept_id:
self.last_created_concept = self.sheerka.new((None, self.last_created_concept_id))
return self.last_created_concept
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"query": "last_created_concept"})
def _get_last_execution_result_event_id_from_db(self):
start = 0
page_size = 2
consumed = 0
while True:
for event in self.sheerka.sdp.load_events(page_size, start):
consumed += 1
if self.sheerka.sdp.has_result(event.get_digest()):
return event.get_digest()
if consumed < page_size:
break
if page_size < 100:
page_size *= 2
start += page_size
consumed = 0
return None
@@ -7,7 +7,7 @@ from cache.Cache import Cache
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept
from core.builtin_helpers import parse_unrecognized, only_successful, ensure_rule from core.builtin_helpers import parse_unrecognized, only_successful, ensure_rule
from core.concept import Concept from core.concept import Concept
from core.global_symbols import RULE_PRECEDENCE_MODIFIED, RULE_COMPARISON_CONTEXT from core.global_symbols import EVENT_RULE_PRECEDENCE_MODIFIED, RULE_COMPARISON_CONTEXT
from core.rule import Rule from core.rule import Rule
from core.sheerka.services.sheerka_service import BaseService from core.sheerka.services.sheerka_service import BaseService
from core.tokenizer import Keywords, TokenKind, Token, IterParser from core.tokenizer import Keywords, TokenKind, Token, IterParser
@@ -112,13 +112,14 @@ class FormatAstList(FormatAstNode):
prefix: str = None prefix: str = None
suffix: str = None suffix: str = None
show_index: bool = False show_index: bool = False
index: object = None
items: object = None items: object = None
def clone(self, **kwargs): def clone(self, **kwargs):
return super().clone( return super().clone(
FormatAstList(self.variable), FormatAstList(self.variable),
("items_prop", "recurse_on", "recursion_depth", "debug", "prefix", "suffix", "show_index", "items"), ("items_prop", "recurse_on", "recursion_depth", "debug", "prefix", "suffix", "show_index", "index", "items"),
**kwargs) **kwargs)
@@ -496,10 +497,12 @@ class SheerkaRuleManager(BaseService):
if is_first_time: if is_first_time:
# add builtin rules if it's the first initialization of Sheerka # add builtin rules if it's the first initialization of Sheerka
self.init_builtin_rules(context) self.init_builtin_rules(context)
else:
# adds the other rules (when it's not the first time) # adds the other rules (when it's not the first time)
self.format_rule_cache.populate(lambda: self.sheerka.sdp.list(self.FORMAT_RULE_ENTRY), lambda rule: rule.id) self.format_rule_cache.populate(lambda: self.sheerka.sdp.list(self.FORMAT_RULE_ENTRY), lambda rule: rule.id)
self.exec_rule_cache.populate(lambda: self.sheerka.sdp.list(self.EXEC_RULE_ENTRY), lambda rule: rule.id) self.exec_rule_cache.populate(lambda: self.sheerka.sdp.list(self.EXEC_RULE_ENTRY), lambda rule: rule.id)
self.format_rule_cache.reset_events()
self.exec_rule_cache.reset_events()
# compile all the rules # compile all the rules
for rule_id in self.format_rule_cache: for rule_id in self.format_rule_cache:
@@ -508,7 +511,7 @@ class SheerkaRuleManager(BaseService):
# update rules priorities # update rules priorities
self.update_rules_priorities(context) self.update_rules_priorities(context)
self.sheerka.subscribe(RULE_PRECEDENCE_MODIFIED, self.update_rules_priorities) self.sheerka.subscribe(EVENT_RULE_PRECEDENCE_MODIFIED, self.update_rules_priorities)
def update_rules_priorities(self, context): def update_rules_priorities(self, context):
""" """
@@ -43,6 +43,7 @@ class SheerkaSetsManager(BaseService):
context.log(f"Setting concept {concept} is a {concept_set}", who=self.NAME) context.log(f"Setting concept {concept} is a {concept_set}", who=self.NAME)
core.builtin_helpers.ensure_concept(concept, concept_set) core.builtin_helpers.ensure_concept(concept, concept_set)
if BuiltinConcepts.ISA in concept.get_metadata().props and concept_set in concept.get_metadata().props[ if BuiltinConcepts.ISA in concept.get_metadata().props and concept_set in concept.get_metadata().props[
BuiltinConcepts.ISA]: BuiltinConcepts.ISA]:
return self.sheerka.ret( return self.sheerka.ret(
+46
View File
@@ -0,0 +1,46 @@
default_debug_name = "*default*"
debug_activated = set()
def my_debug(*args, check_started=None):
"""
Write one line per arg in 'debug.txt'
:param args:
:param check_started:
True : first check if start_debug() was called
<name> : first check if start_debug(name) was called
list of <names> : first check if start_debug() is called for all names
:return:
"""
if check_started and default_debug_name not in debug_activated:
return
if isinstance(check_started, str) and check_started not in debug_activated:
return
if isinstance(check_started, list):
for debug_name in check_started:
if debug_name not in debug_activated:
return
# with open("debug.txt", "a") as f:
# for arg in args:
# if isinstance(arg, list):
# for item in arg:
# f.write(f"{item}\n")
# else:
# f.write(f"{arg}\n")
def start_debug(debug_name=default_debug_name, msg=None):
debug_activated.add(debug_name)
if msg:
with open("debug.txt", "a") as f:
f.write(f"{msg}\n")
def stop_debug(debug_name=default_debug_name, msg=None):
if msg:
with open("debug.txt", "a") as f:
f.write(f"{msg}\n")
debug_activated.remove(debug_name)
+88 -49
View File
@@ -1,16 +1,15 @@
import ast import ast
import importlib import importlib
import inspect import inspect
import os
import pkgutil import pkgutil
from copy import deepcopy
from cache.Cache import Cache from cache.Cache import Cache
from core.ast_helpers import ast_to_props from core.ast_helpers import ast_to_props
from core.tokenizer import TokenKind, Tokenizer from core.tokenizer import TokenKind, Tokenizer
from pyparsing import * from pyparsing import *
default_debug_name = "*default*"
debug_activated = set()
COLORS = { COLORS = {
"black", "black",
"red", "red",
@@ -43,55 +42,17 @@ integer = Word(nums)
escapeSeq = Combine(ESC + '[' + Optional(delimitedList(integer, ';')) + escapeSeq = Combine(ESC + '[' + Optional(delimitedList(integer, ';')) +
oneOf(list(alphas))) oneOf(list(alphas)))
try:
CONSOLE_ROWS, CONSOLE_COLUMNS = os.popen('stty size', 'r').read().split()
CONSOLE_ROWS, CONSOLE_COLUMNS = int(CONSOLE_ROWS), int(CONSOLE_COLUMNS)
except ValueError:
CONSOLE_ROWS, CONSOLE_COLUMNS = 50, 80
def no_color_str(text): def no_color_str(text):
return Suppress(escapeSeq).transformString(str(text)) return Suppress(escapeSeq).transformString(str(text))
def my_debug(*args, check_started=None):
"""
Write one line per arg in 'debug.txt'
:param args:
:param check_started:
True : first check if start_debug() was called
<name> : first check if start_debug(name) was called
list of <names> : first check if start_debug() is called for all names
:return:
"""
if check_started and default_debug_name not in debug_activated:
return
if isinstance(check_started, str) and check_started not in debug_activated:
return
if isinstance(check_started, list):
for debug_name in check_started:
if debug_name not in debug_activated:
return
with open("debug.txt", "a") as f:
for arg in args:
if isinstance(arg, list):
for item in arg:
f.write(f"{item}\n")
else:
f.write(f"{arg}\n")
def start_debug(debug_name=default_debug_name, msg=None):
debug_activated.add(debug_name)
if msg:
with open("debug.txt", "a") as f:
f.write(f"{msg}\n")
def stop_debug(debug_name=default_debug_name, msg=None):
if msg:
with open("debug.txt", "a") as f:
f.write(f"{msg}\n")
debug_activated.remove(debug_name)
def sysarg_to_string(argv): def sysarg_to_string(argv):
""" """
Transform a list of strings into a single string Transform a list of strings into a single string
@@ -606,13 +567,17 @@ def tokens_index(tokens, sub_tokens, skip=0):
raise ValueError(f"sub tokens '{sub_tokens}' not found") raise ValueError(f"sub tokens '{sub_tokens}' not found")
def as_bag(obj): def as_bag(obj, forced_properties=None):
""" """
Get the properties of an object (static and dynamic) Get the properties of an object (static and dynamic)
:param obj: :param obj:
:param forced_properties:
:return: :return:
""" """
if hasattr(obj, "as_bag"):
if forced_properties:
bag = {p: getattr(obj, p) for p in forced_properties}
elif hasattr(obj, "as_bag"):
bag = obj.as_bag() bag = obj.as_bag()
else: else:
bag = {} if type(obj) in PRIMITIVES_TYPES else {prop: getattr(obj, prop) bag = {} if type(obj) in PRIMITIVES_TYPES else {prop: getattr(obj, prop)
@@ -726,3 +691,77 @@ def dump_ast(node):
for to_remove in [", ctx=Load()", ", kind=None", ", type_ignores=[]"]: for to_remove in [", ctx=Load()", ", kind=None", ", type_ignores=[]"]:
dump = dump.replace(to_remove, "") dump = dump.replace(to_remove, "")
return dump return dump
def sheerka_deepcopy(obj):
"""
Internal implementation of deepcopy that can handle Concept circular references
:param obj:
:return:
"""
already_seen = {}
def copy_concept(c):
id_c = id(c)
if id_c in already_seen:
ref = already_seen[id_c]
if ref == '_##_REF_##_':
raise Exception("Circular Ref not managed yet!")
else:
return ref
already_seen[id_c] = '_##_REF_##_'
cls = type(c)
instance = cls()
# update the metadata
for prop_name, prop_value in vars(c.get_metadata()).items():
if prop_name != "props":
setattr(instance.get_metadata(), prop_name, prop_value)
else:
setattr(instance.get_metadata(), prop_name, sheerka_deepcopy(prop_value))
# update the values
for prop_name, prop_value in c.values().items():
setattr(instance, prop_name, prop_value)
already_seen[id_c] = instance
return instance
from core.concept import Concept
if isinstance(obj, dict):
res = {sheerka_deepcopy(k): sheerka_deepcopy(v) for k, v in obj.items()}
return res
elif isinstance(obj, list):
return [sheerka_deepcopy(item) for item in obj]
elif isinstance(obj, set):
return {sheerka_deepcopy(item) for item in obj}
elif isinstance(obj, tuple):
return tuple((sheerka_deepcopy(item) for item in obj))
elif isinstance(obj, Concept):
return copy_concept(obj)
else:
return deepcopy(obj)
def escape_str(x):
"""
Returns a string representation that look like what would produce a debugger
:param x:
:return:
"""
if isinstance(x, str):
return f"'{x}'"
return x
class NextIdManager:
"""
solely return the next integer
"""
def __init__(self):
self.id = -1
def get_next_id(self):
self.id += 1
return self.id
+6
View File
@@ -23,5 +23,11 @@ class AddToMemoryEvaluator(OneReturnValueEvaluator):
return len(context.sheerka.services[SheerkaMemory.NAME].registration) > 0 return len(context.sheerka.services[SheerkaMemory.NAME].registration) > 0
def eval(self, context, return_value): def eval(self, context, return_value):
if context.sheerka.during_initialisation:
from core.sheerka.services.SheerkaMemory import SheerkaMemory
service = context.sheerka.services[SheerkaMemory.NAME]
service.registration.clear()
return None
context.sheerka.add_registered_objects(context) context.sheerka.add_registered_objects(context)
return None # no need to have a second pass return None # no need to have a second pass
@@ -36,10 +36,10 @@ class MultipleSameSuccessEvaluator(AllReturnValuesEvaluator):
to_process = True to_process = True
self.eaten.append(ret) self.eaten.append(ret)
elif ret.who.startswith(BaseEvaluator.PREFIX): elif ret.who.startswith(BaseEvaluator.PREFIX):
self.eaten.append(ret)
if ret.status: if ret.status:
nb_successful_evaluators += 1 nb_successful_evaluators += 1
self.success.append(ret) self.success.append(ret)
self.eaten.append(ret)
elif ret.who.startswith(BaseParser.PREFIX): elif ret.who.startswith(BaseParser.PREFIX):
self.eaten.append(ret) self.eaten.append(ret)
if ret.status: if ret.status:
@@ -63,7 +63,7 @@ class MultipleSameSuccessEvaluator(AllReturnValuesEvaluator):
# I gave a random order to the other # I gave a random order to the other
# #
# I guess that we need a proper algorithm to elect which return value to use if they have the same result # I guess that we need a proper algorithm to elect which return value to use if they have the same result
# I guts feeling is that, it will depend on the intent of the user # My guts feeling is that, it will depend on the intent of the user
# So it depends on the context # So it depends on the context
# try to return a concept if possible # try to return a concept if possible
+22 -7
View File
@@ -1,16 +1,17 @@
import re import re
from core.sheerka.services.SheerkaRuleManager import FormatAstNode from core.sheerka.services.SheerkaRuleManager import FormatAstNode
from core.utils import CONSOLE_COLORS_MAP as CCM, no_color_str from core.utils import CONSOLE_COLORS_MAP as CCM, no_color_str, CONSOLE_COLUMNS
from out.OutVisitor import OutVisitor from out.OutVisitor import OutVisitor
get_start = re.compile(r"^([\(\{\[]\w*)") get_starting_brace_bracket_parent = re.compile(r"^([\(\{\[]\w*)")
class AsStrVisitor(OutVisitor): class AsStrVisitor(OutVisitor):
def __init__(self, expand=False): def __init__(self, expand=None, width=None):
self.expand = expand self.expand = expand
self.width = width
def visit_FormatAstRawText(self, format_ast): def visit_FormatAstRawText(self, format_ast):
return str(format_ast.text) return str(format_ast.text)
@@ -58,6 +59,7 @@ class AsStrVisitor(OutVisitor):
def visit_FormatAstDict(self, format_ast): def visit_FormatAstDict(self, format_ast):
first = True first = True
result = "" result = ""
expand = self.expand or False
keys_values = [] keys_values = []
max_len = 0 max_len = 0
@@ -71,23 +73,36 @@ class AsStrVisitor(OutVisitor):
if format_ast.prefix: if format_ast.prefix:
result += format_ast.prefix result += format_ast.prefix
sep = ",\n" if self.expand else ", " if format_ast.debug else "\n" sep = ",\n" if expand else ", " if format_ast.debug else "\n"
for i, (k, v) in enumerate(format_ast.items): for i, (k, v) in enumerate(format_ast.items):
start = "" if first else sep start = "" if first else sep
key = f"{keys_values[i]:<{max_len}}" if (self.expand or not format_ast.debug) else keys_values[i] key = f"{keys_values[i]:<{max_len}}" if (expand or not format_ast.debug) else keys_values[i]
colon = ": " colon = ": "
indent = len(no_color_str(key)) + len(colon) indent = len(no_color_str(key)) + len(colon)
value = self.visit(v) value = self.visit(v)
if self.expand: if self.debug_activated(v) and self.width and len(no_color_str(value)) >= self.width:
if m := get_start.match(no_color_str(value)): value = forced_expanded_visitor.visit(v)
expand = True
if expand:
if m := get_starting_brace_bracket_parent.match(no_color_str(value)):
indent += len(m.group(1)) indent += len(m.group(1))
value = value.replace("\n", "\n" + " " * indent) value = value.replace("\n", "\n" + " " * indent)
first = False first = False
result += start + key + colon + value result += start + key + colon + value
expand = self.expand or False # reset expand
if format_ast.suffix: if format_ast.suffix:
result += format_ast.suffix result += format_ast.suffix
return result return result
@staticmethod
def debug_activated(node):
return isinstance(node, FormatAstNode) and hasattr(node, "debug") and node.debug
forced_expanded_visitor = AsStrVisitor(expand=True, width=CONSOLE_COLUMNS)
+6 -4
View File
@@ -1,3 +1,4 @@
from core.utils import CONSOLE_COLUMNS
from out.AsStrVisitor import AsStrVisitor from out.AsStrVisitor import AsStrVisitor
@@ -6,7 +7,7 @@ class ConsoleVisitor(AsStrVisitor):
Prints to the console Prints to the console
""" """
def __init__(self, expand_mode="auto"): def __init__(self, expand_mode="auto", console_width=None):
""" """
expand_mode: expand_mode:
auto: not the first dict, the sub ones depends of the width auto: not the first dict, the sub ones depends of the width
@@ -18,6 +19,7 @@ class ConsoleVisitor(AsStrVisitor):
super().__init__() super().__init__()
self.out = print self.out = print
self.expand_mode = expand_mode self.expand_mode = expand_mode
self.console_width = console_width or CONSOLE_COLUMNS
def visit_FormatAstRawText(self, format_ast): def visit_FormatAstRawText(self, format_ast):
self.out(super().visit_FormatAstRawText(format_ast)) self.out(super().visit_FormatAstRawText(format_ast))
@@ -32,11 +34,11 @@ class ConsoleVisitor(AsStrVisitor):
self.out(super().visit_FormatAstColor(format_ast)) self.out(super().visit_FormatAstColor(format_ast))
def visit_FormatAstSequence(self, format_ast): def visit_FormatAstSequence(self, format_ast):
visitor = AsStrVisitor() visitor = AsStrVisitor(width=self.console_width)
self.out(visitor.visit_FormatAstSequence(format_ast)) self.out(visitor.visit_FormatAstSequence(format_ast))
def visit_FormatAstList(self, format_ast): def visit_FormatAstList(self, format_ast):
visitor = AsStrVisitor() visitor = AsStrVisitor(width=self.console_width)
res = visitor.visit_FormatAstList(format_ast) res = visitor.visit_FormatAstList(format_ast)
self.out(res) self.out(res)
@@ -46,6 +48,6 @@ class ConsoleVisitor(AsStrVisitor):
else: else:
expand = False expand = False
visitor = AsStrVisitor(expand) visitor = AsStrVisitor(expand, width=self.console_width)
res = visitor.visit_FormatAstDict(format_ast) res = visitor.visit_FormatAstDict(format_ast)
self.out(res) self.out(res)
+14 -4
View File
@@ -100,10 +100,20 @@ class DeveloperVisitor:
for i, item in enumerate(items): for i, item in enumerate(items):
bag["__item"] = item bag["__item"] = item
sub_visitor = DeveloperVisitor(self.sheerka_out, self.debugger, set(), self.list_recursion_depth) sub_visitor = DeveloperVisitor(self.sheerka_out, self.debugger, set(), self.list_recursion_depth)
result.append(sub_visitor.visit(context, FormatAstVariable("__item", to_visit = FormatAstDict("__item", debug=True, prefix='{', suffix='}') if isinstance(item, dict) else \
debug=format_ast.debug, FormatAstList("__item", debug=True, index=i, prefix='[', suffix=']') if isinstance(item, list) else \
value=item, FormatAstVariable("__item", value=item, index=i, debug=format_ast.debug)
index=i), bag)) result.append(sub_visitor.visit(context, to_visit, bag))
# if isinstance(item, list):
# format_ast_item = sub_visitor.visit(context,
# FormatAstList("__item", debug=format_ast.debug, index=i),
# bag)
# else:
# format_ast_item = sub_visitor.visit(context,
# FormatAstVariable("__item", debug=format_ast.debug, value=item,
# index=i),
# bag)
# result.append(format_ast_item)
# recursion management # recursion management
recursion_depth, recurse_on = self.get_recurse_info(item, recursion_depth, recurse_on) recursion_depth, recurse_on = self.get_recurse_info(item, recursion_depth, recurse_on)
+1 -1
View File
@@ -873,7 +873,7 @@ class BaseNodeParser(BaseParser):
if not to_keep(concept): if not to_keep(concept):
continue continue
concept = to_map(self, concept) if to_map else concept concept = to_map(concept, self, self.sheerka) if to_map else concept
result.append(concept) result.append(concept)
return core.utils.make_unique(result + custom_concepts, return core.utils.make_unique(result + custom_concepts,
+2
View File
@@ -1296,12 +1296,14 @@ class BnfNodeParser(BaseNodeParser):
if not concepts: if not concepts:
if debugger.is_enabled(): if debugger.is_enabled():
debugger.debug_log(debug_prefix + ", no concept found.") debugger.debug_log(debug_prefix + ", no concept found.")
for concept_parser in not_locked: for concept_parser in not_locked:
concept_parser.eat_unrecognized(token) concept_parser.eat_unrecognized(token)
continue continue
if debugger.is_enabled(): if debugger.is_enabled():
debugger.debug_log(debug_prefix + f", concept(s) found={concepts}") debugger.debug_log(debug_prefix + f", concept(s) found={concepts}")
if len(concepts) == 1: if len(concepts) == 1:
for concept_parser in not_locked: for concept_parser in not_locked:
concept_parser.eat_concept(concepts[0], token) concept_parser.eat_concept(concepts[0], token)
-3
View File
@@ -53,9 +53,6 @@ class PythonNode(Node):
self.compiled = compile(self.ast_, "<string>", "eval") self.compiled = compile(self.ast_, "<string>", "eval")
return self.compiled return self.compiled
# def __repr__(self):
# return "PythonNode(parser_input='" + self.parser_input + "', ast=" + self.get_dump(self.ast_) + ")"
def __repr__(self): def __repr__(self):
ast_type = "expr" if isinstance(self.ast_, ast.Expression) else "module" ast_type = "expr" if isinstance(self.ast_, ast.Expression) else "module"
return "PythonNode(" + ast_type + "='" + self.source + "')" return "PythonNode(" + ast_type + "='" + self.source + "')"
+144 -64
View File
@@ -11,7 +11,7 @@ from core.global_symbols import CONCEPT_COMPARISON_CONTEXT
from core.sheerka.services.SheerkaComparisonManager import SheerkaComparisonManager from core.sheerka.services.SheerkaComparisonManager import SheerkaComparisonManager
from core.sheerka.services.SheerkaExecute import ParserInput from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import Token, TokenKind, Tokenizer from core.tokenizer import Token, TokenKind, Tokenizer
from core.utils import get_n_clones from core.utils import get_n_clones, get_text_from_tokens, NextIdManager
from parsers.BaseNodeParser import UnrecognizedTokensNode, ConceptNode, SourceCodeNode, SyaAssociativity, \ from parsers.BaseNodeParser import UnrecognizedTokensNode, ConceptNode, SourceCodeNode, SyaAssociativity, \
SourceCodeWithConceptNode, BaseNodeParser SourceCodeWithConceptNode, BaseNodeParser
from parsers.BaseParser import ErrorNode from parsers.BaseParser import ErrorNode
@@ -25,6 +25,7 @@ DEBUG_PUSH_UNREC = "PUSH_UNREC"
DEBUG_POP = "POP" DEBUG_POP = "POP"
DEBUG_EAT = "EAT" DEBUG_EAT = "EAT"
DEBUG_RECOG = "RECOG" DEBUG_RECOG = "RECOG"
DEBUG_CAN_POP = "CAN_POP"
@dataclass() @dataclass()
@@ -42,12 +43,13 @@ class DebugInfo:
token: Token = None # current token token: Token = None # current token
concept: Concept = None # current concept if ay concept: Concept = None # current concept if ay
action: str = None # action taken action: str = None # action taken
level: str = None
def __repr__(self): def __repr__(self):
token_repr = self.token.repr_value if isinstance(self.token, Token) else self.token token_repr = self.token.repr_value if isinstance(self.token, Token) else self.token
msg = f"{self.pos:3}:{token_repr}" if self.pos != -1 else " _:" msg = f"{self.pos:3}:{token_repr}" if self.pos != -1 else " _:"
if self.concept: if self.concept:
msg += f"({self.concept})" msg += f" {self.concept.short_repr()}"
return msg + f" => {self.action}" return msg + f" => {self.action}"
@@ -118,6 +120,36 @@ class SyaConceptDef:
precedence: int = SheerkaComparisonManager.DEFAULT_COMPARISON_VALUE precedence: int = SheerkaComparisonManager.DEFAULT_COMPARISON_VALUE
associativity: SyaAssociativity = SyaAssociativity.Right associativity: SyaAssociativity = SyaAssociativity.Right
@staticmethod
def get_sya_concept_def(concept, parser, sheerka):
sya_concept_def = SyaConceptDef(concept)
# first, try to look in the parser
# it is where to find the data during the unit tests
if parser and concept.id in parser.sya_definitions:
# Manage when precedence and associativity are given in the unit tests
sya_def = parser.sya_definitions.get(concept.id)
if sya_def[0] is not None:
sya_concept_def.precedence = sya_def[0]
if sya_def[1] is not None:
sya_concept_def.associativity = sya_def[1]
# otherwise, use sheerka
if sheerka:
concept_weight = parser.sheerka.get_concepts_weights(BuiltinConcepts.PRECEDENCE, CONCEPT_COMPARISON_CONTEXT)
if concept.str_id in concept_weight:
sya_concept_def.precedence = concept_weight[concept.str_id]
# in the case of Sheerka, the associativity is managed by the concept itself
# There is no conflict with the settings of the unit test, as I don't use the props in the unit tests
if associativity := concept.get_prop(BuiltinConcepts.ASSOCIATIVITY):
sya_concept_def.associativity = SyaAssociativity(associativity)
return sya_concept_def
def short_repr(self):
return f"({self.concept}, prio={self.precedence}, assoc={self.associativity})"
@dataclass() @dataclass()
class SyaConceptParserHelper: class SyaConceptParserHelper:
@@ -248,9 +280,19 @@ class SyaConceptParserHelper:
class InFixToPostFix: class InFixToPostFix:
def __init__(self, context, debug_enabled=False): def __init__(self, context, next_id_manager, debugger=None):
self.context = context self.context = context
self.debug_enabled = debug_enabled
self.next_id_manager = next_id_manager
self.id = self.next_id_manager.get_next_id()
self.debugger = debugger
if debugger:
self.debug_enabled = debugger.is_enabled()
self.enabled_debug_levels = debugger.get_enabled_vars()
else:
self.debug_enabled = False
self.enabled_debug_levels = None
self.is_locked = False # when locked, cannot process input self.is_locked = False # when locked, cannot process input
@@ -284,9 +326,15 @@ class InFixToPostFix:
def _add_error(self, error): def _add_error(self, error):
if self.debug_enabled: if self.debug_enabled:
self.debug.append(DebugInfo(action=f"=> ERROR {error}")) self._add_debug(DebugInfo(action=f"=> ERROR {error}"))
self.errors.append(error) self.errors.append(error)
def _add_debug(self, debug_info: DebugInfo):
if debug_info.level is None or (self.enabled_debug_levels and
(f"#{self.id}.{debug_info.level}" in self.enabled_debug_levels or
"*" in self.enabled_debug_levels)):
self.debug.append(debug_info)
def _is_lpar(self, token): def _is_lpar(self, token):
""" """
True if the token is a left parenthesis '(' True if the token is a left parenthesis '('
@@ -337,10 +385,10 @@ class InFixToPostFix:
else: else:
item.error = f"token '{item.expected[0].strip_quote}' not found" item.error = f"token '{item.expected[0].strip_quote}' not found"
if self.debug_enabled: if self.debug_enabled:
self.debug.append(DebugInfo(action=f"ERROR {item.error}")) self._add_debug(DebugInfo(action=f"ERROR {item.error}"))
if self.debug_enabled: if self.debug_enabled:
self.debug.append(DebugInfo(action=f"{DEBUG_POP} {item}")) self._add_debug(DebugInfo(action=f"{DEBUG_POP} {item}"))
if isinstance(item, SyaConceptParserHelper) and item.potential_pos != -1: if isinstance(item, SyaConceptParserHelper) and item.potential_pos != -1:
self.out.insert(item.potential_pos, item) self.out.insert(item.potential_pos, item)
else: else:
@@ -402,6 +450,11 @@ class InFixToPostFix:
self.debug.pop() self.debug.pop()
def _debug_nodes(self, nodes_sequences): def _debug_nodes(self, nodes_sequences):
"""
Returns a debug representation of a sequence of LexerNodes
:param nodes_sequences:
:return:
"""
res = "[" res = "["
first = True first = True
for sequence in nodes_sequences: for sequence in nodes_sequences:
@@ -520,7 +573,7 @@ class InFixToPostFix:
# There are more than one solution found # There are more than one solution found
# In the case, we create a new InfixToPostfix for each new possibility # In the case, we create a new InfixToPostfix for each new possibility
if self.debug_enabled: if self.debug_enabled:
self.debug.append(DebugInfo(action=f"{DEBUG_RECOG} {self._debug_nodes(nodes_sequences)}")) self._add_debug(DebugInfo(action=f"{DEBUG_RECOG} {self._debug_nodes(nodes_sequences)}"))
if len(nodes_sequences) > 1: if len(nodes_sequences) > 1:
for node_sequence in nodes_sequences[1:]: for node_sequence in nodes_sequences[1:]:
clone = self.clone() clone = self.clone()
@@ -599,33 +652,52 @@ class InFixToPostFix:
self.stack.pop() self.stack.pop()
self._put_to_out(item) self._put_to_out(item)
def i_can_pop(self, concept_node): def i_can_pop(self, sya_parser_helper):
""" """
Validate the Shunting Yard Algorithm conditions to pop out from the stack Validate the Shunting Yard Algorithm conditions to pop out from the stack
Note that it's a custom implementation as I need to manage UnrecognizedTokensNode Note that it's a custom implementation as I need to manage UnrecognizedTokensNode
:param concept_node: :param sya_parser_helper:
:return: :return:
""" """
if len(self.stack) == 0: if len(self.stack) == 0:
if self.debug_enabled:
self._add_debug(DebugInfo(action=f"No stack. {DEBUG_CAN_POP} false.", level="can_pop"))
return False return False
stack_head = self.stack[-1] stack_head = self.stack[-1]
if not isinstance(stack_head, SyaConceptParserHelper): # mostly left parenthesis if not isinstance(stack_head, SyaConceptParserHelper): # mostly left parenthesis
if self.debug_enabled:
self._add_debug(DebugInfo(action=f"No concept. {DEBUG_CAN_POP} false.", level="can_pop"))
return False return False
current = concept_node.concept current = sya_parser_helper.concept
stack = stack_head.concept stack = stack_head.concept
if stack.associativity == SyaAssociativity.No and current.associativity == SyaAssociativity.No: if stack.associativity == SyaAssociativity.No and current.associativity == SyaAssociativity.No:
self._add_error(NoneAssociativeSequenceErrorNode(current.concept, stack_head.start, concept_node.start)) self._add_error(
NoneAssociativeSequenceErrorNode(current.concept, stack_head.start, sya_parser_helper.start))
if current.associativity == SyaAssociativity.Left and current.precedence <= stack.precedence: if current.associativity == SyaAssociativity.Left and current.precedence <= stack.precedence:
if self.debug_enabled:
current_debug = f"{current.concept.id}({current.precedence})"
stack_debug = f"{stack.concept.id}({stack.precedence})"
self._add_debug(
DebugInfo(action=f"assoc=Left and {current_debug} <= {stack_debug}. {DEBUG_CAN_POP} True.",
level="can_pop"))
return True return True
if current.associativity == SyaAssociativity.Right and current.precedence < stack.precedence: if current.associativity == SyaAssociativity.Right and current.precedence < stack.precedence:
if self.debug_enabled:
current_debug = f"{current.concept.id}({current.precedence})"
stack_debug = f"{stack.concept.id}({stack.precedence})"
self._add_debug(
DebugInfo(action=f"assoc=Right and {current_debug} < {stack_debug}. {DEBUG_CAN_POP} True.",
level="can_pop"))
return True return True
if self.debug_enabled:
self._add_debug(DebugInfo(action=f"No rule. {DEBUG_CAN_POP} False.", level="can_pop"))
return False return False
def handle_expected_token(self, token, pos): def handle_expected_token(self, token, pos):
@@ -693,7 +765,7 @@ class InFixToPostFix:
current_concept.end = pos current_concept.end = pos
if self.debug_enabled: if self.debug_enabled:
self.debug.append(DebugInfo(pos, token, None, "??")) self._add_debug(DebugInfo(pos, token, None, "??"))
self.manage_unrecognized() self.manage_unrecognized()
# manage that some clones may have been forked # manage that some clones may have been forked
for forked in self.forked: for forked in self.forked:
@@ -755,7 +827,7 @@ class InFixToPostFix:
if self.parsing_function: if self.parsing_function:
if self.debug_enabled: if self.debug_enabled:
self.debug.append(DebugInfo(pos, token, None, DEBUG_PUSH_UNREC)) self._add_debug(DebugInfo(pos, token, None, DEBUG_PUSH_UNREC))
self.unrecognized_tokens.add_token(token, pos) self.unrecognized_tokens.add_token(token, pos)
@@ -790,13 +862,13 @@ class InFixToPostFix:
# if the token 'bar' is found, it has to be considered as part of the concept foo # if the token 'bar' is found, it has to be considered as part of the concept foo
if self.debug_enabled: if self.debug_enabled:
self._remove_debug_info_if_needed() self._remove_debug_info_if_needed()
self.debug.append(DebugInfo(pos, token, None, DEBUG_EAT)) self._add_debug(DebugInfo(pos, token, None, DEBUG_EAT))
return True return True
elif self._is_lpar(token): elif self._is_lpar(token):
if self.debug_enabled: if self.debug_enabled:
self.debug.append(DebugInfo(pos, token, None, DEBUG_PUSH_UNREC)) self._add_debug(DebugInfo(pos, token, None, DEBUG_PUSH_UNREC))
if self.unrecognized_tokens.is_empty() or self.unrecognized_tokens.is_whitespace(): if self.unrecognized_tokens.is_empty() or self.unrecognized_tokens.is_whitespace():
@@ -865,7 +937,7 @@ class InFixToPostFix:
elif self._is_rpar(token): elif self._is_rpar(token):
if self.debug_enabled: if self.debug_enabled:
self.debug.append(DebugInfo(pos, token, None, DEBUG_EAT)) self._add_debug(DebugInfo(pos, token, None, DEBUG_EAT))
# first, remove what was in the buffer # first, remove what was in the buffer
self.manage_unrecognized() self.manage_unrecognized()
@@ -933,7 +1005,7 @@ class InFixToPostFix:
if first_pass: if first_pass:
if self.debug_enabled: if self.debug_enabled:
self.debug.append(DebugInfo(pos, token, sya_concept_def, "??")) self._add_debug(DebugInfo(pos, token, sya_concept_def, "??"))
if self.unrecognized_tokens.last_token_type() == TokenKind.WHITESPACE: if self.unrecognized_tokens.last_token_type() == TokenKind.WHITESPACE:
parser_helper.remember_whitespace = self.unrecognized_tokens.tokens[-1] parser_helper.remember_whitespace = self.unrecognized_tokens.tokens[-1]
@@ -970,7 +1042,7 @@ class InFixToPostFix:
else: else:
if self.debug_enabled: if self.debug_enabled:
self._remove_debug_info_if_needed() self._remove_debug_info_if_needed()
self.debug.append(DebugInfo(pos, token, sya_concept_def, DEBUG_PUSH)) self._add_debug(DebugInfo(pos, token, sya_concept_def, DEBUG_PUSH))
self.stack.append(parser_helper) self.stack.append(parser_helper)
self.manage_parameters_when_new_concept(parser_helper) self.manage_parameters_when_new_concept(parser_helper)
@@ -985,7 +1057,7 @@ class InFixToPostFix:
return return
if self.debug_enabled: if self.debug_enabled:
self.debug.append(DebugInfo(pos, token, None, DEBUG_PUSH_UNREC)) self._add_debug(DebugInfo(pos, token, None, DEBUG_PUSH_UNREC))
self.unrecognized_tokens.add_token(token, pos) self.unrecognized_tokens.add_token(token, pos)
@@ -1005,7 +1077,7 @@ class InFixToPostFix:
return # no need to pop the buffer, as no concept is found return # no need to pop the buffer, as no concept is found
if self.debug_enabled: if self.debug_enabled:
self.debug.append(DebugInfo(pos, "<EOF>", None, "??")) self._add_debug(DebugInfo(pos, "<EOF>", None, "??"))
while len(self.stack) > 0: while len(self.stack) > 0:
parser_helper = self.stack[-1] parser_helper = self.stack[-1]
@@ -1036,7 +1108,7 @@ class InFixToPostFix:
forked.finalize(pos) forked.finalize(pos)
def clone(self): def clone(self):
clone = InFixToPostFix(self.context, self.debug_enabled) clone = InFixToPostFix(self.context, self.next_id_manager, self.debugger)
clone.is_locked = self.is_locked clone.is_locked = self.is_locked
clone.out = self.out[:] clone.out = self.out[:]
clone.stack = [i.clone() if hasattr(i, "clone") else i for i in self.stack] clone.stack = [i.clone() if hasattr(i, "clone") else i for i in self.stack]
@@ -1054,6 +1126,7 @@ class PostFixToItem:
start: int start: int
end: int end: int
has_unrecognized: bool has_unrecognized: bool
source: str
class SyaNodeParser(BaseNodeParser): class SyaNodeParser(BaseNodeParser):
@@ -1069,14 +1142,6 @@ class SyaNodeParser(BaseNodeParser):
self.concepts_by_first_keyword = {} self.concepts_by_first_keyword = {}
self.sya_definitions = {} self.sya_definitions = {}
# self.token = None
# self.pos = -1
# self.tokens = None
#
# self.context: ExecutionContext = None
# self.text = None
# self.sheerka = None
def init_from_concepts(self, context, concepts, **kwargs): def init_from_concepts(self, context, concepts, **kwargs):
super().init_from_concepts(context, concepts) super().init_from_concepts(context, concepts)
@@ -1093,27 +1158,8 @@ class SyaNodeParser(BaseNodeParser):
""" """
# We only concepts that has parameter (refuse atoms) # We only concepts that has parameter (refuse atoms)
# Bnf definitions are not supposed to be managed by this parser either # Bnf definitions are not supposed to be managed by this parser either
return len(concept.get_metadata().variables) > 0 and concept.get_metadata().definition_type != DEFINITION_TYPE_BNF return len(
concept.get_metadata().variables) > 0 and concept.get_metadata().definition_type != DEFINITION_TYPE_BNF
def _get_sya_concept_def(self, parser, concept):
sya_concept_def = SyaConceptDef(concept)
if concept.id in parser.sya_definitions:
# Manage when precedence and associativity are given in the unit tests
sya_def = parser.sya_definitions.get(concept.id)
if sya_def[0] is not None:
sya_concept_def.precedence = sya_def[0]
if sya_def[1] is not None:
sya_concept_def.associativity = sya_def[1]
if parser.sheerka:
concept_weight = parser.sheerka.get_concepts_weights(BuiltinConcepts.PRECEDENCE, CONCEPT_COMPARISON_CONTEXT)
if concept.str_id in concept_weight:
sya_concept_def.precedence = concept_weight[concept.str_id]
if associativity := concept.get_prop(BuiltinConcepts.ASSOCIATIVITY):
sya_concept_def.associativity = SyaAssociativity(associativity)
return sya_concept_def
def infix_to_postfix(self, context, parser_input: ParserInput): def infix_to_postfix(self, context, parser_input: ParserInput):
""" """
@@ -1126,6 +1172,9 @@ class SyaNodeParser(BaseNodeParser):
if not self.reset_parser(context, parser_input): if not self.reset_parser(context, parser_input):
return None return None
debugger = context.get_debugger(self.NAME, "parse")
debugger.debug_entering(source=self.parser_input.as_text())
forked = [] forked = []
def _add_forked_to_res(): def _add_forked_to_res():
@@ -1138,16 +1187,21 @@ class SyaNodeParser(BaseNodeParser):
res.extend(forked) res.extend(forked)
forked.clear() forked.clear()
res = [InFixToPostFix(context, context.debug_enabled)] res = [InFixToPostFix(context, NextIdManager(), debugger)]
while self.parser_input.next_token(False): while self.parser_input.next_token(False):
for infix_to_postfix in res: for infix_to_postfix in res:
infix_to_postfix.reset() infix_to_postfix.reset()
token = self.parser_input.token token = self.parser_input.token
if debugger.is_enabled():
debug_prefix = f"pos={self.parser_input.pos}, {token=}, {len(res)} parser(s)"
try: try:
if token.type in (TokenKind.LPAR, TokenKind.RPAR): if token.type in (TokenKind.LPAR, TokenKind.RPAR):
# little optim, no need to lock, unlock or get the concept when parenthesis # little optim, no need to lock, unlock or get the concept when parenthesis
if debugger.is_enabled():
debugger.debug_log(debug_prefix + ", eat token.")
for infix_to_postfix in res: for infix_to_postfix in res:
infix_to_postfix.eat_token(token, self.parser_input.pos) infix_to_postfix.eat_token(token, self.parser_input.pos)
continue continue
@@ -1156,21 +1210,34 @@ class SyaNodeParser(BaseNodeParser):
if infix_to_postfix.eat_token(token, self.parser_input.pos): if infix_to_postfix.eat_token(token, self.parser_input.pos):
infix_to_postfix.lock() infix_to_postfix.lock()
concepts = self.get_concepts(token, self._is_eligible, to_map=self._get_sya_concept_def) nb_locked = len([itp for itp in res if itp.is_locked])
if not concepts: if nb_locked == len(res):
if debugger.is_enabled():
debugger.debug_log(debug_prefix + f", all parsers are locked")
continue
concepts_def = self.get_concepts(token, self._is_eligible, to_map=SyaConceptDef.get_sya_concept_def)
if not concepts_def:
if debugger.is_enabled():
debugger.debug_log(debug_prefix + f", no concept found")
for infix_to_postfix in res: for infix_to_postfix in res:
infix_to_postfix.eat_unrecognized(token, self.parser_input.pos) infix_to_postfix.eat_unrecognized(token, self.parser_input.pos)
continue continue
if len(concepts) == 1: if debugger.is_enabled():
found = [cd.short_repr() for cd in concepts_def]
debugger.debug_log(debug_prefix + f", concept(s) found={found}")
if len(concepts_def) == 1:
for infix_to_postfix in res: for infix_to_postfix in res:
infix_to_postfix.eat_concept(concepts[0], token, self.parser_input.pos) infix_to_postfix.eat_concept(concepts_def[0], token, self.parser_input.pos)
continue continue
# make the cartesian product # make the cartesian product
temp_res = [] temp_res = []
for infix_to_postfix in res: for infix_to_postfix in res:
for concept in concepts: for concept in concepts_def:
clone = infix_to_postfix.clone() clone = infix_to_postfix.clone()
temp_res.append(clone) temp_res.append(clone)
clone.eat_concept(concept, token, self.parser_input.pos) clone.eat_concept(concept, token, self.parser_input.pos)
@@ -1185,13 +1252,13 @@ class SyaNodeParser(BaseNodeParser):
infix_to_postfix.finalize(self.parser_input.pos) infix_to_postfix.finalize(self.parser_input.pos)
_add_forked_to_res() _add_forked_to_res()
if context.debug_enabled: if debugger.is_enabled():
context.debug(self.name, "infix_to_postfix", None, f"Parsing {parser_input}") for r in res:
context.debug(self.name, "infix_to_postfix", "nb_found", f"{len(res)} InfixToPostFix(s) found")
for i, r in enumerate(res):
context.debug(self.name, "infix_to_postfix", "infix_to_postfix", f"#{i}")
for line in r.debug: for line in r.debug:
context.debug(self.name, "infix_to_postfix", "infix_to_postfix", line) if line.level:
debugger.debug_var(f"#{r.id}.{line.level}", line)
else:
debugger.debug_var(f"#{r.id}", line)
return res return res
@@ -1222,6 +1289,7 @@ class SyaNodeParser(BaseNodeParser):
end = item.end end = item.end
has_unrecognized = False has_unrecognized = False
concept = sheerka.new_from_template(item.concept, item.concept.key) concept = sheerka.new_from_template(item.concept, item.concept.key)
concept_metadata = []
for param_index in reversed(range(len(concept.get_metadata().variables))): for param_index in reversed(range(len(concept.get_metadata().variables))):
inner_item = self.postfix_to_item(sheerka, postfixed) inner_item = self.postfix_to_item(sheerka, postfixed)
if inner_item.start < start: if inner_item.start < start:
@@ -1237,8 +1305,20 @@ class SyaNodeParser(BaseNodeParser):
inner_item inner_item
concept.get_compiled()[param_name] = param_value concept.get_compiled()[param_name] = param_value
concept_metadata.append((param_name, inner_item.source))
return PostFixToItem(concept, start, end, has_unrecognized) # update the metadata
concept_metadata.reverse()
# ---- Sanity check. To remove at some point
assert len(concept_metadata) == len(concept.get_metadata().variables)
for meta_orig, meta_new in zip(concept.get_metadata().variables, concept_metadata):
assert meta_orig[0] == meta_new[0]
# ---- Sanity check. To remove at some point
concept.get_metadata().variables = concept_metadata
source = get_text_from_tokens(self.parser_input.tokens[start:end + 1])
return PostFixToItem(concept, start, end, has_unrecognized, source)
def parse(self, context, parser_input: ParserInput): def parse(self, context, parser_input: ParserInput):
""" """
+3
View File
@@ -232,6 +232,7 @@ class SheerkaDataProvider:
""" """
with self.lock: with self.lock:
self.log.debug(f"getting {entry=}, {key=}, {default=}, {load_origin=}")
if entry not in self.state.data: if entry not in self.state.data:
return default return default
@@ -464,6 +465,8 @@ class SheerkaDataProvider:
def load_ref_if_needed_ex(self, item, load_origin): def load_ref_if_needed_ex(self, item, load_origin):
""" """
New version of the function.
The old one must be replaced at some point
Make sure we return the real object, even inside a collection Make sure we return the real object, even inside a collection
:param item: :param item:
:param load_origin: :param load_origin:
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -109,6 +109,7 @@ class Serializer:
raise TypeError(f"Don't know how serializer name={header[0]}, version={header[1]}") raise TypeError(f"Don't know how serializer name={header[0]}, version={header[1]}")
serializer = serializers[0] serializer = serializers[0]
self.log.debug(f"deserializing using '{serializer}'")
return serializer.load(stream, context) return serializer.load(stream, context)
@@ -298,7 +299,7 @@ class MemoryObjectSerializer(SheerkaPickleSerializer):
CLASS_NAME = "core.sheerka.services.SheerkaMemory.MemoryObject" CLASS_NAME = "core.sheerka.services.SheerkaMemory.MemoryObject"
def __init__(self): def __init__(self):
super().__init__(lambda obj: get_full_qualified_name(obj) == self.CLASS_NAME, "R", 1) super().__init__(lambda obj: get_full_qualified_name(obj) == self.CLASS_NAME, "M", 1)
class RuleSerializer(SheerkaPickleSerializer): class RuleSerializer(SheerkaPickleSerializer):
+5 -1
View File
@@ -4,11 +4,15 @@ from logging import Logger
import core.utils import core.utils
from core.concept import Concept, NotInitialized from core.concept import Concept, NotInitialized
from core.sheerka.services.SheerkaExecute import ParserInput from core.sheerka.services.SheerkaExecute import ParserInput
from core.simple_debug import my_debug
from sheerkapickle import utils, tags, handlers from sheerkapickle import utils, tags, handlers
def encode(sheerka, obj): def encode(sheerka, obj):
return json.dumps(SheerkaPickler(sheerka).flatten(obj)) pickler = SheerkaPickler(sheerka)
flatten = pickler.flatten(obj)
my_debug(f"{obj} ids={len(pickler.ids)}, objs={len(pickler.objs)}")
return json.dumps(flatten)
class ToReduce: class ToReduce:
+56 -56
View File
@@ -7,59 +7,59 @@ class TestSheerkaAdmin(TestUsingMemoryBasedSheerka):
def test_i_can_get_last_ret(self): def test_i_can_get_last_ret(self):
pass pass
def test_i_can_get_last_error_ret(self): # def test_i_can_get_last_error_ret(self):
sheerka, context = self.init_concepts() # sheerka, context = self.init_concepts()
#
# nothing in history # # nothing in history
last_error_ret = sheerka.last_error_ret(context) # last_error_ret = sheerka.last_error_ret(context)
assert sheerka.isinstance(last_error_ret, BuiltinConcepts.RETURN_VALUE) # assert sheerka.isinstance(last_error_ret, BuiltinConcepts.RETURN_VALUE)
assert not last_error_ret.status # assert not last_error_ret.status
assert sheerka.isinstance(last_error_ret.body, BuiltinConcepts.NOT_FOUND) # assert sheerka.isinstance(last_error_ret.body, BuiltinConcepts.NOT_FOUND)
#
# only success in history # # only success in history
r_success = sheerka.ret("Test", True, "success") # r_success = sheerka.ret("Test", True, "success")
sheerka.last_return_values.append([r_success]) # sheerka.last_return_values.append([r_success])
last_error_ret = sheerka.last_error_ret(context) # last_error_ret = sheerka.last_error_ret(context)
assert sheerka.isinstance(last_error_ret, BuiltinConcepts.RETURN_VALUE) # assert sheerka.isinstance(last_error_ret, BuiltinConcepts.RETURN_VALUE)
assert not last_error_ret.status # assert not last_error_ret.status
assert sheerka.isinstance(last_error_ret.body, BuiltinConcepts.NOT_FOUND) # assert sheerka.isinstance(last_error_ret.body, BuiltinConcepts.NOT_FOUND)
#
# at least on error # # at least on error
r_failure = sheerka.ret("Test", False, "failure") # r_failure = sheerka.ret("Test", False, "failure")
sheerka.last_return_values.append([r_failure]) # sheerka.last_return_values.append([r_failure])
assert sheerka.last_error_ret(context) == r_failure # assert sheerka.last_error_ret(context) == r_failure
#
# add another success and make sure we get the same error # # add another success and make sure we get the same error
sheerka.last_return_values.append([r_success]) # sheerka.last_return_values.append([r_success])
assert sheerka.last_error_ret(context) == r_failure # assert sheerka.last_error_ret(context) == r_failure
#
# and I only get the last failure # # and I only get the last failure
r_failure2 = sheerka.ret("Test", False, "another failure") # r_failure2 = sheerka.ret("Test", False, "another failure")
sheerka.last_return_values.append([r_failure2]) # sheerka.last_return_values.append([r_failure2])
assert sheerka.last_error_ret(context) == r_failure2 # assert sheerka.last_error_ret(context) == r_failure2
#
# but I still can get the previous error # # but I still can get the previous error
assert sheerka.last_error_ret(context, -2) == r_failure # assert sheerka.last_error_ret(context, -2) == r_failure
#
def test_i_can_get_last_error_ret_when_only_one_ret_has_failed(self): # def test_i_can_get_last_error_ret_when_only_one_ret_has_failed(self):
sheerka, context = self.init_concepts() # sheerka, context = self.init_concepts()
#
r_success = sheerka.ret("Test", True, "success") # r_success = sheerka.ret("Test", True, "success")
r_failure = sheerka.ret("Test", False, "False1") # r_failure = sheerka.ret("Test", False, "False1")
#
sheerka.last_return_values.append([r_success, r_failure]) # sheerka.last_return_values.append([r_success, r_failure])
assert sheerka.last_error_ret(context) == r_failure # assert sheerka.last_error_ret(context) == r_failure
#
def test_i_cannot_get_last_error_ret_when_too_many_errors(self): # def test_i_cannot_get_last_error_ret_when_too_many_errors(self):
sheerka, context = self.init_concepts() # sheerka, context = self.init_concepts()
#
r1 = sheerka.ret("Test", True, "success") # r1 = sheerka.ret("Test", True, "success")
r2 = sheerka.ret("Test", False, "False1") # r2 = sheerka.ret("Test", False, "False1")
r3 = sheerka.ret("Test", False, "False2") # r3 = sheerka.ret("Test", False, "False2")
#
sheerka.last_return_values.append([r1, r2, r3]) # sheerka.last_return_values.append([r1, r2, r3])
last_error_ret = sheerka.last_error_ret(context) # last_error_ret = sheerka.last_error_ret(context)
assert sheerka.isinstance(last_error_ret, BuiltinConcepts.RETURN_VALUE) # assert sheerka.isinstance(last_error_ret, BuiltinConcepts.RETURN_VALUE)
assert not last_error_ret.status # assert not last_error_ret.status
assert sheerka.isinstance(last_error_ret.body, BuiltinConcepts.TOO_MANY_ERRORS) # assert sheerka.isinstance(last_error_ret.body, BuiltinConcepts.TOO_MANY_ERRORS)
assert last_error_ret.body.body == [r2, r3] # assert last_error_ret.body.body == [r2, r3]
+3 -3
View File
@@ -1,7 +1,7 @@
import pytest import pytest
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept from core.concept import Concept
from core.global_symbols import CONCEPT_PRECEDENCE_MODIFIED, CONCEPT_COMPARISON_CONTEXT, RULE_PRECEDENCE_MODIFIED, \ from core.global_symbols import EVENT_CONCEPT_PRECEDENCE_MODIFIED, CONCEPT_COMPARISON_CONTEXT, EVENT_RULE_PRECEDENCE_MODIFIED, \
RULE_COMPARISON_CONTEXT RULE_COMPARISON_CONTEXT
from core.sheerka.services.SheerkaComparisonManager import SheerkaComparisonManager, ComparisonObj from core.sheerka.services.SheerkaComparisonManager import SheerkaComparisonManager, ComparisonObj
@@ -446,7 +446,7 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
nonlocal event_received nonlocal event_received
event_received = True event_received = True
sheerka.subscribe(CONCEPT_PRECEDENCE_MODIFIED, receive_event) sheerka.subscribe(EVENT_CONCEPT_PRECEDENCE_MODIFIED, receive_event)
sheerka.set_is_greater_than(context, foo, one, two) sheerka.set_is_greater_than(context, foo, one, two)
assert not event_received assert not event_received
@@ -471,7 +471,7 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
nonlocal event_received nonlocal event_received
event_received = True event_received = True
sheerka.subscribe(RULE_PRECEDENCE_MODIFIED, receive_event) sheerka.subscribe(EVENT_RULE_PRECEDENCE_MODIFIED, receive_event)
sheerka.set_is_greater_than(context, foo, r1, r2) sheerka.set_is_greater_than(context, foo, r1, r2)
assert not event_received assert not event_received
+347 -1
View File
@@ -1,12 +1,23 @@
import pytest import pytest
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept, NotInit
from core.sheerka.ExecutionContext import ExecutionContext from core.sheerka.ExecutionContext import ExecutionContext
from core.sheerka.services.SheerkaDebugManager import SheerkaDebugManager, DebugItem from core.sheerka.services.SheerkaDebugManager import SheerkaDebugManager, DebugItem, ConceptDebugObj
from parsers.PythonParser import PythonNode
from sdp.sheerkaDataProvider import Event from sdp.sheerkaDataProvider import Event
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class DummyObj:
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return f"DummyObj(a={self.a}, b={self.b})"
class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka): class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
def test_i_can_activate_debug(self): def test_i_can_activate_debug(self):
@@ -478,12 +489,85 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
assert len(service.debug_rules_settings) == 0 assert len(service.debug_rules_settings) == 0
assert len(service.debug_concepts_settings) == 0 assert len(service.debug_concepts_settings) == 0
@pytest.mark.parametrize("settings, expected", [
({"service": "my_service", "item": "var_name"}, {"var_name"}),
({"method": "my_method", "item": "var_name"}, {"var_name"}),
({"debug_id": 0, "item": "var_name"}, {"var_name"}),
({"service": "my_service", "item": "*"}, {"*"}),
({"method": "my_method", "item": "*"}, {"*"}),
({"debug_id": 0, "item": "*"}, {"*"}),
({"service": "my_service"}, set()),
({"method": "my_method"}, set()),
({"debug_id": 0}, set()),
])
def test_i_can_get_enabled_items(self, settings, expected):
sheerka, context = self.init_concepts()
service = sheerka.services[SheerkaDebugManager.NAME]
service.set_debug(context, True)
service.add_or_update_debug_item(context, "vars", **settings)
assert service.get_enabled_items("vars", context, "my_service", "my_method", 0) == expected
def test_i_can_get_enabled_items_for_context(self):
sheerka, context = self.init_concepts()
another_context = context.push(BuiltinConcepts.TESTING, None)
service = sheerka.services[SheerkaDebugManager.NAME]
service.set_debug(context, True)
service.add_or_update_debug_item(context, "rules", context_id=context.id, item="item", enabled=True)
assert service.get_enabled_items("rules", context, "my_service", "my_method", 10) == {"item"}
assert service.get_enabled_items("rules", another_context, "my_service", "my_method", 10) == set()
def test_i_can_get_enabled_items_for_sub_context(self):
sheerka, context = self.init_concepts()
sub_context = context.push(BuiltinConcepts.TESTING, None)
another_context = self.get_context(sheerka)
service = sheerka.services[SheerkaDebugManager.NAME]
service.set_debug(context, True)
service.add_or_update_debug_item(context, "rules", context_id=context.id, item="item", context_children=True)
assert service.get_enabled_items("rules", context, "my_service", "my_method", 10) == {"item"}
assert service.get_enabled_items("rules", sub_context, "my_service", "my_method", 10) == {"item"}
assert service.get_enabled_items("rules", another_context, "my_service", "my_method", 10) == set()
def test_i_can_get_all_enabled_items(self):
sheerka, context = self.init_concepts()
service = sheerka.services[SheerkaDebugManager.NAME]
service.set_debug(context, True)
service.add_or_update_debug_item(context, "vars", service="s1", item="v11")
service.add_or_update_debug_item(context, "vars", service="s2", item="v21")
service.add_or_update_debug_item(context, "vars", method="m1", item="v12")
service.add_or_update_debug_item(context, "vars", method="m2", item="v22")
service.add_or_update_debug_item(context, "vars", debug_id=1, item="v13")
service.add_or_update_debug_item(context, "vars", debug_id=2, item="v23")
service.add_or_update_debug_item(context, "vars", service="s1", method="m1", item="v111")
assert service.get_enabled_items("vars", context, "s1", "m1", 1) == {"v11", "v12", "v13", "v111"}
def test_i_can_manage_duplicates_when_get_enabled_items(self):
sheerka, context = self.init_concepts()
service = sheerka.services[SheerkaDebugManager.NAME]
service.set_debug(context, True)
service.add_or_update_debug_item(context, "vars", service="s1", item="var_name")
service.add_or_update_debug_item(context, "vars", method="m1", item="*")
service.add_or_update_debug_item(context, "vars", debug_id=1, item="var_name")
service.add_or_update_debug_item(context, "vars", service="s1", method="m1", item="*")
assert service.get_enabled_items("vars", context, "s1", "m1", 1) == {"var_name", "*"}
@pytest.mark.parametrize("args, kwargs, expected", [ @pytest.mark.parametrize("args, kwargs, expected", [
(["my_service.my_method.my_var"], {}, ("my_var", "my_service", "my_method", None, False, None, True)), (["my_service.my_method.my_var"], {}, ("my_var", "my_service", "my_method", None, False, None, True)),
(["*.*.my_var"], {}, ("my_var", None, None, None, False, None, True)), (["*.*.my_var"], {}, ("my_var", None, None, None, False, None, True)),
(["my_service"], {}, (None, "my_service", None, None, False, None, True)), (["my_service"], {}, (None, "my_service", None, None, False, None, True)),
(["my_service.my_method"], {}, (None, "my_service", "my_method", None, False, None, True)), (["my_service.my_method"], {}, (None, "my_service", "my_method", None, False, None, True)),
(["*.*.*"], {}, ("*", None, None, None, False, None, True)), (["*.*.*"], {}, ("*", None, None, None, False, None, True)),
(["*.*.*.*.*"], {}, ("*.*.*", None, None, None, False, None, True)),
(["s.m.var.in.multi.parts"], {}, ("var.in.multi.parts", "s", "m", None, False, None, True)),
([1], {}, ("1", None, None, None, False, None, True)), ([1], {}, ("1", None, None, None, False, None, True)),
(["", 1], {}, (None, None, None, 1, False, None, True)), (["", 1], {}, (None, None, None, 1, False, None, True)),
([None, 1], {}, (None, None, None, 1, False, None, True)), ([None, 1], {}, (None, None, None, 1, False, None, True)),
@@ -566,3 +650,265 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
DebugItem('1', None, None, None, False, None, False, True)] DebugItem('1', None, None, None, False, None, False, True)]
assert another_service.debug_concepts_settings == [ assert another_service.debug_concepts_settings == [
DebugItem('1001', None, None, None, False, None, False, True)] DebugItem('1001', None, None, None, False, None, False, True)]
def test_i_can_inspect_concept_all_attributes(self):
sheerka, context, foo = self.init_concepts("foo")
foo.values() # freeze known attributes
foo.set_value("new_var", "var_value")
res = sheerka.inspect(context, foo)
assert res.body == {"#type#": "Concept",
"id": foo.id,
"name": foo.name,
"key": foo.key,
"value.new_var": "'var_value'"}
def test_i_can_inspect_concept_specified_attributes(self):
sheerka, context, foo = self.init_concepts("foo")
foo.values() # freeze known attributes
foo.set_value("new_var", "var_value")
res = sheerka.inspect(context, foo, "id", "value.new_var")
assert res.body == {"id": foo.id, "value.new_var": "'var_value'"}
def test_i_can_inspect_concept_specified_attributes_using_short_name(self):
sheerka, context, foo, bar = self.init_concepts(Concept("foo").def_var("var_name", "default_value"), "bar")
foo.set_value("var_name", "var_value")
foo.get_compiled()["var_name"] = bar
res = sheerka.inspect(context, foo, "id", "var_name")
assert res.body == {"id": foo.id,
"meta.var_name": "'default_value'",
"compiled.var_name": bar,
"value.var_name": "'var_value'"}
def test_i_can_inspect_object_all_attributes(self):
sheerka, context = self.init_concepts()
python_node = PythonNode("one + 1").init_ast()
res = sheerka.inspect(context, python_node)
assert set(res.body.keys()) == {"#type#", 'ast_', 'ast_str', 'compiled', 'objects', 'source'}
def test_i_can_inspect_object_specified_attributes(self):
sheerka, context = self.init_concepts()
python_node = PythonNode("one + 1").init_ast()
res = sheerka.inspect(context, python_node, "#type#", "source", "ast_str")
assert res.body == {
"#type#": "PythonNode",
'ast_str': "Expression(body=BinOp(left=Name(id='one'), op=Add(), right=Constant(value=1)))",
'source': 'one + 1'}
def test_i_can_inspect_object_with_as_bag_all_attributes(self):
sheerka, context = self.init_concepts()
res = sheerka.inspect(context, context)
assert res.body == {'#type#': 'ExecutionContext',
'_children': [],
'action': '__TESTING',
'concepts': None,
'context': None,
'desc': None,
'digest': 'xxx',
'elapsed': 0,
'elapsed_str': '0.0 ms',
'id': context.id,
'inputs': {},
'obj': None,
'status': None,
'values': {},
'who': 'test'}
def test_i_can_inspect_object_with_as_bag_specified_attributes(self):
sheerka, context = self.init_concepts()
res = sheerka.inspect(context, context, "who", "_children")
assert res.body == {'_children': [],
'who': 'test'}
def test_i_can_inspect_object_with_concept(self):
sheerka, context, foo = self.init_concepts("foo")
foo.values() # freeze known attributes
sheerka.set_attr(foo, "new_var", "var_value")
dummy = DummyObj(foo, "value")
res = sheerka.inspect(context, dummy)
assert res.body == {'#type#': 'DummyObj', 'a': foo, 'b': 'value'}
def test_i_can_inspect_object_with_concept_when_as_bag(self):
sheerka, context, foo = self.init_concepts("foo")
foo.values() # freeze known attributes
sheerka.set_attr(foo, "new_var", "var_value")
dummy = DummyObj(foo, "value")
res = sheerka.inspect(context, dummy, as_bag=True)
assert res.body == {'#type#': 'DummyObj',
'a': {'#type#': 'Concept',
'id': '1001',
'key': 'foo',
'name': 'foo',
'value.new_var': "'var_value'"},
'b': 'value'}
def test_i_can_inspect_object_with_concept_when_values(self):
sheerka, context, foo = self.init_concepts("foo")
foo.values() # freeze known attributes
sheerka.set_attr(foo, "new_var", "var_value")
dummy = DummyObj(foo, "value")
res = sheerka.inspect(context, dummy, values=True)
assert res.body == {'#type#': 'DummyObj', 'a': ConceptDebugObj(foo), 'b': 'value'}
def test_i_can_inspect_concept_with_concept(self):
sheerka, context, foo, bar = self.init_concepts(Concept("foo").def_var("var_name"), "bar")
foo.set_value("var_name", bar)
res = sheerka.inspect(context, foo)
assert res.body == {'#type#': 'Concept',
'id': '1001',
'key': 'foo',
'name': 'foo',
'value.var_name': bar}
def test_i_can_inspect_concept_with_concept_when_as_bag(self):
sheerka, context, foo, bar = self.init_concepts(Concept("foo").def_var("var_name"), "bar")
foo.set_value("var_name", bar)
res = sheerka.inspect(context, foo, as_bag=True)
assert res.body == {'#type#': 'Concept',
'id': '1001',
'key': 'foo',
'name': 'foo',
'value.var_name': {'#type#': 'Concept',
'id': '1002',
'key': 'bar',
'name': 'bar'}}
def test_i_can_inspect_concept_with_concept_when_values(self):
sheerka, context, foo, bar = self.init_concepts(Concept("foo").def_var("var_name"), "bar")
foo.set_value("var_name", bar)
res = sheerka.inspect(context, foo, values=True)
assert res.body == {'#type#': 'Concept',
'id': '1001',
'key': 'foo',
'name': 'foo',
'value.var_name': ConceptDebugObj(bar)}
def test_i_can_inspect_execution_context_item(self):
sheerka, context = self.init_concepts()
ExecutionContext.ids.clear()
sheerka.evaluate_user_input("def concept one as 1")
results = list(sheerka.get_last_results(context).body)
user_input_ret_val = results[0].inputs["user_input"]
return_values = results[0].values["return_values"]
res = sheerka.inspect(context, 0)
assert res.body == {'inputs': {'user_input': user_input_ret_val},
'values.return_values': return_values}
def test_i_can_inspect_execution_context_values(self):
sheerka, context = self.init_concepts()
ExecutionContext.ids.clear()
sheerka.evaluate_user_input("def concept one as 1")
results = list(sheerka.get_last_results(context).body)
user_input_ret_val = results[0].inputs["user_input"]
return_values = results[0].values["return_values"]
res = sheerka.inspect(context, 0, values=True)
assert res.body == {'inputs': {'user_input': user_input_ret_val},
'values.return_values': [ConceptDebugObj(return_values[0].body.body)]}
def test_i_can_inspect_when_a_property_does_not_exist(self):
sheerka, context, foo = self.init_concepts("foo")
foo.values() # freeze known attributes
sheerka.set_attr(foo, "new_var", "var_value")
dummy = DummyObj(foo, "value")
res = sheerka.inspect(context, dummy, "#type#", "fake", "a", "b")
assert res.body == {'#type#': 'DummyObj',
'fake': "** Not Found **",
'a': foo,
'b': 'value'}
def test_i_can_inspect_when_properties_are_specified_several_times(self):
sheerka, context, foo = self.init_concepts("foo")
foo.values() # freeze known attributes
sheerka.set_attr(foo, "new_var", "var_value")
dummy = DummyObj(foo, "value")
res = sheerka.inspect(context, dummy, "#type#", "a", "b", "a")
assert res.body == {'#type#': 'DummyObj',
'a': foo,
'b': 'value'}
def test_i_cannot_inspect_execution_context_item_if_no_last_item(self):
sheerka, context = self.init_concepts()
res = sheerka.inspect(context, 0)
assert res.body == {'#type#': 'NotFound',
'id': '70',
'key': '__NOT_FOUND',
'name': '__NOT_FOUND',
'body': 'no digest'}
def test_i_can_inspect_values(self):
sheerka, context, table, how, little = self.init_concepts(
"table",
Concept("how is x").def_var("x"),
Concept("little x").def_var("x"),
create_new=True
)
return_values = sheerka.evaluate_user_input("how is little table")
res = sheerka.inspect(context, return_values[0], values=True)
concept_debug_obj = ConceptDebugObj(return_values[0].body)
assert res.body == {
'body': concept_debug_obj,
'#type#': 'ReturnValueConcept',
'id': '43',
'key': '__RETURN_VALUE',
'message': None,
'name': '__RETURN_VALUE',
'parents': [concept_debug_obj],
'status': True,
'value': concept_debug_obj,
'who': 'evaluators.OneSuccess'}
# I also can print it using bag
res = sheerka.inspect(context, return_values[0], '#type#', "who", "status", "value", values=True, as_bag=True)
assert res.body == {'#type#': 'ReturnValueConcept',
'who': 'evaluators.OneSuccess',
'status': True,
'value': {'#type#': 'Concept',
'compiled.x': {'#type#': 'Concept',
'compiled.x': {'#type#': 'Concept',
'id': '1001',
'key': 'table',
'name': 'table'},
'id': '1003',
'key': 'little __var__0',
'meta.x': "'table'",
'name': 'little x'},
'id': '1002',
'key': 'how is __var__0',
'meta.x': "'little table'",
'name': 'how is x'}}
def test_i_can_display_meta_and_compile_attributes_using_concept_debug_obj(self):
foo = Concept("foo", id=1001, key="foo_key").def_var("x", "x_meta").def_var("y", "y_meta")
foo.get_compiled()["x"] = Concept("bar", id=1002).def_var("a", "a_meta").set_value("a", "a_value")
foo.set_value("x", "x_value")
foo.set_value("y", NotInit)
foo.values() # freeze attributes
foo.set_value("z", "extra_value")
assert str(ConceptDebugObj(foo)) == \
"(:foo|1001:meta.x='x_meta', meta.y='y_meta', compiled.x=(:bar|1002:meta.a='a_meta', value.a='a_value'), value.x='x_value', value.z='extra_value')"
+6 -1
View File
@@ -85,7 +85,7 @@ class TestSheerkaMemory(TestUsingMemoryBasedSheerka):
foo = Concept("foo") foo = Concept("foo")
sheerka.add_to_memory(context, "a", foo) sheerka.add_to_memory(context, "a", foo)
assert service.objects.copy() == {"a": MemoryObject(context.event.get_digest(), foo)} assert service.memory_objects.copy() == {"a": MemoryObject(context.event.get_digest(), foo)}
assert id(sheerka.get_from_memory(context, "a").obj) == id(foo) assert id(sheerka.get_from_memory(context, "a").obj) == id(foo)
def test_i_can_use_memory_to_get_the_list_of_all_objects(self): def test_i_can_use_memory_to_get_the_list_of_all_objects(self):
@@ -134,6 +134,11 @@ class TestSheerkaMemory(TestUsingMemoryBasedSheerka):
assert sheerka.memory(context, "item") == bar assert sheerka.memory(context, "item") == bar
def test_object_are_not_added_in_memory_during_the_initialisation(self):
sheerka, context = self.init_concepts()
assert len(sheerka.memory(context)) == 0
class TestSheerkaMemoryUsingFileBase(TestUsingFileBasedSheerka): class TestSheerkaMemoryUsingFileBase(TestUsingFileBasedSheerka):
def test_i_can_record_memory_objects(self): def test_i_can_record_memory_objects(self):
+8 -8
View File
@@ -2,7 +2,7 @@ import ast
import pytest import pytest
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept from core.concept import Concept, CMV
from core.global_symbols import RULE_COMPARISON_CONTEXT from core.global_symbols import RULE_COMPARISON_CONTEXT
from core.rule import Rule from core.rule import Rule
from core.sheerka.services.SheerkaRuleManager import SheerkaRuleManager, FormatRuleParser, \ from core.sheerka.services.SheerkaRuleManager import SheerkaRuleManager, FormatRuleParser, \
@@ -227,8 +227,8 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
assert res[0].concept == expected assert res[0].concept == expected
@pytest.mark.parametrize("text, expected_variables", [ @pytest.mark.parametrize("text, expected_variables", [
("a cat is an animal", ["cat", "animal"]), ("a cat is an animal", ["a cat", "animal"]),
("a cat is an b", ["a", "animal"]), ("a cat is an b", ["a cat", "b"]),
]) ])
def test_i_can_compile_predicate_when_sya_node_parser(self, text, expected_variables): def test_i_can_compile_predicate_when_sya_node_parser(self, text, expected_variables):
sheerka, context, *concepts = self.init_concepts( sheerka, context, *concepts = self.init_concepts(
@@ -238,7 +238,7 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
create_new=True create_new=True
) )
service = sheerka.services[SheerkaRuleManager.NAME] service = sheerka.services[SheerkaRuleManager.NAME]
expected = concepts[0] expected = CMV(concepts[0], x=expected_variables[0], y=expected_variables[1])
res = service.compile_when(context, "test", text) res = service.compile_when(context, "test", text)
@@ -281,14 +281,14 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
assert isinstance(res[0], RulePredicate) assert isinstance(res[0], RulePredicate)
assert res[0].evaluator == CONCEPT_EVALUATOR_NAME assert res[0].evaluator == CONCEPT_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE) assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[0].predicate)[0].concept == concepts[0] assert sheerka.objvalue(res[0].predicate)[0].concept == CMV(concepts[0], x="a", y="b")
assert res[0].concept == concepts[0] assert res[0].concept == CMV(concepts[0], x="a", y="b")
assert isinstance(res[1], RulePredicate) assert isinstance(res[1], RulePredicate)
assert res[1].evaluator == CONCEPT_EVALUATOR_NAME assert res[1].evaluator == CONCEPT_EVALUATOR_NAME
assert sheerka.isinstance(res[1].predicate, BuiltinConcepts.RETURN_VALUE) assert sheerka.isinstance(res[1].predicate, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[1].predicate)[0].concept == concepts[1] assert sheerka.objvalue(res[1].predicate)[0].concept == CMV(concepts[1], x="a", y="b")
assert res[1].concept == concepts[1] assert res[1].concept == CMV(concepts[1], x="a", y="b")
# @pytest.mark.skip # @pytest.mark.skip
# @pytest.mark.parametrize("text, expected", [ # @pytest.mark.parametrize("text, expected", [
+130 -19
View File
@@ -14,10 +14,8 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
@classmethod @classmethod
def setup_class(cls): def setup_class(cls):
sheerka = cls().get_sheerka() sheerka = cls().get_sheerka(cache_only=False)
sheerka.save_execution_context = True sheerka.save_execution_context = True
sheerka.evaluate_user_input("def concept one as 1")
cls.io_cache = sheerka.sdp.io.cache.copy()
@classmethod @classmethod
def teardown_class(cls): def teardown_class(cls):
@@ -26,21 +24,73 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
def init_test(self): def init_test(self):
sheerka, context = self.init_concepts() sheerka, context = self.init_concepts()
sheerka.sdp.io.cache = self.io_cache.copy() service = sheerka.services[SheerkaResultConcept.NAME]
return sheerka, context
def test_i_can_get_the_result_by_digest(self): return sheerka, context, service
sheerka, context = self.init_test()
digest = sheerka.get_last_execution().event.get_digest() def test_i_can_record_execution_contexts(self):
sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("foo")
executions_contexts_in_cache = service.executions_contexts_cache.copy()
assert len(executions_contexts_in_cache) == 1
event_id = list(executions_contexts_in_cache.keys())[0]
execution_context = list(executions_contexts_in_cache.values())[0]
assert execution_context.desc == "Evaluating 'foo'"
executions_contexts_in_db = sheerka.sdp.load_result(event_id)
assert executions_contexts_in_db is not None
assert executions_contexts_in_db.desc == "Evaluating 'foo'"
assert service.last_execution is not None
assert service.last_execution.desc == "Evaluating 'foo'"
def test_i_can_get_the_result_by_digest_using_cache(self):
sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
digest = service.last_execution.event.get_digest()
res = sheerka.get_results_by_digest(context, digest) res = sheerka.get_results_by_digest(context, digest)
# we get the result
assert sheerka.isinstance(res, BuiltinConcepts.EXPLANATION) assert sheerka.isinstance(res, BuiltinConcepts.EXPLANATION)
assert res.command == "def concept one as 1" assert res.command == "def concept one as 1"
assert res.digest == digest assert res.digest == digest
assert isinstance(res.body, types.GeneratorType) assert isinstance(res.body, types.GeneratorType)
# the digest is correctly recorded
assert sheerka.load_var(SheerkaResultConcept.NAME, "digest") == digest
previous_results = list(res.body)
# Second test,
# I can get the result from the recorded digest
res = sheerka.get_results(context)
assert sheerka.isinstance(res, BuiltinConcepts.EXPLANATION)
assert res.command == "def concept one as 1"
assert res.digest == digest
assert isinstance(res.body, types.GeneratorType)
assert list(res.body) == previous_results
def test_i_can_get_result_by_digest_using_db(self):
sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
digest = service.last_execution.event.get_digest()
service.reset()
res = sheerka.get_results_by_digest(context, digest)
# we get the result
assert sheerka.isinstance(res, BuiltinConcepts.EXPLANATION)
assert res.command == "def concept one as 1"
assert res.digest == digest
assert isinstance(res.body, types.GeneratorType)
# the digest is correctly recorded
assert sheerka.load_var(SheerkaResultConcept.NAME, "digest") == digest assert sheerka.load_var(SheerkaResultConcept.NAME, "digest") == digest
previous_results = list(res.body) previous_results = list(res.body)
@@ -67,9 +117,10 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
assert sheerka.get_results(context) is None assert sheerka.get_results(context) is None
def test_i_can_get_the_result_by_command_name(self): def test_i_can_get_the_result_by_command_name(self):
sheerka, context = self.init_test() sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
digest = sheerka.get_last_execution().event.get_digest() digest = service.last_execution.event.get_digest()
sheerka.evaluate_user_input("one") # another command sheerka.evaluate_user_input("one") # another command
res = sheerka.get_results_by_command(context, "def concept") res = sheerka.get_results_by_command(context, "def concept")
@@ -78,8 +129,18 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
assert res.digest == digest assert res.digest == digest
assert isinstance(res.body, types.GeneratorType) assert isinstance(res.body, types.GeneratorType)
def test_i_can_get_the_result_by_command_name_using_db(self):
sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
sheerka.evaluate_user_input("one") # another command
service.reset()
res = sheerka.get_results_by_command(context, "def concept")
assert res.command == "def concept one as 1"
def test_i_can_get_the_result_by_command_when_not_in_the_same_page_size(self): def test_i_can_get_the_result_by_command_when_not_in_the_same_page_size(self):
sheerka, context = self.init_test() sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
sheerka.evaluate_user_input("one") sheerka.evaluate_user_input("one")
sheerka.evaluate_user_input("one") sheerka.evaluate_user_input("one")
@@ -98,7 +159,8 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
assert res.body == {'command': 'def concept'} assert res.body == {'command': 'def concept'}
def test_i_cannot_get_result_from_command_if_the_command_does_not_exists_multiple_pages(self): def test_i_cannot_get_result_from_command_if_the_command_does_not_exists_multiple_pages(self):
sheerka, context = self.init_test() sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
sheerka.evaluate_user_input("one") sheerka.evaluate_user_input("one")
sheerka.evaluate_user_input("one") sheerka.evaluate_user_input("one")
@@ -110,7 +172,8 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
assert res.body == {'command': 'fake command'} assert res.body == {'command': 'fake command'}
def test_i_can_get_last_results(self): def test_i_can_get_last_results(self):
sheerka, context = self.init_test() sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
sheerka.evaluate_user_input("one") sheerka.evaluate_user_input("one")
@@ -118,6 +181,16 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
assert sheerka.isinstance(res, BuiltinConcepts.EXPLANATION) assert sheerka.isinstance(res, BuiltinConcepts.EXPLANATION)
assert res.command == "one" assert res.command == "one"
def test_i_can_get_last_results_using_db(self):
sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
sheerka.evaluate_user_input("one")
service.reset()
res = sheerka.get_last_results(context)
assert sheerka.isinstance(res, BuiltinConcepts.EXPLANATION)
assert res.command == "one"
def test_i_can_get_last_results_when_event_with_no_result(self): def test_i_can_get_last_results_when_event_with_no_result(self):
sheerka, context = self.init_concepts() sheerka, context = self.init_concepts()
@@ -154,7 +227,8 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
{"desc": "Evaluating 'def concept one as 1'", "id": 0} {"desc": "Evaluating 'def concept one as 1'", "id": 0}
]) ])
def test_i_can_get_last_results_using_kwarg(self, kwargs): def test_i_can_get_last_results_using_kwarg(self, kwargs):
sheerka, context = self.init_test() sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
ExecutionContext.ids.clear() ExecutionContext.ids.clear()
res = sheerka.get_last_results(context, **kwargs) res = sheerka.get_last_results(context, **kwargs)
@@ -170,7 +244,8 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
("'def concept one as 1' in desc and id == 0", {"desc": "Evaluating 'def concept one as 1'", "id": 0}) ("'def concept one as 1' in desc and id == 0", {"desc": "Evaluating 'def concept one as 1'", "id": 0})
]) ])
def test_i_can_get_last_results_using_filter(self, predicate, expected): def test_i_can_get_last_results_using_filter(self, predicate, expected):
sheerka, context = self.init_test() sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
ExecutionContext.ids.clear() ExecutionContext.ids.clear()
res = sheerka.get_last_results(context, filter=predicate) res = sheerka.get_last_results(context, filter=predicate)
@@ -186,7 +261,8 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
("'def concept one as 1' in desc and id == 0", {"desc": "Evaluating 'def concept one as 1'", "id": 0}) ("'def concept one as 1' in desc and id == 0", {"desc": "Evaluating 'def concept one as 1'", "id": 0})
]) ])
def test_i_can_get_last_results_using_the_first_argument_to_filter(self, predicate, expected): def test_i_can_get_last_results_using_the_first_argument_to_filter(self, predicate, expected):
sheerka, context = self.init_test() sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
ExecutionContext.ids.clear() ExecutionContext.ids.clear()
res = sheerka.get_last_results(context, predicate) res = sheerka.get_last_results(context, predicate)
@@ -202,7 +278,8 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
{"desc": "Evaluating 'def concept one as 1'", "id": 0} {"desc": "Evaluating 'def concept one as 1'", "id": 0}
]) ])
def test_i_can_get_results_using_kwarg(self, kwargs): def test_i_can_get_results_using_kwarg(self, kwargs):
sheerka, context = self.init_test() sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
ExecutionContext.ids.clear() ExecutionContext.ids.clear()
sheerka.get_last_results(context) sheerka.get_last_results(context)
@@ -219,7 +296,8 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
("'def concept one as 1' in desc and id == 0", {"desc": "Evaluating 'def concept one as 1'", "id": 0}) ("'def concept one as 1' in desc and id == 0", {"desc": "Evaluating 'def concept one as 1'", "id": 0})
]) ])
def test_i_can_get_results_using_filter(self, predicate, expected): def test_i_can_get_results_using_filter(self, predicate, expected):
sheerka, context = self.init_test() sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
ExecutionContext.ids.clear() ExecutionContext.ids.clear()
sheerka.get_last_results(context) sheerka.get_last_results(context)
@@ -236,7 +314,8 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
("'def concept one as 1' in desc and id == 0", {"desc": "Evaluating 'def concept one as 1'", "id": 0}) ("'def concept one as 1' in desc and id == 0", {"desc": "Evaluating 'def concept one as 1'", "id": 0})
]) ])
def test_i_can_get_results_using_the_first_argument_to_filter(self, predicate, expected): def test_i_can_get_results_using_the_first_argument_to_filter(self, predicate, expected):
sheerka, context = self.init_test() sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
ExecutionContext.ids.clear() ExecutionContext.ids.clear()
sheerka.get_last_results(context) sheerka.get_last_results(context)
@@ -251,3 +330,35 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
predicate = {"filter": "a b c"} predicate = {"filter": "a b c"}
with pytest.raises(SyntaxError): with pytest.raises(SyntaxError):
SheerkaResultConcept.get_predicate(**predicate) SheerkaResultConcept.get_predicate(**predicate)
def test_i_can_get_last_return_value(self):
sheerka, context, service = self.init_test()
sheerka.evaluate_user_input("def concept one as 1")
ret = sheerka.last_ret(context)
assert sheerka.isinstance(ret[0].body, BuiltinConcepts.NEW_CONCEPT)
sheerka.evaluate_user_input("eval one")
ret = sheerka.last_ret(context)
assert ret[0].body == 1
def test_i_can_track_new_concept(self):
sheerka, context, service = self.init_test()
res = sheerka.evaluate_user_input("def concept one as 1")
new_concept = res[0].body.body
assert sheerka.last_created_concept(context) == new_concept
assert service.last_created_concept_id == new_concept.id
def test_last_created_concept_is_recorded(self):
sheerka, context, service = self.init_test()
res = sheerka.evaluate_user_input("def concept one as 1")
new_concept = res[0].body.body
service.reset()
service.initialize_deferred(context, False)
assert service.last_created_concept_id == new_concept.id
assert sheerka.last_created_concept(context) == new_concept
+79
View File
@@ -2,6 +2,7 @@ from dataclasses import dataclass
import core.utils import core.utils
import pytest import pytest
from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept from core.concept import Concept
from core.tokenizer import Token, TokenKind, Tokenizer, Keywords from core.tokenizer import Token, TokenKind, Tokenizer, Keywords
@@ -338,3 +339,81 @@ def test_i_can_get_text_from_tokens(text, expected_text):
def test_i_can_get_text_from_tokens_with_custom_switcher(text, custom, expected_text): def test_i_can_get_text_from_tokens_with_custom_switcher(text, custom, expected_text):
tokens = list(Tokenizer(text)) tokens = list(Tokenizer(text))
assert core.utils.get_text_from_tokens(tokens, custom) == expected_text assert core.utils.get_text_from_tokens(tokens, custom) == expected_text
def test_i_can_deep_copy_a_concept():
def check_are_the_same(actual, expected):
assert id(actual) != id(expected)
for k, v in vars(expected.get_metadata()).items():
assert getattr(actual.get_metadata(), k) == v
# test the values
for k, v in expected.values().items():
assert getattr(actual, k) == v
concept1 = Concept(name="concept1_name",
is_builtin=True,
is_unique=True,
key="concept1_key",
body="concept1_body",
where='concept1_where',
pre="concept1_pre",
post="concept1_post",
ret="concept1_ret",
definition="concept1_definition",
definition_type="concept1_definition_type",
desc="concept1_desc",
id="concept1_ids",
props="concept1_props",
variables=[],
bound_body=None)
concept2 = Concept(name="concept2_name",
is_builtin=True,
is_unique=True,
key="concept2_key",
body="concept2_body",
where='concept2_where',
pre="concept2_pre",
post="concept2_post",
ret="concept2_ret",
definition="concept2_definition",
definition_type="concept2_definition_type",
desc="concept2_desc",
id="concept2_ids",
props={"prop_name": concept1},
variables=[("var1", "default_value1"), ("var2", "default_value2")],
bound_body="var1")
concept = Concept(name="my_name",
is_builtin=True,
is_unique=True,
key="my_key",
body="my_body",
where='my_where',
pre="my_pre",
post="my_post",
ret="my_ret",
definition="my_definition",
definition_type="my_definition_type",
desc="my_desc",
id="my_ids",
props={
BuiltinConcepts.ISA: {concept1, concept2},
"prop2": ["value1, value2"],
"prop3": {"a": 1, "b": 2},
"prop4": "a simple value"},
variables=[("var1", "default_value1"), ("var2", "default_value2")])
concept.set_value("var1", "string_value")
concept.set_value("var2", 10)
concept.set_value("var3", concept1)
copied = core.utils.sheerka_deepcopy(concept)
check_are_the_same(copied, concept)
copied_props = sorted(list(copied.get_prop(BuiltinConcepts.ISA)), key=lambda o: o.id)
concept_props = sorted(list(concept.get_prop(BuiltinConcepts.ISA)), key=lambda o: o.id)
for copied_prop, concept_prop in zip(copied_props, concept_props):
check_are_the_same(copied_prop, concept_prop)
+16
View File
@@ -0,0 +1,16 @@
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestSheerkaNonRegDisplay(TestUsingMemoryBasedSheerka):
def test_i_can_display_results_when_return_values_processing_is_on(self, capsys):
init = [
"def concept one as 1",
]
sheerka = self.init_scenario(init)
sheerka.enable_process_return_values = True
sheerka.evaluate_user_input("one")
captured = capsys.readouterr()
assert captured.out == "ReturnValue(who=evaluators.OneSuccess, status=True, value=(1001)one)\n"
+1 -1
View File
@@ -1092,7 +1092,7 @@ as:
] ]
sheerka = self.init_scenario(init) sheerka = self.init_scenario(init)
res = sheerka.evaluate_user_input("desc(the a)") res = sheerka.evaluate_user_input("desc(c:the a:)")
assert len(res) == 1 assert len(res) == 1
assert res[0].status assert res[0].status
+11 -3
View File
@@ -142,7 +142,7 @@ class TestAsStrVisitor(TestUsingMemoryBasedSheerka):
(FormatAstVariable('__key', index=0, value="key1", debug=True), (FormatAstVariable('__key', index=0, value="key1", debug=True),
FormatAstVariable('__value', index="key1", value=1, debug=True)), FormatAstVariable('__value', index="key1", value=1, debug=True)),
(FormatAstVariable('__key', index=0, value="key2", debug=True), (FormatAstVariable('__key', index=1, value="key2", debug=True),
FormatAstDict("__value", debug=True, prefix="{", suffix="}", items=[ FormatAstDict("__value", debug=True, prefix="{", suffix="}", items=[
(FormatAstVariable('__key', index=0, value="sub_key1", debug=True), (FormatAstVariable('__key', index=0, value="sub_key1", debug=True),
FormatAstVariable('__value', index="sub_key1", value=1, debug=True)), FormatAstVariable('__value', index="sub_key1", value=1, debug=True)),
@@ -155,8 +155,14 @@ class TestAsStrVisitor(TestUsingMemoryBasedSheerka):
])), ])),
])), ])),
(FormatAstVariable('__key', index=1, value="long_key3", debug=True), (FormatAstVariable('__key', index=2, value="long_key3", debug=True),
FormatAstVariable('__value', index="key2", value="value2", debug=True)), FormatAstVariable('__value', index="key2", value="value2", debug=True)),
(FormatAstVariable('__key', index=3, value="key3", debug=True),
FormatAstList("__value", debug=True, prefix="[", suffix="]", items=[
FormatAstVariable('__item', index=0, value="first element", debug=True),
FormatAstVariable('__item', index=1, value="second element", debug=True),
])),
]) ])
res = visitor.visit(bag) res = visitor.visit(bag)
@@ -165,4 +171,6 @@ class TestAsStrVisitor(TestUsingMemoryBasedSheerka):
'key2' : {'sub_key1' : 1, 'key2' : {'sub_key1' : 1,
'sub_long_key2': {'sub_sub_key1': 1, 'sub_long_key2': {'sub_sub_key1': 1,
'sub_sub_key2': 'sub_sub_value'}}, 'sub_sub_key2': 'sub_sub_value'}},
'long_key3': 'value2'}""" 'long_key3': 'value2',
'key3' : ['first element',
'second element']}"""
+40
View File
@@ -0,0 +1,40 @@
from core.sheerka.services.SheerkaDebugManager import NullDebugLogger
from core.sheerka.services.SheerkaOut import SheerkaOut
from core.sheerka.services.SheerkaRuleManager import FormatAstList, FormatAstVariable
from out.DeveloperVisitor import DeveloperVisitor
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestDeveloperVisitor(TestUsingMemoryBasedSheerka):
def test_i_can_develop_list(self):
sheerka, context = self.init_concepts()
service_out = sheerka.services[SheerkaOut.NAME]
dev_visitor = DeveloperVisitor(service_out, NullDebugLogger(), set(), 0)
bag = {"a": ["a", "b", "c"]}
res = dev_visitor.visit(context, FormatAstList("a"), bag)
assert res == FormatAstList(variable="a", items=[
FormatAstVariable(name="__item", index=0, value="a"),
FormatAstVariable(name="__item", index=1, value="b"),
FormatAstVariable(name="__item", index=2, value="c"),
])
def test_i_can_develop_list_of_list(self):
sheerka, context = self.init_concepts()
service_out = sheerka.services[SheerkaOut.NAME]
dev_visitor = DeveloperVisitor(service_out, NullDebugLogger(), set(), 0)
bag = {"a": [["a1", "a2"], ["b1"]]}
res = dev_visitor.visit(context, FormatAstList("a"), bag)
assert res == FormatAstList(variable="a", items=[
FormatAstList(variable="__item", index=0, debug=True, prefix='[', suffix=']', items=[
FormatAstVariable(name="__item", index=0, debug=True, value="a1"),
FormatAstVariable(name="__item", index=1, debug=True, value="a2"),
]),
FormatAstList(variable="__item", index=1, debug=True, prefix='[', suffix=']', items=[
FormatAstVariable(name="__item", index=0, debug=True, value="b1"),
]),
])
+48
View File
@@ -449,3 +449,51 @@ key2: value2
captured = capsys.readouterr() captured = capsys.readouterr()
assert captured.out == """{'\x1b[32mkey1\x1b[0m': 'value1', 'key2': 1, 'key3': DummyObj(prop_1=3.15, prop_2='a string'), 'key4': ['alpha', 0]} assert captured.out == """{'\x1b[32mkey1\x1b[0m': 'value1', 'key2': 1, 'key3': DummyObj(prop_1=3.15, prop_2='a string'), 'key4': ['alpha', 0]}
""" """
def test_i_can_print_out_dict_sub_items(self, capsys):
sheerka, context, service, *rules = self.init_service_with_rules(
("isinstance(__obj, dict)", "dict(__obj)"),
("__key=='key1'", "green(__key)")
)
obj = {
"key1": "value1",
"key2": 1,
"key3": DummyObj(prop_1=3.15, prop_2='a string'),
"key4": {"a": 1, "b": "value"},
"key5": ["alpha", 0]
}
service.process_return_values(context, obj)
captured = capsys.readouterr()
assert captured.out == """\x1b[32mkey1\x1b[0m: value1
key2: 1
key3: DummyObj(prop_1=3.15, prop_2='a string')
key4: {'a': 1, 'b': 'value'}
key5: ['alpha', 0]
"""
def test_i_can_print_out_dict_with_expanded_sub_items(self, capsys):
sheerka, context, service, *rules = self.init_service_with_rules(
("isinstance(__obj, dict)", "dict(__obj)"),
("__key=='key1'", "green(__key)")
)
obj = {
"key1": "value1",
"key2": 1,
"key3": DummyObj(prop_1=3.15, prop_2='a string'),
"key4": {"a": 1, "b": "value"},
"key5": ["alpha", 0]
}
old_value = service.out_visitors[0].console_width
service.out_visitors[0].console_width = 5
service.process_return_values(context, obj)
captured = capsys.readouterr()
assert captured.out == """\x1b[32mkey1\x1b[0m: value1
key2: 1
key3: DummyObj(prop_1=3.15, prop_2='a string')
key4: {'a': 1,
'b': 'value'}
key5: ['alpha',
0]
"""
service.out_visitors[0].console_width = old_value
+53 -29
View File
@@ -1,10 +1,11 @@
import pytest import pytest
from core.builtin_concepts import BuiltinConcepts from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept, CIO, ALL_ATTRIBUTES from core.concept import Concept, CIO, ALL_ATTRIBUTES, CMV
from core.global_symbols import CONCEPT_COMPARISON_CONTEXT from core.global_symbols import CONCEPT_COMPARISON_CONTEXT
from core.sheerka.services.SheerkaExecute import ParserInput from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import Tokenizer from core.tokenizer import Tokenizer
from parsers.BaseNodeParser import utnode, ConceptNode, cnode, short_cnode, UnrecognizedTokensNode, \ from core.utils import NextIdManager
from parsers.BaseNodeParser import utnode, cnode, short_cnode, UnrecognizedTokensNode, \
SCWC, CNC, UTN, SCN, CN SCWC, CNC, UTN, SCN, CN
from parsers.PythonParser import PythonNode from parsers.PythonParser import PythonNode
from parsers.SyaNodeParser import SyaNodeParser, SyaConceptParserHelper, SyaAssociativity, \ from parsers.SyaNodeParser import SyaNodeParser, SyaConceptParserHelper, SyaAssociativity, \
@@ -916,21 +917,21 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
@pytest.mark.parametrize("expression, expected_debugs", [ @pytest.mark.parametrize("expression, expected_debugs", [
("one", [[" 0:one => PUSH_UNREC"]]), ("one", [[" 0:one => PUSH_UNREC"]]),
("one plus two", [[ ("one plus two", [[' 0:one => PUSH_UNREC',
' 0:one => PUSH_UNREC',
' 1:<ws> => PUSH_UNREC', ' 1:<ws> => PUSH_UNREC',
' 2:plus(SyaConceptDef(concept=(1005)a plus b, precedence=1, associativity=right)) => ??', ' 2:plus ((1005)a plus b, prio=1, assoc=SyaAssociativity.Right) => ??',
" _: => RECOG [[CN((1001)one)]]", ' _: => RECOG [[CN((1001)one)]]',
" _: => POP ConceptNode(concept='(1001)one', source='one', start=0, end=0)", " _: => POP ConceptNode(concept='(1001)one', source='one', start=0, end=0)",
' 2:plus(SyaConceptDef(concept=(1005)a plus b, precedence=1, associativity=right)) => PUSH', ' 2:plus ((1005)a plus b, prio=1, assoc=SyaAssociativity.Right) => PUSH',
' 3:<ws> => EAT', ' 3:<ws> => EAT',
' 4:two => PUSH_UNREC', ' 4:two => PUSH_UNREC',
' 5:<EOF> => ??', ' 5:<EOF> => ??',
" _: => RECOG [[CN((1002)two)]]", ' _: => RECOG [[CN((1002)two)]]',
" _: => POP ConceptNode(concept='(1002)two', source='two', start=4, end=4)", " _: => POP ConceptNode(concept='(1002)two', source='two', start=4, end=4)",
' _: => POP SyaConceptParserHelper(concept=(1005)a plus b, start=2, error=None)']]), ' _: => POP SyaConceptParserHelper(concept=(1005)a plus b, start=2, '
'error=None)']]),
("suffixed one", [[ ("suffixed one", [[
' 0:suffixed(SyaConceptDef(concept=(1009)suffixed a, precedence=1, associativity=right)) => PUSH', ' 0:suffixed ((1009)suffixed a, prio=1, assoc=SyaAssociativity.Right) => PUSH',
' 1:<ws> => EAT', ' 1:<ws> => EAT',
' 2:one => PUSH_UNREC', ' 2:one => PUSH_UNREC',
' 3:<EOF> => ??', ' 3:<EOF> => ??',
@@ -941,10 +942,10 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
("one ? twenty one : three", [[ ("one ? twenty one : three", [[
' 0:one => PUSH_UNREC', ' 0:one => PUSH_UNREC',
' 1:<ws> => PUSH_UNREC', ' 1:<ws> => PUSH_UNREC',
' 2:?(SyaConceptDef(concept=(1011)a ? b : c, precedence=1, associativity=right)) => ??', ' 2:? ((1011)a ? b : c, prio=1, assoc=SyaAssociativity.Right) => ??',
" _: => RECOG [[CN((1001)one)]]", ' _: => RECOG [[CN((1001)one)]]',
" _: => POP ConceptNode(concept='(1001)one', source='one', start=0, end=0)", " _: => POP ConceptNode(concept='(1001)one', source='one', start=0, end=0)",
' 2:?(SyaConceptDef(concept=(1011)a ? b : c, precedence=1, associativity=right)) => PUSH', ' 2:? ((1011)a ? b : c, prio=1, assoc=SyaAssociativity.Right) => PUSH',
' 3:<ws> => EAT', ' 3:<ws> => EAT',
' 4:twenty => PUSH_UNREC', ' 4:twenty => PUSH_UNREC',
' 5:<ws> => PUSH_UNREC', ' 5:<ws> => PUSH_UNREC',
@@ -955,14 +956,13 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
" _: => POP UnrecognizedTokensNode(source='twenty ', start=4, end=5)", " _: => POP UnrecognizedTokensNode(source='twenty ', start=4, end=5)",
" _: => POP ConceptNode(concept='(1001)one', source='one', start=6, end=6)", " _: => POP ConceptNode(concept='(1001)one', source='one', start=6, end=6)",
" _: => => ERROR Too many parameters found for '(1011)a ? b : c' before token 'Token(:)'", " _: => => ERROR Too many parameters found for '(1011)a ? b : c' before token 'Token(:)'",
' 8:: => EAT', ' 8:: => EAT'], [
], [
' 0:one => PUSH_UNREC', ' 0:one => PUSH_UNREC',
' 1:<ws> => PUSH_UNREC', ' 1:<ws> => PUSH_UNREC',
' 2:?(SyaConceptDef(concept=(1011)a ? b : c, precedence=1, associativity=right)) => ??', ' 2:? ((1011)a ? b : c, prio=1, assoc=SyaAssociativity.Right) => ??',
' _: => RECOG [[CN((1001)one)]]', ' _: => RECOG [[CN((1001)one)]]',
" _: => POP ConceptNode(concept='(1001)one', source='one', start=0, end=0)", " _: => POP ConceptNode(concept='(1001)one', source='one', start=0, end=0)",
' 2:?(SyaConceptDef(concept=(1011)a ? b : c, precedence=1, associativity=right)) => PUSH', ' 2:? ((1011)a ? b : c, prio=1, assoc=SyaAssociativity.Right) => PUSH',
' 3:<ws> => EAT', ' 3:<ws> => EAT',
' 4:twenty => PUSH_UNREC', ' 4:twenty => PUSH_UNREC',
' 5:<ws> => PUSH_UNREC', ' 5:<ws> => PUSH_UNREC',
@@ -976,12 +976,12 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
' 11:<EOF> => ??', ' 11:<EOF> => ??',
' _: => RECOG [[CN((1003)three)]]', ' _: => RECOG [[CN((1003)three)]]',
" _: => POP ConceptNode(concept='(1003)three', source='three', start=10, end=10)", " _: => POP ConceptNode(concept='(1003)three', source='three', start=10, end=10)",
' _: => POP SyaConceptParserHelper(concept=(1011)a ? b : c, start=2, error=None)' ' _: => POP SyaConceptParserHelper(concept=(1011)a ? b : c, start=2, error=None)']]),
]]),
]) ])
def test_i_can_debug(self, expression, expected_debugs): def test_i_can_debug(self, expression, expected_debugs):
sheerka, context, parser = self.init_parser() sheerka, context, parser = self.init_parser()
context.debug_enabled = True sheerka.set_debug(context, True)
sheerka.debug_var(context, "Sya")
res = parser.infix_to_postfix(context, ParserInput(expression)) res = parser.infix_to_postfix(context, ParserInput(expression))
assert len(res) == len(expected_debugs) assert len(res) == len(expected_debugs)
@@ -989,6 +989,19 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
actual_debug = [str(di) for di in res_i.debug] actual_debug = [str(di) for di in res_i.debug]
assert actual_debug == expected_debug assert actual_debug == expected_debug
@pytest.mark.parametrize("settings", [
"Sya.*.*",
"Sya.*.#0.can_pop"
])
def test_i_can_debug_can_pop_using_star(self, settings):
sheerka, context, parser = self.init_parser()
sheerka.set_debug(context, True)
sheerka.debug_var(context, settings)
res = parser.infix_to_postfix(context, ParserInput("one plus two mult three"))
debug = [str(di) for di in res[0].debug]
assert debug[5] == ' _: => No stack. CAN_POP false.'
def test_i_can_parse_when_concept_atom_only(self): def test_i_can_parse_when_concept_atom_only(self):
sheerka, context, parser = self.init_parser() sheerka, context, parser = self.init_parser()
@@ -999,15 +1012,20 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
assert res.status assert res.status
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert lexer_nodes == [ConceptNode(cmap["plus"], 0, 8, source=text)] assert lexer_nodes == [CN(cmap["plus"], 0, 8, source=text)]
# check the compiled # check the compiled
expected_concept = lexer_nodes[0].concept expected_concept = lexer_nodes[0].concept
assert expected_concept.get_compiled()["a"] == cmap["one"] assert expected_concept.get_compiled()["a"] == cmap["one"]
assert expected_concept.get_compiled()["b"] == cmap["mult"] assert expected_concept.get_compiled()["b"] == CMV(cmap["mult"], a="two", b="three")
assert expected_concept.get_compiled()["b"].get_compiled()["a"] == cmap["two"] assert expected_concept.get_compiled()["b"].get_compiled()["a"] == cmap["two"]
assert expected_concept.get_compiled()["b"].get_compiled()["b"] == cmap["three"] assert expected_concept.get_compiled()["b"].get_compiled()["b"] == cmap["three"]
# check the metadata
expected_concept = lexer_nodes[0].concept
assert expected_concept.get_metadata().variables == [("a", "one"), ("b", "two mult three")]
assert expected_concept.get_compiled()["b"].get_metadata().variables == [("a", "two"), ("b", "three")]
def test_i_can_parse_when_python_code(self): def test_i_can_parse_when_python_code(self):
sheerka, context, parser = self.init_parser() sheerka, context, parser = self.init_parser()
@@ -1018,7 +1036,7 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
assert res.status assert res.status
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert lexer_nodes == [ConceptNode(cmap["suffixed"], 0, 6, source=text)] assert lexer_nodes == [CN(cmap["suffixed"], 0, 6, source=text)]
# check the compiled # check the compiled
expected_concept = lexer_nodes[0].concept expected_concept = lexer_nodes[0].concept
@@ -1031,6 +1049,9 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
assert return_value_a.body.source == "1 + 1" assert return_value_a.body.source == "1 + 1"
assert isinstance(return_value_a.body.body, PythonNode) assert isinstance(return_value_a.body.body, PythonNode)
# check metadata
assert expected_concept.get_metadata().variables == [("a", "1 + 1")]
def test_i_can_parse_when_bnf_concept(self): def test_i_can_parse_when_bnf_concept(self):
sheerka, context, parser = self.init_parser() sheerka, context, parser = self.init_parser()
@@ -1043,13 +1064,16 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
lexer_nodes = res[1].body.body lexer_nodes = res[1].body.body
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert lexer_nodes == [ConceptNode(cmap["suffixed"], 0, 4, source=text)] assert lexer_nodes == [CN(cmap["suffixed"], 0, 4, source=text)]
# check the compiled # check the compiled
expected_concept = lexer_nodes[0].concept expected_concept = lexer_nodes[0].concept
assert sheerka.isinstance(expected_concept.get_compiled()["a"], "twenties") assert sheerka.isinstance(expected_concept.get_compiled()["a"], "twenties")
assert expected_concept.get_compiled()["a"].get_compiled()["unit"] == cmap["one"] assert expected_concept.get_compiled()["a"].get_compiled()["unit"] == cmap["one"]
# check metadata
assert expected_concept.get_metadata().variables == [("a", "twenty one")]
def test_i_can_parse_sequences(self): def test_i_can_parse_sequences(self):
sheerka, context, parser = self.init_parser() sheerka, context, parser = self.init_parser()
@@ -1061,8 +1085,8 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
assert res.status assert res.status
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert lexer_nodes == [ assert lexer_nodes == [
ConceptNode(cmap["plus"], 0, 9, source="one plus 1 + 1 "), CN(cmap["plus"], 0, 9, source="one plus 1 + 1 "),
ConceptNode(cmap["suffixed"], 10, 12, source="suffixed two")] CN(cmap["suffixed"], 10, 12, source="suffixed two")]
# check the compiled # check the compiled
concept_plus_a = lexer_nodes[0].concept.get_compiled()["a"] concept_plus_a = lexer_nodes[0].concept.get_compiled()["a"]
@@ -1198,7 +1222,7 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
assert not res.status assert not res.status
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT) assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert lexer_nodes == [ConceptNode(cmap[expected_concept], 0, expected_end, source=text)] assert lexer_nodes == [CN(cmap[expected_concept], 0, expected_end, source=text)]
concept_found = lexer_nodes[0].concept concept_found = lexer_nodes[0].concept
for unrecognized in expected_unrecognized: for unrecognized in expected_unrecognized:
@@ -1278,7 +1302,7 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
]) ])
def test_i_can_get_functions_names_from_unrecognized(self, expression, expected): def test_i_can_get_functions_names_from_unrecognized(self, expression, expected):
sheerka, context, parser = self.init_parser() sheerka, context, parser = self.init_parser()
infix_to_postfix = InFixToPostFix(context) infix_to_postfix = InFixToPostFix(context, NextIdManager())
tokens = list(Tokenizer(expression, yield_eof=False)) tokens = list(Tokenizer(expression, yield_eof=False))
for pos, token in enumerate(tokens[:-1]): for pos, token in enumerate(tokens[:-1]):
@@ -1302,7 +1326,7 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
]) ])
def test_i_can_get_functions_names_from_unrecognized_when_multiple_results(self, expression, expected_list): def test_i_can_get_functions_names_from_unrecognized_when_multiple_results(self, expression, expected_list):
sheerka, context, parser = self.init_parser() sheerka, context, parser = self.init_parser()
infix_to_postfix = InFixToPostFix(context) infix_to_postfix = InFixToPostFix(context, NextIdManager())
tokens = list(Tokenizer(expression, yield_eof=False)) tokens = list(Tokenizer(expression, yield_eof=False))
for pos, token in enumerate(tokens[:-1]): for pos, token in enumerate(tokens[:-1]):