Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

R: Add regular expression R parser #544

Merged
merged 4 commits into from
Sep 3, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Units/parser-r.r/r-extended.d/expected.tags
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.First input.r /^.First <- function() {$/;" f
MASS input.r /^ library(MASS) # attach a package$/;" l
file.path(Sys.getenv("HOME" input.r /^ source(file.path(Sys.getenv("HOME"), "R", "mystuff.R"))$/;" s
11 changes: 11 additions & 0 deletions Units/parser-r.r/r-extended.d/input.r
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# From:
# https://cran.r-project.org/doc/manuals/r-release/R-intro.html#Customizing-the-environment
.First <- function() {
options(prompt="$ ", continue="+\t") # $ is the prompt
options(digits=5, length=999) # custom numbers and printout
x11() # for graphics
par(pch = "+") # plotting character
source(file.path(Sys.getenv("HOME"), "R", "mystuff.R"))
# my personal functions
library(MASS) # attach a package
}
3 changes: 3 additions & 0 deletions Units/parser-r.r/r-simple.d/expected.tags
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
foo input.r /^foo <- function () {$/;" f
x input.r /^x <- 1$/;" g
y input.r /^ y <- 2$/;" v
7 changes: 7 additions & 0 deletions Units/parser-r.r/r-simple.d/input.r
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copuied from:
# http://stackoverflow.com/questions/32206608/ctags-and-r-regex
x <- 1
foo <- function () {
y <- 2
return(y)
}
1 change: 1 addition & 0 deletions main/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
Perl6Parser, \
PhpParser, \
PythonParser, \
RParser, \
RexxParser, \
RstParser, \
RubyParser, \
Expand Down
215 changes: 215 additions & 0 deletions parsers/r.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
* Copyright (c) 2003-2004, Ascher Stefan <stievie@utanet.at>
*
* This source code is released for free distribution under the terms of the
* GNU General Public License.
*
* This module contains functions for generating tags for R language files.
* R is a programming language for statistical computing.
* R is GPL Software, get it from http://www.r-project.org/
*/

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

#include <string.h>
#include <ctype.h> /* to define isalpha(), isalnum(), isspace() */

#include "debug.h"
#include "entry.h"
#include "read.h"
#include "vstring.h"

#define SKIPSPACE(ch) while (isspace((int)*ch)) \
ch++

typedef enum {
K_FUNCTION,
K_LIBRARY,
K_SOURCE,
K_GLOBALVAR,
K_FUNCVAR,
KIND_COUNT
} rKind;

static kindOption RKinds[KIND_COUNT] = {
{TRUE, 'f', "function", "functions"},
{TRUE, 'l', "library", "libraries"},
{TRUE, 's', "source", "sources"},
{TRUE, 'g', "globalVar", "global variables"},
{TRUE, 'v', "functionVar", "function variables"},
};

static void makeRTag (const vString * const name, rKind kind)
{
tagEntryInfo e;
initTagEntry (&e, vStringValue (name));

Assert (kind < KIND_COUNT);

e.kindName = RKinds[kind].name;
e.kind = RKinds[kind].letter;

makeTagEntry (&e);
}

static void createRTags (void)
{
vString *vLine = vStringNew ();
vString *name = vStringNew ();
int ikind;
const unsigned char *line;

while ((line = fileReadLine ()) != NULL)
{
const unsigned char *cp = (const unsigned char *) line;

vStringClear (name);
while ((*cp != '\0') && (*cp != '#'))
{
/* iterate to the end of line or to a comment */
ikind = -1;
switch (*cp)
{
case 'l':
case 's':
if (strncasecmp ((const char *) cp, "library", (size_t) 7) == 0)
{
/* load a library: library(tools) */
cp += 7;
SKIPSPACE (cp);
if (*cp == '(')
ikind = K_LIBRARY;
else
cp -= 7;
}
else if (strncasecmp ((const char *) cp, "source",
(size_t) 6) == 0)
{
/* load a source file: source("myfile.r") */
cp += 6;
SKIPSPACE (cp);
if (*cp == '(')
ikind = K_SOURCE;
else
cp -= 6;
}
if (ikind != -1)
{
cp++;

vStringClear (name);
while ((!isspace ((int) *cp)) && *cp != '\0' && *cp != ')')
{
vStringPut (name, (int) *cp);
cp++;
}
vStringTerminate (name);

/* if the string really exists, make a tag of it */
if (vStringLength (name) > 0)
makeRTag (name, ikind);

/* prepare for the next iteration */
vStringClear (name);
}
else
{
vStringPut (name, (int) *cp);
cp++;
}
break;
case '<':
cp++;
if (*cp == '-')
{
/* assignment: ident <- someval */
cp++;
SKIPSPACE (cp);

if (*cp == '\0')
{
/* not in this line, read next */
/* sometimes functions are declared this way:
* ident <-
* function(...)
* {
* ...
* }
* I don't know if there is a reason to write the function keyword
* in a new line
*/
if ((line = fileReadLine ()) != NULL)
{
cp = (const unsigned char *) line;
SKIPSPACE (cp);
}
}

if (strncasecmp ((const char *) cp, "function",
(size_t) 8) == 0)
{
/* it's a function: ident <- function(args) */
cp += 8;
vStringTerminate (name);
/* if the string really exists, make a tag of it */
if (vStringLength (name) > 0)
makeRTag (name, K_FUNCTION);

/* prepare for the next iteration */
vStringClear (name);
break;
}
else
{
/* it's a variable: ident <- value */
vStringTerminate (name);
/* if the string really exists, make a tag of it */
if (vStringLength (name) > 0)
{
if (line[0] == ' ' || line[0] == '\t')
makeRTag (name, K_FUNCVAR);
else
makeRTag (name, K_GLOBALVAR);
}

/* prepare for the next iteration */
vStringClear (name);
break;
}
}
case ' ':
case '\x009':
/* skip whitespace */
cp++;
break;
default:
/* collect all characters that could be a part of an identifier */
vStringPut (name, (int) *cp);
cp++;
break;
}
}
}

vStringDelete (name);
vStringDelete (vLine);
}

extern parserDefinition *RParser (void)
{
/* *.r: R files
* *.s;*.q: S files
*/
static const char *const extensions[] = { "r", "s", "q", NULL };
parserDefinition *const def = parserNew ("R");
def->extensions = extensions;
def->kinds = RKinds;
def->kindCount = KIND_COUNT;
def->parser = createRTags;
return def;
}

/* vi:set tabstop=4 shiftwidth=4 noexpandtab: */
1 change: 1 addition & 0 deletions source.mak
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ PARSER_SOURCES = \
$(PARSER_DIR)/perl6.c \
$(PARSER_DIR)/php.c \
$(PARSER_DIR)/python.c \
$(PARSER_DIR)/r.c \
$(PARSER_DIR)/rexx.c \
$(PARSER_DIR)/rst.c \
$(PARSER_DIR)/ruby.c \
Expand Down
1 change: 1 addition & 0 deletions win32/ctags_vs2013.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
<WarningLevel Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">TurnOffAllWarnings</WarningLevel>
</ClCompile>
<ClCompile Include="..\parsers\rst.c" />
<ClCompile Include="..\parsers\r.c" />
<ClCompile Include="..\parsers\rexx.c" />
<ClCompile Include="..\main\routines.c" />
<ClCompile Include="..\parsers\ruby.c" />
Expand Down
3 changes: 3 additions & 0 deletions win32/ctags_vs2013.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@
<ClCompile Include="..\parsers\python.c">
<Filter>Source Files\Parsers</Filter>
</ClCompile>
<ClCompile Include="..\parsers\r.c">
<Filter>Source Files\Parsers</Filter>
</ClCompile>
<ClCompile Include="..\parsers\rexx.c">
<Filter>Source Files\Parsers</Filter>
</ClCompile>
Expand Down