[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