Merge fixes from branch 'xorn'
[geda-gaf.git] / xorn / src / backend / util_getopt.py
blob18a66f42bb6046ad1195a432f276a67fb43a911d
1 # gaf.netlist - gEDA Netlist Extraction and Generation
2 # Copyright (C) 1998-2010 Ales Hvezda
3 # Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
4 # Copyright (C) 2013-2020 Roland Lutz
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software Foundation,
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 # Copyright (C) 2011 Peter Brett <peter@peter-b.co.uk>
22 ## \file util_getopt.py
23 ## gnetlist `-O' command-line option parsing for backends.
25 # This module provides the function \ref backend_getopt to assist
26 # backends which wish to provide command-line gnetlist options via the
27 # `-O' argument. `backend_getopt' accepts a grammar and the set of
28 # `-O' arguments, and extracts the options.
30 # The name of an `-O' option is assumed to be all characters in the
31 # argument up to the first `=' or ` '.
33 import collections, sys
35 ## Raised by \ref backend_getopt if it finds a problem with the arguments.
37 class OptionError(Exception):
38 pass
40 NO_ARGUMENT, REQUIRED_ARGUMENT, OPTIONAL_ARGUMENT = xrange(3)
42 Option = collections.namedtuple(
43 'Option', ['required', 'argument', 'predicate'])
45 ## Parse command-line `-O' arguments against a given backend option grammar.
47 # The \a grammar argument is expected to be a dictionary of this form:
49 # { 'option-name': Option(False, NO_ARGUMENT, None), ... }
51 # For each option, the following properties are given:
53 # required (bool)
54 # If \a required is \c True, the option is required. \ref
55 # backend_getopt will raise an error if it is not found in the
56 # arguments.
58 # value (int)
59 # If \a value is REQUIRED_ARGUMENT, the option requires a value;
60 # if it is NO_ARGUMENT, it does not; and if it is
61 # OPTIONAL_ARGUMENT, the option may appear in the arguments with
62 # or without a value.
64 # predicate (function)
65 # If the option accepts a value (i.e. you specified
66 # REQUIRED_ARGUMENT or OPTIONAL_ARGUMENT for this option), then
67 # \ref backend_getopt will apply \a predicate to the value and
68 # raise an error if it returns \c False. \a predicate should be a
69 # procedure which accepts a string and returns a boolean value.
71 # \returns a dictionary whose keys are the names of the backend
72 # options specified by the user and whose values are the
73 # option values, or \c True where the option was given but
74 # has no value
76 # \throws OptionError if it finds a problem with the arguments
78 def backend_getopt(args, grammar, raise_exception = False):
79 def error(msg):
80 if raise_exception:
81 raise OptionError, msg
82 sys.stderr.write(msg + "\n")
83 sys.exit(3)
85 options = {}
87 # First pass: process options
88 for arg in args:
89 # Find the first index of a ' ' or '=' in the argument
90 try:
91 i = next(i for i, ch in enumerate(arg) if ch in ' =')
92 except StopIteration:
93 # none found
94 name, value = arg, None
95 else:
96 if i == 0:
97 error("Invalid backend option syntax '%s'" % arg)
98 name, value = arg[:i], arg[i + 1:]
100 try:
101 spec = grammar[name]
102 except KeyError:
103 # Is this a valid argument?
104 error("Unrecognized backend option '%s'" % name)
106 # Validate the given `-O' argument name and value against the grammar.
108 # Check that a value was provided, if one was required, or vice versa.
109 if spec.argument == NO_ARGUMENT and value is not None:
110 error("Backend option '%s' doesn't allow an argument" % name)
111 if spec.argument == REQUIRED_ARGUMENT and value is None:
112 error("Backend option '%s' requires an argument" % name)
114 # If a value-verification predicate was provided, use it to verify
115 # the value.
116 if spec.predicate is not None and value is not None \
117 and not spec.predicate(value):
118 error("Invalid argument '%s' to backend option '%s'"
119 % (value, name))
121 # If a value was provided, return it, otherwise return True.
122 if value is None:
123 options[name] = True
124 else:
125 options[name] = value
127 # Second pass: ensure required options have been provided
128 for name in grammar:
129 if grammar[name].required and not name in options:
130 error("Backend option '%s' must be specified" % name)
132 return options