Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

tiny lisp interpreter in C review

125 views
Skip to first unread message

luser droog

unread,
Nov 26, 2014, 9:32:46 PM11/26/14
to
I've posted my baby lisp for review over in comp.lang.lisp.

https://groups.google.com/d/topic/comp.lang.lisp/mpRg2BwGgdo/discussion

But the C may be too heavy for the average reader there.
So I'd appreciate a look (and words) from some C people too.

--
tia, etc.
luser-da-dr00g.

luser droog

unread,
Nov 26, 2014, 9:37:05 PM11/26/14
to
On Wednesday, November 26, 2014 8:32:46 PM UTC-6, luser droog wrote:
> I've posted my baby lisp for review over in comp.lang.lisp.
>
> https://groups.google.com/d/topic/comp.lang.lisp/mpRg2BwGgdo/discussion
>
> But the C may be too heavy for the average reader there.
> So I'd appreciate a look (and words) from some C people too.
>

Warning: implicit int. lots of it.

> --
> tia, etc.
> luser-da-dr00g.

Ian Collins

unread,
Nov 26, 2014, 10:19:43 PM11/26/14
to
Why?

--
Ian Collins

luser droog

unread,
Nov 26, 2014, 10:30:16 PM11/26/14
to
It helps make everything smaller.

Ian Collins

unread,
Nov 26, 2014, 10:35:26 PM11/26/14
to
luser droog wrote:
> On Wednesday, November 26, 2014 9:19:43 PM UTC-6, Ian Collins wrote:
>> luser droog wrote:
>>> On Wednesday, November 26, 2014 8:32:46 PM UTC-6, luser droog wrote:
>>>> I've posted my baby lisp for review over in comp.lang.lisp.
>>>>
>>>> https://groups.google.com/d/topic/comp.lang.lisp/mpRg2BwGgdo/discussion
>>>>
>>>> But the C may be too heavy for the average reader there.
>>>> So I'd appreciate a look (and words) from some C people too.
>>>>
>>>
>>> Warning: implicit int. lots of it.
>>
>> Why?
>>
>
> It helps make everything smaller.

Smaller and harder to read, along with those awful macros... None of
the macros serve any useful purpose.

Most C programmers will gave up as soon as they hit
"#define R return"...

Also by using something that has been obsolete for 15 years as well as
C++ style comments, you have something which is neither C90 or C99.

--
Ian Collins

luser droog

unread,
Nov 26, 2014, 10:42:36 PM11/26/14
to
All of this is true. But the advantage gained is that it
very closely follows the definitions from the original
McCarthy paper, in form as well as semantics.

from http://www-formal.stanford.edu/jmc/recursive/node3.html :
subst [x; y; z] = [atom [z] $\rightarrow$ [eq [z; y] $\rightarrow$ x; T $\rightarrow$ z];
$\quad\quad$T $\rightarrow$ cons [subst [x; y; car [z]]; subst [x; y; cdr [z]]]]

my version:
subst(x,y,z){R atomp(z)?(eq(z,y)?x:z):
cons(subst(x,y,car(z)),subst(x,y,cdr(z)));}

David Brown

unread,
Nov 27, 2014, 2:03:00 AM11/27/14
to
That may be so - and it may make it the best way to write a Lisp
interpreter. But you posted in comp.lang.c, asking for a "C review" -
not a discussion about the language it interprets or the algorithms used
for it. So no one is (currently) saying there is anything wrong with
the program itself - merely that it is very difficult C style. And if
you want to make code clear, fast and flexible, then that is a bad start.

So my first point on looking at that page would be to ask you to look at
your keyboard. What is the biggest key there? What is the second
biggest key? They are big for a reason - learn to use these two
properly. Good use of white space makes a world of difference when
trying to read and understand code.

Then drop the annoying macros, and put in the missing int's. That way
your code will start to look like C, rather than some sort of google
translation from a different language.



luser droog

unread,
Nov 27, 2014, 3:28:49 AM11/27/14
to
On Thursday, November 27, 2014 1:03:00 AM UTC-6, David Brown wrote:
> On 27/11/14 04:42, luser droog wrote:
> > On Wednesday, November 26, 2014 9:35:26 PM UTC-6, Ian Collins wrote:
> >> luser droog wrote:
> >>> On Wednesday, November 26, 2014 9:19:43 PM UTC-6, Ian Collins wrote:
> >>>> luser droog wrote:
> >>>>> On Wednesday, November 26, 2014 8:32:46 PM UTC-6, luser droog wrote:
> >>>>>> I've posted my baby lisp for review over in comp.lang.lisp.
> >>>>>>
> >>>>>> https://groups.google.com/d/topic/comp.lang.lisp/mpRg2BwGgdo/discussion
> >>>>>>
[snip]
With respect, it's only the single '#define R return'
macro that's been identified so far as undesirable.
But you say "macros" in the plural. Was there another one?

