Show HN: Learn C and its lower levels interactively, in the browser
I made a simple virtual machine that runs C in the browser.This project is made as an experiment to see if C can be learned easier if the lower level is covered in paralel.
Sandbox: https://vasyop.github.io/miniC-hosting
Tutorial part 1: https://vasyop.github.io/miniC-hosting/?0
More info: https://github.com/vasyop/miniC-hosting/blob/master/README.md
Please support this project: https://github.com/vasyop/miniC-hosting/blob/master/support.md
36 comments
[ 2.2 ms ] story [ 81.8 ms ] threadThis project is amazing!
My hunch is that academia is already sniffing this and musing to adopt into their courses.
The thing is that you have two levels of learning here: the compilation step and how the machine works. It could be better to start with how the machine works and eventually how to write programs for it..
Keep up with the good work!
Thank you thank you thank you
Reminds me some of Compiler Explorer: https://godbolt.org/
First, do this:
$ cp /site/root/system/wasms/games/binjgb.wasm .
The system is built around the concept of command libraries. You are going to need to import the wasm library like this:
$ import wasm
This loads a fairly powerful command called parsewasm. To get an informational printout of the toplevel sections in that file, run the command:
$ parsewasm --toplevel binjgb.wasm
There are lots of other subcommands in parsewasm, but the coolest part is that you can list out the names of exported functions and then dump out the code bodies of whatever functions you want to inspect.
I couldn't do a simple #define. This is pretty fundamental to typical C programming. One would also expect stringification, token pasting, and variadic macros.
I added a goto, and it won't compile. Yes, this is a valid and useful part of C, and must be learned by any C programmer. If you doubt this, count occurrences of goto in the Linux kernel.
I also couldn't do modern array initializers. I'll guess the same is true of modern struct initializers. These are important for producing maintainable code.
There seems to be no support for the restrict keyword. This keyword makes a dramatic performance difference.
But more importantly, every MVP is going to lack certain features that some folks deem essential. But when we focus on missing features, or worse, miss the point of a project ("you built X but what I really want is Y"), we discourage them or others from trying/sharing future experiments.
The lack of important standard features means I can't just say "this is C".
For teaching, normal data type sizes are important. Being able to have static data is important. Beginners need to see how these things work.
I do applaud the effort. I think it is a great idea.
If this was a good idea, Kernighan and Ritchie would have filled the pages of that book with PDP-11 instruction sequences accompanying bits of C code.
Hyperbole; it's applicable in specific circumstances.
There are ways to code without restrict to get the same performance.
Sample code:
Compiling with GCC, the first example generates 8 memory operations; both of the other two get it down to 7. Both the use of restrict in B and the technique in C have cut down the wasteful memory access. That access is done due to the suspicion that the object was changed by a prior operation due to overlap.Function C works by caching the pred->next value in a local variable and referring to that.
None of the assignments through the structure type can possibly affect the value of aft; the structures cannot overlap with the local variable. (This is an implicit non-overlap restriction similar to what restrict expresses for the two arguments.)
Once we establish aft, all of the pointers involved in the function are local variables; so none of the local->memb = val assignments raise any suspicion that the value of local has been overwritten, requiring it to be reloaded from memory. We code five accesses and got five (plus the two to load the arguments from the stack, making seven).
Function B has the disadvantage that the behavior becomes undefined if pred and succ are pointers to the same node. Function C has no such problem. Even though the code is just as good as for B, the behavior is defined for overlapping pred and succ.
restrict is C trying to keep up with Fortran. What are situations when we can't use this type of load-store coding to reduce memory operations? Why, array processing!
Well, of course we can take the same approach in array processing; but the problem is that array processing is automatically unrolled by the compiler. Unrolling is hampered in some situations when we don't know whether the arrays overlap. If we simply introduce local variables into the loop, it still won't be un...