#ifndef CHAPTER_4_HEADER #define CHAPTER_4_HEADER #include "chapter_0.h" #include "chapter_1.h" #include "chapter_2.h" #include "chapter_3.h" /* In this chapter, you'll learn about: - Programming languages - Syntax highlighting - Importance of readability - Differences between languages - More on memory management - Using curses I believe that this chapter should be a breakpoint for you to write a simple C program. So far, we've learned in: - chapter 0: To format our code properly in order to increase readability and we've implemented some core functions for memory management, strings and input / output. - chapter 1: To declare and define functions, and we've covered character and file descriptor related functions, as well as ASCII table and discussed C keywords. - chapter 2: To use external variables, function pointers and minor part of 'libncurses' reimplementation that doesn't care about portability. - chapter 3: To use standard library 'printf' function, and to implement variadic argument functions, while also covering switch statement in more depth. From this moment onwards, some chapters will have few functions called 'program_*', which we can use to forge even larger programs. They'll each have their own dependencies, for example, some of them will require functions from some or all previous chapter source and header files, but I'll make sure not to use in them functions that'll be in future chapters. Instead of that, we'll (re)implement newer stuff with different approach if necessary. That way, you can be sure that if you're reading chapter four, for example, it'll only use functions and variables defined in chapters zero to three. Lets begin. I'll write this huge 'program_curses_view_file' function in somewhat procedural style of programming, so to say, and in the next chapter, we'll use more modular way, using many more functions. Learning anything, including the C programming language, is like a journey. Maybe you think it won't last long, and it ends up being quite long journey, or maybe you think it'll be very long, that you'll walk miles and miles, and it ends up being short (you rage-quit). The final destination you're going towards always depends on where you left-off and where you're coming from. For example, if you wrote Ada, you'll like chapter four, if you wrote C++, you'll like chapter five. I'll also list a few "traps" right here, where most programmers get caught in: - My program needs to be cross-platform, fully portable, to run on Windblows, Machos, Leenoocks, Raspberries and on Commodore 64. - My program needs to be huge, multiple files and folders, everything is abstracted out, even the wrappers for some library. - My program doesn't need to free used memory, my operating system will do it for me, I don't care about memory leaks, only nerds do. - My compiler warns about stupid things, I don't want to fix all compiler warnings, it'll make the code look bad. First of all, there are a lot of standards, people who don't have more important work to do make those. There are a lot of CPU architectures, x86-64 being used a lot, then ISA (instruction set architecture) such as CISC, RISC, MISC, OISC, and even more things that should't matter for you like SIMD, AVX, ST, MMX, XMM, YMM, ZMM, et fucking cetera. Then, we have many many GPU hardware, they each have some part of their own ISA, writing direct code for one GPU won't work on other GPUs, so we need to use OpenGL, Vulkan or Direct3D. Do you see where this is going, adding complexity on top of complexity, abstracting the abstractions, due to standardization. Then again, we have many programming languages, some of them have multiple standards, like C, C++, Ada, Fortran, basically, popular programming languages. For some of those languages, there are multiple compilers, and sometimes they support language extensions that aren't the part of the core language. That's why nothing is truly portable. If every company make their own standard, thinking they're smartest, there's no standardization. Just look at the mirror, at your PC, laptop, whatever, then take a look outside the window, and say out loud "My program will be written in C, it will run on 64-bit CPUs, it will depend only on Vulkan API, it will use XCB for display.". Take a deep breath, you're not writing some part of the program for the company, you're having fun, you're sane. Then again, pray to Khronos, your OS maintainers or developers and your GPU vendor that your GPU supports Vulkan, that someone there, out in the big white world wrote a driver for it. Keep in mind that I don't work for any programming related company and I want to, also I don't have college, I learned C by writing it a lot and reading it from time to time. Now, hear me out, if 1000 people with some CS degree wrote a simple C program, all of those would look very similar. That's because they've been programmed into that line of thinking, which is dangerous in my opinion for one reason: They think they're always right. I learned a lot from talking with smart people, some of them have CS degree, some not, so I don't own what I know, no one owns anyones' knowledge, but they weren't always right. So, if you don't have a CS degree, you can learn C easier, that's my point. C is good programming language to learn first because it shows you what can you create with very simple tools. Now, lets get to syntax highlighting: I'll say it as many times as I need, readability is the key to good software. If your code is readable, yet simple, without structure-hell or function-hell, but you don't have the knowledge to optimize it currently, if you share it under some permissive license and show it to other people who (probably) know more about optimizing the program than you, they might have less trouble "fixing" it. Again, this depends on more factors, how many global variables you have, because without them compiler will (maybe / probably) be able to optimize it for you. But the source code being readable is a good start. However, no matter how readable and nicely formatted it is, it would be harder to read if everything is printed in white text on black background (like it's with ed text editor). When you open your text editor of choice, as soon as you see certain colour, you know it's a keyword, not an identifier, you notice comments and probably ignore them, it's a good thing, right? So, there are more than one ways to parse a text file and determine what's a keyword, a number, a string, etc. I'm using simple approach from my older program, which was made to replace me constantly using '$ cat text_file.c' in terminal. Imagine this: This is an array of characters, in C type 'char', and only the last one is '\0', aka CHARACTER_NULL in our enumeration. [.................................................................................................................................................................................] This is how we parse it, determine where some syntax rule begins, where it ends, how long it is, without changing that array of characters, aka string. [.................................................................................................................................................................................] ^ - at the start, we're pointing to first character, whose index is 0. ^^^ - then we try to match (for example) first 3 characters, if there's a syntax rule like that, with strict comparison. ^ - if we matched it, then we need to check if next character, 4th one (with index 3), an ending character or string. ^ - if it is, we return index of that rule (with function 'syntax_select') and length is 3, without changing the offset. And we repeat that process until we reach the end of the string, that null termination, it's not a fast algorithm, but it's simple. Easy. */ extern int syntax_define (int enrange, int derange, char * begin, char * end, char escape, int colour, int effect); // This must be called before 'syntax_select' function. extern int syntax_select (char * string, int * length); // And we're not dealing with null-terminated strings here. // Internally use 'syntax_define' to make C (and Ada below) syntax highlighting, it will use global variables internal to 'chapter_4.c' file. // Worth noting: // - We'll only have one "predefined syntax highlighting" per file type, because we don't want them to clash with each other. // - We could use several syntax highlighting rules if we made one more level of arrays for all those global variables, and a rule to switch between them. // - If you want to add support for some other language, you can try to make, for example 'syntax_highlight_python', it'll be a good exercise. extern void syntax_highlight_c (void); extern void syntax_highlight_ada (void); extern void program_curses_view_file (char * text_file, int x, int y); // This is our subprogram that'll view some textual file in terminal, using our curses "library". #endif