summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Vu-Brugier <cvubrugier@yahoo.fr>2013-09-04 11:34:13 +0200
committerChristophe Vu-Brugier <cvubrugier@yahoo.fr>2013-09-12 10:34:25 +0200
commitd9cbcd0419f62912a5925efdbf556bba603fbeb5 (patch)
treed7d06700cd2498fc156665bc167ee9cc34a55c52
parentfb99f2cdbc91995d31a5eb3416e17b86c9285f14 (diff)
downloadconfigshell-fb-d9cbcd0419f62912a5925efdbf556bba603fbeb5.tar.gz
Replace simpleparse with pyparsing
Configshell uses simpleparse to parse the command line. Unfortunately, simpleparse does not seem to be maintained anymore: the last release was three years ago. Moreover, simpleparse is not widely used (on Debian, only configshell depends on it). On the other hand, pyparsing is actively maintained, widely used and ready for Python 3. So let's use it. Signed-off-by: Christophe Vu-Brugier <cvubrugier@yahoo.fr>
-rw-r--r--configshell/shell.py92
-rw-r--r--debian/control4
-rw-r--r--rpm/python-configshell.spec.tmpl4
3 files changed, 50 insertions, 50 deletions
diff --git a/configshell/shell.py b/configshell/shell.py
index a3fbbca..fb82f8d 100644
--- a/configshell/shell.py
+++ b/configshell/shell.py
@@ -17,8 +17,8 @@ under the License.
import os
import sys
-import simpleparse.parser
-import simpleparse.dispatchprocessor
+from pyparsing import Empty, Group, OneOrMore, Optional, ParseResults, Regex, Suppress, Word
+from pyparsing import alphanums
import configshell.log as log
import configshell.prefs as prefs
@@ -49,6 +49,12 @@ if sys.stdout.isatty():
else:
tty=False
+# Pyparsing helper to group the location of a token and its value
+# http://stackoverflow.com/questions/18706631/pyparsing-get-token-location-in-results-name
+locator = Empty().setParseAction(lambda s, l, t: l)
+def locatedExpr(expr):
+ return Group(locator('location') + expr('value'))
+
class ConfigShell(object):
'''
This is a simple CLI command interpreter that can be used both in
@@ -90,23 +96,6 @@ class ConfigShell(object):
_current_token = ''
_current_completions = []
complete_key = 'tab'
- grammar = \
- r'''
- <eol> := '\n'
- <space> := ' '+
- line := (linepath)/(space?, command), parameters?, eol?
- >linepath< := space?, path, space?, command?
- command := [a-zA-Z0-9_]+
- >parameters< := (space, kparam/pparam)+
- pparam := var
- kparam := keyword, '=', value
- >keyword< := [a-zA-Z0-9_\-]+
- >value< := var?
- path := bookmark/pathstd/[0-9]+/'..'/'.'/'*'
- <pathstd> := ([a-zA-Z0-9:_.-]*, '/', [a-zA-Z0-9:_./-]*, '*'?)
- <var> := [a-zA-Z0-9_\\+/.<>~@:-%]+
- <bookmark> := '@', var?
- '''
def __init__(self, preferences_dir=None):
'''
@@ -118,7 +107,21 @@ class ConfigShell(object):
self._root_node = None
self._exit = False
- self._parser = simpleparse.parser.Parser(self.grammar, root='line')
+ # Grammar of the command line
+ command = locatedExpr(Word(alphanums + '_'))('command')
+ var = Word(alphanums + '_\+/.<>()~@:-%]')
+ value = var
+ keyword = Word(alphanums + '_\-')
+ kparam = locatedExpr(keyword + Suppress('=') + Optional(value, default=''))('kparams*')
+ pparam = locatedExpr(var)('pparams*')
+ parameter = kparam | pparam
+ parameters = OneOrMore(parameter)
+ bookmark = Regex('@([A-Za-z0-9:_.]|-)+')
+ pathstd = Regex('([A-Za-z0-9:_.]|-)*' + '/' + '([A-Za-z0-9:_./]|-)*') \
+ | '..' | '.'
+ path = locatedExpr(bookmark | pathstd | '*')('path')
+ parser = Optional(path) + Optional(command) + Optional(parameters)
+ self._parser = parser
if tty:
readline.set_completer_delims('\t\n ~!#$^&()[{]}\|;\'",?')
@@ -676,7 +679,7 @@ class ConfigShell(object):
self._completion_help_topic = ''
self._current_parameter = ''
- (result_trees, path, command, pparams, kparams) = \
+ (parse_results, path, command, pparams, kparams) = \
self._parse_cmdline(cmdline)
beg = readline.get_begidx()
@@ -685,13 +688,17 @@ class ConfigShell(object):
# No text under the cursor, fake it so that the parser
# result_trees gives us a token name on a second parser call
self.log.debug("Faking text entry on commandline.")
- result_trees = self._parse_cmdline(cmdline + 'x')[0]
+ parse_results = self._parse_cmdline(cmdline + 'x')[0]
end += 1
- for tree in result_trees:
- if beg == tree[1] and end == tree[2]:
- current_token = tree[0]
- break
+ if path and beg == parse_results.path.location:
+ current_token = 'path'
+ elif command and beg == parse_results.command.location:
+ current_token = 'command'
+ elif pparams and beg in [p.location for p in parse_results.pparams]:
+ current_token = 'pparam'
+ elif kparams and beg in [k.location for k in parse_results.kparams]:
+ current_token = 'kparam'
self._current_completions = \
self._dispatch_completion(path, command,
@@ -825,41 +832,34 @@ class ConfigShell(object):
def _parse_cmdline(self, line):
'''
Parses the command line entered by the user. This is a wrapper around
- the actual simpleparse parsed that pre-chews the result trees to
+ the actual pyparsing parser that pre-chews the result trees to
cleanly extract the tokens we care for (parameters, path, command).
@param line: The command line to parse.
@type line: str
@return: (result_trees, path, command, pparams, kparams),
pparams being positional parameters and kparams the keyword=value.
- @rtype: (list of tuple, str, str, list, dict) For the exact
- result_trees tuple format, c.f. the simpleparse documentation.
+ @rtype: (pyparsing.ParseResults, str, str, list, dict)
'''
self.log.debug("Parsing commandline.")
path = ''
command = ''
pparams = []
kparams = {}
- (success, result_trees, next_character) = self._parser.parse(line)
- if success:
- self.log.debug(str(result_trees))
- for tree in result_trees:
- token = tree[0]
- value = line[tree[1]:tree[2]]
- self.log.debug("Found %s token %s." % (token, value))
- if token == 'path':
- path = value
- elif token == 'command':
- command = value
- elif token == 'pparam':
- pparams.append(value)
- elif token == 'kparam':
- (keyword, sep, value) = value.partition('=')
- kparams[keyword] = value
+
+ parse_results = self._parser.parseString(line)
+ if isinstance(parse_results.path, ParseResults):
+ path = parse_results.path.value
+ if isinstance(parse_results.command, ParseResults):
+ command = parse_results.command.value
+ if isinstance(parse_results.pparams, ParseResults):
+ pparams = [pparam.value for pparam in parse_results.pparams]
+ if isinstance(parse_results.kparams, ParseResults):
+ kparams = dict([kparam.value for kparam in parse_results.kparams])
self.log.debug("Parse gave path='%s' command='%s' " % (path, command)
+ "pparams=%s " % str(pparams)
+ "kparams=%s" % str(kparams))
- return (result_trees, path, command, pparams, kparams)
+ return (parse_results, path, command, pparams, kparams)
def _execute_command(self, path, command, pparams, kparams):
'''
diff --git a/debian/control b/debian/control
index af9ad15..05bb316 100644
--- a/debian/control
+++ b/debian/control
@@ -2,12 +2,12 @@ Source: configshell
Section: python
Priority: optional
Maintainer: Jerome Martin <jxm@risingtidesystems.com>
-Build-Depends: debhelper(>= 7.0.1), python2.6, python-epydoc, python-simpleparse
+Build-Depends: debhelper(>= 7.0.1), python2.6
Standards-Version: 3.8.1
Package: python-configshell
Architecture: all
-Depends: python (>= 2.6)|python2.6, python-epydoc, python-simpleparse, python-urwid (>=0.9.9)
+Depends: python (>= 2.6)|python2.6, python-epydoc, python-pyparsing, python-urwid (>=0.9.9)
Suggests: configshell-doc
Description: Framework to create CLI interfaces.
.
diff --git a/rpm/python-configshell.spec.tmpl b/rpm/python-configshell.spec.tmpl
index f44722c..ddd3942 100644
--- a/rpm/python-configshell.spec.tmpl
+++ b/rpm/python-configshell.spec.tmpl
@@ -10,8 +10,8 @@ URL: http://www.risingtidesystems.com/git/
Source: %{oname}-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-rpmroot
BuildArch: noarch
-BuildRequires: python-devel, epydoc, python-simpleparse
-Requires: python-simpleparse, python-urwid >= 0.9.9, epydoc
+BuildRequires: python-devel
+Requires: pyparsing, python-urwid >= 0.9.9, epydoc
Vendor: Datera, Inc.
%description