No calls to free() to match calloc(), no overflow checking in gettoken() and then I read this:
> a program with missing or unmatched parenthesis, unresolved symbols, etc will likely just result in something like a segmentation fault.
I understand this is just a fun thing to hack on, but this is an irresponsible way to write software. I hope no one here is reading this and thinking it's how they should be writing C.
It's a lot longer if it's done properly. You should use an array of dotted pairs to store your list elements, out of which you build a free list, from which you allocate by calling cons. To garbage collect, you mark all objects in use (on the stack, or accessible from the symbol table) and then you add the unmarked conses to the free list. You should not be using malloc or calloc at all, certainly not to implement cons.
Below is some of the relevant code in C/C++ from the virtual machine of Emblem (the Lisp dialect I'm using to implement inter alia my visual dataflow language, Full Metal Jacket). To keep things short I haven't included initialization or the garbage collector. cons_op takes the top two stack entries (one on the stack, the other in tos), conses them, and returns it in tos.
-------------------
typedef struct ListStruct {
void *head;
void *tail;
} *List;
static struct ListStruct consTable[NUM_CONSES];
static List freeStore;
#define hd(X) (((List)(X))->head)
#define tl(X) (((List)(X))->tail)
#define NIL (void *)&consTable[0]
#define null(X) ((X) == NIL)
static void **stack;
static void *tos; /* Top of stack register. */
static long sp;
static void cons_op()
{
List x;
if (null(freeStore)) { cons_gc(); }
x = freeStore;
freeStore = (List)tl(freeStore);
hd(x) = stack[sp++];
tl(x) = tos;
tos = x;
}
----------------------
Alternatively, instead of C you could use a garbage collected language such as Java, C#, or Go, and then not have to worry about memory management.
The author isn't responsible if people read it and think it's how they should be writing C, especially when the objective of writing lisp in as little C as possible is communicated.
> The author isn't responsible if people read it and think it's how they should be writing C,
Seems we disagree on the basic premise then. If someone learns the wrong thing based on my example I view myself as responsible. (I have not been a perfect example all the time either. We owe it to ourselves and others to always improve on that front.)
I would add that there is already hundreds of implementations of Lisp in C and hundreds of little tutorials that are extremely similar to this online anyway, it's not like one more is going to make a difference.
Yes, his C code could be a lot better, but at the end of the day it really doesn't matter and nobody is going to be using this for anything important.
it's not irresponsible, but copy/paste proliferation does exist, and happens surprisingly often with code posted online like this. When you write code for posting online, you'd want to consider this problem.
People often do what they love with the tools they love. Complaining that someone wrote a hobby project with C is akin to complaining that your neighbor made a decorative baseball bat with her lathe instead of a 3D printer.
Writing Lisp in 200 lines of Haskell wouldn’t be nearly as interesting. The whole point is (presumably) that it’s fun to code golf in C because it’s not very expressive. Relax, no one is going to use the OP’s code to run a pacemaker.
Sorry, this is wrong. A segfault means you got lucky and your bad memory access hit an unmapped or otherwise invalid page. Other times the program will keep running with incorrect results. Many such issues represent exploitable bugs.
In any case it's a bad issue and should not be a normal failure mode for a syntax error.
"Other times the program will keep running with incorrect results. Many such issues represent exploitable bugs."
None of which matter if it's a prototype, running on a developers machine.
If you want memory safety you run the program through valgrind anyway with a large input dataset. And write unit tests. And integration tests. And so on.
The baggage of production quality software development environment is so high it easily stifles the joy of quick and dirty prototypes.
The main use of prototype is to facilitate understanding. This is the most critical constraint, whose needs drive over anything else.
Besides, who on earth is going to exploit a few hundreds of lines of code a developer runs on his or her own machine?
I don't know if you noticed this but C hackers are a minority on here. Thus it's not unreasonable to think people get an impression of how C is done from submissions here. We owe it to the craft to serve as good examples. It's not as burdensome as you suggest.
I see your point, but respectfully disagree that an incomplete implementation should stop a submitter from sharing his or her discoveries. This is an aesthetic position - I think interesting ideas are more important than clean implementations (It is left as an exercise to the reader... is an ok position, IMO).
> a program with missing or unmatched parenthesis, unresolved symbols, etc will likely just result in something like a segmentation fault.
I understand this is just a fun thing to hack on, but this is an irresponsible way to write software. I hope no one here is reading this and thinking it's how they should be writing C.