[net.micro.apple] C compiler

ee163ahp@sdccs7.UUCP (GALACTUS) (05/05/84)

Is there a C compiler out there that does not use the Z-80 card
for the apple II?
If so, where and how much.
Also, does anyone know of any parsing techniques suitable for
micro-computer implementation besides recursive descent, and operator
precedence. 


					Nick Flor

bbanerje@sjuvax.UUCP (B. Banerjee) (05/07/84)

>> Also, does anyone know of any parsing techniques suitable for
>> micro-computer implementation besides recursive descent, and operator
>> precedence. 
>> 
>> 					Nick Flor

Why anyone would use recursive descent is beyond me.  Try an LL(1)
predictive parser.  You might want to keep the operator precedence
grammar for expressions.  This is just as powerful as the recursive
descent parser (recognises the same grammar(s) ) but avoids recursive
overhead.

This brings me to... has anyone written a parser generator for LL(1)
grammars, sort of like YACC?  I'm planning to do it myself when I find
the time, but would be overjoyed if someone had beaten me to it
(and was willing to make it available).

Regards,
-- 


				Binayak Banerjee
		{allegra | astrovax | bpa | burdvax}!sjuvax!bbanerje

gwyn@brl-vgr.ARPA (Doug Gwyn ) (05/08/84)

The Aztec C compiler from Manx Software Systems (somewhere in NJ)
is available for the Apple II and does not use the Z80 card.
The price etc. are available from their ad in magazines such as
Byte.

willc@iuvax.UUCP (05/09/84)

I assume you're worried about the size of the parser, since any parsing
technique suitable for mainframes ought to work also on micros if the
program will fit.

If you have access to a parser generator such as yacc, use it and don't
worry about what kind of parser it generates.

Yacc happens to generate an LALR(1) parser.  A book on my shelf claims
that an LALR(1) parser table for Algol 60 occupies less than 3000 bytes.
I would guess that an LL(1) parser table would be about the same size.

A true recursive descent parser is just an LL(1) parser in which the
parser table is encoded procedurally.  I would expect table-driven LL(1)
parsers to be more compact than recursive descent parsers.

If you don't have access to a parser generator, use table-driven LL(1)
or recursive descent.  Just be careful as you transform the grammar into
LL(1) form.  Consult a good textbook on compiler construction such as
Aho & Ullman or Hunter.
				William Clinger
				...iuvax!willc

ech@spuxll.UUCP (05/15/84)

What possible reason would you have to USE an LL(k)-based parser generator?
(given that you can use yacc, that is.)  Yacc is based on LALR(1), see
the paper by Aho and Johnson (Computing Surveys, 1974 -- happy 10th 
anniversary!), and is far and away the easiest tool for HUMANS to use to
construct parsers; by contrast, LL(1) is much more restrictive, and
operator precedence is a REAL bear.

Sorry, didn't mean to come on so strong, there.  IS there any good reason
to use any top-down method?

=Ned=

henry@utzoo.UUCP (Henry Spencer) (05/17/84)

spuxll!ech comments:

   What possible reason would you have to USE an LL(k)-based parser generator?
   (given that you can use yacc, that is.)  Yacc is based on LALR(1), see
   the paper by Aho and Johnson (Computing Surveys, 1974 -- happy 10th 
   anniversary!), and is far and away the easiest tool for HUMANS to use to
   construct parsers; by contrast, LL(1) is much more restrictive, and
   operator precedence is a REAL bear.
   
   Sorry, didn't mean to come on so strong, there.  IS there any good reason
   to use any top-down method?

Yes.  The *only* thing bottom-up has going for it (aside from being more
"fashionable") is that one can build bottom-up parser generators that
accept a very wide range of grammars and make it very easy to throw a
parser together quickly.  This is important, particularly when the parser
is secondary to some other objective, but it isn't everything.

Top-down schemes, in general, can do better error handling/repair (see
my other followup for comments on this), are easier to fit semantics
into, cope better with situations where odd parsing methods are needed
for some special component of the language, and are easier to understand
and work with.  Their one problem is that it's often harder to cast a
given language into a suitable form.  Note that there are better ways
of specifying top-down parsers than LL(1) grammars:  LL(1) grammars
are incredibly difficult to come up with, but tables for table-driven
analogs to recursive descent are very easy to write.  Even so, it's not
quite as simple as throwing together a yacc grammar.

In short, top-down methods show up much better when the problem is to
write a "production" compiler/whatever and one is willing to expend
considerable effort on getting things just right.  Bottom-up methods
work much better -- **IF** you have yacc or the equivalent already
written! -- for quick experimenting and throwing together a parser
for a user interface to some other tool.

It is unfortunate that top-down parsing methods are badly neglected
in current textbooks and courses.
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

rpw3@fortune.UUCP (05/25/84)

#R:sdccs7:-120700:fortune:22000004:000:2140
fortune!rpw3    May 24 20:49:00 1984

Incidentally, easy-to-use compiler-compilers for top-down languages HAVE
been written in the past. The "META" class of compiler-copilers got some
attention for a while. I once got a copy of an Algol-W compiler that had
been written with "META-II", and the distribution (from DECUS for the PDP-10)
included the full "META-II" package, so I played with it a bit. I was able
to write a compiler for a toy version of BLISS (took most of the language
but the generated code was *AWFUL*) in a weekend.

In META-II, you write the grammar with imbedded output rules. Once you match
a rule to the point of outputting something, backup is forbidden, so you need
to do a bit of munging to force (nearly) LL(1) prefix structure. Sample rules
(productions):

	expr:	"(" expr ")" | addop ;

	addop:	mulop "+" OUTPUT("push AC") addop OUTPUT("add @sp+");

	mulop:	mulop "*" OUTPUT("push AC") primary OUTPUT("mul @sp+");

	primary: integer OUTPUT("load ac,[%1]") | name OUTPUT("load ac,%1");

Each rule (production) was compiled into a recursive procedure, which
was called by the stack/backup control routine. Each literal string
was compiled into a call to 'match("string")'. The %1 notation was the
top of stack #1 (of 4), which contained the LEXICAL string most recently
matched (note the use in the rule 'primary'). (The stacks were actually
local stack-variables, in that there was a fresh set available on each
new rule invocation.) You could do ^2 to push onto stack #2, etc. Having
the four stacks around let you re-order the code generation for things like
'if' and 'case' statements. The 'OUTPUT' command was no more than a 'printf'
equivalant, so META-II-generated compilers output assembler source (like
C does).

However, once I had seen how they did it, I quit using META-II, and
started crafting my own recognizer procedures directly, using a small
library of things like 'match'. Given that "bag of tricks", small
top-down parsers are easy to whip up when needed.

Rob Warnock

UUCP:	{ihnp4,ucbvax!amd70,hpda,harpo,sri-unix,allegra}!fortune!rpw3
DDD:	(415)595-8444
USPS:	Fortune Systems Corp, 101 Twin Dolphin Drive, Redwood City, CA 94065