diff options
author | Christophe Vu-Brugier <cvubrugier@yahoo.fr> | 2013-09-04 11:34:13 +0200 |
---|---|---|
committer | Christophe Vu-Brugier <cvubrugier@yahoo.fr> | 2013-09-12 10:34:25 +0200 |
commit | d9cbcd0419f62912a5925efdbf556bba603fbeb5 (patch) | |
tree | d7d06700cd2498fc156665bc167ee9cc34a55c52 | |
parent | fb99f2cdbc91995d31a5eb3416e17b86c9285f14 (diff) | |
download | configshell-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.py | 92 | ||||
-rw-r--r-- | debian/control | 4 | ||||
-rw-r--r-- | rpm/python-configshell.spec.tmpl | 4 |
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 |