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'"