%option noyywrap
%option yylineno
%option noinput
%{
/**
# Error message parsing for compilation of GLSL or CUDA code on GPUs
*/
static char * sout;
static char * slang;
static char * fname;
static int line;
static char * str_append (char * dst, char * src) {
if (dst == NULL) {
dst = malloc (strlen(src) + 1);
dst[0] = '\0';
}
else
dst = realloc (dst, strlen(dst) + strlen(src) + 1);
strcat (dst, src);
return dst;
}
static int atoi_check (char * start, char * end)
{
char * s = start;
while (s != end) {
if (*s < '0' || *s > '9')
return -1;
s++;
}
*end = '\0';
return atoi (start);
}
/* parse Intel GPU error messages of the form:
0:31(15):... */
static int parse_intel (char * errors, char ** msg)
{
char * end = strchr (errors, '\n');
char * sline = strchr (errors, ':');
if (!sline) return -1; sline++;
if (end && end < sline) return -1;
char * s1 = strchr (sline, '(');
if (!s1) return -1;
*msg = strchr (s1, ':');
if (!*msg) return -1; (*msg)++;
return atoi_check (sline, s1);
}
/* parse Nvidia GPU error messages of the form:
0(31) :... */
static int parse_nvidia (char * errors, char ** msg)
{
char * end = strchr (errors, '\n');
char * sline = strchr (errors, '(');
if (!sline) return -1; sline++;
if (end && end < sline) return -1;
char * s1 = strchr (sline, ')');
if (!s1) return -1;
*msg = strchr (s1, ':');
if (!*msg) return -1; (*msg)++;
return atoi_check (sline, s1);
}
/* parse OpenCL error messages of the form:
<kernel>:368:78:... */
static int parse_opencl (char * errors, char ** msg)
{
char * end = strchr (errors, '\n');
char * sline = strchr (errors, ':');
if (!sline) return -1; sline++;
if (end && end < sline) return -1;
char * s1 = strchr (sline, ':');
if (!s1) return -1;
*msg = strchr (s1 + 1, ':');
if (!*msg) return -1; (*msg)++;
return atoi_check (sline, s1);
}
static char * next_error (char * errors, int * line, char ** msg)
{
if (!errors || *errors == '\0')
return NULL;
*line = parse_intel (errors, msg);
if (*line < 0)
*line = parse_nvidia (errors, msg);
if (*line < 0)
*line = parse_opencl (errors, msg);
if (*line < 0) {
// unknown error format: assumes first line
*line = 1;
*msg = errors;
}
while (strchr (" \t", **msg)) (*msg)++;
char * s3 = strchr (*msg, '\n');
if (s3) {
*s3 = '\0';
s3++;
}
return s3;
}
static char * merge (char * source, char * errors)
{
char * merged = NULL;
char * msg;
int eline, line = 1;
char * error = next_error (errors, &eline, &msg);
char * src = strdup (source), * s = src;
while (*s != '\0') {
if (*s == '\n') {
while (error && eline <= line) {
if (msg[0] != '\0' && !strstr(msg, "error detected")) {
merged = str_append (merged, "@");
if (eline != line)
merged = str_append (merged, "!");
merged = str_append (merged, msg);
merged = str_append (merged, "@");
}
error = next_error (error, &eline, &msg);
}
line++;
}
char c[] = " ";
c[0] = *s;
merged = str_append (merged, c);
s++;
}
free (src);
return merged;
}
%}
ID [a-zA-Z0-9_]
SP [ \t]
WS [ \t\v\n\f]
ES (\\([\'\"\?\\abfnrtv]|[0-7]{1,3}|x[a-fA-F0-9]+))
%%
^"//"{SP}*#{SP}*(line)?{SP}+[0-9]+({SP}+\".*\")? {
char * sline = yytext;
while (!strchr ("0123456789", *sline)) sline++;
line = atoi (sline) - 1;
if (strchr (yytext, '"')) {
fname = sline; while (*fname >= '0' && *fname <= '9') fname++;
*fname = '\0'; fname++;
fname = strchr (fname, '"'); fname++;
*strchr (fname, '"') = '\0';
}
}
@[^@]*@ {
if (0) // !strncmp (yytext, "@error ", 7))
yytext += 6;
else {
if (fname)
sout = str_append (sout, fname);
else
sout = str_append (sout, "(fragment shader)");
sout = str_append (sout, ":");
char s[81];
snprintf (s, 80, "%d", line);
sout = str_append (sout, s);
sout = str_append (sout, ": ");
sout = str_append (sout, slang);
sout = str_append (sout, ": ");
}
*strchr (yytext + 1, '@') = '\0';
sout = str_append (sout, yytext + 1);
sout = str_append (sout, "\n");
}
\n { line++; }
. ; // very important!!
%%
char * gpu_errors (char * errors, char * source, char * fout,
char * lang)
{
if (0) yyunput (0, NULL); // just prevents 'yyunput unused' compiler warning
char * merged = merge (source, errors);
sout = fout;
sout = str_append (NULL, "");
slang = lang;
YY_BUFFER_STATE buf = yy_scan_string (merged);
fname = NULL;
line = 1;
yylex();
yy_delete_buffer (buf);
free (merged);
return sout;
}