Skip to content

Commit

Permalink
Add AutoIt syntax highlighting and Ctags parser for AutoIt
Browse files Browse the repository at this point in the history
  • Loading branch information
Skif-off committed Apr 13, 2023
1 parent e2ce7db commit e14a076
Show file tree
Hide file tree
Showing 21 changed files with 1,415 additions and 2 deletions.
1 change: 1 addition & 0 deletions ctags/Makefile.am
Expand Up @@ -48,6 +48,7 @@ parsers = \
parsers/ada.c \
parsers/asciidoc.c \
parsers/asm.c \
parsers/autoit.c \
parsers/basic.c \
parsers/bibtex.c \
parsers/geany_c.c \
Expand Down
319 changes: 319 additions & 0 deletions ctags/parsers/autoit.c
@@ -0,0 +1,319 @@
/*
* Copyright (c) 2017, Alexey Olenich
* Copyright (c) 2018, Colomban Wendling <colomban@geany.org>
*
* 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 AutoIt functions.
* Homepage https://www.autoitscript.com/site/autoit/
* Online Documentation https://www.autoitscript.com/autoit3/docs/
*/

/*
* INCLUDE FILES
*/
#include "general.h" /* must always come first */

#include <string.h>

#include "parse.h"
#include "entry.h"
#include "nestlevel.h"
#include "read.h"
#include "routines.h"
#include "vstring.h"

/*
* DATA DEFINITIONS
*/
typedef enum {
K_FUNCTION,
K_REGION,
K_GLOBALVAR,
K_LOCALVAR,
K_SCRIPT,
} AutoItKind;

typedef enum {
R_INCLUDE_SYSTEM,
R_INCLUDE_LOCAL,
} AutoItIncludeRole;

typedef enum {
F_PROPERTIES,
} AutoItField;

static roleDefinition AutoItIncludeRoles [] = {
{ true, "system", "system include" },
{ true, "local", "local include" },
};

static kindDefinition AutoItKinds [] = {
{ true, 'f', "func", "functions" },
{ true, 'r', "region", "regions" },
{ true, 'g', "global", "global variables" },
{ true, 'l', "local", "local variables" },
{ true, 'S', "script", "included scripts",
.referenceOnly = true, ATTACH_ROLES (AutoItIncludeRoles) },
};

static fieldDefinition AutoItFields [] = {
{
.name = "properties",
.description = "properties (static, volatile, ...)",
.enabled = false,
},
};

/*
* FUNCTION DEFINITIONS
*/

/* it's unclear what *is* an identifier character, so maybe we're too strict */
static bool isIdentChar (int c)
{
return isalnum (c) || c == '_';
}

static bool match (const unsigned char *line, const char *word,
const unsigned char **positionAfter)
{
size_t len = strlen (word);
bool matched = (strncasecmp ((const char*) line, word, len) == 0 &&
! isIdentChar (line[len]));

if (matched && positionAfter)
*positionAfter = line + len;

return matched;
}

static int makeAutoItTag (const NestingLevels *const nls,
const vString* const name,
const int kindIndex,
const vString *const signature)
{
int r = CORK_NIL;

if (isInputLanguageKindEnabled(kindIndex) && name != NULL && vStringLength (name) > 0)
{
NestingLevel *nl = nestingLevelsGetCurrent (nls);
tagEntryInfo e;

initTagEntry (&e, vStringValue (name), kindIndex);

if (nl)
e.extensionFields.scopeIndex = nl->corkIndex;
if (signature)
e.extensionFields.signature = vStringValue (signature);

r = makeTagEntry (&e);
}

return r;
}

static int makeSimpleAutoItTag (const NestingLevels *const nls,
const vString* const name,
const int kindIndex)
{
return makeAutoItTag (nls, name, kindIndex, NULL);
}

static void setEndLine (const NestingLevels *const nls)
{
NestingLevel *nl = nestingLevelsGetCurrent (nls);
tagEntryInfo *entry;

if (nl && (entry = getEntryInCorkQueue (nl->corkIndex)) != NULL)
entry->extensionFields.endLine = getInputLineNumber ();
}

static void skipSpaces (const unsigned char **p)
{
while (isspace ((int) **p))
++(*p);
}

/* parses after a "func" keyword */
static int parseFunc (const unsigned char *p, NestingLevels *nls)
{
int k = CORK_NIL;
vString *name = vStringNew ();

skipSpaces (&p);
while (isIdentChar ((int) *p))
{
vStringPut (name, (int) *p);
++p;
}
skipSpaces (&p);
if (*p == '(' && (vStringLength (name) > 0))
{
vString *signature = vStringNew ();

do
vStringPut (signature, (int) *p);
while (*p != ')' && *p++);

k = makeAutoItTag (nls, name, K_FUNCTION, signature);
nestingLevelsPush (nls, k);
vStringDelete (signature);
}

vStringDelete (name);

return k;
}

