lib: clippy the assistant

Wraps the command parsing code for Python, so we can use it to do fancy
preprocessing and replace extract.pl.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
This commit is contained in:
David Lamparter 2016-11-11 17:10:02 +01:00
parent 92e5026146
commit 29ad6f6882
6 changed files with 890 additions and 2 deletions

View file

@ -23,7 +23,36 @@ dnl Get hostname and other information.
dnl -----------------------------------
AC_CANONICAL_BUILD()
AC_CANONICAL_HOST()
AC_CANONICAL_TARGET()
AS_IF([test "$host" != "$build"], [
if test "$srcdir" = "."; then
AC_MSG_ERROR([cross-compilation is only possible with builddir separate from srcdir. create a separate directory and run as .../path-to-frr/configure.])
fi
test -d hosttools || mkdir hosttools
abssrc="`cd \"${srcdir}\"; pwd`"
AC_MSG_NOTICE([...])
AC_MSG_NOTICE([... cross-compilation: creating hosttools directory and self-configuring for build platform tools])
AC_MSG_NOTICE([... use HOST_CPPFLAGS / HOST_CFLAGS / HOST_LDFLAGS if neccessary])
AC_MSG_NOTICE([...])
( CPPFLAGS="$HOST_CPPFLAGS"; \
CFLAGS="$HOST_CFLAGS"; \
LDFLAGS="$HOST_LDFLAGS"; \
cd hosttools; "${abssrc}/configure" "--host=$build" "--build=$build"; )
AC_MSG_NOTICE([...])
AC_MSG_NOTICE([... cross-compilation: finished self-configuring for build platform tools])
AC_MSG_NOTICE([...])
build_clippy="false"
CLIPPYDIR="hosttools/lib"
], [
build_clippy="true"
CLIPPYDIR="lib"
])
AC_SUBST(CLIPPYDIR)
AM_CONDITIONAL([BUILD_CLIPPY], [$build_clippy])
# Disable portability warnings -- our automake code (in particular
# common.am) uses some constructs specific to gmake.
@ -431,6 +460,77 @@ if test "x${enable_dev_build}" = "xyes"; then
fi
AM_CONDITIONAL([DEV_BUILD], [test "x$enable_dev_build" = "xyes"])
#
# Python for clippy
#
AS_IF([test "$host" = "$build"], [
PYTHONCONFIG=""
# ordering:
# 1. try python3, but respect the user's preference on which minor ver
# 2. try python, which might be py3 or py2 again on the user's preference
# 3. try python2 (can really only be 2.7 but eh)
# 4. try 3.5 > 3.4 > 3.3 > 3.2 > 2.7 through pkg-config (no user pref)
#
# (AX_PYTHON_DEVEL has no clue about py3 vs py2)
# (AX_PYTHON does not do what we need)
AC_CHECK_TOOLS([PYTHONCONFIG], [python3-config python-config python2-config])
if test -n "$PYTHONCONFIG"; then
PYTHON_CFLAGS="`\"${PYTHONCONFIG}\" --includes`"
PYTHON_LIBS="`\"${PYTHONCONFIG}\" --libs`"
AC_MSG_CHECKING([whether we found a working Python version])
AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([
#include <Python.h>
#if PY_VERSION_HEX < 0x02070000
#error python too old
#endif
int main(void);
],
[
{
Py_Initialize();
return 0;
}
])], [
PYTHONCONFIG=""
unset PYTHON_LIBS
unset PYTHON_CFLAGS
])
fi
if test -z "$PYTHONCONFIG"; then
PKG_CHECK_MODULES([PYTHON], python-3.5, [], [
PKG_CHECK_MODULES([PYTHON], python-3.4, [], [
PKG_CHECK_MODULES([PYTHON], python-3.3, [], [
PKG_CHECK_MODULES([PYTHON], python-3.2, [], [
PKG_CHECK_MODULES([PYTHON], python-2.7, [], [
AC_MSG_FAILURE([could not find python-config or pkg-config python, please install Python development files from libpython-dev or similar])
])])])])])
AC_MSG_CHECKING([whether we found a working Python version])
AC_LINK_IFELSE_FLAGS([$PYTHON_CFLAGS], [$PYTHON_LIBS], [AC_LANG_PROGRAM([
#include <Python.h>
#if PY_VERSION_HEX < 0x02070000
#error python too old
#endif
int main(void);
],
[
{
Py_Initialize();
return 0;
}
])], [
AC_MSG_FAILURE([could not find python-config or pkg-config python, please install Python development files from libpython-dev or similar])
])
fi
])
AC_SUBST(PYTHON_CFLAGS)
AC_SUBST(PYTHON_LIBS)
#
# Logic for protobuf support.
#