As you say whitespace would only be the beginning.
But to be sure, whitespace alone empirically doesn't
seem to help much. Two early versions are presented
on this page, one processed by `indent`:
http://stackoverflow.com/questions/18096456/why-wont-my-little-lisp-quote

The more spacious version seems to trigger my dyslexia
and I can't focus on anything in it. But with the tighter
style, groups of functions can be identified by their
similarity. For example, the basic list functions.

car(x){R consp(x)?val(x)[m]:0;}
cdr(x){R consp(x)?val(x)[m+1]:0;}
caar(x){R car(car(x));}
cadr(x){R car(cdr(x));}
cadar(x){R car(cdr(car(x)));}
caddr(x){R car(cdr(cdr(x)));}
caddar(x){R car(cdr(cdr(car(x))));}
cons(x,y){int z;R z=n-m,*n++=x,*n++=y,z<<2;}
rplaca(x,y){R consp(x)?val(x)[m]=y:0;}
rplacd(x,y){R consp(x)?val(x)[m+1]=y:0;}

Since they're side-by-side, you can read and compare/contrast
car() and cdr() as a pair. It's the same, but different.

The indented version, makes this harder. It's not parallel
anymore. It's not side-by-side.

car (x)
{
R consp (x) ? val (x)[m] : 0;
}

cdr (x)
{
R consp (x) ? val (x)[m + 1] : 0;
}

caar (x)
{
R car (car (x));
}

cadr (x)
{
R car (cdr (x));
}

cadar (x)
{
R car (cdr (car (x)));
}

caddr (x)
{
R car (cdr (cdr (x)));
}

caddar (x)
{
R car (cdr (cdr (car (x))));
}

cons (x, y)
{
int z;
R z = n - m, *n++ = x, *n++ = y, z << 2;
}

rplaca (x, y)
{
R consp (x) ? val (x)[m] = y : 0;
}

rplacd (x, y)
{
R consp (x) ? val (x)[m + 1] = y : 0;
}

It's just lower density.
Message has been deleted

David Brown

unread,
Nov 27, 2014, 5:13:57 AM11/27/14
to
The other one was "T", but I don't see that it was much used.

>
> As you say whitespace would only be the beginning.
> But to be sure, whitespace alone empirically doesn't
> seem to help much. Two early versions are presented
> on this page, one processed by `indent`:
> http://stackoverflow.com/questions/18096456/why-wont-my-little-lisp-quote
>

Spacing is just a starting point - good spacing does not change the
text, it just makes it easier to parse. In general, your code here is
very terse and compact which makes it hard to interpret and understand.

I find it surprising that you have more problems reading the code when
it is tightly spaced - it is more common for good spacing to help
dyslexia. Are you sure you are actually reading and interpreting the
code here, and not just spotting that there are patterns? For example,
if you were to mix up "car" and "cdr" in one of your definitions, would
you notice?


> The more spacious version seems to trigger my dyslexia
> and I can't focus on anything in it. But with the tighter
> style, groups of functions can be identified by their
> similarity. For example, the basic list functions.
>
> car(x){R consp(x)?val(x)[m]:0;}
> cdr(x){R consp(x)?val(x)[m+1]:0;}
> caar(x){R car(car(x));}
> cadr(x){R car(cdr(x));}
> cadar(x){R car(cdr(car(x)));}
> caddr(x){R car(cdr(cdr(x)));}
> caddar(x){R car(cdr(cdr(car(x))));}
> cons(x,y){int z;R z=n-m,*n++=x,*n++=y,z<<2;}
> rplaca(x,y){R consp(x)?val(x)[m]=y:0;}
> rplacd(x,y){R consp(x)?val(x)[m+1]=y:0;}
>
> Since they're side-by-side, you can read and compare/contrast
> car() and cdr() as a pair. It's the same, but different.
>
> The indented version, makes this harder. It's not parallel
> anymore. It's not side-by-side.


I can appreciate what you mean for the "cadr" type functions, and I can
see the advantage here of putting them on a single line. But at the
very least, I would write them as:

static inline int cadar(int x){return car(cdr(car(x)));}

That is still one line (and I would not worry about line lengths in such
cases), and still follows your patterns - but it looks like a C function
(and is thus easier for humans), obeys the requirements of modern C
standards (making it easier for warnings and automatic error checking),
and will result in smaller and faster object code (since the functions
are "static inline").

