193(1)09:30 PM:~ 0> gcc -std=c99 -o list list.c
list.c:12: warning: declaration does not declare anything
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
int error(char *s){ fprintf(stderr, "%s\n", s); exit(EXIT_FAILURE); }
struct sexpr {
bool atom;
union {
int cari;
struct sexpr *carl;
};
struct sexpr *cdr;
};
struct list {
int *car;
struct list *cdr;
};
bool init() {
struct list *lp, *h;
(lp=malloc(sizeof*lp)) || error("poof!");
h=lp;
for(int *i = (int[]){ 42, 69, 613, 0 }; *i; i++) {
lp->car = i;
(lp->cdr=malloc(sizeof*lp)) || error("poof!");
lp = lp->cdr;
*lp = (struct list){ NULL, NULL };
}
return true;
}
struct list *read(FILE *f) {
struct list *lp;
int c;
(lp=malloc(sizeof*lp)) || error("poof!");
while( EOF!= (c=fgetc(stdin)) ) {
switch(c) {
case '(': lp->car = 0;
lp->cdr = read(f);
break;
case ')': return NULL;
default:
(lp->car=malloc(sizeof*lp)) || error("poof!");
*lp->car = c;
}
}
return lp;
}
struct list *eval(struct list *lp) {
return lp;
}
bool print(FILE *f, struct list *lp) {
if (lp) {
fprintf(f, "%d\n", lp->car);
print(f,lp->cdr);
return true;
} else {
return false;
}
}
bool run() {
while (print(stdout,eval(read(stdin)))) ;
return true;
}
int main() {
return init()&&run()?0:EXIT_FAILURE;
}
/*eof*/
--
lxt
Standard C does not have anonymous unions. Lose the -std=c99
part and the warning goes away.
> #include <stdbool.h>
> #include <stdio.h>
> #include <stdlib.h>
>
> int error(char *s){ fprintf(stderr, "%s\n", s); exit(EXIT_FAILURE); }
>
> struct sexpr {
> bool atom;
> union {
> int cari;
> struct sexpr *carl;
> };
> struct sexpr *cdr;
>
> };
[...]
>
> struct list *read(FILE *f) {
> struct list *lp;
> int c;
> (lp=malloc(sizeof*lp)) || error("poof!");
> while( EOF!= (c=fgetc(stdin)) ) {
Do you mean fgetc(f) ?
[...]
--
Who's your mama?
But that's even worse!
199(1)10:19 PM:~ 0> gcc -o list list.c
list.c: In function 'init':
list.c:25: error: 'for' loop initial declaration used outside C99 mode
Granted that particular loop doesn't do anything useful
and will not be long for this earth. But I want all the goodies.
> > #include <stdbool.h>
> > #include <stdio.h>
> > #include <stdlib.h>
>
> > int error(char *s){ fprintf(stderr, "%s\n", s); exit(EXIT_FAILURE); }
>
> > struct sexpr {
> > bool atom;
> > union {
> > int cari;
> > struct sexpr *carl;
> > };
> > struct sexpr *cdr;
>
> > };
>
> [...]
>
>
>
> > struct list *read(FILE *f) {
> > struct list *lp;
> > int c;
> > (lp=malloc(sizeof*lp)) || error("poof!");
> > while( EOF!= (c=fgetc(stdin)) ) {
>
> Do you mean fgetc(f) ?
Um. Yes. But something else is terribly wrong with it.
Don't anybody run this program! You have to Interrupt it
to make it stop. And I'm trying to start over with
the new structure (having gotten through that first John
McCarthy paper).
> [...]
>
> --
> Who's your mama?
trog
as in ata-ara-ogg
the true mouth of ogh
--
lxt
Use a named union then. When I said "Lose the -std=c99 part" I
wasn't making a suggestion, I was explaining what's happening.
Personally I consider anonymous structs or unions a bug waiting
to happen since they may lead to ambiguous field references
which gcc may not notice.
Thank you. I consider my question answered.
I suppose any tricks for allowing the two constructs to coexist
are implementation-specific and therefore OT.
Bummer.
--
lxt
I'm surprised CBF hasn't been along yet to tell you that lisp is
off-topic, without bothering to read your message and learn that the
subject line has a typo - that's usually the way he rolls.
Still, he's usually late to the party too, so give him 24 hours...
> On May 10, 10:38 pm, Spiros Bousbouras <spi...@gmail.com> wrote:
>> Use a named union then. When I said "Lose the -std=c99 part" I
>> wasn't making a suggestion, I was explaining what's happening.
>> Personally I consider anonymous structs or unions a bug waiting
>> to happen since they may lead to ambiguous field references
>> which gcc may not notice.
>
> Thank you. I consider my question answered.
No chance! I would not recommend it for new code but to get existing
code though a compiler that does not have anonymous unions, you will
sometimes see:
struct sexpr {
bool atom;
union {
int cari;
struct sexpr *carl;
} either;
#define cari either.cari
#define carl either.carl
struct sexpr *cdr;
};
> I suppose any tricks for allowing the two constructs to coexist
> are implementation-specific and therefore OT.
The above might count as such.
--
Ben.
Not necessarily. If you really wanted to, you could write:
struct sexpr {
bool atom;
union {
int cari;
struct sexpr *carl;
} foo;
struct sexpr *cdr;
};
#define cari foo.cari
#define carl foo.carl
--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Anonymous unions are part of C++, not C.
REH
>Not necessarily. If you really wanted to, you could write:
[...]
>#define cari foo.cari
>#define carl foo.carl
Unfortunately this pollutes the namespace, especially since it's
common to want to use variables with the same names as fields.
Anonymous unions make simple object-oriented programming much
neater.
-- Richard
--
Please remember to mention me / in tapes you leave behind.
Yes. That would do it. (Props to Ben, too!)
Apropos, I discovered that a normal declaration sans identifier
produces quite a different warning:
224(1)10:29 PM:~ 0> cat noid.c && make noid
#include <stdio.h>
int main() {
int i;
int;
*((&i) + 1) = 5;
printf("%d\n", (&i)[1]);
return 0;
}
cc noid.c -o noid
noid.c: In function 'main':
noid.c:5: warning: useless type name in empty declaration
Oddly enough, on my system this prints 5 just before
segfaulting.
So it's possible that space for the union with no name
is included in the struct although the internal names aren't
accessable.
But I've decided against it all.
Since you're all here, I present take 2.
No real questions at the moment but if anything jumps out
as misguided, I'd appreciate a heads-up.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
int error(char *s){ fprintf(stderr, "%s\n", s); exit(EXIT_FAILURE); }
union word {
int i;
struct sexpr *link;
};
struct sexpr {
bool atom; //if true, cdr s.b. NULL
union word car;
struct sexpr *cdr;
};
struct sexpr *globl;
bool init() {
struct sexpr *lp, *h;
(lp=malloc(sizeof*lp)) || error("poof!");
h=lp;
for(int *i = (int[]){ 42, 69, 613, 0 }; *i; i++) {
lp->atom = 0;
(lp->car.link=malloc(sizeof*lp->car.link)) || error("poof!");
lp->car.link->atom = 1;
lp->car.link->car.i = *i;
lp->car.link->cdr = NULL;
(lp->cdr=malloc(sizeof*lp)) || error("poof!");
lp = lp->cdr;
}
free(lp);
lp=NULL;
globl=h;
return true;
}
bool print(FILE *f, struct sexpr *lp) {
if (lp) {
if (lp->atom) {
fprintf(f, "%d\n", lp->car.i);
} else {
if (lp->car.link) print(f,lp->car.link);
if (lp->cdr) print(f,lp->cdr);
}
return true;
} else {
return false;
}
}
bool run() {
//while (print(stdout,eval(read(stdin)))) ;
print(stdout,globl);
<snip>
> Apropos, I discovered that a normal declaration sans identifier
> produces quite a different warning:
>
> 224(1)10:29 PM:~ 0> cat noid.c && make noid
> #include <stdio.h>
>
> int main() {
> int i;
> int;
> *((&i) + 1) = 5;
> printf("%d\n", (&i)[1]);
> return 0;
> }
> cc noid.c -o noid
> noid.c: In function 'main':
> noid.c:5: warning: useless type name in empty declaration
>
> Oddly enough, on my system this prints 5 just before
> segfaulting.
I don't see the oddity (a messed up stack often shows up only on
return).
> So it's possible that space for the union with no name
> is included in the struct although the internal names aren't
> accessable.
>
> But I've decided against it all.
> Since you're all here, I present take 2.
> No real questions at the moment but if anything jumps out
> as misguided, I'd appreciate a heads-up.
>
> #include <stdbool.h>
> #include <stdio.h>
> #include <stdlib.h>
>
> int error(char *s){ fprintf(stderr, "%s\n", s); exit(EXIT_FAILURE); }
>
> union word {
> int i;
> struct sexpr *link;
> };
>
> struct sexpr {
> bool atom; //if true, cdr s.b. NULL
> union word car;
> struct sexpr *cdr;
> };
I don't like the asymmetry. I you want an atom to use only one
pointer's worth of space (as is often done -- the cdr pointer being
used for the property list) I'd go so far as to do this:
union word {
int i;
struct sexpr *link;
};
struct sexpr {
bool atom;
union word car;
struct { struct sexpr *link; } cdr;
};
so that the pointers are sp->cdr.link and sp->car.link.
If you want to get the most storage out of an atom, I'd make is span
both links although there is no portable way to define an int that is
the size of two pointers:
struct sexp {
bool atom;
union {
long long int i;
struct {
struct sexp *car, *cdr;
} pair;
} sexp;
};
though this gives the rather messy names sp->sexp.pair.car and
sp->sexp.pair.cdr.
I'll just throw in the option that, for a fixed size heap, declaring
each part separately has some merit. Gregory Chaitin's tiny LISP
interpreter uses:
long car[SIZE], cdr[SIZE]; /* tree storage */
short atom[SIZE]; /* is it an atom? */
short numb[SIZE]; /* is it a number? */
and so on. (He uses indexes rather than pointers but you get the idea).
--
Ben.
Ah. I'd forgotten about the property list (or association list,
as the original paper designates them).
> I'd go so far as to do this:
>
> union word {
> int i;
> struct sexpr *link;
> };
>
> struct sexpr {
> bool atom;
> union word car;
> struct { struct sexpr *link; } cdr;
> };
>
> so that the pointers are sp->cdr.link and sp->car.link.
Yes. Thanks. That's much better.
> If you want to get the most storage out of an atom, I'd make is span
> both links although there is no portable way to define an int that is
> the size of two pointers:
>
> struct sexp {
> bool atom;
> union {
> long long int i;
> struct {
> struct sexp *car, *cdr;
> } pair;
> } sexp;
> };
>
> though this gives the rather messy names sp->sexp.pair.car and
> sp->sexp.pair.cdr.
>
> I'll just throw in the option that, for a fixed size heap, declaring
> each part separately has some merit. Gregory Chaitin's tiny LISP
> interpreter uses:
>
> long car[SIZE], cdr[SIZE]; /* tree storage */
> short atom[SIZE]; /* is it an atom? */
> short numb[SIZE]; /* is it a number? */
>
> and so on. (He uses indexes rather than pointers but you get the idea).
>
Interesting. For the moment I prefer the structs so related data
is kept together. But garbage collection would be much simpler
with fixed-size arrays.
Thanks again.
--
lxt
-std=c99 isn't that. But if you look at the documentation, in the list of
recognized -std= parameters you should find one that does mean effectively
"all the goodies".
--
Alan Curry
> (lp=malloc(sizeof*lp)) || error("poof!");
Not answering your question here, but... C is not PERL. Blech-an'a-half.
Richard
I knew somebody would have strong feelings against this form
if I continued to use it; but I think it reads better than
a negative predicate. It's also shorter than the alternative:
if ((lp=malloc(sizeof*lp)) == NULL) error("poof!");
For full disclosure, it does require extra decoration to pass splint:
(void)((lp=malloc(sizeof*lp)) || error("poof!"));
But it's still shorter.
--
lxt
Nonetheless, the perl features that this idiom relies upon were all
borrowed from C, and it works just as well in C as in perl. I think
it's a somewhat confusing idiom, but that's just a true in perl as in
C.
Perhaps Perl borrowed them from Unix shells rather than C?
--
Let's bring people who google for "Paris Hilton" to
C programming.
Yes, it's shorter. You can make it shorter still by deleting the
spaces around the "||" operator.
Are you assuming that making it shorter is a *good* thing?
Within limits, it can be; it's certainly possible to be too verbose.
<OT>And I certainly use the "do-something or die" idiom in Perl.</OT>
But here's how I'd write it:
if ((lp = malloc(sizeof *lp) == NULL) {
error("poof!");
}
or even:
lp = malloc(sizeof *lp);
if (lp == NULL) {
error("poof!");
}
Note, among other things, the spaces around the "=" operator and after
the "sizeof" operator.
Y M M V.
>> (lp=malloc(sizeof*lp)) || error("poof!");
>Not answering your question here, but... C is not PERL. Blech-an'a-half.
But that style is perfectly idiomatic in Lisp, so given the context
it's strange to attribute it to Perl.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int error(char *s){ fprintf(stderr, "%s\n", s); exit(EXIT_FAILURE); }
union word {
long i;
struct sexpr *link;
};
struct sexpr {
bool atom;
union word car;
struct { struct sexpr *link; } cdr;
};
bool atom(struct sexpr *sp) { return (sp && sp->atom); }
char *charset = "0123456789"
"abcdefghijklmnopqrstuvwxyz" ;
struct sexpr *assc(char *pnom) {
struct sexpr *ascl, *asct;
char *code, *cp = pnom;
int c=24;
for ( ((ascl=malloc(sizeof*ascl)) || error("poof!")), asct=ascl,
asct->car.i=-1U;
*cp && (code=strchr(charset, *cp));
cp++, c-=6 )
{
if ( (cp-pnom) && !((cp-pnom)%5) ) { //5 6bit codes fit in
32bits
(asct->cdr.link=malloc(sizeof*asct)) || error("poof!");
asct=asct->cdr.link;
asct->car.i=0;
}
asct->car.i&= (code-charset)<<c;;
}
asct->cdr.link=NULL;
return ascl;
}
char *pname(struct sexpr *ascl) {
struct sexpr *asct=ascl;
char *pnom, *cp;
size_t sz;
((pnom=malloc(sz=6)) || error("poof"));
cp=pnom;
while (asct) {
for ( int i=0, c=24;
i<5;
i++, c-=6, cp++ )
{
int in;
in= (asct->car.i>>c)%64
if(in==-1U) *cp=0, goto ret;
*cp= charset[ in ];
}
((pnom=realloc(pnom,sz+=5)) || error("poof"));
asct=asct->cdr.link;
}
ret:return pnom;
}
bool print(FILE *f, struct sexpr *sp) {
if (sp) {
if (sp->atom) {
fprintf(f, "%d %s\n", sp->car.i, pname(sp->cdr.link));
} else {
print(f,sp->car.link);
print(f,sp->cdr.link);
}
return true;
} else return false;
}
struct sexpr *globl;
bool init() {
struct sexpr *lp, *h;
(lp=malloc(sizeof*lp)) || error("poof!");
h=lp;
for(int *i = (int[]){ 42, 69, 613, 0 }; *i; i++) {
struct sexpr *neu;
lp->atom = false;
(neu=malloc(sizeof*neu)) || error("poof!");
neu->atom = true;
neu->car.i = *i;
neu->cdr.link = assc("foo");
lp->car.link = neu;
(lp->cdr.link=malloc(sizeof*lp)) || error("poof!");
lp = lp->cdr.link;
}
free(lp);
lp=NULL;
globl=h;
return true;
}
bool run() {
//while (print(stdout,eval(read(stdin)))) ;
print(stdout,globl);
snip
> in= (asct->car.i>>c)%64
0 <= in <= 63
> if(in==-1U) *cp=0, goto ret;
-1U == UINT_MAX >= 32767U (or is it 65535)
In either case, your if condition can never be true.
snip
Oh, right. I guess 63 would serve better.
I'm sure there are other issues with signed shifting;
and the whole malloc madness. I'm starting over with
a big array of unsigned longs, and I'm thinking of
packing four signed ascii bytes in each for the list,
last byte negative. Real pointers are getting
disasterous.
This is all just a horribly extended tangent from
what I'm actually trying to solve which is a decent
way to implement the postscript save/restore mechanism.
It appears to have been borrowed from lisp as a dynamic
scoping mechanism, but for postscript, it initially
had to double as the only means of freeing memory.
All off-topic, of course, unless it involves some
actual c-code illustrating the problem. Chicken <> Egg.
Thanks again. I hope someday to resurrect your NeWS
programs, once this interpreter functions sufficiently.
(Assuming you're the same Barry Schwarz)
--
lxt