View file

@ -9,6 +9,7 @@ command_lex.h: command_lex.c
@if test ! -f $@; then rm -f command_lex.c; else :; fi
@if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) command_lex.c; else :; fi
command_parse.lo: command_lex.h
clippy-command_parse.$(OBJEXT): command_lex.h
lib_LTLIBRARIES = libfrr.la
libfrr_la_LDFLAGS = -version-info 0:0:0
@ -85,13 +86,34 @@ pkginclude_HEADERS = \
noinst_HEADERS = \
plist_int.h \
log_int.h
log_int.h \
clippy.h \
# end
noinst_PROGRAMS = grammar_sandbox
if BUILD_CLIPPY
noinst_PROGRAMS += clippy
endif
grammar_sandbox_SOURCES = grammar_sandbox_main.c
grammar_sandbox_LDADD = libfrr.la
clippy_SOURCES = \
defun_lex.l \
command_parse.y \
command_lex.l \
command_graph.c \
command_py.c \
memory.c \
graph.c \
vector.c \
clippy.c \
# end
clippy_CPPFLAGS = -D_GNU_SOURCE
clippy_CFLAGS = $(PYTHON_CFLAGS)
clippy_LDADD = $(PYTHON_LIBS)
clippy-command_graph.$(OBJEXT): route_types.h
EXTRA_DIST = \
queue.h \
command_lex.h \

137
lib/clippy.c Normal file
View file

@ -0,0 +1,137 @@
/*
* clippy (CLI preparator in python) main executable
* Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include <Python.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "getopt.h"
#include "command_graph.h"
#include "clippy.h"
#if PY_MAJOR_VERSION >= 3
#define pychar wchar_t
static wchar_t *wconv(const char *s)
{
size_t outlen = mbstowcs(NULL, s, 0);
wchar_t *out = malloc((outlen + 1) * sizeof(wchar_t));
mbstowcs(out, s, outlen + 1);
out[outlen] = 0;
return out;
}
#else
#define pychar char
#define wconv(x) x
#endif
int main(int argc, char **argv)
{
pychar **wargv;
#if PY_VERSION_HEX >= 0x03040000 /* 3.4 */
Py_SetStandardStreamEncoding("UTF-8", NULL);
#endif
Py_SetProgramName(wconv(argv[0]));
PyImport_AppendInittab("_clippy", command_py_init);
Py_Initialize();
wargv = malloc(argc * sizeof(pychar *));
for (int i = 1; i < argc; i++)
wargv[i - 1] = wconv(argv[i]);
PySys_SetArgv(argc - 1, wargv);
const char *pyfile = argc > 1 ? argv[1] : NULL;
FILE *fp;
if (pyfile) {
fp = fopen(pyfile, "r");
if (!fp) {
fprintf(stderr, "%s: %s\n", pyfile, strerror(errno));
return 1;
}
} else {
fp = stdin;
char *ver = strdup(Py_GetVersion());
char *cr = strchr(ver, '\n');
if (cr)
*cr = ' ';
fprintf(stderr, "clippy interactive shell\n(Python %s)\n", ver);
free(ver);
PyRun_SimpleString("import rlcompleter, readline\n"
"readline.parse_and_bind('tab: complete')");
}
if (PyRun_AnyFile(fp, pyfile)) {
if (PyErr_Occurred())
PyErr_Print();
else
printf("unknown python failure (?)\n");
return 1;
}
Py_Finalize();
#if PY_MAJOR_VERSION >= 3
for (int i = 1; i < argc; i++)
free(wargv[i - 1]);
#endif
free(wargv);
return 0;
}
/* and now for the ugly part... provide simplified logging functions so we
* don't need to link libzebra (which would be a circular build dep) */
#ifdef __ASSERT_FUNCTION
#undef __ASSERT_FUNCTION
#endif
#include "log.h"
#include "zassert.h"
#define ZLOG_FUNC(FUNCNAME) \
void FUNCNAME(const char *format, ...) \
{ \
va_list args; \
va_start(args, format); \
vfprintf (stderr, format, args); \
fputs ("\n", stderr); \
va_end(args); \
}
ZLOG_FUNC(zlog_err)
ZLOG_FUNC(zlog_warn)
ZLOG_FUNC(zlog_info)
ZLOG_FUNC(zlog_notice)
ZLOG_FUNC(zlog_debug)
void
_zlog_assert_failed (const char *assertion, const char *file,
unsigned int line, const char *function)
{
fprintf(stderr, "Assertion `%s' failed in file %s, line %u, function %s",
assertion, file, line, (function ? function : "?"));
abort();
}
void memory_oom (size_t size, const char *name)
{
abort();
}