Opinions on this vary (sometimes heatedly), but I have never seen a case
where I think the comma operator is appropriate in C :

static int cons(int x, int y) {
int z = n - m;
*n++ = x;
*n++ = y;
return z * 4;
}

A few more points (not everyone here will agree on all these - these are
/my/ opinions and advice, and I am stating them here in very general
terms. There will always be exceptions where it is appropriate to break
these rules - readability and maintainability are more important):

Never use shifts when you mean multiply. If you mean "z * 4", write "z
* 4". If you mean "z * sizeof(int)", then write that. Only use shifts
for bit manipulation and field extraction - and you almost certainly
should be using unsigned types in such cases. The compiler will do a
better job of optimising if you tell it what you really want - don't try
to guess what you think is an optimisation.

If you want 32-bit integers, using int32_t (or uint32_t) - don't use
"int". When you need a specific size, use the specific sized C types to
say what you want. Even if you know you will always be running on a
system with 32-bit ints, it is clearer and better documented if you
write the type you actually want.

Short variable names, like "n" or "m", can be fine for local variables
in a function. Don't use them for globals or file-scope - give such
data a decent descriptive name.

Do not mix pointer and non-pointer variable declarations on the same
line. My preference is normally to have only a single variable on each
line:

int *m; // description of m
int *n; // description of n
int msz; // description of msz


Any data or functions that are not exported from the module or file
should be "static". It leads to smaller, faster and safer code.

Do not put multiple statements on one line.

And (in case it isn't clear already) never use "implicit int".

BartC

unread,
Nov 27, 2014, 5:57:31 AM11/27/14
to


"luser droog" <luser...@gmail.com> wrote in message
news:dd7a67b1-98f5-4b13...@googlegroups.com...
> I've posted my baby lisp for review over in comp.lang.lisp.
>
> https://groups.google.com/d/topic/comp.lang.lisp/mpRg2BwGgdo/discussion
>
> But the C may be too heavy for the average reader there.
> So I'd appreciate a look (and words) from some C people too.

"ppnarg.h: No such file or directory"

After I'd managed to locate the macros that apparently make up that header,
the linker couldn't find sbrk().

I would have thought that such a language application would have little need
for OS-specific functions like this.

Also, why the need to minimise the size of the source? This is supposed to
be a Lisp interpreter not a C one; once compiled, the source code size is
irrelevant.

--
Bartc



Ben Bacarisse

unread,
Nov 27, 2014, 9:08:46 AM11/27/14
to
luser droog <luser...@gmail.com> writes:

> I've posted my baby lisp for review over in comp.lang.lisp.
>
> https://groups.google.com/d/topic/comp.lang.lisp/mpRg2BwGgdo/discussion
>
> But the C may be too heavy for the average reader there.
> So I'd appreciate a look (and words) from some C people too.

You don't say what the purpose of the code is. Is it supposed to be
fast? Is it intended to show how much can be done with as few
characters as possible? Is it intended to be simple to embed in other
programs? Is it supposed to be a useful programming language? Is it
supposed to be portable? Is it supposed to be used to teach the ideas
involved? All will produce different answers, and any time taken to
give an answer before knowing any of the answers is likely to be wasted.

--
Ben.

Joe Pfeiffer

unread,
Nov 27, 2014, 11:00:36 AM11/27/14
to
David Brown <david...@hesbynett.no> writes:
>
> So my first point on looking at that page would be to ask you to look at
> your keyboard. What is the biggest key there? What is the second
> biggest key? They are big for a reason - learn to use these two
> properly. Good use of white space makes a world of difference when
> trying to read and understand code.

I'm going to remember that.

Robert Wessel

unread,
Nov 27, 2014, 11:08:01 AM11/27/14
to
On Thu, 27 Nov 2014 08:02:50 +0100, David Brown
<david...@hesbynett.no> wrote:

>So my first point on looking at that page would be to ask you to look at
>your keyboard. What is the biggest key there? What is the second
>biggest key? They are big for a reason - learn to use these two
>properly. Good use of white space makes a world of difference when
>trying to read and understand code.


Hmmm... The second biggest key on my laptop is the right shift key.
I'm not sure how that's going to help. ;-)

FWIW, the enter key is tied for third place with the *left* shift key.

David Brown

unread,
Nov 27, 2014, 11:18:52 AM11/27/14
to
OK, take a look at /my/ keyboard :-)

The biggest key is almost always the space key. But sometimes the enter
key is not the second biggest - occasionally it is the delete or
backspace key, which can also help improve a lot of code!

Kaz Kylheku

