added logic for preprocessor directives

This commit is contained in:
cozis
2022-05-05 21:51:38 +02:00
parent 401df276a6
commit f2f2db423f
3 changed files with 130 additions and 29 deletions
+2 -2
View File
@@ -1,3 +1,3 @@
c2html
example
*.html
*.html
samples
+127 -27
View File
@@ -52,6 +52,7 @@ typedef enum {
T_FCALLNAME,
T_IDENTIFIER,
T_OPERATOR,
T_DIRECTIVE,
} Kind;
typedef struct {
@@ -103,7 +104,8 @@ static Token *tokenize(const char *str, long len)
Token T;
long curly_bracket_depth = 0;
long bracket_depth = 0;
bool only_spaces_since_line_start = 1;
bool prev_nonspace_was_directive = 0;
do {
if(i == len) {
T.kind = T_DONE;
@@ -193,41 +195,121 @@ static Token *tokenize(const char *str, long len)
T.kind = T_VFLT;
} else T.kind = T_VINT;
T.len = i - T.off;
} else if(isalpha(str[i]) || str[i] == '_') {
T.off = i;
while(isalpha(str[i]) || isdigit(str[i]) || str[i] == '_')
i += 1;
T.len = i - T.off;
/* It may either be an identifier or a
* language keyword.
*/
if(iskword(str + T.off, T.len))
T.kind = T_KWORD;
else {
/* If the identifier is followed by a '(' and
* it's in the global scope, then it's in a
* function definiton. If it's not in the global
* scope then it's a function call.
* Between the identifier and the '(' there may
* be some whitespace. An exception is made if
* before the identifier comes a preprocessor
* directive, in which case the '(' must come
* right after the identifier.
*/
bool followed_by_parenthesis = 0;
bool yes_and_immediately = 0;
{
long k = i;
while(k < len && (str[k] == ' ' || str[k] == '\t'))
k += 1;
if(k < len && str[k] == '(') {
followed_by_parenthesis = 1;
if(k == i)
yes_and_immediately = 1;
}
}
if(followed_by_parenthesis) {
if(curly_bracket_depth == 0) {
if(prev_nonspace_was_directive) {
if(yes_and_immediately)
T.kind = T_FDECLNAME;
else
T.kind = T_IDENTIFIER;
} else
T.kind = T_FDECLNAME;
} else
T.kind = T_FCALLNAME;
} else {
T.kind = T_IDENTIFIER;
}
}
} else if(str[i] == '#' && only_spaces_since_line_start) {
// The first non-whitespace token of the line
// is a '#'. If it's followed by an alphabetical
// character, then it's a directive. (There may
// be whitespace between the '#' and the identifier)
long j = i; // Use a secondary cursor to explore
// what's after the '#'.
j += 1; // Skip the '#'.
// Skip spaces after the '#', if there are any.
while(j < len && (str[j] == ' ' || str[j] == '\t'))
j += 1;
if(isalpha(str[j])) {
// It's a preprocessor directive!
T.kind = T_DIRECTIVE;
T.off = i;
while(j < len && isalpha(str[j]))
j += 1;
T.len = j - T.off;
i = j;
} else {
// Wasn't a directive.. Just tokenize the '#'.
T.kind = '#';
T.off = i;
T.len = 1;
i += 1;
}
} else if(str[i] == '<' && prev_nonspace_was_directive) {
T.kind = T_VSTR;
T.off = i;
while(i < len && str[i] != '>')
i += 1;
if(i < len)
i += 1; // Skip the '>'.
T.len = i - T.off;
} else if(isoperat(str[i])) {
T.kind = T_OPERATOR;
T.off = i;
while(i < len && isoperat(str[i]))
i += 1;
T.len = i - T.off;
} else if(isalpha(str[i]) || str[i] == '_') {
T.off = i;
while(isalpha(str[i]) || isdigit(str[i]) || str[i] == '_')
i += 1;
T.len = i - T.off;
if(iskword(str + T.off, T.len))
T.kind = T_KWORD;
else {
// Is the identifier followed by
// a left parenthesis?
long k = i;
while(k < len && (str[k] == ' ' || str[k] == '\t'))
k += 1;
if(k < len && str[k] == '(') {
if(curly_bracket_depth == 0)
T.kind = T_FDECLNAME;
else
T.kind = T_FCALLNAME;
} else {
T.kind = T_IDENTIFIER;
}
}
} else {
switch(str[i]) {
case '{': curly_bracket_depth += 1; break;
case '}': curly_bracket_depth -= 1; break;
case '(': bracket_depth += 1; break;
case ')': bracket_depth -= 1; break;
}
T.kind = str[i];
@@ -236,6 +318,18 @@ static Token *tokenize(const char *str, long len)
i += 1;
}
if(T.kind == T_NEWL)
only_spaces_since_line_start = 1;
else
if(T.kind != T_TAB && T.kind != T_SPACE)
only_spaces_since_line_start = 0;
if(T.kind == T_DIRECTIVE)
prev_nonspace_was_directive = 1;
else
if(T.kind != T_TAB && T.kind != T_SPACE)
prev_nonspace_was_directive = 0;
if(count == capacity) {
int new_capacity;
if(capacity == 0)
@@ -367,8 +461,8 @@ static void print_escaped(buff_t *buff, const char *str, long len)
switch(str[j]) {
case '<': buff_printf(buff, "&lt;"); break;
case '>': buff_printf(buff, "&gt;"); break;
case '\n': buff_printf(buff, "<br />"); break;
case '\t': buff_printf(&buff, "&emsp;&emsp;&emsp;&emsp;"); break;
case '\n': buff_printf(buff, "<br />\n"); break;
case '\t': buff_printf(buff, "&emsp;&emsp;&emsp;&emsp;"); break;
case ' ':
if(j == 0 || str[j-1] != ' ')
@@ -504,6 +598,12 @@ char *c2html(const char *str, long len, _Bool table_mode, const char *class_pref
buff_printf(&buff, "</span>");
break;
case T_DIRECTIVE:
buff_printf(&buff, "<span class=\"%sdirective\">", class_prefix);
print_escaped(&buff, str + tokens[i].off, tokens[i].len);
buff_printf(&buff, "</span>");
break;
default:
buff_printf(&buff, "%c", str[tokens[i].off]);
break;
+1
View File
@@ -61,6 +61,7 @@
color: hsl(114, 31%, 68%);
}
.c2h-directive,
.c2h-kword {
color: hsl(300, 30%, 68%);
}