diff --git a/.gitignore b/.gitignore index 890e3b9..0b18635 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ c2html -example -*.html \ No newline at end of file +*.html +samples \ No newline at end of file diff --git a/c2html.c b/c2html.c index 6c2ff57..b8bd96e 100644 --- a/c2html.c +++ b/c2html.c @@ -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, "<"); break; case '>': buff_printf(buff, ">"); break; - case '\n': buff_printf(buff, "
"); break; - case '\t': buff_printf(&buff, "    "); break; + case '\n': buff_printf(buff, "
\n"); break; + case '\t': buff_printf(buff, "    "); 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, ""); break; + case T_DIRECTIVE: + buff_printf(&buff, "", class_prefix); + print_escaped(&buff, str + tokens[i].off, tokens[i].len); + buff_printf(&buff, ""); + break; + default: buff_printf(&buff, "%c", str[tokens[i].off]); break; diff --git a/style.css b/style.css index e9a4421..fa08ab0 100644 --- a/style.css +++ b/style.css @@ -61,6 +61,7 @@ color: hsl(114, 31%, 68%); } + .c2h-directive, .c2h-kword { color: hsl(300, 30%, 68%); }