unread,
Nov 27, 2014, 12:22:55 PM11/27/14
to
On 2014-11-27, luser droog <luser...@gmail.com> wrote:
> I've posted my baby lisp for review over in comp.lang.lisp.
>
> https://groups.google.com/d/topic/comp.lang.lisp/mpRg2BwGgdo/discussion
>
> But the C may be too heavy for the average reader there.
> So I'd appreciate a look (and words) from some C people too.

Here are some real comments, instead of nitpicking about your style,
whcih is basically consistent and clear, once the reader accepts it.

The code is entirely transparent to me, and I don't care about implicit int;
it's a deprecated language feature, but everyone has understood it since
K&R published the first edition of their book in 1978.

Ease of notation is very important, at least until the point that you have a
working Lisp compiler. Until that point, you're writing a lot of code inside
the interpreter, which is painful unless you "bend" the C language somehow.
When you have a compiler going, you can write code in Lisp, and it can spit out
C (or assembly or whatever). The generated C then no longer has to be readable,
but before then, it helps you to have a notation you can live with.

Your program is of little practical use because you're denoting objects by
integers, which index into a single array, so the entire heap has to be in a
flat space. As a language, it's probably worse than any Lisp that has ever
existed, including the early work by John McCarthy's group.

You have neglected to implement a garbage collector and your cons function just
runs beyond the allocated range, always grabbing brand new memory:

cons(x,y){int z;R z=n-m,*n++=x,*n++=y,z<<2;}

You have dropped to a nonportable memory scheme involving catching segfaults
and using sbrk. This is not compatible with the use of anything else in
the image, like C libraries that use malloc. Eventually your heap will
crash into something, like mmap'ed library. No language today can afford
to be designed to be this hostile against integration.

Your Lisp dialect is not robust. Though you detect type mismatches, you
sweep them under the carpet. For instance, I noticed right off the bat that
if x is not a cons then (rplaca x y) silently does nothing and returns zero,
instead of raising an exception. Though it's good that the program doesn't
crash in this situation, ignoring a problem isn't much better; someone
trying to write a complicated program will pull their hair out debugging
without any help.

Implementing a Lisp without a plan for implementing exceptions from the get go
is a nonstarter.

I just noticed that your eq function is broken. eq is implementation
equality in Lisp, and so rather than:

eq(x,y){R atomp(x)&&atomp(y)?x==y:0;}

it should really just be:

eq(x,y){R x==y;} /* compare that the bits in x are the same as y */

consider what happens if your eq is invoked like this:

eq(a,a)

and a is not an atom? It will return 0, incorrectly reporting that a is not eq
to itself, which is the "hello world" base case for eq!

By the way, have a look at the Lisp500 project ("Lisp in 500 lines of C"), if
you can still find it. It's something similar to yours.

Geoff

unread,
Nov 27, 2014, 1:48:27 PM11/27/14
to
On 2014-11-27 02:32:38 +0000, luser droog said:

> I've posted my baby lisp for review over in comp.lang.lisp.
>
> https://groups.google.com/d/topic/comp.lang.lisp/mpRg2BwGgdo/discussion
>
> But the C may be too heavy for the average reader there.
> So I'd appreciate a look (and words) from some C people too.
>

Appears to be dead code, line 172, I can find no calls to among()
except to itself:
among(x,y){R!null(y)&&equal(x,car(y))||among(x,cdr(y));}
presents semantic issue of && within || expression.
I think you meant to write
among(x,y){R(!null(y)&&equal(x,car(y)))||among(x,cdr(y));}



BartC

unread,
Nov 27, 2014, 2:36:21 PM11/27/14
to
On 27/11/2014 10:56, BartC wrote:>
>
> "luser droog" <luser...@gmail.com> wrote in message
> news:dd7a67b1-98f5-4b13...@googlegroups.com...
>> I've posted my baby lisp for review over in comp.lang.lisp.
>>
>> https://groups.google.com/d/topic/comp.lang.lisp/mpRg2BwGgdo/discussion
>>
>> But the C may be too heavy for the average reader there.
>> So I'd appreciate a look (and words) from some C people too.
>
> "ppnarg.h: No such file or directory"

It has problems with ppnarg.h under Ubuntu as well.

It did eventually build, and sort of run, but I can't tell if it works.
I don't know Lisp but most things I type into it seem to return NIL. I
think SETQ does something, but setq doesn't. While a Hello World program
gives a seg-fault.

What did you want to know? At present I'd score it low on being able to
effortlessly compile, on docs, and on executing even minimal Lisp. The
style doesn't bother me, unless at some point I have to get into the
source code to fix it, but it's not that terrible.

--
Bartc


Keith Thompson

unread,
Nov 27, 2014, 3:15:44 PM11/27/14
to
How big is the Any key?

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Richard Bos

unread,
Nov 27, 2014, 3:25:35 PM11/27/14
to
Keith Thompson <ks...@mib.org> wrote:

> Robert Wessel <robert...@yahoo.com> writes:
> > On Thu, 27 Nov 2014 08:02:50 +0100, David Brown

> >>So my first point on looking at that page would be to ask you to look at
> >>your keyboard. What is the biggest key there? What is the second
> >>biggest key? They are big for a reason - learn to use these two
> >>properly. Good use of white space makes a world of difference when
> >>trying to read and understand code.
> >
> > Hmmm... The second biggest key on my laptop is the right shift key.
> > I'm not sure how that's going to help. ;-)

Useful if you're emulating a Speccy.

> > FWIW, the enter key is tied for third place with the *left* shift key.
>
> How big is the Any key?

Big. And red.

Richard (whose shift keys are both larger than his Enter)

luser droog

unread,
Nov 27, 2014, 10:26:11 PM11/27/14
to
On Thursday, November 27, 2014 11:22:55 AM UTC-6, Kaz Kylheku wrote:
> On 2014-11-27, luser droog <luser...@gmail.com> wrote:
> > I've posted my baby lisp for review over in comp.lang.lisp.
> >
> > https://groups.google.com/d/topic/comp.lang.lisp/mpRg2BwGgdo/discussion
> >
> > But the C may be too heavy for the average reader there.
> > So I'd appreciate a look (and words) from some C people too.
>
> Here are some real comments, instead of nitpicking about your style,
> whcih is basically consistent and clear, once the reader accepts it.

Much obliged. This is what I had hoped for.

>
> The code is entirely transparent to me,

Each time I read this, I get flashes from David Lynch's Dune.
The scene where the Emperor meets the Guild Navigator in his
enormous elephant tank and he says "You are transparent.
I see through you."

Spooky.

>and I don't care about implicit int;
> it's a deprecated language feature, but everyone has understood it since
> K&R published the first edition of their book in 1978.
>
> Ease of notation is very important, at least until the point that you have a
> working Lisp compiler. Until that point, you're writing a lot of code inside
> the interpreter, which is painful unless you "bend" the C language somehow.
> When you have a compiler going, you can write code in Lisp, and it can spit out
> C (or assembly or whatever). The generated C then no longer has to be readable,
> but before then, it helps you to have a notation you can live with.
>
> Your program is of little practical use because you're denoting objects by
> integers, which index into a single array, so the entire heap has to be in a
> flat space. As a language, it's probably worse than any Lisp that has ever
> existed, including the early work by John McCarthy's group.

That is a severe limitation in the design I've chosen. But, but...
Hm. Can't think of an excuse. It can be changed. Bits from the address
could index an array of base-pointers, to split data across multiple
arrays. And it can be made to allocate chunks with malloc and coalesce
adjacent chunks if the union is still addressable by the remaining
address bits.

But the decisions went kind of like this: implicit int functions will
look more like the published spec, thus easier to verify; so the object
must be encapsulated in an int to achieve this. If the tag data comes
off of the bottom of the int, then the remaining bits can hold a signed
value, so long as right-shifting preserves signedness, hence the assert.

While not necessarily golfing this to the max, brevity was a goal.
The ultimate goal being to fit the entire interpreter comfortably,
needing no comments, on just a few pages. "Paper" portable.

>
> You have neglected to implement a garbage collector and your cons function just
> runs beyond the allocated range, always grabbing brand new memory:
>
> cons(x,y){int z;R z=n-m,*n++=x,*n++=y,z<<2;}

Totally right. Allocation and bounds checking. Yes, it would/will need
garbage collection before it will be useful for an application.
I was so excited that SETQ was working that I want to show the world!

>
> You have dropped to a nonportable memory scheme involving catching segfaults
> and using sbrk. This is not compatible with the use of anything else in
> the image, like C libraries that use malloc. Eventually your heap will
> crash into something, like mmap'ed library. No language today can afford
> to be designed to be this hostile against integration.

That. Yeah. Totally not portable. But, it's so cool. Just take the memory
and run: no "malloc overhead", like a car with no firewall I suppose.
I started doing this with tiny interpreters like

http://codegolf.stackexchange.com/questions/19151/build-interpreter-for-non-existent-language/19240#19240
http://codegolf.stackexchange.com/questions/16476/implement-the-universal-machine-emulator/18694#18694

and then it became a habit. It's pretty miraculous that printf wasn't
giving me problems.

>
> Your Lisp dialect is not robust. Though you detect type mismatches, you
> sweep them under the carpet. For instance, I noticed right off the bat that
> if x is not a cons then (rplaca x y) silently does nothing and returns zero,
> instead of raising an exception. Though it's good that the program doesn't
> crash in this situation, ignoring a problem isn't much better; someone
> trying to write a complicated program will pull their hair out debugging
> without any help.
>
> Implementing a Lisp without a plan for implementing exceptions from the get go
> is a nonstarter.

Hm. My first go-to idea would be setjmp/longjmp.
But it could also be a propagated error code.
My inca interpreter does one, and xpost does the other.
Is there a third way?

>
> I just noticed that your eq function is broken. eq is implementation
> equality in Lisp, and so rather than:
>
> eq(x,y){R atomp(x)&&atomp(y)?x==y:0;}
>
> it should really just be:
>
> eq(x,y){R x==y;} /* compare that the bits in x are the same as y */
>
> consider what happens if your eq is invoked like this:
>
> eq(a,a)
>
> and a is not an atom? It will return 0, incorrectly reporting that a is not eq
> to itself, which is the "hello world" base case for eq!

Indeed yes. I've been having that very bug.
The first DEFUN example from the micro-manual is

(DEFUN NULL(X)(EQ X NIL))

which has not been working.

>
> By the way, have a look at the Lisp500 project ("Lisp in 500 lines of C"), if
> you can still find it. It's something similar to yours.

I did stumble upon that, searching comp.lang.lisp for "DEFUN".
https://web.archive.org/web/20070317222332/http://www.modeemi.fi/~chery/lisp500/

If you thought mine had too many comments, it's your lucky lucky day.

I've made very little sense out of it so far, but I do consider it
inspirational.

luser droog

unread,
Nov 27, 2014, 10:28:48 PM11/27/14
to
Wow. Good call. I forgot the precedence on those an misremembered
them as equal. But aren't these two versions equivalent, since
the lower precedence of || means that the && binds more tightly?
But if it isn't even used, then maybe it doesn't need to be there
at all...

luser droog

unread,
Nov 28, 2014, 12:33:02 AM11/28/14
to
On Thursday, November 27, 2014 1:36:21 PM UTC-6, Bart wrote:
> On 27/11/2014 10:56, BartC wrote:>
> >
> > "luser droog" <luser...@gmail.com> wrote in message
> > news:dd7a67b1-98f5-4b13...@googlegroups.com...
> >> I've posted my baby lisp for review over in comp.lang.lisp.
> >>
> >> https://groups.google.com/d/topic/comp.lang.lisp/mpRg2BwGgdo/discussion
> >>
> >> But the C may be too heavy for the average reader there.
> >> So I'd appreciate a look (and words) from some C people too.
> >
> > "ppnarg.h: No such file or directory"
>
> It has problems with ppnarg.h under Ubuntu as well.
>

Poor posting etiquette on my part. If you psychically followed several
links from the post through the commit to the repo, you'd find it there.
But if I'm going to go posting this file separately like I keep doing,
I really need to address that better. Maybe a /*link*/.

> It did eventually build, and sort of run, but I can't tell if it works.
> I don't know Lisp but most things I type into it seem to return NIL. I
> think SETQ does something, but setq doesn't. While a Hello World program
> gives a seg-fault.

It's literally just what the "micro manual" describes. atoms and lists
the basic basic functions. I have only just implemented the SUBR and FSUBR
interface to C functions.

>
> What did you want to know? At present I'd score it low on being able to
> effortlessly compile, on docs, and on executing even minimal Lisp. The
> style doesn't bother me, unless at some point I have to get into the
> source code to fix it, but it's not that terrible.

I appreciate the look. I may post it again in the future,
once I address all of Kaz's observations.

luser droog

unread,
Nov 28, 2014, 12:47:55 AM11/28/14
to
For what porpoise? Quod Guestion.
All of those goals to varying degrees. It's intended to be "axiomatic".
It builds up its universe out of _these_ pieces and shows _how_ the pieces
are constructed too.

As I commented to the SO question.
> The formatting is a double-edged sword, to be sure. The upside is that
> the source itself does triple duty as a cheat-sheet, a full-reference-
> implementation, and a working prototype.

But I want it to be small, to be comprehensible. This is /all/ there is.
Like it's its own chapter 1 of Genesis, text of its own LOGOs.

I've also been reading Hacker's Delight so there's quite a bit of
eagerness to bit-twiddle in there, too.

Malcolm McLean

unread,
Nov 28, 2014, 3:39:19 AM11/28/14
to
On Friday, November 28, 2014 3:26:11 AM UTC, luser droog wrote:
>
> Totally right. Allocation and bounds checking. Yes, it would/will need
> garbage collection before it will be useful for an application.
> I was so excited that SETQ was working that I want to show the world!
>
I'd consider writing it as a library for Jacob Navia's lcc compiler.

That has garbage collection built in.
Doing it yourself is a major headache.

Rosario193

unread,
Nov 28, 2014, 5:07:15 AM11/28/14
to
yes i agree

but i prefer something as
car(x) {R consp(x)? val(x)[m] :0;}
cdr(x) {R consp(x)? val(x)[m+1]:0;}
caar(x) {R car(car(x));}
cadr(x) {R car(cdr(x));}
cadar(x) {R car(cdr(car(x)));}
caddr(x) {R car(cdr(cdr(x)));}
caddar(x){R car(cdr(cdr(car(x))));}
cons(x,y)
{int z;
R z=n-m,*n++=x,*n++=y,z<<2;}
rplaca(x,y)
{R consp(x)?val(x)[m]=y:0;}
rplacd(x,y)
{R consp(x)?val(x)[m+1]=y:0;}


Rosario193

unread,
Nov 28, 2014, 5:29:43 AM11/28/14
to
On Fri, 28 Nov 2014 11:07:08 +0100, Rosario193 wrote:

>yes i agree
>
>but i prefer something as
>car(x) {R consp(x)? val(x)[m] :0;}
>cdr(x) {R consp(x)? val(x)[m+1]:0;}
>caar(x) {R car(car(x));}
>cadr(x) {R car(cdr(x));}
>cadar(x) {R car(cdr(car(x)));}
>caddr(x) {R car(cdr(cdr(x)));}
>caddar(x){R car(cdr(cdr(car(x))));}
>cons(x,y)
> {int z;
> R z=n-m,*n++=x,*n++=y,z<<2;}
>rplaca(x,y)
> {R consp(x)?val(x)[m]=y:0;}
>rplacd(x,y)
> {R consp(x)?val(x)[m+1]=y:0;}

or better

car(x) {R consp(x)? val(x)[m] : 0;}
cdr(x) {R consp(x)? val(x)[m+1]: 0;}
caar(x) {R car(car(x)); }
cadr(x) {R car(cdr(x)); }
cadar(x) {R car(cdr(car(x))); }
caddr(x) {R car(cdr(cdr(x))); }
caddar(x){R car(cdr(cdr(car(x)))); }
cons(x,y){int z;
R z=n-m,*n++=x,*n++=y,z<<2;}
rplaca(x,y)
{R consp(x)? val(x)[m]=y : 0;}

Ben Bacarisse

unread,
Nov 28, 2014, 5:54:47 AM11/28/14
to
luser droog <luser...@gmail.com> writes:

> On Thursday, November 27, 2014 8:08:46 AM UTC-6, Ben Bacarisse wrote:
>> luser droog <luser...@gmail.com> writes:
>>
>> > I've posted my baby lisp for review over in comp.lang.lisp.
>> >
>> > https://groups.google.com/d/topic/comp.lang.lisp/mpRg2BwGgdo/discussion
>> >
>> > But the C may be too heavy for the average reader there.
>> > So I'd appreciate a look (and words) from some C people too.
>>
>> You don't say what the purpose of the code is. Is it supposed to be
>> fast? Is it intended to show how much can be done with as few
>> characters as possible? Is it intended to be simple to embed in other
>> programs? Is it supposed to be a useful programming language? Is it
>> supposed to be portable? Is it supposed to be used to teach the ideas
>> involved? All will produce different answers, and any time taken to
>> give an answer before knowing any of the answers is likely to be wasted.
>
> For what porpoise? Quod Guestion.
> All of those goals to varying degrees.

Then you are asking a lot of people. I'd have things to say about all
these goals, many of them contradictory. For example, to be faster you
would not repeatedly built atoms like COND right in the inner loop of
the evaluator when they are nothing more than compile-time constants,
but pre-defining them would be less minimal. To be more portable you
would not convert arbitrary unsigned ints to int, but then you could not
use implicit into to minimise the code size. To use it for teaching, a
pure lambda evaluator would be clearer (and you'd have to hit the spec
key a few more times) but then it would be further from being a useful
language. To be a useful language, you'd need arithmetic but that would
be more complex and hide the educational fact that you can define
numbers using functions (if that is where your are headed).

One universal comment is that it does seem to be buggy:

>(LIST 1)
( LIST 1
(
NIL

can't be right, not least because of the incorrectly bracketed trace
output.

> It's intended to be "axiomatic".
> It builds up its universe out of _these_ pieces and shows _how_ the pieces
> are constructed too.

For that purpose it's a bit wordy. You have, for example, four
otherwise identical functions whose only distinction is to set a
differently names union member -- despite all the types being the same.

<snip>
> But I want it to be small, to be comprehensible. This is /all/ there is.
> Like it's its own chapter 1 of Genesis, text of its own LOGOs.

Then it's too much! A pure lambda evaluator would be more minimal.
More minimal still would be a combinator engine -- all computation done
with just two or three functions. Booleans, numbers and lists can all
be built from them.

I thought that might be where you were going with this, but the papers
you reference are about LISP with built-in arithmetic. How do you
plan to do numbers?

<snip>
--
Ben.

BartC

unread,
Nov 28, 2014, 7:29:03 AM11/28/14
to
"Malcolm McLean" <malcolm...@btinternet.com> wrote in message
news:3f995d6b-9ac0-4697...@googlegroups.com...
I introduced a feature on a language last year that needed to be used with
garbage collection. But I initially used a very simple garbage collector
that just did nothing. Then I forgot all about it and carried using it
without any problems!

(And testing that application now, with the most complex input I'm likely to
give it in actual use, the memory consumption on my machine increased from
1.70GB to 1.72GB for second or so, then back down to 1.70GB.)

So for some applications, such as experimental tiny lisp interpreters, or
where most of the data needs to be retained until the end anyway, it's not
much of a priority.

--
Bartc

Malcolm McLean

unread,
Nov 28, 2014, 9:28:40 AM11/28/14
to
On Friday, November 28, 2014 12:29:03 PM UTC, Bart wrote:
> "Malcolm McLean" <malcolm...@btinternet.com> wrote in message
>
> I introduced a feature on a language last year that needed to be used with
> garbage collection. But I initially used a very simple garbage collector
> that just did nothing. Then I forgot all about it and carried using it
> without any problems!
>
> (And testing that application now, with the most complex input I'm likely to
> give it in actual use, the memory consumption on my machine increased from
> 1.70GB to 1.72GB for second or so, then back down to 1.70GB.)
>
> So for some applications, such as experimental tiny lisp interpreters, or
> where most of the data needs to be retained until the end anyway, it's not
> much of a priority.
>
Eventually memory leaks will bite you.
Not in a messing about, little test or demo proof of concept environment,
but when you move from that to real industrial code. For example if you
put a scripting language in a video game, and use the lisp interpreter to
interpret that. People might set the game running and keep it running
for days on end.

Tim Rentsch

unread,
Nov 28, 2014, 2:30:34 PM11/28/14
to
The extra parentheses you suggest are unnecessary. The
expression means the same thing with or without them.

Tim Rentsch

unread,
Nov 28, 2014, 2:32:32 PM11/28/14
to
luser droog <luser...@gmail.com> writes:

> On Thursday, November 27, 2014 12:48:27 PM UTC-6, Geoff wrote:
>> On 2014-11-27 02:32:38 +0000, luser droog said:
>>
>> > I've posted my baby lisp for review over in comp.lang.lisp.
>> >
>> > https://groups.google.com/d/topic/comp.lang.lisp/mpRg2BwGgdo/discussion
>> >
>> > But the C may be too heavy for the average reader there.
>> > So I'd appreciate a look (and words) from some C people too.
>> >
>>
>> Appears to be dead code, line 172, I can find no calls to among()
>> except to itself:
>> among(x,y){R!null(y)&&equal(x,car(y))||among(x,cdr(y));}
>> presents semantic issue of && within || expression.
>> I think you meant to write
>> among(x,y){R(!null(y)&&equal(x,car(y)))||among(x,cdr(y));}
>
> But aren't these two versions equivalent, since the lower
> precedence of || means that the && binds more tightly?

Yes, you are right on both counts.

Kaz Kylheku

unread,
Nov 28, 2014, 3:07:39 PM11/28/14
to
On 2014-11-27, luser droog <luser...@gmail.com> wrote:
> I've posted my baby lisp for review over in comp.lang.lisp.
>
> https://groups.google.com/d/topic/comp.lang.lisp/mpRg2BwGgdo/discussion
>
> But the C may be too heavy for the average reader there.
> So I'd appreciate a look (and words) from some C people too.

By the way:

// instead of { R ...; } hack:

#define defun(NAME, ARGS, ...) \
NAME ARGS { return __VA_ARGS__; }

defun(cadar, (x), car(cdr(car(x))))

You could write a translator from

(defun cadar (x) (car (cdr (car x))))

to the above, and write most of the code in that notation.

luser droog

unread,
Nov 28, 2014, 11:33:43 PM11/28/14
to
THAT'S AWESOME!

I could even add the return type without making a lot of clutter.
0 new messages