From 2f4bc2921750b2cd2f068626cad486af5d07621f Mon Sep 17 00:00:00 2001 From: xolatile Date: Sun, 19 Nov 2023 10:42:14 -0500 Subject: [PATCH] Finished chapter 4, I will hunt grammar mistakes later... --- chapter/chapter_0.c | 1 + chapter/chapter_4.c | 128 ++++++++++++++++++++++++++++------------------------ 2 files changed, 71 insertions(+), 58 deletions(-) diff --git a/chapter/chapter_0.c b/chapter/chapter_0.c index 3358df9..3d6eac2 100644 --- a/chapter/chapter_0.c +++ b/chapter/chapter_0.c @@ -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... } } + if (* string_0 != * string_1) { // Now, we'll do one last termination check. return (FALSE); } diff --git a/chapter/chapter_4.c b/chapter/chapter_4.c index 5c2305a..be0e1fd 100644 --- a/chapter/chapter_4.c +++ b/chapter/chapter_4.c @@ -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) { - 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 (length == NULL, "syntax_select: Length is null."); - for (select = offset = 0; select != syntax_count; ++select) { - if (syntax_enrange [select] == FALSE) { - if (string_compare_limit (string, syntax_begin [select], string_length (syntax_begin [select])) == TRUE) { - break; // We need to limit our string comparisson function. - } else { - continue; - } - } else { - 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; - } + // 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 + // '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 + // it's best to modify only variable 'length', hence we check with 'string_compare_limit' function. + for (select = offset = 0; select != syntax_count; ++select) { // We're looping defined syntax rules: + 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. + break; // If strings are same, we exit the loop. } + } 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. } - } - - selected: + } // 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 + // 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) { * length = 1; - return (syntax_count); } - for (offset = 1; string [offset - 1] != '\0'; ++offset) { - if (string [offset] == syntax_escape [select]) { + // 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 + // 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; continue; } - if (syntax_derange [select] == FALSE) { - if (string_compare_limit (& string [offset], syntax_end [select], string_length (syntax_end [select])) == TRUE) { - * length = offset + string_length (syntax_end [select]); - return (select); + 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) { // Again, we're comparing full string. + * length = offset + string_length (syntax_end [select]); // We found it, yaay! + break; } } else { - subset = 0; - if (string_compare (syntax_end [select], "") == TRUE) { + if (string_compare (syntax_end [select], "") == TRUE) { // And here's our empty string exception. break; - } do { - if (string [offset] == syntax_end [select] [subset]) { - * length = offset; - goto finished; - } - } while (++subset != string_length (syntax_end [select])); - } - } + } + if (character_compare_array (string [offset], syntax_end [select]) == TRUE) { + * length = offset; + break; + } + } // These two loops look similar, but no! + } // And now we have our 'length' value. - finished: - - return (select); + return (select); // Lastly, return syntax rule index. } +/* +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. +*/ + 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 * keywords [] = { @@ -240,7 +248,7 @@ void preview_c_file (char * text_file, int width, int height, int x, int y) { (void) width; (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", '\\', COLOUR_YELLOW, EFFECT_ITALIC); 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, "_", 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_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; - 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; y = reset_y; select = syntax_count; 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; - 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; effect = EFFECT_NORMAL; } else { @@ -286,25 +300,23 @@ void preview_c_file (char * text_file, int width, int height, int x, int y) { effect = syntax_effect [select]; } - for (suboffset = 0; suboffset < length; ++suboffset) { - if (text_data [offset + suboffset] == CHARACTER_LINE_FEED) { - x = reset_x; - y += 1; - } else if (text_data [offset + suboffset] == CHARACTER_TAB_HORIZONTAL) { - x += 8; + for (suboffset = 0; suboffset < length; ++suboffset) { // Sadly, we need to render them one by one character. + if (text_data [offset + suboffset] == CHARACTER_LINE_FEED) { // Rendering of blank characters isn't counted, so: + x = reset_x; // If there's a new line, we need to reset 'x' value. + y += 1; // And increment 'y' value. + } else if (text_data [offset + suboffset] == CHARACTER_TAB_HORIZONTAL) { // If there's a tab, we offset 'x' value by normal count. + x += 8; // Normal indentation is 8-characters wide. } 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; } } - - // 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