From: "André Erdmann" <dywi@mailerd.de> To: gentoo-commits@lists.gentoo.org Subject: [gentoo-commits] proj/R_overlay:master commit in: roverlay/console/ Date: Tue, 23 Jul 2013 07:51:31 +0000 (UTC) [thread overview] Message-ID: <1374175535.f59d23792974b73d890cac678033fabe70ae0bf1.dywi@gentoo> (raw) commit: f59d23792974b73d890cac678033fabe70ae0bf1 Author: André Erdmann <dywi <AT> mailerd <DOT> de> AuthorDate: Thu Jul 18 19:22:32 2013 +0000 Commit: André Erdmann <dywi <AT> mailerd <DOT> de> CommitDate: Thu Jul 18 19:25:35 2013 +0000 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=f59d2379 roverlay/console: console reimplementation roverlay.console features tab-completion, command history, a strict control flow and a clean(er) split between interpreter (command parser) code and actual functionality (mostly kept in roverlay.interface). roverlay.console.depres offers a dependency resolution console, which will replace the old one (roverlay.depres.simpledeprule.console) soon. --- roverlay/console/__init__.py | 5 + roverlay/console/base.py | 75 ++++++++ roverlay/console/depres.py | 143 +++++++++++++++ roverlay/console/interpreter.py | 384 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 607 insertions(+) diff --git a/roverlay/console/__init__.py b/roverlay/console/__init__.py new file mode 100644 index 0000000..d71d876 --- /dev/null +++ b/roverlay/console/__init__.py @@ -0,0 +1,5 @@ +# R overlay -- +# -*- coding: utf-8 -*- +# Copyright (C) 2013 André Erdmann <dywi@mailerd.de> +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. diff --git a/roverlay/console/base.py b/roverlay/console/base.py new file mode 100644 index 0000000..ca3c7a0 --- /dev/null +++ b/roverlay/console/base.py @@ -0,0 +1,75 @@ +# R overlay -- +# -*- coding: utf-8 -*- +# Copyright (C) 2013 André Erdmann <dywi@mailerd.de> +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. + +import sys + +import roverlay.interface.main +import roverlay.console.interpreter + +class RoverlayConsole ( object ): + pass + +class MainConsole ( RoverlayConsole ): + + INTERPRETER_CLS = roverlay.console.interpreter.ConsoleInterpreter + + def __init__ ( self, config_file=None ): + super ( MainConsole, self ).__init__() + self.config = None + self.root_interface = roverlay.interface.main.MainInterface() + self.interpreter = self.INTERPRETER_CLS() + self.interface = None + + if config_file is not None: + self.setup ( config_file ) + # --- end of __init__ (...) --- + + def get_interface ( self ): + return self.root_interface + + def has_config ( self ): + return self.config is not None + # --- end of has_config (...) --- + + def setup ( self, config_file ): + self.root_interface.setup ( config_file ) + + self.config = self.root_interface.config + self.interface = self.get_interface() + + self.interpreter.setup ( self.interface ) + # --- end of setup (...) --- + + def run ( self ): + self.interpreter.cmdloop() + # --- end of run (...) --- + + def _want_resume ( self ): + if self.interpreter.state.is_paused(): + return False + elif self.interpreter.state == self.interpreter.state.STATE_QUIT: + return False + else: + return True + # --- end of _want_resume (...) --- + + def run_forever ( self ): + retry = True + while retry: + retry = False + try: + self.run() + except roverlay.console.interpreter.ConsoleException as ce: + sys.stderr.write ( + "{}: {}\n".format ( ce.__class__.__name__, str ( ce ) ) + ) + retry = self._want_resume() + except KeyboardInterrupt: + sys.stdout.write ( '\n^C\n' ) + retry = self._want_resume() + # --- end of run_forever (...) --- + +# --- end of MainConsole --- diff --git a/roverlay/console/depres.py b/roverlay/console/depres.py new file mode 100644 index 0000000..ce6a11b --- /dev/null +++ b/roverlay/console/depres.py @@ -0,0 +1,143 @@ +# R overlay -- +# -*- coding: utf-8 -*- +# Copyright (C) 2013 André Erdmann <dywi@mailerd.de> +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. + +import sys + +import roverlay.console.base +import roverlay.console.interpreter + +# hidden import +#import roverlay.interface.depres + +# TODO/FIXME: turn off dep resolver logging: "RESOLVED: this as that" + +class DepresConsoleInterpreter ( + roverlay.console.interpreter.ConsoleInterpreter +): + def __init__ ( self, *args, **kwargs ): + super ( DepresConsoleInterpreter, self ).__init__ ( *args, **kwargs ) + self.intro = "depres console (r2)" + + def setup_aliases ( self ): + self.add_alias ( "help", "h" ) + + # rule pool management + self.add_alias ( "add_pool", "<<" ) + self.add_alias ( "unwind_pool", ">>" ) + self.add_alias ( "print_pool", "p" ) + + # rule creation + self.add_alias ( "add_rule", "+" ) + self.add_alias ( "load_conf", "lc" ) + + # dep res + self.add_alias ( "resolve", "??" ) + # --- end of setup_aliases (...) --- + + + def reset ( self, soft=True ): + super ( DepresConsoleInterpreter, self ).reset ( soft=soft ) + if not soft: + self.interface.discard_pools() + + def do_resolve ( self, line ): + """Resolve a dependency string. + + Usage: + * resolve <dependency string> + * ?? <dependency string> + + Examples: + * resolve R + * resolve fftw 3.2 + """ + if line: + ret = self.interface.resolve ( line ) + if ret is None: + sys.stdout.write ( "{!r} could not be resolved.\n".format ( line ) ) + elif not ret: + sys.stdout.write ( + "{!r} has been resolved as nothing(?)\n".format ( line ) + ) + elif len ( ret ) == 1: + sys.stdout.write ( + "{!r} has been resolved as {}.\n".format ( + line, ( + ret[0].dep if ( ret[0] and ret[0].dep is not None ) + else "<ignored>" + ) + ) + ) + else: + sys.stdout.write ( + "{!r} has been resolved as {}.\n".format ( + line, ( ', '.join ( str ( dep ) for dep in ret ) ) + ) + ) + else: + sys.stderr.write ( "Usage: resolve <dependency string>\n" ) + # --- end of do_resolve (...) --- + + def do_add_pool ( self, line ): + """Creates a new rule pool on top of the existing ones.""" + self.interface.get_new_pool() + # --- end of do_add_pool (...) --- + + def do_unwind_pool ( self, line ): + """Removes the topmost rule pool.""" + if self.interface.discard_pool(): + sys.stdout.write ( "pool has been removed.\n" ) + else: + sys.stdout.write ( "resolver has no pools.\n" ) + # --- end of do_unwind_pool (...) --- + + def do_add_rule ( self, line ): + """Adds a rule. Rules have to be given in rule file syntax. + + Usage: + * add_rule <str> + * + <str> + + Examples: + * add_rule dev-lang/R :: R + """ + if self._cmdbuffer: + self.interface.add_rule_list ( self.get_argbuffer() ) + elif line: + self.interface.add_rule ( line ) + else: + self.warn_usage() + + # compile rules if not inside of a rule + self.interface.try_compile_rules() + # --- end of do_add_rule (...) --- + + def do_print_pool ( self, line ): + """Prints the topmost pool (TODO:: or all).""" + sys.stdout.write ( self.interface.visualize_pool() ) + sys.stdout.write ( "\n" ) + # --- end of do_print_pool (...) --- + + def do_print ( self, line ): + """Prints the topmost pool (TODO:: or all).""" + return self.do_print_pool ( line ) + # --- end of do_print (...) --- + + def do_load_conf ( self, line ): + """Load configured dependency rule files.""" + self.interface.discard_empty_pools() + if not self.interface.load_rules_from_config ( ignore_missing=True ): + sys.stderr.write ( "failed to load rule files!\n" ) + + # --- end of do_load_conf (...) --- + + + +class DepresConsole ( roverlay.console.base.MainConsole ): + INTERPRETER_CLS = DepresConsoleInterpreter + + def get_interface ( self ): + return self.root_interface.spawn_interface ( "depres" ) diff --git a/roverlay/console/interpreter.py b/roverlay/console/interpreter.py new file mode 100644 index 0000000..cf1d106 --- /dev/null +++ b/roverlay/console/interpreter.py @@ -0,0 +1,384 @@ +# R overlay -- +# -*- coding: utf-8 -*- +# Copyright (C) 2013 André Erdmann <dywi@mailerd.de> +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. + +import collections +import sys +import cmd + +def fcopy ( func, name=None, mark_as_alias=True ): + """Creates an alias to func.""" + def wrapped ( *args, **kwargs ): + return func ( *args, **kwargs ) + + wrapped.__name__ = name if name is not None else func.__name__ + wrapped.__doc__ = func.__doc__ + wrapped.__dict__.update ( func.__dict__ ) + if mark_as_alias: + wrapped.is_alias = True + + return wrapped +# --- end of fcopy (...) --- + +class RingBuffer ( collections.deque ): + def __init__ ( self, max_size ): + super ( RingBuffer, self ).__init__() + self.max_size = int ( max_size ) + + def reset ( self, max_size=None ): + if max_size is not None: + self.max_size = int ( max_size ) + self.clear() + + def is_full ( self ): + return len ( self ) >= self.max_size + + def append ( self, value ): + if self.is_full(): + self.popleft() + super ( RingBuffer, self ).append ( value ) + + +class CommandHistory ( RingBuffer ): + + def __init__ ( self, max_size=100 ): + super ( CommandHistory, self ).__init__ ( max_size=max_size ) + + + + +class ConsoleException ( Exception ): + pass + +class ConsoleStatusException ( ConsoleException ): + pass + +class ConsoleInterpreterStatus ( object ): + """Object that represents the status of a ConsoleInterpreter.""" + + # STATE := {0..N} + # overall, there are 4 (5) states + # * the undefined state + # * exiting + # * ready to parse + # * parsing a command + # * executing a command + # + STATE_UNDEF = 0 + STATE_QUIT = 1 + STATE_READY = 2 + STATE_CMD_PARSE = 3 + STATE_CMD_EXEC = 4 + + STATE_TRANSITION_TABLE = { + STATE_UNDEF : frozenset(), + STATE_QUIT : frozenset(), + STATE_READY : frozenset ({ STATE_CMD_PARSE, STATE_CMD_EXEC }), + STATE_CMD_PARSE : frozenset ({ + STATE_READY, STATE_CMD_EXEC, STATE_CMD_PARSE + }), + STATE_CMD_EXEC : frozenset ({ STATE_READY, STATE_QUIT }), + } + + # FLAGS := {0,} | {2^k for k in 0..N} + FLAGS_UNDEF = 0 + FLAGS_CONFIGURED = 1 + FLAGS_ONERROR = 2 + + @classmethod + def get_state ( cls, name ): + return getattr ( cls, "STATE_" + name.upper() ) + # --- end of get_state (...) --- + + def __init__ ( self ): + super ( ConsoleInterpreterStatus, self ).__init__() + self.state = self.STATE_UNDEF + self.flags = self.FLAGS_UNDEF + # --- end of __init__ (...) --- + + def set_flag ( self, flag ): + self.flags |= flag + + def clear_flag ( self, flag ): + self.flags &= ~flag + + def has_flag ( self, flag ): + return bool ( self.flags & flag ) + + def set_configured ( self ): + self.flags |= self.FLAGS_CONFIGURED + # --- end of set_configured (...) --- + + def reset ( self ): + self.state = self.STATE_READY + self.clear_flag ( self.FLAGS_ONERROR ) + # --- end of reset (...) --- + + def __eq__ ( self, other ): + if isinstance ( other, int ): + return self.state == other + else: + raise NotImplementedError() + + def __ne__ ( self, other ): + if isinstance ( other, int ): + return self.state != other + else: + raise NotImplementedError() + + + def goto ( self, next_state ): + """state transition""" + #if "self.state => next_state" allowed + #returns ?? + + i_next_state = self.get_state ( next_state ) + + if i_next_state in self.STATE_TRANSITION_TABLE [self.state]: + self.state = i_next_state + return True + else: + raise ConsoleStatusException ( + "invalid state transition {src}->{dest} ({name!r}).".format ( + src=self.state, dest=self.get_state ( next_state ), + name=next_state + ) + ) + # --- end of goto (...) --- + + def force ( self, next_state="READY" ): + """forced state transitition""" + self.state = self.get_state ( next_state ) + return True + # --- end of force (...) --- + + def is_paused ( self ): + return False + + #def __str__ ... + + +# --- end of ConsoleInterpreterStatus --- + +class ConsoleInterpreter ( cmd.Cmd ): + # TODO: line continuation when "\" at the end of a line + + def __init__ ( self, *args, **kwargs ): + super ( ConsoleInterpreter, self ).__init__ ( *args, **kwargs ) + self.state = ConsoleInterpreterStatus() + self.interface = None + + self._locals = {} + # for printing the history + self._history = CommandHistory() + # name => real command name + self._alias = {} + self._cmdbuffer = None + + self.MULTILINE_JOIN = ' ' + + self.DEFAULT_PS1 = 'cmd %' + self.DEFAULT_PS2 = '>' + #self.PS3 = '' + #self.PS4 = '+ ' + + self.intro = "roverlay console" + + self.setup_aliases() + # --- end of __init__ (...) --- + + def setup_aliases ( self ): + pass + # --- end of setup_aliases (...) --- + + def is_onerror ( self ): + return self.state.has_flag ( ConsoleInterpreterStatus.FLAGS_ONERROR ) + # --- end of is_onerror (...) --- + + def set_onerror ( self ): + return self.state.set_flag ( ConsoleInterpreterStatus.FLAGS_ONERROR ) + # --- end of set_onerror (...) --- + + def clear_onerror ( self ): + return self.state.clear_flag ( ConsoleInterpreterStatus.FLAGS_ONERROR ) + # --- end of clear_onerror (...) --- + + def add_alias ( self, dest, *aliases ): + if hasattr ( self, 'do_' + dest ): + for alias in aliases: + self._alias [alias] = dest + return True + elif self.state == ConsoleInterpreterStatus.STATE_UNDEF: + raise AssertionError ( "no such function: do_{}".format ( dest ) ) + else: + sys.stderr.write ( "alias: do_{} does not exist\n".format ( dest ) ) + return False + + def reset ( self, soft=True ): + self.state.reset() + self._cmdbuffer = None + self.prompt = self._locals.get ( "PS1", self.DEFAULT_PS1 ) + ' ' + + def unalias_cmdline ( self, line ): + if line: + lc = line.split ( None, 1 ) + unaliased = self._alias.get ( lc[0] ) if lc[0] else None + + if unaliased: + if len ( lc ) > 1: + return unaliased + ' ' + lc[1] + else: + return unaliased + else: + return line + else: + return line + # --- end of unalias_cmdline (...) --- + + def warn_usage ( self ): + sys.stderr.write ( "{}: bad usage.\n".format ( self.lastcmd ) ) + + def get_argbuffer ( self ): + return self._cmdbuffer[1:] + # --- end of get_linebuffer (...) --- + + def precmd ( self, line ): + sline = line.strip() + + if sline and sline[-1] == chr ( 92 ): + # "\" at end of line => line continuation + self.state.goto ( "CMD_PARSE" ) + self.prompt = self._locals.get ( "PS2", self.DEFAULT_PS2 ) + ' ' + + if self._cmdbuffer is None: + # unalias + self._cmdbuffer = sline[:-1].rstrip().split ( None, 1 ) + if self._cmdbuffer[0] in self._alias: + self._cmdbuffer[0] = self._alias [self._cmdbuffer[0]] + else: + self._cmdbuffer.append ( sline[:-1].rstrip() ) + + return "" + elif self._cmdbuffer: + self._cmdbuffer.append ( sline ) + + self.state.goto ( "CMD_EXEC" ) + return self.MULTILINE_JOIN.join ( self._cmdbuffer ) + else: + # unalias + self.state.goto ( "CMD_EXEC" ) + return self.unalias_cmdline ( line ) + # --- end of precmd (...) --- + + def postcmd ( self, stop, line ): + if self.state == ConsoleInterpreterStatus.STATE_CMD_EXEC: + if self.is_onerror(): + self.clear_onerror() + elif self.lastcmd != "history": + self._history.append ( line ) + + self.state.goto ( "READY" ) + self._cmdbuffer = None + self.prompt = self._locals.get ( "PS1", self.DEFAULT_PS1 ) + ' ' + + return stop + # --- end of postcmd (...) --- + + def onecmd ( self, *a, **b ): + if self.state == ConsoleInterpreterStatus.STATE_CMD_EXEC: + return super ( ConsoleInterpreter, self ).onecmd ( *a, **b ) + else: + # suppress command + return None + # --- end of onecmd (...) --- + + def emptyline ( self ): + pass + # -- end of emptyline (...) --- + + def preloop ( self ): + if not self.state.is_paused(): + self.reset ( soft=True ) + # --- end of preloop (...) --- + + def setup ( self, interface ): + self.interface = interface + self.state.set_configured() + return True + # --- end of setup (...) --- + + def do_alias ( self, line ): + """Show/set aliases (currently only shows all aliases).""" + alen = 1 + len ( max ( self._alias, key=lambda k: len ( k ) ) ) + + sys.stdout.write ( '\n'.join ( + "{:<{l}} is {!r}".format ( alias, name, l=alen ) + for alias, name in self._alias.items() + ) ) + sys.stdout.write ( '\n' ) + # --- end of do_alias (...) --- + + def do_unalias ( self, line ): + """Unsets an alias.""" + sys.stderr.write ( "unalias: method stub\n" ) + # --- end of do_unalias (...) --- + + def do_history ( self, line ): + """Shows the command history.""" + sys.stdout.write ( '\n'.join ( l for l in self._history ) ) + sys.stdout.write ( '\n' ) + # --- end of history (...) --- + + def do_set ( self, line ): + """Sets a variable. + + Usage: set VAR=VALUE + + Examples: + * set PS1=cmd % + * set dep=fftw 3 + """ + name, sepa, value = line.partition ( '=' ) + if not sepa: + sys.stderr.write ( "set, bad syntax: {}\n".format ( line ) ) + else: + self._locals [name.strip()] = value + # --- end of do_set (...) --- + + def do_unset ( self, line ): + """Unsets zero or more variables. + + Usage: unset VAR0 [VAR1...] + + Examples: + * unset PS1 + """ + for varname in line.split ( None ): + try: + del self._locals [varname] + except KeyError: + pass + # --- end of do_unset (...) --- + + def do_quit ( self, *a ): + """Exit""" + sys.stdout.flush() + sys.stderr.flush() + self.state.goto ( "QUIT" ) + return True + + def do_exit ( self, *a ): + """Exit""" + return self.do_quit() + + def do_q ( self, *a ): + """Exit""" + return self.do_quit() + + def do_EOF ( self, *a ): + """Exit""" + sys.stdout.write ( '\n' ) + return self.do_quit() +# --- end of ConsoleInterpreter ---
WARNING: multiple messages have this Message-ID (diff)
From: "André Erdmann" <dywi@mailerd.de> To: gentoo-commits@lists.gentoo.org Subject: [gentoo-commits] proj/R_overlay:gsoc13/next commit in: roverlay/console/ Date: Thu, 18 Jul 2013 19:25:57 +0000 (UTC) [thread overview] Message-ID: <1374175535.f59d23792974b73d890cac678033fabe70ae0bf1.dywi@gentoo> (raw) Message-ID: <20130718192557.jwJa23XXDVJ3kO3m_hkRXTFHh1sfwoL9MZvcqFriigI@z> (raw) commit: f59d23792974b73d890cac678033fabe70ae0bf1 Author: André Erdmann <dywi <AT> mailerd <DOT> de> AuthorDate: Thu Jul 18 19:22:32 2013 +0000 Commit: André Erdmann <dywi <AT> mailerd <DOT> de> CommitDate: Thu Jul 18 19:25:35 2013 +0000 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=f59d2379 roverlay/console: console reimplementation roverlay.console features tab-completion, command history, a strict control flow and a clean(er) split between interpreter (command parser) code and actual functionality (mostly kept in roverlay.interface). roverlay.console.depres offers a dependency resolution console, which will replace the old one (roverlay.depres.simpledeprule.console) soon. --- roverlay/console/__init__.py | 5 + roverlay/console/base.py | 75 ++++++++ roverlay/console/depres.py | 143 +++++++++++++++ roverlay/console/interpreter.py | 384 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 607 insertions(+) diff --git a/roverlay/console/__init__.py b/roverlay/console/__init__.py new file mode 100644 index 0000000..d71d876 --- /dev/null +++ b/roverlay/console/__init__.py @@ -0,0 +1,5 @@ +# R overlay -- +# -*- coding: utf-8 -*- +# Copyright (C) 2013 André Erdmann <dywi@mailerd.de> +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. diff --git a/roverlay/console/base.py b/roverlay/console/base.py new file mode 100644 index 0000000..ca3c7a0 --- /dev/null +++ b/roverlay/console/base.py @@ -0,0 +1,75 @@ +# R overlay -- +# -*- coding: utf-8 -*- +# Copyright (C) 2013 André Erdmann <dywi@mailerd.de> +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. + +import sys + +import roverlay.interface.main +import roverlay.console.interpreter + +class RoverlayConsole ( object ): + pass + +class MainConsole ( RoverlayConsole ): + + INTERPRETER_CLS = roverlay.console.interpreter.ConsoleInterpreter + + def __init__ ( self, config_file=None ): + super ( MainConsole, self ).__init__() + self.config = None + self.root_interface = roverlay.interface.main.MainInterface() + self.interpreter = self.INTERPRETER_CLS() + self.interface = None + + if config_file is not None: + self.setup ( config_file ) + # --- end of __init__ (...) --- + + def get_interface ( self ): + return self.root_interface + + def has_config ( self ): + return self.config is not None + # --- end of has_config (...) --- + + def setup ( self, config_file ): + self.root_interface.setup ( config_file ) + + self.config = self.root_interface.config + self.interface = self.get_interface() + + self.interpreter.setup ( self.interface ) + # --- end of setup (...) --- + + def run ( self ): + self.interpreter.cmdloop() + # --- end of run (...) --- + + def _want_resume ( self ): + if self.interpreter.state.is_paused(): + return False + elif self.interpreter.state == self.interpreter.state.STATE_QUIT: + return False + else: + return True + # --- end of _want_resume (...) --- + + def run_forever ( self ): + retry = True + while retry: + retry = False + try: + self.run() + except roverlay.console.interpreter.ConsoleException as ce: + sys.stderr.write ( + "{}: {}\n".format ( ce.__class__.__name__, str ( ce ) ) + ) + retry = self._want_resume() + except KeyboardInterrupt: + sys.stdout.write ( '\n^C\n' ) + retry = self._want_resume() + # --- end of run_forever (...) --- + +# --- end of MainConsole --- diff --git a/roverlay/console/depres.py b/roverlay/console/depres.py new file mode 100644 index 0000000..ce6a11b --- /dev/null +++ b/roverlay/console/depres.py @@ -0,0 +1,143 @@ +# R overlay -- +# -*- coding: utf-8 -*- +# Copyright (C) 2013 André Erdmann <dywi@mailerd.de> +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. + +import sys + +import roverlay.console.base +import roverlay.console.interpreter + +# hidden import +#import roverlay.interface.depres + +# TODO/FIXME: turn off dep resolver logging: "RESOLVED: this as that" + +class DepresConsoleInterpreter ( + roverlay.console.interpreter.ConsoleInterpreter +): + def __init__ ( self, *args, **kwargs ): + super ( DepresConsoleInterpreter, self ).__init__ ( *args, **kwargs ) + self.intro = "depres console (r2)" + + def setup_aliases ( self ): + self.add_alias ( "help", "h" ) + + # rule pool management + self.add_alias ( "add_pool", "<<" ) + self.add_alias ( "unwind_pool", ">>" ) + self.add_alias ( "print_pool", "p" ) + + # rule creation + self.add_alias ( "add_rule", "+" ) + self.add_alias ( "load_conf", "lc" ) + + # dep res + self.add_alias ( "resolve", "??" ) + # --- end of setup_aliases (...) --- + + + def reset ( self, soft=True ): + super ( DepresConsoleInterpreter, self ).reset ( soft=soft ) + if not soft: + self.interface.discard_pools() + + def do_resolve ( self, line ): + """Resolve a dependency string. + + Usage: + * resolve <dependency string> + * ?? <dependency string> + + Examples: + * resolve R + * resolve fftw 3.2 + """ + if line: + ret = self.interface.resolve ( line ) + if ret is None: + sys.stdout.write ( "{!r} could not be resolved.\n".format ( line ) ) + elif not ret: + sys.stdout.write ( + "{!r} has been resolved as nothing(?)\n".format ( line ) + ) + elif len ( ret ) == 1: + sys.stdout.write ( + "{!r} has been resolved as {}.\n".format ( + line, ( + ret[0].dep if ( ret[0] and ret[0].dep is not None ) + else "<ignored>" + ) + ) + ) + else: + sys.stdout.write ( + "{!r} has been resolved as {}.\n".format ( + line, ( ', '.join ( str ( dep ) for dep in ret ) ) + ) + ) + else: + sys.stderr.write ( "Usage: resolve <dependency string>\n" ) + # --- end of do_resolve (...) --- + + def do_add_pool ( self, line ): + """Creates a new rule pool on top of the existing ones.""" + self.interface.get_new_pool() + # --- end of do_add_pool (...) --- + + def do_unwind_pool ( self, line ): + """Removes the topmost rule pool.""" + if self.interface.discard_pool(): + sys.stdout.write ( "pool has been removed.\n" ) + else: + sys.stdout.write ( "resolver has no pools.\n" ) + # --- end of do_unwind_pool (...) --- + + def do_add_rule ( self, line ): + """Adds a rule. Rules have to be given in rule file syntax. + + Usage: + * add_rule <str> + * + <str> + + Examples: + * add_rule dev-lang/R :: R + """ + if self._cmdbuffer: + self.interface.add_rule_list ( self.get_argbuffer() ) + elif line: + self.interface.add_rule ( line ) + else: + self.warn_usage() + + # compile rules if not inside of a rule + self.interface.try_compile_rules() + # --- end of do_add_rule (...) --- + + def do_print_pool ( self, line ): + """Prints the topmost pool (TODO:: or all).""" + sys.stdout.write ( self.interface.visualize_pool() ) + sys.stdout.write ( "\n" ) + # --- end of do_print_pool (...) --- + + def do_print ( self, line ): + """Prints the topmost pool (TODO:: or all).""" + return self.do_print_pool ( line ) + # --- end of do_print (...) --- + + def do_load_conf ( self, line ): + """Load configured dependency rule files.""" + self.interface.discard_empty_pools() + if not self.interface.load_rules_from_config ( ignore_missing=True ): + sys.stderr.write ( "failed to load rule files!\n" ) + + # --- end of do_load_conf (...) --- + + + +class DepresConsole ( roverlay.console.base.MainConsole ): + INTERPRETER_CLS = DepresConsoleInterpreter + + def get_interface ( self ): + return self.root_interface.spawn_interface ( "depres" ) diff --git a/roverlay/console/interpreter.py b/roverlay/console/interpreter.py new file mode 100644 index 0000000..cf1d106 --- /dev/null +++ b/roverlay/console/interpreter.py @@ -0,0 +1,384 @@ +# R overlay -- +# -*- coding: utf-8 -*- +# Copyright (C) 2013 André Erdmann <dywi@mailerd.de> +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. + +import collections +import sys +import cmd + +def fcopy ( func, name=None, mark_as_alias=True ): + """Creates an alias to func.""" + def wrapped ( *args, **kwargs ): + return func ( *args, **kwargs ) + + wrapped.__name__ = name if name is not None else func.__name__ + wrapped.__doc__ = func.__doc__ + wrapped.__dict__.update ( func.__dict__ ) + if mark_as_alias: + wrapped.is_alias = True + + return wrapped +# --- end of fcopy (...) --- + +class RingBuffer ( collections.deque ): + def __init__ ( self, max_size ): + super ( RingBuffer, self ).__init__() + self.max_size = int ( max_size ) + + def reset ( self, max_size=None ): + if max_size is not None: + self.max_size = int ( max_size ) + self.clear() + + def is_full ( self ): + return len ( self ) >= self.max_size + + def append ( self, value ): + if self.is_full(): + self.popleft() + super ( RingBuffer, self ).append ( value ) + + +class CommandHistory ( RingBuffer ): + + def __init__ ( self, max_size=100 ): + super ( CommandHistory, self ).__init__ ( max_size=max_size ) + + + + +class ConsoleException ( Exception ): + pass + +class ConsoleStatusException ( ConsoleException ): + pass + +class ConsoleInterpreterStatus ( object ): + """Object that represents the status of a ConsoleInterpreter.""" + + # STATE := {0..N} + # overall, there are 4 (5) states + # * the undefined state + # * exiting + # * ready to parse + # * parsing a command + # * executing a command + # + STATE_UNDEF = 0 + STATE_QUIT = 1 + STATE_READY = 2 + STATE_CMD_PARSE = 3 + STATE_CMD_EXEC = 4 + + STATE_TRANSITION_TABLE = { + STATE_UNDEF : frozenset(), + STATE_QUIT : frozenset(), + STATE_READY : frozenset ({ STATE_CMD_PARSE, STATE_CMD_EXEC }), + STATE_CMD_PARSE : frozenset ({ + STATE_READY, STATE_CMD_EXEC, STATE_CMD_PARSE + }), + STATE_CMD_EXEC : frozenset ({ STATE_READY, STATE_QUIT }), + } + + # FLAGS := {0,} | {2^k for k in 0..N} + FLAGS_UNDEF = 0 + FLAGS_CONFIGURED = 1 + FLAGS_ONERROR = 2 + + @classmethod + def get_state ( cls, name ): + return getattr ( cls, "STATE_" + name.upper() ) + # --- end of get_state (...) --- + + def __init__ ( self ): + super ( ConsoleInterpreterStatus, self ).__init__() + self.state = self.STATE_UNDEF + self.flags = self.FLAGS_UNDEF + # --- end of __init__ (...) --- + + def set_flag ( self, flag ): + self.flags |= flag + + def clear_flag ( self, flag ): + self.flags &= ~flag + + def has_flag ( self, flag ): + return bool ( self.flags & flag ) + + def set_configured ( self ): + self.flags |= self.FLAGS_CONFIGURED + # --- end of set_configured (...) --- + + def reset ( self ): + self.state = self.STATE_READY + self.clear_flag ( self.FLAGS_ONERROR ) + # --- end of reset (...) --- + + def __eq__ ( self, other ): + if isinstance ( other, int ): + return self.state == other + else: + raise NotImplementedError() + + def __ne__ ( self, other ): + if isinstance ( other, int ): + return self.state != other + else: + raise NotImplementedError() + + + def goto ( self, next_state ): + """state transition""" + #if "self.state => next_state" allowed + #returns ?? + + i_next_state = self.get_state ( next_state ) + + if i_next_state in self.STATE_TRANSITION_TABLE [self.state]: + self.state = i_next_state + return True + else: + raise ConsoleStatusException ( + "invalid state transition {src}->{dest} ({name!r}).".format ( + src=self.state, dest=self.get_state ( next_state ), + name=next_state + ) + ) + # --- end of goto (...) --- + + def force ( self, next_state="READY" ): + """forced state transitition""" + self.state = self.get_state ( next_state ) + return True + # --- end of force (...) --- + + def is_paused ( self ): + return False + + #def __str__ ... + + +# --- end of ConsoleInterpreterStatus --- + +class ConsoleInterpreter ( cmd.Cmd ): + # TODO: line continuation when "\" at the end of a line + + def __init__ ( self, *args, **kwargs ): + super ( ConsoleInterpreter, self ).__init__ ( *args, **kwargs ) + self.state = ConsoleInterpreterStatus() + self.interface = None + + self._locals = {} + # for printing the history + self._history = CommandHistory() + # name => real command name + self._alias = {} + self._cmdbuffer = None + + self.MULTILINE_JOIN = ' ' + + self.DEFAULT_PS1 = 'cmd %' + self.DEFAULT_PS2 = '>' + #self.PS3 = '' + #self.PS4 = '+ ' + + self.intro = "roverlay console" + + self.setup_aliases() + # --- end of __init__ (...) --- + + def setup_aliases ( self ): + pass + # --- end of setup_aliases (...) --- + + def is_onerror ( self ): + return self.state.has_flag ( ConsoleInterpreterStatus.FLAGS_ONERROR ) + # --- end of is_onerror (...) --- + + def set_onerror ( self ): + return self.state.set_flag ( ConsoleInterpreterStatus.FLAGS_ONERROR ) + # --- end of set_onerror (...) --- + + def clear_onerror ( self ): + return self.state.clear_flag ( ConsoleInterpreterStatus.FLAGS_ONERROR ) + # --- end of clear_onerror (...) --- + + def add_alias ( self, dest, *aliases ): + if hasattr ( self, 'do_' + dest ): + for alias in aliases: + self._alias [alias] = dest + return True + elif self.state == ConsoleInterpreterStatus.STATE_UNDEF: + raise AssertionError ( "no such function: do_{}".format ( dest ) ) + else: + sys.stderr.write ( "alias: do_{} does not exist\n".format ( dest ) ) + return False + + def reset ( self, soft=True ): + self.state.reset() + self._cmdbuffer = None + self.prompt = self._locals.get ( "PS1", self.DEFAULT_PS1 ) + ' ' + + def unalias_cmdline ( self, line ): + if line: + lc = line.split ( None, 1 ) + unaliased = self._alias.get ( lc[0] ) if lc[0] else None + + if unaliased: + if len ( lc ) > 1: + return unaliased + ' ' + lc[1] + else: + return unaliased + else: + return line + else: + return line + # --- end of unalias_cmdline (...) --- + + def warn_usage ( self ): + sys.stderr.write ( "{}: bad usage.\n".format ( self.lastcmd ) ) + + def get_argbuffer ( self ): + return self._cmdbuffer[1:] + # --- end of get_linebuffer (...) --- + + def precmd ( self, line ): + sline = line.strip() + + if sline and sline[-1] == chr ( 92 ): + # "\" at end of line => line continuation + self.state.goto ( "CMD_PARSE" ) + self.prompt = self._locals.get ( "PS2", self.DEFAULT_PS2 ) + ' ' + + if self._cmdbuffer is None: + # unalias + self._cmdbuffer = sline[:-1].rstrip().split ( None, 1 ) + if self._cmdbuffer[0] in self._alias: + self._cmdbuffer[0] = self._alias [self._cmdbuffer[0]] + else: + self._cmdbuffer.append ( sline[:-1].rstrip() ) + + return "" + elif self._cmdbuffer: + self._cmdbuffer.append ( sline ) + + self.state.goto ( "CMD_EXEC" ) + return self.MULTILINE_JOIN.join ( self._cmdbuffer ) + else: + # unalias + self.state.goto ( "CMD_EXEC" ) + return self.unalias_cmdline ( line ) + # --- end of precmd (...) --- + + def postcmd ( self, stop, line ): + if self.state == ConsoleInterpreterStatus.STATE_CMD_EXEC: + if self.is_onerror(): + self.clear_onerror() + elif self.lastcmd != "history": + self._history.append ( line ) + + self.state.goto ( "READY" ) + self._cmdbuffer = None + self.prompt = self._locals.get ( "PS1", self.DEFAULT_PS1 ) + ' ' + + return stop + # --- end of postcmd (...) --- + + def onecmd ( self, *a, **b ): + if self.state == ConsoleInterpreterStatus.STATE_CMD_EXEC: + return super ( ConsoleInterpreter, self ).onecmd ( *a, **b ) + else: + # suppress command + return None + # --- end of onecmd (...) --- + + def emptyline ( self ): + pass + # -- end of emptyline (...) --- + + def preloop ( self ): + if not self.state.is_paused(): + self.reset ( soft=True ) + # --- end of preloop (...) --- + + def setup ( self, interface ): + self.interface = interface + self.state.set_configured() + return True + # --- end of setup (...) --- + + def do_alias ( self, line ): + """Show/set aliases (currently only shows all aliases).""" + alen = 1 + len ( max ( self._alias, key=lambda k: len ( k ) ) ) + + sys.stdout.write ( '\n'.join ( + "{:<{l}} is {!r}".format ( alias, name, l=alen ) + for alias, name in self._alias.items() + ) ) + sys.stdout.write ( '\n' ) + # --- end of do_alias (...) --- + + def do_unalias ( self, line ): + """Unsets an alias.""" + sys.stderr.write ( "unalias: method stub\n" ) + # --- end of do_unalias (...) --- + + def do_history ( self, line ): + """Shows the command history.""" + sys.stdout.write ( '\n'.join ( l for l in self._history ) ) + sys.stdout.write ( '\n' ) + # --- end of history (...) --- + + def do_set ( self, line ): + """Sets a variable. + + Usage: set VAR=VALUE + + Examples: + * set PS1=cmd % + * set dep=fftw 3 + """ + name, sepa, value = line.partition ( '=' ) + if not sepa: + sys.stderr.write ( "set, bad syntax: {}\n".format ( line ) ) + else: + self._locals [name.strip()] = value + # --- end of do_set (...) --- + + def do_unset ( self, line ): + """Unsets zero or more variables. + + Usage: unset VAR0 [VAR1...] + + Examples: + * unset PS1 + """ + for varname in line.split ( None ): + try: + del self._locals [varname] + except KeyError: + pass + # --- end of do_unset (...) --- + + def do_quit ( self, *a ): + """Exit""" + sys.stdout.flush() + sys.stderr.flush() + self.state.goto ( "QUIT" ) + return True + + def do_exit ( self, *a ): + """Exit""" + return self.do_quit() + + def do_q ( self, *a ): + """Exit""" + return self.do_quit() + + def do_EOF ( self, *a ): + """Exit""" + sys.stdout.write ( '\n' ) + return self.do_quit() +# --- end of ConsoleInterpreter ---
next reply other threads:[~2013-07-23 7:51 UTC|newest] Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top 2013-07-23 7:51 André Erdmann [this message] 2013-07-18 19:25 ` [gentoo-commits] proj/R_overlay:gsoc13/next commit in: roverlay/console/ André Erdmann -- strict thread matches above, loose matches on Subject: below -- 2014-08-23 19:03 [gentoo-commits] proj/R_overlay:master " André Erdmann 2014-04-01 16:38 André Erdmann 2013-08-23 13:51 André Erdmann 2013-08-06 10:58 André Erdmann 2013-07-24 16:52 André Erdmann 2013-07-24 16:52 André Erdmann 2013-07-24 16:52 André Erdmann 2013-07-23 7:51 André Erdmann 2013-07-23 7:51 André Erdmann 2013-07-19 18:00 [gentoo-commits] proj/R_overlay:gsoc13/next " André Erdmann 2013-07-23 7:51 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=1374175535.f59d23792974b73d890cac678033fabe70ae0bf1.dywi@gentoo \ --to=dywi@mailerd.de \ --cc=gentoo-commits@lists.gentoo.org \ --cc=gentoo-dev@lists.gentoo.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox