|
|
@@ -55,7 +55,6 @@ about our character and string matching and 'syntax_select' to process our text |
|
|
|
*/ |
|
|
|
|
|
|
|
static int syntax_count = 0; // Number of previously defined syntax rules. |
|
|
|
static int syntax_active = FALSE; // Syntax "library" or subprogram was initialized if this value is TRUE. |
|
|
|
static int * syntax_enrange = NULL; // Syntax rule can start with any character from 'syntax_begin' if this value is TRUE. |
|
|
|
static int * syntax_derange = NULL; // Syntax rule can start with any character from 'syntax_end' if this value is TRUE. |
|
|
|
static char * * syntax_begin = NULL; // Strings containing valid character (sub)sequence for begining the scan. |
|
|
@@ -76,10 +75,10 @@ when 'syntax_active' is FALSE, we'll change it to true, so 'atexit' won't be exe |
|
|
|
we're just deallocating (freeing) the memory, so we don't leak it and generate Valgrind warnings. |
|
|
|
*/ |
|
|
|
|
|
|
|
static void syntax_delete (void) { |
|
|
|
void syntax_delete (void) { |
|
|
|
int offset; |
|
|
|
|
|
|
|
if (syntax_active == FALSE) { // If 'syntax' subprogram wasn't active, we don't want to deallocate memory, we just return. |
|
|
|
if (syntax_count == 0) { // If syntax "library" wasn't used, we don't want to deallocate memory, we just return. |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
@@ -103,8 +102,7 @@ static void syntax_delete (void) { |
|
|
|
syntax_colour = deallocate (syntax_colour); |
|
|
|
syntax_effect = deallocate (syntax_effect); |
|
|
|
|
|
|
|
syntax_active = FALSE; // Lastly, I like to do this, but you don't have to. We'll use it in chapter five tho. |
|
|
|
syntax_count = 0; |
|
|
|
syntax_count = 0; // Lastly, I like to do this, but you don't have to. We'll use it in later chapter tho. |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
@@ -112,22 +110,20 @@ In 'syntax_define' function we're reallocating (enlarging) memory, effectively a |
|
|
|
be used with 'syntax_select' function to make our syntax highlighting. Lets explain what those function arguments do: |
|
|
|
|
|
|
|
@C |
|
|
|
static int syntax_define (int enrange, // Strict matching of string 'begin' in buffer range if FALSE, any character matching if TRUE. |
|
|
|
int derange, // Strict matching of string 'end' in buffer range if FALSE, and again, any character matching if TRUE. |
|
|
|
char * begin, // String of array of characters to begin matching. |
|
|
|
char * end, // String of array of characters to end matching, I don't know why I explain these... |
|
|
|
char escape, // Escape character, useful for C preprocessor. |
|
|
|
int colour, // Colour. |
|
|
|
int effect); // Effect, I hate explaining the code when the identifiers are descriptive. |
|
|
|
int syntax_define (int enrange, // Strict matching of string 'begin' in buffer range if FALSE, any character matching if TRUE. |
|
|
|
int derange, // Strict matching of string 'end' in buffer range if FALSE, and again, any character matching if TRUE. |
|
|
|
char * begin, // String of array of characters to begin matching. |
|
|
|
char * end, // String of array of characters to end matching, I don't know why I explain these... |
|
|
|
char escape, // Escape character, useful for C preprocessor. |
|
|
|
int colour, // Colour. |
|
|
|
int effect); // Effect, I hate explaining the code when the identifiers are descriptive. |
|
|
|
@ |
|
|
|
*/ |
|
|
|
|
|
|
|
static int syntax_define (int enrange, int derange, char * begin, char * end, char escape, int colour, int effect) { |
|
|
|
if (syntax_active == FALSE) { // If our syntax data isn't active, we'll execute this once. |
|
|
|
syntax_active = TRUE; // Now we set it to active state, and: |
|
|
|
|
|
|
|
int syntax_define (int enrange, int derange, char * begin, char * end, char escape, int colour, int effect) { |
|
|
|
if (syntax_count == 0) { // If our syntax isn't active, we'll execute this only once. |
|
|
|
atexit (syntax_delete); // Mark this function to be executed at program exit point. |
|
|
|
} |
|
|
|
} // It's same if we use more 'syntax_highlight_*' functions. |
|
|
|
|
|
|
|
fatal_failure (begin == NULL, "syntax_define: Begin string is null pointer."); // I don't like checking for errors, but here, voila. |
|
|
|
fatal_failure (end == NULL, "syntax_define: End string is null pointer."); |
|
|
@@ -161,12 +157,15 @@ static int syntax_define (int enrange, int derange, char * begin, char * end, ch |
|
|
|
This is more complex, but if you use your eyes to look, your brain to comprehend and your heart to love, I'm sure that you'll understand it. |
|
|
|
*/ |
|
|
|
|
|
|
|
static int syntax_select (char * string, int * length) { |
|
|
|
int syntax_select (char * string, int * length) { |
|
|
|
int offset, select; |
|
|
|
|
|
|
|
fatal_failure (syntax_active == FALSE, "syntax_select: Syntax is not active."); // Don't select without rules, abort! |
|
|
|
fatal_failure (string == NULL, "syntax_select: String is null."); |
|
|
|
fatal_failure (length == NULL, "syntax_select: Length is null."); |
|
|
|
if (syntax_count == 0) { // Don't select without rules, return! |
|
|
|
return (0); |
|
|
|
} |
|
|
|
|
|
|
|
fatal_failure (string == NULL, "syntax_select: String is null."); |
|
|
|
fatal_failure (length == NULL, "syntax_select: Length is null."); |
|
|
|
|
|
|
|
// In this first part of the function, we need to check if our syntax rule has been detected at the string offset we've provided. We're looping defined syntax rules and |
|
|
|
// choosing whether to compare any of the characters, or full string, depending on 'syntax_enrange' value which is essentially boolean, true or false, which I express with |
|
|
@@ -192,7 +191,7 @@ static int syntax_select (char * string, int * length) { |
|
|
|
} // And now we have our 'select' value. |
|
|
|
|
|
|
|
// If there was no syntax rule detected, we need to return from a function, and increment the offset by setting variable 'length' to 1. If we don't increment it, at the |
|
|
|
// first unrecognized character, our second nested-loop inside function 'preview_c_file' would use uninitialized or zero value, depending on how we structured our code |
|
|
|
// first unrecognized character, our second nested-loop inside function 'syntax_render_file' would use uninitialized or zero value, depending on how we structured our code |
|
|
|
// before that. We also return 'syntax_count' as the syntax rule index, which is invalid, and would produce Valgrind warning if we didn't handle it. In my unimportant |
|
|
|
// opinion, this if statement is the ugliest part of the function. |
|
|
|
if (select >= syntax_count) { |
|
|
@@ -233,13 +232,16 @@ static int syntax_select (char * string, int * length) { |
|
|
|
/* |
|
|
|
Imagine my shock, we can now print coloured text, without regular expressions. Nothing much, we can print it without using 'curses_*' functions, but if we want to preview large, |
|
|
|
well more than 24 line of code, we'd want to scroll it or modify it if we're making a text editor, hence, using curses is good. Lets see how our "mini-main" subprogram-like |
|
|
|
function does its' work, and how we use 'syntax_*' functions in them. |
|
|
|
function does its' work, and how we use 'syntax_*' functions in them, and I also want to make few syntax highlighting abstractions. We can call multiple 'syntax_highlight_*' |
|
|
|
functions, but it would mix the highlighting of those languages in that case, so we use 'syntax_delete' to reset it. |
|
|
|
|
|
|
|
Before we begin (Ada pun intended, remove this in final version), I won't (re)align 'separators' and 'keywords', because they fuck-up my comments, which I never write in my |
|
|
|
"official" programs. I write comments only here, to explain stuff in more details. Have fun... Oh, and type of variable 'keywords' an array of string pointers of automatic length, |
|
|
|
which we get with "sizeof (keywords) / sizeof (keywords [0])" part, for those keywords, it would be 32UL, and we cast it to integer. I use "long" comments outside of functions, |
|
|
|
and "short" comments inside them, while aligning them to the longest line of code, or current indentation level. |
|
|
|
*/ |
|
|
|
|
|
|
|
void preview_c_file (char * text_file, int x, int y) { |
|
|
|
// Before we begin (Ada pun intended, remove this in final version), I won't (re)align 'separators' and 'keywords', because they fuck-up my comments, which I never write |
|
|
|
// in my "official" programs. I write comments only here, to explain stuff in more details. Have fun... Oh, and type of variable 'keywords' an array of string pointers of |
|
|
|
// automatic length, which we get with "sizeof (keywords) / sizeof (keywords [0])" part, for those keywords, it would be 32UL, and we cast it to integer. |
|
|
|
void syntax_highlight_c (void) { |
|
|
|
char * separators = ".,:;<=>+*-/%!&~^?|()[]{}'\" \t\r\n"; |
|
|
|
|
|
|
|
char * keywords [] = { |
|
|
@@ -249,17 +251,17 @@ void preview_c_file (char * text_file, int x, int y) { |
|
|
|
"char", "short", "int", "long", "signed", "unsigned", "float", "double" |
|
|
|
}; |
|
|
|
|
|
|
|
char * text_data; |
|
|
|
|
|
|
|
int word, reset_x, reset_y; |
|
|
|
int word; |
|
|
|
|
|
|
|
curses_configure (); |
|
|
|
if (syntax_count != 0) { |
|
|
|
syntax_delete (); |
|
|
|
} |
|
|
|
|
|
|
|
syntax_define (FALSE, FALSE, "/*", "*/", '\0', COLOUR_GREY, EFFECT_BOLD); // Below, we're simply using our 'syntax_define' function. |
|
|
|
syntax_define (FALSE, FALSE, "//", "\n", '\0', COLOUR_GREY, EFFECT_BOLD); |
|
|
|
syntax_define (FALSE, FALSE, "#", "\n", '\\', COLOUR_YELLOW, EFFECT_ITALIC); |
|
|
|
syntax_define (FALSE, FALSE, "'", "'", '\\', COLOUR_PINK, EFFECT_BOLD); |
|
|
|
syntax_define (FALSE, FALSE, "\"", "\"", '\\', COLOUR_PINK, EFFECT_NORMAL); |
|
|
|
syntax_define (FALSE, FALSE, "/*", "*/", '\0', COLOUR_GREY, EFFECT_BOLD); // Below, we're simply using our 'syntax_define' function. |
|
|
|
syntax_define (FALSE, FALSE, "//", "\n", '\0', COLOUR_GREY, EFFECT_BOLD); |
|
|
|
syntax_define (FALSE, FALSE, "#", "\n", '\\', COLOUR_YELLOW, EFFECT_ITALIC); |
|
|
|
syntax_define (FALSE, FALSE, "'", "'", '\\', COLOUR_PINK, EFFECT_BOLD); |
|
|
|
syntax_define (FALSE, FALSE, "\"", "\"", '\\', COLOUR_PINK, EFFECT_NORMAL); |
|
|
|
|
|
|
|
for (word = 0; word != (int) (sizeof (keywords) / sizeof (keywords [0])); ++word) { |
|
|
|
syntax_define (FALSE, TRUE, keywords [word], separators, '\0', COLOUR_YELLOW, EFFECT_BOLD); |
|
|
@@ -272,6 +274,66 @@ void preview_c_file (char * text_file, int x, int y) { |
|
|
|
syntax_define (TRUE, TRUE, "abcdefghijklmnopqrstuvwxyz", separators, '\0', COLOUR_WHITE, EFFECT_NORMAL); |
|
|
|
syntax_define (TRUE, TRUE, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", separators, '\0', COLOUR_WHITE, EFFECT_BOLD); |
|
|
|
syntax_define (TRUE, TRUE, "_", separators, '\0', COLOUR_WHITE, EFFECT_ITALIC); |
|
|
|
} |
|
|
|
|
|
|
|
void syntax_highlight_ada (void) { |
|
|
|
char * separators = ".,:;<=>#+*-/&|()'\" \t\r\n"; |
|
|
|
|
|
|
|
char * keywords [] = { |
|
|
|
"abort", "else", "new", "return", "abs", "elsif", "not", "reverse", |
|
|
|
"abstract", "end", "null", "accept", "entry", "select", "access", "of", |
|
|
|
"separate", "aliased", "exit", "or", "some", "all", "others", "subtype", |
|
|
|
"and", "for", "out", "array", "function", "at", "tagged", "generic", |
|
|
|
"package", "task", "begin", "goto", "pragma", "body", "private", "then", |
|
|
|
"type", "case", "in", "constant", "until", "is", "raise", "use", |
|
|
|
"if", "declare", "range", "delay", "limited", "record", "when", "delta", |
|
|
|
"loop", "rem", "while", "digits", "renames", "with", "do", "mod", |
|
|
|
"requeue", "xor", "procedure", "protected", "interface", "synchronized", "exception", "overriding", |
|
|
|
"terminate" |
|
|
|
}; |
|
|
|
|
|
|
|
int word; |
|
|
|
|
|
|
|
if (syntax_count != 0) { |
|
|
|
syntax_delete (); |
|
|
|
} |
|
|
|
|
|
|
|
syntax_define (FALSE, FALSE, "--", "\n", '\0', COLOUR_GREY, EFFECT_BOLD); |
|
|
|
syntax_define (FALSE, FALSE, "'", "'", '\\', COLOUR_PINK, EFFECT_BOLD); |
|
|
|
syntax_define (FALSE, FALSE, "\"", "\"", '\\', COLOUR_PINK, EFFECT_NORMAL); |
|
|
|
|
|
|
|
for (word = 0; word != (int) (sizeof (keywords) / sizeof (keywords [0])); ++word) { |
|
|
|
syntax_define (FALSE, TRUE, keywords [word], separators, '\0', COLOUR_YELLOW, EFFECT_BOLD); |
|
|
|
} |
|
|
|
|
|
|
|
syntax_define (TRUE, FALSE, "()#", "", '\0', COLOUR_BLUE, EFFECT_NORMAL); |
|
|
|
syntax_define (TRUE, FALSE, ".,:;<=>+*-/&|'", "", '\0', COLOUR_CYAN, EFFECT_NORMAL); |
|
|
|
|
|
|
|
syntax_define (TRUE, TRUE, "0123456789", separators, '\0', COLOUR_PINK, EFFECT_BOLD); |
|
|
|
syntax_define (TRUE, TRUE, "abcdefghijklmnopqrstuvwxyz", separators, '\0', COLOUR_WHITE, EFFECT_NORMAL); |
|
|
|
syntax_define (TRUE, TRUE, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", separators, '\0', COLOUR_WHITE, EFFECT_BOLD); |
|
|
|
syntax_define (TRUE, TRUE, "_", separators, '\0', COLOUR_WHITE, EFFECT_ITALIC); |
|
|
|
} |
|
|
|
|
|
|
|
void syntax_render_file (char * text_file, int x, int y) { |
|
|
|
char * text_data; // This local variable will hold our data. |
|
|
|
|
|
|
|
int reset_x, reset_y; // Since we're using curses, we want to reset the offset. |
|
|
|
|
|
|
|
curses_configure (); // Curses configuration, aka printing ugly text. |
|
|
|
|
|
|
|
switch (file_type (text_file)) { // Depending on our file extension, we select highlighting. |
|
|
|
case FILE_TYPE_C_SOURCE: |
|
|
|
case FILE_TYPE_C_HEADER: |
|
|
|
syntax_highlight_c (); |
|
|
|
break; |
|
|
|
case FILE_TYPE_ADA_BODY: |
|
|
|
case FILE_TYPE_ADA_SPECIFICATION: |
|
|
|
syntax_highlight_ada (); |
|
|
|
break; |
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
text_data = file_record (text_file); // And, imagine, importing our file data into a buffer! |
|
|
|
|
|
|
@@ -285,10 +347,10 @@ void preview_c_file (char * text_file, int x, int y) { |
|
|
|
|
|
|
|
x = reset_x; |
|
|
|
y = reset_y; |
|
|
|
select = syntax_count; |
|
|
|
select = syntax_count; // I intentionally set this to an invalid value. |
|
|
|
length = 0; |
|
|
|
|
|
|
|
for (offset = 0; offset < string_length (text_data); offset += length) { // And it's time to start rendering our C file. |
|
|
|
for (offset = 0; offset < string_length (text_data); offset += length) { // And it's time to start rendering our file. |
|
|
|
int suboffset, colour, effect; |
|
|
|
|
|
|
|
select = syntax_select (& text_data [offset], & length); // Here we're evaluating variables 'select' and 'length'. |
|
|
|