[geany/geany] a50621: Merge pull request #3164 from techee/lisp_parser
Jiří Techet
git-noreply at geany.org
Thu May 12 23:49:37 UTC 2022
Branch: refs/heads/master
Author: Jiří Techet <techet at gmail.com>
Committer: GitHub <noreply at github.com>
Date: Thu, 12 May 2022 23:49:37 UTC
Commit: a506215ee782c6bdc3c016259c6a4c2d81a9cb9f
https://github.com/geany/geany/commit/a506215ee782c6bdc3c016259c6a4c2d81a9cb9f
Log Message:
-----------
Merge pull request #3164 from techee/lisp_parser
Add lisp ctags parser
Modified Paths:
--------------
ctags/Makefile.am
ctags/parsers/lisp.c
meson.build
src/filetypes.c
src/tagmanager/tm_parser.c
src/tagmanager/tm_parser.h
src/tagmanager/tm_parsers.h
tests/ctags/Makefile.am
tests/ctags/simple.lisp
tests/ctags/simple.lisp.tags
tests/meson.build
Modified: ctags/Makefile.am
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -70,6 +70,7 @@ parsers = \
parsers/julia.c \
parsers/geany_lcpp.c \
parsers/geany_lcpp.h \
+ parsers/lisp.c \
parsers/lua.c \
parsers/make.c \
parsers/make.h \
Modified: ctags/parsers/lisp.c
389 lines changed, 389 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,389 @@
+/*
+* Copyright (c) 2000-2002, Darren Hiebert
+*
+* This source code is released for free distribution under the terms of the
+* GNU General Public License version 2 or (at your option) any later version.
+*
+* This module contains functions for generating tags for LISP files.
+*/
+
+/*
+* INCLUDE FILES
+*/
+#include "general.h" /* must always come first */
+
+#include "parse.h"
+#include "read.h"
+#include "routines.h"
+#include "selectors.h"
+#include "vstring.h"
+
+#include <string.h>
+
+/*
+* DATA DEFINITIONS
+*/
+typedef enum {
+ K_UNKNOWN,
+ K_FUNCTION,
+ K_VARIABLE,
+ K_MACRO,
+ K_CONST,
+} lispKind;
+
+static kindDefinition LispKinds [] = {
+ { true, 'u', "unknown", "unknown type of definitions" },
+ { true, 'f', "function", "functions" },
+ { true, 'v', "variable", "variables" },
+ { true, 'm', "macro", "macros" },
+ { true, 'c', "const", "constants" },
+};
+
+typedef enum {
+ eK_UNKNOWN,
+ eK_FUNCTION,
+ eK_VARIABLE,
+ eK_CONST,
+ eK_MACRO,
+ eK_ALIAS,
+ eK_VARALIAS,
+ eK_SUBST,
+ eK_INLINE,
+ eK_ERROR,
+ eK_MINOR_MODE,
+ eK_DERIVED_MODE,
+ /* custom.el */
+ eK_CUSTOM,
+ eK_GROUP,
+ eK_FACE,
+ eK_THEME,
+} emacsLispKind;
+
+/* Following macro/builtin doesn't define a name appeared
+ * at car. So this parser doesn't handle it well.
+ * -----------------------------------------------------
+ * defadvice (=> cadadr)
+ * defconst-mode-local (=> cadr)
+ * defvar-mode-local (=> cadr)
+ */
+static kindDefinition EmacsLispKinds [] = {
+ { true, 'u', "unknown", "unknown type of definitions" },
+ { true, 'f', "function", "functions" },
+ { true, 'v', "variable", "variables" },
+ { true, 'c', "const", "constants" },
+ { true, 'm', "macro", "macros" },
+ { true, 'a', "alias", "aliases for functions" },
+ { true, 'V', "varalias", "aliases for variables" },
+ { true, 's', "subst", "inline function" },
+ { true, 'i', "inline", "inline function" },
+ { true, 'e', "error", "errors" },
+ { true, 'M', "minorMode", "minor modes" },
+ { true, 'D', "derivedMode", "derived major mode" },
+ /* custom.el */
+ { true, 'C', "custom", "customizable variables" },
+ { true, 'G', "group", "customization groups" },
+ { true, 'H', "face", "customizable faces" }, /* 'F' is reserved by ctags */
+ { true, 'T', "theme", "custom themes" },
+};
+
+/*
+* FUNCTION DEFINITIONS
+*/
+
+/*
+ * lisp tag functions
+ * look for (def or (DEF, quote or QUOTE
+ */
+static int L_isdef (const unsigned char *strp, bool case_insensitive)
+{
+ bool cis = case_insensitive; /* Renaming for making code short */
+
+ return ( (strp [1] == 'd' || (cis && strp [1] == 'D'))
+ && (strp [2] == 'e' || (cis && strp [2] == 'E'))
+ && (strp [3] == 'f' || (cis && strp [3] == 'F')));
+}
+
+static int L_isquote (const unsigned char *strp, bool case_insensitive)
+{
+ bool cis = case_insensitive; /* Renaming for making code short */
+
+ return ( (*(++strp) == 'q' || (cis && *strp == 'Q'))
+ && (*(++strp) == 'u' || (cis && *strp == 'U'))
+ && (*(++strp) == 'o' || (cis && *strp == 'O'))
+ && (*(++strp) == 't' || (cis && *strp == 'T'))
+ && (*(++strp) == 'e' || (cis && *strp == 'E'))
+ && isspace (*(++strp)));
+}
+
+static int lisp_hint2kind (const vString *const hint)
+{
+ int k = K_UNKNOWN;
+ int n;
+
+ /* 4 means strlen("(def"). */
+#define EQN(X) strncmp(vStringValue (hint) + 4, &X[3], n) == 0
+ switch (vStringLength (hint) - 4)
+ {
+ case 2:
+ n = 2;
+ if (EQN("DEFUN"))
+ k = K_FUNCTION;
+ break;
+ case 3:
+ n = 3;
+ if (EQN("DEFVAR"))
+ k = K_VARIABLE;
+ break;
+ case 5:
+ n = 5;
+ if (EQN("DEFMACRO"))
+ k = K_MACRO;
+ break;
+ case 8:
+ n = 8;
+ if (EQN("DEFCONSTANT"))
+ k = K_CONST;
+ break;
+ }
+#undef EQN
+ return k;
+}
+
+/* TODO: implement this in hashtable. */
+static int elisp_hint2kind (const vString *const hint)
+{
+ int k = eK_UNKNOWN;
+ int n;
+
+ /* 4 means strlen("(def"). */
+#define EQN(X) strncmp(vStringValue (hint) + 4, &X[3], n) == 0
+ switch (vStringLength (hint) - 4)
+ {
+ case 2:
+ n = 2;
+ if (EQN("defun"))
+ k = eK_FUNCTION;
+ break;
+ case 3:
+ n = 3;
+ if (EQN("defvar"))
+ k = eK_VARIABLE;
+ else if (EQN("defun*"))
+ k = eK_FUNCTION;
+ break;
+ case 4:
+ n = 4;
+ if (EQN("defface"))
+ k = eK_FACE;
+ case 5:
+ n = 5;
+ if (EQN("defconst"))
+ k = eK_CONST;
+ else if (EQN("defmacro"))
+ k = eK_MACRO;
+ else if (EQN("defalias"))
+ k = eK_ALIAS;
+ else if (EQN("defsubst"))
+ k = eK_SUBST;
+ else if (EQN("defgroup"))
+ k = eK_GROUP;
+ else if (EQN("deftheme"))
+ k = eK_THEME;
+ break;
+ case 6:
+ n = 6;
+ if (EQN("defcustom"))
+ k = eK_CUSTOM;
+ else if (EQN("defsubst*"))
+ k = eK_SUBST;
+ else if (EQN("defmacro*"))
+ k = eK_MACRO;
+ break;
+ case 7:
+ n = 7;
+ if (EQN("define-key"))
+ k = KIND_GHOST_INDEX;
+ break;
+ case 9:
+ n = 9;
+ if (EQN("defvar-local"))
+ k = eK_VARIABLE;
+ else if (EQN("define-error"))
+ k = eK_ERROR;
+ break;
+ case 8:
+ n = 8;
+ if (EQN("defvaralias"))
+ k = eK_VARALIAS;
+ break;
+ case 10:
+ n = 10;
+ if (EQN("define-inline"))
+ k = eK_INLINE;
+ break;
+ case 14:
+ n = 14;
+ if (EQN("define-minor-mode"))
+ k = eK_MINOR_MODE;
+ break;
+ case 16:
+ n = 16;
+ if (EQN("define-derived-mode"))
+ k = eK_DERIVED_MODE;
+ break;
+ case 21:
+ n = 21;
+ if (EQN("define-global-minor-mode"))
+ k = eK_MINOR_MODE;
+ break;
+ case 25:
+ n = 25;
+ if (EQN("define-globalized-minor-mode"))
+ k = eK_MINOR_MODE;
+ break;
+ case 27:
+ n = 27;
+ if (EQN("define-obsolete-function-alias"))
+ k = eK_ALIAS;
+ break;
+ }
+#undef EQN
+ return k;
+}
+
+static void L_getit (vString *const name, const unsigned char *dbp,
+ bool case_insensitive,
+ int (*hint2kind) (const vString *),
+ const vString *const kind_hint)
+{
+ const unsigned char *p;
+
+ if (*dbp == '\'') /* Skip prefix quote */
+ dbp++;
+ else if (*dbp == '(' && L_isquote (dbp, case_insensitive)) /* Skip "(quote " */
+ {
+ dbp += 7;
+ while (isspace (*dbp))
+ dbp++;
+ }
+ for (p=dbp ; *p!='\0' && *p!='(' && !isspace ((int) *p) && *p!=')' ; p++)
+ vStringPut (name, *p);
+
+ if (vStringLength (name) > 0)
+ {
+ int kind = hint2kind (kind_hint);
+ if (kind != KIND_GHOST_INDEX)
+ makeSimpleTag (name, kind);
+ }
+ vStringClear (name);
+}
+
+/* Algorithm adapted from from GNU etags.
+ */
+static void findLispTagsCommon (bool case_insensitive,
+ bool has_namespace,
+ int (*hint2kind) (const vString *))
+{
+ vString *name = vStringNew ();
+ vString *kind_hint = vStringNew ();
+ const unsigned char* p;
+
+
+ while ((p = readLineFromInputFile ()) != NULL)
+ {
+ if (*p == '(')
+ {
+ if (L_isdef (p, case_insensitive))
+ {
+ vStringClear (kind_hint);
+ while (*p != '\0' && !isspace ((int) *p))
+ {
+ vStringPut (kind_hint,
+ case_insensitive? toupper((int)*p): *p);
+ p++;
+ }
+ while (isspace ((int) *p))
+ p++;
+ L_getit (name, p, case_insensitive, hint2kind, kind_hint);
+ }
+ else if (has_namespace)
+ {
+ do
+ p++;
+ while (*p != '\0' && !isspace ((int) *p)
+ && *p != ':' && *p != '(' && *p != ')');
+ if (*p == ':')
+ {
+ do
+ p++;
+ while (*p == ':');
+
+ if (L_isdef (p - 1, case_insensitive))
+ {
+ vStringClear (kind_hint);
+ while (*p != '\0' && !isspace ((int) *p))
+ {
+ vStringPut (kind_hint,
+ case_insensitive? toupper((int)*p): *p);
+ p++;
+ }
+ while (isspace (*p))
+ p++;
+ L_getit (name, p, case_insensitive, hint2kind, kind_hint);
+ }
+ }
+ }
+ }
+ }
+ vStringDelete (name);
+ vStringDelete (kind_hint);
+}
+
+static void findLispTags (void)
+{
+ findLispTagsCommon (true, true, lisp_hint2kind);
+}
+
+static void findEmacsLispTags (void)
+{
+ findLispTagsCommon (false, false, elisp_hint2kind);
+}
+
+extern parserDefinition* LispParser (void)
+{
+ static const char *const extensions [] = {
+ "cl", "clisp", "l", "lisp", "lsp", NULL
+ };
+ static const char *const aliases [] = {
+ "clisp", NULL
+ };
+
+ static selectLanguage selectors[] = { selectLispOrLEXByLEXMarker, NULL };
+
+ parserDefinition* def = parserNew ("Lisp");
+ def->kindTable = LispKinds;
+ def->kindCount = ARRAY_SIZE (LispKinds);
+ def->extensions = extensions;
+ def->aliases = aliases;
+ def->parser = findLispTags;
+ def->selectLanguage = selectors;
+ return def;
+}
+
+extern parserDefinition* EmacsLispParser (void)
+{
+ static const char *const extensions [] = {
+ "el", NULL
+ };
+ static const char *const aliases [] = {
+ "emacs-lisp", NULL
+ };
+
+ parserDefinition* def = parserNew ("EmacsLisp");
+ def->kindTable = EmacsLispKinds;
+ def->kindCount = ARRAY_SIZE (EmacsLispKinds);
+ def->extensions = extensions;
+ def->aliases = aliases;
+ def->parser = findEmacsLispTags;
+ return def;
+}
Modified: meson.build
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -647,6 +647,7 @@ ctags = static_library('ctags',
'ctags/parsers/jscript.c',
'ctags/parsers/json.c',
'ctags/parsers/julia.c',
+ 'ctags/parsers/lisp.c',
'ctags/parsers/lua.c',
'ctags/parsers/make.c',
'ctags/parsers/make.h',
Modified: src/filetypes.c
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -164,7 +164,7 @@ static void init_builtin_filetypes(void)
FT_INIT( VHDL, VHDL, "VHDL", NULL, SOURCE_FILE, COMPILED );
FT_INIT( VERILOG, VERILOG, "Verilog", NULL, SOURCE_FILE, COMPILED );
FT_INIT( DIFF, DIFF, "Diff", NULL, FILE, MISC );
- FT_INIT( LISP, NONE, "Lisp", NULL, SOURCE_FILE, SCRIPT );
+ FT_INIT( LISP, LISP, "Lisp", NULL, SOURCE_FILE, SCRIPT );
FT_INIT( ERLANG, ERLANG, "Erlang", NULL, SOURCE_FILE, COMPILED );
FT_INIT( CONF, CONF, "Conf", _("Config"), FILE, MISC );
FT_INIT( PO, NONE, "Po", _("Gettext translation"), FILE, MISC );
Modified: src/tagmanager/tm_parser.c
15 lines changed, 15 insertions(+), 0 deletions(-)
===================================================================
@@ -987,6 +987,20 @@ static TMParserMapGroup group_CLOJURE[] = {
{_("Functions"), TM_ICON_METHOD, tm_tag_function_t},
};
+static TMParserMapEntry map_LISP[] = {
+ {'u', tm_tag_undef_t}, // unknown
+ {'f', tm_tag_function_t}, // function
+ {'v', tm_tag_variable_t}, // variable
+ {'m', tm_tag_macro_t}, // macro
+ {'c', tm_tag_field_t}, // const
+};
+static TMParserMapGroup group_LISP[] = {
+ {_("Functions"), TM_ICON_METHOD, tm_tag_function_t},
+ {_("Macros"), TM_ICON_MACRO, tm_tag_macro_t},
+ {_("Variables"), TM_ICON_VAR, tm_tag_variable_t},
+ {_("Constants"), TM_ICON_VAR, tm_tag_field_t},
+};
+
typedef struct
{
TMParserMapEntry *entries;
@@ -1055,6 +1069,7 @@ static TMParserMap parser_map[] = {
MAP_ENTRY(CPREPROCESSOR),
MAP_ENTRY(TCLOO),
MAP_ENTRY(CLOJURE),
+ MAP_ENTRY(LISP),
};
/* make sure the parser map is consistent and complete */
G_STATIC_ASSERT(G_N_ELEMENTS(parser_map) == TM_PARSER_COUNT);
Modified: src/tagmanager/tm_parser.h
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -114,6 +114,7 @@ enum
TM_PARSER_CPREPROCESSOR,
TM_PARSER_TCLOO,
TM_PARSER_CLOJURE,
+ TM_PARSER_LISP,
TM_PARSER_COUNT
};
Modified: src/tagmanager/tm_parsers.h
3 lines changed, 2 insertions(+), 1 deletions(-)
===================================================================
@@ -69,6 +69,7 @@
BibtexParser, \
CPreProParser, \
TclOOParser, \
- ClojureParser
+ ClojureParser, \
+ LispParser
#endif
Modified: tests/ctags/Makefile.am
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -300,6 +300,7 @@ test_sources = \
simple.js \
simple.json \
simple.ksh \
+ simple.lisp \
simple.lua \
simple.mak \
simple.md \
Modified: tests/ctags/simple.lisp
17 lines changed, 17 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,17 @@
+(defun a0 (n) (+ 1 n))
+(DEFUN A1 (n) (+ 2 n))
+
+(defvar b0 3)
+(DEFVAR B1 4)
+
+(defconstant c0 5)
+(DEFCONSTANT C1 6)
+
+(defmacro defunknown0 (s)
+ `(defconstant ,s 'unknown))
+
+(DEFMACRO DEFUNKNOWN1 (s)
+ `(defconstant ,s 'unknown))
+
+(defunknown1 unknown)
+
Modified: tests/ctags/simple.lisp.tags
9 lines changed, 9 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,9 @@
+# format=tagmanager
+A1�16�0
+B1�16384�0
+C1�8�0
+DEFUNKNOWN1�65536�0
+a0�16�0
+b0�16384�0
+c0�8�0
+defunknown0�65536�0
Modified: tests/meson.build
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -296,6 +296,7 @@ ctags_tests = files([
'ctags/simple.js.tags',
'ctags/simple.json.tags',
'ctags/simple.ksh.tags',
+ 'ctags/simple.lisp.tags',
'ctags/simple.lua.tags',
'ctags/simple.mak.tags',
'ctags/simple.md.tags',
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
More information about the Commits
mailing list