4 ms·
Simple Tiny Compiler in C
- vajrabum 7y agoThe structure of that program looks kinda weird with .h files but no matching .c files. Instead all the code is in the .h files.
- saagarjha 7y agoSince there's only one object file and header guards, this ends up working out.
- zakirullin 7y agoIndeed there's only one object file with header-guards. It's not intended to be used as a library or something. Moreover - the codebase is really tiny. That being said - it made with simplicity in mind
- RealityVoid 7y agoSure, but... wouldn't it have been simpler to have the functions in the .c file? This is just a odd way of doing things. There are good reasons you don't usually see this, add one more .c file and things get awkward real fast. Sure, it works in this particular instance, but it just makes me feel itchy. Maybe it's past trauma where I saw real weird codebases with static functions in .h files used across multiple .c files. It was just horrible to work with and reason about.
- zakirullin 7y agoWell if there are already two remarks on that matter - I'll fix the issue! I know the profits, yeah. Things haven't exactly worked out the way I thought they would - there should be a border between simplicity/bad design. Hahah, yeah it smells, can't argue with that. I believe that everyone had a trauma like that in the past :)
- sjamaan 7y agoMaybe it would make more sense if you used only .c files; people typically expect only prototypes, type definitions, macros and so on in .h files. Including one .c file in another is a bit odd, but less surprising. But then having include guards makes the entire thing a bit weird :)
- zakirullin 7y agoHaha. Yup, I know that, of course. And I have never used that practice in real world projects. Well, I'll put both .c/.h files, that doesn't add that much overhead.
- DecoPerson 7y agoA is something I want to use in multiple files, like a method declaration, a type, a const or a preprocessor define. B is a place that I want to use it, and it's tightly related to A. Unfortunately, B belongs in a .c file. But to use A in multiple places, it belongs in a .h file. Crap, now these two very related things that I wanted to keep together lexically must now be split into separate files. In this case, the author is making a super-simple example project. I'm happy they chose to forgo the standard C header/source file dance. It made it a lot easier to read and understand. But yeah, the dance is required for "real" projects, as you've described.
- RealityVoid 7y agoThe way I view it is the following: - Put everything in the C file. - If you need a function visible outside, put it's _prototype_ in the .h file. - If you want to have a variable visible outside, also make a extern declaration inside the .h file. - If you want to have anything else visible to the outside, just move it to the .h file. The reason I say that putting functions inside a .h file is a pain is because of the way the preprocessor works. It takes the contents of the included file and pastes it at the place of the include. So doing a function inside a .h file and using it inside multiple .c files will mean you have the same function multiple times and it will throw a linker error if the function is not static. At link time the linker will be confused because it will not know about witch of the functions you're talking about when you call it. So then you declare it static, but then you _actually_ have 2x functions inside your executable, that do the same thing. This is even worse with variables declared static inside .h files since here you will think you're changing your variable, but you actually have separate variables inside each compiled file. Of course, my observation is also based on taste. I like when I have everything inside one file, instead of spread over multiple files( of course, as long as this is reasonable). This program here might have had one single .c file and it would have been just file. I actually would have considered it easier to read, but as I said, it's a matter of taste at this point.
- krychu 7y agoDuplicating some code in the executable of this size might not be that much of a deal. I wonder if there are examples where such practice resulted in improved performance due to locality of the code. Of course you could also toy with inlines.
- billpg 7y agoCompiling the entire project as a single (effective) file permits the compiler to optimize in ways it couldn't with separate files.
- saagarjha 7y agoThat would depend on how you've set up your compiler.
- feintruled 7y agoNice job. Read the first couple of entries, look forward to following through the rest. I enjoy this kind of exercise, just complex enough to give you a flavour of how the 'real' thing works whilst not being a mere toy example.
- zakirullin 7y agoLiked your description, thanks!
- krychu 7y agoGreat exercise indeed. Why the choice of LL over LR parsing? I thought the latter one was a more common choice for implementing languages?
- zakirullin 7y agoThank you so much! Without a doubt LR parsers prevail on the market. They tend to be more powerful & complex. Whereas LL parsers are easier to implement & understand. That said, LL parsers are ideal for getting a bit of a taste of complicated compiler-related-stuff-world, in my opinion.
- userbinator 7y agoRecursive-descent which is LL is very common for parsing because of its simplicity; LR is theoretically more powerful, but in practice there doesn't seem to have been any great need for it. GCC, Clang/LLVM, MSVC, javac, csc (the C# one), and a whole bunch of other compilers use RD (often with some sort of precedence-climbing, which is essentially a refactoring of RD). GCC was originally using a generated LR parser (Bison) but moved to RD a long time ago.