From ca68d9f89e0020dbdff7e214ef00da3fdd128998 Mon Sep 17 00:00:00 2001 From: ARCJ137442 <61109168+ARCJ137442@users.noreply.github.com> Date: Fri, 22 Sep 2023 17:01:35 +0800 Subject: [PATCH] feat(ConsolePlus): :sparkles: Now the experimental cmds can be used as normal cmds, just only differents from using `\` insteads of `/` as prefix --- Experiments/ExConsole/main.py | 538 ++++++++++++++++++++-------------- pynars/ConsolePlus.py | 94 +++--- 2 files changed, 376 insertions(+), 256 deletions(-) diff --git a/Experiments/ExConsole/main.py b/Experiments/ExConsole/main.py index 1b42a10..b1ba7ad 100644 --- a/Experiments/ExConsole/main.py +++ b/Experiments/ExConsole/main.py @@ -68,235 +68,341 @@ def printInf(): ## Main program ## -def _cmdExperiment(cmd: str): - '''Cmd experiment entry''' - nars = currentNARSInterface.reasoner # current NARS reasoner - if cmd == 'l': # list the details of the current reasoner - reasonerList() - elif cmd == 't': # test an input sentence - execInput(' B>.') - elif cmd == 'r': # test an random input sentence - execInput(f' A{randint(1,9)}>.') - elif cmd == 'c': # list all concepts - print(infBag(nars.memory.concepts)) - elif cmd == 'exec': # execute code - while (inp := input('Please input Python code (empty to cancel)')): - try: - exec(inp) # separated by a space, followed by a statement - except BaseException as e: - print(f'Error! {e}') - elif cmd == 'v': # list variables - print('locals\n\t'+'\n\t'.join(f'{k} : {v}' for k, v in locals().items()), - 'globals\n\t' + - '\n\t'.join(f'{k} : {v}' for k, v in globals().items()), - sep='\n') - elif cmd == 'h': # history - printHistory('') - printHistory('1') - elif cmd == 'help': # help - print('''The backslash commands is used for feature experiments. - to see the supported certain commands, may you should view `Experiments\ExConsole\main.py\_cmdExperiment`.''') - elif cmd == 'ch': # New channel addition test - # add new channel - channel: Channel = Channel(capacity=5, n_buckets=5, max_duration=10000) - # [2023-09-21 23:51:23] PyNARS does not currently have a built-in "add channel" method - nars.channels.append(channel) - print(infChannel(channel=channel)) - # test new channel - [channel.put(NAL_Parse(testSentence_C)) # auto parse - for testSentence_C in [ - ' cpt_channel_2>.', - ' cpt_channel_3>.', - ' cpt_channel_3>?', - ] - ] # ? multiple input at once - printInf() - execInput('/waitans') - printInf() - elif cmd == 'cha': # Shared channel test - channel: Channel = NarseseChannel( - capacity=500, n_buckets=100, max_duration=1000) - # new reasoner - rName1: str = '1' - rName2: str = '2' - r1: Reasoner = reasonerNew(name=rName1).reasoner - r2: Reasoner = reasonerNew(name=rName2).reasoner - r1.narsese_channel = r2.narsese_channel = r1.channels[0] = r2.channels[0] = channel - execInput(' B>.') # enter the NARS statement in r2 - reasonerGoto(rName1) # r1 also has channels in it - execInput('10') # enter the NARS statement in r2 - reasonerList() - elif cmd == 'me': # Shared memory test - memory: Memory = Memory( - capacity=1000, - n_buckets=50) # This `n_buckets` determines the levels of the concepts in it. - # new reasoner - rName1: str = '1' - rName2: str = '2' - r1: Reasoner = reasonerNew(name=rName1)._NARS - r2: Reasoner = reasonerNew(name=rName2)._NARS - r1.memory = r2.memory = memory - # enter the NARS statement in r2 - execInput('A --> B.', 'B --> C.', 'A --> C?') - reasonerGoto(rName1) # r1 also has channels in it - execInput('1') # enter the NARS statement in r2 - print(r1.memory is r2.memory, r1.memory is memory) - printInf() - elif cmd == 'op': - taskOp: Task = NAL_Parse(' result>.') # auto parse - # term.type = TermType.STATEMENT # ! Only statements can be actions, not mandatory - statementOp = taskOp.term # term of the task - # * Force the term involved in the task to be set to "action", if is_executable = True - statementOp.is_operation = True - print(f'Is operation? {statementOp.is_executable}') - '''concept Concept: Concept = concept. _conceptualize( # Generate concept - nars.memory, +EXPERIMENTAL_CMDS: dict[tuple[str]:tuple[any, tuple[str]]] = {} + + +def expRegister(*cmdNames: tuple[str]): + '''Mimic from `cmdRegister` in "Interface.py" + It's no need of parameters because the experimental functions can utilize all of global variable''' + def decorator(func): + # register + global EXPERIMENTAL_CMDS + EXPERIMENTAL_CMDS[cmdNames] = (func, ) + ''' + Core format & Structure: {Name: (handler function, ordinal and default list)} + ! Tuples can be used as indexes: fixed type + ''' + + # decorator: pass metadata (docstrings) to the decorated function + @functools.wraps(func) + def decorated(*args, **kwargs): + return func(*args, **kwargs) + + # manual synchronization name and documents: Directly modify + decorated.__name__ = func.__name__ + decorated.__doc__ = func.__doc__ + + # finish + return decorated + + return decorator + + +@expRegister('l') +def listDetails_reasoner() -> None: + return reasonerList() + + +@expRegister('t') +def test_simple_output() -> None: + execInput(' B>.') + + +@expRegister('r') +def test_random_input_sentence() -> None: + execInput(f' A{randint(1,9)}>.') + + +@expRegister('c') +def list_all_concepts() -> None: + print(infBag((currentNARSInterface.reasoner.memory.concepts))) + + +@expRegister('exec') +def exec_multiline_python_code() -> None: + while (inp := input('Please input Python code (empty to cancel)')): + try: + exec(inp) # separated by a space, followed by a statement + except BaseException as e: + print(f'Error! {e}') + + +@expRegister('v') +def list_variables() -> None: + print('locals\n\t'+'\n\t'.join(f'{k} : {v}' for k, v in locals().items()), + 'globals\n\t' + + '\n\t'.join(f'{k} : {v}' for k, v in globals().items()), + sep='\n') + + +@expRegister('r') +def list_histories() -> None: + printHistory('') + printHistory('1') + + +@expRegister('help') +def help_of_experiments() -> None: + print('''The backslash commands is used for feature experiments.\nto see the supported certain commands, may you should view `Experiments\ExConsole\main.py\_cmdExperiment`.''') + help('', searchIn=EXPERIMENTAL_CMDS) + + +@expRegister('ch') +def channel_addition_test() -> None: + # add new channel + channel: Channel = Channel(capacity=5, n_buckets=5, max_duration=10000) + # [2023-09-21 23:51:23] PyNARS does not currently have a built-in "add channel" method + currentNARSInterface.reasoner.channels.append(channel) + print(infChannel(channel=channel)) + # test new channel + [channel.put(NAL_Parse(testSentence_C)) # auto parse + for testSentence_C in [ + ' cpt_channel_2>.', + ' cpt_channel_3>.', + ' cpt_channel_3>?', + ] + ] # ? multiple input at once + printInf() + execInput('/waitans') + printInf() + + +@expRegister('cha') +def shared_channel_test() -> None: + channel: Channel = NarseseChannel( + capacity=500, n_buckets=100, max_duration=1000) + # new reasoner + rName1: str = '1' + rName2: str = '2' + r1: Reasoner = reasonerNew(name=rName1).reasoner + r2: Reasoner = reasonerNew(name=rName2).reasoner + r1.narsese_channel = r2.narsese_channel = r1.channels[0] = r2.channels[0] = channel + execInput(' B>.') # enter the NARS statement in r2 + reasonerGoto(rName1) # r1 also has channels in it + execInput('10') # enter the NARS statement in r2 + reasonerList() + + +@expRegister('me') +def shared_memory_test() -> None: + memory: Memory = Memory( + capacity=1000, + n_buckets=50) # This `n_buckets` determines the levels of the concepts in it. + # new reasoner + rName1: str = '1' + rName2: str = '2' + r1: Reasoner = reasonerNew(name=rName1)._NARS + r2: Reasoner = reasonerNew(name=rName2)._NARS + r1.memory = r2.memory = memory + # enter the NARS statement in r2 + execInput('A --> B.', 'B --> C.', 'A --> C?') + reasonerGoto(rName1) # r1 also has channels in it + execInput('1') # enter the NARS statement in r2 + print(r1.memory is r2.memory, r1.memory is memory) + printInf() + + +@expRegister('op') +def operations_test() -> None: + taskOp: Task = NAL_Parse(' result>.') # auto parse + # term.type = TermType.STATEMENT # ! Only statements can be actions, not mandatory + statementOp = taskOp.term # term of the task + # * Force the term involved in the task to be set to "action", if is_executable = True + statementOp.is_operation = True + print(f'Is operation? {statementOp.is_executable}') + '''concept Concept: Concept = concept. _conceptualize( # Generate concept + currentNARSInterface.reasoner.memory, term=statement Operation statement, Budget=budget(0.9, 0.9, 0.5) ) - nars.memory.concepts.put(concept) + currentNARSInterface.reasoner.memory.concepts.put(concept) # into the concept, but it does not seem to be associated with the task, the reasoner will not use it. # ''' - # placing tasks directly into perceptual channels (Narsese channels can only pass text) - nars.perception_channel.put(taskOp) - # Automatic reasoning five steps, let NAS put "antecedent" and "result" into the memory area - execInput('5') - print(infBag(nars.memory.concepts)) # print concept list - elif cmd == 'far': # remote inference test - n: int = int(input('Please enter the length of chain:')) - [execInput(f'chainNo{i} --> chainNo{i+1}.') - for i in range(n) - ] - printInf() - execInput(f'chainNo0 --> chainNo{n}?') - execInput('/waitans') - elif cmd == 'js': # JSON test: Input a series of numbers and construct a set, allowing NARS to determine ownership - from data2nal import auto2NAL, SIGN_RELATION_BELONG - n: int = int(input('Please enter the number: ')) - f: set = {x for x in range(n)} - sN: str = f'Num0to{n}' - s2: set = {sN, 'element2'} - sN2: str = 'bigSet' - execInput(*auto2NAL(f, sN), *auto2NAL(f, sN2), - f'<(*,{1},{sN2}) --> {SIGN_RELATION_BELONG}>?') - printInf() - execInput('/waitans') - printInf() - elif cmd == 'js2': # JSON Test 2: Enter a custom dictionary and ask for relationships one by one - from data2nal import auto2NAL - print('Part II:') - dic: dict = { - 'smallestPositiveInteger': 1, - 'evenNumberLT10': [ - 2, 4, 6, 8 - ], - 'is0notNeg': True, - 1: { - 'dictName': 'aNameOfDict' - } + # placing tasks directly into perceptual channels (Narsese channels can only pass text) + currentNARSInterface.reasoner.perception_channel.put(taskOp) + # Automatic reasoning five steps, let NAS put "antecedent" and "result" into the memory area + execInput('5') + # print concept list + print(infBag(currentNARSInterface.reasoner.memory.concepts)) + + +@expRegister('far', 'chain', 'chain_inference') +def chain_inference_test() -> None: + n: int = int(input('Please enter the length of chain:')) + [execInput(f' chainNo{i+1}>.') + for i in range(n) + ] + printInf() + execInput(f' chainNo{n}>?') + execInput('/waitans') + + +@expRegister('json') +def JSON_test() -> None: + '''Input a series of numbers and construct a set, allowing NARS to determine ownership''' + from data2nal import auto2NAL, SIGN_RELATION_BELONG + n: int = int(input('Please enter the number: ')) + f: set = {x for x in range(n)} + sN: str = f'Num0to{n}' + s2: set = {sN, 'element2'} + sN2: str = 'bigSet' + execInput(*auto2NAL(f, sN), *auto2NAL(f, sN2), + f'<(*,{1},{sN2}) --> {SIGN_RELATION_BELONG}>?') + printInf() + execInput('/waitans') + printInf() + + +@expRegister('json') +def JSON_test2() -> None: + '''Enter a custom dictionary and ask for relationships one by one''' + from data2nal import auto2NAL + print('JSON Test Part II:') + dic: dict = { + 'smallestPositiveInteger': 1, + 'evenNumberLT10': [ + 2, 4, 6, 8 + ], + 'is0notNeg': True, + 1: { + 'dictName': 'aNameOfDict' } - execInput(*auto2NAL(dic, 'myDict')) - printInf() - execInput(*auto2NAL(dic, 'myDict', punct=Punctuation.Question)) - printInf() - execInput('/waitans') - elif cmd == 'js3': # JSON Test 3: Enter a Config.json object that acts as its own "system parameter" - print('Part III') - from pynars.Interface import DEFAULT_CONFIG - from data2nal import auto2NAL - printInf() - execInput(*auto2NAL(DEFAULT_CONFIG, 'systemConfig', - punct=Punctuation.Judgement)) - printInf() - execInput(*auto2NAL(DEFAULT_CONFIG, 'systemConfig', - punct=Punctuation.Question)) - printInf() - execInput('/waitans') - elif cmd == 'eval': # Load any Python object into NARS - from data2nal import auto2NAL - obj: any = None - while not obj: - try: - obj: any = eval(input(' Please input your Python object: ')) - except BaseException as e: - print( - f' parsing failed! Error: {e.With_traceback(None) if e else e}') - name: str = input( - 'Please enter a name for this object (leave blank for automatic generation): ') - punct: str = input( - 'Please enter your modality for the object (./?/!) (Leave blank default.): ') - nals: list[str] = auto2NAL( - obj, punct=punct if punct else '.', name=name if name else None) - print(f'Input object: {repr(obj)}\nNAL text: \n' + "\n".join(nals)) - execInput(*nals) - elif cmd == 'mcopy': # Experiment: Memory copy & Localization retention - import jsonpickle as jp # Use the JSON serialization library jsonpickle - copiedMem: Memory = deepcopy(nars.memory) - execInput('A-->B.', 'B-->C.', 'A-->C?', '/waitans') - print(id(nars.memory), infMemory(nars.memory), - 'copied:\n', id(copiedMem), infMemory(copiedMem)) - jpEmem = jp.encode(nars.memory) - jpEcopied = jp.encode(copiedMem) - print('pickle#Encode:\n', repr(jpEmem), 'copied:\n', repr(jpEcopied)) - decodedMem: Memory = jp.decode(jpEcopied) - print(id(copiedMem), infMemory(copiedMem), 'decoded:\n', - id(decodedMem), infMemory(decodedMem)) - elif cmd == 'rcopy': # Make a deep copy of the entire reasoner - import jsonpickle as jp # Use the JSON serialization library jsonpickle - import json - copiedNar: Reasoner = deepcopy(nars) # deep copy - jpEnar: str = jp.encode(copiedNar) # serialize to JSON string - encodedNar: dict = json.loads(jpEnar) # JSON string to dict - rStrDict: str = repr(encodedNar) # dict to str - decodedNar: Reasoner = jp.decode( - json.dumps(encodedNar)) # str to Reasoner - print( - f'Copied:\n{infReasoner(copiedNar)}\nEncoded:\n{rStrDict}\nDecoded:\n{infReasoner(decodedNar)}') - elif cmd == 'copyJ': # Copy the reasoner data to the clipboard - # import module - from pyperclip import copy - import jsonpickle as jp + } + execInput(*auto2NAL(dic, 'myDict')) + printInf() + execInput(*auto2NAL(dic, 'myDict', punct=Punctuation.Question)) + printInf() + execInput('/waitans') + + +@expRegister('json') +def JSON_test3() -> None: + '''Enter a Config.json object that acts as its own "system parameter"''' + print('JSON Test Part III') + from pynars.Interface import DEFAULT_CONFIG + from data2nal import auto2NAL + printInf() + execInput(*auto2NAL(DEFAULT_CONFIG, 'systemConfig', + punct=Punctuation.Judgement)) + printInf() + execInput(*auto2NAL(DEFAULT_CONFIG, 'systemConfig', + punct=Punctuation.Question)) + printInf() + execInput('/waitans') + + +@expRegister('eval') +def pyObject_loadIn() -> None: + '''Load any Python object into NARS''' + from data2nal import auto2NAL + obj: any = None + while not obj: try: - jpEnar: str = jp.encode(nars) # serialize to JSON string - print( - f'Source reasoner:\n{infReasoner(nars)}\n Serialized JSON string:\n{jpEnar}') - copy(jpEnar) # copy to clipboard - print('JSON data copied!') + obj: any = eval(input(' Please input your Python object: ')) except BaseException as e: - print(f'Save Failed!\nError:{e.with_traceback(None) if e else e}') - elif cmd == 'loadJ': # load the clipboard reasoner data into the interface - # import module - import jsonpickle as jp + print( + f' parsing failed! Error: {e.With_traceback(None) if e else e}') + name: str = input( + 'Please enter a name for this object (leave blank for automatic generation): ') + punct: str = input( + 'Please enter your modality for the object (./?/!) (Leave blank default.): ') + nals: list[str] = auto2NAL( + obj, punct=punct if punct else '.', name=name if name else None) + print(f'Input object: {repr(obj)}\nNAL text: \n' + "\n".join(nals)) + execInput(*nals) + + +@expRegister('mcopyJ') +def memory_copy_JSON() -> None: + '''Experiment: Memory copy & Localization retention''' + nars = currentNARSInterface.reasoner + import jsonpickle as jp # Use the JSON serialization library jsonpickle + copiedMem: Memory = deepcopy(nars.memory) + execInput('A-->B.', 'B-->C.', 'A-->C?', '/waitans') + print(id(nars.memory), infMemory(nars.memory), + 'copied:\n', id(copiedMem), infMemory(copiedMem)) + jpEmem = jp.encode(nars.memory) + jpEcopied = jp.encode(copiedMem) + print('pickle#Encode:\n', repr(jpEmem), 'copied:\n', repr(jpEcopied)) + decodedMem: Memory = jp.decode(jpEcopied) + print(id(copiedMem), infMemory(copiedMem), 'decoded:\n', + id(decodedMem), infMemory(decodedMem)) + + +@expRegister('rcopyJ') +def reasoner_copy_JSON() -> None: + '''Make a deep copy of the entire reasoner''' + nars = currentNARSInterface.reasoner + import jsonpickle as jp # Use the JSON serialization library jsonpickle + import json + copiedNar: Reasoner = deepcopy(nars) # deep copy + jpEnar: str = jp.encode(copiedNar) # serialize to JSON string + encodedNar: dict = json.loads(jpEnar) # JSON string to dict + rStrDict: str = repr(encodedNar) # dict to str + decodedNar: Reasoner = jp.decode( + json.dumps(encodedNar)) # str to Reasoner + print( + f'Copied:\n{infReasoner(copiedNar)}\nEncoded:\n{rStrDict}\nDecoded:\n{infReasoner(decodedNar)}') + + +@expRegister('copyJ') +def copy_JSON() -> None: + '''Copy the reasoner data to the clipboard''' + nars = currentNARSInterface.reasoner + # import module + from pyperclip import copy + import jsonpickle as jp + try: + jpEnar: str = jp.encode(nars) # serialize to JSON string + print( + f'Source reasoner:\n{infReasoner(nars)}\n Serialized JSON string:\n{jpEnar}') + copy(jpEnar) # copy to clipboard + print('JSON data copied!') + except BaseException as e: + print(f'Save Failed!\nError:{e.with_traceback(None) if e else e}') + + +@expRegister('loadJ') +def load_JSON() -> None: + '''load the clipboard reasoner data into the interface''' + # import module + import jsonpickle as jp + try: + jsonPath: str = input( + 'Please enter your saved reasoner JSON path:') # get JSON path + from pathlib import Path + jsonPath: Path = Path(jsonPath) + with open(jsonPath, mode='r', encoding='utf-8') as jsonFile: + jsonStr: str = jsonFile.read() try: - jsonPath: str = input( - 'Please enter your saved reasoner JSON path:') # get JSON path - from pathlib import Path - jsonPath: Path = Path(jsonPath) - with open(jsonPath, mode='r', encoding='utf-8') as jsonFile: - jsonStr: str = jsonFile.read() + decodedNAR: Reasoner = jp.decode( + jsonStr) # deserialize from JSON string try: - decodedNAR: Reasoner = jp.decode( - jsonStr) # deserialize from JSON string - try: - interfaceName: str = input( - 'Please enter a new interface name:') # accept the reasoner with a new interface - interface: NARSInterface = NARSInterface( - NARS=decodedNAR) # create interface - reasoners[interfaceName] = interface # directly add - reasonerGoto(interfaceName) # goto - print( - f"Import a reasoner named {interfaceName}, silent {'on' if interface.silentOutput else 'off'}.") - print( - f'Pre-deserialized JSON string:\n{jsonStr}\nNew reasoner:\n{infReasoner(decodedNAR)}') - except BaseException as e: - print( - f'Import failed! \nError:{e.with_traceback(None) if e else e}') + interfaceName: str = input( + 'Please enter a new interface name:') # accept the reasoner with a new interface + interface: NARSInterface = NARSInterface( + NARS=decodedNAR) # create interface + reasoners[interfaceName] = interface # directly add + reasonerGoto(interfaceName) # goto + print( + f"Import a reasoner named {interfaceName}, silent {'on' if interface.silentOutput else 'off'}.") + print( + f'Pre-deserialized JSON string:\n{jsonStr}\nNew reasoner:\n{infReasoner(decodedNAR)}') except BaseException as e: print( - f'Deserialization failed! \nError:{e.with_traceback(None) if e else e}') + f'Import failed! \nError:{e.with_traceback(None) if e else e}') except BaseException as e: - print(f'Read failed! \nError:{e.with_traceback(None) if e else e}') - return + print( + f'Deserialization failed! \nError:{e.with_traceback(None) if e else e}') + except BaseException as e: + print(f'Read failed! \nError:{e.with_traceback(None) if e else e}') + + +def _cmdExperiment(cmd: str): + '''Cmd experiment entry''' + nars = currentNARSInterface.reasoner # current NARS reasoner + # [2023-09-22 16:34:55] Now reuse the multi-patch to run cmd respectfully + autoExecuteCmdByName(cmd, [], EXPERIMENTAL_CMDS) # Main diff --git a/pynars/ConsolePlus.py b/pynars/ConsolePlus.py index fa82e20..ed898ed 100644 --- a/pynars/ConsolePlus.py +++ b/pynars/ConsolePlus.py @@ -38,22 +38,21 @@ def prefixBrowse(toBrowse: list[str], *keywords: list[str]) -> list[str]: # find matching cmds by: do not repeat for string in toBrowse # match the list of all cmds, as long as they match the search results - not necessarily in order - if any(string.startswith(prefix) for prefix in keywords) - ] + if any( + string.startswith(prefix) + for prefix in keywords)] -def prefixCmdBrowse(*keywords: list[str]) -> list[tuple[str]]: +def prefixCmdBrowse(cmdDict: dict[tuple:tuple], *keywords: list[str]) -> list[tuple[str]]: '''Matches the content against the string prefix''' return [aliasIndices # returns cmd names # find matching cmds by: do not repeat - for aliasIndices in PRESET_CMDS + for aliasIndices in cmdDict if any( - any(aliasIndex.startswith(keyword) - for aliasIndex in aliasIndices - ) - for keyword in keywords - ) # match the list of all cmds, as long as they match the search results - not necessarily in order - ] + any( + aliasIndex.startswith(keyword) + for aliasIndex in aliasIndices) + for keyword in keywords)] # match the list of all cmds, as long as they match the search results - not necessarily in order def quickConvertCmdTypes(inp: list[str], type_N_default: list[tuple[type, any]]) -> list[str]: @@ -275,19 +274,19 @@ def toggleSimplifyParse() -> None: @cmdRegister('help') -def help(*keywords: list[str]) -> None: +def help(*keywords: list[str], searchIn: dict[tuple:tuple] = PRESET_CMDS) -> None: '''Format: help [... specific cmd] Display this help document, or assist in retrieving help for additional cmds''' # Core idea: Empty prefix = all cmds keywords = keywords if keywords else [''] # find a matching cmd name - cmdNameAliases: list[tuple(str)] = prefixCmdBrowse(*keywords) + cmdNameAliases: list[tuple(str)] = prefixCmdBrowse(searchIn, *keywords) # display "matching cmds" as follows if cmdNameAliases: # match the list of all cmds, as long as they match the search results - not necessarily in order for cmdNameAlias in cmdNameAliases: # Structure: {Name: (handler, ordinal and default list)} - cmdFunction = PRESET_CMDS[cmdNameAlias][0] + cmdFunction = searchIn[cmdNameAlias][0] print( f'''<{"/".join(cmdNameAlias)}>: {cmdFunction.__name__}\n{ cmdFunction.__doc__ @@ -613,33 +612,7 @@ def execInput(inp: str, *otherInput: list[str]) -> None: words: list[str] = inp[1:].split() if words: # if not empty cmdName: str = words[0].lower() # case insensitive - nameAliasHints: list[tuple[str]] = prefixCmdBrowse( - cmdName) # auto browse & complete - if len(nameAliasHints) == 1: # Only option: Full match or auto complete - # auto complete - if not cmdName in nameAliasHints[0]: - print( - f'Autocompleted cmd to "{"/".join(nameAliasHints[0])}".') - nameAliasIndex = nameAliasHints[0] - # Cmd execution: Automatically adjust the "parameter requirements" of specific cmds and intercept parameters - cmdData = PRESET_CMDS[nameAliasIndex] - cmdHandler = cmdData[0] - type_N_default: list[tuple[type, any]] = cmdData[1] if len( - cmdData) > 1 else None - params: list = quickConvertCmdTypes(words[1:], type_N_default) - if 1: - # in the form of positional arguments, to the appropriate handler. Structure: {Name: (handler, ordinal and default list)} - cmdHandler(*params) - try: - pass - except BaseException as e: - print('Cmd execute failed: ', - e.with_traceback(None) if e else e) - else: - hint = 'Are you looking for "' + \ - "\"|\"".join('/'.join(alias) for alias in nameAliasHints) + \ - '"?' if nameAliasHints else '' - print(f'Unknown cmd {cmdName}. {hint}') + autoExecuteCmdByName(cmdName, words[1:]) return # If it's executed as a command, it won't execute as Narsese input # Narsese parsing @@ -652,6 +625,47 @@ def execInput(inp: str, *otherInput: list[str]) -> None: # input NAL currentNARSInterface.inputNAL(inp) + +def autoExecuteCmdByName(cmdName: str, words: list[str], cmdDict: dict[tuple:tuple] = PRESET_CMDS) -> bool: + '''Execute cmd by name with autocompletion + returns: whether a cmd is chosen and executed successfully''' + # auto browse & complete + nameAliasHints: list[tuple[str]] = prefixCmdBrowse(cmdDict, cmdName) + # if it have a precise match, directly use it + for i in range(len(nameAliasHints)): + nameAliases = nameAliasHints[i] + if any(nameAlias == cmdName for nameAlias in nameAliases): + nameAliasHints = [nameAliases] + break + # Only option: Full match or auto complete + if len(nameAliasHints) == 1: + # auto complete + if not cmdName in nameAliasHints[0]: + print( + f'Autocompleted cmd to "{"/".join(nameAliasHints[0])}".') + nameAliasIndex = nameAliasHints[0] + # Cmd execution: Automatically adjust the "parameter requirements" of specific cmds and intercept parameters + cmdData = cmdDict[nameAliasIndex] + cmdHandler = cmdData[0] + type_N_default: list[tuple[type, any]] = ( + cmdData[1] + if len(cmdData) > 1 else None) + params: list = quickConvertCmdTypes(words[1:], type_N_default) + try: + # in the form of positional arguments, to the appropriate handler. Structure: {Name: (handler, ordinal and default list)} + cmdHandler(*params) + return True + except BaseException as e: + print('Cmd execute failed: ', + e.with_traceback(None) if e else e) + return False + else: + hint = 'Are you looking for "' + \ + "\"|\"".join('/'.join(alias) for alias in nameAliasHints) + \ + '"?' if nameAliasHints else '' + print(f'Unknown cmd {cmdName}. {hint}') + return False + # @checkExcept error alarm (message formatted message =' Input execution failed: %s') # TODO fix "TypeError: checkExcept error alarm..decorator decorator () missing 1 required positional argument: 'func'"