static void findAutoItTags (void)
{
vString *name = vStringNew ();
const unsigned char *line;
NestingLevels *nls = nestingLevelsNew (0);
unsigned int commentDepth = 0;

while ((line = readLineFromInputFile ()) != NULL)
{
const unsigned char* p = line;
if (p [0] == '#')
{
p++;
if (match (p, "cs", NULL) || match (p, "comments-start", NULL))
commentDepth++;
else if (commentDepth > 0)
{
if (match (p, "ce", NULL) || match (p, "comments-end", NULL))
commentDepth--;
}
else if (match (p, "region", &p))
{
skipSpaces (&p);
while (*p != '\0')
{
vStringPut (name, (int) *p);
++p;
}

if (vStringLength(name) > 0)
{
int k = makeSimpleAutoItTag (nls, name, K_REGION);
nestingLevelsPush (nls, k);
vStringClear (name);
}
}
else if (nls->n > 0 && match (p, "endregion", NULL))
{
setEndLine (nls);
nestingLevelsPop (nls);
}
else if (match (p, "include", &p))
{
skipSpaces (&p);
if (*p == '<' || *p == '"')
{
const AutoItIncludeRole role = (*p == '<')
? R_INCLUDE_SYSTEM
: R_INCLUDE_LOCAL;

++p;
while (*p != '\0' && *p != '>' && *p != '"')
{
vStringPut (name, (int) *p);
++p;
}
if (vStringLength(name) > 0)
{
makeSimpleRefTag (name, K_SCRIPT, role);
vStringClear (name);
}
}
}
}
else if (commentDepth == 0)
{
bool isGlobal = false;
bool isStatic = false;

/* skip white space */
skipSpaces (&p);
if (*p == ';')
/* ignore single-line comments */;
else if (match (p, "volatile", &p))
{
skipSpaces (&p);
if (match (p, "func", &p))
{
int k = parseFunc (p, nls);
if (k != CORK_NIL)
attachParserFieldToCorkEntry (k, AutoItFields[F_PROPERTIES].ftype, "volatile");
}
}
else if (match (p, "func", &p))
parseFunc (p, nls);
else if (nls->n > 0 && match (p, "endfunc", NULL))
{
setEndLine (nls);
nestingLevelsPop (nls);
}
else if ((isStatic = match (p, "static", &p)) ||
(isGlobal = match (p, "global", &p)) ||
match (p, "local", &p))
{
/*
* variable-identifier ::= "$[a-zA-Z_0-9]+"
* scope-modifier ::= "Local" | "Global"
* variable-declaration ::= "Static" [scope-modifier] variable-identifier
* scope-modifier ["Static" | "Const"] variable-identifier
*/
skipSpaces (&p);
if (isStatic)
{
/* skip optional modifiers */
if ((isGlobal = match (p, "global", &p)) ||
match (p, "local", &p))
{
skipSpaces (&p);
}
}
else if ((isStatic = match (p, "static", &p)))
skipSpaces (&p);
else if (match (p, "const", &p))
skipSpaces (&p);
if (*p == '$')
{
vStringPut (name, (int) *p++);
while (isIdentChar ((int) *p))
{
vStringPut (name, (int) *p);
++p;
}
if (vStringLength(name) > 0)
{
int k = makeSimpleAutoItTag (nls, name, isGlobal ? K_GLOBALVAR : K_LOCALVAR);
if (k != CORK_NIL && isStatic)
attachParserFieldToCorkEntry (k, AutoItFields[F_PROPERTIES].ftype, "static");
}
vStringClear (name);
}
}
}
}
vStringDelete (name);
nestingLevelsFree (nls);
}

parserDefinition *AutoItParser (void)
{
static char const *extensions[] = { "au3", "AU3", "aU3", "Au3", NULL };
parserDefinition* def = parserNew ("AutoIt");
def->kindTable = AutoItKinds;
def->kindCount = ARRAY_SIZE (AutoItKinds);
def->fieldTable = AutoItFields;
def->fieldCount = ARRAY_SIZE (AutoItFields);
def->extensions = extensions;
def->parser = findAutoItTags;
def->useCork = CORK_QUEUE;
return def;
}
1 change: 1 addition & 0 deletions data/Makefile.am
Expand Up @@ -10,6 +10,7 @@ filetypes_dist = \
filedefs/filetypes.Arduino.conf \
filedefs/filetypes.asciidoc \
filedefs/filetypes.asm \
filedefs/filetypes.autoit \
filedefs/filetypes.batch \
filedefs/filetypes.bibtex \
filedefs/filetypes.c \
Expand Down

0 comments on commit e14a076

Please sign in to comment.