public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
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 ---


             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: link
Be 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