Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for variable prefix #42

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add support for variable prefix
It is currently not possible to use an option or command name that happesn to be
a C keyword. So for example trying to have a command named "register" will fail
with a compilation error.

This introduces support for setting a `prefix` for the variable names defined
in the struct. So for example using prefix `_` would mean you access
the command via `args._register` instead.
JoakimSoderberg committed Jun 17, 2019
commit de31b33909d5b2b7e5eda637ee8c5e1837d8ce77
44 changes: 26 additions & 18 deletions docopt_c.py
Original file line number Diff line number Diff line change
@@ -18,6 +18,12 @@
-t, --template=<template>
Filename used to read a C template.
-h,--help Show this help message and exit.
-p, --prefix=<prefix>
Add a prefix to all struct member variables.
This is useful if you want to use variable names that
collide with C keywords, for example "register".
The generated struct member becomes args.<prefix>register
where args.register would cause a complitation error.

Arguments:
<docopt> Input file describing your CLI in docopt language.
@@ -63,41 +69,41 @@ def c_option(o):
False, None))


def c_name(s):
return ''.join(c if c.isalnum() else '_' for c in s).strip('_')
def c_name(s, prefix=''):
return prefix + ''.join(c if c.isalnum() else '_' for c in s).strip('_')


def c_if_command(cmd):
def c_if_command(cmd, prefix=''):
t = """if (!strcmp(command->name, %s)) {
args->%s = command->value;
}"""
return t % (to_c(cmd.name), c_name(cmd.name))
return t % (to_c(cmd.name), c_name(cmd.name, prefix))


def c_if_argument(arg):
def c_if_argument(arg, prefix=''):
t = """if (!strcmp(argument->name, %s)) {
args->%s = argument->value;
}"""
return t % (to_c(arg.name), c_name(arg.name))
return t % (to_c(arg.name), c_name(arg.name, prefix))


def c_if_flag(o):
def c_if_flag(o, prefix=''):
t = """ else if (!strcmp(option->o%s, %s)) {
args->%s = option->value;
}"""
return t % (('long' if o.long else 'short'),
to_c(o.long or o.short),
c_name(o.long or o.short))
c_name(o.long or o.short, prefix))


def c_if_option(o):
def c_if_option(o, prefix=''):
t = """ else if (!strcmp(option->o%s, %s)) {
if (option->argument)
args->%s = option->argument;
}"""
return t % (('long' if o.long else 'short'),
to_c(o.long or o.short),
c_name(o.long or o.short))
c_name(o.long or o.short, prefix))


def parse_leafs(pattern, all_options):
@@ -157,23 +163,25 @@ def parse_leafs(pattern, all_options):
if isinstance(usage, list):
raise docopt.DocoptLanguageError(''.join(usage))

prefix = args['--prefix'] or ''

all_options = docopt.parse_defaults(doc)
pattern = docopt.parse_pattern(docopt.formal_usage(usage), all_options)
leafs, commands, arguments, flags, options = parse_leafs(pattern, all_options)

t_commands = ';\n '.join('int %s' % c_name(cmd.name)
t_commands = ';\n '.join('int %s' % c_name(cmd.name, prefix)
for cmd in commands)
t_commands = (('\n /* commands */\n ' + t_commands + ';')
if t_commands != '' else '')
t_arguments = ';\n '.join('char *%s' % c_name(arg.name)
t_arguments = ';\n '.join('char *%s' % c_name(arg.name, prefix)
for arg in arguments)
t_arguments = (('\n /* arguments */\n ' + t_arguments + ';')
if t_arguments != '' else '')
t_flags = ';\n '.join('int %s' % c_name(flag.long or flag.short)
t_flags = ';\n '.join('int %s' % c_name(flag.long or flag.short, prefix)
for flag in flags)
t_flags = (('\n /* options without arguments */\n ' + t_flags + ';')
if t_flags != '' else '')
t_options = ';\n '.join('char *%s' % c_name(opt.long or opt.short)
t_options = ';\n '.join('char *%s' % c_name(opt.long or opt.short, prefix)
for opt in options)
t_options = (('\n /* options with arguments */\n ' + t_options + ';')
if t_options != '' else '')
@@ -189,13 +197,13 @@ def parse_leafs(pattern, all_options):
t_elems_opts = ('\n ' + t_elems_opts) if t_elems_opts != '' else ''
t_elems_n = ', '.join([str(len(l))
for l in [commands, arguments, (flags + options)]])
t_if_command = ' else '.join(c_if_command(command) for command in commands)
t_if_command = ' else '.join(c_if_command(command, prefix) for command in commands)
t_if_command = ('\n ' + t_if_command) if t_if_command != '' else ''
t_if_argument = ' else '.join(c_if_argument(arg) for arg in arguments)
t_if_argument = ' else '.join(c_if_argument(arg, prefix) for arg in arguments)
t_if_argument = (('\n ' + t_if_argument)
if t_if_argument != '' else '')
t_if_flag = ''.join(c_if_flag(flag) for flag in flags)
t_if_option = ''.join(c_if_option(opt) for opt in options)
t_if_flag = ''.join(c_if_flag(flag, prefix) for flag in flags)
t_if_option = ''.join(c_if_option(opt, prefix) for opt in options)

out = Template(args['--template']).safe_substitute(
commands=t_commands,