2 * clippy (CLI preparator in python) C pseudo-lexer
3 * Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 /* This is just enough of a lexer to make rough sense of a C source file.
21 * It handles C preprocessor directives, strings, and looks for FRR-specific
24 * There is some preliminary support for documentation comments for DEFUNs.
25 * They would look like this (note the ~): (replace \ by /)
27 * \*~ documentation for foobar_cmd
28 * * parameter does xyz
30 * DEFUN(foobar_cmd, ...)
32 * This is intended for user documentation / command reference. Don't put
33 * code documentation in it.
42 /* ignore harmless bugs in old versions of flex */
43 #pragma GCC diagnostic ignored "-Wsign-compare"
44 #pragma GCC diagnostic ignored "-Wunused-value"
51 #include "command_graph.h"
70 static void extendbuf(char **what, const char *arg)
75 size_t vall = strlen(*what), argl = strlen(arg);
76 *what = realloc(*what, vall + argl + 1);
77 memcpy(*what + vall, arg, argl);
78 (*what)[vall + argl] = '\0';
81 #define extend(x) extendbuf(&value, x)
86 OPERATOR [!%&/\[\]{}=?:^|\*.;><~'\\+-]
94 %option outfile="lib/defun_lex.c"
95 %option prefix="def_yy"
108 <INITIAL,linestart,preproc>"/*" comment_link = YY_START; extend(yytext); BEGIN(comment);
109 <comment>[^*\n]* extend(yytext);
110 <comment>"*"+[^*/\n]* extend(yytext);
111 <comment>\n extend(yytext);
112 <comment>"*"+"/" extend(yytext); BEGIN(comment_link); return COMMENT;
114 <INITIAL,linestart,preproc>"//" comment_link = YY_START; extend(yytext); BEGIN(linecomment);
115 <linecomment>[^\n]* extend(yytext);
116 <linecomment>\n BEGIN((comment_link == INITIAL) ? linestart : comment_link); return COMMENT;
118 <linestart># BEGIN(preproc);
119 <preproc>\n BEGIN(INITIAL); return PREPROC;
120 <preproc>[^\n\\]+ extend(yytext);
121 <preproc>\\\n extend(yytext);
122 <preproc>\\+[^\n] extend(yytext);
124 [\"\'] string_end = yytext[0]; extend(yytext); BEGIN(rstring);
127 if (yytext[0] == string_end) {
132 <rstring>\\\n /* ignore */
133 <rstring>\\. extend(yytext);
134 <rstring>[^\\\"\']+ extend(yytext);
136 "DEFUN" value = strdup(yytext); return DEFUNNY;
137 "DEFUN_NOSH" value = strdup(yytext); return DEFUNNY;
138 "DEFUN_HIDDEN" value = strdup(yytext); return DEFUNNY;
139 "DEFPY" value = strdup(yytext); return DEFUNNY;
140 "DEFPY_NOSH" value = strdup(yytext); return DEFUNNY;
141 "DEFPY_ATTR" value = strdup(yytext); return DEFUNNY;
142 "DEFPY_HIDDEN" value = strdup(yytext); return DEFUNNY;
143 "ALIAS" value = strdup(yytext); return DEFUNNY;
144 "ALIAS_HIDDEN" value = strdup(yytext); return DEFUNNY;
145 "install_element" value = strdup(yytext); return INSTALL;
146 "VTYSH_TARGETS" value = strdup(yytext); return AUXILIARY;
147 "VTYSH_NODESWITCH" value = strdup(yytext); return AUXILIARY;
149 [ \t\n]+ /* ignore */
151 {ID} BEGIN(INITIAL); value = strdup(yytext); return ID;
152 {OPERATOR} BEGIN(INITIAL); value = strdup(yytext); return OPERATOR;
153 {SPECIAL} BEGIN(INITIAL); value = strdup(yytext); return SPECIAL;
154 . /* printf("-- '%s' in init\n", yytext); */ BEGIN(INITIAL); return yytext[0];
158 static int yylex_clr(char **retbuf)
160 int rv = def_yylex();
166 static PyObject *get_args(void)
168 PyObject *pyObj = PyList_New(0);
169 PyObject *pyArg = NULL;
175 while ((token = yylex_clr(&tval)) != YY_NULL) {
176 if (token == SPECIAL && tval[0] == '(') {
180 if (token == COMMENT) {
184 fprintf(stderr, "invalid input!\n");
188 while ((token = yylex_clr(&tval)) != YY_NULL) {
189 if (token == COMMENT) {
193 if (token == SPECIAL) {
194 if (depth == 1 && (tval[0] == ',' || tval[0] == ')')) {
196 PyList_Append(pyObj, pyArg);
198 if (tval[0] == ')') {
211 pyArg = PyList_New(0);
212 PyList_Append(pyArg, PyUnicode_FromString(tval));
218 /* _clippy.parse() -- read a C file, returning a list of interesting bits.
219 * note this ditches most of the actual C code. */
220 PyObject *clippy_parse(PyObject *self, PyObject *args)
222 const char *filename;
223 if (!PyArg_ParseTuple(args, "s", &filename))
226 FILE *fd = fopen(filename, "r");
228 return PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
235 PyObject *pyCont = PyDict_New();
236 PyObject *pyObj = PyList_New(0);
237 PyDict_SetItemString(pyCont, "filename", PyUnicode_FromString(filename));
238 PyDict_SetItemString(pyCont, "data", pyObj);
240 while ((token = yylex_clr(&tval)) != YY_NULL) {
241 int lineno = yylineno;
242 PyObject *pyItem = NULL, *pyArgs;
248 pyItem = PyDict_New();
249 PyDict_SetItemString(pyItem, "type", PyUnicode_FromString(tval));
250 PyDict_SetItemString(pyItem, "args", pyArgs);
253 if (strncmp(tval, "//~", 3) && strncmp(tval, "/*~", 3))
255 pyItem = PyDict_New();
256 PyDict_SetItemString(pyItem, "type", PyUnicode_FromString("COMMENT"));
257 PyDict_SetItemString(pyItem, "line", PyUnicode_FromString(tval));
260 pyItem = PyDict_New();
261 PyDict_SetItemString(pyItem, "type", PyUnicode_FromString("PREPROC"));
262 PyDict_SetItemString(pyItem, "line", PyUnicode_FromString(tval));
266 PyDict_SetItemString(pyItem, "lineno", PyLong_FromLong(lineno));
267 PyList_Append(pyObj, pyItem);