1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 * clippy (CLI preparator in python) C pseudo-lexer
4 * Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc.
7 /* This is just enough of a lexer to make rough sense of a C source file.
8 * It handles C preprocessor directives, strings, and looks for FRR-specific
11 * There is some preliminary support for documentation comments for DEFUNs.
12 * They would look like this (note the ~): (replace \ by /)
14 * \*~ documentation for foobar_cmd
15 * * parameter does xyz
17 * DEFUN(foobar_cmd, ...)
19 * This is intended for user documentation / command reference. Don't put
20 * code documentation in it.
29 /* ignore harmless bugs in old versions of flex */
30 #pragma GCC diagnostic ignored "-Wsign-compare"
31 #pragma GCC diagnostic ignored "-Wunused-value"
38 #include "command_graph.h"
56 static const char *yyfilename;
58 static void extendbuf(char **what, const char *arg)
63 size_t vall = strlen(*what), argl = strlen(arg);
64 *what = realloc(*what, vall + argl + 1);
65 memcpy(*what + vall, arg, argl);
66 (*what)[vall + argl] = '\0';
69 #define extend(x) extendbuf(&value, x)
71 #ifndef __clang_analyzer__
76 OPERATOR [!%&/\[\]{}=?:^|\*.;><~'\\+-]
84 %option outfile="lib/defun_lex.c"
85 %option prefix="def_yy"
98 <INITIAL,linestart,preproc>"/*" comment_link = YY_START; extend(yytext); BEGIN(comment);
99 <comment>[^*\n]* extend(yytext);
100 <comment>"*"+[^*/\n]* extend(yytext);
101 <comment>\n extend(yytext);
102 <comment>"*"+"/" extend(yytext); BEGIN(comment_link); return COMMENT;
104 <INITIAL,linestart,preproc>"//" comment_link = YY_START; extend(yytext); BEGIN(linecomment);
105 <linecomment>[^\n]* extend(yytext);
106 <linecomment>\n BEGIN((comment_link == INITIAL) ? linestart : comment_link); return COMMENT;
108 <linestart># BEGIN(preproc);
109 <preproc>\n BEGIN(INITIAL); return PREPROC;
110 <preproc>[^\n\\]+ extend(yytext);
111 <preproc>\\\n extend(yytext);
112 <preproc>\\+[^\n] extend(yytext);
114 [\"\'] string_end = yytext[0]; extend(yytext); BEGIN(rstring);
117 if (yytext[0] == string_end) {
122 <rstring>\\\n /* ignore */
125 "%s:%d: string continues past the end of the line\n",
126 yyfilename, yylineno);
132 <rstring>\\. extend(yytext);
133 <rstring>[^\\\"\'\n]+ extend(yytext);
135 "DEFUN" value = strdup(yytext); return DEFUNNY;
136 "DEFUN_NOSH" value = strdup(yytext); return DEFUNNY;
137 "DEFUN_HIDDEN" value = strdup(yytext); return DEFUNNY;
138 "DEFPY" value = strdup(yytext); return DEFUNNY;
139 "DEFPY_NOSH" value = strdup(yytext); return DEFUNNY;
140 "DEFPY_ATTR" value = strdup(yytext); return DEFUNNY;
141 "DEFPY_HIDDEN" value = strdup(yytext); return DEFUNNY;
142 "DEFPY_YANG" value = strdup(yytext); return DEFUNNY;
143 "DEFPY_YANG_NOSH" value = strdup(yytext); return DEFUNNY;
144 "ALIAS" value = strdup(yytext); return DEFUNNY;
145 "ALIAS_HIDDEN" value = strdup(yytext); return DEFUNNY;
146 "install_element" value = strdup(yytext); return INSTALL;
147 "VTYSH_TARGETS" value = strdup(yytext); return AUXILIARY;
148 "VTYSH_NODESWITCH" value = strdup(yytext); return AUXILIARY;
150 [ \t\n]+ /* ignore */
152 {ID} BEGIN(INITIAL); value = strdup(yytext); return ID;
153 {OPERATOR} BEGIN(INITIAL); value = strdup(yytext); return OPERATOR;
154 {SPECIAL} BEGIN(INITIAL); value = strdup(yytext); return SPECIAL;
155 . /* printf("-- '%s' in init\n", yytext); */ BEGIN(INITIAL); return yytext[0];
159 #endif /* __clang_analyzer__ */
161 static int yylex_clr(char **retbuf)
163 int rv = def_yylex();
169 static PyObject *get_args(const char *filename, int lineno)
171 PyObject *pyObj = PyList_New(0);
172 PyObject *pyArg = NULL;
178 while ((token = yylex_clr(&tval)) != YY_NULL) {
179 if (token == SPECIAL && tval[0] == '(') {
183 if (token == COMMENT) {
187 fprintf(stderr, "invalid input!\n");
191 while ((token = yylex_clr(&tval)) != YY_NULL) {
192 if (token == COMMENT) {
196 if (token == PREPROC) {
199 return PyErr_Format(PyExc_ValueError,
200 "%s:%d: cannot process CPP directive within argument list",
203 if (token == SPECIAL) {
204 if (depth == 1 && (tval[0] == ',' || tval[0] == ')')) {
206 PyList_Append(pyObj, pyArg);
208 if (tval[0] == ')') {
221 return PyErr_Format(PyExc_ValueError,
222 "%s:%d: invalid token in DEFPY parameters",
225 pyArg = PyList_New(0);
226 PyList_Append(pyArg, PyUnicode_FromString(tval));
232 /* _clippy.parse() -- read a C file, returning a list of interesting bits.
233 * note this ditches most of the actual C code. */
234 PyObject *clippy_parse(PyObject *self, PyObject *args)
236 const char *filename;
237 if (!PyArg_ParseTuple(args, "s", &filename))
240 FILE *fd = fopen(filename, "r");
242 return PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename);
248 yyfilename = filename;
250 PyObject *pyCont = PyDict_New();
251 PyObject *pyObj = PyList_New(0);
252 PyDict_SetItemString(pyCont, "filename", PyUnicode_FromString(filename));
253 PyDict_SetItemString(pyCont, "data", pyObj);
255 while ((token = yylex_clr(&tval)) != YY_NULL) {
256 int lineno = yylineno;
257 PyObject *pyItem = NULL, *pyArgs;
262 pyArgs = get_args(filename, lineno);
269 pyItem = PyDict_New();
270 PyDict_SetItemString(pyItem, "type", PyUnicode_FromString(tval));
271 PyDict_SetItemString(pyItem, "args", pyArgs);
274 if (strncmp(tval, "//~", 3) && strncmp(tval, "/*~", 3))
276 pyItem = PyDict_New();
277 PyDict_SetItemString(pyItem, "type", PyUnicode_FromString("COMMENT"));
278 PyDict_SetItemString(pyItem, "line", PyUnicode_FromString(tval));
281 pyItem = PyDict_New();
282 PyDict_SetItemString(pyItem, "type", PyUnicode_FromString("PREPROC"));
283 PyDict_SetItemString(pyItem, "line", PyUnicode_FromString(tval));
288 PyDict_SetItemString(pyItem, "lineno", PyLong_FromLong(lineno));
289 PyList_Append(pyObj, pyItem);