continued reorganisation
This commit is contained in:
parent
6f8a7322f2
commit
0a2d9a5694
22 changed files with 190 additions and 61 deletions
1
bcrypter/cli/__init__.py
Normal file
1
bcrypter/cli/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
0
bcrypter/cli/builtins/__init__.py
Normal file
0
bcrypter/cli/builtins/__init__.py
Normal file
46
bcrypter/cli/cmd.py
Normal file
46
bcrypter/cli/cmd.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
from bcrypter.lib.result import Result
|
||||
|
||||
class Command:
|
||||
NAME = '[Abstract]Command'
|
||||
ARGS = []
|
||||
FLAGS = []
|
||||
OPTIONS = []
|
||||
def __init__(self, args: list[string]) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def parse(cls: Command, cmd: list[str]) -> Result[Command]:
|
||||
for arg in cmd[1:]:
|
||||
# flag or option
|
||||
if arg.startswith('--'):
|
||||
match = cls._match_arg(arg[2:])
|
||||
if match
|
||||
|
||||
'''
|
||||
Check whether FLAGS and OPTIONS are defined consistently
|
||||
(ie no duplicate names or parsing ambiguity)
|
||||
'''
|
||||
@classmethod
|
||||
def _is_well_defined(cls: Command) -> Result[None]:
|
||||
raise NotImplementedException('Command.is_consistent()')
|
||||
|
||||
'''
|
||||
Attempt to match an arg to its flag or option
|
||||
NOTE: _match_arg() assumes _is_well_defined() == True
|
||||
'''
|
||||
@classmethod
|
||||
def _match_arg(cls: Command, arg: str) -> Result[None]:
|
||||
for opt in chain(cls.FLAGS, cls.OPTIONS):
|
||||
if opt.matches(arg):
|
||||
return Result.succeed(None)
|
||||
return Result.fail()
|
||||
|
||||
|
||||
class Builtin(Command):
|
||||
self.NAME = '[Abstract]Builtin'
|
||||
def __init__(self,
|
||||
repl_builtins: list[Builtin],
|
||||
repl_cmds: list[Command]) -> None:
|
||||
super().__init__()
|
||||
self._repl_builtins = repl_builtins
|
||||
self._repl_cmds = repl_cmds
|
||||
0
bcrypter/cli/commands/__init__.py
Normal file
0
bcrypter/cli/commands/__init__.py
Normal file
21
bcrypter/cli/opt.py
Normal file
21
bcrypter/cli/opt.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
from enum import Enum
|
||||
|
||||
class OptType(Enum):
|
||||
AbstractOpt # only used by Opt
|
||||
Flag
|
||||
Option
|
||||
|
||||
class Opt:
|
||||
_TYPE: OptType = OptType.AbstractOpt
|
||||
def __init__(self, *args) -> None:
|
||||
pass
|
||||
|
||||
class Flag(Opt):
|
||||
_TYPE: OptType = OptType.Flag
|
||||
def __init__(self, *args) -> None:
|
||||
super().__init__(*args)
|
||||
|
||||
class Option(Opt):
|
||||
_TYPE: OptType = OptType.Option
|
||||
def __init__(self, *args) -> None:
|
||||
super().__init__(*args)
|
||||
42
bcrypter/cli/repl.py
Normal file
42
bcrypter/cli/repl.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import readline # GNU readline (ie allows input() history buffer)
|
||||
from itertools import chain
|
||||
|
||||
from bcrypter.cli.builtins import *
|
||||
from bcrypter.cli.commands import *
|
||||
from bcrypter.lib.result import Result
|
||||
from bcrypter.exceptions import CmdDeclarationError
|
||||
|
||||
class REPL:
|
||||
_PROMPT = '>> '
|
||||
_DEFAULT_HISTORY_FILE = '.bcrypter_history'
|
||||
|
||||
_BUILTINS = [
|
||||
BuiltinHelp(),
|
||||
]
|
||||
_COMMANDS = [
|
||||
]
|
||||
|
||||
def __init__(self, history_file: str = _DEFAULT_HISTORY_FILE) -> None:
|
||||
for cmd in chain(REPL._BUILTINS, REPL._COMMANDS):
|
||||
result = cmd._is_consistent():
|
||||
if result.is_err():
|
||||
raise CmdDeclarationError(result.message)
|
||||
self._history_file = history_file
|
||||
readline.read_history_file(self._history_file)
|
||||
|
||||
def __del__(self) -> None:
|
||||
readline.write_history_file(self._history_file)
|
||||
|
||||
def prompt(self) -> str:
|
||||
return input(REPL._PROMPT)
|
||||
|
||||
'''
|
||||
Parse and execute a string command
|
||||
'''
|
||||
def exec(self, cmd: str) -> Result[Command]:
|
||||
cmd = cmd.strip().split()
|
||||
if not len(cmd):
|
||||
return Result.warn('No command given')
|
||||
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue