Branch: refs/heads/master
Author: Colomban Wendling <ban(a)herbesfolles.org>
Committer: Colomban Wendling <ban(a)herbesfolles.org>
Date: Mon, 15 Apr 2013 16:55:14 UTC
Commit: c2e4111aaf9a7bc2833568fa7248833f201c7c8b
https://github.com/geany/geany/commit/c2e4111aaf9a7bc2833568fa7248833f201c7…
Log Message:
-----------
PHP: parse HereDoc and NowDoc strings
Modified Paths:
--------------
tagmanager/ctags/php.c
Modified: tagmanager/ctags/php.c
140 files changed, 140 insertions(+), 0 deletions(-)
===================================================================
@@ -492,6 +492,129 @@ static void parseString (vString *const string, const int delimiter)
vStringTerminate (string);
}
+/* reads an HereDoc or a NowDoc (the part after the <<<).
+ * <<<[ \t]*(ID|'ID'|"ID")
+ * ...
+ * ID;?
+ *
+ * note that:
+ * 1) starting ID must be immediately followed by a newline;
+ * 2) closing ID is the same as opening one;
+ * 3) closing ID must be immediately followed by a newline or a semicolon
+ * then a newline.
+ *
+ * Example of a *single* valid heredoc:
+ * <<< FOO
+ * something
+ * something else
+ * FOO this is not an end
+ * FOO; this isn't either
+ * FOO; # neither this is
+ * FOO;
+ * # previous line was the end, but the semicolon wasn't required
+ */
+static void parseHeredoc (vString *const string)
+{
+ int c;
+ unsigned int len;
+ char delimiter[64]; /* arbitrary limit, but more is crazy anyway */
+ int quote = 0;
+
+ do
+ {
+ c = fileGetc ();
+ }
+ while (c == ' ' || c == '\t');
+
+ if (c == '\'' || c == '"')
+ {
+ quote = c;
+ c = fileGetc ();
+ }
+ for (len = 0; len < (sizeof delimiter / sizeof delimiter[0]) - 1; len++)
+ {
+ if (! isIdentChar (c))
+ break;
+ delimiter[len] = (char) c;
+ c = fileGetc ();
+ }
+ delimiter[len] = 0;
+
+ if (len == 0) /* no delimiter, give up */
+ goto error;
+ if (quote)
+ {
+ if (c != quote) /* no closing quote for quoted identifier, give up */
+ goto error;
+ c = fileGetc ();
+ }
+ if (c != '\r' && c != '\n') /* missing newline, give up */
+ goto error;
+
+ do
+ {
+ c = fileGetc ();
+
+ if (c != '\r' && c != '\n')
+ vStringPut (string, (char) c);
+ else
+ {
+ /* new line, check for a delimiter right after */
+ int nl = c;
+ int extra = EOF;
+
+ c = fileGetc ();
+ for (len = 0; c != 0 && (c - delimiter[len]) == 0; len++)
+ c = fileGetc ();
+
+ if (delimiter[len] != 0)
+ fileUngetc (c);
+ else
+ {
+ /* line start matched the delimiter, now check whether there
+ * is anything after it */
+ if (c == '\r' || c == '\n')
+ {
+ fileUngetc (c);
+ break;
+ }
+ else if (c == ';')
+ {
+ int d = fileGetc ();
+ if (d == '\r' || d == '\n')
+ {
+ /* put back the semicolon since it's not part of the
+ * string. we can't put back the newline, but it's a
+ * whitespace character nobody cares about it anyway */
+ fileUngetc (';');
+ break;
+ }
+ else
+ {
+ /* put semicolon in the string and continue */
+ extra = ';';
+ fileUngetc (d);
+ }
+ }
+ }
+ /* if we are here it wasn't a delimiter, so put everything in the
+ * string */
+ vStringPut (string, (char) nl);
+ vStringNCatS (string, delimiter, len);
+ if (extra != EOF)
+ vStringPut (string, (char) extra);
+ }
+ }
+ while (c != EOF);
+
+ vStringTerminate (string);
+
+ return;
+
+error:
+ fileUngetc (c);
+}
+
static void parseIdentifier (vString *const string, const int firstChar)
{
int c = firstChar;
@@ -597,6 +720,23 @@ static void readToken (tokenInfo *const token)
token->filePosition = getInputFilePosition ();
break;
+ case '<':
+ {
+ int d;
+ if ((d = fileGetc ()) != '<' ||
+ (d = fileGetc ()) != '<')
+ {
+ fileUngetc (d);
+ token->type = TOKEN_UNDEFINED;
+ }
+ else
+ {
+ token->type = TOKEN_STRING;
+ parseHeredoc (token->string);
+ }
+ break;
+ }
+
case '#': /* comment */
skipToEndOfLine ();
goto getNextChar;
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).