28
lib/clippy.h Normal file
View file

@ -0,0 +1,28 @@
/*
* clippy (CLI preparator in python)
* Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _FRR_CLIPPY_H
#define _FRR_CLIPPY_H
#include <Python.h>
extern PyObject *clippy_parse(PyObject *self, PyObject *args);
extern PyMODINIT_FUNC command_py_init(void);
#endif /* _FRR_CLIPPY_H */

336
lib/command_py.c Normal file
View file

@ -0,0 +1,336 @@
/*
* clippy (CLI preparator in python) wrapper for FRR command_graph
* Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* note: this wrapper is intended to be used as build-time helper. while
* it should be generally correct and proper, there may be the occasional
* memory leak or SEGV for things that haven't been well-tested.
*/
#include <Python.h>
#include "structmember.h"
#include <string.h>
#include <stdlib.h>
#include "command_graph.h"
#include "clippy.h"
struct wrap_graph;
static PyObject *graph_to_pyobj(struct wrap_graph *graph, struct graph_node *gn);
/*
* nodes are wrapped as follows:
* - instances can only be acquired from a graph
* - the same node will return the same wrapper object (they're buffered
* through "idx")
* - a reference is held onto the graph
* - fields are copied for easy access with PyMemberDef
*/
struct wrap_graph_node {
PyObject_HEAD
bool allowrepeat;
const char *type;
bool deprecated;
bool hidden;
const char *text;
const char *desc;
const char *varname;
long long min, max;
struct graph_node *node;
struct wrap_graph *wgraph;
size_t idx;
};
/*
* graphs are wrapped as follows:
* - they can only be created by parsing a definition string
* - there's a table here for the wrapped nodes (nodewrappers), indexed
* by "idx" (corresponds to node's position in graph's table of nodes)
* - graphs do NOT hold references to nodes (would be circular)
*/
struct wrap_graph {
PyObject_HEAD
char *definition;
struct graph *graph;
struct wrap_graph_node **nodewrappers;
};
static PyObject *refuse_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyErr_SetString(PyExc_ValueError, "cannot create instances of this type");
return NULL;
}
#define member(name, type) {(char *)#name, type, offsetof(struct wrap_graph_node, name), READONLY, \
(char *)#name " (" #type ")"}
static PyMemberDef members_graph_node[] = {
member(allowrepeat, T_BOOL),
member(type, T_STRING),
member(deprecated, T_BOOL),
member(hidden, T_BOOL),
member(text, T_STRING),
member(desc, T_STRING),
member(min, T_LONGLONG),
member(max, T_LONGLONG),
member(varname, T_STRING),
{},
};
#undef member
/*
* node.next() -- returns list of all "next" nodes.
* this will include circles if the graph has them.
*/
static PyObject *graph_node_next(PyObject *self, PyObject *args)
{
struct wrap_graph_node *wrap = (struct wrap_graph_node *)self;
PyObject *pylist;
if (wrap->node->data
&& ((struct cmd_token *)wrap->node->data)->type == END_TKN)
return PyList_New(0);
pylist = PyList_New(vector_active(wrap->node->to));
for (size_t i = 0; i < vector_active(wrap->node->to); i++) {
struct graph_node *gn = vector_slot(wrap->node->to, i);
PyList_SetItem(pylist, i, graph_to_pyobj(wrap->wgraph, gn));
}
return pylist;
};
/*
* node.join() -- return FORK's JOIN node or None
*/
static PyObject *graph_node_join(PyObject *self, PyObject *args)
{
struct wrap_graph_node *wrap = (struct wrap_graph_node *)self;
if (!wrap->node->data
|| ((struct cmd_token *)wrap->node->data)->type == END_TKN)
Py_RETURN_NONE;
struct cmd_token *tok = wrap->node->data;
if (tok->type != FORK_TKN)
Py_RETURN_NONE;
return graph_to_pyobj(wrap->wgraph, tok->forkjoin);
};
static PyMethodDef methods_graph_node[] = {
{"next", graph_node_next, METH_NOARGS, "outbound graph edge list"},
{"join", graph_node_join, METH_NOARGS, "outbound join node"},
{}
};
static void graph_node_wrap_free(void *arg)
{
struct wrap_graph_node *wrap = arg;
wrap->wgraph->nodewrappers[wrap->idx] = NULL;
Py_DECREF(wrap->wgraph);
}
static PyTypeObject typeobj_graph_node = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "_clippy.GraphNode",
.tp_basicsize = sizeof(struct wrap_graph_node),
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "struct graph_node *",
.tp_new = refuse_new,
.tp_free = graph_node_wrap_free,
.tp_members = members_graph_node,
.tp_methods = methods_graph_node,
};
static PyObject *graph_to_pyobj(struct wrap_graph *wgraph, struct graph_node *gn)
{
struct wrap_graph_node *wrap;
size_t i;
for (i = 0; i < vector_active(wgraph->graph->nodes); i++)
if (vector_slot(wgraph->graph->nodes, i) == gn)
break;
if (i == vector_active(wgraph->graph->nodes)) {
PyErr_SetString(PyExc_ValueError, "cannot find node in graph");
return NULL;
}
if (wgraph->nodewrappers[i]) {
PyObject *obj = (PyObject *)wgraph->nodewrappers[i];
Py_INCREF(obj);
return obj;
}
wrap = (struct wrap_graph_node *)typeobj_graph_node.tp_alloc(&typeobj_graph_node, 0);
if (!wrap)
return NULL;
wgraph->nodewrappers[i] = wrap;
Py_INCREF(wgraph);
wrap->idx = i;
wrap->wgraph = wgraph;
wrap->node = gn;
wrap->type = "NULL";
wrap->allowrepeat = false;
if (gn->data) {
struct cmd_token *tok = gn->data;
switch (tok->type) {
#define item(x) case x: wrap->type = #x; break;
item(WORD_TKN) // words
item(VARIABLE_TKN) // almost anything
item(RANGE_TKN) // integer range
item(IPV4_TKN) // IPV4 addresses
item(IPV4_PREFIX_TKN) // IPV4 network prefixes
item(IPV6_TKN) // IPV6 prefixes
item(IPV6_PREFIX_TKN) // IPV6 network prefixes
/* plumbing types */
item(FORK_TKN)
item(JOIN_TKN)
item(START_TKN)
item(END_TKN)
default:
wrap->type = "???";
}
wrap->deprecated = (tok->attr == CMD_ATTR_DEPRECATED);
wrap->hidden = (tok->attr == CMD_ATTR_HIDDEN);
wrap->text = tok->text;
wrap->desc = tok->desc;
wrap->varname = tok->varname;
wrap->min = tok->min;
wrap->max = tok->max;
wrap->allowrepeat = tok->allowrepeat;
}
return (PyObject *)wrap;
}
#define member(name, type) {(char *)#name, type, offsetof(struct wrap_graph, name), READONLY, \
(char *)#name " (" #type ")"}
static PyMemberDef members_graph[] = {
member(definition, T_STRING),
{},
};
#undef member
/* graph.first() - root node */
static PyObject *graph_first(PyObject *self, PyObject *args)
{
struct wrap_graph *gwrap = (struct wrap_graph *)self;
struct graph_node *gn = vector_slot(gwrap->graph->nodes, 0);
return graph_to_pyobj(gwrap, gn);
};
static PyMethodDef methods_graph[] = {
{"first", graph_first, METH_NOARGS, "first graph node"},
{}
};
static PyObject *graph_parse(PyTypeObject *type, PyObject *args, PyObject *kwds);
static void graph_wrap_free(void *arg)
{
struct wrap_graph *wgraph = arg;
free(wgraph->nodewrappers);
free(wgraph->definition);
}
static PyTypeObject typeobj_graph = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "_clippy.Graph",
.tp_basicsize = sizeof(struct wrap_graph),
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "struct graph *",
.tp_new = graph_parse,
.tp_free = graph_wrap_free,
.tp_members = members_graph,
.tp_methods = methods_graph,
};
/* top call / entrypoint for python code */
static PyObject *graph_parse(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
const char *def, *doc = NULL;
struct wrap_graph *gwrap;
static const char *kwnames[] = { "cmddef", "doc", NULL };
gwrap = (struct wrap_graph *)typeobj_graph.tp_alloc(&typeobj_graph, 0);
if (!gwrap)
return NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", (char **)kwnames, &def, &doc))
return NULL;
struct graph *graph = graph_new ();
struct cmd_token *token = cmd_token_new (START_TKN, 0, NULL, NULL);
graph_new_node (graph, token, (void (*)(void *)) &cmd_token_del);
struct cmd_element cmd = { .string = def, .doc = doc };
cmd_graph_parse (graph, &cmd);
cmd_graph_names (graph);
gwrap->graph = graph;
gwrap->definition = strdup(def);
gwrap->nodewrappers = calloc(vector_active(graph->nodes),
sizeof (gwrap->nodewrappers[0]));
return (PyObject *)gwrap;
}
static PyMethodDef clippy_methods[] = {
{"parse", clippy_parse, METH_VARARGS, "Parse a C file"},
{NULL, NULL, 0, NULL}
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef pymoddef_clippy = {
PyModuleDef_HEAD_INIT,
"_clippy",
NULL, /* docstring */
-1,
clippy_methods,
};
#define modcreate() PyModule_Create(&pymoddef_clippy)
#define initret(val) return val;
#else
#define modcreate() Py_InitModule("_clippy", clippy_methods)
#define initret(val) do { \
if (!val) Py_FatalError("initialization failure"); \
return; } while (0)
#endif
PyMODINIT_FUNC command_py_init(void)
{
PyObject* pymod;
if (PyType_Ready(&typeobj_graph_node) < 0)
initret(NULL);
if (PyType_Ready(&typeobj_graph) < 0)
initret(NULL);
pymod = modcreate();
if (!pymod)
initret(NULL);
Py_INCREF(&typeobj_graph_node);
PyModule_AddObject(pymod, "GraphNode", (PyObject *)&typeobj_graph_node);
Py_INCREF(&typeobj_graph);
PyModule_AddObject(pymod, "Graph", (PyObject *)&typeobj_graph);
initret(pymod);
}

