Finished chapter 4, I will hunt grammar mistakes later...

This commit is contained in:
Ognjen Milan Robovic 2023-11-19 10:42:14 -05:00
parent 715063ebc6
commit 2f4bc29217
2 changed files with 71 additions and 58 deletions

View File

@ -181,6 +181,7 @@ int string_compare (char * string_0, char * string_1) {
return (FALSE); // > We return FALSE, 0, since strings aren't the same... return (FALSE); // > We return FALSE, 0, since strings aren't the same...
} }
} }
if (* string_0 != * string_1) { // Now, we'll do one last termination check. if (* string_0 != * string_1) { // Now, we'll do one last termination check.
return (FALSE); return (FALSE);
} }

View File

@ -158,72 +158,80 @@ static int syntax_define (int enrange, int derange, char * begin, char * end, ch
} }
/* /*
TODO: WE LEFT OF HERE... 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) { static int syntax_select (char * string, int * length) {
int offset, subset, select; int offset, select;
fatal_failure (syntax_active == FALSE, "syntax_select: Syntax is not active."); 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 (string == NULL, "syntax_select: String is null.");
fatal_failure (length == NULL, "syntax_select: Length is null."); fatal_failure (length == NULL, "syntax_select: Length is null.");
for (select = offset = 0; select != syntax_count; ++select) { // 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
if (syntax_enrange [select] == FALSE) { // 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
if (string_compare_limit (string, syntax_begin [select], string_length (syntax_begin [select])) == TRUE) { // 'int' type for "type-safety simplicity". Keep in mind that we're not returning or modifying the string we provided, so it won't be null-terminated, instead I think
break; // We need to limit our string comparisson function. // it's best to modify only variable 'length', hence we check with 'string_compare_limit' function.
} else { for (select = offset = 0; select != syntax_count; ++select) { // We're looping defined syntax rules:
continue; if (syntax_enrange [select] == FALSE) { // Choosing the comparisson:
} if (string_compare_limit (string, syntax_begin [select], string_length (syntax_begin [select])) == TRUE) { // Limiting our string comparisson.
} else { break; // If strings are same, we exit the loop.
for (subset = 0; subset != string_length (syntax_begin [select]); ++subset) {
if (string [offset] == syntax_begin [select] [subset]) {
goto selected; // We can't use 'break' here, because it will exit only one loop, not both of them.
} else {
continue;
}
}
} }
} else { // Else, we compare any character.
if (character_compare_array (string [offset], syntax_begin [select]) == TRUE) { // With our obviously named function...
break; // We found it, exit the loop!
} // If we didn't, just continue.
} }
} // And now we have our 'select' value.
selected: // 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
// 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) { if (select >= syntax_count) {
* length = 1; * length = 1;
return (syntax_count); return (syntax_count);
} }
for (offset = 1; string [offset - 1] != '\0'; ++offset) { // In this second part, we have our 'select' value, index of the syntax rule, and we want to know the 'length' value, by trying to match with 'syntax_end' string. We have
if (string [offset] == syntax_escape [select]) { // to again, separate two cases for matching any character or full string, except that we use it to determine its' match-length. Important difference is also that there's
// special case where we have escape character matching, and where 'syntax_end' string is empty (but not NULL), so in that case we match only one character. We could have
// nested loop there, and second loop would need goto statement to exit it, so we only use one loop.
for (offset = 1; string [offset - 1] != '\0'; ++offset) { // Now, offset must be 1, and we loop...
if (string [offset] == syntax_escape [select]) { // Here's our escape exception.
++offset; ++offset;
continue; continue;
} }
if (syntax_derange [select] == FALSE) { if (syntax_derange [select] == FALSE) { // Choosing what to compare, yet again...
if (string_compare_limit (& string [offset], syntax_end [select], string_length (syntax_end [select])) == TRUE) { if (string_compare_limit (& string [offset], syntax_end [select], string_length (syntax_end [select])) == TRUE) { // Again, we're comparing full string.
* length = offset + string_length (syntax_end [select]); * length = offset + string_length (syntax_end [select]); // We found it, yaay!
return (select); break;
} }
} else { } else {
subset = 0; if (string_compare (syntax_end [select], "") == TRUE) { // And here's our empty string exception.
if (string_compare (syntax_end [select], "") == TRUE) {
break; break;
} do { }
if (string [offset] == syntax_end [select] [subset]) { if (character_compare_array (string [offset], syntax_end [select]) == TRUE) {
* length = offset; * length = offset;
goto finished; break;
}
} while (++subset != string_length (syntax_end [select]));
} }
} // These two loops look similar, but no!
} // And now we have our 'length' value.
return (select); // Lastly, return syntax rule index.
} }
finished: /*
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,
return (select); 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.
*/
void preview_c_file (char * text_file, int width, int height, int x, int y) { void preview_c_file (char * text_file, int width, int height, 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.
char * separators = ".,:;<=>+*-/%!&~^?|()[]{}'\" \t\r\n"; char * separators = ".,:;<=>+*-/%!&~^?|()[]{}'\" \t\r\n";
char * keywords [] = { char * keywords [] = {
@ -240,7 +248,7 @@ void preview_c_file (char * text_file, int width, int height, int x, int y) {
(void) width; (void) width;
(void) height; (void) height;
syntax_define (FALSE, FALSE, "/*", "*/", '\0', COLOUR_GREY, EFFECT_BOLD); 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", '\0', COLOUR_GREY, EFFECT_BOLD);
syntax_define (FALSE, FALSE, "#", "\n", '\\', COLOUR_YELLOW, EFFECT_ITALIC); syntax_define (FALSE, FALSE, "#", "\n", '\\', COLOUR_YELLOW, EFFECT_ITALIC);
syntax_define (FALSE, FALSE, "'", "'", '\\', COLOUR_PINK, EFFECT_BOLD); syntax_define (FALSE, FALSE, "'", "'", '\\', COLOUR_PINK, EFFECT_BOLD);
@ -258,27 +266,33 @@ void preview_c_file (char * text_file, int width, int height, int x, int y) {
syntax_define (TRUE, TRUE, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", separators, '\0', COLOUR_WHITE, EFFECT_BOLD); syntax_define (TRUE, TRUE, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", separators, '\0', COLOUR_WHITE, EFFECT_BOLD);
syntax_define (TRUE, TRUE, "_", separators, '\0', COLOUR_WHITE, EFFECT_ITALIC); syntax_define (TRUE, TRUE, "_", separators, '\0', COLOUR_WHITE, EFFECT_ITALIC);
text_data = file_record (text_file); text_data = file_record (text_file); // And, imagine, importing our file data into a buffer!
reset_x = x; reset_x = x;
reset_y = y; reset_y = y;
for (curses_active = 1; curses_active != 0; ) { for (curses_active = 1; curses_active != 0; ) { // We enter our main subprogram loop.
int offset, select, length; int offset, select, length;
curses_render_background (' ', COLOUR_WHITE, EFFECT_NORMAL); curses_render_background (' ', COLOUR_WHITE, EFFECT_NORMAL); // We need to clear the screen buffer before rendering.
x = reset_x; x = reset_x;
y = reset_y; y = reset_y;
select = syntax_count; select = syntax_count;
length = 0; length = 0;
for (offset = 0; offset < string_length (text_data); offset += length) { for (offset = 0; offset < string_length (text_data); offset += length) { // And it's time to start rendering our C file.
int suboffset, colour, effect; int suboffset, colour, effect;
select = syntax_select (& text_data [offset], & length); select = syntax_select (& text_data [offset], & length); // Here we're evaluating variables 'select' and 'length'.
if (select >= syntax_count) { // We can do the same thing in 2 lines of code, but it's less readable in my opinion, I prefer longer verbose way below...
// colour = (select >= syntax_count) ? COLOUR_WHITE : syntax_colour [select];
// effect = (select >= syntax_count) ? EFFECT_NORMAL : syntax_effect [select];
// Or, if you find this more intuitive:
// colour = (select < syntax_count) ? syntax_colour [select] : COLOUR_WHITE;
// effect = (select < syntax_count) ? syntax_effect [select] : EFFECT_NORMAL;
if (select >= syntax_count) { // Here, we're handling error value of 'syntax_select'.
colour = COLOUR_WHITE; colour = COLOUR_WHITE;
effect = EFFECT_NORMAL; effect = EFFECT_NORMAL;
} else { } else {
@ -286,25 +300,23 @@ void preview_c_file (char * text_file, int width, int height, int x, int y) {
effect = syntax_effect [select]; effect = syntax_effect [select];
} }
for (suboffset = 0; suboffset < length; ++suboffset) { for (suboffset = 0; suboffset < length; ++suboffset) { // Sadly, we need to render them one by one character.
if (text_data [offset + suboffset] == CHARACTER_LINE_FEED) { if (text_data [offset + suboffset] == CHARACTER_LINE_FEED) { // Rendering of blank characters isn't counted, so:
x = reset_x; x = reset_x; // If there's a new line, we need to reset 'x' value.
y += 1; y += 1; // And increment 'y' value.
} else if (text_data [offset + suboffset] == CHARACTER_TAB_HORIZONTAL) { } else if (text_data [offset + suboffset] == CHARACTER_TAB_HORIZONTAL) { // If there's a tab, we offset 'x' value by normal count.
x += 8; x += 8; // Normal indentation is 8-characters wide.
} else { } else {
curses_render_character (text_data [offset + suboffset], colour, effect, x, y); curses_render_character (text_data [offset + suboffset], colour, effect, x, y); // Finally, we can render it character by character.
x += 1; x += 1;
} }
} }
// curses_render_string_limit (& text_data [offset], length, syntax_colour [select], syntax_effect [select], x, y);
} }
curses_synchronize (); curses_synchronize (); // Lastly, we synchronize our terminal.
} }
text_data = deallocate (text_data); text_data = deallocate (text_data); // And deallocate the memory when we exit the subprogram.
} }
#endif #endif