265
lib/defun_lex.l Normal file
View file

@ -0,0 +1,265 @@
%{
/*
* clippy (CLI preparator in python) C pseudo-lexer
* Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* This is just enough of a lexer to make rough sense of a C source file.
* It handles C preprocessor directives, strings, and looks for FRR-specific
* idioms (aka DEFUN).
*
* There is some preliminary support for documentation comments for DEFUNs.
* They would look like this (note the ~): (replace \ by /)
*
* \*~ documentation for foobar_cmd
* * parameter does xyz
* *\
* DEFUN(foobar_cmd, ...)
*
* This is intended for user documentation / command reference. Don't put
* code documentation in it.
*/
/* ignore harmless bug in old versions of flex */
#pragma GCC diagnostic ignored "-Wsign-compare"
#include "config.h"
#include <Python.h>
#include <string.h>
#include <stdlib.h>
#include "command_graph.h"
#include "clippy.h"
#define ID 258
#define PREPROC 259
#define OPERATOR 260
#define STRING 261
#define COMMENT 262
#define SPECIAL 263
#define DEFUNNY 270
#define INSTALL 271
#define AUXILIARY 272
int comment_link;
char string_end;
char *value;
static void extendbuf(char **what, const char *arg)
{
if (!*what)
*what = strdup(arg);
else {
size_t vall = strlen(*what), argl = strlen(arg);
*what = realloc(*what, vall + argl + 1);
memcpy(*what + vall, arg, argl);
(*what)[vall + argl] = '\0';
}
}
#define extend(x) extendbuf(&value, x)
%}
ID [A-Za-z0-9_]+
OPERATOR [!%&/\[\]{}=?:^|\*.;><~'\\+-]
SPECIAL [(),]
%pointer
%option yylineno
%option noyywrap
%option noinput
%option nounput
%option outfile="defun_lex.c"
%option prefix="def_yy"
%option 8bit
%s linestart
%x comment
%x linecomment
%x preproc
%x rstring
%%
BEGIN(linestart);
\n BEGIN(linestart);
<INITIAL,linestart,preproc>"/*" comment_link = YY_START; extend(yytext); BEGIN(comment);
<comment>[^*\n]* extend(yytext);
<comment>"*"+[^*/\n]* extend(yytext);
<comment>\n extend(yytext);
<comment>"*"+"/" extend(yytext); BEGIN(comment_link); return COMMENT;
<INITIAL,linestart,preproc>"//" comment_link = YY_START; extend(yytext); BEGIN(linecomment);
<linecomment>[^\n]* extend(yytext);
<linecomment>\n BEGIN((comment_link == INITIAL) ? linestart : comment_link); return COMMENT;
<linestart># BEGIN(preproc);
<preproc>\n BEGIN(INITIAL); return PREPROC;
<preproc>[^\n\\]+ extend(yytext);
<preproc>\\\n extend(yytext);
<preproc>\\+[^\n] extend(yytext);
[\"\'] string_end = yytext[0]; extend(yytext); BEGIN(rstring);
<rstring>[\"\'] {
extend(yytext);
if (yytext[0] == string_end) {
BEGIN(INITIAL);
return STRING;
}
}
<rstring>\\\n /* ignore */
<rstring>\\. extend(yytext);
<rstring>[^\\\"\']+ extend(yytext);
"DEFUN" value = strdup(yytext); return DEFUNNY;
"DEFUN_NOSH" value = strdup(yytext); return DEFUNNY;
"DEFUN_HIDDEN" value = strdup(yytext); return DEFUNNY;
"DEFPY" value = strdup(yytext); return DEFUNNY;
"ALIAS" value = strdup(yytext); return DEFUNNY;
"ALIAS_HIDDEN" value = strdup(yytext); return DEFUNNY;
"install_element" value = strdup(yytext); return INSTALL;
"VTYSH_TARGETS" value = strdup(yytext); return AUXILIARY;
"VTYSH_NODESWITCH" value = strdup(yytext); return AUXILIARY;
[ \t\n]+ /* ignore */
\\ /* ignore */
{ID} BEGIN(INITIAL); value = strdup(yytext); return ID;
{OPERATOR} BEGIN(INITIAL); value = strdup(yytext); return OPERATOR;
{SPECIAL} BEGIN(INITIAL); value = strdup(yytext); return SPECIAL;
. /* printf("-- '%s' in init\n", yytext); */ BEGIN(INITIAL); return yytext[0];
%%
static int yylex_clr(char **retbuf)
{
int rv = def_yylex();
*retbuf = value;
value = NULL;
return rv;
}
static PyObject *get_args(void)
{
PyObject *pyObj = PyList_New(0);
PyObject *pyArg = NULL;
char *tval;
int depth = 1;
int token;
while ((token = yylex_clr(&tval)) != YY_NULL) {
if (token == SPECIAL && tval[0] == '(') {
free(tval);
break;
}
if (token == COMMENT) {
free(tval);
continue;
}
fprintf(stderr, "invalid input!\n");
exit(1);
}
while ((token = yylex_clr(&tval)) != YY_NULL) {
if (token == COMMENT) {
free(tval);
continue;
}
if (token == SPECIAL) {
if (depth == 1 && (tval[0] == ',' || tval[0] == ')')) {
if (pyArg)
PyList_Append(pyObj, pyArg);
pyArg = NULL;
if (tval[0] == ')') {
free(tval);
break;
}
free(tval);
continue;
}
if (tval[0] == '(')
depth++;
if (tval[0] == ')')
depth--;
}
if (!pyArg)
pyArg = PyList_New(0);
PyList_Append(pyArg, PyUnicode_FromString(tval));
free(tval);
}
return pyObj;
}
/* _clippy.parse() -- read a C file, returning a list of interesting bits.
* note this ditches most of the actual C code. */
PyObject *clippy_parse(PyObject *self, PyObject *args)
{
const char *filename;
if (!PyArg_ParseTuple(args, "s", &filename))
return NULL;
FILE *fd = fopen(filename, "r");
if (!fd)
return PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
char *tval;
int token;
yyin = fd;
value = NULL;
PyObject *pyCont = PyDict_New();
PyObject *pyObj = PyList_New(0);
PyDict_SetItemString(pyCont, "filename", PyUnicode_FromString(filename));
PyDict_SetItemString(pyCont, "data", pyObj);
while ((token = yylex_clr(&tval)) != YY_NULL) {
int lineno = yylineno;
PyObject *pyItem = NULL, *pyArgs;
switch (token) {
case DEFUNNY:
case INSTALL:
case AUXILIARY:
pyArgs = get_args();
pyItem = PyDict_New();
PyDict_SetItemString(pyItem, "type", PyUnicode_FromString(tval));
PyDict_SetItemString(pyItem, "args", pyArgs);
break;
case COMMENT:
if (strncmp(tval, "//~", 3) && strncmp(tval, "/*~", 3))
break;
pyItem = PyDict_New();
PyDict_SetItemString(pyItem, "type", PyUnicode_FromString("COMMENT"));
PyDict_SetItemString(pyItem, "line", PyUnicode_FromString(tval));
break;
case PREPROC:
pyItem = PyDict_New();
PyDict_SetItemString(pyItem, "type", PyUnicode_FromString("PREPROC"));
PyDict_SetItemString(pyItem, "line", PyUnicode_FromString(tval));
break;
}
if (pyItem) {
PyDict_SetItemString(pyItem, "lineno", PyLong_FromLong(lineno));
PyList_Append(pyObj, pyItem);
}
free(tval);
}
def_yylex_destroy();
fclose(fd);
return pyCont;
}